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