]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Library/AndroidBootImgLib/AndroidBootImgLib.c
2e50cedf6a445bd9cb8dee837e1c82908e6a4714
[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 Status = gBS->InstallConfigurationTable (
362 &gFdtTableGuid,
363 (VOID *)(UINTN)NewFdtBase
364 );
365 }
366
367 if (!EFI_ERROR (Status)) {
368 return EFI_SUCCESS;
369 }
370
371 Fdt_Exit:
372 gBS->FreePages (UpdatedFdtBase, EFI_SIZE_TO_PAGES (NewFdtSize));
373 return Status;
374 }
375
376 EFI_STATUS
377 AndroidBootImgBoot (
378 IN VOID *Buffer,
379 IN UINTN BufferSize
380 )
381 {
382 EFI_STATUS Status;
383 VOID *Kernel;
384 UINTN KernelSize;
385 MEMORY_DEVICE_PATH KernelDevicePath;
386 EFI_HANDLE ImageHandle;
387 VOID *NewKernelArg;
388 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
389 VOID *RamdiskData;
390 UINTN RamdiskSize;
391 IN VOID *FdtBase;
392
393 Status = gBS->LocateProtocol (&gAndroidBootImgProtocolGuid, NULL,
394 (VOID **) &mAndroidBootImg);
395 if (EFI_ERROR (Status)) {
396 return Status;
397 }
398
399 Status = AndroidBootImgGetKernelInfo (
400 Buffer,
401 &Kernel,
402 &KernelSize
403 );
404 if (EFI_ERROR (Status)) {
405 return Status;
406 }
407
408 NewKernelArg = AllocateZeroPool (ANDROID_BOOTIMG_KERNEL_ARGS_SIZE);
409 if (NewKernelArg == NULL) {
410 DEBUG ((DEBUG_ERROR, "Fail to allocate memory\n"));
411 return EFI_OUT_OF_RESOURCES;
412 }
413
414 Status = AndroidBootImgUpdateArgs (Buffer, NewKernelArg);
415 if (EFI_ERROR (Status)) {
416 FreePool (NewKernelArg);
417 return Status;
418 }
419
420 Status = AndroidBootImgGetRamdiskInfo (
421 Buffer,
422 &RamdiskData,
423 &RamdiskSize
424 );
425 if (EFI_ERROR (Status)) {
426 return Status;
427 }
428
429 Status = AndroidBootImgLocateFdt (Buffer, &FdtBase);
430 if (EFI_ERROR (Status)) {
431 FreePool (NewKernelArg);
432 return Status;
433 }
434
435 Status = AndroidBootImgUpdateFdt (Buffer, FdtBase, RamdiskData, RamdiskSize);
436 if (EFI_ERROR (Status)) {
437 FreePool (NewKernelArg);
438 return Status;
439 }
440
441 KernelDevicePath = mMemoryDevicePathTemplate;
442
443 KernelDevicePath.Node1.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel;
444 KernelDevicePath.Node1.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) Kernel
445 + KernelSize;
446
447 Status = gBS->LoadImage (TRUE, gImageHandle,
448 (EFI_DEVICE_PATH *)&KernelDevicePath,
449 (VOID*)(UINTN)Kernel, KernelSize, &ImageHandle);
450
451 // Set kernel arguments
452 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid,
453 (VOID **) &ImageInfo);
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 return EFI_SUCCESS;
464 }