audk/ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/InstallFdt.c

424 lines
14 KiB
C

/** @file
*
* Copyright (c) 2014, ARM Limited. All rights reserved.
*
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found at
* http://opensource.org/licenses/bsd-license.php
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
**/
#include "ArmJunoDxeInternal.h"
#include <Protocol/BlockIo.h>
#include <Protocol/DevicePathFromText.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BdsLib.h>
#include <Library/DevicePathLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PrintLib.h>
#include <Library/SerialPortLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Guid/ArmGlobalVariableHob.h>
#include <Guid/EventGroup.h>
#include <Guid/Fdt.h>
#include <Guid/FileInfo.h>
#include <libfdt.h>
#define FDT_DEFAULT_FILENAME L"juno"
#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))
// Hardware Vendor Device Path node for the Juno NOR Flash. We use the Juno NOR Flash if the user
// has not specified another filesystem location into the UEFI Variable 'Fdt'.
// The Juno NOR Flash has its own filesystem format (supported by ArmPlatformPkg/FileSystem/BootMonFs).
STATIC CONST struct {
VENDOR_DEVICE_PATH NorGuid;
EFI_DEVICE_PATH End;
} mJunoNorFlashDevicePath = {
{
{ HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0 } },
{0xE7223039, 0x5836, 0x41E1, { 0xB5, 0x42, 0xD7, 0xEC, 0x73, 0x6C, 0x5E, 0x59} }
},
{ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } }
};
STATIC EFI_DEVICE_PATH* mFdtFileSystemDevicePath = NULL;
STATIC CHAR16* mFdtFileName = NULL;
STATIC BOOLEAN mFdtTableInstalled = FALSE;
/**
See definition EFI_DRIVER_BINDING_PROTOCOL.Supported()
**/
EFI_STATUS
EFIAPI
JunoFdtSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
//
// Check if the Handle support the Simple File System Protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
gImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
// Check if a DevicePath is attached to the handle
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath,
gImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
// Check if the Device Path is the one from the NOR Flash
if (CompareMem (mFdtFileSystemDevicePath, DevicePath, GetDevicePathSize (mFdtFileSystemDevicePath)) != 0) {
return EFI_NOT_FOUND;
}
gBS->CloseProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid, gImageHandle, ControllerHandle);
return Status;
}
/**
This function is used to print messages back to the user.
We use the Serial terminal for these messages as the gST->ConOut might not be initialized at this stage.
@param Message Message to display to the user
**/
STATIC
VOID
PrintMessage (
IN CHAR8* Message,
...
)
{
UINTN CharCount;
CHAR8 Buffer[100];
VA_LIST Marker;
VA_START (Marker, Message);
CharCount = AsciiVSPrint (Buffer, sizeof (Buffer), Message, Marker);
VA_END (Marker);
SerialPortWrite ((UINT8*)Buffer, CharCount);
}
/**
See definition EFI_DRIVER_BINDING_PROTOCOL.Start ()
**/
EFI_STATUS
EFIAPI
JunoFdtStart (
IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *BootMonFs;
EFI_FILE_PROTOCOL *Fs;
EFI_FILE_PROTOCOL *File;
UINTN Size;
EFI_PHYSICAL_ADDRESS FdtBlob;
EFI_FILE_INFO *FileInfo;
if (mFdtTableInstalled) {
return EFI_ALREADY_STARTED;
}
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiSimpleFileSystemProtocolGuid,
(VOID**)&BootMonFs,
gImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
// Try to Open the volume and get root directory
Status = BootMonFs->OpenVolume (BootMonFs, &Fs);
if (EFI_ERROR (Status)) {
PrintMessage ("Warning: Fail to open file system that should contain FDT file.\n");
goto UNLOAD_PROTOCOL;
}
File = NULL;
Status = Fs->Open (Fs, &File, mFdtFileName, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
PrintMessage ("Warning: Fail to load FDT file '%s'.\n", mFdtFileName);
goto UNLOAD_PROTOCOL;
}
Size = 0;
File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);
FileInfo = AllocatePool (Size);
Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);
if (EFI_ERROR (Status)) {
goto UNLOAD_PROTOCOL;
}
// Get the file size
Size = FileInfo->FileSize;
FreePool (FileInfo);
// The FDT blob is attached to the Configuration Table. It is better to load it as Runtime Service Data
// to prevent the kernel to overwrite its data
Status = gBS->AllocatePages (AllocateAnyPages, EfiRuntimeServicesData, EFI_SIZE_TO_PAGES (Size), &FdtBlob);
if (!EFI_ERROR (Status)) {
Status = File->Read (File, &Size, (VOID*)(UINTN)(FdtBlob));
if (EFI_ERROR (Status)) {
gBS->FreePages (FdtBlob, EFI_SIZE_TO_PAGES (Size));
} else {
// Check the FDT header is valid. We only make this check in DEBUG mode in case the FDT header change on
// production device and this ASSERT() becomes not valid.
ASSERT (fdt_check_header ((VOID*)(UINTN)(FdtBlob)) == 0);
// Ensure the Size of the Device Tree is smaller than the size of the read file
ASSERT ((UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlob) <= Size);
// Install the FDT into the Configuration Table
Status = gBS->InstallConfigurationTable (&gFdtTableGuid, (VOID*)(UINTN)(FdtBlob));
if (!EFI_ERROR (Status)) {
mFdtTableInstalled = TRUE;
}
}
}
UNLOAD_PROTOCOL:
// We do not need the FileSystem protocol
gBS->CloseProtocol (
ControllerHandle,
&gEfiSimpleFileSystemProtocolGuid,
DriverBinding->ImageHandle,
ControllerHandle);
return Status;
}
/**
See definition EFI_DRIVER_BINDING_PROTOCOL.Stop()
**/
EFI_STATUS
EFIAPI
JunoFdtStop (
IN EFI_DRIVER_BINDING_PROTOCOL *DriverBinding,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
UINTN Index;
VOID* FdtBlob;
UINTN FdtSize;
// Look for FDT Table
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
// Check for correct GUID type
if (CompareGuid (&gFdtTableGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {
FdtBlob = gST->ConfigurationTable[Index].VendorTable;
FdtSize = (UINTN)fdt_totalsize (FdtBlob);
// Uninstall the FDT Configuration Table
gBS->InstallConfigurationTable (&gFdtTableGuid, NULL);
// Free the memory
gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)FdtBlob, EFI_SIZE_TO_PAGES (FdtSize));
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
//
// Driver Binding Protocol for Juno FDT support
//
EFI_DRIVER_BINDING_PROTOCOL mJunoFdtBinding = {
JunoFdtSupported,
JunoFdtStart,
JunoFdtStop,
0xa,
NULL,
NULL
};
/**
Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function's context.
**/
STATIC
VOID
EFIAPI
OnEndOfDxe (
EFI_EVENT Event,
VOID *Context
)
{
EFI_DEVICE_PATH *DevicePathNode;
EFI_HANDLE Handle;
EFI_STATUS Status;
UINTN VariableSize;
CHAR16* FdtDevicePathStr;
EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
//
// Read the 'FDT' UEFI Variable to know where we should we read the blob from.
// The 'Fdt' variable contains either the full device path or only the filename of the FDT.
// If 'Fdt' only contains the filename then we assume its location is on the NOR Flash.
//
VariableSize = 0;
Status = gRT->GetVariable (L"Fdt", &gArmGlobalVariableGuid, NULL, &VariableSize, mFdtFileSystemDevicePath);
if (Status == EFI_BUFFER_TOO_SMALL) {
// Get the environment variable value
mFdtFileSystemDevicePath = AllocatePool (VariableSize);
if (mFdtFileSystemDevicePath != NULL) {
Status = gRT->GetVariable (L"Fdt", &gArmGlobalVariableGuid, NULL, &VariableSize, mFdtFileSystemDevicePath);
if (EFI_ERROR (Status)) {
FreePool (mFdtFileSystemDevicePath);
ASSERT_EFI_ERROR (Status);
return;
}
} else {
ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
return;
}
} else if (Status == EFI_NOT_FOUND) {
// If the 'Fdt' variable does not exist then we get the FDT location from the PCD
FdtDevicePathStr = (CHAR16*)PcdGetPtr (PcdFdtDevicePath);
Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return;
}
// Conversion of the Device Path string into EFI Device Path
mFdtFileSystemDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (FdtDevicePathStr);
}
if (mFdtFileSystemDevicePath != NULL) {
// Look for the FDT filename that should be contained into the FilePath device path node
DevicePathNode = mFdtFileSystemDevicePath;
while (!IsDevicePathEnd (DevicePathNode)) {
if (IS_DEVICE_PATH_NODE (DevicePathNode, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP)) {
// Extract the name from the File Path Node. The name of the Filename is the size of the
// device path node minus the size of the device path node header.
mFdtFileName = AllocateCopyPool (
DevicePathNodeLength (DevicePathNode) - sizeof(EFI_DEVICE_PATH_PROTOCOL),
((FILEPATH_DEVICE_PATH*)DevicePathNode)->PathName);
if (mFdtFileName == NULL) {
ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
return;
}
// We remove the FilePath device path node from the FileSystem Device Path
// because it will never match a device path installed by the FileSystem driver
SetDevicePathEndNode (DevicePathNode);
break;
}
DevicePathNode = NextDevicePathNode (DevicePathNode);
}
// The UEFI Variable might just contain the FDT filename. In this case we assume the FileSystem is
// the NOR Flash based one (ie: BootMonFs).
// If it was only containing the FilePath device node then the previous condition should have
// replaced it by the End Device Path Node.
if (IsDevicePathEndType (mFdtFileSystemDevicePath)) {
mFdtFileSystemDevicePath = (EFI_DEVICE_PATH*)&mJunoNorFlashDevicePath;
}
} else {
// Fallback on the NOR Flash filesystem
mFdtFileSystemDevicePath = (EFI_DEVICE_PATH*)&mJunoNorFlashDevicePath;
}
// If the FDT FileName has been provided during the FileSystem identification
if (mFdtFileName == NULL) {
mFdtFileName = AllocateCopyPool (StrSize (FDT_DEFAULT_FILENAME), FDT_DEFAULT_FILENAME);
if (mFdtFileName == NULL) {
ASSERT_EFI_ERROR (Status);
return;
}
}
// Install the Binding protocol to verify when the FileSystem that contains the FDT has been installed
Status = gBS->InstallMultipleProtocolInterfaces (
&gImageHandle,
&gEfiDriverBindingProtocolGuid, &mJunoFdtBinding,
NULL
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return;
}
//
// Force to connect the FileSystem that contains the FDT
//
BdsConnectDevicePath (mFdtFileSystemDevicePath, &Handle, NULL);
}
EFI_STATUS
JunoFdtInstall (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_EVENT EndOfDxeEvent;
// Register the event handling function to set the End Of DXE flag.
// We wait until the end of the DXE phase to load the FDT to make sure
// all the required drivers (NOR Flash, UEFI Variable, BootMonFs) are dispatched
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
OnEndOfDxe,
NULL,
&gEfiEndOfDxeEventGroupGuid,
&EndOfDxeEvent
);
ASSERT_EFI_ERROR (Status);
return Status;
}