]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/InstallFdt.c
ArmPlatformPkg/Bds: Signal when the variable 'Fdt' has been updated
[mirror_edk2.git] / ArmPlatformPkg / ArmJunoPkg / Drivers / ArmJunoDxe / InstallFdt.c
CommitLineData
9f38945f
OM
1/** @file\r
2*\r
5c2d456b 3* Copyright (c) 2014-2015, ARM Limited. All rights reserved.\r
9f38945f
OM
4*\r
5* This program and the accompanying materials\r
6* are licensed and made available under the terms and conditions of the BSD License\r
7* which accompanies this distribution. The full text of the license may be found at\r
8* http://opensource.org/licenses/bsd-license.php\r
9*\r
10* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12*\r
13**/\r
14\r
15#include "ArmJunoDxeInternal.h"\r
16\r
17#include <Protocol/BlockIo.h>\r
18#include <Protocol/DevicePathFromText.h>\r
19#include <Protocol/DriverBinding.h>\r
20#include <Protocol/SimpleFileSystem.h>\r
21\r
22#include <Library/BaseMemoryLib.h>\r
23#include <Library/BdsLib.h>\r
24#include <Library/DevicePathLib.h>\r
25#include <Library/MemoryAllocationLib.h>\r
26#include <Library/PrintLib.h>\r
27#include <Library/SerialPortLib.h>\r
28#include <Library/UefiRuntimeServicesTableLib.h>\r
29\r
30#include <Guid/ArmGlobalVariableHob.h>\r
5c2d456b 31#include <Guid/ArmPlatformEvents.h>\r
9f38945f
OM
32#include <Guid/EventGroup.h>\r
33#include <Guid/Fdt.h>\r
34#include <Guid/FileInfo.h>\r
35\r
36#include <libfdt.h>\r
37\r
38#define FDT_DEFAULT_FILENAME L"juno"\r
39\r
40#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))\r
41\r
42// Hardware Vendor Device Path node for the Juno NOR Flash. We use the Juno NOR Flash if the user\r
43// has not specified another filesystem location into the UEFI Variable 'Fdt'.\r
44// The Juno NOR Flash has its own filesystem format (supported by ArmPlatformPkg/FileSystem/BootMonFs).\r
45STATIC CONST struct {\r
46 VENDOR_DEVICE_PATH NorGuid;\r
47 EFI_DEVICE_PATH End;\r
48} mJunoNorFlashDevicePath = {\r
49 {\r
50 { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0 } },\r
51 {0xE7223039, 0x5836, 0x41E1, { 0xB5, 0x42, 0xD7, 0xEC, 0x73, 0x6C, 0x5E, 0x59} }\r
52 },\r
53 { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } }\r
54};\r
55\r
56STATIC EFI_DEVICE_PATH* mFdtFileSystemDevicePath = NULL;\r
57STATIC CHAR16* mFdtFileName = NULL;\r
58\r
59STATIC BOOLEAN mFdtTableInstalled = FALSE;\r
60\r
61/**\r
62 See definition EFI_DRIVER_BINDING_PROTOCOL.Supported()\r
63**/\r
64EFI_STATUS\r
65EFIAPI\r
66JunoFdtSupported (\r
67 IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,\r
68 IN EFI_HANDLE ControllerHandle,\r
69 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
70 )\r
71{\r
72 EFI_STATUS Status;\r
73 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
74\r
75 //\r
76 // Check if the Handle support the Simple File System Protocol\r
77 //\r
78 Status = gBS->OpenProtocol (\r
79 ControllerHandle,\r
80 &gEfiSimpleFileSystemProtocolGuid,\r
81 NULL,\r
82 gImageHandle,\r
83 ControllerHandle,\r
84 EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
85 );\r
86\r
87 if (EFI_ERROR (Status)) {\r
88 return Status;\r
89 }\r
90\r
91 // Check if a DevicePath is attached to the handle\r
92 Status = gBS->OpenProtocol (\r
93 ControllerHandle,\r
94 &gEfiDevicePathProtocolGuid,\r
95 (VOID **)&DevicePath,\r
96 gImageHandle,\r
97 ControllerHandle,\r
98 EFI_OPEN_PROTOCOL_BY_DRIVER\r
99 );\r
100 if (EFI_ERROR (Status)) {\r
101 return Status;\r
102 }\r
103\r
104 // Check if the Device Path is the one from the NOR Flash\r
105 if (CompareMem (mFdtFileSystemDevicePath, DevicePath, GetDevicePathSize (mFdtFileSystemDevicePath)) != 0) {\r
106 return EFI_NOT_FOUND;\r
107 }\r
108\r
109 gBS->CloseProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid, gImageHandle, ControllerHandle);\r
110 return Status;\r
111}\r
112\r
113/**\r
114 This function is used to print messages back to the user.\r
115\r
116 We use the Serial terminal for these messages as the gST->ConOut might not be initialized at this stage.\r
117\r
118 @param Message Message to display to the user\r
119**/\r
120STATIC\r
121VOID\r
122PrintMessage (\r
123 IN CHAR8* Message,\r
124 ...\r
125 )\r
126{\r
127 UINTN CharCount;\r
128 CHAR8 Buffer[100];\r
129 VA_LIST Marker;\r
130\r
131 VA_START (Marker, Message);\r
132 CharCount = AsciiVSPrint (Buffer, sizeof (Buffer), Message, Marker);\r
133 VA_END (Marker);\r
134\r
135 SerialPortWrite ((UINT8*)Buffer, CharCount);\r
136}\r
137\r
138/**\r
139 See definition EFI_DRIVER_BINDING_PROTOCOL.Start ()\r
140**/\r
141EFI_STATUS\r
142EFIAPI\r
143JunoFdtStart (\r
144 IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,\r
145 IN EFI_HANDLE ControllerHandle,\r
146 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL\r
147 )\r
148{\r
149 EFI_STATUS Status;\r
150 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *BootMonFs;\r
151 EFI_FILE_PROTOCOL *Fs;\r
152 EFI_FILE_PROTOCOL *File;\r
153 UINTN Size;\r
154 EFI_PHYSICAL_ADDRESS FdtBlob;\r
155 EFI_FILE_INFO *FileInfo;\r
156\r
157 if (mFdtTableInstalled) {\r
158 return EFI_ALREADY_STARTED;\r
159 }\r
160\r
161 Status = gBS->OpenProtocol (\r
162 ControllerHandle,\r
163 &gEfiSimpleFileSystemProtocolGuid,\r
164 (VOID**)&BootMonFs,\r
165 gImageHandle,\r
166 ControllerHandle,\r
167 EFI_OPEN_PROTOCOL_BY_DRIVER\r
168 );\r
169\r
170 if (EFI_ERROR (Status)) {\r
171 return Status;\r
172 }\r
173\r
174 // Try to Open the volume and get root directory\r
175 Status = BootMonFs->OpenVolume (BootMonFs, &Fs);\r
176 if (EFI_ERROR (Status)) {\r
177 PrintMessage ("Warning: Fail to open file system that should contain FDT file.\n");\r
f38d0dfb 178 goto CLOSE_PROTOCOL;\r
9f38945f
OM
179 }\r
180\r
181 File = NULL;\r
182 Status = Fs->Open (Fs, &File, mFdtFileName, EFI_FILE_MODE_READ, 0);\r
183 if (EFI_ERROR (Status)) {\r
184 PrintMessage ("Warning: Fail to load FDT file '%s'.\n", mFdtFileName);\r
f38d0dfb 185 goto CLOSE_PROTOCOL;\r
9f38945f
OM
186 }\r
187\r
188 Size = 0;\r
189 File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);\r
190 FileInfo = AllocatePool (Size);\r
191 Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);\r
192 if (EFI_ERROR (Status)) {\r
f98f9d98 193 goto CLOSE_FILE;\r
9f38945f
OM
194 }\r
195\r
196 // Get the file size\r
197 Size = FileInfo->FileSize;\r
198 FreePool (FileInfo);\r
199\r
200 // The FDT blob is attached to the Configuration Table. It is better to load it as Runtime Service Data\r
201 // to prevent the kernel to overwrite its data\r
202 Status = gBS->AllocatePages (AllocateAnyPages, EfiRuntimeServicesData, EFI_SIZE_TO_PAGES (Size), &FdtBlob);\r
203 if (!EFI_ERROR (Status)) {\r
204 Status = File->Read (File, &Size, (VOID*)(UINTN)(FdtBlob));\r
205 if (EFI_ERROR (Status)) {\r
206 gBS->FreePages (FdtBlob, EFI_SIZE_TO_PAGES (Size));\r
207 } else {\r
208 // Check the FDT header is valid. We only make this check in DEBUG mode in case the FDT header change on\r
209 // production device and this ASSERT() becomes not valid.\r
210 ASSERT (fdt_check_header ((VOID*)(UINTN)(FdtBlob)) == 0);\r
211\r
212 // Ensure the Size of the Device Tree is smaller than the size of the read file\r
213 ASSERT ((UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlob) <= Size);\r
214\r
215 // Install the FDT into the Configuration Table\r
216 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, (VOID*)(UINTN)(FdtBlob));\r
217 if (!EFI_ERROR (Status)) {\r
218 mFdtTableInstalled = TRUE;\r
219 }\r
220 }\r
221 }\r
222\r
f98f9d98
RC
223CLOSE_FILE:\r
224 File->Close (File);\r
225\r
f38d0dfb 226CLOSE_PROTOCOL:\r
9f38945f
OM
227 // We do not need the FileSystem protocol\r
228 gBS->CloseProtocol (\r
f38d0dfb
RC
229 ControllerHandle,\r
230 &gEfiSimpleFileSystemProtocolGuid,\r
231 gImageHandle,\r
232 ControllerHandle);\r
9f38945f
OM
233\r
234 return Status;\r
235}\r
236\r
237/**\r
238 See definition EFI_DRIVER_BINDING_PROTOCOL.Stop()\r
239**/\r
240EFI_STATUS\r
241EFIAPI\r
242JunoFdtStop (\r
243 IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,\r
244 IN EFI_HANDLE ControllerHandle,\r
245 IN UINTN NumberOfChildren,\r
246 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
247 )\r
248{\r
249 UINTN Index;\r
250 VOID* FdtBlob;\r
251 UINTN FdtSize;\r
252\r
253 // Look for FDT Table\r
254 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {\r
255 // Check for correct GUID type\r
256 if (CompareGuid (&gFdtTableGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r
257 FdtBlob = gST->ConfigurationTable[Index].VendorTable;\r
258 FdtSize = (UINTN)fdt_totalsize (FdtBlob);\r
259\r
260 // Uninstall the FDT Configuration Table\r
261 gBS->InstallConfigurationTable (&gFdtTableGuid, NULL);\r
262\r
263 // Free the memory\r
264 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)FdtBlob, EFI_SIZE_TO_PAGES (FdtSize));\r
265\r
266 return EFI_SUCCESS;\r
267 }\r
268 }\r
269\r
270 return EFI_NOT_FOUND;\r
271}\r
272\r
273//\r
274// Driver Binding Protocol for Juno FDT support\r
275//\r
276EFI_DRIVER_BINDING_PROTOCOL mJunoFdtBinding = {\r
277 JunoFdtSupported,\r
278 JunoFdtStart,\r
279 JunoFdtStop,\r
280 0xa,\r
281 NULL,\r
282 NULL\r
283};\r
284\r
285/**\r
286 Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group.\r
287\r
288 This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group.\r
289\r
290 @param Event Event whose notification function is being invoked.\r
291 @param Context Pointer to the notification function's context.\r
292\r
293**/\r
294STATIC\r
295VOID\r
296EFIAPI\r
5c2d456b 297LoadFdtOnEvent (\r
9f38945f
OM
298 EFI_EVENT Event,\r
299 VOID *Context\r
300 )\r
301{\r
302 EFI_DEVICE_PATH *DevicePathNode;\r
303 EFI_HANDLE Handle;\r
304 EFI_STATUS Status;\r
305 UINTN VariableSize;\r
306 CHAR16* FdtDevicePathStr;\r
307 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;\r
5c2d456b 308 EFI_EVENT ArmPlatformUpdateFdtEvent;\r
9f38945f
OM
309\r
310 //\r
311 // Read the 'FDT' UEFI Variable to know where we should we read the blob from.\r
312 // The 'Fdt' variable contains either the full device path or only the filename of the FDT.\r
313 // If 'Fdt' only contains the filename then we assume its location is on the NOR Flash.\r
314 //\r
315 VariableSize = 0;\r
316 Status = gRT->GetVariable (L"Fdt", &gArmGlobalVariableGuid, NULL, &VariableSize, mFdtFileSystemDevicePath);\r
317 if (Status == EFI_BUFFER_TOO_SMALL) {\r
318 // Get the environment variable value\r
319 mFdtFileSystemDevicePath = AllocatePool (VariableSize);\r
320 if (mFdtFileSystemDevicePath != NULL) {\r
321 Status = gRT->GetVariable (L"Fdt", &gArmGlobalVariableGuid, NULL, &VariableSize, mFdtFileSystemDevicePath);\r
322 if (EFI_ERROR (Status)) {\r
323 FreePool (mFdtFileSystemDevicePath);\r
324 ASSERT_EFI_ERROR (Status);\r
325 return;\r
326 }\r
327 } else {\r
328 ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);\r
329 return;\r
330 }\r
331 } else if (Status == EFI_NOT_FOUND) {\r
332 // If the 'Fdt' variable does not exist then we get the FDT location from the PCD\r
333 FdtDevicePathStr = (CHAR16*)PcdGetPtr (PcdFdtDevicePath);\r
334\r
335 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);\r
336 if (EFI_ERROR (Status)) {\r
337 ASSERT_EFI_ERROR (Status);\r
338 return;\r
339 }\r
340\r
341 // Conversion of the Device Path string into EFI Device Path\r
342 mFdtFileSystemDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (FdtDevicePathStr);\r
343 }\r
344\r
345 if (mFdtFileSystemDevicePath != NULL) {\r
346 // Look for the FDT filename that should be contained into the FilePath device path node\r
347 DevicePathNode = mFdtFileSystemDevicePath;\r
348 while (!IsDevicePathEnd (DevicePathNode)) {\r
349 if (IS_DEVICE_PATH_NODE (DevicePathNode, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP)) {\r
350 // Extract the name from the File Path Node. The name of the Filename is the size of the\r
351 // device path node minus the size of the device path node header.\r
352 mFdtFileName = AllocateCopyPool (\r
353 DevicePathNodeLength (DevicePathNode) - sizeof(EFI_DEVICE_PATH_PROTOCOL),\r
354 ((FILEPATH_DEVICE_PATH*)DevicePathNode)->PathName);\r
355 if (mFdtFileName == NULL) {\r
356 ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);\r
357 return;\r
358 }\r
359\r
360 // We remove the FilePath device path node from the FileSystem Device Path\r
361 // because it will never match a device path installed by the FileSystem driver\r
362 SetDevicePathEndNode (DevicePathNode);\r
363 break;\r
364 }\r
365 DevicePathNode = NextDevicePathNode (DevicePathNode);\r
366 }\r
367\r
368 // The UEFI Variable might just contain the FDT filename. In this case we assume the FileSystem is\r
369 // the NOR Flash based one (ie: BootMonFs).\r
370 // If it was only containing the FilePath device node then the previous condition should have\r
371 // replaced it by the End Device Path Node.\r
372 if (IsDevicePathEndType (mFdtFileSystemDevicePath)) {\r
373 mFdtFileSystemDevicePath = (EFI_DEVICE_PATH*)&mJunoNorFlashDevicePath;\r
374 }\r
375 } else {\r
376 // Fallback on the NOR Flash filesystem\r
377 mFdtFileSystemDevicePath = (EFI_DEVICE_PATH*)&mJunoNorFlashDevicePath;\r
378 }\r
379\r
380 // If the FDT FileName has been provided during the FileSystem identification\r
381 if (mFdtFileName == NULL) {\r
382 mFdtFileName = AllocateCopyPool (StrSize (FDT_DEFAULT_FILENAME), FDT_DEFAULT_FILENAME);\r
383 if (mFdtFileName == NULL) {\r
384 ASSERT_EFI_ERROR (Status);\r
385 return;\r
386 }\r
387 }\r
388\r
5c2d456b
OM
389 // Context is not NULL when this function is called for a gEfiEndOfDxeEventGroupGuid event\r
390 if (Context) {\r
391 // Install the Binding protocol to verify when the FileSystem that contains the FDT has been installed\r
392 Status = gBS->InstallMultipleProtocolInterfaces (\r
393 &gImageHandle,\r
394 &gEfiDriverBindingProtocolGuid, &mJunoFdtBinding,\r
395 NULL\r
396 );\r
397 if (EFI_ERROR (Status)) {\r
398 ASSERT_EFI_ERROR (Status);\r
399 return;\r
400 }\r
401\r
402 // Register the event triggered when the 'Fdt' variable is updated.\r
403 Status = gBS->CreateEventEx (\r
404 EVT_NOTIFY_SIGNAL,\r
405 TPL_CALLBACK,\r
406 LoadFdtOnEvent,\r
407 NULL,\r
408 &gArmPlatformUpdateFdtEventGuid,\r
409 &ArmPlatformUpdateFdtEvent\r
410 );\r
9f38945f 411 ASSERT_EFI_ERROR (Status);\r
9f38945f
OM
412 }\r
413\r
414 //\r
415 // Force to connect the FileSystem that contains the FDT\r
416 //\r
417 BdsConnectDevicePath (mFdtFileSystemDevicePath, &Handle, NULL);\r
418}\r
419\r
5c2d456b
OM
420STATIC CONST BOOLEAN mIsEndOfDxeEvent = TRUE;\r
421\r
9f38945f
OM
422EFI_STATUS\r
423JunoFdtInstall (\r
424 IN EFI_HANDLE ImageHandle\r
425 )\r
426{\r
427 EFI_STATUS Status;\r
428 EFI_EVENT EndOfDxeEvent;\r
429\r
430 // Register the event handling function to set the End Of DXE flag.\r
431 // We wait until the end of the DXE phase to load the FDT to make sure\r
432 // all the required drivers (NOR Flash, UEFI Variable, BootMonFs) are dispatched\r
433 Status = gBS->CreateEventEx (\r
434 EVT_NOTIFY_SIGNAL,\r
435 TPL_CALLBACK,\r
5c2d456b
OM
436 LoadFdtOnEvent,\r
437 &mIsEndOfDxeEvent,\r
9f38945f
OM
438 &gEfiEndOfDxeEventGroupGuid,\r
439 &EndOfDxeEvent\r
440 );\r
441 ASSERT_EFI_ERROR (Status);\r
442\r
443 return Status;\r
444}\r