[edk2-devel] [RFC PATCH 11/14] OvmfPkg/AmdSev: Build page table for migration handler

Dov Murik dovmurik at linux.vnet.ibm.com
Wed Mar 3 18:58:43 UTC 2021



On 03/03/2021 18:32, Ashish Kalra wrote:
> On Tue, Mar 02, 2021 at 03:48:36PM -0500, Tobin Feldman-Fitzthum wrote:
>> From: Dov Murik <dovmurik at linux.ibm.com>
>>
>> The migration handler builds its own page tables and switches
>> to them. The MH pagetables are reserved as runtime memory.
>>
>> When the hypervisor asks the MH to import/export a page, the HV
>> writes the guest physical address of the page in question to the
>> mailbox. The MH uses an identity mapping so that it can read/write
>> whatever GPA is requested by the HV. The hypervisor only asks the
>> MH to import/export encrypted pages. Thus, the C-Bit can be set
>> for every page in the identity map.
>>
>> The MH also needs to read shared pages, such as the mailbox.
>> These are mapped at an offset. The offset must be added to
>> the physical address before it can be resolved.
>>
>> Signed-off-by: Tobin Feldman-Fitzthum <tobin at linux.ibm.com>
>> Signed-off-by: Dov Murik <dovmurik at linux.vnet.ibm.com>
>> ---
>>  .../ConfidentialMigrationDxe.inf              |   1 +
>>  .../ConfidentialMigration/VirtualMemory.h     | 177 ++++++++++++++++++
>>  .../ConfidentialMigrationDxe.c                |  88 ++++++++-
>>  3 files changed, 265 insertions(+), 1 deletion(-)
>>  create mode 100644 OvmfPkg/AmdSev/ConfidentialMigration/VirtualMemory.h
>>
>> diff --git a/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.inf b/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.inf
>> index 49457d5d17..8dadfd1d13 100644
>> --- a/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.inf
>> +++ b/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.inf
>> @@ -15,6 +15,7 @@
>>  
>>  [Sources]
>>    ConfidentialMigrationDxe.c
>> +  VirtualMemory.h
>>  
>>  [Packages]
>>    MdePkg/MdePkg.dec
>> diff --git a/OvmfPkg/AmdSev/ConfidentialMigration/VirtualMemory.h b/OvmfPkg/AmdSev/ConfidentialMigration/VirtualMemory.h
>> new file mode 100644
>> index 0000000000..c50cb64c63
>> --- /dev/null
>> +++ b/OvmfPkg/AmdSev/ConfidentialMigration/VirtualMemory.h
>> @@ -0,0 +1,177 @@
>> +/** @file
>> +  Virtual Memory Management Services to set or clear the memory encryption bit
>> +  Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
>> +  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +  Code is derived from OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.h
>> +
>> +**/
>> +
>> +#ifndef __VIRTUAL_MEMORY__
>> +#define __VIRTUAL_MEMORY__
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/CacheMaintenanceLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Uefi.h>
>> +
>> +#define SYS_CODE64_SEL 0x38
>> +
>> +#pragma pack(1)
>> +
>> +//
>> +// Page-Map Level-4 Offset (PML4) and
>> +// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
>> +//
>> +
>> +typedef union {
>> +  struct {
>> +    UINT64  Present:1;                // 0 = Not present in memory,
>> +                                      //   1 = Present in memory
>> +    UINT64  ReadWrite:1;              // 0 = Read-Only, 1= Read/Write
>> +    UINT64  UserSupervisor:1;         // 0 = Supervisor, 1=User
>> +    UINT64  WriteThrough:1;           // 0 = Write-Back caching,
>> +                                      //   1 = Write-Through caching
>> +    UINT64  CacheDisabled:1;          // 0 = Cached, 1=Non-Cached
>> +    UINT64  Accessed:1;               // 0 = Not accessed,
>> +                                      //   1 = Accessed (set by CPU)
>> +    UINT64  Reserved:1;               // Reserved
>> +    UINT64  MustBeZero:2;             // Must Be Zero
>> +    UINT64  Available:3;              // Available for use by system software
>> +    UINT64  PageTableBaseAddress:40;  // Page Table Base Address
>> +    UINT64  AvabilableHigh:11;        // Available for use by system software
>> +    UINT64  Nx:1;                     // No Execute bit
>> +  } Bits;
>> +  UINT64    Uint64;
>> +} PAGE_MAP_AND_DIRECTORY_POINTER;
>> +
>> +//
>> +// Page Table Entry 4KB
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT64  Present:1;                // 0 = Not present in memory,
>> +                                      //   1 = Present in memory
>> +    UINT64  ReadWrite:1;              // 0 = Read-Only, 1= Read/Write
>> +    UINT64  UserSupervisor:1;         // 0 = Supervisor, 1=User
>> +    UINT64  WriteThrough:1;           // 0 = Write-Back caching,
>> +                                      //   1 = Write-Through caching
>> +    UINT64  CacheDisabled:1;          // 0 = Cached, 1=Non-Cached
>> +    UINT64  Accessed:1;               // 0 = Not accessed,
>> +                                      //   1 = Accessed (set by CPU)
>> +    UINT64  Dirty:1;                  // 0 = Not Dirty, 1 = written by
>> +                                      //   processor on access to page
>> +    UINT64  PAT:1;                    //
>> +    UINT64  Global:1;                 // 0 = Not global page, 1 = global page
>> +                                      //   TLB not cleared on CR3 write
>> +    UINT64  Available:3;              // Available for use by system software
>> +    UINT64  PageTableBaseAddress:40;  // Page Table Base Address
>> +    UINT64  AvabilableHigh:11;        // Available for use by system software
>> +    UINT64  Nx:1;                     // 0 = Execute Code,
>> +                                      //   1 = No Code Execution
>> +  } Bits;
>> +  UINT64    Uint64;
>> +} PAGE_TABLE_4K_ENTRY;
>> +
>> +//
>> +// Page Table Entry 2MB
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT64  Present:1;                // 0 = Not present in memory,
>> +                                      //   1 = Present in memory
>> +    UINT64  ReadWrite:1;              // 0 = Read-Only, 1= Read/Write
>> +    UINT64  UserSupervisor:1;         // 0 = Supervisor, 1=User
>> +    UINT64  WriteThrough:1;           // 0 = Write-Back caching,
>> +                                      //   1=Write-Through caching
>> +    UINT64  CacheDisabled:1;          // 0 = Cached, 1=Non-Cached
>> +    UINT64  Accessed:1;               // 0 = Not accessed,
>> +                                      //   1 = Accessed (set by CPU)
>> +    UINT64  Dirty:1;                  // 0 = Not Dirty, 1 = written by
>> +                                      //   processor on access to page
>> +    UINT64  MustBe1:1;                // Must be 1
>> +    UINT64  Global:1;                 // 0 = Not global page, 1 = global page
>> +                                      //   TLB not cleared on CR3 write
>> +    UINT64  Available:3;              // Available for use by system software
>> +    UINT64  PAT:1;                    //
>> +    UINT64  MustBeZero:8;             // Must be zero;
>> +    UINT64  PageTableBaseAddress:31;  // Page Table Base Address
>> +    UINT64  AvabilableHigh:11;        // Available for use by system software
>> +    UINT64  Nx:1;                     // 0 = Execute Code,
>> +                                      //   1 = No Code Execution
>> +  } Bits;
>> +  UINT64    Uint64;
>> +} PAGE_TABLE_ENTRY;
>> +
>> +//
>> +// Page Table Entry 1GB
>> +//
>> +typedef union {
>> +  struct {
>> +    UINT64  Present:1;                // 0 = Not present in memory,
>> +                                      //   1 = Present in memory
>> +    UINT64  ReadWrite:1;              // 0 = Read-Only, 1= Read/Write
>> +    UINT64  UserSupervisor:1;         // 0 = Supervisor, 1=User
>> +    UINT64  WriteThrough:1;           // 0 = Write-Back caching,
>> +                                      //   1 = Write-Through caching
>> +    UINT64  CacheDisabled:1;          // 0 = Cached, 1=Non-Cached
>> +    UINT64  Accessed:1;               // 0 = Not accessed,
>> +                                      //   1 = Accessed (set by CPU)
>> +    UINT64  Dirty:1;                  // 0 = Not Dirty, 1 = written by
>> +                                      //   processor on access to page
>> +    UINT64  MustBe1:1;                // Must be 1
>> +    UINT64  Global:1;                 // 0 = Not global page, 1 = global page
>> +                                      //   TLB not cleared on CR3 write
>> +    UINT64  Available:3;              // Available for use by system software
>> +    UINT64  PAT:1;                    //
>> +    UINT64  MustBeZero:17;            // Must be zero;
>> +    UINT64  PageTableBaseAddress:22;  // Page Table Base Address
>> +    UINT64  AvabilableHigh:11;        // Available for use by system software
>> +    UINT64  Nx:1;                     // 0 = Execute Code,
>> +                                      //   1 = No Code Execution
>> +  } Bits;
>> +  UINT64    Uint64;
>> +} PAGE_TABLE_1G_ENTRY;
>> +
>> +#pragma pack()
>> +
>> +#define IA32_PG_P                   BIT0
>> +#define IA32_PG_RW                  BIT1
>> +#define IA32_PG_PS                  BIT7
>> +
>> +#define PAGING_PAE_INDEX_MASK       0x1FF
>> +
>> +#define PAGING_4K_ADDRESS_MASK_64   0x000FFFFFFFFFF000ull
>> +#define PAGING_2M_ADDRESS_MASK_64   0x000FFFFFFFE00000ull
>> +#define PAGING_1G_ADDRESS_MASK_64   0x000FFFFFC0000000ull
>> +
>> +#define PAGING_L1_ADDRESS_SHIFT     12
>> +#define PAGING_L2_ADDRESS_SHIFT     21
>> +#define PAGING_L3_ADDRESS_SHIFT     30
>> +#define PAGING_L4_ADDRESS_SHIFT     39
>> +
>> +#define PAGING_PML4E_NUMBER         4
>> +
>> +#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)
>> +#define PAGING_1G_ADDRESS_MASK_64   0x000FFFFFC0000000ull
>> +
>> +#define PAGE_TABLE_POOL_ALIGNMENT   BASE_2MB
>> +#define PAGE_TABLE_POOL_UNIT_SIZE   SIZE_2MB
>> +#define PAGE_TABLE_POOL_UNIT_PAGES  \
>> +  EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE)
>> +#define PAGE_TABLE_POOL_ALIGN_MASK  \
>> +  (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1))
>> +
>> +typedef struct {
>> +  VOID            *NextPool;
>> +  UINTN           Offset;
>> +  UINTN           FreePages;
>> +} PAGE_TABLE_POOL;
>> +
>> +#endif
>> diff --git a/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.c b/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.c
>> index 8402fcc4fa..3df3b09732 100644
>> --- a/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.c
>> +++ b/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.c
>> @@ -11,6 +11,7 @@
>>  #include <Protocol/MpService.h>
>>  #include <Library/BaseMemoryLib.h>
>>  
>> +#include "VirtualMemory.h"
>>  //
>>  // Functions implemented by the migration handler
>>  //
>> @@ -43,6 +44,83 @@ typedef volatile struct {
>>    UINT32       done;
>>  } MH_COMMAND_PARAMETERS;
>>  
>> +//
>> +// Addresses for MH page table.
>> +//
>> +STATIC PAGE_TABLE_POOL   *mPageTablePool = NULL;
>> +STATIC PHYSICAL_ADDRESS  mMigrationHelperPageTables = 0;
>> +
>> +//
>> +// Offset for non-cbit mapping.
>> +//
>> +#define UNENC_VIRT_ADDR_BASE    0xffffff8000000000ULL
>> +
>> +
>> +/**
>> +  Allocates and fills in custom page tables for Migration Handler.
>> +  The MH must be able to write to any encrypted page. Thus, it
>> +  uses an identity map where the C-bit is set for every page. The
>> +  HV should never ask the MH to import/export a shared page. The
>> +  MH must also be able to read some shared pages. The first 1GB
>> +  of memory is mapped at offset UNENC_VIRT_ADDR_BASE.
>> +
>> +**/
>> +VOID
>> +PrepareMigrationHandlerPageTables (
>> +  VOID
>> +  )
>> +{
>> +  UINTN                            PoolPages;
>> +  VOID                             *Buffer;
>> +  VOID                             *Start;
>> +  PAGE_MAP_AND_DIRECTORY_POINTER   *PageMapLevel4Entry;
>> +  PAGE_TABLE_1G_ENTRY              *PageDirectory1GEntry;
>> +  PAGE_TABLE_1G_ENTRY              *Unenc1GEntry;
>> +  UINT64                           AddressEncMask;
>> +
>> +  PoolPages = 1 + 10;
>> +  Buffer = AllocateAlignedRuntimePages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
>> +  mPageTablePool = Buffer;
>> +  mPageTablePool->NextPool = mPageTablePool;
>> +  mPageTablePool->FreePages  = PoolPages - 1;
>> +  mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
>> +
>> +  Start = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
>> +  ZeroMem(Start, mPageTablePool->FreePages * EFI_PAGE_SIZE);
>> +
>> +  AddressEncMask = 1ULL << 47;
>> +
> 
> Preferably getting the encryption bit location from SEV CPUID
> information.

You're right; I'll fix.


> 
>> +  PageMapLevel4Entry = Start;
>> +  PageDirectory1GEntry = (PAGE_TABLE_1G_ENTRY*)((UINT8*)Start + EFI_PAGE_SIZE);
>> +  Unenc1GEntry = (PAGE_TABLE_1G_ENTRY*)((UINT8*)Start + 2 * EFI_PAGE_SIZE);
>> +
>> +  PageMapLevel4Entry = Start;
>> +  PageMapLevel4Entry += PML4_OFFSET(0x0ULL);
>> +  PageMapLevel4Entry->Uint64 = (UINT64)PageDirectory1GEntry | AddressEncMask | 0x23;
>> +
>> +  PageMapLevel4Entry = Start;
>> +  PageMapLevel4Entry += PML4_OFFSET(UNENC_VIRT_ADDR_BASE); // should be 511
>> +  PageMapLevel4Entry->Uint64 = (UINT64)Unenc1GEntry | AddressEncMask | 0x23;
>> +
>> +  UINT64 PageAddr = 0;
>> +  for (int i = 0; i < 512; i++, PageAddr += SIZE_1GB) {
>> +    PAGE_TABLE_1G_ENTRY *e = PageDirectory1GEntry + i;
>> +    e->Uint64 = PageAddr | AddressEncMask | 0xe3; // 1GB page
>> +  }
>> +
> 
> Changing encryption attributes of a page requires to flush it from
> the caches, you may need to do a clflush here.

Thanks for pointing that out.  I think I didn't see that in the code that
builds the page tables for encrypted memory (which inspired most of this
function), but maybe I missed it.


Thanks,
Dov


> 
> Thanks,
> Ashish
> 
>> +  UINT64 UnencPageAddr = 0;
>> +  Unenc1GEntry->Uint64 = UnencPageAddr | 0xe3; // 1GB page unencrypted
>> +
>> +  mMigrationHelperPageTables = (UINT64)Start | AddressEncMask;
>> +}
>> +
>> +VOID
>> +SwitchToMigrationHelperPageTables(VOID)
>> +{
>> +  AsmWriteCr3(mMigrationHelperPageTables);
>> +}
>> +
>> +
>>  
>>  VOID
>>  EFIAPI
>> @@ -56,7 +134,12 @@ MigrationHandlerMain (
>>  
>>    DebugPrint (DEBUG_INFO,"MIGRATION Handler Started\n");
>>  
>> -  params_base = PcdGet32 (PcdConfidentialMigrationMailboxBase);
>> +  SwitchToMigrationHelperPageTables();
>> +
>> +  //
>> +  // Shared pages must be offset by UNENC_VIRT_ADDR_BASE.
>> +  //
>> +  params_base = PcdGet32 (PcdConfidentialMigrationMailboxBase) + UNENC_VIRT_ADDR_BASE;
>>    params = (VOID *)params_base;
>>    page_va = (VOID *)params_base + 0x1000;
>>  
>> @@ -134,6 +217,8 @@ LaunchMigrationHandler (
>>  
>>    MigrationHandlerCpuIndex = NumProc - 1;
>>  
>> +  PrepareMigrationHandlerPageTables();
>> +
>>    EFI_EVENT Event;
>>    MpProto->GetProcessorInfo (MpProto, MigrationHandlerCpuIndex, &Tcb);
>>    if (Tcb.StatusFlag != 7) {
>> @@ -154,6 +239,7 @@ LaunchMigrationHandler (
>>    if (PcdGetBool(PcdIsConfidentialMigrationTarget)) {
>>      DebugPrint (DEBUG_INFO,"Waiting for incoming confidential migration.\n");
>>      DisableInterrupts ();
>> +    SwitchToMigrationHelperPageTables();
>>      CpuDeadLoop ();
>>    }
>>  
>> -- 
>> 2.20.1
>>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#72400): https://edk2.groups.io/g/devel/message/72400
Mute This Topic: https://groups.io/mt/81036386/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list