--- /dev/null
+;------------------------------------------------------------------------------\r
+; @file\r
+; Intel TDX routines\r
+;\r
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
+; SPDX-License-Identifier: BSD-2-Clause-Patent\r
+;\r
+;------------------------------------------------------------------------------\r
+\r
+%define VM_GUEST_TDX 2\r
+\r
+BITS 32\r
+\r
+;\r
+; Check if it is Intel Tdx\r
+;\r
+; Modified: EAX, EBX, ECX, EDX\r
+;\r
+; If it is Intel Tdx, EAX is 1\r
+; If it is not Intel Tdx, EAX is 0\r
+;\r
+IsTdx:\r
+ ;\r
+ ; CPUID (0)\r
+ ;\r
+ mov eax, 0\r
+ cpuid\r
+ cmp ebx, 0x756e6547 ; "Genu"\r
+ jne IsNotTdx\r
+ cmp edx, 0x49656e69 ; "ineI"\r
+ jne IsNotTdx\r
+ cmp ecx, 0x6c65746e ; "ntel"\r
+ jne IsNotTdx\r
+\r
+ ;\r
+ ; CPUID (1)\r
+ ;\r
+ mov eax, 1\r
+ cpuid\r
+ test ecx, 0x80000000\r
+ jz IsNotTdx\r
+\r
+ ;\r
+ ; CPUID[0].EAX >= 0x21?\r
+ ;\r
+ mov eax, 0\r
+ cpuid\r
+ cmp eax, 0x21\r
+ jl IsNotTdx\r
+\r
+ ;\r
+ ; CPUID (0x21,0)\r
+ ;\r
+ mov eax, 0x21\r
+ mov ecx, 0\r
+ cpuid\r
+\r
+ cmp ebx, 0x65746E49 ; "Inte"\r
+ jne IsNotTdx\r
+ cmp edx, 0x5844546C ; "lTDX"\r
+ jne IsNotTdx\r
+ cmp ecx, 0x20202020 ; " "\r
+ jne IsNotTdx\r
+\r
+ mov eax, 1\r
+ jmp ExitIsTdx\r
+\r
+IsNotTdx:\r
+ xor eax, eax\r
+\r
+ExitIsTdx:\r
+\r
+ OneTimeCallRet IsTdx\r
+\r
+;\r
+; Initialize work area if it is Tdx guest. Detailed definition is in\r
+; OvmfPkg/Include/WorkArea.h.\r
+; BSP and APs all go here. Only BSP initialize this work area.\r
+;\r
+; Param[in] EBX[5:0] CPU Supported GPAW (48 or 52)\r
+; Param[in] ESI[31:0] vCPU ID (BSP is 0, others are AP)\r
+;\r
+; Modified: EBX\r
+;\r
+InitTdxWorkarea:\r
+\r
+ ;\r
+ ; First check if it is Tdx\r
+ ;\r
+ OneTimeCall IsTdx\r
+\r
+ test eax, eax\r
+ jz ExitInitTdxWorkarea\r
+\r
+ cmp esi, 0\r
+ je TdxBspEntry\r
+\r
+ ;\r
+ ; In Td guest, BSP/AP shares the same entry point\r
+ ; BSP builds up the page table, while APs shouldn't do the same task.\r
+ ; Instead, APs just leverage the page table which is built by BSP.\r
+ ; APs will wait until the page table is ready.\r
+ ;\r
+TdxApWait:\r
+ cmp byte[TDX_WORK_AREA_PGTBL_READY], 0\r
+ je TdxApWait\r
+ jmp ExitInitTdxWorkarea\r
+\r
+TdxBspEntry:\r
+ ;\r
+ ; Set Type of WORK_AREA_GUEST_TYPE so that the following code can use\r
+ ; these information.\r
+ ;\r
+ mov byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX\r
+\r
+ ;\r
+ ; EBX[5:0] CPU supported GPA width\r
+ ;\r
+ and ebx, 0x3f\r
+ mov DWORD[TDX_WORK_AREA_GPAW], ebx\r
+\r
+ExitInitTdxWorkarea:\r
+ OneTimeCallRet InitTdxWorkarea\r
+\r
+;\r
+; Load the GDT and set the CS/DS/ES/FS/GS/SS.\r
+;\r
+; Modified: EAX, DS, ES, FS, GS, SS, CS\r
+;\r
+ReloadFlat32:\r
+\r
+ cli\r
+ mov eax, ADDR_OF(gdtr)\r
+ lgdt [eax]\r
+\r
+ jmp LINEAR_CODE_SEL:dword ADDR_OF(jumpToFlat32BitAndLandHere)\r
+\r
+jumpToFlat32BitAndLandHere:\r
+\r
+ debugShowPostCode POSTCODE_32BIT_MODE\r
+\r
+ mov ax, LINEAR_SEL\r
+ mov ds, ax\r
+ mov es, ax\r
+ mov fs, ax\r
+ mov gs, ax\r
+ mov ss, ax\r
+\r
+ OneTimeCallRet ReloadFlat32\r
+\r
+;\r
+; Tdx initialization after entering into ResetVector\r
+;\r
+; Modified: EAX, EBX, ECX, EDX, EBP, EDI, ESP\r
+;\r
+InitTdx:\r
+ ;\r
+ ; First load the GDT and jump to Flat32 mode\r
+ ;\r
+ OneTimeCall ReloadFlat32\r
+\r
+ ;\r
+ ; Initialization of Tdx work area\r
+ ;\r
+ OneTimeCall InitTdxWorkarea\r
+\r
+ OneTimeCallRet InitTdx\r
+\r
+;\r
+; Check TDX features, TDX or TDX-BSP or TDX-APs?\r
+;\r
+; By design TDX BSP is reponsible for initializing the PageTables.\r
+; After PageTables are ready, byte[TDX_WORK_AREA_PGTBL_READY] is set to 1.\r
+; APs will spin when byte[TDX_WORK_AREA_PGTBL_READY] is 0 until it is set to 1.\r
+;\r
+; When this routine is run on TDX BSP, byte[TDX_WORK_AREA_PGTBL_READY] should be 0.\r
+; When this routine is run on TDX APs, byte[TDX_WORK_AREA_PGTBL_READY] should be 1.\r
+;\r
+;\r
+; Modified: EAX, EDX\r
+;\r
+; 0-NonTdx, 1-TdxBsp, 2-TdxAps\r
+;\r
+CheckTdxFeaturesBeforeBuildPagetables:\r
+ xor eax, eax\r
+ cmp byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX\r
+ jne NotTdx\r
+\r
+ xor edx, edx\r
+ mov al, byte[TDX_WORK_AREA_PGTBL_READY]\r
+ inc eax\r
+\r
+NotTdx:\r
+ OneTimeCallRet CheckTdxFeaturesBeforeBuildPagetables\r
+\r
+;\r
+; Set byte[TDX_WORK_AREA_PGTBL_READY] to 1\r
+;\r
+TdxPostBuildPageTables:\r
+ cmp byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX\r
+ jne ExitTdxPostBuildPageTables\r
+ mov byte[TDX_WORK_AREA_PGTBL_READY], 1\r
+\r
+ExitTdxPostBuildPageTables:\r
+ OneTimeCallRet TdxPostBuildPageTables\r
+\r
+;\r
+; Check if TDX is enabled\r
+;\r
+; Modified: EAX\r
+;\r
+; If TDX is enabled then EAX will be 1\r
+; If TDX is disabled then EAX will be 0.\r
+;\r
+IsTdxEnabled:\r
+ xor eax, eax\r
+ cmp byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX\r
+ jne TdxNotEnabled\r
+ mov eax, 1\r
+\r
+TdxNotEnabled:\r
+ OneTimeCallRet IsTdxEnabled\r
PAGE_READ_WRITE + \\r
PAGE_PRESENT)\r
\r
+%define TDX_BSP 1\r
+%define TDX_AP 2\r
+\r
;\r
; Modified: EAX, EBX, ECX, EDX\r
;\r
SetCr3ForPageTables64:\r
+ ; Check the TDX features.\r
+ ; If it is TDX APs, then jump to SetCr3 directly.\r
+ ; In TD guest the initialization is done by BSP, including building\r
+ ; the page tables. APs will spin on until byte[TDX_WORK_AREA_PGTBL_READY]\r
+ ; is set.\r
+ OneTimeCall CheckTdxFeaturesBeforeBuildPagetables\r
+ cmp eax, TDX_BSP\r
+ je ClearOvmfPageTables\r
+ cmp eax, TDX_AP\r
+ je SetCr3\r
\r
; Check whether the SEV is active and populate the SevEsWorkArea\r
OneTimeCall CheckSevFeatures\r
; the page table build below.\r
OneTimeCall GetSevCBitMaskAbove31\r
\r
+ClearOvmfPageTables:\r
;\r
; For OVMF, build some initial page tables at\r
; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000).\r
; Clear the C-bit from the GHCB page if the SEV-ES is enabled.\r
OneTimeCall SevClearPageEncMaskForGhcbPage\r
\r
+ ; TDX will do some PostBuildPages task, such as setting\r
+ ; byte[TDX_WORK_AREA_PGTBL_READY].\r
+ OneTimeCall TdxPostBuildPageTables\r
+\r
SetCr3:\r
;\r
; Set CR3 now that the paging structures are available\r