xref: /openbmc/u-boot/arch/x86/cpu/sipi_vector.S (revision cf0bcd7d)
1/*
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, %esi
194	call	*%esi
195
196	/* This matches struct sipi_param */
197	.align	4
198.globl	sipi_params
199sipi_params:
200idt_ptr:
201	.long 0
202stack_top:
203	.long 0
204stack_size:
205	.long 0
206microcode_lock:
207	.long 0
208microcode_ptr:
209	.long 0
210msr_table_ptr:
211	.long 0
212msr_count:
213	.long 0
214c_handler:
215	.long 0
216ap_count:
217	.long 0
218