* [PATCH V3 01/10] OvmfPkg: Add Tdx BFV/CFV PCDs and PcdOvmfImageSizeInKb
[not found] <cover.1627364332.git.min.m.xu@intel.com>
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 02/10] OvmfPkg: Add Tdx metadata Min Xu
` (8 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Tdx Virtual Firmware (TDVF) includes one Firmware Volume (FV) known
as the Boot Firmware Volume (BFV). The FV format is defined in the
UEFI Platform Initialization (PI) spec. BFV includes all TDVF components
required during boot.
TDVF also include a configuration firmware volume (CFV) that is separated
from the BFV. The reason is because the CFV is measured in RTMR, while
the BFV is measured in MRTD.
In practice BFV is the code part of Ovmf image. CFV is the vars part of
Ovmf image (exclude the SPARE part).
PcdOvmfImageSizeInKb is added which is used to calculate the offset
of TdxMetadata in ResetVectorVtf0.asm.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/OvmfPkg.dec | 13 +++++++++++++
OvmfPkg/OvmfPkgDefines.fdf.inc | 12 +++++++++++-
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 6ae733f6e39f..6d9bb91e9274 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -321,6 +321,19 @@
gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
+ ## The base address and size of the TDX Cfv base and size.
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase|0|UINT32|0x47
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset|0|UINT32|0x48
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize|0|UINT32|0x49
+
+ ## The base address and size of the TDX Bfv base and size.
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase|0|UINT32|0x4a
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset|0|UINT32|0x4b
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize|0|UINT32|0x4c
+
+ ## Size of the Ovmf image in KB
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x4d
+
[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..401e491e4cbe 100644
--- a/OvmfPkg/OvmfPkgDefines.fdf.inc
+++ b/OvmfPkg/OvmfPkgDefines.fdf.inc
@@ -2,13 +2,14 @@
# FDF include file that defines the main macros and sets the dependent PCDs.
#
# Copyright (C) 2014, Red Hat, Inc.
-# Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
DEFINE BLOCK_SIZE = 0x1000
+DEFINE VARS_OFFSET = 0
#
# A firmware binary built with FD_SIZE_IN_KB=1024, and a firmware binary built
@@ -66,6 +67,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 +84,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
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 02/10] OvmfPkg: Add Tdx metadata
[not found] <cover.1627364332.git.min.m.xu@intel.com>
2021-07-27 5:42 ` [PATCH V3 01/10] OvmfPkg: Add Tdx BFV/CFV PCDs and PcdOvmfImageSizeInKb Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 03/10] OvmfPkg: Set TdMailbox initial value and macros Min Xu
` (7 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Tdx Metadata describes the information about the image for VMM use.
For example, the base address and length of the TdHob, TdMailbox, etc.
Its offset is put in a GUID-ed structure which is appended in the GUID-ed
chain from a fixed GPA (0xffffffd0). Below are the items in TdxMetadata:
_Bfv:
Boot Firmware Volume
_Cfv:
Configuration Firmware Volume
_Stack:
Initial stack
_Heap:
Initial heap
_MailBox:
TDVF reserves the memory region so each AP can receive the message
sent by the guest OS.
_TdHob:
VMM pass the resource information in TdHob to TDVF.
_TdxPageTable:
If 5-level page table is supported (GPAW is 52), a top level page
directory pointers (1 * 256TB entry) is generated in this page.
_OvmfPageTable:
Initial page table for standard Ovmf.
TDVF indicate above chunk of temporary initialized memory region (_Stack/
_Heap/_MailBox/_TdHob/_TdxPageTables/OvmfPageTable) to support TDVF code
finishing the memory initialization. Because the other unaccepted memory
cannot be accessed until they're accepted.
Since AMD SEV has already defined some SEV specific memory region in
MEMFD. SEV and TDX will not run at the same time. So TDX re-use the
memory region defined by SEV.
- MailBox : PcdOvmfSecGhcbBackupBase|PcdOvmfSecGhcbBackupSize
- TdHob : PcdOvmfSecGhcbBase|PcdOvmfSecGhcbSize
- TdxPageTable : PcdOvmfSecGhcbPageTableBase|PcdOvmfSecGhcbPageTableSize
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 17 ++++
OvmfPkg/ResetVector/ResetVector.inf | 11 ++-
OvmfPkg/ResetVector/ResetVector.nasmb | 47 +++++++++-
OvmfPkg/ResetVector/X64/TdxMetadata.asm | 97 ++++++++++++++++++++
4 files changed, 169 insertions(+), 3 deletions(-)
create mode 100644 OvmfPkg/ResetVector/X64/TdxMetadata.asm
diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
index 9c0b5853a46f..ac86ce69ebe8 100644
--- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
+++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
@@ -47,6 +47,23 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart + 15) % 16)) DB 0
;
guidedStructureStart:
+%ifdef ARCH_X64
+;
+; TDX Metadata offset block
+;
+; If TdxMetadata.asm is included then we need below block which describes
+; the offset of TdxMetadata block in Ovmf image
+;
+; GUID : e47a6535-984a-4798-865e-4685a7bf8ec2
+;
+tdxMetadataOffsetStart:
+ DD (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16))
+ DD tdxMetadataOffsetEnd - tdxMetadataOffsetStart
+ DB 0x35, 0x65, 0x7a, 0xe4, 0x4a, 0x98, 0x98, 0x47
+ DB 0x86, 0x5e, 0x46, 0x85, 0xa7, 0xbf, 0x8e, 0xc2
+tdxMetadataOffsetEnd:
+
+%endif
;
; SEV Secret block
;
diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index dc38f68919cd..fd65c0c9621d 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.<BR>
+# Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -43,6 +43,15 @@
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize
[FixedPcd]
gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 5fbacaed5f9d..b653fe87abd6 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -4,6 +4,7 @@
;
; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
; Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
;------------------------------------------------------------------------------
@@ -67,6 +68,44 @@
%error "This implementation inherently depends on PcdOvmfSecGhcbBase not straddling a 2MB boundary"
%endif
+ ;
+ ; 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_HEAP_MEMORY_BASE FixedPcdGet32 (PcdOvmfSecPeiTempRamBase)
+ %define TDX_HEAP_MEMORY_SIZE FixedPcdGet32 (PcdOvmfSecPeiTempRamSize) / 2
+
+ %define TDX_STACK_MEMORY_BASE (TDX_HEAP_MEMORY_BASE + TDX_HEAP_MEMORY_SIZE)
+ %define TDX_STACK_MEMORY_SIZE FixedPcdGet32 (PcdOvmfSecPeiTempRamSize) / 2
+
+ %define TDX_HOB_MEMORY_BASE FixedPcdGet32 (PcdOvmfSecGhcbBase)
+ %define TDX_HOB_MEMORY_SIZE FixedPcdGet32 (PcdOvmfSecGhcbSize)
+
+ %define TDX_MAILBOX_MEMORY_BASE FixedPcdGet32 (PcdOvmfSecGhcbBackupBase)
+ %define TDX_MAILBOX_MEMORY_SIZE FixedPcdGet32 (PcdOvmfSecGhcbBackupSize)
+
+ %define OVMF_PAGE_TABLE_BASE FixedPcdGet32 (PcdOvmfSecPageTablesBase)
+ %define OVMF_PAGE_TABLE_SIZE FixedPcdGet32 (PcdOvmfSecPageTablesSize)
+
+ %define TDX_EXTRA_PAGE_TABLE_BASE FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase)
+ %define TDX_EXTRA_PAGE_TABLE_SIZE FixedPcdGet32 (PcdOvmfSecGhcbPageTableSize)
+
%define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
%define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
@@ -76,8 +115,11 @@
%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"
+
+ %include "X64/TdxMetadata.asm"
+
+ %include "Ia32/Flat32ToFlat64.asm"
+ %include "Ia32/PageTables64.asm"
%endif
%include "Ia16/Real16ToFlat32.asm"
@@ -88,5 +130,6 @@
%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/ResetVectorVtf0.asm"
diff --git a/OvmfPkg/ResetVector/X64/TdxMetadata.asm b/OvmfPkg/ResetVector/X64/TdxMetadata.asm
new file mode 100644
index 000000000000..8dba8daa0165
--- /dev/null
+++ b/OvmfPkg/ResetVector/X64/TdxMetadata.asm
@@ -0,0 +1,97 @@
+;------------------------------------------------------------------------------
+; @file
+; Tdx Virtual Firmware metadata
+;
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+BITS 64
+
+%define TDX_VIRTUAL_FIRMWARE
+
+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
+
+_MailBox:
+ DD 0
+ DD 0
+ DQ TDX_MAILBOX_MEMORY_BASE
+ DQ TDX_MAILBOX_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
+
+_TdxPageTable:
+ DD 0
+ DD 0
+ DQ TDX_EXTRA_PAGE_TABLE_BASE
+ DQ TDX_EXTRA_PAGE_TABLE_SIZE
+ DD TDX_METADATA_SECTION_TYPE_TEMP_MEM
+ DD 0
+
+_OvmfPageTable:
+ DD 0
+ DD 0
+ DQ OVMF_PAGE_TABLE_BASE
+ DQ OVMF_PAGE_TABLE_SIZE
+ DD TDX_METADATA_SECTION_TYPE_TEMP_MEM
+ DD 0
+
+TdxGuidedStructureEnd:
+ALIGN 16
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 03/10] OvmfPkg: Set TdMailbox initial value and macros
[not found] <cover.1627364332.git.min.m.xu@intel.com>
2021-07-27 5:42 ` [PATCH V3 01/10] OvmfPkg: Add Tdx BFV/CFV PCDs and PcdOvmfImageSizeInKb Min Xu
2021-07-27 5:42 ` [PATCH V3 02/10] OvmfPkg: Add Tdx metadata Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 04/10] OvmfPkg: Add TDX_PT_ADDR defition in ResetVector.nasmb Min Xu
` (6 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In Tdx the memory region defined by PcdOvmfSecGhcbBackupBase is used as
TdMailbox. It is initialized to all-0 by host VMM. Piece of the memory
region TdMailbox[0x10, 0x20] is used as TDX_WORK_AREA. In this area a
flag 'TDXG' is set so that the following code can check if it is
Tdx guest.
So in Non-Tdx guest, this memory region should be initialized to all-0 in
the definition of MEMFD.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/OvmfPkgX64.fdf | 6 ++++++
OvmfPkg/ResetVector/ResetVector.nasmb | 12 ++++++++++++
2 files changed, 18 insertions(+)
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 5fa8c0895808..c587d1412803 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -87,6 +87,12 @@ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevE
0x00C000|0x001000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
+DATA = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 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
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index b653fe87abd6..42b4a3791d29 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -106,6 +106,18 @@
%define TDX_EXTRA_PAGE_TABLE_BASE FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase)
%define TDX_EXTRA_PAGE_TABLE_SIZE FixedPcdGet32 (PcdOvmfSecGhcbPageTableSize)
+ ;
+ ; TdMailboxBase [0x10, 0x800] is reserved for OS.
+ ; Td guest initialize piece of this area (TdMailboxBase [0x10,0x20]) to
+ ; record the Td guest info so that this information can be used in the
+ ; following ResetVector flow.
+ ;
+ %define TD_MAILBOX_WORKAREA_OFFSET 0x10
+ %define TDX_WORK_AREA (TDX_MAILBOX_MEMORY_BASE + TD_MAILBOX_WORKAREA_OFFSET)
+ %define TDX_WORK_AREA_PAGELEVEL5 (TDX_WORK_AREA + 4)
+ %define TDX_WORK_AREA_PGTBL_READY (TDX_WORK_AREA + 5)
+ %define TDX_WORK_AREA_INITVP (TDX_WORK_AREA + 8)
+ %define TDX_WORK_AREA_INFO (TDX_WORK_AREA + 8 + 4)
%define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
%define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 04/10] OvmfPkg: Add TDX_PT_ADDR defition in ResetVector.nasmb
[not found] <cover.1627364332.git.min.m.xu@intel.com>
` (2 preceding siblings ...)
2021-07-27 5:42 ` [PATCH V3 03/10] OvmfPkg: Set TdMailbox initial value and macros Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 05/10] OvmfPkg: Add IntelTdx.asm in ResetVector Min Xu
` (5 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Tdx support 4-level paging or 5-level paging based on the GPAW. If
5-level page table is supported (GPAW is 52), a top level page directory
pointers (1 * 256TB entry) is generated in the memory region defined by
PcdOvmfSecPageTablesBase.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/ResetVector.nasmb | 3 +++
1 file changed, 3 insertions(+)
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 42b4a3791d29..0ac6d7a6fd33 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -118,6 +118,9 @@
%define TDX_WORK_AREA_PGTBL_READY (TDX_WORK_AREA + 5)
%define TDX_WORK_AREA_INITVP (TDX_WORK_AREA + 8)
%define TDX_WORK_AREA_INFO (TDX_WORK_AREA + 8 + 4)
+
+ %define TDX_PT_ADDR(Offset) (TDX_EXTRA_PAGE_TABLE_BASE + (Offset))
+
%define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
%define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 05/10] OvmfPkg: Add IntelTdx.asm in ResetVector
[not found] <cover.1627364332.git.min.m.xu@intel.com>
` (3 preceding siblings ...)
2021-07-27 5:42 ` [PATCH V3 04/10] OvmfPkg: Add TDX_PT_ADDR defition in ResetVector.nasmb Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm " Min Xu
` (4 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
IntelTdx.asm includes below routines used in ResetVector
- IsTdx
Check if the running system is Tdx guest.
- InitTdx
This is the initialization code for Tdx guest. It sets TDX_WORK_AREA
so that it can be used in later code. Also APs will spin to check
if the PageTable has been built by BSP. If the PageTables is ready,
APs continues.
- PostSetCr3PageTables64Tdx
It is called after SetCr3PageTables64 in Tdx guest to set CR0/CR4.
If GPAW is 52, then CR3 is adjusted as well.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/Ia32/IntelTdx.asm | 172 ++++++++++++++++++++++++++
1 file changed, 172 insertions(+)
create mode 100644 OvmfPkg/ResetVector/Ia32/IntelTdx.asm
diff --git a/OvmfPkg/ResetVector/Ia32/IntelTdx.asm b/OvmfPkg/ResetVector/Ia32/IntelTdx.asm
new file mode 100644
index 000000000000..4a00059a47a7
--- /dev/null
+++ b/OvmfPkg/ResetVector/Ia32/IntelTdx.asm
@@ -0,0 +1,172 @@
+;------------------------------------------------------------------------------
+; @file
+; Intel TDX routines
+;
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+BITS 32
+
+;
+; Check if it is Intel Tdx
+;
+; Modified: EAX, EBX, ECX, EDX
+;
+; If it is Intel Tdx, EAX is zero
+; If it is not Intel Tdx, EAX is non-zero
+;
+IsTdx:
+ ;
+ ; CPUID (0)
+ ;
+ mov eax, 0
+ cpuid
+ cmp ebx, 0x756e6547 ; "Genu"
+ jne IsNotTdx
+ cmp edx, 0x49656e69 ; "ineI"
+ jne IsNotTdx
+ cmp ecx, 0x6c65746e ; "ntel"
+ jne IsNotTdx
+
+ ;
+ ; CPUID (1)
+ ;
+ mov eax, 1
+ cpuid
+ test ecx, 0x80000000
+ jz IsNotTdx
+
+ ;
+ ; CPUID[0].EAX >= 0x21?
+ ;
+ mov eax, 0
+ cpuid
+ cmp eax, 0x21
+ jl IsNotTdx
+
+ ;
+ ; CPUID (0x21,0)
+ ;
+ mov eax, 0x21
+ mov ecx, 0
+ cpuid
+
+ cmp ebx, 0x65746E49 ; "Inte"
+ jne IsNotTdx
+ cmp edx, 0x5844546C ; "lTDX"
+ jne IsNotTdx
+ cmp ecx, 0x20202020 ; " "
+ jne IsNotTdx
+
+ mov eax, 0
+ jmp ExitIsTdx
+
+IsNotTdx:
+ mov eax, 1
+
+ExitIsTdx:
+
+ OneTimeCallRet IsTdx
+
+;
+; Initialization code if it is Tdx guest.
+; If it is Tdx guest, EBP[6:0] holds CPU supported GPAW, ESI[31:0] is the vCPU ID
+;
+; Modified: EBP
+;
+InitTdx:
+
+ ;
+ ; First check if it is Tdx
+ ;
+ OneTimeCall IsTdx
+
+ test eax, eax
+ jnz ExitInitTdx
+
+ ;
+ ; In Td guest, BSP/AP shares the same entry point
+ ; BSP builds up the page table, while APs shouldn't do the same task.
+ ; Instead, APs just leverage the page table which is built by BSP.
+ ; APs will wait until the page table is ready.
+ ; In Td guest, vCPU 0 is treated as the BSP, the others are APs.
+ ; ESI indicates the vCPU ID.
+ ;
+ cmp esi, 0
+ je TdBspEntry
+
+ApWait:
+ cmp byte[TDX_WORK_AREA_PGTBL_READY], 0
+ je ApWait
+ jmp ExitInitTdx
+
+TdBspEntry:
+ ;
+ ; It is of Tdx Guest
+ ; Save the Tdx info in TDX_WORK_AREA so that the following code can use
+ ; these information.
+ ;
+ mov dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+
+ ;
+ ; EBP[6:0] CPU supported GPA width
+ ;
+ and ebp, 0x3f
+ cmp ebp, 52
+ jl NotPageLevel5
+ mov byte[TDX_WORK_AREA_PAGELEVEL5], 1
+
+NotPageLevel5:
+ mov DWORD[TDX_WORK_AREA_INFO], ebp
+
+ExitInitTdx:
+ OneTimeCallRet InitTdx
+
+;
+; Called after SetCr3PageTables64 in Tdx guest to set CR0/CR4.
+; If GPAW is 52, then CR3 is adjusted as well.
+;
+; Modified: EAX, EBX, CR0, CR3, CR4
+;
+PostSetCr3PageTables64Tdx:
+ ;
+ ; TDX_WORK_AREA was set in InitTdx if it is Tdx guest
+ ;
+ cmp dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+ jnz ExitPostSetCr3PageTables64Tdx
+
+ 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 SetCr4
+ bts eax, 12
+
+SetCr4:
+ mov cr4, eax
+ mov ebx, cr3
+
+ ;
+ ; if la57 is not set, we are ok
+ ; if using 5-level paging, adjust top-level page directory
+ ;
+ bt eax, 12
+ jnc TdxSetCr3
+ mov ebx, TDX_PT_ADDR (0)
+
+TdxSetCr3:
+ mov cr3, ebx
+
+ mov eax, cr0
+ bts eax, 31 ; set PG
+ mov cr0, eax ; enable paging
+
+ExitPostSetCr3PageTables64Tdx:
+ OneTimeCallRet PostSetCr3PageTables64Tdx
+
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
[not found] <cover.1627364332.git.min.m.xu@intel.com>
` (4 preceding siblings ...)
2021-07-27 5:42 ` [PATCH V3 05/10] OvmfPkg: Add IntelTdx.asm in ResetVector Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 10:56 ` Brijesh Singh
2021-07-27 5:42 ` [PATCH V3 07/10] OvmfPkg: Add ReloadFlat32 Min Xu
` (3 subsequent siblings)
9 siblings, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
AmdSev.asm includes below routines:
- CheckSevFeatures
Check if Secure Encrypted Virtualization (SEV) features are enabled.
- PreSetCr3ForPageTables64Sev
It is called before SetCr3ForPageTables64 in SEV guests.
- PostSetCr3PageTables64Sev
It is called after SetCr3PageTables64 in SEV guests.
- PostJump64BitAndLandHereSev
It is called after Jump64BitAndLandHere in SEV guests.
- #VC exception handling routines
These routines are extracted from PageTables64.asm and Flat32ToFlat64.asm
Need AMD engineers' help to review/validate the patch so that there is
no regression. Thanks in advance!
Note:
In above Pre/Post routines, dword[TDX_WORK_AREA] should be checked
to see if it is 'TDXG' (Tdx guests). This is because some memory region
for example, byte[SEV_ES_WORK_AREA] cannot be accessed in Tdx guests.
Tdx requires that any memory region to be accessed should be accepted
first or initialized by host VMM before Td guest is launched.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/Ia32/AmdSev.asm | 526 ++++++++++++++++++++++++++++
1 file changed, 526 insertions(+)
create mode 100644 OvmfPkg/ResetVector/Ia32/AmdSev.asm
diff --git a/OvmfPkg/ResetVector/Ia32/AmdSev.asm b/OvmfPkg/ResetVector/Ia32/AmdSev.asm
new file mode 100644
index 000000000000..962b7e169c61
--- /dev/null
+++ b/OvmfPkg/ResetVector/Ia32/AmdSev.asm
@@ -0,0 +1,526 @@
+;------------------------------------------------------------------------------
+; @file
+; AMD SEV routines
+;
+; Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+;
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+
+BITS 32
+;
+; SEV-ES #VC exception handler support
+;
+; #VC handler local variable locations
+;
+%define VC_CPUID_RESULT_EAX 0
+%define VC_CPUID_RESULT_EBX 4
+%define VC_CPUID_RESULT_ECX 8
+%define VC_CPUID_RESULT_EDX 12
+%define VC_GHCB_MSR_EDX 16
+%define VC_GHCB_MSR_EAX 20
+%define VC_CPUID_REQUEST_REGISTER 24
+%define VC_CPUID_FUNCTION 28
+
+; #VC handler total local variable size
+;
+%define VC_VARIABLE_SIZE 32
+
+; #VC handler GHCB CPUID request/response protocol values
+;
+%define GHCB_CPUID_REQUEST 4
+%define GHCB_CPUID_RESPONSE 5
+%define GHCB_CPUID_REGISTER_SHIFT 30
+%define CPUID_INSN_LEN 2
+
+;
+; Check if Secure Encrypted Virtualization (SEV) features are enabled.
+;
+; Register usage is tight in this routine, so multiple calls for the
+; same CPUID and MSR data are performed to keep things simple.
+;
+; Modified: EAX, EBX, ECX, EDX, ESP
+;
+; If SEV is enabled then EAX will be at least 32.
+; If SEV is disabled then EAX will be zero.
+;
+CheckSevFeatures:
+ ; Set the first byte of the workarea to zero to communicate to the SEC
+ ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
+ ; instruction will trigger a #VC exception where the first byte of the
+ ; workarea will be set to one or, if CPUID is not being intercepted,
+ ; the MSR check below will set the first byte of the workarea to one.
+ mov byte[SEV_ES_WORK_AREA], 0
+
+ ;
+ ; Set up exception handlers to check for SEV-ES
+ ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
+ ; stack usage)
+ ; Establish exception handlers
+ ;
+ mov esp, SEV_ES_VC_TOP_OF_STACK
+ mov eax, ADDR_OF(Idtr)
+ lidt [cs:eax]
+
+ ; Check if we have a valid (0x8000_001F) CPUID leaf
+ ; CPUID raises a #VC exception if running as an SEV-ES guest
+ mov eax, 0x80000000
+ cpuid
+
+ ; This check should fail on Intel or Non SEV AMD CPUs. In future if
+ ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
+ ; same bit definition.
+ cmp eax, 0x8000001f
+ jl NoSev
+
+ ; Check for SEV memory encryption feature:
+ ; CPUID Fn8000_001F[EAX] - Bit 1
+ ; CPUID raises a #VC exception if running as an SEV-ES guest
+ mov eax, 0x8000001f
+ cpuid
+ bt eax, 1
+ jnc NoSev
+
+ ; Check if SEV memory encryption is enabled
+ ; MSR_0xC0010131 - Bit 0 (SEV enabled)
+ mov ecx, 0xc0010131
+ rdmsr
+ bt eax, 0
+ jnc NoSev
+
+ ; Check for SEV-ES memory encryption feature:
+ ; CPUID Fn8000_001F[EAX] - Bit 3
+ ; CPUID raises a #VC exception if running as an SEV-ES guest
+ mov eax, 0x8000001f
+ cpuid
+ bt eax, 3
+ jnc GetSevEncBit
+
+ ; Check if SEV-ES is enabled
+ ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
+ mov ecx, 0xc0010131
+ rdmsr
+ bt eax, 1
+ jnc GetSevEncBit
+
+ ; Set the first byte of the workarea to one to communicate to the SEC
+ ; phase that SEV-ES is enabled.
+ mov byte[SEV_ES_WORK_AREA], 1
+
+GetSevEncBit:
+ ; Get pte bit position to enable memory encryption
+ ; CPUID Fn8000_001F[EBX] - Bits 5:0
+ ;
+ and ebx, 0x3f
+ mov eax, ebx
+
+ ; The encryption bit position is always above 31
+ sub ebx, 32
+ jns SevSaveMask
+
+ ; Encryption bit was reported as 31 or below, enter a HLT loop
+SevEncBitLowHlt:
+ cli
+ hlt
+ jmp SevEncBitLowHlt
+
+SevSaveMask:
+ xor edx, edx
+ bts edx, ebx
+
+ mov dword[SEV_ES_WORK_AREA_ENC_MASK], 0
+ mov dword[SEV_ES_WORK_AREA_ENC_MASK + 4], edx
+ jmp SevExit
+
+NoSev:
+ ;
+ ; Perform an SEV-ES sanity check by seeing if a #VC exception occurred.
+ ;
+ cmp byte[SEV_ES_WORK_AREA], 0
+ jz NoSevPass
+
+ ;
+ ; A #VC was received, yet CPUID indicates no SEV-ES support, something
+ ; isn't right.
+ ;
+NoSevEsVcHlt:
+ cli
+ hlt
+ jmp NoSevEsVcHlt
+
+NoSevPass:
+ xor eax, eax
+
+SevExit:
+ ;
+ ; Clear exception handlers and stack
+ ;
+ push eax
+ mov eax, ADDR_OF(IdtrClear)
+ lidt [cs:eax]
+ pop eax
+ mov esp, 0
+
+ OneTimeCallRet CheckSevFeatures
+
+;
+; Called before SetCr3ForPageTables64 in SEV guests
+;
+; Modified: EAX, EBX, ECX, EDX, ESP
+;
+PreSetCr3ForPageTables64Sev:
+ ; In Tdx TDX_WORK_AREA was set to 'TDXG'.
+ ; CheckSevFeatures cannot be called in Tdx guest because SEV_ES_WORK_AREA
+ ; cannot be accessed in this situation. Any memory region to be accessed
+ ; in Td guest should be accepted first.
+ cmp dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+ jz ExitPreSetCr3ForPageTables64Sev
+
+ OneTimeCall CheckSevFeatures
+
+ExitPreSetCr3ForPageTables64Sev:
+ OneTimeCallRet PreSetCr3ForPageTables64Sev
+
+;
+; It is called in SEV after SetCr3PageTables64
+;
+; Modified: EAX, EBX, ECX, EDX, ESP
+;
+PostSetCr3PageTables64Sev:
+ ; In Tdx TDX_WORK_AREA was set to 'TDXG'.
+ ; CheckSevFeatures cannot be called in Tdx because SEV_ES_WORK_AREA
+ ; cannot be accessed in this situation. Any memory region to be accessed
+ ; in Td guest should be accepted first.
+ cmp dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+ jz ExitPostSetCr3PageTables64Sev
+
+ mov eax, cr4
+ bts eax, 5 ; enable PAE
+ mov cr4, eax
+
+ mov ecx, 0xc0000080
+ rdmsr
+ bts eax, 8 ; set LME
+ wrmsr
+
+ ;
+ ; SEV-ES mitigation check support
+ ;
+ xor ebx, ebx
+
+ cmp byte[SEV_ES_WORK_AREA], 0
+ jz EnablePaging
+
+ ;
+ ; SEV-ES is active, perform a quick sanity check against the reported
+ ; encryption bit position. This is to help mitigate against attacks where
+ ; the hypervisor reports an incorrect encryption bit position.
+ ;
+ ; This is the first step in a two step process. Before paging is enabled
+ ; writes to memory are encrypted. Using the RDRAND instruction (available
+ ; on all SEV capable processors), write 64-bits of random data to the
+ ; SEV_ES_WORK_AREA and maintain the random data in registers (register
+ ; state is protected under SEV-ES). This will be used in the second step.
+ ;
+RdRand1:
+ rdrand ecx
+ jnc RdRand1
+ mov dword[SEV_ES_WORK_AREA_RDRAND], ecx
+RdRand2:
+ rdrand edx
+ jnc RdRand2
+ mov dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
+
+ ;
+ ; Use EBX instead of the SEV_ES_WORK_AREA memory to determine whether to
+ ; perform the second step.
+ ;
+ mov ebx, 1
+
+EnablePaging:
+ mov eax, cr0
+ bts eax, 31 ; set PG
+ mov cr0, eax ; enable paging
+
+ExitPostSetCr3PageTables64Sev:
+
+ OneTimeCallRet PostSetCr3PageTables64Sev
+
+;
+; Start of #VC exception handling routines
+;
+
+SevEsIdtNotCpuid:
+ ;
+ ; Use VMGEXIT to request termination.
+ ; 1 - #VC was not for CPUID
+ ;
+ mov eax, 1
+ jmp SevEsIdtTerminate
+
+SevEsIdtNoCpuidResponse:
+ ;
+ ; Use VMGEXIT to request termination.
+ ; 2 - GHCB_CPUID_RESPONSE not received
+ ;
+ mov eax, 2
+
+SevEsIdtTerminate:
+ ;
+ ; Use VMGEXIT to request termination. At this point the reason code is
+ ; located in EAX, so shift it left 16 bits to the proper location.
+ ;
+ ; EAX[11:0] => 0x100 - request termination
+ ; EAX[15:12] => 0x1 - OVMF
+ ; EAX[23:16] => 0xXX - REASON CODE
+ ;
+ shl eax, 16
+ or eax, 0x1100
+ xor edx, edx
+ mov ecx, 0xc0010130
+ wrmsr
+ ;
+ ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
+ ; mode, so work around this by temporarily switching to 64-bit mode.
+ ;
+BITS 64
+ rep vmmcall
+BITS 32
+
+ ;
+ ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
+ ;
+SevEsIdtHlt:
+ hlt
+ jmp SevEsIdtHlt
+ iret
+
+ ;
+ ; Total stack usage for the #VC handler is 44 bytes:
+ ; - 12 bytes for the exception IRET (after popping error code)
+ ; - 32 bytes for the local variables.
+ ;
+SevEsIdtVmmComm:
+ ;
+ ; If we're here, then we are an SEV-ES guest and this
+ ; was triggered by a CPUID instruction
+ ;
+ ; Set the first byte of the workarea to one to communicate that
+ ; a #VC was taken.
+ mov byte[SEV_ES_WORK_AREA], 1
+
+ pop ecx ; Error code
+ cmp ecx, 0x72 ; Be sure it was CPUID
+ jne SevEsIdtNotCpuid
+
+ ; Set up local variable room on the stack
+ ; CPUID function : + 28
+ ; CPUID request register : + 24
+ ; GHCB MSR (EAX) : + 20
+ ; GHCB MSR (EDX) : + 16
+ ; CPUID result (EDX) : + 12
+ ; CPUID result (ECX) : + 8
+ ; CPUID result (EBX) : + 4
+ ; CPUID result (EAX) : + 0
+ sub esp, VC_VARIABLE_SIZE
+
+ ; Save the CPUID function being requested
+ mov [esp + VC_CPUID_FUNCTION], eax
+
+ ; The GHCB CPUID protocol uses the following mapping to request
+ ; a specific register:
+ ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
+ ;
+ ; Set EAX as the first register to request. This will also be used as a
+ ; loop variable to request all register values (EAX to EDX).
+ xor eax, eax
+ mov [esp + VC_CPUID_REQUEST_REGISTER], eax
+
+ ; Save current GHCB MSR value
+ mov ecx, 0xc0010130
+ rdmsr
+ mov [esp + VC_GHCB_MSR_EAX], eax
+ mov [esp + VC_GHCB_MSR_EDX], edx
+
+NextReg:
+ ;
+ ; Setup GHCB MSR
+ ; GHCB_MSR[63:32] = CPUID function
+ ; GHCB_MSR[31:30] = CPUID register
+ ; GHCB_MSR[11:0] = CPUID request protocol
+ ;
+ mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
+ cmp eax, 4
+ jge VmmDone
+
+ shl eax, GHCB_CPUID_REGISTER_SHIFT
+ or eax, GHCB_CPUID_REQUEST
+ mov edx, [esp + VC_CPUID_FUNCTION]
+ mov ecx, 0xc0010130
+ wrmsr
+
+ ;
+ ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
+ ; mode, so work around this by temporarily switching to 64-bit mode.
+ ;
+BITS 64
+ rep vmmcall
+BITS 32
+
+ ;
+ ; Read GHCB MSR
+ ; GHCB_MSR[63:32] = CPUID register value
+ ; GHCB_MSR[31:30] = CPUID register
+ ; GHCB_MSR[11:0] = CPUID response protocol
+ ;
+ mov ecx, 0xc0010130
+ rdmsr
+ mov ecx, eax
+ and ecx, 0xfff
+ cmp ecx, GHCB_CPUID_RESPONSE
+ jne SevEsIdtNoCpuidResponse
+
+ ; Save returned value
+ shr eax, GHCB_CPUID_REGISTER_SHIFT
+ mov [esp + eax * 4], edx
+
+ ; Next register
+ inc word [esp + VC_CPUID_REQUEST_REGISTER]
+
+ jmp NextReg
+
+VmmDone:
+ ;
+ ; At this point we have all CPUID register values. Restore the GHCB MSR,
+ ; set the return register values and return.
+ ;
+ mov eax, [esp + VC_GHCB_MSR_EAX]
+ mov edx, [esp + VC_GHCB_MSR_EDX]
+ mov ecx, 0xc0010130
+ wrmsr
+
+ mov eax, [esp + VC_CPUID_RESULT_EAX]
+ mov ebx, [esp + VC_CPUID_RESULT_EBX]
+ mov ecx, [esp + VC_CPUID_RESULT_ECX]
+ mov edx, [esp + VC_CPUID_RESULT_EDX]
+
+ add esp, VC_VARIABLE_SIZE
+
+ ; Update the EIP value to skip over the now handled CPUID instruction
+ ; (the CPUID instruction has a length of 2)
+ add word [esp], CPUID_INSN_LEN
+ iret
+
+ALIGN 2
+
+Idtr:
+ dw IDT_END - IDT_BASE - 1 ; Limit
+ dd ADDR_OF(IDT_BASE) ; Base
+
+IdtrClear:
+ dw 0 ; Limit
+ dd 0 ; Base
+
+ALIGN 16
+
+;
+; The Interrupt Descriptor Table (IDT)
+; This will be used to determine if SEV-ES is enabled. Upon execution
+; of the CPUID instruction, a VMM Communication Exception will occur.
+; This will tell us if SEV-ES is enabled. We can use the current value
+; of the GHCB MSR to determine the SEV attributes.
+;
+IDT_BASE:
+;
+; Vectors 0 - 28 (No handlers)
+;
+%rep 29
+ dw 0 ; Offset low bits 15..0
+ dw 0x10 ; Selector
+ db 0 ; Reserved
+ db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+ dw 0 ; Offset high bits 31..16
+%endrep
+;
+; Vector 29 (VMM Communication Exception)
+;
+ dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
+ dw 0x10 ; Selector
+ db 0 ; Reserved
+ db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+ dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
+;
+; Vectors 30 - 31 (No handlers)
+;
+%rep 2
+ dw 0 ; Offset low bits 15..0
+ dw 0x10 ; Selector
+ db 0 ; Reserved
+ db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+ dw 0 ; Offset high bits 31..16
+%endrep
+IDT_END:
+
+BITS 64
+
+;
+; Called after Jump64BitAndLandHere
+;
+PostJump64BitAndLandHereSev:
+
+ ;
+ ; If it is Tdx guest, jump to exit point directly.
+ ; This is because following code may access the memory region which has
+ ; not been accepted. It is not allowed in Tdx guests.
+ ;
+ mov eax, dword[TDX_WORK_AREA]
+ cmp eax, 0x47584454 ; 'TDXG'
+ jz GoodCompare
+
+ ;
+ ; Check if the second step of the SEV-ES mitigation is to be performed.
+ ;
+ test ebx, ebx
+ jz InsnCompare
+
+ ;
+ ; SEV-ES is active, perform the second step of the encryption bit postion
+ ; mitigation check. The ECX and EDX register contain data from RDRAND that
+ ; was stored to memory in encrypted form. If the encryption bit position is
+ ; valid, the contents of ECX and EDX will match the memory location.
+ ;
+ cmp dword[SEV_ES_WORK_AREA_RDRAND], ecx
+ jne SevEncBitHlt
+ cmp dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
+ jne SevEncBitHlt
+
+ ;
+ ; If SEV or SEV-ES is active, perform a quick sanity check against
+ ; the reported encryption bit position. This is to help mitigate
+ ; against attacks where the hypervisor reports an incorrect encryption
+ ; bit position. If SEV is not active, this check will always succeed.
+ ;
+ ; The cmp instruction compares the first four bytes of the cmp instruction
+ ; itself (which will be read decrypted if SEV or SEV-ES is active and the
+ ; encryption bit position is valid) against the immediate within the
+ ; instruction (an instruction fetch is always decrypted correctly by
+ ; hardware) based on RIP relative addressing.
+ ;
+InsnCompare:
+ cmp dword[rel InsnCompare], 0xFFF63D81
+ je GoodCompare
+
+ ;
+ ; The hypervisor provided an incorrect encryption bit position, do not
+ ; proceed.
+ ;
+SevEncBitHlt:
+ cli
+ hlt
+ jmp SevEncBitHlt
+
+GoodCompare:
+ OneTimeCallRet PostJump64BitAndLandHereSev
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-27 5:42 ` [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm " Min Xu
@ 2021-07-27 10:56 ` Brijesh Singh
2021-07-27 11:51 ` Min Xu
0 siblings, 1 reply; 36+ messages in thread
From: Brijesh Singh @ 2021-07-27 10:56 UTC (permalink / raw)
To: Min Xu, devel
Cc: brijesh.singh, Ard Biesheuvel, Jordan Justen, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
Hi Min,
This refactoring is already done by the SNP patch series.
https://edk2.groups.io/g/devel/message/77336?p=,,,20,0,0,0::Created,,posterid%3A5969970,20,2,20,83891510
It appears that you are also pulling in some of TDX logic inside the
AMDSev.asm such as
;
+PostJump64BitAndLandHereSev:
+
+ ;
+ ; If it is Tdx guest, jump to exit point directly.
+ ; This is because following code may access the memory region which has
+ ; not been accepted. It is not allowed in Tdx guests.
+ ;
+ mov eax, dword[TDX_WORK_AREA]
+ cmp eax, 0x47584454 ; 'TDXG'
+ jz GoodCompare
Why we are referring the TDX workarea inside the AmdSev.asm ?
I will take out my refactoring patch outside of the SNP series and
submit it so that you can build on top of. This will simplify review
process.
thanks
On 7/27/21 12:42 AM, Min Xu wrote:
> RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
>
> AmdSev.asm includes below routines:
> - CheckSevFeatures
> Check if Secure Encrypted Virtualization (SEV) features are enabled.
> - PreSetCr3ForPageTables64Sev
> It is called before SetCr3ForPageTables64 in SEV guests.
> - PostSetCr3PageTables64Sev
> It is called after SetCr3PageTables64 in SEV guests.
> - PostJump64BitAndLandHereSev
> It is called after Jump64BitAndLandHere in SEV guests.
> - #VC exception handling routines
>
> These routines are extracted from PageTables64.asm and Flat32ToFlat64.asm
> Need AMD engineers' help to review/validate the patch so that there is
> no regression. Thanks in advance!
>
> Note:
> In above Pre/Post routines, dword[TDX_WORK_AREA] should be checked
> to see if it is 'TDXG' (Tdx guests). This is because some memory region
> for example, byte[SEV_ES_WORK_AREA] cannot be accessed in Tdx guests.
> Tdx requires that any memory region to be accessed should be accepted
> first or initialized by host VMM before Td guest is launched.
>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Cc: Erdem Aktas <erdemaktas@google.com>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Signed-off-by: Min Xu <min.m.xu@intel.com>
> ---
> OvmfPkg/ResetVector/Ia32/AmdSev.asm | 526 ++++++++++++++++++++++++++++
> 1 file changed, 526 insertions(+)
> create mode 100644 OvmfPkg/ResetVector/Ia32/AmdSev.asm
>
> diff --git a/OvmfPkg/ResetVector/Ia32/AmdSev.asm b/OvmfPkg/ResetVector/Ia32/AmdSev.asm
> new file mode 100644
> index 000000000000..962b7e169c61
> --- /dev/null
> +++ b/OvmfPkg/ResetVector/Ia32/AmdSev.asm
> @@ -0,0 +1,526 @@
> +;------------------------------------------------------------------------------
> +; @file
> +; AMD SEV routines
> +;
> +; Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
> +; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
> +;
> +; SPDX-License-Identifier: BSD-2-Clause-Patent
> +;
> +;------------------------------------------------------------------------------
> +
> +
> +BITS 32
> +;
> +; SEV-ES #VC exception handler support
> +;
> +; #VC handler local variable locations
> +;
> +%define VC_CPUID_RESULT_EAX 0
> +%define VC_CPUID_RESULT_EBX 4
> +%define VC_CPUID_RESULT_ECX 8
> +%define VC_CPUID_RESULT_EDX 12
> +%define VC_GHCB_MSR_EDX 16
> +%define VC_GHCB_MSR_EAX 20
> +%define VC_CPUID_REQUEST_REGISTER 24
> +%define VC_CPUID_FUNCTION 28
> +
> +; #VC handler total local variable size
> +;
> +%define VC_VARIABLE_SIZE 32
> +
> +; #VC handler GHCB CPUID request/response protocol values
> +;
> +%define GHCB_CPUID_REQUEST 4
> +%define GHCB_CPUID_RESPONSE 5
> +%define GHCB_CPUID_REGISTER_SHIFT 30
> +%define CPUID_INSN_LEN 2
> +
> +;
> +; Check if Secure Encrypted Virtualization (SEV) features are enabled.
> +;
> +; Register usage is tight in this routine, so multiple calls for the
> +; same CPUID and MSR data are performed to keep things simple.
> +;
> +; Modified: EAX, EBX, ECX, EDX, ESP
> +;
> +; If SEV is enabled then EAX will be at least 32.
> +; If SEV is disabled then EAX will be zero.
> +;
> +CheckSevFeatures:
> + ; Set the first byte of the workarea to zero to communicate to the SEC
> + ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
> + ; instruction will trigger a #VC exception where the first byte of the
> + ; workarea will be set to one or, if CPUID is not being intercepted,
> + ; the MSR check below will set the first byte of the workarea to one.
> + mov byte[SEV_ES_WORK_AREA], 0
> +
> + ;
> + ; Set up exception handlers to check for SEV-ES
> + ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
> + ; stack usage)
> + ; Establish exception handlers
> + ;
> + mov esp, SEV_ES_VC_TOP_OF_STACK
> + mov eax, ADDR_OF(Idtr)
> + lidt [cs:eax]
> +
> + ; Check if we have a valid (0x8000_001F) CPUID leaf
> + ; CPUID raises a #VC exception if running as an SEV-ES guest
> + mov eax, 0x80000000
> + cpuid
> +
> + ; This check should fail on Intel or Non SEV AMD CPUs. In future if
> + ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
> + ; same bit definition.
> + cmp eax, 0x8000001f
> + jl NoSev
> +
> + ; Check for SEV memory encryption feature:
> + ; CPUID Fn8000_001F[EAX] - Bit 1
> + ; CPUID raises a #VC exception if running as an SEV-ES guest
> + mov eax, 0x8000001f
> + cpuid
> + bt eax, 1
> + jnc NoSev
> +
> + ; Check if SEV memory encryption is enabled
> + ; MSR_0xC0010131 - Bit 0 (SEV enabled)
> + mov ecx, 0xc0010131
> + rdmsr
> + bt eax, 0
> + jnc NoSev
> +
> + ; Check for SEV-ES memory encryption feature:
> + ; CPUID Fn8000_001F[EAX] - Bit 3
> + ; CPUID raises a #VC exception if running as an SEV-ES guest
> + mov eax, 0x8000001f
> + cpuid
> + bt eax, 3
> + jnc GetSevEncBit
> +
> + ; Check if SEV-ES is enabled
> + ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
> + mov ecx, 0xc0010131
> + rdmsr
> + bt eax, 1
> + jnc GetSevEncBit
> +
> + ; Set the first byte of the workarea to one to communicate to the SEC
> + ; phase that SEV-ES is enabled.
> + mov byte[SEV_ES_WORK_AREA], 1
> +
> +GetSevEncBit:
> + ; Get pte bit position to enable memory encryption
> + ; CPUID Fn8000_001F[EBX] - Bits 5:0
> + ;
> + and ebx, 0x3f
> + mov eax, ebx
> +
> + ; The encryption bit position is always above 31
> + sub ebx, 32
> + jns SevSaveMask
> +
> + ; Encryption bit was reported as 31 or below, enter a HLT loop
> +SevEncBitLowHlt:
> + cli
> + hlt
> + jmp SevEncBitLowHlt
> +
> +SevSaveMask:
> + xor edx, edx
> + bts edx, ebx
> +
> + mov dword[SEV_ES_WORK_AREA_ENC_MASK], 0
> + mov dword[SEV_ES_WORK_AREA_ENC_MASK + 4], edx
> + jmp SevExit
> +
> +NoSev:
> + ;
> + ; Perform an SEV-ES sanity check by seeing if a #VC exception occurred.
> + ;
> + cmp byte[SEV_ES_WORK_AREA], 0
> + jz NoSevPass
> +
> + ;
> + ; A #VC was received, yet CPUID indicates no SEV-ES support, something
> + ; isn't right.
> + ;
> +NoSevEsVcHlt:
> + cli
> + hlt
> + jmp NoSevEsVcHlt
> +
> +NoSevPass:
> + xor eax, eax
> +
> +SevExit:
> + ;
> + ; Clear exception handlers and stack
> + ;
> + push eax
> + mov eax, ADDR_OF(IdtrClear)
> + lidt [cs:eax]
> + pop eax
> + mov esp, 0
> +
> + OneTimeCallRet CheckSevFeatures
> +
> +;
> +; Called before SetCr3ForPageTables64 in SEV guests
> +;
> +; Modified: EAX, EBX, ECX, EDX, ESP
> +;
> +PreSetCr3ForPageTables64Sev:
> + ; In Tdx TDX_WORK_AREA was set to 'TDXG'.
> + ; CheckSevFeatures cannot be called in Tdx guest because SEV_ES_WORK_AREA
> + ; cannot be accessed in this situation. Any memory region to be accessed
> + ; in Td guest should be accepted first.
> + cmp dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
> + jz ExitPreSetCr3ForPageTables64Sev
> +
> + OneTimeCall CheckSevFeatures
> +
> +ExitPreSetCr3ForPageTables64Sev:
> + OneTimeCallRet PreSetCr3ForPageTables64Sev
> +
> +;
> +; It is called in SEV after SetCr3PageTables64
> +;
> +; Modified: EAX, EBX, ECX, EDX, ESP
> +;
> +PostSetCr3PageTables64Sev:
> + ; In Tdx TDX_WORK_AREA was set to 'TDXG'.
> + ; CheckSevFeatures cannot be called in Tdx because SEV_ES_WORK_AREA
> + ; cannot be accessed in this situation. Any memory region to be accessed
> + ; in Td guest should be accepted first.
> + cmp dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
> + jz ExitPostSetCr3PageTables64Sev
> +
> + mov eax, cr4
> + bts eax, 5 ; enable PAE
> + mov cr4, eax
> +
> + mov ecx, 0xc0000080
> + rdmsr
> + bts eax, 8 ; set LME
> + wrmsr
> +
> + ;
> + ; SEV-ES mitigation check support
> + ;
> + xor ebx, ebx
> +
> + cmp byte[SEV_ES_WORK_AREA], 0
> + jz EnablePaging
> +
> + ;
> + ; SEV-ES is active, perform a quick sanity check against the reported
> + ; encryption bit position. This is to help mitigate against attacks where
> + ; the hypervisor reports an incorrect encryption bit position.
> + ;
> + ; This is the first step in a two step process. Before paging is enabled
> + ; writes to memory are encrypted. Using the RDRAND instruction (available
> + ; on all SEV capable processors), write 64-bits of random data to the
> + ; SEV_ES_WORK_AREA and maintain the random data in registers (register
> + ; state is protected under SEV-ES). This will be used in the second step.
> + ;
> +RdRand1:
> + rdrand ecx
> + jnc RdRand1
> + mov dword[SEV_ES_WORK_AREA_RDRAND], ecx
> +RdRand2:
> + rdrand edx
> + jnc RdRand2
> + mov dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
> +
> + ;
> + ; Use EBX instead of the SEV_ES_WORK_AREA memory to determine whether to
> + ; perform the second step.
> + ;
> + mov ebx, 1
> +
> +EnablePaging:
> + mov eax, cr0
> + bts eax, 31 ; set PG
> + mov cr0, eax ; enable paging
> +
> +ExitPostSetCr3PageTables64Sev:
> +
> + OneTimeCallRet PostSetCr3PageTables64Sev
> +
> +;
> +; Start of #VC exception handling routines
> +;
> +
> +SevEsIdtNotCpuid:
> + ;
> + ; Use VMGEXIT to request termination.
> + ; 1 - #VC was not for CPUID
> + ;
> + mov eax, 1
> + jmp SevEsIdtTerminate
> +
> +SevEsIdtNoCpuidResponse:
> + ;
> + ; Use VMGEXIT to request termination.
> + ; 2 - GHCB_CPUID_RESPONSE not received
> + ;
> + mov eax, 2
> +
> +SevEsIdtTerminate:
> + ;
> + ; Use VMGEXIT to request termination. At this point the reason code is
> + ; located in EAX, so shift it left 16 bits to the proper location.
> + ;
> + ; EAX[11:0] => 0x100 - request termination
> + ; EAX[15:12] => 0x1 - OVMF
> + ; EAX[23:16] => 0xXX - REASON CODE
> + ;
> + shl eax, 16
> + or eax, 0x1100
> + xor edx, edx
> + mov ecx, 0xc0010130
> + wrmsr
> + ;
> + ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
> + ; mode, so work around this by temporarily switching to 64-bit mode.
> + ;
> +BITS 64
> + rep vmmcall
> +BITS 32
> +
> + ;
> + ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
> + ;
> +SevEsIdtHlt:
> + hlt
> + jmp SevEsIdtHlt
> + iret
> +
> + ;
> + ; Total stack usage for the #VC handler is 44 bytes:
> + ; - 12 bytes for the exception IRET (after popping error code)
> + ; - 32 bytes for the local variables.
> + ;
> +SevEsIdtVmmComm:
> + ;
> + ; If we're here, then we are an SEV-ES guest and this
> + ; was triggered by a CPUID instruction
> + ;
> + ; Set the first byte of the workarea to one to communicate that
> + ; a #VC was taken.
> + mov byte[SEV_ES_WORK_AREA], 1
> +
> + pop ecx ; Error code
> + cmp ecx, 0x72 ; Be sure it was CPUID
> + jne SevEsIdtNotCpuid
> +
> + ; Set up local variable room on the stack
> + ; CPUID function : + 28
> + ; CPUID request register : + 24
> + ; GHCB MSR (EAX) : + 20
> + ; GHCB MSR (EDX) : + 16
> + ; CPUID result (EDX) : + 12
> + ; CPUID result (ECX) : + 8
> + ; CPUID result (EBX) : + 4
> + ; CPUID result (EAX) : + 0
> + sub esp, VC_VARIABLE_SIZE
> +
> + ; Save the CPUID function being requested
> + mov [esp + VC_CPUID_FUNCTION], eax
> +
> + ; The GHCB CPUID protocol uses the following mapping to request
> + ; a specific register:
> + ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
> + ;
> + ; Set EAX as the first register to request. This will also be used as a
> + ; loop variable to request all register values (EAX to EDX).
> + xor eax, eax
> + mov [esp + VC_CPUID_REQUEST_REGISTER], eax
> +
> + ; Save current GHCB MSR value
> + mov ecx, 0xc0010130
> + rdmsr
> + mov [esp + VC_GHCB_MSR_EAX], eax
> + mov [esp + VC_GHCB_MSR_EDX], edx
> +
> +NextReg:
> + ;
> + ; Setup GHCB MSR
> + ; GHCB_MSR[63:32] = CPUID function
> + ; GHCB_MSR[31:30] = CPUID register
> + ; GHCB_MSR[11:0] = CPUID request protocol
> + ;
> + mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
> + cmp eax, 4
> + jge VmmDone
> +
> + shl eax, GHCB_CPUID_REGISTER_SHIFT
> + or eax, GHCB_CPUID_REQUEST
> + mov edx, [esp + VC_CPUID_FUNCTION]
> + mov ecx, 0xc0010130
> + wrmsr
> +
> + ;
> + ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
> + ; mode, so work around this by temporarily switching to 64-bit mode.
> + ;
> +BITS 64
> + rep vmmcall
> +BITS 32
> +
> + ;
> + ; Read GHCB MSR
> + ; GHCB_MSR[63:32] = CPUID register value
> + ; GHCB_MSR[31:30] = CPUID register
> + ; GHCB_MSR[11:0] = CPUID response protocol
> + ;
> + mov ecx, 0xc0010130
> + rdmsr
> + mov ecx, eax
> + and ecx, 0xfff
> + cmp ecx, GHCB_CPUID_RESPONSE
> + jne SevEsIdtNoCpuidResponse
> +
> + ; Save returned value
> + shr eax, GHCB_CPUID_REGISTER_SHIFT
> + mov [esp + eax * 4], edx
> +
> + ; Next register
> + inc word [esp + VC_CPUID_REQUEST_REGISTER]
> +
> + jmp NextReg
> +
> +VmmDone:
> + ;
> + ; At this point we have all CPUID register values. Restore the GHCB MSR,
> + ; set the return register values and return.
> + ;
> + mov eax, [esp + VC_GHCB_MSR_EAX]
> + mov edx, [esp + VC_GHCB_MSR_EDX]
> + mov ecx, 0xc0010130
> + wrmsr
> +
> + mov eax, [esp + VC_CPUID_RESULT_EAX]
> + mov ebx, [esp + VC_CPUID_RESULT_EBX]
> + mov ecx, [esp + VC_CPUID_RESULT_ECX]
> + mov edx, [esp + VC_CPUID_RESULT_EDX]
> +
> + add esp, VC_VARIABLE_SIZE
> +
> + ; Update the EIP value to skip over the now handled CPUID instruction
> + ; (the CPUID instruction has a length of 2)
> + add word [esp], CPUID_INSN_LEN
> + iret
> +
> +ALIGN 2
> +
> +Idtr:
> + dw IDT_END - IDT_BASE - 1 ; Limit
> + dd ADDR_OF(IDT_BASE) ; Base
> +
> +IdtrClear:
> + dw 0 ; Limit
> + dd 0 ; Base
> +
> +ALIGN 16
> +
> +;
> +; The Interrupt Descriptor Table (IDT)
> +; This will be used to determine if SEV-ES is enabled. Upon execution
> +; of the CPUID instruction, a VMM Communication Exception will occur.
> +; This will tell us if SEV-ES is enabled. We can use the current value
> +; of the GHCB MSR to determine the SEV attributes.
> +;
> +IDT_BASE:
> +;
> +; Vectors 0 - 28 (No handlers)
> +;
> +%rep 29
> + dw 0 ; Offset low bits 15..0
> + dw 0x10 ; Selector
> + db 0 ; Reserved
> + db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
> + dw 0 ; Offset high bits 31..16
> +%endrep
> +;
> +; Vector 29 (VMM Communication Exception)
> +;
> + dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
> + dw 0x10 ; Selector
> + db 0 ; Reserved
> + db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
> + dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
> +;
> +; Vectors 30 - 31 (No handlers)
> +;
> +%rep 2
> + dw 0 ; Offset low bits 15..0
> + dw 0x10 ; Selector
> + db 0 ; Reserved
> + db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
> + dw 0 ; Offset high bits 31..16
> +%endrep
> +IDT_END:
> +
> +BITS 64
> +
> +;
> +; Called after Jump64BitAndLandHere
> +;
> +PostJump64BitAndLandHereSev:
> +
> + ;
> + ; If it is Tdx guest, jump to exit point directly.
> + ; This is because following code may access the memory region which has
> + ; not been accepted. It is not allowed in Tdx guests.
> + ;
> + mov eax, dword[TDX_WORK_AREA]
> + cmp eax, 0x47584454 ; 'TDXG'
> + jz GoodCompare
> +
> + ;
> + ; Check if the second step of the SEV-ES mitigation is to be performed.
> + ;
> + test ebx, ebx
> + jz InsnCompare
> +
> + ;
> + ; SEV-ES is active, perform the second step of the encryption bit postion
> + ; mitigation check. The ECX and EDX register contain data from RDRAND that
> + ; was stored to memory in encrypted form. If the encryption bit position is
> + ; valid, the contents of ECX and EDX will match the memory location.
> + ;
> + cmp dword[SEV_ES_WORK_AREA_RDRAND], ecx
> + jne SevEncBitHlt
> + cmp dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
> + jne SevEncBitHlt
> +
> + ;
> + ; If SEV or SEV-ES is active, perform a quick sanity check against
> + ; the reported encryption bit position. This is to help mitigate
> + ; against attacks where the hypervisor reports an incorrect encryption
> + ; bit position. If SEV is not active, this check will always succeed.
> + ;
> + ; The cmp instruction compares the first four bytes of the cmp instruction
> + ; itself (which will be read decrypted if SEV or SEV-ES is active and the
> + ; encryption bit position is valid) against the immediate within the
> + ; instruction (an instruction fetch is always decrypted correctly by
> + ; hardware) based on RIP relative addressing.
> + ;
> +InsnCompare:
> + cmp dword[rel InsnCompare], 0xFFF63D81
> + je GoodCompare
> +
> + ;
> + ; The hypervisor provided an incorrect encryption bit position, do not
> + ; proceed.
> + ;
> +SevEncBitHlt:
> + cli
> + hlt
> + jmp SevEncBitHlt
> +
> +GoodCompare:
> + OneTimeCallRet PostJump64BitAndLandHereSev
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-27 10:56 ` Brijesh Singh
@ 2021-07-27 11:51 ` Min Xu
2021-07-27 12:31 ` Brijesh Singh
0 siblings, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-27 11:51 UTC (permalink / raw)
To: Brijesh Singh, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Yao, Jiewen, Tom Lendacky
On July 27, 2021 6:57 PM, Brijesh Singh wrote:
> Hi Min,
>
> This refactoring is already done by the SNP patch series.
>
> https://edk2.groups.io/g/devel/message/77336?p=,,,20,0,0,0::Created,,post
> erid%3A5969970,20,2,20,83891510
>
> It appears that you are also pulling in some of TDX logic inside the
> AMDSev.asm such as
>
> ;
> +PostJump64BitAndLandHereSev:
> +
> + ;
> + ; If it is Tdx guest, jump to exit point directly.
> + ; This is because following code may access the memory region which has
> + ; not been accepted. It is not allowed in Tdx guests.
> + ;
> + mov eax, dword[TDX_WORK_AREA]
> + cmp eax, 0x47584454 ; 'TDXG'
> + jz GoodCompare
>
> Why we are referring the TDX workarea inside the AmdSev.asm ?
See my explanation in the above comments. In Tdx guests memory region cannot
be accessed unless it is accepted by guest or initialized by the host VMM. In
PostJump64BitAndLandHereSev there is access to dword[SEV_ES_WORK_AREA_RDRAND]
which is not initialized by host VMM. If this code will not be executed in
Tdx guest, then the above check is not needed. I need your help to confirm it.
There are similar Tdx check in my patch of AmdSev.asm. For example in CheckSevFeatures
byte[SEV_ES_WORK_AREA] is used to record the SEV-ES flag. This memory region is
not initialized by host VMM either. So in Tdx it will trigger error.
Another solution is that the memory region used by SEV in ResetVector are added
Into Tdx metadata so that host VMM will initialize those memory region when
It creates the Td guest. What's your opinion?
>
> I will take out my refactoring patch outside of the SNP series and submit it so
> that you can build on top of. This will simplify review process.
>
Thank you very much for the refactoring. I will refine my patch based on it.
>
> thanks
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-27 11:51 ` Min Xu
@ 2021-07-27 12:31 ` Brijesh Singh
2021-07-27 12:46 ` Yao, Jiewen
2021-07-28 0:40 ` Min Xu
0 siblings, 2 replies; 36+ messages in thread
From: Brijesh Singh @ 2021-07-27 12:31 UTC (permalink / raw)
To: Xu, Min M, devel@edk2.groups.io
Cc: brijesh.singh, Ard Biesheuvel, Justen, Jordan L, Erdem Aktas,
James Bottomley, Yao, Jiewen, Tom Lendacky
On 7/27/21 6:51 AM, Xu, Min M wrote:
> On July 27, 2021 6:57 PM, Brijesh Singh wrote:
>> Hi Min,
>>
>> This refactoring is already done by the SNP patch series.
>>
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fedk2.groups.io%2Fg%2Fdevel%2Fmessage%2F77336%3Fp%3D%2C%2C%2C20%2C0%2C0%2C0%3A%3ACreated%2C%2Cpost&data=04%7C01%7Cbrijesh.singh%40amd.com%7C22b61f2ff5bb48348b0608d950f4d7c5%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637629834792320372%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=tMGpR4a2uZTTR%2FsciTN0oeca2mZ32GfX3K78lA5BWas%3D&reserved=0
>> erid%3A5969970,20,2,20,83891510
>>
>> It appears that you are also pulling in some of TDX logic inside the
>> AMDSev.asm such as
>>
>> ;
>> +PostJump64BitAndLandHereSev:
>> +
>> + ;
>> + ; If it is Tdx guest, jump to exit point directly.
>> + ; This is because following code may access the memory region which has
>> + ; not been accepted. It is not allowed in Tdx guests.
>> + ;
>> + mov eax, dword[TDX_WORK_AREA]
>> + cmp eax, 0x47584454 ; 'TDXG'
>> + jz GoodCompare
>>
>> Why we are referring the TDX workarea inside the AmdSev.asm ?
> See my explanation in the above comments. In Tdx guests memory region cannot
> be accessed unless it is accepted by guest or initialized by the host VMM. In
> PostJump64BitAndLandHereSev there is access to dword[SEV_ES_WORK_AREA_RDRAND]
> which is not initialized by host VMM. If this code will not be executed in
> Tdx guest, then the above check is not needed. I need your help to confirm it.
>
> There are similar Tdx check in my patch of AmdSev.asm. For example in CheckSevFeatures
> byte[SEV_ES_WORK_AREA] is used to record the SEV-ES flag. This memory region is
> not initialized by host VMM either. So in Tdx it will trigger error.
>
> Another solution is that the memory region used by SEV in ResetVector are added
> Into Tdx metadata so that host VMM will initialize those memory region when
> It creates the Td guest. What's your opinion?
I am not full versed on TDX yet and sorry I am not able to follow you
question completely to provide any advice. With SEV and SEV-ES, a guest
can access the memory without going through the validation process, but
with the SEV-SNP, the page need to be validated (aka accepted) before
the access. In SNP series, we ensure that the data pages used in the
reset vector are pre-validated during the VM creation time -- this
allows us to access the pages without going through accept process. If I
follow you correctly on your metadata comment then it is similar to
saying is pre-validate these range of pages used in the reset vector
code (that include GHCB page, Page table pages etc), right ?
For SEV-SNP, see this patch
https://edk2.groups.io/g/devel/message/77342?p=,,,20,0,0,0::Created,,posterid%3A5969970,20,2,20,83891520
A VMM (qemu) looks for the range of page it need to prevalidate before
the boot, the range is provided through the GUID (SevSnpBootBlock).
>> I will take out my refactoring patch outside of the SNP series and submit it so
>> that you can build on top of. This will simplify review process.
>>
> Thank you very much for the refactoring. I will refine my patch based on it.
>> thanks
>>
>>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-27 12:31 ` Brijesh Singh
@ 2021-07-27 12:46 ` Yao, Jiewen
2021-07-28 5:07 ` Min Xu
2021-07-28 0:40 ` Min Xu
1 sibling, 1 reply; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-27 12:46 UTC (permalink / raw)
To: Brijesh Singh, Xu, Min M, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
HI Min
I agree with Brijesh.
The basic rule is: SEV file shall never refer to TDX data structure. TDX file shall never refer to SEV data structure.
These code should be isolated clearly.
Do we still need that logic if we follow the new pattern?
Thank you
Yao Jiewen
> -----Original Message-----
> From: Brijesh Singh <brijesh.singh@amd.com>
> Sent: Tuesday, July 27, 2021 8:31 PM
> To: Xu, Min M <min.m.xu@intel.com>; devel@edk2.groups.io
> Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
> Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Yao,
> Jiewen <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
>
>
> On 7/27/21 6:51 AM, Xu, Min M wrote:
> > On July 27, 2021 6:57 PM, Brijesh Singh wrote:
> >> Hi Min,
> >>
> >> This refactoring is already done by the SNP patch series.
> >>
> >>
> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fedk2.gr
> oups.io%2Fg%2Fdevel%2Fmessage%2F77336%3Fp%3D%2C%2C%2C20%2C0%2
> C0%2C0%3A%3ACreated%2C%2Cpost&data=04%7C01%7Cbrijesh.singh%4
> 0amd.com%7C22b61f2ff5bb48348b0608d950f4d7c5%7C3dd8961fe4884e608e1
> 1a82d994e183d%7C0%7C0%7C637629834792320372%7CUnknown%7CTWFpb
> GZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6M
> n0%3D%7C1000&sdata=tMGpR4a2uZTTR%2FsciTN0oeca2mZ32GfX3K78lA
> 5BWas%3D&reserved=0
> >> erid%3A5969970,20,2,20,83891510
> >>
> >> It appears that you are also pulling in some of TDX logic inside the
> >> AMDSev.asm such as
> >>
> >> ;
> >> +PostJump64BitAndLandHereSev:
> >> +
> >> + ;
> >> + ; If it is Tdx guest, jump to exit point directly.
> >> + ; This is because following code may access the memory region which has
> >> + ; not been accepted. It is not allowed in Tdx guests.
> >> + ;
> >> + mov eax, dword[TDX_WORK_AREA]
> >> + cmp eax, 0x47584454 ; 'TDXG'
> >> + jz GoodCompare
> >>
> >> Why we are referring the TDX workarea inside the AmdSev.asm ?
> > See my explanation in the above comments. In Tdx guests memory region
> cannot
> > be accessed unless it is accepted by guest or initialized by the host VMM. In
> > PostJump64BitAndLandHereSev there is access to
> dword[SEV_ES_WORK_AREA_RDRAND]
> > which is not initialized by host VMM. If this code will not be executed in
> > Tdx guest, then the above check is not needed. I need your help to confirm it.
> >
> > There are similar Tdx check in my patch of AmdSev.asm. For example in
> CheckSevFeatures
> > byte[SEV_ES_WORK_AREA] is used to record the SEV-ES flag. This memory
> region is
> > not initialized by host VMM either. So in Tdx it will trigger error.
> >
> > Another solution is that the memory region used by SEV in ResetVector are
> added
> > Into Tdx metadata so that host VMM will initialize those memory region when
> > It creates the Td guest. What's your opinion?
>
> I am not full versed on TDX yet and sorry I am not able to follow you
> question completely to provide any advice. With SEV and SEV-ES, a guest
> can access the memory without going through the validation process, but
> with the SEV-SNP, the page need to be validated (aka accepted) before
> the access. In SNP series, we ensure that the data pages used in the
> reset vector are pre-validated during the VM creation time -- this
> allows us to access the pages without going through accept process. If I
> follow you correctly on your metadata comment then it is similar to
> saying is pre-validate these range of pages used in the reset vector
> code (that include GHCB page, Page table pages etc), right ?
>
> For SEV-SNP, see this patch
>
> https://edk2.groups.io/g/devel/message/77342?p=,,,20,0,0,0::Created,,posteri
> d%3A5969970,20,2,20,83891520
>
> A VMM (qemu) looks for the range of page it need to prevalidate before
> the boot, the range is provided through the GUID (SevSnpBootBlock).
>
> >> I will take out my refactoring patch outside of the SNP series and submit it so
> >> that you can build on top of. This will simplify review process.
> >>
> > Thank you very much for the refactoring. I will refine my patch based on it.
> >> thanks
> >>
> >>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-27 12:46 ` Yao, Jiewen
@ 2021-07-28 5:07 ` Min Xu
2021-07-28 6:04 ` Yao, Jiewen
0 siblings, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-28 5:07 UTC (permalink / raw)
To: Yao, Jiewen, Brijesh Singh, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> HI Min
> I agree with Brijesh.
>
> The basic rule is: SEV file shall never refer to TDX data structure. TDX file
> shall never refer to SEV data structure.
> These code should be isolated clearly.
>
> Do we still need that logic if we follow the new pattern?
I have replied to Brijesh's mail about the concern of the new pattern.
I have some concern in the current pattern:
====================
OneTimeCall PreMainFunctionHookSev
OneTimeCall PreMainFunctionHookTdx
MainFunction:
XXXXXX
OneTimeCall PostMainFunctionHookSev
OneTimeCall PostMainFunctionHookTdx
====================
The TEE function need implement a TEE check function (such as IsSev, or IsTdx).
Tdx call CPUID(0x21) to determine if it is tdx guest in the very beginning of ResetVector. Then 'TDXG' is set
in TDX_WORK_AREA. SEV does the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to 1.
After that both TDX and SEV read the above WORK_AREA to check if it is TDX or SEV or legacy guest.
In Tdx the access to SEV_ES_WORK_AREA will trigger error because SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error too.
I am wondering if TDX and SEV can use the same memory region (for example, TEE_WORK_AREA) as the work area?
So that this work area is guaranteed to be initialized in both TDX and SEV. Structure of the TEE_WORK_AREA may
look like this:
typedef struct {
UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
UINT8 Others[];
} TEE_WORK_AREA;
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 5:07 ` Min Xu
@ 2021-07-28 6:04 ` Yao, Jiewen
2021-07-28 6:58 ` Min Xu
0 siblings, 1 reply; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-28 6:04 UTC (permalink / raw)
To: Xu, Min M
Cc: Brijesh Singh, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Erdem Aktas, James Bottomley, Tom Lendacky
It does not necessary to be a working area.
We just need a common TEE flag to indicate if the system run in legacy, SEV, or TDX, right?
thank you!
Yao, Jiewen
> 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com> 写道:
>
> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
>> HI Min
>> I agree with Brijesh.
>>
>> The basic rule is: SEV file shall never refer to TDX data structure. TDX file
>> shall never refer to SEV data structure.
>> These code should be isolated clearly.
>>
>> Do we still need that logic if we follow the new pattern?
> I have replied to Brijesh's mail about the concern of the new pattern.
>
> I have some concern in the current pattern:
> ====================
> OneTimeCall PreMainFunctionHookSev
> OneTimeCall PreMainFunctionHookTdx
> MainFunction:
> XXXXXX
> OneTimeCall PostMainFunctionHookSev
> OneTimeCall PostMainFunctionHookTdx
> ====================
> The TEE function need implement a TEE check function (such as IsSev, or IsTdx).
>
> Tdx call CPUID(0x21) to determine if it is tdx guest in the very beginning of ResetVector. Then 'TDXG' is set
> in TDX_WORK_AREA. SEV does the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to 1.
>
> After that both TDX and SEV read the above WORK_AREA to check if it is TDX or SEV or legacy guest.
>
> In Tdx the access to SEV_ES_WORK_AREA will trigger error because SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error too.
>
> I am wondering if TDX and SEV can use the same memory region (for example, TEE_WORK_AREA) as the work area?
> So that this work area is guaranteed to be initialized in both TDX and SEV. Structure of the TEE_WORK_AREA may
> look like this:
> typedef struct {
> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> UINT8 Others[];
> } TEE_WORK_AREA;
>>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 6:04 ` Yao, Jiewen
@ 2021-07-28 6:58 ` Min Xu
2021-07-28 7:54 ` Yao, Jiewen
0 siblings, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-28 6:58 UTC (permalink / raw)
To: Yao, Jiewen
Cc: Brijesh Singh, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Erdem Aktas, James Bottomley, Tom Lendacky
On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
> It does not necessary to be a working area.
>
> We just need a common TEE flag to indicate if the system run in legacy, SEV, or
> TDX, right?
Right. We need somewhere to store this flag, either in a Register or in Memory.
If it is memory, then in Tdx the memory region should be initialized by host VMM.
>
> thank you!
> Yao, Jiewen
>
>
> > 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com> 写道:
> >
> > On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> >> HI Min
> >> I agree with Brijesh.
> >>
> >> The basic rule is: SEV file shall never refer to TDX data structure.
> >> TDX file shall never refer to SEV data structure.
> >> These code should be isolated clearly.
> >>
> >> Do we still need that logic if we follow the new pattern?
> > I have replied to Brijesh's mail about the concern of the new pattern.
> >
> > I have some concern in the current pattern:
> > ====================
> > OneTimeCall PreMainFunctionHookSev
> > OneTimeCall PreMainFunctionHookTdx
> > MainFunction:
> > XXXXXX
> > OneTimeCall PostMainFunctionHookSev
> > OneTimeCall PostMainFunctionHookTdx
> > ====================
> > The TEE function need implement a TEE check function (such as IsSev, or IsTdx).
> >
> > Tdx call CPUID(0x21) to determine if it is tdx guest in the very
> > beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV does
> the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to 1.
> >
> > After that both TDX and SEV read the above WORK_AREA to check if it is TDX
> or SEV or legacy guest.
> >
> > In Tdx the access to SEV_ES_WORK_AREA will trigger error because
> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> > In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error too.
> >
> > I am wondering if TDX and SEV can use the same memory region (for example,
> TEE_WORK_AREA) as the work area?
> > So that this work area is guaranteed to be initialized in both TDX and
> > SEV. Structure of the TEE_WORK_AREA may look like this:
> > typedef struct {
> > UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> > UINT8 Others[];
> > } TEE_WORK_AREA;
> >>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 6:58 ` Min Xu
@ 2021-07-28 7:54 ` Yao, Jiewen
2021-07-28 8:34 ` Min Xu
2021-07-28 14:34 ` Brijesh Singh
0 siblings, 2 replies; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-28 7:54 UTC (permalink / raw)
To: Xu, Min M
Cc: Brijesh Singh, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Erdem Aktas, James Bottomley, Tom Lendacky
Yes. I am thinking the same thing.
[CC Flag memory location]
1) A general purpose register, such as EBP.
2) A global variable, such as
.data
TeeFlags: DD 0
3) A fixed region in stack, such as
dword[STACK_TOP - 4]
4) A new CC common fixed region, such as
dword[CC_COMMON_FLAGS]
5) A fixed region piggyback on existing CC working area, such as
dword[CC_WORKING_AREA]
Hi Brijesh/Min
Any preference?
[CC Indicator Flags]
Proposal: UINT8[4]
Byte [0] Version: 0
byte [1] Length: 4
byte [2] Type:
0: legacy
1: SEV
2: TDX
byte [3] Sub Type:
If Type is 0 (legacy), then
0: legacy
If Type is 1 (SEV), then
0: SEV
1: SEV-ES
2: SEV-SNP
If Type is 2 (TDX), then
0: TDX 1.0
Thank you
Yao Jiewen
> -----Original Message-----
> From: Xu, Min M <min.m.xu@intel.com>
> Sent: Wednesday, July 28, 2021 2:58 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
>
> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
> > It does not necessary to be a working area.
> >
> > We just need a common TEE flag to indicate if the system run in legacy, SEV,
> or
> > TDX, right?
> Right. We need somewhere to store this flag, either in a Register or in Memory.
> If it is memory, then in Tdx the memory region should be initialized by host VMM.
> >
> > thank you!
> > Yao, Jiewen
> >
> >
> > > 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com> 写道:
> > >
> > > On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> > >> HI Min
> > >> I agree with Brijesh.
> > >>
> > >> The basic rule is: SEV file shall never refer to TDX data structure.
> > >> TDX file shall never refer to SEV data structure.
> > >> These code should be isolated clearly.
> > >>
> > >> Do we still need that logic if we follow the new pattern?
> > > I have replied to Brijesh's mail about the concern of the new pattern.
> > >
> > > I have some concern in the current pattern:
> > > ====================
> > > OneTimeCall PreMainFunctionHookSev
> > > OneTimeCall PreMainFunctionHookTdx
> > > MainFunction:
> > > XXXXXX
> > > OneTimeCall PostMainFunctionHookSev
> > > OneTimeCall PostMainFunctionHookTdx
> > > ====================
> > > The TEE function need implement a TEE check function (such as IsSev, or
> IsTdx).
> > >
> > > Tdx call CPUID(0x21) to determine if it is tdx guest in the very
> > > beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV does
> > the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to 1.
> > >
> > > After that both TDX and SEV read the above WORK_AREA to check if it is TDX
> > or SEV or legacy guest.
> > >
> > > In Tdx the access to SEV_ES_WORK_AREA will trigger error because
> > SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> > > In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error too.
> > >
> > > I am wondering if TDX and SEV can use the same memory region (for
> example,
> > TEE_WORK_AREA) as the work area?
> > > So that this work area is guaranteed to be initialized in both TDX and
> > > SEV. Structure of the TEE_WORK_AREA may look like this:
> > > typedef struct {
> > > UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> > > UINT8 Others[];
> > > } TEE_WORK_AREA;
> > >>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 7:54 ` Yao, Jiewen
@ 2021-07-28 8:34 ` Min Xu
2021-07-28 14:34 ` Brijesh Singh
1 sibling, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-28 8:34 UTC (permalink / raw)
To: Yao, Jiewen
Cc: Brijesh Singh, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Erdem Aktas, James Bottomley, Tom Lendacky
I noticed SEV has the memory region of SEV_ES_WORK_AREA (gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase) in MEMFD
The definition is below:
typedef struct _SEC_SEV_ES_WORK_AREA {
UINT8 SevEsEnabled;
UINT8 Reserved1[7];
UINT64 RandomData;
UINT64 EncryptionMask;
} SEC_SEV_ES_WORK_AREA;
In ResetVector SEV flag is recorded in the first byte of SEV_ES_WORK_AREA. In SecMain.c this flag is read to determine SEV.
I am thinking whether this memory region can be used by both TDX and SEV. (This is option 5)
SEV_ES_WORK_AREA will be added in tdx metadata so that it is initialized by host VMM and can be accessed in Tdx guest.
In SEV guest I believe SEV_ES_WORK_AREA can be accessed without any error.
The first 8 bytes of SEV_ES_WORK_AREA can be redefined as [CC Indicator Flags].
> -----Original Message-----
> From: Yao, Jiewen <jiewen.yao@intel.com>
> Sent: Wednesday, July 28, 2021 3:55 PM
> To: Xu, Min M <min.m.xu@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> James Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
>
> Yes. I am thinking the same thing.
>
> [CC Flag memory location]
> 1) A general purpose register, such as EBP.
>
> 2) A global variable, such as
> .data
> TeeFlags: DD 0
>
> 3) A fixed region in stack, such as
> dword[STACK_TOP - 4]
>
> 4) A new CC common fixed region, such as dword[CC_COMMON_FLAGS]
>
> 5) A fixed region piggyback on existing CC working area, such as
> dword[CC_WORKING_AREA]
>
> Hi Brijesh/Min
> Any preference?
>
> [CC Indicator Flags]
> Proposal: UINT8[4]
>
> Byte [0] Version: 0
> byte [1] Length: 4
> byte [2] Type:
> 0: legacy
> 1: SEV
> 2: TDX
> byte [3] Sub Type:
> If Type is 0 (legacy), then
> 0: legacy
> If Type is 1 (SEV), then
> 0: SEV
> 1: SEV-ES
> 2: SEV-SNP
> If Type is 2 (TDX), then
> 0: TDX 1.0
>
> Thank you
> Yao Jiewen
>
>
> > -----Original Message-----
> > From: Xu, Min M <min.m.xu@intel.com>
> > Sent: Wednesday, July 28, 2021 2:58 PM
> > To: Yao, Jiewen <jiewen.yao@intel.com>
> > Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
> > Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> > <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> > James Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> > <thomas.lendacky@amd.com>
> > Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
> >
> > On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
> > > It does not necessary to be a working area.
> > >
> > > We just need a common TEE flag to indicate if the system run in
> > > legacy, SEV,
> > or
> > > TDX, right?
> > Right. We need somewhere to store this flag, either in a Register or in
> Memory.
> > If it is memory, then in Tdx the memory region should be initialized by host
> VMM.
> > >
> > > thank you!
> > > Yao, Jiewen
> > >
> > >
> > > > 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com>
> 写道:
> > > >
> > > > On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> > > >> HI Min
> > > >> I agree with Brijesh.
> > > >>
> > > >> The basic rule is: SEV file shall never refer to TDX data structure.
> > > >> TDX file shall never refer to SEV data structure.
> > > >> These code should be isolated clearly.
> > > >>
> > > >> Do we still need that logic if we follow the new pattern?
> > > > I have replied to Brijesh's mail about the concern of the new pattern.
> > > >
> > > > I have some concern in the current pattern:
> > > > ====================
> > > > OneTimeCall PreMainFunctionHookSev
> > > > OneTimeCall PreMainFunctionHookTdx
> > > > MainFunction:
> > > > XXXXXX
> > > > OneTimeCall PostMainFunctionHookSev
> > > > OneTimeCall PostMainFunctionHookTdx
> > > > ====================
> > > > The TEE function need implement a TEE check function (such as
> > > > IsSev, or
> > IsTdx).
> > > >
> > > > Tdx call CPUID(0x21) to determine if it is tdx guest in the very
> > > > beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV
> > > > does
> > > the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA
> to 1.
> > > >
> > > > After that both TDX and SEV read the above WORK_AREA to check if
> > > > it is TDX
> > > or SEV or legacy guest.
> > > >
> > > > In Tdx the access to SEV_ES_WORK_AREA will trigger error because
> > > SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> > > > In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error
> too.
> > > >
> > > > I am wondering if TDX and SEV can use the same memory region (for
> > example,
> > > TEE_WORK_AREA) as the work area?
> > > > So that this work area is guaranteed to be initialized in both TDX
> > > > and SEV. Structure of the TEE_WORK_AREA may look like this:
> > > > typedef struct {
> > > > UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> > > > UINT8 Others[];
> > > > } TEE_WORK_AREA;
> > > >>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 7:54 ` Yao, Jiewen
2021-07-28 8:34 ` Min Xu
@ 2021-07-28 14:34 ` Brijesh Singh
2021-07-28 15:22 ` [edk2-devel] " Yao, Jiewen
1 sibling, 1 reply; 36+ messages in thread
From: Brijesh Singh @ 2021-07-28 14:34 UTC (permalink / raw)
To: Yao, Jiewen, Xu, Min M
Cc: brijesh.singh, devel@edk2.groups.io, Ard Biesheuvel,
Justen, Jordan L, Erdem Aktas, James Bottomley, Tom Lendacky
Hi Jiewen and Min,
See my comments below.
On 7/28/21 2:54 AM, Yao, Jiewen wrote:
> Yes. I am thinking the same thing.
>
> [CC Flag memory location]
> 1) A general purpose register, such as EBP.
>
> 2) A global variable, such as
> .data
> TeeFlags: DD 0
>
> 3) A fixed region in stack, such as
> dword[STACK_TOP - 4]
>
> 4) A new CC common fixed region, such as
> dword[CC_COMMON_FLAGS]
>
> 5) A fixed region piggyback on existing CC working area, such as
> dword[CC_WORKING_AREA]
>
> Hi Brijesh/Min
> Any preference?
>
> [CC Indicator Flags]
> Proposal: UINT8[4]
>
> Byte [0] Version: 0
> byte [1] Length: 4
> byte [2] Type:
> 0: legacy
> 1: SEV
> 2: TDX
> byte [3] Sub Type:
> If Type is 0 (legacy), then
> 0: legacy
> If Type is 1 (SEV), then
> 0: SEV
> 1: SEV-ES
> 2: SEV-SNP
> If Type is 2 (TDX), then
> 0: TDX 1.0
>
> Thank you
> Yao Jiewen
>
>
>> -----Original Message-----
>> From: Xu, Min M <min.m.xu@intel.com>
>> Sent: Wednesday, July 28, 2021 2:58 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>
>> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
>> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
>>
>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
>>> It does not necessary to be a working area.
>>>
>>> We just need a common TEE flag to indicate if the system run in legacy, SEV,
>> or
>>> TDX, right?
>> Right. We need somewhere to store this flag, either in a Register or in Memory.
>> If it is memory, then in Tdx the memory region should be initialized by host VMM.
>>>
>>> thank you!
>>> Yao, Jiewen
>>>
>>>
>>>> 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com> 写道:
>>>>
>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
>>>>> HI Min
>>>>> I agree with Brijesh.
>>>>>
>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
>>>>> TDX file shall never refer to SEV data structure.
>>>>> These code should be isolated clearly.
>>>>>
>>>>> Do we still need that logic if we follow the new pattern?
>>>> I have replied to Brijesh's mail about the concern of the new pattern.
>>>>
>>>> I have some concern in the current pattern:
>>>> ====================
>>>> OneTimeCall PreMainFunctionHookSev
>>>> OneTimeCall PreMainFunctionHookTdx
>>>> MainFunction:
>>>> XXXXXX
>>>> OneTimeCall PostMainFunctionHookSev
>>>> OneTimeCall PostMainFunctionHookTdx
>>>> ====================
>>>> The TEE function need implement a TEE check function (such as IsSev, or
>> IsTdx).
>>>>
>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the very
>>>> beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV does
>>> the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to 1.
>>>>
>>>> After that both TDX and SEV read the above WORK_AREA to check if it is TDX
>>> or SEV or legacy guest.
>>>>
>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error because
>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error too.
>>>>
>>>> I am wondering if TDX and SEV can use the same memory region (for
>> example,
>>> TEE_WORK_AREA) as the work area?
>>>> So that this work area is guaranteed to be initialized in both TDX and
>>>> SEV. Structure of the TEE_WORK_AREA may look like this:
>>>> typedef struct {
>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
>>>> UINT8 Others[];
>>>> } TEE_WORK_AREA;
>>>>>
Are we reserving a new page for the TDX_WORK_AREA ? I am wondering why
can't we use the SEV_ES_WORK_AREA instead of wasting space in the MEMFD.
The SEV_ES_WORK_AREA layout looks like this:
typedef struct _SEC_SEV_ES_WORK_AREA {
UINT8 SevEsEnabled;
UINT8 Reserved1[7];
UINT64 RandomData;
UINT64 EncryptionMask;
} SEC_SEV_ES_WORK_AREA;
There is reserved bit after the SevEsEnabled and one byte can be used
for the TdxEnabled;
typedef struct _SEC_SEV_ES_WORK_AREA {
UINT8 SevEsEnabled;
UINT8 TdxEnabled;
UINT8 Reserved2[6];
UINT64 RandomData;
UINT64 EncryptionMask;
} SEC_SEV_ES_WORK_AREA;
The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we can be
pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
structure (if needed).
Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA before
booting the guest to ensure that its safe to access the memory without
going through the accept/validation process.
In case of the TDX, the reset vector code sets the TdxEnabled on the
entry. In case of the SEV, the workarea is valid from SEC to PEI phase
only and it gets reused for other purposes. The PEI phase set the Pcd's
(such as SevEsEnabled or SevEnabled etc) so that Dxe or other EDK2 core
does not need to know anything about the workarea and they simply can
read the PCDs.
-Brijesh
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 14:34 ` Brijesh Singh
@ 2021-07-28 15:22 ` Yao, Jiewen
2021-07-28 15:59 ` Brijesh Singh
0 siblings, 1 reply; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-28 15:22 UTC (permalink / raw)
To: devel@edk2.groups.io, brijesh.singh@amd.com, Xu, Min M
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
Hi Brijesh
Thanks!
I think if we want to reuse this, we need rename the data structure.
First, we should use a generic name.
Second, I don’t think it is good idea to define two *enable* fields. Since only one of them should be enabled, we should use 1 field as enumeration.
Third, we should hide the SEV specific and TDX specific definition in CC common work area.
If we agree to use a common work area, I recommend below:
typedef struct {
UINT8 HeaderVersion; // 0
UINT8 HeadLength; // 4
UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
UINT8 SubType; // Type specific sub type, if needed.
} CC_COMMON_WORK_AREA_HEADER;
typedef struct {
CC_COMMON_WORK_AREA_HEADER Header;
// reset is valid if Type == 1
UINT8 Reserved1[4];
UINT64 RandomData;
UINT64 EncryptionMask;
} SEC_SEV_ES_WORK_AREA;
typedef struct {
CC_COMMON_WORK_AREA_HEADER Header;
// reset is valid if Type == 2
UINT8 TdxSpecific[]; // TBD
} TDX_WORK_AREA;
Thank you
Yao Jiewen
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
> Singh via groups.io
> Sent: Wednesday, July 28, 2021 10:34 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M <min.m.xu@intel.com>
> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> Erdem Aktas <erdemaktas@google.com>; James Bottomley
> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> ResetVector
>
> Hi Jiewen and Min,
>
> See my comments below.
>
>
> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
> > Yes. I am thinking the same thing.
> >
> > [CC Flag memory location]
> > 1) A general purpose register, such as EBP.
> >
> > 2) A global variable, such as
> > .data
> > TeeFlags: DD 0
> >
> > 3) A fixed region in stack, such as
> > dword[STACK_TOP - 4]
> >
> > 4) A new CC common fixed region, such as
> > dword[CC_COMMON_FLAGS]
> >
> > 5) A fixed region piggyback on existing CC working area, such as
> > dword[CC_WORKING_AREA]
> >
> > Hi Brijesh/Min
> > Any preference?
> >
> > [CC Indicator Flags]
> > Proposal: UINT8[4]
> >
> > Byte [0] Version: 0
> > byte [1] Length: 4
> > byte [2] Type:
> > 0: legacy
> > 1: SEV
> > 2: TDX
> > byte [3] Sub Type:
> > If Type is 0 (legacy), then
> > 0: legacy
> > If Type is 1 (SEV), then
> > 0: SEV
> > 1: SEV-ES
> > 2: SEV-SNP
> > If Type is 2 (TDX), then
> > 0: TDX 1.0
> >
> > Thank you
> > Yao Jiewen
> >
> >
> >> -----Original Message-----
> >> From: Xu, Min M <min.m.xu@intel.com>
> >> Sent: Wednesday, July 28, 2021 2:58 PM
> >> To: Yao, Jiewen <jiewen.yao@intel.com>
> >> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
> >> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> >> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> James
> >> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> <thomas.lendacky@amd.com>
> >> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
> >>
> >> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
> >>> It does not necessary to be a working area.
> >>>
> >>> We just need a common TEE flag to indicate if the system run in legacy, SEV,
> >> or
> >>> TDX, right?
> >> Right. We need somewhere to store this flag, either in a Register or in
> Memory.
> >> If it is memory, then in Tdx the memory region should be initialized by host
> VMM.
> >>>
> >>> thank you!
> >>> Yao, Jiewen
> >>>
> >>>
> >>>> 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com> 写
> 道:
> >>>>
> >>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> >>>>> HI Min
> >>>>> I agree with Brijesh.
> >>>>>
> >>>>> The basic rule is: SEV file shall never refer to TDX data structure.
> >>>>> TDX file shall never refer to SEV data structure.
> >>>>> These code should be isolated clearly.
> >>>>>
> >>>>> Do we still need that logic if we follow the new pattern?
> >>>> I have replied to Brijesh's mail about the concern of the new pattern.
> >>>>
> >>>> I have some concern in the current pattern:
> >>>> ====================
> >>>> OneTimeCall PreMainFunctionHookSev
> >>>> OneTimeCall PreMainFunctionHookTdx
> >>>> MainFunction:
> >>>> XXXXXX
> >>>> OneTimeCall PostMainFunctionHookSev
> >>>> OneTimeCall PostMainFunctionHookTdx
> >>>> ====================
> >>>> The TEE function need implement a TEE check function (such as IsSev, or
> >> IsTdx).
> >>>>
> >>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the very
> >>>> beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV
> does
> >>> the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to
> 1.
> >>>>
> >>>> After that both TDX and SEV read the above WORK_AREA to check if it is
> TDX
> >>> or SEV or legacy guest.
> >>>>
> >>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error because
> >>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> >>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error
> too.
> >>>>
> >>>> I am wondering if TDX and SEV can use the same memory region (for
> >> example,
> >>> TEE_WORK_AREA) as the work area?
> >>>> So that this work area is guaranteed to be initialized in both TDX and
> >>>> SEV. Structure of the TEE_WORK_AREA may look like this:
> >>>> typedef struct {
> >>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> >>>> UINT8 Others[];
> >>>> } TEE_WORK_AREA;
> >>>>>
>
> Are we reserving a new page for the TDX_WORK_AREA ? I am wondering why
> can't we use the SEV_ES_WORK_AREA instead of wasting space in the MEMFD.
>
> The SEV_ES_WORK_AREA layout looks like this:
>
> typedef struct _SEC_SEV_ES_WORK_AREA {
> UINT8 SevEsEnabled;
> UINT8 Reserved1[7];
>
> UINT64 RandomData;
>
> UINT64 EncryptionMask;
> } SEC_SEV_ES_WORK_AREA;
>
> There is reserved bit after the SevEsEnabled and one byte can be used
> for the TdxEnabled;
>
> typedef struct _SEC_SEV_ES_WORK_AREA {
> UINT8 SevEsEnabled;
> UINT8 TdxEnabled;
> UINT8 Reserved2[6];
>
> UINT64 RandomData;
>
> UINT64 EncryptionMask;
> } SEC_SEV_ES_WORK_AREA;
>
> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we can be
> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
> structure (if needed).
>
> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA before
> booting the guest to ensure that its safe to access the memory without
> going through the accept/validation process.
>
> In case of the TDX, the reset vector code sets the TdxEnabled on the
> entry. In case of the SEV, the workarea is valid from SEC to PEI phase
> only and it gets reused for other purposes. The PEI phase set the Pcd's
> (such as SevEsEnabled or SevEnabled etc) so that Dxe or other EDK2 core
> does not need to know anything about the workarea and they simply can
> read the PCDs.
>
> -Brijesh
>
>
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 15:22 ` [edk2-devel] " Yao, Jiewen
@ 2021-07-28 15:59 ` Brijesh Singh
2021-07-28 16:26 ` Yao, Jiewen
0 siblings, 1 reply; 36+ messages in thread
From: Brijesh Singh @ 2021-07-28 15:59 UTC (permalink / raw)
To: Yao, Jiewen, devel@edk2.groups.io, Xu, Min M
Cc: brijesh.singh, Ard Biesheuvel, Justen, Jordan L, Erdem Aktas,
James Bottomley, Tom Lendacky
Hi Yao Jiewen,
I guess I am still trying to figure out why we need the header in the
work area. Can't we do something like this:
typedef struct {
UINT8 SevEsEnabled;
// If Es is enabled then this field must be zeroed
UINT8 MustBeZero;
UINT8 Reserved1[6];
UINT64 RandomData;
UINT64 EncryptionMask;
} SEC_SEV_ES_WORK_AREA;
typedef struct {
// If Tdx is enabled then it must be zeroed
UINT8 MustBeZero
UINT8 TdxEnabled;
UINT8 Reserved2[6];
....
} TX_WORK_AREA;
typedef union {
SEC_SEV_ES_WORK_AREA SevEsWorkArea;
TDX_WORK_AREA TdxWorkArea;
} CC_WORK_AREA;
I am trying to minimize the changes to the existing code. The SEV and
TDX probe logic should ensure that if the feature is detected, then it
must clear the MustBeZero'ed field.
Basically, we already have a 64-bit value reserved in the SevEsWork area
and currently only one byte is used and second byte can be detected for
the TDX. Basically the which encryption technology is active the
definition of the work area will change.
Am I missing something ?
Thanks
On 7/28/21 10:22 AM, Yao, Jiewen wrote:
> Hi Brijesh
> Thanks!
>
> I think if we want to reuse this, we need rename the data structure.
>
> First, we should use a generic name.
>
> Second, I don’t think it is good idea to define two *enable* fields. Since only one of them should be enabled, we should use 1 field as enumeration.
>
> Third, we should hide the SEV specific and TDX specific definition in CC common work area.
>
> If we agree to use a common work area, I recommend below:
>
> typedef struct {
> UINT8 HeaderVersion; // 0
> UINT8 HeadLength; // 4
> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
> UINT8 SubType; // Type specific sub type, if needed.
> } CC_COMMON_WORK_AREA_HEADER;
>
> typedef struct {
> CC_COMMON_WORK_AREA_HEADER Header;
> // reset is valid if Type == 1
> UINT8 Reserved1[4];
> UINT64 RandomData;
> UINT64 EncryptionMask;
> } SEC_SEV_ES_WORK_AREA;
>
> typedef struct {
> CC_COMMON_WORK_AREA_HEADER Header;
> // reset is valid if Type == 2
> UINT8 TdxSpecific[]; // TBD
> } TDX_WORK_AREA;
>
> Thank you
> Yao Jiewen
>
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
>> Singh via groups.io
>> Sent: Wednesday, July 28, 2021 10:34 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M <min.m.xu@intel.com>
>> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
>> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
>> Erdem Aktas <erdemaktas@google.com>; James Bottomley
>> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>> ResetVector
>>
>> Hi Jiewen and Min,
>>
>> See my comments below.
>>
>>
>> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
>>> Yes. I am thinking the same thing.
>>>
>>> [CC Flag memory location]
>>> 1) A general purpose register, such as EBP.
>>>
>>> 2) A global variable, such as
>>> .data
>>> TeeFlags: DD 0
>>>
>>> 3) A fixed region in stack, such as
>>> dword[STACK_TOP - 4]
>>>
>>> 4) A new CC common fixed region, such as
>>> dword[CC_COMMON_FLAGS]
>>>
>>> 5) A fixed region piggyback on existing CC working area, such as
>>> dword[CC_WORKING_AREA]
>>>
>>> Hi Brijesh/Min
>>> Any preference?
>>>
>>> [CC Indicator Flags]
>>> Proposal: UINT8[4]
>>>
>>> Byte [0] Version: 0
>>> byte [1] Length: 4
>>> byte [2] Type:
>>> 0: legacy
>>> 1: SEV
>>> 2: TDX
>>> byte [3] Sub Type:
>>> If Type is 0 (legacy), then
>>> 0: legacy
>>> If Type is 1 (SEV), then
>>> 0: SEV
>>> 1: SEV-ES
>>> 2: SEV-SNP
>>> If Type is 2 (TDX), then
>>> 0: TDX 1.0
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>
>>>> -----Original Message-----
>>>> From: Xu, Min M <min.m.xu@intel.com>
>>>> Sent: Wednesday, July 28, 2021 2:58 PM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>
>>>> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
>>>> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
>>>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
>> James
>>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
>> <thomas.lendacky@amd.com>
>>>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
>>>>
>>>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
>>>>> It does not necessary to be a working area.
>>>>>
>>>>> We just need a common TEE flag to indicate if the system run in legacy, SEV,
>>>> or
>>>>> TDX, right?
>>>> Right. We need somewhere to store this flag, either in a Register or in
>> Memory.
>>>> If it is memory, then in Tdx the memory region should be initialized by host
>> VMM.
>>>>>
>>>>> thank you!
>>>>> Yao, Jiewen
>>>>>
>>>>>
>>>>>> 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com> 写
>> 道:
>>>>>>
>>>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
>>>>>>> HI Min
>>>>>>> I agree with Brijesh.
>>>>>>>
>>>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
>>>>>>> TDX file shall never refer to SEV data structure.
>>>>>>> These code should be isolated clearly.
>>>>>>>
>>>>>>> Do we still need that logic if we follow the new pattern?
>>>>>> I have replied to Brijesh's mail about the concern of the new pattern.
>>>>>>
>>>>>> I have some concern in the current pattern:
>>>>>> ====================
>>>>>> OneTimeCall PreMainFunctionHookSev
>>>>>> OneTimeCall PreMainFunctionHookTdx
>>>>>> MainFunction:
>>>>>> XXXXXX
>>>>>> OneTimeCall PostMainFunctionHookSev
>>>>>> OneTimeCall PostMainFunctionHookTdx
>>>>>> ====================
>>>>>> The TEE function need implement a TEE check function (such as IsSev, or
>>>> IsTdx).
>>>>>>
>>>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the very
>>>>>> beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV
>> does
>>>>> the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to
>> 1.
>>>>>>
>>>>>> After that both TDX and SEV read the above WORK_AREA to check if it is
>> TDX
>>>>> or SEV or legacy guest.
>>>>>>
>>>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error because
>>>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
>>>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error
>> too.
>>>>>>
>>>>>> I am wondering if TDX and SEV can use the same memory region (for
>>>> example,
>>>>> TEE_WORK_AREA) as the work area?
>>>>>> So that this work area is guaranteed to be initialized in both TDX and
>>>>>> SEV. Structure of the TEE_WORK_AREA may look like this:
>>>>>> typedef struct {
>>>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
>>>>>> UINT8 Others[];
>>>>>> } TEE_WORK_AREA;
>>>>>>>
>>
>> Are we reserving a new page for the TDX_WORK_AREA ? I am wondering why
>> can't we use the SEV_ES_WORK_AREA instead of wasting space in the MEMFD.
>>
>> The SEV_ES_WORK_AREA layout looks like this:
>>
>> typedef struct _SEC_SEV_ES_WORK_AREA {
>> UINT8 SevEsEnabled;
>> UINT8 Reserved1[7];
>>
>> UINT64 RandomData;
>>
>> UINT64 EncryptionMask;
>> } SEC_SEV_ES_WORK_AREA;
>>
>> There is reserved bit after the SevEsEnabled and one byte can be used
>> for the TdxEnabled;
>>
>> typedef struct _SEC_SEV_ES_WORK_AREA {
>> UINT8 SevEsEnabled;
>> UINT8 TdxEnabled;
>> UINT8 Reserved2[6];
>>
>> UINT64 RandomData;
>>
>> UINT64 EncryptionMask;
>> } SEC_SEV_ES_WORK_AREA;
>>
>> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we can be
>> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
>> structure (if needed).
>>
>> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA before
>> booting the guest to ensure that its safe to access the memory without
>> going through the accept/validation process.
>>
>> In case of the TDX, the reset vector code sets the TdxEnabled on the
>> entry. In case of the SEV, the workarea is valid from SEC to PEI phase
>> only and it gets reused for other purposes. The PEI phase set the Pcd's
>> (such as SevEsEnabled or SevEnabled etc) so that Dxe or other EDK2 core
>> does not need to know anything about the workarea and they simply can
>> read the PCDs.
>>
>> -Brijesh
>>
>>
>>
>>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 15:59 ` Brijesh Singh
@ 2021-07-28 16:26 ` Yao, Jiewen
2021-07-28 18:58 ` Brijesh Singh
0 siblings, 1 reply; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-28 16:26 UTC (permalink / raw)
To: devel@edk2.groups.io, brijesh.singh@amd.com, Xu, Min M
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
I would say it is NOT the best software practice to define 2 enable fields to indicate one type.
What if some software error, set both TdxEnable and SevEnable at same time?
How do you detect such error?
If some code may check the SEV only, and some code may check TDX only, then both code can run. That will be a disaster. The code is hard to maintain and hard to debug.
Another consideration is: what if someone wants to set the third type?
Do we want to reserve the 3rd byte? To indicate the third type? It is not scalable.
The best software practice it to just define one field as enumeration. So any software can only set Tdx or Sev. The result is consistent, no matter you check the SEV at first or TDX at first.
If there is 3rd bytes, we just need assign the 3rd value to it, without impact any other field.
I think we can add "must zero" in the comment. But it does not mean there will be no error during development.
UNION is not a type safe structure. Usually, the consumer of UNION shall refer to a common field to know what the type of union is - I treat that as header.
Since we are defining a common data structure, I think we can do some clean up.
Or if you have concern to change SEC_SEV_ES_WORK_AREA, we can define a new CC WORK_AREA without touching SEV_SEV_ES_WORKING_AREA.
Thank you
Yao Jiewen
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
> Singh via groups.io
> Sent: Wednesday, July 28, 2021 11:59 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu, Min M
> <min.m.xu@intel.com>
> Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
> Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
> Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> ResetVector
>
> Hi Yao Jiewen,
>
> I guess I am still trying to figure out why we need the header in the
> work area. Can't we do something like this:
>
> typedef struct {
> UINT8 SevEsEnabled;
>
> // If Es is enabled then this field must be zeroed
> UINT8 MustBeZero;
>
> UINT8 Reserved1[6];
>
> UINT64 RandomData;
>
> UINT64 EncryptionMask;
> } SEC_SEV_ES_WORK_AREA;
>
> typedef struct {
> // If Tdx is enabled then it must be zeroed
> UINT8 MustBeZero
>
> UINT8 TdxEnabled;
>
> UINT8 Reserved2[6];
> ....
>
> } TX_WORK_AREA;
>
> typedef union {
> SEC_SEV_ES_WORK_AREA SevEsWorkArea;
> TDX_WORK_AREA TdxWorkArea;
> } CC_WORK_AREA;
>
> I am trying to minimize the changes to the existing code. The SEV and
> TDX probe logic should ensure that if the feature is detected, then it
> must clear the MustBeZero'ed field.
>
> Basically, we already have a 64-bit value reserved in the SevEsWork area
> and currently only one byte is used and second byte can be detected for
> the TDX. Basically the which encryption technology is active the
> definition of the work area will change.
>
> Am I missing something ?
>
> Thanks
>
> On 7/28/21 10:22 AM, Yao, Jiewen wrote:
> > Hi Brijesh
> > Thanks!
> >
> > I think if we want to reuse this, we need rename the data structure.
> >
> > First, we should use a generic name.
> >
> > Second, I don’t think it is good idea to define two *enable* fields. Since only
> one of them should be enabled, we should use 1 field as enumeration.
> >
> > Third, we should hide the SEV specific and TDX specific definition in CC
> common work area.
> >
> > If we agree to use a common work area, I recommend below:
> >
> > typedef struct {
> > UINT8 HeaderVersion; // 0
> > UINT8 HeadLength; // 4
> > UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
> > UINT8 SubType; // Type specific sub type, if needed.
> > } CC_COMMON_WORK_AREA_HEADER;
> >
> > typedef struct {
> > CC_COMMON_WORK_AREA_HEADER Header;
> > // reset is valid if Type == 1
> > UINT8 Reserved1[4];
> > UINT64 RandomData;
> > UINT64 EncryptionMask;
> > } SEC_SEV_ES_WORK_AREA;
> >
> > typedef struct {
> > CC_COMMON_WORK_AREA_HEADER Header;
> > // reset is valid if Type == 2
> > UINT8 TdxSpecific[]; // TBD
> > } TDX_WORK_AREA;
> >
> > Thank you
> > Yao Jiewen
> >
> >> -----Original Message-----
> >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
> >> Singh via groups.io
> >> Sent: Wednesday, July 28, 2021 10:34 PM
> >> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M <min.m.xu@intel.com>
> >> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
> >> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
> >> Erdem Aktas <erdemaktas@google.com>; James Bottomley
> >> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> >> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> >> ResetVector
> >>
> >> Hi Jiewen and Min,
> >>
> >> See my comments below.
> >>
> >>
> >> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
> >>> Yes. I am thinking the same thing.
> >>>
> >>> [CC Flag memory location]
> >>> 1) A general purpose register, such as EBP.
> >>>
> >>> 2) A global variable, such as
> >>> .data
> >>> TeeFlags: DD 0
> >>>
> >>> 3) A fixed region in stack, such as
> >>> dword[STACK_TOP - 4]
> >>>
> >>> 4) A new CC common fixed region, such as
> >>> dword[CC_COMMON_FLAGS]
> >>>
> >>> 5) A fixed region piggyback on existing CC working area, such as
> >>> dword[CC_WORKING_AREA]
> >>>
> >>> Hi Brijesh/Min
> >>> Any preference?
> >>>
> >>> [CC Indicator Flags]
> >>> Proposal: UINT8[4]
> >>>
> >>> Byte [0] Version: 0
> >>> byte [1] Length: 4
> >>> byte [2] Type:
> >>> 0: legacy
> >>> 1: SEV
> >>> 2: TDX
> >>> byte [3] Sub Type:
> >>> If Type is 0 (legacy), then
> >>> 0: legacy
> >>> If Type is 1 (SEV), then
> >>> 0: SEV
> >>> 1: SEV-ES
> >>> 2: SEV-SNP
> >>> If Type is 2 (TDX), then
> >>> 0: TDX 1.0
> >>>
> >>> Thank you
> >>> Yao Jiewen
> >>>
> >>>
> >>>> -----Original Message-----
> >>>> From: Xu, Min M <min.m.xu@intel.com>
> >>>> Sent: Wednesday, July 28, 2021 2:58 PM
> >>>> To: Yao, Jiewen <jiewen.yao@intel.com>
> >>>> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
> >>>> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> >>>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> >> James
> >>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> >> <thomas.lendacky@amd.com>
> >>>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
> >>>>
> >>>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
> >>>>> It does not necessary to be a working area.
> >>>>>
> >>>>> We just need a common TEE flag to indicate if the system run in legacy,
> SEV,
> >>>> or
> >>>>> TDX, right?
> >>>> Right. We need somewhere to store this flag, either in a Register or in
> >> Memory.
> >>>> If it is memory, then in Tdx the memory region should be initialized by host
> >> VMM.
> >>>>>
> >>>>> thank you!
> >>>>> Yao, Jiewen
> >>>>>
> >>>>>
> >>>>>> 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com>
> 写
> >> 道:
> >>>>>>
> >>>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> >>>>>>> HI Min
> >>>>>>> I agree with Brijesh.
> >>>>>>>
> >>>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
> >>>>>>> TDX file shall never refer to SEV data structure.
> >>>>>>> These code should be isolated clearly.
> >>>>>>>
> >>>>>>> Do we still need that logic if we follow the new pattern?
> >>>>>> I have replied to Brijesh's mail about the concern of the new pattern.
> >>>>>>
> >>>>>> I have some concern in the current pattern:
> >>>>>> ====================
> >>>>>> OneTimeCall PreMainFunctionHookSev
> >>>>>> OneTimeCall PreMainFunctionHookTdx
> >>>>>> MainFunction:
> >>>>>> XXXXXX
> >>>>>> OneTimeCall PostMainFunctionHookSev
> >>>>>> OneTimeCall PostMainFunctionHookTdx
> >>>>>> ====================
> >>>>>> The TEE function need implement a TEE check function (such as IsSev, or
> >>>> IsTdx).
> >>>>>>
> >>>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the very
> >>>>>> beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV
> >> does
> >>>>> the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA
> to
> >> 1.
> >>>>>>
> >>>>>> After that both TDX and SEV read the above WORK_AREA to check if it is
> >> TDX
> >>>>> or SEV or legacy guest.
> >>>>>>
> >>>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error because
> >>>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> >>>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error
> >> too.
> >>>>>>
> >>>>>> I am wondering if TDX and SEV can use the same memory region (for
> >>>> example,
> >>>>> TEE_WORK_AREA) as the work area?
> >>>>>> So that this work area is guaranteed to be initialized in both TDX and
> >>>>>> SEV. Structure of the TEE_WORK_AREA may look like this:
> >>>>>> typedef struct {
> >>>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> >>>>>> UINT8 Others[];
> >>>>>> } TEE_WORK_AREA;
> >>>>>>>
> >>
> >> Are we reserving a new page for the TDX_WORK_AREA ? I am wondering why
> >> can't we use the SEV_ES_WORK_AREA instead of wasting space in the
> MEMFD.
> >>
> >> The SEV_ES_WORK_AREA layout looks like this:
> >>
> >> typedef struct _SEC_SEV_ES_WORK_AREA {
> >> UINT8 SevEsEnabled;
> >> UINT8 Reserved1[7];
> >>
> >> UINT64 RandomData;
> >>
> >> UINT64 EncryptionMask;
> >> } SEC_SEV_ES_WORK_AREA;
> >>
> >> There is reserved bit after the SevEsEnabled and one byte can be used
> >> for the TdxEnabled;
> >>
> >> typedef struct _SEC_SEV_ES_WORK_AREA {
> >> UINT8 SevEsEnabled;
> >> UINT8 TdxEnabled;
> >> UINT8 Reserved2[6];
> >>
> >> UINT64 RandomData;
> >>
> >> UINT64 EncryptionMask;
> >> } SEC_SEV_ES_WORK_AREA;
> >>
> >> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we can
> be
> >> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
> >> structure (if needed).
> >>
> >> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA before
> >> booting the guest to ensure that its safe to access the memory without
> >> going through the accept/validation process.
> >>
> >> In case of the TDX, the reset vector code sets the TdxEnabled on the
> >> entry. In case of the SEV, the workarea is valid from SEC to PEI phase
> >> only and it gets reused for other purposes. The PEI phase set the Pcd's
> >> (such as SevEsEnabled or SevEnabled etc) so that Dxe or other EDK2 core
> >> does not need to know anything about the workarea and they simply can
> >> read the PCDs.
> >>
> >> -Brijesh
> >>
> >>
> >>
> >>
> >
>
>
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 16:26 ` Yao, Jiewen
@ 2021-07-28 18:58 ` Brijesh Singh
2021-07-28 23:48 ` Yao, Jiewen
0 siblings, 1 reply; 36+ messages in thread
From: Brijesh Singh @ 2021-07-28 18:58 UTC (permalink / raw)
To: Yao, Jiewen, devel@edk2.groups.io, Xu, Min M
Cc: brijesh.singh, Ard Biesheuvel, Justen, Jordan L, Erdem Aktas,
James Bottomley, Tom Lendacky
On 7/28/21 11:26 AM, Yao, Jiewen wrote:
> I would say it is NOT the best software practice to define 2 enable fields to indicate one type.
>
> What if some software error, set both TdxEnable and SevEnable at same time?
> How do you detect such error?
Hmm, aren't we saying it is a software bug. In that case another bug can
also mess up the structure header.
>
> If some code may check the SEV only, and some code may check TDX only, then both code can run. That will be a disaster. The code is hard to maintain and hard to debug.
>
> Another consideration is: what if someone wants to set the third type?
> Do we want to reserve the 3rd byte? To indicate the third type? It is not scalable.
>
> The best software practice it to just define one field as enumeration. So any software can only set Tdx or Sev. The result is consistent, no matter you check the SEV at first or TDX at first.
> If there is 3rd bytes, we just need assign the 3rd value to it, without impact any other field.
>
I was trying to see if we can make it work without requiring any changes
to UefiCpuPkg etc (which uses the EsWorkArea).
>
> I think we can add "must zero" in the comment. But it does not mean there will be no error during development.
>
> UNION is not a type safe structure. Usually, the consumer of UNION shall refer to a common field to know what the type of union is - I treat that as header.
>
> Since we are defining a common data structure, I think we can do some clean up.
> Or if you have concern to change SEC_SEV_ES_WORK_AREA, we can define a new CC WORK_AREA without touching SEV_SEV_ES_WORKING_AREA.
>
In your below structure, I assume the SEV or TDX probe will set the Type
only when it detects that feature is active. But which layer of the
software is going to set the type to zero to indicate its a legacy guest ?
typedef struct {
UINT8 HeaderVersion; // 0
UINT8 HeadLength; // 4
UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
UINT8 SubType; // Type specific sub type, if needed.
} CC_COMMON_WORK_AREA_HEADER;
i.e In this call sequence:
OneTimeCall PreMainFunctionHookSev
OneTimeCall PreMainFunctionHookTdx
....
The PreMainFunctionHookSev will detect whether SEV is active or not. If
its active then set the type = MEM_ENCRYPT_SEV; and similarly the Tdx
hook will set the type=MEM_ENCRYPT_TDX. What if neither TDX or SEV is
enabled. At this time we will depend on hypervisor to ensure that value
at that memory is zero.
Additionally, do you see a need for the HeadLength field in this
structure ? In asm it is preferred if we can access the actual workarea
pointer at the fixed location instead of first reading the HeadLength to
determine how much it need to skip.
> Thank you
> Yao Jiewen
>
>
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
>> Singh via groups.io
>> Sent: Wednesday, July 28, 2021 11:59 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu, Min M
>> <min.m.xu@intel.com>
>> Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
>> Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
>> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
>> Lendacky <thomas.lendacky@amd.com>
>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>> ResetVector
>>
>> Hi Yao Jiewen,
>>
>> I guess I am still trying to figure out why we need the header in the
>> work area. Can't we do something like this:
>>
>> typedef struct {
>> UINT8 SevEsEnabled;
>>
>> // If Es is enabled then this field must be zeroed
>> UINT8 MustBeZero;
>>
>> UINT8 Reserved1[6];
>>
>> UINT64 RandomData;
>>
>> UINT64 EncryptionMask;
>> } SEC_SEV_ES_WORK_AREA;
>>
>> typedef struct {
>> // If Tdx is enabled then it must be zeroed
>> UINT8 MustBeZero
>>
>> UINT8 TdxEnabled;
>>
>> UINT8 Reserved2[6];
>> ....
>>
>> } TX_WORK_AREA;
>>
>> typedef union {
>> SEC_SEV_ES_WORK_AREA SevEsWorkArea;
>> TDX_WORK_AREA TdxWorkArea;
>> } CC_WORK_AREA;
>>
>> I am trying to minimize the changes to the existing code. The SEV and
>> TDX probe logic should ensure that if the feature is detected, then it
>> must clear the MustBeZero'ed field.
>>
>> Basically, we already have a 64-bit value reserved in the SevEsWork area
>> and currently only one byte is used and second byte can be detected for
>> the TDX. Basically the which encryption technology is active the
>> definition of the work area will change.
>>
>> Am I missing something ?
>>
>> Thanks
>>
>> On 7/28/21 10:22 AM, Yao, Jiewen wrote:
>>> Hi Brijesh
>>> Thanks!
>>>
>>> I think if we want to reuse this, we need rename the data structure.
>>>
>>> First, we should use a generic name.
>>>
>>> Second, I don’t think it is good idea to define two *enable* fields. Since only
>> one of them should be enabled, we should use 1 field as enumeration.
>>>
>>> Third, we should hide the SEV specific and TDX specific definition in CC
>> common work area.
>>>
>>> If we agree to use a common work area, I recommend below:
>>>
>>> typedef struct {
>>> UINT8 HeaderVersion; // 0
>>> UINT8 HeadLength; // 4
>>> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
>>> UINT8 SubType; // Type specific sub type, if needed.
>>> } CC_COMMON_WORK_AREA_HEADER;
>>>
>>> typedef struct {
>>> CC_COMMON_WORK_AREA_HEADER Header;
>>> // reset is valid if Type == 1
>>> UINT8 Reserved1[4];
>>> UINT64 RandomData;
>>> UINT64 EncryptionMask;
>>> } SEC_SEV_ES_WORK_AREA;
>>>
>>> typedef struct {
>>> CC_COMMON_WORK_AREA_HEADER Header;
>>> // reset is valid if Type == 2
>>> UINT8 TdxSpecific[]; // TBD
>>> } TDX_WORK_AREA;
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
>>>> Singh via groups.io
>>>> Sent: Wednesday, July 28, 2021 10:34 PM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M <min.m.xu@intel.com>
>>>> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L <jordan.l.justen@intel.com>;
>>>> Erdem Aktas <erdemaktas@google.com>; James Bottomley
>>>> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>>>> ResetVector
>>>>
>>>> Hi Jiewen and Min,
>>>>
>>>> See my comments below.
>>>>
>>>>
>>>> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
>>>>> Yes. I am thinking the same thing.
>>>>>
>>>>> [CC Flag memory location]
>>>>> 1) A general purpose register, such as EBP.
>>>>>
>>>>> 2) A global variable, such as
>>>>> .data
>>>>> TeeFlags: DD 0
>>>>>
>>>>> 3) A fixed region in stack, such as
>>>>> dword[STACK_TOP - 4]
>>>>>
>>>>> 4) A new CC common fixed region, such as
>>>>> dword[CC_COMMON_FLAGS]
>>>>>
>>>>> 5) A fixed region piggyback on existing CC working area, such as
>>>>> dword[CC_WORKING_AREA]
>>>>>
>>>>> Hi Brijesh/Min
>>>>> Any preference?
>>>>>
>>>>> [CC Indicator Flags]
>>>>> Proposal: UINT8[4]
>>>>>
>>>>> Byte [0] Version: 0
>>>>> byte [1] Length: 4
>>>>> byte [2] Type:
>>>>> 0: legacy
>>>>> 1: SEV
>>>>> 2: TDX
>>>>> byte [3] Sub Type:
>>>>> If Type is 0 (legacy), then
>>>>> 0: legacy
>>>>> If Type is 1 (SEV), then
>>>>> 0: SEV
>>>>> 1: SEV-ES
>>>>> 2: SEV-SNP
>>>>> If Type is 2 (TDX), then
>>>>> 0: TDX 1.0
>>>>>
>>>>> Thank you
>>>>> Yao Jiewen
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Xu, Min M <min.m.xu@intel.com>
>>>>>> Sent: Wednesday, July 28, 2021 2:58 PM
>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>
>>>>>> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
>>>>>> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
>>>>>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
>>>> James
>>>>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
>>>> <thomas.lendacky@amd.com>
>>>>>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
>>>>>>
>>>>>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
>>>>>>> It does not necessary to be a working area.
>>>>>>>
>>>>>>> We just need a common TEE flag to indicate if the system run in legacy,
>> SEV,
>>>>>> or
>>>>>>> TDX, right?
>>>>>> Right. We need somewhere to store this flag, either in a Register or in
>>>> Memory.
>>>>>> If it is memory, then in Tdx the memory region should be initialized by host
>>>> VMM.
>>>>>>>
>>>>>>> thank you!
>>>>>>> Yao, Jiewen
>>>>>>>
>>>>>>>
>>>>>>>> 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com>
>> 写
>>>> 道:
>>>>>>>>
>>>>>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
>>>>>>>>> HI Min
>>>>>>>>> I agree with Brijesh.
>>>>>>>>>
>>>>>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
>>>>>>>>> TDX file shall never refer to SEV data structure.
>>>>>>>>> These code should be isolated clearly.
>>>>>>>>>
>>>>>>>>> Do we still need that logic if we follow the new pattern?
>>>>>>>> I have replied to Brijesh's mail about the concern of the new pattern.
>>>>>>>>
>>>>>>>> I have some concern in the current pattern:
>>>>>>>> ====================
>>>>>>>> OneTimeCall PreMainFunctionHookSev
>>>>>>>> OneTimeCall PreMainFunctionHookTdx
>>>>>>>> MainFunction:
>>>>>>>> XXXXXX
>>>>>>>> OneTimeCall PostMainFunctionHookSev
>>>>>>>> OneTimeCall PostMainFunctionHookTdx
>>>>>>>> ====================
>>>>>>>> The TEE function need implement a TEE check function (such as IsSev, or
>>>>>> IsTdx).
>>>>>>>>
>>>>>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the very
>>>>>>>> beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA. SEV
>>>> does
>>>>>>> the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA
>> to
>>>> 1.
>>>>>>>>
>>>>>>>> After that both TDX and SEV read the above WORK_AREA to check if it is
>>>> TDX
>>>>>>> or SEV or legacy guest.
>>>>>>>>
>>>>>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error because
>>>>>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
>>>>>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error
>>>> too.
>>>>>>>>
>>>>>>>> I am wondering if TDX and SEV can use the same memory region (for
>>>>>> example,
>>>>>>> TEE_WORK_AREA) as the work area?
>>>>>>>> So that this work area is guaranteed to be initialized in both TDX and
>>>>>>>> SEV. Structure of the TEE_WORK_AREA may look like this:
>>>>>>>> typedef struct {
>>>>>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
>>>>>>>> UINT8 Others[];
>>>>>>>> } TEE_WORK_AREA;
>>>>>>>>>
>>>>
>>>> Are we reserving a new page for the TDX_WORK_AREA ? I am wondering why
>>>> can't we use the SEV_ES_WORK_AREA instead of wasting space in the
>> MEMFD.
>>>>
>>>> The SEV_ES_WORK_AREA layout looks like this:
>>>>
>>>> typedef struct _SEC_SEV_ES_WORK_AREA {
>>>> UINT8 SevEsEnabled;
>>>> UINT8 Reserved1[7];
>>>>
>>>> UINT64 RandomData;
>>>>
>>>> UINT64 EncryptionMask;
>>>> } SEC_SEV_ES_WORK_AREA;
>>>>
>>>> There is reserved bit after the SevEsEnabled and one byte can be used
>>>> for the TdxEnabled;
>>>>
>>>> typedef struct _SEC_SEV_ES_WORK_AREA {
>>>> UINT8 SevEsEnabled;
>>>> UINT8 TdxEnabled;
>>>> UINT8 Reserved2[6];
>>>>
>>>> UINT64 RandomData;
>>>>
>>>> UINT64 EncryptionMask;
>>>> } SEC_SEV_ES_WORK_AREA;
>>>>
>>>> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we can
>> be
>>>> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
>>>> structure (if needed).
>>>>
>>>> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA before
>>>> booting the guest to ensure that its safe to access the memory without
>>>> going through the accept/validation process.
>>>>
>>>> In case of the TDX, the reset vector code sets the TdxEnabled on the
>>>> entry. In case of the SEV, the workarea is valid from SEC to PEI phase
>>>> only and it gets reused for other purposes. The PEI phase set the Pcd's
>>>> (such as SevEsEnabled or SevEnabled etc) so that Dxe or other EDK2 core
>>>> does not need to know anything about the workarea and they simply can
>>>> read the PCDs.
>>>>
>>>> -Brijesh
>>>>
>>>>
>>>>
>>>>
>>>
>>
>>
>>
>>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 18:58 ` Brijesh Singh
@ 2021-07-28 23:48 ` Yao, Jiewen
2021-07-29 2:44 ` Min Xu
0 siblings, 1 reply; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-28 23:48 UTC (permalink / raw)
To: Brijesh Singh, devel@edk2.groups.io, Xu, Min M
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
Comment below:
> -----Original Message-----
> From: Brijesh Singh <brijesh.singh@amd.com>
> Sent: Thursday, July 29, 2021 2:59 AM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu, Min M
> <min.m.xu@intel.com>
> Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
> Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
> Lendacky <thomas.lendacky@amd.com>
> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> ResetVector
>
>
>
> On 7/28/21 11:26 AM, Yao, Jiewen wrote:
> > I would say it is NOT the best software practice to define 2 enable fields to
> indicate one type.
> >
> > What if some software error, set both TdxEnable and SevEnable at same time?
> > How do you detect such error?
>
> Hmm, aren't we saying it is a software bug. In that case another bug can
> also mess up the structure header.
[Jiewen] Yes. I treat it as a software implementation bug.
The key thing in software design is to avoid the developer making mistake, help debug issues, help maintain the code easily.
>
> >
> > If some code may check the SEV only, and some code may check TDX only,
> then both code can run. That will be a disaster. The code is hard to maintain and
> hard to debug.
> >
> > Another consideration is: what if someone wants to set the third type?
> > Do we want to reserve the 3rd byte? To indicate the third type? It is not
> scalable.
> >
> > The best software practice it to just define one field as enumeration. So any
> software can only set Tdx or Sev. The result is consistent, no matter you check
> the SEV at first or TDX at first.
> > If there is 3rd bytes, we just need assign the 3rd value to it, without impact any
> other field.
> >
>
> I was trying to see if we can make it work without requiring any changes
> to UefiCpuPkg etc (which uses the EsWorkArea).
[Jiewen] I agree with you.
And I think the priority should be:
1) make a good design following the best practice.
2) minimize the change.
I don’t think two *enable* fields can satisfy #1.
And I am open on how to do #2. (See rest comment below)
>
>
> >
> > I think we can add "must zero" in the comment. But it does not mean there will
> be no error during development.
> >
> > UNION is not a type safe structure. Usually, the consumer of UNION shall
> refer to a common field to know what the type of union is - I treat that as
> header.
> >
> > Since we are defining a common data structure, I think we can do some clean
> up.
> > Or if you have concern to change SEC_SEV_ES_WORK_AREA, we can define a
> new CC WORK_AREA without touching SEV_SEV_ES_WORKING_AREA.
> >
>
> In your below structure, I assume the SEV or TDX probe will set the Type
> only when it detects that feature is active. But which layer of the
> software is going to set the type to zero to indicate its a legacy guest ?
[Jiewen] Good question.
I expect some initialization function, such as InitCcWorkAreaSev, InitCcWorkAreaTdx.
The default value Legacy shall be override in InitCcWorkArea after capability check.
PreMainFunctionHookXXX is common patter for all function. It just checks the CcWorkArea.
>
> typedef struct {
> UINT8 HeaderVersion; // 0
> UINT8 HeadLength; // 4
> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
> UINT8 SubType; // Type specific sub type, if needed.
> } CC_COMMON_WORK_AREA_HEADER;
>
> i.e In this call sequence:
>
> OneTimeCall PreMainFunctionHookSev
> OneTimeCall PreMainFunctionHookTdx
>
> ....
>
> The PreMainFunctionHookSev will detect whether SEV is active or not. If
> its active then set the type = MEM_ENCRYPT_SEV; and similarly the Tdx
> hook will set the type=MEM_ENCRYPT_TDX. What if neither TDX or SEV is
> enabled. At this time we will depend on hypervisor to ensure that value
> at that memory is zero.
[Jiewen] I think we just let InitCcWorkAreaSev and InitCcWorkAreaTdx override the default value.
InitCcWorkArea{Sev,Tdx}:
// Detect Hardware Capability
// If discovered, then set Type = {SEV,TDX}
// Else, leave it as is
>
> Additionally, do you see a need for the HeadLength field in this
> structure ? In asm it is preferred if we can access the actual workarea
> pointer at the fixed location instead of first reading the HeadLength to
> determine how much it need to skip.
[Jiewen] You are right.
Length/Version is NOT absolutely necessary, if the header is simple enough. If you think we don’t need them, I am OK to remove them.
I think only "Type" is mandatory, which tells us the category.
I think "SubType" is useful, because we might want to know if it is SEV, or SEV-ES, or SEV-SNP, and if it is TDX 1.0, or TDX.future. Please let me know your thought.
One question on SEC_SEV_ES_WORK_AREA. Since you have SEV/SEV-ES/SEV-SNP, is this SEV_ES_WORK_AREA only for SEV_ES? Or it is also used in SEV (no SEV_ES), and SEV_SNP ?
For example, when we see SevEsEnable=0, how do we know it is SEV (no SEV_ES) or legacy (no SEV, no SEV_ES)? Or we don’t need care in SEV (no SEV_ES) case.
>
>
> > Thank you
> > Yao Jiewen
> >
> >
> >> -----Original Message-----
> >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
> >> Singh via groups.io
> >> Sent: Wednesday, July 28, 2021 11:59 PM
> >> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu, Min M
> >> <min.m.xu@intel.com>
> >> Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
> >> Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
> >> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
> >> Lendacky <thomas.lendacky@amd.com>
> >> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> >> ResetVector
> >>
> >> Hi Yao Jiewen,
> >>
> >> I guess I am still trying to figure out why we need the header in the
> >> work area. Can't we do something like this:
> >>
> >> typedef struct {
> >> UINT8 SevEsEnabled;
> >>
> >> // If Es is enabled then this field must be zeroed
> >> UINT8 MustBeZero;
> >>
> >> UINT8 Reserved1[6];
> >>
> >> UINT64 RandomData;
> >>
> >> UINT64 EncryptionMask;
> >> } SEC_SEV_ES_WORK_AREA;
> >>
> >> typedef struct {
> >> // If Tdx is enabled then it must be zeroed
> >> UINT8 MustBeZero
> >>
> >> UINT8 TdxEnabled;
> >>
> >> UINT8 Reserved2[6];
> >> ....
> >>
> >> } TX_WORK_AREA;
> >>
> >> typedef union {
> >> SEC_SEV_ES_WORK_AREA SevEsWorkArea;
> >> TDX_WORK_AREA TdxWorkArea;
> >> } CC_WORK_AREA;
> >>
> >> I am trying to minimize the changes to the existing code. The SEV and
> >> TDX probe logic should ensure that if the feature is detected, then it
> >> must clear the MustBeZero'ed field.
> >>
> >> Basically, we already have a 64-bit value reserved in the SevEsWork area
> >> and currently only one byte is used and second byte can be detected for
> >> the TDX. Basically the which encryption technology is active the
> >> definition of the work area will change.
> >>
> >> Am I missing something ?
> >>
> >> Thanks
> >>
> >> On 7/28/21 10:22 AM, Yao, Jiewen wrote:
> >>> Hi Brijesh
> >>> Thanks!
> >>>
> >>> I think if we want to reuse this, we need rename the data structure.
> >>>
> >>> First, we should use a generic name.
> >>>
> >>> Second, I don’t think it is good idea to define two *enable* fields. Since only
> >> one of them should be enabled, we should use 1 field as enumeration.
> >>>
> >>> Third, we should hide the SEV specific and TDX specific definition in CC
> >> common work area.
> >>>
> >>> If we agree to use a common work area, I recommend below:
> >>>
> >>> typedef struct {
> >>> UINT8 HeaderVersion; // 0
> >>> UINT8 HeadLength; // 4
> >>> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
> >>> UINT8 SubType; // Type specific sub type, if needed.
> >>> } CC_COMMON_WORK_AREA_HEADER;
> >>>
> >>> typedef struct {
> >>> CC_COMMON_WORK_AREA_HEADER Header;
> >>> // reset is valid if Type == 1
> >>> UINT8 Reserved1[4];
> >>> UINT64 RandomData;
> >>> UINT64 EncryptionMask;
> >>> } SEC_SEV_ES_WORK_AREA;
> >>>
> >>> typedef struct {
> >>> CC_COMMON_WORK_AREA_HEADER Header;
> >>> // reset is valid if Type == 2
> >>> UINT8 TdxSpecific[]; // TBD
> >>> } TDX_WORK_AREA;
> >>>
> >>> Thank you
> >>> Yao Jiewen
> >>>
> >>>> -----Original Message-----
> >>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Brijesh
> >>>> Singh via groups.io
> >>>> Sent: Wednesday, July 28, 2021 10:34 PM
> >>>> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M
> <min.m.xu@intel.com>
> >>>> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
> >>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>;
> >>>> Erdem Aktas <erdemaktas@google.com>; James Bottomley
> >>>> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> >>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> >>>> ResetVector
> >>>>
> >>>> Hi Jiewen and Min,
> >>>>
> >>>> See my comments below.
> >>>>
> >>>>
> >>>> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
> >>>>> Yes. I am thinking the same thing.
> >>>>>
> >>>>> [CC Flag memory location]
> >>>>> 1) A general purpose register, such as EBP.
> >>>>>
> >>>>> 2) A global variable, such as
> >>>>> .data
> >>>>> TeeFlags: DD 0
> >>>>>
> >>>>> 3) A fixed region in stack, such as
> >>>>> dword[STACK_TOP - 4]
> >>>>>
> >>>>> 4) A new CC common fixed region, such as
> >>>>> dword[CC_COMMON_FLAGS]
> >>>>>
> >>>>> 5) A fixed region piggyback on existing CC working area, such as
> >>>>> dword[CC_WORKING_AREA]
> >>>>>
> >>>>> Hi Brijesh/Min
> >>>>> Any preference?
> >>>>>
> >>>>> [CC Indicator Flags]
> >>>>> Proposal: UINT8[4]
> >>>>>
> >>>>> Byte [0] Version: 0
> >>>>> byte [1] Length: 4
> >>>>> byte [2] Type:
> >>>>> 0: legacy
> >>>>> 1: SEV
> >>>>> 2: TDX
> >>>>> byte [3] Sub Type:
> >>>>> If Type is 0 (legacy), then
> >>>>> 0: legacy
> >>>>> If Type is 1 (SEV), then
> >>>>> 0: SEV
> >>>>> 1: SEV-ES
> >>>>> 2: SEV-SNP
> >>>>> If Type is 2 (TDX), then
> >>>>> 0: TDX 1.0
> >>>>>
> >>>>> Thank you
> >>>>> Yao Jiewen
> >>>>>
> >>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: Xu, Min M <min.m.xu@intel.com>
> >>>>>> Sent: Wednesday, July 28, 2021 2:58 PM
> >>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>
> >>>>>> Cc: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Ard
> >>>>>> Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> >>>>>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> >>>> James
> >>>>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> >>>> <thomas.lendacky@amd.com>
> >>>>>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> ResetVector
> >>>>>>
> >>>>>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
> >>>>>>> It does not necessary to be a working area.
> >>>>>>>
> >>>>>>> We just need a common TEE flag to indicate if the system run in legacy,
> >> SEV,
> >>>>>> or
> >>>>>>> TDX, right?
> >>>>>> Right. We need somewhere to store this flag, either in a Register or in
> >>>> Memory.
> >>>>>> If it is memory, then in Tdx the memory region should be initialized by
> host
> >>>> VMM.
> >>>>>>>
> >>>>>>> thank you!
> >>>>>>> Yao, Jiewen
> >>>>>>>
> >>>>>>>
> >>>>>>>> 在 2021年7月28日,下午1:07,Xu, Min M <min.m.xu@intel.com>
> >> 写
> >>>> 道:
> >>>>>>>>
> >>>>>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> >>>>>>>>> HI Min
> >>>>>>>>> I agree with Brijesh.
> >>>>>>>>>
> >>>>>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
> >>>>>>>>> TDX file shall never refer to SEV data structure.
> >>>>>>>>> These code should be isolated clearly.
> >>>>>>>>>
> >>>>>>>>> Do we still need that logic if we follow the new pattern?
> >>>>>>>> I have replied to Brijesh's mail about the concern of the new pattern.
> >>>>>>>>
> >>>>>>>> I have some concern in the current pattern:
> >>>>>>>> ====================
> >>>>>>>> OneTimeCall PreMainFunctionHookSev
> >>>>>>>> OneTimeCall PreMainFunctionHookTdx
> >>>>>>>> MainFunction:
> >>>>>>>> XXXXXX
> >>>>>>>> OneTimeCall PostMainFunctionHookSev
> >>>>>>>> OneTimeCall PostMainFunctionHookTdx
> >>>>>>>> ====================
> >>>>>>>> The TEE function need implement a TEE check function (such as IsSev,
> or
> >>>>>> IsTdx).
> >>>>>>>>
> >>>>>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the very
> >>>>>>>> beginning of ResetVector. Then 'TDXG' is set in TDX_WORK_AREA.
> SEV
> >>>> does
> >>>>>>> the similar work which call CheckSevFeatures to set
> SEV_ES_WORK_AREA
> >> to
> >>>> 1.
> >>>>>>>>
> >>>>>>>> After that both TDX and SEV read the above WORK_AREA to check if it
> is
> >>>> TDX
> >>>>>>> or SEV or legacy guest.
> >>>>>>>>
> >>>>>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error because
> >>>>>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> >>>>>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger
> error
> >>>> too.
> >>>>>>>>
> >>>>>>>> I am wondering if TDX and SEV can use the same memory region (for
> >>>>>> example,
> >>>>>>> TEE_WORK_AREA) as the work area?
> >>>>>>>> So that this work area is guaranteed to be initialized in both TDX and
> >>>>>>>> SEV. Structure of the TEE_WORK_AREA may look like this:
> >>>>>>>> typedef struct {
> >>>>>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> >>>>>>>> UINT8 Others[];
> >>>>>>>> } TEE_WORK_AREA;
> >>>>>>>>>
> >>>>
> >>>> Are we reserving a new page for the TDX_WORK_AREA ? I am wondering
> why
> >>>> can't we use the SEV_ES_WORK_AREA instead of wasting space in the
> >> MEMFD.
> >>>>
> >>>> The SEV_ES_WORK_AREA layout looks like this:
> >>>>
> >>>> typedef struct _SEC_SEV_ES_WORK_AREA {
> >>>> UINT8 SevEsEnabled;
> >>>> UINT8 Reserved1[7];
> >>>>
> >>>> UINT64 RandomData;
> >>>>
> >>>> UINT64 EncryptionMask;
> >>>> } SEC_SEV_ES_WORK_AREA;
> >>>>
> >>>> There is reserved bit after the SevEsEnabled and one byte can be used
> >>>> for the TdxEnabled;
> >>>>
> >>>> typedef struct _SEC_SEV_ES_WORK_AREA {
> >>>> UINT8 SevEsEnabled;
> >>>> UINT8 TdxEnabled;
> >>>> UINT8 Reserved2[6];
> >>>>
> >>>> UINT64 RandomData;
> >>>>
> >>>> UINT64 EncryptionMask;
> >>>> } SEC_SEV_ES_WORK_AREA;
> >>>>
> >>>> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we
> can
> >> be
> >>>> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
> >>>> structure (if needed).
> >>>>
> >>>> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA
> before
> >>>> booting the guest to ensure that its safe to access the memory without
> >>>> going through the accept/validation process.
> >>>>
> >>>> In case of the TDX, the reset vector code sets the TdxEnabled on the
> >>>> entry. In case of the SEV, the workarea is valid from SEC to PEI phase
> >>>> only and it gets reused for other purposes. The PEI phase set the Pcd's
> >>>> (such as SevEsEnabled or SevEnabled etc) so that Dxe or other EDK2 core
> >>>> does not need to know anything about the workarea and they simply can
> >>>> read the PCDs.
> >>>>
> >>>> -Brijesh
> >>>>
> >>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> >>
> >>
> >
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-28 23:48 ` Yao, Jiewen
@ 2021-07-29 2:44 ` Min Xu
2021-07-29 4:29 ` Brijesh Singh
0 siblings, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-29 2:44 UTC (permalink / raw)
To: Yao, Jiewen, Brijesh Singh, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
Jiewen & Singh
From the discussion I am thinking we have below rules to follow to the design the structure
of TEE_WORK_AREA:
1. Design should be flexible but not too complicated
2. Reuse the current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as TEE_WORK_AREA
3. TEE_WORK_AREA should be initialized to all-0 at the beginning of ResetVecotr
4. Reduce the changes to exiting code if possible
So I try to make below conclusions below: (Please review)
1. SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and SEV, maybe in
the future it can be used by other CC technologies.
2. In MEMFD, add below initial value. So that TEE_WORK_AREA is guaranteed to be cleared
in legacy guest. In TDX this memory region is initialized to be all-0 by host VMM. In SEV the memory
region is cleared as well.
0x00B000|0x001000
gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
DATA = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}
3. The structure of TEE_WORK_AREA
The current SEV_ES_WORK_AREA is defined as below:
typedef struct {
UINT8 SevEsEnabled;
UINT8 Reserved1[7];
[Others...]
} SEC_SEV_ES_WORK_AREA;
So I think the TEE_WORK_AREA can be:
Byte[0] Type:
0: legacy 1: SEV 2: TDX
Byte[1] Subtype:
If Type is 0, then it is 0
If Type is 1, then it is up to SEV's definition
If Type is 2, then it is up to TDX's definition
Byte[] other bytes are defined based on the Type/Subtype
I check the code in SecMain.c.
SevEsIsEnabled() need updated to check SevEsWorkarea->SevEsEnabled == 1, not non-0.
@Brijesh Singh Is there any other code need update?
> -----Original Message-----
> From: Yao, Jiewen <jiewen.yao@intel.com>
> Sent: Thursday, July 29, 2021 7:49 AM
> To: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Xu, Min M
> <min.m.xu@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> ResetVector
>
> Comment below:
>
> > -----Original Message-----
> > From: Brijesh Singh <brijesh.singh@amd.com>
> > Sent: Thursday, July 29, 2021 2:59 AM
> > To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu, Min
> > M <min.m.xu@intel.com>
> > Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
> > Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
> > <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
> > Lendacky <thomas.lendacky@amd.com>
> > Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> > ResetVector
> >
> >
> >
> > On 7/28/21 11:26 AM, Yao, Jiewen wrote:
> > > I would say it is NOT the best software practice to define 2 enable
> > > fields to
> > indicate one type.
> > >
> > > What if some software error, set both TdxEnable and SevEnable at same
> time?
> > > How do you detect such error?
> >
> > Hmm, aren't we saying it is a software bug. In that case another bug
> > can also mess up the structure header.
> [Jiewen] Yes. I treat it as a software implementation bug.
> The key thing in software design is to avoid the developer making mistake, help
> debug issues, help maintain the code easily.
>
>
> >
> > >
> > > If some code may check the SEV only, and some code may check TDX
> > > only,
> > then both code can run. That will be a disaster. The code is hard to
> > maintain and hard to debug.
> > >
> > > Another consideration is: what if someone wants to set the third type?
> > > Do we want to reserve the 3rd byte? To indicate the third type? It
> > > is not
> > scalable.
> > >
> > > The best software practice it to just define one field as
> > > enumeration. So any
> > software can only set Tdx or Sev. The result is consistent, no matter
> > you check the SEV at first or TDX at first.
> > > If there is 3rd bytes, we just need assign the 3rd value to it,
> > > without impact any
> > other field.
> > >
> >
> > I was trying to see if we can make it work without requiring any
> > changes to UefiCpuPkg etc (which uses the EsWorkArea).
> [Jiewen] I agree with you.
> And I think the priority should be:
> 1) make a good design following the best practice.
> 2) minimize the change.
>
> I don’t think two *enable* fields can satisfy #1.
> And I am open on how to do #2. (See rest comment below)
>
>
>
> >
> >
> > >
> > > I think we can add "must zero" in the comment. But it does not mean
> > > there will
> > be no error during development.
> > >
> > > UNION is not a type safe structure. Usually, the consumer of UNION
> > > shall
> > refer to a common field to know what the type of union is - I treat
> > that as header.
> > >
> > > Since we are defining a common data structure, I think we can do
> > > some clean
> > up.
> > > Or if you have concern to change SEC_SEV_ES_WORK_AREA, we can define
> > > a
> > new CC WORK_AREA without touching SEV_SEV_ES_WORKING_AREA.
> > >
> >
> > In your below structure, I assume the SEV or TDX probe will set the
> > Type only when it detects that feature is active. But which layer of
> > the software is going to set the type to zero to indicate its a legacy guest ?
> [Jiewen] Good question.
> I expect some initialization function, such as InitCcWorkAreaSev,
> InitCcWorkAreaTdx.
> The default value Legacy shall be override in InitCcWorkArea after capability
> check.
> PreMainFunctionHookXXX is common patter for all function. It just checks the
> CcWorkArea.
>
>
>
> >
> > typedef struct {
> > UINT8 HeaderVersion; // 0
> > UINT8 HeadLength; // 4
> > UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
> > UINT8 SubType; // Type specific sub type, if needed.
> > } CC_COMMON_WORK_AREA_HEADER;
> >
> > i.e In this call sequence:
> >
> > OneTimeCall PreMainFunctionHookSev
> > OneTimeCall PreMainFunctionHookTdx
> >
> > ....
> >
> > The PreMainFunctionHookSev will detect whether SEV is active or not.
> > If its active then set the type = MEM_ENCRYPT_SEV; and similarly the
> > Tdx hook will set the type=MEM_ENCRYPT_TDX. What if neither TDX or SEV
> > is enabled. At this time we will depend on hypervisor to ensure that
> > value at that memory is zero.
> [Jiewen] I think we just let InitCcWorkAreaSev and InitCcWorkAreaTdx override
> the default value.
> InitCcWorkArea{Sev,Tdx}:
> // Detect Hardware Capability
> // If discovered, then set Type = {SEV,TDX}
> // Else, leave it as is
>
>
>
>
> >
> > Additionally, do you see a need for the HeadLength field in this
> > structure ? In asm it is preferred if we can access the actual
> > workarea pointer at the fixed location instead of first reading the
> > HeadLength to determine how much it need to skip.
> [Jiewen] You are right.
> Length/Version is NOT absolutely necessary, if the header is simple enough. If
> you think we don’t need them, I am OK to remove them.
> I think only "Type" is mandatory, which tells us the category.
> I think "SubType" is useful, because we might want to know if it is SEV, or SEV-ES,
> or SEV-SNP, and if it is TDX 1.0, or TDX.future. Please let me know your thought.
>
>
> One question on SEC_SEV_ES_WORK_AREA. Since you have SEV/SEV-ES/SEV-
> SNP, is this SEV_ES_WORK_AREA only for SEV_ES? Or it is also used in SEV (no
> SEV_ES), and SEV_SNP ?
> For example, when we see SevEsEnable=0, how do we know it is SEV (no SEV_ES)
> or legacy (no SEV, no SEV_ES)? Or we don’t need care in SEV (no SEV_ES) case.
>
>
>
>
> >
> >
> > > Thank you
> > > Yao Jiewen
> > >
> > >
> > >> -----Original Message-----
> > >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> > >> Brijesh Singh via groups.io
> > >> Sent: Wednesday, July 28, 2021 11:59 PM
> > >> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu,
> > >> Min M <min.m.xu@intel.com>
> > >> Cc: brijesh.singh@amd.com; Ard Biesheuvel
> > >> <ardb+tianocore@kernel.org>; Justen, Jordan L
> > >> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> > >> James Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> > >> <thomas.lendacky@amd.com>
> > >> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm
> > >> in ResetVector
> > >>
> > >> Hi Yao Jiewen,
> > >>
> > >> I guess I am still trying to figure out why we need the header in
> > >> the work area. Can't we do something like this:
> > >>
> > >> typedef struct {
> > >> UINT8 SevEsEnabled;
> > >>
> > >> // If Es is enabled then this field must be zeroed
> > >> UINT8 MustBeZero;
> > >>
> > >> UINT8 Reserved1[6];
> > >>
> > >> UINT64 RandomData;
> > >>
> > >> UINT64 EncryptionMask;
> > >> } SEC_SEV_ES_WORK_AREA;
> > >>
> > >> typedef struct {
> > >> // If Tdx is enabled then it must be zeroed
> > >> UINT8 MustBeZero
> > >>
> > >> UINT8 TdxEnabled;
> > >>
> > >> UINT8 Reserved2[6];
> > >> ....
> > >>
> > >> } TX_WORK_AREA;
> > >>
> > >> typedef union {
> > >> SEC_SEV_ES_WORK_AREA SevEsWorkArea;
> > >> TDX_WORK_AREA TdxWorkArea;
> > >> } CC_WORK_AREA;
> > >>
> > >> I am trying to minimize the changes to the existing code. The SEV
> > >> and TDX probe logic should ensure that if the feature is detected,
> > >> then it must clear the MustBeZero'ed field.
> > >>
> > >> Basically, we already have a 64-bit value reserved in the SevEsWork
> > >> area and currently only one byte is used and second byte can be
> > >> detected for the TDX. Basically the which encryption technology is
> > >> active the definition of the work area will change.
> > >>
> > >> Am I missing something ?
> > >>
> > >> Thanks
> > >>
> > >> On 7/28/21 10:22 AM, Yao, Jiewen wrote:
> > >>> Hi Brijesh
> > >>> Thanks!
> > >>>
> > >>> I think if we want to reuse this, we need rename the data structure.
> > >>>
> > >>> First, we should use a generic name.
> > >>>
> > >>> Second, I don’t think it is good idea to define two *enable*
> > >>> fields. Since only
> > >> one of them should be enabled, we should use 1 field as enumeration.
> > >>>
> > >>> Third, we should hide the SEV specific and TDX specific definition
> > >>> in CC
> > >> common work area.
> > >>>
> > >>> If we agree to use a common work area, I recommend below:
> > >>>
> > >>> typedef struct {
> > >>> UINT8 HeaderVersion; // 0
> > >>> UINT8 HeadLength; // 4
> > >>> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
> > >>> UINT8 SubType; // Type specific sub type, if needed.
> > >>> } CC_COMMON_WORK_AREA_HEADER;
> > >>>
> > >>> typedef struct {
> > >>> CC_COMMON_WORK_AREA_HEADER Header;
> > >>> // reset is valid if Type == 1
> > >>> UINT8 Reserved1[4];
> > >>> UINT64 RandomData;
> > >>> UINT64 EncryptionMask;
> > >>> } SEC_SEV_ES_WORK_AREA;
> > >>>
> > >>> typedef struct {
> > >>> CC_COMMON_WORK_AREA_HEADER Header;
> > >>> // reset is valid if Type == 2
> > >>> UINT8 TdxSpecific[]; // TBD
> > >>> } TDX_WORK_AREA;
> > >>>
> > >>> Thank you
> > >>> Yao Jiewen
> > >>>
> > >>>> -----Original Message-----
> > >>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> > >>>> Brijesh Singh via groups.io
> > >>>> Sent: Wednesday, July 28, 2021 10:34 PM
> > >>>> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M
> > <min.m.xu@intel.com>
> > >>>> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
> > >>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
> > <jordan.l.justen@intel.com>;
> > >>>> Erdem Aktas <erdemaktas@google.com>; James Bottomley
> > >>>> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> > >>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add
> > >>>> AmdSev.asm in ResetVector
> > >>>>
> > >>>> Hi Jiewen and Min,
> > >>>>
> > >>>> See my comments below.
> > >>>>
> > >>>>
> > >>>> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
> > >>>>> Yes. I am thinking the same thing.
> > >>>>>
> > >>>>> [CC Flag memory location]
> > >>>>> 1) A general purpose register, such as EBP.
> > >>>>>
> > >>>>> 2) A global variable, such as
> > >>>>> .data
> > >>>>> TeeFlags: DD 0
> > >>>>>
> > >>>>> 3) A fixed region in stack, such as dword[STACK_TOP - 4]
> > >>>>>
> > >>>>> 4) A new CC common fixed region, such as dword[CC_COMMON_FLAGS]
> > >>>>>
> > >>>>> 5) A fixed region piggyback on existing CC working area, such as
> > >>>>> dword[CC_WORKING_AREA]
> > >>>>>
> > >>>>> Hi Brijesh/Min
> > >>>>> Any preference?
> > >>>>>
> > >>>>> [CC Indicator Flags]
> > >>>>> Proposal: UINT8[4]
> > >>>>>
> > >>>>> Byte [0] Version: 0
> > >>>>> byte [1] Length: 4
> > >>>>> byte [2] Type:
> > >>>>> 0: legacy
> > >>>>> 1: SEV
> > >>>>> 2: TDX
> > >>>>> byte [3] Sub Type:
> > >>>>> If Type is 0 (legacy), then
> > >>>>> 0: legacy
> > >>>>> If Type is 1 (SEV), then
> > >>>>> 0: SEV
> > >>>>> 1: SEV-ES
> > >>>>> 2: SEV-SNP
> > >>>>> If Type is 2 (TDX), then
> > >>>>> 0: TDX 1.0
> > >>>>>
> > >>>>> Thank you
> > >>>>> Yao Jiewen
> > >>>>>
> > >>>>>
> > >>>>>> -----Original Message-----
> > >>>>>> From: Xu, Min M <min.m.xu@intel.com>
> > >>>>>> Sent: Wednesday, July 28, 2021 2:58 PM
> > >>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>
> > >>>>>> Cc: Brijesh Singh <brijesh.singh@amd.com>;
> > >>>>>> devel@edk2.groups.io; Ard Biesheuvel
> > >>>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
> > >>>>>> <jordan.l.justen@intel.com>; Erdem Aktas
> > >>>>>> <erdemaktas@google.com>;
> > >>>> James
> > >>>>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> > >>>> <thomas.lendacky@amd.com>
> > >>>>>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> > ResetVector
> > >>>>>>
> > >>>>>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
> > >>>>>>> It does not necessary to be a working area.
> > >>>>>>>
> > >>>>>>> We just need a common TEE flag to indicate if the system run
> > >>>>>>> in legacy,
> > >> SEV,
> > >>>>>> or
> > >>>>>>> TDX, right?
> > >>>>>> Right. We need somewhere to store this flag, either in a
> > >>>>>> Register or in
> > >>>> Memory.
> > >>>>>> If it is memory, then in Tdx the memory region should be
> > >>>>>> initialized by
> > host
> > >>>> VMM.
> > >>>>>>>
> > >>>>>>> thank you!
> > >>>>>>> Yao, Jiewen
> > >>>>>>>
> > >>>>>>>
> > >>>>>>>> 在 2021年7月28日,下午1:07,Xu, Min M
> <min.m.xu@intel.com>
> > >> 写
> > >>>> 道:
> > >>>>>>>>
> > >>>>>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
> > >>>>>>>>> HI Min
> > >>>>>>>>> I agree with Brijesh.
> > >>>>>>>>>
> > >>>>>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
> > >>>>>>>>> TDX file shall never refer to SEV data structure.
> > >>>>>>>>> These code should be isolated clearly.
> > >>>>>>>>>
> > >>>>>>>>> Do we still need that logic if we follow the new pattern?
> > >>>>>>>> I have replied to Brijesh's mail about the concern of the new pattern.
> > >>>>>>>>
> > >>>>>>>> I have some concern in the current pattern:
> > >>>>>>>> ====================
> > >>>>>>>> OneTimeCall PreMainFunctionHookSev
> > >>>>>>>> OneTimeCall PreMainFunctionHookTdx
> > >>>>>>>> MainFunction:
> > >>>>>>>> XXXXXX
> > >>>>>>>> OneTimeCall PostMainFunctionHookSev
> > >>>>>>>> OneTimeCall PostMainFunctionHookTdx
> > >>>>>>>> ====================
> > >>>>>>>> The TEE function need implement a TEE check function (such as
> > >>>>>>>> IsSev,
> > or
> > >>>>>> IsTdx).
> > >>>>>>>>
> > >>>>>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the
> > >>>>>>>> very beginning of ResetVector. Then 'TDXG' is set in
> TDX_WORK_AREA.
> > SEV
> > >>>> does
> > >>>>>>> the similar work which call CheckSevFeatures to set
> > SEV_ES_WORK_AREA
> > >> to
> > >>>> 1.
> > >>>>>>>>
> > >>>>>>>> After that both TDX and SEV read the above WORK_AREA to check
> > >>>>>>>> if it
> > is
> > >>>> TDX
> > >>>>>>> or SEV or legacy guest.
> > >>>>>>>>
> > >>>>>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error
> > >>>>>>>> because
> > >>>>>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
> > >>>>>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will
> > >>>>>>>> trigger
> > error
> > >>>> too.
> > >>>>>>>>
> > >>>>>>>> I am wondering if TDX and SEV can use the same memory region
> > >>>>>>>> (for
> > >>>>>> example,
> > >>>>>>> TEE_WORK_AREA) as the work area?
> > >>>>>>>> So that this work area is guaranteed to be initialized in
> > >>>>>>>> both TDX and SEV. Structure of the TEE_WORK_AREA may look like
> this:
> > >>>>>>>> typedef struct {
> > >>>>>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
> > >>>>>>>> UINT8 Others[];
> > >>>>>>>> } TEE_WORK_AREA;
> > >>>>>>>>>
> > >>>>
> > >>>> Are we reserving a new page for the TDX_WORK_AREA ? I am
> > >>>> wondering
> > why
> > >>>> can't we use the SEV_ES_WORK_AREA instead of wasting space in the
> > >> MEMFD.
> > >>>>
> > >>>> The SEV_ES_WORK_AREA layout looks like this:
> > >>>>
> > >>>> typedef struct _SEC_SEV_ES_WORK_AREA {
> > >>>> UINT8 SevEsEnabled;
> > >>>> UINT8 Reserved1[7];
> > >>>>
> > >>>> UINT64 RandomData;
> > >>>>
> > >>>> UINT64 EncryptionMask;
> > >>>> } SEC_SEV_ES_WORK_AREA;
> > >>>>
> > >>>> There is reserved bit after the SevEsEnabled and one byte can be
> > >>>> used for the TdxEnabled;
> > >>>>
> > >>>> typedef struct _SEC_SEV_ES_WORK_AREA {
> > >>>> UINT8 SevEsEnabled;
> > >>>> UINT8 TdxEnabled;
> > >>>> UINT8 Reserved2[6];
> > >>>>
> > >>>> UINT64 RandomData;
> > >>>>
> > >>>> UINT64 EncryptionMask;
> > >>>> } SEC_SEV_ES_WORK_AREA;
> > >>>>
> > >>>> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we
> > can
> > >> be
> > >>>> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
> > >>>> structure (if needed).
> > >>>>
> > >>>> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA
> > before
> > >>>> booting the guest to ensure that its safe to access the memory
> > >>>> without going through the accept/validation process.
> > >>>>
> > >>>> In case of the TDX, the reset vector code sets the TdxEnabled on
> > >>>> the entry. In case of the SEV, the workarea is valid from SEC to
> > >>>> PEI phase only and it gets reused for other purposes. The PEI
> > >>>> phase set the Pcd's (such as SevEsEnabled or SevEnabled etc) so
> > >>>> that Dxe or other EDK2 core does not need to know anything about
> > >>>> the workarea and they simply can read the PCDs.
> > >>>>
> > >>>> -Brijesh
> > >>>>
> > >>>>
> > >>>>
> > >>>>
> > >>>
> > >>
> > >>
> > >>
> > >>
> > >
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 2:44 ` Min Xu
@ 2021-07-29 4:29 ` Brijesh Singh
2021-07-29 5:17 ` Yao, Jiewen
2021-07-29 6:07 ` Min Xu
0 siblings, 2 replies; 36+ messages in thread
From: Brijesh Singh @ 2021-07-29 4:29 UTC (permalink / raw)
To: Xu, Min M, Yao, Jiewen, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
On 7/28/21 9:44 PM, Xu, Min M wrote:
> Jiewen & Singh
>
> From the discussion I am thinking we have below rules to follow to the design the structure
> of TEE_WORK_AREA:
> 1. Design should be flexible but not too complicated
> 2. Reuse the current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as TEE_WORK_AREA
> 3. TEE_WORK_AREA should be initialized to all-0 at the beginning of ResetVecotr
> 4. Reduce the changes to exiting code if possible
>
> So I try to make below conclusions below: (Please review)
> 1. SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and SEV, maybe in
> the future it can be used by other CC technologies.
>
> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is guaranteed to be cleared
> in legacy guest. In TDX this memory region is initialized to be all-0 by host VMM. In SEV the memory
> region is cleared as well.
> 0x00B000|0x001000
> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
> DATA = {
> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> }
Hmm, I thought the contents of the data pages are controlled by the host
VMM. If the backing pages are not zero filled then there is no guarantee
that memory will be zero. To verify it:
1. I applied your above change in OvmfPkgX86.fdt. I modified the DATA
values from 0x00 -> 0xCC
2. Modified the SecMain.c to dump the SevEsWorkArea on entry
And dump does not contain the 0xcc.
And to confirm further, I attached to the qemu with the GDB before the
booting the OVMF, and modified the SevEsWorkArea with some garbage
number and this time the dump printed garbage value I put through the
debugger.
In summary, the OVMF to zero the workarea memory on the entry and we
cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
Did I miss something ?
>
> 3. The structure of TEE_WORK_AREA
> The current SEV_ES_WORK_AREA is defined as below:
> typedef struct {
> UINT8 SevEsEnabled;
> UINT8 Reserved1[7];
> [Others...]
> } SEC_SEV_ES_WORK_AREA;
>
> So I think the TEE_WORK_AREA can be:
> Byte[0] Type:
> 0: legacy 1: SEV 2: TDX
> Byte[1] Subtype:
> If Type is 0, then it is 0
> If Type is 1, then it is up to SEV's definition
> If Type is 2, then it is up to TDX's definition
> Byte[] other bytes are defined based on the Type/Subtype
I personally like Yao Jiewen's struct definition, but I can also live
with this one as well :). The only question I had was with his proposal
was what if we remove the Length and Version fields. If the header
length was fixed then life would be much easier in the ASM code.
> I check the code in SecMain.c.
> SevEsIsEnabled() need updated to check SevEsWorkarea->SevEsEnabled == 1, not non-0.
> @Brijesh Singh Is there any other code need update?
As noted before, the SevEsWorkAreas is used to pass the information from
the Sec to PEI phase. The workarea gets reused for totally different
purpose after the PEI phase.
thanks
>> -----Original Message-----
>> From: Yao, Jiewen <jiewen.yao@intel.com>
>> Sent: Thursday, July 29, 2021 7:49 AM
>> To: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Xu, Min M
>> <min.m.xu@intel.com>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>> Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>> ResetVector
>>
>> Comment below:
>>
>>> -----Original Message-----
>>> From: Brijesh Singh <brijesh.singh@amd.com>
>>> Sent: Thursday, July 29, 2021 2:59 AM
>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu, Min
>>> M <min.m.xu@intel.com>
>>> Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
>>> Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
>>> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
>>> Lendacky <thomas.lendacky@amd.com>
>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>>> ResetVector
>>>
>>>
>>>
>>> On 7/28/21 11:26 AM, Yao, Jiewen wrote:
>>>> I would say it is NOT the best software practice to define 2 enable
>>>> fields to
>>> indicate one type.
>>>> What if some software error, set both TdxEnable and SevEnable at same
>> time?
>>>> How do you detect such error?
>>> Hmm, aren't we saying it is a software bug. In that case another bug
>>> can also mess up the structure header.
>> [Jiewen] Yes. I treat it as a software implementation bug.
>> The key thing in software design is to avoid the developer making mistake, help
>> debug issues, help maintain the code easily.
>>
>>
>>>> If some code may check the SEV only, and some code may check TDX
>>>> only,
>>> then both code can run. That will be a disaster. The code is hard to
>>> maintain and hard to debug.
>>>> Another consideration is: what if someone wants to set the third type?
>>>> Do we want to reserve the 3rd byte? To indicate the third type? It
>>>> is not
>>> scalable.
>>>> The best software practice it to just define one field as
>>>> enumeration. So any
>>> software can only set Tdx or Sev. The result is consistent, no matter
>>> you check the SEV at first or TDX at first.
>>>> If there is 3rd bytes, we just need assign the 3rd value to it,
>>>> without impact any
>>> other field.
>>> I was trying to see if we can make it work without requiring any
>>> changes to UefiCpuPkg etc (which uses the EsWorkArea).
>> [Jiewen] I agree with you.
>> And I think the priority should be:
>> 1) make a good design following the best practice.
>> 2) minimize the change.
>>
>> I don’t think two *enable* fields can satisfy #1.
>> And I am open on how to do #2. (See rest comment below)
>>
>>
>>
>>>
>>>> I think we can add "must zero" in the comment. But it does not mean
>>>> there will
>>> be no error during development.
>>>> UNION is not a type safe structure. Usually, the consumer of UNION
>>>> shall
>>> refer to a common field to know what the type of union is - I treat
>>> that as header.
>>>> Since we are defining a common data structure, I think we can do
>>>> some clean
>>> up.
>>>> Or if you have concern to change SEC_SEV_ES_WORK_AREA, we can define
>>>> a
>>> new CC WORK_AREA without touching SEV_SEV_ES_WORKING_AREA.
>>> In your below structure, I assume the SEV or TDX probe will set the
>>> Type only when it detects that feature is active. But which layer of
>>> the software is going to set the type to zero to indicate its a legacy guest ?
>> [Jiewen] Good question.
>> I expect some initialization function, such as InitCcWorkAreaSev,
>> InitCcWorkAreaTdx.
>> The default value Legacy shall be override in InitCcWorkArea after capability
>> check.
>> PreMainFunctionHookXXX is common patter for all function. It just checks the
>> CcWorkArea.
>>
>>
>>
>>> typedef struct {
>>> UINT8 HeaderVersion; // 0
>>> UINT8 HeadLength; // 4
>>> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
>>> UINT8 SubType; // Type specific sub type, if needed.
>>> } CC_COMMON_WORK_AREA_HEADER;
>>>
>>> i.e In this call sequence:
>>>
>>> OneTimeCall PreMainFunctionHookSev
>>> OneTimeCall PreMainFunctionHookTdx
>>>
>>> ....
>>>
>>> The PreMainFunctionHookSev will detect whether SEV is active or not.
>>> If its active then set the type = MEM_ENCRYPT_SEV; and similarly the
>>> Tdx hook will set the type=MEM_ENCRYPT_TDX. What if neither TDX or SEV
>>> is enabled. At this time we will depend on hypervisor to ensure that
>>> value at that memory is zero.
>> [Jiewen] I think we just let InitCcWorkAreaSev and InitCcWorkAreaTdx override
>> the default value.
>> InitCcWorkArea{Sev,Tdx}:
>> // Detect Hardware Capability
>> // If discovered, then set Type = {SEV,TDX}
>> // Else, leave it as is
>>
>>
>>
>>
>>> Additionally, do you see a need for the HeadLength field in this
>>> structure ? In asm it is preferred if we can access the actual
>>> workarea pointer at the fixed location instead of first reading the
>>> HeadLength to determine how much it need to skip.
>> [Jiewen] You are right.
>> Length/Version is NOT absolutely necessary, if the header is simple enough. If
>> you think we don’t need them, I am OK to remove them.
>> I think only "Type" is mandatory, which tells us the category.
>> I think "SubType" is useful, because we might want to know if it is SEV, or SEV-ES,
>> or SEV-SNP, and if it is TDX 1.0, or TDX.future. Please let me know your thought.
>>
>>
>> One question on SEC_SEV_ES_WORK_AREA. Since you have SEV/SEV-ES/SEV-
>> SNP, is this SEV_ES_WORK_AREA only for SEV_ES? Or it is also used in SEV (no
>> SEV_ES), and SEV_SNP ?
>> For example, when we see SevEsEnable=0, how do we know it is SEV (no SEV_ES)
>> or legacy (no SEV, no SEV_ES)? Or we don’t need care in SEV (no SEV_ES) case.
>>
>>
>>
>>
>>>
>>>> Thank you
>>>> Yao Jiewen
>>>>
>>>>
>>>>> -----Original Message-----
>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>> Brijesh Singh via groups.io
>>>>> Sent: Wednesday, July 28, 2021 11:59 PM
>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu,
>>>>> Min M <min.m.xu@intel.com>
>>>>> Cc: brijesh.singh@amd.com; Ard Biesheuvel
>>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
>>>>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
>>>>> James Bottomley <jejb@linux.ibm.com>; Tom Lendacky
>>>>> <thomas.lendacky@amd.com>
>>>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm
>>>>> in ResetVector
>>>>>
>>>>> Hi Yao Jiewen,
>>>>>
>>>>> I guess I am still trying to figure out why we need the header in
>>>>> the work area. Can't we do something like this:
>>>>>
>>>>> typedef struct {
>>>>> UINT8 SevEsEnabled;
>>>>>
>>>>> // If Es is enabled then this field must be zeroed
>>>>> UINT8 MustBeZero;
>>>>>
>>>>> UINT8 Reserved1[6];
>>>>>
>>>>> UINT64 RandomData;
>>>>>
>>>>> UINT64 EncryptionMask;
>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>
>>>>> typedef struct {
>>>>> // If Tdx is enabled then it must be zeroed
>>>>> UINT8 MustBeZero
>>>>>
>>>>> UINT8 TdxEnabled;
>>>>>
>>>>> UINT8 Reserved2[6];
>>>>> ....
>>>>>
>>>>> } TX_WORK_AREA;
>>>>>
>>>>> typedef union {
>>>>> SEC_SEV_ES_WORK_AREA SevEsWorkArea;
>>>>> TDX_WORK_AREA TdxWorkArea;
>>>>> } CC_WORK_AREA;
>>>>>
>>>>> I am trying to minimize the changes to the existing code. The SEV
>>>>> and TDX probe logic should ensure that if the feature is detected,
>>>>> then it must clear the MustBeZero'ed field.
>>>>>
>>>>> Basically, we already have a 64-bit value reserved in the SevEsWork
>>>>> area and currently only one byte is used and second byte can be
>>>>> detected for the TDX. Basically the which encryption technology is
>>>>> active the definition of the work area will change.
>>>>>
>>>>> Am I missing something ?
>>>>>
>>>>> Thanks
>>>>>
>>>>> On 7/28/21 10:22 AM, Yao, Jiewen wrote:
>>>>>> Hi Brijesh
>>>>>> Thanks!
>>>>>>
>>>>>> I think if we want to reuse this, we need rename the data structure.
>>>>>>
>>>>>> First, we should use a generic name.
>>>>>>
>>>>>> Second, I don’t think it is good idea to define two *enable*
>>>>>> fields. Since only
>>>>> one of them should be enabled, we should use 1 field as enumeration.
>>>>>> Third, we should hide the SEV specific and TDX specific definition
>>>>>> in CC
>>>>> common work area.
>>>>>> If we agree to use a common work area, I recommend below:
>>>>>>
>>>>>> typedef struct {
>>>>>> UINT8 HeaderVersion; // 0
>>>>>> UINT8 HeadLength; // 4
>>>>>> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
>>>>>> UINT8 SubType; // Type specific sub type, if needed.
>>>>>> } CC_COMMON_WORK_AREA_HEADER;
>>>>>>
>>>>>> typedef struct {
>>>>>> CC_COMMON_WORK_AREA_HEADER Header;
>>>>>> // reset is valid if Type == 1
>>>>>> UINT8 Reserved1[4];
>>>>>> UINT64 RandomData;
>>>>>> UINT64 EncryptionMask;
>>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>>
>>>>>> typedef struct {
>>>>>> CC_COMMON_WORK_AREA_HEADER Header;
>>>>>> // reset is valid if Type == 2
>>>>>> UINT8 TdxSpecific[]; // TBD
>>>>>> } TDX_WORK_AREA;
>>>>>>
>>>>>> Thank you
>>>>>> Yao Jiewen
>>>>>>
>>>>>>> -----Original Message-----
>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>>>> Brijesh Singh via groups.io
>>>>>>> Sent: Wednesday, July 28, 2021 10:34 PM
>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M
>>> <min.m.xu@intel.com>
>>>>>>> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
>>>>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
>>> <jordan.l.justen@intel.com>;
>>>>>>> Erdem Aktas <erdemaktas@google.com>; James Bottomley
>>>>>>> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>>>>>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add
>>>>>>> AmdSev.asm in ResetVector
>>>>>>>
>>>>>>> Hi Jiewen and Min,
>>>>>>>
>>>>>>> See my comments below.
>>>>>>>
>>>>>>>
>>>>>>> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
>>>>>>>> Yes. I am thinking the same thing.
>>>>>>>>
>>>>>>>> [CC Flag memory location]
>>>>>>>> 1) A general purpose register, such as EBP.
>>>>>>>>
>>>>>>>> 2) A global variable, such as
>>>>>>>> .data
>>>>>>>> TeeFlags: DD 0
>>>>>>>>
>>>>>>>> 3) A fixed region in stack, such as dword[STACK_TOP - 4]
>>>>>>>>
>>>>>>>> 4) A new CC common fixed region, such as dword[CC_COMMON_FLAGS]
>>>>>>>>
>>>>>>>> 5) A fixed region piggyback on existing CC working area, such as
>>>>>>>> dword[CC_WORKING_AREA]
>>>>>>>>
>>>>>>>> Hi Brijesh/Min
>>>>>>>> Any preference?
>>>>>>>>
>>>>>>>> [CC Indicator Flags]
>>>>>>>> Proposal: UINT8[4]
>>>>>>>>
>>>>>>>> Byte [0] Version: 0
>>>>>>>> byte [1] Length: 4
>>>>>>>> byte [2] Type:
>>>>>>>> 0: legacy
>>>>>>>> 1: SEV
>>>>>>>> 2: TDX
>>>>>>>> byte [3] Sub Type:
>>>>>>>> If Type is 0 (legacy), then
>>>>>>>> 0: legacy
>>>>>>>> If Type is 1 (SEV), then
>>>>>>>> 0: SEV
>>>>>>>> 1: SEV-ES
>>>>>>>> 2: SEV-SNP
>>>>>>>> If Type is 2 (TDX), then
>>>>>>>> 0: TDX 1.0
>>>>>>>>
>>>>>>>> Thank you
>>>>>>>> Yao Jiewen
>>>>>>>>
>>>>>>>>
>>>>>>>>> -----Original Message-----
>>>>>>>>> From: Xu, Min M <min.m.xu@intel.com>
>>>>>>>>> Sent: Wednesday, July 28, 2021 2:58 PM
>>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>
>>>>>>>>> Cc: Brijesh Singh <brijesh.singh@amd.com>;
>>>>>>>>> devel@edk2.groups.io; Ard Biesheuvel
>>>>>>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
>>>>>>>>> <jordan.l.justen@intel.com>; Erdem Aktas
>>>>>>>>> <erdemaktas@google.com>;
>>>>>>> James
>>>>>>>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
>>>>>>> <thomas.lendacky@amd.com>
>>>>>>>>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>>> ResetVector
>>>>>>>>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
>>>>>>>>>> It does not necessary to be a working area.
>>>>>>>>>>
>>>>>>>>>> We just need a common TEE flag to indicate if the system run
>>>>>>>>>> in legacy,
>>>>> SEV,
>>>>>>>>> or
>>>>>>>>>> TDX, right?
>>>>>>>>> Right. We need somewhere to store this flag, either in a
>>>>>>>>> Register or in
>>>>>>> Memory.
>>>>>>>>> If it is memory, then in Tdx the memory region should be
>>>>>>>>> initialized by
>>> host
>>>>>>> VMM.
>>>>>>>>>> thank you!
>>>>>>>>>> Yao, Jiewen
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> 在 2021年7月28日,下午1:07,Xu, Min M
>> <min.m.xu@intel.com>
>>>>> 写
>>>>>>> 道:
>>>>>>>>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
>>>>>>>>>>>> HI Min
>>>>>>>>>>>> I agree with Brijesh.
>>>>>>>>>>>>
>>>>>>>>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
>>>>>>>>>>>> TDX file shall never refer to SEV data structure.
>>>>>>>>>>>> These code should be isolated clearly.
>>>>>>>>>>>>
>>>>>>>>>>>> Do we still need that logic if we follow the new pattern?
>>>>>>>>>>> I have replied to Brijesh's mail about the concern of the new pattern.
>>>>>>>>>>>
>>>>>>>>>>> I have some concern in the current pattern:
>>>>>>>>>>> ====================
>>>>>>>>>>> OneTimeCall PreMainFunctionHookSev
>>>>>>>>>>> OneTimeCall PreMainFunctionHookTdx
>>>>>>>>>>> MainFunction:
>>>>>>>>>>> XXXXXX
>>>>>>>>>>> OneTimeCall PostMainFunctionHookSev
>>>>>>>>>>> OneTimeCall PostMainFunctionHookTdx
>>>>>>>>>>> ====================
>>>>>>>>>>> The TEE function need implement a TEE check function (such as
>>>>>>>>>>> IsSev,
>>> or
>>>>>>>>> IsTdx).
>>>>>>>>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the
>>>>>>>>>>> very beginning of ResetVector. Then 'TDXG' is set in
>> TDX_WORK_AREA.
>>> SEV
>>>>>>> does
>>>>>>>>>> the similar work which call CheckSevFeatures to set
>>> SEV_ES_WORK_AREA
>>>>> to
>>>>>>> 1.
>>>>>>>>>>> After that both TDX and SEV read the above WORK_AREA to check
>>>>>>>>>>> if it
>>> is
>>>>>>> TDX
>>>>>>>>>> or SEV or legacy guest.
>>>>>>>>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error
>>>>>>>>>>> because
>>>>>>>>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
>>>>>>>>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will
>>>>>>>>>>> trigger
>>> error
>>>>>>> too.
>>>>>>>>>>> I am wondering if TDX and SEV can use the same memory region
>>>>>>>>>>> (for
>>>>>>>>> example,
>>>>>>>>>> TEE_WORK_AREA) as the work area?
>>>>>>>>>>> So that this work area is guaranteed to be initialized in
>>>>>>>>>>> both TDX and SEV. Structure of the TEE_WORK_AREA may look like
>> this:
>>>>>>>>>>> typedef struct {
>>>>>>>>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
>>>>>>>>>>> UINT8 Others[];
>>>>>>>>>>> } TEE_WORK_AREA;
>>>>>>> Are we reserving a new page for the TDX_WORK_AREA ? I am
>>>>>>> wondering
>>> why
>>>>>>> can't we use the SEV_ES_WORK_AREA instead of wasting space in the
>>>>> MEMFD.
>>>>>>> The SEV_ES_WORK_AREA layout looks like this:
>>>>>>>
>>>>>>> typedef struct _SEC_SEV_ES_WORK_AREA {
>>>>>>> UINT8 SevEsEnabled;
>>>>>>> UINT8 Reserved1[7];
>>>>>>>
>>>>>>> UINT64 RandomData;
>>>>>>>
>>>>>>> UINT64 EncryptionMask;
>>>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>>>
>>>>>>> There is reserved bit after the SevEsEnabled and one byte can be
>>>>>>> used for the TdxEnabled;
>>>>>>>
>>>>>>> typedef struct _SEC_SEV_ES_WORK_AREA {
>>>>>>> UINT8 SevEsEnabled;
>>>>>>> UINT8 TdxEnabled;
>>>>>>> UINT8 Reserved2[6];
>>>>>>>
>>>>>>> UINT64 RandomData;
>>>>>>>
>>>>>>> UINT64 EncryptionMask;
>>>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>>>
>>>>>>> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we
>>> can
>>>>> be
>>>>>>> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
>>>>>>> structure (if needed).
>>>>>>>
>>>>>>> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA
>>> before
>>>>>>> booting the guest to ensure that its safe to access the memory
>>>>>>> without going through the accept/validation process.
>>>>>>>
>>>>>>> In case of the TDX, the reset vector code sets the TdxEnabled on
>>>>>>> the entry. In case of the SEV, the workarea is valid from SEC to
>>>>>>> PEI phase only and it gets reused for other purposes. The PEI
>>>>>>> phase set the Pcd's (such as SevEsEnabled or SevEnabled etc) so
>>>>>>> that Dxe or other EDK2 core does not need to know anything about
>>>>>>> the workarea and they simply can read the PCDs.
>>>>>>>
>>>>>>> -Brijesh
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>
>>>>>
>>>>>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 4:29 ` Brijesh Singh
@ 2021-07-29 5:17 ` Yao, Jiewen
2021-07-29 6:07 ` Min Xu
1 sibling, 0 replies; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-29 5:17 UTC (permalink / raw)
To: devel@edk2.groups.io, brijesh.singh@amd.com
Cc: Xu, Min M, Ard Biesheuvel, Justen, Jordan L, Erdem Aktas,
James Bottomley, Tom Lendacky
comment below
thank you!
Yao, Jiewen
> 在 2021年7月29日,下午12:29,Brijesh Singh via groups.io <brijesh.singh=amd.com@groups.io> 写道:
>
>
>> On 7/28/21 9:44 PM, Xu, Min M wrote:
>> Jiewen & Singh
>>
>> From the discussion I am thinking we have below rules to follow to the design the structure
>> of TEE_WORK_AREA:
>> 1. Design should be flexible but not too complicated
>> 2. Reuse the current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as TEE_WORK_AREA
>> 3. TEE_WORK_AREA should be initialized to all-0 at the beginning of ResetVecotr
>> 4. Reduce the changes to exiting code if possible
>>
>> So I try to make below conclusions below: (Please review)
>> 1. SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and SEV, maybe in
>> the future it can be used by other CC technologies.
>>
>> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is guaranteed to be cleared
>> in legacy guest. In TDX this memory region is initialized to be all-0 by host VMM. In SEV the memory
>> region is cleared as well.
>> 0x00B000|0x001000
>> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>> DATA = {
>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
>> }
>
> Hmm, I thought the contents of the data pages are controlled by the host
> VMM. If the backing pages are not zero filled then there is no guarantee
> that memory will be zero. To verify it:
>
> 1. I applied your above change in OvmfPkgX86.fdt. I modified the DATA
> values from 0x00 -> 0xCC
>
> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
>
> And dump does not contain the 0xcc.
>
> And to confirm further, I attached to the qemu with the GDB before the
> booting the OVMF, and modified the SevEsWorkArea with some garbage
> number and this time the dump printed garbage value I put through the
> debugger.
>
> In summary, the OVMF to zero the workarea memory on the entry and we
> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
>
> Did I miss something ?
[jiewen] I am not sure how SEV works.
For TDX, this memory is private memory. VMM or QEMU cannot modify it *after* launch.
VMM or QEMU may modify it *before* launch. That will be detected by attestation later if it is added memory. That will be zeroed with accept page if it is auged memory.
So in TDX, I have no concern.
Anyway, I think the logic can be:
=====
CcWorkArea.Type = 0;
InitCcWorkAreaSev(); // set Type=1 if SEV
InitCcWorkAreaTdx(); // set Type=2 if TDX
=====
>
>
>>
>> 3. The structure of TEE_WORK_AREA
>> The current SEV_ES_WORK_AREA is defined as below:
>> typedef struct {
>> UINT8 SevEsEnabled;
>> UINT8 Reserved1[7];
>> [Others...]
>> } SEC_SEV_ES_WORK_AREA;
>>
>> So I think the TEE_WORK_AREA can be:
>> Byte[0] Type:
>> 0: legacy 1: SEV 2: TDX
>> Byte[1] Subtype:
>> If Type is 0, then it is 0
>> If Type is 1, then it is up to SEV's definition
>> If Type is 2, then it is up to TDX's definition
>> Byte[] other bytes are defined based on the Type/Subtype
>
> I personally like Yao Jiewen's struct definition, but I can also live
> with this one as well :). The only question I had was with his proposal
> was what if we remove the Length and Version fields. If the header
> length was fixed then life would be much easier in the ASM code.
[jiewen] I am ok to remove version and length. The we need very carefully maintain the compatibility.
How about below:
typedef struct {
UINT8 Type;
UINT8 SubType;
UINT8 Reserved[6];
} CC_WORK_AREA_HEAD;
That almost aligns with existing SEV_ES.
>
>
>> I check the code in SecMain.c.
>> SevEsIsEnabled() need updated to check SevEsWorkarea->SevEsEnabled == 1, not non-0.
>> @Brijesh Singh Is there any other code need update?
>
> As noted before, the SevEsWorkAreas is used to pass the information from
> the Sec to PEI phase. The workarea gets reused for totally different
> purpose after the PEI phase.
[jiewen] Sorry. I am not aware of that.
Any documentation?
Is that SEV specific purpose? Or generic CC purpose?
>
> thanks
>
>>> -----Original Message-----
>>> From: Yao, Jiewen <jiewen.yao@intel.com>
>>> Sent: Thursday, July 29, 2021 7:49 AM
>>> To: Brijesh Singh <brijesh.singh@amd.com>; devel@edk2.groups.io; Xu, Min M
>>> <min.m.xu@intel.com>
>>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
>>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>>> Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>>> ResetVector
>>>
>>> Comment below:
>>>
>>>> -----Original Message-----
>>>> From: Brijesh Singh <brijesh.singh@amd.com>
>>>> Sent: Thursday, July 29, 2021 2:59 AM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu, Min
>>>> M <min.m.xu@intel.com>
>>>> Cc: brijesh.singh@amd.com; Ard Biesheuvel <ardb+tianocore@kernel.org>;
>>>> Justen, Jordan L <jordan.l.justen@intel.com>; Erdem Aktas
>>>> <erdemaktas@google.com>; James Bottomley <jejb@linux.ibm.com>; Tom
>>>> Lendacky <thomas.lendacky@amd.com>
>>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>>>> ResetVector
>>>>
>>>>
>>>>
>>>> On 7/28/21 11:26 AM, Yao, Jiewen wrote:
>>>>> I would say it is NOT the best software practice to define 2 enable
>>>>> fields to
>>>> indicate one type.
>>>>> What if some software error, set both TdxEnable and SevEnable at same
>>> time?
>>>>> How do you detect such error?
>>>> Hmm, aren't we saying it is a software bug. In that case another bug
>>>> can also mess up the structure header.
>>> [Jiewen] Yes. I treat it as a software implementation bug.
>>> The key thing in software design is to avoid the developer making mistake, help
>>> debug issues, help maintain the code easily.
>>>
>>>
>>>>> If some code may check the SEV only, and some code may check TDX
>>>>> only,
>>>> then both code can run. That will be a disaster. The code is hard to
>>>> maintain and hard to debug.
>>>>> Another consideration is: what if someone wants to set the third type?
>>>>> Do we want to reserve the 3rd byte? To indicate the third type? It
>>>>> is not
>>>> scalable.
>>>>> The best software practice it to just define one field as
>>>>> enumeration. So any
>>>> software can only set Tdx or Sev. The result is consistent, no matter
>>>> you check the SEV at first or TDX at first.
>>>>> If there is 3rd bytes, we just need assign the 3rd value to it,
>>>>> without impact any
>>>> other field.
>>>> I was trying to see if we can make it work without requiring any
>>>> changes to UefiCpuPkg etc (which uses the EsWorkArea).
>>> [Jiewen] I agree with you.
>>> And I think the priority should be:
>>> 1) make a good design following the best practice.
>>> 2) minimize the change.
>>>
>>> I don’t think two *enable* fields can satisfy #1.
>>> And I am open on how to do #2. (See rest comment below)
>>>
>>>
>>>
>>>>
>>>>> I think we can add "must zero" in the comment. But it does not mean
>>>>> there will
>>>> be no error during development.
>>>>> UNION is not a type safe structure. Usually, the consumer of UNION
>>>>> shall
>>>> refer to a common field to know what the type of union is - I treat
>>>> that as header.
>>>>> Since we are defining a common data structure, I think we can do
>>>>> some clean
>>>> up.
>>>>> Or if you have concern to change SEC_SEV_ES_WORK_AREA, we can define
>>>>> a
>>>> new CC WORK_AREA without touching SEV_SEV_ES_WORKING_AREA.
>>>> In your below structure, I assume the SEV or TDX probe will set the
>>>> Type only when it detects that feature is active. But which layer of
>>>> the software is going to set the type to zero to indicate its a legacy guest ?
>>> [Jiewen] Good question.
>>> I expect some initialization function, such as InitCcWorkAreaSev,
>>> InitCcWorkAreaTdx.
>>> The default value Legacy shall be override in InitCcWorkArea after capability
>>> check.
>>> PreMainFunctionHookXXX is common patter for all function. It just checks the
>>> CcWorkArea.
>>>
>>>
>>>
>>>> typedef struct {
>>>> UINT8 HeaderVersion; // 0
>>>> UINT8 HeadLength; // 4
>>>> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
>>>> UINT8 SubType; // Type specific sub type, if needed.
>>>> } CC_COMMON_WORK_AREA_HEADER;
>>>>
>>>> i.e In this call sequence:
>>>>
>>>> OneTimeCall PreMainFunctionHookSev
>>>> OneTimeCall PreMainFunctionHookTdx
>>>>
>>>> ....
>>>>
>>>> The PreMainFunctionHookSev will detect whether SEV is active or not.
>>>> If its active then set the type = MEM_ENCRYPT_SEV; and similarly the
>>>> Tdx hook will set the type=MEM_ENCRYPT_TDX. What if neither TDX or SEV
>>>> is enabled. At this time we will depend on hypervisor to ensure that
>>>> value at that memory is zero.
>>> [Jiewen] I think we just let InitCcWorkAreaSev and InitCcWorkAreaTdx override
>>> the default value.
>>> InitCcWorkArea{Sev,Tdx}:
>>> // Detect Hardware Capability
>>> // If discovered, then set Type = {SEV,TDX}
>>> // Else, leave it as is
>>>
>>>
>>>
>>>
>>>> Additionally, do you see a need for the HeadLength field in this
>>>> structure ? In asm it is preferred if we can access the actual
>>>> workarea pointer at the fixed location instead of first reading the
>>>> HeadLength to determine how much it need to skip.
>>> [Jiewen] You are right.
>>> Length/Version is NOT absolutely necessary, if the header is simple enough. If
>>> you think we don’t need them, I am OK to remove them.
>>> I think only "Type" is mandatory, which tells us the category.
>>> I think "SubType" is useful, because we might want to know if it is SEV, or SEV-ES,
>>> or SEV-SNP, and if it is TDX 1.0, or TDX.future. Please let me know your thought.
>>>
>>>
>>> One question on SEC_SEV_ES_WORK_AREA. Since you have SEV/SEV-ES/SEV-
>>> SNP, is this SEV_ES_WORK_AREA only for SEV_ES? Or it is also used in SEV (no
>>> SEV_ES), and SEV_SNP ?
>>> For example, when we see SevEsEnable=0, how do we know it is SEV (no SEV_ES)
>>> or legacy (no SEV, no SEV_ES)? Or we don’t need care in SEV (no SEV_ES) case.
>>>
>>>
>>>
>>>
>>>>
>>>>> Thank you
>>>>> Yao Jiewen
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>>> Brijesh Singh via groups.io
>>>>>> Sent: Wednesday, July 28, 2021 11:59 PM
>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Xu,
>>>>>> Min M <min.m.xu@intel.com>
>>>>>> Cc: brijesh.singh@amd.com; Ard Biesheuvel
>>>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
>>>>>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
>>>>>> James Bottomley <jejb@linux.ibm.com>; Tom Lendacky
>>>>>> <thomas.lendacky@amd.com>
>>>>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm
>>>>>> in ResetVector
>>>>>>
>>>>>> Hi Yao Jiewen,
>>>>>>
>>>>>> I guess I am still trying to figure out why we need the header in
>>>>>> the work area. Can't we do something like this:
>>>>>>
>>>>>> typedef struct {
>>>>>> UINT8 SevEsEnabled;
>>>>>>
>>>>>> // If Es is enabled then this field must be zeroed
>>>>>> UINT8 MustBeZero;
>>>>>>
>>>>>> UINT8 Reserved1[6];
>>>>>>
>>>>>> UINT64 RandomData;
>>>>>>
>>>>>> UINT64 EncryptionMask;
>>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>>
>>>>>> typedef struct {
>>>>>> // If Tdx is enabled then it must be zeroed
>>>>>> UINT8 MustBeZero
>>>>>>
>>>>>> UINT8 TdxEnabled;
>>>>>>
>>>>>> UINT8 Reserved2[6];
>>>>>> ....
>>>>>>
>>>>>> } TX_WORK_AREA;
>>>>>>
>>>>>> typedef union {
>>>>>> SEC_SEV_ES_WORK_AREA SevEsWorkArea;
>>>>>> TDX_WORK_AREA TdxWorkArea;
>>>>>> } CC_WORK_AREA;
>>>>>>
>>>>>> I am trying to minimize the changes to the existing code. The SEV
>>>>>> and TDX probe logic should ensure that if the feature is detected,
>>>>>> then it must clear the MustBeZero'ed field.
>>>>>>
>>>>>> Basically, we already have a 64-bit value reserved in the SevEsWork
>>>>>> area and currently only one byte is used and second byte can be
>>>>>> detected for the TDX. Basically the which encryption technology is
>>>>>> active the definition of the work area will change.
>>>>>>
>>>>>> Am I missing something ?
>>>>>>
>>>>>> Thanks
>>>>>>
>>>>>> On 7/28/21 10:22 AM, Yao, Jiewen wrote:
>>>>>>> Hi Brijesh
>>>>>>> Thanks!
>>>>>>>
>>>>>>> I think if we want to reuse this, we need rename the data structure.
>>>>>>>
>>>>>>> First, we should use a generic name.
>>>>>>>
>>>>>>> Second, I don’t think it is good idea to define two *enable*
>>>>>>> fields. Since only
>>>>>> one of them should be enabled, we should use 1 field as enumeration.
>>>>>>> Third, we should hide the SEV specific and TDX specific definition
>>>>>>> in CC
>>>>>> common work area.
>>>>>>> If we agree to use a common work area, I recommend below:
>>>>>>>
>>>>>>> typedef struct {
>>>>>>> UINT8 HeaderVersion; // 0
>>>>>>> UINT8 HeadLength; // 4
>>>>>>> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
>>>>>>> UINT8 SubType; // Type specific sub type, if needed.
>>>>>>> } CC_COMMON_WORK_AREA_HEADER;
>>>>>>>
>>>>>>> typedef struct {
>>>>>>> CC_COMMON_WORK_AREA_HEADER Header;
>>>>>>> // reset is valid if Type == 1
>>>>>>> UINT8 Reserved1[4];
>>>>>>> UINT64 RandomData;
>>>>>>> UINT64 EncryptionMask;
>>>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>>>
>>>>>>> typedef struct {
>>>>>>> CC_COMMON_WORK_AREA_HEADER Header;
>>>>>>> // reset is valid if Type == 2
>>>>>>> UINT8 TdxSpecific[]; // TBD
>>>>>>> } TDX_WORK_AREA;
>>>>>>>
>>>>>>> Thank you
>>>>>>> Yao Jiewen
>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>>>>> Brijesh Singh via groups.io
>>>>>>>> Sent: Wednesday, July 28, 2021 10:34 PM
>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M
>>>> <min.m.xu@intel.com>
>>>>>>>> Cc: brijesh.singh@amd.com; devel@edk2.groups.io; Ard Biesheuvel
>>>>>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
>>>> <jordan.l.justen@intel.com>;
>>>>>>>> Erdem Aktas <erdemaktas@google.com>; James Bottomley
>>>>>>>> <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>>>>>>>> Subject: Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add
>>>>>>>> AmdSev.asm in ResetVector
>>>>>>>>
>>>>>>>> Hi Jiewen and Min,
>>>>>>>>
>>>>>>>> See my comments below.
>>>>>>>>
>>>>>>>>
>>>>>>>> On 7/28/21 2:54 AM, Yao, Jiewen wrote:
>>>>>>>>> Yes. I am thinking the same thing.
>>>>>>>>>
>>>>>>>>> [CC Flag memory location]
>>>>>>>>> 1) A general purpose register, such as EBP.
>>>>>>>>>
>>>>>>>>> 2) A global variable, such as
>>>>>>>>> .data
>>>>>>>>> TeeFlags: DD 0
>>>>>>>>>
>>>>>>>>> 3) A fixed region in stack, such as dword[STACK_TOP - 4]
>>>>>>>>>
>>>>>>>>> 4) A new CC common fixed region, such as dword[CC_COMMON_FLAGS]
>>>>>>>>>
>>>>>>>>> 5) A fixed region piggyback on existing CC working area, such as
>>>>>>>>> dword[CC_WORKING_AREA]
>>>>>>>>>
>>>>>>>>> Hi Brijesh/Min
>>>>>>>>> Any preference?
>>>>>>>>>
>>>>>>>>> [CC Indicator Flags]
>>>>>>>>> Proposal: UINT8[4]
>>>>>>>>>
>>>>>>>>> Byte [0] Version: 0
>>>>>>>>> byte [1] Length: 4
>>>>>>>>> byte [2] Type:
>>>>>>>>> 0: legacy
>>>>>>>>> 1: SEV
>>>>>>>>> 2: TDX
>>>>>>>>> byte [3] Sub Type:
>>>>>>>>> If Type is 0 (legacy), then
>>>>>>>>> 0: legacy
>>>>>>>>> If Type is 1 (SEV), then
>>>>>>>>> 0: SEV
>>>>>>>>> 1: SEV-ES
>>>>>>>>> 2: SEV-SNP
>>>>>>>>> If Type is 2 (TDX), then
>>>>>>>>> 0: TDX 1.0
>>>>>>>>>
>>>>>>>>> Thank you
>>>>>>>>> Yao Jiewen
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>> -----Original Message-----
>>>>>>>>>> From: Xu, Min M <min.m.xu@intel.com>
>>>>>>>>>> Sent: Wednesday, July 28, 2021 2:58 PM
>>>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>
>>>>>>>>>> Cc: Brijesh Singh <brijesh.singh@amd.com>;
>>>>>>>>>> devel@edk2.groups.io; Ard Biesheuvel
>>>>>>>>>> <ardb+tianocore@kernel.org>; Justen, Jordan L
>>>>>>>>>> <jordan.l.justen@intel.com>; Erdem Aktas
>>>>>>>>>> <erdemaktas@google.com>;
>>>>>>>> James
>>>>>>>>>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky
>>>>>>>> <thomas.lendacky@amd.com>
>>>>>>>>>> Subject: RE: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>>>> ResetVector
>>>>>>>>>> On July 28, 2021 2:05 PM, Yao, Jiewen wrote:
>>>>>>>>>>> It does not necessary to be a working area.
>>>>>>>>>>>
>>>>>>>>>>> We just need a common TEE flag to indicate if the system run
>>>>>>>>>>> in legacy,
>>>>>> SEV,
>>>>>>>>>> or
>>>>>>>>>>> TDX, right?
>>>>>>>>>> Right. We need somewhere to store this flag, either in a
>>>>>>>>>> Register or in
>>>>>>>> Memory.
>>>>>>>>>> If it is memory, then in Tdx the memory region should be
>>>>>>>>>> initialized by
>>>> host
>>>>>>>> VMM.
>>>>>>>>>>> thank you!
>>>>>>>>>>> Yao, Jiewen
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>> 在 2021年7月28日,下午1:07,Xu, Min M
>>> <min.m.xu@intel.com>
>>>>>> 写
>>>>>>>> 道:
>>>>>>>>>>>> On July 27, 2021 8:46 PM, Yao, Jiewen wrote:
>>>>>>>>>>>>> HI Min
>>>>>>>>>>>>> I agree with Brijesh.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The basic rule is: SEV file shall never refer to TDX data structure.
>>>>>>>>>>>>> TDX file shall never refer to SEV data structure.
>>>>>>>>>>>>> These code should be isolated clearly.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Do we still need that logic if we follow the new pattern?
>>>>>>>>>>>> I have replied to Brijesh's mail about the concern of the new pattern.
>>>>>>>>>>>>
>>>>>>>>>>>> I have some concern in the current pattern:
>>>>>>>>>>>> ====================
>>>>>>>>>>>> OneTimeCall PreMainFunctionHookSev
>>>>>>>>>>>> OneTimeCall PreMainFunctionHookTdx
>>>>>>>>>>>> MainFunction:
>>>>>>>>>>>> XXXXXX
>>>>>>>>>>>> OneTimeCall PostMainFunctionHookSev
>>>>>>>>>>>> OneTimeCall PostMainFunctionHookTdx
>>>>>>>>>>>> ====================
>>>>>>>>>>>> The TEE function need implement a TEE check function (such as
>>>>>>>>>>>> IsSev,
>>>> or
>>>>>>>>>> IsTdx).
>>>>>>>>>>>> Tdx call CPUID(0x21) to determine if it is tdx guest in the
>>>>>>>>>>>> very beginning of ResetVector. Then 'TDXG' is set in
>>> TDX_WORK_AREA.
>>>> SEV
>>>>>>>> does
>>>>>>>>>>> the similar work which call CheckSevFeatures to set
>>>> SEV_ES_WORK_AREA
>>>>>> to
>>>>>>>> 1.
>>>>>>>>>>>> After that both TDX and SEV read the above WORK_AREA to check
>>>>>>>>>>>> if it
>>>> is
>>>>>>>> TDX
>>>>>>>>>>> or SEV or legacy guest.
>>>>>>>>>>>> In Tdx the access to SEV_ES_WORK_AREA will trigger error
>>>>>>>>>>>> because
>>>>>>>>>>> SEV_ES_WORK_AREA is *NOT* initialized by host VMM.
>>>>>>>>>>>> In SEV-SNP I am afraid the access to TDX_WORK_AREA will
>>>>>>>>>>>> trigger
>>>> error
>>>>>>>> too.
>>>>>>>>>>>> I am wondering if TDX and SEV can use the same memory region
>>>>>>>>>>>> (for
>>>>>>>>>> example,
>>>>>>>>>>> TEE_WORK_AREA) as the work area?
>>>>>>>>>>>> So that this work area is guaranteed to be initialized in
>>>>>>>>>>>> both TDX and SEV. Structure of the TEE_WORK_AREA may look like
>>> this:
>>>>>>>>>>>> typedef struct {
>>>>>>>>>>>> UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
>>>>>>>>>>>> UINT8 Others[];
>>>>>>>>>>>> } TEE_WORK_AREA;
>>>>>>>> Are we reserving a new page for the TDX_WORK_AREA ? I am
>>>>>>>> wondering
>>>> why
>>>>>>>> can't we use the SEV_ES_WORK_AREA instead of wasting space in the
>>>>>> MEMFD.
>>>>>>>> The SEV_ES_WORK_AREA layout looks like this:
>>>>>>>>
>>>>>>>> typedef struct _SEC_SEV_ES_WORK_AREA {
>>>>>>>> UINT8 SevEsEnabled;
>>>>>>>> UINT8 Reserved1[7];
>>>>>>>>
>>>>>>>> UINT64 RandomData;
>>>>>>>>
>>>>>>>> UINT64 EncryptionMask;
>>>>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>>>>
>>>>>>>> There is reserved bit after the SevEsEnabled and one byte can be
>>>>>>>> used for the TdxEnabled;
>>>>>>>>
>>>>>>>> typedef struct _SEC_SEV_ES_WORK_AREA {
>>>>>>>> UINT8 SevEsEnabled;
>>>>>>>> UINT8 TdxEnabled;
>>>>>>>> UINT8 Reserved2[6];
>>>>>>>>
>>>>>>>> UINT64 RandomData;
>>>>>>>>
>>>>>>>> UINT64 EncryptionMask;
>>>>>>>> } SEC_SEV_ES_WORK_AREA;
>>>>>>>>
>>>>>>>> The SEV_ES_WORK_AREA can be treated as a TEE_WORK_AREA and we
>>>> can
>>>>>> be
>>>>>>>> pull out from MemEncrypSevLib.h to CcWorkAreaLib.h and rename the
>>>>>>>> structure (if needed).
>>>>>>>>
>>>>>>>> Both the SEV-SNP and TEE host-VMM accepts the TEE_WORK_AREA
>>>> before
>>>>>>>> booting the guest to ensure that its safe to access the memory
>>>>>>>> without going through the accept/validation process.
>>>>>>>>
>>>>>>>> In case of the TDX, the reset vector code sets the TdxEnabled on
>>>>>>>> the entry. In case of the SEV, the workarea is valid from SEC to
>>>>>>>> PEI phase only and it gets reused for other purposes. The PEI
>>>>>>>> phase set the Pcd's (such as SevEsEnabled or SevEnabled etc) so
>>>>>>>> that Dxe or other EDK2 core does not need to know anything about
>>>>>>>> the workarea and they simply can read the PCDs.
>>>>>>>>
>>>>>>>> -Brijesh
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>
>
>
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 4:29 ` Brijesh Singh
2021-07-29 5:17 ` Yao, Jiewen
@ 2021-07-29 6:07 ` Min Xu
2021-07-29 10:07 ` Brijesh Singh
1 sibling, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-29 6:07 UTC (permalink / raw)
To: Brijesh Singh, Yao, Jiewen, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
On July 29, 2021 12:29 PM, Brijesh Singh wrote:
> On 7/28/21 9:44 PM, Xu, Min M wrote:
> > Jiewen & Singh
> >
> > From the discussion I am thinking we have below rules to follow to the
> > design the structure of TEE_WORK_AREA:
> > 1. Design should be flexible but not too complicated 2. Reuse the
> > current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as TEE_WORK_AREA 3.
> > TEE_WORK_AREA should be initialized to all-0 at the beginning of
> > ResetVecotr 4. Reduce the changes to exiting code if possible
> >
> > So I try to make below conclusions below: (Please review) 1.
> > SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and SEV,
> > maybe in the future it can be used by other CC technologies.
> >
> > 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is
> > guaranteed to be cleared in legacy guest. In TDX this memory region is
> > initialized to be all-0 by host VMM. In SEV the memory region is cleared as well.
> > 0x00B000|0x001000
> >
> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpace
> Guid.PcdSevEsWorkAreaSize
> > DATA = {
> > 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> > }
>
> Hmm, I thought the contents of the data pages are controlled by the host VMM.
> If the backing pages are not zero filled then there is no guarantee that memory
> will be zero. To verify it:
>
> 1. I applied your above change in OvmfPkgX86.fdt. I modified the DATA values
> from 0x00 -> 0xCC
>
> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
>
> And dump does not contain the 0xcc.
>
> And to confirm further, I attached to the qemu with the GDB before the booting
> the OVMF, and modified the SevEsWorkArea with some garbage number and
> this time the dump printed garbage value I put through the debugger.
>
> In summary, the OVMF to zero the workarea memory on the entry and we
> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
So in legacy guest, CCWorkArea is cleared to all-0 without the DATA={0x00,0x00...}, right?
>
> Did I miss something ?
>
>
> >
> > 3. The structure of TEE_WORK_AREA
> > The current SEV_ES_WORK_AREA is defined as below:
> > typedef struct {
> > UINT8 SevEsEnabled;
> > UINT8 Reserved1[7];
> > [Others...]
> > } SEC_SEV_ES_WORK_AREA;
> >
> > So I think the TEE_WORK_AREA can be:
> > Byte[0] Type:
> > 0: legacy 1: SEV 2: TDX
> > Byte[1] Subtype:
> > If Type is 0, then it is 0
> > If Type is 1, then it is up to SEV's definition
> > If Type is 2, then it is up to TDX's definition Byte[] other bytes
> > are defined based on the Type/Subtype
>
> I personally like Yao Jiewen's struct definition, but I can also live with this one as
> well :). The only question I had was with his proposal was what if we remove the
> Length and Version fields. If the header length was fixed then life would be much
> easier in the ASM code.
Yao Jiewen's structure is like below. If the HeaderVersion/HeaderLength are removed
you will find it is just what I am saying. The first 2 bytes are used to distinguish the
legacy/SEV/TDX. The left bytes are up to the first 2 bytes.
typedef struct {
UINT8 HeaderVersion; // 0
UINT8 HeadLength; // 4
UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
UINT8 SubType; // Type specific sub type, if needed.
} CC_COMMON_WORK_AREA_HEADER;
typedef struct {
CC_COMMON_WORK_AREA_HEADER Header;
// reset is valid if Type == 1
UINT8 Reserved1[4];
UINT64 RandomData;
UINT64 EncryptionMask;
} SEC_SEV_ES_WORK_AREA;
typedef struct {
CC_COMMON_WORK_AREA_HEADER Header;
// reset is valid if Type == 2
UINT8 TdxSpecific[]; // TBD
} TDX_WORK_AREA;
>
>
> > I check the code in SecMain.c.
> > SevEsIsEnabled() need updated to check SevEsWorkarea->SevEsEnabled == 1,
> not non-0.
> > @Brijesh Singh Is there any other code need update?
>
> As noted before, the SevEsWorkAreas is used to pass the information from the
> Sec to PEI phase. The workarea gets reused for totally different purpose after
> the PEI phase.
So only the above line in SecMain.c/SevEsIsEnabled() need updated, right?
>
Thanks!
Xu, Min
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 6:07 ` Min Xu
@ 2021-07-29 10:07 ` Brijesh Singh
2021-07-29 11:53 ` Min Xu
0 siblings, 1 reply; 36+ messages in thread
From: Brijesh Singh @ 2021-07-29 10:07 UTC (permalink / raw)
To: Xu, Min M, Yao, Jiewen, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
On 7/29/21 1:07 AM, Xu, Min M wrote:
> On July 29, 2021 12:29 PM, Brijesh Singh wrote:
>> On 7/28/21 9:44 PM, Xu, Min M wrote:
>>> Jiewen & Singh
>>>
>>> From the discussion I am thinking we have below rules to follow to the
>>> design the structure of TEE_WORK_AREA:
>>> 1. Design should be flexible but not too complicated 2. Reuse the
>>> current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as TEE_WORK_AREA 3.
>>> TEE_WORK_AREA should be initialized to all-0 at the beginning of
>>> ResetVecotr 4. Reduce the changes to exiting code if possible
>>>
>>> So I try to make below conclusions below: (Please review) 1.
>>> SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and SEV,
>>> maybe in the future it can be used by other CC technologies.
>>>
>>> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is
>>> guaranteed to be cleared in legacy guest. In TDX this memory region is
>>> initialized to be all-0 by host VMM. In SEV the memory region is cleared as well.
>>> 0x00B000|0x001000
>>>
>> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpace
>> Guid.PcdSevEsWorkAreaSize
>>> DATA = {
>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
>>> }
>> Hmm, I thought the contents of the data pages are controlled by the host VMM.
>> If the backing pages are not zero filled then there is no guarantee that memory
>> will be zero. To verify it:
>>
>> 1. I applied your above change in OvmfPkgX86.fdt. I modified the DATA values
>> from 0x00 -> 0xCC
>>
>> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
>>
>> And dump does not contain the 0xcc.
>>
>> And to confirm further, I attached to the qemu with the GDB before the booting
>> the OVMF, and modified the SevEsWorkArea with some garbage number and
>> this time the dump printed garbage value I put through the debugger.
>>
>> In summary, the OVMF to zero the workarea memory on the entry and we
>> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
> So in legacy guest, CCWorkArea is cleared to all-0 without the DATA={0x00,0x00...}, right?
Okay, maybe I was not able to communicate it correctly.
The run I did is for the legacy guest. For the legacy guest, the
contents of the CCWorkArea may *not* be always zero even when you use
the DATA={0x00, 0x00...}.
Currently, Qemu uses zero filled backing pages, so we will get a zero
filled CCWorkArea; but nothing says that a backing page *must* be zero.
Another VMM may choose to do things differently. In summary, the OVMF
reset vector code must zero the CCWorkArea before calling SEV or TDX
probes.
thanks
>
>> Did I miss something ?
>>
>>
>>> 3. The structure of TEE_WORK_AREA
>>> The current SEV_ES_WORK_AREA is defined as below:
>>> typedef struct {
>>> UINT8 SevEsEnabled;
>>> UINT8 Reserved1[7];
>>> [Others...]
>>> } SEC_SEV_ES_WORK_AREA;
>>>
>>> So I think the TEE_WORK_AREA can be:
>>> Byte[0] Type:
>>> 0: legacy 1: SEV 2: TDX
>>> Byte[1] Subtype:
>>> If Type is 0, then it is 0
>>> If Type is 1, then it is up to SEV's definition
>>> If Type is 2, then it is up to TDX's definition Byte[] other bytes
>>> are defined based on the Type/Subtype
>> I personally like Yao Jiewen's struct definition, but I can also live with this one as
>> well :). The only question I had was with his proposal was what if we remove the
>> Length and Version fields. If the header length was fixed then life would be much
>> easier in the ASM code.
> Yao Jiewen's structure is like below. If the HeaderVersion/HeaderLength are removed
> you will find it is just what I am saying. The first 2 bytes are used to distinguish the
> legacy/SEV/TDX. The left bytes are up to the first 2 bytes.
> typedef struct {
> UINT8 HeaderVersion; // 0
> UINT8 HeadLength; // 4
> UINT8 Type; // 0 - legacy, 1 - SEV, 2 - TDX
> UINT8 SubType; // Type specific sub type, if needed.
> } CC_COMMON_WORK_AREA_HEADER;
>
> typedef struct {
> CC_COMMON_WORK_AREA_HEADER Header;
> // reset is valid if Type == 1
> UINT8 Reserved1[4];
> UINT64 RandomData;
> UINT64 EncryptionMask;
> } SEC_SEV_ES_WORK_AREA;
>
> typedef struct {
> CC_COMMON_WORK_AREA_HEADER Header;
> // reset is valid if Type == 2
> UINT8 TdxSpecific[]; // TBD
> } TDX_WORK_AREA;
>>
>>> I check the code in SecMain.c.
>>> SevEsIsEnabled() need updated to check SevEsWorkarea->SevEsEnabled == 1,
>> not non-0.
>>> @Brijesh Singh Is there any other code need update?
>> As noted before, the SevEsWorkAreas is used to pass the information from the
>> Sec to PEI phase. The workarea gets reused for totally different purpose after
>> the PEI phase.
> So only the above line in SecMain.c/SevEsIsEnabled() need updated, right?
> Thanks!
> Xu, Min
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 10:07 ` Brijesh Singh
@ 2021-07-29 11:53 ` Min Xu
2021-07-29 12:12 ` Yao, Jiewen
0 siblings, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-29 11:53 UTC (permalink / raw)
To: Brijesh Singh, Yao, Jiewen, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
On July 29, 2021 6:08 PM, Brijesh Singh wrote:
> On 7/29/21 1:07 AM, Xu, Min M wrote:
> > On July 29, 2021 12:29 PM, Brijesh Singh wrote:
> >> On 7/28/21 9:44 PM, Xu, Min M wrote:
> >>> Jiewen & Singh
> >>>
> >>> From the discussion I am thinking we have below rules to follow to
> >>> the design the structure of TEE_WORK_AREA:
> >>> 1. Design should be flexible but not too complicated 2. Reuse the
> >>> current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as
> TEE_WORK_AREA 3.
> >>> TEE_WORK_AREA should be initialized to all-0 at the beginning of
> >>> ResetVecotr 4. Reduce the changes to exiting code if possible
> >>>
> >>> So I try to make below conclusions below: (Please review) 1.
> >>> SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and
> SEV,
> >>> maybe in the future it can be used by other CC technologies.
> >>>
> >>> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is
> >>> guaranteed to be cleared in legacy guest. In TDX this memory region
> >>> is initialized to be all-0 by host VMM. In SEV the memory region is
> cleared as well.
> >>> 0x00B000|0x001000
> >>>
> >>
> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
> ce
> >> Guid.PcdSevEsWorkAreaSize
> >>> DATA = {
> >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> >>> }
> >> Hmm, I thought the contents of the data pages are controlled by the host
> VMM.
> >> If the backing pages are not zero filled then there is no guarantee
> >> that memory will be zero. To verify it:
> >>
> >> 1. I applied your above change in OvmfPkgX86.fdt. I modified the DATA
> >> values from 0x00 -> 0xCC
> >>
> >> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
> >>
> >> And dump does not contain the 0xcc.
> >>
> >> And to confirm further, I attached to the qemu with the GDB before
> >> the booting the OVMF, and modified the SevEsWorkArea with some
> >> garbage number and this time the dump printed garbage value I put
> through the debugger.
> >>
> >> In summary, the OVMF to zero the workarea memory on the entry and
> we
> >> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
> > So in legacy guest, CCWorkArea is cleared to all-0 without the
> DATA={0x00,0x00...}, right?
>
> Okay, maybe I was not able to communicate it correctly.
>
> The run I did is for the legacy guest. For the legacy guest, the contents of the
> CCWorkArea may *not* be always zero even when you use the DATA={0x00,
> 0x00...}.
>
> Currently, Qemu uses zero filled backing pages, so we will get a zero filled
> CCWorkArea; but nothing says that a backing page *must* be zero.
> Another VMM may choose to do things differently. In summary, the OVMF
> reset vector code must zero the CCWorkArea before calling SEV or TDX
> probes.
>
Ah, I see.
In current CheckSevFeatures, byte[SEV_ES_WORK_AREA] is cleared to0.
Then its values is set based on the result of SEV probe.
There is a bug here. CheckTdxFeatures does the similar work and it sets the
WORK_AREA to 2. If CheckSevFeatures is called after CheckTdxFeatures, then
WORK_AREA is cleared and it is set to 0 because it is not SEV. The value is override.
I think there are 2 options:
Option 1:
Neither CheckTdxFeatures nor CheckSevFeatures should clear WORK_AREA. Instead
It should be cleared to 0 outside and before these 2 calls. So in Main16 after
TransitionFromReal16To32BitFlat WORK_AREA is cleared to 0. In Tdx guest this WORK_AREA
is initialized to 0 by host VMM.
Option 2:
Another option is to figure out a mechanism that only one CheckXXXFeatures is called.
Since there are 2 entry point in Main.asm: Main16 and Main32.
In Main16 CheckSevFeatures is called after TransitionFromReal16To32BitFlat. (eax should
be saved because it is used in SetCr3ForPageTables64)
In Main32 CheckTdxFeatures is called after ReloadFlat32.
What's your opinion?
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 11:53 ` Min Xu
@ 2021-07-29 12:12 ` Yao, Jiewen
2021-07-29 12:46 ` Brijesh Singh
2021-07-29 13:22 ` Min Xu
0 siblings, 2 replies; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-29 12:12 UTC (permalink / raw)
To: Xu, Min M, Brijesh Singh, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
Hey
I am not sure why Min did not response to my latest email.
I did give suggestion in my previous comment.
=====
CcWorkArea.Type = 0;
InitCcWorkAreaSev(); // set Type=1 if SEV
InitCcWorkAreaTdx(); // set Type=2 if TDX
=====
That is option 1.
Thank you
Yao Jiewen
> -----Original Message-----
> From: Xu, Min M <min.m.xu@intel.com>
> Sent: Thursday, July 29, 2021 7:54 PM
> To: Brijesh Singh <brijesh.singh@amd.com>; Yao, Jiewen
> <jiewen.yao@intel.com>; devel@edk2.groups.io
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> ResetVector
>
> On July 29, 2021 6:08 PM, Brijesh Singh wrote:
> > On 7/29/21 1:07 AM, Xu, Min M wrote:
> > > On July 29, 2021 12:29 PM, Brijesh Singh wrote:
> > >> On 7/28/21 9:44 PM, Xu, Min M wrote:
> > >>> Jiewen & Singh
> > >>>
> > >>> From the discussion I am thinking we have below rules to follow to
> > >>> the design the structure of TEE_WORK_AREA:
> > >>> 1. Design should be flexible but not too complicated 2. Reuse the
> > >>> current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as
> > TEE_WORK_AREA 3.
> > >>> TEE_WORK_AREA should be initialized to all-0 at the beginning of
> > >>> ResetVecotr 4. Reduce the changes to exiting code if possible
> > >>>
> > >>> So I try to make below conclusions below: (Please review) 1.
> > >>> SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and
> > SEV,
> > >>> maybe in the future it can be used by other CC technologies.
> > >>>
> > >>> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is
> > >>> guaranteed to be cleared in legacy guest. In TDX this memory region
> > >>> is initialized to be all-0 by host VMM. In SEV the memory region is
> > cleared as well.
> > >>> 0x00B000|0x001000
> > >>>
> > >>
> > gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
> > ce
> > >> Guid.PcdSevEsWorkAreaSize
> > >>> DATA = {
> > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> > >>> }
> > >> Hmm, I thought the contents of the data pages are controlled by the host
> > VMM.
> > >> If the backing pages are not zero filled then there is no guarantee
> > >> that memory will be zero. To verify it:
> > >>
> > >> 1. I applied your above change in OvmfPkgX86.fdt. I modified the DATA
> > >> values from 0x00 -> 0xCC
> > >>
> > >> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
> > >>
> > >> And dump does not contain the 0xcc.
> > >>
> > >> And to confirm further, I attached to the qemu with the GDB before
> > >> the booting the OVMF, and modified the SevEsWorkArea with some
> > >> garbage number and this time the dump printed garbage value I put
> > through the debugger.
> > >>
> > >> In summary, the OVMF to zero the workarea memory on the entry and
> > we
> > >> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
> > > So in legacy guest, CCWorkArea is cleared to all-0 without the
> > DATA={0x00,0x00...}, right?
> >
> > Okay, maybe I was not able to communicate it correctly.
> >
> > The run I did is for the legacy guest. For the legacy guest, the contents of the
> > CCWorkArea may *not* be always zero even when you use the DATA={0x00,
> > 0x00...}.
> >
> > Currently, Qemu uses zero filled backing pages, so we will get a zero filled
> > CCWorkArea; but nothing says that a backing page *must* be zero.
> > Another VMM may choose to do things differently. In summary, the OVMF
> > reset vector code must zero the CCWorkArea before calling SEV or TDX
> > probes.
> >
> Ah, I see.
> In current CheckSevFeatures, byte[SEV_ES_WORK_AREA] is cleared to0.
> Then its values is set based on the result of SEV probe.
>
> There is a bug here. CheckTdxFeatures does the similar work and it sets the
> WORK_AREA to 2. If CheckSevFeatures is called after CheckTdxFeatures, then
> WORK_AREA is cleared and it is set to 0 because it is not SEV. The value is
> override.
>
> I think there are 2 options:
> Option 1:
> Neither CheckTdxFeatures nor CheckSevFeatures should clear WORK_AREA.
> Instead
> It should be cleared to 0 outside and before these 2 calls. So in Main16 after
> TransitionFromReal16To32BitFlat WORK_AREA is cleared to 0. In Tdx guest this
> WORK_AREA
> is initialized to 0 by host VMM.
>
> Option 2:
> Another option is to figure out a mechanism that only one CheckXXXFeatures is
> called.
> Since there are 2 entry point in Main.asm: Main16 and Main32.
> In Main16 CheckSevFeatures is called after TransitionFromReal16To32BitFlat.
> (eax should
> be saved because it is used in SetCr3ForPageTables64)
> In Main32 CheckTdxFeatures is called after ReloadFlat32.
>
> What's your opinion?
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 12:12 ` Yao, Jiewen
@ 2021-07-29 12:46 ` Brijesh Singh
2021-07-29 13:22 ` Min Xu
1 sibling, 0 replies; 36+ messages in thread
From: Brijesh Singh @ 2021-07-29 12:46 UTC (permalink / raw)
To: Yao, Jiewen, Xu, Min M, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
On 7/29/21 7:12 AM, Yao, Jiewen wrote:
> Hey
> I am not sure why Min did not response to my latest email.
> I did give suggestion in my previous comment.
>
> =====
> CcWorkArea.Type = 0;
> InitCcWorkAreaSev(); // set Type=1 if SEV
> InitCcWorkAreaTdx(); // set Type=2 if TDX
> =====
>
> That is option 1.
Yes that is exactly what we want Jiewen.
The OvmfPkg reset vector should initialize the type to zero on entry,
and SEV/TDX will update the value (only if the feature is detected).
> Thank you
> Yao Jiewen
>
>> -----Original Message-----
>> From: Xu, Min M <min.m.xu@intel.com>
>> Sent: Thursday, July 29, 2021 7:54 PM
>> To: Brijesh Singh <brijesh.singh@amd.com>; Yao, Jiewen
>> <jiewen.yao@intel.com>; devel@edk2.groups.io
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
>> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
>> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
>> Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
>> ResetVector
>>
>> On July 29, 2021 6:08 PM, Brijesh Singh wrote:
>>> On 7/29/21 1:07 AM, Xu, Min M wrote:
>>>> On July 29, 2021 12:29 PM, Brijesh Singh wrote:
>>>>> On 7/28/21 9:44 PM, Xu, Min M wrote:
>>>>>> Jiewen & Singh
>>>>>>
>>>>>> From the discussion I am thinking we have below rules to follow to
>>>>>> the design the structure of TEE_WORK_AREA:
>>>>>> 1. Design should be flexible but not too complicated 2. Reuse the
>>>>>> current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as
>>> TEE_WORK_AREA 3.
>>>>>> TEE_WORK_AREA should be initialized to all-0 at the beginning of
>>>>>> ResetVecotr 4. Reduce the changes to exiting code if possible
>>>>>>
>>>>>> So I try to make below conclusions below: (Please review) 1.
>>>>>> SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and
>>> SEV,
>>>>>> maybe in the future it can be used by other CC technologies.
>>>>>>
>>>>>> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is
>>>>>> guaranteed to be cleared in legacy guest. In TDX this memory region
>>>>>> is initialized to be all-0 by host VMM. In SEV the memory region is
>>> cleared as well.
>>>>>> 0x00B000|0x001000
>>>>>>
>>> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
>>> ce
>>>>> Guid.PcdSevEsWorkAreaSize
>>>>>> DATA = {
>>>>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>>>>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>>>>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
>>>>>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
>>>>>> }
>>>>> Hmm, I thought the contents of the data pages are controlled by the host
>>> VMM.
>>>>> If the backing pages are not zero filled then there is no guarantee
>>>>> that memory will be zero. To verify it:
>>>>>
>>>>> 1. I applied your above change in OvmfPkgX86.fdt. I modified the DATA
>>>>> values from 0x00 -> 0xCC
>>>>>
>>>>> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
>>>>>
>>>>> And dump does not contain the 0xcc.
>>>>>
>>>>> And to confirm further, I attached to the qemu with the GDB before
>>>>> the booting the OVMF, and modified the SevEsWorkArea with some
>>>>> garbage number and this time the dump printed garbage value I put
>>> through the debugger.
>>>>> In summary, the OVMF to zero the workarea memory on the entry and
>>> we
>>>>> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
>>>> So in legacy guest, CCWorkArea is cleared to all-0 without the
>>> DATA={0x00,0x00...}, right?
>>>
>>> Okay, maybe I was not able to communicate it correctly.
>>>
>>> The run I did is for the legacy guest. For the legacy guest, the contents of the
>>> CCWorkArea may *not* be always zero even when you use the DATA={0x00,
>>> 0x00...}.
>>>
>>> Currently, Qemu uses zero filled backing pages, so we will get a zero filled
>>> CCWorkArea; but nothing says that a backing page *must* be zero.
>>> Another VMM may choose to do things differently. In summary, the OVMF
>>> reset vector code must zero the CCWorkArea before calling SEV or TDX
>>> probes.
>>>
>> Ah, I see.
>> In current CheckSevFeatures, byte[SEV_ES_WORK_AREA] is cleared to0.
>> Then its values is set based on the result of SEV probe.
>>
>> There is a bug here. CheckTdxFeatures does the similar work and it sets the
>> WORK_AREA to 2. If CheckSevFeatures is called after CheckTdxFeatures, then
>> WORK_AREA is cleared and it is set to 0 because it is not SEV. The value is
>> override.
>>
>> I think there are 2 options:
>> Option 1:
>> Neither CheckTdxFeatures nor CheckSevFeatures should clear WORK_AREA.
>> Instead
>> It should be cleared to 0 outside and before these 2 calls. So in Main16 after
>> TransitionFromReal16To32BitFlat WORK_AREA is cleared to 0. In Tdx guest this
>> WORK_AREA
>> is initialized to 0 by host VMM.
>>
>> Option 2:
>> Another option is to figure out a mechanism that only one CheckXXXFeatures is
>> called.
>> Since there are 2 entry point in Main.asm: Main16 and Main32.
>> In Main16 CheckSevFeatures is called after TransitionFromReal16To32BitFlat.
>> (eax should
>> be saved because it is used in SetCr3ForPageTables64)
>> In Main32 CheckTdxFeatures is called after ReloadFlat32.
>>
>> What's your opinion?
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 12:12 ` Yao, Jiewen
2021-07-29 12:46 ` Brijesh Singh
@ 2021-07-29 13:22 ` Min Xu
2021-07-29 15:37 ` Yao, Jiewen
1 sibling, 1 reply; 36+ messages in thread
From: Min Xu @ 2021-07-29 13:22 UTC (permalink / raw)
To: Yao, Jiewen, Brijesh Singh, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
On July 29, 2021 8:13 PM, Yao Jiewen wrote:
> Hey
> I am not sure why Min did not response to my latest email.
> I did give suggestion in my previous comment.
>
Ah, sorry I missed it. There are too many mails.
> =====
> CcWorkArea.Type = 0;
> InitCcWorkAreaSev(); // set Type=1 if SEV InitCcWorkAreaTdx(); // set Type=2 if
> TDX =====
>
> That is option 1.
>
> Thank you
> Yao Jiewen
>
> > -----Original Message-----
> > From: Xu, Min M <min.m.xu@intel.com>
> > Sent: Thursday, July 29, 2021 7:54 PM
> > To: Brijesh Singh <brijesh.singh@amd.com>; Yao, Jiewen
> > <jiewen.yao@intel.com>; devel@edk2.groups.io
> > Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> > <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> > James Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> > <thomas.lendacky@amd.com>
> > Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> > ResetVector
> >
> > On July 29, 2021 6:08 PM, Brijesh Singh wrote:
> > > On 7/29/21 1:07 AM, Xu, Min M wrote:
> > > > On July 29, 2021 12:29 PM, Brijesh Singh wrote:
> > > >> On 7/28/21 9:44 PM, Xu, Min M wrote:
> > > >>> Jiewen & Singh
> > > >>>
> > > >>> From the discussion I am thinking we have below rules to follow
> > > >>> to the design the structure of TEE_WORK_AREA:
> > > >>> 1. Design should be flexible but not too complicated 2. Reuse
> > > >>> the current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as
> > > TEE_WORK_AREA 3.
> > > >>> TEE_WORK_AREA should be initialized to all-0 at the beginning of
> > > >>> ResetVecotr 4. Reduce the changes to exiting code if possible
> > > >>>
> > > >>> So I try to make below conclusions below: (Please review) 1.
> > > >>> SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and
> > > SEV,
> > > >>> maybe in the future it can be used by other CC technologies.
> > > >>>
> > > >>> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is
> > > >>> guaranteed to be cleared in legacy guest. In TDX this memory
> > > >>> region is initialized to be all-0 by host VMM. In SEV the memory
> > > >>> region is
> > > cleared as well.
> > > >>> 0x00B000|0x001000
> > > >>>
> > > >>
> > >
> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
> > > ce
> > > >> Guid.PcdSevEsWorkAreaSize
> > > >>> DATA = {
> > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> > > >>> }
> > > >> Hmm, I thought the contents of the data pages are controlled by
> > > >> the host
> > > VMM.
> > > >> If the backing pages are not zero filled then there is no
> > > >> guarantee that memory will be zero. To verify it:
> > > >>
> > > >> 1. I applied your above change in OvmfPkgX86.fdt. I modified the
> > > >> DATA values from 0x00 -> 0xCC
> > > >>
> > > >> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
> > > >>
> > > >> And dump does not contain the 0xcc.
> > > >>
> > > >> And to confirm further, I attached to the qemu with the GDB
> > > >> before the booting the OVMF, and modified the SevEsWorkArea with
> > > >> some garbage number and this time the dump printed garbage value
> > > >> I put
> > > through the debugger.
> > > >>
> > > >> In summary, the OVMF to zero the workarea memory on the entry and
> > > we
> > > >> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
> > > > So in legacy guest, CCWorkArea is cleared to all-0 without the
> > > DATA={0x00,0x00...}, right?
> > >
> > > Okay, maybe I was not able to communicate it correctly.
> > >
> > > The run I did is for the legacy guest. For the legacy guest, the
> > > contents of the CCWorkArea may *not* be always zero even when you
> > > use the DATA={0x00, 0x00...}.
> > >
> > > Currently, Qemu uses zero filled backing pages, so we will get a
> > > zero filled CCWorkArea; but nothing says that a backing page *must* be
> zero.
> > > Another VMM may choose to do things differently. In summary, the
> > > OVMF reset vector code must zero the CCWorkArea before calling SEV
> > > or TDX probes.
> > >
> > Ah, I see.
> > In current CheckSevFeatures, byte[SEV_ES_WORK_AREA] is cleared to0.
> > Then its values is set based on the result of SEV probe.
> >
> > There is a bug here. CheckTdxFeatures does the similar work and it
> > sets the WORK_AREA to 2. If CheckSevFeatures is called after
> > CheckTdxFeatures, then WORK_AREA is cleared and it is set to 0 because
> > it is not SEV. The value is override.
> >
> > I think there are 2 options:
> > Option 1:
> > Neither CheckTdxFeatures nor CheckSevFeatures should clear WORK_AREA.
> > Instead
> > It should be cleared to 0 outside and before these 2 calls. So in
> > Main16 after TransitionFromReal16To32BitFlat WORK_AREA is cleared to
> > 0. In Tdx guest this WORK_AREA is initialized to 0 by host VMM.
> >
> > Option 2:
> > Another option is to figure out a mechanism that only one
> > CheckXXXFeatures is called.
> > Since there are 2 entry point in Main.asm: Main16 and Main32.
> > In Main16 CheckSevFeatures is called after TransitionFromReal16To32BitFlat.
> > (eax should
> > be saved because it is used in SetCr3ForPageTables64) In Main32
> > CheckTdxFeatures is called after ReloadFlat32.
> >
> > What's your opinion?
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-29 13:22 ` Min Xu
@ 2021-07-29 15:37 ` Yao, Jiewen
0 siblings, 0 replies; 36+ messages in thread
From: Yao, Jiewen @ 2021-07-29 15:37 UTC (permalink / raw)
To: Xu, Min M, Brijesh Singh, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Tom Lendacky
Indeed. Too many emails.
Glad that we can reach consensus finally. :-)
Thanks, Min and Brijesh.
> -----Original Message-----
> From: Xu, Min M <min.m.xu@intel.com>
> Sent: Thursday, July 29, 2021 9:22 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; Brijesh Singh
> <brijesh.singh@amd.com>; devel@edk2.groups.io
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>; James
> Bottomley <jejb@linux.ibm.com>; Tom Lendacky <thomas.lendacky@amd.com>
> Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> ResetVector
>
> On July 29, 2021 8:13 PM, Yao Jiewen wrote:
> > Hey
> > I am not sure why Min did not response to my latest email.
> > I did give suggestion in my previous comment.
> >
> Ah, sorry I missed it. There are too many mails.
> > =====
> > CcWorkArea.Type = 0;
> > InitCcWorkAreaSev(); // set Type=1 if SEV InitCcWorkAreaTdx(); // set Type=2
> if
> > TDX =====
> >
> > That is option 1.
> >
> > Thank you
> > Yao Jiewen
> >
> > > -----Original Message-----
> > > From: Xu, Min M <min.m.xu@intel.com>
> > > Sent: Thursday, July 29, 2021 7:54 PM
> > > To: Brijesh Singh <brijesh.singh@amd.com>; Yao, Jiewen
> > > <jiewen.yao@intel.com>; devel@edk2.groups.io
> > > Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Justen, Jordan L
> > > <jordan.l.justen@intel.com>; Erdem Aktas <erdemaktas@google.com>;
> > > James Bottomley <jejb@linux.ibm.com>; Tom Lendacky
> > > <thomas.lendacky@amd.com>
> > > Subject: RE: [edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in
> > > ResetVector
> > >
> > > On July 29, 2021 6:08 PM, Brijesh Singh wrote:
> > > > On 7/29/21 1:07 AM, Xu, Min M wrote:
> > > > > On July 29, 2021 12:29 PM, Brijesh Singh wrote:
> > > > >> On 7/28/21 9:44 PM, Xu, Min M wrote:
> > > > >>> Jiewen & Singh
> > > > >>>
> > > > >>> From the discussion I am thinking we have below rules to follow
> > > > >>> to the design the structure of TEE_WORK_AREA:
> > > > >>> 1. Design should be flexible but not too complicated 2. Reuse
> > > > >>> the current SEV_ES_WORK_AREA (PcdSevEsWorkAreaBase) as
> > > > TEE_WORK_AREA 3.
> > > > >>> TEE_WORK_AREA should be initialized to all-0 at the beginning of
> > > > >>> ResetVecotr 4. Reduce the changes to exiting code if possible
> > > > >>>
> > > > >>> So I try to make below conclusions below: (Please review) 1.
> > > > >>> SEV_ES_WORK_AREA is used as the TEE_WORK_AREA by both TDX and
> > > > SEV,
> > > > >>> maybe in the future it can be used by other CC technologies.
> > > > >>>
> > > > >>> 2. In MEMFD, add below initial value. So that TEE_WORK_AREA is
> > > > >>> guaranteed to be cleared in legacy guest. In TDX this memory
> > > > >>> region is initialized to be all-0 by host VMM. In SEV the memory
> > > > >>> region is
> > > > cleared as well.
> > > > >>> 0x00B000|0x001000
> > > > >>>
> > > > >>
> > > >
> > gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
> > > > ce
> > > > >> Guid.PcdSevEsWorkAreaSize
> > > > >>> DATA = {
> > > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > > > >>> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> > > > >>> }
> > > > >> Hmm, I thought the contents of the data pages are controlled by
> > > > >> the host
> > > > VMM.
> > > > >> If the backing pages are not zero filled then there is no
> > > > >> guarantee that memory will be zero. To verify it:
> > > > >>
> > > > >> 1. I applied your above change in OvmfPkgX86.fdt. I modified the
> > > > >> DATA values from 0x00 -> 0xCC
> > > > >>
> > > > >> 2. Modified the SecMain.c to dump the SevEsWorkArea on entry
> > > > >>
> > > > >> And dump does not contain the 0xcc.
> > > > >>
> > > > >> And to confirm further, I attached to the qemu with the GDB
> > > > >> before the booting the OVMF, and modified the SevEsWorkArea with
> > > > >> some garbage number and this time the dump printed garbage value
> > > > >> I put
> > > > through the debugger.
> > > > >>
> > > > >> In summary, the OVMF to zero the workarea memory on the entry and
> > > > we
> > > > >> cannot rely on the DATA={0x00, 0x00...} to zero the CCWorkArea.
> > > > > So in legacy guest, CCWorkArea is cleared to all-0 without the
> > > > DATA={0x00,0x00...}, right?
> > > >
> > > > Okay, maybe I was not able to communicate it correctly.
> > > >
> > > > The run I did is for the legacy guest. For the legacy guest, the
> > > > contents of the CCWorkArea may *not* be always zero even when you
> > > > use the DATA={0x00, 0x00...}.
> > > >
> > > > Currently, Qemu uses zero filled backing pages, so we will get a
> > > > zero filled CCWorkArea; but nothing says that a backing page *must* be
> > zero.
> > > > Another VMM may choose to do things differently. In summary, the
> > > > OVMF reset vector code must zero the CCWorkArea before calling SEV
> > > > or TDX probes.
> > > >
> > > Ah, I see.
> > > In current CheckSevFeatures, byte[SEV_ES_WORK_AREA] is cleared to0.
> > > Then its values is set based on the result of SEV probe.
> > >
> > > There is a bug here. CheckTdxFeatures does the similar work and it
> > > sets the WORK_AREA to 2. If CheckSevFeatures is called after
> > > CheckTdxFeatures, then WORK_AREA is cleared and it is set to 0 because
> > > it is not SEV. The value is override.
> > >
> > > I think there are 2 options:
> > > Option 1:
> > > Neither CheckTdxFeatures nor CheckSevFeatures should clear WORK_AREA.
> > > Instead
> > > It should be cleared to 0 outside and before these 2 calls. So in
> > > Main16 after TransitionFromReal16To32BitFlat WORK_AREA is cleared to
> > > 0. In Tdx guest this WORK_AREA is initialized to 0 by host VMM.
> > >
> > > Option 2:
> > > Another option is to figure out a mechanism that only one
> > > CheckXXXFeatures is called.
> > > Since there are 2 entry point in Main.asm: Main16 and Main32.
> > > In Main16 CheckSevFeatures is called after TransitionFromReal16To32BitFlat.
> > > (eax should
> > > be saved because it is used in SetCr3ForPageTables64) In Main32
> > > CheckTdxFeatures is called after ReloadFlat32.
> > >
> > > What's your opinion?
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector
2021-07-27 12:31 ` Brijesh Singh
2021-07-27 12:46 ` Yao, Jiewen
@ 2021-07-28 0:40 ` Min Xu
1 sibling, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-28 0:40 UTC (permalink / raw)
To: Brijesh Singh, devel@edk2.groups.io
Cc: Ard Biesheuvel, Justen, Jordan L, Erdem Aktas, James Bottomley,
Yao, Jiewen, Tom Lendacky
On July 27, 2021 8:31 PM, Brijesh Singh wrote:
> On 7/27/21 6:51 AM, Xu, Min M wrote:
> > On July 27, 2021 6:57 PM, Brijesh Singh wrote:
> >> Hi Min,
> >>
> >> This refactoring is already done by the SNP patch series.
> >>
> >>
> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fedk
> >>
> 2.groups.io%2Fg%2Fdevel%2Fmessage%2F77336%3Fp%3D%2C%2C%2C20%
> 2C0%2C0%2
> >>
> C0%3A%3ACreated%2C%2Cpost&data=04%7C01%7Cbrijesh.singh%40a
> md.com%
> >>
> 7C22b61f2ff5bb48348b0608d950f4d7c5%7C3dd8961fe4884e608e11a82d994
> e183d
> >> %7C0%7C0%7C637629834792320372%7CUnknown%7CTWFpbGZsb3d8ey
> JWIjoiMC4wLjA
> >>
> wMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&s
> data=
> >>
> tMGpR4a2uZTTR%2FsciTN0oeca2mZ32GfX3K78lA5BWas%3D&reserved
> =0
> >> erid%3A5969970,20,2,20,83891510
> >>
> >> It appears that you are also pulling in some of TDX logic inside the
> >> AMDSev.asm such as
> >>
> >> ;
> >> +PostJump64BitAndLandHereSev:
> >> +
> >> + ;
> >> + ; If it is Tdx guest, jump to exit point directly.
> >> + ; This is because following code may access the memory region which
> has
> >> + ; not been accepted. It is not allowed in Tdx guests.
> >> + ;
> >> + mov eax, dword[TDX_WORK_AREA]
> >> + cmp eax, 0x47584454 ; 'TDXG'
> >> + jz GoodCompare
> >>
> >> Why we are referring the TDX workarea inside the AmdSev.asm ?
> > See my explanation in the above comments. In Tdx guests memory region
> > cannot be accessed unless it is accepted by guest or initialized by
> > the host VMM. In PostJump64BitAndLandHereSev there is access to
> > dword[SEV_ES_WORK_AREA_RDRAND] which is not initialized by host
> VMM.
> > If this code will not be executed in Tdx guest, then the above check is not
> needed. I need your help to confirm it.
> >
> > There are similar Tdx check in my patch of AmdSev.asm. For example in
> > CheckSevFeatures byte[SEV_ES_WORK_AREA] is used to record the SEV-ES
> > flag. This memory region is not initialized by host VMM either. So in Tdx it
> will trigger error.
> >
> > Another solution is that the memory region used by SEV in ResetVector
> > are added Into Tdx metadata so that host VMM will initialize those
> > memory region when It creates the Td guest. What's your opinion?
>
> I am not full versed on TDX yet and sorry I am not able to follow you
> question completely to provide any advice. With SEV and SEV-ES, a guest can
> access the memory without going through the validation process, but with
> the SEV-SNP, the page need to be validated (aka accepted) before the access.
TDX has the same requirement.
> In SNP series, we ensure that the data pages used in the reset vector are pre-
> validated during the VM creation time -- this allows us to access the pages
> without going through accept process. If I follow you correctly on your
> metadata comment then it is similar to saying is pre-validate these range of
> pages used in the reset vector code (that include GHCB page, Page table
> pages etc), right ?
That's right. Tdx metadata describes the memory region which host VMM initialized
during the VM creation time.
In the current patch-set, below memory region are described in Tdx metadata.
- TdMailbox (PcdOvmfSecGhcbBackupBase)
- TdHob(PcdOvmfSecGhcbBase)
- TdExtraPage(PcdOvmfSecGhcbPageTableBase)
- OvmfPageTable (PcdOvmfSecPageTablesBase)
These memory regions are initialized by host VMM so they can be accessed in ResetVector in Tdx guests.
In the SEV codes, I find some memory is accessed as well. CheckSevFeatures is the example.
In CheckSevFeatures byte[SEV_ES_WORK_AREA] (PcdSevEsWorkAreaBase) is used to record/check
if it is SEV. So if this function is called in Tdx guest, then error is triggered.
What I am concerned is that, in the current pattern:
====================
OneTimeCall PreMainFunctionHookSev
OneTimeCall PreMainFunctionHookTdx
MainFunction:
XXXXXX
OneTimeCall PostMainFunctionHookSev
OneTimeCall PostMainFunctionHookTdx
====================
The TEE function need implement a TEE check function (such as IsSev, or IsTdx).
Tdx call CPUID(0x21) to determine if it is tdx guest in the very beginning of ResetVector. Then 'TDXG' is set
in TDX_WORK_AREA.
SEV does the similar work which call CheckSevFeatures to set SEV_ES_WORK_AREA to 1.
After that both TDX and SEV read the above WORK_AREA to check it is TDX or SEV or legacy guest.
In Tdx the access to SEV_ES_WORK_AREA will trigger error because SEV_ES_WORK_AREA is initialized by host VMM.
In SEV-SNP I am afraid the access to TDX_WORK_AREA will trigger error too.
I am wondering if TDX and SEV can use the same memory region (for example, TEE_WORK_AREA) as the work area?
So that this work area is guaranteed to be initialized in both TDX and SEV. Structure of the TEE_WORK_AREA may
look like this:
typedef struct {
UINT8 Flag[4]; 'TDXG' or 'SEVG' or all-0
UINT8 Others[];
} TEE_WORK_AREA;
>
> For SEV-SNP, see this patch
>
> https://edk2.groups.io/g/devel/message/77342?p=,,,20,0,0,0::Created,,post
> erid%3A5969970,20,2,20,83891520
>
> A VMM (qemu) looks for the range of page it need to prevalidate before the
> boot, the range is provided through the GUID (SevSnpBootBlock).
>
> >> I will take out my refactoring patch outside of the SNP series and
> >> submit it so that you can build on top of. This will simplify review process.
> >>
> > Thank you very much for the refactoring. I will refine my patch based on it.
> >> thanks
> >>
> >>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH V3 07/10] OvmfPkg: Add ReloadFlat32
[not found] <cover.1627364332.git.min.m.xu@intel.com>
` (5 preceding siblings ...)
2021-07-27 5:42 ` [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm " Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 08/10] OvmfPkg: Add Init32 Min Xu
` (2 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Load the GDT and set the CR0, then jump to Flat 32 protected mode. After
that CR4 is set.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm | 44 +++++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm
diff --git a/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm b/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm
new file mode 100644
index 000000000000..cfcea06fbd11
--- /dev/null
+++ b/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm
@@ -0,0 +1,44 @@
+;------------------------------------------------------------------------------
+; @file
+; Load the GDT and set the CR0, then jump to Flat 32 protected mode.
+;
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+%define SEC_DEFAULT_CR0 0x00000023
+%define SEC_DEFAULT_CR4 0x640
+
+BITS 32
+
+;
+; Modified: EAX, EBX, CR0, CR4, DS, ES, FS, GS, SS
+;
+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
+
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 08/10] OvmfPkg: Add Init32
[not found] <cover.1627364332.git.min.m.xu@intel.com>
` (6 preceding siblings ...)
2021-07-27 5:42 ` [PATCH V3 07/10] OvmfPkg: Add ReloadFlat32 Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 09/10] OvmfPkg: Create Main.asm in ResetVector Min Xu
2021-07-27 5:42 ` [PATCH V3 10/10] OvmfPkg: Update ResetVector to support Tdx Min Xu
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
Init32.asm is the entry point of doing the 32-bit protected mode
initialization. Here ReloadFlat32 is called. After that InitTdx is called
to do Tdx initialization if it is Tdx guests. In the future if SEV has
something to initialize, InitSev (for example) can be called in Init32.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/Ia32/Init32.asm | 32 +++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 OvmfPkg/ResetVector/Ia32/Init32.asm
diff --git a/OvmfPkg/ResetVector/Ia32/Init32.asm b/OvmfPkg/ResetVector/Ia32/Init32.asm
new file mode 100644
index 000000000000..fb78f6856f0a
--- /dev/null
+++ b/OvmfPkg/ResetVector/Ia32/Init32.asm
@@ -0,0 +1,32 @@
+;------------------------------------------------------------------------------
+; @file
+; 32-bit initialization code
+;
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+BITS 32
+
+;
+; Modified: EAX, EBX, ECX, EDX, EBP, EDI, ESP
+;
+Init32:
+ ;
+ ; Save EBX in EBP because EBX will be changed in ReloadFlat32
+ ;
+ mov ebp, ebx
+
+ ;
+ ; First load the GDT and jump to Flat32 mode
+ ;
+ OneTimeCall ReloadFlat32
+
+ ;
+ ; Initialization of Tdx
+ ;
+ OneTimeCall InitTdx
+
+ OneTimeCallRet Init32
+
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 09/10] OvmfPkg: Create Main.asm in ResetVector
[not found] <cover.1627364332.git.min.m.xu@intel.com>
` (7 preceding siblings ...)
2021-07-27 5:42 ` [PATCH V3 08/10] OvmfPkg: Add Init32 Min Xu
@ 2021-07-27 5:42 ` Min Xu
2021-07-27 5:42 ` [PATCH V3 10/10] OvmfPkg: Update ResetVector to support Tdx Min Xu
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
According to suggestion in https://edk2.groups.io/g/devel/message/78152
we drop UefiCpuPkg changes and focus on improving OvmfPkg. So Main.asm
is created in OvmfPkg/ResetVector which is simply copied from UefiCpuPkg.
In the next commit this Main.asm will be updated to add a new Entry
(Main32) to support Tdx.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/Main.asm | 105 +++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
create mode 100644 OvmfPkg/ResetVector/Main.asm
diff --git a/OvmfPkg/ResetVector/Main.asm b/OvmfPkg/ResetVector/Main.asm
new file mode 100644
index 000000000000..dbebfb9e5d29
--- /dev/null
+++ b/OvmfPkg/ResetVector/Main.asm
@@ -0,0 +1,105 @@
+;------------------------------------------------------------------------------
+; @file
+; Main routine of the pre-SEC code up through the jump into SEC
+;
+; Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+
+BITS 16
+
+;
+; Modified: EBX, ECX, EDX, EBP
+;
+; @param[in,out] RAX/EAX Initial value of the EAX register
+; (BIST: Built-in Self Test)
+; @param[in,out] DI 'BP': boot-strap processor, or
+; 'AP': application processor
+; @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
+;
+Main16:
+ OneTimeCall EarlyInit16
+
+ ;
+ ; Transition the processor from 16-bit real mode to 32-bit flat mode
+ ;
+ OneTimeCall TransitionFromReal16To32BitFlat
+
+BITS 32
+
+ ;
+ ; Search for the Boot Firmware Volume (BFV)
+ ;
+ OneTimeCall Flat32SearchForBfvBase
+
+ ;
+ ; EBP - Start of BFV
+ ;
+
+ ;
+ ; Search for the SEC entry point
+ ;
+ OneTimeCall Flat32SearchForSecEntryPoint
+
+ ;
+ ; ESI - SEC Core entry point
+ ; EBP - Start of BFV
+ ;
+
+%ifdef ARCH_IA32
+
+ ;
+ ; Restore initial EAX value into the EAX register
+ ;
+ mov eax, esp
+
+ ;
+ ; Jump to the 32-bit SEC entry point
+ ;
+ jmp esi
+
+%else
+
+ ;
+ ; Transition the processor from 32-bit flat mode to 64-bit flat mode
+ ;
+ OneTimeCall Transition32FlatTo64Flat
+
+BITS 64
+
+ ;
+ ; Some values were calculated in 32-bit mode. Make sure the upper
+ ; 32-bits of 64-bit registers are zero for these values.
+ ;
+ mov rax, 0x00000000ffffffff
+ and rsi, rax
+ and rbp, rax
+ and rsp, rax
+
+ ;
+ ; RSI - SEC Core entry point
+ ; RBP - Start of BFV
+ ;
+
+ ;
+ ; Restore initial EAX value into the RAX register
+ ;
+ mov rax, rsp
+
+ ;
+ ; Jump to the 64-bit SEC entry point
+ ;
+ jmp rsi
+
+%endif
+
+
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH V3 10/10] OvmfPkg: Update ResetVector to support Tdx
[not found] <cover.1627364332.git.min.m.xu@intel.com>
` (8 preceding siblings ...)
2021-07-27 5:42 ` [PATCH V3 09/10] OvmfPkg: Create Main.asm in ResetVector Min Xu
@ 2021-07-27 5:42 ` Min Xu
9 siblings, 0 replies; 36+ messages in thread
From: Min Xu @ 2021-07-27 5:42 UTC (permalink / raw)
To: devel
Cc: Min Xu, Ard Biesheuvel, Jordan Justen, Brijesh Singh, Erdem Aktas,
James Bottomley, Jiewen Yao, Tom Lendacky
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
In the previous ResetVector code there are many SEV stuff mixed in common
routines, such as SetCr3ForPageTables64. If Tdx stuff is added in this way
then it is very hard to review and maintain.
According to the suggestion (https://edk2.groups.io/g/devel/message/78151)
below changes are included in this commit:
1. AmdSev.asm is created to hold the SEV related codes
2. IntelTdx.asm is created to hold the TDX related codes
3. Transition32FlatTo64Flat in Flat32ToFlat64.asm is refactor to the
pattern described in above link.
4. SetCr3ForPageTables64 in PageTables64.asm is refactor to the pattern
described in above link.
Put all above together, the code flow is described below:
1) ResetVector/Ia16/ResetVectorVtf0.asm
In Tdx all CPUs "reset" to run on 32-bit protected mode with flat
descriptor (paging disabled). But in Non-Td guest the initial state of
CPUs is 16-bit real mode. To resolve this conflict, BITS 16/32 is used
in the very beginning of ResetVector. It will check the 32-bit protected
mode or 16-bit real mode, then jump to the corresponding entry point.
2) ReloadFlat32.asm load the GDT and set the CR0, then jump to Flat32.
3) Init32.asm is the entry point of doing the 32-bit protected mode
initialization. Here ReloadFlat32 is called. After that InitTdx is called
to do Tdx initialization if it is Tdx guests.
4) Transition32FlatTo64Flat does the transition from 32-bit to 64-bit.
The Pre/Post functions are called in turn, according to the suggestion in
above link.
After all above is successfully done, Tdx jump to SecEntry.
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 21 +
OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm | 110 +----
OvmfPkg/ResetVector/Ia32/PageTables64.asm | 478 +++----------------
OvmfPkg/ResetVector/Main.asm | 14 +
OvmfPkg/ResetVector/ResetVector.nasmb | 4 +
5 files changed, 131 insertions(+), 496 deletions(-)
diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
index ac86ce69ebe8..a390ed81d021 100644
--- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
+++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
@@ -155,10 +155,31 @@ resetVector:
;
; This is where the processor will begin execution
;
+; In IA32 we follow the standard reset vector flow. While in X64, Td guest
+; may be supported. Td guest requires the startup mode to be 32-bit
+; protected mode but the legacy VM startup mode is 16-bit real mode.
+; To make NASM generate such shared entry code that behaves correctly in
+; both 16-bit and 32-bit mode, more BITS directives are added.
+;
+%ifdef ARCH_IA32
+
nop
nop
jmp EarlyBspInitReal16
+%else
+
+ smsw ax
+ test al, 1
+ jz .Real
+BITS 32
+ jmp Main32
+BITS 16
+.Real:
+ jmp EarlyBspInitReal16
+
+%endif
+
ALIGN 16
fourGigabytes:
diff --git a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm
index c6d0d898bcd1..40cf6cea55bb 100644
--- a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm
+++ b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm
@@ -11,107 +11,39 @@
BITS 32
;
-; Modified: EAX, ECX, EDX
+; Transition from 32 bit flat protected mode into 64 bit flag protected mode
+;
+; To handle the situations of Tdx/SEV/Legacy guests, Pre/Post routines are
+; called. For example, SevPreSetCr3ForPageTables64 check the Sev features
+; and set the EAX value. TdxPostSetCr3PageTables64 set the CR0/CR4 and adjust
+; the CR3 if GPAW is 52.
+;
+; But in Tdx guest, memory region cannot be accessed before it is accepted
+; (except the case that the memory region is initialized by host VMM before
+; the guest is launched.) So in the beginning of Pre/Post routines it would
+; check if it is Tdx guest by checking the TDX_WORK_AREA.
+;
+; Modified: EAX, EBX, ECX, EDX, ESP
;
Transition32FlatTo64Flat:
+ OneTimeCall PreSetCr3ForPageTables64Sev
+
+SetPageTables64:
OneTimeCall SetCr3ForPageTables64
- mov eax, cr4
- bts eax, 5 ; enable PAE
- mov cr4, eax
+ OneTimeCall PostSetCr3PageTables64Tdx
- mov ecx, 0xc0000080
- rdmsr
- bts eax, 8 ; set LME
- wrmsr
-
- ;
- ; SEV-ES mitigation check support
- ;
- xor ebx, ebx
-
- cmp byte[SEV_ES_WORK_AREA], 0
- jz EnablePaging
-
- ;
- ; SEV-ES is active, perform a quick sanity check against the reported
- ; encryption bit position. This is to help mitigate against attacks where
- ; the hypervisor reports an incorrect encryption bit position.
- ;
- ; This is the first step in a two step process. Before paging is enabled
- ; writes to memory are encrypted. Using the RDRAND instruction (available
- ; on all SEV capable processors), write 64-bits of random data to the
- ; SEV_ES_WORK_AREA and maintain the random data in registers (register
- ; state is protected under SEV-ES). This will be used in the second step.
- ;
-RdRand1:
- rdrand ecx
- jnc RdRand1
- mov dword[SEV_ES_WORK_AREA_RDRAND], ecx
-RdRand2:
- rdrand edx
- jnc RdRand2
- mov dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
-
- ;
- ; Use EBX instead of the SEV_ES_WORK_AREA memory to determine whether to
- ; perform the second step.
- ;
- mov ebx, 1
-
-EnablePaging:
- mov eax, cr0
- bts eax, 31 ; set PG
- mov cr0, eax ; enable paging
+ OneTimeCall PostSetCr3PageTables64Sev
jmp LINEAR_CODE64_SEL:ADDR_OF(jumpTo64BitAndLandHere)
+
BITS 64
+
jumpTo64BitAndLandHere:
- ;
- ; Check if the second step of the SEV-ES mitigation is to be performed.
- ;
- test ebx, ebx
- jz InsnCompare
+ OneTimeCall PostJump64BitAndLandHereSev
- ;
- ; SEV-ES is active, perform the second step of the encryption bit postion
- ; mitigation check. The ECX and EDX register contain data from RDRAND that
- ; was stored to memory in encrypted form. If the encryption bit position is
- ; valid, the contents of ECX and EDX will match the memory location.
- ;
- cmp dword[SEV_ES_WORK_AREA_RDRAND], ecx
- jne SevEncBitHlt
- cmp dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
- jne SevEncBitHlt
-
- ;
- ; If SEV or SEV-ES is active, perform a quick sanity check against
- ; the reported encryption bit position. This is to help mitigate
- ; against attacks where the hypervisor reports an incorrect encryption
- ; bit position. If SEV is not active, this check will always succeed.
- ;
- ; The cmp instruction compares the first four bytes of the cmp instruction
- ; itself (which will be read decrypted if SEV or SEV-ES is active and the
- ; encryption bit position is valid) against the immediate within the
- ; instruction (an instruction fetch is always decrypted correctly by
- ; hardware) based on RIP relative addressing.
- ;
-InsnCompare:
- cmp dword[rel InsnCompare], 0xFFF63D81
- je GoodCompare
-
- ;
- ; The hypervisor provided an incorrect encryption bit position, do not
- ; proceed.
- ;
-SevEncBitHlt:
- cli
- hlt
- jmp SevEncBitHlt
-
-GoodCompare:
debugShowPostCode POSTCODE_64BIT_MODE
OneTimeCallRet Transition32FlatTo64Flat
diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
index 5fae8986d9da..ac365b114ee5 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -36,198 +36,97 @@ BITS 32
%define PAGE_PDP_ATTR (PAGE_ACCESSED + \
PAGE_READ_WRITE + \
PAGE_PRESENT)
-
-;
-; SEV-ES #VC exception handler support
-;
-; #VC handler local variable locations
-;
-%define VC_CPUID_RESULT_EAX 0
-%define VC_CPUID_RESULT_EBX 4
-%define VC_CPUID_RESULT_ECX 8
-%define VC_CPUID_RESULT_EDX 12
-%define VC_GHCB_MSR_EDX 16
-%define VC_GHCB_MSR_EAX 20
-%define VC_CPUID_REQUEST_REGISTER 24
-%define VC_CPUID_FUNCTION 28
-
-; #VC handler total local variable size
-;
-%define VC_VARIABLE_SIZE 32
-
-; #VC handler GHCB CPUID request/response protocol values
-;
-%define GHCB_CPUID_REQUEST 4
-%define GHCB_CPUID_RESPONSE 5
-%define GHCB_CPUID_REGISTER_SHIFT 30
-%define CPUID_INSN_LEN 2
-
-
-; Check if Secure Encrypted Virtualization (SEV) features are enabled.
-;
-; Register usage is tight in this routine, so multiple calls for the
-; same CPUID and MSR data are performed to keep things simple.
-;
-; Modified: EAX, EBX, ECX, EDX, ESP
;
-; If SEV is enabled then EAX will be at least 32.
-; If SEV is disabled then EAX will be zero.
+; Extra page tables built by Tdx guests
;
-CheckSevFeatures:
- ; Set the first byte of the workarea to zero to communicate to the SEC
- ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
- ; instruction will trigger a #VC exception where the first byte of the
- ; workarea will be set to one or, if CPUID is not being intercepted,
- ; the MSR check below will set the first byte of the workarea to one.
- mov byte[SEV_ES_WORK_AREA], 0
+TdxBuildExtraPageTables:
+ xor eax, eax
+ mov ecx, 0x400
+tdClearTdxPageTablesMemoryLoop:
+ mov dword [ecx * 4 + TDX_PT_ADDR (0) - 4], eax
+ loop tdClearTdxPageTablesMemoryLoop
+ ;xor edx, edx
;
- ; Set up exception handlers to check for SEV-ES
- ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
- ; stack usage)
- ; Establish exception handlers
- ;
- mov esp, SEV_ES_VC_TOP_OF_STACK
- mov eax, ADDR_OF(Idtr)
- lidt [cs:eax]
-
- ; Check if we have a valid (0x8000_001F) CPUID leaf
- ; CPUID raises a #VC exception if running as an SEV-ES guest
- mov eax, 0x80000000
- cpuid
-
- ; This check should fail on Intel or Non SEV AMD CPUs. In future if
- ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
- ; same bit definition.
- cmp eax, 0x8000001f
- jl NoSev
-
- ; Check for SEV memory encryption feature:
- ; CPUID Fn8000_001F[EAX] - Bit 1
- ; CPUID raises a #VC exception if running as an SEV-ES guest
- mov eax, 0x8000001f
- cpuid
- bt eax, 1
- jnc NoSev
-
- ; Check if SEV memory encryption is enabled
- ; MSR_0xC0010131 - Bit 0 (SEV enabled)
- mov ecx, 0xc0010131
- rdmsr
- bt eax, 0
- jnc NoSev
-
- ; Check for SEV-ES memory encryption feature:
- ; CPUID Fn8000_001F[EAX] - Bit 3
- ; CPUID raises a #VC exception if running as an SEV-ES guest
- mov eax, 0x8000001f
- cpuid
- bt eax, 3
- jnc GetSevEncBit
-
- ; Check if SEV-ES is enabled
- ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
- mov ecx, 0xc0010131
- rdmsr
- bt eax, 1
- jnc GetSevEncBit
-
- ; Set the first byte of the workarea to one to communicate to the SEC
- ; phase that SEV-ES is enabled.
- mov byte[SEV_ES_WORK_AREA], 1
-
-GetSevEncBit:
- ; Get pte bit position to enable memory encryption
- ; CPUID Fn8000_001F[EBX] - Bits 5:0
+ ; Top level Page Directory Pointers (1 * 256TB entry)
;
- and ebx, 0x3f
- mov eax, ebx
-
- ; The encryption bit position is always above 31
- sub ebx, 32
- jns SevSaveMask
+ mov dword[TDX_PT_ADDR (0)], PT_ADDR (0) + PAGE_PDP_ATTR
+ ;mov dword[TDX_PT_ADDR (4)], edx
- ; Encryption bit was reported as 31 or below, enter a HLT loop
-SevEncBitLowHlt:
- cli
- hlt
- jmp SevEncBitLowHlt
+ mov byte[TDX_WORK_AREA_PGTBL_READY], 1
-SevSaveMask:
- xor edx, edx
- bts edx, ebx
+ OneTimeCallRet TdxBuildExtraPageTables
- mov dword[SEV_ES_WORK_AREA_ENC_MASK], 0
- mov dword[SEV_ES_WORK_AREA_ENC_MASK + 4], edx
- jmp SevExit
-
-NoSev:
+;
+; Ghcb page tables built by SEV
+;
+SevBuildGhcbPageTables:
;
- ; Perform an SEV-ES sanity check by seeing if a #VC exception occurred.
+ ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
+ ; This requires the 2MB page for this range be broken down into 512 4KB
+ ; pages. All will be marked encrypted, except for the GHCB.
;
- cmp byte[SEV_ES_WORK_AREA], 0
- jz NoSevPass
+ mov ecx, (GHCB_BASE >> 21)
+ mov eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
+ mov [ecx * 8 + PT_ADDR (0x2000)], eax
;
- ; A #VC was received, yet CPUID indicates no SEV-ES support, something
- ; isn't right.
+ ; Page Table Entries (512 * 4KB entries => 2MB)
;
-NoSevEsVcHlt:
- cli
- hlt
- jmp NoSevEsVcHlt
-
-NoSevPass:
- xor eax, eax
+ mov ecx, 512
+pageTableEntries4kLoop:
+ mov eax, ecx
+ dec eax
+ shl eax, 12
+ add eax, GHCB_BASE & 0xFFE0_0000
+ add eax, PAGE_4K_PDE_ATTR
+ mov [ecx * 8 + GHCB_PT_ADDR - 8], eax
+ mov [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
+ loop pageTableEntries4kLoop
-SevExit:
;
- ; Clear exception handlers and stack
+ ; Clear the encryption bit from the GHCB entry
;
- push eax
- mov eax, ADDR_OF(IdtrClear)
- lidt [cs:eax]
- pop eax
- mov esp, 0
-
- OneTimeCallRet CheckSevFeatures
-
-; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
-; is enabled.
-;
-; Modified: EAX
-;
-; If SEV-ES is enabled then EAX will be non-zero.
-; If SEV-ES is disabled then EAX will be zero.
-;
-IsSevEsEnabled:
- xor eax, eax
-
- ; During CheckSevFeatures, the SEV_ES_WORK_AREA was set to 1 if
- ; SEV-ES is enabled.
- cmp byte[SEV_ES_WORK_AREA], 1
- jne SevEsDisabled
+ mov ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
+ mov [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
- mov eax, 1
+ mov ecx, GHCB_SIZE / 4
+ xor eax, eax
+clearGhcbMemoryLoop:
+ mov dword[ecx * 4 + GHCB_BASE - 4], eax
+ loop clearGhcbMemoryLoop
-SevEsDisabled:
- OneTimeCallRet IsSevEsEnabled
+ OneTimeCallRet SevBuildGhcbPageTables
;
; Modified: EAX, EBX, ECX, EDX
;
SetCr3ForPageTables64:
- OneTimeCall CheckSevFeatures
xor edx, edx
+
+CheckTdx:
+ cmp dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+ jnz CheckSev
+
+ ;
+ ; In Td guest, BSP builds the page table and set the flag of
+ ; TDX_WORK_AREA_PGTBL_READY. APs check this flag and then set
+ ; cr3 directly.
+ ;
+ cmp byte[TDX_WORK_AREA_PGTBL_READY], 1
+ jz SetCr3
+ jmp BuildPageTables
+
+CheckSev:
test eax, eax
- jz SevNotActive
+ jz BuildPageTables
; If SEV is enabled, C-bit is always above 31
sub eax, 32
bts edx, eax
-SevNotActive:
+BuildPageTables:
;
; For OVMF, build some initial page tables at
@@ -277,44 +176,22 @@ pageTableEntriesLoop:
mov [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
loop pageTableEntriesLoop
- OneTimeCall IsSevEsEnabled
- test eax, eax
- jz SetCr3
-
;
- ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
- ; This requires the 2MB page for this range be broken down into 512 4KB
- ; pages. All will be marked encrypted, except for the GHCB.
+ ; If it is Td guest, TdxExtraPageTable should be initialized as well
;
- mov ecx, (GHCB_BASE >> 21)
- mov eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
- mov [ecx * 8 + PT_ADDR (0x2000)], eax
+ cmp dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+ jnz IsSevEs
- ;
- ; Page Table Entries (512 * 4KB entries => 2MB)
- ;
- mov ecx, 512
-pageTableEntries4kLoop:
- mov eax, ecx
- dec eax
- shl eax, 12
- add eax, GHCB_BASE & 0xFFE0_0000
- add eax, PAGE_4K_PDE_ATTR
- mov [ecx * 8 + GHCB_PT_ADDR - 8], eax
- mov [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
- loop pageTableEntries4kLoop
+ OneTimeCall TdxBuildExtraPageTables
+ jmp SetCr3
- ;
- ; Clear the encryption bit from the GHCB entry
- ;
- mov ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
- mov [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
+IsSevEs:
+ ; During CheckSevFeatures, the SEV_ES_WORK_AREA was set to 1 if
+ ; SEV-ES is enabled.
+ cmp byte[SEV_ES_WORK_AREA], 1
+ jne SetCr3
- mov ecx, GHCB_SIZE / 4
- xor eax, eax
-clearGhcbMemoryLoop:
- mov dword[ecx * 4 + GHCB_BASE - 4], eax
- loop clearGhcbMemoryLoop
+ OneTimeCall SevBuildGhcbPageTables
SetCr3:
;
@@ -325,217 +202,4 @@ SetCr3:
OneTimeCallRet SetCr3ForPageTables64
-;
-; Start of #VC exception handling routines
-;
-SevEsIdtNotCpuid:
- ;
- ; Use VMGEXIT to request termination.
- ; 1 - #VC was not for CPUID
- ;
- mov eax, 1
- jmp SevEsIdtTerminate
-
-SevEsIdtNoCpuidResponse:
- ;
- ; Use VMGEXIT to request termination.
- ; 2 - GHCB_CPUID_RESPONSE not received
- ;
- mov eax, 2
-
-SevEsIdtTerminate:
- ;
- ; Use VMGEXIT to request termination. At this point the reason code is
- ; located in EAX, so shift it left 16 bits to the proper location.
- ;
- ; EAX[11:0] => 0x100 - request termination
- ; EAX[15:12] => 0x1 - OVMF
- ; EAX[23:16] => 0xXX - REASON CODE
- ;
- shl eax, 16
- or eax, 0x1100
- xor edx, edx
- mov ecx, 0xc0010130
- wrmsr
- ;
- ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
- ; mode, so work around this by temporarily switching to 64-bit mode.
- ;
-BITS 64
- rep vmmcall
-BITS 32
-
- ;
- ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
- ;
-SevEsIdtHlt:
- hlt
- jmp SevEsIdtHlt
- iret
-
- ;
- ; Total stack usage for the #VC handler is 44 bytes:
- ; - 12 bytes for the exception IRET (after popping error code)
- ; - 32 bytes for the local variables.
- ;
-SevEsIdtVmmComm:
- ;
- ; If we're here, then we are an SEV-ES guest and this
- ; was triggered by a CPUID instruction
- ;
- ; Set the first byte of the workarea to one to communicate that
- ; a #VC was taken.
- mov byte[SEV_ES_WORK_AREA], 1
-
- pop ecx ; Error code
- cmp ecx, 0x72 ; Be sure it was CPUID
- jne SevEsIdtNotCpuid
-
- ; Set up local variable room on the stack
- ; CPUID function : + 28
- ; CPUID request register : + 24
- ; GHCB MSR (EAX) : + 20
- ; GHCB MSR (EDX) : + 16
- ; CPUID result (EDX) : + 12
- ; CPUID result (ECX) : + 8
- ; CPUID result (EBX) : + 4
- ; CPUID result (EAX) : + 0
- sub esp, VC_VARIABLE_SIZE
-
- ; Save the CPUID function being requested
- mov [esp + VC_CPUID_FUNCTION], eax
-
- ; The GHCB CPUID protocol uses the following mapping to request
- ; a specific register:
- ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
- ;
- ; Set EAX as the first register to request. This will also be used as a
- ; loop variable to request all register values (EAX to EDX).
- xor eax, eax
- mov [esp + VC_CPUID_REQUEST_REGISTER], eax
-
- ; Save current GHCB MSR value
- mov ecx, 0xc0010130
- rdmsr
- mov [esp + VC_GHCB_MSR_EAX], eax
- mov [esp + VC_GHCB_MSR_EDX], edx
-
-NextReg:
- ;
- ; Setup GHCB MSR
- ; GHCB_MSR[63:32] = CPUID function
- ; GHCB_MSR[31:30] = CPUID register
- ; GHCB_MSR[11:0] = CPUID request protocol
- ;
- mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
- cmp eax, 4
- jge VmmDone
-
- shl eax, GHCB_CPUID_REGISTER_SHIFT
- or eax, GHCB_CPUID_REQUEST
- mov edx, [esp + VC_CPUID_FUNCTION]
- mov ecx, 0xc0010130
- wrmsr
-
- ;
- ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
- ; mode, so work around this by temporarily switching to 64-bit mode.
- ;
-BITS 64
- rep vmmcall
-BITS 32
-
- ;
- ; Read GHCB MSR
- ; GHCB_MSR[63:32] = CPUID register value
- ; GHCB_MSR[31:30] = CPUID register
- ; GHCB_MSR[11:0] = CPUID response protocol
- ;
- mov ecx, 0xc0010130
- rdmsr
- mov ecx, eax
- and ecx, 0xfff
- cmp ecx, GHCB_CPUID_RESPONSE
- jne SevEsIdtNoCpuidResponse
-
- ; Save returned value
- shr eax, GHCB_CPUID_REGISTER_SHIFT
- mov [esp + eax * 4], edx
-
- ; Next register
- inc word [esp + VC_CPUID_REQUEST_REGISTER]
-
- jmp NextReg
-
-VmmDone:
- ;
- ; At this point we have all CPUID register values. Restore the GHCB MSR,
- ; set the return register values and return.
- ;
- mov eax, [esp + VC_GHCB_MSR_EAX]
- mov edx, [esp + VC_GHCB_MSR_EDX]
- mov ecx, 0xc0010130
- wrmsr
-
- mov eax, [esp + VC_CPUID_RESULT_EAX]
- mov ebx, [esp + VC_CPUID_RESULT_EBX]
- mov ecx, [esp + VC_CPUID_RESULT_ECX]
- mov edx, [esp + VC_CPUID_RESULT_EDX]
-
- add esp, VC_VARIABLE_SIZE
-
- ; Update the EIP value to skip over the now handled CPUID instruction
- ; (the CPUID instruction has a length of 2)
- add word [esp], CPUID_INSN_LEN
- iret
-
-ALIGN 2
-
-Idtr:
- dw IDT_END - IDT_BASE - 1 ; Limit
- dd ADDR_OF(IDT_BASE) ; Base
-
-IdtrClear:
- dw 0 ; Limit
- dd 0 ; Base
-
-ALIGN 16
-
-;
-; The Interrupt Descriptor Table (IDT)
-; This will be used to determine if SEV-ES is enabled. Upon execution
-; of the CPUID instruction, a VMM Communication Exception will occur.
-; This will tell us if SEV-ES is enabled. We can use the current value
-; of the GHCB MSR to determine the SEV attributes.
-;
-IDT_BASE:
-;
-; Vectors 0 - 28 (No handlers)
-;
-%rep 29
- dw 0 ; Offset low bits 15..0
- dw 0x10 ; Selector
- db 0 ; Reserved
- db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
- dw 0 ; Offset high bits 31..16
-%endrep
-;
-; Vector 29 (VMM Communication Exception)
-;
- dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
- dw 0x10 ; Selector
- db 0 ; Reserved
- db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
- dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
-;
-; Vectors 30 - 31 (No handlers)
-;
-%rep 2
- dw 0 ; Offset low bits 15..0
- dw 0x10 ; Selector
- db 0 ; Reserved
- db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
- dw 0 ; Offset high bits 31..16
-%endrep
-IDT_END:
diff --git a/OvmfPkg/ResetVector/Main.asm b/OvmfPkg/ResetVector/Main.asm
index dbebfb9e5d29..0418e0294920 100644
--- a/OvmfPkg/ResetVector/Main.asm
+++ b/OvmfPkg/ResetVector/Main.asm
@@ -36,6 +36,20 @@ Main16:
BITS 32
+%ifdef ARCH_X64
+
+ jmp SearchBfv
+
+;
+; Entry point of Main32
+;
+Main32:
+
+ OneTimeCall Init32
+
+%endif
+
+SearchBfv:
;
; Search for the Boot Firmware Volume (BFV)
;
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 0ac6d7a6fd33..ade8ce6f08a5 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -132,6 +132,10 @@
%define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
%include "X64/TdxMetadata.asm"
+ %include "Ia32/Init32.asm"
+ %include "Ia32/IntelTdx.asm"
+ %include "Ia32/AmdSev.asm"
+ %include "Ia32/ReloadFlat32.asm"
%include "Ia32/Flat32ToFlat64.asm"
%include "Ia32/PageTables64.asm"
--
2.29.2.windows.2
^ permalink raw reply related [flat|nested] 36+ messages in thread