blob: 163ac7ecc90300340f0cb8a6df92a3411f8d208e [file] [log] [blame]
/** @file
Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "AndroidFastbootApp.h"
#include <Protocol/DevicePath.h>
#include <Protocol/LoadedImage.h>
#include <Library/DevicePathLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
// Device Path representing an image in memory
#pragma pack(1)
typedef struct {
MEMMAP_DEVICE_PATH Node1;
EFI_DEVICE_PATH_PROTOCOL End;
} MEMORY_DEVICE_PATH;
#pragma pack()
STATIC CONST MEMORY_DEVICE_PATH MemoryDevicePathTemplate =
{
{
{
HARDWARE_DEVICE_PATH,
HW_MEMMAP_DP,
{
(UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
(UINT8)((sizeof (MEMMAP_DEVICE_PATH)) >> 8),
},
}, // Header
0, // StartingAddress (set at runtime)
0 // EndingAddress (set at runtime)
}, // Node1
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{ sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
} // End
};
/**
Start an EFI Application from a Device Path
@param ParentImageHandle Handle of the calling image
@param DevicePath Location of the EFI Application
@retval EFI_SUCCESS All drivers have been connected
@retval EFI_NOT_FOUND The Linux kernel Device Path has not been found
@retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.
**/
STATIC
EFI_STATUS
StartEfiApplication (
IN EFI_HANDLE ParentImageHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN UINTN LoadOptionsSize,
IN VOID *LoadOptions
)
{
EFI_STATUS Status;
EFI_HANDLE ImageHandle;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
// Load the image from the device path with Boot Services function
Status = gBS->LoadImage (
TRUE,
ParentImageHandle,
DevicePath,
NULL,
0,
&ImageHandle
);
if (EFI_ERROR (Status)) {
//
// With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
// with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
// If the caller doesn't have the option to defer the execution of an image, we should
// unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
//
if (Status == EFI_SECURITY_VIOLATION) {
gBS->UnloadImage (ImageHandle);
}
return Status;
}
// Passed LoadOptions to the EFI Application
if (LoadOptionsSize != 0) {
Status = gBS->HandleProtocol (
ImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **)&LoadedImage
);
if (EFI_ERROR (Status)) {
return Status;
}
LoadedImage->LoadOptionsSize = LoadOptionsSize;
LoadedImage->LoadOptions = LoadOptions;
}
// Before calling the image, enable the Watchdog Timer for the 5 Minute period
gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
// Start the image
Status = gBS->StartImage (ImageHandle, NULL, NULL);
// Clear the Watchdog Timer after the image returns
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
return Status;
}
EFI_STATUS
BootAndroidBootImg (
IN UINTN BufferSize,
IN VOID *Buffer
)
{
EFI_STATUS Status;
CHAR8 KernelArgs[ANDROID_BOOTIMG_KERNEL_ARGS_SIZE];
VOID *Kernel;
UINTN KernelSize;
VOID *Ramdisk;
UINTN RamdiskSize;
MEMORY_DEVICE_PATH KernelDevicePath;
CHAR16 *LoadOptions, *NewLoadOptions;
Status = ParseAndroidBootImg (
Buffer,
&Kernel,
&KernelSize,
&Ramdisk,
&RamdiskSize,
KernelArgs
);
if (EFI_ERROR (Status)) {
return Status;
}
KernelDevicePath = MemoryDevicePathTemplate;
// Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to
// appease GCC.
KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Kernel;
KernelDevicePath.Node1.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Kernel + KernelSize;
// Initialize Linux command line
LoadOptions = CatSPrint (NULL, L"%a", KernelArgs);
if (LoadOptions == NULL) {
return EFI_OUT_OF_RESOURCES;
}
if (RamdiskSize != 0) {
NewLoadOptions = CatSPrint (
LoadOptions,
L" initrd=0x%x,0x%x",
(UINTN)Ramdisk,
RamdiskSize
);
FreePool (LoadOptions);
if (NewLoadOptions == NULL) {
return EFI_OUT_OF_RESOURCES;
}
LoadOptions = NewLoadOptions;
}
Status = StartEfiApplication (
gImageHandle,
(EFI_DEVICE_PATH_PROTOCOL *)&KernelDevicePath,
StrSize (LoadOptions),
LoadOptions
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Couldn't Boot Linux: %d\n", Status));
Status = EFI_DEVICE_ERROR;
goto FreeLoadOptions;
}
// If we got here we do a confused face because BootLinuxFdt returned,
// reporting success.
DEBUG ((DEBUG_ERROR, "WARNING: BdsBootLinuxFdt returned EFI_SUCCESS.\n"));
return EFI_SUCCESS;
FreeLoadOptions:
FreePool (LoadOptions);
return Status;
}