diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2025-02-18 11:36:07 +0100 |
---|---|---|
committer | mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> | 2025-04-23 13:43:23 +0000 |
commit | 9dd47eeea171c4073798b7afb095a5c9cb1d3a69 (patch) | |
tree | afbb7e08781f55482f33805017489de443f6f941 | |
parent | d198c801570c4a98e0eb72e99b1326d5c8841515 (diff) | |
download | edk2-9dd47eeea171c4073798b7afb095a5c9cb1d3a69.zip edk2-9dd47eeea171c4073798b7afb095a5c9cb1d3a69.tar.gz edk2-9dd47eeea171c4073798b7afb095a5c9cb1d3a69.tar.bz2 |
OvmfPkg: add new VirtMmCommunicationDxe driver
EFI driver implementing the MmCommunication2Protocol,
but instead of trapping to MM mode (SMM on x86, el3 on arm)
trap to the host, using a virtual device.
Moving the efi variable management to the host allows to have
persistent efi variables without flash storage, and it also
allows to have secure boot support without requiring MM mode
to protect variable storage in flash.
On x86 etc/hardware-info is used for device discovery, on arm
the FDT will be consulted.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-rw-r--r-- | OvmfPkg/VirtMmCommunicationDxe/QemuFdt.c | 186 | ||||
-rw-r--r-- | OvmfPkg/VirtMmCommunicationDxe/QemuHwInfo.c | 78 | ||||
-rw-r--r-- | OvmfPkg/VirtMmCommunicationDxe/QemuMmio.c | 207 | ||||
-rw-r--r-- | OvmfPkg/VirtMmCommunicationDxe/QemuX64.c | 149 | ||||
-rw-r--r-- | OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c | 400 | ||||
-rw-r--r-- | OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h | 53 | ||||
-rw-r--r-- | OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf | 72 |
7 files changed, 1145 insertions, 0 deletions
diff --git a/OvmfPkg/VirtMmCommunicationDxe/QemuFdt.c b/OvmfPkg/VirtMmCommunicationDxe/QemuFdt.c new file mode 100644 index 0000000..bb86290 --- /dev/null +++ b/OvmfPkg/VirtMmCommunicationDxe/QemuFdt.c @@ -0,0 +1,186 @@ +/** @file
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/QemuUefiVars.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Protocol/FdtClient.h>
+
+#include "VirtMmCommunication.h"
+
+/*
+ Qemu hooks up the uefi vars communication device like this:
+
+ platform-bus@c000000 {
+ interrupt-parent = <0x8002>;
+ ranges = <0x00 0x00 0xc000000 0x2000000>;
+ #address-cells = <0x01>;
+ #size-cells = <0x01>;
+ compatible = "qemu,platform", "simple-bus";
+
+ qemu-uefi-vars@0 {
+ reg = <0x00 0x10>;
+ compatible = "qemu,uefi-vars";
+ };
+ };
+
+ So we have to lookup both platform bus and our device to figure where it
+ actually is mapped in mmio space.
+
+ The code assumes the uefi-vars device is connected to the platform bus without
+ checking that this is actually the case due to FdtClientDxe API limitations.
+*/
+STATIC
+UINT64
+VirtMmGetValue (
+ CONST VOID *Data,
+ UINT32 Size,
+ UINT32 *Pos,
+ UINT32 Cells
+ )
+{
+ UINT32 *Ptr32;
+ UINT64 *Ptr64;
+ UINT64 Value;
+
+ switch (Cells) {
+ case 1:
+ ASSERT (*Pos + 4 <= Size);
+ Ptr32 = (UINT32 *)(Data + *Pos);
+ *Pos += 4;
+ Value = SwapBytes32 (*Ptr32);
+ break;
+ case 2:
+ ASSERT (*Pos + 8 <= Size);
+ Ptr64 = (UINT64 *)(Data + *Pos);
+ *Pos += 8;
+ Value = SwapBytes64 (ReadUnaligned64 (Ptr64));
+ break;
+ default:
+ ASSERT (!"unsupported cell size");
+ Value = 0;
+ }
+
+ return Value;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtMmGetProp (
+ IN FDT_CLIENT_PROTOCOL *FdtClient,
+ IN CHAR8 *Compatible,
+ IN CHAR8 *Property,
+ OUT CONST VOID **Values,
+ OUT UINT32 *ValSize
+ )
+{
+ EFI_STATUS Status;
+
+ Status = FdtClient->FindCompatibleNodeProperty (
+ FdtClient,
+ Compatible,
+ Property,
+ Values,
+ ValSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: node compatible=\"%a/%a\" not found (%r)\n",
+ __func__,
+ Compatible,
+ Property,
+ Status
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a: %a/%a: ok\n", __func__, Compatible, Property));
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwFind (
+ VOID
+ )
+{
+ FDT_CLIENT_PROTOCOL *FdtClient;
+ EFI_STATUS Status;
+ UINT32 Pos;
+ CONST VOID *Ranges;
+ UINT32 RangesSize;
+ CONST VOID *Reg;
+ UINT32 RegSize;
+
+ UINT64 DevAddr;
+ UINT64 DevSize;
+ UINT64 BusChildAddr;
+ UINT64 BusParentAddr;
+ UINT64 BusSize;
+
+ Status = gBS->LocateProtocol (
+ &gFdtClientProtocolGuid,
+ NULL,
+ (VOID **)&FdtClient
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ VirtMmGetProp (
+ FdtClient,
+ "qemu,platform",
+ "ranges",
+ &Ranges,
+ &RangesSize
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ VirtMmGetProp (
+ FdtClient,
+ UEFI_VARS_FDT_COMPAT,
+ "reg",
+ &Reg,
+ &RegSize
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (RegSize == 2 * sizeof (UINT32)) {
+ ASSERT (RangesSize == RegSize + sizeof (UINT64));
+ Pos = 0;
+ BusChildAddr = VirtMmGetValue (Ranges, RangesSize, &Pos, 1);
+ BusParentAddr = VirtMmGetValue (Ranges, RangesSize, &Pos, 2);
+ BusSize = VirtMmGetValue (Ranges, RangesSize, &Pos, 1);
+ Pos = 0;
+ DevAddr = VirtMmGetValue (Reg, RegSize, &Pos, 1);
+ DevSize = VirtMmGetValue (Reg, RegSize, &Pos, 1);
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: unexpected regsize\n", __func__));
+ return RETURN_UNSUPPORTED;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a: %a\n", __func__, "qemu,platform"));
+ DEBUG ((DEBUG_VERBOSE, "%a: bus child %8lx\n", __func__, BusChildAddr));
+ DEBUG ((DEBUG_VERBOSE, "%a: bus parent %8lx\n", __func__, BusParentAddr));
+ DEBUG ((DEBUG_VERBOSE, "%a: bus size %8lx\n", __func__, BusSize));
+ DEBUG ((DEBUG_VERBOSE, "%a: %a\n", __func__, UEFI_VARS_FDT_COMPAT));
+ DEBUG ((DEBUG_VERBOSE, "%a: dev addr %8lx\n", __func__, DevAddr));
+ DEBUG ((DEBUG_VERBOSE, "%a: dev size %8lx\n", __func__, DevSize));
+
+ mUefiVarsAddr = DevAddr - BusChildAddr + BusParentAddr;
+ DEBUG ((DEBUG_VERBOSE, "%a: -> mmio at %8lx\n", __func__, mUefiVarsAddr));
+ return RETURN_SUCCESS;
+}
diff --git a/OvmfPkg/VirtMmCommunicationDxe/QemuHwInfo.c b/OvmfPkg/VirtMmCommunicationDxe/QemuHwInfo.c new file mode 100644 index 0000000..50ca5b4 --- /dev/null +++ b/OvmfPkg/VirtMmCommunicationDxe/QemuHwInfo.c @@ -0,0 +1,78 @@ +/** @file
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <IndustryStandard/QemuFwCfg.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HardwareInfoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include "VirtMmCommunication.h"
+
+EFI_STATUS
+EFIAPI
+VirtMmHwFind (
+ VOID
+ )
+{
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ VOID *HardwareInfoBlob;
+ LIST_ENTRY HwInfoList;
+ LIST_ENTRY *HwLink;
+ HARDWARE_INFO *HwInfo;
+ UINTN Count;
+ EFI_STATUS Status;
+
+ Status = QemuFwCfgFindFile ("etc/hardware-info", &FwCfgItem, &FwCfgSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: etc/hardware-info: exists\n", __func__));
+ HardwareInfoBlob = AllocatePool (FwCfgSize);
+ if (HardwareInfoBlob == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, HardwareInfoBlob);
+
+ InitializeListHead (&HwInfoList);
+ Status = CreateHardwareInfoList (
+ HardwareInfoBlob,
+ FwCfgSize,
+ HardwareInfoTypeQemuUefiVars,
+ &HwInfoList
+ );
+ Count = GetHardwareInfoCountByType (
+ &HwInfoList,
+ HardwareInfoTypeQemuUefiVars,
+ sizeof (SIMPLE_INFO)
+ );
+ if (Count != 1) {
+ FreeHardwareInfoList (&HwInfoList);
+ FreePool (HardwareInfoBlob);
+ return RETURN_NOT_FOUND;
+ }
+
+ HwLink = GetFirstHardwareInfoByType (
+ &HwInfoList,
+ HardwareInfoTypeQemuUefiVars,
+ sizeof (SIMPLE_INFO)
+ );
+ HwInfo = HARDWARE_INFO_FROM_LINK (HwLink);
+ mUefiVarsAddr = HwInfo->Data.SimpleDevice->MmioAddress;
+ DEBUG ((DEBUG_INFO, "%a: etc/hardware-info: uefi vars mmio @ 0x%lx\n", __func__, mUefiVarsAddr));
+
+ FreeHardwareInfoList (&HwInfoList);
+ FreePool (HardwareInfoBlob);
+ return RETURN_SUCCESS;
+}
diff --git a/OvmfPkg/VirtMmCommunicationDxe/QemuMmio.c b/OvmfPkg/VirtMmCommunicationDxe/QemuMmio.c new file mode 100644 index 0000000..4aa8119 --- /dev/null +++ b/OvmfPkg/VirtMmCommunicationDxe/QemuMmio.c @@ -0,0 +1,207 @@ +/** @file
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/QemuUefiVars.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include "VirtMmCommunication.h"
+
+UINT64 mUefiVarsAddr;
+
+STATIC
+EFI_STATUS
+VirtMmHwMemAttr (
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gDS->AddMemorySpace (
+ EfiGcdMemoryTypeMemoryMappedIo,
+ mUefiVarsAddr,
+ EFI_PAGE_SIZE,
+ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: AddMemorySpace failed: %r\n", __func__, Status));
+ return RETURN_UNSUPPORTED;
+ }
+
+ Status = gDS->SetMemorySpaceAttributes (
+ mUefiVarsAddr,
+ EFI_PAGE_SIZE,
+ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: SetMemorySpaceAttributes failed: %r\n", __func__, Status));
+ return RETURN_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtMmHwCommand (
+ UINT32 Cmd
+ )
+{
+ UINT32 Count;
+ UINT32 Sts;
+
+ MmioWrite16 (mUefiVarsAddr + UEFI_VARS_REG_CMD_STS, Cmd);
+ for (Count = 0; Count < 100; Count++) {
+ Sts = MmioRead16 (mUefiVarsAddr + UEFI_VARS_REG_CMD_STS);
+ DEBUG ((DEBUG_VERBOSE, "%a: Sts: 0x%x\n", __func__, Sts));
+ switch (Sts) {
+ case UEFI_VARS_STS_SUCCESS:
+ return RETURN_SUCCESS;
+ case UEFI_VARS_STS_BUSY:
+ CpuPause ();
+ break;
+ case UEFI_VARS_STS_ERR_NOT_SUPPORTED:
+ return RETURN_UNSUPPORTED;
+ case UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE:
+ return RETURN_BAD_BUFFER_SIZE;
+ default:
+ return RETURN_DEVICE_ERROR;
+ }
+ }
+
+ return RETURN_TIMEOUT;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwInit (
+ VOID
+ )
+{
+ UINT32 Magic, AddrLo, AddrHi, Flags;
+ EFI_STATUS Status;
+
+ Status = VirtMmHwFind ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: VirtMmHwFind() failed: %d\n", __func__, Status));
+ return Status;
+ }
+
+ VirtMmHwMemAttr ();
+
+ Magic = MmioRead16 (mUefiVarsAddr + UEFI_VARS_REG_MAGIC);
+ if (Magic != UEFI_VARS_MAGIC_VALUE) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Magic value mismatch (0x%x != 0x%x)\n",
+ __func__,
+ Magic,
+ UEFI_VARS_MAGIC_VALUE
+ ));
+ return RETURN_DEVICE_ERROR;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: Magic 0x%x, good\n", __func__, Magic));
+
+ Status = VirtMmHwCommand (UEFI_VARS_CMD_RESET);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Reset failed: %d\n", __func__, Status));
+ return Status;
+ }
+
+ MmioWrite32 (mUefiVarsAddr + UEFI_VARS_REG_BUFFER_SIZE, MAX_BUFFER_SIZE);
+
+ Flags = MmioRead32 (mUefiVarsAddr + UEFI_VARS_REG_FLAGS);
+ if (Flags & UEFI_VARS_FLAG_USE_PIO) {
+ mUsePioTransfer = TRUE;
+ DEBUG ((DEBUG_INFO, "%a: using pio transfer mode\n", __func__));
+ } else {
+ AddrLo = (UINT32)mCommunicateBufferPhys;
+ AddrHi = (UINT32)RShiftU64 (mCommunicateBufferPhys, 32);
+ MmioWrite32 (mUefiVarsAddr + UEFI_VARS_REG_DMA_BUFFER_ADDR_LO, AddrLo);
+ MmioWrite32 (mUefiVarsAddr + UEFI_VARS_REG_DMA_BUFFER_ADDR_HI, AddrHi);
+ DEBUG ((DEBUG_INFO, "%a: using dma transfer mode\n", __func__));
+ }
+
+ return RETURN_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwPioTransfer (
+ VOID *Buffer,
+ UINT32 BufferSize,
+ BOOLEAN ToDevice
+ )
+{
+ UINT32 *Ptr = Buffer;
+ UINT32 Bytes = 0;
+ UINT32 Crc1;
+ UINT32 Crc2;
+ EFI_STATUS Status;
+
+ Status = VirtMmHwCommand (UEFI_VARS_CMD_PIO_ZERO_OFFSET);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: zero offset failed: %d\n", __func__, Status));
+ return Status;
+ }
+
+ while (Bytes < BufferSize) {
+ if (ToDevice) {
+ MmioWrite32 (mUefiVarsAddr + UEFI_VARS_REG_PIO_BUFFER_TRANSFER, *Ptr);
+ } else {
+ *Ptr = MmioRead32 (mUefiVarsAddr + UEFI_VARS_REG_PIO_BUFFER_TRANSFER);
+ }
+
+ Bytes += sizeof (*Ptr);
+ Ptr++;
+ }
+
+ Crc1 = CalculateCrc32c (Buffer, Bytes, 0);
+ Crc2 = MmioRead32 (mUefiVarsAddr + UEFI_VARS_REG_PIO_BUFFER_CRC32C);
+ if (Crc1 != Crc2) {
+ DEBUG ((DEBUG_ERROR, "%a: crc32c mismatch (0x%08x,0x%08x)\n", __func__, Crc1, Crc2));
+ return RETURN_DEVICE_ERROR;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwVirtMap (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: << %lx\n", __func__, mUefiVarsAddr));
+ Status = gRT->ConvertPointer (EFI_OPTIONAL_PTR, (VOID **)&mUefiVarsAddr);
+ DEBUG ((DEBUG_VERBOSE, "%a: >> %lx\n", __func__, mUefiVarsAddr));
+
+ return Status;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwComm (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Cmd;
+
+ Cmd = mUsePioTransfer ? UEFI_VARS_CMD_PIO_MM : UEFI_VARS_CMD_DMA_MM;
+ Status = VirtMmHwCommand (Cmd);
+ DEBUG ((DEBUG_VERBOSE, "%a: Status: %r\n", __func__, Status));
+
+ return Status;
+}
diff --git a/OvmfPkg/VirtMmCommunicationDxe/QemuX64.c b/OvmfPkg/VirtMmCommunicationDxe/QemuX64.c new file mode 100644 index 0000000..4610553 --- /dev/null +++ b/OvmfPkg/VirtMmCommunicationDxe/QemuX64.c @@ -0,0 +1,149 @@ +/** @file
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/QemuUefiVars.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+
+#include "VirtMmCommunication.h"
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtMmHwCommand (
+ UINT32 Cmd
+ )
+{
+ UINT32 Count;
+ UINT32 Sts;
+
+ IoWrite16 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_CMD_STS, Cmd);
+ for (Count = 0; Count < 100; Count++) {
+ Sts = IoRead16 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_CMD_STS);
+ DEBUG ((DEBUG_VERBOSE, "%a: Sts: 0x%x\n", __func__, Sts));
+ switch (Sts) {
+ case UEFI_VARS_STS_SUCCESS:
+ return RETURN_SUCCESS;
+ case UEFI_VARS_STS_BUSY:
+ CpuPause ();
+ break;
+ case UEFI_VARS_STS_ERR_NOT_SUPPORTED:
+ return RETURN_UNSUPPORTED;
+ case UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE:
+ return RETURN_BAD_BUFFER_SIZE;
+ default:
+ return RETURN_DEVICE_ERROR;
+ }
+ }
+
+ return RETURN_TIMEOUT;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwInit (
+ VOID
+ )
+{
+ UINT32 Magic, AddrLo, AddrHi;
+ EFI_STATUS Status;
+
+ Magic = IoRead16 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_MAGIC);
+ if (Magic != UEFI_VARS_MAGIC_VALUE) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Magic value mismatch (0x%x != 0x%x)\n",
+ __func__,
+ Magic,
+ UEFI_VARS_MAGIC_VALUE
+ ));
+ return RETURN_DEVICE_ERROR;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: Magic 0x%x, good\n", __func__, Magic));
+
+ Status = VirtMmHwCommand (UEFI_VARS_CMD_RESET);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Reset failed: %d\n", __func__, Status));
+ return Status;
+ }
+
+ AddrLo = (UINT32)mCommunicateBufferPhys;
+ AddrHi = (UINT32)RShiftU64 (mCommunicateBufferPhys, 32);
+ IoWrite32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_DMA_BUFFER_ADDR_LO, AddrLo);
+ IoWrite32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_DMA_BUFFER_ADDR_HI, AddrHi);
+ IoWrite32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_BUFFER_SIZE, MAX_BUFFER_SIZE);
+
+ return RETURN_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwPioTransfer (
+ VOID *Buffer,
+ UINT32 BufferSize,
+ BOOLEAN ToDevice
+ )
+{
+ UINT32 *Ptr = Buffer;
+ UINT32 Bytes = 0;
+ UINT32 Crc1;
+ UINT32 Crc2;
+ EFI_STATUS Status;
+
+ Status = VirtMmHwCommand (UEFI_VARS_CMD_PIO_ZERO_OFFSET);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: zero offset failed: %d\n", __func__, Status));
+ return Status;
+ }
+
+ while (Bytes < BufferSize) {
+ if (ToDevice) {
+ IoWrite32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_PIO_BUFFER_TRANSFER, *Ptr);
+ } else {
+ *Ptr = IoRead32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_PIO_BUFFER_TRANSFER);
+ }
+
+ Bytes += sizeof (*Ptr);
+ Ptr++;
+ }
+
+ Crc1 = CalculateCrc32c (Buffer, Bytes, 0);
+ Crc2 = IoRead32 (UEFI_VARS_IO_BASE + UEFI_VARS_REG_PIO_BUFFER_CRC32C);
+ if (Crc1 != Crc2) {
+ DEBUG ((DEBUG_ERROR, "%a: crc32c mismatch (0x%08x,0x%08x)\n", __func__, Crc1, Crc2));
+ return RETURN_DEVICE_ERROR;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwVirtMap (
+ VOID
+ )
+{
+ return RETURN_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+VirtMmHwComm (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Cmd;
+
+ Cmd = mUsePioTransfer ? UEFI_VARS_CMD_PIO_MM : UEFI_VARS_CMD_DMA_MM;
+ Status = VirtMmHwCommand (Cmd);
+ DEBUG ((DEBUG_VERBOSE, "%a: Status: %r\n", __func__, Status));
+
+ return Status;
+}
diff --git a/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c new file mode 100644 index 0000000..8e6679e --- /dev/null +++ b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.c @@ -0,0 +1,400 @@ +/** @file
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Protocol/MmCommunication2.h>
+
+#include "VirtMmCommunication.h"
+
+VOID *mCommunicateBuffer;
+EFI_PHYSICAL_ADDRESS mCommunicateBufferPhys;
+BOOLEAN mUsePioTransfer = FALSE;
+
+// Notification event when virtual address map is set.
+STATIC EFI_EVENT mSetVirtualAddressMapEvent;
+
+// Handle to install the MM Communication Protocol
+STATIC EFI_HANDLE mMmCommunicateHandle;
+
+// Handle to install the EfiSmmVariableProtocol
+STATIC EFI_HANDLE mSmmVariableHandle;
+
+// Handle to install the SmmVariableWrite
+STATIC EFI_HANDLE mSmmVariableWriteHandle;
+
+/**
+ Communicates with a registered handler.
+
+ This function provides a service to send and receive messages from a registered UEFI service.
+
+ @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL instance.
+ @param[in, out] CommBufferPhysical Physical address of the MM communication buffer
+ @param[in, out] CommBufferVirtual Virtual address of the MM communication buffer
+ @param[in, out] CommSize The size of the data buffer being passed in. On input,
+ when not omitted, the buffer should cover EFI_MM_COMMUNICATE_HEADER
+ and the value of MessageLength field. On exit, the size
+ of data being returned. Zero if the handler does not
+ wish to reply with any data. This parameter is optional
+ and may be NULL.
+
+ @retval EFI_SUCCESS The message was successfully posted.
+ @retval EFI_INVALID_PARAMETER CommBufferPhysical or CommBufferVirtual was NULL, or
+ integer value pointed by CommSize does not cover
+ EFI_MM_COMMUNICATE_HEADER and the value of MessageLength
+ field.
+ @retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation.
+ If this error is returned, the MessageLength field
+ in the CommBuffer header or the integer pointed by
+ CommSize, are updated to reflect the maximum payload
+ size the implementation can accommodate.
+ @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter,
+ if not omitted, are in address range that cannot be
+ accessed by the MM environment.
+
+**/
+EFI_STATUS
+EFIAPI
+VirtMmCommunication2Communicate (
+ IN CONST EFI_MM_COMMUNICATION2_PROTOCOL *This,
+ IN OUT VOID *CommBufferPhysical,
+ IN OUT VOID *CommBufferVirtual,
+ IN OUT UINTN *CommSize OPTIONAL
+ )
+{
+ EFI_MM_COMMUNICATE_HEADER *CommunicateHeader;
+ EFI_STATUS Status;
+ UINTN BufferSize;
+
+ //
+ // Check parameters
+ //
+ if ((CommBufferVirtual == NULL) || (CommBufferPhysical == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ CommunicateHeader = CommBufferVirtual;
+ // CommBuffer is a mandatory parameter. Hence, Rely on
+ // MessageLength + Header to ascertain the
+ // total size of the communication payload rather than
+ // rely on optional CommSize parameter
+ BufferSize = CommunicateHeader->MessageLength +
+ sizeof (CommunicateHeader->HeaderGuid) +
+ sizeof (CommunicateHeader->MessageLength);
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: %g msglen %ld, BufferSize %ld, CommSize %ld\n",
+ __func__,
+ &CommunicateHeader->HeaderGuid,
+ CommunicateHeader->MessageLength,
+ BufferSize,
+ *CommSize
+ ));
+
+ // If CommSize is not omitted, perform size inspection before proceeding.
+ if (CommSize != NULL) {
+ // This case can be used by the consumer of this driver to find out the
+ // max size that can be used for allocating CommBuffer.
+ if ((*CommSize == 0) ||
+ (*CommSize > MAX_BUFFER_SIZE))
+ {
+ *CommSize = MAX_BUFFER_SIZE;
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // CommSize should cover at least MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER);
+ //
+ if (*CommSize < BufferSize) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // If the message length is 0 or greater than what can be tolerated by the MM
+ // environment then return the expected size.
+ //
+ if ((CommunicateHeader->MessageLength == 0) ||
+ (BufferSize > MAX_BUFFER_SIZE))
+ {
+ CommunicateHeader->MessageLength = MAX_BUFFER_SIZE -
+ sizeof (CommunicateHeader->HeaderGuid) -
+ sizeof (CommunicateHeader->MessageLength);
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ // MessageLength or CommSize check has failed, return here.
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: check error: %r\n", __func__, Status));
+ return Status;
+ }
+
+ if (mUsePioTransfer) {
+ Status = VirtMmHwPioTransfer (CommBufferVirtual, BufferSize, TRUE);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: pio write error: %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = VirtMmHwComm ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: pio comm error: %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = VirtMmHwPioTransfer (CommBufferVirtual, BufferSize, FALSE);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: pio read error: %r\n", __func__, Status));
+ return Status;
+ }
+ } else {
+ CopyMem (mCommunicateBuffer, CommBufferVirtual, BufferSize);
+
+ Status = VirtMmHwComm ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: dma comm error: %r\n", __func__, Status));
+ return Status;
+ }
+
+ CopyMem (CommBufferVirtual, mCommunicateBuffer, BufferSize);
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a: success (%d)\n", __func__, BufferSize));
+ return EFI_SUCCESS;
+}
+
+//
+// MM Communication Protocol instance
+//
+STATIC EFI_MM_COMMUNICATION2_PROTOCOL mMmCommunication2 = {
+ VirtMmCommunication2Communicate
+};
+
+/**
+ Notification callback on SetVirtualAddressMap event.
+
+ This function notifies the MM communication protocol interface on
+ SetVirtualAddressMap event and converts pointers used in this driver
+ from physical to virtual address.
+
+ @param Event SetVirtualAddressMap event.
+ @param Context A context when the SetVirtualAddressMap triggered.
+
+ @retval EFI_SUCCESS The function executed successfully.
+ @retval Other Some error occurred when executing this function.
+
+**/
+STATIC
+VOID
+EFIAPI
+VirtMmNotifySetVirtualAddressMap (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: << %p\n", __func__, mCommunicateBuffer));
+ Status = gRT->ConvertPointer (EFI_OPTIONAL_PTR, &mCommunicateBuffer);
+ DEBUG ((DEBUG_VERBOSE, "%a: >> %p\n", __func__, mCommunicateBuffer));
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Unable to convert MM runtime pointer. Status: %r\n",
+ __func__,
+ Status
+ ));
+ }
+
+ Status = VirtMmHwVirtMap ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: VirtMmHwVirtMap failed. Status: %r\n",
+ __func__,
+ Status
+ ));
+ }
+}
+
+STATIC EFI_GUID *CONST mGuidedEventGuid[] = {
+ &gEfiEndOfDxeEventGroupGuid,
+ &gEfiEventExitBootServicesGuid,
+ &gEfiEventReadyToBootGuid,
+};
+
+STATIC EFI_EVENT mGuidedEvent[ARRAY_SIZE (mGuidedEventGuid)];
+
+/**
+ Event notification that is fired when GUIDed Event Group is signaled.
+
+ @param Event The Event that is being processed, not used.
+ @param Context Event Context, not used.
+
+**/
+STATIC
+VOID
+EFIAPI
+VirtMmGuidedEventNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_MM_COMMUNICATE_HEADER Header;
+ UINTN Size;
+
+ //
+ // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure
+ //
+ CopyGuid (&Header.HeaderGuid, Context);
+ Header.MessageLength = 1;
+ Header.Data[0] = 0;
+
+ Size = sizeof (Header);
+ VirtMmCommunication2Communicate (&mMmCommunication2, &Header, &Header, &Size);
+}
+
+/**
+ The Entry Point for MM Communication
+
+ This function installs the MM communication protocol interface and finds out
+ what type of buffer management will be required prior to invoking the
+ communication SMC.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval Other Some error occurred when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+VirtMmCommunication2Initialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+
+ if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) {
+ ASSERT (!"Variable driver runtime cache is not supported.\n");
+ CpuDeadLoop ();
+ }
+
+ if (FeaturePcdGet (PcdVariableCollectStatistics)) {
+ ASSERT (!"Variable driver statistics are not supported.\n");
+ CpuDeadLoop ();
+ }
+
+ mCommunicateBuffer = AllocateRuntimePages (EFI_SIZE_TO_PAGES (MAX_BUFFER_SIZE));
+ if (!mCommunicateBuffer) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ReturnErrorStatus;
+ }
+
+ mCommunicateBufferPhys = (EFI_PHYSICAL_ADDRESS)(UINTN)(mCommunicateBuffer);
+ Status = VirtMmHwInit ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Failed to init HW: %r\n",
+ __func__,
+ Status
+ ));
+ goto FreeBufferPages;
+ }
+
+ // Install the communication protocol
+ Status = gBS->InstallProtocolInterface (
+ &mMmCommunicateHandle,
+ &gEfiMmCommunication2ProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mMmCommunication2
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Failed to install MM communication protocol\n",
+ __func__
+ ));
+ goto FreeBufferPages;
+ }
+
+ Status = gBS->InstallProtocolInterface (
+ &mSmmVariableHandle,
+ &gEfiSmmVariableProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->InstallProtocolInterface (
+ &mSmmVariableWriteHandle,
+ &gSmmVariableWriteGuid,
+ EFI_NATIVE_INTERFACE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ // Register notification callback when virtual address is associated
+ // with the physical address.
+ // Create a Set Virtual Address Map event.
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
+ TPL_NOTIFY,
+ VirtMmNotifySetVirtualAddressMap,
+ NULL,
+ &mSetVirtualAddressMapEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ for (Index = 0; Index < ARRAY_SIZE (mGuidedEventGuid); Index++) {
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ VirtMmGuidedEventNotify,
+ mGuidedEventGuid[Index],
+ mGuidedEventGuid[Index],
+ &mGuidedEvent[Index]
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ while (Index-- > 0) {
+ gBS->CloseEvent (mGuidedEvent[Index]);
+ }
+
+ goto UninstallProtocol;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+UninstallProtocol:
+ gBS->UninstallProtocolInterface (
+ mMmCommunicateHandle,
+ &gEfiMmCommunication2ProtocolGuid,
+ &mMmCommunication2
+ );
+
+FreeBufferPages:
+ FreePages (mCommunicateBuffer, EFI_SIZE_TO_PAGES (MAX_BUFFER_SIZE));
+
+ReturnErrorStatus:
+ return EFI_INVALID_PARAMETER;
+}
diff --git a/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h new file mode 100644 index 0000000..ee8268c --- /dev/null +++ b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.h @@ -0,0 +1,53 @@ +/** @file
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _VIRT_MM_COMM_DXE_H_
+#define _VIRT_MM_COMM_DXE_H_
+
+/* communication buffer */
+
+#define MAX_BUFFER_SIZE (64 * 1024)
+
+extern VOID *mCommunicateBuffer;
+extern EFI_PHYSICAL_ADDRESS mCommunicateBufferPhys;
+extern UINT64 mUefiVarsAddr;
+extern BOOLEAN mUsePioTransfer;
+
+/* arch specific hooks */
+
+EFI_STATUS
+EFIAPI
+VirtMmHwFind (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+VirtMmHwInit (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+VirtMmHwComm (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+VirtMmHwVirtMap (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+VirtMmHwPioTransfer (
+ VOID *Buffer,
+ UINT32 BufferSize,
+ BOOLEAN ToDevice
+ );
+
+#endif /* _VIRT_MM_COMM_DXE_H_ */
diff --git a/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf new file mode 100644 index 0000000..1bfe92e --- /dev/null +++ b/OvmfPkg/VirtMmCommunicationDxe/VirtMmCommunication.inf @@ -0,0 +1,72 @@ +#/** @file
+#
+# Virt MM Communicate driver
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x0001001A
+ BASE_NAME = VirtMmCommunication
+ FILE_GUID = 0B807404-2D1C-4066-95F4-F28A58800185
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = VirtMmCommunication2Initialize
+
+[Sources]
+ VirtMmCommunication.c
+
+[Sources.X64]
+# QemuX64.c
+ QemuHwInfo.c
+ QemuMmio.c
+
+[Sources.AARCH64, Sources.ARM]
+ QemuFdt.c
+ QemuMmio.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[Packages.AARCH64, Packages.ARM]
+ EmbeddedPkg/EmbeddedPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DxeServicesTableLib
+ HobLib
+ MemoryAllocationLib
+ UefiDriverEntryPoint
+
+[LibraryClasses.X64]
+ DxeHardwareInfoLib
+ QemuFwCfgLib
+
+[LibraryClasses.AARCH64, LibraryClasses.ARM]
+ FdtLib
+
+[FeaturePcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEnableVariableRuntimeCache ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics ## CONSUMES
+
+[Protocols]
+ gEfiMmCommunication2ProtocolGuid ## PRODUCES
+ gEfiSmmVariableProtocolGuid ## PRODUCES
+
+[Protocols.AARCH64, Protocols.ARM]
+ gFdtClientProtocolGuid
+
+[Guids]
+ gEfiEndOfDxeEventGroupGuid
+ gEfiEventExitBootServicesGuid
+ gEfiEventReadyToBootGuid
+ gSmmVariableWriteGuid
+
+[Depex]
+ gEfiCpuArchProtocolGuid
+
+[Depex.AARCH64, Depex.ARM]
+ gFdtClientProtocolGuid
|