[BOOTMGFW]
[reactos.git] / reactos / boot / environ / app / bootmgr / efiemu.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bootmgr.h"
12 #include <bcd.h>
13
14 /* DATA STRUCTURES ***********************************************************/
15
16 typedef struct _BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
17 {
18 BOOT_APPLICATION_PARAMETER_BLOCK;
19 BL_MEMORY_DATA BootMemoryData;
20 BL_MEMORY_DESCRIPTOR MemEntry;
21 UCHAR AppEntry[788];
22 } BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH;
23
24 /* DATA VARIABLES ************************************************************/
25
26 ULONG BlpApplicationFlags;
27
28 GUID EfiLoadedImageProtocol = EFI_LOADED_IMAGE_PROTOCOL_GUID;
29 GUID EfiDevicePathProtocol = EFI_DEVICE_PATH_PROTOCOL_GUID;
30
31 BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH EfiInitScratch;
32
33 /* FUNCTIONS *****************************************************************/
34
35 NTSTATUS
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
43 )
44 {
45 return STATUS_NOT_IMPLEMENTED;
46 }
47
48 NTSTATUS
49 EfiInitpConvertEfiFilePath (
50 _In_ EFI_DEVICE_PATH_PROTOCOL *FilePath,
51 _In_ ULONG PathType,
52 _In_ PBOOT_ENTRY_OPTION Option,
53 _In_ ULONG MaximumLength
54 )
55 {
56 return STATUS_NOT_IMPLEMENTED;
57 }
58
59 NTSTATUS
60 EfiInitpConvertEfiDevicePath (
61 _In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
62 _In_ ULONG DeviceType,
63 _In_ PBOOT_ENTRY_OPTION Option,
64 _In_ ULONG MaximumLength
65 )
66 {
67 return STATUS_NOT_IMPLEMENTED;
68 }
69
70 VOID
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,
79 __in ULONG Flags,
80 __out PULONG ResultLength,
81 __out PBL_DEVICE_DESCRIPTOR *AppEntryDevice
82 )
83 {
84 PBL_WINDOWS_LOAD_OPTIONS WindowsOptions;
85 PWCHAR ObjectString, CommandLine;
86 PBOOT_ENTRY_OPTION Option, PreviousOption;
87 ULONG HeaderSize, TotalOptionSize, Size, CommandLineSize, RemainingSize;
88 NTSTATUS Status;
89 UNICODE_STRING GuidString;
90 GUID ObjectGuid;
91 PBCDE_DEVICE BcdDevice;
92 BOOLEAN HaveBinaryOptions, HaveGuid;
93 PBL_FILE_PATH_DESCRIPTOR OsPath;
94 EFI_DEVICE_PATH *OsDevicePath;
95
96 /* Initialize everything */
97 TotalOptionSize = 0;
98 *AppEntryDevice = NULL;
99 HeaderSize = 0;
100
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)))
107 {
108 /* They are, so firmware must have loaded us -- extract arguments */
109 CommandLine = WindowsOptions->LoadOptions;
110 CommandLineSize = LoadOptionsSize - FIELD_OFFSET(BL_WINDOWS_LOAD_OPTIONS,
111 LoadOptions);
112
113 /* Remember that we used binary options */
114 HaveBinaryOptions = TRUE;
115 }
116 else
117 {
118 /* Nope -- so treat them as raw command-line options */
119 CommandLine = LoadOptions;
120 CommandLineSize = LoadOptionsSize;
121
122 /* No binary options */
123 HaveBinaryOptions = FALSE;
124 }
125
126 /* EFI uses UTF-16LE, like NT, so convert to characters */
127 CommandLineSize /= sizeof(WCHAR);
128 if (CommandLineSize != 0)
129 {
130 /* And check if the options are not NULL-terminated */
131 if (wcsnlen(CommandLine, CommandLineSize) == CommandLineSize)
132 {
133 /* NULL-terminate them */
134 CommandLine[CommandLineSize - 1] = UNICODE_NULL;
135 }
136 }
137
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))
141 {
142 Status = STATUS_INVALID_PARAMETER;
143 goto Quickie;
144 }
145
146 /* On exit, return that we've at least consumed this much */
147 HeaderSize = FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
148
149 /* Zero out the header, and write down the signature */
150 RtlZeroMemory(Entry, sizeof(BL_APPLICATION_ENTRY));
151 RtlCopyMemory(Entry->Signature, "BTAPENT", 7);
152
153 /* Check if a BCD object was passed on the command-line */
154 ObjectString = wcsstr(CommandLine, L"BCDOBJECT=");
155 if (ObjectString != NULL)
156 {
157 /* Convert the BCD object to a GUID */
158 RtlInitUnicodeString(&GuidString, ObjectString + 10);
159 RtlGUIDFromString(&GuidString, &ObjectGuid);
160
161 /* Store it in the application entry */
162 Entry->Guid = ObjectGuid;
163
164 /* Remember one was passed */
165 HaveGuid = TRUE;
166 }
167 else
168 {
169 /* Remember that no identifier was passed */
170 Entry->Flags |= BL_APPLICATION_ENTRY_FLAG_NO_GUID;
171 HaveGuid = FALSE;
172 }
173
174 /* At this point, the header is consumed, and we must now handle BCD options */
175 RemainingSize -= FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
176
177 /* Convert the device path into a BCD option */
178 Status = EfiInitpConvertEfiDevicePath(DevicePath,
179 BcdLibraryDevice_ApplicationDevice,
180 &Entry->BcdData,
181 RemainingSize);
182 if (!NT_SUCCESS(Status))
183 {
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);
187 goto Quickie;
188 }
189
190 /* Extract the device descriptor and return it */
191 BcdDevice = (PVOID)((ULONG_PTR)&Entry->BcdData + Entry->BcdData.DataOffset);
192 *AppEntryDevice = &BcdDevice->Device;
193
194 /* Calculate how big this option was and consume that from the buffer */
195 TotalOptionSize = BlGetBootOptionSize(&Entry->BcdData);
196 RemainingSize -= TotalOptionSize;
197
198 /* Calculate where the next option should go */
199 Option = (PVOID)((ULONG_PTR)&Entry->BcdData + TotalOptionSize);
200
201 /* Check if we're PXE booting or not */
202 if ((*AppEntryDevice)->DeviceType == UdpDevice)
203 {
204 /* lol */
205 Status = STATUS_NOT_IMPLEMENTED;
206 }
207 else
208 {
209 /* Convert the local file path into a BCD option */
210 Status = EfiInitpConvertEfiFilePath(FilePath,
211 BcdLibraryString_ApplicationPath,
212 Option,
213 RemainingSize);
214 }
215
216 /* Bail out on failure */
217 if (!NT_SUCCESS(Status))
218 {
219 goto Quickie;
220 }
221
222 /* The next option is right after this one */
223 Entry->BcdData.NextEntryOffset = TotalOptionSize;
224
225 /* Now compute the size of the next option, and add to the rolling sum */
226 Size = BlGetBootOptionSize(Option);
227 TotalOptionSize += Size;
228
229 /* Remember the previous option so we can update its next offset */
230 PreviousOption = Option;
231
232 /* Consume the option from the buffer */
233 RemainingSize -= Size;
234
235 /* Calculate where the next option should go */
236 Option = (PVOID)((ULONG_PTR)Option + Size);
237
238 /* Check if we were using binary options without a BCD GUID */
239 if ((HaveBinaryOptions) && !(HaveGuid))
240 {
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);
244
245 /* IS the OS path in EFI format? */
246 if ((OsPath->Length > FIELD_OFFSET(BL_FILE_PATH_DESCRIPTOR, Path)) &&
247 (OsPath->PathType == EfiPath))
248 {
249 /* Convert the device portion */
250 OsDevicePath = (EFI_DEVICE_PATH*)OsPath->Path;
251 Status = EfiInitpConvertEfiDevicePath(OsDevicePath,
252 BcdOSLoaderDevice_OSDevice,
253 Option,
254 RemainingSize);
255 if (!NT_SUCCESS(Status))
256 {
257 goto Quickie;
258 }
259
260 /* Update the offset of the previous option */
261 PreviousOption->NextEntryOffset = (ULONG_PTR)Option - (ULONG_PTR)&Entry->BcdData;
262
263 /* Now compute the size of the next option, and add to the rolling sum */
264 Size = BlGetBootOptionSize(Option);
265 TotalOptionSize += Size;
266
267 /* Remember the previous option so we can update its next offset */
268 PreviousOption = Option;
269
270 /* Consume the option from the buffer */
271 RemainingSize -= Size;
272
273 /* Calculate where the next option should go */
274 Option = (PVOID)((ULONG_PTR)Option + Size);
275
276 /* Convert the path oprtion */
277 Status = EfiInitpConvertEfiFilePath(OsDevicePath,
278 BcdOSLoaderString_SystemRoot,
279 Option,
280 RemainingSize);
281 if (!NT_SUCCESS(Status))
282 {
283 goto Quickie;
284 }
285
286 /* Update the offset of the previous option */
287 PreviousOption->NextEntryOffset = (ULONG_PTR)Option - (ULONG_PTR)&Entry->BcdData;
288
289 /* Now compute the size of the next option, and add to the rolling sum */
290 Size = BlGetBootOptionSize(Option);
291 TotalOptionSize += Size;
292
293 /* Remember the previous option so we can update its next offset */
294 PreviousOption = Option;
295
296 /* Consume the option from the buffer */
297 RemainingSize -= Size;
298
299 /* Calculate where the next option should go */
300 Option = (PVOID)((ULONG_PTR)Option + Size);
301 }
302 }
303
304 /* Now convert everything else */
305 AhCreateLoadOptionsList(CommandLine,
306 &Entry->BcdData,
307 RemainingSize,
308 &TotalOptionSize,
309 &PreviousOption,
310 &Size);
311
312 Quickie:
313 /* Return the final size */
314 *ResultLength = HeaderSize + TotalOptionSize;
315 }
316
317 /*++
318 * @name EfiInitCreateInputParametersEx
319 *
320 * The EfiInitCreateInputParametersEx routine converts UEFI entrypoint
321 * parameters to the ones expected by Windows Boot Applications
322 *
323 * @param ImageHandle
324 * UEFI Image Handle for the current loaded application.
325 *
326 * @param SystemTable
327 * Pointer to the UEFI System Table.
328 *
329 * @return A PBOOT_APPLICATION_PARAMETER_BLOCK structure containing the data
330 * from UEFI, translated to the Boot Library-compatible format.
331 *
332 *--*/
333 PBOOT_APPLICATION_PARAMETER_BLOCK
334 EfiInitCreateInputParametersEx (
335 _In_ EFI_HANDLE ImageHandle,
336 _In_ EFI_SYSTEM_TABLE *SystemTable
337 )
338 {
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;
346 EFI_STATUS Status;
347
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;
352
353 /* Set the image type to x86 */
354 EfiInitScratch.ImageType = EFI_IMAGE_MACHINE_IA32;
355
356 /* Set the translation type to physical */
357 EfiInitScratch.MemoryTranslationType = BOOT_MEMORY_TRANSLATION_TYPE_PHYSICAL;
358
359 /* Indicate that the data was converted from EFI */
360 BlpApplicationFlags |= BL_APPLICATION_FLAG_CONVERTED_FROM_EFI;
361
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)
368 {
369 SystemTable->ConOut->OutputString(SystemTable->ConsoleOutHandle,
370 L"Loaded image failed\n");
371 return NULL;
372 }
373
374 /* Capture it in the boot application parameters */
375 EfiInitScratch.ImageBase = (ULONG_PTR)LoadedImage->ImageBase;
376 EfiInitScratch.ImageSize = (ULONG)LoadedImage->ImageSize;
377
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)
383 {
384 SystemTable->ConOut->OutputString(SystemTable->ConsoleOutHandle,
385 L"Device path failed\n");
386 return NULL;
387 }
388
389 /* The built-in boot memory data comes right after our block */
390 EfiInitScratch.MemoryDataOffset =
391 FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, BootMemoryData);
392
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;
401
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;
407
408 /* The built-in application entry comes right after the memory descriptor*/
409 EfiInitScratch.AppEntryOffset =
410 FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, AppEntry);
411
412 /* Go and build it */
413 EfiInitpCreateApplicationEntry(SystemTable,
414 (PBL_APPLICATION_ENTRY)&EfiInitScratch.AppEntry,
415 sizeof(EfiInitScratch.AppEntry),
416 DevicePath,
417 LoadedImage->FilePath,
418 LoadedImage->LoadOptions,
419 LoadedImage->LoadOptionsSize,
420 EfiInitScratch.MemEntry.PageCount,
421 &ConsumedSize,
422 &AppDevice);
423
424 /* Boot device information comes right after the application entry */
425 EfiInitScratch.BootDeviceOffset = ConsumedSize + EfiInitScratch.AppEntryOffset;
426
427 /* Check if we have a boot device */
428 if (AppDevice != NULL)
429 {
430 /* We do -- copy it */
431 RtlCopyMemory(EfiInitScratch.AppEntry + ConsumedSize,
432 AppDevice,
433 AppDevice->Size);
434
435 /* Firmware data follows right after the boot device entry */
436 FirmwareOffset = AppDevice->Size + EfiInitScratch.BootDeviceOffset;
437 }
438 else
439 {
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));
443
444 /* And start the firmware data past that */
445 FirmwareOffset = EfiInitScratch.BootDeviceOffset + sizeof(BL_DEVICE_DESCRIPTOR);
446 }
447
448 /* Set the computed firmware data offset */
449 EfiInitScratch.FirmwareParametersOffset = FirmwareOffset;
450
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;
456
457 /* Finally, set the return argument offset */
458 EfiInitScratch.ReturnArgumentsOffset = FirmwareOffset + sizeof(BL_FIRMWARE_DESCRIPTOR);
459
460 /* And fill out the return argument data */
461 ReturnArguments = (PVOID)((ULONG_PTR)&EfiInitScratch + EfiInitScratch.ReturnArgumentsOffset);
462 ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
463
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;
467 }
468
469 /*++
470 * @name EfiEntry
471 *
472 * The EfiEntry routine implements the UEFI entrypoint for the application.
473 *
474 * @param ImageHandle
475 * UEFI Image Handle for the current loaded application.
476 *
477 * @param SystemTable
478 * Pointer to the UEFI System Table.
479 *
480 * @return EFI_SUCCESS if the image was loaded correctly, relevant error code
481 * otherwise.
482 *
483 *--*/
484 EFI_STATUS
485 EfiEntry (
486 _In_ EFI_HANDLE ImageHandle,
487 _In_ EFI_SYSTEM_TABLE *SystemTable
488 )
489 {
490 NTSTATUS Status;
491 PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters;
492
493 /* Temporary debugging string */
494 SystemTable->ConOut->OutputString(SystemTable->ConsoleOutHandle, L"Hello from EFI\n");
495
496 /* Convert EFI parameters to Windows Boot Application parameters */
497 BootParameters = EfiInitCreateInputParametersEx(ImageHandle, SystemTable);
498 if (BootParameters != NULL)
499 {
500 /* Conversion was good -- call the Boot Manager Entrypoint */
501 SystemTable->ConOut->OutputString(SystemTable->ConsoleOutHandle, L"EFI input OK!\n");
502 Status = BmMain(BootParameters);
503 }
504 else
505 {
506 /* Conversion failed, bail out */
507 SystemTable->ConOut->OutputString(SystemTable->ConsoleOutHandle, L"EFI input failed\n");
508 Status = STATUS_INVALID_PARAMETER;
509 }
510
511 /* Convert the NT status code to an EFI code */
512 return EfiGetEfiStatusCode(Status);
513 }
514