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