2 * Device tree enumeration DXE driver for ARM Virtual Machines
4 * Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
6 * This program and the accompanying materials are
7 * licensed and made available under the terms and conditions of the BSD License
8 * which accompanies this distribution. The full text of the license may be found at
9 * http://opensource.org/licenses/bsd-license.php
11 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/UefiLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/UefiDriverEntryPoint.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/VirtioMmioDeviceLib.h>
24 #include <Library/DevicePathLib.h>
25 #include <Library/PcdLib.h>
26 #include <Library/DxeServicesLib.h>
27 #include <Library/HobLib.h>
29 #include <Library/XenIoMmioLib.h>
32 #include <Guid/VirtioMmioTransport.h>
33 #include <Guid/FdtHob.h>
37 VENDOR_DEVICE_PATH Vendor
;
39 EFI_DEVICE_PATH_PROTOCOL End
;
40 } VIRTIO_TRANSPORT_DEVICE_PATH
;
57 STATIC CONST PROPERTY CompatibleProperties
[] = {
58 { PropertyTypeRtc
, "arm,pl031" },
59 { PropertyTypeVirtio
, "virtio,mmio" },
60 { PropertyTypeUart
, "arm,pl011" },
61 { PropertyTypePciHost
, "pci-host-ecam-generic" },
62 { PropertyTypeXen
, "xen,xen" },
63 { PropertyTypeUnknown
, "" }
69 IN CONST CHAR8
*NodeType
,
73 CONST CHAR8
*Compatible
;
74 CONST PROPERTY
*CompatibleProperty
;
77 // A 'compatible' node may contain a sequence of NULL terminated
78 // compatible strings so check each one
80 for (Compatible
= NodeType
; Compatible
< NodeType
+ Size
&& *Compatible
;
81 Compatible
+= 1 + AsciiStrLen (Compatible
)) {
82 for (CompatibleProperty
= CompatibleProperties
; CompatibleProperty
->Compatible
[0]; CompatibleProperty
++) {
83 if (AsciiStrCmp (CompatibleProperty
->Compatible
, Compatible
) == 0) {
84 return CompatibleProperty
->Type
;
88 return PropertyTypeUnknown
;
92 // We expect the "ranges" property of "pci-host-ecam-generic" to consist of
101 } DTB_PCI_HOST_RANGE_RECORD
;
104 #define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
105 #define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
106 #define DTB_PCI_HOST_RANGE_ALIASED BIT29
107 #define DTB_PCI_HOST_RANGE_MMIO32 BIT25
108 #define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
109 #define DTB_PCI_HOST_RANGE_IO BIT24
110 #define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
113 Process the device tree node describing the generic PCI host controller.
115 param[in] DeviceTreeBase Pointer to the device tree.
117 param[in] Node Offset of the device tree node whose "compatible"
118 property is "pci-host-ecam-generic".
120 param[in] RegProp Pointer to the "reg" property of Node. The caller
121 is responsible for ensuring that the size of the
122 property is 4 UINT32 cells.
124 @retval EFI_SUCCESS Parsing successful, properties parsed from Node
125 have been stored in dynamic PCDs.
127 @retval EFI_PROTOCOL_ERROR Parsing failed. PCDs are left unchanged.
133 IN CONST VOID
*DeviceTreeBase
,
135 IN CONST VOID
*RegProp
138 UINT64 ConfigBase
, ConfigSize
;
141 UINT32 BusMin
, BusMax
;
143 UINT64 IoBase
, IoSize
, IoTranslation
;
144 UINT64 MmioBase
, MmioSize
, MmioTranslation
;
147 // Fetch the ECAM window.
149 ConfigBase
= fdt64_to_cpu (((CONST UINT64
*)RegProp
)[0]);
150 ConfigSize
= fdt64_to_cpu (((CONST UINT64
*)RegProp
)[1]);
153 // Fetch the bus range (note: inclusive).
155 Prop
= fdt_getprop (DeviceTreeBase
, Node
, "bus-range", &Len
);
156 if (Prop
== NULL
|| Len
!= 2 * sizeof(UINT32
)) {
157 DEBUG ((EFI_D_ERROR
, "%a: 'bus-range' not found or invalid\n",
159 return EFI_PROTOCOL_ERROR
;
161 BusMin
= fdt32_to_cpu (((CONST UINT32
*)Prop
)[0]);
162 BusMax
= fdt32_to_cpu (((CONST UINT32
*)Prop
)[1]);
165 // Sanity check: the config space must accommodate all 4K register bytes of
166 // all 8 functions of all 32 devices of all buses.
168 if (BusMax
< BusMin
|| BusMax
- BusMin
== MAX_UINT32
||
169 DivU64x32 (ConfigSize
, SIZE_4KB
* 8 * 32) < BusMax
- BusMin
+ 1) {
170 DEBUG ((EFI_D_ERROR
, "%a: invalid 'bus-range' and/or 'reg'\n",
172 return EFI_PROTOCOL_ERROR
;
176 // Iterate over "ranges".
178 Prop
= fdt_getprop (DeviceTreeBase
, Node
, "ranges", &Len
);
179 if (Prop
== NULL
|| Len
== 0 ||
180 Len
% sizeof (DTB_PCI_HOST_RANGE_RECORD
) != 0) {
181 DEBUG ((EFI_D_ERROR
, "%a: 'ranges' not found or invalid\n", __FUNCTION__
));
182 return EFI_PROTOCOL_ERROR
;
186 // IoBase, IoTranslation, MmioBase and MmioTranslation are initialized only
187 // in order to suppress '-Werror=maybe-uninitialized' warnings *incorrectly*
188 // emitted by some gcc versions.
196 // IoSize and MmioSize are initialized to zero because the logic below
201 for (RecordIdx
= 0; RecordIdx
< Len
/ sizeof (DTB_PCI_HOST_RANGE_RECORD
);
203 CONST DTB_PCI_HOST_RANGE_RECORD
*Record
;
205 Record
= (CONST DTB_PCI_HOST_RANGE_RECORD
*)Prop
+ RecordIdx
;
206 switch (fdt32_to_cpu (Record
->Type
) & DTB_PCI_HOST_RANGE_TYPEMASK
) {
207 case DTB_PCI_HOST_RANGE_IO
:
208 IoBase
= fdt64_to_cpu (Record
->ChildBase
);
209 IoSize
= fdt64_to_cpu (Record
->Size
);
210 IoTranslation
= fdt64_to_cpu (Record
->CpuBase
) - IoBase
;
213 case DTB_PCI_HOST_RANGE_MMIO32
:
214 MmioBase
= fdt64_to_cpu (Record
->ChildBase
);
215 MmioSize
= fdt64_to_cpu (Record
->Size
);
216 MmioTranslation
= fdt64_to_cpu (Record
->CpuBase
) - MmioBase
;
218 if (MmioBase
> MAX_UINT32
|| MmioSize
> MAX_UINT32
||
219 MmioBase
+ MmioSize
> SIZE_4GB
) {
220 DEBUG ((EFI_D_ERROR
, "%a: MMIO32 space invalid\n", __FUNCTION__
));
221 return EFI_PROTOCOL_ERROR
;
224 if (MmioTranslation
!= 0) {
225 DEBUG ((EFI_D_ERROR
, "%a: unsupported nonzero MMIO32 translation "
226 "0x%Lx\n", __FUNCTION__
, MmioTranslation
));
227 return EFI_UNSUPPORTED
;
233 if (IoSize
== 0 || MmioSize
== 0) {
234 DEBUG ((EFI_D_ERROR
, "%a: %a space empty\n", __FUNCTION__
,
235 (IoSize
== 0) ? "IO" : "MMIO32"));
236 return EFI_PROTOCOL_ERROR
;
239 PcdSet64 (PcdPciExpressBaseAddress
, ConfigBase
);
241 PcdSet32 (PcdPciBusMin
, BusMin
);
242 PcdSet32 (PcdPciBusMax
, BusMax
);
244 PcdSet64 (PcdPciIoBase
, IoBase
);
245 PcdSet64 (PcdPciIoSize
, IoSize
);
246 PcdSet64 (PcdPciIoTranslation
, IoTranslation
);
248 PcdSet32 (PcdPciMmio32Base
, (UINT32
)MmioBase
);
249 PcdSet32 (PcdPciMmio32Size
, (UINT32
)MmioSize
);
251 PcdSetBool (PcdPciDisableBusEnumeration
, FALSE
);
253 DEBUG ((EFI_D_INFO
, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
254 "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x%Lx\n", __FUNCTION__
, ConfigBase
,
255 ConfigSize
, BusMin
, BusMax
, IoBase
, IoSize
, IoTranslation
, MmioBase
,
256 MmioSize
, MmioTranslation
));
263 InitializeVirtFdtDxe (
264 IN EFI_HANDLE ImageHandle
,
265 IN EFI_SYSTEM_TABLE
*SystemTable
269 VOID
*DeviceTreeBase
;
275 PROPERTY_TYPE PropType
;
277 VIRTIO_TRANSPORT_DEVICE_PATH
*DevicePath
;
282 Hob
= GetFirstGuidHob(&gFdtHobGuid
);
283 if (Hob
== NULL
|| GET_GUID_HOB_DATA_SIZE (Hob
) != sizeof (UINT64
)) {
284 return EFI_NOT_FOUND
;
286 DeviceTreeBase
= (VOID
*)(UINTN
)*(UINT64
*)GET_GUID_HOB_DATA (Hob
);
288 if (fdt_check_header (DeviceTreeBase
) != 0) {
289 DEBUG ((EFI_D_ERROR
, "%a: No DTB found @ 0x%p\n", __FUNCTION__
, DeviceTreeBase
));
290 return EFI_NOT_FOUND
;
293 DEBUG ((EFI_D_INFO
, "%a: DTB @ 0x%p\n", __FUNCTION__
, DeviceTreeBase
));
298 // Now enumerate the nodes and install peripherals that we are interested in,
299 // i.e., GIC, RTC and virtio MMIO nodes
301 for (Prev
= 0;; Prev
= Node
) {
302 Node
= fdt_next_node (DeviceTreeBase
, Prev
, NULL
);
307 Type
= fdt_getprop (DeviceTreeBase
, Node
, "compatible", &Len
);
312 PropType
= GetTypeFromNode (Type
, Len
);
313 if (PropType
== PropertyTypeUnknown
) {
318 // Get the 'reg' property of this node. For now, we will assume
319 // 8 byte quantities for base and size, respectively.
320 // TODO use #cells root properties instead
322 RegProp
= fdt_getprop (DeviceTreeBase
, Node
, "reg", &Len
);
323 ASSERT (RegProp
!= NULL
);
326 case PropertyTypePciHost
:
327 ASSERT (Len
== 2 * sizeof (UINT64
));
328 Status
= ProcessPciHost (DeviceTreeBase
, Node
, RegProp
);
329 ASSERT_EFI_ERROR (Status
);
333 case PropertyTypeVirtio
:
336 // Create a unique device path for this transport on the fly
338 RegBase
= fdt64_to_cpu (((UINT64
*)RegProp
)[0]);
339 DevicePath
= (VIRTIO_TRANSPORT_DEVICE_PATH
*)CreateDeviceNode (
340 HARDWARE_DEVICE_PATH
,
342 sizeof (VIRTIO_TRANSPORT_DEVICE_PATH
));
343 if (DevicePath
== NULL
) {
344 DEBUG ((EFI_D_ERROR
, "%a: Out of memory\n", __FUNCTION__
));
348 CopyMem (&DevicePath
->Vendor
.Guid
, &gVirtioMmioTransportGuid
,
350 DevicePath
->PhysBase
= RegBase
;
351 SetDevicePathNodeLength (&DevicePath
->Vendor
,
352 sizeof (*DevicePath
) - sizeof (DevicePath
->End
));
353 SetDevicePathEndNode (&DevicePath
->End
);
356 Status
= gBS
->InstallProtocolInterface (&Handle
,
357 &gEfiDevicePathProtocolGuid
, EFI_NATIVE_INTERFACE
,
359 if (EFI_ERROR (Status
)) {
360 DEBUG ((EFI_D_ERROR
, "%a: Failed to install the EFI_DEVICE_PATH "
361 "protocol on a new handle (Status == %r)\n",
362 __FUNCTION__
, Status
));
363 FreePool (DevicePath
);
367 Status
= VirtioMmioInstallDevice (RegBase
, Handle
);
368 if (EFI_ERROR (Status
)) {
369 DEBUG ((EFI_D_ERROR
, "%a: Failed to install VirtIO transport @ 0x%Lx "
370 "on handle %p (Status == %r)\n", __FUNCTION__
, RegBase
,
373 Status
= gBS
->UninstallProtocolInterface (Handle
,
374 &gEfiDevicePathProtocolGuid
, DevicePath
);
375 ASSERT_EFI_ERROR (Status
);
376 FreePool (DevicePath
);
380 case PropertyTypeRtc
:
383 RegBase
= fdt64_to_cpu (((UINT64
*)RegProp
)[0]);
384 ASSERT (RegBase
< MAX_UINT32
);
386 PcdSet32 (PcdPL031RtcBase
, (UINT32
)RegBase
);
388 DEBUG ((EFI_D_INFO
, "Found PL031 RTC @ 0x%Lx\n", RegBase
));
392 case PropertyTypeXen
:
396 // Retrieve the reg base from this node and wire it up to the
397 // MMIO flavor of the XenBus root device I/O protocol
399 RegBase
= fdt64_to_cpu (((UINT64
*)RegProp
)[0]);
401 Status
= XenIoMmioInstall (&Handle
, RegBase
);
402 if (EFI_ERROR (Status
)) {
403 DEBUG ((EFI_D_ERROR
, "%a: XenIoMmioInstall () failed on a new handle "
404 "(Status == %r)\n", __FUNCTION__
, Status
));
408 DEBUG ((EFI_D_INFO
, "Found Xen node with Grant table @ 0x%Lx\n", RegBase
));
417 if (!FeaturePcdGet (PcdPureAcpiBoot
)) {
419 // Only install the FDT as a configuration table if we want to leave it up
420 // to the OS to decide whether it prefers ACPI over DT.
422 Status
= gBS
->InstallConfigurationTable (&gFdtTableGuid
, DeviceTreeBase
);
423 ASSERT_EFI_ERROR (Status
);
426 // UEFI takes ownership of the RTC hardware, and exposes its functionality
427 // through the UEFI Runtime Services GetTime, SetTime, etc. This means we
428 // need to disable it in the device tree to prevent the OS from attaching its
429 // device driver as well.
431 if ((RtcNode
!= -1) &&
432 fdt_setprop_string (DeviceTreeBase
, RtcNode
, "status",
434 DEBUG ((EFI_D_WARN
, "Failed to set PL031 status to 'disabled'\n"));
439 // Set the /chosen/linux,pci-probe-only property to 1, so that the PCI
440 // setup we will perform in the firmware is honored by the Linux OS,
441 // rather than torn down and done from scratch. This is generally a more
442 // sensible approach, and aligns with what ACPI based OSes do in general.
444 // In case we are exposing an emulated VGA PCI device to the guest, which
445 // may subsequently get exposed via the Graphics Output protocol and
446 // driven as an efifb by Linux, we need this setting to prevent the
447 // framebuffer from becoming unresponsive.
449 Node
= fdt_path_offset (DeviceTreeBase
, "/chosen");
451 Node
= fdt_add_subnode (DeviceTreeBase
, 0, "/chosen");
454 fdt_setprop_u32 (DeviceTreeBase
, Node
, "linux,pci-probe-only", 1) < 0) {
455 DEBUG ((EFI_D_WARN
, "Failed to set /chosen/linux,pci-probe-only property\n"));