2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Manager
4 * FILE: boot/environ/app/efiemu.c
5 * PURPOSE: UEFI Entrypoint for Boot Manager
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
14 /* DATA STRUCTURES ***********************************************************/
16 typedef struct _BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
18 BOOT_APPLICATION_PARAMETER_BLOCK
;
19 BL_MEMORY_DATA BootMemoryData
;
20 BL_MEMORY_DESCRIPTOR MemEntry
;
22 } BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
;
24 /* DATA VARIABLES ************************************************************/
26 ULONG BlpApplicationFlags
;
28 GUID EfiLoadedImageProtocol
= EFI_LOADED_IMAGE_PROTOCOL_GUID
;
29 GUID EfiDevicePathProtocol
= EFI_DEVICE_PATH_PROTOCOL_GUID
;
31 BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH EfiInitScratch
;
33 /* FUNCTIONS *****************************************************************/
36 AhCreateLoadOptionsList (
37 _In_ PWCHAR CommandLine
,
38 _In_ PBOOT_ENTRY_OPTION BootOptions
,
39 _In_ ULONG MaximumLength
,
40 _Out_ PULONG OptionSize
,
41 _In_ PBOOT_ENTRY_OPTION
* PreviousOption
,
42 _In_ PULONG PreviousOptionSize
45 return STATUS_NOT_IMPLEMENTED
;
49 EfiInitpConvertEfiFilePath (
50 _In_ EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
52 _In_ PBOOT_ENTRY_OPTION Option
,
53 _In_ ULONG MaximumLength
56 return STATUS_NOT_IMPLEMENTED
;
60 EfiInitpConvertEfiDevicePath (
61 _In_ EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
62 _In_ ULONG DeviceType
,
63 _In_ PBOOT_ENTRY_OPTION Option
,
64 _In_ ULONG MaximumLength
67 return STATUS_NOT_IMPLEMENTED
;
71 EfiInitpCreateApplicationEntry (
72 __in EFI_SYSTEM_TABLE
*SystemTable
,
73 __in PBL_APPLICATION_ENTRY Entry
,
74 __in ULONG MaximumLength
,
75 __in EFI_DEVICE_PATH
*DevicePath
,
76 __in EFI_DEVICE_PATH
*FilePath
,
77 __in PWCHAR LoadOptions
,
78 __in ULONG LoadOptionsSize
,
80 __out PULONG ResultLength
,
81 __out PBL_DEVICE_DESCRIPTOR
*AppEntryDevice
84 PBL_WINDOWS_LOAD_OPTIONS WindowsOptions
;
85 PWCHAR ObjectString
, CommandLine
;
86 PBOOT_ENTRY_OPTION Option
, PreviousOption
;
87 ULONG HeaderSize
, TotalOptionSize
, Size
, CommandLineSize
, RemainingSize
;
89 UNICODE_STRING GuidString
;
91 PBCDE_DEVICE BcdDevice
;
92 BOOLEAN HaveBinaryOptions
, HaveGuid
;
93 PBL_FILE_PATH_DESCRIPTOR OsPath
;
94 EFI_DEVICE_PATH
*OsDevicePath
;
96 /* Initialize everything */
98 *AppEntryDevice
= NULL
;
101 /* Check if the load options are in binary Windows format */
102 WindowsOptions
= (PBL_WINDOWS_LOAD_OPTIONS
)LoadOptions
;
103 if ((WindowsOptions
!= NULL
) &&
104 (LoadOptionsSize
>= sizeof(BL_WINDOWS_LOAD_OPTIONS
)) &&
105 (WindowsOptions
->Length
>= sizeof(BL_WINDOWS_LOAD_OPTIONS
)) &&
106 !(strncmp(WindowsOptions
->Signature
, "WINDOWS", 7)))
108 /* They are, so firmware must have loaded us -- extract arguments */
109 CommandLine
= WindowsOptions
->LoadOptions
;
110 CommandLineSize
= LoadOptionsSize
- FIELD_OFFSET(BL_WINDOWS_LOAD_OPTIONS
,
113 /* Remember that we used binary options */
114 HaveBinaryOptions
= TRUE
;
118 /* Nope -- so treat them as raw command-line options */
119 CommandLine
= LoadOptions
;
120 CommandLineSize
= LoadOptionsSize
;
122 /* No binary options */
123 HaveBinaryOptions
= FALSE
;
126 /* EFI uses UTF-16LE, like NT, so convert to characters */
127 CommandLineSize
/= sizeof(WCHAR
);
128 if (CommandLineSize
!= 0)
130 /* And check if the options are not NULL-terminated */
131 if (wcsnlen(CommandLine
, CommandLineSize
) == CommandLineSize
)
133 /* NULL-terminate them */
134 CommandLine
[CommandLineSize
- 1] = UNICODE_NULL
;
138 /* Begin by making sure we at least have space for the app entry header */
139 RemainingSize
= MaximumLength
;
140 if (RemainingSize
< sizeof(BL_APPLICATION_ENTRY
))
142 Status
= STATUS_INVALID_PARAMETER
;
146 /* On exit, return that we've at least consumed this much */
147 HeaderSize
= FIELD_OFFSET(BL_APPLICATION_ENTRY
, BcdData
);
149 /* Zero out the header, and write down the signature */
150 RtlZeroMemory(Entry
, sizeof(BL_APPLICATION_ENTRY
));
151 RtlCopyMemory(Entry
->Signature
, "BTAPENT", 7);
153 /* Check if a BCD object was passed on the command-line */
154 ObjectString
= wcsstr(CommandLine
, L
"BCDOBJECT=");
155 if (ObjectString
!= NULL
)
157 /* Convert the BCD object to a GUID */
158 RtlInitUnicodeString(&GuidString
, ObjectString
+ 10);
159 RtlGUIDFromString(&GuidString
, &ObjectGuid
);
161 /* Store it in the application entry */
162 Entry
->Guid
= ObjectGuid
;
164 /* Remember one was passed */
169 /* Remember that no identifier was passed */
170 Entry
->Flags
|= BL_APPLICATION_ENTRY_FLAG_NO_GUID
;
174 /* At this point, the header is consumed, and we must now handle BCD options */
175 RemainingSize
-= FIELD_OFFSET(BL_APPLICATION_ENTRY
, BcdData
);
177 /* Convert the device path into a BCD option */
178 Status
= EfiInitpConvertEfiDevicePath(DevicePath
,
179 BcdLibraryDevice_ApplicationDevice
,
182 if (!NT_SUCCESS(Status
))
184 /* We failed, so mark the option as such and return an empty one */
185 Entry
->BcdData
.Failed
= TRUE
;
186 TotalOptionSize
= sizeof(BOOT_ENTRY_OPTION
);
190 /* Extract the device descriptor and return it */
191 BcdDevice
= (PVOID
)((ULONG_PTR
)&Entry
->BcdData
+ Entry
->BcdData
.DataOffset
);
192 *AppEntryDevice
= &BcdDevice
->Device
;
194 /* Calculate how big this option was and consume that from the buffer */
195 TotalOptionSize
= BlGetBootOptionSize(&Entry
->BcdData
);
196 RemainingSize
-= TotalOptionSize
;
198 /* Calculate where the next option should go */
199 Option
= (PVOID
)((ULONG_PTR
)&Entry
->BcdData
+ TotalOptionSize
);
201 /* Check if we're PXE booting or not */
202 if ((*AppEntryDevice
)->DeviceType
== UdpDevice
)
205 Status
= STATUS_NOT_IMPLEMENTED
;
209 /* Convert the local file path into a BCD option */
210 Status
= EfiInitpConvertEfiFilePath(FilePath
,
211 BcdLibraryString_ApplicationPath
,
216 /* Bail out on failure */
217 if (!NT_SUCCESS(Status
))
222 /* The next option is right after this one */
223 Entry
->BcdData
.NextEntryOffset
= TotalOptionSize
;
225 /* Now compute the size of the next option, and add to the rolling sum */
226 Size
= BlGetBootOptionSize(Option
);
227 TotalOptionSize
+= Size
;
229 /* Remember the previous option so we can update its next offset */
230 PreviousOption
= Option
;
232 /* Consume the option from the buffer */
233 RemainingSize
-= Size
;
235 /* Calculate where the next option should go */
236 Option
= (PVOID
)((ULONG_PTR
)Option
+ Size
);
238 /* Check if we were using binary options without a BCD GUID */
239 if ((HaveBinaryOptions
) && !(HaveGuid
))
241 /* Then this means we have to convert the OS paths to BCD too */
242 WindowsOptions
= (PBL_WINDOWS_LOAD_OPTIONS
)LoadOptions
;
243 OsPath
= (PVOID
)((ULONG_PTR
)WindowsOptions
+ WindowsOptions
->OsPathOffset
);
245 /* IS the OS path in EFI format? */
246 if ((OsPath
->Length
> FIELD_OFFSET(BL_FILE_PATH_DESCRIPTOR
, Path
)) &&
247 (OsPath
->PathType
== EfiPath
))
249 /* Convert the device portion */
250 OsDevicePath
= (EFI_DEVICE_PATH
*)OsPath
->Path
;
251 Status
= EfiInitpConvertEfiDevicePath(OsDevicePath
,
252 BcdOSLoaderDevice_OSDevice
,
255 if (!NT_SUCCESS(Status
))
260 /* Update the offset of the previous option */
261 PreviousOption
->NextEntryOffset
= (ULONG_PTR
)Option
- (ULONG_PTR
)&Entry
->BcdData
;
263 /* Now compute the size of the next option, and add to the rolling sum */
264 Size
= BlGetBootOptionSize(Option
);
265 TotalOptionSize
+= Size
;
267 /* Remember the previous option so we can update its next offset */
268 PreviousOption
= Option
;
270 /* Consume the option from the buffer */
271 RemainingSize
-= Size
;
273 /* Calculate where the next option should go */
274 Option
= (PVOID
)((ULONG_PTR
)Option
+ Size
);
276 /* Convert the path oprtion */
277 Status
= EfiInitpConvertEfiFilePath(OsDevicePath
,
278 BcdOSLoaderString_SystemRoot
,
281 if (!NT_SUCCESS(Status
))
286 /* Update the offset of the previous option */
287 PreviousOption
->NextEntryOffset
= (ULONG_PTR
)Option
- (ULONG_PTR
)&Entry
->BcdData
;
289 /* Now compute the size of the next option, and add to the rolling sum */
290 Size
= BlGetBootOptionSize(Option
);
291 TotalOptionSize
+= Size
;
293 /* Remember the previous option so we can update its next offset */
294 PreviousOption
= Option
;
296 /* Consume the option from the buffer */
297 RemainingSize
-= Size
;
299 /* Calculate where the next option should go */
300 Option
= (PVOID
)((ULONG_PTR
)Option
+ Size
);
304 /* Now convert everything else */
305 AhCreateLoadOptionsList(CommandLine
,
313 /* Return the final size */
314 *ResultLength
= HeaderSize
+ TotalOptionSize
;
318 * @name EfiInitCreateInputParametersEx
320 * The EfiInitCreateInputParametersEx routine converts UEFI entrypoint
321 * parameters to the ones expected by Windows Boot Applications
324 * UEFI Image Handle for the current loaded application.
327 * Pointer to the UEFI System Table.
329 * @return A PBOOT_APPLICATION_PARAMETER_BLOCK structure containing the data
330 * from UEFI, translated to the Boot Library-compatible format.
333 PBOOT_APPLICATION_PARAMETER_BLOCK
334 EfiInitCreateInputParametersEx (
335 _In_ EFI_HANDLE ImageHandle
,
336 _In_ EFI_SYSTEM_TABLE
*SystemTable
339 EFI_BOOT_SERVICES
* BootServices
;
340 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
341 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
342 PBL_FIRMWARE_DESCRIPTOR FirmwareData
;
343 PBL_RETURN_ARGUMENTS ReturnArguments
;
344 ULONG FirmwareOffset
, ConsumedSize
;
345 PBL_DEVICE_DESCRIPTOR AppDevice
;
348 /* Initialize the header with the signature and version */
349 EfiInitScratch
.Signature
[0] = BOOT_APPLICATION_SIGNATURE_1
;
350 EfiInitScratch
.Signature
[1] = BOOT_APPLICATION_SIGNATURE_2
;
351 EfiInitScratch
.Version
= BOOT_APPLICATION_VERSION
;
353 /* Set the image type to x86 */
354 EfiInitScratch
.ImageType
= EFI_IMAGE_MACHINE_IA32
;
356 /* Set the translation type to physical */
357 EfiInitScratch
.MemoryTranslationType
= BOOT_MEMORY_TRANSLATION_TYPE_PHYSICAL
;
359 /* Indicate that the data was converted from EFI */
360 BlpApplicationFlags
|= BL_APPLICATION_FLAG_CONVERTED_FROM_EFI
;
362 /* Grab the loaded image protocol, which has our base and size */
363 BootServices
= SystemTable
->BootServices
;
364 Status
= BootServices
->HandleProtocol(ImageHandle
,
365 &EfiLoadedImageProtocol
,
366 (VOID
**)&LoadedImage
);
367 if (Status
!= EFI_SUCCESS
)
369 SystemTable
->ConOut
->OutputString(SystemTable
->ConsoleOutHandle
,
370 L
"Loaded image failed\n");
374 /* Capture it in the boot application parameters */
375 EfiInitScratch
.ImageBase
= (ULONG_PTR
)LoadedImage
->ImageBase
;
376 EfiInitScratch
.ImageSize
= (ULONG
)LoadedImage
->ImageSize
;
378 /* Now grab our device path protocol, so we can convert the path later on */
379 Status
= BootServices
->HandleProtocol(ImageHandle
,
380 &EfiDevicePathProtocol
,
381 (VOID
**)&DevicePath
);
382 if (Status
!= EFI_SUCCESS
)
384 SystemTable
->ConOut
->OutputString(SystemTable
->ConsoleOutHandle
,
385 L
"Device path failed\n");
389 /* The built-in boot memory data comes right after our block */
390 EfiInitScratch
.MemoryDataOffset
=
391 FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
, BootMemoryData
);
393 /* Build the boot memory data structure, with 1 descriptor */
394 EfiInitScratch
.BootMemoryData
.Version
= BL_MEMORY_DATA_VERSION
;
395 EfiInitScratch
.BootMemoryData
.MdListOffset
=
396 FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
, MemEntry
) -
397 EfiInitScratch
.MemoryDataOffset
;
398 EfiInitScratch
.BootMemoryData
.DescriptorSize
= sizeof(BL_MEMORY_DESCRIPTOR
);
399 EfiInitScratch
.BootMemoryData
.DescriptorCount
= 1;
400 EfiInitScratch
.BootMemoryData
.Unknown
= 8;
402 /* Build the memory entry descriptor for this image itself */
403 EfiInitScratch
.MemEntry
.Flags
= 8;
404 EfiInitScratch
.MemEntry
.Type
= BlLoaderMemory
;
405 EfiInitScratch
.MemEntry
.BasePage
= EfiInitScratch
.ImageBase
>> PAGE_SHIFT
;
406 EfiInitScratch
.MemEntry
.PageCount
= ALIGN_UP_BY(EfiInitScratch
.ImageSize
, PAGE_SIZE
) >> PAGE_SHIFT
;
408 /* The built-in application entry comes right after the memory descriptor*/
409 EfiInitScratch
.AppEntryOffset
=
410 FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
, AppEntry
);
412 /* Go and build it */
413 EfiInitpCreateApplicationEntry(SystemTable
,
414 (PBL_APPLICATION_ENTRY
)&EfiInitScratch
.AppEntry
,
415 sizeof(EfiInitScratch
.AppEntry
),
417 LoadedImage
->FilePath
,
418 LoadedImage
->LoadOptions
,
419 LoadedImage
->LoadOptionsSize
,
420 EfiInitScratch
.MemEntry
.PageCount
,
424 /* Boot device information comes right after the application entry */
425 EfiInitScratch
.BootDeviceOffset
= ConsumedSize
+ EfiInitScratch
.AppEntryOffset
;
427 /* Check if we have a boot device */
428 if (AppDevice
!= NULL
)
430 /* We do -- copy it */
431 RtlCopyMemory(EfiInitScratch
.AppEntry
+ ConsumedSize
,
435 /* Firmware data follows right after the boot device entry */
436 FirmwareOffset
= AppDevice
->Size
+ EfiInitScratch
.BootDeviceOffset
;
440 /* We do not, so zero out the space where a full boot device structure would fit */
441 RtlZeroMemory(EfiInitScratch
.AppEntry
+ ConsumedSize
,
442 sizeof(BL_DEVICE_DESCRIPTOR
));
444 /* And start the firmware data past that */
445 FirmwareOffset
= EfiInitScratch
.BootDeviceOffset
+ sizeof(BL_DEVICE_DESCRIPTOR
);
448 /* Set the computed firmware data offset */
449 EfiInitScratch
.FirmwareParametersOffset
= FirmwareOffset
;
451 /* Fill out the firmware data that's there */
452 FirmwareData
= (PVOID
)((ULONG_PTR
)&EfiInitScratch
+ EfiInitScratch
.FirmwareParametersOffset
);
453 FirmwareData
->Version
= BL_FIRMWARE_DESCRIPTOR_VERSION
;
454 FirmwareData
->ImageHandle
= ImageHandle
;
455 FirmwareData
->SystemTable
= SystemTable
;
457 /* Finally, set the return argument offset */
458 EfiInitScratch
.ReturnArgumentsOffset
= FirmwareOffset
+ sizeof(BL_FIRMWARE_DESCRIPTOR
);
460 /* And fill out the return argument data */
461 ReturnArguments
= (PVOID
)((ULONG_PTR
)&EfiInitScratch
+ EfiInitScratch
.ReturnArgumentsOffset
);
462 ReturnArguments
->Version
= BL_RETURN_ARGUMENTS_VERSION
;
464 /* We're done, compute the final size and return the block */
465 EfiInitScratch
.Size
= EfiInitScratch
.ReturnArgumentsOffset
+ sizeof(BL_RETURN_ARGUMENTS
);
466 return (PBOOT_APPLICATION_PARAMETER_BLOCK
)&EfiInitScratch
;
472 * The EfiEntry routine implements the UEFI entrypoint for the application.
475 * UEFI Image Handle for the current loaded application.
478 * Pointer to the UEFI System Table.
480 * @return EFI_SUCCESS if the image was loaded correctly, relevant error code
486 _In_ EFI_HANDLE ImageHandle
,
487 _In_ EFI_SYSTEM_TABLE
*SystemTable
491 PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
;
493 /* Temporary debugging string */
494 SystemTable
->ConOut
->OutputString(SystemTable
->ConsoleOutHandle
, L
"Hello from EFI\n");
496 /* Convert EFI parameters to Windows Boot Application parameters */
497 BootParameters
= EfiInitCreateInputParametersEx(ImageHandle
, SystemTable
);
498 if (BootParameters
!= NULL
)
500 /* Conversion was good -- call the Boot Manager Entrypoint */
501 SystemTable
->ConOut
->OutputString(SystemTable
->ConsoleOutHandle
, L
"EFI input OK!\n");
502 Status
= BmMain(BootParameters
);
506 /* Conversion failed, bail out */
507 SystemTable
->ConOut
->OutputString(SystemTable
->ConsoleOutHandle
, L
"EFI input failed\n");
508 Status
= STATUS_INVALID_PARAMETER
;
511 /* Convert the NT status code to an EFI code */
512 return EfiGetEfiStatusCode(Status
);