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