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