mirror of https://github.com/acidanthera/audk.git
610 lines
21 KiB
C
610 lines
21 KiB
C
/** @file
|
|
* Device tree enumeration DXE driver for ARM Virtual Machines
|
|
*
|
|
* Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
|
|
*
|
|
* This program and the accompanying materials are
|
|
* licensed and made available under the terms and conditions of the BSD License
|
|
* which accompanies this distribution. The full text of the license may be found at
|
|
* http://opensource.org/licenses/bsd-license.php
|
|
*
|
|
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
*
|
|
**/
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/UefiDriverEntryPoint.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/VirtioMmioDeviceLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/DxeServicesLib.h>
|
|
#include <Library/HobLib.h>
|
|
#include <libfdt.h>
|
|
#include <Library/XenIoMmioLib.h>
|
|
|
|
#include <Guid/Fdt.h>
|
|
#include <Guid/VirtioMmioTransport.h>
|
|
#include <Guid/FdtHob.h>
|
|
|
|
#pragma pack (1)
|
|
typedef struct {
|
|
VENDOR_DEVICE_PATH Vendor;
|
|
UINT64 PhysBase;
|
|
EFI_DEVICE_PATH_PROTOCOL End;
|
|
} VIRTIO_TRANSPORT_DEVICE_PATH;
|
|
#pragma pack ()
|
|
|
|
typedef enum {
|
|
PropertyTypeUnknown,
|
|
PropertyTypeGic,
|
|
PropertyTypeRtc,
|
|
PropertyTypeVirtio,
|
|
PropertyTypeUart,
|
|
PropertyTypeTimer,
|
|
PropertyTypePsci,
|
|
PropertyTypeFwCfg,
|
|
PropertyTypePciHost,
|
|
PropertyTypeGicV3,
|
|
PropertyTypeXen,
|
|
} PROPERTY_TYPE;
|
|
|
|
typedef struct {
|
|
PROPERTY_TYPE Type;
|
|
CHAR8 Compatible[32];
|
|
} PROPERTY;
|
|
|
|
STATIC CONST PROPERTY CompatibleProperties[] = {
|
|
{ PropertyTypeGic, "arm,cortex-a15-gic" },
|
|
{ PropertyTypeRtc, "arm,pl031" },
|
|
{ PropertyTypeVirtio, "virtio,mmio" },
|
|
{ PropertyTypeUart, "arm,pl011" },
|
|
{ PropertyTypeTimer, "arm,armv7-timer" },
|
|
{ PropertyTypeTimer, "arm,armv8-timer" },
|
|
{ PropertyTypePsci, "arm,psci-0.2" },
|
|
{ PropertyTypeFwCfg, "qemu,fw-cfg-mmio" },
|
|
{ PropertyTypePciHost, "pci-host-ecam-generic" },
|
|
{ PropertyTypeGicV3, "arm,gic-v3" },
|
|
{ PropertyTypeXen, "xen,xen" },
|
|
{ PropertyTypeUnknown, "" }
|
|
};
|
|
|
|
typedef struct {
|
|
UINT32 Type;
|
|
UINT32 Number;
|
|
UINT32 Flags;
|
|
} INTERRUPT_PROPERTY;
|
|
|
|
STATIC
|
|
PROPERTY_TYPE
|
|
GetTypeFromNode (
|
|
IN CONST CHAR8 *NodeType,
|
|
IN UINTN Size
|
|
)
|
|
{
|
|
CONST CHAR8 *Compatible;
|
|
CONST PROPERTY *CompatibleProperty;
|
|
|
|
//
|
|
// A 'compatible' node may contain a sequence of NULL terminated
|
|
// compatible strings so check each one
|
|
//
|
|
for (Compatible = NodeType; Compatible < NodeType + Size && *Compatible;
|
|
Compatible += 1 + AsciiStrLen (Compatible)) {
|
|
for (CompatibleProperty = CompatibleProperties; CompatibleProperty->Compatible[0]; CompatibleProperty++) {
|
|
if (AsciiStrCmp (CompatibleProperty->Compatible, Compatible) == 0) {
|
|
return CompatibleProperty->Type;
|
|
}
|
|
}
|
|
}
|
|
return PropertyTypeUnknown;
|
|
}
|
|
|
|
//
|
|
// We expect the "ranges" property of "pci-host-ecam-generic" to consist of
|
|
// records like this.
|
|
//
|
|
#pragma pack (1)
|
|
typedef struct {
|
|
UINT32 Type;
|
|
UINT64 ChildBase;
|
|
UINT64 CpuBase;
|
|
UINT64 Size;
|
|
} DTB_PCI_HOST_RANGE_RECORD;
|
|
#pragma pack ()
|
|
|
|
#define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
|
|
#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
|
|
#define DTB_PCI_HOST_RANGE_ALIASED BIT29
|
|
#define DTB_PCI_HOST_RANGE_MMIO32 BIT25
|
|
#define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
|
|
#define DTB_PCI_HOST_RANGE_IO BIT24
|
|
#define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
|
|
|
|
/**
|
|
Process the device tree node describing the generic PCI host controller.
|
|
|
|
param[in] DeviceTreeBase Pointer to the device tree.
|
|
|
|
param[in] Node Offset of the device tree node whose "compatible"
|
|
property is "pci-host-ecam-generic".
|
|
|
|
param[in] RegProp Pointer to the "reg" property of Node. The caller
|
|
is responsible for ensuring that the size of the
|
|
property is 4 UINT32 cells.
|
|
|
|
@retval EFI_SUCCESS Parsing successful, properties parsed from Node
|
|
have been stored in dynamic PCDs.
|
|
|
|
@retval EFI_PROTOCOL_ERROR Parsing failed. PCDs are left unchanged.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ProcessPciHost (
|
|
IN CONST VOID *DeviceTreeBase,
|
|
IN INT32 Node,
|
|
IN CONST VOID *RegProp
|
|
)
|
|
{
|
|
UINT64 ConfigBase, ConfigSize;
|
|
CONST VOID *Prop;
|
|
INT32 Len;
|
|
UINT32 BusMin, BusMax;
|
|
UINT32 RecordIdx;
|
|
UINT64 IoBase, IoSize, IoTranslation;
|
|
UINT64 MmioBase, MmioSize, MmioTranslation;
|
|
|
|
//
|
|
// Fetch the ECAM window.
|
|
//
|
|
ConfigBase = fdt64_to_cpu (((CONST UINT64 *)RegProp)[0]);
|
|
ConfigSize = fdt64_to_cpu (((CONST UINT64 *)RegProp)[1]);
|
|
|
|
//
|
|
// Fetch the bus range (note: inclusive).
|
|
//
|
|
Prop = fdt_getprop (DeviceTreeBase, Node, "bus-range", &Len);
|
|
if (Prop == NULL || Len != 2 * sizeof(UINT32)) {
|
|
DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",
|
|
__FUNCTION__));
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
BusMin = fdt32_to_cpu (((CONST UINT32 *)Prop)[0]);
|
|
BusMax = fdt32_to_cpu (((CONST UINT32 *)Prop)[1]);
|
|
|
|
//
|
|
// Sanity check: the config space must accommodate all 4K register bytes of
|
|
// all 8 functions of all 32 devices of all buses.
|
|
//
|
|
if (BusMax < BusMin || BusMax - BusMin == MAX_UINT32 ||
|
|
DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < BusMax - BusMin + 1) {
|
|
DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",
|
|
__FUNCTION__));
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Iterate over "ranges".
|
|
//
|
|
Prop = fdt_getprop (DeviceTreeBase, Node, "ranges", &Len);
|
|
if (Prop == NULL || Len == 0 ||
|
|
Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {
|
|
DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
//
|
|
// IoBase, IoTranslation, MmioBase and MmioTranslation are initialized only
|
|
// in order to suppress '-Werror=maybe-uninitialized' warnings *incorrectly*
|
|
// emitted by some gcc versions.
|
|
//
|
|
IoBase = 0;
|
|
IoTranslation = 0;
|
|
MmioBase = 0;
|
|
MmioTranslation = 0;
|
|
|
|
//
|
|
// IoSize and MmioSize are initialized to zero because the logic below
|
|
// requires it.
|
|
//
|
|
IoSize = 0;
|
|
MmioSize = 0;
|
|
for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
|
|
++RecordIdx) {
|
|
CONST DTB_PCI_HOST_RANGE_RECORD *Record;
|
|
|
|
Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
|
|
switch (fdt32_to_cpu (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {
|
|
case DTB_PCI_HOST_RANGE_IO:
|
|
IoBase = fdt64_to_cpu (Record->ChildBase);
|
|
IoSize = fdt64_to_cpu (Record->Size);
|
|
IoTranslation = fdt64_to_cpu (Record->CpuBase) - IoBase;
|
|
break;
|
|
|
|
case DTB_PCI_HOST_RANGE_MMIO32:
|
|
MmioBase = fdt64_to_cpu (Record->ChildBase);
|
|
MmioSize = fdt64_to_cpu (Record->Size);
|
|
MmioTranslation = fdt64_to_cpu (Record->CpuBase) - MmioBase;
|
|
|
|
if (MmioBase > MAX_UINT32 || MmioSize > MAX_UINT32 ||
|
|
MmioBase + MmioSize > SIZE_4GB) {
|
|
DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
if (MmioTranslation != 0) {
|
|
DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "
|
|
"0x%Lx\n", __FUNCTION__, MmioTranslation));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
if (IoSize == 0 || MmioSize == 0) {
|
|
DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,
|
|
(IoSize == 0) ? "IO" : "MMIO32"));
|
|
return EFI_PROTOCOL_ERROR;
|
|
}
|
|
|
|
PcdSet64 (PcdPciExpressBaseAddress, ConfigBase);
|
|
|
|
PcdSet32 (PcdPciBusMin, BusMin);
|
|
PcdSet32 (PcdPciBusMax, BusMax);
|
|
|
|
PcdSet64 (PcdPciIoBase, IoBase);
|
|
PcdSet64 (PcdPciIoSize, IoSize);
|
|
PcdSet64 (PcdPciIoTranslation, IoTranslation);
|
|
|
|
PcdSet32 (PcdPciMmio32Base, (UINT32)MmioBase);
|
|
PcdSet32 (PcdPciMmio32Size, (UINT32)MmioSize);
|
|
|
|
PcdSetBool (PcdPciDisableBusEnumeration, FALSE);
|
|
|
|
DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
|
|
"Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x%Lx\n", __FUNCTION__, ConfigBase,
|
|
ConfigSize, BusMin, BusMax, IoBase, IoSize, IoTranslation, MmioBase,
|
|
MmioSize, MmioTranslation));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitializeVirtFdtDxe (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
VOID *Hob;
|
|
VOID *DeviceTreeBase;
|
|
INT32 Node, Prev;
|
|
INT32 RtcNode;
|
|
EFI_STATUS Status;
|
|
CONST CHAR8 *Type;
|
|
INT32 Len;
|
|
PROPERTY_TYPE PropType;
|
|
CONST VOID *RegProp;
|
|
VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;
|
|
EFI_HANDLE Handle;
|
|
UINT64 RegBase;
|
|
UINT64 DistBase, CpuBase, RedistBase;
|
|
CONST INTERRUPT_PROPERTY *InterruptProp;
|
|
INT32 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum;
|
|
CONST CHAR8 *PsciMethod;
|
|
UINT64 FwCfgSelectorAddress;
|
|
UINT64 FwCfgSelectorSize;
|
|
UINT64 FwCfgDataAddress;
|
|
UINT64 FwCfgDataSize;
|
|
UINT64 FwCfgDmaAddress;
|
|
UINT64 FwCfgDmaSize;
|
|
BOOLEAN HavePci;
|
|
|
|
Hob = GetFirstGuidHob(&gFdtHobGuid);
|
|
if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
|
|
|
|
if (fdt_check_header (DeviceTreeBase) != 0) {
|
|
DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__, DeviceTreeBase));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));
|
|
|
|
RtcNode = -1;
|
|
HavePci = FALSE;
|
|
//
|
|
// Now enumerate the nodes and install peripherals that we are interested in,
|
|
// i.e., GIC, RTC and virtio MMIO nodes
|
|
//
|
|
for (Prev = 0;; Prev = Node) {
|
|
Node = fdt_next_node (DeviceTreeBase, Prev, NULL);
|
|
if (Node < 0) {
|
|
break;
|
|
}
|
|
|
|
Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);
|
|
if (Type == NULL) {
|
|
continue;
|
|
}
|
|
|
|
PropType = GetTypeFromNode (Type, Len);
|
|
if (PropType == PropertyTypeUnknown) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the 'reg' property of this node. For now, we will assume
|
|
// 8 byte quantities for base and size, respectively.
|
|
// TODO use #cells root properties instead
|
|
//
|
|
RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);
|
|
ASSERT ((RegProp != NULL) || (PropType == PropertyTypeTimer) ||
|
|
(PropType == PropertyTypePsci));
|
|
|
|
switch (PropType) {
|
|
case PropertyTypePciHost:
|
|
ASSERT (Len == 2 * sizeof (UINT64));
|
|
Status = ProcessPciHost (DeviceTreeBase, Node, RegProp);
|
|
ASSERT_EFI_ERROR (Status);
|
|
HavePci = TRUE;
|
|
break;
|
|
|
|
case PropertyTypeFwCfg:
|
|
ASSERT (Len == 2 * sizeof (UINT64));
|
|
|
|
FwCfgDataAddress = fdt64_to_cpu (((UINT64 *)RegProp)[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);
|
|
|
|
PcdSet64 (PcdFwCfgSelectorAddress, FwCfgSelectorAddress);
|
|
PcdSet64 (PcdFwCfgDataAddress, FwCfgDataAddress);
|
|
|
|
DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,
|
|
FwCfgDataAddress));
|
|
|
|
if (fdt64_to_cpu (((UINT64 *)RegProp)[1]) >= 0x18) {
|
|
FwCfgDmaAddress = FwCfgDataAddress + 0x10;
|
|
FwCfgDmaSize = 0x08;
|
|
|
|
//
|
|
// See explanation above.
|
|
//
|
|
ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);
|
|
|
|
PcdSet64 (PcdFwCfgDmaAddress, FwCfgDmaAddress);
|
|
DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));
|
|
}
|
|
break;
|
|
|
|
case PropertyTypeVirtio:
|
|
ASSERT (Len == 16);
|
|
//
|
|
// Create a unique device path for this transport on the fly
|
|
//
|
|
RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
|
|
DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode (
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_VENDOR_DP,
|
|
sizeof (VIRTIO_TRANSPORT_DEVICE_PATH));
|
|
if (DevicePath == NULL) {
|
|
DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));
|
|
break;
|
|
}
|
|
|
|
CopyMem (&DevicePath->Vendor.Guid, &gVirtioMmioTransportGuid,
|
|
sizeof (EFI_GUID));
|
|
DevicePath->PhysBase = RegBase;
|
|
SetDevicePathNodeLength (&DevicePath->Vendor,
|
|
sizeof (*DevicePath) - sizeof (DevicePath->End));
|
|
SetDevicePathEndNode (&DevicePath->End);
|
|
|
|
Handle = NULL;
|
|
Status = gBS->InstallProtocolInterface (&Handle,
|
|
&gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,
|
|
DevicePath);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "%a: Failed to install the EFI_DEVICE_PATH "
|
|
"protocol on a new handle (Status == %r)\n",
|
|
__FUNCTION__, Status));
|
|
FreePool (DevicePath);
|
|
break;
|
|
}
|
|
|
|
Status = VirtioMmioInstallDevice (RegBase, Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "%a: Failed to install VirtIO transport @ 0x%Lx "
|
|
"on handle %p (Status == %r)\n", __FUNCTION__, RegBase,
|
|
Handle, Status));
|
|
|
|
Status = gBS->UninstallProtocolInterface (Handle,
|
|
&gEfiDevicePathProtocolGuid, DevicePath);
|
|
ASSERT_EFI_ERROR (Status);
|
|
FreePool (DevicePath);
|
|
}
|
|
break;
|
|
|
|
case PropertyTypeGic:
|
|
ASSERT (Len == 32);
|
|
|
|
DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
|
|
CpuBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);
|
|
ASSERT (DistBase < MAX_UINT32);
|
|
ASSERT (CpuBase < MAX_UINT32);
|
|
|
|
PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);
|
|
PcdSet32 (PcdGicInterruptInterfaceBase, (UINT32)CpuBase);
|
|
PcdSet32 (PcdArmGicRevision, 2);
|
|
|
|
DEBUG ((EFI_D_INFO, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase, CpuBase));
|
|
break;
|
|
|
|
case PropertyTypeGicV3:
|
|
//
|
|
// The GIC v3 DT binding describes a series of at least 3 physical (base
|
|
// addresses, size) pairs: the distributor interface (GICD), at least one
|
|
// redistributor region (GICR) containing dedicated redistributor
|
|
// interfaces for all individual CPUs, and the CPU interface (GICC).
|
|
// Under virtualization, we assume that the first redistributor region
|
|
// listed covers the boot CPU. Also, our GICv3 driver only supports the
|
|
// system register CPU interface, so we can safely ignore the MMIO version
|
|
// which is listed after the sequence of redistributor interfaces.
|
|
// This means we are only interested in the first two memory regions
|
|
// supplied, and ignore everything else.
|
|
//
|
|
ASSERT (Len >= 32);
|
|
|
|
// RegProp[0..1] == { GICD base, GICD size }
|
|
DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
|
|
ASSERT (DistBase < MAX_UINT32);
|
|
|
|
// RegProp[2..3] == { GICR base, GICR size }
|
|
RedistBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);
|
|
ASSERT (RedistBase < MAX_UINT32);
|
|
|
|
PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);
|
|
PcdSet32 (PcdGicRedistributorsBase, (UINT32)RedistBase);
|
|
PcdSet32 (PcdArmGicRevision, 3);
|
|
|
|
DEBUG ((EFI_D_INFO, "Found GIC v3 (re)distributor @ 0x%Lx (0x%Lx)\n",
|
|
DistBase, RedistBase));
|
|
break;
|
|
|
|
case PropertyTypeRtc:
|
|
ASSERT (Len == 16);
|
|
|
|
RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
|
|
ASSERT (RegBase < MAX_UINT32);
|
|
|
|
PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);
|
|
|
|
DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));
|
|
RtcNode = Node;
|
|
break;
|
|
|
|
case PropertyTypeTimer:
|
|
//
|
|
// - interrupts : Interrupt list for secure, non-secure, virtual and
|
|
// hypervisor timers, in that order.
|
|
//
|
|
InterruptProp = fdt_getprop (DeviceTreeBase, Node, "interrupts", &Len);
|
|
ASSERT (Len == 36 || Len == 48);
|
|
|
|
SecIntrNum = fdt32_to_cpu (InterruptProp[0].Number)
|
|
+ (InterruptProp[0].Type ? 16 : 0);
|
|
IntrNum = fdt32_to_cpu (InterruptProp[1].Number)
|
|
+ (InterruptProp[1].Type ? 16 : 0);
|
|
VirtIntrNum = fdt32_to_cpu (InterruptProp[2].Number)
|
|
+ (InterruptProp[2].Type ? 16 : 0);
|
|
HypIntrNum = Len < 48 ? 0 : fdt32_to_cpu (InterruptProp[3].Number)
|
|
+ (InterruptProp[3].Type ? 16 : 0);
|
|
|
|
DEBUG ((EFI_D_INFO, "Found Timer interrupts %d, %d, %d, %d\n",
|
|
SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum));
|
|
|
|
PcdSet32 (PcdArmArchTimerSecIntrNum, SecIntrNum);
|
|
PcdSet32 (PcdArmArchTimerIntrNum, IntrNum);
|
|
PcdSet32 (PcdArmArchTimerVirtIntrNum, VirtIntrNum);
|
|
PcdSet32 (PcdArmArchTimerHypIntrNum, HypIntrNum);
|
|
break;
|
|
|
|
case PropertyTypePsci:
|
|
PsciMethod = fdt_getprop (DeviceTreeBase, Node, "method", &Len);
|
|
|
|
if (PsciMethod && AsciiStrnCmp (PsciMethod, "hvc", 3) == 0) {
|
|
PcdSet32 (PcdArmPsciMethod, 1);
|
|
} else if (PsciMethod && AsciiStrnCmp (PsciMethod, "smc", 3) == 0) {
|
|
PcdSet32 (PcdArmPsciMethod, 2);
|
|
} else {
|
|
DEBUG ((EFI_D_ERROR, "%a: Unknown PSCI method \"%a\"\n", __FUNCTION__,
|
|
PsciMethod));
|
|
}
|
|
break;
|
|
|
|
case PropertyTypeXen:
|
|
ASSERT (Len == 16);
|
|
|
|
//
|
|
// Retrieve the reg base from this node and wire it up to the
|
|
// MMIO flavor of the XenBus root device I/O protocol
|
|
//
|
|
RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
|
|
Handle = NULL;
|
|
Status = XenIoMmioInstall (&Handle, RegBase);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "%a: XenIoMmioInstall () failed on a new handle "
|
|
"(Status == %r)\n", __FUNCTION__, Status));
|
|
break;
|
|
}
|
|
|
|
DEBUG ((EFI_D_INFO, "Found Xen node with Grant table @ 0x%Lx\n", RegBase));
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// UEFI takes ownership of the RTC hardware, and exposes its functionality
|
|
// through the UEFI Runtime Services GetTime, SetTime, etc. This means we
|
|
// need to disable it in the device tree to prevent the OS from attaching its
|
|
// device driver as well.
|
|
//
|
|
if ((RtcNode != -1) &&
|
|
fdt_setprop_string (DeviceTreeBase, RtcNode, "status",
|
|
"disabled") != 0) {
|
|
DEBUG ((EFI_D_WARN, "Failed to set PL031 status to 'disabled'\n"));
|
|
}
|
|
|
|
if (HavePci) {
|
|
//
|
|
// Set the /chosen/linux,pci-probe-only property to 1, so that the PCI
|
|
// setup we will perform in the firmware is honored by the Linux OS,
|
|
// rather than torn down and done from scratch. This is generally a more
|
|
// sensible approach, and aligns with what ACPI based OSes do in general.
|
|
//
|
|
// In case we are exposing an emulated VGA PCI device to the guest, which
|
|
// may subsequently get exposed via the Graphics Output protocol and
|
|
// driven as an efifb by Linux, we need this setting to prevent the
|
|
// framebuffer from becoming unresponsive.
|
|
//
|
|
Node = fdt_path_offset (DeviceTreeBase, "/chosen");
|
|
if (Node < 0) {
|
|
Node = fdt_add_subnode (DeviceTreeBase, 0, "/chosen");
|
|
}
|
|
if (Node < 0 ||
|
|
fdt_setprop_u32 (DeviceTreeBase, Node, "linux,pci-probe-only", 1) < 0) {
|
|
DEBUG ((EFI_D_WARN, "Failed to set /chosen/linux,pci-probe-only property\n"));
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|