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