EmbeddedPkg/AndroidFastboot: support raw kernel image
AndroidFastbootApp could boot raw image with ramdisk & args in existed
boot image on storage device.
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
index 4fe3ac8..edda0f0 100644
--- a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
+++ b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
@@ -14,11 +14,14 @@
#include <Protocol/AndroidFastbootTransport.h>
#include <Protocol/AndroidFastbootPlatform.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePathFromText.h>
#include <Protocol/SimpleTextOut.h>
#include <Protocol/SimpleTextIn.h>
#include <Library/AbootimgLib.h>
#include <Library/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
#include <Library/PcdLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiApplicationEntryPoint.h>
@@ -35,6 +38,10 @@
#define FILL_BUF_SIZE 1024
+#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))
+
+#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
+
typedef struct _SPARSE_HEADER {
UINT32 Magic;
UINT16 MajorVersion;
@@ -313,6 +320,109 @@
}
STATIC
+EFI_STATUS
+BootImageWithKernel (
+ IN VOID *Kernel,
+ IN UINTN KernelSize
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *BootPathStr;
+ EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
+ EFI_DEVICE_PATH *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *Node, *NextNode;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ UINT32 MediaId, BlockSize;
+ VOID *Buffer;
+ EFI_HANDLE Handle;
+ UINTN Size;
+
+ BootPathStr = (CHAR16 *)PcdGetPtr (PcdAndroidBootDevicePath);
+ ASSERT (BootPathStr != NULL);
+ Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
+ ASSERT_EFI_ERROR(Status);
+ DevicePath = (EFI_DEVICE_PATH *)EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (BootPathStr);
+ ASSERT (DevicePath != NULL);
+
+ /* Find DevicePath node of Partition */
+ NextNode = DevicePath;
+ while (1) {
+ Node = NextNode;
+ if (IS_DEVICE_PATH_NODE (Node, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP)) {
+ break;
+ }
+ NextNode = NextDevicePathNode (Node);
+ }
+
+ Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &DevicePath, &Handle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &BlockIo,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Failed to get BlockIo: %r\n", Status));
+ return Status;
+ }
+
+ MediaId = BlockIo->Media->MediaId;
+ BlockSize = BlockIo->Media->BlockSize;
+ Buffer = AllocatePages (1);
+ if (Buffer == NULL) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ /* Load header of boot.img */
+ Status = BlockIo->ReadBlocks (
+ BlockIo,
+ MediaId,
+ 0,
+ BlockSize,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = AbootimgGetImgSize (Buffer, &Size);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Failed to get Abootimg Size: %r\n", Status));
+ return Status;
+ }
+ Size = ALIGN (Size, BlockSize);
+ FreePages (Buffer, 1);
+
+ /* Both PartitionStart and PartitionSize are counted as block size. */
+ Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
+ if (Buffer == NULL) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ /* Load header of boot.img */
+ Status = BlockIo->ReadBlocks (
+ BlockIo,
+ MediaId,
+ 0,
+ Size,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Failed to read blocks: %r\n", Status));
+ goto EXIT;
+ }
+
+ Status = AbootimgBootKernel (Kernel, KernelSize, Buffer, Size);
+
+EXIT:
+ return Status;
+}
+
+STATIC
VOID
HandleBoot (
VOID
@@ -335,6 +445,12 @@
Status = AbootimgBoot (mDataBuffer, mNumDataBytes);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status));
+
+ // Try to boot kernel with original boot image
+ Status = BootImageWithKernel (mDataBuffer, mNumDataBytes);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Failed to boot downloaded kernel: %r\n", Status));
+ }
}
// We shouldn't get here
}
diff --git a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
index 20b4330..d4cdf72 100644
--- a/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
+++ b/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.inf
@@ -43,6 +43,7 @@
[Protocols]
gAndroidFastbootTransportProtocolGuid
gAndroidFastbootPlatformProtocolGuid
+ gEfiBlockIoProtocolGuid
gEfiSimpleTextOutProtocolGuid
gEfiSimpleTextInProtocolGuid
@@ -57,3 +58,6 @@
[Guids]
gFdtTableGuid
+
+[Pcd]
+ gEmbeddedTokenSpaceGuid.PcdAndroidBootDevicePath
diff --git a/EmbeddedPkg/Include/Library/AbootimgLib.h b/EmbeddedPkg/Include/Library/AbootimgLib.h
index 6364d04..3b67d36 100644
--- a/EmbeddedPkg/Include/Library/AbootimgLib.h
+++ b/EmbeddedPkg/Include/Library/AbootimgLib.h
@@ -62,4 +62,12 @@
IN UINTN BufferSize
);
+EFI_STATUS
+AbootimgBootKernel (
+ IN VOID *Buffer,
+ IN UINTN BufferSize,
+ IN VOID *ImgBuffer,
+ IN UINTN ImgBufferSize
+ );
+
#endif /* __ABOOTIMG_H__ */
diff --git a/EmbeddedPkg/Library/AbootimgLib/AbootimgLib.c b/EmbeddedPkg/Library/AbootimgLib/AbootimgLib.c
index b1e529e..040a933 100644
--- a/EmbeddedPkg/Library/AbootimgLib/AbootimgLib.c
+++ b/EmbeddedPkg/Library/AbootimgLib/AbootimgLib.c
@@ -238,7 +238,13 @@
EFI_PHYSICAL_ADDRESS FdtBase;
VOID *NewKernelArg;
EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
+ ANDROID_BOOTIMG_HEADER *Header;
+ Header = Buffer;
+ if (Header->KernelArgs[0] == '\0') {
+ // It's not valid boot image since it's lack of kernel args.
+ return EFI_INVALID_PARAMETER;
+ }
Status = AbootimgGetKernelInfo (
Buffer,
&Kernel,
@@ -268,7 +274,7 @@
return EFI_INVALID_PARAMETER;
}
- KernelDevicePath = MemoryDevicePathTemplate;
+ CopyMem (&KernelDevicePath, &MemoryDevicePathTemplate, sizeof (MemoryDevicePathTemplate));
// Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to
// appease GCC.
@@ -290,3 +296,82 @@
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
return EFI_SUCCESS;
}
+
+EFI_STATUS
+AbootimgBootKernel (
+ IN VOID *Buffer,
+ IN UINTN BufferSize,
+ IN VOID *ImgBuffer,
+ IN UINTN ImgBufferSize
+ )
+{
+ EFI_STATUS Status;
+ VOID *ImgKernel;
+ UINTN ImgKernelSize;
+ VOID *DwnldKernel;
+ UINTN DwnldKernelSize;
+ EFI_HANDLE ImageHandle;
+ EFI_PHYSICAL_ADDRESS ImgFdtBase;
+ VOID *NewKernelArg;
+ EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
+ MEMORY_DEVICE_PATH KernelDevicePath;
+
+ Status = AbootimgGetKernelInfo (
+ Buffer,
+ &DwnldKernel,
+ &DwnldKernelSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = AbootimgGetKernelInfo (
+ ImgBuffer,
+ &ImgKernel,
+ &ImgKernelSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ /* For flatten image, Fdt is attached at the end of kernel.
+ Get real kernel size.
+ */
+ ImgKernelSize = *(UINT32 *)((EFI_PHYSICAL_ADDRESS)(UINTN)ImgKernel + KERNEL_IMAGE_STEXT_OFFSET) +
+ *(UINT32 *)((EFI_PHYSICAL_ADDRESS)(UINTN)ImgKernel + KERNEL_IMAGE_RAW_SIZE_OFFSET);
+ NewKernelArg = AllocateZeroPool (BOOTIMG_KERNEL_ARGS_SIZE);
+ if (NewKernelArg == NULL) {
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* FDT is at the end of kernel image */
+ ImgFdtBase = (EFI_PHYSICAL_ADDRESS)(UINTN)ImgKernel + ImgKernelSize;
+ Status = AbootimgInstallFdt (ImgBuffer, ImgFdtBase, NewKernelArg);
+ if (EFI_ERROR (Status)) {
+ FreePool (NewKernelArg);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (&KernelDevicePath, &MemoryDevicePathTemplate, sizeof (MemoryDevicePathTemplate));
+
+ // Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to
+ // appease GCC.
+ KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) DwnldKernel;
+ KernelDevicePath.Node1.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) DwnldKernel + DwnldKernelSize;
+
+ Status = gBS->LoadImage (TRUE, gImageHandle, (EFI_DEVICE_PATH *)&KernelDevicePath, (VOID*)(UINTN)DwnldKernel, DwnldKernelSize, &ImageHandle);
+
+ // Set kernel arguments
+ Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
+ ImageInfo->LoadOptions = NewKernelArg;
+ ImageInfo->LoadOptionsSize = StrLen (NewKernelArg) * sizeof (CHAR16);
+
+ // 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 EFI_SUCCESS;
+}