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 | if (EFI_ERROR (Status)) {
|
---|
445 | //
|
---|
446 | // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
|
---|
447 | // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
|
---|
448 | // If the caller doesn't have the option to defer the execution of an image, we should
|
---|
449 | // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
|
---|
450 | //
|
---|
451 | if (Status == EFI_SECURITY_VIOLATION) {
|
---|
452 | gBS->UnloadImage (ImageHandle);
|
---|
453 | }
|
---|
454 | return Status;
|
---|
455 | }
|
---|
456 |
|
---|
457 | // Set kernel arguments
|
---|
458 | Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid,
|
---|
459 | (VOID **) &ImageInfo);
|
---|
460 | ImageInfo->LoadOptions = NewKernelArg;
|
---|
461 | ImageInfo->LoadOptionsSize = StrLen (NewKernelArg) * sizeof (CHAR16);
|
---|
462 |
|
---|
463 | // Before calling the image, enable the Watchdog Timer for the 5 Minute period
|
---|
464 | gBS->SetWatchdogTimer (5 * 60, 0x10000, 0, NULL);
|
---|
465 | // Start the image
|
---|
466 | Status = gBS->StartImage (ImageHandle, NULL, NULL);
|
---|
467 | // Clear the Watchdog Timer if the image returns
|
---|
468 | gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);
|
---|
469 | return EFI_SUCCESS;
|
---|
470 | }
|
---|