blob: bb8629065514bd86c30cf18ba64551162ca2feeb [file] [log] [blame]
/** @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;
}