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