]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c
OvmfPkg: introduce VIRTIO_MMIO_TRANSPORT_GUID
[mirror_edk2.git] / ArmPlatformPkg / ArmVirtualizationPkg / 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
27#include <libfdt.h>\r
28\r
29#include <Guid/Fdt.h>\r
30\r
31#pragma pack (1)\r
32typedef struct {\r
33 VENDOR_DEVICE_PATH Vendor;\r
34 UINT64 PhysBase;\r
35 EFI_DEVICE_PATH_PROTOCOL End;\r
36} VIRTIO_TRANSPORT_DEVICE_PATH;\r
37#pragma pack ()\r
38\r
39typedef enum {\r
40 PropertyTypeUnknown,\r
41 PropertyTypeGic,\r
42 PropertyTypeRtc,\r
43 PropertyTypeVirtio,\r
44 PropertyTypeUart,\r
45 PropertyTypeTimer,\r
46 PropertyTypePsci,\r
ad652d46 47 PropertyTypeFwCfg,\r
ad106932
AB
48} PROPERTY_TYPE;\r
49\r
50typedef struct {\r
51 PROPERTY_TYPE Type;\r
52 CHAR8 Compatible[20];\r
53} PROPERTY;\r
54\r
55STATIC CONST PROPERTY CompatibleProperties[] = {\r
56 { PropertyTypeGic, "arm,cortex-a15-gic" },\r
57 { PropertyTypeRtc, "arm,pl031" },\r
58 { PropertyTypeVirtio, "virtio,mmio" },\r
59 { PropertyTypeUart, "arm,pl011" },\r
60 { PropertyTypeTimer, "arm,armv7-timer" },\r
61 { PropertyTypeTimer, "arm,armv8-timer" },\r
62 { PropertyTypePsci, "arm,psci-0.2" },\r
ad652d46 63 { PropertyTypeFwCfg, "qemu,fw-cfg-mmio" },\r
ad106932
AB
64 { PropertyTypeUnknown, "" }\r
65};\r
66\r
67typedef struct {\r
68 UINT32 Type;\r
69 UINT32 Number;\r
70 UINT32 Flags;\r
71} INTERRUPT_PROPERTY;\r
72\r
73STATIC\r
74PROPERTY_TYPE\r
75GetTypeFromNode (\r
76 IN CONST CHAR8 *NodeType,\r
77 IN UINTN Size\r
78 )\r
79{\r
80 CONST CHAR8 *Compatible;\r
81 CONST PROPERTY *CompatibleProperty;\r
82\r
83 //\r
84 // A 'compatible' node may contain a sequence of NULL terminated\r
85 // compatible strings so check each one\r
86 //\r
87 for (Compatible = NodeType; Compatible < NodeType + Size && *Compatible;\r
88 Compatible += 1 + AsciiStrLen (Compatible)) {\r
89 for (CompatibleProperty = CompatibleProperties; CompatibleProperty->Compatible[0]; CompatibleProperty++) {\r
90 if (AsciiStrCmp (CompatibleProperty->Compatible, Compatible) == 0) {\r
91 return CompatibleProperty->Type;\r
92 }\r
93 }\r
94 }\r
95 return PropertyTypeUnknown;\r
96}\r
97\r
98EFI_STATUS\r
99EFIAPI\r
100InitializeVirtFdtDxe (\r
101 IN EFI_HANDLE ImageHandle,\r
102 IN EFI_SYSTEM_TABLE *SystemTable\r
103 )\r
104{\r
105 VOID *DeviceTreeBase;\r
106 INT32 Node, Prev;\r
107 INT32 RtcNode;\r
108 EFI_STATUS Status;\r
109 CONST CHAR8 *Type;\r
110 INT32 Len;\r
111 PROPERTY_TYPE PropType;\r
112 CONST VOID *RegProp;\r
113 VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;\r
114 EFI_HANDLE Handle;\r
115 UINT64 RegBase;\r
116 UINT64 DistBase, CpuBase;\r
117 CONST INTERRUPT_PROPERTY *InterruptProp;\r
118 INT32 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum;\r
119 CONST CHAR8 *PsciMethod;\r
ad652d46
LE
120 UINT64 FwCfgSelectorAddress;\r
121 UINT64 FwCfgSelectorSize;\r
122 UINT64 FwCfgDataAddress;\r
123 UINT64 FwCfgDataSize;\r
ad106932
AB
124\r
125 DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeBaseAddress);\r
126 ASSERT (DeviceTreeBase != NULL);\r
127\r
128 if (fdt_check_header (DeviceTreeBase) != 0) {\r
129 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
130 return EFI_NOT_FOUND;\r
131 }\r
132\r
133 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);\r
134 ASSERT_EFI_ERROR (Status);\r
135\r
136 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
137\r
138 RtcNode = -1;\r
139 //\r
140 // Now enumerate the nodes and install peripherals that we are interested in,\r
141 // i.e., GIC, RTC and virtio MMIO nodes\r
142 //\r
143 for (Prev = 0;; Prev = Node) {\r
144 Node = fdt_next_node (DeviceTreeBase, Prev, NULL);\r
145 if (Node < 0) {\r
146 break;\r
147 }\r
148\r
149 Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);\r
150 if (Type == NULL) {\r
151 continue;\r
152 }\r
153\r
154 PropType = GetTypeFromNode (Type, Len);\r
155 if (PropType == PropertyTypeUnknown) {\r
156 continue;\r
157 }\r
158\r
159 //\r
160 // Get the 'reg' property of this node. For now, we will assume\r
161 // 8 byte quantities for base and size, respectively.\r
162 // TODO use #cells root properties instead\r
163 //\r
164 RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);\r
165 ASSERT ((RegProp != NULL) || (PropType == PropertyTypeTimer) ||\r
166 (PropType == PropertyTypePsci));\r
167\r
168 switch (PropType) {\r
ad652d46
LE
169 case PropertyTypeFwCfg:\r
170 ASSERT (Len == 2 * sizeof (UINT64));\r
171\r
172 FwCfgDataAddress = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
173 FwCfgDataSize = 8;\r
174 FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;\r
175 FwCfgSelectorSize = 2;\r
176\r
177 //\r
178 // The following ASSERT()s express\r
179 //\r
180 // Address + Size - 1 <= MAX_UINTN\r
181 //\r
182 // for both registers, that is, that the last byte in each MMIO range is\r
183 // expressible as a MAX_UINTN. The form below is mathematically\r
184 // equivalent, and it also prevents any unsigned overflow before the\r
185 // comparison.\r
186 //\r
187 ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);\r
188 ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);\r
189\r
190 PcdSet64 (PcdFwCfgSelectorAddress, FwCfgSelectorAddress);\r
191 PcdSet64 (PcdFwCfgDataAddress, FwCfgDataAddress);\r
192\r
193 DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,\r
194 FwCfgDataAddress));\r
195 break;\r
196\r
ad106932
AB
197 case PropertyTypeVirtio:\r
198 ASSERT (Len == 16);\r
199 //\r
200 // Create a unique device path for this transport on the fly\r
201 //\r
202 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
203 DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode (\r
204 HARDWARE_DEVICE_PATH,\r
205 HW_VENDOR_DP,\r
206 sizeof (VIRTIO_TRANSPORT_DEVICE_PATH));\r
207 if (DevicePath == NULL) {\r
208 DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));\r
209 break;\r
210 }\r
211\r
212 CopyMem (&DevicePath->Vendor.Guid, &gEfiCallerIdGuid, sizeof (EFI_GUID));\r
213 DevicePath->PhysBase = RegBase;\r
214 SetDevicePathNodeLength (&DevicePath->Vendor,\r
215 sizeof (*DevicePath) - sizeof (DevicePath->End));\r
216 SetDevicePathEndNode (&DevicePath->End);\r
217\r
218 Handle = NULL;\r
219 Status = gBS->InstallProtocolInterface (&Handle,\r
220 &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,\r
221 DevicePath);\r
222 if (EFI_ERROR (Status)) {\r
223 DEBUG ((EFI_D_ERROR, "%a: Failed to install the EFI_DEVICE_PATH "\r
224 "protocol on a new handle (Status == %r)\n",\r
225 __FUNCTION__, Status));\r
226 FreePool (DevicePath);\r
227 break;\r
228 }\r
229\r
230 Status = VirtioMmioInstallDevice (RegBase, Handle);\r
231 if (EFI_ERROR (Status)) {\r
232 DEBUG ((EFI_D_ERROR, "%a: Failed to install VirtIO transport @ 0x%Lx "\r
233 "on handle %p (Status == %r)\n", __FUNCTION__, RegBase,\r
234 Handle, Status));\r
235\r
236 Status = gBS->UninstallProtocolInterface (Handle,\r
237 &gEfiDevicePathProtocolGuid, DevicePath);\r
238 ASSERT_EFI_ERROR (Status);\r
239 FreePool (DevicePath);\r
240 }\r
241 break;\r
242\r
243 case PropertyTypeGic:\r
244 ASSERT (Len == 32);\r
245\r
246 DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
247 CpuBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);\r
248 ASSERT (DistBase < MAX_UINT32);\r
249 ASSERT (CpuBase < MAX_UINT32);\r
250\r
251 PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);\r
252 PcdSet32 (PcdGicInterruptInterfaceBase, (UINT32)CpuBase);\r
253\r
254 DEBUG ((EFI_D_INFO, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase, CpuBase));\r
255 break;\r
256\r
257 case PropertyTypeRtc:\r
258 ASSERT (Len == 16);\r
259\r
260 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
261 ASSERT (RegBase < MAX_UINT32);\r
262\r
263 PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);\r
264\r
265 DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));\r
266 RtcNode = Node;\r
267 break;\r
268\r
269 case PropertyTypeTimer:\r
270 //\r
271 // - interrupts : Interrupt list for secure, non-secure, virtual and\r
272 // hypervisor timers, in that order.\r
273 //\r
274 InterruptProp = fdt_getprop (DeviceTreeBase, Node, "interrupts", &Len);\r
275 ASSERT (Len == 48);\r
276\r
277 SecIntrNum = fdt32_to_cpu (InterruptProp[0].Number)\r
278 + (InterruptProp[0].Type ? 16 : 0);\r
279 IntrNum = fdt32_to_cpu (InterruptProp[1].Number)\r
280 + (InterruptProp[1].Type ? 16 : 0);\r
281 VirtIntrNum = fdt32_to_cpu (InterruptProp[2].Number)\r
282 + (InterruptProp[2].Type ? 16 : 0);\r
283 HypIntrNum = fdt32_to_cpu (InterruptProp[3].Number)\r
284 + (InterruptProp[3].Type ? 16 : 0);\r
285\r
286 DEBUG ((EFI_D_INFO, "Found Timer interrupts %d, %d, %d, %d\n",\r
287 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum));\r
288\r
289 PcdSet32 (PcdArmArchTimerSecIntrNum, SecIntrNum);\r
290 PcdSet32 (PcdArmArchTimerIntrNum, IntrNum);\r
291 PcdSet32 (PcdArmArchTimerVirtIntrNum, VirtIntrNum);\r
292 PcdSet32 (PcdArmArchTimerHypIntrNum, HypIntrNum);\r
293 break;\r
294\r
295 case PropertyTypePsci:\r
296 PsciMethod = fdt_getprop (DeviceTreeBase, Node, "method", &Len);\r
297\r
298 if (PsciMethod && AsciiStrnCmp (PsciMethod, "hvc", 3) == 0) {\r
299 PcdSet32 (PcdArmPsciMethod, 1);\r
300 } else if (PsciMethod && AsciiStrnCmp (PsciMethod, "smc", 3) == 0) {\r
301 PcdSet32 (PcdArmPsciMethod, 2);\r
302 } else {\r
303 DEBUG ((EFI_D_ERROR, "%a: Unknown PSCI method \"%a\"\n", __FUNCTION__,\r
304 PsciMethod));\r
305 }\r
306 break;\r
307\r
308 default:\r
309 break;\r
310 }\r
311 }\r
312\r
313 //\r
314 // UEFI takes ownership of the RTC hardware, and exposes its functionality\r
315 // through the UEFI Runtime Services GetTime, SetTime, etc. This means we\r
316 // need to disable it in the device tree to prevent the OS from attaching its\r
317 // device driver as well.\r
318 //\r
319 if ((RtcNode != -1) &&\r
320 fdt_setprop_string (DeviceTreeBase, RtcNode, "status",\r
321 "disabled") != 0) {\r
322 DEBUG ((EFI_D_WARN, "Failed to set PL031 status to 'disabled'\n"));\r
323 }\r
324 return EFI_SUCCESS;\r
325}\r