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/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 */
133	cmpl	$0xffffffff, microcode_lock
134	je	load_microcode
135
136	/* Protect microcode loading */
137lock_microcode:
138	lock btsl $0, microcode_lock
139	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 */
156	cmpl	$0xffffffff, microcode_lock
157	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 */
191	mov	c_handler, %esi
192	call	*%esi
193
194	/* This matches struct sipi_param */
195	.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
216