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