[edk2-devel] [PATCH 1/3] OvmfPkg: Introduce runtime XenHypercallLib

Anthony PERARD via groups.io anthony.perard=citrix.com at groups.io
Fri Nov 11 15:27:12 UTC 2022


From: Anthony PERARD <anthony.perard at citrix.com>

We need a way to execute instruction "generated" at runtime from the
runtime library.

This patch introduce XenHypercallRuntimeLib, which is mostly a copy of
XenHypercallLib with a few changes.

We reserved some code space with "XenHypercallPage", which we will
overwrite to put the xen hypercall code. Then we ask Xen to overwrite
the reserved space with the hypercall page via `wrmsr`.

Allocation doesn't work, because the memory allocated at run time is
always considered to be data instead of code, so once Linux takes
over, we can't execute from it.

Signed-off-by: Anthony PERARD <anthony.perard at citrix.com>
---

Notes:
    I've been told that we don't need to ask Xen on how to make hypercall
    on x86, and that was just an helper to not have to figure out which
    instruction to use. That would mean that instead of doing write_msr,
    we would need a way to distinguish between AMD and Intel in order to
    find out which instruction to use. So if this patch is too weird, that
    another way to have hypercall in a runtime service.

 OvmfPkg/OvmfXen.dsc                           |   1 +
 .../XenHypercallRuntimeLib.inf                |  46 ++++
 .../XenHypercallLib/X86RuntimeXenHypercall.c  | 210 ++++++++++++++++++
 .../X64/RuntimeHypercallPage.nasm             |  24 ++
 4 files changed, 281 insertions(+)
 create mode 100644 OvmfPkg/Library/XenHypercallLib/XenHypercallRuntimeLib.inf
 create mode 100644 OvmfPkg/Library/XenHypercallLib/X86RuntimeXenHypercall.c
 create mode 100644 OvmfPkg/Library/XenHypercallLib/X64/RuntimeHypercallPage.nasm

diff --git a/OvmfPkg/OvmfXen.dsc b/OvmfPkg/OvmfXen.dsc
index 58a7c97cddf7..8df173a0ee84 100644
--- a/OvmfPkg/OvmfXen.dsc
+++ b/OvmfPkg/OvmfXen.dsc
@@ -312,6 +312,7 @@ [LibraryClasses.common.DXE_RUNTIME_DRIVER]
   PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
   VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
