diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm index 7be43fb44a..dee2e3f9de 100644 --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm @@ -177,10 +177,30 @@ resetVector: ; ; This is where the processor will begin execution ; +; In IA32 we follow the standard reset vector flow. While in X64, Td guest +; may be supported. Td guest requires the startup mode to be 32-bit +; protected mode but the legacy VM startup mode is 16-bit real mode. +; To make NASM generate such shared entry code that behaves correctly in +; both 16-bit and 32-bit mode, more BITS directives are added. +; +%ifdef ARCH_IA32 nop nop jmp EarlyBspInitReal16 +%else + + mov eax, cr0 + test al, 1 + jz .Real +BITS 32 + jmp Main32 +BITS 16 +.Real: + jmp EarlyBspInitReal16 + +%endif + ALIGN 16 fourGigabytes: diff --git a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm index c6d0d898bc..eb3546668e 100644 --- a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm +++ b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm @@ -21,6 +21,17 @@ Transition32FlatTo64Flat: bts eax, 5 ; enable PAE mov cr4, eax + ; + ; In TDX LME has already been set. So we're done and jump to enable + ; paging directly if Tdx is enabled. + ; EBX is cleared because in the later it will be used to check if + ; the second step of the SEV-ES mitigation is to be performed. + ; + xor ebx, ebx + OneTimeCall IsTdxEnabled + test eax, eax + jnz EnablePaging + mov ecx, 0xc0000080 rdmsr bts eax, 8 ; set LME diff --git a/OvmfPkg/ResetVector/Ia32/IntelTdx.asm b/OvmfPkg/ResetVector/Ia32/IntelTdx.asm new file mode 100644 index 0000000000..06794baef8 --- /dev/null +++ b/OvmfPkg/ResetVector/Ia32/IntelTdx.asm @@ -0,0 +1,222 @@ +;------------------------------------------------------------------------------ +; @file +; Intel TDX routines +; +; Copyright (c) 2021, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +%define VM_GUEST_TDX 2 + +BITS 32 + +; +; Check if it is Intel Tdx +; +; Modified: EAX, EBX, ECX, EDX +; +; If it is Intel Tdx, EAX is 1 +; If it is not Intel Tdx, EAX is 0 +; +IsTdx: + ; + ; CPUID (0) + ; + mov eax, 0 + cpuid + cmp ebx, 0x756e6547 ; "Genu" + jne IsNotTdx + cmp edx, 0x49656e69 ; "ineI" + jne IsNotTdx + cmp ecx, 0x6c65746e ; "ntel" + jne IsNotTdx + + ; + ; CPUID (1) + ; + mov eax, 1 + cpuid + test ecx, 0x80000000 + jz IsNotTdx + + ; + ; CPUID[0].EAX >= 0x21? + ; + mov eax, 0 + cpuid + cmp eax, 0x21 + jl IsNotTdx + + ; + ; CPUID (0x21,0) + ; + mov eax, 0x21 + mov ecx, 0 + cpuid + + cmp ebx, 0x65746E49 ; "Inte" + jne IsNotTdx + cmp edx, 0x5844546C ; "lTDX" + jne IsNotTdx + cmp ecx, 0x20202020 ; " " + jne IsNotTdx + + mov eax, 1 + jmp ExitIsTdx + +IsNotTdx: + xor eax, eax + +ExitIsTdx: + + OneTimeCallRet IsTdx + +; +; Initialize work area if it is Tdx guest. Detailed definition is in +; OvmfPkg/Include/WorkArea.h. +; BSP and APs all go here. Only BSP initialize this work area. +; +; Param[in] EBX[5:0] CPU Supported GPAW (48 or 52) +; Param[in] ESI[31:0] vCPU ID (BSP is 0, others are AP) +; +; Modified: EBX +; +InitTdxWorkarea: + + ; + ; First check if it is Tdx + ; + OneTimeCall IsTdx + + test eax, eax + jz ExitInitTdxWorkarea + + cmp esi, 0 + je TdxBspEntry + + ; + ; In Td guest, BSP/AP shares the same entry point + ; BSP builds up the page table, while APs shouldn't do the same task. + ; Instead, APs just leverage the page table which is built by BSP. + ; APs will wait until the page table is ready. + ; +TdxApWait: + cmp byte[TDX_WORK_AREA_PGTBL_READY], 0 + je TdxApWait + jmp ExitInitTdxWorkarea + +TdxBspEntry: + ; + ; Set Type of WORK_AREA_GUEST_TYPE so that the following code can use + ; these information. + ; + mov byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX + + ; + ; EBX[5:0] CPU supported GPA width + ; + and ebx, 0x3f + mov DWORD[TDX_WORK_AREA_GPAW], ebx + +ExitInitTdxWorkarea: + OneTimeCallRet InitTdxWorkarea + +; +; Load the GDT and set the CS/DS/ES/FS/GS/SS. +; +; Modified: EAX, DS, ES, FS, GS, SS, CS +; +ReloadFlat32: + + cli + mov eax, ADDR_OF(gdtr) + lgdt [eax] + + jmp LINEAR_CODE_SEL:dword ADDR_OF(jumpToFlat32BitAndLandHere) + +jumpToFlat32BitAndLandHere: + + debugShowPostCode POSTCODE_32BIT_MODE + + mov ax, LINEAR_SEL + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + OneTimeCallRet ReloadFlat32 + +; +; Tdx initialization after entering into ResetVector +; +; Modified: EAX, EBX, ECX, EDX, EBP, EDI, ESP +; +InitTdx: + ; + ; First load the GDT and jump to Flat32 mode + ; + OneTimeCall ReloadFlat32 + + ; + ; Initialization of Tdx work area + ; + OneTimeCall InitTdxWorkarea + + OneTimeCallRet InitTdx + +; +; Check TDX features, TDX or TDX-BSP or TDX-APs? +; +; By design TDX BSP is reponsible for initializing the PageTables. +; After PageTables are ready, byte[TDX_WORK_AREA_PGTBL_READY] is set to 1. +; APs will spin when byte[TDX_WORK_AREA_PGTBL_READY] is 0 until it is set to 1. +; +; When this routine is run on TDX BSP, byte[TDX_WORK_AREA_PGTBL_READY] should be 0. +; When this routine is run on TDX APs, byte[TDX_WORK_AREA_PGTBL_READY] should be 1. +; +; +; Modified: EAX, EDX +; +; 0-NonTdx, 1-TdxBsp, 2-TdxAps +; +CheckTdxFeaturesBeforeBuildPagetables: + xor eax, eax + cmp byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX + jne NotTdx + + xor edx, edx + mov al, byte[TDX_WORK_AREA_PGTBL_READY] + inc eax + +NotTdx: + OneTimeCallRet CheckTdxFeaturesBeforeBuildPagetables + +; +; Set byte[TDX_WORK_AREA_PGTBL_READY] to 1 +; +TdxPostBuildPageTables: + cmp byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX + jne ExitTdxPostBuildPageTables + mov byte[TDX_WORK_AREA_PGTBL_READY], 1 + +ExitTdxPostBuildPageTables: + OneTimeCallRet TdxPostBuildPageTables + +; +; Check if TDX is enabled +; +; Modified: EAX +; +; If TDX is enabled then EAX will be 1 +; If TDX is disabled then EAX will be 0. +; +IsTdxEnabled: + xor eax, eax + cmp byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX + jne TdxNotEnabled + mov eax, 1 + +TdxNotEnabled: + OneTimeCallRet IsTdxEnabled diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm index 02528221e5..317cad430f 100644 --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm @@ -37,10 +37,23 @@ BITS 32 PAGE_READ_WRITE + \ PAGE_PRESENT) +%define TDX_BSP 1 +%define TDX_AP 2 + ; ; Modified: EAX, EBX, ECX, EDX ; SetCr3ForPageTables64: + ; Check the TDX features. + ; If it is TDX APs, then jump to SetCr3 directly. + ; In TD guest the initialization is done by BSP, including building + ; the page tables. APs will spin on until byte[TDX_WORK_AREA_PGTBL_READY] + ; is set. + OneTimeCall CheckTdxFeaturesBeforeBuildPagetables + cmp eax, TDX_BSP + je ClearOvmfPageTables + cmp eax, TDX_AP + je SetCr3 ; Check whether the SEV is active and populate the SevEsWorkArea OneTimeCall CheckSevFeatures @@ -50,6 +63,7 @@ SetCr3ForPageTables64: ; the page table build below. OneTimeCall GetSevCBitMaskAbove31 +ClearOvmfPageTables: ; ; For OVMF, build some initial page tables at ; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000). @@ -101,6 +115,10 @@ pageTableEntriesLoop: ; Clear the C-bit from the GHCB page if the SEV-ES is enabled. OneTimeCall SevClearPageEncMaskForGhcbPage + ; TDX will do some PostBuildPages task, such as setting + ; byte[TDX_WORK_AREA_PGTBL_READY]. + OneTimeCall TdxPostBuildPageTables + SetCr3: ; ; Set CR3 now that the paging structures are available diff --git a/OvmfPkg/ResetVector/Main.asm b/OvmfPkg/ResetVector/Main.asm index bbfeac1c88..5cfc0b5c72 100644 --- a/OvmfPkg/ResetVector/Main.asm +++ b/OvmfPkg/ResetVector/Main.asm @@ -40,6 +40,20 @@ BITS 32 ; work area when detected. mov byte[WORK_AREA_GUEST_TYPE], 0 +%ifdef ARCH_X64 + + jmp SearchBfv + +; +; Entry point of Main32 +; +Main32: + OneTimeCall InitTdx + +SearchBfv: + +%endif + ; ; Search for the Boot Firmware Volume (BFV) ; diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb index eb9733e402..87effedb9c 100644 --- a/OvmfPkg/ResetVector/ResetVector.nasmb +++ b/OvmfPkg/ResetVector/ResetVector.nasmb @@ -108,6 +108,7 @@ %include "Ia32/Flat32ToFlat64.asm" %include "Ia32/AmdSev.asm" %include "Ia32/PageTables64.asm" +%include "Ia32/IntelTdx.asm" %endif %include "Ia16/Real16ToFlat32.asm"