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