1 ;------------------------------------------------------------------------------
3 ; Sets the CR3 register for 64-bit paging
5 ; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
6 ; Copyright (c) 2017 - 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
7 ; SPDX-License-Identifier: BSD-2-Clause-Patent
9 ;------------------------------------------------------------------------------
13 %define PAGE_PRESENT 0x01
14 %define PAGE_READ_WRITE 0x02
15 %define PAGE_USER_SUPERVISOR 0x04
16 %define PAGE_WRITE_THROUGH 0x08
17 %define PAGE_CACHE_DISABLE 0x010
18 %define PAGE_ACCESSED 0x020
19 %define PAGE_DIRTY 0x040
20 %define PAGE_PAT 0x080
21 %define PAGE_GLOBAL 0x0100
22 %define PAGE_2M_MBO 0x080
23 %define PAGE_2M_PAT 0x01000
25 %define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
30 %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
36 %define PAGE_PDP_ATTR (PAGE_ACCESSED + \
41 ; SEV-ES #VC exception handler support
43 ; #VC handler local variable locations
45 %define VC_CPUID_RESULT_EAX 0
46 %define VC_CPUID_RESULT_EBX 4
47 %define VC_CPUID_RESULT_ECX 8
48 %define VC_CPUID_RESULT_EDX 12
49 %define VC_GHCB_MSR_EDX 16
50 %define VC_GHCB_MSR_EAX 20
51 %define VC_CPUID_REQUEST_REGISTER 24
52 %define VC_CPUID_FUNCTION 28
54 ; #VC handler total local variable size
56 %define VC_VARIABLE_SIZE 32
58 ; #VC handler GHCB CPUID request/response protocol values
60 %define GHCB_CPUID_REQUEST 4
61 %define GHCB_CPUID_RESPONSE 5
62 %define GHCB_CPUID_REGISTER_SHIFT 30
63 %define CPUID_INSN_LEN 2
66 ; Check if Secure Encrypted Virtualization (SEV) features are enabled.
68 ; Register usage is tight in this routine, so multiple calls for the
69 ; same CPUID and MSR data are performed to keep things simple.
71 ; Modified: EAX, EBX, ECX, EDX, ESP
73 ; If SEV is enabled then EAX will be at least 32.
74 ; If SEV is disabled then EAX will be zero.
77 ; Set the first byte of the workarea to zero to communicate to the SEC
78 ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
79 ; instruction will trigger a #VC exception where the first byte of the
80 ; workarea will be set to one or, if CPUID is not being intercepted,
81 ; the MSR check below will set the first byte of the workarea to one.
82 mov byte[SEV_ES_WORK_AREA], 0
85 ; Set up exception handlers to check for SEV-ES
86 ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
88 ; Establish exception handlers
90 mov esp, SEV_ES_VC_TOP_OF_STACK
91 mov eax, ADDR_OF(Idtr)
94 ; Check if we have a valid (0x8000_001F) CPUID leaf
95 ; CPUID raises a #VC exception if running as an SEV-ES guest
99 ; This check should fail on Intel or Non SEV AMD CPUs. In future if
100 ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
101 ; same bit definition.
105 ; Check for SEV memory encryption feature:
106 ; CPUID Fn8000_001F[EAX] - Bit 1
107 ; CPUID raises a #VC exception if running as an SEV-ES guest
113 ; Check if SEV memory encryption is enabled
114 ; MSR_0xC0010131 - Bit 0 (SEV enabled)
120 ; Check for SEV-ES memory encryption feature:
121 ; CPUID Fn8000_001F[EAX] - Bit 3
122 ; CPUID raises a #VC exception if running as an SEV-ES guest
128 ; Check if SEV-ES is enabled
129 ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
135 ; Set the first byte of the workarea to one to communicate to the SEC
136 ; phase that SEV-ES is enabled.
137 mov byte[SEV_ES_WORK_AREA], 1
140 ; Get pte bit position to enable memory encryption
141 ; CPUID Fn8000_001F[EBX] - Bits 5:0
146 ; The encryption bit position is always above 31
150 ; Encryption bit was reported as 31 or below, enter a HLT loop
161 ; Clear exception handlers and stack
164 mov eax, ADDR_OF(IdtrClear)
169 OneTimeCallRet CheckSevFeatures
171 ; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
176 ; If SEV-ES is enabled then EAX will be non-zero.
177 ; If SEV-ES is disabled then EAX will be zero.
182 ; During CheckSevFeatures, the SEV_ES_WORK_AREA was set to 1 if
184 cmp byte[SEV_ES_WORK_AREA], 1
190 OneTimeCallRet IsSevEsEnabled
193 ; Modified: EAX, EBX, ECX, EDX
195 SetCr3ForPageTables64:
197 OneTimeCall CheckSevFeatures
202 ; If SEV is enabled, C-bit is always above 31
209 ; For OVMF, build some initial page tables at
210 ; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000).
212 ; This range should match with PcdOvmfSecPageTablesSize which is
213 ; declared in the FDF files.
215 ; At the end of PEI, the pages tables will be rebuilt into a
216 ; more permanent location by DxeIpl.
219 mov ecx, 6 * 0x1000 / 4
221 clearPageTablesMemoryLoop:
222 mov dword[ecx * 4 + PT_ADDR (0) - 4], eax
223 loop clearPageTablesMemoryLoop
226 ; Top level Page Directory Pointers (1 * 512GB entry)
228 mov dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDP_ATTR
229 mov dword[PT_ADDR (4)], edx
232 ; Next level Page Directory Pointers (4 * 1GB entries => 4GB)
234 mov dword[PT_ADDR (0x1000)], PT_ADDR (0x2000) + PAGE_PDP_ATTR
235 mov dword[PT_ADDR (0x1004)], edx
236 mov dword[PT_ADDR (0x1008)], PT_ADDR (0x3000) + PAGE_PDP_ATTR
237 mov dword[PT_ADDR (0x100C)], edx
238 mov dword[PT_ADDR (0x1010)], PT_ADDR (0x4000) + PAGE_PDP_ATTR
239 mov dword[PT_ADDR (0x1014)], edx
240 mov dword[PT_ADDR (0x1018)], PT_ADDR (0x5000) + PAGE_PDP_ATTR
241 mov dword[PT_ADDR (0x101C)], edx
244 ; Page Table Entries (2048 * 2MB entries => 4GB)
247 pageTableEntriesLoop:
251 add eax, PAGE_2M_PDE_ATTR
252 mov [ecx * 8 + PT_ADDR (0x2000 - 8)], eax
253 mov [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
254 loop pageTableEntriesLoop
256 OneTimeCall IsSevEsEnabled
261 ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
262 ; This requires the 2MB page for this range be broken down into 512 4KB
263 ; pages. All will be marked encrypted, except for the GHCB.
265 mov ecx, (GHCB_BASE >> 21)
266 mov eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
267 mov [ecx * 8 + PT_ADDR (0x2000)], eax
270 ; Page Table Entries (512 * 4KB entries => 2MB)
273 pageTableEntries4kLoop:
277 add eax, GHCB_BASE & 0xFFE0_0000
278 add eax, PAGE_4K_PDE_ATTR
279 mov [ecx * 8 + GHCB_PT_ADDR - 8], eax
280 mov [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
281 loop pageTableEntries4kLoop
284 ; Clear the encryption bit from the GHCB entry
286 mov ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
287 mov [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
289 mov ecx, GHCB_SIZE / 4
292 mov dword[ecx * 4 + GHCB_BASE - 4], eax
293 loop clearGhcbMemoryLoop
297 ; Set CR3 now that the paging structures are available
302 OneTimeCallRet SetCr3ForPageTables64
305 ; Start of #VC exception handling routines
310 ; Use VMGEXIT to request termination.
311 ; 1 - #VC was not for CPUID
314 jmp SevEsIdtTerminate
316 SevEsIdtNoCpuidResponse:
318 ; Use VMGEXIT to request termination.
319 ; 2 - GHCB_CPUID_RESPONSE not received
325 ; Use VMGEXIT to request termination. At this point the reason code is
326 ; located in EAX, so shift it left 16 bits to the proper location.
328 ; EAX[11:0] => 0x100 - request termination
329 ; EAX[15:12] => 0x1 - OVMF
330 ; EAX[23:16] => 0xXX - REASON CODE
338 ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
339 ; mode, so work around this by temporarily switching to 64-bit mode.
346 ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
354 ; Total stack usage for the #VC handler is 44 bytes:
355 ; - 12 bytes for the exception IRET (after popping error code)
356 ; - 32 bytes for the local variables.
360 ; If we're here, then we are an SEV-ES guest and this
361 ; was triggered by a CPUID instruction
363 ; Set the first byte of the workarea to one to communicate that
365 mov byte[SEV_ES_WORK_AREA], 1
368 cmp ecx, 0x72 ; Be sure it was CPUID
371 ; Set up local variable room on the stack
372 ; CPUID function : + 28
373 ; CPUID request register : + 24
374 ; GHCB MSR (EAX) : + 20
375 ; GHCB MSR (EDX) : + 16
376 ; CPUID result (EDX) : + 12
377 ; CPUID result (ECX) : + 8
378 ; CPUID result (EBX) : + 4
379 ; CPUID result (EAX) : + 0
380 sub esp, VC_VARIABLE_SIZE
382 ; Save the CPUID function being requested
383 mov [esp + VC_CPUID_FUNCTION], eax
385 ; The GHCB CPUID protocol uses the following mapping to request
386 ; a specific register:
387 ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
389 ; Set EAX as the first register to request. This will also be used as a
390 ; loop variable to request all register values (EAX to EDX).
392 mov [esp + VC_CPUID_REQUEST_REGISTER], eax
394 ; Save current GHCB MSR value
397 mov [esp + VC_GHCB_MSR_EAX], eax
398 mov [esp + VC_GHCB_MSR_EDX], edx
403 ; GHCB_MSR[63:32] = CPUID function
404 ; GHCB_MSR[31:30] = CPUID register
405 ; GHCB_MSR[11:0] = CPUID request protocol
407 mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
411 shl eax, GHCB_CPUID_REGISTER_SHIFT
412 or eax, GHCB_CPUID_REQUEST
413 mov edx, [esp + VC_CPUID_FUNCTION]
418 ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
419 ; mode, so work around this by temporarily switching to 64-bit mode.
427 ; GHCB_MSR[63:32] = CPUID register value
428 ; GHCB_MSR[31:30] = CPUID register
429 ; GHCB_MSR[11:0] = CPUID response protocol
435 cmp ecx, GHCB_CPUID_RESPONSE
436 jne SevEsIdtNoCpuidResponse
438 ; Save returned value
439 shr eax, GHCB_CPUID_REGISTER_SHIFT
440 mov [esp + eax * 4], edx
443 inc word [esp + VC_CPUID_REQUEST_REGISTER]
449 ; At this point we have all CPUID register values. Restore the GHCB MSR,
450 ; set the return register values and return.
452 mov eax, [esp + VC_GHCB_MSR_EAX]
453 mov edx, [esp + VC_GHCB_MSR_EDX]
457 mov eax, [esp + VC_CPUID_RESULT_EAX]
458 mov ebx, [esp + VC_CPUID_RESULT_EBX]
459 mov ecx, [esp + VC_CPUID_RESULT_ECX]
460 mov edx, [esp + VC_CPUID_RESULT_EDX]
462 add esp, VC_VARIABLE_SIZE
464 ; Update the EIP value to skip over the now handled CPUID instruction
465 ; (the CPUID instruction has a length of 2)
466 add word [esp], CPUID_INSN_LEN
472 dw IDT_END - IDT_BASE - 1 ; Limit
473 dd ADDR_OF(IDT_BASE) ; Base
482 ; The Interrupt Descriptor Table (IDT)
483 ; This will be used to determine if SEV-ES is enabled. Upon execution
484 ; of the CPUID instruction, a VMM Communication Exception will occur.
485 ; This will tell us if SEV-ES is enabled. We can use the current value
486 ; of the GHCB MSR to determine the SEV attributes.
490 ; Vectors 0 - 28 (No handlers)
493 dw 0 ; Offset low bits 15..0
496 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
497 dw 0 ; Offset high bits 31..16
500 ; Vector 29 (VMM Communication Exception)
502 dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
505 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
506 dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
508 ; Vectors 30 - 31 (No handlers)
511 dw 0 ; Offset low bits 15..0
514 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
515 dw 0 ; Offset high bits 31..16