3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
4 Copyright (c) 2017, Linaro. All rights reserved.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Library/AndroidBootImgLib.h>
12 #include <Library/PrintLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 #include <Library/UefiLib.h>
16 #include <Protocol/AndroidBootImg.h>
17 #include <Protocol/LoadedImage.h>
21 #define FDT_ADDITIONAL_ENTRIES_SIZE 0x400
24 MEMMAP_DEVICE_PATH Node1
;
25 EFI_DEVICE_PATH_PROTOCOL End
;
28 STATIC ANDROID_BOOTIMG_PROTOCOL
*mAndroidBootImg
;
30 STATIC CONST MEMORY_DEVICE_PATH mMemoryDevicePathTemplate
=
37 (UINT8
)(sizeof (MEMMAP_DEVICE_PATH
)),
38 (UINT8
)((sizeof (MEMMAP_DEVICE_PATH
)) >> 8),
41 0, // StartingAddress (set at runtime)
42 0 // EndingAddress (set at runtime)
46 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
47 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
52 AndroidBootImgGetImgSize (
57 ANDROID_BOOTIMG_HEADER
*Header
;
59 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
61 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
62 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
63 return EFI_INVALID_PARAMETER
;
66 /* The page size is not specified, but it should be power of 2 at least */
67 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
69 /* Get real size of abootimg */
70 *ImgSize
= ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
) +
71 ALIGN_VALUE (Header
->RamdiskSize
, Header
->PageSize
) +
72 ALIGN_VALUE (Header
->SecondStageBootloaderSize
, Header
->PageSize
) +
78 AndroidBootImgGetKernelInfo (
84 ANDROID_BOOTIMG_HEADER
*Header
;
86 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
88 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
89 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
90 return EFI_INVALID_PARAMETER
;
93 if (Header
->KernelSize
== 0) {
97 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
99 *KernelSize
= Header
->KernelSize
;
100 *Kernel
= BootImg
+ Header
->PageSize
;
105 AndroidBootImgGetRamdiskInfo (
108 OUT UINTN
*RamdiskSize
111 ANDROID_BOOTIMG_HEADER
*Header
;
113 Header
= (ANDROID_BOOTIMG_HEADER
*)BootImg
;
115 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
116 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
117 return EFI_INVALID_PARAMETER
;
120 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
122 *RamdiskSize
= Header
->RamdiskSize
;
124 if (Header
->RamdiskSize
!= 0) {
125 *Ramdisk
= (VOID
*)((INTN
)BootImg
127 + ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
));
133 AndroidBootImgGetSecondBootLoaderInfo (
136 OUT UINTN
*SecondSize
139 ANDROID_BOOTIMG_HEADER
*Header
;
141 Header
= (ANDROID_BOOTIMG_HEADER
*)BootImg
;
143 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
144 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
145 return EFI_INVALID_PARAMETER
;
148 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
150 *SecondSize
= Header
->SecondStageBootloaderSize
;
152 if (Header
->SecondStageBootloaderSize
!= 0) {
153 *Second
= (VOID
*)((UINTN
)BootImg
155 + ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
)
156 + ALIGN_VALUE (Header
->RamdiskSize
, Header
->PageSize
));
162 AndroidBootImgGetKernelArgs (
164 OUT CHAR8
*KernelArgs
167 ANDROID_BOOTIMG_HEADER
*Header
;
169 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
170 AsciiStrnCpyS (KernelArgs
, ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
, Header
->KernelArgs
,
171 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
177 AndroidBootImgGetFdt (
182 UINTN SecondLoaderSize
;
185 /* Check whether FDT is located in second boot region as some vendor do so,
186 * because second loader is never used as far as I know. */
187 Status
= AndroidBootImgGetSecondBootLoaderInfo (
196 AndroidBootImgUpdateArgs (
201 CHAR8 ImageKernelArgs
[ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
];
204 // Get kernel arguments from Android boot image
205 Status
= AndroidBootImgGetKernelArgs (BootImg
, ImageKernelArgs
);
206 if (EFI_ERROR (Status
)) {
209 AsciiStrToUnicodeStrS (ImageKernelArgs
, KernelArgs
,
210 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
>> 1);
211 // Append platform kernel arguments
212 if(mAndroidBootImg
->AppendArgs
) {
213 Status
= mAndroidBootImg
->AppendArgs (KernelArgs
,
214 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
220 AndroidBootImgLocateFdt (
228 Status
= EfiGetSystemConfigurationTable (&gFdtTableGuid
, FdtBase
);
229 if (!EFI_ERROR (Status
)) {
233 Status
= AndroidBootImgGetFdt (BootImg
, FdtBase
);
234 if (EFI_ERROR (Status
)) {
237 Err
= fdt_check_header (*FdtBase
);
239 DEBUG ((DEBUG_ERROR
, "ERROR: Device Tree header not valid (Err:%d)\n",
241 return EFI_INVALID_PARAMETER
;
247 AndroidBootImgGetChosenNode (
248 IN INTN UpdatedFdtBase
253 ChosenNode
= fdt_subnode_offset ((CONST VOID
*)UpdatedFdtBase
, 0, "chosen");
254 if (ChosenNode
< 0) {
255 ChosenNode
= fdt_add_subnode((VOID
*)UpdatedFdtBase
, 0, "chosen");
256 if (ChosenNode
< 0) {
257 DEBUG ((DEBUG_ERROR
, "Fail to find fdt node chosen!\n"));
265 AndroidBootImgSetProperty64 (
266 IN INTN UpdatedFdtBase
,
268 IN CHAR8
*PropertyName
,
273 struct fdt_property
*Property
;
276 Property
= fdt_get_property_w((VOID
*)UpdatedFdtBase
, ChosenNode
,
278 if (NULL
== Property
&& Len
== -FDT_ERR_NOTFOUND
) {
279 Val
= cpu_to_fdt64(Val
);
280 Err
= fdt_appendprop ((VOID
*)UpdatedFdtBase
, ChosenNode
,
281 PropertyName
, &Val
, sizeof (UINT64
));
283 DEBUG ((DEBUG_ERROR
, "fdt_appendprop() fail: %a\n", fdt_strerror (Err
)));
284 return EFI_INVALID_PARAMETER
;
286 } else if (Property
!= NULL
) {
287 Err
= fdt_setprop_u64((VOID
*)UpdatedFdtBase
, ChosenNode
,
290 DEBUG ((DEBUG_ERROR
, "fdt_setprop_u64() fail: %a\n", fdt_strerror (Err
)));
291 return EFI_INVALID_PARAMETER
;
294 DEBUG ((DEBUG_ERROR
, "Failed to set fdt Property %a\n", PropertyName
));
295 return EFI_INVALID_PARAMETER
;
301 AndroidBootImgUpdateFdt (
304 IN VOID
*RamdiskData
,
308 INTN ChosenNode
, Err
, NewFdtSize
;
310 EFI_PHYSICAL_ADDRESS UpdatedFdtBase
, NewFdtBase
;
312 NewFdtSize
= (UINTN
)fdt_totalsize (FdtBase
)
313 + FDT_ADDITIONAL_ENTRIES_SIZE
;
314 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
,
315 EFI_SIZE_TO_PAGES (NewFdtSize
), &UpdatedFdtBase
);
316 if (EFI_ERROR (Status
)) {
317 DEBUG ((DEBUG_WARN
, "Warning: Failed to reallocate FDT, err %d.\n",
322 // Load the Original FDT tree into the new region
323 Err
= fdt_open_into(FdtBase
, (VOID
*)(INTN
)UpdatedFdtBase
, NewFdtSize
);
325 DEBUG ((DEBUG_ERROR
, "fdt_open_into(): %a\n", fdt_strerror (Err
)));
326 Status
= EFI_INVALID_PARAMETER
;
330 ChosenNode
= AndroidBootImgGetChosenNode(UpdatedFdtBase
);
335 Status
= AndroidBootImgSetProperty64 (UpdatedFdtBase
, ChosenNode
,
336 "linux,initrd-start",
338 if (EFI_ERROR (Status
)) {
342 Status
= AndroidBootImgSetProperty64 (UpdatedFdtBase
, ChosenNode
,
344 (UINTN
)(RamdiskData
+ RamdiskSize
));
345 if (EFI_ERROR (Status
)) {
349 if (mAndroidBootImg
->UpdateDtb
) {
350 Status
= mAndroidBootImg
->UpdateDtb (UpdatedFdtBase
, &NewFdtBase
);
351 if (EFI_ERROR (Status
)) {
355 Status
= gBS
->InstallConfigurationTable (
357 (VOID
*)(UINTN
)NewFdtBase
361 if (!EFI_ERROR (Status
)) {
366 gBS
->FreePages (UpdatedFdtBase
, EFI_SIZE_TO_PAGES (NewFdtSize
));
379 MEMORY_DEVICE_PATH KernelDevicePath
;
380 EFI_HANDLE ImageHandle
;
382 EFI_LOADED_IMAGE_PROTOCOL
*ImageInfo
;
387 Status
= gBS
->LocateProtocol (&gAndroidBootImgProtocolGuid
, NULL
,
388 (VOID
**) &mAndroidBootImg
);
389 if (EFI_ERROR (Status
)) {
393 Status
= AndroidBootImgGetKernelInfo (
398 if (EFI_ERROR (Status
)) {
402 NewKernelArg
= AllocateZeroPool (ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
403 if (NewKernelArg
== NULL
) {
404 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory\n"));
405 return EFI_OUT_OF_RESOURCES
;
408 Status
= AndroidBootImgUpdateArgs (Buffer
, NewKernelArg
);
409 if (EFI_ERROR (Status
)) {
410 FreePool (NewKernelArg
);
414 Status
= AndroidBootImgGetRamdiskInfo (
419 if (EFI_ERROR (Status
)) {
423 Status
= AndroidBootImgLocateFdt (Buffer
, &FdtBase
);
424 if (EFI_ERROR (Status
)) {
425 FreePool (NewKernelArg
);
429 Status
= AndroidBootImgUpdateFdt (Buffer
, FdtBase
, RamdiskData
, RamdiskSize
);
430 if (EFI_ERROR (Status
)) {
431 FreePool (NewKernelArg
);
435 KernelDevicePath
= mMemoryDevicePathTemplate
;
437 KernelDevicePath
.Node1
.StartingAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
) Kernel
;
438 KernelDevicePath
.Node1
.EndingAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
) Kernel
441 Status
= gBS
->LoadImage (TRUE
, gImageHandle
,
442 (EFI_DEVICE_PATH
*)&KernelDevicePath
,
443 (VOID
*)(UINTN
)Kernel
, KernelSize
, &ImageHandle
);
445 // Set kernel arguments
446 Status
= gBS
->HandleProtocol (ImageHandle
, &gEfiLoadedImageProtocolGuid
,
447 (VOID
**) &ImageInfo
);
448 ImageInfo
->LoadOptions
= NewKernelArg
;
449 ImageInfo
->LoadOptionsSize
= StrLen (NewKernelArg
) * sizeof (CHAR16
);
451 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
452 gBS
->SetWatchdogTimer (5 * 60, 0x10000, 0, NULL
);
454 Status
= gBS
->StartImage (ImageHandle
, NULL
, NULL
);
455 // Clear the Watchdog Timer if the image returns
456 gBS
->SetWatchdogTimer (0, 0x10000, 0, NULL
);