]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Library/AndroidBootImgLib/AndroidBootImgLib.c
EmbeddedPkg/AndroidBoot: boot android kernel from storage
[mirror_edk2.git] / EmbeddedPkg / Library / AndroidBootImgLib / AndroidBootImgLib.c
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 <libfdt.h>
17 #include <Library/AndroidBootImgLib.h>
18 #include <Library/PrintLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/UefiLib.h>
21
22 #include <Protocol/AndroidBootImg.h>
23 #include <Protocol/LoadedImage.h>
24
25 #include <libfdt.h>
26
27 #define FDT_ADDITIONAL_ENTRIES_SIZE 0x400
28
29 typedef struct {
30 MEMMAP_DEVICE_PATH Node1;
31 EFI_DEVICE_PATH_PROTOCOL End;
32 } MEMORY_DEVICE_PATH;
33
34 STATIC ANDROID_BOOTIMG_PROTOCOL *mAndroidBootImg;
35
36 STATIC CONST MEMORY_DEVICE_PATH mMemoryDevicePathTemplate =
37 {
38 {
39 {
40 HARDWARE_DEVICE_PATH,
41 HW_MEMMAP_DP,
42 {
43 (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
44 (UINT8)((sizeof (MEMMAP_DEVICE_PATH)) >> 8),
45 },
46 }, // Header
47 0, // StartingAddress (set at runtime)
48 0 // EndingAddress (set at runtime)
49 }, // Node1
50 {
51 END_DEVICE_PATH_TYPE,
52 END_ENTIRE_DEVICE_PATH_SUBTYPE,
53 { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
54 } // End
55 };
56
57 EFI_STATUS
58 AndroidBootImgGetImgSize (
59 IN VOID *BootImg,
60 OUT UINTN *ImgSize
61 )
62 {
63 ANDROID_BOOTIMG_HEADER *Header;
64
65 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
66
67 if (AsciiStrnCmp ((CONST CHAR8 *)Header->BootMagic, ANDROID_BOOT_MAGIC,
68 ANDROID_BOOT_MAGIC_LENGTH) != 0) {
69 return EFI_INVALID_PARAMETER;
70 }
71
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));
74
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) +
79 Header->PageSize;
80 return EFI_SUCCESS;
81 }
82
83 EFI_STATUS
84 AndroidBootImgGetKernelInfo (
85 IN VOID *BootImg,
86 OUT VOID **Kernel,
87 OUT UINTN *KernelSize
88 )
89 {
90 ANDROID_BOOTIMG_HEADER *Header;
91
92 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
93
94 if (AsciiStrnCmp ((CONST CHAR8 *)Header->BootMagic, ANDROID_BOOT_MAGIC,
95 ANDROID_BOOT_MAGIC_LENGTH) != 0) {
96 return EFI_INVALID_PARAMETER;
97 }
98
99 if (Header->KernelSize == 0) {
100 return EFI_NOT_FOUND;
101 }
102
103 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header->PageSize));
104
105 *KernelSize = Header->KernelSize;
106 *Kernel = BootImg + Header->PageSize;
107 return EFI_SUCCESS;
108 }
109
110 EFI_STATUS
111 AndroidBootImgGetRamdiskInfo (
112 IN VOID *BootImg,
113 OUT VOID **Ramdisk,
114 OUT UINTN *RamdiskSize
115 )
116 {
117 ANDROID_BOOTIMG_HEADER *Header;
118
119 Header = (ANDROID_BOOTIMG_HEADER *)BootImg;
120
121 if (AsciiStrnCmp ((CONST CHAR8 *)Header->BootMagic, ANDROID_BOOT_MAGIC,
122 ANDROID_BOOT_MAGIC_LENGTH) != 0) {
123 return EFI_INVALID_PARAMETER;
124 }
125
126 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header->PageSize));
127
128 *RamdiskSize = Header->RamdiskSize;
129
130 if (Header->RamdiskSize != 0) {
131 *Ramdisk = (VOID *)((INTN)BootImg
132 + Header->PageSize
133 + ALIGN_VALUE (Header->KernelSize, Header->PageSize));
134 }
135 return EFI_SUCCESS;
136 }
137
138 EFI_STATUS
139 AndroidBootImgGetSecondBootLoaderInfo (
140 IN VOID *BootImg,
141 OUT VOID **Second,
142 OUT UINTN *SecondSize
143 )
144 {
145 ANDROID_BOOTIMG_HEADER *Header;
146
147 Header = (ANDROID_BOOTIMG_HEADER *)BootImg;
148
149 if (AsciiStrnCmp ((CONST CHAR8 *)Header->BootMagic, ANDROID_BOOT_MAGIC,
150 ANDROID_BOOT_MAGIC_LENGTH) != 0) {
151 return EFI_INVALID_PARAMETER;
152 }
153
154 ASSERT (IS_VALID_ANDROID_PAGE_SIZE (Header->PageSize));
155
156 *SecondSize = Header->SecondStageBootloaderSize;
157
158 if (Header->SecondStageBootloaderSize != 0) {
159 *Second = (VOID *)((UINTN)BootImg
160 + Header->PageSize
161 + ALIGN_VALUE (Header->KernelSize, Header->PageSize)
162 + ALIGN_VALUE (Header->RamdiskSize, Header->PageSize));
163 }
164 return EFI_SUCCESS;
165 }
166
167 EFI_STATUS
168 AndroidBootImgGetKernelArgs (
169 IN VOID *BootImg,
170 OUT CHAR8 *KernelArgs
171 )
172 {
173 ANDROID_BOOTIMG_HEADER *Header;
174
175 Header = (ANDROID_BOOTIMG_HEADER *) BootImg;
176 AsciiStrnCpyS (KernelArgs, ANDROID_BOOTIMG_KERNEL_ARGS_SIZE, Header->KernelArgs,
177 ANDROID_BOOTIMG_KERNEL_ARGS_SIZE);
178
179 return EFI_SUCCESS;
180 }
181
182 EFI_STATUS
183 AndroidBootImgGetFdt (
184 IN VOID *BootImg,
185 IN VOID **FdtBase
186 )
187 {
188 UINTN SecondLoaderSize;
189 EFI_STATUS Status;
190
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 (
194 BootImg,
195 FdtBase,
196 &SecondLoaderSize
197 );
198 return Status;
199 }
200
201 EFI_STATUS
202 AndroidBootImgUpdateArgs (
203 IN VOID *BootImg,
204 OUT VOID *KernelArgs
205 )
206 {
207 CHAR8 ImageKernelArgs[ANDROID_BOOTIMG_KERNEL_ARGS_SIZE];
208 EFI_STATUS Status;
209
210 // Get kernel arguments from Android boot image
211 Status = AndroidBootImgGetKernelArgs (BootImg, ImageKernelArgs);
212 if (EFI_ERROR (Status)) {
213 return Status;
214 }
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);
221 }
222 return Status;
223 }
224
225 EFI_STATUS
226 AndroidBootImgLocateFdt (
227 IN VOID *BootImg,
228 IN VOID **FdtBase
229 )
230 {
231 INTN Err;
232 EFI_STATUS Status;
233
234 Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, FdtBase);
235 if (!EFI_ERROR (Status)) {
236 return EFI_SUCCESS;
237 }
238
239 Status = AndroidBootImgGetFdt (BootImg, FdtBase);
240 if (EFI_ERROR (Status)) {
241 return Status;
242 }
243 Err = fdt_check_header (*FdtBase);
244 if (Err != 0) {
245 DEBUG ((DEBUG_ERROR, "ERROR: Device Tree header not valid (Err:%d)\n",
246 Err));
247 return EFI_INVALID_PARAMETER;
248 }
249 return EFI_SUCCESS;
250 }
251
252 INTN
253 AndroidBootImgGetChosenNode (
254 IN INTN UpdatedFdtBase
255 )
256 {
257 INTN ChosenNode;
258
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"));
264 return 0;
265 }
266 }
267 return ChosenNode;
268 }
269
270 EFI_STATUS
271 AndroidBootImgSetProperty64 (
272 IN INTN UpdatedFdtBase,
273 IN INTN ChosenNode,
274 IN CHAR8 *PropertyName,
275 IN UINT64 Val
276 )
277 {
278 INTN Err;
279 struct fdt_property *Property;
280 int Len;
281
282 Property = fdt_get_property_w((VOID *)UpdatedFdtBase, ChosenNode,
283 PropertyName, &Len);
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));
288 if (Err) {
289 DEBUG ((DEBUG_ERROR, "fdt_appendprop() fail: %a\n", fdt_strerror (Err)));
290 return EFI_INVALID_PARAMETER;
291 }
292 } else if (Property != NULL) {
293 Err = fdt_setprop_u64((VOID *)UpdatedFdtBase, ChosenNode,
294 PropertyName, Val);
295 if (Err) {
296 DEBUG ((DEBUG_ERROR, "fdt_setprop_u64() fail: %a\n", fdt_strerror (Err)));
297 return EFI_INVALID_PARAMETER;
298 }
299 } else {
300 DEBUG ((DEBUG_ERROR, "Failed to set fdt Property %a\n", PropertyName));
301 return EFI_INVALID_PARAMETER;
302 }
303 return EFI_SUCCESS;
304 }
305
306 EFI_STATUS
307 AndroidBootImgUpdateFdt (
308 IN VOID *BootImg,
309 IN VOID *FdtBase,
310 IN VOID *RamdiskData,
311 IN UINTN RamdiskSize
312 )
313 {
314 INTN ChosenNode, Err, NewFdtSize;
315 EFI_STATUS Status;
316 EFI_PHYSICAL_ADDRESS UpdatedFdtBase, NewFdtBase;
317
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",
324 Status));
325 return Status;
326 }
327
328 // Load the Original FDT tree into the new region
329 Err = fdt_open_into(FdtBase, (VOID*)(INTN)UpdatedFdtBase, NewFdtSize);
330 if (Err) {
331 DEBUG ((DEBUG_ERROR, "fdt_open_into(): %a\n", fdt_strerror (Err)));
332 Status = EFI_INVALID_PARAMETER;
333 goto Fdt_Exit;
334 }
335
336 ChosenNode = AndroidBootImgGetChosenNode(UpdatedFdtBase);
337 if (!ChosenNode) {
338 goto Fdt_Exit;
339 }
340
341 Status = AndroidBootImgSetProperty64 (UpdatedFdtBase, ChosenNode,
342 "linux,initrd-start",
343 (UINTN)RamdiskData);
344 if (EFI_ERROR (Status)) {
345 goto Fdt_Exit;
346 }
347
348 Status = AndroidBootImgSetProperty64 (UpdatedFdtBase, ChosenNode,
349 "linux,initrd-end",
350 (UINTN)(RamdiskData + RamdiskSize));
351 if (EFI_ERROR (Status)) {
352 goto Fdt_Exit;
353 }
354
355 if (mAndroidBootImg->UpdateDtb) {
356 Status = mAndroidBootImg->UpdateDtb (UpdatedFdtBase, &NewFdtBase);
357 if (EFI_ERROR (Status)) {
358 goto Fdt_Exit;
359 }
360 }
361
362 Status = gBS->InstallConfigurationTable (
363 &gFdtTableGuid,
364 (VOID *)(UINTN)NewFdtBase
365 );
366 if (!EFI_ERROR (Status)) {
367 return EFI_SUCCESS;
368 }
369
370 Fdt_Exit:
371 gBS->FreePages (UpdatedFdtBase, EFI_SIZE_TO_PAGES (NewFdtSize));
372 return Status;
373 }
374
375 EFI_STATUS
376 AndroidBootImgBoot (
377 IN VOID *Buffer,
378 IN UINTN BufferSize
379 )
380 {
381 EFI_STATUS Status;
382 VOID *Kernel;
383 UINTN KernelSize;
384 MEMORY_DEVICE_PATH KernelDevicePath;
385 EFI_HANDLE ImageHandle;
386 VOID *NewKernelArg;
387 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
388 VOID *RamdiskData;
389 UINTN RamdiskSize;
390 IN VOID *FdtBase;
391
392 Status = gBS->LocateProtocol (&gAndroidBootImgProtocolGuid, NULL,
393 (VOID **) &mAndroidBootImg);
394 if (EFI_ERROR (Status)) {
395 return Status;
396 }
397
398 Status = AndroidBootImgGetKernelInfo (
399 Buffer,
400 &Kernel,
401 &KernelSize
402 );
403 if (EFI_ERROR (Status)) {
404 return Status;
405 }
406
407 NewKernelArg = AllocateZeroPool (ANDROID_BOOTIMG_KERNEL_ARGS_SIZE);
408 if (NewKernelArg == NULL) {
409 DEBUG ((DEBUG_ERROR, "Fail to allocate memory\n"));
410 return EFI_OUT_OF_RESOURCES;
411 }
412
413 Status = AndroidBootImgUpdateArgs (Buffer, NewKernelArg);
414 if (EFI_ERROR (Status)) {
415 FreePool (NewKernelArg);
416 return Status;
417 }
418
419 Status = AndroidBootImgGetRamdiskInfo (
420 Buffer,
421 &RamdiskData,
422 &RamdiskSize
423 );
424 if (EFI_ERROR (Status)) {
425 return Status;
426 }
427
428 Status = AndroidBootImgLocateFdt (Buffer, &FdtBase);
429 if (EFI_ERROR (Status)) {
430 FreePool (NewKernelArg);
431 return Status;
432 }
433
434 Status = AndroidBootImgUpdateFdt (Buffer, FdtBase, RamdiskData, RamdiskSize);
435 if (EFI_ERROR (Status)) {
436 FreePool (NewKernelArg);
437 return Status;
438 }
439
440 KernelDevicePath = mMemoryDevicePathTemplate;
441
442 KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;
443 KernelDevicePath.Node1.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel
444 + KernelSize;
445
446 Status = gBS->LoadImage (TRUE, gImageHandle,
447 (EFI_DEVICE_PATH *)&KernelDevicePath,
448 (VOID*)(UINTN)Kernel, KernelSize, &ImageHandle);
449
450 // Set kernel arguments
451 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid,
452 (VOID **) &ImageInfo);
453 ImageInfo->LoadOptions = NewKernelArg;
454 ImageInfo->LoadOptionsSize = StrLen (NewKernelArg) * sizeof (CHAR16);
455
456 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
457 gBS->SetWatchdogTimer (5 * 60, 0x10000, 0, NULL);
458 // Start the image
459 Status = gBS->StartImage (ImageHandle, NULL, NULL);
460 // Clear the Watchdog Timer if the image returns
461 gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);
462 return EFI_SUCCESS;
463 }