]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/InstallFdt.c
ArmPlatformPkg/ArmJunoPkg: Added Juno development board support
[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 UNLOAD_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 UNLOAD_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 UNLOAD_PROTOCOL;
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 UNLOAD_PROTOCOL:
223 // We do not need the FileSystem protocol
224 gBS->CloseProtocol (
225 ControllerHandle,
226 &gEfiSimpleFileSystemProtocolGuid,
227 DriverBinding->ImageHandle,
228 ControllerHandle);
229
230 return Status;
231 }
232
233 /**
234 See definition EFI_DRIVER_BINDING_PROTOCOL.Stop()
235 **/
236 EFI_STATUS
237 EFIAPI
238 JunoFdtStop (
239 IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
240 IN EFI_HANDLE ControllerHandle,
241 IN UINTN NumberOfChildren,
242 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
243 )
244 {
245 UINTN Index;
246 VOID* FdtBlob;
247 UINTN FdtSize;
248
249 // Look for FDT Table
250 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
251 // Check for correct GUID type
252 if (CompareGuid (&gFdtTableGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {
253 FdtBlob = gST->ConfigurationTable[Index].VendorTable;
254 FdtSize = (UINTN)fdt_totalsize (FdtBlob);
255
256 // Uninstall the FDT Configuration Table
257 gBS->InstallConfigurationTable (&gFdtTableGuid, NULL);
258
259 // Free the memory
260 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)FdtBlob, EFI_SIZE_TO_PAGES (FdtSize));
261
262 return EFI_SUCCESS;
263 }
264 }
265
266 return EFI_NOT_FOUND;
267 }
268
269 //
270 // Driver Binding Protocol for Juno FDT support
271 //
272 EFI_DRIVER_BINDING_PROTOCOL mJunoFdtBinding = {
273 JunoFdtSupported,
274 JunoFdtStart,
275 JunoFdtStop,
276 0xa,
277 NULL,
278 NULL
279 };
280
281 /**
282 Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
283
284 This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
285
286 @param Event Event whose notification function is being invoked.
287 @param Context Pointer to the notification function's context.
288
289 **/
290 STATIC
291 VOID
292 EFIAPI
293 OnEndOfDxe (
294 EFI_EVENT Event,
295 VOID *Context
296 )
297 {
298 EFI_DEVICE_PATH *DevicePathNode;
299 EFI_HANDLE Handle;
300 EFI_STATUS Status;
301 UINTN VariableSize;
302 CHAR16* FdtDevicePathStr;
303 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
304
305 //
306 // Read the 'FDT' UEFI Variable to know where we should we read the blob from.
307 // The 'Fdt' variable contains either the full device path or only the filename of the FDT.
308 // If 'Fdt' only contains the filename then we assume its location is on the NOR Flash.
309 //
310 VariableSize = 0;
311 Status = gRT->GetVariable (L"Fdt", &gArmGlobalVariableGuid, NULL, &VariableSize, mFdtFileSystemDevicePath);
312 if (Status == EFI_BUFFER_TOO_SMALL) {
313 // Get the environment variable value
314 mFdtFileSystemDevicePath = AllocatePool (VariableSize);
315 if (mFdtFileSystemDevicePath != NULL) {
316 Status = gRT->GetVariable (L"Fdt", &gArmGlobalVariableGuid, NULL, &VariableSize, mFdtFileSystemDevicePath);
317 if (EFI_ERROR (Status)) {
318 FreePool (mFdtFileSystemDevicePath);
319 ASSERT_EFI_ERROR (Status);
320 return;
321 }
322 } else {
323 ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
324 return;
325 }
326 } else if (Status == EFI_NOT_FOUND) {
327 // If the 'Fdt' variable does not exist then we get the FDT location from the PCD
328 FdtDevicePathStr = (CHAR16*)PcdGetPtr (PcdFdtDevicePath);
329
330 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
331 if (EFI_ERROR (Status)) {
332 ASSERT_EFI_ERROR (Status);
333 return;
334 }
335
336 // Conversion of the Device Path string into EFI Device Path
337 mFdtFileSystemDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (FdtDevicePathStr);
338 }
339
340 if (mFdtFileSystemDevicePath != NULL) {
341 // Look for the FDT filename that should be contained into the FilePath device path node
342 DevicePathNode = mFdtFileSystemDevicePath;
343 while (!IsDevicePathEnd (DevicePathNode)) {
344 if (IS_DEVICE_PATH_NODE (DevicePathNode, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP)) {
345 // Extract the name from the File Path Node. The name of the Filename is the size of the
346 // device path node minus the size of the device path node header.
347 mFdtFileName = AllocateCopyPool (
348 DevicePathNodeLength (DevicePathNode) - sizeof(EFI_DEVICE_PATH_PROTOCOL),
349 ((FILEPATH_DEVICE_PATH*)DevicePathNode)->PathName);
350 if (mFdtFileName == NULL) {
351 ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
352 return;
353 }
354
355 // We remove the FilePath device path node from the FileSystem Device Path
356 // because it will never match a device path installed by the FileSystem driver
357 SetDevicePathEndNode (DevicePathNode);
358 break;
359 }
360 DevicePathNode = NextDevicePathNode (DevicePathNode);
361 }
362
363 // The UEFI Variable might just contain the FDT filename. In this case we assume the FileSystem is
364 // the NOR Flash based one (ie: BootMonFs).
365 // If it was only containing the FilePath device node then the previous condition should have
366 // replaced it by the End Device Path Node.
367 if (IsDevicePathEndType (mFdtFileSystemDevicePath)) {
368 mFdtFileSystemDevicePath = (EFI_DEVICE_PATH*)&mJunoNorFlashDevicePath;
369 }
370 } else {
371 // Fallback on the NOR Flash filesystem
372 mFdtFileSystemDevicePath = (EFI_DEVICE_PATH*)&mJunoNorFlashDevicePath;
373 }
374
375 // If the FDT FileName has been provided during the FileSystem identification
376 if (mFdtFileName == NULL) {
377 mFdtFileName = AllocateCopyPool (StrSize (FDT_DEFAULT_FILENAME), FDT_DEFAULT_FILENAME);
378 if (mFdtFileName == NULL) {
379 ASSERT_EFI_ERROR (Status);
380 return;
381 }
382 }
383
384 // Install the Binding protocol to verify when the FileSystem that contains the FDT has been installed
385 Status = gBS->InstallMultipleProtocolInterfaces (
386 &gImageHandle,
387 &gEfiDriverBindingProtocolGuid, &mJunoFdtBinding,
388 NULL
389 );
390 if (EFI_ERROR (Status)) {
391 ASSERT_EFI_ERROR (Status);
392 return;
393 }
394
395 //
396 // Force to connect the FileSystem that contains the FDT
397 //
398 BdsConnectDevicePath (mFdtFileSystemDevicePath, &Handle, NULL);
399 }
400
401 EFI_STATUS
402 JunoFdtInstall (
403 IN EFI_HANDLE ImageHandle
404 )
405 {
406 EFI_STATUS Status;
407 EFI_EVENT EndOfDxeEvent;
408
409 // Register the event handling function to set the End Of DXE flag.
410 // We wait until the end of the DXE phase to load the FDT to make sure
411 // all the required drivers (NOR Flash, UEFI Variable, BootMonFs) are dispatched
412 Status = gBS->CreateEventEx (
413 EVT_NOTIFY_SIGNAL,
414 TPL_CALLBACK,
415 OnEndOfDxe,
416 NULL,
417 &gEfiEndOfDxeEventGroupGuid,
418 &EndOfDxeEvent
419 );
420 ASSERT_EFI_ERROR (Status);
421
422 return Status;
423 }