diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c new file mode 100644 index 0000000000..d002e668aa --- /dev/null +++ b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c @@ -0,0 +1,291 @@ +/** @file +* Device tree enumeration DXE driver for ARM Virtual Machines +* +* Copyright (c) 2014, Linaro Ltd. All rights reserved.
+* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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, +} PROPERTY_TYPE; + +typedef struct { + PROPERTY_TYPE Type; + CHAR8 Compatible[20]; +} 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" }, + { 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; +} + +EFI_STATUS +EFIAPI +InitializeVirtFdtDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + 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; + CONST INTERRUPT_PROPERTY *InterruptProp; + INT32 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum; + CONST CHAR8 *PsciMethod; + + DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeBaseAddress); + ASSERT (DeviceTreeBase != NULL); + + 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; + // + // 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 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, &gEfiCallerIdGuid, 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); + + DEBUG ((EFI_D_INFO, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase, CpuBase)); + 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 == 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 = 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; + + 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")); + } + return EFI_SUCCESS; +} diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf new file mode 100644 index 0000000000..1f10179f05 --- /dev/null +++ b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf @@ -0,0 +1,62 @@ +## @file +# Device tree enumeration DXE driver for ARM Virtual Machines +# +# Copyright (c) 2014, Linaro Ltd. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VirtFdtDxe + FILE_GUID = 837DCA9E-E874-4D82-B29A-23FE0E23D1E2 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeVirtFdtDxe + +[Sources] + VirtFdtDxe.c + +[Packages] + MdePkg/MdePkg.dec + ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + PcdLib + UefiDriverEntryPoint + DxeServicesLib + FdtLib + VirtioMmioDeviceLib + +[Guids] + gFdtTableGuid + +[Pcd] + gArmVirtualizationTokenSpaceGuid.PcdDeviceTreeBaseAddress + gArmTokenSpaceGuid.PcdGicDistributorBase + gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase + gArmTokenSpaceGuid.PcdArmArchTimerSecIntrNum + gArmTokenSpaceGuid.PcdArmArchTimerIntrNum + gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum + gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum + gArmPlatformTokenSpaceGuid.PcdPL031RtcBase + gArmTokenSpaceGuid.PcdArmPsciMethod + +[Protocols] + gEfiDevicePathProtocolGuid + +[Depex] + TRUE