blob: bcef12c6f1cbc98b1373a88926974ad0c4e0d9c2 [file] [log] [blame]
Simon Glass45b5a372015-04-29 22:25:59 -06001/*
2 * Copyright (c) 2015 Google, Inc
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 *
6 * Taken from coreboot file of the same name
7 */
8
9/*
10 * The SIPI vector is responsible for initializing the APs in the sytem. It
11 * loads microcode, sets up MSRs, and enables caching before calling into
12 * C code
13 */
14
15#include <asm/global_data.h>
16#include <asm/msr-index.h>
17#include <asm/processor.h>
18#include <asm/processor-flags.h>
19#include <asm/sipi.h>
20
21#define CODE_SEG (X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE)
22#define DATA_SEG (X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE)
23
24/*
25 * First we have the 16-bit section. Every AP process starts here.
26 * The simple task is to load U-Boot's Global Descriptor Table (GDT) to allow
27 * U-Boot's 32-bit code to become visible, then jump to ap_start.
28 *
29 * Note that this code is copied to RAM below 1MB in mp_init.c, and runs from
30 * there, but the 32-bit code (ap_start and onwards) is part of U-Boot and
31 * is therefore relocated to the top of RAM with other U-Boot code. This
32 * means that for the 16-bit code we must write relocatable code, but for the
33 * rest, we can do what we like.
34 */
35.text
36.code16
37.globl ap_start16
38ap_start16:
39 cli
40 xorl %eax, %eax
41 movl %eax, %cr3 /* Invalidate TLB */
42
43 /* setup the data segment */
44 movw %cs, %ax
45 movw %ax, %ds
46
47 /* Use an address relative to the data segment for the GDT */
48 movl $gdtaddr, %ebx
49 subl $ap_start16, %ebx
50
51 data32 lgdt (%ebx)
52
53 movl %cr0, %eax
54 andl $(~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE | \
55 X86_CR0_TS | X86_CR0_EM | X86_CR0_MP)), %eax
56 orl $(X86_CR0_NW | X86_CR0_CD | X86_CR0_PE), %eax
57 movl %eax, %cr0
58
59 movl $ap_start_jmp, %eax
60 subl $ap_start16, %eax
61 movw %ax, %bp
62
63 /* Jump to ap_start within U-Boot */
64data32 cs ljmp *(%bp)
65
66 .align 4
67.globl sipi_params_16bit
68sipi_params_16bit:
69 /* 48-bit far pointer */
70ap_start_jmp:
71 .long 0 /* offset set to ap_start by U-Boot */
72 .word CODE_SEG /* segment */
73
74 .word 0 /* padding */
75gdtaddr:
76 .word 0 /* limit */
77 .long 0 /* table */
78 .word 0 /* unused */
79
80.globl ap_start16_code_end
81ap_start16_code_end:
82
83/*
84 * Set up the special 'fs' segment for global_data. Then jump to ap_continue
85 * to set up the AP.
86 */
87.globl ap_start
88ap_start:
89 .code32
90 movw $DATA_SEG, %ax
91 movw %ax, %ds
92 movw %ax, %es
93 movw %ax, %ss
94 movw %ax, %gs
95
96 movw $(X86_GDT_ENTRY_32BIT_FS * X86_GDT_ENTRY_SIZE), %ax
97 movw %ax, %fs
98
99 /* Load the Interrupt descriptor table */
100 mov idt_ptr, %ebx
101 lidt (%ebx)
102
103 /* Obtain cpu number */
104 movl ap_count, %eax
1051:
106 movl %eax, %ecx
107 inc %ecx
108 lock cmpxchg %ecx, ap_count
109 jnz 1b
110
111 /* Setup stacks for each CPU */
112 movl stack_size, %eax
113 mul %ecx
114 movl stack_top, %edx
115 subl %eax, %edx
116 mov %edx, %esp
117 /* Save cpu number */
118 mov %ecx, %esi
119
120 /* Determine if one should check microcode versions */
121 mov microcode_ptr, %edi
122 test %edi, %edi
123 jz microcode_done /* Bypass if no microde exists */
124
125 /* Get the Microcode version */
126 mov $1, %eax
127 cpuid
128 mov $MSR_IA32_UCODE_REV, %ecx
129 rdmsr
130 /* If something already loaded skip loading again */
131 test %edx, %edx
132 jnz microcode_done
133
134 /* Determine if parallel microcode loading is allowed */
135 cmp $0xffffffff, microcode_lock
136 je load_microcode
137
138 /* Protect microcode loading */
139lock_microcode:
140 lock bts $0, microcode_lock
141 jc lock_microcode
142
143load_microcode:
144 /* Load new microcode */
145 mov $MSR_IA32_UCODE_WRITE, %ecx
146 xor %edx, %edx
147 mov %edi, %eax
148 /*
149 * The microcode pointer is passed in pointing to the header. Adjust
150 * pointer to reflect the payload (header size is 48 bytes)
151 */
152 add $UCODE_HEADER_LEN, %eax
153 pusha
154 wrmsr
155 popa
156
157 /* Unconditionally unlock microcode loading */
158 cmp $0xffffffff, microcode_lock
159 je microcode_done
160
161 xor %eax, %eax
162 mov %eax, microcode_lock
163
164microcode_done:
165 /*
166 * Load MSRs. Each entry in the table consists of:
167 * 0: index,
168 * 4: value[31:0]
169 * 8: value[63:32]
170 * See struct saved_msr in mp_init.c.
171 */
172 mov msr_table_ptr, %edi
173 mov msr_count, %ebx
174 test %ebx, %ebx
175 jz 1f
176load_msr:
177 mov (%edi), %ecx
178 mov 4(%edi), %eax
179 mov 8(%edi), %edx
180 wrmsr
181 add $12, %edi
182 dec %ebx
183 jnz load_msr
184
1851:
186 /* Enable caching */
187 mov %cr0, %eax
188 andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax
189 mov %eax, %cr0
190
191 /* c_handler(cpu_num) */
192 movl %esi, %eax /* cpu_num */
193 mov c_handler, %eax
194 call *%eax
195
196 .align 4
197.globl sipi_params
198sipi_params:
199idt_ptr:
200 .long 0
201stack_top:
202 .long 0
203stack_size:
204 .long 0
205microcode_lock:
206 .long 0
207microcode_ptr:
208 .long 0
209msr_table_ptr:
210 .long 0
211msr_count:
212 .long 0
213c_handler:
214 .long 0
215ap_count:
216 .long 0