From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by mx.groups.io with SMTP id smtpd.web12.43610.1635772730191571034 for ; Mon, 01 Nov 2021 06:18:52 -0700 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: intel.com, ip: 192.55.52.120, mailfrom: min.m.xu@intel.com) X-IronPort-AV: E=McAfee;i="6200,9189,10154"; a="229737767" X-IronPort-AV: E=Sophos;i="5.87,199,1631602800"; d="scan'208";a="229737767" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 01 Nov 2021 06:18:34 -0700 X-IronPort-AV: E=Sophos;i="5.87,199,1631602800"; d="scan'208";a="500035816" Received: from mxu9-mobl1.ccr.corp.intel.com ([10.255.29.216]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 01 Nov 2021 06:18:31 -0700 From: "Min Xu" To: devel@edk2.groups.io Cc: Min Xu , Jian J Wang , Hao A Wu , Brijesh Singh , Erdem Aktas , James Bottomley , Jiewen Yao , Tom Lendacky , Gerd Hoffmann Subject: [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest Date: Mon, 1 Nov 2021 21:16:11 +0800 Message-Id: X-Mailer: git-send-email 2.29.2.windows.2 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 Cc: Hao A Wu Cc: Brijesh Singh Cc: Erdem Aktas Cc: James Bottomley Cc: Jiewen Yao Cc: Tom Lendacky Cc: Gerd Hoffmann Signed-off-by: Min Xu --- 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.
+;* 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