]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/MemEncryptSevLib: add support to validate > 4GB memory in PEI phase
authorBrijesh Singh via groups.io <brijesh.singh=amd.com@groups.io>
Thu, 9 Dec 2021 03:27:45 +0000 (11:27 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Thu, 9 Dec 2021 06:28:10 +0000 (06:28 +0000)
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

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

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

Cc: Michael Roth <michael.roth@amd.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Jiewen Yao <Jiewen.yao@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiDxeVirtualMemory.c
OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h

index bbc48ff6d87903f8904ac6a93db9fbb7c2bbbb6e..f1485722f7cf22abade68fd17f06393750576dfe 100644 (file)
@@ -536,6 +536,120 @@ EnableReadOnlyPageWriteProtect (
   AsmWriteCr0 (AsmReadCr0 () | BIT16);\r
 }\r
 \r
+RETURN_STATUS\r
+EFIAPI\r
+InternalMemEncryptSevCreateIdentityMap1G (\r
+  IN    PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN    PHYSICAL_ADDRESS  PhysicalAddress,\r
+  IN    UINTN             Length\r
+  )\r
+{\r
+  PAGE_MAP_AND_DIRECTORY_POINTER  *PageMapLevel4Entry;\r
+  PAGE_TABLE_1G_ENTRY             *PageDirectory1GEntry;\r
+  UINT64                          PgTableMask;\r
+  UINT64                          AddressEncMask;\r
+  BOOLEAN                         IsWpEnabled;\r
+  RETURN_STATUS                   Status;\r
+\r
+  //\r
+  // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.\r
+  //\r
+  PageMapLevel4Entry = NULL;\r
+\r
+  DEBUG ((\r
+    DEBUG_VERBOSE,\r
+    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx\n",\r
+    gEfiCallerBaseName,\r
+    __FUNCTION__,\r
+    Cr3BaseAddress,\r
+    PhysicalAddress,\r
+    (UINT64)Length\r
+    ));\r
+\r
+  if (Length == 0) {\r
+    return RETURN_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check if we have a valid memory encryption mask\r
+  //\r
+  AddressEncMask = InternalGetMemEncryptionAddressMask ();\r
+  if (!AddressEncMask) {\r
+    return RETURN_ACCESS_DENIED;\r
+  }\r
+\r
+  PgTableMask = AddressEncMask | EFI_PAGE_MASK;\r
+\r
+  //\r
+  // Make sure that the page table is changeable.\r
+  //\r
+  IsWpEnabled = IsReadOnlyPageWriteProtected ();\r
+  if (IsWpEnabled) {\r
+    DisableReadOnlyPageWriteProtect ();\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  while (Length) {\r
+    //\r
+    // If Cr3BaseAddress is not specified then read the current CR3\r
+    //\r
+    if (Cr3BaseAddress == 0) {\r
+      Cr3BaseAddress = AsmReadCr3 ();\r
+    }\r
+\r
+    PageMapLevel4Entry  = (VOID *)(Cr3BaseAddress & ~PgTableMask);\r
+    PageMapLevel4Entry += PML4_OFFSET (PhysicalAddress);\r
+    if (!PageMapLevel4Entry->Bits.Present) {\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a:%a: bad PML4 for Physical=0x%Lx\n",\r
+        gEfiCallerBaseName,\r
+        __FUNCTION__,\r
+        PhysicalAddress\r
+        ));\r
+      Status = RETURN_NO_MAPPING;\r
+      goto Done;\r
+    }\r
+\r
+    PageDirectory1GEntry = (VOID *)(\r
+                                    (PageMapLevel4Entry->Bits.PageTableBaseAddress <<\r
+                                     12) & ~PgTableMask\r
+                                    );\r
+    PageDirectory1GEntry += PDP_OFFSET (PhysicalAddress);\r
+    if (!PageDirectory1GEntry->Bits.Present) {\r
+      PageDirectory1GEntry->Bits.Present    = 1;\r
+      PageDirectory1GEntry->Bits.MustBe1    = 1;\r
+      PageDirectory1GEntry->Bits.MustBeZero = 0;\r
+      PageDirectory1GEntry->Bits.ReadWrite  = 1;\r
+      PageDirectory1GEntry->Uint64         |= (UINT64)PhysicalAddress | AddressEncMask;\r
+    }\r
+\r
+    if (Length <= BIT30) {\r
+      Length = 0;\r
+    } else {\r
+      Length -= BIT30;\r
+    }\r
+\r
+    PhysicalAddress += BIT30;\r
+  }\r
+\r
+  //\r
+  // Flush TLB\r
+  //\r
+  CpuFlushTlb ();\r
+\r
+Done:\r
+  //\r
+  // Restore page table write protection, if any.\r
+  //\r
+  if (IsWpEnabled) {\r
+    EnableReadOnlyPageWriteProtect ();\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
 /**\r
   This function either sets or clears memory encryption bit for the memory\r
   region specified by PhysicalAddress and Length from the current page table\r
index 2d2136f8054c6756a50cf281554958b20131e864..0e3eba3c5121e02149edc04edf4f903dcf560e3d 100644 (file)
 \r
 #include <Uefi/UefiBaseType.h>\r
 #include <Library/BaseLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/DebugLib.h>\r
 #include <Library/MemEncryptSevLib.h>\r
 \r
 #include "SnpPageStateChange.h"\r
+#include "VirtualMemory.h"\r
 \r
 typedef struct {\r
   UINT64    StartAddress;\r
@@ -70,6 +73,7 @@ MemEncryptSevSnpPreValidateSystemRam (
 {\r
   PHYSICAL_ADDRESS         EndAddress;\r
   SNP_PRE_VALIDATED_RANGE  OverlapRange;\r
+  EFI_STATUS               Status;\r
 \r
   if (!MemEncryptSevSnpIsEnabled ()) {\r
     return;\r
@@ -77,6 +81,24 @@ MemEncryptSevSnpPreValidateSystemRam (
 \r
   EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);\r
 \r
+  //\r
+  // The page table used in PEI can address up to 4GB memory. If we are asked to\r
+  // validate a range above the 4GB, then create an identity mapping so that the\r
+  // PVALIDATE instruction can execute correctly. If the page table entry is not\r
+  // present then PVALIDATE will #GP.\r
+  //\r
+  if (BaseAddress >= SIZE_4GB) {\r
+    Status = InternalMemEncryptSevCreateIdentityMap1G (\r
+               0,\r
+               BaseAddress,\r
+               EFI_PAGES_TO_SIZE (NumPages)\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      ASSERT (FALSE);\r
+      CpuDeadLoop ();\r
+    }\r
+  }\r
+\r
   while (BaseAddress < EndAddress) {\r
     //\r
     // Check if the range overlaps with the pre-validated ranges.\r
index 93e3d08589d779ef70018b504913f9f6e9e56bac..ffc7430b2243b7e7db4269674c55e1ad2eda2289 100644 (file)
@@ -144,4 +144,28 @@ InternalMemEncryptSevClearMmioPageEncMask (
   IN  UINTN             Length\r
   );\r
 \r
+/**\r
+  Create 1GB identity mapping for the specified virtual address range.\r
+\r
+  The function is preliminary used by the SEV-SNP page state change\r
+  APIs to build the page table required before issuing the PVALIDATE\r
+  instruction. The function must be removed after the EDK2 core is\r
+  enhanced to do the lazy validation.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  VirtualAddress          Virtual address\r
+  @param[in]  Length                  Length of virtual address range\r
+\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+InternalMemEncryptSevCreateIdentityMap1G (\r
+  IN    PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN    PHYSICAL_ADDRESS  PhysicalAddress,\r
+  IN    UINTN             Length\r
+  );\r
+\r
 #endif\r