1 /* SPDX-License-Identifier: GPL-2.0-only */
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
8 * The stub_entry_params structure needs to correspond to the C structure
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>
18 .section ".module_parameters", "aw", @progbits
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
32 .fill CONFIG_MAX_CPUS,2,0xffff
35 /* Provide fallback stack to use when a valid CPU number cannot be found. */
36 fallback_stack_bottom:
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)
44 #define SMM_DEFAULT_SIZE 0x10000
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.
58 mov $LAPIC_BASE_MSR, %ecx
62 /* Get the current program counter */
66 cmp $(SMM_DEFAULT_SIZE), %eax
69 #if CONFIG(CONSOLE_SERIAL)
70 /* emit "Crash" on serial */
71 mov $(CONFIG_TTYS0_BASE), %dx
82 #endif /* CONFIG_CONSOLE_SERIAL */
83 /* now crash for real */
87 movl $(smm_relocate_gdt), %ebx
91 andl $~CR0_CLEAR_FLAGS, %eax
95 /* Enable protected mode */
96 ljmpl $0x8, $smm_trampoline32
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
105 /* gdt selector 0x08, flat code segment */
107 .byte 0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, 4GB limit */
109 /* gdt selector 0x10, flat data segment */
111 .byte 0x00, 0x93, 0xcf, 0x00
113 /* gdt selector 0x18, flat code segment (64-bit) */
115 .byte 0x00, 0x9b, 0xaf, 0x00
117 /* gdt selector 0x20 tss segment */
119 .byte 0x00, 0x8b, 0x80, 0x00
120 smm_relocate_gdt_end:
124 .global smm_trampoline32
126 /* Use flat data segment */
131 xor %ax, %ax /* zero out the gs and fs segment index */
133 movw %ax, %gs /* Used by cpu_info in ramstage */
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
140 mov $LAPIC_BASE_MSR, %ecx
142 and $LAPIC_BASE_X2APIC_ENABLED, %eax
143 cmp $LAPIC_BASE_X2APIC_ENABLED, %eax
160 mov $(apic_to_cpu_num), %ebx
164 cmp (%ebx, %ecx, 2), %ax
167 cmp $CONFIG_MAX_CPUS, %ecx
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
172 movl $(fallback_stack_top), %esp
175 movl stack_size, %eax
176 mul %ecx /* %eax(stack_size) * %ecx(cpu) = %eax(offset) */
178 subl %eax, %ebx /* global_stack_top - offset = stack_top */
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) */
190 /* Align stack to 16 bytes. Another 32 bytes are pushed below. */
191 andl $0xfffffff0, %esp
193 /* Call into the c-based SMM relocation function with the platform
194 * parameters. Equivalent to:
195 * struct arg = { cpu_num, canary };
200 /* entry64.inc preserves ebx, esi, edi, ebp */
205 push %rbx /* uintptr_t *canary */
206 push %rcx /* size_t cpu */
208 mov %rsp, %rdi /* *arg */
210 movabs c_handler, %eax
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). */
222 /* Exit from SM mode. */