From 9ef8bc043510f256c3ad3d970508bc49699f97e3 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Wed, 13 Jan 2021 09:54:53 +0100 Subject: [PATCH 01/54] ArmVirtPkg: disable list length checks in NOOPT and DEBUG builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In NOOPT and DEBUG builds, if "PcdMaximumLinkedListLength" is nonzero, then several LIST_ENTRY *node* APIs in BaseLib compare the *full* list length against the PCD. This turns the time complexity of node-level APIs from constant to linear, and that of full-list manipulations from linear to quadratic. (See some example OVMF numbers in the previous patch.) Checking list lengths against an arbitrary maximum -- default value, and current ArmVirtPkg setting: 1,000,000 -- seems useless even in NOOPT and DEBUG builds, while the cost is significant; so set the PCD to 0. Cc: Ard Biesheuvel Cc: Julien Grall Cc: Leif Lindholm Cc: Philippe Mathieu-Daudé Cc: Sami Mujawar Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3152 Signed-off-by: Laszlo Ersek Acked-by: Ard Biesheuvel Message-Id: <20210113085453.10168-11-lersek@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- ArmVirtPkg/ArmVirt.dsc.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc index 9ec92930472d..d9abadbe708c 100644 --- a/ArmVirtPkg/ArmVirt.dsc.inc +++ b/ArmVirtPkg/ArmVirt.dsc.inc @@ -290,7 +290,7 @@ [PcdsFixedAtBuild.common] gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength|1000000 gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|1000000 - gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength|1000000 + gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength|0 gEfiMdePkgTokenSpaceGuid.PcdSpinLockTimeout|10000000 gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|320 From 3e58db8be40ea86164830c3b14b6a49d54d859e5 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 08:17:34 +0800 Subject: [PATCH 02/54] OvmfPkg: Update OvmfPkgX64 image layout to add Mailbox for TDX guest The Td guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest BSP wake up guest AP 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. TDX only supports on X64 architecture, so OvmfPkgX64.fdf is updated to reserve a 4K-aligned, 4K-size memory block. --- OvmfPkg/OvmfPkg.dec | 4 ++++ OvmfPkg/OvmfPkgX64.dsc | 6 ++++++ OvmfPkg/OvmfPkgX64.fdf | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 54804962ec02..1716c1a84f51 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -313,6 +313,10 @@ gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42 gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43 + ## PCD used by TDX + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase|0|UINT32|0x46 + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize|0|UINT32|0x47 + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|0x0|UINT32|0x55 [PcdsDynamic, PcdsDynamicEx] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10 diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 1376158e37e3..dd0b7631dfff 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -36,6 +36,9 @@ DEFINE TPM_CONFIG_ENABLE = FALSE # + # TDX flags + # + DEFINE TDX_EMULATION_ENABLE = FALSE # Network definition # DEFINE NETWORK_TLS_ENABLE = FALSE @@ -533,6 +536,9 @@ # // significantly impact boot performance # DEBUG_ERROR 0x80000000 // Error gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000004F +!if $(TDX_EMULATION_ENABLE) == TRUE + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|1 +!endif !if $(SOURCE_DEBUG_ENABLE) == TRUE gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x17 diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index d519f8532822..3117d0b24704 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -88,6 +88,18 @@ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevE 0x00C000|0x001000 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize +0x00D000|0x001000 +gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase|gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize +DATA = { + 0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 +} 0x010000|0x010000 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize From 07d5465a23461856c7005fbf814891b457da9caa Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 08:21:35 +0800 Subject: [PATCH 03/54] MdePkg: Implement library support to probe Td or Non-Td guest Some of the EDK2 driver/lib need to detect the running guest is of Td or Non-Td. For example, the IO operation is different in Td or Non-Td guest. TdxProbeLib is such library to probe the Td or Non-Td guest. A NULL instance of TdxProbeLib is implemented in MdePkg and it always returns FALSE when ProbeTdGuest() is called. It is expected that other packages (like OvmfPkg) will create a version of the library to fully support the ProbeTdGuest(). --- MdePkg/Include/Library/TdxProbeLib.h | 25 +++++++++++++++++ MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c | 25 +++++++++++++++++ .../Library/TdxProbeLib/TdxProbeLibNull.inf | 27 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 MdePkg/Include/Library/TdxProbeLib.h create mode 100644 MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c create mode 100644 MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf diff --git a/MdePkg/Include/Library/TdxProbeLib.h b/MdePkg/Include/Library/TdxProbeLib.h new file mode 100644 index 000000000000..177c684f5622 --- /dev/null +++ b/MdePkg/Include/Library/TdxProbeLib.h @@ -0,0 +1,25 @@ +/** @file + TdxProbeLib definitions + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _TDX_PROBE_LIB_H_ +#define _TDX_PROBE_LIB_H_ + +#include + +/** + Check whether it is TD guest or Non-TD guest + + @return TRUE TD guest + @return FALSE Non-TD guest +**/ +BOOLEAN +EFIAPI +ProbeTdGuest ( + VOID); + +#endif diff --git a/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c new file mode 100644 index 000000000000..631f51ebf79b --- /dev/null +++ b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c @@ -0,0 +1,25 @@ +/** @file + Null instance of TdxProbeLib. + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include +#include + +/** + Probe whether it is TD guest or Non-TD guest. + + @return TRUE TD guest + @return FALSE Non-TD guest +**/ +BOOLEAN +EFIAPI +ProbeTdGuest ( + VOID ) +{ + return FALSE; +} diff --git a/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf new file mode 100644 index 000000000000..f3d5eb69ae15 --- /dev/null +++ b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf @@ -0,0 +1,27 @@ +## @file +# Null Tdx Probe library instance +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxProbeLibNull + FILE_GUID = D0DCCD86-FC42-4EE3-9132-53827C77661C + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxProbeLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 IA32 +# + +[Sources] + TdxProbeLibNull.c + +[Packages] + MdePkg/MdePkg.dec From 10e3845c29e272accaaae5a6e72c0d336bfedd69 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 09:17:59 +0800 Subject: [PATCH 04/54] OvmfPkg: Implement TdxProbLib instance in OvmfPkg For Td guest the initial mode on ResetVector is 32bit protected mode. While for Non-Td guest the initial mode is 16bit real mode. Based on this difference a TD flag is written to the Mailbox[0x10] in ResetVector. So that the TdxProbeLib can probe this flag for Td or Non-Td guest. This is a temporary solution to probe Td or Non-Td. When the related TDX CPUID leaf is ready, this library will be refined to invoke CPUID to probe the Td or Non-Td. --- OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c | 40 +++++++++++++++++++++ OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf | 31 ++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c create mode 100644 OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf diff --git a/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c new file mode 100644 index 000000000000..6f4220607a48 --- /dev/null +++ b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c @@ -0,0 +1,40 @@ +/** @file + instance of TdxProbeLib in OvmfPkg. + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include +#include + +BOOLEAN mTdGuest = FALSE; +BOOLEAN mTdGuestProbed = FALSE; + +#define TDX_WORK_AREA_OFFSET 0x10 + +/** + Probe whether it is TD guest or Non-TD guest. + + @return TRUE TD guest + @return FALSE Non-TD guest +**/ +BOOLEAN +EFIAPI +ProbeTdGuest ( + VOID) +{ + UINT8 * TdxWorkArea; + + if (mTdGuestProbed) { + return mTdGuest; + } + + TdxWorkArea = (UINT8 *)((UINTN)(FixedPcdGet32 (PcdTdMailboxBase) + TDX_WORK_AREA_OFFSET)); + mTdGuest = *TdxWorkArea != 0; + mTdGuestProbed = TRUE; + + return mTdGuest; +} diff --git a/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf new file mode 100644 index 000000000000..7e36735bfeff --- /dev/null +++ b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf @@ -0,0 +1,31 @@ +## @file +# Tdx Probe library instance in OvmfPkg +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxProbeLib + FILE_GUID = 26BF0B58-6E9D-4375-A363-52FD83FB82CE + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxProbeLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 IA32 +# + +[Sources] + TdxProbeLib.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase From 62dc827d5b18c8803605a76259cb42ba7a082679 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 08:23:37 +0800 Subject: [PATCH 05/54] MdePkg: Add Tdx support library Intel Trust Domain Extension (Intel TDX) refers to an Intel technology that extends Virtual Machines Extensions (VMX) and Multi-Key Total Memory Encryption (MKTME) with a new kind of virtual machine guest called a Trust Domain (TD). TdxLib is created with functions to perform the related Tdx operation. It includes functions for: - TdCall : Cause a VM exit to the Intel TDX module - TdVmCall : It is a leaf function 0 for TDCALL - TdVmCallCpuid : Enable the TD guest to request VMM to emulate CPUID - TdAcceptPages : Accept pending private pages - TdExtendRtmr : Extend one of the RTMR registers - TdSharedPageMask: Get the Td guest shared page mask The valid architecture of TdxLib is X64 because Intel TDX only supports X64 architecture. --- MdePkg/Include/IndustryStandard/Tdx.h | 199 +++++++++++++++++++++ MdePkg/Include/Library/TdxLib.h | 145 ++++++++++++++++ MdePkg/Library/TdxLib/AcceptPages.c | 74 ++++++++ MdePkg/Library/TdxLib/Rtmr.c | 125 ++++++++++++++ MdePkg/Library/TdxLib/TdSharedPageMask.c | 46 +++++ MdePkg/Library/TdxLib/TdxLib.inf | 40 +++++ MdePkg/Library/TdxLib/TdxLibNull.c | 158 +++++++++++++++++ MdePkg/Library/TdxLib/TdxLibNull.inf | 31 ++++ MdePkg/Library/TdxLib/X64/Tdcall.nasm | 124 +++++++++++++ MdePkg/Library/TdxLib/X64/Tdvmcall.nasm | 210 +++++++++++++++++++++++ 10 files changed, 1152 insertions(+) create mode 100644 MdePkg/Include/IndustryStandard/Tdx.h 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/TdSharedPageMask.c create mode 100644 MdePkg/Library/TdxLib/TdxLib.inf create mode 100644 MdePkg/Library/TdxLib/TdxLibNull.c create mode 100644 MdePkg/Library/TdxLib/TdxLibNull.inf create mode 100644 MdePkg/Library/TdxLib/X64/Tdcall.nasm create mode 100644 MdePkg/Library/TdxLib/X64/Tdvmcall.nasm diff --git a/MdePkg/Include/IndustryStandard/Tdx.h b/MdePkg/Include/IndustryStandard/Tdx.h new file mode 100644 index 000000000000..106c4daef686 --- /dev/null +++ b/MdePkg/Include/IndustryStandard/Tdx.h @@ -0,0 +1,199 @@ +/** @file + Intel Trust Domain Extension definitions + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _TDX_H_ +#define _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_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 diff --git a/MdePkg/Include/Library/TdxLib.h b/MdePkg/Include/Library/TdxLib.h new file mode 100644 index 000000000000..5c089ed2262a --- /dev/null +++ b/MdePkg/Include/Library/TdxLib.h @@ -0,0 +1,145 @@ +/** @file + TdxLib definitions + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _TDX_LIB_H_ +#define _TDX_LIB_H_ + +#include +#include +#include +#include + +/** + 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 UINT64 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 ges 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 + ); + + +/** + 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 + + @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..358a39b572f3 --- /dev/null +++ b/MdePkg/Library/TdxLib/AcceptPages.c @@ -0,0 +1,74 @@ +/** @file + + There are 4 defined types in TD memory. + Unaccepted memory is a special type of private memory. The guest + firmware must invoke TDCALL [TDG.MEM.PAGE.ACCEPT] the unaccepted + memory before use it. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +UINT64 mNumberOfDuplicatedAcceptedPages; + +/** + This function accept 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. Only accept 1G/2M/4K size. + + @return EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +TdAcceptPages ( + IN UINT64 StartAddress, + IN UINT64 NumberOfPages, + IN UINT64 PageSize + ) +{ + UINT64 Address; + UINT64 Status; + UINT64 Index; + UINT64 GpaPageSize; + + Address = StartAddress; + + if (PageSize == SIZE_4KB) { + GpaPageSize = TDCALL_ACCEPT_PAGE_SIZE_4K; + } else if (PageSize == SIZE_2MB) { + GpaPageSize = TDCALL_ACCEPT_PAGE_SIZE_2M; + } else if (PageSize == SIZE_1GB) { + GpaPageSize = TDCALL_ACCEPT_PAGE_SIZE_1G; + } else { + DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M/1G. Invalid page size - 0x%llx\n", PageSize)); + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < NumberOfPages; Index++) { + Status = TdCall (TDCALL_TDACCEPTPAGE,Address, GpaPageSize, 0, 0); + if (Status != TDX_EXIT_REASON_SUCCESS) { + if ((Status & ~0xFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) { + mNumberOfDuplicatedAcceptedPages++; + DEBUG ((DEBUG_VERBOSE, "Address %llx already accepted. Total number of already accepted pages %ld\n", + Address, mNumberOfDuplicatedAcceptedPages)); + } else { + DEBUG ((DEBUG_ERROR, "Address %llx failed to be accepted. Error = %ld\n", + Address, Status)); + ASSERT (Status == TDX_EXIT_REASON_SUCCESS); + } + } + Address += PageSize; + } + return EFI_SUCCESS; +} diff --git a/MdePkg/Library/TdxLib/Rtmr.c b/MdePkg/Library/TdxLib/Rtmr.c new file mode 100644 index 000000000000..34e6e11423e8 --- /dev/null +++ b/MdePkg/Library/TdxLib/Rtmr.c @@ -0,0 +1,125 @@ +/** @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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#define RTMR_COUNT 4 +#define TD_EXTEND_BUFFER_LEN (16 * 4 + 32) +#define EXTEND_BUFFER_ADDRESS_MASK 0x3f + + +#pragma pack(16) +typedef struct { + UINT8 Buffer[TD_EXTEND_BUFFER_LEN]; +} TDX_EXTEND_BUFFER; +#pragma pack() + +UINT8 *mExtendBufferAddress = NULL; +TDX_EXTEND_BUFFER mExtendBuffer; + +/** + TD.RTMR.EXTEND requires 64B-aligned guest physical address of + 48B-extension data. + #pragma pack() only supports 1/2/4/8/16, So we pre-allocate a + (16*4 + 32) length buffer in stack. In runtime we walk thru the + Buffer to find out a 64B-aligned start address. + + @return Start address of the extend buffer + +**/ +UINT8 * +EFIAPI +GetExtendBuffer ( + VOID + ) +{ + UINT8 ExtendBufferStart; + UINT8 *ExtendBufferAddress; + + if (mExtendBufferAddress != NULL) { + return mExtendBufferAddress; + } + + ExtendBufferStart = 0; + ExtendBufferAddress = mExtendBuffer.Buffer; + + while (ExtendBufferStart < TD_EXTEND_BUFFER_LEN) { + ExtendBufferAddress += ExtendBufferStart; + if (((UINT64)(UINTN)ExtendBufferAddress & EXTEND_BUFFER_ADDRESS_MASK) == 0) { + mExtendBufferAddress = ExtendBufferAddress; + break; + } else { + ExtendBufferStart += 16; + } + } + + ASSERT (ExtendBufferStart < TD_EXTEND_BUFFER_LEN); + DEBUG ((DEBUG_VERBOSE, "ExtendBufferAddress: 0x%p, 0x%x\n", ExtendBufferAddress, ExtendBufferStart)); + return mExtendBufferAddress; +} + + +/** + 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 (Index >= 0 && Index < RTMR_COUNT); + ASSERT (DataLen == SHA384_DIGEST_SIZE); + + ExtendBuffer = GetExtendBuffer(); + ASSERT (ExtendBuffer != NULL); + 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/TdSharedPageMask.c b/MdePkg/Library/TdxLib/TdSharedPageMask.c new file mode 100644 index 000000000000..e6cbad322145 --- /dev/null +++ b/MdePkg/Library/TdxLib/TdSharedPageMask.c @@ -0,0 +1,46 @@ +/** @file + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +UINT64 mTdSharedPageMask = 0; + +/** + This function ges 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); + mTdSharedPageMask = 1ULL << (Gpaw - 1); + ASSERT(Gpaw == 48 || Gpaw == 52); + return mTdSharedPageMask; +} diff --git a/MdePkg/Library/TdxLib/TdxLib.inf b/MdePkg/Library/TdxLib/TdxLib.inf new file mode 100644 index 000000000000..08951fc95b70 --- /dev/null +++ b/MdePkg/Library/TdxLib/TdxLib.inf @@ -0,0 +1,40 @@ +## @file +# Tdx library +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# 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 = X64 +# + +[Sources] + AcceptPages.c + Rtmr.c + TdSharedPageMask.c + X64/Tdcall.nasm + X64/Tdvmcall.nasm + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation diff --git a/MdePkg/Library/TdxLib/TdxLibNull.c b/MdePkg/Library/TdxLib/TdxLibNull.c new file mode 100644 index 000000000000..de357d88d4f9 --- /dev/null +++ b/MdePkg/Library/TdxLib/TdxLibNull.c @@ -0,0 +1,158 @@ +/** @file + Null instance of TdxLib. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +/** + 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 UINT64 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 ges 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; +} + +/** + 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 + + @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/TdxLibNull.inf b/MdePkg/Library/TdxLib/TdxLibNull.inf new file mode 100644 index 000000000000..77d3f0840437 --- /dev/null +++ b/MdePkg/Library/TdxLib/TdxLibNull.inf @@ -0,0 +1,31 @@ +## @file +# Null Tdx library instance +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxLibNull + FILE_GUID = 05C5E621-FC66-4420-9C80-F0DE9E5B95FF + 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 = X64 IA32 +# + +[Sources] + TdxLibNull.c + +[LibraryClasses] + BaseLib + DebugLib + +[Packages] + MdePkg/MdePkg.dec diff --git a/MdePkg/Library/TdxLib/X64/Tdcall.nasm b/MdePkg/Library/TdxLib/X64/Tdcall.nasm new file mode 100644 index 000000000000..e5bbdca3df08 --- /dev/null +++ b/MdePkg/Library/TdxLib/X64/Tdcall.nasm @@ -0,0 +1,124 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66,0x0f,0x01,0xcc +%endif +%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 + +; 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..12de700ff6ff --- /dev/null +++ b/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm @@ -0,0 +1,210 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+;* 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 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66,0x0f,0x01,0xcc +%endif +%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 + +;------------------------------------------------------------------------------ +; 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 + + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK + + ; Save *results pointers + push r8 + + 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 + jnz .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 From f7c572d92e7be4f487bad34914d5a423f20297c1 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 09:23:19 +0800 Subject: [PATCH 06/54] MdePkg: Add BaseIoLibIntrinsicTdx to support Td guest TDX architecture does not prescribe a specific software convention to perform I/O from the guest TD. The 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. In BaseIoLibIntrinsicTdx Para-Virtualized I/O model is used. It replaces IO/MMIO access with TDCALL(TDVMCALL) to invoke VMM provided IO/MMIO emulation functions. --- .../BaseIoLibIntrinsicSev.inf | 104 ++-- .../BaseIoLibIntrinsicTdx.inf | 53 ++ .../Library/BaseIoLibIntrinsic/IoLibGccTdx.c | 231 +++++++++ .../BaseIoLibIntrinsic/IoLibInternalTdx.c | 456 ++++++++++++++++++ .../Library/BaseIoLibIntrinsic/IoLibMscTdx.c | 271 +++++++++++ MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c | 369 ++++++++++++++ MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h | 102 ++++ 7 files changed, 1534 insertions(+), 52 deletions(-) create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibGccTdx.c create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibMscTdx.c create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf index 86a07e60f838..84fdf6adb79c 100644 --- a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf +++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf @@ -1,52 +1,52 @@ -## @file -# Instance of I/O Library using compiler intrinsics. -# -# I/O Library that uses compiler intrinsics to perform IN and OUT instructions -# for IA-32 and x64. -# -# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
-# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
-# Copyright (c) 2017, AMD Incorporated. All rights reserved.
-# -# SPDX-License-Identifier: BSD-2-Clause-Patent -# -## - -[Defines] - INF_VERSION = 0x00010005 - BASE_NAME = BaseIoLibIntrinsicSev - MODULE_UNI_FILE = BaseIoLibIntrinsic.uni - FILE_GUID = 93742f95-6e71-4581-b600-8e1da443f95a - MODULE_TYPE = BASE - VERSION_STRING = 1.0 - LIBRARY_CLASS = IoLib - - -# -# VALID_ARCHITECTURES = IA32 X64 -# - -[Sources] - IoLibMmioBuffer.c - BaseIoLibIntrinsicInternal.h - IoHighLevel.c - -[Sources.IA32] - IoLibGcc.c | GCC - IoLibMsc.c | MSFT - IoLib.c - Ia32/IoFifoSev.nasm - -[Sources.X64] - IoLibGcc.c | GCC - IoLibMsc.c | MSFT - IoLib.c - X64/IoFifoSev.nasm - -[Packages] - MdePkg/MdePkg.dec - -[LibraryClasses] - DebugLib - BaseLib - +## @file +# Instance of I/O Library using compiler intrinsics. +# +# I/O Library that uses compiler intrinsics to perform IN and OUT instructions +# for IA-32 and x64. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseIoLibIntrinsicSev + MODULE_UNI_FILE = BaseIoLibIntrinsic.uni + FILE_GUID = 93742f95-6e71-4581-b600-8e1da443f95a + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = IoLib + + +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + IoLibMmioBuffer.c + BaseIoLibIntrinsicInternal.h + IoHighLevel.c + +[Sources.IA32] + IoLibGcc.c | GCC + IoLibMsc.c | MSFT + IoLib.c + Ia32/IoFifoSev.nasm + +[Sources.X64] + IoLibGcc.c | GCC + IoLibMsc.c | MSFT + IoLib.c + X64/IoFifoSev.nasm + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + BaseLib + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf new file mode 100644 index 000000000000..3032460dfadc --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf @@ -0,0 +1,53 @@ +## @file +# Instance of I/O Library compatible for both Td guest and Non-Td guest. +# +# For Non-Td guest this I/O Library uses compiler intrinsics to perform +# IN and OUT instructions for X64. +# +# For Td guest this I/O Library uses TDVMCALL to perform IN and OUT +# instruction for X64. +# +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.
+# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# Portions Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseIoLibIntrinsicTdx + MODULE_UNI_FILE = BaseIoLibIntrinsic.uni + FILE_GUID = 681b9f30-fc2d-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = IoLib + + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + IoLibMmioBuffer.c + BaseIoLibIntrinsicInternal.h + IoHighLevel.c + +[Sources.X64] + IoLibGccTdx.c | GCC + IoLibMscTdx.c | MSFT + IoLibInternalTdx.c + IoLibTdx.c + X64/IoFifoSev.nasm + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + BaseLib + TdxLib + TdxProbeLib + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibGccTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGccTdx.c new file mode 100644 index 000000000000..a8e4295f8767 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGccTdx.c @@ -0,0 +1,231 @@ +/** @file + I/O Library. This file has compiler specifics for GCC as there is no + ANSI C standard for doing IO. + + GCC - uses EFIAPI assembler. __asm__ calls GAS. __volatile__ makes sure the + compiler puts the assembler in this exact location. The complex GNUC + operations are not optimzed. It would be possible to also write these + with EFIAPI assembler. + + We don't advocate putting compiler specifics in libraries or drivers but there + is no other way to make this work. + + Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "BaseIoLibIntrinsicInternal.h" +#include +#include "IoLibTdx.h" + +/** + Reads an 8-bit I/O port. + + Reads the 8-bit I/O port specified by Port. The 8-bit read value is returned. + 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() + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT8 +EFIAPI +IoRead8 ( + IN UINTN Port + ) +{ + UINT8 Data; + + if (ProbeTdGuest ()) { + Data = TdIoRead8 (Port); + return Data; + } + + __asm__ __volatile__ ("inb %w1,%b0" : "=a" (Data) : "d" ((UINT16)Port)); + return Data; +} + +/** + Writes an 8-bit I/O port. + + Writes the 8-bit I/O port specified by Port with the value specified by Value + and returns Value. 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(). + + 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. + + @return The value written the I/O port. + +**/ +UINT8 +EFIAPI +IoWrite8 ( + IN UINTN Port, + IN UINT8 Value + ) +{ + if (ProbeTdGuest ()) { + TdIoWrite8 (Port, Value); + return Value; + } + __asm__ __volatile__ ("outb %b0,%w1" : : "a" (Value), "d" ((UINT16)Port)); + return Value;; +} + +/** + Reads a 16-bit I/O port. + + Reads the 16-bit I/O port specified by Port. The 16-bit read value is returned. + 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(). + 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. + +**/ +UINT16 +EFIAPI +IoRead16 ( + IN UINTN Port + ) +{ + UINT16 Data; + + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + Data = TdIoRead16 (Port); + return Data; + } + + __asm__ __volatile__ ("inw %w1,%w0" : "=a" (Data) : "d" ((UINT16)Port)); + return Data; +} + +/** + Writes a 16-bit I/O port. + + Writes the 16-bit I/O port specified by Port with the value specified by Value + and returns Value. 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(). + 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. + + @return The value written the I/O port. + +**/ +UINT16 +EFIAPI +IoWrite16 ( + IN UINTN Port, + IN UINT16 Value + ) +{ + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite16 (Port, Value); + return Value; + } + + __asm__ __volatile__ ("outw %w0,%w1" : : "a" (Value), "d" ((UINT16)Port)); + return Value;; +} + +/** + Reads a 32-bit I/O port. + + Reads the 32-bit I/O port specified by Port. The 32-bit read value is returned. + 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(). + 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. + +**/ +UINT32 +EFIAPI +IoRead32 ( + IN UINTN Port + ) +{ + UINT32 Data; + + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + Data = TdIoRead32 (Port); + return Data; + } + + __asm__ __volatile__ ("inl %w1,%0" : "=a" (Data) : "d" ((UINT16)Port)); + return Data; +} + +/** + Writes a 32-bit I/O port. + + Writes the 32-bit I/O port specified by Port with the value specified by Value + and returns Value. 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(). + 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. + + @return The value written the I/O port. + +**/ +UINT32 +EFIAPI +IoWrite32 ( + IN UINTN Port, + IN UINT32 Value + ) +{ + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite32 (Port, Value); + return Value; + } + + __asm__ __volatile__ ("outl %0,%w1" : : "a" (Value), "d" ((UINT16)Port)); + return Value; +} + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c new file mode 100644 index 000000000000..eec490ac3816 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c @@ -0,0 +1,456 @@ +/** @file + TDX I/O Library routines. + + Copyright (c) 2020-2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "IoLibTdx.h" +#include +#include + +// 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 + +/** + 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; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + 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 Val + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Value = Val; + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT8*)Address = Val; + } + MemoryFence (); + + return 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 + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + 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 Val + ) +{ + UINT64 Value; + UINT64 Status; + + ASSERT ((Address & 1) == 0); + + Address |= TdSharedPageMask (); + + MemoryFence (); + Value = Val; + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT16*)Address = Val; + } + MemoryFence (); + + return 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 + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + 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 Val + ) +{ + UINT64 Value; + UINT64 Status; + + ASSERT ((Address & 3) == 0); + + Address |= TdSharedPageMask (); + + MemoryFence (); + Value = Val; + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT32*)Address = Val; + } + MemoryFence (); + + return 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 + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + 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; + + ASSERT ((Address & 7) == 0); + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT64*)Address = Value; + } + MemoryFence (); + return Value; +} + + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibMscTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMscTdx.c new file mode 100644 index 000000000000..247a6bdafa2a --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMscTdx.c @@ -0,0 +1,271 @@ +/** @file + I/O Library. This file has compiler specifics for Microsft C as there is no + ANSI C standard for doing IO. + + MSC - uses intrinsic functions and the optimize will remove the function call + overhead. + + We don't advocate putting compiler specifics in libraries or drivers but there + is no other way to make this work. + + Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + + +#include "BaseIoLibIntrinsicInternal.h" +#include +#include "IoLibTdx.h" + +// +// Microsoft Visual Studio 7.1 Function Prototypes for I/O Intrinsics. +// + +int _inp (unsigned short port); +unsigned short _inpw (unsigned short port); +unsigned long _inpd (unsigned short port); +int _outp (unsigned short port, int databyte ); +unsigned short _outpw (unsigned short port, unsigned short dataword ); +unsigned long _outpd (unsigned short port, unsigned long dataword ); +void _ReadWriteBarrier (void); + +#pragma intrinsic(_inp) +#pragma intrinsic(_inpw) +#pragma intrinsic(_inpd) +#pragma intrinsic(_outp) +#pragma intrinsic(_outpw) +#pragma intrinsic(_outpd) +#pragma intrinsic(_ReadWriteBarrier) + +// +// _ReadWriteBarrier() forces memory reads and writes to complete at the point +// in the call. This is only a hint to the compiler and does emit code. +// In past versions of the compiler, _ReadWriteBarrier was enforced only +// locally and did not affect functions up the call tree. In Visual C++ +// 2005, _ReadWriteBarrier is enforced all the way up the call tree. +// + +/** + Reads an 8-bit I/O port. + + Reads the 8-bit I/O port specified by Port. The 8-bit read value is returned. + 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(). + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT8 +EFIAPI +IoRead8 ( + IN UINTN Port + ) +{ + UINT8 Value; + + if (ProbeTdGuest ()) { + Value = TdIoRead8 (Port); + return Value; + } + + _ReadWriteBarrier (); + Value = (UINT8)_inp ((UINT16)Port); + _ReadWriteBarrier (); + return Value; +} + +/** + Writes an 8-bit I/O port. + + Writes the 8-bit I/O port specified by Port with the value specified by Value + and returns Value. 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(). + + 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. + + @return The value written to the I/O port. + +**/ +UINT8 +EFIAPI +IoWrite8 ( + IN UINTN Port, + IN UINT8 Value + ) +{ + + if (ProbeTdGuest ()) { + TdIoWrite8 (Port, Value); + return Value; + } + + _ReadWriteBarrier (); + (UINT8)_outp ((UINT16)Port, Value); + _ReadWriteBarrier (); + return Value; +} + +/** + Reads a 16-bit I/O port. + + Reads the 16-bit I/O port specified by Port. The 16-bit read value is returned. + 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(). + 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. + +**/ +UINT16 +EFIAPI +IoRead16 ( + IN UINTN Port + ) +{ + UINT16 Value; + + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + Value = TdIoRead16 (Port); + return Value; + } + + _ReadWriteBarrier (); + Value = _inpw ((UINT16)Port); + _ReadWriteBarrier (); + return Value; +} + +/** + Writes a 16-bit I/O port. + + Writes the 16-bit I/O port specified by Port with the value specified by Value + and returns Value. 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(). + 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. + + @return The value written to the I/O port. + +**/ +UINT16 +EFIAPI +IoWrite16 ( + IN UINTN Port, + IN UINT16 Value + ) +{ + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite16 (Port, Value); + return Value; + } + + _ReadWriteBarrier (); + _outpw ((UINT16)Port, Value); + _ReadWriteBarrier (); + return Value; +} + +/** + Reads a 32-bit I/O port. + + Reads the 32-bit I/O port specified by Port. The 32-bit read value is returned. + 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(). + 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. + +**/ +UINT32 +EFIAPI +IoRead32 ( + IN UINTN Port + ) +{ + UINT32 Value; + + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + Value = TdIoRead32 (Port); + return Value; + } + + _ReadWriteBarrier (); + Value = _inpd ((UINT16)Port); + _ReadWriteBarrier (); + return Value; +} + +/** + Writes a 32-bit I/O port. + + Writes the 32-bit I/O port specified by Port with the value specified by Value + and returns Value. 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(). + 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. + + @return The value written to the I/O port. + +**/ +UINT32 +EFIAPI +IoWrite32 ( + IN UINTN Port, + IN UINT32 Value + ) +{ + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite32 (Port, Value); + return Value; + } + + _ReadWriteBarrier (); + _outpd ((UINT16)Port, Value); + _ReadWriteBarrier (); + return Value; +} diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c new file mode 100644 index 000000000000..db208f74a600 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c @@ -0,0 +1,369 @@ +/** @file + Common I/O Library routines. + + Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "BaseIoLibIntrinsicInternal.h" +#include +#include "IoLibTdx.h" + +/** + Reads a 64-bit I/O port. + + Reads the 64-bit I/O port specified by Port. The 64-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 64-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 64-bit boundary, then ASSERT(). + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT64 +EFIAPI +IoRead64 ( + IN UINTN Port + ) +{ + ASSERT (FALSE); + return 0; +} + +/** + Writes a 64-bit I/O port. + + Writes the 64-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 64-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 64-bit boundary, then ASSERT(). + + @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. + +**/ +UINT64 +EFIAPI +IoWrite64 ( + IN UINTN Port, + IN UINT64 Value + ) +{ + ASSERT (FALSE); + return 0; +} + + +/** + Reads an 8-bit MMIO register. + + Reads the 8-bit MMIO register specified by Address. The 8-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + 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. + +**/ +UINT8 +EFIAPI +MmioRead8 ( + IN UINTN Address + ) +{ + UINT8 Value; + + if (ProbeTdGuest ()) { + Value = TdMmioRead8 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT8*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes an 8-bit MMIO register. + + Writes the 8-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + 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. + + @return Value. + +**/ +UINT8 +EFIAPI +MmioWrite8 ( + IN UINTN Address, + IN UINT8 Value + ) +{ + if (ProbeTdGuest ()) { + TdMmioWrite8 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT8*)Address = Value; + MemoryFence (); + + return Value; +} + +/** + Reads a 16-bit MMIO register. + + Reads the 16-bit MMIO register specified by Address. The 16-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + 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. + +**/ +UINT16 +EFIAPI +MmioRead16 ( + IN UINTN Address + ) +{ + UINT16 Value; + + ASSERT ((Address & 1) == 0); + + if (ProbeTdGuest ()) { + Value = TdMmioRead16 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT16*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes a 16-bit MMIO register. + + Writes the 16-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + 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. + + @return Value. + +**/ +UINT16 +EFIAPI +MmioWrite16 ( + IN UINTN Address, + IN UINT16 Value + ) +{ + ASSERT ((Address & 1) == 0); + + if (ProbeTdGuest ()) { + TdMmioWrite16 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT16*)Address = Value; + MemoryFence (); + + return Value; +} + +/** + Reads a 32-bit MMIO register. + + Reads the 32-bit MMIO register specified by Address. The 32-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + 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. + +**/ +UINT32 +EFIAPI +MmioRead32 ( + IN UINTN Address + ) +{ + UINT32 Value; + + ASSERT ((Address & 3) == 0); + + if (ProbeTdGuest ()) { + Value = TdMmioRead32 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT32*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes a 32-bit MMIO register. + + Writes the 32-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + 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. + + @return Value. + +**/ +UINT32 +EFIAPI +MmioWrite32 ( + IN UINTN Address, + IN UINT32 Value + ) +{ + ASSERT ((Address & 3) == 0); + + if (ProbeTdGuest ()) { + TdMmioWrite32 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT32*)Address = Value; + MemoryFence (); + + return Value; +} + +/** + Reads a 64-bit MMIO register. + + Reads the 64-bit MMIO register specified by Address. The 64-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + 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. + +**/ +UINT64 +EFIAPI +MmioRead64 ( + IN UINTN Address + ) +{ + UINT64 Value; + + ASSERT ((Address & 7) == 0); + + if (ProbeTdGuest ()) { + Value = TdMmioRead64 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT64*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes a 64-bit MMIO register. + + Writes the 64-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + 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. + +**/ +UINT64 +EFIAPI +MmioWrite64 ( + IN UINTN Address, + IN UINT64 Value + ) +{ + ASSERT ((Address & 7) == 0); + + if (ProbeTdGuest ()) { + TdMmioWrite64 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT64*)Address = Value; + MemoryFence (); + + return Value; +} + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h new file mode 100644 index 000000000000..551e239c6976 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h @@ -0,0 +1,102 @@ +/** @file + Header file for Tdx IO library. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __IOLIB_TDX_H__ +#define __IOLIB_TDX_H__ + +UINT8 +EFIAPI +TdIoRead8 ( + IN UINTN Port + ); + +UINT16 +EFIAPI +TdIoRead16 ( + IN UINTN Port + ); + +UINT32 +EFIAPI +TdIoRead32 ( + IN UINTN Port + ); + +UINT8 +EFIAPI +TdIoWrite8 ( + IN UINTN Port, + IN UINT8 Value + ); + +UINT16 +EFIAPI +TdIoWrite16 ( + IN UINTN Port, + IN UINT16 Value + ); + +UINT32 +EFIAPI +TdIoWrite32 ( + IN UINTN Port, + IN UINT32 Value + ); + +UINT8 +EFIAPI +TdMmioRead8 ( + IN UINTN Address + ); + +UINT8 +EFIAPI +TdMmioWrite8 ( + IN UINTN Address, + IN UINT8 Val + ); + +UINT16 +EFIAPI +TdMmioRead16 ( + IN UINTN Address + ); + +UINT16 +EFIAPI +TdMmioWrite16 ( + IN UINTN Address, + IN UINT16 Val + ); + +UINT32 +EFIAPI +TdMmioRead32 ( + IN UINTN Address + ); + +UINT32 +EFIAPI +TdMmioWrite32 ( + IN UINTN Address, + IN UINT32 Val + ); + +UINT64 +EFIAPI +TdMmioRead64 ( + IN UINTN Address + ); + +UINT64 +EFIAPI +TdMmioWrite64 ( + IN UINTN Address, + IN UINT64 Value + ); + +#endif From 7b0424d079bfa1540df878d0da572763295cfd56 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 09:24:19 +0800 Subject: [PATCH 07/54] OvmfPkg: Update IoLib in OvmfPkgX64.dsc to support both Td and Non-Td BaseIoLibIntrinsicTdx support both Td and Non-Td guest, including SEV and TDX. --- OvmfPkg/OvmfPkgX64.dsc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index dd0b7631dfff..36f53c93f915 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -157,7 +157,7 @@ PciCapLib|OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf PciCapPciSegmentLib|OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf PciCapPciIoLib|OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf - IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf + IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf OemHookStatusCodeLib|MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf SerialPortLib|PcAtChipsetPkg/Library/SerialIoLib/SerialIoLib.inf MtrrLib|UefiCpuPkg/Library/MtrrLib/MtrrLib.inf @@ -245,6 +245,8 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf + TdxLib|MdePkg/Library/TdxLib/TdxLib.inf + TdxProbeLib|OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf [LibraryClasses.common.SEC] TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf From 0e8e351d83736f253f87ff1d73e8242539693402 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 10:25:51 +0800 Subject: [PATCH 08/54] OvmfPkg: Update PCD for TDX and the layout in OvmfPkgX64.fdf 1. Add PCD definition for TDX layout 2. Update the OvmfPkgX64.fdf to add TdHob --- OvmfPkg/OvmfPkg.dec | 15 +++++++++++++++ OvmfPkg/OvmfPkgDefines.fdf.inc | 11 ++++++++++- OvmfPkg/OvmfPkgX64.fdf | 4 ++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 1716c1a84f51..569c699e4a10 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -316,7 +316,22 @@ ## PCD used by TDX gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase|0|UINT32|0x46 gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize|0|UINT32|0x47 + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase|0|UINT32|0x48 + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize|0|UINT32|0x49 + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase|0|UINT32|0x4a + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize|0|UINT32|0x4b + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapBase|0|UINT32|0x4c + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapSize|0|UINT32|0x4d + + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase|0|UINT32|0x4e + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset|0|UINT32|0x4f + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize|0|UINT32|0x50 + + gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase|0|UINT32|0x51 + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset|0|UINT32|0x52 + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize|0|UINT32|0x53 gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|0x0|UINT32|0x55 + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x58 [PcdsDynamic, PcdsDynamicEx] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10 diff --git a/OvmfPkg/OvmfPkgDefines.fdf.inc b/OvmfPkg/OvmfPkgDefines.fdf.inc index 35fd454b97ab..2b3990b2a3ac 100644 --- a/OvmfPkg/OvmfPkgDefines.fdf.inc +++ b/OvmfPkg/OvmfPkgDefines.fdf.inc @@ -9,7 +9,7 @@ ## DEFINE BLOCK_SIZE = 0x1000 - +DEFINE VARS_OFFSET = 0 # # A firmware binary built with FD_SIZE_IN_KB=1024, and a firmware binary built # with FD_SIZE_IN_KB=2048, use the same variable store layout. @@ -66,6 +66,7 @@ DEFINE SECFV_OFFSET = 0x003CC000 DEFINE SECFV_SIZE = 0x34000 !endif +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb = $(FD_SIZE_IN_KB) SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress = $(FW_BASE_ADDRESS) SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = $(FW_SIZE) SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize = $(BLOCK_SIZE) @@ -82,6 +83,14 @@ SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize = $(BLOCK_SIZ SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize = $(VARS_SPARE_SIZE) +SET gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase = $(FW_BASE_ADDRESS) +SET gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset = $(VARS_OFFSET) +SET gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize = $(VARS_LIVE_SIZE) + +SET gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase = $(CODE_BASE_ADDRESS) +SET gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset = $(VARS_SIZE) +SET gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize = $(CODE_SIZE) + !if $(SMM_REQUIRE) == TRUE SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index 3117d0b24704..e239a646ccaa 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -100,6 +100,10 @@ DATA = { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 } + +0x00E000|0x001000 +gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase|gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize + 0x010000|0x010000 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize From 16c0bce39fa075ef5b3740db37586e3a6d947530 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 10:29:16 +0800 Subject: [PATCH 09/54] OvmfPkg/MdePkg: Update ResetVector for Tdx When upstream to edk2 master, this commit should be split into small ones. --- OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 32 +++++- OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm | 46 +++++++++ OvmfPkg/ResetVector/Ia32/PageTables64.asm | 27 ----- OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm | 49 ++++++++++ OvmfPkg/ResetVector/ResetVector.inf | 19 +++- OvmfPkg/ResetVector/ResetVector.nasmb | 72 +++++++++++++- OvmfPkg/ResetVector/X64/PageTables.asm | 48 +++++++++ OvmfPkg/ResetVector/X64/TdxMetadata.asm | 98 +++++++++++++++++++ UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc | 29 ++++++ UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm | 5 + .../ResetVector/Vtf0/Ia16/Real16ToFlat32.asm | 29 ++++++ UefiCpuPkg/ResetVector/Vtf0/Main.asm | 54 ++++++++++ 12 files changed, 475 insertions(+), 33 deletions(-) create mode 100644 OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm create mode 100644 OvmfPkg/ResetVector/X64/PageTables.asm create mode 100644 OvmfPkg/ResetVector/X64/TdxMetadata.asm diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm index 9c0b5853a46f..024754a965fb 100644 --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm @@ -21,8 +21,12 @@ ALIGN 16 ; This is required so the page tables will be 4k aligned when VTF0 is ; located just below 0x100000000 (4GB) in the firmware device. ; -%ifdef ALIGN_TOP_TO_4K_FOR_PAGING +; For TDX_VIRTUAL_FIRMWARE the padding is in X64\TdxMetadata.asm. +; +%ifndef TDX_VIRTUAL_FIRMWARE + %ifdef ALIGN_TOP_TO_4K_FOR_PAGING TIMES (0x1000 - ($ - EndOfPageTables) - 0x20) DB 0 + %endif %endif ; @@ -115,11 +119,23 @@ applicationProcessorEntryPoint: ; location. (0xffffffe0) This allows the Local APIC Startup IPI to be ; used to wake up the application processors. ; +%ifdef TDX_VIRTUAL_FIRMWARE + DD (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16)) +%else jmp EarlyApInitReal16 +%endif ALIGN 8 +; +; TDX Virtual Firmware injects metadata in VTF0. +; The address of the metadata is injected in this location (0xffffffe8) +; +%ifdef TDX_VIRTUAL_FIRMWARE + DD (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16)) +%else DD 0 +%endif ; ; The VTF signature @@ -137,9 +153,23 @@ resetVector: ; Reset Vector ; ; This is where the processor will begin execution +; +; TDX_VIRTUAL_FIRMWARE is defined in X64/TdxMetadata.asm +; For TDX_VIRTAUL_FIRMWARE, it is of Protected mode in ResetVector +; Note: +; The distance between ResetVector and EarlyBspPmEntry should +; be less than 128 bytes. +; DO NOT ADD MORE DATA between ResetVector and EarlyBspPmEntry ; nop nop + +%ifdef TDX_VIRTUAL_FIRMWARE + smsw ax + test al, 1 + jnz EarlyBspPmEntry +%endif + jmp EarlyBspInitReal16 ALIGN 16 diff --git a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm index c6d0d898bcd1..f034bf1b555d 100644 --- a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm +++ b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm @@ -14,6 +14,8 @@ BITS 32 ; Modified: EAX, ECX, EDX ; Transition32FlatTo64Flat: + cmp byte[TDX_WORK_AREA], 1 + jz TdxTransition32FlatTo64Flat OneTimeCall SetCr3ForPageTables64 @@ -26,6 +28,7 @@ Transition32FlatTo64Flat: bts eax, 8 ; set LME wrmsr +SevEsMitigationCheck: ; ; SEV-ES mitigation check support ; @@ -65,10 +68,53 @@ EnablePaging: bts eax, 31 ; set PG mov cr0, eax ; enable paging + jmp _jumpTo64Bit + +; +; Tdx Transition from 32Flat to 64Flat +; Tdx use the page table built in Vtf0 for quicker performance +; +TdxTransition32FlatTo64Flat: + mov eax, cr4 + bts eax, 5 ; enable PAE + + ; + ; byte[TDX_WORK_AREA_PAGELEVEL5] holds the indicator whether 52bit is supported. + ; if it is the case, need to set LA57 and use 5-level paging + ; + cmp byte[TDX_WORK_AREA_PAGELEVEL5], 0 + jz .set_cr4 + bts eax, 12 +.set_cr4: + mov cr4, eax + + mov ebx, ADDR_OF(TopLevelPageDirectory) + ; + ; if we just set la57, we are ok, if using 4-level paging, adjust top-level page directory + ; + bt eax, 12 + jc .set_cr3 + add ebx, 0x1000 +.set_cr3: + mov cr3, ebx + + mov eax, cr0 + bts eax, 31 ; set PG + mov cr0, eax ; enable paging + +_jumpTo64Bit: jmp LINEAR_CODE64_SEL:ADDR_OF(jumpTo64BitAndLandHere) + BITS 64 jumpTo64BitAndLandHere: + ; + ; For Td guest we are done and jump to the end + ; + mov eax, TDX_WORK_AREA + cmp byte[eax], 1 + jz GoodCompare + ; ; Check if the second step of the SEV-ES mitigation is to be performed. ; diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm index 5fae8986d9da..e6f64900a82f 100644 --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm @@ -10,33 +10,6 @@ BITS 32 -%define PAGE_PRESENT 0x01 -%define PAGE_READ_WRITE 0x02 -%define PAGE_USER_SUPERVISOR 0x04 -%define PAGE_WRITE_THROUGH 0x08 -%define PAGE_CACHE_DISABLE 0x010 -%define PAGE_ACCESSED 0x020 -%define PAGE_DIRTY 0x040 -%define PAGE_PAT 0x080 -%define PAGE_GLOBAL 0x0100 -%define PAGE_2M_MBO 0x080 -%define PAGE_2M_PAT 0x01000 - -%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \ - PAGE_DIRTY + \ - PAGE_READ_WRITE + \ - PAGE_PRESENT) - -%define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \ - PAGE_ACCESSED + \ - PAGE_DIRTY + \ - PAGE_READ_WRITE + \ - PAGE_PRESENT) - -%define PAGE_PDP_ATTR (PAGE_ACCESSED + \ - PAGE_READ_WRITE + \ - PAGE_PRESENT) - ; ; SEV-ES #VC exception handler support ; diff --git a/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm b/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm new file mode 100644 index 000000000000..b4a4960b5c64 --- /dev/null +++ b/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm @@ -0,0 +1,49 @@ +;------------------------------------------------------------------------------ +; @file +; Transition from 16 bit real mode into 32 bit flat protected mode +; +; Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +;------------------------------------------------------------------------------ + +%define SEC_DEFAULT_CR0 0x00000023 +%define SEC_DEFAULT_CR4 0x640 + +BITS 32 + +; +; Modified: EAX, EBX +; +ReloadFlat32: + + cli + mov ebx, ADDR_OF(gdtr) + lgdt [ebx] + + mov eax, SEC_DEFAULT_CR0 + mov cr0, eax + + jmp LINEAR_CODE_SEL:dword ADDR_OF(jumpToFlat32BitAndLandHere) +BITS 32 +jumpToFlat32BitAndLandHere: + + mov eax, SEC_DEFAULT_CR4 + mov cr4, eax + + debugShowPostCode POSTCODE_32BIT_MODE + + mov ax, LINEAR_SEL + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + OneTimeCallRet ReloadFlat32 \ No newline at end of file diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf index dc38f68919cd..d161b9c689c2 100644 --- a/OvmfPkg/ResetVector/ResetVector.inf +++ b/OvmfPkg/ResetVector/ResetVector.inf @@ -1,7 +1,7 @@ ## @file # Reset Vector # -# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+# Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -44,6 +44,23 @@ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapSize + + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize + gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize + + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb [FixedPcd] gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb index 5fbacaed5f9d..abca50e75bc2 100644 --- a/OvmfPkg/ResetVector/ResetVector.nasmb +++ b/OvmfPkg/ResetVector/ResetVector.nasmb @@ -4,6 +4,7 @@ ; ; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.
; Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
+; Copyright (c) 2021, Intel Corporation. All rights reserved.
; SPDX-License-Identifier: BSD-2-Clause-Patent ; ;------------------------------------------------------------------------------ @@ -36,6 +37,56 @@ %include "PostCodes.inc" + +%ifdef ARCH_X64 + + ; + ; TdMailboxBase[0x10, 0x800] is reserved for OS + ; Td guest uses this area (TdMailboxBase[0x10,0x20]) to record the Td guest info + ; SEC can determine Td guest by this information + ; + %define TD_MAILBOX_WORKAREA_OFFSET 0x10 + %define TDX_WORK_AREA (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET) + %define TDX_WORK_AREA_PAGELEVEL5 (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET + 1) + %define TDX_WORK_AREA_INITVP (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET + 8) + %define TDX_WORK_AREA_INFO (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET + 8 + 4) + + ; + ; TDX meta data + ; + %define TDX_METADATA_SECTION_TYPE_BFV 0 + %define TDX_METADATA_SECTION_TYPE_CFV 1 + %define TDX_METADATA_SECTION_TYPE_TD_HOB 2 + %define TDX_METADATA_SECTION_TYPE_TEMP_MEM 3 + %define TDX_METADATA_VERSION 1 + %define TDX_METADATA_ATTRIBUTES_EXTENDMR 0x00000001 + + %define TDX_BFV_RAW_DATA_OFFSET FixedPcdGet32(PcdBfvRawDataOffset) + %define TDX_BFV_RAW_DATA_SIZE FixedPcdGet32(PcdBfvRawDataSize) + %define TDX_BFV_MEMORY_BASE FixedPcdGet32(PcdBfvBase) + %define TDX_BFV_MEMORY_SIZE FixedPcdGet32(PcdBfvRawDataSize) + + %define TDX_CFV_RAW_DATA_OFFSET FixedPcdGet32(PcdCfvRawDataOffset) + %define TDX_CFV_RAW_DATA_SIZE FixedPcdGet32(PcdCfvRawDataSize) + %define TDX_CFV_MEMORY_BASE FixedPcdGet32(PcdCfvBase), + %define TDX_CFV_MEMORY_SIZE FixedPcdGet32(PcdCfvRawDataSize), + + %define TDX_STACK_MEMORY_BASE (FixedPcdGet32(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize)/2) + %define TDX_STACK_MEMORY_SIZE (FixedPcdGet32(PcdOvmfSecPeiTempRamSize)/2) + + %define TDX_HEAP_MEMORY_BASE (FixedPcdGet32(PcdOvmfSecPeiTempRamBase)) + %define TDX_HEAP_MEMORY_SIZE (FixedPcdGet32(PcdOvmfSecPeiTempRamSize)/2) + + %define TDX_HOB_MEMORY_BASE FixedPcdGet32(PcdTdHobBase) + %define TDX_HOB_MEMORY_SIZE FixedPcdGet32(PcdTdHobSize) + + %define TDX_MAILBOX_MEMORY_BASE FixedPcdGet32(PcdTdMailboxBase) + %define TDX_MAILBOX_MEMORY_SIZE FixedPcdGet32(PcdTdMailboxSize) + + %include "X64/PageTables.asm" + %include "X64/TdxMetadata.asm" +%endif + %ifdef DEBUG_PORT80 %include "Port80Debug.asm" %elifdef DEBUG_SERIAL @@ -76,17 +127,30 @@ %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 8) %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16) %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) -%include "Ia32/Flat32ToFlat64.asm" -%include "Ia32/PageTables64.asm" + + ; + ; TDX specific + ; + %if (FixedPcdGet32 (PcdTdMailboxSize) != 0x1000) + %error "This implementation inherently depends on PcdTdMailboxSize" + %endif + + %if (FixedPcdGet32 (PcdTdHobSize) != 0x1000) + %error "This implementation inherently depends on PcdTdHobSize" + %endif + + %include "Ia32/Flat32ToFlat64.asm" + %include "Ia32/PageTables64.asm" + %include "Ia32/ReloadFlat32.asm" %endif %include "Ia16/Real16ToFlat32.asm" -%include "Ia16/Init16.asm" - %include "Main.asm" %define SEV_ES_AP_RESET_IP FixedPcdGet32 (PcdSevEsWorkAreaBase) %define SEV_LAUNCH_SECRET_BASE FixedPcdGet32 (PcdSevLaunchSecretBase) %define SEV_LAUNCH_SECRET_SIZE FixedPcdGet32 (PcdSevLaunchSecretSize) + %define OVMF_IMAGE_SIZE_IN_KB FixedPcdGet32 (PcdOvmfImageSizeInKb) +%include "Ia16/Init16.asm" %include "Ia16/ResetVectorVtf0.asm" diff --git a/OvmfPkg/ResetVector/X64/PageTables.asm b/OvmfPkg/ResetVector/X64/PageTables.asm new file mode 100644 index 000000000000..379f9fd2e355 --- /dev/null +++ b/OvmfPkg/ResetVector/X64/PageTables.asm @@ -0,0 +1,48 @@ +;------------------------------------------------------------------------------ +; @file +; Emits Page Tables for 1:1 mapping of the addresses 0 - 0x100000000 (4GB) +; +; Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 64 + +%define ALIGN_TOP_TO_4K_FOR_PAGING + +%define PGTBLS_OFFSET(x) ((x) - TopLevelPageDirectory) +%define PGTBLS_ADDR(x) (ADDR_OF(TopLevelPageDirectory) + (x)) +%define PDP(offset) (ADDR_OF(TopLevelPageDirectory) + (offset) + PAGE_PDP_ATTR) +%define PTE_2MB(x) ((x << 21) + PAGE_2M_PDE_ATTR) + +TopLevelPageDirectory: + + ; + ; Top level Page Directory Pointers (1 * 512GB entry) + ; + DQ PDP(0x1000) + ; + ; Next level Page Directory Pointers (4 * 1GB entries => 4GB) + ; + TIMES 511 DQ 0 + + DQ PDP(0x2000) + TIMES 511 DQ 0 + DQ PDP(0x3000) + DQ PDP(0x4000) + DQ PDP(0x5000) + DQ PDP(0x6000) + + ; + ; Page Table Entries (2048 * 2MB entries => 4GB) + ; + TIMES 508 DQ 0 + +%assign i 0 +%rep 0x800 + DQ PTE_2MB(i) + %assign i i+1 +%endrep + +EndOfPageTables: diff --git a/OvmfPkg/ResetVector/X64/TdxMetadata.asm b/OvmfPkg/ResetVector/X64/TdxMetadata.asm new file mode 100644 index 000000000000..967773c3d48a --- /dev/null +++ b/OvmfPkg/ResetVector/X64/TdxMetadata.asm @@ -0,0 +1,98 @@ +;------------------------------------------------------------------------------ +; @file +; TDX metadata and padding the image size to 4k when page tables are in VTF0 +; +; Copyright (c) 2021, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 64 + +%define TDX_VIRTUAL_FIRMWARE + +; +; Pad the image size to 4k when page tables are in VTF0 +; +; If the VTF0 image has page tables built in, then we need to make +; sure the end of VTF0 is 4k above where the page tables end. +; +; This is required so the page tables will be 4k aligned when VTF0 is +; located just below 0x100000000 (4GB) in the firmware device. +; +%ifdef TDX_VIRTUAL_FIRMWARE + %ifdef ALIGN_TOP_TO_4K_FOR_PAGING + TIMES ((0x1000 - ($ - EndOfPageTables) - (fourGigabytes - TdxPaddingEnd) % 4096)) DB 0 + %endif +%endif + +TdxPaddingEnd: + +ALIGN 16 +TIMES (15 - ((TdxGuidedStructureEnd - TdxGuidedStructureStart + 15) % 16)) DB 0 + +TdxGuidedStructureStart: + +; +; TDVF meta data +; +TdxMetadataGuid: + DB 0xf3, 0xf9, 0xea, 0xe9, 0x8e, 0x16, 0xd5, 0x44 + DB 0xa8, 0xeb, 0x7f, 0x4d, 0x87, 0x38, 0xf6, 0xae + +_Descriptor: + DB 'T','D','V','F' ; Signature + DD TdxGuidedStructureEnd - _Descriptor ; Length + DD TDX_METADATA_VERSION ; Version + DD (TdxGuidedStructureEnd - _Descriptor - 16)/32 ; Number of sections + +_Bfv: + DD TDX_BFV_RAW_DATA_OFFSET + DD TDX_BFV_RAW_DATA_SIZE + DQ TDX_BFV_MEMORY_BASE + DQ TDX_BFV_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_BFV + DD TDX_METADATA_ATTRIBUTES_EXTENDMR + +_Cfv: + DD TDX_CFV_RAW_DATA_OFFSET + DD TDX_CFV_RAW_DATA_SIZE + DQ TDX_CFV_MEMORY_BASE + DQ TDX_CFV_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_CFV + DD 0 + +_Stack: + DD 0 + DD 0 + DQ TDX_STACK_MEMORY_BASE + DQ TDX_STACK_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TEMP_MEM + DD 0 + +_Heap: + DD 0 + DD 0 + DQ TDX_HEAP_MEMORY_BASE + DQ TDX_HEAP_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TEMP_MEM + DD 0 + +_TdHob: + DD 0 + DD 0 + DQ TDX_HOB_MEMORY_BASE + DQ TDX_HOB_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TD_HOB + DD 0 + +_MailBox: + DD 0 + DD 0 + DQ TDX_MAILBOX_MEMORY_BASE + DQ TDX_MAILBOX_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TEMP_MEM + DD 0 + +TdxGuidedStructureEnd: +ALIGN 16 diff --git a/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc b/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc index 7deee8b6ad5d..ef31f29edb31 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc +++ b/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc @@ -19,6 +19,35 @@ jmp %1 %+ OneTimerCallReturn %endmacro +%define PAGE_PRESENT 0x01 +%define PAGE_READ_WRITE 0x02 +%define PAGE_USER_SUPERVISOR 0x04 +%define PAGE_WRITE_THROUGH 0x08 +%define PAGE_CACHE_DISABLE 0x010 +%define PAGE_ACCESSED 0x020 +%define PAGE_DIRTY 0x040 +%define PAGE_PAT 0x080 +%define PAGE_GLOBAL 0x0100 +%define PAGE_2M_MBO 0x080 +%define PAGE_2M_PAT 0x01000 + +%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \ + PAGE_DIRTY + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \ + PAGE_ACCESSED + \ + PAGE_DIRTY + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PAGE_PDP_ATTR (PAGE_ACCESSED + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PT_ADDR(Base,Offset) ((Base) + (Offset)) + StartOfResetVectorCode: %define ADDR_OF_START_OF_RESET_CODE ADDR_OF(StartOfResetVectorCode) diff --git a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm index cbdadee166a0..34c741827687 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm +++ b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm @@ -40,3 +40,8 @@ EarlyInit16: OneTimeCallRet EarlyInit16 +; +; Protected Mode entry point for Early BSP +; +EarlyBspPmEntry: + jmp MainTd diff --git a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm index 0e79a3984b16..8b03f7087a77 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm +++ b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm @@ -119,6 +119,35 @@ LINEAR_CODE_SEL equ $-GDT_BASE DB 0 ; base 31:24 %ifdef ARCH_X64 +NULL_SEL1 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 +NULL_SEL2 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 +NULL_SEL3 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 +NULL_SEL4 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 + ; linear code (64-bit) segment descriptor LINEAR_CODE64_SEL equ $-GDT_BASE DW 0xffff ; limit 15:0 diff --git a/UefiCpuPkg/ResetVector/Vtf0/Main.asm b/UefiCpuPkg/ResetVector/Vtf0/Main.asm index 19d08482f831..f69c6d2dc6ef 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/Main.asm +++ b/UefiCpuPkg/ResetVector/Vtf0/Main.asm @@ -35,7 +35,61 @@ Main16: OneTimeCall TransitionFromReal16To32BitFlat BITS 32 + jmp SearchBfv +; +; Modified: EBX, ECX, EDX, EBP, EDI, ESP +; +; @param[in,out] RAX/EAX 0 +; @param[in] RFLAGS 2 +; @param[in] RCX [31:0] TDINITVP - Untrusted Configuration +; [63:32] 0 +; @param[in] RDX [31:0] VCPUID +; [63:32] 0 +; @param[in] RBX [6:0] CPU supported GPA width +; [7:7] 5 level page table support +; [63:8] 0 +; @param[in] RSI [31:0] VCPU_Index +; [63:32] 0 +; @param[in] RDI/EDI 0 +; @param[in] RBP/EBP 0 +; @param[in] R8 Same as RCX +; @param[out] RBP/EBP Address of Boot Firmware Volume (BFV) +; @param[out] DS Selector allowing flat access to all addresses +; @param[out] ES Selector allowing flat access to all addresses +; @param[out] FS Selector allowing flat access to all addresses +; @param[out] GS Selector allowing flat access to all addresses +; @param[out] SS Selector allowing flat access to all addresses +; +; @return None This routine jumps to SEC and does not return + +MainTd: +%ifdef ARCH_X64 + ; + ; Save EBX in EBP because EBX will be changed in ReloadFlat32 + ; + mov ebp, ebx + OneTimeCall ReloadFlat32 + + ; + ; It is of Tdx Guest + ; Save the Tdx info in TDX_WORK_AREA to pass to SEC phase + ; + mov byte[TDX_WORK_AREA], 1 + + ; check 5-level paging support + and ebp, 0x3f + cmp ebp, 52 + jl NotPageLevel5 + mov byte[TDX_WORK_AREA_PAGELEVEL5], 1 + +NotPageLevel5: + mov DWORD[TDX_WORK_AREA_INITVP], ecx + mov DWORD[TDX_WORK_AREA_INFO], ebp + +%endif + +SearchBfv: ; ; Search for the Boot Firmware Volume (BFV) ; From dd59e93f5247be31e3ad5ad72e89d98551581fca Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 13:37:59 +0800 Subject: [PATCH 10/54] OvmfPkg: Update SecEntry.nasm to support Tdx --- OvmfPkg/Include/TdxCommondefs.inc | 114 ++++++++++++++++++ OvmfPkg/Sec/SecMain.inf | 9 ++ OvmfPkg/Sec/X64/SecEntry.nasm | 187 ++++++++++++++++++++++++++++++ 3 files changed, 310 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..673e5c867b4d --- /dev/null +++ b/OvmfPkg/Include/TdxCommondefs.inc @@ -0,0 +1,114 @@ +;------------------------------------------------------------------------------ +; @file +; TDX Common defitions used by the APs in mailbox +; +; Copyright (c) 2021, Intel Corporation. All rights reserved.
+; 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 +AcceptPageArgsAcceptSize equ 810h +;AcceptPageArgsTallies equ 818h +AcceptPageArgsPageSize equ 818h +CpuArrivalOffset equ 900h +CpusExitingOffset equ 0a00h +TalliesOffset equ 0a08h + +SIZE_4KB equ 1000h +SIZE_2MB equ 200000h +SIZE_1GB equ 40000000h + +MpProtectedModeWakeupCommandNoop equ 0 +MpProtectedModeWakeupCommandWakeup equ 1 +MpProtectedModeWakeupCommandSleep equ 2 +MpProtectedModeWakeupCommandAcceptPages equ 3 + +MailboxApicIdInvalid equ 0xffffffff +MailboxApicidBroadcast equ 0xfffffffe + +%macro simple_spinlock 3 + mov edx, %1 + mov eax, 0 + mov ebx, 1 +%%testlock: + lock cmpxchg [edx], ebx + jnz %3 + mov eax, 0 + mov ebx, 1 + lock cmpxchg [edx+4], ebx + jnz %2 +%%firstone: + pause +%endmacro + +%macro simple_releaselock 3 +%2: + mov eax, 1 + mov edx, %1 + jmp %%testlock +%3: + pause + mov eax, 0 +%%testlock: + mov ebx, 0 + lock cmpxchg [edx], ebx + jnz %3 +%endmacro + +%define EFI_HOB_TYPE_HANDOFF 0x0001 +%define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002 +%define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003 +%define EFI_HOB_TYPE_GUID_EXTENSION 0x0004 +%define EFI_HOB_TYPE_FV 0x0005 +%define EFI_HOB_TYPE_CPU 0x0006 +%define EFI_HOB_TYPE_MEMORY_POOL 0x0007 +%define EFI_HOB_TYPE_FV2 0x0009 +%define EFI_HOB_TYPE_LOAD_PEIM_UNUSED 0x000A +%define EFI_HOB_TYPE_UEFI_CAPSULE 0x000B +%define EFI_HOB_TYPE_FV3 0x000C +%define EFI_HOB_TYPE_UNUSED 0xFFFE +%define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF + +%define EFI_RESOURCE_SYSTEM_MEMORY 0x00000000 +%define EFI_RESOURCE_MEMORY_MAPPED_IO 0x00000001 +%define EFI_RESOURCE_IO 0x00000002 +%define EFI_RESOURCE_FIRMWARE_DEVICE 0x00000003 +%define EFI_RESOURCE_MEMORY_MAPPED_IO_PORT 0x00000004 +%define EFI_RESOURCE_MEMORY_RESERVED 0x00000005 +%define EFI_RESOURCE_IO_RESERVED 0x00000006 +%define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000007 + +%define EFI_RESOURCE_ATTRIBUTE_PRESENT 0x00000001 +%define EFI_RESOURCE_ATTRIBUTE_INITIALIZED 0x00000002 +%define EFI_RESOURCE_ATTRIBUTE_TESTED 0x00000004 +%define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED 0x00000080 + +%define EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE 0x00000400 +%define EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE 0x00000800 +%define EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE 0x00001000 +%define EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE 0x00002000 + + +%define EFI_IO_ATTR (EFI_RESOURCE_ATTRIBUTE_PRESENT + \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED + \ + EFI_RESOURCE_ATTRIBUTE_TESTED + \ + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE) + +%define EFI_LOW_MEM_ATTR (EFI_RESOURCE_ATTRIBUTE_PRESENT + \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED + \ + EFI_RESOURCE_ATTRIBUTE_TESTED + \ + EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE + \ + EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE + \ + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE + \ + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE) + +%define TDCALL_TDINFO 0x1 +%define TDCALL_TDACCEPTPAGE 0x6 diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf index 7f78dcee2772..471d553fe104 100644 --- a/OvmfPkg/Sec/SecMain.inf +++ b/OvmfPkg/Sec/SecMain.inf @@ -71,5 +71,14 @@ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd gEfiMdeModulePkgTokenSpaceGuid.PcdInitValueInTempStack + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize + + [FeaturePcd] gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire diff --git a/OvmfPkg/Sec/X64/SecEntry.nasm b/OvmfPkg/Sec/X64/SecEntry.nasm index 1cc680a70716..8e8ae7f9daf5 100644 --- a/OvmfPkg/Sec/X64/SecEntry.nasm +++ b/OvmfPkg/Sec/X64/SecEntry.nasm @@ -10,12 +10,21 @@ ;------------------------------------------------------------------------------ #include +%include "TdxCommondefs.inc" DEFAULT REL SECTION .text extern ASM_PFX(SecCoreStartupWithStack) +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66, 0x0f, 0x01, 0xcc +%endif +%endmacro + ; ; SecCore Entry Point ; @@ -35,6 +44,31 @@ extern ASM_PFX(SecCoreStartupWithStack) global ASM_PFX(_ModuleEntryPoint) ASM_PFX(_ModuleEntryPoint): + ; + ; Td guest flag is stored in TDX_WORK_AREA which is in Mailbox[0x10,0x20] + ; + %define TDX_WORK_AREA (FixedPcdGet32 (PcdTdMailboxBase) + 0x10) + mov eax, TDX_WORK_AREA + cmp byte[eax], 1 + 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 +101,156 @@ 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 (PcdTdMailboxBase) + + ; + ; 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 + + ; Get PhysicalAddress/AcceptSize/PageSize + mov rcx, [rsp + AcceptPageArgsPhysicalStart] + mov rbx, [rsp + AcceptPageArgsAcceptSize] + + ; Accept Page size + mov rdx, [rsp + AcceptPageArgsPageSize] + cmp rdx, SIZE_4KB + je .set_4kb + cmp rdx, SIZE_2MB + je .set_2mb + cmp rdx, SIZE_1GB + je .set_1gb +.set_4kb + mov rdx, 0 + jmp .physical_address +.set_2mb + mov rdx, 1 + jmp .physical_address +.set_1gb + mov rdx, 2 + jmp .physical_address + +.physical_address + ; + ; PhysicalAddress += (CpuId * AcceptSize) + mov eax, ebp + mul ebx + add rcx, rax + +.do_accept_next_range: + + ; + ; Make sure we don't accept page beyond ending page + ; This could happen is AcceptSize crosses the end of region + ; + ;while (PhysicalAddress < PhysicalEnd) { + cmp rcx, [rsp + AcceptPageArgsPhysicalEnd ] + jge .do_finish_command + + ; + ; Save starting address for this region + ; + mov r11, rcx + + ; Size = MIN(AcceptSize, PhysicalEnd - PhysicalAddress); + mov rax, [rsp + AcceptPageArgsPhysicalEnd] + + sub rax, rcx + cmp rax, rbx + jge .do_accept_loop + mov rbx, rax + +.do_accept_loop: + + ; + ; Accept address in rcx + ; + mov rax, TDCALL_TDACCEPTPAGE + ;xor rdx, rdx + tdcall + + ; + ; Keep track of how many accepts per cpu + ; + inc dword[rsp + TalliesOffset + rbp * 4] + + ; + ; Reduce accept size by a page, and increment address + ; + mov r12, [rsp + AcceptPageArgsPageSize] + sub rbx, r12 + add rcx, 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 * acceptsize) + ; + mov rcx, r11 + mov eax, r8d + mov rbx, [rsp + AcceptPageArgsAcceptSize] + mul ebx + add rcx, rax + 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 $ From ca855b2ff71b5d480f5d132ebcefa69cdda01f35 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 13:45:29 +0800 Subject: [PATCH 11/54] UefiCpuPkg: Add NULL instance of VmTdExitLib --- UefiCpuPkg/Include/Library/VmTdExitLib.h | 70 +++++++++++++++++++ .../Library/VmTdExitLibNull/VmTdExitLibNull.c | 30 ++++++++ .../VmTdExitLibNull/VmTdExitLibNull.inf | 34 +++++++++ 3 files changed, 134 insertions(+) create mode 100644 UefiCpuPkg/Include/Library/VmTdExitLib.h create mode 100644 UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.c create mode 100644 UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf diff --git a/UefiCpuPkg/Include/Library/VmTdExitLib.h b/UefiCpuPkg/Include/Library/VmTdExitLib.h new file mode 100644 index 000000000000..0971bedf9715 --- /dev/null +++ b/UefiCpuPkg/Include/Library/VmTdExitLib.h @@ -0,0 +1,70 @@ +/** @file + Public header file for the VMTDEXIT Support library class. + + This library class defines some routines used when invoking the VMEXIT + instruction in support of VMX and TDX to handle #VE exceptions. + + Copyright (c) 2020, Intel Inc. All rights reserved.
+ Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __VM_TD_EXIT_LIB_H__ +#define __VM_TD_EXIT_LIB_H__ + +#include +#include +#include + +#define VE_EXCEPTION 20 + +EFI_STATUS +EFIAPI +TdCall( + IN UINT64 Leaf, + IN UINT64 Arg1, + IN UINT64 Arg2, + IN UINT64 Arg3, + IN VOID *Results + ); + +EFI_STATUS +EFIAPI +TdVmCall ( + IN UINT64 Leaf, + IN UINT64 Arg1, + IN UINT64 Arg2, + IN UINT64 Arg3, + IN UINT64 Arg4, + IN VOID *Results + ); + + +/** + 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/VmTdExitLibNull/VmTdExitLibNull.c b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.c new file mode 100644 index 000000000000..08868c24d230 --- /dev/null +++ b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.c @@ -0,0 +1,30 @@ +#include +#include + +/** + 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/VmTdExitLibNull/VmTdExitLibNull.inf b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf new file mode 100644 index 000000000000..e3357de2a6a9 --- /dev/null +++ b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf @@ -0,0 +1,34 @@ +## @file +# VMTDEXIT Support Library. +# +# Copyright (c) 2020, Intel Inc. All rights reserved.
+# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VmTdExitLibNull + FILE_GUID = b29eabb0-f9a3-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = VmTdExitLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 IA32 +# + +[Sources.common] + VmTdExitLibNull.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib From a00c2411340a1127e51eee754606b58bc80905eb Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 14:29:03 +0800 Subject: [PATCH 12/54] OvmfPkg: Add VmTdExitLib in OvmfPkg --- OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf | 41 ++ .../Library/VmTdExitLib/VmTdExitVeHandler.c | 505 ++++++++++++++++++ OvmfPkg/OvmfPkgIa32.dsc | 3 + OvmfPkg/OvmfPkgIa32X64.dsc | 3 + OvmfPkg/OvmfPkgX64.dsc | 1 + 5 files changed, 553 insertions(+) create mode 100644 OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf create mode 100644 OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c diff --git a/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf b/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf new file mode 100644 index 000000000000..f475d492c6bc --- /dev/null +++ b/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf @@ -0,0 +1,41 @@ +## @file +# VMTDEXIT Support Library. +# +# Copyright (c) 2020, Intel Inc. All rights reserved.
+# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VmTdExitLib + FILE_GUID = b29eabb0-f9a3-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = VmTdExitLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources.common] + VmTdExitVeHandler.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + TdxLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt + + diff --git a/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c b/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c new file mode 100644 index 000000000000..43e4384cdb6a --- /dev/null +++ b/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c @@ -0,0 +1,505 @@ +#include +#include +#include +#include +#include +#include +#include + +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 ((EFI_D_INFO,"TDX: #TD[EPT] instruction (%p):", Rip)); + for (i = 0; i < 15; i++){ + DEBUG ((EFI_D_INFO, "%02x:", Rip[i])); + } + DEBUG ((EFI_D_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/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index e5184ef06731..b337dd9c7032 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -238,6 +238,9 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf + TdxLib|MdePkg/Library/TdxLib/TdxLibNull.inf + TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf + VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf [LibraryClasses.common.SEC] TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 3bfec9196eba..33e2ef60490d 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -242,6 +242,9 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf + TdxLib|MdePkg/Library/TdxLib/TdxLibNull.inf + TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf + VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf [LibraryClasses.common.SEC] TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 36f53c93f915..7c6d9692a6bc 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -245,6 +245,7 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf + VmTdExitLib|OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf TdxLib|MdePkg/Library/TdxLib/TdxLib.inf TdxProbeLib|OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf From 24e5a51ef966db8fdb7459247251740ef53e7dcf Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 14:40:07 +0800 Subject: [PATCH 13/54] UefiCpuPkg: Update CpuExceptionHandlerLib to support Tdx This commit should be split into 2 paches later. --- OvmfPkg/OvmfPkg.dec | 2 ++ OvmfPkg/OvmfPkgX64.dsc | 7 +++++++ .../DxeCpuExceptionHandlerLib.inf | 1 + .../PeiCpuExceptionHandlerLib.inf | 2 ++ .../PeiDxeSmmCpuException.c | 18 ++++++++++++++++++ .../SecPeiCpuException.c | 19 +++++++++++++++++++ .../SecPeiCpuExceptionHandlerLib.inf | 1 + .../SmmCpuExceptionHandlerLib.inf | 1 + .../Xcode5SecPeiCpuExceptionHandlerLib.inf | 1 + 9 files changed, 52 insertions(+) diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 569c699e4a10..7edb9ba6f1a4 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -330,6 +330,8 @@ gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase|0|UINT32|0x51 gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset|0|UINT32|0x52 gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize|0|UINT32|0x53 + + gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x54 gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|0x0|UINT32|0x55 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x58 [PcdsDynamic, PcdsDynamicEx] diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 7c6d9692a6bc..72844aa8ec79 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -38,6 +38,7 @@ # # TDX flags # + DEFINE TDX_IGNORE_VE_HLT = FALSE DEFINE TDX_EMULATION_ENABLE = FALSE # Network definition # @@ -572,6 +573,12 @@ gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiReservedMemoryType|0x80 gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|0x100 gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData|0x100 + # + # TDX Pcds + # +!if $(TDX_IGNORE_VE_HLT) == TRUE + gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|TRUE +!endif # # Network Pcds diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf index 07b34c92a892..0305ed55e560 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf @@ -58,3 +58,4 @@ MemoryAllocationLib DebugLib VmgExitLib + VmTdExitLib diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf index feae7b3e06de..255e1e285006 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf @@ -53,6 +53,8 @@ MemoryAllocationLib SynchronizationLib VmgExitLib + VmTdExitLib + [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard # CONSUMES diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c index 892d349d4b37..0976a880825b 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c @@ -8,6 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include +#include #include "CpuExceptionCommon.h" /** @@ -45,6 +46,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..173047a6b494 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c @@ -8,6 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include +#include #include "CpuExceptionCommon.h" CONST UINTN mDoFarReturnFlag = 0; @@ -43,6 +44,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. // diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf index 967cb61ba6d9..94a9a18f1865 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf @@ -49,3 +49,4 @@ LocalApicLib PeCoffGetEntryPointLib VmgExitLib + VmTdExitLib diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf index 4cdb11c04ea0..fee14d027b43 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf @@ -52,4 +52,5 @@ PeCoffGetEntryPointLib DebugLib VmgExitLib + VmTdExitLib diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf index 743c2aa76684..336c9e23b453 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf @@ -54,3 +54,4 @@ LocalApicLib PeCoffGetEntryPointLib VmgExitLib + VmTdExitLib From bc8b5ee74ad28265ae70d35e6e08733858cdccf7 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 14:50:17 +0800 Subject: [PATCH 14/54] UefiCpuPkg: Update BaseXApicX2ApicLib to support Tdx --- OvmfPkg/OvmfPkgX64.dsc | 1 + .../Library/BaseXApicX2ApicLib/ApicLib.c | 30 ++++ .../BaseXApicX2ApicLib/ApicLibInternal.h | 8 + .../BaseXApicX2ApicLib/BaseXApicX2ApicLib.c | 140 +++++++++++++++++- .../BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf | 2 + .../BaseXApicX2ApicLibDxe.inf | 51 +++++++ .../BaseXApicX2ApicLibSec.inf | 51 +++++++ .../Library/BaseXApicX2ApicLib/DxeApicLib.c | 79 ++++++++++ .../Library/BaseXApicX2ApicLib/SecApicLib.c | 36 +++++ 9 files changed, 390 insertions(+), 8 deletions(-) create mode 100644 UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLib.c create mode 100644 UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLibInternal.h create mode 100644 UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf create mode 100644 UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf create mode 100644 UefiCpuPkg/Library/BaseXApicX2ApicLib/DxeApicLib.c create mode 100644 UefiCpuPkg/Library/BaseXApicX2ApicLib/SecApicLib.c diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 72844aa8ec79..2ccf9a49f681 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -214,6 +214,7 @@ VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf + LocalApicLib|UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf # # Network libraries diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLib.c new file mode 100644 index 000000000000..9ee9460b3c6b --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLib.c @@ -0,0 +1,30 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +//#include +#include "ApicLibInternal.h" +//#include + +BOOLEAN +EFIAPI +InitializeMsrAccess ( + VOID + ) +{ +// if(!mMsrAccessInitialized){ +// mMsrAccessInitialized = TRUE; +// } + return FALSE; +} diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLibInternal.h b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLibInternal.h new file mode 100644 index 000000000000..ef3913f79ef6 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLibInternal.h @@ -0,0 +1,8 @@ +#ifndef __APIC_LIB_INTERNAL_H__ +#define __APIC_LIB_INTERNAL_H__ + +BOOLEAN +EFIAPI +InitializeMsrAccess(VOID); + +#endif diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c index cdcbca046191..600cf69ac972 100644 --- a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c @@ -23,11 +23,132 @@ #include #include #include +#include +#include +#include "ApicLibInternal.h" // // Library internal functions // +STATIC +BOOLEAN +EFIAPI +UseMSR ( + IN UINT32 MsrIndex + ) +{ + 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 TRUE; + default: + break; + } + return FALSE; +} + +UINT64 +EFIAPI +ReadMsrReg64 ( + IN UINT32 MsrIndex + ) +{ + UINT64 Val; + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + 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; +} + +VOID +EFIAPI +WriteMsrReg64 ( + IN UINT32 MsrIndex, + IN UINT64 Val + ) +{ + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + Status = TdVmCall(TDVMCALL_WRMSR, (UINT64)MsrIndex, Val, 0, 0, 0); + if (Status != 0) { + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + } else { + AsmWriteMsr64(MsrIndex, Val); + } +} + +UINT32 +EFIAPI +ReadMsrReg32 ( + IN UINT32 MsrIndex + ) +{ + UINT64 Val; + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + Status = TdVmCall(TDVMCALL_RDMSR, (UINT64)MsrIndex, 0, 0, 0, &Val); + if (Status != 0) { + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + } else { + Val = AsmReadMsr32 (MsrIndex); + } + return (UINT32)(UINTN)Val; +} + +VOID +EFIAPI +WriteMsrReg32 ( + IN UINT32 MsrIndex, + IN UINT32 Val + ) +{ + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + Status = TdVmCall(TDVMCALL_WRMSR, (UINT64)MsrIndex, (UINT64)Val, 0, 0, 0); + if (Status != 0) { + DEBUG((DEBUG_ERROR, "WriteMsrReg32 returned failure. Status=0x%llx\n", Status)); + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + } else { + AsmWriteMsr32(MsrIndex, Val); + } +} + + /** Determine if the CPU supports the Local APIC Base Address MSR. @@ -77,7 +198,7 @@ GetLocalApicBaseAddress ( return PcdGet32 (PcdCpuLocalApicBaseAddress); } - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = ReadMsrReg64 (MSR_IA32_APIC_BASE); return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHi, 32)) + (((UINTN)ApicBaseMsr.Bits.ApicBase) << 12); @@ -108,12 +229,12 @@ SetLocalApicBaseAddress ( return; } - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = ReadMsrReg64 (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); + WriteMsrReg64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); } /** @@ -153,7 +274,7 @@ ReadLocalApicReg ( ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET); MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS; - return AsmReadMsr32 (MsrIndex); + return ReadMsrReg32 (MsrIndex); } } @@ -202,7 +323,7 @@ WriteLocalApicReg ( // Use memory fence here to force the serializing semantics to be consisent with xAPIC mode. // MemoryFence (); - AsmWriteMsr32 (MsrIndex, Value); + WriteMsrReg32 (MsrIndex, Value); } } @@ -309,7 +430,7 @@ GetApicMode ( return LOCAL_APIC_MODE_XAPIC; } - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = ReadMsrReg64 (MSR_IA32_APIC_BASE); // // Local APIC should have been enabled // @@ -350,13 +471,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 = ReadMsrReg64 (MSR_IA32_APIC_BASE); ApicBaseMsr.Bits.EXTD = 1; - AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + WriteMsrReg64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); break; default: ASSERT (FALSE); @@ -817,6 +939,8 @@ InitializeApicTimer ( LOCAL_APIC_LVT_TIMER LvtTimer; UINT32 Divisor; + InitializeMsrAccess(); + // // Ensure local APIC is in software-enabled state. // diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf index 1e2a4f8b790f..b49219fbfb6a 100644 --- a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf @@ -26,6 +26,7 @@ # [Sources] + ApicLib.c BaseXApicX2ApicLib.c [Packages] @@ -39,6 +40,7 @@ IoLib PcdLib UefiCpuLib + TdxProbeLib [Pcd] gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf new file mode 100644 index 000000000000..955709765e68 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf @@ -0,0 +1,51 @@ +## @file +# The Local Apic library supports x2APIC capable processors which have xAPIC and x2APIC modes. +# +# Note: Local APIC library assumes local APIC is enabled. It does not handle cases +# where local APIC is disabled. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicX2ApicLibDxe + MODULE_UNI_FILE = BaseXApicX2ApicLib.uni + FILE_GUID = 53DDFFC8-5812-44E2-8994-9542716B61CF + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = LocalApicLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + BaseXApicX2ApicLib.c + DxeApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + PcdLib + UefiCpuLib + TdxLib + TdxProbeLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress ## SOMETIMES_CONSUMES + +[Protocols] + gEfiCpuArchProtocolGuid ## CONSUMES + diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf new file mode 100644 index 000000000000..d14b9edebc78 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf @@ -0,0 +1,51 @@ +## @file +# The Local Apic library supports x2APIC capable processors which have xAPIC and x2APIC modes. +# +# Note: Local APIC library assumes local APIC is enabled. It does not handle cases +# where local APIC is disabled. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicX2ApicLibSec + MODULE_UNI_FILE = BaseXApicX2ApicLib.uni + FILE_GUID = BBDA39E1-1F30-4A77-8F96-DB3052BBC7BB + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = LocalApicLib|SEC + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + BaseXApicX2ApicLib.c + SecApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + PcdLib + UefiCpuLib + TdxLib + TdxProbeLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress ## SOMETIMES_CONSUMES + +[Protocols] + gEfiCpuArchProtocolGuid ## CONSUMES + diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/DxeApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/DxeApicLib.c new file mode 100644 index 000000000000..f679a21c66ed --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/DxeApicLib.c @@ -0,0 +1,79 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ApicLibInternal.h" + +static BOOLEAN mUseTdvmcall = FALSE; +static BOOLEAN mMsrAccessInitialized = FALSE; + +BOOLEAN +EFIAPI +InitializeMsrAccess ( + VOID + ) +{ + EFI_STATUS Status; + EFI_CPU_ARCH_PROTOCOL *Cpu; + BOOLEAN TdGuest; + + // + // We can't handle #ve until after the Cpu Arch Protocol has been installed + // When using the performance counters, it can cause apic access before this + // protocol is enabled. + // + if (!mMsrAccessInitialized) { + + TdGuest = ProbeTdGuest(); + + if(!TdGuest){ + // + // Non-TD guest + // + mMsrAccessInitialized = TRUE; + mUseTdvmcall = FALSE; + + }else{ + // + // TD guest + // We can't handle #ve until the Cpu Arch Protocol has been installed + // When using the performance counters, it can cause apic access before this + // protocol is enabled. + // + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu); + + if (!EFI_ERROR (Status)) { + mMsrAccessInitialized = TRUE; + mUseTdvmcall = TRUE; + } + } + + } + return mUseTdvmcall; +} diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/SecApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/SecApicLib.c new file mode 100644 index 000000000000..bc0acfd5d281 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/SecApicLib.c @@ -0,0 +1,36 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include "ApicLibInternal.h" + +static BOOLEAN mUseTdvmcall = FALSE; +static BOOLEAN mMsrAccessInitialized = FALSE; + +BOOLEAN +EFIAPI +InitializeMsrAccess ( + VOID + ) +{ + if (!mMsrAccessInitialized) { + mUseTdvmcall = ProbeTdGuest(); + + mMsrAccessInitialized = TRUE; + } + return mUseTdvmcall; +} From 0d8ca6e0c0c070b2898ecb4b4f37c92ce57ad9f3 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 15:05:32 +0800 Subject: [PATCH 15/54] MdePkg: Add SecHobLib --- MdePkg/Include/Library/HobLib.h | 99 +++ MdePkg/Library/SecHobLib/HobLib.c | 996 +++++++++++++++++++++++++ MdePkg/Library/SecHobLib/SecHobLib.inf | 50 ++ MdePkg/Library/SecHobLib/SecHobLib.uni | 16 + OvmfPkg/OvmfPkgX64.dsc | 2 +- 5 files changed, 1162 insertions(+), 1 deletion(-) create mode 100644 MdePkg/Library/SecHobLib/HobLib.c create mode 100644 MdePkg/Library/SecHobLib/SecHobLib.inf create mode 100644 MdePkg/Library/SecHobLib/SecHobLib.uni diff --git a/MdePkg/Include/Library/HobLib.h b/MdePkg/Include/Library/HobLib.h index 517365eae594..9e3801103ebb 100644 --- a/MdePkg/Include/Library/HobLib.h +++ b/MdePkg/Include/Library/HobLib.h @@ -473,6 +473,105 @@ BuildMemoryAllocationHob ( IN EFI_MEMORY_TYPE MemoryType ); + +VOID * +EFIAPI +PrePeiGetHobList( + VOID +); + +EFI_STATUS +EFIAPI +PrePeiSetHobList( + IN VOID *HobList + ); + +/** + Builds a Firmware Volume HOB and a resource descriptor hob + + This function builds a Firmware Volume HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + +**/ +VOID +EFIAPI +BuildFvHobs ( + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes, + IN EFI_RESOURCE_ATTRIBUTE_TYPE *ResourceAttribute OPTIONAL + ); + + +/** + Updates the pointer to the HOB list. + + @param HobList Hob list pointer to store + +**/ +EFI_STATUS +EFIAPI +SetHobList ( + IN VOID *HobList + ); + +EFI_HOB_HANDOFF_INFO_TABLE* +HobConstructor ( + IN VOID *EfiMemoryBegin, + IN UINTN EfiMemoryLength, + IN VOID *EfiFreeMemoryBottom, + IN VOID *EfiFreeMemoryTop + ); + +/** + This service enables PEIMs to create various types of HOBs. + + @param Type The type of HOB to be installed. + @param Length The length of the HOB to be added. + + @retval !NULL The HOB was successfully created. + @retval NULL There is no additional space for HOB creation. + +**/ +VOID * +CreateHob ( + IN UINT16 HobType, + IN UINT16 HobLenght + ); + + +/** + This service enables PEIMs to update the boot mode variable. + + @param BootMode The value of the boot mode to set. + + @retval EFI_SUCCESS The value was successfully updated + +**/ +EFI_STATUS +EFIAPI +SetBootMode ( + IN EFI_BOOT_MODE BootMode + ); + +/** + Update the Stack Hob if the stack has been moved + + @param BaseAddress The 64 bit physical address of the Stack. + @param Length The length of the stack in bytes. + +**/ +VOID +UpdateStackHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + + /** Returns the type of a HOB. diff --git a/MdePkg/Library/SecHobLib/HobLib.c b/MdePkg/Library/SecHobLib/HobLib.c new file mode 100644 index 000000000000..5923be7c2b20 --- /dev/null +++ b/MdePkg/Library/SecHobLib/HobLib.c @@ -0,0 +1,996 @@ +/** @file + Provide Hob Library functions for SEC phase. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include + +#include +#include +#include +#include + +VOID *mHobList = NULL; + +VOID * +EFIAPI +PrePeiGetHobList( + VOID +) +{ + return mHobList; +} + +EFI_STATUS +EFIAPI +PrePeiSetHobList( + IN VOID *HobList + ) +{ + mHobList = HobList; + return EFI_SUCCESS; +} + + +/** + Returns the pointer to the HOB list. + + This function returns the pointer to first HOB in the list. + For PEI phase, the PEI service GetHobList() can be used to retrieve the pointer + to the HOB list. For the DXE phase, the HOB list pointer can be retrieved through + the EFI System Table by looking up theHOB list GUID in the System Configuration Table. + Since the System Configuration Table does not exist that the time the DXE Core is + launched, the DXE Core uses a global variable from the DXE Core Entry Point Library + to manage the pointer to the HOB list. + + If the pointer to the HOB list is NULL, then ASSERT(). + + @return The pointer to the HOB list. + +**/ +VOID * +EFIAPI +GetHobList ( + VOID + ) +{ + VOID *HobList; + + HobList = PrePeiGetHobList(); + ASSERT (HobList != NULL); + + return HobList; +} + +EFI_STATUS +EFIAPI +SetHobList ( + IN VOID *HobList + ) +{ + EFI_STATUS Status; + + Status = PrePeiSetHobList(HobList); + + return Status; +} + +EFI_HOB_HANDOFF_INFO_TABLE* +HobConstructor ( + IN VOID *EfiMemoryBegin, + IN UINTN EfiMemoryLength, + IN VOID *EfiFreeMemoryBottom, + IN VOID *EfiFreeMemoryTop + ) +{ + EFI_HOB_HANDOFF_INFO_TABLE *Hob; + EFI_HOB_GENERIC_HEADER *HobEnd; + + Hob = EfiFreeMemoryBottom; + HobEnd = (EFI_HOB_GENERIC_HEADER *)(Hob+1); + + Hob->Header.HobType = EFI_HOB_TYPE_HANDOFF; + Hob->Header.HobLength = sizeof(EFI_HOB_HANDOFF_INFO_TABLE); + Hob->Header.Reserved = 0; + + HobEnd->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST; + HobEnd->HobLength = sizeof(EFI_HOB_GENERIC_HEADER); + HobEnd->Reserved = 0; + + Hob->Version = EFI_HOB_HANDOFF_TABLE_VERSION; + Hob->BootMode = BOOT_WITH_FULL_CONFIGURATION; + + Hob->EfiMemoryTop = (UINTN)EfiMemoryBegin + EfiMemoryLength; + Hob->EfiMemoryBottom = (UINTN)EfiMemoryBegin; + Hob->EfiFreeMemoryTop = (UINTN)EfiFreeMemoryTop; + Hob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS)(UINTN)(HobEnd+1); + Hob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS)(UINTN)HobEnd; + + return Hob; + +} + +VOID * +CreateHob ( + IN UINT16 HobType, + IN UINT16 HobLength + ) +{ + EFI_HOB_HANDOFF_INFO_TABLE *HandOffHob; + EFI_HOB_GENERIC_HEADER *HobEnd; + EFI_PHYSICAL_ADDRESS FreeMemory; + VOID *Hob; + + HandOffHob = GetHobList (); + + HobLength = (UINT16)((HobLength + 0x7) & (~0x7)); + + FreeMemory = HandOffHob->EfiFreeMemoryTop - HandOffHob->EfiFreeMemoryBottom; + + if (FreeMemory < HobLength) { + return NULL; + } + + Hob = (VOID*) (UINTN) HandOffHob->EfiEndOfHobList; + ((EFI_HOB_GENERIC_HEADER*) Hob)->HobType = HobType; + ((EFI_HOB_GENERIC_HEADER*) Hob)->HobLength = HobLength; + ((EFI_HOB_GENERIC_HEADER*) Hob)->Reserved = 0; + + HobEnd = (EFI_HOB_GENERIC_HEADER*) ((UINTN)Hob + HobLength); + HandOffHob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd; + + HobEnd->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST; + HobEnd->HobLength = sizeof(EFI_HOB_GENERIC_HEADER); + HobEnd->Reserved = 0; + HobEnd++; + HandOffHob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd; + + return Hob; +} + + +/** + Returns the next instance of a HOB type from the starting HOB. + + This function searches the first instance of a HOB type from the starting HOB pointer. + If there does not exist such HOB type from the starting HOB pointer, it will return NULL. + In contrast with macro GET_NEXT_HOB(), this function does not skip the starting HOB pointer + unconditionally: it returns HobStart back if HobStart itself meets the requirement; + caller is required to use GET_NEXT_HOB() if it wishes to skip current HobStart. + + If HobStart is NULL, then ASSERT(). + + @param Type The HOB type to return. + @param HobStart The starting HOB pointer to search from. + + @return The next instance of a HOB type from the starting HOB. + +**/ +VOID * +EFIAPI +GetNextHob ( + IN UINT16 Type, + 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)) { + if (Hob.Header->HobType == Type) { + return Hob.Raw; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + return NULL; +} + +/** + Returns the first instance of a HOB type among the whole HOB list. + + This function searches the first instance of a HOB type among the whole HOB list. + If there does not exist such HOB type in the HOB list, it will return NULL. + + If the pointer to the HOB list is NULL, then ASSERT(). + + @param Type The HOB type to return. + + @return The next instance of a HOB type from the starting HOB. + +**/ +VOID * +EFIAPI +GetFirstHob ( + IN UINT16 Type + ) +{ + VOID *HobList; + + HobList = GetHobList (); + return GetNextHob (Type, HobList); +} + +/** + Returns the next instance of the matched GUID HOB from the starting HOB. + + This function searches the first instance of a HOB from the starting HOB pointer. + Such HOB should satisfy two conditions: + its HOB type is EFI_HOB_TYPE_GUID_EXTENSION and its GUID Name equals to the input Guid. + If there does not exist such HOB from the starting HOB pointer, it will return NULL. + Caller is required to apply GET_GUID_HOB_DATA () and GET_GUID_HOB_DATA_SIZE () + to extract the data section and its size information, respectively. + In contrast with macro GET_NEXT_HOB(), this function does not skip the starting HOB pointer + unconditionally: it returns HobStart back if HobStart itself meets the requirement; + caller is required to use GET_NEXT_HOB() if it wishes to skip current HobStart. + + If Guid is NULL, then ASSERT(). + If HobStart is NULL, then ASSERT(). + + @param Guid The GUID to match with in the HOB list. + @param HobStart A pointer to a Guid. + + @return The next instance of the matched GUID HOB from the starting HOB. + +**/ +VOID * +EFIAPI +GetNextGuidHob ( + IN CONST EFI_GUID *Guid, + IN CONST VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS GuidHob; + + GuidHob.Raw = (UINT8 *) HobStart; + while ((GuidHob.Raw = GetNextHob (EFI_HOB_TYPE_GUID_EXTENSION, GuidHob.Raw)) != NULL) { + if (CompareGuid (Guid, &GuidHob.Guid->Name)) { + break; + } + GuidHob.Raw = GET_NEXT_HOB (GuidHob); + } + return GuidHob.Raw; +} + +/** + Returns the first instance of the matched GUID HOB among the whole HOB list. + + This function searches the first instance of a HOB among the whole HOB list. + Such HOB should satisfy two conditions: + its HOB type is EFI_HOB_TYPE_GUID_EXTENSION and its GUID Name equals to the input Guid. + If there does not exist such HOB from the starting HOB pointer, it will return NULL. + Caller is required to apply GET_GUID_HOB_DATA () and GET_GUID_HOB_DATA_SIZE () + to extract the data section and its size information, respectively. + + If the pointer to the HOB list is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + + @param Guid The GUID to match with in the HOB list. + + @return The first instance of the matched GUID HOB among the whole HOB list. + +**/ +VOID * +EFIAPI +GetFirstGuidHob ( + IN CONST EFI_GUID *Guid + ) +{ + VOID *HobList; + + HobList = GetHobList (); + return GetNextGuidHob (Guid, HobList); +} + +/** + Get the system boot mode from the HOB list. + + This function returns the system boot mode information from the + PHIT HOB in HOB list. + + If the pointer to the HOB list is NULL, then ASSERT(). + + @param VOID. + + @return The Boot Mode. + +**/ +EFI_BOOT_MODE +EFIAPI +GetBootModeHob ( + VOID + ) +{ + EFI_BOOT_MODE BootMode; + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + BootMode = Hob.HandoffInformationTable->BootMode; + + return BootMode; +} + +EFI_STATUS +EFIAPI +SetBootMode ( + IN EFI_BOOT_MODE BootMode + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + Hob.HandoffInformationTable->BootMode = BootMode; + return BootMode; +} + +/** + Builds a HOB for a loaded PE32 module. + + This function builds a HOB for a loaded PE32 module. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If ModuleName is NULL, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + + @param ModuleName The GUID File Name of the module. + @param MemoryAllocationModule The 64 bit physical address of the module. + @param ModuleLength The length of the module in bytes. + @param EntryPoint The 64 bit physical address of the module entry point. + +**/ +VOID +EFIAPI +BuildModuleHob ( + IN CONST EFI_GUID *ModuleName, + IN EFI_PHYSICAL_ADDRESS MemoryAllocationModule, + IN UINT64 ModuleLength, + IN EFI_PHYSICAL_ADDRESS EntryPoint + ) +{ + EFI_HOB_MEMORY_ALLOCATION_MODULE *Hob; + + ASSERT (((MemoryAllocationModule & (EFI_PAGE_SIZE - 1)) == 0) && + ((ModuleLength & (EFI_PAGE_SIZE - 1)) == 0)); + + Hob = CreateHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, (UINT16) sizeof (EFI_HOB_MEMORY_ALLOCATION_MODULE)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + CopyGuid (&(Hob->MemoryAllocationHeader.Name), &gEfiHobMemoryAllocModuleGuid); + Hob->MemoryAllocationHeader.MemoryBaseAddress = MemoryAllocationModule; + Hob->MemoryAllocationHeader.MemoryLength = ModuleLength; + Hob->MemoryAllocationHeader.MemoryType = EfiBootServicesCode; + + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->MemoryAllocationHeader.Reserved, sizeof (Hob->MemoryAllocationHeader.Reserved)); + + CopyGuid (&Hob->ModuleName, ModuleName); + Hob->EntryPoint = EntryPoint; +} + +/** + Builds a HOB that describes a chunk of system memory with Owner GUID. + + This function builds a HOB that describes a chunk of system memory. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param ResourceType The type of resource described by this HOB. + @param ResourceAttribute The resource attributes of the memory described by this HOB. + @param PhysicalStart The 64 bit physical address of memory described by this HOB. + @param NumberOfBytes The length of the memory described by this HOB in bytes. + @param OwnerGUID GUID for the owner of this resource. + +**/ +VOID +EFIAPI +BuildResourceDescriptorWithOwnerHob ( + IN EFI_RESOURCE_TYPE ResourceType, + IN EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute, + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes, + IN EFI_GUID *OwnerGUID + ) +{ + EFI_HOB_RESOURCE_DESCRIPTOR *Hob; + Hob = CreateHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (UINT16) sizeof (EFI_HOB_RESOURCE_DESCRIPTOR)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->ResourceType = ResourceType; + Hob->ResourceAttribute = ResourceAttribute; + Hob->PhysicalStart = PhysicalStart; + Hob->ResourceLength = NumberOfBytes; + + CopyGuid (&Hob->Owner, OwnerGUID); +} + +/** + Builds a HOB that describes a chunk of system memory. + + This function builds a HOB that describes a chunk of system memory. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param ResourceType The type of resource described by this HOB. + @param ResourceAttribute The resource attributes of the memory described by this HOB. + @param PhysicalStart The 64 bit physical address of memory described by this HOB. + @param NumberOfBytes The length of the memory described by this HOB in bytes. + +**/ +VOID +EFIAPI +BuildResourceDescriptorHob ( + IN EFI_RESOURCE_TYPE ResourceType, + IN EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute, + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes + ) +{ + EFI_HOB_RESOURCE_DESCRIPTOR *Hob; + + Hob = CreateHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (UINT16) sizeof (EFI_HOB_RESOURCE_DESCRIPTOR)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->ResourceType = ResourceType; + Hob->ResourceAttribute = ResourceAttribute; + Hob->PhysicalStart = PhysicalStart; + Hob->ResourceLength = NumberOfBytes; + ZeroMem (&(Hob->Owner), sizeof (EFI_GUID)); +} + +/** + Builds a customized HOB tagged with a GUID for identification and returns + the start address of GUID HOB data. + + This function builds a customized HOB tagged with a GUID for identification + and returns the start address of GUID HOB data so that caller can fill the customized data. + The HOB Header and Name field is already stripped. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If Guid is NULL, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + If DataLength > (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE)), then ASSERT(). + HobLength is UINT16 and multiples of 8 bytes, so the max HobLength is 0xFFF8. + + @param Guid The GUID to tag the customized HOB. + @param DataLength The size of the data payload for the GUID HOB. + + @retval NULL The GUID HOB could not be allocated. + @retval others The start address of GUID HOB data. + +**/ +VOID * +EFIAPI +BuildGuidHob ( + IN CONST EFI_GUID *Guid, + IN UINTN DataLength + ) +{ + EFI_HOB_GUID_TYPE *Hob; + + // + // Make sure Guid is valid + // + ASSERT (Guid != NULL); + + // + // Make sure that data length is not too long. + // + ASSERT (DataLength <= (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE))); + + Hob = CreateHob (EFI_HOB_TYPE_GUID_EXTENSION, (UINT16) (sizeof (EFI_HOB_GUID_TYPE) + DataLength)); + + if (Hob == NULL) { + ASSERT(FALSE); + return Hob; + } + CopyGuid (&Hob->Name, Guid); + return Hob + 1; +} + +/** + Builds a customized HOB tagged with a GUID for identification, copies the input data to the HOB + data field, and returns the start address of the GUID HOB data. + + This function builds a customized HOB tagged with a GUID for identification and copies the input + data to the HOB data field and returns the start address of the GUID HOB data. It can only be + invoked during PEI phase; for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + The HOB Header and Name field is already stripped. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If Guid is NULL, then ASSERT(). + If Data is NULL and DataLength > 0, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + If DataLength > (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE)), then ASSERT(). + HobLength is UINT16 and multiples of 8 bytes, so the max HobLength is 0xFFF8. + + @param Guid The GUID to tag the customized HOB. + @param Data The data to be copied into the data field of the GUID HOB. + @param DataLength The size of the data payload for the GUID HOB. + + @retval NULL The GUID HOB could not be allocated. + @retval others The start address of GUID HOB data. + +**/ +VOID * +EFIAPI +BuildGuidDataHob ( + IN CONST EFI_GUID *Guid, + IN VOID *Data, + IN UINTN DataLength + ) +{ + VOID *HobData; + + ASSERT (Data != NULL || DataLength == 0); + + HobData = BuildGuidHob (Guid, DataLength); + if (HobData == NULL) { + return HobData; + } + + return CopyMem (HobData, Data, DataLength); +} + +/** + Check FV alignment. + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + + @retval TRUE FvImage buffer is at its required alignment. + @retval FALSE FvImage buffer is not at its required alignment. + +**/ +BOOLEAN +InternalCheckFvAlignment ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + UINT32 FvAlignment; + + FvAlignment = 0; + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress; + + // + // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume + // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from + // its initial linked location and maintain its alignment. + // + if ((FwVolHeader->Attributes & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) { + // + // Get FvHeader alignment + // + FvAlignment = 1 << ((FwVolHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); + // + // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value. + // + if (FvAlignment < 8) { + FvAlignment = 8; + } + if ((UINTN)BaseAddress % FvAlignment != 0) { + // + // FvImage buffer is not at its required alignment. + // + DEBUG (( + DEBUG_ERROR, + "Unaligned FvImage found at 0x%lx:0x%lx, the required alignment is 0x%x\n", + BaseAddress, + Length, + FvAlignment + )); + return FALSE; + } + } + + return TRUE; +} + +VOID +EFIAPI +BuildFvHobs ( + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes, + IN EFI_RESOURCE_ATTRIBUTE_TYPE *ResourceAttribute OPTIONAL + ) +{ + + EFI_RESOURCE_ATTRIBUTE_TYPE Resource; + + BuildFvHob (PhysicalStart, NumberOfBytes); + + if (ResourceAttribute == NULL) { + Resource = (EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | + EFI_RESOURCE_ATTRIBUTE_TESTED | + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE); + } else { + Resource = *ResourceAttribute; + } + + BuildResourceDescriptorHob (EFI_RESOURCE_FIRMWARE_DEVICE, Resource, PhysicalStart, NumberOfBytes); +} + + +/** + Builds a Firmware Volume HOB. + + This function builds a Firmware Volume HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + If the FvImage buffer is not at its required alignment, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + +**/ +VOID +EFIAPI +BuildFvHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_HOB_FIRMWARE_VOLUME *Hob; + + if (!InternalCheckFvAlignment (BaseAddress, Length)) { + ASSERT (FALSE); + return; + } + + Hob = CreateHob (EFI_HOB_TYPE_FV, (UINT16) sizeof (EFI_HOB_FIRMWARE_VOLUME)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->BaseAddress = BaseAddress; + Hob->Length = Length; +} + +/** + Builds a EFI_HOB_TYPE_FV2 HOB. + + This function builds a EFI_HOB_TYPE_FV2 HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + If the FvImage buffer is not at its required alignment, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + @param FvName The name of the Firmware Volume. + @param FileName The name of the file. + +**/ +VOID +EFIAPI +BuildFv2Hob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN CONST EFI_GUID *FvName, + IN CONST EFI_GUID *FileName + ) +{ + EFI_HOB_FIRMWARE_VOLUME2 *Hob; + + if (!InternalCheckFvAlignment (BaseAddress, Length)) { + ASSERT (FALSE); + return; + } + + Hob = CreateHob (EFI_HOB_TYPE_FV2, (UINT16) sizeof (EFI_HOB_FIRMWARE_VOLUME2)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->BaseAddress = BaseAddress; + Hob->Length = Length; + CopyGuid (&Hob->FvName, FvName); + CopyGuid (&Hob->FileName, FileName); +} + +/** + Builds a EFI_HOB_TYPE_FV3 HOB. + + This function builds a EFI_HOB_TYPE_FV3 HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + If the FvImage buffer is not at its required alignment, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + @param AuthenticationStatus The authentication status. + @param ExtractedFv TRUE if the FV was extracted as a file within + another firmware volume. FALSE otherwise. + @param FvName The name of the Firmware Volume. + Valid only if IsExtractedFv is TRUE. + @param FileName The name of the file. + Valid only if IsExtractedFv is TRUE. + +**/ +VOID +EFIAPI +BuildFv3Hob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT32 AuthenticationStatus, + IN BOOLEAN ExtractedFv, + IN CONST EFI_GUID *FvName, OPTIONAL + IN CONST EFI_GUID *FileName OPTIONAL + ) +{ + EFI_HOB_FIRMWARE_VOLUME3 *Hob; + + if (!InternalCheckFvAlignment (BaseAddress, Length)) { + ASSERT (FALSE); + return; + } + + Hob = CreateHob (EFI_HOB_TYPE_FV3, (UINT16) sizeof (EFI_HOB_FIRMWARE_VOLUME3)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->BaseAddress = BaseAddress; + Hob->Length = Length; + Hob->AuthenticationStatus = AuthenticationStatus; + Hob->ExtractedFv = ExtractedFv; + if (ExtractedFv) { + CopyGuid (&Hob->FvName, FvName); + CopyGuid (&Hob->FileName, FileName); + } +} + +/** + Builds a Capsule Volume HOB. + + This function builds a Capsule Volume HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If the platform does not support Capsule Volume HOBs, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The base address of the Capsule Volume. + @param Length The size of the Capsule Volume in bytes. + +**/ +VOID +EFIAPI +BuildCvHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + ASSERT(FALSE); +} + +/** + Builds a HOB for the CPU. + + This function builds a HOB for the CPU. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param SizeOfMemorySpace The maximum physical memory addressability of the processor. + @param SizeOfIoSpace The maximum physical I/O addressability of the processor. + +**/ +VOID +EFIAPI +BuildCpuHob ( + IN UINT8 SizeOfMemorySpace, + IN UINT8 SizeOfIoSpace + ) +{ + EFI_HOB_CPU *Hob; + + Hob = CreateHob (EFI_HOB_TYPE_CPU, (UINT16) sizeof (EFI_HOB_CPU)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->SizeOfMemorySpace = SizeOfMemorySpace; + Hob->SizeOfIoSpace = SizeOfIoSpace; + + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->Reserved, sizeof (Hob->Reserved)); +} + +/** + Builds a HOB for the Stack. + + This function builds a HOB for the stack. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The 64 bit physical address of the Stack. + @param Length The length of the stack in bytes. + +**/ +VOID +EFIAPI +BuildStackHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_HOB_MEMORY_ALLOCATION_STACK *Hob; + + ASSERT (((BaseAddress & (EFI_PAGE_SIZE - 1)) == 0) && + ((Length & (EFI_PAGE_SIZE - 1)) == 0)); + + Hob = CreateHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, (UINT16) sizeof (EFI_HOB_MEMORY_ALLOCATION_STACK)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + CopyGuid (&(Hob->AllocDescriptor.Name), &gEfiHobMemoryAllocStackGuid); + Hob->AllocDescriptor.MemoryBaseAddress = BaseAddress; + Hob->AllocDescriptor.MemoryLength = Length; + Hob->AllocDescriptor.MemoryType = EfiBootServicesData; + + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->AllocDescriptor.Reserved, sizeof (Hob->AllocDescriptor.Reserved)); +} + +/** + Builds a HOB for the BSP store. + + This function builds a HOB for BSP store. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The 64 bit physical address of the BSP. + @param Length The length of the BSP store in bytes. + @param MemoryType The type of memory allocated by this HOB. + +**/ +VOID +EFIAPI +BuildBspStoreHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN EFI_MEMORY_TYPE MemoryType + ) +{ + ASSERT(FALSE); +} + +/** + Update the Stack Hob if the stack has been moved + + @param BaseAddress The 64 bit physical address of the Stack. + @param Length The length of the stack in bytes. + +**/ +VOID +UpdateStackHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) { + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &(Hob.MemoryAllocationStack->AllocDescriptor.Name))) { + // + // Build a new memory allocation HOB with old stack info with EfiConventionalMemory type + // to be reclaimed by DXE core. + // + BuildMemoryAllocationHob ( + Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress, + Hob.MemoryAllocationStack->AllocDescriptor.MemoryLength, + EfiConventionalMemory + ); + // + // Update the BSP Stack Hob to reflect the new stack info. + // + Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress = BaseAddress; + Hob.MemoryAllocationStack->AllocDescriptor.MemoryLength = Length; + break; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + +} + + +/** + Builds a HOB for the memory allocation. + + This function builds a HOB for the memory allocation. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The 64 bit physical address of the memory. + @param Length The length of the memory allocation in bytes. + @param MemoryType The type of memory allocated by this HOB. + +**/ +VOID +EFIAPI +BuildMemoryAllocationHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN EFI_MEMORY_TYPE MemoryType + ) +{ + EFI_HOB_MEMORY_ALLOCATION *Hob; + + ASSERT (((BaseAddress & (EFI_PAGE_SIZE - 1)) == 0) && + ((Length & (EFI_PAGE_SIZE - 1)) == 0)); + + Hob = CreateHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, (UINT16) sizeof (EFI_HOB_MEMORY_ALLOCATION)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + ZeroMem (&(Hob->AllocDescriptor.Name), sizeof (EFI_GUID)); + Hob->AllocDescriptor.MemoryBaseAddress = BaseAddress; + Hob->AllocDescriptor.MemoryLength = Length; + Hob->AllocDescriptor.MemoryType = MemoryType; + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->AllocDescriptor.Reserved, sizeof (Hob->AllocDescriptor.Reserved)); +} diff --git a/MdePkg/Library/SecHobLib/SecHobLib.inf b/MdePkg/Library/SecHobLib/SecHobLib.inf new file mode 100644 index 000000000000..95117bcf9ae4 --- /dev/null +++ b/MdePkg/Library/SecHobLib/SecHobLib.inf @@ -0,0 +1,50 @@ +## @file +# Instance of HOB Library using PEI Services. +# +# HOB Library implementation that uses PEI Services to retrieve the HOB List. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecHobLib + MODULE_UNI_FILE = SecHobLib.uni + FILE_GUID = 95A20151-9407-401A-89B3-5B4D9CDB932A + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = HobLib|SEC + + +# +# VALID_ARCHITECTURES = IA32 X64 EBC (EBC is for build only) +# + +[Sources] + HobLib.c + + +[Packages] + MdePkg/MdePkg.dec + #OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + +[Guids] + gEfiHobMemoryAllocStackGuid ## SOMETIMES_PRODUCES ## HOB # MemoryAllocation StackHob + gEfiHobMemoryAllocBspStoreGuid ## SOMETIMES_PRODUCES ## HOB # MemoryAllocation BspStoreHob + gEfiHobMemoryAllocModuleGuid ## SOMETIMES_PRODUCES ## HOB # MemoryAllocation ModuleHob + +# +# [Hob] +# MEMORY_ALLOCATION ## SOMETIMES_PRODUCES +# RESOURCE_DESCRIPTOR ## SOMETIMES_PRODUCES +# FIRMWARE_VOLUME ## SOMETIMES_PRODUCES +# + diff --git a/MdePkg/Library/SecHobLib/SecHobLib.uni b/MdePkg/Library/SecHobLib/SecHobLib.uni new file mode 100644 index 000000000000..071ac1c2cac9 --- /dev/null +++ b/MdePkg/Library/SecHobLib/SecHobLib.uni @@ -0,0 +1,16 @@ +// /** @file +// Instance of HOB Library using PEI Services. +// +// HOB Library implementation that retrieve the HOB List in SEC phase. +// +// Copyright (c) 2021, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Instance of HOB Library in SEC Phase" + +#string STR_MODULE_DESCRIPTION #language en-US "HOB Library implementation that retrieve the HOB List in SEC Phase." + diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 2ccf9a49f681..c9333ba9bd40 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -264,7 +264,7 @@ !if $(SOURCE_DEBUG_ENABLE) == TRUE DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.inf !endif - HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + HobLib|MdePkg/Library/SecHobLib/SecHobLib.inf PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLibIdt/PeiServicesTablePointerLibIdt.inf MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf From ada5e6348d83f01514c98e366e3937d4680f8859 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 15:34:36 +0800 Subject: [PATCH 16/54] MdePkg: Add SecMemoryAllocationLib --- MdePkg/Include/Library/MemoryAllocationLib.h | 22 + .../MemoryAllocationLib.c | 827 ++++++++++++++++++ .../SecMemoryAllocationLib.inf | 40 + .../SecMemoryAllocationLib.uni | 17 + OvmfPkg/OvmfPkgX64.dsc | 2 +- 5 files changed, 907 insertions(+), 1 deletion(-) create mode 100644 MdePkg/Library/SecMemoryAllocationLib/MemoryAllocationLib.c create mode 100644 MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf create mode 100644 MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.uni diff --git a/MdePkg/Include/Library/MemoryAllocationLib.h b/MdePkg/Include/Library/MemoryAllocationLib.h index 65a30cf146dd..8f0fae6c73e1 100644 --- a/MdePkg/Include/Library/MemoryAllocationLib.h +++ b/MdePkg/Include/Library/MemoryAllocationLib.h @@ -484,4 +484,26 @@ FreePool ( IN VOID *Buffer ); +/** + Allocates one or more 4KB pages of given type MemoryType. + + Allocates the number of 4KB pages of MemoryType 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 MemoryType Type of memory to use for this allocation. + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePagesWithMemoryType ( + IN UINTN MemoryType, + IN UINTN Pages + ); + + #endif diff --git a/MdePkg/Library/SecMemoryAllocationLib/MemoryAllocationLib.c b/MdePkg/Library/SecMemoryAllocationLib/MemoryAllocationLib.c new file mode 100644 index 000000000000..19245e3f6963 --- /dev/null +++ b/MdePkg/Library/SecMemoryAllocationLib/MemoryAllocationLib.c @@ -0,0 +1,827 @@ +/** @file + Support routines for memory allocation routines + based on PeiService for PEI phase drivers. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include + + +#include +#include +#include +#include +#include + +/** + Allocates one or more 4KB pages of type EfiBootServicesData. + + Allocates the number of 4KB pages of type EfiBootServicesData 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. + +**/ +VOID * +EFIAPI +AllocatePages ( + IN UINTN Pages + ) +{ + return AllocatePagesWithMemoryType(EfiBootServicesData, Pages); +} + +/** + Allocates one or more 4KB pages of type EfiRuntimeServicesData. + + Allocates the number of 4KB pages of type EfiRuntimeServicesData 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. + +**/ +VOID * +EFIAPI +AllocateRuntimePages ( + IN UINTN Pages + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates one or more 4KB pages of type EfiReservedMemoryType. + + Allocates the number of 4KB pages of type EfiReservedMemoryType 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. + +**/ +VOID * +EFIAPI +AllocateReservedPages ( + IN UINTN Pages + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Frees one or more 4KB pages that were previously allocated with one of the page allocation + functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer + must have been allocated on a previous call to the page allocation services of the Memory + Allocation Library. If it is not possible to free allocated pages, then this function will + perform no actions. + + If Buffer was not allocated with a page allocation function in the Memory Allocation Library, + then ASSERT(). + If Pages is zero, then ASSERT(). + + @param Buffer The pointer to the buffer of pages to free. + @param Pages The number of 4 KB pages to free. + +**/ +VOID +EFIAPI +FreePages ( + IN VOID *Buffer, + IN UINTN Pages + ) +{ + ASSERT(FALSE); +} + + +/** + Allocates one or more 4KB pages of type EfiBootServicesData at a specified alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiBootServicesData with an + alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is + returned. If there is not enough memory at the specified alignment remaining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT(). + + @param Pages The number of 4 KB pages to allocate. + @param Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateAlignedPages ( + IN UINTN Pages, + IN UINTN Alignment + ) +{ + VOID *Memory; + UINTN AlignmentMask; + + // + // Alignment must be a power of two or zero. + // + ASSERT ((Alignment & (Alignment - 1)) == 0); + + if (Pages == 0) { + return NULL; + } + // + // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow. + // + ASSERT (Pages <= (MAX_ADDRESS - EFI_SIZE_TO_PAGES (Alignment))); + // + // We would rather waste some memory to save PEI code size. + // + Memory = (VOID *)(UINTN)AllocatePages (Pages + EFI_SIZE_TO_PAGES (Alignment)); + if (Alignment == 0) { + AlignmentMask = Alignment; + } else { + AlignmentMask = Alignment - 1; + } + return (VOID *) (UINTN) (((UINTN) Memory + AlignmentMask) & ~AlignmentMask); +} + +/** + Allocates one or more 4KB pages of type EfiRuntimeServicesData at a specified alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiRuntimeServicesData with an + alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is + returned. If there is not enough memory at the specified alignment remaining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT(). + + @param Pages The number of 4 KB pages to allocate. + @param Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateAlignedRuntimePages ( + IN UINTN Pages, + IN UINTN Alignment + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates one or more 4KB pages of type EfiReservedMemoryType at a specified alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiReservedMemoryType with an + alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is + returned. If there is not enough memory at the specified alignment remaining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT(). + + @param Pages The number of 4 KB pages to allocate. + @param Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateAlignedReservedPages ( + IN UINTN Pages, + IN UINTN Alignment + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Frees one or more 4KB pages that were previously allocated with one of the aligned page + allocation functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer + must have been allocated on a previous call to the aligned page allocation services of the Memory + Allocation Library. If it is not possible to free allocated pages, then this function will + perform no actions. + + If Buffer was not allocated with an aligned page allocation function in the Memory Allocation + Library, then ASSERT(). + If Pages is zero, then ASSERT(). + + @param Buffer The pointer to the buffer of pages to free. + @param Pages The number of 4 KB pages to free. + +**/ +VOID +EFIAPI +FreeAlignedPages ( + IN VOID *Buffer, + IN UINTN Pages + ) +{ + ASSERT(FALSE); +} + +/** + Allocates a buffer of a certain pool type. + + Allocates the number bytes specified by AllocationSize of a certain pool type and returns a + pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param MemoryType The type of memory to allocate. + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocatePool ( + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates a buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData and returns a + pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +TdAllocatePool ( + IN UINTN AllocationSize + ) +{ + EFI_HOB_MEMORY_POOL *Hob; + + Hob = GetHobList (); + + // + // Verify that there is sufficient memory to satisfy the allocation + // + if (AllocationSize > 0x10000) { + // Please call AllocatePages for big allocations + return 0; + } else { + + Hob = (EFI_HOB_MEMORY_POOL *)CreateHob (EFI_HOB_TYPE_MEMORY_POOL, (UINT16)(sizeof (EFI_HOB_TYPE_MEMORY_POOL) + AllocationSize)); + return (VOID *)(Hob + 1); + } +} + +/** + Allocates a buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData and returns a + pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePool ( + IN UINTN AllocationSize + ) +{ + EFI_HOB_MEMORY_POOL *Hob; + + Hob = GetHobList (); + + // + // Verify that there is sufficient memory to satisfy the allocation + // + if (AllocationSize > 0x10000) { + // Please call AllocatePages for big allocations + return 0; + } else { + Hob = (EFI_HOB_MEMORY_POOL *)CreateHob (EFI_HOB_TYPE_MEMORY_POOL, (UINT16)(sizeof (EFI_HOB_TYPE_MEMORY_POOL) + AllocationSize)); + return (VOID *)(Hob + 1); + } +} + +/** + Allocates a buffer of type EfiRuntimeServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData and returns + a pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateRuntimePool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates a buffer of type EfiReservedMemoryType. + + Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType and returns + a pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateReservedPool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates and zeros a buffer of a certain pool type. + + Allocates the number bytes specified by AllocationSize of a certain pool type, clears the buffer + with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a valid + buffer of 0 size is returned. If there is not enough memory remaining to satisfy the request, + then NULL is returned. + + @param PoolType The type of memory to allocate. + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocateZeroPool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates and zeros a buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the + request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateZeroPool ( + IN UINTN AllocationSize + ) +{ + VOID *Memory; + + Memory = AllocatePool (AllocationSize); + if (Memory != NULL) { + Memory = ZeroMem (Memory, AllocationSize); + } + return Memory; +} + +/** + Allocates and zeros a buffer of type EfiRuntimeServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the + request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateRuntimeZeroPool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates and zeros a buffer of type EfiReservedMemoryType. + + Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the + request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateReservedZeroPool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Copies a buffer to an allocated buffer of a certain pool type. + + Allocates the number bytes specified by AllocationSize of a certain pool type, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param PoolType The type of pool to allocate. + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocateCopyPool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Copies a buffer to an allocated buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + VOID *Memory; + + ASSERT (Buffer != NULL); + ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1)); + + Memory = AllocatePool (AllocationSize); + if (Memory != NULL) { + Memory = CopyMem (Memory, Buffer, AllocationSize); + } + return Memory; +} + +/** + Copies a buffer to an allocated buffer of type EfiRuntimeServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateRuntimeCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Copies a buffer to an allocated buffer of type EfiReservedMemoryType. + + Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateReservedCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of a specified memory type. + + Allocates and zeros the number bytes specified by NewSize from memory of the type + specified by PoolType. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param PoolType The type of pool to allocate. + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an + optional parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalReallocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of type EfiBootServicesData. + + Allocates and zeros the number bytes specified by NewSize from memory of type + EfiBootServicesData. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an optional + parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +ReallocatePool ( + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of type EfiRuntimeServicesData. + + Allocates and zeros the number bytes specified by NewSize from memory of type + EfiRuntimeServicesData. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an optional + parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +ReallocateRuntimePool ( + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of type EfiReservedMemoryType. + + Allocates and zeros the number bytes specified by NewSize from memory of type + EfiReservedMemoryType. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an + optional parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +ReallocateReservedPool ( + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Frees a buffer that was previously allocated with one of the pool allocation functions in the + Memory Allocation Library. + + Frees the buffer specified by Buffer. Buffer must have been allocated on a previous call to the + pool allocation services of the Memory Allocation Library. If it is not possible to free pool + resources, then this function will perform no actions. + + If Buffer was not allocated with a pool allocation function in the Memory Allocation Library, + then ASSERT(). + + @param Buffer The pointer to the buffer to free. + +**/ +VOID +EFIAPI +FreePool ( + IN VOID *Buffer + ) +{ + // + // PEI phase does not support to free pool, so leave it as NOP. + // +} + +/** + Allocates one or more 4KB pages of given type MemoryType. + + Allocates the number of 4KB pages of MemoryType 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. + @param MemoryType Type of memory to use for this allocation. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePagesWithMemoryType ( + IN UINTN MemoryType, + IN UINTN Pages + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS Offset; + + Hob.Raw = GetHobList (); + + // Check to see if on 4k boundary + Offset = Hob.HandoffInformationTable->EfiFreeMemoryTop & 0xFFF; + if (Offset != 0) { + // If not aligned, make the allocation aligned. + Hob.HandoffInformationTable->EfiFreeMemoryTop -= Offset; + } + + // + // Verify that there is sufficient memory to satisfy the allocation + // + if (Hob.HandoffInformationTable->EfiFreeMemoryTop - ((Pages * EFI_PAGE_SIZE) + sizeof (EFI_HOB_MEMORY_ALLOCATION)) < Hob.HandoffInformationTable->EfiFreeMemoryBottom) { + return 0; + } else { + // + // Update the PHIT to reflect the memory usage + // + Hob.HandoffInformationTable->EfiFreeMemoryTop -= Pages * EFI_PAGE_SIZE; + + // This routine used to create a memory allocation HOB a la PEI, but that's not + // necessary for us. + + // + // Create a memory allocation HOB. + // + BuildMemoryAllocationHob ( + Hob.HandoffInformationTable->EfiFreeMemoryTop, + Pages * EFI_PAGE_SIZE, + (EFI_MEMORY_TYPE)MemoryType + ); + return (VOID *)(UINTN)Hob.HandoffInformationTable->EfiFreeMemoryTop; + } +} + + diff --git a/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf new file mode 100644 index 000000000000..243ef1de5303 --- /dev/null +++ b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf @@ -0,0 +1,40 @@ +## @file +# Instance of Memory Allocation Library using PEI Services. +# +# Memory Allocation Library that uses PEI Services to allocate memory. +# Free operations are ignored. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecMemoryAllocationLib + MODULE_UNI_FILE = SecMemoryAllocationLib.uni + FILE_GUID = 9435C171-D3F0-47AB-B45C-E51D5411EDE2 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemoryAllocationLib|SEC + + +# +# VALID_ARCHITECTURES = IA32 X64 EBC (EBC is for build only) +# + +[Sources] + MemoryAllocationLib.c + +[Packages] + MdePkg/MdePkg.dec + #OvmfPkg/OvmfPkg.dec + + +[LibraryClasses] + DebugLib + BaseMemoryLib + #PeiServicesLib + HobLib \ No newline at end of file diff --git a/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.uni b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.uni new file mode 100644 index 000000000000..f492b6240a88 --- /dev/null +++ b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.uni @@ -0,0 +1,17 @@ +// /** @file +// Instance of Memory Allocation Library using PEI Services. +// +// Memory Allocation Library that uses PEI Services to allocate memory. +// Free operations are ignored. +// +// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Instance of Memory Allocation Library using PEI Services" + +#string STR_MODULE_DESCRIPTION #language en-US "Memory Allocation Library that uses PEI Services to allocate memory. Free operations are ignored." + diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index c9333ba9bd40..68abb54179f5 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -267,7 +267,7 @@ HobLib|MdePkg/Library/SecHobLib/SecHobLib.inf PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLibIdt/PeiServicesTablePointerLibIdt.inf - MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + MemoryAllocationLib|MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf !if $(TOOL_CHAIN_TAG) == "XCODE5" CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf !else From d4ecf584acebc589fcc1ad5995897342e98f47f9 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 15:50:07 +0800 Subject: [PATCH 17/54] OvmfPkg: Add TdvfPlatformLib --- OvmfPkg/Include/Library/TdvfPlatformLib.h | 27 ++ .../Library/TdvfPlatformLibQemu/Platform.c | 270 ++++++++++++++++++ .../TdvfPlatformLibQemuSec.inf | 49 ++++ OvmfPkg/OvmfPkgX64.dsc | 1 + 4 files changed, 347 insertions(+) create mode 100644 OvmfPkg/Include/Library/TdvfPlatformLib.h create mode 100644 OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c create mode 100644 OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf diff --git a/OvmfPkg/Include/Library/TdvfPlatformLib.h b/OvmfPkg/Include/Library/TdvfPlatformLib.h new file mode 100644 index 000000000000..c97b005fba12 --- /dev/null +++ b/OvmfPkg/Include/Library/TdvfPlatformLib.h @@ -0,0 +1,27 @@ +#ifndef __TDVF_PLATFORM_LIB_H__ +#define __TDVF_PLATFORM_LIB_H__ + +#include +#include +#include +#include +#include + +#define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000 + +typedef struct { + /// + EFI_HOB_GUID_TYPE GuidHeader; + UINT64 RelocatedMailBox; + UINT16 HostBridgePciDevId; + BOOLEAN SetNxForStack; + UINT8 SystemStates[6]; +} EFI_HOB_PLATFORM_INFO; + +VOID +EFIAPI +TdvfPlatformInitialize ( + IN OUT EFI_HOB_PLATFORM_INFO * + ); + +#endif diff --git a/OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c b/OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c new file mode 100644 index 000000000000..1c9aa92f9319 --- /dev/null +++ b/OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c @@ -0,0 +1,270 @@ +/**@file + Platform PEI driver + + Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2011, Andrei Warkentin + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// The package level header files this module uses +// +#include + +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +// +// Host Bridge DID Address +// +#define HOSTBRIDGE_DID \ + PCI_LIB_ADDRESS (0, 0, 0, PCI_DEVICE_ID_OFFSET) + +// +// Values we program into the PM base address registers +// +#define PIIX4_PMBA_VALUE 0xB000 +#define ICH9_PMBASE_VALUE 0x0600 + +EFI_STATUS +GetNamedFwCfgBoolean ( + IN CHAR8 *FwCfgFileName, + OUT BOOLEAN *Setting + ) +{ + EFI_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINT8 Value[3]; + + Status = QemuFwCfgFindFile (FwCfgFileName, &FwCfgItem, &FwCfgSize); + if (EFI_ERROR (Status)) { + return Status; + } + if (FwCfgSize > sizeof Value) { + return EFI_BAD_BUFFER_SIZE; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (FwCfgSize, Value); + + if ((FwCfgSize == 1) || + (FwCfgSize == 2 && Value[1] == '\n') || + (FwCfgSize == 3 && Value[1] == '\r' && Value[2] == '\n')) { + switch (Value[0]) { + case '0': + case 'n': + case 'N': + *Setting = FALSE; + return EFI_SUCCESS; + + case '1': + case 'y': + case 'Y': + *Setting = TRUE; + return EFI_SUCCESS; + + default: + break; + } + } + return EFI_PROTOCOL_ERROR; +} + +VOID +PciExBarInitialization ( + VOID + ) +{ + union { + UINT64 Uint64; + UINT32 Uint32[2]; + } PciExBarBase; + + // + // We only support the 256MB size for the MMCONFIG area: + // 256 buses * 32 devices * 8 functions * 4096 bytes config space. + // + // The masks used below enforce the Q35 requirements that the MMCONFIG area + // be (a) correctly aligned -- here at 256 MB --, (b) located under 64 GB. + // + // Note that (b) also ensures that the minimum address width we have + // determined in AddressWidthInitialization(), i.e., 36 bits, will suffice + // for DXE's page tables to cover the MMCONFIG area. + // + PciExBarBase.Uint64 = FixedPcdGet64 (PcdPciExpressBaseAddress); + ASSERT ((PciExBarBase.Uint32[1] & MCH_PCIEXBAR_HIGHMASK) == 0); + ASSERT ((PciExBarBase.Uint32[0] & MCH_PCIEXBAR_LOWMASK) == 0); + + // + // Clear the PCIEXBAREN bit first, before programming the high register. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), 0); + + // + // Program the high register. Then program the low register, setting the + // MMCONFIG area size and enabling decoding at once. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_HIGH), PciExBarBase.Uint32[1]); + PciWrite32 ( + DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), + PciExBarBase.Uint32[0] | MCH_PCIEXBAR_BUS_FF | MCH_PCIEXBAR_EN + ); +} + +VOID +MiscInitialization ( + EFI_HOB_PLATFORM_INFO *PlatformInfoHob + ) +{ + RETURN_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINTN PmCmd; + UINTN Pmba; + UINT32 PmbaAndVal; + UINT32 PmbaOrVal; + UINTN AcpiCtlReg; + UINT8 AcpiEnBit; + // + // Disable A20 Mask + // + IoOr8 (0x92, BIT1); + + // + // Determine platform type and save Host Bridge DID to PCD + // + switch (PlatformInfoHob->HostBridgePciDevId) { + case INTEL_82441_DEVICE_ID: + PmCmd = POWER_MGMT_REGISTER_PIIX4 (PCI_COMMAND_OFFSET); + Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA); + PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK; + PmbaOrVal = PIIX4_PMBA_VALUE; + AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC); + AcpiEnBit = PIIX4_PMREGMISC_PMIOSE; + break; + case INTEL_Q35_MCH_DEVICE_ID: + PmCmd = POWER_MGMT_REGISTER_Q35 (PCI_COMMAND_OFFSET); + Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE); + PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK; + PmbaOrVal = ICH9_PMBASE_VALUE; + AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL); + AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN; + break; + default: + DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n", + __FUNCTION__, PlatformInfoHob->HostBridgePciDevId)); + ASSERT (FALSE); + return; + } + // + // If the appropriate IOspace enable bit is set, assume the ACPI PMBA + // has been configured and skip the setup here. + // This matches the logic in AcpiTimerLibConstructor (). + // + if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) { + // + // The PEI phase should be exited with fully accessibe ACPI PM IO space: + // 1. set PMBA + // + PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal); + + // + // 2. set PCICMD/IOSE + // + PciOr8 (PmCmd, EFI_PCI_COMMAND_IO_SPACE); + + // + // 3. set ACPI PM IO enable bit (PMREGMISC:PMIOSE or ACPI_CNTL:ACPI_EN) + // + PciOr8 (AcpiCtlReg, AcpiEnBit); + } + + if (PlatformInfoHob->HostBridgePciDevId == INTEL_Q35_MCH_DEVICE_ID) { + // + // Set Root Complex Register Block BAR + // + PciWrite32 ( + POWER_MGMT_REGISTER_Q35 (ICH9_RCBA), + ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN + ); + + // + // Set PCI Express Register Range Base Address + // + PciExBarInitialization (); + } + + // + // 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); +} + +/** + Perform Platform initialization. +**/ +VOID +EFIAPI +TdvfPlatformInitialize ( + EFI_HOB_PLATFORM_INFO *PlatformInfoHob +) +{ + DEBUG ((DEBUG_INFO, "Qemu Platform Loaded\n")); + + PlatformInfoHob->HostBridgePciDevId = PciRead16 (HOSTBRIDGE_DID); + + if (PlatformInfoHob->HostBridgePciDevId == INTEL_Q35_MCH_DEVICE_ID) { + BuildResourceDescriptorHob ( + EFI_RESOURCE_IO, + EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED, + 0x6000, + 0xa000 + ); + } else { + BuildResourceDescriptorHob ( + EFI_RESOURCE_IO, + EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED, + 0xc000, + 0x4000 + ); + } + + MiscInitialization (PlatformInfoHob); + + GetNamedFwCfgBoolean ("opt/ovmf/PcdSetNxForStack", &PlatformInfoHob->SetNxForStack); +} diff --git a/OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf b/OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf new file mode 100644 index 000000000000..dd60d4d79da2 --- /dev/null +++ b/OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf @@ -0,0 +1,49 @@ +## @file +# +# Tdvf Platform Lib for the QEMU VMM +# +# Copyright (C) 2013, Red Hat, Inc. +# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdvfPlatformLibQemuSec + FILE_GUID = 44cabe70-fcfb-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdvfPlatformLib|SEC + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + Platform.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + MdeModulePkg/MdeModulePkg.dec + OvmfPkg/OvmfPkg.dec + +[Guids] + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + IoLib + PcdLib + HobLib + PciLib + QemuFwCfgLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 68abb54179f5..5ac6f44a88e5 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -275,6 +275,7 @@ !endif VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf + TdvfPlatformLib|OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf [LibraryClasses.common.PEI_CORE] HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf From 942d5ab4503554fffa1006cd6154183593e0acd2 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 15:55:46 +0800 Subject: [PATCH 18/54] OvmfPkg: Add PrePiLibTdx --- OvmfPkg/Include/Library/PrePiLibTdx.h | 241 +++++ OvmfPkg/Library/PrePiLibTdx/FwVol.c | 978 ++++++++++++++++++++ OvmfPkg/Library/PrePiLibTdx/PrePi.h | 29 + OvmfPkg/Library/PrePiLibTdx/PrePiLib.c | 251 +++++ OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf | 51 + OvmfPkg/OvmfPkgX64.dsc | 1 + 6 files changed, 1551 insertions(+) create mode 100644 OvmfPkg/Include/Library/PrePiLibTdx.h create mode 100644 OvmfPkg/Library/PrePiLibTdx/FwVol.c create mode 100644 OvmfPkg/Library/PrePiLibTdx/PrePi.h create mode 100644 OvmfPkg/Library/PrePiLibTdx/PrePiLib.c create mode 100644 OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf diff --git a/OvmfPkg/Include/Library/PrePiLibTdx.h b/OvmfPkg/Include/Library/PrePiLibTdx.h new file mode 100644 index 000000000000..405ee4e5c628 --- /dev/null +++ b/OvmfPkg/Include/Library/PrePiLibTdx.h @@ -0,0 +1,241 @@ +/** @file + Library that helps implement monolithic PEI. (SEC goes to DXE) + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __PRE_PI_LIB_TDX_H__ +#define __PRE_PI_LIB_TDX_H__ + +/** + This service enables discovery of additional firmware volumes. + + @param Instance This instance of the firmware volume to find. The value 0 is the + Boot Firmware Volume (BFV). + @param FwVolHeader Pointer to the firmware volume header of the volume to return. + + @retval EFI_SUCCESS The volume was found. + @retval EFI_NOT_FOUND The volume was not found. + @retval EFI_INVALID_PARAMETER FwVolHeader is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextVolume ( + IN UINTN Instance, + IN OUT EFI_PEI_FV_HANDLE *VolumeHandle + ); + + +/** + This service enables discovery of additional firmware files. + + @param SearchType A filter to find files only of this type. + @param FwVolHeader Pointer to the firmware volume header of the volume to search. + This parameter must point to a valid FFS volume. + @param FileHeader Pointer to the current file from which to begin searching. + + @retval EFI_SUCCESS The file was found. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_NOT_FOUND The header checksum was not zero. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextFile ( + IN EFI_FV_FILETYPE SearchType, + IN EFI_PEI_FV_HANDLE VolumeHandle, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + + +/** + This service enables discovery sections of a given type within a valid FFS file. + + @param SearchType The value of the section type to find. + @param FfsFileHeader A pointer to the file header that contains the set of sections to + be searched. + @param SectionData A pointer to the discovered section, if successful. + + @retval EFI_SUCCESS The section was found. + @retval EFI_NOT_FOUND The section was not found. + +**/ +EFI_STATUS +EFIAPI +FfsFindSectionData ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT VOID **SectionData + ); + + +/** + Find a file in the volume by name + + @param FileName A pointer to the name of the file to + find within the firmware volume. + + @param VolumeHandle The firmware volume to search FileHandle + Upon exit, points to the found file's + handle or NULL if it could not be found. + + @retval EFI_SUCCESS File was found. + + @retval EFI_NOT_FOUND File was not found. + + @retval EFI_INVALID_PARAMETER VolumeHandle or FileHandle or + FileName was NULL. + +**/ +EFI_STATUS +EFIAPI +FfsFindFileByName ( + IN CONST EFI_GUID *FileName, + IN CONST EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + + +/** + Get information about the file by name. + + @param FileHandle Handle of the file. + + @param FileInfo Upon exit, points to the file's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetFileInfo ( + IN CONST EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_FV_FILE_INFO *FileInfo + ); + + +/** + Get Information about the volume by name + + @param VolumeHandle Handle of the volume. + + @param VolumeInfo Upon exit, points to the volume's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetVolumeInfo ( + IN EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_FV_INFO *VolumeInfo + ); + + + +/** + Get Fv image from the FV type file, then add FV & FV2 Hob. + + @param FileHandle File handle of a Fv type file. + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully to process it. + +**/ +EFI_STATUS +EFIAPI +FfsProcessFvFile ( + IN EFI_PEI_FILE_HANDLE FvFileHandle + ); + + +/** + Search through every FV until you find a file of type FileType + + @param FileType File handle of a Fv type file. + @param Volumehandle On success Volume Handle of the match + @param FileHandle On success File Handle of the match + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully found FileType + +**/ +EFI_STATUS +EFIAPI +FfsAnyFvFindFirstFile ( + IN EFI_FV_FILETYPE FileType, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + +EFI_STATUS +EFIAPI +FfsAnyFvFindFileByName ( + IN CONST EFI_GUID *Name, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + + +/** + Get Fv image from the FV type file, then add FV & FV2 Hob. + + @param FileHandle File handle of a Fv type file. + + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully to process it. + +**/ +EFI_STATUS +EFIAPI +FfsProcessFvFile ( + IN EFI_PEI_FILE_HANDLE FvFileHandle + ); + +EFI_STATUS +EFIAPI +LoadPeCoffImage ( + IN VOID *PeCoffImage, + OUT EFI_PHYSICAL_ADDRESS *ImageAddress, + OUT UINT64 *ImageSize, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint + ); + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFfsFile ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN UINTN StackSize + ); + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFv ( + IN UINTN *FvInstance, OPTIONAL + IN UINTN StackSize + ); + +EFI_STATUS +EFIAPI +DecompressFirstFv ( + VOID + ); + +#endif diff --git a/OvmfPkg/Library/PrePiLibTdx/FwVol.c b/OvmfPkg/Library/PrePiLibTdx/FwVol.c new file mode 100644 index 000000000000..474d48306464 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/FwVol.c @@ -0,0 +1,978 @@ +/** @file + Implementation of the 6 PEI Ffs (FV) APIs in library form. + + This code only knows about a FV if it has a EFI_HOB_TYPE_FV entry in the HOB list + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \ + (ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1)) + +#define MAX_FV_IMAGES 8 +/** + Returns the highest bit set of the State field + + @param ErasePolarity Erase Polarity as defined by EFI_FVB2_ERASE_POLARITY + in the Attributes field. + @param FfsHeader Pointer to FFS File Header + + + @retval the highest bit in the State field + +**/ +STATIC +EFI_FFS_FILE_STATE +GetFileState( + IN UINT8 ErasePolarity, + IN EFI_FFS_FILE_HEADER *FfsHeader + ) +{ + EFI_FFS_FILE_STATE FileState; + EFI_FFS_FILE_STATE HighestBit; + + FileState = FfsHeader->State; + + if (ErasePolarity != 0) { + FileState = (EFI_FFS_FILE_STATE)~FileState; + } + + HighestBit = 0x80; + while (HighestBit != 0 && (HighestBit & FileState) == 0) { + HighestBit >>= 1; + } + + return HighestBit; +} + + +/** + Calculates the checksum of the header of a file. + The header is a zero byte checksum, so zero means header is good + + @param FfsHeader Pointer to FFS File Header + + @retval Checksum of the header + +**/ +STATIC +UINT8 +CalculateHeaderChecksum ( + IN EFI_FFS_FILE_HEADER *FileHeader + ) +{ + UINT8 *Ptr; + UINTN Index; + UINT8 Sum; + + Sum = 0; + Ptr = (UINT8 *)FileHeader; + + for (Index = 0; Index < sizeof(EFI_FFS_FILE_HEADER) - 3; Index += 4) { + Sum = (UINT8)(Sum + Ptr[Index]); + Sum = (UINT8)(Sum + Ptr[Index+1]); + Sum = (UINT8)(Sum + Ptr[Index+2]); + Sum = (UINT8)(Sum + Ptr[Index+3]); + } + + for (; Index < sizeof(EFI_FFS_FILE_HEADER); Index++) { + Sum = (UINT8)(Sum + Ptr[Index]); + } + + // + // State field (since this indicates the different state of file). + // + Sum = (UINT8)(Sum - FileHeader->State); + // + // Checksum field of the file is not part of the header checksum. + // + Sum = (UINT8)(Sum - FileHeader->IntegrityCheck.Checksum.File); + + return Sum; +} + + +/** + Given a FileHandle return the VolumeHandle + + @param FileHandle File handle to look up + @param VolumeHandle Match for FileHandle + + @retval TRUE VolumeHandle is valid + +**/ +STATIC +BOOLEAN +EFIAPI +FileHandleToVolume ( + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_PEI_FV_HANDLE *VolumeHandle + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + if (Hob.Raw == NULL) { + return FALSE; + } + + do { + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, Hob.Raw); + if (Hob.Raw != NULL) { + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)(Hob.FirmwareVolume->BaseAddress); + if (((UINT64) (UINTN) FileHandle > (UINT64) (UINTN) FwVolHeader ) && \ + ((UINT64) (UINTN) FileHandle <= ((UINT64) (UINTN) FwVolHeader + FwVolHeader->FvLength - 1))) { + *VolumeHandle = (EFI_PEI_FV_HANDLE)FwVolHeader; + return TRUE; + } + + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, GET_NEXT_HOB (Hob)); + } + } while (Hob.Raw != NULL); + + return FALSE; +} + + + +/** + Given the input file pointer, search for the next matching file in the + FFS volume as defined by SearchType. The search starts from FileHeader inside + the Firmware Volume defined by FwVolHeader. + + @param FileHandle File handle to look up + @param VolumeHandle Match for FileHandle + + +**/ +EFI_STATUS +FindFileEx ( + IN CONST EFI_PEI_FV_HANDLE FvHandle, + IN CONST EFI_GUID *FileName, OPTIONAL + IN EFI_FV_FILETYPE SearchType, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + EFI_FFS_FILE_HEADER **FileHeader; + EFI_FFS_FILE_HEADER *FfsFileHeader; + EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExHeaderInfo; + UINT32 FileLength; + UINT32 FileOccupiedSize; + UINT32 FileOffset; + UINT64 FvLength; + UINT8 ErasePolarity; + UINT8 FileState; + + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FvHandle; + FileHeader = (EFI_FFS_FILE_HEADER **)FileHandle; + + FvLength = FwVolHeader->FvLength; + if (FwVolHeader->Attributes & EFI_FVB2_ERASE_POLARITY) { + ErasePolarity = 1; + } else { + ErasePolarity = 0; + } + + // + // If FileHeader is not specified (NULL) or FileName is not NULL, + // start with the first file in the firmware volume. Otherwise, + // start from the FileHeader. + // + if ((*FileHeader == NULL) || (FileName != NULL)) { + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FwVolHeader + FwVolHeader->HeaderLength); + if (FwVolHeader->ExtHeaderOffset != 0) { + FwVolExHeaderInfo = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)(((UINT8 *)FwVolHeader) + FwVolHeader->ExtHeaderOffset); + FfsFileHeader = (EFI_FFS_FILE_HEADER *)(((UINT8 *)FwVolExHeaderInfo) + FwVolExHeaderInfo->ExtHeaderSize); + } + } else { + // + // Length is 24 bits wide so mask upper 8 bits + // FileLength is adjusted to FileOccupiedSize as it is 8 byte aligned. + // + FileLength = *(UINT32 *)(*FileHeader)->Size & 0x00FFFFFF; + FileOccupiedSize = GET_OCCUPIED_SIZE (FileLength, 8); + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)*FileHeader + FileOccupiedSize); + } + + // FFS files begin with a header that is aligned on an 8-byte boundary + FfsFileHeader = ALIGN_POINTER (FfsFileHeader, 8); + + FileOffset = (UINT32) ((UINT8 *)FfsFileHeader - (UINT8 *)FwVolHeader); + ASSERT (FileOffset <= 0xFFFFFFFF); + + while (FileOffset < (FvLength - sizeof (EFI_FFS_FILE_HEADER))) { + // + // Get FileState which is the highest bit of the State + // + FileState = GetFileState (ErasePolarity, FfsFileHeader); + + switch (FileState) { + + case EFI_FILE_HEADER_INVALID: + FileOffset += sizeof(EFI_FFS_FILE_HEADER); + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + sizeof(EFI_FFS_FILE_HEADER)); + break; + + case EFI_FILE_DATA_VALID: + case EFI_FILE_MARKED_FOR_UPDATE: + if (CalculateHeaderChecksum (FfsFileHeader) != 0) { + ASSERT (FALSE); + *FileHeader = NULL; + return EFI_NOT_FOUND; + } + + FileLength = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileOccupiedSize = GET_OCCUPIED_SIZE(FileLength, 8); + + if (FileName != NULL) { + if (CompareGuid (&FfsFileHeader->Name, (EFI_GUID*)FileName)) { + *FileHeader = FfsFileHeader; + return EFI_SUCCESS; + } + } else if (((SearchType == FfsFileHeader->Type) || (SearchType == EFI_FV_FILETYPE_ALL)) && + (FfsFileHeader->Type != EFI_FV_FILETYPE_FFS_PAD)) { + *FileHeader = FfsFileHeader; + return EFI_SUCCESS; + } + + FileOffset += FileOccupiedSize; + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize); + break; + + case EFI_FILE_DELETED: + FileLength = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileOccupiedSize = GET_OCCUPIED_SIZE(FileLength, 8); + FileOffset += FileOccupiedSize; + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize); + break; + + default: + *FileHeader = NULL; + return EFI_NOT_FOUND; + } + } + + + *FileHeader = NULL; + return EFI_NOT_FOUND; +} + + +/** + Go through the file to search SectionType section, + when meeting an encapsuled section. + + @param SectionType - Filter to find only section of this type. + @param Section - From where to search. + @param SectionSize - The file size to search. + @param OutputBuffer - Pointer to the section to search. + + @retval EFI_SUCCESS +**/ +EFI_STATUS +FfsProcessSection ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_COMMON_SECTION_HEADER *Section, + IN UINTN SectionSize, + IN OUT UINT32 *SectionDatas, + IN OUT UINT32 *SectionCnt + ) +{ + EFI_STATUS Status; + UINT32 SectionLength; + UINT32 ParsedLength; + EFI_COMPRESSION_SECTION *CompressionSection; + EFI_COMPRESSION_SECTION2 *CompressionSection2; + UINT32 DstBufferSize; + VOID *ScratchBuffer; + UINT32 ScratchBufferSize; + VOID *DstBuffer; + CHAR8 *CompressedData; + UINT32 CompressedDataLength; + UINT16 SectionAttribute; + UINT32 AuthenticationStatus; + UINT32 Cnt; + UINT32 MaxCnt; + + Cnt = 0; + MaxCnt = *SectionCnt; + ParsedLength = 0; + Status = EFI_NOT_FOUND; + + while (ParsedLength < SectionSize && Cnt < MaxCnt) { + if (IS_SECTION2 (Section)) { + ASSERT (SECTION2_SIZE (Section) > 0x00FFFFFF); + } + + if (Section->Type == SectionType) { + + if (IS_SECTION2 (Section)) { + *(SectionDatas + Cnt++) = (UINT32)(UINTN)(VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2)); + } else { + *(SectionDatas + Cnt++) = (UINT32)(UINTN)(VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER)); + } + DEBUG((DEBUG_INFO, "Find Section with type(0x%x) size(0x%x)\n", SectionType, *(UINT32*)(Section->Size) & 0x00ffffff)); + + } else if ((Section->Type == EFI_SECTION_COMPRESSION) || (Section->Type == EFI_SECTION_GUID_DEFINED)) { + + if (Section->Type == EFI_SECTION_COMPRESSION) { + if (IS_SECTION2 (Section)) { + CompressionSection2 = (EFI_COMPRESSION_SECTION2 *) Section; + SectionLength = SECTION2_SIZE (Section); + + if (CompressionSection2->CompressionType != EFI_STANDARD_COMPRESSION) { + return EFI_UNSUPPORTED; + } + + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION2 *) Section + 1); + CompressedDataLength = (UINT32) SectionLength - sizeof (EFI_COMPRESSION_SECTION2); + } else { + CompressionSection = (EFI_COMPRESSION_SECTION *) Section; + SectionLength = SECTION_SIZE (Section); + + if (CompressionSection->CompressionType != EFI_STANDARD_COMPRESSION) { + return EFI_UNSUPPORTED; + } + + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION *) Section + 1); + CompressedDataLength = (UINT32) SectionLength - sizeof (EFI_COMPRESSION_SECTION); + } + + Status = UefiDecompressGetInfo ( + CompressedData, + CompressedDataLength, + &DstBufferSize, + &ScratchBufferSize + ); + } else if (Section->Type == EFI_SECTION_GUID_DEFINED) { + Status = ExtractGuidedSectionGetInfo ( + Section, + &DstBufferSize, + &ScratchBufferSize, + &SectionAttribute + ); + } + + if (EFI_ERROR (Status)) { + // + // GetInfo failed + // + DEBUG ((EFI_D_ERROR, "Decompress GetInfo Failed - %r\n", Status)); + return EFI_NOT_FOUND; + } + // + // Allocate scratch buffer + // + ScratchBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (ScratchBufferSize)); + if (ScratchBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Allocate destination buffer, extra one page for adjustment + // + DstBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (DstBufferSize) + 1); + if (DstBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // DstBuffer still is one section. Adjust DstBuffer offset, skip EFI section header + // to make section data at page alignment. + // + if (IS_SECTION2 (Section)) + DstBuffer = (UINT8 *)DstBuffer + EFI_PAGE_SIZE - sizeof (EFI_COMMON_SECTION_HEADER2); + else + DstBuffer = (UINT8 *)DstBuffer + EFI_PAGE_SIZE - sizeof (EFI_COMMON_SECTION_HEADER); + // + // Call decompress function + // + if (Section->Type == EFI_SECTION_COMPRESSION) { + if (IS_SECTION2 (Section)) { + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION2 *) Section + 1); + } + else { + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION *) Section + 1); + } + + Status = UefiDecompress ( + CompressedData, + DstBuffer, + ScratchBuffer + ); + } else if (Section->Type == EFI_SECTION_GUID_DEFINED) { + Status = ExtractGuidedSectionDecode ( + Section, + &DstBuffer, + ScratchBuffer, + &AuthenticationStatus + ); + } + + if (EFI_ERROR (Status)) { + // + // Decompress failed + // + DEBUG ((EFI_D_ERROR, "Decompress Failed - %r\n", Status)); + return EFI_NOT_FOUND; + } else { + DEBUG((DEBUG_INFO, "Decompress success\n")); + return FfsProcessSection ( + SectionType, + DstBuffer, + DstBufferSize, + SectionDatas, + SectionCnt + ); + } + } + + if (IS_SECTION2 (Section)) { + SectionLength = SECTION2_SIZE (Section); + } else { + SectionLength = SECTION_SIZE (Section); + } + // + // SectionLength is adjusted it is 4 byte aligned. + // Go to the next section + // + SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4); + ASSERT (SectionLength != 0); + ParsedLength += SectionLength; + Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionLength); + } + + *SectionCnt = Cnt; + + return Cnt == 0 ? EFI_NOT_FOUND : EFI_SUCCESS; +} + + + +/** + This service enables discovery sections of a given type within a valid FFS file. + + @param SearchType The value of the section type to find. + @param FfsFileHeader A pointer to the file header that contains the set of sections to + be searched. + @param SectionData A pointer to the discovered section, if successful. + + @retval EFI_SUCCESS The section was found. + @retval EFI_NOT_FOUND The section was not found. + +**/ +EFI_STATUS +EFIAPI +FfsFindSectionData ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT VOID **SectionData + ) +{ + EFI_FFS_FILE_HEADER *FfsFileHeader; + UINT32 FileSize; + EFI_COMMON_SECTION_HEADER *Section; + EFI_STATUS Status; + UINT32 SectionDatas[1]; + UINT32 MaxSectionCnt; + + FfsFileHeader = (EFI_FFS_FILE_HEADER *)(FileHandle); + MaxSectionCnt = 1; + + // + // Size is 24 bits wide so mask upper 8 bits. + // Does not include FfsFileHeader header size + // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned. + // + Section = (EFI_COMMON_SECTION_HEADER *)(FfsFileHeader + 1); + FileSize = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileSize -= sizeof (EFI_FFS_FILE_HEADER); + + Status = FfsProcessSection ( + SectionType, + Section, + FileSize, + SectionDatas, + &MaxSectionCnt + ); + + if(Status == EFI_SUCCESS){ + ASSERT(MaxSectionCnt == 1); + *SectionData = (VOID *)(UINTN)SectionDatas[0]; + } + + return Status; +} + +EFI_STATUS +EFIAPI +FfsFindSectionDataEx ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT UINT32 *SectionDatas, + OUT UINT32 *SectionCnt + ) +{ + EFI_FFS_FILE_HEADER *FfsFileHeader; + UINT32 FileSize; + EFI_COMMON_SECTION_HEADER *Section; + + FfsFileHeader = (EFI_FFS_FILE_HEADER *)(FileHandle); + + // + // Size is 24 bits wide so mask upper 8 bits. + // Does not include FfsFileHeader header size + // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned. + // + Section = (EFI_COMMON_SECTION_HEADER *)(FfsFileHeader + 1); + FileSize = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileSize -= sizeof (EFI_FFS_FILE_HEADER); + + return FfsProcessSection ( + SectionType, + Section, + FileSize, + SectionDatas, + SectionCnt + ); +} + + +/** + This service enables discovery of additional firmware files. + + @param SearchType A filter to find files only of this type. + @param FwVolHeader Pointer to the firmware volume header of the volume to search. + This parameter must point to a valid FFS volume. + @param FileHeader Pointer to the current file from which to begin searching. + + @retval EFI_SUCCESS The file was found. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_NOT_FOUND The header checksum was not zero. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextFile ( + IN UINT8 SearchType, + IN EFI_PEI_FV_HANDLE VolumeHandle, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + return FindFileEx (VolumeHandle, NULL, SearchType, FileHandle); +} + + +/** + This service enables discovery of additional firmware volumes. + + @param Instance This instance of the firmware volume to find. The value 0 is the + Boot Firmware Volume (BFV). + @param FwVolHeader Pointer to the firmware volume header of the volume to return. + + @retval EFI_SUCCESS The volume was found. + @retval EFI_NOT_FOUND The volume was not found. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextVolume ( + IN UINTN Instance, + IN OUT EFI_PEI_FV_HANDLE *VolumeHandle + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + + Hob.Raw = GetHobList (); + if (Hob.Raw == NULL) { + return EFI_NOT_FOUND; + } + + do { + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, Hob.Raw); + if (Hob.Raw != NULL) { + if (Instance-- == 0) { + *VolumeHandle = (EFI_PEI_FV_HANDLE)(UINTN)(Hob.FirmwareVolume->BaseAddress); + return EFI_SUCCESS; + } + + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, GET_NEXT_HOB (Hob)); + } + } while (Hob.Raw != NULL); + + return EFI_NOT_FOUND; + +} + + +/** + Find a file in the volume by name + + @param FileName A pointer to the name of the file to + find within the firmware volume. + + @param VolumeHandle The firmware volume to search FileHandle + Upon exit, points to the found file's + handle or NULL if it could not be found. + + @retval EFI_SUCCESS File was found. + + @retval EFI_NOT_FOUND File was not found. + + @retval EFI_INVALID_PARAMETER VolumeHandle or FileHandle or + FileName was NULL. + +**/ +EFI_STATUS +EFIAPI +FfsFindFileByName ( + IN CONST EFI_GUID *FileName, + IN EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + if ((VolumeHandle == NULL) || (FileName == NULL) || (FileHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + Status = FindFileEx (VolumeHandle, FileName, 0, FileHandle); + if (Status == EFI_NOT_FOUND) { + *FileHandle = NULL; + } + return Status; +} + + + + +/** + Get information about the file by name. + + @param FileHandle Handle of the file. + + @param FileInfo Upon exit, points to the file's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetFileInfo ( + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_FV_FILE_INFO *FileInfo + ) +{ + UINT8 FileState; + UINT8 ErasePolarity; + EFI_FFS_FILE_HEADER *FileHeader; + EFI_PEI_FV_HANDLE VolumeHandle; + + if ((FileHandle == NULL) || (FileInfo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + VolumeHandle = 0; + // + // Retrieve the FirmwareVolume which the file resides in. + // + if (!FileHandleToVolume(FileHandle, &VolumeHandle)) { + return EFI_INVALID_PARAMETER; + } + + if (((EFI_FIRMWARE_VOLUME_HEADER*)VolumeHandle)->Attributes & EFI_FVB2_ERASE_POLARITY) { + ErasePolarity = 1; + } else { + ErasePolarity = 0; + } + + // + // Get FileState which is the highest bit of the State + // + FileState = GetFileState (ErasePolarity, (EFI_FFS_FILE_HEADER*)FileHandle); + + switch (FileState) { + case EFI_FILE_DATA_VALID: + case EFI_FILE_MARKED_FOR_UPDATE: + break; + default: + return EFI_INVALID_PARAMETER; + } + + FileHeader = (EFI_FFS_FILE_HEADER *)FileHandle; + CopyMem (&FileInfo->FileName, &FileHeader->Name, sizeof(EFI_GUID)); + FileInfo->FileType = FileHeader->Type; + FileInfo->FileAttributes = FileHeader->Attributes; + FileInfo->BufferSize = ((*(UINT32 *)FileHeader->Size) & 0x00FFFFFF) - sizeof (EFI_FFS_FILE_HEADER); + FileInfo->Buffer = (FileHeader + 1); + return EFI_SUCCESS; +} + + +/** + Get Information about the volume by name + + @param VolumeHandle Handle of the volume. + + @param VolumeInfo Upon exit, points to the volume's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetVolumeInfo ( + IN EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_FV_INFO *VolumeInfo + ) +{ + EFI_FIRMWARE_VOLUME_HEADER FwVolHeader; + EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExHeaderInfo; + + if (VolumeInfo == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // VolumeHandle may not align at 8 byte, + // but FvLength is UINT64 type, which requires FvHeader align at least 8 byte. + // So, Copy FvHeader into the local FvHeader structure. + // + CopyMem (&FwVolHeader, VolumeHandle, sizeof (EFI_FIRMWARE_VOLUME_HEADER)); + // + // Check Fv Image Signature + // + if (FwVolHeader.Signature != EFI_FVH_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + VolumeInfo->FvAttributes = FwVolHeader.Attributes; + VolumeInfo->FvStart = (VOID *) VolumeHandle; + VolumeInfo->FvSize = FwVolHeader.FvLength; + CopyMem (&VolumeInfo->FvFormat, &FwVolHeader.FileSystemGuid, sizeof(EFI_GUID)); + + if (FwVolHeader.ExtHeaderOffset != 0) { + FwVolExHeaderInfo = (EFI_FIRMWARE_VOLUME_EXT_HEADER*)(((UINT8 *)VolumeHandle) + FwVolHeader.ExtHeaderOffset); + CopyMem (&VolumeInfo->FvName, &FwVolExHeaderInfo->FvName, sizeof(EFI_GUID)); + } + return EFI_SUCCESS; +} + + + +/** + Search through every FV until you find a file of type FileType + + @param FileType File handle of a Fv type file. + @param Volumehandle On success Volume Handle of the match + @param FileHandle On success File Handle of the match + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully found FileType + +**/ +EFI_STATUS +EFIAPI +FfsAnyFvFindFirstFile ( + IN EFI_FV_FILETYPE FileType, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + UINTN Instance; + + // + // Search every FV for the DXE Core + // + Instance = 0; + *FileHandle = NULL; + + while (1) + { + Status = FfsFindNextVolume (Instance++, VolumeHandle); + if (EFI_ERROR (Status)) + { + break; + } + + Status = FfsFindNextFile (FileType, *VolumeHandle, FileHandle); + if (!EFI_ERROR (Status)) + { + break; + } + } + + DEBUG((DEBUG_INFO, "FfsAnyFvFindFirstFile with FileType = 0x%x (DXE_CORE == 0x05). %r\n", FileType, Status)); + + return Status; +} + +EFI_STATUS +EFIAPI +FfsAnyFvFindFileByName ( + IN CONST EFI_GUID *Name, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + UINTN Instance; + + // + // Search every FV for the DXE Core + // + Instance = 0; + *FileHandle = NULL; + + while (1) + { + Status = FfsFindNextVolume (Instance++, VolumeHandle); + if (EFI_ERROR (Status)) + { + break; + } + + Status = FfsFindFileByName (Name, *VolumeHandle, FileHandle); + if (!EFI_ERROR (Status)) + { + break; + } + } + + DEBUG((DEBUG_INFO, "FfsAnyFvFindFileByName with name = %g, %r\n", Name, Status)); + + return Status; +} + + +/** + Get Fv image from the FV type file, then add FV & FV2 Hob. + + @param FileHandle File handle of a Fv type file. + + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully to process it. + +**/ +EFI_STATUS +EFIAPI +FfsProcessFvFile ( + IN EFI_PEI_FILE_HANDLE FvFileHandle + ) +{ + EFI_STATUS Status; + EFI_FV_INFO FvImageInfo; + UINT32 FvAlignment; + VOID *FvBuffer; + EFI_PEI_HOB_POINTERS HobFv2; + UINT32 FvImagesCnt; + UINT32 FvImageHandles[MAX_FV_IMAGES]; + UINT32 Index; + + // + // Check if this EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE file has already + // been extracted. + // + HobFv2.Raw = GetHobList (); + while ((HobFv2.Raw = GetNextHob (EFI_HOB_TYPE_FV2, HobFv2.Raw)) != NULL) { + + if (CompareGuid (&(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name), &HobFv2.FirmwareVolume2->FileName)) { + // + // this FILE has been dispatched, it will not be dispatched again. + // + return EFI_SUCCESS; + } + HobFv2.Raw = GET_NEXT_HOB (HobFv2); + } + + // + // Find FvImage in FvFile + // In FvFile there may be multi FvImages + // + FvImagesCnt = MAX_FV_IMAGES; + Status = FfsFindSectionDataEx (EFI_SECTION_FIRMWARE_VOLUME_IMAGE, + FvFileHandle, + FvImageHandles, + &FvImagesCnt); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + return Status; + } + + DEBUG((DEBUG_INFO, "Collect FvImageInfo\n")); + for(Index=0; Index < FvImagesCnt; Index++){ + + // + // Collect FvImage Info. + // + + ZeroMem (&FvImageInfo, sizeof (FvImageInfo)); + Status = FfsGetVolumeInfo ((EFI_PEI_FV_HANDLE)((UINTN)FvImageHandles[Index]), &FvImageInfo); + ASSERT_EFI_ERROR(Status); + DEBUG((DEBUG_INFO, " Fv Name=%g, Format=%g, Size=0x%x\n", FvImageInfo.FvName, FvImageInfo.FvFormat, FvImageInfo.FvSize)); + + // + // FvAlignment must be more than 8 bytes required by FvHeader structure. + // + FvAlignment = 1 << ((FvImageInfo.FvAttributes & EFI_FVB2_ALIGNMENT) >> 16); + if (FvAlignment < 8) { + FvAlignment = 8; + } + + // + // Check FvImage + // + if ((UINTN) FvImageInfo.FvStart % FvAlignment != 0) { + FvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINT32) FvImageInfo.FvSize), FvAlignment); + if (FvBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (FvBuffer, FvImageInfo.FvStart, (UINTN) FvImageInfo.FvSize); + // + // Update FvImageInfo after reload FvImage to new aligned memory + // + FfsGetVolumeInfo ((EFI_PEI_FV_HANDLE) FvBuffer, &FvImageInfo); + } + + + // + // Inform HOB consumer phase, i.e. DXE core, the existence of this FV + // + BuildFvHob ((EFI_PHYSICAL_ADDRESS) (UINTN) FvImageInfo.FvStart, FvImageInfo.FvSize); + + // + // Makes the encapsulated volume show up in DXE phase to skip processing of + // encapsulated file again. + // + BuildFv2Hob ( + (EFI_PHYSICAL_ADDRESS) (UINTN) FvImageInfo.FvStart, + FvImageInfo.FvSize, + &FvImageInfo.FvName, + &(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name) + ); + } + + return EFI_SUCCESS; +} + + + diff --git a/OvmfPkg/Library/PrePiLibTdx/PrePi.h b/OvmfPkg/Library/PrePiLibTdx/PrePi.h new file mode 100644 index 000000000000..340daef6ed09 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/PrePi.h @@ -0,0 +1,29 @@ +/** @file + Library that helps implement monolithic PEI (i.e. PEI part of SEC) + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PI_PEI_H_ +#define _PI_PEI_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/OvmfPkg/Library/PrePiLibTdx/PrePiLib.c b/OvmfPkg/Library/PrePiLibTdx/PrePiLib.c new file mode 100644 index 000000000000..1ff83b28d1c4 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/PrePiLib.c @@ -0,0 +1,251 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +// +// Hack to work in NT32 +// +EFI_STATUS + +EFIAPI + +SecWinNtPeiLoadFile ( + IN VOID *Pe32Data, + IN EFI_PHYSICAL_ADDRESS *ImageAddress, + IN UINT64 *ImageSize, + IN EFI_PHYSICAL_ADDRESS *EntryPoint + ); + +STATIC +VOID* +EFIAPI +AllocateCodePages ( + IN UINTN Pages + ) +{ + VOID *Alloc; + EFI_PEI_HOB_POINTERS Hob; + + Alloc = AllocatePages (Pages); + if (Alloc == NULL) { + return NULL; + } + + // find the HOB we just created, and change the type to EfiBootServicesCode + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); + while (Hob.Raw != NULL) { + if (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress == (UINTN)Alloc) { + Hob.MemoryAllocation->AllocDescriptor.MemoryType = EfiBootServicesCode; + return Alloc; + } + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, GET_NEXT_HOB (Hob)); + } + + ASSERT (FALSE); + + FreePages (Alloc, Pages); + return NULL; +} + + +EFI_STATUS +EFIAPI +LoadPeCoffImage ( + IN VOID *PeCoffImage, + OUT EFI_PHYSICAL_ADDRESS *ImageAddress, + OUT UINT64 *ImageSize, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint + ) +{ + RETURN_STATUS Status; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + VOID *Buffer; + + ZeroMem (&ImageContext, sizeof (ImageContext)); + + ImageContext.Handle = PeCoffImage; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + Status = PeCoffLoaderGetImageInfo (&ImageContext); + ASSERT_EFI_ERROR (Status); + + + // + // Allocate Memory for the image + // + Buffer = AllocateCodePages (EFI_SIZE_TO_PAGES((UINT32)ImageContext.ImageSize)); + ASSERT (Buffer != 0); + + + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer; + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + ASSERT_EFI_ERROR (Status); + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + ASSERT_EFI_ERROR (Status); + + + *ImageAddress = ImageContext.ImageAddress; + *ImageSize = ImageContext.ImageSize; + *EntryPoint = ImageContext.EntryPoint; + + // + // Flush not needed for all architectures. We could have a processor specific + // function in this library that does the no-op if needed. + // + InvalidateInstructionCacheRange ((VOID *)(UINTN)*ImageAddress, (UINTN)*ImageSize); + + return Status; +} + + + +typedef +VOID +(EFIAPI *DXE_CORE_ENTRY_POINT) ( + IN VOID *HobStart + ); + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFfsFile ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN UINTN StackSize + ) +{ + EFI_STATUS Status; + VOID *PeCoffImage; + EFI_PHYSICAL_ADDRESS ImageAddress; + UINT64 ImageSize; + EFI_PHYSICAL_ADDRESS EntryPoint; + VOID *BaseOfStack; + VOID *TopOfStack; + VOID *Hob; + EFI_FV_FILE_INFO FvFileInfo; + + Status = FfsFindSectionData (EFI_SECTION_PE32, FileHandle, &PeCoffImage); + if (EFI_ERROR (Status)) { + return Status; + } + + + Status = LoadPeCoffImage (PeCoffImage, &ImageAddress, &ImageSize, &EntryPoint); +// For NT32 Debug Status = SecWinNtPeiLoadFile (PeCoffImage, &ImageAddress, &ImageSize, &EntryPoint); + ASSERT_EFI_ERROR (Status); + + // + // Extract the DxeCore GUID file name. + // + Status = FfsGetFileInfo (FileHandle, &FvFileInfo); + ASSERT_EFI_ERROR (Status); + + BuildModuleHob (&FvFileInfo.FileName, (EFI_PHYSICAL_ADDRESS)(UINTN)ImageAddress, EFI_SIZE_TO_PAGES ((UINT32) ImageSize) * EFI_PAGE_SIZE, EntryPoint); + + DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Loading DxeCore at 0x%10p EntryPoint=0x%10p\n", (VOID *)(UINTN)ImageAddress, (VOID *)(UINTN)EntryPoint)); + + Hob = GetHobList (); + if (StackSize == 0) { + // User the current stack + + ((DXE_CORE_ENTRY_POINT)(UINTN)EntryPoint) (Hob); + } else { + + // + // Allocate 128KB for the Stack + // + BaseOfStack = AllocatePages (EFI_SIZE_TO_PAGES (StackSize)); + ASSERT (BaseOfStack != NULL); + + // + // Compute the top of the stack we were allocated. Pre-allocate a UINTN + // for safety. + // + TopOfStack = (VOID *) ((UINTN) BaseOfStack + EFI_SIZE_TO_PAGES (StackSize) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT); + TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT); + + // + // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore. + // + UpdateStackHob ((EFI_PHYSICAL_ADDRESS)(UINTN) BaseOfStack, StackSize); + + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)EntryPoint, + Hob, + NULL, + TopOfStack + ); + + } + + // Should never get here as DXE Core does not return + DEBUG ((EFI_D_ERROR, "DxeCore returned\n")); + ASSERT (FALSE); + + return EFI_DEVICE_ERROR; +} + + + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFv ( + IN UINTN *FvInstance, OPTIONAL + IN UINTN StackSize + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + EFI_PEI_FILE_HANDLE FileHandle = NULL; + + if (FvInstance != NULL) { + // + // Caller passed in a specific FV to try, so only try that one + // + Status = FfsFindNextVolume (*FvInstance, &VolumeHandle); + if (!EFI_ERROR (Status)) { + Status = FfsFindNextFile (EFI_FV_FILETYPE_DXE_CORE, VolumeHandle, &FileHandle); + } + } else { + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, &FileHandle); + } + + if (!EFI_ERROR (Status)) { + return LoadDxeCoreFromFfsFile (FileHandle, StackSize); + } + + return Status; +} + + +EFI_STATUS +EFIAPI +DecompressFirstFv ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + EFI_PEI_FILE_HANDLE FileHandle; + + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, &VolumeHandle, &FileHandle); + if (!EFI_ERROR (Status)) { + Status = FfsProcessFvFile (FileHandle); + } + + return Status; +} + + diff --git a/OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf b/OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf new file mode 100644 index 000000000000..09cdef3a31b8 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf @@ -0,0 +1,51 @@ +#/** @file +# Component description file for TDX Pre PI Library +# +# LIbrary helps you build a platform that skips PEI and loads DXE Core +# directly. Helps building HOBs, reading data from the FV, and doing +# decompression. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2008, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PrePiLibTdx + FILE_GUID = 1F3A3278-82EB-4C0D-86F1-5BCDA5846CB2 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = PrePiLib + + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources.common] + PrePi.h + FwVol.c + PrePiLib.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib + UefiDecompressLib + PeCoffLib + CacheMaintenanceLib + PrintLib + SerialPortLib + TimerLib + PerformanceLib + HobLib + ExtractGuidedSectionLib diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 5ac6f44a88e5..c6f2a193f5d3 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -276,6 +276,7 @@ VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf TdvfPlatformLib|OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf + PrePiLib|OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf [LibraryClasses.common.PEI_CORE] HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf From 64dd5437453fc19317de44a051f493b33da4fb27 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 16:00:18 +0800 Subject: [PATCH 19/54] OvmfPkg: Add TdxStartupLib --- OvmfPkg/Include/Library/TdxStartupLib.h | 28 + OvmfPkg/Library/TdxStartupLib/DxeLoad.c | 325 +++++++ OvmfPkg/Library/TdxStartupLib/Hob.c | 451 +++++++++ OvmfPkg/Library/TdxStartupLib/Mp.c | 156 +++ OvmfPkg/Library/TdxStartupLib/Tcg.c | 231 +++++ OvmfPkg/Library/TdxStartupLib/TdxStartup.c | 258 +++++ .../TdxStartupLib/TdxStartupInternal.h | 198 ++++ .../Library/TdxStartupLib/TdxStartupLib.inf | 100 ++ .../TdxStartupLib/TdxStartupLibNull.inf | 40 + .../Library/TdxStartupLib/TdxStartupNull.c | 17 + .../Library/TdxStartupLib/X64/ApRunLoop.nasm | 88 ++ .../Library/TdxStartupLib/X64/PageTables.h | 208 ++++ .../Library/TdxStartupLib/X64/VirtualMemory.c | 896 ++++++++++++++++++ 13 files changed, 2996 insertions(+) create mode 100644 OvmfPkg/Include/Library/TdxStartupLib.h create mode 100644 OvmfPkg/Library/TdxStartupLib/DxeLoad.c create mode 100644 OvmfPkg/Library/TdxStartupLib/Hob.c create mode 100644 OvmfPkg/Library/TdxStartupLib/Mp.c create mode 100644 OvmfPkg/Library/TdxStartupLib/Tcg.c create mode 100644 OvmfPkg/Library/TdxStartupLib/TdxStartup.c create mode 100644 OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h create mode 100644 OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf create mode 100644 OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf create mode 100644 OvmfPkg/Library/TdxStartupLib/TdxStartupNull.c create mode 100644 OvmfPkg/Library/TdxStartupLib/X64/ApRunLoop.nasm create mode 100644 OvmfPkg/Library/TdxStartupLib/X64/PageTables.h create mode 100644 OvmfPkg/Library/TdxStartupLib/X64/VirtualMemory.c diff --git a/OvmfPkg/Include/Library/TdxStartupLib.h b/OvmfPkg/Include/Library/TdxStartupLib.h new file mode 100644 index 000000000000..7419cc1c4413 --- /dev/null +++ b/OvmfPkg/Include/Library/TdxStartupLib.h @@ -0,0 +1,28 @@ +#ifndef __TDX_STARTUP_LIB_H__ +#define __TDX_STARTUP_LIB_H__ + +#include +#include +#include +#include +#include +#include + +typedef +VOID +(EFIAPI * fProcessLibraryConstructorList)( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ); + + +VOID +EFIAPI +TdxStartup( + IN VOID * Context, + IN VOID * VmmHobList, + IN UINTN Info, + IN fProcessLibraryConstructorList Function +); + +#endif diff --git a/OvmfPkg/Library/TdxStartupLib/DxeLoad.c b/OvmfPkg/Library/TdxStartupLib/DxeLoad.c new file mode 100644 index 000000000000..a3734964fad3 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/DxeLoad.c @@ -0,0 +1,325 @@ +/** @file + Responsibility of this file is to load the DXE Core from a Firmware Volume. + +Copyright (c) 2016 HP Development Company, L.P. +Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "TdxStartupInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "X64/PageTables.h" +#include + +#define PCD_PEIM_GUID { \ + 0x9b3ada4f, 0xae56, 0x4c24, {0x8d, 0xea, 0xf0, 0x3b, 0x75, 0x58, 0xae, 0x50} \ + } + +EFI_GUID mPcdPeimGuid = PCD_PEIM_GUID; + +#define STACK_SIZE 0x20000 + +EFI_MEMORY_TYPE_INFORMATION mDefaultMemoryTypeInformation[] = { + { EfiACPIMemoryNVS, 0x004 }, + { EfiACPIReclaimMemory, 0x008 }, + { EfiReservedMemoryType, 0x004 }, + { EfiRuntimeServicesData, 0x024 }, + { EfiRuntimeServicesCode, 0x030 }, + { EfiBootServicesCode, 0x180 }, + { EfiBootServicesData, 0xF00 }, + { EfiMaxMemoryType, 0x000 } +}; + + +/** + Transfers control to DxeCore. + + This function performs a CPU architecture specific operations to execute + the entry point of DxeCore + + @param DxeCoreEntryPoint The entry point of DxeCore. + +**/ +VOID +HandOffToDxeCore ( + IN EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint + ) +{ + VOID *BaseOfStack; + VOID *TopOfStack; + UINTN PageTables; + + // + // Clear page 0 and mark it as allocated if NULL pointer detection is enabled. + // + if (IsNullDetectionEnabled ()) { + ClearFirst4KPage (GetHobList ()); + BuildMemoryAllocationHob (0, EFI_PAGES_TO_SIZE (1), EfiBootServicesData); + } + + + // + // Allocate 128KB for the Stack + // + BaseOfStack = AllocatePages (EFI_SIZE_TO_PAGES (STACK_SIZE)); + ASSERT (BaseOfStack != NULL); + + // + // Compute the top of the stack we were allocated. Pre-allocate a UINTN + // for safety. + // + TopOfStack = (VOID *) ((UINTN) BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT); + TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT); + + DEBUG((DEBUG_INFO, "BaseOfStack=0x%x, TopOfStack=0x%x\n", BaseOfStack, TopOfStack)); + + PageTables = 0; + if (FeaturePcdGet (PcdDxeIplBuildPageTables)) { + // + // Create page table and save PageMapLevel4 to CR3 + // + PageTables = CreateIdentityMappingPageTables ((EFI_PHYSICAL_ADDRESS) (UINTN) BaseOfStack, + STACK_SIZE); + } else { + // + // Set NX for stack feature also require PcdDxeIplBuildPageTables be TRUE + // for the DxeIpl and the DxeCore are both X64. + // + ASSERT (FixedPcdGetBool (PcdTdxSetNxForStack) == FALSE); + ASSERT (FixedPcdGetBool (PcdCpuStackGuard) == FALSE); + } + + if (FeaturePcdGet (PcdDxeIplBuildPageTables)) { + AsmWriteCr3 (PageTables); + } + + // + // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore. + // + UpdateStackHob ((EFI_PHYSICAL_ADDRESS)(UINTN) BaseOfStack, STACK_SIZE); + + DEBUG ((DEBUG_INFO, "SwitchStack then Jump to DxeCore\n")); + // + // Transfer the control to the entry point of DxeCore. + // + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)DxeCoreEntryPoint, + GetHobList(), + NULL, + TopOfStack + ); +} + +EFI_STATUS +FindPcdPeim ( + IN INTN FvInstance, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + + ASSERT (FileHandle != NULL); + ASSERT (FvInstance != -1); + *FileHandle = NULL; + + // + // Caller passed in a specific FV to try, so only try that one + // + Status = FfsFindNextVolume (FvInstance, &VolumeHandle); + if (!EFI_ERROR (Status)) { + Status = FfsFindNextFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, VolumeHandle, FileHandle); + + if (*FileHandle) { + // Assume the FV that contains multiple compressed FVs. + // So decompress the compressed FVs + Status = FfsProcessFvFile (*FileHandle); + ASSERT_EFI_ERROR (Status); + + Status = FfsAnyFvFindFileByName (&mPcdPeimGuid, &VolumeHandle, FileHandle); + } + } + + return Status; +} + +EFI_STATUS +EFIAPI +InitPeimPcd ( + IN INTN FvInstance) +{ + EFI_STATUS Status; + PEI_PCD_DATABASE *Database; + PEI_PCD_DATABASE *PeiPcdDbBinary; + EFI_PEI_FILE_HANDLE FileHandle; + + // + // Find the PcdPeim and initialize the Pcd Database + // + Status = FindPcdPeim (FvInstance, &FileHandle); + ASSERT_EFI_ERROR (Status); + + Status = FfsFindSectionData (EFI_SECTION_RAW, FileHandle, (VOID**)(UINTN)&PeiPcdDbBinary); + if (EFI_ERROR (Status)) { + return Status; + } + Database = BuildGuidHob (&gPcdDataBaseHobGuid, PeiPcdDbBinary->Length + PeiPcdDbBinary->UninitDataBaseSize); + + ZeroMem (Database, PeiPcdDbBinary->Length + PeiPcdDbBinary->UninitDataBaseSize); + + // + // PeiPcdDbBinary is smaller than Database + // + CopyMem (Database, PeiPcdDbBinary, PeiPcdDbBinary->Length); + + return Status; +} + + +/** + Searches DxeCore in all firmware Volumes and loads the first + instance that contains DxeCore. + + @return FileHandle of DxeCore to load DxeCore. + +**/ +EFI_STATUS +FindDxeCore ( + IN INTN FvInstance, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + + ASSERT (FileHandle != NULL); + *FileHandle = NULL; + + if (FvInstance != -1) { + // + // Caller passed in a specific FV to try, so only try that one + // + Status = FfsFindNextVolume (FvInstance, &VolumeHandle); + if (!EFI_ERROR (Status)) { + Status = FfsFindNextFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, VolumeHandle, FileHandle); + + if (*FileHandle) { + // Assume the FV that contains multiple compressed FVs. + // So decompress the compressed FVs + Status = FfsProcessFvFile (*FileHandle); + ASSERT_EFI_ERROR (Status); + + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, FileHandle); + } + } + } else { + // Assume the FV that contains the SEC (our code) also contains a compressed FV. + Status = DecompressFirstFv (); + ASSERT_EFI_ERROR (Status); + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, FileHandle); + } + + return Status; +} + +/** + This function finds DXE Core in the firmware volume and transfer the control to + DXE core. + + @return EFI_SUCCESS DXE core was successfully loaded. + @return EFI_OUT_OF_RESOURCES There are not enough resources to load DXE core. + +**/ +EFI_STATUS +EFIAPI +DxeLoadCore ( + IN INTN FvInstance + ) +{ + EFI_STATUS Status; + EFI_FV_FILE_INFO DxeCoreFileInfo; + EFI_PHYSICAL_ADDRESS DxeCoreAddress; + UINT64 DxeCoreSize; + EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint; + EFI_PEI_FILE_HANDLE FileHandle; + VOID *PeCoffImage; + + + // + // Create Memory Type Information HOB + // + BuildGuidDataHob ( + &gEfiMemoryTypeInformationGuid, + mDefaultMemoryTypeInformation, + sizeof (mDefaultMemoryTypeInformation) + ); + + // + // Look in all the FVs present and find the DXE Core FileHandle + // + Status = FindDxeCore (FvInstance, &FileHandle); + ASSERT_EFI_ERROR (Status); + + // + // Load the DXE Core from a Firmware Volume. + // + Status = FfsFindSectionData (EFI_SECTION_PE32, FileHandle, &PeCoffImage); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = LoadPeCoffImage (PeCoffImage, &DxeCoreAddress, &DxeCoreSize, &DxeCoreEntryPoint); + ASSERT_EFI_ERROR (Status); + + // + // Extract the DxeCore GUID file name. + // + Status = FfsGetFileInfo (FileHandle, &DxeCoreFileInfo); + ASSERT_EFI_ERROR (Status); + + // + // Add HOB for the DXE Core + // + BuildModuleHob ( + &DxeCoreFileInfo.FileName, + DxeCoreAddress, + ALIGN_VALUE (DxeCoreSize, EFI_PAGE_SIZE), + DxeCoreEntryPoint + ); + + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading DXE CORE at 0x%11p EntryPoint=0x%11p\n", + (VOID *)(UINTN)DxeCoreAddress, FUNCTION_ENTRY_POINT (DxeCoreEntryPoint))); + + // + // Initialize PeimPcd database + // + Status = InitPeimPcd (FvInstance); + ASSERT_EFI_ERROR (Status); + + // Transfer control to the DXE Core + // The hand off state is simply a pointer to the HOB list + // + HandOffToDxeCore (DxeCoreEntryPoint); + + // + // If we get here, then the DXE Core returned. This is an error + // DxeCore should not return. + // + ASSERT (FALSE); + CpuDeadLoop (); + + return EFI_OUT_OF_RESOURCES; +} + + diff --git a/OvmfPkg/Library/TdxStartupLib/Hob.c b/OvmfPkg/Library/TdxStartupLib/Hob.c new file mode 100644 index 000000000000..321e4aeb4935 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/Hob.c @@ -0,0 +1,451 @@ +/** @file + Main SEC phase code. Handles initial TDX Hob List Processing + + Copyright (c) 2008, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TdxStartupInternal.h" + +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 TD + + Firmware must parse list, and accept the pages of memory before their can be + use by the guest. + + In addition, the hob list will be measured so it cannot be modified. + After accepting the pages, the firmware will use the largest memory resource + region as it's initial memory pool. It will initialize a new hoblist there, and the pre-DXE + memory allocation will use that for allocations. + + Also, it will location the highest memory region < 4GIG. It will use this to allocate + ACPI NVS memory for mailbox and spinloop for AP use. The APs will be relocated there + until the guest OS wakes them up. + + @param[in] VmmHobList The Hoblist pass the firmware + +**/ +VOID +EFIAPI +ProcessHobList ( + IN CONST VOID *VmmHobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS PhysicalEnd; + EFI_PHYSICAL_ADDRESS PhysicalStart; + UINT64 Length; + EFI_HOB_RESOURCE_DESCRIPTOR *LowMemoryResource = NULL; + + 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)); + + // + // FixMe: + // We only accept the ResourceType (EFI_RESOURCE_UNACCEPTED_MEMORY) + // But this ResourceType is still in upstream. + // So now we accept the ResourceType (EFI_RESOURCE_SYSTEM_MEMORY) without + // EFI_RESOURCE_ATTRIBUTE_ENCRYPTED. + // After EFI_RESOURCE_UNACCEPTED_MEMORY is in EDK2, then we switch to it. + // + 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; + + if (PhysicalEnd <= BASE_4GB) { + if ((LowMemoryResource == NULL) || (Hob.ResourceDescriptor->ResourceLength > LowMemoryResource->ResourceLength)) { + LowMemoryResource = Hob.ResourceDescriptor; + } + } + + MpAcceptMemoryResourceRange ( + Hob.ResourceDescriptor->PhysicalStart, + PhysicalEnd); + } + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + + ASSERT (LowMemoryResource != NULL); + + PhysicalStart = LowMemoryResource->PhysicalStart; + Length = LowMemoryResource->ResourceLength; + + // + // HobLib doesn't like HobStart at address 0 so adjust is needed + // + if (PhysicalStart == 0) { + PhysicalStart += EFI_PAGE_SIZE; + Length -= EFI_PAGE_SIZE; + } + + + HobConstructor ( + (VOID *) PhysicalStart, + Length, + (VOID *) PhysicalStart, + (VOID *) (PhysicalStart + Length) + ); + + PrePeiSetHobList ((VOID *)(UINT64)PhysicalStart); +} + +/** + Transfer the incoming HobList for the TD to the final HobList for Dxe + + @param[in] VmmHobList The Hoblist pass the firmware + +**/ +VOID +EFIAPI +TransferHobList ( + IN CONST VOID *VmmHobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute; + EFI_PHYSICAL_ADDRESS PhysicalEnd; + + Hob.Raw = (UINT8 *) VmmHobList; + while (!END_OF_HOB_LIST (Hob)) { + switch (Hob.Header->HobType) { + case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR: + ResourceAttribute = Hob.ResourceDescriptor->ResourceAttribute; + PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength; + + // + // We mark each resource that we issue AcceptPage to with EFI_RESOURCE_SYSTEM_MEMORY + // + if ((Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && + (PhysicalEnd <= BASE_4GB)) { + ResourceAttribute |= EFI_RESOURCE_ATTRIBUTE_ENCRYPTED; + } + 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 ()); +} + +/** + Create a log event for the Hoblist passed from the VMM. + + This function will create a unique GUID hob entry will be + found from the TCG driver building the event log. + This module will generate the measurement with the data in + this hob, and log the event. + + @param[in] VmmHobList The Hoblist pass the firmware + +**/ +VOID +EFIAPI +LogHobList ( + IN CONST VOID *VmmHobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + TDX_HANDOFF_TABLE_POINTERS2 HandoffTables; + EFI_STATUS Status; + + Hob.Raw = (UINT8 *) VmmHobList; + + // + // Parse the HOB list until end of list. + // + while (!END_OF_HOB_LIST (Hob)) { + Hob.Raw = GET_NEXT_HOB (Hob); + } + + // + // Init the log event for HOB measurement + // + + HandoffTables.TableDescriptionSize = sizeof (HandoffTables.TableDescription); + CopyMem (HandoffTables.TableDescription, HANDOFF_TABLE_DESC, sizeof (HandoffTables.TableDescription)); + HandoffTables.NumberOfTables = 1; + CopyGuid (&(HandoffTables.TableEntry[0].VendorGuid), &gUefiOvmfPkgTokenSpaceGuid); + HandoffTables.TableEntry[0].VendorTable = (VOID *) VmmHobList; + + Status = CreateTdxExtendEvent ( + 1, // PCRIndex + EV_EFI_HANDOFF_TABLES2, // EventType + (VOID *)&HandoffTables, // EventData + sizeof (HandoffTables), // EventSize + (UINT8*) (UINTN) VmmHobList, // HashData + (UINTN) ((UINT8 *)Hob.Raw - (UINT8 *)VmmHobList) // HashDataLen + ); + + ASSERT_EFI_ERROR (Status); +} diff --git a/OvmfPkg/Library/TdxStartupLib/Mp.c b/OvmfPkg/Library/TdxStartupLib/Mp.c new file mode 100644 index 000000000000..0ce1800c9a6d --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/Mp.c @@ -0,0 +1,156 @@ +/** @file + Main SEC MP code. + + Copyright (c) 2008, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include "TdxStartupInternal.h" + +/** + 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 *)GetMailBox(); + 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; +} + +VOID +EFIAPI +MpSerializeStart ( + VOID + ) +{ + volatile MP_WAKEUP_MAILBOX *MailBox; + UINT32 NumOfCpus; + + NumOfCpus = GetNumCpus(); + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); + + 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); +} + +VOID +EFIAPI +MpSerializeEnd ( + VOID + ) +{ + volatile MP_WAKEUP_MAILBOX *MailBox; + + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); + 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); +} + +VOID +EFIAPI +MpAcceptMemoryResourceRange ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN EFI_PHYSICAL_ADDRESS PhysicalEnd + ) +{ + UINT64 Pages; + EFI_PHYSICAL_ADDRESS Stride; + EFI_PHYSICAL_ADDRESS AcceptSize; + volatile MP_WAKEUP_MAILBOX *MailBox; + + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); + AcceptSize = FixedPcdGet64(PcdTdxAcceptPageChunkSize); + + MpSerializeStart(); + + MpSendWakeupCommand(MpProtectedModeWakeupCommandAcceptPages, + 0, + PhysicalAddress, + PhysicalEnd, + AcceptSize, + EFI_PAGE_SIZE); + + // + // All cpus share the burden of accepting the pages + // A cpu will accept AcceptSize size amount of memory + // and then skip pass range the other cpus do + // Stride is the amount of skip + // + Stride = GetNumCpus() * AcceptSize; + // + // Keep accepting until end of resource + // + while (PhysicalAddress < PhysicalEnd) { + // + // Decrease size of near end of resource if needed. + // + Pages = RShiftU64(MIN(AcceptSize, PhysicalEnd - PhysicalAddress), EFI_PAGE_SHIFT); + + MailBox->Tallies[0] += (UINT32)Pages; + + TdAcceptPages ( PhysicalAddress, Pages, EFI_PAGE_SIZE); + // + // Bump address to next chunk this cpu is responisble for + // + PhysicalAddress += Stride; + } + MpSerializeEnd(); + + DEBUG((DEBUG_INFO, "Tallies %x %x %x %x %x %x %x %x\n", + MailBox->Tallies[0], + MailBox->Tallies[1], + MailBox->Tallies[2], + MailBox->Tallies[3], + MailBox->Tallies[4], + MailBox->Tallies[5], + MailBox->Tallies[6], + MailBox->Tallies[7])); + +} diff --git a/OvmfPkg/Library/TdxStartupLib/Tcg.c b/OvmfPkg/Library/TdxStartupLib/Tcg.c new file mode 100644 index 000000000000..5ef0ec378246 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/Tcg.c @@ -0,0 +1,231 @@ +/** @file + Initialize TPM2 device and measure FVs before handing off control to DXE. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2017, Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TdxStartupInternal.h" + +#pragma pack (1) + +#define FV_HANDOFF_TABLE_DESC "Fv(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)" +typedef struct { + UINT8 BlobDescriptionSize; + UINT8 BlobDescription[sizeof(FV_HANDOFF_TABLE_DESC)]; + EFI_PHYSICAL_ADDRESS BlobBase; + UINT64 BlobLength; +} FV_HANDOFF_TABLE_POINTERS2; + +#pragma pack () + +/** + Add a new entry to the Event Log. + + @param[in] DigestList A list of digest. + @param[in,out] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure. + @param[in] NewEventData Pointer to the new event data. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. +**/ +EFI_STATUS +CreateTdxExtendEvent ( + IN TCG_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN UINT8 *EventData, + IN UINTN EventSize, + IN UINT8 *HashData, + IN UINTN HashDataLen + ) +{ + EFI_STATUS Status; + VOID *EventHobData; + TCG_PCR_EVENT2 *TcgPcrEvent2; + TDX_EVENT *TdxEvent; + UINT8 *DigestBuffer; + TDX_DIGEST_VALUE *TdxDigest; + + DEBUG ((EFI_D_INFO, "Creating Tcg2PcrEvent PCR %d EventType 0x%x\n", PCRIndex, EventType)); + + // + // Use TDX_DIGEST_VALUE in the GUID HOB DataLength calculation + // to reserve enough buffer to hold TPML_DIGEST_VALUES compact binary + // which is limited to a SHA384 digest list + // + EventHobData = BuildGuidHob ( + &gTcgEvent2EntryHobGuid, + sizeof(TcgPcrEvent2->PCRIndex) + sizeof(TcgPcrEvent2->EventType) + + sizeof(TDX_DIGEST_VALUE) + + sizeof(TcgPcrEvent2->EventSize) + EventSize + + sizeof(TDX_EVENT)); + + if (EventHobData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - data %p\n", EventHobData)); + + // + // Initialize PcrEvent data now + // + TcgPcrEvent2 = EventHobData; + TcgPcrEvent2->PCRIndex = PCRIndex; + TcgPcrEvent2->EventType = EventType; + + // + // We don't have a digest to copy yet, but we can to copy the eventsize/data now + // + DigestBuffer = (UINT8 *)&TcgPcrEvent2->Digest; + DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - digest %p\n", DigestBuffer)); + + TdxDigest = (TDX_DIGEST_VALUE *)DigestBuffer; + TdxDigest->count = 1; + TdxDigest->hashAlg = TPM_ALG_SHA384; + + DigestBuffer = DigestBuffer + sizeof(TDX_DIGEST_VALUE); + DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - eventdata %p\n", DigestBuffer)); + + CopyMem (DigestBuffer, &EventSize, sizeof(TcgPcrEvent2->EventSize)); + DigestBuffer = DigestBuffer + sizeof(TcgPcrEvent2->EventSize); + CopyMem (DigestBuffer, EventData, EventSize); + DigestBuffer = DigestBuffer + EventSize; + TdxEvent = (TDX_EVENT *)DigestBuffer; + + // + // Initialize the TdxEvent so we can perform measurement in DXE. + // During early DXE, the gTcgEvent2EntryHobGuid will be parsed, the data hashed, and TcgEvent2 hobs + // updated with the updated hash + // + //TdxEvent->Signature = TCG_TDX_EVENT_DATA_SIGNATURE; + TdxEvent->HashData = HashData; + TdxEvent->HashDataLen = HashDataLen; + + Status = EFI_SUCCESS; + return Status; +} + + +/** + Get the FvName from the FV header. + + Causion: The FV is untrusted input. + + @param[in] FvBase Base address of FV image. + @param[in] FvLength Length of FV image. + + @return FvName pointer + @retval NULL FvName is NOT found +**/ +VOID * +GetFvName ( + IN EFI_PHYSICAL_ADDRESS FvBase, + IN UINT64 FvLength + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FvHeader; + EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader; + + if (FvBase >= MAX_ADDRESS) { + return NULL; + } + if (FvLength >= MAX_ADDRESS - FvBase) { + return NULL; + } + if (FvLength < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { + return NULL; + } + + FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvBase; + if (FvHeader->ExtHeaderOffset < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { + return NULL; + } + if (FvHeader->ExtHeaderOffset + sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER) > FvLength) { + return NULL; + } + FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)(UINTN)(FvBase + FvHeader->ExtHeaderOffset); + + return &FvExtHeader->FvName; +} + +/** + Measure FV image. + Add it into the measured FV list after the FV is measured successfully. + + @param[in] FvBase Base address of FV image. + @param[in] FvLength Length of FV image. + @param[in] PcrIndex Index of PCR + + @retval EFI_SUCCESS Fv image is measured successfully + or it has been already measured. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + +**/ +EFI_STATUS +TdxMeasureFvImage ( + IN EFI_PHYSICAL_ADDRESS FvBase, + IN UINT64 FvLength, + IN UINT8 PcrIndex + ) +{ + EFI_STATUS Status; + FV_HANDOFF_TABLE_POINTERS2 FvBlob2; + VOID *FvName; + + // + // Init the log event for FV measurement + // + FvBlob2.BlobDescriptionSize = sizeof(FvBlob2.BlobDescription); + CopyMem (FvBlob2.BlobDescription, FV_HANDOFF_TABLE_DESC, sizeof(FvBlob2.BlobDescription)); + FvName = GetFvName (FvBase, FvLength); + if (FvName != NULL) { + AsciiSPrint ((CHAR8 *)FvBlob2.BlobDescription, sizeof(FvBlob2.BlobDescription), "Fv(%g)", FvName); + } + FvBlob2.BlobBase = FvBase; + FvBlob2.BlobLength = FvLength; + + // + // Hash the FV, extend digest to the TPM and log TCG event + // + Status = CreateTdxExtendEvent ( + PcrIndex, // PCRIndex + EV_EFI_PLATFORM_FIRMWARE_BLOB2, // EventType + (VOID *)&FvBlob2, // EventData + sizeof (FvBlob2), // EventSize + (UINT8*) (UINTN) FvBase, // HashData + (UINTN) FvLength // HashDataLen + ); + + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "The FV which failed to be measured starts at: 0x%x\n", FvBase)); + return Status; + } + return Status; +} + diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartup.c b/OvmfPkg/Library/TdxStartupLib/TdxStartup.c new file mode 100644 index 000000000000..747c422cd6ad --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartup.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TdxStartupInternal.h" + +volatile VOID *mMailBox = NULL; +UINT32 mNumOfCpus = 0; + +#define GET_GPAW_INIT_STATE(INFO) ((UINT8) ((INFO) & 0x3f)) + +volatile VOID * +EFIAPI +GetMailBox ( + VOID + ) +{ + return mMailBox; +} + +UINT32 +EFIAPI +GetNumCpus ( + VOID + ) +{ + return mNumOfCpus; +} + +/** + Validates the configuration volume, measures it, and created a FV Hob + + @param[in] VolumeAddress The base of the where the CFV must reside + + @retval None +**/ +VOID +EFIAPI +MeasureConfigurationVolume ( + IN UINT64 VolumeAddress + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *Fv; + UINT16 Expected; + UINT16 Checksum; + UINT32 CfvSize; + + ASSERT (VolumeAddress); + ASSERT (((UINTN) VolumeAddress & EFI_PAGE_MASK) == 0); + + Fv = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)VolumeAddress; + ASSERT (Fv->Signature == EFI_FVH_SIGNATURE ); + + // + // Validate Volume Checksum + // + Checksum = CalculateSum16 ((UINT16 *) Fv, Fv->HeaderLength); + + Expected = (UINT16) (((UINTN) Fv->Checksum + 0x10000 - Checksum) & 0xffff); + + DEBUG ((EFI_D_INFO, "FV@%p Checksum is 0x%x, expected 0x%x\n", + Fv, Fv->Checksum, Expected)); + + ASSERT (Fv->Checksum == Expected); + + // + // Add FvHob for the Volume + // + BuildFvHob ((UINTN)Fv, Fv->FvLength); + + // + // The configuration volume needs to be measured + // + CfvSize = PcdGet32 (PcdCfvRawDataSize); + TdxMeasureFvImage ((UINTN)Fv, CfvSize, 1); +} + +VOID +EFIAPI +TdxStartup( + IN VOID * Context, + IN VOID * VmmHobList, + IN UINTN Info, + IN fProcessLibraryConstructorList Function + ) +{ + EFI_SEC_PEI_HAND_OFF *SecCoreData; + EFI_FIRMWARE_VOLUME_HEADER *BootFv; + EFI_STATUS Status; + EFI_HOB_PLATFORM_INFO PlatformInfoHob; + VOID *Address; + VOID *ApLoopFunc = NULL; + UINT32 RelocationPages; + MP_RELOCATION_MAP RelocationMap; + MP_WAKEUP_MAILBOX *RelocatedMailBox; + UINT32 DxeCodeBase; + UINT32 DxeCodeSize; + TD_RETURN_DATA TdReturnData; + + Status = EFI_SUCCESS; + BootFv = NULL; + SecCoreData = (EFI_SEC_PEI_HAND_OFF *) Context; + + Status = TdCall (TDCALL_TDINFO, 0,0,0, &TdReturnData); + ASSERT (Status == EFI_SUCCESS); + mNumOfCpus = TdReturnData.TdInfo.NumVcpus; + mMailBox = (VOID *)(UINTN)PcdGet32 (PcdTdMailboxBase); + + DEBUG ((EFI_D_INFO, + "Tdx started with(Hob: 0x%x, Info: 0x%x, Cpus: %d, MailBox: 0x%x)\n", + (UINT32)(UINTN)VmmHobList, + (UINT32)(UINTN)Info, + mNumOfCpus, + (UINT32)(UINTN)mMailBox + )); + + ZeroMem (&PlatformInfoHob, sizeof (PlatformInfoHob)); + + // + // Validate HobList + // + if (ValidateHobList (VmmHobList) == FALSE) { + ASSERT (FALSE); + CpuDeadLoop (); + } + + // + // Process Hoblist for the TD + // + ProcessHobList (VmmHobList); + + // + // ProcessLibaryConstructorList + // + Function (NULL, NULL); + + // + // Tranfer the Hoblist to the final Hoblist for DXe + // + TransferHobList (VmmHobList); + + // + // Initialize Platform + // + TdvfPlatformInitialize (&PlatformInfoHob); + + // + // 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; + + Address = AllocatePagesWithMemoryType (EfiACPIMemoryNVS, RelocationPages); + ApLoopFunc = (VOID *) ((UINTN) Address + EFI_PAGE_SIZE); + + CopyMem ( + ApLoopFunc, + RelocationMap.RelocateApLoopFuncAddress, + RelocationMap.RelocateApLoopFuncSize + ); + + DEBUG ((DEBUG_INFO, "Ap Relocation: mailbox %p, 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; + + PlatformInfoHob.RelocatedMailBox = (UINT64)RelocatedMailBox; + + // + // Create and event log entry so VMM Hoblist can be measured + // + LogHobList (VmmHobList); + + // + // 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); + + // + // TDVF must not use any CpuHob from input HobList. + // It must create its own using GPWA from VMM and 0 for SizeOfIoSpace + // + BuildCpuHob (GET_GPAW_INIT_STATE(Info), 16); + + // + // SecFV + // + BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase; + BuildFvHob ((UINTN)BootFv, BootFv->FvLength); + + // + // DxeFV + // + DxeCodeBase = PcdGet32 (PcdBfvBase); + DxeCodeSize = PcdGet32 (PcdBfvRawDataSize) - (UINT32)BootFv->FvLength; + BuildFvHob (DxeCodeBase, DxeCodeSize); + + DEBUG ((DEBUG_INFO, "SecFv : %p, 0x%x\n", BootFv, BootFv->FvLength)); + DEBUG ((DEBUG_INFO, "DxeFv : %x, 0x%x\n", DxeCodeBase, DxeCodeSize)); + + MeasureConfigurationVolume ((UINT64)(UINTN)PcdGet32 (PcdCfvBase)); + + BuildGuidDataHob (&gUefiOvmfPkgTdxPlatformGuid, &PlatformInfoHob, sizeof (EFI_HOB_PLATFORM_INFO)); + + BuildStackHob ((UINTN)SecCoreData->StackBase, SecCoreData->StackSize <<=1 ); + + BuildResourceDescriptorHob ( + EFI_RESOURCE_SYSTEM_MEMORY, + EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | + EFI_RESOURCE_ATTRIBUTE_TESTED, + (UINT64)SecCoreData->TemporaryRamBase, + (UINT64)SecCoreData->TemporaryRamSize); + + BuildMemoryAllocationHob ( + FixedPcdGet32 (PcdTdMailboxBase), + EFI_PAGE_SIZE, + EfiACPIMemoryNVS + ); + + // + // Load the DXE Core and transfer control to it + // + Status = DxeLoadCore (1); + + ASSERT (FALSE); + CpuDeadLoop (); + +} + diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h new file mode 100644 index 000000000000..abb40e4e0be6 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h @@ -0,0 +1,198 @@ +#ifndef _TDX_STARTUP_INTERNAL_LIB__H_ +#define _TDX_STARTUP_INTERNAL_LIB__H_ + +#include +#include +#include +#include +#include +#include + +#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 Pad3[0x1f8]; + } 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; + +#define HANDOFF_TABLE_DESC "TdxTable" + typedef struct { + UINT8 TableDescriptionSize; + UINT8 TableDescription[sizeof(HANDOFF_TABLE_DESC)]; + UINT64 NumberOfTables; + EFI_CONFIGURATION_TABLE TableEntry[1]; + } TDX_HANDOFF_TABLE_POINTERS2; + + typedef struct { + UINT32 count; + TPMI_ALG_HASH hashAlg; + BYTE sha384[SHA384_DIGEST_SIZE]; + } TDX_DIGEST_VALUE; + +#pragma pack() + + typedef struct { + UINT32 Signature; + UINT8 *HashData; + UINTN HashDataLen; + } TDX_EVENT; + + +#define LOOPIT(X) do { \ + volatile int foo = (X); \ + while (foo) ; \ +} while(0) + + +volatile VOID * +EFIAPI +GetMailBox( + VOID + ); + +UINT32 +EFIAPI +GetNumCpus( + VOID + ); + +EFI_STATUS +EFIAPI +DxeLoadCore ( + IN INTN FvInstance + ); + +EFI_STATUS +EFIAPI +InitPcdPeim ( + IN INTN FvInstance + ); + +VOID +EFIAPI +MpSendWakeupCommand( + IN UINT16 Command, + IN UINT64 WakeupVector, + IN UINT64 WakeupArgs1, + IN UINT64 WakeupArgs2, + IN UINT64 WakeupArgs3, + IN UINT64 WakeupArgs4 +); + +VOID +EFIAPI +MpSerializeStart ( + VOID + ); + +VOID +EFIAPI +MpSerializeEnd ( + VOID + ); + +VOID +EFIAPI +MpAcceptMemoryResourceRange ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN EFI_PHYSICAL_ADDRESS PhysicalEnd + ); + +/** + 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 + ); + + +VOID +EFIAPI +ProcessHobList ( + IN CONST VOID *HobStart + ); + +VOID +EFIAPI +TransferHobList ( + IN CONST VOID *HobStart + ); + +VOID +EFIAPI +LogHobList ( + IN CONST VOID *HobStart + ); + +EFI_STATUS +TdxMeasureFvImage ( + IN EFI_PHYSICAL_ADDRESS FvBase, + IN UINT64 FvLength, + IN UINT8 PcrIndex + ); + +EFI_STATUS +CreateTdxExtendEvent ( + IN TCG_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN UINT8 *EventData, + IN UINTN EventSize, + IN UINT8 *HashData, + IN UINTN HashDataLen + ); + +VOID +EFIAPI +AsmGetRelocationMap ( + OUT MP_RELOCATION_MAP *AddressMap + ); + +#endif diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf new file mode 100644 index 000000000000..b76411d92759 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf @@ -0,0 +1,100 @@ +#/** @file +# Component description file for TDX Pre PI Library +# +# LIbrary helps you build a platform that skips PEI and loads DXE Core +# directly. Helps building HOBs, reading data from the FV, and doing +# decompression. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2008, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxStartupLib + FILE_GUID = 8FA74135-F841-40A4-86C8-69C923D2E85F + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxStartupLib|SEC + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + TdxStartup.c + Hob.c + Mp.c + DxeLoad.c + Tcg.c + +[Sources.X64] + X64/ApRunLoop.nasm + X64/VirtualMemory.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + OvmfPkg/OvmfPkg.dec + CryptoPkg/CryptoPkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib + PcdLib + UefiCpuLib + DebugAgentLib + IoLib + LocalApicLib + CpuExceptionHandlerLib + SynchronizationLib + HobLib + TdxLib + MemoryAllocationLib + TdvfPlatformLib + PrePiLib + +[Guids] + gEfiHobMemoryAllocModuleGuid + gEfiHobMemoryAllocStackGuid + gUefiOvmfPkgTdxPlatformGuid + gEfiMemoryTypeInformationGuid + gTcgEvent2EntryHobGuid + gPcdDataBaseHobGuid + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize + gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack + gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask + + // + // TODO check these PCDs' impact on Ovmf + // + gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplBuildPageTables ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf b/OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf new file mode 100644 index 000000000000..23600658f135 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf @@ -0,0 +1,40 @@ +#/** @file +# Component description file for TDX Pre PI Library +# +# LIbrary helps you build a platform that skips PEI and loads DXE Core +# directly. Helps building HOBs, reading data from the FV, and doing +# decompression. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2008, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxStartupLibNull + FILE_GUID = 79DD2160-D5E0-48CD-AA87-479EACEE8393 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxStartupLib|SEC + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + TdxStartupNull.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + +[Pcd] diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupNull.c b/OvmfPkg/Library/TdxStartupLib/TdxStartupNull.c new file mode 100644 index 000000000000..18b4e9ecdca0 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupNull.c @@ -0,0 +1,17 @@ +#include +#include +#include + +VOID +EFIAPI +TdxStartup( + IN VOID * Context, + IN VOID * VmmHobList, + IN UINTN Info, + IN fProcessLibraryConstructorList Function + ) +{ + ASSERT (FALSE); + CpuDeadLoop (); +} + diff --git a/OvmfPkg/Library/TdxStartupLib/X64/ApRunLoop.nasm b/OvmfPkg/Library/TdxStartupLib/X64/ApRunLoop.nasm new file mode 100644 index 000000000000..9cc5b7b340fa --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/X64/ApRunLoop.nasm @@ -0,0 +1,88 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015, Intel Corporation. All rights reserved.
+; 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 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66, 0x0f, 0x01, 0xcc +%endif +%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 + diff --git a/OvmfPkg/Library/TdxStartupLib/X64/PageTables.h b/OvmfPkg/Library/TdxStartupLib/X64/PageTables.h new file mode 100644 index 000000000000..f1d1005c9a6b --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/X64/PageTables.h @@ -0,0 +1,208 @@ +/** @file + x64 Long Mode Virtual Memory Management Definitions + + References: + 1) IA-32 Intel(R) Architecture Software Developer's Manual Volume 1:Basic Architecture, Intel + 2) IA-32 Intel(R) Architecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel + 3) IA-32 Intel(R) Architecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel + 4) AMD64 Architecture Programmer's Manual Volume 2: System Programming + +Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef _PAGE_TABLES_H_ +#define _PAGE_TABLES_H_ + + +#define SYS_CODE64_SEL 0x38 + + +#pragma pack(1) + +typedef union { + struct { + UINT32 LimitLow : 16; + UINT32 BaseLow : 16; + UINT32 BaseMid : 8; + UINT32 Type : 4; + UINT32 System : 1; + UINT32 Dpl : 2; + UINT32 Present : 1; + UINT32 LimitHigh : 4; + UINT32 Software : 1; + UINT32 Reserved : 1; + UINT32 DefaultSize : 1; + UINT32 Granularity : 1; + UINT32 BaseHigh : 8; + } Bits; + UINT64 Uint64; +} IA32_GDT; + +typedef struct { + IA32_IDT_GATE_DESCRIPTOR Ia32IdtEntry; + UINT32 Offset32To63; + UINT32 Reserved; +} X64_IDT_GATE_DESCRIPTOR; + +// +// 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 CR0_WP BIT16 + +#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 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; + +UINTN +CreateIdentityMappingPageTables ( + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ); + +/** + Clear legacy memory located at the first 4K-page. + + This function traverses the whole HOB list to check if memory from 0 to 4095 + exists and has not been allocated, and then clear it if so. + + @param HobStart The start of HobList passed to DxeCore. + +**/ +VOID +ClearFirst4KPage ( + IN VOID *HobStart + ); + +/** + Return configure status of NULL pointer detection feature. + + @return TRUE NULL pointer detection feature is enabled + @return FALSE NULL pointer detection feature is disabled +**/ +BOOLEAN +IsNullDetectionEnabled ( + VOID + ); + + +#endif diff --git a/OvmfPkg/Library/TdxStartupLib/X64/VirtualMemory.c b/OvmfPkg/Library/TdxStartupLib/X64/VirtualMemory.c new file mode 100644 index 000000000000..ebdaa85b7c23 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/X64/VirtualMemory.c @@ -0,0 +1,896 @@ +/** @file + x64-specifc functionality for Page Table Setup. + +Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "PageTables.h" + +// +// Global variable to keep track current available memory used as page table. +// +PAGE_TABLE_POOL *mPageTablePool = NULL; + +UINTN mLevelShift[5] = { + 0, + PAGING_L1_ADDRESS_SHIFT, + PAGING_L2_ADDRESS_SHIFT, + PAGING_L3_ADDRESS_SHIFT, + PAGING_L4_ADDRESS_SHIFT +}; + +UINT64 mLevelMask[5] = { + 0, + PAGING_4K_ADDRESS_MASK_64, + PAGING_2M_ADDRESS_MASK_64, + PAGING_1G_ADDRESS_MASK_64, + PAGING_1G_ADDRESS_MASK_64 +}; + +UINT64 mLevelSize[5] = { + 0, + SIZE_4KB, + SIZE_2MB, + SIZE_1GB, + SIZE_512GB +}; + +/** + Clear legacy memory located at the first 4K-page, if available. + + This function traverses the whole HOB list to check if memory from 0 to 4095 + exists and has not been allocated, and then clear it if so. + + @param HobStart The start of HobList passed to DxeCore. + +**/ +VOID +ClearFirst4KPage ( + IN VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS RscHob; + EFI_PEI_HOB_POINTERS MemHob; + BOOLEAN DoClear; + + RscHob.Raw = HobStart; + MemHob.Raw = HobStart; + DoClear = FALSE; + + // + // Check if page 0 exists and free + // + while ((RscHob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, + RscHob.Raw)) != NULL) { + if (RscHob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY && + RscHob.ResourceDescriptor->PhysicalStart == 0) { + DoClear = TRUE; + // + // Make sure memory at 0-4095 has not been allocated. + // + while ((MemHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, + MemHob.Raw)) != NULL) { + if (MemHob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress + < EFI_PAGE_SIZE) { + DoClear = FALSE; + break; + } + MemHob.Raw = GET_NEXT_HOB (MemHob); + } + break; + } + RscHob.Raw = GET_NEXT_HOB (RscHob); + } + + if (DoClear) { + DEBUG ((DEBUG_INFO, "Clearing first 4K-page!\r\n")); + SetMem (NULL, EFI_PAGE_SIZE, 0); + } + + return; +} + +/** + Return configure status of NULL pointer detection feature. + + @return TRUE NULL pointer detection feature is enabled + @return FALSE NULL pointer detection feature is disabled + +**/ +BOOLEAN +IsNullDetectionEnabled ( + VOID + ) +{ + return ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) != 0); +} + +/** + The function will check if Execute Disable Bit is available. + + @retval TRUE Execute Disable Bit is available. + @retval FALSE Execute Disable Bit is not available. + +**/ +BOOLEAN +IsExecuteDisableBitAvailable ( + VOID + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + BOOLEAN Available; + + Available = FALSE; + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT20) != 0) { + // + // Bit 20: Execute Disable Bit available. + // + Available = TRUE; + } + } + + return Available; +} + +/** + Check if Execute Disable Bit (IA32_EFER.NXE) should be enabled or not. + + @retval TRUE IA32_EFER.NXE should be enabled. + @retval FALSE IA32_EFER.NXE should not be enabled. + +**/ +BOOLEAN +IsEnableNonExecNeeded ( + VOID + ) +{ + + if (!IsExecuteDisableBitAvailable ()) { + 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. + // + return (FixedPcdGetBool(PcdTdxSetNxForStack) || + FixedPcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0 || + PcdGet32 (PcdImageProtectionPolicy) != 0); +} + +/** + Enable Execute Disable Bit. + +**/ +VOID +EnableExecuteDisableBit ( + VOID + ) +{ + UINT64 MsrRegisters; + + MsrRegisters = AsmReadMsr64 (0xC0000080); + MsrRegisters |= BIT11; + AsmWriteMsr64 (0xC0000080, MsrRegisters); +} + +/** + The function will check if page table entry should be splitted to smaller + granularity. + + @param Address Physical memory address. + @param Size Size of the given physical memory. + @param StackBase Base address of stack. + @param StackSize Size of stack. + + @retval TRUE Page table should be split. + @retval FALSE Page table should not be split. +**/ +BOOLEAN +ToSplitPageTable ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN Size, + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + if (IsNullDetectionEnabled () && Address == 0) { + return TRUE; + } + + if (FixedPcdGetBool (PcdCpuStackGuard)) { + if (StackBase >= Address && StackBase < (Address + Size)) { + return TRUE; + } + } + + if (FixedPcdGetBool(PcdTdxSetNxForStack)) { + if ((Address < StackBase + StackSize) && ((Address + Size) > StackBase)) { + return TRUE; + } + } + + return FALSE; +} +/** + 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. But usually this won't + happen in practice. + + @param 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. +**/ +BOOLEAN +InitializePageTablePool ( + IN UINTN PoolPages + ) +{ + VOID *Buffer; + + DEBUG((DEBUG_INFO, "InitializePageTablePool PoolPages=%d\n", PoolPages)); + + // + // 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. + +**/ +VOID * +AllocatePageTableMemory ( + IN UINTN Pages + ) +{ + VOID *Buffer; + + if (Pages == 0) { + return NULL; + } + + DEBUG((DEBUG_INFO, "AllocatePageTableMemory. mPageTablePool=%p, Pages=%d\n", mPageTablePool, Pages)); + // + // 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_INFO, + "%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. + +**/ +VOID +Split2MPageTo4K ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN OUT UINT64 *PageEntry2M, + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + EFI_PHYSICAL_ADDRESS PhysicalAddress4K; + UINTN IndexOfPageTableEntries; + PAGE_TABLE_4K_ENTRY *PageTableEntry; + UINT64 AddressEncMask; + + DEBUG((DEBUG_INFO, "Split2MPageTo4K\n")); + // + // Make sure AddressEncMask is contained to smallest supported address field + // + AddressEncMask = 0; + + PageTableEntry = AllocatePageTableMemory (1); + ASSERT (PageTableEntry != NULL); + + // + // Fill in 2M page entry. + // + *PageEntry2M = (UINT64) (UINTN) PageTableEntry | AddressEncMask | IA32_PG_P | IA32_PG_RW; + + 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; + + if ((IsNullDetectionEnabled () && PhysicalAddress4K == 0) || + (FixedPcdGetBool (PcdCpuStackGuard) && PhysicalAddress4K == StackBase)) { + PageTableEntry->Bits.Present = 0; + } else { + PageTableEntry->Bits.Present = 1; + } + + if (FixedPcdGetBool(PcdTdxSetNxForStack) + && (PhysicalAddress4K >= StackBase) + && (PhysicalAddress4K < StackBase + StackSize)) { + // + // Set Nx bit for stack. + // + PageTableEntry->Bits.Nx = 1; + } + } +} + +/** + 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. + +**/ +VOID +Split1GPageTo2M ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN OUT UINT64 *PageEntry1G, + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + EFI_PHYSICAL_ADDRESS PhysicalAddress2M; + UINTN IndexOfPageDirectoryEntries; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + UINT64 AddressEncMask; + + // + // Make sure AddressEncMask is contained to smallest supported address field + // + AddressEncMask = FixedPcdGet64 (PcdTdxPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + PageDirectoryEntry = AllocatePageTableMemory (1); + ASSERT (PageDirectoryEntry != NULL); + + // + // Fill in 1G page entry. + // + *PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | AddressEncMask | IA32_PG_P | IA32_PG_RW; + + PhysicalAddress2M = PhysicalAddress; + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) { + if (ToSplitPageTable (PhysicalAddress2M, SIZE_2MB, StackBase, StackSize)) { + // + // Need to split this 2M page that covers NULL or stack range. + // + Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize); + } else { + // + // Fill in the Page Directory entries + // + PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | AddressEncMask; + PageDirectoryEntry->Bits.ReadWrite = 1; + PageDirectoryEntry->Bits.Present = 1; + PageDirectoryEntry->Bits.MustBe1 = 1; + } + } +} + +/** + 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. + +**/ +VOID +SetPageTablePoolReadOnly ( + IN UINTN PageTableBase, + IN EFI_PHYSICAL_ADDRESS Address, + IN BOOLEAN Level4Paging + ) +{ + UINTN Index; + UINTN EntryIndex; + UINT64 AddressEncMask; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + UINT64 *PageTable; + UINT64 *NewPageTable; + UINT64 PageAttr; + UINTN Level; + UINT64 PoolUnitSize; + + ASSERT (PageTableBase != 0); + + // + // 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; + + AddressEncMask = FixedPcdGet64 (PcdTdxPteMemoryEncryptionAddressOrMask) & + 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, mLevelShift[Level])); + Index &= PAGING_PAE_INDEX_MASK; + + PageAttr = PageTable[Index]; + 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 >= mLevelSize[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 -= mLevelSize[Level]; + + ++Index; + } + } + + break; + + } else { + // + // The smaller granularity of page must be needed. + // + ASSERT (Level > 1); + + NewPageTable = AllocatePageTableMemory (1); + ASSERT (NewPageTable != NULL); + + PhysicalAddress = PageAttr & mLevelMask[Level]; + for (EntryIndex = 0; + EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64); + ++EntryIndex) { + NewPageTable[EntryIndex] = PhysicalAddress | AddressEncMask | + IA32_PG_P | IA32_PG_RW; + if (Level > 2) { + NewPageTable[EntryIndex] |= IA32_PG_PS; + } + PhysicalAddress += mLevelSize[Level - 1]; + } + + PageTable[Index] = (UINT64)(UINTN)NewPageTable | AddressEncMask | + 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. + +**/ +VOID +EnablePageTableProtection ( + IN UINTN PageTableBase, + IN BOOLEAN Level4Paging + ) +{ + PAGE_TABLE_POOL *HeadPool; + PAGE_TABLE_POOL *Pool; + UINT64 PoolSize; + EFI_PHYSICAL_ADDRESS Address; + + DEBUG((DEBUG_INFO, "EnablePageTableProtection\n")); + + if (mPageTablePool == NULL) { + return; + } + + // + // Disable write protection, because we need to mark page table to be write + // protected. + // + AsmWriteCr0 (AsmReadCr0() & ~CR0_WP); + + // + // 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); + + // + // Enable write protection, after page table attribute updated. + // + AsmWriteCr0 (AsmReadCr0() | CR0_WP); +} + +/** + Allocates and fills in the Page Directory and Page Table Entries to + establish a 1:1 Virtual to Physical mapping. + + @param[in] StackBase Stack base address. + @param[in] StackSize Stack size. + + @return The address of 4 level page map. + +**/ +UINTN +CreateIdentityMappingPageTables ( + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + UINT8 PhysicalAddressBits; + EFI_PHYSICAL_ADDRESS PageAddress; + UINTN IndexOfPml5Entries; + UINTN IndexOfPml4Entries; + UINTN IndexOfPdpEntries; + UINTN IndexOfPageDirectoryEntries; + UINT32 NumberOfPml5EntriesNeeded; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel5Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMap; + PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + UINTN TotalPagesNum; + UINTN BigPageAddress; + VOID *Hob; + BOOLEAN Page5LevelSupport; + BOOLEAN Page1GSupport; + PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry; + UINT64 AddressEncMask; + IA32_CR4 Cr4; + + // + // Set PageMapLevel5Entry to suppress incorrect compiler/analyzer warnings + // + PageMapLevel5Entry = NULL; + + // + // Make sure AddressEncMask is contained to smallest supported address field + // + AddressEncMask = FixedPcdGet64 (PcdTdxPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + Page1GSupport = FALSE; + if (FixedPcdGetBool(PcdUse1GPageTable)) { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + Page1GSupport = TRUE; + } + } + } + + // + // Get physical address bits supported. + // + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + ASSERT(Hob != NULL); + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + + // + // CPU will already have LA57 enabled so just check CR4 + Cr4.UintN = AsmReadCr4 (); + Page5LevelSupport = (Cr4.Bits.LA57 ? TRUE : FALSE); + + DEBUG ((DEBUG_INFO, "AddressBits=%u 5LevelPaging=%u 1GPage=%u AddressEncMask=0x%llx\n", + PhysicalAddressBits, Page5LevelSupport, + Page1GSupport, AddressEncMask)); + + // + // Calculate the table entries needed. + // + NumberOfPml5EntriesNeeded = 1; + if (PhysicalAddressBits > 48) { + NumberOfPml5EntriesNeeded = (UINT32) LShiftU64 (1, PhysicalAddressBits - 48); + PhysicalAddressBits = 48; + } + + NumberOfPml4EntriesNeeded = 1; + if (PhysicalAddressBits > 39) { + NumberOfPml4EntriesNeeded = (UINT32) LShiftU64 (1, PhysicalAddressBits - 39); + PhysicalAddressBits = 39; + } + + NumberOfPdpEntriesNeeded = 1; + ASSERT (PhysicalAddressBits > 30); + NumberOfPdpEntriesNeeded = (UINT32) LShiftU64 (1, PhysicalAddressBits - 30); + + // + // Pre-allocate big pages to avoid later allocations. + // + if (!Page1GSupport) { + TotalPagesNum = ((NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1) * NumberOfPml5EntriesNeeded + 1; + } else { + TotalPagesNum = (NumberOfPml4EntriesNeeded + 1) * NumberOfPml5EntriesNeeded + 1; + } + + // + // Substract the one page occupied by PML5 entries if 5-Level Paging is disabled. + // + if (!Page5LevelSupport) { + TotalPagesNum--; + } + + DEBUG ((DEBUG_INFO, "Pml5=%u Pml4=%u Pdp=%u TotalPage=%Lu\n", + NumberOfPml5EntriesNeeded, NumberOfPml4EntriesNeeded, + NumberOfPdpEntriesNeeded, (UINT64)TotalPagesNum)); + + BigPageAddress = (UINTN) AllocatePageTableMemory (TotalPagesNum); + ASSERT (BigPageAddress != 0); + + DEBUG((DEBUG_INFO, "BigPageAddress = 0x%llx\n", BigPageAddress)); + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + PageMap = (VOID *) BigPageAddress; + if (Page5LevelSupport) { + // + // By architecture only one PageMapLevel5 exists - so lets allocate storage for it. + // + PageMapLevel5Entry = PageMap; + BigPageAddress += SIZE_4KB; + } + PageAddress = 0; + + for ( IndexOfPml5Entries = 0 + ; IndexOfPml5Entries < NumberOfPml5EntriesNeeded + ; IndexOfPml5Entries++) { + + // + // Each PML5 entry points to a page of PML4 entires. + // So lets allocate space for them and fill them in in the IndexOfPml4Entries loop. + // When 5-Level Paging is disabled, below allocation happens only once. + // + PageMapLevel4Entry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + if (Page5LevelSupport) { + // + // Make a PML5 Entry + // + PageMapLevel5Entry->Uint64 = (UINT64) (UINTN) PageMapLevel4Entry | AddressEncMask; + PageMapLevel5Entry->Bits.ReadWrite = 1; + PageMapLevel5Entry->Bits.Present = 1; + PageMapLevel5Entry++; + } + + for ( IndexOfPml4Entries = 0 + ; IndexOfPml4Entries < (NumberOfPml5EntriesNeeded == 1 ? NumberOfPml4EntriesNeeded : 512) + ; IndexOfPml4Entries++, PageMapLevel4Entry++) { + // + // Each PML4 entry points to a page of Page Directory Pointer entires. + // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop. + // + PageDirectoryPointerEntry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + // + // Make a PML4 Entry + // + PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask; + PageMapLevel4Entry->Bits.ReadWrite = 1; + PageMapLevel4Entry->Bits.Present = 1; + + if (Page1GSupport) { + PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) { + if (ToSplitPageTable (PageAddress, SIZE_1GB, StackBase, StackSize)) { + Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, + StackBase, StackSize); + } else { + // + // Fill in the Page Directory entries + // + PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectory1GEntry->Bits.ReadWrite = 1; + PageDirectory1GEntry->Bits.Present = 1; + PageDirectory1GEntry->Bits.MustBe1 = 1; + } + } + } else { + for ( IndexOfPdpEntries = 0 + ; IndexOfPdpEntries < (NumberOfPml4EntriesNeeded == 1 ? NumberOfPdpEntriesNeeded : 512) + ; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { + // + // Each Directory Pointer entries points to a page of Page Directory entires. + // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop. + // + PageDirectoryEntry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + // + // Fill in a Page Directory Pointer Entries + // + PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask; + PageDirectoryPointerEntry->Bits.ReadWrite = 1; + PageDirectoryPointerEntry->Bits.Present = 1; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) { + if (ToSplitPageTable (PageAddress, SIZE_2MB, StackBase, StackSize)) { + // + // Need to split this 2M page that covers NULL or stack range. + // + Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize); + } else { + // + // Fill in the Page Directory entries + // + PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectoryEntry->Bits.ReadWrite = 1; + PageDirectoryEntry->Bits.Present = 1; + PageDirectoryEntry->Bits.MustBe1 = 1; + } + } + } + + // + // Fill with null entry for unused PDPTE + // + ZeroMem (PageDirectoryPointerEntry, (512 - IndexOfPdpEntries) * sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)); + } + } + + // + // For the PML4 entries we are not using fill in a null entry. + // + ZeroMem (PageMapLevel4Entry, (512 - IndexOfPml4Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)); + } + + if (Page5LevelSupport) { + // + // For the PML5 entries we are not using fill in a null entry. + // + ZeroMem (PageMapLevel5Entry, (512 - IndexOfPml5Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)); + } + + // + // Protect the page table by marking the memory used for page table to be + // read-only. + // + EnablePageTableProtection ((UINTN)PageMap, TRUE); + + // + // Set IA32_EFER.NXE if necessary. + // + if (IsEnableNonExecNeeded ()) { + // + // ASSERT for now, TDX doesn't allow us to change EFER + // + ASSERT(FALSE); + EnableExecuteDisableBit (); + } + + return (UINTN)PageMap; +} From 6047863f5f873a5fbd3538089a75ed4ca50be0d7 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 19:30:58 +0800 Subject: [PATCH 20/54] OvmfPkg: Update SecMain.c to startup Td guest --- OvmfPkg/OvmfPkg.dec | 14 +++ OvmfPkg/OvmfPkgIa32.dsc | 1 + OvmfPkg/OvmfPkgIa32X64.dsc | 1 + OvmfPkg/OvmfPkgX64.dsc | 25 ++++++ OvmfPkg/Sec/SecMain.c | 169 +++++++++++++++++++++++++++++++++++++ OvmfPkg/Sec/SecMain.inf | 2 + 6 files changed, 212 insertions(+) diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 7edb9ba6f1a4..c2d5ba6964b9 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -118,6 +118,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 @@ -333,7 +334,16 @@ gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x54 gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|0x0|UINT32|0x55 + + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxMsr|TRUE|BOOLEAN|0x56 + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x58 + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize|0x2000|UINT64|0x59 + gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask|FALSE|BOOLEAN|0x5a + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack|FALSE|BOOLEAN|0x5b + gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask|0|UINT64|0x5c + + [PcdsDynamic, PcdsDynamicEx] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10 @@ -369,6 +379,10 @@ # This PCD is only accessed if PcdSmmSmramRequire is TRUE (see below). gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE|BOOLEAN|0x34 + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSharedPageMask|0x0|UINT64|0x101 + gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase|0|UINT64|0x102 + + [PcdsFeatureFlag] gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index b337dd9c7032..cc1c43e4dc71 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -240,6 +240,7 @@ VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf TdxLib|MdePkg/Library/TdxLib/TdxLibNull.inf TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf + TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf [LibraryClasses.common.SEC] diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 33e2ef60490d..468456074234 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -243,6 +243,7 @@ BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf TdxLib|MdePkg/Library/TdxLib/TdxLibNull.inf + TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index c6f2a193f5d3..ef07f47e933d 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -96,6 +96,13 @@ INTEL:*_*_*_CC_FLAGS = /D DISABLE_NEW_DEPRECATED_INTERFACES GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES + # + # TDX Virtual Firmware + # + MSFT:*_*_*_CC_FLAGS = /D TDX_VIRTUAL_FIRMWARE + INTEL:*_*_*_CC_FLAGS = /D TDX_VIRTUAL_FIRMWARE + GCC:*_*_*_CC_FLAGS = -D TDX_VIRTUAL_FIRMWARE + !include NetworkPkg/NetworkBuildOptions.dsc.inc [BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER] @@ -277,6 +284,7 @@ MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf TdvfPlatformLib|OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf PrePiLib|OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf + TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf [LibraryClasses.common.PEI_CORE] HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf @@ -582,6 +590,23 @@ !if $(TDX_IGNORE_VE_HLT) == TRUE gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|TRUE !endif +!if $(TDX_DISABLE_SHARED_MASK) == TRUE + gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask|TRUE +!endif + # 32M + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize|0x2000000 + + # Noexec settings for DXE. + # TDX doesn't allow us to change EFER so make sure these are disabled + gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy|0x00000000 + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy|0x00000000 + # Noexec settings for DXE. + # TDX doesn't allow us to change EFER so make sure these are disabled + #gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack|FALSE + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable|TRUE + + # Set memory encryption mask + gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask|0x0 # # Network Pcds diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c index 9db67e17b2aa..04e4f14514e1 100644 --- a/OvmfPkg/Sec/SecMain.c +++ b/OvmfPkg/Sec/SecMain.c @@ -29,9 +29,24 @@ #include #include #include +#include +#include #include +#if defined (MDE_CPU_X64) +BOOLEAN mTdxSupported = FALSE; + +typedef struct _TDX_WORK_AREA{ + UINT8 TdxIsEnabled; + UINT8 PageLevel5; // 5 page level is supported + UINT8 Rsvd[6]; + + UINT32 TdxInitVp; + UINT32 Info; +}TDX_WORK_AREA; +#endif + #define SEC_IDT_ENTRY_COUNT 34 typedef struct _SEC_IDT_TABLE { @@ -831,6 +846,137 @@ SevEsIsEnabled ( return ((SevEsWorkArea != NULL) && (SevEsWorkArea->SevEsEnabled != 0)); } +VOID +EFIAPI +InitializeSecCoreData( + IN EFI_SEC_PEI_HAND_OFF *SecCoreData, + IN EFI_FIRMWARE_VOLUME_HEADER *BootFv, + IN VOID *TopOfCurrentStack +) +{ + // + // |-------------| <-- TopOfCurrentStack + // | Stack | 32k + // |-------------| + // | Heap | 32k + // |-------------| <-- SecCoreData.TemporaryRamBase + // + + ASSERT ((UINTN) (PcdGet32 (PcdOvmfSecPeiTempRamBase) + + PcdGet32 (PcdOvmfSecPeiTempRamSize)) == + (UINTN) TopOfCurrentStack); + + // + // Initialize SEC hand-off state + // + SecCoreData->DataSize = sizeof(EFI_SEC_PEI_HAND_OFF); + + SecCoreData->TemporaryRamSize = (UINTN) PcdGet32 (PcdOvmfSecPeiTempRamSize); + SecCoreData->TemporaryRamBase = (VOID*)((UINT8 *)TopOfCurrentStack - SecCoreData->TemporaryRamSize); + + SecCoreData->PeiTemporaryRamBase = SecCoreData->TemporaryRamBase; + SecCoreData->PeiTemporaryRamSize = SecCoreData->TemporaryRamSize >> 1; + + SecCoreData->StackBase = (UINT8 *)SecCoreData->TemporaryRamBase + SecCoreData->PeiTemporaryRamSize; + SecCoreData->StackSize = SecCoreData->TemporaryRamSize >> 1; + + SecCoreData->BootFirmwareVolumeBase = BootFv; + SecCoreData->BootFirmwareVolumeSize = (UINTN) BootFv->FvLength; +} + + +#ifdef TDX_VIRTUAL_FIRMWARE +/** + Determine if TDX is supported + + During early booting, TDX support code will set a flag to indicate that + TDX is supported. Some more information are set in TDX_WORK_AREA if TDX + is supported. + + @retval TRUE TDX is supported + @retval FALSE TDX is not supported +**/ +BOOLEAN +CheckTdxSupported( + IN OUT VOID** TdInitVp, + IN OUT UINTN* TdInfo, + IN OUT UINT8* PageLevel5 + ) +{ + BOOLEAN Supported; + TDX_WORK_AREA *TdxWorkArea; + UINT32 TdMailboxBase; + + TdMailboxBase = FixedPcdGet32 (PcdTdMailboxBase); + if (TdMailboxBase == 0) { + return FALSE; + } + + TdxWorkArea = (TDX_WORK_AREA *) ((UINTN)(TdMailboxBase + 0x10)); + Supported = (TdxWorkArea != NULL) && (TdxWorkArea->TdxIsEnabled != 0); + + if (Supported) { + *TdInitVp = (VOID*)(UINTN)TdxWorkArea->TdxInitVp; + *TdInfo = (UINTN)TdxWorkArea->Info; + *PageLevel5 = TdxWorkArea->PageLevel5; + } + + return Supported; +} + +EFI_STATUS +EFIAPI +TdxInitialize ( + IN VOID *Context, + IN EFI_FIRMWARE_VOLUME_HEADER *BootFv, + IN VOID *TopOfCurrentStack + ) +{ + EFI_SEC_PEI_HAND_OFF *SecCoreData; + SEC_IDT_TABLE IdtTableInStack; + IA32_DESCRIPTOR IdtDescriptor; + UINT32 Index; + + SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context; + + IdtTableInStack.PeiService = NULL; + for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) { + CopyMem (&IdtTableInStack.IdtTable[Index], &mIdtEntryTemplate, sizeof (mIdtEntryTemplate)); + } + + IdtDescriptor.Base = (UINTN)&IdtTableInStack.IdtTable; + IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1); + + AsmWriteIdtr(&IdtDescriptor); + InitializeCpuExceptionHandlers(NULL); + + DEBUG ((DEBUG_INFO, + "SecCoreStartupWithStack(0x%x, 0x%x)\n", + (UINT32)(UINTN)BootFv, + (UINT32)(UINTN)TopOfCurrentStack + )); + + // + // Initialize SEC hand-off state + // + InitializeSecCoreData(SecCoreData, BootFv, TopOfCurrentStack); + + IoWrite8 (0x21, 0xff); + IoWrite8 (0xA1, 0xff); + + // + // Initialize Local APIC Timer hardware and disable Local APIC Timer + // interrupts before initializing the Debug Agent and the debug timer is + // enabled. + // + InitializeApicTimer (0, MAX_UINT32, TRUE, 5); + DisableApicTimerInterrupt (); + + return EFI_SUCCESS; +} + +#endif + VOID EFIAPI SecCoreStartupWithStack ( @@ -844,6 +990,29 @@ SecCoreStartupWithStack ( UINT32 Index; volatile UINT8 *Table; +#ifdef TDX_VIRTUAL_FIRMWARE + VOID *TdInitVp; + UINTN TdInfo; + UINT8 PageLevel5; + + // + // To check whether it is of Tdx Guest + // + mTdxSupported = CheckTdxSupported(&TdInitVp, &TdInfo, &PageLevel5); + + if(mTdxSupported){ + + TdxInitialize(&SecCoreData, BootFv, TopOfCurrentStack); + + DEBUG((DEBUG_INFO, "WorkArea: 0x%x, 0x%x, %d\n", TdInitVp, TdInfo, PageLevel5)); + + TdxStartup(&SecCoreData, TdInitVp, TdInfo, ProcessLibraryConstructorList); + + ASSERT(FALSE); + 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 diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf index 471d553fe104..8249bd3d4ec9 100644 --- a/OvmfPkg/Sec/SecMain.inf +++ b/OvmfPkg/Sec/SecMain.inf @@ -51,6 +51,8 @@ ExtractGuidedSectionLib LocalApicLib CpuExceptionHandlerLib + TdxStartupLib + TdxLib [Ppis] gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED From 6dfd41a14fc4c041f7e2a4a5a6c7beb9d927e41d Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 19:33:43 +0800 Subject: [PATCH 21/54] OvmfPkg: Enable DEBUG_ON_SERIAL_PORT to logout debug info --- OvmfPkg/OvmfPkgX64.dsc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index ef07f47e933d..65ef52e34878 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -550,7 +550,12 @@ # DEBUG_VERBOSE 0x00400000 // Detailed debug messages that may # // significantly impact boot performance # DEBUG_ERROR 0x80000000 // Error +!ifdef $(DEBUG_ON_SERIAL_PORT) + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xff1fff4f +!else gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000004F +!endif + !if $(TDX_EMULATION_ENABLE) == TRUE gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|1 !endif From f73899335ea510f572cac9eb3c6b91bdf39530a5 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 19:49:57 +0800 Subject: [PATCH 22/54] MdeModulePkg: Update Core/Dxe to support Tdx This commit should be split into small ones later. --- MdeModulePkg/Core/Dxe/DxeMain.h | 2 ++ MdeModulePkg/Core/Dxe/DxeMain.inf | 1 + MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c | 8 +++++ MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c | 14 ++++++-- MdeModulePkg/Core/Dxe/Gcd/Gcd.c | 36 ++++++++++++++++--- MdeModulePkg/Core/Dxe/Mem/Page.c | 23 ++++++++++-- MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 17 +++++++-- 7 files changed, 89 insertions(+), 12 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/DxeMain.h b/MdeModulePkg/Core/Dxe/DxeMain.h index 9bd3c0d08411..3562d833e707 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.h +++ b/MdeModulePkg/Core/Dxe/DxeMain.h @@ -289,6 +289,8 @@ extern EFI_RUNTIME_ARCH_PROTOCOL gRuntimeTemplate; extern EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE gLoadModuleAtFixAddressConfigurationTable; extern BOOLEAN gLoadFixedAddressCodeMemoryReady; + +extern BOOLEAN gTdGuest; // // Service Initialization Functions // diff --git a/MdeModulePkg/Core/Dxe/DxeMain.inf b/MdeModulePkg/Core/Dxe/DxeMain.inf index e4bca895773d..fb2218e062c4 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.inf +++ b/MdeModulePkg/Core/Dxe/DxeMain.inf @@ -94,6 +94,7 @@ DebugAgentLib CpuExceptionHandlerLib PcdLib + TdxProbeLib [Guids] gEfiEventMemoryMapChangeGuid ## PRODUCES ## Event diff --git a/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c b/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c index db21311f9352..e124a5d8428f 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c +++ b/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c @@ -7,6 +7,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "DxeMain.h" +#include + +BOOLEAN gTdGuest = FALSE; // // DXE Core Global Variables for Protocols from PEI @@ -244,6 +247,11 @@ DxeMain ( EFI_VECTOR_HANDOFF_INFO *VectorInfo; VOID *EntryPoint; + // + // Check whether it is of Td guest + // + gTdGuest = ProbeTdGuest(); + // // Setup the default exception handlers // diff --git a/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c b/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c index 8dcbbeb5ee5a..09cf88c200b4 100644 --- a/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c +++ b/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c @@ -1,7 +1,7 @@ /** @file Implements functions to read firmware file -Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -128,6 +128,7 @@ FvGetNextFile ( UINTN *KeyValue; LIST_ENTRY *Link; FFS_FILE_LIST_ENTRY *FfsFileEntry; + UINTN MaxFileType; FvDevice = FV_DEVICE_FROM_THIS (This); @@ -143,7 +144,16 @@ FvGetNextFile ( return EFI_ACCESS_DENIED; } - if (*FileType > EFI_FV_FILETYPE_MM_CORE_STANDALONE) { + // + // Td guest doesn't support SMM + // + if(gTdGuest) { + MaxFileType = EFI_FV_FILETYPE_SMM_CORE; + } else { + MaxFileType = EFI_FV_FILETYPE_MM_CORE_STANDALONE; + } + + if (*FileType > MaxFileType) { // // File type needs to be in 0 - 0x0F // diff --git a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c index 51b082b7e7eb..1b9d591754c7 100644 --- a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c +++ b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c @@ -35,6 +35,14 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #define PRESENT_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT) +// TDX +#define EXCLUSIVE_MEMORY_ATTRIBUTES (EFI_MEMORY_UC | EFI_MEMORY_WC | \ + EFI_MEMORY_WT | EFI_MEMORY_WB | \ + EFI_MEMORY_WP | EFI_MEMORY_UCE) + +#define NONEXCLUSIVE_MEMORY_ATTRIBUTES (EFI_MEMORY_XP | EFI_MEMORY_RP | \ + EFI_MEMORY_RO) + // // Module Variables // @@ -74,6 +82,9 @@ EFI_GCD_MAP_ENTRY mGcdIoSpaceMapEntryTemplate = { NULL, NULL }; +// TD +#define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000 + GCD_ATTRIBUTE_CONVERSION_ENTRY mAttributeConversionTable[] = { { EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE, EFI_MEMORY_UC, TRUE }, @@ -90,6 +101,7 @@ GCD_ATTRIBUTE_CONVERSION_ENTRY mAttributeConversionTable[] = { { EFI_RESOURCE_ATTRIBUTE_TESTED, EFI_MEMORY_TESTED, FALSE }, { EFI_RESOURCE_ATTRIBUTE_PERSISTABLE, EFI_MEMORY_NV, TRUE }, { EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE, EFI_MEMORY_MORE_RELIABLE, TRUE }, + { EFI_RESOURCE_ATTRIBUTE_ENCRYPTED, EFI_MEMORY_CPU_CRYPTO, TRUE }, // TDX { 0, 0, FALSE } }; @@ -658,7 +670,11 @@ ConverToCpuArchAttributes ( { UINT64 CpuArchAttributes; - CpuArchAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK; + if(gTdGuest) { + CpuArchAttributes = Attributes & NONEXCLUSIVE_MEMORY_ATTRIBUTES; + } else { + CpuArchAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK; + } if ( (Attributes & EFI_MEMORY_UC) == EFI_MEMORY_UC) { CpuArchAttributes |= EFI_MEMORY_UC; @@ -944,7 +960,11 @@ CoreConvertSpace ( // Keep original CPU arch attributes when caller just calls // SetMemorySpaceAttributes() with none CPU arch attributes (for example, RUNTIME). // - Attributes |= (Entry->Attributes & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + if(gTdGuest) { + Attributes |= (Entry->Attributes & (EXCLUSIVE_MEMORY_ATTRIBUTES | NONEXCLUSIVE_MEMORY_ATTRIBUTES)); + } else { + Attributes |= (Entry->Attributes & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + } } Entry->Attributes = Attributes; break; @@ -2289,7 +2309,9 @@ CoreInitializeMemoryServices ( Attributes = PhitResourceHob->ResourceAttribute; BaseAddress = PageAlignAddress (PhitHob->EfiMemoryTop); Length = PageAlignLength (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - BaseAddress); - FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + if(!gTdGuest) { + FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + } if (Length < MinimalMemorySizeNeeded) { // // If that range is not large enough to intialize the DXE Core, then @@ -2305,7 +2327,9 @@ CoreInitializeMemoryServices ( // BaseAddress = PageAlignAddress (ResourceHob->PhysicalStart); Length = PageAlignLength ((UINT64)((UINTN)*HobStart - BaseAddress)); - FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + if(!gTdGuest) { + FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + } } } break; @@ -2369,7 +2393,9 @@ CoreInitializeMemoryServices ( // TestedMemoryBaseAddress = PageAlignAddress (ResourceHob->PhysicalStart); TestedMemoryLength = PageAlignLength (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - TestedMemoryBaseAddress); - FindLargestFreeRegion (&TestedMemoryBaseAddress, &TestedMemoryLength, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + if(!gTdGuest) { + FindLargestFreeRegion (&TestedMemoryBaseAddress, &TestedMemoryLength, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + } if (TestedMemoryLength < MinimalMemorySizeNeeded) { continue; } diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index 731bf08bc959..a749ffd6e32e 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -1856,8 +1856,14 @@ CoreGetMemoryMap ( MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; MemoryMap->VirtualStart = 0; MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); - MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | + if(gTdGuest) { + MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | + (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO | + EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB)); + } else { + MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + } if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) { MemoryMap->Type = EfiReservedMemoryType; @@ -1890,8 +1896,14 @@ CoreGetMemoryMap ( MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; MemoryMap->VirtualStart = 0; MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); - MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | + if(gTdGuest) { + MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | + (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO | + EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB)); + } else { + MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + } MemoryMap->Type = EfiPersistentMemory; // @@ -1933,7 +1945,12 @@ CoreGetMemoryMap ( MemoryMapEnd = MemoryMap; MemoryMap = MemoryMapStart; while (MemoryMap < MemoryMapEnd) { - MemoryMap->Attribute &= ~(UINT64)EFI_MEMORY_ACCESS_MASK; + if(gTdGuest) { + MemoryMap->Attribute &= ~(UINT64)(EFI_MEMORY_RP | EFI_MEMORY_RO | + EFI_MEMORY_XP); + } else { + MemoryMap->Attribute &= ~(UINT64)EFI_MEMORY_ACCESS_MASK; + } MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size); } MergeMemoryMap (MemoryMapStart, &BufferSize, Size); diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c index 7d1daf0b1938..dfa69cd6d3a1 100644 --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c @@ -42,6 +42,10 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "DxeMain.h" #include "Mem/HeapGuard.h" +// TD +#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP) +#define MEMORY_ATTRIBUTE_MASK (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO) + // // Image type definitions // @@ -218,7 +222,11 @@ SetUefiImageMemoryAttributes ( Status = CoreGetMemorySpaceDescriptor(BaseAddress, &Descriptor); ASSERT_EFI_ERROR(Status); - FinalAttributes = (Descriptor.Attributes & EFI_CACHE_ATTRIBUTE_MASK) | (Attributes & EFI_MEMORY_ATTRIBUTE_MASK); + if(gTdGuest) { + FinalAttributes = (Descriptor.Attributes & CACHE_ATTRIBUTE_MASK) | (Attributes & MEMORY_ATTRIBUTE_MASK); + } else { + FinalAttributes = (Descriptor.Attributes & EFI_CACHE_ATTRIBUTE_MASK) | (Attributes & EFI_MEMORY_ATTRIBUTE_MASK); + } DEBUG ((DEBUG_INFO, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress, Length, FinalAttributes)); @@ -920,8 +928,13 @@ InitializeDxeNxMemoryProtectionPolicy ( (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) { - Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) | + if(gTdGuest) { + Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) | + (Entry->Attributes & CACHE_ATTRIBUTE_MASK); + } else { + Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) | (Entry->Attributes & EFI_CACHE_ATTRIBUTE_MASK); + } DEBUG ((DEBUG_INFO, "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n", From 817e28f09115a9bbd62d4b7458738f1c2b93c175 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 19:53:35 +0800 Subject: [PATCH 23/54] MdeModulePkg: Update PciBusDxe for Tdx Tdx doesn't support OptionRom. This commit should be refined to handle this situation. --- MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf | 1 + MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf index e317169d9c57..91bfcdaa38bc 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf @@ -73,6 +73,7 @@ BaseLib UefiDriverEntryPoint DebugLib + TdxProbeLib [Protocols] gEfiPciHotPlugRequestProtocolGuid ## SOMETIMES_PRODUCES diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c index 8db1ebf8ec41..a1e6db17b702 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c @@ -9,6 +9,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "PciBus.h" +#include + /** This routine is used to enumerate entire pci bus system in a given platform. @@ -533,6 +535,9 @@ GetMaxOptionRomSize ( UINT32 TempOptionRomSize; MaxOptionRomSize = 0; + if(ProbeTdGuest()) { + return 0; + } // // Go through bridges to reach all devices From c789c22b5274d6b41d28b4ffab22782e6b075bce Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 19:55:19 +0800 Subject: [PATCH 24/54] MdeModulePkg: Tdx doesn't support Smbios This commit need to refine. Is Smbios support in Td guest? --- MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c | 6 +++++- MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c index 3cdb0b1ed74f..0afa80823c00 100644 --- a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c +++ b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c @@ -8,7 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "SmbiosDxe.h" - +#include // // Module Global: // Since this driver will only ever produce one instance of the @@ -1428,6 +1428,10 @@ SmbiosDriverEntryPoint ( { EFI_STATUS Status; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + mPrivateData.Signature = SMBIOS_INSTANCE_SIGNATURE; mPrivateData.Smbios.Add = SmbiosAdd; mPrivateData.Smbios.UpdateString = SmbiosUpdateString; diff --git a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf index f6c036e1dcbc..91b9321d544b 100644 --- a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf +++ b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf @@ -41,6 +41,7 @@ UefiDriverEntryPoint DebugLib PcdLib + TdxProbeLib [Protocols] gEfiSmbiosProtocolGuid ## PRODUCES From d985e2ef322b7ec0c32abe4d748c6ba846941066 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:02:17 +0800 Subject: [PATCH 25/54] UefiCpuPkg: Update MpInitLib to support Tdx --- UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf | 3 + UefiCpuPkg/Library/MpInitLib/DxeMpLib.c | 14 +- UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c | 124 ++++++++++++++++++ UefiCpuPkg/Library/MpInitLib/MpLib.c | 26 ++++ UefiCpuPkg/Library/MpInitLib/MpLib.h | 53 ++++++++ UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf | 3 + UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c | 72 ++++++++++ 7 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c create mode 100644 UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf index 1771575c69c1..823c21664493 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf @@ -34,6 +34,7 @@ MpLib.c MpLib.h Microcode.c + DxeMpLibTdx.c [Packages] MdePkg/MdePkg.dec @@ -53,6 +54,8 @@ SynchronizationLib PcdLib VmgExitLib + TdxProbeLib + TdxLib [Protocols] gEfiTimerArchProtocolGuid ## SOMETIMES_CONSUMES diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c index 7839c249760e..0597707293e9 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c @@ -15,7 +15,7 @@ #include #include #include - +#include #include #define AP_SAFE_STACK_SIZE 128 @@ -784,6 +784,10 @@ MpInitLibStartupThisAP ( { EFI_STATUS Status; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + // // temporarily stop checkAllApsStatus for avoid resource dead-lock. // @@ -840,6 +844,10 @@ MpInitLibSwitchBSP ( EFI_TIMER_ARCH_PROTOCOL *Timer; UINT64 TimerPeriod; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + TimerPeriod = 0; // // Locate Timer Arch Protocol @@ -913,6 +921,10 @@ MpInitLibEnableDisableAP ( EFI_STATUS Status; BOOLEAN TempStopCheckState; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + TempStopCheckState = FALSE; // // temporarily stop checkAllAPsStatus for initialize parameters. diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c new file mode 100644 index 000000000000..2cd1f5620a06 --- /dev/null +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c @@ -0,0 +1,124 @@ +/** @file + CPU MP Initialize Library common functions. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2020, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.h" + +#include +#include +#include + + +/** + 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 = TdCall(TDCALL_TDINFO, 0, 0, 0, &TdReturnData); + ASSERT(Status == EFI_SUCCESS); + + if (ProcessorNumber >= TdReturnData.TdInfo.NumVcpus) { + return EFI_NOT_FOUND; + } + + 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 = TdCall(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/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c index 681fa79b4cff..bf06e6d8d25d 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -12,6 +12,7 @@ #include #include #include +#include EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID; @@ -1950,6 +1951,10 @@ MpInitLibInitialize ( UINTN BackupBufferAddr; UINTN ApIdtBase; + if(ProbeTdGuest()) { + return EFI_SUCCESS; + } + OldCpuMpData = GetCpuMpDataFromGuidedHob (); if (OldCpuMpData == NULL) { MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber); @@ -2175,6 +2180,10 @@ MpInitLibGetProcessorInfo ( CPU_INFO_IN_HOB *CpuInfoInHob; UINTN OriginalProcessorNumber; + if(ProbeTdGuest()) { + return TdxMpInitLibGetProcessorInfo(ProcessorNumber, ProcessorInfoBuffer, HealthData); + } + CpuMpData = GetCpuMpData (); CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; @@ -2406,6 +2415,10 @@ EnableDisableApWorker ( CPU_MP_DATA *CpuMpData; UINTN CallerNumber; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + CpuMpData = GetCpuMpData (); // @@ -2466,6 +2479,11 @@ MpInitLibWhoAmI ( return EFI_INVALID_PARAMETER; } + if(ProbeTdGuest()) { + *ProcessorNumber = 0; + return EFI_SUCCESS; + } + CpuMpData = GetCpuMpData (); return GetProcessorNumber (CpuMpData, ProcessorNumber); @@ -2504,6 +2522,10 @@ MpInitLibGetNumberOfProcessors ( UINTN EnabledProcessorNumber; UINTN Index; + if(ProbeTdGuest()) { + return TdxMpInitLibGetNumberOfProcessors(NumberOfProcessors, NumberOfEnabledProcessors); + } + CpuMpData = GetCpuMpData (); if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) { @@ -2589,6 +2611,10 @@ StartupAllCPUsWorker ( BOOLEAN HasEnabledAp; CPU_STATE ApState; + if(ProbeTdGuest()) { + return EFI_SUCCESS; + } + CpuMpData = GetCpuMpData (); if (FailedCpuList != NULL) { diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h index 02652eaae126..0bf309423f36 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h @@ -740,5 +740,58 @@ PlatformShadowMicrocode ( IN OUT CPU_MP_DATA *CpuMpData ); +/** + 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 + ); + + #endif diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf index 34abf25d43cd..c454bb78168b 100644 --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf @@ -34,6 +34,7 @@ MpLib.c MpLib.h Microcode.c + PeiMpLibTdx.c [Packages] MdePkg/MdePkg.dec @@ -52,6 +53,8 @@ PeiServicesLib PcdLib VmgExitLib + TdxProbeLib + TdxLib [Pcd] gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c b/UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c new file mode 100644 index 000000000000..a7d4f6d21984 --- /dev/null +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c @@ -0,0 +1,72 @@ +/** @file + CPU MP Initialize Library common functions. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2020, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.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; +} From b16fea85e035af6d2edf13aceb5eea35153aad66 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:08:22 +0800 Subject: [PATCH 26/54] OvmfPkg: Add TdxDxe driver --- OvmfPkg/OvmfPkgX64.dsc | 2 + OvmfPkg/OvmfPkgX64.fdf | 3 + OvmfPkg/TdxDxe/TdxDxe.c | 199 ++++++++++++++++++++++++++++++++++++++ OvmfPkg/TdxDxe/TdxDxe.inf | 57 +++++++++++ 4 files changed, 261 insertions(+) create mode 100644 OvmfPkg/TdxDxe/TdxDxe.c create mode 100644 OvmfPkg/TdxDxe/TdxDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 65ef52e34878..65d9be218cdc 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -1006,6 +1006,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 e239a646ccaa..24e6375c7cdf 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -221,6 +221,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 @@ -330,6 +331,8 @@ INF ShellPkg/Application/Shell/Shell.inf INF MdeModulePkg/Logo/LogoDxe.inf +INF OvmfPkg/TdxDxe/TdxDxe.inf + # # Network modules # diff --git a/OvmfPkg/TdxDxe/TdxDxe.c b/OvmfPkg/TdxDxe/TdxDxe.c new file mode 100644 index 000000000000..8052b07731dd --- /dev/null +++ b/OvmfPkg/TdxDxe/TdxDxe.c @@ -0,0 +1,199 @@ +/** @file + + TDX Dxe driver. This driver is dispatched early in DXE, due to being list + in APRIORI. + + This module is responsible for: + - Installing exception handle for #VE as early as possible. MSRs and MMIO may + generate an exception + - Sets PCDs indicating we are using protected mode resets + - Sets max logical cpus based on TDINFO + - Sets PCI PCDs based on resource hobs + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + 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; + EFI_HOB_CPU * CpuHob; + TD_RETURN_DATA TdReturnData; + + 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); + } + +#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); + } + + if (PcdGetBool(PcdTdxDisableSharedMask) == TRUE) { + PcdStatus = PcdSet64S (PcdTdxSharedPageMask, 0); + ASSERT_RETURN_ERROR(PcdStatus); + } else { + CpuHob = GetFirstHob (EFI_HOB_TYPE_CPU); + ASSERT (CpuHob != NULL); + PcdStatus = PcdSet64S (PcdTdxSharedPageMask, (1ULL << (CpuHob->SizeOfMemorySpace - 1))); + 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..c24b360a3146 --- /dev/null +++ b/OvmfPkg/TdxDxe/TdxDxe.inf @@ -0,0 +1,57 @@ +#/** @file +# +# Driver clears the encryption attribute from MMIO regions when TDX is enabled +# +# Copyright (c) 2017, AMD Inc. All rights reserved.
+# +# 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 + +[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 + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdPciIoBase + gUefiOvmfPkgTokenSpaceGuid.PcdPciIoSize + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Base + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Size + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Base + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSharedPageMask + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation + gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask + gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase From 102fae1346bf587c71f3827118d38eb34a6374a8 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:09:55 +0800 Subject: [PATCH 27/54] OvmfPkg: Add MemEncryptTdxLib --- OvmfPkg/Include/Library/MemEncryptLib.h | 20 + OvmfPkg/Include/Library/MemEncryptTdxLib.h | 109 ++ .../BaseMemEncryptTdxLib.inf | 49 + .../BaseMemEncryptTdxLibNull.inf | 36 + .../BaseMemoryEncryptionNull.c | 123 +++ .../BaseMemEncryptTdxLib/MemoryEncryption.c | 968 ++++++++++++++++++ .../BaseMemEncryptTdxLib/VirtualMemory.h | 181 ++++ 7 files changed, 1486 insertions(+) create mode 100644 OvmfPkg/Include/Library/MemEncryptLib.h 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/MemEncryptLib.h b/OvmfPkg/Include/Library/MemEncryptLib.h new file mode 100644 index 000000000000..c610748cb7b0 --- /dev/null +++ b/OvmfPkg/Include/Library/MemEncryptLib.h @@ -0,0 +1,20 @@ +/** @file + + Define Memory Encrypted Virtualization base library helper function + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MEM_ENCRYPT_LIB_H_ +#define _MEM_ENCRYPT_LIB_H_ + +#define MEM_ENCRYPT_NONE_ENABLED 0x00 +#define MEM_ENCRYPT_SEV_ENABLED 0x01 +#define MEM_ENCRYPT_SEVES_ENABLED 0x02 +#define MEM_ENCRYPT_TDX_ENABLED 0x04 + +#endif // _MEM_ENCRYPT_LIB_H_ diff --git a/OvmfPkg/Include/Library/MemEncryptTdxLib.h b/OvmfPkg/Include/Library/MemEncryptTdxLib.h new file mode 100644 index 000000000000..a93f23affc63 --- /dev/null +++ b/OvmfPkg/Include/Library/MemEncryptTdxLib.h @@ -0,0 +1,109 @@ +/** @file + + Define Memory Encrypted Virtualization base library helper function + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MEM_ENCRYPT_TDX_LIB_H_ +#define _MEM_ENCRYPT_TDX_LIB_H_ + +#include + +/** + 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. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @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 +MemEncryptTdxClearPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ); + +/** + 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. + @param[in] Flush Flush the caches before setting the bit + (mostly TRUE except MMIO addresses) + + @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 +MemEncryptTdxSetPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ); + + +/** + Locate the page range that covers the initial (pre-SMBASE-relocation) SMRAM + Save State Map. + + @param[out] BaseAddress The base address of the lowest-address page that + covers the initial SMRAM Save State Map. + + @param[out] NumberOfPages The number of pages in the page range that covers + the initial SMRAM Save State Map. + + @retval RETURN_SUCCESS BaseAddress and NumberOfPages have been set on + output. + + @retval RETURN_UNSUPPORTED SMM is unavailable. +**/ +RETURN_STATUS +EFIAPI +MemEncryptLocateInitialSmramSaveStateMapPages ( + OUT UINTN *BaseAddress, + OUT UINTN *NumberOfPages + ); +#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..c1332fac9901 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf @@ -0,0 +1,49 @@ +## @file +# Library for Memory Encryption +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 1.25 + BASE_NAME = MemEncryptTdxLibNull + 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 + UefiCpuPkg/UefiCpuPkg.dec + +[Sources] + VirtualMemory.h + MemoryEncryption.c + +[LibraryClasses] + BaseLib + CacheMaintenanceLib + CpuLib + DebugLib + MemoryAllocationLib + PcdLib + TdxLib + TdxProbeLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSharedPageMask + gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf new file mode 100644 index 000000000000..e13670cfeae6 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf @@ -0,0 +1,36 @@ +## @file +# Library for Memory Encryption +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 1.25 + BASE_NAME = MemEncryptTdxLib + 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..689ae39da555 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c @@ -0,0 +1,123 @@ +/** @file + + Virtual Memory Management Services to set or clear the memory encryption + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c + +**/ + +#include +#include +#include +#include + +/** + 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. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @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 +MemEncryptTdxClearPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + 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. + @param[in] Flush Flush the caches before setting the bit + (mostly TRUE except MMIO addresses) + + @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 +MemEncryptTdxSetPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Locate the page range that covers the initial (pre-SMBASE-relocation) SMRAM + Save State Map. + + @param[out] BaseAddress The base address of the lowest-address page that + covers the initial SMRAM Save State Map. + + @param[out] NumberOfPages The number of pages in the page range that covers + the initial SMRAM Save State Map. + + @retval RETURN_SUCCESS BaseAddress and NumberOfPages have been set on + output. + + @retval RETURN_UNSUPPORTED SMM is unavailable. +**/ +RETURN_STATUS +EFIAPI +MemEncryptLocateInitialSmramSaveStateMapPages ( + OUT UINTN *BaseAddress, + OUT UINTN *NumberOfPages + ) +{ + return EFI_UNSUPPORTED; +} + diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c new file mode 100644 index 000000000000..202f491b73b5 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c @@ -0,0 +1,968 @@ +/** @file + + Virtual Memory Management Services to set or clear the memory encryption + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c + +**/ + +#include +#include +#include +#include +#include +#include +#include "VirtualMemory.h" +#include +#include +#include + +typedef enum { + SetSharedBit, + ClearSharedBit +} TDX_PAGETABLE_MODE; + +STATIC BOOLEAN mAddressEncMaskChecked = FALSE; +STATIC UINT64 mAddressEncMask; +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 + ) +{ + // + // If query any encrpytion type this library doesn't support, return FALSE + // + return ProbeTdGuest(); +} + +/** + Get the memory encryption mask + + @param[out] EncryptionMask contains the pte mask. + +**/ +STATIC +UINT64 +GetMemEncryptionAddressMask ( + VOID + ) +{ + if (mAddressEncMaskChecked) { + return mAddressEncMask; + } + + mAddressEncMask = PcdGet64(PcdTdxSharedPageMask); + + mAddressEncMaskChecked = TRUE; + DEBUG (( + DEBUG_INFO, + "%a:%a: AddressEncMask=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + mAddressEncMask)); + + return mAddressEncMask; +} + +/** + 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; + + ASSERT (PageTableEntry != NULL); + + 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; + + ASSERT (PageTableBase != 0); + + // + // 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); + ASSERT (NewPageTable != NULL); + + 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); + + 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 (PcdGetBool(PcdTdxDisableSharedMask) != TRUE) { + ASSERT(Status == 0); + } + + // + // 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 + @param[in] CacheFlush Flush the caches before applying the + encryption mask + + @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, + IN BOOLEAN CacheFlush + ) +{ + + 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 CacheFlush=%u\n", + gEfiCallerBaseName, + __FUNCTION__, + Cr3BaseAddress, + PhysicalAddress, + (UINT64)Length, + (Mode == SetSharedBit) ? "Shared" : "Private", + (UINT32)CacheFlush + )); + + // + // Check if we have a valid memory encryption mask + // + AddressEncMask = GetMemEncryptionAddressMask (); + + PgTableMask = AddressEncMask | EFI_PAGE_MASK; + + if (Length == 0) { + return RETURN_INVALID_PARAMETER; + } + + // + // We are going to change the memory encryption attribute from C=0 -> C=1 or + // vice versa Flush the caches to ensure that data is written into memory + // with correct C-bit + // + if (CacheFlush) { + WriteBackInvalidateDataCacheRange((VOID*) (UINTN)PhysicalAddress, Length); + } + + // + // 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. + @param[in] Flush Flush the caches before clearing the bit + (mostly TRUE except MMIO addresses) + + @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 +MemEncryptTdxClearPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return SetMemorySharedOrPrivate ( + Cr3BaseAddress, + BaseAddress, + EFI_PAGES_TO_SIZE (NumPages), + SetSharedBit, + Flush + ); +} + +/** + 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. + @param[in] Flush Flush the caches before setting the bit + (mostly TRUE except MMIO addresses) + @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 +MemEncryptTdxSetPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return SetMemorySharedOrPrivate ( + Cr3BaseAddress, + BaseAddress, + EFI_PAGES_TO_SIZE (NumPages), + ClearSharedBit, + Flush + ); +} diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h b/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h new file mode 100644 index 000000000000..eca4fe77987d --- /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.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h + +**/ + +#ifndef __VIRTUAL_MEMORY__ +#define __VIRTUAL_MEMORY__ + +#include +#include +#include +#include +#include +#include + +#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 From 1d4ae55d42aa392d564e076b3e2a5e25c5278c14 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:19:52 +0800 Subject: [PATCH 28/54] OvmfPkg: Update IoMmuDxe to support both TDX and SEV --- OvmfPkg/IoMmuDxe/{AmdSevIoMmu.c => IoMmu.c} | 1840 ++++++++++--------- OvmfPkg/IoMmuDxe/{AmdSevIoMmu.h => IoMmu.h} | 78 +- OvmfPkg/IoMmuDxe/IoMmuDxe.c | 17 +- OvmfPkg/IoMmuDxe/IoMmuDxe.inf | 5 +- OvmfPkg/OvmfPkgIa32.dsc | 1 + OvmfPkg/OvmfPkgIa32X64.dsc | 1 + OvmfPkg/OvmfPkgX64.dsc | 1 + 7 files changed, 989 insertions(+), 954 deletions(-) rename OvmfPkg/IoMmuDxe/{AmdSevIoMmu.c => IoMmu.c} (92%) rename OvmfPkg/IoMmuDxe/{AmdSevIoMmu.h => IoMmu.h} (78%) diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c b/OvmfPkg/IoMmuDxe/IoMmu.c similarity index 92% rename from OvmfPkg/IoMmuDxe/AmdSevIoMmu.c rename to OvmfPkg/IoMmuDxe/IoMmu.c index 49ffa2448811..cb6eb0855b8b 100644 --- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c +++ b/OvmfPkg/IoMmuDxe/IoMmu.c @@ -1,910 +1,930 @@ -/** @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. - - Copyright (c) 2017, AMD Inc. All rights reserved.
- Copyright (c) 2017, Intel Corporation. All rights reserved.
- - SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include "AmdSevIoMmu.h" - -#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O') - -typedef struct { - UINT64 Signature; - LIST_ENTRY Link; - EDKII_IOMMU_OPERATION Operation; - UINTN NumberOfBytes; - UINTN NumberOfPages; - EFI_PHYSICAL_ADDRESS CryptedAddress; - EFI_PHYSICAL_ADDRESS PlainTextAddress; -} MAP_INFO; - -// -// List of the MAP_INFO structures that have been set up by IoMmuMap() and not -// yet torn down by IoMmuUnmap(). The list represents the full set of mappings -// currently in effect. -// -STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos); - -#define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F', 'R') - -// -// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging. -// -STATIC CONST CHAR8 * CONST -mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = { - "Read", - "Write", - "CommonBuffer", - "Read64", - "Write64", - "CommonBuffer64" -}; - -// -// The following structure enables Map() and Unmap() to perform in-place -// decryption and encryption, respectively, for BusMasterCommonBuffer[64] -// operations, without dynamic memory allocation or release. -// -// Both COMMON_BUFFER_HEADER and COMMON_BUFFER_HEADER.StashBuffer are allocated -// by AllocateBuffer() and released by FreeBuffer(). -// -#pragma pack (1) -typedef struct { - UINT64 Signature; - - // - // Always allocated from EfiBootServicesData type memory, and always - // encrypted. - // - VOID *StashBuffer; - - // - // Followed by the actual common buffer, starting at the next page. - // -} COMMON_BUFFER_HEADER; -#pragma pack () - -/** - 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 - shared buffer hence we allocate a bounce buffer to map the HostAddress to a - DeviceAddress. The Encryption attribute is removed from the DeviceAddress - buffer. - - @param This The protocol instance pointer. - @param Operation Indicates if the bus master is going to read or - write to system memory. - @param HostAddress The system memory address to map to the PCI - controller. - @param NumberOfBytes On input the number of bytes to map. On output - the number of bytes that were mapped. - @param DeviceAddress The resulting map address for the bus master - PCI controller to use to access the hosts - HostAddress. - @param Mapping A resulting value to pass to Unmap(). - - @retval EFI_SUCCESS The range was mapped for the returned - NumberOfBytes. - @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common - buffer. - @retval EFI_INVALID_PARAMETER One or more parameters are invalid. - @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a - lack of resources. - @retval EFI_DEVICE_ERROR The system hardware could not map the requested - address. - -**/ -EFI_STATUS -EFIAPI -IoMmuMap ( - IN EDKII_IOMMU_PROTOCOL *This, - IN EDKII_IOMMU_OPERATION Operation, - IN VOID *HostAddress, - IN OUT UINTN *NumberOfBytes, - OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, - OUT VOID **Mapping - ) -{ - EFI_STATUS Status; - MAP_INFO *MapInfo; - EFI_ALLOCATE_TYPE AllocateType; - COMMON_BUFFER_HEADER *CommonBufferHeader; - VOID *DecryptionSource; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n", - __FUNCTION__, - ((Operation >= 0 && - Operation < ARRAY_SIZE (mBusMasterOperationName)) ? - mBusMasterOperationName[Operation] : - "Invalid"), - HostAddress, - (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes) - )); - - if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || - Mapping == NULL) { - return EFI_INVALID_PARAMETER; - } - - // - // Allocate a MAP_INFO structure to remember the mapping when Unmap() is - // called later. - // - MapInfo = AllocatePool (sizeof (MAP_INFO)); - if (MapInfo == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Failed; - } - - // - // Initialize the MAP_INFO structure, except the PlainTextAddress field - // - ZeroMem (&MapInfo->Link, sizeof MapInfo->Link); - MapInfo->Signature = MAP_INFO_SIG; - MapInfo->Operation = Operation; - MapInfo->NumberOfBytes = *NumberOfBytes; - MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes); - MapInfo->CryptedAddress = (UINTN)HostAddress; - - // - // In the switch statement below, we point "MapInfo->PlainTextAddress" to the - // plaintext buffer, according to Operation. We also set "DecryptionSource". - // - MapInfo->PlainTextAddress = MAX_ADDRESS; - AllocateType = AllocateAnyPages; - DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress; - switch (Operation) { - // - // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer - // is necessary regardless of whether the original (crypted) buffer crosses - // the 4GB limit or not -- we have to allocate a separate plaintext buffer. - // The only variable is whether the plaintext buffer should be under 4GB. - // - case EdkiiIoMmuOperationBusMasterRead: - case EdkiiIoMmuOperationBusMasterWrite: - MapInfo->PlainTextAddress = BASE_4GB - 1; - AllocateType = AllocateMaxAddress; - // - // fall through - // - case EdkiiIoMmuOperationBusMasterRead64: - case EdkiiIoMmuOperationBusMasterWrite64: - // - // Allocate the implicit plaintext bounce buffer. - // - Status = gBS->AllocatePages ( - AllocateType, - EfiBootServicesData, - MapInfo->NumberOfPages, - &MapInfo->PlainTextAddress - ); - if (EFI_ERROR (Status)) { - goto FreeMapInfo; - } - break; - - // - // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a - // stash buffer (for in-place decryption) have been allocated already, with - // AllocateBuffer(). We only check whether the address of the to-be-plaintext - // buffer is low enough for the requested operation. - // - case EdkiiIoMmuOperationBusMasterCommonBuffer: - if ((MapInfo->CryptedAddress > BASE_4GB) || - (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) > - BASE_4GB - MapInfo->CryptedAddress)) { - // - // CommonBuffer operations cannot be remapped. If the common buffer is - // above 4GB, then it is not possible to generate a mapping, so return an - // error. - // - Status = EFI_UNSUPPORTED; - goto FreeMapInfo; - } - // - // fall through - // - case EdkiiIoMmuOperationBusMasterCommonBuffer64: - // - // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer(). - // - MapInfo->PlainTextAddress = MapInfo->CryptedAddress; - // - // Stash the crypted data. - // - CommonBufferHeader = (COMMON_BUFFER_HEADER *)( - (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE - ); - ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); - CopyMem ( - CommonBufferHeader->StashBuffer, - (VOID *)(UINTN)MapInfo->CryptedAddress, - MapInfo->NumberOfBytes - ); - // - // Point "DecryptionSource" to the stash buffer so that we decrypt - // it to the original location, after the switch statement. - // - DecryptionSource = CommonBufferHeader->StashBuffer; - break; - - default: - // - // Operation is invalid - // - Status = EFI_INVALID_PARAMETER; - goto FreeMapInfo; - } - - // - // Clear the memory encryption mask on the plaintext buffer. - // - Status = MemEncryptSevClearPageEncMask ( - 0, - MapInfo->PlainTextAddress, - MapInfo->NumberOfPages, - TRUE - ); - ASSERT_EFI_ERROR (Status); - if (EFI_ERROR (Status)) { - CpuDeadLoop (); - } - - // - // If this is a read operation from the Bus Master's point of view, - // then copy the contents of the real buffer into the mapped buffer - // so the Bus Master can read the contents of the real buffer. - // - // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt - // the original data (from the stash buffer) back to the original location. - // - if (Operation == EdkiiIoMmuOperationBusMasterRead || - Operation == EdkiiIoMmuOperationBusMasterRead64 || - Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || - Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { - CopyMem ( - (VOID *) (UINTN) MapInfo->PlainTextAddress, - DecryptionSource, - MapInfo->NumberOfBytes - ); - } - - // - // Track all MAP_INFO structures. - // - InsertHeadList (&mMapInfos, &MapInfo->Link); - // - // Populate output parameters. - // - *DeviceAddress = MapInfo->PlainTextAddress; - *Mapping = MapInfo; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx\n", - __FUNCTION__, - MapInfo, - MapInfo->PlainTextAddress, - MapInfo->CryptedAddress, - (UINT64)MapInfo->NumberOfPages - )); - - return EFI_SUCCESS; - -FreeMapInfo: - FreePool (MapInfo); - -Failed: - *NumberOfBytes = 0; - return Status; -} - -/** - Completes the Map() operation and releases any corresponding resources. - - This is an internal worker function that only extends the Map() API with - the MemoryMapLocked parameter. - - @param This The protocol instance pointer. - @param Mapping The mapping value returned from Map(). - @param MemoryMapLocked The function is executing on the stack of - gBS->ExitBootServices(); changes to the UEFI - memory map are forbidden. - - @retval EFI_SUCCESS The range was unmapped. - @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by - Map(). - @retval EFI_DEVICE_ERROR The data was not committed to the target system - memory. -**/ -STATIC -EFI_STATUS -EFIAPI -IoMmuUnmapWorker ( - IN EDKII_IOMMU_PROTOCOL *This, - IN VOID *Mapping, - IN BOOLEAN MemoryMapLocked - ) -{ - MAP_INFO *MapInfo; - EFI_STATUS Status; - COMMON_BUFFER_HEADER *CommonBufferHeader; - VOID *EncryptionTarget; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Mapping=0x%p MemoryMapLocked=%d\n", - __FUNCTION__, - Mapping, - MemoryMapLocked - )); - - if (Mapping == NULL) { - return EFI_INVALID_PARAMETER; - } - - MapInfo = (MAP_INFO *)Mapping; - - // - // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings - // - CommonBufferHeader = NULL; - - // - // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations - // we have to encrypt the results, ultimately to the original place (i.e., - // "MapInfo->CryptedAddress"). - // - // For BusMasterCommonBuffer[64] operations however, this encryption has to - // land in-place, so divert the encryption to the stash buffer first. - // - EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress; - - switch (MapInfo->Operation) { - case EdkiiIoMmuOperationBusMasterCommonBuffer: - case EdkiiIoMmuOperationBusMasterCommonBuffer64: - ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress); - - CommonBufferHeader = (COMMON_BUFFER_HEADER *)( - (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE - ); - ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); - EncryptionTarget = CommonBufferHeader->StashBuffer; - // - // fall through - // - - case EdkiiIoMmuOperationBusMasterWrite: - case EdkiiIoMmuOperationBusMasterWrite64: - CopyMem ( - EncryptionTarget, - (VOID *) (UINTN) MapInfo->PlainTextAddress, - MapInfo->NumberOfBytes - ); - break; - - default: - // - // nothing to encrypt after BusMasterRead[64] operations - // - break; - } - - // - // Restore the memory encryption mask on the area we used to hold the - // plaintext. - // - Status = MemEncryptSevSetPageEncMask ( - 0, - MapInfo->PlainTextAddress, - MapInfo->NumberOfPages, - TRUE - ); - ASSERT_EFI_ERROR (Status); - if (EFI_ERROR (Status)) { - CpuDeadLoop (); - } - - // - // For BusMasterCommonBuffer[64] operations, copy the stashed data to the - // original (now encrypted) location. - // - // For all other operations, fill the late bounce buffer (which existed as - // plaintext at some point) with zeros, and then release it (unless the UEFI - // memory map is locked). - // - if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || - MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { - CopyMem ( - (VOID *)(UINTN)MapInfo->CryptedAddress, - CommonBufferHeader->StashBuffer, - MapInfo->NumberOfBytes - ); - } else { - ZeroMem ( - (VOID *)(UINTN)MapInfo->PlainTextAddress, - EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) - ); - if (!MemoryMapLocked) { - gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages); - } - } - - // - // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is - // locked). - // - RemoveEntryList (&MapInfo->Link); - if (!MemoryMapLocked) { - FreePool (MapInfo); - } - - return EFI_SUCCESS; -} - -/** - Completes the Map() operation and releases any corresponding resources. - - @param This The protocol instance pointer. - @param Mapping The mapping value returned from Map(). - - @retval EFI_SUCCESS The range was unmapped. - @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by - Map(). - @retval EFI_DEVICE_ERROR The data was not committed to the target system - memory. -**/ -EFI_STATUS -EFIAPI -IoMmuUnmap ( - IN EDKII_IOMMU_PROTOCOL *This, - IN VOID *Mapping - ) -{ - return IoMmuUnmapWorker ( - This, - Mapping, - FALSE // MemoryMapLocked - ); -} - -/** - Allocates pages that are suitable for an OperationBusMasterCommonBuffer or - OperationBusMasterCommonBuffer64 mapping. - - @param This The protocol instance pointer. - @param Type This parameter is not used and must be ignored. - @param MemoryType The type of memory to allocate, - EfiBootServicesData or EfiRuntimeServicesData. - @param Pages The number of pages to allocate. - @param HostAddress A pointer to store the base system memory - address of the allocated range. - @param Attributes The requested bit mask of attributes for the - allocated range. - - @retval EFI_SUCCESS The requested memory pages were allocated. - @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal - attribute bits are MEMORY_WRITE_COMBINE and - MEMORY_CACHED. - @retval EFI_INVALID_PARAMETER One or more parameters are invalid. - @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. - -**/ -EFI_STATUS -EFIAPI -IoMmuAllocateBuffer ( - IN EDKII_IOMMU_PROTOCOL *This, - IN EFI_ALLOCATE_TYPE Type, - IN EFI_MEMORY_TYPE MemoryType, - IN UINTN Pages, - IN OUT VOID **HostAddress, - IN UINT64 Attributes - ) -{ - EFI_STATUS Status; - EFI_PHYSICAL_ADDRESS PhysicalAddress; - VOID *StashBuffer; - UINTN CommonBufferPages; - COMMON_BUFFER_HEADER *CommonBufferHeader; - - DEBUG (( - DEBUG_VERBOSE, - "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n", - __FUNCTION__, - (UINT32)MemoryType, - (UINT64)Pages, - Attributes - )); - - // - // Validate Attributes - // - if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) { - return EFI_UNSUPPORTED; - } - - // - // Check for invalid inputs - // - if (HostAddress == NULL) { - return EFI_INVALID_PARAMETER; - } - - // - // The only valid memory types are EfiBootServicesData and - // EfiRuntimeServicesData - // - if (MemoryType != EfiBootServicesData && - MemoryType != EfiRuntimeServicesData) { - return EFI_INVALID_PARAMETER; - } - - // - // We'll need a header page for the COMMON_BUFFER_HEADER structure. - // - if (Pages > MAX_UINTN - 1) { - return EFI_OUT_OF_RESOURCES; - } - CommonBufferPages = Pages + 1; - - // - // Allocate the stash in EfiBootServicesData type memory. - // - // Map() will temporarily save encrypted data in the stash for - // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the - // original location. - // - // Unmap() will temporarily save plaintext data in the stash for - // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the - // original location. - // - // StashBuffer always resides in encrypted memory. - // - StashBuffer = AllocatePages (Pages); - if (StashBuffer == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - PhysicalAddress = (UINTN)-1; - if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) { - // - // Limit allocations to memory below 4GB - // - PhysicalAddress = SIZE_4GB - 1; - } - Status = gBS->AllocatePages ( - AllocateMaxAddress, - MemoryType, - CommonBufferPages, - &PhysicalAddress - ); - if (EFI_ERROR (Status)) { - goto FreeStashBuffer; - } - - CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress; - PhysicalAddress += EFI_PAGE_SIZE; - - CommonBufferHeader->Signature = COMMON_BUFFER_SIG; - CommonBufferHeader->StashBuffer = StashBuffer; - - *HostAddress = (VOID *)(UINTN)PhysicalAddress; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Host=0x%Lx Stash=0x%p\n", - __FUNCTION__, - PhysicalAddress, - StashBuffer - )); - return EFI_SUCCESS; - -FreeStashBuffer: - FreePages (StashBuffer, Pages); - return Status; -} - -/** - Frees memory that was allocated with AllocateBuffer(). - - @param This The protocol instance pointer. - @param Pages The number of pages to free. - @param HostAddress The base system memory address of the allocated - range. - - @retval EFI_SUCCESS The requested memory pages were freed. - @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and - Pages was not allocated with AllocateBuffer(). - -**/ -EFI_STATUS -EFIAPI -IoMmuFreeBuffer ( - IN EDKII_IOMMU_PROTOCOL *This, - IN UINTN Pages, - IN VOID *HostAddress - ) -{ - UINTN CommonBufferPages; - COMMON_BUFFER_HEADER *CommonBufferHeader; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Host=0x%p Pages=0x%Lx\n", - __FUNCTION__, - HostAddress, - (UINT64)Pages - )); - - CommonBufferPages = Pages + 1; - CommonBufferHeader = (COMMON_BUFFER_HEADER *)( - (UINTN)HostAddress - EFI_PAGE_SIZE - ); - - // - // Check the signature. - // - ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); - if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) { - return EFI_INVALID_PARAMETER; - } - - // - // Free the stash buffer. This buffer was always encrypted, so no need to - // zero it. - // - FreePages (CommonBufferHeader->StashBuffer, Pages); - - // - // Release the common buffer itself. Unmap() has re-encrypted it in-place, so - // no need to zero it. - // - return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages); -} - - -/** - Set IOMMU attribute for a system memory. - - If the IOMMU protocol exists, the system memory cannot be used - for DMA by default. - - When a device requests a DMA access for a system memory, - the device driver need use SetAttribute() to update the IOMMU - attribute to request DMA access (read and/or write). - - The DeviceHandle is used to identify which device submits the request. - The IOMMU implementation need translate the device path to an IOMMU device - ID, and set IOMMU hardware register accordingly. - 1) DeviceHandle can be a standard PCI device. - The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. - The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. - The memory for BusMasterCommonBuffer need set - EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. - After the memory is used, the memory need set 0 to keep it being - protected. - 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). - The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or - EDKII_IOMMU_ACCESS_WRITE. - - @param[in] This The protocol instance pointer. - @param[in] DeviceHandle The device who initiates the DMA access - request. - @param[in] Mapping The mapping value returned from Map(). - @param[in] IoMmuAccess The IOMMU access. - - @retval EFI_SUCCESS The IoMmuAccess is set for the memory range - specified by DeviceAddress and Length. - @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. - @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by - Map(). - @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination - of access. - @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. - @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported - by the IOMMU. - @retval EFI_UNSUPPORTED The IOMMU does not support the memory range - specified by Mapping. - @retval EFI_OUT_OF_RESOURCES There are not enough resources available to - modify the IOMMU access. - @retval EFI_DEVICE_ERROR The IOMMU device reported an error while - attempting the operation. - -**/ -EFI_STATUS -EFIAPI -IoMmuSetAttribute ( - IN EDKII_IOMMU_PROTOCOL *This, - IN EFI_HANDLE DeviceHandle, - IN VOID *Mapping, - IN UINT64 IoMmuAccess - ) -{ - return EFI_UNSUPPORTED; -} - -EDKII_IOMMU_PROTOCOL mAmdSev = { - EDKII_IOMMU_PROTOCOL_REVISION, - IoMmuSetAttribute, - IoMmuMap, - IoMmuUnmap, - IoMmuAllocateBuffer, - IoMmuFreeBuffer, -}; - -/** - Notification function that is queued when gBS->ExitBootServices() signals the - EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another - event, received as Context, and returns. - - Signaling an event in this context is safe. The UEFI spec allows - gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not - listed, hence memory is not allocated. The edk2 implementation also does not - release memory (and we only have to care about the edk2 implementation - because EDKII_IOMMU_PROTOCOL is edk2-specific anyway). - - @param[in] Event Event whose notification function is being invoked. - Event is permitted to request the queueing of this - function at TPL_CALLBACK or TPL_NOTIFY task - priority level. - - @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal - is permitted to request the queueing of its - notification function only at TPL_CALLBACK level. -**/ -STATIC -VOID -EFIAPI -AmdSevExitBoot ( - IN EFI_EVENT Event, - IN VOID *EventToSignal - ) -{ - // - // (1) The NotifyFunctions of all the events in - // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before - // AmdSevExitBoot() is entered. - // - // (2) AmdSevExitBoot() is executing minimally at TPL_CALLBACK. - // - // (3) AmdSevExitBoot() 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. - // - // Consequences: - // - // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions - // queued at TPL_CALLBACK may be invoked after AmdSevExitBoot() returns. - // - // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions - // queued at TPL_NOTIFY may be invoked after AmdSevExitBoot() returns; plus - // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly - // after all NotifyFunctions queued at TPL_NOTIFY, including - // AmdSevExitBoot(), have been invoked. - // - // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we - // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all* - // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES. - // - DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); - gBS->SignalEvent (EventToSignal); -} - -/** - Notification function that is queued after the notification functions of all - events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory - map restrictions apply. - - This function unmaps all currently existing IOMMU mappings. - - @param[in] Event Event whose notification function is being invoked. Event - is permitted to request the queueing of this function - only at TPL_CALLBACK task priority level. - - @param[in] Context Ignored. -**/ -STATIC -VOID -EFIAPI -AmdSevUnmapAllMappings ( - IN EFI_EVENT Event, - IN VOID *Context - ) -{ - LIST_ENTRY *Node; - LIST_ENTRY *NextNode; - MAP_INFO *MapInfo; - - DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); - - // - // All drivers that had set up IOMMU mappings have halted their respective - // controllers by now; tear down the mappings. - // - for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) { - NextNode = GetNextNode (&mMapInfos, Node); - MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG); - IoMmuUnmapWorker ( - &mAmdSev, // This - MapInfo, // Mapping - TRUE // MemoryMapLocked - ); - } -} - -/** - Initialize Iommu Protocol. - -**/ -EFI_STATUS -EFIAPI -AmdSevInstallIoMmuProtocol ( - VOID - ) -{ - EFI_STATUS Status; - EFI_EVENT UnmapAllMappingsEvent; - EFI_EVENT ExitBootEvent; - EFI_HANDLE Handle; - - // - // Create the "late" event whose notification function will tear down all - // left-over IOMMU mappings. - // - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, // Type - TPL_CALLBACK, // NotifyTpl - AmdSevUnmapAllMappings, // NotifyFunction - NULL, // NotifyContext - &UnmapAllMappingsEvent // Event - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // Create the event whose notification function will be queued by - // gBS->ExitBootServices() and will signal the event created above. - // - Status = gBS->CreateEvent ( - EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type - TPL_CALLBACK, // NotifyTpl - AmdSevExitBoot, // NotifyFunction - UnmapAllMappingsEvent, // NotifyContext - &ExitBootEvent // Event - ); - if (EFI_ERROR (Status)) { - goto CloseUnmapAllMappingsEvent; - } - - Handle = NULL; - Status = gBS->InstallMultipleProtocolInterfaces ( - &Handle, - &gEdkiiIoMmuProtocolGuid, &mAmdSev, - NULL - ); - if (EFI_ERROR (Status)) { - goto CloseExitBootEvent; - } - - return EFI_SUCCESS; - -CloseExitBootEvent: - gBS->CloseEvent (ExitBootEvent); - -CloseUnmapAllMappingsEvent: - gBS->CloseEvent (UnmapAllMappingsEvent); - - return Status; -} +/** @file + + The protocol provides support to allocate, free, map and umap a DMA buffer + for bus master (e.g PciHostBridge). When Memory Encryption 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.
+ Copyright (c) 2017, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "IoMmu.h" + +#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O') + +typedef struct { + UINT64 Signature; + LIST_ENTRY Link; + EDKII_IOMMU_OPERATION Operation; + UINTN NumberOfBytes; + UINTN NumberOfPages; + EFI_PHYSICAL_ADDRESS CryptedAddress; + EFI_PHYSICAL_ADDRESS PlainTextAddress; +} MAP_INFO; + +UINTN mMemEncryptType; +// +// List of the MAP_INFO structures that have been set up by IoMmuMap() and not +// yet torn down by IoMmuUnmap(). The list represents the full set of mappings +// currently in effect. +// +STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos); + +#define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F', 'R') + +// +// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging. +// +STATIC CONST CHAR8 * CONST +mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = { + "Read", + "Write", + "CommonBuffer", + "Read64", + "Write64", + "CommonBuffer64" +}; + +// +// The following structure enables Map() and Unmap() to perform in-place +// decryption and encryption, respectively, for BusMasterCommonBuffer[64] +// operations, without dynamic memory allocation or release. +// +// Both COMMON_BUFFER_HEADER and COMMON_BUFFER_HEADER.StashBuffer are allocated +// by AllocateBuffer() and released by FreeBuffer(). +// +#pragma pack (1) +typedef struct { + UINT64 Signature; + + // + // Always allocated from EfiBootServicesData type memory, and always + // encrypted. + // + VOID *StashBuffer; + + // + // Followed by the actual common buffer, starting at the next page. + // +} COMMON_BUFFER_HEADER; +#pragma pack () + +/** + Provides the controller-specific addresses required to access system memory + from a DMA bus master. On an encrypted 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. + + @param This The protocol instance pointer. + @param Operation Indicates if the bus master is going to read or + write to system memory. + @param HostAddress The system memory address to map to the PCI + controller. + @param NumberOfBytes On input the number of bytes to map. On output + the number of bytes that were mapped. + @param DeviceAddress The resulting map address for the bus master + PCI controller to use to access the hosts + HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned + NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common + buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested + address. + +**/ +EFI_STATUS +EFIAPI +IoMmuMap ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + MAP_INFO *MapInfo; + EFI_ALLOCATE_TYPE AllocateType; + COMMON_BUFFER_HEADER *CommonBufferHeader; + VOID *DecryptionSource; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n", + __FUNCTION__, + ((Operation >= 0 && + Operation < ARRAY_SIZE (mBusMasterOperationName)) ? + mBusMasterOperationName[Operation] : + "Invalid"), + HostAddress, + (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes) + )); + + if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || + Mapping == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Allocate a MAP_INFO structure to remember the mapping when Unmap() is + // called later. + // + MapInfo = AllocatePool (sizeof (MAP_INFO)); + if (MapInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Failed; + } + + // + // Initialize the MAP_INFO structure, except the PlainTextAddress field + // + ZeroMem (&MapInfo->Link, sizeof MapInfo->Link); + MapInfo->Signature = MAP_INFO_SIG; + MapInfo->Operation = Operation; + MapInfo->NumberOfBytes = *NumberOfBytes; + MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes); + MapInfo->CryptedAddress = (UINTN)HostAddress; + + // + // In the switch statement below, we point "MapInfo->PlainTextAddress" to the + // plaintext buffer, according to Operation. We also set "DecryptionSource". + // + MapInfo->PlainTextAddress = MAX_ADDRESS; + AllocateType = AllocateAnyPages; + DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress; + switch (Operation) { + // + // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer + // is necessary regardless of whether the original (crypted) buffer crosses + // the 4GB limit or not -- we have to allocate a separate plaintext buffer. + // The only variable is whether the plaintext buffer should be under 4GB. + // + case EdkiiIoMmuOperationBusMasterRead: + case EdkiiIoMmuOperationBusMasterWrite: + MapInfo->PlainTextAddress = BASE_4GB - 1; + AllocateType = AllocateMaxAddress; + // + // fall through + // + case EdkiiIoMmuOperationBusMasterRead64: + case EdkiiIoMmuOperationBusMasterWrite64: + // + // Allocate the implicit plaintext bounce buffer. + // + Status = gBS->AllocatePages ( + AllocateType, + EfiBootServicesData, + MapInfo->NumberOfPages, + &MapInfo->PlainTextAddress + ); + if (EFI_ERROR (Status)) { + goto FreeMapInfo; + } + break; + + // + // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a + // stash buffer (for in-place decryption) have been allocated already, with + // AllocateBuffer(). We only check whether the address of the to-be-plaintext + // buffer is low enough for the requested operation. + // + case EdkiiIoMmuOperationBusMasterCommonBuffer: + if ((MapInfo->CryptedAddress > BASE_4GB) || + (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) > + BASE_4GB - MapInfo->CryptedAddress)) { + // + // CommonBuffer operations cannot be remapped. If the common buffer is + // above 4GB, then it is not possible to generate a mapping, so return an + // error. + // + Status = EFI_UNSUPPORTED; + goto FreeMapInfo; + } + // + // fall through + // + case EdkiiIoMmuOperationBusMasterCommonBuffer64: + // + // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer(). + // + MapInfo->PlainTextAddress = MapInfo->CryptedAddress; + // + // Stash the crypted data. + // + CommonBufferHeader = (COMMON_BUFFER_HEADER *)( + (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE + ); + ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); + CopyMem ( + CommonBufferHeader->StashBuffer, + (VOID *)(UINTN)MapInfo->CryptedAddress, + MapInfo->NumberOfBytes + ); + // + // Point "DecryptionSource" to the stash buffer so that we decrypt + // it to the original location, after the switch statement. + // + DecryptionSource = CommonBufferHeader->StashBuffer; + break; + + default: + // + // Operation is invalid + // + Status = EFI_INVALID_PARAMETER; + goto FreeMapInfo; + } + + // + // Clear the memory encryption mask on the plaintext buffer. + // + if (mMemEncryptType == MEM_ENCRYPT_SEV_ENABLED) { + Status = MemEncryptSevClearPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } else if (mMemEncryptType == MEM_ENCRYPT_TDX_ENABLED) { + Status = MemEncryptTdxClearPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + CpuDeadLoop (); + } + + // + // If this is a read operation from the Bus Master's point of view, + // then copy the contents of the real buffer into the mapped buffer + // so the Bus Master can read the contents of the real buffer. + // + // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt + // the original data (from the stash buffer) back to the original location. + // + if (Operation == EdkiiIoMmuOperationBusMasterRead || + Operation == EdkiiIoMmuOperationBusMasterRead64 || + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { + CopyMem ( + (VOID *) (UINTN) MapInfo->PlainTextAddress, + DecryptionSource, + MapInfo->NumberOfBytes + ); + } + + // + // Track all MAP_INFO structures. + // + InsertHeadList (&mMapInfos, &MapInfo->Link); + // + // Populate output parameters. + // + *DeviceAddress = MapInfo->PlainTextAddress; + *Mapping = MapInfo; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx\n", + __FUNCTION__, + MapInfo, + MapInfo->PlainTextAddress, + MapInfo->CryptedAddress, + (UINT64)MapInfo->NumberOfPages + )); + + return EFI_SUCCESS; + +FreeMapInfo: + FreePool (MapInfo); + +Failed: + *NumberOfBytes = 0; + return Status; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + This is an internal worker function that only extends the Map() API with + the MemoryMapLocked parameter. + + @param This The protocol instance pointer. + @param Mapping The mapping value returned from Map(). + @param MemoryMapLocked The function is executing on the stack of + gBS->ExitBootServices(); changes to the UEFI + memory map are forbidden. + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system + memory. +**/ +STATIC +EFI_STATUS +EFIAPI +IoMmuUnmapWorker ( + IN EDKII_IOMMU_PROTOCOL *This, + IN VOID *Mapping, + IN BOOLEAN MemoryMapLocked + ) +{ + MAP_INFO *MapInfo; + EFI_STATUS Status; + COMMON_BUFFER_HEADER *CommonBufferHeader; + VOID *EncryptionTarget; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Mapping=0x%p MemoryMapLocked=%d\n", + __FUNCTION__, + Mapping, + MemoryMapLocked + )); + + if (Mapping == NULL) { + return EFI_INVALID_PARAMETER; + } + + MapInfo = (MAP_INFO *)Mapping; + + // + // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings + // + CommonBufferHeader = NULL; + + // + // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations + // we have to encrypt the results, ultimately to the original place (i.e., + // "MapInfo->CryptedAddress"). + // + // For BusMasterCommonBuffer[64] operations however, this encryption has to + // land in-place, so divert the encryption to the stash buffer first. + // + EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress; + + switch (MapInfo->Operation) { + case EdkiiIoMmuOperationBusMasterCommonBuffer: + case EdkiiIoMmuOperationBusMasterCommonBuffer64: + ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress); + + CommonBufferHeader = (COMMON_BUFFER_HEADER *)( + (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE + ); + ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); + EncryptionTarget = CommonBufferHeader->StashBuffer; + // + // fall through + // + + case EdkiiIoMmuOperationBusMasterWrite: + case EdkiiIoMmuOperationBusMasterWrite64: + CopyMem ( + EncryptionTarget, + (VOID *) (UINTN) MapInfo->PlainTextAddress, + MapInfo->NumberOfBytes + ); + break; + + default: + // + // nothing to encrypt after BusMasterRead[64] operations + // + break; + } + + // + // Restore the memory encryption mask on the area we used to hold the + // plaintext. + // + if (mMemEncryptType == MEM_ENCRYPT_SEV_ENABLED) { + Status = MemEncryptSevSetPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } else if (mMemEncryptType == MEM_ENCRYPT_TDX_ENABLED) { + Status = MemEncryptTdxSetPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + CpuDeadLoop (); + } + + // + // For BusMasterCommonBuffer[64] operations, copy the stashed data to the + // original (now encrypted) location. + // + // For all other operations, fill the late bounce buffer (which existed as + // plaintext at some point) with zeros, and then release it (unless the UEFI + // memory map is locked). + // + if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || + MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { + CopyMem ( + (VOID *)(UINTN)MapInfo->CryptedAddress, + CommonBufferHeader->StashBuffer, + MapInfo->NumberOfBytes + ); + } else { + ZeroMem ( + (VOID *)(UINTN)MapInfo->PlainTextAddress, + EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) + ); + if (!MemoryMapLocked) { + gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages); + } + } + + // + // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is + // locked). + // + RemoveEntryList (&MapInfo->Link); + if (!MemoryMapLocked) { + FreePool (MapInfo); + } + + return EFI_SUCCESS; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This The protocol instance pointer. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system + memory. +**/ +EFI_STATUS +EFIAPI +IoMmuUnmap ( + IN EDKII_IOMMU_PROTOCOL *This, + IN VOID *Mapping + ) +{ + return IoMmuUnmapWorker ( + This, + Mapping, + FALSE // MemoryMapLocked + ); +} + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param This The protocol instance pointer. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, + EfiBootServicesData or EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory + address of the allocated range. + @param Attributes The requested bit mask of attributes for the + allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal + attribute bits are MEMORY_WRITE_COMBINE and + MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +EFIAPI +IoMmuAllocateBuffer ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT VOID **HostAddress, + IN UINT64 Attributes + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + VOID *StashBuffer; + UINTN CommonBufferPages; + COMMON_BUFFER_HEADER *CommonBufferHeader; + + DEBUG (( + DEBUG_VERBOSE, + "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n", + __FUNCTION__, + (UINT32)MemoryType, + (UINT64)Pages, + Attributes + )); + + // + // Validate Attributes + // + if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) { + return EFI_UNSUPPORTED; + } + + // + // Check for invalid inputs + // + if (HostAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The only valid memory types are EfiBootServicesData and + // EfiRuntimeServicesData + // + if (MemoryType != EfiBootServicesData && + MemoryType != EfiRuntimeServicesData) { + return EFI_INVALID_PARAMETER; + } + + // + // We'll need a header page for the COMMON_BUFFER_HEADER structure. + // + if (Pages > MAX_UINTN - 1) { + return EFI_OUT_OF_RESOURCES; + } + CommonBufferPages = Pages + 1; + + // + // Allocate the stash in EfiBootServicesData type memory. + // + // Map() will temporarily save encrypted data in the stash for + // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the + // original location. + // + // Unmap() will temporarily save plaintext data in the stash for + // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the + // original location. + // + // StashBuffer always resides in encrypted memory. + // + StashBuffer = AllocatePages (Pages); + if (StashBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PhysicalAddress = (UINTN)-1; + if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) { + // + // Limit allocations to memory below 4GB + // + PhysicalAddress = SIZE_4GB - 1; + } + Status = gBS->AllocatePages ( + AllocateMaxAddress, + MemoryType, + CommonBufferPages, + &PhysicalAddress + ); + if (EFI_ERROR (Status)) { + goto FreeStashBuffer; + } + + CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress; + PhysicalAddress += EFI_PAGE_SIZE; + + CommonBufferHeader->Signature = COMMON_BUFFER_SIG; + CommonBufferHeader->StashBuffer = StashBuffer; + + *HostAddress = (VOID *)(UINTN)PhysicalAddress; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Host=0x%Lx Stash=0x%p\n", + __FUNCTION__, + PhysicalAddress, + StashBuffer + )); + return EFI_SUCCESS; + +FreeStashBuffer: + FreePages (StashBuffer, Pages); + return Status; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This The protocol instance pointer. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated + range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and + Pages was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +EFIAPI +IoMmuFreeBuffer ( + IN EDKII_IOMMU_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ) +{ + UINTN CommonBufferPages; + COMMON_BUFFER_HEADER *CommonBufferHeader; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Host=0x%p Pages=0x%Lx\n", + __FUNCTION__, + HostAddress, + (UINT64)Pages + )); + + CommonBufferPages = Pages + 1; + CommonBufferHeader = (COMMON_BUFFER_HEADER *)( + (UINTN)HostAddress - EFI_PAGE_SIZE + ); + + // + // Check the signature. + // + ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); + if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) { + return EFI_INVALID_PARAMETER; + } + + // + // Free the stash buffer. This buffer was always encrypted, so no need to + // zero it. + // + FreePages (CommonBufferHeader->StashBuffer, Pages); + + // + // Release the common buffer itself. Unmap() has re-encrypted it in-place, so + // no need to zero it. + // + return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages); +} + + +/** + Set IOMMU attribute for a system memory. + + If the IOMMU protocol exists, the system memory cannot be used + for DMA by default. + + When a device requests a DMA access for a system memory, + the device driver need use SetAttribute() to update the IOMMU + attribute to request DMA access (read and/or write). + + The DeviceHandle is used to identify which device submits the request. + The IOMMU implementation need translate the device path to an IOMMU device + ID, and set IOMMU hardware register accordingly. + 1) DeviceHandle can be a standard PCI device. + The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. + The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. + The memory for BusMasterCommonBuffer need set + EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. + After the memory is used, the memory need set 0 to keep it being + protected. + 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). + The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or + EDKII_IOMMU_ACCESS_WRITE. + + @param[in] This The protocol instance pointer. + @param[in] DeviceHandle The device who initiates the DMA access + request. + @param[in] Mapping The mapping value returned from Map(). + @param[in] IoMmuAccess The IOMMU access. + + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range + specified by DeviceAddress and Length. + @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination + of access. + @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. + @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported + by the IOMMU. + @retval EFI_UNSUPPORTED The IOMMU does not support the memory range + specified by Mapping. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + modify the IOMMU access. + @retval EFI_DEVICE_ERROR The IOMMU device reported an error while + attempting the operation. + +**/ +EFI_STATUS +EFIAPI +IoMmuSetAttribute ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN VOID *Mapping, + IN UINT64 IoMmuAccess + ) +{ + return EFI_UNSUPPORTED; +} + +EDKII_IOMMU_PROTOCOL mIoMmu = { + EDKII_IOMMU_PROTOCOL_REVISION, + IoMmuSetAttribute, + IoMmuMap, + IoMmuUnmap, + IoMmuAllocateBuffer, + IoMmuFreeBuffer, +}; + +/** + Notification function that is queued when gBS->ExitBootServices() signals the + EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another + event, received as Context, and returns. + + Signaling an event in this context is safe. The UEFI spec allows + gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not + listed, hence memory is not allocated. The edk2 implementation also does not + release memory (and we only have to care about the edk2 implementation + because EDKII_IOMMU_PROTOCOL is edk2-specific anyway). + + @param[in] Event Event whose notification function is being invoked. + Event is permitted to request the queueing of this + function at TPL_CALLBACK or TPL_NOTIFY task + priority level. + + @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal + is permitted to request the queueing of its + notification function only at TPL_CALLBACK level. +**/ +STATIC +VOID +EFIAPI +IoMmuExitBoot ( + IN EFI_EVENT Event, + IN VOID *EventToSignal + ) +{ + // + // (1) The NotifyFunctions of all the events in + // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before + // IoMmuExitBoot() is entered. + // + // (2) IoMmuExitBoot() is executing minimally at TPL_CALLBACK. + // + // (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. + // + // Consequences: + // + // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions + // 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 IoMmuExitBoot() returns; plus + // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly + // after all NotifyFunctions queued at TPL_NOTIFY, including + // IoMmuExitBoot(), have been invoked. + // + // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we + // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all* + // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES. + // + DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); + gBS->SignalEvent (EventToSignal); +} + +/** + Notification function that is queued after the notification functions of all + events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory + map restrictions apply. + + This function unmaps all currently existing IOMMU mappings. + + @param[in] Event Event whose notification function is being invoked. Event + is permitted to request the queueing of this function + only at TPL_CALLBACK task priority level. + + @param[in] Context Ignored. +**/ +STATIC +VOID +EFIAPI +IoMmuUnmapAllMappings ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Node; + LIST_ENTRY *NextNode; + MAP_INFO *MapInfo; + + DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); + + // + // All drivers that had set up IOMMU mappings have halted their respective + // controllers by now; tear down the mappings. + // + for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) { + NextNode = GetNextNode (&mMapInfos, Node); + MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG); + IoMmuUnmapWorker ( + &mIoMmu, // This + MapInfo, // Mapping + TRUE // MemoryMapLocked + ); + } +} + +/** + Initialize Iommu Protocol. + +**/ +EFI_STATUS +EFIAPI +IoMmuInstallIoMmuProtocol ( + UINTN MemEncryptType + ) +{ + EFI_STATUS Status; + EFI_EVENT UnmapAllMappingsEvent; + EFI_EVENT ExitBootEvent; + EFI_HANDLE Handle; + + mMemEncryptType = MemEncryptType; + // + // Create the "late" event whose notification function will tear down all + // left-over IOMMU mappings. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, // Type + TPL_CALLBACK, // NotifyTpl + IoMmuUnmapAllMappings, // NotifyFunction + NULL, // NotifyContext + &UnmapAllMappingsEvent // Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create the event whose notification function will be queued by + // gBS->ExitBootServices() and will signal the event created above. + // + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type + TPL_CALLBACK, // NotifyTpl + IoMmuExitBoot, // NotifyFunction + UnmapAllMappingsEvent, // NotifyContext + &ExitBootEvent // Event + ); + if (EFI_ERROR (Status)) { + goto CloseUnmapAllMappingsEvent; + } + + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEdkiiIoMmuProtocolGuid, &mIoMmu, + NULL + ); + if (EFI_ERROR (Status)) { + goto CloseExitBootEvent; + } + + return EFI_SUCCESS; + +CloseExitBootEvent: + gBS->CloseEvent (ExitBootEvent); + +CloseUnmapAllMappingsEvent: + gBS->CloseEvent (UnmapAllMappingsEvent); + + return Status; +} diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h b/OvmfPkg/IoMmuDxe/IoMmu.h similarity index 78% rename from OvmfPkg/IoMmuDxe/AmdSevIoMmu.h rename to OvmfPkg/IoMmuDxe/IoMmu.h index 8244f28b57fd..2ef3b756f792 100644 --- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h +++ b/OvmfPkg/IoMmuDxe/IoMmu.h @@ -1,38 +1,40 @@ -/** @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 protocol clear the encryption - bit from the DMA buffer. - - Copyright (c) 2017, Intel Corporation. All rights reserved.
- Copyright (c) 2017, AMD Inc. All rights reserved.
- (C) Copyright 2017 Hewlett Packard Enterprise Development LP
- SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#ifndef _AMD_SEV_IOMMU_H_ -#define _AMD_SEV_IOMMU_H_ - -#include - -#include -#include -#include -#include -#include -#include - -/** - Install IOMMU protocol to provide the DMA support for PciHostBridge and - MemEncryptSevLib. - -**/ -EFI_STATUS -EFIAPI -AmdSevInstallIoMmuProtocol ( - VOID - ); - -#endif +/** @file + + The protocol provides support to allocate, free, map and umap a DMA buffer + for bus master (e.g PciHostBridge). When memory encryption is enabled, the DMA operations + must be performed on unencrypted buffer hence protocol clear the encryption + bit from the DMA buffer. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ (C) Copyright 2017 Hewlett Packard Enterprise Development LP
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _AMD_SEV_IOMMU_H_ +#define _AMD_SEV_IOMMU_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Install IOMMU protocol to provide the DMA support for PciHostBridge and + MemEncryptLib. + +**/ +EFI_STATUS +EFIAPI +IoMmuInstallIoMmuProtocol ( + UINTN MemEncryptType + ); + +#endif diff --git a/OvmfPkg/IoMmuDxe/IoMmuDxe.c b/OvmfPkg/IoMmuDxe/IoMmuDxe.c index 13df8ba874c5..d511b2b82192 100644 --- a/OvmfPkg/IoMmuDxe/IoMmuDxe.c +++ b/OvmfPkg/IoMmuDxe/IoMmuDxe.c @@ -1,15 +1,16 @@ /** @file IoMmuDxe driver installs EDKII_IOMMU_PROTOCOL to provide the support for DMA - operations when SEV is enabled. + operations when memory encryption is enabled. + Copyright (c) 2020, Intel Corporation. All rights reserved.
Copyright (c) 2017, AMD Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include "AmdSevIoMmu.h" +#include "IoMmu.h" EFI_STATUS EFIAPI @@ -20,13 +21,21 @@ IoMmuDxeEntryPoint ( { EFI_STATUS Status; EFI_HANDLE Handle; + UINTN Type; // - // When SEV is enabled, install IoMmu protocol otherwise install the + // When memory encryption is enabled, install IoMmu protocol otherwise install the // placeholder protocol so that other dependent module can run. // + Type = MEM_ENCRYPT_NONE_ENABLED; if (MemEncryptSevIsEnabled ()) { - Status = AmdSevInstallIoMmuProtocol (); + Type = MEM_ENCRYPT_SEV_ENABLED; + } else if (MemEncryptTdxIsEnabled ()) { + Type = MEM_ENCRYPT_TDX_ENABLED; + } + + if (Type != MEM_ENCRYPT_NONE_ENABLED) { + Status = IoMmuInstallIoMmuProtocol (Type); } else { Handle = NULL; diff --git a/OvmfPkg/IoMmuDxe/IoMmuDxe.inf b/OvmfPkg/IoMmuDxe/IoMmuDxe.inf index 2ebd74e5558c..2aa8413cfe23 100644 --- a/OvmfPkg/IoMmuDxe/IoMmuDxe.inf +++ b/OvmfPkg/IoMmuDxe/IoMmuDxe.inf @@ -18,8 +18,8 @@ ENTRY_POINT = IoMmuDxeEntryPoint [Sources] - AmdSevIoMmu.c - AmdSevIoMmu.h + IoMmu.c + IoMmu.h IoMmuDxe.c [Packages] @@ -32,6 +32,7 @@ BaseMemoryLib DebugLib MemEncryptSevLib + MemEncryptTdxLib MemoryAllocationLib UefiBootServicesTableLib UefiDriverEntryPoint diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index cc1c43e4dc71..4b93068cb5b6 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -171,6 +171,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 468456074234..bdf99013b687 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -175,6 +175,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/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 65d9be218cdc..c884eb9f74c3 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -186,6 +186,7 @@ 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 From 75abea53810eb3c3bdbeac9e5f769f83efb0d278 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:25:08 +0800 Subject: [PATCH 29/54] OvmfPkg: Update QemuFwCfgDxeLib to support Tdx --- 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..9f8ae1227b16 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #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 memory encryption 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..b89a12ca31cb 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf @@ -42,6 +42,7 @@ DebugLib IoLib MemoryAllocationLib + MemEncryptTdxLib MemEncryptSevLib [Protocols] From 7a8ba5d3e6ed58a96f6b7a27cb9aa48d999a245a Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:26:39 +0800 Subject: [PATCH 30/54] OvmfPkg: Update QemuFwCfgS3Lib for Tdx Td guest does not support S3. So always return FALSE in Td guest. --- .../QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf | 1 + .../QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf | 1 + .../QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c | 26 ++++++++++++------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf b/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf index 10f41d64c6a1..da342286db27 100644 --- a/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf +++ b/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf @@ -36,6 +36,7 @@ MemoryAllocationLib QemuFwCfgLib UefiBootServicesTableLib + TdxProbeLib [Protocols] gEfiS3SaveStateProtocolGuid ## SOMETIMES_CONSUMES diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf b/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf index 8268828a7615..0b2643091050 100644 --- a/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf +++ b/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf @@ -37,3 +37,4 @@ [LibraryClasses] DebugLib QemuFwCfgLib + TdxProbeLib diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c b/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c index 5557c70aa3ad..4fca19edf0b4 100644 --- a/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c +++ b/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c @@ -9,6 +9,7 @@ #include #include +#include /** Determine if S3 support is explicitly enabled. @@ -27,16 +28,23 @@ QemuFwCfgS3Enabled ( VOID ) { - RETURN_STATUS Status; - FIRMWARE_CONFIG_ITEM FwCfgItem; - UINTN FwCfgSize; - UINT8 SystemStates[6]; + RETURN_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINT8 SystemStates[6]; - Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); - if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { + if (ProbeTdGuest()) { + return FALSE; + + } else { + + Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); + if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { + return FALSE; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); + return (BOOLEAN) (SystemStates[3] & BIT7); } - QemuFwCfgSelectItem (FwCfgItem); - QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); - return (BOOLEAN) (SystemStates[3] & BIT7); } From 485dcf0a0b0336027a5e54286c9c87401b4d64ac Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:33:56 +0800 Subject: [PATCH 31/54] OvmfPkg: Update AcpiPlatformDxe to support Tdx Update MDAT table to record the Mailbox information. --- OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf | 2 + OvmfPkg/AcpiPlatformDxe/Qemu.c | 41 ++++++++++++++++++--- OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 11 +++++- OvmfPkg/Include/IndustryStandard/AcpiTdx.h | 30 +++++++++++++++ 4 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 OvmfPkg/Include/IndustryStandard/AcpiTdx.h diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf index e486b8afa56d..ba0e064ddc9c 100644 --- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf +++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf @@ -59,6 +59,7 @@ [Guids] gRootBridgesConnectedEventGroupGuid + gUefiOvmfPkgTdxPlatformGuid [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile @@ -66,6 +67,7 @@ gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress gUefiOvmfPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress + gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase [Depex] gEfiAcpiTableProtocolGuid diff --git a/OvmfPkg/AcpiPlatformDxe/Qemu.c b/OvmfPkg/AcpiPlatformDxe/Qemu.c index 7fb42270043f..027bfa4cdd7c 100644 --- a/OvmfPkg/AcpiPlatformDxe/Qemu.c +++ b/OvmfPkg/AcpiPlatformDxe/Qemu.c @@ -17,6 +17,10 @@ #include #include #include +#include +#include + +STATIC EFI_HOB_PLATFORM_INFO *mPlatformInfoHob = NULL; BOOLEAN QemuDetected ( @@ -73,6 +77,7 @@ QemuInstallAcpiMadtTable ( VOID *Ptr; UINTN Loop; EFI_STATUS Status; + ACPI_MADT_MPWK_STRUCT *MadtMpWk; ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER)); @@ -93,6 +98,8 @@ QemuInstallAcpiMadtTable ( (1 + PciLinkIsoCount) * sizeof (*Iso) + 1 * sizeof (*LocalApicNmi); + NewBufferSize += sizeof(ACPI_MADT_MPWK_STRUCT); + Madt = AllocatePool (NewBufferSize); if (Madt == NULL) { return EFI_OUT_OF_RESOURCES; @@ -172,6 +179,17 @@ QemuInstallAcpiMadtTable ( LocalApicNmi->LocalApicInti = 0x01; Ptr = LocalApicNmi + 1; + MadtMpWk = Ptr; + MadtMpWk->Type = ACPI_MADT_MPWK_STRUCT_TYPE; + MadtMpWk->Length = sizeof(ACPI_MADT_MPWK_STRUCT); + MadtMpWk->MailBoxVersion = 1; + MadtMpWk->Reserved2 = 0; + MadtMpWk->MailBoxAddress = PcdGet64 (PcdTdRelocatedMailboxBase); + Ptr = MadtMpWk + 1; + + DEBUG ((DEBUG_INFO, "%a:%d:ACPI setting mailbox to 0x%x 0x%x\n", __func__, __LINE__, + MadtMpWk->MailBoxAddress, PcdGet64 (PcdTdRelocatedMailboxBase))); + ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize); Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey); @@ -355,13 +373,18 @@ GetSuspendStates ( // // check for overrides // - Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); - if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { - DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n")); - return; + if (mPlatformInfoHob) { + CopyMem(SystemStates, mPlatformInfoHob->SystemStates, sizeof SystemStates); + DEBUG ((DEBUG_INFO, ">>>> GetSuspendStates mPlatformInfoHob\n")); + } else { + Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); + if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { + DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n")); + return; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); } - QemuFwCfgSelectItem (FwCfgItem); - QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); // // Each byte corresponds to a system state. In each byte, the MSB tells us @@ -489,6 +512,12 @@ QemuInstallAcpiTable ( { EFI_ACPI_DESCRIPTION_HEADER *Hdr; EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction; + EFI_HOB_GUID_TYPE *GuidHob; + + GuidHob = GetFirstGuidHob(&gUefiOvmfPkgTdxPlatformGuid); + if (GuidHob) { + mPlatformInfoHob = (EFI_HOB_PLATFORM_INFO *)GET_GUID_HOB_DATA (GuidHob); + } Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer; switch (Hdr->Signature) { diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c index b62027db6e66..97e0e0d295c1 100644 --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c @@ -18,7 +18,7 @@ #include #include #include - +#include // // The user structure for the ordered collection that will track the fw_cfg @@ -913,9 +913,16 @@ Process2ndPassCmdAddPointer ( goto RollbackSeenPointer; } - Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, + if(ProbeTdGuest()) { + Status = QemuInstallAcpiTable(AcpiProtocol, + (VOID *)(UINTN)PointerValue, TableSize, + &InstalledKey[*NumInstalled]); + } else { + Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, (VOID *)(UINTN)PointerValue, TableSize, &InstalledKey[*NumInstalled]); + } + if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__, Status)); diff --git a/OvmfPkg/Include/IndustryStandard/AcpiTdx.h b/OvmfPkg/Include/IndustryStandard/AcpiTdx.h new file mode 100644 index 000000000000..16d96b83e20e --- /dev/null +++ b/OvmfPkg/Include/IndustryStandard/AcpiTdx.h @@ -0,0 +1,30 @@ +/** @file + Processor or Compiler specific defines and types x64 (Intel 64, AMD64). + + Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ACPI_TDX_H_ +#define _ACPI_TDX_H_ + +#define ACPI_MADT_MPWK_STRUCT_TYPE 0x10 + +#pragma pack(1) + +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 MailBoxVersion; + UINT32 Reserved2; + UINT64 MailBoxAddress; +} ACPI_MADT_MPWK_STRUCT; +#pragma pack() +#endif From 5fd7a418e82bee08b4d6bc1723f2c3f027b6ee08 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 20:42:10 +0800 Subject: [PATCH 32/54] OvmfPkg: Update LocalApicTimerDxe --- OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c | 489 ++++++++++++++++++ .../LocalApicTimerDxe/LocalApicTimerDxe.inf | 53 ++ .../LocalApicTimerDxe/LocalApicTimerDxe.uni | 14 + OvmfPkg/OvmfPkgX64.dsc | 5 + OvmfPkg/OvmfPkgX64.fdf | 2 + 5 files changed, 563 insertions(+) create mode 100644 OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c create mode 100644 OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf create mode 100644 OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.uni diff --git a/OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c b/OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c new file mode 100644 index 000000000000..0ad97fb8306d --- /dev/null +++ b/OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c @@ -0,0 +1,489 @@ +/** @file + Timer Architectural Protocol module using Local APIC Timer + + Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +/** + This function registers the handler NotifyFunction so it is called every time + the timer interrupt fires. It also passes the amount of time since the last + handler call to the NotifyFunction. If NotifyFunction is NULL, then the + handler is unregistered. If the handler is registered, then EFI_SUCCESS is + returned. If the CPU does not support registering a timer interrupt handler, + then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler + when a handler is already registered, then EFI_ALREADY_STARTED is returned. + If an attempt is made to unregister a handler when a handler is not registered, + then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to + register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR + is returned. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param NotifyFunction The function to call when a timer interrupt fires. + This function executes at TPL_HIGH_LEVEL. The DXE + Core will register a handler for the timer interrupt, + so it can know how much time has passed. This + information is used to signal timer based events. + NULL will unregister the handler. + + @retval EFI_SUCCESS The timer handler was registered. + @retval EFI_UNSUPPORTED The platform does not support timer interrupts. + @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already + registered. + @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not + previously registered. + @retval EFI_DEVICE_ERROR The timer handler could not be registered. + +**/ +EFI_STATUS +EFIAPI +TimerDriverRegisterHandler ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN EFI_TIMER_NOTIFY NotifyFunction + ); + +/** + This function adjusts the period of timer interrupts to the value specified + by TimerPeriod. If the timer period is updated, then the selected timer + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned. + If an error occurs while attempting to update the timer period, then the + timer hardware will be put back in its state prior to this call, and + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt + is disabled. This is not the same as disabling the CPU's interrupts. + Instead, it must either turn off the timer hardware, or it must adjust the + interrupt controller so that a CPU interrupt is not generated when the timer + interrupt fires. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod The rate to program the timer interrupt in 100 nS units. + If the timer hardware is not programmable, then + EFI_UNSUPPORTED is returned. If the timer is programmable, + then the timer period will be rounded up to the nearest + timer period that is supported by the timer hardware. + If TimerPeriod is set to 0, then the timer interrupts + will be disabled. + + @retval EFI_SUCCESS The timer period was changed. + @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt. + @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error. + +**/ +EFI_STATUS +EFIAPI +TimerDriverSetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN UINT64 TimerPeriod + ); + +/** + This function retrieves the period of timer interrupts in 100 ns units, + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is + returned, then the timer is currently disabled. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. + If 0 is returned, then the timer is currently disabled. + + @retval EFI_SUCCESS The timer period was returned in TimerPeriod. + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + OUT UINT64 *TimerPeriod + ); + +/** + This function generates a soft timer interrupt. If the platform does not support soft + timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned. + If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler() + service, then a soft timer interrupt will be generated. If the timer interrupt is + enabled when this service is called, then the registered handler will be invoked. The + registered handler should not be able to distinguish a hardware-generated timer + interrupt from a software-generated timer interrupt. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS The soft timer interrupt was generated. + @retval EFI_UNSUPPORTED The platform does not support the generation of soft + timer interrupts. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGenerateSoftInterrupt ( + IN EFI_TIMER_ARCH_PROTOCOL *This + ); + +/// +/// The handle onto which the Timer Architectural Protocol will be installed. +/// +EFI_HANDLE mTimerHandle = NULL; + +/// +/// The Timer Architectural Protocol that this driver produces. +/// +EFI_TIMER_ARCH_PROTOCOL mTimer = { + TimerDriverRegisterHandler, + TimerDriverSetTimerPeriod, + TimerDriverGetTimerPeriod, + TimerDriverGenerateSoftInterrupt +}; + +/// +/// Pointer to the CPU Architectural Protocol instance. +/// +EFI_CPU_ARCH_PROTOCOL *mCpu = NULL; + +/// +/// The notification function to call on every timer interrupt. +/// +EFI_TIMER_NOTIFY mTimerNotifyFunction = NULL; + +/// +/// The current period of the Local APIC timer interrupt in 100 ns units. +/// +UINT64 mTimerPeriod = 0; + +/// +/// Counts the number of Local APIC Timer interrupts processed by this driver. +/// Only required for debug. +/// +volatile UINTN mNumTicks; + +/** + The interrupt handler for the Local APIC timer. This handler clears the Local + APIC interrupt and computes the amount of time that has passed since the last + Local APIC timer interrupt. If a notification function is registered, then + the amount of time since the last Local APIC timer interrupt is passed to that + notification function in 100 ns units. The Local APIC timer is updated to + generate another interrupt in the required time period. + + @param InterruptType The type of interrupt that occurred. + @param SystemContext A pointer to the system context when the interrupt occurred. +**/ +VOID +EFIAPI +TimerInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + + EFI_TPL OriginalTPL; + + OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + // + // Count number of ticks + // + DEBUG_CODE (mNumTicks++;); + + // + // Check to see if there is a registered notification function + // + if (mTimerNotifyFunction != NULL) { + mTimerNotifyFunction (mTimerPeriod); + } + + gBS->RestoreTPL (OriginalTPL); + + DisableInterrupts (); + + SendApicEoi(); +} + +/** + This function registers the handler NotifyFunction so it is called every time + the timer interrupt fires. It also passes the amount of time since the last + handler call to the NotifyFunction. If NotifyFunction is NULL, then the + handler is unregistered. If the handler is registered, then EFI_SUCCESS is + returned. If the CPU does not support registering a timer interrupt handler, + then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler + when a handler is already registered, then EFI_ALREADY_STARTED is returned. + If an attempt is made to unregister a handler when a handler is not registered, + then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to + register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR + is returned. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param NotifyFunction The function to call when a timer interrupt fires. + This function executes at TPL_HIGH_LEVEL. The DXE + Core will register a handler for the timer interrupt, + so it can know how much time has passed. This + information is used to signal timer based events. + NULL will unregister the handler. + + @retval EFI_SUCCESS The timer handler was registered. + @retval EFI_UNSUPPORTED The platform does not support timer interrupts. + @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already + registered. + @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not + previously registered. + @retval EFI_DEVICE_ERROR The timer handler could not be registered. + +**/ +EFI_STATUS +EFIAPI +TimerDriverRegisterHandler ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN EFI_TIMER_NOTIFY NotifyFunction + ) +{ + // + // Check for invalid parameters + // + if (NotifyFunction == NULL && mTimerNotifyFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + if (NotifyFunction != NULL && mTimerNotifyFunction != NULL) { + return EFI_ALREADY_STARTED; + } + + // + // Cache the registered notification function + // + mTimerNotifyFunction = NotifyFunction; + + return EFI_SUCCESS; +} + +/** + This function adjusts the period of timer interrupts to the value specified + by TimerPeriod. If the timer period is updated, then the selected timer + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned. + If an error occurs while attempting to update the timer period, then the + timer hardware will be put back in its state prior to this call, and + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt + is disabled. This is not the same as disabling the CPU's interrupts. + Instead, it must either turn off the timer hardware, or it must adjust the + interrupt controller so that a CPU interrupt is not generated when the timer + interrupt fires. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod The rate to program the timer interrupt in 100 nS units. + If the timer hardware is not programmable, then + EFI_UNSUPPORTED is returned. If the timer is programmable, + then the timer period will be rounded up to the nearest + timer period that is supported by the timer hardware. + If TimerPeriod is set to 0, then the timer interrupts + will be disabled. + + @retval EFI_SUCCESS The timer period was changed. + @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt. + @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error. + +**/ +EFI_STATUS +EFIAPI +TimerDriverSetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN UINT64 TimerPeriod + ) +{ + EFI_TPL Tpl; + UINTN Divisor; + UINT64 TimerCount; + + // + // Disable interrupts + // + Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + if (TimerPeriod == 0) { + // + // Disable timer interrupt for a TimerPeriod of 0 + // + DisableApicTimerInterrupt (); + } else { + DisableApicTimerInterrupt (); + + // + // Convert TimerPeriod in 100ns units to Local APIC Timer ticks. + // + GetApicTimerState (&Divisor, NULL, NULL); + TimerCount = DivU64x32 ( + MultU64x32 (TimerPeriod, PcdGet32(PcdFSBClock)), + (UINT32)Divisor * 10000000 + ); + + // + // Program the local APIC timer + // + InitializeApicTimer (0, (UINT32)TimerCount, TRUE, PcdGet8 (PcdHpetLocalApicVector)); + + EnableApicTimerInterrupt (); + } + + // + // Save the new timer period + // + mTimerPeriod = TimerPeriod; + + // + // Restore interrupts + // + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + This function retrieves the period of timer interrupts in 100 ns units, + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is + returned, then the timer is currently disabled. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. + If 0 is returned, then the timer is currently disabled. + + @retval EFI_SUCCESS The timer period was returned in TimerPeriod. + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + OUT UINT64 *TimerPeriod + ) +{ + if (TimerPeriod == NULL) { + return EFI_INVALID_PARAMETER; + } + + *TimerPeriod = mTimerPeriod; + + return EFI_SUCCESS; +} + +/** + This function generates a soft timer interrupt. If the platform does not support soft + timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned. + If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler() + service, then a soft timer interrupt will be generated. If the timer interrupt is + enabled when this service is called, then the registered handler will be invoked. The + registered handler should not be able to distinguish a hardware-generated timer + interrupt from a software-generated timer interrupt. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS The soft timer interrupt was generated. + @retval EFI_UNSUPPORTED The platform does not support the generation of soft + timer interrupts. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGenerateSoftInterrupt ( + IN EFI_TIMER_ARCH_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Initialize the Timer Architectural Protocol driver + + @param ImageHandle ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Timer Architectural Protocol created + @retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver. + @retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver. + +**/ +EFI_STATUS +EFIAPI +TimerDriverInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + if (!ProbeTdGuest ()) { + return EFI_UNSUPPORTED; + } + + // + // Make sure the Timer Architectural Protocol is not already installed in the system + // + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid); + + // + // Find the CPU architectural protocol. + // + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &mCpu); + ASSERT_EFI_ERROR (Status); + + // + // Install interrupt handler for the Local APIC Timer + // + Status = mCpu->RegisterInterruptHandler (mCpu, PcdGet8 (PcdHpetLocalApicVector), TimerInterruptHandler); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Unable to register Local APIC interrupt with CPU Arch Protocol. Unload Local APIC timer driver.\n")); + return EFI_DEVICE_ERROR; + } + + // + // Force the Local APIC timer to be disabled while setting everything up + // + DisableApicTimerInterrupt (); + InitializeApicTimer (0, 0, FALSE, PcdGet8 (PcdHpetLocalApicVector)); + + // + // Force the Local APIC Timer to be enabled at its default period + // + Status = TimerDriverSetTimerPeriod (&mTimer, PcdGet64 (PcdHpetDefaultTimerPeriod)); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Unable to set Local APIC default timer rate. Unload Local APIC timer driver.\n")); + return EFI_DEVICE_ERROR; + } + + // + // Show state of enabled timer + // + DEBUG_CODE ( + // + // Wait for a few timer interrupts to fire before continuing + // + while (mNumTicks < 10); + ); + + // + // Install the Timer Architectural Protocol onto a new handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mTimerHandle, + &gEfiTimerArchProtocolGuid, &mTimer, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf new file mode 100644 index 000000000000..8bbd6d467410 --- /dev/null +++ b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf @@ -0,0 +1,53 @@ +## @file +# Timer Architectural Protocol module using Local APIC Timer +# +# Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LocalApicTimerDxe + MODULE_UNI_FILE = LocalApicTimerDxe.uni + FILE_GUID = 74EB4D00-E63E-11EA-8B6E-0800200C9A66 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TimerDriverInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# +# + +[Sources] + LocalApicTimer.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + PcAtChipsetPkg/PcAtChipsetPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + PcdLib + DebugLib + UefiDriverEntryPoint + UefiBootServicesTableLib + BaseLib + LocalApicLib + TdxProbeLib + +[Protocols] + gEfiTimerArchProtocolGuid ## PRODUCES + gEfiCpuArchProtocolGuid ## CONSUMES + +[Pcd] + gPcAtChipsetPkgTokenSpaceGuid.PcdHpetLocalApicVector ## CONSUMES + gPcAtChipsetPkgTokenSpaceGuid.PcdHpetDefaultTimerPeriod ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdFSBClock + +[Depex] + gEfiCpuArchProtocolGuid diff --git a/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.uni b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.uni new file mode 100644 index 000000000000..59f411a60f21 --- /dev/null +++ b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.uni @@ -0,0 +1,14 @@ +// /** @file +// Timer Architectural Protocol module using Local APIC Timer +// +// Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Timer Architectural Protocol module using Local APIC Timer" + +#string STR_MODULE_DESCRIPTION #language en-US "Timer Architectural Protocol module using Local APIC Timer." + diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index c884eb9f74c3..47392607d396 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -283,6 +283,7 @@ !endif VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf + LocalApicLib|UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf TdvfPlatformLib|OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf PrePiLib|OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf @@ -806,6 +807,10 @@ OvmfPkg/8259InterruptControllerDxe/8259.inf UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf UefiCpuPkg/CpuDxe/CpuDxe.inf + OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf { + + LocalApicLib|UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf + } OvmfPkg/8254TimerDxe/8254Timer.inf OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index 24e6375c7cdf..abebe3adb296 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -243,6 +243,8 @@ INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf INF OvmfPkg/8259InterruptControllerDxe/8259.inf INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf INF UefiCpuPkg/CpuDxe/CpuDxe.inf +INF OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf + INF OvmfPkg/8254TimerDxe/8254Timer.inf INF OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf From ceee9b1d6f97e1618c3479df0b03e7e35ad05c7b Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 21:32:07 +0800 Subject: [PATCH 33/54] OvmfPkg: Update IncompatiblePciDeviceSupportDxe to support Tdx TODO: Understand the mechanism --- .../IncompatiblePciDeviceSupport.c | 79 ++++++++++++++++++- .../IncompatiblePciDeviceSupport.inf | 1 + 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c index 53c768167de9..c0f1cddefe6f 100644 --- a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c +++ b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c @@ -19,6 +19,7 @@ #include #include +#include // // The Legacy BIOS protocol has been located. @@ -45,9 +46,15 @@ typedef struct { EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR AddressSpaceDesc; EFI_ACPI_END_TAG_DESCRIPTOR EndDesc; } MMIO64_PREFERENCE; + +typedef struct { + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR AddressSpaceDesc; + EFI_ACPI_END_TAG_DESCRIPTOR EndDesc; +} OPTION_ROM_PREFERENCE; + #pragma pack () -STATIC CONST MMIO64_PREFERENCE mConfiguration = { +STATIC CONST MMIO64_PREFERENCE mMmio6Configuration = { // // AddressSpaceDesc // @@ -85,6 +92,44 @@ STATIC CONST MMIO64_PREFERENCE mConfiguration = { } }; +STATIC CONST OPTION_ROM_PREFERENCE mOptionRomConfiguration = { + // + // AddressSpaceDesc + // + { + ACPI_ADDRESS_SPACE_DESCRIPTOR, // Desc + (UINT16)( // Len + sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - + OFFSET_OF ( + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR, + ResType + ) + ), + ACPI_ADDRESS_SPACE_TYPE_MEM, // ResType + 0, // GenFlag + BIT0, // Disable option roms SpecificFlag + 64, // AddrSpaceGranularity: + // aperture selection hint + // for BAR allocation + MAX_UINT64, // AddrRangeMin + MAX_UINT64, // AddrRangeMax: + // no special alignment + // for affected BARs + MAX_UINT64, // AddrTranslationOffset: + // hint covers all + // eligible BARs + 0 // AddrLen: + // use probed BAR size + }, + // + // EndDesc + // + { + ACPI_END_TAG_DESCRIPTOR, // Desc + 0 // Checksum: to be ignored + } +}; + // // The CheckDevice() member function has been called. // @@ -201,7 +246,20 @@ CheckDevice ( OUT VOID **Configuration ) { + CONST VOID *ConfigurationPtr; + UINTN ConfigurationSize; + BOOLEAN TdGuest; + mCheckDeviceCalled = TRUE; + TdGuest = ProbeTdGuest(); + if(TdGuest) { + ConfigurationPtr = &mOptionRomConfiguration; + ConfigurationSize = sizeof(mOptionRomConfiguration); + goto AllocateConfiguration; + } else { + ConfigurationPtr = &mMmio6Configuration; + ConfigurationSize = sizeof(mMmio6Configuration); + } // // Unlike the general description of this protocol member suggests, there is @@ -231,11 +289,19 @@ CheckDevice ( // the edk2 PCI Bus UEFI_DRIVER actually handles error codes; see the // UpdatePciInfo() function. // - *Configuration = AllocateCopyPool (sizeof mConfiguration, &mConfiguration); +AllocateConfiguration: + *Configuration = AllocateCopyPool (ConfigurationSize, ConfigurationPtr); if (*Configuration == NULL) { + if(TdGuest) { DEBUG ((DEBUG_WARN, - "%a: 64-bit MMIO BARs may be degraded for PCI 0x%04x:0x%04x (rev %d)\n", - __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId)); + "%a: Check device for Option ROM of PCI 0x%04x:0x%04x (rev %d) failed.\n\n", + __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId)); + } else { + DEBUG ((DEBUG_WARN, + "%a: 64-bit MMIO BARs may be degraded for PCI 0x%04x:0x%04x (rev %d)\n", + __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId)); + } + return EFI_OUT_OF_RESOURCES; } return EFI_SUCCESS; @@ -265,6 +331,10 @@ DriverInitialize ( EFI_EVENT Event; VOID *Registration; + if(ProbeTdGuest()) { + goto InstallProtocol; + } + // // If the PCI Bus driver is not supposed to allocate resources, then it makes // no sense to install a protocol that influences the resource allocation. @@ -326,6 +396,7 @@ DriverInitialize ( Status = gBS->SignalEvent (Event); ASSERT_EFI_ERROR (Status); +InstallProtocol: mIncompatiblePciDeviceSupport.CheckDevice = CheckDevice; Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle, &gEfiIncompatiblePciDeviceSupportProtocolGuid, diff --git a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf index f08b6f4bd4b4..971178804704 100644 --- a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf +++ b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf @@ -30,6 +30,7 @@ PcdLib UefiBootServicesTableLib UefiDriverEntryPoint + TdxProbeLib [Protocols] gEfiIncompatiblePciDeviceSupportProtocolGuid ## SOMETIMES_PRODUCES From 5830b422507adcbd0936e948937c3016a060d1a5 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 18 Mar 2021 22:05:58 +0800 Subject: [PATCH 34/54] OvmfPkg: Disable 8254/8259 timer in Td guest TODO: Add more description --- OvmfPkg/8254TimerDxe/8254Timer.inf | 1 + OvmfPkg/8254TimerDxe/Timer.c | 5 +++++ OvmfPkg/8259InterruptControllerDxe/8259.c | 4 ++++ OvmfPkg/8259InterruptControllerDxe/8259.inf | 1 + 4 files changed, 11 insertions(+) diff --git a/OvmfPkg/8254TimerDxe/8254Timer.inf b/OvmfPkg/8254TimerDxe/8254Timer.inf index 8a07c8247ebe..007e3f405c0a 100644 --- a/OvmfPkg/8254TimerDxe/8254Timer.inf +++ b/OvmfPkg/8254TimerDxe/8254Timer.inf @@ -26,6 +26,7 @@ DebugLib UefiDriverEntryPoint IoLib + TdxProbeLib [Sources] Timer.h diff --git a/OvmfPkg/8254TimerDxe/Timer.c b/OvmfPkg/8254TimerDxe/Timer.c index fd1691beb3c7..701cc65a303e 100644 --- a/OvmfPkg/8254TimerDxe/Timer.c +++ b/OvmfPkg/8254TimerDxe/Timer.c @@ -7,6 +7,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Timer.h" +#include // // The handle onto which the Timer Architectural Protocol will be installed @@ -340,6 +341,10 @@ TimerDriverInitialize ( EFI_STATUS Status; UINT32 TimerVector; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + // // Initialize the pointer to our notify function. // diff --git a/OvmfPkg/8259InterruptControllerDxe/8259.c b/OvmfPkg/8259InterruptControllerDxe/8259.c index 1c2ac1039d40..94b7cb83c90e 100644 --- a/OvmfPkg/8259InterruptControllerDxe/8259.c +++ b/OvmfPkg/8259InterruptControllerDxe/8259.c @@ -8,6 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "8259.h" +#include // // Global for the Legacy 8259 Protocol that is produced by this driver // @@ -586,6 +587,9 @@ Install8259 ( EFI_STATUS Status; EFI_8259_IRQ Irq; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } // // Initialze mask values from PCDs // diff --git a/OvmfPkg/8259InterruptControllerDxe/8259.inf b/OvmfPkg/8259InterruptControllerDxe/8259.inf index 7320ff2490a7..09a4b41996af 100644 --- a/OvmfPkg/8259InterruptControllerDxe/8259.inf +++ b/OvmfPkg/8259InterruptControllerDxe/8259.inf @@ -29,6 +29,7 @@ UefiDriverEntryPoint IoLib PcdLib + TdxProbeLib [Protocols] gEfiLegacy8259ProtocolGuid ## PRODUCES From 18886fb4849233a4c7f48b333d9186db18f10658 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 10:15:47 +0800 Subject: [PATCH 35/54] MdePkg: Add TDX Protocol Guid and ACPI table definition --- MdePkg/Include/Protocol/Tdx.h | 40 +++++++++++++++++++++++++++++++ MdePkg/Include/Protocol/TdxAcpi.h | 29 ++++++++++++++++++++++ MdePkg/MdePkg.dec | 3 +++ 3 files changed, 72 insertions(+) create mode 100644 MdePkg/Include/Protocol/Tdx.h create mode 100644 MdePkg/Include/Protocol/TdxAcpi.h diff --git a/MdePkg/Include/Protocol/Tdx.h b/MdePkg/Include/Protocol/Tdx.h new file mode 100644 index 000000000000..1dc427253add --- /dev/null +++ b/MdePkg/Include/Protocol/Tdx.h @@ -0,0 +1,40 @@ +/** @file + If TD-Guest firmware supports measurement and an event is created, TD-Guest + firmware is designed to report the event log with the same data structure + in TCG-Platform-Firmware-Profile specification with + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 format. + + The TD-Guest firmware supports measurement, the TD Guest Firmware is designed + to produce EFI_TD_PROTOCOL with new GUID EFI_TD_PROTOCOL_GUID to report + event log and provides hash capability. + +Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#ifndef __EFI_TDX_PROTOCOL_H__ +#define __EFI_TDX_PROTOCOL_H__ + +#include + +#define REG_TYPE_NA 0 +#define REG_TYPE_MRTD 1 +#define REG_TYPE_RTMR 2 +#define PCR_COUNT 16 + +typedef struct { + UINT8 Pcr; // PCR index + UINT8 RegType; // RTMR or MRTD + UINT8 Index; // index in RTMR/MRTD + UINT8 EventlogIndex;// index in EventLog +} PCR_TDX_EXTEND_MAP; + +#define EFI_TD_PROTOCOL_GUID \ + {0x96751a3d, 0x72f4, 0x41a6, { 0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b }} + +extern EFI_GUID gTdTcg2ProtocolGuid; + + +#endif \ No newline at end of file diff --git a/MdePkg/Include/Protocol/TdxAcpi.h b/MdePkg/Include/Protocol/TdxAcpi.h new file mode 100644 index 000000000000..f7cd2eac5d4f --- /dev/null +++ b/MdePkg/Include/Protocol/TdxAcpi.h @@ -0,0 +1,29 @@ +/** @file + TBD + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#ifndef __TDX_ACPI_H__ +#define __TDX_ACPI_H__ + +#include + +#define EFI_TDX_EVENTLOG_ACPI_TABLE_SIGNATURE SIGNATURE_32('T', 'D', 'E', 'L') +#define EFI_TDX_EVENTLOG_ACPIT_TABLE_REVISION 1 + +#pragma pack(1) + +typedef struct { + EFI_ACPI_DESCRIPTION_HEADER Header; + UINT32 Rsvd; + UINT64 Laml; + UINT64 Lasa; +} EFI_TDX_EVENTLOG_ACPI_TABLE; + +#pragma pack() + +#endif \ No newline at end of file diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec index 3928db65d188..888d92086a49 100644 --- a/MdePkg/MdePkg.dec +++ b/MdePkg/MdePkg.dec @@ -983,6 +983,9 @@ gEfiPeiDelayedDispatchPpiGuid = { 0x869c711d, 0x649c, 0x44fe, { 0x8b, 0x9e, 0x2c, 0xbb, 0x29, 0x11, 0xc3, 0xe6 }} [Protocols] + ## Include/Protocol/Tdx.h + gTdTcg2ProtocolGuid = {0x96751a3d, 0x72f4, 0x41a6, {0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b}} + ## Include/Protocol/Pcd.h gPcdProtocolGuid = { 0x11B34006, 0xD85B, 0x4D0A, { 0xA2, 0x90, 0xD5, 0xA5, 0x71, 0x31, 0x0E, 0xF7 }} From 4b2cc220fa35e6a460cc7f91a73ae8f83e883d11 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 11:02:05 +0800 Subject: [PATCH 36/54] SecurityPkg/Tcg: Tcg2Dxe return EFI_UNSUPPORTED in Td guest Intel TDX doesn't support TPM. So Tcg2Dxe returns EFI_UNSUPPORTED in Td guest. --- SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c | 5 +++++ SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf | 1 + 2 files changed, 6 insertions(+) diff --git a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c index 6d17616c1ce4..8658135c7c82 100644 --- a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c +++ b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c @@ -44,6 +44,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #define PERF_ID_TCG2_DXE 0x3120 @@ -2637,6 +2638,10 @@ DriverEntry ( UINT32 ActivePCRBanks; UINT32 NumberOfPCRBanks; + if (ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + mImageHandle = ImageHandle; if (CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceNoneGuid) || diff --git a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf index 7dc7a2683d71..a427f7bbdd64 100644 --- a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf +++ b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf @@ -64,6 +64,7 @@ ReportStatusCodeLib Tcg2PhysicalPresenceLib PeCoffLib + TdxProbeLib [Guids] ## SOMETIMES_CONSUMES ## Variable:L"SecureBoot" From a2ae7d0910d34d6453e1752713294874bb587ab7 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 13:54:22 +0800 Subject: [PATCH 37/54] OvmfPkg: Add HashLibBaseCryptoRouterTdx for Tdx HashLib for Tdx guest --- .../HashLibBaseCryptoRouterCommon.c | 67 ++++ .../HashLibBaseCryptoRouterCommon.h | 38 +++ .../HashLibBaseCryptoRouterDxe.c | 317 ++++++++++++++++++ .../HashLibBaseCryptoRouterDxe.inf | 52 +++ .../HashLibBaseCryptoRouterDxe.uni | 18 + 5 files changed, 492 insertions(+) create mode 100644 OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.c create mode 100644 OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.h create mode 100644 OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c create mode 100644 OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf create mode 100644 OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.uni diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.c b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.c new file mode 100644 index 000000000000..6a58697d977a --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.c @@ -0,0 +1,67 @@ +/** @file + This is BaseCrypto router support function. + +Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + EFI_GUID Guid; + UINT32 Mask; +} TPM2_HASH_MASK; + +TPM2_HASH_MASK mTpm2HashMask[] = { + {HASH_ALGORITHM_SHA384_GUID, HASH_ALG_SHA384}, +}; + +/** + The function get hash mask info from algorithm. + + @param HashGuid Hash Guid + + @return HashMask +**/ +UINT32 +EFIAPI +Tpm2GetHashMaskFromAlgo ( + IN EFI_GUID *HashGuid + ) +{ + UINTN Index; + for (Index = 0; Index < sizeof(mTpm2HashMask)/sizeof(mTpm2HashMask[0]); Index++) { + if (CompareGuid (HashGuid, &mTpm2HashMask[Index].Guid)) { + return mTpm2HashMask[Index].Mask; + } + } + return 0; +} + +/** + The function set digest to digest list. + + @param DigestList digest list + @param Digest digest data +**/ +VOID +EFIAPI +Tpm2SetHashToDigestList ( + IN OUT TPML_DIGEST_VALUES *DigestList, + IN TPML_DIGEST_VALUES *Digest + ) +{ + CopyMem ( + &DigestList->digests[DigestList->count], + &Digest->digests[0], + sizeof(Digest->digests[0]) + ); + DigestList->count ++; +} diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.h b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.h new file mode 100644 index 000000000000..6af53aa206a7 --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.h @@ -0,0 +1,38 @@ +/** @file + This is BaseCrypto router support function definition. + +Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _HASH_LIB_BASE_CRYPTO_ROUTER_COMMON_H_ +#define _HASH_LIB_BASE_CRYPTO_ROUTER_COMMON_H_ + +/** + The function get hash mask info from algorithm. + + @param HashGuid Hash Guid + + @return HashMask +**/ +UINT32 +EFIAPI +Tpm2GetHashMaskFromAlgo ( + IN EFI_GUID *HashGuid + ); + +/** + The function set digest to digest list. + + @param DigestList digest list + @param Digest digest data +**/ +VOID +EFIAPI +Tpm2SetHashToDigestList ( + IN OUT TPML_DIGEST_VALUES *DigestList, + IN TPML_DIGEST_VALUES *Digest + ); + +#endif diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c new file mode 100644 index 000000000000..577645b69457 --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c @@ -0,0 +1,317 @@ +/** @file + This library is BaseCrypto router. It will redirect hash request to each individual + hash handler registered, such as SHA1, SHA256. + Platform can use PcdTdxHashMask to mask some hash engines. + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HashLibBaseCryptoRouterCommon.h" + +extern PCR_TDX_EXTEND_MAP mPcrTdxExtendMaps[PCR_COUNT]; + +HASH_INTERFACE mHashInterface[HASH_COUNT] = {{{0}, NULL, NULL, NULL}}; +UINTN mHashInterfaceCount = 0; + +UINT32 mSupportedHashMaskLast = 0; +UINT32 mSupportedHashMaskCurrent = 0; + +/** + Check mismatch of supported HashMask between modules + that may link different HashInstanceLib instances. + +**/ +VOID +CheckSupportedHashMaskMismatch ( + VOID + ) +{ + if (mSupportedHashMaskCurrent != mSupportedHashMaskLast) { + DEBUG (( + DEBUG_WARN, + "WARNING: There is mismatch of supported HashMask (0x%x - 0x%x) between modules\n", + mSupportedHashMaskCurrent, + mSupportedHashMaskLast + )); + DEBUG ((DEBUG_WARN, "that are linking different HashInstanceLib instances!\n")); + } +} + +/** + Start hash sequence. + + @param HashHandle Hash handle. + + @retval EFI_SUCCESS Hash sequence start and HandleHandle returned. + @retval EFI_OUT_OF_RESOURCES No enough resource to start hash. +**/ +EFI_STATUS +EFIAPI +HashStart ( + OUT HASH_HANDLE *HashHandle + ) +{ + HASH_HANDLE *HashCtx; + UINTN Index; + UINT32 HashMask; + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + CheckSupportedHashMaskMismatch (); + + HashCtx = AllocatePool (sizeof(*HashCtx) * mHashInterfaceCount); + ASSERT (HashCtx != NULL); + + for (Index = 0; Index < mHashInterfaceCount; Index++) { + HashMask = Tpm2GetHashMaskFromAlgo (&mHashInterface[Index].HashGuid); + if ((HashMask & PcdGet32 (PcdTdxHashMask)) != 0) { + mHashInterface[Index].HashInit (&HashCtx[Index]); + } + } + + *HashHandle = (HASH_HANDLE)HashCtx; + + return EFI_SUCCESS; +} + +/** + Update hash sequence data. + + @param HashHandle Hash handle. + @param DataToHash Data to be hashed. + @param DataToHashLen Data size. + + @retval EFI_SUCCESS Hash sequence updated. +**/ +EFI_STATUS +EFIAPI +HashUpdate ( + IN HASH_HANDLE HashHandle, + IN VOID *DataToHash, + IN UINTN DataToHashLen + ) +{ + HASH_HANDLE *HashCtx; + UINTN Index; + UINT32 HashMask; + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + CheckSupportedHashMaskMismatch (); + + HashCtx = (HASH_HANDLE *)HashHandle; + + for (Index = 0; Index < mHashInterfaceCount; Index++) { + HashMask = Tpm2GetHashMaskFromAlgo (&mHashInterface[Index].HashGuid); + if ((HashMask & PcdGet32 (PcdTdxHashMask)) != 0) { + mHashInterface[Index].HashUpdate (HashCtx[Index], DataToHash, DataToHashLen); + } + } + + return EFI_SUCCESS; +} + +/** + Hash sequence complete and extend to PCR. + + @param HashHandle Hash handle. + @param PcrIndex PCR to be extended. + @param DataToHash Data to be hashed. + @param DataToHashLen Data size. + @param DigestList Digest list. + + @retval EFI_SUCCESS Hash sequence complete and DigestList is returned. +**/ +EFI_STATUS +EFIAPI +HashCompleteAndExtend ( + IN HASH_HANDLE HashHandle, + IN TPMI_DH_PCR PcrIndex, + IN VOID *DataToHash, + IN UINTN DataToHashLen, + OUT TPML_DIGEST_VALUES *DigestList + ) +{ + TPML_DIGEST_VALUES Digest; + HASH_HANDLE *HashCtx; + UINTN Index; + EFI_STATUS Status; + UINT32 HashMask; + PCR_TDX_EXTEND_MAP PcrTdxExtendMap; + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + PcrTdxExtendMap = mPcrTdxExtendMaps[PcrIndex]; + ASSERT(PcrTdxExtendMap.Pcr == PcrIndex); + + CheckSupportedHashMaskMismatch (); + + HashCtx = (HASH_HANDLE *)HashHandle; + ZeroMem (DigestList, sizeof(*DigestList)); + + for (Index = 0; Index < mHashInterfaceCount; Index++) { + HashMask = Tpm2GetHashMaskFromAlgo (&mHashInterface[Index].HashGuid); + if ((HashMask & PcdGet32 (PcdTdxHashMask)) != 0) { + mHashInterface[Index].HashUpdate (HashCtx[Index], DataToHash, DataToHashLen); + mHashInterface[Index].HashFinal (HashCtx[Index], &Digest); + Tpm2SetHashToDigestList (DigestList, &Digest); + } + } + + FreePool (HashCtx); + + ASSERT(DigestList->count == 1 && DigestList->digests[0].hashAlg == TPM_ALG_SHA384); + + Status = TdExtendRtmr ( + (UINT32*)DigestList->digests[0].digest.sha384, + SHA384_DIGEST_SIZE, + (UINT8)PcrTdxExtendMap.Index + ); + return Status; +} + +/** + Hash data and extend to PCR. + + @param PcrIndex PCR to be extended. + @param DataToHash Data to be hashed. + @param DataToHashLen Data size. + @param DigestList Digest list. + + @retval EFI_SUCCESS Hash data and DigestList is returned. +**/ +EFI_STATUS +EFIAPI +HashAndExtend ( + IN TPMI_DH_PCR PcrIndex, + IN VOID *DataToHash, + IN UINTN DataToHashLen, + OUT TPML_DIGEST_VALUES *DigestList + ) +{ + HASH_HANDLE HashHandle; + EFI_STATUS Status; + + DEBUG((DEBUG_INFO, "Td: HashAndExtend: %d, %p, 0x%x\n", PcrIndex, DataToHash, DataToHashLen)); + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + CheckSupportedHashMaskMismatch (); + + HashStart (&HashHandle); + HashUpdate (HashHandle, DataToHash, DataToHashLen); + Status = HashCompleteAndExtend (HashHandle, PcrIndex, NULL, 0, DigestList); + + return Status; +} + +/** + This service register Hash. + + @param HashInterface Hash interface + + @retval EFI_SUCCESS This hash interface is registered successfully. + @retval EFI_UNSUPPORTED System does not support register this interface. + @retval EFI_ALREADY_STARTED System already register this interface. +**/ +EFI_STATUS +EFIAPI +RegisterHashInterfaceLib ( + IN HASH_INTERFACE *HashInterface + ) +{ + UINTN Index; + UINT32 HashMask; + EFI_STATUS Status; + + // + // Check allow + // + HashMask = Tpm2GetHashMaskFromAlgo (&HashInterface->HashGuid); + if ((HashMask & PcdGet32 (PcdTdxHashMask)) == 0) { + return EFI_UNSUPPORTED; + } + DEBUG((DEBUG_INFO, "TD: Hash is registered. mask = 0x%x, guid = %g\n", HashMask, HashInterface->HashGuid)); + + if (mHashInterfaceCount >= sizeof(mHashInterface)/sizeof(mHashInterface[0])) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Check duplication + // + for (Index = 0; Index < mHashInterfaceCount; Index++) { + if (CompareGuid (&mHashInterface[Index].HashGuid, &HashInterface->HashGuid)) { + DEBUG ((DEBUG_ERROR, "Hash Interface (%g) has been registered\n", &HashInterface->HashGuid)); + return EFI_ALREADY_STARTED; + } + } + + // + // Record hash algorithm bitmap of CURRENT module which consumes HashLib. + // + mSupportedHashMaskCurrent = PcdGet32 (PcdTdxHashAlgorithmBitmap) | HashMask; + Status = PcdSet32S (PcdTdxHashAlgorithmBitmap, mSupportedHashMaskCurrent); + ASSERT_EFI_ERROR (Status); + + CopyMem (&mHashInterface[mHashInterfaceCount], HashInterface, sizeof(*HashInterface)); + mHashInterfaceCount ++; + + return EFI_SUCCESS; +} + +/** + The constructor function of HashLibBaseCryptoRouterDxe. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor executed correctly. + +**/ +EFI_STATUS +EFIAPI +HashLibBaseCryptoRouterDxeConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + if (!ProbeTdGuest()) { + return EFI_SUCCESS; + } + + // + // Record hash algorithm bitmap of LAST module which also consumes HashLib. + // + mSupportedHashMaskLast = PcdGet32 (PcdTdxHashAlgorithmBitmap); + + // + // Set PcdTdxHashAlgorithmBitmap to 0 in CONSTRUCTOR for CURRENT module. + // + Status = PcdSet32S (PcdTdxHashAlgorithmBitmap, 0); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf new file mode 100644 index 000000000000..e746b619704a --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf @@ -0,0 +1,52 @@ +## @file +# Provides hash service by registered hash handler in Td guest +# +# This library is BaseCrypto router. It will redirect hash request to each individual +# hash handler registered, such as SHA1, SHA256. Platform can use PcdTpm2HashMask to +# mask some hash engines. +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HashLibBaseCryptoRouterDxe + MODULE_UNI_FILE = HashLibBaseCryptoRouterDxe.uni + FILE_GUID = 77F6EA3E-1ABA-4467-A447-926E8CEB2D13 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = HashLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = HashLibBaseCryptoRouterDxeConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + HashLibBaseCryptoRouterCommon.h + HashLibBaseCryptoRouterCommon.c + HashLibBaseCryptoRouterDxe.c + +[Packages] + MdePkg/MdePkg.dec + SecurityPkg/SecurityPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + PcdLib + TdxProbeLib + TdxLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashMask ## CONSUMES + ## SOMETIMES_CONSUMES + ## SOMETIMES_PRODUCES + gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashAlgorithmBitmap diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.uni b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.uni new file mode 100644 index 000000000000..29fdd4255ca6 --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.uni @@ -0,0 +1,18 @@ +// /** @file +// Provides hash service by registered hash handler +// +// This library is BaseCrypto router. It will redirect hash request to each individual +// hash handler registered, such as SHA1, SHA256. Platform can use PcdTpm2HashMask to +// mask some hash engines. +// +// Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Provides hash service by registered hash handler" + +#string STR_MODULE_DESCRIPTION #language en-US "This library is BaseCrypto router. It will redirect hash request to each individual hash handler registered, such as SHA1, SHA256. Platform can use PcdTpm2HashMask to mask some hash engines." + From 91581db8a626638e13777a419d47a8c3b1bd0cae Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 15:35:38 +0800 Subject: [PATCH 38/54] OvmfPkg/Tcg: Add TdTcg2Dxe for Tdx guest --- OvmfPkg/OvmfPkg.dec | 15 + OvmfPkg/OvmfPkgX64.dsc | 12 + OvmfPkg/OvmfPkgX64.fdf | 5 + OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c | 405 ++++ OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c | 2764 +++++++++++++++++++++++ OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf | 111 + OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni | 21 + OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni | 12 + 8 files changed, 3345 insertions(+) create mode 100644 OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c create mode 100644 OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c create mode 100644 OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf create mode 100644 OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni create mode 100644 OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index c2d5ba6964b9..f54a739a5833 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -382,6 +382,21 @@ gUefiOvmfPkgTokenSpaceGuid.PcdTdxSharedPageMask|0x0|UINT64|0x101 gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase|0|UINT64|0x102 + ## This PCD records LAML field in TDX EVENTLOG ACPI table. + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLaml|0|UINT32|0x103 + + ## This PCD records LASA field in TDX EVENTLOG ACPI table. + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLasa|0|UINT64|0x104 + ## This PCD indicates Hash mask for TDX 1.0 Bit definition strictly follows + # TCG Algorithm Registry. + # TDX 1.0 supports SHA384 only. + # BIT2 - SHA384.
+ gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashMask|0x00000004|UINT32|0x105 + + ## This PCD indicated final BIOS supported Hash mask. + # Bios may choose to register a subset of PcdTdxHashMask. + # So this PCD is final value of how many hash algo is extended to RTMR. + gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashAlgorithmBitmap|0x4|UINT32|0x106 [PcdsFeatureFlag] gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 47392607d396..b9966d78a1b7 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -40,6 +40,7 @@ # DEFINE TDX_IGNORE_VE_HLT = FALSE DEFINE TDX_EMULATION_ENABLE = FALSE + # Network definition # DEFINE NETWORK_TLS_ENABLE = FALSE @@ -1073,6 +1074,17 @@ } !endif + # + # EFI TD PROTOCOL + # + OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf { + + #TdxLib|MdePkg/Library/TdxLib/TdxLib.inf + HashLib|OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf + NULL|SecurityPkg/Library/HashInstanceLibSha384/HashInstanceLibSha384.inf + } + + # # TPM support # diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index abebe3adb296..b693365545d4 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -400,6 +400,11 @@ INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf !endif +# +# TDX support +# +INF OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf + # # TPM support # diff --git a/OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c b/OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c new file mode 100644 index 000000000000..f434fdf6a28d --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c @@ -0,0 +1,405 @@ +/** @file + This module implements measuring PeCoff image for Tcg2 Protocol. + + Caution: This file requires additional review when modified. + This driver will have external input - PE/COFF image. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +UINTN mTcg2DxeImageSize = 0; + +/** + Reads contents of a PE/COFF image in memory buffer. + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will make sure the PE/COFF image content + read is within the image buffer. + + @param FileHandle Pointer to the file handle to read the PE/COFF image. + @param FileOffset Offset into the PE/COFF image to begin the read operation. + @param ReadSize On input, the size in bytes of the requested read operation. + On output, the number of bytes actually read. + @param Buffer Output buffer that contains the data read from the PE/COFF image. + + @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size +**/ +EFI_STATUS +EFIAPI +Tcg2DxeImageRead ( + IN VOID *FileHandle, + IN UINTN FileOffset, + IN OUT UINTN *ReadSize, + OUT VOID *Buffer + ) +{ + UINTN EndPosition; + + if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MAX_ADDRESS - FileOffset < *ReadSize) { + return EFI_INVALID_PARAMETER; + } + + EndPosition = FileOffset + *ReadSize; + if (EndPosition > mTcg2DxeImageSize) { + *ReadSize = (UINT32)(mTcg2DxeImageSize - FileOffset); + } + + if (FileOffset >= mTcg2DxeImageSize) { + *ReadSize = 0; + } + + CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize); + + return EFI_SUCCESS; +} + +/** + Measure PE image into TPM log based on the authenticode image hashing in + PE/COFF Specification 8.0 Appendix A. + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo(). + + @param[in] PCRIndex TPM PCR index + @param[in] ImageAddress Start address of image buffer. + @param[in] ImageSize Image size + @param[out] DigestList Digest list of this image. + + @retval EFI_SUCCESS Successfully measure image. + @retval EFI_OUT_OF_RESOURCES No enough resource to measure image. + @retval other error value +**/ +EFI_STATUS +MeasurePeImageAndExtend ( + IN UINT32 PCRIndex, + IN EFI_PHYSICAL_ADDRESS ImageAddress, + IN UINTN ImageSize, + OUT TPML_DIGEST_VALUES *DigestList + ) +{ + EFI_STATUS Status; + EFI_IMAGE_DOS_HEADER *DosHdr; + UINT32 PeCoffHeaderOffset; + EFI_IMAGE_SECTION_HEADER *Section; + UINT8 *HashBase; + UINTN HashSize; + UINTN SumOfBytesHashed; + EFI_IMAGE_SECTION_HEADER *SectionHeader; + UINTN Index; + UINTN Pos; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + UINT32 NumberOfRvaAndSizes; + UINT32 CertSize; + HASH_HANDLE HashHandle; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + HashHandle = 0xFFFFFFFF; // Know bad value + + Status = EFI_UNSUPPORTED; + SectionHeader = NULL; + + // + // Check PE/COFF image + // + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = (VOID *) (UINTN) ImageAddress; + mTcg2DxeImageSize = ImageSize; + ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) Tcg2DxeImageRead; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + // + // The information can't be got from the invalid PeImage + // + DEBUG ((DEBUG_INFO, "Tcg2Dxe: PeImage invalid. Cannot retrieve image information.\n")); + goto Finish; + } + + DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress; + PeCoffHeaderOffset = 0; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + PeCoffHeaderOffset = DosHdr->e_lfanew; + } + + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset); + if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { + Status = EFI_UNSUPPORTED; + goto Finish; + } + + // + // PE/COFF Image Measurement + // + // NOTE: The following codes/steps are based upon the authenticode image hashing in + // PE/COFF Specification 8.0 Appendix A. + // + // + + // 1. Load the image header into memory. + + // 2. Initialize a SHA hash context. + + Status = HashStart (&HashHandle); + if (EFI_ERROR (Status)) { + goto Finish; + } + + // + // Measuring PE/COFF Image Header; + // But CheckSum field and SECURITY data directory (certificate) are excluded + // + + // + // 3. Calculate the distance from the base of the image header to the image checksum address. + // 4. Hash the image header from its base to beginning of the image checksum. + // + HashBase = (UINT8 *) (UINTN) ImageAddress; + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; + HashSize = (UINTN) (&Hdr.Pe32->OptionalHeader.CheckSum) - (UINTN) HashBase; + } else { + // + // Use PE32+ offset + // + NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + HashSize = (UINTN) (&Hdr.Pe32Plus->OptionalHeader.CheckSum) - (UINTN) HashBase; + } + + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + + // + // 5. Skip over the image checksum (it occupies a single ULONG). + // + if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { + // + // 6. Since there is no Cert Directory in optional header, hash everything + // from the end of the checksum to the end of image header. + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } else { + // + // Use PE32+ offset. + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } + + if (HashSize != 0) { + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } + } else { + // + // 7. Hash everything from the end of the checksum to the start of the Cert Directory. + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) (&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase; + } else { + // + // Use PE32+ offset + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) (&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase; + } + + if (HashSize != 0) { + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } + + // + // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) + // 9. Hash everything from the end of the Cert Directory to the end of image header. + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } else { + // + // Use PE32+ offset + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } + + if (HashSize != 0) { + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } + } + + // + // 10. Set the SUM_OF_BYTES_HASHED to the size of the header + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders; + } else { + // + // Use PE32+ offset + // + SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders; + } + + // + // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER + // structures in the image. The 'NumberOfSections' field of the image + // header indicates how big the table should be. Do not include any + // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. + // + SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections); + if (SectionHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Finish; + } + + // + // 12. Using the 'PointerToRawData' in the referenced section headers as + // a key, arrange the elements in the table in ascending order. In other + // words, sort the section headers according to the disk-file offset of + // the section. + // + Section = (EFI_IMAGE_SECTION_HEADER *) ( + (UINT8 *) (UINTN) ImageAddress + + PeCoffHeaderOffset + + sizeof(UINT32) + + sizeof(EFI_IMAGE_FILE_HEADER) + + Hdr.Pe32->FileHeader.SizeOfOptionalHeader + ); + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + Pos = Index; + while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { + CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof(EFI_IMAGE_SECTION_HEADER)); + Pos--; + } + CopyMem (&SectionHeader[Pos], Section, sizeof(EFI_IMAGE_SECTION_HEADER)); + Section += 1; + } + + // + // 13. Walk through the sorted table, bring the corresponding section + // into memory, and hash the entire section (using the 'SizeOfRawData' + // field in the section header to determine the amount of data to hash). + // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . + // 15. Repeat steps 13 and 14 for all the sections in the sorted table. + // + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + Section = (EFI_IMAGE_SECTION_HEADER *) &SectionHeader[Index]; + if (Section->SizeOfRawData == 0) { + continue; + } + HashBase = (UINT8 *) (UINTN) ImageAddress + Section->PointerToRawData; + HashSize = (UINTN) Section->SizeOfRawData; + + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + + SumOfBytesHashed += HashSize; + } + + // + // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra + // data in the file that needs to be added to the hash. This data begins + // at file offset SUM_OF_BYTES_HASHED and its length is: + // FileSize - (CertDirectory->Size) + // + if (ImageSize > SumOfBytesHashed) { + HashBase = (UINT8 *) (UINTN) ImageAddress + SumOfBytesHashed; + + if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { + CertSize = 0; + } else { + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + } else { + // + // Use PE32+ offset. + // + CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + } + } + + if (ImageSize > CertSize + SumOfBytesHashed) { + HashSize = (UINTN) (ImageSize - CertSize - SumOfBytesHashed); + + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } else if (ImageSize < CertSize + SumOfBytesHashed) { + Status = EFI_UNSUPPORTED; + goto Finish; + } + } + + // + // 17. Finalize the SHA hash. + // + Status = HashCompleteAndExtend (HashHandle, PCRIndex, NULL, 0, DigestList); + if (EFI_ERROR (Status)) { + goto Finish; + } + +Finish: + if (SectionHeader != NULL) { + FreePool (SectionHeader); + } + + return Status; +} diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c new file mode 100644 index 000000000000..bcc16114e30c --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c @@ -0,0 +1,2764 @@ +/** @file + This module implements EFI TD Protocol. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + If TD guest firmware supports measurement and an event is created, + TD guest firmware is designed to report the event log with the same + data structure in TCG-Platform-Firmware-Profile spec with + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 format. + + So there is map between the TDX MRTD/RTMR registers and TPM PCRs. + MRTD => PCR[0] + RTMR[0] => PCR[1,7] + RTMR[1] => PCR[2,3,4,5,6] + RTMR[2] => PCR[8~15] + RTMR[3] => NA + **/ +PCR_TDX_EXTEND_MAP mPcrTdxExtendMaps[PCR_COUNT] = { + {0 , REG_TYPE_MRTD, 0, 0}, + {1 , REG_TYPE_RTMR, 0, 1}, + {2 , REG_TYPE_RTMR, 1, 2}, + {3 , REG_TYPE_RTMR, 1, 2}, + {4 , REG_TYPE_RTMR, 1, 2}, + {5 , REG_TYPE_RTMR, 1, 2}, + {6 , REG_TYPE_RTMR, 1, 2}, + {7 , REG_TYPE_RTMR, 0, 1}, + {8 , REG_TYPE_RTMR, 2, 3}, + {9 , REG_TYPE_RTMR, 2, 3}, + {10, REG_TYPE_RTMR, 2, 3}, + {11, REG_TYPE_RTMR, 2, 3}, + {12, REG_TYPE_RTMR, 2, 3}, + {13, REG_TYPE_RTMR, 2, 3}, + {14, REG_TYPE_RTMR, 2, 3}, + {15, REG_TYPE_RTMR, 2, 3}, +}; + + +#define PERF_ID_TD_TCG2_DXE 0x3130 +typedef struct { + CHAR16 *VariableName; + EFI_GUID *VendorGuid; +} VARIABLE_TYPE; + +// TODO TDX command/response size +#define TCG2_DEFAULT_MAX_COMMAND_SIZE 0x1000 +#define TCG2_DEFAULT_MAX_RESPONSE_SIZE 0x1000 + +typedef struct { + EFI_GUID *EventGuid; + EFI_TCG2_EVENT_LOG_FORMAT LogFormat; +} TCG2_EVENT_INFO_STRUCT; + +TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = { + {&gTcgEvent2EntryHobGuid, EFI_TCG2_EVENT_LOG_FORMAT_TCG_2}, +}; + +#define TCG_EVENT_LOG_AREA_COUNT_MAX 1 + +typedef struct { + EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat; + EFI_PHYSICAL_ADDRESS Lasa; + UINT64 Laml; + UINTN EventLogSize; + UINT8 *LastEvent; + BOOLEAN EventLogStarted; + BOOLEAN EventLogTruncated; + UINTN Next800155EventOffset; +} TCG_EVENT_LOG_AREA_STRUCT; + +#define HANDOFF_TABLE_DESC "TdxTable" + typedef struct { + UINT8 TableDescriptionSize; + UINT8 TableDescription[sizeof(HANDOFF_TABLE_DESC)]; + UINT64 NumberOfTables; + EFI_CONFIGURATION_TABLE TableEntry[1]; + } TDX_HANDOFF_TABLE_POINTERS2; + + typedef struct { + UINT32 count; + TPMI_ALG_HASH hashAlg; + BYTE sha384[SHA384_DIGEST_SIZE]; + } TDX_DIGEST_VALUE; + + typedef struct { + UINT32 Signature; + UINT8 *HashData; + UINTN HashDataLen; + } TDX_EVENT; + + +typedef struct _TCG_DXE_DATA { + EFI_TCG2_BOOT_SERVICE_CAPABILITY BsCap; + TCG_EVENT_LOG_AREA_STRUCT EventLogAreaStruct[TCG_EVENT_LOG_AREA_COUNT_MAX]; + BOOLEAN GetEventLogCalled[TCG_EVENT_LOG_AREA_COUNT_MAX]; + TCG_EVENT_LOG_AREA_STRUCT FinalEventLogAreaStruct[TCG_EVENT_LOG_AREA_COUNT_MAX]; + EFI_TCG2_FINAL_EVENTS_TABLE *FinalEventsTable[TCG_EVENT_LOG_AREA_COUNT_MAX]; +} TCG_DXE_DATA; + +TCG_DXE_DATA mTcgDxeData = { + { + sizeof (EFI_TCG2_BOOT_SERVICE_CAPABILITY), // Size + { 1, 1 }, // StructureVersion + { 1, 1 }, // ProtocolVersion + EFI_TCG2_BOOT_HASH_ALG_SHA384, // HashAlgorithmBitmap + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, // SupportedEventLogs + TRUE, // TPMPresentFlag + TCG2_DEFAULT_MAX_COMMAND_SIZE, // MaxCommandSize + TCG2_DEFAULT_MAX_RESPONSE_SIZE, // MaxResponseSize + 0, // ManufacturerID + 0, // NumberOfPCRBanks + 0, // ActivePcrBanks + }, +}; + +UINTN mBootAttempts = 0; +CHAR16 mBootVarName[] = L"BootOrder"; + +VARIABLE_TYPE mVariableType[] = { + {EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid}, + {EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid}, + {EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid}, + {EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid}, + {EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid}, +}; + +EFI_TDX_EVENTLOG_ACPI_TABLE mTdxEventlogAcpiTemplate = { + { + EFI_TDX_EVENTLOG_ACPI_TABLE_SIGNATURE, + sizeof(mTdxEventlogAcpiTemplate), + EFI_TDX_EVENTLOG_ACPIT_TABLE_REVISION, + // + // Compiler initializes the remaining bytes to 0 + // These fields should be filled in production + // + }, + 0, // rsvd + 0, // laml + 0, // lasa +}; + +typedef struct{ + TPMI_ALG_HASH HashAlgo; + UINT16 HashSize; + UINT32 HashMask; +}TDX_HASH_INFO; + +static TDX_HASH_INFO mHashInfo[] = { + {TPM_ALG_SHA384, SHA384_DIGEST_SIZE, HASH_ALG_SHA384} +}; + +/** + Get hash size based on Algo + + @param[in] HashAlgo Hash Algorithm Id. + + @return Size of the hash. +**/ +UINT16 +GetHashSizeFromAlgo( + IN TPMI_ALG_HASH HashAlgo + ) +{ + UINTN Index; + + for(Index = 0; Index < sizeof(mHashInfo)/sizeof(mHashInfo[0]); Index++){ + if(mHashInfo[Index].HashAlgo == HashAlgo){ + return mHashInfo[Index].HashSize; + } + } + + return 0; +} + +/** + Get hash mask based on Algo + + @param[in] HashAlgo Hash Algorithm Id. + + @return Hash mask. +**/ +UINT32 +GetHashMaskFromAlgo( + IN TPMI_ALG_HASH HashAlgo + ) +{ + UINTN Index; + + for(Index = 0; Index < sizeof(mHashInfo)/sizeof(mHashInfo[0]); Index++){ + if(mHashInfo[Index].HashAlgo == HashAlgo){ + return mHashInfo[Index].HashMask; + } + } + + return 0; +} + +/** + Copy TPML_DIGEST_VALUES into a buffer + + @param[in,out] Buffer Buffer to hold copied TPML_DIGEST_VALUES compact binary. + @param[in] DigestList TPML_DIGEST_VALUES to be copied. + @param[in] HashAlgorithmMask HASH bits corresponding to the desired digests to copy. + + @return The end of buffer to hold TPML_DIGEST_VALUES. +**/ +VOID * +CopyDigestListToBuffer ( + IN OUT VOID *Buffer, + IN TPML_DIGEST_VALUES *DigestList, + IN UINT32 HashAlgorithmMask + ) +{ + UINTN Index; + UINT16 DigestSize; + UINT32 DigestListCount; + UINT32 *DigestListCountPtr; + + DigestListCountPtr = (UINT32 *) Buffer; + DigestListCount = 0; + Buffer = (UINT8 *)Buffer + sizeof(DigestList->count); + for (Index = 0; Index < DigestList->count; Index++) { + if((DigestList->digests[Index].hashAlg & HashAlgorithmMask) == 0){ + DEBUG ((EFI_D_ERROR, "WARNING: TPM2 Event log has HashAlg unsupported by PCR bank (0x%x)\n", DigestList->digests[Index].hashAlg)); + continue; + } + CopyMem (Buffer, &DigestList->digests[Index].hashAlg, sizeof(DigestList->digests[Index].hashAlg)); + Buffer = (UINT8 *)Buffer + sizeof(DigestList->digests[Index].hashAlg); + DigestSize = GetHashSizeFromAlgo (DigestList->digests[Index].hashAlg); + CopyMem (Buffer, &DigestList->digests[Index].digest, DigestSize); + Buffer = (UINT8 *)Buffer + DigestSize; + DigestListCount++; + } + WriteUnaligned32 (DigestListCountPtr, DigestListCount); + + return Buffer; +} + +EFI_HANDLE mImageHandle; + +/** + Measure PE image into TPM log based on the authenticode image hashing in + PE/COFF Specification 8.0 Appendix A. + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo(). + + @param[in] PCRIndex TPM PCR index + @param[in] ImageAddress Start address of image buffer. + @param[in] ImageSize Image size + @param[out] DigestList Digest list of this image. + + @retval EFI_SUCCESS Successfully measure image. + @retval EFI_OUT_OF_RESOURCES No enough resource to measure image. + @retval other error value +**/ +EFI_STATUS +MeasurePeImageAndExtend ( + IN UINT32 PCRIndex, + IN EFI_PHYSICAL_ADDRESS ImageAddress, + IN UINTN ImageSize, + OUT TPML_DIGEST_VALUES *DigestList + ); + +#define COLUME_SIZE (16 * 2) +/** + + This function dump raw data. + + @param Data raw data + @param Size raw data size + +**/ +VOID +InternalDumpData ( + IN UINT8 *Data, + IN UINTN Size + ) +{ + UINTN Index; + for (Index = 0; Index < Size; Index++) { + DEBUG ((EFI_D_INFO, Index == COLUME_SIZE/2 ? " | %02x" : " %02x", (UINTN)Data[Index])); + } +} + +/** + + This function initialize TCG_PCR_EVENT2_HDR for EV_NO_ACTION Event Type other than EFI Specification ID event + The behavior is defined by TCG PC Client PFP Spec. Section 9.3.4 EV_NO_ACTION Event Types + + @param[in, out] NoActionEvent Event Header of EV_NO_ACTION Event + @param[in] EventSize Event Size of the EV_NO_ACTION Event + +**/ +VOID +InitNoActionEvent ( + IN OUT TCG_PCR_EVENT2_HDR *NoActionEvent, + IN UINT32 EventSize + ) +{ + UINT32 DigestListCount; + TPMI_ALG_HASH HashAlgId; + UINT8 *DigestBuffer; + + DigestBuffer = (UINT8 *)NoActionEvent->Digests.digests; + DigestListCount = 0; + + NoActionEvent->PCRIndex = 0; + NoActionEvent->EventType = EV_NO_ACTION; + + // + // Set Hash count & hashAlg accordingly, while Digest.digests[n].digest to all 0 + // + ZeroMem (&NoActionEvent->Digests, sizeof(NoActionEvent->Digests)); + + if ((mTcgDxeData.BsCap.ActivePcrBanks & EFI_TCG2_BOOT_HASH_ALG_SHA384) != 0) { + HashAlgId = TPM_ALG_SHA384; + CopyMem (DigestBuffer, &HashAlgId, sizeof(TPMI_ALG_HASH)); + DigestBuffer += sizeof(TPMI_ALG_HASH) + GetHashSizeFromAlgo (HashAlgId); + DigestListCount++; + } + + // + // Set Digests Count + // + WriteUnaligned32 ((UINT32 *)&NoActionEvent->Digests.count, DigestListCount); + + // + // Set Event Size + // + WriteUnaligned32((UINT32 *)DigestBuffer, EventSize); +} + +/** + + This function dump raw data with colume format. + + @param Data raw data + @param Size raw data size + +**/ +VOID +InternalDumpHex ( + IN UINT8 *Data, + IN UINTN Size + ) +{ + UINTN Index; + UINTN Count; + UINTN Left; + + Count = Size / COLUME_SIZE; + Left = Size % COLUME_SIZE; + for (Index = 0; Index < Count; Index++) { + DEBUG ((EFI_D_INFO, "%04x: ", Index * COLUME_SIZE)); + InternalDumpData (Data + Index * COLUME_SIZE, COLUME_SIZE); + DEBUG ((EFI_D_INFO, "\n")); + } + + if (Left != 0) { + DEBUG ((EFI_D_INFO, "%04x: ", Index * COLUME_SIZE)); + InternalDumpData (Data + Index * COLUME_SIZE, Left); + DEBUG ((EFI_D_INFO, "\n")); + } +} + +/** + Get All processors EFI_CPU_LOCATION in system. LocationBuf is allocated inside the function + Caller is responsible to free LocationBuf. + + @param[out] LocationBuf Returns Processor Location Buffer. + @param[out] Num Returns processor number. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_UNSUPPORTED MpService protocol not found. + +**/ +EFI_STATUS +GetProcessorsCpuLocation ( + OUT EFI_CPU_PHYSICAL_LOCATION **LocationBuf, + OUT UINTN *Num + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *MpProtocol; + UINTN ProcessorNum; + UINTN EnabledProcessorNum; + EFI_PROCESSOR_INFORMATION ProcessorInfo; + EFI_CPU_PHYSICAL_LOCATION *ProcessorLocBuf; + UINTN Index; + + Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **) &MpProtocol); + if (EFI_ERROR (Status)) { + // + // MP protocol is not installed + // + return EFI_UNSUPPORTED; + } + + Status = MpProtocol->GetNumberOfProcessors( + MpProtocol, + &ProcessorNum, + &EnabledProcessorNum + ); + if (EFI_ERROR(Status)){ + return Status; + } + + Status = gBS->AllocatePool( + EfiBootServicesData, + sizeof(EFI_CPU_PHYSICAL_LOCATION) * ProcessorNum, + (VOID **) &ProcessorLocBuf + ); + if (EFI_ERROR(Status)){ + return Status; + } + + // + // Get each processor Location info + // + for (Index = 0; Index < ProcessorNum; Index++) { + Status = MpProtocol->GetProcessorInfo( + MpProtocol, + Index, + &ProcessorInfo + ); + if (EFI_ERROR(Status)){ + FreePool(ProcessorLocBuf); + return Status; + } + + // + // Get all Processor Location info & measure + // + CopyMem( + &ProcessorLocBuf[Index], + &ProcessorInfo.Location, + sizeof(EFI_CPU_PHYSICAL_LOCATION) + ); + } + + *LocationBuf = ProcessorLocBuf; + *Num = ProcessorNum; + + return Status; +} + +/** + The EFI_TCG2_PROTOCOL GetCapability function call provides protocol + capability information and state information. + + @param[in] This Indicates the calling context + @param[in, out] ProtocolCapability The caller allocates memory for a EFI_TCG2_BOOT_SERVICE_CAPABILITY + structure and sets the size field to the size of the structure allocated. + The callee fills in the fields with the EFI protocol capability information + and the current EFI TCG2 state information up to the number of fields which + fit within the size of the structure passed in. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + The ProtocolCapability variable will not be populated. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + The ProtocolCapability variable will not be populated. + @retval EFI_BUFFER_TOO_SMALL The ProtocolCapability variable is too small to hold the full response. + It will be partially populated (required Size field will be set). +**/ +EFI_STATUS +EFIAPI +Tcg2GetCapability ( + IN EFI_TCG2_PROTOCOL *This, + IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY *ProtocolCapability + ) +{ + DEBUG ((DEBUG_INFO, "TdTcg2GetCapability\n")); + return EFI_SUCCESS; +/* + DEBUG ((DEBUG_VERBOSE, "TDVF>>>> Tcg2GetCapability ...\n")); + + if ((This == NULL) || (ProtocolCapability == NULL)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((DEBUG_VERBOSE, "Size - 0x%x\n", ProtocolCapability->Size)); + DEBUG ((DEBUG_VERBOSE, " 1.1 - 0x%x, 1.0 - 0x%x\n", sizeof(EFI_TCG2_BOOT_SERVICE_CAPABILITY), sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0))); + + if (ProtocolCapability->Size < mTcgDxeData.BsCap.Size) { + // + // Handle the case that firmware support 1.1 but OS only support 1.0. + // + if ((mTcgDxeData.BsCap.ProtocolVersion.Major > 0x01) || + ((mTcgDxeData.BsCap.ProtocolVersion.Major == 0x01) && ((mTcgDxeData.BsCap.ProtocolVersion.Minor > 0x00)))) { + if (ProtocolCapability->Size >= sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0)) { + CopyMem (ProtocolCapability, &mTcgDxeData.BsCap, sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0)); + ProtocolCapability->Size = sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0); + ProtocolCapability->StructureVersion.Major = 1; + ProtocolCapability->StructureVersion.Minor = 0; + ProtocolCapability->ProtocolVersion.Major = 1; + ProtocolCapability->ProtocolVersion.Minor = 0; + DEBUG ((EFI_D_ERROR, "TreeGetCapability (Compatible) - %r\n", EFI_SUCCESS)); + return EFI_SUCCESS; + } + } + ProtocolCapability->Size = mTcgDxeData.BsCap.Size; + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (ProtocolCapability, &mTcgDxeData.BsCap, mTcgDxeData.BsCap.Size); + DEBUG ((DEBUG_VERBOSE, "Tcg2GetCapability - %r\n", EFI_SUCCESS)); + return EFI_SUCCESS; +*/ +} + +/** + This function dump PCR event. + + @param[in] EventHdr TCG PCR event structure. +**/ +VOID +DumpEvent ( + IN TCG_PCR_EVENT_HDR *EventHdr + ) +{ + UINTN Index; + + DEBUG ((EFI_D_INFO, " Event:\n")); + DEBUG ((EFI_D_INFO, " PCRIndex - %d\n", EventHdr->PCRIndex)); + DEBUG ((EFI_D_INFO, " EventType - 0x%08x\n", EventHdr->EventType)); + DEBUG ((EFI_D_INFO, " Digest - ")); + for (Index = 0; Index < sizeof(TCG_DIGEST); Index++) { + DEBUG ((EFI_D_INFO, "%02x ", EventHdr->Digest.digest[Index])); + } + DEBUG ((EFI_D_INFO, "\n")); + DEBUG ((EFI_D_INFO, " EventSize - 0x%08x\n", EventHdr->EventSize)); + InternalDumpHex ((UINT8 *)(EventHdr + 1), EventHdr->EventSize); +} + +/** + This function dump TCG_EfiSpecIDEventStruct. + + @param[in] TcgEfiSpecIdEventStruct A pointer to TCG_EfiSpecIDEventStruct. +**/ +VOID +DumpTcgEfiSpecIdEventStruct ( + IN TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct + ) +{ + TCG_EfiSpecIdEventAlgorithmSize *DigestSize; + UINTN Index; + UINT8 *VendorInfoSize; + UINT8 *VendorInfo; + UINT32 NumberOfAlgorithms; + + DEBUG ((EFI_D_INFO, " TCG_EfiSpecIDEventStruct:\n")); + DEBUG ((EFI_D_INFO, " signature - '")); + for (Index = 0; Index < sizeof(TcgEfiSpecIdEventStruct->signature); Index++) { + DEBUG ((EFI_D_INFO, "%c", TcgEfiSpecIdEventStruct->signature[Index])); + } + DEBUG ((EFI_D_INFO, "'\n")); + DEBUG ((EFI_D_INFO, " platformClass - 0x%08x\n", TcgEfiSpecIdEventStruct->platformClass)); + DEBUG ((EFI_D_INFO, " specVersion - %d.%d%d\n", TcgEfiSpecIdEventStruct->specVersionMajor, TcgEfiSpecIdEventStruct->specVersionMinor, TcgEfiSpecIdEventStruct->specErrata)); + DEBUG ((EFI_D_INFO, " uintnSize - 0x%02x\n", TcgEfiSpecIdEventStruct->uintnSize)); + + CopyMem (&NumberOfAlgorithms, TcgEfiSpecIdEventStruct + 1, sizeof(NumberOfAlgorithms)); + DEBUG ((EFI_D_INFO, " NumberOfAlgorithms - 0x%08x\n", NumberOfAlgorithms)); + + DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + sizeof(*TcgEfiSpecIdEventStruct) + sizeof(NumberOfAlgorithms)); + for (Index = 0; Index < NumberOfAlgorithms; Index++) { + DEBUG ((EFI_D_INFO, " digest(%d)\n", Index)); + DEBUG ((EFI_D_INFO, " algorithmId - 0x%04x\n", DigestSize[Index].algorithmId)); + DEBUG ((EFI_D_INFO, " digestSize - 0x%04x\n", DigestSize[Index].digestSize)); + } + VendorInfoSize = (UINT8 *)&DigestSize[NumberOfAlgorithms]; + DEBUG ((EFI_D_INFO, " VendorInfoSize - 0x%02x\n", *VendorInfoSize)); + VendorInfo = VendorInfoSize + 1; + DEBUG ((EFI_D_INFO, " VendorInfo - ")); + for (Index = 0; Index < *VendorInfoSize; Index++) { + DEBUG ((EFI_D_INFO, "%02x ", VendorInfo[Index])); + } + DEBUG ((EFI_D_INFO, "\n")); +} + +/** + This function get size of TCG_EfiSpecIDEventStruct. + + @param[in] TcgEfiSpecIdEventStruct A pointer to TCG_EfiSpecIDEventStruct. +**/ +UINTN +GetTcgEfiSpecIdEventStructSize ( + IN TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct + ) +{ + TCG_EfiSpecIdEventAlgorithmSize *DigestSize; + UINT8 *VendorInfoSize; + UINT32 NumberOfAlgorithms; + + CopyMem (&NumberOfAlgorithms, TcgEfiSpecIdEventStruct + 1, sizeof(NumberOfAlgorithms)); + + DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + sizeof(*TcgEfiSpecIdEventStruct) + sizeof(NumberOfAlgorithms)); + VendorInfoSize = (UINT8 *)&DigestSize[NumberOfAlgorithms]; + return sizeof(TCG_EfiSpecIDEventStruct) + sizeof(UINT32) + (NumberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + sizeof(UINT8) + (*VendorInfoSize); +} + +/** + This function dump PCR event 2. + + @param[in] TcgPcrEvent2 TCG PCR event 2 structure. +**/ +VOID +DumpEvent2 ( + IN TCG_PCR_EVENT2 *TcgPcrEvent2 + ) +{ + UINT32 DigestIndex; + UINT32 DigestCount; + TPMI_ALG_HASH HashAlgo; + UINT32 DigestSize; + UINT8 *DigestBuffer; + UINT32 EventSize; + UINT8 *EventBuffer; + + DEBUG ((EFI_D_INFO, " Event:\n")); + DEBUG ((EFI_D_INFO, " PCRIndex - %d\n", TcgPcrEvent2->PCRIndex)); + DEBUG ((EFI_D_INFO, " EventType - 0x%08x\n", TcgPcrEvent2->EventType)); + + DEBUG ((EFI_D_INFO, " DigestCount: 0x%08x\n", TcgPcrEvent2->Digest.count)); + + DigestCount = TcgPcrEvent2->Digest.count; + HashAlgo = TcgPcrEvent2->Digest.digests[0].hashAlg; + DigestBuffer = (UINT8 *)&TcgPcrEvent2->Digest.digests[0].digest; + for (DigestIndex = 0; DigestIndex < DigestCount; DigestIndex++) { + DEBUG ((EFI_D_INFO, " HashAlgo : 0x%04x\n", HashAlgo)); + DEBUG ((EFI_D_INFO, " Digest(%d): \n", DigestIndex)); + DigestSize = GetHashSizeFromAlgo (HashAlgo); + InternalDumpHex(DigestBuffer, DigestSize); + // + // Prepare next + // + CopyMem (&HashAlgo, DigestBuffer + DigestSize, sizeof(TPMI_ALG_HASH)); + DigestBuffer = DigestBuffer + DigestSize + sizeof(TPMI_ALG_HASH); + } + DEBUG ((EFI_D_INFO, "\n")); + DigestBuffer = DigestBuffer - sizeof(TPMI_ALG_HASH); + + CopyMem (&EventSize, DigestBuffer, sizeof(TcgPcrEvent2->EventSize)); + DEBUG ((EFI_D_INFO, " EventSize - 0x%08x\n", EventSize)); + EventBuffer = DigestBuffer + sizeof(TcgPcrEvent2->EventSize); + InternalDumpHex (EventBuffer, EventSize); +} + +/** + This function returns size of TCG PCR event 2. + + @param[in] TcgPcrEvent2 TCG PCR event 2 structure. + + @return size of TCG PCR event 2. +**/ +UINTN +GetPcrEvent2Size ( + IN TCG_PCR_EVENT2 *TcgPcrEvent2 + ) +{ + UINT32 DigestIndex; + UINT32 DigestCount; + TPMI_ALG_HASH HashAlgo; + UINT32 DigestSize; + UINT8 *DigestBuffer; + UINT32 EventSize; + UINT8 *EventBuffer; + + DigestCount = TcgPcrEvent2->Digest.count; + HashAlgo = TcgPcrEvent2->Digest.digests[0].hashAlg; + DigestBuffer = (UINT8 *)&TcgPcrEvent2->Digest.digests[0].digest; + for (DigestIndex = 0; DigestIndex < DigestCount; DigestIndex++) { + DigestSize = GetHashSizeFromAlgo (HashAlgo); + // + // Prepare next + // + CopyMem (&HashAlgo, DigestBuffer + DigestSize, sizeof(TPMI_ALG_HASH)); + DigestBuffer = DigestBuffer + DigestSize + sizeof(TPMI_ALG_HASH); + } + DigestBuffer = DigestBuffer - sizeof(TPMI_ALG_HASH); + + CopyMem (&EventSize, DigestBuffer, sizeof(TcgPcrEvent2->EventSize)); + EventBuffer = DigestBuffer + sizeof(TcgPcrEvent2->EventSize); + + return (UINTN)EventBuffer + EventSize - (UINTN)TcgPcrEvent2; +} + +/** + This function dump event log. + TDVF only supports EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 + + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[in] EventLogLocation A pointer to the memory address of the event log. + @param[in] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the + address of the start of the last entry in the event log in memory. + @param[in] FinalEventsTable A pointer to the memory address of the final event table. +**/ +VOID +DumpEventLog ( + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + IN EFI_PHYSICAL_ADDRESS EventLogLocation, + IN EFI_PHYSICAL_ADDRESS EventLogLastEntry, + IN EFI_TCG2_FINAL_EVENTS_TABLE *FinalEventsTable + ) +{ + TCG_PCR_EVENT_HDR *EventHdr; + TCG_PCR_EVENT2 *TcgPcrEvent2; + TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct; + UINTN NumberOfEvents; + + DEBUG ((EFI_D_INFO, "EventLogFormat: (0x%x)\n", EventLogFormat)); + + switch (EventLogFormat) { + case EFI_TCG2_EVENT_LOG_FORMAT_TCG_2: + // + // Dump first event + // + EventHdr = (TCG_PCR_EVENT_HDR *)(UINTN)EventLogLocation; + DumpEvent (EventHdr); + + TcgEfiSpecIdEventStruct = (TCG_EfiSpecIDEventStruct *)(EventHdr + 1); + DumpTcgEfiSpecIdEventStruct (TcgEfiSpecIdEventStruct); + + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)((UINTN)TcgEfiSpecIdEventStruct + GetTcgEfiSpecIdEventStructSize (TcgEfiSpecIdEventStruct)); + while ((UINTN)TcgPcrEvent2 <= EventLogLastEntry) { + DumpEvent2 (TcgPcrEvent2); + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)((UINTN)TcgPcrEvent2 + GetPcrEvent2Size (TcgPcrEvent2)); + } + + if (FinalEventsTable == NULL) { + DEBUG ((EFI_D_INFO, "FinalEventsTable: NOT FOUND\n")); + } else { + DEBUG ((EFI_D_INFO, "FinalEventsTable: (0x%x)\n", FinalEventsTable)); + DEBUG ((EFI_D_INFO, " Version: (0x%x)\n", FinalEventsTable->Version)); + DEBUG ((EFI_D_INFO, " NumberOfEvents: (0x%x)\n", FinalEventsTable->NumberOfEvents)); + + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)(UINTN)(FinalEventsTable + 1); + for (NumberOfEvents = 0; NumberOfEvents < FinalEventsTable->NumberOfEvents; NumberOfEvents++) { + DumpEvent2 (TcgPcrEvent2); + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)((UINTN)TcgPcrEvent2 + GetPcrEvent2Size (TcgPcrEvent2)); + } + } + break; + } + + return ; +} + +/** + The EFI_TCG2_PROTOCOL Get Event Log function call allows a caller to + retrieve the address of a given event log and its last entry. + + @param[in] This Indicates the calling context + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[out] EventLogLocation A pointer to the memory address of the event log. + @param[out] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the + address of the start of the last entry in the event log in memory. + @param[out] EventLogTruncated If the Event Log is missing at least one entry because an event would + have exceeded the area allocated for events, this value is set to TRUE. + Otherwise, the value will be FALSE and the Event Log will be complete. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect + (e.g. asking for an event log whose format is not supported). +**/ +EFI_STATUS +EFIAPI +Tcg2GetEventLog ( + IN EFI_TCG2_PROTOCOL *This, + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + OUT EFI_PHYSICAL_ADDRESS *EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry, + OUT BOOLEAN *EventLogTruncated + ) +{ + UINTN Index; + + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog ... (0x%x)\n", EventLogFormat)); + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if (EventLogFormat == mTcg2EventInfo[Index].LogFormat) { + break; + } + } + + if (Index == sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0])) { + return EFI_INVALID_PARAMETER; + } + + if ((mTcg2EventInfo[Index].LogFormat & mTcgDxeData.BsCap.SupportedEventLogs) == 0) { + return EFI_INVALID_PARAMETER; + } + + if (!mTcgDxeData.BsCap.TPMPresentFlag) { + if (EventLogLocation != NULL) { + *EventLogLocation = 0; + } + if (EventLogLastEntry != NULL) { + *EventLogLastEntry = 0; + } + if (EventLogTruncated != NULL) { + *EventLogTruncated = FALSE; + } + return EFI_SUCCESS; + } + + if (EventLogLocation != NULL) { + *EventLogLocation = mTcgDxeData.EventLogAreaStruct[Index].Lasa; + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog (EventLogLocation - %x)\n", *EventLogLocation)); + } + + if (EventLogLastEntry != NULL) { + if (!mTcgDxeData.EventLogAreaStruct[Index].EventLogStarted) { + *EventLogLastEntry = (EFI_PHYSICAL_ADDRESS)(UINTN)0; + } else { + *EventLogLastEntry = (EFI_PHYSICAL_ADDRESS)(UINTN)mTcgDxeData.EventLogAreaStruct[Index].LastEvent; + } + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog (EventLogLastEntry - %x)\n", *EventLogLastEntry)); + } + + if (EventLogTruncated != NULL) { + *EventLogTruncated = mTcgDxeData.EventLogAreaStruct[Index].EventLogTruncated; + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog (EventLogTruncated - %x)\n", *EventLogTruncated)); + } + + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog - %r\n", EFI_SUCCESS)); + + // Dump Event Log for debug purpose + if ((EventLogLocation != NULL) && (EventLogLastEntry != NULL)) { + DumpEventLog (EventLogFormat, *EventLogLocation, *EventLogLastEntry, mTcgDxeData.FinalEventsTable[Index]); + } + + // + // All events generated after the invocation of EFI_TCG2_GET_EVENT_LOG SHALL be stored + // in an instance of an EFI_CONFIGURATION_TABLE named by the VendorGuid of EFI_TCG2_FINAL_EVENTS_TABLE_GUID. + // + mTcgDxeData.GetEventLogCalled[Index] = TRUE; + + return EFI_SUCCESS; +} + +/** + Return if this is a Tcg800155PlatformIdEvent. + + @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure. + @param[in] NewEventHdrSize New event header size. + @param[in] NewEventData Pointer to the new event data. + @param[in] NewEventSize New event data size. + + @retval TRUE This is a Tcg800155PlatformIdEvent. + @retval FALSE This is NOT a Tcg800155PlatformIdEvent. + +**/ +BOOLEAN +Is800155Event ( + IN VOID *NewEventHdr, + IN UINT32 NewEventHdrSize, + IN UINT8 *NewEventData, + IN UINT32 NewEventSize + ) +{ + if ((((TCG_PCR_EVENT2_HDR *)NewEventHdr)->EventType == EV_NO_ACTION) && + (NewEventSize >= sizeof(TCG_Sp800_155_PlatformId_Event2)) && + (CompareMem (NewEventData, TCG_Sp800_155_PlatformId_Event2_SIGNATURE, + sizeof(TCG_Sp800_155_PlatformId_Event2_SIGNATURE) - 1) == 0)) { + return TRUE; + } + return FALSE; +} + +/** + Add a new entry to the Event Log. + + @param[in, out] EventLogAreaStruct The event log area data structure + @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure. + @param[in] NewEventHdrSize New event header size. + @param[in] NewEventData Pointer to the new event data. + @param[in] NewEventSize New event data size. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + +**/ +EFI_STATUS +TcgCommLogEvent ( + IN OUT TCG_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct, + IN VOID *NewEventHdr, + IN UINT32 NewEventHdrSize, + IN UINT8 *NewEventData, + IN UINT32 NewEventSize + ) +{ + UINTN NewLogSize; + BOOLEAN Record800155Event; + + if (NewEventSize > MAX_ADDRESS - NewEventHdrSize) { + return EFI_OUT_OF_RESOURCES; + } + + NewLogSize = NewEventHdrSize + NewEventSize; + + if (NewLogSize > MAX_ADDRESS - EventLogAreaStruct->EventLogSize) { + return EFI_OUT_OF_RESOURCES; + } + + if (NewLogSize + EventLogAreaStruct->EventLogSize > EventLogAreaStruct->Laml) { + DEBUG ((DEBUG_INFO, " Laml - 0x%x\n", EventLogAreaStruct->Laml)); + DEBUG ((DEBUG_INFO, " NewLogSize - 0x%x\n", NewLogSize)); + DEBUG ((DEBUG_INFO, " LogSize - 0x%x\n", EventLogAreaStruct->EventLogSize)); + DEBUG ((DEBUG_INFO, "TcgCommLogEvent - %r\n", EFI_OUT_OF_RESOURCES)); + return EFI_OUT_OF_RESOURCES; + } + + // + // Check 800-155 event + // Record to 800-155 event offset only. + // If the offset is 0, no need to record. + // + Record800155Event = Is800155Event (NewEventHdr, NewEventHdrSize, NewEventData, NewEventSize); + if (Record800155Event) { + if (EventLogAreaStruct->Next800155EventOffset != 0) { + CopyMem ( + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset + NewLogSize, + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset, + EventLogAreaStruct->EventLogSize - EventLogAreaStruct->Next800155EventOffset + ); + + CopyMem ( + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset, + NewEventHdr, + NewEventHdrSize + ); + CopyMem ( + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset + NewEventHdrSize, + NewEventData, + NewEventSize + ); + + EventLogAreaStruct->Next800155EventOffset += NewLogSize; + EventLogAreaStruct->LastEvent += NewLogSize; + EventLogAreaStruct->EventLogSize += NewLogSize; + } + return EFI_SUCCESS; + } + + EventLogAreaStruct->LastEvent = (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->EventLogSize; + EventLogAreaStruct->EventLogSize += NewLogSize; + CopyMem (EventLogAreaStruct->LastEvent, NewEventHdr, NewEventHdrSize); + CopyMem ( + EventLogAreaStruct->LastEvent + NewEventHdrSize, + NewEventData, + NewEventSize + ); + return EFI_SUCCESS; +} + +UINT32 GetMappedIndexInEventLog(UINT32 PCRIndex) +{ + PCR_TDX_EXTEND_MAP *PcrTdxMap; + + ASSERT(PCRIndex >= 0 && PCRIndex < PCR_COUNT); + PcrTdxMap = (PCR_TDX_EXTEND_MAP*)&mPcrTdxExtendMaps[PCRIndex]; + return PcrTdxMap->EventlogIndex; +} + +/** + Add a new entry to the Event Log. + + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure. + @param[in] NewEventHdrSize New event header size. + @param[in] NewEventData Pointer to the new event data. + @param[in] NewEventSize New event data size. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + +**/ +EFI_STATUS +TcgDxeLogEvent ( + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + IN VOID *NewEventHdr, + IN UINT32 NewEventHdrSize, + IN UINT8 *NewEventData, + IN UINT32 NewEventSize + ) +{ + EFI_STATUS Status; + UINTN Index; + TCG_PCR_EVENT2 *TcgPcrEvent2; + TCG_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct; + + // + // map the PCRIndex to RTMR index + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)NewEventHdr; + TcgPcrEvent2->PCRIndex = GetMappedIndexInEventLog(TcgPcrEvent2->PCRIndex); + + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if (EventLogFormat == mTcg2EventInfo[Index].LogFormat) { + break; + } + } + + if (Index == sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0])) { + return EFI_INVALID_PARAMETER; + } + + // + // Record to normal event log + // + EventLogAreaStruct = &mTcgDxeData.EventLogAreaStruct[Index]; + + if (EventLogAreaStruct->EventLogTruncated) { + return EFI_VOLUME_FULL; + } + + Status = TcgCommLogEvent ( + EventLogAreaStruct, + NewEventHdr, + NewEventHdrSize, + NewEventData, + NewEventSize + ); + + if (Status == EFI_OUT_OF_RESOURCES) { + EventLogAreaStruct->EventLogTruncated = TRUE; + return EFI_VOLUME_FULL; + } else if (Status == EFI_SUCCESS) { + EventLogAreaStruct->EventLogStarted = TRUE; + } + + // + // If GetEventLog is called, record to FinalEventsTable, too. + // + if (mTcgDxeData.GetEventLogCalled[Index]) { + if (mTcgDxeData.FinalEventsTable[Index] == NULL) { + // + // no need for FinalEventsTable + // + return EFI_SUCCESS; + } + EventLogAreaStruct = &mTcgDxeData.FinalEventLogAreaStruct[Index]; + + if (EventLogAreaStruct->EventLogTruncated) { + return EFI_VOLUME_FULL; + } + + Status = TcgCommLogEvent ( + EventLogAreaStruct, + NewEventHdr, + NewEventHdrSize, + NewEventData, + NewEventSize + ); + if (Status == EFI_OUT_OF_RESOURCES) { + EventLogAreaStruct->EventLogTruncated = TRUE; + return EFI_VOLUME_FULL; + } else if (Status == EFI_SUCCESS) { + EventLogAreaStruct->EventLogStarted = TRUE; + // + // Increase the NumberOfEvents in FinalEventsTable + // + (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents ++; + DEBUG ((EFI_D_INFO, "FinalEventsTable->NumberOfEvents - 0x%x\n", (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents)); + DEBUG ((EFI_D_INFO, " Size - 0x%x\n", (UINTN)EventLogAreaStruct->EventLogSize)); + } + } + + return Status; +} + +/** + Get TPML_DIGEST_VALUES compact binary buffer size. + + @param[in] DigestListBin TPML_DIGEST_VALUES compact binary buffer. + + @return TPML_DIGEST_VALUES compact binary buffer size. +**/ +UINT32 +GetDigestListBinSize ( + IN VOID *DigestListBin + ) +{ + UINTN Index; + UINT16 DigestSize; + UINT32 TotalSize; + UINT32 Count; + TPMI_ALG_HASH HashAlg; + + Count = ReadUnaligned32 (DigestListBin); + TotalSize = sizeof(Count); + DigestListBin = (UINT8 *)DigestListBin + sizeof(Count); + for (Index = 0; Index < Count; Index++) { + HashAlg = ReadUnaligned16 (DigestListBin); + TotalSize += sizeof(HashAlg); + DigestListBin = (UINT8 *)DigestListBin + sizeof(HashAlg); + + DigestSize = GetHashSizeFromAlgo (HashAlg); + TotalSize += DigestSize; + DigestListBin = (UINT8 *)DigestListBin + DigestSize; + } + + return TotalSize; +} + +/** + Copy TPML_DIGEST_VALUES compact binary into a buffer + + @param[in,out] Buffer Buffer to hold copied TPML_DIGEST_VALUES compact binary. + @param[in] DigestListBin TPML_DIGEST_VALUES compact binary buffer. + @param[in] HashAlgorithmMask HASH bits corresponding to the desired digests to copy. + @param[out] HashAlgorithmMaskCopied Pointer to HASH bits corresponding to the digests copied. + + @return The end of buffer to hold TPML_DIGEST_VALUES compact binary. +**/ +VOID * +CopyDigestListBinToBuffer ( + IN OUT VOID *Buffer, + IN VOID *DigestListBin, + IN UINT32 HashAlgorithmMask, + OUT UINT32 *HashAlgorithmMaskCopied + ) +{ + UINTN Index; + UINT16 DigestSize; + UINT32 Count; + TPMI_ALG_HASH HashAlg; + UINT32 DigestListCount; + UINT32 *DigestListCountPtr; + + DigestListCountPtr = (UINT32 *) Buffer; + DigestListCount = 0; + *HashAlgorithmMaskCopied = 0; + + Count = ReadUnaligned32 (DigestListBin); + Buffer = (UINT8 *)Buffer + sizeof(Count); + DigestListBin = (UINT8 *)DigestListBin + sizeof(Count); + for (Index = 0; Index < Count; Index++) { + HashAlg = ReadUnaligned16 (DigestListBin); + DigestListBin = (UINT8 *)DigestListBin + sizeof(HashAlg); + DigestSize = GetHashSizeFromAlgo (HashAlg); + + if((HashAlg & HashAlgorithmMask) != 0){ + CopyMem (Buffer, &HashAlg, sizeof(HashAlg)); + Buffer = (UINT8 *)Buffer + sizeof(HashAlg); + CopyMem (Buffer, DigestListBin, DigestSize); + Buffer = (UINT8 *)Buffer + DigestSize; + DigestListCount++; + (*HashAlgorithmMaskCopied) |= GetHashMaskFromAlgo (HashAlg); + } else { + DEBUG ((DEBUG_ERROR, "WARNING: CopyDigestListBinToBuffer Event log has HashAlg unsupported by PCR bank (0x%x)\n", HashAlg)); + } + DigestListBin = (UINT8 *)DigestListBin + DigestSize; + } + WriteUnaligned32 (DigestListCountPtr, DigestListCount); + + return Buffer; +} + +/** + Add a new entry to the Event Log. + + @param[in] DigestList A list of digest. + @param[in,out] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure. + @param[in] NewEventData Pointer to the new event data. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. +**/ +EFI_STATUS +TcgDxeLogHashEvent ( + IN TPML_DIGEST_VALUES *DigestList, + IN OUT TCG_PCR_EVENT_HDR *NewEventHdr, + IN UINT8 *NewEventData + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + UINTN Index; + EFI_STATUS RetStatus; + TCG_PCR_EVENT2 TcgPcrEvent2; + UINT8 *DigestBuffer; + UINT32 *EventSizePtr; + + DEBUG ((EFI_D_INFO, "SupportedEventLogs - 0x%08x\n", mTcgDxeData.BsCap.SupportedEventLogs)); + + RetStatus = EFI_SUCCESS; + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { + DEBUG ((EFI_D_INFO, " LogFormat - 0x%08x\n", mTcg2EventInfo[Index].LogFormat)); + switch (mTcg2EventInfo[Index].LogFormat) { + case EFI_TCG2_EVENT_LOG_FORMAT_TCG_2: + ZeroMem (&TcgPcrEvent2, sizeof(TcgPcrEvent2)); + TcgPcrEvent2.PCRIndex = NewEventHdr->PCRIndex; + TcgPcrEvent2.EventType = NewEventHdr->EventType; + DigestBuffer = (UINT8 *)&TcgPcrEvent2.Digest; + EventSizePtr = CopyDigestListToBuffer (DigestBuffer, DigestList, mTcgDxeData.BsCap.ActivePcrBanks); + CopyMem (EventSizePtr, &NewEventHdr->EventSize, sizeof(NewEventHdr->EventSize)); + + // + // Enter critical region + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + Status = TcgDxeLogEvent ( + mTcg2EventInfo[Index].LogFormat, + &TcgPcrEvent2, + sizeof(TcgPcrEvent2.PCRIndex) + sizeof(TcgPcrEvent2.EventType) + GetDigestListBinSize (DigestBuffer) + sizeof(TcgPcrEvent2.EventSize), + NewEventData, + NewEventHdr->EventSize + ); + if (Status != EFI_SUCCESS) { + RetStatus = Status; + } + + gBS->RestoreTPL (OldTpl); + // + // Exit critical region + // + break; + } + } + } + + return RetStatus; +} + +/** + Do a hash operation on a data buffer, extend a specific TPM PCR with the hash result, + and add an entry to the Event Log. + + @param[in] Flags Bitmap providing additional information. + @param[in] HashData Physical address of the start of the data buffer + to be hashed, extended, and logged. + @param[in] HashDataLen The length, in bytes, of the buffer referenced by HashData + @param[in, out] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure. + @param[in] NewEventData Pointer to the new event data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + +**/ +EFI_STATUS +TcgDxeHashLogExtendEvent ( + IN UINT64 Flags, + IN UINT8 *HashData, + IN UINT64 HashDataLen, + IN OUT TCG_PCR_EVENT_HDR *NewEventHdr, + IN UINT8 *NewEventData + ) +{ + EFI_STATUS Status; + TPML_DIGEST_VALUES DigestList; + TCG_PCR_EVENT2_HDR NoActionEvent; + + // + // For TDVF, TPMPresentFlag should always be TRUE + // + if (!mTcgDxeData.BsCap.TPMPresentFlag) { + return EFI_DEVICE_ERROR; + } + + if (NewEventHdr->EventType == EV_NO_ACTION) { + // + // Do not do TPM extend for EV_NO_ACTION + // + Status = EFI_SUCCESS; + InitNoActionEvent (&NoActionEvent, NewEventHdr->EventSize); + if ((Flags & EFI_TCG2_EXTEND_ONLY) == 0) { + Status = TcgDxeLogHashEvent (&(NoActionEvent.Digests), NewEventHdr, NewEventData); + } + + return Status; + } + + Status = HashAndExtend ( + NewEventHdr->PCRIndex, + HashData, + (UINTN)HashDataLen, + &DigestList + ); + if (!EFI_ERROR (Status)) { + if ((Flags & EFI_TCG2_EXTEND_ONLY) == 0) { + Status = TcgDxeLogHashEvent (&DigestList, NewEventHdr, NewEventData); + } + } + + if (Status == EFI_DEVICE_ERROR) { + DEBUG ((EFI_D_ERROR, "TcgDxeHashLogExtendEvent - %r. Disable TPM.\n", Status)); + mTcgDxeData.BsCap.TPMPresentFlag = FALSE; + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (PcdGet32 (PcdStatusCodeSubClassTpmDevice) | EFI_P_EC_INTERFACE_ERROR) + ); + } + + return Status; +} + +/** + The EFI_TCG2_PROTOCOL HashLogExtendEvent function call provides callers with + an opportunity to extend and optionally log events without requiring + knowledge of actual TPM commands. + The extend operation will occur even if this function cannot create an event + log entry (e.g. due to the event log being full). + + @param[in] This Indicates the calling context + @param[in] Flags Bitmap providing additional information. + @param[in] DataToHash Physical address of the start of the data buffer to be hashed. + @param[in] DataToHashLen The length in bytes of the buffer referenced by DataToHash. + @param[in] Event Pointer to data buffer containing information about the event. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_VOLUME_FULL The extend operation occurred, but the event could not be written to one or more event logs. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_UNSUPPORTED The PE/COFF image type is not supported. +**/ +EFI_STATUS +EFIAPI +Tcg2HashLogExtendEvent ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT64 Flags, + IN EFI_PHYSICAL_ADDRESS DataToHash, + IN UINT64 DataToHashLen, + IN EFI_TCG2_EVENT *Event + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT_HDR NewEventHdr; + TPML_DIGEST_VALUES DigestList; + + DEBUG ((DEBUG_VERBOSE, "Tcg2HashLogExtendEvent ...\n")); + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + // + // Do not check hash data size for EV_NO_ACTION event. + // + if ((Event->Header.EventType != EV_NO_ACTION) && (DataToHash == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (!mTcgDxeData.BsCap.TPMPresentFlag) { + return EFI_DEVICE_ERROR; + } + + if (Event->Size < Event->Header.HeaderSize + sizeof(UINT32)) { + return EFI_INVALID_PARAMETER; + } + + if (Event->Header.PCRIndex > MAX_PCR_INDEX) { + return EFI_INVALID_PARAMETER; + } + + NewEventHdr.PCRIndex = Event->Header.PCRIndex; + NewEventHdr.EventType = Event->Header.EventType; + NewEventHdr.EventSize = Event->Size - sizeof(UINT32) - Event->Header.HeaderSize; + if ((Flags & PE_COFF_IMAGE) != 0) { + Status = MeasurePeImageAndExtend ( + NewEventHdr.PCRIndex, + DataToHash, + (UINTN)DataToHashLen, + &DigestList + ); + if (!EFI_ERROR (Status)) { + if ((Flags & EFI_TCG2_EXTEND_ONLY) == 0) { + Status = TcgDxeLogHashEvent (&DigestList, &NewEventHdr, Event->Event); + } + } + if (Status == EFI_DEVICE_ERROR) { + DEBUG ((EFI_D_ERROR, "MeasurePeImageAndExtend - %r. Disable TPM.\n", Status)); + mTcgDxeData.BsCap.TPMPresentFlag = FALSE; + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (PcdGet32 (PcdStatusCodeSubClassTpmDevice) | EFI_P_EC_INTERFACE_ERROR) + ); + } + } else { + Status = TcgDxeHashLogExtendEvent ( + Flags, + (UINT8 *) (UINTN) DataToHash, + DataToHashLen, + &NewEventHdr, + Event->Event + ); + } + DEBUG ((DEBUG_VERBOSE, "Tcg2HashLogExtendEvent - %r\n", Status)); + return Status; +} + +/** + This service enables the sending of commands to the TPM. + TODO For TDVF the commands should be submitted to RTMR + + @param[in] This Indicates the calling context + @param[in] InputParameterBlockSize Size of the TPM input parameter block. + @param[in] InputParameterBlock Pointer to the TPM input parameter block. + @param[in] OutputParameterBlockSize Size of the TPM output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM output parameter block. + + @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. + @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. +**/ +EFI_STATUS +EFIAPI +Tcg2SubmitCommand ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN UINT32 OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ) +{ + return EFI_UNSUPPORTED; +/* + TDX_COMMANDS *TdCmd; + EFI_STATUS Status; + + DEBUG ((EFI_D_INFO, "Tcg2SubmitCommand ...\n")); + + TdCmd = (TDX_COMMANDS*)InputParameterBlock; + if(TdCmd->command != TDX_CMD_EXTEND_RTMR || TdCmd->command != TDX_CMD_TDREPORT){ + ASSERT(FALSE); + DEBUG((EFI_D_ERROR, "Unsupported Tdx commands - %d\n", TdCmd->command)); + return EFI_INVALID_PARAMETER; + } + + if(TdCmd->command == TDX_CMD_EXTEND_RTMR){ + Status = ExtendTdRtmr((UINT32*)TdCmd->params.extend_params.hash, SHA384_DIGEST_SIZE, TdCmd->params.extend_params.index); + }else if(TdCmd->command == TDX_CMD_TDREPORT){ + Status = DoTdReport(OutputParameterBlock, OutputParameterBlockSize, + TdCmd->params.tdreport_params.additional_data, + TdCmd->params.tdreport_params.size); + } + + return Status; +*/ +} + +/** + This service returns the currently active PCR banks. + + @param[in] This Indicates the calling context + @param[out] ActivePcrBanks Pointer to the variable receiving the bitmap of currently active PCR banks. + + @retval EFI_SUCCESS The bitmap of active PCR banks was stored in the ActivePcrBanks parameter. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +EFI_STATUS +EFIAPI +Tcg2GetActivePCRBanks ( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *ActivePcrBanks + ) +{ + if (ActivePcrBanks == NULL) { + return EFI_INVALID_PARAMETER; + } + *ActivePcrBanks = mTcgDxeData.BsCap.ActivePcrBanks; + return EFI_SUCCESS; +} + +/** + This service sets the currently active PCR banks. + TODO Can PCR banks be set externally? + + @param[in] This Indicates the calling context + @param[in] ActivePcrBanks Bitmap of the requested active PCR banks. At least one bit SHALL be set. + + @retval EFI_SUCCESS The bitmap in ActivePcrBank parameter is already active. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +EFI_STATUS +EFIAPI +Tcg2SetActivePCRBanks ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 ActivePcrBanks + ) +{ + DEBUG ((EFI_D_INFO, "Tcg2SetActivePCRBanks ... (0x%x)\n", ActivePcrBanks)); + + return EFI_SUCCESS; + +} + +/** + This service retrieves the result of a previous invocation of SetActivePcrBanks. + + @param[in] This Indicates the calling context + @param[out] OperationPresent Non-zero value to indicate a SetActivePcrBank operation was invoked during the last boot. + @param[out] Response The response from the SetActivePcrBank request. + + @retval EFI_SUCCESS The result value could be returned. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +EFI_STATUS +EFIAPI +Tcg2GetResultOfSetActivePcrBanks ( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *OperationPresent, + OUT UINT32 *Response + ) +{ +// UINT32 ReturnCode; + DEBUG((EFI_D_INFO, "Tcg2GetResultOfSetActivePcrBanks\n")); + if ((OperationPresent == NULL) || (Response == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *OperationPresent = 0; + *Response = 0; + return EFI_SUCCESS; +} + +EFI_TCG2_PROTOCOL mTcg2Protocol = { + Tcg2GetCapability, + Tcg2GetEventLog, + Tcg2HashLogExtendEvent, + Tcg2SubmitCommand, + Tcg2GetActivePCRBanks, + Tcg2SetActivePCRBanks, + Tcg2GetResultOfSetActivePcrBanks, +}; + +/** + Initialize the Event Log and log events passed from the PEI phase. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + +**/ +EFI_STATUS +SetupEventLog ( + VOID + ) +{ + EFI_STATUS Status; + VOID *TcgEvent; + EFI_PEI_HOB_POINTERS GuidHob; + EFI_PHYSICAL_ADDRESS Lasa; + UINTN Index; + VOID *DigestListBin; + UINT32 DigestListBinSize; + UINT8 *Event; + UINT32 EventSize; + TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct; + UINT8 TempBuf[sizeof(TCG_EfiSpecIDEventStruct) + + sizeof(UINT32) + + (HASH_COUNT * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + + sizeof(UINT8)]; + TCG_PCR_EVENT_HDR SpecIdEvent; + TCG_PCR_EVENT2_HDR NoActionEvent; + TCG_EfiSpecIdEventAlgorithmSize *DigestSize; + TCG_EfiSpecIdEventAlgorithmSize *TempDigestSize; + UINT8 *VendorInfoSize; + UINT32 NumberOfAlgorithms; + TDX_EVENT *TdxEvent; + TCG_PCR_EVENT2 *TcgPcrEvent2; + TPML_DIGEST_VALUES DigestList; + UINTN I = 0; + + DEBUG ((EFI_D_INFO, "SetupEventLog\n")); + + // + // 1. Create Log Area + // + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { + mTcgDxeData.EventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat; + + // allocate pages for TDVF Event log + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)), + &Lasa + ); + if (EFI_ERROR (Status)) { + return Status; + } + mTcgDxeData.EventLogAreaStruct[Index].Lasa = Lasa; + mTcgDxeData.EventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcgLogAreaMinLen); + mTcgDxeData.EventLogAreaStruct[Index].Next800155EventOffset = 0; + + if (mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 ) { + // + // Report TCG2 event log address and length, so that they can be reported in TPM2 ACPI table. + // Ignore the return status, because those fields are optional. + // + PcdSet32S(PcdTdxEventlogAcpiTableLaml, (UINT32)mTcgDxeData.EventLogAreaStruct[Index].Laml); + PcdSet64S(PcdTdxEventlogAcpiTableLasa, mTcgDxeData.EventLogAreaStruct[Index].Lasa); + } + + // + // To initialize them as 0xFF is recommended + // because the OS can know the last entry for that. + // + SetMem ((VOID *)(UINTN)Lasa, PcdGet32 (PcdTcgLogAreaMinLen), 0xFF); + // + // Create first entry for Log Header Entry Data + // + if (mTcg2EventInfo[Index].LogFormat != EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2) { + // + // TcgEfiSpecIdEventStruct + // + TcgEfiSpecIdEventStruct = (TCG_EfiSpecIDEventStruct *)TempBuf; + CopyMem (TcgEfiSpecIdEventStruct->signature, TCG_EfiSpecIDEventStruct_SIGNATURE_03, sizeof(TcgEfiSpecIdEventStruct->signature)); + TcgEfiSpecIdEventStruct->platformClass = PcdGet8 (PcdTpmPlatformClass); + TcgEfiSpecIdEventStruct->specVersionMajor = TCG_EfiSpecIDEventStruct_SPEC_VERSION_MAJOR_TPM2; + TcgEfiSpecIdEventStruct->specVersionMinor = TCG_EfiSpecIDEventStruct_SPEC_VERSION_MINOR_TPM2; + TcgEfiSpecIdEventStruct->specErrata = TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2; + TcgEfiSpecIdEventStruct->uintnSize = sizeof(UINTN)/sizeof(UINT32); + NumberOfAlgorithms = 0; + DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + + sizeof(*TcgEfiSpecIdEventStruct) + + sizeof(NumberOfAlgorithms)); + + if ((mTcgDxeData.BsCap.ActivePcrBanks & EFI_TCG2_BOOT_HASH_ALG_SHA384) != 0) { + TempDigestSize = DigestSize; + TempDigestSize += NumberOfAlgorithms; + TempDigestSize->algorithmId = TPM_ALG_SHA384; + TempDigestSize->digestSize = SHA384_DIGEST_SIZE; + NumberOfAlgorithms++; + } + + CopyMem (TcgEfiSpecIdEventStruct + 1, &NumberOfAlgorithms, sizeof(NumberOfAlgorithms)); + TempDigestSize = DigestSize; + TempDigestSize += NumberOfAlgorithms; + VendorInfoSize = (UINT8 *)TempDigestSize; + *VendorInfoSize = 0; + + SpecIdEvent.PCRIndex = 0; + SpecIdEvent.EventType = EV_NO_ACTION; + ZeroMem (&SpecIdEvent.Digest, sizeof(SpecIdEvent.Digest)); + SpecIdEvent.EventSize = (UINT32)GetTcgEfiSpecIdEventStructSize (TcgEfiSpecIdEventStruct); + + // + // Log TcgEfiSpecIdEventStruct as the first Event. Event format is TCG_PCR_EVENT. + // TCG EFI Protocol Spec. Section 5.3 Event Log Header + // TCG PC Client PFP spec. Section 9.2 Measurement Event Entries and Log + // + Status = TcgDxeLogEvent ( + mTcg2EventInfo[Index].LogFormat, + &SpecIdEvent, + sizeof(SpecIdEvent), + (UINT8 *)TcgEfiSpecIdEventStruct, + SpecIdEvent.EventSize + ); + // + // record the offset at the end of 800-155 event. + // the future 800-155 event can be inserted here. + // + mTcgDxeData.EventLogAreaStruct[Index].Next800155EventOffset = \ + mTcgDxeData.EventLogAreaStruct[Index].EventLogSize; + + // + // Tcg800155PlatformIdEvent. Event format is TCG_PCR_EVENT2 + // TDVF needs this event??? + // + GuidHob.Guid = GetFirstGuidHob (&gTcg800155PlatformIdEventHobGuid); + while (GuidHob.Guid != NULL) { + InitNoActionEvent(&NoActionEvent, GET_GUID_HOB_DATA_SIZE (GuidHob.Guid)); + + Status = TcgDxeLogEvent ( + mTcg2EventInfo[Index].LogFormat, + &NoActionEvent, + sizeof(NoActionEvent.PCRIndex) + sizeof(NoActionEvent.EventType) + GetDigestListBinSize (&NoActionEvent.Digests) + sizeof(NoActionEvent.EventSize), + GET_GUID_HOB_DATA (GuidHob.Guid), + GET_GUID_HOB_DATA_SIZE (GuidHob.Guid) + ); + + GuidHob.Guid = GET_NEXT_HOB (GuidHob); + GuidHob.Guid = GetNextGuidHob (&gTcg800155PlatformIdEventHobGuid, GuidHob.Guid); + } + } + } + } + + // + // 2. Create Final Log Area + // + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { + if (mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) { + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcg2FinalLogAreaLen)), + &Lasa + ); + if (EFI_ERROR (Status)) { + return Status; + } + SetMem ((VOID *)(UINTN)Lasa, PcdGet32 (PcdTcg2FinalLogAreaLen), 0xFF); + + // + // Initialize + // + mTcgDxeData.FinalEventsTable[Index] = (VOID *)(UINTN)Lasa; + (mTcgDxeData.FinalEventsTable[Index])->Version = EFI_TCG2_FINAL_EVENTS_TABLE_VERSION; + (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents = 0; + + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat; + mTcgDxeData.FinalEventLogAreaStruct[Index].Lasa = Lasa + sizeof(EFI_TCG2_FINAL_EVENTS_TABLE); + mTcgDxeData.FinalEventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcg2FinalLogAreaLen) - sizeof(EFI_TCG2_FINAL_EVENTS_TABLE); + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogSize = 0; + mTcgDxeData.FinalEventLogAreaStruct[Index].LastEvent = (VOID *)(UINTN)mTcgDxeData.FinalEventLogAreaStruct[Index].Lasa; + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogStarted = FALSE; + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogTruncated = FALSE; + mTcgDxeData.FinalEventLogAreaStruct[Index].Next800155EventOffset = 0; + + // + // Install to configuration table for EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 + // + Status = gBS->InstallConfigurationTable (&gEfiTcg2FinalEventsTableGuid, (VOID *)mTcgDxeData.FinalEventsTable[Index]); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + } + + // + // 3. Sync data from PEI to DXE + // + Status = EFI_SUCCESS; + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { + GuidHob.Raw = GetHobList (); + Status = EFI_SUCCESS; + while (!EFI_ERROR (Status) && (GuidHob.Raw = GetNextGuidHob (mTcg2EventInfo[Index].EventGuid, GuidHob.Raw)) != NULL) { + // + // TcgEvent points to TCG_PCR_EVENT2 + TDX_EVENT + // The Digest field in TCG_PCR_EVENT2 is not populated at this moment + // Instead its value should be calculated here by hashing the DATA designated by TDX_EVENT + // + // typedef struct tdTCG_PCR_EVENT2 { + // TCG_PCRINDEX PCRIndex; + // TCG_EVENTTYPE EventType; + // TPML_DIGEST_VALUES Digest; // In TDVF this field is TDX_DIGEST_VALUE which is same as TPML_DIGEST_VALUES + // UINT32 EventSize; + // UINT8 Event[1]; + // } TCG_PCR_EVENT2; + // + // typedef struct { + // UINT32 Signature; + // UINT8 *HashData; + // UINTN HashDataLen; + // }TDX_EVENT + +// if (I == 1) { +// break; +// } + I++; + TcgEvent = AllocateCopyPool (GET_GUID_HOB_DATA_SIZE (GuidHob.Guid), GET_GUID_HOB_DATA (GuidHob.Guid)); + DEBUG((DEBUG_INFO, "GuidHob size = 0x%x\n", GET_GUID_HOB_DATA_SIZE (GuidHob.Guid))); + ASSERT (TcgEvent != NULL); + TcgPcrEvent2 = (TCG_PCR_EVENT2*)TcgEvent; + + // This is for next iteration + GuidHob.Raw = GET_NEXT_HOB (GuidHob); + + // + // TDVF only supports EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 + if(mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) { + + // + // PCRIndex + EventType + Digest + EventSize + Event + // + DigestListBin = (UINT8 *)TcgEvent + sizeof(TCG_PCRINDEX) + sizeof(TCG_EVENTTYPE); + DigestListBinSize = GetDigestListBinSize (DigestListBin); + + // + // Event size. + // + CopyMem (&EventSize, (UINT8 *)DigestListBin + DigestListBinSize, sizeof(UINT32)); + Event = (UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32); + + // + // TdxEvent + // + TdxEvent = (TDX_EVENT*)((UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32) + EventSize); + DEBUG((DEBUG_INFO, "DigestListBinSize=0x%x, EventSize=0x%x\n", + DigestListBinSize, EventSize)); + DEBUG((DEBUG_INFO, "HashData: %p, HashDataLen=0x%x\n", + TdxEvent->HashData, TdxEvent->HashDataLen)); + + + ZeroMem((UINT8*)&DigestList, sizeof(DigestList)); + // + // Calculate the Digest by hashing(SHA384) the DATA designated by TdxEvent + // + Status = HashAndExtend(TcgPcrEvent2->PCRIndex, + TdxEvent->HashData, + TdxEvent->HashDataLen, + &DigestList); + DEBUG((DEBUG_INFO, "Digest count=%d, hashAlg=0x%x\n", DigestList.count, DigestList.digests[0].hashAlg)); + + ASSERT(Status == EFI_SUCCESS); + ASSERT(DigestList.count == 1 && DigestList.digests[0].hashAlg == TPM_ALG_SHA384); + TcgPcrEvent2->Digest.count = 1; + CopyMem((UINT8*)&TcgPcrEvent2->Digest + sizeof(UINT32), (UINT8*)&DigestList.digests[0], sizeof(TPMI_ALG_HASH) + SHA384_DIGEST_SIZE); + DumpEvent2(TcgPcrEvent2); + + // + // Log the event + // + Status = TcgDxeLogEvent ( + mTcg2EventInfo[Index].LogFormat, + TcgEvent, + sizeof(TCG_PCRINDEX) + sizeof(TCG_EVENTTYPE) + DigestListBinSize + sizeof(UINT32), + Event, + EventSize + ); + } + + FreePool (TcgEvent); + } + } + } + + return Status; +} + +/** + Measure and log an action string, and extend the measurement result into PCR[PCRIndex]. + + @param[in] PCRIndex PCRIndex to extend + @param[in] String A specific string that indicates an Action event. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +TcgMeasureAction ( + IN TPM_PCRINDEX PCRIndex, + IN CHAR8 *String + ) +{ + TCG_PCR_EVENT_HDR TcgEvent; + + TcgEvent.PCRIndex = PCRIndex; + TcgEvent.EventType = EV_EFI_ACTION; + TcgEvent.EventSize = (UINT32)AsciiStrLen (String); + return TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)String, + TcgEvent.EventSize, + &TcgEvent, + (UINT8 *) String + ); +} + +/** + Measure and log EFI handoff tables, and extend the measurement result into PCR[1]. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureHandoffTables ( + VOID + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT_HDR TcgEvent; + EFI_HANDOFF_TABLE_POINTERS HandoffTables; + UINTN ProcessorNum; + EFI_CPU_PHYSICAL_LOCATION *ProcessorLocBuf; + + ProcessorLocBuf = NULL; + Status = EFI_SUCCESS; + + if (PcdGet8 (PcdTpmPlatformClass) == TCG_PLATFORM_TYPE_SERVER) { + // + // Tcg Server spec. + // Measure each processor EFI_CPU_PHYSICAL_LOCATION with EV_TABLE_OF_DEVICES to PCR[1] + // + Status = GetProcessorsCpuLocation(&ProcessorLocBuf, &ProcessorNum); + + if (!EFI_ERROR(Status)){ + TcgEvent.PCRIndex = 1; + TcgEvent.EventType = EV_TABLE_OF_DEVICES; + TcgEvent.EventSize = sizeof (HandoffTables); + + HandoffTables.NumberOfTables = 1; + HandoffTables.TableEntry[0].VendorGuid = gEfiMpServiceProtocolGuid; + HandoffTables.TableEntry[0].VendorTable = ProcessorLocBuf; + + Status = TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)(UINTN)ProcessorLocBuf, + sizeof(EFI_CPU_PHYSICAL_LOCATION) * ProcessorNum, + &TcgEvent, + (UINT8*)&HandoffTables + ); + + FreePool(ProcessorLocBuf); + } + } + + return Status; +} + +/** + Measure and log Separator event, and extend the measurement result into a specific PCR. + + @param[in] PCRIndex PCR index. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureSeparatorEvent ( + IN TPM_PCRINDEX PCRIndex + ) +{ + TCG_PCR_EVENT_HDR TcgEvent; + UINT32 EventData; + + DEBUG ((EFI_D_INFO, "MeasureSeparatorEvent Pcr - %x\n", PCRIndex)); + + EventData = 0; + TcgEvent.PCRIndex = PCRIndex; + TcgEvent.EventType = EV_SEPARATOR; + TcgEvent.EventSize = (UINT32)sizeof (EventData); + return TcgDxeHashLogExtendEvent ( + 0, + (UINT8 *)&EventData, + sizeof (EventData), + &TcgEvent, + (UINT8 *)&EventData + ); +} + +/** + Measure and log an EFI variable, and extend the measurement result into a specific PCR. + + @param[in] PCRIndex PCR Index. + @param[in] EventType Event type. + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in] VarData The content of the variable data. + @param[in] VarSize The size of the variable data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureVariable ( + IN TPM_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + IN VOID *VarData, + IN UINTN VarSize + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT_HDR TcgEvent; + UINTN VarNameLength; + UEFI_VARIABLE_DATA *VarLog; + + DEBUG ((EFI_D_INFO, "TdTcg2Dxe: MeasureVariable (Pcr - %x, EventType - %x, ", (UINTN)PCRIndex, (UINTN)EventType)); + DEBUG ((EFI_D_INFO, "VariableName - %s, VendorGuid - %g)\n", VarName, VendorGuid)); + + VarNameLength = StrLen (VarName); + TcgEvent.PCRIndex = PCRIndex; + TcgEvent.EventType = EventType; + + TcgEvent.EventSize = (UINT32)(sizeof (*VarLog) + VarNameLength * sizeof (*VarName) + VarSize + - sizeof (VarLog->UnicodeName) - sizeof (VarLog->VariableData)); + + VarLog = (UEFI_VARIABLE_DATA *)AllocatePool (TcgEvent.EventSize); + if (VarLog == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + VarLog->VariableName = *VendorGuid; + VarLog->UnicodeNameLength = VarNameLength; + VarLog->VariableDataLength = VarSize; + CopyMem ( + VarLog->UnicodeName, + VarName, + VarNameLength * sizeof (*VarName) + ); + if (VarSize != 0 && VarData != NULL) { + CopyMem ( + (CHAR16 *)VarLog->UnicodeName + VarNameLength, + VarData, + VarSize + ); + } + + if (EventType == EV_EFI_VARIABLE_DRIVER_CONFIG) { + // + // Digest is the event data (UEFI_VARIABLE_DATA) + // + Status = TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)VarLog, + TcgEvent.EventSize, + &TcgEvent, + (UINT8*)VarLog + ); + } else { + ASSERT (VarData != NULL); + Status = TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)VarData, + VarSize, + &TcgEvent, + (UINT8*)VarLog + ); + } + FreePool (VarLog); + return Status; +} + +/** + Read then Measure and log an EFI variable, and extend the measurement result into a specific PCR. + + @param[in] PCRIndex PCR Index. + @param[in] EventType Event type. + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[out] VarSize The size of the variable data. + @param[out] VarData Pointer to the content of the variable. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +ReadAndMeasureVariable ( + IN TPM_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + OUT UINTN *VarSize, + OUT VOID **VarData + ) +{ + EFI_STATUS Status; + + Status = GetVariable2 (VarName, VendorGuid, VarData, VarSize); + if (EventType == EV_EFI_VARIABLE_DRIVER_CONFIG) { + if (EFI_ERROR (Status)) { + // + // It is valid case, so we need handle it. + // + *VarData = NULL; + *VarSize = 0; + } + } else { + // + // if status error, VarData is freed and set NULL by GetVariable2 + // + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + } + + Status = MeasureVariable ( + PCRIndex, + EventType, + VarName, + VendorGuid, + *VarData, + *VarSize + ); + return Status; +} + +/** + Read then Measure and log an EFI boot variable, and extend the measurement result into PCR[1]. +according to TCG PC Client PFP spec 0021 Section 2.4.4.2 + + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[out] VarSize The size of the variable data. + @param[out] VarData Pointer to the content of the variable. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +ReadAndMeasureBootVariable ( + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + OUT UINTN *VarSize, + OUT VOID **VarData + ) +{ + return ReadAndMeasureVariable ( + 1, + EV_EFI_VARIABLE_BOOT, + VarName, + VendorGuid, + VarSize, + VarData + ); +} + +/** + Read then Measure and log an EFI Secure variable, and extend the measurement result into PCR[7]. + + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[out] VarSize The size of the variable data. + @param[out] VarData Pointer to the content of the variable. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +ReadAndMeasureSecureVariable ( + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + OUT UINTN *VarSize, + OUT VOID **VarData + ) +{ + return ReadAndMeasureVariable ( + 7, + EV_EFI_VARIABLE_DRIVER_CONFIG, + VarName, + VendorGuid, + VarSize, + VarData + ); +} + +/** + Measure and log all EFI boot variables, and extend the measurement result into a specific PCR. + + The EFI boot variables are BootOrder and Boot#### variables. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureAllBootVariables ( + VOID + ) +{ + EFI_STATUS Status; + UINT16 *BootOrder; + UINTN BootCount; + UINTN Index; + VOID *BootVarData; + UINTN Size; + + Status = ReadAndMeasureBootVariable ( + mBootVarName, + &gEfiGlobalVariableGuid, + &BootCount, + (VOID **) &BootOrder + ); + if (Status == EFI_NOT_FOUND || BootOrder == NULL) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + // + // BootOrder can't be NULL if status is not EFI_NOT_FOUND + // + FreePool (BootOrder); + return Status; + } + + BootCount /= sizeof (*BootOrder); + for (Index = 0; Index < BootCount; Index++) { + UnicodeSPrint (mBootVarName, sizeof (mBootVarName), L"Boot%04x", BootOrder[Index]); + Status = ReadAndMeasureBootVariable ( + mBootVarName, + &gEfiGlobalVariableGuid, + &Size, + &BootVarData + ); + if (!EFI_ERROR (Status)) { + FreePool (BootVarData); + } + } + + FreePool (BootOrder); + return EFI_SUCCESS; +} + +/** + Measure and log all EFI Secure variables, and extend the measurement result into a specific PCR. + + The EFI boot variables are BootOrder and Boot#### variables. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureAllSecureVariables ( + VOID + ) +{ + EFI_STATUS Status; + VOID *Data; + UINTN DataSize; + UINTN Index; + + Status = EFI_NOT_FOUND; + for (Index = 0; Index < sizeof(mVariableType)/sizeof(mVariableType[0]); Index++) { + Status = ReadAndMeasureSecureVariable ( + mVariableType[Index].VariableName, + mVariableType[Index].VendorGuid, + &DataSize, + &Data + ); + if (!EFI_ERROR (Status)) { + if (Data != NULL) { + FreePool (Data); + } + } + } + + // + // Measure DBT if present and not empty + // + Status = GetVariable2 (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, &Data, &DataSize); + if (!EFI_ERROR(Status)) { + Status = MeasureVariable ( + 7, + EV_EFI_VARIABLE_DRIVER_CONFIG, + EFI_IMAGE_SECURITY_DATABASE2, + &gEfiImageSecurityDatabaseGuid, + Data, + DataSize + ); + FreePool(Data); + } else { + DEBUG((DEBUG_INFO, "Skip measuring variable %s since it's deleted\n", EFI_IMAGE_SECURITY_DATABASE2)); + } + + return EFI_SUCCESS; +} + +/** + Measure and log launch of FirmwareDebugger, and extend the measurement result into a specific PCR. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureLaunchOfFirmwareDebugger ( + VOID + ) +{ + TCG_PCR_EVENT_HDR TcgEvent; + + TcgEvent.PCRIndex = 7; + TcgEvent.EventType = EV_EFI_ACTION; + TcgEvent.EventSize = sizeof(FIRMWARE_DEBUGGER_EVENT_STRING) - 1; + return TcgDxeHashLogExtendEvent ( + 0, + (UINT8 *)FIRMWARE_DEBUGGER_EVENT_STRING, + sizeof(FIRMWARE_DEBUGGER_EVENT_STRING) - 1, + &TcgEvent, + (UINT8 *)FIRMWARE_DEBUGGER_EVENT_STRING + ); +} + +/** + Measure and log all Secure Boot Policy, and extend the measurement result into a specific PCR. + + Platform firmware adhering to the policy must therefore measure the following values into PCR[7]: (in order listed) + - The contents of the SecureBoot variable + - The contents of the PK variable + - The contents of the KEK variable + - The contents of the EFI_IMAGE_SECURITY_DATABASE variable + - The contents of the EFI_IMAGE_SECURITY_DATABASE1 variable + - Separator + - Entries in the EFI_IMAGE_SECURITY_DATABASE that are used to validate EFI Drivers or EFI Boot Applications in the boot path + + NOTE: Because of the above, UEFI variables PK, KEK, EFI_IMAGE_SECURITY_DATABASE, + EFI_IMAGE_SECURITY_DATABASE1 and SecureBoot SHALL NOT be measured into PCR[3]. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context +**/ +VOID +EFIAPI +MeasureSecureBootPolicy ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *Protocol; + + Status = gBS->LocateProtocol (&gEfiVariableWriteArchProtocolGuid, NULL, (VOID **)&Protocol); + if (EFI_ERROR (Status)) { + return; + } + + if (PcdGetBool (PcdFirmwareDebuggerInitialized)) { + Status = MeasureLaunchOfFirmwareDebugger (); + DEBUG ((EFI_D_INFO, "MeasureLaunchOfFirmwareDebugger - %r\n", Status)); + } + + Status = MeasureAllSecureVariables (); + DEBUG ((EFI_D_INFO, "MeasureAllSecureVariables - %r\n", Status)); + + // + // We need measure Separator(7) here, because this event must be between SecureBootPolicy (Configure) + // and ImageVerification (Authority) + // There might be a case that we need measure UEFI image from DriverOrder, besides BootOrder. So + // the Authority measurement happen before ReadToBoot event. + // + Status = MeasureSeparatorEvent (7); + DEBUG ((EFI_D_INFO, "MeasureSeparatorEvent - %r\n", Status)); + return ; +} + +/** + Ready to Boot Event notification handler. + + Sequence of OS boot events is measured in this event notification handler. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TPM_PCRINDEX PcrIndex; + + PERF_START_EX (mImageHandle, "EventRec", "Tcg2Dxe", 0, PERF_ID_TD_TCG2_DXE); + if (mBootAttempts == 0) { + + // + // Measure handoff tables. + // + Status = MeasureHandoffTables (); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HOBs not Measured. Error!\n")); + } + + // + // Measure BootOrder & Boot#### variables. + // + Status = MeasureAllBootVariables (); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Boot Variables not Measured. Error!\n")); + } + + // + // 1. This is the first boot attempt. + // + Status = TcgMeasureAction ( + 4, + EFI_CALLING_EFI_APPLICATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_CALLING_EFI_APPLICATION)); + } + + // + // 2. Draw a line between pre-boot env and entering post-boot env. + // PCR[7] is already done. + // + for (PcrIndex = 0; PcrIndex < 7; PcrIndex++) { + Status = MeasureSeparatorEvent (PcrIndex); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Separator Event not Measured. Error!\n")); + } + } + + // + // 3. Measure GPT. It would be done in SAP driver. + // + + // + // 4. Measure PE/COFF OS loader. It would be done in SAP driver. + // + + // + // 5. Read & Measure variable. BootOrder already measured. + // + } else { + // + // 6. Not first attempt, meaning a return from last attempt + // + Status = TcgMeasureAction ( + 4, + EFI_RETURNING_FROM_EFI_APPLICATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_RETURNING_FROM_EFI_APPLICATION)); + } + + // + // 7. Next boot attempt, measure "Calling EFI Application from Boot Option" again + // TCG PC Client PFP spec Section 2.4.4.5 Step 4 + // + Status = TcgMeasureAction ( + 4, + EFI_CALLING_EFI_APPLICATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_CALLING_EFI_APPLICATION)); + } + } + + DEBUG ((EFI_D_INFO, "TdTcg2Dxe Measure Data when ReadyToBoot\n")); + // + // Increase boot attempt counter. + // + mBootAttempts++; + PERF_END_EX (mImageHandle, "EventRec", "Tcg2Dxe", 0, PERF_ID_TD_TCG2_DXE + 1); +} + +/** + Exit Boot Services Event notification handler. + + Measure invocation and success of ExitBootServices. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Measure invocation of ExitBootServices, + // + Status = TcgMeasureAction ( + 5, + EFI_EXIT_BOOT_SERVICES_INVOCATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_INVOCATION)); + } + + // + // Measure success of ExitBootServices + // + Status = TcgMeasureAction ( + 5, + EFI_EXIT_BOOT_SERVICES_SUCCEEDED + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_SUCCEEDED)); + } +} + +/** + Exit Boot Services Failed Event notification handler. + + Measure Failure of ExitBootServices. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnExitBootServicesFailed ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Measure Failure of ExitBootServices, + // + Status = TcgMeasureAction ( + 5, + EFI_EXIT_BOOT_SERVICES_FAILED + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_FAILED)); + } + +} + +/** + Install TDVF ACPI Table when ACPI Table Protocol is available. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context +**/ +VOID +EFIAPI +InstallAcpiTable ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + UINTN TableKey; + EFI_STATUS Status; + EFI_ACPI_TABLE_PROTOCOL *AcpiTable; + UINT64 OemTableId; + + Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **)&AcpiTable); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "TCG2: AcpiTableProtocol is not installed. %r\n", Status)); + return; + } + + mTdxEventlogAcpiTemplate.Laml = (UINT64)PcdGet32(PcdTdxEventlogAcpiTableLaml); + mTdxEventlogAcpiTemplate.Lasa = PcdGet64(PcdTdxEventlogAcpiTableLasa); + CopyMem (mTdxEventlogAcpiTemplate.Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (mTdxEventlogAcpiTemplate.Header.OemId)); + OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId); + CopyMem (&mTdxEventlogAcpiTemplate.Header.OemTableId, &OemTableId, sizeof (UINT64)); + mTdxEventlogAcpiTemplate.Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + mTdxEventlogAcpiTemplate.Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + mTdxEventlogAcpiTemplate.Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + + // + // Construct ACPI Table + Status = AcpiTable->InstallAcpiTable( + AcpiTable, + &mTdxEventlogAcpiTemplate, + mTdxEventlogAcpiTemplate.Header.Length, + &TableKey + ); + ASSERT_EFI_ERROR (Status); + + DEBUG((DEBUG_INFO, "TDVF Eventlog ACPI Table is installed.\n")); + + Status = TpmMeasureAndLogData( + 1, + EV_POST_CODE, + EV_POSTCODE_INFO_ACPI_DATA, + ACPI_DATA_LEN, + &mTdxEventlogAcpiTemplate, + mTdxEventlogAcpiTemplate.Header.Length + ); + ASSERT_EFI_ERROR (Status); + + DEBUG((DEBUG_INFO, "TDX Eventlog ACPI Table is measured and logged\n")); + +} + +/** + The function install Tcg2 protocol. + + @retval EFI_SUCCESS Tcg2 protocol is installed. + @retval other Some error occurs. +**/ +EFI_STATUS +InstallTdTcg2 ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gTdTcg2ProtocolGuid, + &mTcg2Protocol, + NULL + ); + return Status; +} + +/** + The driver's entry point. It publishes EFI Tcg2 Protocol. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. +**/ +EFI_STATUS +EFIAPI +DriverEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + VOID *Registration; + + if (!ProbeTdGuest()) { + DEBUG ((DEBUG_INFO, "TdTcg2Dxe is not loaded in Non-Td guest.\n")); + return EFI_UNSUPPORTED; + } + + mImageHandle = ImageHandle; + + // + // Fill information + // + ASSERT (TCG_EVENT_LOG_AREA_COUNT_MAX == sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0])); + + mTcgDxeData.BsCap.Size = sizeof(EFI_TCG2_BOOT_SERVICE_CAPABILITY); + mTcgDxeData.BsCap.ProtocolVersion.Major = 1; + mTcgDxeData.BsCap.ProtocolVersion.Minor = 1; + mTcgDxeData.BsCap.StructureVersion.Major = 1; + mTcgDxeData.BsCap.StructureVersion.Minor = 1; + + // + // Get supported PCR and current Active PCRs + // For TD gueset HA384 is supported. + // + mTcgDxeData.BsCap.HashAlgorithmBitmap = HASH_ALG_SHA384; + mTcgDxeData.BsCap.ActivePcrBanks = HASH_ALG_SHA384; + mTcgDxeData.BsCap.NumberOfPCRBanks = 1; + + // TD guest only supports EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 + mTcgDxeData.BsCap.SupportedEventLogs = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; + + DEBUG ((EFI_D_INFO, "TdTcg2.SupportedEventLogs - 0x%08x\n", mTcgDxeData.BsCap.SupportedEventLogs)); + DEBUG ((EFI_D_INFO, "TdTcg2.HashAlgorithmBitmap - 0x%08x\n", mTcgDxeData.BsCap.HashAlgorithmBitmap)); + DEBUG ((EFI_D_INFO, "TdTcg2.NumberOfPCRBanks - 0x%08x\n", mTcgDxeData.BsCap.NumberOfPCRBanks)); + DEBUG ((EFI_D_INFO, "TdTcg2.ActivePcrBanks - 0x%08x\n", mTcgDxeData.BsCap.ActivePcrBanks)); + + // + // Setup the log area and copy event log from hob list to it + // + Status = SetupEventLog (); + ASSERT_EFI_ERROR (Status); + + // + // Measure handoff tables, Boot#### variables etc. + // + Status = EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + OnReadyToBoot, + NULL, + &Event + ); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnExitBootServices, + NULL, + &gEfiEventExitBootServicesGuid, + &Event + ); + + // + // Measure Exit Boot Service failed + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnExitBootServicesFailed, + NULL, + &gEventExitBootServicesFailedGuid, + &Event + ); + + // + // Create event callback, because we need access variable on SecureBootPolicyVariable + // We should use VariableWriteArch instead of VariableArch, because Variable driver + // may update SecureBoot value based on last setting. + // + EfiCreateProtocolNotifyEvent (&gEfiVariableWriteArchProtocolGuid, TPL_CALLBACK, MeasureSecureBootPolicy, NULL, &Registration); + + // + // Install TdTcg2Protocol + // + Status = InstallTdTcg2 (); + DEBUG ((EFI_D_INFO, "InstallTdTcg2 - %r\n", Status)); + + if (Status == EFI_SUCCESS) { + // + // Create event callback to install TDX ACPI Table + EfiCreateProtocolNotifyEvent (&gEfiAcpiTableProtocolGuid, TPL_CALLBACK, InstallAcpiTable, NULL, &Registration); + } + + return Status; +} diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf new file mode 100644 index 000000000000..e27ea7955f51 --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf @@ -0,0 +1,111 @@ +## @file +# +# Produces EFI_TD protocol and measure boot environment +# +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdTcg2Dxe + MODULE_UNI_FILE = TdTcg2Dxe.uni + FILE_GUID = F062221E-C607-44C2-B0B4-C3886331D351 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DriverEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + TdTcg2Dxe.c + MeasureBootPeCoff.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + CryptoPkg/CryptoPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiBootServicesTableLib + HobLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + PrintLib + UefiLib + HashLib + PerformanceLib + ReportStatusCodeLib + #Tcg2PhysicalPresenceLib ? + PeCoffLib + TpmMeasurementLib + TdxProbeLib + TdxLib + +[Guids] + ## SOMETIMES_CONSUMES ## Variable:L"SecureBoot" + ## SOMETIMES_CONSUMES ## Variable:L"PK" + ## SOMETIMES_CONSUMES ## Variable:L"KEK" + ## SOMETIMES_CONSUMES ## Variable:L"BootXXXX" + gEfiGlobalVariableGuid + + ## SOMETIMES_CONSUMES ## Variable:L"db" + ## SOMETIMES_CONSUMES ## Variable:L"dbx" + gEfiImageSecurityDatabaseGuid + + gTcgEventEntryHobGuid ## SOMETIMES_CONSUMES ## HOB + gEfiEventExitBootServicesGuid ## CONSUMES ## Event + gEventExitBootServicesFailedGuid ## SOMETIMES_CONSUMES ## Event + + gTcgEvent2EntryHobGuid ## SOMETIMES_CONSUMES ## HOB + gTcg800155PlatformIdEventHobGuid ## SOMETIMES_CONSUMES ## HOB + #gTdTcg2ProtocolGuid + +[Protocols] + gTdTcg2ProtocolGuid ## PRODUCES + gEfiTcg2FinalEventsTableGuid ## PRODUCES + gEfiMpServiceProtocolGuid ## SOMETIMES_CONSUMES + gEfiVariableWriteArchProtocolGuid ## NOTIFY + gEfiResetNotificationProtocolGuid ## CONSUMES + gEfiAcpiTableProtocolGuid ## NOTIFY + +[Pcd] + gEfiSecurityPkgTokenSpaceGuid.PcdTpmPlatformClass ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdFirmwareDebuggerInitialized ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeSubClassTpmDevice ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcg2HashAlgorithmBitmap ## CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcg2NumberOfPCRBanks ## CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcgLogAreaMinLen ## CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcg2FinalLogAreaLen ## CONSUMES + #gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableRev ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLaml ## PRODUCES + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLasa ## PRODUCES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision ## CONSUMES + #gUefiTdvfPkgTokenSpaceGuid.PcdTdvfAcpiTableRtmr ## CONSUMES + #gUefiTdvfPkgTokenSpaceGuid.PcdTdvfAcpiTableRtmrLen ## CONSUMES + +[Depex] + # According to PcdTpm2AcpiTableRev definition in SecurityPkg.dec + # This PCD should be configured at DynamicHii or DynamicHiiEx. + # So, this PCD read operation depends on GetVariable service. + # Add VariableArch protocol dependency to make sure PCD read works. + gEfiVariableArchProtocolGuid AND gEfiAcpiTableProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + TdTcg2DxeExtra.uni diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni new file mode 100644 index 000000000000..b7addf94c624 --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni @@ -0,0 +1,21 @@ +// /** @file +// Produces TCG2 protocol and measure boot environment +// +// This module will produce TCG2 protocol and measure boot environment. +// +// Caution: This module requires additional review when modified. +// This driver will have external input - PE/COFF image. +// This external input must be validated carefully to avoid security issue like +// buffer overflow, integer overflow. +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Produces TCG2 protocol and measure boot environment" + +#string STR_MODULE_DESCRIPTION #language en-US "This module will produce TCG2 protocol and measure boot environment." + diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni new file mode 100644 index 000000000000..2dfe868fccf9 --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni @@ -0,0 +1,12 @@ +// /** @file +// Tcg2Dxe Localized Strings and Content +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"TCG2 (Trusted Computing Group) DXE" From 3ac83217a0f357d367708dabe3288101464e382d Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 15:42:49 +0800 Subject: [PATCH 39/54] SecurityPkg: Update DxeTpmMeasurementLib to support Tdx guest --- .../DxeTpmMeasurementLib.c | 81 ++++++++++++++++++- .../DxeTpmMeasurementLib.inf | 3 + 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c index 061136ee7860..2d1540066d61 100644 --- a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c +++ b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c @@ -19,8 +19,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include - - +#include +#include +#include /** Tpm12 measure and log data, and extend the measurement result into a specific PCR. @@ -149,6 +150,68 @@ Tpm20MeasureAndLogData ( return Status; } +/** + Tdx measure and log data, and extend the measurement result into a + specific TDX RTMR. + + @param[in] Index RTMR Index. + @param[in] EventType Event type. + @param[in] EventLog Measurement event log. + @param[in] LogLen Event log length in bytes. + @param[in] HashData The start of the data buffer to be hashed, extended. + @param[in] HashDataLen The length, in bytes, of the buffer referenced by HashData + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_UNSUPPORTED Tdx device not available. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. +**/ +EFI_STATUS +EFIAPI +TdxMeasureAndLogData ( + IN UINT32 Index, + IN UINT32 EventType, + IN VOID *EventLog, + IN UINT32 LogLen, + IN VOID *HashData, + IN UINT64 HashDataLen + ) +{ + EFI_STATUS Status; + EFI_TCG2_PROTOCOL *Tcg2Protocol; + EFI_TCG2_EVENT *Tcg2Event; + + DEBUG ((DEBUG_INFO, "TdxMeasureAndLogData\n")); + Status = gBS->LocateProtocol (&gTdTcg2ProtocolGuid, NULL, (VOID **) &Tcg2Protocol); + if (EFI_ERROR (Status)) { + return Status; + } + + Tcg2Event = (EFI_TCG2_EVENT *) AllocateZeroPool (LogLen + sizeof (EFI_TCG2_EVENT)); + if(Tcg2Event == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcg2Event->Size = (UINT32) LogLen + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event); + Tcg2Event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); + Tcg2Event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + Tcg2Event->Header.PCRIndex = Index; + Tcg2Event->Header.EventType = EventType; + CopyMem (&Tcg2Event->Event[0], EventLog, LogLen); + + Status = Tcg2Protocol->HashLogExtendEvent ( + Tcg2Protocol, + 0, + (EFI_PHYSICAL_ADDRESS) (UINTN) HashData, + HashDataLen, + Tcg2Event + ); + FreePool (Tcg2Event); + + return Status; +} + + /** Tpm measure and log data, and extend the measurement result into a specific PCR. @@ -177,6 +240,20 @@ TpmMeasureAndLogData ( { EFI_STATUS Status; + // + // Try to measure using TDX protocol + // + if (ProbeTdGuest()) { + return TdxMeasureAndLogData ( + PcrIndex, + EventType, + EventLog, + LogLen, + HashData, + HashDataLen + ); + } + // // Try to measure using Tpm20 protocol // diff --git a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf index 7d41bc41f95d..84d2674877d4 100644 --- a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf +++ b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf @@ -38,7 +38,10 @@ DebugLib MemoryAllocationLib UefiBootServicesTableLib + TdxProbeLib + TdxLib [Protocols] gEfiTcgProtocolGuid ## SOMETIMES_CONSUMES gEfiTcg2ProtocolGuid ## SOMETIMES_CONSUMES + gTdTcg2ProtocolGuid From cdcf0bba30b6feff65f1cbfd11f0a044d0307ad9 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 15:46:52 +0800 Subject: [PATCH 40/54] SecurityPkg: Update DxeTpm2MeasureBootLib to support Tdx --- .../DxeTpm2MeasureBootLib.c | 70 +++++++++++++------ .../DxeTpm2MeasureBootLib.inf | 2 + 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c index 92eac715800f..a59a65a799c8 100644 --- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c @@ -41,6 +41,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include // // Flag to check GPT partition. It only need be measured once. @@ -374,6 +375,51 @@ Tcg2MeasurePeImage ( return Status; } +EFI_STATUS +EFIAPI +GetTcg2Protocol ( + EFI_TCG2_PROTOCOL **Tcg2Protocol + ) +{ + EFI_STATUS Status; + EFI_TCG2_BOOT_SERVICE_CAPABILITY ProtocolCapability; + + if (ProbeTdGuest()) { + Status = gBS->LocateProtocol (&gTdTcg2ProtocolGuid, NULL, (VOID **) Tcg2Protocol); + if (EFI_ERROR (Status)) { + // + // TdTcg2 protocol is not installed. + // + DEBUG ((EFI_D_VERBOSE, "DxeTpm2MeasureBootHandler - TdTcg2 - %r\n", Status)); + return EFI_UNSUPPORTED; + } + } else { + Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **) Tcg2Protocol); + if (EFI_ERROR (Status)) { + // + // Tcg2 protocol is not installed. So, TPM2 is not present. + // + DEBUG ((EFI_D_VERBOSE, "DxeTpm2MeasureBootHandler - Tcg2 - %r\n", Status)); + return EFI_UNSUPPORTED; + } + + ProtocolCapability.Size = (UINT8) sizeof (ProtocolCapability); + Status = (*Tcg2Protocol)->GetCapability ( + *Tcg2Protocol, + &ProtocolCapability + ); + if (EFI_ERROR (Status) || (!ProtocolCapability.TPMPresentFlag)) { + // + // TPM device doesn't work or activate. + // + DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler (%r) - TPMPresentFlag - %x\n", Status, ProtocolCapability.TPMPresentFlag)); + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + /** The security handler is used to abstract platform-specific policy from the DXE core response to an attempt to use a file that returns a @@ -424,7 +470,7 @@ DxeTpm2MeasureBootHandler ( { EFI_TCG2_PROTOCOL *Tcg2Protocol; EFI_STATUS Status; - EFI_TCG2_BOOT_SERVICE_CAPABILITY ProtocolCapability; + //EFI_TCG2_BOOT_SERVICE_CAPABILITY ProtocolCapability; EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; EFI_DEVICE_PATH_PROTOCOL *OrigDevicePathNode; EFI_HANDLE Handle; @@ -435,26 +481,10 @@ DxeTpm2MeasureBootHandler ( EFI_PHYSICAL_ADDRESS FvAddress; UINT32 Index; - Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **) &Tcg2Protocol); + Tcg2Protocol = NULL; + Status = GetTcg2Protocol(&Tcg2Protocol); + DEBUG ((DEBUG_INFO, "Tcg2Protocol=%p, Status=%r\n", Tcg2Protocol, Status)); if (EFI_ERROR (Status)) { - // - // Tcg2 protocol is not installed. So, TPM2 is not present. - // Don't do any measurement, and directly return EFI_SUCCESS. - // - DEBUG ((EFI_D_VERBOSE, "DxeTpm2MeasureBootHandler - Tcg2 - %r\n", Status)); - return EFI_SUCCESS; - } - - ProtocolCapability.Size = (UINT8) sizeof (ProtocolCapability); - Status = Tcg2Protocol->GetCapability ( - Tcg2Protocol, - &ProtocolCapability - ); - if (EFI_ERROR (Status) || (!ProtocolCapability.TPMPresentFlag)) { - // - // TPM device doesn't work or activate. - // - DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler (%r) - TPMPresentFlag - %x\n", Status, ProtocolCapability.TPMPresentFlag)); return EFI_SUCCESS; } diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf index 2506abbe7c8b..68b4d003fa2b 100644 --- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf @@ -55,12 +55,14 @@ BaseLib SecurityManagementLib HobLib + TdxProbeLib [Guids] gMeasuredFvHobGuid ## SOMETIMES_CONSUMES ## HOB [Protocols] gEfiTcg2ProtocolGuid ## SOMETIMES_CONSUMES + gTdTcg2ProtocolGuid gEfiFirmwareVolumeBlockProtocolGuid ## SOMETIMES_CONSUMES gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES gEfiDiskIoProtocolGuid ## SOMETIMES_CONSUMES From b01f23cdf3088c63b40d3dd8bcc73c22ea0df691 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 15:49:21 +0800 Subject: [PATCH 41/54] OvmfPkg: Update EmuVariableFvbRuntimeDxe to support Tdx In Td guest, Configuration FV need to be copied to FVB. TODO: Add more justification here. --- OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c | 167 ++++++++++++++++++++++- OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf | 3 + 2 files changed, 169 insertions(+), 1 deletion(-) diff --git a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c index 766ad1e59f47..0f9b78e1fdd1 100644 --- a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c +++ b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "Fvb.h" #define EFI_AUTHENTICATED_VARIABLE_GUID \ @@ -596,6 +597,154 @@ ValidateFvHeader ( } +/** + Check padding data all bit should be 1. + + @param[in] Buffer - A pointer to buffer header + @param[in] BufferSize - Buffer size + + @retval TRUE - The padding data is valid. + @retval TRUE - The padding data is invalid. + +**/ +BOOLEAN +CheckPaddingData ( + IN UINT8 *Buffer, + IN UINT32 BufferSize + ) + { + UINT32 index; + + for (index = 0; index < BufferSize; index++) { + if (Buffer[index] != 0xFF) { + return FALSE; + } + } + + return TRUE; + } + + +/** + Check the integrity of CFV data. + + @param[in] TdxCfvBase - A pointer to CFV header + @param[in] TdxCfvSize - CFV data size + + @retval TRUE - The CFV data is valid. + @retval FALSE - The CFV data is invalid. + +**/ +BOOLEAN +ValidateTdxCfv ( + IN UINT8 *TdxCfvBase, + IN UINT32 TdxCfvSize + ) +{ + UINT16 Checksum; + UINTN VariableBase; + UINT32 VariableOffset; + UINT32 VariableOffsetBeforeAlign; + EFI_FIRMWARE_VOLUME_HEADER *CfvFvHeader; + VARIABLE_STORE_HEADER *CfvVariableStoreHeader; + AUTHENTICATED_VARIABLE_HEADER *VariableHeader; + + static EFI_GUID FvHdrGUID = EFI_SYSTEM_NV_DATA_FV_GUID; + static EFI_GUID VarStoreHdrGUID = EFI_AUTHENTICATED_VARIABLE_GUID; + + VariableOffset = 0; + + if (TdxCfvBase == NULL) { + DEBUG ((DEBUG_ERROR, "TDX CFV: CFV pointer is NULL\n")); + return FALSE; + } + + // + // Verify the header zerovetor, filesystemguid, + // revision, signature, attributes, fvlength, checksum + // HeaderLength cannot be an odd number + // + CfvFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) TdxCfvBase; + + if ((!IsZeroBuffer (CfvFvHeader->ZeroVector, 16)) || + (!CompareGuid (&FvHdrGUID, &CfvFvHeader->FileSystemGuid)) || + (CfvFvHeader->Signature != EFI_FVH_SIGNATURE) || + (CfvFvHeader->Attributes != 0x4feff) || + (CfvFvHeader->HeaderLength != EMU_FV_HEADER_LENGTH) || + (CfvFvHeader->Revision != EFI_FVH_REVISION) + ) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Basic FV headers were invalid\n")); + return FALSE; + } + + // + // Verify the header checksum + // + Checksum = CalculateSum16 ((VOID*) CfvFvHeader, CfvFvHeader->HeaderLength); + + if (Checksum != 0) { + DEBUG ((DEBUG_ERROR, "TDX CFV: FV checksum was invalid\n")); + return FALSE; + } + + // + // Verify the header signature, size, format, state + // + CfvVariableStoreHeader = (VARIABLE_STORE_HEADER *) (TdxCfvBase + CfvFvHeader->HeaderLength); + if ((!CompareGuid (&VarStoreHdrGUID, &CfvVariableStoreHeader->Signature)) || + (CfvVariableStoreHeader->Format != VARIABLE_STORE_FORMATTED) || + (CfvVariableStoreHeader->State != VARIABLE_STORE_HEALTHY) + ) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable Store header was invalid\n")); + return FALSE; + } + + // + // Verify the header startId, state + // Verify data to the end + // + VariableBase = (UINTN)TdxCfvBase + CfvFvHeader->HeaderLength + sizeof (VARIABLE_STORE_HEADER); + while (VariableOffset < (CfvVariableStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER))) { + VariableHeader = (AUTHENTICATED_VARIABLE_HEADER *) (VariableBase + VariableOffset); + if (VariableHeader->StartId != VARIABLE_DATA) { + if (!CheckPaddingData ((UINT8 *)VariableHeader, CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER) - VariableOffset)) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + VariableOffset = CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER); + } + else { + if (!((VariableHeader->State == VAR_IN_DELETED_TRANSITION) || + (VariableHeader->State == VAR_DELETED) || + (VariableHeader->State == VAR_HEADER_VALID_ONLY) || + (VariableHeader->State == VAR_ADDED))) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + + VariableOffset += sizeof (AUTHENTICATED_VARIABLE_HEADER) + VariableHeader->NameSize + VariableHeader->DataSize; + // Verify VariableOffset should be less than or equal CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER) + if (VariableOffset > (CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER))) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + + VariableOffsetBeforeAlign = VariableOffset; + // 4 byte align + VariableOffset = (VariableOffset + 3) & (UINTN)(~3); + + if (!CheckPaddingData ((UINT8 *)(VariableBase + VariableOffsetBeforeAlign), VariableOffset - VariableOffsetBeforeAlign)) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + } + } + + return TRUE; + +} + + /** Initializes the FV Header and Variable Store Header to support variable operations. @@ -718,6 +867,8 @@ FvbInitialize ( EFI_HANDLE Handle; EFI_PHYSICAL_ADDRESS Address; RETURN_STATUS PcdStatus; + UINT8 *CfvBase; + UINT32 CfvSize; DEBUG ((DEBUG_INFO, "EMU Variable FVB Started\n")); @@ -770,7 +921,21 @@ FvbInitialize ( mEmuVarsFvb.BufferPtr = Ptr; // - // Initialize the main FV header and variable store header + // Copy the content from Configuration FV in Td guest + // + if (ProbeTdGuest()) { + CfvBase = (UINT8*)(UINTN)PcdGet32(PcdCfvBase); + CfvSize = (UINT32)PcdGet32(PcdCfvRawDataSize); + DEBUG ((DEBUG_INFO, "Copy the content from Configuration FV. 0x%p : 0x%x\n", CfvBase, CfvSize)); + + if (ValidateTdxCfv(CfvBase, CfvSize)) { + CopyMem(Ptr, CfvBase, CfvSize); + Initialize = FALSE; + } + } + + // + // Initialize the main FV header and variable store header. // if (Initialize) { SetMem (Ptr, EMU_FVB_SIZE, ERASED_UINT8); diff --git a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf index 225ea27f5ffd..f6c4dcf4c5f4 100644 --- a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf +++ b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf @@ -44,6 +44,7 @@ UefiDriverEntryPoint UefiLib UefiRuntimeLib + TdxProbeLib [Guids] gEfiEventVirtualAddressChangeGuid # ALWAYS_CONSUMED Create Event: EVENT_GROUP_GUID @@ -56,6 +57,8 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 From 41b1e24f3549b5860214eef0878ebf3d44404f2e Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 31 Mar 2021 16:01:38 +0800 Subject: [PATCH 42/54] OvmfPkg: Enable secure boot and measure boot in OvmfPkgX64.dsc --- OvmfPkg/OvmfPkgX64.dsc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index b9966d78a1b7..5c187b657664 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -29,18 +29,18 @@ # Defines for default states. These can be changed on the command line. # -D FLAG=VALUE # - DEFINE SECURE_BOOT_ENABLE = FALSE + DEFINE SECURE_BOOT_ENABLE = TRUE DEFINE SMM_REQUIRE = FALSE DEFINE SOURCE_DEBUG_ENABLE = FALSE - DEFINE TPM_ENABLE = FALSE - DEFINE TPM_CONFIG_ENABLE = FALSE + DEFINE TPM_ENABLE = TRUE + DEFINE TPM_CONFIG_ENABLE = TRUE # # TDX flags # DEFINE TDX_IGNORE_VE_HLT = FALSE DEFINE TDX_EMULATION_ENABLE = FALSE - + DEFINE TDX_SUPPORT = TRUE # Network definition # DEFINE NETWORK_TLS_ENABLE = FALSE @@ -247,9 +247,15 @@ Tpm2CommandLib|SecurityPkg/Library/Tpm2CommandLib/Tpm2CommandLib.inf Tcg2PhysicalPresenceLib|OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.inf Tcg2PpVendorLib|SecurityPkg/Library/Tcg2PpVendorLibNull/Tcg2PpVendorLibNull.inf - TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf + #TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf !else Tcg2PhysicalPresenceLib|OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.inf + #TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf +!endif + +!if $(TPM_ENABLE) == TRUE or $(TDX_SUPPORT) == TRUE + TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf +!else TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf !endif @@ -707,6 +713,7 @@ gEfiNetworkPkgTokenSpaceGuid.PcdIPv4PXESupport|0x01 gEfiNetworkPkgTokenSpaceGuid.PcdIPv6PXESupport|0x01 + [PcdsDynamicHii] !if $(TPM_ENABLE) == TRUE && $(TPM_CONFIG_ENABLE) == TRUE gEfiSecurityPkgTokenSpaceGuid.PcdTcgPhysicalPresenceInterfaceVer|L"TCG2_VERSION"|gTcg2ConfigFormSetGuid|0x0|"1.3"|NV,BS @@ -800,6 +807,9 @@ !endif !if $(TPM_ENABLE) == TRUE NULL|SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf + #NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf +!endif +!if $(TPM_ENABLE) == TRUE OR $(TDX_SUPPORT) == TRUE NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf !endif } From b460a0725c758253fbe15e04094e3d055fb941d8 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Fri, 2 Apr 2021 16:10:15 +0800 Subject: [PATCH 43/54] MdePkg: Delete TdxLibNull --- MdePkg/Library/TdxLib/TdxLibNull.c | 158 ------------------ MdePkg/Library/TdxLib/TdxLibNull.inf | 31 ---- OvmfPkg/OvmfPkgIa32.dsc | 1 - OvmfPkg/OvmfPkgIa32X64.dsc | 1 - OvmfPkg/Sec/SecMain.inf | 1 - UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf | 1 - UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf | 1 - 7 files changed, 194 deletions(-) delete mode 100644 MdePkg/Library/TdxLib/TdxLibNull.c delete mode 100644 MdePkg/Library/TdxLib/TdxLibNull.inf diff --git a/MdePkg/Library/TdxLib/TdxLibNull.c b/MdePkg/Library/TdxLib/TdxLibNull.c deleted file mode 100644 index de357d88d4f9..000000000000 --- a/MdePkg/Library/TdxLib/TdxLibNull.c +++ /dev/null @@ -1,158 +0,0 @@ -/** @file - Null instance of TdxLib. - - Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
- SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include -#include -#include -#include - -/** - 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 UINT64 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 ges 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; -} - -/** - 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 - - @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/TdxLibNull.inf b/MdePkg/Library/TdxLib/TdxLibNull.inf deleted file mode 100644 index 77d3f0840437..000000000000 --- a/MdePkg/Library/TdxLib/TdxLibNull.inf +++ /dev/null @@ -1,31 +0,0 @@ -## @file -# Null Tdx library instance -# -# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
-# SPDX-License-Identifier: BSD-2-Clause-Patent -# -## - -[Defines] - INF_VERSION = 0x00010005 - BASE_NAME = TdxLibNull - FILE_GUID = 05C5E621-FC66-4420-9C80-F0DE9E5B95FF - 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 = X64 IA32 -# - -[Sources] - TdxLibNull.c - -[LibraryClasses] - BaseLib - DebugLib - -[Packages] - MdePkg/MdePkg.dec diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 4b93068cb5b6..9377a5486e36 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -239,7 +239,6 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf - TdxLib|MdePkg/Library/TdxLib/TdxLibNull.inf TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index bdf99013b687..0cab624266ee 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -243,7 +243,6 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf - TdxLib|MdePkg/Library/TdxLib/TdxLibNull.inf TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf index 8249bd3d4ec9..7474eea1993c 100644 --- a/OvmfPkg/Sec/SecMain.inf +++ b/OvmfPkg/Sec/SecMain.inf @@ -52,7 +52,6 @@ LocalApicLib CpuExceptionHandlerLib TdxStartupLib - TdxLib [Ppis] gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf index 823c21664493..6d514aca5b72 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf @@ -55,7 +55,6 @@ PcdLib VmgExitLib TdxProbeLib - TdxLib [Protocols] gEfiTimerArchProtocolGuid ## SOMETIMES_CONSUMES diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf index c454bb78168b..bcbe186e54f1 100644 --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf @@ -54,7 +54,6 @@ PcdLib VmgExitLib TdxProbeLib - TdxLib [Pcd] gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES From 062cb4aa9e88baed82543416dd12adb5d91f2189 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Tue, 6 Apr 2021 08:39:51 +0800 Subject: [PATCH 44/54] OvmfPkg: Delete PcdTdxSharedPageMask PcdTdxSharedPageMask indicates the GPA Width which is defined in OvmfPkg. But this PCD is used in MdePkg. We can call TdSharedPageMask() to do the same job as PcdTdxSharedPageMask. --- .../BaseMemEncryptTdxLib.inf | 3 --- .../BaseMemEncryptTdxLib/MemoryEncryption.c | 21 +------------------ OvmfPkg/OvmfPkg.dec | 2 -- OvmfPkg/OvmfPkgX64.dsc | 3 --- OvmfPkg/TdxDxe/TdxDxe.c | 11 ---------- OvmfPkg/TdxDxe/TdxDxe.inf | 2 -- 6 files changed, 1 insertion(+), 41 deletions(-) diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf index c1332fac9901..41438394da0d 100644 --- a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf @@ -44,6 +44,3 @@ TdxLib TdxProbeLib -[Pcd] - gUefiOvmfPkgTokenSpaceGuid.PcdTdxSharedPageMask - gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c index 202f491b73b5..b71e1c6843c9 100644 --- a/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c @@ -27,8 +27,6 @@ typedef enum { ClearSharedBit } TDX_PAGETABLE_MODE; -STATIC BOOLEAN mAddressEncMaskChecked = FALSE; -STATIC UINT64 mAddressEncMask; STATIC PAGE_TABLE_POOL *mPageTablePool = NULL; /** @@ -63,21 +61,7 @@ GetMemEncryptionAddressMask ( VOID ) { - if (mAddressEncMaskChecked) { - return mAddressEncMask; - } - - mAddressEncMask = PcdGet64(PcdTdxSharedPageMask); - - mAddressEncMaskChecked = TRUE; - DEBUG (( - DEBUG_INFO, - "%a:%a: AddressEncMask=0x%Lx\n", - gEfiCallerBaseName, - __FUNCTION__, - mAddressEncMask)); - - return mAddressEncMask; + return TdSharedPageMask(); } /** @@ -532,9 +516,6 @@ SetOrClearSharedBit( } Status = TdVmCall(TDVMCALL_MAPGPA, PhysicalAddress, Length, 0, 0, NULL); - if (PcdGetBool(PcdTdxDisableSharedMask) != TRUE) { - ASSERT(Status == 0); - } // // If changing shared to private, must accept-page again diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index f54a739a5833..41491e21c5ea 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -339,7 +339,6 @@ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x58 gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize|0x2000|UINT64|0x59 - gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask|FALSE|BOOLEAN|0x5a gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack|FALSE|BOOLEAN|0x5b gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask|0|UINT64|0x5c @@ -379,7 +378,6 @@ # This PCD is only accessed if PcdSmmSmramRequire is TRUE (see below). gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE|BOOLEAN|0x34 - gUefiOvmfPkgTokenSpaceGuid.PcdTdxSharedPageMask|0x0|UINT64|0x101 gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase|0|UINT64|0x102 ## This PCD records LAML field in TDX EVENTLOG ACPI table. diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 5c187b657664..d5d78e7cde6b 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -603,9 +603,6 @@ # !if $(TDX_IGNORE_VE_HLT) == TRUE gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|TRUE -!endif -!if $(TDX_DISABLE_SHARED_MASK) == TRUE - gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask|TRUE !endif # 32M gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize|0x2000000 diff --git a/OvmfPkg/TdxDxe/TdxDxe.c b/OvmfPkg/TdxDxe/TdxDxe.c index 8052b07731dd..cec3aea7670b 100644 --- a/OvmfPkg/TdxDxe/TdxDxe.c +++ b/OvmfPkg/TdxDxe/TdxDxe.c @@ -123,7 +123,6 @@ TdxDxeEntryPoint ( EFI_HOB_PLATFORM_INFO *PlatformInfo = NULL; EFI_HOB_GUID_TYPE *GuidHob; UINT32 CpuMaxLogicalProcessorNumber; - EFI_HOB_CPU * CpuHob; TD_RETURN_DATA TdReturnData; GuidHob = GetFirstGuidHob(&gUefiOvmfPkgTdxPlatformGuid); @@ -185,15 +184,5 @@ TdxDxeEntryPoint ( ASSERT_RETURN_ERROR(PcdStatus); } - if (PcdGetBool(PcdTdxDisableSharedMask) == TRUE) { - PcdStatus = PcdSet64S (PcdTdxSharedPageMask, 0); - ASSERT_RETURN_ERROR(PcdStatus); - } else { - CpuHob = GetFirstHob (EFI_HOB_TYPE_CPU); - ASSERT (CpuHob != NULL); - PcdStatus = PcdSet64S (PcdTdxSharedPageMask, (1ULL << (CpuHob->SizeOfMemorySpace - 1))); - ASSERT_RETURN_ERROR(PcdStatus); - } - return EFI_SUCCESS; } diff --git a/OvmfPkg/TdxDxe/TdxDxe.inf b/OvmfPkg/TdxDxe/TdxDxe.inf index c24b360a3146..c8f78cdf4f15 100644 --- a/OvmfPkg/TdxDxe/TdxDxe.inf +++ b/OvmfPkg/TdxDxe/TdxDxe.inf @@ -51,7 +51,5 @@ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber - gUefiOvmfPkgTokenSpaceGuid.PcdTdxSharedPageMask gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation - gUefiOvmfPkgTokenSpaceGuid.PcdTdxDisableSharedMask gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase From 4dc23f829761a3c0787b17628b7646138c40e899 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Mon, 12 Apr 2021 20:25:28 +0800 Subject: [PATCH 45/54] OvmfPkg: Fix the exception in GCC5/VS2019 Release build on Td guest Structure definition should be surrounded by #pragma pack(1), otherwise in Release version the structure may be not correct as design. --- .../TdxStartupLib/TdxStartupInternal.h | 3 +- OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c | 136 +++++------------- 2 files changed, 38 insertions(+), 101 deletions(-) diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h index abb40e4e0be6..4adac00583e5 100644 --- a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h @@ -69,14 +69,13 @@ BYTE sha384[SHA384_DIGEST_SIZE]; } TDX_DIGEST_VALUE; -#pragma pack() - typedef struct { UINT32 Signature; UINT8 *HashData; UINTN HashDataLen; } TDX_EVENT; +#pragma pack() #define LOOPIT(X) do { \ volatile int foo = (X); \ diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c index bcc16114e30c..33151729af31 100644 --- a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c @@ -80,28 +80,25 @@ PCR_TDX_EXTEND_MAP mPcrTdxExtendMaps[PCR_COUNT] = { {15, REG_TYPE_RTMR, 2, 3}, }; - #define PERF_ID_TD_TCG2_DXE 0x3130 +// TODO TDX command/response size +#define TCG2_DEFAULT_MAX_COMMAND_SIZE 0x1000 +#define TCG2_DEFAULT_MAX_RESPONSE_SIZE 0x1000 +#define HANDOFF_TABLE_DESC "TdxTable" +#define TCG_EVENT_LOG_AREA_COUNT_MAX 1 + +#pragma pack(1) + typedef struct { CHAR16 *VariableName; EFI_GUID *VendorGuid; } VARIABLE_TYPE; -// TODO TDX command/response size -#define TCG2_DEFAULT_MAX_COMMAND_SIZE 0x1000 -#define TCG2_DEFAULT_MAX_RESPONSE_SIZE 0x1000 - typedef struct { EFI_GUID *EventGuid; EFI_TCG2_EVENT_LOG_FORMAT LogFormat; } TCG2_EVENT_INFO_STRUCT; -TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = { - {&gTcgEvent2EntryHobGuid, EFI_TCG2_EVENT_LOG_FORMAT_TCG_2}, -}; - -#define TCG_EVENT_LOG_AREA_COUNT_MAX 1 - typedef struct { EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat; EFI_PHYSICAL_ADDRESS Lasa; @@ -113,26 +110,24 @@ typedef struct { UINTN Next800155EventOffset; } TCG_EVENT_LOG_AREA_STRUCT; -#define HANDOFF_TABLE_DESC "TdxTable" - typedef struct { - UINT8 TableDescriptionSize; - UINT8 TableDescription[sizeof(HANDOFF_TABLE_DESC)]; - UINT64 NumberOfTables; - EFI_CONFIGURATION_TABLE TableEntry[1]; - } TDX_HANDOFF_TABLE_POINTERS2; - - typedef struct { - UINT32 count; - TPMI_ALG_HASH hashAlg; - BYTE sha384[SHA384_DIGEST_SIZE]; - } TDX_DIGEST_VALUE; - - typedef struct { - UINT32 Signature; - UINT8 *HashData; - UINTN HashDataLen; - } TDX_EVENT; +typedef struct { + UINT8 TableDescriptionSize; + UINT8 TableDescription[sizeof(HANDOFF_TABLE_DESC)]; + UINT64 NumberOfTables; + EFI_CONFIGURATION_TABLE TableEntry[1]; +} TDX_HANDOFF_TABLE_POINTERS2; + +typedef struct { + UINT32 count; + TPMI_ALG_HASH hashAlg; + BYTE sha384[SHA384_DIGEST_SIZE]; +} TDX_DIGEST_VALUE; +typedef struct { + UINT32 Signature; + UINT8 *HashData; + UINTN HashDataLen; +} TDX_EVENT; typedef struct _TCG_DXE_DATA { EFI_TCG2_BOOT_SERVICE_CAPABILITY BsCap; @@ -142,6 +137,18 @@ typedef struct _TCG_DXE_DATA { EFI_TCG2_FINAL_EVENTS_TABLE *FinalEventsTable[TCG_EVENT_LOG_AREA_COUNT_MAX]; } TCG_DXE_DATA; +typedef struct{ + TPMI_ALG_HASH HashAlgo; + UINT16 HashSize; + UINT32 HashMask; +}TDX_HASH_INFO; + +#pragma pack() + +TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = { + {&gTcgEvent2EntryHobGuid, EFI_TCG2_EVENT_LOG_FORMAT_TCG_2}, +}; + TCG_DXE_DATA mTcgDxeData = { { sizeof (EFI_TCG2_BOOT_SERVICE_CAPABILITY), // Size @@ -184,12 +191,6 @@ EFI_TDX_EVENTLOG_ACPI_TABLE mTdxEventlogAcpiTemplate = { 0, // lasa }; -typedef struct{ - TPMI_ALG_HASH HashAlgo; - UINT16 HashSize; - UINT32 HashMask; -}TDX_HASH_INFO; - static TDX_HASH_INFO mHashInfo[] = { {TPM_ALG_SHA384, SHA384_DIGEST_SIZE, HASH_ALG_SHA384} }; @@ -521,41 +522,6 @@ Tcg2GetCapability ( { DEBUG ((DEBUG_INFO, "TdTcg2GetCapability\n")); return EFI_SUCCESS; -/* - DEBUG ((DEBUG_VERBOSE, "TDVF>>>> Tcg2GetCapability ...\n")); - - if ((This == NULL) || (ProtocolCapability == NULL)) { - return EFI_INVALID_PARAMETER; - } - - DEBUG ((DEBUG_VERBOSE, "Size - 0x%x\n", ProtocolCapability->Size)); - DEBUG ((DEBUG_VERBOSE, " 1.1 - 0x%x, 1.0 - 0x%x\n", sizeof(EFI_TCG2_BOOT_SERVICE_CAPABILITY), sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0))); - - if (ProtocolCapability->Size < mTcgDxeData.BsCap.Size) { - // - // Handle the case that firmware support 1.1 but OS only support 1.0. - // - if ((mTcgDxeData.BsCap.ProtocolVersion.Major > 0x01) || - ((mTcgDxeData.BsCap.ProtocolVersion.Major == 0x01) && ((mTcgDxeData.BsCap.ProtocolVersion.Minor > 0x00)))) { - if (ProtocolCapability->Size >= sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0)) { - CopyMem (ProtocolCapability, &mTcgDxeData.BsCap, sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0)); - ProtocolCapability->Size = sizeof(TREE_BOOT_SERVICE_CAPABILITY_1_0); - ProtocolCapability->StructureVersion.Major = 1; - ProtocolCapability->StructureVersion.Minor = 0; - ProtocolCapability->ProtocolVersion.Major = 1; - ProtocolCapability->ProtocolVersion.Minor = 0; - DEBUG ((EFI_D_ERROR, "TreeGetCapability (Compatible) - %r\n", EFI_SUCCESS)); - return EFI_SUCCESS; - } - } - ProtocolCapability->Size = mTcgDxeData.BsCap.Size; - return EFI_BUFFER_TOO_SMALL; - } - - CopyMem (ProtocolCapability, &mTcgDxeData.BsCap, mTcgDxeData.BsCap.Size); - DEBUG ((DEBUG_VERBOSE, "Tcg2GetCapability - %r\n", EFI_SUCCESS)); - return EFI_SUCCESS; -*/ } /** @@ -1469,29 +1435,6 @@ Tcg2SubmitCommand ( ) { return EFI_UNSUPPORTED; -/* - TDX_COMMANDS *TdCmd; - EFI_STATUS Status; - - DEBUG ((EFI_D_INFO, "Tcg2SubmitCommand ...\n")); - - TdCmd = (TDX_COMMANDS*)InputParameterBlock; - if(TdCmd->command != TDX_CMD_EXTEND_RTMR || TdCmd->command != TDX_CMD_TDREPORT){ - ASSERT(FALSE); - DEBUG((EFI_D_ERROR, "Unsupported Tdx commands - %d\n", TdCmd->command)); - return EFI_INVALID_PARAMETER; - } - - if(TdCmd->command == TDX_CMD_EXTEND_RTMR){ - Status = ExtendTdRtmr((UINT32*)TdCmd->params.extend_params.hash, SHA384_DIGEST_SIZE, TdCmd->params.extend_params.index); - }else if(TdCmd->command == TDX_CMD_TDREPORT){ - Status = DoTdReport(OutputParameterBlock, OutputParameterBlockSize, - TdCmd->params.tdreport_params.additional_data, - TdCmd->params.tdreport_params.size); - } - - return Status; -*/ } /** @@ -1614,7 +1557,6 @@ SetupEventLog ( TDX_EVENT *TdxEvent; TCG_PCR_EVENT2 *TcgPcrEvent2; TPML_DIGEST_VALUES DigestList; - UINTN I = 0; DEBUG ((EFI_D_INFO, "SetupEventLog\n")); @@ -1805,10 +1747,6 @@ SetupEventLog ( // UINTN HashDataLen; // }TDX_EVENT -// if (I == 1) { -// break; -// } - I++; TcgEvent = AllocateCopyPool (GET_GUID_HOB_DATA_SIZE (GuidHob.Guid), GET_GUID_HOB_DATA (GuidHob.Guid)); DEBUG((DEBUG_INFO, "GuidHob size = 0x%x\n", GET_GUID_HOB_DATA_SIZE (GuidHob.Guid))); ASSERT (TcgEvent != NULL); From a8648bb7f884e489ddbcb8364c4b227899bbd5e0 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 14 Apr 2021 09:36:46 +0800 Subject: [PATCH 46/54] OvmfPkg: Measure firware configuration from Qemu in DXE phase --- OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf | 2 + OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 90 +++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf index ba0e064ddc9c..91918c8f5533 100644 --- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf +++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf @@ -51,11 +51,13 @@ DxeServicesTableLib OrderedCollectionLib XenPlatformLib + TdxProbeLib [Protocols] gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiFirmwareVolume2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMED gEfiPciIoProtocolGuid # PROTOCOL SOMETIMES_CONSUMED + gTdTcg2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMES [Guids] gRootBridgesConnectedEventGroupGuid diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c index 97e0e0d295c1..b679e6b44ec6 100644 --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c @@ -19,6 +19,10 @@ #include #include #include +#include +#include + +EFI_TCG2_PROTOCOL *mTdTcg2Protocol = NULL; // // The user structure for the ordered collection that will track the fw_cfg @@ -35,6 +39,71 @@ typedef struct { // part of ACPI tables. } BLOB; +/** + Mesure firmware acpi configuration data from qemu. + @param[in] EventData Pointer to the event data. + @param[in] EventSize Size of event data. + @param[in] CfgDataBase Configuration data base address. + @param[in] EventSize Size of configuration data . + @retval EFI_NOT_FOUND Cannot locate protocol. + @retval EFI_OUT_OF_RESOURCES Allocate zero pool failure. + @return Status codes returned by + mTcg2Protocol->HashLogExtendEvent. +**/ +STATIC +EFI_STATUS +EFIAPI +MeasureQemuFwCfgAcpi( + IN CHAR8 *EventData, + IN UINT32 EventSize, + IN EFI_PHYSICAL_ADDRESS CfgDataBase, + IN UINTN CfgDataLength +) +{ + EFI_TCG2_EVENT *Tcg2Event; + EFI_STATUS Status; + + if (ProbeTdGuest () == FALSE) { + return EFI_SUCCESS; + } + + if (mTdTcg2Protocol == NULL) { + Status = gBS->LocateProtocol (&gTdTcg2ProtocolGuid, NULL, (VOID **) &mTdTcg2Protocol); + if (EFI_ERROR (Status)) { + // + // TdTcg2 protocol is not installed. + // + DEBUG ((EFI_D_ERROR, "MesureQemuFwCfgAcpi - TdTcg2 - %r\n", Status)); + return EFI_NOT_FOUND; + } + } + + Tcg2Event = AllocateZeroPool (EventSize + sizeof (EFI_TCG2_EVENT) - sizeof(Tcg2Event->Event)); + if (Tcg2Event == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcg2Event->Size = EventSize + sizeof (EFI_TCG2_EVENT) - sizeof(Tcg2Event->Event); + Tcg2Event->Header.EventType = EV_PLATFORM_CONFIG_FLAGS; + Tcg2Event->Header.PCRIndex = 1; + Tcg2Event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); + Tcg2Event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + CopyMem (&Tcg2Event->Event[0], EventData, EventSize); + + Status = mTdTcg2Protocol->HashLogExtendEvent (mTdTcg2Protocol, + 0, + CfgDataBase, + CfgDataLength, + Tcg2Event + ); + + FreePool (Tcg2Event); + + DEBUG ((DEBUG_INFO, "MeasureQemuFwCfg %s, %r\n", EventData, Status)); + + return Status; +} + /** Compare a standalone key against a user structure containing an embedded key. @@ -382,6 +451,16 @@ ProcessCmdAllocate ( QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, Blob->Base); + + Status = MeasureQemuFwCfgAcpi ((CHAR8 *) Allocate->File, + sizeof(Allocate->File), + (EFI_PHYSICAL_ADDRESS) Blob->Base, + FwCfgSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Measure %s failure\n", Allocate->File)); + } + ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size); DEBUG ((DEBUG_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx " @@ -999,6 +1078,17 @@ InstallQemuFwCfgTables ( EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount); QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, LoaderStart); + + Status = MeasureQemuFwCfgAcpi ( + "etc/table-loader", + sizeof ("etc/table-loader"), + (EFI_PHYSICAL_ADDRESS) LoaderStart, + FwCfgSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Measure etc/table-loader failure\n")); + } + RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount); LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; From 75d6d78c533936a5ceb654aa2dd2ae6e6594506b Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 15 Apr 2021 10:56:01 +0800 Subject: [PATCH 47/54] OvmfPkg: Measure QemuCfg SystemStates in SEC --- OvmfPkg/Include/Library/TdvfPlatformLib.h | 4 ++-- OvmfPkg/Library/TdxStartupLib/Tcg.c | 22 +++++++++++++++++++ OvmfPkg/Library/TdxStartupLib/TdxStartup.c | 4 +++- .../TdxStartupLib/TdxStartupInternal.h | 7 ++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/OvmfPkg/Include/Library/TdvfPlatformLib.h b/OvmfPkg/Include/Library/TdvfPlatformLib.h index c97b005fba12..27bfb03ee2e8 100644 --- a/OvmfPkg/Include/Library/TdvfPlatformLib.h +++ b/OvmfPkg/Include/Library/TdvfPlatformLib.h @@ -8,7 +8,7 @@ #include #define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000 - +#pragma pack(1) typedef struct { /// EFI_HOB_GUID_TYPE GuidHeader; @@ -17,7 +17,7 @@ typedef struct { BOOLEAN SetNxForStack; UINT8 SystemStates[6]; } EFI_HOB_PLATFORM_INFO; - +#pragma pack() VOID EFIAPI TdvfPlatformInitialize ( diff --git a/OvmfPkg/Library/TdxStartupLib/Tcg.c b/OvmfPkg/Library/TdxStartupLib/Tcg.c index 5ef0ec378246..a97cdcea75de 100644 --- a/OvmfPkg/Library/TdxStartupLib/Tcg.c +++ b/OvmfPkg/Library/TdxStartupLib/Tcg.c @@ -229,3 +229,25 @@ TdxMeasureFvImage ( return Status; } +EFI_STATUS +MeasureQemuCfgSystemSts ( + IN TCG_PCRINDEX PCRIndex, + IN UINT8 *HashData, + IN UINTN HashDataLength + ) +{ + EFI_STATUS Status; + CHAR8 *Item = "etc/system-states"; + + Status = CreateTdxExtendEvent ( + PCRIndex, // PCRIndex + EV_PLATFORM_CONFIG_FLAGS, // EventType + (UINT8*)Item, // EventData + AsciiStrLen(Item), // EventSize + HashData, // HashData + HashDataLength // HashDataLen + ); + + return Status; +} + diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartup.c b/OvmfPkg/Library/TdxStartupLib/TdxStartup.c index 747c422cd6ad..e548ec425406 100644 --- a/OvmfPkg/Library/TdxStartupLib/TdxStartup.c +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartup.c @@ -103,6 +103,7 @@ TdxStartup( UINT32 DxeCodeBase; UINT32 DxeCodeSize; TD_RETURN_DATA TdReturnData; + UINT8 *PlatformInfoPtr; Status = EFI_SUCCESS; BootFv = NULL; @@ -224,7 +225,8 @@ TdxStartup( MeasureConfigurationVolume ((UINT64)(UINTN)PcdGet32 (PcdCfvBase)); - BuildGuidDataHob (&gUefiOvmfPkgTdxPlatformGuid, &PlatformInfoHob, sizeof (EFI_HOB_PLATFORM_INFO)); + PlatformInfoPtr = (UINT8*)BuildGuidDataHob (&gUefiOvmfPkgTdxPlatformGuid, &PlatformInfoHob, sizeof (EFI_HOB_PLATFORM_INFO)); + MeasureQemuCfgSystemSts (1, PlatformInfoPtr + sizeof(EFI_HOB_PLATFORM_INFO) - 6, 6); BuildStackHob ((UINTN)SecCoreData->StackBase, SecCoreData->StackSize <<=1 ); diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h index 4adac00583e5..e652de76555c 100644 --- a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h @@ -188,6 +188,13 @@ CreateTdxExtendEvent ( IN UINTN HashDataLen ); +EFI_STATUS +MeasureQemuCfgSystemSts ( + IN TCG_PCRINDEX PCRIndex, + IN UINT8 *HashData, + IN UINTN HashDataLength + ); + VOID EFIAPI AsmGetRelocationMap ( From ba0632317f20556b64edb5771bcf743dda87d279 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Mon, 19 Apr 2021 16:24:09 +0800 Subject: [PATCH 48/54] OvmfPkg: Fix the failure of GCC Debug build --- MdePkg/Include/Protocol/Tdx.h | 14 +- OvmfPkg/Include/Library/TdxStartupLib.h | 18 ++ .../HashLibBaseCryptoRouterDxe.c | 34 ++- OvmfPkg/Library/TdxStartupLib/Tcg.c | 32 ++- .../TdxStartupLib/TdxStartupInternal.h | 25 +- OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c | 258 +++++++----------- 6 files changed, 163 insertions(+), 218 deletions(-) diff --git a/MdePkg/Include/Protocol/Tdx.h b/MdePkg/Include/Protocol/Tdx.h index 1dc427253add..0c09feb6fd4d 100644 --- a/MdePkg/Include/Protocol/Tdx.h +++ b/MdePkg/Include/Protocol/Tdx.h @@ -19,22 +19,10 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include -#define REG_TYPE_NA 0 -#define REG_TYPE_MRTD 1 -#define REG_TYPE_RTMR 2 -#define PCR_COUNT 16 - -typedef struct { - UINT8 Pcr; // PCR index - UINT8 RegType; // RTMR or MRTD - UINT8 Index; // index in RTMR/MRTD - UINT8 EventlogIndex;// index in EventLog -} PCR_TDX_EXTEND_MAP; - #define EFI_TD_PROTOCOL_GUID \ {0x96751a3d, 0x72f4, 0x41a6, { 0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b }} extern EFI_GUID gTdTcg2ProtocolGuid; -#endif \ No newline at end of file +#endif diff --git a/OvmfPkg/Include/Library/TdxStartupLib.h b/OvmfPkg/Include/Library/TdxStartupLib.h index 7419cc1c4413..03d85fc8b82d 100644 --- a/OvmfPkg/Include/Library/TdxStartupLib.h +++ b/OvmfPkg/Include/Library/TdxStartupLib.h @@ -7,6 +7,24 @@ #include #include #include +#include + +#pragma pack(1) + +typedef struct { + UINT32 count; + TPMI_ALG_HASH hashAlg; + BYTE sha384[SHA384_DIGEST_SIZE]; +} TDX_DIGEST_VALUE; + +typedef struct { + UINT32 Signature; + UINT64 HashDataPtr; + UINT64 HashDataLen; +} TDX_EVENT; + +#pragma pack() + typedef VOID diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c index 577645b69457..8aedb5aefb99 100644 --- a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c @@ -18,10 +18,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include "HashLibBaseCryptoRouterCommon.h" -extern PCR_TDX_EXTEND_MAP mPcrTdxExtendMaps[PCR_COUNT]; - HASH_INTERFACE mHashInterface[HASH_COUNT] = {{{0}, NULL, NULL, NULL}}; UINTN mHashInterfaceCount = 0; @@ -127,6 +126,31 @@ HashUpdate ( return EFI_SUCCESS; } +/** + MRTD => PCR[0] + RTMR[0] => PCR[1,7] + RTMR[1] => PCR[2,3,4,5,6] + RTMR[2] => PCR[8~15] + RTMR[3] => NA + +**/ +UINT8 GetMappedRtmrIndex(UINT32 PCRIndex) +{ + UINT8 RtmrIndex; + + ASSERT (PCRIndex <= 16 && PCRIndex >= 0); + RtmrIndex = 0; + if (PCRIndex == 1 || PCRIndex == 7) { + RtmrIndex = 0; + } else if (PCRIndex >= 2 && PCRIndex <= 6) { + RtmrIndex = 1; + } else if (PCRIndex >= 8 && PCRIndex <= 15) { + RtmrIndex = 2; + } + + return RtmrIndex; +} + /** Hash sequence complete and extend to PCR. @@ -153,15 +177,11 @@ HashCompleteAndExtend ( UINTN Index; EFI_STATUS Status; UINT32 HashMask; - PCR_TDX_EXTEND_MAP PcrTdxExtendMap; if (mHashInterfaceCount == 0) { return EFI_UNSUPPORTED; } - PcrTdxExtendMap = mPcrTdxExtendMaps[PcrIndex]; - ASSERT(PcrTdxExtendMap.Pcr == PcrIndex); - CheckSupportedHashMaskMismatch (); HashCtx = (HASH_HANDLE *)HashHandle; @@ -183,7 +203,7 @@ HashCompleteAndExtend ( Status = TdExtendRtmr ( (UINT32*)DigestList->digests[0].digest.sha384, SHA384_DIGEST_SIZE, - (UINT8)PcrTdxExtendMap.Index + GetMappedRtmrIndex(PcrIndex) ); return Status; } diff --git a/OvmfPkg/Library/TdxStartupLib/Tcg.c b/OvmfPkg/Library/TdxStartupLib/Tcg.c index a97cdcea75de..12c103393cc2 100644 --- a/OvmfPkg/Library/TdxStartupLib/Tcg.c +++ b/OvmfPkg/Library/TdxStartupLib/Tcg.c @@ -29,7 +29,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include - +#include #include "TdxStartupInternal.h" #pragma pack (1) @@ -70,6 +70,7 @@ CreateTdxExtendEvent ( TDX_EVENT *TdxEvent; UINT8 *DigestBuffer; TDX_DIGEST_VALUE *TdxDigest; + UINT8 *Ptr; DEBUG ((EFI_D_INFO, "Creating Tcg2PcrEvent PCR %d EventType 0x%x\n", PCRIndex, EventType)); @@ -91,40 +92,43 @@ CreateTdxExtendEvent ( DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - data %p\n", EventHobData)); + Ptr = (UINT8*)EventHobData; // // Initialize PcrEvent data now // - TcgPcrEvent2 = EventHobData; - TcgPcrEvent2->PCRIndex = PCRIndex; - TcgPcrEvent2->EventType = EventType; + CopyMem(Ptr, &PCRIndex, sizeof(TCG_PCRINDEX)); + Ptr += sizeof(TCG_PCRINDEX); + CopyMem(Ptr, &EventType, sizeof(TCG_EVENTTYPE)); + Ptr += sizeof(TCG_EVENTTYPE); // // We don't have a digest to copy yet, but we can to copy the eventsize/data now // - DigestBuffer = (UINT8 *)&TcgPcrEvent2->Digest; + DigestBuffer = Ptr; DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - digest %p\n", DigestBuffer)); TdxDigest = (TDX_DIGEST_VALUE *)DigestBuffer; TdxDigest->count = 1; TdxDigest->hashAlg = TPM_ALG_SHA384; + ZeroMem(TdxDigest->sha384, SHA384_DIGEST_SIZE); - DigestBuffer = DigestBuffer + sizeof(TDX_DIGEST_VALUE); + Ptr += sizeof(TDX_DIGEST_VALUE); DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - eventdata %p\n", DigestBuffer)); - CopyMem (DigestBuffer, &EventSize, sizeof(TcgPcrEvent2->EventSize)); - DigestBuffer = DigestBuffer + sizeof(TcgPcrEvent2->EventSize); - CopyMem (DigestBuffer, EventData, EventSize); - DigestBuffer = DigestBuffer + EventSize; - TdxEvent = (TDX_EVENT *)DigestBuffer; + CopyMem (Ptr, &EventSize, sizeof(UINT32)); + Ptr += sizeof(UINT32); + CopyMem (Ptr, EventData, EventSize); + Ptr += EventSize; + TdxEvent = (TDX_EVENT *)Ptr; // // Initialize the TdxEvent so we can perform measurement in DXE. // During early DXE, the gTcgEvent2EntryHobGuid will be parsed, the data hashed, and TcgEvent2 hobs // updated with the updated hash // - //TdxEvent->Signature = TCG_TDX_EVENT_DATA_SIGNATURE; - TdxEvent->HashData = HashData; - TdxEvent->HashDataLen = HashDataLen; + TdxEvent->Signature = SIGNATURE_32('T', 'D', 'E', 'T'); + TdxEvent->HashDataPtr = (UINT64)(UINTN)HashData; + TdxEvent->HashDataLen = (UINT64)HashDataLen; Status = EFI_SUCCESS; return Status; diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h index e652de76555c..9e2db4e0f3d3 100644 --- a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h @@ -56,25 +56,12 @@ } MP_RELOCATION_MAP; #define HANDOFF_TABLE_DESC "TdxTable" - typedef struct { - UINT8 TableDescriptionSize; - UINT8 TableDescription[sizeof(HANDOFF_TABLE_DESC)]; - UINT64 NumberOfTables; - EFI_CONFIGURATION_TABLE TableEntry[1]; - } TDX_HANDOFF_TABLE_POINTERS2; - - typedef struct { - UINT32 count; - TPMI_ALG_HASH hashAlg; - BYTE sha384[SHA384_DIGEST_SIZE]; - } TDX_DIGEST_VALUE; - - typedef struct { - UINT32 Signature; - UINT8 *HashData; - UINTN HashDataLen; - } TDX_EVENT; - +typedef struct { + UINT8 TableDescriptionSize; + UINT8 TableDescription[sizeof (HANDOFF_TABLE_DESC)]; + UINT64 NumberOfTables; + EFI_CONFIGURATION_TABLE TableEntry[1]; +} TDX_HANDOFF_TABLE_POINTERS2; #pragma pack() #define LOOPIT(X) do { \ diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c index 33151729af31..b22a5744f31d 100644 --- a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c @@ -45,50 +45,17 @@ #include #include +#include #include #include -/** - If TD guest firmware supports measurement and an event is created, - TD guest firmware is designed to report the event log with the same - data structure in TCG-Platform-Firmware-Profile spec with - EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 format. - - So there is map between the TDX MRTD/RTMR registers and TPM PCRs. - MRTD => PCR[0] - RTMR[0] => PCR[1,7] - RTMR[1] => PCR[2,3,4,5,6] - RTMR[2] => PCR[8~15] - RTMR[3] => NA - **/ -PCR_TDX_EXTEND_MAP mPcrTdxExtendMaps[PCR_COUNT] = { - {0 , REG_TYPE_MRTD, 0, 0}, - {1 , REG_TYPE_RTMR, 0, 1}, - {2 , REG_TYPE_RTMR, 1, 2}, - {3 , REG_TYPE_RTMR, 1, 2}, - {4 , REG_TYPE_RTMR, 1, 2}, - {5 , REG_TYPE_RTMR, 1, 2}, - {6 , REG_TYPE_RTMR, 1, 2}, - {7 , REG_TYPE_RTMR, 0, 1}, - {8 , REG_TYPE_RTMR, 2, 3}, - {9 , REG_TYPE_RTMR, 2, 3}, - {10, REG_TYPE_RTMR, 2, 3}, - {11, REG_TYPE_RTMR, 2, 3}, - {12, REG_TYPE_RTMR, 2, 3}, - {13, REG_TYPE_RTMR, 2, 3}, - {14, REG_TYPE_RTMR, 2, 3}, - {15, REG_TYPE_RTMR, 2, 3}, -}; #define PERF_ID_TD_TCG2_DXE 0x3130 // TODO TDX command/response size #define TCG2_DEFAULT_MAX_COMMAND_SIZE 0x1000 #define TCG2_DEFAULT_MAX_RESPONSE_SIZE 0x1000 -#define HANDOFF_TABLE_DESC "TdxTable" #define TCG_EVENT_LOG_AREA_COUNT_MAX 1 -#pragma pack(1) - typedef struct { CHAR16 *VariableName; EFI_GUID *VendorGuid; @@ -110,25 +77,6 @@ typedef struct { UINTN Next800155EventOffset; } TCG_EVENT_LOG_AREA_STRUCT; -typedef struct { - UINT8 TableDescriptionSize; - UINT8 TableDescription[sizeof(HANDOFF_TABLE_DESC)]; - UINT64 NumberOfTables; - EFI_CONFIGURATION_TABLE TableEntry[1]; -} TDX_HANDOFF_TABLE_POINTERS2; - -typedef struct { - UINT32 count; - TPMI_ALG_HASH hashAlg; - BYTE sha384[SHA384_DIGEST_SIZE]; -} TDX_DIGEST_VALUE; - -typedef struct { - UINT32 Signature; - UINT8 *HashData; - UINTN HashDataLen; -} TDX_EVENT; - typedef struct _TCG_DXE_DATA { EFI_TCG2_BOOT_SERVICE_CAPABILITY BsCap; TCG_EVENT_LOG_AREA_STRUCT EventLogAreaStruct[TCG_EVENT_LOG_AREA_COUNT_MAX]; @@ -143,8 +91,6 @@ typedef struct{ UINT32 HashMask; }TDX_HASH_INFO; -#pragma pack() - TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = { {&gTcgEvent2EntryHobGuid, EFI_TCG2_EVENT_LOG_FORMAT_TCG_2}, }; @@ -191,7 +137,7 @@ EFI_TDX_EVENTLOG_ACPI_TABLE mTdxEventlogAcpiTemplate = { 0, // lasa }; -static TDX_HASH_INFO mHashInfo[] = { +TDX_HASH_INFO mHashInfo[] = { {TPM_ALG_SHA384, SHA384_DIGEST_SIZE, HASH_ALG_SHA384} }; @@ -973,13 +919,29 @@ TcgCommLogEvent ( return EFI_SUCCESS; } +/** + MRTD => PCR[0] + RTMR[0] => PCR[1,7] + RTMR[1] => PCR[2,3,4,5,6] + RTMR[2] => PCR[8~15] + RTMR[3] => NA + +**/ UINT32 GetMappedIndexInEventLog(UINT32 PCRIndex) { - PCR_TDX_EXTEND_MAP *PcrTdxMap; + UINT32 RtmrIndex; + + ASSERT (PCRIndex <= 16 && PCRIndex >= 0); + RtmrIndex = 0; + if (PCRIndex == 1 || PCRIndex == 7) { + RtmrIndex = 0; + } else if (PCRIndex >= 2 && PCRIndex <= 6) { + RtmrIndex = 1; + } else if (PCRIndex >= 8 && PCRIndex <= 15) { + RtmrIndex = 2; + } - ASSERT(PCRIndex >= 0 && PCRIndex < PCR_COUNT); - PcrTdxMap = (PCR_TDX_EXTEND_MAP*)&mPcrTdxExtendMaps[PCRIndex]; - return PcrTdxMap->EventlogIndex; + return RtmrIndex + 1; } /** @@ -1535,14 +1497,9 @@ SetupEventLog ( ) { EFI_STATUS Status; - VOID *TcgEvent; EFI_PEI_HOB_POINTERS GuidHob; EFI_PHYSICAL_ADDRESS Lasa; UINTN Index; - VOID *DigestListBin; - UINT32 DigestListBinSize; - UINT8 *Event; - UINT32 EventSize; TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct; UINT8 TempBuf[sizeof(TCG_EfiSpecIDEventStruct) + sizeof(UINT32) @@ -1554,11 +1511,8 @@ SetupEventLog ( TCG_EfiSpecIdEventAlgorithmSize *TempDigestSize; UINT8 *VendorInfoSize; UINT32 NumberOfAlgorithms; - TDX_EVENT *TdxEvent; - TCG_PCR_EVENT2 *TcgPcrEvent2; - TPML_DIGEST_VALUES DigestList; - DEBUG ((EFI_D_INFO, "SetupEventLog\n")); + DEBUG ((EFI_D_INFO, "Td: SetupEventLog\n")); // // 1. Create Log Area @@ -1719,101 +1673,6 @@ SetupEventLog ( } } - // - // 3. Sync data from PEI to DXE - // - Status = EFI_SUCCESS; - for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { - if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { - GuidHob.Raw = GetHobList (); - Status = EFI_SUCCESS; - while (!EFI_ERROR (Status) && (GuidHob.Raw = GetNextGuidHob (mTcg2EventInfo[Index].EventGuid, GuidHob.Raw)) != NULL) { - // - // TcgEvent points to TCG_PCR_EVENT2 + TDX_EVENT - // The Digest field in TCG_PCR_EVENT2 is not populated at this moment - // Instead its value should be calculated here by hashing the DATA designated by TDX_EVENT - // - // typedef struct tdTCG_PCR_EVENT2 { - // TCG_PCRINDEX PCRIndex; - // TCG_EVENTTYPE EventType; - // TPML_DIGEST_VALUES Digest; // In TDVF this field is TDX_DIGEST_VALUE which is same as TPML_DIGEST_VALUES - // UINT32 EventSize; - // UINT8 Event[1]; - // } TCG_PCR_EVENT2; - // - // typedef struct { - // UINT32 Signature; - // UINT8 *HashData; - // UINTN HashDataLen; - // }TDX_EVENT - - TcgEvent = AllocateCopyPool (GET_GUID_HOB_DATA_SIZE (GuidHob.Guid), GET_GUID_HOB_DATA (GuidHob.Guid)); - DEBUG((DEBUG_INFO, "GuidHob size = 0x%x\n", GET_GUID_HOB_DATA_SIZE (GuidHob.Guid))); - ASSERT (TcgEvent != NULL); - TcgPcrEvent2 = (TCG_PCR_EVENT2*)TcgEvent; - - // This is for next iteration - GuidHob.Raw = GET_NEXT_HOB (GuidHob); - - // - // TDVF only supports EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 - if(mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) { - - // - // PCRIndex + EventType + Digest + EventSize + Event - // - DigestListBin = (UINT8 *)TcgEvent + sizeof(TCG_PCRINDEX) + sizeof(TCG_EVENTTYPE); - DigestListBinSize = GetDigestListBinSize (DigestListBin); - - // - // Event size. - // - CopyMem (&EventSize, (UINT8 *)DigestListBin + DigestListBinSize, sizeof(UINT32)); - Event = (UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32); - - // - // TdxEvent - // - TdxEvent = (TDX_EVENT*)((UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32) + EventSize); - DEBUG((DEBUG_INFO, "DigestListBinSize=0x%x, EventSize=0x%x\n", - DigestListBinSize, EventSize)); - DEBUG((DEBUG_INFO, "HashData: %p, HashDataLen=0x%x\n", - TdxEvent->HashData, TdxEvent->HashDataLen)); - - - ZeroMem((UINT8*)&DigestList, sizeof(DigestList)); - // - // Calculate the Digest by hashing(SHA384) the DATA designated by TdxEvent - // - Status = HashAndExtend(TcgPcrEvent2->PCRIndex, - TdxEvent->HashData, - TdxEvent->HashDataLen, - &DigestList); - DEBUG((DEBUG_INFO, "Digest count=%d, hashAlg=0x%x\n", DigestList.count, DigestList.digests[0].hashAlg)); - - ASSERT(Status == EFI_SUCCESS); - ASSERT(DigestList.count == 1 && DigestList.digests[0].hashAlg == TPM_ALG_SHA384); - TcgPcrEvent2->Digest.count = 1; - CopyMem((UINT8*)&TcgPcrEvent2->Digest + sizeof(UINT32), (UINT8*)&DigestList.digests[0], sizeof(TPMI_ALG_HASH) + SHA384_DIGEST_SIZE); - DumpEvent2(TcgPcrEvent2); - - // - // Log the event - // - Status = TcgDxeLogEvent ( - mTcg2EventInfo[Index].LogFormat, - TcgEvent, - sizeof(TCG_PCRINDEX) + sizeof(TCG_EVENTTYPE) + DigestListBinSize + sizeof(UINT32), - Event, - EventSize - ); - } - - FreePool (TcgEvent); - } - } - } - return Status; } @@ -2505,6 +2364,73 @@ OnExitBootServicesFailed ( } +EFI_STATUS +SyncTdTcgEvent() +{ + EFI_STATUS Status; + EFI_PEI_HOB_POINTERS GuidHob; + VOID *TcgEvent; + VOID *DigestListBin; + UINT32 DigestListBinSize; + UINT8 *Event; + UINT32 EventSize; + TDX_EVENT *TdxEvent; + TPML_DIGEST_VALUES DigestList; + + DEBUG ((DEBUG_INFO, "Sync Tdx event from SEC\n")); + + Status = EFI_SUCCESS; + GuidHob.Guid = GetFirstGuidHob (&gTcgEvent2EntryHobGuid); + + while (!EFI_ERROR(Status) && GuidHob.Guid != NULL) { + TcgEvent = AllocateCopyPool (GET_GUID_HOB_DATA_SIZE (GuidHob.Guid), GET_GUID_HOB_DATA (GuidHob.Guid)); + + GuidHob.Guid = GET_NEXT_HOB (GuidHob); + GuidHob.Guid = GetNextGuidHob (&gTcgEvent2EntryHobGuid, GuidHob.Guid); + + DigestListBin = (UINT8 *)TcgEvent + sizeof(TCG_PCRINDEX) + sizeof(TCG_EVENTTYPE); + DigestListBinSize = GetDigestListBinSize(DigestListBin); + + // + // Event size. + // + EventSize = *(UINT32*)((UINT8 *) DigestListBin + DigestListBinSize); + Event = (UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32); + // + // Hash and extend the data from SEC + // + TdxEvent = (TDX_EVENT*)((UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32) + EventSize); + Status = HashAndExtend (*(UINT32*)(TcgEvent), + (VOID*)(UINTN)TdxEvent->HashDataPtr, + TdxEvent->HashDataLen, + &DigestList); + + // + // Copy the hash data to TcgEvent + // + CopyMem ((UINT8*)DigestListBin + sizeof (UINT32) + sizeof (TPMI_ALG_HASH), + DigestList.digests[0].digest.sha384, + SHA384_DIGEST_SIZE); + + // + // Log the event + // + Status = TcgDxeLogEvent ( + mTcg2EventInfo[0].LogFormat, + TcgEvent, + sizeof (TCG_PCRINDEX) + sizeof (TCG_EVENTTYPE) + DigestListBinSize + sizeof (UINT32), + Event, + EventSize + ); + + DumpEvent2 ((TCG_PCR_EVENT2*) TcgEvent); + FreePool (TcgEvent); + } + + return Status; +} + + /** Install TDVF ACPI Table when ACPI Table Protocol is available. @@ -2561,7 +2487,6 @@ InstallAcpiTable ( ASSERT_EFI_ERROR (Status); DEBUG((DEBUG_INFO, "TDX Eventlog ACPI Table is measured and logged\n")); - } /** @@ -2648,6 +2573,9 @@ DriverEntry ( Status = SetupEventLog (); ASSERT_EFI_ERROR (Status); + Status = SyncTdTcgEvent(); + ASSERT_EFI_ERROR (Status); + // // Measure handoff tables, Boot#### variables etc. // From 82f3768bad90ceb67e5ca6219487afb579d63f71 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Tue, 20 Apr 2021 14:26:37 +0800 Subject: [PATCH 49/54] CryptoPkg: Add SecCryptLib --- .../Library/BaseCryptLib/SecCryptLib.inf | 68 +++++++++++++++++++ .../Library/BaseCryptLib/SecCryptLib.uni | 25 +++++++ 2 files changed, 93 insertions(+) create mode 100644 CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf create mode 100644 CryptoPkg/Library/BaseCryptLib/SecCryptLib.uni diff --git a/CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf new file mode 100644 index 000000000000..fb792616921e --- /dev/null +++ b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf @@ -0,0 +1,68 @@ +## @file +# Cryptographic Library Instance for SEC. +# +# Caution: This module requires additional review when modified. +# This library will have external input - signature. +# This external input must be validated carefully to avoid security issues such as +# buffer overflow or integer overflow. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecCryptLib + MODULE_UNI_FILE = SecCryptLib.uni + FILE_GUID = 3689D343-0D32-4284-8053-BF10537990E8 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = BaseCryptLib|SEC + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + InternalCryptLib.h + Hash/CryptSha512.c + + SysCall/CrtWrapper.c + SysCall/ConstantTimeClock.c + SysCall/BaseMemAllocation.c + +[Packages] + MdePkg/MdePkg.dec + CryptoPkg/CryptoPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + MemoryAllocationLib + DebugLib + OpensslLib + IntrinsicLib + +# +# Remove these [BuildOptions] after this library is cleaned up +# +[BuildOptions] + # + # suppress the following warnings so we do not break the build with warnings-as-errors: + # C4090: 'function' : different 'const' qualifiers + # C4718: 'function call' : recursive call has no side effects, deleting + # + MSFT:*_*_*_CC_FLAGS = /wd4090 /wd4718 + + # -JCryptoPkg/Include : To disable the use of the system includes provided by RVCT + # --diag_remark=1 : Reduce severity of "#1-D: last line of file ends without a newline" + RVCT:*_*_ARM_CC_FLAGS = -JCryptoPkg/Include --diag_remark=1 + + GCC:*_CLANG35_*_CC_FLAGS = -std=c99 + GCC:*_CLANG38_*_CC_FLAGS = -std=c99 + GCC:*_CLANGPDB_*_CC_FLAGS = -std=c99 -Wno-error=incompatible-pointer-types + + XCODE:*_*_*_CC_FLAGS = -std=c99 diff --git a/CryptoPkg/Library/BaseCryptLib/SecCryptLib.uni b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.uni new file mode 100644 index 000000000000..aa25c8ce7027 --- /dev/null +++ b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.uni @@ -0,0 +1,25 @@ +// /** @file +// Cryptographic Library Instance for PEIM. +// +// Caution: This module requires additional review when modified. +// This library will have external input - signature. +// This external input must be validated carefully to avoid security issues such as +// buffer overflow or integer overflow. +// +// Note: AES +// functions, RSA external functions, PKCS#7 SignedData sign functions, +// Diffie-Hellman functions, X.509 certificate handler functions, authenticode +// signature verification functions, PEM handler functions, and pseudorandom number +// generator functions are not supported in this instance. +// +// Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Cryptographic Library Instance for PEIM" + +#string STR_MODULE_DESCRIPTION #language en-US "Caution: This module requires additional review when modified. This library will have external input - signature. This external input must be validated carefully to avoid security issues such as buffer overflow or integer overflow. Note: AES functions, RSA external functions, PKCS#7 SignedData sign functions, Diffie-Hellman functions, X.509 certificate handler functions, authenticode signature verification functions, PEM handler functions, and pseudorandom number generator functions are not supported in this instance." + From c517f3290d0399e2068a482fb9b63482ad672188 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Tue, 20 Apr 2021 14:39:53 +0800 Subject: [PATCH 50/54] OvmfPkg: Do hash in SEC phase and record the eventlog in DXE Some TDX configuration need be measured and extended to Rtmr in SEC phase. For example, the TdHob, CFV, QemuCfg, etc. Then the measurement should be recorded to the TdEventlog in DXE phase. --- MdePkg/Library/TdxLib/Rtmr.c | 29 ++-- OvmfPkg/Include/Library/TdxStartupLib.h | 6 - ...oRouterDxe.c => HashLibBaseCryptoRouter.c} | 135 +++--------------- ...terDxe.inf => HashLibBaseCryptoRouter.inf} | 17 +-- ...terDxe.uni => HashLibBaseCryptoRouter.uni} | 0 OvmfPkg/Library/TdxStartupLib/Tcg.c | 38 ++--- .../Library/TdxStartupLib/TdxStartupLib.inf | 1 + OvmfPkg/OvmfPkg.dec | 10 -- OvmfPkg/OvmfPkgX64.dsc | 6 +- OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c | 23 +-- 10 files changed, 64 insertions(+), 201 deletions(-) rename OvmfPkg/Library/HashLibBaseCryptoRouterTdx/{HashLibBaseCryptoRouterDxe.c => HashLibBaseCryptoRouter.c} (58%) rename OvmfPkg/Library/HashLibBaseCryptoRouterTdx/{HashLibBaseCryptoRouterDxe.inf => HashLibBaseCryptoRouter.inf} (61%) rename OvmfPkg/Library/HashLibBaseCryptoRouterTdx/{HashLibBaseCryptoRouterDxe.uni => HashLibBaseCryptoRouter.uni} (100%) diff --git a/MdePkg/Library/TdxLib/Rtmr.c b/MdePkg/Library/TdxLib/Rtmr.c index 34e6e11423e8..9c60d3c65e0e 100644 --- a/MdePkg/Library/TdxLib/Rtmr.c +++ b/MdePkg/Library/TdxLib/Rtmr.c @@ -16,7 +16,7 @@ #include #define RTMR_COUNT 4 -#define TD_EXTEND_BUFFER_LEN (16 * 4 + 32) +#define TD_EXTEND_BUFFER_LEN (64 + 64) #define EXTEND_BUFFER_ADDRESS_MASK 0x3f @@ -31,10 +31,8 @@ TDX_EXTEND_BUFFER mExtendBuffer; /** TD.RTMR.EXTEND requires 64B-aligned guest physical address of - 48B-extension data. - #pragma pack() only supports 1/2/4/8/16, So we pre-allocate a - (16*4 + 32) length buffer in stack. In runtime we walk thru the - Buffer to find out a 64B-aligned start address. + 48B-extension data. In runtime we walk thru the Buffer to find + out a 64B-aligned start address. @return Start address of the extend buffer @@ -45,28 +43,23 @@ GetExtendBuffer ( VOID ) { - UINT8 ExtendBufferStart; UINT8 *ExtendBufferAddress; + UINT64 Gap; if (mExtendBufferAddress != NULL) { return mExtendBufferAddress; } - ExtendBufferStart = 0; ExtendBufferAddress = mExtendBuffer.Buffer; - while (ExtendBufferStart < TD_EXTEND_BUFFER_LEN) { - ExtendBufferAddress += ExtendBufferStart; - if (((UINT64)(UINTN)ExtendBufferAddress & EXTEND_BUFFER_ADDRESS_MASK) == 0) { - mExtendBufferAddress = ExtendBufferAddress; - break; - } else { - ExtendBufferStart += 16; - } - } + Gap = 0x40 - ((UINT64)(UINTN)ExtendBufferAddress & EXTEND_BUFFER_ADDRESS_MASK); + mExtendBufferAddress = (UINT8*)((UINT64)(UINTN)ExtendBufferAddress + Gap); + + DEBUG ((DEBUG_VERBOSE, "ExtendBufferAddress: 0x%p, Gap: 0x%x\n", ExtendBufferAddress, Gap)); + DEBUG ((DEBUG_VERBOSE, "mExtendBufferAddress: 0x%p\n", mExtendBufferAddress)); + + ASSERT (mExtendBufferAddress + 64 <= ExtendBufferAddress + TD_EXTEND_BUFFER_LEN); - ASSERT (ExtendBufferStart < TD_EXTEND_BUFFER_LEN); - DEBUG ((DEBUG_VERBOSE, "ExtendBufferAddress: 0x%p, 0x%x\n", ExtendBufferAddress, ExtendBufferStart)); return mExtendBufferAddress; } diff --git a/OvmfPkg/Include/Library/TdxStartupLib.h b/OvmfPkg/Include/Library/TdxStartupLib.h index 03d85fc8b82d..1a53253cca27 100644 --- a/OvmfPkg/Include/Library/TdxStartupLib.h +++ b/OvmfPkg/Include/Library/TdxStartupLib.h @@ -17,12 +17,6 @@ typedef struct { BYTE sha384[SHA384_DIGEST_SIZE]; } TDX_DIGEST_VALUE; -typedef struct { - UINT32 Signature; - UINT64 HashDataPtr; - UINT64 HashDataLen; -} TDX_EVENT; - #pragma pack() diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.c similarity index 58% rename from OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c rename to OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.c index 8aedb5aefb99..0cf2ae73eaad 100644 --- a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.c +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.c @@ -1,9 +1,7 @@ /** @file - This library is BaseCrypto router. It will redirect hash request to each individual - hash handler registered, such as SHA1, SHA256. - Platform can use PcdTdxHashMask to mask some hash engines. + This library is BaseCrypto router. -Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2013 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -12,7 +10,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include -#include #include #include #include @@ -21,32 +18,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include "HashLibBaseCryptoRouterCommon.h" -HASH_INTERFACE mHashInterface[HASH_COUNT] = {{{0}, NULL, NULL, NULL}}; -UINTN mHashInterfaceCount = 0; - -UINT32 mSupportedHashMaskLast = 0; -UINT32 mSupportedHashMaskCurrent = 0; - -/** - Check mismatch of supported HashMask between modules - that may link different HashInstanceLib instances. +#define TDX_HASH_COUNT 1 +HASH_INTERFACE mHashInterface[TDX_HASH_COUNT] = {{{0}, NULL, NULL, NULL}}; -**/ -VOID -CheckSupportedHashMaskMismatch ( - VOID - ) -{ - if (mSupportedHashMaskCurrent != mSupportedHashMaskLast) { - DEBUG (( - DEBUG_WARN, - "WARNING: There is mismatch of supported HashMask (0x%x - 0x%x) between modules\n", - mSupportedHashMaskCurrent, - mSupportedHashMaskLast - )); - DEBUG ((DEBUG_WARN, "that are linking different HashInstanceLib instances!\n")); - } -} +UINTN mHashInterfaceCount = 0; +HASH_HANDLE mHashCtx[TDX_HASH_COUNT] = {0}; /** Start hash sequence. @@ -63,24 +39,13 @@ HashStart ( ) { HASH_HANDLE *HashCtx; - UINTN Index; - UINT32 HashMask; if (mHashInterfaceCount == 0) { return EFI_UNSUPPORTED; } - CheckSupportedHashMaskMismatch (); - - HashCtx = AllocatePool (sizeof(*HashCtx) * mHashInterfaceCount); - ASSERT (HashCtx != NULL); - - for (Index = 0; Index < mHashInterfaceCount; Index++) { - HashMask = Tpm2GetHashMaskFromAlgo (&mHashInterface[Index].HashGuid); - if ((HashMask & PcdGet32 (PcdTdxHashMask)) != 0) { - mHashInterface[Index].HashInit (&HashCtx[Index]); - } - } + HashCtx = mHashCtx; + mHashInterface[0].HashInit (&HashCtx[0]); *HashHandle = (HASH_HANDLE)HashCtx; @@ -105,23 +70,13 @@ HashUpdate ( ) { HASH_HANDLE *HashCtx; - UINTN Index; - UINT32 HashMask; if (mHashInterfaceCount == 0) { return EFI_UNSUPPORTED; } - CheckSupportedHashMaskMismatch (); - HashCtx = (HASH_HANDLE *)HashHandle; - - for (Index = 0; Index < mHashInterfaceCount; Index++) { - HashMask = Tpm2GetHashMaskFromAlgo (&mHashInterface[Index].HashGuid); - if ((HashMask & PcdGet32 (PcdTdxHashMask)) != 0) { - mHashInterface[Index].HashUpdate (HashCtx[Index], DataToHash, DataToHashLen); - } - } + mHashInterface[0].HashUpdate (HashCtx[0], DataToHash, DataToHashLen); return EFI_SUCCESS; } @@ -174,29 +129,18 @@ HashCompleteAndExtend ( { TPML_DIGEST_VALUES Digest; HASH_HANDLE *HashCtx; - UINTN Index; EFI_STATUS Status; - UINT32 HashMask; if (mHashInterfaceCount == 0) { return EFI_UNSUPPORTED; } - CheckSupportedHashMaskMismatch (); - HashCtx = (HASH_HANDLE *)HashHandle; ZeroMem (DigestList, sizeof(*DigestList)); - for (Index = 0; Index < mHashInterfaceCount; Index++) { - HashMask = Tpm2GetHashMaskFromAlgo (&mHashInterface[Index].HashGuid); - if ((HashMask & PcdGet32 (PcdTdxHashMask)) != 0) { - mHashInterface[Index].HashUpdate (HashCtx[Index], DataToHash, DataToHashLen); - mHashInterface[Index].HashFinal (HashCtx[Index], &Digest); - Tpm2SetHashToDigestList (DigestList, &Digest); - } - } - - FreePool (HashCtx); + mHashInterface[0].HashUpdate (HashCtx[0], DataToHash, DataToHashLen); + mHashInterface[0].HashFinal (HashCtx[0], &Digest); + Tpm2SetHashToDigestList (DigestList, &Digest); ASSERT(DigestList->count == 1 && DigestList->digests[0].hashAlg == TPM_ALG_SHA384); @@ -230,18 +174,17 @@ HashAndExtend ( HASH_HANDLE HashHandle; EFI_STATUS Status; - DEBUG((DEBUG_INFO, "Td: HashAndExtend: %d, %p, 0x%x\n", PcrIndex, DataToHash, DataToHashLen)); - if (mHashInterfaceCount == 0) { return EFI_UNSUPPORTED; } - CheckSupportedHashMaskMismatch (); - HashStart (&HashHandle); HashUpdate (HashHandle, DataToHash, DataToHashLen); Status = HashCompleteAndExtend (HashHandle, PcrIndex, NULL, 0, DigestList); + DEBUG((DEBUG_INFO, "Td: HashAndExtend: %d, %p, 0x%x, %r\n", + PcrIndex, DataToHash, DataToHashLen, Status)); + return Status; } @@ -262,15 +205,17 @@ RegisterHashInterfaceLib ( { UINTN Index; UINT32 HashMask; - EFI_STATUS Status; // // Check allow // HashMask = Tpm2GetHashMaskFromAlgo (&HashInterface->HashGuid); - if ((HashMask & PcdGet32 (PcdTdxHashMask)) == 0) { + ASSERT (HashMask == HASH_ALG_SHA384); + + if (HashMask != HASH_ALG_SHA384) { return EFI_UNSUPPORTED; } + DEBUG((DEBUG_INFO, "TD: Hash is registered. mask = 0x%x, guid = %g\n", HashMask, HashInterface->HashGuid)); if (mHashInterfaceCount >= sizeof(mHashInterface)/sizeof(mHashInterface[0])) { @@ -287,51 +232,9 @@ RegisterHashInterfaceLib ( } } - // - // Record hash algorithm bitmap of CURRENT module which consumes HashLib. - // - mSupportedHashMaskCurrent = PcdGet32 (PcdTdxHashAlgorithmBitmap) | HashMask; - Status = PcdSet32S (PcdTdxHashAlgorithmBitmap, mSupportedHashMaskCurrent); - ASSERT_EFI_ERROR (Status); - CopyMem (&mHashInterface[mHashInterfaceCount], HashInterface, sizeof(*HashInterface)); mHashInterfaceCount ++; return EFI_SUCCESS; } -/** - The constructor function of HashLibBaseCryptoRouterDxe. - - @param ImageHandle The firmware allocated handle for the EFI image. - @param SystemTable A pointer to the EFI System Table. - - @retval EFI_SUCCESS The constructor executed correctly. - -**/ -EFI_STATUS -EFIAPI -HashLibBaseCryptoRouterDxeConstructor ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_STATUS Status; - - if (!ProbeTdGuest()) { - return EFI_SUCCESS; - } - - // - // Record hash algorithm bitmap of LAST module which also consumes HashLib. - // - mSupportedHashMaskLast = PcdGet32 (PcdTdxHashAlgorithmBitmap); - - // - // Set PcdTdxHashAlgorithmBitmap to 0 in CONSTRUCTOR for CURRENT module. - // - Status = PcdSet32S (PcdTdxHashAlgorithmBitmap, 0); - ASSERT_EFI_ERROR (Status); - - return EFI_SUCCESS; -} diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf similarity index 61% rename from OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf rename to OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf index e746b619704a..d8124b214b2b 100644 --- a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf @@ -12,13 +12,12 @@ [Defines] INF_VERSION = 0x00010005 - BASE_NAME = HashLibBaseCryptoRouterDxe - MODULE_UNI_FILE = HashLibBaseCryptoRouterDxe.uni + BASE_NAME = HashLibBaseCryptoRouter + MODULE_UNI_FILE = HashLibBaseCryptoRouter.uni FILE_GUID = 77F6EA3E-1ABA-4467-A447-926E8CEB2D13 - MODULE_TYPE = DXE_DRIVER + MODULE_TYPE = BASE VERSION_STRING = 1.0 - LIBRARY_CLASS = HashLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER - CONSTRUCTOR = HashLibBaseCryptoRouterDxeConstructor + LIBRARY_CLASS = HashLib # # The following information is for reference only and not required by the build tools. @@ -29,7 +28,7 @@ [Sources] HashLibBaseCryptoRouterCommon.h HashLibBaseCryptoRouterCommon.c - HashLibBaseCryptoRouterDxe.c + HashLibBaseCryptoRouter.c [Packages] MdePkg/MdePkg.dec @@ -40,13 +39,7 @@ BaseLib BaseMemoryLib DebugLib - MemoryAllocationLib PcdLib TdxProbeLib TdxLib -[Pcd] - gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashMask ## CONSUMES - ## SOMETIMES_CONSUMES - ## SOMETIMES_PRODUCES - gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashAlgorithmBitmap diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.uni b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.uni similarity index 100% rename from OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.uni rename to OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.uni diff --git a/OvmfPkg/Library/TdxStartupLib/Tcg.c b/OvmfPkg/Library/TdxStartupLib/Tcg.c index 12c103393cc2..7b0b145da0b4 100644 --- a/OvmfPkg/Library/TdxStartupLib/Tcg.c +++ b/OvmfPkg/Library/TdxStartupLib/Tcg.c @@ -44,6 +44,8 @@ typedef struct { #pragma pack () +extern UINT32 GetMappedRtmrIndex(UINT32 PCRIndex); + /** Add a new entry to the Event Log. @@ -65,14 +67,26 @@ CreateTdxExtendEvent ( ) { EFI_STATUS Status; + UINT32 RtmrIndex; VOID *EventHobData; TCG_PCR_EVENT2 *TcgPcrEvent2; - TDX_EVENT *TdxEvent; UINT8 *DigestBuffer; TDX_DIGEST_VALUE *TdxDigest; + TPML_DIGEST_VALUES DigestList; UINT8 *Ptr; - DEBUG ((EFI_D_INFO, "Creating Tcg2PcrEvent PCR %d EventType 0x%x\n", PCRIndex, EventType)); + + DEBUG ((EFI_D_INFO, "Creating TdTcg2PcrEvent PCR %d EventType 0x%x\n", PCRIndex, EventType)); + + RtmrIndex = GetMappedRtmrIndex (PCRIndex); + Status = HashAndExtend (RtmrIndex, + (VOID*)HashData, + HashDataLen, + &DigestList); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Failed to HashAndExtend. %r\n", Status)); + return Status; + } // // Use TDX_DIGEST_VALUE in the GUID HOB DataLength calculation @@ -83,8 +97,7 @@ CreateTdxExtendEvent ( &gTcgEvent2EntryHobGuid, sizeof(TcgPcrEvent2->PCRIndex) + sizeof(TcgPcrEvent2->EventType) + sizeof(TDX_DIGEST_VALUE) + - sizeof(TcgPcrEvent2->EventSize) + EventSize + - sizeof(TDX_EVENT)); + sizeof(TcgPcrEvent2->EventSize) + EventSize); if (EventHobData == NULL) { return EFI_OUT_OF_RESOURCES; @@ -101,16 +114,15 @@ CreateTdxExtendEvent ( CopyMem(Ptr, &EventType, sizeof(TCG_EVENTTYPE)); Ptr += sizeof(TCG_EVENTTYPE); - // - // We don't have a digest to copy yet, but we can to copy the eventsize/data now - // DigestBuffer = Ptr; DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - digest %p\n", DigestBuffer)); TdxDigest = (TDX_DIGEST_VALUE *)DigestBuffer; TdxDigest->count = 1; TdxDigest->hashAlg = TPM_ALG_SHA384; - ZeroMem(TdxDigest->sha384, SHA384_DIGEST_SIZE); + CopyMem (TdxDigest->sha384, + DigestList.digests[0].digest.sha384, + SHA384_DIGEST_SIZE); Ptr += sizeof(TDX_DIGEST_VALUE); DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - eventdata %p\n", DigestBuffer)); @@ -119,16 +131,6 @@ CreateTdxExtendEvent ( Ptr += sizeof(UINT32); CopyMem (Ptr, EventData, EventSize); Ptr += EventSize; - TdxEvent = (TDX_EVENT *)Ptr; - - // - // Initialize the TdxEvent so we can perform measurement in DXE. - // During early DXE, the gTcgEvent2EntryHobGuid will be parsed, the data hashed, and TcgEvent2 hobs - // updated with the updated hash - // - TdxEvent->Signature = SIGNATURE_32('T', 'D', 'E', 'T'); - TdxEvent->HashDataPtr = (UINT64)(UINTN)HashData; - TdxEvent->HashDataLen = (UINT64)HashDataLen; Status = EFI_SUCCESS; return Status; diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf index b76411d92759..e07494315413 100644 --- a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf @@ -60,6 +60,7 @@ MemoryAllocationLib TdvfPlatformLib PrePiLib + HashLib [Guids] gEfiHobMemoryAllocModuleGuid diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 41491e21c5ea..34559c7e5da4 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -385,16 +385,6 @@ ## This PCD records LASA field in TDX EVENTLOG ACPI table. gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLasa|0|UINT64|0x104 - ## This PCD indicates Hash mask for TDX 1.0 Bit definition strictly follows - # TCG Algorithm Registry. - # TDX 1.0 supports SHA384 only. - # BIT2 - SHA384.
- gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashMask|0x00000004|UINT32|0x105 - - ## This PCD indicated final BIOS supported Hash mask. - # Bios may choose to register a subset of PcdTdxHashMask. - # So this PCD is final value of how many hash algo is extended to RTMR. - gUefiOvmfPkgTokenSpaceGuid.PcdTdxHashAlgorithmBitmap|0x4|UINT32|0x106 [PcdsFeatureFlag] gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index d5d78e7cde6b..2d9e3d9a929b 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -294,6 +294,7 @@ TdvfPlatformLib|OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf PrePiLib|OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf [LibraryClasses.common.PEI_CORE] HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf @@ -731,6 +732,8 @@ OvmfPkg/Sec/SecMain.inf { NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf + HashLib|OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf + NULL|SecurityPkg/Library/HashInstanceLibSha384/HashInstanceLibSha384.inf } # @@ -1086,8 +1089,7 @@ # OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf { - #TdxLib|MdePkg/Library/TdxLib/TdxLib.inf - HashLib|OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterDxe.inf + HashLib|OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf NULL|SecurityPkg/Library/HashInstanceLibSha384/HashInstanceLibSha384.inf } diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c index b22a5744f31d..d3a3e8770af4 100644 --- a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c @@ -2374,8 +2374,6 @@ SyncTdTcgEvent() UINT32 DigestListBinSize; UINT8 *Event; UINT32 EventSize; - TDX_EVENT *TdxEvent; - TPML_DIGEST_VALUES DigestList; DEBUG ((DEBUG_INFO, "Sync Tdx event from SEC\n")); @@ -2396,21 +2394,6 @@ SyncTdTcgEvent() // EventSize = *(UINT32*)((UINT8 *) DigestListBin + DigestListBinSize); Event = (UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32); - // - // Hash and extend the data from SEC - // - TdxEvent = (TDX_EVENT*)((UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32) + EventSize); - Status = HashAndExtend (*(UINT32*)(TcgEvent), - (VOID*)(UINTN)TdxEvent->HashDataPtr, - TdxEvent->HashDataLen, - &DigestList); - - // - // Copy the hash data to TcgEvent - // - CopyMem ((UINT8*)DigestListBin + sizeof (UINT32) + sizeof (TPMI_ALG_HASH), - DigestList.digests[0].digest.sha384, - SHA384_DIGEST_SIZE); // // Log the event @@ -2573,8 +2556,10 @@ DriverEntry ( Status = SetupEventLog (); ASSERT_EFI_ERROR (Status); - Status = SyncTdTcgEvent(); - ASSERT_EFI_ERROR (Status); + if (!EFI_ERROR (Status)) { + Status = SyncTdTcgEvent(); + ASSERT_EFI_ERROR (Status); + } // // Measure handoff tables, Boot#### variables etc. From 2f5ffb4ffb9706fd64908bfb3d8bfa67eb0d2bb6 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Tue, 20 Apr 2021 16:26:14 +0800 Subject: [PATCH 51/54] OvmfPkg: Add PcdTdxAcceptPageSize The PCD of PcdTdxAcceptPageSize is used to control the page accept size in Td guest. The valid value is 0x1000, 0x200000 and 0x40000000, i.e. 4k/2M/1G. --- OvmfPkg/Library/TdxStartupLib/Mp.c | 18 ++++++++++-------- .../Library/TdxStartupLib/TdxStartupLib.inf | 15 ++++++++------- OvmfPkg/OvmfPkg.dec | 6 +++++- OvmfPkg/OvmfPkgX64.dsc | 6 ++++-- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/OvmfPkg/Library/TdxStartupLib/Mp.c b/OvmfPkg/Library/TdxStartupLib/Mp.c index 0ce1800c9a6d..41f11e9bcf49 100644 --- a/OvmfPkg/Library/TdxStartupLib/Mp.c +++ b/OvmfPkg/Library/TdxStartupLib/Mp.c @@ -101,12 +101,14 @@ MpAcceptMemoryResourceRange ( ) { UINT64 Pages; - EFI_PHYSICAL_ADDRESS Stride; - EFI_PHYSICAL_ADDRESS AcceptSize; + UINT64 Stride; + UINT64 AcceptChunkSize; + UINT64 AcceptPageSize; volatile MP_WAKEUP_MAILBOX *MailBox; MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); - AcceptSize = FixedPcdGet64(PcdTdxAcceptPageChunkSize); + AcceptChunkSize = FixedPcdGet64(PcdTdxAcceptChunkSize); + AcceptPageSize = FixedPcdGet64(PcdTdxAcceptPageSize); MpSerializeStart(); @@ -114,8 +116,8 @@ MpAcceptMemoryResourceRange ( 0, PhysicalAddress, PhysicalEnd, - AcceptSize, - EFI_PAGE_SIZE); + AcceptChunkSize, + AcceptPageSize); // // All cpus share the burden of accepting the pages @@ -123,7 +125,7 @@ MpAcceptMemoryResourceRange ( // and then skip pass range the other cpus do // Stride is the amount of skip // - Stride = GetNumCpus() * AcceptSize; + Stride = GetNumCpus() * AcceptChunkSize; // // Keep accepting until end of resource // @@ -131,11 +133,11 @@ MpAcceptMemoryResourceRange ( // // Decrease size of near end of resource if needed. // - Pages = RShiftU64(MIN(AcceptSize, PhysicalEnd - PhysicalAddress), EFI_PAGE_SHIFT); + Pages = RShiftU64(MIN(AcceptChunkSize, PhysicalEnd - PhysicalAddress), EFI_PAGE_SHIFT); MailBox->Tallies[0] += (UINT32)Pages; - TdAcceptPages ( PhysicalAddress, Pages, EFI_PAGE_SIZE); + TdAcceptPages ( PhysicalAddress, Pages, AcceptPageSize); // // Bump address to next chunk this cpu is responisble for // diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf index e07494315413..e8a725659eb3 100644 --- a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf @@ -84,14 +84,15 @@ gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation - gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize - gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack - gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack + gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask - // - // TODO check these PCDs' impact on Ovmf - // - gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack ## CONSUMES + // + // TODO check these PCDs' impact on Ovmf + // + gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplBuildPageTables ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 34559c7e5da4..0c7088a3383e 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -338,10 +338,14 @@ gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxMsr|TRUE|BOOLEAN|0x56 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x58 - gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize|0x2000|UINT64|0x59 + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize|0x2000|UINT64|0x59 gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack|FALSE|BOOLEAN|0x5b gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask|0|UINT64|0x5c + ## The Tdx accept page size. 0x1000,0x200000 + # + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize|0x1000|UINT64|0x5d + [PcdsDynamic, PcdsDynamicEx] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2 diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 2d9e3d9a929b..7b2fcb8d66af 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -605,8 +605,10 @@ !if $(TDX_IGNORE_VE_HLT) == TRUE gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|TRUE !endif - # 32M - gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageChunkSize|0x2000000 + # Accept chunk size - 32M + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize|0x2000000 + # Accept page size - 4k + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize|0x1000 # Noexec settings for DXE. # TDX doesn't allow us to change EFER so make sure these are disabled From d603c5db0ef42a5892d200e78db10af17978d3fd Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 21 Apr 2021 10:46:29 +0800 Subject: [PATCH 52/54] OvmfPkg: MADT force edge trigger mode TDX doesn't support eoi intercept because cpu state is protected and the way to inject interrupt into vcpu is only posted interrupt. It implies level trigger interrupt can't be emulated and only edge trigger is supported. So forcibly report edge trigger for all legacy interrupt via interrupt source override entry in MADT. Signed-off-by: Isaku Yamahata --- OvmfPkg/AcpiPlatformDxe/Qemu.c | 53 +++++----------------------------- 1 file changed, 7 insertions(+), 46 deletions(-) diff --git a/OvmfPkg/AcpiPlatformDxe/Qemu.c b/OvmfPkg/AcpiPlatformDxe/Qemu.c index 027bfa4cdd7c..3e1da8ef1037 100644 --- a/OvmfPkg/AcpiPlatformDxe/Qemu.c +++ b/OvmfPkg/AcpiPlatformDxe/Qemu.c @@ -34,28 +34,6 @@ QemuDetected ( return TRUE; } - -STATIC -UINTN -CountBits16 ( - UINT16 Mask - ) -{ - // - // For all N >= 1, N bits are enough to represent the number of bits set - // among N bits. It's true for N == 1. When adding a new bit (N := N+1), - // the maximum number of possibly set bits increases by one, while the - // representable maximum doubles. - // - Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555); - Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333); - Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F); - Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF); - - return Mask; -} - - STATIC EFI_STATUS EFIAPI @@ -67,7 +45,6 @@ QemuInstallAcpiMadtTable ( ) { UINTN CpuCount; - UINTN PciLinkIsoCount; UINTN NewBufferSize; EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt; EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic; @@ -85,17 +62,11 @@ QemuInstallAcpiMadtTable ( CpuCount = QemuFwCfgRead16 (); ASSERT (CpuCount >= 1); - // - // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset - // corresponds to the union of all possible interrupt assignments for the LNKA, - // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT. - // - PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel)); - +#define NUM_8259_IRQS 16 NewBufferSize = 1 * sizeof (*Madt) + CpuCount * sizeof (*LocalApic) + 1 * sizeof (*IoApic) + - (1 + PciLinkIsoCount) * sizeof (*Iso) + + NUM_8259_IRQS * sizeof (*Iso) + 1 * sizeof (*LocalApicNmi); NewBufferSize += sizeof(ACPI_MADT_MPWK_STRUCT); @@ -137,31 +108,21 @@ QemuInstallAcpiMadtTable ( Iso = Ptr; Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE; Iso->Length = sizeof (*Iso); - Iso->Bus = 0x00; // ISA - Iso->Source = 0x00; // IRQ0 + Iso->Bus = 0x00; // ISA + Iso->Source = 0x00; // IRQ0 Iso->GlobalSystemInterruptVector = 0x00000002; - Iso->Flags = 0x0000; // Conforms to specs of the bus + Iso->Flags = 0x0005; // Edge-triggered, Active High ++Iso; - // - // Set Level-triggered, Active High for all possible PCI link targets. - // - for (Loop = 0; Loop < 16; ++Loop) { - if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) { - continue; - } + for (Loop = 1; Loop < NUM_8259_IRQS; ++Loop) { Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE; Iso->Length = sizeof (*Iso); Iso->Bus = 0x00; // ISA Iso->Source = (UINT8) Loop; Iso->GlobalSystemInterruptVector = (UINT32) Loop; - Iso->Flags = 0x000D; // Level-triggered, Active High + Iso->Flags = 0x0005; // Edge-triggered, Active High ++Iso; } - ASSERT ( - (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) == - 1 + PciLinkIsoCount - ); Ptr = Iso; LocalApicNmi = Ptr; From b782518d2bd1824ee680f75c63e2a641675e0c52 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 22 Apr 2021 15:05:44 +0800 Subject: [PATCH 53/54] OvmfPkg: Support 2M page accept size 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. --- OvmfPkg/Library/TdxStartupLib/Mp.c | 201 ++++++++++++++++++++++++----- 1 file changed, 171 insertions(+), 30 deletions(-) diff --git a/OvmfPkg/Library/TdxStartupLib/Mp.c b/OvmfPkg/Library/TdxStartupLib/Mp.c index 41f11e9bcf49..01ede34bb4ac 100644 --- a/OvmfPkg/Library/TdxStartupLib/Mp.c +++ b/OvmfPkg/Library/TdxStartupLib/Mp.c @@ -16,6 +16,8 @@ #include #include "TdxStartupInternal.h" +#define ALIGNED_2MB_MASK 0x1fffff + /** This function will be called by BSP to wakeup APs the are spinning on mailbox in protected mode @@ -93,56 +95,195 @@ MpSerializeEnd ( InterlockedDecrement ((UINT32 *) &MailBox->NumCpusExiting); } -VOID +EFI_STATUS EFIAPI -MpAcceptMemoryResourceRange ( - IN EFI_PHYSICAL_ADDRESS PhysicalAddress, - IN EFI_PHYSICAL_ADDRESS PhysicalEnd +BspAcceptMemoryResourceRange ( + IN EFI_PHYSICAL_ADDRESS StartAddress, + IN UINT64 Length, + IN UINT64 AcceptChunkSize, + IN UINT64 AcceptPageSize ) { + EFI_STATUS Status; UINT64 Pages; UINT64 Stride; - UINT64 AcceptChunkSize; - UINT64 AcceptPageSize; + EFI_PHYSICAL_ADDRESS PhysicalAddress; volatile MP_WAKEUP_MAILBOX *MailBox; + Status = EFI_SUCCESS; + PhysicalAddress = StartAddress; + Stride = GetNumCpus() * AcceptChunkSize; MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); - AcceptChunkSize = FixedPcdGet64(PcdTdxAcceptChunkSize); - AcceptPageSize = FixedPcdGet64(PcdTdxAcceptPageSize); - - MpSerializeStart(); - MpSendWakeupCommand(MpProtectedModeWakeupCommandAcceptPages, - 0, - PhysicalAddress, - PhysicalEnd, - AcceptChunkSize, - AcceptPageSize); - - // - // All cpus share the burden of accepting the pages - // A cpu will accept AcceptSize size amount of memory - // and then skip pass range the other cpus do - // Stride is the amount of skip - // - Stride = GetNumCpus() * AcceptChunkSize; - // - // Keep accepting until end of resource - // - while (PhysicalAddress < PhysicalEnd) { + while (!EFI_ERROR(Status) && PhysicalAddress < StartAddress + Length) { // // Decrease size of near end of resource if needed. // - Pages = RShiftU64(MIN(AcceptChunkSize, PhysicalEnd - PhysicalAddress), EFI_PAGE_SHIFT); + Pages = RShiftU64(MIN(AcceptChunkSize, Length), EFI_PAGE_SHIFT); MailBox->Tallies[0] += (UINT32)Pages; - TdAcceptPages ( PhysicalAddress, Pages, AcceptPageSize); + 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 +**/ +VOID +EFIAPI +MpAcceptMemoryResourceRange ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN EFI_PHYSICAL_ADDRESS PhysicalEnd + ) +{ + EFI_STATUS Status; + UINT64 AcceptChunkSize; + UINT64 AcceptPageSize; + UINT64 StartAddress1; + UINT64 StartAddress2; + UINT64 StartAddress3; + UINT64 TotalLength; + UINT64 Length1; + UINT64 Length2; + UINT64 Length3; + volatile MP_WAKEUP_MAILBOX *MailBox; + + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox (); + AcceptChunkSize = FixedPcdGet64 (PcdTdxAcceptChunkSize); + AcceptPageSize = FixedPcdGet64 (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; + } 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 & 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) & ALIGNED_2MB_MASK; + StartAddress3 = StartAddress2 + Length2; + Length3 = TotalLength - Length1 - Length2; + 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)); + + 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(); DEBUG((DEBUG_INFO, "Tallies %x %x %x %x %x %x %x %x\n", From 96ddbad5725d25ea95fe9b28df40c0095441ad8a Mon Sep 17 00:00:00 2001 From: Min Xu Date: Mon, 26 Apr 2021 16:29:41 +0800 Subject: [PATCH 54/54] OvmfPkg: Delete the PCD for Td temp Stack/Heap --- OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf | 2 -- OvmfPkg/OvmfPkg.dec | 4 ---- OvmfPkg/ResetVector/ResetVector.inf | 4 ---- OvmfPkg/Sec/SecMain.inf | 4 ---- 4 files changed, 14 deletions(-) diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf index e8a725659eb3..02eb185ee3b5 100644 --- a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf @@ -75,8 +75,6 @@ gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 0c7088a3383e..62031ed182ea 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -319,10 +319,6 @@ gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize|0|UINT32|0x47 gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase|0|UINT32|0x48 gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize|0|UINT32|0x49 - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase|0|UINT32|0x4a - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize|0|UINT32|0x4b - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapBase|0|UINT32|0x4c - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapSize|0|UINT32|0x4d gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase|0|UINT32|0x4e gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset|0|UINT32|0x4f diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf index d161b9c689c2..0309295a0831 100644 --- a/OvmfPkg/ResetVector/ResetVector.inf +++ b/OvmfPkg/ResetVector/ResetVector.inf @@ -48,10 +48,6 @@ gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapBase - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapSize gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf index 7474eea1993c..b0bb36a66d98 100644 --- a/OvmfPkg/Sec/SecMain.inf +++ b/OvmfPkg/Sec/SecMain.inf @@ -73,10 +73,6 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdInitValueInTempStack gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackBase - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempStackSize - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapBase - gUefiOvmfPkgTokenSpaceGuid.PcdTdTempHeapSize gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize