RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429

Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode.
When it is in ROMD mode, the device is mapped to guest memory and
satisfies read access directly.

In EDK2 Option ROM is treated as MMIO region. So Tdx guest access
Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set
the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return
INVALID_OPERAND. Tdvf then falls back to direct access. This requires
to set the shared bit to corresponding PageTable entry. Otherwise it
triggers GP fault.

The mmio region information is passed from VMM in TD Hob. So after the
1:1 identity mapping page table is created (before it is wrote to CR3),
this page table will be updated (set shared bit) based on the mmio region
information in the hob list.

PcdTdxSharedBitMask is created in MdeModulePkg.dec to indicate the shared
bit information. Its default value is 0 and it will be set in PlatformPei
driver if it is of Tdx guest.

Cc: Jian J Wang <jian.j.w...@intel.com>
Cc: Hao A Wu <hao.a...@intel.com>
Cc: Brijesh Singh <brijesh.si...@amd.com>
Cc: Erdem Aktas <erdemak...@google.com>
Cc: James Bottomley <j...@linux.ibm.com>
Cc: Jiewen Yao <jiewen....@intel.com>
Cc: Tom Lendacky <thomas.lenda...@amd.com>
Cc: Gerd Hoffmann <kra...@redhat.com>
Signed-off-by: Min Xu <min.m...@intel.com>
---
 MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf       |   2 +
 .../Core/DxeIplPeim/Ia32/DxeLoadFunc.c        |   2 +-
 .../Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm   | 146 ++++++++
 .../Core/DxeIplPeim/X64/VirtualMemory.c       | 325 +++++++++++++++++-
 .../Core/DxeIplPeim/X64/VirtualMemory.h       |  66 +++-
 MdeModulePkg/MdeModulePkg.dec                 |   6 +
 6 files changed, 539 insertions(+), 8 deletions(-)
 create mode 100644 MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm

diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf 
b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
index 106b679b6bd0..b964277133c0 100644
--- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
@@ -43,6 +43,7 @@
   X64/VirtualMemory.h
   X64/VirtualMemory.c
   X64/DxeLoadFunc.c
+  X64/DxeIplTdVmcall.nasm
 
 [Sources.EBC]
   Ebc/DxeLoadFunc.c
@@ -118,6 +119,7 @@
   gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                            ## 
CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize                            ## 
CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdIa32EferChangeAllowed               ## 
CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask                    ## 
CONSUMES
 
 [Pcd.IA32,Pcd.X64,Pcd.ARM,Pcd.AARCH64]
   gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack               ## 
