/** @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; | |
} |