xref: /reactos/boot/environ/app/bootmgr/efiemu.c (revision 40ee59d6)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Manager
4  * FILE:            boot/environ/app/bootmgr/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 
13 /* DATA STRUCTURES ***********************************************************/
14 
15 typedef struct _BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH
16 {
17     BOOT_APPLICATION_PARAMETER_BLOCK;
18     BL_MEMORY_DATA BootMemoryData;
19     BL_MEMORY_DESCRIPTOR MemEntry;
20     UCHAR AppEntry[788];
21 } BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH;
22 
23 /* DATA VARIABLES ************************************************************/
24 
25 BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH EfiInitScratch;
26 
27 /* FUNCTIONS *****************************************************************/
28 
29 /*++
30  * @name AhCreateLoadOptionsList
31  *
32  *     The AhCreateLoadOptionsList routine
33  *
34  * @param  CommandLine
35  *         UEFI Image Handle for the current loaded application.
36  *
37  * @param  BootOptions
38  *         Pointer to the UEFI System Table.
39  *
40  * @param  MaximumLength
41  *         Pointer to the UEFI System Table.
42  *
43  * @param  OptionSize
44  *         Pointer to the UEFI System Table.
45  *
46  * @param  PreviousOption
47  *         Pointer to the UEFI System Table.
48  *
49  * @param  PreviousOptionSize
50  *         Pointer to the UEFI System Table.
51  *
52  * @return None
53  *
54  *--*/
55 NTSTATUS
56 AhCreateLoadOptionsList (
57     _In_ PWCHAR CommandLine,
58     _In_ PBL_BCD_OPTION BootOptions,
59     _In_ ULONG MaximumLength,
60     _Out_ PULONG OptionSize,
61     _In_ PBL_BCD_OPTION* PreviousOption,
62     _In_ PULONG PreviousOptionSize
63     )
64 {
65     return STATUS_NOT_IMPLEMENTED;
66 }
67 
68 /*++
69  * @name EfiInitpAppendPathString
70  *
71  *     The EfiInitpAppendPathString routine
72  *
73  * @param  DestinationPath
74  *         UEFI Image Handle for the current loaded application.
75  *
76  * @param  RemainingSize
77  *         Pointer to the UEFI System Table.
78  *
79  * @param  AppendPath
80  *         Pointer to the UEFI System Table.
81  *
82  * @param  AppendLength
83  *         Pointer to the UEFI System Table.
84  *
85  * @param  BytesAppended
86  *         Pointer to the UEFI System Table.
87  *
88  * @return None
89  *
90  *--*/
91 NTSTATUS
92 EfiInitpAppendPathString (
93     _In_ PWCHAR PathString,
94     _In_ ULONG MaximumLength,
95     _In_ PWCHAR NewPathString,
96     _In_ ULONG NewPathLength,
97     _Out_ PULONG ResultLength
98     )
99 {
100     NTSTATUS Status;
101     ULONG FinalPathLength;
102 
103     /* We deal in Unicode, validate the length */
104     if (NewPathLength & 1)
105     {
106         return STATUS_INVALID_PARAMETER;
107     }
108 
109     /* Is the new element at least a character? */
110     Status = STATUS_SUCCESS;
111     if (NewPathLength >= sizeof(WCHAR))
112     {
113         /* Is the last character already a NULL character? */
114         if (NewPathString[(NewPathLength - sizeof(WCHAR)) / sizeof(WCHAR)] ==
115             UNICODE_NULL)
116         {
117             /* Then we won't need to count it */
118             NewPathLength -= sizeof(UNICODE_NULL);
119         }
120 
121         /* Was it more than just a NULL character? */
122         if (NewPathLength >= sizeof(WCHAR))
123         {
124             /* Yep -- but does it have a separator? */
125             if (*NewPathString == OBJ_NAME_PATH_SEPARATOR)
126             {
127                 /* Skip it, we'll add our own later */
128                 NewPathString++;
129                 NewPathLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
130             }
131 
132             /* Was it more than just a separator? */
133             if (NewPathLength >= sizeof(WCHAR))
134             {
135                 /* Yep -- but does it end with a separator? */
136                 if (NewPathString[(NewPathLength - sizeof(WCHAR)) / sizeof(WCHAR)] ==
137                     OBJ_NAME_PATH_SEPARATOR)
138                 {
139                     /* That's something else we won't need for now */
140                     NewPathLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
141                 }
142             }
143         }
144     }
145 
146     /* Check if anything needs to be appended after all */
147     if (NewPathLength != 0)
148     {
149         /* We will append the length of the new path element, plus a separator */
150         FinalPathLength = NewPathLength + sizeof(OBJ_NAME_PATH_SEPARATOR);
151         if (MaximumLength >= FinalPathLength)
152         {
153             /* Add a separator to the existing path*/
154             *PathString = OBJ_NAME_PATH_SEPARATOR;
155 
156             /* Followed by the new path element */
157             RtlCopyMemory(PathString + 1, NewPathString, NewPathLength);
158 
159             /* Return the number of bytes appended */
160             *ResultLength = FinalPathLength;
161         }
162         else
163         {
164             /* There's not enough space to do this */
165             Status = STATUS_BUFFER_TOO_SMALL;
166         }
167     }
168     else
169     {
170         /* Nothing to append */
171        *ResultLength = 0;
172     }
173 
174     return Status;
175 }
176 
177 /*++
178  * @name EfiInitpConvertEfiDevicePath
179  *
180  *     The EfiInitpConvertEfiDevicePath routine
181  *
182  * @param  DevicePath
183  *         UEFI Image Handle for the current loaded application.
184  *
185  * @param  DeviceType
186  *         Pointer to the UEFI System Table.
187  *
188  * @param  Option
189  *         Pointer to the UEFI System Table.
190  *
191  * @param  MaximumLength
192  *         Pointer to the UEFI System Table.
193  *
194  * @return None
195  *
196  *--*/
197 NTSTATUS
198 EfiInitpConvertEfiFilePath (
199     _In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
200     _In_ ULONG PathType,
201     _In_ PBL_BCD_OPTION Option,
202     _In_ ULONG MaximumLength
203     )
204 {
205     ULONG BytesAppended, DataSize, StringLength;
206     PWCHAR StringEntry, PathString;
207     FILEPATH_DEVICE_PATH *FilePath;
208     NTSTATUS Status;
209 
210     /* Make sure we have enough space for the option */
211     if (MaximumLength < sizeof(*Option))
212     {
213         return STATUS_INVALID_PARAMETER;
214     }
215 
216     /* Set the initial size of the option, and consume from our buffer */
217     DataSize = sizeof(*Option);
218     MaximumLength -= sizeof(*Option);
219 
220     /* Zero out and fill the option header */
221     RtlZeroMemory(Option, DataSize);
222     Option->Type = PathType;
223     Option->DataOffset = sizeof(*Option);
224 
225     /* Extract the string option */
226     StringEntry = (PWCHAR)(Option + 1);
227     PathString = StringEntry;
228 
229     /* Start parsing the device path */
230     FilePath = (FILEPATH_DEVICE_PATH*)DevicePath;
231     while (IsDevicePathEndType(FilePath) == FALSE)
232     {
233         /* Is this a file path? */
234         if ((FilePath->Header.Type == MEDIA_DEVICE_PATH) &&
235             (FilePath->Header.SubType == MEDIA_FILEPATH_DP))
236         {
237             /* Get the length of the file path string, avoiding overflow */
238             StringLength = DevicePathNodeLength(FilePath) -
239                            FIELD_OFFSET(FILEPATH_DEVICE_PATH, PathName);
240             if (StringLength < (ULONG)FIELD_OFFSET(FILEPATH_DEVICE_PATH, PathName))
241             {
242                 Status = STATUS_INTEGER_OVERFLOW;
243                 goto Quickie;
244             }
245 
246             /* Append this path string to the current path string */
247             Status = EfiInitpAppendPathString(PathString,
248                                               MaximumLength,
249                                               FilePath->PathName,
250                                               StringLength,
251                                               &BytesAppended);
252             if (!NT_SUCCESS(Status))
253             {
254                 return Status;
255             }
256 
257             /* Increase the size of the data, consume buffer space */
258             DataSize += BytesAppended;
259             MaximumLength -= BytesAppended;
260 
261             /* Move to the next path string */
262             PathString = (PWCHAR)((ULONG_PTR)PathString + BytesAppended);
263         }
264 
265         /* Move to the next path node */
266         FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode(FilePath);
267     }
268 
269     /* Check if we still have space for a NULL-terminator */
270     if (MaximumLength < sizeof(UNICODE_NULL))
271     {
272         Status = STATUS_INVALID_PARAMETER;
273         goto Quickie;
274     }
275 
276     /* We do -- NULL-terminate the string */
277     *PathString = UNICODE_NULL;
278     DataSize += sizeof(UNICODE_NULL);
279 
280     /* Check if all of this has amounted to a single NULL-char */
281     if (PathString == StringEntry)
282     {
283         /* Then this option is empty */
284         Option->Empty = TRUE;
285     }
286 
287     /* Set the final size of the option */
288     Option->DataSize = DataSize;
289 
290 Quickie:
291     return STATUS_SUCCESS;
292 }
293 
294 /*++
295  * @name EfiInitpGetDeviceNode
296  *
297  *     The EfiInitpGetDeviceNode routine
298  *
299  * @param  DevicePath
300  *         UEFI Image Handle for the current loaded application.
301  *
302  * @return None
303  *
304  *--*/
305 EFI_DEVICE_PATH_PROTOCOL*
306 EfiInitpGetDeviceNode (
307     _In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath
308     )
309 {
310     EFI_DEVICE_PATH_PROTOCOL* NextPath;
311 
312     /* Check if we hit the end terminator */
313     if (IsDevicePathEndType(DevicePath))
314     {
315         return DevicePath;
316     }
317 
318     /* Loop each device path, until we get to the end or to a file path device node */
319     for ((NextPath = NextDevicePathNode(DevicePath));
320          !(IsDevicePathEndType(NextPath)) && ((NextPath->Type != MEDIA_DEVICE_PATH) ||
321                                               (NextPath->SubType != MEDIA_FILEPATH_DP));
322          (NextPath = NextDevicePathNode(NextPath)))
323     {
324         /* Keep iterating down */
325         DevicePath = NextPath;
326     }
327 
328     /* Return the path found */
329     return DevicePath;
330 }
331 
332 /*++
333  * @name EfiInitTranslateDevicePath
334  *
335  *     The EfiInitTranslateDevicePath routine
336  *
337  * @param  DevicePath
338  *         UEFI Image Handle for the current loaded application.
339  *
340  * @param  DeviceEntry
341  *         Pointer to the UEFI System Table.
342  *
343  * @return None
344  *
345  *--*/
346 NTSTATUS
347 EfiInitTranslateDevicePath (
348     _In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
349     _In_ PBL_DEVICE_DESCRIPTOR DeviceEntry
350     )
351 {
352     NTSTATUS Status;
353     EFI_DEVICE_PATH_PROTOCOL* DeviceNode;
354     MEMMAP_DEVICE_PATH* MemDevicePath;
355     ACPI_HID_DEVICE_PATH *AcpiPath;
356     HARDDRIVE_DEVICE_PATH *DiskPath;
357 
358     /* Assume failure */
359     Status = STATUS_UNSUCCESSFUL;
360 
361     /* Set size first */
362     DeviceEntry->Size = sizeof(*DeviceEntry);
363 
364     /* Check if we are booting from a RAM Disk */
365     if ((DevicePath->Type == HARDWARE_DEVICE_PATH) &&
366         (DevicePath->SubType == HW_MEMMAP_DP))
367     {
368         /* Get the EFI data structure matching this */
369         MemDevicePath = (MEMMAP_DEVICE_PATH*)DevicePath;
370 
371         /* Set the boot library specific device types */
372         DeviceEntry->DeviceType = LocalDevice;
373         DeviceEntry->Local.Type = RamDiskDevice;
374 
375         /* Extract the base, size, and offset */
376         DeviceEntry->Local.RamDisk.ImageBase.QuadPart = MemDevicePath->StartingAddress;
377         DeviceEntry->Local.RamDisk.ImageSize.QuadPart = MemDevicePath->EndingAddress -
378                                                         MemDevicePath->StartingAddress;
379         DeviceEntry->Local.RamDisk.ImageOffset = 0;
380         return STATUS_SUCCESS;
381     }
382 
383     /* Otherwise, check what kind of device node this is */
384     DeviceNode = EfiInitpGetDeviceNode(DevicePath);
385     switch (DeviceNode->Type)
386     {
387         /* ACPI */
388         case ACPI_DEVICE_PATH:
389 
390             /* We only support floppy drives */
391             AcpiPath = (ACPI_HID_DEVICE_PATH*)DeviceNode;
392             if ((AcpiPath->HID != EISA_PNP_ID(0x604)) &&
393                 (AcpiPath->HID != EISA_PNP_ID(0x700)))
394             {
395                 return Status;
396             }
397 
398             /* Set the boot library specific device types */
399             DeviceEntry->DeviceType = LocalDevice;
400             DeviceEntry->Local.Type = FloppyDevice;
401 
402             /* The ACPI UID is the drive number */
403             DeviceEntry->Local.FloppyDisk.DriveNumber = AcpiPath->UID;
404             return STATUS_SUCCESS;
405 
406         /* Network, ATAPI, SCSI, USB */
407         case MESSAGING_DEVICE_PATH:
408 
409             /* Check if it's network */
410             if ((DeviceNode->SubType == MSG_MAC_ADDR_DP) ||
411                 (DeviceNode->SubType == MSG_IPv4_DP))
412             {
413                 /* Set the boot library specific device types */
414                 DeviceEntry->DeviceType = UdpDevice;
415                 DeviceEntry->Remote.Unknown = 256;
416                 return STATUS_SUCCESS;
417             }
418 
419             /* Other types should come in as MEDIA_DEVICE_PATH -- Windows assumes this is a floppy */
420             DeviceEntry->DeviceType = DiskDevice;
421             DeviceEntry->Local.Type = FloppyDevice;
422             DeviceEntry->Local.FloppyDisk.DriveNumber = 0;
423             return STATUS_SUCCESS;
424 
425         /* Disk or CDROM */
426         case MEDIA_DEVICE_PATH:
427 
428             /* Extract the disk path and check if it's a physical disk */
429             DiskPath = (HARDDRIVE_DEVICE_PATH*)DeviceNode;
430             if (DeviceNode->SubType == MEDIA_HARDDRIVE_DP)
431             {
432                 /* Check if this is an MBR partition */
433                 if (DiskPath->SignatureType == SIGNATURE_TYPE_MBR)
434                 {
435                     /* Set that this is a local partition */
436                     DeviceEntry->DeviceType = LegacyPartitionDevice;
437                     DeviceEntry->Partition.Disk.Type = LocalDevice;
438 
439                     DeviceEntry->Partition.Disk.HardDisk.PartitionType = MbrPartition;
440                     DeviceEntry->Partition.Disk.HardDisk.Mbr.PartitionSignature =
441                         *(PULONG)&DiskPath->Signature[0];
442                     DeviceEntry->Partition.Mbr.PartitionNumber = DiskPath->PartitionNumber;
443                     return STATUS_SUCCESS;
444                 }
445 
446                 /* Check if it's a GPT partition */
447                 if (DiskPath->SignatureType == SIGNATURE_TYPE_GUID)
448                 {
449                     /* Set that this is a local disk */
450                     DeviceEntry->DeviceType = PartitionDevice;
451                     DeviceEntry->Partition.Disk.Type = LocalDevice;
452 
453                     /* Set GPT partition ID */
454                     DeviceEntry->Partition.Disk.HardDisk.PartitionType = GptPartition;
455 
456                     /* Copy the signature GUID */
457                     RtlCopyMemory(&DeviceEntry->Partition.Gpt.PartitionGuid,
458                                   DiskPath->Signature,
459                                   sizeof(GUID));
460 
461                     DeviceEntry->Flags |= 4u;
462                     return STATUS_SUCCESS;
463                 }
464 
465                 /* Otherwise, raw boot is not supported */
466                 DeviceEntry->DeviceType = PartitionDevice;
467                 DeviceEntry->Partition.Disk.Type = LocalDevice;
468                 DeviceEntry->Partition.Disk.HardDisk.PartitionType = RawPartition;
469                 DeviceEntry->Partition.Disk.HardDisk.Raw.DiskNumber = 0;
470             }
471             else if (DeviceNode->SubType == MEDIA_CDROM_DP)
472             {
473                 /* Set the right type for a CDROM */
474                 DeviceEntry->DeviceType = DiskDevice;
475                 DeviceEntry->Local.Type = CdRomDevice;
476 
477                 /* Set the drive number to zero */
478                 DeviceEntry->Local.FloppyDisk.DriveNumber = 0;
479                 return STATUS_SUCCESS;
480             }
481 
482         /* Fail anything else */
483         default:
484             break;
485     }
486 
487     /* Return here only on failure */
488     return Status;
489 }
490 
491 /*++
492  * @name EfiInitpConvertEfiDevicePath
493  *
494  *     The EfiInitpConvertEfiDevicePath routine
495  *
496  * @param  DevicePath
497  *         UEFI Image Handle for the current loaded application.
498  *
499  * @param  DeviceType
500  *         Pointer to the UEFI System Table.
501  *
502  * @param  Option
503  *         Pointer to the UEFI System Table.
504  *
505  * @param  MaximumLength
506  *         Pointer to the UEFI System Table.
507  *
508  * @return None
509  *
510  *--*/
511 NTSTATUS
512 EfiInitpConvertEfiDevicePath (
513     _In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
514     _In_ ULONG DeviceType,
515     _In_ PBL_BCD_OPTION Option,
516     _In_ ULONG MaximumLength
517     )
518 {
519     PBCD_DEVICE_OPTION BcdDevice;
520     NTSTATUS Status;
521 
522     /* Make sure we have enough space for the option */
523     if (MaximumLength < sizeof(*Option))
524     {
525         Status = STATUS_INVALID_PARAMETER;
526         goto Quickie;
527     }
528 
529     /* Zero out the option */
530     RtlZeroMemory(Option, sizeof(*Option));
531 
532     /* Make sure we have enough space for the device entry */
533     if ((MaximumLength - sizeof(*Option)) <
534         (ULONG)FIELD_OFFSET(BCD_DEVICE_OPTION, DeviceDescriptor))
535     {
536         Status = STATUS_INVALID_PARAMETER;
537         goto Quickie;
538     }
539 
540     /* Fill it out */
541     BcdDevice = (PBCD_DEVICE_OPTION)(Option + 1);
542     Status = EfiInitTranslateDevicePath(DevicePath,
543                                         &BcdDevice->DeviceDescriptor);
544     if (!NT_SUCCESS(Status))
545     {
546         goto Quickie;
547     }
548 
549     /* Fill out the rest of the option structure */
550     Option->DataOffset = sizeof(*Option);
551     Option->Type = DeviceType;
552     Option->DataSize = FIELD_OFFSET(BCD_DEVICE_OPTION, DeviceDescriptor) +
553                        BcdDevice->DeviceDescriptor.Size;
554     Status = STATUS_SUCCESS;
555 
556 Quickie:
557     return Status;
558 }
559 
560 /*++
561  * @name EfiInitpCreateApplicationEntry
562  *
563  *     The EfiInitpCreateApplicationEntry routine
564  *
565  * @param  SystemTable
566  *         UEFI Image Handle for the current loaded application.
567  *
568  * @param  Entry
569  *         Pointer to the UEFI System Table.
570  *
571  * @param  MaximumLength
572  *         Pointer to the UEFI System Table.
573  *
574  * @param  DevicePath
575  *         Pointer to the UEFI System Table.
576  *
577  * @param  FilePath
578  *         Pointer to the UEFI System Table.
579  *
580  * @param  LoadOptions
581  *         Pointer to the UEFI System Table.
582  *
583  * @param  LoadOptionsSize
584  *         Pointer to the UEFI System Table.
585  *
586  * @param  Flags
587  *         Pointer to the UEFI System Table.
588  *
589  * @param  ResultLength
590  *         Pointer to the UEFI System Table.
591  *
592  * @param  AppEntryDevice
593  *         Pointer to the UEFI System Table.
594  *
595  * @return None
596  *
597  *--*/
598 VOID
599 EfiInitpCreateApplicationEntry (
600     __in EFI_SYSTEM_TABLE *SystemTable,
601     __in PBL_APPLICATION_ENTRY Entry,
602     __in ULONG MaximumLength,
603     __in EFI_DEVICE_PATH *DevicePath,
604     __in EFI_DEVICE_PATH *FilePath,
605     __in PWCHAR LoadOptions,
606     __in ULONG LoadOptionsSize,
607     __in ULONG Flags,
608     __out PULONG ResultLength,
609     __out PBL_DEVICE_DESCRIPTOR *AppEntryDevice
610     )
611 {
612     PBL_WINDOWS_LOAD_OPTIONS WindowsOptions;
613     PWCHAR ObjectString, CommandLine;
614     PBL_BCD_OPTION Option, PreviousOption;
615     ULONG HeaderSize, TotalOptionSize, Size, CommandLineSize, RemainingSize;
616     NTSTATUS Status;
617     UNICODE_STRING GuidString;
618     GUID ObjectGuid;
619     PBCD_DEVICE_OPTION BcdDevice;
620     BOOLEAN HaveBinaryOptions, HaveGuid;
621     PBL_FILE_PATH_DESCRIPTOR OsPath;
622     EFI_DEVICE_PATH *OsDevicePath;
623 
624     /* Initialize everything */
625     TotalOptionSize = 0;
626     *AppEntryDevice = NULL;
627     HeaderSize = 0;
628 
629     /* Check if the load options are in binary Windows format */
630     WindowsOptions = (PBL_WINDOWS_LOAD_OPTIONS)LoadOptions;
631     if ((WindowsOptions != NULL) &&
632         (LoadOptionsSize >= sizeof(BL_WINDOWS_LOAD_OPTIONS)) &&
633         (WindowsOptions->Length >= sizeof(BL_WINDOWS_LOAD_OPTIONS)) &&
634         !(strncmp(WindowsOptions->Signature, "WINDOWS", 7)))
635     {
636         /* They are, so firmware must have loaded us -- extract arguments */
637         CommandLine = WindowsOptions->LoadOptions;
638         CommandLineSize = LoadOptionsSize - FIELD_OFFSET(BL_WINDOWS_LOAD_OPTIONS,
639                                                          LoadOptions);
640 
641         /* Remember that we used binary options */
642         HaveBinaryOptions = TRUE;
643     }
644     else
645     {
646         /* Nope -- so treat them as raw command-line options */
647         CommandLine = LoadOptions;
648         CommandLineSize = LoadOptionsSize;
649 
650         /* No binary options */
651         HaveBinaryOptions = FALSE;
652     }
653 
654     /* EFI uses UTF-16LE, like NT, so convert to characters */
655     CommandLineSize /= sizeof(WCHAR);
656     if (CommandLineSize != 0)
657     {
658         /* And check if the options are not NULL-terminated */
659         if (wcsnlen(CommandLine, CommandLineSize) == CommandLineSize)
660         {
661             /* NULL-terminate them */
662             CommandLine[CommandLineSize - 1] = UNICODE_NULL;
663         }
664     }
665 
666     /* Begin by making sure we at least have space for the app entry header */
667     RemainingSize = MaximumLength;
668     if (RemainingSize < sizeof(BL_APPLICATION_ENTRY))
669     {
670         Status = STATUS_INVALID_PARAMETER;
671         goto Quickie;
672     }
673 
674     /* On exit, return that we've at least consumed this much */
675     HeaderSize = FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
676 
677     /* Zero out the header, and write down the signature */
678     RtlZeroMemory(Entry, sizeof(BL_APPLICATION_ENTRY));
679     RtlCopyMemory(Entry->Signature, BL_APP_ENTRY_SIGNATURE, 7);
680 
681     /* Check if a BCD object was passed on the command-line */
682     ObjectString = wcsstr(CommandLine, L"BCDOBJECT=");
683     if (ObjectString != NULL)
684     {
685         /* Convert the BCD object to a GUID */
686         RtlInitUnicodeString(&GuidString, ObjectString + 10);
687         RtlGUIDFromString(&GuidString, &ObjectGuid);
688 
689         /* Store it in the application entry */
690         Entry->Guid = ObjectGuid;
691 
692         /* Remember one was passed */
693         HaveGuid = TRUE;
694     }
695     else
696     {
697         /* Remember that no identifier was passed */
698         Entry->Flags |= BL_APPLICATION_ENTRY_FLAG_NO_GUID;
699         HaveGuid = FALSE;
700     }
701 
702     /* At this point, the header is consumed, and we must now handle BCD options */
703     RemainingSize -= FIELD_OFFSET(BL_APPLICATION_ENTRY, BcdData);
704 
705     /* Convert the device path into a BCD option */
706     Status = EfiInitpConvertEfiDevicePath(DevicePath,
707                                           BcdLibraryDevice_ApplicationDevice,
708                                           &Entry->BcdData,
709                                           RemainingSize);
710     if (!NT_SUCCESS(Status))
711     {
712         /* We failed, so mark the option as such and return an empty one */
713         Entry->BcdData.Empty = TRUE;
714         TotalOptionSize = sizeof(BL_BCD_OPTION);
715         goto Quickie;
716     }
717 
718     /* Extract the device descriptor and return it */
719     BcdDevice = (PVOID)((ULONG_PTR)&Entry->BcdData + Entry->BcdData.DataOffset);
720     *AppEntryDevice = &BcdDevice->DeviceDescriptor;
721 
722     /* Calculate how big this option was and consume that from the buffer */
723     TotalOptionSize = BlGetBootOptionSize(&Entry->BcdData);
724     RemainingSize -= TotalOptionSize;
725 
726     /* Calculate where the next option should go */
727     Option = (PVOID)((ULONG_PTR)&Entry->BcdData + TotalOptionSize);
728 
729     /* Check if we're PXE booting or not */
730     if ((*AppEntryDevice)->DeviceType == UdpDevice)
731     {
732         /* lol */
733         Status = STATUS_NOT_IMPLEMENTED;
734     }
735     else
736     {
737         /* Convert the local file path into a BCD option */
738         Status = EfiInitpConvertEfiFilePath(FilePath,
739                                             BcdLibraryString_ApplicationPath,
740                                             Option,
741                                             RemainingSize);
742     }
743 
744     /* Bail out on failure */
745     if (!NT_SUCCESS(Status))
746     {
747         goto Quickie;
748     }
749 
750     /* The next option is right after this one */
751     Entry->BcdData.NextEntryOffset = TotalOptionSize;
752 
753     /* Now compute the size of the next option, and add to the rolling sum */
754     Size = BlGetBootOptionSize(Option);
755     TotalOptionSize += Size;
756 
757     /* Remember the previous option so we can update its next offset */
758     PreviousOption = Option;
759 
760     /* Consume the option from the buffer */
761     RemainingSize -= Size;
762 
763     /* Calculate where the next option should go */
764     Option = (PVOID)((ULONG_PTR)Option + Size);
765 
766     /* Check if we were using binary options without a BCD GUID */
767     if ((HaveBinaryOptions) && !(HaveGuid))
768     {
769         /* Then this means we have to convert the OS paths to BCD too */
770         WindowsOptions = (PBL_WINDOWS_LOAD_OPTIONS)LoadOptions;
771         OsPath = (PVOID)((ULONG_PTR)WindowsOptions + WindowsOptions->OsPathOffset);
772 
773         /* IS the OS path in EFI format? */
774         if ((OsPath->Length > (ULONG)FIELD_OFFSET(BL_FILE_PATH_DESCRIPTOR, Path)) &&
775             (OsPath->PathType == EfiPath))
776         {
777             /* Convert the device portion  */
778             OsDevicePath = (EFI_DEVICE_PATH*)OsPath->Path;
779             Status = EfiInitpConvertEfiDevicePath(OsDevicePath,
780                                                   BcdOSLoaderDevice_OSDevice,
781                                                   Option,
782                                                   RemainingSize);
783             if (!NT_SUCCESS(Status))
784             {
785                 goto Quickie;
786             }
787 
788             /* Update the offset of the previous option */
789             PreviousOption->NextEntryOffset = (ULONG_PTR)Option - (ULONG_PTR)&Entry->BcdData;
790 
791             /* Now compute the size of the next option, and add to the rolling sum */
792             Size = BlGetBootOptionSize(Option);
793             TotalOptionSize += Size;
794 
795             /* Remember the previous option so we can update its next offset */
796             PreviousOption = Option;
797 
798             /* Consume the option from the buffer */
799             RemainingSize -= Size;
800 
801             /* Calculate where the next option should go */
802             Option = (PVOID)((ULONG_PTR)Option + Size);
803 
804             /* Convert the path option */
805             Status = EfiInitpConvertEfiFilePath(OsDevicePath,
806                                                 BcdOSLoaderString_SystemRoot,
807                                                 Option,
808                                                 RemainingSize);
809             if (!NT_SUCCESS(Status))
810             {
811                 goto Quickie;
812             }
813 
814             /* Update the offset of the previous option */
815             PreviousOption->NextEntryOffset = (ULONG_PTR)Option - (ULONG_PTR)&Entry->BcdData;
816 
817             /* Now compute the size of the next option, and add to the rolling sum */
818             Size = BlGetBootOptionSize(Option);
819             TotalOptionSize += Size;
820 
821             /* Remember the previous option so we can update its next offset */
822             PreviousOption = Option;
823 
824             /* Consume the option from the buffer */
825             RemainingSize -= Size;
826 
827             /* Calculate where the next option should go */
828             Option = (PVOID)((ULONG_PTR)Option + Size);
829         }
830     }
831 
832     /* Now convert everything else */
833     AhCreateLoadOptionsList(CommandLine,
834                             &Entry->BcdData,
835                             RemainingSize,
836                             &TotalOptionSize,
837                             &PreviousOption,
838                             &Size);
839 
840 Quickie:
841     /* Return the final size */
842     *ResultLength = HeaderSize + TotalOptionSize;
843 }
844 
845 /*++
846  * @name EfiInitCreateInputParametersEx
847  *
848  *     The EfiInitCreateInputParametersEx routine converts UEFI entrypoint
849  *     parameters to the ones expected by Windows Boot Applications
850  *
851  * @param  ImageHandle
852  *         UEFI Image Handle for the current loaded application.
853  *
854  * @param  SystemTable
855  *         Pointer to the UEFI System Table.
856  *
857  * @return A PBOOT_APPLICATION_PARAMETER_BLOCK structure containing the data
858  *         from UEFI, translated to the Boot Library-compatible format.
859  *
860  *--*/
861 PBOOT_APPLICATION_PARAMETER_BLOCK
862 EfiInitCreateInputParametersEx (
863     _In_ EFI_HANDLE ImageHandle,
864     _In_ EFI_SYSTEM_TABLE *SystemTable
865     )
866 {
867     EFI_BOOT_SERVICES* BootServices;
868     EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
869     EFI_DEVICE_PATH_PROTOCOL *DevicePath;
870     PBL_FIRMWARE_DESCRIPTOR FirmwareData;
871     PBL_RETURN_ARGUMENTS ReturnArguments;
872     ULONG FirmwareOffset, ConsumedSize;
873     PBL_DEVICE_DESCRIPTOR AppDevice;
874     EFI_STATUS Status;
875 
876     /* Initialize the header with the signature and version */
877     EfiInitScratch.Signature[0] = BOOT_APPLICATION_SIGNATURE_1;
878     EfiInitScratch.Signature[1] = BOOT_APPLICATION_SIGNATURE_2;
879     EfiInitScratch.Version = BOOT_APPLICATION_VERSION;
880 
881     /* Set the image type to x86 */
882     EfiInitScratch.ImageType = EFI_IMAGE_MACHINE_IA32;
883 
884     /* Set the translation type to physical */
885     EfiInitScratch.MemoryTranslationType = BOOT_MEMORY_TRANSLATION_TYPE_PHYSICAL;
886 
887     /* Indicate that the data was converted from EFI */
888     BlpApplicationFlags |= BL_APPLICATION_FLAG_CONVERTED_FROM_EFI;
889 
890     /* Grab the loaded image protocol, which has our base and size */
891     BootServices = SystemTable->BootServices;
892     Status = BootServices->HandleProtocol(ImageHandle,
893                                           &EfiLoadedImageProtocol,
894                                           (VOID**)&LoadedImage);
895     if (Status != EFI_SUCCESS)
896     {
897         return NULL;
898     }
899 
900     /* Capture it in the boot application parameters */
901     EfiInitScratch.ImageBase = (ULONG_PTR)LoadedImage->ImageBase;
902     EfiInitScratch.ImageSize = (ULONG)LoadedImage->ImageSize;
903 
904     /* Now grab our device path protocol, so we can convert the path later on */
905     Status = BootServices->HandleProtocol(LoadedImage->DeviceHandle,
906                                           &EfiDevicePathProtocol,
907                                           (VOID**)&DevicePath);
908     if (Status != EFI_SUCCESS)
909     {
910         return NULL;
911     }
912 
913     /* The built-in boot memory data comes right after our block */
914     EfiInitScratch.MemoryDataOffset =
915         FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, BootMemoryData);
916 
917     /* Build the boot memory data structure, with 1 descriptor */
918     EfiInitScratch.BootMemoryData.Version = BL_MEMORY_DATA_VERSION;
919     EfiInitScratch.BootMemoryData.MdListOffset =
920         FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, MemEntry) -
921         EfiInitScratch.MemoryDataOffset;
922     EfiInitScratch.BootMemoryData.DescriptorSize = sizeof(BL_MEMORY_DESCRIPTOR);
923     EfiInitScratch.BootMemoryData.DescriptorCount = 1;
924     EfiInitScratch.BootMemoryData.DescriptorOffset = FIELD_OFFSET(BL_MEMORY_DESCRIPTOR, BasePage);
925 
926     /* Build the memory entry descriptor for this image itself */
927     EfiInitScratch.MemEntry.Flags = BlMemoryWriteBack;
928     EfiInitScratch.MemEntry.Type = BlLoaderMemory;
929     EfiInitScratch.MemEntry.BasePage = EfiInitScratch.ImageBase >> PAGE_SHIFT;
930     EfiInitScratch.MemEntry.PageCount = ALIGN_UP_BY(EfiInitScratch.ImageSize, PAGE_SIZE) >> PAGE_SHIFT;
931 
932     /* The built-in application entry comes right after the memory descriptor*/
933     EfiInitScratch.AppEntryOffset =
934         FIELD_OFFSET(BOOT_APPLICATION_PARAMETER_BLOCK_SCRATCH, AppEntry);
935 
936     /* Go and build it */
937     EfiInitpCreateApplicationEntry(SystemTable,
938                                    (PBL_APPLICATION_ENTRY)&EfiInitScratch.AppEntry,
939                                    sizeof(EfiInitScratch.AppEntry),
940                                    DevicePath,
941                                    LoadedImage->FilePath,
942                                    LoadedImage->LoadOptions,
943                                    LoadedImage->LoadOptionsSize,
944                                    EfiInitScratch.MemEntry.PageCount,
945                                    &ConsumedSize,
946                                    &AppDevice);
947 
948     /* Boot device information comes right after the application entry */
949     EfiInitScratch.BootDeviceOffset = ConsumedSize + EfiInitScratch.AppEntryOffset;
950 
951     /* Check if we have a boot device */
952     if (AppDevice != NULL)
953     {
954         /* We do -- copy it */
955         RtlCopyMemory(EfiInitScratch.AppEntry + ConsumedSize,
956                       AppDevice,
957                       AppDevice->Size);
958 
959         /* Firmware data follows right after the boot device entry */
960         FirmwareOffset = AppDevice->Size + EfiInitScratch.BootDeviceOffset;
961     }
962     else
963     {
964         /* We do not, so zero out the space where a full boot device structure would fit */
965         RtlZeroMemory(EfiInitScratch.AppEntry + ConsumedSize,
966                       sizeof(BL_DEVICE_DESCRIPTOR));
967 
968         /* And start the firmware data past that */
969         FirmwareOffset = EfiInitScratch.BootDeviceOffset + sizeof(BL_DEVICE_DESCRIPTOR);
970     }
971 
972     /* Set the computed firmware data offset */
973     EfiInitScratch.FirmwareParametersOffset = FirmwareOffset;
974 
975     /* Fill out the firmware data that's there */
976     FirmwareData = (PVOID)((ULONG_PTR)&EfiInitScratch + EfiInitScratch.FirmwareParametersOffset);
977     FirmwareData->Version = BL_FIRMWARE_DESCRIPTOR_VERSION;
978     FirmwareData->ImageHandle = ImageHandle;
979     FirmwareData->SystemTable = SystemTable;
980 
981     /* Finally, set the return argument offset */
982     EfiInitScratch.ReturnArgumentsOffset = FirmwareOffset + sizeof(BL_FIRMWARE_DESCRIPTOR);
983 
984     /* And fill out the return argument data */
985     ReturnArguments = (PVOID)((ULONG_PTR)&EfiInitScratch + EfiInitScratch.ReturnArgumentsOffset);
986     ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
987 
988     /* We're done, compute the final size and return the block */
989     EfiInitScratch.Size = EfiInitScratch.ReturnArgumentsOffset + sizeof(BL_RETURN_ARGUMENTS);
990     return (PBOOT_APPLICATION_PARAMETER_BLOCK)&EfiInitScratch;
991 }
992 
993 /*++
994  * @name EfiEntry
995  *
996  *     The EfiEntry routine implements the UEFI entrypoint for the application.
997  *
998  * @param  ImageHandle
999  *         UEFI Image Handle for the current loaded application.
1000  *
1001  * @param  SystemTable
1002  *         Pointer to the UEFI System Table.
1003  *
1004  * @return EFI_SUCCESS if the image was loaded correctly, relevant error code
1005  *         otherwise.
1006  *
1007  *--*/
1008 EFI_STATUS
1009 EFIAPI
1010 EfiEntry (
1011     _In_ EFI_HANDLE ImageHandle,
1012     _In_ EFI_SYSTEM_TABLE *SystemTable
1013     )
1014 {
1015     NTSTATUS Status;
1016     PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters;
1017 
1018     /* Convert EFI parameters to Windows Boot Application parameters */
1019     BootParameters = EfiInitCreateInputParametersEx(ImageHandle, SystemTable);
1020     if (BootParameters != NULL)
1021     {
1022         /* Conversion was good -- call the Boot Manager Entrypoint */
1023         Status = BmMain(BootParameters);
1024     }
1025     else
1026     {
1027         /* Conversion failed, bail out */
1028         Status = STATUS_INVALID_PARAMETER;
1029     }
1030 
1031     /* Convert the NT status code to an EFI code */
1032     return EfiGetEfiStatusCode(Status);
1033 }
1034 
1035