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