public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH v8 00/46] SEV-ES guest support
@ 2020-05-19 21:50 Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 01/46] MdeModulePkg: Create PCDs to be used in support of SEV-ES Lendacky, Thomas
                   ` (46 more replies)
  0 siblings, 47 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Anthony Perard,
	Benjamin You, Dandan Bi, Guo Dong, Hao A Wu, Jian J Wang,
	Julien Grall, Maurice Ma

This patch series provides support for running EDK2/OVMF under SEV-ES.

Secure Encrypted Virtualization - Encrypted State (SEV-ES) expands on the
SEV support to protect the guest register state from the hypervisor. See
"AMD64 Architecture Programmer's Manual Volume 2: System Programming",
section "15.35 Encrypted State (SEV-ES)" [1].

In order to allow a hypervisor to perform functions on behalf of a guest,
there is architectural support for notifying a guest's operating system
when certain types of VMEXITs are about to occur. This allows the guest to
selectively share information with the hypervisor to satisfy the requested
function. The notification is performed using a new exception, the VMM
Communication exception (#VC). The information is shared through the
Guest-Hypervisor Communication Block (GHCB) using the VMGEXIT instruction.
The GHCB format and the protocol for using it is documented in "SEV-ES
Guest-Hypervisor Communication Block Standardization" [2].

The main areas of the EDK2 code that are updated to support SEV-ES are
around the exception handling support and the AP boot support.

Exception support is required starting in Sec, continuing through Pei
and into Dxe in order to handle #VC exceptions that are generated.  Each
AP requires it's own GHCB page as well as a page to hold values specific
to that AP.

AP booting poses some interesting challenges. The INIT-SIPI-SIPI sequence
is typically used to boot the APs. However, the hypervisor is not allowed
to update the guest registers. The GHCB document [2] talks about how SMP
booting under SEV-ES is performed.

Since the GHCB page must be a shared (unencrypted) page, the processor
must be running in long mode in order for the guest and hypervisor to
communicate with each other. As a result, SEV-ES is only supported under
the X64 architecture.

[1] https://www.amd.com/system/files/TechDocs/24593.pdf
[2] https://developer.amd.com/wp-content/resources/56421.pdf

---

These patches are based on commit:
7b6327ff03bb ("OvmfPkg/PlatformPei: increase memory type info defaults")

A version of the tree can be found at:
https://github.com/AMDESE/ovmf/tree/sev-es-v16

Cc: Anthony Perard <anthony.perard@citrix.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Benjamin You <benjamin.you@intel.com>
Cc: Dandan Bi <dandan.bi@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Guo Dong <guo.dong@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Julien Grall <julien.grall@xen.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Ray Ni <ray.ni@intel.com>

Changes since v7:
- Reserve the SEV-ES workarea when S3 is enabled
- Fix warnings issued by the Visual Studio compiler
- Create a NULL VmgExitLib instance that is used for VMGEXIT
  related operations as well as #VC handling. Then create the full
  VmgExitLib support only in OvmfPkg - where it will be used. This
  removes a bunch of implementation code from platforms that will
  not be using the functionality.
- Remove single use interfaces from the VmgExitLib (VmgMmioWrite
  and VmgSetApJumpTable)

Changes since v6:
- Add function comments to all functions, including local functions
- Add function parameter direction to all functions (in/out)
- Add support for MMIO MOVZX/MOVSX instructions
- Ensure the per-CPU variable page remains encrypted
- Coding-style fixes as identified by Ecc

Changes since v5:
- Remove extraneous VmgExitLib usage
- Miscellaneous changes to address feedback (coding style, etc.)

Changes since v4:
- Move the SEV-ES protocol negotiation out of the SEC exception handler
  and into the SecMain.c file. As a result:
  - Move the SecGhcb related PCDs out of UefiCpuPkg and into OvmfPkg
  - Combine SecAMDSevVcHandler.c and PeiDxeAMDSevVcHandler.c into a
    single AMDSevVcHandler.c
- Consolidate VmgExitLib usage into common LibraryClasses sections
- Add documentation comments to the VmgExitLib functions

Changes since v3:
- Remove the need for the MP library finalization routine. The AP
  jump table address will be held by the hypervisor rather than
  communicated via the GHCB MSR. This removes some fragility around
  the UEFI to OS transition.
- Rename the SEV-ES RIP reset area to SEV-ES workarea and use it to
  communicate the SEV-ES status, so that SEC CPU exception handling is
  only established for an SEV-ES guest.
- Fix SMM build breakageAdd around QemuFlashPtrWrite().
- Fix SMM build breakage by adding VC exception support the SMM CPU
  exception handling.
- Add memory fencing around the invocation of AsmVmgExit().
- Clarify comments around the SEV-ES AP reset RIP values and usage.
- Move some PCD definitions from MdeModulePkg to UefiCpuPkg.
- Remove the 16-bit code selector definition from MdeModulePkg

Changes since v2:
- Added a way to locate the SEV-ES fixed AP RIP address for starting
  AP's to avoid updating the actual flash image (build time location
  that is identified with a GUID value).
- Create a VmgExit library to replace static inline functions.
- Move some PCDs to the appropriate packages
- Add support for writing to QEMU flash under SEV-ES
- Add additional MMIO opcode support
- Cleaned up the GHCB MSR CPUID protocol support

Changes since v1:
- Patches reworked to be more specific to the component/area being updated
  and order of definition/usage
- Created a library for VMGEXIT-related functions to replace use of inline
  functions
- Allocation method for GDT changed from AllocatePool to AllocatePages
- Early caching only enabled for SEV-ES guests
- Ensure AP loop mode set to halt loop mode for SEV-ES guests
- Reserved SEC GHCB-related memory areas when S3 is enabled

Tom Lendacky (46):
  MdeModulePkg: Create PCDs to be used in support of SEV-ES
  UefiCpuPkg: Create PCD to be used in support of SEV-ES
  MdePkg: Add the MSR definition for the GHCB register
  MdePkg: Add a structure definition for the GHCB
  MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page tables
  MdePkg/BaseLib: Add support for the XGETBV instruction
  MdePkg/BaseLib: Add support for the VMGEXIT instruction
  UefiCpuPkg: Implement library support for VMGEXIT
  OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library
  UefiPayloadPkg: Prepare UefiPayloadPkg to use the VmgExitLib library
  UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception
  OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF
  OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events
  OvmfPkg/VmgExitLib: Support string IO for IOIO_PROT NAE events
  OvmfPkg/VmgExitLib: Add support for CPUID NAE events
  OvmfPkg/VmgExitLib: Add support for MSR_PROT NAE events
  OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO)
  OvmfPkg/VmgExitLib: Add support for WBINVD NAE events
  OvmfPkg/VmgExitLib: Add support for RDTSC NAE events
  OvmfPkg/VmgExitLib: Add support for RDPMC NAE events
  OvmfPkg/VmgExitLib: Add support for INVD NAE events
  OvmfPkg/VmgExitLib: Add support for VMMCALL NAE events
  OvmfPkg/VmgExitLib: Add support for RDTSCP NAE events
  OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX NAE events
  OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX NAE events
  OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
  OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function
  OvmfPkg: Add support to perform SEV-ES initialization
  OvmfPkg: Create a GHCB page for use during Sec phase
  OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported
  OvmfPkg: Create GHCB pages for use during Pei and Dxe phase
  OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled
  UefiCpuPkg: Create an SEV-ES workarea PCD
  OvmfPkg: Reserve a page in memory for the SEV-ES usage
  OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported
  OvmfPkg/ResetVector: Add support for a 32-bit SEV check
  OvmfPkg/Sec: Add #VC exception handling for Sec phase
  OvmfPkg/Sec: Enable cache early to speed up booting
  OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with
    SEV-ES
  UefiCpuPkg: Add a 16-bit protected mode code segment descriptor
  UefiCpuPkg/MpInitLib: Add CPU MP data flag to indicate if SEV-ES is
    enabled
  UefiCpuPkg: Allow AP booting under SEV-ES
  OvmfPkg: Use the SEV-ES work area for the SEV-ES AP reset vector
  OvmfPkg: Move the GHCB allocations into reserved memory
  UefiCpuPkg/MpInitLib: Prepare SEV-ES guest APs for OS use
  Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files

 MdeModulePkg/MdeModulePkg.dec                 |    9 +
 OvmfPkg/OvmfPkg.dec                           |    9 +
 UefiCpuPkg/UefiCpuPkg.dec                     |   17 +
 OvmfPkg/OvmfPkgIa32.dsc                       |    6 +
 OvmfPkg/OvmfPkgIa32X64.dsc                    |    6 +
 OvmfPkg/OvmfPkgX64.dsc                        |    6 +
 OvmfPkg/OvmfXen.dsc                           |    1 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |    2 +
 UefiPayloadPkg/UefiPayloadPkgIa32.dsc         |    2 +
 UefiPayloadPkg/UefiPayloadPkgIa32X64.dsc      |    2 +
 OvmfPkg/OvmfPkgX64.fdf                        |    9 +
 MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf       |    2 +
 MdePkg/Library/BaseLib/BaseLib.inf            |    4 +
 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |   36 +
 OvmfPkg/PlatformPei/PlatformPei.inf           |    9 +
 .../FvbServicesRuntimeDxe.inf                 |    2 +
 OvmfPkg/ResetVector/ResetVector.inf           |    8 +
 OvmfPkg/Sec/SecMain.inf                       |    4 +
 .../DxeCpuExceptionHandlerLib.inf             |    1 +
 .../PeiCpuExceptionHandlerLib.inf             |    1 +
 .../SecPeiCpuExceptionHandlerLib.inf          |    1 +
 .../SmmCpuExceptionHandlerLib.inf             |    1 +
 .../Xcode5SecPeiCpuExceptionHandlerLib.inf    |    1 +
 UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |    4 +
 UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |    4 +
 .../Library/VmgExitLibNull/VmgExitLibNull.inf |   27 +
 .../Core/DxeIplPeim/X64/VirtualMemory.h       |   12 +-
 MdePkg/Include/Library/BaseLib.h              |   31 +
 MdePkg/Include/Register/Amd/Fam17Msr.h        |   42 +
 MdePkg/Include/Register/Amd/Ghcb.h            |  134 ++
 OvmfPkg/Include/Library/MemEncryptSevLib.h    |   12 +
 .../QemuFlash.h                               |   13 +
 UefiCpuPkg/CpuDxe/CpuGdt.h                    |    4 +-
 UefiCpuPkg/Include/Library/VmgExitLib.h       |  103 +
 UefiCpuPkg/Library/MpInitLib/MpLib.h          |   68 +-
 .../Core/DxeIplPeim/Ia32/DxeLoadFunc.c        |    4 +-
 .../Core/DxeIplPeim/X64/DxeLoadFunc.c         |   11 +-
 .../Core/DxeIplPeim/X64/VirtualMemory.c       |   57 +-
 MdePkg/Library/BaseLib/Ia32/GccInline.c       |   45 +
 MdePkg/Library/BaseLib/X64/GccInline.c        |   47 +
 .../MemEncryptSevLibInternal.c                |   75 +-
 OvmfPkg/Library/VmgExitLib/VmgExitLib.c       |  155 ++
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 1721 +++++++++++++++++
 OvmfPkg/PlatformPei/AmdSev.c                  |   89 +
 OvmfPkg/PlatformPei/MemDetect.c               |   43 +
 .../QemuFlash.c                               |   23 +-
 .../QemuFlashDxe.c                            |   40 +
 .../QemuFlashSmm.c                            |   16 +
 OvmfPkg/Sec/SecMain.c                         |  188 +-
 UefiCpuPkg/CpuDxe/CpuGdt.c                    |    8 +-
 .../CpuExceptionCommon.c                      |   10 +-
 .../PeiDxeSmmCpuException.c                   |   20 +-
 .../SecPeiCpuException.c                      |   19 +
 UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  120 +-
 UefiCpuPkg/Library/MpInitLib/MpLib.c          |  313 ++-
 UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |   19 +
 .../Library/VmgExitLibNull/VmgExitLibNull.c   |  121 ++
 UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |    2 +-
 Maintainers.txt                               |   10 +
 MdeModulePkg/MdeModulePkg.uni                 |    8 +
 MdePkg/Library/BaseLib/Ia32/VmgExit.nasm      |   37 +
 MdePkg/Library/BaseLib/Ia32/XGetBv.nasm       |   31 +
 MdePkg/Library/BaseLib/X64/VmgExit.nasm       |   32 +
 MdePkg/Library/BaseLib/X64/XGetBv.nasm        |   34 +
 OvmfPkg/Library/VmgExitLib/VmgExitLib.uni     |   15 +
 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm  |  100 +
 OvmfPkg/ResetVector/Ia32/PageTables64.asm     |  348 +++-
 OvmfPkg/ResetVector/ResetVector.nasmb         |   20 +
 .../X64/ExceptionHandlerAsm.nasm              |   17 +
 .../X64/Xcode5ExceptionHandlerAsm.nasm        |   17 +
 UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |    2 +-
 .../Library/MpInitLib/Ia32/MpFuncs.nasm       |   15 +
 UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |    4 +-
 UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm |  370 +++-
 .../Library/VmgExitLibNull/VmgExitLibNull.uni |   15 +
 .../ResetVector/Vtf0/Ia16/Real16ToFlat32.asm  |    9 +
 UefiCpuPkg/UefiCpuPkg.uni                     |   11 +
 77 files changed, 4730 insertions(+), 104 deletions(-)
 create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
 create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 create mode 100644 MdePkg/Include/Register/Amd/Ghcb.h
 create mode 100644 UefiCpuPkg/Include/Library/VmgExitLib.h
 create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.c
 create mode 100644 OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
 create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
 create mode 100644 MdePkg/Library/BaseLib/Ia32/VmgExit.nasm
 create mode 100644 MdePkg/Library/BaseLib/Ia32/XGetBv.nasm
 create mode 100644 MdePkg/Library/BaseLib/X64/VmgExit.nasm
 create mode 100644 MdePkg/Library/BaseLib/X64/XGetBv.nasm
 create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
 create mode 100644 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
 create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni

-- 
2.17.1


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

* [PATCH v8 01/46] MdeModulePkg: Create PCDs to be used in support of SEV-ES
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 02/46] UefiCpuPkg: Create PCD " Lendacky, Thomas
                   ` (45 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Jian J Wang,
	Hao A Wu

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

Two new dynamic MdeModulePkg PCDs are needed to support SEV-ES under OVMF:
  - PcdGhcbBase:       UINT64 value that is the base address of the GHCB
                       allocation.
  - PcdGhcbSize:       UINT64 value that is the size, in bytes, of the
                       GHCB allocation (size is dependent on the number of
                       APs).

Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 MdeModulePkg/MdeModulePkg.dec | 9 +++++++++
 MdeModulePkg/MdeModulePkg.uni | 8 ++++++++
 2 files changed, 17 insertions(+)

diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 4f44af694862..8085e761bbeb 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -2051,6 +2051,15 @@ [PcdsDynamic, PcdsDynamicEx]
   # @Prompt If there is any test key used by the platform.
   gEfiMdeModulePkgTokenSpaceGuid.PcdTestKeyUsed|FALSE|BOOLEAN|0x00030003
 
+  ## This dynamic PCD holds the base address of the GHCB pool allocation.
+  # @Prompt GHCB Pool Base Address
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0|UINT64|0x00030007
+
+  ## This dynamic PCD holds the total size of the GHCB pool allocation.
+  #  The amount of memory allocated for GHCBs is dependent on the number of APs.
+  # @Prompt GHCB Pool Size
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0|UINT64|0x00030008
+
 [PcdsDynamicEx]
   ## This dynamic PCD enables the default variable setting.
   #  Its value is the default store ID value. The default value is zero as Standard default.
diff --git a/MdeModulePkg/MdeModulePkg.uni b/MdeModulePkg/MdeModulePkg.uni
index 2007e0596c4f..2f8cca03e527 100644
--- a/MdeModulePkg/MdeModulePkg.uni
+++ b/MdeModulePkg/MdeModulePkg.uni
@@ -1297,3 +1297,11 @@
 #string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdTcgPfpMeasurementRevision_PROMPT #language en-US "TCG Platform Firmware Profile revision"
 
 #string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdTcgPfpMeasurementRevision_HELP #language en-US "Indicates which TCG Platform Firmware Profile revision the EDKII firmware follows."
+
+#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdGhcbBase_PROMPT #language en-US "GHCB Pool Base Address"
+
+#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdGhcbBase_HELP #language en-US "Used with SEV-ES support to identify an address range that is not to be encrypted."
+
+#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdGhcbSize_PROMPT #language en-US "GHCB Pool Base Size"
+
+#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdGhcbSize_HELP #language en-US "Used with SEV-ES support to identify the size of the address range that is not to be encrypted."
-- 
2.17.1


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

* [PATCH v8 02/46] UefiCpuPkg: Create PCD to be used in support of SEV-ES
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 01/46] MdeModulePkg: Create PCDs to be used in support of SEV-ES Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 03/46] MdePkg: Add the MSR definition for the GHCB register Lendacky, Thomas
                   ` (44 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

A new dynamic UefiCpuPkg PCD is needed to support SEV-ES under OVMF:
  - PcdSevEsIsEnabled: BOOLEAN value used to indicate if SEV-ES is enabled

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiCpuPkg/UefiCpuPkg.dec | 6 ++++++
 UefiCpuPkg/UefiCpuPkg.uni | 3 +++
 2 files changed, 9 insertions(+)

diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index 762badf5d239..df5d02bae6b4 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -370,5 +370,11 @@ [PcdsDynamic, PcdsDynamicEx]
   # @ValidRange  0x80000001 | 0 - 1
   gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTraceOutputScheme|0x0|UINT8|0x60000015
 
+  ## This dynamic PCD indicates whether SEV-ES is enabled
+  #   TRUE  - SEV-ES is enabled
+  #   FALSE - SEV-ES is not enabled
+  # @Prompt SEV-ES Status
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|FALSE|BOOLEAN|0x60000016
+
 [UserExtensions.TianoCore."ExtraFiles"]
   UefiCpuPkgExtra.uni
diff --git a/UefiCpuPkg/UefiCpuPkg.uni b/UefiCpuPkg/UefiCpuPkg.uni
index 1780dfdc126d..f4a0c72f6293 100644
--- a/UefiCpuPkg/UefiCpuPkg.uni
+++ b/UefiCpuPkg/UefiCpuPkg.uni
@@ -278,3 +278,6 @@
 
 #string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApStatusCheckIntervalInMicroSeconds_PROMPT  #language en-US "Periodic interval value in microseconds for AP status check in DXE.\n"
 #string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApStatusCheckIntervalInMicroSeconds_HELP    #language en-US "Periodic interval value in microseconds for the status check of APs for StartupAllAPs() and StartupThisAP() executed in non-blocking mode in DXE phase.\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_PROMPT  #language en-US "Specifies whether SEV-ES is enabled"
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_HELP    #language en-US "Set to TRUE when running as an SEV-ES guest, FALSE otherwise."
-- 
2.17.1


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

* [PATCH v8 03/46] MdePkg: Add the MSR definition for the GHCB register
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 01/46] MdeModulePkg: Create PCDs to be used in support of SEV-ES Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 02/46] UefiCpuPkg: Create PCD " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 04/46] MdePkg: Add a structure definition for the GHCB Lendacky, Thomas
                   ` (43 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

For SEV-ES, the GHCB page address is stored in the GHCB MSR register
(0xc0010130). Define the register and the format used for register
during GHCB protocol negotiation.

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 MdePkg/Include/Register/Amd/Fam17Msr.h | 42 ++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/MdePkg/Include/Register/Amd/Fam17Msr.h b/MdePkg/Include/Register/Amd/Fam17Msr.h
index 6ef45a9b21d3..466a3143599c 100644
--- a/MdePkg/Include/Register/Amd/Fam17Msr.h
+++ b/MdePkg/Include/Register/Amd/Fam17Msr.h
@@ -17,6 +17,48 @@
 #ifndef __FAM17_MSR_H__
 #define __FAM17_MSR_H__
 
+/**
+  Secure Encrypted Virtualization - Encrypted State (SEV-ES) GHCB register
+
+**/
+#define MSR_SEV_ES_GHCB                    0xc0010130
+
+/**
+  MSR information returned for #MSR_SEV_ES_GHCB
+**/
+typedef union {
+  struct {
+    UINT64  Function:12;
+  } GhcbInfo;
+
+  struct {
+    UINT8   Reserved[3];
+    UINT8   SevEncryptionBitPos;
+    UINT16  SevEsProtocolMin;
+    UINT16  SevEsProtocolMax;
+  } GhcbProtocol;
+
+  struct {
+    UINT64  Function:12;
+    UINT64  ReasonCodeSet:4;
+    UINT64  ReasonCode:8;
+  } GhcbTerminate;
+
+  VOID    *Ghcb;
+
+  UINT64  GhcbPhysicalAddress;
+} MSR_SEV_ES_GHCB_REGISTER;
+
+#define GHCB_INFO_SEV_INFO                 1
+#define GHCB_INFO_SEV_INFO_GET             2
+#define GHCB_INFO_CPUID_REQUEST            4
+#define GHCB_INFO_CPUID_RESPONSE           5
+#define GHCB_INFO_TERMINATE_REQUEST        256
+
+#define GHCB_TERMINATE_GHCB                0
+#define GHCB_TERMINATE_GHCB_GENERAL        0
+#define GHCB_TERMINATE_GHCB_PROTOCOL       1
+
 /**
   Secure Encrypted Virtualization (SEV) status register
 
-- 
2.17.1


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

* [PATCH v8 04/46] MdePkg: Add a structure definition for the GHCB
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (2 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 03/46] MdePkg: Add the MSR definition for the GHCB register Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 05/46] MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page tables Lendacky, Thomas
                   ` (42 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

The GHCB is used by an SEV-ES guest for communicating between the guest
and the hypervisor. Create the GHCB definition as defined by the GHCB
protocol definition.

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 MdePkg/Include/Register/Amd/Ghcb.h | 134 +++++++++++++++++++++++++++++
 1 file changed, 134 insertions(+)
 create mode 100644 MdePkg/Include/Register/Amd/Ghcb.h

diff --git a/MdePkg/Include/Register/Amd/Ghcb.h b/MdePkg/Include/Register/Amd/Ghcb.h
new file mode 100644
index 000000000000..25c7823238b5
--- /dev/null
+++ b/MdePkg/Include/Register/Amd/Ghcb.h
@@ -0,0 +1,134 @@
+/** @file
+  Guest-Hypervisor Communication Block (GHCB) Definition.
+
+  Provides data types allowing an SEV-ES guest to interact with the hypervisor
+  using the GHCB protocol.
+
+  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Specification Reference:
+  SEV-ES Guest-Hypervisor Communication Block Standardization
+
+**/
+
+#ifndef __GHCB_H__
+#define __GHCB_H__
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+
+#define UD_EXCEPTION  6
+#define GP_EXCEPTION 13
+#define VC_EXCEPTION 29
+
+#define GHCB_VERSION_MIN     1
+#define GHCB_VERSION_MAX     1
+
+#define GHCB_STANDARD_USAGE  0
+
+#define SVM_EXIT_DR7_READ       0x27ULL
+#define SVM_EXIT_DR7_WRITE      0x37ULL
+#define SVM_EXIT_RDTSC          0x6EULL
+#define SVM_EXIT_RDPMC          0x6FULL
+#define SVM_EXIT_CPUID          0x72ULL
+#define SVM_EXIT_INVD           0x76ULL
+#define SVM_EXIT_IOIO_PROT      0x7BULL
+#define SVM_EXIT_MSR            0x7CULL
+#define SVM_EXIT_VMMCALL        0x81ULL
+#define SVM_EXIT_RDTSCP         0x87ULL
+#define SVM_EXIT_WBINVD         0x89ULL
+#define SVM_EXIT_MONITOR        0x8AULL
+#define SVM_EXIT_MWAIT          0x8BULL
+#define SVM_EXIT_NPF            0x400ULL
+
+// VMG Special Exits
+#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
+
+typedef enum {
+  GhcbCpl              = 25,
+  GhcbRflags           = 46,
+  GhcbRip,
+  GhcbRsp              = 59,
+  GhcbRax              = 63,
+  GhcbRcx              = 97,
+  GhcbRdx,
+  GhcbRbx,
+  GhcbRbp              = 101,
+  GhcbRsi,
+  GhcbRdi,
+  GhcbR8,
+  GhcbR9,
+  GhcbR10,
+  GhcbR11,
+  GhcbR12,
+  GhcbR13,
+  GhcbR14,
+  GhcbR15,
+  GhcbXCr0             = 125,
+} GHCB_REGISTER;
+
+typedef PACKED struct {
+  UINT8                  Reserved1[203];
+  UINT8                  Cpl;
+  UINT8                  Reserved2[148];
+  UINT64                 Dr7;
+  UINT8                  Reserved3[144];
+  UINT64                 Rax;
+  UINT8                  Reserved4[264];
+  UINT64                 Rcx;
+  UINT64                 Rdx;
+  UINT64                 Rbx;
+  UINT8                  Reserved5[112];
+  UINT64                 SwExitCode;
+  UINT64                 SwExitInfo1;
+  UINT64                 SwExitInfo2;
+  UINT64                 SwScratch;
+  UINT8                  Reserved6[56];
+  UINT64                 XCr0;
+  UINT8                  ValidBitmap[16];
+  UINT64                 X87StateGpa;
+  UINT8                  Reserved7[1016];
+} GHCB_SAVE_AREA;
+
+typedef PACKED struct {
+  GHCB_SAVE_AREA         SaveArea;
+  UINT8                  SharedBuffer[2032];
+  UINT8                  Reserved1[10];
+  UINT16                 ProtocolVersion;
+  UINT32                 GhcbUsage;
+} GHCB;
+
+typedef union {
+  struct {
+    UINT32  Lower32Bits;
+    UINT32  Upper32Bits;
+  } Elements;
+
+  UINT64    Uint64;
+} GHCB_EXIT_INFO;
+
+typedef union {
+  struct {
+    UINT32  Vector:8;
+    UINT32  Type:3;
+    UINT32  ErrorCodeValid:1;
+    UINT32  Rsvd:19;
+    UINT32  Valid:1;
+    UINT32  ErrorCode;
+  } Elements;
+
+  UINT64    Uint64;
+} GHCB_EVENT_INJECTION;
+
+#define GHCB_EVENT_INJECTION_TYPE_INT        0
+#define GHCB_EVENT_INJECTION_TYPE_NMI        2
+#define GHCB_EVENT_INJECTION_TYPE_EXCEPTION  3
+#define GHCB_EVENT_INJECTION_TYPE_SOFT_INT   4
+
+#endif
-- 
2.17.1


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

* [PATCH v8 05/46] MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page tables
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (3 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 04/46] MdePkg: Add a structure definition for the GHCB Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 06/46] MdePkg/BaseLib: Add support for the XGETBV instruction Lendacky, Thomas
                   ` (41 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Jian J Wang,
	Hao A Wu, Dandan Bi

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

GHCB pages must be mapped as shared pages, so modify the process of
creating identity mapped pagetable entries so that GHCB entries are
created without the encryption bit set. The GHCB range consists of
two pages per CPU, the first being the GHCB and the second being a
per-CPU variable page. Only the GHCB page is mapped as shared.

Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Dandan Bi <dandan.bi@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf       |  2 +
 .../Core/DxeIplPeim/X64/VirtualMemory.h       | 12 +++-
 .../Core/DxeIplPeim/Ia32/DxeLoadFunc.c        |  4 +-
 .../Core/DxeIplPeim/X64/DxeLoadFunc.c         | 11 +++-
 .../Core/DxeIplPeim/X64/VirtualMemory.c       | 57 +++++++++++++++----
 5 files changed, 70 insertions(+), 16 deletions(-)

diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
index 3f1702854660..19b8a4c8aefa 100644
--- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
@@ -115,6 +115,8 @@ [Pcd.IA32,Pcd.X64]
   gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask               ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                       ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdUse5LevelPageTable                  ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                            ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize                            ## CONSUMES
 
 [Pcd.IA32,Pcd.X64,Pcd.ARM,Pcd.AARCH64]
   gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack               ## SOMETIMES_CONSUMES
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
index 2d0493f109e8..6b7c38a441d6 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
@@ -201,6 +201,8 @@ EnableExecuteDisableBit (
   @param[in, out] PageEntry2M           Pointer to 2M page entry.
   @param[in]      StackBase             Stack base address.
   @param[in]      StackSize             Stack size.
+  @param[in]      GhcbBase              GHCB page area base address.
+  @param[in]      GhcbSize              GHCB page area size.
 
 **/
 VOID
@@ -208,7 +210,9 @@ Split2MPageTo4K (
   IN EFI_PHYSICAL_ADDRESS               PhysicalAddress,
   IN OUT UINT64                         *PageEntry2M,
   IN EFI_PHYSICAL_ADDRESS               StackBase,
-  IN UINTN                              StackSize
+  IN UINTN                              StackSize,
+  IN EFI_PHYSICAL_ADDRESS               GhcbBase,
+  IN UINTN                              GhcbSize
   );
 
 /**
@@ -217,6 +221,8 @@ Split2MPageTo4K (
 
   @param[in] StackBase  Stack base address.
   @param[in] StackSize  Stack size.
+  @param[in] GhcbBase   GHCB page area base address.
+  @param[in] GhcbSize   GHCB page area size.
 
   @return The address of 4 level page map.
 
@@ -224,7 +230,9 @@ Split2MPageTo4K (
 UINTN
 CreateIdentityMappingPageTables (
   IN EFI_PHYSICAL_ADDRESS   StackBase,
-  IN UINTN                  StackSize
+  IN UINTN                  StackSize,
+  IN EFI_PHYSICAL_ADDRESS   GhcbBase,
+  IN UINTN                  GhcbkSize
   );
 
 
diff --git a/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c b/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c
index 6e8ca824d469..284b34818ca7 100644
--- a/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c
+++ b/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c
@@ -123,7 +123,7 @@ Create4GPageTablesIa32Pae (
         //
         // Need to split this 2M page that covers stack range.
         //
-        Split2MPageTo4K (PhysicalAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
+        Split2MPageTo4K (PhysicalAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, 0, 0);
       } else {
         //
         // Fill in the Page Directory entries
@@ -282,7 +282,7 @@ HandOffToDxeCore (
     //
     // Create page table and save PageMapLevel4 to CR3
     //
-    PageTables = CreateIdentityMappingPageTables (BaseOfStack, STACK_SIZE);
+    PageTables = CreateIdentityMappingPageTables (BaseOfStack, STACK_SIZE, 0, 0);
 
     //
     // End of PEI phase signal
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c b/MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c
index f465eb1d8ac4..156a477d8467 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c
@@ -35,6 +35,8 @@ HandOffToDxeCore (
   UINT32                          Index;
   EFI_VECTOR_HANDOFF_INFO         *VectorInfo;
   EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi;
+  VOID                            *GhcbBase;
+  UINTN                           GhcbSize;
 
   //
   // Clear page 0 and mark it as allocated if NULL pointer detection is enabled.
@@ -81,12 +83,19 @@ HandOffToDxeCore (
   TopOfStack = (VOID *) ((UINTN) BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT);
   TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT);
 
+  //
+  // Get the address and size of the GHCB pages
+  //
+  GhcbBase = (VOID *) PcdGet64 (PcdGhcbBase);
+  GhcbSize = PcdGet64 (PcdGhcbSize);
+
   PageTables = 0;
   if (FeaturePcdGet (PcdDxeIplBuildPageTables)) {
     //
     // Create page table and save PageMapLevel4 to CR3
     //
-    PageTables = CreateIdentityMappingPageTables ((EFI_PHYSICAL_ADDRESS) (UINTN) BaseOfStack, STACK_SIZE);
+    PageTables = CreateIdentityMappingPageTables ((EFI_PHYSICAL_ADDRESS) (UINTN) BaseOfStack, STACK_SIZE,
+                                                  (EFI_PHYSICAL_ADDRESS) (UINTN) GhcbBase, GhcbSize);
   } else {
     //
     // Set NX for stack feature also require PcdDxeIplBuildPageTables be TRUE
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
index 516cf908bc88..e097508d72af 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
@@ -181,6 +181,8 @@ EnableExecuteDisableBit (
   @param Size         Size of the given physical memory.
   @param StackBase    Base address of stack.
   @param StackSize    Size of stack.
+  @param GhcbBase     Base address of GHCB pages.
+  @param GhcbSize     Size of GHCB area.
 
   @retval TRUE      Page table should be split.
   @retval FALSE     Page table should not be split.
@@ -190,7 +192,9 @@ ToSplitPageTable (
   IN EFI_PHYSICAL_ADDRESS               Address,
   IN UINTN                              Size,
   IN EFI_PHYSICAL_ADDRESS               StackBase,
-  IN UINTN                              StackSize
+  IN UINTN                              StackSize,
+  IN EFI_PHYSICAL_ADDRESS               GhcbBase,
+  IN UINTN                              GhcbSize
   )
 {
   if (IsNullDetectionEnabled () && Address == 0) {
@@ -209,6 +213,12 @@ ToSplitPageTable (
     }
   }
 
+  if (GhcbBase != 0) {
+    if ((Address < GhcbBase + GhcbSize) && ((Address + Size) > GhcbBase)) {
+      return TRUE;
+    }
+  }
+
   return FALSE;
 }
 /**
@@ -322,6 +332,8 @@ AllocatePageTableMemory (
   @param[in, out] PageEntry2M           Pointer to 2M page entry.
   @param[in]      StackBase             Stack base address.
   @param[in]      StackSize             Stack size.
+  @param[in]      GhcbBase              GHCB page area base address.
+  @param[in]      GhcbSize              GHCB page area size.
 
 **/
 VOID
@@ -329,7 +341,9 @@ Split2MPageTo4K (
   IN EFI_PHYSICAL_ADDRESS               PhysicalAddress,
   IN OUT UINT64                         *PageEntry2M,
   IN EFI_PHYSICAL_ADDRESS               StackBase,
-  IN UINTN                              StackSize
+  IN UINTN                              StackSize,
+  IN EFI_PHYSICAL_ADDRESS               GhcbBase,
+  IN UINTN                              GhcbSize
   )
 {
   EFI_PHYSICAL_ADDRESS                  PhysicalAddress4K;
@@ -355,7 +369,20 @@ Split2MPageTo4K (
     //
     // Fill in the Page Table entries
     //
-    PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask;
+    PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K;
+
+    //
+    // The GHCB range consists of two pages per CPU, the GHCB and a
+    // per-CPU variable page. The GHCB page needs to be mapped as an
+    // unencrypted page while the per-CPU variable page needs to be
+    // mapped encrypted. These pages alternate in assignment.
+    //
+    if ((GhcbBase == 0)
+        || (PhysicalAddress4K < GhcbBase)
+        || (PhysicalAddress4K >= GhcbBase + GhcbSize)
+        || ((PhysicalAddress4K - GhcbBase) & 0x1000)) {
+      PageTableEntry->Uint64 |= AddressEncMask;
+    }
     PageTableEntry->Bits.ReadWrite = 1;
 
     if ((IsNullDetectionEnabled () && PhysicalAddress4K == 0) ||
@@ -383,6 +410,8 @@ Split2MPageTo4K (
   @param[in, out] PageEntry1G           Pointer to 1G page entry.
   @param[in]      StackBase             Stack base address.
   @param[in]      StackSize             Stack size.
+  @param[in]      GhcbBase              GHCB page area base address.
+  @param[in]      GhcbSize              GHCB page area size.
 
 **/
 VOID
@@ -390,7 +419,9 @@ Split1GPageTo2M (
   IN EFI_PHYSICAL_ADDRESS               PhysicalAddress,
   IN OUT UINT64                         *PageEntry1G,
   IN EFI_PHYSICAL_ADDRESS               StackBase,
-  IN UINTN                              StackSize
+  IN UINTN                              StackSize,
+  IN EFI_PHYSICAL_ADDRESS               GhcbBase,
+  IN UINTN                              GhcbSize
   )
 {
   EFI_PHYSICAL_ADDRESS                  PhysicalAddress2M;
@@ -413,11 +444,11 @@ Split1GPageTo2M (
 
   PhysicalAddress2M = PhysicalAddress;
   for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) {
-    if (ToSplitPageTable (PhysicalAddress2M, SIZE_2MB, StackBase, StackSize)) {
+    if (ToSplitPageTable (PhysicalAddress2M, SIZE_2MB, StackBase, StackSize, GhcbBase, GhcbSize)) {
       //
       // Need to split this 2M page that covers NULL or stack range.
       //
-      Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
+      Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, GhcbBase, GhcbSize);
     } else {
       //
       // Fill in the Page Directory entries
@@ -616,6 +647,8 @@ EnablePageTableProtection (
 
   @param[in] StackBase  Stack base address.
   @param[in] StackSize  Stack size.
+  @param[in] GhcbBase   GHCB base address.
+  @param[in] GhcbSize   GHCB size.
 
   @return The address of 4 level page map.
 
@@ -623,7 +656,9 @@ EnablePageTableProtection (
 UINTN
 CreateIdentityMappingPageTables (
   IN EFI_PHYSICAL_ADDRESS   StackBase,
-  IN UINTN                  StackSize
+  IN UINTN                  StackSize,
+  IN EFI_PHYSICAL_ADDRESS   GhcbBase,
+  IN UINTN                  GhcbSize
   )
 {
   UINT32                                        RegEax;
@@ -809,8 +844,8 @@ CreateIdentityMappingPageTables (
         PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;
 
         for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
-          if (ToSplitPageTable (PageAddress, SIZE_1GB, StackBase, StackSize)) {
-            Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, StackBase, StackSize);
+          if (ToSplitPageTable (PageAddress, SIZE_1GB, StackBase, StackSize, GhcbBase, GhcbSize)) {
+            Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, StackBase, StackSize, GhcbBase, GhcbSize);
           } else {
             //
             // Fill in the Page Directory entries
@@ -840,11 +875,11 @@ CreateIdentityMappingPageTables (
           PageDirectoryPointerEntry->Bits.Present = 1;
 
           for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
-            if (ToSplitPageTable (PageAddress, SIZE_2MB, StackBase, StackSize)) {
+            if (ToSplitPageTable (PageAddress, SIZE_2MB, StackBase, StackSize, GhcbBase, GhcbSize)) {
               //
               // Need to split this 2M page that covers NULL or stack range.
               //
-              Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
+              Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize, GhcbBase, GhcbSize);
             } else {
               //
               // Fill in the Page Directory entries
-- 
2.17.1


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

* [PATCH v8 06/46] MdePkg/BaseLib: Add support for the XGETBV instruction
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (4 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 05/46] MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page tables Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 07/46] MdePkg/BaseLib: Add support for the VMGEXIT instruction Lendacky, Thomas
                   ` (40 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

Under SEV-ES, a CPUID instruction requires the current value of the XCR0
register. In order to retrieve that value, the XGETBV instruction needs
to be executed.

Provide the necessary support to execute the XGETBV instruction.

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 MdePkg/Library/BaseLib/BaseLib.inf      |  2 ++
 MdePkg/Include/Library/BaseLib.h        | 17 +++++++++++++
 MdePkg/Library/BaseLib/Ia32/GccInline.c | 28 ++++++++++++++++++++
 MdePkg/Library/BaseLib/X64/GccInline.c  | 30 ++++++++++++++++++++++
 MdePkg/Library/BaseLib/Ia32/XGetBv.nasm | 31 ++++++++++++++++++++++
 MdePkg/Library/BaseLib/X64/XGetBv.nasm  | 34 +++++++++++++++++++++++++
 6 files changed, 142 insertions(+)
 create mode 100644 MdePkg/Library/BaseLib/Ia32/XGetBv.nasm
 create mode 100644 MdePkg/Library/BaseLib/X64/XGetBv.nasm

diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index a57ae2da31f3..da6bc22a3e2b 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -153,6 +153,7 @@ [Sources.Ia32]
   Ia32/ARShiftU64.c | MSFT
   Ia32/EnableCache.c | MSFT
   Ia32/DisableCache.c | MSFT
+  Ia32/XGetBv.nasm | MSFT
 
 
   Ia32/GccInline.c | GCC
@@ -287,6 +288,7 @@ [Sources.X64]
   X64/ReadCr2.nasm| MSFT
   X64/ReadCr0.nasm| MSFT
   X64/ReadEflags.nasm| MSFT
+  X64/XGetBv.nasm | MSFT
 
 
   X64/Non-existing.c
diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index b0bbe8cef834..049fd1b872b8 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -7942,6 +7942,23 @@ AsmLfence (
   VOID
   );
 
+/**
+  Executes a XGETBV instruction
+
+  Executes a XGETBV instruction. This function is only available on IA-32 and
+  x64.
+
+  @param[in] Index        Extended control register index
+
+  @retval                 The current value of the extended control register
+**/
+UINT64
+EFIAPI
+AsmXGetBv (
+  IN UINT32  Index
+  );
+
+
 /**
   Patch the immediate operand of an IA32 or X64 instruction such that the byte,
   word, dword or qword operand is encoded at the end of the instruction's
diff --git a/MdePkg/Library/BaseLib/Ia32/GccInline.c b/MdePkg/Library/BaseLib/Ia32/GccInline.c
index 5287200f8754..591f0bb0e097 100644
--- a/MdePkg/Library/BaseLib/Ia32/GccInline.c
+++ b/MdePkg/Library/BaseLib/Ia32/GccInline.c
@@ -1763,3 +1763,31 @@ AsmFlushCacheLine (
 }
 
 
+/**
+  Executes a XGETBV instruction
+
+  Executes a XGETBV instruction. This function is only available on IA-32 and
+  x64.
+
+  @param[in] Index        Extended control register index
+
+  @retval                 The current value of the extended control register
+**/
+UINT64
+EFIAPI
+AsmXGetBv (
+  IN UINT32 Index
+  )
+{
+  UINT64 Data;
+
+  __asm__ __volatile__ (
+    "xgetbv"
+    : "=A" (Data)
+    : "c"  (Index)
+    );
+
+  return Data;
+}
+
+
diff --git a/MdePkg/Library/BaseLib/X64/GccInline.c b/MdePkg/Library/BaseLib/X64/GccInline.c
index 154ce1f57e92..3eed1205adb2 100644
--- a/MdePkg/Library/BaseLib/X64/GccInline.c
+++ b/MdePkg/Library/BaseLib/X64/GccInline.c
@@ -1798,3 +1798,33 @@ AsmFlushCacheLine (
 }
 
 
+/**
+  Executes a XGETBV instruction
+
+  Executes a XGETBV instruction. This function is only available on IA-32 and
+  x64.
+
+  @param[in] Index        Extended control register index
+
+  @retval                 The current value of the extended control register
+**/
+UINT64
+EFIAPI
+AsmXGetBv (
+  IN UINT32 Index
+  )
+{
+  UINT32 LowData;
+  UINT32 HighData;
+
+  __asm__ __volatile__ (
+    "xgetbv"
+    : "=a" (LowData),
+      "=d" (HighData)
+    : "c"  (Index)
+    );
+
+  return (((UINT64)HighData) << 32) | LowData;
+}
+
+
diff --git a/MdePkg/Library/BaseLib/Ia32/XGetBv.nasm b/MdePkg/Library/BaseLib/Ia32/XGetBv.nasm
new file mode 100644
index 000000000000..9f7b03bbff35
--- /dev/null
+++ b/MdePkg/Library/BaseLib/Ia32/XGetBv.nasm
@@ -0,0 +1,31 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;   XGetBv.Asm
+;
+; Abstract:
+;
+;   AsmXgetBv function
+;
+; Notes:
+;
+;------------------------------------------------------------------------------
+
+    SECTION .text
+
+;------------------------------------------------------------------------------
+; UINT64
+; EFIAPI
+; AsmXGetBv (
+;   IN UINT32  Index
+;   );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmXGetBv)
+ASM_PFX(AsmXGetBv):
+    mov     ecx, [esp + 4]
+    xgetbv
+    ret
diff --git a/MdePkg/Library/BaseLib/X64/XGetBv.nasm b/MdePkg/Library/BaseLib/X64/XGetBv.nasm
new file mode 100644
index 000000000000..09f3be8ae0a8
--- /dev/null
+++ b/MdePkg/Library/BaseLib/X64/XGetBv.nasm
@@ -0,0 +1,34 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;   XGetBv.Asm
+;
+; Abstract:
+;
+;   AsmXgetBv function
+;
+; Notes:
+;
+;------------------------------------------------------------------------------
+
+    DEFAULT REL
+    SECTION .text
+
+;------------------------------------------------------------------------------
+; UINT64
+; EFIAPI
+; AsmXGetBv (
+;   IN UINT32  Index
+;   );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmXGetBv)
+ASM_PFX(AsmXGetBv):
+    xgetbv
+    shl     rdx, 32
+    or      rax, rdx
+    ret
+
-- 
2.17.1


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

* [PATCH v8 07/46] MdePkg/BaseLib: Add support for the VMGEXIT instruction
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (5 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 06/46] MdePkg/BaseLib: Add support for the XGETBV instruction Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 08/46] UefiCpuPkg: Implement library support for VMGEXIT Lendacky, Thomas
                   ` (39 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

VMGEXIT is a new instruction used for Hypervisor/Guest communication when
running as an SEV-ES guest. A VMGEXIT will cause an automatic exit (AE)
to occur, resulting in a #VMEXIT with an exit code value of 0x403.

Provide the necessary support to execute the VMGEXIT instruction, which
is "rep; vmmcall".

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 MdePkg/Library/BaseLib/BaseLib.inf       |  2 ++
 MdePkg/Include/Library/BaseLib.h         | 14 +++++++++
 MdePkg/Library/BaseLib/Ia32/GccInline.c  | 17 +++++++++++
 MdePkg/Library/BaseLib/X64/GccInline.c   | 17 +++++++++++
 MdePkg/Library/BaseLib/Ia32/VmgExit.nasm | 37 ++++++++++++++++++++++++
 MdePkg/Library/BaseLib/X64/VmgExit.nasm  | 32 ++++++++++++++++++++
 6 files changed, 119 insertions(+)
 create mode 100644 MdePkg/Library/BaseLib/Ia32/VmgExit.nasm
 create mode 100644 MdePkg/Library/BaseLib/X64/VmgExit.nasm

diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index da6bc22a3e2b..7cf6467fa77b 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -154,6 +154,7 @@ [Sources.Ia32]
   Ia32/EnableCache.c | MSFT
   Ia32/DisableCache.c | MSFT
   Ia32/XGetBv.nasm | MSFT
+  Ia32/VmgExit.nasm | MSFT
 
 
   Ia32/GccInline.c | GCC
@@ -289,6 +290,7 @@ [Sources.X64]
   X64/ReadCr0.nasm| MSFT
   X64/ReadEflags.nasm| MSFT
   X64/XGetBv.nasm | MSFT
+  X64/VmgExit.nasm | MSFT
 
 
   X64/Non-existing.c
diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index 049fd1b872b8..2d131b2ac8a1 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -7959,6 +7959,20 @@ AsmXGetBv (
   );
 
 
+/**
+  Executes a VMGEXIT instruction (VMMCALL with a REP prefix)
+
+  Executes a VMGEXIT instruction. This function is only available on IA-32 and
+  x64.
+
+**/
+VOID
+EFIAPI
+AsmVmgExit (
+  VOID
+  );
+
+
 /**
   Patch the immediate operand of an IA32 or X64 instruction such that the byte,
   word, dword or qword operand is encoded at the end of the instruction's
diff --git a/MdePkg/Library/BaseLib/Ia32/GccInline.c b/MdePkg/Library/BaseLib/Ia32/GccInline.c
index 591f0bb0e097..ee8c62c79c93 100644
--- a/MdePkg/Library/BaseLib/Ia32/GccInline.c
+++ b/MdePkg/Library/BaseLib/Ia32/GccInline.c
@@ -1791,3 +1791,20 @@ AsmXGetBv (
 }
 
 
+/**
+  Executes a VMGEXIT instruction.
+
+  Executes a VMGEXIT instruction. This function is only available on IA-32 and
+  X64.
+
+**/
+VOID
+EFIAPI
+AsmVmgExit (
+  VOID
+  )
+{
+  __asm__ __volatile__ ("rep; vmmcall":::"memory");
+}
+
+
diff --git a/MdePkg/Library/BaseLib/X64/GccInline.c b/MdePkg/Library/BaseLib/X64/GccInline.c
index 3eed1205adb2..277974eff9ee 100644
--- a/MdePkg/Library/BaseLib/X64/GccInline.c
+++ b/MdePkg/Library/BaseLib/X64/GccInline.c
@@ -1828,3 +1828,20 @@ AsmXGetBv (
 }
 
 
+/**
+  Executes a VMGEXIT instruction.
+
+  Executes a VMGEXIT instruction. This function is only available on IA-32 and
+  X64.
+
+**/
+VOID
+EFIAPI
+AsmVmgExit (
+  VOID
+  )
+{
+  __asm__ __volatile__ ("rep; vmmcall":::"memory");
+}
+
+
diff --git a/MdePkg/Library/BaseLib/Ia32/VmgExit.nasm b/MdePkg/Library/BaseLib/Ia32/VmgExit.nasm
new file mode 100644
index 000000000000..a4b37385cc7a
--- /dev/null
+++ b/MdePkg/Library/BaseLib/Ia32/VmgExit.nasm
@@ -0,0 +1,37 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;   VmgExit.Asm
+;
+; Abstract:
+;
+;   AsmVmgExit function
+;
+; Notes:
+;
+;------------------------------------------------------------------------------
+
+    SECTION .text
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmVmgExit (
+;   VOID
+;   );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmVmgExit)
+ASM_PFX(AsmVmgExit):
+;
+; 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
+    ret
+
diff --git a/MdePkg/Library/BaseLib/X64/VmgExit.nasm b/MdePkg/Library/BaseLib/X64/VmgExit.nasm
new file mode 100644
index 000000000000..26f034593c67
--- /dev/null
+++ b/MdePkg/Library/BaseLib/X64/VmgExit.nasm
@@ -0,0 +1,32 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;   VmgExit.Asm
+;
+; Abstract:
+;
+;   AsmVmgExit function
+;
+; Notes:
+;
+;------------------------------------------------------------------------------
+
+    DEFAULT REL
+    SECTION .text
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; AsmVmgExit (
+;   VOID
+;   );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmVmgExit)
+ASM_PFX(AsmVmgExit):
+    rep     vmmcall
+    ret
+
-- 
2.17.1


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

* [PATCH v8 08/46] UefiCpuPkg: Implement library support for VMGEXIT
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (6 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 07/46] MdePkg/BaseLib: Add support for the VMGEXIT instruction Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 09/46] OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library Lendacky, Thomas
                   ` (38 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

To support handling #VC exceptions and issuing VMGEXIT instructions,
create a library with functions that can be used to perform these
#VC/VMGEXIT related operations. This includes functions for:
  - Handling #VC exceptions
  - Preparing for and issuing a VMGEXIT
  - Performing MMIO-related write operations to support flash emulation
  - Performing AP related boot opeations

The base functions in this driver will not do anything and will return
an error if a return value is required. It is expected that other packages
(like OvmfPkg) will create a version of the library to fully support an
SEV-ES guest.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiCpuPkg/UefiCpuPkg.dec                     |   3 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |   2 +
 .../Library/VmgExitLibNull/VmgExitLibNull.inf |  27 ++++
 UefiCpuPkg/Include/Library/VmgExitLib.h       | 103 +++++++++++++++
 .../Library/VmgExitLibNull/VmgExitLibNull.c   | 121 ++++++++++++++++++
 .../Library/VmgExitLibNull/VmgExitLibNull.uni |  15 +++
 6 files changed, 271 insertions(+)
 create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 create mode 100644 UefiCpuPkg/Include/Library/VmgExitLib.h
 create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
 create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni

diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index df5d02bae6b4..cb92f34b6f55 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -53,6 +53,9 @@ [LibraryClasses.IA32, LibraryClasses.X64]
   ##
   MpInitLib|Include/Library/MpInitLib.h
 
+  ##  @libraryclass  Provides function to support VMGEXIT processing.
+  VmgExitLib|Include/Library/VmgExitLib.h
+
 [Guids]
   gUefiCpuPkgTokenSpaceGuid      = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }}
   gMsegSmramGuid                 = { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }}
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index afa304128221..f0e58b90ff0a 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -56,6 +56,7 @@ [LibraryClasses]
   PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
   PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
   TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.SEC]
   PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf
@@ -143,6 +144,7 @@ [Components.IA32, Components.X64]
   UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.inf
   UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf
   UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibStm.inf
+  UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
   UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.inf
   UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.inf
   UefiCpuPkg/SecCore/SecCore.inf
diff --git a/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
new file mode 100644
index 000000000000..d8770a21c355
--- /dev/null
+++ b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
@@ -0,0 +1,27 @@
+## @file
+#  VMGEXIT Support Library.
+#
+#  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = VmgExitLibNull
+  MODULE_UNI_FILE                = VmgExitLibNull.uni
+  FILE_GUID                      = 3cd7368f-ef9b-4a9b-9571-2ed93813677e
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = VmgExitLib
+
+[Sources.common]
+  VmgExitLibNull.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+
diff --git a/UefiCpuPkg/Include/Library/VmgExitLib.h b/UefiCpuPkg/Include/Library/VmgExitLib.h
new file mode 100644
index 000000000000..e7282a7e225d
--- /dev/null
+++ b/UefiCpuPkg/Include/Library/VmgExitLib.h
@@ -0,0 +1,103 @@
+/** @file
+  Public header file for the VMGEXIT Support library class.
+
+  This library class defines some routines used when invoking the VMGEXIT
+  instruction in support of SEV-ES and to handle #VC exceptions.
+
+  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __VMG_EXIT_LIB_H__
+#define __VMG_EXIT_LIB_H__
+
+#include <Protocol/DebugSupport.h>
+#include <Register/Amd/Ghcb.h>
+
+
+/**
+  Perform VMGEXIT.
+
+  Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and
+  then handles the return actions.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+  @param[in]       ExitCode   VMGEXIT code to be assigned to the SwExitCode
+                              field of the GHCB.
+  @param[in]       ExitInfo1  VMGEXIT information to be assigned to the
+                              SwExitInfo1 field of the GHCB.
+  @param[in]       ExitInfo2  VMGEXIT information to be assigned to the
+                              SwExitInfo2 field of the GHCB.
+
+  @retval  0                  VMGEXIT succeeded.
+  @retval  Others             VMGEXIT processing did not succeed. Exception
+                              number to be propagated.
+
+**/
+UINT64
+EFIAPI
+VmgExit (
+  IN OUT GHCB                *Ghcb,
+  IN     UINT64              ExitCode,
+  IN     UINT64              ExitInfo1,
+  IN     UINT64              ExitInfo2
+  );
+
+/**
+  Perform pre-VMGEXIT initialization/preparation.
+
+  Performs the necessary steps in preparation for invoking VMGEXIT. Must be
+  called before setting any fields within the GHCB.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgInit (
+  IN OUT GHCB                *Ghcb
+  );
+
+/**
+  Perform post-VMGEXIT cleanup.
+
+  Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
+  called after obtaining needed fields within the GHCB.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgDone (
+  IN OUT GHCB                *Ghcb
+  );
+
+/**
+  Handle a #VC exception.
+
+  Performs the necessary processing to handle a #VC exception.
+
+  The base library function returns an error equal to VC_EXCEPTION,
+  to be propagated to the standard exception handling stack.
+
+  @param[in, out]  ExceptionType  Pointer to an EFI_EXCEPTION_TYPE to be set
+                                  as value to use on error.
+  @param[in, out]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT
+
+  @retval  EFI_SUCCESS            Exception handled
+  @retval  EFI_UNSUPPORTED        #VC not supported, (new) exception value to
+                                  propagate provided
+  @retval  EFI_PROTOCOL_ERROR     #VC handling failed, (new) exception value to
+                                  propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+VmgExitHandleVc (
+  IN OUT EFI_EXCEPTION_TYPE  *ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+#endif
diff --git a/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
new file mode 100644
index 000000000000..da680ced5b09
--- /dev/null
+++ b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
@@ -0,0 +1,121 @@
+/** @file
+  VMGEXIT Base Support Library.
+
+  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <Library/VmgExitLib.h>
+
+/**
+  Perform VMGEXIT.
+
+  Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and
+  then handles the return actions.
+
+  The base library function returns an error in the form of a
+  GHCB_EVENT_INJECTION representing a GP_EXCEPTION.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+  @param[in]       ExitCode   VMGEXIT code to be assigned to the SwExitCode
+                              field of the GHCB.
+  @param[in]       ExitInfo1  VMGEXIT information to be assigned to the
+                              SwExitInfo1 field of the GHCB.
+  @param[in]       ExitInfo2  VMGEXIT information to be assigned to the
+                              SwExitInfo2 field of the GHCB.
+
+  @retval  0                  VMGEXIT succeeded.
+  @retval  Others             VMGEXIT processing did not succeed. Exception
+                              event to be propagated.
+
+**/
+UINT64
+EFIAPI
+VmgExit (
+  IN OUT GHCB                *Ghcb,
+  IN     UINT64              ExitCode,
+  IN     UINT64              ExitInfo1,
+  IN     UINT64              ExitInfo2
+  )
+{
+  GHCB_EVENT_INJECTION  Event;
+
+  Event.Uint64 = 0;
+  Event.Elements.Vector = GP_EXCEPTION;
+  Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+  Event.Elements.Valid  = 1;
+
+  return Event.Uint64;
+}
+
+/**
+  Perform pre-VMGEXIT initialization/preparation.
+
+  Performs the necessary steps in preparation for invoking VMGEXIT. Must be
+  called before setting any fields within the GHCB.
+
+  The base library function does nothing.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgInit (
+  IN OUT GHCB                *Ghcb
+  )
+{
+}
+
+/**
+  Perform post-VMGEXIT cleanup.
+
+  Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
+  called after obtaining needed fields within the GHCB.
+
+  The base library function does nothing.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgDone (
+  IN OUT GHCB                *Ghcb
+  )
+{
+}
+
+/**
+  Handle a #VC exception.
+
+  Performs the necessary processing to handle a #VC exception.
+
+  The base library function returns an error equal to VC_EXCEPTION,
+  to be propagated to the standard exception handling stack.
+
+  @param[in, out]  ExceptionType  Pointer to an EFI_EXCEPTION_TYPE to be set
+                                  as value to use on error.
+  @param[in, out]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT
+
+  @retval  EFI_SUCCESS            Exception handled
+  @retval  EFI_UNSUPPORTED        #VC not supported, (new) exception value to
+                                  propagate provided
+  @retval  EFI_PROTOCOL_ERROR     #VC handling failed, (new) exception value to
+                                  propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+VmgExitHandleVc (
+  IN OUT EFI_EXCEPTION_TYPE  *ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  *ExceptionType = VC_EXCEPTION;
+
+  return EFI_UNSUPPORTED;
+}
diff --git a/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni
new file mode 100644
index 000000000000..8639bc0e8ce9
--- /dev/null
+++ b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni
@@ -0,0 +1,15 @@
+// /** @file
+// VMGEXIT support library instance.
+//
+// VMGEXIT support library instance.
+//
+// Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT             #language en-US "VMGEXIT support NULL library instance"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "VMGEXIT support NULL library instance."
+
-- 
2.17.1


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

* [PATCH v8 09/46] OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (7 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 08/46] UefiCpuPkg: Implement library support for VMGEXIT Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-21 16:42   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 10/46] UefiPayloadPkg: Prepare UefiPayloadPkg " Lendacky, Thomas
                   ` (37 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Anthony Perard,
	Julien Grall

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

Various CpuExceptionHandlerLib libraries will updated to use the new
VmgExitLib library. To prevent any build breakage, update the OvmfPkg
DSC files that use a form of the CpuExceptionHandlerLib library to
include the VmgExitLib library.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Anthony Perard <anthony.perard@citrix.com>
Cc: Julien Grall <julien@xen.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/OvmfPkgIa32.dsc    | 1 +
 OvmfPkg/OvmfPkgIa32X64.dsc | 1 +
 OvmfPkg/OvmfPkgX64.dsc     | 1 +
 OvmfPkg/OvmfXen.dsc        | 1 +
 4 files changed, 4 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index d0df9cbbfb2b..7d7297aa1cd8 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -228,6 +228,7 @@ [LibraryClasses]
 
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index b3ae62fee92b..c85e988372ec 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -232,6 +232,7 @@ [LibraryClasses]
 
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index f7fe75ebf531..0b9189ab1e38 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -232,6 +232,7 @@ [LibraryClasses]
 
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/OvmfXen.dsc b/OvmfPkg/OvmfXen.dsc
index 3af0ee705407..a3ba96f7002a 100644
--- a/OvmfPkg/OvmfXen.dsc
+++ b/OvmfPkg/OvmfXen.dsc
@@ -213,6 +213,7 @@ [LibraryClasses]
 
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.SEC]
   QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSecLib.inf
-- 
2.17.1


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

* [PATCH v8 10/46] UefiPayloadPkg: Prepare UefiPayloadPkg to use the VmgExitLib library
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (8 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 09/46] OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 11/46] UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception Lendacky, Thomas
                   ` (36 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Maurice Ma,
	Guo Dong, Benjamin You

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

Various CpuExceptionHandlerLib libraries will updated to use the new
VmgExitLib library. To prevent any build breakage, update the
UefiPayloadPkg DSC files that use a form of the CpuExceptionHandlerLib
library to include the VmgExitLib library.

Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Guo Dong <guo.dong@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Reviewed-by: Guo Dong <guo.dong@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiPayloadPkg/UefiPayloadPkgIa32.dsc    | 2 ++
 UefiPayloadPkg/UefiPayloadPkgIa32X64.dsc | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/UefiPayloadPkg/UefiPayloadPkgIa32.dsc b/UefiPayloadPkg/UefiPayloadPkgIa32.dsc
index d52945442e0e..810de274652c 100644
--- a/UefiPayloadPkg/UefiPayloadPkgIa32.dsc
+++ b/UefiPayloadPkg/UefiPayloadPkgIa32.dsc
@@ -233,6 +233,7 @@ [LibraryClasses.common.DXE_CORE]
   DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf
 !endif
   CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.DXE_DRIVER]
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
@@ -245,6 +246,7 @@ [LibraryClasses.common.DXE_DRIVER]
 !endif
   CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.DXE_RUNTIME_DRIVER]
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
diff --git a/UefiPayloadPkg/UefiPayloadPkgIa32X64.dsc b/UefiPayloadPkg/UefiPayloadPkgIa32X64.dsc
index 0736cd995476..adb754b28baa 100644
--- a/UefiPayloadPkg/UefiPayloadPkgIa32X64.dsc
+++ b/UefiPayloadPkg/UefiPayloadPkgIa32X64.dsc
@@ -234,6 +234,7 @@ [LibraryClasses.common.DXE_CORE]
   DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf
 !endif
   CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.DXE_DRIVER]
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
@@ -246,6 +247,7 @@ [LibraryClasses.common.DXE_DRIVER]
 !endif
   CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
 
 [LibraryClasses.common.DXE_RUNTIME_DRIVER]
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
-- 
2.17.1


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

* [PATCH v8 11/46] UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (9 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 10/46] UefiPayloadPkg: Prepare UefiPayloadPkg " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 12/46] OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF Lendacky, Thomas
                   ` (35 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

Add base support to handle #VC exceptions. Update the common exception
handlers to invoke the VmgExitHandleVc () function of the VmgExitLib
library when a #VC is encountered. A non-zero return code will propagate
to the targeted exception handler.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../DxeCpuExceptionHandlerLib.inf             |  1 +
 .../PeiCpuExceptionHandlerLib.inf             |  1 +
 .../SecPeiCpuExceptionHandlerLib.inf          |  1 +
 .../SmmCpuExceptionHandlerLib.inf             |  1 +
 .../Xcode5SecPeiCpuExceptionHandlerLib.inf    |  1 +
 .../CpuExceptionCommon.c                      | 10 +++++++++-
 .../PeiDxeSmmCpuException.c                   | 20 ++++++++++++++++++-
 .../SecPeiCpuException.c                      | 19 ++++++++++++++++++
 8 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
index 61e2ec30b089..07b34c92a892 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
@@ -57,3 +57,4 @@ [LibraryClasses]
   PeCoffGetEntryPointLib
   MemoryAllocationLib
   DebugLib
+  VmgExitLib
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf
index 093374944df6..feae7b3e06de 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf
@@ -52,6 +52,7 @@ [LibraryClasses]
   HobLib
   MemoryAllocationLib
   SynchronizationLib
+  VmgExitLib
 
 [Pcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard    # CONSUMES
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
index 6d25cafe2ca3..967cb61ba6d9 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
@@ -48,3 +48,4 @@ [LibraryClasses]
   PrintLib
   LocalApicLib
   PeCoffGetEntryPointLib
+  VmgExitLib
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf
index 2ffbbccc302f..4cdb11c04ea0 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf
@@ -51,4 +51,5 @@ [LibraryClasses]
   LocalApicLib
   PeCoffGetEntryPointLib
   DebugLib
+  VmgExitLib
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf
index 7e21beaab6f2..743c2aa76684 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf
@@ -53,3 +53,4 @@ [LibraryClasses]
   PrintLib
   LocalApicLib
   PeCoffGetEntryPointLib
+  VmgExitLib
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index 8adbd43fefb4..c9003b10e552 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -14,7 +14,7 @@
 //
 // 1 means an error code will be pushed, otherwise 0
 //
-CONST UINT32 mErrorCodeFlag = 0x00227d00;
+CONST UINT32 mErrorCodeFlag = 0x20227d00;
 
 //
 // Define the maximum message length
@@ -45,6 +45,14 @@ CONST CHAR8 *mExceptionNameStr[] = {
   "#XM - SIMD floating-point",
   "#VE - Virtualization",
   "#CP - Control Protection"
+  "Reserved",
+  "Reserved",
+  "Reserved",
+  "Reserved",
+  "Reserved",
+  "Reserved",
+  "Reserved",
+  "#VC - VMM Communication",
 };
 
 #define EXCEPTION_KNOWN_NAME_NUM  (sizeof (mExceptionNameStr) / sizeof (CHAR8 *))
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
index 6a2670d55918..892d349d4b37 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c
@@ -6,8 +6,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
 
-#include "CpuExceptionCommon.h"
 #include <Library/DebugLib.h>
+#include <Library/VmgExitLib.h>
+#include "CpuExceptionCommon.h"
 
 /**
   Internal worker function for common exception handler.
@@ -27,6 +28,23 @@ CommonExceptionHandlerWorker (
   RESERVED_VECTORS_DATA          *ReservedVectors;
   EFI_CPU_INTERRUPT_HANDLER      *ExternalInterruptHandler;
 
+  if (ExceptionType == VC_EXCEPTION) {
+    EFI_STATUS  Status;
+    //
+    // #VC needs to be handled immediately upon enabling exception handling
+    // and therefore can't use the RegisterCpuInterruptHandler() interface.
+    //
+    // Handle the #VC:
+    //   On EFI_SUCCESS - Exception has been handled, return
+    //   On other       - ExceptionType contains (possibly new) exception
+    //                    value
+    //
+    Status = VmgExitHandleVc (&ExceptionType, SystemContext);
+    if (!EFI_ERROR (Status)) {
+      return;
+    }
+  }
+
   ExceptionHandlerContext  = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);
   ReservedVectors          = ExceptionHandlerData->ReservedVectors;
   ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c
index 20148db74cf8..52c6886f0372 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c
@@ -7,6 +7,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #include <PiPei.h>
+#include <Library/VmgExitLib.h>
 #include "CpuExceptionCommon.h"
 
 CONST UINTN    mDoFarReturnFlag  = 0;
@@ -24,6 +25,24 @@ CommonExceptionHandler (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  if (ExceptionType == VC_EXCEPTION) {
+    EFI_STATUS  Status;
+    //
+    // #VC needs to be handled immediately upon enabling exception handling
+    // and therefore can't use the RegisterCpuInterruptHandler() interface
+    // (which isn't supported under Sec and Pei anyway).
+    //
+    // Handle the #VC:
+    //   On EFI_SUCCESS - Exception has been handled, return
+    //   On other       - ExceptionType contains (possibly new) exception
+    //                    value
+    //
+    Status = VmgExitHandleVc (&ExceptionType, SystemContext);
+    if (!EFI_ERROR (Status)) {
+      return;
+    }
+  }
+
   //
   // Initialize the serial port before dumping.
   //
-- 
2.17.1


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

* [PATCH v8 12/46] OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (10 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 11/46] UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-21 16:52   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events Lendacky, Thomas
                   ` (34 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

The base VmgExitLib library provides a default limited interface. As it
does not provide full support, create an OVMF version of this library to
begin the process of providing full support of SEV-ES within OVMF.

SEV-ES support is only provided for X64 builds, so only OvmfPkgX64.dsc is
updated to make use of the OvmfPkg version of the library.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/OvmfPkgX64.dsc                        |   2 +-
 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |  36 ++++
 OvmfPkg/Library/VmgExitLib/VmgExitLib.c       | 155 ++++++++++++++++++
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c |  81 +++++++++
 OvmfPkg/Library/VmgExitLib/VmgExitLib.uni     |  15 ++
 5 files changed, 288 insertions(+), 1 deletion(-)
 create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
 create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.c
 create mode 100644 OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
 create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.uni

diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 0b9189ab1e38..b5f3859420d0 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -232,7 +232,7 @@ [LibraryClasses]
 
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
-  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+  VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
new file mode 100644
index 000000000000..0e6bc8432314
--- /dev/null
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
@@ -0,0 +1,36 @@
+## @file
+#  VMGEXIT Support Library.
+#
+#  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = VmgExitLib
+  MODULE_UNI_FILE                = VmgExitLib.uni
+  FILE_GUID                      = 0e923c25-13cd-430b-8714-ffe85652a97b
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = VmgExitLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources.X64]
+  X64/VmgExitVcHandler.c
+
+[Sources.common]
+  VmgExitLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.c b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
new file mode 100644
index 000000000000..7b7ebea85256
--- /dev/null
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
@@ -0,0 +1,155 @@
+/** @file
+  VMGEXIT Support Library.
+
+  Copyright (C) 2020, 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 <Register/Amd/Msr.h>
+
+/**
+  Check for VMGEXIT error
+
+  Check if the hypervisor has returned an error after completion of the VMGEXIT
+  by examining the SwExitInfo1 field of the GHCB.
+
+  @param[in]  Ghcb       A pointer to the GHCB
+
+  @retval  0             VMGEXIT succeeded.
+  @retval  Others        VMGEXIT processing did not succeed. Exception number to
+                         be propagated.
+
+**/
+STATIC
+UINT64
+VmgExitErrorCheck (
+  IN GHCB                *Ghcb
+  )
+{
+  GHCB_EVENT_INJECTION  Event;
+  GHCB_EXIT_INFO        ExitInfo;
+  UINT64                Status;
+
+  ExitInfo.Uint64 = Ghcb->SaveArea.SwExitInfo1;
+  ASSERT ((ExitInfo.Elements.Lower32Bits == 0) ||
+          (ExitInfo.Elements.Lower32Bits == 1));
+
+  Status = 0;
+  if (ExitInfo.Elements.Lower32Bits == 0) {
+    return Status;
+  }
+
+  if (ExitInfo.Elements.Lower32Bits == 1) {
+    ASSERT (Ghcb->SaveArea.SwExitInfo2 != 0);
+
+    // Check that the return event is valid
+    Event.Uint64 = Ghcb->SaveArea.SwExitInfo2;
+    if (Event.Elements.Valid &&
+        Event.Elements.Type == GHCB_EVENT_INJECTION_TYPE_EXCEPTION) {
+      switch (Event.Elements.Vector) {
+      case GP_EXCEPTION:
+      case UD_EXCEPTION:
+        // Use returned event as return code
+        Status = Event.Uint64;
+      }
+    }
+  }
+
+  if (Status == 0) {
+    GHCB_EVENT_INJECTION  GpEvent;
+
+    GpEvent.Uint64 = 0;
+    GpEvent.Elements.Vector = GP_EXCEPTION;
+    GpEvent.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+    GpEvent.Elements.Valid  = 1;
+
+    Status = GpEvent.Uint64;
+  }
+
+  return Status;
+}
+
+/**
+  Perform VMGEXIT.
+
+  Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and
+  then handles the return actions.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+  @param[in]       ExitCode   VMGEXIT code to be assigned to the SwExitCode
+                              field of the GHCB.
+  @param[in]       ExitInfo1  VMGEXIT information to be assigned to the
+                              SwExitInfo1 field of the GHCB.
+  @param[in]       ExitInfo2  VMGEXIT information to be assigned to the
+                              SwExitInfo2 field of the GHCB.
+
+  @retval  0                  VMGEXIT succeeded.
+  @retval  Others             VMGEXIT processing did not succeed. Exception
+                              event to be propagated.
+
+**/
+UINT64
+EFIAPI
+VmgExit (
+  IN OUT GHCB                *Ghcb,
+  IN     UINT64              ExitCode,
+  IN     UINT64              ExitInfo1,
+  IN     UINT64              ExitInfo2
+  )
+{
+  Ghcb->SaveArea.SwExitCode = ExitCode;
+  Ghcb->SaveArea.SwExitInfo1 = ExitInfo1;
+  Ghcb->SaveArea.SwExitInfo2 = ExitInfo2;
+
+  //
+  // Guest memory is used for the guest-hypervisor communication, so fence
+  // the invocation of the VMGEXIT instruction to ensure GHCB accesses are
+  // synchronized properly.
+  //
+  MemoryFence ();
+  AsmVmgExit ();
+  MemoryFence ();
+
+  return VmgExitErrorCheck (Ghcb);
+}
+
+/**
+  Perform pre-VMGEXIT initialization/preparation.
+
+  Performs the necessary steps in preparation for invoking VMGEXIT. Must be
+  called before setting any fields within the GHCB.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgInit (
+  IN OUT GHCB                *Ghcb
+  )
+{
+  SetMem (&Ghcb->SaveArea, sizeof (Ghcb->SaveArea), 0);
+}
+
+/**
+  Perform post-VMGEXIT cleanup.
+
+  Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
+  called after obtaining needed fields within the GHCB.
+
+  @param[in, out]  Ghcb       A pointer to the GHCB
+
+**/
+VOID
+EFIAPI
+VmgDone (
+  IN OUT GHCB                *Ghcb
+  )
+{
+}
+
diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
new file mode 100644
index 000000000000..036f030d6b34
--- /dev/null
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -0,0 +1,81 @@
+/** @file
+  X64 #VC Exception Handler functon.
+
+  Copyright (C) 2020, 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 <Register/Amd/Msr.h>
+
+/**
+  Handle a #VC exception.
+
+  Performs the necessary processing to handle a #VC exception.
+
+  @param[in, out]  ExceptionType  Pointer to an EFI_EXCEPTION_TYPE to be set
+                                  as value to use on error.
+  @param[in, out]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT
+
+  @retval  EFI_SUCCESS            Exception handled
+  @retval  EFI_UNSUPPORTED        #VC not supported, (new) exception value to
+                                  propagate provided
+  @retval  EFI_PROTOCOL_ERROR     #VC handling failed, (new) exception value to
+                                  propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+VmgExitHandleVc (
+  IN OUT EFI_EXCEPTION_TYPE  *ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  MSR_SEV_ES_GHCB_REGISTER  Msr;
+  EFI_SYSTEM_CONTEXT_X64    *Regs;
+  GHCB                      *Ghcb;
+  UINT64                    ExitCode, Status;
+  EFI_STATUS                VcRet;
+
+  VcRet = EFI_SUCCESS;
+
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+  ASSERT (Msr.GhcbInfo.Function == 0);
+  ASSERT (Msr.Ghcb != 0);
+
+  Regs = SystemContext.SystemContextX64;
+  Ghcb = Msr.Ghcb;
+
+  VmgInit (Ghcb);
+
+  ExitCode = Regs->ExceptionData;
+  switch (ExitCode) {
+  default:
+    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
+    if (Status == 0) {
+      Regs->ExceptionData = 0;
+      *ExceptionType = GP_EXCEPTION;
+    } else {
+      GHCB_EVENT_INJECTION  Event;
+
+      Event.Uint64 = Status;
+      if (Event.Elements.ErrorCodeValid) {
+        Regs->ExceptionData = Event.Elements.ErrorCode;
+      } else {
+        Regs->ExceptionData = 0;
+      }
+
+      *ExceptionType = Event.Elements.Vector;
+    }
+
+    VcRet = EFI_PROTOCOL_ERROR;
+  }
+
+  VmgDone (Ghcb);
+
+  return VcRet;
+}
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni b/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
new file mode 100644
index 000000000000..a919b484c319
--- /dev/null
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
@@ -0,0 +1,15 @@
+// /** @file
+// VMGEXIT support library instance.
+//
+// VMGEXIT support library instance.
+//
+// Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT             #language en-US "OVMF VMGEXIT Support Library."
+
+#string STR_MODULE_DESCRIPTION          #language en-US "OVMF VMGEXIT Support Library."
+
-- 
2.17.1


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

* [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (11 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 12/46] OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-21 17:25   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 14/46] OvmfPkg/VmgExitLib: Support string IO " Lendacky, Thomas
                   ` (33 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a IOIO_PROT intercept generates a #VC exception. VMGEXIT
must be used to allow the hypervisor to handle this intercept.

Add support to construct the required GHCB values to support a IOIO_PROT
NAE event.  Parse the instruction that generated the #VC exception,
setting the required register values in the GHCB and creating the proper
SW_EXITINFO1 value in the GHCB.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 604 +++++++++++++++++-
 1 file changed, 590 insertions(+), 14 deletions(-)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 036f030d6b34..b4578ae922c1 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -12,6 +12,573 @@
 #include <Library/VmgExitLib.h>
 #include <Register/Amd/Msr.h>
 
+//
+// Instruction execution mode definition
+//
+typedef enum {
+  LongMode64Bit        = 0,
+  LongModeCompat32Bit,
+  LongModeCompat16Bit,
+} SEV_ES_INSTRUCTION_MODE;
+
+//
+// Instruction size definition (for operand and address)
+//
+typedef enum {
+  Size8Bits            = 0,
+  Size16Bits,
+  Size32Bits,
+  Size64Bits,
+} SEV_ES_INSTRUCTION_SIZE;
+
+//
+// Intruction segment definition
+//
+typedef enum {
+  SegmentEs            = 0,
+  SegmentCs,
+  SegmentSs,
+  SegmentDs,
+  SegmentFs,
+  SegmentGs,
+} SEV_ES_INSTRUCTION_SEGMENT;
+
+//
+// Instruction rep function definition
+//
+typedef enum {
+  RepNone              = 0,
+  RepZ,
+  RepNZ,
+} SEV_ES_INSTRUCTION_REP;
+
+//
+// Instruction REX prefix definition
+//
+typedef union {
+  struct {
+    UINT8  BitB:1;
+    UINT8  BitX:1;
+    UINT8  BitR:1;
+    UINT8  BitW:1;
+    UINT8  Rex:4;
+  } Bits;
+
+  UINT8  Uint8;
+} SEV_ES_INSTRUCTION_REX_PREFIX;
+
+//
+// Instruction ModRM definition
+//
+typedef union {
+  struct {
+    UINT8  Rm:3;
+    UINT8  Reg:3;
+    UINT8  Mod:2;
+  } Bits;
+
+  UINT8  Uint8;
+} SEV_ES_INSTRUCTION_MODRM;
+
+typedef struct {
+  UINT8  Rm;
+  UINT8  Reg;
+  UINT8  Mod;
+} SEV_ES_INSTRUCTION_MODRM_EXT;
+
+//
+// Instruction SIB definition
+//
+typedef union {
+  struct {
+    UINT8  Base:3;
+    UINT8  Index:3;
+    UINT8  Scale:2;
+  } Bits;
+
+  UINT8  Uint8;
+} SEV_ES_INSTRUCTION_SIB;
+
+typedef struct {
+  UINT8  Base;
+  UINT8  Index;
+  UINT8  Scale;
+} SEV_ES_INSTRUCTION_SIB_EXT;
+
+//
+// Instruction opcode definition
+//
+typedef struct {
+  SEV_ES_INSTRUCTION_MODRM_EXT  ModRm;
+
+  SEV_ES_INSTRUCTION_SIB_EXT    Sib;
+
+  UINTN                         RegData;
+  UINTN                         RmData;
+} SEV_ES_INSTRUCTION_OPCODE_EXT;
+
+//
+// Instruction parsing context definition
+//
+typedef struct {
+  GHCB                           *Ghcb;
+
+  SEV_ES_INSTRUCTION_MODE        Mode;
+  SEV_ES_INSTRUCTION_SIZE        DataSize;
+  SEV_ES_INSTRUCTION_SIZE        AddrSize;
+  BOOLEAN                        SegmentSpecified;
+  SEV_ES_INSTRUCTION_SEGMENT     Segment;
+  SEV_ES_INSTRUCTION_REP         RepMode;
+
+  UINT8                          *Begin;
+  UINT8                          *End;
+
+  UINT8                          *Prefixes;
+  UINT8                          *OpCodes;
+  UINT8                          *Displacement;
+  UINT8                          *Immediate;
+
+  SEV_ES_INSTRUCTION_REX_PREFIX  RexPrefix;
+
+  BOOLEAN                        ModRmPresent;
+  SEV_ES_INSTRUCTION_MODRM       ModRm;
+
+  BOOLEAN                        SibPresent;
+  SEV_ES_INSTRUCTION_SIB         Sib;
+
+  UINTN                          PrefixSize;
+  UINTN                          OpCodeSize;
+  UINTN                          DisplacementSize;
+  UINTN                          ImmediateSize;
+
+  SEV_ES_INSTRUCTION_OPCODE_EXT  Ext;
+} SEV_ES_INSTRUCTION_DATA;
+
+//
+// Non-automatic Exit function prototype
+//
+typedef
+UINT64
+(*NAE_EXIT) (
+  GHCB                     *Ghcb,
+  EFI_SYSTEM_CONTEXT_X64   *Regs,
+  SEV_ES_INSTRUCTION_DATA  *InstructionData
+  );
+
+
+/**
+  Checks the GHCB to determine if the specified register has been marked valid.
+
+  The ValidBitmap area represents the areas of the GHCB that have been marked
+  valid. Return an indication of whether the area of the GHCB that holds the
+  specified register has been marked valid.
+
+  @param[in] Ghcb    Pointer to the Guest-Hypervisor Communication Block
+  @param[in] Reg     Offset in the GHCB of the register to check
+
+  @retval TRUE       Register has been marked vald in the GHCB
+  @retval FALSE      Register has not been marked valid in the GHCB
+
+**/
+STATIC
+BOOLEAN
+GhcbIsRegValid (
+  IN GHCB                *Ghcb,
+  IN GHCB_REGISTER       Reg
+  )
+{
+  UINT32  RegIndex;
+  UINT32  RegBit;
+
+  RegIndex = Reg / 8;
+  RegBit   = Reg & 0x07;
+
+  return (Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit));
+}
+
+/**
+  Marks a register as valid in the GHCB.
+
+  The ValidBitmap area represents the areas of the GHCB that have been marked
+  valid. Set the area of the GHCB that holds the specified register as valid.
+
+  @param[in, out] Ghcb    Pointer to the Guest-Hypervisor Communication Block
+  @param[in] Reg          Offset in the GHCB of the register to mark valid
+
+**/
+STATIC
+VOID
+GhcbSetRegValid (
+  IN OUT GHCB                *Ghcb,
+  IN     GHCB_REGISTER       Reg
+  )
+{
+  UINT32  RegIndex;
+  UINT32  RegBit;
+
+  RegIndex = Reg / 8;
+  RegBit   = Reg & 0x07;
+
+  Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
+}
+
+/**
+  Decode instruction prefixes.
+
+  Parse the instruction data to track the instruction prefixes that have
+  been used.
+
+  @param[in]      Regs             x64 processor context
+  @param[in, out] InstructionData  Instruction parsing context
+
+**/
+STATIC
+VOID
+DecodePrefixes (
+  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  SEV_ES_INSTRUCTION_MODE  Mode;
+  SEV_ES_INSTRUCTION_SIZE  ModeDataSize;
+  SEV_ES_INSTRUCTION_SIZE  ModeAddrSize;
+  UINT8                    *Byte;
+
+  /*TODO: Determine current mode - 64-bit for now */
+  Mode = LongMode64Bit;
+  ModeDataSize = Size32Bits;
+  ModeAddrSize = Size64Bits;
+
+  InstructionData->Mode = Mode;
+  InstructionData->DataSize = ModeDataSize;
+  InstructionData->AddrSize = ModeAddrSize;
+
+  InstructionData->Prefixes = InstructionData->Begin;
+
+  Byte = InstructionData->Prefixes;
+  for ( ; ; Byte++, InstructionData->PrefixSize++) {
+    //
+    // Check the 0x40 to 0x4F range using an if statement here since some
+    // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
+    // 16 case statements below.
+    //
+    if ((*Byte >= 0x40) && (*Byte <= 0x4F)) {
+      InstructionData->RexPrefix.Uint8 = *Byte;
+      if (*Byte & 0x08)
+        InstructionData->DataSize = Size64Bits;
+      continue;
+    }
+
+    switch (*Byte) {
+    case 0x26:
+    case 0x2E:
+    case 0x36:
+    case 0x3E:
+      if (Mode != LongMode64Bit) {
+        InstructionData->SegmentSpecified = TRUE;
+        InstructionData->Segment = (*Byte >> 3) & 3;
+      }
+      break;
+
+    case 0x64:
+      InstructionData->SegmentSpecified = TRUE;
+      InstructionData->Segment = *Byte & 7;
+      break;
+
+    case 0x66:
+      if (!InstructionData->RexPrefix.Uint8) {
+        InstructionData->DataSize =
+          (Mode == LongMode64Bit)       ? Size16Bits :
+          (Mode == LongModeCompat32Bit) ? Size16Bits :
+          (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
+      }
+      break;
+
+    case 0x67:
+      InstructionData->AddrSize =
+        (Mode == LongMode64Bit)       ? Size32Bits :
+        (Mode == LongModeCompat32Bit) ? Size16Bits :
+        (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
+      break;
+
+    case 0xF0:
+      break;
+
+    case 0xF2:
+      InstructionData->RepMode = RepZ;
+      break;
+
+    case 0xF3:
+      InstructionData->RepMode = RepNZ;
+      break;
+
+    default:
+      InstructionData->OpCodes = Byte;
+      InstructionData->OpCodeSize = (*Byte == 0x0F) ? 2 : 1;
+
+      InstructionData->End = Byte + InstructionData->OpCodeSize;
+      InstructionData->Displacement = InstructionData->End;
+      InstructionData->Immediate = InstructionData->End;
+      return;
+    }
+  }
+}
+
+/**
+  Determine instruction length
+
+  Return the total length of the parsed instruction.
+
+  @param[in] InstructionData  Instruction parsing context
+
+  @retval                     Length of parsed instruction
+
+**/
+STATIC
+UINT64
+InstructionLength (
+  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  return (UINT64) (InstructionData->End - InstructionData->Begin);
+}
+
+/**
+  Initialize the instruction parsing context.
+
+  Initialize the instruction parsing context, which includes decoding the
+  instruction prefixes.
+
+  @param[in, out] InstructionData  Instruction parsing context
+  @param[in]      Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in]      Regs             x64 processor context
+
+**/
+STATIC
+VOID
+InitInstructionData (
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
+  IN     GHCB                     *Ghcb,
+  IN     EFI_SYSTEM_CONTEXT_X64   *Regs
+  )
+{
+  SetMem (InstructionData, sizeof (*InstructionData), 0);
+  InstructionData->Ghcb = Ghcb;
+  InstructionData->Begin = (UINT8 *) Regs->Rip;
+  InstructionData->End = (UINT8 *) Regs->Rip;
+
+  DecodePrefixes (Regs, InstructionData);
+}
+
+/**
+  Report an unsupported event to the hypervisor
+
+  Use the VMGEXIT support to report an unsupported event to the hypervisor.
+
+  @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication
+                              Block
+  @param[in] Regs             x64 processor context
+  @param[in] InstructionData  Instruction parsing context
+
+  @retval                     New exception value to propagate
+
+**/
+STATIC
+UINT64
+UnsupportedExit (
+  IN GHCB                     *Ghcb,
+  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
+  if (Status == 0) {
+    GHCB_EVENT_INJECTION  Event;
+
+    Event.Uint64 = 0;
+    Event.Elements.Vector = GP_EXCEPTION;
+    Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+    Event.Elements.Valid  = 1;
+
+    Status = Event.Uint64;
+  }
+
+  return Status;
+}
+
+#define IOIO_TYPE_STR       (1 << 2)
+#define IOIO_TYPE_IN        1
+#define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
+#define IOIO_TYPE_OUT       0
+#define IOIO_TYPE_OUTS      (IOIO_TYPE_OUT | IOIO_TYPE_STR)
+
+#define IOIO_REP            (1 << 3)
+
+#define IOIO_ADDR_64        (1 << 9)
+#define IOIO_ADDR_32        (1 << 8)
+#define IOIO_ADDR_16        (1 << 7)
+
+#define IOIO_DATA_32        (1 << 6)
+#define IOIO_DATA_16        (1 << 5)
+#define IOIO_DATA_8         (1 << 4)
+#define IOIO_DATA_BYTES(x)  (((x) & 0x70) >> 4)
+
+#define IOIO_SEG_ES         (0 << 10)
+#define IOIO_SEG_DS         (3 << 10)
+
+/**
+  Build the IOIO event information.
+
+  The IOIO event information identifies the type of IO operation to be performed
+  by the hypervisor. Build this information based on the instruction data.
+
+  @param[in]       Regs             x64 processor context
+  @param[in, out]  InstructionData  Instruction parsing context
+
+  @retval Others                    IOIO event information value
+
+**/
+STATIC
+UINT64
+IoioExitInfo (
+  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  ExitInfo;
+
+  ExitInfo = 0;
+
+  switch (*(InstructionData->OpCodes)) {
+  // IN immediate opcodes
+  case 0xE4:
+  case 0xE5:
+    InstructionData->ImmediateSize = 1;
+    InstructionData->End++;
+    ExitInfo |= IOIO_TYPE_IN;
+    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
+    break;
+
+  // OUT immediate opcodes
+  case 0xE6:
+  case 0xE7:
+    InstructionData->ImmediateSize = 1;
+    InstructionData->End++;
+    ExitInfo |= IOIO_TYPE_OUT;
+    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
+    break;
+
+  // IN register opcodes
+  case 0xEC:
+  case 0xED:
+    ExitInfo |= IOIO_TYPE_IN;
+    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+    break;
+
+  // OUT register opcodes
+  case 0xEE:
+  case 0xEF:
+    ExitInfo |= IOIO_TYPE_OUT;
+    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+    break;
+
+  default:
+    return 0;
+  }
+
+  switch (*(InstructionData->OpCodes)) {
+  case 0xE4:
+  case 0xE6:
+  case 0xEC:
+  case 0xEE:
+    // Single-byte opcodes
+    ExitInfo |= IOIO_DATA_8;
+    break;
+
+  default:
+    // Length determined by instruction parsing
+    ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
+                                                          : IOIO_DATA_32;
+  }
+
+  switch (InstructionData->AddrSize) {
+  case Size16Bits:
+    ExitInfo |= IOIO_ADDR_16;
+    break;
+
+  case Size32Bits:
+    ExitInfo |= IOIO_ADDR_32;
+    break;
+
+  case Size64Bits:
+    ExitInfo |= IOIO_ADDR_64;
+    break;
+
+  default:
+    break;
+  }
+
+  if (InstructionData->RepMode) {
+    ExitInfo |= IOIO_REP;
+  }
+
+  return ExitInfo;
+}
+
+/**
+  Handle an IOIO event.
+
+  Use the VMGEXIT instruction to handle an IOIO event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+IoioExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  ExitInfo1, Status;
+
+  ExitInfo1 = IoioExitInfo (Regs, InstructionData);
+  if (!ExitInfo1) {
+    return UnsupportedExit (Ghcb, Regs, InstructionData);
+  }
+
+  if (ExitInfo1 & IOIO_TYPE_IN) {
+    Ghcb->SaveArea.Rax = 0;
+  } else {
+    CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
+  }
+  GhcbSetRegValid (Ghcb, GhcbRax);
+
+  Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
+  if (Status) {
+    return Status;
+  }
+
+  if (ExitInfo1 & IOIO_TYPE_IN) {
+    if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
+      return UnsupportedExit (Ghcb, Regs, InstructionData);
+    }
+    CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
+  }
+
+  return 0;
+}
+
 /**
   Handle a #VC exception.
 
@@ -38,6 +605,8 @@ VmgExitHandleVc (
   MSR_SEV_ES_GHCB_REGISTER  Msr;
   EFI_SYSTEM_CONTEXT_X64    *Regs;
   GHCB                      *Ghcb;
+  NAE_EXIT                  NaeExit;
+  SEV_ES_INSTRUCTION_DATA   InstructionData;
   UINT64                    ExitCode, Status;
   EFI_STATUS                VcRet;
 
@@ -54,24 +623,31 @@ VmgExitHandleVc (
 
   ExitCode = Regs->ExceptionData;
   switch (ExitCode) {
+  case SVM_EXIT_IOIO_PROT:
+    NaeExit = IoioExit;
+    break;
+
   default:
-    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
-    if (Status == 0) {
-      Regs->ExceptionData = 0;
-      *ExceptionType = GP_EXCEPTION;
+    NaeExit = UnsupportedExit;
+  }
+
+  InitInstructionData (&InstructionData, Ghcb, Regs);
+
+  Status = NaeExit (Ghcb, Regs, &InstructionData);
+  if (Status == 0) {
+    Regs->Rip += InstructionLength (&InstructionData);
+  } else {
+    GHCB_EVENT_INJECTION  Event;
+
+    Event.Uint64 = Status;
+    if (Event.Elements.ErrorCodeValid) {
+      Regs->ExceptionData = Event.Elements.ErrorCode;
     } else {
-      GHCB_EVENT_INJECTION  Event;
-
-      Event.Uint64 = Status;
-      if (Event.Elements.ErrorCodeValid) {
-        Regs->ExceptionData = Event.Elements.ErrorCode;
-      } else {
-        Regs->ExceptionData = 0;
-      }
-
-      *ExceptionType = Event.Elements.Vector;
+      Regs->ExceptionData = 0;
     }
 
+    *ExceptionType = Event.Elements.Vector;
+
     VcRet = EFI_PROTOCOL_ERROR;
   }
 
-- 
2.17.1


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

* [PATCH v8 14/46] OvmfPkg/VmgExitLib: Support string IO for IOIO_PROT NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (12 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 10:14   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 15/46] OvmfPkg/VmgExitLib: Add support for CPUID " Lendacky, Thomas
                   ` (32 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Add support to the #VC exception handler to handle string IO. This
requires expanding the IO instruction parsing to recognize string based
IO instructions as well as preparing an un-encrypted buffer to be used
to transfer (either to or from the guest) the string contents for the IO
operation. The SW_EXITINFO2 and SW_SCRATCH fields of the GHCB are set
appropriately for the operation. Multiple VMGEXIT invocations may be
needed to complete the string IO operation.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 86 ++++++++++++++++---
 1 file changed, 72 insertions(+), 14 deletions(-)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index b4578ae922c1..906b32e93d53 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -453,6 +453,22 @@ IoioExitInfo (
   ExitInfo = 0;
 
   switch (*(InstructionData->OpCodes)) {
+  // INS opcodes
+  case 0x6C:
+  case 0x6D:
+    ExitInfo |= IOIO_TYPE_INS;
+    ExitInfo |= IOIO_SEG_ES;
+    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+    break;
+
+  // OUTS opcodes
+  case 0x6E:
+  case 0x6F:
+    ExitInfo |= IOIO_TYPE_OUTS;
+    ExitInfo |= IOIO_SEG_DS;
+    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+    break;
+
   // IN immediate opcodes
   case 0xE4:
   case 0xE5:
@@ -490,6 +506,8 @@ IoioExitInfo (
   }
 
   switch (*(InstructionData->OpCodes)) {
+  case 0x6C:
+  case 0x6E:
   case 0xE4:
   case 0xE6:
   case 0xEC:
@@ -550,30 +568,70 @@ IoioExit (
   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
   )
 {
-  UINT64  ExitInfo1, Status;
+  UINT64   ExitInfo1, ExitInfo2, Status;
+  BOOLEAN  String;
 
   ExitInfo1 = IoioExitInfo (Regs, InstructionData);
   if (!ExitInfo1) {
     return UnsupportedExit (Ghcb, Regs, InstructionData);
   }
 
-  if (ExitInfo1 & IOIO_TYPE_IN) {
-    Ghcb->SaveArea.Rax = 0;
+  String = (ExitInfo1 & IOIO_TYPE_STR) ? TRUE : FALSE;
+  if (String) {
+    UINTN  IoBytes, VmgExitBytes;
+    UINTN  GhcbCount, OpCount;
+
+    Status = 0;
+
+    IoBytes = (ExitInfo1 >> 4) & 0x7;
+    GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
+
+    OpCount = (ExitInfo1 & IOIO_REP) ? Regs->Rcx : 1;
+    while (OpCount) {
+      ExitInfo2 = MIN (OpCount, GhcbCount);
+      VmgExitBytes = ExitInfo2 * IoBytes;
+
+      if (!(ExitInfo1 & IOIO_TYPE_IN)) {
+        CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);
+        Regs->Rsi += VmgExitBytes;
+      }
+
+      Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+      Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
+      if (Status) {
+        return Status;
+      }
+
+      if (ExitInfo1 & IOIO_TYPE_IN) {
+        CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
+        Regs->Rdi += VmgExitBytes;
+      }
+
+      if (ExitInfo1 & IOIO_REP) {
+        Regs->Rcx -= ExitInfo2;
+      }
+
+      OpCount -= ExitInfo2;
+    }
   } else {
-    CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
-  }
-  GhcbSetRegValid (Ghcb, GhcbRax);
+    if (ExitInfo1 & IOIO_TYPE_IN) {
+      Ghcb->SaveArea.Rax = 0;
+    } else {
+      CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
+    }
+    GhcbSetRegValid (Ghcb, GhcbRax);
 
-  Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
-  if (Status) {
-    return Status;
-  }
+    Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
+    if (Status) {
+      return Status;
+    }
 
-  if (ExitInfo1 & IOIO_TYPE_IN) {
-    if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
-      return UnsupportedExit (Ghcb, Regs, InstructionData);
+    if (ExitInfo1 & IOIO_TYPE_IN) {
+      if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
+        return UnsupportedExit (Ghcb, Regs, InstructionData);
+      }
+      CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
     }
-    CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
   }
 
   return 0;
-- 
2.17.1


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

* [PATCH v8 15/46] OvmfPkg/VmgExitLib: Add support for CPUID NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (13 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 14/46] OvmfPkg/VmgExitLib: Support string IO " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 10:27   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 16/46] OvmfPkg/VmgExitLib: Add support for MSR_PROT " Lendacky, Thomas
                   ` (31 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a CPUID intercept generates a #VC exception. VMGEXIT must be
used to allow the hypervisor to handle this intercept.

Add support to construct the required GHCB values to support a CPUID NAE
event. Additionally, CPUID 0x0000_000d requires XCR0 to be supplied in
the GHCB, so add support to issue the XGETBV instruction.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 58 +++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 906b32e93d53..2f62795edf61 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -12,6 +12,8 @@
 #include <Library/VmgExitLib.h>
 #include <Register/Amd/Msr.h>
 
+#define CR4_OSXSAVE (1 << 18)
+
 //
 // Instruction execution mode definition
 //
@@ -637,6 +639,58 @@ IoioExit (
   return 0;
 }
 
+/**
+  Handle a CPUID event.
+
+  Use the VMGEXIT instruction to handle a CPUID event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+CpuidExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  Ghcb->SaveArea.Rax = Regs->Rax;
+  GhcbSetRegValid (Ghcb, GhcbRax);
+  Ghcb->SaveArea.Rcx = Regs->Rcx;
+  GhcbSetRegValid (Ghcb, GhcbRcx);
+  if (Regs->Rax == 0x0000000d) {
+    Ghcb->SaveArea.XCr0 = (AsmReadCr4 () & CR4_OSXSAVE) ? AsmXGetBv (0) : 1;
+    GhcbSetRegValid (Ghcb, GhcbXCr0);
+  }
+
+  Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+      !GhcbIsRegValid (Ghcb, GhcbRbx) ||
+      !GhcbIsRegValid (Ghcb, GhcbRcx) ||
+      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+    return UnsupportedExit (Ghcb, Regs, InstructionData);
+  }
+  Regs->Rax = Ghcb->SaveArea.Rax;
+  Regs->Rbx = Ghcb->SaveArea.Rbx;
+  Regs->Rcx = Ghcb->SaveArea.Rcx;
+  Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+  return 0;
+}
+
 /**
   Handle a #VC exception.
 
@@ -681,6 +735,10 @@ VmgExitHandleVc (
 
   ExitCode = Regs->ExceptionData;
   switch (ExitCode) {
+  case SVM_EXIT_CPUID:
+    NaeExit = CpuidExit;
+    break;
+
   case SVM_EXIT_IOIO_PROT:
     NaeExit = IoioExit;
     break;
-- 
2.17.1


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

* [PATCH v8 16/46] OvmfPkg/VmgExitLib: Add support for MSR_PROT NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (14 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 15/46] OvmfPkg/VmgExitLib: Add support for CPUID " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 10:31   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO) Lendacky, Thomas
                   ` (30 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a MSR_PROT intercept generates a #VC exception. VMGEXIT must
be used to allow the hypervisor to handle this intercept.

Add support to construct the required GHCB values to support an MSR_PROT
NAE event. Parse the instruction that generated the #VC exception to
determine whether it is RDMSR or WRMSR, setting the required register
register values in the GHCB and creating the proper SW_EXIT_INFO1 value in
the GHCB.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 2f62795edf61..1c6b472a47c4 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -411,6 +411,65 @@ UnsupportedExit (
   return Status;
 }
 
+/**
+  Handle an MSR event.
+
+  Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+MsrExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  ExitInfo1, Status;
+
+  ExitInfo1 = 0;
+
+  switch (*(InstructionData->OpCodes + 1)) {
+  case 0x30: // WRMSR
+    ExitInfo1 = 1;
+    Ghcb->SaveArea.Rax = Regs->Rax;
+    GhcbSetRegValid (Ghcb, GhcbRax);
+    Ghcb->SaveArea.Rdx = Regs->Rdx;
+    GhcbSetRegValid (Ghcb, GhcbRdx);
+    /* Fallthrough */
+  case 0x32: // RDMSR
+    Ghcb->SaveArea.Rcx = Regs->Rcx;
+    GhcbSetRegValid (Ghcb, GhcbRcx);
+    break;
+  default:
+    return UnsupportedExit (Ghcb, Regs, InstructionData);
+  }
+
+  Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
+  if (Status) {
+    return Status;
+  }
+
+  if (!ExitInfo1) {
+    if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+        !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+      return UnsupportedExit (Ghcb, Regs, InstructionData);
+    }
+    Regs->Rax = Ghcb->SaveArea.Rax;
+    Regs->Rdx = Ghcb->SaveArea.Rdx;
+  }
+
+  return 0;
+}
+
 #define IOIO_TYPE_STR       (1 << 2)
 #define IOIO_TYPE_IN        1
 #define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
@@ -743,6 +802,10 @@ VmgExitHandleVc (
     NaeExit = IoioExit;
     break;
 
+  case SVM_EXIT_MSR:
+    NaeExit = MsrExit;
+    break;
+
   default:
     NaeExit = UnsupportedExit;
   }
-- 
2.17.1


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

* [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO)
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (15 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 16/46] OvmfPkg/VmgExitLib: Add support for MSR_PROT " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:14   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events Lendacky, Thomas
                   ` (29 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a NPF intercept for an NPT entry with a reserved bit set
generates a #VC exception. This condition is assumed to be an MMIO access.
VMGEXIT must be used to allow the hypervisor to handle this intercept.

Add support to construct the required GHCB values to support a NPF NAE
event for MMIO.  Parse the instruction that generated the #VC exception,
setting the required register values in the GHCB and creating the proper
SW_EXIT_INFO1, SW_EXITINFO2 and SW_SCRATCH values in the GHCB.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 436 ++++++++++++++++++
 1 file changed, 436 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 1c6b472a47c4..50199845ceef 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -224,6 +224,263 @@ GhcbSetRegValid (
   Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
 }
 
+/**
+  Return a pointer to the contents of the specified register.
+
+  Based upon the input register, return a pointer to the registers contents
+  in the x86 processor context.
+
+  @param[in] Regs      x64 processor context
+  @param[in] Register  Register to obtain pointer for
+
+  @retval              Pointer to the contents of the requested register
+
+**/
+STATIC
+INT64 *
+GetRegisterPointer (
+  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN UINT8                    Register
+  )
+{
+  UINT64 *Reg;
+
+  switch (Register) {
+  case 0:
+    Reg = &Regs->Rax;
+    break;
+  case 1:
+    Reg = &Regs->Rcx;
+    break;
+  case 2:
+    Reg = &Regs->Rdx;
+    break;
+  case 3:
+    Reg = &Regs->Rbx;
+    break;
+  case 4:
+    Reg = &Regs->Rsp;
+    break;
+  case 5:
+    Reg = &Regs->Rbp;
+    break;
+  case 6:
+    Reg = &Regs->Rsi;
+    break;
+  case 7:
+    Reg = &Regs->Rdi;
+    break;
+  case 8:
+    Reg = &Regs->R8;
+    break;
+  case 9:
+    Reg = &Regs->R9;
+    break;
+  case 10:
+    Reg = &Regs->R10;
+    break;
+  case 11:
+    Reg = &Regs->R11;
+    break;
+  case 12:
+    Reg = &Regs->R12;
+    break;
+  case 13:
+    Reg = &Regs->R13;
+    break;
+  case 14:
+    Reg = &Regs->R14;
+    break;
+  case 15:
+    Reg = &Regs->R15;
+    break;
+  default:
+    Reg = NULL;
+  }
+  ASSERT (Reg != NULL);
+
+  return (INT64 *) Reg;
+}
+
+/**
+  Update the instruction parsing context for displacement bytes.
+
+  @param[in, out] InstructionData  Instruction parsing context
+  @param[in]      Size             The instruction displacement size
+
+**/
+STATIC
+VOID
+UpdateForDisplacement (
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
+  IN     UINTN                    Size
+  )
+{
+  InstructionData->DisplacementSize = Size;
+  InstructionData->Immediate += Size;
+  InstructionData->End += Size;
+}
+
+/**
+  Determine if an instruction address if RIP relative.
+
+  Examine the instruction parsing context to determine if the address offset
+  is relative to the instruction pointer.
+
+  @param[in] InstructionData  Instruction parsing context
+
+  @retval TRUE                Instruction addressing is RIP relative
+  @retval FALSE               Instruction addressing is not RIP relative
+
+**/
+STATIC
+BOOLEAN
+IsRipRelative (
+  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
+
+  Ext = &InstructionData->Ext;
+
+  return ((InstructionData->Mode == LongMode64Bit) &&
+          (Ext->ModRm.Mod == 0) &&
+          (Ext->ModRm.Rm == 5)  &&
+          (InstructionData->SibPresent == FALSE));
+}
+
+/**
+  Return the effective address of a memory operand.
+
+  Examine the instruction parsing context to obtain the effective memory
+  address of a memory operand.
+
+  @param[in] Regs             x64 processor context
+  @param[in] InstructionData  Instruction parsing context
+
+  @retval                     The memory operand effective address
+
+**/
+STATIC
+UINTN
+GetEffectiveMemoryAddress (
+  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
+  INTN                           EffectiveAddress;
+
+  Ext = &InstructionData->Ext;
+  EffectiveAddress = 0;
+
+  if (IsRipRelative (InstructionData)) {
+    /* RIP-relative displacement is a 32-bit signed value */
+    INT32 RipRelative;
+
+    RipRelative = *(INT32 *) InstructionData->Displacement;
+
+    UpdateForDisplacement (InstructionData, 4);
+    return (UINTN) ((INTN) Regs->Rip + RipRelative);
+  }
+
+  switch (Ext->ModRm.Mod) {
+  case 1:
+    UpdateForDisplacement (InstructionData, 1);
+    EffectiveAddress += (INT8) (*(INT8 *) (InstructionData->Displacement));
+    break;
+  case 2:
+    switch (InstructionData->AddrSize) {
+    case Size16Bits:
+      UpdateForDisplacement (InstructionData, 2);
+      EffectiveAddress += (INT16) (*(INT16 *) (InstructionData->Displacement));
+      break;
+    default:
+      UpdateForDisplacement (InstructionData, 4);
+      EffectiveAddress += (INT32) (*(INT32 *) (InstructionData->Displacement));
+      break;
+    }
+    break;
+  }
+
+  if (InstructionData->SibPresent) {
+    if (Ext->Sib.Index != 4) {
+      EffectiveAddress += (*GetRegisterPointer (Regs, Ext->Sib.Index) << Ext->Sib.Scale);
+    }
+
+    if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {
+      EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);
+    } else {
+      UpdateForDisplacement (InstructionData, 4);
+      EffectiveAddress += (INT32) (*(INT32 *) (InstructionData->Displacement));
+    }
+  } else {
+    EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);
+  }
+
+  return (UINTN) EffectiveAddress;
+}
+
+/**
+  Decode a ModRM byte.
+
+  Examine the instruction parsing context to decode a ModRM byte and the SIB
+  byte, if present.
+
+  @param[in]      Regs             x64 processor context
+  @param[in, out] InstructionData  Instruction parsing context
+
+**/
+STATIC
+VOID
+DecodeModRm (
+  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  SEV_ES_INSTRUCTION_REX_PREFIX  *RexPrefix;
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
+  SEV_ES_INSTRUCTION_MODRM       *ModRm;
+  SEV_ES_INSTRUCTION_SIB         *Sib;
+
+  RexPrefix = &InstructionData->RexPrefix;
+  Ext = &InstructionData->Ext;
+  ModRm = &InstructionData->ModRm;
+  Sib = &InstructionData->Sib;
+
+  InstructionData->ModRmPresent = TRUE;
+  ModRm->Uint8 = *(InstructionData->End);
+
+  InstructionData->Displacement++;
+  InstructionData->Immediate++;
+  InstructionData->End++;
+
+  Ext->ModRm.Mod = ModRm->Bits.Mod;
+  Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;
+  Ext->ModRm.Rm  = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;
+
+  Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);
+
+  if (Ext->ModRm.Mod == 3) {
+    Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);
+  } else {
+    if (ModRm->Bits.Rm == 4) {
+      InstructionData->SibPresent = TRUE;
+      Sib->Uint8 = *(InstructionData->End);
+
+      InstructionData->Displacement++;
+      InstructionData->Immediate++;
+      InstructionData->End++;
+
+      Ext->Sib.Scale = Sib->Bits.Scale;
+      Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;
+      Ext->Sib.Base  = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;
+    }
+
+    Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);
+  }
+}
+
 /**
   Decode instruction prefixes.
 
@@ -411,6 +668,181 @@ UnsupportedExit (
   return Status;
 }
 
+/**
+  Handle an MMIO event.
+
+  Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in, out] InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+MmioExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  ExitInfo1, ExitInfo2, Status;
+  UINTN   Bytes;
+  INTN    *Register;
+  UINT8   OpCode, SignByte;
+
+  Bytes = 0;
+
+  OpCode = *(InstructionData->OpCodes);
+  if (OpCode == 0x0F) {
+    OpCode = *(InstructionData->OpCodes + 1);
+  }
+
+  switch (OpCode) {
+  /* MMIO write */
+  case 0x88:
+    Bytes = 1;
+  case 0x89:
+    DecodeModRm (Regs, InstructionData);
+    Bytes = (Bytes) ? Bytes
+                    : (InstructionData->DataSize == Size16Bits) ? 2
+                    : (InstructionData->DataSize == Size32Bits) ? 4
+                    : (InstructionData->DataSize == Size64Bits) ? 8
+                    : 0;
+
+    if (InstructionData->Ext.ModRm.Mod == 3) {
+      /* NPF on two register operands??? */
+      return UnsupportedExit (Ghcb, Regs, InstructionData);
+    }
+
+    ExitInfo1 = InstructionData->Ext.RmData;
+    ExitInfo2 = Bytes;
+    CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
+
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
+    if (Status) {
+      return Status;
+    }
+    break;
+
+  case 0xC6:
+    Bytes = 1;
+  case 0xC7:
+    DecodeModRm (Regs, InstructionData);
+    Bytes = (Bytes) ? Bytes
+                    : (InstructionData->DataSize == Size16Bits) ? 2
+                    : (InstructionData->DataSize == Size32Bits) ? 4
+                    : 0;
+
+    InstructionData->ImmediateSize = Bytes;
+    InstructionData->End += Bytes;
+
+    ExitInfo1 = InstructionData->Ext.RmData;
+    ExitInfo2 = Bytes;
+    CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
+
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
+    if (Status) {
+      return Status;
+    }
+    break;
+
+  /* MMIO read */
+  case 0x8A:
+    Bytes = 1;
+  case 0x8B:
+    DecodeModRm (Regs, InstructionData);
+    Bytes = (Bytes) ? Bytes
+                    : (InstructionData->DataSize == Size16Bits) ? 2
+                    : (InstructionData->DataSize == Size32Bits) ? 4
+                    : (InstructionData->DataSize == Size64Bits) ? 8
+                    : 0;
+    if (InstructionData->Ext.ModRm.Mod == 3) {
+      /* NPF on two register operands??? */
+      return UnsupportedExit (Ghcb, Regs, InstructionData);
+    }
+
+    ExitInfo1 = InstructionData->Ext.RmData;
+    ExitInfo2 = Bytes;
+
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+    if (Status) {
+      return Status;
+    }
+
+    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+    if (Bytes == 4) {
+      /* Zero-extend for 32-bit operation */
+      *Register = 0;
+    }
+    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+    break;
+
+  /* MMIO Read w/ zero-extension */
+  case 0xB6:
+    Bytes = 1;
+  case 0xB7:
+    Bytes = (Bytes) ? Bytes : 2;
+
+    ExitInfo1 = InstructionData->Ext.RmData;
+    ExitInfo2 = Bytes;
+
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+    if (Status) {
+      return Status;
+    }
+
+    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+    SetMem (Register, InstructionData->DataSize, 0);
+    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+    break;
+
+  /* MMIO Read w/ sign-extension */
+  case 0xBE:
+    Bytes = 1;
+  case 0xBF:
+    Bytes = (Bytes) ? Bytes : 2;
+
+    ExitInfo1 = InstructionData->Ext.RmData;
+    ExitInfo2 = Bytes;
+
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+    if (Status) {
+      return Status;
+    }
+
+    if (Bytes == 1) {
+      UINT8 *Data = (UINT8 *) Ghcb->SharedBuffer;
+
+      SignByte = (*Data & 0x80) ? 0xFF : 0x00;
+    } else {
+      UINT16 *Data = (UINT16 *) Ghcb->SharedBuffer;
+
+      SignByte = (*Data & 0x8000) ? 0xFF : 0x00;
+    }
+
+    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+    SetMem (Register, InstructionData->DataSize, SignByte);
+    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+    break;
+
+  default:
+    Status = GP_EXCEPTION;
+    ASSERT (FALSE);
+  }
+
+  return Status;
+}
+
 /**
   Handle an MSR event.
 
@@ -806,6 +1238,10 @@ VmgExitHandleVc (
     NaeExit = MsrExit;
     break;
 
+  case SVM_EXIT_NPF:
+    NaeExit = MmioExit;
+    break;
+
   default:
     NaeExit = UnsupportedExit;
   }
-- 
2.17.1


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

* [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (16 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO) Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:19   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 19/46] OvmfPkg/VmgExitLib: Add support for RDTSC " Lendacky, Thomas
                   ` (28 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a WBINVD intercept generates a #VC exception. VMGEXIT must be
used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 36 +++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 50199845ceef..0cf26c052b32 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -843,6 +843,38 @@ MmioExit (
   return Status;
 }
 
+/**
+  Handle a WBINVD event.
+
+  Use the VMGEXIT instruction to handle a WBINVD event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+WbinvdExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  Status = VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  return 0;
+}
+
 /**
   Handle an MSR event.
 
@@ -1238,6 +1270,10 @@ VmgExitHandleVc (
     NaeExit = MsrExit;
     break;
 
+  case SVM_EXIT_WBINVD:
+    NaeExit = WbinvdExit;
+    break;
+
   case SVM_EXIT_NPF:
     NaeExit = MmioExit;
     break;
-- 
2.17.1


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

* [PATCH v8 19/46] OvmfPkg/VmgExitLib: Add support for RDTSC NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (17 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:42   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 20/46] OvmfPkg/VmgExitLib: Add support for RDPMC " Lendacky, Thomas
                   ` (27 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a RDTSC intercept generates a #VC exception. VMGEXIT must be
used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 0cf26c052b32..3d2a9c6f9bc9 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -1214,6 +1214,45 @@ CpuidExit (
   return 0;
 }
 
+/**
+  Handle a RDTSC event.
+
+  Use the VMGEXIT instruction to handle a RDTSC event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdtscExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  Status = VmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+    return UnsupportedExit (Ghcb, Regs, InstructionData);
+  }
+  Regs->Rax = Ghcb->SaveArea.Rax;
+  Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+  return 0;
+}
+
 /**
   Handle a #VC exception.
 
@@ -1258,6 +1297,10 @@ VmgExitHandleVc (
 
   ExitCode = Regs->ExceptionData;
   switch (ExitCode) {
+  case SVM_EXIT_RDTSC:
+    NaeExit = RdtscExit;
+    break;
+
   case SVM_EXIT_CPUID:
     NaeExit = CpuidExit;
     break;
-- 
2.17.1


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

* [PATCH v8 20/46] OvmfPkg/VmgExitLib: Add support for RDPMC NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (18 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 19/46] OvmfPkg/VmgExitLib: Add support for RDTSC " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:43   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 21/46] OvmfPkg/VmgExitLib: Add support for INVD " Lendacky, Thomas
                   ` (26 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a RDPMC intercept generates a #VC exception. VMGEXIT must be
used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 46 +++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 3d2a9c6f9bc9..d6b35b3019c3 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -1214,6 +1214,48 @@ CpuidExit (
   return 0;
 }
 
+/**
+  Handle a RDPMC event.
+
+  Use the VMGEXIT instruction to handle a RDPMC event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdpmcExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  Ghcb->SaveArea.Rcx = Regs->Rcx;
+  GhcbSetRegValid (Ghcb, GhcbRcx);
+
+  Status = VmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+    return UnsupportedExit (Ghcb, Regs, InstructionData);
+  }
+  Regs->Rax = Ghcb->SaveArea.Rax;
+  Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+  return 0;
+}
+
 /**
   Handle a RDTSC event.
 
@@ -1301,6 +1343,10 @@ VmgExitHandleVc (
     NaeExit = RdtscExit;
     break;
 
+  case SVM_EXIT_RDPMC:
+    NaeExit = RdpmcExit;
+    break;
+
   case SVM_EXIT_CPUID:
     NaeExit = CpuidExit;
     break;
-- 
2.17.1


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

* [PATCH v8 21/46] OvmfPkg/VmgExitLib: Add support for INVD NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (19 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 20/46] OvmfPkg/VmgExitLib: Add support for RDPMC " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:46   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL " Lendacky, Thomas
                   ` (25 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a INVD intercept generates a #VC exception. VMGEXIT must be
used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 36 +++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index d6b35b3019c3..0b7f24fd962c 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -1162,6 +1162,38 @@ IoioExit (
   return 0;
 }
 
+/**
+  Handle a INVD event.
+
+  Use the VMGEXIT instruction to handle a INVD event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+InvdExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  Status = VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  return 0;
+}
+
 /**
   Handle a CPUID event.
 
@@ -1351,6 +1383,10 @@ VmgExitHandleVc (
     NaeExit = CpuidExit;
     break;
 
+  case SVM_EXIT_INVD:
+    NaeExit = InvdExit;
+    break;
+
   case SVM_EXIT_IOIO_PROT:
     NaeExit = IoioExit;
     break;
-- 
2.17.1


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

* [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (20 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 21/46] OvmfPkg/VmgExitLib: Add support for INVD " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:48   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 23/46] OvmfPkg/VmgExitLib: Add support for RDTSCP " Lendacky, Thomas
                   ` (24 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a VMMCALL intercept generates a #VC exception. VMGEXIT must
be used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 48 +++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 0b7f24fd962c..bf1555234d69 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -875,6 +875,50 @@ WbinvdExit (
   return 0;
 }
 
+/**
+  Handle a VMMCALL event.
+
+  Use the VMGEXIT instruction to handle either a VMMCALL event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+VmmCallExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  DecodeModRm (Regs, InstructionData);
+
+  Ghcb->SaveArea.Rax = Regs->Rax;
+  GhcbSetRegValid (Ghcb, GhcbRax);
+  Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);
+  GhcbSetRegValid (Ghcb, GhcbCpl);
+
+  Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
+    return UnsupportedExit (Ghcb, Regs, InstructionData);
+  }
+  Regs->Rax = Ghcb->SaveArea.Rax;
+
+  return 0;
+}
+
 /**
   Handle an MSR event.
 
@@ -1395,6 +1439,10 @@ VmgExitHandleVc (
     NaeExit = MsrExit;
     break;
 
+  case SVM_EXIT_VMMCALL:
+    NaeExit = VmmCallExit;
+    break;
+
   case SVM_EXIT_WBINVD:
     NaeExit = WbinvdExit;
     break;
-- 
2.17.1


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

* [PATCH v8 23/46] OvmfPkg/VmgExitLib: Add support for RDTSCP NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (21 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:52   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 24/46] OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX " Lendacky, Thomas
                   ` (23 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a RDTSCP intercept generates a #VC exception. VMGEXIT must be
used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index bf1555234d69..716f21a9ee0e 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -875,6 +875,49 @@ WbinvdExit (
   return 0;
 }
 
+/**
+  Handle a RDTSCP event.
+
+  Use the VMGEXIT instruction to handle either a RDTSCP event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdtscpExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  DecodeModRm (Regs, InstructionData);
+
+  Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
+      !GhcbIsRegValid (Ghcb, GhcbRcx) ||
+      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
+    return UnsupportedExit (Ghcb, Regs, InstructionData);
+  }
+  Regs->Rax = Ghcb->SaveArea.Rax;
+  Regs->Rcx = Ghcb->SaveArea.Rcx;
+  Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+  return 0;
+}
+
 /**
   Handle a VMMCALL event.
 
@@ -1443,6 +1486,10 @@ VmgExitHandleVc (
     NaeExit = VmmCallExit;
     break;
 
+  case SVM_EXIT_RDTSCP:
+    NaeExit = RdtscpExit;
+    break;
+
   case SVM_EXIT_WBINVD:
     NaeExit = WbinvdExit;
     break;
-- 
2.17.1


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

* [PATCH v8 24/46] OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (22 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 23/46] OvmfPkg/VmgExitLib: Add support for RDTSCP " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:55   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 25/46] OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX " Lendacky, Thomas
                   ` (22 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a MONITOR/MONITORX intercept generates a #VC exception.
VMGEXIT must be used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 716f21a9ee0e..9b7639069276 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -843,6 +843,47 @@ MmioExit (
   return Status;
 }
 
+/**
+  Handle a MONITOR event.
+
+  Use the VMGEXIT instruction to handle a MONITOR event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+MonitorExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  DecodeModRm (Regs, InstructionData);
+
+  Ghcb->SaveArea.Rax = Regs->Rax;  // Identity mapped, so VA = PA
+  GhcbSetRegValid (Ghcb, GhcbRax);
+  Ghcb->SaveArea.Rcx = Regs->Rcx;
+  GhcbSetRegValid (Ghcb, GhcbRcx);
+  Ghcb->SaveArea.Rdx = Regs->Rdx;
+  GhcbSetRegValid (Ghcb, GhcbRdx);
+
+  Status = VmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  return 0;
+}
+
 /**
   Handle a WBINVD event.
 
@@ -1494,6 +1535,10 @@ VmgExitHandleVc (
     NaeExit = WbinvdExit;
     break;
 
+  case SVM_EXIT_MONITOR:
+    NaeExit = MonitorExit;
+    break;
+
   case SVM_EXIT_NPF:
     NaeExit = MmioExit;
     break;
-- 
2.17.1


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

* [PATCH v8 25/46] OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (23 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 24/46] OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:56   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write " Lendacky, Thomas
                   ` (21 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a MWAIT/MWAITX intercept generates a #VC exception.
VMGEXIT must be used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index 9b7639069276..b028b20f255a 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -843,6 +843,45 @@ MmioExit (
   return Status;
 }
 
+/**
+  Handle a MWAIT event.
+
+  Use the VMGEXIT instruction to handle a MWAIT event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+MwaitExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  UINT64  Status;
+
+  DecodeModRm (Regs, InstructionData);
+
+  Ghcb->SaveArea.Rax = Regs->Rax;
+  GhcbSetRegValid (Ghcb, GhcbRax);
+  Ghcb->SaveArea.Rcx = Regs->Rcx;
+  GhcbSetRegValid (Ghcb, GhcbRcx);
+
+  Status = VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  return 0;
+}
+
 /**
   Handle a MONITOR event.
 
@@ -1539,6 +1578,10 @@ VmgExitHandleVc (
     NaeExit = MonitorExit;
     break;
 
+  case SVM_EXIT_MWAIT:
+    NaeExit = MwaitExit;
+    break;
+
   case SVM_EXIT_NPF:
     NaeExit = MmioExit;
     break;
-- 
2.17.1


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

* [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (24 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 25/46] OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-22 14:59   ` [edk2-devel] " Laszlo Ersek
  2020-05-25 14:47   ` Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 27/46] OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function Lendacky, Thomas
                   ` (20 subsequent siblings)
  46 siblings, 2 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

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

Under SEV-ES, a DR7 read or write intercept generates a #VC exception.
The #VC handler must provide special support to the guest for this. On
a DR7 write, the #VC handler must cache the value and issue a VMGEXIT
to notify the hypervisor of the write. However, the #VC handler must
not actually set the value of the DR7 register. On a DR7 read, the #VC
handler must return the cached value of the DR7 register to the guest.
VMGEXIT is not invoked for a DR7 register read.

To avoid exception recursion, a #VC exception will not try to read and
push the actual debug registers into the EFI_SYSTEM_CONTEXT_X64 struct
and instead push zeroes. The #VC exception handler does not make use of
the debug registers from saved context.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 105 ++++++++++++++++++
 .../X64/ExceptionHandlerAsm.nasm              |  17 +++
 .../X64/Xcode5ExceptionHandlerAsm.nasm        |  17 +++
 3 files changed, 139 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
index b028b20f255a..e4072d79d704 100644
--- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
@@ -14,6 +14,16 @@
 
 #define CR4_OSXSAVE (1 << 18)
 
+#define DR7_RESET_VALUE 0x400
+
+//
+// Per-CPU data mapping structure
+//
+typedef struct {
+  BOOLEAN  Dr7Cached;
+  UINT64   Dr7;
+} SEV_ES_PER_CPU_DATA;
+
 //
 // Instruction execution mode definition
 //
@@ -1494,6 +1504,93 @@ RdtscExit (
   return 0;
 }
 
+/**
+  Handle a DR7 register write event.
+
+  Use the VMGEXIT instruction to handle a DR7 write event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+  @retval Others                   New exception value to propagate
+
+**/
+STATIC
+UINT64
+Dr7WriteExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
+  SEV_ES_PER_CPU_DATA            *SevEsData;
+  INTN                           *Register;
+  UINT64                         Status;
+
+  Ext = &InstructionData->Ext;
+  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
+
+  DecodeModRm (Regs, InstructionData);
+
+  /* MOV DRn always treats MOD == 3 no matter how encoded */
+  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+
+  /* Using a value of 0 for ExitInfo1 means RAX holds the value */
+  Ghcb->SaveArea.Rax = *Register;
+  GhcbSetRegValid (Ghcb, GhcbRax);
+
+  Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
+  if (Status) {
+    return Status;
+  }
+
+  SevEsData->Dr7 = *Register;
+  SevEsData->Dr7Cached = TRUE;
+
+  return 0;
+}
+
+/**
+  Handle a DR7 register read event.
+
+  Use the VMGEXIT instruction to handle a DR7 read event.
+
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
+                                   Block
+  @param[in, out] Regs             x64 processor context
+  @param[in]      InstructionData  Instruction parsing context
+
+  @retval 0                        Event handled successfully
+
+**/
+STATIC
+UINT64
+Dr7ReadExit (
+  IN OUT GHCB                     *Ghcb,
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
+  )
+{
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
+  SEV_ES_PER_CPU_DATA            *SevEsData;
+  INTN                           *Register;
+
+  Ext = &InstructionData->Ext;
+  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
+
+  DecodeModRm (Regs, InstructionData);
+
+  /* MOV DRn always treats MOD == 3 no matter how encoded */
+  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+  *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : DR7_RESET_VALUE;
+
+  return 0;
+}
+
 /**
   Handle a #VC exception.
 
@@ -1538,6 +1635,14 @@ VmgExitHandleVc (
 
   ExitCode = Regs->ExceptionData;
   switch (ExitCode) {
+  case SVM_EXIT_DR7_READ:
+    NaeExit = Dr7ReadExit;
+    break;
+
+  case SVM_EXIT_DR7_WRITE:
+    NaeExit = Dr7WriteExit;
+    break;
+
   case SVM_EXIT_RDTSC:
     NaeExit = RdtscExit;
     break;
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
index 3814f9de3703..2a5545ecfd41 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
@@ -18,6 +18,8 @@
 ; CommonExceptionHandler()
 ;
 
+%define VC_EXCEPTION 29
+
 extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
 extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
 extern ASM_PFX(CommonExceptionHandler)
@@ -224,6 +226,9 @@ HasErrorCode:
     push    rax
 
 ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
+    cmp     qword [rbp + 8], VC_EXCEPTION
+    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers ignored
+
     mov     rax, dr7
     push    rax
     mov     rax, dr6
@@ -236,7 +241,19 @@ HasErrorCode:
     push    rax
     mov     rax, dr0
     push    rax
+    jmp     DrFinish
 
+VcDebugRegs:
+;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion
+    xor     rax, rax
+    push    rax
+    push    rax
+    push    rax
+    push    rax
+    push    rax
+    push    rax
+
+DrFinish:
 ;; FX_SAVE_STATE_X64 FxSaveState;
     sub rsp, 512
     mov rdi, rsp
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
index 19198f273137..26cae56cc5cf 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
@@ -18,6 +18,8 @@
 ; CommonExceptionHandler()
 ;
 
+%define VC_EXCEPTION 29
+
 extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
 extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
 extern ASM_PFX(CommonExceptionHandler)
@@ -225,6 +227,9 @@ HasErrorCode:
     push    rax
 
 ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
+    cmp     qword [rbp + 8], VC_EXCEPTION
+    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers ignored
+
     mov     rax, dr7
     push    rax
     mov     rax, dr6
@@ -237,7 +242,19 @@ HasErrorCode:
     push    rax
     mov     rax, dr0
     push    rax
+    jmp     DrFinish
 
+VcDebugRegs:
+;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion
+    xor     rax, rax
+    push    rax
+    push    rax
+    push    rax
+    push    rax
+    push    rax
+    push    rax
+
+DrFinish:
 ;; FX_SAVE_STATE_X64 FxSaveState;
     sub rsp, 512
     mov rdi, rsp
-- 
2.17.1


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

* [PATCH v8 27/46] OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (25 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write " Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 28/46] OvmfPkg: Add support to perform SEV-ES initialization Lendacky, Thomas
                   ` (19 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

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

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/Include/Library/MemEncryptSevLib.h    | 12 +++
 .../MemEncryptSevLibInternal.c                | 75 ++++++++++++-------
 2 files changed, 60 insertions(+), 27 deletions(-)

diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h b/OvmfPkg/Include/Library/MemEncryptSevLib.h
index 64dd6977b0f8..a50a0de9c870 100644
--- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
+++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
@@ -13,6 +13,18 @@
 
 #include <Base.h>
 
+/**
+  Returns a boolean to indicate whether SEV-ES is enabled
+
+  @retval TRUE           SEV-ES is enabled
+  @retval FALSE          SEV-ES is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevEsIsEnabled (
+  VOID
+  );
+
 /**
   Returns a boolean to indicate whether SEV is enabled
 
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c
index 96a66e373f11..3301c5c2862f 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c
@@ -20,19 +20,17 @@
 #include <Uefi/UefiBaseType.h>
 
 STATIC BOOLEAN mSevStatus = FALSE;
+STATIC BOOLEAN mSevEsStatus = FALSE;
 STATIC BOOLEAN mSevStatusChecked = FALSE;
 
 /**
+  Reads and sets the status of SEV features
 
-  Returns a boolean to indicate whether SEV is enabled
-
-  @retval TRUE           SEV is enabled
-  @retval FALSE          SEV is not enabled
   **/
 STATIC
-BOOLEAN
+VOID
 EFIAPI
-InternalMemEncryptSevIsEnabled (
+InternalMemEncryptSevStatus (
   VOID
   )
 {
@@ -56,32 +54,55 @@ InternalMemEncryptSevIsEnabled (
       //
       Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
       if (Msr.Bits.SevBit) {
-        return TRUE;
+        mSevStatus = TRUE;
+      }
+
+      //
+      // Check MSR_0xC0010131 Bit 1 (Sev-Es Enabled)
+      //
+      if (Msr.Bits.SevEsBit) {
+        mSevEsStatus = TRUE;
       }
     }
   }
 
-  return FALSE;
-}
-
-/**
-  Returns a boolean to indicate whether SEV is enabled
-
-  @retval TRUE           SEV is enabled
-  @retval FALSE          SEV is not enabled
-**/
-BOOLEAN
-EFIAPI
-MemEncryptSevIsEnabled (
-  VOID
-  )
-{
-  if (mSevStatusChecked) {
-    return mSevStatus;
-  }
-
-  mSevStatus = InternalMemEncryptSevIsEnabled();
   mSevStatusChecked = TRUE;
+}
+
+/**
+  Returns a boolean to indicate whether SEV-ES is enabled
+
+  @retval TRUE           SEV-ES is enabled
+  @retval FALSE          SEV-ES is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevEsIsEnabled (
+  VOID
+  )
+{
+  if (!mSevStatusChecked) {
+    InternalMemEncryptSevStatus ();
+  }
+
+  return mSevEsStatus;
+}
+
+/**
+  Returns a boolean to indicate whether SEV is enabled
+
+  @retval TRUE           SEV is enabled
+  @retval FALSE          SEV is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevIsEnabled (
+  VOID
+  )
+{
+  if (!mSevStatusChecked) {
+    InternalMemEncryptSevStatus ();
+  }
 
   return mSevStatus;
 }
-- 
2.17.1


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

* [PATCH v8 28/46] OvmfPkg: Add support to perform SEV-ES initialization
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (26 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 27/46] OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase Lendacky, Thomas
                   ` (18 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

When SEV-ES is enabled, then SEV is also enabled. Add support to the SEV
initialization function to also check for SEV-ES being enabled, and if
enabled, set the SEV-ES enabled PCD (PcdSevEsIsEnabled).

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/OvmfPkgIa32.dsc             |  3 +++
 OvmfPkg/OvmfPkgIa32X64.dsc          |  3 +++
 OvmfPkg/OvmfPkgX64.dsc              |  3 +++
 OvmfPkg/PlatformPei/PlatformPei.inf |  1 +
 OvmfPkg/PlatformPei/AmdSev.c        | 26 ++++++++++++++++++++++++++
 5 files changed, 36 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 7d7297aa1cd8..b289260c1f2d 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -610,6 +610,9 @@ [PcdsDynamicDefault]
   # Set memory encryption mask
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
 
+  # Set SEV-ES defaults
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
+
 !if $(SMM_REQUIRE) == TRUE
   gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes|8
   gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index c85e988372ec..00faf5036670 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -622,6 +622,9 @@ [PcdsDynamicDefault]
   # Set memory encryption mask
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
 
+  # Set SEV-ES defaults
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
+
 !if $(SMM_REQUIRE) == TRUE
   gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes|8
   gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index b5f3859420d0..58671828c5fe 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -620,6 +620,9 @@ [PcdsDynamicDefault]
   # Set memory encryption mask
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
 
+  # Set SEV-ES defaults
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
+
 !if $(SMM_REQUIRE) == TRUE
   gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes|8
   gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE
diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index ff397b3ee9d7..00feb96c9308 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -103,6 +103,7 @@ [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
   gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled
 
 [FixedPcd]
   gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index e484f4b311fe..4dc5340caa7a 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -21,6 +21,27 @@
 
 #include "Platform.h"
 
+/**
+
+  Initialize SEV-ES support if running as an SEV-ES guest.
+
+  **/
+STATIC
+VOID
+AmdSevEsInitialize (
+  VOID
+  )
+{
+  RETURN_STATUS     PcdStatus;
+
+  if (!MemEncryptSevEsIsEnabled ()) {
+    return;
+  }
+
+  PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
+  ASSERT_RETURN_ERROR (PcdStatus);
+}
+
 /**
 
   Function checks if SEV support is available, if present then it sets
@@ -103,4 +124,9 @@ AmdSevInitialize (
         );
     }
   }
+
+  //
+  // Check and perform SEV-ES initialization if required.
+  //
+  AmdSevEsInitialize ();
 }
-- 
2.17.1


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

* [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (27 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 28/46] OvmfPkg: Add support to perform SEV-ES initialization Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-25 15:07   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:50 ` [PATCH v8 30/46] OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported Lendacky, Thomas
                   ` (17 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

A GHCB page is needed during the Sec phase, so this new page must be
created. Since the #VC exception handler routines assume that a per-CPU
variable area is immediately after the GHCB, this per-CPU variable area
must also be created. Since the GHCB must be marked as an un-encrypted,
or shared, page, an additional pagetable page is required to break down
the 2MB region where the GHCB page lives into 4K pagetable entries.

Create a new entry in the OVMF memory layout for the new page table
page and for the SEC GHCB and per-CPU variable pages. After breaking down
the 2MB page, update the GHCB page table entry to remove the encryption
mask.

The GHCB page will be used by the SEC #VC exception handler. The #VC
exception handler will fill in the necessary fields of the GHCB and exit
to the hypervisor using the VMGEXIT instruction. The hypervisor then
accesses the GHCB in order to perform the requested function.

Two new fixed PCDs are needed to support the SEC GHCB page:
  - PcdOvmfSecGhcbBase  UINT64 value that is the base address of the
                        GHCB used during the SEC phase.
  - PcdOvmfSecGhcbSize  UINT64 value that is the size, in bytes, of the
                        GHCB area used during the SEC phase.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/OvmfPkg.dec                       |  9 +++
 OvmfPkg/OvmfPkgX64.fdf                    |  6 ++
 OvmfPkg/ResetVector/ResetVector.inf       |  5 ++
 OvmfPkg/ResetVector/Ia32/PageTables64.asm | 70 +++++++++++++++++++++++
 OvmfPkg/ResetVector/ResetVector.nasmb     | 17 ++++++
 5 files changed, 107 insertions(+)

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 65bb2bb0eb4c..02ad62ed9f43 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -281,6 +281,15 @@ [PcdsFixedAtBuild]
   ## Number of page frames to use for storing grant table entries.
   gUefiOvmfPkgTokenSpaceGuid.PcdXenGrantFrames|4|UINT32|0x33
 
+  ## Specify the extra page table needed to mark the GHCB as unencrypted.
+  #  The value should be a multiple of 4KB for each.
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|0x0|UINT32|0x3a
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize|0x0|UINT32|0x3b
+
+  ## The base address of the SEC GHCB page used by SEV-ES.
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|0|UINT32|0x3c
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize|0|UINT32|0x3d
+
 [PcdsDynamic, PcdsDynamicEx]
   gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index bfca1eff9e83..88b1e880e603 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -76,6 +76,12 @@ [FD.MEMFD]
 0x007000|0x001000
 gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
 
+0x008000|0x001000
+gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
+
+0x009000|0x002000
+gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
+
 0x010000|0x010000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
 
diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index b0ddfa5832a2..483fd90fe785 100644
--- a/OvmfPkg/ResetVector/ResetVector.inf
+++ b/OvmfPkg/ResetVector/ResetVector.inf
@@ -26,6 +26,7 @@ [Sources]
 [Packages]
   OvmfPkg/OvmfPkg.dec
   MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
 
 [BuildOptions]
@@ -33,5 +34,9 @@ [BuildOptions]
    *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
 
 [Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
index abad009f20f5..c3587a1b7814 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -21,6 +21,11 @@ BITS    32
 %define PAGE_2M_MBO            0x080
 %define PAGE_2M_PAT          0x01000
 
+%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
+                          PAGE_DIRTY + \
+                          PAGE_READ_WRITE + \
+                          PAGE_PRESENT)
+
 %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
                           PAGE_ACCESSED + \
                           PAGE_DIRTY + \
@@ -75,6 +80,37 @@ NoSev:
 SevExit:
     OneTimeCallRet CheckSevFeature
 
+; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
+; is enabled.
+;
+; Modified:  EAX, EBX, ECX
+;
+; If SEV-ES is enabled then EAX will be non-zero.
+; If SEV-ES is disabled then EAX will be zero.
+;
+CheckSevEsFeature:
+    xor       eax, eax
+
+    ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
+    ; mask.
+    test      edx, edx
+    jz        NoSevEs
+
+    ; Save current value of encryption mask
+    mov       ebx, edx
+
+    ; Check if SEV-ES is enabled
+    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
+    mov       ecx, 0xc0010131
+    rdmsr
+    and       eax, 2
+
+    ; Restore encryption mask
+    mov       edx, ebx
+
+NoSevEs:
+    OneTimeCallRet CheckSevEsFeature
+
 ;
 ; Modified:  EAX, EBX, ECX, EDX
 ;
@@ -139,6 +175,40 @@ pageTableEntriesLoop:
     mov     [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
     loop    pageTableEntriesLoop
 
+    OneTimeCall   CheckSevEsFeature
+    test    eax, eax
+    jz      SetCr3
+
+    ;
+    ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
+    ; This requires the 2MB page for this range be broken down into 512 4KB
+    ; pages.  All will be marked encrypted, except for the GHCB.
+    ;
+    mov     ecx, (GHCB_BASE >> 21)
+    mov     eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
+    mov     [ecx * 8 + PT_ADDR (0x2000)], eax
+
+    ;
+    ; Page Table Entries (512 * 4KB entries => 2MB)
+    ;
+    mov     ecx, 512
+pageTableEntries4kLoop:
+    mov     eax, ecx
+    dec     eax
+    shl     eax, 12
+    add     eax, GHCB_BASE & 0xFFE0_0000
+    add     eax, PAGE_4K_PDE_ATTR
+    mov     [ecx * 8 + GHCB_PT_ADDR - 8], eax
+    mov     [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
+    loop    pageTableEntries4kLoop
+
+    ;
+    ; Clear the encryption bit from the GHCB entry
+    ;
+    mov     ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
+    mov     [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
+
+SetCr3:
     ;
     ; Set CR3 now that the paging structures are available
     ;
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 75cfe16654b1..bfb77e439105 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -53,8 +53,25 @@
     %error "This implementation inherently depends on PcdOvmfSecPageTablesSize"
   %endif
 
+  %if (FixedPcdGet32 (PcdOvmfSecGhcbPageTableSize) != 0x1000)
+    %error "This implementation inherently depends on PcdOvmfSecGhcbPageTableSize"
+  %endif
+
+  %if (FixedPcdGet32 (PcdOvmfSecGhcbSize) != 0x2000)
+    %error "This implementation inherently depends on PcdOvmfSecGhcbSize"
+  %endif
+
+  %if ((FixedPcdGet32 (PcdOvmfSecGhcbBase) >> 21) != \
+       ((FixedPcdGet32 (PcdOvmfSecGhcbBase) + FixedPcdGet32 (PcdOvmfSecGhcbSize) - 1) >> 21))
+    %error "This implementation inherently depends on PcdOvmfSecGhcbBase not straddling a 2MB boundary"
+  %endif
+
   %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
 %include "Ia32/Flat32ToFlat64.asm"
+
+  %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
+  %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
+  %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
 %include "Ia32/PageTables64.asm"
 %endif
 
-- 
2.17.1


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

* [PATCH v8 30/46] OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (28 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-19 21:50 ` [PATCH v8 31/46] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase Lendacky, Thomas
                   ` (16 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Anthony Perard,
	Julien Grall

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

Protect the memory used by an SEV-ES guest when S3 is supported. This
includes the page table used to break down the 2MB page that contains
the GHCB so that it can be marked un-encrypted, as well as the GHCB
area.

Regarding the lifecycle of the GHCB-related memory areas:
  PcdOvmfSecGhcbPageTableBase
  PcdOvmfSecGhcbBase

(a) when and how it is initialized after first boot of the VM

  If SEV-ES is enabled, the GHCB-related areas are initialized during
  the SEC phase [OvmfPkg/ResetVector/Ia32/PageTables64.asm].

(b) how it is protected from memory allocations during DXE

  If S3 and SEV-ES are enabled, then InitializeRamRegions()
  [OvmfPkg/PlatformPei/MemDetect.c] protects the ranges with an AcpiNVS
  memory allocation HOB, in PEI.

  If S3 is disabled, then these ranges are not protected. DXE's own page
  tables are first built while still in PEI (see HandOffToDxeCore()
  [MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c]). Those tables are
  located in permanent PEI memory. After CR3 is switched over to them
  (which occurs before jumping to the DXE core entry point), we don't have
  to preserve PcdOvmfSecGhcbPageTableBase. PEI switches to GHCB pages in
  permanent PEI memory and DXE will use these PEI GHCB pages, so we don't
  have to preserve PcdOvmfSecGhcbBase.

(c) how it is protected from the OS

  If S3 is enabled, then (b) reserves it from the OS too.

  If S3 is disabled, then the range needs no protection.

(d) how it is accessed on the S3 resume path

  It is rewritten same as in (a), which is fine because (b) reserved it.

(e) how it is accessed on the warm reset path

  It is rewritten same as in (a).

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Anthony Perard <anthony.perard@citrix.com>
Cc: Julien Grall <julien.grall@arm.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/PlatformPei/PlatformPei.inf |  4 ++++
 OvmfPkg/PlatformPei/MemDetect.c     | 23 +++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index 00feb96c9308..a54d10ba90d5 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -75,6 +75,10 @@ [Pcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
   gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
diff --git a/OvmfPkg/PlatformPei/MemDetect.c b/OvmfPkg/PlatformPei/MemDetect.c
index 3b46ea431ade..6b5fee166b5d 100644
--- a/OvmfPkg/PlatformPei/MemDetect.c
+++ b/OvmfPkg/PlatformPei/MemDetect.c
@@ -27,6 +27,7 @@ Module Name:
 #include <Library/DebugLib.h>
 #include <Library/HobLib.h>
 #include <Library/IoLib.h>
+#include <Library/MemEncryptSevLib.h>
 #include <Library/PcdLib.h>
 #include <Library/PciLib.h>
 #include <Library/PeimEntryPoint.h>
@@ -866,6 +867,28 @@ InitializeRamRegions (
       (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),
       EfiACPIMemoryNVS
       );
+
+    if (MemEncryptSevEsIsEnabled ()) {
+      //
+      // If SEV-ES is enabled, reserve the GHCB-related memory area. This
+      // includes the extra page table used to break down the 2MB page
+      // mapping into 4KB page entries where the GHCB resides and the
+      // GHCB area itself.
+      //
+      // Since this memory range will be used by the Reset Vector on S3
+      // resume, it must be reserved as ACPI NVS.
+      //
+      BuildMemoryAllocationHob (
+        (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecGhcbPageTableBase),
+        (UINT64)(UINTN) PcdGet32 (PcdOvmfSecGhcbPageTableSize),
+        EfiACPIMemoryNVS
+        );
+      BuildMemoryAllocationHob (
+        (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecGhcbBase),
+        (UINT64)(UINTN) PcdGet32 (PcdOvmfSecGhcbSize),
+        EfiACPIMemoryNVS
+        );
+    }
 #endif
   }
 
-- 
2.17.1


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

* [PATCH v8 31/46] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (29 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 30/46] OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported Lendacky, Thomas
@ 2020-05-19 21:50 ` Lendacky, Thomas
  2020-05-25 15:21   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:51 ` [PATCH v8 32/46] OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled Lendacky, Thomas
                   ` (15 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:50 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

Allocate memory for the GHCB pages and the per-CPU variable pages during
SEV initialization for use during Pei and Dxe phases. The GHCB page(s)
must be shared pages, so clear the encryption mask from the current page
table entries. Upon successful allocation, set the GHCB PCDs (PcdGhcbBase
and PcdGhcbSize).

The per-CPU variable page needs to be unique per AP. Using the page after
the GHCB ensures that it is unique per AP. Only the GHCB page is marked as
shared, keeping the per-CPU variable page encyrpted. The same logic is
used in DXE using CreateIdentityMappingPageTables() before switching to
the DXE pagetables.

The GHCB pages (one per vCPU) will be used by the PEI and DXE #VC
exception handlers. The #VC exception handler will fill in the necessary
fields of the GHCB and exit to the hypervisor using the VMGEXIT
instruction. The hypervisor then accesses the GHCB associated with the
vCPU in order to perform the requested function.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/OvmfPkgIa32.dsc             |  2 ++
 OvmfPkg/OvmfPkgIa32X64.dsc          |  2 ++
 OvmfPkg/OvmfPkgX64.dsc              |  2 ++
 OvmfPkg/PlatformPei/PlatformPei.inf |  2 ++
 OvmfPkg/PlatformPei/AmdSev.c        | 45 ++++++++++++++++++++++++++++-
 5 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index b289260c1f2d..f849a7d698cd 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -611,6 +611,8 @@ [PcdsDynamicDefault]
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
 
   # Set SEV-ES defaults
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
 
 !if $(SMM_REQUIRE) == TRUE
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 00faf5036670..d1b4ffedc6af 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -623,6 +623,8 @@ [PcdsDynamicDefault]
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
 
   # Set SEV-ES defaults
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
 
 !if $(SMM_REQUIRE) == TRUE
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 58671828c5fe..ebc903d922cf 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -621,6 +621,8 @@ [PcdsDynamicDefault]
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
 
   # Set SEV-ES defaults
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
 
 !if $(SMM_REQUIRE) == TRUE
diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index a54d10ba90d5..4742e1bdf42b 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -102,6 +102,8 @@ [Pcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack
   gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize
   gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy
   gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
   gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index 4dc5340caa7a..4fd4534cabea 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -10,12 +10,15 @@
 // The package level header files this module uses
 //
 #include <IndustryStandard/Q35MchIch9.h>
+#include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
 #include <Library/HobLib.h>
 #include <Library/MemEncryptSevLib.h>
+#include <Library/MemoryAllocationLib.h>
 #include <Library/PcdLib.h>
 #include <PiPei.h>
 #include <Register/Amd/Cpuid.h>
+#include <Register/Amd/Msr.h>
 #include <Register/Cpuid.h>
 #include <Register/Intel/SmramSaveStateMap.h>
 
@@ -32,7 +35,10 @@ AmdSevEsInitialize (
   VOID
   )
 {
-  RETURN_STATUS     PcdStatus;
+  VOID              *GhcbBase;
+  PHYSICAL_ADDRESS  GhcbBasePa;
+  UINTN             GhcbPageCount, PageCount;
+  RETURN_STATUS     PcdStatus, DecryptStatus;
 
   if (!MemEncryptSevEsIsEnabled ()) {
     return;
@@ -40,6 +46,43 @@ AmdSevEsInitialize (
 
   PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
   ASSERT_RETURN_ERROR (PcdStatus);
+
+  //
+  // Allocate GHCB and per-CPU variable pages.
+  //
+  GhcbPageCount = mMaxCpuCount * 2;
+  GhcbBase = AllocatePages (GhcbPageCount);
+  ASSERT (GhcbBase != NULL);
+
+  GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN) GhcbBase;
+
+  //
+  // Each vCPU gets two consecutive pages, the first is the GHCB and the
+  // second is the per-CPU variable page. Loop through the allocation and
+  // only clear the encryption mask for the GHCB pages.
+  //
+  for (PageCount = 0; PageCount < GhcbPageCount; PageCount += 2) {
+    DecryptStatus = MemEncryptSevClearPageEncMask (
+      0,
+      GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),
+      1,
+      TRUE
+      );
+    ASSERT_RETURN_ERROR (DecryptStatus);
+  }
+
+  ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));
+
+  PcdStatus = PcdSet64S (PcdGhcbBase, GhcbBasePa);
+  ASSERT_RETURN_ERROR (PcdStatus);
+  PcdStatus = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));
+  ASSERT_RETURN_ERROR (PcdStatus);
+
+  DEBUG ((DEBUG_INFO,
+    "SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
+    (UINT64)GhcbPageCount, GhcbBase));
+
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
 }
 
 /**
-- 
2.17.1


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

* [PATCH v8 32/46] OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (30 preceding siblings ...)
  2020-05-19 21:50 ` [PATCH v8 31/46] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-19 21:51 ` [PATCH v8 33/46] UefiCpuPkg: Create an SEV-ES workarea PCD Lendacky, Thomas
                   ` (14 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

The SEV support will clear the C-bit from non-RAM areas.  The early GDT
lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT
will be read as un-encrypted even though it is encrypted. This will result
in a failure to be able to handle the exception.

Move the GDT into RAM so it can be accessed without error when running as
an SEV-ES guest.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/PlatformPei/AmdSev.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index 4fd4534cabea..a2b38c591236 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -39,6 +39,8 @@ AmdSevEsInitialize (
   PHYSICAL_ADDRESS  GhcbBasePa;
   UINTN             GhcbPageCount, PageCount;
   RETURN_STATUS     PcdStatus, DecryptStatus;
+  IA32_DESCRIPTOR   Gdtr;
+  VOID              *Gdt;
 
   if (!MemEncryptSevEsIsEnabled ()) {
     return;
@@ -83,6 +85,22 @@ AmdSevEsInitialize (
     (UINT64)GhcbPageCount, GhcbBase));
 
   AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
+
+  //
+  // The SEV support will clear the C-bit from non-RAM areas.  The early GDT
+  // lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT
+  // will be read as un-encrypted even though it was created before the C-bit
+  // was cleared (encrypted). This will result in a failure to be able to
+  // handle the exception.
+  //
+  AsmReadGdtr (&Gdtr);
+
+  Gdt = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN) Gdtr.Limit + 1));
+  ASSERT (Gdt != NULL);
+
+  CopyMem (Gdt, (VOID *) Gdtr.Base, Gdtr.Limit + 1);
+  Gdtr.Base = (UINTN) Gdt;
+  AsmWriteGdtr (&Gdtr);
 }
 
 /**
-- 
2.17.1


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

* [PATCH v8 33/46] UefiCpuPkg: Create an SEV-ES workarea PCD
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (31 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 32/46] OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-19 21:51 ` [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage Lendacky, Thomas
                   ` (13 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

Create an SEV-ES workarea PCD. This PCD will be used for BSP communication
during SEC and for AP startup during PEI and DXE phases, the latter is the
reason for creating it in the UefiCpuPkg.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiCpuPkg/UefiCpuPkg.dec | 8 ++++++++
 UefiCpuPkg/UefiCpuPkg.uni | 8 ++++++++
 2 files changed, 16 insertions(+)

diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index cb92f34b6f55..8c614f9b42bd 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -161,6 +161,14 @@ [PcdsFixedAtBuild]
   # @Prompt Specify the count of pre allocated SMM MP tokens per chunk.
   gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmMpTokenCountPerChunk|64|UINT32|0x30002002
 
+  ## Area of memory where the SEV-ES work area block lives.
+  # @Prompt Configure the SEV-ES work area base
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|0x0|UINT32|0x30002005
+
+  ## Size of teh area of memory where the SEV-ES work area block lives.
+  # @Prompt Configure the SEV-ES work area base
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize|0x0|UINT32|0x30002006
+
 [PcdsFixedAtBuild, PcdsPatchableInModule]
   ## This value is the CPU Local APIC base address, which aligns the address on a 4-KByte boundary.
   # @Prompt Configure base address of CPU Local APIC
diff --git a/UefiCpuPkg/UefiCpuPkg.uni b/UefiCpuPkg/UefiCpuPkg.uni
index f4a0c72f6293..219c1963bf08 100644
--- a/UefiCpuPkg/UefiCpuPkg.uni
+++ b/UefiCpuPkg/UefiCpuPkg.uni
@@ -281,3 +281,11 @@
 
 #string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_PROMPT  #language en-US "Specifies whether SEV-ES is enabled"
 #string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_HELP    #language en-US "Set to TRUE when running as an SEV-ES guest, FALSE otherwise."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaBase_PROMPT  #language en-US "Specify the address of the SEV-ES work area"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaBase_HELP    #language en-US "Specifies the address of the work area used by an SEV-ES guest."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaSize_PROMPT  #language en-US "Specify the size of the SEV-ES work area"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaSize_HELP    #language en-US "Specifies the size of the work area used by an SEV-ES guest."
-- 
2.17.1


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

* [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (32 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 33/46] UefiCpuPkg: Create an SEV-ES workarea PCD Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-25 16:00   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:51 ` [PATCH v8 35/46] OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported Lendacky, Thomas
                   ` (12 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

Reserve a fixed area of memory for SEV-ES use and set a fixed PCD,
PcdSevEsWorkAreaBase, to this value.

This area will be used by SEV-ES support for two purposes:
  1. Communicating the SEV-ES status during BSP boot to SEC:
     Using a byte of memory from the page, the BSP reset vector code can
     communicate the SEV-ES status to SEC for use before exception
     handling can be enabled in SEC. After SEC, this field is no longer
     valid and the standard way of determine if SEV-ES is active should
     be used.

  2. Establishing an area of memory for AP boot support:
     A hypervisor is not allowed to update an SEV-ES guest's register
     state, so when booting an SEV-ES guest AP, the hypervisor is not
     allowed to set the RIP to the guest requested value. Instead an
     SEV-ES AP must be re-directed from within the guest to the actual
     requested staring location as specified in the INIT-SIPI-SIPI
     sequence.

     Use this memory for reset vector code that can be programmed to have
     the AP jump to the desired RIP location after starting the AP. This
     is required for only the very first AP reset.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/OvmfPkgX64.fdf                    |  3 +++
 OvmfPkg/ResetVector/ResetVector.inf       |  1 +
 OvmfPkg/ResetVector/Ia32/PageTables64.asm | 11 +++++++++++
 OvmfPkg/ResetVector/ResetVector.nasmb     |  1 +
 4 files changed, 16 insertions(+)

diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 88b1e880e603..8836b30a0cef 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -82,6 +82,9 @@ [FD.MEMFD]
 0x009000|0x002000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
 
+0x00B000|0x001000
+gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
+
 0x010000|0x010000
 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
 
diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index 483fd90fe785..e94e1bfcce7e 100644
--- a/OvmfPkg/ResetVector/ResetVector.inf
+++ b/OvmfPkg/ResetVector/ResetVector.inf
@@ -34,6 +34,7 @@ [BuildOptions]
    *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
 
 [Pcd]
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
index c3587a1b7814..73a4eaadb1b6 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -89,6 +89,10 @@ SevExit:
 ; If SEV-ES is disabled then EAX will be zero.
 ;
 CheckSevEsFeature:
+    ; Initialize the first byte of the workarea to zero to communicate to
+    ; the SEC phase that SEV-ES is not enabled.
+    mov     byte[SEV_ES_WORK_AREA], 0
+
     xor       eax, eax
 
     ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
@@ -108,6 +112,13 @@ CheckSevEsFeature:
     ; Restore encryption mask
     mov       edx, ebx
 
+    test      eax, eax
+    jz        NoSevEs
+
+    ; Set the first byte of the workarea to one to communicate to the SEC
+    ; phase that SEV-ES is enabled.
+    mov       byte[SEV_ES_WORK_AREA], 1
+
 NoSevEs:
     OneTimeCallRet CheckSevEsFeature
 
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index bfb77e439105..2967617bfaa0 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -72,6 +72,7 @@
   %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
   %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
   %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
+  %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
 %include "Ia32/PageTables64.asm"
 %endif
 
-- 
2.17.1


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

* [PATCH v8 35/46] OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (33 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-26  7:53   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:51 ` [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check Lendacky, Thomas
                   ` (11 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel,
	Anthony Perard, Julien Grall

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

Protect the SEV-ES work area memory used by an SEV-ES guest.

Regarding the lifecycle of the SEV-ES memory area:
  PcdSevEsWorkArea

(a) when and how it is initialized after first boot of the VM

  If SEV-ES is enabled, the SEV-ES area is initialized during
  the SEC phase [OvmfPkg/ResetVector/Ia32/PageTables64.asm].

(b) how it is protected from memory allocations during DXE

  If SEV-ES is enabled, then InitializeRamRegions()
  [OvmfPkg/PlatformPei/MemDetect.c] protects the ranges with either
  an AcpiNVS (S3 enabled) or BootServicesData (S3 disabled) memory
  allocation HOB, in PEI.

(c) how it is protected from the OS

  If S3 is enabled, then (b) reserves it from the OS too.

  If S3 is disabled, then the range needs no protection.

(d) how it is accessed on the S3 resume path

  It is rewritten same as in (a), which is fine because (b) reserved it.

(e) how it is accessed on the warm reset path

  It is rewritten same as in (a).

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: Anthony Perard <anthony.perard@citrix.com>
Cc: Julien Grall <julien@xen.org>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/PlatformPei/PlatformPei.inf |  2 ++
 OvmfPkg/PlatformPei/MemDetect.c     | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index 4742e1bdf42b..c53be2f4925c 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -118,6 +118,8 @@ [FixedPcd]
   gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiReservedMemoryType
   gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode
   gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
 
 [FeaturePcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdCsmEnable
diff --git a/OvmfPkg/PlatformPei/MemDetect.c b/OvmfPkg/PlatformPei/MemDetect.c
index 6b5fee166b5d..ffbbef891a11 100644
--- a/OvmfPkg/PlatformPei/MemDetect.c
+++ b/OvmfPkg/PlatformPei/MemDetect.c
@@ -940,5 +940,25 @@ InitializeRamRegions (
           );
       }
     }
+
+#ifdef MDE_CPU_X64
+    if (MemEncryptSevEsIsEnabled ()) {
+      //
+      // If SEV-ES is enabled, reserve the SEV-ES work area.
+      //
+      // Since this memory range will be used by the Reset Vector on S3
+      // resume, it must be reserved as ACPI NVS.
+      //
+      // If S3 is unsupported, then various drivers might still write to the
+      // work area. We ought to prevent DXE from serving allocation requests
+      // such that they would overlap the work area.
+      //
+      BuildMemoryAllocationHob (
+        (EFI_PHYSICAL_ADDRESS)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaBase),
+        (UINT64)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaSize),
+        mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
+        );
+    }
+#endif
   }
 }
-- 
2.17.1


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

* [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (34 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 35/46] OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-25 16:50   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:51 ` [PATCH v8 37/46] OvmfPkg/Sec: Add #VC exception handling for Sec phase Lendacky, Thomas
                   ` (10 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

During BSP startup, the reset vector code will issue a CPUID instruction
while in 32-bit mode. When running as an SEV-ES guest, this will trigger
a #VC exception.

Add exception handling support to the early reset vector code to catch
these exceptions.  Also, since the guest is in 32-bit mode at this point,
writes to the GHCB will be encrypted and thus not able to be read by the
hypervisor, so use the GHCB CPUID request/response protocol to obtain the
requested CPUID function values and provide these to the guest.

The exception handling support is active during the SEV check and uses the
OVMF temporary RAM space for a stack. After the SEV check is complete, the
exception handling support is removed and the stack pointer cleared.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/ResetVector/ResetVector.inf       |   2 +
 OvmfPkg/ResetVector/Ia32/PageTables64.asm | 329 +++++++++++++++++++---
 OvmfPkg/ResetVector/ResetVector.nasmb     |   1 +
 3 files changed, 294 insertions(+), 38 deletions(-)

diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index e94e1bfcce7e..a53ae6c194ae 100644
--- a/OvmfPkg/ResetVector/ResetVector.inf
+++ b/OvmfPkg/ResetVector/ResetVector.inf
@@ -41,3 +41,5 @@ [Pcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
index 73a4eaadb1b6..8a24e7fd42f6 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -36,13 +36,56 @@ BITS    32
                        PAGE_READ_WRITE + \
                        PAGE_PRESENT)
 
-; Check if Secure Encrypted Virtualization (SEV) feature is enabled
 ;
-; If SEV is enabled then EAX will be at least 32
+; SEV-ES #VC exception handler support
+;
+; #VC handler local variable locations
+;
+%define VC_CPUID_RESULT_EAX         0
+%define VC_CPUID_RESULT_EBX         4
+%define VC_CPUID_RESULT_ECX         8
+%define VC_CPUID_RESULT_EDX        12
+%define VC_GHCB_MSR_EDX            16
+%define VC_GHCB_MSR_EAX            20
+%define VC_CPUID_REQUEST_REGISTER  24
+%define VC_CPUID_FUNCTION          28
+
+; #VC handler total local variable size
+;
+%define VC_VARIABLE_SIZE           32
+
+; #VC handler GHCB CPUID request/response protocol values
+;
+%define GHCB_CPUID_REQUEST          4
+%define GHCB_CPUID_RESPONSE         5
+%define GHCB_CPUID_REGISTER_SHIFT  30
+%define CPUID_INSN_LEN              2
+
+
+; Check if Secure Encrypted Virtualization (SEV) features are enabled
+;
+; Modified:  EAX, EBX, ECX, EDX, ESP
+;
+; If SEV is enabled then EAX will be at least 32.
 ; If SEV is disabled then EAX will be zero.
 ;
-CheckSevFeature:
+CheckSevFeatures:
+    ; Initialize the first byte of the workarea to zero to communicate to
+    ; the SEC phase that SEV-ES is not enabled.
+    mov     byte[SEV_ES_WORK_AREA], 0
+
+    ;
+    ; Set up exception handlers to check for SEV-ES
+    ;   Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
+    ;   stack usage)
+    ;   Establish exception handlers
+    ;
+    mov       esp, SEV_ES_VC_TOP_OF_STACK
+    mov       eax, ADDR_OF(Idtr)
+    lidt      [cs:eax]
+
     ; Check if we have a valid (0x8000_001F) CPUID leaf
+    ;   CPUID raises a #VC exception if running as an SEV-ES guest
     mov       eax, 0x80000000
     cpuid
 
@@ -53,8 +96,8 @@ CheckSevFeature:
     jl        NoSev
 
     ; Check for memory encryption feature:
-    ;  CPUID  Fn8000_001F[EAX] - Bit 1
-    ;
+    ; CPUID  Fn8000_001F[EAX] - Bit 1
+    ;   CPUID raises a #VC exception if running as an SEV-ES guest
     mov       eax,  0x8000001f
     cpuid
     bt        eax, 1
@@ -67,6 +110,16 @@ CheckSevFeature:
     bt        eax, 0
     jnc       NoSev
 
+    ; Check if SEV-ES is enabled
+    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
+    bt        eax, 1
+    jnc       NoSevEs
+
+    ; Set the first byte of the workarea to one to communicate to the SEC
+    ; phase that SEV-ES is enabled.
+    mov       byte[SEV_ES_WORK_AREA], 1
+
+NoSevEs:
     ; Get pte bit position to enable memory encryption
     ; CPUID Fn8000_001F[EBX] - Bits 5:0
     ;
@@ -78,56 +131,44 @@ NoSev:
     xor       eax, eax
 
 SevExit:
-    OneTimeCallRet CheckSevFeature
+    ;
+    ; Clear exception handlers and stack
+    ;
+    push      eax
+    mov       eax, ADDR_OF(IdtrClear)
+    lidt      [cs:eax]
+    pop       eax
+    mov       esp, 0
+
+    OneTimeCallRet CheckSevFeatures
 
 ; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
 ; is enabled.
 ;
-; Modified:  EAX, EBX, ECX
+; Modified:  EAX
 ;
 ; If SEV-ES is enabled then EAX will be non-zero.
 ; If SEV-ES is disabled then EAX will be zero.
 ;
-CheckSevEsFeature:
-    ; Initialize the first byte of the workarea to zero to communicate to
-    ; the SEC phase that SEV-ES is not enabled.
-    mov     byte[SEV_ES_WORK_AREA], 0
-
+IsSevEsEnabled:
     xor       eax, eax
 
-    ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
-    ; mask.
-    test      edx, edx
-    jz        NoSevEs
+    ; During CheckSevFeatures, the SEV_ES_WORK_AREA was set to 1 if
+    ; SEV-ES is enabled.
+    cmp       byte[SEV_ES_WORK_AREA], 0
+    jz        SevEsDisabled
 
-    ; Save current value of encryption mask
-    mov       ebx, edx
+    mov       eax, 1
 
-    ; Check if SEV-ES is enabled
-    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
-    mov       ecx, 0xc0010131
-    rdmsr
-    and       eax, 2
-
-    ; Restore encryption mask
-    mov       edx, ebx
-
-    test      eax, eax
-    jz        NoSevEs
-
-    ; Set the first byte of the workarea to one to communicate to the SEC
-    ; phase that SEV-ES is enabled.
-    mov       byte[SEV_ES_WORK_AREA], 1
-
-NoSevEs:
-    OneTimeCallRet CheckSevEsFeature
+SevEsDisabled:
+    OneTimeCallRet IsSevEsEnabled
 
 ;
 ; Modified:  EAX, EBX, ECX, EDX
 ;
 SetCr3ForPageTables64:
 
-    OneTimeCall   CheckSevFeature
+    OneTimeCall   CheckSevFeatures
     xor     edx, edx
     test    eax, eax
     jz      SevNotActive
@@ -186,7 +227,7 @@ pageTableEntriesLoop:
     mov     [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
     loop    pageTableEntriesLoop
 
-    OneTimeCall   CheckSevEsFeature
+    OneTimeCall   IsSevEsEnabled
     test    eax, eax
     jz      SetCr3
 
@@ -227,3 +268,215 @@ SetCr3:
     mov     cr3, eax
 
     OneTimeCallRet SetCr3ForPageTables64
+
+;
+; Start of #VC exception handling routines
+;
+
+SevEsIdtNotCpuid:
+    ;
+    ; Use VMGEXIT to request termination.
+    ;   1 - #VC was not for CPUID
+    ;
+    mov     eax, 1
+    jmp     SevEsIdtTerminate
+
+SevEsIdtNoCpuidResponse:
+    ;
+    ; Use VMGEXIT to request termination.
+    ;   2 - GHCB_CPUID_RESPONSE not received
+    ;
+    mov     eax, 2
+
+SevEsIdtTerminate:
+    ;
+    ; Use VMGEXIT to request termination. At this point the reason code is
+    ; located in EAX, so shift it left 16 bits to the proper location.
+    ;
+    ; EAX[11:0]  => 0x100 - request termination
+    ; EAX[15:12] => 0x1   - OVMF
+    ; EAX[23:16] => 0xXX  - REASON CODE
+    ;
+    shl     eax, 16
+    or      eax, 0x1100
+    xor     edx, edx
+    mov     ecx, 0xc0010130
+    wrmsr
+    ;
+    ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
+    ; mode, so work around this by temporarily switching to 64-bit mode.
+    ;
+BITS    64
+    rep     vmmcall
+BITS    32
+
+    ;
+    ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
+    ;
+SevEsIdtHlt:
+    hlt
+    jmp     SevEsIdtHlt
+    iret
+
+    ;
+    ; Total stack usage for the #VC handler is 44 bytes:
+    ;   - 12 bytes for the exception IRET (after popping error code)
+    ;   - 32 bytes for the local variables.
+    ;
+SevEsIdtVmmComm:
+    ;
+    ; If we're here, then we are an SEV-ES guest and this
+    ; was triggered by a CPUID instruction
+    ;
+
+    pop     ecx                     ; Error code
+    cmp     ecx, 0x72               ; Be sure it was CPUID
+    jne     SevEsIdtNotCpuid
+
+    ; Set up local variable room on the stack
+    ;   CPUID function         : + 28
+    ;   CPUID request register : + 24
+    ;   GHCB MSR (EAX)         : + 20
+    ;   GHCB MSR (EDX)         : + 16
+    ;   CPUID result (EDX)     : + 12
+    ;   CPUID result (ECX)     : + 8
+    ;   CPUID result (EBX)     : + 4
+    ;   CPUID result (EAX)     : + 0
+    sub     esp, VC_VARIABLE_SIZE
+
+    ; Save the CPUID function being requested
+    mov     [esp + VC_CPUID_FUNCTION], eax
+
+    ; The GHCB CPUID protocol uses the following mapping to request
+    ; a specific register:
+    ;   0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
+    ;
+    ; Set EAX as the first register to request. This will also be used as a
+    ; loop variable to request all register values (EAX to EDX).
+    xor     eax, eax
+    mov     [esp + VC_CPUID_REQUEST_REGISTER], eax
+
+    ; Save current GHCB MSR value
+    mov     ecx, 0xc0010130
+    rdmsr
+    mov     [esp + VC_GHCB_MSR_EAX], eax
+    mov     [esp + VC_GHCB_MSR_EDX], edx
+
+NextReg:
+    ;
+    ; Setup GHCB MSR
+    ;   GHCB_MSR[63:32] = CPUID function
+    ;   GHCB_MSR[31:30] = CPUID register
+    ;   GHCB_MSR[11:0]  = CPUID request protocol
+    ;
+    mov     eax, [esp + VC_CPUID_REQUEST_REGISTER]
+    cmp     eax, 4
+    jge     VmmDone
+
+    shl     eax, GHCB_CPUID_REGISTER_SHIFT
+    or      eax, GHCB_CPUID_REQUEST
+    mov     edx, [esp + VC_CPUID_FUNCTION]
+    mov     ecx, 0xc0010130
+    wrmsr
+
+    ;
+    ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
+    ; mode, so work around this by temporarily switching to 64-bit mode.
+    ;
+BITS    64
+    rep     vmmcall
+BITS    32
+
+    ;
+    ; Read GHCB MSR
+    ;   GHCB_MSR[63:32] = CPUID register value
+    ;   GHCB_MSR[31:30] = CPUID register
+    ;   GHCB_MSR[11:0]  = CPUID response protocol
+    ;
+    mov     ecx, 0xc0010130
+    rdmsr
+    mov     ecx, eax
+    and     ecx, 0xfff
+    cmp     ecx, GHCB_CPUID_RESPONSE
+    jne     SevEsIdtNoCpuidResponse
+
+    ; Save returned value
+    shr     eax, GHCB_CPUID_REGISTER_SHIFT
+    mov     [esp + eax * 4], edx
+
+    ; Next register
+    inc     word [esp + VC_CPUID_REQUEST_REGISTER]
+
+    jmp     NextReg
+
+VmmDone:
+    ;
+    ; At this point we have all CPUID register values. Restore the GHCB MSR,
+    ; set the return register values and return.
+    ;
+    mov     eax, [esp + VC_GHCB_MSR_EAX]
+    mov     edx, [esp + VC_GHCB_MSR_EDX]
+    mov     ecx, 0xc0010130
+    wrmsr
+
+    mov     eax, [esp + VC_CPUID_RESULT_EAX]
+    mov     ebx, [esp + VC_CPUID_RESULT_EBX]
+    mov     ecx, [esp + VC_CPUID_RESULT_ECX]
+    mov     edx, [esp + VC_CPUID_RESULT_EDX]
+
+    add     esp, VC_VARIABLE_SIZE
+
+    ; Update the EIP value to skip over the now handled CPUID instruction
+    ; (the CPUID instruction has a length of 2)
+    add     word [esp], CPUID_INSN_LEN
+    iret
+
+ALIGN   2
+
+Idtr:
+    dw      IDT_END - IDT_BASE - 1  ; Limit
+    dd      ADDR_OF(IDT_BASE)       ; Base
+
+IdtrClear:
+    dw      0                       ; Limit
+    dd      0                       ; Base
+
+ALIGN   16
+
+;
+; The Interrupt Descriptor Table (IDT)
+;   This will be used to determine if SEV-ES is enabled.  Upon execution
+;   of the CPUID instruction, a VMM Communication Exception will occur.
+;   This will tell us if SEV-ES is enabled.  We can use the current value
+;   of the GHCB MSR to determine the SEV attributes.
+;
+IDT_BASE:
+;
+; Vectors 0 - 28 (No handlers)
+;
+%rep 29
+    dw      0                                    ; Offset low bits 15..0
+    dw      0x10                                 ; Selector
+    db      0                                    ; Reserved
+    db      0x8E                                 ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+    dw      0                                    ; Offset high bits 31..16
+%endrep
+;
+; Vector 29 (VMM Communication Exception)
+;
+    dw      (ADDR_OF(SevEsIdtVmmComm) & 0xffff)  ; Offset low bits 15..0
+    dw      0x10                                 ; Selector
+    db      0                                    ; Reserved
+    db      0x8E                                 ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+    dw      (ADDR_OF(SevEsIdtVmmComm) >> 16)     ; Offset high bits 31..16
+;
+; Vectors 30 - 31 (No handlers)
+;
+%rep 2
+    dw      0                                    ; Offset low bits 15..0
+    dw      0x10                                 ; Selector
+    db      0                                    ; Reserved
+    db      0x8E                                 ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+    dw      0                                    ; Offset high bits 31..16
+%endrep
+IDT_END:
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 2967617bfaa0..762661115d50 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -73,6 +73,7 @@
   %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
   %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
   %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
+  %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
 %include "Ia32/PageTables64.asm"
 %endif
 
-- 
2.17.1


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

* [PATCH v8 37/46] OvmfPkg/Sec: Add #VC exception handling for Sec phase
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (35 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-26 13:58   ` [edk2-devel] " Laszlo Ersek
  2020-05-19 21:51 ` [PATCH v8 38/46] OvmfPkg/Sec: Enable cache early to speed up booting Lendacky, Thomas
                   ` (9 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

An SEV-ES guest will generate a #VC exception when it encounters a
non-automatic exit (NAE) event. It is expected that the #VC exception
handler will communicate with the hypervisor using the GHCB to handle
the NAE event.

NAE events can occur during the Sec phase, so initialize exception
handling early in the OVMF Sec support.

Before establishing the exception handling, validate that the supported
version of the SEV-ES protocol in OVMF is supported by the hypervisor.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/Sec/SecMain.inf |   4 +
 OvmfPkg/Sec/SecMain.c   | 181 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 172 insertions(+), 13 deletions(-)

diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf
index 63ba4cb555fb..7f78dcee2772 100644
--- a/OvmfPkg/Sec/SecMain.inf
+++ b/OvmfPkg/Sec/SecMain.inf
@@ -50,15 +50,19 @@ [LibraryClasses]
   PeCoffExtraActionLib
   ExtractGuidedSectionLib
   LocalApicLib
+  CpuExceptionHandlerLib
 
 [Ppis]
   gEfiTemporaryRamSupportPpiGuid                # PPI ALWAYS_PRODUCED
 
 [Pcd]
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index 6dea6e771a29..c2a35463dce4 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -24,6 +24,9 @@
 #include <Library/PeCoffExtraActionLib.h>
 #include <Library/ExtractGuidedSectionLib.h>
 #include <Library/LocalApicLib.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Register/Amd/Ghcb.h>
+#include <Register/Amd/Msr.h>
 
 #include <Ppi/TemporaryRamSupport.h>
 
@@ -34,6 +37,10 @@ typedef struct _SEC_IDT_TABLE {
   IA32_IDT_GATE_DESCRIPTOR  IdtTable[SEC_IDT_ENTRY_COUNT];
 } SEC_IDT_TABLE;
 
+typedef struct _SEC_SEV_ES_WORK_AREA {
+  UINT8  SevEsEnabled;
+} SEC_SEV_ES_WORK_AREA;
+
 VOID
 EFIAPI
 SecStartupPhase2 (
@@ -712,6 +719,120 @@ FindAndReportEntryPoints (
   return;
 }
 
+/**
+  Handle an SEV-ES/GHCB protocol check failure.
+
+  Notify the hypervisor using the VMGEXIT instruction that the SEV-ES 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 ();
+}
+
+/**
+  Validate the SEV-ES/GHCB protocol level.
+
+  Verify that the level of SEV-ES/GHCB protocol supported by the hypervisor
+  and the guest intersect. If they don't intersect, request termination.
+
+**/
+STATIC
+VOID
+SevEsProtocolCheck (
+  VOID
+  )
+{
+  MSR_SEV_ES_GHCB_REGISTER  Msr;
+  GHCB                      *Ghcb;
+
+  //
+  // Use the GHCB MSR Protocol to obtain the GHCB SEV-ES Information for
+  // protocol checking
+  //
+  Msr.GhcbPhysicalAddress = 0;
+  Msr.GhcbInfo.Function = GHCB_INFO_SEV_INFO_GET;
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+  AsmVmgExit ();
+
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+
+  if (Msr.GhcbInfo.Function != GHCB_INFO_SEV_INFO) {
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
+  }
+
+  if (Msr.GhcbProtocol.SevEsProtocolMin > Msr.GhcbProtocol.SevEsProtocolMax) {
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
+  }
+
+  if ((Msr.GhcbProtocol.SevEsProtocolMin > GHCB_VERSION_MAX) ||
+      (Msr.GhcbProtocol.SevEsProtocolMax < GHCB_VERSION_MIN)) {
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
+  }
+
+  //
+  // SEV-ES protocol checking succeeded, set the initial GHCB address
+  //
+  Msr.GhcbPhysicalAddress = FixedPcdGet32 (PcdOvmfSecGhcbBase);
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+  Ghcb = Msr.Ghcb;
+  SetMem (Ghcb, sizeof (*Ghcb), 0);
+
+  //
+  // Set the version to the maximum that can be supported
+  //
+  Ghcb->ProtocolVersion = MIN (Msr.GhcbProtocol.SevEsProtocolMax, GHCB_VERSION_MAX);
+  Ghcb->GhcbUsage = GHCB_STANDARD_USAGE;
+}
+
+/**
+  Determine if SEV-ES is active.
+
+  During early booting, SEV-ES support code will set a flag to indicate that
+  SEV-ES is enabled. Return the value of this flag as an indicator that SEV-ES
+  is enabled.
+
+  @retval TRUE   SEV-ES is enabled
+  @retval FALSE  SEV-ES is not enabled
+
+**/
+STATIC
+BOOLEAN
+SevEsIsEnabled (
+  VOID
+  )
+{
+  SEC_SEV_ES_WORK_AREA  *SevEsWorkArea;
+
+  SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *) FixedPcdGet32 (PcdSevEsWorkAreaBase);
+
+  return ((SevEsWorkArea != NULL) && (SevEsWorkArea->SevEsEnabled != 0));
+}
+
 VOID
 EFIAPI
 SecCoreStartupWithStack (
@@ -737,8 +858,55 @@ SecCoreStartupWithStack (
     Table[Index] = 0;
   }
 
+  //
+  // Initialize IDT - Since this is before library constructors are called,
+  // we use a loop rather than CopyMem.
+  //
+  IdtTableInStack.PeiService = NULL;
+  for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {
+    UINT8  *Src, *Dst;
+    UINTN  Byte;
+
+    Src = (UINT8 *) &mIdtEntryTemplate;
+    Dst = (UINT8 *) &IdtTableInStack.IdtTable[Index];
+    for (Byte = 0; Byte < sizeof (mIdtEntryTemplate); Byte++) {
+      Dst[Byte] = Src[Byte];
+    }
+  }
+
+  IdtDescriptor.Base  = (UINTN)&IdtTableInStack.IdtTable;
+  IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);
+
+  if (SevEsIsEnabled ()) {
+    SevEsProtocolCheck ();
+
+    //
+    // For SEV-ES guests, the exception handler is needed before calling
+    // ProcessLibraryConstructorList() because some of the library constructors
+    // perform some functions that result in #VC exceptions being generated.
+    //
+    // Due to this code executing before library constructors, *all* library
+    // API calls are theoretically interface contract violations. However,
+    // because this is SEC (executing in flash), those constructors cannot
+    // write variables with static storage duration anyway. Furthermore, only
+    // a small, restricted set of APIs, such as AsmWriteIdtr() and
+    // InitializeCpuExceptionHandlers(), are called, where we require that the
+    // underlying library not require constructors to have been invoked and
+    // that the library instance not trigger any #VC exceptions.
+    //
+    AsmWriteIdtr (&IdtDescriptor);
+    InitializeCpuExceptionHandlers (NULL);
+  }
+
   ProcessLibraryConstructorList (NULL, NULL);
 
+  if (!SevEsIsEnabled ()) {
+    //
+    // For non SEV-ES guests, just load the IDTR.
+    //
+    AsmWriteIdtr (&IdtDescriptor);
+  }
+
   DEBUG ((DEBUG_INFO,
     "SecCoreStartupWithStack(0x%x, 0x%x)\n",
     (UINT32)(UINTN)BootFv,
@@ -751,19 +919,6 @@ SecCoreStartupWithStack (
   //
   InitializeFloatingPointUnits ();
 
-  //
-  // Initialize IDT
-  //
-  IdtTableInStack.PeiService = NULL;
-  for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {
-    CopyMem (&IdtTableInStack.IdtTable[Index], &mIdtEntryTemplate, sizeof (mIdtEntryTemplate));
-  }
-
-  IdtDescriptor.Base  = (UINTN)&IdtTableInStack.IdtTable;
-  IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);
-
-  AsmWriteIdtr (&IdtDescriptor);
-
 #if defined (MDE_CPU_X64)
   //
   // ASSERT that the Page Tables were set by the reset vector code to
-- 
2.17.1


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

* [PATCH v8 38/46] OvmfPkg/Sec: Enable cache early to speed up booting
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (36 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 37/46] OvmfPkg/Sec: Add #VC exception handling for Sec phase Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-19 21:51 ` [PATCH v8 39/46] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES Lendacky, Thomas
                   ` (8 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

Currently, the OVMF code relies on the hypervisor to enable the cache
support on the processor in order to improve the boot speed. However,
with SEV-ES, the hypervisor is not allowed to change the CR0 register
to enable caching.

Update the OVMF Sec support to enable caching in order to improve the
boot speed when running as an SEV-ES guest.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/Sec/SecMain.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index c2a35463dce4..271a06348ed8 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -905,6 +905,13 @@ SecCoreStartupWithStack (
     // For non SEV-ES guests, just load the IDTR.
     //
     AsmWriteIdtr (&IdtDescriptor);
+  } else {
+    //
+    // Under SEV-ES, the hypervisor can't modify CR0 and so can't enable
+    // caching in order to speed up the boot. Enable caching early for
+    // an SEV-ES guest.
+    //
+    AsmEnableCache ();
   }
 
   DEBUG ((DEBUG_INFO,
-- 
2.17.1


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

* [PATCH v8 39/46] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (37 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 38/46] OvmfPkg/Sec: Enable cache early to speed up booting Lendacky, Thomas
@ 2020-05-19 21:51 ` Lendacky, Thomas
  2020-05-26 14:07   ` [edk2-devel] " Laszlo Ersek
  2020-05-20  4:46 ` [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (7 subsequent siblings)
  46 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-19 21:51 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh

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

The flash detection routine will attempt to determine how the flash
device behaves (e.g. ROM, RAM, Flash). But when SEV-ES is enabled and
the flash device behaves as a ROM device (meaning it is marked read-only
by the hypervisor), this check may result in an infinite nested page fault
because of the attempted write. Since the instruction cannot be emulated
when SEV-ES is enabled, the RIP is never advanced, resulting in repeated
nested page faults.

When SEV-ES is enabled, exit the flash detection early and assume that
the FD behaves as Flash. This will result in QemuFlashWrite() being called
to store EFI variables, which will also result in an infinite nested page
fault when the write is performed. In this case, update QemuFlashWrite()
to use the VMGEXIT MMIO write support to have the hypervisor perform the
write without having to emulate the instruction.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 .../FvbServicesRuntimeDxe.inf                 |  2 +
 .../QemuFlash.h                               | 13 ++++++
 .../QemuFlash.c                               | 23 +++++++++--
 .../QemuFlashDxe.c                            | 40 +++++++++++++++++++
 .../QemuFlashSmm.c                            | 16 ++++++++
 5 files changed, 91 insertions(+), 3 deletions(-)

diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
index 72cabba4357d..8bb2325157ea 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
@@ -38,6 +38,7 @@ [Sources]
 [Packages]
   MdePkg/MdePkg.dec
   MdeModulePkg/MdeModulePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
@@ -52,6 +53,7 @@ [LibraryClasses]
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiRuntimeLib
+  VmgExitLib
 
 [Guids]
   gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
index f1afabcbe6ae..219d0d6e83cf 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
@@ -89,5 +89,18 @@ QemuFlashBeforeProbe (
   IN  UINTN                   FdBlockCount
   );
 
+/**
+  Write to QEMU Flash
+
+  @param[in] Ptr    Pointer to the location to write.
+  @param[in] Value  The value to write.
+
+**/
+VOID
+QemuFlashPtrWrite (
+  IN        volatile UINT8    *Ptr,
+  IN        UINT8             Value
+  );
+
 #endif
 
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
index 1b0d6c053f1a..0d29bf701aca 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
@@ -9,6 +9,7 @@
 
 #include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
+#include <Library/MemEncryptSevLib.h>
 #include <Library/PcdLib.h>
 
 #include "QemuFlash.h"
@@ -80,6 +81,21 @@ QemuFlashDetected (
 
   DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
 
+  if (MemEncryptSevEsIsEnabled ()) {
+    //
+    // When SEV-ES is enabled, the check below can result in an infinite
+    // loop with respect to a nested page fault. When the memslot is mapped
+    // read-only, the nested page table entry is read-only. The check below
+    // will cause a nested page fault that cannot be emulated, causing
+    // the instruction to retried over and over. For SEV-ES, acknowledge that
+    // the FD appears as ROM and not as FLASH, but report FLASH anyway because
+    // FLASH behavior can be simulated using VMGEXIT.
+    //
+    DEBUG ((DEBUG_INFO,
+      "QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"));
+    return TRUE;
+  }
+
   OriginalUint8 = *Ptr;
   *Ptr = CLEAR_STATUS_CMD;
   ProbeUint8 = *Ptr;
@@ -181,8 +197,9 @@ QemuFlashWrite (
   //
   Ptr = QemuFlashPtr (Lba, Offset);
   for (Loop = 0; Loop < *NumBytes; Loop++) {
-    *Ptr = WRITE_BYTE_CMD;
-    *Ptr = Buffer[Loop];
+    QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);
+    QemuFlashPtrWrite (Ptr, Buffer[Loop]);
+
     Ptr++;
   }
 
@@ -190,7 +207,7 @@ QemuFlashWrite (
   // Restore flash to read mode
   //
   if (*NumBytes > 0) {
-    *(Ptr - 1) = READ_ARRAY_CMD;
+    QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);
   }
 
   return EFI_SUCCESS;
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
index 5aabe9d7b59c..565383ee26d2 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
@@ -10,6 +10,9 @@
 **/
 
 #include <Library/UefiRuntimeLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/VmgExitLib.h>
+#include <Register/Amd/Msr.h>
 
 #include "QemuFlash.h"
 
@@ -32,3 +35,40 @@ QemuFlashBeforeProbe (
   // Do nothing
   //
 }
+
+/**
+  Write to QEMU Flash
+
+  @param[in] Ptr    Pointer to the location to write.
+  @param[in] Value  The value to write.
+
+**/
+VOID
+QemuFlashPtrWrite (
+  IN        volatile UINT8    *Ptr,
+  IN        UINT8             Value
+  )
+{
+  if (MemEncryptSevEsIsEnabled ()) {
+    MSR_SEV_ES_GHCB_REGISTER  Msr;
+    GHCB                      *Ghcb;
+
+    Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+    Ghcb = Msr.Ghcb;
+
+    //
+    // Writing to flash is emulated by the hypervisor through the use of write
+    // protection. This won't work for an SEV-ES guest because the write won't
+    // be recognized as a true MMIO write, which would result in the required
+    // #VC exception. Instead, use the the VMGEXIT MMIO write support directly
+    // to perform the update.
+    //
+    VmgInit (Ghcb);
+    Ghcb->SharedBuffer[0] = Value;
+    Ghcb->SaveArea.SwScratch = (UINT64) (UINTN) Ghcb->SharedBuffer;
+    VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, (UINT64) (UINTN) Ptr, 1);
+    VmgDone (Ghcb);
+  } else {
+    *Ptr = Value;
+  }
+}
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
index 7eb426e03855..7eb80bfeffae 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
@@ -46,3 +46,19 @@ QemuFlashBeforeProbe (
              );
   ASSERT_EFI_ERROR (Status);
 }
+
+/**
+  Write to QEMU Flash
+
+  @param[in] Ptr    Pointer to the location to write.
+  @param[in] Value  The value to write.
+
+**/
+VOID
+QemuFlashPtrWrite (
+  IN        volatile UINT8    *Ptr,
+  IN        UINT8             Value
+  )
+{
+  *Ptr = Value;
+}
-- 
2.17.1


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

* Re: [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files
  2020-05-20 16:56 ` [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files Lendacky, Thomas
@ 2020-05-19 21:54   ` Brijesh Singh
  2020-05-26 14:12   ` [edk2-devel] " Laszlo Ersek
  1 sibling, 0 replies; 100+ messages in thread
From: Brijesh Singh @ 2020-05-19 21:54 UTC (permalink / raw)
  To: Tom Lendacky, devel
  Cc: brijesh.singh, Jordan Justen, Laszlo Ersek, Ard Biesheuvel,
	Michael D Kinney, Liming Gao, Eric Dong, Ray Ni, Andrew Fish,
	Leif Lindholm


On 5/19/20 4:51 PM, Tom Lendacky wrote:
> Register reviewers for the SEV-related files in OvmfPkg.
>
> Cc: Andrew Fish <afish@apple.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Leif Lindholm <leif@nuviainc.com>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Acked-By: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  Maintainers.txt | 10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/Maintainers.txt b/Maintainers.txt
> index 896ac5821fc6..76f336b7dcc4 100644
> --- a/Maintainers.txt
> +++ b/Maintainers.txt
> @@ -441,6 +441,16 @@ F: OvmfPkg/PvScsiDxe/
>  R: Liran Alon <liran.alon@oracle.com>
>  R: Nikita Leshenko <nikita.leshchenko@oracle.com>
>  
> +OvmfPkg: SEV-related modules
> +F: OvmfPkg/AmdSevDxe/
> +F: OvmfPkg/Include/Library/MemEncryptSevLib.h
> +F: OvmfPkg/IoMmuDxe/AmdSevIoMmu.*
> +F: OvmfPkg/Library/BaseMemEncryptSevLib/
> +F: OvmfPkg/Library/VmgExitLib
> +F: OvmfPkg/PlatformPei/AmdSev.c
> +R: Tom Lendacky <thomas.lendacky@amd.com>
> +R: Brijesh Singh <brijesh.singh@amd.com>
> +
>  PcAtChipsetPkg
>  F: PcAtChipsetPkg/
>  W: https://github.com/tianocore/tianocore.github.io/wiki/PcAtChipsetPkg

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

* Re: [PATCH v8 00/46] SEV-ES guest support
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (38 preceding siblings ...)
  2020-05-19 21:51 ` [PATCH v8 39/46] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES Lendacky, Thomas
@ 2020-05-20  4:46 ` Lendacky, Thomas
  2020-05-20 16:56 ` [PATCH v8 40/46] UefiCpuPkg: Add a 16-bit protected mode code segment descriptor Lendacky, Thomas
                   ` (6 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20  4:46 UTC (permalink / raw)
  To: devel
  Cc: Jordan Justen, Laszlo Ersek, Ard Biesheuvel, Michael D Kinney,
	Liming Gao, Eric Dong, Ray Ni, Brijesh Singh, Anthony Perard,
	Benjamin You, Dandan Bi, Guo Dong, Hao A Wu, Jian J Wang,
	Julien Grall, Maurice Ma

On 5/19/20 4:50 PM, Tom Lendacky wrote:
> This patch series provides support for running EDK2/OVMF under SEV-ES.

Over the next few days I'll work on the Wiki page that has been requested, 
as well as getting the feature added to the request plan page.

Thanks,
Tom

> 
> Secure Encrypted Virtualization - Encrypted State (SEV-ES) expands on the
> SEV support to protect the guest register state from the hypervisor. See
> "AMD64 Architecture Programmer's Manual Volume 2: System Programming",
> section "15.35 Encrypted State (SEV-ES)" [1].
> 
> In order to allow a hypervisor to perform functions on behalf of a guest,
> there is architectural support for notifying a guest's operating system
> when certain types of VMEXITs are about to occur. This allows the guest to
> selectively share information with the hypervisor to satisfy the requested
> function. The notification is performed using a new exception, the VMM
> Communication exception (#VC). The information is shared through the
> Guest-Hypervisor Communication Block (GHCB) using the VMGEXIT instruction.
> The GHCB format and the protocol for using it is documented in "SEV-ES
> Guest-Hypervisor Communication Block Standardization" [2].
> 
> The main areas of the EDK2 code that are updated to support SEV-ES are
> around the exception handling support and the AP boot support.
> 
> Exception support is required starting in Sec, continuing through Pei
> and into Dxe in order to handle #VC exceptions that are generated.  Each
> AP requires it's own GHCB page as well as a page to hold values specific
> to that AP.
> 
> AP booting poses some interesting challenges. The INIT-SIPI-SIPI sequence
> is typically used to boot the APs. However, the hypervisor is not allowed
> to update the guest registers. The GHCB document [2] talks about how SMP
> booting under SEV-ES is performed.
> 
> Since the GHCB page must be a shared (unencrypted) page, the processor
> must be running in long mode in order for the guest and hypervisor to
> communicate with each other. As a result, SEV-ES is only supported under
> the X64 architecture.
> 
> [1] https://www.amd.com/system/files/TechDocs/24593.pdf
> [2] https://developer.amd.com/wp-content/resources/56421.pdf
> 
> ---
> 
> These patches are based on commit:
> 7b6327ff03bb ("OvmfPkg/PlatformPei: increase memory type info defaults")
> 
> A version of the tree can be found at:
> https://github.com/AMDESE/ovmf/tree/sev-es-v16
> 
> Cc: Anthony Perard <anthony.perard@citrix.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Cc: Benjamin You <benjamin.you@intel.com>
> Cc: Dandan Bi <dandan.bi@intel.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Guo Dong <guo.dong@intel.com>
> Cc: Hao A Wu <hao.a.wu@intel.com>
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Julien Grall <julien.grall@xen.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Liming Gao <liming.gao@intel.com>
> Cc: Maurice Ma <maurice.ma@intel.com>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> 
> Changes since v7:
> - Reserve the SEV-ES workarea when S3 is enabled
> - Fix warnings issued by the Visual Studio compiler
> - Create a NULL VmgExitLib instance that is used for VMGEXIT
>    related operations as well as #VC handling. Then create the full
>    VmgExitLib support only in OvmfPkg - where it will be used. This
>    removes a bunch of implementation code from platforms that will
>    not be using the functionality.
> - Remove single use interfaces from the VmgExitLib (VmgMmioWrite
>    and VmgSetApJumpTable)
> 
> Changes since v6:
> - Add function comments to all functions, including local functions
> - Add function parameter direction to all functions (in/out)
> - Add support for MMIO MOVZX/MOVSX instructions
> - Ensure the per-CPU variable page remains encrypted
> - Coding-style fixes as identified by Ecc
> 
> Changes since v5:
> - Remove extraneous VmgExitLib usage
> - Miscellaneous changes to address feedback (coding style, etc.)
> 
> Changes since v4:
> - Move the SEV-ES protocol negotiation out of the SEC exception handler
>    and into the SecMain.c file. As a result:
>    - Move the SecGhcb related PCDs out of UefiCpuPkg and into OvmfPkg
>    - Combine SecAMDSevVcHandler.c and PeiDxeAMDSevVcHandler.c into a
>      single AMDSevVcHandler.c
> - Consolidate VmgExitLib usage into common LibraryClasses sections
> - Add documentation comments to the VmgExitLib functions
> 
> Changes since v3:
> - Remove the need for the MP library finalization routine. The AP
>    jump table address will be held by the hypervisor rather than
>    communicated via the GHCB MSR. This removes some fragility around
>    the UEFI to OS transition.
> - Rename the SEV-ES RIP reset area to SEV-ES workarea and use it to
>    communicate the SEV-ES status, so that SEC CPU exception handling is
>    only established for an SEV-ES guest.
> - Fix SMM build breakageAdd around QemuFlashPtrWrite().
> - Fix SMM build breakage by adding VC exception support the SMM CPU
>    exception handling.
> - Add memory fencing around the invocation of AsmVmgExit().
> - Clarify comments around the SEV-ES AP reset RIP values and usage.
> - Move some PCD definitions from MdeModulePkg to UefiCpuPkg.
> - Remove the 16-bit code selector definition from MdeModulePkg
> 
> Changes since v2:
> - Added a way to locate the SEV-ES fixed AP RIP address for starting
>    AP's to avoid updating the actual flash image (build time location
>    that is identified with a GUID value).
> - Create a VmgExit library to replace static inline functions.
> - Move some PCDs to the appropriate packages
> - Add support for writing to QEMU flash under SEV-ES
> - Add additional MMIO opcode support
> - Cleaned up the GHCB MSR CPUID protocol support
> 
> Changes since v1:
> - Patches reworked to be more specific to the component/area being updated
>    and order of definition/usage
> - Created a library for VMGEXIT-related functions to replace use of inline
>    functions
> - Allocation method for GDT changed from AllocatePool to AllocatePages
> - Early caching only enabled for SEV-ES guests
> - Ensure AP loop mode set to halt loop mode for SEV-ES guests
> - Reserved SEC GHCB-related memory areas when S3 is enabled
> 
> Tom Lendacky (46):
>    MdeModulePkg: Create PCDs to be used in support of SEV-ES
>    UefiCpuPkg: Create PCD to be used in support of SEV-ES
>    MdePkg: Add the MSR definition for the GHCB register
>    MdePkg: Add a structure definition for the GHCB
>    MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page tables
>    MdePkg/BaseLib: Add support for the XGETBV instruction
>    MdePkg/BaseLib: Add support for the VMGEXIT instruction
>    UefiCpuPkg: Implement library support for VMGEXIT
>    OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library
>    UefiPayloadPkg: Prepare UefiPayloadPkg to use the VmgExitLib library
>    UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception
>    OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF
>    OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events
>    OvmfPkg/VmgExitLib: Support string IO for IOIO_PROT NAE events
>    OvmfPkg/VmgExitLib: Add support for CPUID NAE events
>    OvmfPkg/VmgExitLib: Add support for MSR_PROT NAE events
>    OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO)
>    OvmfPkg/VmgExitLib: Add support for WBINVD NAE events
>    OvmfPkg/VmgExitLib: Add support for RDTSC NAE events
>    OvmfPkg/VmgExitLib: Add support for RDPMC NAE events
>    OvmfPkg/VmgExitLib: Add support for INVD NAE events
>    OvmfPkg/VmgExitLib: Add support for VMMCALL NAE events
>    OvmfPkg/VmgExitLib: Add support for RDTSCP NAE events
>    OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX NAE events
>    OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX NAE events
>    OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
>    OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function
>    OvmfPkg: Add support to perform SEV-ES initialization
>    OvmfPkg: Create a GHCB page for use during Sec phase
>    OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported
>    OvmfPkg: Create GHCB pages for use during Pei and Dxe phase
>    OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled
>    UefiCpuPkg: Create an SEV-ES workarea PCD
>    OvmfPkg: Reserve a page in memory for the SEV-ES usage
>    OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported
>    OvmfPkg/ResetVector: Add support for a 32-bit SEV check
>    OvmfPkg/Sec: Add #VC exception handling for Sec phase
>    OvmfPkg/Sec: Enable cache early to speed up booting
>    OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with
>      SEV-ES
>    UefiCpuPkg: Add a 16-bit protected mode code segment descriptor
>    UefiCpuPkg/MpInitLib: Add CPU MP data flag to indicate if SEV-ES is
>      enabled
>    UefiCpuPkg: Allow AP booting under SEV-ES
>    OvmfPkg: Use the SEV-ES work area for the SEV-ES AP reset vector
>    OvmfPkg: Move the GHCB allocations into reserved memory
>    UefiCpuPkg/MpInitLib: Prepare SEV-ES guest APs for OS use
>    Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files
> 
>   MdeModulePkg/MdeModulePkg.dec                 |    9 +
>   OvmfPkg/OvmfPkg.dec                           |    9 +
>   UefiCpuPkg/UefiCpuPkg.dec                     |   17 +
>   OvmfPkg/OvmfPkgIa32.dsc                       |    6 +
>   OvmfPkg/OvmfPkgIa32X64.dsc                    |    6 +
>   OvmfPkg/OvmfPkgX64.dsc                        |    6 +
>   OvmfPkg/OvmfXen.dsc                           |    1 +
>   UefiCpuPkg/UefiCpuPkg.dsc                     |    2 +
>   UefiPayloadPkg/UefiPayloadPkgIa32.dsc         |    2 +
>   UefiPayloadPkg/UefiPayloadPkgIa32X64.dsc      |    2 +
>   OvmfPkg/OvmfPkgX64.fdf                        |    9 +
>   MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf       |    2 +
>   MdePkg/Library/BaseLib/BaseLib.inf            |    4 +
>   OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |   36 +
>   OvmfPkg/PlatformPei/PlatformPei.inf           |    9 +
>   .../FvbServicesRuntimeDxe.inf                 |    2 +
>   OvmfPkg/ResetVector/ResetVector.inf           |    8 +
>   OvmfPkg/Sec/SecMain.inf                       |    4 +
>   .../DxeCpuExceptionHandlerLib.inf             |    1 +
>   .../PeiCpuExceptionHandlerLib.inf             |    1 +
>   .../SecPeiCpuExceptionHandlerLib.inf          |    1 +
>   .../SmmCpuExceptionHandlerLib.inf             |    1 +
>   .../Xcode5SecPeiCpuExceptionHandlerLib.inf    |    1 +
>   UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |    4 +
>   UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |    4 +
>   .../Library/VmgExitLibNull/VmgExitLibNull.inf |   27 +
>   .../Core/DxeIplPeim/X64/VirtualMemory.h       |   12 +-
>   MdePkg/Include/Library/BaseLib.h              |   31 +
>   MdePkg/Include/Register/Amd/Fam17Msr.h        |   42 +
>   MdePkg/Include/Register/Amd/Ghcb.h            |  134 ++
>   OvmfPkg/Include/Library/MemEncryptSevLib.h    |   12 +
>   .../QemuFlash.h                               |   13 +
>   UefiCpuPkg/CpuDxe/CpuGdt.h                    |    4 +-
>   UefiCpuPkg/Include/Library/VmgExitLib.h       |  103 +
>   UefiCpuPkg/Library/MpInitLib/MpLib.h          |   68 +-
>   .../Core/DxeIplPeim/Ia32/DxeLoadFunc.c        |    4 +-
>   .../Core/DxeIplPeim/X64/DxeLoadFunc.c         |   11 +-
>   .../Core/DxeIplPeim/X64/VirtualMemory.c       |   57 +-
>   MdePkg/Library/BaseLib/Ia32/GccInline.c       |   45 +
>   MdePkg/Library/BaseLib/X64/GccInline.c        |   47 +
>   .../MemEncryptSevLibInternal.c                |   75 +-
>   OvmfPkg/Library/VmgExitLib/VmgExitLib.c       |  155 ++
>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 1721 +++++++++++++++++
>   OvmfPkg/PlatformPei/AmdSev.c                  |   89 +
>   OvmfPkg/PlatformPei/MemDetect.c               |   43 +
>   .../QemuFlash.c                               |   23 +-
>   .../QemuFlashDxe.c                            |   40 +
>   .../QemuFlashSmm.c                            |   16 +
>   OvmfPkg/Sec/SecMain.c                         |  188 +-
>   UefiCpuPkg/CpuDxe/CpuGdt.c                    |    8 +-
>   .../CpuExceptionCommon.c                      |   10 +-
>   .../PeiDxeSmmCpuException.c                   |   20 +-
>   .../SecPeiCpuException.c                      |   19 +
>   UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  120 +-
>   UefiCpuPkg/Library/MpInitLib/MpLib.c          |  313 ++-
>   UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |   19 +
>   .../Library/VmgExitLibNull/VmgExitLibNull.c   |  121 ++
>   UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |    2 +-
>   Maintainers.txt                               |   10 +
>   MdeModulePkg/MdeModulePkg.uni                 |    8 +
>   MdePkg/Library/BaseLib/Ia32/VmgExit.nasm      |   37 +
>   MdePkg/Library/BaseLib/Ia32/XGetBv.nasm       |   31 +
>   MdePkg/Library/BaseLib/X64/VmgExit.nasm       |   32 +
>   MdePkg/Library/BaseLib/X64/XGetBv.nasm        |   34 +
>   OvmfPkg/Library/VmgExitLib/VmgExitLib.uni     |   15 +
>   OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm  |  100 +
>   OvmfPkg/ResetVector/Ia32/PageTables64.asm     |  348 +++-
>   OvmfPkg/ResetVector/ResetVector.nasmb         |   20 +
>   .../X64/ExceptionHandlerAsm.nasm              |   17 +
>   .../X64/Xcode5ExceptionHandlerAsm.nasm        |   17 +
>   UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |    2 +-
>   .../Library/MpInitLib/Ia32/MpFuncs.nasm       |   15 +
>   UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |    4 +-
>   UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm |  370 +++-
>   .../Library/VmgExitLibNull/VmgExitLibNull.uni |   15 +
>   .../ResetVector/Vtf0/Ia16/Real16ToFlat32.asm  |    9 +
>   UefiCpuPkg/UefiCpuPkg.uni                     |   11 +
>   77 files changed, 4730 insertions(+), 104 deletions(-)
>   create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
>   create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
>   create mode 100644 MdePkg/Include/Register/Amd/Ghcb.h
>   create mode 100644 UefiCpuPkg/Include/Library/VmgExitLib.h
>   create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.c
>   create mode 100644 OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>   create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
>   create mode 100644 MdePkg/Library/BaseLib/Ia32/VmgExit.nasm
>   create mode 100644 MdePkg/Library/BaseLib/Ia32/XGetBv.nasm
>   create mode 100644 MdePkg/Library/BaseLib/X64/VmgExit.nasm
>   create mode 100644 MdePkg/Library/BaseLib/X64/XGetBv.nasm
>   create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
>   create mode 100644 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
>   create mode 100644 UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni
> 

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

* [PATCH v8 40/46] UefiCpuPkg: Add a 16-bit protected mode code segment descriptor
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (39 preceding siblings ...)
  2020-05-20  4:46 ` [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
@ 2020-05-20 16:56 ` Lendacky, Thomas
  2020-05-20 16:56 ` [PATCH v8 41/46] UefiCpuPkg/MpInitLib: Add CPU MP data flag to indicate if SEV-ES is enabled Lendacky, Thomas
                   ` (5 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20 16:56 UTC (permalink / raw)
  To: devel

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

A hypervisor is not allowed to update an SEV-ES guests register state,
so when booting an SEV-ES guest AP, the hypervisor is not allowed to
set the RIP to the guest requested value. Instead, an SEV-ES AP must be
transition from 64-bit long mode to 16-bit real mode in response to an
INIT-SIPI-SIPI sequence. This requires a 16-bit code segment descriptor.
For PEI, create this descriptor in the reset vector GDT table. For DXE,
create this descriptor from the newly reserved entry at location 0x28.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiCpuPkg/CpuDxe/CpuGdt.h                          | 4 ++--
 UefiCpuPkg/CpuDxe/CpuGdt.c                          | 8 ++++----
 UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm | 9 +++++++++
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/UefiCpuPkg/CpuDxe/CpuGdt.h b/UefiCpuPkg/CpuDxe/CpuGdt.h
index 3a0210b2f172..1c94487cbee8 100644
--- a/UefiCpuPkg/CpuDxe/CpuGdt.h
+++ b/UefiCpuPkg/CpuDxe/CpuGdt.h
@@ -36,7 +36,7 @@ struct _GDT_ENTRIES {
   GDT_ENTRY LinearCode;
   GDT_ENTRY SysData;
   GDT_ENTRY SysCode;
-  GDT_ENTRY Spare4;
+  GDT_ENTRY SysCode16;
   GDT_ENTRY LinearData64;
   GDT_ENTRY LinearCode64;
   GDT_ENTRY Spare5;
@@ -49,7 +49,7 @@ struct _GDT_ENTRIES {
 #define LINEAR_CODE_SEL   OFFSET_OF (GDT_ENTRIES, LinearCode)
 #define SYS_DATA_SEL      OFFSET_OF (GDT_ENTRIES, SysData)
 #define SYS_CODE_SEL      OFFSET_OF (GDT_ENTRIES, SysCode)
-#define SPARE4_SEL        OFFSET_OF (GDT_ENTRIES, Spare4)
+#define SYS_CODE16_SEL    OFFSET_OF (GDT_ENTRIES, SysCode16)
 #define LINEAR_DATA64_SEL OFFSET_OF (GDT_ENTRIES, LinearData64)
 #define LINEAR_CODE64_SEL OFFSET_OF (GDT_ENTRIES, LinearCode64)
 #define SPARE5_SEL        OFFSET_OF (GDT_ENTRIES, Spare5)
diff --git a/UefiCpuPkg/CpuDxe/CpuGdt.c b/UefiCpuPkg/CpuDxe/CpuGdt.c
index 64efadeba601..a1ab543f2da5 100644
--- a/UefiCpuPkg/CpuDxe/CpuGdt.c
+++ b/UefiCpuPkg/CpuDxe/CpuGdt.c
@@ -70,14 +70,14 @@ STATIC GDT_ENTRIES GdtTemplate = {
     0x0,
   },
   //
-  // SPARE4_SEL
+  // SYS_CODE16_SEL
   //
   {
-    0x0,            // limit 15:0
+    0x0FFFF,        // limit 15:0
     0x0,            // base 15:0
     0x0,            // base 23:16
-    0x0,            // type
-    0x0,            // limit 19:16, flags
+    0x09A,          // present, ring 0, code, execute/read
+    0x08F,          // page-granular, 16-bit
     0x0,            // base 31:24
   },
   //
diff --git a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm
index ce4ebfffb688..0e79a3984b16 100644
--- a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm
+++ b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm
@@ -129,5 +129,14 @@ LINEAR_CODE64_SEL   equ $-GDT_BASE
     DB      0            ; base 31:24
 %endif
 
+; linear code segment descriptor
+LINEAR_CODE16_SEL     equ $-GDT_BASE
+    DW      0xffff       ; limit 15:0
+    DW      0            ; base 15:0
+    DB      0            ; base 23:16
+    DB      PRESENT_FLAG(1)|DPL(0)|SYSTEM_FLAG(1)|DESC_TYPE(CODE32_TYPE)
+    DB      GRANULARITY_FLAG(1)|DEFAULT_SIZE32(0)|CODE64_FLAG(0)|UPPER_LIMIT(0xf)
+    DB      0            ; base 31:24
+
 GDT_END:
 
-- 
2.17.1


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

* [PATCH v8 41/46] UefiCpuPkg/MpInitLib: Add CPU MP data flag to indicate if SEV-ES is enabled
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (40 preceding siblings ...)
  2020-05-20 16:56 ` [PATCH v8 40/46] UefiCpuPkg: Add a 16-bit protected mode code segment descriptor Lendacky, Thomas
@ 2020-05-20 16:56 ` Lendacky, Thomas
  2020-05-20 16:56 ` [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Lendacky, Thomas
                   ` (4 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20 16:56 UTC (permalink / raw)
  To: devel

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

When starting APs in an SMP configuration, the AP needs to know if it is
running as an SEV-ES guest in order to assign a GHCB page.

Add a field to the CPU_MP_DATA structure that will indicate if SEV-ES is
enabled. This new field is set during MP library initialization with the
PCD value PcdSevEsIsEnabled. This flag can then be used to determine if
SEV-ES is enabled.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf | 1 +
 UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf | 1 +
 UefiCpuPkg/Library/MpInitLib/MpLib.h          | 2 ++
 UefiCpuPkg/Library/MpInitLib/MpLib.c          | 1 +
 4 files changed, 5 insertions(+)

diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
index 9907f4157b09..583276595619 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
@@ -71,4 +71,5 @@ [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                           ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ## SOMETIMES_CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds  ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ## CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
index 89ee9a79d8c5..4b3d39fbf36c 100644
--- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
@@ -61,6 +61,7 @@ [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize         ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ## SOMETIMES_CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES
 
 [Ppis]
   gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h
index a8ca03efb8e3..5b46c295b6b2 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
@@ -276,6 +276,8 @@ struct _CPU_MP_DATA {
   // driver.
   //
   BOOLEAN                        WakeUpByInitSipiSipi;
+
+  BOOLEAN                        SevEsIsEnabled;
 };
 
 extern EFI_GUID mCpuInitMpLibHobGuid;
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index ab7a8ed6633a..a8b605f569bf 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -1727,6 +1727,7 @@ MpInitLibInitialize (
   CpuMpData->CpuData          = (CPU_AP_DATA *) (CpuMpData + 1);
   CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);
   InitializeSpinLock(&CpuMpData->MpLock);
+  CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
 
   //
   // Make sure no memory usage outside of the allocated buffer.
-- 
2.17.1


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

* [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (41 preceding siblings ...)
  2020-05-20 16:56 ` [PATCH v8 41/46] UefiCpuPkg/MpInitLib: Add CPU MP data flag to indicate if SEV-ES is enabled Lendacky, Thomas
@ 2020-05-20 16:56 ` Lendacky, Thomas
  2020-06-01  6:17   ` Dong, Eric
  2020-06-01  7:28   ` Dong, Eric
  2020-05-20 16:56 ` [PATCH v8 43/46] OvmfPkg: Use the SEV-ES work area for the SEV-ES AP reset vector Lendacky, Thomas
                   ` (3 subsequent siblings)
  46 siblings, 2 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20 16:56 UTC (permalink / raw)
  To: devel

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

Typically, an AP is booted using the INIT-SIPI-SIPI sequence. This
sequence is intercepted by the hypervisor, which sets the AP's registers
to the values requested by the sequence. At that point, the hypervisor can
start the AP, which will then begin execution at the appropriate location.

Under SEV-ES, AP booting presents some challenges since the hypervisor is
not allowed to alter the AP's register state. In this situation, we have
to distinguish between the AP's first boot and AP's subsequent boots.

First boot:
 Once the AP's register state has been defined (which is before the guest
 is first booted) it cannot be altered. Should the hypervisor attempt to
 alter the register state, the change would be detected by the hardware
 and the VMRUN instruction would fail. Given this, the first boot for the
 AP is required to begin execution with this initial register state, which
 is typically the reset vector. This prevents the BSP from directing the
 AP startup location through the INIT-SIPI-SIPI sequence.

 To work around this, the firmware will provide a build time reserved area
 that can be used as the initial IP value. The hypervisor can extract this
 location value by checking for the SEV-ES reset block GUID that must be
 located 48-bytes from the end of the firmware. The format of the SEV-ES
 reset block area is:

   0x00 - 0x01 - SEV-ES Reset IP
   0x02 - 0x03 - SEV-ES Reset CS Segment Base[31:16]
   0x04 - 0x05 - Size of the SEV-ES reset block
   0x06 - 0x15 - SEV-ES Reset Block GUID
                   (00f771de-1a7e-4fcb-890e-68c77e2fb44e)

   The total size is 22 bytes. Any expansion to this block must be done
   by adding new values before existing values.

 The hypervisor will use the IP and CS values obtained from the SEV-ES
 reset block to set as the AP's initial values. The CS Segment Base
 represents the upper 16 bits of the CS segment base and must be left
 shifted by 16 bits to form the complete CS segment base value.

 Before booting the AP for the first time, the BSP must initialize the
 SEV-ES reset area. This consists of programming a FAR JMP instruction
 to the contents of a memory location that is also located in the SEV-ES
 reset area. The BSP must program the IP and CS values for the FAR JMP
 based on values drived from the INIT-SIPI-SIPI sequence.

Subsequent boots:
 Again, the hypervisor cannot alter the AP register state, so a method is
 required to take the AP out of halt state and redirect it to the desired
 IP location. If it is determined that the AP is running in an SEV-ES
 guest, then instead of calling CpuSleep(), a VMGEXIT is issued with the
 AP Reset Hold exit code (0x80000004). The hypervisor will put the AP in
 a halt state, waiting for an INIT-SIPI-SIPI sequence. Once the sequence
 is recognized, the hypervisor will resume the AP. At this point the AP
 must transition from the current 64-bit long mode down to 16-bit real
 mode and begin executing at the derived location from the INIT-SIPI-SIPI
 sequence.

 Another change is around the area of obtaining the (x2)APIC ID during AP
 startup. During AP startup, the AP can't take a #VC exception before the
 AP has established a stack. However, the AP stack is set by using the
 (x2)APIC ID, which is obtained through CPUID instructions. A CPUID
 instruction will cause a #VC, so a different method must be used. The
 GHCB protocol supports a method to obtain CPUID information from the
 hypervisor through the GHCB MSR. This method does not require a stack,
 so it is used to obtain the necessary CPUID information to determine the
 (x2)APIC ID.

The new 16-bit protected mode GDT entry is used in order to transition
from 64-bit long mode down to 16-bit real mode.

A new assembler routine is created that takes the AP from 64-bit long mode
to 16-bit real mode.  This is located under 1MB in memory and transitions
from 64-bit long mode to 32-bit compatibility mode to 16-bit protected
mode and finally 16-bit real mode.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   3 +
 UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   3 +
 UefiCpuPkg/Library/MpInitLib/MpLib.h          |  60 ++++
 UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  70 +++-
 UefiCpuPkg/Library/MpInitLib/MpLib.c          | 312 +++++++++++++++++-
 UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |  19 ++
 UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |   2 +-
 UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |   2 +-
 .../Library/MpInitLib/Ia32/MpFuncs.nasm       |  15 +
 UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |   4 +-
 UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 239 ++++++++++++++
 11 files changed, 714 insertions(+), 15 deletions(-)

diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
index 583276595619..1771575c69c1 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
@@ -52,6 +52,7 @@ [LibraryClasses]
   DebugAgentLib
   SynchronizationLib
   PcdLib
+  VmgExitLib
 
 [Protocols]
   gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES
@@ -72,4 +73,6 @@ [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ## SOMETIMES_CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds  ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ## SOMETIMES_CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ## CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
index 4b3d39fbf36c..34abf25d43cd 100644
--- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
@@ -51,6 +51,7 @@ [LibraryClasses]
   SynchronizationLib
   PeiServicesLib
   PcdLib
+  VmgExitLib
 
 [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ## CONSUMES
@@ -62,6 +63,8 @@ [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ## SOMETIMES_CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ## CONSUMES
 
 [Ppis]
   gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h
index 5b46c295b6b2..f0cbb3763b5d 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
@@ -173,6 +173,11 @@ typedef struct {
   UINT8             *RelocateApLoopFuncAddress;
   UINTN             RelocateApLoopFuncSize;
   UINTN             ModeTransitionOffset;
+  UINTN             SwitchToRealSize;
+  UINTN             SwitchToRealOffset;
+  UINTN             SwitchToRealNoNxOffset;
+  UINTN             SwitchToRealPM16ModeOffset;
+  UINTN             SwitchToRealPM16ModeSize;
 } MP_ASSEMBLY_ADDRESS_MAP;
 
 typedef struct _CPU_MP_DATA  CPU_MP_DATA;
@@ -211,6 +216,8 @@ typedef struct {
   // Enable5LevelPaging indicates whether 5-level paging is enabled in long mode.
   //
   BOOLEAN               Enable5LevelPaging;
+  BOOLEAN               SevEsIsEnabled;
+  UINTN                 GhcbBase;
 } MP_CPU_EXCHANGE_INFO;
 
 #pragma pack()
@@ -257,6 +264,7 @@ struct _CPU_MP_DATA {
   UINT8                          ApLoopMode;
   UINT8                          ApTargetCState;
   UINT16                         PmCodeSegment;
+  UINT16                         Pm16CodeSegment;
   CPU_AP_DATA                    *CpuData;
   volatile MP_CPU_EXCHANGE_INFO  *MpCpuExchangeInfo;
 
@@ -278,8 +286,47 @@ struct _CPU_MP_DATA {
   BOOLEAN                        WakeUpByInitSipiSipi;
 
   BOOLEAN                        SevEsIsEnabled;
+  UINTN                          SevEsAPBuffer;
+  UINTN                          SevEsAPResetStackStart;
+  CPU_MP_DATA                    *NewCpuMpData;
+
+  UINT64                         GhcbBase;
 };
 
+#define AP_RESET_STACK_SIZE 64
+
+#pragma pack(1)
+
+typedef struct {
+  UINT8   InsnBuffer[8];
+  UINT16  Rip;
+  UINT16  Segment;
+} SEV_ES_AP_JMP_FAR;
+
+#pragma pack()
+
+/**
+  Assembly code to move an AP from long mode to real mode.
+
+  Move an AP from long mode to real mode in preparation to invoking
+  the reset vector.  This is used for SEV-ES guests where a hypervisor
+  is not allowed to set the CS and RIP to point to the reset vector.
+
+  @param[in]  BufferStart  The reset vector target.
+  @param[in]  Code16       16-bit protected mode code segment value.
+  @param[in]  Code32       32-bit protected mode code segment value.
+  @param[in]  StackStart   The start of a stack to be used for transitioning
+                           from long mode to real mode.
+**/
+typedef
+VOID
+(EFIAPI AP_RESET) (
+  IN UINTN    BufferStart,
+  IN UINT16   Code16,
+  IN UINT16   Code32,
+  IN UINTN    StackStart
+  );
+
 extern EFI_GUID mCpuInitMpLibHobGuid;
 
 /**
@@ -385,6 +432,19 @@ GetModeTransitionBuffer (
   IN UINTN                BufferSize
   );
 
+/**
+  Return the address of the SEV-ES AP jump table.
+
+  This buffer is required in order for an SEV-ES guest to transition from
+  UEFI into an OS.
+
+  @retval other   Return SEV-ES AP jump table buffer
+**/
+UINTN
+GetSevEsAPMemory (
+  VOID
+  );
+
 /**
   This function will be called by BSP to wakeup AP.
 
diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
index 8ccddf8e9f9c..19527300ff3a 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
@@ -12,6 +12,8 @@
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/DebugAgentLib.h>
 #include <Library/DxeServicesTableLib.h>
+#include <Register/Amd/Fam17Msr.h>
+#include <Register/Amd/Ghcb.h>
 
 #include <Protocol/Timer.h>
 
@@ -144,6 +146,39 @@ GetModeTransitionBuffer (
   return (UINTN)StartAddress;
 }
 
+/**
+  Return the address of the SEV-ES AP jump table.
+
+  This buffer is required in order for an SEV-ES guest to transition from
+  UEFI into an OS.
+
+  @retval other   Return SEV-ES AP jump table buffer
+**/
+UINTN
+GetSevEsAPMemory (
+  VOID
+  )
+{
+  EFI_STATUS            Status;
+  EFI_PHYSICAL_ADDRESS  StartAddress;
+
+  //
+  // Allocate 1 page for AP jump table page
+  //
+  StartAddress = BASE_4GB - 1;
+  Status = gBS->AllocatePages (
+                  AllocateMaxAddress,
+                  EfiReservedMemoryType,
+                  1,
+                  &StartAddress
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN) StartAddress));
+
+  return (UINTN) StartAddress;
+}
+
 /**
   Checks APs status and updates APs status if needed.
 
@@ -218,6 +253,38 @@ CheckApsStatus (
   }
 }
 
+/**
+  Get Protected mode code segment with 16-bit default addressing
+  from current GDT table.
+
+  @return  Protected mode 16-bit code segment value.
+**/
+UINT16
+GetProtectedMode16CS (
+  VOID
+  )
+{
+  IA32_DESCRIPTOR          GdtrDesc;
+  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
+  UINTN                    GdtEntryCount;
+  UINT16                   Index;
+
+  Index = (UINT16) -1;
+  AsmReadGdtr (&GdtrDesc);
+  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
+  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
+  for (Index = 0; Index < GdtEntryCount; Index++) {
+    if (GdtEntry->Bits.L == 0) {
+      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) {
+        break;
+      }
+    }
+    GdtEntry++;
+  }
+  ASSERT (Index != GdtEntryCount);
+  return Index * 8;
+}
+
 /**
   Get Protected mode code segment from current GDT table.
 
@@ -238,7 +305,7 @@ GetProtectedModeCS (
   GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
   for (Index = 0; Index < GdtEntryCount; Index++) {
     if (GdtEntry->Bits.L == 0) {
-      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
+      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
         break;
       }
     }
@@ -300,6 +367,7 @@ MpInitChangeApLoopCallback (
 
   CpuMpData = GetCpuMpData ();
   CpuMpData->PmCodeSegment = GetProtectedModeCS ();
+  CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();
   CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
   mNumberToFinish = CpuMpData->CpuCount - 1;
   WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE);
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index a8b605f569bf..aeab575bb525 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -9,6 +9,9 @@
 **/
 
 #include "MpLib.h"
+#include <Library/VmgExitLib.h>
+#include <Register/Amd/Fam17Msr.h>
+#include <Register/Amd/Ghcb.h>
 
 EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
 
@@ -314,6 +317,14 @@ GetApLoopMode (
       //
       ApLoopMode = ApInHltLoop;
     }
+
+    if (PcdGetBool (PcdSevEsIsEnabled)) {
+      //
+      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB
+      // protocol for starting APs
+      //
+      ApLoopMode = ApInHltLoop;
+    }
   }
 
   if (ApLoopMode != ApInMwaitLoop) {
@@ -610,6 +621,112 @@ InitializeApData (
   SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);
 }
 
+/**
+  Get Protected mode code segment with 16-bit default addressing
+  from current GDT table.
+
+  @return  Protected mode 16-bit code segment value.
+**/
+STATIC
+UINT16
+GetProtectedMode16CS (
+  VOID
+  )
+{
+  IA32_DESCRIPTOR          GdtrDesc;
+  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
+  UINTN                    GdtEntryCount;
+  UINT16                   Index;
+
+  Index = (UINT16) -1;
+  AsmReadGdtr (&GdtrDesc);
+  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
+  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
+  for (Index = 0; Index < GdtEntryCount; Index++) {
+    if (GdtEntry->Bits.L == 0 &&
+        GdtEntry->Bits.DB == 0 &&
+        GdtEntry->Bits.Type > 8) {
+      break;
+    }
+    GdtEntry++;
+  }
+  ASSERT (Index != GdtEntryCount);
+  return Index * 8;
+}
+
+/**
+  Get Protected mode code segment with 32-bit default addressing
+  from current GDT table.
+
+  @return  Protected mode 32-bit code segment value.
+**/
+STATIC
+UINT16
+GetProtectedMode32CS (
+  VOID
+  )
+{
+  IA32_DESCRIPTOR          GdtrDesc;
+  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
+  UINTN                    GdtEntryCount;
+  UINT16                   Index;
+
+  Index = (UINT16) -1;
+  AsmReadGdtr (&GdtrDesc);
+  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
+  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
+  for (Index = 0; Index < GdtEntryCount; Index++) {
+    if (GdtEntry->Bits.L == 0 &&
+        GdtEntry->Bits.DB == 1 &&
+        GdtEntry->Bits.Type > 8) {
+      break;
+    }
+    GdtEntry++;
+  }
+  ASSERT (Index != GdtEntryCount);
+  return Index * 8;
+}
+
+/**
+  Reset an AP when in SEV-ES mode.
+
+  If successful, this function never returns.
+
+  @param[in] Ghcb                 Pointer to the GHCB
+  @param[in] CpuMpData            Pointer to CPU MP Data
+
+**/
+STATIC
+VOID
+MpInitLibSevEsAPReset (
+  IN GHCB                         *Ghcb,
+  IN CPU_MP_DATA                  *CpuMpData
+  )
+{
+  UINT16           Code16, Code32;
+  AP_RESET         *APResetFn;
+  UINTN            BufferStart;
+  UINTN            StackStart;
+
+  Code16 = GetProtectedMode16CS ();
+  Code32 = GetProtectedMode32CS ();
+
+  if (CpuMpData->WakeupBufferHigh != 0) {
+    APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
+  } else {
+    APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset);
+  }
+
+  BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
+  StackStart = CpuMpData->SevEsAPResetStackStart -
+                 (AP_RESET_STACK_SIZE * GetApicId ());
+
+  //
+  // This call never returns.
+  //
+  APResetFn (BufferStart, Code16, Code32, StackStart);
+}
+
 /**
   This function will be called from AP reset code if BSP uses WakeUpAP.
 
@@ -671,7 +788,14 @@ ApWakeupFunction (
       InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);
       ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;
 
-      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
+      //
+      // Delay decrementing the APs executing count when SEV-ES is enabled
+      // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly
+      // performs another INIT-SIPI-SIPI sequence.
+      //
+      if (!CpuMpData->SevEsIsEnabled) {
+        InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
+      }
     } else {
       //
       // Execute AP function if AP is ready
@@ -778,7 +902,52 @@ ApWakeupFunction (
       //
       while (TRUE) {
         DisableInterrupts ();
-        CpuSleep ();
+        if (CpuMpData->SevEsIsEnabled) {
+          MSR_SEV_ES_GHCB_REGISTER  Msr;
+          GHCB                      *Ghcb;
+          UINT64                    Status;
+          BOOLEAN                   DoDecrement;
+
+          if (CpuMpData->InitFlag == ApInitConfig) {
+            DoDecrement = TRUE;
+          }
+
+          while (TRUE) {
+            Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+            Ghcb = Msr.Ghcb;
+
+            VmgInit (Ghcb);
+
+            if (DoDecrement) {
+              DoDecrement = FALSE;
+
+              //
+              // Perform the delayed decrement just before issuing the first
+              // VMGEXIT with AP_RESET_HOLD.
+              //
+              InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
+            }
+
+            Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
+            if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
+              VmgDone (Ghcb);
+              break;
+            }
+
+            VmgDone (Ghcb);
+          }
+
+          //
+          // Awakened in a new phase? Use the new CpuMpData
+          //
+          if (CpuMpData->NewCpuMpData) {
+            CpuMpData = CpuMpData->NewCpuMpData;
+          }
+
+          MpInitLibSevEsAPReset (Ghcb, CpuMpData);
+        } else {
+          CpuSleep ();
+        }
         CpuPause ();
       }
     }
@@ -891,6 +1060,9 @@ FillExchangeInfoData (
   ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
   DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging));
 
+  ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;
+  ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;
+
   //
   // Get the BSP's data of GDT and IDT
   //
@@ -917,8 +1089,9 @@ FillExchangeInfoData (
   // EfiBootServicesCode to avoid page fault if NX memory protection is enabled.
   //
   if (CpuMpData->WakeupBufferHigh != 0) {
-    Size = CpuMpData->AddressMap.RendezvousFunnelSize -
-           CpuMpData->AddressMap.ModeTransitionOffset;
+    Size = CpuMpData->AddressMap.RendezvousFunnelSize +
+             CpuMpData->AddressMap.SwitchToRealSize -
+             CpuMpData->AddressMap.ModeTransitionOffset;
     CopyMem (
       (VOID *)CpuMpData->WakeupBufferHigh,
       CpuMpData->AddressMap.RendezvousFunnelAddress +
@@ -971,7 +1144,8 @@ BackupAndPrepareWakeupBuffer(
   CopyMem (
     (VOID *) CpuMpData->WakeupBuffer,
     (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
-    CpuMpData->AddressMap.RendezvousFunnelSize
+    CpuMpData->AddressMap.RendezvousFunnelSize +
+      CpuMpData->AddressMap.SwitchToRealSize
     );
 }
 
@@ -992,6 +1166,44 @@ RestoreWakeupBuffer(
     );
 }
 
+/**
+  Calculate the size of the reset stack.
+
+  @retval                 Total amount of memory required for stacks
+**/
+STATIC
+UINTN
+GetApResetStackSize (
+  VOID
+  )
+{
+  return AP_RESET_STACK_SIZE * PcdGet32(PcdCpuMaxLogicalProcessorNumber);
+}
+
+/**
+  Calculate the size of the reset vector.
+
+  @param[in]  AddressMap  The pointer to Address Map structure.
+
+  @retval                 Total amount of memory required for the AP reset area
+**/
+STATIC
+UINTN
+GetApResetVectorSize (
+  IN MP_ASSEMBLY_ADDRESS_MAP  *AddressMap
+  )
+{
+  UINTN  Size;
+
+  Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +
+                        AddressMap->SwitchToRealSize +
+                        sizeof (MP_CPU_EXCHANGE_INFO),
+                      CPU_STACK_ALIGNMENT);
+  Size += GetApResetStackSize ();
+
+  return Size;
+}
+
 /**
   Allocate reset vector buffer.
 
@@ -1005,16 +1217,22 @@ AllocateResetVector (
   UINTN           ApResetVectorSize;
 
   if (CpuMpData->WakeupBuffer == (UINTN) -1) {
-    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
-                          sizeof (MP_CPU_EXCHANGE_INFO);
+    ApResetVectorSize = GetApResetVectorSize (&CpuMpData->AddressMap);
 
     CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);
     CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)
-                    (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);
+                    (CpuMpData->WakeupBuffer +
+                       CpuMpData->AddressMap.RendezvousFunnelSize +
+                       CpuMpData->AddressMap.SwitchToRealSize);
     CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (
-                                    CpuMpData->AddressMap.RendezvousFunnelSize -
+                                    CpuMpData->AddressMap.RendezvousFunnelSize +
+                                    CpuMpData->AddressMap.SwitchToRealSize -
                                     CpuMpData->AddressMap.ModeTransitionOffset
                                     );
+    //
+    // The reset stack starts at the end of the buffer.
+    //
+    CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer + ApResetVectorSize;
   }
   BackupAndPrepareWakeupBuffer (CpuMpData);
 }
@@ -1029,7 +1247,31 @@ FreeResetVector (
   IN CPU_MP_DATA              *CpuMpData
   )
 {
-  RestoreWakeupBuffer (CpuMpData);
+  //
+  // If SEV-ES is enabled, the reset area is needed for AP parking and
+  // and AP startup in the OS, so the reset area is reserved. Do not
+  // perform the restore as this will overwrite memory which has data
+  // needed by SEV-ES.
+  //
+  if (!CpuMpData->SevEsIsEnabled) {
+    RestoreWakeupBuffer (CpuMpData);
+  }
+}
+
+/**
+  Allocate the SEV-ES AP jump table buffer.
+
+  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.
+**/
+VOID
+AllocateSevEsAPMemory (
+  IN OUT CPU_MP_DATA          *CpuMpData
+  )
+{
+  if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {
+    CpuMpData->SevEsAPBuffer =
+      CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
+  }
 }
 
 /**
@@ -1066,6 +1308,7 @@ WakeUpAP (
       CpuMpData->InitFlag   != ApInitDone) {
     ResetVectorRequired = TRUE;
     AllocateResetVector (CpuMpData);
+    AllocateSevEsAPMemory (CpuMpData);
     FillExchangeInfoData (CpuMpData);
     SaveLocalApicTimerSetting (CpuMpData);
   }
@@ -1102,6 +1345,50 @@ WakeUpAP (
       }
     }
     if (ResetVectorRequired) {
+      //
+      // For SEV-ES, the initial AP boot address will be defined by
+      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address
+      // from the original INIT-SIPI-SIPI.
+      //
+      if (CpuMpData->SevEsIsEnabled) {
+        SEV_ES_AP_JMP_FAR *JmpFar;
+        UINT32            Offset, InsnByte;
+        UINT8             LoNib, HiNib;
+
+        JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32 (PcdSevEsWorkAreaBase);
+        ASSERT (JmpFar != NULL);
+
+        //
+        // Obtain the address of the Segment/Rip location in the workarea.
+        // This will be set to a value derived from the SIPI vector and will
+        // be the memory address used for the far jump below.
+        //
+        Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
+        Offset += sizeof (JmpFar->InsnBuffer);
+        LoNib = (UINT8) Offset;
+        HiNib = (UINT8) (Offset >> 8);
+
+        //
+        // Program the workarea (which is the initial AP boot address) with
+        // far jump to the SIPI vector (where XX and YY represent the
+        // address of where the SIPI vector is stored.
+        //
+        //   JMP FAR [CS:XXYY] => 2E FF 2E YY XX
+        //
+        InsnByte = 0;
+        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // CS override prefix
+        JmpFar->InsnBuffer[InsnByte++] = 0xFF;  // JMP (FAR)
+        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // ModRM (JMP memory location)
+        JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
+        JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
+
+        //
+        // Program the Segment/Rip based on the SIPI vector (always at least
+        // 16-byte aligned, so Rip is set to 0).
+        //
+        JmpFar->Rip = 0;
+        JmpFar->Segment = (UINT16) (ExchangeInfo->BufferStart >> 4);
+      }
       //
       // Wakeup all APs
       //
@@ -1669,7 +1956,7 @@ MpInitLibInitialize (
   ASSERT (MaxLogicalProcessorNumber != 0);
 
   AsmGetAddressMap (&AddressMap);
-  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);
+  ApResetVectorSize = GetApResetVectorSize (&AddressMap);
   ApStackSize = PcdGet32(PcdCpuApStackSize);
   ApLoopMode  = GetApLoopMode (&MonitorFilterSize);
 
@@ -1728,6 +2015,8 @@ MpInitLibInitialize (
   CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);
   InitializeSpinLock(&CpuMpData->MpLock);
   CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
+  CpuMpData->SevEsAPBuffer  = (UINTN) -1;
+  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
 
   //
   // Make sure no memory usage outside of the allocated buffer.
@@ -1786,6 +2075,7 @@ MpInitLibInitialize (
     // APs have been wakeup before, just get the CPU Information
     // from HOB
     //
+    OldCpuMpData->NewCpuMpData = CpuMpData;
     CpuMpData->CpuCount  = OldCpuMpData->CpuCount;
     CpuMpData->BspNumber = OldCpuMpData->BspNumber;
     CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob;
diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
index a548fed23fa7..e17a351e5cfd 100644
--- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
@@ -280,6 +280,25 @@ GetModeTransitionBuffer (
   return 0;
 }
 
+/**
+  Return the address of the SEV-ES AP jump table.
+
+  This buffer is required in order for an SEV-ES guest to transition from
+  UEFI into an OS.
+
+  @retval other   Return SEV-ES AP jump table buffer
+**/
+UINTN
+GetSevEsAPMemory (
+  VOID
+  )
+{
+  //
+  // PEI phase doesn't need to do such transition. So simply return 0.
+  //
+  return 0;
+}
+
 /**
   Checks APs status and updates APs status if needed.
 
diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
index 6298571e29b2..28f8e8e133e5 100644
--- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
+++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
@@ -121,7 +121,7 @@ GetProtectedModeCS (
   GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
   for (Index = 0; Index < GdtEntryCount; Index++) {
     if (GdtEntry->Bits.L == 0) {
-      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
+      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
         break;
       }
     }
diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
index efb1bc2bf7cb..4f5a7c859a56 100644
--- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
+++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
@@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
 CPU_SWITCH_STATE_STORED       equ        1
 CPU_SWITCH_STATE_LOADED       equ        2
 
-LockLocation                  equ        (RendezvousFunnelProcEnd - RendezvousFunnelProcStart)
+LockLocation                  equ        (SwitchToRealProcEnd - RendezvousFunnelProcStart)
 StackStartAddressLocation     equ        LockLocation + 04h
 StackSizeLocation             equ        LockLocation + 08h
 ApProcedureLocation           equ        LockLocation + 0Ch
diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
index b74046b76af3..309d53bf3b37 100644
--- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
+++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
@@ -215,6 +215,16 @@ CProcedureInvoke:
     jmp        $                 ; Never reach here
 RendezvousFunnelProcEnd:
 
+;-------------------------------------------------------------------------------------
+;SwitchToRealProc procedure follows.
+;NOT USED IN 32 BIT MODE.
+;-------------------------------------------------------------------------------------
+global ASM_PFX(SwitchToRealProc)
+ASM_PFX(SwitchToRealProc):
+SwitchToRealProcStart:
+    jmp        $                 ; Never reach here
+SwitchToRealProcEnd:
+
 ;-------------------------------------------------------------------------------------
 ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);
 ;-------------------------------------------------------------------------------------
@@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
     mov        dword [ebx + 0Ch], AsmRelocateApLoopStart
     mov        dword [ebx + 10h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
     mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
+    mov        dword [ebx + 18h], SwitchToRealProcEnd - SwitchToRealProcStart       ; SwitchToRealSize
+    mov        dword [ebx + 1Ch], SwitchToRealProcStart - RendezvousFunnelProcStart ; SwitchToRealOffset
+    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start               ; SwitchToRealNoNxOffset
+    mov        dword [ebx + 24h], 0                                                 ; SwitchToRealPM16ModeOffset
+    mov        dword [ebx + 28h], 0                                                 ; SwitchToRealPM16ModeSize
 
     popad
     ret
diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
index 58ef369342a7..c92daaaffd6b 100644
--- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
+++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
@@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
 CPU_SWITCH_STATE_STORED       equ        1
 CPU_SWITCH_STATE_LOADED       equ        2
 
-LockLocation                  equ        (RendezvousFunnelProcEnd - RendezvousFunnelProcStart)
+LockLocation                  equ        (SwitchToRealProcEnd - RendezvousFunnelProcStart)
 StackStartAddressLocation     equ        LockLocation + 08h
 StackSizeLocation             equ        LockLocation + 10h
 ApProcedureLocation           equ        LockLocation + 18h
@@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ  LockLocation + 98h
 ModeHighMemoryLocation              equ  LockLocation + 9Ah
 ModeHighSegmentLocation             equ  LockLocation + 9Eh
 Enable5LevelPagingLocation          equ  LockLocation + 0A0h
+SevEsIsEnabledLocation              equ  LockLocation + 0A1h
+GhcbBaseLocation                    equ  LockLocation + 0A2h
diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
index 87f2523e856f..6956b408d004 100644
--- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
+++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
@@ -184,9 +184,97 @@ Releaselock:
     add        edi, StackStartAddressLocation
     add        rax, qword [edi]
     mov        rsp, rax
+
+    lea        edi, [esi + SevEsIsEnabledLocation]
+    cmp        byte [edi], 1        ; SevEsIsEnabled
+    jne        CProcedureInvoke
+
+    ;
+    ; program GHCB
+    ;   Each page after the GHCB is a per-CPU page, so the calculation programs
+    ;   a GHCB to be every 8KB.
+    ;
+    mov        eax, SIZE_4KB
+    shl        eax, 1                            ; EAX = SIZE_4K * 2
+    mov        ecx, ebx
+    mul        ecx                               ; EAX = SIZE_4K * 2 * CpuNumber
+    mov        edi, esi
+    add        edi, GhcbBaseLocation
+    add        rax, qword [edi]
+    mov        rdx, rax
+    shr        rdx, 32
+    mov        rcx, 0xc0010130
+    wrmsr
     jmp        CProcedureInvoke
 
 GetApicId:
+    lea        edi, [esi + SevEsIsEnabledLocation]
+    cmp        byte [edi], 1        ; SevEsIsEnabled
+    jne        DoCpuid
+
+    ;
+    ; Since we don't have a stack yet, we can't take a #VC
+    ; exception. Use the GHCB protocol to perform the CPUID
+    ; calls.
+    ;
+    mov        rcx, 0xc0010130
+    rdmsr
+    shl        rdx, 32
+    or         rax, rdx
+    mov        rdi, rax             ; RDI now holds the original GHCB GPA
+
+    mov        rdx, 0               ; CPUID function 0
+    mov        rax, 0               ; RAX register requested
+    or         rax, 4
+    wrmsr
+    rep vmmcall
+    rdmsr
+    cmp        edx, 0bh
+    jb         NoX2ApicSevEs        ; CPUID level below CPUID_EXTENDED_TOPOLOGY
+
+    mov        rdx, 0bh             ; CPUID function 0x0b
+    mov        rax, 040000000h      ; RBX register requested
+    or         rax, 4
+    wrmsr
+    rep vmmcall
+    rdmsr
+    test       edx, 0ffffh
+    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero
+
+    mov        rdx, 0bh             ; CPUID function 0x0b
+    mov        rax, 0c0000000h      ; RDX register requested
+    or         rax, 4
+    wrmsr
+    rep vmmcall
+    rdmsr
+
+    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
+    jmp        RestoreGhcb
+
+NoX2ApicSevEs:
+    ; Processor is not x2APIC capable, so get 8-bit APIC ID
+    mov        rdx, 1               ; CPUID function 1
+    mov        rax, 040000000h      ; RBX register requested
+    or         rax, 4
+    wrmsr
+    rep vmmcall
+    rdmsr
+    shr        edx, 24
+
+RestoreGhcb:
+    mov        rbx, rdx             ; Save x2APIC/APIC ID
+
+    mov        rdx, rdi             ; RDI holds the saved GHCB GPA
+    shr        rdx, 32
+    mov        eax, edi
+    wrmsr
+
+    mov        rdx, rbx
+
+    ; x2APIC ID or APIC ID is in EDX
+    jmp        GetProcessorNumber
+
+DoCpuid:
     mov        eax, 0
     cpuid
     cmp        eax, 0bh
@@ -253,12 +341,158 @@ CProcedureInvoke:
 
 RendezvousFunnelProcEnd:
 
+;-------------------------------------------------------------------------------------
+;SwitchToRealProc procedure follows.
+;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT MODE. HENCE THIS PROC
+;IS IN MACHINE CODE.
+;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, UINTN StackStart)
+;  rcx - Buffer Start
+;  rdx - Code16 Selector Offset
+;  r8  - Code32 Selector Offset
+;  r9  - Stack Start
+;-------------------------------------------------------------------------------------
+global ASM_PFX(SwitchToRealProc)
+ASM_PFX(SwitchToRealProc):
+SwitchToRealProcStart:
+BITS 64
+    cli
+
+    ;
+    ; Get RDX reset value before changing stacks since the
+    ; new stack won't be able to accomodate a #VC exception.
+    ;
+    push       rax
+    push       rbx
+    push       rcx
+    push       rdx
+
+    mov        rax, 1
+    cpuid
+    mov        rsi, rax                    ; Save off the reset value for RDX
+
+    pop        rdx
+    pop        rcx
+    pop        rbx
+    pop        rax
+
+    ;
+    ; Establish stack below 1MB
+    ;
+    mov        rsp, r9
+
+    ;
+    ; Push ultimate Reset Vector onto the stack
+    ;
+    mov        rax, rcx
+    shr        rax, 4
+    push       word 0x0002                 ; RFLAGS
+    push       ax                          ; CS
+    push       word 0x0000                 ; RIP
+    push       word 0x0000                 ; For alignment, will be discarded
+
+    ;
+    ; Get address of "16-bit operand size" label
+    ;
+    lea        rbx, [PM16Mode]
+
+    ;
+    ; Push addresses used to change to compatibility mode
+    ;
+    lea        rax, [CompatMode]
+    push       r8
+    push       rax
+
+    ;
+    ; Clear R8 - R15, for reset, before going into 32-bit mode
+    ;
+    xor        r8, r8
+    xor        r9, r9
+    xor        r10, r10
+    xor        r11, r11
+    xor        r12, r12
+    xor        r13, r13
+    xor        r14, r14
+    xor        r15, r15
+
+    ;
+    ; Far return into 32-bit mode
+    ;
+o64 retf
+
+BITS 32
+CompatMode:
+    ;
+    ; Set up stack to prepare for exiting protected mode
+    ;
+    push       edx                         ; Code16 CS
+    push       ebx                         ; PM16Mode label address
+
+    ;
+    ; Disable paging
+    ;
+    mov        eax, cr0                    ; Read CR0
+    btr        eax, 31                     ; Set PG=0
+    mov        cr0, eax                    ; Write CR0
+
+    ;
+    ; Disable long mode
+    ;
+    mov        ecx, 0c0000080h             ; EFER MSR number
+    rdmsr                                  ; Read EFER
+    btr        eax, 8                      ; Set LME=0
+    wrmsr                                  ; Write EFER
+
+    ;
+    ; Disable PAE
+    ;
+    mov        eax, cr4                    ; Read CR4
+    btr        eax, 5                      ; Set PAE=0
+    mov        cr4, eax                    ; Write CR4
+
+    mov        edx, esi                    ; Restore RDX reset value
+
+    ;
+    ; Switch to 16-bit operand size
+    ;
+    retf
+
+BITS 16
+    ;
+    ; At entry to this label
+    ;   - RDX will have its reset value
+    ;   - On the top of the stack
+    ;     - Alignment data (two bytes) to be discarded
+    ;     - IP for Real Mode (two bytes)
+    ;     - CS for Real Mode (two bytes)
+    ;
+PM16Mode:
+    mov        eax, cr0                    ; Read CR0
+    btr        eax, 0                      ; Set PE=0
+    mov        cr0, eax                    ; Write CR0
+
+    pop        ax                          ; Discard alignment data
+
+    ;
+    ; Clear registers (except RDX and RSP) before going into 16-bit mode
+    ;
+    xor        eax, eax
+    xor        ebx, ebx
+    xor        ecx, ecx
+    xor        esi, esi
+    xor        edi, edi
+    xor        ebp, ebp
+
+    iret
+
+SwitchToRealProcEnd:
+
 ;-------------------------------------------------------------------------------------
 ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);
 ;-------------------------------------------------------------------------------------
 global ASM_PFX(AsmRelocateApLoop)
 ASM_PFX(AsmRelocateApLoop):
 AsmRelocateApLoopStart:
+BITS 64
     cli                          ; Disable interrupt before switching to 32-bit mode
     mov        rax, [rsp + 40]   ; CountTofinish
     lock dec   dword [rax]       ; (*CountTofinish)--
@@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
     mov        qword [rcx + 18h], rax
     mov        qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
     mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
+    mov        qword [rcx + 30h], SwitchToRealProcEnd - SwitchToRealProcStart          ; SwitchToRealSize
+    mov        qword [rcx + 38h], SwitchToRealProcStart - RendezvousFunnelProcStart    ; SwitchToRealOffset
+    mov        qword [rcx + 40h], SwitchToRealProcStart - Flat32Start                  ; SwitchToRealNoNxOffset
+    mov        qword [rcx + 48h], PM16Mode - RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset
+    mov        qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode                       ; SwitchToRealPM16ModeSize
     ret
 
 ;-------------------------------------------------------------------------------------
-- 
2.17.1


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

* [PATCH v8 43/46] OvmfPkg: Use the SEV-ES work area for the SEV-ES AP reset vector
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (42 preceding siblings ...)
  2020-05-20 16:56 ` [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Lendacky, Thomas
@ 2020-05-20 16:56 ` Lendacky, Thomas
  2020-05-20 16:56 ` [PATCH v8 44/46] OvmfPkg: Move the GHCB allocations into reserved memory Lendacky, Thomas
                   ` (2 subsequent siblings)
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20 16:56 UTC (permalink / raw)
  To: devel

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

A hypervisor is not allowed to update an SEV-ES guest's register state,
so when booting an SEV-ES guest AP, the hypervisor is not allowed to
set the RIP to the guest requested value. Instead an SEV-ES AP must be
re-directed from within the guest to the actual requested staring location
as specified in the INIT-SIPI-SIPI sequence.

Use the SEV-ES work area for the reset vector code that contains support
to jump to the desired RIP location after having been started. This is
required for only the very first AP reset.

This new OVMF source file, ResetVectorVtf0.asm, is used in place of the
original file through the use of the include path order set in
OvmfPkg/ResetVector/ResetVector.inf under "[BuildOptions]".

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm | 100 +++++++++++++++++++
 OvmfPkg/ResetVector/ResetVector.nasmb        |   1 +
 2 files changed, 101 insertions(+)
 create mode 100644 OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm

diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
new file mode 100644
index 000000000000..980e0138e7fe
--- /dev/null
+++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
@@ -0,0 +1,100 @@
+;------------------------------------------------------------------------------
+; @file
+; First code executed by processor after resetting.
+; Derived from UefiCpuPkg/ResetVector/Vtf0/Ia16/ResetVectorVtf0.asm
+;
+; Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+BITS    16
+
+ALIGN   16
+
+;
+; Pad the image size to 4k when page tables are in VTF0
+;
+; If the VTF0 image has page tables built in, then we need to make
+; sure the end of VTF0 is 4k above where the page tables end.
+;
+; This is required so the page tables will be 4k aligned when VTF0 is
+; located just below 0x100000000 (4GB) in the firmware device.
+;
+%ifdef ALIGN_TOP_TO_4K_FOR_PAGING
+    TIMES (0x1000 - ($ - EndOfPageTables) - 0x20) DB 0
+%endif
+
+;
+; SEV-ES Processor Reset support
+;
+; sevEsResetBlock:
+;   For the initial boot of an AP under SEV-ES, the "reset" RIP must be
+;   programmed to the RAM area defined by SEV_ES_AP_RESET_IP. A known offset
+;   and GUID will be used to locate this block in the firmware and extract
+;   the build time RIP value. The GUID must always be 48 bytes from the
+;   end of the firmware.
+;
+;   0xffffffca (-0x36) - IP value
+;   0xffffffcc (-0x34) - CS segment base [31:16]
+;   0xffffffce (-0x32) - Size of the SEV-ES reset block
+;   0xffffffd0 (-0x30) - SEV-ES reset block GUID
+;                        (00f771de-1a7e-4fcb-890e-68c77e2fb44e)
+;
+;   A hypervisor reads the CS segement base and IP value. The CS segment base
+;   value represents the high order 16-bits of the CS segment base, so the
+;   hypervisor must left shift the value of the CS segement base by 16 bits to
+;   form the full CS segment base for the CS segment register. It would then
+;   program the EIP register with the IP value as read.
+;
+
+TIMES (32 - (sevEsResetBlockEnd - sevEsResetBlockStart)) DB 0
+
+sevEsResetBlockStart:
+    DD      SEV_ES_AP_RESET_IP
+    DW      sevEsResetBlockEnd - sevEsResetBlockStart
+    DB      0xDE, 0x71, 0xF7, 0x00, 0x7E, 0x1A, 0xCB, 0x4F
+    DB      0x89, 0x0E, 0x68, 0xC7, 0x7E, 0x2F, 0xB4, 0x4E
+sevEsResetBlockEnd:
+
+ALIGN   16
+
+applicationProcessorEntryPoint:
+;
+; Application Processors entry point
+;
+; GenFv generates code aligned on a 4k boundary which will jump to this
+; location.  (0xffffffe0)  This allows the Local APIC Startup IPI to be
+; used to wake up the application processors.
+;
+    jmp     EarlyApInitReal16
+
+ALIGN   8
+
+    DD      0
+
+;
+; The VTF signature
+;
+; VTF-0 means that the VTF (Volume Top File) code does not require
+; any fixups.
+;
+vtfSignature:
+    DB      'V', 'T', 'F', 0
+
+ALIGN   16
+
+resetVector:
+;
+; Reset Vector
+;
+; This is where the processor will begin execution
+;
+    nop
+    nop
+    jmp     EarlyBspInitReal16
+
+ALIGN   16
+
+fourGigabytes:
+
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 762661115d50..4913b379a993 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -82,5 +82,6 @@
 
 %include "Main.asm"
 
+  %define SEV_ES_AP_RESET_IP  FixedPcdGet32 (PcdSevEsWorkAreaBase)
 %include "Ia16/ResetVectorVtf0.asm"
 
-- 
2.17.1


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

* [PATCH v8 44/46] OvmfPkg: Move the GHCB allocations into reserved memory
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (43 preceding siblings ...)
  2020-05-20 16:56 ` [PATCH v8 43/46] OvmfPkg: Use the SEV-ES work area for the SEV-ES AP reset vector Lendacky, Thomas
@ 2020-05-20 16:56 ` Lendacky, Thomas
  2020-05-20 16:56 ` [PATCH v8 45/46] UefiCpuPkg/MpInitLib: Prepare SEV-ES guest APs for OS use Lendacky, Thomas
  2020-05-20 16:56 ` [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files Lendacky, Thomas
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20 16:56 UTC (permalink / raw)
  To: devel

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

After having transitioned from UEFI to the OS, the OS will need to boot
the APs. For an SEV-ES guest, the APs will have been parked by UEFI using
GHCB pages allocated by UEFI. The hypervisor will write to the GHCB
SW_EXITINFO2 field of the GHCB when the AP is booted. As a result, the
GHCB pages must be marked reserved so that the OS does not attempt to use
them and experience memory corruption because of the hypervisor write.

Change the GHCB allocation from the default boot services memory to
reserved memory.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/PlatformPei/AmdSev.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index a2b38c591236..4a515a484720 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -51,9 +51,11 @@ AmdSevEsInitialize (
 
   //
   // Allocate GHCB and per-CPU variable pages.
+  //   Since the pages must survive across the UEFI to OS transition
+  //   make them reserved.
   //
   GhcbPageCount = mMaxCpuCount * 2;
-  GhcbBase = AllocatePages (GhcbPageCount);
+  GhcbBase = AllocateReservedPages (GhcbPageCount);
   ASSERT (GhcbBase != NULL);
 
   GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN) GhcbBase;
-- 
2.17.1


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

* [PATCH v8 45/46] UefiCpuPkg/MpInitLib: Prepare SEV-ES guest APs for OS use
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (44 preceding siblings ...)
  2020-05-20 16:56 ` [PATCH v8 44/46] OvmfPkg: Move the GHCB allocations into reserved memory Lendacky, Thomas
@ 2020-05-20 16:56 ` Lendacky, Thomas
  2020-05-20 16:56 ` [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files Lendacky, Thomas
  46 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20 16:56 UTC (permalink / raw)
  To: devel

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

Before UEFI transfers control to the OS, it must park the AP. This is
done using the AsmRelocateApLoop function to transition into 32-bit
non-paging mode. For an SEV-ES guest, a few additional things must be
done:
  - AsmRelocateApLoop must be updated to support SEV-ES. This means
    performing a VMGEXIT AP Reset Hold instead of an MWAIT or HLT loop.
  - Since the AP must transition to real mode, a small routine is copied
    to the WakeupBuffer area. Since the WakeupBuffer will be used by
    the AP during OS booting, it must be placed in reserved memory.
    Additionally, the AP stack must be located where it can be accessed
    in real mode.
  - Once the AP is in real mode it will transfer control to the
    destination specified by the OS in the SEV-ES AP Jump Table. The
    SEV-ES AP Jump Table address is saved by the hypervisor for the OS
    using the GHCB VMGEXIT AP Jump Table exit code.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 UefiCpuPkg/Library/MpInitLib/MpLib.h          |   8 +-
 UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  54 +++++++-
 UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 131 ++++++++++++++++--
 3 files changed, 175 insertions(+), 18 deletions(-)

diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h
index f0cbb3763b5d..f576937890c9 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
@@ -293,7 +293,8 @@ struct _CPU_MP_DATA {
   UINT64                         GhcbBase;
 };
 
-#define AP_RESET_STACK_SIZE 64
+#define AP_SAFE_STACK_SIZE  128
+#define AP_RESET_STACK_SIZE AP_SAFE_STACK_SIZE
 
 #pragma pack(1)
 
@@ -349,8 +350,11 @@ VOID
   IN BOOLEAN                 MwaitSupport,
   IN UINTN                   ApTargetCState,
   IN UINTN                   PmCodeSegment,
+  IN UINTN                   Pm16CodeSegment,
   IN UINTN                   TopOfApStack,
-  IN UINTN                   NumberToFinish
+  IN UINTN                   NumberToFinish,
+  IN UINTN                   SevEsAPJumpTable,
+  IN UINTN                   WakeupBuffer
   );
 
 /**
diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
index 19527300ff3a..869c895ef27c 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
@@ -12,6 +12,7 @@
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/DebugAgentLib.h>
 #include <Library/DxeServicesTableLib.h>
+#include <Library/VmgExitLib.h>
 #include <Register/Amd/Fam17Msr.h>
 #include <Register/Amd/Ghcb.h>
 
@@ -85,6 +86,13 @@ GetWakeupBuffer (
 {
   EFI_STATUS              Status;
   EFI_PHYSICAL_ADDRESS    StartAddress;
+  EFI_MEMORY_TYPE         MemoryType;
+
+  if (PcdGetBool (PcdSevEsIsEnabled)) {
+    MemoryType = EfiReservedMemoryType;
+  } else {
+    MemoryType = EfiBootServicesData;
+  }
 
   //
   // Try to allocate buffer below 1M for waking vector.
@@ -97,7 +105,7 @@ GetWakeupBuffer (
   StartAddress = 0x88000;
   Status = gBS->AllocatePages (
                   AllocateMaxAddress,
-                  EfiBootServicesData,
+                  MemoryType,
                   EFI_SIZE_TO_PAGES (WakeupBufferSize),
                   &StartAddress
                   );
@@ -159,8 +167,10 @@ GetSevEsAPMemory (
   VOID
   )
 {
-  EFI_STATUS            Status;
-  EFI_PHYSICAL_ADDRESS  StartAddress;
+  EFI_STATUS                Status;
+  EFI_PHYSICAL_ADDRESS      StartAddress;
+  MSR_SEV_ES_GHCB_REGISTER  Msr;
+  GHCB                      *Ghcb;
 
   //
   // Allocate 1 page for AP jump table page
@@ -176,6 +186,16 @@ GetSevEsAPMemory (
 
   DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN) StartAddress));
 
+  //
+  // Save the SevEsAPMemory as the AP jump table.
+  //
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+  Ghcb = Msr.Ghcb;
+
+  VmgInit (Ghcb);
+  VmgExit (Ghcb, SVM_EXIT_AP_JUMP_TABLE, 0, (UINT64) (UINTN) StartAddress);
+  VmgDone (Ghcb);
+
   return (UINTN) StartAddress;
 }
 
@@ -330,17 +350,26 @@ RelocateApLoop (
   BOOLEAN                MwaitSupport;
   ASM_RELOCATE_AP_LOOP   AsmRelocateApLoopFunc;
   UINTN                  ProcessorNumber;
+  UINTN                  StackStart;
 
   MpInitLibWhoAmI (&ProcessorNumber);
   CpuMpData    = GetCpuMpData ();
   MwaitSupport = IsMwaitSupport ();
+  if (CpuMpData->SevEsIsEnabled) {
+    StackStart = CpuMpData->SevEsAPResetStackStart;
+  } else {
+    StackStart = mReservedTopOfApStack;
+  }
   AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) mReservedApLoopFunc;
   AsmRelocateApLoopFunc (
     MwaitSupport,
     CpuMpData->ApTargetCState,
     CpuMpData->PmCodeSegment,
-    mReservedTopOfApStack - ProcessorNumber * AP_SAFE_STACK_SIZE,
-    (UINTN) &mNumberToFinish
+    CpuMpData->Pm16CodeSegment,
+    StackStart - ProcessorNumber * AP_SAFE_STACK_SIZE,
+    (UINTN) &mNumberToFinish,
+    CpuMpData->SevEsAPBuffer,
+    CpuMpData->WakeupBuffer
     );
   //
   // It should never reach here
@@ -374,6 +403,21 @@ MpInitChangeApLoopCallback (
   while (mNumberToFinish > 0) {
     CpuPause ();
   }
+
+  if (CpuMpData->SevEsIsEnabled && (CpuMpData->WakeupBuffer != (UINTN) -1)) {
+    //
+    // There are APs present. Re-use reserved memory area below 1MB from
+    // WakeupBuffer as the area to be used for transitioning to 16-bit mode
+    // in support of booting of the AP by an OS.
+    //
+    CopyMem (
+      (VOID *) CpuMpData->WakeupBuffer,
+      (VOID *) (CpuMpData->AddressMap.RendezvousFunnelAddress +
+                  CpuMpData->AddressMap.SwitchToRealPM16ModeOffset),
+      CpuMpData->AddressMap.SwitchToRealPM16ModeSize
+      );
+  }
+
   DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__));
 }
 
diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
index 6956b408d004..3b8ec477b8b3 100644
--- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
+++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
@@ -465,6 +465,10 @@ BITS 16
     ;     - IP for Real Mode (two bytes)
     ;     - CS for Real Mode (two bytes)
     ;
+    ; This label is also used with AsmRelocateApLoop. During MP finalization,
+    ; the code from PM16Mode to SwitchToRealProcEnd is copied to the start of
+    ; the WakeupBuffer, allowing a parked AP to be booted by an OS.
+    ;
 PM16Mode:
     mov        eax, cr0                    ; Read CR0
     btr        eax, 0                      ; Set PE=0
@@ -487,32 +491,95 @@ PM16Mode:
 SwitchToRealProcEnd:
 
 ;-------------------------------------------------------------------------------------
-;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);
+;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, Pm16CodeSegment, TopOfApStack, CountTofinish, SevEsAPJumpTable, WakeupBuffer);
 ;-------------------------------------------------------------------------------------
 global ASM_PFX(AsmRelocateApLoop)
 ASM_PFX(AsmRelocateApLoop):
 AsmRelocateApLoopStart:
 BITS 64
+    cmp        qword [rsp + 56], 0
+    je         NoSevEs
+
+    ;
+    ; Perform some SEV-ES related setup before leaving 64-bit mode
+    ;
+    push       rcx
+    push       rdx
+
+    ;
+    ; Get the RDX reset value using CPUID
+    ;
+    mov        rax, 1
+    cpuid
+    mov        rsi, rax          ; Save off the reset value for RDX
+
+    ;
+    ; Prepare the GHCB for the AP_HLT_LOOP VMGEXIT call
+    ;   - Must be done while in 64-bit long mode so that writes to
+    ;     the GHCB memory will be unencrypted.
+    ;   - No NAE events can be generated once this is set otherwise
+    ;     the AP_RESET_HOLD SW_EXITCODE will be overwritten.
+    ;
+    mov        rcx, 0xc0010130
+    rdmsr                        ; Retrieve current GHCB address
+    shl        rdx, 32
+    or         rdx, rax
+
+    mov        rdi, rdx
+    xor        rax, rax
+    mov        rcx, 0x800
+    shr        rcx, 3
+    rep stosq                    ; Clear the GHCB
+
+    mov        rax, 0x80000004   ; VMGEXIT AP_RESET_HOLD
+    mov        [rdx + 0x390], rax
+
+    pop        rdx
+    pop        rcx
+
+NoSevEs:
     cli                          ; Disable interrupt before switching to 32-bit mode
-    mov        rax, [rsp + 40]   ; CountTofinish
+    mov        rax, [rsp + 48]   ; CountTofinish
     lock dec   dword [rax]       ; (*CountTofinish)--
-    mov        rsp, r9
-    push       rcx
-    push       rdx
 
-    lea        rsi, [PmEntry]    ; rsi <- The start address of transition code
+    mov        rax, [rsp + 56]   ; SevEsAPJumpTable
+    mov        rbx, [rsp + 64]   ; WakeupBuffer
+    mov        rsp, [rsp + 40]   ; TopOfApStack
+
+    push       rax               ; Save SevEsAPJumpTable
+    push       rbx               ; Save WakeupBuffer
+    push       r9                ; Save Pm16CodeSegment
+    push       rcx               ; Save MwaitSupport
+    push       rdx               ; Save ApTargetCState
+
+    lea        rax, [PmEntry]    ; rax <- The start address of transition code
 
     push       r8
-    push       rsi
-    DB         0x48
-    retf
+    push       rax
+
+    ;
+    ; Clear R8 - R15, for reset, before going into 32-bit mode
+    ;
+    xor        r8, r8
+    xor        r9, r9
+    xor        r10, r10
+    xor        r11, r11
+    xor        r12, r12
+    xor        r13, r13
+    xor        r14, r14
+    xor        r15, r15
+
+    ;
+    ; Far return into 32-bit mode
+    ;
+o64 retf
+
 BITS 32
 PmEntry:
     mov        eax, cr0
     btr        eax, 31           ; Clear CR0.PG
     mov        cr0, eax          ; Disable paging and caches
 
-    mov        ebx, edx          ; Save EntryPoint to rbx, for rdmsr will overwrite rdx
     mov        ecx, 0xc0000080
     rdmsr
     and        ah, ~ 1           ; Clear LME
@@ -525,6 +592,8 @@ PmEntry:
     add        esp, 4
     pop        ecx,
     add        esp, 4
+
+MwaitCheck:
     cmp        cl, 1              ; Check mwait-monitor support
     jnz        HltLoop
     mov        ebx, edx           ; Save C-State to ebx
@@ -538,10 +607,50 @@ MwaitLoop:
     shl        eax, 4
     mwait
     jmp        MwaitLoop
+
 HltLoop:
+    pop        edx                ; PM16CodeSegment
+    add        esp, 4
+    pop        ebx                ; WakeupBuffer
+    add        esp, 4
+    pop        eax                ; SevEsAPJumpTable
+    add        esp, 4
+    cmp        eax, 0             ; Check for SEV-ES
+    je         DoHlt
+
+    cli
+    ;
+    ; SEV-ES is enabled, use VMGEXIT (GHCB information already
+    ; set by caller)
+    ;
+BITS 64
+    rep        vmmcall
+BITS 32
+
+    ;
+    ; Back from VMGEXIT AP_HLT_LOOP
+    ;   Push the FLAGS/CS/IP values to use
+    ;
+    push       word 0x0002        ; EFLAGS
+    xor        ecx, ecx
+    mov        cx, [eax + 2]      ; CS
+    push       cx
+    mov        cx, [eax]          ; IP
+    push       cx
+    push       word 0x0000        ; For alignment, will be discarded
+
+    push       edx
+    push       ebx
+
+    mov        edx, esi           ; Restore RDX reset value
+
+    retf
+
+DoHlt:
     cli
     hlt
-    jmp        HltLoop
+    jmp        DoHlt
+
 BITS 64
 AsmRelocateApLoopEnd:
 
-- 
2.17.1


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

* [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files
  2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
                   ` (45 preceding siblings ...)
  2020-05-20 16:56 ` [PATCH v8 45/46] UefiCpuPkg/MpInitLib: Prepare SEV-ES guest APs for OS use Lendacky, Thomas
@ 2020-05-20 16:56 ` Lendacky, Thomas
  2020-05-19 21:54   ` Brijesh Singh
  2020-05-26 14:12   ` [edk2-devel] " Laszlo Ersek
  46 siblings, 2 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-20 16:56 UTC (permalink / raw)
  To: devel

Register reviewers for the SEV-related files in OvmfPkg.

Cc: Andrew Fish <afish@apple.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 Maintainers.txt | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Maintainers.txt b/Maintainers.txt
index 896ac5821fc6..76f336b7dcc4 100644
--- a/Maintainers.txt
+++ b/Maintainers.txt
@@ -441,6 +441,16 @@ F: OvmfPkg/PvScsiDxe/
 R: Liran Alon <liran.alon@oracle.com>
 R: Nikita Leshenko <nikita.leshchenko@oracle.com>
 
+OvmfPkg: SEV-related modules
+F: OvmfPkg/AmdSevDxe/
+F: OvmfPkg/Include/Library/MemEncryptSevLib.h
+F: OvmfPkg/IoMmuDxe/AmdSevIoMmu.*
+F: OvmfPkg/Library/BaseMemEncryptSevLib/
+F: OvmfPkg/Library/VmgExitLib
+F: OvmfPkg/PlatformPei/AmdSev.c
+R: Tom Lendacky <thomas.lendacky@amd.com>
+R: Brijesh Singh <brijesh.singh@amd.com>
+
 PcAtChipsetPkg
 F: PcAtChipsetPkg/
 W: https://github.com/tianocore/tianocore.github.io/wiki/PcAtChipsetPkg
-- 
2.17.1


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

* Re: [edk2-devel] [PATCH v8 09/46] OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library
  2020-05-19 21:50 ` [PATCH v8 09/46] OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library Lendacky, Thomas
@ 2020-05-21 16:42   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-21 16:42 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Anthony Perard, Julien Grall

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Various CpuExceptionHandlerLib libraries will updated to use the new
> VmgExitLib library. To prevent any build breakage, update the OvmfPkg
> DSC files that use a form of the CpuExceptionHandlerLib library to
> include the VmgExitLib library.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Cc: Anthony Perard <anthony.perard@citrix.com>
> Cc: Julien Grall <julien@xen.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/OvmfPkgIa32.dsc    | 1 +
>  OvmfPkg/OvmfPkgIa32X64.dsc | 1 +
>  OvmfPkg/OvmfPkgX64.dsc     | 1 +
>  OvmfPkg/OvmfXen.dsc        | 1 +
>  4 files changed, 4 insertions(+)
> 
> diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
> index d0df9cbbfb2b..7d7297aa1cd8 100644
> --- a/OvmfPkg/OvmfPkgIa32.dsc
> +++ b/OvmfPkg/OvmfPkgIa32.dsc
> @@ -228,6 +228,7 @@ [LibraryClasses]
>  
>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
> +  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
>  
>  [LibraryClasses.common.SEC]
>    TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
> diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
> index b3ae62fee92b..c85e988372ec 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.dsc
> +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
> @@ -232,6 +232,7 @@ [LibraryClasses]
>  
>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
> +  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
>  
>  [LibraryClasses.common.SEC]
>    TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> index f7fe75ebf531..0b9189ab1e38 100644
> --- a/OvmfPkg/OvmfPkgX64.dsc
> +++ b/OvmfPkg/OvmfPkgX64.dsc
> @@ -232,6 +232,7 @@ [LibraryClasses]
>  
>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
> +  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
>  
>  [LibraryClasses.common.SEC]
>    TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
> diff --git a/OvmfPkg/OvmfXen.dsc b/OvmfPkg/OvmfXen.dsc
> index 3af0ee705407..a3ba96f7002a 100644
> --- a/OvmfPkg/OvmfXen.dsc
> +++ b/OvmfPkg/OvmfXen.dsc
> @@ -213,6 +213,7 @@ [LibraryClasses]
>  
>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
> +  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
>  
>  [LibraryClasses.common.SEC]
>    QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSecLib.inf
> 

(Reviewing this series incrementally against v6.)

The updates in this patch look good.

Thanks
Laszlo


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

* Re: [edk2-devel] [PATCH v8 12/46] OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF
  2020-05-19 21:50 ` [PATCH v8 12/46] OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF Lendacky, Thomas
@ 2020-05-21 16:52   ` Laszlo Ersek
  2020-05-21 17:08     ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-21 16:52 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> The base VmgExitLib library provides a default limited interface. As it
> does not provide full support, create an OVMF version of this library to
> begin the process of providing full support of SEV-ES within OVMF.
> 
> SEV-ES support is only provided for X64 builds, so only OvmfPkgX64.dsc is
> updated to make use of the OvmfPkg version of the library.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/OvmfPkgX64.dsc                        |   2 +-
>  OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |  36 ++++
>  OvmfPkg/Library/VmgExitLib/VmgExitLib.c       | 155 ++++++++++++++++++
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c |  81 +++++++++
>  OvmfPkg/Library/VmgExitLib/VmgExitLib.uni     |  15 ++

(1) Please drop the UNI file. UNI files are needed (to my understanding)
with UPT (UEFI Packaging Tool), but OvmfPkg content is not distributed
like that. So UNI files would only be a distraction under OvmfPkg.

>  5 files changed, 288 insertions(+), 1 deletion(-)
>  create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
>  create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.c
>  create mode 100644 OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>  create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
> 
> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> index 0b9189ab1e38..b5f3859420d0 100644
> --- a/OvmfPkg/OvmfPkgX64.dsc
> +++ b/OvmfPkg/OvmfPkgX64.dsc
> @@ -232,7 +232,7 @@ [LibraryClasses]
>  
>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
> -  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
> +  VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
>  
>  [LibraryClasses.common.SEC]
>    TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
> diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
> new file mode 100644
> index 000000000000..0e6bc8432314
> --- /dev/null
> +++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
> @@ -0,0 +1,36 @@
> +## @file
> +#  VMGEXIT Support Library.
> +#
> +#  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010005
> +  BASE_NAME                      = VmgExitLib
> +  MODULE_UNI_FILE                = VmgExitLib.uni

(2) Please drop MODULE_UNI_FILE too, according to (1).

> +  FILE_GUID                      = 0e923c25-13cd-430b-8714-ffe85652a97b
> +  MODULE_TYPE                    = BASE
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = VmgExitLib
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +#  VALID_ARCHITECTURES           = X64
> +#
> +
> +[Sources.X64]
> +  X64/VmgExitVcHandler.c
> +
> +[Sources.common]
> +  VmgExitLib.c

(3) I think this split for [Sources] does not make sense under OvmfPkg.
I'd only use one [Sources] Section, and also move VmgExitVcHandler.c out
of the X64 subdir. The X64 subdir per se doesn't look useful either.

> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +
> diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.c b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
> new file mode 100644
> index 000000000000..7b7ebea85256
> --- /dev/null
> +++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
> @@ -0,0 +1,155 @@
> +/** @file
> +  VMGEXIT Support Library.
> +
> +  Copyright (C) 2020, 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 <Register/Amd/Msr.h>
> +
> +/**
> +  Check for VMGEXIT error
> +
> +  Check if the hypervisor has returned an error after completion of the VMGEXIT
> +  by examining the SwExitInfo1 field of the GHCB.
> +
> +  @param[in]  Ghcb       A pointer to the GHCB
> +
> +  @retval  0             VMGEXIT succeeded.
> +  @retval  Others        VMGEXIT processing did not succeed. Exception number to
> +                         be propagated.
> +
> +**/
> +STATIC
> +UINT64
> +VmgExitErrorCheck (
> +  IN GHCB                *Ghcb
> +  )
> +{
> +  GHCB_EVENT_INJECTION  Event;
> +  GHCB_EXIT_INFO        ExitInfo;
> +  UINT64                Status;
> +
> +  ExitInfo.Uint64 = Ghcb->SaveArea.SwExitInfo1;
> +  ASSERT ((ExitInfo.Elements.Lower32Bits == 0) ||
> +          (ExitInfo.Elements.Lower32Bits == 1));
> +
> +  Status = 0;
> +  if (ExitInfo.Elements.Lower32Bits == 0) {
> +    return Status;
> +  }
> +
> +  if (ExitInfo.Elements.Lower32Bits == 1) {
> +    ASSERT (Ghcb->SaveArea.SwExitInfo2 != 0);
> +
> +    // Check that the return event is valid

(4) Please prepend and append empty "//" lines.

> +    Event.Uint64 = Ghcb->SaveArea.SwExitInfo2;
> +    if (Event.Elements.Valid &&
> +        Event.Elements.Type == GHCB_EVENT_INJECTION_TYPE_EXCEPTION) {
> +      switch (Event.Elements.Vector) {
> +      case GP_EXCEPTION:
> +      case UD_EXCEPTION:
> +        // Use returned event as return code

(5) Same as (4).

With these addressed:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks,
Laszlo

> +        Status = Event.Uint64;
> +      }
> +    }
> +  }
> +
> +  if (Status == 0) {
> +    GHCB_EVENT_INJECTION  GpEvent;
> +
> +    GpEvent.Uint64 = 0;
> +    GpEvent.Elements.Vector = GP_EXCEPTION;
> +    GpEvent.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
> +    GpEvent.Elements.Valid  = 1;
> +
> +    Status = GpEvent.Uint64;
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Perform VMGEXIT.
> +
> +  Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and
> +  then handles the return actions.
> +
> +  @param[in, out]  Ghcb       A pointer to the GHCB
> +  @param[in]       ExitCode   VMGEXIT code to be assigned to the SwExitCode
> +                              field of the GHCB.
> +  @param[in]       ExitInfo1  VMGEXIT information to be assigned to the
> +                              SwExitInfo1 field of the GHCB.
> +  @param[in]       ExitInfo2  VMGEXIT information to be assigned to the
> +                              SwExitInfo2 field of the GHCB.
> +
> +  @retval  0                  VMGEXIT succeeded.
> +  @retval  Others             VMGEXIT processing did not succeed. Exception
> +                              event to be propagated.
> +
> +**/
> +UINT64
> +EFIAPI
> +VmgExit (
> +  IN OUT GHCB                *Ghcb,
> +  IN     UINT64              ExitCode,
> +  IN     UINT64              ExitInfo1,
> +  IN     UINT64              ExitInfo2
> +  )
> +{
> +  Ghcb->SaveArea.SwExitCode = ExitCode;
> +  Ghcb->SaveArea.SwExitInfo1 = ExitInfo1;
> +  Ghcb->SaveArea.SwExitInfo2 = ExitInfo2;
> +
> +  //
> +  // Guest memory is used for the guest-hypervisor communication, so fence
> +  // the invocation of the VMGEXIT instruction to ensure GHCB accesses are
> +  // synchronized properly.
> +  //
> +  MemoryFence ();
> +  AsmVmgExit ();
> +  MemoryFence ();
> +
> +  return VmgExitErrorCheck (Ghcb);
> +}
> +
> +/**
> +  Perform pre-VMGEXIT initialization/preparation.
> +
> +  Performs the necessary steps in preparation for invoking VMGEXIT. Must be
> +  called before setting any fields within the GHCB.
> +
> +  @param[in, out]  Ghcb       A pointer to the GHCB
> +
> +**/
> +VOID
> +EFIAPI
> +VmgInit (
> +  IN OUT GHCB                *Ghcb
> +  )
> +{
> +  SetMem (&Ghcb->SaveArea, sizeof (Ghcb->SaveArea), 0);
> +}
> +
> +/**
> +  Perform post-VMGEXIT cleanup.
> +
> +  Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
> +  called after obtaining needed fields within the GHCB.
> +
> +  @param[in, out]  Ghcb       A pointer to the GHCB
> +
> +**/
> +VOID
> +EFIAPI
> +VmgDone (
> +  IN OUT GHCB                *Ghcb
> +  )
> +{
> +}
> +
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> new file mode 100644
> index 000000000000..036f030d6b34
> --- /dev/null
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -0,0 +1,81 @@
> +/** @file
> +  X64 #VC Exception Handler functon.
> +
> +  Copyright (C) 2020, 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 <Register/Amd/Msr.h>
> +
> +/**
> +  Handle a #VC exception.
> +
> +  Performs the necessary processing to handle a #VC exception.
> +
> +  @param[in, out]  ExceptionType  Pointer to an EFI_EXCEPTION_TYPE to be set
> +                                  as value to use on error.
> +  @param[in, out]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT
> +
> +  @retval  EFI_SUCCESS            Exception handled
> +  @retval  EFI_UNSUPPORTED        #VC not supported, (new) exception value to
> +                                  propagate provided
> +  @retval  EFI_PROTOCOL_ERROR     #VC handling failed, (new) exception value to
> +                                  propagate provided
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +VmgExitHandleVc (
> +  IN OUT EFI_EXCEPTION_TYPE  *ExceptionType,
> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  MSR_SEV_ES_GHCB_REGISTER  Msr;
> +  EFI_SYSTEM_CONTEXT_X64    *Regs;
> +  GHCB                      *Ghcb;
> +  UINT64                    ExitCode, Status;
> +  EFI_STATUS                VcRet;
> +
> +  VcRet = EFI_SUCCESS;
> +
> +  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
> +  ASSERT (Msr.GhcbInfo.Function == 0);
> +  ASSERT (Msr.Ghcb != 0);
> +
> +  Regs = SystemContext.SystemContextX64;
> +  Ghcb = Msr.Ghcb;
> +
> +  VmgInit (Ghcb);
> +
> +  ExitCode = Regs->ExceptionData;
> +  switch (ExitCode) {
> +  default:
> +    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
> +    if (Status == 0) {
> +      Regs->ExceptionData = 0;
> +      *ExceptionType = GP_EXCEPTION;
> +    } else {
> +      GHCB_EVENT_INJECTION  Event;
> +
> +      Event.Uint64 = Status;
> +      if (Event.Elements.ErrorCodeValid) {
> +        Regs->ExceptionData = Event.Elements.ErrorCode;
> +      } else {
> +        Regs->ExceptionData = 0;
> +      }
> +
> +      *ExceptionType = Event.Elements.Vector;
> +    }
> +
> +    VcRet = EFI_PROTOCOL_ERROR;
> +  }
> +
> +  VmgDone (Ghcb);
> +
> +  return VcRet;
> +}
> diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni b/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
> new file mode 100644
> index 000000000000..a919b484c319
> --- /dev/null
> +++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
> @@ -0,0 +1,15 @@
> +// /** @file
> +// VMGEXIT support library instance.
> +//
> +// VMGEXIT support library instance.
> +//
> +// Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
> +// SPDX-License-Identifier: BSD-2-Clause-Patent
> +//
> +// **/
> +
> +
> +#string STR_MODULE_ABSTRACT             #language en-US "OVMF VMGEXIT Support Library."
> +
> +#string STR_MODULE_DESCRIPTION          #language en-US "OVMF VMGEXIT Support Library."
> +
> 


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

* Re: [edk2-devel] [PATCH v8 12/46] OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF
  2020-05-21 16:52   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-21 17:08     ` Lendacky, Thomas
  0 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-21 17:08 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/21/20 11:52 AM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> The base VmgExitLib library provides a default limited interface. As it
>> does not provide full support, create an OVMF version of this library to
>> begin the process of providing full support of SEV-ES within OVMF.
>>
>> SEV-ES support is only provided for X64 builds, so only OvmfPkgX64.dsc is
>> updated to make use of the OvmfPkg version of the library.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   OvmfPkg/OvmfPkgX64.dsc                        |   2 +-
>>   OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |  36 ++++
>>   OvmfPkg/Library/VmgExitLib/VmgExitLib.c       | 155 ++++++++++++++++++
>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c |  81 +++++++++
>>   OvmfPkg/Library/VmgExitLib/VmgExitLib.uni     |  15 ++
> 
> (1) Please drop the UNI file. UNI files are needed (to my understanding)
> with UPT (UEFI Packaging Tool), but OvmfPkg content is not distributed
> like that. So UNI files would only be a distraction under OvmfPkg.

Will do.

> 
>>   5 files changed, 288 insertions(+), 1 deletion(-)
>>   create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
>>   create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.c
>>   create mode 100644 OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>   create mode 100644 OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
>>
>> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
>> index 0b9189ab1e38..b5f3859420d0 100644
>> --- a/OvmfPkg/OvmfPkgX64.dsc
>> +++ b/OvmfPkg/OvmfPkgX64.dsc
>> @@ -232,7 +232,7 @@ [LibraryClasses]
>>   
>>   [LibraryClasses.common]
>>     BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
>> -  VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
>> +  VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
>>   
>>   [LibraryClasses.common.SEC]
>>     TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
>> diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
>> new file mode 100644
>> index 000000000000..0e6bc8432314
>> --- /dev/null
>> +++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
>> @@ -0,0 +1,36 @@
>> +## @file
>> +#  VMGEXIT Support Library.
>> +#
>> +#  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
>> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +##
>> +
>> +[Defines]
>> +  INF_VERSION                    = 0x00010005
>> +  BASE_NAME                      = VmgExitLib
>> +  MODULE_UNI_FILE                = VmgExitLib.uni
> 
> (2) Please drop MODULE_UNI_FILE too, according to (1).

Will do.

> 
>> +  FILE_GUID                      = 0e923c25-13cd-430b-8714-ffe85652a97b
>> +  MODULE_TYPE                    = BASE
>> +  VERSION_STRING                 = 1.0
>> +  LIBRARY_CLASS                  = VmgExitLib
>> +
>> +#
>> +# The following information is for reference only and not required by the build tools.
>> +#
>> +#  VALID_ARCHITECTURES           = X64
>> +#
>> +
>> +[Sources.X64]
>> +  X64/VmgExitVcHandler.c
>> +
>> +[Sources.common]
>> +  VmgExitLib.c
> 
> (3) I think this split for [Sources] does not make sense under OvmfPkg.
> I'd only use one [Sources] Section, and also move VmgExitVcHandler.c out
> of the X64 subdir. The X64 subdir per se doesn't look useful either.

Will do.

> 
>> +
>> +[Packages]
>> +  MdePkg/MdePkg.dec
>> +  UefiCpuPkg/UefiCpuPkg.dec
>> +
>> +[LibraryClasses]
>> +  BaseLib
>> +
>> diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.c b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
>> new file mode 100644
>> index 000000000000..7b7ebea85256
>> --- /dev/null
>> +++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
>> @@ -0,0 +1,155 @@
>> +/** @file
>> +  VMGEXIT Support Library.
>> +
>> +  Copyright (C) 2020, 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 <Register/Amd/Msr.h>
>> +
>> +/**
>> +  Check for VMGEXIT error
>> +
>> +  Check if the hypervisor has returned an error after completion of the VMGEXIT
>> +  by examining the SwExitInfo1 field of the GHCB.
>> +
>> +  @param[in]  Ghcb       A pointer to the GHCB
>> +
>> +  @retval  0             VMGEXIT succeeded.
>> +  @retval  Others        VMGEXIT processing did not succeed. Exception number to
>> +                         be propagated.
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +VmgExitErrorCheck (
>> +  IN GHCB                *Ghcb
>> +  )
>> +{
>> +  GHCB_EVENT_INJECTION  Event;
>> +  GHCB_EXIT_INFO        ExitInfo;
>> +  UINT64                Status;
>> +
>> +  ExitInfo.Uint64 = Ghcb->SaveArea.SwExitInfo1;
>> +  ASSERT ((ExitInfo.Elements.Lower32Bits == 0) ||
>> +          (ExitInfo.Elements.Lower32Bits == 1));
>> +
>> +  Status = 0;
>> +  if (ExitInfo.Elements.Lower32Bits == 0) {
>> +    return Status;
>> +  }
>> +
>> +  if (ExitInfo.Elements.Lower32Bits == 1) {
>> +    ASSERT (Ghcb->SaveArea.SwExitInfo2 != 0);
>> +
>> +    // Check that the return event is valid
> 
> (4) Please prepend and append empty "//" lines.

Will do.

> 
>> +    Event.Uint64 = Ghcb->SaveArea.SwExitInfo2;
>> +    if (Event.Elements.Valid &&
>> +        Event.Elements.Type == GHCB_EVENT_INJECTION_TYPE_EXCEPTION) {
>> +      switch (Event.Elements.Vector) {
>> +      case GP_EXCEPTION:
>> +      case UD_EXCEPTION:
>> +        // Use returned event as return code
> 
> (5) Same as (4).

Will do.

> 
> With these addressed:
> 
> Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks!
Tom

> 
> Thanks,
> Laszlo
> 
>> +        Status = Event.Uint64;
>> +      }
>> +    }
>> +  }
>> +
>> +  if (Status == 0) {
>> +    GHCB_EVENT_INJECTION  GpEvent;
>> +
>> +    GpEvent.Uint64 = 0;
>> +    GpEvent.Elements.Vector = GP_EXCEPTION;
>> +    GpEvent.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
>> +    GpEvent.Elements.Valid  = 1;
>> +
>> +    Status = GpEvent.Uint64;
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +/**
>> +  Perform VMGEXIT.
>> +
>> +  Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and
>> +  then handles the return actions.
>> +
>> +  @param[in, out]  Ghcb       A pointer to the GHCB
>> +  @param[in]       ExitCode   VMGEXIT code to be assigned to the SwExitCode
>> +                              field of the GHCB.
>> +  @param[in]       ExitInfo1  VMGEXIT information to be assigned to the
>> +                              SwExitInfo1 field of the GHCB.
>> +  @param[in]       ExitInfo2  VMGEXIT information to be assigned to the
>> +                              SwExitInfo2 field of the GHCB.
>> +
>> +  @retval  0                  VMGEXIT succeeded.
>> +  @retval  Others             VMGEXIT processing did not succeed. Exception
>> +                              event to be propagated.
>> +
>> +**/
>> +UINT64
>> +EFIAPI
>> +VmgExit (
>> +  IN OUT GHCB                *Ghcb,
>> +  IN     UINT64              ExitCode,
>> +  IN     UINT64              ExitInfo1,
>> +  IN     UINT64              ExitInfo2
>> +  )
>> +{
>> +  Ghcb->SaveArea.SwExitCode = ExitCode;
>> +  Ghcb->SaveArea.SwExitInfo1 = ExitInfo1;
>> +  Ghcb->SaveArea.SwExitInfo2 = ExitInfo2;
>> +
>> +  //
>> +  // Guest memory is used for the guest-hypervisor communication, so fence
>> +  // the invocation of the VMGEXIT instruction to ensure GHCB accesses are
>> +  // synchronized properly.
>> +  //
>> +  MemoryFence ();
>> +  AsmVmgExit ();
>> +  MemoryFence ();
>> +
>> +  return VmgExitErrorCheck (Ghcb);
>> +}
>> +
>> +/**
>> +  Perform pre-VMGEXIT initialization/preparation.
>> +
>> +  Performs the necessary steps in preparation for invoking VMGEXIT. Must be
>> +  called before setting any fields within the GHCB.
>> +
>> +  @param[in, out]  Ghcb       A pointer to the GHCB
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +VmgInit (
>> +  IN OUT GHCB                *Ghcb
>> +  )
>> +{
>> +  SetMem (&Ghcb->SaveArea, sizeof (Ghcb->SaveArea), 0);
>> +}
>> +
>> +/**
>> +  Perform post-VMGEXIT cleanup.
>> +
>> +  Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
>> +  called after obtaining needed fields within the GHCB.
>> +
>> +  @param[in, out]  Ghcb       A pointer to the GHCB
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +VmgDone (
>> +  IN OUT GHCB                *Ghcb
>> +  )
>> +{
>> +}
>> +
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> new file mode 100644
>> index 000000000000..036f030d6b34
>> --- /dev/null
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -0,0 +1,81 @@
>> +/** @file
>> +  X64 #VC Exception Handler functon.
>> +
>> +  Copyright (C) 2020, 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 <Register/Amd/Msr.h>
>> +
>> +/**
>> +  Handle a #VC exception.
>> +
>> +  Performs the necessary processing to handle a #VC exception.
>> +
>> +  @param[in, out]  ExceptionType  Pointer to an EFI_EXCEPTION_TYPE to be set
>> +                                  as value to use on error.
>> +  @param[in, out]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT
>> +
>> +  @retval  EFI_SUCCESS            Exception handled
>> +  @retval  EFI_UNSUPPORTED        #VC not supported, (new) exception value to
>> +                                  propagate provided
>> +  @retval  EFI_PROTOCOL_ERROR     #VC handling failed, (new) exception value to
>> +                                  propagate provided
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +VmgExitHandleVc (
>> +  IN OUT EFI_EXCEPTION_TYPE  *ExceptionType,
>> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  MSR_SEV_ES_GHCB_REGISTER  Msr;
>> +  EFI_SYSTEM_CONTEXT_X64    *Regs;
>> +  GHCB                      *Ghcb;
>> +  UINT64                    ExitCode, Status;
>> +  EFI_STATUS                VcRet;
>> +
>> +  VcRet = EFI_SUCCESS;
>> +
>> +  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
>> +  ASSERT (Msr.GhcbInfo.Function == 0);
>> +  ASSERT (Msr.Ghcb != 0);
>> +
>> +  Regs = SystemContext.SystemContextX64;
>> +  Ghcb = Msr.Ghcb;
>> +
>> +  VmgInit (Ghcb);
>> +
>> +  ExitCode = Regs->ExceptionData;
>> +  switch (ExitCode) {
>> +  default:
>> +    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
>> +    if (Status == 0) {
>> +      Regs->ExceptionData = 0;
>> +      *ExceptionType = GP_EXCEPTION;
>> +    } else {
>> +      GHCB_EVENT_INJECTION  Event;
>> +
>> +      Event.Uint64 = Status;
>> +      if (Event.Elements.ErrorCodeValid) {
>> +        Regs->ExceptionData = Event.Elements.ErrorCode;
>> +      } else {
>> +        Regs->ExceptionData = 0;
>> +      }
>> +
>> +      *ExceptionType = Event.Elements.Vector;
>> +    }
>> +
>> +    VcRet = EFI_PROTOCOL_ERROR;
>> +  }
>> +
>> +  VmgDone (Ghcb);
>> +
>> +  return VcRet;
>> +}
>> diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni b/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
>> new file mode 100644
>> index 000000000000..a919b484c319
>> --- /dev/null
>> +++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.uni
>> @@ -0,0 +1,15 @@
>> +// /** @file
>> +// VMGEXIT support library instance.
>> +//
>> +// VMGEXIT support library instance.
>> +//
>> +// Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
>> +// SPDX-License-Identifier: BSD-2-Clause-Patent
>> +//
>> +// **/
>> +
>> +
>> +#string STR_MODULE_ABSTRACT             #language en-US "OVMF VMGEXIT Support Library."
>> +
>> +#string STR_MODULE_DESCRIPTION          #language en-US "OVMF VMGEXIT Support Library."
>> +
>>
> 

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

* Re: [edk2-devel] [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events
  2020-05-19 21:50 ` [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events Lendacky, Thomas
@ 2020-05-21 17:25   ` Laszlo Ersek
  2020-05-22 10:05     ` Laszlo Ersek
  2020-05-22 13:40     ` Lendacky, Thomas
  0 siblings, 2 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-21 17:25 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a IOIO_PROT intercept generates a #VC exception. VMGEXIT
> must be used to allow the hypervisor to handle this intercept.
> 
> Add support to construct the required GHCB values to support a IOIO_PROT
> NAE event.  Parse the instruction that generated the #VC exception,
> setting the required register values in the GHCB and creating the proper
> SW_EXITINFO1 value in the GHCB.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 604 +++++++++++++++++-
>  1 file changed, 590 insertions(+), 14 deletions(-)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 036f030d6b34..b4578ae922c1 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -12,6 +12,573 @@
>  #include <Library/VmgExitLib.h>
>  #include <Register/Amd/Msr.h>
>  
> +//
> +// Instruction execution mode definition
> +//
> +typedef enum {
> +  LongMode64Bit        = 0,
> +  LongModeCompat32Bit,
> +  LongModeCompat16Bit,
> +} SEV_ES_INSTRUCTION_MODE;
> +
> +//
> +// Instruction size definition (for operand and address)
> +//
> +typedef enum {
> +  Size8Bits            = 0,
> +  Size16Bits,
> +  Size32Bits,
> +  Size64Bits,
> +} SEV_ES_INSTRUCTION_SIZE;
> +
> +//
> +// Intruction segment definition
> +//
> +typedef enum {
> +  SegmentEs            = 0,
> +  SegmentCs,
> +  SegmentSs,
> +  SegmentDs,
> +  SegmentFs,
> +  SegmentGs,
> +} SEV_ES_INSTRUCTION_SEGMENT;
> +
> +//
> +// Instruction rep function definition
> +//
> +typedef enum {
> +  RepNone              = 0,
> +  RepZ,
> +  RepNZ,
> +} SEV_ES_INSTRUCTION_REP;
> +
> +//
> +// Instruction REX prefix definition
> +//
> +typedef union {
> +  struct {
> +    UINT8  BitB:1;
> +    UINT8  BitX:1;
> +    UINT8  BitR:1;
> +    UINT8  BitW:1;
> +    UINT8  Rex:4;
> +  } Bits;
> +
> +  UINT8  Uint8;
> +} SEV_ES_INSTRUCTION_REX_PREFIX;
> +
> +//
> +// Instruction ModRM definition
> +//
> +typedef union {
> +  struct {
> +    UINT8  Rm:3;
> +    UINT8  Reg:3;
> +    UINT8  Mod:2;
> +  } Bits;
> +
> +  UINT8  Uint8;
> +} SEV_ES_INSTRUCTION_MODRM;
> +
> +typedef struct {
> +  UINT8  Rm;
> +  UINT8  Reg;
> +  UINT8  Mod;
> +} SEV_ES_INSTRUCTION_MODRM_EXT;
> +
> +//
> +// Instruction SIB definition
> +//
> +typedef union {
> +  struct {
> +    UINT8  Base:3;
> +    UINT8  Index:3;
> +    UINT8  Scale:2;
> +  } Bits;
> +
> +  UINT8  Uint8;
> +} SEV_ES_INSTRUCTION_SIB;
> +
> +typedef struct {
> +  UINT8  Base;
> +  UINT8  Index;
> +  UINT8  Scale;
> +} SEV_ES_INSTRUCTION_SIB_EXT;
> +
> +//
> +// Instruction opcode definition
> +//
> +typedef struct {
> +  SEV_ES_INSTRUCTION_MODRM_EXT  ModRm;
> +
> +  SEV_ES_INSTRUCTION_SIB_EXT    Sib;
> +
> +  UINTN                         RegData;
> +  UINTN                         RmData;
> +} SEV_ES_INSTRUCTION_OPCODE_EXT;
> +
> +//
> +// Instruction parsing context definition
> +//
> +typedef struct {
> +  GHCB                           *Ghcb;
> +
> +  SEV_ES_INSTRUCTION_MODE        Mode;
> +  SEV_ES_INSTRUCTION_SIZE        DataSize;
> +  SEV_ES_INSTRUCTION_SIZE        AddrSize;
> +  BOOLEAN                        SegmentSpecified;
> +  SEV_ES_INSTRUCTION_SEGMENT     Segment;
> +  SEV_ES_INSTRUCTION_REP         RepMode;
> +
> +  UINT8                          *Begin;
> +  UINT8                          *End;
> +
> +  UINT8                          *Prefixes;
> +  UINT8                          *OpCodes;
> +  UINT8                          *Displacement;
> +  UINT8                          *Immediate;
> +
> +  SEV_ES_INSTRUCTION_REX_PREFIX  RexPrefix;
> +
> +  BOOLEAN                        ModRmPresent;
> +  SEV_ES_INSTRUCTION_MODRM       ModRm;
> +
> +  BOOLEAN                        SibPresent;
> +  SEV_ES_INSTRUCTION_SIB         Sib;
> +
> +  UINTN                          PrefixSize;
> +  UINTN                          OpCodeSize;
> +  UINTN                          DisplacementSize;
> +  UINTN                          ImmediateSize;
> +
> +  SEV_ES_INSTRUCTION_OPCODE_EXT  Ext;
> +} SEV_ES_INSTRUCTION_DATA;
> +
> +//
> +// Non-automatic Exit function prototype
> +//
> +typedef
> +UINT64
> +(*NAE_EXIT) (
> +  GHCB                     *Ghcb,
> +  EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  );
> +

(1) From the typedefs above, can we move those that are defined in
industry specs (such as AMD SEV specs) to header file(s)? For example,
under OvmfPkg/Include/Register or OvmfPkg/Include/IndustryStandard.

> +
> +/**
> +  Checks the GHCB to determine if the specified register has been marked valid.
> +
> +  The ValidBitmap area represents the areas of the GHCB that have been marked
> +  valid. Return an indication of whether the area of the GHCB that holds the
> +  specified register has been marked valid.
> +
> +  @param[in] Ghcb    Pointer to the Guest-Hypervisor Communication Block
> +  @param[in] Reg     Offset in the GHCB of the register to check
> +
> +  @retval TRUE       Register has been marked vald in the GHCB
> +  @retval FALSE      Register has not been marked valid in the GHCB
> +
> +**/
> +STATIC
> +BOOLEAN
> +GhcbIsRegValid (
> +  IN GHCB                *Ghcb,
> +  IN GHCB_REGISTER       Reg
> +  )
> +{
> +  UINT32  RegIndex;
> +  UINT32  RegBit;
> +
> +  RegIndex = Reg / 8;
> +  RegBit   = Reg & 0x07;
> +
> +  return (Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit));
> +}
> +
> +/**
> +  Marks a register as valid in the GHCB.
> +
> +  The ValidBitmap area represents the areas of the GHCB that have been marked
> +  valid. Set the area of the GHCB that holds the specified register as valid.
> +
> +  @param[in, out] Ghcb    Pointer to the Guest-Hypervisor Communication Block
> +  @param[in] Reg          Offset in the GHCB of the register to mark valid
> +
> +**/
> +STATIC
> +VOID
> +GhcbSetRegValid (
> +  IN OUT GHCB                *Ghcb,
> +  IN     GHCB_REGISTER       Reg
> +  )
> +{
> +  UINT32  RegIndex;
> +  UINT32  RegBit;
> +
> +  RegIndex = Reg / 8;
> +  RegBit   = Reg & 0x07;
> +
> +  Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
> +}
> +
> +/**
> +  Decode instruction prefixes.
> +
> +  Parse the instruction data to track the instruction prefixes that have
> +  been used.
> +
> +  @param[in]      Regs             x64 processor context
> +  @param[in, out] InstructionData  Instruction parsing context
> +
> +**/
> +STATIC
> +VOID
> +DecodePrefixes (
> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  SEV_ES_INSTRUCTION_MODE  Mode;
> +  SEV_ES_INSTRUCTION_SIZE  ModeDataSize;
> +  SEV_ES_INSTRUCTION_SIZE  ModeAddrSize;
> +  UINT8                    *Byte;
> +
> +  /*TODO: Determine current mode - 64-bit for now */

(2) I'm OK with a TODO comment, but please use the idiomatic style.

> +  Mode = LongMode64Bit;
> +  ModeDataSize = Size32Bits;
> +  ModeAddrSize = Size64Bits;
> +
> +  InstructionData->Mode = Mode;
> +  InstructionData->DataSize = ModeDataSize;
> +  InstructionData->AddrSize = ModeAddrSize;
> +
> +  InstructionData->Prefixes = InstructionData->Begin;
> +
> +  Byte = InstructionData->Prefixes;
> +  for ( ; ; Byte++, InstructionData->PrefixSize++) {
> +    //
> +    // Check the 0x40 to 0x4F range using an if statement here since some
> +    // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
> +    // 16 case statements below.
> +    //
> +    if ((*Byte >= 0x40) && (*Byte <= 0x4F)) {
> +      InstructionData->RexPrefix.Uint8 = *Byte;
> +      if (*Byte & 0x08)
> +        InstructionData->DataSize = Size64Bits;
> +      continue;
> +    }
> +
> +    switch (*Byte) {
> +    case 0x26:
> +    case 0x2E:
> +    case 0x36:
> +    case 0x3E:
> +      if (Mode != LongMode64Bit) {
> +        InstructionData->SegmentSpecified = TRUE;
> +        InstructionData->Segment = (*Byte >> 3) & 3;
> +      }
> +      break;
> +
> +    case 0x64:
> +      InstructionData->SegmentSpecified = TRUE;
> +      InstructionData->Segment = *Byte & 7;
> +      break;
> +
> +    case 0x66:
> +      if (!InstructionData->RexPrefix.Uint8) {
> +        InstructionData->DataSize =
> +          (Mode == LongMode64Bit)       ? Size16Bits :
> +          (Mode == LongModeCompat32Bit) ? Size16Bits :
> +          (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
> +      }
> +      break;
> +
> +    case 0x67:
> +      InstructionData->AddrSize =
> +        (Mode == LongMode64Bit)       ? Size32Bits :
> +        (Mode == LongModeCompat32Bit) ? Size16Bits :
> +        (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
> +      break;
> +
> +    case 0xF0:
> +      break;
> +
> +    case 0xF2:
> +      InstructionData->RepMode = RepZ;
> +      break;
> +
> +    case 0xF3:
> +      InstructionData->RepMode = RepNZ;
> +      break;
> +
> +    default:
> +      InstructionData->OpCodes = Byte;
> +      InstructionData->OpCodeSize = (*Byte == 0x0F) ? 2 : 1;
> +
> +      InstructionData->End = Byte + InstructionData->OpCodeSize;
> +      InstructionData->Displacement = InstructionData->End;
> +      InstructionData->Immediate = InstructionData->End;
> +      return;
> +    }
> +  }
> +}

(3) Can we introduce macros or enum constants for the prefixes?

Although, I seem to remember that QEMU's TCG and even KVM's instruction
parser uses open-coded magic constants for the prefixes, so the above
code would not be without precedent.

Can we please add comments (stating the prefix names) near the case
labels at least?

> +
> +/**
> +  Determine instruction length
> +
> +  Return the total length of the parsed instruction.
> +
> +  @param[in] InstructionData  Instruction parsing context
> +
> +  @retval                     Length of parsed instruction

(4) @retval is for specific return values (enum constants, macros etc).
For general descriptions, please use @return, not @retval.

Please review the rest of the patches from this angle.

> +
> +**/
> +STATIC
> +UINT64
> +InstructionLength (
> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  return (UINT64) (InstructionData->End - InstructionData->Begin);
> +}
> +
> +/**
> +  Initialize the instruction parsing context.
> +
> +  Initialize the instruction parsing context, which includes decoding the
> +  instruction prefixes.
> +
> +  @param[in, out] InstructionData  Instruction parsing context
> +  @param[in]      Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in]      Regs             x64 processor context
> +
> +**/
> +STATIC
> +VOID
> +InitInstructionData (
> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
> +  IN     GHCB                     *Ghcb,
> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs
> +  )
> +{
> +  SetMem (InstructionData, sizeof (*InstructionData), 0);
> +  InstructionData->Ghcb = Ghcb;
> +  InstructionData->Begin = (UINT8 *) Regs->Rip;
> +  InstructionData->End = (UINT8 *) Regs->Rip;
> +
> +  DecodePrefixes (Regs, InstructionData);
> +}
> +
> +/**
> +  Report an unsupported event to the hypervisor
> +
> +  Use the VMGEXIT support to report an unsupported event to the hypervisor.
> +
> +  @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                              Block
> +  @param[in] Regs             x64 processor context
> +  @param[in] InstructionData  Instruction parsing context
> +
> +  @retval                     New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +UnsupportedExit (
> +  IN GHCB                     *Ghcb,
> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
> +  if (Status == 0) {
> +    GHCB_EVENT_INJECTION  Event;
> +
> +    Event.Uint64 = 0;
> +    Event.Elements.Vector = GP_EXCEPTION;
> +    Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
> +    Event.Elements.Valid  = 1;
> +
> +    Status = Event.Uint64;
> +  }
> +
> +  return Status;
> +}
> +
> +#define IOIO_TYPE_STR       (1 << 2)
> +#define IOIO_TYPE_IN        1
> +#define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
> +#define IOIO_TYPE_OUT       0
> +#define IOIO_TYPE_OUTS      (IOIO_TYPE_OUT | IOIO_TYPE_STR)
> +
> +#define IOIO_REP            (1 << 3)
> +
> +#define IOIO_ADDR_64        (1 << 9)
> +#define IOIO_ADDR_32        (1 << 8)
> +#define IOIO_ADDR_16        (1 << 7)
> +
> +#define IOIO_DATA_32        (1 << 6)
> +#define IOIO_DATA_16        (1 << 5)
> +#define IOIO_DATA_8         (1 << 4)
> +#define IOIO_DATA_BYTES(x)  (((x) & 0x70) >> 4)
> +
> +#define IOIO_SEG_ES         (0 << 10)
> +#define IOIO_SEG_DS         (3 << 10)

(5) I feel like these macros belong in:

  MdePkg/Include/Register/Amd/Ghcb.h

Do you agree?

If that exact header file is not the best, then I'd request a new header
file under OvmfPkg/Include/Register.

(6) I'd also suggest using BIT2, BIT3, BIT9, rather than the open-coded
shifts. BITx reads more natural in edk2 source.


> +
> +/**
> +  Build the IOIO event information.
> +
> +  The IOIO event information identifies the type of IO operation to be performed
> +  by the hypervisor. Build this information based on the instruction data.
> +
> +  @param[in]       Regs             x64 processor context
> +  @param[in, out]  InstructionData  Instruction parsing context
> +
> +  @retval Others                    IOIO event information value

(7) Even though "@retval Others" is a common pattern in edk2, I consider
it a mis-use of "@retval". Whenever we're tempted to write "@retval
Others", we should just use "@return". Here, for example:

  @return  IOIO event information values.

If you strongly disagree, I won't insist; I'm not trying to create
busywork for you. Otherwise, please check the rest of the OvmfPkg
patches for "@retval Others".

> +
> +**/
> +STATIC
> +UINT64
> +IoioExitInfo (
> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  ExitInfo;
> +
> +  ExitInfo = 0;
> +
> +  switch (*(InstructionData->OpCodes)) {
> +  // IN immediate opcodes

(8) Right, I love these comments; please do use one of the idiomatic
comment styles though. Either prepend and append empty "//" lines, or
move the single-line "//" to the right of the case labels.

> +  case 0xE4:
> +  case 0xE5:
> +    InstructionData->ImmediateSize = 1;
> +    InstructionData->End++;
> +    ExitInfo |= IOIO_TYPE_IN;
> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
> +    break;
> +
> +  // OUT immediate opcodes
> +  case 0xE6:
> +  case 0xE7:
> +    InstructionData->ImmediateSize = 1;
> +    InstructionData->End++;
> +    ExitInfo |= IOIO_TYPE_OUT;
> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
> +    break;
> +
> +  // IN register opcodes
> +  case 0xEC:
> +  case 0xED:
> +    ExitInfo |= IOIO_TYPE_IN;
> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
> +    break;
> +
> +  // OUT register opcodes
> +  case 0xEE:
> +  case 0xEF:
> +    ExitInfo |= IOIO_TYPE_OUT;
> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
> +    break;
> +
> +  default:
> +    return 0;
> +  }
> +
> +  switch (*(InstructionData->OpCodes)) {
> +  case 0xE4:
> +  case 0xE6:
> +  case 0xEC:
> +  case 0xEE:
> +    // Single-byte opcodes

(9) Please make both the location and the style of this comment
consistent with (8).

All my comments are superficial, and that's fine. I totally intend to
let you do what you need to do in this library (I can tell that writing
a new instruction decoder must have been hellish work), so I don't want
to get in your way -- just please make this easier to browse with "edk2
eyes".

I'm ready to ACK the patch once all comments have been addressed -- I'm
not giving an A-b at once because some of my requests / proposals need
concrete values filled in (such as header file names).

Thanks!
Laszlo

> +    ExitInfo |= IOIO_DATA_8;
> +    break;
> +
> +  default:
> +    // Length determined by instruction parsing
> +    ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
> +                                                          : IOIO_DATA_32;
> +  }
> +
> +  switch (InstructionData->AddrSize) {
> +  case Size16Bits:
> +    ExitInfo |= IOIO_ADDR_16;
> +    break;
> +
> +  case Size32Bits:
> +    ExitInfo |= IOIO_ADDR_32;
> +    break;
> +
> +  case Size64Bits:
> +    ExitInfo |= IOIO_ADDR_64;
> +    break;
> +
> +  default:
> +    break;
> +  }
> +
> +  if (InstructionData->RepMode) {
> +    ExitInfo |= IOIO_REP;
> +  }
> +
> +  return ExitInfo;
> +}
> +
> +/**
> +  Handle an IOIO event.
> +
> +  Use the VMGEXIT instruction to handle an IOIO event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +IoioExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  ExitInfo1, Status;
> +
> +  ExitInfo1 = IoioExitInfo (Regs, InstructionData);
> +  if (!ExitInfo1) {
> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
> +  }
> +
> +  if (ExitInfo1 & IOIO_TYPE_IN) {
> +    Ghcb->SaveArea.Rax = 0;
> +  } else {
> +    CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
> +  }
> +  GhcbSetRegValid (Ghcb, GhcbRax);
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
> +  if (Status) {
> +    return Status;
> +  }
> +
> +  if (ExitInfo1 & IOIO_TYPE_IN) {
> +    if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
> +    }
> +    CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
> +  }
> +
> +  return 0;
> +}
> +
>  /**
>    Handle a #VC exception.
>  
> @@ -38,6 +605,8 @@ VmgExitHandleVc (
>    MSR_SEV_ES_GHCB_REGISTER  Msr;
>    EFI_SYSTEM_CONTEXT_X64    *Regs;
>    GHCB                      *Ghcb;
> +  NAE_EXIT                  NaeExit;
> +  SEV_ES_INSTRUCTION_DATA   InstructionData;
>    UINT64                    ExitCode, Status;
>    EFI_STATUS                VcRet;
>  
> @@ -54,24 +623,31 @@ VmgExitHandleVc (
>  
>    ExitCode = Regs->ExceptionData;
>    switch (ExitCode) {
> +  case SVM_EXIT_IOIO_PROT:
> +    NaeExit = IoioExit;
> +    break;
> +
>    default:
> -    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
> -    if (Status == 0) {
> -      Regs->ExceptionData = 0;
> -      *ExceptionType = GP_EXCEPTION;
> +    NaeExit = UnsupportedExit;
> +  }
> +
> +  InitInstructionData (&InstructionData, Ghcb, Regs);
> +
> +  Status = NaeExit (Ghcb, Regs, &InstructionData);
> +  if (Status == 0) {
> +    Regs->Rip += InstructionLength (&InstructionData);
> +  } else {
> +    GHCB_EVENT_INJECTION  Event;
> +
> +    Event.Uint64 = Status;
> +    if (Event.Elements.ErrorCodeValid) {
> +      Regs->ExceptionData = Event.Elements.ErrorCode;
>      } else {
> -      GHCB_EVENT_INJECTION  Event;
> -
> -      Event.Uint64 = Status;
> -      if (Event.Elements.ErrorCodeValid) {
> -        Regs->ExceptionData = Event.Elements.ErrorCode;
> -      } else {
> -        Regs->ExceptionData = 0;
> -      }
> -
> -      *ExceptionType = Event.Elements.Vector;
> +      Regs->ExceptionData = 0;
>      }
>  
> +    *ExceptionType = Event.Elements.Vector;
> +
>      VcRet = EFI_PROTOCOL_ERROR;
>    }
>  
> 


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

* Re: [edk2-devel] [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events
  2020-05-21 17:25   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-22 10:05     ` Laszlo Ersek
  2020-05-22 13:41       ` Lendacky, Thomas
  2020-05-22 13:40     ` Lendacky, Thomas
  1 sibling, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 10:05 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/21/20 19:25, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
>>
>> Under SEV-ES, a IOIO_PROT intercept generates a #VC exception. VMGEXIT
>> must be used to allow the hypervisor to handle this intercept.
>>
>> Add support to construct the required GHCB values to support a IOIO_PROT
>> NAE event.  Parse the instruction that generated the #VC exception,
>> setting the required register values in the GHCB and creating the proper
>> SW_EXITINFO1 value in the GHCB.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 604 +++++++++++++++++-
>>  1 file changed, 590 insertions(+), 14 deletions(-)
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index 036f030d6b34..b4578ae922c1 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -12,6 +12,573 @@
>>  #include <Library/VmgExitLib.h>
>>  #include <Register/Amd/Msr.h>
>>  
>> +//
>> +// Instruction execution mode definition
>> +//
>> +typedef enum {
>> +  LongMode64Bit        = 0,
>> +  LongModeCompat32Bit,
>> +  LongModeCompat16Bit,
>> +} SEV_ES_INSTRUCTION_MODE;
>> +
>> +//
>> +// Instruction size definition (for operand and address)
>> +//
>> +typedef enum {
>> +  Size8Bits            = 0,
>> +  Size16Bits,
>> +  Size32Bits,
>> +  Size64Bits,
>> +} SEV_ES_INSTRUCTION_SIZE;
>> +
>> +//
>> +// Intruction segment definition
>> +//
>> +typedef enum {
>> +  SegmentEs            = 0,
>> +  SegmentCs,
>> +  SegmentSs,
>> +  SegmentDs,
>> +  SegmentFs,
>> +  SegmentGs,
>> +} SEV_ES_INSTRUCTION_SEGMENT;
>> +
>> +//
>> +// Instruction rep function definition
>> +//
>> +typedef enum {
>> +  RepNone              = 0,
>> +  RepZ,
>> +  RepNZ,
>> +} SEV_ES_INSTRUCTION_REP;
>> +
>> +//
>> +// Instruction REX prefix definition
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT8  BitB:1;
>> +    UINT8  BitX:1;
>> +    UINT8  BitR:1;
>> +    UINT8  BitW:1;
>> +    UINT8  Rex:4;
>> +  } Bits;
>> +
>> +  UINT8  Uint8;
>> +} SEV_ES_INSTRUCTION_REX_PREFIX;
>> +
>> +//
>> +// Instruction ModRM definition
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT8  Rm:3;
>> +    UINT8  Reg:3;
>> +    UINT8  Mod:2;
>> +  } Bits;
>> +
>> +  UINT8  Uint8;
>> +} SEV_ES_INSTRUCTION_MODRM;
>> +
>> +typedef struct {
>> +  UINT8  Rm;
>> +  UINT8  Reg;
>> +  UINT8  Mod;
>> +} SEV_ES_INSTRUCTION_MODRM_EXT;
>> +
>> +//
>> +// Instruction SIB definition
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT8  Base:3;
>> +    UINT8  Index:3;
>> +    UINT8  Scale:2;
>> +  } Bits;
>> +
>> +  UINT8  Uint8;
>> +} SEV_ES_INSTRUCTION_SIB;
>> +
>> +typedef struct {
>> +  UINT8  Base;
>> +  UINT8  Index;
>> +  UINT8  Scale;
>> +} SEV_ES_INSTRUCTION_SIB_EXT;
>> +
>> +//
>> +// Instruction opcode definition
>> +//
>> +typedef struct {
>> +  SEV_ES_INSTRUCTION_MODRM_EXT  ModRm;
>> +
>> +  SEV_ES_INSTRUCTION_SIB_EXT    Sib;
>> +
>> +  UINTN                         RegData;
>> +  UINTN                         RmData;
>> +} SEV_ES_INSTRUCTION_OPCODE_EXT;
>> +
>> +//
>> +// Instruction parsing context definition
>> +//
>> +typedef struct {
>> +  GHCB                           *Ghcb;
>> +
>> +  SEV_ES_INSTRUCTION_MODE        Mode;
>> +  SEV_ES_INSTRUCTION_SIZE        DataSize;
>> +  SEV_ES_INSTRUCTION_SIZE        AddrSize;
>> +  BOOLEAN                        SegmentSpecified;
>> +  SEV_ES_INSTRUCTION_SEGMENT     Segment;
>> +  SEV_ES_INSTRUCTION_REP         RepMode;
>> +
>> +  UINT8                          *Begin;
>> +  UINT8                          *End;
>> +
>> +  UINT8                          *Prefixes;
>> +  UINT8                          *OpCodes;
>> +  UINT8                          *Displacement;
>> +  UINT8                          *Immediate;
>> +
>> +  SEV_ES_INSTRUCTION_REX_PREFIX  RexPrefix;
>> +
>> +  BOOLEAN                        ModRmPresent;
>> +  SEV_ES_INSTRUCTION_MODRM       ModRm;
>> +
>> +  BOOLEAN                        SibPresent;
>> +  SEV_ES_INSTRUCTION_SIB         Sib;
>> +
>> +  UINTN                          PrefixSize;
>> +  UINTN                          OpCodeSize;
>> +  UINTN                          DisplacementSize;
>> +  UINTN                          ImmediateSize;
>> +
>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  Ext;
>> +} SEV_ES_INSTRUCTION_DATA;
>> +
>> +//
>> +// Non-automatic Exit function prototype
>> +//
>> +typedef
>> +UINT64
>> +(*NAE_EXIT) (
>> +  GHCB                     *Ghcb,
>> +  EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  );
>> +
> 
> (1) From the typedefs above, can we move those that are defined in
> industry specs (such as AMD SEV specs) to header file(s)? For example,
> under OvmfPkg/Include/Register or OvmfPkg/Include/IndustryStandard.
> 
>> +
>> +/**
>> +  Checks the GHCB to determine if the specified register has been marked valid.
>> +
>> +  The ValidBitmap area represents the areas of the GHCB that have been marked
>> +  valid. Return an indication of whether the area of the GHCB that holds the
>> +  specified register has been marked valid.
>> +
>> +  @param[in] Ghcb    Pointer to the Guest-Hypervisor Communication Block
>> +  @param[in] Reg     Offset in the GHCB of the register to check
>> +
>> +  @retval TRUE       Register has been marked vald in the GHCB
>> +  @retval FALSE      Register has not been marked valid in the GHCB
>> +
>> +**/
>> +STATIC
>> +BOOLEAN
>> +GhcbIsRegValid (
>> +  IN GHCB                *Ghcb,
>> +  IN GHCB_REGISTER       Reg
>> +  )
>> +{
>> +  UINT32  RegIndex;
>> +  UINT32  RegBit;
>> +
>> +  RegIndex = Reg / 8;
>> +  RegBit   = Reg & 0x07;
>> +
>> +  return (Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit));

(10) Please check the patches for uses of the bitwise AND operator,
where the result is considered as a logical value.

In those contexts, the edk2 coding style needs an explicit comparison
against 0. For example, the above would be:

  return (Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0;


This is actually more than just a style question, generally speaking.
BOOLEAN in edk2 is a typedef to UINT8 (well, more precisely, to
"unsigned char"). It is not like the _Bool type in ISO C99.

ISO C99 says about _Bool, in section "6.3.1.2 Boolean type": "When any
scalar value is converted to _Bool, the result is 0 if the value
compares equal to 0; otherwise, the result is 1." (Footnote: "NaNs do
not compare equal to 0 and thus convert to 1.")

However, BOOLEAN is "unsigned char" and not _Bool; therefore, if you do
for example:

BOOLEAN
IsBit8Set (
  IN UINT16 Value
  )
{
  return Value & BIT8;
}

then IsBit8Set (0x100) will return FALSE -- converting 0x100 to
"unsigned char" yields 0.

In edk2, in every context (not just in return statements) where we have
(Expr1 & Expr2) consumed as a logical result, we're supposed to write
((Expr1 & Expr2) != 0). (The inner parens are important as bit-AND has
weaker binding than "==" and "!=".)

Please check the rest of the patches for this.

Thanks!
Laszlo

>> +}
>> +
>> +/**
>> +  Marks a register as valid in the GHCB.
>> +
>> +  The ValidBitmap area represents the areas of the GHCB that have been marked
>> +  valid. Set the area of the GHCB that holds the specified register as valid.
>> +
>> +  @param[in, out] Ghcb    Pointer to the Guest-Hypervisor Communication Block
>> +  @param[in] Reg          Offset in the GHCB of the register to mark valid
>> +
>> +**/
>> +STATIC
>> +VOID
>> +GhcbSetRegValid (
>> +  IN OUT GHCB                *Ghcb,
>> +  IN     GHCB_REGISTER       Reg
>> +  )
>> +{
>> +  UINT32  RegIndex;
>> +  UINT32  RegBit;
>> +
>> +  RegIndex = Reg / 8;
>> +  RegBit   = Reg & 0x07;
>> +
>> +  Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
>> +}
>> +
>> +/**
>> +  Decode instruction prefixes.
>> +
>> +  Parse the instruction data to track the instruction prefixes that have
>> +  been used.
>> +
>> +  @param[in]      Regs             x64 processor context
>> +  @param[in, out] InstructionData  Instruction parsing context
>> +
>> +**/
>> +STATIC
>> +VOID
>> +DecodePrefixes (
>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  SEV_ES_INSTRUCTION_MODE  Mode;
>> +  SEV_ES_INSTRUCTION_SIZE  ModeDataSize;
>> +  SEV_ES_INSTRUCTION_SIZE  ModeAddrSize;
>> +  UINT8                    *Byte;
>> +
>> +  /*TODO: Determine current mode - 64-bit for now */
> 
> (2) I'm OK with a TODO comment, but please use the idiomatic style.
> 
>> +  Mode = LongMode64Bit;
>> +  ModeDataSize = Size32Bits;
>> +  ModeAddrSize = Size64Bits;
>> +
>> +  InstructionData->Mode = Mode;
>> +  InstructionData->DataSize = ModeDataSize;
>> +  InstructionData->AddrSize = ModeAddrSize;
>> +
>> +  InstructionData->Prefixes = InstructionData->Begin;
>> +
>> +  Byte = InstructionData->Prefixes;
>> +  for ( ; ; Byte++, InstructionData->PrefixSize++) {
>> +    //
>> +    // Check the 0x40 to 0x4F range using an if statement here since some
>> +    // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
>> +    // 16 case statements below.
>> +    //
>> +    if ((*Byte >= 0x40) && (*Byte <= 0x4F)) {
>> +      InstructionData->RexPrefix.Uint8 = *Byte;
>> +      if (*Byte & 0x08)
>> +        InstructionData->DataSize = Size64Bits;
>> +      continue;
>> +    }
>> +
>> +    switch (*Byte) {
>> +    case 0x26:
>> +    case 0x2E:
>> +    case 0x36:
>> +    case 0x3E:
>> +      if (Mode != LongMode64Bit) {
>> +        InstructionData->SegmentSpecified = TRUE;
>> +        InstructionData->Segment = (*Byte >> 3) & 3;
>> +      }
>> +      break;
>> +
>> +    case 0x64:
>> +      InstructionData->SegmentSpecified = TRUE;
>> +      InstructionData->Segment = *Byte & 7;
>> +      break;
>> +
>> +    case 0x66:
>> +      if (!InstructionData->RexPrefix.Uint8) {
>> +        InstructionData->DataSize =
>> +          (Mode == LongMode64Bit)       ? Size16Bits :
>> +          (Mode == LongModeCompat32Bit) ? Size16Bits :
>> +          (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
>> +      }
>> +      break;
>> +
>> +    case 0x67:
>> +      InstructionData->AddrSize =
>> +        (Mode == LongMode64Bit)       ? Size32Bits :
>> +        (Mode == LongModeCompat32Bit) ? Size16Bits :
>> +        (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
>> +      break;
>> +
>> +    case 0xF0:
>> +      break;
>> +
>> +    case 0xF2:
>> +      InstructionData->RepMode = RepZ;
>> +      break;
>> +
>> +    case 0xF3:
>> +      InstructionData->RepMode = RepNZ;
>> +      break;
>> +
>> +    default:
>> +      InstructionData->OpCodes = Byte;
>> +      InstructionData->OpCodeSize = (*Byte == 0x0F) ? 2 : 1;
>> +
>> +      InstructionData->End = Byte + InstructionData->OpCodeSize;
>> +      InstructionData->Displacement = InstructionData->End;
>> +      InstructionData->Immediate = InstructionData->End;
>> +      return;
>> +    }
>> +  }
>> +}
> 
> (3) Can we introduce macros or enum constants for the prefixes?
> 
> Although, I seem to remember that QEMU's TCG and even KVM's instruction
> parser uses open-coded magic constants for the prefixes, so the above
> code would not be without precedent.
> 
> Can we please add comments (stating the prefix names) near the case
> labels at least?
> 
>> +
>> +/**
>> +  Determine instruction length
>> +
>> +  Return the total length of the parsed instruction.
>> +
>> +  @param[in] InstructionData  Instruction parsing context
>> +
>> +  @retval                     Length of parsed instruction
> 
> (4) @retval is for specific return values (enum constants, macros etc).
> For general descriptions, please use @return, not @retval.
> 
> Please review the rest of the patches from this angle.
> 
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +InstructionLength (
>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  return (UINT64) (InstructionData->End - InstructionData->Begin);
>> +}
>> +
>> +/**
>> +  Initialize the instruction parsing context.
>> +
>> +  Initialize the instruction parsing context, which includes decoding the
>> +  instruction prefixes.
>> +
>> +  @param[in, out] InstructionData  Instruction parsing context
>> +  @param[in]      Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in]      Regs             x64 processor context
>> +
>> +**/
>> +STATIC
>> +VOID
>> +InitInstructionData (
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
>> +  IN     GHCB                     *Ghcb,
>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs
>> +  )
>> +{
>> +  SetMem (InstructionData, sizeof (*InstructionData), 0);
>> +  InstructionData->Ghcb = Ghcb;
>> +  InstructionData->Begin = (UINT8 *) Regs->Rip;
>> +  InstructionData->End = (UINT8 *) Regs->Rip;
>> +
>> +  DecodePrefixes (Regs, InstructionData);
>> +}
>> +
>> +/**
>> +  Report an unsupported event to the hypervisor
>> +
>> +  Use the VMGEXIT support to report an unsupported event to the hypervisor.
>> +
>> +  @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                              Block
>> +  @param[in] Regs             x64 processor context
>> +  @param[in] InstructionData  Instruction parsing context
>> +
>> +  @retval                     New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +UnsupportedExit (
>> +  IN GHCB                     *Ghcb,
>> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  Status;
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
>> +  if (Status == 0) {
>> +    GHCB_EVENT_INJECTION  Event;
>> +
>> +    Event.Uint64 = 0;
>> +    Event.Elements.Vector = GP_EXCEPTION;
>> +    Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
>> +    Event.Elements.Valid  = 1;
>> +
>> +    Status = Event.Uint64;
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +#define IOIO_TYPE_STR       (1 << 2)
>> +#define IOIO_TYPE_IN        1
>> +#define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
>> +#define IOIO_TYPE_OUT       0
>> +#define IOIO_TYPE_OUTS      (IOIO_TYPE_OUT | IOIO_TYPE_STR)
>> +
>> +#define IOIO_REP            (1 << 3)
>> +
>> +#define IOIO_ADDR_64        (1 << 9)
>> +#define IOIO_ADDR_32        (1 << 8)
>> +#define IOIO_ADDR_16        (1 << 7)
>> +
>> +#define IOIO_DATA_32        (1 << 6)
>> +#define IOIO_DATA_16        (1 << 5)
>> +#define IOIO_DATA_8         (1 << 4)
>> +#define IOIO_DATA_BYTES(x)  (((x) & 0x70) >> 4)
>> +
>> +#define IOIO_SEG_ES         (0 << 10)
>> +#define IOIO_SEG_DS         (3 << 10)
> 
> (5) I feel like these macros belong in:
> 
>   MdePkg/Include/Register/Amd/Ghcb.h
> 
> Do you agree?
> 
> If that exact header file is not the best, then I'd request a new header
> file under OvmfPkg/Include/Register.
> 
> (6) I'd also suggest using BIT2, BIT3, BIT9, rather than the open-coded
> shifts. BITx reads more natural in edk2 source.
> 
> 
>> +
>> +/**
>> +  Build the IOIO event information.
>> +
>> +  The IOIO event information identifies the type of IO operation to be performed
>> +  by the hypervisor. Build this information based on the instruction data.
>> +
>> +  @param[in]       Regs             x64 processor context
>> +  @param[in, out]  InstructionData  Instruction parsing context
>> +
>> +  @retval Others                    IOIO event information value
> 
> (7) Even though "@retval Others" is a common pattern in edk2, I consider
> it a mis-use of "@retval". Whenever we're tempted to write "@retval
> Others", we should just use "@return". Here, for example:
> 
>   @return  IOIO event information values.
> 
> If you strongly disagree, I won't insist; I'm not trying to create
> busywork for you. Otherwise, please check the rest of the OvmfPkg
> patches for "@retval Others".
> 
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +IoioExitInfo (
>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  ExitInfo;
>> +
>> +  ExitInfo = 0;
>> +
>> +  switch (*(InstructionData->OpCodes)) {
>> +  // IN immediate opcodes
> 
> (8) Right, I love these comments; please do use one of the idiomatic
> comment styles though. Either prepend and append empty "//" lines, or
> move the single-line "//" to the right of the case labels.
> 
>> +  case 0xE4:
>> +  case 0xE5:
>> +    InstructionData->ImmediateSize = 1;
>> +    InstructionData->End++;
>> +    ExitInfo |= IOIO_TYPE_IN;
>> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
>> +    break;
>> +
>> +  // OUT immediate opcodes
>> +  case 0xE6:
>> +  case 0xE7:
>> +    InstructionData->ImmediateSize = 1;
>> +    InstructionData->End++;
>> +    ExitInfo |= IOIO_TYPE_OUT;
>> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
>> +    break;
>> +
>> +  // IN register opcodes
>> +  case 0xEC:
>> +  case 0xED:
>> +    ExitInfo |= IOIO_TYPE_IN;
>> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
>> +    break;
>> +
>> +  // OUT register opcodes
>> +  case 0xEE:
>> +  case 0xEF:
>> +    ExitInfo |= IOIO_TYPE_OUT;
>> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
>> +    break;
>> +
>> +  default:
>> +    return 0;
>> +  }
>> +
>> +  switch (*(InstructionData->OpCodes)) {
>> +  case 0xE4:
>> +  case 0xE6:
>> +  case 0xEC:
>> +  case 0xEE:
>> +    // Single-byte opcodes
> 
> (9) Please make both the location and the style of this comment
> consistent with (8).
> 
> All my comments are superficial, and that's fine. I totally intend to
> let you do what you need to do in this library (I can tell that writing
> a new instruction decoder must have been hellish work), so I don't want
> to get in your way -- just please make this easier to browse with "edk2
> eyes".
> 
> I'm ready to ACK the patch once all comments have been addressed -- I'm
> not giving an A-b at once because some of my requests / proposals need
> concrete values filled in (such as header file names).
> 
> Thanks!
> Laszlo
> 
>> +    ExitInfo |= IOIO_DATA_8;
>> +    break;
>> +
>> +  default:
>> +    // Length determined by instruction parsing
>> +    ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
>> +                                                          : IOIO_DATA_32;
>> +  }
>> +
>> +  switch (InstructionData->AddrSize) {
>> +  case Size16Bits:
>> +    ExitInfo |= IOIO_ADDR_16;
>> +    break;
>> +
>> +  case Size32Bits:
>> +    ExitInfo |= IOIO_ADDR_32;
>> +    break;
>> +
>> +  case Size64Bits:
>> +    ExitInfo |= IOIO_ADDR_64;
>> +    break;
>> +
>> +  default:
>> +    break;
>> +  }
>> +
>> +  if (InstructionData->RepMode) {
>> +    ExitInfo |= IOIO_REP;
>> +  }
>> +
>> +  return ExitInfo;
>> +}
>> +
>> +/**
>> +  Handle an IOIO event.
>> +
>> +  Use the VMGEXIT instruction to handle an IOIO event.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +IoioExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  ExitInfo1, Status;
>> +
>> +  ExitInfo1 = IoioExitInfo (Regs, InstructionData);
>> +  if (!ExitInfo1) {
>> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +  }
>> +
>> +  if (ExitInfo1 & IOIO_TYPE_IN) {
>> +    Ghcb->SaveArea.Rax = 0;
>> +  } else {
>> +    CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
>> +  }
>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
>> +  if (Status) {
>> +    return Status;
>> +  }
>> +
>> +  if (ExitInfo1 & IOIO_TYPE_IN) {
>> +    if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
>> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +    }
>> +    CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
>> +  }
>> +
>> +  return 0;
>> +}
>> +
>>  /**
>>    Handle a #VC exception.
>>  
>> @@ -38,6 +605,8 @@ VmgExitHandleVc (
>>    MSR_SEV_ES_GHCB_REGISTER  Msr;
>>    EFI_SYSTEM_CONTEXT_X64    *Regs;
>>    GHCB                      *Ghcb;
>> +  NAE_EXIT                  NaeExit;
>> +  SEV_ES_INSTRUCTION_DATA   InstructionData;
>>    UINT64                    ExitCode, Status;
>>    EFI_STATUS                VcRet;
>>  
>> @@ -54,24 +623,31 @@ VmgExitHandleVc (
>>  
>>    ExitCode = Regs->ExceptionData;
>>    switch (ExitCode) {
>> +  case SVM_EXIT_IOIO_PROT:
>> +    NaeExit = IoioExit;
>> +    break;
>> +
>>    default:
>> -    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
>> -    if (Status == 0) {
>> -      Regs->ExceptionData = 0;
>> -      *ExceptionType = GP_EXCEPTION;
>> +    NaeExit = UnsupportedExit;
>> +  }
>> +
>> +  InitInstructionData (&InstructionData, Ghcb, Regs);
>> +
>> +  Status = NaeExit (Ghcb, Regs, &InstructionData);
>> +  if (Status == 0) {
>> +    Regs->Rip += InstructionLength (&InstructionData);
>> +  } else {
>> +    GHCB_EVENT_INJECTION  Event;
>> +
>> +    Event.Uint64 = Status;
>> +    if (Event.Elements.ErrorCodeValid) {
>> +      Regs->ExceptionData = Event.Elements.ErrorCode;
>>      } else {
>> -      GHCB_EVENT_INJECTION  Event;
>> -
>> -      Event.Uint64 = Status;
>> -      if (Event.Elements.ErrorCodeValid) {
>> -        Regs->ExceptionData = Event.Elements.ErrorCode;
>> -      } else {
>> -        Regs->ExceptionData = 0;
>> -      }
>> -
>> -      *ExceptionType = Event.Elements.Vector;
>> +      Regs->ExceptionData = 0;
>>      }
>>  
>> +    *ExceptionType = Event.Elements.Vector;
>> +
>>      VcRet = EFI_PROTOCOL_ERROR;
>>    }
>>  
>>
> 
> 
> 
> 


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

* Re: [edk2-devel] [PATCH v8 14/46] OvmfPkg/VmgExitLib: Support string IO for IOIO_PROT NAE events
  2020-05-19 21:50 ` [PATCH v8 14/46] OvmfPkg/VmgExitLib: Support string IO " Lendacky, Thomas
@ 2020-05-22 10:14   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 10:14 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Add support to the #VC exception handler to handle string IO. This
> requires expanding the IO instruction parsing to recognize string based
> IO instructions as well as preparing an un-encrypted buffer to be used
> to transfer (either to or from the guest) the string contents for the IO
> operation. The SW_EXITINFO2 and SW_SCRATCH fields of the GHCB are set
> appropriately for the operation. Multiple VMGEXIT invocations may be
> needed to complete the string IO operation.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 86 ++++++++++++++++---
>  1 file changed, 72 insertions(+), 14 deletions(-)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index b4578ae922c1..906b32e93d53 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -453,6 +453,22 @@ IoioExitInfo (
>    ExitInfo = 0;
>  
>    switch (*(InstructionData->OpCodes)) {
> +  // INS opcodes
> +  case 0x6C:
> +  case 0x6D:
> +    ExitInfo |= IOIO_TYPE_INS;
> +    ExitInfo |= IOIO_SEG_ES;
> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
> +    break;
> +
> +  // OUTS opcodes
> +  case 0x6E:
> +  case 0x6F:
> +    ExitInfo |= IOIO_TYPE_OUTS;
> +    ExitInfo |= IOIO_SEG_DS;
> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
> +    break;
> +
>    // IN immediate opcodes
>    case 0xE4:
>    case 0xE5:

(1) Same request about comment style as before.

> @@ -490,6 +506,8 @@ IoioExitInfo (
>    }
>  
>    switch (*(InstructionData->OpCodes)) {
> +  case 0x6C:
> +  case 0x6E:
>    case 0xE4:
>    case 0xE6:
>    case 0xEC:
> @@ -550,30 +568,70 @@ IoioExit (
>    IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>    )
>  {
> -  UINT64  ExitInfo1, Status;
> +  UINT64   ExitInfo1, ExitInfo2, Status;
> +  BOOLEAN  String;

(2) Please call this "IsString" or "IsStringOp" or something that
suggests it's a boolean, not an actual string.

>  
>    ExitInfo1 = IoioExitInfo (Regs, InstructionData);
>    if (!ExitInfo1) {
>      return UnsupportedExit (Ghcb, Regs, InstructionData);
>    }
>  
> -  if (ExitInfo1 & IOIO_TYPE_IN) {
> -    Ghcb->SaveArea.Rax = 0;
> +  String = (ExitInfo1 & IOIO_TYPE_STR) ? TRUE : FALSE;

(3) Please use the more idiomatic ((ExitInfo1 & IOIO_TYPE_STR) != 0) syntax.

> +  if (String) {
> +    UINTN  IoBytes, VmgExitBytes;
> +    UINTN  GhcbCount, OpCount;
> +
> +    Status = 0;
> +
> +    IoBytes = (ExitInfo1 >> 4) & 0x7;
> +    GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
> +
> +    OpCount = (ExitInfo1 & IOIO_REP) ? Regs->Rcx : 1;

(4) Please write

  ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1

> +    while (OpCount) {
> +      ExitInfo2 = MIN (OpCount, GhcbCount);
> +      VmgExitBytes = ExitInfo2 * IoBytes;
> +
> +      if (!(ExitInfo1 & IOIO_TYPE_IN)) {

(5) Please write

  if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {

> +        CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);
> +        Regs->Rsi += VmgExitBytes;
> +      }
> +
> +      Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
> +      Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
> +      if (Status) {

(6) Status is not a BOOLEAN but a UINT64. Please check (Status != 0) or
(Status > 0). Please revise the rest of the patches for this.

> +        return Status;
> +      }
> +
> +      if (ExitInfo1 & IOIO_TYPE_IN) {

(7) Similar to (4) and (5)

There are some more instances of this in the patch.

With the style nits fixed

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

> +        CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
> +        Regs->Rdi += VmgExitBytes;
> +      }
> +
> +      if (ExitInfo1 & IOIO_REP) {
> +        Regs->Rcx -= ExitInfo2;
> +      }
> +
> +      OpCount -= ExitInfo2;
> +    }
>    } else {
> -    CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
> -  }
> -  GhcbSetRegValid (Ghcb, GhcbRax);
> +    if (ExitInfo1 & IOIO_TYPE_IN) {
> +      Ghcb->SaveArea.Rax = 0;
> +    } else {
> +      CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
> +    }
> +    GhcbSetRegValid (Ghcb, GhcbRax);
>  
> -  Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
> -  if (Status) {
> -    return Status;
> -  }
> +    Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
> +    if (Status) {
> +      return Status;
> +    }
>  
> -  if (ExitInfo1 & IOIO_TYPE_IN) {
> -    if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
> -      return UnsupportedExit (Ghcb, Regs, InstructionData);
> +    if (ExitInfo1 & IOIO_TYPE_IN) {
> +      if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
> +        return UnsupportedExit (Ghcb, Regs, InstructionData);
> +      }
> +      CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
>      }
> -    CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
>    }
>  
>    return 0;
> 


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

* Re: [edk2-devel] [PATCH v8 15/46] OvmfPkg/VmgExitLib: Add support for CPUID NAE events
  2020-05-19 21:50 ` [PATCH v8 15/46] OvmfPkg/VmgExitLib: Add support for CPUID " Lendacky, Thomas
@ 2020-05-22 10:27   ` Laszlo Ersek
  2020-05-22 19:02     ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 10:27 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a CPUID intercept generates a #VC exception. VMGEXIT must be
> used to allow the hypervisor to handle this intercept.
> 
> Add support to construct the required GHCB values to support a CPUID NAE
> event. Additionally, CPUID 0x0000_000d requires XCR0 to be supplied in
> the GHCB, so add support to issue the XGETBV instruction.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 58 +++++++++++++++++++
>  1 file changed, 58 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 906b32e93d53..2f62795edf61 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -12,6 +12,8 @@
>  #include <Library/VmgExitLib.h>
>  #include <Register/Amd/Msr.h>
>  
> +#define CR4_OSXSAVE (1 << 18)
> +

(1) Please drop this macro, and:

>  //
>  // Instruction execution mode definition
>  //
> @@ -637,6 +639,58 @@ IoioExit (
>    return 0;
>  }
>  
> +/**
> +  Handle a CPUID event.
> +
> +  Use the VMGEXIT instruction to handle a CPUID event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +CpuidExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  Ghcb->SaveArea.Rax = Regs->Rax;
> +  GhcbSetRegValid (Ghcb, GhcbRax);
> +  Ghcb->SaveArea.Rcx = Regs->Rcx;
> +  GhcbSetRegValid (Ghcb, GhcbRcx);
> +  if (Regs->Rax == 0x0000000d) {

(2a) Can we use CPUID_EXTENDED_STATE here, from <Register/Intel/Cpuid.h>?

(2b) If so, I'd suggest updating the commit message too: replace "CPUID
0x0000_000d" with "CPUID 0x0000_000d (CPUID_EXTENDED_STATE)".

> +    Ghcb->SaveArea.XCr0 = (AsmReadCr4 () & CR4_OSXSAVE) ? AsmXGetBv (0) : 1;

(3) Here, please use the IA32_CR4 type from <Library/BaseLib.h>:

    IA32_CR4 Cr4;

    Cr4.UintN = AsmReadCr4 ();
    Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;


Some of the style requests I made under earlier patches in this series
apply here, so I won't spell them out again.


With the style updated:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

> +    GhcbSetRegValid (Ghcb, GhcbXCr0);
> +  }
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
> +  if (Status) {
> +    return Status;
> +  }
> +
> +  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
> +      !GhcbIsRegValid (Ghcb, GhcbRbx) ||
> +      !GhcbIsRegValid (Ghcb, GhcbRcx) ||
> +      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
> +  }
> +  Regs->Rax = Ghcb->SaveArea.Rax;
> +  Regs->Rbx = Ghcb->SaveArea.Rbx;
> +  Regs->Rcx = Ghcb->SaveArea.Rcx;
> +  Regs->Rdx = Ghcb->SaveArea.Rdx;
> +
> +  return 0;
> +}
> +
>  /**
>    Handle a #VC exception.
>  
> @@ -681,6 +735,10 @@ VmgExitHandleVc (
>  
>    ExitCode = Regs->ExceptionData;
>    switch (ExitCode) {
> +  case SVM_EXIT_CPUID:
> +    NaeExit = CpuidExit;
> +    break;
> +
>    case SVM_EXIT_IOIO_PROT:
>      NaeExit = IoioExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 16/46] OvmfPkg/VmgExitLib: Add support for MSR_PROT NAE events
  2020-05-19 21:50 ` [PATCH v8 16/46] OvmfPkg/VmgExitLib: Add support for MSR_PROT " Lendacky, Thomas
@ 2020-05-22 10:31   ` Laszlo Ersek
  2020-05-22 19:06     ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 10:31 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a MSR_PROT intercept generates a #VC exception. VMGEXIT must
> be used to allow the hypervisor to handle this intercept.
> 
> Add support to construct the required GHCB values to support an MSR_PROT
> NAE event. Parse the instruction that generated the #VC exception to
> determine whether it is RDMSR or WRMSR, setting the required register
> register values in the GHCB and creating the proper SW_EXIT_INFO1 value in
> the GHCB.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 63 +++++++++++++++++++
>  1 file changed, 63 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 2f62795edf61..1c6b472a47c4 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -411,6 +411,65 @@ UnsupportedExit (
>    return Status;
>  }
>  
> +/**
> +  Handle an MSR event.
> +
> +  Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +MsrExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  ExitInfo1, Status;
> +
> +  ExitInfo1 = 0;
> +
> +  switch (*(InstructionData->OpCodes + 1)) {
> +  case 0x30: // WRMSR

This comment looks great!

> +    ExitInfo1 = 1;
> +    Ghcb->SaveArea.Rax = Regs->Rax;
> +    GhcbSetRegValid (Ghcb, GhcbRax);
> +    Ghcb->SaveArea.Rdx = Regs->Rdx;
> +    GhcbSetRegValid (Ghcb, GhcbRdx);
> +    /* Fallthrough */

(1) This comment is very appreciated (I vaguely remember that the coding
style actually requires it), but we're supposed to put it like this:

    //
    // fall through
    //

(See: "git grep -B1 -A1 -i 'fall through'".)

> +  case 0x32: // RDMSR
> +    Ghcb->SaveArea.Rcx = Regs->Rcx;
> +    GhcbSetRegValid (Ghcb, GhcbRcx);
> +    break;
> +  default:
> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
> +  }
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
> +  if (Status) {

(2) As usual, please check (Status > 0) or (Status != 0) explicitly.

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

> +    return Status;
> +  }
> +
> +  if (!ExitInfo1) {
> +    if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
> +        !GhcbIsRegValid (Ghcb, GhcbRdx)) {
> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
> +    }
> +    Regs->Rax = Ghcb->SaveArea.Rax;
> +    Regs->Rdx = Ghcb->SaveArea.Rdx;
> +  }
> +
> +  return 0;
> +}
> +
>  #define IOIO_TYPE_STR       (1 << 2)
>  #define IOIO_TYPE_IN        1
>  #define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
> @@ -743,6 +802,10 @@ VmgExitHandleVc (
>      NaeExit = IoioExit;
>      break;
>  
> +  case SVM_EXIT_MSR:
> +    NaeExit = MsrExit;
> +    break;
> +
>    default:
>      NaeExit = UnsupportedExit;
>    }
> 


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

* Re: [edk2-devel] [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events
  2020-05-21 17:25   ` [edk2-devel] " Laszlo Ersek
  2020-05-22 10:05     ` Laszlo Ersek
@ 2020-05-22 13:40     ` Lendacky, Thomas
  1 sibling, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-22 13:40 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/21/20 12:25 PM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7Ccba3f15d7c694d95e8e908d7fdabffd6%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637256787485321976&amp;sdata=W4gPd4XgieGLxMCe4Ze77i1iCOZWE60UqnZmem8hpXE%3D&amp;reserved=0
>>
>> Under SEV-ES, a IOIO_PROT intercept generates a #VC exception. VMGEXIT
>> must be used to allow the hypervisor to handle this intercept.
>>
>> Add support to construct the required GHCB values to support a IOIO_PROT
>> NAE event.  Parse the instruction that generated the #VC exception,
>> setting the required register values in the GHCB and creating the proper
>> SW_EXITINFO1 value in the GHCB.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 604 +++++++++++++++++-
>>   1 file changed, 590 insertions(+), 14 deletions(-)
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index 036f030d6b34..b4578ae922c1 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -12,6 +12,573 @@
>>   #include <Library/VmgExitLib.h>
>>   #include <Register/Amd/Msr.h>
>>   
>> +//
>> +// Instruction execution mode definition
>> +//
>> +typedef enum {
>> +  LongMode64Bit        = 0,
>> +  LongModeCompat32Bit,
>> +  LongModeCompat16Bit,
>> +} SEV_ES_INSTRUCTION_MODE;
>> +
>> +//
>> +// Instruction size definition (for operand and address)
>> +//
>> +typedef enum {
>> +  Size8Bits            = 0,
>> +  Size16Bits,
>> +  Size32Bits,
>> +  Size64Bits,
>> +} SEV_ES_INSTRUCTION_SIZE;
>> +
>> +//
>> +// Intruction segment definition
>> +//
>> +typedef enum {
>> +  SegmentEs            = 0,
>> +  SegmentCs,
>> +  SegmentSs,
>> +  SegmentDs,
>> +  SegmentFs,
>> +  SegmentGs,
>> +} SEV_ES_INSTRUCTION_SEGMENT;
>> +
>> +//
>> +// Instruction rep function definition
>> +//
>> +typedef enum {
>> +  RepNone              = 0,
>> +  RepZ,
>> +  RepNZ,
>> +} SEV_ES_INSTRUCTION_REP;
>> +
>> +//
>> +// Instruction REX prefix definition
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT8  BitB:1;
>> +    UINT8  BitX:1;
>> +    UINT8  BitR:1;
>> +    UINT8  BitW:1;
>> +    UINT8  Rex:4;
>> +  } Bits;
>> +
>> +  UINT8  Uint8;
>> +} SEV_ES_INSTRUCTION_REX_PREFIX;
>> +
>> +//
>> +// Instruction ModRM definition
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT8  Rm:3;
>> +    UINT8  Reg:3;
>> +    UINT8  Mod:2;
>> +  } Bits;
>> +
>> +  UINT8  Uint8;
>> +} SEV_ES_INSTRUCTION_MODRM;
>> +
>> +typedef struct {
>> +  UINT8  Rm;
>> +  UINT8  Reg;
>> +  UINT8  Mod;
>> +} SEV_ES_INSTRUCTION_MODRM_EXT;
>> +
>> +//
>> +// Instruction SIB definition
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT8  Base:3;
>> +    UINT8  Index:3;
>> +    UINT8  Scale:2;
>> +  } Bits;
>> +
>> +  UINT8  Uint8;
>> +} SEV_ES_INSTRUCTION_SIB;
>> +
>> +typedef struct {
>> +  UINT8  Base;
>> +  UINT8  Index;
>> +  UINT8  Scale;
>> +} SEV_ES_INSTRUCTION_SIB_EXT;
>> +
>> +//
>> +// Instruction opcode definition
>> +//
>> +typedef struct {
>> +  SEV_ES_INSTRUCTION_MODRM_EXT  ModRm;
>> +
>> +  SEV_ES_INSTRUCTION_SIB_EXT    Sib;
>> +
>> +  UINTN                         RegData;
>> +  UINTN                         RmData;
>> +} SEV_ES_INSTRUCTION_OPCODE_EXT;
>> +
>> +//
>> +// Instruction parsing context definition
>> +//
>> +typedef struct {
>> +  GHCB                           *Ghcb;
>> +
>> +  SEV_ES_INSTRUCTION_MODE        Mode;
>> +  SEV_ES_INSTRUCTION_SIZE        DataSize;
>> +  SEV_ES_INSTRUCTION_SIZE        AddrSize;
>> +  BOOLEAN                        SegmentSpecified;
>> +  SEV_ES_INSTRUCTION_SEGMENT     Segment;
>> +  SEV_ES_INSTRUCTION_REP         RepMode;
>> +
>> +  UINT8                          *Begin;
>> +  UINT8                          *End;
>> +
>> +  UINT8                          *Prefixes;
>> +  UINT8                          *OpCodes;
>> +  UINT8                          *Displacement;
>> +  UINT8                          *Immediate;
>> +
>> +  SEV_ES_INSTRUCTION_REX_PREFIX  RexPrefix;
>> +
>> +  BOOLEAN                        ModRmPresent;
>> +  SEV_ES_INSTRUCTION_MODRM       ModRm;
>> +
>> +  BOOLEAN                        SibPresent;
>> +  SEV_ES_INSTRUCTION_SIB         Sib;
>> +
>> +  UINTN                          PrefixSize;
>> +  UINTN                          OpCodeSize;
>> +  UINTN                          DisplacementSize;
>> +  UINTN                          ImmediateSize;
>> +
>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  Ext;
>> +} SEV_ES_INSTRUCTION_DATA;
>> +
>> +//
>> +// Non-automatic Exit function prototype
>> +//
>> +typedef
>> +UINT64
>> +(*NAE_EXIT) (
>> +  GHCB                     *Ghcb,
>> +  EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  );
>> +
> 
> (1) From the typedefs above, can we move those that are defined in
> industry specs (such as AMD SEV specs) to header file(s)? For example,
> under OvmfPkg/Include/Register or OvmfPkg/Include/IndustryStandard.

Yes, I'll move them to a header file.

> 
>> +
>> +/**
>> +  Checks the GHCB to determine if the specified register has been marked valid.
>> +
>> +  The ValidBitmap area represents the areas of the GHCB that have been marked
>> +  valid. Return an indication of whether the area of the GHCB that holds the
>> +  specified register has been marked valid.
>> +
>> +  @param[in] Ghcb    Pointer to the Guest-Hypervisor Communication Block
>> +  @param[in] Reg     Offset in the GHCB of the register to check
>> +
>> +  @retval TRUE       Register has been marked vald in the GHCB
>> +  @retval FALSE      Register has not been marked valid in the GHCB
>> +
>> +**/
>> +STATIC
>> +BOOLEAN
>> +GhcbIsRegValid (
>> +  IN GHCB                *Ghcb,
>> +  IN GHCB_REGISTER       Reg
>> +  )
>> +{
>> +  UINT32  RegIndex;
>> +  UINT32  RegBit;
>> +
>> +  RegIndex = Reg / 8;
>> +  RegBit   = Reg & 0x07;
>> +
>> +  return (Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit));
>> +}
>> +
>> +/**
>> +  Marks a register as valid in the GHCB.
>> +
>> +  The ValidBitmap area represents the areas of the GHCB that have been marked
>> +  valid. Set the area of the GHCB that holds the specified register as valid.
>> +
>> +  @param[in, out] Ghcb    Pointer to the Guest-Hypervisor Communication Block
>> +  @param[in] Reg          Offset in the GHCB of the register to mark valid
>> +
>> +**/
>> +STATIC
>> +VOID
>> +GhcbSetRegValid (
>> +  IN OUT GHCB                *Ghcb,
>> +  IN     GHCB_REGISTER       Reg
>> +  )
>> +{
>> +  UINT32  RegIndex;
>> +  UINT32  RegBit;
>> +
>> +  RegIndex = Reg / 8;
>> +  RegBit   = Reg & 0x07;
>> +
>> +  Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
>> +}
>> +
>> +/**
>> +  Decode instruction prefixes.
>> +
>> +  Parse the instruction data to track the instruction prefixes that have
>> +  been used.
>> +
>> +  @param[in]      Regs             x64 processor context
>> +  @param[in, out] InstructionData  Instruction parsing context
>> +
>> +**/
>> +STATIC
>> +VOID
>> +DecodePrefixes (
>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  SEV_ES_INSTRUCTION_MODE  Mode;
>> +  SEV_ES_INSTRUCTION_SIZE  ModeDataSize;
>> +  SEV_ES_INSTRUCTION_SIZE  ModeAddrSize;
>> +  UINT8                    *Byte;
>> +
>> +  /*TODO: Determine current mode - 64-bit for now */
> 
> (2) I'm OK with a TODO comment, but please use the idiomatic style.

Will do.

> 
>> +  Mode = LongMode64Bit;
>> +  ModeDataSize = Size32Bits;
>> +  ModeAddrSize = Size64Bits;
>> +
>> +  InstructionData->Mode = Mode;
>> +  InstructionData->DataSize = ModeDataSize;
>> +  InstructionData->AddrSize = ModeAddrSize;
>> +
>> +  InstructionData->Prefixes = InstructionData->Begin;
>> +
>> +  Byte = InstructionData->Prefixes;
>> +  for ( ; ; Byte++, InstructionData->PrefixSize++) {
>> +    //
>> +    // Check the 0x40 to 0x4F range using an if statement here since some
>> +    // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
>> +    // 16 case statements below.
>> +    //
>> +    if ((*Byte >= 0x40) && (*Byte <= 0x4F)) {
>> +      InstructionData->RexPrefix.Uint8 = *Byte;
>> +      if (*Byte & 0x08)
>> +        InstructionData->DataSize = Size64Bits;
>> +      continue;
>> +    }
>> +
>> +    switch (*Byte) {
>> +    case 0x26:
>> +    case 0x2E:
>> +    case 0x36:
>> +    case 0x3E:
>> +      if (Mode != LongMode64Bit) {
>> +        InstructionData->SegmentSpecified = TRUE;
>> +        InstructionData->Segment = (*Byte >> 3) & 3;
>> +      }
>> +      break;
>> +
>> +    case 0x64:
>> +      InstructionData->SegmentSpecified = TRUE;
>> +      InstructionData->Segment = *Byte & 7;
>> +      break;
>> +
>> +    case 0x66:
>> +      if (!InstructionData->RexPrefix.Uint8) {
>> +        InstructionData->DataSize =
>> +          (Mode == LongMode64Bit)       ? Size16Bits :
>> +          (Mode == LongModeCompat32Bit) ? Size16Bits :
>> +          (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
>> +      }
>> +      break;
>> +
>> +    case 0x67:
>> +      InstructionData->AddrSize =
>> +        (Mode == LongMode64Bit)       ? Size32Bits :
>> +        (Mode == LongModeCompat32Bit) ? Size16Bits :
>> +        (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
>> +      break;
>> +
>> +    case 0xF0:
>> +      break;
>> +
>> +    case 0xF2:
>> +      InstructionData->RepMode = RepZ;
>> +      break;
>> +
>> +    case 0xF3:
>> +      InstructionData->RepMode = RepNZ;
>> +      break;
>> +
>> +    default:
>> +      InstructionData->OpCodes = Byte;
>> +      InstructionData->OpCodeSize = (*Byte == 0x0F) ? 2 : 1;
>> +
>> +      InstructionData->End = Byte + InstructionData->OpCodeSize;
>> +      InstructionData->Displacement = InstructionData->End;
>> +      InstructionData->Immediate = InstructionData->End;
>> +      return;
>> +    }
>> +  }
>> +}
> 
> (3) Can we introduce macros or enum constants for the prefixes?
> 
> Although, I seem to remember that QEMU's TCG and even KVM's instruction
> parser uses open-coded magic constants for the prefixes, so the above
> code would not be without precedent.
> 
> Can we please add comments (stating the prefix names) near the case
> labels at least?

Will do.

> 
>> +
>> +/**
>> +  Determine instruction length
>> +
>> +  Return the total length of the parsed instruction.
>> +
>> +  @param[in] InstructionData  Instruction parsing context
>> +
>> +  @retval                     Length of parsed instruction
> 
> (4) @retval is for specific return values (enum constants, macros etc).
> For general descriptions, please use @return, not @retval.
> 
> Please review the rest of the patches from this angle.

I'll review all patches for @return/@retval inconsistencies.

> 
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +InstructionLength (
>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  return (UINT64) (InstructionData->End - InstructionData->Begin);
>> +}
>> +
>> +/**
>> +  Initialize the instruction parsing context.
>> +
>> +  Initialize the instruction parsing context, which includes decoding the
>> +  instruction prefixes.
>> +
>> +  @param[in, out] InstructionData  Instruction parsing context
>> +  @param[in]      Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in]      Regs             x64 processor context
>> +
>> +**/
>> +STATIC
>> +VOID
>> +InitInstructionData (
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
>> +  IN     GHCB                     *Ghcb,
>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs
>> +  )
>> +{
>> +  SetMem (InstructionData, sizeof (*InstructionData), 0);
>> +  InstructionData->Ghcb = Ghcb;
>> +  InstructionData->Begin = (UINT8 *) Regs->Rip;
>> +  InstructionData->End = (UINT8 *) Regs->Rip;
>> +
>> +  DecodePrefixes (Regs, InstructionData);
>> +}
>> +
>> +/**
>> +  Report an unsupported event to the hypervisor
>> +
>> +  Use the VMGEXIT support to report an unsupported event to the hypervisor.
>> +
>> +  @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                              Block
>> +  @param[in] Regs             x64 processor context
>> +  @param[in] InstructionData  Instruction parsing context
>> +
>> +  @retval                     New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +UnsupportedExit (
>> +  IN GHCB                     *Ghcb,
>> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  Status;
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
>> +  if (Status == 0) {
>> +    GHCB_EVENT_INJECTION  Event;
>> +
>> +    Event.Uint64 = 0;
>> +    Event.Elements.Vector = GP_EXCEPTION;
>> +    Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
>> +    Event.Elements.Valid  = 1;
>> +
>> +    Status = Event.Uint64;
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +#define IOIO_TYPE_STR       (1 << 2)
>> +#define IOIO_TYPE_IN        1
>> +#define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
>> +#define IOIO_TYPE_OUT       0
>> +#define IOIO_TYPE_OUTS      (IOIO_TYPE_OUT | IOIO_TYPE_STR)
>> +
>> +#define IOIO_REP            (1 << 3)
>> +
>> +#define IOIO_ADDR_64        (1 << 9)
>> +#define IOIO_ADDR_32        (1 << 8)
>> +#define IOIO_ADDR_16        (1 << 7)
>> +
>> +#define IOIO_DATA_32        (1 << 6)
>> +#define IOIO_DATA_16        (1 << 5)
>> +#define IOIO_DATA_8         (1 << 4)
>> +#define IOIO_DATA_BYTES(x)  (((x) & 0x70) >> 4)
>> +
>> +#define IOIO_SEG_ES         (0 << 10)
>> +#define IOIO_SEG_DS         (3 << 10)
> 
> (5) I feel like these macros belong in:
> 
>    MdePkg/Include/Register/Amd/Ghcb.h
> 
> Do you agree?

Yes, I can put them in Ghcb.h or some other file.

> 
> If that exact header file is not the best, then I'd request a new header
> file under OvmfPkg/Include/Register.
> 
> (6) I'd also suggest using BIT2, BIT3, BIT9, rather than the open-coded
> shifts. BITx reads more natural in edk2 source.

Will do.

> 
> 
>> +
>> +/**
>> +  Build the IOIO event information.
>> +
>> +  The IOIO event information identifies the type of IO operation to be performed
>> +  by the hypervisor. Build this information based on the instruction data.
>> +
>> +  @param[in]       Regs             x64 processor context
>> +  @param[in, out]  InstructionData  Instruction parsing context
>> +
>> +  @retval Others                    IOIO event information value
> 
> (7) Even though "@retval Others" is a common pattern in edk2, I consider
> it a mis-use of "@retval". Whenever we're tempted to write "@retval
> Others", we should just use "@return". Here, for example:
> 
>    @return  IOIO event information values.
> 
> If you strongly disagree, I won't insist; I'm not trying to create
> busywork for you. Otherwise, please check the rest of the OvmfPkg
> patches for "@retval Others".
> 
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +IoioExitInfo (
>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  ExitInfo;
>> +
>> +  ExitInfo = 0;
>> +
>> +  switch (*(InstructionData->OpCodes)) {
>> +  // IN immediate opcodes
> 
> (8) Right, I love these comments; please do use one of the idiomatic
> comment styles though. Either prepend and append empty "//" lines, or
> move the single-line "//" to the right of the case labels.

Will do.

> 
>> +  case 0xE4:
>> +  case 0xE5:
>> +    InstructionData->ImmediateSize = 1;
>> +    InstructionData->End++;
>> +    ExitInfo |= IOIO_TYPE_IN;
>> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
>> +    break;
>> +
>> +  // OUT immediate opcodes
>> +  case 0xE6:
>> +  case 0xE7:
>> +    InstructionData->ImmediateSize = 1;
>> +    InstructionData->End++;
>> +    ExitInfo |= IOIO_TYPE_OUT;
>> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
>> +    break;
>> +
>> +  // IN register opcodes
>> +  case 0xEC:
>> +  case 0xED:
>> +    ExitInfo |= IOIO_TYPE_IN;
>> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
>> +    break;
>> +
>> +  // OUT register opcodes
>> +  case 0xEE:
>> +  case 0xEF:
>> +    ExitInfo |= IOIO_TYPE_OUT;
>> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
>> +    break;
>> +
>> +  default:
>> +    return 0;
>> +  }
>> +
>> +  switch (*(InstructionData->OpCodes)) {
>> +  case 0xE4:
>> +  case 0xE6:
>> +  case 0xEC:
>> +  case 0xEE:
>> +    // Single-byte opcodes
> 
> (9) Please make both the location and the style of this comment
> consistent with (8).

Yup.

> 
> All my comments are superficial, and that's fine. I totally intend to
> let you do what you need to do in this library (I can tell that writing
> a new instruction decoder must have been hellish work), so I don't want
> to get in your way -- just please make this easier to browse with "edk2
> eyes".
> 
> I'm ready to ACK the patch once all comments have been addressed -- I'm
> not giving an A-b at once because some of my requests / proposals need
> concrete values filled in (such as header file names).
> 

Thanks!
Tom

> Thanks!
> Laszlo
> 
>> +    ExitInfo |= IOIO_DATA_8;
>> +    break;
>> +
>> +  default:
>> +    // Length determined by instruction parsing
>> +    ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
>> +                                                          : IOIO_DATA_32;
>> +  }
>> +
>> +  switch (InstructionData->AddrSize) {
>> +  case Size16Bits:
>> +    ExitInfo |= IOIO_ADDR_16;
>> +    break;
>> +
>> +  case Size32Bits:
>> +    ExitInfo |= IOIO_ADDR_32;
>> +    break;
>> +
>> +  case Size64Bits:
>> +    ExitInfo |= IOIO_ADDR_64;
>> +    break;
>> +
>> +  default:
>> +    break;
>> +  }
>> +
>> +  if (InstructionData->RepMode) {
>> +    ExitInfo |= IOIO_REP;
>> +  }
>> +
>> +  return ExitInfo;
>> +}
>> +
>> +/**
>> +  Handle an IOIO event.
>> +
>> +  Use the VMGEXIT instruction to handle an IOIO event.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +IoioExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  ExitInfo1, Status;
>> +
>> +  ExitInfo1 = IoioExitInfo (Regs, InstructionData);
>> +  if (!ExitInfo1) {
>> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +  }
>> +
>> +  if (ExitInfo1 & IOIO_TYPE_IN) {
>> +    Ghcb->SaveArea.Rax = 0;
>> +  } else {
>> +    CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
>> +  }
>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
>> +  if (Status) {
>> +    return Status;
>> +  }
>> +
>> +  if (ExitInfo1 & IOIO_TYPE_IN) {
>> +    if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
>> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +    }
>> +    CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
>> +  }
>> +
>> +  return 0;
>> +}
>> +
>>   /**
>>     Handle a #VC exception.
>>   
>> @@ -38,6 +605,8 @@ VmgExitHandleVc (
>>     MSR_SEV_ES_GHCB_REGISTER  Msr;
>>     EFI_SYSTEM_CONTEXT_X64    *Regs;
>>     GHCB                      *Ghcb;
>> +  NAE_EXIT                  NaeExit;
>> +  SEV_ES_INSTRUCTION_DATA   InstructionData;
>>     UINT64                    ExitCode, Status;
>>     EFI_STATUS                VcRet;
>>   
>> @@ -54,24 +623,31 @@ VmgExitHandleVc (
>>   
>>     ExitCode = Regs->ExceptionData;
>>     switch (ExitCode) {
>> +  case SVM_EXIT_IOIO_PROT:
>> +    NaeExit = IoioExit;
>> +    break;
>> +
>>     default:
>> -    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
>> -    if (Status == 0) {
>> -      Regs->ExceptionData = 0;
>> -      *ExceptionType = GP_EXCEPTION;
>> +    NaeExit = UnsupportedExit;
>> +  }
>> +
>> +  InitInstructionData (&InstructionData, Ghcb, Regs);
>> +
>> +  Status = NaeExit (Ghcb, Regs, &InstructionData);
>> +  if (Status == 0) {
>> +    Regs->Rip += InstructionLength (&InstructionData);
>> +  } else {
>> +    GHCB_EVENT_INJECTION  Event;
>> +
>> +    Event.Uint64 = Status;
>> +    if (Event.Elements.ErrorCodeValid) {
>> +      Regs->ExceptionData = Event.Elements.ErrorCode;
>>       } else {
>> -      GHCB_EVENT_INJECTION  Event;
>> -
>> -      Event.Uint64 = Status;
>> -      if (Event.Elements.ErrorCodeValid) {
>> -        Regs->ExceptionData = Event.Elements.ErrorCode;
>> -      } else {
>> -        Regs->ExceptionData = 0;
>> -      }
>> -
>> -      *ExceptionType = Event.Elements.Vector;
>> +      Regs->ExceptionData = 0;
>>       }
>>   
>> +    *ExceptionType = Event.Elements.Vector;
>> +
>>       VcRet = EFI_PROTOCOL_ERROR;
>>     }
>>   
>>
> 

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

* Re: [edk2-devel] [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events
  2020-05-22 10:05     ` Laszlo Ersek
@ 2020-05-22 13:41       ` Lendacky, Thomas
  0 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-22 13:41 UTC (permalink / raw)
  To: devel, lersek
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/22/20 5:05 AM, Laszlo Ersek wrote:
> On 05/21/20 19:25, Laszlo Ersek wrote:
>> On 05/19/20 23:50, Lendacky, Thomas wrote:
>>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C6eeb665fe5884b34f2b408d7fe37afcb%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637257387447015060&amp;sdata=zteeG%2B8CL03l1w6SrqcadCH7tSTe8F5FmLhBRxLFy4k%3D&amp;reserved=0
>>>
>>> Under SEV-ES, a IOIO_PROT intercept generates a #VC exception. VMGEXIT
>>> must be used to allow the hypervisor to handle this intercept.
>>>
>>> Add support to construct the required GHCB values to support a IOIO_PROT
>>> NAE event.  Parse the instruction that generated the #VC exception,
>>> setting the required register values in the GHCB and creating the proper
>>> SW_EXITINFO1 value in the GHCB.
>>>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>>> ---
>>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 604 +++++++++++++++++-
>>>   1 file changed, 590 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> index 036f030d6b34..b4578ae922c1 100644
>>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> @@ -12,6 +12,573 @@
>>>   #include <Library/VmgExitLib.h>
>>>   #include <Register/Amd/Msr.h>
>>>   
>>> +//
>>> +// Instruction execution mode definition
>>> +//
>>> +typedef enum {
>>> +  LongMode64Bit        = 0,
>>> +  LongModeCompat32Bit,
>>> +  LongModeCompat16Bit,
>>> +} SEV_ES_INSTRUCTION_MODE;
>>> +
>>> +//
>>> +// Instruction size definition (for operand and address)
>>> +//
>>> +typedef enum {
>>> +  Size8Bits            = 0,
>>> +  Size16Bits,
>>> +  Size32Bits,
>>> +  Size64Bits,
>>> +} SEV_ES_INSTRUCTION_SIZE;
>>> +
>>> +//
>>> +// Intruction segment definition
>>> +//
>>> +typedef enum {
>>> +  SegmentEs            = 0,
>>> +  SegmentCs,
>>> +  SegmentSs,
>>> +  SegmentDs,
>>> +  SegmentFs,
>>> +  SegmentGs,
>>> +} SEV_ES_INSTRUCTION_SEGMENT;
>>> +
>>> +//
>>> +// Instruction rep function definition
>>> +//
>>> +typedef enum {
>>> +  RepNone              = 0,
>>> +  RepZ,
>>> +  RepNZ,
>>> +} SEV_ES_INSTRUCTION_REP;
>>> +
>>> +//
>>> +// Instruction REX prefix definition
>>> +//
>>> +typedef union {
>>> +  struct {
>>> +    UINT8  BitB:1;
>>> +    UINT8  BitX:1;
>>> +    UINT8  BitR:1;
>>> +    UINT8  BitW:1;
>>> +    UINT8  Rex:4;
>>> +  } Bits;
>>> +
>>> +  UINT8  Uint8;
>>> +} SEV_ES_INSTRUCTION_REX_PREFIX;
>>> +
>>> +//
>>> +// Instruction ModRM definition
>>> +//
>>> +typedef union {
>>> +  struct {
>>> +    UINT8  Rm:3;
>>> +    UINT8  Reg:3;
>>> +    UINT8  Mod:2;
>>> +  } Bits;
>>> +
>>> +  UINT8  Uint8;
>>> +} SEV_ES_INSTRUCTION_MODRM;
>>> +
>>> +typedef struct {
>>> +  UINT8  Rm;
>>> +  UINT8  Reg;
>>> +  UINT8  Mod;
>>> +} SEV_ES_INSTRUCTION_MODRM_EXT;
>>> +
>>> +//
>>> +// Instruction SIB definition
>>> +//
>>> +typedef union {
>>> +  struct {
>>> +    UINT8  Base:3;
>>> +    UINT8  Index:3;
>>> +    UINT8  Scale:2;
>>> +  } Bits;
>>> +
>>> +  UINT8  Uint8;
>>> +} SEV_ES_INSTRUCTION_SIB;
>>> +
>>> +typedef struct {
>>> +  UINT8  Base;
>>> +  UINT8  Index;
>>> +  UINT8  Scale;
>>> +} SEV_ES_INSTRUCTION_SIB_EXT;
>>> +
>>> +//
>>> +// Instruction opcode definition
>>> +//
>>> +typedef struct {
>>> +  SEV_ES_INSTRUCTION_MODRM_EXT  ModRm;
>>> +
>>> +  SEV_ES_INSTRUCTION_SIB_EXT    Sib;
>>> +
>>> +  UINTN                         RegData;
>>> +  UINTN                         RmData;
>>> +} SEV_ES_INSTRUCTION_OPCODE_EXT;
>>> +
>>> +//
>>> +// Instruction parsing context definition
>>> +//
>>> +typedef struct {
>>> +  GHCB                           *Ghcb;
>>> +
>>> +  SEV_ES_INSTRUCTION_MODE        Mode;
>>> +  SEV_ES_INSTRUCTION_SIZE        DataSize;
>>> +  SEV_ES_INSTRUCTION_SIZE        AddrSize;
>>> +  BOOLEAN                        SegmentSpecified;
>>> +  SEV_ES_INSTRUCTION_SEGMENT     Segment;
>>> +  SEV_ES_INSTRUCTION_REP         RepMode;
>>> +
>>> +  UINT8                          *Begin;
>>> +  UINT8                          *End;
>>> +
>>> +  UINT8                          *Prefixes;
>>> +  UINT8                          *OpCodes;
>>> +  UINT8                          *Displacement;
>>> +  UINT8                          *Immediate;
>>> +
>>> +  SEV_ES_INSTRUCTION_REX_PREFIX  RexPrefix;
>>> +
>>> +  BOOLEAN                        ModRmPresent;
>>> +  SEV_ES_INSTRUCTION_MODRM       ModRm;
>>> +
>>> +  BOOLEAN                        SibPresent;
>>> +  SEV_ES_INSTRUCTION_SIB         Sib;
>>> +
>>> +  UINTN                          PrefixSize;
>>> +  UINTN                          OpCodeSize;
>>> +  UINTN                          DisplacementSize;
>>> +  UINTN                          ImmediateSize;
>>> +
>>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  Ext;
>>> +} SEV_ES_INSTRUCTION_DATA;
>>> +
>>> +//
>>> +// Non-automatic Exit function prototype
>>> +//
>>> +typedef
>>> +UINT64
>>> +(*NAE_EXIT) (
>>> +  GHCB                     *Ghcb,
>>> +  EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  );
>>> +
>>
>> (1) From the typedefs above, can we move those that are defined in
>> industry specs (such as AMD SEV specs) to header file(s)? For example,
>> under OvmfPkg/Include/Register or OvmfPkg/Include/IndustryStandard.
>>
>>> +
>>> +/**
>>> +  Checks the GHCB to determine if the specified register has been marked valid.
>>> +
>>> +  The ValidBitmap area represents the areas of the GHCB that have been marked
>>> +  valid. Return an indication of whether the area of the GHCB that holds the
>>> +  specified register has been marked valid.
>>> +
>>> +  @param[in] Ghcb    Pointer to the Guest-Hypervisor Communication Block
>>> +  @param[in] Reg     Offset in the GHCB of the register to check
>>> +
>>> +  @retval TRUE       Register has been marked vald in the GHCB
>>> +  @retval FALSE      Register has not been marked valid in the GHCB
>>> +
>>> +**/
>>> +STATIC
>>> +BOOLEAN
>>> +GhcbIsRegValid (
>>> +  IN GHCB                *Ghcb,
>>> +  IN GHCB_REGISTER       Reg
>>> +  )
>>> +{
>>> +  UINT32  RegIndex;
>>> +  UINT32  RegBit;
>>> +
>>> +  RegIndex = Reg / 8;
>>> +  RegBit   = Reg & 0x07;
>>> +
>>> +  return (Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit));
> 
> (10) Please check the patches for uses of the bitwise AND operator,
> where the result is considered as a logical value.
> 
> In those contexts, the edk2 coding style needs an explicit comparison
> against 0. For example, the above would be:
> 
>    return (Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0;
> 
> 
> This is actually more than just a style question, generally speaking.
> BOOLEAN in edk2 is a typedef to UINT8 (well, more precisely, to
> "unsigned char"). It is not like the _Bool type in ISO C99.
> 
> ISO C99 says about _Bool, in section "6.3.1.2 Boolean type": "When any
> scalar value is converted to _Bool, the result is 0 if the value
> compares equal to 0; otherwise, the result is 1." (Footnote: "NaNs do
> not compare equal to 0 and thus convert to 1.")
> 
> However, BOOLEAN is "unsigned char" and not _Bool; therefore, if you do
> for example:
> 
> BOOLEAN
> IsBit8Set (
>    IN UINT16 Value
>    )
> {
>    return Value & BIT8;
> }
> 
> then IsBit8Set (0x100) will return FALSE -- converting 0x100 to
> "unsigned char" yields 0.
> 
> In edk2, in every context (not just in return statements) where we have
> (Expr1 & Expr2) consumed as a logical result, we're supposed to write
> ((Expr1 & Expr2) != 0). (The inner parens are important as bit-AND has
> weaker binding than "==" and "!=".)
> 
> Please check the rest of the patches for this.

Yes, I'll audit all the patches for this.

Thanks!
Tom

> 
> Thanks!
> Laszlo
> 
>>> +}
>>> +
>>> +/**
>>> +  Marks a register as valid in the GHCB.
>>> +
>>> +  The ValidBitmap area represents the areas of the GHCB that have been marked
>>> +  valid. Set the area of the GHCB that holds the specified register as valid.
>>> +
>>> +  @param[in, out] Ghcb    Pointer to the Guest-Hypervisor Communication Block
>>> +  @param[in] Reg          Offset in the GHCB of the register to mark valid
>>> +
>>> +**/
>>> +STATIC
>>> +VOID
>>> +GhcbSetRegValid (
>>> +  IN OUT GHCB                *Ghcb,
>>> +  IN     GHCB_REGISTER       Reg
>>> +  )
>>> +{
>>> +  UINT32  RegIndex;
>>> +  UINT32  RegBit;
>>> +
>>> +  RegIndex = Reg / 8;
>>> +  RegBit   = Reg & 0x07;
>>> +
>>> +  Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
>>> +}
>>> +
>>> +/**
>>> +  Decode instruction prefixes.
>>> +
>>> +  Parse the instruction data to track the instruction prefixes that have
>>> +  been used.
>>> +
>>> +  @param[in]      Regs             x64 processor context
>>> +  @param[in, out] InstructionData  Instruction parsing context
>>> +
>>> +**/
>>> +STATIC
>>> +VOID
>>> +DecodePrefixes (
>>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  SEV_ES_INSTRUCTION_MODE  Mode;
>>> +  SEV_ES_INSTRUCTION_SIZE  ModeDataSize;
>>> +  SEV_ES_INSTRUCTION_SIZE  ModeAddrSize;
>>> +  UINT8                    *Byte;
>>> +
>>> +  /*TODO: Determine current mode - 64-bit for now */
>>
>> (2) I'm OK with a TODO comment, but please use the idiomatic style.
>>
>>> +  Mode = LongMode64Bit;
>>> +  ModeDataSize = Size32Bits;
>>> +  ModeAddrSize = Size64Bits;
>>> +
>>> +  InstructionData->Mode = Mode;
>>> +  InstructionData->DataSize = ModeDataSize;
>>> +  InstructionData->AddrSize = ModeAddrSize;
>>> +
>>> +  InstructionData->Prefixes = InstructionData->Begin;
>>> +
>>> +  Byte = InstructionData->Prefixes;
>>> +  for ( ; ; Byte++, InstructionData->PrefixSize++) {
>>> +    //
>>> +    // Check the 0x40 to 0x4F range using an if statement here since some
>>> +    // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
>>> +    // 16 case statements below.
>>> +    //
>>> +    if ((*Byte >= 0x40) && (*Byte <= 0x4F)) {
>>> +      InstructionData->RexPrefix.Uint8 = *Byte;
>>> +      if (*Byte & 0x08)
>>> +        InstructionData->DataSize = Size64Bits;
>>> +      continue;
>>> +    }
>>> +
>>> +    switch (*Byte) {
>>> +    case 0x26:
>>> +    case 0x2E:
>>> +    case 0x36:
>>> +    case 0x3E:
>>> +      if (Mode != LongMode64Bit) {
>>> +        InstructionData->SegmentSpecified = TRUE;
>>> +        InstructionData->Segment = (*Byte >> 3) & 3;
>>> +      }
>>> +      break;
>>> +
>>> +    case 0x64:
>>> +      InstructionData->SegmentSpecified = TRUE;
>>> +      InstructionData->Segment = *Byte & 7;
>>> +      break;
>>> +
>>> +    case 0x66:
>>> +      if (!InstructionData->RexPrefix.Uint8) {
>>> +        InstructionData->DataSize =
>>> +          (Mode == LongMode64Bit)       ? Size16Bits :
>>> +          (Mode == LongModeCompat32Bit) ? Size16Bits :
>>> +          (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
>>> +      }
>>> +      break;
>>> +
>>> +    case 0x67:
>>> +      InstructionData->AddrSize =
>>> +        (Mode == LongMode64Bit)       ? Size32Bits :
>>> +        (Mode == LongModeCompat32Bit) ? Size16Bits :
>>> +        (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
>>> +      break;
>>> +
>>> +    case 0xF0:
>>> +      break;
>>> +
>>> +    case 0xF2:
>>> +      InstructionData->RepMode = RepZ;
>>> +      break;
>>> +
>>> +    case 0xF3:
>>> +      InstructionData->RepMode = RepNZ;
>>> +      break;
>>> +
>>> +    default:
>>> +      InstructionData->OpCodes = Byte;
>>> +      InstructionData->OpCodeSize = (*Byte == 0x0F) ? 2 : 1;
>>> +
>>> +      InstructionData->End = Byte + InstructionData->OpCodeSize;
>>> +      InstructionData->Displacement = InstructionData->End;
>>> +      InstructionData->Immediate = InstructionData->End;
>>> +      return;
>>> +    }
>>> +  }
>>> +}
>>
>> (3) Can we introduce macros or enum constants for the prefixes?
>>
>> Although, I seem to remember that QEMU's TCG and even KVM's instruction
>> parser uses open-coded magic constants for the prefixes, so the above
>> code would not be without precedent.
>>
>> Can we please add comments (stating the prefix names) near the case
>> labels at least?
>>
>>> +
>>> +/**
>>> +  Determine instruction length
>>> +
>>> +  Return the total length of the parsed instruction.
>>> +
>>> +  @param[in] InstructionData  Instruction parsing context
>>> +
>>> +  @retval                     Length of parsed instruction
>>
>> (4) @retval is for specific return values (enum constants, macros etc).
>> For general descriptions, please use @return, not @retval.
>>
>> Please review the rest of the patches from this angle.
>>
>>> +
>>> +**/
>>> +STATIC
>>> +UINT64
>>> +InstructionLength (
>>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  return (UINT64) (InstructionData->End - InstructionData->Begin);
>>> +}
>>> +
>>> +/**
>>> +  Initialize the instruction parsing context.
>>> +
>>> +  Initialize the instruction parsing context, which includes decoding the
>>> +  instruction prefixes.
>>> +
>>> +  @param[in, out] InstructionData  Instruction parsing context
>>> +  @param[in]      Ghcb             Pointer to the Guest-Hypervisor Communication
>>> +                                   Block
>>> +  @param[in]      Regs             x64 processor context
>>> +
>>> +**/
>>> +STATIC
>>> +VOID
>>> +InitInstructionData (
>>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
>>> +  IN     GHCB                     *Ghcb,
>>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs
>>> +  )
>>> +{
>>> +  SetMem (InstructionData, sizeof (*InstructionData), 0);
>>> +  InstructionData->Ghcb = Ghcb;
>>> +  InstructionData->Begin = (UINT8 *) Regs->Rip;
>>> +  InstructionData->End = (UINT8 *) Regs->Rip;
>>> +
>>> +  DecodePrefixes (Regs, InstructionData);
>>> +}
>>> +
>>> +/**
>>> +  Report an unsupported event to the hypervisor
>>> +
>>> +  Use the VMGEXIT support to report an unsupported event to the hypervisor.
>>> +
>>> +  @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication
>>> +                              Block
>>> +  @param[in] Regs             x64 processor context
>>> +  @param[in] InstructionData  Instruction parsing context
>>> +
>>> +  @retval                     New exception value to propagate
>>> +
>>> +**/
>>> +STATIC
>>> +UINT64
>>> +UnsupportedExit (
>>> +  IN GHCB                     *Ghcb,
>>> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  UINT64  Status;
>>> +
>>> +  Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
>>> +  if (Status == 0) {
>>> +    GHCB_EVENT_INJECTION  Event;
>>> +
>>> +    Event.Uint64 = 0;
>>> +    Event.Elements.Vector = GP_EXCEPTION;
>>> +    Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
>>> +    Event.Elements.Valid  = 1;
>>> +
>>> +    Status = Event.Uint64;
>>> +  }
>>> +
>>> +  return Status;
>>> +}
>>> +
>>> +#define IOIO_TYPE_STR       (1 << 2)
>>> +#define IOIO_TYPE_IN        1
>>> +#define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
>>> +#define IOIO_TYPE_OUT       0
>>> +#define IOIO_TYPE_OUTS      (IOIO_TYPE_OUT | IOIO_TYPE_STR)
>>> +
>>> +#define IOIO_REP            (1 << 3)
>>> +
>>> +#define IOIO_ADDR_64        (1 << 9)
>>> +#define IOIO_ADDR_32        (1 << 8)
>>> +#define IOIO_ADDR_16        (1 << 7)
>>> +
>>> +#define IOIO_DATA_32        (1 << 6)
>>> +#define IOIO_DATA_16        (1 << 5)
>>> +#define IOIO_DATA_8         (1 << 4)
>>> +#define IOIO_DATA_BYTES(x)  (((x) & 0x70) >> 4)
>>> +
>>> +#define IOIO_SEG_ES         (0 << 10)
>>> +#define IOIO_SEG_DS         (3 << 10)
>>
>> (5) I feel like these macros belong in:
>>
>>    MdePkg/Include/Register/Amd/Ghcb.h
>>
>> Do you agree?
>>
>> If that exact header file is not the best, then I'd request a new header
>> file under OvmfPkg/Include/Register.
>>
>> (6) I'd also suggest using BIT2, BIT3, BIT9, rather than the open-coded
>> shifts. BITx reads more natural in edk2 source.
>>
>>
>>> +
>>> +/**
>>> +  Build the IOIO event information.
>>> +
>>> +  The IOIO event information identifies the type of IO operation to be performed
>>> +  by the hypervisor. Build this information based on the instruction data.
>>> +
>>> +  @param[in]       Regs             x64 processor context
>>> +  @param[in, out]  InstructionData  Instruction parsing context
>>> +
>>> +  @retval Others                    IOIO event information value
>>
>> (7) Even though "@retval Others" is a common pattern in edk2, I consider
>> it a mis-use of "@retval". Whenever we're tempted to write "@retval
>> Others", we should just use "@return". Here, for example:
>>
>>    @return  IOIO event information values.
>>
>> If you strongly disagree, I won't insist; I'm not trying to create
>> busywork for you. Otherwise, please check the rest of the OvmfPkg
>> patches for "@retval Others".
>>
>>> +
>>> +**/
>>> +STATIC
>>> +UINT64
>>> +IoioExitInfo (
>>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  UINT64  ExitInfo;
>>> +
>>> +  ExitInfo = 0;
>>> +
>>> +  switch (*(InstructionData->OpCodes)) {
>>> +  // IN immediate opcodes
>>
>> (8) Right, I love these comments; please do use one of the idiomatic
>> comment styles though. Either prepend and append empty "//" lines, or
>> move the single-line "//" to the right of the case labels.
>>
>>> +  case 0xE4:
>>> +  case 0xE5:
>>> +    InstructionData->ImmediateSize = 1;
>>> +    InstructionData->End++;
>>> +    ExitInfo |= IOIO_TYPE_IN;
>>> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
>>> +    break;
>>> +
>>> +  // OUT immediate opcodes
>>> +  case 0xE6:
>>> +  case 0xE7:
>>> +    InstructionData->ImmediateSize = 1;
>>> +    InstructionData->End++;
>>> +    ExitInfo |= IOIO_TYPE_OUT;
>>> +    ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
>>> +    break;
>>> +
>>> +  // IN register opcodes
>>> +  case 0xEC:
>>> +  case 0xED:
>>> +    ExitInfo |= IOIO_TYPE_IN;
>>> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
>>> +    break;
>>> +
>>> +  // OUT register opcodes
>>> +  case 0xEE:
>>> +  case 0xEF:
>>> +    ExitInfo |= IOIO_TYPE_OUT;
>>> +    ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
>>> +    break;
>>> +
>>> +  default:
>>> +    return 0;
>>> +  }
>>> +
>>> +  switch (*(InstructionData->OpCodes)) {
>>> +  case 0xE4:
>>> +  case 0xE6:
>>> +  case 0xEC:
>>> +  case 0xEE:
>>> +    // Single-byte opcodes
>>
>> (9) Please make both the location and the style of this comment
>> consistent with (8).
>>
>> All my comments are superficial, and that's fine. I totally intend to
>> let you do what you need to do in this library (I can tell that writing
>> a new instruction decoder must have been hellish work), so I don't want
>> to get in your way -- just please make this easier to browse with "edk2
>> eyes".
>>
>> I'm ready to ACK the patch once all comments have been addressed -- I'm
>> not giving an A-b at once because some of my requests / proposals need
>> concrete values filled in (such as header file names).
>>
>> Thanks!
>> Laszlo
>>
>>> +    ExitInfo |= IOIO_DATA_8;
>>> +    break;
>>> +
>>> +  default:
>>> +    // Length determined by instruction parsing
>>> +    ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
>>> +                                                          : IOIO_DATA_32;
>>> +  }
>>> +
>>> +  switch (InstructionData->AddrSize) {
>>> +  case Size16Bits:
>>> +    ExitInfo |= IOIO_ADDR_16;
>>> +    break;
>>> +
>>> +  case Size32Bits:
>>> +    ExitInfo |= IOIO_ADDR_32;
>>> +    break;
>>> +
>>> +  case Size64Bits:
>>> +    ExitInfo |= IOIO_ADDR_64;
>>> +    break;
>>> +
>>> +  default:
>>> +    break;
>>> +  }
>>> +
>>> +  if (InstructionData->RepMode) {
>>> +    ExitInfo |= IOIO_REP;
>>> +  }
>>> +
>>> +  return ExitInfo;
>>> +}
>>> +
>>> +/**
>>> +  Handle an IOIO event.
>>> +
>>> +  Use the VMGEXIT instruction to handle an IOIO event.
>>> +
>>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>>> +                                   Block
>>> +  @param[in, out] Regs             x64 processor context
>>> +  @param[in]      InstructionData  Instruction parsing context
>>> +
>>> +  @retval 0                        Event handled successfully
>>> +  @retval Others                   New exception value to propagate
>>> +
>>> +**/
>>> +STATIC
>>> +UINT64
>>> +IoioExit (
>>> +  IN OUT GHCB                     *Ghcb,
>>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  UINT64  ExitInfo1, Status;
>>> +
>>> +  ExitInfo1 = IoioExitInfo (Regs, InstructionData);
>>> +  if (!ExitInfo1) {
>>> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
>>> +  }
>>> +
>>> +  if (ExitInfo1 & IOIO_TYPE_IN) {
>>> +    Ghcb->SaveArea.Rax = 0;
>>> +  } else {
>>> +    CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
>>> +  }
>>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>>> +
>>> +  Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
>>> +  if (Status) {
>>> +    return Status;
>>> +  }
>>> +
>>> +  if (ExitInfo1 & IOIO_TYPE_IN) {
>>> +    if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
>>> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
>>> +    }
>>> +    CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
>>> +  }
>>> +
>>> +  return 0;
>>> +}
>>> +
>>>   /**
>>>     Handle a #VC exception.
>>>   
>>> @@ -38,6 +605,8 @@ VmgExitHandleVc (
>>>     MSR_SEV_ES_GHCB_REGISTER  Msr;
>>>     EFI_SYSTEM_CONTEXT_X64    *Regs;
>>>     GHCB                      *Ghcb;
>>> +  NAE_EXIT                  NaeExit;
>>> +  SEV_ES_INSTRUCTION_DATA   InstructionData;
>>>     UINT64                    ExitCode, Status;
>>>     EFI_STATUS                VcRet;
>>>   
>>> @@ -54,24 +623,31 @@ VmgExitHandleVc (
>>>   
>>>     ExitCode = Regs->ExceptionData;
>>>     switch (ExitCode) {
>>> +  case SVM_EXIT_IOIO_PROT:
>>> +    NaeExit = IoioExit;
>>> +    break;
>>> +
>>>     default:
>>> -    Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);
>>> -    if (Status == 0) {
>>> -      Regs->ExceptionData = 0;
>>> -      *ExceptionType = GP_EXCEPTION;
>>> +    NaeExit = UnsupportedExit;
>>> +  }
>>> +
>>> +  InitInstructionData (&InstructionData, Ghcb, Regs);
>>> +
>>> +  Status = NaeExit (Ghcb, Regs, &InstructionData);
>>> +  if (Status == 0) {
>>> +    Regs->Rip += InstructionLength (&InstructionData);
>>> +  } else {
>>> +    GHCB_EVENT_INJECTION  Event;
>>> +
>>> +    Event.Uint64 = Status;
>>> +    if (Event.Elements.ErrorCodeValid) {
>>> +      Regs->ExceptionData = Event.Elements.ErrorCode;
>>>       } else {
>>> -      GHCB_EVENT_INJECTION  Event;
>>> -
>>> -      Event.Uint64 = Status;
>>> -      if (Event.Elements.ErrorCodeValid) {
>>> -        Regs->ExceptionData = Event.Elements.ErrorCode;
>>> -      } else {
>>> -        Regs->ExceptionData = 0;
>>> -      }
>>> -
>>> -      *ExceptionType = Event.Elements.Vector;
>>> +      Regs->ExceptionData = 0;
>>>       }
>>>   
>>> +    *ExceptionType = Event.Elements.Vector;
>>> +
>>>       VcRet = EFI_PROTOCOL_ERROR;
>>>     }
>>>   
>>>
>>
>>
>> 
>>
> 

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

* Re: [edk2-devel] [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO)
  2020-05-19 21:50 ` [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO) Lendacky, Thomas
@ 2020-05-22 14:14   ` Laszlo Ersek
  2020-05-22 14:31     ` Laszlo Ersek
  2020-05-22 20:41     ` Lendacky, Thomas
  0 siblings, 2 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:14 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
>
> Under SEV-ES, a NPF intercept for an NPT entry with a reserved bit set
> generates a #VC exception. This condition is assumed to be an MMIO access.
> VMGEXIT must be used to allow the hypervisor to handle this intercept.
>
> Add support to construct the required GHCB values to support a NPF NAE
> event for MMIO.  Parse the instruction that generated the #VC exception,
> setting the required register values in the GHCB and creating the proper
> SW_EXIT_INFO1, SW_EXITINFO2 and SW_SCRATCH values in the GHCB.
>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 436 ++++++++++++++++++
>  1 file changed, 436 insertions(+)
>
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 1c6b472a47c4..50199845ceef 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -224,6 +224,263 @@ GhcbSetRegValid (
>    Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
>  }
>
> +/**
> +  Return a pointer to the contents of the specified register.
> +
> +  Based upon the input register, return a pointer to the registers contents
> +  in the x86 processor context.
> +
> +  @param[in] Regs      x64 processor context
> +  @param[in] Register  Register to obtain pointer for
> +
> +  @retval              Pointer to the contents of the requested register
> +
> +**/
> +STATIC
> +INT64 *

(1) Please change the return type from (INT64*) to (UINT64*).

My request will look more justified once I get to the rest of my points
below.

> +GetRegisterPointer (
> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN UINT8                    Register
> +  )
> +{
> +  UINT64 *Reg;
> +
> +  switch (Register) {
> +  case 0:
> +    Reg = &Regs->Rax;
> +    break;
> +  case 1:
> +    Reg = &Regs->Rcx;
> +    break;
> +  case 2:
> +    Reg = &Regs->Rdx;
> +    break;
> +  case 3:
> +    Reg = &Regs->Rbx;
> +    break;
> +  case 4:
> +    Reg = &Regs->Rsp;
> +    break;
> +  case 5:
> +    Reg = &Regs->Rbp;
> +    break;
> +  case 6:
> +    Reg = &Regs->Rsi;
> +    break;
> +  case 7:
> +    Reg = &Regs->Rdi;
> +    break;
> +  case 8:
> +    Reg = &Regs->R8;
> +    break;
> +  case 9:
> +    Reg = &Regs->R9;
> +    break;
> +  case 10:
> +    Reg = &Regs->R10;
> +    break;
> +  case 11:
> +    Reg = &Regs->R11;
> +    break;
> +  case 12:
> +    Reg = &Regs->R12;
> +    break;
> +  case 13:
> +    Reg = &Regs->R13;
> +    break;
> +  case 14:
> +    Reg = &Regs->R14;
> +    break;
> +  case 15:
> +    Reg = &Regs->R15;
> +    break;
> +  default:
> +    Reg = NULL;
> +  }
> +  ASSERT (Reg != NULL);
> +
> +  return (INT64 *) Reg;
> +}

(2) Please remove the cast in the "return" statement.

> +
> +/**
> +  Update the instruction parsing context for displacement bytes.
> +
> +  @param[in, out] InstructionData  Instruction parsing context
> +  @param[in]      Size             The instruction displacement size
> +
> +**/
> +STATIC
> +VOID
> +UpdateForDisplacement (
> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
> +  IN     UINTN                    Size
> +  )
> +{
> +  InstructionData->DisplacementSize = Size;
> +  InstructionData->Immediate += Size;
> +  InstructionData->End += Size;
> +}
> +
> +/**
> +  Determine if an instruction address if RIP relative.
> +
> +  Examine the instruction parsing context to determine if the address offset
> +  is relative to the instruction pointer.
> +
> +  @param[in] InstructionData  Instruction parsing context
> +
> +  @retval TRUE                Instruction addressing is RIP relative
> +  @retval FALSE               Instruction addressing is not RIP relative
> +
> +**/
> +STATIC
> +BOOLEAN
> +IsRipRelative (
> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
> +
> +  Ext = &InstructionData->Ext;
> +
> +  return ((InstructionData->Mode == LongMode64Bit) &&
> +          (Ext->ModRm.Mod == 0) &&
> +          (Ext->ModRm.Rm == 5)  &&
> +          (InstructionData->SibPresent == FALSE));
> +}
> +
> +/**
> +  Return the effective address of a memory operand.
> +
> +  Examine the instruction parsing context to obtain the effective memory
> +  address of a memory operand.
> +
> +  @param[in] Regs             x64 processor context
> +  @param[in] InstructionData  Instruction parsing context
> +
> +  @retval                     The memory operand effective address
> +
> +**/
> +STATIC
> +UINTN

(3) Please make the return type UINT64.

It doesn't change behavior at all, as this is X64-only code, but it will
make our reasoning easier.

(The return value of GetEffectiveMemoryAddress() is assigned to
Ext->RmData (SEV_ES_INSTRUCTION_OPCODE_EXT.RmData) later, which has type
UINTN. But this is X64-only code, so that assignment is fine.)

> +GetEffectiveMemoryAddress (
> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
> +  INTN                           EffectiveAddress;

(4) Please make this a UINT64 too.

> +
> +  Ext = &InstructionData->Ext;
> +  EffectiveAddress = 0;
> +
> +  if (IsRipRelative (InstructionData)) {
> +    /* RIP-relative displacement is a 32-bit signed value */

(5) Please update the comment style.

> +    INT32 RipRelative;
> +
> +    RipRelative = *(INT32 *) InstructionData->Displacement;

OK.

> +
> +    UpdateForDisplacement (InstructionData, 4);
> +    return (UINTN) ((INTN) Regs->Rip + RipRelative);

So, casting "Regs->Rip" (of type UINT64) to INTN is where I start
fidgeting :) The C standard says in "6.3.1.3 Signed and unsigned
integers", paragraph 3:

  Otherwise, the new type is signed and the value cannot be represented
  in it; either the result is implementation-defined or an
  implementation-defined signal is raised.

Now I *do* realize that our particular C language implementation(s) *do*
define the behavior here. If Rip is in the upper half of the address
space, we flip to negative (in two's complement representation), perform
the signed addition, then flip back to positive (which is *not*
implementation defined but standard-defined, but will do the right thing
here).

But that's way too hard to follow if you actually want to pay attention
to the signed/unsigned conversions. We can do this without relying on
the implementation-dependent two's complement representation. Here's
what I suggest:

RipRelative is an INT32, and may be negative. Consider the cast

  (UINT64)RipRelative

If RipRelative is non-negative, then the value doesn't change (we'll
perform a plain increment).

If RipRelative is negative, we'll get the following value from the cast,
mathematically speaking:

  (MAX_UINT64 + 1) - (-RipRelative)                                  [*]

which is just a different way of writing

  (MAX_UINT64 + 1) + RipRelative

And the latter comes straight from the C standard, 6.3.1.3p2:

  Otherwise, if the new type is unsigned, the value is converted by
  repeatedly adding or subtracting one more than the maximum value that
  can be represented in the new type until the value is in the range of
  the new type.

Now consider what happens when we add [*] to Regs->Rip (which is itself
a UINT64):

  Regs->Rip + ((MAX_UINT64 + 1) - (-RipRelative))

Unpack the outer parens:

  Regs->Rip + (MAX_UINT64 + 1) - (-RipRelative)

making for

  (Regs->Rip + (MAX_UINT64 + 1)) - (-RipRelative)

The middle term falls away, per "6.2.5 Types", paragraph 9:

  [...] A computation involving unsigned operands can never overflow,
  because a result that cannot be represented by the resulting unsigned
  integer type is reduced modulo the number that is one greater than the
  largest value that can be represented by the resulting type.

Therefore we get:

  Regs->Rip - (-RipRelative)

which is exactly what we want, for a negative RipRelative.

(6) Thus, the return statement should be:

  //
  // Negative displacement is handled by standard UINT64 wrap-around.
  //
  return Regs->Rip + (UINT64)RipRelative;

(Technically, we could even drop the explicit (UINT64) cast --
RipRelative would be converted automatically to UINT64 --, but we should
keep the (UINT64) cast for documentation purposes.)

> +  }
> +
> +  switch (Ext->ModRm.Mod) {
> +  case 1:
> +    UpdateForDisplacement (InstructionData, 1);
> +    EffectiveAddress += (INT8) (*(INT8 *) (InstructionData->Displacement));

Considering the patch as-is, the outer (INT8) cast is redundant. But,
that's not really my point. My point is how we should update this, after
changing the type of EffectiveAddress to UINT64:

(7) Replace the outer (INT8) cast with (UINT64).

    EffectiveAddress += (UINT64) (*(INT8 *) (InstructionData->Displacement));

The reasoning is the same as for (6). If the displacement is negative,
the value we get on the right hand side is

  (MAX_UINT64 + 1) - (-Displacement)

And when we add that to EffectiveAddress (also of type UINT64), the
(MAX_UINT64 + 1) term falls away, and we get

  EffectiveAddress - (-Displacement)

(The UINT64 conversion would happen anyway, per the "usual arithmetic
conversions", given the new UINT64 type of EffectiveAddress; so the cast
is mainly for documentation, again.)

> +    break;
> +  case 2:
> +    switch (InstructionData->AddrSize) {
> +    case Size16Bits:
> +      UpdateForDisplacement (InstructionData, 2);
> +      EffectiveAddress += (INT16) (*(INT16 *) (InstructionData->Displacement));

(8) Same as (7); please change the outer cast to (UINT64).

> +      break;
> +    default:
> +      UpdateForDisplacement (InstructionData, 4);
> +      EffectiveAddress += (INT32) (*(INT32 *) (InstructionData->Displacement));

(9) Same as (7); please change the outer cast to (UINT64).

> +      break;
> +    }
> +    break;
> +  }
> +
> +  if (InstructionData->SibPresent) {
> +    if (Ext->Sib.Index != 4) {
> +      EffectiveAddress += (*GetRegisterPointer (Regs, Ext->Sib.Index) << Ext->Sib.Scale);

In the patch, as-is, we're left-shifting an INT64 that may be negative.
That's not defined by the standard; see "6.5.7 Bitwise shift operators",
p4:

  [...] If E1 has a signed type and nonnegative value, and E1 * 2^E2 is
  representable in the result type, then that is the resulting value;
  otherwise, the behavior is undefined.

(10) Therefore we should do:

  INT64 Displacement;

  CopyMem (&Displacement, GetRegisterPointer (Regs, Ext->Sib.Index),
    sizeof Displacement);
  Displacement *= (1 << Ext->Sib.Scale);
  //
  // Negative displacement is handled by standard UINT64 wrap-around.
  //
  EffectiveAddress += (UINT64)Displacement;

Assuming that the instruction we're decoding isn't malformed in the
first place, this is safe.

(10a) The CopyMem could be replaced with

  Displacement = *(INT64 *)GetRegisterPointer (Regs, Ext->Sib.Index);

but the CopyMem() is cleaner. (It is where we *explicitly* rely on two's
complement representation.)

(10b) "Ext->Sib.Scale" is at most 3 (from DecodeModRm() below -- it
comes from a 2-bits wide bitfield), so left-shifting value 1 (of type
INT32) is OK.

(10c) Multiplying a negative INT64 by 1, 2, 4, or 8 is well-defined
(assuming again that the initial Displacement value is small enough,
which depends on the original instruction).

If we wanted to be super-safe, we could replace this open-coded
INT64 multiplication with a call to SafeInt64Mult(), from
<Library/SafeIntLib.h>, and hang here, if the call fails.

Up to you.

(10d) The final addition follows the same argument as above. We could
again drop the UINT64 cast (the INT64 operand would be converted to
UINT64 via the "usual arithmetic conversions"), but we should keep it
for documentation purposes.

> +    }
> +
> +    if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {
> +      EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);

This will just continue working, with EffectiveAddress and
(*GetRegisterPointer()) being both UINT64's. A negative displacement
will be encoded within the register that (*GetRegisterPointer()) reads
out.

> +    } else {
> +      UpdateForDisplacement (InstructionData, 4);
> +      EffectiveAddress += (INT32) (*(INT32 *) (InstructionData->Displacement));

(11) Same as (9) -- please change the outer (INT32) cast to (UINT64),
for documentation.

> +    }
> +  } else {
> +    EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);

Continues working fine.

> +  }
> +
> +  return (UINTN) EffectiveAddress;

(12) Please drop the cast.

> +}
> +
> +/**
> +  Decode a ModRM byte.
> +
> +  Examine the instruction parsing context to decode a ModRM byte and the SIB
> +  byte, if present.
> +
> +  @param[in]      Regs             x64 processor context
> +  @param[in, out] InstructionData  Instruction parsing context
> +
> +**/
> +STATIC
> +VOID
> +DecodeModRm (
> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  SEV_ES_INSTRUCTION_REX_PREFIX  *RexPrefix;
> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
> +  SEV_ES_INSTRUCTION_MODRM       *ModRm;
> +  SEV_ES_INSTRUCTION_SIB         *Sib;
> +
> +  RexPrefix = &InstructionData->RexPrefix;
> +  Ext = &InstructionData->Ext;
> +  ModRm = &InstructionData->ModRm;
> +  Sib = &InstructionData->Sib;
> +
> +  InstructionData->ModRmPresent = TRUE;
> +  ModRm->Uint8 = *(InstructionData->End);
> +
> +  InstructionData->Displacement++;
> +  InstructionData->Immediate++;
> +  InstructionData->End++;
> +
> +  Ext->ModRm.Mod = ModRm->Bits.Mod;
> +  Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;
> +  Ext->ModRm.Rm  = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;
> +
> +  Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);
> +
> +  if (Ext->ModRm.Mod == 3) {
> +    Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);

Both of these UINTN field assignments will continue working, with
GetRegisterPointer() returning (UINT64*).

> +  } else {
> +    if (ModRm->Bits.Rm == 4) {
> +      InstructionData->SibPresent = TRUE;
> +      Sib->Uint8 = *(InstructionData->End);
> +
> +      InstructionData->Displacement++;
> +      InstructionData->Immediate++;
> +      InstructionData->End++;
> +
> +      Ext->Sib.Scale = Sib->Bits.Scale;
> +      Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;
> +      Ext->Sib.Base  = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;
> +    }
> +
> +    Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);
> +  }
> +}
> +
>  /**
>    Decode instruction prefixes.
>
> @@ -411,6 +668,181 @@ UnsupportedExit (
>    return Status;
>  }
>
> +/**
> +  Handle an MMIO event.
> +
> +  Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in, out] InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +MmioExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  ExitInfo1, ExitInfo2, Status;
> +  UINTN   Bytes;
> +  INTN    *Register;

(13) Please change this to (UINT64 *).

> +  UINT8   OpCode, SignByte;
> +
> +  Bytes = 0;
> +
> +  OpCode = *(InstructionData->OpCodes);
> +  if (OpCode == 0x0F) {
> +    OpCode = *(InstructionData->OpCodes + 1);
> +  }

(14) Can you add a comment regarding the 0x0F constant?

> +
> +  switch (OpCode) {
> +  /* MMIO write */

(15) Please update the comment style.

Also, can we be more explicit about the opcodes, with comments?

> +  case 0x88:
> +    Bytes = 1;

(16) Please add a "fall through" comment.

> +  case 0x89:
> +    DecodeModRm (Regs, InstructionData);
> +    Bytes = (Bytes) ? Bytes

(17) Please use an explicit (Bytes > 0) comparison.

> +                    : (InstructionData->DataSize == Size16Bits) ? 2
> +                    : (InstructionData->DataSize == Size32Bits) ? 4
> +                    : (InstructionData->DataSize == Size64Bits) ? 8
> +                    : 0;

I struggled for a while to figure out what bothered me about this syntax
:)

(18) The colons ":" should be at the ends of the lines.

    Bytes = ((Bytes > 0) ? Bytes :
             (InstructionData->DataSize == Size16Bits) ? 2 :
             (InstructionData->DataSize == Size32Bits) ? 4 :
             (InstructionData->DataSize == Size64Bits) ? 8 :
             0);

(I recommend the outermost parens only for supporting the indentation.)

> +
> +    if (InstructionData->Ext.ModRm.Mod == 3) {
> +      /* NPF on two register operands??? */

(19) Please update the comment style.

> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
> +    }
> +
> +    ExitInfo1 = InstructionData->Ext.RmData;
> +    ExitInfo2 = Bytes;
> +    CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
> +
> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
> +    if (Status) {

(20) please write (Status > 0) or (Status != 0)

> +      return Status;
> +    }
> +    break;
> +
> +  case 0xC6:
> +    Bytes = 1;

(21) Please add a "fall through" comment.

> +  case 0xC7:
> +    DecodeModRm (Regs, InstructionData);
> +    Bytes = (Bytes) ? Bytes
> +                    : (InstructionData->DataSize == Size16Bits) ? 2
> +                    : (InstructionData->DataSize == Size32Bits) ? 4
> +                    : 0;

(22) please see (17) and (18)

> +
> +    InstructionData->ImmediateSize = Bytes;
> +    InstructionData->End += Bytes;
> +
> +    ExitInfo1 = InstructionData->Ext.RmData;
> +    ExitInfo2 = Bytes;
> +    CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
> +
> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
> +    if (Status) {

(23) please write (Status > 0) or (Status != 0)

> +      return Status;
> +    }
> +    break;
> +
> +  /* MMIO read */

(24) pls see (15)

> +  case 0x8A:
> +    Bytes = 1;

(25) Please add a "fall through" comment.

> +  case 0x8B:
> +    DecodeModRm (Regs, InstructionData);
> +    Bytes = (Bytes) ? Bytes
> +                    : (InstructionData->DataSize == Size16Bits) ? 2
> +                    : (InstructionData->DataSize == Size32Bits) ? 4
> +                    : (InstructionData->DataSize == Size64Bits) ? 8
> +                    : 0;

(26) please see (17) and (18)

> +    if (InstructionData->Ext.ModRm.Mod == 3) {
> +      /* NPF on two register operands??? */

(27) Please update the comment style.

> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
> +    }
> +
> +    ExitInfo1 = InstructionData->Ext.RmData;
> +    ExitInfo2 = Bytes;
> +
> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
> +    if (Status) {

(28) please write (Status > 0) or (Status != 0)

> +      return Status;
> +    }
> +
> +    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
> +    if (Bytes == 4) {
> +      /* Zero-extend for 32-bit operation */

(29) Please update the comment style.

> +      *Register = 0;

Continues working with Register having type (UINT64 *).

> +    }
> +    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
> +    break;
> +
> +  /* MMIO Read w/ zero-extension */

(30) Please see (15)

> +  case 0xB6:
> +    Bytes = 1;

(31) Please add a "fall through" comment.

> +  case 0xB7:
> +    Bytes = (Bytes) ? Bytes : 2;

(32) Please use an explicit (Bytes > 0) comparison.

> +
> +    ExitInfo1 = InstructionData->Ext.RmData;
> +    ExitInfo2 = Bytes;
> +
> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
> +    if (Status) {

(33) please write (Status > 0) or (Status != 0)

> +      return Status;
> +    }
> +
> +    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
> +    SetMem (Register, InstructionData->DataSize, 0);
> +    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
> +    break;
> +
> +  /* MMIO Read w/ sign-extension */

(34) Please see (15)

> +  case 0xBE:
> +    Bytes = 1;

(35) Please add a "fall through" comment.

> +  case 0xBF:
> +    Bytes = (Bytes) ? Bytes : 2;

(36) Please see (17)

> +
> +    ExitInfo1 = InstructionData->Ext.RmData;
> +    ExitInfo2 = Bytes;
> +
> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
> +    if (Status) {

(37) please write (Status > 0) or (Status != 0)

> +      return Status;
> +    }
> +
> +    if (Bytes == 1) {
> +      UINT8 *Data = (UINT8 *) Ghcb->SharedBuffer;
> +
> +      SignByte = (*Data & 0x80) ? 0xFF : 0x00;

(38) Please use BIT7 (or if there's an even better dedicated macro, then
that), rather than 0x80.

(39) Usual comment about bitmask used in logical context.

> +    } else {
> +      UINT16 *Data = (UINT16 *) Ghcb->SharedBuffer;
> +
> +      SignByte = (*Data & 0x8000) ? 0xFF : 0x00;
> +    }

(40) Same two comments as (38) and (39).

> +
> +    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
> +    SetMem (Register, InstructionData->DataSize, SignByte);
> +    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
> +    break;
> +
> +  default:
> +    Status = GP_EXCEPTION;
> +    ASSERT (FALSE);
> +  }
> +
> +  return Status;
> +}
> +
>  /**
>    Handle an MSR event.
>
> @@ -806,6 +1238,10 @@ VmgExitHandleVc (
>      NaeExit = MsrExit;
>      break;
>
> +  case SVM_EXIT_NPF:
> +    NaeExit = MmioExit;
> +    break;
> +
>    default:
>      NaeExit = UnsupportedExit;
>    }
>

Thanks!
Laszlo


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

* Re: [edk2-devel] [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events
  2020-05-19 21:50 ` [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events Lendacky, Thomas
@ 2020-05-22 14:19   ` Laszlo Ersek
  2020-05-22 20:51     ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:19 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a WBINVD intercept generates a #VC exception. VMGEXIT must be
> used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 36 +++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 50199845ceef..0cf26c052b32 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -843,6 +843,38 @@ MmioExit (
>    return Status;
>  }
>  
> +/**
> +  Handle a WBINVD event.
> +
> +  Use the VMGEXIT instruction to handle a WBINVD event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +WbinvdExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
> +  if (Status) {
> +    return Status;
> +  }
> +
> +  return 0;

(1) I'd simplify:

  return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);

unless you want to keep the Status variable (maybe for inserting DEBUG
statements more easily, if needed). Even in that case:

  Status = VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
  return Status;

With either option adopted:

Acked-by: Laszlo Ersek <lersek@redhat.com>

(The real wart I'm trying to address here is the evaluation of "Status"
in a logical context, as usual.)

Thanks,
Laszlo

> +}
> +
>  /**
>    Handle an MSR event.
>  
> @@ -1238,6 +1270,10 @@ VmgExitHandleVc (
>      NaeExit = MsrExit;
>      break;
>  
> +  case SVM_EXIT_WBINVD:
> +    NaeExit = WbinvdExit;
> +    break;
> +
>    case SVM_EXIT_NPF:
>      NaeExit = MmioExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO)
  2020-05-22 14:14   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-22 14:31     ` Laszlo Ersek
  2020-05-22 20:41     ` Lendacky, Thomas
  1 sibling, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:31 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/22/20 16:14, Laszlo Ersek wrote:

>   INT64 Displacement;

>   Displacement *= (1 << Ext->Sib.Scale);

> (10c) Multiplying a negative INT64 by 1, 2, 4, or 8 is well-defined
> (assuming again that the initial Displacement value is small enough,
> which depends on the original instruction).
> 
> If we wanted to be super-safe, we could replace this open-coded
> INT64 multiplication with a call to SafeInt64Mult(), from
> <Library/SafeIntLib.h>, and hang here, if the call fails.
> 
> Up to you.

Side comment: normally, even if we were 100% sure the result would be in
range, we shouldn't use the "*" operator for INT64 multiplication --
because of intrinsics.

BaseLib offers MultS64x64() for that problem.

But, this is X64-only code. So using "*" for INT64 multiplication should
be fine on all toolchains, regarded purely from an intrinsics perspective.

So it's a choice between "*" (if we trust the instruction being decoded
to be sane) or SafeInt64Mult() (if we don't).

MultS64x64() sits in the middle, and doesn't buy us anything here.

Thanks
Laszlo


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

* Re: [edk2-devel] [PATCH v8 19/46] OvmfPkg/VmgExitLib: Add support for RDTSC NAE events
  2020-05-19 21:50 ` [PATCH v8 19/46] OvmfPkg/VmgExitLib: Add support for RDTSC " Lendacky, Thomas
@ 2020-05-22 14:42   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:42 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a RDTSC intercept generates a #VC exception. VMGEXIT must be
> used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 43 +++++++++++++++++++
>  1 file changed, 43 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 0cf26c052b32..3d2a9c6f9bc9 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -1214,6 +1214,45 @@ CpuidExit (
>    return 0;
>  }
>  
> +/**
> +  Handle a RDTSC event.
> +
> +  Use the VMGEXIT instruction to handle a RDTSC event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +RdtscExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);
> +  if (Status) {

(1) Please write (Status > 0) or (Status != 0) explicitly.

With that:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks,
Laszlo

> +    return Status;
> +  }
> +
> +  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
> +      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
> +  }
> +  Regs->Rax = Ghcb->SaveArea.Rax;
> +  Regs->Rdx = Ghcb->SaveArea.Rdx;
> +
> +  return 0;
> +}
> +
>  /**
>    Handle a #VC exception.
>  
> @@ -1258,6 +1297,10 @@ VmgExitHandleVc (
>  
>    ExitCode = Regs->ExceptionData;
>    switch (ExitCode) {
> +  case SVM_EXIT_RDTSC:
> +    NaeExit = RdtscExit;
> +    break;
> +
>    case SVM_EXIT_CPUID:
>      NaeExit = CpuidExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 20/46] OvmfPkg/VmgExitLib: Add support for RDPMC NAE events
  2020-05-19 21:50 ` [PATCH v8 20/46] OvmfPkg/VmgExitLib: Add support for RDPMC " Lendacky, Thomas
@ 2020-05-22 14:43   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:43 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a RDPMC intercept generates a #VC exception. VMGEXIT must be
> used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 46 +++++++++++++++++++
>  1 file changed, 46 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 3d2a9c6f9bc9..d6b35b3019c3 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -1214,6 +1214,48 @@ CpuidExit (
>    return 0;
>  }
>  
> +/**
> +  Handle a RDPMC event.
> +
> +  Use the VMGEXIT instruction to handle a RDPMC event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +RdpmcExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  Ghcb->SaveArea.Rcx = Regs->Rcx;
> +  GhcbSetRegValid (Ghcb, GhcbRcx);
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);
> +  if (Status) {

(1) Please write (Status > 0) or (Status != 0) explicitly.

With that:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

> +    return Status;
> +  }
> +
> +  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
> +      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
> +  }
> +  Regs->Rax = Ghcb->SaveArea.Rax;
> +  Regs->Rdx = Ghcb->SaveArea.Rdx;
> +
> +  return 0;
> +}
> +
>  /**
>    Handle a RDTSC event.
>  
> @@ -1301,6 +1343,10 @@ VmgExitHandleVc (
>      NaeExit = RdtscExit;
>      break;
>  
> +  case SVM_EXIT_RDPMC:
> +    NaeExit = RdpmcExit;
> +    break;
> +
>    case SVM_EXIT_CPUID:
>      NaeExit = CpuidExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 21/46] OvmfPkg/VmgExitLib: Add support for INVD NAE events
  2020-05-19 21:50 ` [PATCH v8 21/46] OvmfPkg/VmgExitLib: Add support for INVD " Lendacky, Thomas
@ 2020-05-22 14:46   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:46 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a INVD intercept generates a #VC exception. VMGEXIT must be
> used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 36 +++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index d6b35b3019c3..0b7f24fd962c 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -1162,6 +1162,38 @@ IoioExit (
>    return 0;
>  }
>  
> +/**
> +  Handle a INVD event.
> +
> +  Use the VMGEXIT instruction to handle a INVD event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +InvdExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);
> +  if (Status) {
> +    return Status;
> +  }
> +
> +  return 0;
> +}

(1) Please see my feedback under:

  [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events

and pick the same approach here that you prefer there.

With that:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

> +
>  /**
>    Handle a CPUID event.
>  
> @@ -1351,6 +1383,10 @@ VmgExitHandleVc (
>      NaeExit = CpuidExit;
>      break;
>  
> +  case SVM_EXIT_INVD:
> +    NaeExit = InvdExit;
> +    break;
> +
>    case SVM_EXIT_IOIO_PROT:
>      NaeExit = IoioExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL NAE events
  2020-05-19 21:50 ` [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL " Lendacky, Thomas
@ 2020-05-22 14:48   ` Laszlo Ersek
  2020-05-22 14:50     ` Laszlo Ersek
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:48 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a VMMCALL intercept generates a #VC exception. VMGEXIT must
> be used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 48 +++++++++++++++++++
>  1 file changed, 48 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 0b7f24fd962c..bf1555234d69 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -875,6 +875,50 @@ WbinvdExit (
>    return 0;
>  }
>  
> +/**
> +  Handle a VMMCALL event.
> +
> +  Use the VMGEXIT instruction to handle either a VMMCALL event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +VmmCallExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  DecodeModRm (Regs, InstructionData);
> +
> +  Ghcb->SaveArea.Rax = Regs->Rax;
> +  GhcbSetRegValid (Ghcb, GhcbRax);
> +  Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);
> +  GhcbSetRegValid (Ghcb, GhcbCpl);
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
> +  if (Status) {

(1) Please write (Status > 0) or (Status != 0).

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

> +    return Status;
> +  }
> +
> +  if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
> +  }
> +  Regs->Rax = Ghcb->SaveArea.Rax;
> +
> +  return 0;
> +}
> +
>  /**
>    Handle an MSR event.
>  
> @@ -1395,6 +1439,10 @@ VmgExitHandleVc (
>      NaeExit = MsrExit;
>      break;
>  
> +  case SVM_EXIT_VMMCALL:
> +    NaeExit = VmmCallExit;
> +    break;
> +
>    case SVM_EXIT_WBINVD:
>      NaeExit = WbinvdExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL NAE events
  2020-05-22 14:48   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-22 14:50     ` Laszlo Ersek
  2020-05-22 21:18       ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:50 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/22/20 16:48, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
>>
>> Under SEV-ES, a VMMCALL intercept generates a #VC exception. VMGEXIT must
>> be used to allow the hypervisor to handle this intercept.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 48 +++++++++++++++++++
>>  1 file changed, 48 insertions(+)
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index 0b7f24fd962c..bf1555234d69 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -875,6 +875,50 @@ WbinvdExit (
>>    return 0;
>>  }
>>  
>> +/**
>> +  Handle a VMMCALL event.
>> +
>> +  Use the VMGEXIT instruction to handle either a VMMCALL event.

(2) pls drop the word "either".

Thanks
Laszlo

>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +VmmCallExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  Status;
>> +
>> +  DecodeModRm (Regs, InstructionData);
>> +
>> +  Ghcb->SaveArea.Rax = Regs->Rax;
>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>> +  Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);
>> +  GhcbSetRegValid (Ghcb, GhcbCpl);
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
>> +  if (Status) {
> 
> (1) Please write (Status > 0) or (Status != 0).
> 
> Acked-by: Laszlo Ersek <lersek@redhat.com>
> 
> Thanks
> Laszlo
> 
>> +    return Status;
>> +  }
>> +
>> +  if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
>> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +  }
>> +  Regs->Rax = Ghcb->SaveArea.Rax;
>> +
>> +  return 0;
>> +}
>> +
>>  /**
>>    Handle an MSR event.
>>  
>> @@ -1395,6 +1439,10 @@ VmgExitHandleVc (
>>      NaeExit = MsrExit;
>>      break;
>>  
>> +  case SVM_EXIT_VMMCALL:
>> +    NaeExit = VmmCallExit;
>> +    break;
>> +
>>    case SVM_EXIT_WBINVD:
>>      NaeExit = WbinvdExit;
>>      break;
>>
> 


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

* Re: [edk2-devel] [PATCH v8 23/46] OvmfPkg/VmgExitLib: Add support for RDTSCP NAE events
  2020-05-19 21:50 ` [PATCH v8 23/46] OvmfPkg/VmgExitLib: Add support for RDTSCP " Lendacky, Thomas
@ 2020-05-22 14:52   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:52 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a RDTSCP intercept generates a #VC exception. VMGEXIT must be
> used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 47 +++++++++++++++++++
>  1 file changed, 47 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index bf1555234d69..716f21a9ee0e 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -875,6 +875,49 @@ WbinvdExit (
>    return 0;
>  }
>  
> +/**
> +  Handle a RDTSCP event.
> +
> +  Use the VMGEXIT instruction to handle either a RDTSCP event.

(1) please remove "either"

> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +RdtscpExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  DecodeModRm (Regs, InstructionData);
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);
> +  if (Status) {

(2) please write (Status != 0) or (Status > 0).

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks,
Laszlo

> +    return Status;
> +  }
> +
> +  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
> +      !GhcbIsRegValid (Ghcb, GhcbRcx) ||
> +      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
> +  }
> +  Regs->Rax = Ghcb->SaveArea.Rax;
> +  Regs->Rcx = Ghcb->SaveArea.Rcx;
> +  Regs->Rdx = Ghcb->SaveArea.Rdx;
> +
> +  return 0;
> +}
> +
>  /**
>    Handle a VMMCALL event.
>  
> @@ -1443,6 +1486,10 @@ VmgExitHandleVc (
>      NaeExit = VmmCallExit;
>      break;
>  
> +  case SVM_EXIT_RDTSCP:
> +    NaeExit = RdtscpExit;
> +    break;
> +
>    case SVM_EXIT_WBINVD:
>      NaeExit = WbinvdExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 24/46] OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX NAE events
  2020-05-19 21:50 ` [PATCH v8 24/46] OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX " Lendacky, Thomas
@ 2020-05-22 14:55   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:55 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a MONITOR/MONITORX intercept generates a #VC exception.
> VMGEXIT must be used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 45 +++++++++++++++++++
>  1 file changed, 45 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 716f21a9ee0e..9b7639069276 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -843,6 +843,47 @@ MmioExit (
>    return Status;
>  }
>  
> +/**
> +  Handle a MONITOR event.
> +
> +  Use the VMGEXIT instruction to handle a MONITOR event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +MonitorExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  DecodeModRm (Regs, InstructionData);
> +
> +  Ghcb->SaveArea.Rax = Regs->Rax;  // Identity mapped, so VA = PA
> +  GhcbSetRegValid (Ghcb, GhcbRax);
> +  Ghcb->SaveArea.Rcx = Regs->Rcx;
> +  GhcbSetRegValid (Ghcb, GhcbRcx);
> +  Ghcb->SaveArea.Rdx = Regs->Rdx;
> +  GhcbSetRegValid (Ghcb, GhcbRdx);
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);
> +  if (Status) {
> +    return Status;
> +  }
> +
> +  return 0;
> +}
> +

(1) Please see my feedback under:

  [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events

and pick the same approach here that you prefer there.

With that:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

>  /**
>    Handle a WBINVD event.
>  
> @@ -1494,6 +1535,10 @@ VmgExitHandleVc (
>      NaeExit = WbinvdExit;
>      break;
>  
> +  case SVM_EXIT_MONITOR:
> +    NaeExit = MonitorExit;
> +    break;
> +
>    case SVM_EXIT_NPF:
>      NaeExit = MmioExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 25/46] OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX NAE events
  2020-05-19 21:50 ` [PATCH v8 25/46] OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX " Lendacky, Thomas
@ 2020-05-22 14:56   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:56 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a MWAIT/MWAITX intercept generates a #VC exception.
> VMGEXIT must be used to allow the hypervisor to handle this intercept.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 43 +++++++++++++++++++
>  1 file changed, 43 insertions(+)
> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index 9b7639069276..b028b20f255a 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -843,6 +843,45 @@ MmioExit (
>    return Status;
>  }
>  
> +/**
> +  Handle a MWAIT event.
> +
> +  Use the VMGEXIT instruction to handle a MWAIT event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +MwaitExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  UINT64  Status;
> +
> +  DecodeModRm (Regs, InstructionData);
> +
> +  Ghcb->SaveArea.Rax = Regs->Rax;
> +  GhcbSetRegValid (Ghcb, GhcbRax);
> +  Ghcb->SaveArea.Rcx = Regs->Rcx;
> +  GhcbSetRegValid (Ghcb, GhcbRcx);
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);
> +  if (Status) {
> +    return Status;
> +  }
> +
> +  return 0;
> +}
> +

(1) Please see my feedback under:

  [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events

and pick the same approach here that you prefer there.

With that:

Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo

>  /**
>    Handle a MONITOR event.
>  
> @@ -1539,6 +1578,10 @@ VmgExitHandleVc (
>      NaeExit = MonitorExit;
>      break;
>  
> +  case SVM_EXIT_MWAIT:
> +    NaeExit = MwaitExit;
> +    break;
> +
>    case SVM_EXIT_NPF:
>      NaeExit = MmioExit;
>      break;
> 


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

* Re: [edk2-devel] [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
  2020-05-19 21:50 ` [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write " Lendacky, Thomas
@ 2020-05-22 14:59   ` Laszlo Ersek
  2020-05-25 14:47   ` Laszlo Ersek
  1 sibling, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-22 14:59 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a DR7 read or write intercept generates a #VC exception.
> The #VC handler must provide special support to the guest for this. On
> a DR7 write, the #VC handler must cache the value and issue a VMGEXIT
> to notify the hypervisor of the write. However, the #VC handler must
> not actually set the value of the DR7 register. On a DR7 read, the #VC
> handler must return the cached value of the DR7 register to the guest.
> VMGEXIT is not invoked for a DR7 register read.
> 
> To avoid exception recursion, a #VC exception will not try to read and
> push the actual debug registers into the EFI_SYSTEM_CONTEXT_X64 struct
> and instead push zeroes. The #VC exception handler does not make use of
> the debug registers from saved context.
> 
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 105 ++++++++++++++++++
>  .../X64/ExceptionHandlerAsm.nasm              |  17 +++
>  .../X64/Xcode5ExceptionHandlerAsm.nasm        |  17 +++
>  3 files changed, 139 insertions(+)

My brain is mush (the NPF/MMIO patch wasn't easy), so I'll pick up the
review at this patch next week.

Thanks!
Laszlo


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

* Re: [edk2-devel] [PATCH v8 15/46] OvmfPkg/VmgExitLib: Add support for CPUID NAE events
  2020-05-22 10:27   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-22 19:02     ` Lendacky, Thomas
  0 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-22 19:02 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/22/20 5:27 AM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C34f8c61055564e148e1408d7fe3ab37f%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637257400397998277&amp;sdata=96rpbKClaJMRK86jpyryqNPdUVsPDqMBOFgSuO9j%2BzA%3D&amp;reserved=0
>>
>> Under SEV-ES, a CPUID intercept generates a #VC exception. VMGEXIT must be
>> used to allow the hypervisor to handle this intercept.
>>
>> Add support to construct the required GHCB values to support a CPUID NAE
>> event. Additionally, CPUID 0x0000_000d requires XCR0 to be supplied in
>> the GHCB, so add support to issue the XGETBV instruction.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 58 +++++++++++++++++++
>>   1 file changed, 58 insertions(+)
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index 906b32e93d53..2f62795edf61 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -12,6 +12,8 @@
>>   #include <Library/VmgExitLib.h>
>>   #include <Register/Amd/Msr.h>
>>   
>> +#define CR4_OSXSAVE (1 << 18)
>> +
> 
> (1) Please drop this macro, and:

Done.

> 
>>   //
>>   // Instruction execution mode definition
>>   //
>> @@ -637,6 +639,58 @@ IoioExit (
>>     return 0;
>>   }
>>   
>> +/**
>> +  Handle a CPUID event.
>> +
>> +  Use the VMGEXIT instruction to handle a CPUID event.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +CpuidExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  Status;
>> +
>> +  Ghcb->SaveArea.Rax = Regs->Rax;
>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>> +  Ghcb->SaveArea.Rcx = Regs->Rcx;
>> +  GhcbSetRegValid (Ghcb, GhcbRcx);
>> +  if (Regs->Rax == 0x0000000d) {
> 
> (2a) Can we use CPUID_EXTENDED_STATE here, from <Register/Intel/Cpuid.h>?

Yup.

> 
> (2b) If so, I'd suggest updating the commit message too: replace "CPUID
> 0x0000_000d" with "CPUID 0x0000_000d (CPUID_EXTENDED_STATE)".

Will do.

> 
>> +    Ghcb->SaveArea.XCr0 = (AsmReadCr4 () & CR4_OSXSAVE) ? AsmXGetBv (0) : 1;
> 
> (3) Here, please use the IA32_CR4 type from <Library/BaseLib.h>:
> 
>      IA32_CR4 Cr4;
> 
>      Cr4.UintN = AsmReadCr4 ();
>      Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;

Will do.

> 
> 
> Some of the style requests I made under earlier patches in this series
> apply here, so I won't spell them out again.
> 
> 
> With the style updated:
> 
> Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks!
Tom

> 
> Thanks
> Laszlo
> 
>> +    GhcbSetRegValid (Ghcb, GhcbXCr0);
>> +  }
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
>> +  if (Status) {
>> +    return Status;
>> +  }
>> +
>> +  if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
>> +      !GhcbIsRegValid (Ghcb, GhcbRbx) ||
>> +      !GhcbIsRegValid (Ghcb, GhcbRcx) ||
>> +      !GhcbIsRegValid (Ghcb, GhcbRdx)) {
>> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +  }
>> +  Regs->Rax = Ghcb->SaveArea.Rax;
>> +  Regs->Rbx = Ghcb->SaveArea.Rbx;
>> +  Regs->Rcx = Ghcb->SaveArea.Rcx;
>> +  Regs->Rdx = Ghcb->SaveArea.Rdx;
>> +
>> +  return 0;
>> +}
>> +
>>   /**
>>     Handle a #VC exception.
>>   
>> @@ -681,6 +735,10 @@ VmgExitHandleVc (
>>   
>>     ExitCode = Regs->ExceptionData;
>>     switch (ExitCode) {
>> +  case SVM_EXIT_CPUID:
>> +    NaeExit = CpuidExit;
>> +    break;
>> +
>>     case SVM_EXIT_IOIO_PROT:
>>       NaeExit = IoioExit;
>>       break;
>>
> 

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

* Re: [edk2-devel] [PATCH v8 16/46] OvmfPkg/VmgExitLib: Add support for MSR_PROT NAE events
  2020-05-22 10:31   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-22 19:06     ` Lendacky, Thomas
  0 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-22 19:06 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/22/20 5:31 AM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C95b407c13bd44ad1b89608d7fe3b450a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637257402837183063&amp;sdata=jSpUywdrvuJ5Iw1lfVb30VUuZc%2FBOGn%2B670DymPBePY%3D&amp;reserved=0
>>
>> Under SEV-ES, a MSR_PROT intercept generates a #VC exception. VMGEXIT must
>> be used to allow the hypervisor to handle this intercept.
>>
>> Add support to construct the required GHCB values to support an MSR_PROT
>> NAE event. Parse the instruction that generated the #VC exception to
>> determine whether it is RDMSR or WRMSR, setting the required register
>> register values in the GHCB and creating the proper SW_EXIT_INFO1 value in
>> the GHCB.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 63 +++++++++++++++++++
>>   1 file changed, 63 insertions(+)
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index 2f62795edf61..1c6b472a47c4 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -411,6 +411,65 @@ UnsupportedExit (
>>     return Status;
>>   }
>>   
>> +/**
>> +  Handle an MSR event.
>> +
>> +  Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +MsrExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  ExitInfo1, Status;
>> +
>> +  ExitInfo1 = 0;
>> +
>> +  switch (*(InstructionData->OpCodes + 1)) {
>> +  case 0x30: // WRMSR
> 
> This comment looks great!
> 
>> +    ExitInfo1 = 1;
>> +    Ghcb->SaveArea.Rax = Regs->Rax;
>> +    GhcbSetRegValid (Ghcb, GhcbRax);
>> +    Ghcb->SaveArea.Rdx = Regs->Rdx;
>> +    GhcbSetRegValid (Ghcb, GhcbRdx);
>> +    /* Fallthrough */
> 
> (1) This comment is very appreciated (I vaguely remember that the coding
> style actually requires it), but we're supposed to put it like this:
> 
>      //
>      // fall through
>      //
> 
> (See: "git grep -B1 -A1 -i 'fall through'".)
> 

Will do.

>> +  case 0x32: // RDMSR
>> +    Ghcb->SaveArea.Rcx = Regs->Rcx;
>> +    GhcbSetRegValid (Ghcb, GhcbRcx);
>> +    break;
>> +  default:
>> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +  }
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
>> +  if (Status) {
> 
> (2) As usual, please check (Status > 0) or (Status != 0) explicitly.

Yup.

> 
> Acked-by: Laszlo Ersek <lersek@redhat.com>

Thanks!
Tom

> 
> Thanks
> Laszlo
> 
>> +    return Status;
>> +  }
>> +
>> +  if (!ExitInfo1) {
>> +    if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
>> +        !GhcbIsRegValid (Ghcb, GhcbRdx)) {
>> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +    }
>> +    Regs->Rax = Ghcb->SaveArea.Rax;
>> +    Regs->Rdx = Ghcb->SaveArea.Rdx;
>> +  }
>> +
>> +  return 0;
>> +}
>> +
>>   #define IOIO_TYPE_STR       (1 << 2)
>>   #define IOIO_TYPE_IN        1
>>   #define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
>> @@ -743,6 +802,10 @@ VmgExitHandleVc (
>>       NaeExit = IoioExit;
>>       break;
>>   
>> +  case SVM_EXIT_MSR:
>> +    NaeExit = MsrExit;
>> +    break;
>> +
>>     default:
>>       NaeExit = UnsupportedExit;
>>     }
>>
> 

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

* Re: [edk2-devel] [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO)
  2020-05-22 14:14   ` [edk2-devel] " Laszlo Ersek
  2020-05-22 14:31     ` Laszlo Ersek
@ 2020-05-22 20:41     ` Lendacky, Thomas
  1 sibling, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-22 20:41 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/22/20 9:14 AM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7Cfdd2325c2e5341d8ae5408d7fe5a75f5%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637257536808864483&amp;sdata=RICgoxIHQzIwTS0UWB0gFK39ENqFaoSH%2FeX6DU0h0VI%3D&amp;reserved=0
>>
>> Under SEV-ES, a NPF intercept for an NPT entry with a reserved bit set
>> generates a #VC exception. This condition is assumed to be an MMIO access.
>> VMGEXIT must be used to allow the hypervisor to handle this intercept.
>>
>> Add support to construct the required GHCB values to support a NPF NAE
>> event for MMIO.  Parse the instruction that generated the #VC exception,
>> setting the required register values in the GHCB and creating the proper
>> SW_EXIT_INFO1, SW_EXITINFO2 and SW_SCRATCH values in the GHCB.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 436 ++++++++++++++++++
>>   1 file changed, 436 insertions(+)
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index 1c6b472a47c4..50199845ceef 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -224,6 +224,263 @@ GhcbSetRegValid (
>>     Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
>>   }
>>
>> +/**
>> +  Return a pointer to the contents of the specified register.
>> +
>> +  Based upon the input register, return a pointer to the registers contents
>> +  in the x86 processor context.
>> +
>> +  @param[in] Regs      x64 processor context
>> +  @param[in] Register  Register to obtain pointer for
>> +
>> +  @retval              Pointer to the contents of the requested register
>> +
>> +**/
>> +STATIC
>> +INT64 *
> 
> (1) Please change the return type from (INT64*) to (UINT64*).
> 
> My request will look more justified once I get to the rest of my points
> below.
> 
>> +GetRegisterPointer (
>> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN UINT8                    Register
>> +  )
>> +{
>> +  UINT64 *Reg;
>> +
>> +  switch (Register) {
>> +  case 0:
>> +    Reg = &Regs->Rax;
>> +    break;
>> +  case 1:
>> +    Reg = &Regs->Rcx;
>> +    break;
>> +  case 2:
>> +    Reg = &Regs->Rdx;
>> +    break;
>> +  case 3:
>> +    Reg = &Regs->Rbx;
>> +    break;
>> +  case 4:
>> +    Reg = &Regs->Rsp;
>> +    break;
>> +  case 5:
>> +    Reg = &Regs->Rbp;
>> +    break;
>> +  case 6:
>> +    Reg = &Regs->Rsi;
>> +    break;
>> +  case 7:
>> +    Reg = &Regs->Rdi;
>> +    break;
>> +  case 8:
>> +    Reg = &Regs->R8;
>> +    break;
>> +  case 9:
>> +    Reg = &Regs->R9;
>> +    break;
>> +  case 10:
>> +    Reg = &Regs->R10;
>> +    break;
>> +  case 11:
>> +    Reg = &Regs->R11;
>> +    break;
>> +  case 12:
>> +    Reg = &Regs->R12;
>> +    break;
>> +  case 13:
>> +    Reg = &Regs->R13;
>> +    break;
>> +  case 14:
>> +    Reg = &Regs->R14;
>> +    break;
>> +  case 15:
>> +    Reg = &Regs->R15;
>> +    break;
>> +  default:
>> +    Reg = NULL;
>> +  }
>> +  ASSERT (Reg != NULL);
>> +
>> +  return (INT64 *) Reg;
>> +}
> 
> (2) Please remove the cast in the "return" statement.
> 
>> +
>> +/**
>> +  Update the instruction parsing context for displacement bytes.
>> +
>> +  @param[in, out] InstructionData  Instruction parsing context
>> +  @param[in]      Size             The instruction displacement size
>> +
>> +**/
>> +STATIC
>> +VOID
>> +UpdateForDisplacement (
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
>> +  IN     UINTN                    Size
>> +  )
>> +{
>> +  InstructionData->DisplacementSize = Size;
>> +  InstructionData->Immediate += Size;
>> +  InstructionData->End += Size;
>> +}
>> +
>> +/**
>> +  Determine if an instruction address if RIP relative.
>> +
>> +  Examine the instruction parsing context to determine if the address offset
>> +  is relative to the instruction pointer.
>> +
>> +  @param[in] InstructionData  Instruction parsing context
>> +
>> +  @retval TRUE                Instruction addressing is RIP relative
>> +  @retval FALSE               Instruction addressing is not RIP relative
>> +
>> +**/
>> +STATIC
>> +BOOLEAN
>> +IsRipRelative (
>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
>> +
>> +  Ext = &InstructionData->Ext;
>> +
>> +  return ((InstructionData->Mode == LongMode64Bit) &&
>> +          (Ext->ModRm.Mod == 0) &&
>> +          (Ext->ModRm.Rm == 5)  &&
>> +          (InstructionData->SibPresent == FALSE));
>> +}
>> +
>> +/**
>> +  Return the effective address of a memory operand.
>> +
>> +  Examine the instruction parsing context to obtain the effective memory
>> +  address of a memory operand.
>> +
>> +  @param[in] Regs             x64 processor context
>> +  @param[in] InstructionData  Instruction parsing context
>> +
>> +  @retval                     The memory operand effective address
>> +
>> +**/
>> +STATIC
>> +UINTN
> 
> (3) Please make the return type UINT64.
> 
> It doesn't change behavior at all, as this is X64-only code, but it will
> make our reasoning easier.
> 
> (The return value of GetEffectiveMemoryAddress() is assigned to
> Ext->RmData (SEV_ES_INSTRUCTION_OPCODE_EXT.RmData) later, which has type
> UINTN. But this is X64-only code, so that assignment is fine.)
> 
>> +GetEffectiveMemoryAddress (
>> +  IN EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
>> +  INTN                           EffectiveAddress;
> 
> (4) Please make this a UINT64 too.
> 
>> +
>> +  Ext = &InstructionData->Ext;
>> +  EffectiveAddress = 0;
>> +
>> +  if (IsRipRelative (InstructionData)) {
>> +    /* RIP-relative displacement is a 32-bit signed value */
> 
> (5) Please update the comment style.
> 
>> +    INT32 RipRelative;
>> +
>> +    RipRelative = *(INT32 *) InstructionData->Displacement;
> 
> OK.
> 
>> +
>> +    UpdateForDisplacement (InstructionData, 4);
>> +    return (UINTN) ((INTN) Regs->Rip + RipRelative);
> 
> So, casting "Regs->Rip" (of type UINT64) to INTN is where I start
> fidgeting :) The C standard says in "6.3.1.3 Signed and unsigned
> integers", paragraph 3:
> 
>    Otherwise, the new type is signed and the value cannot be represented
>    in it; either the result is implementation-defined or an
>    implementation-defined signal is raised.
> 
> Now I *do* realize that our particular C language implementation(s) *do*
> define the behavior here. If Rip is in the upper half of the address
> space, we flip to negative (in two's complement representation), perform
> the signed addition, then flip back to positive (which is *not*
> implementation defined but standard-defined, but will do the right thing
> here).
> 
> But that's way too hard to follow if you actually want to pay attention
> to the signed/unsigned conversions. We can do this without relying on
> the implementation-dependent two's complement representation. Here's
> what I suggest:
> 
> RipRelative is an INT32, and may be negative. Consider the cast
> 
>    (UINT64)RipRelative
> 
> If RipRelative is non-negative, then the value doesn't change (we'll
> perform a plain increment).
> 
> If RipRelative is negative, we'll get the following value from the cast,
> mathematically speaking:
> 
>    (MAX_UINT64 + 1) - (-RipRelative)                                  [*]
> 
> which is just a different way of writing
> 
>    (MAX_UINT64 + 1) + RipRelative
> 
> And the latter comes straight from the C standard, 6.3.1.3p2:
> 
>    Otherwise, if the new type is unsigned, the value is converted by
>    repeatedly adding or subtracting one more than the maximum value that
>    can be represented in the new type until the value is in the range of
>    the new type.
> 
> Now consider what happens when we add [*] to Regs->Rip (which is itself
> a UINT64):
> 
>    Regs->Rip + ((MAX_UINT64 + 1) - (-RipRelative))
> 
> Unpack the outer parens:
> 
>    Regs->Rip + (MAX_UINT64 + 1) - (-RipRelative)
> 
> making for
> 
>    (Regs->Rip + (MAX_UINT64 + 1)) - (-RipRelative)
> 
> The middle term falls away, per "6.2.5 Types", paragraph 9:
> 
>    [...] A computation involving unsigned operands can never overflow,
>    because a result that cannot be represented by the resulting unsigned
>    integer type is reduced modulo the number that is one greater than the
>    largest value that can be represented by the resulting type.
> 
> Therefore we get:
> 
>    Regs->Rip - (-RipRelative)
> 
> which is exactly what we want, for a negative RipRelative.
> 
> (6) Thus, the return statement should be:
> 
>    //
>    // Negative displacement is handled by standard UINT64 wrap-around.
>    //
>    return Regs->Rip + (UINT64)RipRelative;
> 
> (Technically, we could even drop the explicit (UINT64) cast --
> RipRelative would be converted automatically to UINT64 --, but we should
> keep the (UINT64) cast for documentation purposes.)

Impressive!  I'll make all those changes.

> 
>> +  }
>> +
>> +  switch (Ext->ModRm.Mod) {
>> +  case 1:
>> +    UpdateForDisplacement (InstructionData, 1);
>> +    EffectiveAddress += (INT8) (*(INT8 *) (InstructionData->Displacement));
> 
> Considering the patch as-is, the outer (INT8) cast is redundant. But,
> that's not really my point. My point is how we should update this, after
> changing the type of EffectiveAddress to UINT64:
> 
> (7) Replace the outer (INT8) cast with (UINT64).
> 
>      EffectiveAddress += (UINT64) (*(INT8 *) (InstructionData->Displacement));
> 
> The reasoning is the same as for (6). If the displacement is negative,
> the value we get on the right hand side is
> 
>    (MAX_UINT64 + 1) - (-Displacement)
> 
> And when we add that to EffectiveAddress (also of type UINT64), the
> (MAX_UINT64 + 1) term falls away, and we get
> 
>    EffectiveAddress - (-Displacement)
> 
> (The UINT64 conversion would happen anyway, per the "usual arithmetic
> conversions", given the new UINT64 type of EffectiveAddress; so the cast
> is mainly for documentation, again.)
> 
>> +    break;
>> +  case 2:
>> +    switch (InstructionData->AddrSize) {
>> +    case Size16Bits:
>> +      UpdateForDisplacement (InstructionData, 2);
>> +      EffectiveAddress += (INT16) (*(INT16 *) (InstructionData->Displacement));
> 
> (8) Same as (7); please change the outer cast to (UINT64).
> 
>> +      break;
>> +    default:
>> +      UpdateForDisplacement (InstructionData, 4);
>> +      EffectiveAddress += (INT32) (*(INT32 *) (InstructionData->Displacement));
> 
> (9) Same as (7); please change the outer cast to (UINT64).
> 
>> +      break;
>> +    }
>> +    break;
>> +  }
>> +
>> +  if (InstructionData->SibPresent) {
>> +    if (Ext->Sib.Index != 4) {
>> +      EffectiveAddress += (*GetRegisterPointer (Regs, Ext->Sib.Index) << Ext->Sib.Scale);
> 
> In the patch, as-is, we're left-shifting an INT64 that may be negative.
> That's not defined by the standard; see "6.5.7 Bitwise shift operators",
> p4:
> 
>    [...] If E1 has a signed type and nonnegative value, and E1 * 2^E2 is
>    representable in the result type, then that is the resulting value;
>    otherwise, the behavior is undefined.
> 
> (10) Therefore we should do:
> 
>    INT64 Displacement;
> 
>    CopyMem (&Displacement, GetRegisterPointer (Regs, Ext->Sib.Index),
>      sizeof Displacement);
>    Displacement *= (1 << Ext->Sib.Scale);
>    //
>    // Negative displacement is handled by standard UINT64 wrap-around.
>    //
>    EffectiveAddress += (UINT64)Displacement;
> 
> Assuming that the instruction we're decoding isn't malformed in the
> first place, this is safe.
> 
> (10a) The CopyMem could be replaced with
> 
>    Displacement = *(INT64 *)GetRegisterPointer (Regs, Ext->Sib.Index);
> 
> but the CopyMem() is cleaner. (It is where we *explicitly* rely on two's
> complement representation.)
> 
> (10b) "Ext->Sib.Scale" is at most 3 (from DecodeModRm() below -- it
> comes from a 2-bits wide bitfield), so left-shifting value 1 (of type
> INT32) is OK.
> 
> (10c) Multiplying a negative INT64 by 1, 2, 4, or 8 is well-defined
> (assuming again that the initial Displacement value is small enough,
> which depends on the original instruction).
> 
> If we wanted to be super-safe, we could replace this open-coded
> INT64 multiplication with a call to SafeInt64Mult(), from
> <Library/SafeIntLib.h>, and hang here, if the call fails.
> 
> Up to you.
> 
> (10d) The final addition follows the same argument as above. We could
> again drop the UINT64 cast (the INT64 operand would be converted to
> UINT64 via the "usual arithmetic conversions"), but we should keep it
> for documentation purposes.
> 
>> +    }
>> +
>> +    if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {
>> +      EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);
> 
> This will just continue working, with EffectiveAddress and
> (*GetRegisterPointer()) being both UINT64's. A negative displacement
> will be encoded within the register that (*GetRegisterPointer()) reads
> out.
> 
>> +    } else {
>> +      UpdateForDisplacement (InstructionData, 4);
>> +      EffectiveAddress += (INT32) (*(INT32 *) (InstructionData->Displacement));
> 
> (11) Same as (9) -- please change the outer (INT32) cast to (UINT64),
> for documentation.
> 
>> +    }
>> +  } else {
>> +    EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);
> 
> Continues working fine.
> 
>> +  }
>> +
>> +  return (UINTN) EffectiveAddress;
> 
> (12) Please drop the cast.

Ditto here.

> 
>> +}
>> +
>> +/**
>> +  Decode a ModRM byte.
>> +
>> +  Examine the instruction parsing context to decode a ModRM byte and the SIB
>> +  byte, if present.
>> +
>> +  @param[in]      Regs             x64 processor context
>> +  @param[in, out] InstructionData  Instruction parsing context
>> +
>> +**/
>> +STATIC
>> +VOID
>> +DecodeModRm (
>> +  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  SEV_ES_INSTRUCTION_REX_PREFIX  *RexPrefix;
>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
>> +  SEV_ES_INSTRUCTION_MODRM       *ModRm;
>> +  SEV_ES_INSTRUCTION_SIB         *Sib;
>> +
>> +  RexPrefix = &InstructionData->RexPrefix;
>> +  Ext = &InstructionData->Ext;
>> +  ModRm = &InstructionData->ModRm;
>> +  Sib = &InstructionData->Sib;
>> +
>> +  InstructionData->ModRmPresent = TRUE;
>> +  ModRm->Uint8 = *(InstructionData->End);
>> +
>> +  InstructionData->Displacement++;
>> +  InstructionData->Immediate++;
>> +  InstructionData->End++;
>> +
>> +  Ext->ModRm.Mod = ModRm->Bits.Mod;
>> +  Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;
>> +  Ext->ModRm.Rm  = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;
>> +
>> +  Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);
>> +
>> +  if (Ext->ModRm.Mod == 3) {
>> +    Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);
> 
> Both of these UINTN field assignments will continue working, with
> GetRegisterPointer() returning (UINT64*).
> 
>> +  } else {
>> +    if (ModRm->Bits.Rm == 4) {
>> +      InstructionData->SibPresent = TRUE;
>> +      Sib->Uint8 = *(InstructionData->End);
>> +
>> +      InstructionData->Displacement++;
>> +      InstructionData->Immediate++;
>> +      InstructionData->End++;
>> +
>> +      Ext->Sib.Scale = Sib->Bits.Scale;
>> +      Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;
>> +      Ext->Sib.Base  = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;
>> +    }
>> +
>> +    Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);
>> +  }
>> +}
>> +
>>   /**
>>     Decode instruction prefixes.
>>
>> @@ -411,6 +668,181 @@ UnsupportedExit (
>>     return Status;
>>   }
>>
>> +/**
>> +  Handle an MMIO event.
>> +
>> +  Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in, out] InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +MmioExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  ExitInfo1, ExitInfo2, Status;
>> +  UINTN   Bytes;
>> +  INTN    *Register;
> 
> (13) Please change this to (UINT64 *).
> 
>> +  UINT8   OpCode, SignByte;
>> +
>> +  Bytes = 0;
>> +
>> +  OpCode = *(InstructionData->OpCodes);
>> +  if (OpCode == 0x0F) {
>> +    OpCode = *(InstructionData->OpCodes + 1);
>> +  }
> 
> (14) Can you add a comment regarding the 0x0F constant?

I'll create a #define (TWO_BYTE_OPCODE_ESCAPE) that should (hopefully) be 
self commenting.

> 
>> +
>> +  switch (OpCode) {
>> +  /* MMIO write */
> 
> (15) Please update the comment style.
> 
> Also, can we be more explicit about the opcodes, with comments?

Can do.

> 
>> +  case 0x88:
>> +    Bytes = 1;
> 
> (16) Please add a "fall through" comment.

For this and remaining comments: Will do.

Thanks!
Tom

> 
>> +  case 0x89:
>> +    DecodeModRm (Regs, InstructionData);
>> +    Bytes = (Bytes) ? Bytes
> 
> (17) Please use an explicit (Bytes > 0) comparison.
> 
>> +                    : (InstructionData->DataSize == Size16Bits) ? 2
>> +                    : (InstructionData->DataSize == Size32Bits) ? 4
>> +                    : (InstructionData->DataSize == Size64Bits) ? 8
>> +                    : 0;
> 
> I struggled for a while to figure out what bothered me about this syntax
> :)
> 
> (18) The colons ":" should be at the ends of the lines.
> 
>      Bytes = ((Bytes > 0) ? Bytes :
>               (InstructionData->DataSize == Size16Bits) ? 2 :
>               (InstructionData->DataSize == Size32Bits) ? 4 :
>               (InstructionData->DataSize == Size64Bits) ? 8 :
>               0);
> 
> (I recommend the outermost parens only for supporting the indentation.)
> 
>> +
>> +    if (InstructionData->Ext.ModRm.Mod == 3) {
>> +      /* NPF on two register operands??? */
> 
> (19) Please update the comment style.
> 
>> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +    }
>> +
>> +    ExitInfo1 = InstructionData->Ext.RmData;
>> +    ExitInfo2 = Bytes;
>> +    CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
>> +
>> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
>> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
>> +    if (Status) {
> 
> (20) please write (Status > 0) or (Status != 0)
> 
>> +      return Status;
>> +    }
>> +    break;
>> +
>> +  case 0xC6:
>> +    Bytes = 1;
> 
> (21) Please add a "fall through" comment.
> 
>> +  case 0xC7:
>> +    DecodeModRm (Regs, InstructionData);
>> +    Bytes = (Bytes) ? Bytes
>> +                    : (InstructionData->DataSize == Size16Bits) ? 2
>> +                    : (InstructionData->DataSize == Size32Bits) ? 4
>> +                    : 0;
> 
> (22) please see (17) and (18)
> 
>> +
>> +    InstructionData->ImmediateSize = Bytes;
>> +    InstructionData->End += Bytes;
>> +
>> +    ExitInfo1 = InstructionData->Ext.RmData;
>> +    ExitInfo2 = Bytes;
>> +    CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
>> +
>> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
>> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
>> +    if (Status) {
> 
> (23) please write (Status > 0) or (Status != 0)
> 
>> +      return Status;
>> +    }
>> +    break;
>> +
>> +  /* MMIO read */
> 
> (24) pls see (15)
> 
>> +  case 0x8A:
>> +    Bytes = 1;
> 
> (25) Please add a "fall through" comment.
> 
>> +  case 0x8B:
>> +    DecodeModRm (Regs, InstructionData);
>> +    Bytes = (Bytes) ? Bytes
>> +                    : (InstructionData->DataSize == Size16Bits) ? 2
>> +                    : (InstructionData->DataSize == Size32Bits) ? 4
>> +                    : (InstructionData->DataSize == Size64Bits) ? 8
>> +                    : 0;
> 
> (26) please see (17) and (18)
> 
>> +    if (InstructionData->Ext.ModRm.Mod == 3) {
>> +      /* NPF on two register operands??? */
> 
> (27) Please update the comment style.
> 
>> +      return UnsupportedExit (Ghcb, Regs, InstructionData);
>> +    }
>> +
>> +    ExitInfo1 = InstructionData->Ext.RmData;
>> +    ExitInfo2 = Bytes;
>> +
>> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
>> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
>> +    if (Status) {
> 
> (28) please write (Status > 0) or (Status != 0)
> 
>> +      return Status;
>> +    }
>> +
>> +    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
>> +    if (Bytes == 4) {
>> +      /* Zero-extend for 32-bit operation */
> 
> (29) Please update the comment style.
> 
>> +      *Register = 0;
> 
> Continues working with Register having type (UINT64 *).
> 
>> +    }
>> +    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
>> +    break;
>> +
>> +  /* MMIO Read w/ zero-extension */
> 
> (30) Please see (15)
> 
>> +  case 0xB6:
>> +    Bytes = 1;
> 
> (31) Please add a "fall through" comment.
> 
>> +  case 0xB7:
>> +    Bytes = (Bytes) ? Bytes : 2;
> 
> (32) Please use an explicit (Bytes > 0) comparison.
> 
>> +
>> +    ExitInfo1 = InstructionData->Ext.RmData;
>> +    ExitInfo2 = Bytes;
>> +
>> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
>> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
>> +    if (Status) {
> 
> (33) please write (Status > 0) or (Status != 0)
> 
>> +      return Status;
>> +    }
>> +
>> +    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
>> +    SetMem (Register, InstructionData->DataSize, 0);
>> +    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
>> +    break;
>> +
>> +  /* MMIO Read w/ sign-extension */
> 
> (34) Please see (15)
> 
>> +  case 0xBE:
>> +    Bytes = 1;
> 
> (35) Please add a "fall through" comment.
> 
>> +  case 0xBF:
>> +    Bytes = (Bytes) ? Bytes : 2;
> 
> (36) Please see (17)
> 
>> +
>> +    ExitInfo1 = InstructionData->Ext.RmData;
>> +    ExitInfo2 = Bytes;
>> +
>> +    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
>> +    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
>> +    if (Status) {
> 
> (37) please write (Status > 0) or (Status != 0)
> 
>> +      return Status;
>> +    }
>> +
>> +    if (Bytes == 1) {
>> +      UINT8 *Data = (UINT8 *) Ghcb->SharedBuffer;
>> +
>> +      SignByte = (*Data & 0x80) ? 0xFF : 0x00;
> 
> (38) Please use BIT7 (or if there's an even better dedicated macro, then
> that), rather than 0x80.
> 
> (39) Usual comment about bitmask used in logical context.
> 
>> +    } else {
>> +      UINT16 *Data = (UINT16 *) Ghcb->SharedBuffer;
>> +
>> +      SignByte = (*Data & 0x8000) ? 0xFF : 0x00;
>> +    }
> 
> (40) Same two comments as (38) and (39).
> 
>> +
>> +    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
>> +    SetMem (Register, InstructionData->DataSize, SignByte);
>> +    CopyMem (Register, Ghcb->SharedBuffer, Bytes);
>> +    break;
>> +
>> +  default:
>> +    Status = GP_EXCEPTION;
>> +    ASSERT (FALSE);
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>>   /**
>>     Handle an MSR event.
>>
>> @@ -806,6 +1238,10 @@ VmgExitHandleVc (
>>       NaeExit = MsrExit;
>>       break;
>>
>> +  case SVM_EXIT_NPF:
>> +    NaeExit = MmioExit;
>> +    break;
>> +
>>     default:
>>       NaeExit = UnsupportedExit;
>>     }
>>
> 
> Thanks!
> Laszlo
> 

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

* Re: [edk2-devel] [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events
  2020-05-22 14:19   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-22 20:51     ` Lendacky, Thomas
  0 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-22 20:51 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/22/20 9:19 AM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7Cfc8d0918a2474d08139808d7fe5b2a93%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637257539824804827&amp;sdata=aiIeMfwp9GQCrgqOw2si6NzLZOZzRRV%2Bzu3dpT6S%2F5k%3D&amp;reserved=0
>>
>> Under SEV-ES, a WBINVD intercept generates a #VC exception. VMGEXIT must be
>> used to allow the hypervisor to handle this intercept.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 36 +++++++++++++++++++
>>   1 file changed, 36 insertions(+)
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index 50199845ceef..0cf26c052b32 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -843,6 +843,38 @@ MmioExit (
>>     return Status;
>>   }
>>   
>> +/**
>> +  Handle a WBINVD event.
>> +
>> +  Use the VMGEXIT instruction to handle a WBINVD event.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +WbinvdExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  UINT64  Status;
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
>> +  if (Status) {
>> +    return Status;
>> +  }
>> +
>> +  return 0;
> 
> (1) I'd simplify:
> 
>    return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
> 
> unless you want to keep the Status variable (maybe for inserting DEBUG
> statements more easily, if needed). Even in that case:
> 
>    Status = VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
>    return Status;
> 
> With either option adopted:
> 
> Acked-by: Laszlo Ersek <lersek@redhat.com>
> 
> (The real wart I'm trying to address here is the evaluation of "Status"
> in a logical context, as usual.)

Yup, I'm going through all of the patches.

Thanks!
Tom

> 
> Thanks,
> Laszlo
> 
>> +}
>> +
>>   /**
>>     Handle an MSR event.
>>   
>> @@ -1238,6 +1270,10 @@ VmgExitHandleVc (
>>       NaeExit = MsrExit;
>>       break;
>>   
>> +  case SVM_EXIT_WBINVD:
>> +    NaeExit = WbinvdExit;
>> +    break;
>> +
>>     case SVM_EXIT_NPF:
>>       NaeExit = MmioExit;
>>       break;
>>
> 

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

* Re: [edk2-devel] [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL NAE events
  2020-05-22 14:50     ` Laszlo Ersek
@ 2020-05-22 21:18       ` Lendacky, Thomas
  0 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-22 21:18 UTC (permalink / raw)
  To: devel, lersek
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/22/20 9:50 AM, Laszlo Ersek via groups.io wrote:
> On 05/22/20 16:48, Laszlo Ersek wrote:
>> On 05/19/20 23:50, Lendacky, Thomas wrote:
>>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7Cba73e0d6ccb9488ab10608d7fe5f7c8f%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637257558381229776&amp;sdata=kwjA9Hgo1KKgY%2FarqEiQFDg5QVpx60Kfoh1AWqSuock%3D&amp;reserved=0
>>>
>>> Under SEV-ES, a VMMCALL intercept generates a #VC exception. VMGEXIT must
>>> be used to allow the hypervisor to handle this intercept.
>>>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>>> ---
>>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 48 +++++++++++++++++++
>>>   1 file changed, 48 insertions(+)
>>>
>>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> index 0b7f24fd962c..bf1555234d69 100644
>>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> @@ -875,6 +875,50 @@ WbinvdExit (
>>>     return 0;
>>>   }
>>>   
>>> +/**
>>> +  Handle a VMMCALL event.
>>> +
>>> +  Use the VMGEXIT instruction to handle either a VMMCALL event.
> 
> (2) pls drop the word "either".

I have this in a couple of places, so I'll take care of all of them.

Thanks!
TOm

> 
> Thanks
> Laszlo
> 
>>> +
>>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>>> +                                   Block
>>> +  @param[in, out] Regs             x64 processor context
>>> +  @param[in]      InstructionData  Instruction parsing context
>>> +
>>> +  @retval 0                        Event handled successfully
>>> +  @retval Others                   New exception value to propagate
>>> +
>>> +**/
>>> +STATIC
>>> +UINT64
>>> +VmmCallExit (
>>> +  IN OUT GHCB                     *Ghcb,
>>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  UINT64  Status;
>>> +
>>> +  DecodeModRm (Regs, InstructionData);
>>> +
>>> +  Ghcb->SaveArea.Rax = Regs->Rax;
>>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>>> +  Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);
>>> +  GhcbSetRegValid (Ghcb, GhcbCpl);
>>> +
>>> +  Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
>>> +  if (Status) {
>>
>> (1) Please write (Status > 0) or (Status != 0).
>>
>> Acked-by: Laszlo Ersek <lersek@redhat.com>
>>
>> Thanks
>> Laszlo
>>
>>> +    return Status;
>>> +  }
>>> +
>>> +  if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
>>> +    return UnsupportedExit (Ghcb, Regs, InstructionData);
>>> +  }
>>> +  Regs->Rax = Ghcb->SaveArea.Rax;
>>> +
>>> +  return 0;
>>> +}
>>> +
>>>   /**
>>>     Handle an MSR event.
>>>   
>>> @@ -1395,6 +1439,10 @@ VmgExitHandleVc (
>>>       NaeExit = MsrExit;
>>>       break;
>>>   
>>> +  case SVM_EXIT_VMMCALL:
>>> +    NaeExit = VmmCallExit;
>>> +    break;
>>> +
>>>     case SVM_EXIT_WBINVD:
>>>       NaeExit = WbinvdExit;
>>>       break;
>>>
>>
> 
> 
> 
> 

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

* Re: [edk2-devel] [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
  2020-05-19 21:50 ` [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write " Lendacky, Thomas
  2020-05-22 14:59   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-25 14:47   ` Laszlo Ersek
  2020-05-26 15:06     ` Lendacky, Thomas
  1 sibling, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-25 14:47 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Under SEV-ES, a DR7 read or write intercept generates a #VC exception.
> The #VC handler must provide special support to the guest for this. On
> a DR7 write, the #VC handler must cache the value and issue a VMGEXIT
> to notify the hypervisor of the write. However, the #VC handler must
> not actually set the value of the DR7 register. On a DR7 read, the #VC
> handler must return the cached value of the DR7 register to the guest.
> VMGEXIT is not invoked for a DR7 register read.
> 
> To avoid exception recursion, a #VC exception will not try to read and
> push the actual debug registers into the EFI_SYSTEM_CONTEXT_X64 struct
> and instead push zeroes. The #VC exception handler does not make use of
> the debug registers from saved context.

AFAICS the following patches introcuce / reiterate the per-CPU page concept:

- "MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page
tables" (v8 05/46)
- "OvmfPkg: Create a GHCB page for use during Sec phase" (v8 29/46)
- "OvmfPkg: Create GHCB pages for use during Pei and Dxe phase" (v8 31/46)

I find it somewhat difficult to locate those patches and to learn about
the per-cpu pages from them. The first patch listed above belongs to a
different package. And the two other patches listed above do not precede
(but follow) the present patch.

(1) Therefore please include a paragraph about the per-cpu pages in the
commit message of this patch.

> 
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 105 ++++++++++++++++++
>  .../X64/ExceptionHandlerAsm.nasm              |  17 +++
>  .../X64/Xcode5ExceptionHandlerAsm.nasm        |  17 +++
>  3 files changed, 139 insertions(+)

Please pass "--stat=1000 --stat-graph-width=20" to git-format-patch;
that way, the pathnames will not be truncated, and the graph to the
right will still not be wider than 20 chars.

Why I'm requesting this (and unfortunately there is no way to make the
second switch above permanent, in the git config): because I almost
missed that this patch modifies both UefiCpuPkg and OvmfPkg. It would
have been obvious from the diffstat (if the pathnames had not been
truncated).

(2) Please split the UefiCpuPkg hunks to a separate patch, if possible.

(Or maybe consider squashing those hunks into patch
"UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception"
(v8 11/46), if the UefiCpuPkg owners prefer that.)

> 
> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> index b028b20f255a..e4072d79d704 100644
> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
> @@ -14,6 +14,16 @@
>  
>  #define CR4_OSXSAVE (1 << 18)
>  
> +#define DR7_RESET_VALUE 0x400

(3) From the Intel SDM, this looks like a standard value. I'd say if we
deem it important enough for turning into a macro, then it belongs
elsewhere (in some more visible header file).

Otherwise (given that we only use it once, below), I think we could
simply open-code it at the location of use, with a comment.

> +
> +//
> +// Per-CPU data mapping structure
> +//
> +typedef struct {
> +  BOOLEAN  Dr7Cached;
> +  UINT64   Dr7;
> +} SEV_ES_PER_CPU_DATA;
> +
>  //
>  // Instruction execution mode definition
>  //
> @@ -1494,6 +1504,93 @@ RdtscExit (
>    return 0;
>  }
>  
> +/**
> +  Handle a DR7 register write event.
> +
> +  Use the VMGEXIT instruction to handle a DR7 write event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +  @retval Others                   New exception value to propagate
> +
> +**/
> +STATIC
> +UINT64
> +Dr7WriteExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
> +  SEV_ES_PER_CPU_DATA            *SevEsData;
> +  INTN                           *Register;

(4) This should be UINT64, per my earlier request.

> +  UINT64                         Status;
> +
> +  Ext = &InstructionData->Ext;
> +  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
> +
> +  DecodeModRm (Regs, InstructionData);
> +
> +  /* MOV DRn always treats MOD == 3 no matter how encoded */

(5) comment style

> +  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
> +
> +  /* Using a value of 0 for ExitInfo1 means RAX holds the value */

(6) comment style

> +  Ghcb->SaveArea.Rax = *Register;
> +  GhcbSetRegValid (Ghcb, GhcbRax);
> +
> +  Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
> +  if (Status) {

(7) please compare with 0 explicitly

> +    return Status;
> +  }
> +
> +  SevEsData->Dr7 = *Register;
> +  SevEsData->Dr7Cached = TRUE;

Hmmm... I'm wondering where this BOOLEAN gets re-set to FALSE on a
platform reset.

In patch "OvmfPkg: Create GHCB pages for use during Pei and Dxe phase",
in function AmdSevEsInitialize(), we have a ZeroMem(). That should cover
it for PEI and DXE; OK.

(8) In patch "OvmfPkg: Create a GHCB page for use during Sec phase"
however, we don't seem to zero out the per-cpu page itself (which
resides just after PcdOvmfSecGhcbBase).

Do we do that elsewhere? (Sorry if I'm just not seeing it.)

I'm asking because, after a platform reset, SevEsData->Dr7Cached may
read as TRUE in SEC at the very first access (it lives at a fixed
location, and QEMU platform reset does not clear RAM). And so we could
return the value cached from the previous boot rather than 0x400.


> +
> +  return 0;
> +}
> +
> +/**
> +  Handle a DR7 register read event.
> +
> +  Use the VMGEXIT instruction to handle a DR7 read event.
> +
> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
> +                                   Block
> +  @param[in, out] Regs             x64 processor context
> +  @param[in]      InstructionData  Instruction parsing context
> +
> +  @retval 0                        Event handled successfully
> +
> +**/
> +STATIC
> +UINT64
> +Dr7ReadExit (
> +  IN OUT GHCB                     *Ghcb,
> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
> +  )
> +{
> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
> +  SEV_ES_PER_CPU_DATA            *SevEsData;
> +  INTN                           *Register;

(9) Should be UINT64.

> +
> +  Ext = &InstructionData->Ext;
> +  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
> +
> +  DecodeModRm (Regs, InstructionData);
> +
> +  /* MOV DRn always treats MOD == 3 no matter how encoded */

(10) Please fix the comment style.

> +  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
> +  *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : DR7_RESET_VALUE;
> +
> +  return 0;
> +}
> +
>  /**
>    Handle a #VC exception.
>  
> @@ -1538,6 +1635,14 @@ VmgExitHandleVc (
>  
>    ExitCode = Regs->ExceptionData;
>    switch (ExitCode) {
> +  case SVM_EXIT_DR7_READ:
> +    NaeExit = Dr7ReadExit;
> +    break;
> +
> +  case SVM_EXIT_DR7_WRITE:
> +    NaeExit = Dr7WriteExit;
> +    break;
> +
>    case SVM_EXIT_RDTSC:
>      NaeExit = RdtscExit;
>      break;

Stopping here (before the UefiCpuPkg hunks).

Thanks!
Laszlo

> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
> index 3814f9de3703..2a5545ecfd41 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
> @@ -18,6 +18,8 @@
>  ; CommonExceptionHandler()
>  ;
>  
> +%define VC_EXCEPTION 29
> +
>  extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
>  extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
>  extern ASM_PFX(CommonExceptionHandler)
> @@ -224,6 +226,9 @@ HasErrorCode:
>      push    rax
>  
>  ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
> +    cmp     qword [rbp + 8], VC_EXCEPTION
> +    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers ignored
> +
>      mov     rax, dr7
>      push    rax
>      mov     rax, dr6
> @@ -236,7 +241,19 @@ HasErrorCode:
>      push    rax
>      mov     rax, dr0
>      push    rax
> +    jmp     DrFinish
>  
> +VcDebugRegs:
> +;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion
> +    xor     rax, rax
> +    push    rax
> +    push    rax
> +    push    rax
> +    push    rax
> +    push    rax
> +    push    rax
> +
> +DrFinish:
>  ;; FX_SAVE_STATE_X64 FxSaveState;
>      sub rsp, 512
>      mov rdi, rsp
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
> index 19198f273137..26cae56cc5cf 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
> @@ -18,6 +18,8 @@
>  ; CommonExceptionHandler()
>  ;
>  
> +%define VC_EXCEPTION 29
> +
>  extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
>  extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
>  extern ASM_PFX(CommonExceptionHandler)
> @@ -225,6 +227,9 @@ HasErrorCode:
>      push    rax
>  
>  ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
> +    cmp     qword [rbp + 8], VC_EXCEPTION
> +    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers ignored
> +
>      mov     rax, dr7
>      push    rax
>      mov     rax, dr6
> @@ -237,7 +242,19 @@ HasErrorCode:
>      push    rax
>      mov     rax, dr0
>      push    rax
> +    jmp     DrFinish
>  
> +VcDebugRegs:
> +;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion
> +    xor     rax, rax
> +    push    rax
> +    push    rax
> +    push    rax
> +    push    rax
> +    push    rax
> +    push    rax
> +
> +DrFinish:
>  ;; FX_SAVE_STATE_X64 FxSaveState;
>      sub rsp, 512
>      mov rdi, rsp
> 


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

* Re: [edk2-devel] [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase
  2020-05-19 21:50 ` [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase Lendacky, Thomas
@ 2020-05-25 15:07   ` Laszlo Ersek
  2020-05-26 15:41     ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-25 15:07 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> A GHCB page is needed during the Sec phase, so this new page must be
> created. Since the #VC exception handler routines assume that a per-CPU
> variable area is immediately after the GHCB, this per-CPU variable area
> must also be created. Since the GHCB must be marked as an un-encrypted,
> or shared, page, an additional pagetable page is required to break down
> the 2MB region where the GHCB page lives into 4K pagetable entries.
> 
> Create a new entry in the OVMF memory layout for the new page table
> page and for the SEC GHCB and per-CPU variable pages. After breaking down
> the 2MB page, update the GHCB page table entry to remove the encryption
> mask.
> 
> The GHCB page will be used by the SEC #VC exception handler. The #VC
> exception handler will fill in the necessary fields of the GHCB and exit
> to the hypervisor using the VMGEXIT instruction. The hypervisor then
> accesses the GHCB in order to perform the requested function.
> 
> Two new fixed PCDs are needed to support the SEC GHCB page:
>   - PcdOvmfSecGhcbBase  UINT64 value that is the base address of the
>                         GHCB used during the SEC phase.
>   - PcdOvmfSecGhcbSize  UINT64 value that is the size, in bytes, of the
>                         GHCB area used during the SEC phase.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/OvmfPkg.dec                       |  9 +++
>  OvmfPkg/OvmfPkgX64.fdf                    |  6 ++
>  OvmfPkg/ResetVector/ResetVector.inf       |  5 ++
>  OvmfPkg/ResetVector/Ia32/PageTables64.asm | 70 +++++++++++++++++++++++
>  OvmfPkg/ResetVector/ResetVector.nasmb     | 17 ++++++
>  5 files changed, 107 insertions(+)
> 
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 65bb2bb0eb4c..02ad62ed9f43 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -281,6 +281,15 @@ [PcdsFixedAtBuild]
>    ## Number of page frames to use for storing grant table entries.
>    gUefiOvmfPkgTokenSpaceGuid.PcdXenGrantFrames|4|UINT32|0x33
>  
> +  ## Specify the extra page table needed to mark the GHCB as unencrypted.
> +  #  The value should be a multiple of 4KB for each.
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|0x0|UINT32|0x3a
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize|0x0|UINT32|0x3b
> +
> +  ## The base address of the SEC GHCB page used by SEV-ES.
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|0|UINT32|0x3c
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize|0|UINT32|0x3d
> +
>  [PcdsDynamic, PcdsDynamicEx]
>    gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10

OK, the token values have been updated, due to:

- commit 7efce2e59c20 ("OvmfPkg/PvScsiDxe: Report the number of targets
and LUNs", 2020-03-30)

- commit c4c15b870239 ("OvmfPkg/PvScsiDxe: Support sending SCSI request
and receive response", 2020-03-30)

- commit 093cceaf79b5 ("OvmfPkg/MptScsiDxe: Report targets and one LUN",
2020-05-05)

(Independently, when I reviewed what would become 505812ae1d2d
("OvmfPkg/MptScsiDxe: Implement the PassThru method", 2020-05-05), I
missed that 0x39 is followed by 0x3A, not 0x40. Oh well.)


> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> index bfca1eff9e83..88b1e880e603 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -76,6 +76,12 @@ [FD.MEMFD]
>  0x007000|0x001000
>  gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>  
> +0x008000|0x001000
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
> +
> +0x009000|0x002000
> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
> +
>  0x010000|0x010000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>  
> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
> index b0ddfa5832a2..483fd90fe785 100644
> --- a/OvmfPkg/ResetVector/ResetVector.inf
> +++ b/OvmfPkg/ResetVector/ResetVector.inf
> @@ -26,6 +26,7 @@ [Sources]
>  [Packages]
>    OvmfPkg/OvmfPkg.dec
>    MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
>    UefiCpuPkg/UefiCpuPkg.dec
>  
>  [BuildOptions]
> @@ -33,5 +34,9 @@ [BuildOptions]
>     *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>  
>  [Pcd]
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> index abad009f20f5..c3587a1b7814 100644
> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> @@ -21,6 +21,11 @@ BITS    32
>  %define PAGE_2M_MBO            0x080
>  %define PAGE_2M_PAT          0x01000
>  
> +%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
> +                          PAGE_DIRTY + \
> +                          PAGE_READ_WRITE + \
> +                          PAGE_PRESENT)
> +
>  %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
>                            PAGE_ACCESSED + \
>                            PAGE_DIRTY + \
> @@ -75,6 +80,37 @@ NoSev:
>  SevExit:
>      OneTimeCallRet CheckSevFeature
>  
> +; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
> +; is enabled.
> +;
> +; Modified:  EAX, EBX, ECX
> +;
> +; If SEV-ES is enabled then EAX will be non-zero.
> +; If SEV-ES is disabled then EAX will be zero.
> +;
> +CheckSevEsFeature:
> +    xor       eax, eax
> +
> +    ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
> +    ; mask.
> +    test      edx, edx
> +    jz        NoSevEs
> +
> +    ; Save current value of encryption mask
> +    mov       ebx, edx
> +
> +    ; Check if SEV-ES is enabled
> +    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
> +    mov       ecx, 0xc0010131
> +    rdmsr
> +    and       eax, 2
> +
> +    ; Restore encryption mask
> +    mov       edx, ebx
> +
> +NoSevEs:
> +    OneTimeCallRet CheckSevEsFeature
> +
>  ;
>  ; Modified:  EAX, EBX, ECX, EDX
>  ;
> @@ -139,6 +175,40 @@ pageTableEntriesLoop:
>      mov     [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
>      loop    pageTableEntriesLoop
>  
> +    OneTimeCall   CheckSevEsFeature
> +    test    eax, eax
> +    jz      SetCr3
> +
> +    ;
> +    ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
> +    ; This requires the 2MB page for this range be broken down into 512 4KB
> +    ; pages.  All will be marked encrypted, except for the GHCB.
> +    ;
> +    mov     ecx, (GHCB_BASE >> 21)
> +    mov     eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
> +    mov     [ecx * 8 + PT_ADDR (0x2000)], eax
> +
> +    ;
> +    ; Page Table Entries (512 * 4KB entries => 2MB)
> +    ;
> +    mov     ecx, 512
> +pageTableEntries4kLoop:
> +    mov     eax, ecx
> +    dec     eax
> +    shl     eax, 12
> +    add     eax, GHCB_BASE & 0xFFE0_0000
> +    add     eax, PAGE_4K_PDE_ATTR
> +    mov     [ecx * 8 + GHCB_PT_ADDR - 8], eax
> +    mov     [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
> +    loop    pageTableEntries4kLoop
> +
> +    ;
> +    ; Clear the encryption bit from the GHCB entry
> +    ;
> +    mov     ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
> +    mov     [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
> +

(1) Why did you remove "clearGhcbMemoryLoop" (in the v6->v7 transition)?

I think that's exactly the clearing loop (minimally for the CPU#0
per-CPU page) that I was just looking for in point (8) under
"OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events" (v8 26/46).

Hm... the v7 blurb says, "Ensure the per-CPU variable page remains
encrypted". OK, but that still doesn't explain why we don't clear it
(just for the guest to see).

Also, if the patch was non-trivially modified in v7, then arguably my
R-b (given originally under "RFC PATCH v3 26/43") should have been removed.

Please re-instate "clearGhcbMemoryLoop" (and then keep the R-b).

Thanks,
Laszlo

> +SetCr3:
>      ;
>      ; Set CR3 now that the paging structures are available
>      ;
> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
> index 75cfe16654b1..bfb77e439105 100644
> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
> @@ -53,8 +53,25 @@
>      %error "This implementation inherently depends on PcdOvmfSecPageTablesSize"
>    %endif
>  
> +  %if (FixedPcdGet32 (PcdOvmfSecGhcbPageTableSize) != 0x1000)
> +    %error "This implementation inherently depends on PcdOvmfSecGhcbPageTableSize"
> +  %endif
> +
> +  %if (FixedPcdGet32 (PcdOvmfSecGhcbSize) != 0x2000)
> +    %error "This implementation inherently depends on PcdOvmfSecGhcbSize"
> +  %endif
> +
> +  %if ((FixedPcdGet32 (PcdOvmfSecGhcbBase) >> 21) != \
> +       ((FixedPcdGet32 (PcdOvmfSecGhcbBase) + FixedPcdGet32 (PcdOvmfSecGhcbSize) - 1) >> 21))
> +    %error "This implementation inherently depends on PcdOvmfSecGhcbBase not straddling a 2MB boundary"
> +  %endif
> +
>    %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
>  %include "Ia32/Flat32ToFlat64.asm"
> +
> +  %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
> +  %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
> +  %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
>  %include "Ia32/PageTables64.asm"
>  %endif
>  
> 


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

* Re: [edk2-devel] [PATCH v8 31/46] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase
  2020-05-19 21:50 ` [PATCH v8 31/46] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase Lendacky, Thomas
@ 2020-05-25 15:21   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-25 15:21 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/19/20 23:50, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Allocate memory for the GHCB pages and the per-CPU variable pages during
> SEV initialization for use during Pei and Dxe phases. The GHCB page(s)
> must be shared pages, so clear the encryption mask from the current page
> table entries. Upon successful allocation, set the GHCB PCDs (PcdGhcbBase
> and PcdGhcbSize).
> 
> The per-CPU variable page needs to be unique per AP. Using the page after
> the GHCB ensures that it is unique per AP. Only the GHCB page is marked as
> shared, keeping the per-CPU variable page encyrpted. The same logic is
> used in DXE using CreateIdentityMappingPageTables() before switching to
> the DXE pagetables.
> 
> The GHCB pages (one per vCPU) will be used by the PEI and DXE #VC
> exception handlers. The #VC exception handler will fill in the necessary
> fields of the GHCB and exit to the hypervisor using the VMGEXIT
> instruction. The hypervisor then accesses the GHCB associated with the
> vCPU in order to perform the requested function.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/OvmfPkgIa32.dsc             |  2 ++
>  OvmfPkg/OvmfPkgIa32X64.dsc          |  2 ++
>  OvmfPkg/OvmfPkgX64.dsc              |  2 ++
>  OvmfPkg/PlatformPei/PlatformPei.inf |  2 ++
>  OvmfPkg/PlatformPei/AmdSev.c        | 45 ++++++++++++++++++++++++++++-
>  5 files changed, 52 insertions(+), 1 deletion(-)
> 
> diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
> index b289260c1f2d..f849a7d698cd 100644
> --- a/OvmfPkg/OvmfPkgIa32.dsc
> +++ b/OvmfPkg/OvmfPkgIa32.dsc
> @@ -611,6 +611,8 @@ [PcdsDynamicDefault]
>    gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
>  
>    # Set SEV-ES defaults
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
>  
>  !if $(SMM_REQUIRE) == TRUE
> diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
> index 00faf5036670..d1b4ffedc6af 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.dsc
> +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
> @@ -623,6 +623,8 @@ [PcdsDynamicDefault]
>    gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
>  
>    # Set SEV-ES defaults
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
>  
>  !if $(SMM_REQUIRE) == TRUE
> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> index 58671828c5fe..ebc903d922cf 100644
> --- a/OvmfPkg/OvmfPkgX64.dsc
> +++ b/OvmfPkg/OvmfPkgX64.dsc
> @@ -621,6 +621,8 @@ [PcdsDynamicDefault]
>    gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0
>  
>    # Set SEV-ES defaults
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
>  
>  !if $(SMM_REQUIRE) == TRUE
> diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
> index a54d10ba90d5..4742e1bdf42b 100644
> --- a/OvmfPkg/PlatformPei/PlatformPei.inf
> +++ b/OvmfPkg/PlatformPei/PlatformPei.inf
> @@ -102,6 +102,8 @@ [Pcd]
>    gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack
>    gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable
>    gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize
>    gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
> diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
> index 4dc5340caa7a..4fd4534cabea 100644
> --- a/OvmfPkg/PlatformPei/AmdSev.c
> +++ b/OvmfPkg/PlatformPei/AmdSev.c
> @@ -10,12 +10,15 @@
>  // The package level header files this module uses
>  //
>  #include <IndustryStandard/Q35MchIch9.h>
> +#include <Library/BaseMemoryLib.h>
>  #include <Library/DebugLib.h>
>  #include <Library/HobLib.h>
>  #include <Library/MemEncryptSevLib.h>
> +#include <Library/MemoryAllocationLib.h>
>  #include <Library/PcdLib.h>
>  #include <PiPei.h>
>  #include <Register/Amd/Cpuid.h>
> +#include <Register/Amd/Msr.h>
>  #include <Register/Cpuid.h>
>  #include <Register/Intel/SmramSaveStateMap.h>
>  
> @@ -32,7 +35,10 @@ AmdSevEsInitialize (
>    VOID
>    )
>  {
> -  RETURN_STATUS     PcdStatus;
> +  VOID              *GhcbBase;
> +  PHYSICAL_ADDRESS  GhcbBasePa;
> +  UINTN             GhcbPageCount, PageCount;
> +  RETURN_STATUS     PcdStatus, DecryptStatus;
>  
>    if (!MemEncryptSevEsIsEnabled ()) {
>      return;
> @@ -40,6 +46,43 @@ AmdSevEsInitialize (
>  
>    PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
>    ASSERT_RETURN_ERROR (PcdStatus);
> +
> +  //
> +  // Allocate GHCB and per-CPU variable pages.
> +  //
> +  GhcbPageCount = mMaxCpuCount * 2;
> +  GhcbBase = AllocatePages (GhcbPageCount);
> +  ASSERT (GhcbBase != NULL);
> +
> +  GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN) GhcbBase;
> +
> +  //
> +  // Each vCPU gets two consecutive pages, the first is the GHCB and the
> +  // second is the per-CPU variable page. Loop through the allocation and
> +  // only clear the encryption mask for the GHCB pages.
> +  //
> +  for (PageCount = 0; PageCount < GhcbPageCount; PageCount += 2) {
> +    DecryptStatus = MemEncryptSevClearPageEncMask (
> +      0,
> +      GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),
> +      1,
> +      TRUE
> +      );
> +    ASSERT_RETURN_ERROR (DecryptStatus);
> +  }

This part is new in v7 (in sync with the updated commit message).

It looks OK to me, but please don't carry over R-b tags given previously
when modifying patches non-trivially.

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo


> +
> +  ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));
> +
> +  PcdStatus = PcdSet64S (PcdGhcbBase, GhcbBasePa);
> +  ASSERT_RETURN_ERROR (PcdStatus);
> +  PcdStatus = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));
> +  ASSERT_RETURN_ERROR (PcdStatus);
> +
> +  DEBUG ((DEBUG_INFO,
> +    "SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
> +    (UINT64)GhcbPageCount, GhcbBase));
> +
> +  AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
>  }
>  
>  /**
> 


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

* Re: [edk2-devel] [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage
  2020-05-19 21:51 ` [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage Lendacky, Thomas
@ 2020-05-25 16:00   ` Laszlo Ersek
  2020-05-26 14:28     ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-25 16:00 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/19/20 23:51, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Reserve a fixed area of memory for SEV-ES use and set a fixed PCD,
> PcdSevEsWorkAreaBase, to this value.
> 
> This area will be used by SEV-ES support for two purposes:
>   1. Communicating the SEV-ES status during BSP boot to SEC:
>      Using a byte of memory from the page, the BSP reset vector code can
>      communicate the SEV-ES status to SEC for use before exception
>      handling can be enabled in SEC. After SEC, this field is no longer
>      valid and the standard way of determine if SEV-ES is active should
>      be used.
> 
>   2. Establishing an area of memory for AP boot support:
>      A hypervisor is not allowed to update an SEV-ES guest's register
>      state, so when booting an SEV-ES guest AP, the hypervisor is not
>      allowed to set the RIP to the guest requested value. Instead an
>      SEV-ES AP must be re-directed from within the guest to the actual
>      requested staring location as specified in the INIT-SIPI-SIPI
>      sequence.
> 
>      Use this memory for reset vector code that can be programmed to have
>      the AP jump to the desired RIP location after starting the AP. This
>      is required for only the very first AP reset.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/OvmfPkgX64.fdf                    |  3 +++
>  OvmfPkg/ResetVector/ResetVector.inf       |  1 +
>  OvmfPkg/ResetVector/Ia32/PageTables64.asm | 11 +++++++++++
>  OvmfPkg/ResetVector/ResetVector.nasmb     |  1 +
>  4 files changed, 16 insertions(+)
> 
> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> index 88b1e880e603..8836b30a0cef 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -82,6 +82,9 @@ [FD.MEMFD]
>  0x009000|0x002000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>  
> +0x00B000|0x001000
> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
> +
>  0x010000|0x010000
>  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>  
> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
> index 483fd90fe785..e94e1bfcce7e 100644
> --- a/OvmfPkg/ResetVector/ResetVector.inf
> +++ b/OvmfPkg/ResetVector/ResetVector.inf
> @@ -34,6 +34,7 @@ [BuildOptions]
>     *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>  
>  [Pcd]
> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> index c3587a1b7814..73a4eaadb1b6 100644
> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> @@ -89,6 +89,10 @@ SevExit:
>  ; If SEV-ES is disabled then EAX will be zero.
>  ;
>  CheckSevEsFeature:
> +    ; Initialize the first byte of the workarea to zero to communicate to
> +    ; the SEC phase that SEV-ES is not enabled.
> +    mov     byte[SEV_ES_WORK_AREA], 0
> +
>      xor       eax, eax
>  
>      ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
> @@ -108,6 +112,13 @@ CheckSevEsFeature:
>      ; Restore encryption mask
>      mov       edx, ebx
>  
> +    test      eax, eax
> +    jz        NoSevEs
> +
> +    ; Set the first byte of the workarea to one to communicate to the SEC
> +    ; phase that SEV-ES is enabled.
> +    mov       byte[SEV_ES_WORK_AREA], 1
> +
>  NoSevEs:
>      OneTimeCallRet CheckSevEsFeature
>  
> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
> index bfb77e439105..2967617bfaa0 100644
> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
> @@ -72,6 +72,7 @@
>    %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
>    %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
>    %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
> +  %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>  %include "Ia32/PageTables64.asm"
>  %endif
>  
> 

The OvmfPkg/ResetVector modifications have been moved to this patch, at
least in part, from patch "OvmfPkg/ResetVector: Add support for a 32-bit
SEV check".

And I don't understand why.

I mean it's possible that setting the first byte of the work area to 1
does not belong in "OvmfPkg/ResetVector: Add support for a 32-bit SEV
check". That's OK; then said manipulation of the work area should be
split to its own patch, which I should then review afresh.

What's not OK is to move code between two reviewed patches *and* keep my
R-b on both.

Please be more transparent about incremental changes.

(1) Please revert this patch to its v7 state, and keep my R-b on it.

(2) Please split the ResetVector changes to a new patch. For the subject
line, I suggest:

OvmfPkg/ResetVector: communicate SEV-ES status to SEC before exceptions

or something similar.

Thanks
Laszlo


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

* Re: [edk2-devel] [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check
  2020-05-19 21:51 ` [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check Lendacky, Thomas
@ 2020-05-25 16:50   ` Laszlo Ersek
  2020-05-26 16:31     ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-25 16:50 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

Tom,

On 05/19/20 23:51, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
>
> During BSP startup, the reset vector code will issue a CPUID instruction
> while in 32-bit mode. When running as an SEV-ES guest, this will trigger
> a #VC exception.
>
> Add exception handling support to the early reset vector code to catch
> these exceptions.  Also, since the guest is in 32-bit mode at this point,
> writes to the GHCB will be encrypted and thus not able to be read by the
> hypervisor, so use the GHCB CPUID request/response protocol to obtain the
> requested CPUID function values and provide these to the guest.
>
> The exception handling support is active during the SEV check and uses the
> OVMF temporary RAM space for a stack. After the SEV check is complete, the
> exception handling support is removed and the stack pointer cleared.
>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/ResetVector/ResetVector.inf       |   2 +
>  OvmfPkg/ResetVector/Ia32/PageTables64.asm | 329 +++++++++++++++++++---
>  OvmfPkg/ResetVector/ResetVector.nasmb     |   1 +
>  3 files changed, 294 insertions(+), 38 deletions(-)

this doesn't work for me.

Under your v5 posting, I reviewed those OvmfPkg patches that still
needed my review.

The v6 posting carried all my R-b's; all OvmfPkg patches had been
reviewed. I trusted you and I only verified the commit messages for my
R-b's. I thought the OvmfPkg state was final.

The v7 posting again carried my R-b's; I briefly checked the v6->v7
changes in the blurb, and re-checked my R-b's on the OvmfPkg patches.
This was in the v7 blurb:

> Changes since v6:
> - Add function comments to all functions, including local functions
> - Add function parameter direction to all functions (in/out)
> - Add support for MMIO MOVZX/MOVSX instructions
> - Ensure the per-CPU variable page remains encrypted
> - Coding-style fixes as identified by Ecc

This summary didn't indicate I'd have to go through the OvmfPkg patches
again -- and the presence of my R-b's on all the OvmfPkg patches
supported that impression.

I commented on v7 only later, independently; namely on two topics:

- on one of the S3 reservation aspects,
- on the upcoming / requested movement of VmgExitLib to OvmfPkg.

These were the two updates I was going to expect in v8.

So, in order to "page in" your work again, in preparation for reviewing
v8, I decided to review the v5->v6 changes in more detail -- the code
too (incrementally), not just the picking up of my R-b's, like I had
originally done under v6. I was happy with v6, after performing this
review; see <https://bugzilla.tianocore.org/show_bug.cgi?id=2198#c10>.

Now I'm reviewing the differences (incrementally from v6 to v8), and I'm
shocked how many changes you incorporated into preexistent patches,
while keeping my R-b's.

On this patch, you significantly changed the logic from v6 to v7, and I
don't have the slightest clue why. I don't feel inclined to
reverse-engineer the logic change from the v6->v7 interdiff. The right
way to present a significant change is to (a) drop the existent R-b's
from the patch, and (b) spell out the news in the blurb and/or in the
"notes" section of the individual patch. If you had dropped the R-b in
v7, then I would have known to review the changes in v7 at once (rather
than let it accumulate to v8). And if you had explained the updates, I
may have started with a re-review of the patch from scratch (and
wouldn't be stuck with an incremental one / interdiff now, between v6
and v8).

Then, the patch changed *again*, from v7 to v8; and my R-b (which only
applied to v6) got carried forward again.

Consider the v7->v8 changes noted in the blurb:

> Changes since v7:
> - Reserve the SEV-ES workarea when S3 is enabled
> - Fix warnings issued by the Visual Studio compiler
> - Create a NULL VmgExitLib instance that is used for VMGEXIT
>   related operations as well as #VC handling. Then create the full
>   VmgExitLib support only in OvmfPkg - where it will be used. This
>   removes a bunch of implementation code from platforms that will
>   not be using the functionality.
> - Remove single use interfaces from the VmgExitLib (VmgMmioWrite
>   and VmgSetApJumpTable)

Not a word on this patch, as far as I can see.

I don't even know what to do about this patch now. I'd be really unhappy
to review it from zero; it's a difficult one. The reset vector is also
shared with non-SEV X64, so it's not like I can just slap an Acked-by on
it.

(1) Unless there was an actual bug in the v6 version of this patch,
please let's go back to that. IOW, if the v6->v8 changes are only
cleanups or optimizations, let's please postpone them.

I'm going to take a walk now.

Laszlo


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

* Re: [edk2-devel] [PATCH v8 35/46] OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported
  2020-05-19 21:51 ` [PATCH v8 35/46] OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported Lendacky, Thomas
@ 2020-05-26  7:53   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-26  7:53 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel, Anthony Perard,
	Julien Grall

On 05/19/20 23:51, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Protect the SEV-ES work area memory used by an SEV-ES guest.
> 
> Regarding the lifecycle of the SEV-ES memory area:
>   PcdSevEsWorkArea
> 
> (a) when and how it is initialized after first boot of the VM
> 
>   If SEV-ES is enabled, the SEV-ES area is initialized during
>   the SEC phase [OvmfPkg/ResetVector/Ia32/PageTables64.asm].
> 
> (b) how it is protected from memory allocations during DXE
> 
>   If SEV-ES is enabled, then InitializeRamRegions()
>   [OvmfPkg/PlatformPei/MemDetect.c] protects the ranges with either
>   an AcpiNVS (S3 enabled) or BootServicesData (S3 disabled) memory
>   allocation HOB, in PEI.
> 
> (c) how it is protected from the OS
> 
>   If S3 is enabled, then (b) reserves it from the OS too.
> 
>   If S3 is disabled, then the range needs no protection.
> 
> (d) how it is accessed on the S3 resume path
> 
>   It is rewritten same as in (a), which is fine because (b) reserved it.
> 
> (e) how it is accessed on the warm reset path
> 
>   It is rewritten same as in (a).
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
> Cc: Anthony Perard <anthony.perard@citrix.com>
> Cc: Julien Grall <julien@xen.org>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/PlatformPei/PlatformPei.inf |  2 ++
>  OvmfPkg/PlatformPei/MemDetect.c     | 20 ++++++++++++++++++++
>  2 files changed, 22 insertions(+)
> 
> diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
> index 4742e1bdf42b..c53be2f4925c 100644
> --- a/OvmfPkg/PlatformPei/PlatformPei.inf
> +++ b/OvmfPkg/PlatformPei/PlatformPei.inf
> @@ -118,6 +118,8 @@ [FixedPcd]
>    gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiReservedMemoryType
>    gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode
>    gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData
> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>  
>  [FeaturePcd]
>    gUefiOvmfPkgTokenSpaceGuid.PcdCsmEnable
> diff --git a/OvmfPkg/PlatformPei/MemDetect.c b/OvmfPkg/PlatformPei/MemDetect.c
> index 6b5fee166b5d..ffbbef891a11 100644
> --- a/OvmfPkg/PlatformPei/MemDetect.c
> +++ b/OvmfPkg/PlatformPei/MemDetect.c
> @@ -940,5 +940,25 @@ InitializeRamRegions (
>            );
>        }
>      }
> +
> +#ifdef MDE_CPU_X64
> +    if (MemEncryptSevEsIsEnabled ()) {
> +      //
> +      // If SEV-ES is enabled, reserve the SEV-ES work area.
> +      //
> +      // Since this memory range will be used by the Reset Vector on S3
> +      // resume, it must be reserved as ACPI NVS.
> +      //
> +      // If S3 is unsupported, then various drivers might still write to the
> +      // work area. We ought to prevent DXE from serving allocation requests
> +      // such that they would overlap the work area.
> +      //
> +      BuildMemoryAllocationHob (
> +        (EFI_PHYSICAL_ADDRESS)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaBase),
> +        (UINT64)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaSize),
> +        mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
> +        );
> +    }
> +#endif
>    }
>  }
> 

Reviewed-by: Laszlo Ersek <lersek@redhat.com>


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

* Re: [edk2-devel] [PATCH v8 37/46] OvmfPkg/Sec: Add #VC exception handling for Sec phase
  2020-05-19 21:51 ` [PATCH v8 37/46] OvmfPkg/Sec: Add #VC exception handling for Sec phase Lendacky, Thomas
@ 2020-05-26 13:58   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-26 13:58 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/19/20 23:51, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> An SEV-ES guest will generate a #VC exception when it encounters a
> non-automatic exit (NAE) event. It is expected that the #VC exception
> handler will communicate with the hypervisor using the GHCB to handle
> the NAE event.
> 
> NAE events can occur during the Sec phase, so initialize exception
> handling early in the OVMF Sec support.
> 
> Before establishing the exception handling, validate that the supported
> version of the SEV-ES protocol in OVMF is supported by the hypervisor.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  OvmfPkg/Sec/SecMain.inf |   4 +
>  OvmfPkg/Sec/SecMain.c   | 181 +++++++++++++++++++++++++++++++++++++---
>  2 files changed, 172 insertions(+), 13 deletions(-)

Nice comments relative to v6, my R-b stands.

Thanks,
Laszlo


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

* Re: [edk2-devel] [PATCH v8 39/46] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES
  2020-05-19 21:51 ` [PATCH v8 39/46] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES Lendacky, Thomas
@ 2020-05-26 14:07   ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-26 14:07 UTC (permalink / raw)
  To: devel, thomas.lendacky
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/19/20 23:51, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> The flash detection routine will attempt to determine how the flash
> device behaves (e.g. ROM, RAM, Flash). But when SEV-ES is enabled and
> the flash device behaves as a ROM device (meaning it is marked read-only
> by the hypervisor), this check may result in an infinite nested page fault
> because of the attempted write. Since the instruction cannot be emulated
> when SEV-ES is enabled, the RIP is never advanced, resulting in repeated
> nested page faults.
> 
> When SEV-ES is enabled, exit the flash detection early and assume that
> the FD behaves as Flash. This will result in QemuFlashWrite() being called
> to store EFI variables, which will also result in an infinite nested page
> fault when the write is performed. In this case, update QemuFlashWrite()
> to use the VMGEXIT MMIO write support to have the hypervisor perform the
> write without having to emulate the instruction.
> 
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  .../FvbServicesRuntimeDxe.inf                 |  2 +
>  .../QemuFlash.h                               | 13 ++++++
>  .../QemuFlash.c                               | 23 +++++++++--
>  .../QemuFlashDxe.c                            | 40 +++++++++++++++++++
>  .../QemuFlashSmm.c                            | 16 ++++++++
>  5 files changed, 91 insertions(+), 3 deletions(-)

- subject line has been cleaned up relative to v6, OK

- commit message updated in sync with open-coding VmgMmioWrite() in this
patch; and VmgMmioWrite() has been documented in the v8 blurb -- OK

> 
> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> index 72cabba4357d..8bb2325157ea 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> @@ -38,6 +38,7 @@ [Sources]
>  [Packages]
>    MdePkg/MdePkg.dec
>    MdeModulePkg/MdeModulePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
>    OvmfPkg/OvmfPkg.dec
>  
>  [LibraryClasses]
> @@ -52,6 +53,7 @@ [LibraryClasses]
>    UefiBootServicesTableLib
>    UefiDriverEntryPoint
>    UefiRuntimeLib
> +  VmgExitLib
>  
>  [Guids]
>    gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
> index f1afabcbe6ae..219d0d6e83cf 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
> @@ -89,5 +89,18 @@ QemuFlashBeforeProbe (
>    IN  UINTN                   FdBlockCount
>    );
>  
> +/**
> +  Write to QEMU Flash
> +
> +  @param[in] Ptr    Pointer to the location to write.
> +  @param[in] Value  The value to write.
> +
> +**/
> +VOID
> +QemuFlashPtrWrite (
> +  IN        volatile UINT8    *Ptr,
> +  IN        UINT8             Value
> +  );
> +
>  #endif
>  

- New comment, OK.

> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
> index 1b0d6c053f1a..0d29bf701aca 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
> @@ -9,6 +9,7 @@
>  
>  #include <Library/BaseMemoryLib.h>
>  #include <Library/DebugLib.h>
> +#include <Library/MemEncryptSevLib.h>
>  #include <Library/PcdLib.h>
>  
>  #include "QemuFlash.h"
> @@ -80,6 +81,21 @@ QemuFlashDetected (
>  
>    DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
>  
> +  if (MemEncryptSevEsIsEnabled ()) {
> +    //
> +    // When SEV-ES is enabled, the check below can result in an infinite
> +    // loop with respect to a nested page fault. When the memslot is mapped
> +    // read-only, the nested page table entry is read-only. The check below
> +    // will cause a nested page fault that cannot be emulated, causing
> +    // the instruction to retried over and over. For SEV-ES, acknowledge that
> +    // the FD appears as ROM and not as FLASH, but report FLASH anyway because
> +    // FLASH behavior can be simulated using VMGEXIT.
> +    //
> +    DEBUG ((DEBUG_INFO,
> +      "QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"));
> +    return TRUE;
> +  }
> +
>    OriginalUint8 = *Ptr;
>    *Ptr = CLEAR_STATUS_CMD;
>    ProbeUint8 = *Ptr;
> @@ -181,8 +197,9 @@ QemuFlashWrite (
>    //
>    Ptr = QemuFlashPtr (Lba, Offset);
>    for (Loop = 0; Loop < *NumBytes; Loop++) {
> -    *Ptr = WRITE_BYTE_CMD;
> -    *Ptr = Buffer[Loop];
> +    QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);
> +    QemuFlashPtrWrite (Ptr, Buffer[Loop]);
> +
>      Ptr++;
>    }
>  
> @@ -190,7 +207,7 @@ QemuFlashWrite (
>    // Restore flash to read mode
>    //
>    if (*NumBytes > 0) {
> -    *(Ptr - 1) = READ_ARRAY_CMD;
> +    QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);
>    }
>  
>    return EFI_SUCCESS;
> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
> index 5aabe9d7b59c..565383ee26d2 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
> @@ -10,6 +10,9 @@
>  **/
>  
>  #include <Library/UefiRuntimeLib.h>
> +#include <Library/MemEncryptSevLib.h>
> +#include <Library/VmgExitLib.h>
> +#include <Register/Amd/Msr.h>

- new include directive, most likely due to flattening VmgMmioWrite; OK

>  
>  #include "QemuFlash.h"
>  
> @@ -32,3 +35,40 @@ QemuFlashBeforeProbe (
>    // Do nothing
>    //
>  }
> +
> +/**
> +  Write to QEMU Flash
> +
> +  @param[in] Ptr    Pointer to the location to write.
> +  @param[in] Value  The value to write.
> +
> +**/

- new comment, OK

> +VOID
> +QemuFlashPtrWrite (
> +  IN        volatile UINT8    *Ptr,
> +  IN        UINT8             Value
> +  )
> +{
> +  if (MemEncryptSevEsIsEnabled ()) {
> +    MSR_SEV_ES_GHCB_REGISTER  Msr;
> +    GHCB                      *Ghcb;
> +
> +    Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
> +    Ghcb = Msr.Ghcb;
> +
> +    //
> +    // Writing to flash is emulated by the hypervisor through the use of write
> +    // protection. This won't work for an SEV-ES guest because the write won't
> +    // be recognized as a true MMIO write, which would result in the required
> +    // #VC exception. Instead, use the the VMGEXIT MMIO write support directly
> +    // to perform the update.
> +    //
> +    VmgInit (Ghcb);
> +    Ghcb->SharedBuffer[0] = Value;
> +    Ghcb->SaveArea.SwScratch = (UINT64) (UINTN) Ghcb->SharedBuffer;
> +    VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, (UINT64) (UINTN) Ptr, 1);
> +    VmgDone (Ghcb);

- this is what replaces VmgMmioWrite(); seems reasonable

> +  } else {
> +    *Ptr = Value;
> +  }
> +}
> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
> index 7eb426e03855..7eb80bfeffae 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
> @@ -46,3 +46,19 @@ QemuFlashBeforeProbe (
>               );
>    ASSERT_EFI_ERROR (Status);
>  }
> +
> +/**
> +  Write to QEMU Flash
> +
> +  @param[in] Ptr    Pointer to the location to write.
> +  @param[in] Value  The value to write.
> +
> +**/

- new comment.

> +VOID
> +QemuFlashPtrWrite (
> +  IN        volatile UINT8    *Ptr,
> +  IN        UINT8             Value
> +  )
> +{
> +  *Ptr = Value;
> +}
> 

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo


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

* Re: [edk2-devel] [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files
  2020-05-20 16:56 ` [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files Lendacky, Thomas
  2020-05-19 21:54   ` Brijesh Singh
@ 2020-05-26 14:12   ` Laszlo Ersek
  1 sibling, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-26 14:12 UTC (permalink / raw)
  To: devel, thomas.lendacky

On 05/20/20 18:56, Lendacky, Thomas wrote:
> Register reviewers for the SEV-related files in OvmfPkg.
> 
> Cc: Andrew Fish <afish@apple.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Leif Lindholm <leif@nuviainc.com>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Cc: Brijesh Singh <brijesh.singh@amd.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  Maintainers.txt | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/Maintainers.txt b/Maintainers.txt
> index 896ac5821fc6..76f336b7dcc4 100644
> --- a/Maintainers.txt
> +++ b/Maintainers.txt
> @@ -441,6 +441,16 @@ F: OvmfPkg/PvScsiDxe/
>  R: Liran Alon <liran.alon@oracle.com>
>  R: Nikita Leshenko <nikita.leshchenko@oracle.com>
>  
> +OvmfPkg: SEV-related modules
> +F: OvmfPkg/AmdSevDxe/
> +F: OvmfPkg/Include/Library/MemEncryptSevLib.h
> +F: OvmfPkg/IoMmuDxe/AmdSevIoMmu.*
> +F: OvmfPkg/Library/BaseMemEncryptSevLib/
> +F: OvmfPkg/Library/VmgExitLib

(1) Please append a slash character here -- it has a particular meaning:

  F: Files and directories with wildcard patterns.
     A trailing slash includes all files and subdirectory files.

With that:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

... I think I've covered everything I needed to review in this
iteration. I'm happy to continue the discussion of course, just stating
that I've finished looking for v8 patches to review.

Thanks!
Laszlo

> +F: OvmfPkg/PlatformPei/AmdSev.c
> +R: Tom Lendacky <thomas.lendacky@amd.com>
> +R: Brijesh Singh <brijesh.singh@amd.com>
> +
>  PcAtChipsetPkg
>  F: PcAtChipsetPkg/
>  W: https://github.com/tianocore/tianocore.github.io/wiki/PcAtChipsetPkg
> 


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

* Re: [edk2-devel] [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage
  2020-05-25 16:00   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-26 14:28     ` Lendacky, Thomas
  2020-05-26 21:47       ` Lendacky, Thomas
  0 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-26 14:28 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 5/25/20 11:00 AM, Laszlo Ersek wrote:
> On 05/19/20 23:51, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C498df3e8d335449e596508d800c4c955%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260192476035384&amp;sdata=UKux5gXwpNe59RKQHTyk577b%2B%2FBmTIdblij8JWhXBG4%3D&amp;reserved=0
>>
>> Reserve a fixed area of memory for SEV-ES use and set a fixed PCD,
>> PcdSevEsWorkAreaBase, to this value.
>>
>> This area will be used by SEV-ES support for two purposes:
>>    1. Communicating the SEV-ES status during BSP boot to SEC:
>>       Using a byte of memory from the page, the BSP reset vector code can
>>       communicate the SEV-ES status to SEC for use before exception
>>       handling can be enabled in SEC. After SEC, this field is no longer
>>       valid and the standard way of determine if SEV-ES is active should
>>       be used.
>>
>>    2. Establishing an area of memory for AP boot support:
>>       A hypervisor is not allowed to update an SEV-ES guest's register
>>       state, so when booting an SEV-ES guest AP, the hypervisor is not
>>       allowed to set the RIP to the guest requested value. Instead an
>>       SEV-ES AP must be re-directed from within the guest to the actual
>>       requested staring location as specified in the INIT-SIPI-SIPI
>>       sequence.
>>
>>       Use this memory for reset vector code that can be programmed to have
>>       the AP jump to the desired RIP location after starting the AP. This
>>       is required for only the very first AP reset.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   OvmfPkg/OvmfPkgX64.fdf                    |  3 +++
>>   OvmfPkg/ResetVector/ResetVector.inf       |  1 +
>>   OvmfPkg/ResetVector/Ia32/PageTables64.asm | 11 +++++++++++
>>   OvmfPkg/ResetVector/ResetVector.nasmb     |  1 +
>>   4 files changed, 16 insertions(+)
>>
>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>> index 88b1e880e603..8836b30a0cef 100644
>> --- a/OvmfPkg/OvmfPkgX64.fdf
>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>> @@ -82,6 +82,9 @@ [FD.MEMFD]
>>   0x009000|0x002000
>>   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>   
>> +0x00B000|0x001000
>> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>> +
>>   0x010000|0x010000
>>   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>   
>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
>> index 483fd90fe785..e94e1bfcce7e 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>> @@ -34,6 +34,7 @@ [BuildOptions]
>>      *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>>   
>>   [Pcd]
>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>> index c3587a1b7814..73a4eaadb1b6 100644
>> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>> @@ -89,6 +89,10 @@ SevExit:
>>   ; If SEV-ES is disabled then EAX will be zero.
>>   ;
>>   CheckSevEsFeature:
>> +    ; Initialize the first byte of the workarea to zero to communicate to
>> +    ; the SEC phase that SEV-ES is not enabled.
>> +    mov     byte[SEV_ES_WORK_AREA], 0
>> +
>>       xor       eax, eax
>>   
>>       ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
>> @@ -108,6 +112,13 @@ CheckSevEsFeature:
>>       ; Restore encryption mask
>>       mov       edx, ebx
>>   
>> +    test      eax, eax
>> +    jz        NoSevEs
>> +
>> +    ; Set the first byte of the workarea to one to communicate to the SEC
>> +    ; phase that SEV-ES is enabled.
>> +    mov       byte[SEV_ES_WORK_AREA], 1
>> +
>>   NoSevEs:
>>       OneTimeCallRet CheckSevEsFeature
>>   
>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
>> index bfb77e439105..2967617bfaa0 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>> @@ -72,6 +72,7 @@
>>     %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
>>     %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
>>     %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
>> +  %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>>   %include "Ia32/PageTables64.asm"
>>   %endif
>>   
>>
> 
> The OvmfPkg/ResetVector modifications have been moved to this patch, at
> least in part, from patch "OvmfPkg/ResetVector: Add support for a 32-bit
> SEV check".
> 
> And I don't understand why.

I was trying to keep everything logically grouped. The early use of this 
area is to communicate the SEV-ES status to SEC and so logically I thought 
that should be done when the area was introduced.

> 
> I mean it's possible that setting the first byte of the work area to 1
> does not belong in "OvmfPkg/ResetVector: Add support for a 32-bit SEV
> check". That's OK; then said manipulation of the work area should be
> split to its own patch, which I should then review afresh.
> 
> What's not OK is to move code between two reviewed patches *and* keep my
> R-b on both.

Sorry about that. A bad assumption on my part about being able to do that 
here and in a few other places.

> 
> Please be more transparent about incremental changes.
> 
> (1) Please revert this patch to its v7 state, and keep my R-b on it.

Will do.

> 
> (2) Please split the ResetVector changes to a new patch. For the subject
> line, I suggest:
> 
> OvmfPkg/ResetVector: communicate SEV-ES status to SEC before exceptions
> 
> or something similar.

Will do.

Thanks,
Tom

> 
> Thanks
> Laszlo
> 

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

* Re: [edk2-devel] [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
  2020-05-25 14:47   ` Laszlo Ersek
@ 2020-05-26 15:06     ` Lendacky, Thomas
  2020-05-27 11:54       ` Laszlo Ersek
  0 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-26 15:06 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 5/25/20 9:47 AM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C8d75a8b2107f4def062c08d800ba8795%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260148432921212&amp;sdata=WNj6rvvOB%2FeVbeozpvRTXmrqFZEQuEjzEOGIU9KvJVs%3D&amp;reserved=0
>>
>> Under SEV-ES, a DR7 read or write intercept generates a #VC exception.
>> The #VC handler must provide special support to the guest for this. On
>> a DR7 write, the #VC handler must cache the value and issue a VMGEXIT
>> to notify the hypervisor of the write. However, the #VC handler must
>> not actually set the value of the DR7 register. On a DR7 read, the #VC
>> handler must return the cached value of the DR7 register to the guest.
>> VMGEXIT is not invoked for a DR7 register read.
>>
>> To avoid exception recursion, a #VC exception will not try to read and
>> push the actual debug registers into the EFI_SYSTEM_CONTEXT_X64 struct
>> and instead push zeroes. The #VC exception handler does not make use of
>> the debug registers from saved context.
> 
> AFAICS the following patches introcuce / reiterate the per-CPU page concept:
> 
> - "MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page
> tables" (v8 05/46)
> - "OvmfPkg: Create a GHCB page for use during Sec phase" (v8 29/46)
> - "OvmfPkg: Create GHCB pages for use during Pei and Dxe phase" (v8 31/46)
> 
> I find it somewhat difficult to locate those patches and to learn about
> the per-cpu pages from them. The first patch listed above belongs to a
> different package. And the two other patches listed above do not precede
> (but follow) the present patch.
> 
> (1) Therefore please include a paragraph about the per-cpu pages in the
> commit message of this patch.

Will do.

> 
>>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Ray Ni <ray.ni@intel.com>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 105 ++++++++++++++++++
>>   .../X64/ExceptionHandlerAsm.nasm              |  17 +++
>>   .../X64/Xcode5ExceptionHandlerAsm.nasm        |  17 +++
>>   3 files changed, 139 insertions(+)
> 
> Please pass "--stat=1000 --stat-graph-width=20" to git-format-patch;
> that way, the pathnames will not be truncated, and the graph to the
> right will still not be wider than 20 chars.
> 
> Why I'm requesting this (and unfortunately there is no way to make the
> second switch above permanent, in the git config): because I almost
> missed that this patch modifies both UefiCpuPkg and OvmfPkg. It would
> have been obvious from the diffstat (if the pathnames had not been
> truncated).
> 
> (2) Please split the UefiCpuPkg hunks to a separate patch, if possible.
> 
> (Or maybe consider squashing those hunks into patch
> "UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception"
> (v8 11/46), if the UefiCpuPkg owners prefer that.)

It would probably fit nicely into the existing patch. I'll look and either 
move it to there or create a new patch.

> 
>>
>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> index b028b20f255a..e4072d79d704 100644
>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>> @@ -14,6 +14,16 @@
>>   
>>   #define CR4_OSXSAVE (1 << 18)
>>   
>> +#define DR7_RESET_VALUE 0x400
> 
> (3) From the Intel SDM, this looks like a standard value. I'd say if we
> deem it important enough for turning into a macro, then it belongs
> elsewhere (in some more visible header file).
> 
> Otherwise (given that we only use it once, below), I think we could
> simply open-code it at the location of use, with a comment.

I'll do the latter.

> 
>> +
>> +//
>> +// Per-CPU data mapping structure
>> +//
>> +typedef struct {
>> +  BOOLEAN  Dr7Cached;
>> +  UINT64   Dr7;
>> +} SEV_ES_PER_CPU_DATA;
>> +
>>   //
>>   // Instruction execution mode definition
>>   //
>> @@ -1494,6 +1504,93 @@ RdtscExit (
>>     return 0;
>>   }
>>   
>> +/**
>> +  Handle a DR7 register write event.
>> +
>> +  Use the VMGEXIT instruction to handle a DR7 write event.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +  @retval Others                   New exception value to propagate
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +Dr7WriteExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
>> +  SEV_ES_PER_CPU_DATA            *SevEsData;
>> +  INTN                           *Register;
> 
> (4) This should be UINT64, per my earlier request.
> 
>> +  UINT64                         Status;
>> +
>> +  Ext = &InstructionData->Ext;
>> +  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
>> +
>> +  DecodeModRm (Regs, InstructionData);
>> +
>> +  /* MOV DRn always treats MOD == 3 no matter how encoded */
> 
> (5) comment style
> 
>> +  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
>> +
>> +  /* Using a value of 0 for ExitInfo1 means RAX holds the value */
> 
> (6) comment style
> 
>> +  Ghcb->SaveArea.Rax = *Register;
>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>> +
>> +  Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
>> +  if (Status) {
> 
> (7) please compare with 0 explicitly

4 - 7 will be taken care of.

> 
>> +    return Status;
>> +  }
>> +
>> +  SevEsData->Dr7 = *Register;
>> +  SevEsData->Dr7Cached = TRUE;
> 
> Hmmm... I'm wondering where this BOOLEAN gets re-set to FALSE on a
> platform reset.
> 
> In patch "OvmfPkg: Create GHCB pages for use during Pei and Dxe phase",
> in function AmdSevEsInitialize(), we have a ZeroMem(). That should cover
> it for PEI and DXE; OK.
> 
> (8) In patch "OvmfPkg: Create a GHCB page for use during Sec phase"
> however, we don't seem to zero out the per-cpu page itself (which
> resides just after PcdOvmfSecGhcbBase).
> 
> Do we do that elsewhere? (Sorry if I'm just not seeing it.)
> 
> I'm asking because, after a platform reset, SevEsData->Dr7Cached may
> read as TRUE in SEC at the very first access (it lives at a fixed
> location, and QEMU platform reset does not clear RAM). And so we could
> return the value cached from the previous boot rather than 0x400.

An SEV-ES guest can't be rebooted/reset without restarting Qemu because 
the guest register can't be changed by the hypervisor. So a full reboot 
isn't initially supported SEV-ES.

But, yes, this should still clear it to be safe for any future support. 
I'll find an appropriate place to zero it out.

> 
> 
>> +
>> +  return 0;
>> +}
>> +
>> +/**
>> +  Handle a DR7 register read event.
>> +
>> +  Use the VMGEXIT instruction to handle a DR7 read event.
>> +
>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
>> +                                   Block
>> +  @param[in, out] Regs             x64 processor context
>> +  @param[in]      InstructionData  Instruction parsing context
>> +
>> +  @retval 0                        Event handled successfully
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +Dr7ReadExit (
>> +  IN OUT GHCB                     *Ghcb,
>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>> +  )
>> +{
>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
>> +  SEV_ES_PER_CPU_DATA            *SevEsData;
>> +  INTN                           *Register;
> 
> (9) Should be UINT64.
> 
>> +
>> +  Ext = &InstructionData->Ext;
>> +  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
>> +
>> +  DecodeModRm (Regs, InstructionData);
>> +
>> +  /* MOV DRn always treats MOD == 3 no matter how encoded */
> 
> (10) Please fix the comment style.
> 
>> +  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
>> +  *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : DR7_RESET_VALUE;
>> +
>> +  return 0;
>> +}
>> +
>>   /**
>>     Handle a #VC exception.
>>   
>> @@ -1538,6 +1635,14 @@ VmgExitHandleVc (
>>   
>>     ExitCode = Regs->ExceptionData;
>>     switch (ExitCode) {
>> +  case SVM_EXIT_DR7_READ:
>> +    NaeExit = Dr7ReadExit;
>> +    break;
>> +
>> +  case SVM_EXIT_DR7_WRITE:
>> +    NaeExit = Dr7WriteExit;
>> +    break;
>> +
>>     case SVM_EXIT_RDTSC:
>>       NaeExit = RdtscExit;
>>       break;
> 
> Stopping here (before the UefiCpuPkg hunks).

9 - 10 to be taken care of.

Thanks,
Tom

> 
> Thanks!
> Laszlo
> 
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
>> index 3814f9de3703..2a5545ecfd41 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
>> @@ -18,6 +18,8 @@
>>   ; CommonExceptionHandler()
>>   ;
>>   
>> +%define VC_EXCEPTION 29
>> +
>>   extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
>>   extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
>>   extern ASM_PFX(CommonExceptionHandler)
>> @@ -224,6 +226,9 @@ HasErrorCode:
>>       push    rax
>>   
>>   ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
>> +    cmp     qword [rbp + 8], VC_EXCEPTION
>> +    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers ignored
>> +
>>       mov     rax, dr7
>>       push    rax
>>       mov     rax, dr6
>> @@ -236,7 +241,19 @@ HasErrorCode:
>>       push    rax
>>       mov     rax, dr0
>>       push    rax
>> +    jmp     DrFinish
>>   
>> +VcDebugRegs:
>> +;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion
>> +    xor     rax, rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +
>> +DrFinish:
>>   ;; FX_SAVE_STATE_X64 FxSaveState;
>>       sub rsp, 512
>>       mov rdi, rsp
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
>> index 19198f273137..26cae56cc5cf 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
>> @@ -18,6 +18,8 @@
>>   ; CommonExceptionHandler()
>>   ;
>>   
>> +%define VC_EXCEPTION 29
>> +
>>   extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
>>   extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
>>   extern ASM_PFX(CommonExceptionHandler)
>> @@ -225,6 +227,9 @@ HasErrorCode:
>>       push    rax
>>   
>>   ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
>> +    cmp     qword [rbp + 8], VC_EXCEPTION
>> +    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers ignored
>> +
>>       mov     rax, dr7
>>       push    rax
>>       mov     rax, dr6
>> @@ -237,7 +242,19 @@ HasErrorCode:
>>       push    rax
>>       mov     rax, dr0
>>       push    rax
>> +    jmp     DrFinish
>>   
>> +VcDebugRegs:
>> +;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion
>> +    xor     rax, rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +    push    rax
>> +
>> +DrFinish:
>>   ;; FX_SAVE_STATE_X64 FxSaveState;
>>       sub rsp, 512
>>       mov rdi, rsp
>>
> 

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

* Re: [edk2-devel] [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase
  2020-05-25 15:07   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-26 15:41     ` Lendacky, Thomas
  2020-05-26 15:45       ` Lendacky, Thomas
  2020-05-27 11:45       ` Laszlo Ersek
  0 siblings, 2 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-26 15:41 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 5/25/20 10:07 AM, Laszlo Ersek wrote:
> On 05/19/20 23:50, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C39b71c622d2d4bbf9e5b08d800bd69a5%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260160817275268&amp;sdata=hz43pd7UO60%2FWfNALLyUuUax8KX%2Bpq4SyU9NIN32Pfc%3D&amp;reserved=0
>>
>> A GHCB page is needed during the Sec phase, so this new page must be
>> created. Since the #VC exception handler routines assume that a per-CPU
>> variable area is immediately after the GHCB, this per-CPU variable area
>> must also be created. Since the GHCB must be marked as an un-encrypted,
>> or shared, page, an additional pagetable page is required to break down
>> the 2MB region where the GHCB page lives into 4K pagetable entries.
>>
>> Create a new entry in the OVMF memory layout for the new page table
>> page and for the SEC GHCB and per-CPU variable pages. After breaking down
>> the 2MB page, update the GHCB page table entry to remove the encryption
>> mask.
>>
>> The GHCB page will be used by the SEC #VC exception handler. The #VC
>> exception handler will fill in the necessary fields of the GHCB and exit
>> to the hypervisor using the VMGEXIT instruction. The hypervisor then
>> accesses the GHCB in order to perform the requested function.
>>
>> Two new fixed PCDs are needed to support the SEC GHCB page:
>>    - PcdOvmfSecGhcbBase  UINT64 value that is the base address of the
>>                          GHCB used during the SEC phase.
>>    - PcdOvmfSecGhcbSize  UINT64 value that is the size, in bytes, of the
>>                          GHCB area used during the SEC phase.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   OvmfPkg/OvmfPkg.dec                       |  9 +++
>>   OvmfPkg/OvmfPkgX64.fdf                    |  6 ++
>>   OvmfPkg/ResetVector/ResetVector.inf       |  5 ++
>>   OvmfPkg/ResetVector/Ia32/PageTables64.asm | 70 +++++++++++++++++++++++
>>   OvmfPkg/ResetVector/ResetVector.nasmb     | 17 ++++++
>>   5 files changed, 107 insertions(+)
>>
>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>> index 65bb2bb0eb4c..02ad62ed9f43 100644
>> --- a/OvmfPkg/OvmfPkg.dec
>> +++ b/OvmfPkg/OvmfPkg.dec
>> @@ -281,6 +281,15 @@ [PcdsFixedAtBuild]
>>     ## Number of page frames to use for storing grant table entries.
>>     gUefiOvmfPkgTokenSpaceGuid.PcdXenGrantFrames|4|UINT32|0x33
>>   
>> +  ## Specify the extra page table needed to mark the GHCB as unencrypted.
>> +  #  The value should be a multiple of 4KB for each.
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|0x0|UINT32|0x3a
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize|0x0|UINT32|0x3b
>> +
>> +  ## The base address of the SEC GHCB page used by SEV-ES.
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|0|UINT32|0x3c
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize|0|UINT32|0x3d
>> +
>>   [PcdsDynamic, PcdsDynamicEx]
>>     gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
> 
> OK, the token values have been updated, due to:
> 
> - commit 7efce2e59c20 ("OvmfPkg/PvScsiDxe: Report the number of targets
> and LUNs", 2020-03-30)
> 
> - commit c4c15b870239 ("OvmfPkg/PvScsiDxe: Support sending SCSI request
> and receive response", 2020-03-30)
> 
> - commit 093cceaf79b5 ("OvmfPkg/MptScsiDxe: Report targets and one LUN",
> 2020-05-05)
> 
> (Independently, when I reviewed what would become 505812ae1d2d
> ("OvmfPkg/MptScsiDxe: Implement the PassThru method", 2020-05-05), I
> missed that 0x39 is followed by 0x3A, not 0x40. Oh well.)
> 
> 
>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>> index bfca1eff9e83..88b1e880e603 100644
>> --- a/OvmfPkg/OvmfPkgX64.fdf
>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>> @@ -76,6 +76,12 @@ [FD.MEMFD]
>>   0x007000|0x001000
>>   gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>>   
>> +0x008000|0x001000
>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>> +
>> +0x009000|0x002000
>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>> +
>>   0x010000|0x010000
>>   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>   
>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
>> index b0ddfa5832a2..483fd90fe785 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>> @@ -26,6 +26,7 @@ [Sources]
>>   [Packages]
>>     OvmfPkg/OvmfPkg.dec
>>     MdePkg/MdePkg.dec
>> +  MdeModulePkg/MdeModulePkg.dec
>>     UefiCpuPkg/UefiCpuPkg.dec
>>   
>>   [BuildOptions]
>> @@ -33,5 +34,9 @@ [BuildOptions]
>>      *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>>   
>>   [Pcd]
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
>> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>> index abad009f20f5..c3587a1b7814 100644
>> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>> @@ -21,6 +21,11 @@ BITS    32
>>   %define PAGE_2M_MBO            0x080
>>   %define PAGE_2M_PAT          0x01000
>>   
>> +%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
>> +                          PAGE_DIRTY + \
>> +                          PAGE_READ_WRITE + \
>> +                          PAGE_PRESENT)
>> +
>>   %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
>>                             PAGE_ACCESSED + \
>>                             PAGE_DIRTY + \
>> @@ -75,6 +80,37 @@ NoSev:
>>   SevExit:
>>       OneTimeCallRet CheckSevFeature
>>   
>> +; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
>> +; is enabled.
>> +;
>> +; Modified:  EAX, EBX, ECX
>> +;
>> +; If SEV-ES is enabled then EAX will be non-zero.
>> +; If SEV-ES is disabled then EAX will be zero.
>> +;
>> +CheckSevEsFeature:
>> +    xor       eax, eax
>> +
>> +    ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
>> +    ; mask.
>> +    test      edx, edx
>> +    jz        NoSevEs
>> +
>> +    ; Save current value of encryption mask
>> +    mov       ebx, edx
>> +
>> +    ; Check if SEV-ES is enabled
>> +    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
>> +    mov       ecx, 0xc0010131
>> +    rdmsr
>> +    and       eax, 2
>> +
>> +    ; Restore encryption mask
>> +    mov       edx, ebx
>> +
>> +NoSevEs:
>> +    OneTimeCallRet CheckSevEsFeature
>> +
>>   ;
>>   ; Modified:  EAX, EBX, ECX, EDX
>>   ;
>> @@ -139,6 +175,40 @@ pageTableEntriesLoop:
>>       mov     [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
>>       loop    pageTableEntriesLoop
>>   
>> +    OneTimeCall   CheckSevEsFeature
>> +    test    eax, eax
>> +    jz      SetCr3
>> +
>> +    ;
>> +    ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
>> +    ; This requires the 2MB page for this range be broken down into 512 4KB
>> +    ; pages.  All will be marked encrypted, except for the GHCB.
>> +    ;
>> +    mov     ecx, (GHCB_BASE >> 21)
>> +    mov     eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
>> +    mov     [ecx * 8 + PT_ADDR (0x2000)], eax
>> +
>> +    ;
>> +    ; Page Table Entries (512 * 4KB entries => 2MB)
>> +    ;
>> +    mov     ecx, 512
>> +pageTableEntries4kLoop:
>> +    mov     eax, ecx
>> +    dec     eax
>> +    shl     eax, 12
>> +    add     eax, GHCB_BASE & 0xFFE0_0000
>> +    add     eax, PAGE_4K_PDE_ATTR
>> +    mov     [ecx * 8 + GHCB_PT_ADDR - 8], eax
>> +    mov     [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
>> +    loop    pageTableEntries4kLoop
>> +
>> +    ;
>> +    ; Clear the encryption bit from the GHCB entry
>> +    ;
>> +    mov     ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
>> +    mov     [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
>> +
> 
> (1) Why did you remove "clearGhcbMemoryLoop" (in the v6->v7 transition)?

I removed it because it actually wasn't clearing the GHCB at all. Since 
this occurred before the new page tables are loaded, the page is accessed 
encrypted. After loading the new page tables, the GHCB is now referenced 
unencrypted and so the "zeroed" page isn't actually zeroes anymore, it is 
cipher-text.

Since the GHCB is always cleared on #VC, I dropped it.

> 
> I think that's exactly the clearing loop (minimally for the CPU#0
> per-CPU page) that I was just looking for in point (8) under
> "OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events" (v8 26/46).
> 
> Hm... the v7 blurb says, "Ensure the per-CPU variable page remains
> encrypted". OK, but that still doesn't explain why we don't clear it
> (just for the guest to see).

I'll add a loop to clear the GHCB page and the per-CPU page after 
establishing the new page tables.

> 
> Also, if the patch was non-trivially modified in v7, then arguably my
> R-b (given originally under "RFC PATCH v3 26/43") should have been removed.
> 
> Please re-instate "clearGhcbMemoryLoop" (and then keep the R-b).

I'll actually drop your Reviewed-by: since I'll need to expand and move 
the loop to clear the memory area from the original location in order for 
the clearing of the pages to be correct.

Thanks,
Tom

> 
> Thanks,
> Laszlo
> 
>> +SetCr3:
>>       ;
>>       ; Set CR3 now that the paging structures are available
>>       ;
>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
>> index 75cfe16654b1..bfb77e439105 100644
>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>> @@ -53,8 +53,25 @@
>>       %error "This implementation inherently depends on PcdOvmfSecPageTablesSize"
>>     %endif
>>   
>> +  %if (FixedPcdGet32 (PcdOvmfSecGhcbPageTableSize) != 0x1000)
>> +    %error "This implementation inherently depends on PcdOvmfSecGhcbPageTableSize"
>> +  %endif
>> +
>> +  %if (FixedPcdGet32 (PcdOvmfSecGhcbSize) != 0x2000)
>> +    %error "This implementation inherently depends on PcdOvmfSecGhcbSize"
>> +  %endif
>> +
>> +  %if ((FixedPcdGet32 (PcdOvmfSecGhcbBase) >> 21) != \
>> +       ((FixedPcdGet32 (PcdOvmfSecGhcbBase) + FixedPcdGet32 (PcdOvmfSecGhcbSize) - 1) >> 21))
>> +    %error "This implementation inherently depends on PcdOvmfSecGhcbBase not straddling a 2MB boundary"
>> +  %endif
>> +
>>     %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
>>   %include "Ia32/Flat32ToFlat64.asm"
>> +
>> +  %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
>> +  %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
>> +  %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
>>   %include "Ia32/PageTables64.asm"
>>   %endif
>>   
>>
> 

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

* Re: [edk2-devel] [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase
  2020-05-26 15:41     ` Lendacky, Thomas
@ 2020-05-26 15:45       ` Lendacky, Thomas
  2020-05-27 11:45       ` Laszlo Ersek
  1 sibling, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-26 15:45 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 5/26/20 10:41 AM, Tom Lendacky wrote:
> On 5/25/20 10:07 AM, Laszlo Ersek wrote:
>> On 05/19/20 23:50, Lendacky, Thomas wrote:
>>> BZ: 
>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C39b71c622d2d4bbf9e5b08d800bd69a5%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260160817275268&amp;sdata=hz43pd7UO60%2FWfNALLyUuUax8KX%2Bpq4SyU9NIN32Pfc%3D&amp;reserved=0 
>>>
>>>
>>> A GHCB page is needed during the Sec phase, so this new page must be
>>> created. Since the #VC exception handler routines assume that a per-CPU
>>> variable area is immediately after the GHCB, this per-CPU variable area
>>> must also be created. Since the GHCB must be marked as an un-encrypted,
>>> or shared, page, an additional pagetable page is required to break down
>>> the 2MB region where the GHCB page lives into 4K pagetable entries.
>>>
>>> Create a new entry in the OVMF memory layout for the new page table
>>> page and for the SEC GHCB and per-CPU variable pages. After breaking down
>>> the 2MB page, update the GHCB page table entry to remove the encryption
>>> mask.
>>>
>>> The GHCB page will be used by the SEC #VC exception handler. The #VC
>>> exception handler will fill in the necessary fields of the GHCB and exit
>>> to the hypervisor using the VMGEXIT instruction. The hypervisor then
>>> accesses the GHCB in order to perform the requested function.
>>>
>>> Two new fixed PCDs are needed to support the SEC GHCB page:
>>>    - PcdOvmfSecGhcbBase  UINT64 value that is the base address of the
>>>                          GHCB used during the SEC phase.
>>>    - PcdOvmfSecGhcbSize  UINT64 value that is the size, in bytes, of the
>>>                          GHCB area used during the SEC phase.
>>>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>>> ---
>>>   OvmfPkg/OvmfPkg.dec                       |  9 +++
>>>   OvmfPkg/OvmfPkgX64.fdf                    |  6 ++
>>>   OvmfPkg/ResetVector/ResetVector.inf       |  5 ++
>>>   OvmfPkg/ResetVector/Ia32/PageTables64.asm | 70 +++++++++++++++++++++++
>>>   OvmfPkg/ResetVector/ResetVector.nasmb     | 17 ++++++
>>>   5 files changed, 107 insertions(+)
>>>
>>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>>> index 65bb2bb0eb4c..02ad62ed9f43 100644
>>> --- a/OvmfPkg/OvmfPkg.dec
>>> +++ b/OvmfPkg/OvmfPkg.dec
>>> @@ -281,6 +281,15 @@ [PcdsFixedAtBuild]
>>>     ## Number of page frames to use for storing grant table entries.
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdXenGrantFrames|4|UINT32|0x33
>>> +  ## Specify the extra page table needed to mark the GHCB as unencrypted.
>>> +  #  The value should be a multiple of 4KB for each.
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|0x0|UINT32|0x3a
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize|0x0|UINT32|0x3b
>>> +
>>> +  ## The base address of the SEC GHCB page used by SEV-ES.
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|0|UINT32|0x3c
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize|0|UINT32|0x3d
>>> +
>>>   [PcdsDynamic, PcdsDynamicEx]
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>>>     
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
>>
>> OK, the token values have been updated, due to:
>>
>> - commit 7efce2e59c20 ("OvmfPkg/PvScsiDxe: Report the number of targets
>> and LUNs", 2020-03-30)
>>
>> - commit c4c15b870239 ("OvmfPkg/PvScsiDxe: Support sending SCSI request
>> and receive response", 2020-03-30)
>>
>> - commit 093cceaf79b5 ("OvmfPkg/MptScsiDxe: Report targets and one LUN",
>> 2020-05-05)
>>
>> (Independently, when I reviewed what would become 505812ae1d2d
>> ("OvmfPkg/MptScsiDxe: Implement the PassThru method", 2020-05-05), I
>> missed that 0x39 is followed by 0x3A, not 0x40. Oh well.)
>>
>>
>>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>>> index bfca1eff9e83..88b1e880e603 100644
>>> --- a/OvmfPkg/OvmfPkgX64.fdf
>>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>>> @@ -76,6 +76,12 @@ [FD.MEMFD]
>>>   0x007000|0x001000
>>>   
>>> gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize 
>>>
>>> +0x008000|0x001000
>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize 
>>>
>>> +
>>> +0x009000|0x002000
>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize 
>>>
>>> +
>>>   0x010000|0x010000
>>>   
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize 
>>>
>>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf 
>>> b/OvmfPkg/ResetVector/ResetVector.inf
>>> index b0ddfa5832a2..483fd90fe785 100644
>>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>>> @@ -26,6 +26,7 @@ [Sources]
>>>   [Packages]
>>>     OvmfPkg/OvmfPkg.dec
>>>     MdePkg/MdePkg.dec
>>> +  MdeModulePkg/MdeModulePkg.dec
>>>     UefiCpuPkg/UefiCpuPkg.dec
>>>   [BuildOptions]
>>> @@ -33,5 +34,9 @@ [BuildOptions]
>>>      *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>>>   [Pcd]
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
>>> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm 
>>> b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> index abad009f20f5..c3587a1b7814 100644
>>> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> @@ -21,6 +21,11 @@ BITS    32
>>>   %define PAGE_2M_MBO            0x080
>>>   %define PAGE_2M_PAT          0x01000
>>> +%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
>>> +                          PAGE_DIRTY + \
>>> +                          PAGE_READ_WRITE + \
>>> +                          PAGE_PRESENT)
>>> +
>>>   %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
>>>                             PAGE_ACCESSED + \
>>>                             PAGE_DIRTY + \
>>> @@ -75,6 +80,37 @@ NoSev:
>>>   SevExit:
>>>       OneTimeCallRet CheckSevFeature
>>> +; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) 
>>> feature
>>> +; is enabled.
>>> +;
>>> +; Modified:  EAX, EBX, ECX
>>> +;
>>> +; If SEV-ES is enabled then EAX will be non-zero.
>>> +; If SEV-ES is disabled then EAX will be zero.
>>> +;
>>> +CheckSevEsFeature:
>>> +    xor       eax, eax
>>> +
>>> +    ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
>>> +    ; mask.
>>> +    test      edx, edx
>>> +    jz        NoSevEs
>>> +
>>> +    ; Save current value of encryption mask
>>> +    mov       ebx, edx
>>> +
>>> +    ; Check if SEV-ES is enabled
>>> +    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
>>> +    mov       ecx, 0xc0010131
>>> +    rdmsr
>>> +    and       eax, 2
>>> +
>>> +    ; Restore encryption mask
>>> +    mov       edx, ebx
>>> +
>>> +NoSevEs:
>>> +    OneTimeCallRet CheckSevEsFeature
>>> +
>>>   ;
>>>   ; Modified:  EAX, EBX, ECX, EDX
>>>   ;
>>> @@ -139,6 +175,40 @@ pageTableEntriesLoop:
>>>       mov     [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
>>>       loop    pageTableEntriesLoop
>>> +    OneTimeCall   CheckSevEsFeature
>>> +    test    eax, eax
>>> +    jz      SetCr3
>>> +
>>> +    ;
>>> +    ; The initial GHCB will live at GHCB_BASE and needs to be 
>>> un-encrypted.
>>> +    ; This requires the 2MB page for this range be broken down into 
>>> 512 4KB
>>> +    ; pages.  All will be marked encrypted, except for the GHCB.
>>> +    ;
>>> +    mov     ecx, (GHCB_BASE >> 21)
>>> +    mov     eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
>>> +    mov     [ecx * 8 + PT_ADDR (0x2000)], eax
>>> +
>>> +    ;
>>> +    ; Page Table Entries (512 * 4KB entries => 2MB)
>>> +    ;
>>> +    mov     ecx, 512
>>> +pageTableEntries4kLoop:
>>> +    mov     eax, ecx
>>> +    dec     eax
>>> +    shl     eax, 12
>>> +    add     eax, GHCB_BASE & 0xFFE0_0000
>>> +    add     eax, PAGE_4K_PDE_ATTR
>>> +    mov     [ecx * 8 + GHCB_PT_ADDR - 8], eax
>>> +    mov     [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
>>> +    loop    pageTableEntries4kLoop
>>> +
>>> +    ;
>>> +    ; Clear the encryption bit from the GHCB entry
>>> +    ;
>>> +    mov     ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
>>> +    mov     [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
>>> +
>>
>> (1) Why did you remove "clearGhcbMemoryLoop" (in the v6->v7 transition)?
> 
> I removed it because it actually wasn't clearing the GHCB at all. Since 
> this occurred before the new page tables are loaded, the page is accessed 
> encrypted. After loading the new page tables, the GHCB is now referenced 
> unencrypted and so the "zeroed" page isn't actually zeroes anymore, it is 
> cipher-text.
> 
> Since the GHCB is always cleared on #VC, I dropped it.
> 
>>
>> I think that's exactly the clearing loop (minimally for the CPU#0
>> per-CPU page) that I was just looking for in point (8) under
>> "OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events" (v8 26/46).
>>
>> Hm... the v7 blurb says, "Ensure the per-CPU variable page remains
>> encrypted". OK, but that still doesn't explain why we don't clear it
>> (just for the guest to see).
> 
> I'll add a loop to clear the GHCB page and the per-CPU page after 
> establishing the new page tables.
> 
>>
>> Also, if the patch was non-trivially modified in v7, then arguably my
>> R-b (given originally under "RFC PATCH v3 26/43") should have been removed.
>>
>> Please re-instate "clearGhcbMemoryLoop" (and then keep the R-b).
> 
> I'll actually drop your Reviewed-by: since I'll need to expand and move 
> the loop to clear the memory area from the original location in order for 
> the clearing of the pages to be correct.

It's not expanding (not sure why I typed that), just moving to after the 
setting of CR3.

Thanks,
Tom

> 
> Thanks,
> Tom
> 
>>
>> Thanks,
>> Laszlo
>>
>>> +SetCr3:
>>>       ;
>>>       ; Set CR3 now that the paging structures are available
>>>       ;
>>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb 
>>> b/OvmfPkg/ResetVector/ResetVector.nasmb
>>> index 75cfe16654b1..bfb77e439105 100644
>>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>>> @@ -53,8 +53,25 @@
>>>       %error "This implementation inherently depends on 
>>> PcdOvmfSecPageTablesSize"
>>>     %endif
>>> +  %if (FixedPcdGet32 (PcdOvmfSecGhcbPageTableSize) != 0x1000)
>>> +    %error "This implementation inherently depends on 
>>> PcdOvmfSecGhcbPageTableSize"
>>> +  %endif
>>> +
>>> +  %if (FixedPcdGet32 (PcdOvmfSecGhcbSize) != 0x2000)
>>> +    %error "This implementation inherently depends on PcdOvmfSecGhcbSize"
>>> +  %endif
>>> +
>>> +  %if ((FixedPcdGet32 (PcdOvmfSecGhcbBase) >> 21) != \
>>> +       ((FixedPcdGet32 (PcdOvmfSecGhcbBase) + FixedPcdGet32 
>>> (PcdOvmfSecGhcbSize) - 1) >> 21))
>>> +    %error "This implementation inherently depends on 
>>> PcdOvmfSecGhcbBase not straddling a 2MB boundary"
>>> +  %endif
>>> +
>>>     %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + 
>>> (Offset))
>>>   %include "Ia32/Flat32ToFlat64.asm"
>>> +
>>> +  %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
>>> +  %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
>>> +  %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
>>>   %include "Ia32/PageTables64.asm"
>>>   %endif
>>>
>>

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

* Re: [edk2-devel] [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check
  2020-05-25 16:50   ` [edk2-devel] " Laszlo Ersek
@ 2020-05-26 16:31     ` Lendacky, Thomas
  2020-05-27 11:59       ` Laszlo Ersek
  0 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-26 16:31 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 5/25/20 11:50 AM, Laszlo Ersek wrote:
> Tom,
> 
> On 05/19/20 23:51, Lendacky, Thomas wrote:
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C4fe7191ef9fe43793eb408d800cbbb44%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260222303712873&amp;sdata=DJP9%2Fbe1ttZ%2FGEwqZ2Flv5z4dTV0T8QkYdeSnaIGWcY%3D&amp;reserved=0
>>
>> During BSP startup, the reset vector code will issue a CPUID instruction
>> while in 32-bit mode. When running as an SEV-ES guest, this will trigger
>> a #VC exception.
>>
>> Add exception handling support to the early reset vector code to catch
>> these exceptions.  Also, since the guest is in 32-bit mode at this point,
>> writes to the GHCB will be encrypted and thus not able to be read by the
>> hypervisor, so use the GHCB CPUID request/response protocol to obtain the
>> requested CPUID function values and provide these to the guest.
>>
>> The exception handling support is active during the SEV check and uses the
>> OVMF temporary RAM space for a stack. After the SEV check is complete, the
>> exception handling support is removed and the stack pointer cleared.
>>
>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>   OvmfPkg/ResetVector/ResetVector.inf       |   2 +
>>   OvmfPkg/ResetVector/Ia32/PageTables64.asm | 329 +++++++++++++++++++---
>>   OvmfPkg/ResetVector/ResetVector.nasmb     |   1 +
>>   3 files changed, 294 insertions(+), 38 deletions(-)
> 
> this doesn't work for me.
> 
> Under your v5 posting, I reviewed those OvmfPkg patches that still
> needed my review.
> 
> The v6 posting carried all my R-b's; all OvmfPkg patches had been
> reviewed. I trusted you and I only verified the commit messages for my
> R-b's. I thought the OvmfPkg state was final.
> 
> The v7 posting again carried my R-b's; I briefly checked the v6->v7
> changes in the blurb, and re-checked my R-b's on the OvmfPkg patches.
> This was in the v7 blurb:
> 
>> Changes since v6:
>> - Add function comments to all functions, including local functions
>> - Add function parameter direction to all functions (in/out)
>> - Add support for MMIO MOVZX/MOVSX instructions
>> - Ensure the per-CPU variable page remains encrypted
>> - Coding-style fixes as identified by Ecc
> 
> This summary didn't indicate I'd have to go through the OvmfPkg patches
> again -- and the presence of my R-b's on all the OvmfPkg patches
> supported that impression.
> 
> I commented on v7 only later, independently; namely on two topics:
> 
> - on one of the S3 reservation aspects,
> - on the upcoming / requested movement of VmgExitLib to OvmfPkg.
> 
> These were the two updates I was going to expect in v8.
> 
> So, in order to "page in" your work again, in preparation for reviewing
> v8, I decided to review the v5->v6 changes in more detail -- the code
> too (incrementally), not just the picking up of my R-b's, like I had
> originally done under v6. I was happy with v6, after performing this
> review; see <https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198%23c10&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C4fe7191ef9fe43793eb408d800cbbb44%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260222303712873&amp;sdata=bK4imTQl9UvEJ9Y96QR7CuBkLSwi14MqSacXwh7JzhY%3D&amp;reserved=0>.
> 
> Now I'm reviewing the differences (incrementally from v6 to v8), and I'm
> shocked how many changes you incorporated into preexistent patches,
> while keeping my R-b's.

My apologies for this. I was experimenting with cleaning things up and 
making the code more readable and I guess I forgot to either remove it or 
note it as a change to be re-reviewed - thinking I had done one or the other.

> 
> On this patch, you significantly changed the logic from v6 to v7, and I
> don't have the slightest clue why. I don't feel inclined to
> reverse-engineer the logic change from the v6->v7 interdiff. The right
> way to present a significant change is to (a) drop the existent R-b's
> from the patch, and (b) spell out the news in the blurb and/or in the
> "notes" section of the individual patch. If you had dropped the R-b in
> v7, then I would have known to review the changes in v7 at once (rather
> than let it accumulate to v8). And if you had explained the updates, I
> may have started with a re-review of the patch from scratch (and
> wouldn't be stuck with an incremental one / interdiff now, between v6
> and v8).
> 
> Then, the patch changed *again*, from v7 to v8; and my R-b (which only
> applied to v6) got carried forward again.
> 
> Consider the v7->v8 changes noted in the blurb:
> 
>> Changes since v7:
>> - Reserve the SEV-ES workarea when S3 is enabled
>> - Fix warnings issued by the Visual Studio compiler
>> - Create a NULL VmgExitLib instance that is used for VMGEXIT
>>    related operations as well as #VC handling. Then create the full
>>    VmgExitLib support only in OvmfPkg - where it will be used. This
>>    removes a bunch of implementation code from platforms that will
>>    not be using the functionality.
>> - Remove single use interfaces from the VmgExitLib (VmgMmioWrite
>>    and VmgSetApJumpTable)
> 
> Not a word on this patch, as far as I can see.
> 
> I don't even know what to do about this patch now. I'd be really unhappy
> to review it from zero; it's a difficult one. The reset vector is also
> shared with non-SEV X64, so it's not like I can just slap an Acked-by on
> it.
> 
> (1) Unless there was an actual bug in the v6 version of this patch,
> please let's go back to that. IOW, if the v6->v8 changes are only
> cleanups or optimizations, let's please postpone them.

Yes, I'll revert it back to the original version. Again, sorry for all of 
this churn.

Thanks,
Tom

> 
> I'm going to take a walk now.
> 
> Laszlo
> 

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

* Re: [edk2-devel] [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage
  2020-05-26 14:28     ` Lendacky, Thomas
@ 2020-05-26 21:47       ` Lendacky, Thomas
  2020-05-27 11:50         ` Laszlo Ersek
  0 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-05-26 21:47 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 5/26/20 9:28 AM, Tom Lendacky wrote:
> On 5/25/20 11:00 AM, Laszlo Ersek wrote:
>> On 05/19/20 23:51, Lendacky, Thomas wrote:
>>> BZ: 
>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C498df3e8d335449e596508d800c4c955%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260192476035384&amp;sdata=UKux5gXwpNe59RKQHTyk577b%2B%2FBmTIdblij8JWhXBG4%3D&amp;reserved=0 
>>>
>>>
>>> Reserve a fixed area of memory for SEV-ES use and set a fixed PCD,
>>> PcdSevEsWorkAreaBase, to this value.
>>>
>>> This area will be used by SEV-ES support for two purposes:
>>>    1. Communicating the SEV-ES status during BSP boot to SEC:
>>>       Using a byte of memory from the page, the BSP reset vector code can
>>>       communicate the SEV-ES status to SEC for use before exception
>>>       handling can be enabled in SEC. After SEC, this field is no longer
>>>       valid and the standard way of determine if SEV-ES is active should
>>>       be used.
>>>
>>>    2. Establishing an area of memory for AP boot support:
>>>       A hypervisor is not allowed to update an SEV-ES guest's register
>>>       state, so when booting an SEV-ES guest AP, the hypervisor is not
>>>       allowed to set the RIP to the guest requested value. Instead an
>>>       SEV-ES AP must be re-directed from within the guest to the actual
>>>       requested staring location as specified in the INIT-SIPI-SIPI
>>>       sequence.
>>>
>>>       Use this memory for reset vector code that can be programmed to have
>>>       the AP jump to the desired RIP location after starting the AP. This
>>>       is required for only the very first AP reset.
>>>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>>> ---
>>>   OvmfPkg/OvmfPkgX64.fdf                    |  3 +++
>>>   OvmfPkg/ResetVector/ResetVector.inf       |  1 +
>>>   OvmfPkg/ResetVector/Ia32/PageTables64.asm | 11 +++++++++++
>>>   OvmfPkg/ResetVector/ResetVector.nasmb     |  1 +
>>>   4 files changed, 16 insertions(+)
>>>
>>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>>> index 88b1e880e603..8836b30a0cef 100644
>>> --- a/OvmfPkg/OvmfPkgX64.fdf
>>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>>> @@ -82,6 +82,9 @@ [FD.MEMFD]
>>>   0x009000|0x002000
>>>   
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize 
>>>
>>> +0x00B000|0x001000
>>> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize 
>>>
>>> +
>>>   0x010000|0x010000
>>>   
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize 
>>>
>>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf 
>>> b/OvmfPkg/ResetVector/ResetVector.inf
>>> index 483fd90fe785..e94e1bfcce7e 100644
>>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>>> @@ -34,6 +34,7 @@ [BuildOptions]
>>>      *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>>>   [Pcd]
>>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm 
>>> b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> index c3587a1b7814..73a4eaadb1b6 100644
>>> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> @@ -89,6 +89,10 @@ SevExit:
>>>   ; If SEV-ES is disabled then EAX will be zero.
>>>   ;
>>>   CheckSevEsFeature:
>>> +    ; Initialize the first byte of the workarea to zero to communicate to
>>> +    ; the SEC phase that SEV-ES is not enabled.
>>> +    mov     byte[SEV_ES_WORK_AREA], 0
>>> +
>>>       xor       eax, eax
>>>       ; SEV-ES can't be enabled if SEV isn't, so first check the 
>>> encryption
>>> @@ -108,6 +112,13 @@ CheckSevEsFeature:
>>>       ; Restore encryption mask
>>>       mov       edx, ebx
>>> +    test      eax, eax
>>> +    jz        NoSevEs
>>> +
>>> +    ; Set the first byte of the workarea to one to communicate to the SEC
>>> +    ; phase that SEV-ES is enabled.
>>> +    mov       byte[SEV_ES_WORK_AREA], 1
>>> +
>>>   NoSevEs:
>>>       OneTimeCallRet CheckSevEsFeature
>>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb 
>>> b/OvmfPkg/ResetVector/ResetVector.nasmb
>>> index bfb77e439105..2967617bfaa0 100644
>>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>>> @@ -72,6 +72,7 @@
>>>     %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
>>>     %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
>>>     %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
>>> +  %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>>>   %include "Ia32/PageTables64.asm"
>>>   %endif
>>>
>>
>> The OvmfPkg/ResetVector modifications have been moved to this patch, at
>> least in part, from patch "OvmfPkg/ResetVector: Add support for a 32-bit
>> SEV check".
>>
>> And I don't understand why.
> 
> I was trying to keep everything logically grouped. The early use of this 
> area is to communicate the SEV-ES status to SEC and so logically I thought 
> that should be done when the area was introduced.
> 
>>
>> I mean it's possible that setting the first byte of the work area to 1
>> does not belong in "OvmfPkg/ResetVector: Add support for a 32-bit SEV
>> check". That's OK; then said manipulation of the work area should be
>> split to its own patch, which I should then review afresh.
>>
>> What's not OK is to move code between two reviewed patches *and* keep my
>> R-b on both.
> 
> Sorry about that. A bad assumption on my part about being able to do that 
> here and in a few other places.
> 
>>
>> Please be more transparent about incremental changes.
>>
>> (1) Please revert this patch to its v7 state, and keep my R-b on it.
> 
> Will do.
> 
>>
>> (2) Please split the ResetVector changes to a new patch. For the subject
>> line, I suggest:
>>
>> OvmfPkg/ResetVector: communicate SEV-ES status to SEC before exceptions
>>
>> or something similar.
> 
> Will do.

Actually, these changes can remain part of the revert to the v6 version of 
patch 36 ("OvmfPkg/ResetVector: Add support for a 32-bit SEV check") so 
that no changes are seen in that patch from the original v6 that was reviewed.

Thanks,
Tom

> 
> Thanks,
> Tom
> 
>>
>> Thanks
>> Laszlo
>>

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

* Re: [edk2-devel] [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase
  2020-05-26 15:41     ` Lendacky, Thomas
  2020-05-26 15:45       ` Lendacky, Thomas
@ 2020-05-27 11:45       ` Laszlo Ersek
  1 sibling, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-27 11:45 UTC (permalink / raw)
  To: Tom Lendacky, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/26/20 17:41, Tom Lendacky wrote:
> On 5/25/20 10:07 AM, Laszlo Ersek wrote:
>> On 05/19/20 23:50, Lendacky, Thomas wrote:
>>> BZ:
>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C39b71c622d2d4bbf9e5b08d800bd69a5%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260160817275268&amp;sdata=hz43pd7UO60%2FWfNALLyUuUax8KX%2Bpq4SyU9NIN32Pfc%3D&amp;reserved=0
>>>
>>>
>>> A GHCB page is needed during the Sec phase, so this new page must be
>>> created. Since the #VC exception handler routines assume that a per-CPU
>>> variable area is immediately after the GHCB, this per-CPU variable area
>>> must also be created. Since the GHCB must be marked as an un-encrypted,
>>> or shared, page, an additional pagetable page is required to break down
>>> the 2MB region where the GHCB page lives into 4K pagetable entries.
>>>
>>> Create a new entry in the OVMF memory layout for the new page table
>>> page and for the SEC GHCB and per-CPU variable pages. After breaking
>>> down
>>> the 2MB page, update the GHCB page table entry to remove the encryption
>>> mask.
>>>
>>> The GHCB page will be used by the SEC #VC exception handler. The #VC
>>> exception handler will fill in the necessary fields of the GHCB and exit
>>> to the hypervisor using the VMGEXIT instruction. The hypervisor then
>>> accesses the GHCB in order to perform the requested function.
>>>
>>> Two new fixed PCDs are needed to support the SEC GHCB page:
>>>    - PcdOvmfSecGhcbBase  UINT64 value that is the base address of the
>>>                          GHCB used during the SEC phase.
>>>    - PcdOvmfSecGhcbSize  UINT64 value that is the size, in bytes, of the
>>>                          GHCB area used during the SEC phase.
>>>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>>> ---
>>>   OvmfPkg/OvmfPkg.dec                       |  9 +++
>>>   OvmfPkg/OvmfPkgX64.fdf                    |  6 ++
>>>   OvmfPkg/ResetVector/ResetVector.inf       |  5 ++
>>>   OvmfPkg/ResetVector/Ia32/PageTables64.asm | 70 +++++++++++++++++++++++
>>>   OvmfPkg/ResetVector/ResetVector.nasmb     | 17 ++++++
>>>   5 files changed, 107 insertions(+)
>>>
>>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>>> index 65bb2bb0eb4c..02ad62ed9f43 100644
>>> --- a/OvmfPkg/OvmfPkg.dec
>>> +++ b/OvmfPkg/OvmfPkg.dec
>>> @@ -281,6 +281,15 @@ [PcdsFixedAtBuild]
>>>     ## Number of page frames to use for storing grant table entries.
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdXenGrantFrames|4|UINT32|0x33
>>>   +  ## Specify the extra page table needed to mark the GHCB as
>>> unencrypted.
>>> +  #  The value should be a multiple of 4KB for each.
>>> + 
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|0x0|UINT32|0x3a
>>> + 
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize|0x0|UINT32|0x3b
>>> +
>>> +  ## The base address of the SEC GHCB page used by SEV-ES.
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|0|UINT32|0x3c
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize|0|UINT32|0x3d
>>> +
>>>   [PcdsDynamic, PcdsDynamicEx]
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
>>>    
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
>>>
>>
>> OK, the token values have been updated, due to:
>>
>> - commit 7efce2e59c20 ("OvmfPkg/PvScsiDxe: Report the number of targets
>> and LUNs", 2020-03-30)
>>
>> - commit c4c15b870239 ("OvmfPkg/PvScsiDxe: Support sending SCSI request
>> and receive response", 2020-03-30)
>>
>> - commit 093cceaf79b5 ("OvmfPkg/MptScsiDxe: Report targets and one LUN",
>> 2020-05-05)
>>
>> (Independently, when I reviewed what would become 505812ae1d2d
>> ("OvmfPkg/MptScsiDxe: Implement the PassThru method", 2020-05-05), I
>> missed that 0x39 is followed by 0x3A, not 0x40. Oh well.)
>>
>>
>>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>>> index bfca1eff9e83..88b1e880e603 100644
>>> --- a/OvmfPkg/OvmfPkgX64.fdf
>>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>>> @@ -76,6 +76,12 @@ [FD.MEMFD]
>>>   0x007000|0x001000
>>>  
>>> gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>>>
>>>   +0x008000|0x001000
>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>>
>>> +
>>> +0x009000|0x002000
>>> +gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>>
>>> +
>>>   0x010000|0x010000
>>>  
>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>>
>>>   diff --git a/OvmfPkg/ResetVector/ResetVector.inf
>>> b/OvmfPkg/ResetVector/ResetVector.inf
>>> index b0ddfa5832a2..483fd90fe785 100644
>>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>>> @@ -26,6 +26,7 @@ [Sources]
>>>   [Packages]
>>>     OvmfPkg/OvmfPkg.dec
>>>     MdePkg/MdePkg.dec
>>> +  MdeModulePkg/MdeModulePkg.dec
>>>     UefiCpuPkg/UefiCpuPkg.dec
>>>     [BuildOptions]
>>> @@ -33,5 +34,9 @@ [BuildOptions]
>>>      *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>>>     [Pcd]
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
>>> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> index abad009f20f5..c3587a1b7814 100644
>>> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>> @@ -21,6 +21,11 @@ BITS    32
>>>   %define PAGE_2M_MBO            0x080
>>>   %define PAGE_2M_PAT          0x01000
>>>   +%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
>>> +                          PAGE_DIRTY + \
>>> +                          PAGE_READ_WRITE + \
>>> +                          PAGE_PRESENT)
>>> +
>>>   %define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
>>>                             PAGE_ACCESSED + \
>>>                             PAGE_DIRTY + \
>>> @@ -75,6 +80,37 @@ NoSev:
>>>   SevExit:
>>>       OneTimeCallRet CheckSevFeature
>>>   +; Check if Secure Encrypted Virtualization - Encrypted State
>>> (SEV-ES) feature
>>> +; is enabled.
>>> +;
>>> +; Modified:  EAX, EBX, ECX
>>> +;
>>> +; If SEV-ES is enabled then EAX will be non-zero.
>>> +; If SEV-ES is disabled then EAX will be zero.
>>> +;
>>> +CheckSevEsFeature:
>>> +    xor       eax, eax
>>> +
>>> +    ; SEV-ES can't be enabled if SEV isn't, so first check the
>>> encryption
>>> +    ; mask.
>>> +    test      edx, edx
>>> +    jz        NoSevEs
>>> +
>>> +    ; Save current value of encryption mask
>>> +    mov       ebx, edx
>>> +
>>> +    ; Check if SEV-ES is enabled
>>> +    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
>>> +    mov       ecx, 0xc0010131
>>> +    rdmsr
>>> +    and       eax, 2
>>> +
>>> +    ; Restore encryption mask
>>> +    mov       edx, ebx
>>> +
>>> +NoSevEs:
>>> +    OneTimeCallRet CheckSevEsFeature
>>> +
>>>   ;
>>>   ; Modified:  EAX, EBX, ECX, EDX
>>>   ;
>>> @@ -139,6 +175,40 @@ pageTableEntriesLoop:
>>>       mov     [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
>>>       loop    pageTableEntriesLoop
>>>   +    OneTimeCall   CheckSevEsFeature
>>> +    test    eax, eax
>>> +    jz      SetCr3
>>> +
>>> +    ;
>>> +    ; The initial GHCB will live at GHCB_BASE and needs to be
>>> un-encrypted.
>>> +    ; This requires the 2MB page for this range be broken down into
>>> 512 4KB
>>> +    ; pages.  All will be marked encrypted, except for the GHCB.
>>> +    ;
>>> +    mov     ecx, (GHCB_BASE >> 21)
>>> +    mov     eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
>>> +    mov     [ecx * 8 + PT_ADDR (0x2000)], eax
>>> +
>>> +    ;
>>> +    ; Page Table Entries (512 * 4KB entries => 2MB)
>>> +    ;
>>> +    mov     ecx, 512
>>> +pageTableEntries4kLoop:
>>> +    mov     eax, ecx
>>> +    dec     eax
>>> +    shl     eax, 12
>>> +    add     eax, GHCB_BASE & 0xFFE0_0000
>>> +    add     eax, PAGE_4K_PDE_ATTR
>>> +    mov     [ecx * 8 + GHCB_PT_ADDR - 8], eax
>>> +    mov     [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
>>> +    loop    pageTableEntries4kLoop
>>> +
>>> +    ;
>>> +    ; Clear the encryption bit from the GHCB entry
>>> +    ;
>>> +    mov     ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
>>> +    mov     [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
>>> +
>>
>> (1) Why did you remove "clearGhcbMemoryLoop" (in the v6->v7 transition)?
> 
> I removed it because it actually wasn't clearing the GHCB at all. Since
> this occurred before the new page tables are loaded, the page is
> accessed encrypted. After loading the new page tables, the GHCB is now
> referenced unencrypted and so the "zeroed" page isn't actually zeroes
> anymore, it is cipher-text.
> 
> Since the GHCB is always cleared on #VC, I dropped it.
> 
>>
>> I think that's exactly the clearing loop (minimally for the CPU#0
>> per-CPU page) that I was just looking for in point (8) under
>> "OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events" (v8
>> 26/46).
>>
>> Hm... the v7 blurb says, "Ensure the per-CPU variable page remains
>> encrypted". OK, but that still doesn't explain why we don't clear it
>> (just for the guest to see).
> 
> I'll add a loop to clear the GHCB page and the per-CPU page after
> establishing the new page tables.
> 
>>
>> Also, if the patch was non-trivially modified in v7, then arguably my
>> R-b (given originally under "RFC PATCH v3 26/43") should have been
>> removed.
>>
>> Please re-instate "clearGhcbMemoryLoop" (and then keep the R-b).
> 
> I'll actually drop your Reviewed-by: since I'll need to expand and move
> the loop to clear the memory area from the original location in order
> for the clearing of the pages to be correct.

Thank you, that works for me (both code-wise and process-wise).

Cheers,
Laszlo


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

* Re: [edk2-devel] [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage
  2020-05-26 21:47       ` Lendacky, Thomas
@ 2020-05-27 11:50         ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-27 11:50 UTC (permalink / raw)
  To: Tom Lendacky, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/26/20 23:47, Tom Lendacky wrote:
> On 5/26/20 9:28 AM, Tom Lendacky wrote:
>> On 5/25/20 11:00 AM, Laszlo Ersek wrote:
>>> On 05/19/20 23:51, Lendacky, Thomas wrote:
>>>> BZ:
>>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C498df3e8d335449e596508d800c4c955%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260192476035384&amp;sdata=UKux5gXwpNe59RKQHTyk577b%2B%2FBmTIdblij8JWhXBG4%3D&amp;reserved=0
>>>>
>>>>
>>>> Reserve a fixed area of memory for SEV-ES use and set a fixed PCD,
>>>> PcdSevEsWorkAreaBase, to this value.
>>>>
>>>> This area will be used by SEV-ES support for two purposes:
>>>>    1. Communicating the SEV-ES status during BSP boot to SEC:
>>>>       Using a byte of memory from the page, the BSP reset vector
>>>> code can
>>>>       communicate the SEV-ES status to SEC for use before exception
>>>>       handling can be enabled in SEC. After SEC, this field is no
>>>> longer
>>>>       valid and the standard way of determine if SEV-ES is active
>>>> should
>>>>       be used.
>>>>
>>>>    2. Establishing an area of memory for AP boot support:
>>>>       A hypervisor is not allowed to update an SEV-ES guest's register
>>>>       state, so when booting an SEV-ES guest AP, the hypervisor is not
>>>>       allowed to set the RIP to the guest requested value. Instead an
>>>>       SEV-ES AP must be re-directed from within the guest to the actual
>>>>       requested staring location as specified in the INIT-SIPI-SIPI
>>>>       sequence.
>>>>
>>>>       Use this memory for reset vector code that can be programmed
>>>> to have
>>>>       the AP jump to the desired RIP location after starting the AP.
>>>> This
>>>>       is required for only the very first AP reset.
>>>>
>>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>>>> ---
>>>>   OvmfPkg/OvmfPkgX64.fdf                    |  3 +++
>>>>   OvmfPkg/ResetVector/ResetVector.inf       |  1 +
>>>>   OvmfPkg/ResetVector/Ia32/PageTables64.asm | 11 +++++++++++
>>>>   OvmfPkg/ResetVector/ResetVector.nasmb     |  1 +
>>>>   4 files changed, 16 insertions(+)
>>>>
>>>> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
>>>> index 88b1e880e603..8836b30a0cef 100644
>>>> --- a/OvmfPkg/OvmfPkgX64.fdf
>>>> +++ b/OvmfPkg/OvmfPkgX64.fdf
>>>> @@ -82,6 +82,9 @@ [FD.MEMFD]
>>>>   0x009000|0x002000
>>>>  
>>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>>>
>>>> +0x00B000|0x001000
>>>> +gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
>>>>
>>>> +
>>>>   0x010000|0x010000
>>>>  
>>>> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
>>>>
>>>> diff --git a/OvmfPkg/ResetVector/ResetVector.inf
>>>> b/OvmfPkg/ResetVector/ResetVector.inf
>>>> index 483fd90fe785..e94e1bfcce7e 100644
>>>> --- a/OvmfPkg/ResetVector/ResetVector.inf
>>>> +++ b/OvmfPkg/ResetVector/ResetVector.inf
>>>> @@ -34,6 +34,7 @@ [BuildOptions]
>>>>      *_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/
>>>>   [Pcd]
>>>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
>>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
>>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
>>>>     gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
>>>> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>>> b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>>> index c3587a1b7814..73a4eaadb1b6 100644
>>>> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>>> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
>>>> @@ -89,6 +89,10 @@ SevExit:
>>>>   ; If SEV-ES is disabled then EAX will be zero.
>>>>   ;
>>>>   CheckSevEsFeature:
>>>> +    ; Initialize the first byte of the workarea to zero to
>>>> communicate to
>>>> +    ; the SEC phase that SEV-ES is not enabled.
>>>> +    mov     byte[SEV_ES_WORK_AREA], 0
>>>> +
>>>>       xor       eax, eax
>>>>       ; SEV-ES can't be enabled if SEV isn't, so first check the
>>>> encryption
>>>> @@ -108,6 +112,13 @@ CheckSevEsFeature:
>>>>       ; Restore encryption mask
>>>>       mov       edx, ebx
>>>> +    test      eax, eax
>>>> +    jz        NoSevEs
>>>> +
>>>> +    ; Set the first byte of the workarea to one to communicate to
>>>> the SEC
>>>> +    ; phase that SEV-ES is enabled.
>>>> +    mov       byte[SEV_ES_WORK_AREA], 1
>>>> +
>>>>   NoSevEs:
>>>>       OneTimeCallRet CheckSevEsFeature
>>>> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb
>>>> b/OvmfPkg/ResetVector/ResetVector.nasmb
>>>> index bfb77e439105..2967617bfaa0 100644
>>>> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
>>>> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
>>>> @@ -72,6 +72,7 @@
>>>>     %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
>>>>     %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
>>>>     %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
>>>> +  %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
>>>>   %include "Ia32/PageTables64.asm"
>>>>   %endif
>>>>
>>>
>>> The OvmfPkg/ResetVector modifications have been moved to this patch, at
>>> least in part, from patch "OvmfPkg/ResetVector: Add support for a 32-bit
>>> SEV check".
>>>
>>> And I don't understand why.
>>
>> I was trying to keep everything logically grouped. The early use of
>> this area is to communicate the SEV-ES status to SEC and so logically
>> I thought that should be done when the area was introduced.
>>
>>>
>>> I mean it's possible that setting the first byte of the work area to 1
>>> does not belong in "OvmfPkg/ResetVector: Add support for a 32-bit SEV
>>> check". That's OK; then said manipulation of the work area should be
>>> split to its own patch, which I should then review afresh.
>>>
>>> What's not OK is to move code between two reviewed patches *and* keep my
>>> R-b on both.
>>
>> Sorry about that. A bad assumption on my part about being able to do
>> that here and in a few other places.
>>
>>>
>>> Please be more transparent about incremental changes.
>>>
>>> (1) Please revert this patch to its v7 state, and keep my R-b on it.
>>
>> Will do.
>>
>>>
>>> (2) Please split the ResetVector changes to a new patch. For the subject
>>> line, I suggest:
>>>
>>> OvmfPkg/ResetVector: communicate SEV-ES status to SEC before exceptions
>>>
>>> or something similar.
>>
>> Will do.
> 
> Actually, these changes can remain part of the revert to the v6 version
> of patch 36 ("OvmfPkg/ResetVector: Add support for a 32-bit SEV check")
> so that no changes are seen in that patch from the original v6 that was
> reviewed.

Yes, that works perfectly fine too; I just thought you really wanted to
move the "communicate SEV-ES status to SEC before exceptions" part to a
different patch.

If you are OK with keeping version 6 of the patch, I'm completely OK
with that!

When you post v9 of the series, please explain in the blurb what patches
you've re-set to version 6.

If you can use "git notes" (or manual editing) to mark the same
information on the individual patches (outside of their commit
messages), that's even better.

Thanks!
Laszlo


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

* Re: [edk2-devel] [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
  2020-05-26 15:06     ` Lendacky, Thomas
@ 2020-05-27 11:54       ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-27 11:54 UTC (permalink / raw)
  To: Tom Lendacky, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh, Ard Biesheuvel

On 05/26/20 17:06, Tom Lendacky wrote:
> On 5/25/20 9:47 AM, Laszlo Ersek wrote:
>> On 05/19/20 23:50, Lendacky, Thomas wrote:
>>> BZ:
>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C8d75a8b2107f4def062c08d800ba8795%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637260148432921212&amp;sdata=WNj6rvvOB%2FeVbeozpvRTXmrqFZEQuEjzEOGIU9KvJVs%3D&amp;reserved=0
>>>
>>>
>>> Under SEV-ES, a DR7 read or write intercept generates a #VC exception.
>>> The #VC handler must provide special support to the guest for this. On
>>> a DR7 write, the #VC handler must cache the value and issue a VMGEXIT
>>> to notify the hypervisor of the write. However, the #VC handler must
>>> not actually set the value of the DR7 register. On a DR7 read, the #VC
>>> handler must return the cached value of the DR7 register to the guest.
>>> VMGEXIT is not invoked for a DR7 register read.
>>>
>>> To avoid exception recursion, a #VC exception will not try to read and
>>> push the actual debug registers into the EFI_SYSTEM_CONTEXT_X64 struct
>>> and instead push zeroes. The #VC exception handler does not make use of
>>> the debug registers from saved context.
>>
>> AFAICS the following patches introcuce / reiterate the per-CPU page
>> concept:
>>
>> - "MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page
>> tables" (v8 05/46)
>> - "OvmfPkg: Create a GHCB page for use during Sec phase" (v8 29/46)
>> - "OvmfPkg: Create GHCB pages for use during Pei and Dxe phase" (v8
>> 31/46)
>>
>> I find it somewhat difficult to locate those patches and to learn about
>> the per-cpu pages from them. The first patch listed above belongs to a
>> different package. And the two other patches listed above do not precede
>> (but follow) the present patch.
>>
>> (1) Therefore please include a paragraph about the per-cpu pages in the
>> commit message of this patch.
> 
> Will do.
> 
>>
>>>
>>> Cc: Eric Dong <eric.dong@intel.com>
>>> Cc: Ray Ni <ray.ni@intel.com>
>>> Cc: Jordan Justen <jordan.l.justen@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
>>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>>> ---
>>>   .../Library/VmgExitLib/X64/VmgExitVcHandler.c | 105 ++++++++++++++++++
>>>   .../X64/ExceptionHandlerAsm.nasm              |  17 +++
>>>   .../X64/Xcode5ExceptionHandlerAsm.nasm        |  17 +++
>>>   3 files changed, 139 insertions(+)
>>
>> Please pass "--stat=1000 --stat-graph-width=20" to git-format-patch;
>> that way, the pathnames will not be truncated, and the graph to the
>> right will still not be wider than 20 chars.
>>
>> Why I'm requesting this (and unfortunately there is no way to make the
>> second switch above permanent, in the git config): because I almost
>> missed that this patch modifies both UefiCpuPkg and OvmfPkg. It would
>> have been obvious from the diffstat (if the pathnames had not been
>> truncated).
>>
>> (2) Please split the UefiCpuPkg hunks to a separate patch, if possible.
>>
>> (Or maybe consider squashing those hunks into patch
>> "UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception"
>> (v8 11/46), if the UefiCpuPkg owners prefer that.)
> 
> It would probably fit nicely into the existing patch. I'll look and
> either move it to there or create a new patch.
> 
>>
>>>
>>> diff --git a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> index b028b20f255a..e4072d79d704 100644
>>> --- a/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> +++ b/OvmfPkg/Library/VmgExitLib/X64/VmgExitVcHandler.c
>>> @@ -14,6 +14,16 @@
>>>     #define CR4_OSXSAVE (1 << 18)
>>>   +#define DR7_RESET_VALUE 0x400
>>
>> (3) From the Intel SDM, this looks like a standard value. I'd say if we
>> deem it important enough for turning into a macro, then it belongs
>> elsewhere (in some more visible header file).
>>
>> Otherwise (given that we only use it once, below), I think we could
>> simply open-code it at the location of use, with a comment.
> 
> I'll do the latter.
> 
>>
>>> +
>>> +//
>>> +// Per-CPU data mapping structure
>>> +//
>>> +typedef struct {
>>> +  BOOLEAN  Dr7Cached;
>>> +  UINT64   Dr7;
>>> +} SEV_ES_PER_CPU_DATA;
>>> +
>>>   //
>>>   // Instruction execution mode definition
>>>   //
>>> @@ -1494,6 +1504,93 @@ RdtscExit (
>>>     return 0;
>>>   }
>>>   +/**
>>> +  Handle a DR7 register write event.
>>> +
>>> +  Use the VMGEXIT instruction to handle a DR7 write event.
>>> +
>>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor
>>> Communication
>>> +                                   Block
>>> +  @param[in, out] Regs             x64 processor context
>>> +  @param[in]      InstructionData  Instruction parsing context
>>> +
>>> +  @retval 0                        Event handled successfully
>>> +  @retval Others                   New exception value to propagate
>>> +
>>> +**/
>>> +STATIC
>>> +UINT64
>>> +Dr7WriteExit (
>>> +  IN OUT GHCB                     *Ghcb,
>>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
>>> +  SEV_ES_PER_CPU_DATA            *SevEsData;
>>> +  INTN                           *Register;
>>
>> (4) This should be UINT64, per my earlier request.
>>
>>> +  UINT64                         Status;
>>> +
>>> +  Ext = &InstructionData->Ext;
>>> +  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
>>> +
>>> +  DecodeModRm (Regs, InstructionData);
>>> +
>>> +  /* MOV DRn always treats MOD == 3 no matter how encoded */
>>
>> (5) comment style
>>
>>> +  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
>>> +
>>> +  /* Using a value of 0 for ExitInfo1 means RAX holds the value */
>>
>> (6) comment style
>>
>>> +  Ghcb->SaveArea.Rax = *Register;
>>> +  GhcbSetRegValid (Ghcb, GhcbRax);
>>> +
>>> +  Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
>>> +  if (Status) {
>>
>> (7) please compare with 0 explicitly
> 
> 4 - 7 will be taken care of.
> 
>>
>>> +    return Status;
>>> +  }
>>> +
>>> +  SevEsData->Dr7 = *Register;
>>> +  SevEsData->Dr7Cached = TRUE;
>>
>> Hmmm... I'm wondering where this BOOLEAN gets re-set to FALSE on a
>> platform reset.
>>
>> In patch "OvmfPkg: Create GHCB pages for use during Pei and Dxe phase",
>> in function AmdSevEsInitialize(), we have a ZeroMem(). That should cover
>> it for PEI and DXE; OK.
>>
>> (8) In patch "OvmfPkg: Create a GHCB page for use during Sec phase"
>> however, we don't seem to zero out the per-cpu page itself (which
>> resides just after PcdOvmfSecGhcbBase).
>>
>> Do we do that elsewhere? (Sorry if I'm just not seeing it.)
>>
>> I'm asking because, after a platform reset, SevEsData->Dr7Cached may
>> read as TRUE in SEC at the very first access (it lives at a fixed
>> location, and QEMU platform reset does not clear RAM). And so we could
>> return the value cached from the previous boot rather than 0x400.
> 
> An SEV-ES guest can't be rebooted/reset without restarting Qemu because
> the guest register can't be changed by the hypervisor. So a full reboot
> isn't initially supported SEV-ES.

Apologies for missing that!

> 
> But, yes, this should still clear it to be safe for any future support.
> I'll find an appropriate place to zero it out.

With your explanation above, about platform reset, I think I'm happy
with the current handling of "Dr7Cached". So I'd like to leave the
choice to you: please either add the clearing, or document in the commit
message and/or the code that platform reset will not happen. Whichever
you like more.

Thank you!
Laszlo

> 
>>
>>
>>> +
>>> +  return 0;
>>> +}
>>> +
>>> +/**
>>> +  Handle a DR7 register read event.
>>> +
>>> +  Use the VMGEXIT instruction to handle a DR7 read event.
>>> +
>>> +  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor
>>> Communication
>>> +                                   Block
>>> +  @param[in, out] Regs             x64 processor context
>>> +  @param[in]      InstructionData  Instruction parsing context
>>> +
>>> +  @retval 0                        Event handled successfully
>>> +
>>> +**/
>>> +STATIC
>>> +UINT64
>>> +Dr7ReadExit (
>>> +  IN OUT GHCB                     *Ghcb,
>>> +  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
>>> +  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
>>> +  )
>>> +{
>>> +  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
>>> +  SEV_ES_PER_CPU_DATA            *SevEsData;
>>> +  INTN                           *Register;
>>
>> (9) Should be UINT64.
>>
>>> +
>>> +  Ext = &InstructionData->Ext;
>>> +  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
>>> +
>>> +  DecodeModRm (Regs, InstructionData);
>>> +
>>> +  /* MOV DRn always treats MOD == 3 no matter how encoded */
>>
>> (10) Please fix the comment style.
>>
>>> +  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
>>> +  *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 :
>>> DR7_RESET_VALUE;
>>> +
>>> +  return 0;
>>> +}
>>> +
>>>   /**
>>>     Handle a #VC exception.
>>>   @@ -1538,6 +1635,14 @@ VmgExitHandleVc (
>>>       ExitCode = Regs->ExceptionData;
>>>     switch (ExitCode) {
>>> +  case SVM_EXIT_DR7_READ:
>>> +    NaeExit = Dr7ReadExit;
>>> +    break;
>>> +
>>> +  case SVM_EXIT_DR7_WRITE:
>>> +    NaeExit = Dr7WriteExit;
>>> +    break;
>>> +
>>>     case SVM_EXIT_RDTSC:
>>>       NaeExit = RdtscExit;
>>>       break;
>>
>> Stopping here (before the UefiCpuPkg hunks).
> 
> 9 - 10 to be taken care of.
> 
> Thanks,
> Tom
> 
>>
>> Thanks!
>> Laszlo
>>
>>> diff --git
>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
>>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
>>> index 3814f9de3703..2a5545ecfd41 100644
>>> ---
>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
>>> +++
>>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm
>>> @@ -18,6 +18,8 @@
>>>   ; CommonExceptionHandler()
>>>   ;
>>>   +%define VC_EXCEPTION 29
>>> +
>>>   extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
>>>   extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
>>>   extern ASM_PFX(CommonExceptionHandler)
>>> @@ -224,6 +226,9 @@ HasErrorCode:
>>>       push    rax
>>>     ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
>>> +    cmp     qword [rbp + 8], VC_EXCEPTION
>>> +    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers
>>> ignored
>>> +
>>>       mov     rax, dr7
>>>       push    rax
>>>       mov     rax, dr6
>>> @@ -236,7 +241,19 @@ HasErrorCode:
>>>       push    rax
>>>       mov     rax, dr0
>>>       push    rax
>>> +    jmp     DrFinish
>>>   +VcDebugRegs:
>>> +;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid
>>> exception recursion
>>> +    xor     rax, rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +
>>> +DrFinish:
>>>   ;; FX_SAVE_STATE_X64 FxSaveState;
>>>       sub rsp, 512
>>>       mov rdi, rsp
>>> diff --git
>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
>>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
>>>
>>> index 19198f273137..26cae56cc5cf 100644
>>> ---
>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
>>>
>>> +++
>>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm
>>>
>>> @@ -18,6 +18,8 @@
>>>   ; CommonExceptionHandler()
>>>   ;
>>>   +%define VC_EXCEPTION 29
>>> +
>>>   extern ASM_PFX(mErrorCodeFlag)    ; Error code flags for exceptions
>>>   extern ASM_PFX(mDoFarReturnFlag)  ; Do far return flag
>>>   extern ASM_PFX(CommonExceptionHandler)
>>> @@ -225,6 +227,9 @@ HasErrorCode:
>>>       push    rax
>>>     ;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
>>> +    cmp     qword [rbp + 8], VC_EXCEPTION
>>> +    je      VcDebugRegs          ; For SEV-ES (#VC) Debug registers
>>> ignored
>>> +
>>>       mov     rax, dr7
>>>       push    rax
>>>       mov     rax, dr6
>>> @@ -237,7 +242,19 @@ HasErrorCode:
>>>       push    rax
>>>       mov     rax, dr0
>>>       push    rax
>>> +    jmp     DrFinish
>>>   +VcDebugRegs:
>>> +;; UINT64  Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid
>>> exception recursion
>>> +    xor     rax, rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +    push    rax
>>> +
>>> +DrFinish:
>>>   ;; FX_SAVE_STATE_X64 FxSaveState;
>>>       sub rsp, 512
>>>       mov rdi, rsp
>>>
>>
> 


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

* Re: [edk2-devel] [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check
  2020-05-26 16:31     ` Lendacky, Thomas
@ 2020-05-27 11:59       ` Laszlo Ersek
  0 siblings, 0 replies; 100+ messages in thread
From: Laszlo Ersek @ 2020-05-27 11:59 UTC (permalink / raw)
  To: Tom Lendacky, devel
  Cc: Jordan Justen, Ard Biesheuvel, Michael D Kinney, Liming Gao,
	Eric Dong, Ray Ni, Brijesh Singh

On 05/26/20 18:31, Tom Lendacky wrote:
> On 5/25/20 11:50 AM, Laszlo Ersek wrote:

>> Now I'm reviewing the differences (incrementally from v6 to v8), and I'm
>> shocked how many changes you incorporated into preexistent patches,
>> while keeping my R-b's.
> 
> My apologies for this. I was experimenting with cleaning things up and
> making the code more readable and I guess I forgot to either remove it
> or note it as a change to be re-reviewed - thinking I had done one or
> the other.

I agree that it's not easy to track previously given R-b's across
rebases (reordering patches, moving hunks between them, etc), especially
if there's no huge need to touch up the commit messages themselves.

What helps IME is to review the full set (just about to be posted)
before sending, with "git-range-diff", against the last posted version.
If code changes are shown for any patch (not just context differences),
take notes about those patches, then perform a final rebase, dropping
all the previously given tags from the affected patches.

(Clearly such a "git-range-diff" is incomparably faster on the author's
end than on the reviewer's end, as the author has it all in their
"working set".)

>> (1) Unless there was an actual bug in the v6 version of this patch,
>> please let's go back to that. IOW, if the v6->v8 changes are only
>> cleanups or optimizations, let's please postpone them.
> 
> Yes, I'll revert it back to the original version. Again, sorry for all
> of this churn.

Thank you!
Laszlo


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

* Re: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
  2020-05-20 16:56 ` [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Lendacky, Thomas
@ 2020-06-01  6:17   ` Dong, Eric
  2020-06-01 16:10     ` Lendacky, Thomas
  2020-06-01  7:28   ` Dong, Eric
  1 sibling, 1 reply; 100+ messages in thread
From: Dong, Eric @ 2020-06-01  6:17 UTC (permalink / raw)
  To: Tom Lendacky, devel@edk2.groups.io
  Cc: Justen, Jordan L, Laszlo Ersek, Ard Biesheuvel, Kinney, Michael D,
	Gao, Liming, Ni, Ray, Brijesh Singh

Hi Tom,

> -----Original Message-----
> From: Tom Lendacky <thomas.lendacky@amd.com>
> Sent: Wednesday, May 20, 2020 5:51 AM
> To: devel@edk2.groups.io
> Cc: Justen, Jordan L <jordan.l.justen@intel.com>; Laszlo Ersek
> <lersek@redhat.com>; Ard Biesheuvel <ard.biesheuvel@linaro.org>; Kinney,
> Michael D <michael.d.kinney@intel.com>; Gao, Liming
> <liming.gao@intel.com>; Dong, Eric <eric.dong@intel.com>; Ni, Ray
> <ray.ni@intel.com>; Brijesh Singh <brijesh.singh@amd.com>
> Subject: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Typically, an AP is booted using the INIT-SIPI-SIPI sequence. This sequence is
> intercepted by the hypervisor, which sets the AP's registers to the values
> requested by the sequence. At that point, the hypervisor can start the AP,
> which will then begin execution at the appropriate location.
> 
> Under SEV-ES, AP booting presents some challenges since the hypervisor is
> not allowed to alter the AP's register state. In this situation, we have to
> distinguish between the AP's first boot and AP's subsequent boots.
> 
> First boot:
>  Once the AP's register state has been defined (which is before the guest  is
> first booted) it cannot be altered. Should the hypervisor attempt to  alter the
> register state, the change would be detected by the hardware  and the
> VMRUN instruction would fail. Given this, the first boot for the  AP is
> required to begin execution with this initial register state, which  is typically
> the reset vector. This prevents the BSP from directing the  AP startup
> location through the INIT-SIPI-SIPI sequence.
> 
>  To work around this, the firmware will provide a build time reserved area
> that can be used as the initial IP value. The hypervisor can extract this
> location value by checking for the SEV-ES reset block GUID that must be
> located 48-bytes from the end of the firmware. The format of the SEV-ES
> reset block area is:
> 
>    0x00 - 0x01 - SEV-ES Reset IP
>    0x02 - 0x03 - SEV-ES Reset CS Segment Base[31:16]
>    0x04 - 0x05 - Size of the SEV-ES reset block
>    0x06 - 0x15 - SEV-ES Reset Block GUID
>                    (00f771de-1a7e-4fcb-890e-68c77e2fb44e)
> 
>    The total size is 22 bytes. Any expansion to this block must be done
>    by adding new values before existing values.
> 
>  The hypervisor will use the IP and CS values obtained from the SEV-ES  reset
> block to set as the AP's initial values. The CS Segment Base  represents the
> upper 16 bits of the CS segment base and must be left  shifted by 16 bits to
> form the complete CS segment base value.
> 
>  Before booting the AP for the first time, the BSP must initialize the  SEV-ES
> reset area. This consists of programming a FAR JMP instruction  to the
> contents of a memory location that is also located in the SEV-ES  reset area.
> The BSP must program the IP and CS values for the FAR JMP  based on values
> drived from the INIT-SIPI-SIPI sequence.
> 
> Subsequent boots:
>  Again, the hypervisor cannot alter the AP register state, so a method is
> required to take the AP out of halt state and redirect it to the desired  IP
> location. If it is determined that the AP is running in an SEV-ES  guest, then
> instead of calling CpuSleep(), a VMGEXIT is issued with the  AP Reset Hold
> exit code (0x80000004). The hypervisor will put the AP in  a halt state, waiting
> for an INIT-SIPI-SIPI sequence. Once the sequence  is recognized, the
> hypervisor will resume the AP. At this point the AP  must transition from the
> current 64-bit long mode down to 16-bit real  mode and begin executing at
> the derived location from the INIT-SIPI-SIPI  sequence.
> 
>  Another change is around the area of obtaining the (x2)APIC ID during AP
> startup. During AP startup, the AP can't take a #VC exception before the  AP
> has established a stack. However, the AP stack is set by using the  (x2)APIC ID,
> which is obtained through CPUID instructions. A CPUID  instruction will cause
> a #VC, so a different method must be used. The  GHCB protocol supports a
> method to obtain CPUID information from the  hypervisor through the GHCB
> MSR. This method does not require a stack,  so it is used to obtain the
> necessary CPUID information to determine the  (x2)APIC ID.
> 
> The new 16-bit protected mode GDT entry is used in order to transition from
> 64-bit long mode down to 16-bit real mode.
> 
> A new assembler routine is created that takes the AP from 64-bit long mode
> to 16-bit real mode.  This is located under 1MB in memory and transitions
> from 64-bit long mode to 32-bit compatibility mode to 16-bit protected mode
> and finally 16-bit real mode.
> 
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   3 +
>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   3 +
>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |  60 ++++
>  UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  70 +++-
>  UefiCpuPkg/Library/MpInitLib/MpLib.c          | 312 +++++++++++++++++-
>  UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |  19 ++
>  UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |   2 +-
>  UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |   2 +-
>  .../Library/MpInitLib/Ia32/MpFuncs.nasm       |  15 +
>  UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |   4 +-
>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 239 ++++++++++++++
>  11 files changed, 714 insertions(+), 15 deletions(-)
> 
> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> index 583276595619..1771575c69c1 100644
> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> @@ -52,6 +52,7 @@ [LibraryClasses]
>    DebugAgentLib
>    SynchronizationLib
>    PcdLib
> +  VmgExitLib
> 
>  [Protocols]
>    gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES
> @@ -72,4 +73,6 @@ [Pcd]
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ##
> SOMETIMES_CONSUMES
> 
> gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds
> ## CONSUMES
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ##
> CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ##
> SOMETIMES_CONSUMES
>    gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ##
> CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ##
> CONSUMES
> diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> index 4b3d39fbf36c..34abf25d43cd 100644
> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> @@ -51,6 +51,7 @@ [LibraryClasses]
>    SynchronizationLib
>    PeiServicesLib
>    PcdLib
> +  VmgExitLib
> 
>  [Pcd]
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ##
> CONSUMES
> @@ -62,6 +63,8 @@ [Pcd]
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ##
> CONSUMES
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ##
> SOMETIMES_CONSUMES
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ##
> SOMETIMES_CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ##
> CONSUMES
> 
>  [Ppis]
>    gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
> diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h
> b/UefiCpuPkg/Library/MpInitLib/MpLib.h
> index 5b46c295b6b2..f0cbb3763b5d 100644
> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
> @@ -173,6 +173,11 @@ typedef struct {
>    UINT8             *RelocateApLoopFuncAddress;
>    UINTN             RelocateApLoopFuncSize;
>    UINTN             ModeTransitionOffset;
> +  UINTN             SwitchToRealSize;
> +  UINTN             SwitchToRealOffset;
> +  UINTN             SwitchToRealNoNxOffset;
> +  UINTN             SwitchToRealPM16ModeOffset;
> +  UINTN             SwitchToRealPM16ModeSize;
>  } MP_ASSEMBLY_ADDRESS_MAP;
> 
>  typedef struct _CPU_MP_DATA  CPU_MP_DATA; @@ -211,6 +216,8 @@
> typedef struct {
>    // Enable5LevelPaging indicates whether 5-level paging is enabled in long
> mode.
>    //
>    BOOLEAN               Enable5LevelPaging;
> +  BOOLEAN               SevEsIsEnabled;
> +  UINTN                 GhcbBase;
>  } MP_CPU_EXCHANGE_INFO;
> 
>  #pragma pack()
> @@ -257,6 +264,7 @@ struct _CPU_MP_DATA {
>    UINT8                          ApLoopMode;
>    UINT8                          ApTargetCState;
>    UINT16                         PmCodeSegment;
> +  UINT16                         Pm16CodeSegment;
>    CPU_AP_DATA                    *CpuData;
>    volatile MP_CPU_EXCHANGE_INFO  *MpCpuExchangeInfo;
> 
> @@ -278,8 +286,47 @@ struct _CPU_MP_DATA {
>    BOOLEAN                        WakeUpByInitSipiSipi;
> 
>    BOOLEAN                        SevEsIsEnabled;
> +  UINTN                          SevEsAPBuffer;
> +  UINTN                          SevEsAPResetStackStart;
> +  CPU_MP_DATA                    *NewCpuMpData;
> +
> +  UINT64                         GhcbBase;
>  };
> 
> +#define AP_RESET_STACK_SIZE 64
> +
> +#pragma pack(1)
> +
> +typedef struct {
> +  UINT8   InsnBuffer[8];
> +  UINT16  Rip;
> +  UINT16  Segment;
> +} SEV_ES_AP_JMP_FAR;
> +
> +#pragma pack()
> +
> +/**
> +  Assembly code to move an AP from long mode to real mode.
> +
> +  Move an AP from long mode to real mode in preparation to invoking
> + the reset vector.  This is used for SEV-ES guests where a hypervisor
> + is not allowed to set the CS and RIP to point to the reset vector.
> +
> +  @param[in]  BufferStart  The reset vector target.
> +  @param[in]  Code16       16-bit protected mode code segment value.
> +  @param[in]  Code32       32-bit protected mode code segment value.
> +  @param[in]  StackStart   The start of a stack to be used for transitioning
> +                           from long mode to real mode.
> +**/
> +typedef
> +VOID
> +(EFIAPI AP_RESET) (
> +  IN UINTN    BufferStart,
> +  IN UINT16   Code16,
> +  IN UINT16   Code32,
> +  IN UINTN    StackStart
> +  );
> +
>  extern EFI_GUID mCpuInitMpLibHobGuid;
> 
>  /**
> @@ -385,6 +432,19 @@ GetModeTransitionBuffer (
>    IN UINTN                BufferSize
>    );
> 
> +/**
> +  Return the address of the SEV-ES AP jump table.
> +
> +  This buffer is required in order for an SEV-ES guest to transition
> + from  UEFI into an OS.
> +
> +  @retval other   Return SEV-ES AP jump table buffer
> +**/
> +UINTN
> +GetSevEsAPMemory (
> +  VOID
> +  );
> +
>  /**
>    This function will be called by BSP to wakeup AP.
> 
> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> index 8ccddf8e9f9c..19527300ff3a 100644
> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> @@ -12,6 +12,8 @@
>  #include <Library/UefiBootServicesTableLib.h>
>  #include <Library/DebugAgentLib.h>
>  #include <Library/DxeServicesTableLib.h>
> +#include <Register/Amd/Fam17Msr.h>
> +#include <Register/Amd/Ghcb.h>
> 
>  #include <Protocol/Timer.h>
> 
> @@ -144,6 +146,39 @@ GetModeTransitionBuffer (
>    return (UINTN)StartAddress;
>  }
> 
> +/**
> +  Return the address of the SEV-ES AP jump table.
> +
> +  This buffer is required in order for an SEV-ES guest to transition
> + from  UEFI into an OS.
> +
> +  @retval other   Return SEV-ES AP jump table buffer
> +**/
> +UINTN
> +GetSevEsAPMemory (
> +  VOID
> +  )
> +{
> +  EFI_STATUS            Status;
> +  EFI_PHYSICAL_ADDRESS  StartAddress;
> +
> +  //
> +  // Allocate 1 page for AP jump table page  //  StartAddress =
> + BASE_4GB - 1;  Status = gBS->AllocatePages (
> +                  AllocateMaxAddress,
> +                  EfiReservedMemoryType,
> +                  1,
> +                  &StartAddress
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN)
> + StartAddress));
> +
> +  return (UINTN) StartAddress;
> +}
> +
>  /**
>    Checks APs status and updates APs status if needed.
> 
> @@ -218,6 +253,38 @@ CheckApsStatus (
>    }
>  }
> 
> +/**
> +  Get Protected mode code segment with 16-bit default addressing
> +  from current GDT table.
> +
> +  @return  Protected mode 16-bit code segment value.
> +**/
> +UINT16
> +GetProtectedMode16CS (
> +  VOID
> +  )
> +{
> +  IA32_DESCRIPTOR          GdtrDesc;
> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
> +  UINTN                    GdtEntryCount;
> +  UINT16                   Index;
> +
> +  Index = (UINT16) -1;
> +  AsmReadGdtr (&GdtrDesc);
> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
> +(IA32_SEGMENT_DESCRIPTOR);
> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
> +  for (Index = 0; Index < GdtEntryCount; Index++) {
> +    if (GdtEntry->Bits.L == 0) {
> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) {
> +        break;
> +      }
> +    }
> +    GdtEntry++;
> +  }
> +  ASSERT (Index != GdtEntryCount);
> +  return Index * 8;
> +}
> +
>  /**
>    Get Protected mode code segment from current GDT table.
> 
> @@ -238,7 +305,7 @@ GetProtectedModeCS (
>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>    for (Index = 0; Index < GdtEntryCount; Index++) {
>      if (GdtEntry->Bits.L == 0) {
> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>          break;
>        }
>      }
> @@ -300,6 +367,7 @@ MpInitChangeApLoopCallback (
> 
>    CpuMpData = GetCpuMpData ();
>    CpuMpData->PmCodeSegment = GetProtectedModeCS ();
> +  CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();
>    CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
>    mNumberToFinish = CpuMpData->CpuCount - 1;
>    WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); diff --git
> a/UefiCpuPkg/Library/MpInitLib/MpLib.c
> b/UefiCpuPkg/Library/MpInitLib/MpLib.c
> index a8b605f569bf..aeab575bb525 100644
> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
> @@ -9,6 +9,9 @@
>  **/
> 
>  #include "MpLib.h"
> +#include <Library/VmgExitLib.h>
> +#include <Register/Amd/Fam17Msr.h>
> +#include <Register/Amd/Ghcb.h>
> 
>  EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
> 
> @@ -314,6 +317,14 @@ GetApLoopMode (
>        //
>        ApLoopMode = ApInHltLoop;
>      }
> +
> +    if (PcdGetBool (PcdSevEsIsEnabled)) {
> +      //
> +      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB
> +      // protocol for starting APs
> +      //
> +      ApLoopMode = ApInHltLoop;
> +    }
>    }
> 
>    if (ApLoopMode != ApInMwaitLoop) {
> @@ -610,6 +621,112 @@ InitializeApData (
>    SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);  }
> 
> +/**
> +  Get Protected mode code segment with 16-bit default addressing
> +  from current GDT table.
> +
> +  @return  Protected mode 16-bit code segment value.
> +**/
> +STATIC
> +UINT16
> +GetProtectedMode16CS (
> +  VOID
> +  )
> +{
> +  IA32_DESCRIPTOR          GdtrDesc;
> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
> +  UINTN                    GdtEntryCount;
> +  UINT16                   Index;
> +
> +  Index = (UINT16) -1;
> +  AsmReadGdtr (&GdtrDesc);
> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
> +(IA32_SEGMENT_DESCRIPTOR);
> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
> +  for (Index = 0; Index < GdtEntryCount; Index++) {
> +    if (GdtEntry->Bits.L == 0 &&
> +        GdtEntry->Bits.DB == 0 &&
> +        GdtEntry->Bits.Type > 8) {
> +      break;
> +    }
> +    GdtEntry++;
> +  }
> +  ASSERT (Index != GdtEntryCount);
> +  return Index * 8;
> +}
> +
> +/**
> +  Get Protected mode code segment with 32-bit default addressing
> +  from current GDT table.
> +
> +  @return  Protected mode 32-bit code segment value.
> +**/
> +STATIC
> +UINT16
> +GetProtectedMode32CS (
> +  VOID
> +  )
> +{
> +  IA32_DESCRIPTOR          GdtrDesc;
> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
> +  UINTN                    GdtEntryCount;
> +  UINT16                   Index;
> +
> +  Index = (UINT16) -1;
> +  AsmReadGdtr (&GdtrDesc);
> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
> +(IA32_SEGMENT_DESCRIPTOR);
> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
> +  for (Index = 0; Index < GdtEntryCount; Index++) {
> +    if (GdtEntry->Bits.L == 0 &&
> +        GdtEntry->Bits.DB == 1 &&
> +        GdtEntry->Bits.Type > 8) {
> +      break;
> +    }
> +    GdtEntry++;
> +  }
> +  ASSERT (Index != GdtEntryCount);
> +  return Index * 8;
> +}
> +
> +/**
> +  Reset an AP when in SEV-ES mode.
> +
> +  If successful, this function never returns.
> +
> +  @param[in] Ghcb                 Pointer to the GHCB
> +  @param[in] CpuMpData            Pointer to CPU MP Data
> +
> +**/
> +STATIC
> +VOID
> +MpInitLibSevEsAPReset (
> +  IN GHCB                         *Ghcb,
> +  IN CPU_MP_DATA                  *CpuMpData
> +  )
> +{
> +  UINT16           Code16, Code32;
> +  AP_RESET         *APResetFn;
> +  UINTN            BufferStart;
> +  UINTN            StackStart;
> +
> +  Code16 = GetProtectedMode16CS ();
> +  Code32 = GetProtectedMode32CS ();
> +
> +  if (CpuMpData->WakeupBufferHigh != 0) {
> +    APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh +
> + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
> +  } else {
> +    APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo-
> >BufferStart
> + + CpuMpData->AddressMap.SwitchToRealOffset);
> +  }
> +
> +  BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
> +  StackStart = CpuMpData->SevEsAPResetStackStart -
> +                 (AP_RESET_STACK_SIZE * GetApicId ());
> +
> +  //
> +  // This call never returns.
> +  //
> +  APResetFn (BufferStart, Code16, Code32, StackStart); }
> +
>  /**
>    This function will be called from AP reset code if BSP uses WakeUpAP.
> 
> @@ -671,7 +788,14 @@ ApWakeupFunction (
>        InitializeApData (CpuMpData, ProcessorNumber, BistData,
> ApTopOfStack);
>        ApStartupSignalBuffer = CpuMpData-
> >CpuData[ProcessorNumber].StartupApSignal;
> 
> -      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo-
> >NumApsExecuting);
> +      //
> +      // Delay decrementing the APs executing count when SEV-ES is enabled
> +      // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly
> +      // performs another INIT-SIPI-SIPI sequence.
> +      //
> +      if (!CpuMpData->SevEsIsEnabled) {
> +        InterlockedDecrement ((UINT32 *) &CpuMpData-
> >MpCpuExchangeInfo->NumApsExecuting);
> +      }
>      } else {
>        //
>        // Execute AP function if AP is ready @@ -778,7 +902,52 @@
> ApWakeupFunction (
>        //
>        while (TRUE) {
>          DisableInterrupts ();
> -        CpuSleep ();
> +        if (CpuMpData->SevEsIsEnabled) {
> +          MSR_SEV_ES_GHCB_REGISTER  Msr;
> +          GHCB                      *Ghcb;
> +          UINT64                    Status;
> +          BOOLEAN                   DoDecrement;
> +
> +          if (CpuMpData->InitFlag == ApInitConfig) {
> +            DoDecrement = TRUE;
> +          }
> +
> +          while (TRUE) {
> +            Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
> +            Ghcb = Msr.Ghcb;
> +
> +            VmgInit (Ghcb);
> +
> +            if (DoDecrement) {
> +              DoDecrement = FALSE;
> +
> +              //
> +              // Perform the delayed decrement just before issuing the first
> +              // VMGEXIT with AP_RESET_HOLD.
> +              //
> +              InterlockedDecrement ((UINT32 *) &CpuMpData-
> >MpCpuExchangeInfo->NumApsExecuting);
> +            }
> +
> +            Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
> +            if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
> +              VmgDone (Ghcb);
> +              break;
> +            }
> +
> +            VmgDone (Ghcb);
> +          }
> +
> +          //
> +          // Awakened in a new phase? Use the new CpuMpData
> +          //
> +          if (CpuMpData->NewCpuMpData) {
> +            CpuMpData = CpuMpData->NewCpuMpData;
> +          }
> +
> +          MpInitLibSevEsAPReset (Ghcb, CpuMpData);
> +        } else {
> +          CpuSleep ();
> +        }
>          CpuPause ();
>        }
>      }
> @@ -891,6 +1060,9 @@ FillExchangeInfoData (
>    ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
>    DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName,
> ExchangeInfo->Enable5LevelPaging));
> 
> +  ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;
> +  ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;
> +
>    //
>    // Get the BSP's data of GDT and IDT
>    //
> @@ -917,8 +1089,9 @@ FillExchangeInfoData (
>    // EfiBootServicesCode to avoid page fault if NX memory protection is
> enabled.
>    //
>    if (CpuMpData->WakeupBufferHigh != 0) {
> -    Size = CpuMpData->AddressMap.RendezvousFunnelSize -
> -           CpuMpData->AddressMap.ModeTransitionOffset;
> +    Size = CpuMpData->AddressMap.RendezvousFunnelSize +
> +             CpuMpData->AddressMap.SwitchToRealSize -
> +             CpuMpData->AddressMap.ModeTransitionOffset;
>      CopyMem (
>        (VOID *)CpuMpData->WakeupBufferHigh,
>        CpuMpData->AddressMap.RendezvousFunnelAddress + @@ -971,7
> +1144,8 @@ BackupAndPrepareWakeupBuffer(
>    CopyMem (
>      (VOID *) CpuMpData->WakeupBuffer,
>      (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
> -    CpuMpData->AddressMap.RendezvousFunnelSize
> +    CpuMpData->AddressMap.RendezvousFunnelSize +
> +      CpuMpData->AddressMap.SwitchToRealSize
>      );
>  }
> 
> @@ -992,6 +1166,44 @@ RestoreWakeupBuffer(
>      );
>  }
> 
> +/**
> +  Calculate the size of the reset stack.
> +
> +  @retval                 Total amount of memory required for stacks
> +**/
> +STATIC
> +UINTN
> +GetApResetStackSize (
> +  VOID
> +  )
> +{
> +  return AP_RESET_STACK_SIZE *
> +PcdGet32(PcdCpuMaxLogicalProcessorNumber);
> +}
> +
> +/**
> +  Calculate the size of the reset vector.
> +
> +  @param[in]  AddressMap  The pointer to Address Map structure.
> +
> +  @retval                 Total amount of memory required for the AP reset area
> +**/
> +STATIC
> +UINTN
> +GetApResetVectorSize (
> +  IN MP_ASSEMBLY_ADDRESS_MAP  *AddressMap
> +  )
> +{
> +  UINTN  Size;
> +
> +  Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +
> +                        AddressMap->SwitchToRealSize +
> +                        sizeof (MP_CPU_EXCHANGE_INFO),
> +                      CPU_STACK_ALIGNMENT);  Size +=
> + GetApResetStackSize ();
> +
> +  return Size;
> +}
> +
>  /**
>    Allocate reset vector buffer.
> 
> @@ -1005,16 +1217,22 @@ AllocateResetVector (
>    UINTN           ApResetVectorSize;
> 
>    if (CpuMpData->WakeupBuffer == (UINTN) -1) {
> -    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
> -                          sizeof (MP_CPU_EXCHANGE_INFO);
> +    ApResetVectorSize = GetApResetVectorSize (&CpuMpData-
> >AddressMap);
> 
>      CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);
>      CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)
> (UINTN)
> -                    (CpuMpData->WakeupBuffer + CpuMpData-
> >AddressMap.RendezvousFunnelSize);
> +                    (CpuMpData->WakeupBuffer +
> +                       CpuMpData->AddressMap.RendezvousFunnelSize +
> +                       CpuMpData->AddressMap.SwitchToRealSize);
>      CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (
> -                                    CpuMpData->AddressMap.RendezvousFunnelSize -
> +                                    CpuMpData->AddressMap.RendezvousFunnelSize +
> +
> + CpuMpData->AddressMap.SwitchToRealSize -
>                                      CpuMpData->AddressMap.ModeTransitionOffset
>                                      );
> +    //
> +    // The reset stack starts at the end of the buffer.
> +    //
> +    CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer +
> + ApResetVectorSize;
>    }
>    BackupAndPrepareWakeupBuffer (CpuMpData);  } @@ -1029,7 +1247,31
> @@ FreeResetVector (
>    IN CPU_MP_DATA              *CpuMpData
>    )
>  {
> -  RestoreWakeupBuffer (CpuMpData);
> +  //
> +  // If SEV-ES is enabled, the reset area is needed for AP parking and
> +  // and AP startup in the OS, so the reset area is reserved. Do not
> +  // perform the restore as this will overwrite memory which has data
> +  // needed by SEV-ES.
> +  //
> +  if (!CpuMpData->SevEsIsEnabled) {
> +    RestoreWakeupBuffer (CpuMpData);
> +  }
> +}
> +
> +/**
> +  Allocate the SEV-ES AP jump table buffer.
> +
> +  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.
> +**/
> +VOID
> +AllocateSevEsAPMemory (
> +  IN OUT CPU_MP_DATA          *CpuMpData
> +  )
> +{
> +  if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {
> +    CpuMpData->SevEsAPBuffer =
> +      CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
> +  }
>  }
> 
>  /**
> @@ -1066,6 +1308,7 @@ WakeUpAP (
>        CpuMpData->InitFlag   != ApInitDone) {
>      ResetVectorRequired = TRUE;
>      AllocateResetVector (CpuMpData);
> +    AllocateSevEsAPMemory (CpuMpData);
>      FillExchangeInfoData (CpuMpData);
>      SaveLocalApicTimerSetting (CpuMpData);
>    }
> @@ -1102,6 +1345,50 @@ WakeUpAP (
>        }
>      }
>      if (ResetVectorRequired) {
> +      //
> +      // For SEV-ES, the initial AP boot address will be defined by
> +      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address
> +      // from the original INIT-SIPI-SIPI.
> +      //
> +      if (CpuMpData->SevEsIsEnabled) {
> +        SEV_ES_AP_JMP_FAR *JmpFar;
> +        UINT32            Offset, InsnByte;
> +        UINT8             LoNib, HiNib;
> +
> +        JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32
> (PcdSevEsWorkAreaBase);
> +        ASSERT (JmpFar != NULL);
> +
> +        //
> +        // Obtain the address of the Segment/Rip location in the workarea.
> +        // This will be set to a value derived from the SIPI vector and will
> +        // be the memory address used for the far jump below.
> +        //
> +        Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
> +        Offset += sizeof (JmpFar->InsnBuffer);
> +        LoNib = (UINT8) Offset;
> +        HiNib = (UINT8) (Offset >> 8);
> +
> +        //
> +        // Program the workarea (which is the initial AP boot address) with
> +        // far jump to the SIPI vector (where XX and YY represent the
> +        // address of where the SIPI vector is stored.
> +        //
> +        //   JMP FAR [CS:XXYY] => 2E FF 2E YY XX
> +        //
> +        InsnByte = 0;
> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // CS override prefix
> +        JmpFar->InsnBuffer[InsnByte++] = 0xFF;  // JMP (FAR)
> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // ModRM (JMP memory
> location)
> +        JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
> +        JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
> +
> +        //
> +        // Program the Segment/Rip based on the SIPI vector (always at least
> +        // 16-byte aligned, so Rip is set to 0).
> +        //
> +        JmpFar->Rip = 0;
> +        JmpFar->Segment = (UINT16) (ExchangeInfo->BufferStart >> 4);
> +      }

For this wake-up process, current code just handles the broadcast type. I think it also needs to handle wake-up specific AP case. Right?

Thanks,
Eric
>        //
>        // Wakeup all APs
>        //
> @@ -1669,7 +1956,7 @@ MpInitLibInitialize (
>    ASSERT (MaxLogicalProcessorNumber != 0);
> 
>    AsmGetAddressMap (&AddressMap);
> -  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof
> (MP_CPU_EXCHANGE_INFO);
> +  ApResetVectorSize = GetApResetVectorSize (&AddressMap);
>    ApStackSize = PcdGet32(PcdCpuApStackSize);
>    ApLoopMode  = GetApLoopMode (&MonitorFilterSize);
> 
> @@ -1728,6 +2015,8 @@ MpInitLibInitialize (
>    CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData
> + MaxLogicalProcessorNumber);
>    InitializeSpinLock(&CpuMpData->MpLock);
>    CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
> +  CpuMpData->SevEsAPBuffer  = (UINTN) -1;
> +  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
> 
>    //
>    // Make sure no memory usage outside of the allocated buffer.
> @@ -1786,6 +2075,7 @@ MpInitLibInitialize (
>      // APs have been wakeup before, just get the CPU Information
>      // from HOB
>      //
> +    OldCpuMpData->NewCpuMpData = CpuMpData;
>      CpuMpData->CpuCount  = OldCpuMpData->CpuCount;
>      CpuMpData->BspNumber = OldCpuMpData->BspNumber;
>      CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; diff --git
> a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> index a548fed23fa7..e17a351e5cfd 100644
> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> @@ -280,6 +280,25 @@ GetModeTransitionBuffer (
>    return 0;
>  }
> 
> +/**
> +  Return the address of the SEV-ES AP jump table.
> +
> +  This buffer is required in order for an SEV-ES guest to transition
> + from  UEFI into an OS.
> +
> +  @retval other   Return SEV-ES AP jump table buffer
> +**/
> +UINTN
> +GetSevEsAPMemory (
> +  VOID
> +  )
> +{
> +  //
> +  // PEI phase doesn't need to do such transition. So simply return 0.
> +  //
> +  return 0;
> +}
> +
>  /**
>    Checks APs status and updates APs status if needed.
> 
> diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> index 6298571e29b2..28f8e8e133e5 100644
> --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> @@ -121,7 +121,7 @@ GetProtectedModeCS (
>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>    for (Index = 0; Index < GdtEntryCount; Index++) {
>      if (GdtEntry->Bits.L == 0) {
> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>          break;
>        }
>      }
> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> index efb1bc2bf7cb..4f5a7c859a56 100644
> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>  CPU_SWITCH_STATE_STORED       equ        1
>  CPU_SWITCH_STATE_LOADED       equ        2
> 
> -LockLocation                  equ        (RendezvousFunnelProcEnd -
> RendezvousFunnelProcStart)
> +LockLocation                  equ        (SwitchToRealProcEnd -
> RendezvousFunnelProcStart)
>  StackStartAddressLocation     equ        LockLocation + 04h
>  StackSizeLocation             equ        LockLocation + 08h
>  ApProcedureLocation           equ        LockLocation + 0Ch
> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> index b74046b76af3..309d53bf3b37 100644
> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> @@ -215,6 +215,16 @@ CProcedureInvoke:
>      jmp        $                 ; Never reach here
>  RendezvousFunnelProcEnd:
> 
> +;----------------------------------------------------------------------
> +---------------
> +;SwitchToRealProc procedure follows.
> +;NOT USED IN 32 BIT MODE.
> +;----------------------------------------------------------------------
> +---------------
> +global ASM_PFX(SwitchToRealProc)
> +ASM_PFX(SwitchToRealProc):
> +SwitchToRealProcStart:
> +    jmp        $                 ; Never reach here
> +SwitchToRealProcEnd:
> +
>  ;-------------------------------------------------------------------------------------
>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
> TopOfApStack, CountTofinish);
>  ;-------------------------------------------------------------------------------------
> @@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
>      mov        dword [ebx + 0Ch], AsmRelocateApLoopStart
>      mov        dword [ebx + 10h], AsmRelocateApLoopEnd -
> AsmRelocateApLoopStart
>      mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
> +    mov        dword [ebx + 18h], SwitchToRealProcEnd -
> SwitchToRealProcStart       ; SwitchToRealSize
> +    mov        dword [ebx + 1Ch], SwitchToRealProcStart -
> RendezvousFunnelProcStart ; SwitchToRealOffset
> +    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start               ;
> SwitchToRealNoNxOffset
> +    mov        dword [ebx + 24h], 0                                                 ;
> SwitchToRealPM16ModeOffset
> +    mov        dword [ebx + 28h], 0                                                 ;
> SwitchToRealPM16ModeSize
> 
>      popad
>      ret
> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> index 58ef369342a7..c92daaaffd6b 100644
> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>  CPU_SWITCH_STATE_STORED       equ        1
>  CPU_SWITCH_STATE_LOADED       equ        2
> 
> -LockLocation                  equ        (RendezvousFunnelProcEnd -
> RendezvousFunnelProcStart)
> +LockLocation                  equ        (SwitchToRealProcEnd -
> RendezvousFunnelProcStart)
>  StackStartAddressLocation     equ        LockLocation + 08h
>  StackSizeLocation             equ        LockLocation + 10h
>  ApProcedureLocation           equ        LockLocation + 18h
> @@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ  LockLocation +
> 98h
>  ModeHighMemoryLocation              equ  LockLocation + 9Ah
>  ModeHighSegmentLocation             equ  LockLocation + 9Eh
>  Enable5LevelPagingLocation          equ  LockLocation + 0A0h
> +SevEsIsEnabledLocation              equ  LockLocation + 0A1h
> +GhcbBaseLocation                    equ  LockLocation + 0A2h
> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> index 87f2523e856f..6956b408d004 100644
> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> @@ -184,9 +184,97 @@ Releaselock:
>      add        edi, StackStartAddressLocation
>      add        rax, qword [edi]
>      mov        rsp, rax
> +
> +    lea        edi, [esi + SevEsIsEnabledLocation]
> +    cmp        byte [edi], 1        ; SevEsIsEnabled
> +    jne        CProcedureInvoke
> +
> +    ;
> +    ; program GHCB
> +    ;   Each page after the GHCB is a per-CPU page, so the calculation
> programs
> +    ;   a GHCB to be every 8KB.
> +    ;
> +    mov        eax, SIZE_4KB
> +    shl        eax, 1                            ; EAX = SIZE_4K * 2
> +    mov        ecx, ebx
> +    mul        ecx                               ; EAX = SIZE_4K * 2 * CpuNumber
> +    mov        edi, esi
> +    add        edi, GhcbBaseLocation
> +    add        rax, qword [edi]
> +    mov        rdx, rax
> +    shr        rdx, 32
> +    mov        rcx, 0xc0010130
> +    wrmsr
>      jmp        CProcedureInvoke
> 
>  GetApicId:
> +    lea        edi, [esi + SevEsIsEnabledLocation]
> +    cmp        byte [edi], 1        ; SevEsIsEnabled
> +    jne        DoCpuid
> +
> +    ;
> +    ; Since we don't have a stack yet, we can't take a #VC
> +    ; exception. Use the GHCB protocol to perform the CPUID
> +    ; calls.
> +    ;
> +    mov        rcx, 0xc0010130
> +    rdmsr
> +    shl        rdx, 32
> +    or         rax, rdx
> +    mov        rdi, rax             ; RDI now holds the original GHCB GPA
> +
> +    mov        rdx, 0               ; CPUID function 0
> +    mov        rax, 0               ; RAX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +    cmp        edx, 0bh
> +    jb         NoX2ApicSevEs        ; CPUID level below
> CPUID_EXTENDED_TOPOLOGY
> +
> +    mov        rdx, 0bh             ; CPUID function 0x0b
> +    mov        rax, 040000000h      ; RBX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +    test       edx, 0ffffh
> +    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero
> +
> +    mov        rdx, 0bh             ; CPUID function 0x0b
> +    mov        rax, 0c0000000h      ; RDX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +
> +    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
> +    jmp        RestoreGhcb
> +
> +NoX2ApicSevEs:
> +    ; Processor is not x2APIC capable, so get 8-bit APIC ID
> +    mov        rdx, 1               ; CPUID function 1
> +    mov        rax, 040000000h      ; RBX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +    shr        edx, 24
> +
> +RestoreGhcb:
> +    mov        rbx, rdx             ; Save x2APIC/APIC ID
> +
> +    mov        rdx, rdi             ; RDI holds the saved GHCB GPA
> +    shr        rdx, 32
> +    mov        eax, edi
> +    wrmsr
> +
> +    mov        rdx, rbx
> +
> +    ; x2APIC ID or APIC ID is in EDX
> +    jmp        GetProcessorNumber
> +
> +DoCpuid:
>      mov        eax, 0
>      cpuid
>      cmp        eax, 0bh
> @@ -253,12 +341,158 @@ CProcedureInvoke:
> 
>  RendezvousFunnelProcEnd:
> 
> +;----------------------------------------------------------------------
> +---------------
> +;SwitchToRealProc procedure follows.
> +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT
> MODE.
> +HENCE THIS PROC ;IS IN MACHINE CODE.
> +;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32,
> +UINTN StackStart) ;  rcx - Buffer Start ;  rdx - Code16 Selector Offset
> +;  r8  - Code32 Selector Offset ;  r9  - Stack Start
> +;----------------------------------------------------------------------
> +---------------
> +global ASM_PFX(SwitchToRealProc)
> +ASM_PFX(SwitchToRealProc):
> +SwitchToRealProcStart:
> +BITS 64
> +    cli
> +
> +    ;
> +    ; Get RDX reset value before changing stacks since the
> +    ; new stack won't be able to accomodate a #VC exception.
> +    ;
> +    push       rax
> +    push       rbx
> +    push       rcx
> +    push       rdx
> +
> +    mov        rax, 1
> +    cpuid
> +    mov        rsi, rax                    ; Save off the reset value for RDX
> +
> +    pop        rdx
> +    pop        rcx
> +    pop        rbx
> +    pop        rax
> +
> +    ;
> +    ; Establish stack below 1MB
> +    ;
> +    mov        rsp, r9
> +
> +    ;
> +    ; Push ultimate Reset Vector onto the stack
> +    ;
> +    mov        rax, rcx
> +    shr        rax, 4
> +    push       word 0x0002                 ; RFLAGS
> +    push       ax                          ; CS
> +    push       word 0x0000                 ; RIP
> +    push       word 0x0000                 ; For alignment, will be discarded
> +
> +    ;
> +    ; Get address of "16-bit operand size" label
> +    ;
> +    lea        rbx, [PM16Mode]
> +
> +    ;
> +    ; Push addresses used to change to compatibility mode
> +    ;
> +    lea        rax, [CompatMode]
> +    push       r8
> +    push       rax
> +
> +    ;
> +    ; Clear R8 - R15, for reset, before going into 32-bit mode
> +    ;
> +    xor        r8, r8
> +    xor        r9, r9
> +    xor        r10, r10
> +    xor        r11, r11
> +    xor        r12, r12
> +    xor        r13, r13
> +    xor        r14, r14
> +    xor        r15, r15
> +
> +    ;
> +    ; Far return into 32-bit mode
> +    ;
> +o64 retf
> +
> +BITS 32
> +CompatMode:
> +    ;
> +    ; Set up stack to prepare for exiting protected mode
> +    ;
> +    push       edx                         ; Code16 CS
> +    push       ebx                         ; PM16Mode label address
> +
> +    ;
> +    ; Disable paging
> +    ;
> +    mov        eax, cr0                    ; Read CR0
> +    btr        eax, 31                     ; Set PG=0
> +    mov        cr0, eax                    ; Write CR0
> +
> +    ;
> +    ; Disable long mode
> +    ;
> +    mov        ecx, 0c0000080h             ; EFER MSR number
> +    rdmsr                                  ; Read EFER
> +    btr        eax, 8                      ; Set LME=0
> +    wrmsr                                  ; Write EFER
> +
> +    ;
> +    ; Disable PAE
> +    ;
> +    mov        eax, cr4                    ; Read CR4
> +    btr        eax, 5                      ; Set PAE=0
> +    mov        cr4, eax                    ; Write CR4
> +
> +    mov        edx, esi                    ; Restore RDX reset value
> +
> +    ;
> +    ; Switch to 16-bit operand size
> +    ;
> +    retf
> +
> +BITS 16
> +    ;
> +    ; At entry to this label
> +    ;   - RDX will have its reset value
> +    ;   - On the top of the stack
> +    ;     - Alignment data (two bytes) to be discarded
> +    ;     - IP for Real Mode (two bytes)
> +    ;     - CS for Real Mode (two bytes)
> +    ;
> +PM16Mode:
> +    mov        eax, cr0                    ; Read CR0
> +    btr        eax, 0                      ; Set PE=0
> +    mov        cr0, eax                    ; Write CR0
> +
> +    pop        ax                          ; Discard alignment data
> +
> +    ;
> +    ; Clear registers (except RDX and RSP) before going into 16-bit mode
> +    ;
> +    xor        eax, eax
> +    xor        ebx, ebx
> +    xor        ecx, ecx
> +    xor        esi, esi
> +    xor        edi, edi
> +    xor        ebp, ebp
> +
> +    iret
> +
> +SwitchToRealProcEnd:
> +
>  ;-------------------------------------------------------------------------------------
>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
> TopOfApStack, CountTofinish);
>  ;-------------------------------------------------------------------------------------
>  global ASM_PFX(AsmRelocateApLoop)
>  ASM_PFX(AsmRelocateApLoop):
>  AsmRelocateApLoopStart:
> +BITS 64
>      cli                          ; Disable interrupt before switching to 32-bit mode
>      mov        rax, [rsp + 40]   ; CountTofinish
>      lock dec   dword [rax]       ; (*CountTofinish)--
> @@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
>      mov        qword [rcx + 18h], rax
>      mov        qword [rcx + 20h], AsmRelocateApLoopEnd -
> AsmRelocateApLoopStart
>      mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
> +    mov        qword [rcx + 30h], SwitchToRealProcEnd -
> SwitchToRealProcStart          ; SwitchToRealSize
> +    mov        qword [rcx + 38h], SwitchToRealProcStart -
> RendezvousFunnelProcStart    ; SwitchToRealOffset
> +    mov        qword [rcx + 40h], SwitchToRealProcStart - Flat32Start                  ;
> SwitchToRealNoNxOffset
> +    mov        qword [rcx + 48h], PM16Mode -
> RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset
> +    mov        qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode                       ;
> SwitchToRealPM16ModeSize
>      ret
> 
>  ;-------------------------------------------------------------------------------------
> --
> 2.17.1


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

* Re: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
  2020-05-20 16:56 ` [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Lendacky, Thomas
  2020-06-01  6:17   ` Dong, Eric
@ 2020-06-01  7:28   ` Dong, Eric
  2020-06-01 16:58     ` Lendacky, Thomas
  1 sibling, 1 reply; 100+ messages in thread
From: Dong, Eric @ 2020-06-01  7:28 UTC (permalink / raw)
  To: Tom Lendacky, devel@edk2.groups.io
  Cc: Justen, Jordan L, Laszlo Ersek, Ard Biesheuvel, Kinney, Michael D,
	Gao, Liming, Ni, Ray, Brijesh Singh

Hi Tom,

> -----Original Message-----
> From: Tom Lendacky <thomas.lendacky@amd.com>
> Sent: Wednesday, May 20, 2020 5:51 AM
> To: devel@edk2.groups.io
> Cc: Justen, Jordan L <jordan.l.justen@intel.com>; Laszlo Ersek
> <lersek@redhat.com>; Ard Biesheuvel <ard.biesheuvel@linaro.org>; Kinney,
> Michael D <michael.d.kinney@intel.com>; Gao, Liming
> <liming.gao@intel.com>; Dong, Eric <eric.dong@intel.com>; Ni, Ray
> <ray.ni@intel.com>; Brijesh Singh <brijesh.singh@amd.com>
> Subject: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
> 
> Typically, an AP is booted using the INIT-SIPI-SIPI sequence. This sequence is
> intercepted by the hypervisor, which sets the AP's registers to the values
> requested by the sequence. At that point, the hypervisor can start the AP,
> which will then begin execution at the appropriate location.
> 
> Under SEV-ES, AP booting presents some challenges since the hypervisor is
> not allowed to alter the AP's register state. In this situation, we have to
> distinguish between the AP's first boot and AP's subsequent boots.
> 
> First boot:
>  Once the AP's register state has been defined (which is before the guest  is
> first booted) it cannot be altered. Should the hypervisor attempt to  alter the
> register state, the change would be detected by the hardware  and the
> VMRUN instruction would fail. Given this, the first boot for the  AP is
> required to begin execution with this initial register state, which  is typically
> the reset vector. This prevents the BSP from directing the  AP startup
> location through the INIT-SIPI-SIPI sequence.
> 
>  To work around this, the firmware will provide a build time reserved area
> that can be used as the initial IP value. The hypervisor can extract this
> location value by checking for the SEV-ES reset block GUID that must be
> located 48-bytes from the end of the firmware. The format of the SEV-ES
> reset block area is:
> 
>    0x00 - 0x01 - SEV-ES Reset IP
>    0x02 - 0x03 - SEV-ES Reset CS Segment Base[31:16]
>    0x04 - 0x05 - Size of the SEV-ES reset block
>    0x06 - 0x15 - SEV-ES Reset Block GUID
>                    (00f771de-1a7e-4fcb-890e-68c77e2fb44e)
> 
>    The total size is 22 bytes. Any expansion to this block must be done
>    by adding new values before existing values.
> 
>  The hypervisor will use the IP and CS values obtained from the SEV-ES  reset
> block to set as the AP's initial values. The CS Segment Base  represents the
> upper 16 bits of the CS segment base and must be left  shifted by 16 bits to
> form the complete CS segment base value.
> 
>  Before booting the AP for the first time, the BSP must initialize the  SEV-ES
> reset area. This consists of programming a FAR JMP instruction  to the
> contents of a memory location that is also located in the SEV-ES  reset area.
> The BSP must program the IP and CS values for the FAR JMP  based on values
> drived from the INIT-SIPI-SIPI sequence.
> 
> Subsequent boots:
>  Again, the hypervisor cannot alter the AP register state, so a method is
> required to take the AP out of halt state and redirect it to the desired  IP
> location. If it is determined that the AP is running in an SEV-ES  guest, then
> instead of calling CpuSleep(), a VMGEXIT is issued with the  AP Reset Hold
> exit code (0x80000004). The hypervisor will put the AP in  a halt state, waiting
> for an INIT-SIPI-SIPI sequence. Once the sequence  is recognized, the
> hypervisor will resume the AP. At this point the AP  must transition from the
> current 64-bit long mode down to 16-bit real  mode and begin executing at
> the derived location from the INIT-SIPI-SIPI  sequence.
> 
>  Another change is around the area of obtaining the (x2)APIC ID during AP
> startup. During AP startup, the AP can't take a #VC exception before the  AP
> has established a stack. However, the AP stack is set by using the  (x2)APIC ID,
> which is obtained through CPUID instructions. A CPUID  instruction will cause
> a #VC, so a different method must be used. The  GHCB protocol supports a
> method to obtain CPUID information from the  hypervisor through the GHCB
> MSR. This method does not require a stack,  so it is used to obtain the
> necessary CPUID information to determine the  (x2)APIC ID.
> 
> The new 16-bit protected mode GDT entry is used in order to transition from
> 64-bit long mode down to 16-bit real mode.
> 
> A new assembler routine is created that takes the AP from 64-bit long mode
> to 16-bit real mode.  This is located under 1MB in memory and transitions
> from 64-bit long mode to 32-bit compatibility mode to 16-bit protected mode
> and finally 16-bit real mode.
> 
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   3 +
>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   3 +
>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |  60 ++++
>  UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  70 +++-
>  UefiCpuPkg/Library/MpInitLib/MpLib.c          | 312 +++++++++++++++++-
>  UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |  19 ++
>  UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |   2 +-
>  UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |   2 +-
>  .../Library/MpInitLib/Ia32/MpFuncs.nasm       |  15 +
>  UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |   4 +-
>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 239 ++++++++++++++
>  11 files changed, 714 insertions(+), 15 deletions(-)
> 
> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> index 583276595619..1771575c69c1 100644
> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> @@ -52,6 +52,7 @@ [LibraryClasses]
>    DebugAgentLib
>    SynchronizationLib
>    PcdLib
> +  VmgExitLib
> 
>  [Protocols]
>    gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES
> @@ -72,4 +73,6 @@ [Pcd]
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ##
> SOMETIMES_CONSUMES
> 
> gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds
> ## CONSUMES
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ##
> CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ##
> SOMETIMES_CONSUMES
>    gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ##
> CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ##
> CONSUMES
> diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> index 4b3d39fbf36c..34abf25d43cd 100644
> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> @@ -51,6 +51,7 @@ [LibraryClasses]
>    SynchronizationLib
>    PeiServicesLib
>    PcdLib
> +  VmgExitLib
> 
>  [Pcd]
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ##
> CONSUMES
> @@ -62,6 +63,8 @@ [Pcd]
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ##
> CONSUMES
>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ##
> SOMETIMES_CONSUMES
>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ##
> SOMETIMES_CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ##
> CONSUMES
> 
>  [Ppis]
>    gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
> diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h
> b/UefiCpuPkg/Library/MpInitLib/MpLib.h
> index 5b46c295b6b2..f0cbb3763b5d 100644
> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
> @@ -173,6 +173,11 @@ typedef struct {
>    UINT8             *RelocateApLoopFuncAddress;
>    UINTN             RelocateApLoopFuncSize;
>    UINTN             ModeTransitionOffset;
> +  UINTN             SwitchToRealSize;
> +  UINTN             SwitchToRealOffset;
> +  UINTN             SwitchToRealNoNxOffset;
> +  UINTN             SwitchToRealPM16ModeOffset;
> +  UINTN             SwitchToRealPM16ModeSize;
>  } MP_ASSEMBLY_ADDRESS_MAP;
> 
>  typedef struct _CPU_MP_DATA  CPU_MP_DATA; @@ -211,6 +216,8 @@
> typedef struct {
>    // Enable5LevelPaging indicates whether 5-level paging is enabled in long
> mode.
>    //
>    BOOLEAN               Enable5LevelPaging;
> +  BOOLEAN               SevEsIsEnabled;
> +  UINTN                 GhcbBase;
>  } MP_CPU_EXCHANGE_INFO;
> 
>  #pragma pack()
> @@ -257,6 +264,7 @@ struct _CPU_MP_DATA {
>    UINT8                          ApLoopMode;
>    UINT8                          ApTargetCState;
>    UINT16                         PmCodeSegment;
> +  UINT16                         Pm16CodeSegment;
>    CPU_AP_DATA                    *CpuData;
>    volatile MP_CPU_EXCHANGE_INFO  *MpCpuExchangeInfo;
> 
> @@ -278,8 +286,47 @@ struct _CPU_MP_DATA {
>    BOOLEAN                        WakeUpByInitSipiSipi;
> 
>    BOOLEAN                        SevEsIsEnabled;
> +  UINTN                          SevEsAPBuffer;
> +  UINTN                          SevEsAPResetStackStart;
> +  CPU_MP_DATA                    *NewCpuMpData;
> +
> +  UINT64                         GhcbBase;
>  };
> 
> +#define AP_RESET_STACK_SIZE 64
> +
> +#pragma pack(1)
> +
> +typedef struct {
> +  UINT8   InsnBuffer[8];
> +  UINT16  Rip;
> +  UINT16  Segment;
> +} SEV_ES_AP_JMP_FAR;
> +
> +#pragma pack()
> +
> +/**
> +  Assembly code to move an AP from long mode to real mode.
> +
> +  Move an AP from long mode to real mode in preparation to invoking
> + the reset vector.  This is used for SEV-ES guests where a hypervisor
> + is not allowed to set the CS and RIP to point to the reset vector.
> +
> +  @param[in]  BufferStart  The reset vector target.
> +  @param[in]  Code16       16-bit protected mode code segment value.
> +  @param[in]  Code32       32-bit protected mode code segment value.
> +  @param[in]  StackStart   The start of a stack to be used for transitioning
> +                           from long mode to real mode.
> +**/
> +typedef
> +VOID
> +(EFIAPI AP_RESET) (
> +  IN UINTN    BufferStart,
> +  IN UINT16   Code16,
> +  IN UINT16   Code32,
> +  IN UINTN    StackStart
> +  );
> +
>  extern EFI_GUID mCpuInitMpLibHobGuid;
> 
>  /**
> @@ -385,6 +432,19 @@ GetModeTransitionBuffer (
>    IN UINTN                BufferSize
>    );
> 
> +/**
> +  Return the address of the SEV-ES AP jump table.
> +
> +  This buffer is required in order for an SEV-ES guest to transition
> + from  UEFI into an OS.
> +
> +  @retval other   Return SEV-ES AP jump table buffer
> +**/
> +UINTN
> +GetSevEsAPMemory (
> +  VOID
> +  );
> +
>  /**
>    This function will be called by BSP to wakeup AP.
> 
> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> index 8ccddf8e9f9c..19527300ff3a 100644
> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
> @@ -12,6 +12,8 @@
>  #include <Library/UefiBootServicesTableLib.h>
>  #include <Library/DebugAgentLib.h>
>  #include <Library/DxeServicesTableLib.h>
> +#include <Register/Amd/Fam17Msr.h>
> +#include <Register/Amd/Ghcb.h>
> 
>  #include <Protocol/Timer.h>
> 
> @@ -144,6 +146,39 @@ GetModeTransitionBuffer (
>    return (UINTN)StartAddress;
>  }
> 
> +/**
> +  Return the address of the SEV-ES AP jump table.
> +
> +  This buffer is required in order for an SEV-ES guest to transition
> + from  UEFI into an OS.
> +
> +  @retval other   Return SEV-ES AP jump table buffer
> +**/
> +UINTN
> +GetSevEsAPMemory (
> +  VOID
> +  )
> +{
> +  EFI_STATUS            Status;
> +  EFI_PHYSICAL_ADDRESS  StartAddress;
> +
> +  //
> +  // Allocate 1 page for AP jump table page  //  StartAddress =
> + BASE_4GB - 1;  Status = gBS->AllocatePages (
> +                  AllocateMaxAddress,
> +                  EfiReservedMemoryType,
> +                  1,
> +                  &StartAddress
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN)
> + StartAddress));
> +
> +  return (UINTN) StartAddress;
> +}
> +
>  /**
>    Checks APs status and updates APs status if needed.
> 
> @@ -218,6 +253,38 @@ CheckApsStatus (
>    }
>  }
> 
> +/**
> +  Get Protected mode code segment with 16-bit default addressing
> +  from current GDT table.
> +
> +  @return  Protected mode 16-bit code segment value.
> +**/
> +UINT16
> +GetProtectedMode16CS (
> +  VOID
> +  )
> +{
> +  IA32_DESCRIPTOR          GdtrDesc;
> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
> +  UINTN                    GdtEntryCount;
> +  UINT16                   Index;
> +
> +  Index = (UINT16) -1;
> +  AsmReadGdtr (&GdtrDesc);
> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
> +(IA32_SEGMENT_DESCRIPTOR);
> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
> +  for (Index = 0; Index < GdtEntryCount; Index++) {
> +    if (GdtEntry->Bits.L == 0) {
> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) {
> +        break;
> +      }
> +    }
> +    GdtEntry++;
> +  }
> +  ASSERT (Index != GdtEntryCount);
> +  return Index * 8;
> +}
> +
>  /**
>    Get Protected mode code segment from current GDT table.
> 
> @@ -238,7 +305,7 @@ GetProtectedModeCS (
>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>    for (Index = 0; Index < GdtEntryCount; Index++) {
>      if (GdtEntry->Bits.L == 0) {
> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>          break;
>        }
>      }
> @@ -300,6 +367,7 @@ MpInitChangeApLoopCallback (
> 
>    CpuMpData = GetCpuMpData ();
>    CpuMpData->PmCodeSegment = GetProtectedModeCS ();
> +  CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();
>    CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
>    mNumberToFinish = CpuMpData->CpuCount - 1;
>    WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); diff --git
> a/UefiCpuPkg/Library/MpInitLib/MpLib.c
> b/UefiCpuPkg/Library/MpInitLib/MpLib.c
> index a8b605f569bf..aeab575bb525 100644
> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
> @@ -9,6 +9,9 @@
>  **/
> 
>  #include "MpLib.h"
> +#include <Library/VmgExitLib.h>
> +#include <Register/Amd/Fam17Msr.h>
> +#include <Register/Amd/Ghcb.h>
> 
>  EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
> 
> @@ -314,6 +317,14 @@ GetApLoopMode (
>        //
>        ApLoopMode = ApInHltLoop;
>      }
> +
> +    if (PcdGetBool (PcdSevEsIsEnabled)) {
> +      //
> +      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB
> +      // protocol for starting APs
> +      //
> +      ApLoopMode = ApInHltLoop;
> +    }
>    }
> 
>    if (ApLoopMode != ApInMwaitLoop) {
> @@ -610,6 +621,112 @@ InitializeApData (
>    SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);  }
> 
> +/**
> +  Get Protected mode code segment with 16-bit default addressing
> +  from current GDT table.
> +
> +  @return  Protected mode 16-bit code segment value.
> +**/
> +STATIC
> +UINT16
> +GetProtectedMode16CS (
> +  VOID
> +  )
> +{
> +  IA32_DESCRIPTOR          GdtrDesc;
> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
> +  UINTN                    GdtEntryCount;
> +  UINT16                   Index;
> +
> +  Index = (UINT16) -1;
> +  AsmReadGdtr (&GdtrDesc);
> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
> +(IA32_SEGMENT_DESCRIPTOR);
> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
> +  for (Index = 0; Index < GdtEntryCount; Index++) {
> +    if (GdtEntry->Bits.L == 0 &&
> +        GdtEntry->Bits.DB == 0 &&
> +        GdtEntry->Bits.Type > 8) {
> +      break;
> +    }
> +    GdtEntry++;
> +  }
> +  ASSERT (Index != GdtEntryCount);
> +  return Index * 8;
> +}
> +
> +/**
> +  Get Protected mode code segment with 32-bit default addressing
> +  from current GDT table.
> +
> +  @return  Protected mode 32-bit code segment value.
> +**/
> +STATIC
> +UINT16
> +GetProtectedMode32CS (
> +  VOID
> +  )
> +{
> +  IA32_DESCRIPTOR          GdtrDesc;
> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
> +  UINTN                    GdtEntryCount;
> +  UINT16                   Index;
> +
> +  Index = (UINT16) -1;
> +  AsmReadGdtr (&GdtrDesc);
> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
> +(IA32_SEGMENT_DESCRIPTOR);
> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
> +  for (Index = 0; Index < GdtEntryCount; Index++) {
> +    if (GdtEntry->Bits.L == 0 &&
> +        GdtEntry->Bits.DB == 1 &&
> +        GdtEntry->Bits.Type > 8) {
> +      break;
> +    }
> +    GdtEntry++;
> +  }
> +  ASSERT (Index != GdtEntryCount);
> +  return Index * 8;
> +}
> +
> +/**
> +  Reset an AP when in SEV-ES mode.
> +
> +  If successful, this function never returns.
> +
> +  @param[in] Ghcb                 Pointer to the GHCB
> +  @param[in] CpuMpData            Pointer to CPU MP Data
> +
> +**/
> +STATIC
> +VOID
> +MpInitLibSevEsAPReset (
> +  IN GHCB                         *Ghcb,
> +  IN CPU_MP_DATA                  *CpuMpData
> +  )
> +{
> +  UINT16           Code16, Code32;
> +  AP_RESET         *APResetFn;
> +  UINTN            BufferStart;
> +  UINTN            StackStart;
> +
> +  Code16 = GetProtectedMode16CS ();
> +  Code32 = GetProtectedMode32CS ();
> +
> +  if (CpuMpData->WakeupBufferHigh != 0) {
> +    APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh +
> + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
> +  } else {
> +    APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo-
> >BufferStart
> + + CpuMpData->AddressMap.SwitchToRealOffset);
> +  }
> +
> +  BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
> +  StackStart = CpuMpData->SevEsAPResetStackStart -
> +                 (AP_RESET_STACK_SIZE * GetApicId ());
> +
> +  //
> +  // This call never returns.
> +  //
> +  APResetFn (BufferStart, Code16, Code32, StackStart); }
> +
>  /**
>    This function will be called from AP reset code if BSP uses WakeUpAP.
> 
> @@ -671,7 +788,14 @@ ApWakeupFunction (
>        InitializeApData (CpuMpData, ProcessorNumber, BistData,
> ApTopOfStack);
>        ApStartupSignalBuffer = CpuMpData-
> >CpuData[ProcessorNumber].StartupApSignal;
> 
> -      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo-
> >NumApsExecuting);
> +      //
> +      // Delay decrementing the APs executing count when SEV-ES is enabled
> +      // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly
> +      // performs another INIT-SIPI-SIPI sequence.
> +      //
> +      if (!CpuMpData->SevEsIsEnabled) {
> +        InterlockedDecrement ((UINT32 *) &CpuMpData-
> >MpCpuExchangeInfo->NumApsExecuting);
> +      }
>      } else {
>        //
>        // Execute AP function if AP is ready @@ -778,7 +902,52 @@
> ApWakeupFunction (
>        //
>        while (TRUE) {
>          DisableInterrupts ();
> -        CpuSleep ();
> +        if (CpuMpData->SevEsIsEnabled) {
> +          MSR_SEV_ES_GHCB_REGISTER  Msr;
> +          GHCB                      *Ghcb;
> +          UINT64                    Status;
> +          BOOLEAN                   DoDecrement;
> +
> +          if (CpuMpData->InitFlag == ApInitConfig) {
> +            DoDecrement = TRUE;
> +          }
> +
> +          while (TRUE) {
> +            Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
> +            Ghcb = Msr.Ghcb;
> +
> +            VmgInit (Ghcb);
> +
> +            if (DoDecrement) {
> +              DoDecrement = FALSE;
> +
> +              //
> +              // Perform the delayed decrement just before issuing the first
> +              // VMGEXIT with AP_RESET_HOLD.
> +              //
> +              InterlockedDecrement ((UINT32 *) &CpuMpData-
> >MpCpuExchangeInfo->NumApsExecuting);

1. Why needs to postpose the CpuMpData->MpCpuExchangeInfo->NumApsExecuting update here?

> +            }
> +
> +            Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);

2. The AP will enter Halt mode after this call? Not continue the execute of the below code? I'm not clear how the AP works for this case.

> +            if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
> +              VmgDone (Ghcb);
> +              break;
> +            }
> +
> +            VmgDone (Ghcb);
> +          }
> +
> +          //
> +          // Awakened in a new phase? Use the new CpuMpData
> +          //
> +          if (CpuMpData->NewCpuMpData) {
> +            CpuMpData = CpuMpData->NewCpuMpData;
> +          }
> +
> +          MpInitLibSevEsAPReset (Ghcb, CpuMpData);

3. With this function, this AP will be wake up and execute the new procedure? 

Thanks,
Eric

> +        } else {
> +          CpuSleep ();
> +        }
>          CpuPause ();
>        }
>      }
> @@ -891,6 +1060,9 @@ FillExchangeInfoData (
>    ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
>    DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName,
> ExchangeInfo->Enable5LevelPaging));
> 
> +  ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;
> +  ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;
> +
>    //
>    // Get the BSP's data of GDT and IDT
>    //
> @@ -917,8 +1089,9 @@ FillExchangeInfoData (
>    // EfiBootServicesCode to avoid page fault if NX memory protection is
> enabled.
>    //
>    if (CpuMpData->WakeupBufferHigh != 0) {
> -    Size = CpuMpData->AddressMap.RendezvousFunnelSize -
> -           CpuMpData->AddressMap.ModeTransitionOffset;
> +    Size = CpuMpData->AddressMap.RendezvousFunnelSize +
> +             CpuMpData->AddressMap.SwitchToRealSize -
> +             CpuMpData->AddressMap.ModeTransitionOffset;
>      CopyMem (
>        (VOID *)CpuMpData->WakeupBufferHigh,
>        CpuMpData->AddressMap.RendezvousFunnelAddress + @@ -971,7
> +1144,8 @@ BackupAndPrepareWakeupBuffer(
>    CopyMem (
>      (VOID *) CpuMpData->WakeupBuffer,
>      (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
> -    CpuMpData->AddressMap.RendezvousFunnelSize
> +    CpuMpData->AddressMap.RendezvousFunnelSize +
> +      CpuMpData->AddressMap.SwitchToRealSize
>      );
>  }
> 
> @@ -992,6 +1166,44 @@ RestoreWakeupBuffer(
>      );
>  }
> 
> +/**
> +  Calculate the size of the reset stack.
> +
> +  @retval                 Total amount of memory required for stacks
> +**/
> +STATIC
> +UINTN
> +GetApResetStackSize (
> +  VOID
> +  )
> +{
> +  return AP_RESET_STACK_SIZE *
> +PcdGet32(PcdCpuMaxLogicalProcessorNumber);
> +}
> +
> +/**
> +  Calculate the size of the reset vector.
> +
> +  @param[in]  AddressMap  The pointer to Address Map structure.
> +
> +  @retval                 Total amount of memory required for the AP reset area
> +**/
> +STATIC
> +UINTN
> +GetApResetVectorSize (
> +  IN MP_ASSEMBLY_ADDRESS_MAP  *AddressMap
> +  )
> +{
> +  UINTN  Size;
> +
> +  Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +
> +                        AddressMap->SwitchToRealSize +
> +                        sizeof (MP_CPU_EXCHANGE_INFO),
> +                      CPU_STACK_ALIGNMENT);  Size +=
> + GetApResetStackSize ();
> +
> +  return Size;
> +}
> +
>  /**
>    Allocate reset vector buffer.
> 
> @@ -1005,16 +1217,22 @@ AllocateResetVector (
>    UINTN           ApResetVectorSize;
> 
>    if (CpuMpData->WakeupBuffer == (UINTN) -1) {
> -    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
> -                          sizeof (MP_CPU_EXCHANGE_INFO);
> +    ApResetVectorSize = GetApResetVectorSize (&CpuMpData-
> >AddressMap);
> 
>      CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);
>      CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)
> (UINTN)
> -                    (CpuMpData->WakeupBuffer + CpuMpData-
> >AddressMap.RendezvousFunnelSize);
> +                    (CpuMpData->WakeupBuffer +
> +                       CpuMpData->AddressMap.RendezvousFunnelSize +
> +                       CpuMpData->AddressMap.SwitchToRealSize);
>      CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (
> -                                    CpuMpData->AddressMap.RendezvousFunnelSize -
> +                                    CpuMpData->AddressMap.RendezvousFunnelSize +
> +
> + CpuMpData->AddressMap.SwitchToRealSize -
>                                      CpuMpData->AddressMap.ModeTransitionOffset
>                                      );
> +    //
> +    // The reset stack starts at the end of the buffer.
> +    //
> +    CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer +
> + ApResetVectorSize;
>    }
>    BackupAndPrepareWakeupBuffer (CpuMpData);  } @@ -1029,7 +1247,31
> @@ FreeResetVector (
>    IN CPU_MP_DATA              *CpuMpData
>    )
>  {
> -  RestoreWakeupBuffer (CpuMpData);
> +  //
> +  // If SEV-ES is enabled, the reset area is needed for AP parking and
> +  // and AP startup in the OS, so the reset area is reserved. Do not
> +  // perform the restore as this will overwrite memory which has data
> +  // needed by SEV-ES.
> +  //
> +  if (!CpuMpData->SevEsIsEnabled) {
> +    RestoreWakeupBuffer (CpuMpData);
> +  }
> +}
> +
> +/**
> +  Allocate the SEV-ES AP jump table buffer.
> +
> +  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.
> +**/
> +VOID
> +AllocateSevEsAPMemory (
> +  IN OUT CPU_MP_DATA          *CpuMpData
> +  )
> +{
> +  if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {
> +    CpuMpData->SevEsAPBuffer =
> +      CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
> +  }
>  }
> 
>  /**
> @@ -1066,6 +1308,7 @@ WakeUpAP (
>        CpuMpData->InitFlag   != ApInitDone) {
>      ResetVectorRequired = TRUE;
>      AllocateResetVector (CpuMpData);
> +    AllocateSevEsAPMemory (CpuMpData);
>      FillExchangeInfoData (CpuMpData);
>      SaveLocalApicTimerSetting (CpuMpData);
>    }
> @@ -1102,6 +1345,50 @@ WakeUpAP (
>        }
>      }
>      if (ResetVectorRequired) {
> +      //
> +      // For SEV-ES, the initial AP boot address will be defined by
> +      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address
> +      // from the original INIT-SIPI-SIPI.
> +      //
> +      if (CpuMpData->SevEsIsEnabled) {
> +        SEV_ES_AP_JMP_FAR *JmpFar;
> +        UINT32            Offset, InsnByte;
> +        UINT8             LoNib, HiNib;
> +
> +        JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32
> (PcdSevEsWorkAreaBase);
> +        ASSERT (JmpFar != NULL);
> +
> +        //
> +        // Obtain the address of the Segment/Rip location in the workarea.
> +        // This will be set to a value derived from the SIPI vector and will
> +        // be the memory address used for the far jump below.
> +        //
> +        Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
> +        Offset += sizeof (JmpFar->InsnBuffer);
> +        LoNib = (UINT8) Offset;
> +        HiNib = (UINT8) (Offset >> 8);
> +
> +        //
> +        // Program the workarea (which is the initial AP boot address) with
> +        // far jump to the SIPI vector (where XX and YY represent the
> +        // address of where the SIPI vector is stored.
> +        //
> +        //   JMP FAR [CS:XXYY] => 2E FF 2E YY XX
> +        //
> +        InsnByte = 0;
> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // CS override prefix
> +        JmpFar->InsnBuffer[InsnByte++] = 0xFF;  // JMP (FAR)
> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // ModRM (JMP memory
> location)
> +        JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
> +        JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
> +
> +        //
> +        // Program the Segment/Rip based on the SIPI vector (always at least
> +        // 16-byte aligned, so Rip is set to 0).
> +        //
> +        JmpFar->Rip = 0;
> +        JmpFar->Segment = (UINT16) (ExchangeInfo->BufferStart >> 4);
> +      }
>        //
>        // Wakeup all APs
>        //
> @@ -1669,7 +1956,7 @@ MpInitLibInitialize (
>    ASSERT (MaxLogicalProcessorNumber != 0);
> 
>    AsmGetAddressMap (&AddressMap);
> -  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof
> (MP_CPU_EXCHANGE_INFO);
> +  ApResetVectorSize = GetApResetVectorSize (&AddressMap);
>    ApStackSize = PcdGet32(PcdCpuApStackSize);
>    ApLoopMode  = GetApLoopMode (&MonitorFilterSize);
> 
> @@ -1728,6 +2015,8 @@ MpInitLibInitialize (
>    CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData
> + MaxLogicalProcessorNumber);
>    InitializeSpinLock(&CpuMpData->MpLock);
>    CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
> +  CpuMpData->SevEsAPBuffer  = (UINTN) -1;
> +  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
> 
>    //
>    // Make sure no memory usage outside of the allocated buffer.
> @@ -1786,6 +2075,7 @@ MpInitLibInitialize (
>      // APs have been wakeup before, just get the CPU Information
>      // from HOB
>      //
> +    OldCpuMpData->NewCpuMpData = CpuMpData;
>      CpuMpData->CpuCount  = OldCpuMpData->CpuCount;
>      CpuMpData->BspNumber = OldCpuMpData->BspNumber;
>      CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; diff --git
> a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> index a548fed23fa7..e17a351e5cfd 100644
> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> @@ -280,6 +280,25 @@ GetModeTransitionBuffer (
>    return 0;
>  }
> 
> +/**
> +  Return the address of the SEV-ES AP jump table.
> +
> +  This buffer is required in order for an SEV-ES guest to transition
> + from  UEFI into an OS.
> +
> +  @retval other   Return SEV-ES AP jump table buffer
> +**/
> +UINTN
> +GetSevEsAPMemory (
> +  VOID
> +  )
> +{
> +  //
> +  // PEI phase doesn't need to do such transition. So simply return 0.
> +  //
> +  return 0;
> +}
> +
>  /**
>    Checks APs status and updates APs status if needed.
> 
> diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> index 6298571e29b2..28f8e8e133e5 100644
> --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> @@ -121,7 +121,7 @@ GetProtectedModeCS (
>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>    for (Index = 0; Index < GdtEntryCount; Index++) {
>      if (GdtEntry->Bits.L == 0) {
> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>          break;
>        }
>      }
> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> index efb1bc2bf7cb..4f5a7c859a56 100644
> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>  CPU_SWITCH_STATE_STORED       equ        1
>  CPU_SWITCH_STATE_LOADED       equ        2
> 
> -LockLocation                  equ        (RendezvousFunnelProcEnd -
> RendezvousFunnelProcStart)
> +LockLocation                  equ        (SwitchToRealProcEnd -
> RendezvousFunnelProcStart)
>  StackStartAddressLocation     equ        LockLocation + 04h
>  StackSizeLocation             equ        LockLocation + 08h
>  ApProcedureLocation           equ        LockLocation + 0Ch
> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> index b74046b76af3..309d53bf3b37 100644
> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> @@ -215,6 +215,16 @@ CProcedureInvoke:
>      jmp        $                 ; Never reach here
>  RendezvousFunnelProcEnd:
> 
> +;----------------------------------------------------------------------
> +---------------
> +;SwitchToRealProc procedure follows.
> +;NOT USED IN 32 BIT MODE.
> +;----------------------------------------------------------------------
> +---------------
> +global ASM_PFX(SwitchToRealProc)
> +ASM_PFX(SwitchToRealProc):
> +SwitchToRealProcStart:
> +    jmp        $                 ; Never reach here
> +SwitchToRealProcEnd:
> +
>  ;-------------------------------------------------------------------------------------
>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
> TopOfApStack, CountTofinish);
>  ;-------------------------------------------------------------------------------------
> @@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
>      mov        dword [ebx + 0Ch], AsmRelocateApLoopStart
>      mov        dword [ebx + 10h], AsmRelocateApLoopEnd -
> AsmRelocateApLoopStart
>      mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
> +    mov        dword [ebx + 18h], SwitchToRealProcEnd -
> SwitchToRealProcStart       ; SwitchToRealSize
> +    mov        dword [ebx + 1Ch], SwitchToRealProcStart -
> RendezvousFunnelProcStart ; SwitchToRealOffset
> +    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start               ;
> SwitchToRealNoNxOffset
> +    mov        dword [ebx + 24h], 0                                                 ;
> SwitchToRealPM16ModeOffset
> +    mov        dword [ebx + 28h], 0                                                 ;
> SwitchToRealPM16ModeSize
> 
>      popad
>      ret
> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> index 58ef369342a7..c92daaaffd6b 100644
> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>  CPU_SWITCH_STATE_STORED       equ        1
>  CPU_SWITCH_STATE_LOADED       equ        2
> 
> -LockLocation                  equ        (RendezvousFunnelProcEnd -
> RendezvousFunnelProcStart)
> +LockLocation                  equ        (SwitchToRealProcEnd -
> RendezvousFunnelProcStart)
>  StackStartAddressLocation     equ        LockLocation + 08h
>  StackSizeLocation             equ        LockLocation + 10h
>  ApProcedureLocation           equ        LockLocation + 18h
> @@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ  LockLocation +
> 98h
>  ModeHighMemoryLocation              equ  LockLocation + 9Ah
>  ModeHighSegmentLocation             equ  LockLocation + 9Eh
>  Enable5LevelPagingLocation          equ  LockLocation + 0A0h
> +SevEsIsEnabledLocation              equ  LockLocation + 0A1h
> +GhcbBaseLocation                    equ  LockLocation + 0A2h
> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> index 87f2523e856f..6956b408d004 100644
> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> @@ -184,9 +184,97 @@ Releaselock:
>      add        edi, StackStartAddressLocation
>      add        rax, qword [edi]
>      mov        rsp, rax
> +
> +    lea        edi, [esi + SevEsIsEnabledLocation]
> +    cmp        byte [edi], 1        ; SevEsIsEnabled
> +    jne        CProcedureInvoke
> +
> +    ;
> +    ; program GHCB
> +    ;   Each page after the GHCB is a per-CPU page, so the calculation
> programs
> +    ;   a GHCB to be every 8KB.
> +    ;
> +    mov        eax, SIZE_4KB
> +    shl        eax, 1                            ; EAX = SIZE_4K * 2
> +    mov        ecx, ebx
> +    mul        ecx                               ; EAX = SIZE_4K * 2 * CpuNumber
> +    mov        edi, esi
> +    add        edi, GhcbBaseLocation
> +    add        rax, qword [edi]
> +    mov        rdx, rax
> +    shr        rdx, 32
> +    mov        rcx, 0xc0010130
> +    wrmsr
>      jmp        CProcedureInvoke
> 
>  GetApicId:
> +    lea        edi, [esi + SevEsIsEnabledLocation]
> +    cmp        byte [edi], 1        ; SevEsIsEnabled
> +    jne        DoCpuid
> +
> +    ;
> +    ; Since we don't have a stack yet, we can't take a #VC
> +    ; exception. Use the GHCB protocol to perform the CPUID
> +    ; calls.
> +    ;
> +    mov        rcx, 0xc0010130
> +    rdmsr
> +    shl        rdx, 32
> +    or         rax, rdx
> +    mov        rdi, rax             ; RDI now holds the original GHCB GPA
> +
> +    mov        rdx, 0               ; CPUID function 0
> +    mov        rax, 0               ; RAX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +    cmp        edx, 0bh
> +    jb         NoX2ApicSevEs        ; CPUID level below
> CPUID_EXTENDED_TOPOLOGY
> +
> +    mov        rdx, 0bh             ; CPUID function 0x0b
> +    mov        rax, 040000000h      ; RBX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +    test       edx, 0ffffh
> +    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero
> +
> +    mov        rdx, 0bh             ; CPUID function 0x0b
> +    mov        rax, 0c0000000h      ; RDX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +
> +    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
> +    jmp        RestoreGhcb
> +
> +NoX2ApicSevEs:
> +    ; Processor is not x2APIC capable, so get 8-bit APIC ID
> +    mov        rdx, 1               ; CPUID function 1
> +    mov        rax, 040000000h      ; RBX register requested
> +    or         rax, 4
> +    wrmsr
> +    rep vmmcall
> +    rdmsr
> +    shr        edx, 24
> +
> +RestoreGhcb:
> +    mov        rbx, rdx             ; Save x2APIC/APIC ID
> +
> +    mov        rdx, rdi             ; RDI holds the saved GHCB GPA
> +    shr        rdx, 32
> +    mov        eax, edi
> +    wrmsr
> +
> +    mov        rdx, rbx
> +
> +    ; x2APIC ID or APIC ID is in EDX
> +    jmp        GetProcessorNumber
> +
> +DoCpuid:
>      mov        eax, 0
>      cpuid
>      cmp        eax, 0bh
> @@ -253,12 +341,158 @@ CProcedureInvoke:
> 
>  RendezvousFunnelProcEnd:
> 
> +;----------------------------------------------------------------------
> +---------------
> +;SwitchToRealProc procedure follows.
> +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT
> MODE.
> +HENCE THIS PROC ;IS IN MACHINE CODE.
> +;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32,
> +UINTN StackStart) ;  rcx - Buffer Start ;  rdx - Code16 Selector Offset
> +;  r8  - Code32 Selector Offset ;  r9  - Stack Start
> +;----------------------------------------------------------------------
> +---------------
> +global ASM_PFX(SwitchToRealProc)
> +ASM_PFX(SwitchToRealProc):
> +SwitchToRealProcStart:
> +BITS 64
> +    cli
> +
> +    ;
> +    ; Get RDX reset value before changing stacks since the
> +    ; new stack won't be able to accomodate a #VC exception.
> +    ;
> +    push       rax
> +    push       rbx
> +    push       rcx
> +    push       rdx
> +
> +    mov        rax, 1
> +    cpuid
> +    mov        rsi, rax                    ; Save off the reset value for RDX
> +
> +    pop        rdx
> +    pop        rcx
> +    pop        rbx
> +    pop        rax
> +
> +    ;
> +    ; Establish stack below 1MB
> +    ;
> +    mov        rsp, r9
> +
> +    ;
> +    ; Push ultimate Reset Vector onto the stack
> +    ;
> +    mov        rax, rcx
> +    shr        rax, 4
> +    push       word 0x0002                 ; RFLAGS
> +    push       ax                          ; CS
> +    push       word 0x0000                 ; RIP
> +    push       word 0x0000                 ; For alignment, will be discarded
> +
> +    ;
> +    ; Get address of "16-bit operand size" label
> +    ;
> +    lea        rbx, [PM16Mode]
> +
> +    ;
> +    ; Push addresses used to change to compatibility mode
> +    ;
> +    lea        rax, [CompatMode]
> +    push       r8
> +    push       rax
> +
> +    ;
> +    ; Clear R8 - R15, for reset, before going into 32-bit mode
> +    ;
> +    xor        r8, r8
> +    xor        r9, r9
> +    xor        r10, r10
> +    xor        r11, r11
> +    xor        r12, r12
> +    xor        r13, r13
> +    xor        r14, r14
> +    xor        r15, r15
> +
> +    ;
> +    ; Far return into 32-bit mode
> +    ;
> +o64 retf
> +
> +BITS 32
> +CompatMode:
> +    ;
> +    ; Set up stack to prepare for exiting protected mode
> +    ;
> +    push       edx                         ; Code16 CS
> +    push       ebx                         ; PM16Mode label address
> +
> +    ;
> +    ; Disable paging
> +    ;
> +    mov        eax, cr0                    ; Read CR0
> +    btr        eax, 31                     ; Set PG=0
> +    mov        cr0, eax                    ; Write CR0
> +
> +    ;
> +    ; Disable long mode
> +    ;
> +    mov        ecx, 0c0000080h             ; EFER MSR number
> +    rdmsr                                  ; Read EFER
> +    btr        eax, 8                      ; Set LME=0
> +    wrmsr                                  ; Write EFER
> +
> +    ;
> +    ; Disable PAE
> +    ;
> +    mov        eax, cr4                    ; Read CR4
> +    btr        eax, 5                      ; Set PAE=0
> +    mov        cr4, eax                    ; Write CR4
> +
> +    mov        edx, esi                    ; Restore RDX reset value
> +
> +    ;
> +    ; Switch to 16-bit operand size
> +    ;
> +    retf
> +
> +BITS 16
> +    ;
> +    ; At entry to this label
> +    ;   - RDX will have its reset value
> +    ;   - On the top of the stack
> +    ;     - Alignment data (two bytes) to be discarded
> +    ;     - IP for Real Mode (two bytes)
> +    ;     - CS for Real Mode (two bytes)
> +    ;
> +PM16Mode:
> +    mov        eax, cr0                    ; Read CR0
> +    btr        eax, 0                      ; Set PE=0
> +    mov        cr0, eax                    ; Write CR0
> +
> +    pop        ax                          ; Discard alignment data
> +
> +    ;
> +    ; Clear registers (except RDX and RSP) before going into 16-bit mode
> +    ;
> +    xor        eax, eax
> +    xor        ebx, ebx
> +    xor        ecx, ecx
> +    xor        esi, esi
> +    xor        edi, edi
> +    xor        ebp, ebp
> +
> +    iret
> +
> +SwitchToRealProcEnd:
> +
>  ;-------------------------------------------------------------------------------------
>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
> TopOfApStack, CountTofinish);
>  ;-------------------------------------------------------------------------------------
>  global ASM_PFX(AsmRelocateApLoop)
>  ASM_PFX(AsmRelocateApLoop):
>  AsmRelocateApLoopStart:
> +BITS 64
>      cli                          ; Disable interrupt before switching to 32-bit mode
>      mov        rax, [rsp + 40]   ; CountTofinish
>      lock dec   dword [rax]       ; (*CountTofinish)--
> @@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
>      mov        qword [rcx + 18h], rax
>      mov        qword [rcx + 20h], AsmRelocateApLoopEnd -
> AsmRelocateApLoopStart
>      mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
> +    mov        qword [rcx + 30h], SwitchToRealProcEnd -
> SwitchToRealProcStart          ; SwitchToRealSize
> +    mov        qword [rcx + 38h], SwitchToRealProcStart -
> RendezvousFunnelProcStart    ; SwitchToRealOffset
> +    mov        qword [rcx + 40h], SwitchToRealProcStart - Flat32Start                  ;
> SwitchToRealNoNxOffset
> +    mov        qword [rcx + 48h], PM16Mode -
> RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset
> +    mov        qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode                       ;
> SwitchToRealPM16ModeSize
>      ret
> 
>  ;-------------------------------------------------------------------------------------
> --
> 2.17.1


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

* Re: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
  2020-06-01  6:17   ` Dong, Eric
@ 2020-06-01 16:10     ` Lendacky, Thomas
  2020-06-05  6:13       ` Dong, Eric
  0 siblings, 1 reply; 100+ messages in thread
From: Lendacky, Thomas @ 2020-06-01 16:10 UTC (permalink / raw)
  To: Dong, Eric, devel@edk2.groups.io
  Cc: Justen, Jordan L, Laszlo Ersek, Ard Biesheuvel, Kinney, Michael D,
	Gao, Liming, Ni, Ray, Brijesh Singh

On 6/1/20 1:17 AM, Dong, Eric wrote:
> Hi Tom,

Hi Eric,

> 
>> -----Original Message-----
>> From: Tom Lendacky <thomas.lendacky@amd.com>
>> Sent: Wednesday, May 20, 2020 5:51 AM
>> To: devel@edk2.groups.io
>> Cc: Justen, Jordan L <jordan.l.justen@intel.com>; Laszlo Ersek
>> <lersek@redhat.com>; Ard Biesheuvel <ard.biesheuvel@linaro.org>; Kinney,
>> Michael D <michael.d.kinney@intel.com>; Gao, Liming
>> <liming.gao@intel.com>; Dong, Eric <eric.dong@intel.com>; Ni, Ray
>> <ray.ni@intel.com>; Brijesh Singh <brijesh.singh@amd.com>
>> Subject: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
>>
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7C7f0288f6b6964c30b79808d805f3719d%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637265890432557809&amp;sdata=t8Gz8tXGoSpO3N5BDGCyRp%2FqH3PF6xitfIzV27rGSe0%3D&amp;reserved=0
>>
>> Typically, an AP is booted using the INIT-SIPI-SIPI sequence. This sequence is
>> intercepted by the hypervisor, which sets the AP's registers to the values
>> requested by the sequence. At that point, the hypervisor can start the AP,
>> which will then begin execution at the appropriate location.
>>
>> Under SEV-ES, AP booting presents some challenges since the hypervisor is
>> not allowed to alter the AP's register state. In this situation, we have to
>> distinguish between the AP's first boot and AP's subsequent boots.
>>
>> First boot:
>>  Once the AP's register state has been defined (which is before the guest  is
>> first booted) it cannot be altered. Should the hypervisor attempt to  alter the
>> register state, the change would be detected by the hardware  and the
>> VMRUN instruction would fail. Given this, the first boot for the  AP is
>> required to begin execution with this initial register state, which  is typically
>> the reset vector. This prevents the BSP from directing the  AP startup
>> location through the INIT-SIPI-SIPI sequence.
>>
>>  To work around this, the firmware will provide a build time reserved area
>> that can be used as the initial IP value. The hypervisor can extract this
>> location value by checking for the SEV-ES reset block GUID that must be
>> located 48-bytes from the end of the firmware. The format of the SEV-ES
>> reset block area is:
>>
>>    0x00 - 0x01 - SEV-ES Reset IP
>>    0x02 - 0x03 - SEV-ES Reset CS Segment Base[31:16]
>>    0x04 - 0x05 - Size of the SEV-ES reset block
>>    0x06 - 0x15 - SEV-ES Reset Block GUID
>>                    (00f771de-1a7e-4fcb-890e-68c77e2fb44e)
>>
>>    The total size is 22 bytes. Any expansion to this block must be done
>>    by adding new values before existing values.
>>
>>  The hypervisor will use the IP and CS values obtained from the SEV-ES  reset
>> block to set as the AP's initial values. The CS Segment Base  represents the
>> upper 16 bits of the CS segment base and must be left  shifted by 16 bits to
>> form the complete CS segment base value.
>>
>>  Before booting the AP for the first time, the BSP must initialize the  SEV-ES
>> reset area. This consists of programming a FAR JMP instruction  to the
>> contents of a memory location that is also located in the SEV-ES  reset area.
>> The BSP must program the IP and CS values for the FAR JMP  based on values
>> drived from the INIT-SIPI-SIPI sequence.
>>
>> Subsequent boots:
>>  Again, the hypervisor cannot alter the AP register state, so a method is
>> required to take the AP out of halt state and redirect it to the desired  IP
>> location. If it is determined that the AP is running in an SEV-ES  guest, then
>> instead of calling CpuSleep(), a VMGEXIT is issued with the  AP Reset Hold
>> exit code (0x80000004). The hypervisor will put the AP in  a halt state, waiting
>> for an INIT-SIPI-SIPI sequence. Once the sequence  is recognized, the
>> hypervisor will resume the AP. At this point the AP  must transition from the
>> current 64-bit long mode down to 16-bit real  mode and begin executing at
>> the derived location from the INIT-SIPI-SIPI  sequence.
>>
>>  Another change is around the area of obtaining the (x2)APIC ID during AP
>> startup. During AP startup, the AP can't take a #VC exception before the  AP
>> has established a stack. However, the AP stack is set by using the  (x2)APIC ID,
>> which is obtained through CPUID instructions. A CPUID  instruction will cause
>> a #VC, so a different method must be used. The  GHCB protocol supports a
>> method to obtain CPUID information from the  hypervisor through the GHCB
>> MSR. This method does not require a stack,  so it is used to obtain the
>> necessary CPUID information to determine the  (x2)APIC ID.
>>
>> The new 16-bit protected mode GDT entry is used in order to transition from
>> 64-bit long mode down to 16-bit real mode.
>>
>> A new assembler routine is created that takes the AP from 64-bit long mode
>> to 16-bit real mode.  This is located under 1MB in memory and transitions
>> from 64-bit long mode to 32-bit compatibility mode to 16-bit protected mode
>> and finally 16-bit real mode.
>>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Ray Ni <ray.ni@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   3 +
>>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   3 +
>>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |  60 ++++
>>  UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  70 +++-
>>  UefiCpuPkg/Library/MpInitLib/MpLib.c          | 312 +++++++++++++++++-
>>  UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |  19 ++
>>  UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |   2 +-
>>  UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |   2 +-
>>  .../Library/MpInitLib/Ia32/MpFuncs.nasm       |  15 +
>>  UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |   4 +-
>>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 239 ++++++++++++++
>>  11 files changed, 714 insertions(+), 15 deletions(-)
>>
>> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> index 583276595619..1771575c69c1 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> @@ -52,6 +52,7 @@ [LibraryClasses]
>>    DebugAgentLib
>>    SynchronizationLib
>>    PcdLib
>> +  VmgExitLib
>>
>>  [Protocols]
>>    gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES
>> @@ -72,4 +73,6 @@ [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ##
>> SOMETIMES_CONSUMES
>>
>> gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds
>> ## CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ##
>> CONSUMES
>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ##
>> SOMETIMES_CONSUMES
>>    gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ##
>> CONSUMES
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ##
>> CONSUMES
>> diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> index 4b3d39fbf36c..34abf25d43cd 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> @@ -51,6 +51,7 @@ [LibraryClasses]
>>    SynchronizationLib
>>    PeiServicesLib
>>    PcdLib
>> +  VmgExitLib
>>
>>  [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ##
>> CONSUMES
>> @@ -62,6 +63,8 @@ [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ##
>> CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ##
>> SOMETIMES_CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES
>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ##
>> SOMETIMES_CONSUMES
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ##
>> CONSUMES
>>
>>  [Ppis]
>>    gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
>> diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> b/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> index 5b46c295b6b2..f0cbb3763b5d 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> @@ -173,6 +173,11 @@ typedef struct {
>>    UINT8             *RelocateApLoopFuncAddress;
>>    UINTN             RelocateApLoopFuncSize;
>>    UINTN             ModeTransitionOffset;
>> +  UINTN             SwitchToRealSize;
>> +  UINTN             SwitchToRealOffset;
>> +  UINTN             SwitchToRealNoNxOffset;
>> +  UINTN             SwitchToRealPM16ModeOffset;
>> +  UINTN             SwitchToRealPM16ModeSize;
>>  } MP_ASSEMBLY_ADDRESS_MAP;
>>
>>  typedef struct _CPU_MP_DATA  CPU_MP_DATA; @@ -211,6 +216,8 @@
>> typedef struct {
>>    // Enable5LevelPaging indicates whether 5-level paging is enabled in long
>> mode.
>>    //
>>    BOOLEAN               Enable5LevelPaging;
>> +  BOOLEAN               SevEsIsEnabled;
>> +  UINTN                 GhcbBase;
>>  } MP_CPU_EXCHANGE_INFO;
>>
>>  #pragma pack()
>> @@ -257,6 +264,7 @@ struct _CPU_MP_DATA {
>>    UINT8                          ApLoopMode;
>>    UINT8                          ApTargetCState;
>>    UINT16                         PmCodeSegment;
>> +  UINT16                         Pm16CodeSegment;
>>    CPU_AP_DATA                    *CpuData;
>>    volatile MP_CPU_EXCHANGE_INFO  *MpCpuExchangeInfo;
>>
>> @@ -278,8 +286,47 @@ struct _CPU_MP_DATA {
>>    BOOLEAN                        WakeUpByInitSipiSipi;
>>
>>    BOOLEAN                        SevEsIsEnabled;
>> +  UINTN                          SevEsAPBuffer;
>> +  UINTN                          SevEsAPResetStackStart;
>> +  CPU_MP_DATA                    *NewCpuMpData;
>> +
>> +  UINT64                         GhcbBase;
>>  };
>>
>> +#define AP_RESET_STACK_SIZE 64
>> +
>> +#pragma pack(1)
>> +
>> +typedef struct {
>> +  UINT8   InsnBuffer[8];
>> +  UINT16  Rip;
>> +  UINT16  Segment;
>> +} SEV_ES_AP_JMP_FAR;
>> +
>> +#pragma pack()
>> +
>> +/**
>> +  Assembly code to move an AP from long mode to real mode.
>> +
>> +  Move an AP from long mode to real mode in preparation to invoking
>> + the reset vector.  This is used for SEV-ES guests where a hypervisor
>> + is not allowed to set the CS and RIP to point to the reset vector.
>> +
>> +  @param[in]  BufferStart  The reset vector target.
>> +  @param[in]  Code16       16-bit protected mode code segment value.
>> +  @param[in]  Code32       32-bit protected mode code segment value.
>> +  @param[in]  StackStart   The start of a stack to be used for transitioning
>> +                           from long mode to real mode.
>> +**/
>> +typedef
>> +VOID
>> +(EFIAPI AP_RESET) (
>> +  IN UINTN    BufferStart,
>> +  IN UINT16   Code16,
>> +  IN UINT16   Code32,
>> +  IN UINTN    StackStart
>> +  );
>> +
>>  extern EFI_GUID mCpuInitMpLibHobGuid;
>>
>>  /**
>> @@ -385,6 +432,19 @@ GetModeTransitionBuffer (
>>    IN UINTN                BufferSize
>>    );
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  );
>> +
>>  /**
>>    This function will be called by BSP to wakeup AP.
>>
>> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> index 8ccddf8e9f9c..19527300ff3a 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> @@ -12,6 +12,8 @@
>>  #include <Library/UefiBootServicesTableLib.h>
>>  #include <Library/DebugAgentLib.h>
>>  #include <Library/DxeServicesTableLib.h>
>> +#include <Register/Amd/Fam17Msr.h>
>> +#include <Register/Amd/Ghcb.h>
>>
>>  #include <Protocol/Timer.h>
>>
>> @@ -144,6 +146,39 @@ GetModeTransitionBuffer (
>>    return (UINTN)StartAddress;
>>  }
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS            Status;
>> +  EFI_PHYSICAL_ADDRESS  StartAddress;
>> +
>> +  //
>> +  // Allocate 1 page for AP jump table page  //  StartAddress =
>> + BASE_4GB - 1;  Status = gBS->AllocatePages (
>> +                  AllocateMaxAddress,
>> +                  EfiReservedMemoryType,
>> +                  1,
>> +                  &StartAddress
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN)
>> + StartAddress));
>> +
>> +  return (UINTN) StartAddress;
>> +}
>> +
>>  /**
>>    Checks APs status and updates APs status if needed.
>>
>> @@ -218,6 +253,38 @@ CheckApsStatus (
>>    }
>>  }
>>
>> +/**
>> +  Get Protected mode code segment with 16-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 16-bit code segment value.
>> +**/
>> +UINT16
>> +GetProtectedMode16CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) {
>> +        break;
>> +      }
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>>  /**
>>    Get Protected mode code segment from current GDT table.
>>
>> @@ -238,7 +305,7 @@ GetProtectedModeCS (
>>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>>    for (Index = 0; Index < GdtEntryCount; Index++) {
>>      if (GdtEntry->Bits.L == 0) {
>> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>>          break;
>>        }
>>      }
>> @@ -300,6 +367,7 @@ MpInitChangeApLoopCallback (
>>
>>    CpuMpData = GetCpuMpData ();
>>    CpuMpData->PmCodeSegment = GetProtectedModeCS ();
>> +  CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();
>>    CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
>>    mNumberToFinish = CpuMpData->CpuCount - 1;
>>    WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); diff --git
>> a/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> index a8b605f569bf..aeab575bb525 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> @@ -9,6 +9,9 @@
>>  **/
>>
>>  #include "MpLib.h"
>> +#include <Library/VmgExitLib.h>
>> +#include <Register/Amd/Fam17Msr.h>
>> +#include <Register/Amd/Ghcb.h>
>>
>>  EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
>>
>> @@ -314,6 +317,14 @@ GetApLoopMode (
>>        //
>>        ApLoopMode = ApInHltLoop;
>>      }
>> +
>> +    if (PcdGetBool (PcdSevEsIsEnabled)) {
>> +      //
>> +      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB
>> +      // protocol for starting APs
>> +      //
>> +      ApLoopMode = ApInHltLoop;
>> +    }
>>    }
>>
>>    if (ApLoopMode != ApInMwaitLoop) {
>> @@ -610,6 +621,112 @@ InitializeApData (
>>    SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);  }
>>
>> +/**
>> +  Get Protected mode code segment with 16-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 16-bit code segment value.
>> +**/
>> +STATIC
>> +UINT16
>> +GetProtectedMode16CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0 &&
>> +        GdtEntry->Bits.DB == 0 &&
>> +        GdtEntry->Bits.Type > 8) {
>> +      break;
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>> +/**
>> +  Get Protected mode code segment with 32-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 32-bit code segment value.
>> +**/
>> +STATIC
>> +UINT16
>> +GetProtectedMode32CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0 &&
>> +        GdtEntry->Bits.DB == 1 &&
>> +        GdtEntry->Bits.Type > 8) {
>> +      break;
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>> +/**
>> +  Reset an AP when in SEV-ES mode.
>> +
>> +  If successful, this function never returns.
>> +
>> +  @param[in] Ghcb                 Pointer to the GHCB
>> +  @param[in] CpuMpData            Pointer to CPU MP Data
>> +
>> +**/
>> +STATIC
>> +VOID
>> +MpInitLibSevEsAPReset (
>> +  IN GHCB                         *Ghcb,
>> +  IN CPU_MP_DATA                  *CpuMpData
>> +  )
>> +{
>> +  UINT16           Code16, Code32;
>> +  AP_RESET         *APResetFn;
>> +  UINTN            BufferStart;
>> +  UINTN            StackStart;
>> +
>> +  Code16 = GetProtectedMode16CS ();
>> +  Code32 = GetProtectedMode32CS ();
>> +
>> +  if (CpuMpData->WakeupBufferHigh != 0) {
>> +    APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh +
>> + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
>> +  } else {
>> +    APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo-
>>> BufferStart
>> + + CpuMpData->AddressMap.SwitchToRealOffset);
>> +  }
>> +
>> +  BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
>> +  StackStart = CpuMpData->SevEsAPResetStackStart -
>> +                 (AP_RESET_STACK_SIZE * GetApicId ());
>> +
>> +  //
>> +  // This call never returns.
>> +  //
>> +  APResetFn (BufferStart, Code16, Code32, StackStart); }
>> +
>>  /**
>>    This function will be called from AP reset code if BSP uses WakeUpAP.
>>
>> @@ -671,7 +788,14 @@ ApWakeupFunction (
>>        InitializeApData (CpuMpData, ProcessorNumber, BistData,
>> ApTopOfStack);
>>        ApStartupSignalBuffer = CpuMpData-
>>> CpuData[ProcessorNumber].StartupApSignal;
>>
>> -      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo-
>>> NumApsExecuting);
>> +      //
>> +      // Delay decrementing the APs executing count when SEV-ES is enabled
>> +      // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly
>> +      // performs another INIT-SIPI-SIPI sequence.
>> +      //
>> +      if (!CpuMpData->SevEsIsEnabled) {
>> +        InterlockedDecrement ((UINT32 *) &CpuMpData-
>>> MpCpuExchangeInfo->NumApsExecuting);
>> +      }
>>      } else {
>>        //
>>        // Execute AP function if AP is ready @@ -778,7 +902,52 @@
>> ApWakeupFunction (
>>        //
>>        while (TRUE) {
>>          DisableInterrupts ();
>> -        CpuSleep ();
>> +        if (CpuMpData->SevEsIsEnabled) {
>> +          MSR_SEV_ES_GHCB_REGISTER  Msr;
>> +          GHCB                      *Ghcb;
>> +          UINT64                    Status;
>> +          BOOLEAN                   DoDecrement;
>> +
>> +          if (CpuMpData->InitFlag == ApInitConfig) {
>> +            DoDecrement = TRUE;
>> +          }
>> +
>> +          while (TRUE) {
>> +            Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
>> +            Ghcb = Msr.Ghcb;
>> +
>> +            VmgInit (Ghcb);
>> +
>> +            if (DoDecrement) {
>> +              DoDecrement = FALSE;
>> +
>> +              //
>> +              // Perform the delayed decrement just before issuing the first
>> +              // VMGEXIT with AP_RESET_HOLD.
>> +              //
>> +              InterlockedDecrement ((UINT32 *) &CpuMpData-
>>> MpCpuExchangeInfo->NumApsExecuting);
>> +            }
>> +
>> +            Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
>> +            if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
>> +              VmgDone (Ghcb);
>> +              break;
>> +            }
>> +
>> +            VmgDone (Ghcb);
>> +          }
>> +
>> +          //
>> +          // Awakened in a new phase? Use the new CpuMpData
>> +          //
>> +          if (CpuMpData->NewCpuMpData) {
>> +            CpuMpData = CpuMpData->NewCpuMpData;
>> +          }
>> +
>> +          MpInitLibSevEsAPReset (Ghcb, CpuMpData);
>> +        } else {
>> +          CpuSleep ();
>> +        }
>>          CpuPause ();
>>        }
>>      }
>> @@ -891,6 +1060,9 @@ FillExchangeInfoData (
>>    ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
>>    DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName,
>> ExchangeInfo->Enable5LevelPaging));
>>
>> +  ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;
>> +  ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;
>> +
>>    //
>>    // Get the BSP's data of GDT and IDT
>>    //
>> @@ -917,8 +1089,9 @@ FillExchangeInfoData (
>>    // EfiBootServicesCode to avoid page fault if NX memory protection is
>> enabled.
>>    //
>>    if (CpuMpData->WakeupBufferHigh != 0) {
>> -    Size = CpuMpData->AddressMap.RendezvousFunnelSize -
>> -           CpuMpData->AddressMap.ModeTransitionOffset;
>> +    Size = CpuMpData->AddressMap.RendezvousFunnelSize +
>> +             CpuMpData->AddressMap.SwitchToRealSize -
>> +             CpuMpData->AddressMap.ModeTransitionOffset;
>>      CopyMem (
>>        (VOID *)CpuMpData->WakeupBufferHigh,
>>        CpuMpData->AddressMap.RendezvousFunnelAddress + @@ -971,7
>> +1144,8 @@ BackupAndPrepareWakeupBuffer(
>>    CopyMem (
>>      (VOID *) CpuMpData->WakeupBuffer,
>>      (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
>> -    CpuMpData->AddressMap.RendezvousFunnelSize
>> +    CpuMpData->AddressMap.RendezvousFunnelSize +
>> +      CpuMpData->AddressMap.SwitchToRealSize
>>      );
>>  }
>>
>> @@ -992,6 +1166,44 @@ RestoreWakeupBuffer(
>>      );
>>  }
>>
>> +/**
>> +  Calculate the size of the reset stack.
>> +
>> +  @retval                 Total amount of memory required for stacks
>> +**/
>> +STATIC
>> +UINTN
>> +GetApResetStackSize (
>> +  VOID
>> +  )
>> +{
>> +  return AP_RESET_STACK_SIZE *
>> +PcdGet32(PcdCpuMaxLogicalProcessorNumber);
>> +}
>> +
>> +/**
>> +  Calculate the size of the reset vector.
>> +
>> +  @param[in]  AddressMap  The pointer to Address Map structure.
>> +
>> +  @retval                 Total amount of memory required for the AP reset area
>> +**/
>> +STATIC
>> +UINTN
>> +GetApResetVectorSize (
>> +  IN MP_ASSEMBLY_ADDRESS_MAP  *AddressMap
>> +  )
>> +{
>> +  UINTN  Size;
>> +
>> +  Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +
>> +                        AddressMap->SwitchToRealSize +
>> +                        sizeof (MP_CPU_EXCHANGE_INFO),
>> +                      CPU_STACK_ALIGNMENT);  Size +=
>> + GetApResetStackSize ();
>> +
>> +  return Size;
>> +}
>> +
>>  /**
>>    Allocate reset vector buffer.
>>
>> @@ -1005,16 +1217,22 @@ AllocateResetVector (
>>    UINTN           ApResetVectorSize;
>>
>>    if (CpuMpData->WakeupBuffer == (UINTN) -1) {
>> -    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
>> -                          sizeof (MP_CPU_EXCHANGE_INFO);
>> +    ApResetVectorSize = GetApResetVectorSize (&CpuMpData-
>>> AddressMap);
>>
>>      CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);
>>      CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)
>> (UINTN)
>> -                    (CpuMpData->WakeupBuffer + CpuMpData-
>>> AddressMap.RendezvousFunnelSize);
>> +                    (CpuMpData->WakeupBuffer +
>> +                       CpuMpData->AddressMap.RendezvousFunnelSize +
>> +                       CpuMpData->AddressMap.SwitchToRealSize);
>>      CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (
>> -                                    CpuMpData->AddressMap.RendezvousFunnelSize -
>> +                                    CpuMpData->AddressMap.RendezvousFunnelSize +
>> +
>> + CpuMpData->AddressMap.SwitchToRealSize -
>>                                      CpuMpData->AddressMap.ModeTransitionOffset
>>                                      );
>> +    //
>> +    // The reset stack starts at the end of the buffer.
>> +    //
>> +    CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer +
>> + ApResetVectorSize;
>>    }
>>    BackupAndPrepareWakeupBuffer (CpuMpData);  } @@ -1029,7 +1247,31
>> @@ FreeResetVector (
>>    IN CPU_MP_DATA              *CpuMpData
>>    )
>>  {
>> -  RestoreWakeupBuffer (CpuMpData);
>> +  //
>> +  // If SEV-ES is enabled, the reset area is needed for AP parking and
>> +  // and AP startup in the OS, so the reset area is reserved. Do not
>> +  // perform the restore as this will overwrite memory which has data
>> +  // needed by SEV-ES.
>> +  //
>> +  if (!CpuMpData->SevEsIsEnabled) {
>> +    RestoreWakeupBuffer (CpuMpData);
>> +  }
>> +}
>> +
>> +/**
>> +  Allocate the SEV-ES AP jump table buffer.
>> +
>> +  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.
>> +**/
>> +VOID
>> +AllocateSevEsAPMemory (
>> +  IN OUT CPU_MP_DATA          *CpuMpData
>> +  )
>> +{
>> +  if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {
>> +    CpuMpData->SevEsAPBuffer =
>> +      CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
>> +  }
>>  }
>>
>>  /**
>> @@ -1066,6 +1308,7 @@ WakeUpAP (
>>        CpuMpData->InitFlag   != ApInitDone) {
>>      ResetVectorRequired = TRUE;
>>      AllocateResetVector (CpuMpData);
>> +    AllocateSevEsAPMemory (CpuMpData);
>>      FillExchangeInfoData (CpuMpData);
>>      SaveLocalApicTimerSetting (CpuMpData);
>>    }
>> @@ -1102,6 +1345,50 @@ WakeUpAP (
>>        }
>>      }
>>      if (ResetVectorRequired) {
>> +      //
>> +      // For SEV-ES, the initial AP boot address will be defined by
>> +      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address
>> +      // from the original INIT-SIPI-SIPI.
>> +      //
>> +      if (CpuMpData->SevEsIsEnabled) {
>> +        SEV_ES_AP_JMP_FAR *JmpFar;
>> +        UINT32            Offset, InsnByte;
>> +        UINT8             LoNib, HiNib;
>> +
>> +        JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32
>> (PcdSevEsWorkAreaBase);
>> +        ASSERT (JmpFar != NULL);
>> +
>> +        //
>> +        // Obtain the address of the Segment/Rip location in the workarea.
>> +        // This will be set to a value derived from the SIPI vector and will
>> +        // be the memory address used for the far jump below.
>> +        //
>> +        Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
>> +        Offset += sizeof (JmpFar->InsnBuffer);
>> +        LoNib = (UINT8) Offset;
>> +        HiNib = (UINT8) (Offset >> 8);
>> +
>> +        //
>> +        // Program the workarea (which is the initial AP boot address) with
>> +        // far jump to the SIPI vector (where XX and YY represent the
>> +        // address of where the SIPI vector is stored.
>> +        //
>> +        //   JMP FAR [CS:XXYY] => 2E FF 2E YY XX
>> +        //
>> +        InsnByte = 0;
>> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // CS override prefix
>> +        JmpFar->InsnBuffer[InsnByte++] = 0xFF;  // JMP (FAR)
>> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // ModRM (JMP memory
>> location)
>> +        JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
>> +        JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
>> +
>> +        //
>> +        // Program the Segment/Rip based on the SIPI vector (always at least
>> +        // 16-byte aligned, so Rip is set to 0).
>> +        //
>> +        JmpFar->Rip = 0;
>> +        JmpFar->Segment = (UINT16) (ExchangeInfo->BufferStart >> 4);
>> +      }
> 
> For this wake-up process, current code just handles the broadcast type. I think it also needs to handle wake-up specific AP case. Right?

Yes, it should be. I never encountered a non-broadcast call under OVMF,
but it should be supported for error cases, etc. and for any future
changes in support.

I'll add it and make the above code a function so as not to duplicate it.

Thanks,
Tom

> 
> Thanks,
> Eric
>>        //
>>        // Wakeup all APs
>>        //
>> @@ -1669,7 +1956,7 @@ MpInitLibInitialize (
>>    ASSERT (MaxLogicalProcessorNumber != 0);
>>
>>    AsmGetAddressMap (&AddressMap);
>> -  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof
>> (MP_CPU_EXCHANGE_INFO);
>> +  ApResetVectorSize = GetApResetVectorSize (&AddressMap);
>>    ApStackSize = PcdGet32(PcdCpuApStackSize);
>>    ApLoopMode  = GetApLoopMode (&MonitorFilterSize);
>>
>> @@ -1728,6 +2015,8 @@ MpInitLibInitialize (
>>    CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData
>> + MaxLogicalProcessorNumber);
>>    InitializeSpinLock(&CpuMpData->MpLock);
>>    CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
>> +  CpuMpData->SevEsAPBuffer  = (UINTN) -1;
>> +  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
>>
>>    //
>>    // Make sure no memory usage outside of the allocated buffer.
>> @@ -1786,6 +2075,7 @@ MpInitLibInitialize (
>>      // APs have been wakeup before, just get the CPU Information
>>      // from HOB
>>      //
>> +    OldCpuMpData->NewCpuMpData = CpuMpData;
>>      CpuMpData->CpuCount  = OldCpuMpData->CpuCount;
>>      CpuMpData->BspNumber = OldCpuMpData->BspNumber;
>>      CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; diff --git
>> a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> index a548fed23fa7..e17a351e5cfd 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> @@ -280,6 +280,25 @@ GetModeTransitionBuffer (
>>    return 0;
>>  }
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  )
>> +{
>> +  //
>> +  // PEI phase doesn't need to do such transition. So simply return 0.
>> +  //
>> +  return 0;
>> +}
>> +
>>  /**
>>    Checks APs status and updates APs status if needed.
>>
>> diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> index 6298571e29b2..28f8e8e133e5 100644
>> --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> @@ -121,7 +121,7 @@ GetProtectedModeCS (
>>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>>    for (Index = 0; Index < GdtEntryCount; Index++) {
>>      if (GdtEntry->Bits.L == 0) {
>> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>>          break;
>>        }
>>      }
>> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> index efb1bc2bf7cb..4f5a7c859a56 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>>  CPU_SWITCH_STATE_STORED       equ        1
>>  CPU_SWITCH_STATE_LOADED       equ        2
>>
>> -LockLocation                  equ        (RendezvousFunnelProcEnd -
>> RendezvousFunnelProcStart)
>> +LockLocation                  equ        (SwitchToRealProcEnd -
>> RendezvousFunnelProcStart)
>>  StackStartAddressLocation     equ        LockLocation + 04h
>>  StackSizeLocation             equ        LockLocation + 08h
>>  ApProcedureLocation           equ        LockLocation + 0Ch
>> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> index b74046b76af3..309d53bf3b37 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> @@ -215,6 +215,16 @@ CProcedureInvoke:
>>      jmp        $                 ; Never reach here
>>  RendezvousFunnelProcEnd:
>>
>> +;----------------------------------------------------------------------
>> +---------------
>> +;SwitchToRealProc procedure follows.
>> +;NOT USED IN 32 BIT MODE.
>> +;----------------------------------------------------------------------
>> +---------------
>> +global ASM_PFX(SwitchToRealProc)
>> +ASM_PFX(SwitchToRealProc):
>> +SwitchToRealProcStart:
>> +    jmp        $                 ; Never reach here
>> +SwitchToRealProcEnd:
>> +
>>  ;-------------------------------------------------------------------------------------
>>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
>> TopOfApStack, CountTofinish);
>>  ;-------------------------------------------------------------------------------------
>> @@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
>>      mov        dword [ebx + 0Ch], AsmRelocateApLoopStart
>>      mov        dword [ebx + 10h], AsmRelocateApLoopEnd -
>> AsmRelocateApLoopStart
>>      mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
>> +    mov        dword [ebx + 18h], SwitchToRealProcEnd -
>> SwitchToRealProcStart       ; SwitchToRealSize
>> +    mov        dword [ebx + 1Ch], SwitchToRealProcStart -
>> RendezvousFunnelProcStart ; SwitchToRealOffset
>> +    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start               ;
>> SwitchToRealNoNxOffset
>> +    mov        dword [ebx + 24h], 0                                                 ;
>> SwitchToRealPM16ModeOffset
>> +    mov        dword [ebx + 28h], 0                                                 ;
>> SwitchToRealPM16ModeSize
>>
>>      popad
>>      ret
>> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> index 58ef369342a7..c92daaaffd6b 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>>  CPU_SWITCH_STATE_STORED       equ        1
>>  CPU_SWITCH_STATE_LOADED       equ        2
>>
>> -LockLocation                  equ        (RendezvousFunnelProcEnd -
>> RendezvousFunnelProcStart)
>> +LockLocation                  equ        (SwitchToRealProcEnd -
>> RendezvousFunnelProcStart)
>>  StackStartAddressLocation     equ        LockLocation + 08h
>>  StackSizeLocation             equ        LockLocation + 10h
>>  ApProcedureLocation           equ        LockLocation + 18h
>> @@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ  LockLocation +
>> 98h
>>  ModeHighMemoryLocation              equ  LockLocation + 9Ah
>>  ModeHighSegmentLocation             equ  LockLocation + 9Eh
>>  Enable5LevelPagingLocation          equ  LockLocation + 0A0h
>> +SevEsIsEnabledLocation              equ  LockLocation + 0A1h
>> +GhcbBaseLocation                    equ  LockLocation + 0A2h
>> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> index 87f2523e856f..6956b408d004 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> @@ -184,9 +184,97 @@ Releaselock:
>>      add        edi, StackStartAddressLocation
>>      add        rax, qword [edi]
>>      mov        rsp, rax
>> +
>> +    lea        edi, [esi + SevEsIsEnabledLocation]
>> +    cmp        byte [edi], 1        ; SevEsIsEnabled
>> +    jne        CProcedureInvoke
>> +
>> +    ;
>> +    ; program GHCB
>> +    ;   Each page after the GHCB is a per-CPU page, so the calculation
>> programs
>> +    ;   a GHCB to be every 8KB.
>> +    ;
>> +    mov        eax, SIZE_4KB
>> +    shl        eax, 1                            ; EAX = SIZE_4K * 2
>> +    mov        ecx, ebx
>> +    mul        ecx                               ; EAX = SIZE_4K * 2 * CpuNumber
>> +    mov        edi, esi
>> +    add        edi, GhcbBaseLocation
>> +    add        rax, qword [edi]
>> +    mov        rdx, rax
>> +    shr        rdx, 32
>> +    mov        rcx, 0xc0010130
>> +    wrmsr
>>      jmp        CProcedureInvoke
>>
>>  GetApicId:
>> +    lea        edi, [esi + SevEsIsEnabledLocation]
>> +    cmp        byte [edi], 1        ; SevEsIsEnabled
>> +    jne        DoCpuid
>> +
>> +    ;
>> +    ; Since we don't have a stack yet, we can't take a #VC
>> +    ; exception. Use the GHCB protocol to perform the CPUID
>> +    ; calls.
>> +    ;
>> +    mov        rcx, 0xc0010130
>> +    rdmsr
>> +    shl        rdx, 32
>> +    or         rax, rdx
>> +    mov        rdi, rax             ; RDI now holds the original GHCB GPA
>> +
>> +    mov        rdx, 0               ; CPUID function 0
>> +    mov        rax, 0               ; RAX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    cmp        edx, 0bh
>> +    jb         NoX2ApicSevEs        ; CPUID level below
>> CPUID_EXTENDED_TOPOLOGY
>> +
>> +    mov        rdx, 0bh             ; CPUID function 0x0b
>> +    mov        rax, 040000000h      ; RBX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    test       edx, 0ffffh
>> +    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero
>> +
>> +    mov        rdx, 0bh             ; CPUID function 0x0b
>> +    mov        rax, 0c0000000h      ; RDX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +
>> +    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
>> +    jmp        RestoreGhcb
>> +
>> +NoX2ApicSevEs:
>> +    ; Processor is not x2APIC capable, so get 8-bit APIC ID
>> +    mov        rdx, 1               ; CPUID function 1
>> +    mov        rax, 040000000h      ; RBX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    shr        edx, 24
>> +
>> +RestoreGhcb:
>> +    mov        rbx, rdx             ; Save x2APIC/APIC ID
>> +
>> +    mov        rdx, rdi             ; RDI holds the saved GHCB GPA
>> +    shr        rdx, 32
>> +    mov        eax, edi
>> +    wrmsr
>> +
>> +    mov        rdx, rbx
>> +
>> +    ; x2APIC ID or APIC ID is in EDX
>> +    jmp        GetProcessorNumber
>> +
>> +DoCpuid:
>>      mov        eax, 0
>>      cpuid
>>      cmp        eax, 0bh
>> @@ -253,12 +341,158 @@ CProcedureInvoke:
>>
>>  RendezvousFunnelProcEnd:
>>
>> +;----------------------------------------------------------------------
>> +---------------
>> +;SwitchToRealProc procedure follows.
>> +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT
>> MODE.
>> +HENCE THIS PROC ;IS IN MACHINE CODE.
>> +;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32,
>> +UINTN StackStart) ;  rcx - Buffer Start ;  rdx - Code16 Selector Offset
>> +;  r8  - Code32 Selector Offset ;  r9  - Stack Start
>> +;----------------------------------------------------------------------
>> +---------------
>> +global ASM_PFX(SwitchToRealProc)
>> +ASM_PFX(SwitchToRealProc):
>> +SwitchToRealProcStart:
>> +BITS 64
>> +    cli
>> +
>> +    ;
>> +    ; Get RDX reset value before changing stacks since the
>> +    ; new stack won't be able to accomodate a #VC exception.
>> +    ;
>> +    push       rax
>> +    push       rbx
>> +    push       rcx
>> +    push       rdx
>> +
>> +    mov        rax, 1
>> +    cpuid
>> +    mov        rsi, rax                    ; Save off the reset value for RDX
>> +
>> +    pop        rdx
>> +    pop        rcx
>> +    pop        rbx
>> +    pop        rax
>> +
>> +    ;
>> +    ; Establish stack below 1MB
>> +    ;
>> +    mov        rsp, r9
>> +
>> +    ;
>> +    ; Push ultimate Reset Vector onto the stack
>> +    ;
>> +    mov        rax, rcx
>> +    shr        rax, 4
>> +    push       word 0x0002                 ; RFLAGS
>> +    push       ax                          ; CS
>> +    push       word 0x0000                 ; RIP
>> +    push       word 0x0000                 ; For alignment, will be discarded
>> +
>> +    ;
>> +    ; Get address of "16-bit operand size" label
>> +    ;
>> +    lea        rbx, [PM16Mode]
>> +
>> +    ;
>> +    ; Push addresses used to change to compatibility mode
>> +    ;
>> +    lea        rax, [CompatMode]
>> +    push       r8
>> +    push       rax
>> +
>> +    ;
>> +    ; Clear R8 - R15, for reset, before going into 32-bit mode
>> +    ;
>> +    xor        r8, r8
>> +    xor        r9, r9
>> +    xor        r10, r10
>> +    xor        r11, r11
>> +    xor        r12, r12
>> +    xor        r13, r13
>> +    xor        r14, r14
>> +    xor        r15, r15
>> +
>> +    ;
>> +    ; Far return into 32-bit mode
>> +    ;
>> +o64 retf
>> +
>> +BITS 32
>> +CompatMode:
>> +    ;
>> +    ; Set up stack to prepare for exiting protected mode
>> +    ;
>> +    push       edx                         ; Code16 CS
>> +    push       ebx                         ; PM16Mode label address
>> +
>> +    ;
>> +    ; Disable paging
>> +    ;
>> +    mov        eax, cr0                    ; Read CR0
>> +    btr        eax, 31                     ; Set PG=0
>> +    mov        cr0, eax                    ; Write CR0
>> +
>> +    ;
>> +    ; Disable long mode
>> +    ;
>> +    mov        ecx, 0c0000080h             ; EFER MSR number
>> +    rdmsr                                  ; Read EFER
>> +    btr        eax, 8                      ; Set LME=0
>> +    wrmsr                                  ; Write EFER
>> +
>> +    ;
>> +    ; Disable PAE
>> +    ;
>> +    mov        eax, cr4                    ; Read CR4
>> +    btr        eax, 5                      ; Set PAE=0
>> +    mov        cr4, eax                    ; Write CR4
>> +
>> +    mov        edx, esi                    ; Restore RDX reset value
>> +
>> +    ;
>> +    ; Switch to 16-bit operand size
>> +    ;
>> +    retf
>> +
>> +BITS 16
>> +    ;
>> +    ; At entry to this label
>> +    ;   - RDX will have its reset value
>> +    ;   - On the top of the stack
>> +    ;     - Alignment data (two bytes) to be discarded
>> +    ;     - IP for Real Mode (two bytes)
>> +    ;     - CS for Real Mode (two bytes)
>> +    ;
>> +PM16Mode:
>> +    mov        eax, cr0                    ; Read CR0
>> +    btr        eax, 0                      ; Set PE=0
>> +    mov        cr0, eax                    ; Write CR0
>> +
>> +    pop        ax                          ; Discard alignment data
>> +
>> +    ;
>> +    ; Clear registers (except RDX and RSP) before going into 16-bit mode
>> +    ;
>> +    xor        eax, eax
>> +    xor        ebx, ebx
>> +    xor        ecx, ecx
>> +    xor        esi, esi
>> +    xor        edi, edi
>> +    xor        ebp, ebp
>> +
>> +    iret
>> +
>> +SwitchToRealProcEnd:
>> +
>>  ;-------------------------------------------------------------------------------------
>>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
>> TopOfApStack, CountTofinish);
>>  ;-------------------------------------------------------------------------------------
>>  global ASM_PFX(AsmRelocateApLoop)
>>  ASM_PFX(AsmRelocateApLoop):
>>  AsmRelocateApLoopStart:
>> +BITS 64
>>      cli                          ; Disable interrupt before switching to 32-bit mode
>>      mov        rax, [rsp + 40]   ; CountTofinish
>>      lock dec   dword [rax]       ; (*CountTofinish)--
>> @@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
>>      mov        qword [rcx + 18h], rax
>>      mov        qword [rcx + 20h], AsmRelocateApLoopEnd -
>> AsmRelocateApLoopStart
>>      mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
>> +    mov        qword [rcx + 30h], SwitchToRealProcEnd -
>> SwitchToRealProcStart          ; SwitchToRealSize
>> +    mov        qword [rcx + 38h], SwitchToRealProcStart -
>> RendezvousFunnelProcStart    ; SwitchToRealOffset
>> +    mov        qword [rcx + 40h], SwitchToRealProcStart - Flat32Start                  ;
>> SwitchToRealNoNxOffset
>> +    mov        qword [rcx + 48h], PM16Mode -
>> RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset
>> +    mov        qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode                       ;
>> SwitchToRealPM16ModeSize
>>      ret
>>
>>  ;-------------------------------------------------------------------------------------
>> --
>> 2.17.1
> 

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

* Re: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
  2020-06-01  7:28   ` Dong, Eric
@ 2020-06-01 16:58     ` Lendacky, Thomas
  0 siblings, 0 replies; 100+ messages in thread
From: Lendacky, Thomas @ 2020-06-01 16:58 UTC (permalink / raw)
  To: Dong, Eric, devel@edk2.groups.io
  Cc: Justen, Jordan L, Laszlo Ersek, Ard Biesheuvel, Kinney, Michael D,
	Gao, Liming, Ni, Ray, Brijesh Singh

On 6/1/20 2:28 AM, Dong, Eric wrote:
> Hi Tom,

Hi Eric,

> 
>> -----Original Message-----
>> From: Tom Lendacky <thomas.lendacky@amd.com>
>> Sent: Wednesday, May 20, 2020 5:51 AM
>> To: devel@edk2.groups.io
>> Cc: Justen, Jordan L <jordan.l.justen@intel.com>; Laszlo Ersek
>> <lersek@redhat.com>; Ard Biesheuvel <ard.biesheuvel@linaro.org>; Kinney,
>> Michael D <michael.d.kinney@intel.com>; Gao, Liming
>> <liming.gao@intel.com>; Dong, Eric <eric.dong@intel.com>; Ni, Ray
>> <ray.ni@intel.com>; Brijesh Singh <brijesh.singh@amd.com>
>> Subject: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
>>
>> BZ: https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2198&amp;data=02%7C01%7Cthomas.lendacky%40amd.com%7Ccb3255e534ac46be29e108d805fd6f22%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637265933337671439&amp;sdata=Rc%2BTzZ4%2FcJnmX%2FMOEnY4ixy45vPKVo4DCBVjsmdGogk%3D&amp;reserved=0
>>
>> Typically, an AP is booted using the INIT-SIPI-SIPI sequence. This sequence is
>> intercepted by the hypervisor, which sets the AP's registers to the values
>> requested by the sequence. At that point, the hypervisor can start the AP,
>> which will then begin execution at the appropriate location.
>>
>> Under SEV-ES, AP booting presents some challenges since the hypervisor is
>> not allowed to alter the AP's register state. In this situation, we have to
>> distinguish between the AP's first boot and AP's subsequent boots.
>>
>> First boot:
>>  Once the AP's register state has been defined (which is before the guest  is
>> first booted) it cannot be altered. Should the hypervisor attempt to  alter the
>> register state, the change would be detected by the hardware  and the
>> VMRUN instruction would fail. Given this, the first boot for the  AP is
>> required to begin execution with this initial register state, which  is typically
>> the reset vector. This prevents the BSP from directing the  AP startup
>> location through the INIT-SIPI-SIPI sequence.
>>
>>  To work around this, the firmware will provide a build time reserved area
>> that can be used as the initial IP value. The hypervisor can extract this
>> location value by checking for the SEV-ES reset block GUID that must be
>> located 48-bytes from the end of the firmware. The format of the SEV-ES
>> reset block area is:
>>
>>    0x00 - 0x01 - SEV-ES Reset IP
>>    0x02 - 0x03 - SEV-ES Reset CS Segment Base[31:16]
>>    0x04 - 0x05 - Size of the SEV-ES reset block
>>    0x06 - 0x15 - SEV-ES Reset Block GUID
>>                    (00f771de-1a7e-4fcb-890e-68c77e2fb44e)
>>
>>    The total size is 22 bytes. Any expansion to this block must be done
>>    by adding new values before existing values.
>>
>>  The hypervisor will use the IP and CS values obtained from the SEV-ES  reset
>> block to set as the AP's initial values. The CS Segment Base  represents the
>> upper 16 bits of the CS segment base and must be left  shifted by 16 bits to
>> form the complete CS segment base value.
>>
>>  Before booting the AP for the first time, the BSP must initialize the  SEV-ES
>> reset area. This consists of programming a FAR JMP instruction  to the
>> contents of a memory location that is also located in the SEV-ES  reset area.
>> The BSP must program the IP and CS values for the FAR JMP  based on values
>> drived from the INIT-SIPI-SIPI sequence.
>>
>> Subsequent boots:
>>  Again, the hypervisor cannot alter the AP register state, so a method is
>> required to take the AP out of halt state and redirect it to the desired  IP
>> location. If it is determined that the AP is running in an SEV-ES  guest, then
>> instead of calling CpuSleep(), a VMGEXIT is issued with the  AP Reset Hold
>> exit code (0x80000004). The hypervisor will put the AP in  a halt state, waiting
>> for an INIT-SIPI-SIPI sequence. Once the sequence  is recognized, the
>> hypervisor will resume the AP. At this point the AP  must transition from the
>> current 64-bit long mode down to 16-bit real  mode and begin executing at
>> the derived location from the INIT-SIPI-SIPI  sequence.
>>
>>  Another change is around the area of obtaining the (x2)APIC ID during AP
>> startup. During AP startup, the AP can't take a #VC exception before the  AP
>> has established a stack. However, the AP stack is set by using the  (x2)APIC ID,
>> which is obtained through CPUID instructions. A CPUID  instruction will cause
>> a #VC, so a different method must be used. The  GHCB protocol supports a
>> method to obtain CPUID information from the  hypervisor through the GHCB
>> MSR. This method does not require a stack,  so it is used to obtain the
>> necessary CPUID information to determine the  (x2)APIC ID.
>>
>> The new 16-bit protected mode GDT entry is used in order to transition from
>> 64-bit long mode down to 16-bit real mode.
>>
>> A new assembler routine is created that takes the AP from 64-bit long mode
>> to 16-bit real mode.  This is located under 1MB in memory and transitions
>> from 64-bit long mode to 32-bit compatibility mode to 16-bit protected mode
>> and finally 16-bit real mode.
>>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Ray Ni <ray.ni@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
>> ---
>>  UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   3 +
>>  UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   3 +
>>  UefiCpuPkg/Library/MpInitLib/MpLib.h          |  60 ++++
>>  UefiCpuPkg/Library/MpInitLib/DxeMpLib.c       |  70 +++-
>>  UefiCpuPkg/Library/MpInitLib/MpLib.c          | 312 +++++++++++++++++-
>>  UefiCpuPkg/Library/MpInitLib/PeiMpLib.c       |  19 ++
>>  UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c  |   2 +-
>>  UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc   |   2 +-
>>  .../Library/MpInitLib/Ia32/MpFuncs.nasm       |  15 +
>>  UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc    |   4 +-
>>  UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 239 ++++++++++++++
>>  11 files changed, 714 insertions(+), 15 deletions(-)
>>
>> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> index 583276595619..1771575c69c1 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> @@ -52,6 +52,7 @@ [LibraryClasses]
>>    DebugAgentLib
>>    SynchronizationLib
>>    PcdLib
>> +  VmgExitLib
>>
>>  [Protocols]
>>    gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES
>> @@ -72,4 +73,6 @@ [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ##
>> SOMETIMES_CONSUMES
>>
>> gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds
>> ## CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                          ##
>> CONSUMES
>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ##
>> SOMETIMES_CONSUMES
>>    gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ##
>> CONSUMES
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ##
>> CONSUMES
>> diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> index 4b3d39fbf36c..34abf25d43cd 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> @@ -51,6 +51,7 @@ [LibraryClasses]
>>    SynchronizationLib
>>    PeiServicesLib
>>    PcdLib
>> +  VmgExitLib
>>
>>  [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ##
>> CONSUMES
>> @@ -62,6 +63,8 @@ [Pcd]
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ##
>> CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ##
>> SOMETIMES_CONSUMES
>>    gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled                      ## CONSUMES
>> +  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ##
>> SOMETIMES_CONSUMES
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ##
>> CONSUMES
>>
>>  [Ppis]
>>    gEdkiiPeiShadowMicrocodePpiGuid        ## SOMETIMES_CONSUMES
>> diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> b/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> index 5b46c295b6b2..f0cbb3763b5d 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
>> @@ -173,6 +173,11 @@ typedef struct {
>>    UINT8             *RelocateApLoopFuncAddress;
>>    UINTN             RelocateApLoopFuncSize;
>>    UINTN             ModeTransitionOffset;
>> +  UINTN             SwitchToRealSize;
>> +  UINTN             SwitchToRealOffset;
>> +  UINTN             SwitchToRealNoNxOffset;
>> +  UINTN             SwitchToRealPM16ModeOffset;
>> +  UINTN             SwitchToRealPM16ModeSize;
>>  } MP_ASSEMBLY_ADDRESS_MAP;
>>
>>  typedef struct _CPU_MP_DATA  CPU_MP_DATA; @@ -211,6 +216,8 @@
>> typedef struct {
>>    // Enable5LevelPaging indicates whether 5-level paging is enabled in long
>> mode.
>>    //
>>    BOOLEAN               Enable5LevelPaging;
>> +  BOOLEAN               SevEsIsEnabled;
>> +  UINTN                 GhcbBase;
>>  } MP_CPU_EXCHANGE_INFO;
>>
>>  #pragma pack()
>> @@ -257,6 +264,7 @@ struct _CPU_MP_DATA {
>>    UINT8                          ApLoopMode;
>>    UINT8                          ApTargetCState;
>>    UINT16                         PmCodeSegment;
>> +  UINT16                         Pm16CodeSegment;
>>    CPU_AP_DATA                    *CpuData;
>>    volatile MP_CPU_EXCHANGE_INFO  *MpCpuExchangeInfo;
>>
>> @@ -278,8 +286,47 @@ struct _CPU_MP_DATA {
>>    BOOLEAN                        WakeUpByInitSipiSipi;
>>
>>    BOOLEAN                        SevEsIsEnabled;
>> +  UINTN                          SevEsAPBuffer;
>> +  UINTN                          SevEsAPResetStackStart;
>> +  CPU_MP_DATA                    *NewCpuMpData;
>> +
>> +  UINT64                         GhcbBase;
>>  };
>>
>> +#define AP_RESET_STACK_SIZE 64
>> +
>> +#pragma pack(1)
>> +
>> +typedef struct {
>> +  UINT8   InsnBuffer[8];
>> +  UINT16  Rip;
>> +  UINT16  Segment;
>> +} SEV_ES_AP_JMP_FAR;
>> +
>> +#pragma pack()
>> +
>> +/**
>> +  Assembly code to move an AP from long mode to real mode.
>> +
>> +  Move an AP from long mode to real mode in preparation to invoking
>> + the reset vector.  This is used for SEV-ES guests where a hypervisor
>> + is not allowed to set the CS and RIP to point to the reset vector.
>> +
>> +  @param[in]  BufferStart  The reset vector target.
>> +  @param[in]  Code16       16-bit protected mode code segment value.
>> +  @param[in]  Code32       32-bit protected mode code segment value.
>> +  @param[in]  StackStart   The start of a stack to be used for transitioning
>> +                           from long mode to real mode.
>> +**/
>> +typedef
>> +VOID
>> +(EFIAPI AP_RESET) (
>> +  IN UINTN    BufferStart,
>> +  IN UINT16   Code16,
>> +  IN UINT16   Code32,
>> +  IN UINTN    StackStart
>> +  );
>> +
>>  extern EFI_GUID mCpuInitMpLibHobGuid;
>>
>>  /**
>> @@ -385,6 +432,19 @@ GetModeTransitionBuffer (
>>    IN UINTN                BufferSize
>>    );
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  );
>> +
>>  /**
>>    This function will be called by BSP to wakeup AP.
>>
>> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> index 8ccddf8e9f9c..19527300ff3a 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
>> @@ -12,6 +12,8 @@
>>  #include <Library/UefiBootServicesTableLib.h>
>>  #include <Library/DebugAgentLib.h>
>>  #include <Library/DxeServicesTableLib.h>
>> +#include <Register/Amd/Fam17Msr.h>
>> +#include <Register/Amd/Ghcb.h>
>>
>>  #include <Protocol/Timer.h>
>>
>> @@ -144,6 +146,39 @@ GetModeTransitionBuffer (
>>    return (UINTN)StartAddress;
>>  }
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS            Status;
>> +  EFI_PHYSICAL_ADDRESS  StartAddress;
>> +
>> +  //
>> +  // Allocate 1 page for AP jump table page  //  StartAddress =
>> + BASE_4GB - 1;  Status = gBS->AllocatePages (
>> +                  AllocateMaxAddress,
>> +                  EfiReservedMemoryType,
>> +                  1,
>> +                  &StartAddress
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN)
>> + StartAddress));
>> +
>> +  return (UINTN) StartAddress;
>> +}
>> +
>>  /**
>>    Checks APs status and updates APs status if needed.
>>
>> @@ -218,6 +253,38 @@ CheckApsStatus (
>>    }
>>  }
>>
>> +/**
>> +  Get Protected mode code segment with 16-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 16-bit code segment value.
>> +**/
>> +UINT16
>> +GetProtectedMode16CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) {
>> +        break;
>> +      }
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>>  /**
>>    Get Protected mode code segment from current GDT table.
>>
>> @@ -238,7 +305,7 @@ GetProtectedModeCS (
>>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>>    for (Index = 0; Index < GdtEntryCount; Index++) {
>>      if (GdtEntry->Bits.L == 0) {
>> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>>          break;
>>        }
>>      }
>> @@ -300,6 +367,7 @@ MpInitChangeApLoopCallback (
>>
>>    CpuMpData = GetCpuMpData ();
>>    CpuMpData->PmCodeSegment = GetProtectedModeCS ();
>> +  CpuMpData->Pm16CodeSegment = GetProtectedMode16CS ();
>>    CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
>>    mNumberToFinish = CpuMpData->CpuCount - 1;
>>    WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); diff --git
>> a/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> index a8b605f569bf..aeab575bb525 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
>> @@ -9,6 +9,9 @@
>>  **/
>>
>>  #include "MpLib.h"
>> +#include <Library/VmgExitLib.h>
>> +#include <Register/Amd/Fam17Msr.h>
>> +#include <Register/Amd/Ghcb.h>
>>
>>  EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
>>
>> @@ -314,6 +317,14 @@ GetApLoopMode (
>>        //
>>        ApLoopMode = ApInHltLoop;
>>      }
>> +
>> +    if (PcdGetBool (PcdSevEsIsEnabled)) {
>> +      //
>> +      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB
>> +      // protocol for starting APs
>> +      //
>> +      ApLoopMode = ApInHltLoop;
>> +    }
>>    }
>>
>>    if (ApLoopMode != ApInMwaitLoop) {
>> @@ -610,6 +621,112 @@ InitializeApData (
>>    SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);  }
>>
>> +/**
>> +  Get Protected mode code segment with 16-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 16-bit code segment value.
>> +**/
>> +STATIC
>> +UINT16
>> +GetProtectedMode16CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0 &&
>> +        GdtEntry->Bits.DB == 0 &&
>> +        GdtEntry->Bits.Type > 8) {
>> +      break;
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>> +/**
>> +  Get Protected mode code segment with 32-bit default addressing
>> +  from current GDT table.
>> +
>> +  @return  Protected mode 32-bit code segment value.
>> +**/
>> +STATIC
>> +UINT16
>> +GetProtectedMode32CS (
>> +  VOID
>> +  )
>> +{
>> +  IA32_DESCRIPTOR          GdtrDesc;
>> +  IA32_SEGMENT_DESCRIPTOR  *GdtEntry;
>> +  UINTN                    GdtEntryCount;
>> +  UINT16                   Index;
>> +
>> +  Index = (UINT16) -1;
>> +  AsmReadGdtr (&GdtrDesc);
>> +  GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof
>> +(IA32_SEGMENT_DESCRIPTOR);
>> +  GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>> +  for (Index = 0; Index < GdtEntryCount; Index++) {
>> +    if (GdtEntry->Bits.L == 0 &&
>> +        GdtEntry->Bits.DB == 1 &&
>> +        GdtEntry->Bits.Type > 8) {
>> +      break;
>> +    }
>> +    GdtEntry++;
>> +  }
>> +  ASSERT (Index != GdtEntryCount);
>> +  return Index * 8;
>> +}
>> +
>> +/**
>> +  Reset an AP when in SEV-ES mode.
>> +
>> +  If successful, this function never returns.
>> +
>> +  @param[in] Ghcb                 Pointer to the GHCB
>> +  @param[in] CpuMpData            Pointer to CPU MP Data
>> +
>> +**/
>> +STATIC
>> +VOID
>> +MpInitLibSevEsAPReset (
>> +  IN GHCB                         *Ghcb,
>> +  IN CPU_MP_DATA                  *CpuMpData
>> +  )
>> +{
>> +  UINT16           Code16, Code32;
>> +  AP_RESET         *APResetFn;
>> +  UINTN            BufferStart;
>> +  UINTN            StackStart;
>> +
>> +  Code16 = GetProtectedMode16CS ();
>> +  Code32 = GetProtectedMode32CS ();
>> +
>> +  if (CpuMpData->WakeupBufferHigh != 0) {
>> +    APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh +
>> + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
>> +  } else {
>> +    APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo-
>>> BufferStart
>> + + CpuMpData->AddressMap.SwitchToRealOffset);
>> +  }
>> +
>> +  BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
>> +  StackStart = CpuMpData->SevEsAPResetStackStart -
>> +                 (AP_RESET_STACK_SIZE * GetApicId ());
>> +
>> +  //
>> +  // This call never returns.
>> +  //
>> +  APResetFn (BufferStart, Code16, Code32, StackStart); }
>> +
>>  /**
>>    This function will be called from AP reset code if BSP uses WakeUpAP.
>>
>> @@ -671,7 +788,14 @@ ApWakeupFunction (
>>        InitializeApData (CpuMpData, ProcessorNumber, BistData,
>> ApTopOfStack);
>>        ApStartupSignalBuffer = CpuMpData-
>>> CpuData[ProcessorNumber].StartupApSignal;
>>
>> -      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo-
>>> NumApsExecuting);
>> +      //
>> +      // Delay decrementing the APs executing count when SEV-ES is enabled
>> +      // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly
>> +      // performs another INIT-SIPI-SIPI sequence.
>> +      //
>> +      if (!CpuMpData->SevEsIsEnabled) {
>> +        InterlockedDecrement ((UINT32 *) &CpuMpData-
>>> MpCpuExchangeInfo->NumApsExecuting);
>> +      }
>>      } else {
>>        //
>>        // Execute AP function if AP is ready @@ -778,7 +902,52 @@
>> ApWakeupFunction (
>>        //
>>        while (TRUE) {
>>          DisableInterrupts ();
>> -        CpuSleep ();
>> +        if (CpuMpData->SevEsIsEnabled) {
>> +          MSR_SEV_ES_GHCB_REGISTER  Msr;
>> +          GHCB                      *Ghcb;
>> +          UINT64                    Status;
>> +          BOOLEAN                   DoDecrement;
>> +
>> +          if (CpuMpData->InitFlag == ApInitConfig) {
>> +            DoDecrement = TRUE;
>> +          }
>> +
>> +          while (TRUE) {
>> +            Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
>> +            Ghcb = Msr.Ghcb;
>> +
>> +            VmgInit (Ghcb);
>> +
>> +            if (DoDecrement) {
>> +              DoDecrement = FALSE;
>> +
>> +              //
>> +              // Perform the delayed decrement just before issuing the first
>> +              // VMGEXIT with AP_RESET_HOLD.
>> +              //
>> +              InterlockedDecrement ((UINT32 *) &CpuMpData-
>>> MpCpuExchangeInfo->NumApsExecuting);
> 
> 1. Why needs to postpose the CpuMpData->MpCpuExchangeInfo->NumApsExecuting update here?

This was done as a result of reports from backports of these patches. The
reading of the MSR and clearing of the GHCB took enough time that the BSP
was able to send another SIPI before the AP reached the point where the
VmgExit was issued. As a result, the AP would enter the AP reset hold and
not return because the SIPI will have already been issued.

> 
>> +            }
>> +
>> +            Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
> 
> 2. The AP will enter Halt mode after this call? Not continue the execute of the below code? I'm not clear how the AP works for this case.

The AP will be put in a pseudo-HLT mode by the VMM. A subsequent SIPI
request will result in the VMM making the vCPU runnable again, causing
this VMGEXIT to return and continue processing the code below.

> 
>> +            if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
>> +              VmgDone (Ghcb);
>> +              break;
>> +            }
>> +
>> +            VmgDone (Ghcb);
>> +          }
>> +
>> +          //
>> +          // Awakened in a new phase? Use the new CpuMpData
>> +          //
>> +          if (CpuMpData->NewCpuMpData) {
>> +            CpuMpData = CpuMpData->NewCpuMpData;
>> +          }
>> +
>> +          MpInitLibSevEsAPReset (Ghcb, CpuMpData);
> 
> 3. With this function, this AP will be wake up and execute the new procedure? 

Yes, the MpInitLibSevEsAPReset () function will cause the AP to transition
back to 16-bit mode and execute starting at the address from the SIPI
request that awoke it - imitating the INIT-SIPI-SIPI sequence.

I can add some more comments to the MpInitLibSevEsAPReset () function to
better describe this.

Thanks,
Tom

> 
> Thanks,
> Eric
> 
>> +        } else {
>> +          CpuSleep ();
>> +        }
>>          CpuPause ();
>>        }
>>      }
>> @@ -891,6 +1060,9 @@ FillExchangeInfoData (
>>    ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
>>    DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName,
>> ExchangeInfo->Enable5LevelPaging));
>>
>> +  ExchangeInfo->SevEsIsEnabled  = CpuMpData->SevEsIsEnabled;
>> +  ExchangeInfo->GhcbBase        = (UINTN) CpuMpData->GhcbBase;
>> +
>>    //
>>    // Get the BSP's data of GDT and IDT
>>    //
>> @@ -917,8 +1089,9 @@ FillExchangeInfoData (
>>    // EfiBootServicesCode to avoid page fault if NX memory protection is
>> enabled.
>>    //
>>    if (CpuMpData->WakeupBufferHigh != 0) {
>> -    Size = CpuMpData->AddressMap.RendezvousFunnelSize -
>> -           CpuMpData->AddressMap.ModeTransitionOffset;
>> +    Size = CpuMpData->AddressMap.RendezvousFunnelSize +
>> +             CpuMpData->AddressMap.SwitchToRealSize -
>> +             CpuMpData->AddressMap.ModeTransitionOffset;
>>      CopyMem (
>>        (VOID *)CpuMpData->WakeupBufferHigh,
>>        CpuMpData->AddressMap.RendezvousFunnelAddress + @@ -971,7
>> +1144,8 @@ BackupAndPrepareWakeupBuffer(
>>    CopyMem (
>>      (VOID *) CpuMpData->WakeupBuffer,
>>      (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
>> -    CpuMpData->AddressMap.RendezvousFunnelSize
>> +    CpuMpData->AddressMap.RendezvousFunnelSize +
>> +      CpuMpData->AddressMap.SwitchToRealSize
>>      );
>>  }
>>
>> @@ -992,6 +1166,44 @@ RestoreWakeupBuffer(
>>      );
>>  }
>>
>> +/**
>> +  Calculate the size of the reset stack.
>> +
>> +  @retval                 Total amount of memory required for stacks
>> +**/
>> +STATIC
>> +UINTN
>> +GetApResetStackSize (
>> +  VOID
>> +  )
>> +{
>> +  return AP_RESET_STACK_SIZE *
>> +PcdGet32(PcdCpuMaxLogicalProcessorNumber);
>> +}
>> +
>> +/**
>> +  Calculate the size of the reset vector.
>> +
>> +  @param[in]  AddressMap  The pointer to Address Map structure.
>> +
>> +  @retval                 Total amount of memory required for the AP reset area
>> +**/
>> +STATIC
>> +UINTN
>> +GetApResetVectorSize (
>> +  IN MP_ASSEMBLY_ADDRESS_MAP  *AddressMap
>> +  )
>> +{
>> +  UINTN  Size;
>> +
>> +  Size = ALIGN_VALUE (AddressMap->RendezvousFunnelSize +
>> +                        AddressMap->SwitchToRealSize +
>> +                        sizeof (MP_CPU_EXCHANGE_INFO),
>> +                      CPU_STACK_ALIGNMENT);  Size +=
>> + GetApResetStackSize ();
>> +
>> +  return Size;
>> +}
>> +
>>  /**
>>    Allocate reset vector buffer.
>>
>> @@ -1005,16 +1217,22 @@ AllocateResetVector (
>>    UINTN           ApResetVectorSize;
>>
>>    if (CpuMpData->WakeupBuffer == (UINTN) -1) {
>> -    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
>> -                          sizeof (MP_CPU_EXCHANGE_INFO);
>> +    ApResetVectorSize = GetApResetVectorSize (&CpuMpData-
>>> AddressMap);
>>
>>      CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);
>>      CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)
>> (UINTN)
>> -                    (CpuMpData->WakeupBuffer + CpuMpData-
>>> AddressMap.RendezvousFunnelSize);
>> +                    (CpuMpData->WakeupBuffer +
>> +                       CpuMpData->AddressMap.RendezvousFunnelSize +
>> +                       CpuMpData->AddressMap.SwitchToRealSize);
>>      CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (
>> -                                    CpuMpData->AddressMap.RendezvousFunnelSize -
>> +                                    CpuMpData->AddressMap.RendezvousFunnelSize +
>> +
>> + CpuMpData->AddressMap.SwitchToRealSize -
>>                                      CpuMpData->AddressMap.ModeTransitionOffset
>>                                      );
>> +    //
>> +    // The reset stack starts at the end of the buffer.
>> +    //
>> +    CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer +
>> + ApResetVectorSize;
>>    }
>>    BackupAndPrepareWakeupBuffer (CpuMpData);  } @@ -1029,7 +1247,31
>> @@ FreeResetVector (
>>    IN CPU_MP_DATA              *CpuMpData
>>    )
>>  {
>> -  RestoreWakeupBuffer (CpuMpData);
>> +  //
>> +  // If SEV-ES is enabled, the reset area is needed for AP parking and
>> +  // and AP startup in the OS, so the reset area is reserved. Do not
>> +  // perform the restore as this will overwrite memory which has data
>> +  // needed by SEV-ES.
>> +  //
>> +  if (!CpuMpData->SevEsIsEnabled) {
>> +    RestoreWakeupBuffer (CpuMpData);
>> +  }
>> +}
>> +
>> +/**
>> +  Allocate the SEV-ES AP jump table buffer.
>> +
>> +  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.
>> +**/
>> +VOID
>> +AllocateSevEsAPMemory (
>> +  IN OUT CPU_MP_DATA          *CpuMpData
>> +  )
>> +{
>> +  if (CpuMpData->SevEsAPBuffer == (UINTN) -1) {
>> +    CpuMpData->SevEsAPBuffer =
>> +      CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
>> +  }
>>  }
>>
>>  /**
>> @@ -1066,6 +1308,7 @@ WakeUpAP (
>>        CpuMpData->InitFlag   != ApInitDone) {
>>      ResetVectorRequired = TRUE;
>>      AllocateResetVector (CpuMpData);
>> +    AllocateSevEsAPMemory (CpuMpData);
>>      FillExchangeInfoData (CpuMpData);
>>      SaveLocalApicTimerSetting (CpuMpData);
>>    }
>> @@ -1102,6 +1345,50 @@ WakeUpAP (
>>        }
>>      }
>>      if (ResetVectorRequired) {
>> +      //
>> +      // For SEV-ES, the initial AP boot address will be defined by
>> +      // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address
>> +      // from the original INIT-SIPI-SIPI.
>> +      //
>> +      if (CpuMpData->SevEsIsEnabled) {
>> +        SEV_ES_AP_JMP_FAR *JmpFar;
>> +        UINT32            Offset, InsnByte;
>> +        UINT8             LoNib, HiNib;
>> +
>> +        JmpFar = (SEV_ES_AP_JMP_FAR *) FixedPcdGet32
>> (PcdSevEsWorkAreaBase);
>> +        ASSERT (JmpFar != NULL);
>> +
>> +        //
>> +        // Obtain the address of the Segment/Rip location in the workarea.
>> +        // This will be set to a value derived from the SIPI vector and will
>> +        // be the memory address used for the far jump below.
>> +        //
>> +        Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
>> +        Offset += sizeof (JmpFar->InsnBuffer);
>> +        LoNib = (UINT8) Offset;
>> +        HiNib = (UINT8) (Offset >> 8);
>> +
>> +        //
>> +        // Program the workarea (which is the initial AP boot address) with
>> +        // far jump to the SIPI vector (where XX and YY represent the
>> +        // address of where the SIPI vector is stored.
>> +        //
>> +        //   JMP FAR [CS:XXYY] => 2E FF 2E YY XX
>> +        //
>> +        InsnByte = 0;
>> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // CS override prefix
>> +        JmpFar->InsnBuffer[InsnByte++] = 0xFF;  // JMP (FAR)
>> +        JmpFar->InsnBuffer[InsnByte++] = 0x2E;  // ModRM (JMP memory
>> location)
>> +        JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
>> +        JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
>> +
>> +        //
>> +        // Program the Segment/Rip based on the SIPI vector (always at least
>> +        // 16-byte aligned, so Rip is set to 0).
>> +        //
>> +        JmpFar->Rip = 0;
>> +        JmpFar->Segment = (UINT16) (ExchangeInfo->BufferStart >> 4);
>> +      }
>>        //
>>        // Wakeup all APs
>>        //
>> @@ -1669,7 +1956,7 @@ MpInitLibInitialize (
>>    ASSERT (MaxLogicalProcessorNumber != 0);
>>
>>    AsmGetAddressMap (&AddressMap);
>> -  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof
>> (MP_CPU_EXCHANGE_INFO);
>> +  ApResetVectorSize = GetApResetVectorSize (&AddressMap);
>>    ApStackSize = PcdGet32(PcdCpuApStackSize);
>>    ApLoopMode  = GetApLoopMode (&MonitorFilterSize);
>>
>> @@ -1728,6 +2015,8 @@ MpInitLibInitialize (
>>    CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData
>> + MaxLogicalProcessorNumber);
>>    InitializeSpinLock(&CpuMpData->MpLock);
>>    CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
>> +  CpuMpData->SevEsAPBuffer  = (UINTN) -1;
>> +  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
>>
>>    //
>>    // Make sure no memory usage outside of the allocated buffer.
>> @@ -1786,6 +2075,7 @@ MpInitLibInitialize (
>>      // APs have been wakeup before, just get the CPU Information
>>      // from HOB
>>      //
>> +    OldCpuMpData->NewCpuMpData = CpuMpData;
>>      CpuMpData->CpuCount  = OldCpuMpData->CpuCount;
>>      CpuMpData->BspNumber = OldCpuMpData->BspNumber;
>>      CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; diff --git
>> a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> index a548fed23fa7..e17a351e5cfd 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
>> @@ -280,6 +280,25 @@ GetModeTransitionBuffer (
>>    return 0;
>>  }
>>
>> +/**
>> +  Return the address of the SEV-ES AP jump table.
>> +
>> +  This buffer is required in order for an SEV-ES guest to transition
>> + from  UEFI into an OS.
>> +
>> +  @retval other   Return SEV-ES AP jump table buffer
>> +**/
>> +UINTN
>> +GetSevEsAPMemory (
>> +  VOID
>> +  )
>> +{
>> +  //
>> +  // PEI phase doesn't need to do such transition. So simply return 0.
>> +  //
>> +  return 0;
>> +}
>> +
>>  /**
>>    Checks APs status and updates APs status if needed.
>>
>> diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> index 6298571e29b2..28f8e8e133e5 100644
>> --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
>> @@ -121,7 +121,7 @@ GetProtectedModeCS (
>>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
>>    for (Index = 0; Index < GdtEntryCount; Index++) {
>>      if (GdtEntry->Bits.L == 0) {
>> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
>> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
>>          break;
>>        }
>>      }
>> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> index efb1bc2bf7cb..4f5a7c859a56 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
>> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>>  CPU_SWITCH_STATE_STORED       equ        1
>>  CPU_SWITCH_STATE_LOADED       equ        2
>>
>> -LockLocation                  equ        (RendezvousFunnelProcEnd -
>> RendezvousFunnelProcStart)
>> +LockLocation                  equ        (SwitchToRealProcEnd -
>> RendezvousFunnelProcStart)
>>  StackStartAddressLocation     equ        LockLocation + 04h
>>  StackSizeLocation             equ        LockLocation + 08h
>>  ApProcedureLocation           equ        LockLocation + 0Ch
>> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> index b74046b76af3..309d53bf3b37 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
>> @@ -215,6 +215,16 @@ CProcedureInvoke:
>>      jmp        $                 ; Never reach here
>>  RendezvousFunnelProcEnd:
>>
>> +;----------------------------------------------------------------------
>> +---------------
>> +;SwitchToRealProc procedure follows.
>> +;NOT USED IN 32 BIT MODE.
>> +;----------------------------------------------------------------------
>> +---------------
>> +global ASM_PFX(SwitchToRealProc)
>> +ASM_PFX(SwitchToRealProc):
>> +SwitchToRealProcStart:
>> +    jmp        $                 ; Never reach here
>> +SwitchToRealProcEnd:
>> +
>>  ;-------------------------------------------------------------------------------------
>>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
>> TopOfApStack, CountTofinish);
>>  ;-------------------------------------------------------------------------------------
>> @@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
>>      mov        dword [ebx + 0Ch], AsmRelocateApLoopStart
>>      mov        dword [ebx + 10h], AsmRelocateApLoopEnd -
>> AsmRelocateApLoopStart
>>      mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
>> +    mov        dword [ebx + 18h], SwitchToRealProcEnd -
>> SwitchToRealProcStart       ; SwitchToRealSize
>> +    mov        dword [ebx + 1Ch], SwitchToRealProcStart -
>> RendezvousFunnelProcStart ; SwitchToRealOffset
>> +    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start               ;
>> SwitchToRealNoNxOffset
>> +    mov        dword [ebx + 24h], 0                                                 ;
>> SwitchToRealPM16ModeOffset
>> +    mov        dword [ebx + 28h], 0                                                 ;
>> SwitchToRealPM16ModeSize
>>
>>      popad
>>      ret
>> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> index 58ef369342a7..c92daaaffd6b 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
>> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
>>  CPU_SWITCH_STATE_STORED       equ        1
>>  CPU_SWITCH_STATE_LOADED       equ        2
>>
>> -LockLocation                  equ        (RendezvousFunnelProcEnd -
>> RendezvousFunnelProcStart)
>> +LockLocation                  equ        (SwitchToRealProcEnd -
>> RendezvousFunnelProcStart)
>>  StackStartAddressLocation     equ        LockLocation + 08h
>>  StackSizeLocation             equ        LockLocation + 10h
>>  ApProcedureLocation           equ        LockLocation + 18h
>> @@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ  LockLocation +
>> 98h
>>  ModeHighMemoryLocation              equ  LockLocation + 9Ah
>>  ModeHighSegmentLocation             equ  LockLocation + 9Eh
>>  Enable5LevelPagingLocation          equ  LockLocation + 0A0h
>> +SevEsIsEnabledLocation              equ  LockLocation + 0A1h
>> +GhcbBaseLocation                    equ  LockLocation + 0A2h
>> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> index 87f2523e856f..6956b408d004 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
>> @@ -184,9 +184,97 @@ Releaselock:
>>      add        edi, StackStartAddressLocation
>>      add        rax, qword [edi]
>>      mov        rsp, rax
>> +
>> +    lea        edi, [esi + SevEsIsEnabledLocation]
>> +    cmp        byte [edi], 1        ; SevEsIsEnabled
>> +    jne        CProcedureInvoke
>> +
>> +    ;
>> +    ; program GHCB
>> +    ;   Each page after the GHCB is a per-CPU page, so the calculation
>> programs
>> +    ;   a GHCB to be every 8KB.
>> +    ;
>> +    mov        eax, SIZE_4KB
>> +    shl        eax, 1                            ; EAX = SIZE_4K * 2
>> +    mov        ecx, ebx
>> +    mul        ecx                               ; EAX = SIZE_4K * 2 * CpuNumber
>> +    mov        edi, esi
>> +    add        edi, GhcbBaseLocation
>> +    add        rax, qword [edi]
>> +    mov        rdx, rax
>> +    shr        rdx, 32
>> +    mov        rcx, 0xc0010130
>> +    wrmsr
>>      jmp        CProcedureInvoke
>>
>>  GetApicId:
>> +    lea        edi, [esi + SevEsIsEnabledLocation]
>> +    cmp        byte [edi], 1        ; SevEsIsEnabled
>> +    jne        DoCpuid
>> +
>> +    ;
>> +    ; Since we don't have a stack yet, we can't take a #VC
>> +    ; exception. Use the GHCB protocol to perform the CPUID
>> +    ; calls.
>> +    ;
>> +    mov        rcx, 0xc0010130
>> +    rdmsr
>> +    shl        rdx, 32
>> +    or         rax, rdx
>> +    mov        rdi, rax             ; RDI now holds the original GHCB GPA
>> +
>> +    mov        rdx, 0               ; CPUID function 0
>> +    mov        rax, 0               ; RAX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    cmp        edx, 0bh
>> +    jb         NoX2ApicSevEs        ; CPUID level below
>> CPUID_EXTENDED_TOPOLOGY
>> +
>> +    mov        rdx, 0bh             ; CPUID function 0x0b
>> +    mov        rax, 040000000h      ; RBX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    test       edx, 0ffffh
>> +    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero
>> +
>> +    mov        rdx, 0bh             ; CPUID function 0x0b
>> +    mov        rax, 0c0000000h      ; RDX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +
>> +    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
>> +    jmp        RestoreGhcb
>> +
>> +NoX2ApicSevEs:
>> +    ; Processor is not x2APIC capable, so get 8-bit APIC ID
>> +    mov        rdx, 1               ; CPUID function 1
>> +    mov        rax, 040000000h      ; RBX register requested
>> +    or         rax, 4
>> +    wrmsr
>> +    rep vmmcall
>> +    rdmsr
>> +    shr        edx, 24
>> +
>> +RestoreGhcb:
>> +    mov        rbx, rdx             ; Save x2APIC/APIC ID
>> +
>> +    mov        rdx, rdi             ; RDI holds the saved GHCB GPA
>> +    shr        rdx, 32
>> +    mov        eax, edi
>> +    wrmsr
>> +
>> +    mov        rdx, rbx
>> +
>> +    ; x2APIC ID or APIC ID is in EDX
>> +    jmp        GetProcessorNumber
>> +
>> +DoCpuid:
>>      mov        eax, 0
>>      cpuid
>>      cmp        eax, 0bh
>> @@ -253,12 +341,158 @@ CProcedureInvoke:
>>
>>  RendezvousFunnelProcEnd:
>>
>> +;----------------------------------------------------------------------
>> +---------------
>> +;SwitchToRealProc procedure follows.
>> +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT
>> MODE.
>> +HENCE THIS PROC ;IS IN MACHINE CODE.
>> +;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32,
>> +UINTN StackStart) ;  rcx - Buffer Start ;  rdx - Code16 Selector Offset
>> +;  r8  - Code32 Selector Offset ;  r9  - Stack Start
>> +;----------------------------------------------------------------------
>> +---------------
>> +global ASM_PFX(SwitchToRealProc)
>> +ASM_PFX(SwitchToRealProc):
>> +SwitchToRealProcStart:
>> +BITS 64
>> +    cli
>> +
>> +    ;
>> +    ; Get RDX reset value before changing stacks since the
>> +    ; new stack won't be able to accomodate a #VC exception.
>> +    ;
>> +    push       rax
>> +    push       rbx
>> +    push       rcx
>> +    push       rdx
>> +
>> +    mov        rax, 1
>> +    cpuid
>> +    mov        rsi, rax                    ; Save off the reset value for RDX
>> +
>> +    pop        rdx
>> +    pop        rcx
>> +    pop        rbx
>> +    pop        rax
>> +
>> +    ;
>> +    ; Establish stack below 1MB
>> +    ;
>> +    mov        rsp, r9
>> +
>> +    ;
>> +    ; Push ultimate Reset Vector onto the stack
>> +    ;
>> +    mov        rax, rcx
>> +    shr        rax, 4
>> +    push       word 0x0002                 ; RFLAGS
>> +    push       ax                          ; CS
>> +    push       word 0x0000                 ; RIP
>> +    push       word 0x0000                 ; For alignment, will be discarded
>> +
>> +    ;
>> +    ; Get address of "16-bit operand size" label
>> +    ;
>> +    lea        rbx, [PM16Mode]
>> +
>> +    ;
>> +    ; Push addresses used to change to compatibility mode
>> +    ;
>> +    lea        rax, [CompatMode]
>> +    push       r8
>> +    push       rax
>> +
>> +    ;
>> +    ; Clear R8 - R15, for reset, before going into 32-bit mode
>> +    ;
>> +    xor        r8, r8
>> +    xor        r9, r9
>> +    xor        r10, r10
>> +    xor        r11, r11
>> +    xor        r12, r12
>> +    xor        r13, r13
>> +    xor        r14, r14
>> +    xor        r15, r15
>> +
>> +    ;
>> +    ; Far return into 32-bit mode
>> +    ;
>> +o64 retf
>> +
>> +BITS 32
>> +CompatMode:
>> +    ;
>> +    ; Set up stack to prepare for exiting protected mode
>> +    ;
>> +    push       edx                         ; Code16 CS
>> +    push       ebx                         ; PM16Mode label address
>> +
>> +    ;
>> +    ; Disable paging
>> +    ;
>> +    mov        eax, cr0                    ; Read CR0
>> +    btr        eax, 31                     ; Set PG=0
>> +    mov        cr0, eax                    ; Write CR0
>> +
>> +    ;
>> +    ; Disable long mode
>> +    ;
>> +    mov        ecx, 0c0000080h             ; EFER MSR number
>> +    rdmsr                                  ; Read EFER
>> +    btr        eax, 8                      ; Set LME=0
>> +    wrmsr                                  ; Write EFER
>> +
>> +    ;
>> +    ; Disable PAE
>> +    ;
>> +    mov        eax, cr4                    ; Read CR4
>> +    btr        eax, 5                      ; Set PAE=0
>> +    mov        cr4, eax                    ; Write CR4
>> +
>> +    mov        edx, esi                    ; Restore RDX reset value
>> +
>> +    ;
>> +    ; Switch to 16-bit operand size
>> +    ;
>> +    retf
>> +
>> +BITS 16
>> +    ;
>> +    ; At entry to this label
>> +    ;   - RDX will have its reset value
>> +    ;   - On the top of the stack
>> +    ;     - Alignment data (two bytes) to be discarded
>> +    ;     - IP for Real Mode (two bytes)
>> +    ;     - CS for Real Mode (two bytes)
>> +    ;
>> +PM16Mode:
>> +    mov        eax, cr0                    ; Read CR0
>> +    btr        eax, 0                      ; Set PE=0
>> +    mov        cr0, eax                    ; Write CR0
>> +
>> +    pop        ax                          ; Discard alignment data
>> +
>> +    ;
>> +    ; Clear registers (except RDX and RSP) before going into 16-bit mode
>> +    ;
>> +    xor        eax, eax
>> +    xor        ebx, ebx
>> +    xor        ecx, ecx
>> +    xor        esi, esi
>> +    xor        edi, edi
>> +    xor        ebp, ebp
>> +
>> +    iret
>> +
>> +SwitchToRealProcEnd:
>> +
>>  ;-------------------------------------------------------------------------------------
>>  ;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment,
>> TopOfApStack, CountTofinish);
>>  ;-------------------------------------------------------------------------------------
>>  global ASM_PFX(AsmRelocateApLoop)
>>  ASM_PFX(AsmRelocateApLoop):
>>  AsmRelocateApLoopStart:
>> +BITS 64
>>      cli                          ; Disable interrupt before switching to 32-bit mode
>>      mov        rax, [rsp + 40]   ; CountTofinish
>>      lock dec   dword [rax]       ; (*CountTofinish)--
>> @@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
>>      mov        qword [rcx + 18h], rax
>>      mov        qword [rcx + 20h], AsmRelocateApLoopEnd -
>> AsmRelocateApLoopStart
>>      mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
>> +    mov        qword [rcx + 30h], SwitchToRealProcEnd -
>> SwitchToRealProcStart          ; SwitchToRealSize
>> +    mov        qword [rcx + 38h], SwitchToRealProcStart -
>> RendezvousFunnelProcStart    ; SwitchToRealOffset
>> +    mov        qword [rcx + 40h], SwitchToRealProcStart - Flat32Start                  ;
>> SwitchToRealNoNxOffset
>> +    mov        qword [rcx + 48h], PM16Mode -
>> RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset
>> +    mov        qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode                       ;
>> SwitchToRealPM16ModeSize
>>      ret
>>
>>  ;-------------------------------------------------------------------------------------
>> --
>> 2.17.1
> 

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

* Re: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES
  2020-06-01 16:10     ` Lendacky, Thomas
@ 2020-06-05  6:13       ` Dong, Eric
  0 siblings, 0 replies; 100+ messages in thread
From: Dong, Eric @ 2020-06-05  6:13 UTC (permalink / raw)
  To: Tom Lendacky, devel@edk2.groups.io
  Cc: Justen, Jordan L, Laszlo Ersek, Ard Biesheuvel, Kinney, Michael D,
	Gao, Liming, Ni, Ray, Brijesh Singh

Hi Tom,

> >> +        //
> >> +        // Program the Segment/Rip based on the SIPI vector (always at
> least
> >> +        // 16-byte aligned, so Rip is set to 0).
> >> +        //
> >> +        JmpFar->Rip = 0;
> >> +        JmpFar->Segment = (UINT16) (ExchangeInfo->BufferStart >> 4);
> >> +      }
> >
> > For this wake-up process, current code just handles the broadcast type. I
> think it also needs to handle wake-up specific AP case. Right?
> 
> Yes, it should be. I never encountered a non-broadcast call under OVMF, but
> it should be supported for error cases, etc. and for any future changes in
> support.
> 
> I'll add it and make the above code a function so as not to duplicate it.
> 

Yes, that will be good.

Thanks,
Eric

> Thanks,
> Tom
> 
> >
> > Thanks,
> > Eric
> >>        //
> >>        // Wakeup all APs
> >>        //
> >> @@ -1669,7 +1956,7 @@ MpInitLibInitialize (
> >>    ASSERT (MaxLogicalProcessorNumber != 0);
> >>
> >>    AsmGetAddressMap (&AddressMap);
> >> -  ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof
> >> (MP_CPU_EXCHANGE_INFO);
> >> +  ApResetVectorSize = GetApResetVectorSize (&AddressMap);
> >>    ApStackSize = PcdGet32(PcdCpuApStackSize);
> >>    ApLoopMode  = GetApLoopMode (&MonitorFilterSize);
> >>
> >> @@ -1728,6 +2015,8 @@ MpInitLibInitialize (
> >>    CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData-
> >CpuData
> >> + MaxLogicalProcessorNumber);
> >>    InitializeSpinLock(&CpuMpData->MpLock);
> >>    CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled);
> >> +  CpuMpData->SevEsAPBuffer  = (UINTN) -1;
> >> +  CpuMpData->GhcbBase       = PcdGet64 (PcdGhcbBase);
> >>
> >>    //
> >>    // Make sure no memory usage outside of the allocated buffer.
> >> @@ -1786,6 +2075,7 @@ MpInitLibInitialize (
> >>      // APs have been wakeup before, just get the CPU Information
> >>      // from HOB
> >>      //
> >> +    OldCpuMpData->NewCpuMpData = CpuMpData;
> >>      CpuMpData->CpuCount  = OldCpuMpData->CpuCount;
> >>      CpuMpData->BspNumber = OldCpuMpData->BspNumber;
> >>      CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; diff --git
> >> a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> >> b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> >> index a548fed23fa7..e17a351e5cfd 100644
> >> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> >> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c
> >> @@ -280,6 +280,25 @@ GetModeTransitionBuffer (
> >>    return 0;
> >>  }
> >>
> >> +/**
> >> +  Return the address of the SEV-ES AP jump table.
> >> +
> >> +  This buffer is required in order for an SEV-ES guest to transition
> >> + from  UEFI into an OS.
> >> +
> >> +  @retval other   Return SEV-ES AP jump table buffer
> >> +**/
> >> +UINTN
> >> +GetSevEsAPMemory (
> >> +  VOID
> >> +  )
> >> +{
> >> +  //
> >> +  // PEI phase doesn't need to do such transition. So simply return 0.
> >> +  //
> >> +  return 0;
> >> +}
> >> +
> >>  /**
> >>    Checks APs status and updates APs status if needed.
> >>
> >> diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> >> b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> >> index 6298571e29b2..28f8e8e133e5 100644
> >> --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> >> +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c
> >> @@ -121,7 +121,7 @@ GetProtectedModeCS (
> >>    GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
> >>    for (Index = 0; Index < GdtEntryCount; Index++) {
> >>      if (GdtEntry->Bits.L == 0) {
> >> -      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
> >> +      if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) {
> >>          break;
> >>        }
> >>      }
> >> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> >> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> >> index efb1bc2bf7cb..4f5a7c859a56 100644
> >> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> >> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc
> >> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
> >>  CPU_SWITCH_STATE_STORED       equ        1
> >>  CPU_SWITCH_STATE_LOADED       equ        2
> >>
> >> -LockLocation                  equ        (RendezvousFunnelProcEnd -
> >> RendezvousFunnelProcStart)
> >> +LockLocation                  equ        (SwitchToRealProcEnd -
> >> RendezvousFunnelProcStart)
> >>  StackStartAddressLocation     equ        LockLocation + 04h
> >>  StackSizeLocation             equ        LockLocation + 08h
> >>  ApProcedureLocation           equ        LockLocation + 0Ch
> >> diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> >> b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> >> index b74046b76af3..309d53bf3b37 100644
> >> --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> >> +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
> >> @@ -215,6 +215,16 @@ CProcedureInvoke:
> >>      jmp        $                 ; Never reach here
> >>  RendezvousFunnelProcEnd:
> >>
> >> +;-------------------------------------------------------------------
> >> +---
> >> +---------------
> >> +;SwitchToRealProc procedure follows.
> >> +;NOT USED IN 32 BIT MODE.
> >> +;-------------------------------------------------------------------
> >> +---
> >> +---------------
> >> +global ASM_PFX(SwitchToRealProc)
> >> +ASM_PFX(SwitchToRealProc):
> >> +SwitchToRealProcStart:
> >> +    jmp        $                 ; Never reach here
> >> +SwitchToRealProcEnd:
> >> +
> >>
> >> ;--------------------------------------------------------------------
> >> -----------------  ;  AsmRelocateApLoop (MwaitSupport,
> >> ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);
> >>
> >> ;--------------------------------------------------------------------
> >> ----------------- @@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap):
> >>      mov        dword [ebx + 0Ch], AsmRelocateApLoopStart
> >>      mov        dword [ebx + 10h], AsmRelocateApLoopEnd -
> >> AsmRelocateApLoopStart
> >>      mov        dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
> >> +    mov        dword [ebx + 18h], SwitchToRealProcEnd -
> >> SwitchToRealProcStart       ; SwitchToRealSize
> >> +    mov        dword [ebx + 1Ch], SwitchToRealProcStart -
> >> RendezvousFunnelProcStart ; SwitchToRealOffset
> >> +    mov        dword [ebx + 20h], SwitchToRealProcStart - Flat32Start               ;
> >> SwitchToRealNoNxOffset
> >> +    mov        dword [ebx + 24h], 0                                                 ;
> >> SwitchToRealPM16ModeOffset
> >> +    mov        dword [ebx + 28h], 0                                                 ;
> >> SwitchToRealPM16ModeSize
> >>
> >>      popad
> >>      ret
> >> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> >> b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> >> index 58ef369342a7..c92daaaffd6b 100644
> >> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> >> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc
> >> @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE         equ        0
> >>  CPU_SWITCH_STATE_STORED       equ        1
> >>  CPU_SWITCH_STATE_LOADED       equ        2
> >>
> >> -LockLocation                  equ        (RendezvousFunnelProcEnd -
> >> RendezvousFunnelProcStart)
> >> +LockLocation                  equ        (SwitchToRealProcEnd -
> >> RendezvousFunnelProcStart)
> >>  StackStartAddressLocation     equ        LockLocation + 08h
> >>  StackSizeLocation             equ        LockLocation + 10h
> >>  ApProcedureLocation           equ        LockLocation + 18h
> >> @@ -41,3 +41,5 @@ ModeTransitionSegmentLocation       equ
> LockLocation +
> >> 98h
> >>  ModeHighMemoryLocation              equ  LockLocation + 9Ah
> >>  ModeHighSegmentLocation             equ  LockLocation + 9Eh
> >>  Enable5LevelPagingLocation          equ  LockLocation + 0A0h
> >> +SevEsIsEnabledLocation              equ  LockLocation + 0A1h
> >> +GhcbBaseLocation                    equ  LockLocation + 0A2h
> >> diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> >> b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> >> index 87f2523e856f..6956b408d004 100644
> >> --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> >> +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
> >> @@ -184,9 +184,97 @@ Releaselock:
> >>      add        edi, StackStartAddressLocation
> >>      add        rax, qword [edi]
> >>      mov        rsp, rax
> >> +
> >> +    lea        edi, [esi + SevEsIsEnabledLocation]
> >> +    cmp        byte [edi], 1        ; SevEsIsEnabled
> >> +    jne        CProcedureInvoke
> >> +
> >> +    ;
> >> +    ; program GHCB
> >> +    ;   Each page after the GHCB is a per-CPU page, so the calculation
> >> programs
> >> +    ;   a GHCB to be every 8KB.
> >> +    ;
> >> +    mov        eax, SIZE_4KB
> >> +    shl        eax, 1                            ; EAX = SIZE_4K * 2
> >> +    mov        ecx, ebx
> >> +    mul        ecx                               ; EAX = SIZE_4K * 2 * CpuNumber
> >> +    mov        edi, esi
> >> +    add        edi, GhcbBaseLocation
> >> +    add        rax, qword [edi]
> >> +    mov        rdx, rax
> >> +    shr        rdx, 32
> >> +    mov        rcx, 0xc0010130
> >> +    wrmsr
> >>      jmp        CProcedureInvoke
> >>
> >>  GetApicId:
> >> +    lea        edi, [esi + SevEsIsEnabledLocation]
> >> +    cmp        byte [edi], 1        ; SevEsIsEnabled
> >> +    jne        DoCpuid
> >> +
> >> +    ;
> >> +    ; Since we don't have a stack yet, we can't take a #VC
> >> +    ; exception. Use the GHCB protocol to perform the CPUID
> >> +    ; calls.
> >> +    ;
> >> +    mov        rcx, 0xc0010130
> >> +    rdmsr
> >> +    shl        rdx, 32
> >> +    or         rax, rdx
> >> +    mov        rdi, rax             ; RDI now holds the original GHCB GPA
> >> +
> >> +    mov        rdx, 0               ; CPUID function 0
> >> +    mov        rax, 0               ; RAX register requested
> >> +    or         rax, 4
> >> +    wrmsr
> >> +    rep vmmcall
> >> +    rdmsr
> >> +    cmp        edx, 0bh
> >> +    jb         NoX2ApicSevEs        ; CPUID level below
> >> CPUID_EXTENDED_TOPOLOGY
> >> +
> >> +    mov        rdx, 0bh             ; CPUID function 0x0b
> >> +    mov        rax, 040000000h      ; RBX register requested
> >> +    or         rax, 4
> >> +    wrmsr
> >> +    rep vmmcall
> >> +    rdmsr
> >> +    test       edx, 0ffffh
> >> +    jz         NoX2ApicSevEs        ; CPUID.0BH:EBX[15:0] is zero
> >> +
> >> +    mov        rdx, 0bh             ; CPUID function 0x0b
> >> +    mov        rax, 0c0000000h      ; RDX register requested
> >> +    or         rax, 4
> >> +    wrmsr
> >> +    rep vmmcall
> >> +    rdmsr
> >> +
> >> +    ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
> >> +    jmp        RestoreGhcb
> >> +
> >> +NoX2ApicSevEs:
> >> +    ; Processor is not x2APIC capable, so get 8-bit APIC ID
> >> +    mov        rdx, 1               ; CPUID function 1
> >> +    mov        rax, 040000000h      ; RBX register requested
> >> +    or         rax, 4
> >> +    wrmsr
> >> +    rep vmmcall
> >> +    rdmsr
> >> +    shr        edx, 24
> >> +
> >> +RestoreGhcb:
> >> +    mov        rbx, rdx             ; Save x2APIC/APIC ID
> >> +
> >> +    mov        rdx, rdi             ; RDI holds the saved GHCB GPA
> >> +    shr        rdx, 32
> >> +    mov        eax, edi
> >> +    wrmsr
> >> +
> >> +    mov        rdx, rbx
> >> +
> >> +    ; x2APIC ID or APIC ID is in EDX
> >> +    jmp        GetProcessorNumber
> >> +
> >> +DoCpuid:
> >>      mov        eax, 0
> >>      cpuid
> >>      cmp        eax, 0bh
> >> @@ -253,12 +341,158 @@ CProcedureInvoke:
> >>
> >>  RendezvousFunnelProcEnd:
> >>
> >> +;-------------------------------------------------------------------
> >> +---
> >> +---------------
> >> +;SwitchToRealProc procedure follows.
> >> +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT
> >> MODE.
> >> +HENCE THIS PROC ;IS IN MACHINE CODE.
> >> +;  SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16
> >> +Code32, UINTN StackStart) ;  rcx - Buffer Start ;  rdx - Code16
> >> +Selector Offset ;  r8  - Code32 Selector Offset ;  r9  - Stack Start
> >> +;-------------------------------------------------------------------
> >> +---
> >> +---------------
> >> +global ASM_PFX(SwitchToRealProc)
> >> +ASM_PFX(SwitchToRealProc):
> >> +SwitchToRealProcStart:
> >> +BITS 64
> >> +    cli
> >> +
> >> +    ;
> >> +    ; Get RDX reset value before changing stacks since the
> >> +    ; new stack won't be able to accomodate a #VC exception.
> >> +    ;
> >> +    push       rax
> >> +    push       rbx
> >> +    push       rcx
> >> +    push       rdx
> >> +
> >> +    mov        rax, 1
> >> +    cpuid
> >> +    mov        rsi, rax                    ; Save off the reset value for RDX
> >> +
> >> +    pop        rdx
> >> +    pop        rcx
> >> +    pop        rbx
> >> +    pop        rax
> >> +
> >> +    ;
> >> +    ; Establish stack below 1MB
> >> +    ;
> >> +    mov        rsp, r9
> >> +
> >> +    ;
> >> +    ; Push ultimate Reset Vector onto the stack
> >> +    ;
> >> +    mov        rax, rcx
> >> +    shr        rax, 4
> >> +    push       word 0x0002                 ; RFLAGS
> >> +    push       ax                          ; CS
> >> +    push       word 0x0000                 ; RIP
> >> +    push       word 0x0000                 ; For alignment, will be discarded
> >> +
> >> +    ;
> >> +    ; Get address of "16-bit operand size" label
> >> +    ;
> >> +    lea        rbx, [PM16Mode]
> >> +
> >> +    ;
> >> +    ; Push addresses used to change to compatibility mode
> >> +    ;
> >> +    lea        rax, [CompatMode]
> >> +    push       r8
> >> +    push       rax
> >> +
> >> +    ;
> >> +    ; Clear R8 - R15, for reset, before going into 32-bit mode
> >> +    ;
> >> +    xor        r8, r8
> >> +    xor        r9, r9
> >> +    xor        r10, r10
> >> +    xor        r11, r11
> >> +    xor        r12, r12
> >> +    xor        r13, r13
> >> +    xor        r14, r14
> >> +    xor        r15, r15
> >> +
> >> +    ;
> >> +    ; Far return into 32-bit mode
> >> +    ;
> >> +o64 retf
> >> +
> >> +BITS 32
> >> +CompatMode:
> >> +    ;
> >> +    ; Set up stack to prepare for exiting protected mode
> >> +    ;
> >> +    push       edx                         ; Code16 CS
> >> +    push       ebx                         ; PM16Mode label address
> >> +
> >> +    ;
> >> +    ; Disable paging
> >> +    ;
> >> +    mov        eax, cr0                    ; Read CR0
> >> +    btr        eax, 31                     ; Set PG=0
> >> +    mov        cr0, eax                    ; Write CR0
> >> +
> >> +    ;
> >> +    ; Disable long mode
> >> +    ;
> >> +    mov        ecx, 0c0000080h             ; EFER MSR number
> >> +    rdmsr                                  ; Read EFER
> >> +    btr        eax, 8                      ; Set LME=0
> >> +    wrmsr                                  ; Write EFER
> >> +
> >> +    ;
> >> +    ; Disable PAE
> >> +    ;
> >> +    mov        eax, cr4                    ; Read CR4
> >> +    btr        eax, 5                      ; Set PAE=0
> >> +    mov        cr4, eax                    ; Write CR4
> >> +
> >> +    mov        edx, esi                    ; Restore RDX reset value
> >> +
> >> +    ;
> >> +    ; Switch to 16-bit operand size
> >> +    ;
> >> +    retf
> >> +
> >> +BITS 16
> >> +    ;
> >> +    ; At entry to this label
> >> +    ;   - RDX will have its reset value
> >> +    ;   - On the top of the stack
> >> +    ;     - Alignment data (two bytes) to be discarded
> >> +    ;     - IP for Real Mode (two bytes)
> >> +    ;     - CS for Real Mode (two bytes)
> >> +    ;
> >> +PM16Mode:
> >> +    mov        eax, cr0                    ; Read CR0
> >> +    btr        eax, 0                      ; Set PE=0
> >> +    mov        cr0, eax                    ; Write CR0
> >> +
> >> +    pop        ax                          ; Discard alignment data
> >> +
> >> +    ;
> >> +    ; Clear registers (except RDX and RSP) before going into 16-bit mode
> >> +    ;
> >> +    xor        eax, eax
> >> +    xor        ebx, ebx
> >> +    xor        ecx, ecx
> >> +    xor        esi, esi
> >> +    xor        edi, edi
> >> +    xor        ebp, ebp
> >> +
> >> +    iret
> >> +
> >> +SwitchToRealProcEnd:
> >> +
> >>
> >> ;--------------------------------------------------------------------
> >> -----------------  ;  AsmRelocateApLoop (MwaitSupport,
> >> ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);
> >>
> >> ;--------------------------------------------------------------------
> >> -----------------
> >>  global ASM_PFX(AsmRelocateApLoop)
> >>  ASM_PFX(AsmRelocateApLoop):
> >>  AsmRelocateApLoopStart:
> >> +BITS 64
> >>      cli                          ; Disable interrupt before switching to 32-bit mode
> >>      mov        rax, [rsp + 40]   ; CountTofinish
> >>      lock dec   dword [rax]       ; (*CountTofinish)--
> >> @@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap):
> >>      mov        qword [rcx + 18h], rax
> >>      mov        qword [rcx + 20h], AsmRelocateApLoopEnd -
> >> AsmRelocateApLoopStart
> >>      mov        qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
> >> +    mov        qword [rcx + 30h], SwitchToRealProcEnd -
> >> SwitchToRealProcStart          ; SwitchToRealSize
> >> +    mov        qword [rcx + 38h], SwitchToRealProcStart -
> >> RendezvousFunnelProcStart    ; SwitchToRealOffset
> >> +    mov        qword [rcx + 40h], SwitchToRealProcStart -
> Flat32Start                  ;
> >> SwitchToRealNoNxOffset
> >> +    mov        qword [rcx + 48h], PM16Mode -
> >> RendezvousFunnelProcStart                 ; SwitchToRealPM16ModeOffset
> >> +    mov        qword [rcx + 50h], SwitchToRealProcEnd -
> PM16Mode                       ;
> >> SwitchToRealPM16ModeSize
> >>      ret
> >>
> >>
> >> ;--------------------------------------------------------------------
> >> -----------------
> >> --
> >> 2.17.1
> >

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

end of thread, other threads:[~2020-06-05  6:13 UTC | newest]

Thread overview: 100+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-05-19 21:50 [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 01/46] MdeModulePkg: Create PCDs to be used in support of SEV-ES Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 02/46] UefiCpuPkg: Create PCD " Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 03/46] MdePkg: Add the MSR definition for the GHCB register Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 04/46] MdePkg: Add a structure definition for the GHCB Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 05/46] MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page tables Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 06/46] MdePkg/BaseLib: Add support for the XGETBV instruction Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 07/46] MdePkg/BaseLib: Add support for the VMGEXIT instruction Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 08/46] UefiCpuPkg: Implement library support for VMGEXIT Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 09/46] OvmfPkg: Prepare OvmfPkg to use the VmgExitLib library Lendacky, Thomas
2020-05-21 16:42   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 10/46] UefiPayloadPkg: Prepare UefiPayloadPkg " Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 11/46] UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 12/46] OvmfPkg/VmgExitLib: Implement library support for VmgExitLib in OVMF Lendacky, Thomas
2020-05-21 16:52   ` [edk2-devel] " Laszlo Ersek
2020-05-21 17:08     ` Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 13/46] OvmfPkg/VmgExitLib: Add support for IOIO_PROT NAE events Lendacky, Thomas
2020-05-21 17:25   ` [edk2-devel] " Laszlo Ersek
2020-05-22 10:05     ` Laszlo Ersek
2020-05-22 13:41       ` Lendacky, Thomas
2020-05-22 13:40     ` Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 14/46] OvmfPkg/VmgExitLib: Support string IO " Lendacky, Thomas
2020-05-22 10:14   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 15/46] OvmfPkg/VmgExitLib: Add support for CPUID " Lendacky, Thomas
2020-05-22 10:27   ` [edk2-devel] " Laszlo Ersek
2020-05-22 19:02     ` Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 16/46] OvmfPkg/VmgExitLib: Add support for MSR_PROT " Lendacky, Thomas
2020-05-22 10:31   ` [edk2-devel] " Laszlo Ersek
2020-05-22 19:06     ` Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 17/46] OvmfPkg/VmgExitLib: Add support for NPF NAE events (MMIO) Lendacky, Thomas
2020-05-22 14:14   ` [edk2-devel] " Laszlo Ersek
2020-05-22 14:31     ` Laszlo Ersek
2020-05-22 20:41     ` Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 18/46] OvmfPkg/VmgExitLib: Add support for WBINVD NAE events Lendacky, Thomas
2020-05-22 14:19   ` [edk2-devel] " Laszlo Ersek
2020-05-22 20:51     ` Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 19/46] OvmfPkg/VmgExitLib: Add support for RDTSC " Lendacky, Thomas
2020-05-22 14:42   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 20/46] OvmfPkg/VmgExitLib: Add support for RDPMC " Lendacky, Thomas
2020-05-22 14:43   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 21/46] OvmfPkg/VmgExitLib: Add support for INVD " Lendacky, Thomas
2020-05-22 14:46   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 22/46] OvmfPkg/VmgExitLib: Add support for VMMCALL " Lendacky, Thomas
2020-05-22 14:48   ` [edk2-devel] " Laszlo Ersek
2020-05-22 14:50     ` Laszlo Ersek
2020-05-22 21:18       ` Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 23/46] OvmfPkg/VmgExitLib: Add support for RDTSCP " Lendacky, Thomas
2020-05-22 14:52   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 24/46] OvmfPkg/VmgExitLib: Add support for MONITOR/MONITORX " Lendacky, Thomas
2020-05-22 14:55   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 25/46] OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX " Lendacky, Thomas
2020-05-22 14:56   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write " Lendacky, Thomas
2020-05-22 14:59   ` [edk2-devel] " Laszlo Ersek
2020-05-25 14:47   ` Laszlo Ersek
2020-05-26 15:06     ` Lendacky, Thomas
2020-05-27 11:54       ` Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 27/46] OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 28/46] OvmfPkg: Add support to perform SEV-ES initialization Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 29/46] OvmfPkg: Create a GHCB page for use during Sec phase Lendacky, Thomas
2020-05-25 15:07   ` [edk2-devel] " Laszlo Ersek
2020-05-26 15:41     ` Lendacky, Thomas
2020-05-26 15:45       ` Lendacky, Thomas
2020-05-27 11:45       ` Laszlo Ersek
2020-05-19 21:50 ` [PATCH v8 30/46] OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported Lendacky, Thomas
2020-05-19 21:50 ` [PATCH v8 31/46] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase Lendacky, Thomas
2020-05-25 15:21   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:51 ` [PATCH v8 32/46] OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled Lendacky, Thomas
2020-05-19 21:51 ` [PATCH v8 33/46] UefiCpuPkg: Create an SEV-ES workarea PCD Lendacky, Thomas
2020-05-19 21:51 ` [PATCH v8 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage Lendacky, Thomas
2020-05-25 16:00   ` [edk2-devel] " Laszlo Ersek
2020-05-26 14:28     ` Lendacky, Thomas
2020-05-26 21:47       ` Lendacky, Thomas
2020-05-27 11:50         ` Laszlo Ersek
2020-05-19 21:51 ` [PATCH v8 35/46] OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported Lendacky, Thomas
2020-05-26  7:53   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:51 ` [PATCH v8 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check Lendacky, Thomas
2020-05-25 16:50   ` [edk2-devel] " Laszlo Ersek
2020-05-26 16:31     ` Lendacky, Thomas
2020-05-27 11:59       ` Laszlo Ersek
2020-05-19 21:51 ` [PATCH v8 37/46] OvmfPkg/Sec: Add #VC exception handling for Sec phase Lendacky, Thomas
2020-05-26 13:58   ` [edk2-devel] " Laszlo Ersek
2020-05-19 21:51 ` [PATCH v8 38/46] OvmfPkg/Sec: Enable cache early to speed up booting Lendacky, Thomas
2020-05-19 21:51 ` [PATCH v8 39/46] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES Lendacky, Thomas
2020-05-26 14:07   ` [edk2-devel] " Laszlo Ersek
2020-05-20  4:46 ` [PATCH v8 00/46] SEV-ES guest support Lendacky, Thomas
2020-05-20 16:56 ` [PATCH v8 40/46] UefiCpuPkg: Add a 16-bit protected mode code segment descriptor Lendacky, Thomas
2020-05-20 16:56 ` [PATCH v8 41/46] UefiCpuPkg/MpInitLib: Add CPU MP data flag to indicate if SEV-ES is enabled Lendacky, Thomas
2020-05-20 16:56 ` [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Lendacky, Thomas
2020-06-01  6:17   ` Dong, Eric
2020-06-01 16:10     ` Lendacky, Thomas
2020-06-05  6:13       ` Dong, Eric
2020-06-01  7:28   ` Dong, Eric
2020-06-01 16:58     ` Lendacky, Thomas
2020-05-20 16:56 ` [PATCH v8 43/46] OvmfPkg: Use the SEV-ES work area for the SEV-ES AP reset vector Lendacky, Thomas
2020-05-20 16:56 ` [PATCH v8 44/46] OvmfPkg: Move the GHCB allocations into reserved memory Lendacky, Thomas
2020-05-20 16:56 ` [PATCH v8 45/46] UefiCpuPkg/MpInitLib: Prepare SEV-ES guest APs for OS use Lendacky, Thomas
2020-05-20 16:56 ` [PATCH v8 46/46] Maintainers.txt: Add reviewers for the OvmfPkg SEV-related files Lendacky, Thomas
2020-05-19 21:54   ` Brijesh Singh
2020-05-26 14:12   ` [edk2-devel] " Laszlo Ersek

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