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