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