xref: /aosp_15_r20/external/coreboot/src/cpu/x86/smm/smm_stub.S (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1/* SPDX-License-Identifier: GPL-2.0-only */
2
3/*
4 * The stub is a generic wrapper for bootstrapping a C-based SMM handler. Its
5 * primary purpose is to put the CPU into protected mode with a stack and call
6 * into the C handler.
7 *
8 * The stub_entry_params structure needs to correspond to the C structure
9 * found in smm.h.
10 */
11
12#include <cpu/x86/cr.h>
13#include <cpu/x86/msr.h>
14#include <cpu/x86/lapic_def.h>
15#include <cpu/x86/64bit/entry64.inc>
16
17.code32
18.section ".module_parameters", "aw", @progbits
19stub_entry_params:
20stack_size:
21.long 0
22stack_top:
23.long 0
24c_handler:
25.long 0
26cr3:
27.long 0
28/* apic_to_cpu_num is a table mapping the default APIC id to CPU num. If the
29 * APIC id is found at the given index, the contiguous CPU number is index
30 * into the table. */
31apic_to_cpu_num:
32.fill CONFIG_MAX_CPUS,2,0xffff
33
34.data
35/* Provide fallback stack to use when a valid CPU number cannot be found. */
36fallback_stack_bottom:
37.skip 128
38fallback_stack_top:
39
40#define CR0_CLEAR_FLAGS \
41	(CR0_CD | CR0_NW | CR0_PG | CR0_AM | CR0_WP | \
42	 CR0_NE | CR0_TS | CR0_EM | CR0_MP)
43
44#define SMM_DEFAULT_SIZE 0x10000
45
46.text
47.code16
48.global _start
49_start:
50smm_handler_start:
51#if CONFIG(SMM_LAPIC_REMAP_MITIGATION)
52	/* Check if the LAPIC register block overlaps with the stub.
53	 * This block needs to work without data accesses because they
54	 * may be routed into the LAPIC register block.
55	 * Code accesses, on the other hand, are never routed to LAPIC,
56	 * which is what makes this work in the first place.
57	 */
58	mov	$LAPIC_BASE_MSR, %ecx
59	rdmsr
60	and	$(~0xfff), %eax
61	call	1f
62	/* Get the current program counter */
631:
64	pop	%ebx
65	sub	%ebx, %eax
66	cmp	$(SMM_DEFAULT_SIZE), %eax
67	ja	untampered_lapic
681:
69#if CONFIG(CONSOLE_SERIAL)
70	/* emit "Crash" on serial */
71	mov	$(CONFIG_TTYS0_BASE), %dx
72	mov	$'C', %al
73	out	%al, (%dx)
74	mov	$'r', %al
75	out	%al, (%dx)
76	mov	$'a', %al
77	out	%al, (%dx)
78	mov	$'s', %al
79	out	%al, (%dx)
80	mov	$'h', %al
81	out	%al, (%dx)
82#endif /* CONFIG_CONSOLE_SERIAL */
83	/* now crash for real */
84	ud2
85untampered_lapic:
86#endif
87	movl	$(smm_relocate_gdt), %ebx
88	lgdtl	(%ebx)
89
90	movl	%cr0, %eax
91	andl	$~CR0_CLEAR_FLAGS, %eax
92	orl	$CR0_PE, %eax
93	movl	%eax, %cr0
94
95	/* Enable protected mode */
96	ljmpl	$0x8, $smm_trampoline32
97
98.align 4
99smm_relocate_gdt:
100	/* The first GDT entry is used for the lgdt instruction. */
101	.word	smm_relocate_gdt_end - smm_relocate_gdt - 1
102	.long	smm_relocate_gdt
103	.word	0x0000
104
105	/* gdt selector 0x08, flat code segment */
106	.word	0xffff, 0x0000
107	.byte	0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, 4GB limit */
108
109	/* gdt selector 0x10, flat data segment */
110	.word	0xffff, 0x0000
111	.byte	0x00, 0x93, 0xcf, 0x00
112
113	/* gdt selector 0x18, flat code segment (64-bit) */
114	.word	0xffff, 0x0000
115	.byte	0x00, 0x9b, 0xaf, 0x00
116
117	/* gdt selector 0x20 tss segment */
118	.word   0xffff, 0x0000
119	.byte   0x00, 0x8b, 0x80, 0x00
120smm_relocate_gdt_end:
121
122.align 4
123.code32
124.global smm_trampoline32
125smm_trampoline32:
126	/* Use flat data segment */
127	movw	$0x10, %ax
128	movw	%ax, %ds
129	movw	%ax, %es
130	movw	%ax, %ss
131	xor	%ax, %ax /* zero out the gs and fs segment index */
132	movw	%ax, %fs
133	movw	%ax, %gs /* Used by cpu_info in ramstage */
134
135	/* The CPU number is calculated by reading the initial APIC id. Since
136	 * the OS can manipulate the APIC id use the non-changing cpuid result
137	 * for APIC id (eax). A table is used to handle a discontiguous
138	 * APIC id space.  */
139apic_id:
140	mov	$LAPIC_BASE_MSR, %ecx
141	rdmsr
142	and	$LAPIC_BASE_X2APIC_ENABLED, %eax
143	cmp	$LAPIC_BASE_X2APIC_ENABLED, %eax
144	jne	xapic
145
146x2apic:
147	mov	$0xb, %eax
148	mov	$0, %ecx
149	cpuid
150	mov	%edx, %eax
151	jmp	apicid_end
152
153xapic:
154	mov	$1, %eax
155	cpuid
156	mov	%ebx, %eax
157	shr	$24, %eax
158
159apicid_end:
160	mov	$(apic_to_cpu_num), %ebx
161	xor	%ecx, %ecx
162
1631:
164	cmp	(%ebx, %ecx, 2), %ax
165	je	1f
166	inc	%ecx
167	cmp	$CONFIG_MAX_CPUS, %ecx
168	jne	1b
169	/* This is bad. One cannot find a stack entry because a CPU num could
170	 * not be assigned. Use the fallback stack and check this condition in
171	 * C handler. */
172	movl	$(fallback_stack_top), %esp
173	jmp	align_stack
1741:
175	movl	stack_size, %eax
176	mul	%ecx /* %eax(stack_size) * %ecx(cpu) = %eax(offset) */
177	movl	stack_top, %ebx
178	subl	%eax, %ebx /* global_stack_top - offset = stack_top */
179	mov	%ebx, %esp
180
181	/* Write canary to the bottom of the stack */
182	movl	stack_size, %eax
183	subl	%eax, %ebx /* %ebx(stack_top) - size = %ebx(stack_bottom) */
184	movl	%ebx, (%ebx)
185#if ENV_X86_64
186	movl	$0, 4(%ebx)
187#endif
188
189align_stack:
190	/* Align stack to 16 bytes. Another 32 bytes are pushed below. */
191	andl	$0xfffffff0, %esp
192
193	/* Call into the c-based SMM relocation function with the platform
194	 * parameters. Equivalent to:
195	 *   struct arg = { cpu_num, canary };
196	 *   c_handler(&arg)
197	 */
198#if ENV_X86_64
199	mov	%ecx, %edi
200	/* entry64.inc preserves ebx, esi, edi, ebp */
201	setup_longmode cr3
202	mov	%edi, %ecx
203
204
205	push	%rbx /* uintptr_t *canary */
206	push	%rcx /* size_t cpu */
207
208	mov	%rsp, %rdi	/* *arg */
209
210	movabs	c_handler, %eax
211	call	*%rax
212#else
213	push	$0x0 /* Padding */
214	push	%ebx /* uintptr_t *canary */
215	push	%ecx /* size_t cpu */
216	push	%esp /* smm_module_params *arg (allocated on stack). */
217	mov	c_handler, %eax
218	call	*%eax
219#endif
220
2211:
222	/* Exit from SM mode. */
223	rsm
224