]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/ResetVector/Ia32/PageTables64.asm
EmbeddedPkg/RealTimeClockRuntimeDxe: Use helper functions from TimeBaseLib
[mirror_edk2.git] / OvmfPkg / ResetVector / Ia32 / PageTables64.asm
1 ;------------------------------------------------------------------------------
2 ; @file
3 ; Sets the CR3 register for 64-bit paging
4 ;
5 ; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
6 ; SPDX-License-Identifier: BSD-2-Clause-Patent
7 ;
8 ;------------------------------------------------------------------------------
9
10 BITS 32
11
12 %define PAGE_PRESENT 0x01
13 %define PAGE_READ_WRITE 0x02
14 %define PAGE_USER_SUPERVISOR 0x04
15 %define PAGE_WRITE_THROUGH 0x08
16 %define PAGE_CACHE_DISABLE 0x010
17 %define PAGE_ACCESSED 0x020
18 %define PAGE_DIRTY 0x040
19 %define PAGE_PAT 0x080
20 %define PAGE_GLOBAL 0x0100
21 %define PAGE_2M_MBO 0x080
22 %define PAGE_2M_PAT 0x01000
23
24 %define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
25 PAGE_DIRTY + \
26 PAGE_READ_WRITE + \
27 PAGE_PRESENT)
28
29 %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
30 PAGE_ACCESSED + \
31 PAGE_DIRTY + \
32 PAGE_READ_WRITE + \
33 PAGE_PRESENT)
34
35 %define PAGE_PDP_ATTR (PAGE_ACCESSED + \
36 PAGE_READ_WRITE + \
37 PAGE_PRESENT)
38
39 ;
40 ; SEV-ES #VC exception handler support
41 ;
42 ; #VC handler local variable locations
43 ;
44 %define VC_CPUID_RESULT_EAX 0
45 %define VC_CPUID_RESULT_EBX 4
46 %define VC_CPUID_RESULT_ECX 8
47 %define VC_CPUID_RESULT_EDX 12
48 %define VC_GHCB_MSR_EDX 16
49 %define VC_GHCB_MSR_EAX 20
50 %define VC_CPUID_REQUEST_REGISTER 24
51 %define VC_CPUID_FUNCTION 28
52
53 ; #VC handler total local variable size
54 ;
55 %define VC_VARIABLE_SIZE 32
56
57 ; #VC handler GHCB CPUID request/response protocol values
58 ;
59 %define GHCB_CPUID_REQUEST 4
60 %define GHCB_CPUID_RESPONSE 5
61 %define GHCB_CPUID_REGISTER_SHIFT 30
62 %define CPUID_INSN_LEN 2
63
64
65 ; Check if Secure Encrypted Virtualization (SEV) feature is enabled
66 ;
67 ; Modified: EAX, EBX, ECX, EDX, ESP
68 ;
69 ; If SEV is enabled then EAX will be at least 32.
70 ; If SEV is disabled then EAX will be zero.
71 ;
72 CheckSevFeature:
73 ; Set the first byte of the workarea to zero to communicate to the SEC
74 ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
75 ; instruction will trigger a #VC exception where the first byte of the
76 ; workarea will be set to one.
77 mov byte[SEV_ES_WORK_AREA], 0
78
79 ;
80 ; Set up exception handlers to check for SEV-ES
81 ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
82 ; stack usage)
83 ; Establish exception handlers
84 ;
85 mov esp, SEV_ES_VC_TOP_OF_STACK
86 mov eax, ADDR_OF(Idtr)
87 lidt [cs:eax]
88
89 ; Check if we have a valid (0x8000_001F) CPUID leaf
90 ; CPUID raises a #VC exception if running as an SEV-ES guest
91 mov eax, 0x80000000
92 cpuid
93
94 ; This check should fail on Intel or Non SEV AMD CPUs. In future if
95 ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
96 ; same bit definition.
97 cmp eax, 0x8000001f
98 jl NoSev
99
100 ; Check for memory encryption feature:
101 ; CPUID Fn8000_001F[EAX] - Bit 1
102 ; CPUID raises a #VC exception if running as an SEV-ES guest
103 mov eax, 0x8000001f
104 cpuid
105 bt eax, 1
106 jnc NoSev
107
108 ; Check if memory encryption is enabled
109 ; MSR_0xC0010131 - Bit 0 (SEV enabled)
110 mov ecx, 0xc0010131
111 rdmsr
112 bt eax, 0
113 jnc NoSev
114
115 ; Get pte bit position to enable memory encryption
116 ; CPUID Fn8000_001F[EBX] - Bits 5:0
117 ;
118 mov eax, ebx
119 and eax, 0x3f
120 jmp SevExit
121
122 NoSev:
123 xor eax, eax
124
125 SevExit:
126 ;
127 ; Clear exception handlers and stack
128 ;
129 push eax
130 mov eax, ADDR_OF(IdtrClear)
131 lidt [cs:eax]
132 pop eax
133 mov esp, 0
134
135 OneTimeCallRet CheckSevFeature
136
137 ; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
138 ; is enabled.
139 ;
140 ; Modified: EAX, EBX, ECX
141 ;
142 ; If SEV-ES is enabled then EAX will be non-zero.
143 ; If SEV-ES is disabled then EAX will be zero.
144 ;
145 CheckSevEsFeature:
146 xor eax, eax
147
148 ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
149 ; mask.
150 test edx, edx
151 jz NoSevEs
152
153 ; Save current value of encryption mask
154 mov ebx, edx
155
156 ; Check if SEV-ES is enabled
157 ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
158 mov ecx, 0xc0010131
159 rdmsr
160 and eax, 2
161
162 ; Restore encryption mask
163 mov edx, ebx
164
165 NoSevEs:
166 OneTimeCallRet CheckSevEsFeature
167
168 ;
169 ; Modified: EAX, EBX, ECX, EDX
170 ;
171 SetCr3ForPageTables64:
172
173 OneTimeCall CheckSevFeature
174 xor edx, edx
175 test eax, eax
176 jz SevNotActive
177
178 ; If SEV is enabled, C-bit is always above 31
179 sub eax, 32
180 bts edx, eax
181
182 SevNotActive:
183
184 ;
185 ; For OVMF, build some initial page tables at
186 ; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000).
187 ;
188 ; This range should match with PcdOvmfSecPageTablesSize which is
189 ; declared in the FDF files.
190 ;
191 ; At the end of PEI, the pages tables will be rebuilt into a
192 ; more permanent location by DxeIpl.
193 ;
194
195 mov ecx, 6 * 0x1000 / 4
196 xor eax, eax
197 clearPageTablesMemoryLoop:
198 mov dword[ecx * 4 + PT_ADDR (0) - 4], eax
199 loop clearPageTablesMemoryLoop
200
201 ;
202 ; Top level Page Directory Pointers (1 * 512GB entry)
203 ;
204 mov dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDP_ATTR
205 mov dword[PT_ADDR (4)], edx
206
207 ;
208 ; Next level Page Directory Pointers (4 * 1GB entries => 4GB)
209 ;
210 mov dword[PT_ADDR (0x1000)], PT_ADDR (0x2000) + PAGE_PDP_ATTR
211 mov dword[PT_ADDR (0x1004)], edx
212 mov dword[PT_ADDR (0x1008)], PT_ADDR (0x3000) + PAGE_PDP_ATTR
213 mov dword[PT_ADDR (0x100C)], edx
214 mov dword[PT_ADDR (0x1010)], PT_ADDR (0x4000) + PAGE_PDP_ATTR
215 mov dword[PT_ADDR (0x1014)], edx
216 mov dword[PT_ADDR (0x1018)], PT_ADDR (0x5000) + PAGE_PDP_ATTR
217 mov dword[PT_ADDR (0x101C)], edx
218
219 ;
220 ; Page Table Entries (2048 * 2MB entries => 4GB)
221 ;
222 mov ecx, 0x800
223 pageTableEntriesLoop:
224 mov eax, ecx
225 dec eax
226 shl eax, 21
227 add eax, PAGE_2M_PDE_ATTR
228 mov [ecx * 8 + PT_ADDR (0x2000 - 8)], eax
229 mov [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
230 loop pageTableEntriesLoop
231
232 OneTimeCall CheckSevEsFeature
233 test eax, eax
234 jz SetCr3
235
236 ;
237 ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
238 ; This requires the 2MB page for this range be broken down into 512 4KB
239 ; pages. All will be marked encrypted, except for the GHCB.
240 ;
241 mov ecx, (GHCB_BASE >> 21)
242 mov eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
243 mov [ecx * 8 + PT_ADDR (0x2000)], eax
244
245 ;
246 ; Page Table Entries (512 * 4KB entries => 2MB)
247 ;
248 mov ecx, 512
249 pageTableEntries4kLoop:
250 mov eax, ecx
251 dec eax
252 shl eax, 12
253 add eax, GHCB_BASE & 0xFFE0_0000
254 add eax, PAGE_4K_PDE_ATTR
255 mov [ecx * 8 + GHCB_PT_ADDR - 8], eax
256 mov [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
257 loop pageTableEntries4kLoop
258
259 ;
260 ; Clear the encryption bit from the GHCB entry
261 ;
262 mov ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
263 mov [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
264
265 mov ecx, GHCB_SIZE / 4
266 xor eax, eax
267 clearGhcbMemoryLoop:
268 mov dword[ecx * 4 + GHCB_BASE - 4], eax
269 loop clearGhcbMemoryLoop
270
271 SetCr3:
272 ;
273 ; Set CR3 now that the paging structures are available
274 ;
275 mov eax, PT_ADDR (0)
276 mov cr3, eax
277
278 OneTimeCallRet SetCr3ForPageTables64
279
280 ;
281 ; Start of #VC exception handling routines
282 ;
283
284 SevEsIdtNotCpuid:
285 ;
286 ; Use VMGEXIT to request termination.
287 ; 1 - #VC was not for CPUID
288 ;
289 mov eax, 1
290 jmp SevEsIdtTerminate
291
292 SevEsIdtNoCpuidResponse:
293 ;
294 ; Use VMGEXIT to request termination.
295 ; 2 - GHCB_CPUID_RESPONSE not received
296 ;
297 mov eax, 2
298
299 SevEsIdtTerminate:
300 ;
301 ; Use VMGEXIT to request termination. At this point the reason code is
302 ; located in EAX, so shift it left 16 bits to the proper location.
303 ;
304 ; EAX[11:0] => 0x100 - request termination
305 ; EAX[15:12] => 0x1 - OVMF
306 ; EAX[23:16] => 0xXX - REASON CODE
307 ;
308 shl eax, 16
309 or eax, 0x1100
310 xor edx, edx
311 mov ecx, 0xc0010130
312 wrmsr
313 ;
314 ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
315 ; mode, so work around this by temporarily switching to 64-bit mode.
316 ;
317 BITS 64
318 rep vmmcall
319 BITS 32
320
321 ;
322 ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
323 ;
324 SevEsIdtHlt:
325 hlt
326 jmp SevEsIdtHlt
327 iret
328
329 ;
330 ; Total stack usage for the #VC handler is 44 bytes:
331 ; - 12 bytes for the exception IRET (after popping error code)
332 ; - 32 bytes for the local variables.
333 ;
334 SevEsIdtVmmComm:
335 ;
336 ; If we're here, then we are an SEV-ES guest and this
337 ; was triggered by a CPUID instruction
338 ;
339 ; Set the first byte of the workarea to one to communicate to the SEC
340 ; phase that SEV-ES is enabled.
341 mov byte[SEV_ES_WORK_AREA], 1
342
343 pop ecx ; Error code
344 cmp ecx, 0x72 ; Be sure it was CPUID
345 jne SevEsIdtNotCpuid
346
347 ; Set up local variable room on the stack
348 ; CPUID function : + 28
349 ; CPUID request register : + 24
350 ; GHCB MSR (EAX) : + 20
351 ; GHCB MSR (EDX) : + 16
352 ; CPUID result (EDX) : + 12
353 ; CPUID result (ECX) : + 8
354 ; CPUID result (EBX) : + 4
355 ; CPUID result (EAX) : + 0
356 sub esp, VC_VARIABLE_SIZE
357
358 ; Save the CPUID function being requested
359 mov [esp + VC_CPUID_FUNCTION], eax
360
361 ; The GHCB CPUID protocol uses the following mapping to request
362 ; a specific register:
363 ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
364 ;
365 ; Set EAX as the first register to request. This will also be used as a
366 ; loop variable to request all register values (EAX to EDX).
367 xor eax, eax
368 mov [esp + VC_CPUID_REQUEST_REGISTER], eax
369
370 ; Save current GHCB MSR value
371 mov ecx, 0xc0010130
372 rdmsr
373 mov [esp + VC_GHCB_MSR_EAX], eax
374 mov [esp + VC_GHCB_MSR_EDX], edx
375
376 NextReg:
377 ;
378 ; Setup GHCB MSR
379 ; GHCB_MSR[63:32] = CPUID function
380 ; GHCB_MSR[31:30] = CPUID register
381 ; GHCB_MSR[11:0] = CPUID request protocol
382 ;
383 mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
384 cmp eax, 4
385 jge VmmDone
386
387 shl eax, GHCB_CPUID_REGISTER_SHIFT
388 or eax, GHCB_CPUID_REQUEST
389 mov edx, [esp + VC_CPUID_FUNCTION]
390 mov ecx, 0xc0010130
391 wrmsr
392
393 ;
394 ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
395 ; mode, so work around this by temporarily switching to 64-bit mode.
396 ;
397 BITS 64
398 rep vmmcall
399 BITS 32
400
401 ;
402 ; Read GHCB MSR
403 ; GHCB_MSR[63:32] = CPUID register value
404 ; GHCB_MSR[31:30] = CPUID register
405 ; GHCB_MSR[11:0] = CPUID response protocol
406 ;
407 mov ecx, 0xc0010130
408 rdmsr
409 mov ecx, eax
410 and ecx, 0xfff
411 cmp ecx, GHCB_CPUID_RESPONSE
412 jne SevEsIdtNoCpuidResponse
413
414 ; Save returned value
415 shr eax, GHCB_CPUID_REGISTER_SHIFT
416 mov [esp + eax * 4], edx
417
418 ; Next register
419 inc word [esp + VC_CPUID_REQUEST_REGISTER]
420
421 jmp NextReg
422
423 VmmDone:
424 ;
425 ; At this point we have all CPUID register values. Restore the GHCB MSR,
426 ; set the return register values and return.
427 ;
428 mov eax, [esp + VC_GHCB_MSR_EAX]
429 mov edx, [esp + VC_GHCB_MSR_EDX]
430 mov ecx, 0xc0010130
431 wrmsr
432
433 mov eax, [esp + VC_CPUID_RESULT_EAX]
434 mov ebx, [esp + VC_CPUID_RESULT_EBX]
435 mov ecx, [esp + VC_CPUID_RESULT_ECX]
436 mov edx, [esp + VC_CPUID_RESULT_EDX]
437
438 add esp, VC_VARIABLE_SIZE
439
440 ; Update the EIP value to skip over the now handled CPUID instruction
441 ; (the CPUID instruction has a length of 2)
442 add word [esp], CPUID_INSN_LEN
443 iret
444
445 ALIGN 2
446
447 Idtr:
448 dw IDT_END - IDT_BASE - 1 ; Limit
449 dd ADDR_OF(IDT_BASE) ; Base
450
451 IdtrClear:
452 dw 0 ; Limit
453 dd 0 ; Base
454
455 ALIGN 16
456
457 ;
458 ; The Interrupt Descriptor Table (IDT)
459 ; This will be used to determine if SEV-ES is enabled. Upon execution
460 ; of the CPUID instruction, a VMM Communication Exception will occur.
461 ; This will tell us if SEV-ES is enabled. We can use the current value
462 ; of the GHCB MSR to determine the SEV attributes.
463 ;
464 IDT_BASE:
465 ;
466 ; Vectors 0 - 28 (No handlers)
467 ;
468 %rep 29
469 dw 0 ; Offset low bits 15..0
470 dw 0x10 ; Selector
471 db 0 ; Reserved
472 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
473 dw 0 ; Offset high bits 31..16
474 %endrep
475 ;
476 ; Vector 29 (VMM Communication Exception)
477 ;
478 dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
479 dw 0x10 ; Selector
480 db 0 ; Reserved
481 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
482 dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
483 ;
484 ; Vectors 30 - 31 (No handlers)
485 ;
486 %rep 2
487 dw 0 ; Offset low bits 15..0
488 dw 0x10 ; Selector
489 db 0 ; Reserved
490 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
491 dw 0 ; Offset high bits 31..16
492 %endrep
493 IDT_END: