3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
4 Copyright (c) 2017, Linaro. All rights reserved.
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <Library/AndroidBootImgLib.h>
18 #include <Library/PrintLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/UefiLib.h>
22 #include <Protocol/AndroidBootImg.h>
23 #include <Protocol/LoadedImage.h>
27 #define FDT_ADDITIONAL_ENTRIES_SIZE 0x400
30 MEMMAP_DEVICE_PATH Node1
;
31 EFI_DEVICE_PATH_PROTOCOL End
;
34 STATIC ANDROID_BOOTIMG_PROTOCOL
*mAndroidBootImg
;
36 STATIC CONST MEMORY_DEVICE_PATH mMemoryDevicePathTemplate
=
43 (UINT8
)(sizeof (MEMMAP_DEVICE_PATH
)),
44 (UINT8
)((sizeof (MEMMAP_DEVICE_PATH
)) >> 8),
47 0, // StartingAddress (set at runtime)
48 0 // EndingAddress (set at runtime)
52 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
53 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
58 AndroidBootImgGetImgSize (
63 ANDROID_BOOTIMG_HEADER
*Header
;
65 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
67 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
68 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
69 return EFI_INVALID_PARAMETER
;
72 /* The page size is not specified, but it should be power of 2 at least */
73 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
75 /* Get real size of abootimg */
76 *ImgSize
= ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
) +
77 ALIGN_VALUE (Header
->RamdiskSize
, Header
->PageSize
) +
78 ALIGN_VALUE (Header
->SecondStageBootloaderSize
, Header
->PageSize
) +
84 AndroidBootImgGetKernelInfo (
90 ANDROID_BOOTIMG_HEADER
*Header
;
92 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
94 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
95 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
96 return EFI_INVALID_PARAMETER
;
99 if (Header
->KernelSize
== 0) {
100 return EFI_NOT_FOUND
;
103 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
105 *KernelSize
= Header
->KernelSize
;
106 *Kernel
= BootImg
+ Header
->PageSize
;
111 AndroidBootImgGetRamdiskInfo (
114 OUT UINTN
*RamdiskSize
117 ANDROID_BOOTIMG_HEADER
*Header
;
119 Header
= (ANDROID_BOOTIMG_HEADER
*)BootImg
;
121 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
122 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
123 return EFI_INVALID_PARAMETER
;
126 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
128 *RamdiskSize
= Header
->RamdiskSize
;
130 if (Header
->RamdiskSize
!= 0) {
131 *Ramdisk
= (VOID
*)((INTN
)BootImg
133 + ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
));
139 AndroidBootImgGetSecondBootLoaderInfo (
142 OUT UINTN
*SecondSize
145 ANDROID_BOOTIMG_HEADER
*Header
;
147 Header
= (ANDROID_BOOTIMG_HEADER
*)BootImg
;
149 if (AsciiStrnCmp ((CONST CHAR8
*)Header
->BootMagic
, ANDROID_BOOT_MAGIC
,
150 ANDROID_BOOT_MAGIC_LENGTH
) != 0) {
151 return EFI_INVALID_PARAMETER
;
154 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header
->PageSize
));
156 *SecondSize
= Header
->SecondStageBootloaderSize
;
158 if (Header
->SecondStageBootloaderSize
!= 0) {
159 *Second
= (VOID
*)((UINTN
)BootImg
161 + ALIGN_VALUE (Header
->KernelSize
, Header
->PageSize
)
162 + ALIGN_VALUE (Header
->RamdiskSize
, Header
->PageSize
));
168 AndroidBootImgGetKernelArgs (
170 OUT CHAR8
*KernelArgs
173 ANDROID_BOOTIMG_HEADER
*Header
;
175 Header
= (ANDROID_BOOTIMG_HEADER
*) BootImg
;
176 AsciiStrnCpyS (KernelArgs
, ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
, Header
->KernelArgs
,
177 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
183 AndroidBootImgGetFdt (
188 UINTN SecondLoaderSize
;
191 /* Check whether FDT is located in second boot region as some vendor do so,
192 * because second loader is never used as far as I know. */
193 Status
= AndroidBootImgGetSecondBootLoaderInfo (
202 AndroidBootImgUpdateArgs (
207 CHAR8 ImageKernelArgs
[ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
];
210 // Get kernel arguments from Android boot image
211 Status
= AndroidBootImgGetKernelArgs (BootImg
, ImageKernelArgs
);
212 if (EFI_ERROR (Status
)) {
215 AsciiStrToUnicodeStrS (ImageKernelArgs
, KernelArgs
,
216 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
>> 1);
217 // Append platform kernel arguments
218 if(mAndroidBootImg
->AppendArgs
) {
219 Status
= mAndroidBootImg
->AppendArgs (KernelArgs
,
220 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
226 AndroidBootImgLocateFdt (
234 Status
= EfiGetSystemConfigurationTable (&gFdtTableGuid
, FdtBase
);
235 if (!EFI_ERROR (Status
)) {
239 Status
= AndroidBootImgGetFdt (BootImg
, FdtBase
);
240 if (EFI_ERROR (Status
)) {
243 Err
= fdt_check_header (*FdtBase
);
245 DEBUG ((DEBUG_ERROR
, "ERROR: Device Tree header not valid (Err:%d)\n",
247 return EFI_INVALID_PARAMETER
;
253 AndroidBootImgGetChosenNode (
254 IN INTN UpdatedFdtBase
259 ChosenNode
= fdt_subnode_offset ((CONST VOID
*)UpdatedFdtBase
, 0, "chosen");
260 if (ChosenNode
< 0) {
261 ChosenNode
= fdt_add_subnode((VOID
*)UpdatedFdtBase
, 0, "chosen");
262 if (ChosenNode
< 0) {
263 DEBUG ((DEBUG_ERROR
, "Fail to find fdt node chosen!\n"));
271 AndroidBootImgSetProperty64 (
272 IN INTN UpdatedFdtBase
,
274 IN CHAR8
*PropertyName
,
279 struct fdt_property
*Property
;
282 Property
= fdt_get_property_w((VOID
*)UpdatedFdtBase
, ChosenNode
,
284 if (NULL
== Property
&& Len
== -FDT_ERR_NOTFOUND
) {
285 Val
= cpu_to_fdt64(Val
);
286 Err
= fdt_appendprop ((VOID
*)UpdatedFdtBase
, ChosenNode
,
287 PropertyName
, &Val
, sizeof (UINT64
));
289 DEBUG ((DEBUG_ERROR
, "fdt_appendprop() fail: %a\n", fdt_strerror (Err
)));
290 return EFI_INVALID_PARAMETER
;
292 } else if (Property
!= NULL
) {
293 Err
= fdt_setprop_u64((VOID
*)UpdatedFdtBase
, ChosenNode
,
296 DEBUG ((DEBUG_ERROR
, "fdt_setprop_u64() fail: %a\n", fdt_strerror (Err
)));
297 return EFI_INVALID_PARAMETER
;
300 DEBUG ((DEBUG_ERROR
, "Failed to set fdt Property %a\n", PropertyName
));
301 return EFI_INVALID_PARAMETER
;
307 AndroidBootImgUpdateFdt (
310 IN VOID
*RamdiskData
,
314 INTN ChosenNode
, Err
, NewFdtSize
;
316 EFI_PHYSICAL_ADDRESS UpdatedFdtBase
, NewFdtBase
;
318 NewFdtSize
= (UINTN
)fdt_totalsize (FdtBase
)
319 + FDT_ADDITIONAL_ENTRIES_SIZE
;
320 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
,
321 EFI_SIZE_TO_PAGES (NewFdtSize
), &UpdatedFdtBase
);
322 if (EFI_ERROR (Status
)) {
323 DEBUG ((DEBUG_WARN
, "Warning: Failed to reallocate FDT, err %d.\n",
328 // Load the Original FDT tree into the new region
329 Err
= fdt_open_into(FdtBase
, (VOID
*)(INTN
)UpdatedFdtBase
, NewFdtSize
);
331 DEBUG ((DEBUG_ERROR
, "fdt_open_into(): %a\n", fdt_strerror (Err
)));
332 Status
= EFI_INVALID_PARAMETER
;
336 ChosenNode
= AndroidBootImgGetChosenNode(UpdatedFdtBase
);
341 Status
= AndroidBootImgSetProperty64 (UpdatedFdtBase
, ChosenNode
,
342 "linux,initrd-start",
344 if (EFI_ERROR (Status
)) {
348 Status
= AndroidBootImgSetProperty64 (UpdatedFdtBase
, ChosenNode
,
350 (UINTN
)(RamdiskData
+ RamdiskSize
));
351 if (EFI_ERROR (Status
)) {
355 if (mAndroidBootImg
->UpdateDtb
) {
356 Status
= mAndroidBootImg
->UpdateDtb (UpdatedFdtBase
, &NewFdtBase
);
357 if (EFI_ERROR (Status
)) {
361 Status
= gBS
->InstallConfigurationTable (
363 (VOID
*)(UINTN
)NewFdtBase
367 if (!EFI_ERROR (Status
)) {
372 gBS
->FreePages (UpdatedFdtBase
, EFI_SIZE_TO_PAGES (NewFdtSize
));
385 MEMORY_DEVICE_PATH KernelDevicePath
;
386 EFI_HANDLE ImageHandle
;
388 EFI_LOADED_IMAGE_PROTOCOL
*ImageInfo
;
393 Status
= gBS
->LocateProtocol (&gAndroidBootImgProtocolGuid
, NULL
,
394 (VOID
**) &mAndroidBootImg
);
395 if (EFI_ERROR (Status
)) {
399 Status
= AndroidBootImgGetKernelInfo (
404 if (EFI_ERROR (Status
)) {
408 NewKernelArg
= AllocateZeroPool (ANDROID_BOOTIMG_KERNEL_ARGS_SIZE
);
409 if (NewKernelArg
== NULL
) {
410 DEBUG ((DEBUG_ERROR
, "Fail to allocate memory\n"));
411 return EFI_OUT_OF_RESOURCES
;
414 Status
= AndroidBootImgUpdateArgs (Buffer
, NewKernelArg
);
415 if (EFI_ERROR (Status
)) {
416 FreePool (NewKernelArg
);
420 Status
= AndroidBootImgGetRamdiskInfo (
425 if (EFI_ERROR (Status
)) {
429 Status
= AndroidBootImgLocateFdt (Buffer
, &FdtBase
);
430 if (EFI_ERROR (Status
)) {
431 FreePool (NewKernelArg
);
435 Status
= AndroidBootImgUpdateFdt (Buffer
, FdtBase
, RamdiskData
, RamdiskSize
);
436 if (EFI_ERROR (Status
)) {
437 FreePool (NewKernelArg
);
441 KernelDevicePath
= mMemoryDevicePathTemplate
;
443 KernelDevicePath
.Node1
.StartingAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
) Kernel
;
444 KernelDevicePath
.Node1
.EndingAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
) Kernel
447 Status
= gBS
->LoadImage (TRUE
, gImageHandle
,
448 (EFI_DEVICE_PATH
*)&KernelDevicePath
,
449 (VOID
*)(UINTN
)Kernel
, KernelSize
, &ImageHandle
);
451 // Set kernel arguments
452 Status
= gBS
->HandleProtocol (ImageHandle
, &gEfiLoadedImageProtocolGuid
,
453 (VOID
**) &ImageInfo
);
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
);