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