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