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