]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Library/AndroidBootImgLib/AndroidBootImgLib.c
33425060b15d7b6311de75f5351c841a58113a54
[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
353 Status = gBS->InstallConfigurationTable (
354 &gFdtTableGuid,
355 (VOID *)(UINTN)NewFdtBase
356 );
357 }
358
359 if (!EFI_ERROR (Status)) {
360 return EFI_SUCCESS;
361 }
362
363 Fdt_Exit:
364 gBS->FreePages (UpdatedFdtBase, EFI_SIZE_TO_PAGES (NewFdtSize));
365 return Status;
366 }
367
368 EFI_STATUS
369 AndroidBootImgBoot (
370 IN VOID *Buffer,
371 IN UINTN BufferSize
372 )
373 {
374 EFI_STATUS Status;
375 VOID *Kernel;
376 UINTN KernelSize;
377 MEMORY_DEVICE_PATH KernelDevicePath;
378 EFI_HANDLE ImageHandle;
379 VOID *NewKernelArg;
380 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
381 VOID *RamdiskData;
382 UINTN RamdiskSize;
383 IN VOID *FdtBase;
384
385 Status = gBS->LocateProtocol (&gAndroidBootImgProtocolGuid, NULL,
386 (VOID **) &mAndroidBootImg);
387 if (EFI_ERROR (Status)) {
388 return Status;
389 }
390
391 Status = AndroidBootImgGetKernelInfo (
392 Buffer,
393 &Kernel,
394 &KernelSize
395 );
396 if (EFI_ERROR (Status)) {
397 return Status;
398 }
399
400 NewKernelArg = AllocateZeroPool (ANDROID_BOOTIMG_KERNEL_ARGS_SIZE);
401 if (NewKernelArg == NULL) {
402 DEBUG ((DEBUG_ERROR, "Fail to allocate memory\n"));
403 return EFI_OUT_OF_RESOURCES;
404 }
405
406 Status = AndroidBootImgUpdateArgs (Buffer, NewKernelArg);
407 if (EFI_ERROR (Status)) {
408 FreePool (NewKernelArg);
409 return Status;
410 }
411
412 Status = AndroidBootImgGetRamdiskInfo (
413 Buffer,
414 &RamdiskData,
415 &RamdiskSize
416 );
417 if (EFI_ERROR (Status)) {
418 return Status;
419 }
420
421 Status = AndroidBootImgLocateFdt (Buffer, &FdtBase);
422 if (EFI_ERROR (Status)) {
423 FreePool (NewKernelArg);
424 return Status;
425 }
426
427 Status = AndroidBootImgUpdateFdt (Buffer, FdtBase, RamdiskData, RamdiskSize);
428 if (EFI_ERROR (Status)) {
429 FreePool (NewKernelArg);
430 return Status;
431 }
432
433 KernelDevicePath = mMemoryDevicePathTemplate;
434
435 KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;
436 KernelDevicePath.Node1.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel
437 + KernelSize;
438
439 Status = gBS->LoadImage (TRUE, gImageHandle,
440 (EFI_DEVICE_PATH *)&KernelDevicePath,
441 (VOID*)(UINTN)Kernel, KernelSize, &ImageHandle);
442 if (EFI_ERROR (Status)) {
443 //
444 // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
445 // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
446 // If the caller doesn't have the option to defer the execution of an image, we should
447 // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
448 //
449 if (Status == EFI_SECURITY_VIOLATION) {
450 gBS->UnloadImage (ImageHandle);
451 }
452 return Status;
453 }
454
455 // Set kernel arguments
456 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid,
457 (VOID **) &ImageInfo);
458 ImageInfo->LoadOptions = NewKernelArg;
459 ImageInfo->LoadOptionsSize = StrLen (NewKernelArg) * sizeof (CHAR16);
460
461 // Before calling the image, enable the Watchdog Timer for the 5 Minute period
462 gBS->SetWatchdogTimer (5 * 60, 0x10000, 0, NULL);
463 // Start the image
464 Status = gBS->StartImage (ImageHandle, NULL, NULL);
465 // Clear the Watchdog Timer if the image returns
466 gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);
467 return EFI_SUCCESS;
468 }