+  XenHypercallLib|OvmfPkg/Library/XenHypercallLib/XenHypercallRuntimeLib.inf
 
 [LibraryClasses.common.UEFI_DRIVER]
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
diff --git a/OvmfPkg/Library/XenHypercallLib/XenHypercallRuntimeLib.inf b/OvmfPkg/Library/XenHypercallLib/XenHypercallRuntimeLib.inf
new file mode 100644
index 000000000000..07fdeb612760
--- /dev/null
+++ b/OvmfPkg/Library/XenHypercallLib/XenHypercallRuntimeLib.inf
@@ -0,0 +1,46 @@
+## @file
+#  Xen Hypercall abstraction lib for Intel for runtime services
+#
+#  Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
+#  Copyright (c) 2022, Citrix Systems, Inc.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = XenHypercallRuntimeLib
+  FILE_GUID                      = f657a395-1d2c-40b5-bd34-eedc203899ab
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  CONSTRUCTOR                    = XenHypercallRuntimeLibConstruct
+
+[Defines.IA32, Defines.X64]
+  LIBRARY_CLASS                  = XenHypercallLib|DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources.X64]
+  X86RuntimeXenHypercall.c
+  X64/hypercall.nasm
+  X64/RuntimeHypercallPage.nasm
+
+[Sources]
+  XenHypercall.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  UefiRuntimeLib
+
+[Guids.X64]
+  gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
diff --git a/OvmfPkg/Library/XenHypercallLib/X86RuntimeXenHypercall.c b/OvmfPkg/Library/XenHypercallLib/X86RuntimeXenHypercall.c
new file mode 100644
index 000000000000..d1c097e00f3a
--- /dev/null
+++ b/OvmfPkg/Library/XenHypercallLib/X86RuntimeXenHypercall.c
@@ -0,0 +1,210 @@
+/** @file
+  Xen Hypercall Library implementation for Intel architecture
+
+  Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
+  Copyright (c) 2022, Citrix Systems, Inc.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <Guid/EventGroup.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/XenHypercallLib.h>
+
+STATIC VOID  *mHyperPage;
+
+//
+// Pointer to reserved page for Xen's hypercall page.
+//
+extern VOID  *XenHypercallPage;
+
+//
+// Virtual Address Change Event
+//
+// This is needed for runtime variable access.
+//
+EFI_EVENT  mXenHypercallLibAddrChangeEvent = NULL;
+
+RETURN_STATUS
+EFIAPI
+XenHypercallRuntimeLibConstruct (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  XenHypercallLibInit ();
+
+  //
+  // We don't fail library construction, since that has catastrophic
+  // consequences for client modules (whereas those modules may easily be
+  // running on a non-Xen platform). Instead, XenHypercallIsAvailable()
+  // will return FALSE.
+  //
+  return RETURN_SUCCESS;
+}
+
+/**
+  Check if the Xen Hypercall library is able to make calls to the Xen
+  hypervisor.
+
+  Client code should call further functions in this library only if, and after,
+  this function returns TRUE.
+
+  @retval TRUE   Hypercalls are available.
+  @retval FALSE  Hypercalls are not available.
+**/
+BOOLEAN
+EFIAPI
+XenHypercallIsAvailable (
+  VOID
+  )
+{
+  return mHyperPage != NULL;
+}
+
+//
+// Interface exposed by the ASM implementation of the core hypercall
+//
+INTN
+EFIAPI
+__XenHypercall2 (
+  IN     VOID  *HypercallAddr,
+  IN OUT INTN  Arg1,
+  IN OUT INTN  Arg2
+  );
+
+/**
+  Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
+
+  This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
+  It converts pointer to new virtual address.
+
+  @param  Event        Event whose notification function is being invoked.
+  @param  Context      Pointer to the notification function's context.
+
+**/
+STATIC
+VOID
+EFIAPI
+XenHypercallLibAddrChangeEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = EfiConvertFunctionPointer (0, &mHyperPage);
+  ASSERT_EFI_ERROR (Status);
+}
+
+STATIC
+UINT32
+XenCpuidLeaf (
+  VOID
+  )
+{
+  UINT8   Signature[13];
+  UINT32  XenLeaf;
+
+  Signature[12] = '\0';
+  for (XenLeaf = 0x40000000; XenLeaf < 0x40010000; XenLeaf += 0x100) {
+    AsmCpuid (
+      XenLeaf,
+      NULL,
+      (UINT32 *)&Signature[0],
+      (UINT32 *)&Signature[4],
+      (UINT32 *)&Signature[8]
+      );
+
+    if (!AsciiStrCmp ((CHAR8 *)Signature, "XenVMMXenVMM")) {
+      return XenLeaf;
+    }
+  }
+
+  return 0;
+}
+
+/**
+  Library constructor: populate hypercall page.
+**/
+RETURN_STATUS
+EFIAPI
+XenHypercallLibInit (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      TransferReg;
+  UINT32      TransferPages;
+  UINT32      XenLeaf;
+
+  XenLeaf = XenCpuidLeaf ();
+
+  if (XenLeaf == 0) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  AsmCpuid (XenLeaf + 2, &TransferPages, &TransferReg, NULL, NULL);
+
+  //
+  // Only populate the first page of the hypercall even if there's more
+  // than one, that is even if TransferPages > 1.
+  // We don't use hypercall id > 127.
+  //
+  AsmWriteMsr64 (TransferReg, (UINTN)&XenHypercallPage);
+
+  mHyperPage = &XenHypercallPage;
+
+  //
+  // Register for the virtual address change event
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  XenHypercallLibAddrChangeEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &mXenHypercallLibAddrChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  This function will put the two arguments in the right place (registers) and
+  invoke the hypercall identified by HypercallID.
+
+  @param HypercallID    The symbolic ID of the hypercall to be invoked
+  @param Arg1           First argument.
+  @param Arg2           Second argument.
+
+  @return   Return 0 if success otherwise it return an errno.
+**/
+INTN
+EFIAPI
+XenHypercall2 (
+  IN     UINTN  HypercallID,
+  IN OUT INTN   Arg1,
+  IN OUT INTN   Arg2
+  )
+{
+  ASSERT (mHyperPage != NULL);
+  //
+  // Hypercall must not use code beyong the first hypercall page.
+  // Only the first page is populated by XenHypercallLibInit ()
+  //
+  ASSERT (HypercallID < EFI_PAGE_SIZE / 32);
+  if (HypercallID >= EFI_PAGE_SIZE / 32) {
+    return -38; // -ENOSYS
+  }
+
+  return __XenHypercall2 ((UINT8 *)mHyperPage + HypercallID * 32, Arg1, Arg2);
+}
diff --git a/OvmfPkg/Library/XenHypercallLib/X64/RuntimeHypercallPage.nasm b/OvmfPkg/Library/XenHypercallLib/X64/RuntimeHypercallPage.nasm
new file mode 100644
index 000000000000..f0897c51b31c
--- /dev/null
+++ b/OvmfPkg/Library/XenHypercallLib/X64/RuntimeHypercallPage.nasm
@@ -0,0 +1,24 @@
+;; @file
+;  Provide one page of code space to be overwritten at boot and to be used by
+;  runtime drivers to make Xen hypercall on x86.
+;
+;  Copyright (c) 2022, Citrix Systems, Inc.
+;
+;  SPDX-License-Identifier: BSD-2-Clause-Patent
+;;
+
+DEFAULT REL
+SECTION .text
+
+;
+; Align at page boundary as we need a pointer on a page without offset.
+;
+ALIGN EFI_PAGE_SIZE
+
+;
+; reserve some .text space to put-in Xen's hypercall instructions in at runtime.
+; Poisoned with `ret`
+;
+global ASM_PFX(XenHypercallPage)
+ASM_PFX(XenHypercallPage):
+  times EFI_PAGE_SIZE ret
-- 
Anthony PERARD



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#96304): https://edk2.groups.io/g/devel/message/96304
Mute This Topic: https://groups.io/mt/94960818/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