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>
19 #define FDT_ADDITIONAL_ENTRIES_SIZE 0x400
22 MEMMAP_DEVICE_PATH Node1
;
23 EFI_DEVICE_PATH_PROTOCOL End
;
26 STATIC ANDROID_BOOTIMG_PROTOCOL
*mAndroidBootImg
;
28 STATIC CONST MEMORY_DEVICE_PATH mMemoryDevicePathTemplate
=
35 (UINT8
)(sizeof (MEMMAP_DEVICE_PATH
)),
36 (UINT8
)((sizeof (MEMMAP_DEVICE_PATH
)) >> 8),
39 0, // StartingAddress (set at runtime)
40 0 // EndingAddress (set at runtime)
44 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
45 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
50 AndroidBootImgGetImgSize (
55 ANDROID_BOOTIMG_HEADER
*Header
;
57 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
59 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
60 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
61 return EFI_INVALID_PARAMETER
;
64 /* The page size is not specified, but it should be power of 2 at least */
65 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
67 /* Get real size of abootimg */
68 *ImgSize
= ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
) +
69 ALIGN_VALUE (Header
->RamdiskSize
, Header
->PageSize
) +
70 ALIGN_VALUE (Header
->SecondStageBootloaderSize
, Header
->PageSize
) +
76 AndroidBootImgGetKernelInfo (
82 ANDROID_BOOTIMG_HEADER
*Header
;
84 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
86 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
87 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
88 return EFI_INVALID_PARAMETER
;
91 if (Header
->KernelSize
== 0) {
95 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
97 *KernelSize
= Header
->KernelSize
;
98 *Kernel
= (VOID
*)((UINTN
)BootImg
+ Header
->PageSize
);
103 AndroidBootImgGetRamdiskInfo (
106 OUT UINTN
*RamdiskSize
109 ANDROID_BOOTIMG_HEADER
*Header
;
111 Header
= (ANDROID_BOOTIMG_HEADER
*)BootImg
;
113 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
114 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
115 return EFI_INVALID_PARAMETER
;
118 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
120 *RamdiskSize
= Header
->RamdiskSize
;
122 if (Header
->RamdiskSize
!= 0) {
123 *Ramdisk
= (VOID
*)((INTN
)BootImg
125 + ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
));
131 AndroidBootImgGetSecondBootLoaderInfo (
134 OUT UINTN
*SecondSize
137 ANDROID_BOOTIMG_HEADER
*Header
;
139 Header
= (ANDROID_BOOTIMG_HEADER
*)BootImg
;
141 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
142 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
143 return EFI_INVALID_PARAMETER
;
146 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
148 *SecondSize
= Header
->SecondStageBootloaderSize
;
150 if (Header
->SecondStageBootloaderSize
!= 0) {
151 *Second
= (VOID
*)((UINTN
)BootImg
153 + ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
)
154 + ALIGN_VALUE (Header
->RamdiskSize
, Header
->PageSize
));
160 AndroidBootImgGetKernelArgs (
162 OUT CHAR8
*KernelArgs
165 ANDROID_BOOTIMG_HEADER
*Header
;
167 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
168 AsciiStrnCpyS (KernelArgs
, ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
, Header
->KernelArgs
,
169 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
175 AndroidBootImgGetFdt (
180 UINTN SecondLoaderSize
;
183 /* Check whether FDT is located in second boot region as some vendor do so,
184 * because second loader is never used as far as I know. */
185 Status
= AndroidBootImgGetSecondBootLoaderInfo (
194 AndroidBootImgUpdateArgs (
199 CHAR8 ImageKernelArgs
[ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
];
202 // Get kernel arguments from Android boot image
203 Status
= AndroidBootImgGetKernelArgs (BootImg
, ImageKernelArgs
);
204 if (EFI_ERROR (Status
)) {
207 AsciiStrToUnicodeStrS (ImageKernelArgs
, KernelArgs
,
208 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
>> 1);
209 // Append platform kernel arguments
210 if(mAndroidBootImg
->AppendArgs
) {
211 Status
= mAndroidBootImg
->AppendArgs (KernelArgs
,
212 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
218 AndroidBootImgLocateFdt (
226 Status
= EfiGetSystemConfigurationTable (&gFdtTableGuid
, FdtBase
);
227 if (!EFI_ERROR (Status
)) {
231 Status
= AndroidBootImgGetFdt (BootImg
, FdtBase
);
232 if (EFI_ERROR (Status
)) {
235 Err
= fdt_check_header (*FdtBase
);
237 DEBUG ((DEBUG_ERROR
, "ERROR: Device Tree header not valid (Err:%d)\n",
239 return EFI_INVALID_PARAMETER
;
245 AndroidBootImgGetChosenNode (
246 IN INTN UpdatedFdtBase
251 ChosenNode
= fdt_subnode_offset ((CONST VOID
*)UpdatedFdtBase
, 0, "chosen");
252 if (ChosenNode
< 0) {
253 ChosenNode
= fdt_add_subnode((VOID
*)UpdatedFdtBase
, 0, "chosen");
254 if (ChosenNode
< 0) {
255 DEBUG ((DEBUG_ERROR
, "Fail to find fdt node chosen!\n"));
263 AndroidBootImgSetProperty64 (
264 IN INTN UpdatedFdtBase
,
266 IN CHAR8
*PropertyName
,
271 struct fdt_property
*Property
;
274 Property
= fdt_get_property_w((VOID
*)UpdatedFdtBase
, ChosenNode
,
276 if (NULL
== Property
&& Len
== -FDT_ERR_NOTFOUND
) {
277 Val
= cpu_to_fdt64(Val
);
278 Err
= fdt_appendprop ((VOID
*)UpdatedFdtBase
, ChosenNode
,
279 PropertyName
, &Val
, sizeof (UINT64
));
281 DEBUG ((DEBUG_ERROR
, "fdt_appendprop() fail: %a\n", fdt_strerror (Err
)));
282 return EFI_INVALID_PARAMETER
;
284 } else if (Property
!= NULL
) {
285 Err
= fdt_setprop_u64((VOID
*)UpdatedFdtBase
, ChosenNode
,
288 DEBUG ((DEBUG_ERROR
, "fdt_setprop_u64() fail: %a\n", fdt_strerror (Err
)));
289 return EFI_INVALID_PARAMETER
;
292 DEBUG ((DEBUG_ERROR
, "Failed to set fdt Property %a\n", PropertyName
));
293 return EFI_INVALID_PARAMETER
;
299 AndroidBootImgUpdateFdt (
302 IN VOID
*RamdiskData
,
306 INTN ChosenNode
, Err
, NewFdtSize
;
308 EFI_PHYSICAL_ADDRESS UpdatedFdtBase
, NewFdtBase
;
310 NewFdtSize
= (UINTN
)fdt_totalsize (FdtBase
)
311 + FDT_ADDITIONAL_ENTRIES_SIZE
;
312 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
,
313 EFI_SIZE_TO_PAGES (NewFdtSize
), &UpdatedFdtBase
);
314 if (EFI_ERROR (Status
)) {
315 DEBUG ((DEBUG_WARN
, "Warning: Failed to reallocate FDT, err %d.\n",
320 // Load the Original FDT tree into the new region
321 Err
= fdt_open_into(FdtBase
, (VOID
*)(INTN
)UpdatedFdtBase
, NewFdtSize
);
323 DEBUG ((DEBUG_ERROR
, "fdt_open_into(): %a\n", fdt_strerror (Err
)));
324 Status
= EFI_INVALID_PARAMETER
;
328 ChosenNode
= AndroidBootImgGetChosenNode(UpdatedFdtBase
);
333 Status
= AndroidBootImgSetProperty64 (UpdatedFdtBase
, ChosenNode
,
334 "linux,initrd-start",
336 if (EFI_ERROR (Status
)) {
340 Status
= AndroidBootImgSetProperty64 (UpdatedFdtBase
, ChosenNode
,
342 (UINTN
)RamdiskData
+ RamdiskSize
);
343 if (EFI_ERROR (Status
)) {
347 if (mAndroidBootImg
->UpdateDtb
) {
348 Status
= mAndroidBootImg
->UpdateDtb (UpdatedFdtBase
, &NewFdtBase
);
349 if (EFI_ERROR (Status
)) {
353 NewFdtBase
= UpdatedFdtBase
;
355 Status
= gBS
->InstallConfigurationTable (
357 (VOID
*)(UINTN
)NewFdtBase
360 if (!EFI_ERROR (Status
)) {
365 gBS
->FreePages (UpdatedFdtBase
, EFI_SIZE_TO_PAGES (NewFdtSize
));
378 MEMORY_DEVICE_PATH KernelDevicePath
;
379 EFI_HANDLE ImageHandle
;
381 EFI_LOADED_IMAGE_PROTOCOL
*ImageInfo
;
389 Status
= gBS
->LocateProtocol (&gAndroidBootImgProtocolGuid
, NULL
,
390 (VOID
**) &mAndroidBootImg
);
391 if (EFI_ERROR (Status
)) {
395 Status
= AndroidBootImgGetKernelInfo (
400 if (EFI_ERROR (Status
)) {
404 NewKernelArg
= AllocateZeroPool (ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
405 if (NewKernelArg
== NULL
) {
406 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory\n"));
407 Status
= EFI_OUT_OF_RESOURCES
;
411 Status
= AndroidBootImgUpdateArgs (Buffer
, NewKernelArg
);
412 if (EFI_ERROR (Status
)) {
416 Status
= AndroidBootImgGetRamdiskInfo (
421 if (EFI_ERROR (Status
)) {
425 Status
= AndroidBootImgLocateFdt (Buffer
, &FdtBase
);
426 if (EFI_ERROR (Status
)) {
430 Status
= AndroidBootImgUpdateFdt (Buffer
, FdtBase
, RamdiskData
, RamdiskSize
);
431 if (EFI_ERROR (Status
)) {
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
);
444 if (EFI_ERROR (Status
)) {
448 // Set kernel arguments
449 Status
= gBS
->HandleProtocol (ImageHandle
, &gEfiLoadedImageProtocolGuid
,
450 (VOID
**) &ImageInfo
);
451 if (EFI_ERROR (Status
)) {
454 ImageInfo
->LoadOptions
= NewKernelArg
;
455 ImageInfo
->LoadOptionsSize
= StrLen (NewKernelArg
) * sizeof (CHAR16
);
457 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
458 gBS
->SetWatchdogTimer (5 * 60, 0x10000, 0, NULL
);
460 Status
= gBS
->StartImage (ImageHandle
, NULL
, NULL
);
461 // Clear the Watchdog Timer if the image returns
462 gBS
->SetWatchdogTimer (0, 0x10000, 0, NULL
);
465 //Unload image as it will not be used anymore
466 if (ImageHandle
!= NULL
) {
467 gBS
->UnloadImage (ImageHandle
);
470 if (EFI_ERROR (Status
)) {
471 if (NewKernelArg
!= NULL
) {
472 FreePool (NewKernelArg
);