public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
@ 2021-03-24 15:31 brijesh.singh
  2021-03-24 15:31 ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Brijesh Singh
                   ` (20 more replies)
  0 siblings, 21 replies; 68+ messages in thread
From: brijesh.singh @ 2021-03-24 15:31 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

SEV-SNP builds upon existing SEV and SEV-ES functionality while adding
new hardware-based memory protections. SEV-SNP adds strong memory integrity
protection to help prevent malicious hypervisor-based attacks like data
replay, memory re-mapping and more in order to create an isolated memory
encryption environment.
 
This series provides the basic building blocks to support booting the SEV-SNP
VMs, it does not cover all the security enhancement introduced by the SEV-SNP
such as interrupt protection.

Many of the integrity guarantees of SEV-SNP are enforced through a new
structure called the Reverse Map Table (RMP). Adding a new page to SEV-SNP
VM requires a 2-step process. First, the hypervisor assigns a page to the
guest using the new RMPUPDATE instruction. This transitions the page to
guest-invalid. Second, the guest validates the page using the new PVALIDATE
instruction. The SEV-SNP VMs can use the new "Page State Change Request NAE"
defined in the GHCB specification to ask hypervisor to add or remove page
from the RMP table.
 
Each page assigned to the SEV-SNP VM can either be validated or unvalidated,
as indicated by the Validated flag in the page's RMP entry. There are two
approaches that can be taken for the page validation: Pre-validation and
Lazy Validation.
  
Under pre-validation, the pages are validated prior to first use. And under
lazy validation, pages are validated when first accessed. An access to a
unvalidated page results in a #VC exception, at which time the exception
handler may validate the page. Lazy validation requires careful tracking of
the validated pages to avoid validating the same GPA more than once. The
recently introduced "Unaccepted" memory type can be used to communicate the
unvalidated memory ranges to the Guest OS.

At this time we only support the pre-validation. OVMF detects all the available
system RAM in the PEI phase. When SEV-SNP is enabled, the memory is validated
before it is made available to the EDK2 core.

This series does not implements the following SEV-SNP features yet:

* CPUID filtering
* AP bring up using the new SEV-SNP NAE
* Lazy validation
* Interrupt security

The series is based on commit:
e542e05d4f UefiCpuPkg/SmmCpuFeaturesLib: Abstract PcdCpuMaxLogicalProcessorNumber

Additional resources
---------------------
SEV-SNP whitepaper
https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf
 
APM 2: https://www.amd.com/system/files/TechDocs/24593.pdf (section 15.36)

The complete source is available at
https://github.com/AMDESE/ovmf/tree/sev-snp-rfc-1

GHCB spec v2:
  The draft specification is posted on AMD-SEV-SNP mailing list:
   https://lists.suse.com/mailman/private/amd-sev-snp/

  Copy of the spec is also available at 
  https://github.com/AMDESE/AMDSEV/blob/sev-snp-devel/docs/56421-Guest_Hypervisor_Communication_Block_Standardization.pdf

GHCB spec v1:
SEV-SNP firmware specification:
 https://developer.amd.com/sev/
  
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>

Brijesh Singh (19):
  OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  OvmfPkg: validate the data pages used in the SEC phase
  MdePkg: Expand the SEV MSR to include the SNP definition
  OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled()
  MdePkg: Define the GHCB GPA structure
  UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is
    enabled
  OvmfPkg: Add a library to support registering GHCB GPA
  OvmfPkg: register GHCB gpa for the SEV-SNP guest
  MdePkg: Add AsmPvalidate() support
  OvmfPkg: Define the Page State Change VMGEXIT structures
  OvmfPkg/ResetVector: Invalidate the GHCB page
  OvmfPkg/MemEncryptSevLib: Add support to validate system RAM
  OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase
  OvmfPkg/MemEncryptSevLib: Add support to validate RAM in PEI phase
  OvmfPkg/PlatformPei: Validate the system RAM when SNP is active
  OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI
    phase
  OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase
  OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc
    attribute
  OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region

 MdePkg/Include/Library/BaseLib.h              |  37 +++
 MdePkg/Include/Register/Amd/Fam17Msr.h        |  31 ++-
 MdePkg/Include/Register/Amd/Ghcb.h            |  39 ++-
 MdePkg/Library/BaseLib/BaseLib.inf            |   1 +
 MdePkg/Library/BaseLib/X64/Pvalidate.nasm     |  43 +++
 OvmfPkg/Include/Library/GhcbRegisterLib.h     |  27 ++
 OvmfPkg/Include/Library/MemEncryptSevLib.h    |  30 +++
 .../DxeMemEncryptSevLib.inf                   |   7 +
 .../DxeMemEncryptSevLibInternal.c             |  27 ++
 .../Ia32/SnpPageStateChange.c                 |  17 ++
 .../PeiMemEncryptSevLib.inf                   |   9 +
 .../PeiMemEncryptSevLibInternal.c             |  47 ++++
 .../SecMemEncryptSevLib.inf                   |   4 +
 .../SecMemEncryptSevLibInternal.c             |  39 +++
 .../BaseMemEncryptSevLib/SnpPageStateChange.h |  37 +++
 .../X64/PeiDxeSnpSetPageState.c               |  63 +++++
 .../X64/PeiDxeVirtualMemory.c                 | 151 ++++++++++-
 .../X64/PeiSnpSystemRamValidate.c             | 129 +++++++++
 .../X64/SecSnpSystemRamValidate.c             |  23 ++
 .../X64/SnpPageStateChangeInternal.c          | 254 ++++++++++++++++++
 .../X64/SnpPageStateTrack.c                   | 119 ++++++++
 .../X64/SnpPageStateTrack.h                   |  36 +++
 .../X64/SnpSetPageState.h                     |  27 ++
 .../BaseMemEncryptSevLib/X64/VirtualMemory.h  |  19 ++
 .../Library/GhcbRegisterLib/GhcbRegisterLib.c |  97 +++++++
 .../GhcbRegisterLib/GhcbRegisterLib.inf       |  33 +++
 OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf  |   4 +
 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |   7 +
 OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c |  45 ++++
 OvmfPkg/OvmfPkg.dec                           |  12 +
 OvmfPkg/OvmfPkgX64.dsc                        |   1 +
 OvmfPkg/OvmfPkgX64.fdf                        |  33 ++-
 OvmfPkg/PlatformPei/AmdSev.c                  |  52 ++++
 OvmfPkg/PlatformPei/PlatformPei.inf           |   2 +
 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm  |  24 ++
 OvmfPkg/ResetVector/Ia32/PageTables64.asm     | 106 ++++++++
 OvmfPkg/ResetVector/ResetVector.inf           |   5 +
 OvmfPkg/ResetVector/ResetVector.nasmb         |   4 +
 OvmfPkg/Sec/SecMain.c                         | 102 +++++++
 OvmfPkg/Sec/SecMain.inf                       |   2 +
 UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   1 +
 UefiCpuPkg/Library/MpInitLib/MpEqu.inc        |   1 +
 UefiCpuPkg/Library/MpInitLib/MpLib.c          |   2 +
 UefiCpuPkg/Library/MpInitLib/MpLib.h          |   2 +
 UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   1 +
 UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm |  51 ++++
 UefiCpuPkg/UefiCpuPkg.dec                     |   6 +
 47 files changed, 1790 insertions(+), 19 deletions(-)
 create mode 100644 MdePkg/Library/BaseLib/X64/Pvalidate.nasm
 create mode 100644 OvmfPkg/Include/Library/GhcbRegisterLib.h
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
 create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c
 create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf

-- 
2.17.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
@ 2021-03-24 15:31 ` Brijesh Singh
  2021-04-06  8:11   ` Min Xu
  2021-04-12 14:52   ` Brijesh Singh
  2021-03-24 15:31 ` [RFC PATCH 02/19] OvmfPkg: validate the data pages used in the SEC phase Brijesh Singh
                   ` (19 subsequent siblings)
  20 siblings, 2 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:31 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

During the SEV-SNP guest launch sequence, two special pages need to
be inserted, the secrets page and cpuid page. The secrets page,
contain the VM platform communication keys. The guest BIOS and OS
can use this key to communicate with the SEV firmware to get the
attestation report. The Cpuid page, contain the CPUIDs entries
filtered through the AMD-SEV firmware.

The VMM will locate the secrets and cpuid page addresses through a
fixed GUID and pass them to SEV firmware to populate further.
For more information about the page content, see the SEV-SNP spec.

To simplify the pre-validation range calculation in the next patch,
the CPUID and Secrets pages are moved to the start of the
MEMFD_BASE_ADDRESS.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/OvmfPkg.dec                          |  8 +++++++
 OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
 OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
 OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
 5 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 4348bb45c6..062926772d 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -317,6 +317,14 @@
   gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
   gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
 
+  ## The base address of the CPUID page used by SEV-SNP
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
+
+  ## The base address of the Secrets page used by SEV-SNP
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
+
 [PcdsDynamic, PcdsDynamicEx]
   gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index d519f85328..ea214600be 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -67,27 +67,33 @@ ErasePolarity = 1
 BlockSize     = 0x10000
 NumBlocks     = 0xD0
 
-0x000000|0x006000
+0x000000|0x001000
+gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
+
+0x001000|0x001000
+gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
+
+0x002000|0x006000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
 
-0x006000|0x001000
+0x008000|0x001000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
 
-0x007000|0x001000
+0x009000|0x001000
 gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
 
-0x008000|0x001000
+0x00A000|0x001000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
 
-0x009000|0x002000
+0x00B000|0x002000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
 
-0x00B000|0x001000
-gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
-
-0x00C000|0x001000
+0x00D000|0x001000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
 
+0x00F000|0x001000
+gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
+
 0x010000|0x010000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
 
diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
index 9c0b5853a4..5456f02924 100644
--- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
+++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
@@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart + 15) % 16)) DB 0
 ;
 guidedStructureStart:
 
+;
+; SEV-SNP boot support
+;
+; sevSnpBlock:
+;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must be
+;   reserved by the BIOS at a RAM area defined by SEV_SNP_SECRETS_PAGE
+;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
+;   SEV-SNP boot block.
+;
+; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
+;
+sevSnpBootBlockStart:
+    DD      SEV_SNP_SECRETS_PAGE
+    DD      SEV_SNP_CPUID_PAGE
+    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
+    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
+    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
+sevSnpBootBlockEnd:
+
 ;
 ; SEV Secret block
 ;
diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index dc38f68919..d890bb6b29 100644
--- a/OvmfPkg/ResetVector/ResetVector.inf
+++ b/OvmfPkg/ResetVector/ResetVector.inf
@@ -37,6 +37,10 @@
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 5fbacaed5f..2c194958f4 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -75,6 +75,8 @@
   %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
   %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 8)
   %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16)
+  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
+  %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
   %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
 %include "Ia32/Flat32ToFlat64.asm"
 %include "Ia32/PageTables64.asm"
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 02/19] OvmfPkg: validate the data pages used in the SEC phase
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
  2021-03-24 15:31 ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Brijesh Singh
@ 2021-03-24 15:31 ` Brijesh Singh
  2021-03-24 15:31 ` [RFC PATCH 03/19] MdePkg: Expand the SEV MSR to include the SNP definition Brijesh Singh
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:31 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

An SEV-SNP guest requires that private memory (aka pages mapped as
encrypted) must be validated before being accessed.

The validation process consist of the following sequence:

1) Set the memory encryption attribute in the page table (aka C-bit).
   Note: If the processor is in non-PAE mode, then all the memory accesses
   are considered private.
2) Add the memory range as private in the RMP table. This can be performed
   using the Page State Change VMGEXIT defined in the GHCB specification.
3) Use the PVALIDATE instruction to set the Validated Bit in the RMP table.

During the guest creation time, the VMM encrypts the OVMF_CODE.fd using
the SEV-SNP firmware provided LAUNCH_UPDATE_DATA command. In addition to
encrypting the content, the command also validates the memory region.
This allows us to execute the code without going through the validation
sequence.

During execution, the reset vector need to access some data pages
(such as page tables, SevESWorkarea, Sec stack). The data pages are
accessed as private memory. The data pages are not part of the
OVMF_CODE.fd, so they were not validated during the guest creation.

There are two approaches we can take to validate the data pages before
the access:

a) Enhance the OVMF reset vector code to validate the pages as described
   above (go through step 2 - 3).
OR
b) Validate the pages during the guest creation time. The SEV firmware
   provides a command which can be used by the VMM to validate the pages
   without affecting the measurement of the launch.

Approach #b seems much simpler; it does not require any changes to the
OVMF reset vector code.

Extend the SnpBootBlock to provide the range that can be pre-validated
using the SEV-SNP firmware command during the guest creation time.

At the end of the guest creation the pre-validated range looks like this:

0x800000   0x801000   (CPUID Page     [Validated + Measured])
0x801000   0x802000   (Secrets Page   [Validated + Measured])
0x802000   0x820000   (Data Pages for the SEC phase  [Validated + unmeasured])

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/OvmfPkg.dec                          | 4 ++++
 OvmfPkg/OvmfPkgX64.fdf                       | 9 ++++++++-
 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 5 +++++
 OvmfPkg/ResetVector/ResetVector.inf          | 1 +
 OvmfPkg/ResetVector/ResetVector.nasmb        | 2 ++
 5 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 062926772d..6fb70e2c10 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -325,6 +325,10 @@
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
 
+  ## The range of memory pre-validated through the SEV-SNP launch sequence
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpLaunchValidatedStart|0|UINT32|0x52
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpLaunchValidatedEnd|0|UINT32|0x53
+
 [PcdsDynamic, PcdsDynamicEx]
   gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index ea214600be..16383453f1 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -105,7 +105,14 @@ FV = PEIFV
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvSize
 FV = DXEFV
 
-################################################################################
+##############################################################################
+#
+# The range of the pages validated through the SEV-SNP launch sequence.
+#
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpLaunchValidatedStart = $(MEMFD_BASE_ADDRESS)
+SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpLaunchValidatedEnd = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase
+
+###############################################################################
 
 [FV.SECFV]
 FvNameGuid         = 763BED0D-DE9F-48F5-81F1-3E90E1B1A015
diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
index 5456f02924..9be887c4fc 100644
--- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
+++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
@@ -56,11 +56,16 @@ guidedStructureStart:
 ;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
 ;   SEV-SNP boot block.
 ;
+;   In addition to Secret and CPUID page, the SEV-SNP boot block also contain
+;   the range of memory that must be pre-validated by the VMM before the execution.
+;
 ; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
 ;
 sevSnpBootBlockStart:
     DD      SEV_SNP_SECRETS_PAGE
     DD      SEV_SNP_CPUID_PAGE
+    DD      SEV_SNP_LAUNCH_VALIDATED_START
+    DD      SEV_SNP_LAUNCH_VALIDATED_END
     DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
     DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
     DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index d890bb6b29..49a527c0b1 100644
--- a/OvmfPkg/ResetVector/ResetVector.inf
+++ b/OvmfPkg/ResetVector/ResetVector.inf
@@ -47,6 +47,7 @@
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase
 
 [FixedPcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 2c194958f4..6d399c4739 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -77,6 +77,8 @@
   %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16)
   %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
   %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
+  %define SEV_SNP_LAUNCH_VALIDATED_START FixedPcdGet32 (PcdOvmfSecPageTablesBase)
+  %define SEV_SNP_LAUNCH_VALIDATED_END FixedPcdGet32 (PcdOvmfPeiMemFvBase)
   %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
 %include "Ia32/Flat32ToFlat64.asm"
 %include "Ia32/PageTables64.asm"
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 03/19] MdePkg: Expand the SEV MSR to include the SNP definition
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
  2021-03-24 15:31 ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Brijesh Singh
  2021-03-24 15:31 ` [RFC PATCH 02/19] OvmfPkg: validate the data pages used in the SEC phase Brijesh Singh
@ 2021-03-24 15:31 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 04/19] OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled() Brijesh Singh
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:31 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

Define the SEV-SNP MSR bits.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 MdePkg/Include/Register/Amd/Fam17Msr.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/MdePkg/Include/Register/Amd/Fam17Msr.h b/MdePkg/Include/Register/Amd/Fam17Msr.h
index e4db09c518..4d33bef220 100644
--- a/MdePkg/Include/Register/Amd/Fam17Msr.h
+++ b/MdePkg/Include/Register/Amd/Fam17Msr.h
@@ -87,7 +87,12 @@ typedef union {
     ///
     UINT32  SevEsBit:1;
 
-    UINT32  Reserved:30;
+    ///
+    /// [Bit 2] Secure Nested Paging (SevSnp) is enabled
+    ///
+    UINT32  SevSnpBit:1;
+
+    UINT32  Reserved:29;
   } Bits;
   ///
   /// All bit fields as a 32-bit value
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 04/19] OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled()
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (2 preceding siblings ...)
  2021-03-24 15:31 ` [RFC PATCH 03/19] MdePkg: Expand the SEV MSR to include the SNP definition Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 05/19] MdePkg: Define the GHCB GPA structure Brijesh Singh
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

Create a function can be used to determine if VM is running as an
SEV-SNP guest.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Include/Library/MemEncryptSevLib.h                         | 12 +++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLibInternal.c | 27 ++++++++++++++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c | 27 ++++++++++++++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c | 19 ++++++++++++++
 4 files changed, 85 insertions(+)

diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h b/OvmfPkg/Include/Library/MemEncryptSevLib.h
index 99f15a7d12..03d9eda392 100644
--- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
+++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
@@ -66,6 +66,18 @@ typedef enum {
   MemEncryptSevAddressRangeError,
 } MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE;
 
+/**
+  Returns a boolean to indicate whether SEV-SNP is enabled
+
+  @retval TRUE           SEV-SNP is enabled
+  @retval FALSE          SEV-SNP is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevSnpIsEnabled (
+  VOID
+  );
+
 /**
   Returns a boolean to indicate whether SEV-ES is enabled.
 
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLibInternal.c
index 2816f859a0..0571297238 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLibInternal.c
@@ -19,6 +19,7 @@
 
 STATIC BOOLEAN mSevStatus = FALSE;
 STATIC BOOLEAN mSevEsStatus = FALSE;
+STATIC BOOLEAN mSevSnpStatus = FALSE;
 STATIC BOOLEAN mSevStatusChecked = FALSE;
 
 STATIC UINT64  mSevEncryptionMask = 0;
@@ -82,11 +83,37 @@ InternalMemEncryptSevStatus (
     if (Msr.Bits.SevEsBit) {
       mSevEsStatus = TRUE;
     }
+
+    //
+    // Check MSR_0xC0010131 Bit 2 (Sev-Snp Enabled)
+    //
+    if (Msr.Bits.SevSnpBit) {
+      mSevSnpStatus = TRUE;
+    }
   }
 
   mSevStatusChecked = TRUE;
 }
 
+/**
+  Returns a boolean to indicate whether SEV-SNP is enabled.
+
+  @retval TRUE           SEV-SNP is enabled
+  @retval FALSE          SEV-SNP is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevSnpIsEnabled (
+  VOID
+  )
+{
+  if (!mSevStatusChecked) {
+    InternalMemEncryptSevStatus ();
+  }
+
+  return mSevSnpStatus;
+}
+
 /**
   Returns a boolean to indicate whether SEV-ES is enabled.
 
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c
index e2fd109d12..b561f211f5 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c
@@ -19,6 +19,7 @@
 
 STATIC BOOLEAN mSevStatus = FALSE;
 STATIC BOOLEAN mSevEsStatus = FALSE;
+STATIC BOOLEAN mSevSnpStatus = FALSE;
 STATIC BOOLEAN mSevStatusChecked = FALSE;
 
 STATIC UINT64  mSevEncryptionMask = 0;
@@ -82,11 +83,37 @@ InternalMemEncryptSevStatus (
     if (Msr.Bits.SevEsBit) {
       mSevEsStatus = TRUE;
     }
+
+    //
+    // Check MSR_0xC0010131 Bit 2 (Sev-Snp Enabled)
+    //
+    if (Msr.Bits.SevSnpBit) {
+      mSevSnpStatus = TRUE;
+    }
   }
 
   mSevStatusChecked = TRUE;
 }
 
+/**
+  Returns a boolean to indicate whether SEV-SNP is enabled.
+
+  @retval TRUE           SEV-SNP is enabled
+  @retval FALSE          SEV-SNP is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevSnpIsEnabled (
+  VOID
+  )
+{
+  if (!mSevStatusChecked) {
+    InternalMemEncryptSevStatus ();
+  }
+
+  return mSevSnpStatus;
+}
+
 /**
   Returns a boolean to indicate whether SEV-ES is enabled.
 
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
index 56d8f3f318..69852779e2 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
@@ -62,6 +62,25 @@ InternalMemEncryptSevStatus (
   return ReadSevMsr ? AsmReadMsr32 (MSR_SEV_STATUS) : 0;
 }
 
+/**
+  Returns a boolean to indicate whether SEV-SNP is enabled.
+
+  @retval TRUE           SEV-SNP is enabled
+  @retval FALSE          SEV-SNP is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevSnpIsEnabled (
+  VOID
+  )
+{
+  MSR_SEV_STATUS_REGISTER           Msr;
+
+  Msr.Uint32 = InternalMemEncryptSevStatus ();
+
+  return Msr.Bits.SevSnpBit ? TRUE : FALSE;
+}
+
 /**
   Returns a boolean to indicate whether SEV-ES is enabled.
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 05/19] MdePkg: Define the GHCB GPA structure
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (3 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 04/19] OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled() Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 06/19] UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is enabled Brijesh Singh
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

An SEV-SNP guest is required to perform the GHCB GPA registration. See
the GHCB specification for further details.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 MdePkg/Include/Register/Amd/Fam17Msr.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MdePkg/Include/Register/Amd/Fam17Msr.h b/MdePkg/Include/Register/Amd/Fam17Msr.h
index 4d33bef220..c074e871a7 100644
--- a/MdePkg/Include/Register/Amd/Fam17Msr.h
+++ b/MdePkg/Include/Register/Amd/Fam17Msr.h
@@ -48,6 +48,11 @@ typedef union {
     UINT32  Reserved2:32;
   } GhcbTerminate;
 
+  struct {
+    UINT64  Function:12;
+    UINT64  GuestFrameNumber:52;
+  } GhcbGpaRegister;
+
   VOID    *Ghcb;
 
   UINT64  GhcbPhysicalAddress;
@@ -58,6 +63,8 @@ typedef union {
 #define GHCB_INFO_CPUID_REQUEST            4
 #define GHCB_INFO_CPUID_RESPONSE           5
 #define GHCB_INFO_TERMINATE_REQUEST        256
+#define GHCB_INFO_GHCB_GPA_REGISTER_REQUEST   18
+#define GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE  19
 
 #define GHCB_TERMINATE_GHCB                0
 #define GHCB_TERMINATE_GHCB_GENERAL        0
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 06/19] UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is enabled
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (4 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 05/19] MdePkg: Define the GHCB GPA structure Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 07/19] OvmfPkg: Add a library to support registering GHCB GPA Brijesh Singh
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

An SEV-SNP guest requires that the physical address of the GHCB must
be registered with the hypervisor before using it. See the GHCB specification
for the futher detail.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |  1 +
 UefiCpuPkg/Library/MpInitLib/MpEqu.inc        |  1 +
 UefiCpuPkg/Library/MpInitLib/MpLib.c          |  2 +
 UefiCpuPkg/Library/MpInitLib/MpLib.h          |  2 +
 UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |  1 +
 UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 51 ++++++++++++++++++++
 UefiCpuPkg/UefiCpuPkg.dec                     |  6 +++
 7 files changed, 64 insertions(+)

diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
index 860a9750e2..9a366ca5b1 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
@@ -75,3 +75,4 @@
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ## SOMETIMES_CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdSevSnpIsEnabled                         ## CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/MpEqu.inc b/UefiCpuPkg/Library/MpInitLib/MpEqu.inc
index 2e9368a374..01668638f2 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpEqu.inc
+++ b/UefiCpuPkg/Library/MpInitLib/MpEqu.inc
@@ -92,6 +92,7 @@ struc MP_CPU_EXCHANGE_INFO
   .ModeHighSegment:              CTYPE_UINT16 1
   .Enable5LevelPaging:           CTYPE_BOOLEAN 1
   .SevEsIsEnabled:               CTYPE_BOOLEAN 1
+  .SevSnpIsEnabled               CTYPE_BOOLEAN 1
   .GhcbBase:                     CTYPE_UINTN 1
 endstruc
 
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index 5040053dad..da6fbbc1cc 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -1040,6 +1040,7 @@ FillExchangeInfoData (
   DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging));
 
   ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;
+  ExchangeInfo->SevSnpIsEnabled  = CpuMpData->SevSnpIsEnabled;
   ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;
 
   //
@@ -2016,6 +2017,7 @@ MpInitLibInitialize (
   CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);
   InitializeSpinLock(&CpuMpData->MpLock);
   CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
+  CpuMpData->SevSnpIsEnabled = PcdGetBool (PcdSevSnpIsEnabled);
   CpuMpData->SevEsAPBuffer  = (UINTN) -1;
   CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
 
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h
index 0bd60388b1..7d3ce61d63 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
@@ -216,6 +216,7 @@ typedef struct {
   //
   BOOLEAN               Enable5LevelPaging;
   BOOLEAN               SevEsIsEnabled;
+  BOOLEAN               SevSnpIsEnabled;
   UINTN                 GhcbBase;
 } MP_CPU_EXCHANGE_INFO;
 
@@ -285,6 +286,7 @@ struct _CPU_MP_DATA {
   BOOLEAN                        WakeUpByInitSipiSipi;
 
   BOOLEAN                        SevEsIsEnabled;
+  BOOLEAN                        SevSnpIsEnabled;
   UINTN                          SevEsAPBuffer;
   UINTN                          SevEsAPResetStackStart;
   CPU_MP_DATA                    *NewCpuMpData;
diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
index 49b0ffe8be..4477dd1b9f 100644
--- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
@@ -64,6 +64,7 @@
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ## SOMETIMES_CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdSevSnpIsEnabled                     ## CONSUMES
 
 [Ppis]
   gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
index 50df802d1f..19939c093d 100644
--- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
+++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
@@ -194,9 +194,60 @@ LongModeStart:
     mov        rdx, rax
     shr        rdx, 32
     mov        rcx, 0xc0010130
+
+    ;
+    ; Register GHCB GPA when SEV-SNP is enabled
+    ;
+    lea        edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpIsEnabled)]
+    cmp        byte [edi], 1        ; SevSnpIsEnabled
+    jne        SetGhcbAddress
+
+    ; Save the rdi and rsi to used for later comparison
+    push       rdi
+    push       rsi
+    mov        edi, eax
+    mov        esi, edx
+    or         eax, 18              ; Ghcb registration request
+    wrmsr
+    rep vmmcall
+    rdmsr
+    mov        r12, rax
+    and        r12, 0fffh
+    cmp        r12, 19              ; Ghcb registration response
+    jne        GhcbGpaRegisterFailure
+
+    ; Verify that GPA is not changed
+    and        eax, 0fffff000h
+    cmp        edi, eax
+    jne        GhcbGpaRegisterFailure
+    cmp        esi, edx
+    jne        GhcbGpaRegisterFailure
+    pop        rsi
+    pop        rdi
+
+    ;
+    ; Program GHCB
+    ;
+SetGhcbAddress:
     wrmsr
     jmp        CProcedureInvoke
 
+    ;
+    ; Request the guest termination
+    ;
+GhcbGpaRegisterFailure:
+    xor        edx, edx
+    mov        eax, 256             ; GHCB terminate
+    wrmsr
+    rep vmmcall
+
+    ; We should not return from the above terminate request, but if we do
+    ; then enter into the hlt loop.
+DoHltLoop:
+    cli
+    hlt
+    jmp        DoHltLoop
+
 GetApicId:
     lea        edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]
     cmp        byte [edi], 1        ; SevEsIsEnabled
diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index a639ce5412..51bd7a6fe2 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -393,5 +393,11 @@
   # @Prompt SEV-ES Status
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|FALSE|BOOLEAN|0x60000016
 
+  ## This dynamic PCD indicates whether SEV-SNP is enabled
+  #   TRUE  - SEV-SNP is enabled
+  #   FALSE - SEV-SNP is not enabled
+  # @Prompt SEV-SNP Status
+  gUefiCpuPkgTokenSpaceGuid.PcdSevSnpIsEnabled|FALSE|BOOLEAN|0x60000017
+
 [UserExtensions.TianoCore."ExtraFiles"]
   UefiCpuPkgExtra.uni
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 07/19] OvmfPkg: Add a library to support registering GHCB GPA
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (5 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 06/19] UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is enabled Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 08/19] OvmfPkg: register GHCB gpa for the SEV-SNP guest Brijesh Singh
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

An SEV-SNP guest us required to perform GHCB GPA registration before
using a GHCB. See the GHCB spec section 2.5.2 for more details.

Add a library that can be called to perform the GHCB GPA registration.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Include/Library/GhcbRegisterLib.h           | 27 ++++++
 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c   | 97 ++++++++++++++++++++
 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf | 33 +++++++
 OvmfPkg/OvmfPkgX64.dsc                              |  1 +
 4 files changed, 158 insertions(+)

diff --git a/OvmfPkg/Include/Library/GhcbRegisterLib.h b/OvmfPkg/Include/Library/GhcbRegisterLib.h
new file mode 100644
index 0000000000..7d98b6eb36
--- /dev/null
+++ b/OvmfPkg/Include/Library/GhcbRegisterLib.h
@@ -0,0 +1,27 @@
+/** @file
+
+  Declarations of utility functions used for GHCB GPA registration.
+
+  Copyright (C) 2021, AMD Inc, All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _GHCB_REGISTER_LIB_H_
+#define _GHCB_REGISTER_LIB_H_
+
+/**
+
+  This function can be used to register the GHCB GPA.
+
+  @param[in]  Address           The physical address to registered.
+
+**/
+VOID
+EFIAPI
+GhcbRegister (
+  IN  EFI_PHYSICAL_ADDRESS   Address
+  );
+
+#endif // _GHCB_REGISTER_LIB_H_
diff --git a/OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c b/OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c
new file mode 100644
index 0000000000..7fe0aad75a
--- /dev/null
+++ b/OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c
@@ -0,0 +1,97 @@
+/** @file
+  GHCBRegister Support Library.
+
+  Copyright (C) 2021, Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/VmgExitLib.h>
+#include <Library/GhcbRegisterLib.h>
+#include <Register/Amd/Msr.h>
+
+/**
+  Handle an SEV-SNP/GHCB protocol check failure.
+
+  Notify the hypervisor using the VMGEXIT instruction that the SEV-SNP guest
+  wishes to be terminated.
+
+  @param[in] ReasonCode  Reason code to provide to the hypervisor for the
+                         termination request.
+
+**/
+STATIC
+VOID
+SevEsProtocolFailure (
+  IN UINT8  ReasonCode
+  )
+{
+  MSR_SEV_ES_GHCB_REGISTER  Msr;
+
+  //
+  // Use the GHCB MSR Protocol to request termination by the hypervisor
+  //
+  Msr.GhcbPhysicalAddress = 0;
+  Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
+  Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
+  Msr.GhcbTerminate.ReasonCode = ReasonCode;
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+  AsmVmgExit ();
+
+  ASSERT (FALSE);
+  CpuDeadLoop ();
+}
+
+/**
+
+  This function can be used to register the GHCB GPA.
+
+  @param[in]  Address           The physical address to be registered.
+
+**/
+VOID
+EFIAPI
+GhcbRegister (
+  IN  EFI_PHYSICAL_ADDRESS   Address
+  )
+{
+  MSR_SEV_ES_GHCB_REGISTER  Msr;
+  MSR_SEV_ES_GHCB_REGISTER  CurrentMsr;
+  EFI_PHYSICAL_ADDRESS      GuestFrameNumber;
+
+  GuestFrameNumber = Address >> EFI_PAGE_SHIFT;
+
+  //
+  // Save the current MSR Value
+  //
+  CurrentMsr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+
+  //
+  // Use the GHCB MSR Protocol to request to register the GPA.
+  //
+  Msr.GhcbPhysicalAddress = 0;
+  Msr.GhcbGpaRegister.Function = GHCB_INFO_GHCB_GPA_REGISTER_REQUEST;
+  Msr.GhcbGpaRegister.GuestFrameNumber = GuestFrameNumber;
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+  AsmVmgExit ();
+
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+
+  //
+  // If hypervisor responded with a different GPA than requested then fail.
+  //
+  if ((Msr.GhcbGpaRegister.Function != GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE) ||
+      (Msr.GhcbGpaRegister.GuestFrameNumber != GuestFrameNumber)) {
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
+  }
+
+  //
+  // Restore the MSR
+  //
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, CurrentMsr.GhcbPhysicalAddress);
+}
diff --git a/OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf b/OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf
new file mode 100644
index 0000000000..8cc39ef715
--- /dev/null
+++ b/OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf
@@ -0,0 +1,33 @@
+## @file
+#  GHCBRegisterLib Support Library.
+#
+#  Copyright (C) 2021, Advanced Micro Devices, Inc. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = GhcbRegisterLib
+  FILE_GUID                      = 0e913c15-12cd-430b-8714-ffe85672a77b
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = GhcbRegisterLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources.common]
+  GhcbRegisterLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index d4d601b444..aa81bf9c66 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -242,6 +242,7 @@
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
   VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
+  GhcbRegisterLib|OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 08/19] OvmfPkg: register GHCB gpa for the SEV-SNP guest
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (6 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 07/19] OvmfPkg: Add a library to support registering GHCB GPA Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support Brijesh Singh
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

The SEV-SNP guest requires that GHCB GPA must be registered before using.
The GHCB GPA can be registred using the GhcbGPARegister().

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/PlatformPei/AmdSev.c        | 11 +++
 OvmfPkg/PlatformPei/PlatformPei.inf |  2 +
 OvmfPkg/Sec/SecMain.c               | 76 ++++++++++++++++++++
 3 files changed, 89 insertions(+)

diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index dddffdebda..95c5ad235f 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -14,6 +14,7 @@
 #include <Library/DebugLib.h>
 #include <Library/HobLib.h>
 #include <Library/MemEncryptSevLib.h>
+#include <Library/GhcbRegisterLib.h>
 #include <Library/MemoryAllocationLib.h>
 #include <Library/PcdLib.h>
 #include <PiPei.h>
@@ -110,6 +111,16 @@ AmdSevEsInitialize (
     "SEV-ES is enabled, %lu GHCB backup pages allocated starting at 0x%p\n",
     (UINT64)GhcbBackupPageCount, GhcbBackupBase));
 
+  if (MemEncryptSevSnpIsEnabled ()) {
+    //
+    // SEV-SNP guest requires that GHCB GPA must be registered before using it.
+    //
+    GhcbRegister (GhcbBasePa);
+
+    PcdStatus = PcdSetBoolS (PcdSevSnpIsEnabled, TRUE);
+    ASSERT_RETURN_ERROR (PcdStatus);
+  }
+
   AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
 
   //
diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index 6ef77ba7bb..cb6f5ac091 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -52,6 +52,7 @@
   BaseLib
   CacheMaintenanceLib
   DebugLib
+  GhcbRegisterLib
   HobLib
   IoLib
   PciLib
@@ -110,6 +111,7 @@
   gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled
+  gUefiCpuPkgTokenSpaceGuid.PcdSevSnpIsEnabled
 
 [FixedPcd]
   gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index 9db67e17b2..df6722b546 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -750,6 +750,76 @@ SevEsProtocolFailure (
   CpuDeadLoop ();
 }
 
+/**
+  Determine if SEV-SNP is active. There is a MemEncryptIsSnpEnabled() in MemEncryptSevLib
+  but we can not use it because the SEV-SNP check need to be done before the
+  ProcessLibraryConstructorList() is called.
+
+  @retval TRUE   SEV-SNP is enabled
+  @retval FALSE  SEV-SNP is not enabled
+
+**/
+STATIC
+BOOLEAN
+SevSnpIsEnabled (
+  VOID
+  )
+{
+   MSR_SEV_STATUS_REGISTER           Msr;
+
+   Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
+
+   return Msr.Bits.SevSnpBit ? TRUE : FALSE;
+}
+
+/**
+ The GHCB GPA registeration need to be done before the ProcessLibraryConstructorList()
+ is called. So use a local implementation instead of including the GhcbRegisterLib.
+
+ */
+STATIC
+VOID
+SevSnpGhcbRegister (
+  UINTN   Address
+  )
+{
+  MSR_SEV_ES_GHCB_REGISTER  Msr;
+  MSR_SEV_ES_GHCB_REGISTER  CurrentMsr;
+  EFI_PHYSICAL_ADDRESS      GuestFrameNumber;
+
+  GuestFrameNumber = Address >> EFI_PAGE_SHIFT;
+
+  //
+  // Save the current MSR Value
+  //
+  CurrentMsr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+
+  //
+  // Use the GHCB MSR Protocol to request to register the GPA.
+  //
+  Msr.GhcbPhysicalAddress = 0;
+  Msr.GhcbGpaRegister.Function = GHCB_INFO_GHCB_GPA_REGISTER_REQUEST;
+  Msr.GhcbGpaRegister.GuestFrameNumber = GuestFrameNumber;
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+  AsmVmgExit ();
+
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+
+  //
+  // If hypervisor responded with a different GPA than requested then fail.
+  //
+  if ((Msr.GhcbGpaRegister.Function != GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE) ||
+      (Msr.GhcbGpaRegister.GuestFrameNumber != GuestFrameNumber)) {
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
+  }
+
+  //
+  // Restore the MSR
+  //
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, CurrentMsr.GhcbPhysicalAddress);
+}
+
 /**
   Validate the SEV-ES/GHCB protocol level.
 
@@ -791,6 +861,12 @@ SevEsProtocolCheck (
     SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
   }
 
+  if (SevSnpIsEnabled ()) {
+    //
+    // SEV-SNP guest requires that GHCB GPA must be registered before using it.
+    //
+    SevSnpGhcbRegister (FixedPcdGet32 (PcdOvmfSecGhcbBase));
+  }
   //
   // SEV-ES protocol checking succeeded, set the initial GHCB address
   //
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (7 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 08/19] OvmfPkg: register GHCB gpa for the SEV-SNP guest Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-25  2:49   ` 回复: [edk2-devel] " gaoliming
  2021-03-24 15:32 ` [RFC PATCH 10/19] OvmfPkg: Define the Page State Change VMGEXIT structures Brijesh Singh
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

The PVALIDATE instruction validates or rescinds validation of a guest
page RMP entry. Upon completion, a return code is stored in EAX, rFLAGS
bits OF, ZF, AF, PF and SF are set based on this return code. If the
instruction completed succesfully, the rFLAGS bit CF indicates if the
contents of the RMP entry were changed or not.

For more information about the instruction see AMD APM volume 3.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 MdePkg/Include/Library/BaseLib.h          | 37 +++++++++++++++++
 MdePkg/Library/BaseLib/BaseLib.inf        |  1 +
 MdePkg/Library/BaseLib/X64/Pvalidate.nasm | 43 ++++++++++++++++++++
 3 files changed, 81 insertions(+)

diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index 1171a0ffb5..fee27e9a1b 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -7495,5 +7495,42 @@ PatchInstructionX86 (
   IN  UINTN                    ValueSize
   );
 
+/**
+ Execute a PVALIDATE instruction to validate or rescnids validation of a guest
+ page's RMP entry.
+
+ Upon completion, in addition to the return value the instruction also updates
+ the eFlags. A caller must check both the return code as well as eFlags to
+ determine if the RMP entry has been updated.
+
+ The function is available on x64.
+
+ @param[in]    Address        The guest virtual address to validate.
+ @param[in]    PageSize       The page size to use.
+ @param[i]     Validate       Validate or rescinds.
+ @param[out]   Eflags         The value of Eflags after PVALIDATE completion.
+
+ @retval       PvalidateRetValue  The return value from the PVALIDATE instruction.
+**/
+typedef enum {
+  PVALIDATE_PAGE_SIZE_4K = 0,
+  PVALIDATE_PAGE_SIZE_2M,
+} PvalidatePageSize;
+
+typedef enum {
+  PVALIDATE_RET_SUCCESS = 0,
+  PVALIDATE_RET_FAIL_INPUT = 1,
+  PVALIDATE_RET_FAIL_SIZEMISMATCH = 6,
+} PvalidateRetValue;
+
+PvalidateRetValue
+EFIAPI
+AsmPvalidate (
+  IN   PvalidatePageSize       PageSize,
+  IN   BOOLEAN                 Validate,
+  IN   UINTN                   Address,
+  OUT  IA32_EFLAGS32           *Eflags
+  );
+
 #endif // defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
 #endif // !defined (__BASE_LIB__)
diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index 3b85c56c3c..01aa5cc7a4 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -319,6 +319,7 @@
   X64/RdRand.nasm
   X64/XGetBv.nasm
   X64/VmgExit.nasm
+  X64/Pvalidate.nasm
   ChkStkGcc.c  | GCC
 
 [Sources.EBC]
diff --git a/MdePkg/Library/BaseLib/X64/Pvalidate.nasm b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
new file mode 100644
index 0000000000..f2aba114ac
--- /dev/null
+++ b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
@@ -0,0 +1,43 @@
+;-----------------------------------------------------------------------------
+;
+; Copyright (c) 2020-2021, AMD. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;   Pvalidate.Asm
+;
+; Abstract:
+;
+;   AsmPvalidate function
+;
+; Notes:
+;
+;-----------------------------------------------------------------------------
+
+    SECTION .text
+
+;-----------------------------------------------------------------------------
+;  PvalidateRetValue
+;  EFIAPI
+;  AsmPvalidate (
+;    IN   UINT32  RmpPageSize
+;    IN   UINT32  Validate,
+;    IN   UINTN   Address,
+;    OUT  UINTN  *Eflags,
+;    )
+;-----------------------------------------------------------------------------
+global ASM_PFX(AsmPvalidate)
+ASM_PFX(AsmPvalidate):
+  mov     rax, r8
+
+  ; PVALIDATE instruction opcode
+  DB      0xF2, 0x0F, 0x01, 0xFF
+
+  ; Read the Eflags
+  pushfq
+  pop     r8
+  mov     [r9], r8
+
+  ; The PVALIDATE instruction returns the status in rax register.
+  ret
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 10/19] OvmfPkg: Define the Page State Change VMGEXIT structures
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (8 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 11/19] OvmfPkg/ResetVector: Invalidate the GHCB page Brijesh Singh
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

The Page State Change NAE exit will be used by the SEV-SNP guest to
request a page state change using the GHCB protocol. See the GHCB
spec section 4.1.6 and 2.3.1 for more detail on the structure
definitions.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 MdePkg/Include/Register/Amd/Fam17Msr.h | 17 +++++++++
 MdePkg/Include/Register/Amd/Ghcb.h     | 39 +++++++++++++++++---
 2 files changed, 50 insertions(+), 6 deletions(-)

diff --git a/MdePkg/Include/Register/Amd/Fam17Msr.h b/MdePkg/Include/Register/Amd/Fam17Msr.h
index c074e871a7..084e7fb63a 100644
--- a/MdePkg/Include/Register/Amd/Fam17Msr.h
+++ b/MdePkg/Include/Register/Amd/Fam17Msr.h
@@ -53,6 +53,19 @@ typedef union {
     UINT64  GuestFrameNumber:52;
   } GhcbGpaRegister;
 
+  struct {
+    UINT64 Function:12;
+    UINT64 GuestFrameNumber:40;
+    UINT64 Operation:4;
+    UINT64 Reserved:8;
+  } SnpPageStateChangeRequest;
+
+  struct {
+    UINT32 Function:12;
+    UINT32 Reserved:20;
+    UINT32 ErrorCode;
+  } SnpPageStateChangeResponse;
+
   VOID    *Ghcb;
 
   UINT64  GhcbPhysicalAddress;
@@ -65,6 +78,10 @@ typedef union {
 #define GHCB_INFO_TERMINATE_REQUEST        256
 #define GHCB_INFO_GHCB_GPA_REGISTER_REQUEST   18
 #define GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE  19
+#define GHCB_INFO_SNP_PAGE_STATE_CHANGE_REQUEST   20
+#define GHCB_INFO_SNP_PAGE_STATE_CHANGE_RESPONSE  21
+#define   SNP_PAGE_STATE_PRIVATE          1
+#define   SNP_PAGE_STATE_SHARED           2
 
 #define GHCB_TERMINATE_GHCB                0
 #define GHCB_TERMINATE_GHCB_GENERAL        0
diff --git a/MdePkg/Include/Register/Amd/Ghcb.h b/MdePkg/Include/Register/Amd/Ghcb.h
index ccdb662af7..f3c719e70e 100644
--- a/MdePkg/Include/Register/Amd/Ghcb.h
+++ b/MdePkg/Include/Register/Amd/Ghcb.h
@@ -49,12 +49,13 @@
 //
 // VMG Special Exit Codes
 //
-#define SVM_EXIT_MMIO_READ      0x80000001ULL
-#define SVM_EXIT_MMIO_WRITE     0x80000002ULL
-#define SVM_EXIT_NMI_COMPLETE   0x80000003ULL
-#define SVM_EXIT_AP_RESET_HOLD  0x80000004ULL
-#define SVM_EXIT_AP_JUMP_TABLE  0x80000005ULL
-#define SVM_EXIT_UNSUPPORTED    0x8000FFFFULL
+#define SVM_EXIT_MMIO_READ                0x80000001ULL
+#define SVM_EXIT_MMIO_WRITE               0x80000002ULL
+#define SVM_EXIT_NMI_COMPLETE             0x80000003ULL
+#define SVM_EXIT_AP_RESET_HOLD            0x80000004ULL
+#define SVM_EXIT_AP_JUMP_TABLE            0x80000005ULL
+#define SVM_EXIT_SNP_PAGE_STATE_CHANGE    0x80000010ULL
+#define SVM_EXIT_UNSUPPORTED              0x8000FFFFULL
 
 //
 // IOIO Exit Information
@@ -154,4 +155,30 @@ typedef union {
 #define GHCB_EVENT_INJECTION_TYPE_EXCEPTION  3
 #define GHCB_EVENT_INJECTION_TYPE_SOFT_INT   4
 
+#define SNP_PAGE_STATE_MAX_NPAGES           4095
+#define SNP_PAGE_STATE_MAX_ENTRY            253
+#define SNP_PAGE_STATE_PRIVATE              1
+#define SNP_PAGE_STATE_SHARED               2
+#define SNP_PAGE_STATE_PSMASH               3
+#define SNP_PAGE_STATE_UNSMASH              4
+
+typedef PACKED struct {
+  UINT64  CurrentPage:12;
+  UINT64  GuestFrameNumber:40;
+  UINT64  Op:4;
+  UINT64  PageSize:1;
+  UINT64  Rsvd: 7;
+} SNP_PAGE_STATE_ENTRY;
+
+typedef PACKED struct {
+  UINT16 CurrentEntry;
+  UINT16 EndEntry;
+  UINT32 Rsvd;
+} SNP_PAGE_STATE_HEADER;
+
+typedef struct {
+  SNP_PAGE_STATE_HEADER  Header;
+  SNP_PAGE_STATE_ENTRY   Entry[SNP_PAGE_STATE_MAX_ENTRY];
+} SNP_PAGE_STATE_CHANGE_INFO;
+
 #endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 11/19] OvmfPkg/ResetVector: Invalidate the GHCB page
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (9 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 10/19] OvmfPkg: Define the Page State Change VMGEXIT structures Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to validate system RAM Brijesh Singh
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

When SEV-SNP is active, the GHCB page is mapped un-encrypted in the
initial page table built by the reset vector code. Just clearing the
encryption attribute from the page table is not enough. The page also
needs to be added as shared in the RMP table.

The GHCB page was part of the pre-validated memory range specified
through the SnpBootBlock GUID. To maintain the security guarantees,
we must invalidate the GHCB page before clearing the encryption
attribute from the page table, and add the page shared in the RMP
table.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/ResetVector/Ia32/PageTables64.asm | 106 ++++++++++++++++++++
 1 file changed, 106 insertions(+)

diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
index 5fae8986d9..63b864bcf2 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -62,6 +62,99 @@ BITS    32
 %define GHCB_CPUID_REGISTER_SHIFT  30
 %define CPUID_INSN_LEN              2
 
+; GHCB Page Invalidate request and response protocol values
+;
+%define GHCB_PAGE_STATE_CHANGE_REQUEST    20
+%define GHCB_PAGE_STATE_CHANGE_RESPONSE   21
+%define   GHCB_PAGE_STATE_SHARED          2
+
+; GHCB request to terminate protocol values
+%define GHCB_GENERAL_TERMINATE_REQUEST    255
+
+;
+; If SEV-SNP is enabled, invalidate the GHCB page
+InvalidateGHCBPage:
+    ; Read the SEV_STATUS MSR to check whether SEV-SNP is enabled.
+    ;  MSR_0xC0010131 - Bit 2 (SEV-SNP enabled)
+    mov     ecx, 0xc0010131
+    rdmsr
+    bt      eax, 2
+    jnc     InvalidateGHCBPageDone
+
+    ; Use PVALIDATE instruction to invalidate the page
+    mov     eax, GHCB_BASE
+    mov     ecx, 0
+    mov     edx, 0
+    DB      0xF2, 0x0F, 0x01, 0xFF
+    cmp     eax, 0
+    jnz     ValidationFailure
+
+    ; Ask hypervisor to change the page state to shared using the
+    ; Page State Change VMGEXIT.
+    ;
+    ; Setup GHCB MSR
+    ;   GHCB_MSR[55:52] = Page Operation
+    ;   GHCB_MSR[51:12] = Guest Physical Frame Number
+    ;   GHCB_MSR[11:0]  = Page State Change Request
+    ;
+    mov     eax, (GHCB_BASE >> 12)
+    shl     eax, 12
+    or      eax, GHCB_PAGE_STATE_CHANGE_REQUEST
+    mov     edx, (GHCB_PAGE_STATE_SHARED << 20)
+    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
+
+    ;
+    ; Response GHCB MSR
+    ;   GHCB_MSR[51:12] = Guest Physical Frame Number
+    ;   GHCB_MSR[11:0]  = Page State Change Response
+    ;
+    mov     ecx, 0xc0010130
+    rdmsr
+    and     eax, 0xfff
+    cmp     eax, GHCB_PAGE_STATE_CHANGE_RESPONSE
+    jnz     ValidationFailure
+    cmp     edx, 0
+    jnz     ValidationFailure
+
+    jmp     InvalidateGHCBPageDone
+
+ValidationFailure:
+    ;
+    ; Setup GHCB MSR
+    ;   GHCB_MSR[23:16] = 0
+    ;   GHCB_MSR[15:12] = 0
+    ;   GHCB_MSR[11:0]  = Terminate Request
+    ;
+    mov     edx, 0
+    mov     eax, GHCB_GENERAL_TERMINATE_REQUEST
+    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
+
+SnpPageStateFailureHlt:
+    cli
+    hlt
+    jmp     SnpPageStateFailureHlt
+
+InvalidateGHCBPageDone:
+    OneTimeCallRet InvalidateGHCBPage
+
 
 ; Check if Secure Encrypted Virtualization (SEV) features are enabled.
 ;
@@ -316,6 +409,19 @@ clearGhcbMemoryLoop:
     mov     dword[ecx * 4 + GHCB_BASE - 4], eax
     loop    clearGhcbMemoryLoop
 
+    ;
+    ; The page table built above cleared the memory encryption mask from the
+    ; GHCB_BASE (aka made it shared). When SEV-SNP is enabled, to maintain
+    ; the security guarantees, the page state transition from private to
+    ; shared must go through the page invalidation steps. Invalidate the
+    ; memory range before loading the page table below.
+    ;
+    ; NOTE: the invalidation must happen after zeroing the GHCB memory. This
+    ;       is because, in the 32-bit mode all the access are considered private.
+    ;       The invalidation before the zero'ing will cause a #VC.
+    ;
+    OneTimeCall  InvalidateGHCBPage
+
 SetCr3:
     ;
     ; Set CR3 now that the paging structures are available
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to validate system RAM
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (10 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 11/19] OvmfPkg/ResetVector: Invalidate the GHCB page Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-04-01  6:37   ` Yao, Jiewen
  2021-03-24 15:32 ` [RFC PATCH 13/19] OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase Brijesh Singh
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

Many of the integrity guarantees of SEV-SNP are enforced through the
Reverse Map Table (RMP). Each RMP entry contains the GPA at which a
particular page of DRAM should be mapped. The guest can request the
hypervisor to add pages in the RMP table via the Page State Change VMGEXIT
defined in the GHCB specification section 2.5.1 and 4.1.6. Inside each RMP
entry is a Validated flag; this flag is automatically cleared to 0 by the
CPU hardware when a new RMP entry is created for a guest. Each VM page
can be either validated or invalidated, as indicated by the Validated
flag in the RMP entry. Memory access to a private page that is not
validated generates a #VC. A VM can use the PVALIDATE instruction to
validate the private page before using it.

During the guest creation, the boot ROM memory is pre-validated by the
AMD-SEV firmware. The MemEncryptSevSnpValidateSystemRam() can be called
during the SEC and PEI phase to validate the detected system RAM.

One of the fields in the Page State Change NAE is the RMP page size. The
page size input parameter indicates that either a 4KB or 2MB page should
be used while adding the RMP entry. During the validation, when possible,
the MemEncryptSevSnpValidateSystemRam() will use the 2MB entry. A
hypervisor backing the memory may choose to use the different page size
in the RMP entry. In those cases, the PVALIDATE instruction should return
SIZEMISMATCH. If a SIZEMISMATCH is detected, then validate all 512-pages
constituting a 2MB region.

Upon completion, the PVALIDATE instruction sets the rFLAGS.CF to 0 if
instruction changed the RMP entry and to 1 if the instruction did not
change the RMP entry. The rFlags.CF will be 1 only when a memory region
is already validated. We should not double validate a memory
as it could lead to a security compromise. If double validation is
detected, terminate the boot.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Include/Library/MemEncryptSevLib.h                            |  15 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c        |  17 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf          |   4 +
 OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c    |  20 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h             |  37 +++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c    |  23 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c | 254 ++++++++++++++++++++
 7 files changed, 370 insertions(+)

diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h b/OvmfPkg/Include/Library/MemEncryptSevLib.h
index 03d9eda392..47d6802b61 100644
--- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
+++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
@@ -215,4 +215,19 @@ MemEncryptSevGetAddressRangeState (
   IN UINTN                    Length
   );
 
+/**
+  If SEV-SNP is active then set the page state of the specified virtual
+  address range. This should be called in SEC and PEI phases only.
+
+  @param[in]  BaseAddress             Base address
+  @param[in]  NumPages                Number of pages starting from the base address
+
+**/
+VOID
+EFIAPI
+MemEncryptSevSnpValidateSystemRam (
+  IN PHYSICAL_ADDRESS                   BaseAddress,
+  IN UINTN                              NumPages
+  );
+
 #endif // _MEM_ENCRYPT_SEV_LIB_H_
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
new file mode 100644
index 0000000000..dace5c0bcf
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
@@ -0,0 +1,17 @@
+#include <Uefi/UefiBaseType.h>
+
+#include "../SnpPageStateChange.h"
+
+/**
+ The function is used to set the page state when SEV-SNP is active. The page state
+ transition consist of changing the page ownership in the RMP table, and using the
+ PVALIDATE instruction to update the Validated bit in RMP table.
+
+ */
+VOID
+SevSnpValidateSystemRamInternal (
+  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
+  IN UINTN                            NumPages
+  )
+{
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
index 279c38bfbc..8595e244c2 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
@@ -31,15 +31,19 @@
 
 [Sources]
   SecMemEncryptSevLibInternal.c
+  SnpPageStateChange.h
 
 [Sources.X64]
   X64/MemEncryptSevLib.c
   X64/SecVirtualMemory.c
+  X64/SecSnpSystemRamValidate.c
+  X64/SnpPageStateChangeInternal.c
   X64/VirtualMemory.c
   X64/VirtualMemory.h
 
 [Sources.IA32]
   Ia32/MemEncryptSevLib.c
+  Ia32/SnpPageStateChange.c
 
 [LibraryClasses]
   BaseLib
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
index 69852779e2..35a222e75e 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
@@ -17,6 +17,8 @@
 #include <Register/Cpuid.h>
 #include <Uefi/UefiBaseType.h>
 
+#include "SnpPageStateChange.h"
+
 /**
   Reads and sets the status of SEV features.
 
@@ -172,3 +174,21 @@ MemEncryptSevLocateInitialSmramSaveStateMapPages (
 {
   return RETURN_UNSUPPORTED;
 }
+
+/**
+  If SEV-SNP is active then set the page state of the specified virtual
+  address range. This should be called in SEC and PEI phases only.
+
+  @param[in]  BaseAddress             Base address
+  @param[in]  NumPages                Number of pages starting from the base address
+
+**/
+VOID
+EFIAPI
+MemEncryptSevSnpValidateSystemRam (
+  IN PHYSICAL_ADDRESS                   BaseAddress,
+  IN UINTN                              NumPages
+  )
+{
+  SevSnpValidateSystemRam (BaseAddress, NumPages);
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h b/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
new file mode 100644
index 0000000000..3040930999
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
@@ -0,0 +1,37 @@
+/** @file
+
+  SEV-SNP Page Validation functions.
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SNP_PAGE_STATE_INTERNAL_H_
+#define SNP_PAGE_STATE_INTERNAL_H_
+
+//
+// SEV-SNP Page states
+//
+typedef enum {
+  SevSnpPagePrivate,
+  SevSnpPageShared,
+
+} SEV_SNP_PAGE_STATE;
+
+VOID
+SevSnpValidateSystemRam (
+  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
+  IN UINTN                            NumPages
+  );
+
+VOID
+SetPageStateInternal (
+  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
+  IN UINTN                            NumPages,
+  IN SEV_SNP_PAGE_STATE               State,
+  IN BOOLEAN                          UseLargeEntry
+  );
+
+#endif
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
new file mode 100644
index 0000000000..915706aad0
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
@@ -0,0 +1,23 @@
+/** @file
+
+  SEV-SNP Page Validation functions.
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseLib.h>
+
+#include "../SnpPageStateChange.h"
+
+VOID
+SevSnpValidateSystemRam (
+  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
+  IN UINTN                            NumPages
+  )
+{
+  SetPageStateInternal (BaseAddress, NumPages, SevSnpPagePrivate, TRUE);
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
new file mode 100644
index 0000000000..5a34db33fe
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
@@ -0,0 +1,254 @@
+/** @file
+
+  SEV-SNP Page Validation functions.
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/DebugLib.h>
+#include <Library/VmgExitLib.h>
+
+#include <Register/Amd/Ghcb.h>
+#include <Register/Amd/Msr.h>
+
+#include "../SnpPageStateChange.h"
+
+#define IS_ALIGNED(x, y)        ((((x) & (y - 1)) == 0))
+#define PAGES_PER_LARGE_ENTRY   512
+#define EFI_LARGE_PAGE          (EFI_PAGE_SIZE * PAGES_PER_LARGE_ENTRY)
+
+STATIC
+UINTN
+MemoryStateToGhcbOp (
+  IN SEV_SNP_PAGE_STATE   State
+  )
+{
+  UINTN Cmd;
+
+  switch (State) {
+    case SevSnpPageShared: Cmd = SNP_PAGE_STATE_SHARED; break;
+    case SevSnpPagePrivate: Cmd = SNP_PAGE_STATE_PRIVATE; break;
+    default: ASSERT(0);
+  }
+
+  return Cmd;
+}
+
+STATIC
+VOID
+SnpPageStateFailureTerminate (
+  VOID
+  )
+{
+  MSR_SEV_ES_GHCB_REGISTER  Msr;
+
+  //
+  // Use the GHCB MSR Protocol to request termination by the hypervisor
+  //
+  Msr.GhcbPhysicalAddress = 0;
+  Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
+  Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
+  Msr.GhcbTerminate.ReasonCode = GHCB_TERMINATE_GHCB_GENERAL;
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+  AsmVmgExit ();
+
+  ASSERT (FALSE);
+  CpuDeadLoop ();
+}
+
+STATIC
+UINTN
+IssuePvalidate (
+  IN    UINTN       Address,
+  IN    UINTN       RmpPageSize,
+  IN    BOOLEAN     Validate
+  )
+{
+  IA32_EFLAGS32         EFlags;
+  UINTN                 Ret;
+
+  Ret = AsmPvalidate (RmpPageSize, Validate, Address, &EFlags);
+
+  //
+  // Check the rFlags.CF to verify that PVALIDATE updated the RMP
+  // entry. If there was a no change in the RMP entry then we are
+  // either double validating or invalidating the memory. This can
+  // lead to a security compromise.
+  //
+  if (EFlags.Bits.CF) {
+    DEBUG ((DEBUG_ERROR, "%a:%a: Double %a detected for address 0x%Lx\n",
+           gEfiCallerBaseName,
+           __FUNCTION__,
+           Validate ? "Validate" : "Invalidate",
+           Address));
+    SnpPageStateFailureTerminate ();
+  }
+
+  return Ret;
+}
+
+/**
+ This function issues the PVALIDATE instruction to validate or invalidate the memory
+ range specified. If PVALIDATE returns size mismatch then it tries validating with
+ smaller page size.
+
+ */
+STATIC
+VOID
+PvalidateRange (
+  IN  SNP_PAGE_STATE_CHANGE_INFO    *Info,
+  IN  UINTN                         StartIndex,
+  IN  UINTN                         EndIndex,
+  IN  BOOLEAN                       Validate
+  )
+{
+  UINTN         Address, RmpPageSize, Ret, i;
+
+  for (; StartIndex < EndIndex; StartIndex++) {
+    Address = Info->Entry[StartIndex].GuestFrameNumber << EFI_PAGE_SHIFT;
+    RmpPageSize = Info->Entry[StartIndex].PageSize;
+
+    Ret = IssuePvalidate (Address, RmpPageSize, Validate);
+
+    //
+    // If we fail to validate due to size mismatch then try with the
+    // smaller page size. This senario will occur if the backing page in
+    // the RMP entry is 4K and we are validating it as a 2MB.
+    //
+    if ((Ret == PVALIDATE_RET_FAIL_SIZEMISMATCH) &&
+        (RmpPageSize == PVALIDATE_PAGE_SIZE_2M)) {
+      for (i = 0; i < PAGES_PER_LARGE_ENTRY; i++) {
+
+        Ret = IssuePvalidate (Address, PVALIDATE_PAGE_SIZE_4K, Validate);
+        if (Ret) {
+          break;
+        }
+
+        Address = Address + EFI_PAGE_SIZE;
+      }
+    }
+
+    if (Ret) {
+    DEBUG ((DEBUG_ERROR, "%a:%a: Failed to %a address 0x%Lx Error code %d\n",
+           gEfiCallerBaseName,
+           __FUNCTION__,
+           Validate ? "Validate" : "Invalidate",
+           Address,
+           Ret));
+      SnpPageStateFailureTerminate ();
+    }
+  }
+}
+
+/**
+ The function is used to set the page state when SEV-SNP is active. The page state
+ transition consist of changing the page ownership in the RMP table, and using the
+ PVALIDATE instruction to update the Validated bit in RMP table.
+
+ When the UseLargeEntry is set to TRUE, then use the large RMP entry.
+ */
+VOID
+SetPageStateInternal (
+  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
+  IN UINTN                            NumPages,
+  IN SEV_SNP_PAGE_STATE               State,
+  IN BOOLEAN                          UseLargeEntry
+  )
+{
+  EFI_STATUS                      Status;
+  GHCB                            *Ghcb;
+  EFI_PHYSICAL_ADDRESS            NextAddress, EndAddress;
+  MSR_SEV_ES_GHCB_REGISTER        Msr;
+  BOOLEAN                         InterruptState;
+  SNP_PAGE_STATE_CHANGE_INFO      *Info;
+  UINTN                           i, RmpPageSize;
+
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+  Ghcb = Msr.Ghcb;
+
+  EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
+
+  DEBUG ((DEBUG_VERBOSE, "%a:%a Address 0x%Lx - 0x%Lx State = %a LargeEntry = %d\n",
+          gEfiCallerBaseName,
+          __FUNCTION__,
+          BaseAddress,
+          EndAddress,
+          State == SevSnpPageShared ? "Shared" : "Private",
+          UseLargeEntry));
+
+  for (; BaseAddress < EndAddress; BaseAddress = NextAddress) {
+
+    //
+    // Initialize the GHCB and setup scratch sw to point to shared buffer.
+    //
+    VmgInit (Ghcb, &InterruptState);
+    Info = (SNP_PAGE_STATE_CHANGE_INFO *) Ghcb->SharedBuffer;
+
+    SetMem (Info, sizeof (*Info), 0);
+
+    //
+    // Build page state change buffer
+    //
+    for (i = 0; (EndAddress > BaseAddress) && i < SNP_PAGE_STATE_MAX_ENTRY;
+          BaseAddress = NextAddress, i++) {
+      //
+      // Is this a 2MB aligned page? Check if we can use the Large RMP entry.
+      //
+      if (UseLargeEntry &&
+          IS_ALIGNED (BaseAddress, EFI_LARGE_PAGE) &&
+          ((EndAddress - BaseAddress) >> EFI_PAGE_SHIFT) >= PAGES_PER_LARGE_ENTRY) {
+        RmpPageSize = PVALIDATE_PAGE_SIZE_2M;
+        NextAddress = BaseAddress + EFI_LARGE_PAGE;
+      } else {
+        RmpPageSize = PVALIDATE_PAGE_SIZE_4K;
+        NextAddress = BaseAddress + EFI_PAGE_SIZE;
+      }
+
+      Info->Entry[i].GuestFrameNumber = BaseAddress >> EFI_PAGE_SHIFT;
+      Info->Entry[i].PageSize = RmpPageSize;
+      Info->Entry[i].Op = MemoryStateToGhcbOp (State);
+      Info->Entry[i].CurrentPage = 0;
+    }
+
+    Info->Header.CurrentEntry = 0;
+    Info->Header.EndEntry = i - 1;
+
+    //
+    // If the request page state change is shared then invalidate the pages before
+    // adding the page in the RMP table.
+    //
+    if (State == SevSnpPageShared) {
+      PvalidateRange (Info, 0, i, FALSE);
+    }
+
+    //
+    // Issue the VMGEXIT and retry if hypervisor failed to process all the entries.
+    //
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+    VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+    while (Info->Header.CurrentEntry <= Info->Header.EndEntry) {
+      Status = VmgExit (Ghcb, SVM_EXIT_SNP_PAGE_STATE_CHANGE, 0, 0);
+      if (EFI_ERROR (Status)) {
+        SnpPageStateFailureTerminate ();
+      }
+    }
+
+    //
+    // If the request page state change is shared then invalidate the pages before
+    // adding the page in the RMP table.
+    //
+    if (State == SevSnpPagePrivate) {
+      PvalidateRange (Info, 0, i, TRUE);
+    }
+
+    VmgDone (Ghcb, InterruptState);
+  }
+}
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 13/19] OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (11 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to validate system RAM Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 14/19] OvmfPkg/MemEncryptSevLib: Add support to validate RAM in " Brijesh Singh
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

The VMM launch sequence should have validated all the data pages used
in the SEC phase. Before decompressing the firmware volume, validate
the data/code pages used during the decompression steps, and any other
pages used during the PEI phase entry.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Sec/SecMain.c   | 26 ++++++++++++++++++++
 OvmfPkg/Sec/SecMain.inf |  2 ++
 2 files changed, 28 insertions(+)

diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index df6722b546..b491810376 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -351,6 +351,32 @@ DecompressMemFvs (
     return Status;
   }
 
+  if (MemEncryptSevSnpIsEnabled ()) {
+    EFI_PHYSICAL_ADDRESS    LaunchValidatedBase, LaunchValidatedEnd;
+    UINTN                   Size;
+
+    //
+    // The VMM launch sequence should have validated the memory range from
+    // MEMFD_BASE_ADDRESS to PcdOvmfPeiMemFvBase. The PCD values are also
+    // accessible through PcdOvmfSnpLaunchValidatedStart, and PcdOvmfSnpLaunchValidatedEnd.
+    // The pre-validation was sufficent to access the data pages used in the SEC
+    // phase.
+    //
+    // Now that we are getting ready to decompress firmware volumes, and enter
+    // to PEI phase. Lets validate the code/data pages used for entering to the
+    // PEI phase.
+    //
+    // See FvmainCompactScratchEnd.fdf.inc for more detail.
+    //
+    LaunchValidatedBase =
+        (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSnpLaunchValidatedStart);
+    LaunchValidatedEnd = LaunchValidatedBase +
+        (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSnpLaunchValidatedEnd);
+    Size = PcdGet32 (PcdOvmfDecompressionScratchEnd) - LaunchValidatedEnd;
+
+    MemEncryptSevSnpValidateSystemRam (LaunchValidatedEnd, EFI_SIZE_TO_PAGES (Size));
+  }
+
   Status = ExtractGuidedSectionGetInfo (
              Section,
              &OutputBufferSize,
diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf
index 7f78dcee27..207accb53c 100644
--- a/OvmfPkg/Sec/SecMain.inf
+++ b/OvmfPkg/Sec/SecMain.inf
@@ -70,6 +70,8 @@
   gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd
   gEfiMdeModulePkgTokenSpaceGuid.PcdInitValueInTempStack
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpLaunchValidatedStart
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpLaunchValidatedEnd
 
 [FeaturePcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 14/19] OvmfPkg/MemEncryptSevLib: Add support to validate RAM in PEI phase
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (12 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 13/19] OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 15/19] OvmfPkg/PlatformPei: Validate the system RAM when SNP is active Brijesh Singh
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

MemEncryptSevSnpValidateSystemRam() is used for validating the system
RAM. During the validation process, we must avoid double validation
cases. The double validation can lead to security issues. Extend the
MemEncryptSevSnpValidateSystemRam() to use the interval search tree to
keep track of the validated range; if the requested range is already
validated, then do nothing. If the requested range overlaps with the
previous validation, then validate only non-overlapped ranges.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf       |   8 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c |  20 ++++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c | 105 +++++++++++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c       | 119 ++++++++++++++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h       |  36 ++++++
 5 files changed, 288 insertions(+)

diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
index 03a78c32df..cb9dd2bb21 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
@@ -32,10 +32,15 @@
 [Sources]
   PeiDxeMemEncryptSevLibInternal.c
   PeiMemEncryptSevLibInternal.c
+  SnpPageStateChange.h
 
 [Sources.X64]
   X64/MemEncryptSevLib.c
   X64/PeiDxeVirtualMemory.c
+  X64/PeiSnpSystemRamValidate.c
+  X64/SnpPageStateTrack.c
+  X64/SnpPageStateChangeInternal.c
+  X64/SnpPageStateTrack.h
   X64/VirtualMemory.c
   X64/VirtualMemory.h
 
@@ -49,9 +54,12 @@
   DebugLib
   MemoryAllocationLib
   PcdLib
+  VmgExitLib
 
 [FeaturePcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
 
 [FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c
index b561f211f5..9863722e9d 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLibInternal.c
@@ -17,6 +17,8 @@
 #include <Register/Cpuid.h>
 #include <Uefi/UefiBaseType.h>
 
+#include "SnpPageStateChange.h"
+
 STATIC BOOLEAN mSevStatus = FALSE;
 STATIC BOOLEAN mSevEsStatus = FALSE;
 STATIC BOOLEAN mSevSnpStatus = FALSE;
@@ -184,3 +186,21 @@ MemEncryptSevGetEncryptionMask (
 
   return mSevEncryptionMask;
 }
+
+/**
+  If SEV-SNP is active then set the page state of the specified virtual
+  address range. This should be called in SEC and PEI phases only.
+
+  @param[in]  BaseAddress             Base address
+  @param[in]  NumPages                Number of pages starting from the base address
+
+**/
+VOID
+EFIAPI
+MemEncryptSevSnpValidateSystemRam (
+  IN PHYSICAL_ADDRESS                   BaseAddress,
+  IN UINTN                              NumPages
+  )
+{
+  SevSnpValidateSystemRam (BaseAddress, NumPages);
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
new file mode 100644
index 0000000000..ce8a05bb1f
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
@@ -0,0 +1,105 @@
+/** @file
+
+  SEV-SNP Page Validation functions.
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/DebugLib.h>
+
+#include "../SnpPageStateChange.h"
+#include "SnpPageStateTrack.h"
+
+STATIC SNP_VALIDATED_RANGE     *mRootNode;
+
+STATIC
+SNP_VALIDATED_RANGE *
+SetPageStateChangeInitialize (
+  VOID
+  )
+{
+  UINTN                      StartAddress, EndAddress;
+  SNP_VALIDATED_RANGE        *RootNode;
+
+  //
+  // The memory range from PcdOvmfSnpCpuidBase to PcdOvmfDecompressionScratchEnd is
+  // prevalidated before we enter into the Pei phase. The pre-validation breakdown
+  // looks like this:
+  //
+  //    SnpCpuidBase                                      (VMM)
+  //    SnpSecretBase                                     (VMM)
+  //    SnpLaunchValidatedStart - SnpLaunchValidatedEnd   (VMM)
+  //    SnpLaunchValidatedEnd - DecompressionScratchEnd   (SecMain)
+  //
+  // Add the range in system ram region tracker interval tree. The interval tree will
+  // used to check whether there is an overlap with the pre-validated region. We will
+  // skip validating the pre-validated region.
+  //
+  StartAddress = (UINTN) PcdGet32 (PcdOvmfSnpCpuidBase);
+  EndAddress = (UINTN) PcdGet32 (PcdOvmfDecompressionScratchEnd);
+
+  RootNode = AddRangeToIntervalTree (NULL, StartAddress, EndAddress);
+  if (RootNode == NULL) {
+    DEBUG ((DEBUG_ERROR, "Failed to add range to interval tree\n"));
+    ASSERT (FALSE);
+  }
+
+  return RootNode;
+}
+
+VOID
+SevSnpValidateSystemRam (
+  IN UINTN      BaseAddress,
+  IN UINTN      NumPages
+  )
+{
+  UINTN                   EndAddress;
+  SNP_VALIDATED_RANGE     *Range;
+
+  EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
+
+  //
+  // If the Root is NULL then its the first call. Lets initialize the List before
+  // we process the request.
+  //
+  if (mRootNode == NULL) {
+    mRootNode = SetPageStateChangeInitialize ();
+  }
+
+  //
+  // Check if the range is already validated
+  //
+  EndAddress = BaseAddress + EFI_PAGES_TO_SIZE(NumPages);
+  Range = FindOverlapRange (mRootNode, BaseAddress, EndAddress);
+
+  //
+  // Range is not validated
+  if (Range == NULL) {
+    SetPageStateInternal (BaseAddress, NumPages, SevSnpPagePrivate, TRUE);
+    AddRangeToIntervalTree (mRootNode, BaseAddress, EndAddress);
+    return;
+  }
+
+  //
+  // The input range overlaps with the pre-validated range. Calculate the non-overlap
+  // region and validate them.
+  //
+  if (BaseAddress < Range->StartAddress) {
+    NumPages = EFI_SIZE_TO_PAGES (Range->StartAddress - BaseAddress);
+    SetPageStateInternal (BaseAddress, NumPages, SevSnpPagePrivate, TRUE);
+    AddRangeToIntervalTree (mRootNode, BaseAddress, Range->StartAddress);
+  }
+
+  if (EndAddress > Range->EndAddress) {
+    NumPages = EFI_SIZE_TO_PAGES (EndAddress - Range->EndAddress);
+    SetPageStateInternal (Range->EndAddress, NumPages, SevSnpPagePrivate, TRUE);
+    AddRangeToIntervalTree (mRootNode, Range->StartAddress, EndAddress);
+  }
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c
new file mode 100644
index 0000000000..91b4fc8db4
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c
@@ -0,0 +1,119 @@
+/** @file
+
+  Provides a simple interval search tree implementation that will be used
+  by the SnpValidateSystemRam() to keep track of the memory range validated
+  during the SEC/PEI phases.
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/MemoryAllocationLib.h>
+
+#include "SnpPageStateTrack.h"
+
+STATIC
+SNP_VALIDATED_RANGE *
+AllocNewNode (
+  IN  UINTN     StartAddress,
+  IN  UINTN     EndAddress
+  )
+{
+  SNP_VALIDATED_RANGE   *Node;
+
+  Node = AllocatePool (sizeof (SNP_VALIDATED_RANGE));
+  if (Node == NULL) {
+    return NULL;
+  }
+
+  Node->StartAddress  = StartAddress;
+  Node->EndAddress = EndAddress;
+  Node->MaxAddress = Node->EndAddress;
+  Node->Left = Node->Right = NULL;
+
+  return Node;
+}
+
+STATIC
+BOOLEAN
+RangeIsOverlap (
+  IN  SNP_VALIDATED_RANGE     *Node,
+  IN  UINTN                   StartAddress,
+  IN  UINTN                   EndAddress
+  )
+{
+  if (Node->StartAddress < EndAddress && StartAddress < Node->EndAddress) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+/**
+ Function to find the overlapping range within the interval tree. If range is not
+ found then NULL is returned.
+
+ */
+SNP_VALIDATED_RANGE *
+FindOverlapRange (
+  IN  SNP_VALIDATED_RANGE   *RootNode,
+  IN  UINTN                 StartAddress,
+  IN  UINTN                 EndAddress
+  )
+{
+  // Tree is empty or no overlap found
+  if (RootNode == NULL) {
+    return NULL;
+  }
+
+  // Check with the range exist in the root node
+  if (RangeIsOverlap(RootNode, StartAddress, EndAddress)) {
+    return RootNode;
+  }
+
+  //
+  // If the left child of root is present and the max of the left child is
+  // greater than or equal to a given range then requested range will overlap
+  // with left subtree
+  //
+  if (RootNode->Left != NULL && (RootNode->Left->MaxAddress >= StartAddress)) {
+    return FindOverlapRange (RootNode->Left, StartAddress, EndAddress);
+  }
+
+  // The range can only overlap with the right subtree
+  return FindOverlapRange (RootNode->Right, StartAddress, EndAddress);
+}
+
+/**
+ Function to insert the validated range in the interval search tree.
+
+ */
+SNP_VALIDATED_RANGE *
+AddRangeToIntervalTree (
+  IN  SNP_VALIDATED_RANGE   *RootNode,
+  IN  UINTN                 StartAddress,
+  IN  UINTN                 EndAddress
+  )
+{
+  // Tree is empty or we reached to the leaf
+  if (RootNode == NULL) {
+    return AllocNewNode (StartAddress, EndAddress);
+  }
+
+  // If the StartAddress is smaller then the BaseAddress then go to the left in the tree.
+  if (StartAddress < RootNode->StartAddress) {
+    RootNode->Left = AddRangeToIntervalTree (RootNode->Left, StartAddress, EndAddress);
+  } else {
+    RootNode->Right = AddRangeToIntervalTree (RootNode->Right, StartAddress, EndAddress);
+  }
+
+  // Update the max value of the ancestor if needed
+  if (RootNode->MaxAddress < EndAddress) {
+    RootNode->MaxAddress = EndAddress;
+  }
+
+  return RootNode;
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h
new file mode 100644
index 0000000000..106c3411f0
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h
@@ -0,0 +1,36 @@
+/** @file
+
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SNP_PAGE_STATE_TRACK_INTERNAL_H_
+#define SNP_PAGE_STATE_TRACK_INTERNAL_H_
+
+#include <Uefi/UefiBaseType.h>
+
+typedef struct SNP_VALIDATED_RANGE {
+  UINT64    StartAddress, EndAddress;
+  UINT64    MaxAddress;
+
+  struct SNP_VALIDATED_RANGE  *Left, *Right;
+} SNP_VALIDATED_RANGE;
+
+SNP_VALIDATED_RANGE *
+FindOverlapRange (
+  IN  SNP_VALIDATED_RANGE   *RootNode,
+  IN  UINTN                 StartAddress,
+  IN  UINTN                 EndAddress
+  );
+
+SNP_VALIDATED_RANGE *
+AddRangeToIntervalTree (
+  IN  SNP_VALIDATED_RANGE   *RootNode,
+  IN  UINTN                 StartAddress,
+  IN  UINTN                 EndAddress
+  );
+
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 15/19] OvmfPkg/PlatformPei: Validate the system RAM when SNP is active
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (13 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 14/19] OvmfPkg/MemEncryptSevLib: Add support to validate RAM in " Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 16/19] OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI phase Brijesh Singh
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

When SEV-SNP is active, a memory region mapped encrypted in the page
table must be validated before access. There are two approaches that
can be taken to validate the system RAM detected during the PEI phase:

1) Validate on-demand
OR
2) Validate before access

On-demand
=========
If memory is not validated before access, it will cause a #VC
exception with the page-not-validated error code. The VC exception
handler can perform the validation steps.

The pages that have been validated will need to be tracked to avoid
the double validation scenarios. The range of memory that has not
been validated will need to be communicated to the OS through the
recently introduced unaccepted memory type
https://github.com/microsoft/mu_basecore/pull/66, so that OS can
validate those ranges before using them.

Validate before access
======================
Since the PEI phase detects all the available system RAM, use the
MemEncryptSevSnpValidateSystemRam() function to pre-validate the
system RAM in the PEI phase.

For now, we have chosen option 2 due to the dependency and the complexity
of the on-demand validation.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/PlatformPei/AmdSev.c | 41 ++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index 95c5ad235f..abbbef54c1 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -140,6 +140,42 @@ AmdSevEsInitialize (
   AsmWriteGdtr (&Gdtr);
 }
 
+/**
+
+  Initialize SEV-SNP support if running as an SEV-SNP guest.
+
+  **/
+STATIC
+VOID
+AmdSevSnpInitialize (
+  VOID
+  )
+{
+  EFI_PEI_HOB_POINTERS          Hob;
+  EFI_HOB_RESOURCE_DESCRIPTOR   *ResourceHob;
+
+  if (!MemEncryptSevSnpIsEnabled ()) {
+    return;
+  }
+
+  DEBUG ((EFI_D_INFO, "SEV-SNP is enabled.\n"));
+
+  //
+  // Iterate through the system RAM and validate it.
+  //
+  for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
+    if (Hob.Raw != NULL && GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
+      ResourceHob = Hob.ResourceDescriptor;
+
+      if (ResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+        MemEncryptSevSnpValidateSystemRam (ResourceHob->PhysicalStart,
+                                           EFI_SIZE_TO_PAGES (ResourceHob->ResourceLength)
+                                          );
+      }
+    }
+  }
+}
+
 /**
 
   Function checks if SEV support is available, if present then it sets
@@ -217,6 +253,11 @@ AmdSevInitialize (
     }
   }
 
+  //
+  // Check and perform SEV-SNP initialization if required.
+  //
+  AmdSevSnpInitialize ();
+
   //
   // Check and perform SEV-ES initialization if required.
   //
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 16/19] OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI phase
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (14 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 15/19] OvmfPkg/PlatformPei: Validate the system RAM when SNP is active Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-04-01  6:43   ` Yao, Jiewen
  2021-03-24 15:32 ` [RFC PATCH 17/19] OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase Brijesh Singh
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

The initial page built during the SEC phase is used by the
MemEncryptSevSnpValidateSystemRam() for the system RAM validation. The
page validation process requires using the PVALIDATE instruction;  the
instruction accepts a virtual address of the memory region that needs
to be validated. If hardware encounters a page table walk failure
(due to page-not-present) then it raises #GP.

The initial page table built in SEC phase address up to 4GB. Add an
internal function to extend the page table to cover > 4GB. The function
builds 1GB entries in the page table for access > 4GB. This will provide
the support to call PVALIDATE instruction for the virtual address >
4GB in PEI phase.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c     | 115 ++++++++++++++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c |  16 +++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h           |  19 ++++
 3 files changed, 150 insertions(+)

diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
index d3455e812b..33d9bafe9f 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
@@ -536,6 +536,121 @@ EnableReadOnlyPageWriteProtect (
   AsmWriteCr0 (AsmReadCr0() | BIT16);
 }
 
+RETURN_STATUS
+EFIAPI
+InternalMemEncryptSevCreateIdentityMap1G (
+  IN    PHYSICAL_ADDRESS      Cr3BaseAddress,
+  IN    PHYSICAL_ADDRESS      PhysicalAddress,
+  IN    UINTN                 Length
+  )
+{
+  PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+  PAGE_TABLE_1G_ENTRY            *PageDirectory1GEntry;
+  UINT64                         PgTableMask;
+  UINT64                         AddressEncMask;
+  BOOLEAN                        IsWpEnabled;
+  RETURN_STATUS                  Status;
+
+  //
+  // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.
+  //
+  PageMapLevel4Entry = NULL;
+
+  DEBUG ((
+    DEBUG_VERBOSE,
+    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx\n",
+    gEfiCallerBaseName,
+    __FUNCTION__,
+    Cr3BaseAddress,
+    PhysicalAddress,
+    (UINT64)Length
+    ));
+
+  if (Length == 0) {
+    return RETURN_INVALID_PARAMETER;
+  }
+
+  //
+  // Check if we have a valid memory encryption mask
+  //
+  AddressEncMask = InternalGetMemEncryptionAddressMask ();
+  if (!AddressEncMask) {
+    return RETURN_ACCESS_DENIED;
+  }
+
+  PgTableMask = AddressEncMask | EFI_PAGE_MASK;
+
+
+  //
+  // Make sure that the page table is changeable.
+  //
+  IsWpEnabled = IsReadOnlyPageWriteProtected ();
+  if (IsWpEnabled) {
+    DisableReadOnlyPageWriteProtect ();
+  }
+
+  Status = EFI_SUCCESS;
+
+  while (Length)
+  {
+    //
+    // If Cr3BaseAddress is not specified then read the current CR3
+    //
+    if (Cr3BaseAddress == 0) {
+      Cr3BaseAddress = AsmReadCr3();
+    }
+
+    PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask);
+    PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress);
+    if (!PageMapLevel4Entry->Bits.Present) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "%a:%a: bad PML4 for Physical=0x%Lx\n",
+        gEfiCallerBaseName,
+        __FUNCTION__,
+        PhysicalAddress
+        ));
+      Status = RETURN_NO_MAPPING;
+      goto Done;
+    }
+
+    PageDirectory1GEntry = (VOID *)(
+                             (PageMapLevel4Entry->Bits.PageTableBaseAddress <<
+                              12) & ~PgTableMask
+                             );
+    PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress);
+    if (!PageDirectory1GEntry->Bits.Present) {
+      PageDirectory1GEntry->Bits.Present = 1;
+      PageDirectory1GEntry->Bits.MustBe1 = 1;
+      PageDirectory1GEntry->Bits.MustBeZero = 0;
+      PageDirectory1GEntry->Bits.ReadWrite = 1;
+      PageDirectory1GEntry->Uint64 |= (UINT64)PhysicalAddress | AddressEncMask;
+    }
+
+    if (Length <= BIT30) {
+      Length = 0;
+    } else {
+      Length -= BIT30;
+    }
+
+    PhysicalAddress += BIT30;
+  }
+
+  //
+  // Flush TLB
+  //
+  CpuFlushTlb();
+
+Done:
+  //
+  // Restore page table write protection, if any.
+  //
+  if (IsWpEnabled) {
+    EnableReadOnlyPageWriteProtect ();
+  }
+
+  return Status;
+}
 
 /**
   This function either sets or clears memory encryption bit for the memory
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
index ce8a05bb1f..41bf301efe 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
@@ -16,6 +16,7 @@
 
 #include "../SnpPageStateChange.h"
 #include "SnpPageStateTrack.h"
+#include "VirtualMemory.h"
 
 STATIC SNP_VALIDATED_RANGE     *mRootNode;
 
@@ -62,9 +63,24 @@ SevSnpValidateSystemRam (
 {
   UINTN                   EndAddress;
   SNP_VALIDATED_RANGE     *Range;
+  EFI_STATUS              Status;
 
   EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
 
+  //
+  // The page table used in PEI can address up to 4GB memory. If we are asked to validate
+  // a range above the 4GB, then create an identity mapping so that the PVALIDATE instruction
+  // can execute correctly. If the page table entry is not present then PVALIDATE will
+  // cause the #GP.
+  //
+  if (BaseAddress >= SIZE_4GB) {
+    Status = InternalMemEncryptSevCreateIdentityMap1G (0, BaseAddress,
+                  EFI_PAGES_TO_SIZE (NumPages));
+    if (EFI_ERROR (Status)) {
+      ASSERT (FALSE);
+    }
+  }
+
   //
   // If the Root is NULL then its the first call. Lets initialize the List before
   // we process the request.
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
index 996f94f07e..829dc96a1d 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
@@ -267,4 +267,23 @@ InternalMemEncryptSevGetAddressRangeState (
   IN UINTN                    Length
   );
 
+/**
+  Create 1GB identity mapping for the specified virtual address range.
+
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use
+                                      current CR3)
+  @param[in]  VirtualAddress          Virtual address to check
+  @param[in]  Length                  Length of virtual address range
+
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.
+
+**/
+RETURN_STATUS
+EFIAPI
+InternalMemEncryptSevCreateIdentityMap1G (
+  IN    PHYSICAL_ADDRESS      Cr3BaseAddress,
+  IN    PHYSICAL_ADDRESS      PhysicalAddress,
+  IN    UINTN                 Length
+  );
+
 #endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 17/19] OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (15 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 16/19] OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI phase Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 18/19] OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc attribute Brijesh Singh
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

Commit 85b8eac59b8c5bd9c7eb9afdb64357ce1aa2e803 added support to ensure
that MMIO is only performed against the un-encrypted memory. If MMIO
is performed against encrypted memory, a #GP is raised.

The VmgExitLib library depends on ApicTimerLib to get the APIC base
address so that it can exclude the APIC range from the un-encrypted
check. The OvmfPkg provides ApicTimerLib for the DXE phase. The
constructor AcpiTimerLibConstructor() used in the ApicTimerLib uses
the PciRead to get the PMBASE register. The PciRead() will cause an
MMIO access.

The AmdSevDxe driver clears the memory encryption attribute from the
MMIO ranges. However, if VmgExitLib is linked to AmdSevDxe driver then the
AcpiTimerLibConstructor() will be called before AmdSevDxe driver can
clear the encryption attributes for the MMIO regions.

Exclude the PMBASE register from the encrypted check so that we
can link VmgExitLib to the MemEncryptSevLib; which gets linked to
AmdSevDxe driver.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf  |  4 ++
 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |  7 +++
 OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c | 45 ++++++++++++++++++++
 3 files changed, 56 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
index e6f6ea7972..22435a0590 100644
--- a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
@@ -27,6 +27,7 @@
   SecVmgExitVcHandler.c
 
 [Packages]
+  MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
@@ -42,4 +43,7 @@
 [FixedPcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
+  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
 
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
index c66c68726c..d3175c260e 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
@@ -27,6 +27,7 @@
   PeiDxeVmgExitVcHandler.c
 
 [Packages]
+  MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
@@ -37,4 +38,10 @@
   DebugLib
   LocalApicLib
   MemEncryptSevLib
+  PcdLib
 
+[FixedPcd]
+  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
index 24259060fd..01ac5d8c19 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
@@ -14,7 +14,10 @@
 #include <Library/VmgExitLib.h>
 #include <Register/Amd/Msr.h>
 #include <Register/Intel/Cpuid.h>
+#include <IndustryStandard/Q35MchIch9.h>
+#include <IndustryStandard/I440FxPiix4.h>
 #include <IndustryStandard/InstructionParsing.h>
+#include <Library/PcdLib.h>
 
 #include "VmgExitVcHandler.h"
 
@@ -596,6 +599,40 @@ UnsupportedExit (
   return Status;
 }
 
+STATIC
+BOOLEAN
+IsPmbaBaseAddress (
+  IN  UINTN     Address
+  )
+{
+  UINT16 HostBridgeDevId;
+  UINTN Pmba;
+
+  //
+  // Query Host Bridge DID to determine platform type
+  //
+  HostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
+  switch (HostBridgeDevId) {
+    case INTEL_82441_DEVICE_ID:
+      Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
+      break;
+    case INTEL_Q35_MCH_DEVICE_ID:
+      Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
+      //
+      // Add the MMCONFIG base address to get the Pmba base access address
+      //
+      Pmba += FixedPcdGet64 (PcdPciExpressBaseAddress);
+      break;
+    default:
+      return FALSE;
+  }
+
+  // Round up the offset to page size
+  Pmba = Pmba & ~(SIZE_4KB - 1);
+
+  return (Address == Pmba);
+}
+
 /**
   Validate that the MMIO memory access is not to encrypted memory.
 
@@ -640,6 +677,14 @@ ValidateMmioMemory (
     return 0;
   }
 
+  //
+  // Allow PMBASE accesses (which will have the encryption bit set before
+  // AmdSevDxe runs in the DXE phase)
+  //
+  if (IsPmbaBaseAddress (Address)) {
+    return 0;
+  }
+
   //
   // Any state other than unencrypted is an error, issue a #GP.
   //
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 18/19] OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc attribute
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (16 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 17/19] OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 20:07   ` Brijesh Singh
  2021-03-24 15:32 ` [RFC PATCH 19/19] OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region Brijesh Singh
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

The MemEncryptSev{Set,Clear}PageEncMask() functions are used to set or
clear the memory encryption attribute in the page table. When SEV-SNP is
active, we also need to validate or invalidate the pages and update the
RMP entry.

Before clearing the encryption attribute we need to invalidate the memory,
and then make the page shared in the RMP entry. Similarly, after setting
the encryption attribute in the page table, we add the memory as private
in the RMP entry and validate it.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf     |  3 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf     |  1 +
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c | 32 +++++++++++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c   | 36 ++++++++++++++++++--
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h       | 27 +++++++++++++++
 5 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
index f2e162d680..fa8f7719a7 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
@@ -36,6 +36,8 @@
 [Sources.X64]
   X64/MemEncryptSevLib.c
   X64/PeiDxeVirtualMemory.c
+  X64/SnpPageStateChangeInternal.c
+  X64/PeiDxeSnpSetPageState.c
   X64/VirtualMemory.c
   X64/VirtualMemory.h
 
@@ -49,6 +51,7 @@
   DebugLib
   MemoryAllocationLib
   PcdLib
+  VmgExitLib
 
 [FeaturePcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
index cb9dd2bb21..d16ec44954 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
@@ -37,6 +37,7 @@
 [Sources.X64]
   X64/MemEncryptSevLib.c
   X64/PeiDxeVirtualMemory.c
+  X64/PeiDxeSnpSetPageState.c
   X64/PeiSnpSystemRamValidate.c
   X64/SnpPageStateTrack.c
   X64/SnpPageStateChangeInternal.c
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
new file mode 100644
index 0000000000..0a3d58ac22
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
@@ -0,0 +1,32 @@
+/** @file
+
+  SEV-SNP Page Validation functions.
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseLib.h>
+
+#include "PeiSnpPageStateChange.h"
+
+VOID
+SnpSetMemoryPrivate (
+  IN  PHYSICAL_ADDRESS      PhysicalAddress,
+  IN  UINTN                 Length
+  )
+{
+  SetPageStateInternal (PhysicalAddress, EFI_SIZE_TO_PAGES (Length), SevSnpPagePrivate, FALSE);
+}
+
+VOID
+SnpSetMemoryShared (
+  IN  PHYSICAL_ADDRESS      PhysicalAddress,
+  IN  UINTN                 Length
+  )
+{
+  SetPageStateInternal (PhysicalAddress, EFI_SIZE_TO_PAGES (Length), SevSnpPageShared, FALSE);
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
index 33d9bafe9f..26d363d427 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
@@ -17,6 +17,7 @@
 #include <Register/Cpuid.h>
 
 #include "VirtualMemory.h"
+#include "SnpSetPageState.h"
 
 STATIC BOOLEAN mAddressEncMaskChecked = FALSE;
 STATIC UINT64  mAddressEncMask;
@@ -700,22 +701,34 @@ SetMemoryEncDec (
   UINT64                         AddressEncMask;
   BOOLEAN                        IsWpEnabled;
   RETURN_STATUS                  Status;
+  BOOLEAN                        NeedPageStateChange;
+  PHYSICAL_ADDRESS               OrigPhysicalAddress;
+  UINTN                          OrigLength;
 
   //
   // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.
   //
   PageMapLevel4Entry = NULL;
 
+  //
+  // When SEV-SNP is active, before clearing the encryption attribute from
+  // the page table we also need to update the RMP entry for the memory
+  // region to make the region shared. And after setting the encryption
+  // attribute, the region must be made private in the RMP table.
+  //
+  NeedPageStateChange = MemEncryptSevSnpIsEnabled ();
+
   DEBUG ((
     DEBUG_VERBOSE,
-    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u\n",
+    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u Rmpupdate=%u\n",
     gEfiCallerBaseName,
     __FUNCTION__,
     Cr3BaseAddress,
     PhysicalAddress,
     (UINT64)Length,
     (Mode == SetCBit) ? "Encrypt" : "Decrypt",
-    (UINT32)CacheFlush
+    (UINT32)CacheFlush,
+    (UINT32)NeedPageStateChange
     ));
 
   //
@@ -749,6 +762,18 @@ SetMemoryEncDec (
     DisableReadOnlyPageWriteProtect ();
   }
 
+  //
+  // Make the RMP updates before clearing the encryption attribute in the page table.
+  //
+  if (NeedPageStateChange && (Mode == ClearCBit)) {
+    SnpSetMemoryShared (PhysicalAddress, Length);
+  }
+
+  //
+  // Save the values, we need it later during the Page state change.
+  //
+  OrigPhysicalAddress = PhysicalAddress;
+  OrigLength = Length;
   Status = EFI_SUCCESS;
 
   while (Length != 0)
@@ -923,6 +948,13 @@ SetMemoryEncDec (
   //
   CpuFlushTlb();
 
+  //
+  // Make the RMP updates after setting the encryption attribute in the page table.
+  //
+  if (NeedPageStateChange && (Mode == SetCBit)) {
+    SnpSetMemoryPrivate (OrigPhysicalAddress, OrigLength);
+  }
+
 Done:
   //
   // Restore page table write protection, if any.
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
new file mode 100644
index 0000000000..0b29bad612
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
@@ -0,0 +1,27 @@
+/** @file
+
+  SEV-SNP Page Validation functions.
+
+  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PEI_DXE_SNP_PAGE_STATE_INTERNAL_H_
+#define PEI_DXE_SNP_PAGE_STATE_INTERNAL_H_
+
+#include "../SnpPageStateChange.h"
+
+VOID
+SnpSetMemoryPrivate (
+  IN  PHYSICAL_ADDRESS      PhysicalAddress,
+  IN  UINTN                 Length
+  );
+
+VOID
+SnpSetMemoryShared (
+  IN  PHYSICAL_ADDRESS      PhysicalAddress,
+  IN  UINTN                 Length
+  );
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [RFC PATCH 19/19] OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (17 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 18/19] OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc attribute Brijesh Singh
@ 2021-03-24 15:32 ` Brijesh Singh
  2021-03-24 19:14 ` [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support Laszlo Ersek
  2021-04-08  9:58 ` Laszlo Ersek
  20 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 15:32 UTC (permalink / raw)
  To: devel
  Cc: Brijesh Singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

The PEI phases uses MemEncryptSevSnpValidateSystemRam() to validate
the system RAM. MemEncryptSev{Set,Clear}PageEncMask() is
later used by libraries/drivers to change the page state from private
to shared and vice versa.

The AmdSevDxe driver calls the MemEncryptSevClearPageEncMask() to remove
the encryption attribute from the reserved and non-existent memory regions.
Those regions where not part of system RAM, and was not pre-validated
during PEI phase, so we fail to change the page state for it.

There are multiple approaches to fix it:

1) Add a new parameter to MemEncryptSevClearPageEncMask(), that should be
   set by the caller to indicate whether the region is RAM.
OR
2) Lookup the address in the interval tree maintained by the
   MemEncryptSevSnpValidateSystemRam() to determine whether we validated
   the page in the past.
OR
3) Iterate through the Memory space GCD to calculate if the address range
   is RAM.

For now, we have chosen #2, it does not require a changes to the
caller of MemEncryptSevClearPageEncMask() and lookup routine is
already available.

Extend the SEV-ES workarea to pass the interval tree root pointer
so that we can perform the lookup later. If the specified address
was not present in the tree, then do not invalidate the page as it
could result in page state change failure.

Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 OvmfPkg/Include/Library/MemEncryptSevLib.h                         |  3 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf       |  4 ++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c   | 31 +++++++++++++++
 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c | 42 ++++++++++++--------
 4 files changed, 63 insertions(+), 17 deletions(-)

diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h b/OvmfPkg/Include/Library/MemEncryptSevLib.h
index 47d6802b61..712590b64d 100644
--- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
+++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
@@ -54,6 +54,9 @@ typedef struct _SEC_SEV_ES_WORK_AREA {
   UINT64   RandomData;
 
   UINT64   EncryptionMask;
+
+  UINT64   SnpSystemRamValidatedRootAddress;
+
 } SEC_SEV_ES_WORK_AREA;
 
 //
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
index fa8f7719a7..43b842254f 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
@@ -38,6 +38,7 @@
   X64/PeiDxeVirtualMemory.c
   X64/SnpPageStateChangeInternal.c
   X64/PeiDxeSnpSetPageState.c
+  X64/SnpPageStateTrack.c
   X64/VirtualMemory.c
   X64/VirtualMemory.h
 
@@ -58,3 +59,6 @@
 
 [Pcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask
+
+[FixedPcd]
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
index 0a3d58ac22..9fe6831368 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
@@ -10,8 +10,12 @@
 
 #include <Uefi/UefiBaseType.h>
 #include <Library/BaseLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/PcdLib.h>
+#include <Library/DebugLib.h>
 
 #include "PeiSnpPageStateChange.h"
+#include "SnpPageStateTrack.h"
 
 VOID
 SnpSetMemoryPrivate (
@@ -28,5 +32,32 @@ SnpSetMemoryShared (
   IN  UINTN                 Length
   )
 {
+  SEC_SEV_ES_WORK_AREA       *SevEsWorkArea;
+  SNP_VALIDATED_RANGE        *RootNode, *Range;
+
+  //
+  // Get the Page State tracker root node. The information will be used to lookup
+  // the address in the page state tracker.
+  //
+  SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *) FixedPcdGet32 (PcdSevEsWorkAreaBase);
+  RootNode = (SNP_VALIDATED_RANGE *) SevEsWorkArea->SnpSystemRamValidatedRootAddress;
+
+  //
+  // Check if the region is validated during the System RAM validation process.
+  // If region is not validated then do nothing. This typically will happen if
+  // we are getting called to make the page state change for the MMIO region.
+  // The MMIO regions fall within reserved memory type and does not require
+  // page state changes.
+  //
+  Range = FindOverlapRange (RootNode, PhysicalAddress, PhysicalAddress + Length);
+  if (Range == NULL) {
+    DEBUG ((EFI_D_INFO, "%a:%a %Lx - %Lx is not RAM, skipping it.\n",
+          gEfiCallerBaseName,
+          __FUNCTION__,
+          PhysicalAddress,
+          PhysicalAddress + Length));
+    return;
+  }
+
   SetPageStateInternal (PhysicalAddress, EFI_SIZE_TO_PAGES (Length), SevSnpPageShared, FALSE);
 }
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
index 41bf301efe..2e049d3df7 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
@@ -18,8 +18,6 @@
 #include "SnpPageStateTrack.h"
 #include "VirtualMemory.h"
 
-STATIC SNP_VALIDATED_RANGE     *mRootNode;
-
 STATIC
 SNP_VALIDATED_RANGE *
 SetPageStateChangeInitialize (
@@ -64,15 +62,34 @@ SevSnpValidateSystemRam (
   UINTN                   EndAddress;
   SNP_VALIDATED_RANGE     *Range;
   EFI_STATUS              Status;
+  SEC_SEV_ES_WORK_AREA    *SevEsWorkArea;
+  SNP_VALIDATED_RANGE     *RootNode;
 
   EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
 
+  // The Root of SNP_VALIDATED_RANGE is saved in the EsWorkArea.
+  SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *) FixedPcdGet32 (PcdSevEsWorkAreaBase);
+  RootNode = (SNP_VALIDATED_RANGE *) SevEsWorkArea->SnpSystemRamValidatedRootAddress;
+
+  //
+  // If the Root is NULL then its the first call. Lets initialize the List before
+  // we process the request.
+  //
+  if (RootNode == NULL) {
+    RootNode = SetPageStateChangeInitialize ();
+
+    //
+    // Save the RootNode in the workarea
+    //
+    SevEsWorkArea->SnpSystemRamValidatedRootAddress = (UINT64) (UINTN) RootNode;
+  }
+
   //
   // The page table used in PEI can address up to 4GB memory. If we are asked to validate
   // a range above the 4GB, then create an identity mapping so that the PVALIDATE instruction
-  // can execute correctly. If the page table entry is not present then PVALIDATE will
-  // cause the #GP.
   //
+  //
+  
   if (BaseAddress >= SIZE_4GB) {
     Status = InternalMemEncryptSevCreateIdentityMap1G (0, BaseAddress,
                   EFI_PAGES_TO_SIZE (NumPages));
@@ -81,25 +98,16 @@ SevSnpValidateSystemRam (
     }
   }
 
-  //
-  // If the Root is NULL then its the first call. Lets initialize the List before
-  // we process the request.
-  //
-  if (mRootNode == NULL) {
-    mRootNode = SetPageStateChangeInitialize ();
-  }
-
   //
   // Check if the range is already validated
   //
-  EndAddress = BaseAddress + EFI_PAGES_TO_SIZE(NumPages);
-  Range = FindOverlapRange (mRootNode, BaseAddress, EndAddress);
+  Range = FindOverlapRange (RootNode, BaseAddress, EndAddress);
 
   //
   // Range is not validated
   if (Range == NULL) {
     SetPageStateInternal (BaseAddress, NumPages, SevSnpPagePrivate, TRUE);
-    AddRangeToIntervalTree (mRootNode, BaseAddress, EndAddress);
+    AddRangeToIntervalTree (RootNode, BaseAddress, EndAddress);
     return;
   }
 
@@ -110,12 +118,12 @@ SevSnpValidateSystemRam (
   if (BaseAddress < Range->StartAddress) {
     NumPages = EFI_SIZE_TO_PAGES (Range->StartAddress - BaseAddress);
     SetPageStateInternal (BaseAddress, NumPages, SevSnpPagePrivate, TRUE);
-    AddRangeToIntervalTree (mRootNode, BaseAddress, Range->StartAddress);
+    AddRangeToIntervalTree (RootNode, BaseAddress, Range->StartAddress);
   }
 
   if (EndAddress > Range->EndAddress) {
     NumPages = EFI_SIZE_TO_PAGES (EndAddress - Range->EndAddress);
     SetPageStateInternal (Range->EndAddress, NumPages, SevSnpPagePrivate, TRUE);
-    AddRangeToIntervalTree (mRootNode, Range->StartAddress, EndAddress);
+    AddRangeToIntervalTree (RootNode, Range->StartAddress, EndAddress);
   }
 }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (18 preceding siblings ...)
  2021-03-24 15:32 ` [RFC PATCH 19/19] OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region Brijesh Singh
@ 2021-03-24 19:14 ` Laszlo Ersek
  2021-04-08  9:58 ` Laszlo Ersek
  20 siblings, 0 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-03-24 19:14 UTC (permalink / raw)
  To: devel, brijesh.singh
  Cc: James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky, Jordan Justen,
	Ard Biesheuvel

On 03/24/21 16:31, Brijesh Singh wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
> 
> SEV-SNP builds upon existing SEV and SEV-ES functionality while adding
> new hardware-based memory protections. SEV-SNP adds strong memory integrity
> protection to help prevent malicious hypervisor-based attacks like data
> replay, memory re-mapping and more in order to create an isolated memory
> encryption environment.
>  
> This series provides the basic building blocks to support booting the SEV-SNP
> VMs, it does not cover all the security enhancement introduced by the SEV-SNP
> such as interrupt protection.

Thanks, Brijesh. I'm adding the series to my review queue. Due to some
PTO coming up, I'll probably start reviewing this work only in April.
Other reviewers, please feel free to have at it.

Cheers
Laszlo

> 
> Many of the integrity guarantees of SEV-SNP are enforced through a new
> structure called the Reverse Map Table (RMP). Adding a new page to SEV-SNP
> VM requires a 2-step process. First, the hypervisor assigns a page to the
> guest using the new RMPUPDATE instruction. This transitions the page to
> guest-invalid. Second, the guest validates the page using the new PVALIDATE
> instruction. The SEV-SNP VMs can use the new "Page State Change Request NAE"
> defined in the GHCB specification to ask hypervisor to add or remove page
> from the RMP table.
>  
> Each page assigned to the SEV-SNP VM can either be validated or unvalidated,
> as indicated by the Validated flag in the page's RMP entry. There are two
> approaches that can be taken for the page validation: Pre-validation and
> Lazy Validation.
>   
> Under pre-validation, the pages are validated prior to first use. And under
> lazy validation, pages are validated when first accessed. An access to a
> unvalidated page results in a #VC exception, at which time the exception
> handler may validate the page. Lazy validation requires careful tracking of
> the validated pages to avoid validating the same GPA more than once. The
> recently introduced "Unaccepted" memory type can be used to communicate the
> unvalidated memory ranges to the Guest OS.
> 
> At this time we only support the pre-validation. OVMF detects all the available
> system RAM in the PEI phase. When SEV-SNP is enabled, the memory is validated
> before it is made available to the EDK2 core.
> 
> This series does not implements the following SEV-SNP features yet:
> 
> * CPUID filtering
> * AP bring up using the new SEV-SNP NAE
> * Lazy validation
> * Interrupt security
> 
> The series is based on commit:
> e542e05d4f UefiCpuPkg/SmmCpuFeaturesLib: Abstract PcdCpuMaxLogicalProcessorNumber
> 
> Additional resources
> ---------------------
> SEV-SNP whitepaper
> https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf
>  
> APM 2: https://www.amd.com/system/files/TechDocs/24593.pdf (section 15.36)
> 
> The complete source is available at
> https://github.com/AMDESE/ovmf/tree/sev-snp-rfc-1
> 
> GHCB spec v2:
>   The draft specification is posted on AMD-SEV-SNP mailing list:
>    https://lists.suse.com/mailman/private/amd-sev-snp/
> 
>   Copy of the spec is also available at 
>   https://github.com/AMDESE/AMDSEV/blob/sev-snp-devel/docs/56421-Guest_Hypervisor_Communication_Block_Standardization.pdf
> 
> GHCB spec v1:
> SEV-SNP firmware specification:
>  https://developer.amd.com/sev/
>   
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> 
> Brijesh Singh (19):
>   OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
>   OvmfPkg: validate the data pages used in the SEC phase
>   MdePkg: Expand the SEV MSR to include the SNP definition
>   OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled()
>   MdePkg: Define the GHCB GPA structure
>   UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is
>     enabled
>   OvmfPkg: Add a library to support registering GHCB GPA
>   OvmfPkg: register GHCB gpa for the SEV-SNP guest
>   MdePkg: Add AsmPvalidate() support
>   OvmfPkg: Define the Page State Change VMGEXIT structures
>   OvmfPkg/ResetVector: Invalidate the GHCB page
>   OvmfPkg/MemEncryptSevLib: Add support to validate system RAM
>   OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase
>   OvmfPkg/MemEncryptSevLib: Add support to validate RAM in PEI phase
>   OvmfPkg/PlatformPei: Validate the system RAM when SNP is active
>   OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI
>     phase
>   OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase
>   OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc
>     attribute
>   OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region
> 
>  MdePkg/Include/Library/BaseLib.h              |  37 +++
>  MdePkg/Include/Register/Amd/Fam17Msr.h        |  31 ++-
>  MdePkg/Include/Register/Amd/Ghcb.h            |  39 ++-
>  MdePkg/Library/BaseLib/BaseLib.inf            |   1 +
>  MdePkg/Library/BaseLib/X64/Pvalidate.nasm     |  43 +++
>  OvmfPkg/Include/Library/GhcbRegisterLib.h     |  27 ++
>  OvmfPkg/Include/Library/MemEncryptSevLib.h    |  30 +++
>  .../DxeMemEncryptSevLib.inf                   |   7 +
>  .../DxeMemEncryptSevLibInternal.c             |  27 ++
>  .../Ia32/SnpPageStateChange.c                 |  17 ++
>  .../PeiMemEncryptSevLib.inf                   |   9 +
>  .../PeiMemEncryptSevLibInternal.c             |  47 ++++
>  .../SecMemEncryptSevLib.inf                   |   4 +
>  .../SecMemEncryptSevLibInternal.c             |  39 +++
>  .../BaseMemEncryptSevLib/SnpPageStateChange.h |  37 +++
>  .../X64/PeiDxeSnpSetPageState.c               |  63 +++++
>  .../X64/PeiDxeVirtualMemory.c                 | 151 ++++++++++-
>  .../X64/PeiSnpSystemRamValidate.c             | 129 +++++++++
>  .../X64/SecSnpSystemRamValidate.c             |  23 ++
>  .../X64/SnpPageStateChangeInternal.c          | 254 ++++++++++++++++++
>  .../X64/SnpPageStateTrack.c                   | 119 ++++++++
>  .../X64/SnpPageStateTrack.h                   |  36 +++
>  .../X64/SnpSetPageState.h                     |  27 ++
>  .../BaseMemEncryptSevLib/X64/VirtualMemory.h  |  19 ++
>  .../Library/GhcbRegisterLib/GhcbRegisterLib.c |  97 +++++++
>  .../GhcbRegisterLib/GhcbRegisterLib.inf       |  33 +++
>  OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf  |   4 +
>  OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |   7 +
>  OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c |  45 ++++
>  OvmfPkg/OvmfPkg.dec                           |  12 +
>  OvmfPkg/OvmfPkgX64.dsc                        |   1 +
>  OvmfPkg/OvmfPkgX64.fdf                        |  33 ++-
>  OvmfPkg/PlatformPei/AmdSev.c                  |  52 ++++
>  OvmfPkg/PlatformPei/PlatformPei.inf           |   2 +
>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm  |  24 ++
>  OvmfPkg/ResetVector/Ia32/PageTables64.asm     | 106 ++++++++
>  OvmfPkg/ResetVector/ResetVector.inf           |   5 +
>  OvmfPkg/ResetVector/ResetVector.nasmb         |   4 +
>  OvmfPkg/Sec/SecMain.c                         | 102 +++++++
>  OvmfPkg/Sec/SecMain.inf                       |   2 +
>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   1 +
>  UefiCpuPkg/Library/MpInitLib/MpEqu.inc        |   1 +
>  UefiCpuPkg/Library/MpInitLib/MpLib.c          |   2 +
>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |   2 +
>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   1 +
>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm |  51 ++++
>  UefiCpuPkg/UefiCpuPkg.dec                     |   6 +
>  47 files changed, 1790 insertions(+), 19 deletions(-)
>  create mode 100644 MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>  create mode 100644 OvmfPkg/Include/Library/GhcbRegisterLib.h
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
>  create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c
>  create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 18/19] OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc attribute
  2021-03-24 15:32 ` [RFC PATCH 18/19] OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc attribute Brijesh Singh
@ 2021-03-24 20:07   ` Brijesh Singh
  0 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-03-24 20:07 UTC (permalink / raw)
  To: devel
  Cc: brijesh.singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek


On 3/24/21 10:32 AM, Brijesh Singh wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
>
> The MemEncryptSev{Set,Clear}PageEncMask() functions are used to set or
> clear the memory encryption attribute in the page table. When SEV-SNP is
> active, we also need to validate or invalidate the pages and update the
> RMP entry.
>
> Before clearing the encryption attribute we need to invalidate the memory,
> and then make the page shared in the RMP entry. Similarly, after setting
> the encryption attribute in the page table, we add the memory as private
> in the RMP entry and validate it.
>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf     |  3 ++
>  OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf     |  1 +
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c | 32 +++++++++++++++++
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c   | 36 ++++++++++++++++++--
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h       | 27 +++++++++++++++
>  5 files changed, 97 insertions(+), 2 deletions(-)
>
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
> index f2e162d680..fa8f7719a7 100644
> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
> @@ -36,6 +36,8 @@
>  [Sources.X64]
>    X64/MemEncryptSevLib.c
>    X64/PeiDxeVirtualMemory.c
> +  X64/SnpPageStateChangeInternal.c
> +  X64/PeiDxeSnpSetPageState.c
>    X64/VirtualMemory.c
>    X64/VirtualMemory.h
>  
> @@ -49,6 +51,7 @@
>    DebugLib
>    MemoryAllocationLib
>    PcdLib
> +  VmgExitLib
>  
>  [FeaturePcd]
>    gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
> index cb9dd2bb21..d16ec44954 100644
> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
> @@ -37,6 +37,7 @@
>  [Sources.X64]
>    X64/MemEncryptSevLib.c
>    X64/PeiDxeVirtualMemory.c
> +  X64/PeiDxeSnpSetPageState.c
>    X64/PeiSnpSystemRamValidate.c
>    X64/SnpPageStateTrack.c
>    X64/SnpPageStateChangeInternal.c
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
> new file mode 100644
> index 0000000000..0a3d58ac22
> --- /dev/null
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
> @@ -0,0 +1,32 @@
> +/** @file
> +
> +  SEV-SNP Page Validation functions.
> +
> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Uefi/UefiBaseType.h>
> +#include <Library/BaseLib.h>
> +
> +#include "PeiSnpPageStateChange.h"


There is one minor error in this hunk, Please ignore it during your
reviews. I will fix it in next version.

The include should be "../SnpPageStateChange.h" and not
"PeiSnppageStateChange.h".


> +
> +VOID
> +SnpSetMemoryPrivate (
> +  IN  PHYSICAL_ADDRESS      PhysicalAddress,
> +  IN  UINTN                 Length
> +  )
> +{
> +  SetPageStateInternal (PhysicalAddress, EFI_SIZE_TO_PAGES (Length), SevSnpPagePrivate, FALSE);
> +}
> +
> +VOID
> +SnpSetMemoryShared (
> +  IN  PHYSICAL_ADDRESS      PhysicalAddress,
> +  IN  UINTN                 Length
> +  )
> +{
> +  SetPageStateInternal (PhysicalAddress, EFI_SIZE_TO_PAGES (Length), SevSnpPageShared, FALSE);
> +}
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
> index 33d9bafe9f..26d363d427 100644
> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
> @@ -17,6 +17,7 @@
>  #include <Register/Cpuid.h>
>  
>  #include "VirtualMemory.h"
> +#include "SnpSetPageState.h"
>  
>  STATIC BOOLEAN mAddressEncMaskChecked = FALSE;
>  STATIC UINT64  mAddressEncMask;
> @@ -700,22 +701,34 @@ SetMemoryEncDec (
>    UINT64                         AddressEncMask;
>    BOOLEAN                        IsWpEnabled;
>    RETURN_STATUS                  Status;
> +  BOOLEAN                        NeedPageStateChange;
> +  PHYSICAL_ADDRESS               OrigPhysicalAddress;
> +  UINTN                          OrigLength;
>  
>    //
>    // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.
>    //
>    PageMapLevel4Entry = NULL;
>  
> +  //
> +  // When SEV-SNP is active, before clearing the encryption attribute from
> +  // the page table we also need to update the RMP entry for the memory
> +  // region to make the region shared. And after setting the encryption
> +  // attribute, the region must be made private in the RMP table.
> +  //
> +  NeedPageStateChange = MemEncryptSevSnpIsEnabled ();
> +
>    DEBUG ((
>      DEBUG_VERBOSE,
> -    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u\n",
> +    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u Rmpupdate=%u\n",
>      gEfiCallerBaseName,
>      __FUNCTION__,
>      Cr3BaseAddress,
>      PhysicalAddress,
>      (UINT64)Length,
>      (Mode == SetCBit) ? "Encrypt" : "Decrypt",
> -    (UINT32)CacheFlush
> +    (UINT32)CacheFlush,
> +    (UINT32)NeedPageStateChange
>      ));
>  
>    //
> @@ -749,6 +762,18 @@ SetMemoryEncDec (
>      DisableReadOnlyPageWriteProtect ();
>    }
>  
> +  //
> +  // Make the RMP updates before clearing the encryption attribute in the page table.
> +  //
> +  if (NeedPageStateChange && (Mode == ClearCBit)) {
> +    SnpSetMemoryShared (PhysicalAddress, Length);
> +  }
> +
> +  //
> +  // Save the values, we need it later during the Page state change.
> +  //
> +  OrigPhysicalAddress = PhysicalAddress;
> +  OrigLength = Length;
>    Status = EFI_SUCCESS;
>  
>    while (Length != 0)
> @@ -923,6 +948,13 @@ SetMemoryEncDec (
>    //
>    CpuFlushTlb();
>  
> +  //
> +  // Make the RMP updates after setting the encryption attribute in the page table.
> +  //
> +  if (NeedPageStateChange && (Mode == SetCBit)) {
> +    SnpSetMemoryPrivate (OrigPhysicalAddress, OrigLength);
> +  }
> +
>  Done:
>    //
>    // Restore page table write protection, if any.
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
> new file mode 100644
> index 0000000000..0b29bad612
> --- /dev/null
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
> @@ -0,0 +1,27 @@
> +/** @file
> +
> +  SEV-SNP Page Validation functions.
> +
> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef PEI_DXE_SNP_PAGE_STATE_INTERNAL_H_
> +#define PEI_DXE_SNP_PAGE_STATE_INTERNAL_H_
> +
> +#include "../SnpPageStateChange.h"
> +
> +VOID
> +SnpSetMemoryPrivate (
> +  IN  PHYSICAL_ADDRESS      PhysicalAddress,
> +  IN  UINTN                 Length
> +  );
> +
> +VOID
> +SnpSetMemoryShared (
> +  IN  PHYSICAL_ADDRESS      PhysicalAddress,
> +  IN  UINTN                 Length
> +  );
> +#endif

^ permalink raw reply	[flat|nested] 68+ messages in thread

* 回复: [edk2-devel] [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support
  2021-03-24 15:32 ` [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support Brijesh Singh
@ 2021-03-25  2:49   ` gaoliming
  2021-03-25 10:54     ` Brijesh Singh
  0 siblings, 1 reply; 68+ messages in thread
From: gaoliming @ 2021-03-25  2:49 UTC (permalink / raw)
  To: devel, brijesh.singh
  Cc: 'James Bottomley', 'Min Xu', 'Jiewen Yao',
	'Tom Lendacky', 'Jordan Justen',
	'Ard Biesheuvel', 'Laszlo Ersek'

Is this API X64 only? Or IA32 and X64 both?

Thanks
Liming
> -----邮件原件-----
> 发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 Brijesh Singh
> 发送时间: 2021年3月24日 23:32
> 收件人: devel@edk2.groups.io
> 抄送: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
> <jejb@linux.ibm.com>; Min Xu <min.m.xu@intel.com>; Jiewen Yao
> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>;
> Jordan Justen <jordan.l.justen@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
> 主题: [edk2-devel] [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
> 
> The PVALIDATE instruction validates or rescinds validation of a guest
> page RMP entry. Upon completion, a return code is stored in EAX, rFLAGS
> bits OF, ZF, AF, PF and SF are set based on this return code. If the
> instruction completed succesfully, the rFLAGS bit CF indicates if the
> contents of the RMP entry were changed or not.
> 
> For more information about the instruction see AMD APM volume 3.
> 
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  MdePkg/Include/Library/BaseLib.h          | 37 +++++++++++++++++
>  MdePkg/Library/BaseLib/BaseLib.inf        |  1 +
>  MdePkg/Library/BaseLib/X64/Pvalidate.nasm | 43 ++++++++++++++++++++
>  3 files changed, 81 insertions(+)
> 
> diff --git a/MdePkg/Include/Library/BaseLib.h
> b/MdePkg/Include/Library/BaseLib.h
> index 1171a0ffb5..fee27e9a1b 100644
> --- a/MdePkg/Include/Library/BaseLib.h
> +++ b/MdePkg/Include/Library/BaseLib.h
> @@ -7495,5 +7495,42 @@ PatchInstructionX86 (
>    IN  UINTN                    ValueSize
>    );
> 
> +/**
> + Execute a PVALIDATE instruction to validate or rescnids validation of a
guest
> + page's RMP entry.
> +
> + Upon completion, in addition to the return value the instruction also
> updates
> + the eFlags. A caller must check both the return code as well as eFlags
to
> + determine if the RMP entry has been updated.
> +
> + The function is available on x64.
> +
> + @param[in]    Address        The guest virtual address to validate.
> + @param[in]    PageSize       The page size to use.
> + @param[i]     Validate       Validate or rescinds.
> + @param[out]   Eflags         The value of Eflags after PVALIDATE
> completion.
> +
> + @retval       PvalidateRetValue  The return value from the PVALIDATE
> instruction.
> +**/
> +typedef enum {
> +  PVALIDATE_PAGE_SIZE_4K = 0,
> +  PVALIDATE_PAGE_SIZE_2M,
> +} PvalidatePageSize;
> +
> +typedef enum {
> +  PVALIDATE_RET_SUCCESS = 0,
> +  PVALIDATE_RET_FAIL_INPUT = 1,
> +  PVALIDATE_RET_FAIL_SIZEMISMATCH = 6,
> +} PvalidateRetValue;
> +
> +PvalidateRetValue
> +EFIAPI
> +AsmPvalidate (
> +  IN   PvalidatePageSize       PageSize,
> +  IN   BOOLEAN                 Validate,
> +  IN   UINTN                   Address,
> +  OUT  IA32_EFLAGS32           *Eflags
> +  );
> +
>  #endif // defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
>  #endif // !defined (__BASE_LIB__)
> diff --git a/MdePkg/Library/BaseLib/BaseLib.inf
> b/MdePkg/Library/BaseLib/BaseLib.inf
> index 3b85c56c3c..01aa5cc7a4 100644
> --- a/MdePkg/Library/BaseLib/BaseLib.inf
> +++ b/MdePkg/Library/BaseLib/BaseLib.inf
> @@ -319,6 +319,7 @@
>    X64/RdRand.nasm
>    X64/XGetBv.nasm
>    X64/VmgExit.nasm
> +  X64/Pvalidate.nasm
>    ChkStkGcc.c  | GCC
> 
>  [Sources.EBC]
> diff --git a/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
> b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
> new file mode 100644
> index 0000000000..f2aba114ac
> --- /dev/null
> +++ b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
> @@ -0,0 +1,43 @@
>
+;--------------------------------------------------------------------------
---
> +;
> +; Copyright (c) 2020-2021, AMD. All rights reserved.<BR>
> +; SPDX-License-Identifier: BSD-2-Clause-Patent
> +;
> +; Module Name:
> +;
> +;   Pvalidate.Asm
> +;
> +; Abstract:
> +;
> +;   AsmPvalidate function
> +;
> +; Notes:
> +;
>
+;--------------------------------------------------------------------------
---
> +
> +    SECTION .text
> +
>
+;--------------------------------------------------------------------------
---
> +;  PvalidateRetValue
> +;  EFIAPI
> +;  AsmPvalidate (
> +;    IN   UINT32  RmpPageSize
> +;    IN   UINT32  Validate,
> +;    IN   UINTN   Address,
> +;    OUT  UINTN  *Eflags,
> +;    )
>
+;--------------------------------------------------------------------------
---
> +global ASM_PFX(AsmPvalidate)
> +ASM_PFX(AsmPvalidate):
> +  mov     rax, r8
> +
> +  ; PVALIDATE instruction opcode
> +  DB      0xF2, 0x0F, 0x01, 0xFF
> +
> +  ; Read the Eflags
> +  pushfq
> +  pop     r8
> +  mov     [r9], r8
> +
> +  ; The PVALIDATE instruction returns the status in rax register.
> +  ret
> --
> 2.17.1
> 
> 
> 
> 
> 




^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: 回复: [edk2-devel] [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support
  2021-03-25  2:49   ` 回复: [edk2-devel] " gaoliming
@ 2021-03-25 10:54     ` Brijesh Singh
  2021-03-26 20:02       ` Andrew Fish
  0 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-03-25 10:54 UTC (permalink / raw)
  To: gaoliming, devel
  Cc: brijesh.singh, 'James Bottomley', 'Min Xu',
	'Jiewen Yao', 'Tom Lendacky',
	'Jordan Justen', 'Ard Biesheuvel',
	'Laszlo Ersek'

On 3/24/21 9:49 PM, gaoliming wrote:
> Is this API X64 only? Or IA32 and X64 both?


Theoretically the instruction is available on both IA32 and X64 but its
used only in X64. AMD SEV, SEV-ES and SEV-SNP support is available for
X64 arch only. I was not sure if the EDK2 community is okay with the
dead-code. Do you think it still makes sense to add the IA32 API for it ?


>
> Thanks
> Liming
>> -----邮件原件-----
>> 发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 Brijesh Singh
>> 发送时间: 2021年3月24日 23:32
>> 收件人: devel@edk2.groups.io
>> 抄送: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
>> <jejb@linux.ibm.com>; Min Xu <min.m.xu@intel.com>; Jiewen Yao
>> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>;
>> Jordan Justen <jordan.l.justen@intel.com>; Ard Biesheuvel
>> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
>> 主题: [edk2-devel] [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support
>>
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D3275&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C125d11ea64cf4f4ecd2108d8ef38a8e7%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637522373939810930%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=f3fM%2Fnw3X1lHhf7SPKTxDOLo0GcnU465yvyf0IIyD80%3D&amp;reserved=0
>>
>> The PVALIDATE instruction validates or rescinds validation of a guest
>> page RMP entry. Upon completion, a return code is stored in EAX, rFLAGS
>> bits OF, ZF, AF, PF and SF are set based on this return code. If the
>> instruction completed succesfully, the rFLAGS bit CF indicates if the
>> contents of the RMP entry were changed or not.
>>
>> For more information about the instruction see AMD APM volume 3.
>>
>> Cc: James Bottomley <jejb@linux.ibm.com>
>> Cc: Min Xu <min.m.xu@intel.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Tom Lendacky <thomas.lendacky@amd.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>> ---
>>  MdePkg/Include/Library/BaseLib.h          | 37 +++++++++++++++++
>>  MdePkg/Library/BaseLib/BaseLib.inf        |  1 +
>>  MdePkg/Library/BaseLib/X64/Pvalidate.nasm | 43 ++++++++++++++++++++
>>  3 files changed, 81 insertions(+)
>>
>> diff --git a/MdePkg/Include/Library/BaseLib.h
>> b/MdePkg/Include/Library/BaseLib.h
>> index 1171a0ffb5..fee27e9a1b 100644
>> --- a/MdePkg/Include/Library/BaseLib.h
>> +++ b/MdePkg/Include/Library/BaseLib.h
>> @@ -7495,5 +7495,42 @@ PatchInstructionX86 (
>>    IN  UINTN                    ValueSize
>>    );
>>
>> +/**
>> + Execute a PVALIDATE instruction to validate or rescnids validation of a
> guest
>> + page's RMP entry.
>> +
>> + Upon completion, in addition to the return value the instruction also
>> updates
>> + the eFlags. A caller must check both the return code as well as eFlags
> to
>> + determine if the RMP entry has been updated.
>> +
>> + The function is available on x64.
>> +
>> + @param[in]    Address        The guest virtual address to validate.
>> + @param[in]    PageSize       The page size to use.
>> + @param[i]     Validate       Validate or rescinds.
>> + @param[out]   Eflags         The value of Eflags after PVALIDATE
>> completion.
>> +
>> + @retval       PvalidateRetValue  The return value from the PVALIDATE
>> instruction.
>> +**/
>> +typedef enum {
>> +  PVALIDATE_PAGE_SIZE_4K = 0,
>> +  PVALIDATE_PAGE_SIZE_2M,
>> +} PvalidatePageSize;
>> +
>> +typedef enum {
>> +  PVALIDATE_RET_SUCCESS = 0,
>> +  PVALIDATE_RET_FAIL_INPUT = 1,
>> +  PVALIDATE_RET_FAIL_SIZEMISMATCH = 6,
>> +} PvalidateRetValue;
>> +
>> +PvalidateRetValue
>> +EFIAPI
>> +AsmPvalidate (
>> +  IN   PvalidatePageSize       PageSize,
>> +  IN   BOOLEAN                 Validate,
>> +  IN   UINTN                   Address,
>> +  OUT  IA32_EFLAGS32           *Eflags
>> +  );
>> +
>>  #endif // defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
>>  #endif // !defined (__BASE_LIB__)
>> diff --git a/MdePkg/Library/BaseLib/BaseLib.inf
>> b/MdePkg/Library/BaseLib/BaseLib.inf
>> index 3b85c56c3c..01aa5cc7a4 100644
>> --- a/MdePkg/Library/BaseLib/BaseLib.inf
>> +++ b/MdePkg/Library/BaseLib/BaseLib.inf
>> @@ -319,6 +319,7 @@
>>    X64/RdRand.nasm
>>    X64/XGetBv.nasm
>>    X64/VmgExit.nasm
>> +  X64/Pvalidate.nasm
>>    ChkStkGcc.c  | GCC
>>
>>  [Sources.EBC]
>> diff --git a/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>> b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>> new file mode 100644
>> index 0000000000..f2aba114ac
>> --- /dev/null
>> +++ b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>> @@ -0,0 +1,43 @@
>>
> +;--------------------------------------------------------------------------
> ---
>> +;
>> +; Copyright (c) 2020-2021, AMD. All rights reserved.<BR>
>> +; SPDX-License-Identifier: BSD-2-Clause-Patent
>> +;
>> +; Module Name:
>> +;
>> +;   Pvalidate.Asm
>> +;
>> +; Abstract:
>> +;
>> +;   AsmPvalidate function
>> +;
>> +; Notes:
>> +;
>>
> +;--------------------------------------------------------------------------
> ---
>> +
>> +    SECTION .text
>> +
>>
> +;--------------------------------------------------------------------------
> ---
>> +;  PvalidateRetValue
>> +;  EFIAPI
>> +;  AsmPvalidate (
>> +;    IN   UINT32  RmpPageSize
>> +;    IN   UINT32  Validate,
>> +;    IN   UINTN   Address,
>> +;    OUT  UINTN  *Eflags,
>> +;    )
>>
> +;--------------------------------------------------------------------------
> ---
>> +global ASM_PFX(AsmPvalidate)
>> +ASM_PFX(AsmPvalidate):
>> +  mov     rax, r8
>> +
>> +  ; PVALIDATE instruction opcode
>> +  DB      0xF2, 0x0F, 0x01, 0xFF
>> +
>> +  ; Read the Eflags
>> +  pushfq
>> +  pop     r8
>> +  mov     [r9], r8
>> +
>> +  ; The PVALIDATE instruction returns the status in rax register.
>> +  ret
>> --
>> 2.17.1
>>
>>
>>
>> 
>>
>
>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support
  2021-03-25 10:54     ` Brijesh Singh
@ 2021-03-26 20:02       ` Andrew Fish
  0 siblings, 0 replies; 68+ messages in thread
From: Andrew Fish @ 2021-03-26 20:02 UTC (permalink / raw)
  To: edk2-devel-groups-io, Brijesh Singh
  Cc: gaoliming, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

[-- Attachment #1: Type: text/plain, Size: 7625 bytes --]



> On Mar 25, 2021, at 3:54 AM, Brijesh Singh <brijesh.singh@amd.com> wrote:
> 
> On 3/24/21 9:49 PM, gaoliming wrote:
>> Is this API X64 only? Or IA32 and X64 both?
> 
> 
> Theoretically the instruction is available on both IA32 and X64 but its
> used only in X64. AMD SEV, SEV-ES and SEV-SNP support is available for
> X64 arch only. I was not sure if the EDK2 community is okay with the
> dead-code. Do you think it still makes sense to add the IA32 API for it ?
> 

If this is only implemented for MDE_CPU_X64 should it be only defined in BaseLIib.h for MDE_CPU_X64? vs. "#endif // defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)”?

I think today we may only have types in MDE_CPU_X64 only and all the lib functions are in "#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)”. So we should think about adding an IA32 function implementation or not defining the function to exist for MDE_CPU_IA32 in BaseLib.h?

What do other people think?

Thanks,

Andrew Fish

> 
>> 
>> Thanks
>> Liming
>>> -----邮件原件-----
>>> 发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 Brijesh Singh
>>> 发送时间: 2021年3月24日 23:32
>>> 收件人: devel@edk2.groups.io
>>> 抄送: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
>>> <jejb@linux.ibm.com>; Min Xu <min.m.xu@intel.com>; Jiewen Yao
>>> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>;
>>> Jordan Justen <jordan.l.justen@intel.com>; Ard Biesheuvel
>>> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
>>> 主题: [edk2-devel] [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support
>>> 
>>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D3275&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C125d11ea64cf4f4ecd2108d8ef38a8e7%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637522373939810930%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=f3fM%2Fnw3X1lHhf7SPKTxDOLo0GcnU465yvyf0IIyD80%3D&amp;reserved=0 <https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D3275&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C125d11ea64cf4f4ecd2108d8ef38a8e7%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637522373939810930%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=f3fM%2Fnw3X1lHhf7SPKTxDOLo0GcnU465yvyf0IIyD80%3D&amp;reserved=0>
>>> 
>>> The PVALIDATE instruction validates or rescinds validation of a guest
>>> page RMP entry. Upon completion, a return code is stored in EAX, rFLAGS
>>> bits OF, ZF, AF, PF and SF are set based on this return code. If the
>>> instruction completed succesfully, the rFLAGS bit CF indicates if the
>>> contents of the RMP entry were changed or not.
>>> 
>>> For more information about the instruction see AMD APM volume 3.
>>> 
>>> Cc: James Bottomley <jejb@linux.ibm.com <mailto:jejb@linux.ibm.com>>
>>> Cc: Min Xu <min.m.xu@intel.com <mailto:min.m.xu@intel.com>>
>>> Cc: Jiewen Yao <jiewen.yao@intel.com <mailto:jiewen.yao@intel.com>>
>>> Cc: Tom Lendacky <thomas.lendacky@amd.com <mailto:thomas.lendacky@amd.com>>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com <mailto:jordan.l.justen@intel.com>>
>>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org <mailto:ardb+tianocore@kernel.org>>
>>> Cc: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
>>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com <mailto:brijesh.singh@amd.com>>
>>> ---
>>> MdePkg/Include/Library/BaseLib.h          | 37 +++++++++++++++++
>>> MdePkg/Library/BaseLib/BaseLib.inf        |  1 +
>>> MdePkg/Library/BaseLib/X64/Pvalidate.nasm | 43 ++++++++++++++++++++
>>> 3 files changed, 81 insertions(+)
>>> 
>>> diff --git a/MdePkg/Include/Library/BaseLib.h
>>> b/MdePkg/Include/Library/BaseLib.h
>>> index 1171a0ffb5..fee27e9a1b 100644
>>> --- a/MdePkg/Include/Library/BaseLib.h
>>> +++ b/MdePkg/Include/Library/BaseLib.h
>>> @@ -7495,5 +7495,42 @@ PatchInstructionX86 (
>>>   IN  UINTN                    ValueSize
>>>   );
>>> 
>>> +/**
>>> + Execute a PVALIDATE instruction to validate or rescnids validation of a
>> guest
>>> + page's RMP entry.
>>> +
>>> + Upon completion, in addition to the return value the instruction also
>>> updates
>>> + the eFlags. A caller must check both the return code as well as eFlags
>> to
>>> + determine if the RMP entry has been updated.
>>> +
>>> + The function is available on x64.
>>> +
>>> + @param[in]    Address        The guest virtual address to validate.
>>> + @param[in]    PageSize       The page size to use.
>>> + @param[i]     Validate       Validate or rescinds.
>>> + @param[out]   Eflags         The value of Eflags after PVALIDATE
>>> completion.
>>> +
>>> + @retval       PvalidateRetValue  The return value from the PVALIDATE
>>> instruction.
>>> +**/
>>> +typedef enum {
>>> +  PVALIDATE_PAGE_SIZE_4K = 0,
>>> +  PVALIDATE_PAGE_SIZE_2M,
>>> +} PvalidatePageSize;
>>> +
>>> +typedef enum {
>>> +  PVALIDATE_RET_SUCCESS = 0,
>>> +  PVALIDATE_RET_FAIL_INPUT = 1,
>>> +  PVALIDATE_RET_FAIL_SIZEMISMATCH = 6,
>>> +} PvalidateRetValue;
>>> +
>>> +PvalidateRetValue
>>> +EFIAPI
>>> +AsmPvalidate (
>>> +  IN   PvalidatePageSize       PageSize,
>>> +  IN   BOOLEAN                 Validate,
>>> +  IN   UINTN                   Address,
>>> +  OUT  IA32_EFLAGS32           *Eflags
>>> +  );
>>> +
>>> #endif // defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
>>> #endif // !defined (__BASE_LIB__)
>>> diff --git a/MdePkg/Library/BaseLib/BaseLib.inf
>>> b/MdePkg/Library/BaseLib/BaseLib.inf
>>> index 3b85c56c3c..01aa5cc7a4 100644
>>> --- a/MdePkg/Library/BaseLib/BaseLib.inf
>>> +++ b/MdePkg/Library/BaseLib/BaseLib.inf
>>> @@ -319,6 +319,7 @@
>>>   X64/RdRand.nasm
>>>   X64/XGetBv.nasm
>>>   X64/VmgExit.nasm
>>> +  X64/Pvalidate.nasm
>>>   ChkStkGcc.c  | GCC
>>> 
>>> [Sources.EBC]
>>> diff --git a/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>>> b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>>> new file mode 100644
>>> index 0000000000..f2aba114ac
>>> --- /dev/null
>>> +++ b/MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>>> @@ -0,0 +1,43 @@
>>> 
>> +;--------------------------------------------------------------------------
>> ---
>>> +;
>>> +; Copyright (c) 2020-2021, AMD. All rights reserved.<BR>
>>> +; SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +;
>>> +; Module Name:
>>> +;
>>> +;   Pvalidate.Asm
>>> +;
>>> +; Abstract:
>>> +;
>>> +;   AsmPvalidate function
>>> +;
>>> +; Notes:
>>> +;
>>> 
>> +;--------------------------------------------------------------------------
>> ---
>>> +
>>> +    SECTION .text
>>> +
>>> 
>> +;--------------------------------------------------------------------------
>> ---
>>> +;  PvalidateRetValue
>>> +;  EFIAPI
>>> +;  AsmPvalidate (
>>> +;    IN   UINT32  RmpPageSize
>>> +;    IN   UINT32  Validate,
>>> +;    IN   UINTN   Address,
>>> +;    OUT  UINTN  *Eflags,
>>> +;    )
>>> 
>> +;--------------------------------------------------------------------------
>> ---
>>> +global ASM_PFX(AsmPvalidate)
>>> +ASM_PFX(AsmPvalidate):
>>> +  mov     rax, r8
>>> +
>>> +  ; PVALIDATE instruction opcode
>>> +  DB      0xF2, 0x0F, 0x01, 0xFF
>>> +
>>> +  ; Read the Eflags
>>> +  pushfq
>>> +  pop     r8
>>> +  mov     [r9], r8
>>> +
>>> +  ; The PVALIDATE instruction returns the status in rax register.
>>> +  ret
>>> --
>>> 2.17.1
>>> 
>>> 
>>> 
>>> 
>>> 
>> 
>> 
> 
> 
> 


[-- Attachment #2: Type: text/html, Size: 18681 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to validate system RAM
  2021-03-24 15:32 ` [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to validate system RAM Brijesh Singh
@ 2021-04-01  6:37   ` Yao, Jiewen
  2021-04-01 13:07     ` Brijesh Singh
  0 siblings, 1 reply; 68+ messages in thread
From: Yao, Jiewen @ 2021-04-01  6:37 UTC (permalink / raw)
  To: Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Xu, Min M, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel, Laszlo Ersek

Hi
Would you please clarify why the IA32 version implementation is empty?

Does it mean IA32 does not need validate?
Or IA32 should never call this function?

Anyway, I recommend to add some comment to describe it clearly.
If it should never be called, I recommend to add ASSERT(FALSE).


+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
@@ -0,0 +1,17 @@
+#include <Uefi/UefiBaseType.h>
+
+#include "../SnpPageStateChange.h"
+
+/**
+ The function is used to set the page state when SEV-SNP is active. The page state
+ transition consist of changing the page ownership in the RMP table, and using the
+ PVALIDATE instruction to update the Validated bit in RMP table.
+
+ */
+VOID
+SevSnpValidateSystemRamInternal (
+  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
+  IN UINTN                            NumPages
+  )
+{
+}

> -----Original Message-----
> From: Brijesh Singh <brijesh.singh@amd.com>
> Sent: Wednesday, March 24, 2021 11:32 PM
> To: devel@edk2.groups.io
> Cc: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
> <jejb@linux.ibm.com>; Xu, Min M <min.m.xu@intel.com>; Yao, Jiewen
> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
> Subject: [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to
> validate system RAM
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
> 
> Many of the integrity guarantees of SEV-SNP are enforced through the
> Reverse Map Table (RMP). Each RMP entry contains the GPA at which a
> particular page of DRAM should be mapped. The guest can request the
> hypervisor to add pages in the RMP table via the Page State Change VMGEXIT
> defined in the GHCB specification section 2.5.1 and 4.1.6. Inside each RMP
> entry is a Validated flag; this flag is automatically cleared to 0 by the
> CPU hardware when a new RMP entry is created for a guest. Each VM page
> can be either validated or invalidated, as indicated by the Validated
> flag in the RMP entry. Memory access to a private page that is not
> validated generates a #VC. A VM can use the PVALIDATE instruction to
> validate the private page before using it.
> 
> During the guest creation, the boot ROM memory is pre-validated by the
> AMD-SEV firmware. The MemEncryptSevSnpValidateSystemRam() can be called
> during the SEC and PEI phase to validate the detected system RAM.
> 
> One of the fields in the Page State Change NAE is the RMP page size. The
> page size input parameter indicates that either a 4KB or 2MB page should
> be used while adding the RMP entry. During the validation, when possible,
> the MemEncryptSevSnpValidateSystemRam() will use the 2MB entry. A
> hypervisor backing the memory may choose to use the different page size
> in the RMP entry. In those cases, the PVALIDATE instruction should return
> SIZEMISMATCH. If a SIZEMISMATCH is detected, then validate all 512-pages
> constituting a 2MB region.
> 
> Upon completion, the PVALIDATE instruction sets the rFLAGS.CF to 0 if
> instruction changed the RMP entry and to 1 if the instruction did not
> change the RMP entry. The rFlags.CF will be 1 only when a memory region
> is already validated. We should not double validate a memory
> as it could lead to a security compromise. If double validation is
> detected, terminate the boot.
> 
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  OvmfPkg/Include/Library/MemEncryptSevLib.h                            |  15 ++
>  OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c        |  17
> ++
>  OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf          |   4 +
>  OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c    |
> 20 ++
>  OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h             |  37
> +++
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c    |
> 23 ++
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c |
> 254 ++++++++++++++++++++
>  7 files changed, 370 insertions(+)
> 
> diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h
> b/OvmfPkg/Include/Library/MemEncryptSevLib.h
> index 03d9eda392..47d6802b61 100644
> --- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
> +++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
> @@ -215,4 +215,19 @@ MemEncryptSevGetAddressRangeState (
>    IN UINTN                    Length
>    );
> 
> +/**
> +  If SEV-SNP is active then set the page state of the specified virtual
> +  address range. This should be called in SEC and PEI phases only.
> +
> +  @param[in]  BaseAddress             Base address
> +  @param[in]  NumPages                Number of pages starting from the base
> address
> +
> +**/
> +VOID
> +EFIAPI
> +MemEncryptSevSnpValidateSystemRam (
> +  IN PHYSICAL_ADDRESS                   BaseAddress,
> +  IN UINTN                              NumPages
> +  );
> +
>  #endif // _MEM_ENCRYPT_SEV_LIB_H_
> diff --git
> a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
> b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
> new file mode 100644
> index 0000000000..dace5c0bcf
> --- /dev/null
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
> @@ -0,0 +1,17 @@
> +#include <Uefi/UefiBaseType.h>
> +
> +#include "../SnpPageStateChange.h"
> +
> +/**
> + The function is used to set the page state when SEV-SNP is active. The page
> state
> + transition consist of changing the page ownership in the RMP table, and using
> the
> + PVALIDATE instruction to update the Validated bit in RMP table.
> +
> + */
> +VOID
> +SevSnpValidateSystemRamInternal (
> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
> +  IN UINTN                            NumPages
> +  )
> +{
> +}
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
> b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
> index 279c38bfbc..8595e244c2 100644
> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
> @@ -31,15 +31,19 @@
> 
>  [Sources]
>    SecMemEncryptSevLibInternal.c
> +  SnpPageStateChange.h
> 
>  [Sources.X64]
>    X64/MemEncryptSevLib.c
>    X64/SecVirtualMemory.c
> +  X64/SecSnpSystemRamValidate.c
> +  X64/SnpPageStateChangeInternal.c
>    X64/VirtualMemory.c
>    X64/VirtualMemory.h
> 
>  [Sources.IA32]
>    Ia32/MemEncryptSevLib.c
> +  Ia32/SnpPageStateChange.c
> 
>  [LibraryClasses]
>    BaseLib
> diff --git
> a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
> b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
> index 69852779e2..35a222e75e 100644
> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
> +++
> b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
> @@ -17,6 +17,8 @@
>  #include <Register/Cpuid.h>
>  #include <Uefi/UefiBaseType.h>
> 
> +#include "SnpPageStateChange.h"
> +
>  /**
>    Reads and sets the status of SEV features.
> 
> @@ -172,3 +174,21 @@
> MemEncryptSevLocateInitialSmramSaveStateMapPages (
>  {
>    return RETURN_UNSUPPORTED;
>  }
> +
> +/**
> +  If SEV-SNP is active then set the page state of the specified virtual
> +  address range. This should be called in SEC and PEI phases only.
> +
> +  @param[in]  BaseAddress             Base address
> +  @param[in]  NumPages                Number of pages starting from the base
> address
> +
> +**/
> +VOID
> +EFIAPI
> +MemEncryptSevSnpValidateSystemRam (
> +  IN PHYSICAL_ADDRESS                   BaseAddress,
> +  IN UINTN                              NumPages
> +  )
> +{
> +  SevSnpValidateSystemRam (BaseAddress, NumPages);
> +}
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
> b/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
> new file mode 100644
> index 0000000000..3040930999
> --- /dev/null
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
> @@ -0,0 +1,37 @@
> +/** @file
> +
> +  SEV-SNP Page Validation functions.
> +
> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef SNP_PAGE_STATE_INTERNAL_H_
> +#define SNP_PAGE_STATE_INTERNAL_H_
> +
> +//
> +// SEV-SNP Page states
> +//
> +typedef enum {
> +  SevSnpPagePrivate,
> +  SevSnpPageShared,
> +
> +} SEV_SNP_PAGE_STATE;
> +
> +VOID
> +SevSnpValidateSystemRam (
> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
> +  IN UINTN                            NumPages
> +  );
> +
> +VOID
> +SetPageStateInternal (
> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
> +  IN UINTN                            NumPages,
> +  IN SEV_SNP_PAGE_STATE               State,
> +  IN BOOLEAN                          UseLargeEntry
> +  );
> +
> +#endif
> diff --git
> a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
> new file mode 100644
> index 0000000000..915706aad0
> --- /dev/null
> +++
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
> @@ -0,0 +1,23 @@
> +/** @file
> +
> +  SEV-SNP Page Validation functions.
> +
> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Uefi/UefiBaseType.h>
> +#include <Library/BaseLib.h>
> +
> +#include "../SnpPageStateChange.h"
> +
> +VOID
> +SevSnpValidateSystemRam (
> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
> +  IN UINTN                            NumPages
> +  )
> +{
> +  SetPageStateInternal (BaseAddress, NumPages, SevSnpPagePrivate, TRUE);
> +}
> diff --git
> a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
> new file mode 100644
> index 0000000000..5a34db33fe
> --- /dev/null
> +++
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
> @@ -0,0 +1,254 @@
> +/** @file
> +
> +  SEV-SNP Page Validation functions.
> +
> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Uefi/UefiBaseType.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/MemEncryptSevLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/VmgExitLib.h>
> +
> +#include <Register/Amd/Ghcb.h>
> +#include <Register/Amd/Msr.h>
> +
> +#include "../SnpPageStateChange.h"
> +
> +#define IS_ALIGNED(x, y)        ((((x) & (y - 1)) == 0))
> +#define PAGES_PER_LARGE_ENTRY   512
> +#define EFI_LARGE_PAGE          (EFI_PAGE_SIZE * PAGES_PER_LARGE_ENTRY)
> +
> +STATIC
> +UINTN
> +MemoryStateToGhcbOp (
> +  IN SEV_SNP_PAGE_STATE   State
> +  )
> +{
> +  UINTN Cmd;
> +
> +  switch (State) {
> +    case SevSnpPageShared: Cmd = SNP_PAGE_STATE_SHARED; break;
> +    case SevSnpPagePrivate: Cmd = SNP_PAGE_STATE_PRIVATE; break;
> +    default: ASSERT(0);
> +  }
> +
> +  return Cmd;
> +}
> +
> +STATIC
> +VOID
> +SnpPageStateFailureTerminate (
> +  VOID
> +  )
> +{
> +  MSR_SEV_ES_GHCB_REGISTER  Msr;
> +
> +  //
> +  // Use the GHCB MSR Protocol to request termination by the hypervisor
> +  //
> +  Msr.GhcbPhysicalAddress = 0;
> +  Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
> +  Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
> +  Msr.GhcbTerminate.ReasonCode = GHCB_TERMINATE_GHCB_GENERAL;
> +  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
> +
> +  AsmVmgExit ();
> +
> +  ASSERT (FALSE);
> +  CpuDeadLoop ();
> +}
> +
> +STATIC
> +UINTN
> +IssuePvalidate (
> +  IN    UINTN       Address,
> +  IN    UINTN       RmpPageSize,
> +  IN    BOOLEAN     Validate
> +  )
> +{
> +  IA32_EFLAGS32         EFlags;
> +  UINTN                 Ret;
> +
> +  Ret = AsmPvalidate (RmpPageSize, Validate, Address, &EFlags);
> +
> +  //
> +  // Check the rFlags.CF to verify that PVALIDATE updated the RMP
> +  // entry. If there was a no change in the RMP entry then we are
> +  // either double validating or invalidating the memory. This can
> +  // lead to a security compromise.
> +  //
> +  if (EFlags.Bits.CF) {
> +    DEBUG ((DEBUG_ERROR, "%a:%a: Double %a detected for address 0x%Lx\n",
> +           gEfiCallerBaseName,
> +           __FUNCTION__,
> +           Validate ? "Validate" : "Invalidate",
> +           Address));
> +    SnpPageStateFailureTerminate ();
> +  }
> +
> +  return Ret;
> +}
> +
> +/**
> + This function issues the PVALIDATE instruction to validate or invalidate the
> memory
> + range specified. If PVALIDATE returns size mismatch then it tries validating
> with
> + smaller page size.
> +
> + */
> +STATIC
> +VOID
> +PvalidateRange (
> +  IN  SNP_PAGE_STATE_CHANGE_INFO    *Info,
> +  IN  UINTN                         StartIndex,
> +  IN  UINTN                         EndIndex,
> +  IN  BOOLEAN                       Validate
> +  )
> +{
> +  UINTN         Address, RmpPageSize, Ret, i;
> +
> +  for (; StartIndex < EndIndex; StartIndex++) {
> +    Address = Info->Entry[StartIndex].GuestFrameNumber << EFI_PAGE_SHIFT;
> +    RmpPageSize = Info->Entry[StartIndex].PageSize;
> +
> +    Ret = IssuePvalidate (Address, RmpPageSize, Validate);
> +
> +    //
> +    // If we fail to validate due to size mismatch then try with the
> +    // smaller page size. This senario will occur if the backing page in
> +    // the RMP entry is 4K and we are validating it as a 2MB.
> +    //
> +    if ((Ret == PVALIDATE_RET_FAIL_SIZEMISMATCH) &&
> +        (RmpPageSize == PVALIDATE_PAGE_SIZE_2M)) {
> +      for (i = 0; i < PAGES_PER_LARGE_ENTRY; i++) {
> +
> +        Ret = IssuePvalidate (Address, PVALIDATE_PAGE_SIZE_4K, Validate);
> +        if (Ret) {
> +          break;
> +        }
> +
> +        Address = Address + EFI_PAGE_SIZE;
> +      }
> +    }
> +
> +    if (Ret) {
> +    DEBUG ((DEBUG_ERROR, "%a:%a: Failed to %a address 0x%Lx Error
> code %d\n",
> +           gEfiCallerBaseName,
> +           __FUNCTION__,
> +           Validate ? "Validate" : "Invalidate",
> +           Address,
> +           Ret));
> +      SnpPageStateFailureTerminate ();
> +    }
> +  }
> +}
> +
> +/**
> + The function is used to set the page state when SEV-SNP is active. The page
> state
> + transition consist of changing the page ownership in the RMP table, and using
> the
> + PVALIDATE instruction to update the Validated bit in RMP table.
> +
> + When the UseLargeEntry is set to TRUE, then use the large RMP entry.
> + */
> +VOID
> +SetPageStateInternal (
> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
> +  IN UINTN                            NumPages,
> +  IN SEV_SNP_PAGE_STATE               State,
> +  IN BOOLEAN                          UseLargeEntry
> +  )
> +{
> +  EFI_STATUS                      Status;
> +  GHCB                            *Ghcb;
> +  EFI_PHYSICAL_ADDRESS            NextAddress, EndAddress;
> +  MSR_SEV_ES_GHCB_REGISTER        Msr;
> +  BOOLEAN                         InterruptState;
> +  SNP_PAGE_STATE_CHANGE_INFO      *Info;
> +  UINTN                           i, RmpPageSize;
> +
> +  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
> +  Ghcb = Msr.Ghcb;
> +
> +  EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
> +
> +  DEBUG ((DEBUG_VERBOSE, "%a:%a Address 0x%Lx - 0x%Lx State = %a
> LargeEntry = %d\n",
> +          gEfiCallerBaseName,
> +          __FUNCTION__,
> +          BaseAddress,
> +          EndAddress,
> +          State == SevSnpPageShared ? "Shared" : "Private",
> +          UseLargeEntry));
> +
> +  for (; BaseAddress < EndAddress; BaseAddress = NextAddress) {
> +
> +    //
> +    // Initialize the GHCB and setup scratch sw to point to shared buffer.
> +    //
> +    VmgInit (Ghcb, &InterruptState);
> +    Info = (SNP_PAGE_STATE_CHANGE_INFO *) Ghcb->SharedBuffer;
> +
> +    SetMem (Info, sizeof (*Info), 0);
> +
> +    //
> +    // Build page state change buffer
> +    //
> +    for (i = 0; (EndAddress > BaseAddress) && i < SNP_PAGE_STATE_MAX_ENTRY;
> +          BaseAddress = NextAddress, i++) {
> +      //
> +      // Is this a 2MB aligned page? Check if we can use the Large RMP entry.
> +      //
> +      if (UseLargeEntry &&
> +          IS_ALIGNED (BaseAddress, EFI_LARGE_PAGE) &&
> +          ((EndAddress - BaseAddress) >> EFI_PAGE_SHIFT) >=
> PAGES_PER_LARGE_ENTRY) {
> +        RmpPageSize = PVALIDATE_PAGE_SIZE_2M;
> +        NextAddress = BaseAddress + EFI_LARGE_PAGE;
> +      } else {
> +        RmpPageSize = PVALIDATE_PAGE_SIZE_4K;
> +        NextAddress = BaseAddress + EFI_PAGE_SIZE;
> +      }
> +
> +      Info->Entry[i].GuestFrameNumber = BaseAddress >> EFI_PAGE_SHIFT;
> +      Info->Entry[i].PageSize = RmpPageSize;
> +      Info->Entry[i].Op = MemoryStateToGhcbOp (State);
> +      Info->Entry[i].CurrentPage = 0;
> +    }
> +
> +    Info->Header.CurrentEntry = 0;
> +    Info->Header.EndEntry = i - 1;
> +
> +    //
> +    // If the request page state change is shared then invalidate the pages before
> +    // adding the page in the RMP table.
> +    //
> +    if (State == SevSnpPageShared) {
> +      PvalidateRange (Info, 0, i, FALSE);
> +    }
> +
> +    //
> +    // Issue the VMGEXIT and retry if hypervisor failed to process all the entries.
> +    //
> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
> +    VmgSetOffsetValid (Ghcb, GhcbSwScratch);
> +    while (Info->Header.CurrentEntry <= Info->Header.EndEntry) {
> +      Status = VmgExit (Ghcb, SVM_EXIT_SNP_PAGE_STATE_CHANGE, 0, 0);
> +      if (EFI_ERROR (Status)) {
> +        SnpPageStateFailureTerminate ();
> +      }
> +    }
> +
> +    //
> +    // If the request page state change is shared then invalidate the pages before
> +    // adding the page in the RMP table.
> +    //
> +    if (State == SevSnpPagePrivate) {
> +      PvalidateRange (Info, 0, i, TRUE);
> +    }
> +
> +    VmgDone (Ghcb, InterruptState);
> +  }
> +}
> --
> 2.17.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 16/19] OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI phase
  2021-03-24 15:32 ` [RFC PATCH 16/19] OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI phase Brijesh Singh
@ 2021-04-01  6:43   ` Yao, Jiewen
  0 siblings, 0 replies; 68+ messages in thread
From: Yao, Jiewen @ 2021-04-01  6:43 UTC (permalink / raw)
  To: Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Xu, Min M, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel, Laszlo Ersek

I recommend to use SIZE_1GB to indicate size, instead of BIT30, for readability.

===========================
    if (Length <= BIT30) {
      Length = 0;
    } else {
      Length -= BIT30;
    }

    PhysicalAddress += BIT30;
===========================

Also 
===========================
    if (Length <= BIT30) {
      Length = 0;
===========================
Can be:
===========================
    if (Length <= BIT30) {
      break;
===========================

Thank you

> -----Original Message-----
> From: Brijesh Singh <brijesh.singh@amd.com>
> Sent: Wednesday, March 24, 2021 11:32 PM
> To: devel@edk2.groups.io
> Cc: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
> <jejb@linux.ibm.com>; Xu, Min M <min.m.xu@intel.com>; Yao, Jiewen
> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
> Subject: [RFC PATCH 16/19] OvmfPkg/MemEncryptSevLib: Add support to
> validate > 4GB memory in PEI phase
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
> 
> The initial page built during the SEC phase is used by the
> MemEncryptSevSnpValidateSystemRam() for the system RAM validation. The
> page validation process requires using the PVALIDATE instruction;  the
> instruction accepts a virtual address of the memory region that needs
> to be validated. If hardware encounters a page table walk failure
> (due to page-not-present) then it raises #GP.
> 
> The initial page table built in SEC phase address up to 4GB. Add an
> internal function to extend the page table to cover > 4GB. The function
> builds 1GB entries in the page table for access > 4GB. This will provide
> the support to call PVALIDATE instruction for the virtual address >
> 4GB in PEI phase.
> 
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c     | 115
> ++++++++++++++++++++
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c |
> 16 +++
>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h           |  19
> ++++
>  3 files changed, 150 insertions(+)
> 
> diff --git
> a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
> index d3455e812b..33d9bafe9f 100644
> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
> @@ -536,6 +536,121 @@ EnableReadOnlyPageWriteProtect (
>    AsmWriteCr0 (AsmReadCr0() | BIT16);
>  }
> 
> +RETURN_STATUS
> +EFIAPI
> +InternalMemEncryptSevCreateIdentityMap1G (
> +  IN    PHYSICAL_ADDRESS      Cr3BaseAddress,
> +  IN    PHYSICAL_ADDRESS      PhysicalAddress,
> +  IN    UINTN                 Length
> +  )
> +{
> +  PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
> +  PAGE_TABLE_1G_ENTRY            *PageDirectory1GEntry;
> +  UINT64                         PgTableMask;
> +  UINT64                         AddressEncMask;
> +  BOOLEAN                        IsWpEnabled;
> +  RETURN_STATUS                  Status;
> +
> +  //
> +  // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.
> +  //
> +  PageMapLevel4Entry = NULL;
> +
> +  DEBUG ((
> +    DEBUG_VERBOSE,
> +    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx\n",
> +    gEfiCallerBaseName,
> +    __FUNCTION__,
> +    Cr3BaseAddress,
> +    PhysicalAddress,
> +    (UINT64)Length
> +    ));
> +
> +  if (Length == 0) {
> +    return RETURN_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Check if we have a valid memory encryption mask
> +  //
> +  AddressEncMask = InternalGetMemEncryptionAddressMask ();
> +  if (!AddressEncMask) {
> +    return RETURN_ACCESS_DENIED;
> +  }
> +
> +  PgTableMask = AddressEncMask | EFI_PAGE_MASK;
> +
> +
> +  //
> +  // Make sure that the page table is changeable.
> +  //
> +  IsWpEnabled = IsReadOnlyPageWriteProtected ();
> +  if (IsWpEnabled) {
> +    DisableReadOnlyPageWriteProtect ();
> +  }
> +
> +  Status = EFI_SUCCESS;
> +
> +  while (Length)
> +  {
> +    //
> +    // If Cr3BaseAddress is not specified then read the current CR3
> +    //
> +    if (Cr3BaseAddress == 0) {
> +      Cr3BaseAddress = AsmReadCr3();
> +    }
> +
> +    PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask);
> +    PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress);
> +    if (!PageMapLevel4Entry->Bits.Present) {
> +      DEBUG ((
> +        DEBUG_ERROR,
> +        "%a:%a: bad PML4 for Physical=0x%Lx\n",
> +        gEfiCallerBaseName,
> +        __FUNCTION__,
> +        PhysicalAddress
> +        ));
> +      Status = RETURN_NO_MAPPING;
> +      goto Done;
> +    }
> +
> +    PageDirectory1GEntry = (VOID *)(
> +                             (PageMapLevel4Entry->Bits.PageTableBaseAddress <<
> +                              12) & ~PgTableMask
> +                             );
> +    PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress);
> +    if (!PageDirectory1GEntry->Bits.Present) {
> +      PageDirectory1GEntry->Bits.Present = 1;
> +      PageDirectory1GEntry->Bits.MustBe1 = 1;
> +      PageDirectory1GEntry->Bits.MustBeZero = 0;
> +      PageDirectory1GEntry->Bits.ReadWrite = 1;
> +      PageDirectory1GEntry->Uint64 |= (UINT64)PhysicalAddress |
> AddressEncMask;
> +    }
> +
> +    if (Length <= BIT30) {
> +      Length = 0;
> +    } else {
> +      Length -= BIT30;
> +    }
> +
> +    PhysicalAddress += BIT30;
> +  }
> +
> +  //
> +  // Flush TLB
> +  //
> +  CpuFlushTlb();
> +
> +Done:
> +  //
> +  // Restore page table write protection, if any.
> +  //
> +  if (IsWpEnabled) {
> +    EnableReadOnlyPageWriteProtect ();
> +  }
> +
> +  return Status;
> +}
> 
>  /**
>    This function either sets or clears memory encryption bit for the memory
> diff --git
> a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
> index ce8a05bb1f..41bf301efe 100644
> ---
> a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
> +++
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
> @@ -16,6 +16,7 @@
> 
>  #include "../SnpPageStateChange.h"
>  #include "SnpPageStateTrack.h"
> +#include "VirtualMemory.h"
> 
>  STATIC SNP_VALIDATED_RANGE     *mRootNode;
> 
> @@ -62,9 +63,24 @@ SevSnpValidateSystemRam (
>  {
>    UINTN                   EndAddress;
>    SNP_VALIDATED_RANGE     *Range;
> +  EFI_STATUS              Status;
> 
>    EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
> 
> +  //
> +  // The page table used in PEI can address up to 4GB memory. If we are asked
> to validate
> +  // a range above the 4GB, then create an identity mapping so that the
> PVALIDATE instruction
> +  // can execute correctly. If the page table entry is not present then PVALIDATE
> will
> +  // cause the #GP.
> +  //
> +  if (BaseAddress >= SIZE_4GB) {
> +    Status = InternalMemEncryptSevCreateIdentityMap1G (0, BaseAddress,
> +                  EFI_PAGES_TO_SIZE (NumPages));
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (FALSE);
> +    }
> +  }
> +
>    //
>    // If the Root is NULL then its the first call. Lets initialize the List before
>    // we process the request.
> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
> index 996f94f07e..829dc96a1d 100644
> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
> @@ -267,4 +267,23 @@ InternalMemEncryptSevGetAddressRangeState (
>    IN UINTN                    Length
>    );
> 
> +/**
> +  Create 1GB identity mapping for the specified virtual address range.
> +
> +  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use
> +                                      current CR3)
> +  @param[in]  VirtualAddress          Virtual address to check
> +  @param[in]  Length                  Length of virtual address range
> +
> +  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.
> +
> +**/
> +RETURN_STATUS
> +EFIAPI
> +InternalMemEncryptSevCreateIdentityMap1G (
> +  IN    PHYSICAL_ADDRESS      Cr3BaseAddress,
> +  IN    PHYSICAL_ADDRESS      PhysicalAddress,
> +  IN    UINTN                 Length
> +  );
> +
>  #endif
> --
> 2.17.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to validate system RAM
  2021-04-01  6:37   ` Yao, Jiewen
@ 2021-04-01 13:07     ` Brijesh Singh
  0 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-04-01 13:07 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io
  Cc: brijesh.singh, James Bottomley, Xu, Min M, Tom Lendacky,
	Justen, Jordan L, Ard Biesheuvel, Laszlo Ersek

Hi Yao,

On 4/1/21 1:37 AM, Yao, Jiewen wrote:
> Hi
> Would you please clarify why the IA32 version implementation is empty?
>
> Does it mean IA32 does not need validate?
> Or IA32 should never call this function?
>
> Anyway, I recommend to add some comment to describe it clearly.
> If it should never be called, I recommend to add ASSERT(FALSE).


AMD SEV, SEV-ES and SEV-SNP support is not available for the IA32
architecture. Nobody should be calling it. I will add an ASSERT().

Thanks

>
> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
> @@ -0,0 +1,17 @@
> +#include <Uefi/UefiBaseType.h>
> +
> +#include "../SnpPageStateChange.h"
> +
> +/**
> + The function is used to set the page state when SEV-SNP is active. The page state
> + transition consist of changing the page ownership in the RMP table, and using the
> + PVALIDATE instruction to update the Validated bit in RMP table.
> +
> + */
> +VOID
> +SevSnpValidateSystemRamInternal (
> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
> +  IN UINTN                            NumPages
> +  )
> +{
> +}
>
>> -----Original Message-----
>> From: Brijesh Singh <brijesh.singh@amd.com>
>> Sent: Wednesday, March 24, 2021 11:32 PM
>> To: devel@edk2.groups.io
>> Cc: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
>> <jejb@linux.ibm.com>; Xu, Min M <min.m.xu@intel.com>; Yao, Jiewen
>> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>; Justen,
>> Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
>> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
>> Subject: [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to
>> validate system RAM
>>
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D3275&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C599bf9c821c94c0241ed08d8f4d8b2a5%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637528558846316719%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=4Lq97rE69lPFAsGjYb44e3u2mA9QlrJdayxTP9FG7w8%3D&amp;reserved=0
>>
>> Many of the integrity guarantees of SEV-SNP are enforced through the
>> Reverse Map Table (RMP). Each RMP entry contains the GPA at which a
>> particular page of DRAM should be mapped. The guest can request the
>> hypervisor to add pages in the RMP table via the Page State Change VMGEXIT
>> defined in the GHCB specification section 2.5.1 and 4.1.6. Inside each RMP
>> entry is a Validated flag; this flag is automatically cleared to 0 by the
>> CPU hardware when a new RMP entry is created for a guest. Each VM page
>> can be either validated or invalidated, as indicated by the Validated
>> flag in the RMP entry. Memory access to a private page that is not
>> validated generates a #VC. A VM can use the PVALIDATE instruction to
>> validate the private page before using it.
>>
>> During the guest creation, the boot ROM memory is pre-validated by the
>> AMD-SEV firmware. The MemEncryptSevSnpValidateSystemRam() can be called
>> during the SEC and PEI phase to validate the detected system RAM.
>>
>> One of the fields in the Page State Change NAE is the RMP page size. The
>> page size input parameter indicates that either a 4KB or 2MB page should
>> be used while adding the RMP entry. During the validation, when possible,
>> the MemEncryptSevSnpValidateSystemRam() will use the 2MB entry. A
>> hypervisor backing the memory may choose to use the different page size
>> in the RMP entry. In those cases, the PVALIDATE instruction should return
>> SIZEMISMATCH. If a SIZEMISMATCH is detected, then validate all 512-pages
>> constituting a 2MB region.
>>
>> Upon completion, the PVALIDATE instruction sets the rFLAGS.CF to 0 if
>> instruction changed the RMP entry and to 1 if the instruction did not
>> change the RMP entry. The rFlags.CF will be 1 only when a memory region
>> is already validated. We should not double validate a memory
>> as it could lead to a security compromise. If double validation is
>> detected, terminate the boot.
>>
>> Cc: James Bottomley <jejb@linux.ibm.com>
>> Cc: Min Xu <min.m.xu@intel.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Tom Lendacky <thomas.lendacky@amd.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>> ---
>>  OvmfPkg/Include/Library/MemEncryptSevLib.h                            |  15 ++
>>  OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c        |  17
>> ++
>>  OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf          |   4 +
>>  OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c    |
>> 20 ++
>>  OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h             |  37
>> +++
>>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c    |
>> 23 ++
>>  OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c |
>> 254 ++++++++++++++++++++
>>  7 files changed, 370 insertions(+)
>>
>> diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h
>> b/OvmfPkg/Include/Library/MemEncryptSevLib.h
>> index 03d9eda392..47d6802b61 100644
>> --- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
>> +++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
>> @@ -215,4 +215,19 @@ MemEncryptSevGetAddressRangeState (
>>    IN UINTN                    Length
>>    );
>>
>> +/**
>> +  If SEV-SNP is active then set the page state of the specified virtual
>> +  address range. This should be called in SEC and PEI phases only.
>> +
>> +  @param[in]  BaseAddress             Base address
>> +  @param[in]  NumPages                Number of pages starting from the base
>> address
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +MemEncryptSevSnpValidateSystemRam (
>> +  IN PHYSICAL_ADDRESS                   BaseAddress,
>> +  IN UINTN                              NumPages
>> +  );
>> +
>>  #endif // _MEM_ENCRYPT_SEV_LIB_H_
>> diff --git
>> a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
>> new file mode 100644
>> index 0000000000..dace5c0bcf
>> --- /dev/null
>> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
>> @@ -0,0 +1,17 @@
>> +#include <Uefi/UefiBaseType.h>
>> +
>> +#include "../SnpPageStateChange.h"
>> +
>> +/**
>> + The function is used to set the page state when SEV-SNP is active. The page
>> state
>> + transition consist of changing the page ownership in the RMP table, and using
>> the
>> + PVALIDATE instruction to update the Validated bit in RMP table.
>> +
>> + */
>> +VOID
>> +SevSnpValidateSystemRamInternal (
>> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
>> +  IN UINTN                            NumPages
>> +  )
>> +{
>> +}
>> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
>> index 279c38bfbc..8595e244c2 100644
>> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
>> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf
>> @@ -31,15 +31,19 @@
>>
>>  [Sources]
>>    SecMemEncryptSevLibInternal.c
>> +  SnpPageStateChange.h
>>
>>  [Sources.X64]
>>    X64/MemEncryptSevLib.c
>>    X64/SecVirtualMemory.c
>> +  X64/SecSnpSystemRamValidate.c
>> +  X64/SnpPageStateChangeInternal.c
>>    X64/VirtualMemory.c
>>    X64/VirtualMemory.h
>>
>>  [Sources.IA32]
>>    Ia32/MemEncryptSevLib.c
>> +  Ia32/SnpPageStateChange.c
>>
>>  [LibraryClasses]
>>    BaseLib
>> diff --git
>> a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
>> index 69852779e2..35a222e75e 100644
>> --- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
>> +++
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
>> @@ -17,6 +17,8 @@
>>  #include <Register/Cpuid.h>
>>  #include <Uefi/UefiBaseType.h>
>>
>> +#include "SnpPageStateChange.h"
>> +
>>  /**
>>    Reads and sets the status of SEV features.
>>
>> @@ -172,3 +174,21 @@
>> MemEncryptSevLocateInitialSmramSaveStateMapPages (
>>  {
>>    return RETURN_UNSUPPORTED;
>>  }
>> +
>> +/**
>> +  If SEV-SNP is active then set the page state of the specified virtual
>> +  address range. This should be called in SEC and PEI phases only.
>> +
>> +  @param[in]  BaseAddress             Base address
>> +  @param[in]  NumPages                Number of pages starting from the base
>> address
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +MemEncryptSevSnpValidateSystemRam (
>> +  IN PHYSICAL_ADDRESS                   BaseAddress,
>> +  IN UINTN                              NumPages
>> +  )
>> +{
>> +  SevSnpValidateSystemRam (BaseAddress, NumPages);
>> +}
>> diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
>> new file mode 100644
>> index 0000000000..3040930999
>> --- /dev/null
>> +++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
>> @@ -0,0 +1,37 @@
>> +/** @file
>> +
>> +  SEV-SNP Page Validation functions.
>> +
>> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#ifndef SNP_PAGE_STATE_INTERNAL_H_
>> +#define SNP_PAGE_STATE_INTERNAL_H_
>> +
>> +//
>> +// SEV-SNP Page states
>> +//
>> +typedef enum {
>> +  SevSnpPagePrivate,
>> +  SevSnpPageShared,
>> +
>> +} SEV_SNP_PAGE_STATE;
>> +
>> +VOID
>> +SevSnpValidateSystemRam (
>> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
>> +  IN UINTN                            NumPages
>> +  );
>> +
>> +VOID
>> +SetPageStateInternal (
>> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
>> +  IN UINTN                            NumPages,
>> +  IN SEV_SNP_PAGE_STATE               State,
>> +  IN BOOLEAN                          UseLargeEntry
>> +  );
>> +
>> +#endif
>> diff --git
>> a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
>> new file mode 100644
>> index 0000000000..915706aad0
>> --- /dev/null
>> +++
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
>> @@ -0,0 +1,23 @@
>> +/** @file
>> +
>> +  SEV-SNP Page Validation functions.
>> +
>> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Uefi/UefiBaseType.h>
>> +#include <Library/BaseLib.h>
>> +
>> +#include "../SnpPageStateChange.h"
>> +
>> +VOID
>> +SevSnpValidateSystemRam (
>> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
>> +  IN UINTN                            NumPages
>> +  )
>> +{
>> +  SetPageStateInternal (BaseAddress, NumPages, SevSnpPagePrivate, TRUE);
>> +}
>> diff --git
>> a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
>> new file mode 100644
>> index 0000000000..5a34db33fe
>> --- /dev/null
>> +++
>> b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
>> @@ -0,0 +1,254 @@
>> +/** @file
>> +
>> +  SEV-SNP Page Validation functions.
>> +
>> +  Copyright (c) 2020 - 2021, AMD Incorporated. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Uefi/UefiBaseType.h>
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/MemEncryptSevLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/VmgExitLib.h>
>> +
>> +#include <Register/Amd/Ghcb.h>
>> +#include <Register/Amd/Msr.h>
>> +
>> +#include "../SnpPageStateChange.h"
>> +
>> +#define IS_ALIGNED(x, y)        ((((x) & (y - 1)) == 0))
>> +#define PAGES_PER_LARGE_ENTRY   512
>> +#define EFI_LARGE_PAGE          (EFI_PAGE_SIZE * PAGES_PER_LARGE_ENTRY)
>> +
>> +STATIC
>> +UINTN
>> +MemoryStateToGhcbOp (
>> +  IN SEV_SNP_PAGE_STATE   State
>> +  )
>> +{
>> +  UINTN Cmd;
>> +
>> +  switch (State) {
>> +    case SevSnpPageShared: Cmd = SNP_PAGE_STATE_SHARED; break;
>> +    case SevSnpPagePrivate: Cmd = SNP_PAGE_STATE_PRIVATE; break;
>> +    default: ASSERT(0);
>> +  }
>> +
>> +  return Cmd;
>> +}
>> +
>> +STATIC
>> +VOID
>> +SnpPageStateFailureTerminate (
>> +  VOID
>> +  )
>> +{
>> +  MSR_SEV_ES_GHCB_REGISTER  Msr;
>> +
>> +  //
>> +  // Use the GHCB MSR Protocol to request termination by the hypervisor
>> +  //
>> +  Msr.GhcbPhysicalAddress = 0;
>> +  Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
>> +  Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
>> +  Msr.GhcbTerminate.ReasonCode = GHCB_TERMINATE_GHCB_GENERAL;
>> +  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
>> +
>> +  AsmVmgExit ();
>> +
>> +  ASSERT (FALSE);
>> +  CpuDeadLoop ();
>> +}
>> +
>> +STATIC
>> +UINTN
>> +IssuePvalidate (
>> +  IN    UINTN       Address,
>> +  IN    UINTN       RmpPageSize,
>> +  IN    BOOLEAN     Validate
>> +  )
>> +{
>> +  IA32_EFLAGS32         EFlags;
>> +  UINTN                 Ret;
>> +
>> +  Ret = AsmPvalidate (RmpPageSize, Validate, Address, &EFlags);
>> +
>> +  //
>> +  // Check the rFlags.CF to verify that PVALIDATE updated the RMP
>> +  // entry. If there was a no change in the RMP entry then we are
>> +  // either double validating or invalidating the memory. This can
>> +  // lead to a security compromise.
>> +  //
>> +  if (EFlags.Bits.CF) {
>> +    DEBUG ((DEBUG_ERROR, "%a:%a: Double %a detected for address 0x%Lx\n",
>> +           gEfiCallerBaseName,
>> +           __FUNCTION__,
>> +           Validate ? "Validate" : "Invalidate",
>> +           Address));
>> +    SnpPageStateFailureTerminate ();
>> +  }
>> +
>> +  return Ret;
>> +}
>> +
>> +/**
>> + This function issues the PVALIDATE instruction to validate or invalidate the
>> memory
>> + range specified. If PVALIDATE returns size mismatch then it tries validating
>> with
>> + smaller page size.
>> +
>> + */
>> +STATIC
>> +VOID
>> +PvalidateRange (
>> +  IN  SNP_PAGE_STATE_CHANGE_INFO    *Info,
>> +  IN  UINTN                         StartIndex,
>> +  IN  UINTN                         EndIndex,
>> +  IN  BOOLEAN                       Validate
>> +  )
>> +{
>> +  UINTN         Address, RmpPageSize, Ret, i;
>> +
>> +  for (; StartIndex < EndIndex; StartIndex++) {
>> +    Address = Info->Entry[StartIndex].GuestFrameNumber << EFI_PAGE_SHIFT;
>> +    RmpPageSize = Info->Entry[StartIndex].PageSize;
>> +
>> +    Ret = IssuePvalidate (Address, RmpPageSize, Validate);
>> +
>> +    //
>> +    // If we fail to validate due to size mismatch then try with the
>> +    // smaller page size. This senario will occur if the backing page in
>> +    // the RMP entry is 4K and we are validating it as a 2MB.
>> +    //
>> +    if ((Ret == PVALIDATE_RET_FAIL_SIZEMISMATCH) &&
>> +        (RmpPageSize == PVALIDATE_PAGE_SIZE_2M)) {
>> +      for (i = 0; i < PAGES_PER_LARGE_ENTRY; i++) {
>> +
>> +        Ret = IssuePvalidate (Address, PVALIDATE_PAGE_SIZE_4K, Validate);
>> +        if (Ret) {
>> +          break;
>> +        }
>> +
>> +        Address = Address + EFI_PAGE_SIZE;
>> +      }
>> +    }
>> +
>> +    if (Ret) {
>> +    DEBUG ((DEBUG_ERROR, "%a:%a: Failed to %a address 0x%Lx Error
>> code %d\n",
>> +           gEfiCallerBaseName,
>> +           __FUNCTION__,
>> +           Validate ? "Validate" : "Invalidate",
>> +           Address,
>> +           Ret));
>> +      SnpPageStateFailureTerminate ();
>> +    }
>> +  }
>> +}
>> +
>> +/**
>> + The function is used to set the page state when SEV-SNP is active. The page
>> state
>> + transition consist of changing the page ownership in the RMP table, and using
>> the
>> + PVALIDATE instruction to update the Validated bit in RMP table.
>> +
>> + When the UseLargeEntry is set to TRUE, then use the large RMP entry.
>> + */
>> +VOID
>> +SetPageStateInternal (
>> +  IN EFI_PHYSICAL_ADDRESS             BaseAddress,
>> +  IN UINTN                            NumPages,
>> +  IN SEV_SNP_PAGE_STATE               State,
>> +  IN BOOLEAN                          UseLargeEntry
>> +  )
>> +{
>> +  EFI_STATUS                      Status;
>> +  GHCB                            *Ghcb;
>> +  EFI_PHYSICAL_ADDRESS            NextAddress, EndAddress;
>> +  MSR_SEV_ES_GHCB_REGISTER        Msr;
>> +  BOOLEAN                         InterruptState;
>> +  SNP_PAGE_STATE_CHANGE_INFO      *Info;
>> +  UINTN                           i, RmpPageSize;
>> +
>> +  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
>> +  Ghcb = Msr.Ghcb;
>> +
>> +  EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
>> +
>> +  DEBUG ((DEBUG_VERBOSE, "%a:%a Address 0x%Lx - 0x%Lx State = %a
>> LargeEntry = %d\n",
>> +          gEfiCallerBaseName,
>> +          __FUNCTION__,
>> +          BaseAddress,
>> +          EndAddress,
>> +          State == SevSnpPageShared ? "Shared" : "Private",
>> +          UseLargeEntry));
>> +
>> +  for (; BaseAddress < EndAddress; BaseAddress = NextAddress) {
>> +
>> +    //
>> +    // Initialize the GHCB and setup scratch sw to point to shared buffer.
>> +    //
>> +    VmgInit (Ghcb, &InterruptState);
>> +    Info = (SNP_PAGE_STATE_CHANGE_INFO *) Ghcb->SharedBuffer;
>> +
>> +    SetMem (Info, sizeof (*Info), 0);
>> +
>> +    //
>> +    // Build page state change buffer
>> +    //
>> +    for (i = 0; (EndAddress > BaseAddress) && i < SNP_PAGE_STATE_MAX_ENTRY;
>> +          BaseAddress = NextAddress, i++) {
>> +      //
>> +      // Is this a 2MB aligned page? Check if we can use the Large RMP entry.
>> +      //
>> +      if (UseLargeEntry &&
>> +          IS_ALIGNED (BaseAddress, EFI_LARGE_PAGE) &&
>> +          ((EndAddress - BaseAddress) >> EFI_PAGE_SHIFT) >=
>> PAGES_PER_LARGE_ENTRY) {
>> +        RmpPageSize = PVALIDATE_PAGE_SIZE_2M;
>> +        NextAddress = BaseAddress + EFI_LARGE_PAGE;
>> +      } else {
>> +        RmpPageSize = PVALIDATE_PAGE_SIZE_4K;
>> +        NextAddress = BaseAddress + EFI_PAGE_SIZE;
>> +      }
>> +
>> +      Info->Entry[i].GuestFrameNumber = BaseAddress >> EFI_PAGE_SHIFT;
>> +      Info->Entry[i].PageSize = RmpPageSize;
>> +      Info->Entry[i].Op = MemoryStateToGhcbOp (State);
>> +      Info->Entry[i].CurrentPage = 0;
>> +    }
>> +
>> +    Info->Header.CurrentEntry = 0;
>> +    Info->Header.EndEntry = i - 1;
>> +
>> +    //
>> +    // If the request page state change is shared then invalidate the pages before
>> +    // adding the page in the RMP table.
>> +    //
>> +    if (State == SevSnpPageShared) {
>> +      PvalidateRange (Info, 0, i, FALSE);
>> +    }
>> +
>> +    //
>> +    // Issue the VMGEXIT and retry if hypervisor failed to process all the entries.
>> +    //
>> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
>> +    VmgSetOffsetValid (Ghcb, GhcbSwScratch);
>> +    while (Info->Header.CurrentEntry <= Info->Header.EndEntry) {
>> +      Status = VmgExit (Ghcb, SVM_EXIT_SNP_PAGE_STATE_CHANGE, 0, 0);
>> +      if (EFI_ERROR (Status)) {
>> +        SnpPageStateFailureTerminate ();
>> +      }
>> +    }
>> +
>> +    //
>> +    // If the request page state change is shared then invalidate the pages before
>> +    // adding the page in the RMP table.
>> +    //
>> +    if (State == SevSnpPagePrivate) {
>> +      PvalidateRange (Info, 0, i, TRUE);
>> +    }
>> +
>> +    VmgDone (Ghcb, InterruptState);
>> +  }
>> +}
>> --
>> 2.17.1

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-03-24 15:31 ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Brijesh Singh
@ 2021-04-06  8:11   ` Min Xu
  2021-04-06 12:16     ` Laszlo Ersek
  2021-04-12 14:52   ` Brijesh Singh
  1 sibling, 1 reply; 68+ messages in thread
From: Min Xu @ 2021-04-06  8:11 UTC (permalink / raw)
  To: Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Yao, Jiewen, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel, Laszlo Ersek

Hi, Singh
I have a concern about the sevSnpBlock in ResetVectorVtf0.asm. Actually
SEV has inserted 3 blocks in ResetVectorVtf0.asm and the total bytes are
(26 + 22 + 20 = 68 bytes). If sevSnpBlock is added, then the total bytes
will be (68 +26 = 94 bytes).

I am not sure whether there will be more blocks added in
ResetVectorVtf0.asm in the future. But I don't think ResetVectorVtf0.asm
is a good place to add these data blobs. Can these data be packed into a
single file, for example, SevMetadata.asm, then a pointer is inserted in
ResetVectorVtf0.asm which then points to the SevMetadata. In this way we
can keep ResetVectorVtf0.asm clean, small and straight forward.

Another reason is that I am working on the Intel TDX which will update
the ResetVectorVtf0.asm as well. My change depends on the assumption that
the distance between ResetVector(0xfffffff0) and EarlyBspInitReal16 is
less than 128 bytes. The blocks in ResetVectorVtf0.asm make it impossible.

Thanks!

> -----Original Message-----
> From: Brijesh Singh <brijesh.singh@amd.com>
> Sent: Wednesday, March 24, 2021 11:32 PM
> To: devel@edk2.groups.io
> Cc: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
> <jejb@linux.ibm.com>; Xu, Min M <min.m.xu@intel.com>; Yao, Jiewen
> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>;
> Justen, Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
> Subject: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for
> the SEV-SNP guest
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
> 
> During the SEV-SNP guest launch sequence, two special pages need to be
> inserted, the secrets page and cpuid page. The secrets page, contain the VM
> platform communication keys. The guest BIOS and OS can use this key to
> communicate with the SEV firmware to get the attestation report. The Cpuid
> page, contain the CPUIDs entries filtered through the AMD-SEV firmware.
> 
> The VMM will locate the secrets and cpuid page addresses through a fixed
> GUID and pass them to SEV firmware to populate further.
> For more information about the page content, see the SEV-SNP spec.
> 
> To simplify the pre-validation range calculation in the next patch, the CPUID
> and Secrets pages are moved to the start of the MEMFD_BASE_ADDRESS.
> 
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  OvmfPkg/OvmfPkg.dec                          |  8 +++++++
>  OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
>  OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
>  OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
>  5 files changed, 48 insertions(+), 9 deletions(-)
> 
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index
> 4348bb45c6..062926772d 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -317,6 +317,14 @@
>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
> 
> +  ## The base address of the CPUID page used by SEV-SNP
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
> +
> +  ## The base address of the Secrets page used by SEV-SNP
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
> +
>  [PcdsDynamic, PcdsDynamicEx]
>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
> 
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN
> |0x10
> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index
> d519f85328..ea214600be 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -67,27 +67,33 @@ ErasePolarity = 1
>  BlockSize     = 0x10000
>  NumBlocks     = 0xD0
> 
> -0x000000|0x006000
> +0x000000|0x001000
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgTokenS
> paceGu
> +id.PcdOvmfSnpCpuidSize
> +
> +0x001000|0x001000
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgToken
> Space
> +Guid.PcdOvmfSnpSecretsSize
> +
> +0x002000|0x006000
> 
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTok
> enSpaceGuid.PcdOvmfSecPageTablesSize
> 
> -0x006000|0x001000
> +0x008000|0x001000
> 
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTo
> kenSpaceGuid.PcdOvmfLockBoxStorageSize
> 
> -0x007000|0x001000
> +0x009000|0x001000
> 
> gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvm
> fPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
> 
> -0x008000|0x001000
> +0x00A000|0x001000
> 
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkg
> TokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
> 
> -0x009000|0x002000
> +0x00B000|0x002000
> 
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpa
> ceGuid.PcdOvmfSecGhcbSize
> 
> -0x00B000|0x001000
> -
> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpac
> eGuid.PcdSevEsWorkAreaSize
> -
> -0x00C000|0x001000
> +0x00D000|0x001000
> 
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTo
> kenSpaceGuid.PcdOvmfSecGhcbBackupSize
> 
> +0x00F000|0x001000
> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
> ceGui
> +d.PcdSevEsWorkAreaSize
> +
>  0x010000|0x010000
> 
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTo
> kenSpaceGuid.PcdOvmfSecPeiTempRamSize
> 
> diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> index 9c0b5853a4..5456f02924 100644
> --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> @@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart
> + 15) % 16)) DB 0  ;
>  guidedStructureStart:
> 
> +;
> +; SEV-SNP boot support
> +;
> +; sevSnpBlock:
> +;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must be
> +;   reserved by the BIOS at a RAM area defined by SEV_SNP_SECRETS_PAGE
> +;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
> +;   SEV-SNP boot block.
> +;
> +; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
> +;
> +sevSnpBootBlockStart:
> +    DD      SEV_SNP_SECRETS_PAGE
> +    DD      SEV_SNP_CPUID_PAGE
> +    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
> +    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
> +    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
> +sevSnpBootBlockEnd:
> +
>  ;
>  ; SEV Secret block
>  ;
> diff --git a/OvmfPkg/ResetVector/ResetVector.inf
> b/OvmfPkg/ResetVector/ResetVector.inf
> index dc38f68919..d890bb6b29 100644
> --- a/OvmfPkg/ResetVector/ResetVector.inf
> +++ b/OvmfPkg/ResetVector/ResetVector.inf
> @@ -37,6 +37,10 @@
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb
> b/OvmfPkg/ResetVector/ResetVector.nasmb
> index 5fbacaed5f..2c194958f4 100644
> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
> @@ -75,6 +75,8 @@
>    %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>    %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32
> (PcdSevEsWorkAreaBase) + 8)
>    %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32
> (PcdSevEsWorkAreaBase) + 16)
> +  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
> + %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
>    %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32
> (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32
> (PcdOvmfSecPeiTempRamSize))  %include "Ia32/Flat32ToFlat64.asm"
>  %include "Ia32/PageTables64.asm"
> --
> 2.17.1


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-06  8:11   ` Min Xu
@ 2021-04-06 12:16     ` Laszlo Ersek
  2021-04-07  0:21       ` Min Xu
  2021-04-07  0:31       ` James Bottomley
  0 siblings, 2 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-06 12:16 UTC (permalink / raw)
  To: Xu, Min M, Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Yao, Jiewen, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel

On 04/06/21 10:11, Xu, Min M wrote:
> Hi, Singh
> I have a concern about the sevSnpBlock in ResetVectorVtf0.asm. Actually
> SEV has inserted 3 blocks in ResetVectorVtf0.asm and the total bytes are
> (26 + 22 + 20 = 68 bytes). If sevSnpBlock is added, then the total bytes
> will be (68 +26 = 94 bytes).
> 
> I am not sure whether there will be more blocks added in
> ResetVectorVtf0.asm in the future. But I don't think ResetVectorVtf0.asm
> is a good place to add these data blobs. Can these data be packed into a
> single file, for example, SevMetadata.asm, then a pointer is inserted in
> ResetVectorVtf0.asm which then points to the SevMetadata. In this way we
> can keep ResetVectorVtf0.asm clean, small and straight forward.
> 
> Another reason is that I am working on the Intel TDX which will update
> the ResetVectorVtf0.asm as well. My change depends on the assumption that
> the distance between ResetVector(0xfffffff0) and EarlyBspInitReal16 is
> less than 128 bytes. The blocks in ResetVectorVtf0.asm make it impossible.

That's a problem. These info blocks are placed in the reset vector
because then they can be found by QEMU easily -- they are not
compressed, and they appear at a known location in the guest physical
address space. (More precisely, a GUID-ed structure chain starts at a
known location, and then QEMU can traverse the chain of structures, for
learning various bits of information about the firmware.)

Do we absolutely need a short jump?

Thanks
Laszlo

> 
> Thanks!
> 
>> -----Original Message-----
>> From: Brijesh Singh <brijesh.singh@amd.com>
>> Sent: Wednesday, March 24, 2021 11:32 PM
>> To: devel@edk2.groups.io
>> Cc: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
>> <jejb@linux.ibm.com>; Xu, Min M <min.m.xu@intel.com>; Yao, Jiewen
>> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>;
>> Justen, Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
>> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
>> Subject: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for
>> the SEV-SNP guest
>>
>> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
>>
>> During the SEV-SNP guest launch sequence, two special pages need to be
>> inserted, the secrets page and cpuid page. The secrets page, contain the VM
>> platform communication keys. The guest BIOS and OS can use this key to
>> communicate with the SEV firmware to get the attestation report. The Cpuid
>> page, contain the CPUIDs entries filtered through the AMD-SEV firmware.
>>
>> The VMM will locate the secrets and cpuid page addresses through a fixed
>> GUID and pass them to SEV firmware to populate further.
>> For more information about the page content, see the SEV-SNP spec.
>>
>> To simplify the pre-validation range calculation in the next patch, the CPUID
>> and Secrets pages are moved to the start of the MEMFD_BASE_ADDRESS.
>>
>> Cc: James Bottomley <jejb@linux.ibm.com>
>> Cc: Min Xu <min.m.xu@intel.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Tom Lendacky <thomas.lendacky@amd.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>> ---
>>  OvmfPkg/OvmfPkg.dec                          |  8 +++++++
>>  OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
>>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
>>  OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
>>  OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
>>  5 files changed, 48 insertions(+), 9 deletions(-)
>>
>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index
>> 4348bb45c6..062926772d 100644
>> --- a/OvmfPkg/OvmfPkg.dec
>> +++ b/OvmfPkg/OvmfPkg.dec
>> @@ -317,6 +317,14 @@
>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
>>
>> +  ## The base address of the CPUID page used by SEV-SNP
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
>> +
>> +  ## The base address of the Secrets page used by SEV-SNP
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
>> +
>>  [PcdsDynamic, PcdsDynamicEx]
>>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>>
>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN
>> |0x10
>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index
>> d519f85328..ea214600be 100644
>> --- a/OvmfPkg/OvmfPkgX64.fdf
>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>> @@ -67,27 +67,33 @@ ErasePolarity = 1
>>  BlockSize     = 0x10000
>>  NumBlocks     = 0xD0
>>
>> -0x000000|0x006000
>> +0x000000|0x001000
>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgTokenS
>> paceGu
>> +id.PcdOvmfSnpCpuidSize
>> +
>> +0x001000|0x001000
>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgToken
>> Space
>> +Guid.PcdOvmfSnpSecretsSize
>> +
>> +0x002000|0x006000
>>
>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTok
>> enSpaceGuid.PcdOvmfSecPageTablesSize
>>
>> -0x006000|0x001000
>> +0x008000|0x001000
>>
>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTo
>> kenSpaceGuid.PcdOvmfLockBoxStorageSize
>>
>> -0x007000|0x001000
>> +0x009000|0x001000
>>
>> gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvm
>> fPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>>
>> -0x008000|0x001000
>> +0x00A000|0x001000
>>
>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkg
>> TokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>
>> -0x009000|0x002000
>> +0x00B000|0x002000
>>
>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpa
>> ceGuid.PcdOvmfSecGhcbSize
>>
>> -0x00B000|0x001000
>> -
>> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpac
>> eGuid.PcdSevEsWorkAreaSize
>> -
>> -0x00C000|0x001000
>> +0x00D000|0x001000
>>
>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTo
>> kenSpaceGuid.PcdOvmfSecGhcbBackupSize
>>
>> +0x00F000|0x001000
>> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
>> ceGui
>> +d.PcdSevEsWorkAreaSize
>> +
>>  0x010000|0x010000
>>
>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTo
>> kenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>
>> diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>> b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>> index 9c0b5853a4..5456f02924 100644
>> --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>> +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>> @@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart
>> + 15) % 16)) DB 0  ;
>>  guidedStructureStart:
>>
>> +;
>> +; SEV-SNP boot support
>> +;
>> +; sevSnpBlock:
>> +;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must be
>> +;   reserved by the BIOS at a RAM area defined by SEV_SNP_SECRETS_PAGE
>> +;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
>> +;   SEV-SNP boot block.
>> +;
>> +; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
>> +;
>> +sevSnpBootBlockStart:
>> +    DD      SEV_SNP_SECRETS_PAGE
>> +    DD      SEV_SNP_CPUID_PAGE
>> +    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
>> +    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
>> +    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
>> +sevSnpBootBlockEnd:
>> +
>>  ;
>>  ; SEV Secret block
>>  ;
>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf
>> b/OvmfPkg/ResetVector/ResetVector.inf
>> index dc38f68919..d890bb6b29 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>> @@ -37,6 +37,10 @@
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb
>> b/OvmfPkg/ResetVector/ResetVector.nasmb
>> index 5fbacaed5f..2c194958f4 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>> @@ -75,6 +75,8 @@
>>    %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>>    %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32
>> (PcdSevEsWorkAreaBase) + 8)
>>    %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32
>> (PcdSevEsWorkAreaBase) + 16)
>> +  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
>> + %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
>>    %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32
>> (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32
>> (PcdOvmfSecPeiTempRamSize))  %include "Ia32/Flat32ToFlat64.asm"
>>  %include "Ia32/PageTables64.asm"
>> --
>> 2.17.1
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-06 12:16     ` Laszlo Ersek
@ 2021-04-07  0:21       ` Min Xu
  2021-04-07  0:44         ` James Bottomley
  2021-04-07 13:22         ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Laszlo Ersek
  2021-04-07  0:31       ` James Bottomley
  1 sibling, 2 replies; 68+ messages in thread
From: Min Xu @ 2021-04-07  0:21 UTC (permalink / raw)
  To: Laszlo Ersek, Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Yao, Jiewen, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel, Xu, Min M

Hi, Laszlo

For Intel TDX supported guest, all processors start in 32-bit protected
mode, while for Non-Td guest, it starts in 16-bit real mode. To make the
ResetVector work on both Td-guest and Non-Td guest, ResetVector are
updated as below:
------------------------------------------------------------------
  ALIGN   16
  resetVector:
  ;
  ; Reset Vector
  ;
  ; This is where the processor will begin execution
  ;
      nop
      nop
      smsw    ax
      test    al, 1
      jnz     EarlyBspPmEntry
      jmp     EarlyBspInitReal16

  ALIGN   16
  fourGigabytes:
------------------------------------------------------------------

For Non-Td guest, jmp to EarlyBspInitReal16 in 16-bit real mode is ok.

For Td-guest, first jmp to EarlyBspPmEntry in 32-bit protected mode, then
in EarlyBspPmEntry jmp to MainTd which is the the main entry for Td-guest.
This requires the distance between ResetVector and EarlyBspPmEntry less
than 128 bytes.

Intel TDX also has metadata which is consumed by QEMU. We put the metadata
in a single file (TdxMetadata.asm) and put it at the end of ResetVectorVtf0.
Then a pointer is placed in a known location in ResetVector.nasm. In this way
QEMU can easily read the Metadata by the pointer.
------------------------------------------------------------------
ALIGN   8
;
; TDX Virtual Firmware injects metadata in VTF0.
; The address of the metadata is injected in this location (0xffffffe8)
;
    DD      (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16))
;
; The VTF signature
;
; VTF-0 means that the VTF (Volume Top File) code does not require
; any fixups.
;
vtfSignature:
    DB      'V', 'T', 'F', 0
------------------------------------------------------------------

The space in ResetVector is very precious and we all want a known location so that QEMU
can find the metadata easily. Putting the metadata in a single file give the developers
more flexible (They can put anything they want). So I think a pointer (point to a metadata
file) in a known location maybe a better solution.

Thanks!

> -----Original Message-----
> From: Laszlo Ersek <lersek@redhat.com>
> Sent: Tuesday, April 6, 2021 8:17 PM
> To: Xu, Min M <min.m.xu@intel.com>; Brijesh Singh
> <brijesh.singh@amd.com>; devel@edk2.groups.io
> Cc: James Bottomley <jejb@linux.ibm.com>; Yao, Jiewen
> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>;
> Justen, Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>
> Subject: Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page
> for the SEV-SNP guest
> 
> On 04/06/21 10:11, Xu, Min M wrote:
> > Hi, Singh
> > I have a concern about the sevSnpBlock in ResetVectorVtf0.asm.
> > Actually SEV has inserted 3 blocks in ResetVectorVtf0.asm and the
> > total bytes are
> > (26 + 22 + 20 = 68 bytes). If sevSnpBlock is added, then the total
> > bytes will be (68 +26 = 94 bytes).
> >
> > I am not sure whether there will be more blocks added in
> > ResetVectorVtf0.asm in the future. But I don't think
> > ResetVectorVtf0.asm is a good place to add these data blobs. Can these
> > data be packed into a single file, for example, SevMetadata.asm, then
> > a pointer is inserted in ResetVectorVtf0.asm which then points to the
> > SevMetadata. In this way we can keep ResetVectorVtf0.asm clean, small
> and straight forward.
> >
> > Another reason is that I am working on the Intel TDX which will update
> > the ResetVectorVtf0.asm as well. My change depends on the assumption
> > that the distance between ResetVector(0xfffffff0) and
> > EarlyBspInitReal16 is less than 128 bytes. The blocks in
> ResetVectorVtf0.asm make it impossible.
> 
> That's a problem. These info blocks are placed in the reset vector because
> then they can be found by QEMU easily -- they are not compressed, and they
> appear at a known location in the guest physical address space. (More
> precisely, a GUID-ed structure chain starts at a known location, and then
> QEMU can traverse the chain of structures, for learning various bits of
> information about the firmware.)
> 
> Do we absolutely need a short jump?
> 
> Thanks
> Laszlo
> 
> >
> > Thanks!
> >
> >> -----Original Message-----
> >> From: Brijesh Singh <brijesh.singh@amd.com>
> >> Sent: Wednesday, March 24, 2021 11:32 PM
> >> To: devel@edk2.groups.io
> >> Cc: Brijesh Singh <brijesh.singh@amd.com>; James Bottomley
> >> <jejb@linux.ibm.com>; Xu, Min M <min.m.xu@intel.com>; Yao, Jiewen
> >> <jiewen.yao@intel.com>; Tom Lendacky <thomas.lendacky@amd.com>;
> >> Justen, Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> >> <ardb+tianocore@kernel.org>; Laszlo Ersek <lersek@redhat.com>
> >> Subject: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid
> >> page for the SEV-SNP guest
> >>
> >> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
> >>
> >> During the SEV-SNP guest launch sequence, two special pages need to
> >> be inserted, the secrets page and cpuid page. The secrets page,
> >> contain the VM platform communication keys. The guest BIOS and OS can
> >> use this key to communicate with the SEV firmware to get the
> >> attestation report. The Cpuid page, contain the CPUIDs entries filtered
> through the AMD-SEV firmware.
> >>
> >> The VMM will locate the secrets and cpuid page addresses through a
> >> fixed GUID and pass them to SEV firmware to populate further.
> >> For more information about the page content, see the SEV-SNP spec.
> >>
> >> To simplify the pre-validation range calculation in the next patch,
> >> the CPUID and Secrets pages are moved to the start of the
> MEMFD_BASE_ADDRESS.
> >>
> >> Cc: James Bottomley <jejb@linux.ibm.com>
> >> Cc: Min Xu <min.m.xu@intel.com>
> >> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> >> Cc: Jordan Justen <jordan.l.justen@intel.com>
> >> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> >> Cc: Laszlo Ersek <lersek@redhat.com>
> >> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> >> ---
> >>  OvmfPkg/OvmfPkg.dec                          |  8 +++++++
> >>  OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
> >>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
> >>  OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
> >>  OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
> >>  5 files changed, 48 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index
> >> 4348bb45c6..062926772d 100644
> >> --- a/OvmfPkg/OvmfPkg.dec
> >> +++ b/OvmfPkg/OvmfPkg.dec
> >> @@ -317,6 +317,14 @@
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
> >>
> >> +  ## The base address of the CPUID page used by SEV-SNP
> >> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
> >> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
> >> +
> >> +  ## The base address of the Secrets page used by SEV-SNP
> >> +
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
> >> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
> >> +
> >>  [PcdsDynamic, PcdsDynamicEx]
> >>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
> >>
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLE
> AN
> >> |0x10
> >> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index
> >> d519f85328..ea214600be 100644
> >> --- a/OvmfPkg/OvmfPkgX64.fdf
> >> +++ b/OvmfPkg/OvmfPkgX64.fdf
> >> @@ -67,27 +67,33 @@ ErasePolarity = 1
> >>  BlockSize     = 0x10000
> >>  NumBlocks     = 0xD0
> >>
> >> -0x000000|0x006000
> >> +0x000000|0x001000
> >>
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgToken
> S
> >> paceGu
> >> +id.PcdOvmfSnpCpuidSize
> >> +
> >> +0x001000|0x001000
> >>
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgToke
> n
> >> Space
> >> +Guid.PcdOvmfSnpSecretsSize
> >> +
> >> +0x002000|0x006000
> >>
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTo
> k
> >> enSpaceGuid.PcdOvmfSecPageTablesSize
> >>
> >> -0x006000|0x001000
> >> +0x008000|0x001000
> >>
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgT
> o
> >> kenSpaceGuid.PcdOvmfLockBoxStorageSize
> >>
> >> -0x007000|0x001000
> >> +0x009000|0x001000
> >>
> >>
> gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOv
> m
> >> fPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
> >>
> >> -0x008000|0x001000
> >> +0x00A000|0x001000
> >>
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfP
> kg
> >> TokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
> >>
> >> -0x009000|0x002000
> >> +0x00B000|0x002000
> >>
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSp
> a
> >> ceGuid.PcdOvmfSecGhcbSize
> >>
> >> -0x00B000|0x001000
> >> -
> >>
> gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpa
> c
> >> eGuid.PcdSevEsWorkAreaSize
> >> -
> >> -0x00C000|0x001000
> >> +0x00D000|0x001000
> >>
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgT
> o
> >> kenSpaceGuid.PcdOvmfSecGhcbBackupSize
> >>
> >> +0x00F000|0x001000
> >>
> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSp
> a
> >> ceGui
> >> +d.PcdSevEsWorkAreaSize
> >> +
> >>  0x010000|0x010000
> >>
> >>
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkg
> To
> >> kenSpaceGuid.PcdOvmfSecPeiTempRamSize
> >>
> >> diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> >> b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> >> index 9c0b5853a4..5456f02924 100644
> >> --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> >> +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> >> @@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd -
> >> guidedStructureStart
> >> + 15) % 16)) DB 0  ;
> >>  guidedStructureStart:
> >>
> >> +;
> >> +; SEV-SNP boot support
> >> +;
> >> +; sevSnpBlock:
> >> +;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must
> be
> >> +;   reserved by the BIOS at a RAM area defined by
> SEV_SNP_SECRETS_PAGE
> >> +;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using
> the
> >> +;   SEV-SNP boot block.
> >> +;
> >> +; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
> >> +;
> >> +sevSnpBootBlockStart:
> >> +    DD      SEV_SNP_SECRETS_PAGE
> >> +    DD      SEV_SNP_CPUID_PAGE
> >> +    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
> >> +    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
> >> +    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
> >> +sevSnpBootBlockEnd:
> >> +
> >>  ;
> >>  ; SEV Secret block
> >>  ;
> >> diff --git a/OvmfPkg/ResetVector/ResetVector.inf
> >> b/OvmfPkg/ResetVector/ResetVector.inf
> >> index dc38f68919..d890bb6b29 100644
> >> --- a/OvmfPkg/ResetVector/ResetVector.inf
> >> +++ b/OvmfPkg/ResetVector/ResetVector.inf
> >> @@ -37,6 +37,10 @@
> >>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
> >>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
> >>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
> >> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
> >> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
> >> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
> >> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
> >>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
> >>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
> >>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
> >> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb
> >> b/OvmfPkg/ResetVector/ResetVector.nasmb
> >> index 5fbacaed5f..2c194958f4 100644
> >> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
> >> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
> >> @@ -75,6 +75,8 @@
> >>    %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
> >>    %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32
> >> (PcdSevEsWorkAreaBase) + 8)
> >>    %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32
> >> (PcdSevEsWorkAreaBase) + 16)
> >> +  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32
> (PcdOvmfSnpSecretsBase)
> >> + %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
> >>    %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32
> >> (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32
> >> (PcdOvmfSecPeiTempRamSize))  %include "Ia32/Flat32ToFlat64.asm"
> >>  %include "Ia32/PageTables64.asm"
> >> --
> >> 2.17.1
> >


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-06 12:16     ` Laszlo Ersek
  2021-04-07  0:21       ` Min Xu
@ 2021-04-07  0:31       ` James Bottomley
  1 sibling, 0 replies; 68+ messages in thread
From: James Bottomley @ 2021-04-07  0:31 UTC (permalink / raw)
  To: Laszlo Ersek, Xu, Min M, Brijesh Singh, devel@edk2.groups.io
  Cc: Yao, Jiewen, Tom Lendacky, Justen, Jordan L, Ard Biesheuvel

On Tue, 2021-04-06 at 14:16 +0200, Laszlo Ersek wrote:
> On 04/06/21 10:11, Xu, Min M wrote:
> > Hi, Singh
> > I have a concern about the sevSnpBlock in ResetVectorVtf0.asm.
> > Actually
> > SEV has inserted 3 blocks in ResetVectorVtf0.asm and the total
> > bytes are
> > (26 + 22 + 20 = 68 bytes). If sevSnpBlock is added, then the total
> > bytes
> > will be (68 +26 = 94 bytes).
> > 
> > I am not sure whether there will be more blocks added in
> > ResetVectorVtf0.asm in the future. But I don't think
> > ResetVectorVtf0.asm
> > is a good place to add these data blobs. Can these data be packed
> > into a
> > single file, for example, SevMetadata.asm, then a pointer is
> > inserted in
> > ResetVectorVtf0.asm which then points to the SevMetadata. In this
> > way we
> > can keep ResetVectorVtf0.asm clean, small and straight forward.
> > 
> > Another reason is that I am working on the Intel TDX which will
> > update the ResetVectorVtf0.asm as well. My change depends on the
> > assumption that the distance between ResetVector(0xfffffff0) and
> > EarlyBspInitReal16 is less than 128 bytes. The blocks in
> > ResetVectorVtf0.asm make it impossible.

Why?  I think the short jump can cover 32k as an offset.

> That's a problem. These info blocks are placed in the reset vector
> because then they can be found by QEMU easily -- they are not
> compressed, and they appear at a known location in the guest physical
> address space. (More precisely, a GUID-ed structure chain starts at a
> known location, and then QEMU can traverse the chain of structures,
> for learning various bits of information about the firmware.)
> 
> Do we absolutely need a short jump?

Yes, I explained this before:

https://listman.redhat.com/archives/edk2-devel-archive/2020-November/msg01063.html

Sorry, it's buried in the middle about why we can only have 32k worth
of entries.  However, the restriction is 32k not 128 bytes unless Intel
is doing something strange.

James



^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-07  0:21       ` Min Xu
@ 2021-04-07  0:44         ` James Bottomley
  2021-04-07 15:02           ` Laszlo Ersek
  2021-04-07 13:22         ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Laszlo Ersek
  1 sibling, 1 reply; 68+ messages in thread
From: James Bottomley @ 2021-04-07  0:44 UTC (permalink / raw)
  To: Xu, Min M, Laszlo Ersek, Brijesh Singh, devel@edk2.groups.io
  Cc: Yao, Jiewen, Tom Lendacky, Justen, Jordan L, Ard Biesheuvel

On Wed, 2021-04-07 at 00:21 +0000, Xu, Min M wrote:
> Hi, Laszlo
> 
> For Intel TDX supported guest, all processors start in 32-bit
> protected
> mode, while for Non-Td guest, it starts in 16-bit real mode. To make
> the
> ResetVector work on both Td-guest and Non-Td guest, ResetVector are
> updated as below:
> ------------------------------------------------------------------
>   ALIGN   16
>   resetVector:
>   ;
>   ; Reset Vector
>   ;
>   ; This is where the processor will begin execution
>   ;
>       nop
>       nop
>       smsw    ax
>       test    al, 1
>       jnz     EarlyBspPmEntry
>       jmp     EarlyBspInitReal16

Well, then use the rel8 jump like the compiler would in this situation:

      smsw    ax
      test    al, 1
      jz      1f
      jmp     EarlyBspPmEntry
1:
      jmp     EarlyBspInitReal16

So now both entries can be 32k away.

James



^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-07  0:21       ` Min Xu
  2021-04-07  0:44         ` James Bottomley
@ 2021-04-07 13:22         ` Laszlo Ersek
  2021-04-07 13:24           ` Laszlo Ersek
  2021-04-08  0:45           ` Min Xu
  1 sibling, 2 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-07 13:22 UTC (permalink / raw)
  To: Xu, Min M, Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Yao, Jiewen, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel

On 04/07/21 02:21, Xu, Min M wrote:

> Intel TDX also has metadata which is consumed by QEMU. We put the metadata
> in a single file (TdxMetadata.asm) and put it at the end of ResetVectorVtf0.
> Then a pointer is placed in a known location in ResetVector.nasm. In this way
> QEMU can easily read the Metadata by the pointer.
> ------------------------------------------------------------------
> ALIGN   8
> ;
> ; TDX Virtual Firmware injects metadata in VTF0.
> ; The address of the metadata is injected in this location (0xffffffe8)
> ;
>     DD      (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16))
> ;
> ; The VTF signature
> ;
> ; VTF-0 means that the VTF (Volume Top File) code does not require
> ; any fixups.
> ;
> vtfSignature:
>     DB      'V', 'T', 'F', 0
> ------------------------------------------------------------------
> 
> The space in ResetVector is very precious and we all want a known location so that QEMU
> can find the metadata easily. Putting the metadata in a single file give the developers
> more flexible (They can put anything they want). So I think a pointer (point to a metadata
> file) in a known location maybe a better solution.

Assuming a QEMU version has been released that looks for the chain of
GUID-ed structs already, then I think such a change would break
compatibility with that QEMU version.

If we definitely need a separate spot to include more information in the
flash, for QEMU's parsing, then please introduce a new GUIDed structure,
which contains nothing but a pointer to that spot.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-07 13:22         ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Laszlo Ersek
@ 2021-04-07 13:24           ` Laszlo Ersek
  2021-04-08  0:45           ` Min Xu
  1 sibling, 0 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-07 13:24 UTC (permalink / raw)
  To: Xu, Min M, Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Yao, Jiewen, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel

On 04/07/21 15:22, Laszlo Ersek wrote:
> On 04/07/21 02:21, Xu, Min M wrote:
> 
>> Intel TDX also has metadata which is consumed by QEMU. We put the metadata
>> in a single file (TdxMetadata.asm) and put it at the end of ResetVectorVtf0.
>> Then a pointer is placed in a known location in ResetVector.nasm. In this way
>> QEMU can easily read the Metadata by the pointer.
>> ------------------------------------------------------------------
>> ALIGN   8
>> ;
>> ; TDX Virtual Firmware injects metadata in VTF0.
>> ; The address of the metadata is injected in this location (0xffffffe8)
>> ;
>>     DD      (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16))
>> ;
>> ; The VTF signature
>> ;
>> ; VTF-0 means that the VTF (Volume Top File) code does not require
>> ; any fixups.
>> ;
>> vtfSignature:
>>     DB      'V', 'T', 'F', 0
>> ------------------------------------------------------------------
>>
>> The space in ResetVector is very precious and we all want a known location so that QEMU
>> can find the metadata easily. Putting the metadata in a single file give the developers
>> more flexible (They can put anything they want). So I think a pointer (point to a metadata
>> file) in a known location maybe a better solution.
> 
> Assuming a QEMU version has been released that looks for the chain of
> GUID-ed structs already, then I think such a change would break
> compatibility with that QEMU version.
> 
> If we definitely need a separate spot to include more information in the
> flash, for QEMU's parsing, then please introduce a new GUIDed structure,
> which contains nothing but a pointer to that spot.

Ah, upon re-reading James's earlier message which he just referenced,
namely at

https://listman.redhat.com/archives/edk2-devel-archive/2020-November/msg01063.html

it's clear that James had already proposed this:

> That's not to say we can't have a larger table ... we definitely can,
> but it can't be where it is now.  we'd have to do something different
> like make the table guid describe where the actual table is located
> (as a 32 bit offset and length) so as not to break up the 16 bit jump
> code.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-07  0:44         ` James Bottomley
@ 2021-04-07 15:02           ` Laszlo Ersek
  2021-04-07 15:12             ` James Bottomley
  2021-04-08  6:24             ` [edk2-devel] " Min Xu
  0 siblings, 2 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-07 15:02 UTC (permalink / raw)
  To: jejb, Xu, Min M, Brijesh Singh, devel@edk2.groups.io
  Cc: Yao, Jiewen, Tom Lendacky, Justen, Jordan L, Ard Biesheuvel

On 04/07/21 02:44, James Bottomley wrote:
> On Wed, 2021-04-07 at 00:21 +0000, Xu, Min M wrote:
>> Hi, Laszlo
>>
>> For Intel TDX supported guest, all processors start in 32-bit
>> protected
>> mode, while for Non-Td guest, it starts in 16-bit real mode. To make
>> the
>> ResetVector work on both Td-guest and Non-Td guest, ResetVector are
>> updated as below:
>> ------------------------------------------------------------------
>>   ALIGN   16
>>   resetVector:
>>   ;
>>   ; Reset Vector
>>   ;
>>   ; This is where the processor will begin execution
>>   ;
>>       nop
>>       nop
>>       smsw    ax
>>       test    al, 1
>>       jnz     EarlyBspPmEntry
>>       jmp     EarlyBspInitReal16
> 
> Well, then use the rel8 jump like the compiler would in this situation:
> 
>       smsw    ax
>       test    al, 1
>       jz      1f
>       jmp     EarlyBspPmEntry
> 1:
>       jmp     EarlyBspInitReal16
> 
> So now both entries can be 32k away.

The problem is that we need NASM to generate such *shared* entry code
that behaves correctly when executed in either 16-bit or 32-bit mode.

The rel8 near jumps ("short jumps") are like that -- for example, the
"74 cb" opcode decodes to the same "JZ rel8" in both modes.

But the rel16 ("non-short") near jumps turn into rel32 near jumps when
decoded in 32-bit mode. For example, "E9 cw" decodes to "JMP rel16" in
16-bit mode, but it gets parsed as "E9 cd" (= "JMP rel32") in 32-bit mode.

So the idea is to add more BITS directives, for covering the non-short
near jumps themselves:

> ; instructions up to and including the rel8 JZ decode identically
> ; between BITS 16 and BITS 32
> BITS 16
>       smsw    ax
>       test    al, 1
>       jz     Real
>
> ; the unconditional near jumps are mode-specific
> BITS 32
>       jmp     near EarlyBspPmEntry
> BITS 16
> Real:
>       jmp     near EarlyBspInitReal16
>
> ; --------------------
>
> BITS 16
> EarlyBspInitReal16:
>       nop
>
> BITS 32
> EarlyBspPmEntry:
>       nop

$ nasm -f bin jz.nasmb

Decoded (executed) in 16-bit mode:

$ ndisasm -b 16 -k 7,5 -k 0x10,1 jz
00000000  0F01E0            smsw ax
00000003  A801              test al,0x1
00000005  7405              jz 0xc         ; taken
00000007  skipping 0x5 bytes
0000000C  E90000            jmp word 0xf
0000000F  90                nop
00000010  skipping 0x1 bytes

Decoded (executed) in 32-bit mode:

$ ndisasm -b 32 -k 0xc,4 jz
00000000  0F01E0            smsw eax
00000003  A801              test al,0x1
00000005  7405              jz 0xc         ; not taken
00000007  E904000000        jmp dword 0x10
0000000C  skipping 0x4 bytes
00000010  90                nop


With the garbage *not* hidden:

$ ndisasm -b 16 -s 0xc jz

00000000  0F01E0            smsw ax
00000003  A801              test al,0x1
00000005  7405              jz 0xc          ; taken
00000007  E90400            jmp word 0xe    ; garbage
0000000A  0000              add [bx+si],al  ; garbage
0000000C  E90000            jmp word 0xf
0000000F  90                nop
00000010  90                nop             ; garbage

$ ndisasm -b 32 -s 0x10 jz

00000000  0F01E0            smsw eax
00000003  A801              test al,0x1
00000005  7405              jz 0xc          ; not taken
00000007  E904000000        jmp dword 0x10
0000000C  E9                db 0xe9         ; garbage
0000000D  0000              add [eax],al    ; garbage
0000000F  90                nop             ; garbage
00000010  90                nop

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-07 15:02           ` Laszlo Ersek
@ 2021-04-07 15:12             ` James Bottomley
  2021-04-08  6:24             ` [edk2-devel] " Min Xu
  1 sibling, 0 replies; 68+ messages in thread
From: James Bottomley @ 2021-04-07 15:12 UTC (permalink / raw)
  To: Laszlo Ersek, Xu, Min M, Brijesh Singh, devel@edk2.groups.io
  Cc: Yao, Jiewen, Tom Lendacky, Justen, Jordan L, Ard Biesheuvel

On Wed, 2021-04-07 at 17:02 +0200, Laszlo Ersek wrote:
> On 04/07/21 02:44, James Bottomley wrote:
> > On Wed, 2021-04-07 at 00:21 +0000, Xu, Min M wrote:
> > > Hi, Laszlo
> > > 
> > > For Intel TDX supported guest, all processors start in 32-bit
> > > protected mode, while for Non-Td guest, it starts in 16-bit real
> > > mode. To make the ResetVector work on both Td-guest and Non-Td
> > > guest, ResetVector are updated as below:
> > > ---------------------------------------------------------------
> > > ---
> > >   ALIGN   16
> > >   resetVector:
> > >   ;
> > >   ; Reset Vector
> > >   ;
> > >   ; This is where the processor will begin execution
> > >   ;
> > >       nop
> > >       nop
> > >       smsw    ax
> > >       test    al, 1
> > >       jnz     EarlyBspPmEntry
> > >       jmp     EarlyBspInitReal16
> > 
> > Well, then use the rel8 jump like the compiler would in this
> > situation:
> > 
> >       smsw    ax
> >       test    al, 1
> >       jz      1f
> >       jmp     EarlyBspPmEntry
> > 1:
> >       jmp     EarlyBspInitReal16
> > 
> > So now both entries can be 32k away.
> 
> The problem is that we need NASM to generate such *shared* entry code
> that behaves correctly when executed in either 16-bit or 32-bit mode.
> 
> The rel8 near jumps ("short jumps") are like that -- for example, the
> "74 cb" opcode decodes to the same "JZ rel8" in both modes.
> 
> But the rel16 ("non-short") near jumps turn into rel32 near jumps
> when decoded in 32-bit mode. For example, "E9 cw" decodes to "JMP
> rel16" in 16-bit mode, but it gets parsed as "E9 cd" (= "JMP rel32")
> in 32-bit mode.
> 
> So the idea is to add more BITS directives, for covering the non-
> short near jumps themselves:

Absolutely ... sorry, I should have said this was just the first thing
I thought of.  The key point is don't do a rel8 jump over the guid
table, use the rel8 jump within the reset vector to sort out the final
destination and use the wider jumps to go over the guid table.  As you
say, we have to be very careful about the wider jumps given the
differences between the entry modes.

James





^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-07 13:22         ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Laszlo Ersek
  2021-04-07 13:24           ` Laszlo Ersek
@ 2021-04-08  0:45           ` Min Xu
  1 sibling, 0 replies; 68+ messages in thread
From: Min Xu @ 2021-04-08  0:45 UTC (permalink / raw)
  To: Laszlo Ersek, Brijesh Singh, devel@edk2.groups.io
  Cc: James Bottomley, Yao, Jiewen, Tom Lendacky, Justen, Jordan L,
	Ard Biesheuvel

On April 7, 2021 9:23 PM, Laszlo wrote:
> 
> On 04/07/21 02:21, Xu, Min M wrote:
> 
> > Intel TDX also has metadata which is consumed by QEMU. We put the
> > metadata in a single file (TdxMetadata.asm) and put it at the end of
> ResetVectorVtf0.
> > Then a pointer is placed in a known location in ResetVector.nasm. In
> > this way QEMU can easily read the Metadata by the pointer.
> > ------------------------------------------------------------------
> > ALIGN   8
> > ;
> > ; TDX Virtual Firmware injects metadata in VTF0.
> > ; The address of the metadata is injected in this location
> > (0xffffffe8) ;
> >     DD      (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes -
> TdxMetadataGuid - 16))
> > ;
> > ; The VTF signature
> > ;
> > ; VTF-0 means that the VTF (Volume Top File) code does not require ;
> > any fixups.
> > ;
> > vtfSignature:
> >     DB      'V', 'T', 'F', 0
> > ------------------------------------------------------------------
> >
> > The space in ResetVector is very precious and we all want a known
> > location so that QEMU can find the metadata easily. Putting the
> > metadata in a single file give the developers more flexible (They can
> > put anything they want). So I think a pointer (point to a metadata
> > file) in a known location maybe a better solution.
> 
> Assuming a QEMU version has been released that looks for the chain of GUID-
> ed structs already, then I think such a change would break compatibility with
> that QEMU version.
Agree. The existing GUIDed structs should be kept. Otherwise the QEMU version
Which has been released would be broken.

> 
> If we definitely need a separate spot to include more information in the flash,
> for QEMU's parsing, then please introduce a new GUIDed structure, which
> contains nothing but a pointer to that spot.
I suggest if new information is to be added in the flash in the future, then we'd
better pack the information in a separate spot and place a pointer to that spot
in a known location. Intel TDX has metadata too. I believe AMD SEV will add
more information in the future. Even ARM may has its metadata. So putting the
metadata in separate spot is more friendly and flexible.

Since it is a pointer in a known location, for example, 0xffffffe0, then do we still
need a GUIDed structure?
> 
> Thanks
> Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-07 15:02           ` Laszlo Ersek
  2021-04-07 15:12             ` James Bottomley
@ 2021-04-08  6:24             ` Min Xu
  2021-04-08 13:31               ` Lendacky, Thomas
  1 sibling, 1 reply; 68+ messages in thread
From: Min Xu @ 2021-04-08  6:24 UTC (permalink / raw)
  To: devel@edk2.groups.io, lersek@redhat.com, jejb@linux.ibm.com,
	Brijesh Singh
  Cc: Yao, Jiewen, Tom Lendacky, Justen, Jordan L, Ard Biesheuvel

On Wednesday, April 7, 2021 11:03 PM, Laszlo wrote:
> On 04/07/21 02:44, James Bottomley wrote:
> > On Wed, 2021-04-07 at 00:21 +0000, Xu, Min M wrote:
> >> Hi, Laszlo
> >>
> >> For Intel TDX supported guest, all processors start in 32-bit
> >> protected mode, while for Non-Td guest, it starts in 16-bit real
> >> mode. To make the ResetVector work on both Td-guest and Non-Td guest,
> >> ResetVector are updated as below:
> >> ------------------------------------------------------------------
> >>   ALIGN   16
> >>   resetVector:
> >>   ;
> >>   ; Reset Vector
> >>   ;
> >>   ; This is where the processor will begin execution
> >>   ;
> >>       nop
> >>       nop
> >>       smsw    ax
> >>       test    al, 1
> >>       jnz     EarlyBspPmEntry
> >>       jmp     EarlyBspInitReal16
> >
> > Well, then use the rel8 jump like the compiler would in this situation:
> >
> >       smsw    ax
> >       test    al, 1
> >       jz      1f
> >       jmp     EarlyBspPmEntry
> > 1:
> >       jmp     EarlyBspInitReal16
> >
> > So now both entries can be 32k away.
> 
> The problem is that we need NASM to generate such *shared* entry code that
> behaves correctly when executed in either 16-bit or 32-bit mode.
> 
> The rel8 near jumps ("short jumps") are like that -- for example, the
> "74 cb" opcode decodes to the same "JZ rel8" in both modes.
> 
> But the rel16 ("non-short") near jumps turn into rel32 near jumps when
> decoded in 32-bit mode. For example, "E9 cw" decodes to "JMP rel16" in 16-bit
> mode, but it gets parsed as "E9 cd" (= "JMP rel32") in 32-bit mode.
> 
> So the idea is to add more BITS directives, for covering the non-short near
> jumps themselves:

Yes this is the root cause. TDX requires the startup mode to be 32-bit
protected mode while the legacy VM startup mode is 16-bit real mode.
Add more BITS directives can work round this. I have tried it and it works.

So my initial solution is to use *jmp rel8* because it works in both 16-bit
and 32-bit mode. But *jmp rel8* depends on the distance which should
be less than 128 bytes. If more metadata is added in the ResetVector.asm
then we have to use the BITS solution. 
 
> 
> > ; instructions up to and including the rel8 JZ decode identically ;
> > between BITS 16 and BITS 32 BITS 16
> >       smsw    ax
> >       test    al, 1
> >       jz     Real
> >
> > ; the unconditional near jumps are mode-specific BITS 32
> >       jmp     near EarlyBspPmEntry
> > BITS 16
> > Real:
> >       jmp     near EarlyBspInitReal16
> >
> > ; --------------------
> >
> > BITS 16
> > EarlyBspInitReal16:
> >       nop
> >
> > BITS 32
> > EarlyBspPmEntry:
> >       nop
> 
> $ nasm -f bin jz.nasmb
> 
> Decoded (executed) in 16-bit mode:
> 
> $ ndisasm -b 16 -k 7,5 -k 0x10,1 jz
> 00000000  0F01E0            smsw ax
> 00000003  A801              test al,0x1
> 00000005  7405              jz 0xc         ; taken
> 00000007  skipping 0x5 bytes
> 0000000C  E90000            jmp word 0xf
> 0000000F  90                nop
> 00000010  skipping 0x1 bytes
> 
> Decoded (executed) in 32-bit mode:
> 
> $ ndisasm -b 32 -k 0xc,4 jz
> 00000000  0F01E0            smsw eax
> 00000003  A801              test al,0x1
> 00000005  7405              jz 0xc         ; not taken
> 00000007  E904000000        jmp dword 0x10
> 0000000C  skipping 0x4 bytes
> 00000010  90                nop
> 
> 
> With the garbage *not* hidden:
> 
> $ ndisasm -b 16 -s 0xc jz
> 
> 00000000  0F01E0            smsw ax
> 00000003  A801              test al,0x1
> 00000005  7405              jz 0xc          ; taken
> 00000007  E90400            jmp word 0xe    ; garbage
> 0000000A  0000              add [bx+si],al  ; garbage
> 0000000C  E90000            jmp word 0xf
> 0000000F  90                nop
> 00000010  90                nop             ; garbage
> 
> $ ndisasm -b 32 -s 0x10 jz
> 
> 00000000  0F01E0            smsw eax
> 00000003  A801              test al,0x1
> 00000005  7405              jz 0xc          ; not taken
> 00000007  E904000000        jmp dword 0x10
> 0000000C  E9                db 0xe9         ; garbage
> 0000000D  0000              add [eax],al    ; garbage
> 0000000F  90                nop             ; garbage
> 00000010  90                nop
> 
> Thanks
> Laszlo
> 
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
                   ` (19 preceding siblings ...)
  2021-03-24 19:14 ` [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support Laszlo Ersek
@ 2021-04-08  9:58 ` Laszlo Ersek
  2021-04-08 11:59   ` Brijesh Singh
  20 siblings, 1 reply; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-08  9:58 UTC (permalink / raw)
  To: brijesh.singh
  Cc: devel, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel

Hi Brijesh,

On 03/24/21 16:31, Brijesh Singh wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
> 
> SEV-SNP builds upon existing SEV and SEV-ES functionality while adding
> new hardware-based memory protections. SEV-SNP adds strong memory integrity
> protection to help prevent malicious hypervisor-based attacks like data
> replay, memory re-mapping and more in order to create an isolated memory
> encryption environment.
>  
> This series provides the basic building blocks to support booting the SEV-SNP
> VMs, it does not cover all the security enhancement introduced by the SEV-SNP
> such as interrupt protection.
> 
> Many of the integrity guarantees of SEV-SNP are enforced through a new
> structure called the Reverse Map Table (RMP). Adding a new page to SEV-SNP
> VM requires a 2-step process. First, the hypervisor assigns a page to the
> guest using the new RMPUPDATE instruction. This transitions the page to
> guest-invalid. Second, the guest validates the page using the new PVALIDATE
> instruction. The SEV-SNP VMs can use the new "Page State Change Request NAE"
> defined in the GHCB specification to ask hypervisor to add or remove page
> from the RMP table.
>  
> Each page assigned to the SEV-SNP VM can either be validated or unvalidated,
> as indicated by the Validated flag in the page's RMP entry. There are two
> approaches that can be taken for the page validation: Pre-validation and
> Lazy Validation.
>   
> Under pre-validation, the pages are validated prior to first use. And under
> lazy validation, pages are validated when first accessed. An access to a
> unvalidated page results in a #VC exception, at which time the exception
> handler may validate the page. Lazy validation requires careful tracking of
> the validated pages to avoid validating the same GPA more than once. The
> recently introduced "Unaccepted" memory type can be used to communicate the
> unvalidated memory ranges to the Guest OS.
> 
> At this time we only support the pre-validation. OVMF detects all the available
> system RAM in the PEI phase. When SEV-SNP is enabled, the memory is validated
> before it is made available to the EDK2 core.

Can you describe this in a bit more detail, before I look at the
individual patches? Specifically, what existing logic in the PEI phase
was taken, and extended, and how?

If there is a particular patch whose commit message is closely related
to my question, can you point it out? Patch#15 perhaps? (Doesn't seem
like a big patch; for some reason I'd expect something more complex, but
perhaps that's only because it builds upon the many earlier patches.)

Thanks,
Laszlo

> 
> This series does not implements the following SEV-SNP features yet:
> 
> * CPUID filtering
> * AP bring up using the new SEV-SNP NAE
> * Lazy validation
> * Interrupt security
> 
> The series is based on commit:
> e542e05d4f UefiCpuPkg/SmmCpuFeaturesLib: Abstract PcdCpuMaxLogicalProcessorNumber
> 
> Additional resources
> ---------------------
> SEV-SNP whitepaper
> https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf
>  
> APM 2: https://www.amd.com/system/files/TechDocs/24593.pdf (section 15.36)
> 
> The complete source is available at
> https://github.com/AMDESE/ovmf/tree/sev-snp-rfc-1
> 
> GHCB spec v2:
>   The draft specification is posted on AMD-SEV-SNP mailing list:
>    https://lists.suse.com/mailman/private/amd-sev-snp/
> 
>   Copy of the spec is also available at 
>   https://github.com/AMDESE/AMDSEV/blob/sev-snp-devel/docs/56421-Guest_Hypervisor_Communication_Block_Standardization.pdf
> 
> GHCB spec v1:
> SEV-SNP firmware specification:
>  https://developer.amd.com/sev/
>   
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> 
> Brijesh Singh (19):
>   OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
>   OvmfPkg: validate the data pages used in the SEC phase
>   MdePkg: Expand the SEV MSR to include the SNP definition
>   OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled()
>   MdePkg: Define the GHCB GPA structure
>   UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is
>     enabled
>   OvmfPkg: Add a library to support registering GHCB GPA
>   OvmfPkg: register GHCB gpa for the SEV-SNP guest
>   MdePkg: Add AsmPvalidate() support
>   OvmfPkg: Define the Page State Change VMGEXIT structures
>   OvmfPkg/ResetVector: Invalidate the GHCB page
>   OvmfPkg/MemEncryptSevLib: Add support to validate system RAM
>   OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase
>   OvmfPkg/MemEncryptSevLib: Add support to validate RAM in PEI phase
>   OvmfPkg/PlatformPei: Validate the system RAM when SNP is active
>   OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI
>     phase
>   OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase
>   OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc
>     attribute
>   OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region
> 
>  MdePkg/Include/Library/BaseLib.h              |  37 +++
>  MdePkg/Include/Register/Amd/Fam17Msr.h        |  31 ++-
>  MdePkg/Include/Register/Amd/Ghcb.h            |  39 ++-
>  MdePkg/Library/BaseLib/BaseLib.inf            |   1 +
>  MdePkg/Library/BaseLib/X64/Pvalidate.nasm     |  43 +++
>  OvmfPkg/Include/Library/GhcbRegisterLib.h     |  27 ++
>  OvmfPkg/Include/Library/MemEncryptSevLib.h    |  30 +++
>  .../DxeMemEncryptSevLib.inf                   |   7 +
>  .../DxeMemEncryptSevLibInternal.c             |  27 ++
>  .../Ia32/SnpPageStateChange.c                 |  17 ++
>  .../PeiMemEncryptSevLib.inf                   |   9 +
>  .../PeiMemEncryptSevLibInternal.c             |  47 ++++
>  .../SecMemEncryptSevLib.inf                   |   4 +
>  .../SecMemEncryptSevLibInternal.c             |  39 +++
>  .../BaseMemEncryptSevLib/SnpPageStateChange.h |  37 +++
>  .../X64/PeiDxeSnpSetPageState.c               |  63 +++++
>  .../X64/PeiDxeVirtualMemory.c                 | 151 ++++++++++-
>  .../X64/PeiSnpSystemRamValidate.c             | 129 +++++++++
>  .../X64/SecSnpSystemRamValidate.c             |  23 ++
>  .../X64/SnpPageStateChangeInternal.c          | 254 ++++++++++++++++++
>  .../X64/SnpPageStateTrack.c                   | 119 ++++++++
>  .../X64/SnpPageStateTrack.h                   |  36 +++
>  .../X64/SnpSetPageState.h                     |  27 ++
>  .../BaseMemEncryptSevLib/X64/VirtualMemory.h  |  19 ++
>  .../Library/GhcbRegisterLib/GhcbRegisterLib.c |  97 +++++++
>  .../GhcbRegisterLib/GhcbRegisterLib.inf       |  33 +++
>  OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf  |   4 +
>  OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |   7 +
>  OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c |  45 ++++
>  OvmfPkg/OvmfPkg.dec                           |  12 +
>  OvmfPkg/OvmfPkgX64.dsc                        |   1 +
>  OvmfPkg/OvmfPkgX64.fdf                        |  33 ++-
>  OvmfPkg/PlatformPei/AmdSev.c                  |  52 ++++
>  OvmfPkg/PlatformPei/PlatformPei.inf           |   2 +
>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm  |  24 ++
>  OvmfPkg/ResetVector/Ia32/PageTables64.asm     | 106 ++++++++
>  OvmfPkg/ResetVector/ResetVector.inf           |   5 +
>  OvmfPkg/ResetVector/ResetVector.nasmb         |   4 +
>  OvmfPkg/Sec/SecMain.c                         | 102 +++++++
>  OvmfPkg/Sec/SecMain.inf                       |   2 +
>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   1 +
>  UefiCpuPkg/Library/MpInitLib/MpEqu.inc        |   1 +
>  UefiCpuPkg/Library/MpInitLib/MpLib.c          |   2 +
>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |   2 +
>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   1 +
>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm |  51 ++++
>  UefiCpuPkg/UefiCpuPkg.dec                     |   6 +
>  47 files changed, 1790 insertions(+), 19 deletions(-)
>  create mode 100644 MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>  create mode 100644 OvmfPkg/Include/Library/GhcbRegisterLib.h
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h
>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
>  create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c
>  create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-04-08  9:58 ` Laszlo Ersek
@ 2021-04-08 11:59   ` Brijesh Singh
  2021-04-09 12:24     ` Laszlo Ersek
  0 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-04-08 11:59 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: brijesh.singh, devel, James Bottomley, Min Xu, Jiewen Yao,
	Tom Lendacky, Jordan Justen, Ard Biesheuvel

Hi Laszlo,

On 4/8/21 4:58 AM, Laszlo Ersek wrote:
> Hi Brijesh,
>
> On 03/24/21 16:31, Brijesh Singh wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D3275&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7Cb8ee3abf81aa4e5b1e6008d8fa74dbbc%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637534728122143364%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=G2AQ%2FCks3%2BbczHJXMwqlqpWpoBJmb0pmxb1VNLw6t%2BA%3D&amp;reserved=0
>>
>> SEV-SNP builds upon existing SEV and SEV-ES functionality while adding
>> new hardware-based memory protections. SEV-SNP adds strong memory integrity
>> protection to help prevent malicious hypervisor-based attacks like data
>> replay, memory re-mapping and more in order to create an isolated memory
>> encryption environment.
>>  
>> This series provides the basic building blocks to support booting the SEV-SNP
>> VMs, it does not cover all the security enhancement introduced by the SEV-SNP
>> such as interrupt protection.
>>
>> Many of the integrity guarantees of SEV-SNP are enforced through a new
>> structure called the Reverse Map Table (RMP). Adding a new page to SEV-SNP
>> VM requires a 2-step process. First, the hypervisor assigns a page to the
>> guest using the new RMPUPDATE instruction. This transitions the page to
>> guest-invalid. Second, the guest validates the page using the new PVALIDATE
>> instruction. The SEV-SNP VMs can use the new "Page State Change Request NAE"
>> defined in the GHCB specification to ask hypervisor to add or remove page
>> from the RMP table.
>>  
>> Each page assigned to the SEV-SNP VM can either be validated or unvalidated,
>> as indicated by the Validated flag in the page's RMP entry. There are two
>> approaches that can be taken for the page validation: Pre-validation and
>> Lazy Validation.
>>   
>> Under pre-validation, the pages are validated prior to first use. And under
>> lazy validation, pages are validated when first accessed. An access to a
>> unvalidated page results in a #VC exception, at which time the exception
>> handler may validate the page. Lazy validation requires careful tracking of
>> the validated pages to avoid validating the same GPA more than once. The
>> recently introduced "Unaccepted" memory type can be used to communicate the
>> unvalidated memory ranges to the Guest OS.
>>
>> At this time we only support the pre-validation. OVMF detects all the available
>> system RAM in the PEI phase. When SEV-SNP is enabled, the memory is validated
>> before it is made available to the EDK2 core.
> Can you describe this in a bit more detail, before I look at the
> individual patches? Specifically, what existing logic in the PEI phase
> was taken, and extended, and how?

One of the key requirement is that the guest private pages much be
validated before the access. If guest tries to access the pages before
the validation then it will result in #VC (page-not-validated)
exception. To avoid the #VC, we propose the validating the memory before
the access. We will incrementally add the support to lazy validate (i.e
validate on access).

Let me try explaining a bit, the page validation process consist of two
steps:

1. Add the pages in the RMP table -- must be done by the hypervisor
using the RMPUPDATE instruction. The guest can use VMGEXIT NAEs to ask
hypervisor to add or remove pages from the RMP table.

2. Guest issue the PVALIDATE instruction -- this sets the validate bit
in the RMP table.

Similar to SEV, the OVMF_CODE.fd is encrypted through the SNP firmware
before the launch. The SNP firmware also validates the memory page after
encrypting. This allows us to boot the initial entry code without guest
going through the validation process.

The OVMF reset vector uses few data pages (e.g page table, early Sec
stack). Access to these data pages will result in #VC. There are two
approaches we can take to validate these data pages:

1. Ask SNP firmware to pre-validate it -- SNP firmware provides an
special command that can be used to pre-validate the pages without
affecting the measurement.

2. Enhance the reset vector code to validate the pages.

For now I choose #1.

The pre-validation performed by the SNP firmware is sufficient to boot
through the SEC phase. The SEC phase later decompress the Fv to a new
memory location. Now we need the OVMF to take over the validation
procedure.  The series extends the MemEncryptSevLib to add a new helper
MemEncryptSevSnpValidateRam(). The helper is used to validate the system
RAM. See patch #12. SEC phase calls the MemEncryptSevSnpValidateRam() to
validate the output buffer used for the decompression. This was
sufficient to boot into the PEI phase, see patch #13. The PEI detects
all the available system RAM. After the memory detection is completed
the PlatformPei calls the AmdSevSnpInitialize(). The initialization
routine iterate through the HOB and calls the
MemEncryptSevSnpValidateRam() to validate all the system RAM. Is it
possible the more system ram can be detected after the PlatformPei is
completed ?

One of the important thing is we should *never* validate the pages
twice. The MemEncryptSevSnpValidateRam() uses a interval search tree to
keep the record of what has been validated. Before validating the range,
it lookup in its tree and if it finds that range is already validated
then do nothing. If it detects an overlap then it will validate only non
overlapping regions -- see patch #14.

The patch #18 extend the MemEncrypt{Set,Clear}PageEncMask() to call the
SNP page state change during the C-bit toggle.

Please let me know if you have any questions. We can hash out the design
here before you taking a closure look at the code.

>
> If there is a particular patch whose commit message is closely related
> to my question, can you point it out? Patch#15 perhaps? (Doesn't seem
> like a big patch; for some reason I'd expect something more complex, but
> perhaps that's only because it builds upon the many earlier patches.)
>
> Thanks,
> Laszlo
>
>> This series does not implements the following SEV-SNP features yet:
>>
>> * CPUID filtering
>> * AP bring up using the new SEV-SNP NAE
>> * Lazy validation
>> * Interrupt security
>>
>> The series is based on commit:
>> e542e05d4f UefiCpuPkg/SmmCpuFeaturesLib: Abstract PcdCpuMaxLogicalProcessorNumber
>>
>> Additional resources
>> ---------------------
>> SEV-SNP whitepaper
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.amd.com%2Fsystem%2Ffiles%2FTechDocs%2FSEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7Cb8ee3abf81aa4e5b1e6008d8fa74dbbc%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637534728122143364%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=uUqlVHhWQ6geGDaNHwxGMpoSpIamB%2F1vHH69h%2FEGUro%3D&amp;reserved=0
>>  
>> APM 2: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.amd.com%2Fsystem%2Ffiles%2FTechDocs%2F24593.pdf&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7Cb8ee3abf81aa4e5b1e6008d8fa74dbbc%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637534728122143364%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=R2kU42jvCDZat8kGZ5gDDz2nFXIawHfXdRW1aovhNK8%3D&amp;reserved=0 (section 15.36)
>>
>> The complete source is available at
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FAMDESE%2Fovmf%2Ftree%2Fsev-snp-rfc-1&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7Cb8ee3abf81aa4e5b1e6008d8fa74dbbc%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637534728122143364%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=yreFg2hr%2F82WYEjxqCmb7pXUdtrRCJRYPrPHgfrWjM8%3D&amp;reserved=0
>>
>> GHCB spec v2:
>>   The draft specification is posted on AMD-SEV-SNP mailing list:
>>    https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.suse.com%2Fmailman%2Fprivate%2Famd-sev-snp%2F&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7Cb8ee3abf81aa4e5b1e6008d8fa74dbbc%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637534728122143364%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=PFV2mA7T%2Fbl2zP5j52kNdT%2FavDMRLWDEDqz6JGusEFg%3D&amp;reserved=0
>>
>>   Copy of the spec is also available at 
>>   https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FAMDESE%2FAMDSEV%2Fblob%2Fsev-snp-devel%2Fdocs%2F56421-Guest_Hypervisor_Communication_Block_Standardization.pdf&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7Cb8ee3abf81aa4e5b1e6008d8fa74dbbc%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637534728122143364%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=U3oKRe5m0NxI0xqv1gBoyh%2BEEX1LVeCWR42rvPh6XZ8%3D&amp;reserved=0
>>
>> GHCB spec v1:
>> SEV-SNP firmware specification:
>>  https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdeveloper.amd.com%2Fsev%2F&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7Cb8ee3abf81aa4e5b1e6008d8fa74dbbc%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637534728122143364%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=G%2BttkORsTJchJ1Fy1iNlD%2B%2BqiQZuwI8md5vhJjEb%2Fn4%3D&amp;reserved=0
>>   
>> Cc: James Bottomley <jejb@linux.ibm.com>
>> Cc: Min Xu <min.m.xu@intel.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Tom Lendacky <thomas.lendacky@amd.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>>
>> Brijesh Singh (19):
>>   OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
>>   OvmfPkg: validate the data pages used in the SEC phase
>>   MdePkg: Expand the SEV MSR to include the SNP definition
>>   OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled()
>>   MdePkg: Define the GHCB GPA structure
>>   UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is
>>     enabled
>>   OvmfPkg: Add a library to support registering GHCB GPA
>>   OvmfPkg: register GHCB gpa for the SEV-SNP guest
>>   MdePkg: Add AsmPvalidate() support
>>   OvmfPkg: Define the Page State Change VMGEXIT structures
>>   OvmfPkg/ResetVector: Invalidate the GHCB page
>>   OvmfPkg/MemEncryptSevLib: Add support to validate system RAM
>>   OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase
>>   OvmfPkg/MemEncryptSevLib: Add support to validate RAM in PEI phase
>>   OvmfPkg/PlatformPei: Validate the system RAM when SNP is active
>>   OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI
>>     phase
>>   OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase
>>   OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc
>>     attribute
>>   OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region
>>
>>  MdePkg/Include/Library/BaseLib.h              |  37 +++
>>  MdePkg/Include/Register/Amd/Fam17Msr.h        |  31 ++-
>>  MdePkg/Include/Register/Amd/Ghcb.h            |  39 ++-
>>  MdePkg/Library/BaseLib/BaseLib.inf            |   1 +
>>  MdePkg/Library/BaseLib/X64/Pvalidate.nasm     |  43 +++
>>  OvmfPkg/Include/Library/GhcbRegisterLib.h     |  27 ++
>>  OvmfPkg/Include/Library/MemEncryptSevLib.h    |  30 +++
>>  .../DxeMemEncryptSevLib.inf                   |   7 +
>>  .../DxeMemEncryptSevLibInternal.c             |  27 ++
>>  .../Ia32/SnpPageStateChange.c                 |  17 ++
>>  .../PeiMemEncryptSevLib.inf                   |   9 +
>>  .../PeiMemEncryptSevLibInternal.c             |  47 ++++
>>  .../SecMemEncryptSevLib.inf                   |   4 +
>>  .../SecMemEncryptSevLibInternal.c             |  39 +++
>>  .../BaseMemEncryptSevLib/SnpPageStateChange.h |  37 +++
>>  .../X64/PeiDxeSnpSetPageState.c               |  63 +++++
>>  .../X64/PeiDxeVirtualMemory.c                 | 151 ++++++++++-
>>  .../X64/PeiSnpSystemRamValidate.c             | 129 +++++++++
>>  .../X64/SecSnpSystemRamValidate.c             |  23 ++
>>  .../X64/SnpPageStateChangeInternal.c          | 254 ++++++++++++++++++
>>  .../X64/SnpPageStateTrack.c                   | 119 ++++++++
>>  .../X64/SnpPageStateTrack.h                   |  36 +++
>>  .../X64/SnpSetPageState.h                     |  27 ++
>>  .../BaseMemEncryptSevLib/X64/VirtualMemory.h  |  19 ++
>>  .../Library/GhcbRegisterLib/GhcbRegisterLib.c |  97 +++++++
>>  .../GhcbRegisterLib/GhcbRegisterLib.inf       |  33 +++
>>  OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf  |   4 +
>>  OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |   7 +
>>  OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c |  45 ++++
>>  OvmfPkg/OvmfPkg.dec                           |  12 +
>>  OvmfPkg/OvmfPkgX64.dsc                        |   1 +
>>  OvmfPkg/OvmfPkgX64.fdf                        |  33 ++-
>>  OvmfPkg/PlatformPei/AmdSev.c                  |  52 ++++
>>  OvmfPkg/PlatformPei/PlatformPei.inf           |   2 +
>>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm  |  24 ++
>>  OvmfPkg/ResetVector/Ia32/PageTables64.asm     | 106 ++++++++
>>  OvmfPkg/ResetVector/ResetVector.inf           |   5 +
>>  OvmfPkg/ResetVector/ResetVector.nasmb         |   4 +
>>  OvmfPkg/Sec/SecMain.c                         | 102 +++++++
>>  OvmfPkg/Sec/SecMain.inf                       |   2 +
>>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   1 +
>>  UefiCpuPkg/Library/MpInitLib/MpEqu.inc        |   1 +
>>  UefiCpuPkg/Library/MpInitLib/MpLib.c          |   2 +
>>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |   2 +
>>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   1 +
>>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm |  51 ++++
>>  UefiCpuPkg/UefiCpuPkg.dec                     |   6 +
>>  47 files changed, 1790 insertions(+), 19 deletions(-)
>>  create mode 100644 MdePkg/Library/BaseLib/X64/Pvalidate.nasm
>>  create mode 100644 OvmfPkg/Include/Library/GhcbRegisterLib.h
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/SnpPageStateChange.c
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/SnpPageStateChange.h
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeSnpSetPageState.c
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SecSnpSystemRamValidate.c
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.c
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateTrack.h
>>  create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpSetPageState.h
>>  create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.c
>>  create mode 100644 OvmfPkg/Library/GhcbRegisterLib/GhcbRegisterLib.inf
>>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-08  6:24             ` [edk2-devel] " Min Xu
@ 2021-04-08 13:31               ` Lendacky, Thomas
  2021-04-09 12:29                 ` Laszlo Ersek
  2021-04-09 13:32                 ` Laszlo Ersek
  0 siblings, 2 replies; 68+ messages in thread
From: Lendacky, Thomas @ 2021-04-08 13:31 UTC (permalink / raw)
  To: Xu, Min M, devel@edk2.groups.io, lersek@redhat.com,
	jejb@linux.ibm.com, Brijesh Singh
  Cc: Yao, Jiewen, Justen, Jordan L, Ard Biesheuvel

On 4/8/21 1:24 AM, Xu, Min M wrote:
> On Wednesday, April 7, 2021 11:03 PM, Laszlo wrote:
>> On 04/07/21 02:44, James Bottomley wrote:
>>> On Wed, 2021-04-07 at 00:21 +0000, Xu, Min M wrote:
>>>> Hi, Laszlo
>>>>
>>>> For Intel TDX supported guest, all processors start in 32-bit
>>>> protected mode, while for Non-Td guest, it starts in 16-bit real
>>>> mode. To make the ResetVector work on both Td-guest and Non-Td guest,
>>>> ResetVector are updated as below:
>>>> ------------------------------------------------------------------
>>>>   ALIGN   16
>>>>   resetVector:
>>>>   ;
>>>>   ; Reset Vector
>>>>   ;
>>>>   ; This is where the processor will begin execution
>>>>   ;
>>>>       nop
>>>>       nop
>>>>       smsw    ax
>>>>       test    al, 1
>>>>       jnz     EarlyBspPmEntry
>>>>       jmp     EarlyBspInitReal16
>>>
>>> Well, then use the rel8 jump like the compiler would in this situation:
>>>
>>>       smsw    ax
>>>       test    al, 1
>>>       jz      1f
>>>       jmp     EarlyBspPmEntry
>>> 1:
>>>       jmp     EarlyBspInitReal16
>>>
>>> So now both entries can be 32k away.
>>
>> The problem is that we need NASM to generate such *shared* entry code that
>> behaves correctly when executed in either 16-bit or 32-bit mode.
>>
>> The rel8 near jumps ("short jumps") are like that -- for example, the
>> "74 cb" opcode decodes to the same "JZ rel8" in both modes.
>>
>> But the rel16 ("non-short") near jumps turn into rel32 near jumps when
>> decoded in 32-bit mode. For example, "E9 cw" decodes to "JMP rel16" in 16-bit
>> mode, but it gets parsed as "E9 cd" (= "JMP rel32") in 32-bit mode.
>>
>> So the idea is to add more BITS directives, for covering the non-short near
>> jumps themselves:
> 
> Yes this is the root cause. TDX requires the startup mode to be 32-bit
> protected mode while the legacy VM startup mode is 16-bit real mode.
> Add more BITS directives can work round this. I have tried it and it works.
> 
> So my initial solution is to use *jmp rel8* because it works in both 16-bit
> and 32-bit mode. But *jmp rel8* depends on the distance which should
> be less than 128 bytes. If more metadata is added in the ResetVector.asm
> then we have to use the BITS solution. 

To me, it sounds like the BITS solution should be the approach you use
from the start.

Thanks,
Tom

>  
>>
>>> ; instructions up to and including the rel8 JZ decode identically ;
>>> between BITS 16 and BITS 32 BITS 16
>>>       smsw    ax
>>>       test    al, 1
>>>       jz     Real
>>>
>>> ; the unconditional near jumps are mode-specific BITS 32
>>>       jmp     near EarlyBspPmEntry
>>> BITS 16
>>> Real:
>>>       jmp     near EarlyBspInitReal16
>>>
>>> ; --------------------
>>>
>>> BITS 16
>>> EarlyBspInitReal16:
>>>       nop
>>>
>>> BITS 32
>>> EarlyBspPmEntry:
>>>       nop
>>
>> $ nasm -f bin jz.nasmb
>>
>> Decoded (executed) in 16-bit mode:
>>
>> $ ndisasm -b 16 -k 7,5 -k 0x10,1 jz
>> 00000000  0F01E0            smsw ax
>> 00000003  A801              test al,0x1
>> 00000005  7405              jz 0xc         ; taken
>> 00000007  skipping 0x5 bytes
>> 0000000C  E90000            jmp word 0xf
>> 0000000F  90                nop
>> 00000010  skipping 0x1 bytes
>>
>> Decoded (executed) in 32-bit mode:
>>
>> $ ndisasm -b 32 -k 0xc,4 jz
>> 00000000  0F01E0            smsw eax
>> 00000003  A801              test al,0x1
>> 00000005  7405              jz 0xc         ; not taken
>> 00000007  E904000000        jmp dword 0x10
>> 0000000C  skipping 0x4 bytes
>> 00000010  90                nop
>>
>>
>> With the garbage *not* hidden:
>>
>> $ ndisasm -b 16 -s 0xc jz
>>
>> 00000000  0F01E0            smsw ax
>> 00000003  A801              test al,0x1
>> 00000005  7405              jz 0xc          ; taken
>> 00000007  E90400            jmp word 0xe    ; garbage
>> 0000000A  0000              add [bx+si],al  ; garbage
>> 0000000C  E90000            jmp word 0xf
>> 0000000F  90                nop
>> 00000010  90                nop             ; garbage
>>
>> $ ndisasm -b 32 -s 0x10 jz
>>
>> 00000000  0F01E0            smsw eax
>> 00000003  A801              test al,0x1
>> 00000005  7405              jz 0xc          ; not taken
>> 00000007  E904000000        jmp dword 0x10
>> 0000000C  E9                db 0xe9         ; garbage
>> 0000000D  0000              add [eax],al    ; garbage
>> 0000000F  90                nop             ; garbage
>> 00000010  90                nop
>>
>> Thanks
>> Laszlo
>>
>>
>>
>> 
>>
> 

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-04-08 11:59   ` Brijesh Singh
@ 2021-04-09 12:24     ` Laszlo Ersek
  2021-04-09 22:43       ` Brijesh Singh
  0 siblings, 1 reply; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-09 12:24 UTC (permalink / raw)
  To: devel, brijesh.singh
  Cc: James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky, Jordan Justen,
	Ard Biesheuvel

On 04/08/21 13:59, Brijesh Singh wrote:
> On 4/8/21 4:58 AM, Laszlo Ersek wrote:
>> On 03/24/21 16:31, Brijesh Singh wrote:

>>> At this time we only support the pre-validation. OVMF detects all the available
>>> system RAM in the PEI phase. When SEV-SNP is enabled, the memory is validated
>>> before it is made available to the EDK2 core.
>> Can you describe this in a bit more detail, before I look at the
>> individual patches? Specifically, what existing logic in the PEI phase
>> was taken, and extended, and how?
> 
> One of the key requirement is that the guest private pages much be
> validated before the access. If guest tries to access the pages before
> the validation then it will result in #VC (page-not-validated)
> exception. To avoid the #VC, we propose the validating the memory before
> the access. We will incrementally add the support to lazy validate (i.e
> validate on access).

What is the primary threat (in simple terms please) that validation is
supposed to prevent?

And, against that particular threat, does pre-validation offer some
protection, or will that only come with lazy validation?

> 
> Let me try explaining a bit, the page validation process consist of two
> steps:
> 
> 1. Add the pages in the RMP table -- must be done by the hypervisor
> using the RMPUPDATE instruction. The guest can use VMGEXIT NAEs to ask
> hypervisor to add or remove pages from the RMP table.
> 
> 2. Guest issue the PVALIDATE instruction -- this sets the validate bit
> in the RMP table.
> 
> Similar to SEV, the OVMF_CODE.fd is encrypted through the SNP firmware
> before the launch. The SNP firmware also validates the memory page after
> encrypting. This allows us to boot the initial entry code without guest
> going through the validation process.
> 
> The OVMF reset vector uses few data pages (e.g page table, early Sec
> stack). Access to these data pages will result in #VC. There are two
> approaches we can take to validate these data pages:
> 
> 1. Ask SNP firmware to pre-validate it -- SNP firmware provides an
> special command that can be used to pre-validate the pages without
> affecting the measurement.

This means the two pflash chips, right?

> 
> 2. Enhance the reset vector code to validate the pages.
> 
> For now I choose #1.
> 
> The pre-validation performed by the SNP firmware is sufficient to boot
> through the SEC phase. The SEC phase later decompress the Fv to a new
> memory location. Now we need the OVMF to take over the validation
> procedure.  The series extends the MemEncryptSevLib to add a new helper
> MemEncryptSevSnpValidateRam(). The helper is used to validate the system
> RAM. See patch #12. SEC phase calls the MemEncryptSevSnpValidateRam() to
> validate the output buffer used for the decompression. This was
> sufficient to boot into the PEI phase, see patch #13.

Two questions here:

- Is ACPI S3 in scope for now?

- We need more areas made accessible in SEC than just the decompression
buffer; for example the temporary SEC/PEI heap and stack, and (IIRC)
various special SEV-ES areas laid out via MEMFD. Where are all of those
handled / enumerated?

> The PEI detects
> all the available system RAM. After the memory detection is completed
> the PlatformPei calls the AmdSevSnpInitialize(). The initialization
> routine iterate through the HOB and calls the
> MemEncryptSevSnpValidateRam() to validate all the system RAM. Is it
> possible the more system ram can be detected after the PlatformPei is
> completed ?

That would cause problems even without SEV-SNP (i.e., with plain SEV),
so I'm not worried about it.

> 
> One of the important thing is we should *never* validate the pages
> twice.

What are the symptoms / consequences of:

- the guest accessing an unvalidated page (I understand it causes a #VC,
but what is the direct result of that, when this series is applied?),

- doubly-validating a page?

The first question is relevant because we should crash as soon as we
touch a page we forgot to validate (we shouldn't let any corruption or
similar spread out until we finally realize there's a problem).

The second question is relevant for security I guess. What attacks
become possible, and/or what symptoms are produced, if we
doubly-validate a page?

Furthermore, IIRC, we have separate #VC handlers for the different
firmware phases; do they behave consistently / identicall when a
#VC(page-not-validated) occurs, when this patch set is applied?

My first question is basically asking whether we can *exclusively* rely
on #VC(page-not-validated) faults to tell us that we missed validating a
particular page. If we can do that, then the job is a bit easier,
because from the GPA, we can more or less also derive *when and where*
we should pre-validate the page (at least until validation is done
completely lazily).

> The MemEncryptSevSnpValidateRam() uses a interval search tree to
> keep the record of what has been validated. Before validating the range,
> it lookup in its tree and if it finds that range is already validated
> then do nothing. If it detects an overlap then it will validate only non
> overlapping regions -- see patch #14.

What data structure is used for implementing the interval tree?

I'm not necessarily looking for a data structure with "nice" asymptotic
time complexity. With pre-validation especially, I think simplicity
(ease of review) is more important for the data structure than
performance. If it's not an actual "tree", we shouldn't call it a
"tree". (An "interval tree" is usually an extension of a Red-Black Tree,
and that's not the simplest data structure in existence; although edk2
does offer an rbtree library.)

Furthermore, what you describe above is called idempotency. No matter
how many times we attempt to validate a range, it may (or may not even)
cause an actual change in the first action only. Is this property
(=idempotency) an inherent requirement of the technology, or is it a
simplification of the implementation? Put differently: if you called
CpuDeadLoop() in the validation function any time an overlapping
validation request were received, would that hugely complicate the call
sites?

I'm kind of "obsessing" about idempotency because you say we must
*never* doubly-validate a page, so the *difference* between:
- explicitly crashing on such an attempt,
- and silently ignoring such an attempt,
may be meaningful.

It's kind of the difference between "oops this is a call site *bug*, but
we patched it up", vs. "this is expected of call sites, we should just
handle it internally".

> The patch #18 extend the MemEncrypt{Set,Clear}PageEncMask() to call the
> SNP page state change during the C-bit toggle.

- When exactly do we invalidate?

- Does the time and place of invalidation depend on whether we perform
pre-validation, or lazy validation?

- Is page invalidation idempotent too?

- What is the consequence (security impact) if we forget invalidation?

- There are four page states I can imagine:
  - encrypted for host access, valid for guest access
  - encrypted for host access, invalid for guest access
  - decrypted for host access, valid for guest access
  - decrypted for host access, invalid for guest access

Does a state exist, from these four, that's never used? (Potentially
caught by the hardware?)

Do the patches highlight / explain the validity transitions, in
comments? My understanding is that the C-bit toggle and the guest access
valid/invalid toggle are separate actions, so "middle" states do exist,
but perhaps only temporarily.

I'm curious how it works, for example, with variousvirtio transfers (bus
master read, bus master write, bus master common buffer). In the
IoMmuDxe driver minimally, we access memory both through PTEs with the
C-bit set and through PTEs with the C-bit clear, meaning that
"encrypted, valid", and "decrypted, valid" are both required states. But
that seems to conflict with the notion that "C-bit toggle" be directly
coupled with a "validity toggle".

Put differently, I'm happy that modifying IoMmuDxe appears unnecessary,
but then that tells me I'm missing something about the state transitions
between the above *four* states.

> 
> Please let me know if you have any questions. We can hash out the design
> here before you taking a closure look at the code.

Sorry that I've been (and am being) slow to start reviewing this series.

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-08 13:31               ` Lendacky, Thomas
@ 2021-04-09 12:29                 ` Laszlo Ersek
  2021-04-09 13:32                 ` Laszlo Ersek
  1 sibling, 0 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-09 12:29 UTC (permalink / raw)
  To: Tom Lendacky, Xu, Min M, devel@edk2.groups.io, jejb@linux.ibm.com,
	Brijesh Singh
  Cc: Yao, Jiewen, Justen, Jordan L, Ard Biesheuvel

On 04/08/21 15:31, Tom Lendacky wrote:
> On 4/8/21 1:24 AM, Xu, Min M wrote:
>> On Wednesday, April 7, 2021 11:03 PM, Laszlo wrote:
>>> On 04/07/21 02:44, James Bottomley wrote:
>>>> On Wed, 2021-04-07 at 00:21 +0000, Xu, Min M wrote:
>>>>> Hi, Laszlo
>>>>>
>>>>> For Intel TDX supported guest, all processors start in 32-bit
>>>>> protected mode, while for Non-Td guest, it starts in 16-bit real
>>>>> mode. To make the ResetVector work on both Td-guest and Non-Td guest,
>>>>> ResetVector are updated as below:
>>>>> ------------------------------------------------------------------
>>>>>   ALIGN   16
>>>>>   resetVector:
>>>>>   ;
>>>>>   ; Reset Vector
>>>>>   ;
>>>>>   ; This is where the processor will begin execution
>>>>>   ;
>>>>>       nop
>>>>>       nop
>>>>>       smsw    ax
>>>>>       test    al, 1
>>>>>       jnz     EarlyBspPmEntry
>>>>>       jmp     EarlyBspInitReal16
>>>>
>>>> Well, then use the rel8 jump like the compiler would in this situation:
>>>>
>>>>       smsw    ax
>>>>       test    al, 1
>>>>       jz      1f
>>>>       jmp     EarlyBspPmEntry
>>>> 1:
>>>>       jmp     EarlyBspInitReal16
>>>>
>>>> So now both entries can be 32k away.
>>>
>>> The problem is that we need NASM to generate such *shared* entry code that
>>> behaves correctly when executed in either 16-bit or 32-bit mode.
>>>
>>> The rel8 near jumps ("short jumps") are like that -- for example, the
>>> "74 cb" opcode decodes to the same "JZ rel8" in both modes.
>>>
>>> But the rel16 ("non-short") near jumps turn into rel32 near jumps when
>>> decoded in 32-bit mode. For example, "E9 cw" decodes to "JMP rel16" in 16-bit
>>> mode, but it gets parsed as "E9 cd" (= "JMP rel32") in 32-bit mode.
>>>
>>> So the idea is to add more BITS directives, for covering the non-short near
>>> jumps themselves:
>>
>> Yes this is the root cause. TDX requires the startup mode to be 32-bit
>> protected mode while the legacy VM startup mode is 16-bit real mode.
>> Add more BITS directives can work round this. I have tried it and it works.
>>
>> So my initial solution is to use *jmp rel8* because it works in both 16-bit
>> and 32-bit mode. But *jmp rel8* depends on the distance which should
>> be less than 128 bytes. If more metadata is added in the ResetVector.asm
>> then we have to use the BITS solution. 
> 
> To me, it sounds like the BITS solution should be the approach you use
> from the start.

I agree; we have an extensible approach in place already (with the
GUIDed struct list), and QEMU already knows about one magic GPA (for
locating the head of the list). Using one conditional rel8 jump, and two
non-conditional mode-specific (rel16/rel32) jumps, doesn't cost much in
the assembly code, it's compatible with future GUID additions, and
shouldn't affect QEMU's GUIDed struct list traversal.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-08 13:31               ` Lendacky, Thomas
  2021-04-09 12:29                 ` Laszlo Ersek
@ 2021-04-09 13:32                 ` Laszlo Ersek
  2021-04-09 13:44                   ` Yao, Jiewen
  1 sibling, 1 reply; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-09 13:32 UTC (permalink / raw)
  To: Xu, Min M
  Cc: devel, thomas.lendacky, jejb@linux.ibm.com, Brijesh Singh,
	Yao, Jiewen, Justen, Jordan L, Ard Biesheuvel, Paolo Bonzini

Hi Min,

On 04/08/21 15:31, Lendacky, Thomas wrote:
> On 4/8/21 1:24 AM, Xu, Min M wrote:

>> Yes this is the root cause. TDX requires the startup mode to be 32-bit
>> protected mode while the legacy VM startup mode is 16-bit real mode.
>> Add more BITS directives can work round this. I have tried it and it works.
>>
>> So my initial solution is to use *jmp rel8* because it works in both 16-bit
>> and 32-bit mode. But *jmp rel8* depends on the distance which should
>> be less than 128 bytes. If more metadata is added in the ResetVector.asm
>> then we have to use the BITS solution. 
> 
> To me, it sounds like the BITS solution should be the approach you use
> from the start.

BTW, have you considered using a separate ResetVector module for TDX?
That would obviate this multi-mode trickery. (Most recently raised by
Paolo.)

I think TDX will need a separate platform DSC / FDF / fw binary anyway.
I realize that's not a done deal yet; it may depend on who provides the
firmware binary (guest owner or cloud owner) -- of course the guest
owner will have to perform the attestation in either case, but the
"provenance" question remains open, IIUC.

And even if TDX gets its own firmware platform, it's a separate question
whether the ResetVector module itself should be split. I'm inclined to
think a separation at that level would make development and maintenance
easier.

(FWIW, Xen PVH has its own reset vector module, in OvmfPkg/XenResetVector.)

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-09 13:32                 ` Laszlo Ersek
@ 2021-04-09 13:44                   ` Yao, Jiewen
  2021-04-09 14:11                     ` separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest] Laszlo Ersek
  0 siblings, 1 reply; 68+ messages in thread
From: Yao, Jiewen @ 2021-04-09 13:44 UTC (permalink / raw)
  To: Laszlo Ersek, Xu, Min M
  Cc: devel@edk2.groups.io, thomas.lendacky@amd.com, jejb@linux.ibm.com,
	Brijesh Singh, Justen, Jordan L, Ard Biesheuvel, Paolo Bonzini

Hi Laszlo
Thanks.

We did provide a separate binary in the beginning - see https://github.com/tianocore/edk2-staging/tree/TDVF, with same goal - easy to maintain and develop. A clean solution, definitely.

However, we got requirement to deliver one binary solution together with 1) normal OVMF, 2) AMD-SEV, 3) Intel-TDX.
Now, we are struggling to merge them......

For DXE, we hope to isolate TDX driver whenever it is possible.
But we only have one reset vector here. Sigh...



> -----Original Message-----
> From: Laszlo Ersek <lersek@redhat.com>
> Sent: Friday, April 9, 2021 9:33 PM
> To: Xu, Min M <min.m.xu@intel.com>
> Cc: devel@edk2.groups.io; thomas.lendacky@amd.com; jejb@linux.ibm.com;
> Brijesh Singh <brijesh.singh@amd.com>; Yao, Jiewen <jiewen.yao@intel.com>;
> Justen, Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [edk2-devel] [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and
> Cpuid page for the SEV-SNP guest
> 
> Hi Min,
> 
> On 04/08/21 15:31, Lendacky, Thomas wrote:
> > On 4/8/21 1:24 AM, Xu, Min M wrote:
> 
> >> Yes this is the root cause. TDX requires the startup mode to be 32-bit
> >> protected mode while the legacy VM startup mode is 16-bit real mode.
> >> Add more BITS directives can work round this. I have tried it and it works.
> >>
> >> So my initial solution is to use *jmp rel8* because it works in both 16-bit
> >> and 32-bit mode. But *jmp rel8* depends on the distance which should
> >> be less than 128 bytes. If more metadata is added in the ResetVector.asm
> >> then we have to use the BITS solution.
> >
> > To me, it sounds like the BITS solution should be the approach you use
> > from the start.
> 
> BTW, have you considered using a separate ResetVector module for TDX?
> That would obviate this multi-mode trickery. (Most recently raised by
> Paolo.)
> 
> I think TDX will need a separate platform DSC / FDF / fw binary anyway.
> I realize that's not a done deal yet; it may depend on who provides the
> firmware binary (guest owner or cloud owner) -- of course the guest
> owner will have to perform the attestation in either case, but the
> "provenance" question remains open, IIUC.
> 
> And even if TDX gets its own firmware platform, it's a separate question
> whether the ResetVector module itself should be split. I'm inclined to
> think a separation at that level would make development and maintenance
> easier.
> 
> (FWIW, Xen PVH has its own reset vector module, in OvmfPkg/XenResetVector.)
> 
> Thanks
> Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-09 13:44                   ` Yao, Jiewen
@ 2021-04-09 14:11                     ` Laszlo Ersek
  2021-04-12  8:35                       ` Dr. David Alan Gilbert
  0 siblings, 1 reply; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-09 14:11 UTC (permalink / raw)
  To: Yao, Jiewen, Xu, Min M
  Cc: devel@edk2.groups.io, thomas.lendacky@amd.com, jejb@linux.ibm.com,
	Brijesh Singh, Justen, Jordan L, Ard Biesheuvel, Paolo Bonzini,
	Dr. David Alan Gilbert, Nathaniel McCallum

On 04/09/21 15:44, Yao, Jiewen wrote:
> Hi Laszlo
> Thanks.
> 
> We did provide a separate binary in the beginning - see https://github.com/tianocore/edk2-staging/tree/TDVF, with same goal - easy to maintain and develop. A clean solution, definitely.
> 
> However, we got requirement to deliver one binary solution together with 1) normal OVMF, 2) AMD-SEV, 3) Intel-TDX.
> Now, we are struggling to merge them......
> 
> For DXE, we hope to isolate TDX driver whenever it is possible.
> But we only have one reset vector here. Sigh...

Can we please pry a little bit at that "one binary" requirement?

Ultimately the "guest bundle" is going to be composed by much
higher-level code, I expect (such as some userspace code, written in
python or similar); selecting a firmware binary in such an environment
is surely easier than handling this "polymorphism" in the most
restrictive software environment imaginable (reset vector assembly code
in the guest)?

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-04-09 12:24     ` Laszlo Ersek
@ 2021-04-09 22:43       ` Brijesh Singh
  2021-04-12 16:23         ` Laszlo Ersek
  0 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-04-09 22:43 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: brijesh.singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel


On 4/9/21 7:24 AM, Laszlo Ersek wrote:
> On 04/08/21 13:59, Brijesh Singh wrote:
>> On 4/8/21 4:58 AM, Laszlo Ersek wrote:
>>> On 03/24/21 16:31, Brijesh Singh wrote:
>>>> At this time we only support the pre-validation. OVMF detects all the available
>>>> system RAM in the PEI phase. When SEV-SNP is enabled, the memory is validated
>>>> before it is made available to the EDK2 core.
>>> Can you describe this in a bit more detail, before I look at the
>>> individual patches? Specifically, what existing logic in the PEI phase
>>> was taken, and extended, and how?
>> One of the key requirement is that the guest private pages much be
>> validated before the access. If guest tries to access the pages before
>> the validation then it will result in #VC (page-not-validated)
>> exception. To avoid the #VC, we propose the validating the memory before
>> the access. We will incrementally add the support to lazy validate (i.e
>> validate on access).
> What is the primary threat (in simple terms please) that validation is
> supposed to prevent?


To protect against the memory re-mapping attack the guest pages must be
validated. The idea is that every guest page can map only to a single
physical memory page at one time.


> And, against that particular threat, does pre-validation offer some
> protection, or will that only come with lazy validation?

For the hardware it does not matter how the memory was validated -- lazy
vs prevalidate. Both approaches use the PVALIDATE instruction to
validate the page.

In the case of pre-validation, the memory is validated before the
access. Whereas in the lazy validation, the access will cause a fault
and fault handler should validate the page and retry the access. Its
similar to a page fault handler, OS can populate the page table or back
the pages on demand.

The only downside of pre-validation is, we will take a hit on the boot
time. The GHCB spec provides method by which we can batch multiple
requests at once to minimize the context switches.


>
>> Let me try explaining a bit, the page validation process consist of two
>> steps:
>>
>> 1. Add the pages in the RMP table -- must be done by the hypervisor
>> using the RMPUPDATE instruction. The guest can use VMGEXIT NAEs to ask
>> hypervisor to add or remove pages from the RMP table.
>>
>> 2. Guest issue the PVALIDATE instruction -- this sets the validate bit
>> in the RMP table.
>>
>> Similar to SEV, the OVMF_CODE.fd is encrypted through the SNP firmware
>> before the launch. The SNP firmware also validates the memory page after
>> encrypting. This allows us to boot the initial entry code without guest
>> going through the validation process.
>>
>> The OVMF reset vector uses few data pages (e.g page table, early Sec
>> stack). Access to these data pages will result in #VC. There are two
>> approaches we can take to validate these data pages:
>>
>> 1. Ask SNP firmware to pre-validate it -- SNP firmware provides an
>> special command that can be used to pre-validate the pages without
>> affecting the measurement.
> This means the two pflash chips, right?


This does not need to be two pflash chips. The SNP firmware command does
not know anything about the ROM or pflash chip. The command accepts the
system physical address that need to be validated by the firmware. In
patch #2, OVMF provides a range of data pages that need to be validated
by the SNP firmware before booting the guest.

>
>> 2. Enhance the reset vector code to validate the pages.
>>
>> For now I choose #1.
>>
>> The pre-validation performed by the SNP firmware is sufficient to boot
>> through the SEC phase. The SEC phase later decompress the Fv to a new
>> memory location. Now we need the OVMF to take over the validation
>> procedure.  The series extends the MemEncryptSevLib to add a new helper
>> MemEncryptSevSnpValidateRam(). The helper is used to validate the system
>> RAM. See patch #12. SEC phase calls the MemEncryptSevSnpValidateRam() to
>> validate the output buffer used for the decompression. This was
>> sufficient to boot into the PEI phase, see patch #13.
> Two questions here:
>
> - Is ACPI S3 in scope for now?


Its not in the scope yet. I have not looked at it.


>
> - We need more areas made accessible in SEC than just the decompression
> buffer; for example the temporary SEC/PEI heap and stack, and (IIRC)
> various special SEV-ES areas laid out via MEMFD. Where are all of those
> handled / enumerated?


Sorry, I simplified my response by saying just decompression. You are
right that its more than the decompression buffer. In my current patch,
the SEC phase validates all the memory up to
PcdOvmfDecompressionScratchEnd (which includes more than just output
buffer). See patch #13.

>
>> The PEI detects
>> all the available system RAM. After the memory detection is completed
>> the PlatformPei calls the AmdSevSnpInitialize(). The initialization
>> routine iterate through the HOB and calls the
>> MemEncryptSevSnpValidateRam() to validate all the system RAM. Is it
>> possible the more system ram can be detected after the PlatformPei is
>> completed ?
> That would cause problems even without SEV-SNP (i.e., with plain SEV),
> so I'm not worried about it.
>
>> One of the important thing is we should *never* validate the pages
>> twice.
> What are the symptoms / consequences of:
>
> - the guest accessing an unvalidated page (I understand it causes a #VC,
> but what is the direct result of that, when this series is applied?),


After the series it applied, an access to an unvalidated page will
result in #VC. The series does not extend the VC handler to handle the
page-not-validated error code. The current VC handler
[InternalVmgExitHandleVc()] will inject a #GP to terminate the guest if
#VC is raised for unvalidated page.


>
> - doubly-validating a page?
>
> The first question is relevant because we should crash as soon as we
> touch a page we forgot to validate (we shouldn't let any corruption or
> similar spread out until we finally realize there's a problem).
>
> The second question is relevant for security I guess. What attacks
> become possible, and/or what symptoms are produced, if we
> doubly-validate a page?

Assuming that guest validates its memory, this guarantees the injective
mapping between GPAs and SPAs.

As I noted that the page validation process consist of two steps #1
RMPUPDATE and 2#PVALIDATE. The RMPUPDATE is a hypervisor instruction and
PVALIDATE is a guest instruction. A malicious hypervisor can remap gpa
to different physical address in RMP table using the RMPUPDATE just
before the second PVALIDATE instruction is executed. The guest need to
ensure that it does not leave the security vulnerability that can be
exploited by a malicious hypervisor. The SNP white paper recommends that
a guest OS should keep track of what is has validate so that it can
avoid getting into these situation.


>
> Furthermore, IIRC, we have separate #VC handlers for the different
> firmware phases; do they behave consistently / identicall when a
> #VC(page-not-validated) occurs, when this patch set is applied?
>
> My first question is basically asking whether we can *exclusively* rely
> on #VC(page-not-validated) faults to tell us that we missed validating a
> particular page. If we can do that, then the job is a bit easier,
> because from the GPA, we can more or less also derive *when and where*
> we should pre-validate the page (at least until validation is done
> completely lazily).

Yes, we should be able to rely #VC to tell us that we missed validating
the page. In my this series so far I have not seen a #VC due to
page-not-validated because we have carefully validated the pages before
they are accessed. I am thinking that #VC(page-not-validated) should be
treated as an error in the first phase. Incrementally we will add the
lazy validation, in which the #VC(page-not-validated) will provide a
hint as to what has not been validated.


>
>> The MemEncryptSevSnpValidateRam() uses a interval search tree to
>> keep the record of what has been validated. Before validating the range,
>> it lookup in its tree and if it finds that range is already validated
>> then do nothing. If it detects an overlap then it will validate only non
>> overlapping regions -- see patch #14.
> What data structure is used for implementing the interval tree?
>
> I'm not necessarily looking for a data structure with "nice" asymptotic
> time complexity. With pre-validation especially, I think simplicity
> (ease of review) is more important for the data structure than
> performance. If it's not an actual "tree", we shouldn't call it a
> "tree". (An "interval tree" is usually an extension of a Red-Black Tree,
> and that's not the simplest data structure in existence; although edk2
> does offer an rbtree library.)


I am using binary search tree. Currently, we don't have many nodes to
track. Most of the guest memory is private and are validated before we
exit from the PEI phase.

> Furthermore, what you describe above is called idempotency. No matter
> how many times we attempt to validate a range, it may (or may not even)
> cause an actual change in the first action only. Is this property
> (=idempotency) an inherent requirement of the technology, or is it a
> simplification of the implementation? Put differently: if you called
> CpuDeadLoop() in the validation function any time an overlapping
> validation request were received, would that hugely complicate the call
> sites?

>From the technology point of view you can validate the same page again
and again. Hardware does not force any restriction. To protect against
time-based remap attack a guest also need to ensure that it does not
blindly validates the pages otherwise guest is taking risk. Avoiding a
double validation is strong recommendation. In current implementation,
the validation burden is not put on the caller. Caller calls standard
APIs to toggle the encryption attribute. Under the hood, if we see that
page is already validated then no need to issue the PVALIDATE. If the
page is getting changed from C=1 -> 0 then unvalidate it.


> I'm kind of "obsessing" about idempotency because you say we must
> *never* doubly-validate a page, so the *difference* between:
> - explicitly crashing on such an attempt,
> - and silently ignoring such an attempt,
> may be meaningful.


I said we should never doubly validate to avoid creating a vulnerability
that can later be exploited by the malicious hypervisor. Currently we
lookup address in our tree to ensure that we don't do a double
validation. The PVALIDATE instruction also can be used to determine if
we are attempting to validate an already validated page. In current
implementation after the PVALIDATE completion, I check the rflags.CF to
ensure that its not a double validation condition. If its double
validation then abort the guest. Its bug in the guest.

> It's kind of the difference between "oops this is a call site *bug*, but
> we patched it up", vs. "this is expected of call sites, we should just
> handle it internally".

Our attempt should be to patch to avoid the double validation. I would
still check the status of PVALIDATE, if instruction detects it as a
double validation then we have a bug in our tracking and should abort
the guest.

>
>> The patch #18 extend the MemEncrypt{Set,Clear}PageEncMask() to call the
>> SNP page state change during the C-bit toggle.
> - When exactly do we invalidate?

Basically before the page is transition from C=1 -> 0 in the page table.

>
> - Does the time and place of invalidation depend on whether we perform
> pre-validation, or lazy validation?
It does not matter. Both type of validation update the Validated bit in
the RMP table. The important thing is that invalidation can happen only
for a already validated private page. If the page is shared then
invalidation will result in #GP.
>
> - Is page invalidation idempotent too?
We should treat this as idempotent too. As I said, tacking the page
state is strongly recommended for the security reason. The SNP white
paper has a section dedicated for the validation and may able to clarify
things which is not covered in my response.
>
> - What is the consequence (security impact) if we forget invalidation?
I can't think of a security implication of it but if we forget to
invalidate then it can lead issues in the tracking data structure when
it comes to validation next time. e.g a page was mapped as C=1 and we
changed it to C=0 without invalidating it. Now if the same page is being
changed from C=0 -> 1 then tracking data structure may think that page
is already validated and will skip the validation process and thus
access will result in #VC (page-not-validated).
>
> - There are four page states I can imagine:
>   - encrypted for host access, valid for guest access
>   - encrypted for host access, invalid for guest access
>   - decrypted for host access, valid for guest access
>   - decrypted for host access, invalid for guest access
>
> Does a state exist, from these four, that's never used? (Potentially
> caught by the hardware?)

I would not mix the page state with the host access. Basically there is
two page state exist

- Guest-Valid

- Guest-Invalid

By default all the guest memory is in Guest-Invalid state on boot. The
PVALIDATE instruction is used to transition from Valid->Invalid. Guest
can use the PVALIDATE to change the state from Valid to Invalid.

A guest private page (i.e page mapped C=1) must be in Guest Valid state.
If hardware see that a private access page is not in the Guest-Valid
state then it will trigger #VC.


>
> Do the patches highlight / explain the validity transitions, in
> comments? My understanding is that the C-bit toggle and the guest access
> valid/invalid toggle are separate actions, so "middle" states do exist,
> but perhaps only temporarily.

I have tried to document the steps in the patch description and I can
add more comments as required. When SEV-SNP is active then we need to
invalidate the page before toggling the C-bit from C=1 -> 0 and
similarly after the page is toggled from C=0 -> 1 we must validate it.


> I'm curious how it works, for example, with variousvirtio transfers (bus
> master read, bus master write, bus master common buffer). In the
> IoMmuDxe driver minimally, we access memory both through PTEs with the
> C-bit set and through PTEs with the C-bit clear, meaning that
> "encrypted, valid", and "decrypted, valid" are both required states. But
> that seems to conflict with the notion that "C-bit toggle" be directly
> coupled with a "validity toggle".

That should not be an issue. Each page have entry in the RMP table. So,
you can have one page as guest invalid and another page as a guest valid.

> Put differently, I'm happy that modifying IoMmuDxe appears unnecessary,
> but then that tells me I'm missing something about the state transitions
> between the above *four* states.


Only thing which I would propose changing in IoMmuDxe driver is to use a
pre-allocated buffer and avoid toggling the C-bit on every read/write.
In past the C-bit toggle was all contained inside the guest. But when
SNP is enabled, each C-bit toggle causes two additional steps #1
RMPUPDATE and #2 PVALIDATE. The RMPUPDATE is requested using the VMEXIT
so it will cause a VM context switch. I would prefer to avoid it.

My current patch set does not have this optimization yet.


>
>> Please let me know if you have any questions. We can hash out the design
>> here before you taking a closure look at the code.
> Sorry that I've been (and am being) slow to start reviewing this series.


No worries, take your time. Thank you for all your feedback.

-Brijesh

>
> Thanks,
> Laszlo
>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-09 14:11                     ` separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest] Laszlo Ersek
@ 2021-04-12  8:35                       ` Dr. David Alan Gilbert
  2021-04-12 11:54                         ` [edk2-devel] " Yao, Jiewen
  0 siblings, 1 reply; 68+ messages in thread
From: Dr. David Alan Gilbert @ 2021-04-12  8:35 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: Yao, Jiewen, Xu, Min M, devel@edk2.groups.io,
	thomas.lendacky@amd.com, jejb@linux.ibm.com, Brijesh Singh,
	Justen, Jordan L, Ard Biesheuvel, Paolo Bonzini,
	Nathaniel McCallum

* Laszlo Ersek (lersek@redhat.com) wrote:
> On 04/09/21 15:44, Yao, Jiewen wrote:
> > Hi Laszlo
> > Thanks.
> > 
> > We did provide a separate binary in the beginning - see https://github.com/tianocore/edk2-staging/tree/TDVF, with same goal - easy to maintain and develop. A clean solution, definitely.
> > 
> > However, we got requirement to deliver one binary solution together with 1) normal OVMF, 2) AMD-SEV, 3) Intel-TDX.
> > Now, we are struggling to merge them......
> > 
> > For DXE, we hope to isolate TDX driver whenever it is possible.
> > But we only have one reset vector here. Sigh...
> 
> Can we please pry a little bit at that "one binary" requirement?
> 
> Ultimately the "guest bundle" is going to be composed by much
> higher-level code, I expect (such as some userspace code, written in
> python or similar); selecting a firmware binary in such an environment
> is surely easier than handling this "polymorphism" in the most
> restrictive software environment imaginable (reset vector assembly code
> in the guest)?

I think also there's a security argument here; some people like to
measure security in kloc's; so having your secure boot image as small
as possible for the environment you're actually running does make some
sense, which favours the 2 image idea.

Dave

> Thanks
> Laszlo
-- 
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-12  8:35                       ` Dr. David Alan Gilbert
@ 2021-04-12 11:54                         ` Yao, Jiewen
  2021-04-12 14:33                           ` James Bottomley
  0 siblings, 1 reply; 68+ messages in thread
From: Yao, Jiewen @ 2021-04-12 11:54 UTC (permalink / raw)
  To: devel@edk2.groups.io, dgilbert@redhat.com, Laszlo Ersek
  Cc: Xu, Min M, thomas.lendacky@amd.com, jejb@linux.ibm.com,
	Brijesh Singh, Justen, Jordan L, Ard Biesheuvel, Paolo Bonzini,
	Nathaniel McCallum, Yao, Jiewen

I totally agree with you that from security perspective, the best idea to isolate AMD SEV/Intel TDX from standard OVMF.

Do you want to propose move AMD SEV support to another SEC?

> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Dr. David
> Alan Gilbert
> Sent: Monday, April 12, 2021 4:35 PM
> To: Laszlo Ersek <lersek@redhat.com>
> Cc: Yao, Jiewen <jiewen.yao@intel.com>; Xu, Min M <min.m.xu@intel.com>;
> devel@edk2.groups.io; thomas.lendacky@amd.com; jejb@linux.ibm.com;
> Brijesh Singh <brijesh.singh@amd.com>; Justen, Jordan L
> <jordan.l.justen@intel.com>; Ard Biesheuvel <ardb+tianocore@kernel.org>;
> Paolo Bonzini <pbonzini@redhat.com>; Nathaniel McCallum
> <npmccallum@redhat.com>
> Subject: Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg:
> Reserve the Secrets and Cpuid page for the SEV-SNP guest]
> 
> * Laszlo Ersek (lersek@redhat.com) wrote:
> > On 04/09/21 15:44, Yao, Jiewen wrote:
> > > Hi Laszlo
> > > Thanks.
> > >
> > > We did provide a separate binary in the beginning - see
> https://github.com/tianocore/edk2-staging/tree/TDVF, with same goal - easy to
> maintain and develop. A clean solution, definitely.
> > >
> > > However, we got requirement to deliver one binary solution together with 1)
> normal OVMF, 2) AMD-SEV, 3) Intel-TDX.
> > > Now, we are struggling to merge them......
> > >
> > > For DXE, we hope to isolate TDX driver whenever it is possible.
> > > But we only have one reset vector here. Sigh...
> >
> > Can we please pry a little bit at that "one binary" requirement?
> >
> > Ultimately the "guest bundle" is going to be composed by much
> > higher-level code, I expect (such as some userspace code, written in
> > python or similar); selecting a firmware binary in such an environment
> > is surely easier than handling this "polymorphism" in the most
> > restrictive software environment imaginable (reset vector assembly code
> > in the guest)?
> 
> I think also there's a security argument here; some people like to
> measure security in kloc's; so having your secure boot image as small
> as possible for the environment you're actually running does make some
> sense, which favours the 2 image idea.
> 
> Dave
> 
> > Thanks
> > Laszlo
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
> 
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-12 11:54                         ` [edk2-devel] " Yao, Jiewen
@ 2021-04-12 14:33                           ` James Bottomley
  2021-04-14 23:34                             ` erdemaktas
  0 siblings, 1 reply; 68+ messages in thread
From: James Bottomley @ 2021-04-12 14:33 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io, dgilbert@redhat.com,
	Laszlo Ersek
  Cc: Xu, Min M, thomas.lendacky@amd.com, Brijesh Singh,
	Justen, Jordan L, Ard Biesheuvel, Paolo Bonzini,
	Nathaniel McCallum

On Mon, 2021-04-12 at 11:54 +0000, Yao, Jiewen wrote:
> I totally agree with you that from security perspective, the best
> idea to isolate AMD SEV/Intel TDX from standard OVMF.

There's a big difference between building tuned binaries and separating
the subsystems entirely.  Ideally we don't want customers running
images to have to build them differently for Intel or AMD (a bit like
how QEMU/KVM hide the VM differences from users), and Confidential
Computing shares a huge amount of interface similarity, so we wouldn't
want that separated.  I think the rule should be that if both Intel and
AMD expose a feature in different ways, OVMF tries to expose a uniform
API for that feature over two differing implementations.

> Do you want to propose move AMD SEV support to another SEC?

You mean have an entirely separate SEC for AMD, OVMF and Intel?  I
really wouldn't do that: much that's in the SEC: page table setup,
memory mapping and decompression is common to all of them.  This all
follows for a lot of the components.

To build separate binaries, we just need separate dsc and fdf files. 
Then I think the goal would be to share as much as possible to avoid
duplicating the maintenance and possibly diverging the user API.

James



^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-03-24 15:31 ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Brijesh Singh
  2021-04-06  8:11   ` Min Xu
@ 2021-04-12 14:52   ` Brijesh Singh
  2021-04-13  9:49     ` Laszlo Ersek
  1 sibling, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-04-12 14:52 UTC (permalink / raw)
  To: devel
  Cc: brijesh.singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel, Laszlo Ersek

Hi James and Laszlo,

I was planning to work to add the support to reserve the Secrets and
CPUID page in E820 map and then create the EFI configuration table entry
for it so that guest OS can reach to it. We have two packages
"SecretDxe" and "SecretPei" in OvmfPkg/AmdSev. Any issues if I use them
in the OvmfPkg.dsc ? Here is what I was thinking:

1) Rename the PcdSevLaunchSecretBase -> PcdSevSecretsBase

2) When SNP is enabled then VMM use this page as secrets page for the SNP

3) When SEV or SEV-ES is enabled then VMM uses this page as a launch
secret page

This will allow me to drop PcdOvmfSnpSecretsBase. This will not just
save 4-bytes but also minimize the code duplication.

Thoughts ?

-Brijesh

On 3/24/21 10:31 AM, Brijesh Singh wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
>
> During the SEV-SNP guest launch sequence, two special pages need to
> be inserted, the secrets page and cpuid page. The secrets page,
> contain the VM platform communication keys. The guest BIOS and OS
> can use this key to communicate with the SEV firmware to get the
> attestation report. The Cpuid page, contain the CPUIDs entries
> filtered through the AMD-SEV firmware.
>
> The VMM will locate the secrets and cpuid page addresses through a
> fixed GUID and pass them to SEV firmware to populate further.
> For more information about the page content, see the SEV-SNP spec.
>
> To simplify the pre-validation range calculation in the next patch,
> the CPUID and Secrets pages are moved to the start of the
> MEMFD_BASE_ADDRESS.
>
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  OvmfPkg/OvmfPkg.dec                          |  8 +++++++
>  OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
>  OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
>  OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
>  5 files changed, 48 insertions(+), 9 deletions(-)
>
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 4348bb45c6..062926772d 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -317,6 +317,14 @@
>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
>  
> +  ## The base address of the CPUID page used by SEV-SNP
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
> +
> +  ## The base address of the Secrets page used by SEV-SNP
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
> +
>  [PcdsDynamic, PcdsDynamicEx]
>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> index d519f85328..ea214600be 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -67,27 +67,33 @@ ErasePolarity = 1
>  BlockSize     = 0x10000
>  NumBlocks     = 0xD0
>  
> -0x000000|0x006000
> +0x000000|0x001000
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
> +
> +0x001000|0x001000
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
> +
> +0x002000|0x006000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
>  
> -0x006000|0x001000
> +0x008000|0x001000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
>  
> -0x007000|0x001000
> +0x009000|0x001000
>  gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>  
> -0x008000|0x001000
> +0x00A000|0x001000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>  
> -0x009000|0x002000
> +0x00B000|0x002000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>  
> -0x00B000|0x001000
> -gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
> -
> -0x00C000|0x001000
> +0x00D000|0x001000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
>  
> +0x00F000|0x001000
> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
> +
>  0x010000|0x010000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>  
> diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> index 9c0b5853a4..5456f02924 100644
> --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
> @@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart + 15) % 16)) DB 0
>  ;
>  guidedStructureStart:
>  
> +;
> +; SEV-SNP boot support
> +;
> +; sevSnpBlock:
> +;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must be
> +;   reserved by the BIOS at a RAM area defined by SEV_SNP_SECRETS_PAGE
> +;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
> +;   SEV-SNP boot block.
> +;
> +; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
> +;
> +sevSnpBootBlockStart:
> +    DD      SEV_SNP_SECRETS_PAGE
> +    DD      SEV_SNP_CPUID_PAGE
> +    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
> +    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
> +    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
> +sevSnpBootBlockEnd:
> +
>  ;
>  ; SEV Secret block
>  ;
> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
> index dc38f68919..d890bb6b29 100644
> --- a/OvmfPkg/ResetVector/ResetVector.inf
> +++ b/OvmfPkg/ResetVector/ResetVector.inf
> @@ -37,6 +37,10 @@
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
> index 5fbacaed5f..2c194958f4 100644
> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
> @@ -75,6 +75,8 @@
>    %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>    %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 8)
>    %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16)
> +  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
> +  %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
>    %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
>  %include "Ia32/Flat32ToFlat64.asm"
>  %include "Ia32/PageTables64.asm"

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-04-09 22:43       ` Brijesh Singh
@ 2021-04-12 16:23         ` Laszlo Ersek
  2021-04-12 20:14           ` Brijesh Singh
  0 siblings, 1 reply; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-12 16:23 UTC (permalink / raw)
  To: Brijesh Singh, devel
  Cc: James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky, Jordan Justen,
	Ard Biesheuvel

On 04/10/21 00:43, Brijesh Singh wrote:
>
> On 4/9/21 7:24 AM, Laszlo Ersek wrote:
>> On 04/08/21 13:59, Brijesh Singh wrote:
>>> On 4/8/21 4:58 AM, Laszlo Ersek wrote:
>>>> On 03/24/21 16:31, Brijesh Singh wrote:
>>>>> At this time we only support the pre-validation. OVMF detects all
>>>>> the available system RAM in the PEI phase. When SEV-SNP is
>>>>> enabled, the memory is validated before it is made available to
>>>>> the EDK2 core.
>>>> Can you describe this in a bit more detail, before I look at the
>>>> individual patches? Specifically, what existing logic in the PEI
>>>> phase was taken, and extended, and how?
>>> One of the key requirement is that the guest private pages much be
>>> validated before the access. If guest tries to access the pages
>>> before the validation then it will result in #VC
>>> (page-not-validated) exception. To avoid the #VC, we propose the
>>> validating the memory before the access. We will incrementally add
>>> the support to lazy validate (i.e validate on access).
>> What is the primary threat (in simple terms please) that validation
>> is supposed to prevent?
>
>
> To protect against the memory re-mapping attack the guest pages must
> be validated. The idea is that every guest page can map only to a
> single physical memory page at one time.

I don't understand. How can a given guest page frame map to multiple
host page frames?

Do you mean that the situation to prevent is when multiple guest page
frames (GPAs) map to the same host page frame, as set up by the
hypervisor?


>> And, against that particular threat, does pre-validation offer some
>> protection, or will that only come with lazy validation?
>
> For the hardware it does not matter how the memory was validated --
> lazy vs prevalidate. Both approaches use the PVALIDATE instruction to
> validate the page.
>
> In the case of pre-validation, the memory is validated before the
> access. Whereas in the lazy validation, the access will cause a fault
> and fault handler should validate the page and retry the access. Its
> similar to a page fault handler, OS can populate the page table or
> back the pages on demand.
>
> The only downside of pre-validation is, we will take a hit on the boot
> time. The GHCB spec provides method by which we can batch multiple
> requests at once to minimize the context switches.

If pre-validation is simpler, and the only drawback is a one-time hit
during boot, then wouldn't it be better to stick with pre-validation
forever? I expect that would be a lot simpler for (a) the #VC handlers,
(b) the tracking of the "already validated" information.


>>> Similar to SEV, the OVMF_CODE.fd is encrypted through the SNP
>>> firmware before the launch. The SNP firmware also validates the
>>> memory page after encrypting. This allows us to boot the initial
>>> entry code without guest going through the validation process.
>>>
>>> The OVMF reset vector uses few data pages (e.g page table, early Sec
>>> stack). Access to these data pages will result in #VC. There are two
>>> approaches we can take to validate these data pages:
>>>
>>> 1. Ask SNP firmware to pre-validate it -- SNP firmware provides an
>>> special command that can be used to pre-validate the pages without
>>> affecting the measurement.
>> This means the two pflash chips, right?
>
>
> This does not need to be two pflash chips. The SNP firmware command
> does not know anything about the ROM or pflash chip. The command
> accepts the system physical address that need to be validated by the
> firmware. In patch #2, OVMF provides a range of data pages that need
> to be validated by the SNP firmware before booting the guest.

I guess my question related to the guest code that executes from pflash,
and/or accesses data from pflash, before the guest itself could ask for
any validation. Such as, the reset vector (which runs from pflash).
Then, some non-volatile UEFI variable data that resides in the other
pflash chip, and is accessed (read-only) during the PEI phase (the
memory type information variable namely). I understood your comments as
QEMU pre-validating those areas before guest launch. Is that correct?

Anyway, I guess at this point I should just start reviewing the series.

Some more comments below.


>> - We need more areas made accessible in SEC than just the
>> decompression buffer; for example the temporary SEC/PEI heap and
>> stack, and (IIRC) various special SEV-ES areas laid out via MEMFD.
>> Where are all of those handled / enumerated?
>
>
> Sorry, I simplified my response by saying just decompression. You are
> right that its more than the decompression buffer. In my current
> patch, the SEC phase validates all the memory up to
> PcdOvmfDecompressionScratchEnd (which includes more than just output
> buffer). See patch #13.

That sounds good (will check the patch out of course).


>>> One of the important thing is we should *never* validate the pages
>>> twice.
>> What are the symptoms / consequences of:
>>
>> - the guest accessing an unvalidated page (I understand it causes a
>> #VC, but what is the direct result of that, when this series is
>> applied?),
>
>
> After the series it applied, an access to an unvalidated page will
> result in #VC. The series does not extend the VC handler to handle the
> page-not-validated error code. The current VC handler
> [InternalVmgExitHandleVc()] will inject a #GP to terminate the guest
> if #VC is raised for unvalidated page.

OK.


>> - doubly-validating a page?
>>
>> The first question is relevant because we should crash as soon as we
>> touch a page we forgot to validate (we shouldn't let any corruption
>> or similar spread out until we finally realize there's a problem).
>>
>> The second question is relevant for security I guess. What attacks
>> become possible, and/or what symptoms are produced, if we
>> doubly-validate a page?
>
> Assuming that guest validates its memory, this guarantees the
> injective mapping between GPAs and SPAs.
>
> As I noted that the page validation process consist of two steps #1
> RMPUPDATE and 2#PVALIDATE. The RMPUPDATE is a hypervisor instruction
> and PVALIDATE is a guest instruction. A malicious hypervisor can remap
> gpa to different physical address in RMP table using the RMPUPDATE
> just before the second PVALIDATE instruction is executed. The guest
> need to ensure that it does not leave the security vulnerability that
> can be exploited by a malicious hypervisor. The SNP white paper
> recommends that a guest OS should keep track of what is has validate
> so that it can avoid getting into these situation.

Hm, OK. I think this answers my top-most question.


>> Furthermore, IIRC, we have separate #VC handlers for the different
>> firmware phases; do they behave consistently / identicall when a
>> #VC(page-not-validated) occurs, when this patch set is applied?
>>
>> My first question is basically asking whether we can *exclusively*
>> rely on #VC(page-not-validated) faults to tell us that we missed
>> validating a particular page. If we can do that, then the job is a
>> bit easier, because from the GPA, we can more or less also derive
>> *when and where* we should pre-validate the page (at least until
>> validation is done completely lazily).
>
> Yes, we should be able to rely #VC to tell us that we missed
> validating the page. In my this series so far I have not seen a #VC
> due to page-not-validated because we have carefully validated the
> pages before they are accessed. I am thinking that
> #VC(page-not-validated) should be treated as an error in the first
> phase. Incrementally we will add the lazy validation, in which the
> #VC(page-not-validated) will provide a hint as to what has not been
> validated.

I'm OK with the current handling of #VC(page-not-validated) -- i.e.,
fatal error.

I'm not yet convinced that lazy validation will be useful at all, but
that could be just my lack of understanding.


>>> The MemEncryptSevSnpValidateRam() uses a interval search tree to
>>> keep the record of what has been validated. Before validating the
>>> range, it lookup in its tree and if it finds that range is already
>>> validated then do nothing. If it detects an overlap then it will
>>> validate only non overlapping regions -- see patch #14. hat data
>>> structure is used for implementing the interval tree?
>>
>> I'm not necessarily looking for a data structure with "nice"
>> asymptotic time complexity. With pre-validation especially, I think
>> simplicity (ease of review) is more important for the data structure
>> than performance. If it's not an actual "tree", we shouldn't call it
>> a "tree". (An "interval tree" is usually an extension of a Red-Black
>> Tree, and that's not the simplest data structure in existence;
>> although edk2 does offer an rbtree library.)
>
>
> I am using binary search tree. Currently, we don't have many nodes to
> track. Most of the guest memory is private and are validated before we
> exit from the PEI phase.

My preliminary comments about MemEncryptSevSnpValidateSystemRam():

(1) The function prototype (patch#12) promises that the function can
never fail. In patch#14 however, I see some AddRangeToIntervalTree()
calls whose return values are not checked. I think error checking should
be strict, and if an error is detected (such as memory allocation
failure), a plain ASSERT (FALSE) is not sufficient. Both ASSERT() and
CpuDeadLoop() should be used explicitly, for the sake of RELEASE builds.
And the function prototype (the leading comment) should highlight that
the function either succeeds, or doesn't return.


(2) I don't like the recursive nature of the functions. That's always
convenient first, and almost always a mess to get out of later. However,
this is not a request to complicate the tree's implementation,
because...


(3) ... you mention we don't have many nodes to track. Is a tree
structure, with dynamically allocated nodes, justified at all?


(4) Regarding repeating dynamic allocation -- that's never a great thing
to do in the SEC / PEI phases. Can we use a fixed size, once-allocated,
or even static, array somewhere?

In particular, patch#14 uses AllocatePool() for SNP_VALIDATED_RANGE, in
the PEI phase, that results in EFI_HOB_TYPE_MEMORY_POOL HOBs; see:

  AllocatePool()              [MdePkg/Library/PeiMemoryAllocationLib/MemoryAllocationLib.c]
    PeiServicesAllocatePool() [MdePkg/Library/PeiServicesLib/PeiServicesLib.c]
      PeiAllocatePool()       [MdeModulePkg/Core/Pei/Memory/MemoryServices.c]

Now, dependent on where you call this AllocatePool() from, it could
occur still on the temporary SEC/PEI RAM -- i.e., before permanent PEI
RAM is installed. That means that such HOBs would be subject to the HOB
list migration, after permanent PEI RAM is installed. The PI spec writes
about EFI_HOB_MEMORY_POOL: "The HOB consumer phase should be able to
ignore these HOBs". The PI spec writes about AllocatePool(): "The early
allocations from temporary memory will be migrated to permanent memory
when permanent main memory is installed; this migration shall occur when
the HOB list is migrated to permanent memory".

So, whenever we use AllocatePool() in the PEI phase, we need an "address
stability proof", namely that the AllocatePool() call, and any later
access to the allocated area (the HOB), are never separated by the
installation of "gEfiPeiMemoryDiscoveredPpiGuid" in the PPI database, by
the PEI Core. If we can't guarantee that, then an access later on could
occur to the original allocation address, which has by then been
invalidated by the temporary RAM migration (= the allocation HOB has
been moved).

AllocatePages() is "stable" however, in this sense; no proof like the
one described above is necessary.


(5) The "mRootNode" variable is defined in the file
"OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c"
(patch #14). That means that every single module (i.e., PEIM) which
consumes this library instance will own a separate "mRootNode" variable,
with a validation-tracking tree that's correspondingly private to that
PEIM.

But the validation state is global to the entire firmware. I think we
should somehow clarify, or even enforce, that the tracking data
structure is firmware-global.

Perhaps this can be solved by (a) moving the ownership of the data
structure to a particular firmware module, with sole responsibility for
pre-validation, and (b) updating the validation APIs to take this data
structure explicitly.

Alternatively, if multiple modules are expected to inter-operate on the
tracker data structure (by which I mean inter-operation *in sequence*,
not concurrently in a multiprocessing sense), then we might need a
dynamic PCD for inter-driver understanding.


(NB: both points (4) and (5) seem to conflict with *existent* code in
BaseMemEncryptSevLib. However:

- concerning (4), the current allocations are all performed with
  AllocateAlignedPages(), which are unaffected by temporary-to-permanent
  RAM migration,

- and concerning (5), namely the existent "mPageTablePool" global
  variable, is a *known bug* -- see commit b721aa749b86a and
  TianoCore#847. This issue (namely, multiple modules messing with the
  page tables independently) remains unsolved, and I'd like to avoid
  introducing *more* paging-related allocations that are module-owned,
  and not firmware-level.)


All in all, here's what I'd prefer (and please correct me if it's
unrealistic):

- make the SNP validation functions take a simple, *unresizeable*
  tracker structure for input/output,

- allocate that data structure with a single AllocatePages() call in the
  proper PEIM,

- if the SNP validation functions run out of space in the structure,
  hang hard,

- avoid recursion, and use a simple linear search through the array,

- evaluate whether graceful handling of overlaps (= range splitting,
  merging) is actually a requirement. See more on this below.


(6) Can you please confirm that the order in which
MemEncryptSevSnpValidateSystemRam()'s declaration, call sites, and
implementations are introduced, is sensible? I can't tell immediately,
but I'd like to be sure that the tree at least builds at every stage (no
linkage errors at any stage).


>> Furthermore, what you describe above is called idempotency. No matter
>> how many times we attempt to validate a range, it may (or may not
>> even) cause an actual change in the first action only. Is this
>> property (=idempotency) an inherent requirement of the technology, or
>> is it a simplification of the implementation? Put differently: if you
>> called CpuDeadLoop() in the validation function any time an
>> overlapping validation request were received, would that hugely
>> complicate the call sites?
>
> From the technology point of view you can validate the same page again
> and again. Hardware does not force any restriction. To protect against
> time-based remap attack a guest also need to ensure that it does not
> blindly validates the pages otherwise guest is taking risk. Avoiding a
> double validation is strong recommendation. In current implementation,
> the validation burden is not put on the caller. Caller calls standard
> APIs to toggle the encryption attribute. Under the hood, if we see
> that page is already validated then no need to issue the PVALIDATE. If
> the page is getting changed from C=1 -> 0 then unvalidate it.

There remains a huge gap in my understanding here. (The rest of my
earlier questions did concern the relationship between the C bit and
validation, but I might as well comment right at this spot.)

"Invalidation in connection to C-bit toggling" means that there's going
to be significant "validation churn" in the DXE phase. For example when
fw_cfg is used (which relies on the edk2 IOMMU protocol), or when virtio
transfers are performed (which also relies on the edk2 IOMMU protocol,
although less directly -- through PciIo, namely).

That, however, brings up several problems for me:

- Is the tracker data structure scalable? I don't see any balancing in
  it. (And I wouldn't want a new, complex data structure for it anyway.)

- In fact, I don't see any code related to invalidation -- more
  precisely, I don't see where and how the C-bit changes consult the
  tracker structure *at all*.

- Given issue (5), I don't see how the tracker structure would be
  inherited from the PEI phase to the DXE phase.

In particular, in patch#18, the flipping of the C-bit results in calls
to SetPageStateInternal(). But SetPageStateInternal() does not go
*through* the tracker structure. Even in patch#14,
SevSnpValidateSystemRam() calls SetPageStateInternal() *in addition to*
AddRangeToIntervalTree().

In other words, I don't see where validity is tracked in the DXE phase,
in response to the C-bit toggling.


>> I'm kind of "obsessing" about idempotency because you say we must
>> *never* doubly-validate a page, so the *difference* between:
>> - explicitly crashing on such an attempt,
>> - and silently ignoring such an attempt,
>> may be meaningful.
>
>
> I said we should never doubly validate to avoid creating a
> vulnerability that can later be exploited by the malicious hypervisor.
> Currently we lookup address in our tree to ensure that we don't do a
> double validation. The PVALIDATE instruction also can be used to
> determine if we are attempting to validate an already validated page.
> In current implementation after the PVALIDATE completion, I check the
> rflags.CF to ensure that its not a double validation condition. If its
> double validation then abort the guest. Its bug in the guest.
>
>> It's kind of the difference between "oops this is a call site *bug*,
>> but we patched it up", vs. "this is expected of call sites, we should
>> just handle it internally".
>
> Our attempt should be to patch to avoid the double validation. I would
> still check the status of PVALIDATE, if instruction detects it as a
> double validation then we have a bug in our tracking and should abort
> the guest.

Let me rephrase. My question concerns the *abstraction level* at which
we should catch and handle double-validation attempts.

If we silently split and merge ranges in the tracker structure, then the
guest (as a whole) will not double-validate, which is good. However,
double-validation attempts will potentially still *exist*, in
higher-level modules in the firmware. My question is whether it is
*right* to paper over such overlaps, coming from the high-level code, in
the tracker structure. I suspect that it's not right.

In other words, the spot where we should abort, due to
"double-validation detected", is not when the PVALIDATE instruction
reports a problem in the tracker structure. Instead, the spot where we
should abort is when we detect an overlapping request *before* issuing
PVALIDATE. I'd like the tracker to be as simple and dumb as possible,
and I'd like the actual sites that request validation to blow up
immediately, if they issue an overlapping request. Is my expectation
wrong?


>>> The patch #18 extend the MemEncrypt{Set,Clear}PageEncMask() to call
>>> the SNP page state change during the C-bit toggle.
>> - When exactly do we invalidate?
>
> Basically before the page is transition from C=1 -> 0 in the page
> table.
>
>>
>> - Does the time and place of invalidation depend on whether we
>> perform pre-validation, or lazy validation?
> It does not matter. Both type of validation update the Validated bit
> in the RMP table. The important thing is that invalidation can happen
> only for a already validated private page. If the page is shared then
> invalidation will result in #GP.
>>
>> - Is page invalidation idempotent too?
> We should treat this as idempotent too. As I said, tacking the page
> state is strongly recommended for the security reason. The SNP white
> paper has a section dedicated for the validation and may able to
> clarify things which is not covered in my response.
>>
>> - What is the consequence (security impact) if we forget
>> invalidation?
> I can't think of a security implication of it but if we forget to
> invalidate then it can lead issues in the tracking data structure when
> it comes to validation next time. e.g a page was mapped as C=1 and we
> changed it to C=0 without invalidating it. Now if the same page is
> being changed from C=0 -> 1 then tracking data structure may think
> that page is already validated and will skip the validation process
> and thus access will result in #VC (page-not-validated).

As I said above, I don't see where the validation tracking and the C-bit
flipping "meet". Especially across the PEI and DXE phases.

Basically I'm missing the core concept for maintaining a firmware-global
structure, for tracking the validation status of GPA intervals.

Up to and *excluding* the DXE phase, the validation tracking should be
trivial, naive, and unforgiving. This is because, in the PEI phase,
validation is one-shot. I would expect the tracker structure to live in
the PlatformPei module.

Starting with the DXE phase, where we need (in)validation to closely
accompany C-bit toggling, we need a different, performant data structure
for tracking validation status. This data structure should be primed
from the status last set up in the PEI phase. The tracking should still
be unforgiving. I believe *this* tracker structure may have to live in
the IOMMU driver.

BaseMemEncryptSevLib may offer helper functions, but even if it does, it
should never attempt to *own* tracking information, via an internal
global variable. Furthermore, those helper functions that only make
sense for a particular firmware phase, should have such implementations
that hang hard, in library instances that match the *other* firmware
phases. For example, SevSnpValidateSystemRam() should have a
DXE-specific implementation that hangs hard.

It's entirely possible that my points above are complete garbage. I
don't have a grasp on the core concept, for the firmware-wide tracking
of the validation status.

... Ugh, wait. I've just noticed something. In patch#19,
"SEC_SEV_ES_WORK_AREA.SnpSystemRamValidatedRootAddress" is introduced,
which seems (?) to be implementing the kind of global (inter-driver,
inter-phase) tracking that I've been missing. However, even if my guess
(?) is correct about that, this idea definitely doesn't belong in the
last patch, presented as a "bugfix". Even if it were a bugfix, it should
be incorporated into one of the earlier patches (so that we preferably
not introduce the bug in the first place). However, my argument is that
it's not a bugfix -- to me that seems to be the core concept. That's
what the whole series needs to be built upon. (In all of my thinking,
before this particular paragraph, I *never* looked at patch#19. There
was no reason to.)


>> - There are four page states I can imagine:
>>   - encrypted for host access, valid for guest access
>>   - encrypted for host access, invalid for guest access
>>   - decrypted for host access, valid for guest access
>>   - decrypted for host access, invalid for guest access
>>
>> Does a state exist, from these four, that's never used? (Potentially
>> caught by the hardware?)
>
> I would not mix the page state with the host access. Basically there
> is two page state exist
>
> - Guest-Valid
>
> - Guest-Invalid
>
> By default all the guest memory is in Guest-Invalid state on boot. The
> PVALIDATE instruction is used to transition from Valid->Invalid.

Do you mean "Invalid->Valid" transition?


> Guest can use the PVALIDATE to change the state from Valid to Invalid.
>
> A guest private page (i.e page mapped C=1) must be in Guest Valid
> state. If hardware see that a private access page is not in the
> Guest-Valid state then it will trigger #VC.
>
>
>>
>> Do the patches highlight / explain the validity transitions, in
>> comments? My understanding is that the C-bit toggle and the guest
>> access valid/invalid toggle are separate actions, so "middle" states
>> do exist, but perhaps only temporarily.
>
> I have tried to document the steps in the patch description and I can
> add more comments as required. When SEV-SNP is active then we need to
> invalidate the page before toggling the C-bit from C=1 -> 0 and
> similarly after the page is toggled from C=0 -> 1 we must validate it.
>
>
>> I'm curious how it works, for example, with variousvirtio transfers
>> (bus master read, bus master write, bus master common buffer). In the
>> IoMmuDxe driver minimally, we access memory both through PTEs with
>> the C-bit set and through PTEs with the C-bit clear, meaning that
>> "encrypted, valid", and "decrypted, valid" are both required states.
>> But that seems to conflict with the notion that "C-bit toggle" be
>> directly coupled with a "validity toggle".
>
> That should not be an issue. Each page have entry in the RMP table.
> So, you can have one page as guest invalid and another page as a guest
> valid.
>
>> Put differently, I'm happy that modifying IoMmuDxe appears
>> unnecessary, but then that tells me I'm missing something about the
>> state transitions between the above *four* states.
>
>
> Only thing which I would propose changing in IoMmuDxe driver is to use
> a pre-allocated buffer and avoid toggling the C-bit on every
> read/write. In past the C-bit toggle was all contained inside the
> guest. But when SNP is enabled, each C-bit toggle causes two
> additional steps #1 RMPUPDATE and #2 PVALIDATE. The RMPUPDATE is
> requested using the VMEXIT so it will cause a VM context switch. I
> would prefer to avoid it.
>
> My current patch set does not have this optimization yet.

Please let's stick with the absolute minimum in this series, yes.

I realize my email is complete chaos. That's because my understanding of
this work is complete chaos.

Can we approach this feature as follows:

- Make "validation tracking" the core tenet (central principle) of this
  feature.

- Design the validation *patterns* that are required in each firmware
  phase.

- Identify the *sole* owner module, for validation tracking, in each
  firmware phase. If we can't readily do this, we might have to
  introduce new drivers (PEIMs, DXE drivers?) and new interfaces (PPIs,
  protocols?)

- Design the hand-off between the owner modules (on firmware phase
  boundaries).

- Add only helper functions to BaseMemEncryptSevLib, without any
  ownership of tracking info. The helpers should be as restricted as
  possible.

I'd like to avoid "smearing" page validation responsibility over a bunch
of modules. (See again: TianoCore#847.) I'd like responsibilities
strictly centralized.

If you feel that my points make no sense, I can start going over the
individual patches. However, even that way, I'd have found patch#19 only
in the end, and that doesn't seem right. And I wouldn't like to go
"numb" on the actual code, being distracted by style issues and so on,
before we have some agreement on the core approach.

Another tip: if you can add content to MdePkg that is governed by AMD
specifications, such as patches #3, #5, #9, I would recommend splitting
that off to a separate (pre-requisite) series. That content seems
unaffected by whatever we do in OvmfPkg, and it would help decrease the
size of the OvmfPkg series. It's not like we're going to abandon the
SEV-SNP feature, so I don't think there's a risk in merging the MdePkg
series first, while the OvmfPkg series is not ready. The MdePkg stuff
will not remain unused indefinitely.

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-04-12 16:23         ` Laszlo Ersek
@ 2021-04-12 20:14           ` Brijesh Singh
  2021-04-13 13:00             ` Laszlo Ersek
  0 siblings, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-04-12 20:14 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: brijesh.singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel


On 4/12/21 11:23 AM, Laszlo Ersek wrote:
> On 04/10/21 00:43, Brijesh Singh wrote:
>> On 4/9/21 7:24 AM, Laszlo Ersek wrote:
>>> On 04/08/21 13:59, Brijesh Singh wrote:
>>>> On 4/8/21 4:58 AM, Laszlo Ersek wrote:
>>>>> On 03/24/21 16:31, Brijesh Singh wrote:
>>>>>> At this time we only support the pre-validation. OVMF detects all
>>>>>> the available system RAM in the PEI phase. When SEV-SNP is
>>>>>> enabled, the memory is validated before it is made available to
>>>>>> the EDK2 core.
>>>>> Can you describe this in a bit more detail, before I look at the
>>>>> individual patches? Specifically, what existing logic in the PEI
>>>>> phase was taken, and extended, and how?
>>>> One of the key requirement is that the guest private pages much be
>>>> validated before the access. If guest tries to access the pages
>>>> before the validation then it will result in #VC
>>>> (page-not-validated) exception. To avoid the #VC, we propose the
>>>> validating the memory before the access. We will incrementally add
>>>> the support to lazy validate (i.e validate on access).
>>> What is the primary threat (in simple terms please) that validation
>>> is supposed to prevent?
>>
>> To protect against the memory re-mapping attack the guest pages must
>> be validated. The idea is that every guest page can map only to a
>> single physical memory page at one time.
> I don't understand. How can a given guest page frame map to multiple
> host page frames?
>
> Do you mean that the situation to prevent is when multiple guest page
> frames (GPAs) map to the same host page frame, as set up by the
> hypervisor?
>
A malicious hypervisor may attempt to remap validated gpa to a different
host frame. The PVALIDATE is designed to protect against those type of
attacks. See
https://static.sched.com/hosted_files/lsseu2019/65/SEV-SNP%20Slides%20Nov%201%202019.pdf
(slide 13). You can also find more information about it in the SEV-SNP
whitepaper.


>>> And, against that particular threat, does pre-validation offer some
>>> protection, or will that only come with lazy validation?
>> For the hardware it does not matter how the memory was validated --
>> lazy vs prevalidate. Both approaches use the PVALIDATE instruction to
>> validate the page.
>>
>> In the case of pre-validation, the memory is validated before the
>> access. Whereas in the lazy validation, the access will cause a fault
>> and fault handler should validate the page and retry the access. Its
>> similar to a page fault handler, OS can populate the page table or
>> back the pages on demand.
>>
>> The only downside of pre-validation is, we will take a hit on the boot
>> time. The GHCB spec provides method by which we can batch multiple
>> requests at once to minimize the context switches.
> If pre-validation is simpler, and the only drawback is a one-time hit
> during boot, then wouldn't it be better to stick with pre-validation
> forever? I expect that would be a lot simpler for (a) the #VC handlers,
> (b) the tracking of the "already validated" information.

This could be an issue for the larger VMs. The boot delay will vary
based on the VM size. In addition to the boot delay,  the PVALIDATE
instruction requires that there is a valid entry for the page in the
nested page table. If the entry does not exist in the NPT then HV will
get #NPF and will fill the NPT with the backing host page. By using the
lazy-validation we can push allocation of the backing pages to only when
required and thus release the memory pressure on the VMM.


>
>
>>>> Similar to SEV, the OVMF_CODE.fd is encrypted through the SNP
>>>> firmware before the launch. The SNP firmware also validates the
>>>> memory page after encrypting. This allows us to boot the initial
>>>> entry code without guest going through the validation process.
>>>>
>>>> The OVMF reset vector uses few data pages (e.g page table, early Sec
>>>> stack). Access to these data pages will result in #VC. There are two
>>>> approaches we can take to validate these data pages:
>>>>
>>>> 1. Ask SNP firmware to pre-validate it -- SNP firmware provides an
>>>> special command that can be used to pre-validate the pages without
>>>> affecting the measurement.
>>> This means the two pflash chips, right?
>>
>> This does not need to be two pflash chips. The SNP firmware command
>> does not know anything about the ROM or pflash chip. The command
>> accepts the system physical address that need to be validated by the
>> firmware. In patch #2, OVMF provides a range of data pages that need
>> to be validated by the SNP firmware before booting the guest.
> I guess my question related to the guest code that executes from pflash,
> and/or accesses data from pflash, before the guest itself could ask for
> any validation. Such as, the reset vector (which runs from pflash).
> Then, some non-volatile UEFI variable data that resides in the other
> pflash chip, and is accessed (read-only) during the PEI phase (the
> memory type information variable namely). I understood your comments as
> QEMU pre-validating those areas before guest launch. Is that correct?


Yes that is correct. All the OVMF pflash0 memory will be pre-validated
before the guest is launched. IIRC, the variables which resides on the
other pflash chip are accessed unencrypted in the guest. Only the
encrypted memory access (aka C=1) need to be validated. The host MMIO
regions does not need to be validated.


>
> Anyway, I guess at this point I should just start reviewing the series.
>
> Some more comments below.
>
>
>>> - We need more areas made accessible in SEC than just the
>>> decompression buffer; for example the temporary SEC/PEI heap and
>>> stack, and (IIRC) various special SEV-ES areas laid out via MEMFD.
>>> Where are all of those handled / enumerated?
>>
>> Sorry, I simplified my response by saying just decompression. You are
>> right that its more than the decompression buffer. In my current
>> patch, the SEC phase validates all the memory up to
>> PcdOvmfDecompressionScratchEnd (which includes more than just output
>> buffer). See patch #13.
> That sounds good (will check the patch out of course).
>
>
>>>> One of the important thing is we should *never* validate the pages
>>>> twice.
>>> What are the symptoms / consequences of:
>>>
>>> - the guest accessing an unvalidated page (I understand it causes a
>>> #VC, but what is the direct result of that, when this series is
>>> applied?),
>>
>> After the series it applied, an access to an unvalidated page will
>> result in #VC. The series does not extend the VC handler to handle the
>> page-not-validated error code. The current VC handler
>> [InternalVmgExitHandleVc()] will inject a #GP to terminate the guest
>> if #VC is raised for unvalidated page.
> OK.
>
>
>>> - doubly-validating a page?
>>>
>>> The first question is relevant because we should crash as soon as we
>>> touch a page we forgot to validate (we shouldn't let any corruption
>>> or similar spread out until we finally realize there's a problem).
>>>
>>> The second question is relevant for security I guess. What attacks
>>> become possible, and/or what symptoms are produced, if we
>>> doubly-validate a page?
>> Assuming that guest validates its memory, this guarantees the
>> injective mapping between GPAs and SPAs.
>>
>> As I noted that the page validation process consist of two steps #1
>> RMPUPDATE and 2#PVALIDATE. The RMPUPDATE is a hypervisor instruction
>> and PVALIDATE is a guest instruction. A malicious hypervisor can remap
>> gpa to different physical address in RMP table using the RMPUPDATE
>> just before the second PVALIDATE instruction is executed. The guest
>> need to ensure that it does not leave the security vulnerability that
>> can be exploited by a malicious hypervisor. The SNP white paper
>> recommends that a guest OS should keep track of what is has validate
>> so that it can avoid getting into these situation.
> Hm, OK. I think this answers my top-most question.
>
>
>>> Furthermore, IIRC, we have separate #VC handlers for the different
>>> firmware phases; do they behave consistently / identicall when a
>>> #VC(page-not-validated) occurs, when this patch set is applied?
>>>
>>> My first question is basically asking whether we can *exclusively*
>>> rely on #VC(page-not-validated) faults to tell us that we missed
>>> validating a particular page. If we can do that, then the job is a
>>> bit easier, because from the GPA, we can more or less also derive
>>> *when and where* we should pre-validate the page (at least until
>>> validation is done completely lazily).
>> Yes, we should be able to rely #VC to tell us that we missed
>> validating the page. In my this series so far I have not seen a #VC
>> due to page-not-validated because we have carefully validated the
>> pages before they are accessed. I am thinking that
>> #VC(page-not-validated) should be treated as an error in the first
>> phase. Incrementally we will add the lazy validation, in which the
>> #VC(page-not-validated) will provide a hint as to what has not been
>> validated.
> I'm OK with the current handling of #VC(page-not-validated) -- i.e.,
> fatal error.
>
> I'm not yet convinced that lazy validation will be useful at all, but
> that could be just my lack of understanding.


See if my above comment makes sense on why we may need the lazy
validation. IMO, we should not be going with the lazy validation in
OVMF. To minimize the boot delay and avoid the complexity of the code in
the best case OVMF validated lower 4GB memory and mark rest of the
memory as "unaccepted" in the UEFI map.

David from google has started a thread about lazy validation in kernel
https://www.spinics.net/lists/linux-mm/msg243954.html. We would like to
find a method which works for both Intel TDX and AMD SEV. We will build
these support incrementally both in the kernel and OVMF.

>
>
>>>> The MemEncryptSevSnpValidateRam() uses a interval search tree to
>>>> keep the record of what has been validated. Before validating the
>>>> range, it lookup in its tree and if it finds that range is already
>>>> validated then do nothing. If it detects an overlap then it will
>>>> validate only non overlapping regions -- see patch #14. hat data
>>>> structure is used for implementing the interval tree?
>>> I'm not necessarily looking for a data structure with "nice"
>>> asymptotic time complexity. With pre-validation especially, I think
>>> simplicity (ease of review) is more important for the data structure
>>> than performance. If it's not an actual "tree", we shouldn't call it
>>> a "tree". (An "interval tree" is usually an extension of a Red-Black
>>> Tree, and that's not the simplest data structure in existence;
>>> although edk2 does offer an rbtree library.)
>>
>> I am using binary search tree. Currently, we don't have many nodes to
>> track. Most of the guest memory is private and are validated before we
>> exit from the PEI phase.
> My preliminary comments about MemEncryptSevSnpValidateSystemRam():
>
> (1) The function prototype (patch#12) promises that the function can
> never fail. In patch#14 however, I see some AddRangeToIntervalTree()
> calls whose return values are not checked. I think error checking should
> be strict, and if an error is detected (such as memory allocation
> failure), a plain ASSERT (FALSE) is not sufficient. Both ASSERT() and
> CpuDeadLoop() should be used explicitly, for the sake of RELEASE builds.
> And the function prototype (the leading comment) should highlight that
> the function either succeeds, or doesn't return.
>
>
> (2) I don't like the recursive nature of the functions. That's always
> convenient first, and almost always a mess to get out of later. However,
> this is not a request to complicate the tree's implementation,
> because...
>
>
> (3) ... you mention we don't have many nodes to track. Is a tree
> structure, with dynamically allocated nodes, justified at all?
>
>
> (4) Regarding repeating dynamic allocation -- that's never a great thing
> to do in the SEC / PEI phases. Can we use a fixed size, once-allocated,
> or even static, array somewhere?
>
> In particular, patch#14 uses AllocatePool() for SNP_VALIDATED_RANGE, in
> the PEI phase, that results in EFI_HOB_TYPE_MEMORY_POOL HOBs; see:
>
>   AllocatePool()              [MdePkg/Library/PeiMemoryAllocationLib/MemoryAllocationLib.c]
>     PeiServicesAllocatePool() [MdePkg/Library/PeiServicesLib/PeiServicesLib.c]
>       PeiAllocatePool()       [MdeModulePkg/Core/Pei/Memory/MemoryServices.c]
>
> Now, dependent on where you call this AllocatePool() from, it could
> occur still on the temporary SEC/PEI RAM -- i.e., before permanent PEI
> RAM is installed. That means that such HOBs would be subject to the HOB
> list migration, after permanent PEI RAM is installed. The PI spec writes
> about EFI_HOB_MEMORY_POOL: "The HOB consumer phase should be able to
> ignore these HOBs". The PI spec writes about AllocatePool(): "The early
> allocations from temporary memory will be migrated to permanent memory
> when permanent main memory is installed; this migration shall occur when
> the HOB list is migrated to permanent memory".
>
> So, whenever we use AllocatePool() in the PEI phase, we need an "address
> stability proof", namely that the AllocatePool() call, and any later
> access to the allocated area (the HOB), are never separated by the
> installation of "gEfiPeiMemoryDiscoveredPpiGuid" in the PPI database, by
> the PEI Core. If we can't guarantee that, then an access later on could
> occur to the original allocation address, which has by then been
> invalidated by the temporary RAM migration (= the allocation HOB has
> been moved).
>
> AllocatePages() is "stable" however, in this sense; no proof like the
> one described above is necessary.
>
>
> (5) The "mRootNode" variable is defined in the file
> "OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c"
> (patch #14). That means that every single module (i.e., PEIM) which
> consumes this library instance will own a separate "mRootNode" variable,
> with a validation-tracking tree that's correspondingly private to that
> PEIM.
>
> But the validation state is global to the entire firmware. I think we
> should somehow clarify, or even enforce, that the tracking data
> structure is firmware-global.
>
> Perhaps this can be solved by (a) moving the ownership of the data
> structure to a particular firmware module, with sole responsibility for
> pre-validation, and (b) updating the validation APIs to take this data
> structure explicitly.
>
> Alternatively, if multiple modules are expected to inter-operate on the
> tracker data structure (by which I mean inter-operation *in sequence*,
> not concurrently in a multiprocessing sense), then we might need a
> dynamic PCD for inter-driver understanding.
>
>
> (NB: both points (4) and (5) seem to conflict with *existent* code in
> BaseMemEncryptSevLib. However:
>
> - concerning (4), the current allocations are all performed with
>   AllocateAlignedPages(), which are unaffected by temporary-to-permanent
>   RAM migration,
>
> - and concerning (5), namely the existent "mPageTablePool" global
>   variable, is a *known bug* -- see commit b721aa749b86a and
>   TianoCore#847. This issue (namely, multiple modules messing with the
>   page tables independently) remains unsolved, and I'd like to avoid
>   introducing *more* paging-related allocations that are module-owned,
>   and not firmware-level.)
>
>
> All in all, here's what I'd prefer (and please correct me if it's
> unrealistic):
>
> - make the SNP validation functions take a simple, *unresizeable*
>   tracker structure for input/output,
>
> - allocate that data structure with a single AllocatePages() call in the
>   proper PEIM,
>
> - if the SNP validation functions run out of space in the structure,
>   hang hard,
>
> - avoid recursion, and use a simple linear search through the array,
>
> - evaluate whether graceful handling of overlaps (= range splitting,
>   merging) is actually a requirement. See more on this below.


Yes this will work fine. The primary reason for me adding the interval
tree was to detect the overlap condition before we finalize the
pre-validation in PEI. The validation flow works like this:

- Qemu validated few pages

- SEC validates few more page

- When we detect the memory in the PEI we need to exclude the pages
validated by the Qemu and SEC.

The page ranges validated using the Qemu and SEC are accessible through
a fixed PCDs. I can do simple overlap check with those fixed PCDs and
skip the pre-validated ranges. I don't have any strong reason to use the
interval tree to detect the overlap condition. There is no call to
toggle the C-bit before we finalize the validation. 

IMO, there is no need for tracking the page state after we finish the
pre-validation. After the pre-validtion is completed, the caller will
use either clear the C-bit or set the C-bit. If it attempts to set the
C-bit without clearing it first then its a bug -- which will be caught
by the page state change internal function. We can use the output of CF
flag from the PVALIDATE to determine where caller is trying to do doubly
validation or invalidation and abort it.


>
> (6) Can you please confirm that the order in which
> MemEncryptSevSnpValidateSystemRam()'s declaration, call sites, and
> implementations are introduced, is sensible? I can't tell immediately,
> but I'd like to be sure that the tree at least builds at every stage (no
> linkage errors at any stage).

I can certainly say the order in which they are introduced and the call
sites are a sensible. I don't expect any linkage failure but since it
was an RFC so I didn't go through making sure that it builds in every
stage. I wanted to get the overall direction on where community would
like to go before making the final changes. You have been asking some
very good question and that is certainly helping us to reduce some of
the complexity in the patch.


>
>>> Furthermore, what you describe above is called idempotency. No matter
>>> how many times we attempt to validate a range, it may (or may not
>>> even) cause an actual change in the first action only. Is this
>>> property (=idempotency) an inherent requirement of the technology, or
>>> is it a simplification of the implementation? Put differently: if you
>>> called CpuDeadLoop() in the validation function any time an
>>> overlapping validation request were received, would that hugely
>>> complicate the call sites?
>> From the technology point of view you can validate the same page again
>> and again. Hardware does not force any restriction. To protect against
>> time-based remap attack a guest also need to ensure that it does not
>> blindly validates the pages otherwise guest is taking risk. Avoiding a
>> double validation is strong recommendation. In current implementation,
>> the validation burden is not put on the caller. Caller calls standard
>> APIs to toggle the encryption attribute. Under the hood, if we see
>> that page is already validated then no need to issue the PVALIDATE. If
>> the page is getting changed from C=1 -> 0 then unvalidate it.
> There remains a huge gap in my understanding here. (The rest of my
> earlier questions did concern the relationship between the C bit and
> validation, but I might as well comment right at this spot.)
>
> "Invalidation in connection to C-bit toggling" means that there's going
> to be significant "validation churn" in the DXE phase. For example when
> fw_cfg is used (which relies on the edk2 IOMMU protocol), or when virtio
> transfers are performed (which also relies on the edk2 IOMMU protocol,
> although less directly -- through PciIo, namely).
>
> That, however, brings up several problems for me:
>
> - Is the tracker data structure scalable? I don't see any balancing in
>   it. (And I wouldn't want a new, complex data structure for it anyway.)
>
> - In fact, I don't see any code related to invalidation -- more
>   precisely, I don't see where and how the C-bit changes consult the
>   tracker structure *at all*.
>
> - Given issue (5), I don't see how the tracker structure would be
>   inherited from the PEI phase to the DXE phase.
>
> In particular, in patch#18, the flipping of the C-bit results in calls
> to SetPageStateInternal(). But SetPageStateInternal() does not go
> *through* the tracker structure. Even in patch#14,
> SevSnpValidateSystemRam() calls SetPageStateInternal() *in addition to*
> AddRangeToIntervalTree().
>
> In other words, I don't see where validity is tracked in the DXE phase,
> in response to the C-bit toggling.

As I highlighted above there is actually no need to track the page state
after we successfully complete the pre-validation. All these guest page
validation or invalidation are applicable to the system RAM. But
AmdSevDxe driver calls to clear the C-bit of the MMIO range. These range
are not a system and have never been validated so we invalidating it
will cause an issue. Since I had the data structures available in PEI
phase for the tracking the page state hence made those available to DXE
to verify that we are called to invalidate the SYSTEM_RAM and not MMIO.
IMO, we should either extend the MemEncryptSev{Set,Clear}PageEncMask()
to pass either a new flag to hint where its system RAM or non-RAM.
Thoughts ?



>
>>> I'm kind of "obsessing" about idempotency because you say we must
>>> *never* doubly-validate a page, so the *difference* between:
>>> - explicitly crashing on such an attempt,
>>> - and silently ignoring such an attempt,
>>> may be meaningful.
>>
>> I said we should never doubly validate to avoid creating a
>> vulnerability that can later be exploited by the malicious hypervisor.
>> Currently we lookup address in our tree to ensure that we don't do a
>> double validation. The PVALIDATE instruction also can be used to
>> determine if we are attempting to validate an already validated page.
>> In current implementation after the PVALIDATE completion, I check the
>> rflags.CF to ensure that its not a double validation condition. If its
>> double validation then abort the guest. Its bug in the guest.
>>
>>> It's kind of the difference between "oops this is a call site *bug*,
>>> but we patched it up", vs. "this is expected of call sites, we should
>>> just handle it internally".
>> Our attempt should be to patch to avoid the double validation. I would
>> still check the status of PVALIDATE, if instruction detects it as a
>> double validation then we have a bug in our tracking and should abort
>> the guest.
> Let me rephrase. My question concerns the *abstraction level* at which
> we should catch and handle double-validation attempts.
>
> If we silently split and merge ranges in the tracker structure, then the
> guest (as a whole) will not double-validate, which is good. However,
> double-validation attempts will potentially still *exist*, in
> higher-level modules in the firmware. My question is whether it is
> *right* to paper over such overlaps, coming from the high-level code, in
> the tracker structure. I suspect that it's not right.

Yes we should bail out if such a request comes from the high level
module. This is why I was actually not checking the tracking structure
when toggling the C-bit. The complete memory much have been validated
before we are asked to toggle the C-bit. If a module is performing a
doubly validation or invalidation then it should be aborted to avoid any
future exploits.


>
> In other words, the spot where we should abort, due to
> "double-validation detected", is not when the PVALIDATE instruction
> reports a problem in the tracker structure. Instead, the spot where we
> should abort is when we detect an overlapping request *before* issuing
> PVALIDATE. I'd like the tracker to be as simple and dumb as possible,
> and I'd like the actual sites that request validation to blow up
> immediately, if they issue an overlapping request. Is my expectation
> wrong?
>

If we go with the tracking only until we complete the pre-validation
then we can use the hardware PVALIDATE help to detect the doubly
validation and abort it. There is no need to check the overlap conditions.


>>>> The patch #18 extend the MemEncrypt{Set,Clear}PageEncMask() to call
>>>> the SNP page state change during the C-bit toggle.
>>> - When exactly do we invalidate?
>> Basically before the page is transition from C=1 -> 0 in the page
>> table.
>>
>>> - Does the time and place of invalidation depend on whether we
>>> perform pre-validation, or lazy validation?
>> It does not matter. Both type of validation update the Validated bit
>> in the RMP table. The important thing is that invalidation can happen
>> only for a already validated private page. If the page is shared then
>> invalidation will result in #GP.
>>> - Is page invalidation idempotent too?
>> We should treat this as idempotent too. As I said, tacking the page
>> state is strongly recommended for the security reason. The SNP white
>> paper has a section dedicated for the validation and may able to
>> clarify things which is not covered in my response.
>>> - What is the consequence (security impact) if we forget
>>> invalidation?
>> I can't think of a security implication of it but if we forget to
>> invalidate then it can lead issues in the tracking data structure when
>> it comes to validation next time. e.g a page was mapped as C=1 and we
>> changed it to C=0 without invalidating it. Now if the same page is
>> being changed from C=0 -> 1 then tracking data structure may think
>> that page is already validated and will skip the validation process
>> and thus access will result in #VC (page-not-validated).
> As I said above, I don't see where the validation tracking and the C-bit
> flipping "meet". Especially across the PEI and DXE phases.
>
> Basically I'm missing the core concept for maintaining a firmware-global
> structure, for tracking the validation status of GPA intervals.
>
> Up to and *excluding* the DXE phase, the validation tracking should be
> trivial, naive, and unforgiving. This is because, in the PEI phase,
> validation is one-shot. I would expect the tracker structure to live in
> the PlatformPei module.
>
> Starting with the DXE phase, where we need (in)validation to closely
> accompany C-bit toggling, we need a different, performant data structure
> for tracking validation status. This data structure should be primed
> from the status last set up in the PEI phase. The tracking should still
> be unforgiving. I believe *this* tracker structure may have to live in
> the IOMMU driver.
>
> BaseMemEncryptSevLib may offer helper functions, but even if it does, it
> should never attempt to *own* tracking information, via an internal
> global variable. Furthermore, those helper functions that only make
> sense for a particular firmware phase, should have such implementations
> that hang hard, in library instances that match the *other* firmware
> phases. For example, SevSnpValidateSystemRam() should have a
> DXE-specific implementation that hangs hard.
>
> It's entirely possible that my points above are complete garbage. I
> don't have a grasp on the core concept, for the firmware-wide tracking
> of the validation status.
>
> ... Ugh, wait. I've just noticed something. In patch#19,
> "SEC_SEV_ES_WORK_AREA.SnpSystemRamValidatedRootAddress" is introduced,
> which seems (?) to be implementing the kind of global (inter-driver,
> inter-phase) tracking that I've been missing. However, even if my guess
> (?) is correct about that, this idea definitely doesn't belong in the
> last patch, presented as a "bugfix". Even if it were a bugfix, it should
> be incorporated into one of the earlier patches (so that we preferably
> not introduce the bug in the first place). However, my argument is that
> it's not a bugfix -- to me that seems to be the core concept. That's
> what the whole series needs to be built upon. (In all of my thinking,
> before this particular paragraph, I *never* looked at patch#19. There
> was no reason to.)

It was my bad that I should have highlighted this before and not left it
to the last patch. As you can see the only reason pass the data
structure from PEI to DXE is to catch those non SYSTEM invalidation
request coming from the AmdSev Driver. I am too happy about the solution
I have in this RFC. I want to go with option to change the call sites to
tell us whether its a system RAM or non existence memory type. 


>
>>> - There are four page states I can imagine:
>>>   - encrypted for host access, valid for guest access
>>>   - encrypted for host access, invalid for guest access
>>>   - decrypted for host access, valid for guest access
>>>   - decrypted for host access, invalid for guest access
>>>
>>> Does a state exist, from these four, that's never used? (Potentially
>>> caught by the hardware?)
>> I would not mix the page state with the host access. Basically there
>> is two page state exist
>>
>> - Guest-Valid
>>
>> - Guest-Invalid
>>
>> By default all the guest memory is in Guest-Invalid state on boot. The
>> PVALIDATE instruction is used to transition from Valid->Invalid.
> Do you mean "Invalid->Valid" transition?


Ah yes that is correct.


>
>
>> Guest can use the PVALIDATE to change the state from Valid to Invalid.
>>
>> A guest private page (i.e page mapped C=1) must be in Guest Valid
>> state. If hardware see that a private access page is not in the
>> Guest-Valid state then it will trigger #VC.
>>
>>
>>> Do the patches highlight / explain the validity transitions, in
>>> comments? My understanding is that the C-bit toggle and the guest
>>> access valid/invalid toggle are separate actions, so "middle" states
>>> do exist, but perhaps only temporarily.
>> I have tried to document the steps in the patch description and I can
>> add more comments as required. When SEV-SNP is active then we need to
>> invalidate the page before toggling the C-bit from C=1 -> 0 and
>> similarly after the page is toggled from C=0 -> 1 we must validate it.
>>
>>
>>> I'm curious how it works, for example, with variousvirtio transfers
>>> (bus master read, bus master write, bus master common buffer). In the
>>> IoMmuDxe driver minimally, we access memory both through PTEs with
>>> the C-bit set and through PTEs with the C-bit clear, meaning that
>>> "encrypted, valid", and "decrypted, valid" are both required states.
>>> But that seems to conflict with the notion that "C-bit toggle" be
>>> directly coupled with a "validity toggle".
>> That should not be an issue. Each page have entry in the RMP table.
>> So, you can have one page as guest invalid and another page as a guest
>> valid.
>>
>>> Put differently, I'm happy that modifying IoMmuDxe appears
>>> unnecessary, but then that tells me I'm missing something about the
>>> state transitions between the above *four* states.
>>
>> Only thing which I would propose changing in IoMmuDxe driver is to use
>> a pre-allocated buffer and avoid toggling the C-bit on every
>> read/write. In past the C-bit toggle was all contained inside the
>> guest. But when SNP is enabled, each C-bit toggle causes two
>> additional steps #1 RMPUPDATE and #2 PVALIDATE. The RMPUPDATE is
>> requested using the VMEXIT so it will cause a VM context switch. I
>> would prefer to avoid it.
>>
>> My current patch set does not have this optimization yet.
> Please let's stick with the absolute minimum in this series, yes.
>
> I realize my email is complete chaos. That's because my understanding of
> this work is complete chaos.
>
> Can we approach this feature as follows:
>
> - Make "validation tracking" the core tenet (central principle) of this
>   feature.
>
> - Design the validation *patterns* that are required in each firmware
>   phase.
>
> - Identify the *sole* owner module, for validation tracking, in each
>   firmware phase. If we can't readily do this, we might have to
>   introduce new drivers (PEIMs, DXE drivers?) and new interfaces (PPIs,
>   protocols?)
>
> - Design the hand-off between the owner modules (on firmware phase
>   boundaries).
>
> - Add only helper functions to BaseMemEncryptSevLib, without any
>   ownership of tracking info. The helpers should be as restricted as
>   possible.
>
> I'd like to avoid "smearing" page validation responsibility over a bunch
> of modules. (See again: TianoCore#847.) I'd like responsibilities
> strictly centralized.
>
> If you feel that my points make no sense, I can start going over the
> individual patches. However, even that way, I'd have found patch#19 only
> in the end, and that doesn't seem right. And I wouldn't like to go
> "numb" on the actual code, being distracted by style issues and so on,
> before we have some agreement on the core approach.


Certainly your points makes sense. Let me know what you think about
removing the tracking data structure and using a liner array of
pre-valiated range (build with a Fixed PCD) works ? I was hoping you to
just glance over it and provide me high level feedback. I will go
through styles and comments in great details on non RFC patches. I still
have some TODO items (e.g Secrets, CPUID etc) before we take off the RFC
tag. Thank you so much for your detail feedback.


>
> Another tip: if you can add content to MdePkg that is governed by AMD
> specifications, such as patches #3, #5, #9, I would recommend splitting
> that off to a separate (pre-requisite) series. That content seems
> unaffected by whatever we do in OvmfPkg, and it would help decrease the
> size of the OvmfPkg series. It's not like we're going to abandon the
> SEV-SNP feature, so I don't think there's a risk in merging the MdePkg
> series first, while the OvmfPkg series is not ready. The MdePkg stuff
> will not remain unused indefinitely.


Ah, sure I can do that. 


-Brijsh


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-12 14:52   ` Brijesh Singh
@ 2021-04-13  9:49     ` Laszlo Ersek
  2021-04-13 11:29       ` Brijesh Singh
  2021-04-19 21:42       ` Brijesh Singh
  0 siblings, 2 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-13  9:49 UTC (permalink / raw)
  To: Brijesh Singh, devel
  Cc: James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky, Jordan Justen,
	Ard Biesheuvel

On 04/12/21 16:52, Brijesh Singh wrote:
> Hi James and Laszlo,
> 
> I was planning to work to add the support to reserve the Secrets and
> CPUID page in E820 map and then create the EFI configuration table entry
> for it so that guest OS can reach to it. We have two packages
> "SecretDxe" and "SecretPei" in OvmfPkg/AmdSev. Any issues if I use them
> in the OvmfPkg.dsc ? Here is what I was thinking:
> 
> 1) Rename the PcdSevLaunchSecretBase -> PcdSevSecretsBase
> 
> 2) When SNP is enabled then VMM use this page as secrets page for the SNP
> 
> 3) When SEV or SEV-ES is enabled then VMM uses this page as a launch
> secret page
> 
> This will allow me to drop PcdOvmfSnpSecretsBase. This will not just
> save 4-bytes but also minimize the code duplication.

I'm pretty unhappy about needing a separate page for each such purpose.
We're wasting room in MEMFD. The GUIDed structs that we expose to QEMU
seem to be flexible enough to describe non-page-aligned addresses,
right? Can we pack larger amounts of cruft into MEMFD pages?

I'm not looking forward to the day when we run out of slack in MEMFD and
we get to shift PEIFV / DXEFV. (Every time we need to increase the DXEFV
size, the same risk exists -- which is why I've been thinking for a
while now that OVMF includes too many features already.) This can
introduce obscure changes to the UEFI memory map, which has caused
compat problems in the past, for example with the "crash" utility.

The feature creep in OVMF has gone off the rails in the last few years,
really. (Not that I'm not guilty myself.)

Thanks,
Laszlo

> 
> Thoughts ?
> 
> -Brijesh
> 
> On 3/24/21 10:31 AM, Brijesh Singh wrote:
>> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275
>>
>> During the SEV-SNP guest launch sequence, two special pages need to
>> be inserted, the secrets page and cpuid page. The secrets page,
>> contain the VM platform communication keys. The guest BIOS and OS
>> can use this key to communicate with the SEV firmware to get the
>> attestation report. The Cpuid page, contain the CPUIDs entries
>> filtered through the AMD-SEV firmware.
>>
>> The VMM will locate the secrets and cpuid page addresses through a
>> fixed GUID and pass them to SEV firmware to populate further.
>> For more information about the page content, see the SEV-SNP spec.
>>
>> To simplify the pre-validation range calculation in the next patch,
>> the CPUID and Secrets pages are moved to the start of the
>> MEMFD_BASE_ADDRESS.
>>
>> Cc: James Bottomley <jejb@linux.ibm.com>
>> Cc: Min Xu <min.m.xu@intel.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Tom Lendacky <thomas.lendacky@amd.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>> ---
>>  OvmfPkg/OvmfPkg.dec                          |  8 +++++++
>>  OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
>>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
>>  OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
>>  OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
>>  5 files changed, 48 insertions(+), 9 deletions(-)
>>
>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>> index 4348bb45c6..062926772d 100644
>> --- a/OvmfPkg/OvmfPkg.dec
>> +++ b/OvmfPkg/OvmfPkg.dec
>> @@ -317,6 +317,14 @@
>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
>>  
>> +  ## The base address of the CPUID page used by SEV-SNP
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
>> +
>> +  ## The base address of the Secrets page used by SEV-SNP
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
>> +
>>  [PcdsDynamic, PcdsDynamicEx]
>>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>> index d519f85328..ea214600be 100644
>> --- a/OvmfPkg/OvmfPkgX64.fdf
>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>> @@ -67,27 +67,33 @@ ErasePolarity = 1
>>  BlockSize     = 0x10000
>>  NumBlocks     = 0xD0
>>  
>> -0x000000|0x006000
>> +0x000000|0x001000
>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
>> +
>> +0x001000|0x001000
>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>> +
>> +0x002000|0x006000
>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
>>  
>> -0x006000|0x001000
>> +0x008000|0x001000
>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
>>  
>> -0x007000|0x001000
>> +0x009000|0x001000
>>  gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>>  
>> -0x008000|0x001000
>> +0x00A000|0x001000
>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>  
>> -0x009000|0x002000
>> +0x00B000|0x002000
>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>  
>> -0x00B000|0x001000
>> -gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>> -
>> -0x00C000|0x001000
>> +0x00D000|0x001000
>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
>>  
>> +0x00F000|0x001000
>> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>> +
>>  0x010000|0x010000
>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>  
>> diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>> index 9c0b5853a4..5456f02924 100644
>> --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>> +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>> @@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart + 15) % 16)) DB 0
>>  ;
>>  guidedStructureStart:
>>  
>> +;
>> +; SEV-SNP boot support
>> +;
>> +; sevSnpBlock:
>> +;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must be
>> +;   reserved by the BIOS at a RAM area defined by SEV_SNP_SECRETS_PAGE
>> +;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
>> +;   SEV-SNP boot block.
>> +;
>> +; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
>> +;
>> +sevSnpBootBlockStart:
>> +    DD      SEV_SNP_SECRETS_PAGE
>> +    DD      SEV_SNP_CPUID_PAGE
>> +    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
>> +    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
>> +    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
>> +sevSnpBootBlockEnd:
>> +
>>  ;
>>  ; SEV Secret block
>>  ;
>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
>> index dc38f68919..d890bb6b29 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>> @@ -37,6 +37,10 @@
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
>> index 5fbacaed5f..2c194958f4 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>> @@ -75,6 +75,8 @@
>>    %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>>    %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 8)
>>    %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16)
>> +  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
>> +  %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
>>    %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
>>  %include "Ia32/Flat32ToFlat64.asm"
>>  %include "Ia32/PageTables64.asm"
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-13  9:49     ` Laszlo Ersek
@ 2021-04-13 11:29       ` Brijesh Singh
  2021-04-13 13:13         ` Laszlo Ersek
  2021-04-19 21:42       ` Brijesh Singh
  1 sibling, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-04-13 11:29 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: brijesh.singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel


On 4/13/21 4:49 AM, Laszlo Ersek wrote:
> On 04/12/21 16:52, Brijesh Singh wrote:
>> Hi James and Laszlo,
>>
>> I was planning to work to add the support to reserve the Secrets and
>> CPUID page in E820 map and then create the EFI configuration table entry
>> for it so that guest OS can reach to it. We have two packages
>> "SecretDxe" and "SecretPei" in OvmfPkg/AmdSev. Any issues if I use them
>> in the OvmfPkg.dsc ? Here is what I was thinking:
>>
>> 1) Rename the PcdSevLaunchSecretBase -> PcdSevSecretsBase
>>
>> 2) When SNP is enabled then VMM use this page as secrets page for the SNP
>>
>> 3) When SEV or SEV-ES is enabled then VMM uses this page as a launch
>> secret page
>>
>> This will allow me to drop PcdOvmfSnpSecretsBase. This will not just
>> save 4-bytes but also minimize the code duplication.
> I'm pretty unhappy about needing a separate page for each such purpose.
> We're wasting room in MEMFD. The GUIDed structs that we expose to QEMU
> seem to be flexible enough to describe non-page-aligned addresses,
> right? Can we pack larger amounts of cruft into MEMFD pages?

With the GUID approach we should be able to pack multiple fields into a
page but unfortunately in the case of SEV-SNP both the CPUID and Secrets
need to be a page size. Without SNP support the we reserve the following
page for the SEV/SEV-ES:

1 page for Launch Secret.

2 pages for the GHCB

1 page for EsWorkArea

Both the EsWorkArea and LaunchSecret does not need to be the page
aligned or page sized. Since the SNP needs a full page for the secrets
so  I was inclined to use the same secrets page for both SEV and SNP. 
At the end all we need to do is  reserve one extra page for CPUID to
make the SNP work.

In future the EsWorkArea page can be used to pack additional information
without needed to reserve full page (if feature does not require page).


>
> I'm not looking forward to the day when we run out of slack in MEMFD and
> we get to shift PEIFV / DXEFV. (Every time we need to increase the DXEFV
> size, the same risk exists -- which is why I've been thinking for a
> while now that OVMF includes too many features already.) This can
> introduce obscure changes to the UEFI memory map, which has caused
> compat problems in the past, for example with the "crash" utility.
>
> The feature creep in OVMF has gone off the rails in the last few years,
> really. (Not that I'm not guilty myself.)
>
> Thanks,
> Laszlo
>
>> Thoughts ?
>>
>> -Brijesh
>>
>> On 3/24/21 10:31 AM, Brijesh Singh wrote:
>>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D3275&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C04be68371db9458cbdc108d8fe61713a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637539041773579324%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=J62%2BVSCZRawAkzsi9xtS43cpowZxSCx%2BwcDYNwdF3qA%3D&amp;reserved=0
>>>
>>> During the SEV-SNP guest launch sequence, two special pages need to
>>> be inserted, the secrets page and cpuid page. The secrets page,
>>> contain the VM platform communication keys. The guest BIOS and OS
>>> can use this key to communicate with the SEV firmware to get the
>>> attestation report. The Cpuid page, contain the CPUIDs entries
>>> filtered through the AMD-SEV firmware.
>>>
>>> The VMM will locate the secrets and cpuid page addresses through a
>>> fixed GUID and pass them to SEV firmware to populate further.
>>> For more information about the page content, see the SEV-SNP spec.
>>>
>>> To simplify the pre-validation range calculation in the next patch,
>>> the CPUID and Secrets pages are moved to the start of the
>>> MEMFD_BASE_ADDRESS.
>>>
>>> Cc: James Bottomley <jejb@linux.ibm.com>
>>> Cc: Min Xu <min.m.xu@intel.com>
>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>> Cc: Tom Lendacky <thomas.lendacky@amd.com>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>>> ---
>>>  OvmfPkg/OvmfPkg.dec                          |  8 +++++++
>>>  OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
>>>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
>>>  OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
>>>  OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
>>>  5 files changed, 48 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>>> index 4348bb45c6..062926772d 100644
>>> --- a/OvmfPkg/OvmfPkg.dec
>>> +++ b/OvmfPkg/OvmfPkg.dec
>>> @@ -317,6 +317,14 @@
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
>>>  
>>> +  ## The base address of the CPUID page used by SEV-SNP
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
>>> +
>>> +  ## The base address of the Secrets page used by SEV-SNP
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
>>> +
>>>  [PcdsDynamic, PcdsDynamicEx]
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
>>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>>> index d519f85328..ea214600be 100644
>>> --- a/OvmfPkg/OvmfPkgX64.fdf
>>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>>> @@ -67,27 +67,33 @@ ErasePolarity = 1
>>>  BlockSize     = 0x10000
>>>  NumBlocks     = 0xD0
>>>  
>>> -0x000000|0x006000
>>> +0x000000|0x001000
>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
>>> +
>>> +0x001000|0x001000
>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>>> +
>>> +0x002000|0x006000
>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
>>>  
>>> -0x006000|0x001000
>>> +0x008000|0x001000
>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
>>>  
>>> -0x007000|0x001000
>>> +0x009000|0x001000
>>>  gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>>>  
>>> -0x008000|0x001000
>>> +0x00A000|0x001000
>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>>  
>>> -0x009000|0x002000
>>> +0x00B000|0x002000
>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>>  
>>> -0x00B000|0x001000
>>> -gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>>> -
>>> -0x00C000|0x001000
>>> +0x00D000|0x001000
>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
>>>  
>>> +0x00F000|0x001000
>>> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>>> +
>>>  0x010000|0x010000
>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>>  
>>> diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>>> index 9c0b5853a4..5456f02924 100644
>>> --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>>> +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>>> @@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart + 15) % 16)) DB 0
>>>  ;
>>>  guidedStructureStart:
>>>  
>>> +;
>>> +; SEV-SNP boot support
>>> +;
>>> +; sevSnpBlock:
>>> +;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must be
>>> +;   reserved by the BIOS at a RAM area defined by SEV_SNP_SECRETS_PAGE
>>> +;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
>>> +;   SEV-SNP boot block.
>>> +;
>>> +; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
>>> +;
>>> +sevSnpBootBlockStart:
>>> +    DD      SEV_SNP_SECRETS_PAGE
>>> +    DD      SEV_SNP_CPUID_PAGE
>>> +    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
>>> +    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
>>> +    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
>>> +sevSnpBootBlockEnd:
>>> +
>>>  ;
>>>  ; SEV Secret block
>>>  ;
>>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
>>> index dc38f68919..d890bb6b29 100644
>>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>>> @@ -37,6 +37,10 @@
>>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
>>> index 5fbacaed5f..2c194958f4 100644
>>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>>> @@ -75,6 +75,8 @@
>>>    %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>>>    %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 8)
>>>    %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16)
>>> +  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
>>> +  %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
>>>    %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
>>>  %include "Ia32/Flat32ToFlat64.asm"
>>>  %include "Ia32/PageTables64.asm"

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-04-12 20:14           ` Brijesh Singh
@ 2021-04-13 13:00             ` Laszlo Ersek
  2021-04-14 11:18               ` Brijesh Singh
  0 siblings, 1 reply; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-13 13:00 UTC (permalink / raw)
  To: devel, brijesh.singh
  Cc: James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky, Jordan Justen,
	Ard Biesheuvel

On 04/12/21 22:14, Brijesh Singh wrote:
>
> On 4/12/21 11:23 AM, Laszlo Ersek wrote:
>> On 04/10/21 00:43, Brijesh Singh wrote:
>>> On 4/9/21 7:24 AM, Laszlo Ersek wrote:

>>>> What is the primary threat (in simple terms please) that validation
>>>> is supposed to prevent?
>>>
>>> To protect against the memory re-mapping attack the guest pages must
>>> be validated. The idea is that every guest page can map only to a
>>> single physical memory page at one time.
>> I don't understand. How can a given guest page frame map to multiple
>> host page frames?
>>
>> Do you mean that the situation to prevent is when multiple guest page
>> frames (GPAs) map to the same host page frame, as set up by the
>> hypervisor?
>>
> A malicious hypervisor may attempt to remap validated gpa to a
> different host frame. The PVALIDATE is designed to protect against
> those type of attacks. See
> https://static.sched.com/hosted_files/lsseu2019/65/SEV-SNP%20Slides%20Nov%201%202019.pdf
> (slide 13). You can also find more information about it in the SEV-SNP
> whitepaper.

Thanks for the link. Page 7 confirms my understanding:

"""
Memory Aliasing
Example attack: Map two guest pages to same DRAM page
"""

(On the other hand, slide 12 seems to be buggy, "Page validation
guarantees that each GPA only maps to one valid SPA at any given time"
-- the GPA:SPA relationship is n:1, so any given GPA could never ever
map to multiple SPAs, regardless of SEV-SNP. The multiplicity is
possible in the other direction (a single SPA is exposed as multiple
GPAs, maybe in a single guest, maybe in multiple guests), and that's
what SEV-SNP is supposed to prevent. The GPA->SPA mapping must be
injective, but that's not what the words on the slide actually express.)

The page remapping slide (13) does not necessarily break injectivity,
and even if the hypervisor does that, the same GPA *still* corresponds
to a single SPA, at this new ("one") time. It's just that the
correspondence is not what the guest has approved. Expressing this
correctly would require a temporal logic formula of sorts.

Anyway... at least now I (believe I) understand that these are two
separate attacks (aliasing vs. remapping).

>> If pre-validation is simpler, and the only drawback is a one-time hit
>> during boot, then wouldn't it be better to stick with pre-validation
>> forever? I expect that would be a lot simpler for (a) the #VC
>> handlers, (b) the tracking of the "already validated" information.
>
> This could be an issue for the larger VMs. The boot delay will vary
> based on the VM size. In addition to the boot delay,  the PVALIDATE
> instruction requires that there is a valid entry for the page in the
> nested page table. If the entry does not exist in the NPT then HV will
> get #NPF and will fill the NPT with the backing host page. By using
> the lazy-validation we can push allocation of the backing pages to
> only when required and thus release the memory pressure on the VMM.

Ugh... OK, I guess. I'd need to digest this a lot more, for a fully
informed agreement, but I'm happy with this answer (and my rudimentary
understanding thereof) for now.

>> I guess my question related to the guest code that executes from
>> pflash, and/or accesses data from pflash, before the guest itself
>> could ask for any validation. Such as, the reset vector (which runs
>> from pflash). Then, some non-volatile UEFI variable data that resides
>> in the other pflash chip, and is accessed (read-only) during the PEI
>> phase (the memory type information variable namely). I understood
>> your comments as QEMU pre-validating those areas before guest launch.
>> Is that correct?
>
>
> Yes that is correct. All the OVMF pflash0 memory will be pre-validated
> before the guest is launched. IIRC, the variables which resides on the
> other pflash chip are accessed unencrypted in the guest. Only the
> encrypted memory access (aka C=1) need to be validated. The host MMIO
> regions does not need to be validated.

Good point. Thanks for the explanation / reminder!

> See if my above comment makes sense on why we may need the lazy
> validation. IMO, we should not be going with the lazy validation in
> OVMF. To minimize the boot delay and avoid the complexity of the code
> in the best case OVMF validated lower 4GB memory and mark rest of the
> memory as "unaccepted" in the UEFI map.

I'm going to violently support any idea that keeps things simple for
OVMF -- is this "lower 4GB" what the current patch set does already? Or
is it the proposed next step?

> David from google has started a thread about lazy validation in kernel
> https://www.spinics.net/lists/linux-mm/msg243954.html. We would like
> to find a method which works for both Intel TDX and AMD SEV. We will
> build these support incrementally both in the kernel and OVMF.

Ah, OK. That explains it. Next step then. Hmm, yes, patch#15 seems to
pre-validate everything now. OK.

> Yes this will work fine. The primary reason for me adding the interval
> tree was to detect the overlap condition before we finalize the
> pre-validation in PEI. The validation flow works like this:
>
> - Qemu validated few pages
>
> - SEC validates few more page
>
> - When we detect the memory in the PEI we need to exclude the pages
> validated by the Qemu and SEC.
>
> The page ranges validated using the Qemu and SEC are accessible
> through a fixed PCDs. I can do simple overlap check with those fixed
> PCDs and skip the pre-validated ranges. I don't have any strong reason
> to use the interval tree to detect the overlap condition. There is no
> call to toggle the C-bit before we finalize the validation.

Thank you. This is a *huge* relief.

> IMO, there is no need for tracking the page state after we finish the
> pre-validation. After the pre-validtion is completed, the caller will
> use either clear the C-bit or set the C-bit. If it attempts to set the
> C-bit without clearing it first then its a bug -- which will be caught
> by the page state change internal function. We can use the output of
> CF flag from the PVALIDATE to determine where caller is trying to do
> doubly validation or invalidation and abort it.

Sounds good. Even in case the final PVALIDATE action proves incorrect,
if we catch that immediately via CF, and refuse to do anything else
(i.e. we won't go ahead and use the page(s) in question), things should
be OK.

>> (6) Can you please confirm that the order in which
>> MemEncryptSevSnpValidateSystemRam()'s declaration, call sites, and
>> implementations are introduced, is sensible? I can't tell
>> immediately, but I'd like to be sure that the tree at least builds at
>> every stage (no linkage errors at any stage).
>
> I can certainly say the order in which they are introduced and the
> call sites are a sensible. I don't expect any linkage failure but
> since it was an RFC so I didn't go through making sure that it builds
> in every stage. I wanted to get the overall direction on where
> community would like to go before making the final changes. You have
> been asking some very good question and that is certainly helping us
> to reduce some of the complexity in the patch.

Thanks. I've been very stressed as to whether my questions or
"proposals" make any sense.

And yes, it's a desirable trait for a patch set to build at every stage
(bisectability).

> As I highlighted above there is actually no need to track the page
> state after we successfully complete the pre-validation.

Again, I welcome this very much.

> All these guest page
> validation or invalidation are applicable to the system RAM. But
> AmdSevDxe driver calls to clear the C-bit of the MMIO range. These
> range are not a system and have never been validated so we
> invalidating it will cause an issue.

Understood.

> Since I had the data structures available in PEI
> phase for the tracking the page state hence made those available to
> DXE to verify that we are called to invalidate the SYSTEM_RAM and not
> MMIO. IMO, we should either extend the
> MemEncryptSev{Set,Clear}PageEncMask() to pass either a new flag to
> hint where its system RAM or non-RAM. Thoughts ?

I think I'd prefer a new function in the lib class, one that's dedicated
to clearing the C bit on MMIO without attempting invalidation. It's a
special-case function, and the dedicated name helps with two things:

- no need to mess with existing call sites except in AmdSevDxe,
- the new function is easier to grep for.

BTW, there would be at least one other approach for this, I think -- the
PEI instance of the library could consult the HOB list, and the DXE
instance of the library could consult the GCD memory space map, to
decide whether the page being C-bit-flipped is MMIO or not. But that's a
lot of complexity (and likely some performance hit too), when in fact we
know at the call sites whether the area being encrypted/decrypted is
MMIO or RAM.

Note also that (IIUC) in AmdSevDxe, we only *decrypt*, so we only need
*one* new function, "MemEncryptSevClearMmioPageEncMask" or similar.

BTW, in the "MemEncryptSevLib.h" header, the documentation of the
MemEncryptSev{Set,Clear}PageEncMask function declarations should be
updated -- the leading comments should explain that, in case SEV-SNP is
found active, then page (in)validation will occur too (as appropriate).

> Yes we should bail out if such a request comes from the high level
> module. This is why I was actually not checking the tracking structure
> when toggling the C-bit. The complete memory much have been validated
> before we are asked to toggle the C-bit. If a module is performing a
> doubly validation or invalidation then it should be aborted to avoid
> any future exploits.

Great, thank you.

> If we go with the tracking only until we complete the pre-validation
> then we can use the hardware PVALIDATE help to detect the doubly
> validation and abort it. There is no need to check the overlap
> conditions.

Again, great. In the pre-validation step, where you explicitly carve out
the PCD-described ranges that have been handled already by QEMU and
earlier phases of the firmware, you can still check the PVALIDATE result
(CF), just to be sure.

> Certainly your points makes sense. Let me know what you think about
> removing the tracking data structure and using a liner array of
> pre-valiated range (build with a Fixed PCD) works ?

I couldn't have hoped for such a great resolution to this conundrum.
Yes, please do that.

> I was hoping you to just glance over it and provide me high level
> feedback. I will go through styles and comments in great details on
> non RFC patches. I still have some TODO items (e.g Secrets, CPUID etc)
> before we take off the RFC tag. Thank you so much for your detail
> feedback.

Well, this is the first email on the SEV-SNP topic where what I feel can
be described as "relief" :)

Thank you!
Laszlo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-13 11:29       ` Brijesh Singh
@ 2021-04-13 13:13         ` Laszlo Ersek
  0 siblings, 0 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-13 13:13 UTC (permalink / raw)
  To: Brijesh Singh, devel
  Cc: James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky, Jordan Justen,
	Ard Biesheuvel

On 04/13/21 13:29, Brijesh Singh wrote:
> 
> On 4/13/21 4:49 AM, Laszlo Ersek wrote:
>> On 04/12/21 16:52, Brijesh Singh wrote:
>>> Hi James and Laszlo,
>>>
>>> I was planning to work to add the support to reserve the Secrets and
>>> CPUID page in E820 map and then create the EFI configuration table entry
>>> for it so that guest OS can reach to it. We have two packages
>>> "SecretDxe" and "SecretPei" in OvmfPkg/AmdSev. Any issues if I use them
>>> in the OvmfPkg.dsc ? Here is what I was thinking:
>>>
>>> 1) Rename the PcdSevLaunchSecretBase -> PcdSevSecretsBase
>>>
>>> 2) When SNP is enabled then VMM use this page as secrets page for the SNP
>>>
>>> 3) When SEV or SEV-ES is enabled then VMM uses this page as a launch
>>> secret page
>>>
>>> This will allow me to drop PcdOvmfSnpSecretsBase. This will not just
>>> save 4-bytes but also minimize the code duplication.
>> I'm pretty unhappy about needing a separate page for each such purpose.
>> We're wasting room in MEMFD. The GUIDed structs that we expose to QEMU
>> seem to be flexible enough to describe non-page-aligned addresses,
>> right? Can we pack larger amounts of cruft into MEMFD pages?
> 
> With the GUID approach we should be able to pack multiple fields into a
> page but unfortunately in the case of SEV-SNP both the CPUID and Secrets
> need to be a page size. Without SNP support the we reserve the following
> page for the SEV/SEV-ES:
> 
> 1 page for Launch Secret.
> 
> 2 pages for the GHCB
> 
> 1 page for EsWorkArea
> 
> Both the EsWorkArea and LaunchSecret does not need to be the page
> aligned or page sized. Since the SNP needs a full page for the secrets
> so  I was inclined to use the same secrets page for both SEV and SNP. 
> At the end all we need to do is  reserve one extra page for CPUID to
> make the SNP work.
> 
> In future the EsWorkArea page can be used to pack additional information
> without needed to reserve full page (if feature does not require page).

Thank you for doing this analysis. I would much welcome a separate
series, just for "compressing" as many of the artifacts we now have down
to as few pages as possible. Could you propose a series like that?
Please CC Tom, James and Jiewen for review.

For this, feel free to rename PCDs as needed, and also to introduce new
(packed, if needed) structure types; probably under <OvmfPkg/Include/Guid>.

If some constants have to be doubly-defined, for C code and for assembly
code separately, for example, I'm fine with that -- normally, that's bad
practice, and if we can avoid it, that's great; but being conservative
with MEMFD is more important to me. (I hope I'm not going to eat my
words a few months down the road; this is how I feel right now anyway.)

Thanks!
Laszlo

> 
> 
>>
>> I'm not looking forward to the day when we run out of slack in MEMFD and
>> we get to shift PEIFV / DXEFV. (Every time we need to increase the DXEFV
>> size, the same risk exists -- which is why I've been thinking for a
>> while now that OVMF includes too many features already.) This can
>> introduce obscure changes to the UEFI memory map, which has caused
>> compat problems in the past, for example with the "crash" utility.
>>
>> The feature creep in OVMF has gone off the rails in the last few years,
>> really. (Not that I'm not guilty myself.)
>>
>> Thanks,
>> Laszlo
>>
>>> Thoughts ?
>>>
>>> -Brijesh
>>>
>>> On 3/24/21 10:31 AM, Brijesh Singh wrote:
>>>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D3275&amp;data=04%7C01%7Cbrijesh.singh%40amd.com%7C04be68371db9458cbdc108d8fe61713a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637539041773579324%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=J62%2BVSCZRawAkzsi9xtS43cpowZxSCx%2BwcDYNwdF3qA%3D&amp;reserved=0
>>>>
>>>> During the SEV-SNP guest launch sequence, two special pages need to
>>>> be inserted, the secrets page and cpuid page. The secrets page,
>>>> contain the VM platform communication keys. The guest BIOS and OS
>>>> can use this key to communicate with the SEV firmware to get the
>>>> attestation report. The Cpuid page, contain the CPUIDs entries
>>>> filtered through the AMD-SEV firmware.
>>>>
>>>> The VMM will locate the secrets and cpuid page addresses through a
>>>> fixed GUID and pass them to SEV firmware to populate further.
>>>> For more information about the page content, see the SEV-SNP spec.
>>>>
>>>> To simplify the pre-validation range calculation in the next patch,
>>>> the CPUID and Secrets pages are moved to the start of the
>>>> MEMFD_BASE_ADDRESS.
>>>>
>>>> Cc: James Bottomley <jejb@linux.ibm.com>
>>>> Cc: Min Xu <min.m.xu@intel.com>
>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>> Cc: Tom Lendacky <thomas.lendacky@amd.com>
>>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>>>> ---
>>>>  OvmfPkg/OvmfPkg.dec                          |  8 +++++++
>>>>  OvmfPkg/OvmfPkgX64.fdf                       | 24 ++++++++++++--------
>>>>  OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 19 ++++++++++++++++
>>>>  OvmfPkg/ResetVector/ResetVector.inf          |  4 ++++
>>>>  OvmfPkg/ResetVector/ResetVector.nasmb        |  2 ++
>>>>  5 files changed, 48 insertions(+), 9 deletions(-)
>>>>
>>>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>>>> index 4348bb45c6..062926772d 100644
>>>> --- a/OvmfPkg/OvmfPkg.dec
>>>> +++ b/OvmfPkg/OvmfPkg.dec
>>>> @@ -317,6 +317,14 @@
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43
>>>>  
>>>> +  ## The base address of the CPUID page used by SEV-SNP
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|0|UINT32|0x48
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize|0|UINT32|0x49
>>>> +
>>>> +  ## The base address of the Secrets page used by SEV-SNP
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|0|UINT32|0x50
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize|0|UINT32|0x51
>>>> +
>>>>  [PcdsDynamic, PcdsDynamicEx]
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
>>>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>>>> index d519f85328..ea214600be 100644
>>>> --- a/OvmfPkg/OvmfPkgX64.fdf
>>>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>>>> @@ -67,27 +67,33 @@ ErasePolarity = 1
>>>>  BlockSize     = 0x10000
>>>>  NumBlocks     = 0xD0
>>>>  
>>>> -0x000000|0x006000
>>>> +0x000000|0x001000
>>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
>>>> +
>>>> +0x001000|0x001000
>>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>>>> +
>>>> +0x002000|0x006000
>>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
>>>>  
>>>> -0x006000|0x001000
>>>> +0x008000|0x001000
>>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
>>>>  
>>>> -0x007000|0x001000
>>>> +0x009000|0x001000
>>>>  gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>>>>  
>>>> -0x008000|0x001000
>>>> +0x00A000|0x001000
>>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>>>  
>>>> -0x009000|0x002000
>>>> +0x00B000|0x002000
>>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>>>  
>>>> -0x00B000|0x001000
>>>> -gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>>>> -
>>>> -0x00C000|0x001000
>>>> +0x00D000|0x001000
>>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
>>>>  
>>>> +0x00F000|0x001000
>>>> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>>>> +
>>>>  0x010000|0x010000
>>>>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>>>  
>>>> diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>>>> index 9c0b5853a4..5456f02924 100644
>>>> --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>>>> +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>>>> @@ -47,6 +47,25 @@ TIMES (15 - ((guidedStructureEnd - guidedStructureStart + 15) % 16)) DB 0
>>>>  ;
>>>>  guidedStructureStart:
>>>>  
>>>> +;
>>>> +; SEV-SNP boot support
>>>> +;
>>>> +; sevSnpBlock:
>>>> +;   For the initial boot of SEV-SNP guest, a Secrets and CPUID page must be
>>>> +;   reserved by the BIOS at a RAM area defined by SEV_SNP_SECRETS_PAGE
>>>> +;   and SEV_SNP_CPUID_PAGE. A VMM will locate this information using the
>>>> +;   SEV-SNP boot block.
>>>> +;
>>>> +; GUID (SEV-SNP boot block): bd39c0c2-2f8e-4243-83e8-1b74cebcb7d9
>>>> +;
>>>> +sevSnpBootBlockStart:
>>>> +    DD      SEV_SNP_SECRETS_PAGE
>>>> +    DD      SEV_SNP_CPUID_PAGE
>>>> +    DW      sevSnpBootBlockEnd - sevSnpBootBlockStart
>>>> +    DB      0xC2, 0xC0, 0x39, 0xBD, 0x8e, 0x2F, 0x43, 0x42
>>>> +    DB      0x83, 0xE8, 0x1B, 0x74, 0xCE, 0xBC, 0xB7, 0xD9
>>>> +sevSnpBootBlockEnd:
>>>> +
>>>>  ;
>>>>  ; SEV Secret block
>>>>  ;
>>>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
>>>> index dc38f68919..d890bb6b29 100644
>>>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>>>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>>>> @@ -37,6 +37,10 @@
>>>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidBase
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpCpuidSize
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
>>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>>>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
>>>> index 5fbacaed5f..2c194958f4 100644
>>>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>>>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>>>> @@ -75,6 +75,8 @@
>>>>    %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>>>>    %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 8)
>>>>    %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16)
>>>> +  %define SEV_SNP_SECRETS_PAGE FixedPcdGet32 (PcdOvmfSnpSecretsBase)
>>>> +  %define SEV_SNP_CPUID_PAGE  FixedPcdGet32 (PcdOvmfSnpCpuidBase)
>>>>    %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
>>>>  %include "Ia32/Flat32ToFlat64.asm"
>>>>  %include "Ia32/PageTables64.asm"
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support
  2021-04-13 13:00             ` Laszlo Ersek
@ 2021-04-14 11:18               ` Brijesh Singh
  0 siblings, 0 replies; 68+ messages in thread
From: Brijesh Singh @ 2021-04-14 11:18 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: brijesh.singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel


>> Since I had the data structures available in PEI
>> phase for the tracking the page state hence made those available to
>> DXE to verify that we are called to invalidate the SYSTEM_RAM and not
>> MMIO. IMO, we should either extend the
>> MemEncryptSev{Set,Clear}PageEncMask() to pass either a new flag to
>> hint where its system RAM or non-RAM. Thoughts ?
> I think I'd prefer a new function in the lib class, one that's dedicated
> to clearing the C bit on MMIO without attempting invalidation. It's a
> special-case function, and the dedicated name helps with two things:
>
> - no need to mess with existing call sites except in AmdSevDxe,
> - the new function is easier to grep for.
>
> BTW, there would be at least one other approach for this, I think -- the
> PEI instance of the library could consult the HOB list, and the DXE
> instance of the library could consult the GCD memory space map, to
> decide whether the page being C-bit-flipped is MMIO or not. But that's a
> lot of complexity (and likely some performance hit too), when in fact we
> know at the call sites whether the area being encrypted/decrypted is
> MMIO or RAM.


Sound good to me, will introduce new function for the MMIO in v2.


> Note also that (IIUC) in AmdSevDxe, we only *decrypt*, so we only need
> *one* new function, "MemEncryptSevClearMmioPageEncMask" or similar.
>
> BTW, in the "MemEncryptSevLib.h" header, the documentation of the
> MemEncryptSev{Set,Clear}PageEncMask function declarations should be
> updated -- the leading comments should explain that, in case SEV-SNP is
> found active, then page (in)validation will occur too (as appropriate).

Noted.



^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-12 14:33                           ` James Bottomley
@ 2021-04-14 23:34                             ` erdemaktas
  2021-04-15  7:59                               ` Paolo Bonzini
  0 siblings, 1 reply; 68+ messages in thread
From: erdemaktas @ 2021-04-14 23:34 UTC (permalink / raw)
  To: devel, jejb
  Cc: Yao, Jiewen, dgilbert@redhat.com, Laszlo Ersek, Xu, Min M,
	thomas.lendacky@amd.com, Brijesh Singh, Justen, Jordan L,
	Ard Biesheuvel, Paolo Bonzini, Nathaniel McCallum

Hi all,

>>Can we please pry a little bit at that "one binary" requirement?

I think when we call it a "one binary" requirement, it sounds like we
are asking something new but what we are asking is pretty much
captured by James Bottomley.
We do not want to generate different binaries for AMD, Intel, Intel
with TDX, AMD with SEV/SNP etc therefore we were expecting the TDX
changes to be part of the upstream code.
Of course there can be tuning or customization for specific use cases
but those are all future and product specific questions. I just do not
see the reason why the upstreamed code should have a limitation of not
being able to generate a single binary for the same architecture.

-Erdem

On Mon, Apr 12, 2021 at 7:34 AM James Bottomley <jejb@linux.ibm.com> wrote:
>
> On Mon, 2021-04-12 at 11:54 +0000, Yao, Jiewen wrote:
> > I totally agree with you that from security perspective, the best
> > idea to isolate AMD SEV/Intel TDX from standard OVMF.
>
> There's a big difference between building tuned binaries and separating
> the subsystems entirely.  Ideally we don't want customers running
> images to have to build them differently for Intel or AMD (a bit like
> how QEMU/KVM hide the VM differences from users), and Confidential
> Computing shares a huge amount of interface similarity, so we wouldn't
> want that separated.  I think the rule should be that if both Intel and
> AMD expose a feature in different ways, OVMF tries to expose a uniform
> API for that feature over two differing implementations.
>
> > Do you want to propose move AMD SEV support to another SEC?
>
> You mean have an entirely separate SEC for AMD, OVMF and Intel?  I
> really wouldn't do that: much that's in the SEC: page table setup,
> memory mapping and decompression is common to all of them.  This all
> follows for a lot of the components.
>
> To build separate binaries, we just need separate dsc and fdf files.
> Then I think the goal would be to share as much as possible to avoid
> duplicating the maintenance and possibly diverging the user API.
>
> James
>
>
>
>
> 
>
>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-14 23:34                             ` erdemaktas
@ 2021-04-15  7:59                               ` Paolo Bonzini
  2021-04-15 19:42                                 ` Erdem Aktas
  0 siblings, 1 reply; 68+ messages in thread
From: Paolo Bonzini @ 2021-04-15  7:59 UTC (permalink / raw)
  To: Erdem Aktas, devel, jejb
  Cc: Yao, Jiewen, dgilbert@redhat.com, Laszlo Ersek, Xu, Min M,
	thomas.lendacky@amd.com, Brijesh Singh, Justen, Jordan L,
	Ard Biesheuvel, Nathaniel McCallum

On 15/04/21 01:34, Erdem Aktas wrote:
> We do not want to generate different binaries for AMD, Intel, Intel
> with TDX, AMD with SEV/SNP etc

My question is why the user would want a single binary for VMs with and 
without TDX/SNP.  I know there is attestation, but why would you even 
want the _possibility_ that your guest starts running without TDX or SNP 
protection, and only find out later via attestation?

For a similar reason, OVMF already supports shipping a binary that fails 
to boot if SMM is not available to the firmware, because then secure 
boot would be trivially circumvented.

I can understand having a single binary for both TDX or SNP.  That's not 
a problem since you can set up the SEV startup VMSA to 32-bit protected 
mode just like TDX wants.

> therefore we were expecting the TDX
> changes to be part of the upstream code.

Having 1 or more binaries should be unrelated to the changes being 
upstream (or more likely, I am misunderstanding you).

Thanks,

Paolo


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-15  7:59                               ` Paolo Bonzini
@ 2021-04-15 19:42                                 ` Erdem Aktas
  2021-04-21  0:38                                   ` Yao, Jiewen
  0 siblings, 1 reply; 68+ messages in thread
From: Erdem Aktas @ 2021-04-15 19:42 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: devel, jejb, Yao, Jiewen, dgilbert@redhat.com, Laszlo Ersek,
	Xu, Min M, thomas.lendacky@amd.com, Brijesh Singh,
	Justen, Jordan L, Ard Biesheuvel, Nathaniel McCallum, Ning Yang

Thanks Paolo.

On Thu, Apr 15, 2021 at 12:59 AM Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> On 15/04/21 01:34, Erdem Aktas wrote:
> > We do not want to generate different binaries for AMD, Intel, Intel
> > with TDX, AMD with SEV/SNP etc
>
> My question is why the user would want a single binary for VMs with and
> without TDX/SNP.  I know there is attestation, but why would you even
> want the _possibility_ that your guest starts running without TDX or SNP
> protection, and only find out later via attestation?

There might be multiple reasons why customers want it but we need this
requirement for a couple of other reasons too.

We do not only have hardware based confidential VMs. We might have
some other solutions which measure the initial image before boot.
Ultimately we might want to use a common attestation interface where
customers might be running different kinds of VMs. Using a single
binary will make it easier to manage/verify measurements for both of
us and the customers. I am not a PM so I cannot give more context on
customer use cases.

Another reason is how we deploy and manage guest firmware. We have a
lot of optimization and customization to speed up firmware loading
time and also reduce the time to deploy new builds on the whole fleet
uniformly.  Adding a new firmware binary is a big challenge for us to
enable these features. On the top of integration challenges, it will
create maintainability issues in the long run for us when we provide
tools to verify/reproduce the hashes in the attestation report.

> want the _possibility_ that your guest starts running without TDX or SNP
> protection, and only find out later via attestation?

I am missing the point here. Customers should rely on only the
attestation report to establish the trust.
-If firmware does not support TDX and TDX is enabled, that firmware
will crash at some point.
-If firmware is generic firmware that supports TDX and SNP and others,
and TDX is enabled or not,  still the customer needs to verify the TDX
enablement through attestation.
-If firmware is a customized binary compiled to support TDX,
irrelevant of TDX being enabled or not,  still the customer needs to
verify the TDX enablement through attestation.


> For a similar reason, OVMF already supports shipping a binary that fails
> to boot if SMM is not available to the firmware, because then secure
> boot would be trivially circumvented.
>
> I can understand having a single binary for both TDX or SNP.  That's not
> a problem since you can set up the SEV startup VMSA to 32-bit protected
> mode just like TDX wants.

I agree that this is doable but I am not sure if we need to also
modify the reset vector for AMD SNP in that case. Also it will not
solve our problem. If we start to generate a new firmware for every
feature , it will not end well for us, I think. Both TDX and SNP are
still new features in the same architecture, and it seems to me that
they are sharing a lot of common/similar code. AMD has already made
some of their patches in (SEV and SEV-ES) which works very nicely for
our use case and integration. Looks like Intel just has an issue on
how to fix their reset vector problem. Once they solve it and upstream
accepts the changes, I do not see any other big blocker. OVMF was
doing a great job on abstracting differences and providing a common
interface without creating multiple binaries. I do not see why it
should not do the same thing here.

> > therefore we were expecting the TDX
> > changes to be part of the upstream code.
>
> Having 1 or more binaries should be unrelated to the changes being
> upstream (or more likely, I am misunderstanding you).

You are right, it is my bad for not clarifying it. What I mean is we
want it to be part of the upstream so it can be easier for us to pull
the changes and they are compatible with the changes that SNP is doing
but we also do not want to use different configuration files to
generate different binaries for each use case.


> Thanks,
>
> Paolo
>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-13  9:49     ` Laszlo Ersek
  2021-04-13 11:29       ` Brijesh Singh
@ 2021-04-19 21:42       ` Brijesh Singh
  2021-04-20  8:14         ` Laszlo Ersek
  1 sibling, 1 reply; 68+ messages in thread
From: Brijesh Singh @ 2021-04-19 21:42 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: brijesh.singh, James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky,
	Jordan Justen, Ard Biesheuvel


On 4/13/21 4:49 AM, Laszlo Ersek wrote:
> On 04/12/21 16:52, Brijesh Singh wrote:
>> Hi James and Laszlo,
>>
>> I was planning to work to add the support to reserve the Secrets and
>> CPUID page in E820 map and then create the EFI configuration table entry
>> for it so that guest OS can reach to it. We have two packages
>> "SecretDxe" and "SecretPei" in OvmfPkg/AmdSev. Any issues if I use them
>> in the OvmfPkg.dsc ? Here is what I was thinking:
>>
>> 1) Rename the PcdSevLaunchSecretBase -> PcdSevSecretsBase
>>
>> 2) When SNP is enabled then VMM use this page as secrets page for the SNP
>>
>> 3) When SEV or SEV-ES is enabled then VMM uses this page as a launch
>> secret page
>>
>> This will allow me to drop PcdOvmfSnpSecretsBase. This will not just
>> save 4-bytes but also minimize the code duplication.
> I'm pretty unhappy about needing a separate page for each such purpose.
> We're wasting room in MEMFD. The GUIDed structs that we expose to QEMU
> seem to be flexible enough to describe non-page-aligned addresses,
> right? Can we pack larger amounts of cruft into MEMFD pages?
>
> I'm not looking forward to the day when we run out of slack in MEMFD and
> we get to shift PEIFV / DXEFV. (Every time we need to increase the DXEFV
> size, the same risk exists -- which is why I've been thinking for a
> while now that OVMF includes too many features already.) This can
> introduce obscure changes to the UEFI memory map, which has caused
> compat problems in the past, for example with the "crash" utility.


What's your take to move all SEV-specific reserved pages at the end of
PcdOvmfDecompressionScratchEnd ? I have not tried yet, but I can give
try to make sure the ES works after such moves.

What is a general rule of thump to what goes in MEMFD ? Is this all the
data pages accessed during the SEC phase ? If so, then we probably can't
do everything after the PcdOvmfDecompressionScratchEnd. The only thing
which we can quickly move out is a secret page.


> The feature creep in OVMF has gone off the rails in the last few years,
> really. (Not that I'm not guilty myself.)
>
> Thanks,
> Laszlo
>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest
  2021-04-19 21:42       ` Brijesh Singh
@ 2021-04-20  8:14         ` Laszlo Ersek
  0 siblings, 0 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-20  8:14 UTC (permalink / raw)
  To: Brijesh Singh, devel
  Cc: James Bottomley, Min Xu, Jiewen Yao, Tom Lendacky, Jordan Justen,
	Ard Biesheuvel

On 04/19/21 23:42, Brijesh Singh wrote:
> 
> On 4/13/21 4:49 AM, Laszlo Ersek wrote:
>> On 04/12/21 16:52, Brijesh Singh wrote:
>>> Hi James and Laszlo,
>>>
>>> I was planning to work to add the support to reserve the Secrets and
>>> CPUID page in E820 map and then create the EFI configuration table entry
>>> for it so that guest OS can reach to it. We have two packages
>>> "SecretDxe" and "SecretPei" in OvmfPkg/AmdSev. Any issues if I use them
>>> in the OvmfPkg.dsc ? Here is what I was thinking:
>>>
>>> 1) Rename the PcdSevLaunchSecretBase -> PcdSevSecretsBase
>>>
>>> 2) When SNP is enabled then VMM use this page as secrets page for the SNP
>>>
>>> 3) When SEV or SEV-ES is enabled then VMM uses this page as a launch
>>> secret page
>>>
>>> This will allow me to drop PcdOvmfSnpSecretsBase. This will not just
>>> save 4-bytes but also minimize the code duplication.
>> I'm pretty unhappy about needing a separate page for each such purpose.
>> We're wasting room in MEMFD. The GUIDed structs that we expose to QEMU
>> seem to be flexible enough to describe non-page-aligned addresses,
>> right? Can we pack larger amounts of cruft into MEMFD pages?
>>
>> I'm not looking forward to the day when we run out of slack in MEMFD and
>> we get to shift PEIFV / DXEFV. (Every time we need to increase the DXEFV
>> size, the same risk exists -- which is why I've been thinking for a
>> while now that OVMF includes too many features already.) This can
>> introduce obscure changes to the UEFI memory map, which has caused
>> compat problems in the past, for example with the "crash" utility.
> 
> 
> What's your take to move all SEV-specific reserved pages at the end of
> PcdOvmfDecompressionScratchEnd ? I have not tried yet, but I can give
> try to make sure the ES works after such moves.

I don't recommend that, without a serious audit anyway. While we use
PCDs in the source code rather than open-coded constants, and so it's
relatively easy to change the value of a PCD, the source code may very
well rely on the *relations* between PCDs. If you change PCDs such that
their relations change (by moving around areas), things can break.

If you absolutely need more room, I'd prefer enlarging the currently
available gap in-place (pushing up everything that's currently above it).

> What is a general rule of thump to what goes in MEMFD ? Is this all the
> data pages accessed during the SEC phase ?

More or less; it lets us lay out ranges, with symbolic names, that are
needed very early (reset vector, SEC), during normal boot or ACPI S3
resume. If something can be allocated manually during PEI or later,
yielding a variable address, then it arguably doesn't beling on MEMFD.

> If so, then we probably can't
> do everything after the PcdOvmfDecompressionScratchEnd. The only thing
> which we can quickly move out is a secret page.

IIRC, PcdOvmfDecompressionScratchEnd stands for the highest RAM address
overwritten during ACPI S3 resume. I wouldn't like to put anything above
it through MEMFD.

Thanks
Laszlo

> 
> 
>> The feature creep in OVMF has gone off the rails in the last few years,
>> really. (Not that I'm not guilty myself.)
>>
>> Thanks,
>> Laszlo
>>
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-15 19:42                                 ` Erdem Aktas
@ 2021-04-21  0:38                                   ` Yao, Jiewen
  2021-04-21 10:44                                     ` Laszlo Ersek
  0 siblings, 1 reply; 68+ messages in thread
From: Yao, Jiewen @ 2021-04-21  0:38 UTC (permalink / raw)
  To: Erdem Aktas, Paolo Bonzini
  Cc: devel@edk2.groups.io, jejb@linux.ibm.com, dgilbert@redhat.com,
	Laszlo Ersek, Xu, Min M, thomas.lendacky@amd.com, Brijesh Singh,
	Justen, Jordan L, Ard Biesheuvel, Nathaniel McCallum, Ning Yang

Hello 
Do we have some conclusion on this topic?

Do we agree the one-binary solution in OVMF or we need more discussion?


Thank you
Yao Jiewen

> -----Original Message-----
> From: Erdem Aktas <erdemaktas@google.com>
> Sent: Friday, April 16, 2021 3:43 AM
> To: Paolo Bonzini <pbonzini@redhat.com>
> Cc: devel@edk2.groups.io; jejb@linux.ibm.com; Yao, Jiewen
> <jiewen.yao@intel.com>; dgilbert@redhat.com; Laszlo Ersek
> <lersek@redhat.com>; Xu, Min M <min.m.xu@intel.com>;
> thomas.lendacky@amd.com; Brijesh Singh <brijesh.singh@amd.com>; Justen,
> Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> <ardb+tianocore@kernel.org>; Nathaniel McCallum
> <npmccallum@redhat.com>; Ning Yang <ningyang@google.com>
> Subject: Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg:
> Reserve the Secrets and Cpuid page for the SEV-SNP guest]
> 
> Thanks Paolo.
> 
> On Thu, Apr 15, 2021 at 12:59 AM Paolo Bonzini <pbonzini@redhat.com>
> wrote:
> >
> > On 15/04/21 01:34, Erdem Aktas wrote:
> > > We do not want to generate different binaries for AMD, Intel, Intel
> > > with TDX, AMD with SEV/SNP etc
> >
> > My question is why the user would want a single binary for VMs with and
> > without TDX/SNP.  I know there is attestation, but why would you even
> > want the _possibility_ that your guest starts running without TDX or SNP
> > protection, and only find out later via attestation?
> 
> There might be multiple reasons why customers want it but we need this
> requirement for a couple of other reasons too.
> 
> We do not only have hardware based confidential VMs. We might have
> some other solutions which measure the initial image before boot.
> Ultimately we might want to use a common attestation interface where
> customers might be running different kinds of VMs. Using a single
> binary will make it easier to manage/verify measurements for both of
> us and the customers. I am not a PM so I cannot give more context on
> customer use cases.
> 
> Another reason is how we deploy and manage guest firmware. We have a
> lot of optimization and customization to speed up firmware loading
> time and also reduce the time to deploy new builds on the whole fleet
> uniformly.  Adding a new firmware binary is a big challenge for us to
> enable these features. On the top of integration challenges, it will
> create maintainability issues in the long run for us when we provide
> tools to verify/reproduce the hashes in the attestation report.
> 
> > want the _possibility_ that your guest starts running without TDX or SNP
> > protection, and only find out later via attestation?
> 
> I am missing the point here. Customers should rely on only the
> attestation report to establish the trust.
> -If firmware does not support TDX and TDX is enabled, that firmware
> will crash at some point.
> -If firmware is generic firmware that supports TDX and SNP and others,
> and TDX is enabled or not,  still the customer needs to verify the TDX
> enablement through attestation.
> -If firmware is a customized binary compiled to support TDX,
> irrelevant of TDX being enabled or not,  still the customer needs to
> verify the TDX enablement through attestation.
> 
> 
> > For a similar reason, OVMF already supports shipping a binary that fails
> > to boot if SMM is not available to the firmware, because then secure
> > boot would be trivially circumvented.
> >
> > I can understand having a single binary for both TDX or SNP.  That's not
> > a problem since you can set up the SEV startup VMSA to 32-bit protected
> > mode just like TDX wants.
> 
> I agree that this is doable but I am not sure if we need to also
> modify the reset vector for AMD SNP in that case. Also it will not
> solve our problem. If we start to generate a new firmware for every
> feature , it will not end well for us, I think. Both TDX and SNP are
> still new features in the same architecture, and it seems to me that
> they are sharing a lot of common/similar code. AMD has already made
> some of their patches in (SEV and SEV-ES) which works very nicely for
> our use case and integration. Looks like Intel just has an issue on
> how to fix their reset vector problem. Once they solve it and upstream
> accepts the changes, I do not see any other big blocker. OVMF was
> doing a great job on abstracting differences and providing a common
> interface without creating multiple binaries. I do not see why it
> should not do the same thing here.
> 
> > > therefore we were expecting the TDX
> > > changes to be part of the upstream code.
> >
> > Having 1 or more binaries should be unrelated to the changes being
> > upstream (or more likely, I am misunderstanding you).
> 
> You are right, it is my bad for not clarifying it. What I mean is we
> want it to be part of the upstream so it can be easier for us to pull
> the changes and they are compatible with the changes that SNP is doing
> but we also do not want to use different configuration files to
> generate different binaries for each use case.
> 
> 
> > Thanks,
> >
> > Paolo
> >

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-21  0:38                                   ` Yao, Jiewen
@ 2021-04-21 10:44                                     ` Laszlo Ersek
  2021-04-21 17:07                                       ` Erdem Aktas
  0 siblings, 1 reply; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-21 10:44 UTC (permalink / raw)
  To: Yao, Jiewen, Erdem Aktas, Paolo Bonzini
  Cc: devel@edk2.groups.io, jejb@linux.ibm.com, dgilbert@redhat.com,
	Xu, Min M, thomas.lendacky@amd.com, Brijesh Singh,
	Justen, Jordan L, Ard Biesheuvel, Nathaniel McCallum, Ning Yang

On 04/21/21 02:38, Yao, Jiewen wrote:
> Hello 
> Do we have some conclusion on this topic?
> 
> Do we agree the one-binary solution in OVMF or we need more discussion?

Well it's not technically impossible to do, just very ugly and brittle.
And I'm doubtful that this is a unique problem ("just fix the reset
vector") the likes of which will supposedly never return during the
integration of SEV and TDX. Once we make this promise ("one firmware
binary at all costs"), the hacks we accept for its sake will only
accumulate over time, and we'll have more and more precedent to justify
the next hack. Technical debt is not exactly what we don't have enough
of, in edk2.

I won't make a secret out of the fact that I'm slightly annoyed that
this approach is being dictated by Google (as far as I understand, at
this point, anyway). I don't see or recall a lot of Google contributions
in the edk2 history or the bug tracker. I'm not enthusiastic about
complexity without explicit commitment / investment on the beneficiary's
side.

I won't nack the approach personally, but I'm quite unhappy about it.
Can Google at least propose a designated reviewer ("R") for the
"OvmfPkg: Confidential Computing" section of "Maintainers.txt", in a patch?

Thanks
Laszlo

> 
> 
> Thank you
> Yao Jiewen
> 
>> -----Original Message-----
>> From: Erdem Aktas <erdemaktas@google.com>
>> Sent: Friday, April 16, 2021 3:43 AM
>> To: Paolo Bonzini <pbonzini@redhat.com>
>> Cc: devel@edk2.groups.io; jejb@linux.ibm.com; Yao, Jiewen
>> <jiewen.yao@intel.com>; dgilbert@redhat.com; Laszlo Ersek
>> <lersek@redhat.com>; Xu, Min M <min.m.xu@intel.com>;
>> thomas.lendacky@amd.com; Brijesh Singh <brijesh.singh@amd.com>; Justen,
>> Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
>> <ardb+tianocore@kernel.org>; Nathaniel McCallum
>> <npmccallum@redhat.com>; Ning Yang <ningyang@google.com>
>> Subject: Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg:
>> Reserve the Secrets and Cpuid page for the SEV-SNP guest]
>>
>> Thanks Paolo.
>>
>> On Thu, Apr 15, 2021 at 12:59 AM Paolo Bonzini <pbonzini@redhat.com>
>> wrote:
>>>
>>> On 15/04/21 01:34, Erdem Aktas wrote:
>>>> We do not want to generate different binaries for AMD, Intel, Intel
>>>> with TDX, AMD with SEV/SNP etc
>>>
>>> My question is why the user would want a single binary for VMs with and
>>> without TDX/SNP.  I know there is attestation, but why would you even
>>> want the _possibility_ that your guest starts running without TDX or SNP
>>> protection, and only find out later via attestation?
>>
>> There might be multiple reasons why customers want it but we need this
>> requirement for a couple of other reasons too.
>>
>> We do not only have hardware based confidential VMs. We might have
>> some other solutions which measure the initial image before boot.
>> Ultimately we might want to use a common attestation interface where
>> customers might be running different kinds of VMs. Using a single
>> binary will make it easier to manage/verify measurements for both of
>> us and the customers. I am not a PM so I cannot give more context on
>> customer use cases.
>>
>> Another reason is how we deploy and manage guest firmware. We have a
>> lot of optimization and customization to speed up firmware loading
>> time and also reduce the time to deploy new builds on the whole fleet
>> uniformly.  Adding a new firmware binary is a big challenge for us to
>> enable these features. On the top of integration challenges, it will
>> create maintainability issues in the long run for us when we provide
>> tools to verify/reproduce the hashes in the attestation report.
>>
>>> want the _possibility_ that your guest starts running without TDX or SNP
>>> protection, and only find out later via attestation?
>>
>> I am missing the point here. Customers should rely on only the
>> attestation report to establish the trust.
>> -If firmware does not support TDX and TDX is enabled, that firmware
>> will crash at some point.
>> -If firmware is generic firmware that supports TDX and SNP and others,
>> and TDX is enabled or not,  still the customer needs to verify the TDX
>> enablement through attestation.
>> -If firmware is a customized binary compiled to support TDX,
>> irrelevant of TDX being enabled or not,  still the customer needs to
>> verify the TDX enablement through attestation.
>>
>>
>>> For a similar reason, OVMF already supports shipping a binary that fails
>>> to boot if SMM is not available to the firmware, because then secure
>>> boot would be trivially circumvented.
>>>
>>> I can understand having a single binary for both TDX or SNP.  That's not
>>> a problem since you can set up the SEV startup VMSA to 32-bit protected
>>> mode just like TDX wants.
>>
>> I agree that this is doable but I am not sure if we need to also
>> modify the reset vector for AMD SNP in that case. Also it will not
>> solve our problem. If we start to generate a new firmware for every
>> feature , it will not end well for us, I think. Both TDX and SNP are
>> still new features in the same architecture, and it seems to me that
>> they are sharing a lot of common/similar code. AMD has already made
>> some of their patches in (SEV and SEV-ES) which works very nicely for
>> our use case and integration. Looks like Intel just has an issue on
>> how to fix their reset vector problem. Once they solve it and upstream
>> accepts the changes, I do not see any other big blocker. OVMF was
>> doing a great job on abstracting differences and providing a common
>> interface without creating multiple binaries. I do not see why it
>> should not do the same thing here.
>>
>>>> therefore we were expecting the TDX
>>>> changes to be part of the upstream code.
>>>
>>> Having 1 or more binaries should be unrelated to the changes being
>>> upstream (or more likely, I am misunderstanding you).
>>
>> You are right, it is my bad for not clarifying it. What I mean is we
>> want it to be part of the upstream so it can be easier for us to pull
>> the changes and they are compatible with the changes that SNP is doing
>> but we also do not want to use different configuration files to
>> generate different binaries for each use case.
>>
>>
>>> Thanks,
>>>
>>> Paolo
>>>


^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-21 10:44                                     ` Laszlo Ersek
@ 2021-04-21 17:07                                       ` Erdem Aktas
  2021-04-22 14:20                                         ` Laszlo Ersek
  0 siblings, 1 reply; 68+ messages in thread
From: Erdem Aktas @ 2021-04-21 17:07 UTC (permalink / raw)
  To: devel, Laszlo Ersek
  Cc: Yao, Jiewen, Paolo Bonzini, jejb@linux.ibm.com,
	dgilbert@redhat.com, Xu, Min M, thomas.lendacky@amd.com,
	Brijesh Singh, Justen, Jordan L, Ard Biesheuvel,
	Nathaniel McCallum, Ning Yang

[-- Attachment #1: Type: text/plain, Size: 7911 bytes --]

Hi Laszlo,

I am sorry to hear that it sounded like we are dictating a certain
approach. Although I can see why it sounded that way, it certainly was not
my intention.
We want to work with the EDK2 community to have a solution that is
beneficial for everyone and we appreciate the inputs that we got from you
and Paolo.  Code quality is always a high priority for us. Therefore, if,
at some point, things get too hacky to impact the
quality/maintainability of the upstream code, we will consider making
adjustments on our side.

With the current discussion, I was just trying to describe our use case and
the importance of having a single binary where there is no absolute need
for architectural differences. As far as I know, the only problematic area
is modifying the reset vector to be compatible with TDX and it seems like
Intel has a solution for it without impacting the overall quality of the
upstream code. I just want to reiterate that we are open for discussion and
what we ask should not be considered "at all cost" and please let us know
that if edk2 community/maintainers are still thinking that what Intel is
proposing is not feasible.

>>Can Google at least propose a designated reviewer ("R") for the
>>"OvmfPkg: Confidential Computing" section of "Maintainers.txt", in a
patch?
Sure I would be happy too.

-Erdem

On Wed, Apr 21, 2021 at 3:44 AM Laszlo Ersek <lersek@redhat.com> wrote:

> On 04/21/21 02:38, Yao, Jiewen wrote:
> > Hello
> > Do we have some conclusion on this topic?
> >
> > Do we agree the one-binary solution in OVMF or we need more discussion?
>
> Well it's not technically impossible to do, just very ugly and brittle.
> And I'm doubtful that this is a unique problem ("just fix the reset
> vector") the likes of which will supposedly never return during the
> integration of SEV and TDX. Once we make this promise ("one firmware
> binary at all costs"), the hacks we accept for its sake will only
> accumulate over time, and we'll have more and more precedent to justify
> the next hack. Technical debt is not exactly what we don't have enough
> of, in edk2.
>
> I won't make a secret out of the fact that I'm slightly annoyed that
> this approach is being dictated by Google (as far as I understand, at
> this point, anyway). I don't see or recall a lot of Google contributions
> in the edk2 history or the bug tracker. I'm not enthusiastic about
> complexity without explicit commitment / investment on the beneficiary's
> side.
>
> I won't nack the approach personally, but I'm quite unhappy about it.
> Can Google at least propose a designated reviewer ("R") for the
> "OvmfPkg: Confidential Computing" section of "Maintainers.txt", in a patch?
>
> Thanks
> Laszlo
>
> >
> >
> > Thank you
> > Yao Jiewen
> >
> >> -----Original Message-----
> >> From: Erdem Aktas <erdemaktas@google.com>
> >> Sent: Friday, April 16, 2021 3:43 AM
> >> To: Paolo Bonzini <pbonzini@redhat.com>
> >> Cc: devel@edk2.groups.io; jejb@linux.ibm.com; Yao, Jiewen
> >> <jiewen.yao@intel.com>; dgilbert@redhat.com; Laszlo Ersek
> >> <lersek@redhat.com>; Xu, Min M <min.m.xu@intel.com>;
> >> thomas.lendacky@amd.com; Brijesh Singh <brijesh.singh@amd.com>; Justen,
> >> Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
> >> <ardb+tianocore@kernel.org>; Nathaniel McCallum
> >> <npmccallum@redhat.com>; Ning Yang <ningyang@google.com>
> >> Subject: Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg:
> >> Reserve the Secrets and Cpuid page for the SEV-SNP guest]
> >>
> >> Thanks Paolo.
> >>
> >> On Thu, Apr 15, 2021 at 12:59 AM Paolo Bonzini <pbonzini@redhat.com>
> >> wrote:
> >>>
> >>> On 15/04/21 01:34, Erdem Aktas wrote:
> >>>> We do not want to generate different binaries for AMD, Intel, Intel
> >>>> with TDX, AMD with SEV/SNP etc
> >>>
> >>> My question is why the user would want a single binary for VMs with and
> >>> without TDX/SNP.  I know there is attestation, but why would you even
> >>> want the _possibility_ that your guest starts running without TDX or
> SNP
> >>> protection, and only find out later via attestation?
> >>
> >> There might be multiple reasons why customers want it but we need this
> >> requirement for a couple of other reasons too.
> >>
> >> We do not only have hardware based confidential VMs. We might have
> >> some other solutions which measure the initial image before boot.
> >> Ultimately we might want to use a common attestation interface where
> >> customers might be running different kinds of VMs. Using a single
> >> binary will make it easier to manage/verify measurements for both of
> >> us and the customers. I am not a PM so I cannot give more context on
> >> customer use cases.
> >>
> >> Another reason is how we deploy and manage guest firmware. We have a
> >> lot of optimization and customization to speed up firmware loading
> >> time and also reduce the time to deploy new builds on the whole fleet
> >> uniformly.  Adding a new firmware binary is a big challenge for us to
> >> enable these features. On the top of integration challenges, it will
> >> create maintainability issues in the long run for us when we provide
> >> tools to verify/reproduce the hashes in the attestation report.
> >>
> >>> want the _possibility_ that your guest starts running without TDX or
> SNP
> >>> protection, and only find out later via attestation?
> >>
> >> I am missing the point here. Customers should rely on only the
> >> attestation report to establish the trust.
> >> -If firmware does not support TDX and TDX is enabled, that firmware
> >> will crash at some point.
> >> -If firmware is generic firmware that supports TDX and SNP and others,
> >> and TDX is enabled or not,  still the customer needs to verify the TDX
> >> enablement through attestation.
> >> -If firmware is a customized binary compiled to support TDX,
> >> irrelevant of TDX being enabled or not,  still the customer needs to
> >> verify the TDX enablement through attestation.
> >>
> >>
> >>> For a similar reason, OVMF already supports shipping a binary that
> fails
> >>> to boot if SMM is not available to the firmware, because then secure
> >>> boot would be trivially circumvented.
> >>>
> >>> I can understand having a single binary for both TDX or SNP.  That's
> not
> >>> a problem since you can set up the SEV startup VMSA to 32-bit protected
> >>> mode just like TDX wants.
> >>
> >> I agree that this is doable but I am not sure if we need to also
> >> modify the reset vector for AMD SNP in that case. Also it will not
> >> solve our problem. If we start to generate a new firmware for every
> >> feature , it will not end well for us, I think. Both TDX and SNP are
> >> still new features in the same architecture, and it seems to me that
> >> they are sharing a lot of common/similar code. AMD has already made
> >> some of their patches in (SEV and SEV-ES) which works very nicely for
> >> our use case and integration. Looks like Intel just has an issue on
> >> how to fix their reset vector problem. Once they solve it and upstream
> >> accepts the changes, I do not see any other big blocker. OVMF was
> >> doing a great job on abstracting differences and providing a common
> >> interface without creating multiple binaries. I do not see why it
> >> should not do the same thing here.
> >>
> >>>> therefore we were expecting the TDX
> >>>> changes to be part of the upstream code.
> >>>
> >>> Having 1 or more binaries should be unrelated to the changes being
> >>> upstream (or more likely, I am misunderstanding you).
> >>
> >> You are right, it is my bad for not clarifying it. What I mean is we
> >> want it to be part of the upstream so it can be easier for us to pull
> >> the changes and they are compatible with the changes that SNP is doing
> >> but we also do not want to use different configuration files to
> >> generate different binaries for each use case.
> >>
> >>
> >>> Thanks,
> >>>
> >>> Paolo
> >>>
>
>
>
> 
>
>
>

[-- Attachment #2: Type: text/html, Size: 10576 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest]
  2021-04-21 17:07                                       ` Erdem Aktas
@ 2021-04-22 14:20                                         ` Laszlo Ersek
  0 siblings, 0 replies; 68+ messages in thread
From: Laszlo Ersek @ 2021-04-22 14:20 UTC (permalink / raw)
  To: Erdem Aktas, devel
  Cc: Yao, Jiewen, Paolo Bonzini, jejb@linux.ibm.com,
	dgilbert@redhat.com, Xu, Min M, thomas.lendacky@amd.com,
	Brijesh Singh, Justen, Jordan L, Ard Biesheuvel,
	Nathaniel McCallum, Ning Yang

On 04/21/21 19:07, Erdem Aktas wrote:
> Hi Laszlo,
> 
> I am sorry to hear that it sounded like we are dictating a certain
> approach. Although I can see why it sounded that way, it certainly was not
> my intention.
> We want to work with the EDK2 community to have a solution that is
> beneficial for everyone and we appreciate the inputs that we got from you
> and Paolo.  Code quality is always a high priority for us. Therefore, if,
> at some point, things get too hacky to impact the
> quality/maintainability of the upstream code, we will consider making
> adjustments on our side.
> 
> With the current discussion, I was just trying to describe our use case and
> the importance of having a single binary where there is no absolute need
> for architectural differences. As far as I know, the only problematic area
> is modifying the reset vector to be compatible with TDX and it seems like
> Intel has a solution for it without impacting the overall quality of the
> upstream code. I just want to reiterate that we are open for discussion and
> what we ask should not be considered "at all cost" and please let us know
> that if edk2 community/maintainers are still thinking that what Intel is
> proposing is not feasible.

OK.

It's not lost on me that we're talking about ~3 instructions.

Let's keep a close eye on further "polymorphisms" that would require hacks.

> 
>>> Can Google at least propose a designated reviewer ("R") for the
>>> "OvmfPkg: Confidential Computing" section of "Maintainers.txt", in a
> patch?
> Sure I would be happy too.

Yes, please do that. It can be included in the TDX patch set from Min Xu
that modifies the beginning of reset vector as discussed above.

Thanks!
Laszlo

> 
> -Erdem
> 
> On Wed, Apr 21, 2021 at 3:44 AM Laszlo Ersek <lersek@redhat.com> wrote:
> 
>> On 04/21/21 02:38, Yao, Jiewen wrote:
>>> Hello
>>> Do we have some conclusion on this topic?
>>>
>>> Do we agree the one-binary solution in OVMF or we need more discussion?
>>
>> Well it's not technically impossible to do, just very ugly and brittle.
>> And I'm doubtful that this is a unique problem ("just fix the reset
>> vector") the likes of which will supposedly never return during the
>> integration of SEV and TDX. Once we make this promise ("one firmware
>> binary at all costs"), the hacks we accept for its sake will only
>> accumulate over time, and we'll have more and more precedent to justify
>> the next hack. Technical debt is not exactly what we don't have enough
>> of, in edk2.
>>
>> I won't make a secret out of the fact that I'm slightly annoyed that
>> this approach is being dictated by Google (as far as I understand, at
>> this point, anyway). I don't see or recall a lot of Google contributions
>> in the edk2 history or the bug tracker. I'm not enthusiastic about
>> complexity without explicit commitment / investment on the beneficiary's
>> side.
>>
>> I won't nack the approach personally, but I'm quite unhappy about it.
>> Can Google at least propose a designated reviewer ("R") for the
>> "OvmfPkg: Confidential Computing" section of "Maintainers.txt", in a patch?
>>
>> Thanks
>> Laszlo
>>
>>>
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>> -----Original Message-----
>>>> From: Erdem Aktas <erdemaktas@google.com>
>>>> Sent: Friday, April 16, 2021 3:43 AM
>>>> To: Paolo Bonzini <pbonzini@redhat.com>
>>>> Cc: devel@edk2.groups.io; jejb@linux.ibm.com; Yao, Jiewen
>>>> <jiewen.yao@intel.com>; dgilbert@redhat.com; Laszlo Ersek
>>>> <lersek@redhat.com>; Xu, Min M <min.m.xu@intel.com>;
>>>> thomas.lendacky@amd.com; Brijesh Singh <brijesh.singh@amd.com>; Justen,
>>>> Jordan L <jordan.l.justen@intel.com>; Ard Biesheuvel
>>>> <ardb+tianocore@kernel.org>; Nathaniel McCallum
>>>> <npmccallum@redhat.com>; Ning Yang <ningyang@google.com>
>>>> Subject: Re: [edk2-devel] separate OVMF binary for TDX? [was: OvmfPkg:
>>>> Reserve the Secrets and Cpuid page for the SEV-SNP guest]
>>>>
>>>> Thanks Paolo.
>>>>
>>>> On Thu, Apr 15, 2021 at 12:59 AM Paolo Bonzini <pbonzini@redhat.com>
>>>> wrote:
>>>>>
>>>>> On 15/04/21 01:34, Erdem Aktas wrote:
>>>>>> We do not want to generate different binaries for AMD, Intel, Intel
>>>>>> with TDX, AMD with SEV/SNP etc
>>>>>
>>>>> My question is why the user would want a single binary for VMs with and
>>>>> without TDX/SNP.  I know there is attestation, but why would you even
>>>>> want the _possibility_ that your guest starts running without TDX or
>> SNP
>>>>> protection, and only find out later via attestation?
>>>>
>>>> There might be multiple reasons why customers want it but we need this
>>>> requirement for a couple of other reasons too.
>>>>
>>>> We do not only have hardware based confidential VMs. We might have
>>>> some other solutions which measure the initial image before boot.
>>>> Ultimately we might want to use a common attestation interface where
>>>> customers might be running different kinds of VMs. Using a single
>>>> binary will make it easier to manage/verify measurements for both of
>>>> us and the customers. I am not a PM so I cannot give more context on
>>>> customer use cases.
>>>>
>>>> Another reason is how we deploy and manage guest firmware. We have a
>>>> lot of optimization and customization to speed up firmware loading
>>>> time and also reduce the time to deploy new builds on the whole fleet
>>>> uniformly.  Adding a new firmware binary is a big challenge for us to
>>>> enable these features. On the top of integration challenges, it will
>>>> create maintainability issues in the long run for us when we provide
>>>> tools to verify/reproduce the hashes in the attestation report.
>>>>
>>>>> want the _possibility_ that your guest starts running without TDX or
>> SNP
>>>>> protection, and only find out later via attestation?
>>>>
>>>> I am missing the point here. Customers should rely on only the
>>>> attestation report to establish the trust.
>>>> -If firmware does not support TDX and TDX is enabled, that firmware
>>>> will crash at some point.
>>>> -If firmware is generic firmware that supports TDX and SNP and others,
>>>> and TDX is enabled or not,  still the customer needs to verify the TDX
>>>> enablement through attestation.
>>>> -If firmware is a customized binary compiled to support TDX,
>>>> irrelevant of TDX being enabled or not,  still the customer needs to
>>>> verify the TDX enablement through attestation.
>>>>
>>>>
>>>>> For a similar reason, OVMF already supports shipping a binary that
>> fails
>>>>> to boot if SMM is not available to the firmware, because then secure
>>>>> boot would be trivially circumvented.
>>>>>
>>>>> I can understand having a single binary for both TDX or SNP.  That's
>> not
>>>>> a problem since you can set up the SEV startup VMSA to 32-bit protected
>>>>> mode just like TDX wants.
>>>>
>>>> I agree that this is doable but I am not sure if we need to also
>>>> modify the reset vector for AMD SNP in that case. Also it will not
>>>> solve our problem. If we start to generate a new firmware for every
>>>> feature , it will not end well for us, I think. Both TDX and SNP are
>>>> still new features in the same architecture, and it seems to me that
>>>> they are sharing a lot of common/similar code. AMD has already made
>>>> some of their patches in (SEV and SEV-ES) which works very nicely for
>>>> our use case and integration. Looks like Intel just has an issue on
>>>> how to fix their reset vector problem. Once they solve it and upstream
>>>> accepts the changes, I do not see any other big blocker. OVMF was
>>>> doing a great job on abstracting differences and providing a common
>>>> interface without creating multiple binaries. I do not see why it
>>>> should not do the same thing here.
>>>>
>>>>>> therefore we were expecting the TDX
>>>>>> changes to be part of the upstream code.
>>>>>
>>>>> Having 1 or more binaries should be unrelated to the changes being
>>>>> upstream (or more likely, I am misunderstanding you).
>>>>
>>>> You are right, it is my bad for not clarifying it. What I mean is we
>>>> want it to be part of the upstream so it can be easier for us to pull
>>>> the changes and they are compatible with the changes that SNP is doing
>>>> but we also do not want to use different configuration files to
>>>> generate different binaries for each use case.
>>>>
>>>>
>>>>> Thanks,
>>>>>
>>>>> Paolo
>>>>>
>>
>>
>>
>> 
>>
>>
>>
> 


^ permalink raw reply	[flat|nested] 68+ messages in thread

end of thread, other threads:[~2021-04-22 14:20 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-03-24 15:31 [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support brijesh.singh
2021-03-24 15:31 ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Brijesh Singh
2021-04-06  8:11   ` Min Xu
2021-04-06 12:16     ` Laszlo Ersek
2021-04-07  0:21       ` Min Xu
2021-04-07  0:44         ` James Bottomley
2021-04-07 15:02           ` Laszlo Ersek
2021-04-07 15:12             ` James Bottomley
2021-04-08  6:24             ` [edk2-devel] " Min Xu
2021-04-08 13:31               ` Lendacky, Thomas
2021-04-09 12:29                 ` Laszlo Ersek
2021-04-09 13:32                 ` Laszlo Ersek
2021-04-09 13:44                   ` Yao, Jiewen
2021-04-09 14:11                     ` separate OVMF binary for TDX? [was: OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest] Laszlo Ersek
2021-04-12  8:35                       ` Dr. David Alan Gilbert
2021-04-12 11:54                         ` [edk2-devel] " Yao, Jiewen
2021-04-12 14:33                           ` James Bottomley
2021-04-14 23:34                             ` erdemaktas
2021-04-15  7:59                               ` Paolo Bonzini
2021-04-15 19:42                                 ` Erdem Aktas
2021-04-21  0:38                                   ` Yao, Jiewen
2021-04-21 10:44                                     ` Laszlo Ersek
2021-04-21 17:07                                       ` Erdem Aktas
2021-04-22 14:20                                         ` Laszlo Ersek
2021-04-07 13:22         ` [RFC PATCH 01/19] OvmfPkg: Reserve the Secrets and Cpuid page for the SEV-SNP guest Laszlo Ersek
2021-04-07 13:24           ` Laszlo Ersek
2021-04-08  0:45           ` Min Xu
2021-04-07  0:31       ` James Bottomley
2021-04-12 14:52   ` Brijesh Singh
2021-04-13  9:49     ` Laszlo Ersek
2021-04-13 11:29       ` Brijesh Singh
2021-04-13 13:13         ` Laszlo Ersek
2021-04-19 21:42       ` Brijesh Singh
2021-04-20  8:14         ` Laszlo Ersek
2021-03-24 15:31 ` [RFC PATCH 02/19] OvmfPkg: validate the data pages used in the SEC phase Brijesh Singh
2021-03-24 15:31 ` [RFC PATCH 03/19] MdePkg: Expand the SEV MSR to include the SNP definition Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 04/19] OvmfPkg/MemEncryptSevLib: add MemEncryptSevSnpEnabled() Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 05/19] MdePkg: Define the GHCB GPA structure Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 06/19] UefiCpuPkg/MpLib: add support to register GHCB GPA when SEV-SNP is enabled Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 07/19] OvmfPkg: Add a library to support registering GHCB GPA Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 08/19] OvmfPkg: register GHCB gpa for the SEV-SNP guest Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 09/19] MdePkg: Add AsmPvalidate() support Brijesh Singh
2021-03-25  2:49   ` 回复: [edk2-devel] " gaoliming
2021-03-25 10:54     ` Brijesh Singh
2021-03-26 20:02       ` Andrew Fish
2021-03-24 15:32 ` [RFC PATCH 10/19] OvmfPkg: Define the Page State Change VMGEXIT structures Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 11/19] OvmfPkg/ResetVector: Invalidate the GHCB page Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 12/19] OvmfPkg/MemEncryptSevLib: Add support to validate system RAM Brijesh Singh
2021-04-01  6:37   ` Yao, Jiewen
2021-04-01 13:07     ` Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 13/19] OvmfPkg/SecMain: Validate the data/code pages used for the PEI phase Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 14/19] OvmfPkg/MemEncryptSevLib: Add support to validate RAM in " Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 15/19] OvmfPkg/PlatformPei: Validate the system RAM when SNP is active Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 16/19] OvmfPkg/MemEncryptSevLib: Add support to validate > 4GB memory in PEI phase Brijesh Singh
2021-04-01  6:43   ` Yao, Jiewen
2021-03-24 15:32 ` [RFC PATCH 17/19] OvmfPkg/VmgExitLib: Allow PMBASE register access in Dxe phase Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 18/19] OvmfPkg/MemEncryptSevLib: Validate the memory during set or clear enc attribute Brijesh Singh
2021-03-24 20:07   ` Brijesh Singh
2021-03-24 15:32 ` [RFC PATCH 19/19] OvmfPkg/MemEncryptSevLib: Skip page state change for non RAM region Brijesh Singh
2021-03-24 19:14 ` [edk2-devel] [RFC PATCH 00/19] Add AMD Secure Nested Paging (SEV-SNP) support Laszlo Ersek
2021-04-08  9:58 ` Laszlo Ersek
2021-04-08 11:59   ` Brijesh Singh
2021-04-09 12:24     ` Laszlo Ersek
2021-04-09 22:43       ` Brijesh Singh
2021-04-12 16:23         ` Laszlo Ersek
2021-04-12 20:14           ` Brijesh Singh
2021-04-13 13:00             ` Laszlo Ersek
2021-04-14 11:18               ` Brijesh Singh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox