]> git.proxmox.com Git - mirror_edk2.git/blob - ArmVirtPkg/VirtFdtDxe/VirtFdtDxe.c
ArmVirtPkg/VirtFdtDxe: remove timer DT node handling
[mirror_edk2.git] / ArmVirtPkg / VirtFdtDxe / VirtFdtDxe.c
1 /** @file
2 * Device tree enumeration DXE driver for ARM Virtual Machines
3 *
4 * Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
5 *
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
10 *
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.
13 *
14 **/
15
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>
28 #include <libfdt.h>
29 #include <Library/XenIoMmioLib.h>
30
31 #include <Guid/Fdt.h>
32 #include <Guid/VirtioMmioTransport.h>
33 #include <Guid/FdtHob.h>
34
35 #pragma pack (1)
36 typedef struct {
37 VENDOR_DEVICE_PATH Vendor;
38 UINT64 PhysBase;
39 EFI_DEVICE_PATH_PROTOCOL End;
40 } VIRTIO_TRANSPORT_DEVICE_PATH;
41 #pragma pack ()
42
43 typedef enum {
44 PropertyTypeUnknown,
45 PropertyTypeRtc,
46 PropertyTypeVirtio,
47 PropertyTypeUart,
48 PropertyTypeFwCfg,
49 PropertyTypePciHost,
50 PropertyTypeXen,
51 } PROPERTY_TYPE;
52
53 typedef struct {
54 PROPERTY_TYPE Type;
55 CHAR8 Compatible[32];
56 } PROPERTY;
57
58 STATIC CONST PROPERTY CompatibleProperties[] = {
59 { PropertyTypeRtc, "arm,pl031" },
60 { PropertyTypeVirtio, "virtio,mmio" },
61 { PropertyTypeUart, "arm,pl011" },
62 { PropertyTypeFwCfg, "qemu,fw-cfg-mmio" },
63 { PropertyTypePciHost, "pci-host-ecam-generic" },
64 { PropertyTypeXen, "xen,xen" },
65 { PropertyTypeUnknown, "" }
66 };
67
68 STATIC
69 PROPERTY_TYPE
70 GetTypeFromNode (
71 IN CONST CHAR8 *NodeType,
72 IN UINTN Size
73 )
74 {
75 CONST CHAR8 *Compatible;
76 CONST PROPERTY *CompatibleProperty;
77
78 //
79 // A 'compatible' node may contain a sequence of NULL terminated
80 // compatible strings so check each one
81 //
82 for (Compatible = NodeType; Compatible < NodeType + Size && *Compatible;
83 Compatible += 1 + AsciiStrLen (Compatible)) {
84 for (CompatibleProperty = CompatibleProperties; CompatibleProperty->Compatible[0]; CompatibleProperty++) {
85 if (AsciiStrCmp (CompatibleProperty->Compatible, Compatible) == 0) {
86 return CompatibleProperty->Type;
87 }
88 }
89 }
90 return PropertyTypeUnknown;
91 }
92
93 //
94 // We expect the "ranges" property of "pci-host-ecam-generic" to consist of
95 // records like this.
96 //
97 #pragma pack (1)
98 typedef struct {
99 UINT32 Type;
100 UINT64 ChildBase;
101 UINT64 CpuBase;
102 UINT64 Size;
103 } DTB_PCI_HOST_RANGE_RECORD;
104 #pragma pack ()
105
106 #define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
107 #define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
108 #define DTB_PCI_HOST_RANGE_ALIASED BIT29
109 #define DTB_PCI_HOST_RANGE_MMIO32 BIT25
110 #define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
111 #define DTB_PCI_HOST_RANGE_IO BIT24
112 #define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
113
114 /**
115 Process the device tree node describing the generic PCI host controller.
116
117 param[in] DeviceTreeBase Pointer to the device tree.
118
119 param[in] Node Offset of the device tree node whose "compatible"
120 property is "pci-host-ecam-generic".
121
122 param[in] RegProp Pointer to the "reg" property of Node. The caller
123 is responsible for ensuring that the size of the
124 property is 4 UINT32 cells.
125
126 @retval EFI_SUCCESS Parsing successful, properties parsed from Node
127 have been stored in dynamic PCDs.
128
129 @retval EFI_PROTOCOL_ERROR Parsing failed. PCDs are left unchanged.
130 **/
131 STATIC
132 EFI_STATUS
133 EFIAPI
134 ProcessPciHost (
135 IN CONST VOID *DeviceTreeBase,
136 IN INT32 Node,
137 IN CONST VOID *RegProp
138 )
139 {
140 UINT64 ConfigBase, ConfigSize;
141 CONST VOID *Prop;
142 INT32 Len;
143 UINT32 BusMin, BusMax;
144 UINT32 RecordIdx;
145 UINT64 IoBase, IoSize, IoTranslation;
146 UINT64 MmioBase, MmioSize, MmioTranslation;
147
148 //
149 // Fetch the ECAM window.
150 //
151 ConfigBase = fdt64_to_cpu (((CONST UINT64 *)RegProp)[0]);
152 ConfigSize = fdt64_to_cpu (((CONST UINT64 *)RegProp)[1]);
153
154 //
155 // Fetch the bus range (note: inclusive).
156 //
157 Prop = fdt_getprop (DeviceTreeBase, Node, "bus-range", &Len);
158 if (Prop == NULL || Len != 2 * sizeof(UINT32)) {
159 DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",
160 __FUNCTION__));
161 return EFI_PROTOCOL_ERROR;
162 }
163 BusMin = fdt32_to_cpu (((CONST UINT32 *)Prop)[0]);
164 BusMax = fdt32_to_cpu (((CONST UINT32 *)Prop)[1]);
165
166 //
167 // Sanity check: the config space must accommodate all 4K register bytes of
168 // all 8 functions of all 32 devices of all buses.
169 //
170 if (BusMax < BusMin || BusMax - BusMin == MAX_UINT32 ||
171 DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < BusMax - BusMin + 1) {
172 DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",
173 __FUNCTION__));
174 return EFI_PROTOCOL_ERROR;
175 }
176
177 //
178 // Iterate over "ranges".
179 //
180 Prop = fdt_getprop (DeviceTreeBase, Node, "ranges", &Len);
181 if (Prop == NULL || Len == 0 ||
182 Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {
183 DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));
184 return EFI_PROTOCOL_ERROR;
185 }
186
187 //
188 // IoBase, IoTranslation, MmioBase and MmioTranslation are initialized only
189 // in order to suppress '-Werror=maybe-uninitialized' warnings *incorrectly*
190 // emitted by some gcc versions.
191 //
192 IoBase = 0;
193 IoTranslation = 0;
194 MmioBase = 0;
195 MmioTranslation = 0;
196
197 //
198 // IoSize and MmioSize are initialized to zero because the logic below
199 // requires it.
200 //
201 IoSize = 0;
202 MmioSize = 0;
203 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
204 ++RecordIdx) {
205 CONST DTB_PCI_HOST_RANGE_RECORD *Record;
206
207 Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
208 switch (fdt32_to_cpu (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {
209 case DTB_PCI_HOST_RANGE_IO:
210 IoBase = fdt64_to_cpu (Record->ChildBase);
211 IoSize = fdt64_to_cpu (Record->Size);
212 IoTranslation = fdt64_to_cpu (Record->CpuBase) - IoBase;
213 break;
214
215 case DTB_PCI_HOST_RANGE_MMIO32:
216 MmioBase = fdt64_to_cpu (Record->ChildBase);
217 MmioSize = fdt64_to_cpu (Record->Size);
218 MmioTranslation = fdt64_to_cpu (Record->CpuBase) - MmioBase;
219
220 if (MmioBase > MAX_UINT32 || MmioSize > MAX_UINT32 ||
221 MmioBase + MmioSize > SIZE_4GB) {
222 DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));
223 return EFI_PROTOCOL_ERROR;
224 }
225
226 if (MmioTranslation != 0) {
227 DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "
228 "0x%Lx\n", __FUNCTION__, MmioTranslation));
229 return EFI_UNSUPPORTED;
230 }
231
232 break;
233 }
234 }
235 if (IoSize == 0 || MmioSize == 0) {
236 DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,
237 (IoSize == 0) ? "IO" : "MMIO32"));
238 return EFI_PROTOCOL_ERROR;
239 }
240
241 PcdSet64 (PcdPciExpressBaseAddress, ConfigBase);
242
243 PcdSet32 (PcdPciBusMin, BusMin);
244 PcdSet32 (PcdPciBusMax, BusMax);
245
246 PcdSet64 (PcdPciIoBase, IoBase);
247 PcdSet64 (PcdPciIoSize, IoSize);
248 PcdSet64 (PcdPciIoTranslation, IoTranslation);
249
250 PcdSet32 (PcdPciMmio32Base, (UINT32)MmioBase);
251 PcdSet32 (PcdPciMmio32Size, (UINT32)MmioSize);
252
253 PcdSetBool (PcdPciDisableBusEnumeration, FALSE);
254
255 DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
256 "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x%Lx\n", __FUNCTION__, ConfigBase,
257 ConfigSize, BusMin, BusMax, IoBase, IoSize, IoTranslation, MmioBase,
258 MmioSize, MmioTranslation));
259 return EFI_SUCCESS;
260 }
261
262
263 EFI_STATUS
264 EFIAPI
265 InitializeVirtFdtDxe (
266 IN EFI_HANDLE ImageHandle,
267 IN EFI_SYSTEM_TABLE *SystemTable
268 )
269 {
270 VOID *Hob;
271 VOID *DeviceTreeBase;
272 INT32 Node, Prev;
273 INT32 RtcNode;
274 EFI_STATUS Status;
275 CONST CHAR8 *Type;
276 INT32 Len;
277 PROPERTY_TYPE PropType;
278 CONST VOID *RegProp;
279 VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;
280 EFI_HANDLE Handle;
281 UINT64 RegBase;
282 UINT64 FwCfgSelectorAddress;
283 UINT64 FwCfgSelectorSize;
284 UINT64 FwCfgDataAddress;
285 UINT64 FwCfgDataSize;
286 UINT64 FwCfgDmaAddress;
287 UINT64 FwCfgDmaSize;
288 BOOLEAN HavePci;
289
290 Hob = GetFirstGuidHob(&gFdtHobGuid);
291 if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {
292 return EFI_NOT_FOUND;
293 }
294 DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
295
296 if (fdt_check_header (DeviceTreeBase) != 0) {
297 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__, DeviceTreeBase));
298 return EFI_NOT_FOUND;
299 }
300
301 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));
302
303 RtcNode = -1;
304 HavePci = FALSE;
305 //
306 // Now enumerate the nodes and install peripherals that we are interested in,
307 // i.e., GIC, RTC and virtio MMIO nodes
308 //
309 for (Prev = 0;; Prev = Node) {
310 Node = fdt_next_node (DeviceTreeBase, Prev, NULL);
311 if (Node < 0) {
312 break;
313 }
314
315 Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);
316 if (Type == NULL) {
317 continue;
318 }
319
320 PropType = GetTypeFromNode (Type, Len);
321 if (PropType == PropertyTypeUnknown) {
322 continue;
323 }
324
325 //
326 // Get the 'reg' property of this node. For now, we will assume
327 // 8 byte quantities for base and size, respectively.
328 // TODO use #cells root properties instead
329 //
330 RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);
331 ASSERT (RegProp != NULL);
332
333 switch (PropType) {
334 case PropertyTypePciHost:
335 ASSERT (Len == 2 * sizeof (UINT64));
336 Status = ProcessPciHost (DeviceTreeBase, Node, RegProp);
337 ASSERT_EFI_ERROR (Status);
338 HavePci = TRUE;
339 break;
340
341 case PropertyTypeFwCfg:
342 ASSERT (Len == 2 * sizeof (UINT64));
343
344 FwCfgDataAddress = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
345 FwCfgDataSize = 8;
346 FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;
347 FwCfgSelectorSize = 2;
348
349 //
350 // The following ASSERT()s express
351 //
352 // Address + Size - 1 <= MAX_UINTN
353 //
354 // for both registers, that is, that the last byte in each MMIO range is
355 // expressible as a MAX_UINTN. The form below is mathematically
356 // equivalent, and it also prevents any unsigned overflow before the
357 // comparison.
358 //
359 ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);
360 ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);
361
362 PcdSet64 (PcdFwCfgSelectorAddress, FwCfgSelectorAddress);
363 PcdSet64 (PcdFwCfgDataAddress, FwCfgDataAddress);
364
365 DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,
366 FwCfgDataAddress));
367
368 if (fdt64_to_cpu (((UINT64 *)RegProp)[1]) >= 0x18) {
369 FwCfgDmaAddress = FwCfgDataAddress + 0x10;
370 FwCfgDmaSize = 0x08;
371
372 //
373 // See explanation above.
374 //
375 ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);
376
377 PcdSet64 (PcdFwCfgDmaAddress, FwCfgDmaAddress);
378 DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));
379 }
380 break;
381
382 case PropertyTypeVirtio:
383 ASSERT (Len == 16);
384 //
385 // Create a unique device path for this transport on the fly
386 //
387 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
388 DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode (
389 HARDWARE_DEVICE_PATH,
390 HW_VENDOR_DP,
391 sizeof (VIRTIO_TRANSPORT_DEVICE_PATH));
392 if (DevicePath == NULL) {
393 DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));
394 break;
395 }
396
397 CopyMem (&DevicePath->Vendor.Guid, &gVirtioMmioTransportGuid,
398 sizeof (EFI_GUID));
399 DevicePath->PhysBase = RegBase;
400 SetDevicePathNodeLength (&DevicePath->Vendor,
401 sizeof (*DevicePath) - sizeof (DevicePath->End));
402 SetDevicePathEndNode (&DevicePath->End);
403
404 Handle = NULL;
405 Status = gBS->InstallProtocolInterface (&Handle,
406 &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,
407 DevicePath);
408 if (EFI_ERROR (Status)) {
409 DEBUG ((EFI_D_ERROR, "%a: Failed to install the EFI_DEVICE_PATH "
410 "protocol on a new handle (Status == %r)\n",
411 __FUNCTION__, Status));
412 FreePool (DevicePath);
413 break;
414 }
415
416 Status = VirtioMmioInstallDevice (RegBase, Handle);
417 if (EFI_ERROR (Status)) {
418 DEBUG ((EFI_D_ERROR, "%a: Failed to install VirtIO transport @ 0x%Lx "
419 "on handle %p (Status == %r)\n", __FUNCTION__, RegBase,
420 Handle, Status));
421
422 Status = gBS->UninstallProtocolInterface (Handle,
423 &gEfiDevicePathProtocolGuid, DevicePath);
424 ASSERT_EFI_ERROR (Status);
425 FreePool (DevicePath);
426 }
427 break;
428
429 case PropertyTypeRtc:
430 ASSERT (Len == 16);
431
432 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
433 ASSERT (RegBase < MAX_UINT32);
434
435 PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);
436
437 DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));
438 RtcNode = Node;
439 break;
440
441 case PropertyTypeXen:
442 ASSERT (Len == 16);
443
444 //
445 // Retrieve the reg base from this node and wire it up to the
446 // MMIO flavor of the XenBus root device I/O protocol
447 //
448 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
449 Handle = NULL;
450 Status = XenIoMmioInstall (&Handle, RegBase);
451 if (EFI_ERROR (Status)) {
452 DEBUG ((EFI_D_ERROR, "%a: XenIoMmioInstall () failed on a new handle "
453 "(Status == %r)\n", __FUNCTION__, Status));
454 break;
455 }
456
457 DEBUG ((EFI_D_INFO, "Found Xen node with Grant table @ 0x%Lx\n", RegBase));
458
459 break;
460
461 default:
462 break;
463 }
464 }
465
466 if (!FeaturePcdGet (PcdPureAcpiBoot)) {
467 //
468 // Only install the FDT as a configuration table if we want to leave it up
469 // to the OS to decide whether it prefers ACPI over DT.
470 //
471 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
472 ASSERT_EFI_ERROR (Status);
473
474 //
475 // UEFI takes ownership of the RTC hardware, and exposes its functionality
476 // through the UEFI Runtime Services GetTime, SetTime, etc. This means we
477 // need to disable it in the device tree to prevent the OS from attaching its
478 // device driver as well.
479 //
480 if ((RtcNode != -1) &&
481 fdt_setprop_string (DeviceTreeBase, RtcNode, "status",
482 "disabled") != 0) {
483 DEBUG ((EFI_D_WARN, "Failed to set PL031 status to 'disabled'\n"));
484 }
485
486 if (HavePci) {
487 //
488 // Set the /chosen/linux,pci-probe-only property to 1, so that the PCI
489 // setup we will perform in the firmware is honored by the Linux OS,
490 // rather than torn down and done from scratch. This is generally a more
491 // sensible approach, and aligns with what ACPI based OSes do in general.
492 //
493 // In case we are exposing an emulated VGA PCI device to the guest, which
494 // may subsequently get exposed via the Graphics Output protocol and
495 // driven as an efifb by Linux, we need this setting to prevent the
496 // framebuffer from becoming unresponsive.
497 //
498 Node = fdt_path_offset (DeviceTreeBase, "/chosen");
499 if (Node < 0) {
500 Node = fdt_add_subnode (DeviceTreeBase, 0, "/chosen");
501 }
502 if (Node < 0 ||
503 fdt_setprop_u32 (DeviceTreeBase, Node, "linux,pci-probe-only", 1) < 0) {
504 DEBUG ((EFI_D_WARN, "Failed to set /chosen/linux,pci-probe-only property\n"));
505 }
506 }
507 }
508 return EFI_SUCCESS;
509 }