From 2c000372cab80ab68a8672138c8d0f5cb1ae43d9 Mon Sep 17 00:00:00 2001
From: Ashish Kalra <ashish.kalra@amd.com>
Date: Tue, 5 Apr 2022 16:23:53 +0000
Subject: [PATCH 2/9] OvmfPkg/BaseMemEncryptLib: Hypercall API for page
 encryption state change

cherry-picked from https://patchew.org/EDK2/cover.1629380011.git.ashish.kalra@amd.com .

Add API to issue hypercall on page encryption state change.

By default all the SEV guest memory regions are considered encrypted,
if a guest changes the encryption attribute of the page (e.g mark a
page as decrypted) then notify hypervisor. Hypervisor will need to
track the unencrypted pages. The information will be used during
guest live migration, guest page migration and guest debugging.

This hypercall is used to notify hypervisor when the page's
encryption state changes.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
 OvmfPkg/Include/Library/MemEncryptSevLib.h    | 52 +++++++++++++++
 .../DxeMemEncryptSevLib.inf                   |  1 +
 .../Ia32/MemEncryptSevLib.c                   | 27 ++++++++
 .../PeiMemEncryptSevLib.inf                   |  1 +
 .../SecMemEncryptSevLibInternal.c             | 20 ++++++
 .../X64/AsmHelperStub.nasm                    | 33 ++++++++++
 .../X64/MemEncryptSevLib.c                    | 66 +++++++++++++++++++
 7 files changed, 200 insertions(+)
 create mode 100644 OvmfPkg/Library/BaseMemEncryptSevLib/X64/AsmHelperStub.nasm

diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h b/OvmfPkg/Include/Library/MemEncryptSevLib.h
index babec60d..b60496c2 100644
--- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
+++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
@@ -240,4 +240,56 @@ MemEncryptSevSnpPreValidateSystemRam (
   IN UINTN             NumPages
   );
 
+/**
+ This hypercall is used to notify hypervisor when the page's encryption
+ state changes.
+
+ @param[in]   PhysicalAddress       The physical address that is the start address
+                                    of a memory region.
+ @param[in]   Pages                 Number of pages in memory region.
+ @param[in]   IsEncrypted           Encrypted or Decrypted.
+
+ @retval RETURN_SUCCESS             Hypercall returned success.
+ @retval RETURN_UNSUPPORTED         Hypercall not supported.
+ @retval RETURN_NO_MAPPING          Hypercall returned error.
+**/
+RETURN_STATUS
+EFIAPI
+SetMemoryEncDecHypercall3 (
+  IN  UINTN    PhysicalAddress,
+  IN  UINTN    Pages,
+  IN  BOOLEAN  IsEncrypted
+  );
+
+#define KVM_HC_MAP_GPA_RANGE          12
+#define KVM_MAP_GPA_RANGE_PAGE_SZ_4K  0
+#define KVM_MAP_GPA_RANGE_PAGE_SZ_2M  BIT0
+#define KVM_MAP_GPA_RANGE_PAGE_SZ_1G  BIT1
+#define KVM_MAP_GPA_RANGE_ENC_STATE(n)  ((n) << 4)
+#define KVM_MAP_GPA_RANGE_ENCRYPTED  KVM_MAP_GPA_RANGE_ENC_STATE(1)
+#define KVM_MAP_GPA_RANGE_DECRYPTED  KVM_MAP_GPA_RANGE_ENC_STATE(0)
+
+/**
+ Interface exposed by the ASM implementation of the core hypercall
+
+ @param[in]   HypercallNum          KVM_HC_MAP_GPA_RANGE hypercall.
+ @param[in]   PhysicalAddress       The physical address that is the start address
+                                    of a memory region.
+ @param[in]   Pages                 Number of pages in memory region.
+ @param[in]   Attributes            Bits 3:0 - preferred page size encoding,
+                                    0 = 4kb, 1 = 2mb, 2 = 1gb, etc...
+                                    Bit  4 - plaintext = 0, encrypted = 1
+                                    Bits 63:5 - reserved (must be zero)
+
+  @retval Hypercall returned status.
+**/
+UINTN
+EFIAPI
+SetMemoryEncDecHypercall3AsmStub (
+  IN  UINTN  HypercallNum,
+  IN  UINTN  PhysicalAddress,
+  IN  UINTN  Pages,
+  IN  UINTN  Attributes
+  );
+
 #endif // _MEM_ENCRYPT_SEV_LIB_H_
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
index 3a1d3089..4d32fae6 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
@@ -40,6 +40,7 @@
   X64/SnpPageStateChangeInternal.c
   X64/VirtualMemory.c
   X64/VirtualMemory.h
