| ;------------------------------------------------------------------------------ | |
| ; @file | |
| ; Intel TDX routines | |
| ; | |
| ; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> | |
| ; 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: | |
| ; | |
| ; 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, 3-TdxAps5Level | |
| ; | |
| 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: | |
| mov byte[TDX_WORK_AREA_PGTBL_READY], 1 | |
| OneTimeCallRet TdxPostBuildPageTables | |
| %if PG_5_LEVEL | |
| ; | |
| ; Set byte[TDX_WORK_AREA_PGTBL_READY] to 2 | |
| ; | |
| TdxPostBuildPageTables5Level: | |
| mov byte[TDX_WORK_AREA_PGTBL_READY], 2 | |
| OneTimeCallRet TdxPostBuildPageTables5Level | |
| %endif | |
| ; | |
| ; 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 |