| /** @file | |
| Stateful and implicitly initialized fw_cfg library implementation. | |
| Copyright (C) 2013 - 2014, Red Hat, Inc. | |
| Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR> | |
| (C) Copyright 2021 Hewlett Packard Enterprise Development LP<BR> | |
| Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Uefi.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/FdtLib.h> | |
| #include <Library/IoLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/QemuFwCfgLib.h> | |
| #include "QemuFwCfgLibMmioInternal.h" | |
| /** | |
| To get firmware configure selector address. | |
| @param VOID | |
| @retval firmware configure selector address | |
| **/ | |
| UINTN | |
| EFIAPI | |
| QemuGetFwCfgSelectorAddress ( | |
| VOID | |
| ) | |
| { | |
| QEMU_FW_CFG_RESOURCE *FwCfgResource; | |
| FwCfgResource = QemuGetFwCfgResourceHob (); | |
| ASSERT (FwCfgResource != NULL); | |
| return FwCfgResource->FwCfgSelectorAddress; | |
| } | |
| /** | |
| To get firmware configure Data address. | |
| @param VOID | |
| @retval firmware configure data address | |
| **/ | |
| UINTN | |
| EFIAPI | |
| QemuGetFwCfgDataAddress ( | |
| VOID | |
| ) | |
| { | |
| QEMU_FW_CFG_RESOURCE *FwCfgResource; | |
| FwCfgResource = QemuGetFwCfgResourceHob (); | |
| ASSERT (FwCfgResource != NULL); | |
| return FwCfgResource->FwCfgDataAddress; | |
| } | |
| /** | |
| To get firmware DMA address. | |
| @param VOID | |
| @retval firmware DMA address | |
| **/ | |
| UINTN | |
| EFIAPI | |
| QemuGetFwCfgDmaAddress ( | |
| VOID | |
| ) | |
| { | |
| QEMU_FW_CFG_RESOURCE *FwCfgResource; | |
| FwCfgResource = QemuGetFwCfgResourceHob (); | |
| ASSERT (FwCfgResource != NULL); | |
| return FwCfgResource->FwCfgDmaAddress; | |
| } | |
| RETURN_STATUS | |
| EFIAPI | |
| QemuFwCfgInitialize ( | |
| VOID | |
| ) | |
| { | |
| VOID *DeviceTreeBase; | |
| INT32 Node; | |
| INT32 Prev; | |
| UINT32 Signature; | |
| CONST CHAR8 *Type; | |
| INT32 Len; | |
| CONST UINT64 *Reg; | |
| UINT64 FwCfgSelectorAddress; | |
| UINT64 FwCfgSelectorSize; | |
| UINT64 FwCfgDataAddress; | |
| UINT64 FwCfgDataSize; | |
| UINT64 FwCfgDmaAddress; | |
| UINT64 FwCfgDmaSize; | |
| QEMU_FW_CFG_RESOURCE *FwCfgResource; | |
| // | |
| // Check whether the Qemu firmware configure resources HOB has been created, | |
| // if so use the resources in the HOB. | |
| // | |
| FwCfgResource = QemuGetFwCfgResourceHob (); | |
| if (FwCfgResource != NULL) { | |
| return RETURN_SUCCESS; | |
| } | |
| DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress); | |
| ASSERT (DeviceTreeBase != NULL); | |
| // | |
| // Make sure we have a valid device tree blob | |
| // | |
| ASSERT (FdtCheckHeader (DeviceTreeBase) == 0); | |
| // | |
| // Create resouce memory | |
| // | |
| FwCfgResource = AllocateZeroPool (sizeof (QEMU_FW_CFG_RESOURCE)); | |
| ASSERT (FwCfgResource != NULL); | |
| for (Prev = 0; ; Prev = Node) { | |
| Node = FdtNextNode (DeviceTreeBase, Prev, NULL); | |
| if (Node < 0) { | |
| break; | |
| } | |
| // | |
| // Check for memory node | |
| // | |
| Type = FdtGetProp (DeviceTreeBase, Node, "compatible", &Len); | |
| if ((Type != NULL) && | |
| (AsciiStrnCmp (Type, "qemu,fw-cfg-mmio", Len) == 0)) | |
| { | |
| // | |
| // Get the 'reg' property of this node. For now, we will assume | |
| // two 8 byte quantities for base and size, respectively. | |
| // | |
| Reg = FdtGetProp (DeviceTreeBase, Node, "reg", &Len); | |
| if ((Reg != 0) && (Len == (2 * sizeof (UINT64)))) { | |
| FwCfgDataAddress = SwapBytes64 (Reg[0]); | |
| FwCfgDataSize = 8; | |
| FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize; | |
| FwCfgSelectorSize = 2; | |
| // | |
| // The following ASSERT()s express | |
| // | |
| // Address + Size - 1 <= MAX_UINTN | |
| // | |
| // for both registers, that is, that the last byte in each MMIO range is | |
| // expressible as a MAX_UINTN. The form below is mathematically | |
| // equivalent, and it also prevents any unsigned overflow before the | |
| // comparison. | |
| // | |
| ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1); | |
| ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1); | |
| FwCfgResource->FwCfgSelectorAddress = FwCfgSelectorAddress; | |
| FwCfgResource->FwCfgDataAddress = FwCfgDataAddress; | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "Found FwCfg @ 0x%Lx/0x%Lx\n", | |
| FwCfgSelectorAddress, | |
| FwCfgDataAddress | |
| )); | |
| if (SwapBytes64 (Reg[1]) >= 0x18) { | |
| FwCfgDmaAddress = FwCfgDataAddress + 0x10; | |
| FwCfgDmaSize = 0x08; | |
| // | |
| // See explanation above. | |
| // | |
| ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1); | |
| DEBUG ((DEBUG_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress)); | |
| FwCfgResource->FwCfgDmaAddress = FwCfgDmaAddress; | |
| } else { | |
| FwCfgDmaAddress = 0; | |
| } | |
| if ((FwCfgSelectorAddress != 0) && (FwCfgDataAddress != 0)) { | |
| // | |
| // Select Item Signature | |
| // | |
| MmioWrite16 (FwCfgSelectorAddress, SwapBytes16 ((UINT16)QemuFwCfgItemSignature)); | |
| // | |
| // Readout the Signature. | |
| // | |
| Signature = MmioRead32 (FwCfgDataAddress); | |
| if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) { | |
| // | |
| // Build the firmware configure resource HOB. | |
| // | |
| QemuBuildFwCfgResourceHob (FwCfgResource); | |
| } else { | |
| FwCfgResource->FwCfgDataAddress = 0; | |
| FwCfgResource->FwCfgSelectorAddress = 0; | |
| FwCfgResource->FwCfgDmaAddress = 0; | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Signature dose not match QEMU!\n", | |
| __func__ | |
| )); | |
| break; | |
| } | |
| } | |
| break; | |
| } else { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Failed to parse FDT QemuCfg node\n", | |
| __func__ | |
| )); | |
| break; | |
| } | |
| } | |
| } | |
| return RETURN_SUCCESS; | |
| } |