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