• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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