* [PATCH V3 01/29] MdePkg: Add Tdx.h
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-01 13:15 ` [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations Min Xu
` (27 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Michael D Kinney, Liming Gao, Zhiguang Liu, Gerd Hoffmann,
Jiewen Yao
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Tdx.h includes the Intel Trust Domain Extension definitions.
Detailed information can be found in below document:
https://software.intel.com/content/dam/develop/external/us/en/
documents/tdx-module-1eas-v0.85.039.pdf
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
MdePkg/Include/IndustryStandard/Tdx.h | 203 ++++++++++++++++++++++++++
1 file changed, 203 insertions(+)
create mode 100644 MdePkg/Include/IndustryStandard/Tdx.h
diff --git a/MdePkg/Include/IndustryStandard/Tdx.h b/MdePkg/Include/IndustryStandard/Tdx.h
new file mode 100644
index 000000000000..11b3d3345de3
--- /dev/null
+++ b/MdePkg/Include/IndustryStandard/Tdx.h
@@ -0,0 +1,203 @@
+/** @file
+ Intel Trust Domain Extension definitions
+ Detailed information is in below document:
+ https://software.intel.com/content/dam/develop/external/us/en/documents
+ /tdx-module-1eas-v0.85.039.pdf
+
+ Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef MDE_PKG_TDX_H_
+#define MDE_PKG_TDX_H_
+
+#define EXIT_REASON_EXTERNAL_INTERRUPT 1
+#define EXIT_REASON_TRIPLE_FAULT 2
+
+#define EXIT_REASON_PENDING_INTERRUPT 7
+#define EXIT_REASON_NMI_WINDOW 8
+#define EXIT_REASON_TASK_SWITCH 9
+#define EXIT_REASON_CPUID 10
+#define EXIT_REASON_HLT 12
+#define EXIT_REASON_INVD 13
+#define EXIT_REASON_INVLPG 14
+#define EXIT_REASON_RDPMC 15
+#define EXIT_REASON_RDTSC 16
+#define EXIT_REASON_VMCALL 18
+#define EXIT_REASON_VMCLEAR 19
+#define EXIT_REASON_VMLAUNCH 20
+#define EXIT_REASON_VMPTRLD 21
+#define EXIT_REASON_VMPTRST 22
+#define EXIT_REASON_VMREAD 23
+#define EXIT_REASON_VMRESUME 24
+#define EXIT_REASON_VMWRITE 25
+#define EXIT_REASON_VMOFF 26
+#define EXIT_REASON_VMON 27
+#define EXIT_REASON_CR_ACCESS 28
+#define EXIT_REASON_DR_ACCESS 29
+#define EXIT_REASON_IO_INSTRUCTION 30
+#define EXIT_REASON_MSR_READ 31
+#define EXIT_REASON_MSR_WRITE 32
+#define EXIT_REASON_INVALID_STATE 33
+#define EXIT_REASON_MSR_LOAD_FAIL 34
+#define EXIT_REASON_MWAIT_INSTRUCTION 36
+#define EXIT_REASON_MONITOR_TRAP_FLAG 37
+#define EXIT_REASON_MONITOR_INSTRUCTION 39
+#define EXIT_REASON_PAUSE_INSTRUCTION 40
+#define EXIT_REASON_MCE_DURING_VMENTRY 41
+#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
+#define EXIT_REASON_APIC_ACCESS 44
+#define EXIT_REASON_EOI_INDUCED 45
+#define EXIT_REASON_GDTR_IDTR 46
+#define EXIT_REASON_LDTR_TR 47
+#define EXIT_REASON_EPT_VIOLATION 48
+#define EXIT_REASON_EPT_MISCONFIG 49
+#define EXIT_REASON_INVEPT 50
+#define EXIT_REASON_RDTSCP 51
+#define EXIT_REASON_PREEMPTION_TIMER 52
+#define EXIT_REASON_INVVPID 53
+#define EXIT_REASON_WBINVD 54
+#define EXIT_REASON_XSETBV 55
+#define EXIT_REASON_APIC_WRITE 56
+#define EXIT_REASON_RDRAND 57
+#define EXIT_REASON_INVPCID 58
+#define EXIT_REASON_VMFUNC 59
+#define EXIT_REASON_ENCLS 60
+#define EXIT_REASON_RDSEED 61
+#define EXIT_REASON_PML_FULL 62
+#define EXIT_REASON_XSAVES 63
+#define EXIT_REASON_XRSTORS 64
+
+// TDCALL API Function Completion Status Codes
+#define TDX_EXIT_REASON_SUCCESS 0x0000000000000000
+#define TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED 0x00000B0A00000000
+#define TDX_EXIT_REASON_PAGE_SIZE_MISMATCH 0xC0000B0B00000000
+#define TDX_EXIT_REASON_OPERAND_INVALID 0xC000010000000000
+#define TDX_EXIT_REASON_OPERAND_BUSY 0x8000020000000000
+
+// TDCALL [TDG.MEM.PAGE.ACCEPT] page size
+#define TDCALL_ACCEPT_PAGE_SIZE_4K 0
+#define TDCALL_ACCEPT_PAGE_SIZE_2M 1
+#define TDCALL_ACCEPT_PAGE_SIZE_1G 2
+
+#define TDCALL_TDVMCALL 0
+#define TDCALL_TDINFO 1
+#define TDCALL_TDEXTENDRTMR 2
+#define TDCALL_TDGETVEINFO 3
+#define TDCALL_TDREPORT 4
+#define TDCALL_TDSETCPUIDVE 5
+#define TDCALL_TDACCEPTPAGE 6
+
+#define TDVMCALL_CPUID 0x0000a
+#define TDVMCALL_HALT 0x0000c
+#define TDVMCALL_IO 0x0001e
+#define TDVMCALL_RDMSR 0x0001f
+#define TDVMCALL_WRMSR 0x00020
+#define TDVMCALL_MMIO 0x00030
+#define TDVMCALL_PCONFIG 0x00041
+
+#define TDVMCALL_GET_TDVMCALL_INFO 0x10000
+#define TDVMCALL_MAPGPA 0x10001
+#define TDVMCALL_GET_QUOTE 0x10002
+#define TDVMCALL_REPORT_FATAL_ERR 0x10003
+#define TDVMCALL_SETUP_EVENT_NOTIFY 0x10004
+
+#pragma pack(1)
+typedef struct {
+ UINT64 Data[6];
+} TDCALL_GENERIC_RETURN_DATA;
+
+typedef struct {
+ UINT64 Gpaw;
+ UINT64 Attributes;
+ UINT32 MaxVcpus;
+ UINT32 NumVcpus;
+ UINT64 Resv[3];
+} TDCALL_INFO_RETURN_DATA;
+
+typedef union {
+ UINT64 Val;
+ struct {
+ UINT32 Size:3;
+ UINT32 Direction:1;
+ UINT32 String:1;
+ UINT32 Rep:1;
+ UINT32 Encoding:1;
+ UINT32 Resv:9;
+ UINT32 Port:16;
+ UINT32 Resv2;
+ } Io;
+} VMX_EXIT_QUALIFICATION;
+
+typedef struct {
+ UINT32 ExitReason;
+ UINT32 Resv;
+ VMX_EXIT_QUALIFICATION ExitQualification;
+ UINT64 GuestLA;
+ UINT64 GuestPA;
+ UINT32 ExitInstructionLength;
+ UINT32 ExitInstructionInfo;
+ UINT32 Resv1;
+} TDCALL_VEINFO_RETURN_DATA;
+
+typedef union {
+ TDCALL_GENERIC_RETURN_DATA Generic;
+ TDCALL_INFO_RETURN_DATA TdInfo;
+ TDCALL_VEINFO_RETURN_DATA VeInfo;
+} TD_RETURN_DATA;
+
+/* data structure used in TDREPORT_STRUCT */
+typedef struct {
+ UINT8 Type;
+ UINT8 Subtype;
+ UINT8 Version;
+ UINT8 Rsvd;
+} TD_REPORT_TYPE;
+
+typedef struct {
+ TD_REPORT_TYPE ReportType;
+ UINT8 Rsvd1[12];
+ UINT8 CpuSvn[16];
+ UINT8 TeeTcbInfoHash[48];
+ UINT8 TeeInfoHash[48];
+ UINT8 ReportData[64];
+ UINT8 Rsvd2[32];
+ UINT8 Mac[32];
+} REPORTMACSTRUCT;
+
+typedef struct {
+ UINT8 Seam[2];
+ UINT8 Rsvd[14];
+} TEE_TCB_SVN;
+
+typedef struct {
+ UINT8 Valid[8];
+ TEE_TCB_SVN TeeTcbSvn;
+ UINT8 Mrseam[48];
+ UINT8 Mrsignerseam[48];
+ UINT8 Attributes[8];
+ UINT8 Rsvd[111];
+} TEE_TCB_INFO;
+
+typedef struct {
+ UINT8 Attributes[8];
+ UINT8 Xfam[8];
+ UINT8 Mrtd[48];
+ UINT8 Mrconfigid[48];
+ UINT8 Mrowner[48];
+ UINT8 Mrownerconfig[48];
+ UINT8 Rtmrs[4][48];
+ UINT8 Rsvd[112];
+} TDINFO;
+
+typedef struct {
+ REPORTMACSTRUCT ReportMacStruct;
+ TEE_TCB_INFO TeeTcbInfo;
+ UINT8 Rsvd[17];
+ TDINFO Tdinfo;
+} TDREPORT_STRUCT;
+
+#pragma pack()
+
+#endif
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
2021-11-01 13:15 ` [PATCH V3 01/29] MdePkg: Add Tdx.h Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-02 14:06 ` Gerd Hoffmann
2021-11-10 10:38 ` Erdem Aktas
2021-11-01 13:15 ` [PATCH V3 03/29] UefiCpuPkg: Extend VmgExitLibNull to handle #VE exception Min Xu
` (26 subsequent siblings)
28 siblings, 2 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Michael D Kinney, Liming Gao, Zhiguang Liu, Brijesh Singh,
Erdem Aktas, James Bottomley, Jiewen Yao, Tom Lendacky,
Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
TdxLib is created with functions to perform the related Tdx operation.
This includes functions for:
- TdCall : Cause a VM exit to the Intel TDX module.
- TdVmCall : It helps invoke services from the host VMM to pass/
receive information.
- TdVmCallCpuid : Enable the TD guest to request VMM to emulate CPUID
- TdAcceptPages : Accept pending private pages and initialize the pages
to all-0 using the TD ephemeral private key.
- TdExtendRtmr : Extend measurement to one of the RTMR registers.
- TdSharedPageMask: Get the Td guest shared page mask which indicates it
is a Shared or Private page.
- TdMaxVCpuNum : Get the maximum number of virtual CPUs.
- TdVCpuNum : Get the number of virtual CPUs.
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
MdePkg/Include/Library/TdxLib.h | 167 +++++++++++++++++++
MdePkg/Library/TdxLib/AcceptPages.c | 137 ++++++++++++++++
MdePkg/Library/TdxLib/Rtmr.c | 83 ++++++++++
MdePkg/Library/TdxLib/TdInfo.c | 103 ++++++++++++
MdePkg/Library/TdxLib/TdxLib.inf | 39 +++++
MdePkg/Library/TdxLib/TdxLibNull.c | 192 ++++++++++++++++++++++
MdePkg/Library/TdxLib/X64/Tdcall.nasm | 85 ++++++++++
MdePkg/Library/TdxLib/X64/Tdvmcall.nasm | 207 ++++++++++++++++++++++++
MdePkg/MdePkg.dec | 3 +
MdePkg/MdePkg.dsc | 1 +
10 files changed, 1017 insertions(+)
create mode 100644 MdePkg/Include/Library/TdxLib.h
create mode 100644 MdePkg/Library/TdxLib/AcceptPages.c
create mode 100644 MdePkg/Library/TdxLib/Rtmr.c
create mode 100644 MdePkg/Library/TdxLib/TdInfo.c
create mode 100644 MdePkg/Library/TdxLib/TdxLib.inf
create mode 100644 MdePkg/Library/TdxLib/TdxLibNull.c
create mode 100644 MdePkg/Library/TdxLib/X64/Tdcall.nasm
create mode 100644 MdePkg/Library/TdxLib/X64/Tdvmcall.nasm
diff --git a/MdePkg/Include/Library/TdxLib.h b/MdePkg/Include/Library/TdxLib.h
new file mode 100644
index 000000000000..d9e0335b2300
--- /dev/null
+++ b/MdePkg/Include/Library/TdxLib.h
@@ -0,0 +1,167 @@
+/** @file
+ TdxLib definitions
+
+ Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TDX_LIB_H_
+#define TDX_LIB_H_
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Protocol/DebugSupport.h>
+
+/**
+ This function accepts a pending private page, and initialize the page to
+ all-0 using the TD ephemeral private key.
+
+ @param[in] StartAddress Guest physical address of the private page
+ to accept.
+ @param[in] NumberOfPages Number of the pages to be accepted.
+ @param[in] PageSize GPA page size. Accept 2M/4K page size.
+
+ @return EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+TdAcceptPages (
+ IN UINT64 StartAddress,
+ IN UINT64 NumberOfPages,
+ IN UINT32 PageSize
+ );
+
+/**
+ This function extends one of the RTMR measurement register
+ in TDCS with the provided extension data in memory.
+ RTMR extending supports SHA384 which length is 48 bytes.
+
+ @param[in] Data Point to the data to be extended
+ @param[in] DataLen Length of the data. Must be 48
+ @param[in] Index RTMR index
+
+ @return EFI_SUCCESS
+ @return EFI_INVALID_PARAMETER
+ @return EFI_DEVICE_ERROR
+
+**/
+EFI_STATUS
+EFIAPI
+TdExtendRtmr (
+ IN UINT32 *Data,
+ IN UINT32 DataLen,
+ IN UINT8 Index
+ );
+
+
+/**
+ This function gets the Td guest shared page mask.
+
+ The guest indicates if a page is shared using the Guest Physical Address
+ (GPA) Shared (S) bit. If the GPA Width(GPAW) is 48, the S-bit is bit-47.
+ If the GPAW is 52, the S-bit is bit-51.
+
+ @return Shared page bit mask
+**/
+UINT64
+EFIAPI
+TdSharedPageMask (
+ VOID
+ );
+
+/**
+ This function gets the maximum number of Virtual CPUs that are usable for
+ Td Guest.
+
+ @return maximum Virtual CPUs number
+**/
+UINT32
+EFIAPI
+TdMaxVCpuNum (
+ VOID
+ );
+
+/**
+ This function gets the number of Virtual CPUs that are usable for Td
+ Guest.
+
+ @return Virtual CPUs number
+**/
+UINT32
+EFIAPI
+TdVCpuNum (
+ VOID
+ );
+
+
+/**
+ The TDCALL instruction causes a VM exit to the Intel TDX module. It is
+ used to call guest-side Intel TDX functions, either local or a TD exit
+ to the host VMM, as selected by Leaf.
+
+ @param[in] Leaf Leaf number of TDCALL instruction
+ @param[in] Arg1 Arg1
+ @param[in] Arg2 Arg2
+ @param[in] Arg3 Arg3
+ @param[in,out] Results Returned result of the Leaf function
+
+ @return EFI_SUCCESS
+ @return Other See individual leaf functions
+**/
+EFI_STATUS
+EFIAPI
+TdCall (
+ IN UINT64 Leaf,
+ IN UINT64 Arg1,
+ IN UINT64 Arg2,
+ IN UINT64 Arg3,
+ IN OUT VOID *Results
+ );
+
+/**
+ TDVMALL is a leaf function 0 for TDCALL. It helps invoke services from the
+ host VMM to pass/receive information.
+
+ @param[in] Leaf Number of sub-functions
+ @param[in] Arg1 Arg1
+ @param[in] Arg2 Arg2
+ @param[in] Arg3 Arg3
+ @param[in] Arg4 Arg4
+ @param[in,out] Results Returned result of the sub-function
+
+ @return EFI_SUCCESS
+ @return Other See individual sub-functions
+
+**/
+EFI_STATUS
+EFIAPI
+TdVmCall (
+ IN UINT64 Leaf,
+ IN UINT64 Arg1,
+ IN UINT64 Arg2,
+ IN UINT64 Arg3,
+ IN UINT64 Arg4,
+ IN OUT VOID *Results
+ );
+
+/**
+ This function enable the TD guest to request the VMM to emulate CPUID
+ operation, especially for non-architectural, CPUID leaves.
+
+ @param[in] Eax Main leaf of the CPUID
+ @param[in] Ecx Sub-leaf of the CPUID
+ @param[out] Results Returned result of CPUID operation
+
+ @return EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+TdVmCallCpuid (
+ IN UINT64 Eax,
+ IN UINT64 Ecx,
+ OUT VOID *Results
+ );
+
+#endif
diff --git a/MdePkg/Library/TdxLib/AcceptPages.c b/MdePkg/Library/TdxLib/AcceptPages.c
new file mode 100644
index 000000000000..18e94b13c351
--- /dev/null
+++ b/MdePkg/Library/TdxLib/AcceptPages.c
@@ -0,0 +1,137 @@
+/** @file
+
+ Unaccepted memory is a special type of private memory. In Td guest
+ TDCALL [TDG.MEM.PAGE.ACCEPT] is invoked to accept the unaccepted
+ memory before use it.
+
+ Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
+#include <Library/BaseMemoryLib.h>
+
+UINT64 mNumberOfDuplicatedAcceptedPages;
+
+// PageSize is mapped to PageLevel like below:
+// 4KB - 0, 2MB - 1
+UINT32 mTdxAcceptPageLevelMap[2] = {
+ SIZE_4KB,
+ SIZE_2MB
+};
+
+/**
+ This function gets the PageLevel according to the input page size.
+
+ @param[in] PageSize Page size
+
+ @return UINT32 The mapped page level
+**/
+UINT32
+GetGpaPageLevel (
+ UINT32 PageSize
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {
+ if (mTdxAcceptPageLevelMap[Index] == PageSize) {
+ break;
+ }
+ }
+
+ return Index == ARRAY_SIZE (mTdxAcceptPageLevelMap) ? -1 : Index;
+}
+
+/**
+ This function accept a pending private page, and initialize the page to
+ all-0 using the TD ephemeral private key.
+
+ Sometimes TDCALL [TDG.MEM.PAGE.ACCEPT] may return
+ TDX_EXIT_REASON_PAGE_SIZE_MISMATCH. It indicates the input PageLevel is
+ not workable. In this case we need to try to fallback to a smaller
+ PageLevel if possible.
+
+ @param[in] StartAddress Guest physical address of the private
+ page to accept.
+ @param[in] NumberOfPages Number of the pages to be accepted.
+ @param[in] PageSize GPA page size. Only accept 1G/2M/4K size.
+
+ @return EFI_SUCCESS Accept successfully
+ @return others Indicate other errors
+**/
+EFI_STATUS
+EFIAPI
+TdAcceptPages (
+ IN UINT64 StartAddress,
+ IN UINT64 NumberOfPages,
+ IN UINT32 PageSize
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Address;
+ UINT64 TdxStatus;
+ UINT64 Index;
+ UINT32 GpaPageLevel;
+ UINT32 PageSize2;
+
+ Address = StartAddress;
+
+ GpaPageLevel = GetGpaPageLevel (PageSize);
+ if (GpaPageLevel == -1) {
+ DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size - 0x%llx\n", PageSize));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ for (Index = 0; Index < NumberOfPages; Index++) {
+ TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel, 0, 0, 0);
+ if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {
+ if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {
+ //
+ // Already accepted
+ //
+ mNumberOfDuplicatedAcceptedPages++;
+ DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));
+ } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {
+ //
+ // GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if possible
+ //
+ DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in PageLevel of %d\n", Address, GpaPageLevel));
+
+ if (GpaPageLevel == 0) {
+ //
+ // Cannot fall back to smaller page level
+ //
+ DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from PageLevel %d\n", GpaPageLevel));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ } else {
+ //
+ // Fall back to a smaller page size
+ //
+ PageSize2 = mTdxAcceptPageLevelMap [GpaPageLevel - 1];
+ Status = TdAcceptPages(Address, 512, PageSize2);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ }else {
+
+ //
+ // Other errors
+ //
+ DEBUG ((DEBUG_ERROR, "Address %llx (%d) failed to be accepted. Error = 0x%llx\n",
+ Address, Index, TdxStatus));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ }
+ Address += PageSize;
+ }
+ return Status;
+}
diff --git a/MdePkg/Library/TdxLib/Rtmr.c b/MdePkg/Library/TdxLib/Rtmr.c
new file mode 100644
index 000000000000..42e85eb9d9bb
--- /dev/null
+++ b/MdePkg/Library/TdxLib/Rtmr.c
@@ -0,0 +1,83 @@
+/** @file
+
+ Extends one of the RTMR measurement registers in TDCS with the provided
+ extension data in memory.
+
+ Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TdxLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <IndustryStandard/Tpm20.h>
+#include <IndustryStandard/Tdx.h>
+
+#define RTMR_COUNT 4
+#define TD_EXTEND_BUFFER_LEN (64 + 48)
+
+UINT8 mExtendBuffer[TD_EXTEND_BUFFER_LEN];
+
+/**
+ This function extends one of the RTMR measurement register
+ in TDCS with the provided extension data in memory.
+ RTMR extending supports SHA384 which length is 48 bytes.
+
+ @param[in] Data Point to the data to be extended
+ @param[in] DataLen Length of the data. Must be 48
+ @param[in] Index RTMR index
+
+ @return EFI_SUCCESS
+ @return EFI_INVALID_PARAMETER
+ @return EFI_DEVICE_ERROR
+
+**/
+EFI_STATUS
+EFIAPI
+TdExtendRtmr (
+ IN UINT32 *Data,
+ IN UINT32 DataLen,
+ IN UINT8 Index
+ )
+{
+ EFI_STATUS Status;
+ UINT64 TdCallStatus;
+ UINT8 *ExtendBuffer;
+
+ Status = EFI_SUCCESS;
+
+ ASSERT (Data != NULL);
+ ASSERT (DataLen == SHA384_DIGEST_SIZE);
+ ASSERT (Index >= 0 && Index < RTMR_COUNT);
+
+ if (Data == NULL || DataLen != SHA384_DIGEST_SIZE || Index >= RTMR_COUNT) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // TD.RTMR.EXTEND requires 64B-aligned guest physical address of
+ // 48B-extension data. We use ALIGN_POINTER(Pointer, 64) to get
+ // the 64B-aligned guest physical address.
+ ExtendBuffer = ALIGN_POINTER (mExtendBuffer, 64);
+ ASSERT (((UINTN)ExtendBuffer & 0x3f) == 0 );
+
+ ZeroMem (ExtendBuffer, SHA384_DIGEST_SIZE);
+ CopyMem (ExtendBuffer, Data, SHA384_DIGEST_SIZE);
+
+ TdCallStatus = TdCall (TDCALL_TDEXTENDRTMR, (UINT64)(UINTN)ExtendBuffer, Index, 0, 0);
+
+ if (TdCallStatus == TDX_EXIT_REASON_SUCCESS) {
+ Status = EFI_SUCCESS;
+ } else if (TdCallStatus == TDX_EXIT_REASON_OPERAND_INVALID) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((DEBUG_ERROR, "Error returned from TdExtendRtmr call - 0x%lx\n", TdCallStatus));
+ }
+
+ return Status;
+}
diff --git a/MdePkg/Library/TdxLib/TdInfo.c b/MdePkg/Library/TdxLib/TdInfo.c
new file mode 100644
index 000000000000..56c268e70c8d
--- /dev/null
+++ b/MdePkg/Library/TdxLib/TdInfo.c
@@ -0,0 +1,103 @@
+/** @file
+
+ Fetch the Tdx info.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
+#include <Library/BaseMemoryLib.h>
+
+UINT64 mTdSharedPageMask = 0;
+UINT32 mTdMaxVCpuNum = 0;
+UINT32 mTdVCpuNum = 0;
+
+/**
+ This function gets the Td guest shared page mask.
+
+ The guest indicates if a page is shared using the Guest Physical Address
+ (GPA) Shared (S) bit. If the GPA Width(GPAW) is 48, the S-bit is bit-47.
+ If the GPAW is 52, the S-bit is bit-51.
+
+ @return Shared page bit mask
+**/
+UINT64
+EFIAPI
+TdSharedPageMask (
+ VOID
+ )
+{
+ UINT64 Status;
+ UINT8 Gpaw;
+ TD_RETURN_DATA TdReturnData;
+
+ if (mTdSharedPageMask != 0) {
+ return mTdSharedPageMask;
+ }
+
+ Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ ASSERT (Status == TDX_EXIT_REASON_SUCCESS);
+
+ Gpaw = (UINT8)(TdReturnData.TdInfo.Gpaw & 0x3f);
+ ASSERT(Gpaw == 48 || Gpaw == 52);
+ mTdSharedPageMask = 1ULL << (Gpaw - 1);
+ return mTdSharedPageMask;
+}
+
+/**
+ This function gets the maximum number of Virtual CPUs that are usable for
+ Td Guest.
+
+ @return maximum Virtual CPUs number
+**/
+UINT32
+EFIAPI
+TdMaxVCpuNum (
+ VOID
+ )
+{
+ UINT64 Status;
+ TD_RETURN_DATA TdReturnData;
+
+ if (mTdMaxVCpuNum != 0) {
+ return mTdMaxVCpuNum;
+ }
+
+ Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ ASSERT (Status == TDX_EXIT_REASON_SUCCESS);
+
+ mTdMaxVCpuNum = TdReturnData.TdInfo.MaxVcpus;
+
+ return mTdMaxVCpuNum;
+}
+
+/**
+ This function gets the number of Virtual CPUs that are usable for Td
+ Guest.
+
+ @return Virtual CPUs number
+**/
+UINT32
+EFIAPI
+TdVCpuNum (
+ VOID
+ )
+{
+ UINT64 Status;
+ TD_RETURN_DATA TdReturnData;
+
+ if (mTdVCpuNum != 0) {
+ return mTdVCpuNum;
+ }
+
+ Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ ASSERT (Status == TDX_EXIT_REASON_SUCCESS);
+
+ mTdVCpuNum = TdReturnData.TdInfo.NumVcpus;
+ return mTdVCpuNum;
+}
diff --git a/MdePkg/Library/TdxLib/TdxLib.inf b/MdePkg/Library/TdxLib/TdxLib.inf
new file mode 100644
index 000000000000..772abcc49d8b
--- /dev/null
+++ b/MdePkg/Library/TdxLib/TdxLib.inf
@@ -0,0 +1,39 @@
+## @file
+# Tdx library
+#
+# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = TdxLib
+ FILE_GUID = 032A8E0D-0C27-40C0-9CAA-23B731C1B223
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TdxLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources.IA32]
+ TdxLibNull.c
+
+[Sources.X64]
+ AcceptPages.c
+ Rtmr.c
+ TdInfo.c
+ X64/Tdcall.nasm
+ X64/Tdvmcall.nasm
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
diff --git a/MdePkg/Library/TdxLib/TdxLibNull.c b/MdePkg/Library/TdxLib/TdxLibNull.c
new file mode 100644
index 000000000000..d57f03b29cfe
--- /dev/null
+++ b/MdePkg/Library/TdxLib/TdxLibNull.c
@@ -0,0 +1,192 @@
+/** @file
+
+ Null stub of TdxLib
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TdxLib.h>
+
+/**
+ This function accepts a pending private page, and initialize the page to
+ all-0 using the TD ephemeral private key.
+
+ @param[in] StartAddress Guest physical address of the private page
+ to accept.
+ @param[in] NumberOfPages Number of the pages to be accepted.
+ @param[in] PageSize GPA page size. Accept 1G/2M/4K page size.
+
+ @return EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+TdAcceptPages (
+ IN UINT64 StartAddress,
+ IN UINT64 NumberOfPages,
+ IN UINT32 PageSize
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ This function extends one of the RTMR measurement register
+ in TDCS with the provided extension data in memory.
+ RTMR extending supports SHA384 which length is 48 bytes.
+
+ @param[in] Data Point to the data to be extended
+ @param[in] DataLen Length of the data. Must be 48
+ @param[in] Index RTMR index
+
+ @return EFI_SUCCESS
+ @return EFI_INVALID_PARAMETER
+ @return EFI_DEVICE_ERROR
+
+**/
+EFI_STATUS
+EFIAPI
+TdExtendRtmr (
+ IN UINT32 *Data,
+ IN UINT32 DataLen,
+ IN UINT8 Index
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ This function gets the Td guest shared page mask.
+
+ The guest indicates if a page is shared using the Guest Physical Address
+ (GPA) Shared (S) bit. If the GPA Width(GPAW) is 48, the S-bit is bit-47.
+ If the GPAW is 52, the S-bit is bit-51.
+
+ @return Shared page bit mask
+**/
+UINT64
+EFIAPI
+TdSharedPageMask (
+ VOID
+ )
+{
+ return 0;
+}
+
+
+/**
+ This function gets the maximum number of Virtual CPUs that are usable for
+ Td Guest.
+
+ @return maximum Virtual CPUs number
+**/
+UINT32
+EFIAPI
+TdMaxVCpuNum (
+ VOID
+ )
+{
+ return 0;
+}
+
+
+/**
+ This function gets the number of Virtual CPUs that are usable for Td
+ Guest.
+
+ @return Virtual CPUs number
+**/
+UINT32
+EFIAPI
+TdVCpuNum (
+ VOID
+ )
+{
+ return 0;
+}
+
+
+/**
+ The TDCALL instruction causes a VM exit to the Intel TDX module. It is
+ used to call guest-side Intel TDX functions, either local or a TD exit
+ to the host VMM, as selected by Leaf.
+ Leaf functions are described at <https://software.intel.com/content/
+ www/us/en/develop/articles/intel-trust-domain-extensions.html>
+
+ @param[in] Leaf Leaf number of TDCALL instruction
+ @param[in] Arg1 Arg1
+ @param[in] Arg2 Arg2
+ @param[in] Arg3 Arg3
+ @param[in,out] Results Returned result of the Leaf function
+
+ @return EFI_SUCCESS
+ @return Other See individual leaf functions
+**/
+EFI_STATUS
+EFIAPI
+TdCall (
+ IN UINT64 Leaf,
+ IN UINT64 Arg1,
+ IN UINT64 Arg2,
+ IN UINT64 Arg3,
+ IN OUT VOID *Results
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ TDVMALL is a leaf function 0 for TDCALL. It helps invoke services from the
+ host VMM to pass/receive information.
+
+ @param[in] Leaf Number of sub-functions
+ @param[in] Arg1 Arg1
+ @param[in] Arg2 Arg2
+ @param[in] Arg3 Arg3
+ @param[in] Arg4 Arg4
+ @param[in,out] Results Returned result of the sub-function
+
+ @return EFI_SUCCESS
+ @return Other See individual sub-functions
+
+**/
+EFI_STATUS
+EFIAPI
+TdVmCall (
+ IN UINT64 Leaf,
+ IN UINT64 Arg1,
+ IN UINT64 Arg2,
+ IN UINT64 Arg3,
+ IN UINT64 Arg4,
+ IN OUT VOID *Results
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ This function enable the TD guest to request the VMM to emulate CPUID
+ operation, especially for non-architectural, CPUID leaves.
+
+ @param[in] Eax Main leaf of the CPUID
+ @param[in] Ecx Sub-leaf of the CPUID
+ @param[out] Results Returned result of CPUID operation
+
+ @return EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+TdVmCallCpuid (
+ IN UINT64 Eax,
+ IN UINT64 Ecx,
+ OUT VOID *Results
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdePkg/Library/TdxLib/X64/Tdcall.nasm b/MdePkg/Library/TdxLib/X64/Tdcall.nasm
new file mode 100644
index 000000000000..e8a094b0eb3f
--- /dev/null
+++ b/MdePkg/Library/TdxLib/X64/Tdcall.nasm
@@ -0,0 +1,85 @@
+;------------------------------------------------------------------------------
+;*
+;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+;* SPDX-License-Identifier: BSD-2-Clause-Patent
+;*
+;*
+;------------------------------------------------------------------------------
+
+DEFAULT REL
+SECTION .text
+
+%macro tdcall 0
+ db 0x66,0x0f,0x01,0xcc
+%endmacro
+
+%macro tdcall_push_regs 0
+ push rbp
+ mov rbp, rsp
+ push r15
+ push r14
+ push r13
+ push r12
+ push rbx
+ push rsi
+ push rdi
+%endmacro
+
+%macro tdcall_pop_regs 0
+ pop rdi
+ pop rsi
+ pop rbx
+ pop r12
+ pop r13
+ pop r14
+ pop r15
+ pop rbp
+%endmacro
+
+%define number_of_regs_pushed 8
+%define number_of_parameters 4
+
+;
+; Keep these in sync for push_regs/pop_regs, code below
+; uses them to find 5th or greater parameters
+;
+%define first_variable_on_stack_offset \
+ ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8)
+%define second_variable_on_stack_offset \
+ ((first_variable_on_stack_offset) + 8)
+
+; TdCall (
+; UINT64 Leaf, // Rcx
+; UINT64 P1, // Rdx
+; UINT64 P2, // R8
+; UINT64 P3, // R9
+; UINT64 Results, // rsp + 0x28
+; )
+global ASM_PFX(TdCall)
+ASM_PFX(TdCall):
+ tdcall_push_regs
+
+ mov rax, rcx
+ mov rcx, rdx
+ mov rdx, r8
+ mov r8, r9
+
+ tdcall
+
+ ; exit if tdcall reports failure.
+ test rax, rax
+ jnz .exit
+
+ ; test if caller wanted results
+ mov r12, [rsp + first_variable_on_stack_offset ]
+ test r12, r12
+ jz .exit
+ mov [r12 + 0 ], rcx
+ mov [r12 + 8 ], rdx
+ mov [r12 + 16], r8
+ mov [r12 + 24], r9
+ mov [r12 + 32], r10
+ mov [r12 + 40], r11
+.exit:
+ tdcall_pop_regs
+ ret
diff --git a/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm b/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm
new file mode 100644
index 000000000000..eb1cb967dc29
--- /dev/null
+++ b/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm
@@ -0,0 +1,207 @@
+;------------------------------------------------------------------------------
+;*
+;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+;* SPDX-License-Identifier: BSD-2-Clause-Patent
+;*
+;*
+;------------------------------------------------------------------------------
+
+DEFAULT REL
+SECTION .text
+
+%define TDVMCALL_EXPOSE_REGS_MASK 0xffec
+%define TDVMCALL 0x0
+%define EXIT_REASON_CPUID 0xa
+
+%macro tdcall 0
+ db 0x66,0x0f,0x01,0xcc
+%endmacro
+
+%macro tdcall_push_regs 0
+ push rbp
+ mov rbp, rsp
+ push r15
+ push r14
+ push r13
+ push r12
+ push rbx
+ push rsi
+ push rdi
+%endmacro
+
+%macro tdcall_pop_regs 0
+ pop rdi
+ pop rsi
+ pop rbx
+ pop r12
+ pop r13
+ pop r14
+ pop r15
+ pop rbp
+%endmacro
+
+%define number_of_regs_pushed 8
+%define number_of_parameters 4
+
+;
+; Keep these in sync for push_regs/pop_regs, code below
+; uses them to find 5th or greater parameters
+;
+%define first_variable_on_stack_offset \
+ ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8)
+%define second_variable_on_stack_offset \
+ ((first_variable_on_stack_offset) + 8)
+
+%macro tdcall_regs_preamble 2
+ mov rax, %1
+
+ xor rcx, rcx
+ mov ecx, %2
+
+ ; R10 = 0 (standard TDVMCALL)
+
+ xor r10d, r10d
+
+ ; Zero out unused (for standard TDVMCALL) registers to avoid leaking
+ ; secrets to the VMM.
+
+ xor ebx, ebx
+ xor esi, esi
+ xor edi, edi
+
+ xor edx, edx
+ xor ebp, ebp
+ xor r8d, r8d
+ xor r9d, r9d
+%endmacro
+
+%macro tdcall_regs_postamble 0
+ xor ebx, ebx
+ xor esi, esi
+ xor edi, edi
+
+ xor ecx, ecx
+ xor edx, edx
+ xor r8d, r8d
+ xor r9d, r9d
+ xor r10d, r10d
+ xor r11d, r11d
+%endmacro
+
+;------------------------------------------------------------------------------
+; 0 => RAX = TDCALL leaf
+; M => RCX = TDVMCALL register behavior
+; 1 => R10 = standard vs. vendor
+; RDI => R11 = TDVMCALL function / nr
+; RSI = R12 = p1
+; RDX => R13 = p2
+; RCX => R14 = p3
+; R8 => R15 = p4
+
+; UINT64
+; EFIAPI
+; TdVmCall (
+; UINT64 Leaf, // Rcx
+; UINT64 P1, // Rdx
+; UINT64 P2, // R8
+; UINT64 P3, // R9
+; UINT64 P4, // rsp + 0x28
+; UINT64 *Val // rsp + 0x30
+; )
+global ASM_PFX(TdVmCall)
+ASM_PFX(TdVmCall):
+ tdcall_push_regs
+
+ mov r11, rcx
+ mov r12, rdx
+ mov r13, r8
+ mov r14, r9
+ mov r15, [rsp + first_variable_on_stack_offset ]
+
+ tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
+
+ tdcall
+
+ ; ignore return dataif TDCALL reports failure.
+ test rax, rax
+ jnz .no_return_data
+
+ ; Propagate TDVMCALL success/failure to return value.
+ mov rax, r10
+
+ ; Retrieve the Val pointer.
+ mov r9, [rsp + second_variable_on_stack_offset ]
+ test r9, r9
+ jz .no_return_data
+
+ ; On success, propagate TDVMCALL output value to output param
+ test rax, rax
+ jnz .no_return_data
+ mov [r9], r11
+.no_return_data:
+ tdcall_regs_postamble
+
+ tdcall_pop_regs
+
+ ret
+
+;------------------------------------------------------------------------------
+; 0 => RAX = TDCALL leaf
+; M => RCX = TDVMCALL register behavior
+; 1 => R10 = standard vs. vendor
+; RDI => R11 = TDVMCALL function / nr
+; RSI = R12 = p1
+; RDX => R13 = p2
+; RCX => R14 = p3
+; R8 => R15 = p4
+
+; UINT64
+; EFIAPI
+; TdVmCallCpuid (
+; UINT64 EaxIn, // Rcx
+; UINT64 EcxIn, // Rdx
+; UINT64 *Results // R8
+; )
+global ASM_PFX(TdVmCallCpuid)
+ASM_PFX(TdVmCallCpuid):
+ tdcall_push_regs
+
+ mov r11, EXIT_REASON_CPUID
+ mov r12, rcx
+ mov r13, rdx
+
+ ; Save *results pointers
+ push r8
+
+ tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
+
+ tdcall
+
+ ; Panic if TDCALL reports failure.
+ test rax, rax
+ jnz .no_return_data
+
+ ; Propagate TDVMCALL success/failure to return value.
+ mov rax, r10
+ test rax, rax
+ jnz .no_return_data
+
+ ; Retrieve *Results
+ pop r8
+ test r8, r8
+ jz .no_return_data
+ ; Caller pass in buffer so store results r12-r15 contains eax-edx
+ mov [r8 + 0], r12
+ mov [r8 + 8], r13
+ mov [r8 + 16], r14
+ mov [r8 + 24], r15
+
+.no_return_data:
+ tdcall_regs_postamble
+
+ tdcall_pop_regs
+
+ ret
+
+.panic:
+ ud2
diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
index 8b18415b107a..321a14fbaa0a 100644
--- a/MdePkg/MdePkg.dec
+++ b/MdePkg/MdePkg.dec
@@ -296,6 +296,9 @@
## @libraryclass Provides services to log the SMI handler registration.
SmiHandlerProfileLib|Include/Library/SmiHandlerProfileLib.h
+ ## @libraryclass Provides function to support TDX processing.
+ TdxLib|Include/Library/TdxLib.h
+
[Guids]
#
# GUID defined in UEFI2.1/UEFI2.0/EFI1.1
diff --git a/MdePkg/MdePkg.dsc b/MdePkg/MdePkg.dsc
index a94959169b2f..d6a7af412be7 100644
--- a/MdePkg/MdePkg.dsc
+++ b/MdePkg/MdePkg.dsc
@@ -175,6 +175,7 @@
MdePkg/Library/SmiHandlerProfileLibNull/SmiHandlerProfileLibNull.inf
MdePkg/Library/MmServicesTableLib/MmServicesTableLib.inf
MdePkg/Library/MmUnblockMemoryLib/MmUnblockMemoryLibNull.inf
+ MdePkg/Library/TdxLib/TdxLib.inf
[Components.EBC]
MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-01 13:15 ` [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations Min Xu
@ 2021-11-02 14:06 ` Gerd Hoffmann
2021-11-10 4:58 ` [edk2-devel] " Min Xu
2021-11-10 10:38 ` Erdem Aktas
1 sibling, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:06 UTC (permalink / raw)
To: Min Xu
Cc: devel, Michael D Kinney, Liming Gao, Zhiguang Liu, Brijesh Singh,
Erdem Aktas, James Bottomley, Jiewen Yao, Tom Lendacky
Hi,
> +UINT64 mTdSharedPageMask = 0;
> +UINT32 mTdMaxVCpuNum = 0;
> +UINT32 mTdVCpuNum = 0;
> +UINT64
> +EFIAPI
> +TdSharedPageMask (
> + VOID
> + )
> +{
> + UINT64 Status;
> + UINT8 Gpaw;
> + TD_RETURN_DATA TdReturnData;
> +
> + if (mTdSharedPageMask != 0) {
> + return mTdSharedPageMask;
> + }
Small possible optimization: you can cache the whole TD_RETURN_DATA
struct instead of the three extracted values, then ...
> + Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
> + ASSERT (Status == TDX_EXIT_REASON_SUCCESS);
... you need a single TDCALL_TDINFO call only.
> + tdcall
> +
> + ; Panic if TDCALL reports failure.
> + test rax, rax
> + jnz .no_return_data
> +.panic:
> + ud2
Comment doesn't match code. jnz .panic ?
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-02 14:06 ` Gerd Hoffmann
@ 2021-11-10 4:58 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-10 4:58 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com
Cc: Kinney, Michael D, Liming Gao, Liu, Zhiguang, Brijesh Singh,
Erdem Aktas, James Bottomley, Yao, Jiewen, Tom Lendacky
On November 2, 2021 10:07 PM, Gerd Hoffmann wrote:
> Hi,
>
> > +UINT64 mTdSharedPageMask = 0;
> > +UINT32 mTdMaxVCpuNum = 0;
> > +UINT32 mTdVCpuNum = 0;
>
> > +UINT64
> > +EFIAPI
> > +TdSharedPageMask (
> > + VOID
> > + )
> > +{
> > + UINT64 Status;
> > + UINT8 Gpaw;
> > + TD_RETURN_DATA TdReturnData;
> > +
> > + if (mTdSharedPageMask != 0) {
> > + return mTdSharedPageMask;
> > + }
>
> Small possible optimization: you can cache the whole TD_RETURN_DATA struct
> instead of the three extracted values, then ...
>
> > + Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData); ASSERT
> > + (Status == TDX_EXIT_REASON_SUCCESS);
>
> ... you need a single TDCALL_TDINFO call only.
Thanks for reminder. It will be updated in the next version.
>
> > + tdcall
> > +
> > + ; Panic if TDCALL reports failure.
> > + test rax, rax
> > + jnz .no_return_data
>
> > +.panic:
> > + ud2
>
> Comment doesn't match code. jnz .panic ?
Ah, good catch. It should jnz .panic. It will be fixed in the next version.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-01 13:15 ` [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations Min Xu
2021-11-02 14:06 ` Gerd Hoffmann
@ 2021-11-10 10:38 ` Erdem Aktas
2021-11-12 2:38 ` Min Xu
1 sibling, 1 reply; 107+ messages in thread
From: Erdem Aktas @ 2021-11-10 10:38 UTC (permalink / raw)
To: Min Xu
Cc: devel, Michael D Kinney, Liming Gao, Zhiguang Liu, Brijesh Singh,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
Hi Min,
Sorry for the late review. My comments and a few questions are inline.
Thanks
-Erdem
On Mon, Nov 1, 2021 at 6:16 AM Min Xu <min.m.xu@intel.com> wrote:
>
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
....
> +**/
> +UINT32
> +GetGpaPageLevel (
> + UINT32 PageSize
> + )
> +{
> + UINT32 Index;
> +
> + for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {
> + if (mTdxAcceptPageLevelMap[Index] == PageSize) {
> + break;
> + }
> + }
> +
> + return Index == ARRAY_SIZE (mTdxAcceptPageLevelMap) ? -1 : Index;
Is this intentional? Returning signed integer but the function returns
unsigned integer.
+/**
+ This function accepts a pending private page, and initialize the page to
+ all-0 using the TD ephemeral private key.
+
+ @param[in] StartAddress Guest physical address of the private page
+ to accept.
+ @param[in] NumberOfPages Number of the pages to be accepted.
+ @param[in] PageSize GPA page size. Accept 1G/2M/4K page size.
The comment says that 1G is acceptable but the code only accepts 2M or
4K page sizes.
+
+ @return EFI_SUCCESS
> +EFI_STATUS
> +EFIAPI
> +TdAcceptPages (
> + IN UINT64 StartAddress,
> + IN UINT64 NumberOfPages,
> + IN UINT32 PageSize
> + )
> +{
> + EFI_STATUS Status;
> + UINT64 Address;
> + UINT64 TdxStatus;
> + UINT64 Index;
> + UINT32 GpaPageLevel;
> + UINT32 PageSize2;
> +
> + Address = StartAddress;
> +
> + GpaPageLevel = GetGpaPageLevel (PageSize);
> + if (GpaPageLevel == -1) {
Comparing unsigned int to signed int. I believe this should work with
GCC with warning messages.
Just checking if this is intentional?
> + DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size - 0x%llx\n", PageSize));
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Status = EFI_SUCCESS;
> + for (Index = 0; Index < NumberOfPages; Index++) {
> + TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel, 0, 0, 0);
Address[11:3] and [63:52] are reserved and must be 0. The code does
not check it, spec is not clear about the behavior but I am assuming
that in that case, TDX_OPERAND_INVALID will be returned as error code
but should we check and log it properly?
> + if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {
> + if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {
> + //
> + // Already accepted
> + //
> + mNumberOfDuplicatedAcceptedPages++;
Is there any legit case that a page should be accepted twice? Looks
like other than debug printing, this information is ignored.
> + DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));
> + } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {
> + //
> + // GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if possible
> + //
> + DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in PageLevel of %d\n", Address, GpaPageLevel));
> +
> + if (GpaPageLevel == 0) {
> + //
> + // Cannot fall back to smaller page level
> + //
Could you help me understand this? So if ,for some reason, VMM maps a
2MB private page and a guest wants to accept it in 4KB page chunks,
this will always fail. Is it not a possible use case?
> + DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from PageLevel %d\n", GpaPageLevel));
> + Status = EFI_INVALID_PARAMETER;
> + break;
> + } else {
> + //
> + // Fall back to a smaller page size
> + //
> + PageSize2 = mTdxAcceptPageLevelMap [GpaPageLevel - 1];
> + Status = TdAcceptPages(Address, 512, PageSize2);
> + if (EFI_ERROR (Status)) {
> + break;
> + }
> + }
> + }else {
> +
> + //
> + // Other errors
> + //
Why not handle the TDX_OPERAND_BUSY? It is not an error and tdaccept
needs to be retired, I guess, handling it like an error might cause
reliability issues.
> + DEBUG ((DEBUG_ERROR, "Address %llx (%d) failed to be accepted. Error = 0x%llx\n",
> + Address, Index, TdxStatus));
> + Status = EFI_INVALID_PARAMETER;
> + break;
> + }
> + }
> + Address += PageSize;
> + }
> + return Status;
> +}
.....
> +**/
> +EFI_STATUS
> +EFIAPI
> +TdExtendRtmr (
> + IN UINT32 *Data,
> + IN UINT32 DataLen,
> + IN UINT8 Index
> + )
> +{
> + EFI_STATUS Status;
> + UINT64 TdCallStatus;
> + UINT8 *ExtendBuffer;
> +
> + Status = EFI_SUCCESS;
> +
> + ASSERT (Data != NULL);
> + ASSERT (DataLen == SHA384_DIGEST_SIZE);
> + ASSERT (Index >= 0 && Index < RTMR_COUNT);
> +
Because of the Asserts above , the following condition will never run, right?
> + if (Data == NULL || DataLen != SHA384_DIGEST_SIZE || Index >= RTMR_COUNT) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
...
> +; UINT64
> +; EFIAPI
> +; TdVmCall (
> +; UINT64 Leaf, // Rcx
> +; UINT64 P1, // Rdx
> +; UINT64 P2, // R8
> +; UINT64 P3, // R9
> +; UINT64 P4, // rsp + 0x28
> +; UINT64 *Val // rsp + 0x30
> +; )
> +global ASM_PFX(TdVmCall)
> +ASM_PFX(TdVmCall):
> + tdcall_push_regs
> +
> + mov r11, rcx
> + mov r12, rdx
> + mov r13, r8
> + mov r14, r9
> + mov r15, [rsp + first_variable_on_stack_offset ]
> +
> + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
> +
> + tdcall
> +
> + ; ignore return dataif TDCALL reports failure.
should we not panic here also?
> + test rax, rax
> + jnz .no_return_data
> +
> + ; Propagate TDVMCALL success/failure to return value.
> + mov rax, r10
> +
> + ; Retrieve the Val pointer.
> + mov r9, [rsp + second_variable_on_stack_offset ]
> + test r9, r9
> + jz .no_return_data
> +
> + ; On success, propagate TDVMCALL output value to output param
> + test rax, rax
> + jnz .no_return_data
> + mov [r9], r11
> +.no_return_data:
> + tdcall_regs_postamble
> +
> + tdcall_pop_regs
> +
> + ret
> +
> +;------------------------------------------------------------------------------
> +; 0 => RAX = TDCALL leaf
> +; M => RCX = TDVMCALL register behavior
> +; 1 => R10 = standard vs. vendor
> +; RDI => R11 = TDVMCALL function / nr
> +; RSI = R12 = p1
> +; RDX => R13 = p2
> +; RCX => R14 = p3
> +; R8 => R15 = p4
> +
Above comment does not match the below definition.
> +; UINT64
> +; EFIAPI
> +; TdVmCallCpuid (
> +; UINT64 EaxIn, // Rcx
> +; UINT64 EcxIn, // Rdx
> +; UINT64 *Results // R8
> +; )
> +global ASM_PFX(TdVmCallCpuid)
> +ASM_PFX(TdVmCallCpuid):
> + tdcall_push_regs
> +
> + mov r11, EXIT_REASON_CPUID
> + mov r12, rcx
> + mov r13, rdx
> +
> + ; Save *results pointers
> + push r8
> +
Looks like we are leaking r14 and r15 here.
> + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
> +
> + tdcall
> +
> + ; Panic if TDCALL reports failure.
> + test rax, rax
> + jnz .no_return_data
> +
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-10 10:38 ` Erdem Aktas
@ 2021-11-12 2:38 ` Min Xu
2021-11-12 2:42 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-12 2:38 UTC (permalink / raw)
To: Erdem Aktas
Cc: devel@edk2.groups.io, Kinney, Michael D, Liming Gao,
Liu, Zhiguang, Brijesh Singh, James Bottomley, Yao, Jiewen,
Tom Lendacky, Gerd Hoffmann
On November 10, 2021 6:38 PM, Erdem Aktas wrote:
> On Mon, Nov 1, 2021 at 6:16 AM Min Xu <min.m.xu@intel.com> wrote:
> >
> > RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
> >
> ....
> > +**/
> > +UINT32
> > +GetGpaPageLevel (
> > + UINT32 PageSize
> > + )
> > +{
> > + UINT32 Index;
> > +
> > + for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++)
> {
> > + if (mTdxAcceptPageLevelMap[Index] == PageSize) {
> > + break;
> > + }
> > + }
> > +
> > + return Index == ARRAY_SIZE (mTdxAcceptPageLevelMap) ? -1 : Index;
>
> Is this intentional? Returning signed integer but the function returns
> unsigned integer.
Ah, -1 should not be returned because the function returns unsigned integer.
It will be fixed in the next version like this:
UINT32 mTdxAcceptPageLevelMap[2] = {
SIZE_4KB,
SIZE_2MB
};
#define INVALID_ACCEPT_PAGELEVEL ARRAY_SIZE(mTdxAcceptPageLevelMap)
UINT32
GetGpaPageLevel (
UINT32 PageSize
)
{
UINT32 Index;
for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {
if (mTdxAcceptPageLevelMap[Index] == PageSize) {
break;
}
}
return Index;
}
... ...
GpaPageLevel = GetGpaPageLevel (PageSize);
if (GpaPageLevel == INVALID_ACCEPT_PAGELEVEL) {
DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size - 0x%llx\n", PageSize));
return EFI_INVALID_PARAMETER;
}
>
> +/**
> + This function accepts a pending private page, and initialize the page
> +to
> + all-0 using the TD ephemeral private key.
> +
> + @param[in] StartAddress Guest physical address of the private page
> + to accept.
> + @param[in] NumberOfPages Number of the pages to be accepted.
> + @param[in] PageSize GPA page size. Accept 1G/2M/4K page size.
>
> The comment says that 1G is acceptable but the code only accepts 2M or 4K
> page sizes.
Currently 2M/4K accept page size is supported. The comments will be fixed in the next version.
>
> +
> + @return EFI_SUCCESS
> > +EFI_STATUS
> > +EFIAPI
> > +TdAcceptPages (
> > + IN UINT64 StartAddress,
> > + IN UINT64 NumberOfPages,
> > + IN UINT32 PageSize
> > + )
> > +{
> > + EFI_STATUS Status;
> > + UINT64 Address;
> > + UINT64 TdxStatus;
> > + UINT64 Index;
> > + UINT32 GpaPageLevel;
> > + UINT32 PageSize2;
> > +
> > + Address = StartAddress;
> > +
> > + GpaPageLevel = GetGpaPageLevel (PageSize); if (GpaPageLevel == -1)
> > + {
>
> Comparing unsigned int to signed int. I believe this should work with GCC
> with warning messages.
> Just checking if this is intentional?
It will be fixed. See my first comments.
>
> > + DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page
> size - 0x%llx\n", PageSize));
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + Status = EFI_SUCCESS;
> > + for (Index = 0; Index < NumberOfPages; Index++) {
> > + TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel,
> > + 0, 0, 0);
>
> Address[11:3] and [63:52] are reserved and must be 0. The code does not
> check it, spec is not clear about the behavior but I am assuming that in that
> case, TDX_OPERAND_INVALID will be returned as error code but should we
> check and log it properly?
Right. The input address should be checked with Address[11:3] and [63:52].
Address[2:0] should be 0 too. Because Address[2:0] will be set with Accept Page Level.
It will be fixed in the next version.
>
> > + if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {
> > + if ((TdxStatus & ~0xFFFFULL) ==
> TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {
> > + //
> > + // Already accepted
> > + //
> > + mNumberOfDuplicatedAcceptedPages++;
>
> Is there any legit case that a page should be accepted twice? Looks like other
> than debug printing, this information is ignored.
Ideally a page should be accepted only once. But if a page is accepted twice, it is not an error (from the perspective of TdCall_Accept). In the PoC we do ran into such cases (it is a bug in the code). So we keep it as debug printing.
>
> > + DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been
> accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));
> > + } else if ((TdxStatus & ~0xFFFFULL) ==
> TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {
> > + //
> > + // GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if
> possible
> > + //
> > + DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in
> > + PageLevel of %d\n", Address, GpaPageLevel));
> > +
> > + if (GpaPageLevel == 0) {
> > + //
> > + // Cannot fall back to smaller page level
> > + //
>
> Could you help me understand this? So if ,for some reason, VMM maps a
> 2MB private page and a guest wants to accept it in 4KB page chunks, this will
> always fail. Is it not a possible use case?
Guest want to accept page with 2M size, but in some case, the VMM cannot do that with 2M page size. In this case, Guest will get a returned result (TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) from the TdCall. So Guest falls back to accept the page with a smaller page size. Currently only 2M/4K accept page size is supported. In the future, 1G accept page size will be supported. In that case, there may be 2 fall backs: 1G->2M and 2M->4K. But if the accept page size is 4K, it cannot fall back to a smaller size.
Guest start to accept pages, VMM just response to the accept page command from Guest.
>
> > + DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from
> PageLevel %d\n", GpaPageLevel));
> > + Status = EFI_INVALID_PARAMETER;
> > + break;
> > + } else {
> > + //
> > + // Fall back to a smaller page size
> > + //
> > + PageSize2 = mTdxAcceptPageLevelMap [GpaPageLevel - 1];
> > + Status = TdAcceptPages(Address, 512, PageSize2);
> > + if (EFI_ERROR (Status)) {
> > + break;
> > + }
> > + }
> > + }else {
> > +
> > + //
> > + // Other errors
> > + //
>
> Why not handle the TDX_OPERAND_BUSY? It is not an error and tdaccept
> needs to be retired, I guess, handling it like an error might cause reliability
> issues.
Thanks for reminder. It will be fixed in the next version.
>
> > + DEBUG ((DEBUG_ERROR, "Address %llx (%d) failed to be accepted.
> Error = 0x%llx\n",
> > + Address, Index, TdxStatus));
> > + Status = EFI_INVALID_PARAMETER;
> > + break;
> > + }
> > + }
> > + Address += PageSize;
> > + }
> > + return Status;
> > +}
>
> .....
>
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +TdExtendRtmr (
> > + IN UINT32 *Data,
> > + IN UINT32 DataLen,
> > + IN UINT8 Index
> > + )
> > +{
> > + EFI_STATUS Status;
> > + UINT64 TdCallStatus;
> > + UINT8 *ExtendBuffer;
> > +
> > + Status = EFI_SUCCESS;
> > +
> > + ASSERT (Data != NULL);
> > + ASSERT (DataLen == SHA384_DIGEST_SIZE); ASSERT (Index >= 0 &&
> > + Index < RTMR_COUNT);
> > +
>
> Because of the Asserts above , the following condition will never run, right?
ASSERT () is for debugging purpose. It will be ignored in release mode. So the following condition will run in release mode.
See https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DebugLib.h#L385-L409
>
> > + if (Data == NULL || DataLen != SHA384_DIGEST_SIZE || Index >=
> RTMR_COUNT) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
>
> ...
>
> > +; UINT64
> > +; EFIAPI
> > +; TdVmCall (
> > +; UINT64 Leaf, // Rcx
> > +; UINT64 P1, // Rdx
> > +; UINT64 P2, // R8
> > +; UINT64 P3, // R9
> > +; UINT64 P4, // rsp + 0x28
> > +; UINT64 *Val // rsp + 0x30
> > +; )
> > +global ASM_PFX(TdVmCall)
> > +ASM_PFX(TdVmCall):
> > + tdcall_push_regs
> > +
> > + mov r11, rcx
> > + mov r12, rdx
> > + mov r13, r8
> > + mov r14, r9
> > + mov r15, [rsp + first_variable_on_stack_offset ]
> > +
> > + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
> > +
> > + tdcall
> > +
> > + ; ignore return dataif TDCALL reports failure.
>
> should we not panic here also?
I don't think we should panic here.
Because TdVmCall is a common function (see sub-leaf of TDVMCALL). There are various errors. Some of them are not so serious that panic is needed. Caller of TdVmCall will handle the returned result.
>
> > + test rax, rax
> > + jnz .no_return_data
> > +
> > + ; Propagate TDVMCALL success/failure to return value.
> > + mov rax, r10
> > +
> > + ; Retrieve the Val pointer.
> > + mov r9, [rsp + second_variable_on_stack_offset ]
> > + test r9, r9
> > + jz .no_return_data
> > +
> > + ; On success, propagate TDVMCALL output value to output param
> > + test rax, rax
> > + jnz .no_return_data
> > + mov [r9], r11
> > +.no_return_data:
> > + tdcall_regs_postamble
> > +
> > + tdcall_pop_regs
> > +
> > + ret
> > +
> > +;------------------------------------------------------------------------------
> > +; 0 => RAX = TDCALL leaf
> > +; M => RCX = TDVMCALL register behavior
> > +; 1 => R10 = standard vs. vendor
> > +; RDI => R11 = TDVMCALL function / nr ; RSI = R12 = p1 ; RDX => R13
> > += p2 ; RCX => R14 = p3 ; R8 => R15 = p4
> > +
>
> Above comment does not match the below definition.
Thanks for reminder. It will be updated in the next version.
>
> > +; UINT64
> > +; EFIAPI
> > +; TdVmCallCpuid (
> > +; UINT64 EaxIn, // Rcx
> > +; UINT64 EcxIn, // Rdx
> > +; UINT64 *Results // R8
> > +; )
> > +global ASM_PFX(TdVmCallCpuid)
> > +ASM_PFX(TdVmCallCpuid):
> > + tdcall_push_regs
> > +
> > + mov r11, EXIT_REASON_CPUID
> > + mov r12, rcx
> > + mov r13, rdx
> > +
> > + ; Save *results pointers
> > + push r8
> > +
>
> Looks like we are leaking r14 and r15 here.
Thanks for reminder. It will be fixed in the next version.
>
> > + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
> > +
> > + tdcall
> > +
> > + ; Panic if TDCALL reports failure.
> > + test rax, rax
> > + jnz .no_return_data
> > +
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-12 2:38 ` Min Xu
@ 2021-11-12 2:42 ` Yao, Jiewen
2021-11-12 5:29 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-12 2:42 UTC (permalink / raw)
To: Xu, Min M, Erdem Aktas
Cc: devel@edk2.groups.io, Kinney, Michael D, Liming Gao,
Liu, Zhiguang, Brijesh Singh, James Bottomley, Tom Lendacky,
Gerd Hoffmann
BTW: Is this internal API?
I feel we should add ASSERT() for invalid page size as well, to catch issue earlier.
Thank you
> -----Original Message-----
> From: Xu, Min M <min.m.xu@intel.com>
> Sent: Friday, November 12, 2021 10:39 AM
> To: Erdem Aktas <erdemaktas@google.com>
> Cc: devel@edk2.groups.io; Kinney, Michael D <michael.d.kinney@intel.com>;
> Liming Gao <gaoliming@byosoft.com.cn>; Liu, Zhiguang
> <zhiguang.liu@intel.com>; Brijesh Singh <brijesh.singh@amd.com>; James
> Bottomley <jejb@linux.ibm.com>; Yao, Jiewen <jiewen.yao@intel.com>; Tom
> Lendacky <Thomas.Lendacky@amd.com>; Gerd Hoffmann <kraxel@redhat.com>
> Subject: RE: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
>
> On November 10, 2021 6:38 PM, Erdem Aktas wrote:
> > On Mon, Nov 1, 2021 at 6:16 AM Min Xu <min.m.xu@intel.com> wrote:
> > >
> > > RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
> > >
> > ....
> > > +**/
> > > +UINT32
> > > +GetGpaPageLevel (
> > > + UINT32 PageSize
> > > + )
> > > +{
> > > + UINT32 Index;
> > > +
> > > + for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++)
> > {
> > > + if (mTdxAcceptPageLevelMap[Index] == PageSize) {
> > > + break;
> > > + }
> > > + }
> > > +
> > > + return Index == ARRAY_SIZE (mTdxAcceptPageLevelMap) ? -1 : Index;
> >
> > Is this intentional? Returning signed integer but the function returns
> > unsigned integer.
> Ah, -1 should not be returned because the function returns unsigned integer.
> It will be fixed in the next version like this:
>
> UINT32 mTdxAcceptPageLevelMap[2] = {
> SIZE_4KB,
> SIZE_2MB
> };
> #define INVALID_ACCEPT_PAGELEVEL ARRAY_SIZE(mTdxAcceptPageLevelMap)
>
> UINT32
> GetGpaPageLevel (
> UINT32 PageSize
> )
> {
> UINT32 Index;
> for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {
> if (mTdxAcceptPageLevelMap[Index] == PageSize) {
> break;
> }
> }
> return Index;
> }
> ... ...
> GpaPageLevel = GetGpaPageLevel (PageSize);
> if (GpaPageLevel == INVALID_ACCEPT_PAGELEVEL) {
> DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size -
> 0x%llx\n", PageSize));
> return EFI_INVALID_PARAMETER;
> }
> >
> > +/**
> > + This function accepts a pending private page, and initialize the page
> > +to
> > + all-0 using the TD ephemeral private key.
> > +
> > + @param[in] StartAddress Guest physical address of the private page
> > + to accept.
> > + @param[in] NumberOfPages Number of the pages to be accepted.
> > + @param[in] PageSize GPA page size. Accept 1G/2M/4K page size.
> >
> > The comment says that 1G is acceptable but the code only accepts 2M or 4K
> > page sizes.
> Currently 2M/4K accept page size is supported. The comments will be fixed in
> the next version.
> >
> > +
> > + @return EFI_SUCCESS
> > > +EFI_STATUS
> > > +EFIAPI
> > > +TdAcceptPages (
> > > + IN UINT64 StartAddress,
> > > + IN UINT64 NumberOfPages,
> > > + IN UINT32 PageSize
> > > + )
> > > +{
> > > + EFI_STATUS Status;
> > > + UINT64 Address;
> > > + UINT64 TdxStatus;
> > > + UINT64 Index;
> > > + UINT32 GpaPageLevel;
> > > + UINT32 PageSize2;
> > > +
> > > + Address = StartAddress;
> > > +
> > > + GpaPageLevel = GetGpaPageLevel (PageSize); if (GpaPageLevel == -1)
> > > + {
> >
> > Comparing unsigned int to signed int. I believe this should work with GCC
> > with warning messages.
> > Just checking if this is intentional?
> It will be fixed. See my first comments.
> >
> > > + DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page
> > size - 0x%llx\n", PageSize));
> > > + return EFI_INVALID_PARAMETER;
> > > + }
> > > +
> > > + Status = EFI_SUCCESS;
> > > + for (Index = 0; Index < NumberOfPages; Index++) {
> > > + TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel,
> > > + 0, 0, 0);
> >
> > Address[11:3] and [63:52] are reserved and must be 0. The code does not
> > check it, spec is not clear about the behavior but I am assuming that in that
> > case, TDX_OPERAND_INVALID will be returned as error code but should we
> > check and log it properly?
> Right. The input address should be checked with Address[11:3] and [63:52].
> Address[2:0] should be 0 too. Because Address[2:0] will be set with Accept Page
> Level.
> It will be fixed in the next version.
> >
> > > + if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {
> > > + if ((TdxStatus & ~0xFFFFULL) ==
> > TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {
> > > + //
> > > + // Already accepted
> > > + //
> > > + mNumberOfDuplicatedAcceptedPages++;
> >
> > Is there any legit case that a page should be accepted twice? Looks like other
> > than debug printing, this information is ignored.
> Ideally a page should be accepted only once. But if a page is accepted twice, it is
> not an error (from the perspective of TdCall_Accept). In the PoC we do ran into
> such cases (it is a bug in the code). So we keep it as debug printing.
> >
> > > + DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been
> > accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));
> > > + } else if ((TdxStatus & ~0xFFFFULL) ==
> > TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {
> > > + //
> > > + // GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if
> > possible
> > > + //
> > > + DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in
> > > + PageLevel of %d\n", Address, GpaPageLevel));
> > > +
> > > + if (GpaPageLevel == 0) {
> > > + //
> > > + // Cannot fall back to smaller page level
> > > + //
> >
> > Could you help me understand this? So if ,for some reason, VMM maps a
> > 2MB private page and a guest wants to accept it in 4KB page chunks, this will
> > always fail. Is it not a possible use case?
> Guest want to accept page with 2M size, but in some case, the VMM cannot do
> that with 2M page size. In this case, Guest will get a returned result
> (TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) from the TdCall. So Guest falls
> back to accept the page with a smaller page size. Currently only 2M/4K accept
> page size is supported. In the future, 1G accept page size will be supported. In
> that case, there may be 2 fall backs: 1G->2M and 2M->4K. But if the accept page
> size is 4K, it cannot fall back to a smaller size.
> Guest start to accept pages, VMM just response to the accept page command
> from Guest.
> >
> > > + DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from
> > PageLevel %d\n", GpaPageLevel));
> > > + Status = EFI_INVALID_PARAMETER;
> > > + break;
> > > + } else {
> > > + //
> > > + // Fall back to a smaller page size
> > > + //
> > > + PageSize2 = mTdxAcceptPageLevelMap [GpaPageLevel - 1];
> > > + Status = TdAcceptPages(Address, 512, PageSize2);
> > > + if (EFI_ERROR (Status)) {
> > > + break;
> > > + }
> > > + }
> > > + }else {
> > > +
> > > + //
> > > + // Other errors
> > > + //
> >
> > Why not handle the TDX_OPERAND_BUSY? It is not an error and tdaccept
> > needs to be retired, I guess, handling it like an error might cause reliability
> > issues.
> Thanks for reminder. It will be fixed in the next version.
> >
> > > + DEBUG ((DEBUG_ERROR, "Address %llx (%d) failed to be accepted.
> > Error = 0x%llx\n",
> > > + Address, Index, TdxStatus));
> > > + Status = EFI_INVALID_PARAMETER;
> > > + break;
> > > + }
> > > + }
> > > + Address += PageSize;
> > > + }
> > > + return Status;
> > > +}
> >
> > .....
> >
> > > +**/
> > > +EFI_STATUS
> > > +EFIAPI
> > > +TdExtendRtmr (
> > > + IN UINT32 *Data,
> > > + IN UINT32 DataLen,
> > > + IN UINT8 Index
> > > + )
> > > +{
> > > + EFI_STATUS Status;
> > > + UINT64 TdCallStatus;
> > > + UINT8 *ExtendBuffer;
> > > +
> > > + Status = EFI_SUCCESS;
> > > +
> > > + ASSERT (Data != NULL);
> > > + ASSERT (DataLen == SHA384_DIGEST_SIZE); ASSERT (Index >= 0 &&
> > > + Index < RTMR_COUNT);
> > > +
> >
> > Because of the Asserts above , the following condition will never run, right?
> ASSERT () is for debugging purpose. It will be ignored in release mode. So the
> following condition will run in release mode.
> See
> https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/Debu
> gLib.h#L385-L409
> >
> > > + if (Data == NULL || DataLen != SHA384_DIGEST_SIZE || Index >=
> > RTMR_COUNT) {
> > > + return EFI_INVALID_PARAMETER;
> > > + }
> > > +
> >
> > ...
> >
> > > +; UINT64
> > > +; EFIAPI
> > > +; TdVmCall (
> > > +; UINT64 Leaf, // Rcx
> > > +; UINT64 P1, // Rdx
> > > +; UINT64 P2, // R8
> > > +; UINT64 P3, // R9
> > > +; UINT64 P4, // rsp + 0x28
> > > +; UINT64 *Val // rsp + 0x30
> > > +; )
> > > +global ASM_PFX(TdVmCall)
> > > +ASM_PFX(TdVmCall):
> > > + tdcall_push_regs
> > > +
> > > + mov r11, rcx
> > > + mov r12, rdx
> > > + mov r13, r8
> > > + mov r14, r9
> > > + mov r15, [rsp + first_variable_on_stack_offset ]
> > > +
> > > + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
> > > +
> > > + tdcall
> > > +
> > > + ; ignore return dataif TDCALL reports failure.
> >
> > should we not panic here also?
> I don't think we should panic here.
> Because TdVmCall is a common function (see sub-leaf of TDVMCALL). There are
> various errors. Some of them are not so serious that panic is needed. Caller of
> TdVmCall will handle the returned result.
> >
> > > + test rax, rax
> > > + jnz .no_return_data
> > > +
> > > + ; Propagate TDVMCALL success/failure to return value.
> > > + mov rax, r10
> > > +
> > > + ; Retrieve the Val pointer.
> > > + mov r9, [rsp + second_variable_on_stack_offset ]
> > > + test r9, r9
> > > + jz .no_return_data
> > > +
> > > + ; On success, propagate TDVMCALL output value to output param
> > > + test rax, rax
> > > + jnz .no_return_data
> > > + mov [r9], r11
> > > +.no_return_data:
> > > + tdcall_regs_postamble
> > > +
> > > + tdcall_pop_regs
> > > +
> > > + ret
> > > +
> > > +;------------------------------------------------------------------------------
> > > +; 0 => RAX = TDCALL leaf
> > > +; M => RCX = TDVMCALL register behavior
> > > +; 1 => R10 = standard vs. vendor
> > > +; RDI => R11 = TDVMCALL function / nr ; RSI = R12 = p1 ; RDX => R13
> > > += p2 ; RCX => R14 = p3 ; R8 => R15 = p4
> > > +
> >
> > Above comment does not match the below definition.
> Thanks for reminder. It will be updated in the next version.
> >
> > > +; UINT64
> > > +; EFIAPI
> > > +; TdVmCallCpuid (
> > > +; UINT64 EaxIn, // Rcx
> > > +; UINT64 EcxIn, // Rdx
> > > +; UINT64 *Results // R8
> > > +; )
> > > +global ASM_PFX(TdVmCallCpuid)
> > > +ASM_PFX(TdVmCallCpuid):
> > > + tdcall_push_regs
> > > +
> > > + mov r11, EXIT_REASON_CPUID
> > > + mov r12, rcx
> > > + mov r13, rdx
> > > +
> > > + ; Save *results pointers
> > > + push r8
> > > +
> >
> > Looks like we are leaking r14 and r15 here.
> Thanks for reminder. It will be fixed in the next version.
> >
> > > + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
> > > +
> > > + tdcall
> > > +
> > > + ; Panic if TDCALL reports failure.
> > > + test rax, rax
> > > + jnz .no_return_data
> > > +
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-12 2:42 ` Yao, Jiewen
@ 2021-11-12 5:29 ` Min Xu
2021-11-12 5:33 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-12 5:29 UTC (permalink / raw)
To: Yao, Jiewen, Erdem Aktas
Cc: devel@edk2.groups.io, Kinney, Michael D, Liming Gao,
Liu, Zhiguang, Brijesh Singh, James Bottomley, Tom Lendacky,
Gerd Hoffmann
On November 12, 2021 10:42 AM, Yao Jiewen wrote:
> BTW: Is this internal API?
> I feel we should add ASSERT() for invalid page size as well, to catch issue
> earlier.
TdAcceptPages () is not an internal API. It is exposed in TdxLib.
Sure, I will add more check to the input parameter in the code.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
2021-11-12 5:29 ` Min Xu
@ 2021-11-12 5:33 ` Yao, Jiewen
0 siblings, 0 replies; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-12 5:33 UTC (permalink / raw)
To: Xu, Min M, Erdem Aktas
Cc: devel@edk2.groups.io, Kinney, Michael D, Liming Gao,
Liu, Zhiguang, Brijesh Singh, James Bottomley, Tom Lendacky,
Gerd Hoffmann
Sorry for the confusing. My word is not accurate.
When I say "internal" in this context, I really mean: it is produced and consumed by OVMF. The API will not be called by *third party*, such as OS loader, or Option ROM.
If so, it is OK to add ASSERT. If you look at the BaseLib, many functions add ASSERT. It can help to check the usage of API is correctly.
Thank you
Yao Jiewen
> -----Original Message-----
> From: Xu, Min M <min.m.xu@intel.com>
> Sent: Friday, November 12, 2021 1:30 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; Erdem Aktas
> <erdemaktas@google.com>
> Cc: devel@edk2.groups.io; Kinney, Michael D <michael.d.kinney@intel.com>;
> Liming Gao <gaoliming@byosoft.com.cn>; Liu, Zhiguang
> <zhiguang.liu@intel.com>; Brijesh Singh <brijesh.singh@amd.com>; James
> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> <Thomas.Lendacky@amd.com>; Gerd Hoffmann <kraxel@redhat.com>
> Subject: RE: [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations
>
> On November 12, 2021 10:42 AM, Yao Jiewen wrote:
> > BTW: Is this internal API?
> > I feel we should add ASSERT() for invalid page size as well, to catch issue
> > earlier.
> TdAcceptPages () is not an internal API. It is exposed in TdxLib.
> Sure, I will add more check to the input parameter in the code.
>
> Thanks
> Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 03/29] UefiCpuPkg: Extend VmgExitLibNull to handle #VE exception
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
2021-11-01 13:15 ` [PATCH V3 01/29] MdePkg: Add Tdx.h Min Xu
2021-11-01 13:15 ` [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-02 14:11 ` Gerd Hoffmann
2021-11-01 13:15 ` [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib " Min Xu
` (25 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
VmgExitLib performs the necessary processing to handle a #VC exception.
VmgExitLibNull is a NULL instance of VmgExitLib which provides a
default limited interface. In this commit VmgExitLibNull is extended to
handle a #VE exception with a default limited interface. A full feature
version of #VE handler will be created later.
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
UefiCpuPkg/Include/Library/VmgExitLib.h | 27 +++++++++++++
.../Library/VmgExitLibNull/VmTdExitNull.c | 38 +++++++++++++++++++
.../Library/VmgExitLibNull/VmgExitLibNull.inf | 1 +
3 files changed, 66 insertions(+)
create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmTdExitNull.c
diff --git a/UefiCpuPkg/Include/Library/VmgExitLib.h b/UefiCpuPkg/Include/Library/VmgExitLib.h
index 061948cf840d..8664149c3f57 100644
--- a/UefiCpuPkg/Include/Library/VmgExitLib.h
+++ b/UefiCpuPkg/Include/Library/VmgExitLib.h
@@ -15,6 +15,7 @@
#include <Protocol/DebugSupport.h>
#include <Register/Amd/Ghcb.h>
+#define VE_EXCEPTION 20
/**
Perform VMGEXIT.
@@ -143,4 +144,30 @@ VmgExitHandleVc (
IN OUT EFI_SYSTEM_CONTEXT SystemContext
);
+/**
+ Handle a #VE exception.
+
+ Performs the necessary processing to handle a #VE exception.
+
+ The base library function returns an error equal to VE_EXCEPTION,
+ to be propagated to the standard exception handling stack.
+
+ @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
+ as value to use on error.
+ @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
+
+ @retval EFI_SUCCESS Exception handled
+ @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to
+ propagate provided
+ @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to
+ propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+VmTdExitHandleVe (
+ IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ );
+
#endif
diff --git a/UefiCpuPkg/Library/VmgExitLibNull/VmTdExitNull.c b/UefiCpuPkg/Library/VmgExitLibNull/VmTdExitNull.c
new file mode 100644
index 000000000000..6a4e8087cb89
--- /dev/null
+++ b/UefiCpuPkg/Library/VmgExitLibNull/VmTdExitNull.c
@@ -0,0 +1,38 @@
+/** @file
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Base.h>
+#include <Uefi.h>
+#include <Library/VmgExitLib.h>
+
+/**
+ Handle a #VE exception.
+
+ Performs the necessary processing to handle a #VE exception.
+
+ @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
+ as value to use on error.
+ @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
+
+ @retval EFI_SUCCESS Exception handled
+ @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to
+ propagate provided
+ @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to
+ propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+VmTdExitHandleVe (
+ IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ *ExceptionType = VE_EXCEPTION;
+
+ return EFI_UNSUPPORTED;
+}
diff --git a/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
index d8770a21c355..4aab601939ff 100644
--- a/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+++ b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
@@ -17,6 +17,7 @@
[Sources.common]
VmgExitLibNull.c
+ VmTdExitNull.c
[Packages]
MdePkg/MdePkg.dec
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 03/29] UefiCpuPkg: Extend VmgExitLibNull to handle #VE exception
2021-11-01 13:15 ` [PATCH V3 03/29] UefiCpuPkg: Extend VmgExitLibNull to handle #VE exception Min Xu
@ 2021-11-02 14:11 ` Gerd Hoffmann
0 siblings, 0 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:11 UTC (permalink / raw)
To: Min Xu
Cc: devel, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar
On Mon, Nov 01, 2021 at 09:15:52PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> VmgExitLib performs the necessary processing to handle a #VC exception.
> VmgExitLibNull is a NULL instance of VmgExitLib which provides a
> default limited interface. In this commit VmgExitLibNull is extended to
> handle a #VE exception with a default limited interface. A full feature
> version of #VE handler will be created later.
>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Cc: Erdem Aktas <erdemaktas@google.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Rahul Kumar <rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib to handle #VE exception
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (2 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 03/29] UefiCpuPkg: Extend VmgExitLibNull to handle #VE exception Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-02 14:23 ` Gerd Hoffmann
2021-11-17 0:32 ` Erdem Aktas
2021-11-01 13:15 ` [PATCH V3 05/29] UefiCpuPkg/CpuExceptionHandler: Add base support for the " Min Xu
` (24 subsequent siblings)
28 siblings, 2 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jiewen Yao, Jordan Justen, Brijesh Singh,
Erdem Aktas, James Bottomley, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
The base VmgExitLib library provides a default limited interface to
handle #VE exception. To provide full support, the OVMF version of
VmgExitLib is extended to provide full support of #VE handler.
PcdIgnoreVeHalt is created in OvmfPkg.dec to ignore the VE halt.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf | 3 +-
.../Library/VmgExitLib/VmTdExitVeHandler.c | 515 ++++++++++++++++++
OvmfPkg/Library/VmgExitLib/VmgExitLib.inf | 4 +
OvmfPkg/OvmfPkg.dec | 3 +
4 files changed, 524 insertions(+), 1 deletion(-)
create mode 100644 OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c
diff --git a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
index e6f6ea7972fd..20d381387bf9 100644
--- a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
@@ -25,6 +25,7 @@
VmgExitVcHandler.c
VmgExitVcHandler.h
SecVmgExitVcHandler.c
+ VmTdExitVeHandler.c
[Packages]
MdePkg/MdePkg.dec
@@ -42,4 +43,4 @@
[FixedPcd]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
-
+ gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt
diff --git a/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c b/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c
new file mode 100644
index 000000000000..fe157208154a
--- /dev/null
+++ b/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c
@@ -0,0 +1,515 @@
+/** @file
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TdxLib.h>
+#include <Library/VmgExitLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <IndustryStandard/InstructionParsing.h>
+
+typedef union {
+ struct {
+ UINT32 Eax;
+ UINT32 Edx;
+ } Regs;
+ UINT64 Val;
+} MSR_DATA;
+
+typedef union {
+ UINT8 Val;
+ struct {
+ UINT8 B:1;
+ UINT8 X:1;
+ UINT8 R:1;
+ UINT8 W:1;
+ } Bits;
+} REX;
+
+typedef union {
+ UINT8 Val;
+ struct {
+ UINT8 Rm:3;
+ UINT8 Reg:3;
+ UINT8 Mod:2;
+ } Bits;
+} MODRM;
+
+typedef struct {
+ UINT64 Regs[4];
+} CPUID_DATA;
+
+/**
+ Handle an CPUID event.
+
+ Use the TDVMCALL instruction to handle cpuid #ve
+
+ @param[in, out] Regs x64 processor context
+ @param[in] Veinfo VE Info
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+**/
+STATIC
+UINT64
+EFIAPI
+CpuIdExit (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN TDCALL_VEINFO_RETURN_DATA *Veinfo
+ )
+{
+ CPUID_DATA CpuIdData;
+ UINT64 Status;
+
+ Status = TdVmCallCpuid (Regs->Rax, Regs->Rcx, &CpuIdData);
+
+ if (Status == 0) {
+ Regs->Rax = CpuIdData.Regs[0];
+ Regs->Rbx = CpuIdData.Regs[1];
+ Regs->Rcx = CpuIdData.Regs[2];
+ Regs->Rdx = CpuIdData.Regs[3];
+ }
+
+ return Status;
+}
+
+/**
+ Handle an IO event.
+
+ Use the TDVMCALL instruction to handle either an IO read or an IO write.
+
+ @param[in, out] Regs x64 processor context
+ @param[in] Veinfo VE Info
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+**/
+STATIC
+UINT64
+EFIAPI
+IoExit (
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN TDCALL_VEINFO_RETURN_DATA *Veinfo
+ )
+{
+ BOOLEAN Write;
+ UINTN Size;
+ UINTN Port;
+ UINT64 Val;
+ UINT64 RepCnt;
+ UINT64 Status;
+
+ Val = 0;
+ Write = Veinfo->ExitQualification.Io.Direction ? FALSE : TRUE;
+ Size = Veinfo->ExitQualification.Io.Size + 1;
+ Port = Veinfo->ExitQualification.Io.Port;
+
+ if (Veinfo->ExitQualification.Io.String) {
+ //
+ // If REP is set, get rep-cnt from Rcx
+ //
+ RepCnt = Veinfo->ExitQualification.Io.Rep ? Regs->Rcx : 1;
+
+ while (RepCnt) {
+ Val = 0;
+ if (Write == TRUE) {
+ CopyMem (&Val, (VOID *) Regs->Rsi, Size);
+ Regs->Rsi += Size;
+ }
+
+ Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));
+ if (Status != 0) {
+ break;
+ }
+ if (Write == FALSE) {
+ CopyMem ((VOID *) Regs->Rdi, &Val, Size);
+ Regs->Rdi += Size;
+ }
+
+ if (Veinfo->ExitQualification.Io.Rep) {
+ Regs->Rcx -= 1;
+ }
+ RepCnt -= 1;
+ }
+ } else {
+ if (Write == TRUE) {
+ CopyMem (&Val, (VOID *) &Regs->Rax, Size);
+ }
+ Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));
+ if ((Status == 0) && (Write == FALSE)) {
+ CopyMem ((VOID *) &Regs->Rax, &Val, Size);
+ }
+ }
+ return Status;
+}
+
+/**
+ Handle an READ MSR event.
+
+ Use the TDVMCALL instruction to handle msr read
+
+ @param[in, out] Regs x64 processor context
+ @param[in] Veinfo VE Info
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+**/
+STATIC
+UINT64
+ReadMsrExit (
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN TDCALL_VEINFO_RETURN_DATA *Veinfo
+ )
+{
+ MSR_DATA Data;
+ UINT64 Status;
+
+ Status = TdVmCall (EXIT_REASON_MSR_READ, Regs->Rcx, 0, 0, 0, &Data);
+ if (Status == 0) {
+ Regs->Rax = Data.Regs.Eax;
+ Regs->Rdx = Data.Regs.Edx;
+ }
+
+ return Status;
+}
+
+/**
+ Handle an WRITE MSR event.
+
+ Use the TDVMCALL instruction to handle msr write
+
+ @param[in, out] Regs x64 processor context
+ @param[in] Veinfo VE Info
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+**/
+STATIC
+UINT64
+WriteMsrExit (
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN TDCALL_VEINFO_RETURN_DATA *Veinfo
+ )
+{
+ UINT64 Status;
+ MSR_DATA Data;
+
+ Data.Regs.Eax = (UINT32) Regs->Rax;
+ Data.Regs.Edx = (UINT32) Regs->Rdx;
+
+ Status = TdVmCall (EXIT_REASON_MSR_WRITE, Regs->Rcx, Data.Val, 0, 0, NULL);
+
+ return Status;
+}
+
+STATIC
+VOID
+EFIAPI
+TdxDecodeInstruction (
+ IN UINT8 *Rip
+)
+{
+ UINTN i;
+ DEBUG ((DEBUG_INFO,"TDX: #TD[EPT] instruction (%p):", Rip));
+ for (i = 0; i < 15; i++) {
+ DEBUG ((DEBUG_INFO, "%02x:", Rip[i]));
+ }
+ DEBUG ((DEBUG_INFO, "\n"));
+}
+
+#define TDX_DECODER_BUG_ON(x) \
+ if ((x)) { \
+ TdxDecodeInstruction(Rip); \
+ TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \
+ }
+
+STATIC
+UINT64 *
+EFIAPI
+GetRegFromContext (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN UINTN RegIndex
+)
+{
+ switch (RegIndex) {
+ case 0: return &Regs->Rax; break;
+ case 1: return &Regs->Rcx; break;
+ case 2: return &Regs->Rdx; break;
+ case 3: return &Regs->Rbx; break;
+ case 4: return &Regs->Rsp; break;
+ case 5: return &Regs->Rbp; break;
+ case 6: return &Regs->Rsi; break;
+ case 7: return &Regs->Rdi; break;
+ case 8: return &Regs->R8; break;
+ case 9: return &Regs->R9; break;
+ case 10: return &Regs->R10; break;
+ case 11: return &Regs->R11; break;
+ case 12: return &Regs->R12; break;
+ case 13: return &Regs->R13; break;
+ case 14: return &Regs->R14; break;
+ case 15: return &Regs->R15; break;
+ }
+ return NULL;
+}
+
+/**
+ Handle an MMIO event.
+
+ Use the TDVMCALL instruction to handle either an mmio read or an mmio write.
+
+ @param[in, out] Regs x64 processor context
+ @param[in] Veinfo VE Info
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+**/
+STATIC
+INTN
+EFIAPI
+MmioExit (
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN TDCALL_VEINFO_RETURN_DATA *Veinfo
+ )
+{
+ UINT64 Status;
+ UINT32 MmioSize;
+ UINT32 RegSize;;
+ UINT8 OpCode;
+ BOOLEAN SeenRex;
+ UINT64 *Reg;
+ UINT8 *Rip;
+ UINT64 Val;
+ UINT32 OpSize;
+ MODRM ModRm;
+ REX Rex;
+
+ Rip = (UINT8 *) Regs->Rip;
+ Val = 0;
+ Rex.Val = 0;
+ SeenRex = FALSE;
+
+ //
+ // Default to 32bit transfer
+ //
+ OpSize = 4;
+
+ do {
+ OpCode = *Rip++;
+ if (OpCode == 0x66) {
+ OpSize = 2;
+ } else if (OpCode == 0x64 || OpCode == 0x65 || OpCode == 0x67) {
+ continue;
+ } else if (OpCode >= 0x40 && OpCode <= 0x4f) {
+ SeenRex = TRUE;
+ Rex.Val = OpCode;
+ } else {
+ break;
+ }
+ } while (TRUE);
+
+ //
+ // We need to have at least 2 more bytes for this instruction
+ //
+ TDX_DECODER_BUG_ON(((UINT64)Rip - Regs->Rip) > 13);
+
+ OpCode = *Rip++;
+ //
+ // Two-byte opecode, get next byte
+ //
+ if (OpCode == 0x0F) {
+ OpCode = *Rip++;
+ }
+
+ switch (OpCode) {
+ case 0x88:
+ case 0x8A:
+ case 0xB6:
+ MmioSize = 1;
+ break;
+ case 0xB7:
+ MmioSize = 2;
+ break;
+ default:
+ MmioSize = Rex.Bits.W ? 8 : OpSize;
+ break;
+ }
+
+ /* Punt on AH/BH/CH/DH unless it shows up. */
+ ModRm.Val = *Rip++;
+ TDX_DECODER_BUG_ON(MmioSize == 1 && ModRm.Bits.Reg > 4 && !SeenRex && OpCode != 0xB6);
+ Reg = GetRegFromContext (Regs, ModRm.Bits.Reg | ((int)Rex.Bits.R << 3));
+ TDX_DECODER_BUG_ON(!Reg);
+
+ if (ModRm.Bits.Rm == 4)
+ ++Rip; /* SIB byte */
+
+ if (ModRm.Bits.Mod == 2 || (ModRm.Bits.Mod == 0 && ModRm.Bits.Rm == 5))
+ Rip += 4; /* DISP32 */
+ else if (ModRm.Bits.Mod == 1)
+ ++Rip; /* DISP8 */
+
+ switch (OpCode) {
+ case 0x88:
+ case 0x89:
+ CopyMem ((void *)&Val, Reg, MmioSize);
+ Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
+ break;
+ case 0xC7:
+ CopyMem ((void *)&Val, Rip, OpSize);
+ Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
+ Rip += OpSize;
+ default:
+ //
+ // 32-bit write registers are zero extended to the full register
+ // Hence 'MOVZX r[32/64], r/m16' is
+ // hardcoded to reg size 8, and the straight MOV case has a reg
+ // size of 8 in the 32-bit read case.
+ //
+ switch (OpCode) {
+ case 0xB6:
+ RegSize = Rex.Bits.W ? 8 : OpSize;
+ break;
+ case 0xB7:
+ RegSize = 8;
+ break;
+ default:
+ RegSize = MmioSize == 4 ? 8 : MmioSize;
+ break;
+ }
+
+ Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 0, Veinfo->GuestPA, 0, &Val);
+ if (Status == 0) {
+ ZeroMem (Reg, RegSize);
+ CopyMem (Reg, (void *)&Val, MmioSize);
+ }
+ }
+
+ if (Status == 0) {
+ TDX_DECODER_BUG_ON(((UINT64)Rip - Regs->Rip) > 15);
+
+ //
+ // We change instruction length to reflect true size so handler can
+ // bump rip
+ //
+ Veinfo->ExitInstructionLength = (UINT32)((UINT64)Rip - Regs->Rip);
+ }
+
+ return Status;
+}
+
+/**
+ Handle a #VE exception.
+
+ Performs the necessary processing to handle a #VE exception.
+
+ @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
+ as value to use on error.
+ @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
+
+ @retval EFI_SUCCESS Exception handled
+ @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to
+ propagate provided
+ @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to
+ propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+VmTdExitHandleVe (
+ IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ UINT64 Status;
+ TD_RETURN_DATA ReturnData;
+ EFI_SYSTEM_CONTEXT_X64 *Regs;
+
+ Regs = SystemContext.SystemContextX64;
+ Status = TdCall (TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData);
+ ASSERT (Status == 0);
+ if (Status != 0) {
+ DEBUG ((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status));
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+
+ switch (ReturnData.VeInfo.ExitReason) {
+ case EXIT_REASON_CPUID:
+ Status = CpuIdExit (Regs, &ReturnData.VeInfo);
+ DEBUG ((DEBUG_VERBOSE ,
+ "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
+ ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val
+ ));
+ break;
+
+ case EXIT_REASON_HLT:
+ if (FixedPcdGetBool (PcdIgnoreVeHalt) == FALSE) {
+ Status = TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0);
+ }
+ break;
+
+ case EXIT_REASON_IO_INSTRUCTION:
+ Status = IoExit (Regs, &ReturnData.VeInfo);
+ DEBUG ((DEBUG_VERBOSE ,
+ "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
+ ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val
+ ));
+ break;
+
+ case EXIT_REASON_MSR_READ:
+ Status = ReadMsrExit (Regs, &ReturnData.VeInfo);
+ DEBUG ((DEBUG_VERBOSE ,
+ "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
+ ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val, Regs->Rcx, Status
+ ));
+ break;
+
+ case EXIT_REASON_MSR_WRITE:
+ Status = WriteMsrExit (Regs, &ReturnData.VeInfo);
+ DEBUG ((DEBUG_VERBOSE ,
+ "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
+ ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val, Regs->Rcx, Status
+ ));
+ break;
+
+ case EXIT_REASON_EPT_VIOLATION:
+ Status = MmioExit (Regs, &ReturnData.VeInfo);
+ DEBUG ((DEBUG_VERBOSE ,
+ "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
+ ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val
+ ));
+ break;
+
+ case EXIT_REASON_VMCALL:
+ case EXIT_REASON_MWAIT_INSTRUCTION:
+ case EXIT_REASON_MONITOR_INSTRUCTION:
+ case EXIT_REASON_WBINVD:
+ case EXIT_REASON_RDPMC:
+ /* Handle as nops. */
+ break;
+
+ default:
+ DEBUG ((DEBUG_ERROR,
+ "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n",
+ ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val
+ ));
+
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+ if (Status) {
+ DEBUG ((DEBUG_ERROR,
+ "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n",
+ Status, ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val
+ ));
+
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength;
+ return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
index c66c68726cdb..e3da12af2f7a 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
@@ -25,6 +25,7 @@
VmgExitVcHandler.c
VmgExitVcHandler.h
PeiDxeVmgExitVcHandler.c
+ VmTdExitVeHandler.c
[Packages]
MdePkg/MdePkg.dec
@@ -37,4 +38,7 @@
DebugLib
LocalApicLib
MemEncryptSevLib
+ TdxLib
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 340d83f794d0..2124bd639399 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -350,6 +350,9 @@
gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset|0|UINT32|0x56
gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize|0|UINT32|0x57
+ ## Ignore the VE halt in Tdx
+ gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x58
+
[PcdsDynamic, PcdsDynamicEx]
gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib to handle #VE exception
2021-11-01 13:15 ` [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib " Min Xu
@ 2021-11-02 14:23 ` Gerd Hoffmann
2021-11-10 6:46 ` Min Xu
2021-11-17 0:32 ` Erdem Aktas
1 sibling, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:23 UTC (permalink / raw)
To: Min Xu
Cc: devel, Ard Biesheuvel, Jiewen Yao, Jordan Justen, Brijesh Singh,
Erdem Aktas, James Bottomley, Tom Lendacky
Hi,
> +MmioExit (
> + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
> + IN TDCALL_VEINFO_RETURN_DATA *Veinfo
> + )
> +{
> + do {
> + OpCode = *Rip++;
> + if (OpCode == 0x66) {
> + OpSize = 2;
Oh, wow. So the VE exit data doesn't provide the information which
io/mmio access actually triggered the exception, so you have to go
decode the instruction which trapped?
> + switch (ReturnData.VeInfo.ExitReason) {
> + case EXIT_REASON_CPUID:
> + Status = CpuIdExit (Regs, &ReturnData.VeInfo);
Indention is strange (switch + case should be the same).
Otherwise looks sane to me.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib to handle #VE exception
2021-11-02 14:23 ` Gerd Hoffmann
@ 2021-11-10 6:46 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-10 6:46 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: devel@edk2.groups.io, Ard Biesheuvel, Yao, Jiewen,
Justen, Jordan L, Brijesh Singh, Erdem Aktas, James Bottomley,
Tom Lendacky
Hi, Gerd
> > +MmioExit (
> > + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
> > + IN TDCALL_VEINFO_RETURN_DATA *Veinfo
> > + )
> > +{
>
> > + do {
> > + OpCode = *Rip++;
> > + if (OpCode == 0x66) {
> > + OpSize = 2;
>
> Oh, wow. So the VE exit data doesn't provide the information which io/mmio
> access actually triggered the exception, so you have to go decode the
> instruction which trapped?
The VE exit data provides the information of ExitReason (ReturnData.VeInfo.ExitReason). Based upon the ExitReason, #Ve handler knows about which operation triggers this #Ve (EPT_VIOLATION maps to Mmio).
SystemContext.SystemContextX64 and ReturnData.VeInfo are then passed to MmioExit as the input parameters. They have the information with which MmioExit can emulates the MMIO instruction and then converts them to a controlled TdVmCall(TDVMCALL_MMIO).
Actually this way of handling is similar to AMD SEV. SEV has a common function (InitInstructionData) to initialize a data structure (InstructionData) with Regs->Rip. In its MmioExit the instruction are decoded as well.
>
> > + switch (ReturnData.VeInfo.ExitReason) {
> > + case EXIT_REASON_CPUID:
> > + Status = CpuIdExit (Regs, &ReturnData.VeInfo);
>
> Indention is strange (switch + case should be the same).
> Otherwise looks sane to me.
Sure, this will be fixed in the next version.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib to handle #VE exception
2021-11-01 13:15 ` [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib " Min Xu
2021-11-02 14:23 ` Gerd Hoffmann
@ 2021-11-17 0:32 ` Erdem Aktas
1 sibling, 0 replies; 107+ messages in thread
From: Erdem Aktas @ 2021-11-17 0:32 UTC (permalink / raw)
To: Min Xu
Cc: devel, Ard Biesheuvel, Jiewen Yao, Jordan Justen, Brijesh Singh,
James Bottomley, Tom Lendacky, Gerd Hoffmann
[-- Attachment #1: Type: text/plain, Size: 1357 bytes --]
Hi Min,
On Mon, Nov 1, 2021 at 6:16 AM Min Xu <min.m.xu@intel.com> wrote:
>
> +VmTdExitHandleVe (
> + IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
> + IN OUT EFI_SYSTEM_CONTEXT SystemContext
> + )
> +{
> + UINT64 Status;
> + TD_RETURN_DATA ReturnData;
> + EFI_SYSTEM_CONTEXT_X64 *Regs;
> +
> + Regs = SystemContext.SystemContextX64;
> + Status = TdCall (TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData);
> + ASSERT (Status == 0);
> + if (Status != 0) {
> + DEBUG ((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status =
> 0x%llx\n", Status));
> + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
> + }
>
What is the difference between TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0) vs
TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
>From the VMM stand point both seems the same?
+
> + switch (ReturnData.VeInfo.ExitReason) {
> + case EXIT_REASON_CPUID:
> + Status = CpuIdExit (Regs, &ReturnData.VeInfo);
> + DEBUG ((DEBUG_VERBOSE ,
> + "CPUID #VE happened, ExitReasion is %d, ExitQualification =
> 0x%x.\n",
> + ReturnData.VeInfo.ExitReason,
> ReturnData.VeInfo.ExitQualification.Val
> + ));
> + break;
> +
> + case EXIT_REASON_HLT:
> + if (FixedPcdGetBool (PcdIgnoreVeHalt) == FALSE) {
> + Status = TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0);
> + }
> + break;
>
>
[-- Attachment #2: Type: text/html, Size: 2120 bytes --]
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 05/29] UefiCpuPkg/CpuExceptionHandler: Add base support for the #VE exception
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (3 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib " Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-02 14:24 ` Gerd Hoffmann
2021-11-01 13:15 ` [PATCH V3 06/29] MdePkg: Add helper functions for Tdx guest in BaseIoLibIntrinsic Min Xu
` (23 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Add base support to handle #VE exceptions. Update the common exception
handlers to invoke the VmTdExitHandleVe () function of the VmgExitLib
library when a #VE is encountered. A non-zero return code will propagate
to the targeted exception handler.
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
.../PeiDxeSmmCpuException.c | 17 +++++++++++++++++
.../SecPeiCpuException.c | 18 ++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
index 892d349d4b37..7dd30b43ed13 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
@@ -45,6 +45,23 @@ CommonExceptionHandlerWorker (
}
}
+ if (ExceptionType == VE_EXCEPTION) {
+ EFI_STATUS Status;
+ //
+ // #VE needs to be handled immediately upon enabling exception handling
+ // and therefore can't use the RegisterCpuInterruptHandler() interface.
+ //
+ // Handle the #VE:
+ // On EFI_SUCCESS - Exception has been handled, return
+ // On other - ExceptionType contains (possibly new) exception
+ // value
+ //
+ Status = VmTdExitHandleVe (&ExceptionType, SystemContext);
+ if (!EFI_ERROR (Status)) {
+ return;
+ }
+ }
+
ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);
ReservedVectors = ExceptionHandlerData->ReservedVectors;
ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c
index 01b5a2f1f4fc..0ce26908834e 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c
@@ -43,6 +43,24 @@ CommonExceptionHandler (
}
}
+ if (ExceptionType == VE_EXCEPTION) {
+ EFI_STATUS Status;
+ //
+ // #VE needs to be handled immediately upon enabling exception handling
+ // and therefore can't use the RegisterCpuInterruptHandler() interface
+ // (which isn't supported under Sec and Pei anyway).
+ //
+ // Handle the #VE:
+ // On EFI_SUCCESS - Exception has been handled, return
+ // On other - ExceptionType contains (possibly new) exception
+ // value
+ //
+ Status = VmTdExitHandleVe (&ExceptionType, SystemContext);
+ if (!EFI_ERROR (Status)) {
+ return;
+ }
+ }
+
//
// Initialize the serial port before dumping.
//
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 05/29] UefiCpuPkg/CpuExceptionHandler: Add base support for the #VE exception
2021-11-01 13:15 ` [PATCH V3 05/29] UefiCpuPkg/CpuExceptionHandler: Add base support for the " Min Xu
@ 2021-11-02 14:24 ` Gerd Hoffmann
0 siblings, 0 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:24 UTC (permalink / raw)
To: Min Xu
Cc: devel, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar
On Mon, Nov 01, 2021 at 09:15:54PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> Add base support to handle #VE exceptions. Update the common exception
> handlers to invoke the VmTdExitHandleVe () function of the VmgExitLib
> library when a #VE is encountered. A non-zero return code will propagate
> to the targeted exception handler.
>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Cc: Erdem Aktas <erdemaktas@google.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Rahul Kumar <rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 06/29] MdePkg: Add helper functions for Tdx guest in BaseIoLibIntrinsic
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (4 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 05/29] UefiCpuPkg/CpuExceptionHandler: Add base support for the " Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-01 13:15 ` [PATCH V3 07/29] MdePkg: Support mmio " Min Xu
` (22 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Michael D Kinney, Liming Gao, Zhiguang Liu, Brijesh Singh,
Erdem Aktas, James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Intel TDX architecture does not prescribe a specific software convention
to perform I/O from the guest TD. Guest TD providers have many choices to
provide I/O to the guest. The common I/O models are emulated devices,
para-virtualized devices, SRIOV devices and Direct Device assignments.
TDVF chooses para-virtualized I/O (Choice-A) which use the TDG.VP.VMCALL
function to invoke the funtions provided by the host VMM to perform I/O.
Another choice (Choice-B) is the emulation performed by the #VE handler.
There are 2 benefits of para-virtualized I/O:
1. Performance.
VMEXIT/VMENTRY is skipped so that the performance is better than #VE
handler.
2. De-couple with #VE handler.
Choice-B depends on the #VE handler which means I/O is not available
until #VE handler is installed. For example, in PEI phase #VE handler
is installed in CpuMpPei, while communication with Qemu (via I/O port)
happen earlier than it.
IoLibInternalTdx.c provides the helper functions for Tdx guest.
IoLibInternalTdxNull.c provides the null version of the helper functions.
It is included in the Non-X64 IoLib so that the build will not be broken.
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
.../BaseIoLibIntrinsic/IoLibInternalTdx.c | 704 ++++++++++++++++++
.../BaseIoLibIntrinsic/IoLibInternalTdxNull.c | 499 +++++++++++++
MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h | 411 ++++++++++
3 files changed, 1614 insertions(+)
create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c
create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c
create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c
new file mode 100644
index 000000000000..d93f59f9c952
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c
@@ -0,0 +1,704 @@
+/** @file
+ TDX I/O Library routines.
+
+ Copyright (c) 2020-2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include "BaseIoLibIntrinsicInternal.h"
+#include <Include/IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
+#include <Library/BaseLib.h>
+#include <Register/Intel/Cpuid.h>
+#include <Base.h>
+#include "IoLibTdx.h"
+
+// Size of TDVMCALL Access, including IO and MMIO
+#define TDVMCALL_ACCESS_SIZE_1 1
+#define TDVMCALL_ACCESS_SIZE_2 2
+#define TDVMCALL_ACCESS_SIZE_4 4
+#define TDVMCALL_ACCESS_SIZE_8 8
+
+// Direction of TDVMCALL Access, including IO and MMIO
+#define TDVMCALL_ACCESS_READ 0
+#define TDVMCALL_ACCESS_WRITE 1
+
+BOOLEAN mTdxEnabled = FALSE;
+BOOLEAN mTdxProbed = FALSE;
+
+/**
+ Check if it is Tdx guest.
+
+ @return TRUE It is Tdx guest
+ @return FALSE It is not Tdx guest
+
+**/
+BOOLEAN
+EFIAPI
+IsTdxGuest (
+ VOID
+ )
+{
+ UINT32 Eax;
+ UINT32 Ebx;
+ UINT32 Ecx;
+ UINT32 Edx;
+ UINT32 LargestEax;
+
+ if (mTdxProbed) {
+ return mTdxEnabled;
+ }
+
+ mTdxEnabled = FALSE;
+
+ do {
+ AsmCpuid (CPUID_SIGNATURE, &LargestEax, &Ebx, &Ecx, &Edx);
+
+ if (Ebx != CPUID_SIGNATURE_GENUINE_INTEL_EBX
+ || Edx != CPUID_SIGNATURE_GENUINE_INTEL_EDX
+ || Ecx != CPUID_SIGNATURE_GENUINE_INTEL_ECX) {
+ break;
+ }
+
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &Ecx, NULL);
+ if ((Ecx & BIT31) == 0) {
+ break;
+ }
+
+ if (LargestEax < 0x21) {
+ break;
+ }
+
+ AsmCpuidEx (0x21, 0, &Eax, &Ebx, &Ecx, &Edx);
+ if (Ebx != SIGNATURE_32 ('I', 'n', 't', 'e')
+ || Edx != SIGNATURE_32 ('l', 'T', 'D', 'X')
+ || Ecx != SIGNATURE_32 (' ', ' ', ' ', ' ')) {
+ break;
+ }
+
+ mTdxEnabled = TRUE;
+ }while (FALSE);
+
+ mTdxProbed = TRUE;
+
+ return mTdxEnabled;
+}
+
+
+/**
+ Reads an 8-bit I/O port.
+
+ TDVMCALL_IO is invoked to read I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdIoRead8 (
+ IN UINTN Port
+ )
+{
+ UINT64 Status;
+ UINT64 Val;
+
+ Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Port, 0, &Val);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ return (UINT8)Val;
+}
+
+/**
+ Reads a 16-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdIoRead16 (
+ IN UINTN Port
+ )
+{
+ UINT64 Status;
+ UINT64 Val;
+
+ ASSERT ((Port & 1) == 0);
+
+ Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_READ, Port, 0, &Val);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ return (UINT16)Val;
+}
+
+/**
+ Reads a 32-bit I/O port.
+
+ TDVMCALL_IO is invoked to read I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdIoRead32 (
+ IN UINTN Port
+ )
+{
+ UINT64 Status;
+ UINT64 Val;
+
+ ASSERT ((Port & 3) == 0);
+
+ Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_READ, Port, 0, &Val);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ return (UINT32)Val;
+}
+
+/**
+ Writes an 8-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT8
+EFIAPI
+TdIoWrite8 (
+ IN UINTN Port,
+ IN UINT8 Value
+ )
+{
+ UINT64 Status;
+ UINT64 Val;
+
+ Val = Value;
+ Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_WRITE, Port, Val, 0);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ return Value;
+}
+
+/**
+ Writes a 16-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT16
+EFIAPI
+TdIoWrite16 (
+ IN UINTN Port,
+ IN UINT16 Value
+ )
+{
+ UINT64 Status;
+ UINT64 Val;
+
+ ASSERT ((Port & 1) == 0);
+ Val = Value;
+ Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_WRITE, Port, Val, 0);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ return Value;
+}
+
+/**
+ Writes a 32-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT32
+EFIAPI
+TdIoWrite32 (
+ IN UINTN Port,
+ IN UINT32 Value
+ )
+{
+ UINT64 Status;
+ UINT64 Val;
+
+ ASSERT ((Port & 3) == 0);
+ Val = Value;
+ Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_WRITE, Port, Val, 0);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ return Value;
+}
+
+/**
+ Reads an 8-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdMmioRead8 (
+ IN UINTN Address
+ )
+{
+ UINT64 Value;
+ UINT64 Status;
+
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Address | TdSharedPageMask (), 0, &Value);
+ if (Status != 0) {
+ Value = *(volatile UINT64*)Address;
+ }
+
+ return (UINT8)Value;
+}
+
+/**
+ Writes an 8-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read write registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT8
+EFIAPI
+TdMmioWrite8 (
+ IN UINTN Address,
+ IN UINT8 Value
+ )
+{
+ UINT64 Val;
+ UINT64 Status;
+
+ Val = Value;
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_WRITE, Address | TdSharedPageMask (), Val, 0);
+ if (Status != 0) {
+ *(volatile UINT8*)Address = Value;
+ }
+
+ return Value;
+}
+
+/**
+ Reads a 16-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdMmioRead16 (
+ IN UINTN Address
+ )
+{
+ UINT64 Value;
+ UINT64 Status;
+
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_READ, Address | TdSharedPageMask (), 0, &Value);
+ if (Status != 0) {
+ Value = *(volatile UINT64*)Address;
+ }
+
+ return (UINT16)Value;
+}
+
+/**
+ Writes a 16-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT16
+EFIAPI
+TdMmioWrite16 (
+ IN UINTN Address,
+ IN UINT16 Value
+ )
+{
+ UINT64 Val;
+ UINT64 Status;
+
+ ASSERT ((Address & 1) == 0);
+
+ Val = Value;
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_WRITE, Address | TdSharedPageMask (), Val, 0);
+ if (Status != 0) {
+ *(volatile UINT16*)Address = Value;
+ }
+
+ return Value;
+}
+
+/**
+ Reads a 32-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdMmioRead32 (
+ IN UINTN Address
+ )
+{
+ UINT64 Value;
+ UINT64 Status;
+
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_READ, Address | TdSharedPageMask (), 0, &Value);
+ if (Status != 0) {
+ Value = *(volatile UINT64*)Address;
+ }
+
+ return (UINT32)Value;
+}
+
+/**
+ Writes a 32-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT32
+EFIAPI
+TdMmioWrite32 (
+ IN UINTN Address,
+ IN UINT32 Value
+ )
+{
+ UINT64 Val;
+ UINT64 Status;
+
+ ASSERT ((Address & 3) == 0);
+
+ Val = Value;
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_WRITE, Address | TdSharedPageMask (), Val, 0);
+ if (Status != 0) {
+ *(volatile UINT32*)Address = Value;
+ }
+
+ return Value;
+}
+
+/**
+ Reads a 64-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT64
+EFIAPI
+TdMmioRead64 (
+ IN UINTN Address
+ )
+{
+ UINT64 Value;
+ UINT64 Status;
+
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_READ, Address | TdSharedPageMask (), 0, &Value);
+ if (Status != 0) {
+ Value = *(volatile UINT64*)Address;
+ }
+
+ return Value;
+}
+
+/**
+ Writes a 64-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+**/
+UINT64
+EFIAPI
+TdMmioWrite64 (
+ IN UINTN Address,
+ IN UINT64 Value
+ )
+{
+ UINT64 Status;
+ UINT64 Val;
+
+ ASSERT ((Address & 7) == 0);
+
+ Val = Value;
+ Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_WRITE, Address | TdSharedPageMask (), Val, 0);
+ if (Status != 0) {
+ *(volatile UINT64*)Address = Value;
+ }
+ return Value;
+}
+
+/**
+ Reads an 8-bit I/O port fifo into a block of memory.
+
+ Reads the 8-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead8 is invoked to read the I/O port fifo.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ UINT8 *Buf8;
+ UINTN Index;
+
+ Buf8 = (UINT8 *) Buffer;
+ for (Index = 0; Index < Count; Index++) {
+ Buf8[Index] = TdIoRead8 (Port);
+ }
+}
+
+/**
+ Writes a block of memory into an 8-bit I/O port fifo.
+
+ Writes the 8-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite8 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ UINT8 *Buf8;
+ UINTN Index;
+
+ Buf8 = (UINT8 *) Buffer;
+ for (Index = 0; Index < Count; Index++) {
+ TdIoWrite8 (Port, Buf8[Index]);
+ }
+}
+
+/**
+ Reads a 16-bit I/O port fifo into a block of memory.
+
+ Reads the 16-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead16 is invoked to read data from the I/O port.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ UINT16 *Buf16;
+ UINTN Index;
+
+ Buf16 = (UINT16 *) Buffer;
+ for (Index = 0; Index < Count; Index++) {
+ Buf16[Index] = TdIoRead16 (Port);
+ }
+}
+
+/**
+ Writes a block of memory into a 16-bit I/O port fifo.
+
+ Writes the 16-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite16 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ UINT16 *Buf16;
+ UINTN Index;
+
+ Buf16 = (UINT16 *) Buffer;
+ for (Index = 0; Index < Count; Index++) {
+ TdIoWrite16 (Port, Buf16[Index]);
+ }
+}
+
+/**
+ Reads a 32-bit I/O port fifo into a block of memory.
+
+ Reads the 32-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead32 is invoked to read data from the I/O port.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ UINT32 *Buf32;
+ UINTN Index;
+
+ Buf32 = (UINT32 *) Buffer;
+ for (Index = 0; Index < Count; Index++) {
+ Buf32[Index] = TdIoRead32 (Port);
+ }
+}
+
+/**
+ Writes a block of memory into a 32-bit I/O port fifo.
+
+ Writes the 32-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite32 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ UINT32 *Buf32;
+ UINTN Index;
+
+ Buf32 = (UINT32 *) Buffer;
+ for (Index = 0; Index < Count; Index++) {
+ TdIoWrite32 (Port, Buf32[Index]);
+ }
+}
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c
new file mode 100644
index 000000000000..f518d8ffd825
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c
@@ -0,0 +1,499 @@
+/** @file
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include <Library/BaseLib.h>
+#include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
+
+/**
+ Check if it is Tdx guest.
+
+ @return TRUE It is Tdx guest
+ @return FALSE It is not Tdx guest
+
+**/
+BOOLEAN
+EFIAPI
+IsTdxGuest (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+
+/**
+ Reads an 8-bit I/O port.
+
+ TDVMCALL_IO is invoked to read I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdIoRead8 (
+ IN UINTN Port
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Reads a 16-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdIoRead16 (
+ IN UINTN Port
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Reads a 32-bit I/O port.
+
+ TDVMCALL_IO is invoked to read I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdIoRead32 (
+ IN UINTN Port
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Writes an 8-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT8
+EFIAPI
+TdIoWrite8 (
+ IN UINTN Port,
+ IN UINT8 Value
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Writes a 16-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT16
+EFIAPI
+TdIoWrite16 (
+ IN UINTN Port,
+ IN UINT16 Value
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Writes a 32-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT32
+EFIAPI
+TdIoWrite32 (
+ IN UINTN Port,
+ IN UINT32 Value
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Reads an 8-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdMmioRead8 (
+ IN UINTN Address
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Writes an 8-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read write registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT8
+EFIAPI
+TdMmioWrite8 (
+ IN UINTN Address,
+ IN UINT8 Val
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Reads a 16-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdMmioRead16 (
+ IN UINTN Address
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Writes a 16-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT16
+EFIAPI
+TdMmioWrite16 (
+ IN UINTN Address,
+ IN UINT16 Val
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Reads a 32-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdMmioRead32 (
+ IN UINTN Address
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Writes a 32-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT32
+EFIAPI
+TdMmioWrite32 (
+ IN UINTN Address,
+ IN UINT32 Val
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Reads a 64-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT64
+EFIAPI
+TdMmioRead64 (
+ IN UINTN Address
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Writes a 64-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+**/
+UINT64
+EFIAPI
+TdMmioWrite64 (
+ IN UINTN Address,
+ IN UINT64 Value
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Reads an 8-bit I/O port fifo into a block of memory.
+
+ Reads the 8-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead8 is invoked to read the I/O port fifo.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+}
+
+/**
+ Writes a block of memory into an 8-bit I/O port fifo.
+
+ Writes the 8-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite8 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+}
+
+/**
+ Reads a 16-bit I/O port fifo into a block of memory.
+
+ Reads the 16-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead16 is invoked to read data from the I/O port.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+}
+
+/**
+ Writes a block of memory into a 16-bit I/O port fifo.
+
+ Writes the 16-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite16 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+}
+
+/**
+ Reads a 32-bit I/O port fifo into a block of memory.
+
+ Reads the 32-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead32 is invoked to read data from the I/O port.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+}
+
+/**
+ Writes a block of memory into a 32-bit I/O port fifo.
+
+ Writes the 32-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite32 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ ASSERT (FALSE);
+}
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h
new file mode 100644
index 000000000000..3aad197d3b39
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h
@@ -0,0 +1,411 @@
+/** @file
+ Header file for Tdx IO library.
+
+ Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef IOLIB_TDX_H_
+#define IOLIB_TDX_H_
+
+/**
+ Check if it is Tdx guest.
+
+ @return TRUE It is Tdx guest
+ @return FALSE It is not Tdx guest
+
+**/
+BOOLEAN
+EFIAPI
+IsTdxGuest (
+ VOID
+ );
+
+
+/**
+ Reads an 8-bit I/O port.
+
+ TDVMCALL_IO is invoked to read I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdIoRead8 (
+ IN UINTN Port
+ );
+
+/**
+ Reads a 16-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdIoRead16 (
+ IN UINTN Port
+ );
+
+/**
+ Reads a 32-bit I/O port.
+
+ TDVMCALL_IO is invoked to read I/O port.
+
+ @param Port The I/O port to read.
+
+ @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdIoRead32 (
+ IN UINTN Port
+ );
+
+/**
+ Writes an 8-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT8
+EFIAPI
+TdIoWrite8 (
+ IN UINTN Port,
+ IN UINT8 Value
+ );
+
+/**
+ Writes a 16-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT16
+EFIAPI
+TdIoWrite16 (
+ IN UINTN Port,
+ IN UINT16 Value
+ );
+
+/**
+ Writes a 32-bit I/O port.
+
+ TDVMCALL_IO is invoked to write I/O port.
+
+ @param Port The I/O port to write.
+ @param Value The value to write to the I/O port.
+
+ @return The value written the I/O port.
+
+**/
+UINT32
+EFIAPI
+TdIoWrite32 (
+ IN UINTN Port,
+ IN UINT32 Value
+ );
+
+/**
+ Reads an 8-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdMmioRead8 (
+ IN UINTN Address
+ );
+
+/**
+ Writes an 8-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read write registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT8
+EFIAPI
+TdMmioWrite8 (
+ IN UINTN Address,
+ IN UINT8 Val
+ );
+
+/**
+ Reads a 16-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdMmioRead16 (
+ IN UINTN Address
+ );
+
+/**
+ Writes a 16-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT16
+EFIAPI
+TdMmioWrite16 (
+ IN UINTN Address,
+ IN UINT16 Val
+ );
+
+/**
+ Reads a 32-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdMmioRead32 (
+ IN UINTN Address
+ );
+
+/**
+ Writes a 32-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+ @return Value.
+
+**/
+UINT32
+EFIAPI
+TdMmioWrite32 (
+ IN UINTN Address,
+ IN UINT32 Val
+ );
+
+/**
+ Reads a 64-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to read MMIO registers.
+
+ @param Address The MMIO register to read.
+
+ @return The value read.
+
+**/
+UINT64
+EFIAPI
+TdMmioRead64 (
+ IN UINTN Address
+ );
+
+/**
+ Writes a 64-bit MMIO register.
+
+ TDVMCALL_MMIO is invoked to write MMIO registers.
+
+ @param Address The MMIO register to write.
+ @param Value The value to write to the MMIO register.
+
+**/
+UINT64
+EFIAPI
+TdMmioWrite64 (
+ IN UINTN Address,
+ IN UINT64 Value
+ );
+
+/**
+ Reads an 8-bit I/O port fifo into a block of memory in Tdx.
+
+ Reads the 8-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ );
+
+/**
+ Writes a block of memory into an 8-bit I/O port fifo in Tdx.
+
+ Writes the 8-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ );
+
+/**
+ Reads a 16-bit I/O port fifo into a block of memory in Tdx.
+
+ Reads the 16-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ );
+
+/**
+ Writes a block of memory into a 16-bit I/O port fifo in Tdx.
+
+ Writes the 16-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ );
+
+/**
+ Reads a 32-bit I/O port fifo into a block of memory in Tdx.
+
+ Reads the 32-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ );
+
+/**
+ Writes a block of memory into a 32-bit I/O port fifo in Tdx.
+
+ Writes the 32-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ );
+
+#endif
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 07/29] MdePkg: Support mmio for Tdx guest in BaseIoLibIntrinsic
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (5 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 06/29] MdePkg: Add helper functions for Tdx guest in BaseIoLibIntrinsic Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-01 13:15 ` [PATCH V3 08/29] MdePkg: Support IoFifo " Min Xu
` (21 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Michael D Kinney, Liming Gao, Zhiguang Liu, Brijesh Singh,
Erdem Aktas, James Bottomley, Jiewen Yao, Tom Lendacky,
Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
TDVF access MMIO with TDG.VP.VMCALL to invoke VMM provided emulation
functions. If the access to MMIO fails, it fall backs to the direct
access.
BaseIoLibIntrinsic.inf is the IoLib used by other packages. It will
not support I/O in Td guest. But some files are shared between
BaseIoLibIntrinsic and BaseIoLibIntrinsicSev (IoLib.c is the example). So
IoLibInternalTdxNull.c (which holds the null stub of the Td I/O routines)
is included in BaseIoLibIntrinsic.inf. BaseIoLibIntrinsic.inf doesn't
import TdxLib so that the Pkgs which include BaseIoLibIntrinsic.inf
need not include TdxLib.
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
.../BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf | 2 +
.../BaseIoLibIntrinsicSev.inf | 5 +-
MdePkg/Library/BaseIoLibIntrinsic/IoLib.c | 82 +++++++++++++++++--
3 files changed, 80 insertions(+), 9 deletions(-)
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
index 97eeada0656e..27b15d9ae256 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
+++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
@@ -34,6 +34,8 @@
IoLibMmioBuffer.c
BaseIoLibIntrinsicInternal.h
IoHighLevel.c
+ IoLibInternalTdxNull.c
+ IoLibTdx.h
[Sources.IA32]
IoLibGcc.c | GCC
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
index 34f9d1d1062f..525aefb0d822 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
+++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
@@ -30,17 +30,20 @@
IoLibMmioBuffer.c
BaseIoLibIntrinsicInternal.h
IoHighLevel.c
+ IoLibTdx.h
[Sources.IA32]
IoLibGcc.c | GCC
IoLibMsc.c | MSFT
IoLib.c
+ IoLibInternalTdxNull.c
Ia32/IoFifoSev.nasm
[Sources.X64]
IoLibGcc.c | GCC
IoLibMsc.c | MSFT
IoLib.c
+ IoLibInternalTdx.c
X64/IoFifoSev.nasm
[Packages]
@@ -50,4 +53,4 @@
DebugLib
BaseLib
RegisterFilterLib
-
+ TdxLib
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c
index d0d7044f0901..62381647fd1d 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c
@@ -7,6 +7,7 @@
**/
#include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
/**
Reads a 64-bit I/O port.
@@ -70,6 +71,8 @@ IoWrite64 (
If 8-bit MMIO register operations are not supported, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
@param Address The MMIO register to read.
@return The value read.
@@ -87,8 +90,15 @@ MmioRead8 (
Flag = FilterBeforeMmIoRead (FilterWidth8, Address, &Value);
if (Flag) {
MemoryFence ();
- Value = *(volatile UINT8*)Address;
+
+ if (IsTdxGuest ()) {
+ Value = TdMmioRead8 (Address);
+ } else {
+ Value = *(volatile UINT8*)Address;
+ }
+
MemoryFence ();
+
}
FilterAfterMmIoRead (FilterWidth8, Address, &Value);
@@ -104,6 +114,8 @@ MmioRead8 (
If 8-bit MMIO register operations are not supported, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
@param Address The MMIO register to write.
@param Value The value to write to the MMIO register.
@@ -122,7 +134,13 @@ MmioWrite8 (
Flag = FilterBeforeMmIoWrite (FilterWidth8, Address, &Value);
if (Flag) {
MemoryFence ();
- *(volatile UINT8*)Address = Value;
+
+ if (IsTdxGuest ()) {
+ TdMmioWrite8 (Address, Value);
+ } else {
+ *(volatile UINT8*)Address = Value;
+ }
+
MemoryFence ();
}
FilterAfterMmIoWrite (FilterWidth8, Address, &Value);
@@ -140,6 +158,8 @@ MmioWrite8 (
If 16-bit MMIO register operations are not supported, then ASSERT().
If Address is not aligned on a 16-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
@param Address The MMIO register to read.
@return The value read.
@@ -158,7 +178,13 @@ MmioRead16 (
Flag = FilterBeforeMmIoRead (FilterWidth16, Address, &Value);
if (Flag) {
MemoryFence ();
- Value = *(volatile UINT16*)Address;
+
+ if (IsTdxGuest ()) {
+ Value = TdMmioRead16 (Address);
+ } else {
+ Value = *(volatile UINT16*)Address;
+ }
+
MemoryFence ();
}
FilterAfterMmIoRead (FilterWidth16, Address, &Value);
@@ -176,6 +202,8 @@ MmioRead16 (
If 16-bit MMIO register operations are not supported, then ASSERT().
If Address is not aligned on a 16-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
@param Address The MMIO register to write.
@param Value The value to write to the MMIO register.
@@ -196,7 +224,13 @@ MmioWrite16 (
Flag = FilterBeforeMmIoWrite (FilterWidth16, Address, &Value);
if (Flag) {
MemoryFence ();
- *(volatile UINT16*)Address = Value;
+
+ if (IsTdxGuest ()) {
+ TdMmioWrite16 (Address, Value);
+ } else {
+ *(volatile UINT16*)Address = Value;
+ }
+
MemoryFence ();
}
FilterAfterMmIoWrite (FilterWidth16, Address, &Value);
@@ -214,6 +248,8 @@ MmioWrite16 (
If 32-bit MMIO register operations are not supported, then ASSERT().
If Address is not aligned on a 32-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
@param Address The MMIO register to read.
@return The value read.
@@ -233,7 +269,13 @@ MmioRead32 (
Flag = FilterBeforeMmIoRead (FilterWidth32, Address, &Value);
if (Flag) {
MemoryFence ();
- Value = *(volatile UINT32*)Address;
+
+ if (IsTdxGuest ()) {
+ Value = TdMmioRead32 (Address);
+ } else {
+ Value = *(volatile UINT32*)Address;
+ }
+
MemoryFence ();
}
FilterAfterMmIoRead (FilterWidth32, Address, &Value);
@@ -251,6 +293,8 @@ MmioRead32 (
If 32-bit MMIO register operations are not supported, then ASSERT().
If Address is not aligned on a 32-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
@param Address The MMIO register to write.
@param Value The value to write to the MMIO register.
@@ -271,7 +315,13 @@ MmioWrite32 (
Flag = FilterBeforeMmIoWrite (FilterWidth32, Address, &Value);
if (Flag) {
MemoryFence ();
- *(volatile UINT32*)Address = Value;
+
+ if (IsTdxGuest ()) {
+ TdMmioWrite32 (Address, Value);
+ } else {
+ *(volatile UINT32*)Address = Value;
+ }
+
MemoryFence ();
}
FilterAfterMmIoWrite (FilterWidth32, Address, &Value);
@@ -289,6 +339,8 @@ MmioWrite32 (
If 64-bit MMIO register operations are not supported, then ASSERT().
If Address is not aligned on a 64-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
@param Address The MMIO register to read.
@return The value read.
@@ -308,7 +360,13 @@ MmioRead64 (
Flag = FilterBeforeMmIoRead (FilterWidth64, Address, &Value);
if (Flag) {
MemoryFence ();
- Value = *(volatile UINT64*)Address;
+
+ if (IsTdxGuest ()) {
+ Value = TdMmioRead64 (Address);
+ } else {
+ Value = *(volatile UINT64*)Address;
+ }
+
MemoryFence ();
}
FilterAfterMmIoRead (FilterWidth64, Address, &Value);
@@ -326,6 +384,8 @@ MmioRead64 (
If 64-bit MMIO register operations are not supported, then ASSERT().
If Address is not aligned on a 64-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
@param Address The MMIO register to write.
@param Value The value to write to the MMIO register.
@@ -344,7 +404,13 @@ MmioWrite64 (
Flag = FilterBeforeMmIoWrite (FilterWidth64, Address, &Value);
if (Flag) {
MemoryFence ();
- *(volatile UINT64*)Address = Value;
+
+ if (IsTdxGuest ()) {
+ TdMmioWrite64 (Address, Value);
+ } else {
+ *(volatile UINT64*)Address = Value;
+ }
+
MemoryFence ();
}
FilterAfterMmIoWrite (FilterWidth64, Address, &Value);
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 08/29] MdePkg: Support IoFifo for Tdx guest in BaseIoLibIntrinsic
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (6 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 07/29] MdePkg: Support mmio " Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-01 13:15 ` [PATCH V3 09/29] MdePkg: Support IoRead/IoWrite " Min Xu
` (20 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Michael D Kinney, Liming Gao, Zhiguang Liu, Brijesh Singh,
Erdem Aktas, James Bottomley, Jiewen Yao, Tom Lendacky,
Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Previously IoFifo functions are in X64/IoFifoSev.nasm which supports
both SEV guest and Legacy guest. IoLibFifo.c is introduced to support
SEV/TDX/Legacy guest in one binary. It checks the guest type in runtime
and call corresponding functions then.
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
.../BaseIoLibIntrinsicSev.inf | 2 +
MdePkg/Library/BaseIoLibIntrinsic/IoLibFifo.c | 216 ++++++++++++++++++
MdePkg/Library/BaseIoLibIntrinsic/IoLibSev.h | 166 ++++++++++++++
.../BaseIoLibIntrinsic/X64/IoFifoSev.nasm | 34 +--
4 files changed, 401 insertions(+), 17 deletions(-)
create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibFifo.c
create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibSev.h
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
index 525aefb0d822..d75e20cc9d76 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
+++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
@@ -31,6 +31,7 @@
BaseIoLibIntrinsicInternal.h
IoHighLevel.c
IoLibTdx.h
+ IoLibSev.h
[Sources.IA32]
IoLibGcc.c | GCC
@@ -44,6 +45,7 @@
IoLibMsc.c | MSFT
IoLib.c
IoLibInternalTdx.c
+ IoLibFifo.c
X64/IoFifoSev.nasm
[Packages]
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibFifo.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibFifo.c
new file mode 100644
index 000000000000..9e243543cfe2
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibFifo.c
@@ -0,0 +1,216 @@
+/** @file
+ IoFifo read/write routines.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibSev.h"
+#include "IoLibTdx.h"
+#include <Library/TdxLib.h>
+
+/**
+ Reads an 8-bit I/O port fifo into a block of memory.
+
+ Reads the 8-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead8 is invoked to read the I/O port fifo.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+IoReadFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ if (IsTdxGuest ()) {
+ TdIoReadFifo8 (Port, Count, Buffer);
+ } else {
+ SevIoReadFifo8 (Port, Count, Buffer);
+ }
+}
+
+/**
+ Writes a block of memory into an 8-bit I/O port fifo.
+
+ Writes the 8-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite8 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+IoWriteFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ if (IsTdxGuest ()) {
+ TdIoWriteFifo8 (Port, Count, Buffer);
+ } else {
+ SevIoWriteFifo8 (Port, Count, Buffer);
+ }
+}
+
+/**
+ Reads a 16-bit I/O port fifo into a block of memory.
+
+ Reads the 16-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead16 is invoked to read data from the I/O port.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+IoReadFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ if (IsTdxGuest ()) {
+ TdIoReadFifo16 (Port, Count, Buffer);
+ } else {
+ SevIoReadFifo16 (Port, Count, Buffer);
+ }
+}
+
+/**
+ Writes a block of memory into a 16-bit I/O port fifo.
+
+ Writes the 16-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 16-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite16 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+IoWriteFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ if (IsTdxGuest ()) {
+ TdIoWriteFifo16 (Port, Count, Buffer);
+ } else {
+ SevIoWriteFifo16 (Port, Count, Buffer);
+ }
+}
+
+/**
+ Reads a 32-bit I/O port fifo into a block of memory.
+
+ Reads the 32-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoRead32 is invoked to read data from the I/O port.
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+IoReadFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ )
+{
+ if (IsTdxGuest ()) {
+ TdIoReadFifo32 (Port, Count, Buffer);
+ } else {
+ SevIoReadFifo32 (Port, Count, Buffer);
+ }
+}
+
+/**
+ Writes a block of memory into a 32-bit I/O port fifo.
+
+ Writes the 32-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 32-bit I/O port operations are not supported, then ASSERT().
+
+ In TDX a serial of TdIoWrite32 is invoked to write data to the I/O port.
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+IoWriteFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ )
+{
+ if (IsTdxGuest ()) {
+ TdIoWriteFifo32 (Port, Count, Buffer);
+ } else {
+ SevIoWriteFifo32 (Port, Count, Buffer);
+ }
+}
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibSev.h b/MdePkg/Library/BaseIoLibIntrinsic/IoLibSev.h
new file mode 100644
index 000000000000..e219f8a36a47
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibSev.h
@@ -0,0 +1,166 @@
+/** @file
+ Header file for SEV IO library.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef IOLIB_SEV_H_
+#define IOLIB_SEV_H_
+
+#include <Base.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+
+/**
+ Reads an 8-bit I/O port fifo into a block of memory.
+
+ Reads the 8-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+SevIoReadFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ );
+
+/**
+ Writes a block of memory into an 8-bit I/O port fifo.
+
+ Writes the 8-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+SevIoWriteFifo8 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ );
+
+/**
+ Reads an 8-bit I/O port fifo into a block of memory.
+
+ Reads the 8-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+SevIoReadFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ );
+
+/**
+ Writes a block of memory into an 8-bit I/O port fifo.
+
+ Writes the 8-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+SevIoWriteFifo16 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ );
+
+/**
+ Reads an 8-bit I/O port fifo into a block of memory.
+
+ Reads the 8-bit I/O fifo port specified by Port.
+ The port is read Count times, and the read data is
+ stored in the provided Buffer.
+
+ This function must guarantee that all I/O read and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to read.
+ @param Count The number of times to read I/O port.
+ @param Buffer The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+SevIoReadFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ OUT VOID *Buffer
+ );
+
+/**
+ Writes a block of memory into an 8-bit I/O port fifo.
+
+ Writes the 8-bit I/O fifo port specified by Port.
+ The port is written Count times, and the write data is
+ retrieved from the provided Buffer.
+
+ This function must guarantee that all I/O write and write operations are
+ serialized.
+
+ If 8-bit I/O port operations are not supported, then ASSERT().
+
+ @param Port The I/O port to write.
+ @param Count The number of times to write I/O port.
+ @param Buffer The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+SevIoWriteFifo32 (
+ IN UINTN Port,
+ IN UINTN Count,
+ IN VOID *Buffer
+ );
+
+#endif
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm b/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm
index 106f8881c55c..d02286b4d518 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm
+++ b/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm
@@ -67,14 +67,14 @@ ASM_PFX(SevNoRepIo):
;------------------------------------------------------------------------------
; VOID
; EFIAPI
-; IoReadFifo8 (
+; SevIoReadFifo8 (
; IN UINTN Port, // rcx
; IN UINTN Size, // rdx
; OUT VOID *Buffer // r8
; );
;------------------------------------------------------------------------------
-global ASM_PFX(IoReadFifo8)
-ASM_PFX(IoReadFifo8):
+global ASM_PFX(SevIoReadFifo8)
+ASM_PFX(SevIoReadFifo8):
xchg rcx, rdx
xchg rdi, r8 ; rdi: buffer address; r8: save rdi
@@ -103,14 +103,14 @@ ASM_PFX(IoReadFifo8):
;------------------------------------------------------------------------------
; VOID
; EFIAPI
-; IoReadFifo16 (
+; SevIoReadFifo16 (
; IN UINTN Port, // rcx
; IN UINTN Size, // rdx
; OUT VOID *Buffer // r8
; );
;------------------------------------------------------------------------------
-global ASM_PFX(IoReadFifo16)
-ASM_PFX(IoReadFifo16):
+global ASM_PFX(SevIoReadFifo16)
+ASM_PFX(SevIoReadFifo16):
xchg rcx, rdx
xchg rdi, r8 ; rdi: buffer address; r8: save rdi
@@ -139,14 +139,14 @@ ASM_PFX(IoReadFifo16):
;------------------------------------------------------------------------------
; VOID
; EFIAPI
-; IoReadFifo32 (
+; SevIoReadFifo32 (
; IN UINTN Port, // rcx
; IN UINTN Size, // rdx
; OUT VOID *Buffer // r8
; );
;------------------------------------------------------------------------------
-global ASM_PFX(IoReadFifo32)
-ASM_PFX(IoReadFifo32):
+global ASM_PFX(SevIoReadFifo32)
+ASM_PFX(SevIoReadFifo32):
xchg rcx, rdx
xchg rdi, r8 ; rdi: buffer address; r8: save rdi
@@ -181,8 +181,8 @@ ASM_PFX(IoReadFifo32):
; IN VOID *Buffer // r8
; );
;------------------------------------------------------------------------------
-global ASM_PFX(IoWriteFifo8)
-ASM_PFX(IoWriteFifo8):
+global ASM_PFX(SevIoWriteFifo8)
+ASM_PFX(SevIoWriteFifo8):
xchg rcx, rdx
xchg rsi, r8 ; rsi: buffer address; r8: save rsi
@@ -211,14 +211,14 @@ ASM_PFX(IoWriteFifo8):
;------------------------------------------------------------------------------
; VOID
; EFIAPI
-; IoWriteFifo16 (
+; SevIoWriteFifo16 (
; IN UINTN Port, // rcx
; IN UINTN Size, // rdx
; IN VOID *Buffer // r8
; );
;------------------------------------------------------------------------------
-global ASM_PFX(IoWriteFifo16)
-ASM_PFX(IoWriteFifo16):
+global ASM_PFX(SevIoWriteFifo16)
+ASM_PFX(SevIoWriteFifo16):
xchg rcx, rdx
xchg rsi, r8 ; rsi: buffer address; r8: save rsi
@@ -247,14 +247,14 @@ ASM_PFX(IoWriteFifo16):
;------------------------------------------------------------------------------
; VOID
; EFIAPI
-; IoWriteFifo32 (
+; SevIoWriteFifo32 (
; IN UINTN Port, // rcx
; IN UINTN Size, // rdx
; IN VOID *Buffer // r8
; );
;------------------------------------------------------------------------------
-global ASM_PFX(IoWriteFifo32)
-ASM_PFX(IoWriteFifo32):
+global ASM_PFX(SevIoWriteFifo32)
+ASM_PFX(SevIoWriteFifo32):
xchg rcx, rdx
xchg rsi, r8 ; rsi: buffer address; r8: save rsi
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 09/29] MdePkg: Support IoRead/IoWrite for Tdx guest in BaseIoLibIntrinsic
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (7 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 08/29] MdePkg: Support IoFifo " Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-01 13:15 ` [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib Min Xu
` (19 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Michael D Kinney, Liming Gao, Zhiguang Liu, Brijesh Singh,
Erdem Aktas, James Bottomley, Jiewen Yao, Tom Lendacky,
Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
This commit supports IoRead/IoWrite for SEV/TDX/Legacy guest in one
binary. It checks the guest type in runtime and then call corresponding
functions.
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c | 49 +++++++++++--
MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c | 73 +++++++++++++++-----
2 files changed, 98 insertions(+), 24 deletions(-)
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c
index ecf9ed61911f..42b5d5743a4f 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c
@@ -17,6 +17,7 @@
#include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
/**
Reads an 8-bit I/O port.
@@ -25,7 +26,9 @@
This function must guarantee that all I/O read and write operations are
serialized.
- If 8-bit I/O port operations are not supported, then ASSERT().
+ If 8-bit I/O port operations are not supported, then ASSERT()
+
+ For Td guest TDVMCALL_IO is invoked to read I/O port.
@param Port The I/O port to read.
@@ -43,7 +46,11 @@ IoRead8 (
Flag = FilterBeforeIoRead (FilterWidth8, Port, &Data);
if (Flag) {
- __asm__ __volatile__ ("inb %w1,%b0" : "=a" (Data) : "d" ((UINT16)Port));
+ if (IsTdxGuest ()) {
+ Data = TdIoRead8 (Port);
+ } else {
+ __asm__ __volatile__ ("inb %w1,%b0" : "=a" (Data) : "d" ((UINT16)Port));
+ }
}
FilterAfterIoRead (FilterWidth8, Port, &Data);
@@ -59,6 +66,8 @@ IoRead8 (
If 8-bit I/O port operations are not supported, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to write I/O port.
+
@param Port The I/O port to write.
@param Value The value to write to the I/O port.
@@ -76,7 +85,11 @@ IoWrite8 (
Flag = FilterBeforeIoWrite (FilterWidth8, Port, &Value);
if (Flag) {
- __asm__ __volatile__ ("outb %b0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+ if (IsTdxGuest ()) {
+ TdIoWrite8 (Port, Value);
+ } else {
+ __asm__ __volatile__ ("outb %b0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+ }
}
FilterAfterIoWrite (FilterWidth8, Port, &Value);
@@ -93,6 +106,8 @@ IoWrite8 (
If 16-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 16-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to read I/O port.
+
@param Port The I/O port to read.
@return The value read.
@@ -111,7 +126,11 @@ IoRead16 (
Flag = FilterBeforeIoRead (FilterWidth16, Port, &Data);
if (Flag) {
+ if (IsTdxGuest ()) {
+ Data = TdIoRead16 (Port);
+ } else {
__asm__ __volatile__ ("inw %w1,%w0" : "=a" (Data) : "d" ((UINT16)Port));
+ }
}
FilterAfterIoRead (FilterWidth16, Port, &Data);
@@ -128,6 +147,8 @@ IoRead16 (
If 16-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 16-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to write I/O port.
+
@param Port The I/O port to write.
@param Value The value to write to the I/O port.
@@ -148,7 +169,11 @@ IoWrite16 (
Flag = FilterBeforeIoWrite (FilterWidth16, Port, &Value);
if (Flag) {
- __asm__ __volatile__ ("outw %w0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+ if (IsTdxGuest ()) {
+ TdIoWrite16 (Port, Value);
+ } else {
+ __asm__ __volatile__ ("outw %w0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+ }
}
FilterAfterIoWrite (FilterWidth16, Port, &Value);
@@ -165,6 +190,8 @@ IoWrite16 (
If 32-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 32-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to read I/O port.
+
@param Port The I/O port to read.
@return The value read.
@@ -183,7 +210,11 @@ IoRead32 (
Flag = FilterBeforeIoRead (FilterWidth32, Port, &Data);
if (Flag) {
- __asm__ __volatile__ ("inl %w1,%0" : "=a" (Data) : "d" ((UINT16)Port));
+ if (IsTdxGuest ()) {
+ Data = TdIoRead32 (Port);
+ } else {
+ __asm__ __volatile__ ("inl %w1,%0" : "=a" (Data) : "d" ((UINT16)Port));
+ }
}
FilterAfterIoRead (FilterWidth32, Port, &Data);
@@ -200,6 +231,8 @@ IoRead32 (
If 32-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 32-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to write I/O port.
+
@param Port The I/O port to write.
@param Value The value to write to the I/O port.
@@ -219,7 +252,11 @@ IoWrite32 (
Flag = FilterBeforeIoWrite (FilterWidth32, Port, &Value);
if (Flag) {
- __asm__ __volatile__ ("outl %0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+ if (IsTdxGuest ()) {
+ TdIoWrite32 (Port, Value);
+ } else {
+ __asm__ __volatile__ ("outl %0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+ }
}
FilterAfterIoWrite (FilterWidth32, Port, &Value);
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c
index d2bc5f527cf6..4d7945ae496f 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c
@@ -16,6 +16,7 @@
#include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
//
// Microsoft Visual Studio 7.1 Function Prototypes for I/O Intrinsics.
@@ -54,6 +55,8 @@ void _ReadWriteBarrier (void);
If 8-bit I/O port operations are not supported, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to read I/O port.
+
@param Port The I/O port to read.
@return The value read.
@@ -70,9 +73,13 @@ IoRead8 (
Flag = FilterBeforeIoRead (FilterWidth8, Port, &Value);
if (Flag) {
- _ReadWriteBarrier ();
- Value = (UINT8)_inp ((UINT16)Port);
- _ReadWriteBarrier ();
+ if (IsTdxGuest ()) {
+ Value = TdIoRead8 (Port);
+ } else {
+ _ReadWriteBarrier ();
+ Value = (UINT8)_inp ((UINT16)Port);
+ _ReadWriteBarrier ();
+ }
}
FilterAfterIoRead (FilterWidth8, Port, &Value);
@@ -88,6 +95,8 @@ IoRead8 (
If 8-bit I/O port operations are not supported, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to write I/O port.
+
@param Port The I/O port to write.
@param Value The value to write to the I/O port.
@@ -105,9 +114,13 @@ IoWrite8 (
Flag = FilterBeforeIoWrite(FilterWidth8, Port, &Value);
if (Flag) {
- _ReadWriteBarrier ();
- (UINT8)_outp ((UINT16)Port, Value);
- _ReadWriteBarrier ();
+ if (IsTdxGuest ()) {
+ TdIoWrite8 (Port, Value);
+ } else {
+ _ReadWriteBarrier ();
+ (UINT8)_outp ((UINT16)Port, Value);
+ _ReadWriteBarrier ();
+ }
}
FilterAfterIoWrite (FilterWidth8, Port, &Value);
@@ -124,6 +137,8 @@ IoWrite8 (
If 16-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 16-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to read I/O port.
+
@param Port The I/O port to read.
@return The value read.
@@ -142,9 +157,13 @@ IoRead16 (
Flag = FilterBeforeIoRead (FilterWidth16, Port, &Value);
if (Flag) {
- _ReadWriteBarrier ();
- Value = _inpw ((UINT16)Port);
- _ReadWriteBarrier ();
+ if (IsTdxGuest ()) {
+ Value = TdIoRead16 (Port);
+ } else {
+ _ReadWriteBarrier ();
+ Value = _inpw ((UINT16)Port);
+ _ReadWriteBarrier ();
+ }
}
FilterBeforeIoRead (FilterWidth16, Port, &Value);
@@ -161,6 +180,8 @@ IoRead16 (
If 16-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 16-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to write I/O port.
+
@param Port The I/O port to write.
@param Value The value to write to the I/O port.
@@ -180,9 +201,13 @@ IoWrite16 (
Flag = FilterBeforeIoWrite(FilterWidth16, Port, &Value);
if (Flag) {
- _ReadWriteBarrier ();
- _outpw ((UINT16)Port, Value);
- _ReadWriteBarrier ();
+ if (IsTdxGuest ()) {
+ TdIoWrite16 (Port, Value);
+ } else {
+ _ReadWriteBarrier ();
+ _outpw ((UINT16)Port, Value);
+ _ReadWriteBarrier ();
+ }
}
FilterAfterIoWrite (FilterWidth16, Port, &Value);
@@ -199,6 +224,8 @@ IoWrite16 (
If 32-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 32-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to read I/O port.
+
@param Port The I/O port to read.
@return The value read.
@@ -217,9 +244,13 @@ IoRead32 (
Flag = FilterBeforeIoRead(FilterWidth32, Port, &Value);
if (Flag) {
- _ReadWriteBarrier ();
- Value = _inpd ((UINT16)Port);
- _ReadWriteBarrier ();
+ if (IsTdxGuest ()) {
+ Value = TdIoRead32 (Port);
+ } else {
+ _ReadWriteBarrier ();
+ Value = _inpd ((UINT16)Port);
+ _ReadWriteBarrier ();
+ }
}
FilterAfterIoRead (FilterWidth32, Port, &Value);
@@ -236,6 +267,8 @@ IoRead32 (
If 32-bit I/O port operations are not supported, then ASSERT().
If Port is not aligned on a 32-bit boundary, then ASSERT().
+ For Td guest TDVMCALL_IO is invoked to write I/O port.
+
@param Port The I/O port to write.
@param Value The value to write to the I/O port.
@@ -255,9 +288,13 @@ IoWrite32 (
Flag = FilterBeforeIoWrite(FilterWidth32, Port, &Value);
if (Flag) {
- _ReadWriteBarrier ();
- _outpd ((UINT16)Port, Value);
- _ReadWriteBarrier ();
+ if (IsTdxGuest ()) {
+ TdIoWrite32 (Port, Value);
+ } else {
+ _ReadWriteBarrier ();
+ _outpd ((UINT16)Port, Value);
+ _ReadWriteBarrier ();
+ }
}
FilterAfterIoWrite (FilterWidth32, Port, &Value);
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (8 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 09/29] MdePkg: Support IoRead/IoWrite " Min Xu
@ 2021-11-01 13:15 ` Min Xu
2021-11-01 15:31 ` Guo Dong
` (2 more replies)
2021-11-01 13:16 ` [PATCH V3 11/29] UefiCpuPkg: Support TDX in BaseXApicX2ApicLib Min Xu
` (18 subsequent siblings)
28 siblings, 3 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:15 UTC (permalink / raw)
To: devel
Cc: Min Xu, Guo Dong, Ray Ni, Maurice Ma, Benjamin You, Jiewen Yao,
Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
TdxLib is included in BaseXApicX2ApicLib. To prevent any build
breakage, update the UefiPayloadPkg.dsc to include the TdxLib
library.
Cc: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
UefiPayloadPkg/UefiPayloadPkg.dsc | 1 +
1 file changed, 1 insertion(+)
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc b/UefiPayloadPkg/UefiPayloadPkg.dsc
index 0df5f827c903..e3b6135382ee 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dsc
+++ b/UefiPayloadPkg/UefiPayloadPkg.dsc
@@ -260,6 +260,7 @@
VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf
VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+ TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
[LibraryClasses.common.SEC]
HobLib|UefiPayloadPkg/Library/PayloadEntryHobLib/HobLib.inf
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib
2021-11-01 13:15 ` [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib Min Xu
@ 2021-11-01 15:31 ` Guo Dong
2021-11-01 15:58 ` Ma, Maurice
2021-11-02 14:32 ` Gerd Hoffmann
2 siblings, 0 replies; 107+ messages in thread
From: Guo Dong @ 2021-11-01 15:31 UTC (permalink / raw)
To: Xu, Min M, devel@edk2.groups.io
Cc: Ni, Ray, Ma, Maurice, You, Benjamin, Yao, Jiewen, Gerd Hoffmann
Reviewed-by: Guo Dong <guo.dong@intel.com>
-----Original Message-----
From: Xu, Min M <min.m.xu@intel.com>
Sent: Monday, November 1, 2021 6:16 AM
To: devel@edk2.groups.io
Cc: Xu, Min M <min.m.xu@intel.com>; Dong, Guo <guo.dong@intel.com>; Ni, Ray <ray.ni@intel.com>; Ma, Maurice <maurice.ma@intel.com>; You, Benjamin <benjamin.you@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>; Gerd Hoffmann <kraxel@redhat.com>
Subject: [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
TdxLib is included in BaseXApicX2ApicLib. To prevent any build breakage, update the UefiPayloadPkg.dsc to include the TdxLib library.
Cc: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
UefiPayloadPkg/UefiPayloadPkg.dsc | 1 +
1 file changed, 1 insertion(+)
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc b/UefiPayloadPkg/UefiPayloadPkg.dsc
index 0df5f827c903..e3b6135382ee 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dsc
+++ b/UefiPayloadPkg/UefiPayloadPkg.dsc
@@ -260,6 +260,7 @@
VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf
VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+ TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
[LibraryClasses.common.SEC]
HobLib|UefiPayloadPkg/Library/PayloadEntryHobLib/HobLib.inf
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib
2021-11-01 13:15 ` [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib Min Xu
2021-11-01 15:31 ` Guo Dong
@ 2021-11-01 15:58 ` Ma, Maurice
2021-11-02 0:07 ` Min Xu
2021-11-02 14:32 ` Gerd Hoffmann
2 siblings, 1 reply; 107+ messages in thread
From: Ma, Maurice @ 2021-11-01 15:58 UTC (permalink / raw)
To: Xu, Min M
Cc: Dong, Guo, Ni, Ray, You, Benjamin, Yao, Jiewen, Gerd Hoffmann,
devel@edk2.groups.io
Minor comment on the subject "PreparePrepare UefiPayloadPkg to use TdxLib". Duplicated "Prepare" ?
Otherwise, looks good to me.
Reviewed-by: Maurice Ma <maurice.ma@intel.com>
> -----Original Message-----
> From: Xu, Min M <min.m.xu@intel.com>
> Sent: Monday, November 1, 2021 6:16
> To: devel@edk2.groups.io
> Cc: Xu, Min M <min.m.xu@intel.com>; Dong, Guo <guo.dong@intel.com>; Ni,
> Ray <ray.ni@intel.com>; Ma, Maurice <maurice.ma@intel.com>; You,
> Benjamin <benjamin.you@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>;
> Gerd Hoffmann <kraxel@redhat.com>
> Subject: [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg
> to use TdxLib
>
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> TdxLib is included in BaseXApicX2ApicLib. To prevent any build breakage,
> update the UefiPayloadPkg.dsc to include the TdxLib library.
>
> Cc: Guo Dong <guo.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Maurice Ma <maurice.ma@intel.com>
> Cc: Benjamin You <benjamin.you@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
> ---
> UefiPayloadPkg/UefiPayloadPkg.dsc | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc
> b/UefiPayloadPkg/UefiPayloadPkg.dsc
> index 0df5f827c903..e3b6135382ee 100644
> --- a/UefiPayloadPkg/UefiPayloadPkg.dsc
> +++ b/UefiPayloadPkg/UefiPayloadPkg.dsc
> @@ -260,6 +260,7 @@
>
> VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLi
> b.inf
>
> VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/Va
> riablePolicyHelperLib.inf
> VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
> + TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
>
> [LibraryClasses.common.SEC]
> HobLib|UefiPayloadPkg/Library/PayloadEntryHobLib/HobLib.inf
> --
> 2.29.2.windows.2
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib
2021-11-01 15:58 ` Ma, Maurice
@ 2021-11-02 0:07 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-02 0:07 UTC (permalink / raw)
To: Ma, Maurice
Cc: Dong, Guo, Ni, Ray, You, Benjamin, Yao, Jiewen, Gerd Hoffmann,
devel@edk2.groups.io
On Monday, November 1, 2021 11:59 PM, Ma, Maurice wrote:
> Minor comment on the subject "PreparePrepare UefiPayloadPkg to use TdxLib".
> Duplicated "Prepare" ?
Thanks much for the reminder. It will be fixed in the next version.
> Otherwise, looks good to me.
>
> Reviewed-by: Maurice Ma <maurice.ma@intel.com>
>
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib
2021-11-01 13:15 ` [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib Min Xu
2021-11-01 15:31 ` Guo Dong
2021-11-01 15:58 ` Ma, Maurice
@ 2021-11-02 14:32 ` Gerd Hoffmann
2 siblings, 0 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:32 UTC (permalink / raw)
To: Min Xu; +Cc: devel, Guo Dong, Ray Ni, Maurice Ma, Benjamin You, Jiewen Yao
On Mon, Nov 01, 2021 at 09:15:59PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> TdxLib is included in BaseXApicX2ApicLib. To prevent any build
> breakage, update the UefiPayloadPkg.dsc to include the TdxLib
> library.
>
> Cc: Guo Dong <guo.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Maurice Ma <maurice.ma@intel.com>
> Cc: Benjamin You <benjamin.you@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 11/29] UefiCpuPkg: Support TDX in BaseXApicX2ApicLib
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (9 preceding siblings ...)
2021-11-01 13:15 ` [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-02 14:33 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 12/29] UefiCpuPkg: Define ConfidentialComputingGuestAttr Min Xu
` (17 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Eric Dong, Ray Ni, Rahul Kumar, Brijesh Singh,
Erdem Aktas, James Bottomley, Jiewen Yao, Tom Lendacky,
Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
MSR is accessed in BaseXApicX2ApicLib. In TDX some MSRs are accessed
directly from/to CPU. Some should be accessed via explicit requests
from the host VMM using TDCALL(TDG.VP.VMCALL). This is done by the
help of TdxLib.
Please refer to [TDX] Section 18.1
TDX: https://software.intel.com/content/dam/develop/external/us/en/
documents/tdx-module-1.0-public-spec-v0.931.pdf
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
.../BaseXApicX2ApicLib/BaseXApicX2ApicLib.c | 219 +++++++++++++++++-
.../BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf | 1 +
UefiCpuPkg/UefiCpuPkg.dsc | 1 +
3 files changed, 213 insertions(+), 8 deletions(-)
diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c
index cdcbca046191..304a3d2a8006 100644
--- a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c
+++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c
@@ -23,11 +23,213 @@
#include <Library/TimerLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiCpuLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
//
// Library internal functions
//
+BOOLEAN mBaseXApicIsTdxEnabled = FALSE;
+BOOLEAN mBaseXApicTdxProbed = FALSE;
+
+/**
+ Check if it is Tdx guest.
+
+ @return TRUE It is Tdx guest
+ @return FALSE It is not Tdx guest
+
+**/
+BOOLEAN
+BaseXApicIsTdxGuest (
+ VOID
+ )
+{
+ UINT32 Eax;
+ UINT32 Ebx;
+ UINT32 Ecx;
+ UINT32 Edx;
+ UINT32 LargestEax;
+
+ if (mBaseXApicTdxProbed) {
+ return mBaseXApicIsTdxEnabled;
+ }
+
+ mBaseXApicIsTdxEnabled = FALSE;
+
+ do {
+ AsmCpuid (CPUID_SIGNATURE, &LargestEax, &Ebx, &Ecx, &Edx);
+
+ if (Ebx != CPUID_SIGNATURE_GENUINE_INTEL_EBX
+ || Edx != CPUID_SIGNATURE_GENUINE_INTEL_EDX
+ || Ecx != CPUID_SIGNATURE_GENUINE_INTEL_ECX) {
+ break;
+ }
+
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &Ecx, NULL);
+ if ((Ecx & BIT31) == 0) {
+ break;
+ }
+
+ if (LargestEax < 0x21) {
+ break;
+ }
+
+ AsmCpuidEx (0x21, 0, &Eax, &Ebx, &Ecx, &Edx);
+ if (Ebx != SIGNATURE_32 ('I', 'n', 't', 'e')
+ || Edx != SIGNATURE_32 ('l', 'T', 'D', 'X')
+ || Ecx != SIGNATURE_32 (' ', ' ', ' ', ' ')) {
+ break;
+ }
+
+ mBaseXApicIsTdxEnabled = TRUE;
+ }while (FALSE);
+
+ mBaseXApicTdxProbed = TRUE;
+
+ return mBaseXApicIsTdxEnabled;
+}
+
+
+/**
+ Some MSRs in TDX are accessed via TdCall.
+ Some are directly read/write from/to CPU.
+
+ @param MsrIndex Index of the MSR
+ @retval TRUE MSR accessed via TdCall.
+ @retval FALSE MSR accessed not via TdCall.
+
+**/
+BOOLEAN
+AccessMsrTdxCall (
+ IN UINT32 MsrIndex
+ )
+{
+ if (!BaseXApicIsTdxGuest ()) {
+ return FALSE;
+ }
+
+ switch (MsrIndex) {
+ case MSR_IA32_X2APIC_TPR:
+ case MSR_IA32_X2APIC_PPR:
+ case MSR_IA32_X2APIC_EOI:
+ case MSR_IA32_X2APIC_ISR0:
+ case MSR_IA32_X2APIC_ISR1:
+ case MSR_IA32_X2APIC_ISR2:
+ case MSR_IA32_X2APIC_ISR3:
+ case MSR_IA32_X2APIC_ISR4:
+ case MSR_IA32_X2APIC_ISR5:
+ case MSR_IA32_X2APIC_ISR6:
+ case MSR_IA32_X2APIC_ISR7:
+ case MSR_IA32_X2APIC_TMR0:
+ case MSR_IA32_X2APIC_TMR1:
+ case MSR_IA32_X2APIC_TMR2:
+ case MSR_IA32_X2APIC_TMR3:
+ case MSR_IA32_X2APIC_TMR4:
+ case MSR_IA32_X2APIC_TMR5:
+ case MSR_IA32_X2APIC_TMR6:
+ case MSR_IA32_X2APIC_TMR7:
+ case MSR_IA32_X2APIC_IRR0:
+ case MSR_IA32_X2APIC_IRR1:
+ case MSR_IA32_X2APIC_IRR2:
+ case MSR_IA32_X2APIC_IRR3:
+ case MSR_IA32_X2APIC_IRR4:
+ case MSR_IA32_X2APIC_IRR5:
+ case MSR_IA32_X2APIC_IRR6:
+ case MSR_IA32_X2APIC_IRR7:
+ return FALSE;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+/**
+ Read MSR value.
+
+ @param MsrIndex Index of the MSR to read
+ @retval 64-bit Value of MSR.
+
+**/
+UINT64
+LocalApicReadMsrReg64 (
+ IN UINT32 MsrIndex
+ )
+{
+ UINT64 Val;
+ UINT64 Status;
+ if (AccessMsrTdxCall (MsrIndex)) {
+ Status = TdVmCall (TDVMCALL_RDMSR, (UINT64) MsrIndex, 0, 0, 0, &Val);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ } else {
+ Val = AsmReadMsr64 (MsrIndex);
+ }
+ return Val;
+}
+
+/**
+ Write to MSR.
+
+ @param MsrIndex Index of the MSR to write to
+ @param Value Value to be written to the MSR
+
+ @return Value
+
+**/
+UINT64
+LocalApicWriteMsrReg64 (
+ IN UINT32 MsrIndex,
+ IN UINT64 Value
+ )
+{
+ UINT64 Status;
+ if (AccessMsrTdxCall (MsrIndex)) {
+ Status = TdVmCall (TDVMCALL_WRMSR, (UINT64) MsrIndex, Value, 0, 0, 0);
+ if (Status != 0) {
+ TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+ }
+ } else {
+ AsmWriteMsr64 (MsrIndex, Value);
+ }
+
+ return Value;
+}
+
+/**
+ Read MSR value.
+
+ @param MsrIndex Index of the MSR to read
+ @retval 32-bit Value of MSR.
+
+**/
+UINT32
+LocalApicReadMsrReg32 (
+ IN UINT32 MsrIndex
+ )
+{
+ return (UINT32)LocalApicReadMsrReg64 (MsrIndex);
+}
+
+/**
+ Write to MSR.
+
+ @param MsrIndex Index of the MSR to write to
+ @param Value Value to be written to the MSR
+
+ @return Value
+
+**/
+UINT32
+LocalApicWriteMsrReg32 (
+ IN UINT32 MsrIndex,
+ IN UINT32 Value
+ )
+{
+ return (UINT32) LocalApicWriteMsrReg64 (MsrIndex, Value);
+}
+
/**
Determine if the CPU supports the Local APIC Base Address MSR.
@@ -77,7 +279,7 @@ GetLocalApicBaseAddress (
return PcdGet32 (PcdCpuLocalApicBaseAddress);
}
- ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
+ ApicBaseMsr.Uint64 = LocalApicReadMsrReg64 (MSR_IA32_APIC_BASE);
return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHi, 32)) +
(((UINTN)ApicBaseMsr.Bits.ApicBase) << 12);
@@ -108,12 +310,12 @@ SetLocalApicBaseAddress (
return;
}
- ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
+ ApicBaseMsr.Uint64 = LocalApicReadMsrReg64 (MSR_IA32_APIC_BASE);
ApicBaseMsr.Bits.ApicBase = (UINT32) (BaseAddress >> 12);
ApicBaseMsr.Bits.ApicBaseHi = (UINT32) (RShiftU64((UINT64) BaseAddress, 32));
- AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
+ LocalApicWriteMsrReg64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
}
/**
@@ -153,7 +355,7 @@ ReadLocalApicReg (
ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET);
MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS;
- return AsmReadMsr32 (MsrIndex);
+ return LocalApicReadMsrReg32 (MsrIndex);
}
}
@@ -202,7 +404,7 @@ WriteLocalApicReg (
// Use memory fence here to force the serializing semantics to be consisent with xAPIC mode.
//
MemoryFence ();
- AsmWriteMsr32 (MsrIndex, Value);
+ LocalApicWriteMsrReg32 (MsrIndex, Value);
}
}
@@ -309,7 +511,7 @@ GetApicMode (
return LOCAL_APIC_MODE_XAPIC;
}
- ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
+ ApicBaseMsr.Uint64 = LocalApicReadMsrReg64 (MSR_IA32_APIC_BASE);
//
// Local APIC should have been enabled
//
@@ -350,13 +552,14 @@ SetApicMode (
CurrentMode = GetApicMode ();
if (CurrentMode == LOCAL_APIC_MODE_XAPIC) {
+
switch (ApicMode) {
case LOCAL_APIC_MODE_XAPIC:
break;
case LOCAL_APIC_MODE_X2APIC:
- ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
+ ApicBaseMsr.Uint64 = LocalApicReadMsrReg64 (MSR_IA32_APIC_BASE);
ApicBaseMsr.Bits.EXTD = 1;
- AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
+ LocalApicWriteMsrReg64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
break;
default:
ASSERT (FALSE);
diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf
index 1e2a4f8b790f..1276f6ec06d6 100644
--- a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf
+++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf
@@ -39,6 +39,7 @@
IoLib
PcdLib
UefiCpuLib
+ TdxLib
[Pcd]
gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index 870b45284087..e5e6bf77c8e2 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -61,6 +61,7 @@
TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf
+ TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
[LibraryClasses.common.SEC]
PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 11/29] UefiCpuPkg: Support TDX in BaseXApicX2ApicLib
2021-11-01 13:16 ` [PATCH V3 11/29] UefiCpuPkg: Support TDX in BaseXApicX2ApicLib Min Xu
@ 2021-11-02 14:33 ` Gerd Hoffmann
0 siblings, 0 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:33 UTC (permalink / raw)
To: Min Xu
Cc: devel, Eric Dong, Ray Ni, Rahul Kumar, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
On Mon, Nov 01, 2021 at 09:16:00PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> MSR is accessed in BaseXApicX2ApicLib. In TDX some MSRs are accessed
> directly from/to CPU. Some should be accessed via explicit requests
> from the host VMM using TDCALL(TDG.VP.VMCALL). This is done by the
> help of TdxLib.
>
> Please refer to [TDX] Section 18.1
> TDX: https://software.intel.com/content/dam/develop/external/us/en/
> documents/tdx-module-1.0-public-spec-v0.931.pdf
>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Rahul Kumar <rahul1.kumar@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Cc: Erdem Aktas <erdemaktas@google.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 12/29] UefiCpuPkg: Define ConfidentialComputingGuestAttr
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (10 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 11/29] UefiCpuPkg: Support TDX in BaseXApicX2ApicLib Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-02 14:36 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 13/29] MdePkg: Add macro to check SEV/TDX guest Min Xu
` (16 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Brijesh Singh via groups.io, Michael Roth, Ray Ni, Rahul Kumar,
Eric Dong, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
Jordan Justen, Ard Biesheuvel, Erdem Aktas, Gerd Hoffmann,
Brijesh Singh
From: "Brijesh Singh via groups.io" <brijesh.singh=amd.com@groups.io>
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
While initializing APs, the MpInitLib may need to know whether the
guest is running with active AMD SEV or Intel TDX memory encryption.
Add a new ConfidentialComputingGuestAttr PCD that can be used to query
the memory encryption attribute.
Cc: Michael Roth <michael.roth@amd.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Suggested-by: Jiewen Yao <jiewen.yao@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
.../Include/ConfidentialComputingGuestAttr.h | 25 +++++++++++++++++++
MdePkg/MdePkg.dec | 4 +++
2 files changed, 29 insertions(+)
create mode 100644 MdePkg/Include/ConfidentialComputingGuestAttr.h
diff --git a/MdePkg/Include/ConfidentialComputingGuestAttr.h b/MdePkg/Include/ConfidentialComputingGuestAttr.h
new file mode 100644
index 000000000000..495b0df0ac33
--- /dev/null
+++ b/MdePkg/Include/ConfidentialComputingGuestAttr.h
@@ -0,0 +1,25 @@
+/** @file
+Definitions for Confidential Computing Attribute
+
+Copyright (c) 2021 AMD Inc. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef CONFIDENTIAL_COMPUTING_GUEST_ATTR_H_
+#define CONFIDENTIAL_COMPUTING_GUEST_ATTR_H_
+
+typedef enum {
+ /* The guest is running with memory encryption disabled. */
+ CCAttrNotEncrypted = 0,
+
+ /* The guest is running with AMD SEV memory encryption enabled. */
+ CCAttrAmdSev = 0x100,
+ CCAttrAmdSevEs = 0x101,
+ CCAttrAmdSevSnp = 0x102,
+
+ /* The guest is running with Intel TDX memory encryption enabled. */
+ CCAttrIntelTdx = 0x200,
+} CONFIDENTIAL_COMPUTING_GUEST_ATTR;
+
+#endif
diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
index 321a14fbaa0a..679a13d2ab1d 100644
--- a/MdePkg/MdePkg.dec
+++ b/MdePkg/MdePkg.dec
@@ -2399,5 +2399,9 @@
# @Prompt FSB Clock.
gEfiMdePkgTokenSpaceGuid.PcdFSBClock|200000000|UINT32|0x0000000c
+ ## This dynamic PCD indicates the memory encryption attribute of the guest.
+ # @Prompt Memory encryption attribute
+ gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr|0|UINT64|0x0000002e
+
[UserExtensions.TianoCore."ExtraFiles"]
MdePkgExtra.uni
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 12/29] UefiCpuPkg: Define ConfidentialComputingGuestAttr
2021-11-01 13:16 ` [PATCH V3 12/29] UefiCpuPkg: Define ConfidentialComputingGuestAttr Min Xu
@ 2021-11-02 14:36 ` Gerd Hoffmann
2021-11-03 8:32 ` [edk2-devel] " Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:36 UTC (permalink / raw)
To: Min Xu
Cc: devel, Brijesh Singh via groups.io, Michael Roth, Ray Ni,
Rahul Kumar, Eric Dong, James Bottomley, Jiewen Yao, Tom Lendacky,
Jordan Justen, Ard Biesheuvel, Erdem Aktas, Brijesh Singh
Hi,
> .../Include/ConfidentialComputingGuestAttr.h | 25 +++++++++++++++++++
> MdePkg/MdePkg.dec | 4 +++
Subject doesn't match patch (UefiCpuPkg vs MdePkg).
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 12/29] UefiCpuPkg: Define ConfidentialComputingGuestAttr
2021-11-02 14:36 ` Gerd Hoffmann
@ 2021-11-03 8:32 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-03 8:32 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com
Cc: Brijesh Singh via groups.io, Michael Roth, Ni, Ray, Kumar, Rahul1,
Dong, Eric, James Bottomley, Yao, Jiewen, Tom Lendacky,
Justen, Jordan L, Ard Biesheuvel, Erdem Aktas, Brijesh Singh
On November 2, 2021 10:36 PM, Gerd Hoffmann wrote:
> Hi,
>
> > .../Include/ConfidentialComputingGuestAttr.h | 25 +++++++++++++++++++
> > MdePkg/MdePkg.dec | 4 +++
>
> Subject doesn't match patch (UefiCpuPkg vs MdePkg).
>
It is SEV's patch and they're aware of it.
See https://edk2.groups.io/g/devel/message/82650
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 13/29] MdePkg: Add macro to check SEV/TDX guest
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (11 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 12/29] UefiCpuPkg: Define ConfidentialComputingGuestAttr Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-02 14:36 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib Min Xu
` (15 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Michael Roth, Ray Ni, Rahul Kumar, Eric Dong,
James Bottomley, Jiewen Yao, Tom Lendacky, Jordan Justen,
Ard Biesheuvel, Erdem Aktas, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Add macros CC_GUEST_IS_TDX/CC_GUEST_IS_SEV to check SEV/TDX guest.
Cc: Michael Roth <michael.roth@amd.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel>
---
MdePkg/Include/ConfidentialComputingGuestAttr.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/MdePkg/Include/ConfidentialComputingGuestAttr.h b/MdePkg/Include/ConfidentialComputingGuestAttr.h
index 495b0df0ac33..0fbc0add3e47 100644
--- a/MdePkg/Include/ConfidentialComputingGuestAttr.h
+++ b/MdePkg/Include/ConfidentialComputingGuestAttr.h
@@ -22,4 +22,7 @@ typedef enum {
CCAttrIntelTdx = 0x200,
} CONFIDENTIAL_COMPUTING_GUEST_ATTR;
+#define CC_GUEST_IS_TDX(x) ((x) == CCAttrIntelTdx)
+#define CC_GUEST_IS_SEV(x) ((x) == CCAttrAmdSev || (x) == CCAttrAmdSevEs || (x) == CCAttrAmdSevSnp)
+
#endif
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 13/29] MdePkg: Add macro to check SEV/TDX guest
2021-11-01 13:16 ` [PATCH V3 13/29] MdePkg: Add macro to check SEV/TDX guest Min Xu
@ 2021-11-02 14:36 ` Gerd Hoffmann
0 siblings, 0 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-02 14:36 UTC (permalink / raw)
To: Min Xu
Cc: devel, Michael Roth, Ray Ni, Rahul Kumar, Eric Dong,
James Bottomley, Jiewen Yao, Tom Lendacky, Jordan Justen,
Ard Biesheuvel, Erdem Aktas
On Mon, Nov 01, 2021 at 09:16:02PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> Add macros CC_GUEST_IS_TDX/CC_GUEST_IS_SEV to check SEV/TDX guest.
>
> Cc: Michael Roth <michael.roth@amd.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Rahul Kumar <rahul1.kumar@intel.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Erdem Aktas <erdemaktas@google.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (12 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 13/29] MdePkg: Add macro to check SEV/TDX guest Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 6:09 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx Min Xu
` (14 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In TDVF BSP and APs are simplified. BSP is the vCPU-0, while the others
are treated as APs.
So MP intialization is rather simple. The processor info is retrieved by
TDCALL, ApWorker is not supported, BSP is always the working processor,
while the APs are just in a wait-for-precedure state.
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf | 5 +
UefiCpuPkg/Library/MpInitLib/DxeMpLib.c | 15 ++-
UefiCpuPkg/Library/MpInitLib/MpIntelTdx.h | 107 +++++++++++++++
UefiCpuPkg/Library/MpInitLib/MpLib.c | 27 ++++
UefiCpuPkg/Library/MpInitLib/MpLibTdx.c | 126 ++++++++++++++++++
UefiCpuPkg/Library/MpInitLib/MpLibTdxNull.c | 117 ++++++++++++++++
UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf | 5 +
.../Library/MpInitLib/X64/IntelTdcall.nasm | 120 +++++++++++++++++
8 files changed, 521 insertions(+), 1 deletion(-)
create mode 100644 UefiCpuPkg/Library/MpInitLib/MpIntelTdx.h
create mode 100644 UefiCpuPkg/Library/MpInitLib/MpLibTdx.c
create mode 100644 UefiCpuPkg/Library/MpInitLib/MpLibTdxNull.c
create mode 100644 UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
index d34419c2a524..d78b6c179501 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
@@ -22,10 +22,13 @@
#
[Sources.IA32]
+ MpLibTdxNull.c
Ia32/MpFuncs.nasm
[Sources.X64]
+ MpLibTdx.c
X64/MpFuncs.nasm
+ X64/IntelTdcall.nasm
[Sources.common]
MpEqu.inc
@@ -33,6 +36,7 @@
MpLib.c
MpLib.h
Microcode.c
+ MpIntelTdx.h
[Packages]
MdePkg/MdePkg.dec
@@ -76,3 +80,4 @@
gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr ## CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
index 93fc63bf93e3..718c6adb9d2a 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
@@ -7,6 +7,7 @@
**/
#include "MpLib.h"
+#include "MpIntelTdx.h"
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
@@ -15,8 +16,8 @@
#include <Library/VmgExitLib.h>
#include <Register/Amd/Fam17Msr.h>
#include <Register/Amd/Ghcb.h>
-
#include <Protocol/Timer.h>
+#include <ConfidentialComputingGuestAttr.h>
#define AP_SAFE_STACK_SIZE 128
@@ -801,6 +802,10 @@ MpInitLibStartupThisAP (
{
EFI_STATUS Status;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return EFI_UNSUPPORTED;
+ }
+
//
// temporarily stop checkAllApsStatus for avoid resource dead-lock.
//
@@ -857,6 +862,10 @@ MpInitLibSwitchBSP (
EFI_TIMER_ARCH_PROTOCOL *Timer;
UINT64 TimerPeriod;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return EFI_UNSUPPORTED;
+ }
+
TimerPeriod = 0;
//
// Locate Timer Arch Protocol
@@ -930,6 +939,10 @@ MpInitLibEnableDisableAP (
EFI_STATUS Status;
BOOLEAN TempStopCheckState;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return EFI_UNSUPPORTED;
+ }
+
TempStopCheckState = FALSE;
//
// temporarily stop checkAllAPsStatus for initialize parameters.
diff --git a/UefiCpuPkg/Library/MpInitLib/MpIntelTdx.h b/UefiCpuPkg/Library/MpInitLib/MpIntelTdx.h
new file mode 100644
index 000000000000..59bd739eed22
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/MpIntelTdx.h
@@ -0,0 +1,107 @@
+/** @file
+ Intel Tdx header file.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef MP_INTEL_TDX_H_
+#define MP_INTEL_TDX_H_
+
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Protocol/MpService.h>
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+ @param[out] HealthData Return processor health data.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+TdxMpInitLibGetProcessorInfo (
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer,
+ OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL
+ );
+
+/**
+ Retrieves the number of logical processor in the platform and the number of
+ those logical processors that are enabled on this boot. This service may only
+ be called from the BSP.
+
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors
+ is NULL.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+TdxMpInitLibGetNumberOfProcessors (
+ OUT UINTN *NumberOfProcessors, OPTIONAL
+ OUT UINTN *NumberOfEnabledProcessors OPTIONAL
+ );
+
+/**
+ The TDCALL instruction causes a VM exit to the Intel TDX module. It is
+ used to call guest-side Intel TDX functions, either local or a TD exit
+ to the host VMM, as selected by Leaf.
+
+ @param[in] Leaf Leaf number of TDCALL instruction
+ @param[in] Arg1 Arg1
+ @param[in] Arg2 Arg2
+ @param[in] Arg3 Arg3
+ @param[in,out] Results Returned result of the Leaf function
+
+ @return EFI_SUCCESS
+ @return Other See individual leaf functions
+**/
+EFI_STATUS
+EFIAPI
+MpTdCall (
+ IN UINT64 Leaf,
+ IN UINT64 Arg1,
+ IN UINT64 Arg2,
+ IN UINT64 Arg3,
+ IN OUT VOID *Results
+ );
+
+/**
+ Whether Intel TDX is enabled.
+
+ @return TRUE TDX enabled
+ @return FALSE TDX not enabled
+**/
+BOOLEAN
+EFIAPI
+MpTdxIsEnabled (
+ VOID
+ );
+
+#endif
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index b9a06747edbf..7ccfde0d79f7 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -9,9 +9,11 @@
**/
#include "MpLib.h"
+#include "MpIntelTdx.h"
#include <Library/VmgExitLib.h>
#include <Register/Amd/Fam17Msr.h>
#include <Register/Amd/Ghcb.h>
+#include <ConfidentialComputingGuestAttr.h>
EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
@@ -1965,6 +1967,10 @@ MpInitLibInitialize (
UINTN BackupBufferAddr;
UINTN ApIdtBase;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return EFI_SUCCESS;
+ }
+
OldCpuMpData = GetCpuMpDataFromGuidedHob ();
if (OldCpuMpData == NULL) {
MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber);
@@ -2215,6 +2221,10 @@ MpInitLibGetProcessorInfo (
CPU_INFO_IN_HOB *CpuInfoInHob;
UINTN OriginalProcessorNumber;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return TdxMpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, HealthData);
+ }
+
CpuMpData = GetCpuMpData ();
CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
@@ -2446,6 +2456,10 @@ EnableDisableApWorker (
CPU_MP_DATA *CpuMpData;
UINTN CallerNumber;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return EFI_UNSUPPORTED;
+ }
+
CpuMpData = GetCpuMpData ();
//
@@ -2506,6 +2520,11 @@ MpInitLibWhoAmI (
return EFI_INVALID_PARAMETER;
}
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ *ProcessorNumber = 0;
+ return EFI_SUCCESS;
+ }
+
CpuMpData = GetCpuMpData ();
return GetProcessorNumber (CpuMpData, ProcessorNumber);
@@ -2544,6 +2563,10 @@ MpInitLibGetNumberOfProcessors (
UINTN EnabledProcessorNumber;
UINTN Index;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return TdxMpInitLibGetNumberOfProcessors(NumberOfProcessors, NumberOfEnabledProcessors);
+ }
+
CpuMpData = GetCpuMpData ();
if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) {
@@ -2629,6 +2652,10 @@ StartupAllCPUsWorker (
BOOLEAN HasEnabledAp;
CPU_STATE ApState;
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ return EFI_SUCCESS;
+ }
+
CpuMpData = GetCpuMpData ();
if (FailedCpuList != NULL) {
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLibTdx.c b/UefiCpuPkg/Library/MpInitLib/MpLibTdx.c
new file mode 100644
index 000000000000..fb9f6e3ee4b0
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/MpLibTdx.c
@@ -0,0 +1,126 @@
+/** @file
+ CPU MP Initialize Library common functions.
+
+ Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2020, AMD Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "MpLib.h"
+#include "MpIntelTdx.h"
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+#include <Library/DebugLib.h>
+#include <IndustryStandard/Tdx.h>
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+ @param[out] HealthData Return processor health data.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+TdxMpInitLibGetProcessorInfo (
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer,
+ OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ TD_RETURN_DATA TdReturnData;
+
+ if (ProcessorInfoBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = MpTdCall(TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ ASSERT(Status == EFI_SUCCESS);
+
+ if (ProcessorNumber >= TdReturnData.TdInfo.NumVcpus) {
+ return EFI_NOT_FOUND;
+ }
+
+ ProcessorInfoBuffer->ProcessorId = ProcessorNumber;
+ ProcessorInfoBuffer->StatusFlag = 0;
+ if (ProcessorNumber == 0) {
+ ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT;
+ }
+ ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT;
+
+ //
+ // Get processor location information
+ //
+ GetProcessorLocationByApicId (
+ (UINT32)ProcessorNumber,
+ &ProcessorInfoBuffer->Location.Package,
+ &ProcessorInfoBuffer->Location.Core,
+ &ProcessorInfoBuffer->Location.Thread
+ );
+
+ if (HealthData != NULL) {
+ HealthData->Uint32 = 0;
+ }
+
+ return Status;
+}
+
+/**
+ Retrieves the number of logical processor in the platform and the number of
+ those logical processors that are enabled on this boot. This service may only
+ be called from the BSP.
+
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors
+ is NULL.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+TdxMpInitLibGetNumberOfProcessors (
+ OUT UINTN *NumberOfProcessors, OPTIONAL
+ OUT UINTN *NumberOfEnabledProcessors OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ TD_RETURN_DATA TdReturnData;
+
+ if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = MpTdCall(TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ ASSERT(Status == EFI_SUCCESS);
+
+ if (NumberOfProcessors != NULL) {
+ *NumberOfProcessors = TdReturnData.TdInfo.NumVcpus;
+ }
+ if (NumberOfEnabledProcessors != NULL) {
+ *NumberOfEnabledProcessors = TdReturnData.TdInfo.MaxVcpus;
+ }
+
+ return Status;
+}
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLibTdxNull.c b/UefiCpuPkg/Library/MpInitLib/MpLibTdxNull.c
new file mode 100644
index 000000000000..f9cfedf01240
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/MpLibTdxNull.c
@@ -0,0 +1,117 @@
+/** @file
+ CPU MP Initialize Library common functions.
+
+ Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2020, AMD Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "MpLib.h"
+#include "MpIntelTdx.h"
+#include <Library/DebugLib.h>
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+ @param[out] HealthData Return processor health data.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+TdxMpInitLibGetProcessorInfo (
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer,
+ OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Retrieves the number of logical processor in the platform and the number of
+ those logical processors that are enabled on this boot. This service may only
+ be called from the BSP.
+
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors
+ is NULL.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+TdxMpInitLibGetNumberOfProcessors (
+ OUT UINTN *NumberOfProcessors, OPTIONAL
+ OUT UINTN *NumberOfEnabledProcessors OPTIONAL
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Whether Intel TDX is enabled.
+
+ @return TRUE TDX enabled
+ @return FALSE TDX not enabled
+**/
+BOOLEAN
+EFIAPI
+MpTdxIsEnabled (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+/**
+ The TDCALL instruction causes a VM exit to the Intel TDX module. It is
+ used to call guest-side Intel TDX functions, either local or a TD exit
+ to the host VMM, as selected by Leaf.
+ Leaf functions are described at <https://software.intel.com/content/
+ www/us/en/develop/articles/intel-trust-domain-extensions.html>
+
+ @param[in] Leaf Leaf number of TDCALL instruction
+ @param[in] Arg1 Arg1
+ @param[in] Arg2 Arg2
+ @param[in] Arg3 Arg3
+ @param[in,out] Results Returned result of the Leaf function
+
+ @return EFI_SUCCESS
+ @return Other See individual leaf functions
+**/
+EFI_STATUS
+EFIAPI
+MpTdCall (
+ IN UINT64 Leaf,
+ IN UINT64 Arg1,
+ IN UINT64 Arg2,
+ IN UINT64 Arg3,
+ IN OUT VOID *Results
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
index 36fcb96b5852..7c1a773f2bef 100644
--- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
@@ -22,10 +22,13 @@
#
[Sources.IA32]
+ MpLibTdxNull.c
Ia32/MpFuncs.nasm
[Sources.X64]
+ MpLibTdx.c
X64/MpFuncs.nasm
+ X64/IntelTdcall.nasm
[Sources.common]
MpEqu.inc
@@ -33,6 +36,7 @@
MpLib.c
MpLib.h
Microcode.c
+ MpIntelTdx.h
[Packages]
MdePkg/MdePkg.dec
@@ -65,6 +69,7 @@
gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr ## CONSUMES
[Ppis]
gEdkiiPeiShadowMicrocodePpiGuid ## SOMETIMES_CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm b/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
new file mode 100644
index 000000000000..5e98557d5590
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
@@ -0,0 +1,120 @@
+;------------------------------------------------------------------------------
+;*
+;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+;* SPDX-License-Identifier: BSD-2-Clause-Patent
+;*
+;*
+;------------------------------------------------------------------------------
+
+DEFAULT REL
+SECTION .text
+
+%macro tdcall 0
+ db 0x66,0x0f,0x01,0xcc
+%endmacro
+
+%macro tdcall_push_regs 0
+ push rbp
+ mov rbp, rsp
+ push r15
+ push r14
+ push r13
+ push r12
+ push rbx
+ push rsi
+ push rdi
+%endmacro
+
+%macro tdcall_pop_regs 0
+ pop rdi
+ pop rsi
+ pop rbx
+ pop r12
+ pop r13
+ pop r14
+ pop r15
+ pop rbp
+%endmacro
+
+%define number_of_regs_pushed 8
+%define number_of_parameters 4
+
+;
+; Keep these in sync for push_regs/pop_regs, code below
+; uses them to find 5th or greater parameters
+;
+%define first_variable_on_stack_offset \
+ ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8)
+%define second_variable_on_stack_offset \
+ ((first_variable_on_stack_offset) + 8)
+
+%macro tdcall_regs_preamble 2
+ mov rax, %1
+
+ mov ecx, %2
+
+ ; R10 = 0 (standard TDVMCALL)
+
+ xor r10d, r10d
+
+ ; Zero out unused (for standard TDVMCALL) registers to avoid leaking
+ ; secrets to the VMM.
+
+ xor ebx, ebx
+ xor esi, esi
+ xor edi, edi
+
+ xor edx, edx
+ xor ebp, ebp
+ xor r8d, r8d
+ xor r9d, r9d
+%endmacro
+
+%macro tdcall_regs_postamble 0
+ xor ebx, ebx
+ xor esi, esi
+ xor edi, edi
+
+ xor ecx, ecx
+ xor edx, edx
+ xor r8d, r8d
+ xor r9d, r9d
+ xor r10d, r10d
+ xor r11d, r11d
+%endmacro
+
+; MpTdCall (
+; UINT64 Leaf, // Rcx
+; UINT64 P1, // Rdx
+; UINT64 P2, // R8
+; UINT64 P3, // R9
+; UINT64 Results, // rsp + 0x28
+; )
+global ASM_PFX(MpTdCall)
+ASM_PFX(MpTdCall):
+ tdcall_push_regs
+
+ mov rax, rcx
+ mov rcx, rdx
+ mov rdx, r8
+ mov r8, r9
+
+ tdcall
+
+ ; exit if tdcall reports failure.
+ test rax, rax
+ jnz .exit
+
+ ; test if caller wanted results
+ mov r12, [rsp + first_variable_on_stack_offset ]
+ test r12, r12
+ jz .exit
+ mov [r12 + 0 ], rcx
+ mov [r12 + 8 ], rdx
+ mov [r12 + 16], r8
+ mov [r12 + 24], r9
+ mov [r12 + 32], r10
+ mov [r12 + 40], r11
+.exit:
+ tdcall_pop_regs
+ ret
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-01 13:16 ` [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib Min Xu
@ 2021-11-03 6:09 ` Gerd Hoffmann
2021-11-03 12:57 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 6:09 UTC (permalink / raw)
To: Min Xu
Cc: devel, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar
> +++ b/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
> @@ -0,0 +1,120 @@
> +;------------------------------------------------------------------------------
> +;*
> +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
> +;* SPDX-License-Identifier: BSD-2-Clause-Patent
> +;*
> +;*
> +;------------------------------------------------------------------------------
> +
> +DEFAULT REL
> +SECTION .text
> +
> +%macro tdcall 0
> + db 0x66,0x0f,0x01,0xcc
> +%endmacro
Hmm, could you just use TdxLib instead of bringing your own copy of the
assembler code?
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-03 6:09 ` Gerd Hoffmann
@ 2021-11-03 12:57 ` Min Xu
2021-11-04 8:10 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-03 12:57 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: devel@edk2.groups.io, Brijesh Singh, Erdem Aktas, James Bottomley,
Yao, Jiewen, Tom Lendacky, Dong, Eric, Ni, Ray, Kumar, Rahul1
On November 3, 2021 2:09 PM, Gerd Hoffmann wrote:
> > +++ b/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
> > @@ -0,0 +1,120 @@
> > +;--------------------------------------------------------------------
> > +----------
> > +;*
> > +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights
> > +reserved.<BR>
> > +;* SPDX-License-Identifier: BSD-2-Clause-Patent
> > +;*
> > +;*
> > +;--------------------------------------------------------------------
> > +----------
> > +
> > +DEFAULT REL
> > +SECTION .text
> > +
> > +%macro tdcall 0
> > + db 0x66,0x0f,0x01,0xcc
> > +%endmacro
>
> Hmm, could you just use TdxLib instead of bringing your own copy of the
> assembler code?
>
My initial thought was to include TdxLib in the .dsc as little as possible. For example, DxeMpInitLib is included in OvmfPkg/Microvm/MicrovmX64.dsc. If TdxLib is used by DxeMpInitLib, then it has to be included in MicrovmX64.dsc as well.
So I copy the assemble code in MpInitLib.
But now VmgExitLib is extended to handle #VE exception ( TdxLib must be used ), so TdxLib has to be included in the .dsc where VmgExitLib is included.
I will update the MpInitLib to use TdxLib in the next version.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-03 12:57 ` Min Xu
@ 2021-11-04 8:10 ` Gerd Hoffmann
2021-11-04 15:21 ` Lendacky, Thomas
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-04 8:10 UTC (permalink / raw)
To: Xu, Min M
Cc: devel@edk2.groups.io, Brijesh Singh, Erdem Aktas, James Bottomley,
Yao, Jiewen, Tom Lendacky, Dong, Eric, Ni, Ray, Kumar, Rahul1
On Wed, Nov 03, 2021 at 12:57:37PM +0000, Xu, Min M wrote:
> On November 3, 2021 2:09 PM, Gerd Hoffmann wrote:
> > > +++ b/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
> > > @@ -0,0 +1,120 @@
> > > +;--------------------------------------------------------------------
> > > +----------
> > > +;*
> > > +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights
> > > +reserved.<BR>
> > > +;* SPDX-License-Identifier: BSD-2-Clause-Patent
> > > +;*
> > > +;*
> > > +;--------------------------------------------------------------------
> > > +----------
> > > +
> > > +DEFAULT REL
> > > +SECTION .text
> > > +
> > > +%macro tdcall 0
> > > + db 0x66,0x0f,0x01,0xcc
> > > +%endmacro
> >
> > Hmm, could you just use TdxLib instead of bringing your own copy of the
> > assembler code?
> My initial thought was to include TdxLib in the .dsc as little as
> possible. For example, DxeMpInitLib is included in
> OvmfPkg/Microvm/MicrovmX64.dsc. If TdxLib is used by DxeMpInitLib,
> then it has to be included in MicrovmX64.dsc as well.
Hmm, yes. Adding a TdxLib dependency has its downsides indeed.
> So I copy the assemble code in MpInitLib.
The problem with copying code is that long-term maintenance becomes
harder. When a bug is found you have to find and fix all the copies of
that code. That's why I strongly prefer to avoid code copy&paste.
Sometimes there is no easy way around creating a copy though.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-04 8:10 ` Gerd Hoffmann
@ 2021-11-04 15:21 ` Lendacky, Thomas
2021-11-04 23:24 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Lendacky, Thomas @ 2021-11-04 15:21 UTC (permalink / raw)
To: Gerd Hoffmann, Xu, Min M
Cc: devel@edk2.groups.io, Brijesh Singh, Erdem Aktas, James Bottomley,
Yao, Jiewen, Dong, Eric, Ni, Ray, Kumar, Rahul1
On 11/4/21 3:10 AM, Gerd Hoffmann wrote:
> On Wed, Nov 03, 2021 at 12:57:37PM +0000, Xu, Min M wrote:
>> On November 3, 2021 2:09 PM, Gerd Hoffmann wrote:
>>>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
>>>> @@ -0,0 +1,120 @@
>>>> +;--------------------------------------------------------------------
>>>> +----------
>>>> +;*
>>>> +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights
>>>> +reserved.<BR>
>>>> +;* SPDX-License-Identifier: BSD-2-Clause-Patent
>>>> +;*
>>>> +;*
>>>> +;--------------------------------------------------------------------
>>>> +----------
>>>> +
>>>> +DEFAULT REL
>>>> +SECTION .text
>>>> +
>>>> +%macro tdcall 0
>>>> + db 0x66,0x0f,0x01,0xcc
>>>> +%endmacro
>>>
>>> Hmm, could you just use TdxLib instead of bringing your own copy of the
>>> assembler code?
>
>> My initial thought was to include TdxLib in the .dsc as little as
>> possible. For example, DxeMpInitLib is included in
>> OvmfPkg/Microvm/MicrovmX64.dsc. If TdxLib is used by DxeMpInitLib,
>> then it has to be included in MicrovmX64.dsc as well.
>
> Hmm, yes. Adding a TdxLib dependency has its downsides indeed.
>
>> So I copy the assemble code in MpInitLib.
>
> The problem with copying code is that long-term maintenance becomes
> harder. When a bug is found you have to find and fix all the copies of
> that code. That's why I strongly prefer to avoid code copy&paste.
> Sometimes there is no easy way around creating a copy though.
Can't you create something in MdePkg/Library/Baselib and then use it
everywhere it's needed?
Thanks,
Tom
>
> take care,
> Gerd
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-04 15:21 ` Lendacky, Thomas
@ 2021-11-04 23:24 ` Min Xu
2021-11-05 6:46 ` [edk2-devel] " Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-04 23:24 UTC (permalink / raw)
To: Tom Lendacky, Gerd Hoffmann
Cc: devel@edk2.groups.io, Brijesh Singh, Erdem Aktas, James Bottomley,
Yao, Jiewen, Dong, Eric, Ni, Ray, Kumar, Rahul1
On November 4, 2021 11:21 PM, Tom Lendacky wrote:
> On 11/4/21 3:10 AM, Gerd Hoffmann wrote:
> > On Wed, Nov 03, 2021 at 12:57:37PM +0000, Xu, Min M wrote:
> >> On November 3, 2021 2:09 PM, Gerd Hoffmann wrote:
> >>>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/IntelTdcall.nasm
> >>>> @@ -0,0 +1,120 @@
> >>>> +;-----------------------------------------------------------------
> >>>> +---
> >>>> +----------
> >>>> +;*
> >>>> +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights
> >>>> +reserved.<BR>
> >>>> +;* SPDX-License-Identifier: BSD-2-Clause-Patent
> >>>> +;*
> >>>> +;*
> >>>> +;-----------------------------------------------------------------
> >>>> +---
> >>>> +----------
> >>>> +
> >>>> +DEFAULT REL
> >>>> +SECTION .text
> >>>> +
> >>>> +%macro tdcall 0
> >>>> + db 0x66,0x0f,0x01,0xcc
> >>>> +%endmacro
> >>>
> >>> Hmm, could you just use TdxLib instead of bringing your own copy of
> >>> the assembler code?
> >
> >> My initial thought was to include TdxLib in the .dsc as little as
> >> possible. For example, DxeMpInitLib is included in
> >> OvmfPkg/Microvm/MicrovmX64.dsc. If TdxLib is used by DxeMpInitLib,
> >> then it has to be included in MicrovmX64.dsc as well.
> >
> > Hmm, yes. Adding a TdxLib dependency has its downsides indeed.
> >
> >> So I copy the assemble code in MpInitLib.
> >
> > The problem with copying code is that long-term maintenance becomes
> > harder. When a bug is found you have to find and fix all the copies
> > of that code. That's why I strongly prefer to avoid code copy&paste.
> > Sometimes there is no easy way around creating a copy though.
>
> Can't you create something in MdePkg/Library/Baselib and then use it
> everywhere it's needed?
>
Do you mean put the basic Tdx functions in MdePkg/Library/BaseLib? If that is the case, then I would add below basic Tdx functions in BaseLib:
- TdIsEnabled ()
- TdCall ()
- TdVmCall ()
Gerd, what's your thought?
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-04 23:24 ` Min Xu
@ 2021-11-05 6:46 ` Gerd Hoffmann
2021-11-05 6:53 ` Min Xu
2021-11-09 2:44 ` Min Xu
0 siblings, 2 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-05 6:46 UTC (permalink / raw)
To: devel, min.m.xu
Cc: Tom Lendacky, Brijesh Singh, Erdem Aktas, James Bottomley,
Yao, Jiewen, Dong, Eric, Ni, Ray, Kumar, Rahul1
> > Can't you create something in MdePkg/Library/Baselib and then use it
> > everywhere it's needed?
> >
> Do you mean put the basic Tdx functions in MdePkg/Library/BaseLib? If that is the case, then I would add below basic Tdx functions in BaseLib:
> - TdIsEnabled ()
> - TdCall ()
> - TdVmCall ()
That is the cpuid-based check and the asm code for td calls, correct?
> Gerd, what's your thought?
Looks fine to me.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-05 6:46 ` [edk2-devel] " Gerd Hoffmann
@ 2021-11-05 6:53 ` Min Xu
2021-11-09 2:44 ` Min Xu
1 sibling, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-05 6:53 UTC (permalink / raw)
To: Gerd Hoffmann, devel@edk2.groups.io
Cc: Tom Lendacky, Brijesh Singh, Erdem Aktas, James Bottomley,
Yao, Jiewen, Dong, Eric, Ni, Ray, Kumar, Rahul1
On November 5, 2021 2:46 PM, Gerd Hoffmann wrote:
> > > Can't you create something in MdePkg/Library/Baselib and then use it
> > > everywhere it's needed?
> > >
> > Do you mean put the basic Tdx functions in MdePkg/Library/BaseLib? If that is
> the case, then I would add below basic Tdx functions in BaseLib:
> > - TdIsEnabled ()
> > - TdCall ()
> > - TdVmCall ()
>
> That is the cpuid-based check and the asm code for td calls, correct?
TdIsEnabled() is the cpuid-based check.
TdCall/TdVmCall are the asm code.
>
> > Gerd, what's your thought?
>
> Looks fine to me.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib
2021-11-05 6:46 ` [edk2-devel] " Gerd Hoffmann
2021-11-05 6:53 ` Min Xu
@ 2021-11-09 2:44 ` Min Xu
1 sibling, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-09 2:44 UTC (permalink / raw)
To: Liming Gao, Kinney, Michael D
Cc: Tom Lendacky, Brijesh Singh, Erdem Aktas, James Bottomley,
Yao, Jiewen, Dong, Eric, Ni, Ray, Kumar, Rahul1, Gerd Hoffmann,
devel@edk2.groups.io
On November 5, 2021 2:46 PM, Gerd Hoffmann wrote:
> > > Can't you create something in MdePkg/Library/Baselib and then use it
> > > everywhere it's needed?
> > >
> > Do you mean put the basic Tdx functions in MdePkg/Library/BaseLib? If that is
> the case, then I would add below basic Tdx functions in BaseLib:
> > - TdIsEnabled ()
> > - TdCall ()
> > - TdVmCall ()
>
> That is the cpuid-based check and the asm code for td calls, correct?
>
> > Gerd, what's your thought?
>
> Looks fine to me.
>
Hi, Liming & Michael
You're the MdePkg maintainers. What's your thoughts about adding these Tdx functions in MdePkg/Library/BaseLib?
- TdIsEnabled () <-- cpuid-based check if it is a TD guest
- TdCall () <-- TD call
- TdVmCall () <-- TD vmcall
https://github.com/mxu9/edk2/blob/tdvf_wave2.v3/MdePkg/Library/TdxLib/X64/Tdcall.nasm
https://github.com/mxu9/edk2/blob/tdvf_wave2.v3/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (13 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 6:30 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 16/29] OvmfPkg: Add IntelTdx.h in OvmfPkg/Include/IndustryStandard Min Xu
` (13 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In TDX BSP and APs goes to the same entry point in SecEntry.nasm.
BSP initialize the temporary stack and then jumps to SecMain, just as
legacy Ovmf does.
APs spin in a modified mailbox loop using initial mailbox structure.
Its structure defition is in OvmfPkg/Include/IndustryStandard/IntelTdx.h.
APs wait for command to see if the command is for me. If so execute the
command.
There are 2 commands are supported:
- WakeUp:
BSP issues this command to move APs to final OS spinloop and Mailbox
in reserved memory.
- AcceptPages:
To mitigate the performance impact of accepting pages in SEC phase on
BSP, BSP will parse memory resources and assign each AP the task of
accepting a subset of pages. This command may be called several times
until all memory resources are processed. In accepting pages, PageLevel
may fall back to smaller one if SIZE_MISMATCH error is returned.
TdxCommondefs.inc is added which includes the common definitions used by
the APs in SecEntry.nasm.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/Include/TdxCommondefs.inc | 51 +++++
OvmfPkg/Sec/SecMain.inf | 1 +
OvmfPkg/Sec/X64/SecEntry.nasm | 314 ++++++++++++++++++++++++++++++
3 files changed, 366 insertions(+)
create mode 100644 OvmfPkg/Include/TdxCommondefs.inc
diff --git a/OvmfPkg/Include/TdxCommondefs.inc b/OvmfPkg/Include/TdxCommondefs.inc
new file mode 100644
index 000000000000..970eac96592a
--- /dev/null
+++ b/OvmfPkg/Include/TdxCommondefs.inc
@@ -0,0 +1,51 @@
+;------------------------------------------------------------------------------
+; @file
+; TDX Common defitions used by the APs in mailbox
+;
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+CommandOffset equ 00h
+ApicidOffset equ 04h
+WakeupVectorOffset equ 08h
+OSArgsOffset equ 10h
+FirmwareArgsOffset equ 800h
+WakeupArgsRelocatedMailBox equ 800h
+AcceptPageArgsPhysicalStart equ 800h
+AcceptPageArgsPhysicalEnd equ 808h
+AcceptPageArgsChunkSize equ 810h
+AcceptPageArgsPageSize equ 818h
+CpuArrivalOffset equ 900h
+CpusExitingOffset equ 0a00h
+TalliesOffset equ 0a08h
+ErrorsOffset equ 0e08h
+
+SIZE_4KB equ 1000h
+SIZE_2MB equ 200000h
+SIZE_1GB equ 40000000h
+
+PAGE_ACCEPT_LEVEL_4K equ 0
+PAGE_ACCEPT_LEVEL_2M equ 1
+PAGE_ACCEPT_LEVEL_1G equ 2
+
+TDX_PAGE_ALREADY_ACCEPTED equ 0x00000b0a
+TDX_PAGE_SIZE_MISMATCH equ 0xc0000b0b
+
+; Errors of APs in Mailbox
+ERROR_NON equ 0
+ERROR_INVALID_ACCEPT_PAGE_SIZE equ 1
+ERROR_ACCEPT_PAGE_ERROR equ 2
+ERROR_INVALID_FALLBACK_PAGE_LEVEL equ 3
+
+MpProtectedModeWakeupCommandNoop equ 0
+MpProtectedModeWakeupCommandWakeup equ 1
+MpProtectedModeWakeupCommandSleep equ 2
+MpProtectedModeWakeupCommandAcceptPages equ 3
+
+MailboxApicIdInvalid equ 0xffffffff
+MailboxApicidBroadcast equ 0xfffffffe
+
+%define TDCALL_TDINFO 0x1
+%define TDCALL_TDACCEPTPAGE 0x6
diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf
index ea4b9611f52d..6083fa21a433 100644
--- a/OvmfPkg/Sec/SecMain.inf
+++ b/OvmfPkg/Sec/SecMain.inf
@@ -72,6 +72,7 @@
gEfiMdeModulePkgTokenSpaceGuid.PcdInitValueInTempStack
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfConfidentialComputingWorkAreaHeader
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
[FeaturePcd]
gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
diff --git a/OvmfPkg/Sec/X64/SecEntry.nasm b/OvmfPkg/Sec/X64/SecEntry.nasm
index 1cc680a70716..d0833db68410 100644
--- a/OvmfPkg/Sec/X64/SecEntry.nasm
+++ b/OvmfPkg/Sec/X64/SecEntry.nasm
@@ -10,12 +10,17 @@
;------------------------------------------------------------------------------
#include <Base.h>
+%include "TdxCommondefs.inc"
DEFAULT REL
SECTION .text
extern ASM_PFX(SecCoreStartupWithStack)
+%macro tdcall 0
+ db 0x66, 0x0f, 0x01, 0xcc
+%endmacro
+
;
; SecCore Entry Point
;
@@ -35,6 +40,32 @@ extern ASM_PFX(SecCoreStartupWithStack)
global ASM_PFX(_ModuleEntryPoint)
ASM_PFX(_ModuleEntryPoint):
+ ;
+ ; Guest type is stored in OVMF_WORK_AREA
+ ;
+ %define OVMF_WORK_AREA FixedPcdGet32 (PcdOvmfWorkAreaBase)
+ %define VM_GUEST_TYPE_TDX 2
+ mov eax, OVMF_WORK_AREA
+ cmp byte[eax], VM_GUEST_TYPE_TDX
+ jne InitStack
+
+ mov rax, TDCALL_TDINFO
+ tdcall
+
+ ;
+ ; R8 [31:0] NUM_VCPUS
+ ; [63:32] MAX_VCPUS
+ ; R9 [31:0] VCPU_INDEX
+ ; Td Guest set the VCPU0 as the BSP, others are the APs
+ ; APs jump to spinloop and get released by DXE's MpInitLib
+ ;
+ mov rax, r9
+ and rax, 0xffff
+ test rax, rax
+ jne ParkAp
+
+InitStack:
+
;
; Fill the temporary RAM with the initial stack value.
; The loop below will seed the heap as well, but that's harmless.
@@ -67,3 +98,286 @@ ASM_PFX(_ModuleEntryPoint):
sub rsp, 0x20
call ASM_PFX(SecCoreStartupWithStack)
+ ;
+ ; Note: BSP never gets here. APs will be unblocked by DXE
+ ;
+ ; R8 [31:0] NUM_VCPUS
+ ; [63:32] MAX_VCPUS
+ ; R9 [31:0] VCPU_INDEX
+ ;
+ParkAp:
+
+ mov rbp, r9
+
+.do_wait_loop:
+ mov rsp, FixedPcdGet32 (PcdOvmfSecGhcbBackupBase)
+
+ ;
+ ; register itself in [rsp + CpuArrivalOffset]
+ ;
+ mov rax, 1
+ lock xadd dword [rsp + CpuArrivalOffset], eax
+ inc eax
+
+.check_arrival_cnt:
+ cmp eax, r8d
+ je .check_command
+ mov eax, dword[rsp + CpuArrivalOffset]
+ jmp .check_arrival_cnt
+
+.check_command:
+ mov eax, dword[rsp + CommandOffset]
+ cmp eax, MpProtectedModeWakeupCommandNoop
+ je .check_command
+
+ cmp eax, MpProtectedModeWakeupCommandWakeup
+ je .do_wakeup
+
+ cmp eax, MpProtectedModeWakeupCommandAcceptPages
+ jne .check_command
+
+ ;
+ ; AP Accept Pages
+ ;
+ ; Accept Pages in TDX is time-consuming, especially for big memory.
+ ; One of the mitigation is to accept pages by BSP and APs parallely.
+ ;
+ ; For example, there are 4 CPUs (1 BSP and 3 APs). Totally there are
+ ; 1G memory to be accepted.
+ ;
+ ; BSP is responsible for the memory regions of:
+ ; Start : StartAddress + ChunkSize * (4) * Index
+ ; Length: ChunkSize
+ ; APs is reponsible for the memory regions of:
+ ; Start : StartAddress + ChunkSize * (4) * Index + ChunkSize * CpuId
+ ; Length: ChunkSize
+ ;
+ ; TDCALL_TDACCEPTPAGE supports the PageSize of 4K and 2M. Sometimes when
+ ; the PageSize is 2M, TDX_PAGE_SIZE_MISMATCH is returned as the error code.
+ ; In this case, TDVF need fall back to 4k PageSize to accept again.
+ ;
+ ; If any errors happened in accept pages, an error code is recorded in
+ ; Mailbox [ErrorsOffset + CpuIndex]
+ ;
+.ap_accept_page:
+
+ ;
+ ; Clear the errors and fallback flag
+ ;
+ mov al, ERROR_NON
+ mov byte[rsp + ErrorsOffset + rbp], al
+ xor r12, r12
+
+ ;
+ ; Get PhysicalAddress/ChunkSize/PageSize
+ ;
+ mov rcx, [rsp + AcceptPageArgsPhysicalStart]
+ mov rbx, [rsp + AcceptPageArgsChunkSize]
+
+ ;
+ ; Set AcceptPageLevel based on the AcceptPagesize
+ ; Currently only 2M/4K page size is acceptable
+ ;
+ mov r15, [rsp + AcceptPageArgsPageSize]
+ cmp r15, SIZE_4KB
+ je .set_4kb
+ cmp r15, SIZE_2MB
+ je .set_2mb
+
+ mov al, ERROR_INVALID_ACCEPT_PAGE_SIZE
+ mov byte[rsp + ErrorsOffset + rbp], al
+ jmp .do_finish_command
+
+.set_4kb:
+ mov r15, PAGE_ACCEPT_LEVEL_4K
+ jmp .physical_address
+
+.set_2mb:
+ mov r15, PAGE_ACCEPT_LEVEL_2M
+
+.physical_address:
+ ;
+ ; PhysicalAddress += (CpuId * ChunkSize)
+ ;
+ xor rdx, rdx
+ mov eax, ebp
+ mul ebx
+ add rcx, rax
+ shl rdx, 32
+ add rcx, rdx
+
+.do_accept_next_range:
+ ;
+ ; Make sure we don't accept page beyond ending page
+ ; This could happen is ChunkSize crosses the end of region
+ ;
+ cmp rcx, [rsp + AcceptPageArgsPhysicalEnd ]
+ jge .do_finish_command
+
+ ;
+ ; Save starting address for this region
+ ;
+ mov r11, rcx
+
+ ;
+ ; Size = MIN(ChunkSize, PhysicalEnd - PhysicalAddress);
+ ;
+ mov rax, [rsp + AcceptPageArgsPhysicalEnd]
+ sub rax, rcx
+ cmp rax, rbx
+ jge .do_accept_loop
+ mov rbx, rax
+
+.do_accept_loop:
+ ;
+ ; RCX: Accept address
+ ; R15: Accept Page Level
+ ; R12: Flag of fall back accept
+ ;
+ mov rax, TDCALL_TDACCEPTPAGE
+ xor rdx, rdx
+ or rcx, r15
+
+ tdcall
+
+ ;
+ ; Check status code in RAX
+ ;
+ test rax, rax
+ jz .accept_success
+
+ shr rax, 32
+ cmp eax, TDX_PAGE_ALREADY_ACCEPTED
+ jz .already_accepted
+
+ cmp eax, TDX_PAGE_SIZE_MISMATCH
+ jz .accept_size_mismatch
+
+ ;
+ ; other error
+ ;
+ mov al, ERROR_ACCEPT_PAGE_ERROR
+ mov byte[rsp + ErrorsOffset + rbp], al
+ jmp .do_finish_command
+
+.accept_size_mismatch:
+ ;
+ ; Check the current PageLevel.
+ ; ACCEPT_LEVEL_4K is the least level and cannot fall back any more.
+ ; If in this case, just record the error and return
+ ;
+ cmp r15, PAGE_ACCEPT_LEVEL_4K
+ jne .do_fallback_accept
+ mov al, ERROR_INVALID_FALLBACK_PAGE_LEVEL
+ mov byte[rsp + ErrorsOffset + rbp], al
+ jmp .do_finish_command
+
+.do_fallback_accept:
+ ;
+ ; In fall back accept, just loop 512 times (2M = 512 * 4K)
+ ; Save the rcx in r13.
+ ; Decrease the PageLevel in R15.
+ ; R12 indicates it is in a fall back accept loop.
+ ;
+ mov r14, 512
+ and rcx, ~0x3ULL
+ mov r13, rcx
+ xor rdx, rdx
+ dec r15
+ mov r12, 1
+
+ jmp .do_accept_loop
+
+.accept_success:
+ ;
+ ; Keep track of how many accepts per cpu
+ ;
+ inc dword[rsp + TalliesOffset + rbp * 4]
+
+ ;
+ ; R12 indicate whether it is a fall back accept
+ ; If it is a success of fall back accept
+ ; Just loop 512 times to .do_accept_loop
+ ;
+ test r12, r12
+ jz .normal_accept_success
+
+ ;
+ ; This is fallback accept success
+ ;
+ add rcx, SIZE_4KB
+ dec r14
+ test r14, r14
+ jz .fallback_accept_done
+ jmp .do_accept_loop
+
+.fallback_accept_done:
+ ;
+ ; Fall back accept done.
+ ; Restore the start address to RCX from R13
+ ; Clear the fall back accept flag
+ ;
+ mov rcx, r13
+ inc r15
+ xor r12, r12
+
+.already_accepted:
+ ;
+ ; Handle the sitution of fall back accpet
+ ;
+ test r12, r12
+ jnz .accept_success
+
+.normal_accept_success:
+ ;
+ ; Reduce accept size by a PageSize, and increment address
+ ;
+ mov r12, [rsp + AcceptPageArgsPageSize]
+ sub rbx, r12
+ add rcx, r12
+ xor r12, r12
+
+ ;
+ ; We may be given multiple pages to accept, make sure we
+ ; aren't done
+ ;
+ test rbx, rbx
+ jne .do_accept_loop
+
+ ;
+ ; Restore address before, and then increment by stride (num-cpus * ChunkSize)
+ ;
+ xor rdx, rdx
+ mov rcx, r11
+ mov eax, r8d
+ mov ebx, [rsp + AcceptPageArgsChunkSize]
+ mul ebx
+ add rcx, rax
+ shl rdx, 32
+ add rcx, rdx
+ jmp .do_accept_next_range
+
+.do_finish_command:
+ mov eax, 0FFFFFFFFh
+ lock xadd dword [rsp + CpusExitingOffset], eax
+ dec eax
+
+.check_exiting_cnt:
+ cmp eax, 0
+ je .do_wait_loop
+ mov eax, dword[rsp + CpusExitingOffset]
+ jmp .check_exiting_cnt
+
+.do_wakeup:
+ ;
+ ; BSP sets these variables before unblocking APs
+ ; RAX: WakeupVectorOffset
+ ; RBX: Relocated mailbox address
+ ; RBP: vCpuId
+ ;
+ mov rax, 0
+ mov eax, dword[rsp + WakeupVectorOffset]
+ mov rbx, [rsp + WakeupArgsRelocatedMailBox]
+ nop
+ jmp rax
+ jmp $
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-01 13:16 ` [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx Min Xu
@ 2021-11-03 6:30 ` Gerd Hoffmann
2021-11-16 12:11 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 6:30 UTC (permalink / raw)
To: Min Xu
Cc: devel, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
Hi,
> - AcceptPages:
> To mitigate the performance impact of accepting pages in SEC phase on
> BSP, BSP will parse memory resources and assign each AP the task of
> accepting a subset of pages. This command may be called several times
> until all memory resources are processed. In accepting pages, PageLevel
> may fall back to smaller one if SIZE_MISMATCH error is returned.
Why add an assembler version of this? There already is a C version (in
TdxLib, patch #2). When adding lazy accept at some point in the future
we will stop accepting all pages in the SEC phase anyway. There is Mp
support (patch #14) so you can distribute the load to all CPUs in PEI /
DXE phase if you want (although the benefits of parallel accept will be
much smaller once lazy accept is there).
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-03 6:30 ` Gerd Hoffmann
@ 2021-11-16 12:11 ` Min Xu
2021-11-17 15:19 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-16 12:11 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Yao, Jiewen,
Tom Lendacky, Xu, Min M
On November 3, 2021 2:31 PM, Gerd Hoffmann wrote:
> Hi,
>
> > - AcceptPages:
> > To mitigate the performance impact of accepting pages in SEC phase on
> > BSP, BSP will parse memory resources and assign each AP the task of
> > accepting a subset of pages. This command may be called several times
> > until all memory resources are processed. In accepting pages, PageLevel
> > may fall back to smaller one if SIZE_MISMATCH error is returned.
>
> Why add an assembler version of this? There already is a C version (in TdxLib,
> patch #2). When adding lazy accept at some point in the future we will stop
> accepting all pages in the SEC phase anyway. There is Mp support (patch #14)
> so you can distribute the load to all CPUs in PEI / DXE phase if you want
> (although the benefits of parallel accept will be much smaller once lazy
> accept is there).
There are below considerations about accept pages in SEC phase.
1. There is a minimal memory requirement in DxeCore [1]. In legacy Ovmf the memory is initialized in PEI phase.
But TDVF has 2 configurations (Config-A and Config-B) [2]. PEI phase is skipped in Config-B. So we have to accept memories in SEC phase. This is to make the code consistent in Config-A and Config-B.
2. TDVF's MP support is via Mailbox [3] . BSP wakes up APs by putting command/parameters in Mailbox. So we have this patch [4].
While EDK2's MP support (CpuMpPei/CpuDxe) is via INIT-SIPI-SIPI. They're different. We cannot distribute the load to all CPUs with EDK2's MP service.
Patch #14 [5] is the commit to enable TDX in MpInitLib. Actually this is to make sure the CpuMpPei/CpuDxe will not break in Td guest in run-time. Patch #14 is rather simple, for example, ApWorker is not supported.
3. In current TDVF implementation, Stack is not set for APs. So C functions cannot be called for APs. If stack is set for APs, then more memories should be reserved in MEMFD. For example, if 1 AP needs 4k size stack, then 15 AP need 60k memory. (We assume only 1+15 CPUs accept memory). This makes things complicated.
Based on above considerations, we use the current design that BSP-APs accept memory in SEC phase (in assembly code).
[1] https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Dxe/Gcd/Gcd.c#L2245
[2] https://edk2.groups.io/g/devel/message/76367
[3] https://www.intel.com/content/dam/develop/external/us/en/documents/intel-tdx-guest-hypervisor-communication-interface-1.0-344426-002.pdf Section 4.1
[4] https://github.com/mxu9/edk2/commit/4501df794c2c4dbfc5ba63c93a1e2c96660c072e
[5] https://github.com/mxu9/edk2/commit/189ad46fb732460eaa3e3f0787f098466b1504a6
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-16 12:11 ` Min Xu
@ 2021-11-17 15:19 ` Gerd Hoffmann
2021-11-18 9:59 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-17 15:19 UTC (permalink / raw)
To: Xu, Min M
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Yao, Jiewen,
Tom Lendacky
On Tue, Nov 16, 2021 at 12:11:33PM +0000, Xu, Min M wrote:
> On November 3, 2021 2:31 PM, Gerd Hoffmann wrote:
> > Hi,
> >
> > > - AcceptPages:
> > > To mitigate the performance impact of accepting pages in SEC phase on
> > > BSP, BSP will parse memory resources and assign each AP the task of
> > > accepting a subset of pages. This command may be called several times
> > > until all memory resources are processed. In accepting pages, PageLevel
> > > may fall back to smaller one if SIZE_MISMATCH error is returned.
> >
> > Why add an assembler version of this? There already is a C version (in TdxLib,
> > patch #2). When adding lazy accept at some point in the future we will stop
> > accepting all pages in the SEC phase anyway. There is Mp support (patch #14)
> > so you can distribute the load to all CPUs in PEI / DXE phase if you want
> > (although the benefits of parallel accept will be much smaller once lazy
> > accept is there).
> There are below considerations about accept pages in SEC phase.
>
> 1. There is a minimal memory requirement in DxeCore [1]. In legacy
> Ovmf the memory is initialized in PEI phase.
Yes.
> But TDVF has 2 configurations (Config-A and Config-B) [2]. PEI phase
> is skipped in Config-B. So we have to accept memories in SEC phase.
I'm sure I've asked this before: Why skip the PEI phase? So far
I have not seen any convincing argument for it.
Jiewen argued this is a simplification. Which is not completely wrong,
but it's also only half the truth. Switching all OVMF builds over to
PEI-less boot doesn't work because some features supported by OVMF
depend on PEI Modules. Therefore TDX Config-B skipping the PEI phase
means we would have to maintain two boot work flows (with and without
PEI phase) for OVMF. Which in turn would imply more work for
maintenance, testing and so on.
Also note that accepting all memory in SEC phase would be temporary
only. Once we have support for lazy accept in OVMF we will accept most
memory in DXE phase (or leave it to the linux kernel), whereas SEC/PEI
will accept only a small fraction of the memory. Just enough to allow
DXE phase initialize memory management & lazy accept support.
> This is to make the code consistent in Config-A and Config-B.
I want TDVF be consistent with the rest of OVMF. Makes long-term
maintenance easier. Building a single binary for both SEV and TDX with
full confidential computing support (including config-b features) will
be easier too.
> 2. TDVF's MP support is via Mailbox [3] . BSP wakes up APs by putting
> command/parameters in Mailbox. So we have this patch [4]. While
> EDK2's MP support (CpuMpPei/CpuDxe) is via INIT-SIPI-SIPI. They're
> different. We cannot distribute the load to all CPUs with EDK2's MP
> service.
> Patch #14 [5] is the commit to enable TDX in MpInitLib. Actually this
> is to make sure the CpuMpPei/CpuDxe will not break in Td guest in
> run-time. Patch #14 is rather simple, for example, ApWorker is not
> supported.
Well, MpInitLib seems to have full support (including ApWorker) for SEV.
I'd expect you can add TDX support too, and schedule the jobs you want
run on the APs via TDX mailbox instead of using IPIs.
And I think to support parallel lazy accept in the DXE phase (for lazy
accept) you will need proper MpInitLib support anyway.
> 3. In current TDVF implementation, Stack is not set for APs. So C
> functions cannot be called for APs. If stack is set for APs, then more
> memories should be reserved in MEMFD. For example, if 1 AP needs 4k
> size stack, then 15 AP need 60k memory. (We assume only 1+15 CPUs
> accept memory). This makes things complicated.
I think skipping PEI phase and moving stuff to SEC phase makes things
complicated. Reserving stacks in MEMFD would only be needed because you
can't allocate memory in the SEC phase. When you initialize the APs in
PEI instead you can just allocate memory and the MEMFD dependency goes
away.
> Based on above considerations, we use the current design that BSP-APs
> accept memory in SEC phase (in assembly code).
I think the design doesn't make much sense for the reasons outlined
above.
I'd suggest to put aside parallel accept for now and just let the BSP
accept the memory for initial TDX support. Focus on adding lazy accept
support next. Finally re-evaluate parallel accept support, *after*
merging lazy accept.
With a major change in the memory acceptance workflow being planned it
doesn't look like a good idea to me to tune memory accept performance
*now*.
My naive expectation would be that parallel accept in SEC/PEI simply
isn't worth the effort due to the small amount of memory being needed.
Parallel accept in DXE probably is useful, but how much it speeds up
boot depends on how much accepted memory we must hand over to the linux
kernel so it has enough room to successfully initialize memory
management & lazy accept.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-17 15:19 ` Gerd Hoffmann
@ 2021-11-18 9:59 ` Yao, Jiewen
2021-11-19 15:11 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-18 9:59 UTC (permalink / raw)
To: Gerd Hoffmann, Xu, Min M
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Tom Lendacky
Comment on config-B.
> -----Original Message-----
> From: Gerd Hoffmann <kraxel@redhat.com>
> Sent: Wednesday, November 17, 2021 11:20 PM
> To: Xu, Min M <min.m.xu@intel.com>
> Cc: devel@edk2.groups.io; Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>;
> Erdem Aktas <erdemaktas@google.com>; James Bottomley
> <jejb@linux.ibm.com>; Yao, Jiewen <jiewen.yao@intel.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> Subject: Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
>
> On Tue, Nov 16, 2021 at 12:11:33PM +0000, Xu, Min M wrote:
> > On November 3, 2021 2:31 PM, Gerd Hoffmann wrote:
> > > Hi,
> > >
> > > > - AcceptPages:
> > > > To mitigate the performance impact of accepting pages in SEC phase on
> > > > BSP, BSP will parse memory resources and assign each AP the task of
> > > > accepting a subset of pages. This command may be called several times
> > > > until all memory resources are processed. In accepting pages, PageLevel
> > > > may fall back to smaller one if SIZE_MISMATCH error is returned.
> > >
> > > Why add an assembler version of this? There already is a C version (in TdxLib,
> > > patch #2). When adding lazy accept at some point in the future we will stop
> > > accepting all pages in the SEC phase anyway. There is Mp support (patch #14)
> > > so you can distribute the load to all CPUs in PEI / DXE phase if you want
> > > (although the benefits of parallel accept will be much smaller once lazy
> > > accept is there).
> > There are below considerations about accept pages in SEC phase.
> >
> > 1. There is a minimal memory requirement in DxeCore [1]. In legacy
> > Ovmf the memory is initialized in PEI phase.
>
> Yes.
>
> > But TDVF has 2 configurations (Config-A and Config-B) [2]. PEI phase
> > is skipped in Config-B. So we have to accept memories in SEC phase.
>
> I'm sure I've asked this before: Why skip the PEI phase? So far
> I have not seen any convincing argument for it.
[Jiewen]
First, keeping or skipping PEI phase is a platform decision, not an architecture decision.
Skipping PEI phase is valid architecture design.
In EDKII, most platforms choose to include PEI.
And we also have multiple platforms skipping PEI phase. For example:
https://github.com/tianocore/edk2-platforms/blob/master/Platform/ARM/JunoPkg/ArmJuno.fdf
https://github.com/tianocore/edk2-platforms/blob/master/Platform/BeagleBoard/BeagleBoardPkg/BeagleBoardPkg.fdf
https://github.com/tianocore/edk2-platforms/blob/master/Platform/Hisilicon/HiKey/HiKey.fdf
https://github.com/tianocore/edk2-platforms/blob/master/Platform/Hisilicon/HiKey960/HiKey960.fdf
https://github.com/tianocore/edk2-platforms/blob/master/Platform/RaspberryPi/RPi3/RPi3.fdf
https://github.com/tianocore/edk2-platforms/blob/master/Platform/RaspberryPi/RPi4/RPi4.fdf
Second, the confidential computing changes the threat model completely.
One of our goal is to simplify the design for CC-firmware (TDVF) - remove unnecessary modules, remove unnecessary interface, make the image smaller and faster.
That will reduce the validation effort, too.
That is the main motivation.
Third, I have explained this clearly in the original email and review meeting.
Because it is a big change, the original maintainer (Laszlo) recommend we use two configuration.
Config-A is to keep current architecture, to maximum compatible with OVMF. And we don't remove VMM out of TCB.
Config-B is to have a new TDVF design, to maximum satisfy the security requirement. And we remove VMM out of TCB.
We agreed, and we are proceeding.
> Jiewen argued this is a simplification. Which is not completely wrong,
> but it's also only half the truth. Switching all OVMF builds over to
> PEI-less boot doesn't work because some features supported by OVMF
> depend on PEI Modules. Therefore TDX Config-B skipping the PEI phase
> means we would have to maintain two boot work flows (with and without
> PEI phase) for OVMF. Which in turn would imply more work for
> maintenance, testing and so on.
[Jiewen] I am not asking your to OVMF build to PEI-less.
But if you want to do, I will not object.
As EDKII maintainer, I don't see a burden to maintain multiple boot work flow.
We are already doing that on other project. As least for me, I don't see any problem.
If you think it is a burden, you may work partial of the feature.
EDKII is a big project. Nobody knows everything, including me. I will also rely on other people to handle some features I am not familiar with.
I don't see any problem.
On contrast, if we keep PEI for config B, it adds extra burden from security assurance perspective.
That means, every issue in PEI may be exposed to TDVF.
Comparing the effort to maintain the work flow and the effort to handle potential security issue, I would choose to maintain the work flow.
I have experience to wait for 1 year embargo to fix EDKII security issue, it is very painful and brings huge burden.
> Also note that accepting all memory in SEC phase would be temporary
> only. Once we have support for lazy accept in OVMF we will accept most
> memory in DXE phase (or leave it to the linux kernel), whereas SEC/PEI
> will accept only a small fraction of the memory. Just enough to allow
> DXE phase initialize memory management & lazy accept support.
>
> > This is to make the code consistent in Config-A and Config-B.
>
> I want TDVF be consistent with the rest of OVMF. Makes long-term
> maintenance easier. Building a single binary for both SEV and TDX with
> full confidential computing support (including config-b features) will
> be easier too.
[Jiewen] I am not convinced that TDVF be consist with rest of OVMF.
The goal of project is different. The choice can be different.
I don't see a reason that one platform must be in this way, just because other platform does in this way.
Easy and difficult are very subjective term.
I think a PEI-less TDVF is much easier to maintain, comparing with complicated OVMF flow, at least from security perspective.
The less code we have, the less issue we have.
> > 2. TDVF's MP support is via Mailbox [3] . BSP wakes up APs by putting
> > command/parameters in Mailbox. So we have this patch [4]. While
> > EDK2's MP support (CpuMpPei/CpuDxe) is via INIT-SIPI-SIPI. They're
> > different. We cannot distribute the load to all CPUs with EDK2's MP
> > service.
>
> > Patch #14 [5] is the commit to enable TDX in MpInitLib. Actually this
> > is to make sure the CpuMpPei/CpuDxe will not break in Td guest in
> > run-time. Patch #14 is rather simple, for example, ApWorker is not
> > supported.
>
> Well, MpInitLib seems to have full support (including ApWorker) for SEV.
> I'd expect you can add TDX support too, and schedule the jobs you want
> run on the APs via TDX mailbox instead of using IPIs.
>
> And I think to support parallel lazy accept in the DXE phase (for lazy
> accept) you will need proper MpInitLib support anyway.
>
> > 3. In current TDVF implementation, Stack is not set for APs. So C
> > functions cannot be called for APs. If stack is set for APs, then more
> > memories should be reserved in MEMFD. For example, if 1 AP needs 4k
> > size stack, then 15 AP need 60k memory. (We assume only 1+15 CPUs
> > accept memory). This makes things complicated.
>
> I think skipping PEI phase and moving stuff to SEC phase makes things
> complicated. Reserving stacks in MEMFD would only be needed because you
> can't allocate memory in the SEC phase. When you initialize the APs in
> PEI instead you can just allocate memory and the MEMFD dependency goes
> away.
>
> > Based on above considerations, we use the current design that BSP-APs
> > accept memory in SEC phase (in assembly code).
>
> I think the design doesn't make much sense for the reasons outlined
> above.
>
> I'd suggest to put aside parallel accept for now and just let the BSP
> accept the memory for initial TDX support. Focus on adding lazy accept
> support next. Finally re-evaluate parallel accept support, *after*
> merging lazy accept.
>
> With a major change in the memory acceptance workflow being planned it
> doesn't look like a good idea to me to tune memory accept performance
> *now*.
>
> My naive expectation would be that parallel accept in SEC/PEI simply
> isn't worth the effort due to the small amount of memory being needed.
> Parallel accept in DXE probably is useful, but how much it speeds up
> boot depends on how much accepted memory we must hand over to the linux
> kernel so it has enough room to successfully initialize memory
> management & lazy accept.
>
> take care,
> Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-18 9:59 ` Yao, Jiewen
@ 2021-11-19 15:11 ` Gerd Hoffmann
2021-11-20 3:18 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-19 15:11 UTC (permalink / raw)
To: Yao, Jiewen
Cc: Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Tom Lendacky
Hi,
> Comment on config-B.
> > I'm sure I've asked this before: Why skip the PEI phase? So far
> > I have not seen any convincing argument for it.
>
> Skipping PEI phase is valid architecture design.
Sure.
> Second, the confidential computing changes the threat model
> completely. One of our goal is to simplify the design for CC-firmware
> (TDVF) - remove unnecessary modules, remove unnecessary interface,
> make the image smaller and faster. That will reduce the validation
> effort, too.
>
> That is the main motivation.
That totally makes sense. I expect TDVF Config-B will look alot like
the existing AmdSev configuration variant which is stripped down too.
No SMM support, no network stack, ...
There wouldn't be left much in PEI beside PeiCore and
OvmfPkg/PlatformPei.
But I don't see how dropping the PEI phase altogether helps much in
stripping down the firmware image. The initialization currently handled
by OvmfPkg/PlatformPei must happen somewhere else instead. Given SEC is
a very restricted environment I don't expect the code can be shared
easily, so we will probably end up with code duplication. Also two
different boot workflows which I fear can easily introduce subtle bugs
due to differences like a initialization order changes. This is what I
see as maintenance problem.
> Config-A is to keep current architecture, to maximum compatible with
> OVMF. And we don't remove VMM out of TCB.
> Config-B is to have a new TDVF design, to maximum satisfy the security
> requirement. And we remove VMM out of TCB.
Sure.
config-a is ovmf with tdx support added, all uefi features present, only
basic tdx/sev support (basically support memory encryption).
config-b is simliar to AmdSev (maybe we'll merge them some day),
stripped down uefi feature set (no network etc), full tdx support
including attestation etc.
I don't want question all that. I still don't see the point in dropping
the PEI phase and make config-b work different that all other ovmf
variants though.
> > Jiewen argued this is a simplification. Which is not completely wrong,
> > but it's also only half the truth. Switching all OVMF builds over to
> > PEI-less boot doesn't work because some features supported by OVMF
> > depend on PEI Modules. Therefore TDX Config-B skipping the PEI phase
> > means we would have to maintain two boot work flows (with and without
> > PEI phase) for OVMF. Which in turn would imply more work for
> > maintenance, testing and so on.
>
> [Jiewen] I am not asking your to OVMF build to PEI-less.
> But if you want to do, I will not object.
s3, smm, tpm and maybe more depends on pei modules, so dropping the PEi
phase is not an option for a full-featured OVMF build. So I'd very much
prefer all ovmf variants (including tdvf) use the PEI phase.
> On contrast, if we keep PEI for config B, it adds extra burden from
> security assurance perspective. That means, every issue in PEI may be
> exposed to TDVF.
Same for all other modules used by tdvf.
The list of affected PEI modules is rather short though. I think it's
only PeiCore and DxeIpl. PlatformPei doesn't count as the code wouldn't
go away but would be moved to SEC (and maybe parts of it to DXE).
> Comparing the effort to maintain the work flow and the effort to
> handle potential security issue, I would choose to maintain the work
> flow. I have experience to wait for 1 year embargo to fix EDKII
> security issue, it is very painful and brings huge burden.
The security workflow is a serious problem indeed. Not only for TDVF,
also for OVMF in general, and other platforms too. We should certainly
try to improve it.
I'm not going to open that discussion in this thread. But let me drop
two links two links to osfc talk and workshop (Not 30th + Dec 1st),
titled "The firmware supply-chain security is broken: can we fix it?"
https://talks.osfc.io/osfc2021/talk/D9X39Z/
https://talks.osfc.io/osfc2021/talk/E9YYJF/
> > I want TDVF be consistent with the rest of OVMF. Makes long-term
> > maintenance easier. Building a single binary for both SEV and TDX with
> > full confidential computing support (including config-b features) will
> > be easier too.
>
> [Jiewen] I am not convinced that TDVF be consist with rest of OVMF.
> The goal of project is different. The choice can be different.
> I don't see a reason that one platform must be in this way, just because other platform does in this way.
Hmm? Seeing TDVF as "other platform" is a rather strange view given
that we are integrating tdx support into OVMF right now ...
> I think a PEI-less TDVF is much easier to maintain, comparing with
> complicated OVMF flow, at least from security perspective. The less
> code we have, the less issue we have.
Well, we will have TDX support in the normal OVMF workflow anyway,
because we need that for config-a. Why use and maintain something
different for config-b? That looks rather pointless to me ...
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-19 15:11 ` Gerd Hoffmann
@ 2021-11-20 3:18 ` Yao, Jiewen
2021-11-23 12:38 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-20 3:18 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Tom Lendacky
> -----Original Message-----
> From: Gerd Hoffmann <kraxel@redhat.com>
> Sent: Friday, November 19, 2021 11:12 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: Xu, Min M <min.m.xu@intel.com>; devel@edk2.groups.io; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> Brijesh Singh <brijesh.singh@amd.com>; Erdem Aktas
> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
> Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
>
> Hi,
>
> > Comment on config-B.
>
> > > I'm sure I've asked this before: Why skip the PEI phase? So far
> > > I have not seen any convincing argument for it.
> >
> > Skipping PEI phase is valid architecture design.
>
> Sure.
>
> > Second, the confidential computing changes the threat model
> > completely. One of our goal is to simplify the design for CC-firmware
> > (TDVF) - remove unnecessary modules, remove unnecessary interface,
> > make the image smaller and faster. That will reduce the validation
> > effort, too.
> >
> > That is the main motivation.
>
> That totally makes sense. I expect TDVF Config-B will look alot like
> the existing AmdSev configuration variant which is stripped down too.
[Jiewen] I don't think TDVF config-B will be like the AMD SEV is right statement.
TDVF and SEV are two different platforms.
Intel mainly focuses on TDVF and we will let AMD defines the feature set in SEV.
They MAY be alike if possible.
But difference is also acceptable if there is architecture difference or different decision in different company.
> No SMM support, no network stack, ...
>
> There wouldn't be left much in PEI beside PeiCore and
> OvmfPkg/PlatformPei.
>
> But I don't see how dropping the PEI phase altogether helps much in
> stripping down the firmware image. The initialization currently handled
> by OvmfPkg/PlatformPei must happen somewhere else instead. Given SEC is
> a very restricted environment I don't expect the code can be shared
> easily, so we will probably end up with code duplication. Also two
> different boot workflows which I fear can easily introduce subtle bugs
> due to differences like a initialization order changes. This is what I
> see as maintenance problem.
[Jiewen] I don't think this is right statement.
In Tiano history, there were security bugs exposed in PEI phase, even the PEI Core on FV processing.
I do see the value to skip PEI core.
Again, I am very familiar with non-PEI flow.
Back to 10 years ago, I was maintainer of a non-PEI platform (named DUET) and we jumped from SEC to DXE directly.
I don't see any maintenance problem.
>
> > Config-A is to keep current architecture, to maximum compatible with
> > OVMF. And we don't remove VMM out of TCB.
>
> > Config-B is to have a new TDVF design, to maximum satisfy the security
> > requirement. And we remove VMM out of TCB.
>
> Sure.
>
> config-a is ovmf with tdx support added, all uefi features present, only
> basic tdx/sev support (basically support memory encryption).
>
> config-b is simliar to AmdSev (maybe we'll merge them some day),
> stripped down uefi feature set (no network etc), full tdx support
> including attestation etc.
[Jiewen] I think we are debating two different things.
Your statement that "config-B is similar to AmdSev" does not support the statement "config-B should be adopt what AmdSev chooses".
TDVF config-B is proposed by Intel. AMD SEV is proposed by AMD. I respect SEV people and I *may* propose something, but I will ack their decision.
I would not force them to accept the TDVF config-B. And vice versa.
>
> I don't want question all that. I still don't see the point in dropping
> the PEI phase and make config-b work different that all other ovmf
> variants though.
[Jiewen] My point is simple - Threat Model is different.
That causes security objective difference and design difference.
Each CC env owner should have freedom to choose what they want to enforce.
IMHO, they are the policy decision maker.
> > > Jiewen argued this is a simplification. Which is not completely wrong,
> > > but it's also only half the truth. Switching all OVMF builds over to
> > > PEI-less boot doesn't work because some features supported by OVMF
> > > depend on PEI Modules. Therefore TDX Config-B skipping the PEI phase
> > > means we would have to maintain two boot work flows (with and without
> > > PEI phase) for OVMF. Which in turn would imply more work for
> > > maintenance, testing and so on.
> >
> > [Jiewen] I am not asking your to OVMF build to PEI-less.
> > But if you want to do, I will not object.
>
> s3, smm, tpm and maybe more depends on pei modules, so dropping the PEi
> phase is not an option for a full-featured OVMF build. So I'd very much
> prefer all ovmf variants (including tdvf) use the PEI phase.
>
> > On contrast, if we keep PEI for config B, it adds extra burden from
> > security assurance perspective. That means, every issue in PEI may be
> > exposed to TDVF.
>
> Same for all other modules used by tdvf.
>
> The list of affected PEI modules is rather short though. I think it's
> only PeiCore and DxeIpl. PlatformPei doesn't count as the code wouldn't
> go away but would be moved to SEC (and maybe parts of it to DXE).
>
> > Comparing the effort to maintain the work flow and the effort to
> > handle potential security issue, I would choose to maintain the work
> > flow. I have experience to wait for 1 year embargo to fix EDKII
> > security issue, it is very painful and brings huge burden.
>
> The security workflow is a serious problem indeed. Not only for TDVF,
> also for OVMF in general, and other platforms too. We should certainly
> try to improve it.
[Jiewen] If you look at how we state config-A and config-B again, you will find we made difference statement.
I copy it here again.
1) Config-A is to keep current architecture, to maximum compatible with OVMF. And we don't remove VMM out of TCB.
2) Config-B is to have a new TDVF design, to maximum satisfy the security requirement. And we remove VMM out of TCB.
Because of the threat model difference, in config-A, we can safely make some assumption that the VMM is benign and VMM will not input malicious data. As such, we might not perform data validation. We just trust VMM input.
However, in config-B, VMM is malicious, which means we need be careful to NOT trust VMM at any time.
The code in config-A and config-B may do totally different thing to handle the difference situation.
I don't think it is hidden assumption that if TDVF need do some check, then a generic OVMF need do this check.
If OVMF trusts the VMM, the check might be totally unnecessary.
> I'm not going to open that discussion in this thread. But let me drop
> two links two links to osfc talk and workshop (Not 30th + Dec 1st),
> titled "The firmware supply-chain security is broken: can we fix it?"
>
> https://talks.osfc.io/osfc2021/talk/D9X39Z/
> https://talks.osfc.io/osfc2021/talk/E9YYJF/
>
> > > I want TDVF be consistent with the rest of OVMF. Makes long-term
> > > maintenance easier. Building a single binary for both SEV and TDX with
> > > full confidential computing support (including config-b features) will
> > > be easier too.
> >
> > [Jiewen] I am not convinced that TDVF be consist with rest of OVMF.
> > The goal of project is different. The choice can be different.
> > I don't see a reason that one platform must be in this way, just because other
> platform does in this way.
>
> Hmm? Seeing TDVF as "other platform" is a rather strange view given
> that we are integrating tdx support into OVMF right now ...
[Jiewen] This is how Intel views the "platform".
In history, we call this one binary mode is "multiple-platform" or "multiple-SKU".
That means we only have one binary, and this single binary can boot different platforms, which has similar CPU or silicon but different platform board design.
And there will be platform specific code flow, such as
Switch (PlatformId) {
Case PlatformA:
{do platformA init}
Case PlatformB:
{do platformB init}
}
If you treat CC_TYPE to be platformID, it perfectly matches.
Also a platform may has extra module (a driver or an FV) to support the platform specific feature. Or a platform may much simpler design and skip some drivers.
The actual multi-platform design is even more complicated, because we also have group concept. So I will stop the discussion here.
>
> > I think a PEI-less TDVF is much easier to maintain, comparing with
> > complicated OVMF flow, at least from security perspective. The less
> > code we have, the less issue we have.
>
> Well, we will have TDX support in the normal OVMF workflow anyway,
> because we need that for config-a. Why use and maintain something
> different for config-b? That looks rather pointless to me ...
[Jiewen] I think I have explained a lot above.
The key difference between config-a and config-b is threat mode.
I even propose config-a skip PEI phase. I am persuaded to let config-a adopt the OVMF way, because the threat model of config-A is same as the normal OVMF.
But config-B is NOT.
Different threat model drives different solution.
I completely don't understand why they must be same.
If you force me to add PEI to config-B. Finally, that causes TDVF config-B is compromised due to an issue in PEI.
Who will take the responsibility? Will you or RedHat take that?
>
> take care,
> Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-20 3:18 ` Yao, Jiewen
@ 2021-11-23 12:38 ` Gerd Hoffmann
2021-11-23 13:07 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-23 12:38 UTC (permalink / raw)
To: Yao, Jiewen
Cc: Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Tom Lendacky
Hi,
> > That totally makes sense. I expect TDVF Config-B will look alot like
> > the existing AmdSev configuration variant which is stripped down too.
>
> [Jiewen] I don't think TDVF config-B will be like the AMD SEV is right statement.
> TDVF and SEV are two different platforms.
Yes, of course. But even though there are differences in both
implementation and supported features both platforms have similar goals
and there is quite some overlap in concepts too.
> Intel mainly focuses on TDVF and we will let AMD defines the feature
> set in SEV. They MAY be alike if possible. But difference is also
> acceptable if there is architecture difference or different decision
> in different company.
Yes. Whenever they are close enough that merging them makes sense
remains to be seen.
> > But I don't see how dropping the PEI phase altogether helps much in
> > stripping down the firmware image. The initialization currently handled
> > by OvmfPkg/PlatformPei must happen somewhere else instead. Given SEC is
> > a very restricted environment I don't expect the code can be shared
> > easily, so we will probably end up with code duplication. Also two
> > different boot workflows which I fear can easily introduce subtle bugs
> > due to differences like a initialization order changes. This is what I
> > see as maintenance problem.
>
> [Jiewen] I don't think this is right statement.
> In Tiano history, there were security bugs exposed in PEI phase, even the PEI Core on FV processing.
> I do see the value to skip PEI core.
On the other hand there are probably more eyes are looking at PEI Core
because it is used by a lot of platforms, increasing the chance that
bugs are actually spotted.
> Again, I am very familiar with non-PEI flow. Back to 10 years ago, I
> was maintainer of a non-PEI platform (named DUET) and we jumped from
> SEC to DXE directly. I don't see any maintenance problem.
The maintenance problem isn't a non-PEI flow. If a platform chooses
that -- fine. The problem having to maintain both PEI and non-PEI
workflow.
> [Jiewen] I think we are debating two different things.
> Your statement that "config-B is similar to AmdSev" does not support
> the statement "config-B should be adopt what AmdSev chooses".
I never stated "config-B should be adopt what AmdSev chooses".
I stated "TDVF boot workflow should be simlar to the other OVMF
platforms to simplify maintenance".
AmdSev is just an example showing that you can easily strip down the
firmware build without putting the boot workflow upside down (which one
of the things config-b wants too).
> > I don't want question all that. I still don't see the point in dropping
> > the PEI phase and make config-b work different that all other ovmf
> > variants though.
>
> [Jiewen] My point is simple - Threat Model is different.
> That causes security objective difference and design difference.
Does not using PEI phase have any advantages from a security point of
view (other than "not using PEI Core code which might have bugs")?
> > The security workflow is a serious problem indeed. Not only for TDVF,
> > also for OVMF in general, and other platforms too. We should certainly
> > try to improve it.
>
> [Jiewen] If you look at how we state config-A and config-B again, you will find we made difference statement.
> I copy it here again.
> 1) Config-A is to keep current architecture, to maximum compatible with OVMF. And we don't remove VMM out of TCB.
> 2) Config-B is to have a new TDVF design, to maximum satisfy the security requirement. And we remove VMM out of TCB.
>
> Because of the threat model difference, in config-A, we can safely
> make some assumption that the VMM is benign and VMM will not input
> malicious data. As such, we might not perform data validation. We just
> trust VMM input.
>
> However, in config-B, VMM is malicious, which means we need be careful to NOT trust VMM at any time.
> The code in config-A and config-B may do totally different thing to handle the difference situation.
>
> I don't think it is hidden assumption that if TDVF need do some check, then a generic OVMF need do this check.
> If OVMF trusts the VMM, the check might be totally unnecessary.
Do you have a concrete example for that?
I can't think of a good reason to skip checks on OVMF. When we -- for
example -- review and improve virtio drivers to make sure they can't be
used by the VMM to exploit a TDVF guest: We surely would use the
improved sanity checks on OVMF too, for better OVMF stability and also
for better test coverage of the sanity checks.
> > Hmm? Seeing TDVF as "other platform" is a rather strange view given
> > that we are integrating tdx support into OVMF right now ...
>
> [Jiewen] This is how Intel views the "platform".
> In history, we call this one binary mode is "multiple-platform" or "multiple-SKU".
> That means we only have one binary, and this single binary can boot different platforms, which has similar CPU or silicon but different platform board design.
> And there will be platform specific code flow, such as
> Switch (PlatformId) {
> Case PlatformA:
> {do platformA init}
> Case PlatformB:
> {do platformB init}
> }
>
> If you treat CC_TYPE to be platformID, it perfectly matches.
Yes. We have that in quite a few places. IoLib for example.
It's required for Config-A, obviously.
So, what is your plan for IoLib for Config-B?
> Also a platform may has extra module (a driver or an FV) to support
> the platform specific feature. Or a platform may much simpler design
> and skip some drivers.
Sure. Config-A will need some drivers from OvmfPkg/AmdSev/ so both SEV
and TDX work, whereas Config-B will not.
> I even propose config-a skip PEI phase.
Care to back your proposal by posting patches to the list?
I think it's easier to discuss the advantages + disadvantages
with concrete code at hand.
> I am persuaded to let config-a adopt the OVMF way, because the threat model of config-A is same as the normal OVMF.
> But config-B is NOT.
> Different threat model drives different solution.
> I completely don't understand why they must be same.
I don't understand why you want separate them. Improving OvmfPkg
security is good for both OVMF and TDVF. For TDVF it is clearly much
more important due to the different threat model, but it is good for
OVMF too. Likewise edk2 in general.
> If you force me to add PEI to config-B. Finally, that causes TDVF config-B is compromised due to an issue in PEI.
> Who will take the responsibility? Will you or RedHat take that?
Bugs happen. There could also be bugs in the additional SEC code you
need for platform setup in a non-PEI configuration ...
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-23 12:38 ` Gerd Hoffmann
@ 2021-11-23 13:07 ` Yao, Jiewen
2021-11-23 14:26 ` James Bottomley
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-23 13:07 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Tom Lendacky
Comment below only:
> I am persuaded to let config-a adopt the OVMF way, because the threat model of config-A is same as the normal OVMF.
> But config-B is NOT.
> Different threat model drives different solution.
> I completely don't understand why they must be same.
I don't understand why you want separate them. Improving OvmfPkg
security is good for both OVMF and TDVF. For TDVF it is clearly much
more important due to the different threat model, but it is good for
OVMF too. Likewise edk2 in general.
[Jiewen] My answer is very clear as I mentioned multiple times.
The threat model is different.
IMHO, talking about "Improving OvmfPkg security" without a clear threat model is *meaningless*.
All mitigation must be based upon threat mode and security objective.
Otherwise, what you are doing might be either unnecessary or insufficient.
> If you force me to add PEI to config-B. Finally, that causes TDVF config-B is compromised due to an issue in PEI.
> Who will take the responsibility? Will you or RedHat take that?
Bugs happen. There could also be bugs in the additional SEC code you
need for platform setup in a non-PEI configuration ...
[Jiewen] I agree. That is why we need smaller code.
We can just focus on review that small portion of code what we have written for TDVF, instead of the whole PEI.
We have process to review and maintain the extra TDVF code.
I think it is totally *unfair* that you force me to add PEI, without knowing the quality of PEI.
Thank you
Yao Jiewen
> -----Original Message-----
> From: Gerd Hoffmann <kraxel@redhat.com>
> Sent: Tuesday, November 23, 2021 8:38 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: Xu, Min M <min.m.xu@intel.com>; devel@edk2.groups.io; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> Brijesh Singh <brijesh.singh@amd.com>; Erdem Aktas
> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
> Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
>
> Hi,
>
> > > That totally makes sense. I expect TDVF Config-B will look alot like
> > > the existing AmdSev configuration variant which is stripped down too.
> >
> > [Jiewen] I don't think TDVF config-B will be like the AMD SEV is right statement.
> > TDVF and SEV are two different platforms.
>
> Yes, of course. But even though there are differences in both
> implementation and supported features both platforms have similar goals
> and there is quite some overlap in concepts too.
>
> > Intel mainly focuses on TDVF and we will let AMD defines the feature
> > set in SEV. They MAY be alike if possible. But difference is also
> > acceptable if there is architecture difference or different decision
> > in different company.
>
> Yes. Whenever they are close enough that merging them makes sense
> remains to be seen.
>
> > > But I don't see how dropping the PEI phase altogether helps much in
> > > stripping down the firmware image. The initialization currently handled
> > > by OvmfPkg/PlatformPei must happen somewhere else instead. Given SEC is
> > > a very restricted environment I don't expect the code can be shared
> > > easily, so we will probably end up with code duplication. Also two
> > > different boot workflows which I fear can easily introduce subtle bugs
> > > due to differences like a initialization order changes. This is what I
> > > see as maintenance problem.
> >
> > [Jiewen] I don't think this is right statement.
> > In Tiano history, there were security bugs exposed in PEI phase, even the PEI
> Core on FV processing.
> > I do see the value to skip PEI core.
>
> On the other hand there are probably more eyes are looking at PEI Core
> because it is used by a lot of platforms, increasing the chance that
> bugs are actually spotted.
>
> > Again, I am very familiar with non-PEI flow. Back to 10 years ago, I
> > was maintainer of a non-PEI platform (named DUET) and we jumped from
> > SEC to DXE directly. I don't see any maintenance problem.
>
> The maintenance problem isn't a non-PEI flow. If a platform chooses
> that -- fine. The problem having to maintain both PEI and non-PEI
> workflow.
>
> > [Jiewen] I think we are debating two different things.
> > Your statement that "config-B is similar to AmdSev" does not support
> > the statement "config-B should be adopt what AmdSev chooses".
>
> I never stated "config-B should be adopt what AmdSev chooses".
>
> I stated "TDVF boot workflow should be simlar to the other OVMF
> platforms to simplify maintenance".
>
> AmdSev is just an example showing that you can easily strip down the
> firmware build without putting the boot workflow upside down (which one
> of the things config-b wants too).
>
> > > I don't want question all that. I still don't see the point in dropping
> > > the PEI phase and make config-b work different that all other ovmf
> > > variants though.
> >
> > [Jiewen] My point is simple - Threat Model is different.
> > That causes security objective difference and design difference.
>
> Does not using PEI phase have any advantages from a security point of
> view (other than "not using PEI Core code which might have bugs")?
>
> > > The security workflow is a serious problem indeed. Not only for TDVF,
> > > also for OVMF in general, and other platforms too. We should certainly
> > > try to improve it.
> >
> > [Jiewen] If you look at how we state config-A and config-B again, you will find
> we made difference statement.
> > I copy it here again.
> > 1) Config-A is to keep current architecture, to maximum compatible with
> OVMF. And we don't remove VMM out of TCB.
> > 2) Config-B is to have a new TDVF design, to maximum satisfy the security
> requirement. And we remove VMM out of TCB.
> >
> > Because of the threat model difference, in config-A, we can safely
> > make some assumption that the VMM is benign and VMM will not input
> > malicious data. As such, we might not perform data validation. We just
> > trust VMM input.
> >
> > However, in config-B, VMM is malicious, which means we need be careful to
> NOT trust VMM at any time.
> > The code in config-A and config-B may do totally different thing to handle the
> difference situation.
> >
> > I don't think it is hidden assumption that if TDVF need do some check, then a
> generic OVMF need do this check.
> > If OVMF trusts the VMM, the check might be totally unnecessary.
>
> Do you have a concrete example for that?
>
> I can't think of a good reason to skip checks on OVMF. When we -- for
> example -- review and improve virtio drivers to make sure they can't be
> used by the VMM to exploit a TDVF guest: We surely would use the
> improved sanity checks on OVMF too, for better OVMF stability and also
> for better test coverage of the sanity checks.
>
> > > Hmm? Seeing TDVF as "other platform" is a rather strange view given
> > > that we are integrating tdx support into OVMF right now ...
> >
> > [Jiewen] This is how Intel views the "platform".
> > In history, we call this one binary mode is "multiple-platform" or "multiple-
> SKU".
> > That means we only have one binary, and this single binary can boot different
> platforms, which has similar CPU or silicon but different platform board design.
> > And there will be platform specific code flow, such as
> > Switch (PlatformId) {
> > Case PlatformA:
> > {do platformA init}
> > Case PlatformB:
> > {do platformB init}
> > }
> >
> > If you treat CC_TYPE to be platformID, it perfectly matches.
>
> Yes. We have that in quite a few places. IoLib for example.
> It's required for Config-A, obviously.
>
> So, what is your plan for IoLib for Config-B?
>
> > Also a platform may has extra module (a driver or an FV) to support
> > the platform specific feature. Or a platform may much simpler design
> > and skip some drivers.
>
> Sure. Config-A will need some drivers from OvmfPkg/AmdSev/ so both SEV
> and TDX work, whereas Config-B will not.
>
> > I even propose config-a skip PEI phase.
>
> Care to back your proposal by posting patches to the list?
>
> I think it's easier to discuss the advantages + disadvantages
> with concrete code at hand.
>
> > I am persuaded to let config-a adopt the OVMF way, because the threat model
> of config-A is same as the normal OVMF.
> > But config-B is NOT.
> > Different threat model drives different solution.
> > I completely don't understand why they must be same.
>
> I don't understand why you want separate them. Improving OvmfPkg
> security is good for both OVMF and TDVF. For TDVF it is clearly much
> more important due to the different threat model, but it is good for
> OVMF too. Likewise edk2 in general.
>
> > If you force me to add PEI to config-B. Finally, that causes TDVF config-B is
> compromised due to an issue in PEI.
> > Who will take the responsibility? Will you or RedHat take that?
>
> Bugs happen. There could also be bugs in the additional SEC code you
> need for platform setup in a non-PEI configuration ...
>
> take care,
> Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-23 13:07 ` Yao, Jiewen
@ 2021-11-23 14:26 ` James Bottomley
2021-11-23 14:36 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: James Bottomley @ 2021-11-23 14:26 UTC (permalink / raw)
To: Yao, Jiewen, Gerd Hoffmann
Cc: Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, Tom Lendacky
On Tue, 2021-11-23 at 13:07 +0000, Yao, Jiewen wrote:
> Comment below only:
>
> > I am persuaded to let config-a adopt the OVMF way, because the
> > threat model of config-A is same as the normal OVMF.
> > But config-B is NOT.
> > Different threat model drives different solution.
> > I completely don't understand why they must be same.
>
> I don't understand why you want separate them. Improving OvmfPkg
> security is good for both OVMF and TDVF. For TDVF it is clearly much
> more important due to the different threat model, but it is good for
> OVMF too. Likewise edk2 in general.
>
> [Jiewen] My answer is very clear as I mentioned multiple times.
> The threat model is different.
> IMHO, talking about "Improving OvmfPkg security" without a clear
> threat model is *meaningless*.
> All mitigation must be based upon threat mode and security objective.
> Otherwise, what you are doing might be either unnecessary or
> insufficient.
Can we take a step back and look at the bigger picture. The way EFI is
supposed to operate, according to the architecture model:
https://uefi.org/sites/default/files/resources/PI_Spec_1_7_final_Jan_2019.pdf
(Figure 1 and Table 4)
is that SEC is supposed to be as small and compact as possible, so it
could be ROM installed without worrying about upgrade issues. It's job
is only to get the CPU initialized to the point it can run PEI, measure
PEI and transfer control. Security depends on the smallness of SEC
because if it's rom installed (as a root of trust ought to be) it can't
be upgraded to fix a bug.
PEI is supposed to initialize the hardware: set up the CPU, memory
Application Processors and board configuration so we're in a known
state with described features for DXE. It then measures DXE (only if
SEC didn't measure it) and hands off to DXE. PEI code is designed not
to interact with anything except setup to minimize external
exploitation of internal bugs.
DXE is supposed to contain only runtime code, including device drivers.
The security point here is that the job of PEI is over as soon as it
hands off to DXE, so at that point all the PEI code could be discarded
(it usually isn't, but it could be).
This strict isolation between DXE and PEI means that once we're in DXE,
any bugs in PEI can't be exploited to attack the DXE environment. This
allows us to minimize DXE which is where most of the main risk of
configuration based exploitation is.
In this security model, you increase security by moving as much code as
you can from DXE to PEI because the true attack surface is DXE.
To a lot of us, cutting out PEI, which requires redistributing code
into DXE sounds like an increase not a reduction in the attack surface
of the code. You can argue that OVMF doesn't obey the model above and
has a lot of initialization code in DXE anyway, but then the correct
route would be to fix our PEI/DXE separation to recover the security
properties.
> > If you force me to add PEI to config-B. Finally, that causes TDVF
> > config-B is compromised due to an issue in PEI.
> > Who will take the responsibility? Will you or RedHat take that?
>
> Bugs happen. There could also be bugs in the additional SEC code you
> need for platform setup in a non-PEI configuration ...
>
> [Jiewen] I agree. That is why we need smaller code.
> We can just focus on review that small portion of code what we have
> written for TDVF, instead of the whole PEI.
> We have process to review and maintain the extra TDVF code.
This depends ... if you agree with the security model outlined above,
bugs in PEI are less of a problem because they can't be exploited. Or
do you not agree with this security model?
Security isn't about total bug elimination, it's about exploit
elimination. You fix a security vulnerability either by fixing the bug
that can be exploited or removing the ability to exploit it. This is
the reason for technologies like NX ... they don't stop people
exploiting bugs to write code to the stack, but they do mean that once
you have the code on the stack you can no-longer execute it and the
attackers have to move on to other means of gaining control.
The great thing about exploit elimination vs bug elimination is the
former can usually be done on a whole class of bugs and the latter
requires a whack-a-mole per bug type approach.
James
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-23 14:26 ` James Bottomley
@ 2021-11-23 14:36 ` Yao, Jiewen
2021-11-23 14:51 ` James Bottomley
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-23 14:36 UTC (permalink / raw)
To: jejb@linux.ibm.com
Cc: Gerd Hoffmann, Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Brijesh Singh, Erdem Aktas, Tom Lendacky
> This strict isolation between DXE and PEI means that once we're in DXE,
> any bugs in PEI can't be exploited to attack the DXE environment.
[jiewen] I would disagree the statement above.
There is not strict isolation. Actually no isolation at all.
The DXE is loaded by PEI.
A bug in PEI has global impact and it can definitely be used to attack the DXE.
thank you!
Yao, Jiewen
> 在 2021年11月23日,下午10:27,James Bottomley <jejb@linux.ibm.com> 写道:
>
> On Tue, 2021-11-23 at 13:07 +0000, Yao, Jiewen wrote:
>> Comment below only:
>>
>>> I am persuaded to let config-a adopt the OVMF way, because the
>>> threat model of config-A is same as the normal OVMF.
>>> But config-B is NOT.
>>> Different threat model drives different solution.
>>> I completely don't understand why they must be same.
>>
>> I don't understand why you want separate them. Improving OvmfPkg
>> security is good for both OVMF and TDVF. For TDVF it is clearly much
>> more important due to the different threat model, but it is good for
>> OVMF too. Likewise edk2 in general.
>>
>> [Jiewen] My answer is very clear as I mentioned multiple times.
>> The threat model is different.
>> IMHO, talking about "Improving OvmfPkg security" without a clear
>> threat model is *meaningless*.
>> All mitigation must be based upon threat mode and security objective.
>> Otherwise, what you are doing might be either unnecessary or
>> insufficient.
>
> Can we take a step back and look at the bigger picture. The way EFI is
> supposed to operate, according to the architecture model:
>
> https://uefi.org/sites/default/files/resources/PI_Spec_1_7_final_Jan_2019.pdf
> (Figure 1 and Table 4)
>
> is that SEC is supposed to be as small and compact as possible, so it
> could be ROM installed without worrying about upgrade issues. It's job
> is only to get the CPU initialized to the point it can run PEI, measure
> PEI and transfer control. Security depends on the smallness of SEC
> because if it's rom installed (as a root of trust ought to be) it can't
> be upgraded to fix a bug.
>
> PEI is supposed to initialize the hardware: set up the CPU, memory
> Application Processors and board configuration so we're in a known
> state with described features for DXE. It then measures DXE (only if
> SEC didn't measure it) and hands off to DXE. PEI code is designed not
> to interact with anything except setup to minimize external
> exploitation of internal bugs.
>
> DXE is supposed to contain only runtime code, including device drivers.
>
> The security point here is that the job of PEI is over as soon as it
> hands off to DXE, so at that point all the PEI code could be discarded
> (it usually isn't, but it could be).
>
> This strict isolation between DXE and PEI means that once we're in DXE,
> any bugs in PEI can't be exploited to attack the DXE environment. This
> allows us to minimize DXE which is where most of the main risk of
> configuration based exploitation is.
>
> In this security model, you increase security by moving as much code as
> you can from DXE to PEI because the true attack surface is DXE.
>
> To a lot of us, cutting out PEI, which requires redistributing code
> into DXE sounds like an increase not a reduction in the attack surface
> of the code. You can argue that OVMF doesn't obey the model above and
> has a lot of initialization code in DXE anyway, but then the correct
> route would be to fix our PEI/DXE separation to recover the security
> properties.
>
>>> If you force me to add PEI to config-B. Finally, that causes TDVF
>>> config-B is compromised due to an issue in PEI.
>>> Who will take the responsibility? Will you or RedHat take that?
>>
>> Bugs happen. There could also be bugs in the additional SEC code you
>> need for platform setup in a non-PEI configuration ...
>>
>> [Jiewen] I agree. That is why we need smaller code.
>> We can just focus on review that small portion of code what we have
>> written for TDVF, instead of the whole PEI.
>> We have process to review and maintain the extra TDVF code.
>
> This depends ... if you agree with the security model outlined above,
> bugs in PEI are less of a problem because they can't be exploited. Or
> do you not agree with this security model?
>
> Security isn't about total bug elimination, it's about exploit
> elimination. You fix a security vulnerability either by fixing the bug
> that can be exploited or removing the ability to exploit it. This is
> the reason for technologies like NX ... they don't stop people
> exploiting bugs to write code to the stack, but they do mean that once
> you have the code on the stack you can no-longer execute it and the
> attackers have to move on to other means of gaining control.
>
> The great thing about exploit elimination vs bug elimination is the
> former can usually be done on a whole class of bugs and the latter
> requires a whack-a-mole per bug type approach.
>
> James
>
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-23 14:36 ` Yao, Jiewen
@ 2021-11-23 14:51 ` James Bottomley
2021-11-23 15:10 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: James Bottomley @ 2021-11-23 14:51 UTC (permalink / raw)
To: Yao, Jiewen
Cc: Gerd Hoffmann, Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Brijesh Singh, Erdem Aktas, Tom Lendacky
On Tue, 2021-11-23 at 14:36 +0000, Yao, Jiewen wrote:
> > This strict isolation between DXE and PEI means that once we're in
> > DXE, any bugs in PEI can't be exploited to attack the DXE
> > environment.
>
> [jiewen] I would disagree the statement above.
> There is not strict isolation. Actually no isolation at all.
> The DXE is loaded by PEI.
Not in OVMF ... DXE and PEI are actually loaded by SEC. PEI eventually
jumps to execute DXE but that's after all its own tasks are completed.
> A bug in PEI has global impact and it can definitely be used to
> attack the DXE.
Only if it can be exploited. Moving things to PEI is mitigating the
exploitability not the bugs. The point about exploitability and PEI is
that it doesn't read any config files, it can't execute any EFI
binaries and it has no Human Interface modules so can't be influenced
even by a physically present attacker. No ability to influence is what
removes the ability to exploit.
James
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-23 14:51 ` James Bottomley
@ 2021-11-23 15:10 ` Yao, Jiewen
2021-11-23 15:37 ` [edk2-devel] " James Bottomley
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-23 15:10 UTC (permalink / raw)
To: jejb@linux.ibm.com
Cc: Gerd Hoffmann, Xu, Min M, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Brijesh Singh, Erdem Aktas, Tom Lendacky
I would say the PEI owns the system and all memory (including the DXE).
A bug in PEI may override the loaded DXE memory or the whole system.
In history I did see PEI security issues.
Some security issue in PEI caused system compromised completely. You even have no chance to run DXE.
thank you!
Yao, Jiewen
> 在 2021年11月23日,下午10:52,James Bottomley <jejb@linux.ibm.com> 写道:
>
> On Tue, 2021-11-23 at 14:36 +0000, Yao, Jiewen wrote:
>>> This strict isolation between DXE and PEI means that once we're in
>>> DXE, any bugs in PEI can't be exploited to attack the DXE
>>> environment.
>>
>> [jiewen] I would disagree the statement above.
>> There is not strict isolation. Actually no isolation at all.
>> The DXE is loaded by PEI.
>
> Not in OVMF ... DXE and PEI are actually loaded by SEC. PEI eventually
> jumps to execute DXE but that's after all its own tasks are completed.
>
>> A bug in PEI has global impact and it can definitely be used to
>> attack the DXE.
>
> Only if it can be exploited. Moving things to PEI is mitigating the
> exploitability not the bugs. The point about exploitability and PEI is
> that it doesn't read any config files, it can't execute any EFI
> binaries and it has no Human Interface modules so can't be influenced
> even by a physically present attacker. No ability to influence is what
> removes the ability to exploit.
>
> James
>
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-23 15:10 ` Yao, Jiewen
@ 2021-11-23 15:37 ` James Bottomley
2021-11-24 3:15 ` Yao, Jiewen
[not found] ` <16BA5D1709524394.9880@groups.io>
0 siblings, 2 replies; 107+ messages in thread
From: James Bottomley @ 2021-11-23 15:37 UTC (permalink / raw)
To: devel, jiewen.yao
Cc: Gerd Hoffmann, Xu, Min M, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, Tom Lendacky
On Tue, 2021-11-23 at 15:10 +0000, Yao, Jiewen wrote:
> I would say the PEI owns the system and all memory (including the
> DXE).
>
> A bug in PEI may override the loaded DXE memory or the whole system.
That's not the correct way to analyse the security properties. From
the security point of view this is a trapdoor system: once you go
through the door, you can't go back (the trapdoor being the jump from
PEI to DXE). The trapdoor isolates the domains and allows you to
analyse the security properties of each separately. It also allows
separation of exposure ... which is what we use in this case: the PEI
domain has very limited exposure, it's the DXE domain that has full
exposure but, because of the trapdoor, bugs in PEI code can't be used
to exploit the system when it has transitioned to the DXE domain.
> In history I did see PEI security issues.
> Some security issue in PEI caused system compromised completely. You
> even have no chance to run DXE.
The security domain analysis above doesn't mean no bug in PEI is ever
exploitable but it does mean that there are fewer exploitability
classes in PEI than DXE because the security domain is much less
exposed.
James
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-23 15:37 ` [edk2-devel] " James Bottomley
@ 2021-11-24 3:15 ` Yao, Jiewen
2021-11-24 8:12 ` Gerd Hoffmann
[not found] ` <16BA5D1709524394.9880@groups.io>
1 sibling, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-24 3:15 UTC (permalink / raw)
To: jejb@linux.ibm.com, devel@edk2.groups.io
Cc: Gerd Hoffmann, Xu, Min M, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, Tom Lendacky
I think we are discussing under different context.
First, the term "isolation" shall be clarified.
In my context, "isolation" means two domain cannot impact each other.
The isolation is enforced by a 3rd higher privileged component. Examples: Ring3 apps are isolated by OS. TDs are isolated by TDX Module. That is why I say: there is no isolation.
In your context, if one domain jumps to another domain and never jump back, then you call it "isolation".
Second, in EDKII, we have similar concept - we call trust region (TR):
1) Recovery Trust Region (PEI)
2) Main Trust Region (DXE-before EndOfDxe)
3) MM Trust Region (SMM)
4) Boot Trust Region (DXE w/o CSM-after EndOfDxe)
5) Legacy Trust Region (DXE with CSM-after EndOfDxe)
6) OS Trust Region (OS Boot)
We use term "transition" to describe the domain jump. We don’t use term "isolation".
We use "isolation" where two co-existed RT cannot tamper each other. For example, MM trust region and Boot Trust Region are isolated.
Actually, the only isolation example we have in BIOS is x86 SMM or ARM TrustZone.
We have below security assumption:
1) What can be trusted? The later launched TR can trust the earlier TR. Here "trust" means "use data input without validation"
For example:
1.1) Main TR can trust the input from Recovery TR.
1.2) MM RT can trust the input from Main TR.
2) What cannot be trusted? Here "not trust" means "validate data input before use "
For example:
2.1) MM RT cannot trust the input from Boot TR.
2.2) Recovery RT cannot trust the input from Boot TR.
However, TR just means a region definition to help us do security analysis.
It is NOT related to any security exposure, severity, or exploitability.
There is no conclusion that a bug in PEI is more or less exploitable than DXE or SMM.
Here, I have comment for the sentence below:
1. " the PEI domain has very limited exposure, it's the DXE domain that has full exposure "
[Jiewen] I don’t understand how that is concluded, on " limited exposure ", " full exposure ".
2. "bugs in PEI code can't be used to exploit the system when it has transitioned to the DXE domain."
[Jiewen] I disagree. A bug in PEI code may already modify the HOB, while the HOB is an architecture data input for DXE.
If DXE relies on some malicious data from PEI, DXE might be exploited later.
3. " but it does mean that there are fewer exploitability classes in PEI than DXE because the security domain is much less exposed."
[Jiewen] I don’t understand how that is concluded, on "fewer", "less".
In history, there are security bugs in PEI and there are security bugs in DXE. I won't say fewer or less.
Also because we use *LOCK* mechanism, and some LOCKs are enforced in PEI phase.
A bug in PEI might be more severe than a bug in DXE.
Hi James
Sorry, I am a little lost now.
To be honest, I am not sure what is objection on the discussion.
Are you question the general threat model analysis on UEFI PI architecture?
Or are you trying to persuade me we should include PEI in TDVF, because you think it is safer to add code in PEI ?
Or something else?
Please enlighten me that.
Thank you
Yao, Jiewen
> -----Original Message-----
> From: James Bottomley <jejb@linux.ibm.com>
> Sent: Tuesday, November 23, 2021 11:38 PM
> To: devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>; Xu, Min M <min.m.xu@intel.com>;
> Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>; Erdem
> Aktas <erdemaktas@google.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> On Tue, 2021-11-23 at 15:10 +0000, Yao, Jiewen wrote:
> > I would say the PEI owns the system and all memory (including the
> > DXE).
> >
> > A bug in PEI may override the loaded DXE memory or the whole system.
>
> That's not the correct way to analyse the security properties. From
> the security point of view this is a trapdoor system: once you go
> through the door, you can't go back (the trapdoor being the jump from
> PEI to DXE). The trapdoor isolates the domains and allows you to
> analyse the security properties of each separately. It also allows
> separation of exposure ... which is what we use in this case: the PEI
> domain has very limited exposure, it's the DXE domain that has full
> exposure but, because of the trapdoor, bugs in PEI code can't be used
> to exploit the system when it has transitioned to the DXE domain.
>
> > In history I did see PEI security issues.
> > Some security issue in PEI caused system compromised completely. You
> > even have no chance to run DXE.
>
> The security domain analysis above doesn't mean no bug in PEI is ever
> exploitable but it does mean that there are fewer exploitability
> classes in PEI than DXE because the security domain is much less
> exposed.
>
> James
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-24 3:15 ` Yao, Jiewen
@ 2021-11-24 8:12 ` Gerd Hoffmann
2021-11-24 11:08 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-24 8:12 UTC (permalink / raw)
To: Yao, Jiewen
Cc: jejb@linux.ibm.com, devel@edk2.groups.io, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
Hi,
> 1. " the PEI domain has very limited exposure, it's the DXE domain that has full exposure "
> [Jiewen] I don’t understand how that is concluded, on " limited exposure ", " full exposure ".
exposure == "the need to process external input, which an attacker might
use to exploit bugs in edk2 by crafting input data accordingly."
There isn't much external input to process in PEI phase. Virtual
machines are a bit different than physical machines. They need to
process some input from the host here which describes the virtual
hardware so they can initialize it properly. For example parse the
etc/e820 fw_cfg file to figure how much memory is installed (or parse
the td hob in case tdx is used).
That platform-specific code for virtual machine initialization must do
careful sanity checking when you don't want trust the VMM of course.
Whenever that code lives in SEC or PEI doesn't change the picture much
though.
> 2. "bugs in PEI code can't be used to exploit the system when it has transitioned to the DXE domain."
> [Jiewen] I disagree. A bug in PEI code may already modify the HOB, while the HOB is an architecture data input for DXE.
> If DXE relies on some malicious data from PEI, DXE might be exploited later.
Attacking PEI is harder though because the external input it
processes is limited when compared to DXE.
Once you are transitioned to DXE you can't call into PEI Modules
any more.
So, how would an attacker trick PEI code into modifying HOBs (or
carrying out other actions under attackers control)?
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-24 8:12 ` Gerd Hoffmann
@ 2021-11-24 11:08 ` Yao, Jiewen
2021-11-24 13:35 ` James Bottomley
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-24 11:08 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: jejb@linux.ibm.com, devel@edk2.groups.io, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
> -----Original Message-----
> From: Gerd Hoffmann <kraxel@redhat.com>
> Sent: Wednesday, November 24, 2021 4:12 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: jejb@linux.ibm.com; devel@edk2.groups.io; Xu, Min M
> <min.m.xu@intel.com>; Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>;
> Erdem Aktas <erdemaktas@google.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> Hi,
>
> > 1. " the PEI domain has very limited exposure, it's the DXE domain that has full
> exposure "
> > [Jiewen] I don’t understand how that is concluded, on " limited exposure ", "
> full exposure ".
>
> exposure == "the need to process external input, which an attacker might
> use to exploit bugs in edk2 by crafting input data accordingly."
[Jiewen] Thanks.
>
> There isn't much external input to process in PEI phase. Virtual
> machines are a bit different than physical machines. They need to
> process some input from the host here which describes the virtual
> hardware so they can initialize it properly. For example parse the
> etc/e820 fw_cfg file to figure how much memory is installed (or parse
> the td hob in case tdx is used).
>
> That platform-specific code for virtual machine initialization must do
> careful sanity checking when you don't want trust the VMM of course.
> Whenever that code lives in SEC or PEI doesn't change the picture much
> though.
[Jiewen] I am not sure what information you are trying to convey.
I never said that TDVF don’t need check the input after remove PEI.
The check is always needed, no matter in PEI or SEC. I totally agree.
However, what I want to say is PEI has much more unnecessary code than SEC.
You never know the quality of the those unnecessary code. Maybe it has a security bug, but it is just not triggered.
For example, can you guarantee PEI code has not bug? Or DXE IPL has not bug?
What I did is a process of risk management - if PEI is removed, I don’t need care the risk brought by PEI.
Unless you can prove that the risk of PEI is zero, then the risk is there.
>
> > 2. "bugs in PEI code can't be used to exploit the system when it has
> transitioned to the DXE domain."
> > [Jiewen] I disagree. A bug in PEI code may already modify the HOB, while the
> HOB is an architecture data input for DXE.
> > If DXE relies on some malicious data from PEI, DXE might be exploited later.
>
> Attacking PEI is harder though because the external input it
> processes is limited when compared to DXE.
>
> Once you are transitioned to DXE you can't call into PEI Modules
> any more.
>
> So, how would an attacker trick PEI code into modifying HOBs (or
> carrying out other actions under attackers control)?
[Jiewen] I would disagree " Attacking PEI is harder though because the external input it processes is limited when compared to DXE "
First, the number of extern input != the difficulty of the attack.
Second, the number of extern input != the severity of the consequence.
On how to trick PEI, if you search the public UEFI BIOS attack history in the web, you can find example.
The attack simply used an integer overflow, to cause buffer overflow.
And the buffer overflow can be used to inject shellcode, then gain the execution privilege.
Finally, it can control the whole system.
Modifying HOB is not a difficult task, as long as there is proper vulnerability, being used to gain a memory write.
In security world, we always say: Even if you cannot do it, you cannot assume other people cannot do it.
Unless you can prove it cannot be done. Otherwise, anything might be possible.
That is why people are in favor of crypto notation and formal verification to prove something is correct.
>
> take care,
> Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-24 11:08 ` Yao, Jiewen
@ 2021-11-24 13:35 ` James Bottomley
2021-11-24 14:03 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: James Bottomley @ 2021-11-24 13:35 UTC (permalink / raw)
To: devel, jiewen.yao, Gerd Hoffmann
Cc: Xu, Min M, Ard Biesheuvel, Justen, Jordan L, Brijesh Singh,
Erdem Aktas, Tom Lendacky
On Wed, 2021-11-24 at 11:08 +0000, Yao, Jiewen wrote:
> > -----Original Message-----
> > From: Gerd Hoffmann <kraxel@redhat.com>
[...]
> > There isn't much external input to process in PEI phase. Virtual
> > machines are a bit different than physical machines. They need to
> > process some input from the host here which describes the virtual
> > hardware so they can initialize it properly. For example parse the
> > etc/e820 fw_cfg file to figure how much memory is installed (or
> > parse the td hob in case tdx is used).
> >
> > That platform-specific code for virtual machine initialization must
> > do careful sanity checking when you don't want trust the VMM of
> > course. Whenever that code lives in SEC or PEI doesn't change the
> > picture much though.
I don't disagree on this because we don't have a rom root of trust in
OVMF ... however it matters for most of the rest of edk2
> [Jiewen] I am not sure what information you are trying to convey.
> I never said that TDVF don’t need check the input after remove PEI.
> The check is always needed, no matter in PEI or SEC. I totally agree.
>
> However, what I want to say is PEI has much more unnecessary code
> than SEC.
> You never know the quality of the those unnecessary code. Maybe it
> has a security bug, but it is just not triggered.
> For example, can you guarantee PEI code has not bug? Or DXE IPL has
> not bug?
Code removal to thin down the image is orthogonal to the location of
that code ... I don't think anyone objected to securing the path
through PEI by reducing unnecessary code; the doubt is that the
elimination of PEI somehow improves security.
>
> What I did is a process of risk management - if PEI is removed, I
> don’t need care the risk brought by PEI.
Well, as I said above, you can remove the unnecessary code in PEI (and
SEC and DXE). However, once you've done that, you don't eliminate risk
by removing PEI because all you do is move the necessary code that was
in PEI to somewhere else. If you move it to SEC, I agree with Gerd
that it doesn't alter the security position much because SEC is a low
exposure domain like PEI. If you move it to DXE it actually decreases
your security measurably because DXE is a high exposure security
domain.
> Unless you can prove that the risk of PEI is zero, then the risk is
> there.
But you don't make it zero by eliminating PEI, you simply move the risk
elsewhere because the necessary code still has to execute.
> > > 2. "bugs in PEI code can't be used to exploit the system when it
> > > has transitioned to the DXE domain."
> > > [Jiewen] I disagree. A bug in PEI code may already modify the
> > > HOB, while the HOB is an architecture data input for DXE.
> > > If DXE relies on some malicious data from PEI, DXE might be
> > > exploited later.
> >
> > Attacking PEI is harder though because the external input it
> > processes is limited when compared to DXE.
> >
> > Once you are transitioned to DXE you can't call into PEI Modules
> > any more.
> >
> > So, how would an attacker trick PEI code into modifying HOBs (or
> > carrying out other actions under attackers control)?
>
> [Jiewen] I would disagree " Attacking PEI is harder though because
> the external input it processes is limited when compared to DXE "
> First, the number of extern input != the difficulty of the attack.
> Second, the number of extern input != the severity of the
> consequence.
Attacking PEI is harder than attacking DXE because of the limited
exposure to external influences. It doesn't mean it can't be done but
it does mean exploiting the same code can be harder or impossible in
PEI compared to DXE.
The key point is there are some classes of bug that can't be exploited
in PEI because of the limited exposure. These bugs can be exploited in
DXE because of the wider exposure. This is why moving code from DXE to
PEI increases the risk.
> On how to trick PEI, if you search the public UEFI BIOS attack
> history in the web, you can find example.
> The attack simply used an integer overflow, to cause buffer overflow.
> And the buffer overflow can be used to inject shellcode, then gain
> the execution privilege.
> Finally, it can control the whole system.
>
> Modifying HOB is not a difficult task, as long as there is proper
> vulnerability, being used to gain a memory write.
>
> In security world, we always say: Even if you cannot do it, you
> cannot assume other people cannot do it.
> Unless you can prove it cannot be done. Otherwise, anything might be
> possible.
>
> That is why people are in favor of crypto notation and formal
> verification to prove something is correct.
There are many ways of improving security. Formal verification has its
place, but grows exponentially harder with the complexity of the
system. Separation into multiple security domains is also a common
technique (which also facilitates formal verification because it
reduces the state space). What I worry about is that elimination of
one of the UEFI security domains causes code to move to places that
makes it more exploitable.
James
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-24 13:35 ` James Bottomley
@ 2021-11-24 14:03 ` Yao, Jiewen
2021-11-24 14:07 ` James Bottomley
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-24 14:03 UTC (permalink / raw)
To: devel@edk2.groups.io, jejb@linux.ibm.com, Gerd Hoffmann
Cc: Xu, Min M, Ard Biesheuvel, Justen, Jordan L, Brijesh Singh,
Erdem Aktas, Tom Lendacky
James
I am sorry that it is hard for me to understand your point.
To be honest, I am not sure what is objective on the discussion.
Are you question the general threat model analysis on UEFI PI architecture?
Or are you trying to persuade me we should include PEI in TDVF, because you think it is safer to add code in PEI ?
Or something else?
Please enlighten me that.
Thank you
Yao Jiewen
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of James
> Bottomley
> Sent: Wednesday, November 24, 2021 9:35 PM
> To: devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>; Gerd
> Hoffmann <kraxel@redhat.com>
> Cc: Xu, Min M <min.m.xu@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> Brijesh Singh <brijesh.singh@amd.com>; Erdem Aktas
> <erdemaktas@google.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> On Wed, 2021-11-24 at 11:08 +0000, Yao, Jiewen wrote:
> > > -----Original Message-----
> > > From: Gerd Hoffmann <kraxel@redhat.com>
> [...]
> > > There isn't much external input to process in PEI phase. Virtual
> > > machines are a bit different than physical machines. They need to
> > > process some input from the host here which describes the virtual
> > > hardware so they can initialize it properly. For example parse the
> > > etc/e820 fw_cfg file to figure how much memory is installed (or
> > > parse the td hob in case tdx is used).
> > >
> > > That platform-specific code for virtual machine initialization must
> > > do careful sanity checking when you don't want trust the VMM of
> > > course. Whenever that code lives in SEC or PEI doesn't change the
> > > picture much though.
>
> I don't disagree on this because we don't have a rom root of trust in
> OVMF ... however it matters for most of the rest of edk2
>
> > [Jiewen] I am not sure what information you are trying to convey.
> > I never said that TDVF don’t need check the input after remove PEI.
> > The check is always needed, no matter in PEI or SEC. I totally agree.
> >
> > However, what I want to say is PEI has much more unnecessary code
> > than SEC.
> > You never know the quality of the those unnecessary code. Maybe it
> > has a security bug, but it is just not triggered.
> > For example, can you guarantee PEI code has not bug? Or DXE IPL has
> > not bug?
>
> Code removal to thin down the image is orthogonal to the location of
> that code ... I don't think anyone objected to securing the path
> through PEI by reducing unnecessary code; the doubt is that the
> elimination of PEI somehow improves security.
>
> >
> > What I did is a process of risk management - if PEI is removed, I
> > don’t need care the risk brought by PEI.
>
> Well, as I said above, you can remove the unnecessary code in PEI (and
> SEC and DXE). However, once you've done that, you don't eliminate risk
> by removing PEI because all you do is move the necessary code that was
> in PEI to somewhere else. If you move it to SEC, I agree with Gerd
> that it doesn't alter the security position much because SEC is a low
> exposure domain like PEI. If you move it to DXE it actually decreases
> your security measurably because DXE is a high exposure security
> domain.
>
> > Unless you can prove that the risk of PEI is zero, then the risk is
> > there.
>
> But you don't make it zero by eliminating PEI, you simply move the risk
> elsewhere because the necessary code still has to execute.
>
> > > > 2. "bugs in PEI code can't be used to exploit the system when it
> > > > has transitioned to the DXE domain."
> > > > [Jiewen] I disagree. A bug in PEI code may already modify the
> > > > HOB, while the HOB is an architecture data input for DXE.
> > > > If DXE relies on some malicious data from PEI, DXE might be
> > > > exploited later.
> > >
> > > Attacking PEI is harder though because the external input it
> > > processes is limited when compared to DXE.
> > >
> > > Once you are transitioned to DXE you can't call into PEI Modules
> > > any more.
> > >
> > > So, how would an attacker trick PEI code into modifying HOBs (or
> > > carrying out other actions under attackers control)?
> >
> > [Jiewen] I would disagree " Attacking PEI is harder though because
> > the external input it processes is limited when compared to DXE "
> > First, the number of extern input != the difficulty of the attack.
> > Second, the number of extern input != the severity of the
> > consequence.
>
> Attacking PEI is harder than attacking DXE because of the limited
> exposure to external influences. It doesn't mean it can't be done but
> it does mean exploiting the same code can be harder or impossible in
> PEI compared to DXE.
>
> The key point is there are some classes of bug that can't be exploited
> in PEI because of the limited exposure. These bugs can be exploited in
> DXE because of the wider exposure. This is why moving code from DXE to
> PEI increases the risk.
>
> > On how to trick PEI, if you search the public UEFI BIOS attack
> > history in the web, you can find example.
> > The attack simply used an integer overflow, to cause buffer overflow.
> > And the buffer overflow can be used to inject shellcode, then gain
> > the execution privilege.
> > Finally, it can control the whole system.
> >
> > Modifying HOB is not a difficult task, as long as there is proper
> > vulnerability, being used to gain a memory write.
> >
> > In security world, we always say: Even if you cannot do it, you
> > cannot assume other people cannot do it.
> > Unless you can prove it cannot be done. Otherwise, anything might be
> > possible.
> >
> > That is why people are in favor of crypto notation and formal
> > verification to prove something is correct.
>
> There are many ways of improving security. Formal verification has its
> place, but grows exponentially harder with the complexity of the
> system. Separation into multiple security domains is also a common
> technique (which also facilitates formal verification because it
> reduces the state space). What I worry about is that elimination of
> one of the UEFI security domains causes code to move to places that
> makes it more exploitable.
>
> James
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-24 14:03 ` Yao, Jiewen
@ 2021-11-24 14:07 ` James Bottomley
2021-11-24 14:59 ` Yao, Jiewen
[not found] ` <16BA8381113E7B1B.22735@groups.io>
0 siblings, 2 replies; 107+ messages in thread
From: James Bottomley @ 2021-11-24 14:07 UTC (permalink / raw)
To: Yao, Jiewen, devel@edk2.groups.io, Gerd Hoffmann
Cc: Xu, Min M, Ard Biesheuvel, Justen, Jordan L, Brijesh Singh,
Erdem Aktas, Tom Lendacky
On Wed, 2021-11-24 at 14:03 +0000, Yao, Jiewen wrote:
> James
> I am sorry that it is hard for me to understand your point.
>
> To be honest, I am not sure what is objective on the discussion.
> Are you question the general threat model analysis on UEFI PI
> architecture?
The object is for me to understand why you think eliminating PEI
improves security because I think it moves it in the opposite
direction.
> Or are you trying to persuade me we should include PEI in TDVF,
> because you think it is safer to add code in PEI ?
> Or something else?
>
> Please enlighten me that.
Somewhere a decision was taken to remove PEI from the OVMF that is used
to bring up TDX on the grounds of "improving security". I'm struggling
to understand the rationale for this.
James
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-24 14:07 ` James Bottomley
@ 2021-11-24 14:59 ` Yao, Jiewen
2021-11-25 8:32 ` Gerd Hoffmann
[not found] ` <16BA8381113E7B1B.22735@groups.io>
1 sibling, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-24 14:59 UTC (permalink / raw)
To: devel@edk2.groups.io, jejb@linux.ibm.com, Gerd Hoffmann
Cc: Xu, Min M, Ard Biesheuvel, Justen, Jordan L, Brijesh Singh,
Erdem Aktas, Tom Lendacky
OK. Got it.
Let me explain it in more detail.
Let's assume PEI phase include 3 major classes {PEI Core, PEI Arch PEM*, Feature X*}. * means 0~multiple.
To all of us what really matter is Feature X, the existence of PEI Core + PEI Arch PEIM* is to support Feature X*.
From architecture perspective, if a platform is complex (e.g. there are lots of Feature X*) and feature X* have lots of inter-dependency, then PEI is a good place to coordinate the Feature X*. (Example, Feature X* are memory init and silicon init)
But if a platform simple (e.g. there is only few Feature X*) and feature X* have no much dependency, the including PEI does not bring too much value. That is why you see multiple platforms in EDKII does not include PEI.
From security perspective, Feature X* shall always perform check, no matter where the feature X sits in SEC, PEI or DXE. The risk of Feature X always exists, no matter where the feature X sits in SEC, PEI or DXE. I completely agree.
At same time, the PEI Core + PEI Arch PEIM* also bring unknown security risk. That was TRUE in history. It did happen. So my motivation to remove PEI phase is to reduce the risk introduced by PEI Core + PEI Arch PEIM*. Again, I do not mean to reduce the risk introduced by Feature X.
Now it seems we are really debating two things: (please correct me if I am wrong)
1) What is risk introduced by PEI Core + PEI Arch PEIM* ?
2) What is the delta of risk by moving Feature X from PEI to other place (SEC or DXE) ?
For 1), my answer is that the risk is definitely bigger than zero, based upon history data. (This is an objective answer.) That is the main of my motivation to make it become zero by removing PEI.
For 2), my answer is that the delta is almost 0, based upon my experience. (I admit this is a subjective answer, because I cannot prove.). We are trying our best to reduce the risk of the Feature A* as well. Assuming delta of risk <= risk, then it will become very smaller.
So, my judgement is by removing PEI, we can reduce the risk introduce by PEI Core + PEI Arch PEIM*. Reducing code == Reducing Security Risk.
Also, this gives us a chance to focus on reviewing Feature X itself, instead of the complex interaction with PEI Core + PEI Arch PEIM*. Reducing complexity == Reducing Security Risk.
(In history, we got lots of complain on the complexity of the non-deterministic flow by CALLBACK and NOTIFY function in Core. A feature developer might not have idea on when the code will be called, and what the system status is at that moment.)
Thank you
Yao Jiewen
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of James
> Bottomley
> Sent: Wednesday, November 24, 2021 10:07 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Gerd
> Hoffmann <kraxel@redhat.com>
> Cc: Xu, Min M <min.m.xu@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> Brijesh Singh <brijesh.singh@amd.com>; Erdem Aktas
> <erdemaktas@google.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> On Wed, 2021-11-24 at 14:03 +0000, Yao, Jiewen wrote:
> > James
> > I am sorry that it is hard for me to understand your point.
> >
> > To be honest, I am not sure what is objective on the discussion.
> > Are you question the general threat model analysis on UEFI PI
> > architecture?
>
> The object is for me to understand why you think eliminating PEI
> improves security because I think it moves it in the opposite
> direction.
>
> > Or are you trying to persuade me we should include PEI in TDVF,
> > because you think it is safer to add code in PEI ?
> > Or something else?
> >
> > Please enlighten me that.
>
> Somewhere a decision was taken to remove PEI from the OVMF that is used
> to bring up TDX on the grounds of "improving security". I'm struggling
> to understand the rationale for this.
>
> James
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-24 14:59 ` Yao, Jiewen
@ 2021-11-25 8:32 ` Gerd Hoffmann
2021-11-26 6:29 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-25 8:32 UTC (permalink / raw)
To: Yao, Jiewen
Cc: devel@edk2.groups.io, jejb@linux.ibm.com, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
Hi,
> So, my judgement is by removing PEI, we can reduce the risk introduce
> by PEI Core + PEI Arch PEIM*. Reducing code == Reducing Security Risk.
Yes, PEI Core goes away.
No, PEI Arch PEIM (aka OvmfPkg/PlatformPei) wouldn't go away, you would
only move the code to SEC or DXE phase, the platform initialization has
to happen somewhere.
Moving code to DXE has its problems as outlines by James at length.
Moving code to SEC has its problems too. SEC is a much more restricted
environment. A direct consequence is that you have re-invented
multiprocessor job scheduling (using tdx mailbox) instead of using
standard mp service for parallel accept. I do not account that as
"reducing complexity". And I've not yet seen the other changes you
have done for pei-less tdvf ...
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-25 8:32 ` Gerd Hoffmann
@ 2021-11-26 6:29 ` Yao, Jiewen
2021-12-01 13:55 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-26 6:29 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com
Cc: jejb@linux.ibm.com, Xu, Min M, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, Tom Lendacky
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Gerd
> Hoffmann
> Sent: Thursday, November 25, 2021 4:32 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: devel@edk2.groups.io; jejb@linux.ibm.com; Xu, Min M
> <min.m.xu@intel.com>; Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>;
> Erdem Aktas <erdemaktas@google.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> Hi,
>
> > So, my judgement is by removing PEI, we can reduce the risk introduce
> > by PEI Core + PEI Arch PEIM*. Reducing code == Reducing Security Risk.
>
> Yes, PEI Core goes away.
>
> No, PEI Arch PEIM (aka OvmfPkg/PlatformPei) wouldn't go away, you would
> only move the code to SEC or DXE phase, the platform initialization has
> to happen somewhere.
[Jiewen] "Arch PEIM" refers to the PEIM required by the PEI Core.
The "platform initialization" is FeatureX. It is something you have to do, no mater you have PEI or non-PEI.
> Moving code to DXE has its problems as outlines by James at length.
[Jiewen] I don't think this statement is right, with assumption that your interpretation for James' "exposure" is correct: "Exposure == External Interface", and assumption that we are discussing general design principle.
I give my reason below:
1) From architecture perspective.
Please refer to PI specification 1.7A (https://uefi.org/sites/default/files/resources/PI_Spec_1_7_A_final_May1.pdf)
Section 2.1 - Introduction.
"Philosophically, the PEI phase is intended to be the thinnest amount of code to achieve the ends
listed above. As such, any more sophisticated algorithms or processing should be deferred to the
DXE phase of execution."
In history, the first EFI 1.02 implementation does not have PEI.
PEI was added, just because the EFI need permanent RAM, while it is not TRUE for the platform that requires DRAM init.
If the DRAM init simple, then you don't PEI, because it can be done in SEC.
PEI was invented, because the memory init becomes more and more complicated and has more dependency. We want to have a better architecture to support that.
The *intention* of PEI is *only* to initialize the environment for the EFI. That is why it is called as "Pre-EFI-initialization".
This is very similar to the coreboot - ROM stage and RAM stage. (You can treat PEI as ROM stage and DXE as RAM stage)
PEI should only include memory init (biggest part) and related code. If you don't have a strong reason to put a feature to PEI, then you had better to put it to DXE, according to the architecture direction.
2) From security perspective.
I am not convinced that "exposure (external interface)" is a good reason to decide where the module should be.
Thinking of this, if you prefer to put a module to the PEI because PEI has *less* exposure, then PEI will have *more* exposure after that.
If you move half of DXE features to PEI, then PEI has same exposure as DXE. What benefit we can get?
In EDKII, the general security guideline is that module location should be based upon "privilege", following the security principle - "least privilege".
If a module does not requires high privilege, then it should be put to lower privilege location.
In one of my email, I listed Trust Region (TR) concept. I copy them here again.
(I added SEC/BDS/RT to them as well, because I miss that in the first email)
================
Second, in EDKII, we have similar concept - we call trust region (TR):
1) Recovery Trust Region (SEC, PEI)
2) Main Trust Region (DXE-before EndOfDxe)
3) MM Trust Region (SMM)
4) Boot Trust Region (DXE w/o CSM-after EndOfDxe, BDS)
5) Legacy Trust Region (DXE with CSM-after EndOfDxe, BDS)
6) OS Trust Region (OS Boot, RT)
We use term "transition" to describe the domain jump. We don't use term
"isolation".
We use "isolation" where two co-existed RT cannot tamper each other. For
example, MM trust region and Boot Trust Region are isolated.
Actually, the only isolation example we have in BIOS is x86 SMM or ARM
TrustZone.
================
For example, SMM has much smaller exposure (external interface) than DXE.
But SMM has much higher privilege than DXE by design. However, it does not make sense to move feature from DXE to SMM.
All direction we have is to remove module from SMM to DXE, because the privilege concern, regardless of the exposure.
The SEC and PEI are almost same from TR perspective, they are in recovery TR.
Recovery TR has a little high privilege than Main TR, because there are some security LOCK activities happened in Recovery TR.
For a specific case, PEI might be a better place. I agree. For example, flash lock. I will definitely vote to put it to PEI.
But in general, I don't see why moving feature from PEI to DXE becomes a problem, unless you can explain in detail what the "problem" is.
> Moving code to SEC has its problems too. SEC is a much more restricted
> environment. A direct consequence is that you have re-invented
> multiprocessor job scheduling (using tdx mailbox) instead of using
> standard mp service for parallel accept. I do not account that as
> "reducing complexity".
[Jiewen] OK. Let me explain multiprocessor related topic.
I don't think we intent to "reduce" complexity in this area. I would say, it is same with or without PEI.
TDX (also SEV) has special requirement to *accept* memory, before use it. The accepting is time consuming process.
So the motivation is to use multiprocessor to accelerate the process.
We have at least three architecture places to accept the memory - SEC, PEI and DXE.
A) Accept in SEC
We need write multiprocessor code in SEC. This is already mandatory, even without accepting memory.
The TDX architecture already changes the reset vector flow - ALL processors jumps to the reset vector at same time.
Having multiprocessor code in SEC is unavoidable. We have to do it, to rendez-vous APs and initialize mailbox.
The code is written because of TDX architecture change, not because memory accepting. So I won't treat it as a burden to add additional feature to memory accept in SEC.
At same time, the benefic to accept memory in SEC is significant, you can have memory ready for use. I will explain later why that matters.
B) Accept in PEI
PEI has MP_PPI, that is TRUE. But the problem is: MP_PPI starts *later*.
MP_PPI starts *after* the memory discovery (https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/CpuMpPei/CpuMpPei.c#L706), which mean the process will be:
Step 1: TdxPeim accepts PEI required memory without MP_PPI
Step 2: PlatformPei installs PEI required memory.
Step 3: MP_PPI starts.
Step 4: TdxPeim accepts additional memory with MP_PPI
Now, you can see the benefit to accept PEI memory is not there.
NOTE: PEI memory is ~64M if GPAW is 48, it is ~98M if GPAW is 52, which is a big number.
That mean, with the solution you proposed (use PEI MP_PPI), we will have trouble on boot performance.
C) Accept in DXE
Almost same as PEI.
You have to accept some memory before launch MP_SERVICE. Same boot performance issue.
As such, we conclude that doing memory accept in SEC is the best choice for TDX architecture.
You may argue that we re-invented is MP work in SEC.
Right, but this is done because of TDX architecture.
It is NOT related to config-A (w/ PEI) or config-B (w/o PEI).
> And I've not yet seen the other changes you
> have done for pei-less tdvf ...
[Jiewen] That is because there is no many change. :-)
Here is just some early code.
1) Platform Init
In config-B, it is at https://github.com/tianocore/edk2-staging/blob/TDVF/OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c
In config-A, it is at https://github.com/mxu9/edk2/blob/tdvf_wave2.v3/OvmfPkg/PlatformPei/Platform.c
2) DXE hand off
In config-B, it is at https://github.com/tianocore/edk2-staging/blob/TDVF/OvmfPkg/Library/TdxStartupLib/DxeLoad.c
In config-A, it is at https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c
The reset should be almost same in from SEC/PEI perspective.
I don't see a reason to carry whole PEI just to run platform init (~300 line of code).
For config-B, Min is working on it and doing clean up.
You are welcome to provide feedback after we send out patch for config-B.
> take care,
> Gerd
>
>
>
>
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-11-26 6:29 ` Yao, Jiewen
@ 2021-12-01 13:55 ` Gerd Hoffmann
2021-12-02 13:22 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-12-01 13:55 UTC (permalink / raw)
To: Yao, Jiewen
Cc: devel@edk2.groups.io, jejb@linux.ibm.com, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
Hi,
> Please refer to PI specification 1.7A (https://uefi.org/sites/default/files/resources/PI_Spec_1_7_A_final_May1.pdf)
> Section 2.1 - Introduction.
> "Philosophically, the PEI phase is intended to be the thinnest amount
> of code to achieve the ends listed above. As such, any more
> sophisticated algorithms or processing should be deferred to the DXE
> phase of execution."
Well, that might have been the original intention. Reality is that a
bunch of security-related stuff ended up in PEI too. Support for TPM,
SMM, suspend. And I think the motivation for that is exactly what James
described: Given that most communication with external entities happens
in the DXE phase it is much harder to trick PEI code because there are
simply less attack vectors available.
> I am not convinced that "exposure (external interface)" is a good
> reason to decide where the module should be. Thinking of this, if you
> prefer to put a module to the PEI because PEI has *less* exposure,
> then PEI will have *more* exposure after that. If you move half of
> DXE features to PEI, then PEI has same exposure as DXE. What benefit
> we can get?
Why is TPM and SMM and suspend support in PEI not DXE today?
> > Moving code to SEC has its problems too. SEC is a much more restricted
> > environment. A direct consequence is that you have re-invented
> > multiprocessor job scheduling (using tdx mailbox) instead of using
> > standard mp service for parallel accept. I do not account that as
> > "reducing complexity".
>
> [Jiewen] OK. Let me explain multiprocessor related topic.
>
> I don't think we intent to "reduce" complexity in this area. I would
> say, it is same with or without PEI.
>
> TDX (also SEV) has special requirement to *accept* memory, before use
> it. The accepting is time consuming process. So the motivation is to
> use multiprocessor to accelerate the process.
>
> We have at least three architecture places to accept the memory - SEC,
> PEI and DXE.
Well, I want focus on how all this will look like long-term, i.e. once
we have lazy accept implemented. I don't think it makes sense to put
much effort into optimizing a workflow which will only be temporary
anyway.
The lazy accept concept pretty much implies that the vast majority of
memory will either be accepted in the DXE phase, or will not be accepted
by the firmware at all and the linux kernel will handle that instead.
I expect in the future SEC and/or PEI will accept barely enough memory
that the firmware is able to enter the DXE phase and initialize core
memory management. Then lazy accept takes over.
> A) Accept in SEC
> We need write multiprocessor code in SEC. This is already mandatory,
> even without accepting memory. The TDX architecture already changes
> the reset vector flow - ALL processors jumps to the reset vector at
> same time. Having multiprocessor code in SEC is unavoidable. We have
> to do it, to rendez-vous APs and initialize mailbox.
Sure.
> The code is written because of TDX architecture change, not because
> memory accepting. So I won't treat it as a burden to add additional
> feature to memory accept in SEC.
That is the point where you start re-inventing the wheel though.
You add logic to distribute memory acceptance jobs to the APs.
I'd suggest to add full MP service support to TDX (probably also using
the mailbox), then use MP service to distribute memory acceptance jobs
to the APs. I think you will need that anyway for lazy accept, to do
parallel accept in DXE phase.
> B) Accept in PEI
> PEI has MP_PPI, that is TRUE. But the problem is: MP_PPI starts *later*.
> MP_PPI starts *after* the memory discovery (https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/CpuMpPei/CpuMpPei.c#L706), which mean the process will be:
> Step 1: TdxPeim accepts PEI required memory without MP_PPI
> Step 2: PlatformPei installs PEI required memory.
> Step 3: MP_PPI starts.
> Step 4: TdxPeim accepts additional memory with MP_PPI
Yes. Or just don't initialize MP in PEI and do that in DXE only.
Lazy accept will need that anyway.
> Now, you can see the benefit to accept PEI memory is not there.
> NOTE: PEI memory is ~64M if GPAW is 48, it is ~98M if GPAW is 52, which is a big number.
What is all this memory needed for?
Why do you need 32M additional memory for 5-level paging?
Is that page table memory? If so, how does lazy accept change the
picture?
> As such, we conclude that doing memory accept in SEC is the best
> choice for TDX architecture.
Not convinced this is still the case with lazy accept on the horizon.
> For config-B, Min is working on it and doing clean up.
> You are welcome to provide feedback after we send out patch for config-B.
Will do for sure.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-12-01 13:55 ` Gerd Hoffmann
@ 2021-12-02 13:22 ` Yao, Jiewen
2021-12-06 14:57 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-12-02 13:22 UTC (permalink / raw)
To: kraxel@redhat.com
Cc: devel@edk2.groups.io, jejb@linux.ibm.com, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
> -----Original Message-----
> From: kraxel@redhat.com <kraxel@redhat.com>
> Sent: Wednesday, December 1, 2021 9:55 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: devel@edk2.groups.io; jejb@linux.ibm.com; Xu, Min M
> <min.m.xu@intel.com>; Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>;
> Erdem Aktas <erdemaktas@google.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> Hi,
>
> > Please refer to PI specification 1.7A
> (https://uefi.org/sites/default/files/resources/PI_Spec_1_7_A_final_May1.pdf)
> > Section 2.1 - Introduction.
>
> > "Philosophically, the PEI phase is intended to be the thinnest amount
> > of code to achieve the ends listed above. As such, any more
> > sophisticated algorithms or processing should be deferred to the DXE
> > phase of execution."
>
> Well, that might have been the original intention. Reality is that a
> bunch of security-related stuff ended up in PEI too. Support for TPM,
> SMM, suspend. And I think the motivation for that is exactly what James
> described: Given that most communication with external entities happens
> in the DXE phase it is much harder to trick PEI code because there are
> simply less attack vectors available.
[Jiewen] Again, I feel lost.
Would you please clarify what is your purpose for this discussion?
Yes, Security related stuff in PEI, that is not a problem. For example, we moved flash lock from DXE to PEI. (I already describe that in my previous email.)
The key is *privilege*. If you don't need PEI privilege, you don't need move to PEI.
1) For "TPM" feature. In history, the Root-of-trust for measurement (RTM) belongs to Recovery TR. That is why it is in PEI naturally.
2) "SMM" support is in DXE today. What do you mean SMM support in PEI ?
3) "Suspend" is S3 *feature*, it is clearly documented in PI spec. In S3, DXE does not run. You have to put Suspend code in PEI.
Yes, you give some examples.
But I don't see how the examples support your statement on "exposure".
IMHO, they are totally unrelated.
> > I am not convinced that "exposure (external interface)" is a good
> > reason to decide where the module should be. Thinking of this, if you
> > prefer to put a module to the PEI because PEI has *less* exposure,
> > then PEI will have *more* exposure after that. If you move half of
> > DXE features to PEI, then PEI has same exposure as DXE. What benefit
> > we can get?
>
> Why is TPM and SMM and suspend support in PEI not DXE today?
[Jiewen] See above.
>
> > > Moving code to SEC has its problems too. SEC is a much more restricted
> > > environment. A direct consequence is that you have re-invented
> > > multiprocessor job scheduling (using tdx mailbox) instead of using
> > > standard mp service for parallel accept. I do not account that as
> > > "reducing complexity".
> >
> > [Jiewen] OK. Let me explain multiprocessor related topic.
> >
> > I don't think we intent to "reduce" complexity in this area. I would
> > say, it is same with or without PEI.
> >
> > TDX (also SEV) has special requirement to *accept* memory, before use
> > it. The accepting is time consuming process. So the motivation is to
> > use multiprocessor to accelerate the process.
> >
> > We have at least three architecture places to accept the memory - SEC,
> > PEI and DXE.
>
> Well, I want focus on how all this will look like long-term, i.e. once
> we have lazy accept implemented. I don't think it makes sense to put
> much effort into optimizing a workflow which will only be temporary
> anyway.
[Jiewen] This is the long-term solution.
Lazy accept and MP accept are two required feature.
"Lazy accept" just mean you can do things later, but you still need do it.
"MP accept" means you can do things much quicker.
I don't think we can remove MP accept just because we have lazy accept.
> The lazy accept concept pretty much implies that the vast majority of
> memory will either be accepted in the DXE phase, or will not be accepted
> by the firmware at all and the linux kernel will handle that instead.
>
> I expect in the future SEC and/or PEI will accept barely enough memory
> that the firmware is able to enter the DXE phase and initialize core
> memory management. Then lazy accept takes over.
[Jiewen] the "barely enough memory" is 64M and it takes long time to accept if you don't use MP.
That is how SEC/PEI/DXE designed today.
> > A) Accept in SEC
> > We need write multiprocessor code in SEC. This is already mandatory,
> > even without accepting memory. The TDX architecture already changes
> > the reset vector flow - ALL processors jumps to the reset vector at
> > same time. Having multiprocessor code in SEC is unavoidable. We have
> > to do it, to rendez-vous APs and initialize mailbox.
>
> Sure.
>
> > The code is written because of TDX architecture change, not because
> > memory accepting. So I won't treat it as a burden to add additional
> > feature to memory accept in SEC.
>
> That is the point where you start re-inventing the wheel though.
> You add logic to distribute memory acceptance jobs to the APs.
> I'd suggest to add full MP service support to TDX (probably also using
> the mailbox), then use MP service to distribute memory acceptance jobs
> to the APs. I think you will need that anyway for lazy accept, to do
> parallel accept in DXE phase.
[Jiewen] I think I have stated it clearly that this is due to TDX architecture, we have to rendezvous all APs in SEC.
So the MP code SEC is unavoidable. We have to reinvent the wheel in some way.
>
> > B) Accept in PEI
> > PEI has MP_PPI, that is TRUE. But the problem is: MP_PPI starts *later*.
> > MP_PPI starts *after* the memory discovery
> (https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/CpuMpPei/CpuM
> pPei.c#L706), which mean the process will be:
> > Step 1: TdxPeim accepts PEI required memory without MP_PPI
> > Step 2: PlatformPei installs PEI required memory.
> > Step 3: MP_PPI starts.
> > Step 4: TdxPeim accepts additional memory with MP_PPI
>
> Yes. Or just don't initialize MP in PEI and do that in DXE only.
> Lazy accept will need that anyway.
>
> > Now, you can see the benefit to accept PEI memory is not there.
> > NOTE: PEI memory is ~64M if GPAW is 48, it is ~98M if GPAW is 52, which is a
> big number.
>
> What is all this memory needed for?
[Jiewen] These are initial memory for PEI Core and DXE Core to initialize the content.
If you don't have initial memory, the core will fail.
> Why do you need 32M additional memory for 5-level paging?
> Is that page table memory? If so, how does lazy accept change the
> picture?
>
> > As such, we conclude that doing memory accept in SEC is the best
> > choice for TDX architecture.
>
> Not convinced this is still the case with lazy accept on the horizon.
[Jiewen] I think MP accept is irrelevant to lazy accept, as I explained before.
Having "lazy accept" != you can drop "MP accept" in SEC, because SEC is to "accept initial memory" before you can do lazy.
>
> > For config-B, Min is working on it and doing clean up.
> > You are welcome to provide feedback after we send out patch for config-B.
>
> Will do for sure.
>
> take care,
> Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-12-02 13:22 ` Yao, Jiewen
@ 2021-12-06 14:57 ` Gerd Hoffmann
2021-12-07 2:28 ` Yao, Jiewen
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-12-06 14:57 UTC (permalink / raw)
To: Yao, Jiewen
Cc: devel@edk2.groups.io, jejb@linux.ibm.com, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
Hi,
> [Jiewen] Again, I feel lost.
>
> Would you please clarify what is your purpose for this discussion?
>
> Yes, Security related stuff in PEI, that is not a problem. For
> example, we moved flash lock from DXE to PEI. (I already describe that
> in my previous email.)
Well, you tried to make the point that PEI shouldn't do anything beyond
memory initialization. Which might have been correct for the initial
design, but meanwhile it is not true any more.
> The key is *privilege*. If you don't need PEI privilege, you don't need move to PEI.
> 2) "SMM" support is in DXE today. What do you mean SMM support in PEI ?
ovmf has a pei module for smm support (see
OvmfPkg/SmmAccess/SmmAccessPei.inf).
> But I don't see how the examples support your statement on "exposure".
Well, lets stick to the flash lock example. Moving it from DXE to PEI
makes it less exposed. It'll run before DXE starts processing
externally controlled input (efi vars, kbd input, disk reads, option
roms, ...). So trying trick it into not actually locking the flash is
much harder.
Or, to put it into another way, it reduces the attack surface for the
code with higher privilege.
(it's of course also need to make sure you can't unlock flash with a
suspend-resume cycle).
> > Well, I want focus on how all this will look like long-term, i.e. once
> > we have lazy accept implemented. I don't think it makes sense to put
> > much effort into optimizing a workflow which will only be temporary
> > anyway.
>
> [Jiewen] This is the long-term solution.
> Lazy accept and MP accept are two required feature.
>
> "Lazy accept" just mean you can do things later, but you still need do it.
> "MP accept" means you can do things much quicker.
>
> I don't think we can remove MP accept just because we have lazy accept.
I don't want remove it. But with lazy accept you have more options to
implement it. No need to go for assembler code in SEC, you can use MP
service with standard C code in PEI or DXE.
> [Jiewen] the "barely enough memory" is 64M and it takes long time to
> accept if you don't use MP.
If I remember the numbers correctly (roughly 4 seconds for 2G on a
single processor) that "long time" would be roughly 12 ms for 64M.
> > That is the point where you start re-inventing the wheel though.
> > You add logic to distribute memory acceptance jobs to the APs.
> > I'd suggest to add full MP service support to TDX (probably also using
> > the mailbox), then use MP service to distribute memory acceptance jobs
> > to the APs. I think you will need that anyway for lazy accept, to do
> > parallel accept in DXE phase.
>
> [Jiewen] I think I have stated it clearly that this is due to TDX
> architecture, we have to rendezvous all APs in SEC. So the MP code
> SEC is unavoidable. We have to reinvent the wheel in some way.
Well, adding TDX rendezvous support isn't re-inventing the wheel, you
surely need that, no question.
But then you go create your own job scheduler (also accept job
implementation), all in assembler, instead of using standard edk2 MP
services with more readable C code. *That* is where you re-invent the
wheel.
> > > Now, you can see the benefit to accept PEI memory is not there.
> > > NOTE: PEI memory is ~64M if GPAW is 48, it is ~98M if GPAW is 52, which is a
> > big number.
> >
> > What is all this memory needed for?
>
> [Jiewen] These are initial memory for PEI Core and DXE Core to initialize the content.
> If you don't have initial memory, the core will fail.
Where does the 50% increase for GPAW=52 comes from?
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-12-06 14:57 ` Gerd Hoffmann
@ 2021-12-07 2:28 ` Yao, Jiewen
2021-12-07 8:04 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Yao, Jiewen @ 2021-12-07 2:28 UTC (permalink / raw)
To: kraxel@redhat.com
Cc: devel@edk2.groups.io, jejb@linux.ibm.com, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
> -----Original Message-----
> From: kraxel@redhat.com <kraxel@redhat.com>
> Sent: Monday, December 6, 2021 10:58 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: devel@edk2.groups.io; jejb@linux.ibm.com; Xu, Min M
> <min.m.xu@intel.com>; Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>;
> Erdem Aktas <erdemaktas@google.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> Hi,
>
> > [Jiewen] Again, I feel lost.
> >
> > Would you please clarify what is your purpose for this discussion?
> >
> > Yes, Security related stuff in PEI, that is not a problem. For
> > example, we moved flash lock from DXE to PEI. (I already describe that
> > in my previous email.)
>
> Well, you tried to make the point that PEI shouldn't do anything beyond
> memory initialization. Which might have been correct for the initial
> design, but meanwhile it is not true any more.
[Jiewen] No, I am not making this point.
My point is : It is legal to move a feature from PEI to DXE.
My understanding is that: you are making point - "It is problematic that moving a feature from PEI to DXE". I take "feature" as "any generic feature".
I accept the statement that "It *may* be problematic that moving a *specific (such as security)* feature from PEI to DXE".
But I disagree the general statement that moving any feature from PEI to DXE will cause problem.
For PEI, my point is
1) According to PI spec, PEI is minimal so it is legal to move feature from PEI to DXE.
2) We do see some examples that we move from DXE to PEI, but that is case by case. It does not mean you cannot move feature from PEI to DXE.
>
> > The key is *privilege*. If you don't need PEI privilege, you don't need move to
> PEI.
>
> > 2) "SMM" support is in DXE today. What do you mean SMM support in PEI ?
>
> ovmf has a pei module for smm support (see
> OvmfPkg/SmmAccess/SmmAccessPei.inf).
[Jiewen] The SmmAccessPei is for S3 resume. It is already documented to PI spec.
I don't see anything wrong here.
> > But I don't see how the examples support your statement on "exposure".
>
> Well, lets stick to the flash lock example. Moving it from DXE to PEI
> makes it less exposed. It'll run before DXE starts processing
> externally controlled input (efi vars, kbd input, disk reads, option
> roms, ...). So trying trick it into not actually locking the flash is
> much harder.
[Jiewen] I am tired on using word "exposure". You gave the definition that "exposure == external interface".
And your statement is that we should move "feature" to the component with less exposure. - I take "feature" as "any generic feature".
In my opinion, less exposure != high privilege. There might be some relationship. A high privilege module may have less exposure usually. However, the privilege is *cause*, less exposure is *consequence*. Not verse versa. We made judgement based upon privilege, not the exposure.
I accept the statement that "We should move a *security* feature to component with *high privilege*".
But I disagree the general statement that we should moving any feature to less exposure.
I give a counter example - SMM. SMM has less external interface, but we cannot move "any generic feature" to SMM.
>
> Or, to put it into another way, it reduces the attack surface for the
> code with higher privilege.
>
> (it's of course also need to make sure you can't unlock flash with a
> suspend-resume cycle).
>
> > > Well, I want focus on how all this will look like long-term, i.e. once
> > > we have lazy accept implemented. I don't think it makes sense to put
> > > much effort into optimizing a workflow which will only be temporary
> > > anyway.
> >
> > [Jiewen] This is the long-term solution.
> > Lazy accept and MP accept are two required feature.
> >
> > "Lazy accept" just mean you can do things later, but you still need do it.
> > "MP accept" means you can do things much quicker.
> >
> > I don't think we can remove MP accept just because we have lazy accept.
>
> I don't want remove it. But with lazy accept you have more options to
> implement it. No need to go for assembler code in SEC, you can use MP
> service with standard C code in PEI or DXE.
>
> > [Jiewen] the "barely enough memory" is 64M and it takes long time to
> > accept if you don't use MP.
>
> If I remember the numbers correctly (roughly 4 seconds for 2G on a
> single processor) that "long time" would be roughly 12 ms for 64M.
[Jiewen] OK, I talked with Min again. 12ms is not right data today.
We have bigger number, but I cannot share the data according to legal reason.
But I agree with your statement that, if the data is small enough, then we don't need MP in sec.
I propose this way:
1) In first patch, we drop MP in SEC.
2) We can revisit if it is really needed later, when the TDX platform is about to launch.
I believe we will have more precise data at that moment.
>
> > > That is the point where you start re-inventing the wheel though.
> > > You add logic to distribute memory acceptance jobs to the APs.
> > > I'd suggest to add full MP service support to TDX (probably also using
> > > the mailbox), then use MP service to distribute memory acceptance jobs
> > > to the APs. I think you will need that anyway for lazy accept, to do
> > > parallel accept in DXE phase.
> >
>
> > [Jiewen] I think I have stated it clearly that this is due to TDX
> > architecture, we have to rendezvous all APs in SEC. So the MP code
> > SEC is unavoidable. We have to reinvent the wheel in some way.
>
> Well, adding TDX rendezvous support isn't re-inventing the wheel, you
> surely need that, no question.
>
> But then you go create your own job scheduler (also accept job
> implementation), all in assembler, instead of using standard edk2 MP
> services with more readable C code. *That* is where you re-invent the
> wheel.
>
> > > > Now, you can see the benefit to accept PEI memory is not there.
> > > > NOTE: PEI memory is ~64M if GPAW is 48, it is ~98M if GPAW is 52, which is
> a
> > > big number.
> > >
> > > What is all this memory needed for?
> >
> > [Jiewen] These are initial memory for PEI Core and DXE Core to initialize the
> content.
> > If you don't have initial memory, the core will fail.
>
> Where does the 50% increase for GPAW=52 comes from?
[Jiewen] Yes, this is about page table.
The reason is that UEFI spec requires you to map all memory. You have to create page table for all.
>
> take care,
> Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-12-07 2:28 ` Yao, Jiewen
@ 2021-12-07 8:04 ` Gerd Hoffmann
2021-12-08 5:13 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-12-07 8:04 UTC (permalink / raw)
To: Yao, Jiewen
Cc: devel@edk2.groups.io, jejb@linux.ibm.com, Xu, Min M,
Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
Tom Lendacky
Hi,
> [Jiewen] OK, I talked with Min again. 12ms is not right data today.
> We have bigger number, but I cannot share the data according to legal reason.
>
> But I agree with your statement that, if the data is small enough, then we don't need MP in sec.
>
> I propose this way:
> 1) In first patch, we drop MP in SEC.
Yes. Next implement lazy accept ...
> 2) We can revisit if it is really needed later, when the TDX platform is about to launch.
... then revisit where we stand in terms of boot performance.
And, yes, doing that on the final tdx platform hardware instead
of preliminary development hardware makes sense too.
> > Where does the 50% increase for GPAW=52 comes from?
>
> [Jiewen] Yes, this is about page table.
> The reason is that UEFI spec requires you to map all memory. You have to create page table for all.
Seems that has changed with the latest (2.9) revision of the specs which
explicitly excludes unaccepted memory. From section 2.3.4:
Paging mode is enabled and any memory space defined by the UEFI
memory map is identity mapped (virtual address equals physical
address), although the attributes of certain regions may not have
all read, write, and execute attributes or be unmarked for purposes
of platform protection. The mappings to other regions, such as those
for unaccepted memory, are undefined and may vary from
implementation to implementation.
So implementing lazy accept should bring the initial memory footprint
down because page tables for unaccepted memory are not needed in
SEC/PEI. We can lazily allocate them in DXE instead when accepting
memory (either all memory, or just enough to load the linux kernel and
have linux accept the remaining memory).
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
2021-12-07 8:04 ` Gerd Hoffmann
@ 2021-12-08 5:13 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-12-08 5:13 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com, Yao, Jiewen
Cc: jejb@linux.ibm.com, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, Tom Lendacky
On December 7, 2021 4:05 PM, Gerd Hoffmann wrote:
> > [Jiewen] OK, I talked with Min again. 12ms is not right data today.
> > We have bigger number, but I cannot share the data according to legal
> reason.
> >
> > But I agree with your statement that, if the data is small enough, then we
> don't need MP in sec.
> >
> > I propose this way:
> > 1) In first patch, we drop MP in SEC.
>
> Yes. Next implement lazy accept ...
>
> > 2) We can revisit if it is really needed later, when the TDX platform is about
> to launch.
>
> ... then revisit where we stand in terms of boot performance.
>
> And, yes, doing that on the final tdx platform hardware instead of preliminary
> development hardware makes sense too.
>
> > > Where does the 50% increase for GPAW=52 comes from?
> >
> > [Jiewen] Yes, this is about page table.
> > The reason is that UEFI spec requires you to map all memory. You have to
> create page table for all.
>
> Seems that has changed with the latest (2.9) revision of the specs which
> explicitly excludes unaccepted memory. From section 2.3.4:
>
> Paging mode is enabled and any memory space defined by the UEFI
> memory map is identity mapped (virtual address equals physical
> address), although the attributes of certain regions may not have
> all read, write, and execute attributes or be unmarked for purposes
> of platform protection. The mappings to other regions, such as those
> for unaccepted memory, are undefined and may vary from
> implementation to implementation.
>
> So implementing lazy accept should bring the initial memory footprint down
> because page tables for unaccepted memory are not needed in SEC/PEI. We
> can lazily allocate them in DXE instead when accepting memory (either all
> memory, or just enough to load the linux kernel and have linux accept the
> remaining memory).
So as the first step I will submit the patch-set without MP in SEC. Lazy accept will be a separate patch-set after that.
In the Lazy accept there are changes in multiply places, such as accept initial memory, publish pei memory, transfer to DXE hob list, DXE Core GCD services, Shell(memmap). Also we have to consider accept more memory in Pool/Page functions when OOM occurs.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
[parent not found: <16BA8381113E7B1B.22735@groups.io>]
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
[not found] ` <16BA8381113E7B1B.22735@groups.io>
@ 2021-11-24 15:30 ` Yao, Jiewen
0 siblings, 0 replies; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-24 15:30 UTC (permalink / raw)
To: devel@edk2.groups.io, Yao, Jiewen, jejb@linux.ibm.com,
Gerd Hoffmann
Cc: Xu, Min M, Ard Biesheuvel, Justen, Jordan L, Brijesh Singh,
Erdem Aktas, Tom Lendacky
One more clarification: My comment below is only applicable for the TDVF platform, but not applicable to a general platform including OVMF.
In TDVF, Feature X* is a very small set, but in OVMF or general platform, Feature X* is a large set.
For example, if a platform need support S3 resume, Recovery, Capsule Update, then I won't recommend to remove PEI.
The reason is 2) the delta of risk becomes high then. Current PEI already provides a mature (and complex) infrastructure for them.
Moving those feature to somewhere else means to carry the burden to reinvent the infrastructure for S3, recovery, capsule update.
Here, I only recommend to remove for TDVF config-B, because the Feature X* is so simple that it could be moved to SEC without extra risk.
Removing PEI for general OVMF is a different topic. I don’t want to discuss in this thread.
Thank you
Yao Jiewen
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Yao, Jiewen
> Sent: Wednesday, November 24, 2021 11:00 PM
> To: devel@edk2.groups.io; jejb@linux.ibm.com; Gerd Hoffmann
> <kraxel@redhat.com>
> Cc: Xu, Min M <min.m.xu@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> Brijesh Singh <brijesh.singh@amd.com>; Erdem Aktas
> <erdemaktas@google.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> OK. Got it.
> Let me explain it in more detail.
>
> Let's assume PEI phase include 3 major classes {PEI Core, PEI Arch PEM*,
> Feature X*}. * means 0~multiple.
> To all of us what really matter is Feature X, the existence of PEI Core + PEI Arch
> PEIM* is to support Feature X*.
>
> From architecture perspective, if a platform is complex (e.g. there are lots of
> Feature X*) and feature X* have lots of inter-dependency, then PEI is a good
> place to coordinate the Feature X*. (Example, Feature X* are memory init and
> silicon init)
> But if a platform simple (e.g. there is only few Feature X*) and feature X* have
> no much dependency, the including PEI does not bring too much value. That is
> why you see multiple platforms in EDKII does not include PEI.
>
> From security perspective, Feature X* shall always perform check, no matter
> where the feature X sits in SEC, PEI or DXE. The risk of Feature X always exists,
> no matter where the feature X sits in SEC, PEI or DXE. I completely agree.
> At same time, the PEI Core + PEI Arch PEIM* also bring unknown security risk.
> That was TRUE in history. It did happen. So my motivation to remove PEI phase
> is to reduce the risk introduced by PEI Core + PEI Arch PEIM*. Again, I do not
> mean to reduce the risk introduced by Feature X.
>
> Now it seems we are really debating two things: (please correct me if I am
> wrong)
> 1) What is risk introduced by PEI Core + PEI Arch PEIM* ?
> 2) What is the delta of risk by moving Feature X from PEI to other place (SEC or
> DXE) ?
>
> For 1), my answer is that the risk is definitely bigger than zero, based upon
> history data. (This is an objective answer.) That is the main of my motivation to
> make it become zero by removing PEI.
> For 2), my answer is that the delta is almost 0, based upon my experience. (I
> admit this is a subjective answer, because I cannot prove.). We are trying our
> best to reduce the risk of the Feature A* as well. Assuming delta of risk <= risk,
> then it will become very smaller.
>
> So, my judgement is by removing PEI, we can reduce the risk introduce by PEI
> Core + PEI Arch PEIM*. Reducing code == Reducing Security Risk.
> Also, this gives us a chance to focus on reviewing Feature X itself, instead of the
> complex interaction with PEI Core + PEI Arch PEIM*. Reducing complexity ==
> Reducing Security Risk.
> (In history, we got lots of complain on the complexity of the non-deterministic
> flow by CALLBACK and NOTIFY function in Core. A feature developer might not
> have idea on when the code will be called, and what the system status is at that
> moment.)
>
>
> Thank you
> Yao Jiewen
>
>
> > -----Original Message-----
> > From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of James
> > Bottomley
> > Sent: Wednesday, November 24, 2021 10:07 PM
> > To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Gerd
> > Hoffmann <kraxel@redhat.com>
> > Cc: Xu, Min M <min.m.xu@intel.com>; Ard Biesheuvel
> > <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> > Brijesh Singh <brijesh.singh@amd.com>; Erdem Aktas
> > <erdemaktas@google.com>; Tom Lendacky <thomas.lendacky@amd.com>
> > Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm
> to
> > support Tdx
> >
> > On Wed, 2021-11-24 at 14:03 +0000, Yao, Jiewen wrote:
> > > James
> > > I am sorry that it is hard for me to understand your point.
> > >
> > > To be honest, I am not sure what is objective on the discussion.
> > > Are you question the general threat model analysis on UEFI PI
> > > architecture?
> >
> > The object is for me to understand why you think eliminating PEI
> > improves security because I think it moves it in the opposite
> > direction.
> >
> > > Or are you trying to persuade me we should include PEI in TDVF,
> > > because you think it is safer to add code in PEI ?
> > > Or something else?
> > >
> > > Please enlighten me that.
> >
> > Somewhere a decision was taken to remove PEI from the OVMF that is used
> > to bring up TDX on the grounds of "improving security". I'm struggling
> > to understand the rationale for this.
> >
> > James
> >
> >
> >
> >
> >
> >
>
>
>
>
>
^ permalink raw reply [flat|nested] 107+ messages in thread
[parent not found: <16BA5D1709524394.9880@groups.io>]
* Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx
[not found] ` <16BA5D1709524394.9880@groups.io>
@ 2021-11-24 3:21 ` Yao, Jiewen
0 siblings, 0 replies; 107+ messages in thread
From: Yao, Jiewen @ 2021-11-24 3:21 UTC (permalink / raw)
To: devel@edk2.groups.io, Yao, Jiewen, jejb@linux.ibm.com
Cc: Gerd Hoffmann, Xu, Min M, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, Tom Lendacky
My apology - fix typo: objection on the discussion => objective on the discussion.
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Yao, Jiewen
> Sent: Wednesday, November 24, 2021 11:16 AM
> To: jejb@linux.ibm.com; devel@edk2.groups.io
> Cc: Gerd Hoffmann <kraxel@redhat.com>; Xu, Min M <min.m.xu@intel.com>;
> Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>; Erdem
> Aktas <erdemaktas@google.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to
> support Tdx
>
> I think we are discussing under different context.
>
> First, the term "isolation" shall be clarified.
> In my context, "isolation" means two domain cannot impact each other.
> The isolation is enforced by a 3rd higher privileged component. Examples: Ring3
> apps are isolated by OS. TDs are isolated by TDX Module. That is why I say: there
> is no isolation.
>
> In your context, if one domain jumps to another domain and never jump back,
> then you call it "isolation".
>
>
> Second, in EDKII, we have similar concept - we call trust region (TR):
> 1) Recovery Trust Region (PEI)
> 2) Main Trust Region (DXE-before EndOfDxe)
> 3) MM Trust Region (SMM)
> 4) Boot Trust Region (DXE w/o CSM-after EndOfDxe)
> 5) Legacy Trust Region (DXE with CSM-after EndOfDxe)
> 6) OS Trust Region (OS Boot)
>
> We use term "transition" to describe the domain jump. We don’t use term
> "isolation".
> We use "isolation" where two co-existed RT cannot tamper each other. For
> example, MM trust region and Boot Trust Region are isolated.
> Actually, the only isolation example we have in BIOS is x86 SMM or ARM
> TrustZone.
>
> We have below security assumption:
> 1) What can be trusted? The later launched TR can trust the earlier TR. Here
> "trust" means "use data input without validation"
> For example:
> 1.1) Main TR can trust the input from Recovery TR.
> 1.2) MM RT can trust the input from Main TR.
>
> 2) What cannot be trusted? Here "not trust" means "validate data input before
> use "
> For example:
> 2.1) MM RT cannot trust the input from Boot TR.
> 2.2) Recovery RT cannot trust the input from Boot TR.
>
> However, TR just means a region definition to help us do security analysis.
> It is NOT related to any security exposure, severity, or exploitability.
> There is no conclusion that a bug in PEI is more or less exploitable than DXE or
> SMM.
>
>
> Here, I have comment for the sentence below:
>
> 1. " the PEI domain has very limited exposure, it's the DXE domain that has full
> exposure "
> [Jiewen] I don’t understand how that is concluded, on " limited exposure ", " full
> exposure ".
>
> 2. "bugs in PEI code can't be used to exploit the system when it has transitioned
> to the DXE domain."
> [Jiewen] I disagree. A bug in PEI code may already modify the HOB, while the
> HOB is an architecture data input for DXE.
> If DXE relies on some malicious data from PEI, DXE might be exploited later.
>
> 3. " but it does mean that there are fewer exploitability classes in PEI than DXE
> because the security domain is much less exposed."
> [Jiewen] I don’t understand how that is concluded, on "fewer", "less".
> In history, there are security bugs in PEI and there are security bugs in DXE. I
> won't say fewer or less.
> Also because we use *LOCK* mechanism, and some LOCKs are enforced in PEI
> phase.
> A bug in PEI might be more severe than a bug in DXE.
>
>
>
> Hi James
> Sorry, I am a little lost now.
> To be honest, I am not sure what is objective on the discussion.
> Are you question the general threat model analysis on UEFI PI architecture?
> Or are you trying to persuade me we should include PEI in TDVF, because you
> think it is safer to add code in PEI ?
> Or something else?
>
> Please enlighten me that.
>
>
> Thank you
> Yao, Jiewen
>
>
> > -----Original Message-----
> > From: James Bottomley <jejb@linux.ibm.com>
> > Sent: Tuesday, November 23, 2021 11:38 PM
> > To: devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
> > Cc: Gerd Hoffmann <kraxel@redhat.com>; Xu, Min M <min.m.xu@intel.com>;
> > Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> > <jordan.l.justen@intel.com>; Brijesh Singh <brijesh.singh@amd.com>; Erdem
> > Aktas <erdemaktas@google.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> > Subject: Re: [edk2-devel] [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm
> to
> > support Tdx
> >
> > On Tue, 2021-11-23 at 15:10 +0000, Yao, Jiewen wrote:
> > > I would say the PEI owns the system and all memory (including the
> > > DXE).
> > >
> > > A bug in PEI may override the loaded DXE memory or the whole system.
> >
> > That's not the correct way to analyse the security properties. From
> > the security point of view this is a trapdoor system: once you go
> > through the door, you can't go back (the trapdoor being the jump from
> > PEI to DXE). The trapdoor isolates the domains and allows you to
> > analyse the security properties of each separately. It also allows
> > separation of exposure ... which is what we use in this case: the PEI
> > domain has very limited exposure, it's the DXE domain that has full
> > exposure but, because of the trapdoor, bugs in PEI code can't be used
> > to exploit the system when it has transitioned to the DXE domain.
> >
> > > In history I did see PEI security issues.
> > > Some security issue in PEI caused system compromised completely. You
> > > even have no chance to run DXE.
> >
> > The security domain analysis above doesn't mean no bug in PEI is ever
> > exploitable but it does mean that there are fewer exploitability
> > classes in PEI than DXE because the security domain is much less
> > exposed.
> >
> > James
> >
>
>
>
>
>
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 16/29] OvmfPkg: Add IntelTdx.h in OvmfPkg/Include/IndustryStandard
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (14 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-01 13:16 ` [PATCH V3 17/29] OvmfPkg: Add TdxMailboxLib Min Xu
` (12 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
IntelTdx.h defines the defitions used by TDX in OvmfPkg:
- Mailbox related defitions,such as the data structure, command code,
AP relocation defitions.
- EFI_HOB_PLATFORM_INFO describes the TDX platform information
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/Include/IndustryStandard/IntelTdx.h | 77 +++++++++++++++++++++
1 file changed, 77 insertions(+)
create mode 100644 OvmfPkg/Include/IndustryStandard/IntelTdx.h
diff --git a/OvmfPkg/Include/IndustryStandard/IntelTdx.h b/OvmfPkg/Include/IndustryStandard/IntelTdx.h
new file mode 100644
index 000000000000..2370f18289a1
--- /dev/null
+++ b/OvmfPkg/Include/IndustryStandard/IntelTdx.h
@@ -0,0 +1,77 @@
+/** @file
+ TBD
+
+ Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _OVMF_INTEL_TDX__H_
+#define _OVMF_INTEL_TDX__H_
+
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Uefi/UefiSpec.h>
+#include <Uefi/UefiBaseType.h>
+
+#define MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID 0xFFFFFFFF
+#define MP_CPU_PROTECTED_MODE_MAILBOX_APICID_BROADCAST 0xFFFFFFFE
+
+typedef enum {
+ MpProtectedModeWakeupCommandNoop = 0,
+ MpProtectedModeWakeupCommandWakeup = 1,
+ MpProtectedModeWakeupCommandSleep = 2,
+ MpProtectedModeWakeupCommandAcceptPages = 3,
+} MP_CPU_PROTECTED_MODE_WAKEUP_CMD;
+
+#pragma pack (1)
+
+ //
+ // Describes the CPU MAILBOX control structure use to
+ // wakeup cpus spinning in long mode
+ //
+ typedef struct {
+ UINT16 Command;
+ UINT16 Resv;
+ UINT32 ApicId;
+ UINT64 WakeUpVector;
+ UINT8 ResvForOs[2032];
+ //
+ // Arguments available for wakeup code
+ //
+ UINT64 WakeUpArgs1;
+ UINT64 WakeUpArgs2;
+ UINT64 WakeUpArgs3;
+ UINT64 WakeUpArgs4;
+ UINT8 Pad1[0xe0];
+ UINT64 NumCpusArriving;
+ UINT8 Pad2[0xf8];
+ UINT64 NumCpusExiting;
+ UINT32 Tallies[256];
+ UINT8 Errors[256];
+ UINT8 Pad3[0xf8];
+ } MP_WAKEUP_MAILBOX;
+
+
+//
+// AP relocation code information including code address and size,
+// this structure will be shared be C code and assembly code.
+// It is natural aligned by design.
+//
+typedef struct {
+ UINT8 *RelocateApLoopFuncAddress;
+ UINTN RelocateApLoopFuncSize;
+} MP_RELOCATION_MAP;
+
+typedef struct {
+ ///
+ EFI_HOB_GUID_TYPE GuidHeader;
+ UINT64 RelocatedMailBox;
+ UINT16 HostBridgePciDevId;
+ BOOLEAN SetNxForStack;
+ UINT8 SystemStates[6];
+} EFI_HOB_PLATFORM_INFO;
+
+#pragma pack()
+
+#endif
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 17/29] OvmfPkg: Add TdxMailboxLib
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (15 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 16/29] OvmfPkg: Add IntelTdx.h in OvmfPkg/Include/IndustryStandard Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-01 13:16 ` [PATCH V3 18/29] MdePkg: Add EFI_RESOURCE_ATTRIBUTE_ENCRYPTED in PiHob.h Min Xu
` (11 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In Tdx BSP may issues commands to APs for some task, for example, to
accept pages paralelly. BSP also need to wait until all the APs have
done the task. TdxMailboxLib wraps these common funtions for BSP.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/Include/Library/TdxMailboxLib.h | 75 ++++++++++
OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c | 138 ++++++++++++++++++
.../Library/TdxMailboxLib/TdxMailboxLib.inf | 52 +++++++
.../Library/TdxMailboxLib/TdxMailboxNull.c | 86 +++++++++++
OvmfPkg/OvmfPkg.dec | 4 +
5 files changed, 355 insertions(+)
create mode 100644 OvmfPkg/Include/Library/TdxMailboxLib.h
create mode 100644 OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c
create mode 100644 OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
create mode 100644 OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c
diff --git a/OvmfPkg/Include/Library/TdxMailboxLib.h b/OvmfPkg/Include/Library/TdxMailboxLib.h
new file mode 100644
index 000000000000..a410a9618495
--- /dev/null
+++ b/OvmfPkg/Include/Library/TdxMailboxLib.h
@@ -0,0 +1,75 @@
+/** @file
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef __TDX_MAILBOX_LIB_H__
+#define __TDX_MAILBOX_LIB_H__
+
+#include <Library/BaseLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Uefi/UefiSpec.h>
+#include <Pi/PiPeiCis.h>
+#include <Library/DebugLib.h>
+#include <Protocol/DebugSupport.h>
+
+/**
+ This function will be called by BSP to get the CPU number.
+
+ @retval CPU number
+**/
+UINT32
+EFIAPI
+GetCpusNum (
+ VOID
+);
+
+/**
+ Get the address of Td mailbox.
+**/
+volatile VOID *
+EFIAPI
+GetTdxMailBox (
+ VOID
+);
+
+/**
+ This function will be called by BSP to wakeup APs the are spinning on mailbox
+ in protected mode
+
+ @param[in] Command Command to send APs
+ @param[in] WakeupVector If used, address for APs to start executing
+ @param[in] WakeArgsX Args to pass to APs for excuting commands
+**/
+VOID
+EFIAPI
+MpSendWakeupCommand(
+ IN UINT16 Command,
+ IN UINT64 WakeupVector,
+ IN UINT64 WakeupArgs1,
+ IN UINT64 WakeupArgs2,
+ IN UINT64 WakeupArgs3,
+ IN UINT64 WakeupArgs4
+);
+
+/**
+ BSP wait until all the APs arriving. It means the task triggered by BSP is started.
+**/
+VOID
+EFIAPI
+MpSerializeStart (
+ VOID
+ );
+
+/**
+ BSP wait until all the APs arriving. It means the task triggered by BSP is ended.
+**/
+VOID
+EFIAPI
+MpSerializeEnd (
+ VOID
+ );
+
+#endif
diff --git a/OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c b/OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c
new file mode 100644
index 000000000000..688ac6ca8262
--- /dev/null
+++ b/OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c
@@ -0,0 +1,138 @@
+/** @file
+
+ Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiCpuLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/TdxLib.h>
+#include <IndustryStandard/IntelTdx.h>
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxMailboxLib.h>
+
+volatile VOID *mMailBox = NULL;
+UINT32 mNumOfCpus = 0;
+
+/**
+ This function will be called by BSP to get the CPU number.
+
+ @retval CPU number
+**/
+UINT32
+EFIAPI
+GetCpusNum (
+ VOID
+ )
+{
+ if (mNumOfCpus == 0) {
+ mNumOfCpus = TdVCpuNum ();
+ }
+
+ return mNumOfCpus;
+}
+
+/**
+ Get the address of Td mailbox.
+**/
+volatile VOID *
+EFIAPI
+GetTdxMailBox (
+ VOID
+ )
+{
+ if (mMailBox == NULL) {
+ mMailBox = (VOID *)(UINTN) PcdGet32 (PcdOvmfSecGhcbBackupBase);
+ }
+
+ return mMailBox;
+}
+
+/**
+ This function will be called by BSP to wakeup APs the are spinning on mailbox
+ in protected mode
+
+ @param[in] Command Command to send APs
+ @param[in] WakeupVector If used, address for APs to start executing
+ @param[in] WakeArgsX Args to pass to APs for excuting commands
+**/
+VOID
+EFIAPI
+MpSendWakeupCommand (
+ IN UINT16 Command,
+ IN UINT64 WakeupVector,
+ IN UINT64 WakeupArgs1,
+ IN UINT64 WakeupArgs2,
+ IN UINT64 WakeupArgs3,
+ IN UINT64 WakeupArgs4
+)
+{
+ volatile MP_WAKEUP_MAILBOX *MailBox;
+
+ MailBox = (volatile MP_WAKEUP_MAILBOX *) GetTdxMailBox ();
+ MailBox->ApicId = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID;
+ MailBox->WakeUpVector = 0;
+ MailBox->Command = MpProtectedModeWakeupCommandNoop;
+ MailBox->ApicId = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_BROADCAST;
+ MailBox->WakeUpVector = WakeupVector;
+ MailBox->WakeUpArgs1 = WakeupArgs1;
+ MailBox->WakeUpArgs2 = WakeupArgs2;
+ MailBox->WakeUpArgs3 = WakeupArgs3;
+ MailBox->WakeUpArgs4 = WakeupArgs4;
+ AsmCpuid (0x01, NULL, NULL, NULL, NULL);
+ MailBox->Command = Command;
+ AsmCpuid (0x01, NULL, NULL, NULL, NULL);
+ return;
+}
+
+/**
+ BSP wait until all the APs arriving. It means the task triggered by BSP is started.
+**/
+VOID
+EFIAPI
+MpSerializeStart (
+ VOID
+ )
+{
+ volatile MP_WAKEUP_MAILBOX *MailBox;
+ UINT32 NumOfCpus;
+
+ NumOfCpus = GetCpusNum ();
+ MailBox = (volatile MP_WAKEUP_MAILBOX *) GetTdxMailBox ();
+
+ DEBUG ((DEBUG_VERBOSE, "Waiting for APs to arriving. NumOfCpus=%d, MailBox=%p\n", NumOfCpus, MailBox));
+ while (MailBox->NumCpusArriving != ( NumOfCpus -1 )) {
+ CpuPause ();
+ }
+ DEBUG ((DEBUG_VERBOSE, "Releasing APs\n"));
+ MailBox->NumCpusExiting = NumOfCpus;
+ InterlockedIncrement ((UINT32 *) &MailBox->NumCpusArriving);
+}
+
+/**
+ BSP wait until all the APs arriving. It means the task triggered by BSP is ended.
+**/
+VOID
+EFIAPI
+MpSerializeEnd (
+ VOID
+ )
+{
+ volatile MP_WAKEUP_MAILBOX *MailBox;
+
+ MailBox = (volatile MP_WAKEUP_MAILBOX *) GetTdxMailBox ();
+ DEBUG ((DEBUG_VERBOSE, "Waiting for APs to finish\n"));
+ while (MailBox->NumCpusExiting != 1 ) {
+ CpuPause ();
+ }
+ DEBUG ((DEBUG_VERBOSE, "Restarting APs\n"));
+ MailBox->Command = MpProtectedModeWakeupCommandNoop;
+ MailBox->NumCpusArriving = 0;
+ InterlockedDecrement ((UINT32 *) &MailBox->NumCpusExiting);
+}
diff --git a/OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
new file mode 100644
index 000000000000..3cf3690a16c7
--- /dev/null
+++ b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
@@ -0,0 +1,52 @@
+#/** @file
+#
+# TBD
+#
+# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2008, Apple Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = TdxMailboxLib
+ FILE_GUID = 2F81A9BA-748E-4519-BB11-A63A039D561E
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TdxMailboxLib
+
+#
+# VALID_ARCHITECTURES = X64 IA32
+#
+
+[Sources.IA32]
+ TdxMailboxNull.c
+
+[Sources.X64]
+ TdxMailbox.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ PcdLib
+ UefiCpuLib
+ DebugAgentLib
+ IoLib
+ SynchronizationLib
+ MemoryAllocationLib
+
+[Guids]
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
diff --git a/OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c
new file mode 100644
index 000000000000..f15222d51f45
--- /dev/null
+++ b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c
@@ -0,0 +1,86 @@
+/** @file
+
+ Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/TdxMailboxLib.h>
+
+
+/**
+ This function will be called by BSP to get the CPU number.
+
+ @retval CPU number
+**/
+UINT32
+EFIAPI
+GetCpusNum (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return 0;
+}
+
+/**
+ Get the address of Td mailbox.
+**/
+volatile VOID *
+EFIAPI
+GetTdxMailBox (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return (volatile VOID *) NULL;
+}
+
+/**
+ This function will be called by BSP to wakeup APs the are spinning on mailbox
+ in protected mode
+
+ @param[in] Command Command to send APs
+ @param[in] WakeupVector If used, address for APs to start executing
+ @param[in] WakeArgsX Args to pass to APs for excuting commands
+**/
+VOID
+EFIAPI
+MpSendWakeupCommand (
+ IN UINT16 Command,
+ IN UINT64 WakeupVector,
+ IN UINT64 WakeupArgs1,
+ IN UINT64 WakeupArgs2,
+ IN UINT64 WakeupArgs3,
+ IN UINT64 WakeupArgs4
+)
+{
+ ASSERT (FALSE);
+}
+
+/**
+ BSP wait until all the APs arriving. It means the task triggered by BSP is started.
+**/
+VOID
+EFIAPI
+MpSerializeStart (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+}
+
+/**
+ BSP wait until all the APs arriving. It means the task triggered by BSP is ended.
+**/
+VOID
+EFIAPI
+MpSerializeEnd (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+}
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 2124bd639399..145943532a74 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -109,6 +109,10 @@
#
XenPlatformLib|Include/Library/XenPlatformLib.h
+ ## @libraryclass TdxMailboxLib
+ #
+ TdxMailboxLib|Include/Library/TdxMailboxLib.h
+
[Guids]
gUefiOvmfPkgTokenSpaceGuid = {0x93bb96af, 0xb9f2, 0x4eb8, {0x94, 0x62, 0xe0, 0xba, 0x74, 0x56, 0x42, 0x36}}
gEfiXenInfoGuid = {0xd3b46f3b, 0xd441, 0x1244, {0x9a, 0x12, 0x0, 0x12, 0x27, 0x3f, 0xc1, 0x4d}}
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 18/29] MdePkg: Add EFI_RESOURCE_ATTRIBUTE_ENCRYPTED in PiHob.h
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (16 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 17/29] OvmfPkg: Add TdxMailboxLib Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-01 13:16 ` [PATCH V3 19/29] OvmfPkg: Enable Tdx in SecMain.c Min Xu
` (10 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
EFI_RESOURCE_ATTRIBUTE_ENCRYPTED is Physical memory encrypted attribute.
It indicates the memory uses platform encrpytion capabilities for
protection. If this bit is clear, the memory does not use platform
encryption protection.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
MdePkg/Include/Pi/PiHob.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MdePkg/Include/Pi/PiHob.h b/MdePkg/Include/Pi/PiHob.h
index 62c07742a688..600ec2d4919e 100644
--- a/MdePkg/Include/Pi/PiHob.h
+++ b/MdePkg/Include/Pi/PiHob.h
@@ -298,6 +298,14 @@ typedef UINT32 EFI_RESOURCE_ATTRIBUTE_TYPE;
//
#define EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE 0x02000000
+//
+// Physical memory encrypted attribute. This
+// memory uses platform encrpytion capabilities for
+// protection. If this bit is clear, the memory does
+// not use platform encryption protection
+//
+#define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000
+
///
/// Describes the resource properties of all fixed,
/// nonrelocatable resource ranges found on the processor
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 19/29] OvmfPkg: Enable Tdx in SecMain.c
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (17 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 18/29] MdePkg: Add EFI_RESOURCE_ATTRIBUTE_ENCRYPTED in PiHob.h Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-01 13:16 ` [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation Min Xu
` (9 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
When host VMM create the Td guest, the system memory informations are
stored in TdHob, which is a memory region described in Tdx metadata.
The system memory region in TdHob should be accepted before it can be
accessed. So the major task of this patch set is to process the TdHobList
to accept the memory. After that TDVF follow the standard OVMF flow
and jump to PEI phase.
PcdTdxAcceptChunkSize & PcdTdxAcceptPageSize are added for page accepting.
They're the default settings of the chunk size and the Accept page size.
PcdUse1GPageTable is set to FALSE by default in OvmfPkgX64.dsc. It gives
no chance for Intel TDX to support 1G page table. To support 1G page
table this PCD is set to TRUE in OvmfPkgX64.dsc.
3 Tdx specific libs are used by OvmfPkgX64:
- TdxLib
- TdxMailboxLib
TDX only works on X64, so the code is only valid in X64 arch.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/OvmfPkg.dec | 6 +
OvmfPkg/OvmfPkgIa32.dsc | 2 +
OvmfPkg/OvmfPkgIa32X64.dsc | 2 +
OvmfPkg/OvmfPkgX64.dsc | 6 +
OvmfPkg/Sec/IntelTdx.c | 597 +++++++++++++++++++++++++++++++++++++
OvmfPkg/Sec/IntelTdx.h | 33 ++
OvmfPkg/Sec/SecMain.c | 60 +++-
OvmfPkg/Sec/SecMain.inf | 6 +
8 files changed, 706 insertions(+), 6 deletions(-)
create mode 100644 OvmfPkg/Sec/IntelTdx.c
create mode 100644 OvmfPkg/Sec/IntelTdx.h
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 145943532a74..ccf8fc33ce27 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -357,6 +357,12 @@
## Ignore the VE halt in Tdx
gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x58
+ ## The chunk size of Tdx accept page
+ gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize|0x2000000|UINT64|0x59
+
+ ## The Tdx accept page size. 0x1000(4k),0x200000(2M)
+ gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize|0x1000|UINT32|0x5a
+
[PcdsDynamic, PcdsDynamicEx]
gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 6a5be97c059d..056055c8d799 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -243,6 +243,8 @@
[LibraryClasses.common]
BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+ TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
+ TdxMailboxLib|OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
[LibraryClasses.common.SEC]
TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 71227d1b709a..27887da8f2a2 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -247,6 +247,8 @@
[LibraryClasses.common]
BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+ TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
+ TdxMailboxLib|OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
[LibraryClasses.common.SEC]
TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 52f7598cf1c7..3eb29811d822 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -247,6 +247,8 @@
[LibraryClasses.common]
BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
+ TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
+ TdxMailboxLib|OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
[LibraryClasses.common.SEC]
TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
@@ -572,6 +574,10 @@
gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|0x100
gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData|0x100
+ #
+ # TDX need 1G PageTable support
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable|TRUE
+
#
# Network Pcds
#
diff --git a/OvmfPkg/Sec/IntelTdx.c b/OvmfPkg/Sec/IntelTdx.c
new file mode 100644
index 000000000000..a70bb61e8ee8
--- /dev/null
+++ b/OvmfPkg/Sec/IntelTdx.c
@@ -0,0 +1,597 @@
+/** @file
+
+ Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Uefi/UefiSpec.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <IndustryStandard/UefiTcgPlatform.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/TdxLib.h>
+#include <Library/TdxMailboxLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <IndustryStandard/IntelTdx.h>
+#include "IntelTdx.h"
+
+#define ALIGNED_2MB_MASK 0x1fffff
+
+/**
+ BSP call this function to accept memory in a range.
+
+ @param[in] StartAddress Start address of the memory region
+ @param[in] Length Length of the memory region
+ @param[in] AcceptChunkSize Accept chunk size
+ @param[in] AcceptPageSize Accept page size
+ @retval EFI_SUCCESS Successfully accept the memory region
+ @retval Others Indicate the other errors
+**/
+EFI_STATUS
+EFIAPI
+BspAcceptMemoryResourceRange (
+ IN EFI_PHYSICAL_ADDRESS StartAddress,
+ IN UINT64 Length,
+ IN UINT64 AcceptChunkSize,
+ IN UINT32 AcceptPageSize
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Pages;
+ UINT64 Stride;
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;
+ volatile MP_WAKEUP_MAILBOX *MailBox;
+
+ Status = EFI_SUCCESS;
+ PhysicalAddress = StartAddress;
+ Stride = GetCpusNum () * AcceptChunkSize;
+ MailBox = (volatile MP_WAKEUP_MAILBOX *) GetTdxMailBox ();
+
+ while (!EFI_ERROR(Status) && PhysicalAddress < StartAddress + Length) {
+ //
+ // Decrease size of near end of resource if needed.
+ //
+ Pages = MIN (AcceptChunkSize, StartAddress + Length - PhysicalAddress) / AcceptPageSize;
+
+ MailBox->Tallies[0] += (UINT32)Pages;
+
+ Status = TdAcceptPages (PhysicalAddress, Pages, AcceptPageSize);
+ //
+ // Bump address to next chunk this cpu is responisble for
+ //
+ PhysicalAddress += Stride;
+ }
+
+ return Status;
+}
+
+/**
+ This function will be called to accept pages. BSP and APs are invokded
+ to do the task together.
+
+ TDCALL(ACCEPT_PAGE) supports the accept page size of 4k and 2M. To
+ simplify the implementation, the Memory to be accpeted is splitted
+ into 3 parts:
+ ----------------- <-- StartAddress1 (not 2M aligned)
+ | part 1 | Length1 < 2M
+ |---------------| <-- StartAddress2 (2M aligned)
+ | | Length2 = Integer multiples of 2M
+ | part 2 |
+ | |
+ |---------------| <-- StartAddress3
+ | part 3 | Length3 < 2M
+ |---------------|
+
+ part 1) will be accepted in 4k and by BSP.
+ Part 2) will be accepted in 2M and by BSP/AP.
+ Part 3) will be accepted in 4k and by BSP.
+
+ @param[in] PhysicalAddress Start physical adress
+ @param[in] PhysicalEnd End physical address
+
+ @retval EFI_SUCCESS Accept memory successfully
+ @retval Others Other errors as indicated
+**/
+EFI_STATUS
+EFIAPI
+MpAcceptMemoryResourceRange (
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddress,
+ IN EFI_PHYSICAL_ADDRESS PhysicalEnd
+ )
+{
+ EFI_STATUS Status;
+ UINT64 AcceptChunkSize;
+ UINT32 AcceptPageSize;
+ UINT64 StartAddress1;
+ UINT64 StartAddress2;
+ UINT64 StartAddress3;
+ UINT64 TotalLength;
+ UINT64 Length1;
+ UINT64 Length2;
+ UINT64 Length3;
+ UINT32 Index;
+ UINT32 CpusNum;
+ volatile MP_WAKEUP_MAILBOX *MailBox;
+
+ AcceptChunkSize = FixedPcdGet64 (PcdTdxAcceptChunkSize);
+ AcceptPageSize = FixedPcdGet32 (PcdTdxAcceptPageSize);
+ TotalLength = PhysicalEnd - PhysicalAddress;
+ StartAddress1 = 0;
+ StartAddress2 = 0;
+ StartAddress3 = 0;
+ Length1 = 0;
+ Length2 = 0;
+ Length3 = 0;
+
+ if (AcceptPageSize == SIZE_4KB || TotalLength <= SIZE_2MB) {
+ //
+ // if total length is less than 2M, then we accept pages in 4k
+ //
+ StartAddress1 = 0;
+ Length1 = 0;
+ StartAddress2 = PhysicalAddress;
+ Length2 = PhysicalEnd - PhysicalAddress;
+ StartAddress3 = 0;
+ Length3 = 0;
+ AcceptPageSize = SIZE_4KB;
+ } else if (AcceptPageSize == SIZE_2MB) {
+ //
+ // Total length is bigger than 2M and Page Accept size 2M is supported.
+ //
+ if ((PhysicalAddress & ALIGNED_2MB_MASK) == 0) {
+ //
+ // Start address is 2M aligned
+ //
+ StartAddress1 = 0;
+ Length1 = 0;
+ StartAddress2 = PhysicalAddress;
+ Length2 = TotalLength & ~(UINT64)ALIGNED_2MB_MASK;
+
+ if (TotalLength > Length2) {
+ //
+ // There is remaining part 3)
+ //
+ StartAddress3 = StartAddress2 + Length2;
+ Length3 = TotalLength - Length2;
+ ASSERT (Length3 < SIZE_2MB);
+ }
+ } else {
+ //
+ // Start address is not 2M aligned and total length is bigger than 2M.
+ //
+ StartAddress1 = PhysicalAddress;
+ ASSERT (TotalLength > SIZE_2MB);
+ Length1 = SIZE_2MB - (PhysicalAddress & ALIGNED_2MB_MASK);
+ if (TotalLength - Length1 < SIZE_2MB) {
+ //
+ // The Part 2) length is less than 2MB, so let's accept all the
+ // memory in 4K
+ //
+ Length1 = TotalLength;
+
+ } else {
+ StartAddress2 = PhysicalAddress + Length1;
+ Length2 = (TotalLength - Length1) & ~(UINT64)ALIGNED_2MB_MASK;
+ Length3 = TotalLength - Length1 - Length2;
+ StartAddress3 = Length3 > 0 ? StartAddress2 + Length2 : 0;
+ ASSERT (Length3 < SIZE_2MB);
+ }
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "TdAccept: 0x%llx - 0x%llx\n", PhysicalAddress, TotalLength));
+ DEBUG ((DEBUG_INFO, " Part1: 0x%llx - 0x%llx\n", StartAddress1, Length1));
+ DEBUG ((DEBUG_INFO, " Part2: 0x%llx - 0x%llx\n", StartAddress2, Length2));
+ DEBUG ((DEBUG_INFO, " Part3: 0x%llx - 0x%llx\n", StartAddress3, Length3));
+ DEBUG ((DEBUG_INFO, " Chunk: 0x%llx, Page : 0x%llx\n", AcceptChunkSize, AcceptPageSize));
+
+ MpSerializeStart ();
+
+ if (Length2 > 0) {
+ MpSendWakeupCommand (
+ MpProtectedModeWakeupCommandAcceptPages,
+ 0,
+ StartAddress2,
+ StartAddress2 + Length2,
+ AcceptChunkSize,
+ AcceptPageSize);
+
+ Status = BspAcceptMemoryResourceRange (
+ StartAddress2,
+ Length2,
+ AcceptChunkSize,
+ AcceptPageSize);
+ ASSERT (!EFI_ERROR (Status));
+ }
+
+ if (Length1 > 0) {
+ Status = BspAcceptMemoryResourceRange (
+ StartAddress1,
+ Length1,
+ AcceptChunkSize,
+ SIZE_4KB);
+ ASSERT (!EFI_ERROR (Status));
+ }
+
+ if (Length3 > 0) {
+ Status = BspAcceptMemoryResourceRange (
+ StartAddress3,
+ Length3,
+ AcceptChunkSize,
+ SIZE_4KB);
+ ASSERT (!EFI_ERROR (Status));
+ }
+
+ MpSerializeEnd ();
+
+ CpusNum = GetCpusNum ();
+ MailBox = (volatile MP_WAKEUP_MAILBOX *) GetTdxMailBox ();
+
+ for (Index = 0; Index < CpusNum; Index++) {
+ if (MailBox->Errors[Index] > 0) {
+ Status = EFI_DEVICE_ERROR;
+ DEBUG ((DEBUG_ERROR, "Error(%d) of CPU-%d when accepting memory\n",
+ MailBox->Errors[Index], Index));
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Dump out the hob list
+
+ @param[in] HobStart Start address of the hob list
+**/
+VOID
+EFIAPI
+DEBUG_HOBLIST (
+ IN CONST VOID *HobStart
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ Hob.Raw = (UINT8 *) HobStart;
+ //
+ // Parse the HOB list until end of list or matching type is found.
+ //
+ while (!END_OF_HOB_LIST (Hob)) {
+ DEBUG ((DEBUG_INFO, "HOB(%p) : %x %x\n", Hob, Hob.Header->HobType, Hob.Header->HobLength));
+ switch (Hob.Header->HobType) {
+ case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:
+ DEBUG ((DEBUG_INFO, "\t: %x %x %llx %llx\n",
+ Hob.ResourceDescriptor->ResourceType,
+ Hob.ResourceDescriptor->ResourceAttribute,
+ Hob.ResourceDescriptor->PhysicalStart,
+ Hob.ResourceDescriptor->ResourceLength));
+
+ break;
+ case EFI_HOB_TYPE_MEMORY_ALLOCATION:
+ DEBUG ((DEBUG_INFO, "\t: %llx %llx %x\n",
+ Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress,
+ Hob.MemoryAllocation->AllocDescriptor.MemoryLength,
+ Hob.MemoryAllocation->AllocDescriptor.MemoryType));
+ break;
+ default:
+ break;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+}
+
+/**
+ Check the value whether in the valid list.
+
+ @param[in] Value A value
+ @param[in] ValidList A pointer to valid list
+ @param[in] ValidListLength Length of valid list
+
+ @retval TRUE The value is in valid list.
+ @retval FALSE The value is not in valid list.
+
+**/
+BOOLEAN
+EFIAPI
+IsInValidList (
+ IN UINT32 Value,
+ IN UINT32 *ValidList,
+ IN UINT32 ValidListLength
+) {
+ UINT32 index;
+
+ if (ValidList == NULL) {
+ return FALSE;
+ }
+
+ for (index = 0; index < ValidListLength; index ++) {
+ if (ValidList[index] == Value) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Check the integrity of VMM Hob List.
+
+ @param[in] VmmHobList A pointer to Hob List
+
+ @retval TRUE The Hob List is valid.
+ @retval FALSE The Hob List is invalid.
+
+**/
+BOOLEAN
+EFIAPI
+ValidateHobList (
+ IN CONST VOID *VmmHobList
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ UINT32 EFI_BOOT_MODE_LIST[12] = { BOOT_WITH_FULL_CONFIGURATION,
+ BOOT_WITH_MINIMAL_CONFIGURATION,
+ BOOT_ASSUMING_NO_CONFIGURATION_CHANGES,
+ BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS,
+ BOOT_WITH_DEFAULT_SETTINGS,
+ BOOT_ON_S4_RESUME,
+ BOOT_ON_S5_RESUME,
+ BOOT_WITH_MFG_MODE_SETTINGS,
+ BOOT_ON_S2_RESUME,
+ BOOT_ON_S3_RESUME,
+ BOOT_ON_FLASH_UPDATE,
+ BOOT_IN_RECOVERY_MODE
+ };
+
+ UINT32 EFI_RESOURCE_TYPE_LIST[8] = { EFI_RESOURCE_SYSTEM_MEMORY,
+ EFI_RESOURCE_MEMORY_MAPPED_IO,
+ EFI_RESOURCE_IO,
+ EFI_RESOURCE_FIRMWARE_DEVICE,
+ EFI_RESOURCE_MEMORY_MAPPED_IO_PORT,
+ EFI_RESOURCE_MEMORY_RESERVED,
+ EFI_RESOURCE_IO_RESERVED,
+ EFI_RESOURCE_MAX_MEMORY_TYPE
+ };
+
+ if (VmmHobList == NULL) {
+ DEBUG ((DEBUG_ERROR, "HOB: HOB data pointer is NULL\n"));
+ return FALSE;
+ }
+
+ Hob.Raw = (UINT8 *) VmmHobList;
+
+ //
+ // Parse the HOB list until end of list or matching type is found.
+ //
+ while (!END_OF_HOB_LIST (Hob)) {
+ if (Hob.Header->Reserved != (UINT32) 0) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob header Reserved filed should be zero\n"));
+ return FALSE;
+ }
+
+ if (Hob.Header->HobLength == 0) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob header LEANGTH should not be zero\n"));
+ return FALSE;
+ }
+
+ switch (Hob.Header->HobType) {
+ case EFI_HOB_TYPE_HANDOFF:
+ if (Hob.Header->HobLength != sizeof(EFI_HOB_HANDOFF_INFO_TABLE)) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_HANDOFF));
+ return FALSE;
+ }
+
+ if (IsInValidList (Hob.HandoffInformationTable->BootMode, EFI_BOOT_MODE_LIST, 12) == FALSE) {
+ DEBUG ((DEBUG_ERROR, "HOB: Unknow HandoffInformationTable BootMode type. Type: 0x%08x\n", Hob.HandoffInformationTable->BootMode));
+ return FALSE;
+ }
+
+ if ((Hob.HandoffInformationTable->EfiFreeMemoryTop % 4096) != 0) {
+ DEBUG ((DEBUG_ERROR, "HOB: HandoffInformationTable EfiFreeMemoryTop address must be 4-KB aligned to meet page restrictions of UEFI.\
+ Address: 0x%016lx\n", Hob.HandoffInformationTable->EfiFreeMemoryTop));
+ return FALSE;
+ }
+ break;
+
+ case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:
+ if (Hob.Header->HobLength != sizeof(EFI_HOB_RESOURCE_DESCRIPTOR)) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_RESOURCE_DESCRIPTOR));
+ return FALSE;
+ }
+
+ if (IsInValidList (Hob.ResourceDescriptor->ResourceType, EFI_RESOURCE_TYPE_LIST, 8) == FALSE) {
+ DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceType type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceType));
+ return FALSE;
+ }
+
+ if ((Hob.ResourceDescriptor->ResourceAttribute & (~(EFI_RESOURCE_ATTRIBUTE_PRESENT |
+ EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+ EFI_RESOURCE_ATTRIBUTE_TESTED |
+ EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |
+ EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED |
+ EFI_RESOURCE_ATTRIBUTE_PERSISTENT |
+ EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC |
+ EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC |
+ EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 |
+ EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 |
+ EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+ EFI_RESOURCE_ATTRIBUTE_16_BIT_IO |
+ EFI_RESOURCE_ATTRIBUTE_32_BIT_IO |
+ EFI_RESOURCE_ATTRIBUTE_64_BIT_IO |
+ EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED |
+ EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE |
+ EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE |
+ EFI_RESOURCE_ATTRIBUTE_PERSISTABLE |
+ EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED |
+ EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE |
+ EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE |
+ EFI_RESOURCE_ATTRIBUTE_ENCRYPTED))) != 0) {
+ DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceAttribute type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceAttribute));
+ return FALSE;
+ }
+ break;
+
+ // EFI_HOB_GUID_TYPE is variable length data, so skip check
+ case EFI_HOB_TYPE_GUID_EXTENSION:
+ break;
+
+ case EFI_HOB_TYPE_FV:
+ if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME)) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV));
+ return FALSE;
+ }
+ break;
+
+ case EFI_HOB_TYPE_FV2:
+ if (Hob.Header->HobLength != sizeof(EFI_HOB_FIRMWARE_VOLUME2)) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV2));
+ return FALSE;
+ }
+ break;
+
+ case EFI_HOB_TYPE_FV3:
+ if (Hob.Header->HobLength != sizeof(EFI_HOB_FIRMWARE_VOLUME3)) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV3));
+ return FALSE;
+ }
+ break;
+
+ case EFI_HOB_TYPE_CPU:
+ if (Hob.Header->HobLength != sizeof(EFI_HOB_CPU)) {
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_CPU));
+ return FALSE;
+ }
+
+ for (UINT32 index = 0; index < 6; index ++) {
+ if (Hob.Cpu->Reserved[index] != 0) {
+ DEBUG ((DEBUG_ERROR, "HOB: Cpu Reserved field will always be set to zero.\n"));
+ return FALSE;
+ }
+ }
+ break;
+
+ default:
+ DEBUG ((DEBUG_ERROR, "HOB: Hob type is not know. Type: 0x%04x\n", Hob.Header->HobType));
+ return FALSE;
+ }
+ // Get next HOB
+ Hob.Raw = (UINT8 *) (Hob.Raw + Hob.Header->HobLength);
+ }
+
+ return TRUE;
+}
+
+/**
+ Processing the incoming HobList for the TDX
+
+ Firmware must parse list, and accept the pages of memory before their can be
+ use by the guest.
+
+ @param[in] VmmHobList The Hoblist pass the firmware
+
+ @retval EFI_SUCCESS Process the HobList successfully
+ @retval Others Other errors as indicated
+
+**/
+EFI_STATUS
+EFIAPI
+ProcessHobList (
+ IN CONST VOID *VmmHobList
+ )
+{
+ EFI_STATUS Status;
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_PHYSICAL_ADDRESS PhysicalEnd;
+
+ Status = EFI_SUCCESS;
+ ASSERT (VmmHobList != NULL);
+ Hob.Raw = (UINT8 *) VmmHobList;
+
+ //
+ // Parse the HOB list until end of list or matching type is found.
+ //
+ while (!END_OF_HOB_LIST (Hob)) {
+
+ if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
+ DEBUG ((DEBUG_INFO, "\nResourceType: 0x%x\n", Hob.ResourceDescriptor->ResourceType));
+
+ if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+ DEBUG ((DEBUG_INFO, "ResourceAttribute: 0x%x\n", Hob.ResourceDescriptor->ResourceAttribute));
+ DEBUG ((DEBUG_INFO, "PhysicalStart: 0x%llx\n", Hob.ResourceDescriptor->PhysicalStart));
+ DEBUG ((DEBUG_INFO, "ResourceLength: 0x%llx\n", Hob.ResourceDescriptor->ResourceLength));
+ DEBUG ((DEBUG_INFO, "Owner: %g\n\n", &Hob.ResourceDescriptor->Owner));
+
+ PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength;
+
+ Status = MpAcceptMemoryResourceRange (
+ Hob.ResourceDescriptor->PhysicalStart,
+ PhysicalEnd);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+
+ return Status;
+}
+
+/**
+ In Tdx guest, some information need to be passed from host VMM to guest
+ firmware. For example, the memory resource, etc. These information are
+ prepared by host VMM and put in HobList which is described in TdxMetadata.
+
+ Information in HobList is treated as external input. From the security
+ perspective before it is consumed, it should be validated.
+
+ @retval EFI_SUCCESS Successfully process the hoblist
+ @retval Others Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+ProcessTdxHobList (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ VOID *TdHob;
+ TD_RETURN_DATA TdReturnData;
+
+ TdHob = (VOID *) (UINTN) FixedPcdGet32 (PcdOvmfSecGhcbBase);
+ Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DEBUG ((DEBUG_INFO,
+ "Intel Tdx Started with (GPAW: %d, Cpus: %d)\n",
+ TdReturnData.TdInfo.Gpaw,
+ TdReturnData.TdInfo.NumVcpus
+ ));
+
+ //
+ // Validate HobList
+ //
+ if (ValidateHobList (TdHob) == FALSE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Process Hoblist to accept memory
+ //
+ Status = ProcessHobList (TdHob);
+
+ return Status;
+}
diff --git a/OvmfPkg/Sec/IntelTdx.h b/OvmfPkg/Sec/IntelTdx.h
new file mode 100644
index 000000000000..9420f586b176
--- /dev/null
+++ b/OvmfPkg/Sec/IntelTdx.h
@@ -0,0 +1,33 @@
+/** @file
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef __INTEL_TDX_H__
+#define __INTEL_TDX_H__
+
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Uefi/UefiSpec.h>
+#include <Uefi/UefiBaseType.h>
+#include <IndustryStandard/UefiTcgPlatform.h>
+
+/**
+ In Tdx guest, some information need to be passed from host VMM to guest
+ firmware. For example, the memory resource, etc. These information are
+ prepared by host VMM and put in HobList which is described in TdxMetadata.
+
+ Information in HobList is treated as external input. From the security
+ perspective before it is consumed, it should be validated.
+
+ @retval EFI_SUCCESS Successfully process the hoblist
+ @retval Others Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+ProcessTdxHobList (
+ VOID
+ );
+#endif
diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index 707b0d4bbff4..56ee8c4a230c 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -29,8 +29,10 @@
#include <Library/MemEncryptSevLib.h>
#include <Register/Amd/Ghcb.h>
#include <Register/Amd/Msr.h>
-
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
#include <Ppi/TemporaryRamSupport.h>
+#include "IntelTdx.h"
#define SEC_IDT_ENTRY_COUNT 34
@@ -865,6 +867,24 @@ SevEsIsEnabled (
return (SevEsWorkArea->SevEsEnabled != 0);
}
+/**
+ Check TDX is enabled.
+
+ @retval TRUE TDX is enabled
+ @retval FALSE TDX is not enabled
+**/
+BOOLEAN
+SecTdxIsEnabled (
+ VOID
+ )
+{
+ CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *CcWorkAreaHeader;
+
+ CcWorkAreaHeader = (CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *) FixedPcdGet32 (PcdOvmfWorkAreaBase);
+ return (CcWorkAreaHeader != NULL && CcWorkAreaHeader->GuestType == GUEST_TYPE_INTEL_TDX);
+}
+
+
VOID
EFIAPI
SecCoreStartupWithStack (
@@ -878,6 +898,19 @@ SecCoreStartupWithStack (
UINT32 Index;
volatile UINT8 *Table;
+#if defined (MDE_CPU_X64)
+ if (SecTdxIsEnabled ()) {
+ //
+ // For Td guests, the memory map info is in TdHobLib. It should be processed
+ // first so that the memory is accepted. Otherwise access to the unaccepted
+ // memory will trigger tripple fault.
+ //
+ if (ProcessTdxHobList () != EFI_SUCCESS) {
+ CpuDeadLoop ();
+ }
+ }
+#endif
+
//
// To ensure SMM can't be compromised on S3 resume, we must force re-init of
// the BaseExtractGuidedSectionLib. Since this is before library contructors
@@ -895,13 +928,20 @@ SecCoreStartupWithStack (
// we use a loop rather than CopyMem.
//
IdtTableInStack.PeiService = NULL;
+
for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {
- UINT8 *Src;
- UINT8 *Dst;
- UINTN Byte;
+ //
+ // Declare the local variables that actually move the data elements as
+ // volatile to prevent the optimizer from replacing this function with
+ // the intrinsic memcpy()
+ //
+ CONST UINT8 *Src;
+ volatile UINT8 *Dst;
+ UINTN Byte;
+
+ Src = (CONST UINT8 *) &mIdtEntryTemplate;
+ Dst = (volatile UINT8 *) &IdtTableInStack.IdtTable[Index];
- Src = (UINT8 *) &mIdtEntryTemplate;
- Dst = (UINT8 *) &IdtTableInStack.IdtTable[Index];
for (Byte = 0; Byte < sizeof (mIdtEntryTemplate); Byte++) {
Dst[Byte] = Src[Byte];
}
@@ -947,6 +987,14 @@ SecCoreStartupWithStack (
AsmEnableCache ();
}
+ if (SecTdxIsEnabled ()) {
+ //
+ // InitializeCpuExceptionHandlers () should be called in Td guests so that
+ // #VE exceptions can be handled correctly.
+ //
+ InitializeCpuExceptionHandlers (NULL);
+ }
+
DEBUG ((DEBUG_INFO,
"SecCoreStartupWithStack(0x%x, 0x%x)\n",
(UINT32)(UINTN)BootFv,
diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf
index 6083fa21a433..4f16ff5d8330 100644
--- a/OvmfPkg/Sec/SecMain.inf
+++ b/OvmfPkg/Sec/SecMain.inf
@@ -28,6 +28,7 @@
Ia32/SecEntry.nasm
[Sources.X64]
+ IntelTdx.c
X64/SecEntry.nasm
[Packages]
@@ -51,6 +52,8 @@
ExtractGuidedSectionLib
LocalApicLib
CpuExceptionHandlerLib
+ TdxLib
+ TdxMailboxLib
[Ppis]
gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED
@@ -73,6 +76,9 @@
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfConfidentialComputingWorkAreaHeader
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
[FeaturePcd]
gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (18 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 19/29] OvmfPkg: Enable Tdx in SecMain.c Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 6:50 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 21/29] MdeModulePkg: EFER should not be changed in TDX Min Xu
` (8 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
If TDX is enabled then we do not support DMA operation in PEI phase.
This is mainly because DMA in TDX guest requires using bounce buffer
(which need to allocate dynamic memory and allocating a PAGE size'd
buffer can be challenge in PEI phase).
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
.../QemuFwCfgLib/QemuFwCfgLibInternal.h | 11 +++++++
OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c | 32 +++++++++++++++++++
.../Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf | 2 ++
3 files changed, 45 insertions(+)
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h
index 1fa80686e0bd..1130552bc60a 100644
--- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h
@@ -60,4 +60,15 @@ InternalQemuFwCfgDmaBytes (
IN UINT32 Control
);
+/**
+ Check if it is Tdx guest
+
+ @retval TRUE It is Tdx guest
+ @retval FALSE It is not Tdx guest
+**/
+BOOLEAN
+QemuFwCfgIsTdxGuest (
+ VOID
+ );
+
#endif
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c
index ecabd88fab66..bd2e51363bc6 100644
--- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c
@@ -14,12 +14,30 @@
#include <Library/DebugLib.h>
#include <Library/QemuFwCfgLib.h>
#include <Library/MemEncryptSevLib.h>
+#include <WorkArea.h>
#include "QemuFwCfgLibInternal.h"
STATIC BOOLEAN mQemuFwCfgSupported = FALSE;
STATIC BOOLEAN mQemuFwCfgDmaSupported;
+/**
+ Check if it is Tdx guest
+
+ @retval TRUE It is Tdx guest
+ @retval FALSE It is not Tdx guest
+**/
+BOOLEAN
+QemuFwCfgIsTdxGuest (
+ VOID
+ )
+{
+ CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *CcWorkAreaHeader;
+
+ CcWorkAreaHeader = (CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *) FixedPcdGet32 (PcdOvmfWorkAreaBase);
+ return (CcWorkAreaHeader != NULL && CcWorkAreaHeader->GuestType == GUEST_TYPE_INTEL_TDX);
+}
+
/**
Returns a boolean indicating if the firmware configuration interface
@@ -82,6 +100,14 @@ QemuFwCfgInitialize (
//
if (MemEncryptSevIsEnabled ()) {
DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port interface.\n"));
+ } else if (QemuFwCfgIsTdxGuest ()) {
+ //
+ // If TDX is enabled then we do not support DMA operations in PEI phase.
+ // This is mainly because DMA in TDX guest requires using bounce buffer
+ // (which need to allocate dynamic memory and allocating a PAGE size'd
+ // buffer can be challenge in PEI phase)
+ //
+ DEBUG ((DEBUG_INFO, "TDX: QemuFwCfg fallback to IO Port interface.\n"));
} else {
mQemuFwCfgDmaSupported = TRUE;
DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
@@ -162,6 +188,12 @@ InternalQemuFwCfgDmaBytes (
//
ASSERT (!MemEncryptSevIsEnabled ());
+ //
+ // TDX does not support DMA operations in PEI stage, we should
+ // not have reached here.
+ //
+ ASSERT (!QemuFwCfgIsTdxGuest ());
+
Access.Control = SwapBytes32 (Control);
Access.Length = SwapBytes32 (Size);
Access.Address = SwapBytes64 ((UINTN)Buffer);
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
index 9f9af7d03201..3910511880c9 100644
--- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
@@ -43,3 +43,5 @@
MemoryAllocationLib
MemEncryptSevLib
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation
2021-11-01 13:16 ` [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation Min Xu
@ 2021-11-03 6:50 ` Gerd Hoffmann
2021-11-03 13:07 ` Min Xu
2021-11-03 13:35 ` Min Xu
0 siblings, 2 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 6:50 UTC (permalink / raw)
To: Min Xu
Cc: devel, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
> +/**
> + Check if it is Tdx guest
> +
> + @retval TRUE It is Tdx guest
> + @retval FALSE It is not Tdx guest
> +**/
> +BOOLEAN
> +QemuFwCfgIsTdxGuest (
QemuFwCfgIsCC()
> + return (CcWorkAreaHeader != NULL && CcWorkAreaHeader->GuestType == GUEST_TYPE_INTEL_TDX);
GuestType != GUEST_TYPE_NON_ENCRYPTED
> if (MemEncryptSevIsEnabled ()) {
> DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port interface.\n"));
> + } else if (QemuFwCfgIsTdxGuest ()) {
if (QemuFwCfgIsCC()
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation
2021-11-03 6:50 ` Gerd Hoffmann
@ 2021-11-03 13:07 ` Min Xu
2021-11-03 13:35 ` Min Xu
1 sibling, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-03 13:07 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Yao, Jiewen,
Tom Lendacky
On November 3, 2021 2:51 PM, Gerd Hoffmann wrote:
> > +/**
> > + Check if it is Tdx guest
> > +
> > + @retval TRUE It is Tdx guest
> > + @retval FALSE It is not Tdx guest
> > +**/
> > +BOOLEAN
> > +QemuFwCfgIsTdxGuest (
>
> QemuFwCfgIsCC()
>
> > + return (CcWorkAreaHeader != NULL && CcWorkAreaHeader-
> >GuestType == GUEST_TYPE_INTEL_TDX);
>
> GuestType != GUEST_TYPE_NON_ENCRYPTED
>
> > if (MemEncryptSevIsEnabled ()) {
> > DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port
> interface.\n"));
> > + } else if (QemuFwCfgIsTdxGuest ()) {
>
> if (QemuFwCfgIsCC()
>
Thanks for reminder. It will be updated in the next version.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation
2021-11-03 6:50 ` Gerd Hoffmann
2021-11-03 13:07 ` Min Xu
@ 2021-11-03 13:35 ` Min Xu
2021-11-04 14:36 ` Brijesh Singh
1 sibling, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-03 13:35 UTC (permalink / raw)
To: Gerd Hoffmann, Brijesh Singh
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Erdem Aktas, James Bottomley, Yao, Jiewen,
Tom Lendacky
On November 3, 2021 2:51 PM, Gerd Hoffmann wrote:
> > +/**
> > + Check if it is Tdx guest
> > +
> > + @retval TRUE It is Tdx guest
> > + @retval FALSE It is not Tdx guest
> > +**/
> > +BOOLEAN
> > +QemuFwCfgIsTdxGuest (
>
> QemuFwCfgIsCC()
>
> > + return (CcWorkAreaHeader != NULL && CcWorkAreaHeader-
> >GuestType == GUEST_TYPE_INTEL_TDX);
>
> GuestType != GUEST_TYPE_NON_ENCRYPTED
>
> > if (MemEncryptSevIsEnabled ()) {
> > DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port
> interface.\n"));
> > + } else if (QemuFwCfgIsTdxGuest ()) {
>
> if (QemuFwCfgIsCC()
>
Hi, Gerd
I re-check the MemEncryptSevIsEnabled() and it doesn't simply check the GuestType. Instead it does more checking.
See https://github.com/tianocore/edk2/blob/master/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c#L34-L88
Brijesh, what's your thought about Gerd's suggestion?
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation
2021-11-03 13:35 ` Min Xu
@ 2021-11-04 14:36 ` Brijesh Singh
0 siblings, 0 replies; 107+ messages in thread
From: Brijesh Singh @ 2021-11-04 14:36 UTC (permalink / raw)
To: Xu, Min M, Gerd Hoffmann
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Erdem Aktas, James Bottomley, Yao, Jiewen, Lendacky, Thomas
[-- Attachment #1: Type: text/plain, Size: 2282 bytes --]
[AMD Official Use Only]
Hi Min,
I am good with Gerd suggestion. Now that we have workarea containing the guest type, I can sure summit a patch to simplify the checks greatly. I am waiting for the UefiCpuPkg maintainers to ack our SNP series so that I can start building/cleaning other stuff.
Thanks
Brijesh
________________________________
From: Xu, Min M <min.m.xu@intel.com>
Sent: Wednesday, November 3, 2021 8:35 AM
To: Gerd Hoffmann <kraxel@redhat.com>; Singh, Brijesh <brijesh.singh@amd.com>
Cc: devel@edk2.groups.io <devel@edk2.groups.io>; Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>; Singh, Brijesh <brijesh.singh@amd.com>; Erdem Aktas <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Yao, Jiewen <jiewen.yao@intel.com>; Lendacky, Thomas <Thomas.Lendacky@amd.com>
Subject: RE: [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation
On November 3, 2021 2:51 PM, Gerd Hoffmann wrote:
> > +/**
> > + Check if it is Tdx guest
> > +
> > + @retval TRUE It is Tdx guest
> > + @retval FALSE It is not Tdx guest
> > +**/
> > +BOOLEAN
> > +QemuFwCfgIsTdxGuest (
>
> QemuFwCfgIsCC()
>
> > + return (CcWorkAreaHeader != NULL && CcWorkAreaHeader-
> >GuestType == GUEST_TYPE_INTEL_TDX);
>
> GuestType != GUEST_TYPE_NON_ENCRYPTED
>
> > if (MemEncryptSevIsEnabled ()) {
> > DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port
> interface.\n"));
> > + } else if (QemuFwCfgIsTdxGuest ()) {
>
> if (QemuFwCfgIsCC()
>
Hi, Gerd
I re-check the MemEncryptSevIsEnabled() and it doesn't simply check the GuestType. Instead it does more checking.
See https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Ftianocore%2Fedk2%2Fblob%2Fmaster%2FOvmfPkg%2FLibrary%2FBaseMemEncryptSevLib%2FPeiMemEncryptSevLibInternal.c%23L34-L88&data=04%7C01%7Cbrijesh.singh%40amd.com%7Ce9ff44a121444737aa8d08d99ecedcd6%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637715433574095127%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=h2q2P79zm%2FfF44q35%2FGFmVI%2BOShQGwdNIUEZZSToM%2FQ%3D&reserved=0
Brijesh, what's your thought about Gerd's suggestion?
Thanks
Min
[-- Attachment #2: Type: text/html, Size: 4831 bytes --]
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 21/29] MdeModulePkg: EFER should not be changed in TDX
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (19 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 6:51 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest Min Xu
` (7 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Jian J Wang, Hao A Wu, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In TDX IA32_ERER is RO to host VMM. It could not be changed.
PcdIa32EferChangeAllowed is added in MdeModulePkg.dec and it is
to be set to FALSE in Tdx guest.
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf | 1 +
MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c | 6 ++++++
MdeModulePkg/MdeModulePkg.dec | 5 +++++
3 files changed, 12 insertions(+)
diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
index 19b8a4c8aefa..106b679b6bd0 100644
--- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
@@ -117,6 +117,7 @@
gEfiMdeModulePkgTokenSpaceGuid.PcdUse5LevelPageTable ## SOMETIMES_CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdIa32EferChangeAllowed ## CONSUMES
[Pcd.IA32,Pcd.X64,Pcd.ARM,Pcd.AARCH64]
gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack ## SOMETIMES_CONSUMES
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
index 6831946c54d3..8a3b72509310 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
@@ -148,6 +148,12 @@ IsEnableNonExecNeeded (
return FALSE;
}
+ //
+ // Intel TDX sets this flag to FALSE.
+ //
+ if (!PcdGetBool (PcdIa32EferChangeAllowed)) {
+ return FALSE;
+ }
//
// XD flag (BIT63) in page table entry is only valid if IA32_EFER.NXE is set.
// Features controlled by Following PCDs need this feature to be enabled.
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 133e04ee86ca..007044a311c2 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -2138,6 +2138,11 @@
# @Prompt GHCB Pool Size
gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0|UINT64|0x00030008
+ ## This dynamic PCD indicates if IA32_EFER can be changed. The default value is TRUE but in
+ # Intel TDX change of IA32_EFER is not allowed.
+ # @Prompt The flag which indicates if IA32_EFER is allowed to be changed.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdIa32EferChangeAllowed|TRUE|BOOLEAN|0x00030009
+
[PcdsDynamicEx]
## This dynamic PCD enables the default variable setting.
# Its value is the default store ID value. The default value is zero as Standard default.
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 21/29] MdeModulePkg: EFER should not be changed in TDX
2021-11-01 13:16 ` [PATCH V3 21/29] MdeModulePkg: EFER should not be changed in TDX Min Xu
@ 2021-11-03 6:51 ` Gerd Hoffmann
0 siblings, 0 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 6:51 UTC (permalink / raw)
To: Min Xu
Cc: devel, Jian J Wang, Hao A Wu, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
On Mon, Nov 01, 2021 at 09:16:10PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> In TDX IA32_ERER is RO to host VMM. It could not be changed.
> PcdIa32EferChangeAllowed is added in MdeModulePkg.dec and it is
> to be set to FALSE in Tdx guest.
>
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Hao A Wu <hao.a.wu@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Cc: Erdem Aktas <erdemaktas@google.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (20 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 21/29] MdeModulePkg: EFER should not be changed in TDX Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 6:57 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable Min Xu
` (6 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Jian J Wang, Hao A Wu, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode.
When it is in ROMD mode, the device is mapped to guest memory and
satisfies read access directly.
In EDK2 Option ROM is treated as MMIO region. So Tdx guest access
Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set
the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return
INVALID_OPERAND. Tdvf then falls back to direct access. This requires
to set the shared bit to corresponding PageTable entry. Otherwise it
triggers GP fault.
The mmio region information is passed from VMM in TD Hob. So after the
1:1 identity mapping page table is created (before it is wrote to CR3),
this page table will be updated (set shared bit) based on the mmio region
information in the hob list.
PcdTdxSharedBitMask is created in MdeModulePkg.dec to indicate the shared
bit information. Its default value is 0 and it will be set in PlatformPei
driver if it is of Tdx guest.
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf | 2 +
.../Core/DxeIplPeim/Ia32/DxeLoadFunc.c | 2 +-
.../Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm | 146 ++++++++
.../Core/DxeIplPeim/X64/VirtualMemory.c | 325 +++++++++++++++++-
.../Core/DxeIplPeim/X64/VirtualMemory.h | 66 +++-
MdeModulePkg/MdeModulePkg.dec | 6 +
6 files changed, 539 insertions(+), 8 deletions(-)
create mode 100644 MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm
diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
index 106b679b6bd0..b964277133c0 100644
--- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
@@ -43,6 +43,7 @@
X64/VirtualMemory.h
X64/VirtualMemory.c
X64/DxeLoadFunc.c
+ X64/DxeIplTdVmcall.nasm
[Sources.EBC]
Ebc/DxeLoadFunc.c
@@ -118,6 +119,7 @@
gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdIa32EferChangeAllowed ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask ## CONSUMES
[Pcd.IA32,Pcd.X64,Pcd.ARM,Pcd.AARCH64]
gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack ## SOMETIMES_CONSUMES
diff --git a/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c b/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c
index 284b34818ca7..cd60f8139205 100644
--- a/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c
+++ b/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c
@@ -123,7 +123,7 @@ Create4GPageTablesIa32Pae (
//
// Need to split this 2M page that covers stack range.
//
- Split2MPageTo4K (PhysicalAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, 0, 0);
+ Split2MPageTo4K (PhysicalAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, 0, 0, 0);
} else {
//
// Fill in the Page Directory entries
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm b/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm
new file mode 100644
index 000000000000..c55de3b89f93
--- /dev/null
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm
@@ -0,0 +1,146 @@
+;------------------------------------------------------------------------------
+;*
+;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+;* SPDX-License-Identifier: BSD-2-Clause-Patent
+;*
+;*
+;------------------------------------------------------------------------------
+
+DEFAULT REL
+SECTION .text
+
+%define TDVMCALL_EXPOSE_REGS_MASK 0xffec
+%define TDVMCALL 0x0
+%define EXIT_REASON_CPUID 0xa
+
+%macro tdcall 0
+ db 0x66,0x0f,0x01,0xcc
+%endmacro
+
+%macro tdcall_push_regs 0
+ push rbp
+ mov rbp, rsp
+ push r15
+ push r14
+ push r13
+ push r12
+ push rbx
+ push rsi
+ push rdi
+%endmacro
+
+%macro tdcall_pop_regs 0
+ pop rdi
+ pop rsi
+ pop rbx
+ pop r12
+ pop r13
+ pop r14
+ pop r15
+ pop rbp
+%endmacro
+
+%define number_of_regs_pushed 8
+%define number_of_parameters 4
+
+;
+; Keep these in sync for push_regs/pop_regs, code below
+; uses them to find 5th or greater parameters
+;
+%define first_variable_on_stack_offset \
+ ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8)
+%define second_variable_on_stack_offset \
+ ((first_variable_on_stack_offset) + 8)
+
+%macro tdcall_regs_preamble 2
+ mov rax, %1
+
+ xor rcx, rcx
+ mov ecx, %2
+
+ ; R10 = 0 (standard TDVMCALL)
+
+ xor r10d, r10d
+
+ ; Zero out unused (for standard TDVMCALL) registers to avoid leaking
+ ; secrets to the VMM.
+
+ xor ebx, ebx
+ xor esi, esi
+ xor edi, edi
+
+ xor edx, edx
+ xor ebp, ebp
+ xor r8d, r8d
+ xor r9d, r9d
+%endmacro
+
+%macro tdcall_regs_postamble 0
+ xor ebx, ebx
+ xor esi, esi
+ xor edi, edi
+
+ xor ecx, ecx
+ xor edx, edx
+ xor r8d, r8d
+ xor r9d, r9d
+ xor r10d, r10d
+ xor r11d, r11d
+%endmacro
+
+;------------------------------------------------------------------------------
+; 0 => RAX = TDCALL leaf
+; M => RCX = TDVMCALL register behavior
+; 1 => R10 = standard vs. vendor
+; RDI => R11 = TDVMCALL function / nr
+; RSI = R12 = p1
+; RDX => R13 = p2
+; RCX => R14 = p3
+; R8 => R15 = p4
+
+; UINT64
+; EFIAPI
+; DxeIplTdVmCall (
+; UINT64 Leaf, // Rcx
+; UINT64 P1, // Rdx
+; UINT64 P2, // R8
+; UINT64 P3, // R9
+; UINT64 P4, // rsp + 0x28
+; UINT64 *Val // rsp + 0x30
+; )
+global ASM_PFX(DxeIplTdVmCall)
+ASM_PFX(DxeIplTdVmCall):
+ tdcall_push_regs
+
+ mov r11, rcx
+ mov r12, rdx
+ mov r13, r8
+ mov r14, r9
+ mov r15, [rsp + first_variable_on_stack_offset ]
+
+ tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
+
+ tdcall
+
+ ; ignore return dataif TDCALL reports failure.
+ test rax, rax
+ jnz .no_return_data
+
+ ; Propagate TDVMCALL success/failure to return value.
+ mov rax, r10
+
+ ; Retrieve the Val pointer.
+ mov r9, [rsp + second_variable_on_stack_offset ]
+ test r9, r9
+ jz .no_return_data
+
+ ; On success, propagate TDVMCALL output value to output param
+ test rax, rax
+ jnz .no_return_data
+ mov [r9], r11
+.no_return_data:
+ tdcall_regs_postamble
+
+ tdcall_pop_regs
+
+ ret
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
index 8a3b72509310..c7cb7f007b4f 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
@@ -26,6 +26,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include "DxeIpl.h"
#include "VirtualMemory.h"
+#define TDVMCALL_MAPGPA 0x10001
+
//
// Global variable to keep track current available memory used as page table.
//
@@ -340,6 +342,7 @@ AllocatePageTableMemory (
@param[in] StackSize Stack size.
@param[in] GhcbBase GHCB page area base address.
@param[in] GhcbSize GHCB page area size.
+ @param[in] SharedBitMask Bit mask for Tdx shared memory.
**/
VOID
@@ -349,7 +352,8 @@ Split2MPageTo4K (
IN EFI_PHYSICAL_ADDRESS StackBase,
IN UINTN StackSize,
IN EFI_PHYSICAL_ADDRESS GhcbBase,
- IN UINTN GhcbSize
+ IN UINTN GhcbSize,
+ IN UINT64 SharedBitMask
)
{
EFI_PHYSICAL_ADDRESS PhysicalAddress4K;
@@ -361,6 +365,10 @@ Split2MPageTo4K (
// Make sure AddressEncMask is contained to smallest supported address field
//
AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
+ if (SharedBitMask != 0) {
+ ASSERT (AddressEncMask == 0);
+ AddressEncMask = SharedBitMask;
+ }
PageTableEntry = AllocatePageTableMemory (1);
ASSERT (PageTableEntry != NULL);
@@ -418,6 +426,7 @@ Split2MPageTo4K (
@param[in] StackSize Stack size.
@param[in] GhcbBase GHCB page area base address.
@param[in] GhcbSize GHCB page area size.
+ @param[in] SharedBitMask Bit mask for Tdx shared memory.
**/
VOID
@@ -427,7 +436,8 @@ Split1GPageTo2M (
IN EFI_PHYSICAL_ADDRESS StackBase,
IN UINTN StackSize,
IN EFI_PHYSICAL_ADDRESS GhcbBase,
- IN UINTN GhcbSize
+ IN UINTN GhcbSize,
+ IN UINT64 SharedBitMask
)
{
EFI_PHYSICAL_ADDRESS PhysicalAddress2M;
@@ -440,6 +450,11 @@ Split1GPageTo2M (
//
AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
+ if (SharedBitMask != 0) {
+ ASSERT (AddressEncMask == 0);
+ AddressEncMask = *PageEntry1G & SharedBitMask;
+ }
+
PageDirectoryEntry = AllocatePageTableMemory (1);
ASSERT (PageDirectoryEntry != NULL);
@@ -454,7 +469,7 @@ Split1GPageTo2M (
//
// Need to split this 2M page that covers NULL or stack range.
//
- Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, GhcbBase, GhcbSize);
+ Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, GhcbBase, GhcbSize, SharedBitMask == 0 ? 0 : AddressEncMask);
} else {
//
// Fill in the Page Directory entries
@@ -647,6 +662,296 @@ EnablePageTableProtection (
AsmWriteCr0 (AsmReadCr0() | CR0_WP);
}
+#ifdef MDE_CPU_X64
+/**
+ Set the memory shared bit
+
+ @param[in,out] PageTablePointer Page table entry pointer (PTE).
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region
+ @param[in] Length Length of the memory region
+ @param[in] SharedBitMask Shared bit mask
+
+ @retval Return status of DxeIplTdVmCall
+**/
+UINT64
+SetSharedBit(
+ IN OUT UINT64* PageTablePointer,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINT64 Length,
+ IN UINT64 SharedBitMask
+ )
+{
+ UINT64 Status;
+
+ *PageTablePointer |= SharedBitMask;
+ PhysicalAddress |= SharedBitMask;
+
+ Status = DxeIplTdVmCall(TDVMCALL_MAPGPA, PhysicalAddress, Length, 0, 0, NULL);
+
+ return Status;
+}
+
+/**
+ This function sets the shared bit for the memory region specified by
+ PhysicalAddress and Length from the current page table context.
+
+ The function iterates through the PhysicalAddress one page at a time, and set
+ or clears the memory encryption in the page table. If it encounters
+ that a given physical address range is part of large page then it attempts to
+ change the attribute at one go (based on size), otherwise it splits the
+ large pages into smaller (e.g 2M page into 4K pages) and then try to set or
+ clear the encryption bit on the smallest page size.
+
+ @param[in] PageTableBaseAddress Base Address of Page table
+ @param[in] Page5LevelSupport Indicates if Level-5 paging supported
+ @param[in] SharedBitMask Shared bit mask for the memory region
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Pages Number of pages of memory region
+
+ @retval EFI_SUCCESS The shared bit is set successfully.
+ @retval EFI_INVALID_PARAMETER Number of pages is zero.
+ @retval EFI_NO_MAPPING Physical address is not mapped in PageTable
+**/
+EFI_STATUS
+SetMemorySharedBit (
+ IN PHYSICAL_ADDRESS PageTableBaseAddress,
+ IN BOOLEAN Page5LevelSupport,
+ IN UINT64 SharedBitMask,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Pages
+ )
+{
+ EFI_STATUS Status;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageUpperDirectoryPointerEntry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
+ PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
+ PAGE_TABLE_ENTRY *PageDirectory2MEntry;
+ PAGE_TABLE_4K_ENTRY *PageTableEntry;
+ UINT64 PgTableMask;
+ UINT64 ActiveSharedBitMask;
+ UINTN Length;
+
+ if (Pages == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ PageMapLevel4Entry = NULL;
+ PgTableMask = SharedBitMask | EFI_PAGE_MASK;
+ Length = EFI_PAGES_TO_SIZE (Pages);
+
+ //
+ // If 5-level pages, adjust PageTableBaseAddress to point to first 4-level page directory,
+ // we will only have 1
+ //
+ if (Page5LevelSupport) {
+ PageTableBaseAddress = *(UINT64 *)PageTableBaseAddress & ~PgTableMask;
+ }
+
+ while (Length > 0) {
+ PageMapLevel4Entry = (VOID*) (PageTableBaseAddress & ~PgTableMask);
+ PageMapLevel4Entry += PML4_OFFSET (PhysicalAddress);
+ if (!PageMapLevel4Entry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PML4 for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = EFI_NO_MAPPING;
+ break;
+ }
+
+ PageDirectory1GEntry = (VOID *)((PageMapLevel4Entry->Bits.PageTableBaseAddress << 12) & ~PgTableMask);
+ PageDirectory1GEntry += PDP_OFFSET (PhysicalAddress);
+ if (!PageDirectory1GEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PDPE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = EFI_NO_MAPPING;
+ break;
+ }
+
+ //
+ // If the MustBe1 bit is not 1, it's not actually a 1GB entry
+ //
+ if (PageDirectory1GEntry->Bits.MustBe1) {
+ //
+ // Valid 1GB page
+ // If we have at least 1GB to go, we can just update this entry
+ //
+ if ((PhysicalAddress & (BIT30 - 1)) == 0 && Length >= BIT30) {
+ SetSharedBit (&PageDirectory1GEntry->Uint64, PhysicalAddress, BIT30, SharedBitMask);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: updated 1GB entry for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ PhysicalAddress += BIT30;
+ Length -= BIT30;
+ } else {
+ //
+ // We must split the page
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: splitting 1GB page for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Split1GPageTo2M (
+ (UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress << 30,
+ (UINT64 *)PageDirectory1GEntry,
+ 0, 0, 0, 0,
+ SharedBitMask
+ );
+ continue;
+ }
+ } else {
+ //
+ // Actually a PDP
+ //
+ PageUpperDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory1GEntry;
+ PageDirectory2MEntry = (VOID *)((PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress <<12) & ~PgTableMask);
+ PageDirectory2MEntry += PDE_OFFSET (PhysicalAddress);
+ if (!PageDirectory2MEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PDE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = EFI_NO_MAPPING;
+ break;
+ }
+ //
+ // If the MustBe1 bit is not a 1, it's not a 2MB entry
+ //
+ if (PageDirectory2MEntry->Bits.MustBe1) {
+ //
+ // Valid 2MB page
+ // If we have at least 2MB left to go, we can just update this entry
+ //
+ if ((PhysicalAddress & (BIT21-1)) == 0 && Length >= BIT21) {
+ SetSharedBit (&PageDirectory2MEntry->Uint64, PhysicalAddress, BIT21, SharedBitMask);
+ PhysicalAddress += BIT21;
+ Length -= BIT21;
+ } else {
+ //
+ // We must split up this page into 4K pages
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: splitting 2MB page for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+
+ ActiveSharedBitMask = PageDirectory2MEntry->Uint64 & SharedBitMask;
+
+ Split2MPageTo4K (
+ (UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress << 21,
+ (UINT64 *)PageDirectory2MEntry,
+ 0, 0, 0, 0,
+ ActiveSharedBitMask
+ );
+ continue;
+ }
+ } else {
+ PageDirectoryPointerEntry =(PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory2MEntry;
+ PageTableEntry = (VOID *)((PageDirectoryPointerEntry->Bits.PageTableBaseAddress <<12) & ~PgTableMask);
+ PageTableEntry += PTE_OFFSET(PhysicalAddress);
+ if (!PageTableEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PTE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = EFI_NO_MAPPING;
+ break;
+ }
+ SetSharedBit (&PageTableEntry->Uint64, PhysicalAddress, EFI_PAGE_SIZE, SharedBitMask);
+ PhysicalAddress += EFI_PAGE_SIZE;
+ Length -= EFI_PAGE_SIZE;
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Set the shared bit for mmio region in Tdx guest.
+
+ In Tdx guest there are 2 ways to access mmio, TdVmcall or direct access.
+ For direct access, the shared bit of the PageTableEntry should be set.
+ The mmio region information is retrieved from hob list.
+
+ @param[in] PageTableBaseAddress Base Address of Page table.
+ @param[in] Page5LevelSupport Indicates if Level-5 paging is supported.
+
+ @retval EFI_SUCCESS The shared bit is set successfully.
+ @retval EFI_UNSUPPORTED Setting the shared bit of memory region
+ is not supported
+**/
+EFI_STATUS
+DxeIplSetMmioSharedBit (
+ IN UINT64 PageTableBaseAddress,
+ IN BOOLEAN Page5LevelSupport
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ UINT64 SharedBitMask;
+
+ //
+ // Check if we have a valid memory shared bit mask
+ //
+ SharedBitMask = PcdGet64 (PcdTdxSharedBitMask) & PAGING_1G_ADDRESS_MASK_64;
+ if (SharedBitMask == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+
+ Hob.Raw = (UINT8 *) GetHobList ();
+
+ //
+ // Parse the HOB list until end of list or matching type is found.
+ //
+ while (!END_OF_HOB_LIST (Hob)) {
+ if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
+ && Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_MAPPED_IO) {
+
+ SetMemorySharedBit (
+ PageTableBaseAddress,
+ Page5LevelSupport,
+ SharedBitMask,
+ Hob.ResourceDescriptor->PhysicalStart,
+ EFI_SIZE_TO_PAGES (Hob.ResourceDescriptor->ResourceLength));
+ }
+
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+
+ return EFI_SUCCESS;
+}
+
+#endif
+
/**
Allocates and fills in the Page Directory and Page Table Entries to
establish a 1:1 Virtual to Physical mapping.
@@ -851,7 +1156,7 @@ CreateIdentityMappingPageTables (
for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
if (ToSplitPageTable (PageAddress, SIZE_1GB, StackBase, StackSize, GhcbBase, GhcbSize)) {
- Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, StackBase, StackSize, GhcbBase, GhcbSize);
+ Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, StackBase, StackSize, GhcbBase, GhcbSize, 0);
} else {
//
// Fill in the Page Directory entries
@@ -885,7 +1190,7 @@ CreateIdentityMappingPageTables (
//
// Need to split this 2M page that covers NULL or stack range.
//
- Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, GhcbBase, GhcbSize);
+ Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, GhcbBase, GhcbSize, 0);
} else {
//
// Fill in the Page Directory entries
@@ -921,6 +1226,15 @@ CreateIdentityMappingPageTables (
ZeroMem (PageMapLevel5Entry, (512 - IndexOfPml5Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER));
}
+#ifdef MDE_CPU_X64
+ //
+ // Set shared bit for TDX
+ //
+ if (PcdGet64 (PcdTdxSharedBitMask) != 0) {
+ DxeIplSetMmioSharedBit ((UINTN)PageMap, Page5LevelSupport);
+ }
+#endif
+
//
// Protect the page table by marking the memory used for page table to be
// read-only.
@@ -936,4 +1250,3 @@ CreateIdentityMappingPageTables (
return (UINTN)PageMap;
}
-
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
index 6b7c38a441d6..6223bb69c403 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
@@ -19,6 +19,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#define SYS_CODE64_SEL 0x38
+#define PAGETABLE_ENTRY_MASK ((1UL << 9) - 1)
+#define PML4_OFFSET(x) ( (x >> 39) & PAGETABLE_ENTRY_MASK)
+#define PDP_OFFSET(x) ( (x >> 30) & PAGETABLE_ENTRY_MASK)
+#define PDE_OFFSET(x) ( (x >> 21) & PAGETABLE_ENTRY_MASK)
+#define PTE_OFFSET(x) ( (x >> 12) & PAGETABLE_ENTRY_MASK)
#pragma pack(1)
@@ -203,6 +208,7 @@ EnableExecuteDisableBit (
@param[in] StackSize Stack size.
@param[in] GhcbBase GHCB page area base address.
@param[in] GhcbSize GHCB page area size.
+ @param[in] SharedBitMask Bit mask for Tdx shared memory.
**/
VOID
@@ -212,7 +218,8 @@ Split2MPageTo4K (
IN EFI_PHYSICAL_ADDRESS StackBase,
IN UINTN StackSize,
IN EFI_PHYSICAL_ADDRESS GhcbBase,
- IN UINTN GhcbSize
+ IN UINTN GhcbSize,
+ IN UINT64 SharedBitMask
);
/**
@@ -327,4 +334,61 @@ AllocatePageTableMemory (
IN UINTN Pages
);
+#ifdef MDE_CPU_X64
+/**
+ This function sets the shared bit for the memory region specified by
+ PhysicalAddress and Length from the current page table context.
+
+ The function iterates through the PhysicalAddress one page at a time, and set
+ or clears the memory encryption in the page table. If it encounters
+ that a given physical address range is part of large page then it attempts to
+ change the attribute at one go (based on size), otherwise it splits the
+ large pages into smaller (e.g 2M page into 4K pages) and then try to set or
+ clear the encryption bit on the smallest page size.
+
+ @param[in] PageTableBaseAddress Base Address of Page table
+ @param[in] Page5LevelSupport Indicates if Level-5 paging supported
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Pages Number of pages of memory region
+
+ @retval EFI_SUCCESS The shared bit is set successfully.
+ @retval EFI_INVALID_PARAMETER Number of pages is zero.
+ @retval EFI_NO_MAPPING Physical address is not mapped in PageTable
+**/
+EFI_STATUS
+SetMemorySharedBit (
+ IN PHYSICAL_ADDRESS PageTableBaseAddress,
+ IN BOOLEAN Page5LevelSupport,
+ IN UINT64 SharedBitMask,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Pages
+ );
+
+/**
+ TDVMALL is a leaf function 0 for TDCALL. It helps invoke services from the
+ host VMM to pass/receive information.
+
+ @param[in] Leaf Number of sub-functions
+ @param[in] Arg1 Arg1
+ @param[in] Arg2 Arg2
+ @param[in] Arg3 Arg3
+ @param[in] Arg4 Arg4
+ @param[in,out] Results Returned result of the sub-function
+
+ @return EFI_SUCCESS
+ @return Other See individual sub-functions
+
+**/
+EFI_STATUS
+DxeIplTdVmCall (
+ IN UINT64 Leaf,
+ IN UINT64 Arg1,
+ IN UINT64 Arg2,
+ IN UINT64 Arg3,
+ IN UINT64 Arg4,
+ IN OUT VOID *Results
+ );
+#endif
+
#endif
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 007044a311c2..4d8f7f5cc55b 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -2143,6 +2143,12 @@
# @Prompt The flag which indicates if IA32_EFER is allowed to be changed.
gEfiMdeModulePkgTokenSpaceGuid.PcdIa32EferChangeAllowed|TRUE|BOOLEAN|0x00030009
+ ## This PCD holds the shared bit mask for page table entries when Tdx is enabled.
+ # This mask should be applied to mmio region when creating 1:1 virtual to physical
+ # mapping tables.
+ # @Prompt The shared bit mask when Intel Tdx is enabled.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask|0x0|UINT64|0x0003000a
+
[PcdsDynamicEx]
## This dynamic PCD enables the default variable setting.
# Its value is the default store ID value. The default value is zero as Standard default.
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest
2021-11-01 13:16 ` [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest Min Xu
@ 2021-11-03 6:57 ` Gerd Hoffmann
2021-11-04 7:03 ` [edk2-devel] " Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 6:57 UTC (permalink / raw)
To: Min Xu
Cc: devel, Jian J Wang, Hao A Wu, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
> +++ b/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm
> @@ -0,0 +1,146 @@
> +;------------------------------------------------------------------------------
> +;*
> +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
> +;* SPDX-License-Identifier: BSD-2-Clause-Patent
> +;*
> +;*
> +;------------------------------------------------------------------------------
> +
> +DEFAULT REL
> +SECTION .text
> +
> +%define TDVMCALL_EXPOSE_REGS_MASK 0xffec
> +%define TDVMCALL 0x0
> +%define EXIT_REASON_CPUID 0xa
> +
> +%macro tdcall 0
> + db 0x66,0x0f,0x01,0xcc
> +%endmacro
Again, shouldn't this just call TdxLib instead of having its own copy?
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest
2021-11-03 6:57 ` Gerd Hoffmann
@ 2021-11-04 7:03 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-04 7:03 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com
Cc: Wang, Jian J, Wu, Hao A, Brijesh Singh, Erdem Aktas,
James Bottomley, Yao, Jiewen, Tom Lendacky
On November 3, 2021 2:57 PM, Gerd Hoffmann wrote:
> > +++ b/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm
> > @@ -0,0 +1,146 @@
> > +;------------------------------------------------------------------------------
> > +;*
> > +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
> > +;* SPDX-License-Identifier: BSD-2-Clause-Patent
> > +;*
> > +;*
> > +;------------------------------------------------------------------------------
> > +
> > +DEFAULT REL
> > +SECTION .text
> > +
> > +%define TDVMCALL_EXPOSE_REGS_MASK 0xffec
> > +%define TDVMCALL 0x0
> > +%define EXIT_REASON_CPUID 0xa
> > +
> > +%macro tdcall 0
> > + db 0x66,0x0f,0x01,0xcc
> > +%endmacro
>
> Again, shouldn't this just call TdxLib instead of having its own copy?
>
Sure. It will be updated in the next version.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (21 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 7:00 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 24/29] OvmfPkg: Update PlatformPei to support TDX Min Xu
` (5 subsequent siblings)
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
MMIO region in Tdx guest is set with PcdTdxSharedBitMask when
creating 1:1 identity page table. In SEV guest the page table
entries is set with PcdPteMemoryEncryptionAddressOrMask when
creating 1:1 identity table.
So the AddressEncMask in GetPageTableEntry (@CpuPageTable.c) is either
PcdPteMemoryEncryptionAddressOrMask (in SEV guest), or
PcdTdxSharedBitMask (in TDX guest), or all-0 (in Legacy guest).
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
UefiCpuPkg/CpuDxe/CpuDxe.inf | 1 +
UefiCpuPkg/CpuDxe/CpuPageTable.c | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
index d87fe503d152..235241899222 100644
--- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
+++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
@@ -80,6 +80,7 @@
gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask ## CONSUMES
[Depex]
TRUE
diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c b/UefiCpuPkg/CpuDxe/CpuPageTable.c
index 06ee1b823171..7982ebd23f98 100644
--- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
+++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
@@ -299,6 +299,9 @@ GetPageTableEntry (
// Make sure AddressEncMask is contained to smallest supported address field.
//
AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
+ if (AddressEncMask == 0) {
+ AddressEncMask = PcdGet64 (PcdTdxSharedBitMask) & PAGING_1G_ADDRESS_MASK_64;
+ }
if (PagingContext->MachineType == IMAGE_FILE_MACHINE_X64) {
if ((PagingContext->ContextData.X64.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_5_LEVEL) != 0) {
@@ -345,6 +348,7 @@ GetPageTableEntry (
// 4k
L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);
+
if ((L1PageTable[Index1] == 0) && (Address != 0)) {
*PageAttribute = PageNone;
return NULL;
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable
2021-11-01 13:16 ` [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable Min Xu
@ 2021-11-03 7:00 ` Gerd Hoffmann
2021-11-22 3:09 ` [edk2-devel] " Ni, Ray
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 7:00 UTC (permalink / raw)
To: Min Xu
Cc: devel, Brijesh Singh, Erdem Aktas, James Bottomley, Jiewen Yao,
Tom Lendacky, Eric Dong, Ray Ni, Rahul Kumar
Hi,
> + gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask ## CONSUMES
> AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
> + if (AddressEncMask == 0) {
> + AddressEncMask = PcdGet64 (PcdTdxSharedBitMask) & PAGING_1G_ADDRESS_MASK_64;
> + }
Looks like two PCDs for basically the same thing.
Should we create a common CC PCD here?
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable
2021-11-03 7:00 ` Gerd Hoffmann
@ 2021-11-22 3:09 ` Ni, Ray
2021-12-07 3:50 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Ni, Ray @ 2021-11-22 3:09 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com, Xu, Min M
Cc: Brijesh Singh, Erdem Aktas, James Bottomley, Yao, Jiewen,
Tom Lendacky, Dong, Eric, Kumar, Rahul1
Gerd, thanks. I am about to raise the same comments...
-----Original Message-----
From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Gerd Hoffmann
Sent: Wednesday, November 3, 2021 3:00 PM
To: Xu, Min M <min.m.xu@intel.com>
Cc: devel@edk2.groups.io; Brijesh Singh <brijesh.singh@amd.com>; Erdem Aktas <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Yao, Jiewen <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>; Dong, Eric <eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com>; Kumar, Rahul1 <rahul1.kumar@intel.com>
Subject: Re: [edk2-devel] [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable
Hi,
> + gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask ## CONSUMES
> AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) &
> PAGING_1G_ADDRESS_MASK_64;
> + if (AddressEncMask == 0) {
> + AddressEncMask = PcdGet64 (PcdTdxSharedBitMask) &
> + PAGING_1G_ADDRESS_MASK_64; }
Looks like two PCDs for basically the same thing.
Should we create a common CC PCD here?
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable
2021-11-22 3:09 ` [edk2-devel] " Ni, Ray
@ 2021-12-07 3:50 ` Min Xu
2021-12-07 7:15 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-12-07 3:50 UTC (permalink / raw)
To: Ni, Ray, devel@edk2.groups.io, kraxel@redhat.com
Cc: Brijesh Singh, Erdem Aktas, James Bottomley, Yao, Jiewen,
Tom Lendacky, Dong, Eric, Kumar, Rahul1
On November 22, 2021 11:09 AM, Ni Ray wrote:
> Gerd, thanks. I am about to raise the same comments...
> > + gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask ##
> CONSUMES
>
> > AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask)
> &
> > PAGING_1G_ADDRESS_MASK_64;
> > + if (AddressEncMask == 0) {
> > + AddressEncMask = PcdGet64 (PcdTdxSharedBitMask) &
> > + PAGING_1G_ADDRESS_MASK_64; }
>
> Looks like two PCDs for basically the same thing.
> Should we create a common CC PCD here?
>
1. The current situation of PcdPteMemoryEncryptionAddressOrMask is:
1.1 PcdPteMemoryEncryptionAddressOrMask is now set by AmdSev.
1.2 In CreateIdentityMappingPageTables(), this value (AddressEncMask) is set to the page tables in SEV guest.
1.3 This PCD is also used as an indicator in InternalMemEncryptSevStatus() if ReadSevMsr is TRUE or FALSE.
1.4 This PCD is also used in BootScriptExecutorEntryPoint()
2. The meaning and usage scenario of PcdTdxSharedBitMask are somehow different from PcdPteMemoryEncryptionAddressOrMask.
2.1 Guest physical address (GPA) space of Td guest is divided into private and shared sub-spaces, determined by the shared bit of GPA.[1]
2.2 PcdTdxSharedBitMask indicates the above shared bit of GPA. And only the shared GPA has the shared bit set. This breaks 1.2.
2.3 It also breaks above 1.3. Because not all the MSR can be read in Td guest (It will trigger #VE).
2.4 It breaks above 1.4 as well. Because the private GPA doesn't have the shared bit set (2.2). So BootScriptExecutorEntryPoint() has to check Td guest in run-time so that the correct AddressEncMask is used.
Based on above investigation and consideration, I suggest use PcdTdxSharedBitMask for Td guest and PcdPteMemoryEncryptionAddressOrMask for SEV guest. We can re-visit it later.
[1] https://www.intel.com/content/dam/develop/external/us/en/documents/tdx-module-1.0-public-spec-v0.931.pdf Section 2.4.2
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable
2021-12-07 3:50 ` Min Xu
@ 2021-12-07 7:15 ` Gerd Hoffmann
0 siblings, 0 replies; 107+ messages in thread
From: Gerd Hoffmann @ 2021-12-07 7:15 UTC (permalink / raw)
To: Xu, Min M
Cc: Ni, Ray, devel@edk2.groups.io, Brijesh Singh, Erdem Aktas,
James Bottomley, Yao, Jiewen, Tom Lendacky, Dong, Eric,
Kumar, Rahul1
Hi,
> > Looks like two PCDs for basically the same thing.
> > Should we create a common CC PCD here?
> >
> 1. The current situation of PcdPteMemoryEncryptionAddressOrMask is:
> 1.1 PcdPteMemoryEncryptionAddressOrMask is now set by AmdSev.
> 1.2 In CreateIdentityMappingPageTables(), this value (AddressEncMask) is set to the page tables in SEV guest.
> 1.3 This PCD is also used as an indicator in InternalMemEncryptSevStatus() if ReadSevMsr is TRUE or FALSE.
> 1.4 This PCD is also used in BootScriptExecutorEntryPoint()
Yes. Creating a common CC PCD may require some changes on the SEV side
too. The code (1.3 for example) assumes sev is active when
PcdPteMemoryEncryptionAddressOrMask is set, which will obviously not be
the case any more when tdx uses it too. But there are other ways to
check for sev which can be used instead ...
> 2. The meaning and usage scenario of PcdTdxSharedBitMask are somehow different from PcdPteMemoryEncryptionAddressOrMask.
> 2.1 Guest physical address (GPA) space of Td guest is divided into private and shared sub-spaces, determined by the shared bit of GPA.[1]
Well, there are some differences in detail but the underlying concept is
the same. The page table bit says whenever the page is private to the vm
or not. With SEV the bit enables/disables encryption. With TDX the bit
switches between private and shared encryption key.
> 2.2 PcdTdxSharedBitMask indicates the above shared bit of GPA. And
> only the shared GPA has the shared bit set. This breaks 1.2.
Hmm, ok. So the logic is different. SEV enables the bit for private
pages whereas TDX enables the bit for shared pages.
Too bad. That indeed makes it impossible to share a single PCD.
We could still define something generic, like a
"set-this-bit-for-shared-pages" pcd and a
"set-this-bit-for-private-pages" pcd. But at the end of the day that
probably wouldn't be very different from having
PcdPteMemoryEncryptionAddressOrMask + PcdTdxSharedBitMask ...
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 24/29] OvmfPkg: Update PlatformPei to support TDX
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (22 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-01 13:16 ` [PATCH V3 25/29] OvmfPkg: Update AcpiPlatformDxe to alter MADT table Min Xu
` (4 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Intel TDX has its own requirement in InitializePlatform (PlatformPei).
1. Publish the ram region
Host VMM pass the memory region to TDVF in TD Hob. These memory
are accepted by TDVF before they're available for access. TDVF
publish these memory information in the final hoblist for DXE.
2. Relocate mailbox
At the beginning of system boot, a 4K-aligned, 4K-size memory (Td
mailbox) is pre-allocated by host VMM. BSP & APs do the page accept
together in that memory region.
After that TDVF is designed to relocate the mailbox to a 4K-aligned,
4K-size memory block which is allocated in the ACPI Nvs memory. APs
are waken up and spin around the relocated mailbox waiting for
further command.
3. Create PlatformInfoHob
PlatformInfoHob contains the TDX specific information, for example,
the relocated Mailbox address. gUefiOvmfPkgTdxPlatformGuid is the new
GUID added in OvmfPkg.dec for this purpose.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/OvmfPkg.dec | 1 +
OvmfPkg/OvmfPkgX64.dsc | 2 +
OvmfPkg/PlatformPei/FeatureControl.c | 8 +-
OvmfPkg/PlatformPei/IntelTdx.c | 290 +++++++++++++++++++++++++
OvmfPkg/PlatformPei/IntelTdxNull.c | 49 +++++
OvmfPkg/PlatformPei/MemDetect.c | 57 +++--
OvmfPkg/PlatformPei/Platform.c | 1 +
OvmfPkg/PlatformPei/Platform.h | 28 +++
OvmfPkg/PlatformPei/PlatformPei.inf | 14 ++
OvmfPkg/PlatformPei/X64/ApRunLoop.nasm | 83 +++++++
10 files changed, 519 insertions(+), 14 deletions(-)
create mode 100644 OvmfPkg/PlatformPei/IntelTdx.c
create mode 100644 OvmfPkg/PlatformPei/IntelTdxNull.c
create mode 100644 OvmfPkg/PlatformPei/X64/ApRunLoop.nasm
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index ccf8fc33ce27..e8cd126fc161 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -128,6 +128,7 @@
gQemuKernelLoaderFsMediaGuid = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
gGrubFileGuid = {0xb5ae312c, 0xbc8a, 0x43b1, {0x9c, 0x62, 0xeb, 0xb8, 0x26, 0xdd, 0x5d, 0x07}}
gConfidentialComputingSecretGuid = {0xadf956ad, 0xe98c, 0x484c, {0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47}}
+ gUefiOvmfPkgTdxPlatformGuid = {0xdec9b486, 0x1f16, 0x47c7, {0x8f, 0x68, 0xdf, 0x1a, 0x41, 0x88, 0x8b, 0xa5}}
[Ppis]
# PPI whose presence in the PPI database signals that the TPM base address
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 3eb29811d822..850953f20e35 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -664,6 +664,8 @@
gEfiSecurityPkgTokenSpaceGuid.PcdTpmInstanceGuid|{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
!endif
+ gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr|0
+
# IPv4 and IPv6 PXE Boot support.
gEfiNetworkPkgTokenSpaceGuid.PcdIPv4PXESupport|0x01
gEfiNetworkPkgTokenSpaceGuid.PcdIPv6PXESupport|0x01
diff --git a/OvmfPkg/PlatformPei/FeatureControl.c b/OvmfPkg/PlatformPei/FeatureControl.c
index dccf9505dd7b..cf1a25722704 100644
--- a/OvmfPkg/PlatformPei/FeatureControl.c
+++ b/OvmfPkg/PlatformPei/FeatureControl.c
@@ -12,6 +12,8 @@
#include <Library/QemuFwCfgLib.h>
#include <Ppi/MpServices.h>
#include <Register/ArchitecturalMsr.h>
+#include <Library/TdxLib.h>
+#include <IndustryStandard/Tdx.h>
#include "Platform.h"
@@ -37,7 +39,11 @@ WriteFeatureControl (
IN OUT VOID *WorkSpace
)
{
- AsmWriteMsr64 (MSR_IA32_FEATURE_CONTROL, mFeatureControlValue);
+ if (PlatformPeiIsTdxGuest ()) {
+ TdVmCall (TDVMCALL_WRMSR, (UINT64) MSR_IA32_FEATURE_CONTROL, mFeatureControlValue, 0, 0, 0);
+ } else {
+ AsmWriteMsr64 (MSR_IA32_FEATURE_CONTROL, mFeatureControlValue);
+ }
}
/**
diff --git a/OvmfPkg/PlatformPei/IntelTdx.c b/OvmfPkg/PlatformPei/IntelTdx.c
new file mode 100644
index 000000000000..3ca5161a0c44
--- /dev/null
+++ b/OvmfPkg/PlatformPei/IntelTdx.c
@@ -0,0 +1,290 @@
+/** @file
+ Initialize Intel TDX support.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TdxMailboxLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <IndustryStandard/IntelTdx.h>
+#include <IndustryStandard/QemuFwCfg.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/TdxLib.h>
+#include <WorkArea.h>
+#include <ConfidentialComputingGuestAttr.h>
+#include "Platform.h"
+
+/**
+ Check if it is Tdx guest
+
+ @retval TRUE It is Tdx guest
+ @retval FALSE It is not Tdx guest
+**/
+BOOLEAN
+PlatformPeiIsTdxGuest (
+ VOID
+ )
+{
+ CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *CcWorkAreaHeader;
+
+ CcWorkAreaHeader = (CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *) FixedPcdGet32 (PcdOvmfWorkAreaBase);
+ return (CcWorkAreaHeader != NULL && CcWorkAreaHeader->GuestType == GUEST_TYPE_INTEL_TDX);
+}
+
+
+VOID
+EFIAPI
+DEBUG_HOBLIST (
+ IN CONST VOID *HobStart
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ Hob.Raw = (UINT8 *) HobStart;
+ //
+ // Parse the HOB list until end of list or matching type is found.
+ //
+ while (!END_OF_HOB_LIST (Hob)) {
+ DEBUG ((DEBUG_INFO, "HOB(%p) : %x %x\n", Hob, Hob.Header->HobType, Hob.Header->HobLength));
+ switch (Hob.Header->HobType) {
+ case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:
+ DEBUG ((DEBUG_INFO, "\t: %x %x %llx %llx\n",
+ Hob.ResourceDescriptor->ResourceType,
+ Hob.ResourceDescriptor->ResourceAttribute,
+ Hob.ResourceDescriptor->PhysicalStart,
+ Hob.ResourceDescriptor->ResourceLength));
+
+ break;
+ case EFI_HOB_TYPE_MEMORY_ALLOCATION:
+ DEBUG ((DEBUG_INFO, "\t: %llx %llx %x\n",
+ Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress,
+ Hob.MemoryAllocation->AllocDescriptor.MemoryLength,
+ Hob.MemoryAllocation->AllocDescriptor.MemoryType));
+ break;
+ default:
+ break;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+}
+
+/**
+ Transfer the incoming HobList for the TD to the final HobList for Dxe.
+ The Hobs transferred in this function are ResourceDescriptor hob and
+ MemoryAllocation hob.
+
+ @param[in] VmmHobList The Hoblist pass the firmware
+
+**/
+VOID
+EFIAPI
+TransferTdxHobList (
+ VOID
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute;
+
+ //
+ // PcdOvmfSecGhcbBase is used as the TD_HOB in Tdx guest.
+ //
+ Hob.Raw = (UINT8 *) (UINTN) PcdGet32 (PcdOvmfSecGhcbBase);
+ while (!END_OF_HOB_LIST (Hob)) {
+ switch (Hob.Header->HobType) {
+ case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:
+ ResourceAttribute = Hob.ResourceDescriptor->ResourceAttribute;
+
+ BuildResourceDescriptorHob (
+ Hob.ResourceDescriptor->ResourceType,
+ ResourceAttribute,
+ Hob.ResourceDescriptor->PhysicalStart,
+ Hob.ResourceDescriptor->ResourceLength);
+ break;
+ case EFI_HOB_TYPE_MEMORY_ALLOCATION:
+ BuildMemoryAllocationHob (
+ Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress,
+ Hob.MemoryAllocation->AllocDescriptor.MemoryLength,
+ Hob.MemoryAllocation->AllocDescriptor.MemoryType);
+ break;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+ DEBUG_HOBLIST (GetHobList ());
+}
+
+/**
+
+ Publish memory regions in Intel TDX guest.
+
+**/
+VOID
+TdxPublishRamRegions (
+ VOID
+ )
+{
+ TransferTdxHobList ();
+
+ //
+ // The memory region defined by PcdOvmfSecGhcbBackupBase is pre-allocated by
+ // host VMM and used as the td mailbox at the beginning of system boot.
+ //
+ BuildMemoryAllocationHob (
+ PcdGet32 (PcdOvmfSecGhcbBackupBase),
+ PcdGet32 (PcdOvmfSecGhcbBackupSize),
+ EfiACPIMemoryNVS
+ );
+}
+
+/**
+ This function check the system status from QEMU via fw_cfg.
+ If the system status from QEMU is retrieved, its value is set
+ into PlatformInfoHob.
+
+ @param[in] PlatformInfoHob The data structure of PlatformInfo hob
+**/
+VOID
+EFIAPI
+CheckSystemStatsForOverride (
+ IN EFI_HOB_PLATFORM_INFO * PlatformInfoHob
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+
+ //
+ // check for overrides
+ //
+ Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);
+ if (Status != RETURN_SUCCESS || FwCfgSize != sizeof PlatformInfoHob->SystemStates) {
+ DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n"));
+ return;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (sizeof (PlatformInfoHob->SystemStates), PlatformInfoHob->SystemStates);
+}
+
+/**
+ At the beginning of system boot, a 4K-aligned, 4K-size memory (Td mailbox) is
+ pre-allocated by host VMM. BSP & APs do the page accept together in that memory
+ region.
+
+ After that TDVF is designed to relocate the mailbox to a 4K-aligned, 4K-size
+ memory block which is allocated in the ACPI Nvs memory. APs are waken up and
+ spin around the relocated mailbox for further command.
+
+ @return UINT64 Address of the relocated mailbox
+**/
+UINT64
+EFIAPI
+TdxRelocateMailbox (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Address;
+ VOID *ApLoopFunc = NULL;
+ UINT32 RelocationPages;
+ MP_RELOCATION_MAP RelocationMap;
+ MP_WAKEUP_MAILBOX *RelocatedMailBox;
+
+ //
+ // Get information needed to setup aps running in their
+ // run loop in allocated acpi reserved memory
+ // Add another page for mailbox
+ //
+ AsmGetRelocationMap (&RelocationMap);
+ RelocationPages = EFI_SIZE_TO_PAGES ((UINT32)RelocationMap.RelocateApLoopFuncSize) + 1;
+
+ Status = PeiServicesAllocatePages (EfiACPIMemoryNVS, RelocationPages, &Address);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to allocate pages to relocate Td mailbox. %r\n", Status));
+ ASSERT (FALSE);
+ return 0;
+ }
+
+ ApLoopFunc = (VOID *) ((UINTN) Address + EFI_PAGE_SIZE);
+
+ CopyMem (
+ ApLoopFunc,
+ RelocationMap.RelocateApLoopFuncAddress,
+ RelocationMap.RelocateApLoopFuncSize
+ );
+
+ DEBUG ((DEBUG_INFO, "Ap Relocation: mailbox %llx, loop %p\n",
+ Address, ApLoopFunc));
+
+ //
+ // Initialize mailbox
+ //
+ RelocatedMailBox = (MP_WAKEUP_MAILBOX *)Address;
+ RelocatedMailBox->Command = MpProtectedModeWakeupCommandNoop;
+ RelocatedMailBox->ApicId = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID;
+ RelocatedMailBox->WakeUpVector = 0;
+
+ //
+ // Wakup APs and have been move to the finalized run loop
+ // They will spin until guest OS wakes them
+ //
+ MpSerializeStart ();
+
+ MpSendWakeupCommand (
+ MpProtectedModeWakeupCommandWakeup,
+ (UINT64)ApLoopFunc,
+ (UINT64)RelocatedMailBox,
+ 0,
+ 0,
+ 0);
+
+ return (UINT64)RelocatedMailBox;
+}
+
+/**
+
+ This Function checks if TDX is available, if present then it sets
+ the dynamic PcdTdxIsEnabled and PcdIa32EferChangeAllowed.
+
+ It relocates the td mailbox and create the PlatformInfo Hob which includes
+ the TDX specific information which will be consumed in DXE phase.
+
+ **/
+VOID
+IntelTdxInitialize (
+ VOID
+ )
+{
+ EFI_HOB_PLATFORM_INFO PlatformInfoHob;
+ RETURN_STATUS PcdStatus;
+
+ if (!PlatformPeiIsTdxGuest ()) {
+ return;
+ }
+
+ PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, CCAttrIntelTdx);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ PcdStatus = PcdSetBoolS (PcdIa32EferChangeAllowed, FALSE);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ PcdStatus = PcdSet64S (PcdTdxSharedBitMask, TdSharedPageMask ());
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ ZeroMem (&PlatformInfoHob, sizeof (PlatformInfoHob));
+ PlatformInfoHob.HostBridgePciDevId = mHostBridgeDevId;
+
+ PlatformInfoHob.RelocatedMailBox = TdxRelocateMailbox ();
+
+ CheckSystemStatsForOverride (&PlatformInfoHob);
+
+ BuildGuidDataHob (&gUefiOvmfPkgTdxPlatformGuid, &PlatformInfoHob, sizeof (EFI_HOB_PLATFORM_INFO));
+}
diff --git a/OvmfPkg/PlatformPei/IntelTdxNull.c b/OvmfPkg/PlatformPei/IntelTdxNull.c
new file mode 100644
index 000000000000..35a079d82f66
--- /dev/null
+++ b/OvmfPkg/PlatformPei/IntelTdxNull.c
@@ -0,0 +1,49 @@
+/** @file
+ Main SEC phase code. Handles initial TDX Hob List Processing
+
+ Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <IndustryStandard/IntelTdx.h>
+
+/**
+ Check if it is Tdx guest
+
+ @retval TRUE It is Tdx guest
+ @retval FALSE It is not Tdx guest
+**/
+BOOLEAN
+PlatformPeiIsTdxGuest (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+VOID
+TdxPublishRamRegions (
+ VOID
+ )
+{
+}
+
+VOID
+IntelTdxInitialize (
+ VOID
+ )
+{
+}
+
+VOID
+AsmGetRelocationMap (
+ OUT MP_RELOCATION_MAP *AddressMap
+ )
+{
+}
diff --git a/OvmfPkg/PlatformPei/MemDetect.c b/OvmfPkg/PlatformPei/MemDetect.c
index d736b85e0d90..1724bd9638df 100644
--- a/OvmfPkg/PlatformPei/MemDetect.c
+++ b/OvmfPkg/PlatformPei/MemDetect.c
@@ -35,6 +35,7 @@ Module Name:
#include <Library/MtrrLib.h>
#include <Library/QemuFwCfgLib.h>
#include <Library/QemuFwCfgSimpleParserLib.h>
+#include <Library/TdxLib.h>
#include "Platform.h"
#include "Cmos.h"
@@ -488,6 +489,7 @@ AddressWidthInitialization (
)
{
UINT64 FirstNonAddress;
+ UINT64 TdxSharedPageMask;
//
// As guest-physical memory size grows, the permanent PEI RAM requirements
@@ -515,7 +517,17 @@ AddressWidthInitialization (
if (mPhysMemAddressWidth <= 36) {
mPhysMemAddressWidth = 36;
}
- ASSERT (mPhysMemAddressWidth <= 48);
+
+ if (PlatformPeiIsTdxGuest ()) {
+ TdxSharedPageMask = TdSharedPageMask ();
+ if (TdxSharedPageMask == (1ULL << 47)) {
+ mPhysMemAddressWidth = 48;
+ } else {
+ mPhysMemAddressWidth = 52;
+ }
+ }
+
+ ASSERT (mPhysMemAddressWidth <= 52);
}
@@ -532,8 +544,10 @@ GetPeiMemoryCap (
UINT32 RegEax;
UINT32 RegEdx;
UINT32 Pml4Entries;
+ UINT32 Pml5Entries;
UINT32 PdpEntries;
UINTN TotalPages;
+ UINT8 PhysicalAddressBits;
//
// If DXE is 32-bit, then just return the traditional 64 MB cap.
@@ -561,20 +575,33 @@ GetPeiMemoryCap (
}
}
- if (mPhysMemAddressWidth <= 39) {
- Pml4Entries = 1;
- PdpEntries = 1 << (mPhysMemAddressWidth - 30);
- ASSERT (PdpEntries <= 0x200);
+ PhysicalAddressBits = mPhysMemAddressWidth;
+ Pml5Entries = 1;
+
+ if (PhysicalAddressBits > 48) {
+ Pml5Entries = (UINT32) LShiftU64 (1, PhysicalAddressBits - 48);
+ PhysicalAddressBits = 48;
+ }
+
+ Pml4Entries = 1;
+ if (PhysicalAddressBits > 39) {
+ Pml4Entries = (UINT32) LShiftU64 (1, PhysicalAddressBits - 39);
+ PhysicalAddressBits = 39;
+ }
+
+ PdpEntries = 1;
+ ASSERT (PhysicalAddressBits > 30);
+ PdpEntries = (UINT32) LShiftU64 (1, PhysicalAddressBits - 30);
+
+ //
+ // Pre-allocate big pages to avoid later allocations.
+ //
+ if (!Page1GSupport) {
+ TotalPages = ((PdpEntries + 1) * Pml4Entries + 1) * Pml5Entries + 1;
} else {
- Pml4Entries = 1 << (mPhysMemAddressWidth - 39);
- ASSERT (Pml4Entries <= 0x200);
- PdpEntries = 512;
+ TotalPages = (Pml4Entries + 1) * Pml5Entries + 1;
}
- TotalPages = Page1GSupport ? Pml4Entries + 1 :
- (PdpEntries + 1) * Pml4Entries + 1;
- ASSERT (TotalPages <= 0x40201);
-
//
// Add 64 MB for miscellaneous allocations. Note that for
// mPhysMemAddressWidth values close to 36, the cap will actually be
@@ -819,7 +846,11 @@ InitializeRamRegions (
VOID
)
{
- QemuInitializeRam ();
+ if (PlatformPeiIsTdxGuest ()) {
+ TdxPublishRamRegions ();
+ } else {
+ QemuInitializeRam ();
+ }
if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) {
//
diff --git a/OvmfPkg/PlatformPei/Platform.c b/OvmfPkg/PlatformPei/Platform.c
index df2d9ad015aa..7b3d187eea43 100644
--- a/OvmfPkg/PlatformPei/Platform.c
+++ b/OvmfPkg/PlatformPei/Platform.c
@@ -756,6 +756,7 @@ InitializePlatform (
InstallClearCacheCallback ();
AmdSevInitialize ();
+ IntelTdxInitialize ();
MiscInitialization ();
InstallFeatureControlCallback ();
diff --git a/OvmfPkg/PlatformPei/Platform.h b/OvmfPkg/PlatformPei/Platform.h
index 8b1d270c2b0b..bf12b52d153e 100644
--- a/OvmfPkg/PlatformPei/Platform.h
+++ b/OvmfPkg/PlatformPei/Platform.h
@@ -10,6 +10,7 @@
#define _PLATFORM_PEI_H_INCLUDED_
#include <IndustryStandard/E820.h>
+#include <IndustryStandard/IntelTdx.h>
VOID
AddIoMemoryBaseSizeHob (
@@ -102,6 +103,33 @@ AmdSevInitialize (
VOID
);
+VOID
+TdxPublishRamRegions (
+ VOID
+ );
+
+VOID
+AsmGetRelocationMap (
+ OUT MP_RELOCATION_MAP *AddressMap
+ );
+
+
+VOID
+IntelTdxInitialize (
+ VOID
+ );
+
+/**
+ Check if it is Tdx guest
+
+ @retval TRUE It is Tdx guest
+ @retval FALSE It is not Tdx guest
+**/
+BOOLEAN
+PlatformPeiIsTdxGuest (
+ VOID
+ );
+
extern EFI_BOOT_MODE mBootMode;
extern BOOLEAN mS3Supported;
diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index 67eb7aa7166b..9bddf14edc9b 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -34,6 +34,13 @@
Platform.c
Platform.h
+[Sources.IA32, Sources.EBC]
+ IntelTdxNull.c
+
+[Sources.X64]
+ IntelTdx.c
+ X64/ApRunLoop.nasm
+
[Packages]
EmbeddedPkg/EmbeddedPkg.dec
MdePkg/MdePkg.dec
@@ -44,6 +51,7 @@
[Guids]
gEfiMemoryTypeInformationGuid
+ gUefiOvmfPkgTdxPlatformGuid
[LibraryClasses]
BaseLib
@@ -62,6 +70,9 @@
MtrrLib
MemEncryptSevLib
PcdLib
+ TdxMailboxLib
+ TdxLib
+ MemoryAllocationLib
[Pcd]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase
@@ -106,6 +117,9 @@
gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber
gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize
gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled
+ gEfiMdeModulePkgTokenSpaceGuid.PcdIa32EferChangeAllowed
+ gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr
+ gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask
[FixedPcd]
gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
diff --git a/OvmfPkg/PlatformPei/X64/ApRunLoop.nasm b/OvmfPkg/PlatformPei/X64/ApRunLoop.nasm
new file mode 100644
index 000000000000..adf4f03c3a9e
--- /dev/null
+++ b/OvmfPkg/PlatformPei/X64/ApRunLoop.nasm
@@ -0,0 +1,83 @@
+;------------------------------------------------------------------------------ ;
+; Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+; ApRunLoop.nasm
+;
+; Abstract:
+;
+; This is the assembly code for run loop for APs in the guest TD
+;
+;-------------------------------------------------------------------------------
+
+%include "TdxCommondefs.inc"
+
+DEFAULT REL
+
+SECTION .text
+
+BITS 64
+
+%macro tdcall 0
+ db 0x66, 0x0f, 0x01, 0xcc
+%endmacro
+
+;
+; Relocated Ap Mailbox loop
+;
+; @param[in] RBX: Relocated mailbox address
+; @param[in] RBP: vCpuId
+;
+; @return None This routine does not return
+;
+global ASM_PFX(AsmRelocateApMailBoxLoop)
+ASM_PFX(AsmRelocateApMailBoxLoop):
+AsmRelocateApMailBoxLoopStart:
+
+ ;
+ ; TdCall[TDINFO] to get the vCpuId
+ ;
+ ;mov rax, 1
+ ;tdcall
+ ;
+ ; R8 [31:0] NUM_VCPUS
+ ; [63:32] MAX_VCPUS
+ ; R9 [31:0] VCPU_INDEX
+ ;
+
+ mov r8, rbp
+MailBoxLoop:
+ ; Spin until command set
+ cmp dword [rbx + CommandOffset], MpProtectedModeWakeupCommandNoop
+ je MailBoxLoop
+ ; Determine if this is a broadcast or directly for my apic-id, if not, ignore
+ cmp dword [rbx + ApicidOffset], MailboxApicidBroadcast
+ je MailBoxProcessCommand
+ cmp dword [rbx + ApicidOffset], r8d
+ jne MailBoxLoop
+MailBoxProcessCommand:
+ cmp dword [rbx + CommandOffset], MpProtectedModeWakeupCommandWakeup
+ je MailBoxWakeUp
+ cmp dword [rbx + CommandOffset], MpProtectedModeWakeupCommandSleep
+ je MailBoxSleep
+ ; Don't support this command, so ignore
+ jmp MailBoxLoop
+MailBoxWakeUp:
+ mov rax, [rbx + WakeupVectorOffset]
+ jmp rax
+MailBoxSleep:
+ jmp $
+BITS 64
+AsmRelocateApMailBoxLoopEnd:
+
+;-------------------------------------------------------------------------------------
+; AsmGetRelocationMap (&RelocationMap);
+;-------------------------------------------------------------------------------------
+global ASM_PFX(AsmGetRelocationMap)
+ASM_PFX(AsmGetRelocationMap):
+ lea rax, [ASM_PFX(AsmRelocateApMailBoxLoopStart)]
+ mov qword [rcx], rax
+ mov qword [rcx + 8h], AsmRelocateApMailBoxLoopEnd - AsmRelocateApMailBoxLoopStart
+ ret
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 25/29] OvmfPkg: Update AcpiPlatformDxe to alter MADT table
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (23 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 24/29] OvmfPkg: Update PlatformPei to support TDX Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-01 13:16 ` [PATCH V3 26/29] OvmfPkg: Add TdxDxe driver Min Xu
` (3 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In TDX the guest firmware is designed to publish a multiprocessor-wakeup
structure to let the guest-bootstrap processor wake up guest-application
processors with a mailbox. The mailbox is memory that the guest firmware
can reserve so each guest virtual processor can have the guest OS send
a message to them. The address of the mailbox is recorded in the MADT
table. See [ACPI].
To maintain the simplicity of the AcpiPlatformDxe, the MADT ACPI table
will be altered in another driver (TdxDxe) by installing a protocol
to notify that the ACPI table provided by Qemu is ready. Then in TdxDxe
a notification functioin will be called to alter the MADT table to recorded
the mailbox address.
The protocol is gQemuAcpiTableNotifyProtocolGuid.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 12 +++++++-
.../QemuFwCfgAcpiPlatformDxe.inf | 1 +
.../Include/Protocol/QemuAcpiTableNotify.h | 29 +++++++++++++++++++
3 files changed, 41 insertions(+), 1 deletion(-)
create mode 100644 OvmfPkg/Include/Protocol/QemuAcpiTableNotify.h
diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
index df800b149275..e8a9aff089e8 100644
--- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
+++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
@@ -19,7 +19,10 @@
#include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3Enabled()
#include <Library/UefiBootServicesTableLib.h> // gBS
+#include <Protocol/QemuAcpiTableNotify.h>
#include "AcpiPlatform.h"
+EFI_HANDLE mQemuAcpiHandle = NULL;
+QEMU_ACPI_TABLE_NOTIFY_PROTOCOL mAcpiNotifyProtocol;
//
// The user structure for the ordered collection that will track the fw_cfg
@@ -1129,7 +1132,14 @@ UninstallAcpiTables:
AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
}
} else {
- DEBUG ((DEBUG_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
+ //
+ // Install a protocol to notify that the ACPI table provided by Qemu is
+ // ready.
+ //
+ gBS->InstallProtocolInterface (&mQemuAcpiHandle,
+ &gQemuAcpiTableNotifyProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mAcpiNotifyProtocol);
}
for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);
diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf
index dac25d1505be..6ffce2db5f99 100644
--- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf
+++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf
@@ -48,6 +48,7 @@
[Protocols]
gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUMED
gEfiPciIoProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+ gQemuAcpiTableNotifyProtocolGuid # PROTOCOL PRODUCES
[Guids]
gRootBridgesConnectedEventGroupGuid
diff --git a/OvmfPkg/Include/Protocol/QemuAcpiTableNotify.h b/OvmfPkg/Include/Protocol/QemuAcpiTableNotify.h
new file mode 100644
index 000000000000..8549ea9ec374
--- /dev/null
+++ b/OvmfPkg/Include/Protocol/QemuAcpiTableNotify.h
@@ -0,0 +1,29 @@
+/** @file
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef QEMU_ACPI_TABLE_NOTIFY_H_
+#define QEMU_ACPI_TABLE_NOTIFY_H_
+
+
+#define QEMU_ACPI_TABLE_NOTIFY_GUID \
+ { 0x928939b2, 0x4235, 0x462f, { 0x95, 0x80, 0xf6, 0xa2, 0xb2, 0xc2, 0x1a, 0x4f } };
+
+
+///
+/// Forward declaration
+///
+typedef struct _QEMU_ACPI_TABLE_NOTIFY_PROTOCOL QEMU_ACPI_TABLE_NOTIFY_PROTOCOL;
+
+///
+/// Protocol structure
+///
+struct _QEMU_ACPI_TABLE_NOTIFY_PROTOCOL {
+ UINT8 Notify;
+};
+
+extern EFI_GUID gQemuAcpiTableNotifyProtocolGuid;
+
+#endif
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 26/29] OvmfPkg: Add TdxDxe driver
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (24 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 25/29] OvmfPkg: Update AcpiPlatformDxe to alter MADT table Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-01 13:16 ` [PATCH V3 27/29] OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library Min Xu
` (2 subsequent siblings)
28 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
TdxDxe driver is dispatched early in DXE, due to being list in APRIORI.
This module is responsible for below features:
- Sets max logical cpus based on TDINFO
- Sets PCI PCDs based on resource hobs
Besides above features, TdxDxe driver will update the ACPI MADT
Mutiprocessor Wakeup Table.
In TDX the guest firmware is designed to publish a multiprocessor-wakeup
structure to let the guest-bootstrap processor wake up guest-application
processors with a mailbox. The mailbox is memory that the guest firmware
can reserve so each guest virtual processor can have the guest OS send
a message to them. The address of the mailbox is recorded in the MADT
table. See [ACPI].
TdxDxe registers for protocol notification
(gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in
which MADT table is altered by the above Mailbox address. The protocol
will be installed in AcpiPlatformDxe when the MADT table provided by
Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe.
AlterAcpiTable is the registered function which traverses the ACPI
table list to find the original MADT from Qemu. After the new MADT is
configured and installed, the original one will be uninstalled.
[ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model
/ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/Include/IndustryStandard/IntelTdx.h | 5 +-
OvmfPkg/OvmfPkg.dec | 4 +
OvmfPkg/OvmfPkgX64.dsc | 2 +
OvmfPkg/OvmfPkgX64.fdf | 3 +
OvmfPkg/TdxDxe/TdxAcpiTable.c | 112 +++++++++++
OvmfPkg/TdxDxe/TdxAcpiTable.h | 38 ++++
OvmfPkg/TdxDxe/TdxDxe.c | 207 ++++++++++++++++++++
OvmfPkg/TdxDxe/TdxDxe.inf | 62 ++++++
8 files changed, 430 insertions(+), 3 deletions(-)
create mode 100644 OvmfPkg/TdxDxe/TdxAcpiTable.c
create mode 100644 OvmfPkg/TdxDxe/TdxAcpiTable.h
create mode 100644 OvmfPkg/TdxDxe/TdxDxe.c
create mode 100644 OvmfPkg/TdxDxe/TdxDxe.inf
diff --git a/OvmfPkg/Include/IndustryStandard/IntelTdx.h b/OvmfPkg/Include/IndustryStandard/IntelTdx.h
index 2370f18289a1..bb02970394d7 100644
--- a/OvmfPkg/Include/IndustryStandard/IntelTdx.h
+++ b/OvmfPkg/Include/IndustryStandard/IntelTdx.h
@@ -6,8 +6,8 @@
**/
-#ifndef _OVMF_INTEL_TDX__H_
-#define _OVMF_INTEL_TDX__H_
+#ifndef OVMF_INTEL_TDX_H_
+#define OVMF_INTEL_TDX_H_
#include <PiPei.h>
#include <Library/BaseLib.h>
@@ -52,7 +52,6 @@ typedef enum {
UINT8 Pad3[0xf8];
} MP_WAKEUP_MAILBOX;
-
//
// AP relocation code information including code address and size,
// this structure will be shared be C code and assembly code.
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index e8cd126fc161..ed7f2ff42465 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -153,6 +153,7 @@
gEfiLegacyInterruptProtocolGuid = {0x31ce593d, 0x108a, 0x485d, {0xad, 0xb2, 0x78, 0xf2, 0x1f, 0x29, 0x66, 0xbe}}
gEfiVgaMiniPortProtocolGuid = {0xc7735a2f, 0x88f5, 0x4882, {0xae, 0x63, 0xfa, 0xac, 0x8c, 0x8b, 0x86, 0xb3}}
gOvmfLoadedX86LinuxKernelProtocolGuid = {0xa3edc05d, 0xb618, 0x4ff6, {0x95, 0x52, 0x76, 0xd7, 0x88, 0x63, 0x43, 0xc8}}
+ gQemuAcpiTableNotifyProtocolGuid = {0x928939b2, 0x4235, 0x462f, {0x95, 0x80, 0xf6, 0xa2, 0xb2, 0xc2, 0x1a, 0x4f}}
[PcdsFixedAtBuild]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase|0x0|UINT32|0
@@ -403,6 +404,9 @@
# instance in PiSmmCpuDxeSmm, and CpuHotplugSmm.
gUefiOvmfPkgTokenSpaceGuid.PcdCpuHotEjectDataAddress|0|UINT64|0x46
+ ## TDX relocated Mailbox base address
+ gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase|0|UINT64|0x60
+
[PcdsFeatureFlag]
gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c
gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 850953f20e35..c8ab0dc7060e 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -978,6 +978,8 @@
OvmfPkg/AmdSevDxe/AmdSevDxe.inf
OvmfPkg/IoMmuDxe/IoMmuDxe.inf
+ OvmfPkg/TdxDxe/TdxDxe.inf
+
!if $(SMM_REQUIRE) == TRUE
OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index b6cc3cabdd69..bbd9303ab14f 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -214,6 +214,7 @@ READ_LOCK_STATUS = TRUE
APRIORI DXE {
INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
INF MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+ INF OvmfPkg/TdxDxe/TdxDxe.inf
INF OvmfPkg/AmdSevDxe/AmdSevDxe.inf
!if $(SMM_REQUIRE) == FALSE
INF OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
@@ -319,6 +320,8 @@ INF ShellPkg/Application/Shell/Shell.inf
INF MdeModulePkg/Logo/LogoDxe.inf
+INF OvmfPkg/TdxDxe/TdxDxe.inf
+
#
# Network modules
#
diff --git a/OvmfPkg/TdxDxe/TdxAcpiTable.c b/OvmfPkg/TdxDxe/TdxAcpiTable.c
new file mode 100644
index 000000000000..ce3b3f0afcd5
--- /dev/null
+++ b/OvmfPkg/TdxDxe/TdxAcpiTable.c
@@ -0,0 +1,112 @@
+/** @file
+ OVMF ACPI QEMU support
+
+ Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ Copyright (C) 2012-2014, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/PcdLib.h>
+#include <Library/OrderedCollectionLib.h>
+#include <Library/TdxLib.h>
+#include <IndustryStandard/Acpi.h>
+#include <Protocol/AcpiSystemDescriptionTable.h>
+#include <Protocol/AcpiTable.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Protocol/Cpu.h>
+#include <Uefi.h>
+#include <TdxAcpiTable.h>
+
+/**
+ Alter the MADT when ACPI Table from QEMU is available.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+**/
+VOID
+EFIAPI
+AlterAcpiTable (
+ IN EFI_EVENT Event,
+ IN VOID* Context
+ )
+{
+ EFI_ACPI_SDT_PROTOCOL *AcpiSdtProtocol;
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol;
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_ACPI_SDT_HEADER *Table;
+ EFI_ACPI_TABLE_VERSION Version;
+ UINTN OriginalTableKey;
+ UINTN NewTableKey;
+ UINT8 *NewMadtTable;
+ UINTN NewMadtTableLength;
+ EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE *MadtMpWk;
+ EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *MadtHeader;
+
+ Index = 0;
+ NewMadtTable = NULL;
+ MadtHeader = NULL;
+
+ Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (void **) &AcpiSdtProtocol);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Unable to locate ACPI SDT protocol.\n"));
+ return;
+ }
+
+ do {
+ Status = AcpiSdtProtocol->GetAcpiTable (Index, &Table, &Version, &OriginalTableKey);
+
+ if (!EFI_ERROR (Status) && Table->Signature == EFI_ACPI_1_0_APIC_SIGNATURE) {
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (void **) &AcpiTableProtocol);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Unable to locate ACPI Table protocol.\n"));
+ break;
+ }
+
+ NewMadtTableLength = Table->Length + sizeof (EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE);
+ NewMadtTable = AllocatePool (NewMadtTableLength);
+ if (NewMadtTable == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: OUT_OF_SOURCES error.\n", __FUNCTION__));
+ break;
+ }
+
+ CopyMem (NewMadtTable, (UINT8 *)Table, Table->Length);
+ MadtHeader = (EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *)NewMadtTable;
+ MadtHeader->Header.Length = (UINT32)NewMadtTableLength;
+
+ MadtMpWk = (EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE *)(NewMadtTable + Table->Length);
+ MadtMpWk->Type = EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP;
+ MadtMpWk->Length = sizeof (EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE);
+ MadtMpWk->MailBoxVersion = 1;
+ MadtMpWk->Reserved = 0;
+ MadtMpWk->MailBoxAddress = PcdGet64 (PcdTdRelocatedMailboxBase);
+
+ Status = AcpiTableProtocol->InstallAcpiTable (AcpiTableProtocol, NewMadtTable, NewMadtTableLength, &NewTableKey);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to install new MADT table. %r\n", Status));
+ break;
+ }
+
+ Status = AcpiTableProtocol->UninstallAcpiTable (AcpiTableProtocol, OriginalTableKey);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Uninstall old MADT table error.\n"));
+ }
+ break;
+ }
+ Index ++;
+ } while (!EFI_ERROR (Status));
+
+ if (NewMadtTable != NULL) {
+ FreePool (NewMadtTable);
+ }
+}
diff --git a/OvmfPkg/TdxDxe/TdxAcpiTable.h b/OvmfPkg/TdxDxe/TdxAcpiTable.h
new file mode 100644
index 000000000000..36aaab9d1f41
--- /dev/null
+++ b/OvmfPkg/TdxDxe/TdxAcpiTable.h
@@ -0,0 +1,38 @@
+/** @file
+ Sample ACPI Platform Driver
+
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _TDX_QEMU_ACPI_H_INCLUDED_
+#define _TDX_QEMU_ACPI_H_INCLUDED_
+
+#include <PiDxe.h>
+
+#include <Protocol/AcpiTable.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/PciIo.h>
+
+#include <Library/BaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+
+#include <IndustryStandard/Acpi.h>
+
+/**
+ Alter the MADT when ACPI Table from QEMU is available.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+**/
+VOID
+EFIAPI
+AlterAcpiTable (
+ IN EFI_EVENT Event,
+ IN VOID* Context
+ );
+
+#endif
diff --git a/OvmfPkg/TdxDxe/TdxDxe.c b/OvmfPkg/TdxDxe/TdxDxe.c
new file mode 100644
index 000000000000..eecad8f6e050
--- /dev/null
+++ b/OvmfPkg/TdxDxe/TdxDxe.c
@@ -0,0 +1,207 @@
+/** @file
+
+ TDX Dxe driver. This driver is dispatched early in DXE, due to being list
+ in APRIORI.
+
+ This module is responsible for:
+ - Sets max logical cpus based on TDINFO
+ - Sets PCI PCDs based on resource hobs
+ - Alter MATD table to record address of Mailbox
+
+ Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiLib.h>
+#include <Library/HobLib.h>
+#include <Protocol/Cpu.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <IndustryStandard/IntelTdx.h>
+#include <Library/TdxLib.h>
+#include <TdxAcpiTable.h>
+
+/**
+ Location of resource hob matching type and starting address
+
+ @param[in] Type The type of resource hob to locate.
+
+ @param[in] Start The resource hob must at least begin at address.
+
+ @retval pointer to resource Return pointer to a resource hob that matches or NULL.
+**/
+STATIC
+EFI_HOB_RESOURCE_DESCRIPTOR *
+GetResourceDescriptor(
+ EFI_RESOURCE_TYPE Type,
+ EFI_PHYSICAL_ADDRESS Start,
+ EFI_PHYSICAL_ADDRESS End
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor = NULL;
+
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
+ while (Hob.Raw != NULL) {
+
+ DEBUG ((DEBUG_INFO, "%a:%d: resource type 0x%x %llx %llx\n",
+ __func__, __LINE__,
+ Hob.ResourceDescriptor->ResourceType,
+ Hob.ResourceDescriptor->PhysicalStart,
+ Hob.ResourceDescriptor->ResourceLength));
+
+ if ((Hob.ResourceDescriptor->ResourceType == Type) &&
+ (Hob.ResourceDescriptor->PhysicalStart >= Start) &&
+ ((Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength) < End)) {
+ ResourceDescriptor = Hob.ResourceDescriptor;
+ break;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
+ }
+
+ return ResourceDescriptor;
+}
+
+/**
+ Location of resource hob matching type and highest address below end
+
+ @param[in] Type The type of resource hob to locate.
+
+ @param[in] End The resource hob return is the closest to the End address
+
+ @retval pointer to resource Return pointer to a resource hob that matches or NULL.
+**/
+STATIC
+EFI_HOB_RESOURCE_DESCRIPTOR *
+GetHighestResourceDescriptor(
+ EFI_RESOURCE_TYPE Type,
+ EFI_PHYSICAL_ADDRESS End
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor = NULL;
+
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
+ while (Hob.Raw != NULL) {
+ if ((Hob.ResourceDescriptor->ResourceType == Type) &&
+ (Hob.ResourceDescriptor->PhysicalStart < End)) {
+ if (!ResourceDescriptor ||
+ (ResourceDescriptor->PhysicalStart < Hob.ResourceDescriptor->PhysicalStart)) {
+ ResourceDescriptor = Hob.ResourceDescriptor;
+ }
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
+ }
+
+ return ResourceDescriptor;
+}
+
+EFI_STATUS
+EFIAPI
+TdxDxeEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+
+ EFI_STATUS Status;
+ RETURN_STATUS PcdStatus;
+ EFI_HOB_RESOURCE_DESCRIPTOR *Res = NULL;
+ EFI_HOB_RESOURCE_DESCRIPTOR *MemRes = NULL;
+ EFI_HOB_PLATFORM_INFO *PlatformInfo = NULL;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ UINT32 CpuMaxLogicalProcessorNumber;
+ TD_RETURN_DATA TdReturnData;
+ EFI_EVENT QemuAcpiTableEvent;
+ void *Registration;
+
+ GuidHob = GetFirstGuidHob (&gUefiOvmfPkgTdxPlatformGuid);
+
+ if(GuidHob == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ PlatformInfo = (EFI_HOB_PLATFORM_INFO *) GET_GUID_HOB_DATA (GuidHob);
+
+ //
+ // Call TDINFO to get actual number of cpus in domain
+ //
+ Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ ASSERT(Status == EFI_SUCCESS);
+
+ CpuMaxLogicalProcessorNumber = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
+
+ //
+ // Adjust PcdCpuMaxLogicalProcessorNumber, if needed. If firmware is configured for
+ // more than number of reported cpus, update.
+ //
+ if (CpuMaxLogicalProcessorNumber > TdReturnData.TdInfo.NumVcpus) {
+ PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, TdReturnData.TdInfo.NumVcpus);
+ ASSERT_RETURN_ERROR(PcdStatus);
+ }
+
+ //
+ // Register for protocol notifications to call the AlterAcpiTable(),
+ // the protocol will be installed in AcpiPlatformDxe when the ACPI
+ // table provided by Qemu is ready.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ AlterAcpiTable,
+ NULL,
+ &QemuAcpiTableEvent
+ );
+
+ Status = gBS->RegisterProtocolNotify (
+ &gQemuAcpiTableNotifyProtocolGuid,
+ QemuAcpiTableEvent,
+ &Registration
+ );
+
+#define INIT_PCDSET(NAME, RES) do { \
+ PcdStatus = PcdSet64S (NAME##Base, (RES)->PhysicalStart); \
+ ASSERT_RETURN_ERROR (PcdStatus); \
+ PcdStatus = PcdSet64S (NAME##Size, (RES)->ResourceLength); \
+ ASSERT_RETURN_ERROR (PcdStatus); \
+} while(0)
+
+ if (PlatformInfo) {
+ PcdSet16S (PcdOvmfHostBridgePciDevId, PlatformInfo->HostBridgePciDevId);
+
+ if ((Res = GetResourceDescriptor(EFI_RESOURCE_MEMORY_MAPPED_IO, (EFI_PHYSICAL_ADDRESS)0x100000000, (EFI_PHYSICAL_ADDRESS)-1)) != NULL) {
+ INIT_PCDSET(PcdPciMmio64, Res);
+ }
+
+ if ((Res = GetResourceDescriptor(EFI_RESOURCE_IO, 0, 0x10001)) != NULL) {
+ INIT_PCDSET(PcdPciIo, Res);
+ }
+
+ //
+ // To find low mmio, first find top of low memory, and then search for io space.
+ //
+ if ((MemRes = GetHighestResourceDescriptor(EFI_RESOURCE_SYSTEM_MEMORY, 0xffc00000)) != NULL) {
+ if ((Res = GetResourceDescriptor(EFI_RESOURCE_MEMORY_MAPPED_IO, MemRes->PhysicalStart, 0x100000000)) != NULL) {
+ INIT_PCDSET(PcdPciMmio32, Res);
+ }
+ }
+ //
+ // Set initial protected mode reset address to our initial mailbox
+ // After DXE, will update address before exiting
+ //
+ PcdStatus = PcdSet64S (PcdTdRelocatedMailboxBase, PlatformInfo->RelocatedMailBox);
+ ASSERT_RETURN_ERROR(PcdStatus);
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/TdxDxe/TdxDxe.inf b/OvmfPkg/TdxDxe/TdxDxe.inf
new file mode 100644
index 000000000000..b77c6e5e9252
--- /dev/null
+++ b/OvmfPkg/TdxDxe/TdxDxe.inf
@@ -0,0 +1,62 @@
+#/** @file
+#
+# Driver clears the encryption attribute from MMIO regions when TDX is enabled
+#
+# Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = TdxDxe
+ FILE_GUID = E750224E-7BCE-40AF-B5BB-47E3611EB5C2
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = TdxDxeEntryPoint
+
+[Sources]
+ TdxDxe.c
+ TdxAcpiTable.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ PcdLib
+ UefiDriverEntryPoint
+ TdxLib
+ HobLib
+
+[Depex]
+ TRUE
+
+[Guids]
+ gUefiOvmfPkgTdxPlatformGuid ## CONSUMES
+
+[Protocols]
+ gQemuAcpiTableNotifyProtocolGuid ## CONSUMES
+ gEfiAcpiSdtProtocolGuid ## CONSUMES
+ gEfiAcpiTableProtocolGuid ## CONSUMES
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciIoBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciIoSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Base
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Size
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Base
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
+ gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* [PATCH V3 27/29] OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (25 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 26/29] OvmfPkg: Add TdxDxe driver Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 7:10 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 28/29] OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe Min Xu
2021-11-01 13:16 ` [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX Min Xu
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Add Intel Tdx helper library. The library provides the routines to:
- set or clear Shared bit for a given memory region.
- query whether TDX is enabled.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/Include/Library/MemEncryptTdxLib.h | 81 ++
.../BaseMemEncryptTdxLib.inf | 44 +
.../BaseMemEncryptTdxLibNull.inf | 35 +
.../BaseMemoryEncryptionNull.c | 90 ++
.../BaseMemEncryptTdxLib/MemoryEncryption.c | 938 ++++++++++++++++++
.../BaseMemEncryptTdxLib/VirtualMemory.h | 181 ++++
OvmfPkg/OvmfPkg.dec | 4 +
OvmfPkg/OvmfPkgIa32.dsc | 1 +
OvmfPkg/OvmfPkgIa32X64.dsc | 1 +
9 files changed, 1375 insertions(+)
create mode 100644 OvmfPkg/Include/Library/MemEncryptTdxLib.h
create mode 100644 OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf
create mode 100644 OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf
create mode 100644 OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c
create mode 100644 OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c
create mode 100644 OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h
diff --git a/OvmfPkg/Include/Library/MemEncryptTdxLib.h b/OvmfPkg/Include/Library/MemEncryptTdxLib.h
new file mode 100644
index 000000000000..6a482422f5ed
--- /dev/null
+++ b/OvmfPkg/Include/Library/MemEncryptTdxLib.h
@@ -0,0 +1,81 @@
+/** @file
+
+ Define Memory Encrypted Virtualization base library helper function
+
+ Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef MEM_ENCRYPT_TDX_LIB_H_
+#define MEM_ENCRYPT_TDX_LIB_H_
+
+#include <Base.h>
+
+/**
+ Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled
+
+ @param[in] Type Bitmask of encryption technologies to check is enabled
+
+ @retval TRUE The encryption type(s) are enabled
+ @retval FALSE The encryption type(s) are not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptTdxIsEnabled (
+ VOID
+ );
+
+/**
+ This function clears memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Clearing the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptTdxSetPageSharedBit (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages
+ );
+
+/**
+ This function sets memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+
+ @retval RETURN_SUCCESS The attributes were set for the memory
+ region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptTdxClearPageSharedBit (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages
+ );
+
+#endif // _MEM_ENCRYPT_TDX_LIB_H_
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf
new file mode 100644
index 000000000000..a8abfec12fa3
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf
@@ -0,0 +1,44 @@
+## @file
+# Library for TDX Memory Encryption
+#
+# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = MemEncryptTdxLib
+ FILE_GUID = 7E6651B2-B775-4593-A410-FC05B8C61993
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = MemEncryptTdxLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = X64
+#
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[Sources]
+ VirtualMemory.h
+ MemoryEncryption.c
+
+[LibraryClasses]
+ BaseLib
+ CacheMaintenanceLib
+ CpuLib
+ DebugLib
+ MemoryAllocationLib
+ PcdLib
+ TdxLib
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf
new file mode 100644
index 000000000000..a050edb5b734
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf
@@ -0,0 +1,35 @@
+## @file
+# Library for Memory Encryption
+#
+# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = MemEncryptTdxLibNull
+ FILE_GUID = 3C69C4CA-DE46-44D7-8AA5-6EE51A4E3EA7
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = MemEncryptTdxLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+# VALID_ARCHITECTURES = X64 IA32
+#
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[Sources]
+ BaseMemoryEncryptionNull.c
+
+[LibraryClasses]
+ BaseLib
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c
new file mode 100644
index 000000000000..3deb6ffeae3d
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c
@@ -0,0 +1,90 @@
+/** @file
+
+ Virtual Memory Management Services to set or clear the memory encryption
+
+ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
+
+**/
+
+#include <Uefi.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseLib.h>
+#include <Library/MemEncryptTdxLib.h>
+
+/**
+ Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled
+
+ @param[in] Type Bitmask of encryption technologies to check is enabled
+
+ @retval TRUE The encryption type(s) are enabled
+ @retval FALSE The encryption type(s) are not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptTdxIsEnabled (
+ VOID
+ )
+{
+ return FALSE;
+}
+
+/**
+ This function clears memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Clearing the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptTdxSetPageSharedBit (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ This function sets memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+
+ @retval RETURN_SUCCESS The attributes were set for the memory
+ region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptTdxClearPageSharedBit (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c
new file mode 100644
index 000000000000..521a2c1fd934
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c
@@ -0,0 +1,938 @@
+/** @file
+
+ Virtual Memory Management Services to set or clear the memory encryption
+
+ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
+
+**/
+
+#include <Uefi.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/CpuLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemEncryptTdxLib.h>
+#include "VirtualMemory.h"
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
+#include <ConfidentialComputingGuestAttr.h>
+
+typedef enum {
+ SetSharedBit,
+ ClearSharedBit
+} TDX_PAGETABLE_MODE;
+
+STATIC PAGE_TABLE_POOL *mPageTablePool = NULL;
+
+/**
+ Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled
+
+ @param[in] Type Bitmask of encryption technologies to check is enabled
+
+ @retval TRUE The encryption type(s) are enabled
+ @retval FALSE The encryption type(s) are not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptTdxIsEnabled (
+ VOID
+ )
+{
+ return CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr));
+}
+
+/**
+ Get the memory encryption mask
+
+ @param[out] EncryptionMask contains the pte mask.
+
+**/
+STATIC
+UINT64
+GetMemEncryptionAddressMask (
+ VOID
+ )
+{
+ return TdSharedPageMask();
+}
+
+/**
+ Initialize a buffer pool for page table use only.
+
+ To reduce the potential split operation on page table, the pages reserved for
+ page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
+ at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
+ initialized with number of pages greater than or equal to the given
+ PoolPages.
+
+ Once the pages in the pool are used up, this method should be called again to
+ reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't
+ happen often in practice.
+
+ @param[in] PoolPages The least page number of the pool to be created.
+
+ @retval TRUE The pool is initialized successfully.
+ @retval FALSE The memory is out of resource.
+**/
+STATIC
+BOOLEAN
+InitializePageTablePool (
+ IN UINTN PoolPages
+ )
+{
+ VOID *Buffer;
+
+ //
+ // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
+ // header.
+ //
+ PoolPages += 1; // Add one page for header.
+ PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
+ PAGE_TABLE_POOL_UNIT_PAGES;
+ Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
+ if (Buffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
+ return FALSE;
+ }
+
+ //
+ // Link all pools into a list for easier track later.
+ //
+ if (mPageTablePool == NULL) {
+ mPageTablePool = Buffer;
+ mPageTablePool->NextPool = mPageTablePool;
+ } else {
+ ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
+ mPageTablePool->NextPool = Buffer;
+ mPageTablePool = Buffer;
+ }
+
+ //
+ // Reserve one page for pool header.
+ //
+ mPageTablePool->FreePages = PoolPages - 1;
+ mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
+
+ return TRUE;
+}
+
+/**
+ This API provides a way to allocate memory for page table.
+
+ This API can be called more than once to allocate memory for page tables.
+
+ Allocates the number of 4KB pages and returns a pointer to the allocated
+ buffer. The buffer returned is aligned on a 4KB boundary.
+
+ If Pages is 0, then NULL is returned.
+ If there is not enough memory remaining to satisfy the request, then NULL is
+ returned.
+
+ @param Pages The number of 4 KB pages to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+STATIC
+VOID *
+EFIAPI
+AllocatePageTableMemory (
+ IN UINTN Pages
+ )
+{
+ VOID *Buffer;
+
+ if (Pages == 0) {
+ return NULL;
+ }
+
+ //
+ // Renew the pool if necessary.
+ //
+ if (mPageTablePool == NULL ||
+ Pages > mPageTablePool->FreePages) {
+ if (!InitializePageTablePool (Pages)) {
+ return NULL;
+ }
+ }
+
+ Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
+
+ mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
+ mPageTablePool->FreePages -= Pages;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: Buffer=0x%Lx Pages=%ld\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ Buffer,
+ Pages
+ ));
+
+ return Buffer;
+}
+
+
+/**
+ Split 2M page to 4K.
+
+ @param[in] PhysicalAddress Start physical address the 2M page
+ covered.
+ @param[in, out] PageEntry2M Pointer to 2M page entry.
+ @param[in] StackBase Stack base address.
+ @param[in] StackSize Stack size.
+
+**/
+STATIC
+VOID
+Split2MPageTo4K (
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN OUT UINT64 *PageEntry2M,
+ IN PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize,
+ IN UINT64 AddressEncMask
+ )
+{
+ PHYSICAL_ADDRESS PhysicalAddress4K;
+ UINTN IndexOfPageTableEntries;
+ PAGE_TABLE_4K_ENTRY *PageTableEntry, *PageTableEntry1;
+
+ PageTableEntry = AllocatePageTableMemory(1);
+
+ PageTableEntry1 = PageTableEntry;
+
+ if (PageTableEntry == NULL) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ PhysicalAddress4K = PhysicalAddress;
+ for (IndexOfPageTableEntries = 0;
+ IndexOfPageTableEntries < 512;
+ (IndexOfPageTableEntries++,
+ PageTableEntry++,
+ PhysicalAddress4K += SIZE_4KB)) {
+ //
+ // Fill in the Page Table entries
+ //
+ PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask;
+ PageTableEntry->Bits.ReadWrite = 1;
+ PageTableEntry->Bits.Present = 1;
+ if ((PhysicalAddress4K >= StackBase) &&
+ (PhysicalAddress4K < StackBase + StackSize)) {
+ //
+ // Set Nx bit for stack.
+ //
+ PageTableEntry->Bits.Nx = 1;
+ }
+ }
+
+ //
+ // Fill in 2M page entry.
+ //
+ *PageEntry2M = ((UINT64)(UINTN)PageTableEntry1 |
+ IA32_PG_P | IA32_PG_RW | AddressEncMask);
+}
+
+/**
+ Set one page of page table pool memory to be read-only.
+
+ @param[in] PageTableBase Base address of page table (CR3).
+ @param[in] Address Start address of a page to be set as read-only.
+ @param[in] Level4Paging Level 4 paging flag.
+
+**/
+STATIC
+VOID
+SetPageTablePoolReadOnly (
+ IN UINTN PageTableBase,
+ IN EFI_PHYSICAL_ADDRESS Address,
+ IN BOOLEAN Level4Paging
+ )
+{
+ UINTN Index;
+ UINTN EntryIndex;
+ UINT64 AddressEncMask;
+ UINT64 ActiveAddressEncMask;
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;
+ UINT64 *PageTable;
+ UINT64 *NewPageTable;
+ UINT64 PageAttr;
+ UINT64 LevelSize[5];
+ UINT64 LevelMask[5];
+ UINTN LevelShift[5];
+ UINTN Level;
+ UINT64 PoolUnitSize;
+
+ if (PageTableBase == 0) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ //
+ // Since the page table is always from page table pool, which is always
+ // located at the boundary of PcdPageTablePoolAlignment, we just need to
+ // set the whole pool unit to be read-only.
+ //
+ Address = Address & PAGE_TABLE_POOL_ALIGN_MASK;
+
+ LevelShift[1] = PAGING_L1_ADDRESS_SHIFT;
+ LevelShift[2] = PAGING_L2_ADDRESS_SHIFT;
+ LevelShift[3] = PAGING_L3_ADDRESS_SHIFT;
+ LevelShift[4] = PAGING_L4_ADDRESS_SHIFT;
+
+ LevelMask[1] = PAGING_4K_ADDRESS_MASK_64;
+ LevelMask[2] = PAGING_2M_ADDRESS_MASK_64;
+ LevelMask[3] = PAGING_1G_ADDRESS_MASK_64;
+ LevelMask[4] = PAGING_1G_ADDRESS_MASK_64;
+
+ LevelSize[1] = SIZE_4KB;
+ LevelSize[2] = SIZE_2MB;
+ LevelSize[3] = SIZE_1GB;
+ LevelSize[4] = SIZE_512GB;
+
+ AddressEncMask = GetMemEncryptionAddressMask() &
+ PAGING_1G_ADDRESS_MASK_64;
+ PageTable = (UINT64 *)(UINTN)PageTableBase;
+ PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE;
+
+ for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) {
+ Index = ((UINTN)RShiftU64 (Address, LevelShift[Level]));
+ Index &= PAGING_PAE_INDEX_MASK;
+
+ PageAttr = PageTable[Index];
+ ActiveAddressEncMask = GetMemEncryptionAddressMask() & PageAttr;
+
+ if ((PageAttr & IA32_PG_PS) == 0) {
+ //
+ // Go to next level of table.
+ //
+ PageTable = (UINT64 *)(UINTN)(PageAttr & ~AddressEncMask &
+ PAGING_4K_ADDRESS_MASK_64);
+ continue;
+ }
+
+ if (PoolUnitSize >= LevelSize[Level]) {
+ //
+ // Clear R/W bit if current page granularity is not larger than pool unit
+ // size.
+ //
+ if ((PageAttr & IA32_PG_RW) != 0) {
+ while (PoolUnitSize > 0) {
+ //
+ // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in
+ // one page (2MB). Then we don't need to update attributes for pages
+ // crossing page directory. ASSERT below is for that purpose.
+ //
+ ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64));
+
+ PageTable[Index] &= ~(UINT64)IA32_PG_RW;
+ PoolUnitSize -= LevelSize[Level];
+
+ ++Index;
+ }
+ }
+
+ break;
+
+ } else {
+ //
+ // The smaller granularity of page must be needed.
+ //
+ ASSERT (Level > 1);
+
+ NewPageTable = AllocatePageTableMemory (1);
+ if (NewPageTable == NULL) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ PhysicalAddress = PageAttr & LevelMask[Level];
+ for (EntryIndex = 0;
+ EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64);
+ ++EntryIndex) {
+ NewPageTable[EntryIndex] = PhysicalAddress | ActiveAddressEncMask |
+ IA32_PG_P | IA32_PG_RW;
+ if (Level > 2) {
+ NewPageTable[EntryIndex] |= IA32_PG_PS;
+ }
+ PhysicalAddress += LevelSize[Level - 1];
+ }
+
+ PageTable[Index] = (UINT64)(UINTN)NewPageTable | ActiveAddressEncMask |
+ IA32_PG_P | IA32_PG_RW;
+ PageTable = NewPageTable;
+ }
+ }
+}
+
+/**
+ Prevent the memory pages used for page table from been overwritten.
+
+ @param[in] PageTableBase Base address of page table (CR3).
+ @param[in] Level4Paging Level 4 paging flag.
+
+**/
+STATIC
+VOID
+EnablePageTableProtection (
+ IN UINTN PageTableBase,
+ IN BOOLEAN Level4Paging
+ )
+{
+ PAGE_TABLE_POOL *HeadPool;
+ PAGE_TABLE_POOL *Pool;
+ UINT64 PoolSize;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ if (mPageTablePool == NULL) {
+ return;
+ }
+
+ //
+ // SetPageTablePoolReadOnly might update mPageTablePool. It's safer to
+ // remember original one in advance.
+ //
+ HeadPool = mPageTablePool;
+ Pool = HeadPool;
+ do {
+ Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;
+ PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);
+
+ //
+ // The size of one pool must be multiple of PAGE_TABLE_POOL_UNIT_SIZE,
+ // which is one of page size of the processor (2MB by default). Let's apply
+ // the protection to them one by one.
+ //
+ while (PoolSize > 0) {
+ SetPageTablePoolReadOnly(PageTableBase, Address, Level4Paging);
+ Address += PAGE_TABLE_POOL_UNIT_SIZE;
+ PoolSize -= PAGE_TABLE_POOL_UNIT_SIZE;
+ }
+
+ Pool = Pool->NextPool;
+ } while (Pool != HeadPool);
+
+}
+
+
+/**
+ Split 1G page to 2M.
+
+ @param[in] PhysicalAddress Start physical address the 1G page
+ covered.
+ @param[in, out] PageEntry1G Pointer to 1G page entry.
+ @param[in] StackBase Stack base address.
+ @param[in] StackSize Stack size.
+
+**/
+STATIC
+VOID
+Split1GPageTo2M (
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN OUT UINT64 *PageEntry1G,
+ IN PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ )
+{
+ PHYSICAL_ADDRESS PhysicalAddress2M;
+ UINTN IndexOfPageDirectoryEntries;
+ PAGE_TABLE_ENTRY *PageDirectoryEntry;
+ UINT64 AddressEncMask;
+ UINT64 ActiveAddressEncMask;
+
+ PageDirectoryEntry = AllocatePageTableMemory(1);
+ if (PageDirectoryEntry == NULL) {
+ return;
+ }
+
+ AddressEncMask = GetMemEncryptionAddressMask ();
+ ASSERT (PageDirectoryEntry != NULL);
+
+ ActiveAddressEncMask = *PageEntry1G & AddressEncMask;
+ //
+ // Fill in 1G page entry.
+ //
+ *PageEntry1G = ((UINT64)(UINTN)PageDirectoryEntry |
+ IA32_PG_P | IA32_PG_RW | ActiveAddressEncMask);
+
+ PhysicalAddress2M = PhysicalAddress;
+ for (IndexOfPageDirectoryEntries = 0;
+ IndexOfPageDirectoryEntries < 512;
+ (IndexOfPageDirectoryEntries++,
+ PageDirectoryEntry++,
+ PhysicalAddress2M += SIZE_2MB)) {
+ if ((PhysicalAddress2M < StackBase + StackSize) &&
+ ((PhysicalAddress2M + SIZE_2MB) > StackBase)) {
+ //
+ // Need to split this 2M page that covers stack range.
+ //
+ Split2MPageTo4K (
+ PhysicalAddress2M,
+ (UINT64 *)PageDirectoryEntry,
+ StackBase,
+ StackSize,
+ ActiveAddressEncMask
+ );
+ } else {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | ActiveAddressEncMask;
+ PageDirectoryEntry->Bits.ReadWrite = 1;
+ PageDirectoryEntry->Bits.Present = 1;
+ PageDirectoryEntry->Bits.MustBe1 = 1;
+ }
+ }
+}
+
+
+/**
+ Set or Clear the memory encryption bit
+
+ @param[in] PagetablePoint Page table entry pointer (PTE).
+ @param[in] Mode Set or Clear encryption bit
+
+**/
+STATIC VOID
+SetOrClearSharedBit(
+ IN OUT UINT64* PageTablePointer,
+ IN TDX_PAGETABLE_MODE Mode,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINT64 Length
+ )
+{
+ UINT64 AddressEncMask;
+ UINT64 Status;
+
+ AddressEncMask = GetMemEncryptionAddressMask ();
+
+ //
+ // Set or clear page table entry. Also, set shared bit in physical address, before calling MapGPA
+ //
+ if (Mode == SetSharedBit) {
+ *PageTablePointer |= AddressEncMask;
+ PhysicalAddress |= AddressEncMask;
+ } else {
+ *PageTablePointer &= ~AddressEncMask;
+ PhysicalAddress &= ~AddressEncMask;
+ }
+
+ Status = TdVmCall(TDVMCALL_MAPGPA, PhysicalAddress, Length, 0, 0, NULL);
+
+ //
+ // If changing shared to private, must accept-page again
+ //
+ if (Mode == ClearSharedBit) {
+ TdAcceptPages(PhysicalAddress, Length / EFI_PAGE_SIZE, EFI_PAGE_SIZE);
+ }
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: pte=0x%Lx AddressEncMask=0x%Lx Mode=0x%x MapGPA Status=0x%x\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ *PageTablePointer,
+ AddressEncMask,
+ Mode, Status));
+}
+
+/**
+ Check the WP status in CR0 register. This bit is used to lock or unlock write
+ access to pages marked as read-only.
+
+ @retval TRUE Write protection is enabled.
+ @retval FALSE Write protection is disabled.
+**/
+STATIC
+BOOLEAN
+IsReadOnlyPageWriteProtected (
+ VOID
+ )
+{
+ return ((AsmReadCr0 () & BIT16) != 0);
+}
+
+
+/**
+ Disable Write Protect on pages marked as read-only.
+**/
+STATIC
+VOID
+DisableReadOnlyPageWriteProtect (
+ VOID
+ )
+{
+ AsmWriteCr0 (AsmReadCr0() & ~BIT16);
+}
+
+/**
+ Enable Write Protect on pages marked as read-only.
+**/
+VOID
+EnableReadOnlyPageWriteProtect (
+ VOID
+ )
+{
+ AsmWriteCr0 (AsmReadCr0() | BIT16);
+}
+
+/**
+ This function either sets or clears memory encryption for the memory
+ region specified by PhysicalAddress and Length from the current page table
+ context.
+
+ The function iterates through the PhysicalAddress one page at a time, and set
+ or clears the memory encryption in the page table. If it encounters
+ that a given physical address range is part of large page then it attempts to
+ change the attribute at one go (based on size), otherwise it splits the
+ large pages into smaller (e.g 2M page into 4K pages) and then try to set or
+ clear the encryption bit on the smallest page size.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] PhysicalAddress The physical address that is the start
+ address of a memory region.
+ @param[in] Length The length of memory region
+ @param[in] Mode Set or Clear mode
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute
+ is not supported
+**/
+
+STATIC
+RETURN_STATUS
+EFIAPI
+SetMemorySharedOrPrivate (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN UINTN Length,
+ IN TDX_PAGETABLE_MODE Mode
+ )
+{
+
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageUpperDirectoryPointerEntry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
+ PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
+ PAGE_TABLE_ENTRY *PageDirectory2MEntry;
+ PAGE_TABLE_4K_ENTRY *PageTableEntry;
+ UINT64 PgTableMask;
+ UINT64 AddressEncMask;
+ UINT64 ActiveEncMask;
+ BOOLEAN IsWpEnabled;
+ RETURN_STATUS Status;
+ IA32_CR4 Cr4;
+ BOOLEAN Page5LevelSupport;
+
+ //
+ // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.
+ //
+ PageMapLevel4Entry = NULL;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ Cr3BaseAddress,
+ PhysicalAddress,
+ (UINT64)Length,
+ (Mode == SetSharedBit) ? "Shared" : "Private"
+ ));
+
+ //
+ // Check if we have a valid memory encryption mask
+ //
+ AddressEncMask = GetMemEncryptionAddressMask ();
+
+ PgTableMask = AddressEncMask | EFI_PAGE_MASK;
+
+ if (Length == 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Make sure that the page table is changeable.
+ //
+ IsWpEnabled = IsReadOnlyPageWriteProtected ();
+ if (IsWpEnabled) {
+ DisableReadOnlyPageWriteProtect ();
+ }
+
+ //
+ // If Cr3BaseAddress is not specified then read the current CR3
+ //
+ if (Cr3BaseAddress == 0) {
+ Cr3BaseAddress = AsmReadCr3();
+ }
+ //
+ // CPU will already have LA57 enabled so just check CR4
+ //
+ Cr4.UintN = AsmReadCr4 ();
+
+ Page5LevelSupport = (Cr4.Bits.LA57 ? TRUE : FALSE);
+ //
+ // If 5-level pages, adjust Cr3BaseAddress to point to first 4-level page directory,
+ // we will only have 1
+ //
+ if (Page5LevelSupport) {
+ Cr3BaseAddress = *(UINT64 *)Cr3BaseAddress & ~PgTableMask;
+ }
+
+ Status = EFI_SUCCESS;
+
+ while (Length)
+ {
+ PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask);
+ PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress);
+ if (!PageMapLevel4Entry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PML4 for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+
+ PageDirectory1GEntry = (VOID *)(
+ (PageMapLevel4Entry->Bits.PageTableBaseAddress <<
+ 12) & ~PgTableMask
+ );
+ PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress);
+ if (!PageDirectory1GEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PDPE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+
+ //
+ // If the MustBe1 bit is not 1, it's not actually a 1GB entry
+ //
+ if (PageDirectory1GEntry->Bits.MustBe1) {
+ //
+ // Valid 1GB page
+ // If we have at least 1GB to go, we can just update this entry
+ //
+ if (!(PhysicalAddress & (BIT30 - 1)) && Length >= BIT30) {
+ SetOrClearSharedBit(&PageDirectory1GEntry->Uint64, Mode, PhysicalAddress, BIT30);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: updated 1GB entry for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ PhysicalAddress += BIT30;
+ Length -= BIT30;
+ } else {
+ //
+ // We must split the page
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: splitting 1GB page for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Split1GPageTo2M (
+ (UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress << 30,
+ (UINT64 *)PageDirectory1GEntry,
+ 0,
+ 0
+ );
+ continue;
+ }
+ } else {
+ //
+ // Actually a PDP
+ //
+ PageUpperDirectoryPointerEntry =
+ (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory1GEntry;
+ PageDirectory2MEntry =
+ (VOID *)(
+ (PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress <<
+ 12) & ~PgTableMask
+ );
+ PageDirectory2MEntry += PDE_OFFSET(PhysicalAddress);
+ if (!PageDirectory2MEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PDE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+ //
+ // If the MustBe1 bit is not a 1, it's not a 2MB entry
+ //
+ if (PageDirectory2MEntry->Bits.MustBe1) {
+ //
+ // Valid 2MB page
+ // If we have at least 2MB left to go, we can just update this entry
+ //
+ if (!(PhysicalAddress & (BIT21-1)) && Length >= BIT21) {
+ SetOrClearSharedBit (&PageDirectory2MEntry->Uint64, Mode, PhysicalAddress, BIT21);
+ PhysicalAddress += BIT21;
+ Length -= BIT21;
+ } else {
+ //
+ // We must split up this page into 4K pages
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%a: splitting 2MB page for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+
+ ActiveEncMask = PageDirectory2MEntry->Uint64 & AddressEncMask;
+
+ Split2MPageTo4K (
+ (UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress << 21,
+ (UINT64 *)PageDirectory2MEntry,
+ 0,
+ 0,
+ ActiveEncMask
+ );
+ continue;
+ }
+ } else {
+ PageDirectoryPointerEntry =
+ (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory2MEntry;
+ PageTableEntry =
+ (VOID *)(
+ (PageDirectoryPointerEntry->Bits.PageTableBaseAddress <<
+ 12) & ~PgTableMask
+ );
+ PageTableEntry += PTE_OFFSET(PhysicalAddress);
+ if (!PageTableEntry->Bits.Present) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: bad PTE for Physical=0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ PhysicalAddress
+ ));
+ Status = RETURN_NO_MAPPING;
+ goto Done;
+ }
+ SetOrClearSharedBit (&PageTableEntry->Uint64, Mode, PhysicalAddress, EFI_PAGE_SIZE);
+ PhysicalAddress += EFI_PAGE_SIZE;
+ Length -= EFI_PAGE_SIZE;
+ }
+ }
+ }
+
+ //
+ // Protect the page table by marking the memory used for page table to be
+ // read-only.
+ //
+ if (IsWpEnabled) {
+ EnablePageTableProtection ((UINTN)PageMapLevel4Entry, TRUE);
+ }
+
+ //
+ // Flush TLB
+ //
+ CpuFlushTlb();
+
+Done:
+ //
+ // Restore page table write protection, if any.
+ //
+ if (IsWpEnabled) {
+ EnableReadOnlyPageWriteProtect ();
+ }
+
+ return Status;
+}
+
+/**
+ This function clears memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+
+ @retval RETURN_SUCCESS The attributes were cleared for the
+ memory region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Clearing the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptTdxSetPageSharedBit (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages
+ )
+{
+ return SetMemorySharedOrPrivate (
+ Cr3BaseAddress,
+ BaseAddress,
+ EFI_PAGES_TO_SIZE (NumPages),
+ SetSharedBit
+ );
+}
+
+/**
+ This function sets memory encryption bit for the memory region specified by
+ BaseAddress and NumPages from the current page table context.
+
+ @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use
+ current CR3)
+ @param[in] BaseAddress The physical address that is the start
+ address of a memory region.
+ @param[in] NumPages The number of pages from start memory
+ region.
+
+ @retval RETURN_SUCCESS The attributes were set for the memory
+ region.
+ @retval RETURN_INVALID_PARAMETER Number of pages is zero.
+ @retval RETURN_UNSUPPORTED Setting the memory encryption attribute
+ is not supported
+**/
+RETURN_STATUS
+EFIAPI
+MemEncryptTdxClearPageSharedBit (
+ IN PHYSICAL_ADDRESS Cr3BaseAddress,
+ IN PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN NumPages
+ )
+{
+ return SetMemorySharedOrPrivate (
+ Cr3BaseAddress,
+ BaseAddress,
+ EFI_PAGES_TO_SIZE (NumPages),
+ ClearSharedBit
+ );
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h b/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h
new file mode 100644
index 000000000000..c23472f71909
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h
@@ -0,0 +1,181 @@
+/** @file
+
+ Virtual Memory Management Services to set or clear the memory encryption bit
+
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
+
+**/
+
+#ifndef __VIRTUAL_MEMORY__
+#define __VIRTUAL_MEMORY__
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Uefi.h>
+
+#define SYS_CODE64_SEL 0x38
+
+#pragma pack(1)
+
+//
+// Page-Map Level-4 Offset (PML4) and
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
+//
+
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1 = Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Reserved:1; // Reserved
+ UINT64 MustBeZero:2; // Must Be Zero
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PageTableBaseAddress:40; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // No Execute bit
+ } Bits;
+ UINT64 Uint64;
+} PAGE_MAP_AND_DIRECTORY_POINTER;
+
+//
+// Page Table Entry 4KB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1 = Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by
+ // processor on access to page
+ UINT64 PAT:1; //
+ UINT64 Global:1; // 0 = Not global page, 1 = global page
+ // TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PageTableBaseAddress:40; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code,
+ // 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_4K_ENTRY;
+
+//
+// Page Table Entry 2MB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1=Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by
+ // processor on access to page
+ UINT64 MustBe1:1; // Must be 1
+ UINT64 Global:1; // 0 = Not global page, 1 = global page
+ // TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PAT:1; //
+ UINT64 MustBeZero:8; // Must be zero;
+ UINT64 PageTableBaseAddress:31; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code,
+ // 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_ENTRY;
+
+//
+// Page Table Entry 1GB
+//
+typedef union {
+ struct {
+ UINT64 Present:1; // 0 = Not present in memory,
+ // 1 = Present in memory
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough:1; // 0 = Write-Back caching,
+ // 1 = Write-Through caching
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed:1; // 0 = Not accessed,
+ // 1 = Accessed (set by CPU)
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by
+ // processor on access to page
+ UINT64 MustBe1:1; // Must be 1
+ UINT64 Global:1; // 0 = Not global page, 1 = global page
+ // TLB not cleared on CR3 write
+ UINT64 Available:3; // Available for use by system software
+ UINT64 PAT:1; //
+ UINT64 MustBeZero:17; // Must be zero;
+ UINT64 PageTableBaseAddress:22; // Page Table Base Address
+ UINT64 AvabilableHigh:11; // Available for use by system software
+ UINT64 Nx:1; // 0 = Execute Code,
+ // 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_1G_ENTRY;
+
+#pragma pack()
+
+#define IA32_PG_P BIT0
+#define IA32_PG_RW BIT1
+#define IA32_PG_PS BIT7
+
+#define PAGING_PAE_INDEX_MASK 0x1FF
+
+#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
+#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
+#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
+
+#define PAGING_L1_ADDRESS_SHIFT 12
+#define PAGING_L2_ADDRESS_SHIFT 21
+#define PAGING_L3_ADDRESS_SHIFT 30
+#define PAGING_L4_ADDRESS_SHIFT 39
+
+#define PAGING_PML4E_NUMBER 4
+
+#define PAGETABLE_ENTRY_MASK ((1UL << 9) - 1)
+#define PML4_OFFSET(x) ( (x >> 39) & PAGETABLE_ENTRY_MASK)
+#define PDP_OFFSET(x) ( (x >> 30) & PAGETABLE_ENTRY_MASK)
+#define PDE_OFFSET(x) ( (x >> 21) & PAGETABLE_ENTRY_MASK)
+#define PTE_OFFSET(x) ( (x >> 12) & PAGETABLE_ENTRY_MASK)
+#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
+
+#define PAGE_TABLE_POOL_ALIGNMENT BASE_2MB
+#define PAGE_TABLE_POOL_UNIT_SIZE SIZE_2MB
+#define PAGE_TABLE_POOL_UNIT_PAGES \
+ EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE)
+#define PAGE_TABLE_POOL_ALIGN_MASK \
+ (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1))
+
+typedef struct {
+ VOID *NextPool;
+ UINTN Offset;
+ UINTN FreePages;
+} PAGE_TABLE_POOL;
+
+#endif
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index ed7f2ff42465..80225433397a 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -34,6 +34,10 @@
# Virtualization (SEV) guests.
MemEncryptSevLib|Include/Library/MemEncryptSevLib.h
+ ## @libraryclass Declares helper functions for TDX guests.
+ #
+ MemEncryptTdxLib|Include/Library/MemEncryptTdxLib.h
+
## @libraryclass Save and restore variables using a file
#
NvVarsFileLib|Include/Library/NvVarsFileLib.h
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 056055c8d799..a3b4c230d689 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -176,6 +176,7 @@
VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf
LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf
MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
+ MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf
!if $(SMM_REQUIRE) == FALSE
LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf
!endif
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 27887da8f2a2..575f05c9e7aa 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -180,6 +180,7 @@
VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf
LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf
MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
+ MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf
!if $(SMM_REQUIRE) == FALSE
LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf
!endif
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 27/29] OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library
2021-11-01 13:16 ` [PATCH V3 27/29] OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library Min Xu
@ 2021-11-03 7:10 ` Gerd Hoffmann
2021-12-08 8:37 ` [edk2-devel] " Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 7:10 UTC (permalink / raw)
To: Min Xu
Cc: devel, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
On Mon, Nov 01, 2021 at 09:16:16PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> Add Intel Tdx helper library. The library provides the routines to:
> - set or clear Shared bit for a given memory region.
> - query whether TDX is enabled.
Hmm, patch 22 adds functions to set the shared bit too.
Looks like duplicate functionality on a first glance.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 27/29] OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library
2021-11-03 7:10 ` Gerd Hoffmann
@ 2021-12-08 8:37 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-12-08 8:37 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com
Cc: Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Erdem Aktas,
James Bottomley, Yao, Jiewen, Tom Lendacky
Hi,
> >
> > Add Intel Tdx helper library. The library provides the routines to:
> > - set or clear Shared bit for a given memory region.
> > - query whether TDX is enabled.
>
> Hmm, patch 22 adds functions to set the shared bit too.
> Looks like duplicate functionality on a first glance.
>
Ah yes, patch 22 (setting the shared bit) is duplicated with BaseMemEncryptTdxLib. And setting shared bit for Mmio space can be moved to TdxDxe driver (It is more reasonable). Then MdeModulePkg/Core/DxeIplPeim can be un-touched. To be honest I am a little nervous to touch the core code.
Thanks much for the reminder.
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 28/29] OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (26 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 27/29] OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 7:12 ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX Min Xu
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In the previous QemuFwCfgDxe only SEV is supported. This commit
introduce TDX support in QemuFwCfgDxe.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c | 9 +++++----
OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf | 1 +
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
index 0182c9235cac..7a60b3e82863 100644
--- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
@@ -19,6 +19,7 @@
#include <Library/DebugLib.h>
#include <Library/QemuFwCfgLib.h>
#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemEncryptTdxLib.h>
#include <Library/MemEncryptSevLib.h>
#include "QemuFwCfgLibInternal.h"
@@ -85,7 +86,7 @@ QemuFwCfgInitialize (
DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
}
- if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {
+ if (mQemuFwCfgDmaSupported && (MemEncryptSevIsEnabled () || (MemEncryptTdxIsEnabled ()))) {
EFI_STATUS Status;
//
@@ -96,7 +97,7 @@ QemuFwCfgInitialize (
(VOID **)&mIoMmuProtocol);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
- "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n",
+ "QemuFwCfgDma %a:%a Failed to locate IOMMU protocol.\n",
gEfiCallerBaseName, __FUNCTION__));
ASSERT (FALSE);
CpuDeadLoop ();
@@ -371,10 +372,10 @@ InternalQemuFwCfgDmaBytes (
DataBuffer = Buffer;
//
- // When SEV is enabled, map Buffer to DMA address before issuing the DMA
+ // When SEV or TDX is enabled, map Buffer to DMA address before issuing the DMA
// request
//
- if (MemEncryptSevIsEnabled ()) {
+ if (MemEncryptSevIsEnabled() || MemEncryptTdxIsEnabled ()) {
VOID *AccessBuffer;
EFI_PHYSICAL_ADDRESS DataBufferAddress;
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf
index 48899ff1236a..ce3eaa5ed8b4 100644
--- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf
@@ -43,6 +43,7 @@
IoLib
MemoryAllocationLib
MemEncryptSevLib
+ MemEncryptTdxLib
[Protocols]
gEdkiiIoMmuProtocolGuid ## SOMETIMES_CONSUMES
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 28/29] OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe
2021-11-01 13:16 ` [PATCH V3 28/29] OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe Min Xu
@ 2021-11-03 7:12 ` Gerd Hoffmann
2021-12-13 2:06 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 7:12 UTC (permalink / raw)
To: Min Xu
Cc: devel, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
On Mon, Nov 01, 2021 at 09:16:17PM +0800, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> In the previous QemuFwCfgDxe only SEV is supported. This commit
> introduce TDX support in QemuFwCfgDxe.
>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Cc: Erdem Aktas <erdemaktas@google.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
> ---
> OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c | 9 +++++----
> OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf | 1 +
> 2 files changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
> index 0182c9235cac..7a60b3e82863 100644
> --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
> +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
> @@ -19,6 +19,7 @@
> #include <Library/DebugLib.h>
> #include <Library/QemuFwCfgLib.h>
> #include <Library/UefiBootServicesTableLib.h>
> +#include <Library/MemEncryptTdxLib.h>
> #include <Library/MemEncryptSevLib.h>
>
> #include "QemuFwCfgLibInternal.h"
> @@ -85,7 +86,7 @@ QemuFwCfgInitialize (
> DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
> }
>
> - if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {
> + if (mQemuFwCfgDmaSupported && (MemEncryptSevIsEnabled () || (MemEncryptTdxIsEnabled ()))) {
> EFI_STATUS Status;
Should be possible to just check the ConfidentialComputing PCD here.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [PATCH V3 28/29] OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe
2021-11-03 7:12 ` Gerd Hoffmann
@ 2021-12-13 2:06 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-12-13 2:06 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Aktas, Erdem, James Bottomley, Yao, Jiewen,
Tom Lendacky
Hi,
> > diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
> > b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
> > index 0182c9235cac..7a60b3e82863 100644
> > --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
> > +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
> > @@ -19,6 +19,7 @@
> > #include <Library/DebugLib.h>
> > #include <Library/QemuFwCfgLib.h>
> > #include <Library/UefiBootServicesTableLib.h>
> > +#include <Library/MemEncryptTdxLib.h>
> > #include <Library/MemEncryptSevLib.h>
> >
> > #include "QemuFwCfgLibInternal.h"
> > @@ -85,7 +86,7 @@ QemuFwCfgInitialize (
> > DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
> > }
> >
> > - if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {
> > + if (mQemuFwCfgDmaSupported && (MemEncryptSevIsEnabled () ||
> > + (MemEncryptTdxIsEnabled ()))) {
> > EFI_STATUS Status;
>
> Should be possible to just check the ConfidentialComputing PCD here.
>
MemEncryptTdxIsEnabled() is checking the ConfidentialComputing PCD.
MemEncryptSevIsEnabled () has 3 implementations in SEC/PEI/DXE. In SEC/PEI phase the ConfidentialComputing PCD has not been ready and it just checks the Msr.Bits.SevBit.
Another consideration is that as the first step we make the least change so that it will not break the existing feature. After that we revisit here and refine the code if possible.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
` (27 preceding siblings ...)
2021-11-01 13:16 ` [PATCH V3 28/29] OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe Min Xu
@ 2021-11-01 13:16 ` Min Xu
2021-11-03 7:17 ` Gerd Hoffmann
28 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-11-01 13:16 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky, Gerd Hoffmann
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
The IOMMU protocol driver provides capabilities to set a DMA access
attribute and methods to allocate, free, map and unmap the DMA memory
for the PCI Bus devices.
The current IoMmuDxe driver supports DMA operations inside SEV guest.
To support DMA operation in TDX guest, mIoMmuType is added to determine
if it is Legac guest, SEV guest or TDX guest.
Due to security reasons all DMA operations inside the SEV/TDX guest must
be performed on shared pages. The IOMMU protocol driver for the SEV/TDX
guest uses a bounce buffer to map guest DMA buffer to shared pages in
order to provide the support for DMA operations inside SEV/TDX guest.
The call of SEV or TDX specific function to set/clear EncMask/SharedBit
is determined by mIoMmuType.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/IoMmuDxe/AmdSevIoMmu.c | 104 +++++++++++++++++++++------------
OvmfPkg/IoMmuDxe/AmdSevIoMmu.h | 6 +-
OvmfPkg/IoMmuDxe/IoMmuDxe.c | 6 +-
OvmfPkg/IoMmuDxe/IoMmuDxe.inf | 5 ++
OvmfPkg/OvmfPkgX64.dsc | 2 +
5 files changed, 81 insertions(+), 42 deletions(-)
diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
index b30628078f73..f6ccb4b2c675 100644
--- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
+++ b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
@@ -1,9 +1,9 @@
/** @file
The protocol provides support to allocate, free, map and umap a DMA buffer
- for bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations
- must be performed on unencrypted buffer hence we use a bounce buffer to map
- the guest buffer into an unencrypted DMA buffer.
+ for bus master (e.g PciHostBridge). When SEV or TDX is enabled, the DMA
+ operations must be performed on unencrypted buffer hence we use a bounce
+ buffer to map the guest buffer into an unencrypted DMA buffer.
Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
@@ -12,6 +12,8 @@
**/
+#include <Library/PcdLib.h>
+#include <ConfidentialComputingGuestAttr.h>
#include "AmdSevIoMmu.h"
#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O')
@@ -74,7 +76,7 @@ typedef struct {
/**
Provides the controller-specific addresses required to access system memory
- from a DMA bus master. On SEV guest, the DMA operations must be performed on
+ from a DMA bus master. On SEV/TDX guest, the DMA operations must be performed on
shared buffer hence we allocate a bounce buffer to map the HostAddress to a
DeviceAddress. The Encryption attribute is removed from the DeviceAddress
buffer.
@@ -246,14 +248,29 @@ IoMmuMap (
goto FreeMapInfo;
}
- //
- // Clear the memory encryption mask on the plaintext buffer.
- //
- Status = MemEncryptSevClearPageEncMask (
- 0,
- MapInfo->PlainTextAddress,
- MapInfo->NumberOfPages
- );
+ if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ //
+ // Clear the memory encryption mask on the plaintext buffer.
+ //
+ Status = MemEncryptSevClearPageEncMask (
+ 0,
+ MapInfo->PlainTextAddress,
+ MapInfo->NumberOfPages
+ );
+ } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ //
+ // Set the memory shared bit.
+ //
+ Status = MemEncryptTdxSetPageSharedBit (
+ 0,
+ MapInfo->PlainTextAddress,
+ MapInfo->NumberOfPages
+ );
+
+ } else {
+ ASSERT (FALSE);
+ }
+
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
CpuDeadLoop ();
@@ -353,7 +370,7 @@ IoMmuUnmapWorker (
}
MapInfo = (MAP_INFO *)Mapping;
-
+ Status = EFI_SUCCESS;
//
// set CommonBufferHeader to suppress incorrect compiler/analyzer warnings
//
@@ -399,15 +416,30 @@ IoMmuUnmapWorker (
break;
}
- //
- // Restore the memory encryption mask on the area we used to hold the
- // plaintext.
- //
- Status = MemEncryptSevSetPageEncMask (
- 0,
- MapInfo->PlainTextAddress,
- MapInfo->NumberOfPages
- );
+ if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ //
+ // Restore the memory encryption mask on the area we used to hold the
+ // plaintext.
+ //
+ Status = MemEncryptSevSetPageEncMask (
+ 0,
+ MapInfo->PlainTextAddress,
+ MapInfo->NumberOfPages
+ );
+ } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
+ //
+ // Restore the memory shared bit mask on the area we used to hold the
+ // plaintext.
+ //
+ Status = MemEncryptTdxClearPageSharedBit (
+ 0,
+ MapInfo->PlainTextAddress,
+ MapInfo->NumberOfPages
+ );
+ } else {
+ ASSERT (FALSE);
+ }
+
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
CpuDeadLoop ();
@@ -731,7 +763,7 @@ IoMmuSetAttribute (
return EFI_UNSUPPORTED;
}
-EDKII_IOMMU_PROTOCOL mAmdSev = {
+EDKII_IOMMU_PROTOCOL mIoMmu = {
EDKII_IOMMU_PROTOCOL_REVISION,
IoMmuSetAttribute,
IoMmuMap,
@@ -763,7 +795,7 @@ EDKII_IOMMU_PROTOCOL mAmdSev = {
STATIC
VOID
EFIAPI
-AmdSevExitBoot (
+IoMmuExitBoot (
IN EFI_EVENT Event,
IN VOID *EventToSignal
)
@@ -771,11 +803,11 @@ AmdSevExitBoot (
//
// (1) The NotifyFunctions of all the events in
// EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before
- // AmdSevExitBoot() is entered.
+ // IoMmuExitBoot() is entered.
//
- // (2) AmdSevExitBoot() is executing minimally at TPL_CALLBACK.
+ // (2) IoMmuExitBoot() is executing minimally at TPL_CALLBACK.
//
- // (3) AmdSevExitBoot() has been queued in unspecified order relative to the
+ // (3) IoMmuExitBoot() has been queued in unspecified order relative to the
// NotifyFunctions of all the other events in
// EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as
// Event's.
@@ -783,13 +815,13 @@ AmdSevExitBoot (
// Consequences:
//
// - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions
- // queued at TPL_CALLBACK may be invoked after AmdSevExitBoot() returns.
+ // queued at TPL_CALLBACK may be invoked after IoMmuExitBoot() returns.
//
// - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions
- // queued at TPL_NOTIFY may be invoked after AmdSevExitBoot() returns; plus
+ // queued at TPL_NOTIFY may be invoked after IoMmuExitBoot() returns; plus
// *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly
// after all NotifyFunctions queued at TPL_NOTIFY, including
- // AmdSevExitBoot(), have been invoked.
+ // IoMmuExitBoot(), have been invoked.
//
// - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we
// queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*
@@ -815,7 +847,7 @@ AmdSevExitBoot (
STATIC
VOID
EFIAPI
-AmdSevUnmapAllMappings (
+IoMmuUnmapAllMappings (
IN EFI_EVENT Event,
IN VOID *Context
)
@@ -834,7 +866,7 @@ AmdSevUnmapAllMappings (
NextNode = GetNextNode (&mMapInfos, Node);
MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);
IoMmuUnmapWorker (
- &mAmdSev, // This
+ &mIoMmu, // This
MapInfo, // Mapping
TRUE // MemoryMapLocked
);
@@ -847,7 +879,7 @@ AmdSevUnmapAllMappings (
**/
EFI_STATUS
EFIAPI
-AmdSevInstallIoMmuProtocol (
+InstallIoMmuProtocol (
VOID
)
{
@@ -863,7 +895,7 @@ AmdSevInstallIoMmuProtocol (
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL, // Type
TPL_CALLBACK, // NotifyTpl
- AmdSevUnmapAllMappings, // NotifyFunction
+ IoMmuUnmapAllMappings, // NotifyFunction
NULL, // NotifyContext
&UnmapAllMappingsEvent // Event
);
@@ -878,7 +910,7 @@ AmdSevInstallIoMmuProtocol (
Status = gBS->CreateEvent (
EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
TPL_CALLBACK, // NotifyTpl
- AmdSevExitBoot, // NotifyFunction
+ IoMmuExitBoot, // NotifyFunction
UnmapAllMappingsEvent, // NotifyContext
&ExitBootEvent // Event
);
@@ -889,7 +921,7 @@ AmdSevInstallIoMmuProtocol (
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
- &gEdkiiIoMmuProtocolGuid, &mAmdSev,
+ &gEdkiiIoMmuProtocolGuid, &mIoMmu,
NULL
);
if (EFI_ERROR (Status)) {
diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h
index 8244f28b57fd..8fdfa9968593 100644
--- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h
+++ b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h
@@ -21,17 +21,17 @@
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemEncryptSevLib.h>
+#include <Library/MemEncryptTdxLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
/**
- Install IOMMU protocol to provide the DMA support for PciHostBridge and
- MemEncryptSevLib.
+ Install IOMMU protocol to provide the DMA support for PciHostBridge.
**/
EFI_STATUS
EFIAPI
-AmdSevInstallIoMmuProtocol (
+InstallIoMmuProtocol (
VOID
);
diff --git a/OvmfPkg/IoMmuDxe/IoMmuDxe.c b/OvmfPkg/IoMmuDxe/IoMmuDxe.c
index 13df8ba874c5..89ebe254601d 100644
--- a/OvmfPkg/IoMmuDxe/IoMmuDxe.c
+++ b/OvmfPkg/IoMmuDxe/IoMmuDxe.c
@@ -22,11 +22,11 @@ IoMmuDxeEntryPoint (
EFI_HANDLE Handle;
//
- // When SEV is enabled, install IoMmu protocol otherwise install the
+ // When SEV or TDX is enabled, install IoMmu protocol otherwise install the
// placeholder protocol so that other dependent module can run.
//
- if (MemEncryptSevIsEnabled ()) {
- Status = AmdSevInstallIoMmuProtocol ();
+ if (MemEncryptSevIsEnabled () || MemEncryptTdxIsEnabled ()) {
+ Status = InstallIoMmuProtocol ();
} else {
Handle = NULL;
diff --git a/OvmfPkg/IoMmuDxe/IoMmuDxe.inf b/OvmfPkg/IoMmuDxe/IoMmuDxe.inf
index 2ebd74e5558c..e10be1dcff49 100644
--- a/OvmfPkg/IoMmuDxe/IoMmuDxe.inf
+++ b/OvmfPkg/IoMmuDxe/IoMmuDxe.inf
@@ -26,16 +26,21 @@
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
OvmfPkg/OvmfPkg.dec
+# UefiCpuPkg/UefiCpuPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
MemEncryptSevLib
+ MemEncryptTdxLib
MemoryAllocationLib
UefiBootServicesTableLib
UefiDriverEntryPoint
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr
+
[Protocols]
gEdkiiIoMmuProtocolGuid ## SOMETIME_PRODUCES
gIoMmuAbsentProtocolGuid ## SOMETIME_PRODUCES
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index c8ab0dc7060e..11e3f00e8c14 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -180,6 +180,8 @@
VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf
LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf
MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
+ MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf
+
!if $(SMM_REQUIRE) == FALSE
LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf
!endif
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 107+ messages in thread
* Re: [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX
2021-11-01 13:16 ` [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX Min Xu
@ 2021-11-03 7:17 ` Gerd Hoffmann
2021-12-13 2:39 ` [edk2-devel] " Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-11-03 7:17 UTC (permalink / raw)
To: Min Xu
Cc: devel, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
Hi,
> + if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
> + //
> + // Clear the memory encryption mask on the plaintext buffer.
> + //
> + Status = MemEncryptSevClearPageEncMask (
> + 0,
> + MapInfo->PlainTextAddress,
> + MapInfo->NumberOfPages
> + );
> + } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
> + //
> + // Set the memory shared bit.
> + //
> + Status = MemEncryptTdxSetPageSharedBit (
> + 0,
> + MapInfo->PlainTextAddress,
> + MapInfo->NumberOfPages
> + );
Again, this looks very simliar and like a great opportunity to share
code.
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX
2021-11-03 7:17 ` Gerd Hoffmann
@ 2021-12-13 2:39 ` Min Xu
2021-12-13 6:42 ` Gerd Hoffmann
0 siblings, 1 reply; 107+ messages in thread
From: Min Xu @ 2021-12-13 2:39 UTC (permalink / raw)
To: devel@edk2.groups.io, kraxel@redhat.com
Cc: Ard Biesheuvel, Justen, Jordan L, Brijesh Singh, Aktas, Erdem,
James Bottomley, Yao, Jiewen, Tom Lendacky
Hi
>
> > + if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
> > + //
> > + // Clear the memory encryption mask on the plaintext buffer.
> > + //
> > + Status = MemEncryptSevClearPageEncMask (
> > + 0,
> > + MapInfo->PlainTextAddress,
> > + MapInfo->NumberOfPages
> > + );
> > + } else if (CC_GUEST_IS_TDX (PcdGet64
> (PcdConfidentialComputingGuestAttr))) {
> > + //
> > + // Set the memory shared bit.
> > + //
> > + Status = MemEncryptTdxSetPageSharedBit (
> > + 0,
> > + MapInfo->PlainTextAddress,
> > + MapInfo->NumberOfPages
> > + );
>
> Again, this looks very simliar and like a great opportunity to share code.
>
MemEncryptSevClearPageEncMask () is implemented in MemEncryptSevLib.
MemEncryptTdxSetPageSharedBit () is implemented in MemEncryptTdxlib.
Yes, we have considered to merge these 2 EncryptLib into one lib (for example: MemoryEncryptCcLib). But after investigation and some PoC, we find it will make the code complicated and hard to maintain. (many if-else checking in the code)
1. From the naming perspective (in SEV/TDX documentation), SEV's bit is Enc bit, but TDX's bit is shared bit.
2. In SEV's SetMemoryEncDec () it handles differently for the different version of SEV (for example, Sev-Snp). I am not sure if there will be more specific process will be added in the future.
3. In TDX's SetMemorySharedOrPrivate, currently it is simple and clean. But there maybe some new features added in the future.
I am thinking if it is a better choice that every vendor takes their responsibility to maintain their own lib/code?
In the current EDK2 CI there is no test case for SEV or TDX, I am a little nervous if some changes will impact the existing feature.
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX
2021-12-13 2:39 ` [edk2-devel] " Min Xu
@ 2021-12-13 6:42 ` Gerd Hoffmann
2021-12-13 7:33 ` Min Xu
0 siblings, 1 reply; 107+ messages in thread
From: Gerd Hoffmann @ 2021-12-13 6:42 UTC (permalink / raw)
To: Xu, Min M
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Aktas, Erdem, James Bottomley, Yao, Jiewen,
Tom Lendacky
On Mon, Dec 13, 2021 at 02:39:53AM +0000, Xu, Min M wrote:
> Hi
> >
> > > + if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
> > > + //
> > > + // Clear the memory encryption mask on the plaintext buffer.
> > > + //
> > > + Status = MemEncryptSevClearPageEncMask (
> > > + 0,
> > > + MapInfo->PlainTextAddress,
> > > + MapInfo->NumberOfPages
> > > + );
> > > + } else if (CC_GUEST_IS_TDX (PcdGet64
> > (PcdConfidentialComputingGuestAttr))) {
> > > + //
> > > + // Set the memory shared bit.
> > > + //
> > > + Status = MemEncryptTdxSetPageSharedBit (
> > > + 0,
> > > + MapInfo->PlainTextAddress,
> > > + MapInfo->NumberOfPages
> > > + );
> >
> > Again, this looks very simliar and like a great opportunity to share code.
> >
> MemEncryptSevClearPageEncMask () is implemented in MemEncryptSevLib.
> MemEncryptTdxSetPageSharedBit () is implemented in MemEncryptTdxlib.
>
> Yes, we have considered to merge these 2 EncryptLib into one lib (for
> example: MemoryEncryptCcLib). But after investigation and some PoC, we
> find it will make the code complicated and hard to maintain. (many
> if-else checking in the code)
> 1. From the naming perspective (in SEV/TDX documentation), SEV's bit is Enc bit, but TDX's bit is shared bit.
> 2. In SEV's SetMemoryEncDec () it handles differently for the different version of SEV (for example, Sev-Snp). I am not sure if there will be more specific process will be added in the future.
> 3. In TDX's SetMemorySharedOrPrivate, currently it is simple and clean. But there maybe some new features added in the future.
> I am thinking if it is a better choice that every vendor takes their responsibility to maintain their own lib/code?
Well, I still think there is opportunity to share code, specifically the
page table handling. Have a generic page table walker which is able to
set and clear bits for a given memory range. Then the sev/tdx specific
code can just call that instead of both having their own, duplicated
page table walking logic.
Maybe the page table walking should even be a MdeModulePkg Library, i.e.
move the code for page table walking (and huge page splitting) in
MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c to a library so it can
be reused elsewhere without duplicating the code,
take care,
Gerd
^ permalink raw reply [flat|nested] 107+ messages in thread
* Re: [edk2-devel] [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX
2021-12-13 6:42 ` Gerd Hoffmann
@ 2021-12-13 7:33 ` Min Xu
0 siblings, 0 replies; 107+ messages in thread
From: Min Xu @ 2021-12-13 7:33 UTC (permalink / raw)
To: kraxel@redhat.com
Cc: devel@edk2.groups.io, Ard Biesheuvel, Justen, Jordan L,
Brijesh Singh, Aktas, Erdem, James Bottomley, Yao, Jiewen,
Tom Lendacky
Hi,
> > > > + if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr)))
> {
> > > > + //
> > > > + // Clear the memory encryption mask on the plaintext buffer.
> > > > + //
> > > > + Status = MemEncryptSevClearPageEncMask (
> > > > + 0,
> > > > + MapInfo->PlainTextAddress,
> > > > + MapInfo->NumberOfPages
> > > > + );
> > > > + } else if (CC_GUEST_IS_TDX (PcdGet64
> > > (PcdConfidentialComputingGuestAttr))) {
> > > > + //
> > > > + // Set the memory shared bit.
> > > > + //
> > > > + Status = MemEncryptTdxSetPageSharedBit (
> > > > + 0,
> > > > + MapInfo->PlainTextAddress,
> > > > + MapInfo->NumberOfPages
> > > > + );
> > >
> > > Again, this looks very simliar and like a great opportunity to share code.
> > >
> > MemEncryptSevClearPageEncMask () is implemented in MemEncryptSevLib.
> > MemEncryptTdxSetPageSharedBit () is implemented in MemEncryptTdxlib.
> >
> > Yes, we have considered to merge these 2 EncryptLib into one lib (for
> > example: MemoryEncryptCcLib). But after investigation and some PoC, we
> > find it will make the code complicated and hard to maintain. (many
> > if-else checking in the code)
>
> > 1. From the naming perspective (in SEV/TDX documentation), SEV's bit is Enc
> bit, but TDX's bit is shared bit.
> > 2. In SEV's SetMemoryEncDec () it handles differently for the different version
> of SEV (for example, Sev-Snp). I am not sure if there will be more specific
> process will be added in the future.
> > 3. In TDX's SetMemorySharedOrPrivate, currently it is simple and clean. But
> there maybe some new features added in the future.
>
> > I am thinking if it is a better choice that every vendor takes their responsibility
> to maintain their own lib/code?
>
> Well, I still think there is opportunity to share code, specifically the page table
> handling. Have a generic page table walker which is able to set and clear bits for
> a given memory range. Then the sev/tdx specific code can just call that instead
> of both having their own, duplicated page table walking logic.
>
> Maybe the page table walking should even be a MdeModulePkg Library, i.e.
> move the code for page table walking (and huge page splitting) in
> MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c to a library so it can be
> reused elsewhere without duplicating the code,
Thanks for the suggestion. I will carefully think about it and figure out if it is feasible. (A Poc as the first step )
Thanks
Min
^ permalink raw reply [flat|nested] 107+ messages in thread