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