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>
30 #include <Guid/VirtioMmioTransport.h>
34 VENDOR_DEVICE_PATH Vendor
;
36 EFI_DEVICE_PATH_PROTOCOL End
;
37 } VIRTIO_TRANSPORT_DEVICE_PATH
;
56 STATIC CONST PROPERTY CompatibleProperties
[] = {
57 { PropertyTypeGic
, "arm,cortex-a15-gic" },
58 { PropertyTypeRtc
, "arm,pl031" },
59 { PropertyTypeVirtio
, "virtio,mmio" },
60 { PropertyTypeUart
, "arm,pl011" },
61 { PropertyTypeTimer
, "arm,armv7-timer" },
62 { PropertyTypeTimer
, "arm,armv8-timer" },
63 { PropertyTypePsci
, "arm,psci-0.2" },
64 { PropertyTypeFwCfg
, "qemu,fw-cfg-mmio" },
65 { PropertyTypeUnknown
, "" }
77 IN CONST CHAR8
*NodeType
,
81 CONST CHAR8
*Compatible
;
82 CONST PROPERTY
*CompatibleProperty
;
85 // A 'compatible' node may contain a sequence of NULL terminated
86 // compatible strings so check each one
88 for (Compatible
= NodeType
; Compatible
< NodeType
+ Size
&& *Compatible
;
89 Compatible
+= 1 + AsciiStrLen (Compatible
)) {
90 for (CompatibleProperty
= CompatibleProperties
; CompatibleProperty
->Compatible
[0]; CompatibleProperty
++) {
91 if (AsciiStrCmp (CompatibleProperty
->Compatible
, Compatible
) == 0) {
92 return CompatibleProperty
->Type
;
96 return PropertyTypeUnknown
;
101 InitializeVirtFdtDxe (
102 IN EFI_HANDLE ImageHandle
,
103 IN EFI_SYSTEM_TABLE
*SystemTable
106 VOID
*DeviceTreeBase
;
112 PROPERTY_TYPE PropType
;
114 VIRTIO_TRANSPORT_DEVICE_PATH
*DevicePath
;
117 UINT64 DistBase
, CpuBase
;
118 CONST INTERRUPT_PROPERTY
*InterruptProp
;
119 INT32 SecIntrNum
, IntrNum
, VirtIntrNum
, HypIntrNum
;
120 CONST CHAR8
*PsciMethod
;
121 UINT64 FwCfgSelectorAddress
;
122 UINT64 FwCfgSelectorSize
;
123 UINT64 FwCfgDataAddress
;
124 UINT64 FwCfgDataSize
;
126 DeviceTreeBase
= (VOID
*)(UINTN
)PcdGet64 (PcdDeviceTreeBaseAddress
);
127 ASSERT (DeviceTreeBase
!= NULL
);
129 if (fdt_check_header (DeviceTreeBase
) != 0) {
130 DEBUG ((EFI_D_ERROR
, "%a: No DTB found @ 0x%p\n", __FUNCTION__
, DeviceTreeBase
));
131 return EFI_NOT_FOUND
;
134 Status
= gBS
->InstallConfigurationTable (&gFdtTableGuid
, DeviceTreeBase
);
135 ASSERT_EFI_ERROR (Status
);
137 DEBUG ((EFI_D_INFO
, "%a: DTB @ 0x%p\n", __FUNCTION__
, DeviceTreeBase
));
141 // Now enumerate the nodes and install peripherals that we are interested in,
142 // i.e., GIC, RTC and virtio MMIO nodes
144 for (Prev
= 0;; Prev
= Node
) {
145 Node
= fdt_next_node (DeviceTreeBase
, Prev
, NULL
);
150 Type
= fdt_getprop (DeviceTreeBase
, Node
, "compatible", &Len
);
155 PropType
= GetTypeFromNode (Type
, Len
);
156 if (PropType
== PropertyTypeUnknown
) {
161 // Get the 'reg' property of this node. For now, we will assume
162 // 8 byte quantities for base and size, respectively.
163 // TODO use #cells root properties instead
165 RegProp
= fdt_getprop (DeviceTreeBase
, Node
, "reg", &Len
);
166 ASSERT ((RegProp
!= NULL
) || (PropType
== PropertyTypeTimer
) ||
167 (PropType
== PropertyTypePsci
));
170 case PropertyTypeFwCfg
:
171 ASSERT (Len
== 2 * sizeof (UINT64
));
173 FwCfgDataAddress
= fdt64_to_cpu (((UINT64
*)RegProp
)[0]);
175 FwCfgSelectorAddress
= FwCfgDataAddress
+ FwCfgDataSize
;
176 FwCfgSelectorSize
= 2;
179 // The following ASSERT()s express
181 // Address + Size - 1 <= MAX_UINTN
183 // for both registers, that is, that the last byte in each MMIO range is
184 // expressible as a MAX_UINTN. The form below is mathematically
185 // equivalent, and it also prevents any unsigned overflow before the
188 ASSERT (FwCfgSelectorAddress
<= MAX_UINTN
- FwCfgSelectorSize
+ 1);
189 ASSERT (FwCfgDataAddress
<= MAX_UINTN
- FwCfgDataSize
+ 1);
191 PcdSet64 (PcdFwCfgSelectorAddress
, FwCfgSelectorAddress
);
192 PcdSet64 (PcdFwCfgDataAddress
, FwCfgDataAddress
);
194 DEBUG ((EFI_D_INFO
, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress
,
198 case PropertyTypeVirtio
:
201 // Create a unique device path for this transport on the fly
203 RegBase
= fdt64_to_cpu (((UINT64
*)RegProp
)[0]);
204 DevicePath
= (VIRTIO_TRANSPORT_DEVICE_PATH
*)CreateDeviceNode (
205 HARDWARE_DEVICE_PATH
,
207 sizeof (VIRTIO_TRANSPORT_DEVICE_PATH
));
208 if (DevicePath
== NULL
) {
209 DEBUG ((EFI_D_ERROR
, "%a: Out of memory\n", __FUNCTION__
));
213 CopyMem (&DevicePath
->Vendor
.Guid
, &gVirtioMmioTransportGuid
,
215 DevicePath
->PhysBase
= RegBase
;
216 SetDevicePathNodeLength (&DevicePath
->Vendor
,
217 sizeof (*DevicePath
) - sizeof (DevicePath
->End
));
218 SetDevicePathEndNode (&DevicePath
->End
);
221 Status
= gBS
->InstallProtocolInterface (&Handle
,
222 &gEfiDevicePathProtocolGuid
, EFI_NATIVE_INTERFACE
,
224 if (EFI_ERROR (Status
)) {
225 DEBUG ((EFI_D_ERROR
, "%a: Failed to install the EFI_DEVICE_PATH "
226 "protocol on a new handle (Status == %r)\n",
227 __FUNCTION__
, Status
));
228 FreePool (DevicePath
);
232 Status
= VirtioMmioInstallDevice (RegBase
, Handle
);
233 if (EFI_ERROR (Status
)) {
234 DEBUG ((EFI_D_ERROR
, "%a: Failed to install VirtIO transport @ 0x%Lx "
235 "on handle %p (Status == %r)\n", __FUNCTION__
, RegBase
,
238 Status
= gBS
->UninstallProtocolInterface (Handle
,
239 &gEfiDevicePathProtocolGuid
, DevicePath
);
240 ASSERT_EFI_ERROR (Status
);
241 FreePool (DevicePath
);
245 case PropertyTypeGic
:
248 DistBase
= fdt64_to_cpu (((UINT64
*)RegProp
)[0]);
249 CpuBase
= fdt64_to_cpu (((UINT64
*)RegProp
)[2]);
250 ASSERT (DistBase
< MAX_UINT32
);
251 ASSERT (CpuBase
< MAX_UINT32
);
253 PcdSet32 (PcdGicDistributorBase
, (UINT32
)DistBase
);
254 PcdSet32 (PcdGicInterruptInterfaceBase
, (UINT32
)CpuBase
);
256 DEBUG ((EFI_D_INFO
, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase
, CpuBase
));
259 case PropertyTypeRtc
:
262 RegBase
= fdt64_to_cpu (((UINT64
*)RegProp
)[0]);
263 ASSERT (RegBase
< MAX_UINT32
);
265 PcdSet32 (PcdPL031RtcBase
, (UINT32
)RegBase
);
267 DEBUG ((EFI_D_INFO
, "Found PL031 RTC @ 0x%Lx\n", RegBase
));
271 case PropertyTypeTimer
:
273 // - interrupts : Interrupt list for secure, non-secure, virtual and
274 // hypervisor timers, in that order.
276 InterruptProp
= fdt_getprop (DeviceTreeBase
, Node
, "interrupts", &Len
);
279 SecIntrNum
= fdt32_to_cpu (InterruptProp
[0].Number
)
280 + (InterruptProp
[0].Type
? 16 : 0);
281 IntrNum
= fdt32_to_cpu (InterruptProp
[1].Number
)
282 + (InterruptProp
[1].Type
? 16 : 0);
283 VirtIntrNum
= fdt32_to_cpu (InterruptProp
[2].Number
)
284 + (InterruptProp
[2].Type
? 16 : 0);
285 HypIntrNum
= fdt32_to_cpu (InterruptProp
[3].Number
)
286 + (InterruptProp
[3].Type
? 16 : 0);
288 DEBUG ((EFI_D_INFO
, "Found Timer interrupts %d, %d, %d, %d\n",
289 SecIntrNum
, IntrNum
, VirtIntrNum
, HypIntrNum
));
291 PcdSet32 (PcdArmArchTimerSecIntrNum
, SecIntrNum
);
292 PcdSet32 (PcdArmArchTimerIntrNum
, IntrNum
);
293 PcdSet32 (PcdArmArchTimerVirtIntrNum
, VirtIntrNum
);
294 PcdSet32 (PcdArmArchTimerHypIntrNum
, HypIntrNum
);
297 case PropertyTypePsci
:
298 PsciMethod
= fdt_getprop (DeviceTreeBase
, Node
, "method", &Len
);
300 if (PsciMethod
&& AsciiStrnCmp (PsciMethod
, "hvc", 3) == 0) {
301 PcdSet32 (PcdArmPsciMethod
, 1);
302 } else if (PsciMethod
&& AsciiStrnCmp (PsciMethod
, "smc", 3) == 0) {
303 PcdSet32 (PcdArmPsciMethod
, 2);
305 DEBUG ((EFI_D_ERROR
, "%a: Unknown PSCI method \"%a\"\n", __FUNCTION__
,
316 // UEFI takes ownership of the RTC hardware, and exposes its functionality
317 // through the UEFI Runtime Services GetTime, SetTime, etc. This means we
318 // need to disable it in the device tree to prevent the OS from attaching its
319 // device driver as well.
321 if ((RtcNode
!= -1) &&
322 fdt_setprop_string (DeviceTreeBase
, RtcNode
, "status",
324 DEBUG ((EFI_D_WARN
, "Failed to set PL031 status to 'disabled'\n"));