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