cpu/x86/(sipi|smm): Pass on CR3 from ramstage
[coreboot2.git] / src / cpu / x86 / smm / smm_stub.S
blob9b4b966f7c447ba2a115645b82eb8bff1bd500b8
1 /* SPDX-License-Identifier: GPL-2.0-only */
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  */
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>
17 .code32
18 .section ".module_parameters", "aw", @progbits
19 stub_entry_params:
20 stack_size:
21 .long 0
22 stack_top:
23 .long 0
24 c_handler:
25 .long 0
26 cr3:
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. */
31 apic_to_cpu_num:
32 .fill CONFIG_MAX_CPUS,2,0xffff
34 .data
35 /* Provide fallback stack to use when a valid CPU number cannot be found. */
36 fallback_stack_bottom:
37 .skip 128
38 fallback_stack_top:
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
46 .text
47 .code16
48 .global _start
49 _start:
50 smm_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 */
64         pop     %ebx
65         sub     %ebx, %eax
66         cmp     $(SMM_DEFAULT_SIZE), %eax
67         ja      untampered_lapic
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
85 untampered_lapic:
86 #endif
87         movl    $(smm_relocate_gdt), %ebx
88         lgdtl   (%ebx)
90         movl    %cr0, %eax
91         andl    $~CR0_CLEAR_FLAGS, %eax
92         orl     $CR0_PE, %eax
93         movl    %eax, %cr0
95         /* Enable protected mode */
96         ljmpl   $0x8, $smm_trampoline32
98 .align 4
99 smm_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
105         /* gdt selector 0x08, flat code segment */
106         .word   0xffff, 0x0000
107         .byte   0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, 4GB limit */
109         /* gdt selector 0x10, flat data segment */
110         .word   0xffff, 0x0000
111         .byte   0x00, 0x93, 0xcf, 0x00
113         /* gdt selector 0x18, flat code segment (64-bit) */
114         .word   0xffff, 0x0000
115         .byte   0x00, 0x9b, 0xaf, 0x00
117         /* gdt selector 0x20 tss segment */
118         .word   0xffff, 0x0000
119         .byte   0x00, 0x8b, 0x80, 0x00
120 smm_relocate_gdt_end:
122 .align 4
123 .code32
124 .global smm_trampoline32
125 smm_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 */
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.  */
139 apic_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
146 x2apic:
147         mov     $0xb, %eax
148         mov     $0, %ecx
149         cpuid
150         mov     %edx, %eax
151         jmp     apicid_end
153 xapic:
154         mov     $1, %eax
155         cpuid
156         mov     %ebx, %eax
157         shr     $24, %eax
159 apicid_end:
160         mov     $(apic_to_cpu_num), %ebx
161         xor     %ecx, %ecx
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
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
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
189 align_stack:
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 };
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
205         push    %rbx /* uintptr_t *canary */
206         push    %rcx /* size_t cpu */
208         mov     %rsp, %rdi      /* *arg */
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
222         /* Exit from SM mode. */
223         rsm