SOMETIMES_CONSUMES
diff --git a/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c 
b/MdeModulePkg/Core/DxeIplPeim/Ia32/DxeLoadFunc.c
index 284b34818ca7..cd60f8139205 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, 0, 0);
+        Split2MPageTo4K (PhysicalAddress, (UINT64 *) PageDirectoryEntry, 
StackBase, StackSize, 0, 0, 0);
       } else {
         //
         // Fill in the Page Directory entries
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm 
b/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm
new file mode 100644
index 000000000000..c55de3b89f93
--- /dev/null
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/DxeIplTdVmcall.nasm
@@ -0,0 +1,146 @@
+;------------------------------------------------------------------------------
+;*
+;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+;* SPDX-License-Identifier: BSD-2-Clause-Patent
+;*
+;*
+;------------------------------------------------------------------------------
+
+DEFAULT REL
+SECTION .text
+
+%define TDVMCALL_EXPOSE_REGS_MASK       0xffec
+%define TDVMCALL                        0x0
+%define EXIT_REASON_CPUID               0xa
+
+%macro tdcall 0
+    db 0x66,0x0f,0x01,0xcc
+%endmacro
+
+%macro tdcall_push_regs 0
+    push rbp
+    mov  rbp, rsp
+    push r15
+    push r14
+    push r13
+    push r12
+    push rbx
+    push rsi
+    push rdi
+%endmacro
+
+%macro tdcall_pop_regs 0
+    pop rdi
+    pop rsi
+    pop rbx
+    pop r12
+    pop r13
+    pop r14
+    pop r15
+    pop rbp
+%endmacro
+
+%define number_of_regs_pushed 8
+%define number_of_parameters  4
+
+;
+; Keep these in sync for push_regs/pop_regs, code below
+; uses them to find 5th or greater parameters
+;
+%define first_variable_on_stack_offset \
+  ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8)
+%define second_variable_on_stack_offset \
+  ((first_variable_on_stack_offset) + 8)
+
+%macro tdcall_regs_preamble 2
+    mov rax, %1
+
+    xor rcx, rcx
+    mov ecx, %2
+
+    ; R10 = 0 (standard TDVMCALL)
+
+    xor r10d, r10d
+
+    ; Zero out unused (for standard TDVMCALL) registers to avoid leaking
+    ; secrets to the VMM.
+
+    xor ebx, ebx
+    xor esi, esi
+    xor edi, edi
+
+    xor edx, edx
+    xor ebp, ebp
+    xor r8d, r8d
+    xor r9d, r9d
+%endmacro
+
+%macro tdcall_regs_postamble 0
+    xor ebx, ebx
+    xor esi, esi
+    xor edi, edi
+
+    xor ecx, ecx
+    xor edx, edx
+    xor r8d,  r8d
+    xor r9d,  r9d
+    xor r10d, r10d
+    xor r11d, r11d
+%endmacro
+
+;------------------------------------------------------------------------------
+; 0   => RAX = TDCALL leaf
+; M   => RCX = TDVMCALL register behavior
+; 1   => R10 = standard vs. vendor
+; RDI => R11 = TDVMCALL function / nr
+; RSI =  R12 = p1
+; RDX => R13 = p2
+; RCX => R14 = p3
+; R8  => R15 = p4
+
+;  UINT64
+;  EFIAPI
+;  DxeIplTdVmCall (
+;    UINT64  Leaf,  // Rcx
+;    UINT64  P1,  // Rdx
+;    UINT64  P2,  // R8
+;    UINT64  P3,  // R9
+;    UINT64  P4,  // rsp + 0x28
+;    UINT64  *Val // rsp + 0x30
+;    )
+global ASM_PFX(DxeIplTdVmCall)
+ASM_PFX(DxeIplTdVmCall):
+       tdcall_push_regs
+
+       mov r11, rcx
+       mov r12, rdx
+       mov r13, r8
+       mov r14, r9
+       mov r15, [rsp + first_variable_on_stack_offset ]
+
+       tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
+
+       tdcall
+
+       ; ignore return dataif TDCALL reports failure.
+       test rax, rax
+       jnz .no_return_data
+
+       ; Propagate TDVMCALL success/failure to return value.
+       mov rax, r10
+
+       ; Retrieve the Val pointer.
+       mov r9, [rsp + second_variable_on_stack_offset ]
+       test r9, r9
+       jz .no_return_data
+
+       ; On success, propagate TDVMCALL output value to output param
+       test rax, rax
+       jnz .no_return_data
+       mov [r9], r11
+.no_return_data:
+       tdcall_regs_postamble
+
+       tdcall_pop_regs
+
+       ret
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c 
b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
index 8a3b72509310..c7cb7f007b4f 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c
@@ -26,6 +26,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include "DxeIpl.h"
 #include "VirtualMemory.h"
 
+#define TDVMCALL_MAPGPA                 0x10001
+
 //
 // Global variable to keep track current available memory used as page table.
 //
