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