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