]> git.proxmox.com Git - mirror_edk2.git/blob - ArmVirtPkg/VirtFdtDxe/VirtFdtDxe.c
ArmVirtPkg/VirtFdtDxe: remove handling of fw_cfg DT node
[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 PropertyTypePciHost,
49 PropertyTypeXen,
50 } PROPERTY_TYPE;
51
52 typedef struct {
53 PROPERTY_TYPE Type;
54 CHAR8 Compatible[32];
55 } PROPERTY;
56
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, "" }
64 };
65
66 STATIC
67 PROPERTY_TYPE
68 GetTypeFromNode (
69 IN CONST CHAR8 *NodeType,
70 IN UINTN Size
71 )
72 {
73 CONST CHAR8 *Compatible;
74 CONST PROPERTY *CompatibleProperty;
75
76 //
77 // A 'compatible' node may contain a sequence of NULL terminated
78 // compatible strings so check each one
79 //
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;
85 }
86 }
87 }
88 return PropertyTypeUnknown;
89 }
90
91 //
92 // We expect the "ranges" property of "pci-host-ecam-generic" to consist of
93 // records like this.
94 //
95 #pragma pack (1)
96 typedef struct {
97 UINT32 Type;
98 UINT64 ChildBase;
99 UINT64 CpuBase;
100 UINT64 Size;
101 } DTB_PCI_HOST_RANGE_RECORD;
102 #pragma pack ()
103
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)
111
112 /**
113 Process the device tree node describing the generic PCI host controller.
114
115 param[in] DeviceTreeBase Pointer to the device tree.
116
117 param[in] Node Offset of the device tree node whose "compatible"
118 property is "pci-host-ecam-generic".
119
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.
123
124 @retval EFI_SUCCESS Parsing successful, properties parsed from Node
125 have been stored in dynamic PCDs.
126
127 @retval EFI_PROTOCOL_ERROR Parsing failed. PCDs are left unchanged.
128 **/
129 STATIC
130 EFI_STATUS
131 EFIAPI
132 ProcessPciHost (
133 IN CONST VOID *DeviceTreeBase,
134 IN INT32 Node,
135 IN CONST VOID *RegProp
136 )
137 {
138 UINT64 ConfigBase, ConfigSize;
139 CONST VOID *Prop;
140 INT32 Len;
141 UINT32 BusMin, BusMax;
142 UINT32 RecordIdx;
143 UINT64 IoBase, IoSize, IoTranslation;
144 UINT64 MmioBase, MmioSize, MmioTranslation;
145
146 //
147 // Fetch the ECAM window.
148 //
149 ConfigBase = fdt64_to_cpu (((CONST UINT64 *)RegProp)[0]);
150 ConfigSize = fdt64_to_cpu (((CONST UINT64 *)RegProp)[1]);
151
152 //
153 // Fetch the bus range (note: inclusive).
154 //
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",
158 __FUNCTION__));
159 return EFI_PROTOCOL_ERROR;
160 }
161 BusMin = fdt32_to_cpu (((CONST UINT32 *)Prop)[0]);
162 BusMax = fdt32_to_cpu (((CONST UINT32 *)Prop)[1]);
163
164 //
165 // Sanity check: the config space must accommodate all 4K register bytes of
166 // all 8 functions of all 32 devices of all buses.
167 //
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",
171 __FUNCTION__));
172 return EFI_PROTOCOL_ERROR;
173 }
174
175 //
176 // Iterate over "ranges".
177 //
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;
183 }
184
185 //
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.
189 //
190 IoBase = 0;
191 IoTranslation = 0;
192 MmioBase = 0;
193 MmioTranslation = 0;
194
195 //
196 // IoSize and MmioSize are initialized to zero because the logic below
197 // requires it.
198 //
199 IoSize = 0;
200 MmioSize = 0;
201 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
202 ++RecordIdx) {
203 CONST DTB_PCI_HOST_RANGE_RECORD *Record;
204
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;
211 break;
212
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;
217
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;
222 }
223
224 if (MmioTranslation != 0) {
225 DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "
226 "0x%Lx\n", __FUNCTION__, MmioTranslation));
227 return EFI_UNSUPPORTED;
228 }
229
230 break;
231 }
232 }
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;
237 }
238
239 PcdSet64 (PcdPciExpressBaseAddress, ConfigBase);
240
241 PcdSet32 (PcdPciBusMin, BusMin);
242 PcdSet32 (PcdPciBusMax, BusMax);
243
244 PcdSet64 (PcdPciIoBase, IoBase);
245 PcdSet64 (PcdPciIoSize, IoSize);
246 PcdSet64 (PcdPciIoTranslation, IoTranslation);
247
248 PcdSet32 (PcdPciMmio32Base, (UINT32)MmioBase);
249 PcdSet32 (PcdPciMmio32Size, (UINT32)MmioSize);
250
251 PcdSetBool (PcdPciDisableBusEnumeration, FALSE);
252
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));
257 return EFI_SUCCESS;
258 }
259
260
261 EFI_STATUS
262 EFIAPI
263 InitializeVirtFdtDxe (
264 IN EFI_HANDLE ImageHandle,
265 IN EFI_SYSTEM_TABLE *SystemTable
266 )
267 {
268 VOID *Hob;
269 VOID *DeviceTreeBase;
270 INT32 Node, Prev;
271 INT32 RtcNode;
272 EFI_STATUS Status;
273 CONST CHAR8 *Type;
274 INT32 Len;
275 PROPERTY_TYPE PropType;
276 CONST VOID *RegProp;
277 VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;
278 EFI_HANDLE Handle;
279 UINT64 RegBase;
280 BOOLEAN HavePci;
281
282 Hob = GetFirstGuidHob(&gFdtHobGuid);
283 if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {
284 return EFI_NOT_FOUND;
285 }
286 DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
287
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;
291 }
292
293 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));
294
295 RtcNode = -1;
296 HavePci = FALSE;
297 //
298 // Now enumerate the nodes and install peripherals that we are interested in,
299 // i.e., GIC, RTC and virtio MMIO nodes
300 //
301 for (Prev = 0;; Prev = Node) {
302 Node = fdt_next_node (DeviceTreeBase, Prev, NULL);
303 if (Node < 0) {
304 break;
305 }
306
307 Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);
308 if (Type == NULL) {
309 continue;
310 }
311
312 PropType = GetTypeFromNode (Type, Len);
313 if (PropType == PropertyTypeUnknown) {
314 continue;
315 }
316
317 //
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
321 //
322 RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);
323 ASSERT (RegProp != NULL);
324
325 switch (PropType) {
326 case PropertyTypePciHost:
327 ASSERT (Len == 2 * sizeof (UINT64));
328 Status = ProcessPciHost (DeviceTreeBase, Node, RegProp);
329 ASSERT_EFI_ERROR (Status);
330 HavePci = TRUE;
331 break;
332
333 case PropertyTypeVirtio:
334 ASSERT (Len == 16);
335 //
336 // Create a unique device path for this transport on the fly
337 //
338 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
339 DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode (
340 HARDWARE_DEVICE_PATH,
341 HW_VENDOR_DP,
342 sizeof (VIRTIO_TRANSPORT_DEVICE_PATH));
343 if (DevicePath == NULL) {
344 DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));
345 break;
346 }
347
348 CopyMem (&DevicePath->Vendor.Guid, &gVirtioMmioTransportGuid,
349 sizeof (EFI_GUID));
350 DevicePath->PhysBase = RegBase;
351 SetDevicePathNodeLength (&DevicePath->Vendor,
352 sizeof (*DevicePath) - sizeof (DevicePath->End));
353 SetDevicePathEndNode (&DevicePath->End);
354
355 Handle = NULL;
356 Status = gBS->InstallProtocolInterface (&Handle,
357 &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,
358 DevicePath);
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);
364 break;
365 }
366
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,
371 Handle, Status));
372
373 Status = gBS->UninstallProtocolInterface (Handle,
374 &gEfiDevicePathProtocolGuid, DevicePath);
375 ASSERT_EFI_ERROR (Status);
376 FreePool (DevicePath);
377 }
378 break;
379
380 case PropertyTypeRtc:
381 ASSERT (Len == 16);
382
383 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
384 ASSERT (RegBase < MAX_UINT32);
385
386 PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);
387
388 DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));
389 RtcNode = Node;
390 break;
391
392 case PropertyTypeXen:
393 ASSERT (Len == 16);
394
395 //
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
398 //
399 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
400 Handle = NULL;
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));
405 break;
406 }
407
408 DEBUG ((EFI_D_INFO, "Found Xen node with Grant table @ 0x%Lx\n", RegBase));
409
410 break;
411
412 default:
413 break;
414 }
415 }
416
417 if (!FeaturePcdGet (PcdPureAcpiBoot)) {
418 //
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.
421 //
422 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
423 ASSERT_EFI_ERROR (Status);
424
425 //
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.
430 //
431 if ((RtcNode != -1) &&
432 fdt_setprop_string (DeviceTreeBase, RtcNode, "status",
433 "disabled") != 0) {
434 DEBUG ((EFI_D_WARN, "Failed to set PL031 status to 'disabled'\n"));
435 }
436
437 if (HavePci) {
438 //
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.
443 //
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.
448 //
449 Node = fdt_path_offset (DeviceTreeBase, "/chosen");
450 if (Node < 0) {
451 Node = fdt_add_subnode (DeviceTreeBase, 0, "/chosen");
452 }
453 if (Node < 0 ||
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"));
456 }
457 }
458 }
459 return EFI_SUCCESS;
460 }