mirror of https://github.com/acidanthera/audk.git
424 lines
14 KiB
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;
|
|
}
|