1 ;------------------------------------------------------------------------------
3 ; Sets the CR3 register for 64-bit paging
5 ; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
6 ; SPDX-License-Identifier: BSD-2-Clause-Patent
8 ;------------------------------------------------------------------------------
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
24 %define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
29 %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
35 %define PAGE_PDP_ATTR (PAGE_ACCESSED + \
40 ; SEV-ES #VC exception handler support
42 ; #VC handler local variable locations
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
53 ; #VC handler total local variable size
55 %define VC_VARIABLE_SIZE 32
57 ; #VC handler GHCB CPUID request/response protocol values
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
65 ; Check if Secure Encrypted Virtualization (SEV) feature is enabled
67 ; Modified: EAX, EBX, ECX, EDX, ESP
69 ; If SEV is enabled then EAX will be at least 32.
70 ; If SEV is disabled then EAX will be zero.
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
80 ; Set up exception handlers to check for SEV-ES
81 ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
83 ; Establish exception handlers
85 mov esp, SEV_ES_VC_TOP_OF_STACK
86 mov eax, ADDR_OF(Idtr)
89 ; Check if we have a valid (0x8000_001F) CPUID leaf
90 ; CPUID raises a #VC exception if running as an SEV-ES guest
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.
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
108 ; Check if memory encryption is enabled
109 ; MSR_0xC0010131 - Bit 0 (SEV enabled)
115 ; Get pte bit position to enable memory encryption
116 ; CPUID Fn8000_001F[EBX] - Bits 5:0
127 ; Clear exception handlers and stack
130 mov eax, ADDR_OF(IdtrClear)
135 OneTimeCallRet CheckSevFeature
137 ; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
140 ; Modified: EAX, EBX, ECX
142 ; If SEV-ES is enabled then EAX will be non-zero.
143 ; If SEV-ES is disabled then EAX will be zero.
148 ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
153 ; Save current value of encryption mask
156 ; Check if SEV-ES is enabled
157 ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
162 ; Restore encryption mask
166 OneTimeCallRet CheckSevEsFeature
169 ; Modified: EAX, EBX, ECX, EDX
171 SetCr3ForPageTables64:
173 OneTimeCall CheckSevFeature
178 ; If SEV is enabled, C-bit is always above 31
185 ; For OVMF, build some initial page tables at
186 ; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000).
188 ; This range should match with PcdOvmfSecPageTablesSize which is
189 ; declared in the FDF files.
191 ; At the end of PEI, the pages tables will be rebuilt into a
192 ; more permanent location by DxeIpl.
195 mov ecx, 6 * 0x1000 / 4
197 clearPageTablesMemoryLoop:
198 mov dword[ecx * 4 + PT_ADDR (0) - 4], eax
199 loop clearPageTablesMemoryLoop
202 ; Top level Page Directory Pointers (1 * 512GB entry)
204 mov dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDP_ATTR
205 mov dword[PT_ADDR (4)], edx
208 ; Next level Page Directory Pointers (4 * 1GB entries => 4GB)
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
220 ; Page Table Entries (2048 * 2MB entries => 4GB)
223 pageTableEntriesLoop:
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
232 OneTimeCall CheckSevEsFeature
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.
241 mov ecx, (GHCB_BASE >> 21)
242 mov eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
243 mov [ecx * 8 + PT_ADDR (0x2000)], eax
246 ; Page Table Entries (512 * 4KB entries => 2MB)
249 pageTableEntries4kLoop:
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
260 ; Clear the encryption bit from the GHCB entry
262 mov ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
263 mov [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
265 mov ecx, GHCB_SIZE / 4
268 mov dword[ecx * 4 + GHCB_BASE - 4], eax
269 loop clearGhcbMemoryLoop
273 ; Set CR3 now that the paging structures are available
278 OneTimeCallRet SetCr3ForPageTables64
281 ; Start of #VC exception handling routines
286 ; Use VMGEXIT to request termination.
287 ; 1 - #VC was not for CPUID
290 jmp SevEsIdtTerminate
292 SevEsIdtNoCpuidResponse:
294 ; Use VMGEXIT to request termination.
295 ; 2 - GHCB_CPUID_RESPONSE not received
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.
304 ; EAX[11:0] => 0x100 - request termination
305 ; EAX[15:12] => 0x1 - OVMF
306 ; EAX[23:16] => 0xXX - REASON CODE
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.
322 ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
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.
336 ; If we're here, then we are an SEV-ES guest and this
337 ; was triggered by a CPUID instruction
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
344 cmp ecx, 0x72 ; Be sure it was CPUID
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
358 ; Save the CPUID function being requested
359 mov [esp + VC_CPUID_FUNCTION], eax
361 ; The GHCB CPUID protocol uses the following mapping to request
362 ; a specific register:
363 ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
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).
368 mov [esp + VC_CPUID_REQUEST_REGISTER], eax
370 ; Save current GHCB MSR value
373 mov [esp + VC_GHCB_MSR_EAX], eax
374 mov [esp + VC_GHCB_MSR_EDX], edx
379 ; GHCB_MSR[63:32] = CPUID function
380 ; GHCB_MSR[31:30] = CPUID register
381 ; GHCB_MSR[11:0] = CPUID request protocol
383 mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
387 shl eax, GHCB_CPUID_REGISTER_SHIFT
388 or eax, GHCB_CPUID_REQUEST
389 mov edx, [esp + VC_CPUID_FUNCTION]
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.
403 ; GHCB_MSR[63:32] = CPUID register value
404 ; GHCB_MSR[31:30] = CPUID register
405 ; GHCB_MSR[11:0] = CPUID response protocol
411 cmp ecx, GHCB_CPUID_RESPONSE
412 jne SevEsIdtNoCpuidResponse
414 ; Save returned value
415 shr eax, GHCB_CPUID_REGISTER_SHIFT
416 mov [esp + eax * 4], edx
419 inc word [esp + VC_CPUID_REQUEST_REGISTER]
425 ; At this point we have all CPUID register values. Restore the GHCB MSR,
426 ; set the return register values and return.
428 mov eax, [esp + VC_GHCB_MSR_EAX]
429 mov edx, [esp + VC_GHCB_MSR_EDX]
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]
438 add esp, VC_VARIABLE_SIZE
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
448 dw IDT_END - IDT_BASE - 1 ; Limit
449 dd ADDR_OF(IDT_BASE) ; Base
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.
466 ; Vectors 0 - 28 (No handlers)
469 dw 0 ; Offset low bits 15..0
472 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
473 dw 0 ; Offset high bits 31..16
476 ; Vector 29 (VMM Communication Exception)
478 dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
481 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
482 dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
484 ; Vectors 30 - 31 (No handlers)
487 dw 0 ; Offset low bits 15..0
490 db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
491 dw 0 ; Offset high bits 31..16