[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))
257 {
258 return Status;
259 }
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 = DiskDevice;
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 = LegacyPartitionDevice;
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 = PartitionDevice;
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 /* Otherwise, raw boot is not supported */
470 DeviceEntry->DeviceType = PartitionDevice;
471 DeviceEntry->Partition.Disk.Type = LocalDevice;
472 DeviceEntry->Partition.Disk.HardDisk.PartitionType = RawPartition;
473 DeviceEntry->Partition.Disk.HardDisk.Raw.DiskNumber = 0;
474 }
475 else if (DeviceNode->SubType == MEDIA_CDROM_DP)
476 {
477 /* Set the right type for a CDROM */
478 DeviceEntry->DeviceType = DiskDevice;
479 DeviceEntry->Local.Type = CdRomDevice;
480
481 /* Set the drive number to zero */
482 DeviceEntry->Local.FloppyDisk.DriveNumber = 0;
483 return STATUS_SUCCESS;
484 }
485
486 /* Fail anything else */
487 default:
488 break;
489 }
490
491 /* Return here only on failure */
492 return Status;
493 }
494
495 /*++
496 * @name EfiInitpConvertEfiDevicePath
497 *
498 * The EfiInitpConvertEfiDevicePath routine
499 *
500 * @param DevicePath
501 * UEFI Image Handle for the current loaded application.
502 *
503 * @param DeviceType
504 * Pointer to the UEFI System Table.
505 *
506 * @param Option
507 * Pointer to the UEFI System Table.
508 *
509 * @param MaximumLength
510 * Pointer to the UEFI System Table.
511 *
512 * @return None
513 *
514 *--*/
515 NTSTATUS
516 EfiInitpConvertEfiDevicePath (
517 _In_ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
518 _In_ ULONG DeviceType,
519 _In_ PBL_BCD_OPTION Option,
520 _In_ ULONG MaximumLength
521 )
522 {
523 PBCDE_DEVICE DeviceEntry;
524 NTSTATUS Status;
525
526 /* Make sure we have enough space for the option */
527 if (MaximumLength < sizeof(*Option))
528 {
529 Status = STATUS_INVALID_PARAMETER;
530 goto Quickie;
531 }
532
533 /* Zero out the option */
534 RtlZeroMemory(Option, sizeof(*Option));
535
536 /* Make sure we have enough space for the device entry */
537 if ((MaximumLength - sizeof(*Option)) < (ULONG)FIELD_OFFSET(BCDE_DEVICE, Device))
538 {
539 Status = STATUS_INVALID_PARAMETER;
540 goto Quickie;
541 }
542
543 /* Fill it out */
544 DeviceEntry = (PBCDE_DEVICE)(Option + 1);
545 Status = EfiInitTranslateDevicePath(DevicePath, &DeviceEntry->Device);
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(BCDE_DEVICE, Device) +
555 DeviceEntry->Device.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 PBCDE_DEVICE 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->Device;
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 oprtion */
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