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