1 /** @file
2 
3   Copyright (c) 2013-2015, ARM Ltd. All rights reserved.<BR>
4 
5   This program and the accompanying materials
6   are licensed and made available under the terms and conditions of the BSD License
7   which accompanies this distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "AndroidFastbootApp.h"
16 
17 #include <Protocol/DevicePath.h>
18 
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/BdsLib.h>
21 #include <Library/DevicePathLib.h>
22 #include <Library/PrintLib.h>
23 #include <Library/UefiBootServicesTableLib.h>
24 #include <Library/UefiRuntimeServicesTableLib.h>
25 #include <Library/UefiLib.h>
26 
27 #define LINUX_LOADER_COMMAND_LINE       L"%s -f %s -c %s"
28 
29 // This GUID is defined in the INGF file of ArmPkg/Application/LinuxLoader
30 CONST EFI_GUID mLinuxLoaderAppGuid = { 0x701f54f2, 0x0d70, 0x4b89, { 0xbc, 0x0a, 0xd9, 0xca, 0x25, 0x37, 0x90, 0x59 }};
31 
32 // Device Path representing an image in memory
33 #pragma pack(1)
34 typedef struct {
35   MEMMAP_DEVICE_PATH                      Node1;
36   EFI_DEVICE_PATH_PROTOCOL                End;
37 } MEMORY_DEVICE_PATH;
38 #pragma pack()
39 
40 STATIC CONST MEMORY_DEVICE_PATH MemoryDevicePathTemplate =
41 {
42   {
43     {
44       HARDWARE_DEVICE_PATH,
45       HW_MEMMAP_DP,
46       {
47         (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
48         (UINT8)((sizeof (MEMMAP_DEVICE_PATH)) >> 8),
49       },
50     }, // Header
51     0, // StartingAddress (set at runtime)
52     0  // EndingAddress   (set at runtime)
53   }, // Node1
54   {
55     END_DEVICE_PATH_TYPE,
56     END_ENTIRE_DEVICE_PATH_SUBTYPE,
57     { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
58   } // End
59 };
60 
61 #define KERNEL_IMAGE_STEXT_OFFSET     0x12C
62 #define KERNEL_IMAGE_RAW_SIZE_OFFSET  0x130
63 
64 EFI_STATUS
BootAndroidBootImg(IN UINTN BufferSize,IN VOID * Buffer)65 BootAndroidBootImg (
66   IN UINTN    BufferSize,
67   IN VOID    *Buffer
68   )
69 {
70   EFI_STATUS                          Status;
71   CHAR8                               KernelArgs[BOOTIMG_KERNEL_ARGS_SIZE];
72   VOID                               *Kernel;
73   UINTN                               KernelSize;
74   VOID                               *Ramdisk;
75   UINTN                               RamdiskSize;
76   MEMORY_DEVICE_PATH                  KernelDevicePath;
77   MEMORY_DEVICE_PATH*                 RamdiskDevicePath;
78   EFI_PHYSICAL_ADDRESS                FdtBase;
79   CHAR16                              UnicodeArgs[BOOTIMG_KERNEL_ARGS_SIZE];
80   CHAR16                              InitrdArgs[64];
81   BDS_LOAD_OPTION                    *BdsLoadOptions;
82   UINTN                               VariableSize;
83   CHAR16                              SerialNoArgs[40], DataUnicode[17];
84 
85   Status = ParseAndroidBootImg (
86             Buffer,
87             &Kernel,
88             &KernelSize,
89             &Ramdisk,
90             &RamdiskSize,
91             KernelArgs
92             );
93   if (EFI_ERROR (Status)) {
94     return Status;
95   }
96 
97   KernelDevicePath = MemoryDevicePathTemplate;
98 
99   KernelSize = *(UINT32 *)(Kernel + KERNEL_IMAGE_STEXT_OFFSET) +
100                *(UINT32 *)(Kernel + KERNEL_IMAGE_RAW_SIZE_OFFSET);
101   FdtBase = (EFI_PHYSICAL_ADDRESS)(UINTN)Kernel + KernelSize;
102   Status = gBS->InstallConfigurationTable (&gFdtTableGuid, (VOID *)FdtBase);
103   ASSERT_EFI_ERROR (Status);
104 
105   // Have to cast to UINTN before casting to EFI_PHYSICAL_ADDRESS in order to
106   // appease GCC.
107   KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;
108   KernelDevicePath.Node1.EndingAddress   = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel + KernelSize;
109 
110   RamdiskDevicePath = NULL;
111   if (RamdiskSize != 0) {
112     RamdiskDevicePath = (MEMORY_DEVICE_PATH*)DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL*) &MemoryDevicePathTemplate);
113 
114     RamdiskDevicePath->Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Ramdisk;
115     RamdiskDevicePath->Node1.EndingAddress   = ((EFI_PHYSICAL_ADDRESS)(UINTN) Ramdisk) + RamdiskSize;
116   }
117 
118   BdsLoadOptions = (BDS_LOAD_OPTION *)AllocateZeroPool (sizeof(BDS_LOAD_OPTION));
119   ASSERT (BdsLoadOptions != NULL);
120   BdsLoadOptions->FilePathList = &KernelDevicePath.Node1.Header;
121   ASSERT (BdsLoadOptions->FilePathList != NULL);
122   BdsLoadOptions->FilePathListLength = GetDevicePathSize (BdsLoadOptions->FilePathList);
123   BdsLoadOptions->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT;
124 
125   AsciiStrToUnicodeStr (KernelArgs, UnicodeArgs);
126   UnicodeSPrint (InitrdArgs, 64 * sizeof(CHAR16), L" initrd=0x%x,0x%x",
127 		 (EFI_PHYSICAL_ADDRESS)(UINTN) Ramdisk, RamdiskSize);
128   StrCat (UnicodeArgs, InitrdArgs);
129 
130   VariableSize = 17 * sizeof(CHAR16);
131   Status = gRT->GetVariable ((CHAR16 *)L"SerialNo", &gHiKeyVariableGuid, NULL,
132 				&VariableSize, &DataUnicode);
133   if (!EFI_ERROR (Status)) {
134     DataUnicode[(VariableSize / sizeof(CHAR16)) - 1] = '\0';
135     ZeroMem (SerialNoArgs, 40 * sizeof (CHAR16));
136     UnicodeSPrint (SerialNoArgs, 40 * sizeof(CHAR16), L" androidboot.serialno=%s", DataUnicode);
137     StrCat (UnicodeArgs, SerialNoArgs);
138   }
139 
140   BdsLoadOptions->OptionalDataSize = 512;
141   BdsLoadOptions->OptionalData = UnicodeArgs;
142   BdsLoadOptions->Description = NULL;
143 
144 
145   Status = BdsStartEfiApplication (gImageHandle, BdsLoadOptions->FilePathList, BdsLoadOptions->OptionalDataSize, BdsLoadOptions->OptionalData);
146   if (EFI_ERROR (Status)) {
147     DEBUG ((EFI_D_ERROR, "Couldn't Boot Linux: %d\n", Status));
148     return EFI_DEVICE_ERROR;
149   }
150 
151   if (RamdiskDevicePath) {
152     FreePool (RamdiskDevicePath);
153   }
154 
155   // If we got here we do a confused face because BootLinuxFdt returned,
156   // reporting success.
157   DEBUG ((EFI_D_ERROR, "WARNING: BdsBootLinuxFdt returned EFI_SUCCESS.\n"));
158   return EFI_SUCCESS;
159 }
160