public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Tobin Feldman-Fitzthum" <tobin@linux.ibm.com>
To: tobin@ibm.com, dovmurik@linux.vnet.ibm.com, jejb@linux.ibm.com,
	frankeh@us.ibm.com, pbonzini@redhat.com, ashish.kalra@amd.com,
	thomas.lendacky@amd.com, brijesh.singh@amd.com,
	dgilbert@redhat.com, srutherford@google.com,
	devel@edk2.groups.io, ard.biesheuvel@arm.com,
	jiewen.yao@intel.com
Subject: [RFC PATCH 5/9] OvmfPkg/AmdSev: Build page table for migration handler
Date: Wed, 18 Aug 2021 17:20:44 -0400	[thread overview]
Message-ID: <20210818212048.162626-6-tobin@linux.ibm.com> (raw)
In-Reply-To: <20210818212048.162626-1-tobin@linux.ibm.com>

From: Dov Murik <dovmurik@linux.vnet.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@linux.ibm.com>
Signed-off-by: Dov Murik <dovmurik@linux.vnet.ibm.com>
---
 .../ConfidentialMigrationDxe.inf              |   1 +
 .../ConfidentialMigration/VirtualMemory.h     | 177 ++++++++++++++++++
 .../ConfidentialMigrationDxe.c                |  73 +++++++-
 3 files changed, 249 insertions(+), 2 deletions(-)
 create mode 100644 OvmfPkg/AmdSev/ConfidentialMigration/VirtualMemory.h

diff --git a/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.inf b/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.inf
index cb5609271c..42875095fc 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 a981aaeac7..34d449fe10 100644
--- a/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.c
+++ b/OvmfPkg/AmdSev/ConfidentialMigration/ConfidentialMigrationDxe.c
@@ -9,6 +9,8 @@
 #include <Library/BaseMemoryLib.h>
 #include <Library/UefiLib.h>
 
+#include "VirtualMemory.h"
+
 //
 // Functions implemented by the migration handler
 //
@@ -36,6 +38,71 @@ typedef volatile struct {
   UINT32       Done;
 } MH_COMMAND_PARAMETERS;
 
+//
+// Offset for non-cbit mapping.
+//
+#define UNENC_VIRT_ADDR_BASE    0xffffff8000000000ULL
+
+STATIC PAGE_TABLE_POOL   *mPageTablePool = NULL;
+PHYSICAL_ADDRESS  mMigrationHandlerPageTables = 0;
+
+/**
+  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;
+
+  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
+  }
+
+  UINT64 UnencPageAddr = 0;
+  Unenc1GEntry->Uint64 = UnencPageAddr | 0xe3; // 1GB page unencrypted
+
+  mMigrationHandlerPageTables = (UINT64)Start | AddressEncMask;
+}
+
 
 VOID
 EFIAPI
@@ -48,8 +115,8 @@ MigrationHandlerMain ()
   DebugPrint (DEBUG_INFO,"Migration Handler Started\n");
 
   MailboxStart = PcdGet32 (PcdConfidentialMigrationMailboxBase);
-  Params = (VOID *)MailboxStart;
-  PageVa = (VOID *)(MailboxStart + 0x1000);
+  Params = (VOID *)(MailboxStart + UNENC_VIRT_ADDR_BASE);
+  PageVa = (VOID *)(MailboxStart + UNENC_VIRT_ADDR_BASE + 0x1000);
 
   DisableInterrupts ();
   Params->Go = 0;
@@ -108,6 +175,8 @@ SetupMigrationHandler (
     return 0;
   }
 
+	PrepareMigrationHandlerPageTables ();
+
   //
   // If VM is migration target, wait until hypervisor modifies CPU state
   // and restarts execution.
-- 
2.20.1


  parent reply	other threads:[~2021-08-18 21:21 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-18 21:20 [RFC PATCH 0/9] Firmware Support for Fast Live Migration for AMD SEV Tobin Feldman-Fitzthum
2021-08-18 21:20 ` [RFC PATCH 1/9] OvmfPkg/AmdSev: Base for Confidential Migration Handler Tobin Feldman-Fitzthum
2021-08-18 21:20 ` [RFC PATCH 2/9] OvmfPkg/PlatfomPei: Set Confidential Migration PCD Tobin Feldman-Fitzthum
2021-08-18 21:20 ` [RFC PATCH 3/9] OvmfPkg/AmdSev: Setup Migration Handler Mailbox Tobin Feldman-Fitzthum
2021-08-18 21:20 ` [RFC PATCH 4/9] OvmfPkg/AmdSev: MH support for mailbox protocol Tobin Feldman-Fitzthum
2021-08-18 21:20 ` Tobin Feldman-Fitzthum [this message]
2021-08-18 21:20 ` [RFC PATCH 6/9] OvmfPkg/AmdSev: Don't overwrite mailbox or pagetables Tobin Feldman-Fitzthum
2021-08-18 21:20 ` [RFC PATCH 7/9] OvmfPkg/AmdSev: Don't overwrite MH stack Tobin Feldman-Fitzthum
2021-08-18 21:20 ` [RFC PATCH 8/9] OvmfPkg/AmdSev: Add Migration Handler entry point Tobin Feldman-Fitzthum
2021-08-18 21:20 ` [RFC PATCH 9/9] OvmfPkg/ResetVector: Expose Migration Handler Entry Addresses Tobin Feldman-Fitzthum

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210818212048.162626-6-tobin@linux.ibm.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox