1 /** @file
2
3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
4 Copyright (c) 2017, Linaro. All rights reserved.
5
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
10
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.
13
14 **/
15
16 #include <Library/AbootimgLib.h>
17 #include <Library/DevicePathLib.h>
18 #include <Library/PrintLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/UefiLib.h>
21 #include <Library/ZLib.h>
22
23 #include <Protocol/Abootimg.h>
24 #include <Protocol/BlockIo.h>
25 #include <Protocol/DevicePathFromText.h>
26 #include <Protocol/LoadedImage.h>
27
28 #include <libfdt.h>
29
30 // Check Val (unsigned) is a power of 2 (has only one bit set)
31 #define IS_POWER_OF_2(Val) (Val != 0 && ((Val & (Val - 1)) == 0))
32
33 // Offset in Kernel Image
34 #define KERNEL_SIZE_OFFSET 0x10
35 #define KERNEL_MAGIC_OFFSET 0x38
36 #define KERNEL_MAGIC "ARMd"
37
38 #define BOOTIMG_HEADER_BLOCKS 1
39 #define FDTIMG_HEADER_BLOCKS 1
40
41 #define DEFAULT_UNCOMPRESS_BUFFER_SIZE (32 * 1024 * 1024)
42
43 #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))
44
45 typedef struct {
46 MEMMAP_DEVICE_PATH Node1;
47 EFI_DEVICE_PATH_PROTOCOL End;
48 } MEMORY_DEVICE_PATH;
49
50 STATIC ABOOTIMG_PROTOCOL *mAbootimg;
51
52 STATIC CONST MEMORY_DEVICE_PATH MemoryDevicePathTemplate =
53 {
54 {
55 {
56 HARDWARE_DEVICE_PATH,
57 HW_MEMMAP_DP,
58 {
59 (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
60 (UINT8)((sizeof (MEMMAP_DEVICE_PATH)) >> 8),
61 },
62 }, // Header
63 0, // StartingAddress (set at runtime)
64 0 // EndingAddress (set at runtime)
65 }, // Node1
66 {
67 END_DEVICE_PATH_TYPE,
68 END_ENTIRE_DEVICE_PATH_SUBTYPE,
69 { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
70 } // End
71 };
72
73 STATIC
74 EFI_STATUS
CheckKernelImageHeader(IN VOID * Kernel)75 CheckKernelImageHeader (
76 IN VOID *Kernel
77 )
78 {
79 if (Kernel == NULL) {
80 return EFI_INVALID_PARAMETER;
81 }
82 /* Check magic number of uncompressed Kernel Image */
83 if (AsciiStrnCmp ((VOID *)((UINTN)Kernel + KERNEL_MAGIC_OFFSET), KERNEL_MAGIC, 4) == 0) {
84 return EFI_SUCCESS;
85 }
86 return EFI_INVALID_PARAMETER;
87 }
88
89 STATIC
90 EFI_STATUS
UncompressKernel(IN VOID * Source,IN UINTN * SourceSize,OUT VOID ** Kernel,OUT UINTN * KernelSize)91 UncompressKernel (
92 IN VOID *Source,
93 IN UINTN *SourceSize,
94 OUT VOID **Kernel,
95 OUT UINTN *KernelSize
96 )
97 {
98 EFI_STATUS Status;
99 EFI_PHYSICAL_ADDRESS Address;
100 INTN err;
101
102 Status = gBS->AllocatePages (
103 AllocateAnyPages, EfiBootServicesData,
104 EFI_SIZE_TO_PAGES (DEFAULT_UNCOMPRESS_BUFFER_SIZE),
105 &Address
106 );
107 if (EFI_ERROR (Status)) {
108 return Status;
109 }
110 *Kernel = (VOID *)(UINTN)Address;
111 *KernelSize = DEFAULT_UNCOMPRESS_BUFFER_SIZE;
112 err = GzipDecompress (Source, SourceSize, *Kernel, KernelSize);
113 if (err) {
114 return EFI_INVALID_PARAMETER;
115 }
116 return EFI_SUCCESS;
117 }
118
119 STATIC
120 EFI_STATUS
GetImgSize(IN VOID * BootImg,OUT UINTN * ImgSize)121 GetImgSize (
122 IN VOID *BootImg,
123 OUT UINTN *ImgSize
124 )
125 {
126 ANDROID_BOOTIMG_HEADER *Header;
127
128 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
129
130 if (AsciiStrnCmp (Header->BootMagic, BOOT_MAGIC, BOOT_MAGIC_LENGTH) != 0) {
131 return EFI_INVALID_PARAMETER;
132 }
133
134 ASSERT (IS_POWER_OF_2 (Header->PageSize));
135
136 /* Get real size of abootimg */
137 *ImgSize = ALIGN_VALUE (Header->KernelSize, Header->PageSize) +
138 ALIGN_VALUE (Header->RamdiskSize, Header->PageSize) +
139 ALIGN_VALUE (Header->SecondStageBootloaderSize, Header->PageSize) +
140 Header->PageSize;
141 return EFI_SUCCESS;
142 }
143
144 EFI_STATUS
AbootimgGetKernelInfo(IN VOID * BootImg,OUT VOID ** Kernel,OUT UINTN * KernelSize)145 AbootimgGetKernelInfo (
146 IN VOID *BootImg,
147 OUT VOID **Kernel,
148 OUT UINTN *KernelSize
149 )
150 {
151 ANDROID_BOOTIMG_HEADER *Header;
152
153 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
154
155 if (AsciiStrnCmp (Header->BootMagic, BOOT_MAGIC, BOOT_MAGIC_LENGTH) != 0) {
156 return EFI_INVALID_PARAMETER;
157 }
158
159 if (Header->KernelSize == 0) {
160 return EFI_NOT_FOUND;
161 }
162
163 ASSERT (IS_POWER_OF_2 (Header->PageSize));
164
165 *KernelSize = Header->KernelSize;
166 *Kernel = BootImg + Header->PageSize;
167 return EFI_SUCCESS;
168 }
169
170 EFI_STATUS
GetRamdiskInfo(IN VOID * BootImg,OUT VOID ** Ramdisk,OUT UINTN * RamdiskSize)171 GetRamdiskInfo (
172 IN VOID *BootImg,
173 OUT VOID **Ramdisk,
174 OUT UINTN *RamdiskSize
175 )
176 {
177 ANDROID_BOOTIMG_HEADER *Header;
178 UINT8 *BootImgBytePtr;
179
180 // Cast to UINT8 so we can do pointer arithmetic
181 BootImgBytePtr = (UINT8 *) BootImg;
182
183 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
184
185 if (AsciiStrnCmp (Header->BootMagic, BOOT_MAGIC, BOOT_MAGIC_LENGTH) != 0) {
186 return EFI_INVALID_PARAMETER;
187 }
188
189 ASSERT (IS_POWER_OF_2 (Header->PageSize));
190
191 *RamdiskSize = Header->RamdiskSize;
192
193 if (Header->RamdiskSize != 0) {
194 *Ramdisk = (VOID *) (BootImgBytePtr + Header->PageSize +
195 ALIGN_VALUE (Header->KernelSize, Header->PageSize));
196 }
197 return EFI_SUCCESS;
198 }
199
200 EFI_STATUS
GetKernelArgs(IN VOID * BootImg,OUT CHAR8 * KernelArgs)201 GetKernelArgs (
202 IN VOID *BootImg,
203 OUT CHAR8 *KernelArgs
204 )
205 {
206 ANDROID_BOOTIMG_HEADER *Header;
207
208 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
209 AsciiStrnCpyS (KernelArgs, BOOTIMG_KERNEL_ARGS_SIZE, Header->KernelArgs,
210 BOOTIMG_KERNEL_ARGS_SIZE);
211
212 return EFI_SUCCESS;
213 }
214
215 STATIC
216 EFI_STATUS
GetAttachedFdt(IN VOID * Kernel,OUT VOID ** Fdt)217 GetAttachedFdt (
218 IN VOID *Kernel,
219 OUT VOID **Fdt
220 )
221 {
222 UINTN RawKernelSize;
223 INTN err;
224
225 err = fdt_check_header ((VOID*)(UINTN)*Fdt);
226 if (err == 0) {
227 return EFI_SUCCESS;
228 }
229
230 // Get real kernel size.
231 RawKernelSize = *(UINT32 *)((EFI_PHYSICAL_ADDRESS)(UINTN)Kernel + KERNEL_IMAGE_STEXT_OFFSET) +
232 *(UINT32 *)((EFI_PHYSICAL_ADDRESS)(UINTN)Kernel + KERNEL_IMAGE_RAW_SIZE_OFFSET);
233
234 /* FDT is at the end of kernel image */
235 *Fdt = (VOID *)((EFI_PHYSICAL_ADDRESS)(UINTN)Kernel + RawKernelSize);
236
237 //
238 // Sanity checks on the FDT blob.
239 //
240 err = fdt_check_header ((VOID*)(UINTN)*Fdt);
241 if (err != 0) {
242 Print (L"ERROR: Device Tree header not valid (err:%d)\n", err);
243 return EFI_INVALID_PARAMETER;
244 }
245 return EFI_SUCCESS;
246 }
247
248 STATIC
249 EFI_STATUS
AllocateRamdisk(IN VOID * BootImg,IN OUT VOID * KernelArgs)250 AllocateRamdisk (
251 IN VOID *BootImg,
252 IN OUT VOID *KernelArgs
253 )
254 {
255 EFI_STATUS Status;
256 ANDROID_BOOTIMG_HEADER *Header;
257 EFI_PHYSICAL_ADDRESS Address;
258 UINT8 *BootImgBytePtr;
259 VOID *Source;
260
261 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
262 // Cast to UINT8 so we can do pointer arithmetic
263 BootImgBytePtr = (UINT8 *) BootImg;
264
265 if (AsciiStrnCmp (Header->BootMagic, BOOT_MAGIC, BOOT_MAGIC_LENGTH) != 0) {
266 return EFI_INVALID_PARAMETER;
267 }
268
269 ASSERT (IS_POWER_OF_2 (Header->PageSize));
270
271 Status = EFI_SUCCESS;
272 if (Header->RamdiskAddress && Header->RamdiskSize) {
273 Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Header->RamdiskAddress;
274 Status = gBS->AllocatePages (
275 AllocateAddress, EfiBootServicesData,
276 EFI_SIZE_TO_PAGES (Header->RamdiskSize), &Address);
277 if (EFI_ERROR (Status)) {
278 return Status;
279 }
280 Source = (VOID *) (BootImgBytePtr + Header->PageSize +
281 ALIGN_VALUE (Header->KernelSize, Header->PageSize));
282 CopyMem ((VOID *)(UINTN)Address, Source, Header->RamdiskSize);
283 // Set the ramdisk in command line arguments
284 if (KernelArgs != NULL) {
285 UnicodeSPrint (
286 (CHAR16 *)KernelArgs + StrLen (KernelArgs), BOOTIMG_KERNEL_ARGS_SIZE,
287 L" initrd=0x%x,0x%x",
288 (UINTN)Address, Header->RamdiskSize
289 );
290 }
291 }
292 return Status;
293 }
294
295 STATIC
296 EFI_STATUS
InstallFdt(IN VOID * BootImg,IN EFI_PHYSICAL_ADDRESS FdtBase,IN OUT VOID * KernelArgs)297 InstallFdt (
298 IN VOID *BootImg,
299 IN EFI_PHYSICAL_ADDRESS FdtBase,
300 IN OUT VOID *KernelArgs
301 )
302 {
303 CHAR8 ImgKernelArgs[BOOTIMG_KERNEL_ARGS_SIZE];
304 INTN err;
305 EFI_STATUS Status;
306 EFI_PHYSICAL_ADDRESS NewFdtBase;
307
308 Status = gBS->LocateProtocol (&gAbootimgProtocolGuid, NULL, (VOID **) &mAbootimg);
309 if (EFI_ERROR (Status)) {
310 return Status;
311 }
312
313 Status = GetKernelArgs (
314 BootImg,
315 ImgKernelArgs
316 );
317 if (EFI_ERROR (Status)) {
318 return Status;
319 }
320
321 if (ImgKernelArgs != NULL) {
322 // Get kernel arguments from Android boot image
323 AsciiStrToUnicodeStrS (ImgKernelArgs, KernelArgs, BOOTIMG_KERNEL_ARGS_SIZE);
324 // Append platform kernel arguments
325 Status = mAbootimg->AppendArgs (KernelArgs, BOOTIMG_KERNEL_ARGS_SIZE);
326 if (EFI_ERROR (Status)) {
327 return Status;
328 }
329 }
330 Status = AllocateRamdisk (BootImg, KernelArgs);
331 if (EFI_ERROR (Status)) {
332 return Status;
333 }
334
335 Status = mAbootimg->UpdateDtb (FdtBase, &NewFdtBase);
336 if (EFI_ERROR (Status)) {
337 return Status;
338 }
339
340 //
341 // Sanity checks on the new FDT blob.
342 //
343 err = fdt_check_header ((VOID*)(UINTN)NewFdtBase);
344 if (err != 0) {
345 Print (L"ERROR: Device Tree header not valid (err:%d)\n", err);
346 return EFI_INVALID_PARAMETER;
347 }
348
349 Status = gBS->InstallConfigurationTable (
350 &gFdtTableGuid,
351 (VOID *)(UINTN)NewFdtBase
352 );
353 return Status;
354 }
355
356 STATIC
357 EFI_STATUS
GetPartition(IN CHAR16 * PathStr,OUT EFI_BLOCK_IO_PROTOCOL ** BlockIo)358 GetPartition (
359 IN CHAR16 *PathStr,
360 OUT EFI_BLOCK_IO_PROTOCOL **BlockIo
361 )
362 {
363 EFI_STATUS Status;
364 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
365 EFI_DEVICE_PATH *DevicePath;
366 EFI_DEVICE_PATH_PROTOCOL *Node, *NextNode;
367 EFI_HANDLE Handle;
368
369 if (PathStr == NULL) {
370 return EFI_INVALID_PARAMETER;
371 }
372 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
373 if (EFI_ERROR (Status)) {
374 return Status;
375 }
376 DevicePath = (EFI_DEVICE_PATH *)EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (PathStr);
377 if (DevicePath == NULL) {
378 return EFI_INVALID_PARAMETER;
379 }
380
381 /* Find DevicePath node of Partition */
382 NextNode = DevicePath;
383 while (1) {
384 Node = NextNode;
385 if (IS_DEVICE_PATH_NODE (Node, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP)) {
386 break;
387 }
388 NextNode = NextDevicePathNode (Node);
389 }
390
391 Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &DevicePath, &Handle);
392 if (EFI_ERROR (Status)) {
393 return Status;
394 }
395
396 Status = gBS->OpenProtocol (
397 Handle,
398 &gEfiBlockIoProtocolGuid,
399 (VOID **) BlockIo,
400 gImageHandle,
401 NULL,
402 EFI_OPEN_PROTOCOL_GET_PROTOCOL
403 );
404 if (EFI_ERROR (Status)) {
405 DEBUG ((EFI_D_ERROR, "Failed to get BlockIo: %r\n", Status));
406 return Status;
407 }
408
409 return Status;
410 }
411
412 STATIC
413 EFI_STATUS
LoadBootImage(IN CHAR16 * BootPathStr,OUT VOID ** Buffer,OUT UINTN * BufferSize)414 LoadBootImage (
415 IN CHAR16 *BootPathStr,
416 OUT VOID **Buffer,
417 OUT UINTN *BufferSize
418 )
419 {
420 EFI_STATUS Status;
421 EFI_BLOCK_IO_PROTOCOL *BlockIo;
422 UINTN BootImageSize = 0;
423 EFI_PHYSICAL_ADDRESS Address;
424
425 if ((Buffer == NULL) || (BufferSize == NULL)) {
426 return EFI_INVALID_PARAMETER;
427 }
428 // Read boot device to get kernel
429 Status = GetPartition (BootPathStr, &BlockIo);
430 if (EFI_ERROR (Status)) {
431 return Status;
432 }
433 // Read both image header and kernel header
434 Status = gBS->AllocatePages (
435 AllocateAnyPages, EfiBootServicesData,
436 EFI_SIZE_TO_PAGES (BlockIo->Media->BlockSize * BOOTIMG_HEADER_BLOCKS),
437 &Address
438 );
439 if (EFI_ERROR (Status)) {
440 return Status;
441 }
442 *Buffer = (VOID *)(UINTN)Address;
443 Status = BlockIo->ReadBlocks (
444 BlockIo,
445 BlockIo->Media->MediaId,
446 0,
447 BlockIo->Media->BlockSize * BOOTIMG_HEADER_BLOCKS,
448 *Buffer
449 );
450 if (EFI_ERROR (Status)) {
451 return Status;
452 }
453
454 // Get the real size of boot image
455 Status = GetImgSize (*Buffer, &BootImageSize);
456 if (EFI_ERROR (Status)) {
457 DEBUG ((DEBUG_ERROR, "Failed to get Abootimg Size: %r\n", Status));
458 return Status;
459 }
460 gBS->FreePages (
461 (EFI_PHYSICAL_ADDRESS)(UINTN)*Buffer,
462 EFI_SIZE_TO_PAGES (BlockIo->Media->BlockSize * BOOTIMG_HEADER_BLOCKS)
463 );
464 BootImageSize = ALIGN_VALUE (BootImageSize, BlockIo->Media->BlockSize);
465
466 /* Both PartitionStart and PartitionSize are counted as block size. */
467 Status = gBS->AllocatePages (
468 AllocateAnyPages, EfiBootServicesData,
469 EFI_SIZE_TO_PAGES (BootImageSize), &Address
470 );
471 if (EFI_ERROR (Status)) {
472 return Status;
473 }
474
475 *Buffer = (VOID *)(UINTN)Address;
476 /* Load the full boot.img */
477 Status = BlockIo->ReadBlocks (
478 BlockIo,
479 BlockIo->Media->MediaId,
480 0,
481 BootImageSize,
482 *Buffer
483 );
484 if (EFI_ERROR (Status)) {
485 DEBUG ((EFI_D_ERROR, "Failed to read blocks: %r\n", Status));
486 return Status;
487 }
488 return Status;
489 }
490
491 STATIC
492 EFI_STATUS
LoadFdtFromBootImage(IN VOID * BootImage,IN OUT VOID ** Fdt)493 LoadFdtFromBootImage (
494 IN VOID *BootImage,
495 IN OUT VOID **Fdt
496 )
497 {
498 EFI_STATUS Status;
499 VOID *CompressedKernel;
500 VOID *StoredKernel;
501 UINTN CompressedKernelSize;
502 UINTN StoredKernelSize;
503
504 // Get kernel size
505 Status = AbootimgGetKernelInfo (BootImage, &StoredKernel, &StoredKernelSize);
506 if (EFI_ERROR (Status)) {
507 DEBUG ((DEBUG_ERROR, "Failed to get kernel information from stored Android Boot Image: %r\n", Status));
508 return Status;
509 }
510 // Check whether it's raw kernel
511 Status = CheckKernelImageHeader (StoredKernel);
512 if (EFI_ERROR (Status)) {
513 DEBUG ((DEBUG_ERROR, "Stored kernel image is not raw format: %r\n", Status));
514 CompressedKernel = StoredKernel;
515 CompressedKernelSize = StoredKernelSize;
516 Status = UncompressKernel (
517 CompressedKernel,
518 &CompressedKernelSize,
519 &StoredKernel,
520 &StoredKernelSize
521 );
522 if (EFI_ERROR (Status)) {
523 DEBUG ((DEBUG_ERROR, "Failed to uncompress stored kernel with gzip format: %r\n", Status));
524 return Status;
525 }
526 // Get the FDT that attached at the end of gzip kernel
527 *Fdt = CompressedKernel + CompressedKernelSize;
528 }
529 // Verify & get the FDT that attached at the end of raw kernel or gzip kernel
530 Status = GetAttachedFdt (StoredKernel, Fdt);
531 if (EFI_ERROR (Status)) {
532 DEBUG ((DEBUG_ERROR, "Failed to get attached FDT from the end of stored kernel: %r\n", Status));
533 return Status;
534 }
535 return Status;
536 }
537
538 /*
539 * Boot from RAM
540 */
541 STATIC
542 EFI_STATUS
BootFromRam(IN VOID * Buffer,IN CHAR16 * BootPathStr,IN CHAR16 * FdtPathStr,IN OUT VOID * KernelArgs,OUT VOID ** Kernel,OUT UINTN * KernelSize)543 BootFromRam (
544 IN VOID *Buffer,
545 IN CHAR16 *BootPathStr,
546 IN CHAR16 *FdtPathStr,
547 IN OUT VOID *KernelArgs,
548 OUT VOID **Kernel,
549 OUT UINTN *KernelSize
550 )
551 {
552 EFI_STATUS Status;
553 VOID *BootImage = NULL;
554 VOID *CompressedKernel;
555 VOID *Fdt;
556 VOID *Ramdisk;
557 UINTN BootImageSize;
558 UINTN CompressedKernelSize;
559 UINTN RamdiskSize;
560
561 Status = AbootimgGetKernelInfo (Buffer, Kernel, KernelSize);
562 if (EFI_ERROR (Status)) {
563 DEBUG ((DEBUG_ERROR, "Failed to get kernel information from Android Boot Image: %r\n", Status));
564 return Status;
565 }
566 Status = CheckKernelImageHeader (*Kernel);
567 if (EFI_ERROR (Status)) {
568 DEBUG ((DEBUG_ERROR, "The kernel is not raw format.\n"));
569 CompressedKernel = *Kernel;
570 CompressedKernelSize = *KernelSize;
571 Status = UncompressKernel (
572 CompressedKernel,
573 &CompressedKernelSize,
574 Kernel,
575 KernelSize
576 );
577 if (EFI_ERROR (Status)) {
578 DEBUG ((DEBUG_ERROR, "Failed to uncompress kernel with gzip format: %r\n", Status));
579 return Status;
580 }
581 // Get the FDT that attached at the end of gzip kernel
582 Fdt = CompressedKernel + CompressedKernelSize;
583 }
584 // Verify & get the FDT that attached at the end of raw kernel or gzip kernel
585 Status = GetAttachedFdt (*Kernel, &Fdt);
586 if (EFI_ERROR (Status)) {
587 DEBUG ((DEBUG_ERROR, "Failed to load FDT from gzip kernel\n"));
588 // Get the FDT from the boot image in partition
589 if (BootImage == NULL) {
590 Status = LoadBootImage (BootPathStr, &BootImage, &BootImageSize);
591 if (EFI_ERROR (Status)) {
592 DEBUG ((DEBUG_ERROR, "Failed to load boot image from partition: %r\n", Status));
593 return Status;
594 }
595 }
596 Status = LoadFdtFromBootImage (BootImage, &Fdt);
597 if (EFI_ERROR (Status)) {
598 return Status;
599 }
600 }
601 // Get ramdisk from boot image in RAM
602 Status = GetRamdiskInfo (Buffer, &Ramdisk, &RamdiskSize);
603 if (RamdiskSize == 0) {
604 if (BootImage == NULL) {
605 Status = LoadBootImage (BootPathStr, &BootImage, &BootImageSize);
606 if (EFI_ERROR (Status)) {
607 DEBUG ((DEBUG_ERROR, "Failed to load boot image from partition: %r\n", Status));
608 return Status;
609 }
610 }
611 // Get ramdisk from boot image in partition
612 Status = GetRamdiskInfo (BootImage, &Ramdisk, &RamdiskSize);
613 if (EFI_ERROR (Status)) {
614 DEBUG ((DEBUG_ERROR, "Failed to get ramdisk from boot image: %r\n", Status));
615 return Status;
616 }
617 Status = InstallFdt (BootImage, (UINTN)Fdt, KernelArgs);
618 if (EFI_ERROR (Status)) {
619 DEBUG ((DEBUG_ERROR, "Failed to install FDT: %r\n", Status));
620 return Status;
621 }
622 } else {
623 Status = InstallFdt (Buffer, (UINTN)Fdt, KernelArgs);
624 if (EFI_ERROR (Status)) {
625 DEBUG ((DEBUG_ERROR, "Failed to install FDT: %r\n", Status));
626 return Status;
627 }
628 }
629 return EFI_SUCCESS;
630 }
631
632 /*
633 * Boot from partition
634 */
635 STATIC
636 EFI_STATUS
BootFromPartition(IN CHAR16 * BootPathStr,IN CHAR16 * FdtPathStr,IN OUT VOID * KernelArgs,OUT VOID ** Kernel,OUT UINTN * KernelSize)637 BootFromPartition (
638 IN CHAR16 *BootPathStr,
639 IN CHAR16 *FdtPathStr,
640 IN OUT VOID *KernelArgs,
641 OUT VOID **Kernel,
642 OUT UINTN *KernelSize
643 )
644 {
645 EFI_STATUS Status;
646 VOID *BootImage;
647 VOID *CompressedKernel;
648 VOID *Fdt;
649 VOID *Ramdisk;
650 UINTN BootImageSize;
651 UINTN CompressedKernelSize;
652 UINTN RamdiskSize;
653
654 Status = LoadBootImage (BootPathStr, &BootImage, &BootImageSize);
655 if (EFI_ERROR (Status)) {
656 DEBUG ((DEBUG_ERROR, "Failed to load boot image from boot partition: %r\n", Status));
657 return Status;
658 }
659 Status = AbootimgGetKernelInfo (BootImage, Kernel, KernelSize);
660 if (EFI_ERROR (Status)) {
661 DEBUG ((DEBUG_ERROR, "Failed to get kernel information from Android Boot Image: %r\n", Status));
662 return Status;
663 }
664 Status = CheckKernelImageHeader (*Kernel);
665 if (EFI_ERROR (Status)) {
666 DEBUG ((DEBUG_ERROR, "The kernel image is not raw format: %r\n", Status));
667 CompressedKernel = *Kernel;
668 CompressedKernelSize = *KernelSize;
669 Status = UncompressKernel (
670 CompressedKernel,
671 &CompressedKernelSize,
672 Kernel,
673 KernelSize
674 );
675 if (EFI_ERROR (Status)) {
676 DEBUG ((DEBUG_ERROR, "Failed to uncompress kernel with gzip format: %r\n", Status));
677 return Status;
678 }
679 // gzip kernel with attached FDT
680 Fdt = CompressedKernel + CompressedKernelSize;
681 }
682 // Get FDT from the end of kernel in boot image
683 Status = GetAttachedFdt (*Kernel, &Fdt);
684 if (EFI_ERROR (Status)) {
685 DEBUG ((DEBUG_ERROR, "Failed to get attached FDT from the end of raw kernel: %r\n", Status));
686 return Status;
687 }
688 // Get ramdisk from boot image
689 Status = GetRamdiskInfo (BootImage, &Ramdisk, &RamdiskSize);
690 if (EFI_ERROR (Status)) {
691 DEBUG ((DEBUG_ERROR, "Failed to get ramdisk from boot image: %r\n", Status));
692 return Status;
693 }
694 Status = InstallFdt (BootImage, (UINTN)Fdt, KernelArgs);
695 if (EFI_ERROR (Status)) {
696 DEBUG ((DEBUG_ERROR, "Failed to install FDT: %r\n", Status));
697 return Status;
698 }
699 return EFI_SUCCESS;
700 }
701
702 EFI_STATUS
AbootimgBootRam(IN VOID * Buffer,IN UINTN BufferSize,IN CHAR16 * BootPathStr,IN CHAR16 * FdtPathStr)703 AbootimgBootRam (
704 IN VOID *Buffer,
705 IN UINTN BufferSize,
706 IN CHAR16 *BootPathStr,
707 IN CHAR16 *FdtPathStr
708 )
709 {
710 EFI_STATUS Status;
711 VOID *Kernel;
712 UINTN KernelSize;
713 MEMORY_DEVICE_PATH KernelDevicePath;
714 EFI_HANDLE ImageHandle;
715 VOID *NewKernelArgs;
716 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
717 EFI_PHYSICAL_ADDRESS Address;
718
719 Status = gBS->AllocatePages (
720 AllocateAnyPages, EfiBootServicesData,
721 EFI_SIZE_TO_PAGES (BOOTIMG_KERNEL_ARGS_SIZE << 1),
722 &Address
723 );
724 if (EFI_ERROR (Status)) {
725 DEBUG ((DEBUG_ERROR, "Failed to allocate memory for kernel args: %r\n", Status));
726 return Status;
727 }
728 NewKernelArgs = (VOID *)(UINTN)Address;
729 SetMem (NewKernelArgs, EFI_SIZE_TO_PAGES (BOOTIMG_KERNEL_ARGS_SIZE << 1), 0);
730
731 Status = BootFromRam (Buffer, BootPathStr, FdtPathStr, NewKernelArgs, &Kernel, &KernelSize);
732 if (EFI_ERROR (Status)) {
733 DEBUG ((DEBUG_ERROR, "Failed to boot from RAM: %r\n", Status));
734 return Status;
735 }
736
737 CopyMem (&KernelDevicePath, &MemoryDevicePathTemplate, sizeof (MemoryDevicePathTemplate));
738
739 // Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to
740 // appease GCC.
741 KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;
742 KernelDevicePath.Node1.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel + KernelSize;
743
744 Status = gBS->LoadImage (TRUE, gImageHandle, (EFI_DEVICE_PATH *)&KernelDevicePath, (VOID*)(UINTN)Kernel, KernelSize, &ImageHandle);
745
746 // Set kernel arguments
747 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
748 ImageInfo->LoadOptions = NewKernelArgs;
749 ImageInfo->LoadOptionsSize = StrLen (NewKernelArgs) * sizeof (CHAR16);
750
751 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
752 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
753 // Start the image
754 Status = gBS->StartImage (ImageHandle, NULL, NULL);
755 // Clear the Watchdog Timer after the image returns
756 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
757 return EFI_SUCCESS;
758 }
759
760 EFI_STATUS
AbootimgBootPartition(IN CHAR16 * BootPathStr,IN CHAR16 * FdtPathStr)761 AbootimgBootPartition (
762 IN CHAR16 *BootPathStr,
763 IN CHAR16 *FdtPathStr
764 )
765 {
766 EFI_STATUS Status;
767 VOID *Kernel;
768 UINTN KernelSize;
769 MEMORY_DEVICE_PATH KernelDevicePath;
770 EFI_HANDLE ImageHandle;
771 VOID *NewKernelArgs;
772 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
773
774 NewKernelArgs = AllocateZeroPool (BOOTIMG_KERNEL_ARGS_SIZE << 1);
775 if (NewKernelArgs == NULL) {
776 DEBUG ((DEBUG_ERROR, "Fail to allocate memory\n"));
777 return EFI_OUT_OF_RESOURCES;
778 }
779
780 Status = BootFromPartition (BootPathStr, FdtPathStr, NewKernelArgs, &Kernel, &KernelSize);
781 if (EFI_ERROR (Status)) {
782 DEBUG ((DEBUG_ERROR, "Failed to boot from partition: %r\n", Status));
783 return Status;
784 }
785
786 CopyMem (&KernelDevicePath, &MemoryDevicePathTemplate, sizeof (MemoryDevicePathTemplate));
787
788 // Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to
789 // appease GCC.
790 KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;
791 KernelDevicePath.Node1.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel + KernelSize;
792
793 Status = gBS->LoadImage (TRUE, gImageHandle, (EFI_DEVICE_PATH *)&KernelDevicePath, (VOID*)(UINTN)Kernel, KernelSize, &ImageHandle);
794
795 // Set kernel arguments
796 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
797 ImageInfo->LoadOptions = NewKernelArgs;
798 ImageInfo->LoadOptionsSize = StrLen (NewKernelArgs) * sizeof (CHAR16);
799
800 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
801 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
802 // Start the image
803 Status = gBS->StartImage (ImageHandle, NULL, NULL);
804 // Clear the Watchdog Timer after the image returns
805 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
806 return EFI_SUCCESS;
807 }
808