PAGE_READ_WRITE + \\r
PAGE_PRESENT)\r
\r
-;\r
-; SEV-ES #VC exception handler support\r
-;\r
-; #VC handler local variable locations\r
-;\r
-%define VC_CPUID_RESULT_EAX 0\r
-%define VC_CPUID_RESULT_EBX 4\r
-%define VC_CPUID_RESULT_ECX 8\r
-%define VC_CPUID_RESULT_EDX 12\r
-%define VC_GHCB_MSR_EDX 16\r
-%define VC_GHCB_MSR_EAX 20\r
-%define VC_CPUID_REQUEST_REGISTER 24\r
-%define VC_CPUID_FUNCTION 28\r
-\r
-; #VC handler total local variable size\r
-;\r
-%define VC_VARIABLE_SIZE 32\r
-\r
-; #VC handler GHCB CPUID request/response protocol values\r
-;\r
-%define GHCB_CPUID_REQUEST 4\r
-%define GHCB_CPUID_RESPONSE 5\r
-%define GHCB_CPUID_REGISTER_SHIFT 30\r
-%define CPUID_INSN_LEN 2\r
-\r
-\r
-; Check if Secure Encrypted Virtualization (SEV) features are enabled.\r
-;\r
-; Register usage is tight in this routine, so multiple calls for the\r
-; same CPUID and MSR data are performed to keep things simple.\r
-;\r
-; Modified: EAX, EBX, ECX, EDX, ESP\r
-;\r
-; If SEV is enabled then EAX will be at least 32.\r
-; If SEV is disabled then EAX will be zero.\r
-;\r
-CheckSevFeatures:\r
- ; Set the first byte of the workarea to zero to communicate to the SEC\r
- ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID\r
- ; instruction will trigger a #VC exception where the first byte of the\r
- ; workarea will be set to one or, if CPUID is not being intercepted,\r
- ; the MSR check below will set the first byte of the workarea to one.\r
- mov byte[SEV_ES_WORK_AREA], 0\r
-\r
- ;\r
- ; Set up exception handlers to check for SEV-ES\r
- ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for\r
- ; stack usage)\r
- ; Establish exception handlers\r
- ;\r
- mov esp, SEV_ES_VC_TOP_OF_STACK\r
- mov eax, ADDR_OF(Idtr)\r
- lidt [cs:eax]\r
-\r
- ; Check if we have a valid (0x8000_001F) CPUID leaf\r
- ; CPUID raises a #VC exception if running as an SEV-ES guest\r
- mov eax, 0x80000000\r
- cpuid\r
-\r
- ; This check should fail on Intel or Non SEV AMD CPUs. In future if\r
- ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact\r
- ; same bit definition.\r
- cmp eax, 0x8000001f\r
- jl NoSev\r
-\r
- ; Check for SEV memory encryption feature:\r
- ; CPUID Fn8000_001F[EAX] - Bit 1\r
- ; CPUID raises a #VC exception if running as an SEV-ES guest\r
- mov eax, 0x8000001f\r
- cpuid\r
- bt eax, 1\r
- jnc NoSev\r
-\r
- ; Check if SEV memory encryption is enabled\r
- ; MSR_0xC0010131 - Bit 0 (SEV enabled)\r
- mov ecx, 0xc0010131\r
- rdmsr\r
- bt eax, 0\r
- jnc NoSev\r
-\r
- ; Check for SEV-ES memory encryption feature:\r
- ; CPUID Fn8000_001F[EAX] - Bit 3\r
- ; CPUID raises a #VC exception if running as an SEV-ES guest\r
- mov eax, 0x8000001f\r
- cpuid\r
- bt eax, 3\r
- jnc GetSevEncBit\r
-\r
- ; Check if SEV-ES is enabled\r
- ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)\r
- mov ecx, 0xc0010131\r
- rdmsr\r
- bt eax, 1\r
- jnc GetSevEncBit\r
-\r
- ; Set the first byte of the workarea to one to communicate to the SEC\r
- ; phase that SEV-ES is enabled.\r
- mov byte[SEV_ES_WORK_AREA], 1\r
-\r
-GetSevEncBit:\r
- ; Get pte bit position to enable memory encryption\r
- ; CPUID Fn8000_001F[EBX] - Bits 5:0\r
- ;\r
- and ebx, 0x3f\r
- mov eax, ebx\r
-\r
- ; The encryption bit position is always above 31\r
- sub ebx, 32\r
- jns SevSaveMask\r
-\r
- ; Encryption bit was reported as 31 or below, enter a HLT loop\r
-SevEncBitLowHlt:\r
- cli\r
- hlt\r
- jmp SevEncBitLowHlt\r
-\r
-SevSaveMask:\r
- xor edx, edx\r
- bts edx, ebx\r
-\r
- mov dword[SEV_ES_WORK_AREA_ENC_MASK], 0\r
- mov dword[SEV_ES_WORK_AREA_ENC_MASK + 4], edx\r
- jmp SevExit\r
-\r
-NoSev:\r
- ;\r
- ; Perform an SEV-ES sanity check by seeing if a #VC exception occurred.\r
- ;\r
- cmp byte[SEV_ES_WORK_AREA], 0\r
- jz NoSevPass\r
-\r
- ;\r
- ; A #VC was received, yet CPUID indicates no SEV-ES support, something\r
- ; isn't right.\r
- ;\r
-NoSevEsVcHlt:\r
- cli\r
- hlt\r
- jmp NoSevEsVcHlt\r
-\r
-NoSevPass:\r
- xor eax, eax\r
-\r
-SevExit:\r
- ;\r
- ; Clear exception handlers and stack\r
- ;\r
- push eax\r
- mov eax, ADDR_OF(IdtrClear)\r
- lidt [cs:eax]\r
- pop eax\r
- mov esp, 0\r
-\r
- OneTimeCallRet CheckSevFeatures\r
-\r
-; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature\r
-; is enabled.\r
-;\r
-; Modified: EAX\r
-;\r
-; If SEV-ES is enabled then EAX will be non-zero.\r
-; If SEV-ES is disabled then EAX will be zero.\r
-;\r
-IsSevEsEnabled:\r
- xor eax, eax\r
-\r
- ; During CheckSevFeatures, the SEV_ES_WORK_AREA was set to 1 if\r
- ; SEV-ES is enabled.\r
- cmp byte[SEV_ES_WORK_AREA], 1\r
- jne SevEsDisabled\r
-\r
- mov eax, 1\r
-\r
-SevEsDisabled:\r
- OneTimeCallRet IsSevEsEnabled\r
-\r
;\r
; Modified: EAX, EBX, ECX, EDX\r
;\r
mov cr3, eax\r
\r
OneTimeCallRet SetCr3ForPageTables64\r
-\r
-;\r
-; Start of #VC exception handling routines\r
-;\r
-\r
-SevEsIdtNotCpuid:\r
- ;\r
- ; Use VMGEXIT to request termination.\r
- ; 1 - #VC was not for CPUID\r
- ;\r
- mov eax, 1\r
- jmp SevEsIdtTerminate\r
-\r
-SevEsIdtNoCpuidResponse:\r
- ;\r
- ; Use VMGEXIT to request termination.\r
- ; 2 - GHCB_CPUID_RESPONSE not received\r
- ;\r
- mov eax, 2\r
-\r
-SevEsIdtTerminate:\r
- ;\r
- ; Use VMGEXIT to request termination. At this point the reason code is\r
- ; located in EAX, so shift it left 16 bits to the proper location.\r
- ;\r
- ; EAX[11:0] => 0x100 - request termination\r
- ; EAX[15:12] => 0x1 - OVMF\r
- ; EAX[23:16] => 0xXX - REASON CODE\r
- ;\r
- shl eax, 16\r
- or eax, 0x1100\r
- xor edx, edx\r
- mov ecx, 0xc0010130\r
- wrmsr\r
- ;\r
- ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit\r
- ; mode, so work around this by temporarily switching to 64-bit mode.\r
- ;\r
-BITS 64\r
- rep vmmcall\r
-BITS 32\r
-\r
- ;\r
- ; We shouldn't come back from the VMGEXIT, but if we do, just loop.\r
- ;\r
-SevEsIdtHlt:\r
- hlt\r
- jmp SevEsIdtHlt\r
- iret\r
-\r
- ;\r
- ; Total stack usage for the #VC handler is 44 bytes:\r
- ; - 12 bytes for the exception IRET (after popping error code)\r
- ; - 32 bytes for the local variables.\r
- ;\r
-SevEsIdtVmmComm:\r
- ;\r
- ; If we're here, then we are an SEV-ES guest and this\r
- ; was triggered by a CPUID instruction\r
- ;\r
- ; Set the first byte of the workarea to one to communicate that\r
- ; a #VC was taken.\r
- mov byte[SEV_ES_WORK_AREA], 1\r
-\r
- pop ecx ; Error code\r
- cmp ecx, 0x72 ; Be sure it was CPUID\r
- jne SevEsIdtNotCpuid\r
-\r
- ; Set up local variable room on the stack\r
- ; CPUID function : + 28\r
- ; CPUID request register : + 24\r
- ; GHCB MSR (EAX) : + 20\r
- ; GHCB MSR (EDX) : + 16\r
- ; CPUID result (EDX) : + 12\r
- ; CPUID result (ECX) : + 8\r
- ; CPUID result (EBX) : + 4\r
- ; CPUID result (EAX) : + 0\r
- sub esp, VC_VARIABLE_SIZE\r
-\r
- ; Save the CPUID function being requested\r
- mov [esp + VC_CPUID_FUNCTION], eax\r
-\r
- ; The GHCB CPUID protocol uses the following mapping to request\r
- ; a specific register:\r
- ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX\r
- ;\r
- ; Set EAX as the first register to request. This will also be used as a\r
- ; loop variable to request all register values (EAX to EDX).\r
- xor eax, eax\r
- mov [esp + VC_CPUID_REQUEST_REGISTER], eax\r
-\r
- ; Save current GHCB MSR value\r
- mov ecx, 0xc0010130\r
- rdmsr\r
- mov [esp + VC_GHCB_MSR_EAX], eax\r
- mov [esp + VC_GHCB_MSR_EDX], edx\r
-\r
-NextReg:\r
- ;\r
- ; Setup GHCB MSR\r
- ; GHCB_MSR[63:32] = CPUID function\r
- ; GHCB_MSR[31:30] = CPUID register\r
- ; GHCB_MSR[11:0] = CPUID request protocol\r
- ;\r
- mov eax, [esp + VC_CPUID_REQUEST_REGISTER]\r
- cmp eax, 4\r
- jge VmmDone\r
-\r
- shl eax, GHCB_CPUID_REGISTER_SHIFT\r
- or eax, GHCB_CPUID_REQUEST\r
- mov edx, [esp + VC_CPUID_FUNCTION]\r
- mov ecx, 0xc0010130\r
- wrmsr\r
-\r
- ;\r
- ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit\r
- ; mode, so work around this by temporarily switching to 64-bit mode.\r
- ;\r
-BITS 64\r
- rep vmmcall\r
-BITS 32\r
-\r
- ;\r
- ; Read GHCB MSR\r
- ; GHCB_MSR[63:32] = CPUID register value\r
- ; GHCB_MSR[31:30] = CPUID register\r
- ; GHCB_MSR[11:0] = CPUID response protocol\r
- ;\r
- mov ecx, 0xc0010130\r
- rdmsr\r
- mov ecx, eax\r
- and ecx, 0xfff\r
- cmp ecx, GHCB_CPUID_RESPONSE\r
- jne SevEsIdtNoCpuidResponse\r
-\r
- ; Save returned value\r
- shr eax, GHCB_CPUID_REGISTER_SHIFT\r
- mov [esp + eax * 4], edx\r
-\r
- ; Next register\r
- inc word [esp + VC_CPUID_REQUEST_REGISTER]\r
-\r
- jmp NextReg\r
-\r
-VmmDone:\r
- ;\r
- ; At this point we have all CPUID register values. Restore the GHCB MSR,\r
- ; set the return register values and return.\r
- ;\r
- mov eax, [esp + VC_GHCB_MSR_EAX]\r
- mov edx, [esp + VC_GHCB_MSR_EDX]\r
- mov ecx, 0xc0010130\r
- wrmsr\r
-\r
- mov eax, [esp + VC_CPUID_RESULT_EAX]\r
- mov ebx, [esp + VC_CPUID_RESULT_EBX]\r
- mov ecx, [esp + VC_CPUID_RESULT_ECX]\r
- mov edx, [esp + VC_CPUID_RESULT_EDX]\r
-\r
- add esp, VC_VARIABLE_SIZE\r
-\r
- ; Update the EIP value to skip over the now handled CPUID instruction\r
- ; (the CPUID instruction has a length of 2)\r
- add word [esp], CPUID_INSN_LEN\r
- iret\r
-\r
-ALIGN 2\r
-\r
-Idtr:\r
- dw IDT_END - IDT_BASE - 1 ; Limit\r
- dd ADDR_OF(IDT_BASE) ; Base\r
-\r
-IdtrClear:\r
- dw 0 ; Limit\r
- dd 0 ; Base\r
-\r
-ALIGN 16\r
-\r
-;\r
-; The Interrupt Descriptor Table (IDT)\r
-; This will be used to determine if SEV-ES is enabled. Upon execution\r
-; of the CPUID instruction, a VMM Communication Exception will occur.\r
-; This will tell us if SEV-ES is enabled. We can use the current value\r
-; of the GHCB MSR to determine the SEV attributes.\r
-;\r
-IDT_BASE:\r
-;\r
-; Vectors 0 - 28 (No handlers)\r
-;\r
-%rep 29\r
- dw 0 ; Offset low bits 15..0\r
- dw 0x10 ; Selector\r
- db 0 ; Reserved\r
- db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)\r
- dw 0 ; Offset high bits 31..16\r
-%endrep\r
-;\r
-; Vector 29 (VMM Communication Exception)\r
-;\r
- dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0\r
- dw 0x10 ; Selector\r
- db 0 ; Reserved\r
- db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)\r
- dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16\r
-;\r
-; Vectors 30 - 31 (No handlers)\r
-;\r
-%rep 2\r
- dw 0 ; Offset low bits 15..0\r
- dw 0x10 ; Selector\r
- db 0 ; Reserved\r
- db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)\r
- dw 0 ; Offset high bits 31..16\r
-%endrep\r
-IDT_END:\r