+  X64/AsmHelperStub.nasm
 
 [Sources.IA32]
   Ia32/MemEncryptSevLib.c
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c
index f92299fc..c1c10a61 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/Ia32/MemEncryptSevLib.c
@@ -153,3 +153,30 @@ MemEncryptSevSnpPreValidateSystemRam (
 {
   ASSERT (FALSE);
 }
+
+/**
+ This hyercall is used to notify hypervisor when the page's encryption
+ state changes.
+
+ @param[in]   PhysicalAddress       The physical address that is the start address
+                                    of a memory region.
+ @param[in]   Pages                 Number of Pages in the memory region.
+ @param[in]   IsEncrypted           Encrypted or Decrypted.
+
+ @retval RETURN_SUCCESS             Hypercall returned success.
+ @retval RETURN_UNSUPPORTED         Hypercall not supported.
+ @retval RETURN_NO_MAPPING          Hypercall returned error.
+**/
+RETURN_STATUS
+EFIAPI
+SetMemoryEncDecHypercall3 (
+  IN  UINTN    PhysicalAddress,
+  IN  UINTN    Pages,
+  IN  BOOLEAN  IsEncrypted
+  )
+{
+  //
+  // Memory encryption bit is not accessible in 32-bit mode
+  //
+  return RETURN_UNSUPPORTED;
+}
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
index 8f56783d..3f11f06a 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/PeiMemEncryptSevLib.inf
@@ -40,6 +40,7 @@
   X64/SnpPageStateChangeInternal.c
   X64/VirtualMemory.c
   X64/VirtualMemory.h
+  X64/AsmHelperStub.nasm
 
 [Sources.IA32]
   Ia32/MemEncryptSevLib.c
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
index 9142ac40..ffb22a08 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLibInternal.c
@@ -139,6 +139,26 @@ MemEncryptSevLiveMigrationIsEnabled (
   return FALSE;
 }
 
+/**
+  Interface exposed by the ASM implementation of the core hypercall
+
+  @retval Hypercall returned status.
+**/
+UINTN
+EFIAPI
+SetMemoryEncDecHypercall3AsmStub (
+  IN  UINTN  HypercallNum,
+  IN  UINTN  PhysicalAddress,
+  IN  UINTN  Pages,
+  IN  UINTN  Attributes
+  )
+{
+  //
+  // Not used in SEC phase.
+  //
+  return RETURN_UNSUPPORTED;
+}
+
 /**
   Returns the SEV encryption mask.
 
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/AsmHelperStub.nasm b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/AsmHelperStub.nasm
new file mode 100644
index 00000000..0ec35dd9
--- /dev/null
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/AsmHelperStub.nasm
@@ -0,0 +1,33 @@
+/** @file
+
+  ASM helper stub to invoke hypercall
+
+  Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+DEFAULT REL
+SECTION .text
+
+; UINTN
+; EFIAPI
+; SetMemoryEncDecHypercall3AsmStub (
+;   IN UINTN HypercallNum,
+;   IN UINTN Arg1,
+;   IN UINTN Arg2,
+;   IN UINTN Arg3
+;   );
+global ASM_PFX(SetMemoryEncDecHypercall3AsmStub)
+ASM_PFX(SetMemoryEncDecHypercall3AsmStub):
+  ; UEFI calling conventions require RBX to
+  ; be nonvolatile/callee-saved.
+  push rbx
+  mov rax, rcx    ; Copy HypercallNumber to rax
+  mov rbx, rdx    ; Copy Arg1 to the register expected by KVM
+  mov rcx, r8     ; Copy Arg2 to register expected by KVM
+  mov rdx, r9     ; Copy Arg3 to register expected by KVM
+  vmmcall         ; Call VMMCALL
+  pop rbx
+  ret
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c
index e7c703bb..a64ff2a5 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/X64/MemEncryptSevLib.c
@@ -142,3 +142,69 @@ MemEncryptSevClearMmioPageEncMask (
            EFI_PAGES_TO_SIZE (NumPages)
            );
 }
+
+/**
+ This hyercall is used to notify hypervisor when the page's encryption
+ state changes.
+
+ @param[in]   PhysicalAddress       The physical address that is the start address
+                                    of a memory region.
+ @param[in]   Pages                 Number of Pages in the memory region.
+ @param[in]   IsEncrypted           Encrypted or Decrypted.
+
+ @retval RETURN_SUCCESS             Hypercall returned success.
+ @retval RETURN_UNSUPPORTED         Hypercall not supported.
+ @retval RETURN_NO_MAPPING          Hypercall returned error.
+**/
+RETURN_STATUS
+EFIAPI
+SetMemoryEncDecHypercall3 (
+  IN  UINTN    PhysicalAddress,
+  IN  UINTN    Pages,
+  IN  BOOLEAN  IsEncrypted
+  )
+{
+  RETURN_STATUS  Ret;
+  UINTN          Error;
+  UINTN          EncryptState;
+
+  Ret = RETURN_UNSUPPORTED;
+
+  if (MemEncryptSevLiveMigrationIsEnabled ()) {
+    Ret = RETURN_SUCCESS;
+    //
+    // The encryption bit is set/clear on the smallest page size, hence
+    // use the 4k page size in MAP_GPA_RANGE hypercall below.
+    //
+    // Also, when the GCD map is being walked and the c-bit being cleared
+    // from MMIO and NonExistent memory spaces, the physical address
+    // range being passed may not be page-aligned and adding an assert
+    // here prevents booting. Hence, rounding it down when calling
+    // SetMemoryEncDecHypercall3AsmStub below.
+    //
+
+    EncryptState = IsEncrypted ? KVM_MAP_GPA_RANGE_ENCRYPTED :
+                   KVM_MAP_GPA_RANGE_DECRYPTED;
+
+    Error = SetMemoryEncDecHypercall3AsmStub (
+              KVM_HC_MAP_GPA_RANGE,
+              PhysicalAddress & ~EFI_PAGE_MASK,
+              Pages,
+              KVM_MAP_GPA_RANGE_PAGE_SZ_4K | EncryptState
+              );
+
+    if (Error != 0) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "SetMemoryEncDecHypercall3 failed, Phys = %x, Pages = %d, Err = %Ld\n",
+        PhysicalAddress,
+        Pages,
+        (INT64)Error
+        ));
+
+      Ret = RETURN_NO_MAPPING;
+    }
+  }
+
+  return Ret;
+}
-- 
2.25.1