@@ -340,6 +342,7 @@ AllocatePageTableMemory (
   @param[in]      StackSize             Stack size.
   @param[in]      GhcbBase              GHCB page area base address.
   @param[in]      GhcbSize              GHCB page area size.
+  @param[in]      SharedBitMask         Bit mask for Tdx shared memory.
 
 **/
 VOID
@@ -349,7 +352,8 @@ Split2MPageTo4K (
   IN EFI_PHYSICAL_ADDRESS               StackBase,
   IN UINTN                              StackSize,
   IN EFI_PHYSICAL_ADDRESS               GhcbBase,
-  IN UINTN                              GhcbSize
+  IN UINTN                              GhcbSize,
+  IN UINT64                             SharedBitMask
   )
 {
   EFI_PHYSICAL_ADDRESS                  PhysicalAddress4K;
@@ -361,6 +365,10 @@ Split2MPageTo4K (
   // Make sure AddressEncMask is contained to smallest supported address field
   //
   AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & 
PAGING_1G_ADDRESS_MASK_64;
+  if (SharedBitMask != 0) {
+    ASSERT (AddressEncMask == 0);
+    AddressEncMask = SharedBitMask;
+  }
 
   PageTableEntry = AllocatePageTableMemory (1);
   ASSERT (PageTableEntry != NULL);
@@ -418,6 +426,7 @@ Split2MPageTo4K (
   @param[in]      StackSize             Stack size.
   @param[in]      GhcbBase              GHCB page area base address.
   @param[in]      GhcbSize              GHCB page area size.
+  @param[in]      SharedBitMask         Bit mask for Tdx shared memory.
 
 **/
 VOID
@@ -427,7 +436,8 @@ Split1GPageTo2M (
   IN EFI_PHYSICAL_ADDRESS               StackBase,
   IN UINTN                              StackSize,
   IN EFI_PHYSICAL_ADDRESS               GhcbBase,
-  IN UINTN                              GhcbSize
+  IN UINTN                              GhcbSize,
+  IN UINT64                             SharedBitMask
   )
 {
   EFI_PHYSICAL_ADDRESS                  PhysicalAddress2M;
@@ -440,6 +450,11 @@ Split1GPageTo2M (
   //
   AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & 
PAGING_1G_ADDRESS_MASK_64;
 
+  if (SharedBitMask != 0) {
+    ASSERT (AddressEncMask == 0);
+    AddressEncMask = *PageEntry1G & SharedBitMask;
+  }
+
   PageDirectoryEntry = AllocatePageTableMemory (1);
   ASSERT (PageDirectoryEntry != NULL);
 
@@ -454,7 +469,7 @@ Split1GPageTo2M (
       //
       // Need to split this 2M page that covers NULL or stack range.
       //
-      Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, 
StackBase, StackSize, GhcbBase, GhcbSize);
+      Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, 
StackBase, StackSize, GhcbBase, GhcbSize, SharedBitMask == 0 ? 0 : 
AddressEncMask);
     } else {
       //
       // Fill in the Page Directory entries
@@ -647,6 +662,296 @@ EnablePageTableProtection (
   AsmWriteCr0 (AsmReadCr0() | CR0_WP);
 }
 
+#ifdef MDE_CPU_X64
+/**
+  Set the memory shared bit
+
+  @param[in,out]      PageTablePointer  Page table entry pointer (PTE).
+  @param[in]          PhysicalAddress   The physical address that is the start
+                                        address of a memory region
+  @param[in]          Length            Length of the memory region
+  @param[in]          SharedBitMask     Shared bit mask
+
+  @retval             Return status of DxeIplTdVmCall
+**/
+UINT64
+SetSharedBit(
+  IN   OUT     UINT64*                PageTablePointer,
+  IN           PHYSICAL_ADDRESS       PhysicalAddress,
+  IN           UINT64                 Length,
+  IN           UINT64                 SharedBitMask
+  )
+{
+  UINT64      Status;
+
+  *PageTablePointer |= SharedBitMask;
+  PhysicalAddress   |= SharedBitMask;
+
+  Status = DxeIplTdVmCall(TDVMCALL_MAPGPA, PhysicalAddress, Length, 0, 0, 
NULL);
+
+  return Status;
+}
+
+/**
+  This function sets the shared bit for the memory region specified by
+  PhysicalAddress and Length from the current page table  context.
+
+  The function iterates through the PhysicalAddress one page at a time, and set
+  or clears the memory encryption in the page table. If it encounters
+  that a given physical address range is part of large page then it attempts to
+  change the attribute at one go (based on size), otherwise it splits the
+  large pages into smaller (e.g 2M page into 4K pages) and then try to set or
+  clear the encryption bit on the smallest page size.
+
+  @param[in]  PageTableBaseAddress    Base Address of Page table
+  @param[in]  Page5LevelSupport       Indicates if Level-5 paging supported
+  @param[in]  SharedBitMask           Shared bit mask for the memory region
+  @param[in]  PhysicalAddress         The physical address that is the start
+                                      address of a memory region.
+  @param[in]  Pages                   Number of pages of memory region
+
+  @retval EFI_SUCCESS                 The shared bit is set successfully.
+  @retval EFI_INVALID_PARAMETER       Number of pages is zero.
+  @retval EFI_NO_MAPPING              Physical address is not mapped in 
PageTable
+**/
+EFI_STATUS
+SetMemorySharedBit (
+  IN    PHYSICAL_ADDRESS         PageTableBaseAddress,
+  IN    BOOLEAN                  Page5LevelSupport,
+  IN    UINT64                   SharedBitMask,
+  IN    PHYSICAL_ADDRESS         PhysicalAddress,
+  IN    UINTN                    Pages
+  )
+{
+  EFI_STATUS                     Status;
+  PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+  PAGE_MAP_AND_DIRECTORY_POINTER *PageUpperDirectoryPointerEntry;
+  PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
+  PAGE_TABLE_1G_ENTRY            *PageDirectory1GEntry;
+  PAGE_TABLE_ENTRY               *PageDirectory2MEntry;
+  PAGE_TABLE_4K_ENTRY            *PageTableEntry;
+  UINT64                         PgTableMask;
+  UINT64                         ActiveSharedBitMask;
+  UINTN                          Length;
+
+  if (Pages == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status              = EFI_SUCCESS;
+  PageMapLevel4Entry  = NULL;
+  PgTableMask         = SharedBitMask | EFI_PAGE_MASK;
+  Length              = EFI_PAGES_TO_SIZE (Pages);
+
+  //
+  // If 5-level pages, adjust PageTableBaseAddress to point to first 4-level 
page directory,
+  // we will only have 1
+  //
+  if (Page5LevelSupport) {
+    PageTableBaseAddress = *(UINT64 *)PageTableBaseAddress & ~PgTableMask;
+  }
+
+  while (Length > 0) {
+    PageMapLevel4Entry  = (VOID*) (PageTableBaseAddress & ~PgTableMask);
+    PageMapLevel4Entry += PML4_OFFSET (PhysicalAddress);
+    if (!PageMapLevel4Entry->Bits.Present) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "%a:%a: bad PML4 for Physical=0x%Lx\n",
+        gEfiCallerBaseName,
+        __FUNCTION__,
+        PhysicalAddress
+        ));
+      Status = EFI_NO_MAPPING;
+      break;
+    }
+
+    PageDirectory1GEntry  = (VOID 
*)((PageMapLevel4Entry->Bits.PageTableBaseAddress << 12) & ~PgTableMask);
+    PageDirectory1GEntry += PDP_OFFSET (PhysicalAddress);
+    if (!PageDirectory1GEntry->Bits.Present) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "%a:%a: bad PDPE for Physical=0x%Lx\n",
+        gEfiCallerBaseName,
+        __FUNCTION__,
+        PhysicalAddress
+        ));
+      Status = EFI_NO_MAPPING;
+      break;
+    }
+
+    //
+    // If the MustBe1 bit is not 1, it's not actually a 1GB entry
+    //
+    if (PageDirectory1GEntry->Bits.MustBe1) {
+      //
+      // Valid 1GB page
+      // If we have at least 1GB to go, we can just update this entry
+      //
+      if ((PhysicalAddress & (BIT30 - 1)) == 0 && Length >= BIT30) {
+        SetSharedBit (&PageDirectory1GEntry->Uint64, PhysicalAddress, BIT30, 
SharedBitMask);
+        DEBUG ((
+          DEBUG_VERBOSE,
+          "%a:%a: updated 1GB entry for Physical=0x%Lx\n",
+          gEfiCallerBaseName,
+          __FUNCTION__,
+          PhysicalAddress
+          ));
+        PhysicalAddress += BIT30;
+        Length          -= BIT30;
+      } else {
+        //
+        // We must split the page
+        //
+        DEBUG ((
+          DEBUG_VERBOSE,
+          "%a:%a: splitting 1GB page for Physical=0x%Lx\n",
+          gEfiCallerBaseName,
+          __FUNCTION__,
+          PhysicalAddress
+          ));
+        Split1GPageTo2M (
+          (UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress << 30,
+          (UINT64 *)PageDirectory1GEntry,
+          0, 0, 0, 0,
+          SharedBitMask
+          );
+        continue;
+      }
+    } else {
+      //
+      // Actually a PDP
+      //
+      PageUpperDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER 
*)PageDirectory1GEntry;
+      PageDirectory2MEntry = (VOID 
*)((PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress <<12) & 
~PgTableMask);
+      PageDirectory2MEntry += PDE_OFFSET (PhysicalAddress);
+      if (!PageDirectory2MEntry->Bits.Present) {
+        DEBUG ((
+          DEBUG_ERROR,
+          "%a:%a: bad PDE for Physical=0x%Lx\n",
+          gEfiCallerBaseName,
+          __FUNCTION__,
+          PhysicalAddress
+          ));
+        Status = EFI_NO_MAPPING;
+        break;
+      }
+      //
+      // If the MustBe1 bit is not a 1, it's not a 2MB entry
+      //
+      if (PageDirectory2MEntry->Bits.MustBe1) {
+        //
+        // Valid 2MB page
+        // If we have at least 2MB left to go, we can just update this entry
+        //
+        if ((PhysicalAddress & (BIT21-1)) == 0 && Length >= BIT21) {
+          SetSharedBit (&PageDirectory2MEntry->Uint64, PhysicalAddress, BIT21, 
SharedBitMask);
+          PhysicalAddress += BIT21;
+          Length          -= BIT21;
+        } else {
+          //
+          // We must split up this page into 4K pages
+          //
+          DEBUG ((
+            DEBUG_VERBOSE,
+            "%a:%a: splitting 2MB page for Physical=0x%Lx\n",
+            gEfiCallerBaseName,
+            __FUNCTION__,
+            PhysicalAddress
+            ));
+
+          ActiveSharedBitMask = PageDirectory2MEntry->Uint64 & SharedBitMask;
+
+          Split2MPageTo4K (
+            (UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress << 21,
+            (UINT64 *)PageDirectory2MEntry,
+            0, 0, 0, 0,
+            ActiveSharedBitMask
+            );
+          continue;
+        }
+      } else {
+        PageDirectoryPointerEntry =(PAGE_MAP_AND_DIRECTORY_POINTER 
*)PageDirectory2MEntry;
+        PageTableEntry = (VOID 
*)((PageDirectoryPointerEntry->Bits.PageTableBaseAddress <<12) & ~PgTableMask);
+        PageTableEntry += PTE_OFFSET(PhysicalAddress);
+        if (!PageTableEntry->Bits.Present) {
+          DEBUG ((
+            DEBUG_ERROR,
+            "%a:%a: bad PTE for Physical=0x%Lx\n",
+            gEfiCallerBaseName,
+            __FUNCTION__,
+            PhysicalAddress
+            ));
+          Status = EFI_NO_MAPPING;
+          break;
+        }
+        SetSharedBit (&PageTableEntry->Uint64, PhysicalAddress, EFI_PAGE_SIZE, 
SharedBitMask);
+        PhysicalAddress += EFI_PAGE_SIZE;
+        Length          -= EFI_PAGE_SIZE;
+      }
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Set the shared bit for mmio region in Tdx guest.
+
+  In Tdx guest there are 2 ways to access mmio, TdVmcall or direct access.
+  For direct access, the shared bit of the PageTableEntry should be set.
+  The mmio region information is retrieved from hob list.
+
+  @param[in]  PageTableBaseAddress    Base Address of Page table.
+  @param[in]  Page5LevelSupport       Indicates if Level-5 paging is supported.
+
+  @retval EFI_SUCCESS                 The shared bit is set successfully.
+  @retval EFI_UNSUPPORTED             Setting the shared bit of memory region
+                                      is not supported
+**/
+EFI_STATUS
+DxeIplSetMmioSharedBit (
+  IN UINT64    PageTableBaseAddress,
+  IN BOOLEAN   Page5LevelSupport
+  )
+{
+  EFI_PEI_HOB_POINTERS      Hob;
+  UINT64                    SharedBitMask;
+
+  //
+  // Check if we have a valid memory shared bit mask
+  //
+  SharedBitMask = PcdGet64 (PcdTdxSharedBitMask) & PAGING_1G_ADDRESS_MASK_64;
+  if (SharedBitMask == 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+
+  Hob.Raw = (UINT8 *) GetHobList ();
+
+  //
+  // Parse the HOB list until end of list or matching type is found.
+  //
+  while (!END_OF_HOB_LIST (Hob)) {
+    if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
+        && Hob.ResourceDescriptor->ResourceType == 
EFI_RESOURCE_MEMORY_MAPPED_IO) {
+
+      SetMemorySharedBit (
+        PageTableBaseAddress,
+        Page5LevelSupport,
+        SharedBitMask,
+        Hob.ResourceDescriptor->PhysicalStart,
+        EFI_SIZE_TO_PAGES (Hob.ResourceDescriptor->ResourceLength));
+    }
+
+    Hob.Raw = GET_NEXT_HOB (Hob);
+  }
+
+  return EFI_SUCCESS;
+}
+
+#endif
+
 /**
   Allocates and fills in the Page Directory and Page Table Entries to
   establish a 1:1 Virtual to Physical mapping.
@@ -851,7 +1156,7 @@ CreateIdentityMappingPageTables (
 
         for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 
512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += 
SIZE_1GB) {
           if (ToSplitPageTable (PageAddress, SIZE_1GB, StackBase, StackSize, 
GhcbBase, GhcbSize)) {
-            Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, 
StackBase, StackSize, GhcbBase, GhcbSize);
+            Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, 
StackBase, StackSize, GhcbBase, GhcbSize, 0);
           } else {
             //
             // Fill in the Page Directory entries
@@ -885,7 +1190,7 @@ CreateIdentityMappingPageTables (
               //
               // Need to split this 2M page that covers NULL or stack range.
               //
-              Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, 
StackBase, StackSize, GhcbBase, GhcbSize);
+              Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, 
StackBase, StackSize, GhcbBase, GhcbSize, 0);
             } else {
               //
               // Fill in the Page Directory entries
@@ -921,6 +1226,15 @@ CreateIdentityMappingPageTables (
     ZeroMem (PageMapLevel5Entry, (512 - IndexOfPml5Entries) * sizeof 
(PAGE_MAP_AND_DIRECTORY_POINTER));
   }
 
+#ifdef MDE_CPU_X64
+  //
+  // Set shared bit for TDX
+  //
+  if (PcdGet64 (PcdTdxSharedBitMask) != 0) {
+    DxeIplSetMmioSharedBit ((UINTN)PageMap, Page5LevelSupport);
+  }
+#endif
+
   //
   // Protect the page table by marking the memory used for page table to be
   // read-only.
@@ -936,4 +1250,3 @@ CreateIdentityMappingPageTables (
 
   return (UINTN)PageMap;
 }
-
diff --git a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h 
b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
index 6b7c38a441d6..6223bb69c403 100644
--- a/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
+++ b/MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h
@@ -19,6 +19,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 
 #define SYS_CODE64_SEL 0x38
 
+#define PAGETABLE_ENTRY_MASK        ((1UL << 9) - 1)
+#define PML4_OFFSET(x)              ( (x >> 39) & PAGETABLE_ENTRY_MASK)
+#define PDP_OFFSET(x)               ( (x >> 30) & PAGETABLE_ENTRY_MASK)
+#define PDE_OFFSET(x)               ( (x >> 21) & PAGETABLE_ENTRY_MASK)
+#define PTE_OFFSET(x)               ( (x >> 12) & PAGETABLE_ENTRY_MASK)
 
 #pragma pack(1)
 
@@ -203,6 +208,7 @@ EnableExecuteDisableBit (
   @param[in]      StackSize             Stack size.
   @param[in]      GhcbBase              GHCB page area base address.
   @param[in]      GhcbSize              GHCB page area size.
+  @param[in]      SharedBitMask         Bit mask for Tdx shared memory.
 
 **/
 VOID
@@ -212,7 +218,8 @@ Split2MPageTo4K (
   IN EFI_PHYSICAL_ADDRESS               StackBase,
   IN UINTN                              StackSize,
   IN EFI_PHYSICAL_ADDRESS               GhcbBase,
-  IN UINTN                              GhcbSize
+  IN UINTN                              GhcbSize,
+  IN UINT64                             SharedBitMask
   );
 
 /**
@@ -327,4 +334,61 @@ AllocatePageTableMemory (
   IN UINTN           Pages
   );
 
+#ifdef MDE_CPU_X64
+/**
+  This function sets the shared bit for the memory region specified by
+  PhysicalAddress and Length from the current page table  context.
+
+  The function iterates through the PhysicalAddress one page at a time, and set
+  or clears the memory encryption in the page table. If it encounters
+  that a given physical address range is part of large page then it attempts to
+  change the attribute at one go (based on size), otherwise it splits the
+  large pages into smaller (e.g 2M page into 4K pages) and then try to set or
+  clear the encryption bit on the smallest page size.
+
+  @param[in]  PageTableBaseAddress    Base Address of Page table
+  @param[in]  Page5LevelSupport       Indicates if Level-5 paging supported
+  @param[in]  PhysicalAddress         The physical address that is the start
+                                      address of a memory region.
+  @param[in]  Pages                   Number of pages of memory region
+
+  @retval EFI_SUCCESS                 The shared bit is set successfully.
+  @retval EFI_INVALID_PARAMETER       Number of pages is zero.
+  @retval EFI_NO_MAPPING              Physical address is not mapped in 
PageTable
+**/
+EFI_STATUS
+SetMemorySharedBit (
+  IN    PHYSICAL_ADDRESS         PageTableBaseAddress,
+  IN    BOOLEAN                  Page5LevelSupport,
+  IN    UINT64                   SharedBitMask,
+  IN    PHYSICAL_ADDRESS         PhysicalAddress,
+  IN    UINTN                    Pages
+  );
+
+/**
+  TDVMALL is a leaf function 0 for TDCALL. It helps invoke services from the
+  host VMM to pass/receive information.
+
+  @param[in]     Leaf        Number of sub-functions
+  @param[in]     Arg1        Arg1
+  @param[in]     Arg2        Arg2
+  @param[in]     Arg3        Arg3
+  @param[in]     Arg4        Arg4
+  @param[in,out] Results     Returned result of the sub-function
+
+  @return EFI_SUCCESS
+  @return Other           See individual sub-functions
+
+**/
+EFI_STATUS
+DxeIplTdVmCall (
+  IN UINT64          Leaf,
+  IN UINT64          Arg1,
+  IN UINT64          Arg2,
+  IN UINT64          Arg3,
+  IN UINT64          Arg4,
+  IN OUT VOID        *Results
+  );
+#endif
+
 #endif
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 007044a311c2..4d8f7f5cc55b 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -2143,6 +2143,12 @@
   # @Prompt The flag which indicates if IA32_EFER is allowed to be changed.
   
gEfiMdeModulePkgTokenSpaceGuid.PcdIa32EferChangeAllowed|TRUE|BOOLEAN|0x00030009
 
+  ## This PCD holds the shared bit mask for page table entries when Tdx is 
enabled.
+  #  This mask should be applied to mmio region when creating 1:1 virtual to 
physical
+  #  mapping tables.
+  # @Prompt The shared bit mask when Intel Tdx is enabled.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask|0x0|UINT64|0x0003000a
+
 [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.
-- 
2.29.2.windows.2



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#82977): https://edk2.groups.io/g/devel/message/82977
Mute This Topic: https://groups.io/mt/86739887/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to