--- /dev/null
+/** @file\r
+* Device tree enumeration DXE driver for ARM Virtual Machines\r
+*\r
+* Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>\r
+*\r
+* This program and the accompanying materials are\r
+* licensed and made available under the terms and conditions of the BSD License\r
+* which accompanies this distribution. The full text of the license may be found at\r
+* http://opensource.org/licenses/bsd-license.php\r
+*\r
+* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+*\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/VirtioMmioDeviceLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/DxeServicesLib.h>\r
+#include <libfdt.h>\r
+\r
+#include <Guid/Fdt.h>\r
+\r
+#pragma pack (1)\r
+typedef struct {\r
+ VENDOR_DEVICE_PATH Vendor;\r
+ UINT64 PhysBase;\r
+ EFI_DEVICE_PATH_PROTOCOL End;\r
+} VIRTIO_TRANSPORT_DEVICE_PATH;\r
+#pragma pack ()\r
+\r
+typedef enum {\r
+ PropertyTypeUnknown,\r
+ PropertyTypeGic,\r
+ PropertyTypeRtc,\r
+ PropertyTypeVirtio,\r
+ PropertyTypeUart,\r
+ PropertyTypeTimer,\r
+ PropertyTypePsci,\r
+} PROPERTY_TYPE;\r
+\r
+typedef struct {\r
+ PROPERTY_TYPE Type;\r
+ CHAR8 Compatible[20];\r
+} PROPERTY;\r
+\r
+STATIC CONST PROPERTY CompatibleProperties[] = {\r
+ { PropertyTypeGic, "arm,cortex-a15-gic" },\r
+ { PropertyTypeRtc, "arm,pl031" },\r
+ { PropertyTypeVirtio, "virtio,mmio" },\r
+ { PropertyTypeUart, "arm,pl011" },\r
+ { PropertyTypeTimer, "arm,armv7-timer" },\r
+ { PropertyTypeTimer, "arm,armv8-timer" },\r
+ { PropertyTypePsci, "arm,psci-0.2" },\r
+ { PropertyTypeUnknown, "" }\r
+};\r
+\r
+typedef struct {\r
+ UINT32 Type;\r
+ UINT32 Number;\r
+ UINT32 Flags;\r
+} INTERRUPT_PROPERTY;\r
+\r
+STATIC\r
+PROPERTY_TYPE\r
+GetTypeFromNode (\r
+ IN CONST CHAR8 *NodeType,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ CONST CHAR8 *Compatible;\r
+ CONST PROPERTY *CompatibleProperty;\r
+\r
+ //\r
+ // A 'compatible' node may contain a sequence of NULL terminated\r
+ // compatible strings so check each one\r
+ //\r
+ for (Compatible = NodeType; Compatible < NodeType + Size && *Compatible;\r
+ Compatible += 1 + AsciiStrLen (Compatible)) {\r
+ for (CompatibleProperty = CompatibleProperties; CompatibleProperty->Compatible[0]; CompatibleProperty++) {\r
+ if (AsciiStrCmp (CompatibleProperty->Compatible, Compatible) == 0) {\r
+ return CompatibleProperty->Type;\r
+ }\r
+ }\r
+ }\r
+ return PropertyTypeUnknown;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeVirtFdtDxe (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ VOID *DeviceTreeBase;\r
+ INT32 Node, Prev;\r
+ INT32 RtcNode;\r
+ EFI_STATUS Status;\r
+ CONST CHAR8 *Type;\r
+ INT32 Len;\r
+ PROPERTY_TYPE PropType;\r
+ CONST VOID *RegProp;\r
+ VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;\r
+ EFI_HANDLE Handle;\r
+ UINT64 RegBase;\r
+ UINT64 DistBase, CpuBase;\r
+ CONST INTERRUPT_PROPERTY *InterruptProp;\r
+ INT32 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum;\r
+ CONST CHAR8 *PsciMethod;\r
+\r
+ DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeBaseAddress);\r
+ ASSERT (DeviceTreeBase != NULL);\r
+\r
+ if (fdt_check_header (DeviceTreeBase) != 0) {\r
+ DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
+\r
+ RtcNode = -1;\r
+ //\r
+ // Now enumerate the nodes and install peripherals that we are interested in,\r
+ // i.e., GIC, RTC and virtio MMIO nodes\r
+ //\r
+ for (Prev = 0;; Prev = Node) {\r
+ Node = fdt_next_node (DeviceTreeBase, Prev, NULL);\r
+ if (Node < 0) {\r
+ break;\r
+ }\r
+\r
+ Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);\r
+ if (Type == NULL) {\r
+ continue;\r
+ }\r
+\r
+ PropType = GetTypeFromNode (Type, Len);\r
+ if (PropType == PropertyTypeUnknown) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Get the 'reg' property of this node. For now, we will assume\r
+ // 8 byte quantities for base and size, respectively.\r
+ // TODO use #cells root properties instead\r
+ //\r
+ RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);\r
+ ASSERT ((RegProp != NULL) || (PropType == PropertyTypeTimer) ||\r
+ (PropType == PropertyTypePsci));\r
+\r
+ switch (PropType) {\r
+ case PropertyTypeVirtio:\r
+ ASSERT (Len == 16);\r
+ //\r
+ // Create a unique device path for this transport on the fly\r
+ //\r
+ RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
+ DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode (\r
+ HARDWARE_DEVICE_PATH,\r
+ HW_VENDOR_DP,\r
+ sizeof (VIRTIO_TRANSPORT_DEVICE_PATH));\r
+ if (DevicePath == NULL) {\r
+ DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));\r
+ break;\r
+ }\r
+\r
+ CopyMem (&DevicePath->Vendor.Guid, &gEfiCallerIdGuid, sizeof (EFI_GUID));\r
+ DevicePath->PhysBase = RegBase;\r
+ SetDevicePathNodeLength (&DevicePath->Vendor,\r
+ sizeof (*DevicePath) - sizeof (DevicePath->End));\r
+ SetDevicePathEndNode (&DevicePath->End);\r
+\r
+ Handle = NULL;\r
+ Status = gBS->InstallProtocolInterface (&Handle,\r
+ &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,\r
+ DevicePath);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "%a: Failed to install the EFI_DEVICE_PATH "\r
+ "protocol on a new handle (Status == %r)\n",\r
+ __FUNCTION__, Status));\r
+ FreePool (DevicePath);\r
+ break;\r
+ }\r
+\r
+ Status = VirtioMmioInstallDevice (RegBase, Handle);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "%a: Failed to install VirtIO transport @ 0x%Lx "\r
+ "on handle %p (Status == %r)\n", __FUNCTION__, RegBase,\r
+ Handle, Status));\r
+\r
+ Status = gBS->UninstallProtocolInterface (Handle,\r
+ &gEfiDevicePathProtocolGuid, DevicePath);\r
+ ASSERT_EFI_ERROR (Status);\r
+ FreePool (DevicePath);\r
+ }\r
+ break;\r
+\r
+ case PropertyTypeGic:\r
+ ASSERT (Len == 32);\r
+\r
+ DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
+ CpuBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);\r
+ ASSERT (DistBase < MAX_UINT32);\r
+ ASSERT (CpuBase < MAX_UINT32);\r
+\r
+ PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);\r
+ PcdSet32 (PcdGicInterruptInterfaceBase, (UINT32)CpuBase);\r
+\r
+ DEBUG ((EFI_D_INFO, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase, CpuBase));\r
+ break;\r
+\r
+ case PropertyTypeRtc:\r
+ ASSERT (Len == 16);\r
+\r
+ RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
+ ASSERT (RegBase < MAX_UINT32);\r
+\r
+ PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);\r
+\r
+ DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));\r
+ RtcNode = Node;\r
+ break;\r
+\r
+ case PropertyTypeTimer:\r
+ //\r
+ // - interrupts : Interrupt list for secure, non-secure, virtual and\r
+ // hypervisor timers, in that order.\r
+ //\r
+ InterruptProp = fdt_getprop (DeviceTreeBase, Node, "interrupts", &Len);\r
+ ASSERT (Len == 48);\r
+\r
+ SecIntrNum = fdt32_to_cpu (InterruptProp[0].Number)\r
+ + (InterruptProp[0].Type ? 16 : 0);\r
+ IntrNum = fdt32_to_cpu (InterruptProp[1].Number)\r
+ + (InterruptProp[1].Type ? 16 : 0);\r
+ VirtIntrNum = fdt32_to_cpu (InterruptProp[2].Number)\r
+ + (InterruptProp[2].Type ? 16 : 0);\r
+ HypIntrNum = fdt32_to_cpu (InterruptProp[3].Number)\r
+ + (InterruptProp[3].Type ? 16 : 0);\r
+\r
+ DEBUG ((EFI_D_INFO, "Found Timer interrupts %d, %d, %d, %d\n",\r
+ SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum));\r
+\r
+ PcdSet32 (PcdArmArchTimerSecIntrNum, SecIntrNum);\r
+ PcdSet32 (PcdArmArchTimerIntrNum, IntrNum);\r
+ PcdSet32 (PcdArmArchTimerVirtIntrNum, VirtIntrNum);\r
+ PcdSet32 (PcdArmArchTimerHypIntrNum, HypIntrNum);\r
+ break;\r
+\r
+ case PropertyTypePsci:\r
+ PsciMethod = fdt_getprop (DeviceTreeBase, Node, "method", &Len);\r
+\r
+ if (PsciMethod && AsciiStrnCmp (PsciMethod, "hvc", 3) == 0) {\r
+ PcdSet32 (PcdArmPsciMethod, 1);\r
+ } else if (PsciMethod && AsciiStrnCmp (PsciMethod, "smc", 3) == 0) {\r
+ PcdSet32 (PcdArmPsciMethod, 2);\r
+ } else {\r
+ DEBUG ((EFI_D_ERROR, "%a: Unknown PSCI method \"%a\"\n", __FUNCTION__,\r
+ PsciMethod));\r
+ }\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // UEFI takes ownership of the RTC hardware, and exposes its functionality\r
+ // through the UEFI Runtime Services GetTime, SetTime, etc. This means we\r
+ // need to disable it in the device tree to prevent the OS from attaching its\r
+ // device driver as well.\r
+ //\r
+ if ((RtcNode != -1) &&\r
+ fdt_setprop_string (DeviceTreeBase, RtcNode, "status",\r
+ "disabled") != 0) {\r
+ DEBUG ((EFI_D_WARN, "Failed to set PL031 status to 'disabled'\n"));\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r