TianoCore EDK2 master
Loading...
Searching...
No Matches
QemuBootOrderLib.c
Go to the documentation of this file.
1
11#include <Library/DebugLib.h>
16#include <Library/BaseLib.h>
17#include <Library/PrintLib.h>
21#include <Guid/GlobalVariable.h>
23
24#include "ExtraRootBusMap.h"
25
29#define TRANSLATION_OUTPUT_SIZE 0x100
30
35#define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40
36
40#define REQUIRED_PCI_OFW_NODES 2
41#define REQUIRED_MMIO_OFW_NODES 1
42#define EXAMINED_OFW_NODES 6
43
49BOOLEAN
51 IN CHAR8 Chr
52 )
53{
54 return (('0' <= Chr && Chr <= '9') ||
55 ('A' <= Chr && Chr <= 'Z') ||
56 ('a' <= Chr && Chr <= 'z')
57 );
58}
59
61BOOLEAN
62IsDriverNamePunct (
63 IN CHAR8 Chr
64 )
65{
66 return (Chr == ',' || Chr == '.' || Chr == '_' ||
67 Chr == '+' || Chr == '-'
68 );
69}
70
72BOOLEAN
73IsPrintNotDelim (
74 IN CHAR8 Chr
75 )
76{
77 return (32 <= Chr && Chr <= 126 &&
78 Chr != '/' && Chr != '@' && Chr != ':');
79}
80
84typedef struct {
85 CONST CHAR8 *Ptr; // not necessarily NUL-terminated
86 UINTN Len; // number of non-NUL characters
87} SUBSTRING;
88
104STATIC
105BOOLEAN
107 IN SUBSTRING Substring,
108 IN CONST CHAR8 *String
109 )
110{
111 UINTN Pos;
112 CONST CHAR8 *Chr;
113
114 Pos = 0;
115 Chr = String;
116
117 while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {
118 ++Pos;
119 ++Chr;
120 }
121
122 return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');
123}
124
164STATIC
165RETURN_STATUS
167 IN SUBSTRING UnitAddress,
168 OUT UINT64 *Result,
169 IN OUT UINTN *NumResults
170 )
171{
172 UINTN Entry; // number of entry currently being parsed
173 UINT64 EntryVal; // value being constructed for current entry
174 CHAR8 PrevChr; // UnitAddress character previously checked
175 UINTN Pos; // current position within UnitAddress
176 RETURN_STATUS Status;
177
178 Entry = 0;
179 EntryVal = 0;
180 PrevChr = ',';
181
182 for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {
183 CHAR8 Chr;
184 INT8 Val;
185
186 Chr = UnitAddress.Ptr[Pos];
187 Val = ('a' <= Chr && Chr <= 'f') ? (Chr - 'a' + 10) :
188 ('A' <= Chr && Chr <= 'F') ? (Chr - 'A' + 10) :
189 ('0' <= Chr && Chr <= '9') ? (Chr - '0') :
190 -1;
191
192 if (Val >= 0) {
193 if (EntryVal > 0xFFFFFFFFFFFFFFFull) {
195 }
196
197 EntryVal = LShiftU64 (EntryVal, 4) | Val;
198 } else if (Chr == ',') {
199 if (PrevChr == ',') {
201 }
202
203 if (Entry < *NumResults) {
204 Result[Entry] = EntryVal;
205 }
206
207 ++Entry;
208 EntryVal = 0;
209 } else {
211 }
212
213 PrevChr = Chr;
214 }
215
216 if (PrevChr == ',') {
218 }
219
220 if (Entry < *NumResults) {
221 Result[Entry] = EntryVal;
222 Status = RETURN_SUCCESS;
223 } else {
225 }
226
227 ++Entry;
228
229 *NumResults = Entry;
230 return Status;
231}
232
236typedef struct {
237 UINT16 *Data;
238 UINTN Allocated;
239 UINTN Produced;
240} BOOT_ORDER;
241
246typedef struct {
247 CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; // reference only, no
248 // ownership
249 BOOLEAN Appended; // has been added to a
250 // BOOT_ORDER?
252
269STATIC
270RETURN_STATUS
272 IN OUT BOOT_ORDER *BootOrder,
273 IN OUT ACTIVE_OPTION *ActiveOption
274 )
275{
276 if (BootOrder->Produced == BootOrder->Allocated) {
277 UINTN AllocatedNew;
278 UINT16 *DataNew;
279
280 ASSERT (BootOrder->Allocated > 0);
281 AllocatedNew = BootOrder->Allocated * 2;
282 DataNew = ReallocatePool (
283 BootOrder->Allocated * sizeof (*BootOrder->Data),
284 AllocatedNew * sizeof (*DataNew),
285 BootOrder->Data
286 );
287 if (DataNew == NULL) {
289 }
290
291 BootOrder->Allocated = AllocatedNew;
292 BootOrder->Data = DataNew;
293 }
294
295 BootOrder->Data[BootOrder->Produced++] =
296 (UINT16)ActiveOption->BootOption->OptionNumber;
297 ActiveOption->Appended = TRUE;
298 return RETURN_SUCCESS;
299}
300
326STATIC
327RETURN_STATUS
330 IN UINTN BootOptionCount,
331 OUT ACTIVE_OPTION **ActiveOption,
332 OUT UINTN *Count
333 )
334{
335 UINTN Index;
336 UINTN ScanMode;
337
338 *ActiveOption = NULL;
339
340 //
341 // Scan the list twice:
342 // - count active entries,
343 // - store links to active entries.
344 //
345 for (ScanMode = 0; ScanMode < 2; ++ScanMode) {
346 *Count = 0;
347 for (Index = 0; Index < BootOptionCount; Index++) {
348 if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {
349 if (ScanMode == 1) {
350 (*ActiveOption)[*Count].BootOption = &BootOptions[Index];
351 (*ActiveOption)[*Count].Appended = FALSE;
352 }
353
354 ++*Count;
355 }
356 }
357
358 if (ScanMode == 0) {
359 if (*Count == 0) {
360 return RETURN_NOT_FOUND;
361 }
362
363 *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);
364 if (*ActiveOption == NULL) {
366 }
367 }
368 }
369
370 return RETURN_SUCCESS;
371}
372
376typedef struct {
377 SUBSTRING DriverName;
378 SUBSTRING UnitAddress;
379 SUBSTRING DeviceArguments;
380} OFW_NODE;
381
427STATIC
428RETURN_STATUS
430 IN OUT CONST CHAR8 **Ptr,
431 OUT OFW_NODE *OfwNode,
432 OUT BOOLEAN *IsFinal
433 )
434{
435 BOOLEAN AcceptSlash = FALSE;
436
437 //
438 // A leading slash is expected. End of string is tolerated.
439 //
440 switch (**Ptr) {
441 case '\0':
442 return RETURN_NOT_FOUND;
443
444 case '/':
445 ++*Ptr;
446 break;
447
448 default:
450 }
451
452 //
453 // driver-name
454 //
455 OfwNode->DriverName.Ptr = *Ptr;
456 OfwNode->DriverName.Len = 0;
457 while (OfwNode->DriverName.Len < 32 &&
458 (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))
459 )
460 {
461 ++*Ptr;
462 ++OfwNode->DriverName.Len;
463 }
464
465 if ((OfwNode->DriverName.Len == 0) || (OfwNode->DriverName.Len == 32)) {
467 }
468
469 if (SubstringEq (OfwNode->DriverName, "rom")) {
470 //
471 // bug compatibility hack
472 //
473 // qemu passes fw_cfg filenames as rom unit address.
474 // The filenames have slashes:
475 // /rom@genroms/linuxboot_dma.bin
476 //
477 // Alow slashes in the unit address to avoid the parser trip up,
478 // so we can successfully parse the following lines (the rom
479 // entries themself are ignored).
480 //
481 AcceptSlash = TRUE;
482 }
483
484 //
485 // unit-address
486 //
487 if (**Ptr != '@') {
489 }
490
491 ++*Ptr;
492
493 OfwNode->UnitAddress.Ptr = *Ptr;
494 OfwNode->UnitAddress.Len = 0;
495 while (IsPrintNotDelim (**Ptr) || (AcceptSlash && **Ptr == '/')) {
496 ++*Ptr;
497 ++OfwNode->UnitAddress.Len;
498 }
499
500 if (OfwNode->UnitAddress.Len == 0) {
502 }
503
504 //
505 // device-arguments, may be omitted
506 //
507 OfwNode->DeviceArguments.Len = 0;
508 if (**Ptr == ':') {
509 ++*Ptr;
510 OfwNode->DeviceArguments.Ptr = *Ptr;
511
512 while (IsPrintNotDelim (**Ptr)) {
513 ++*Ptr;
514 ++OfwNode->DeviceArguments.Len;
515 }
516
517 if (OfwNode->DeviceArguments.Len == 0) {
519 }
520 } else {
521 OfwNode->DeviceArguments.Ptr = NULL;
522 }
523
524 switch (**Ptr) {
525 case '\n':
526 ++*Ptr;
527 //
528 // fall through
529 //
530
531 case '\0':
532 *IsFinal = TRUE;
533 break;
534
535 case '/':
536 *IsFinal = FALSE;
537 break;
538
539 default:
541 }
542
543 DEBUG ((
544 DEBUG_VERBOSE,
545 "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
546 __func__,
547 OfwNode->DriverName.Len,
548 OfwNode->DriverName.Ptr,
549 OfwNode->UnitAddress.Len,
550 OfwNode->UnitAddress.Ptr,
551 OfwNode->DeviceArguments.Len,
552 OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr
553 ));
554 return RETURN_SUCCESS;
555}
556
598STATIC
599RETURN_STATUS
601 IN CONST OFW_NODE *OfwNode,
602 IN UINTN NumNodes,
603 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
604 OUT CHAR16 *Translated,
605 IN OUT UINTN *TranslatedSize
606 )
607{
608 UINT32 PciRoot;
609 CHAR8 *Comma;
610 UINTN FirstNonBridge;
611 CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];
612 UINTN BridgesLen;
613 UINT64 PciDevFun[2];
614 UINTN NumEntries;
615 UINTN Written;
616
617 //
618 // Resolve the PCI root bus number.
619 //
620 // The initial OFW node for the main root bus (ie. bus number 0) is:
621 //
622 // /pci@i0cf8
623 //
624 // For extra root buses, the initial OFW node is
625 //
626 // /pci@i0cf8,4
627 // ^
628 // root bus serial number (not PCI bus number)
629 //
630 if ((NumNodes < REQUIRED_PCI_OFW_NODES) ||
631 !SubstringEq (OfwNode[0].DriverName, "pci")
632 )
633 {
634 return RETURN_UNSUPPORTED;
635 }
636
637 PciRoot = 0;
638 Comma = ScanMem8 (
639 OfwNode[0].UnitAddress.Ptr,
640 OfwNode[0].UnitAddress.Len,
641 ','
642 );
643 if (Comma != NULL) {
644 SUBSTRING PciRootSerialSubString;
645 UINT64 PciRootSerial;
646
647 //
648 // Parse the root bus serial number from the unit address after the comma.
649 //
650 PciRootSerialSubString.Ptr = Comma + 1;
651 PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -
652 (PciRootSerialSubString.Ptr -
653 OfwNode[0].UnitAddress.Ptr);
654 NumEntries = 1;
655 if (RETURN_ERROR (
657 PciRootSerialSubString,
658 &PciRootSerial,
659 &NumEntries
660 )
661 ))
662 {
663 return RETURN_UNSUPPORTED;
664 }
665
666 //
667 // Map the extra root bus's serial number to its actual bus number.
668 //
669 if (EFI_ERROR (
671 ExtraPciRoots,
672 PciRootSerial,
673 &PciRoot
674 )
675 ))
676 {
678 }
679 }
680
681 //
682 // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
683 //
684 // pci-bridge@1e[,0]
685 // ^ ^
686 // PCI slot & function on the parent, holding the bridge
687 //
688 // and the UEFI device path node is:
689 //
690 // Pci(0x1E,0x0)
691 //
692 FirstNonBridge = 1;
693 Bridges[0] = L'\0';
694 BridgesLen = 0;
695 do {
696 UINT64 BridgeDevFun[2];
697 UINTN BridgesFreeBytes;
698
699 if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {
700 break;
701 }
702
703 BridgeDevFun[1] = 0;
704 NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];
706 OfwNode[FirstNonBridge].UnitAddress,
707 BridgeDevFun,
708 &NumEntries
709 ) != RETURN_SUCCESS)
710 {
711 return RETURN_UNSUPPORTED;
712 }
713
714 BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];
715 Written = UnicodeSPrintAsciiFormat (
716 Bridges + BridgesLen,
717 BridgesFreeBytes,
718 "/Pci(0x%Lx,0x%Lx)",
719 BridgeDevFun[0],
720 BridgeDevFun[1]
721 );
722 BridgesLen += Written;
723
724 //
725 // There's no way to differentiate between "completely used up without
726 // truncation" and "truncated", so treat the former as the latter.
727 //
728 if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {
729 return RETURN_UNSUPPORTED;
730 }
731
732 ++FirstNonBridge;
733 } while (FirstNonBridge < NumNodes);
734
735 if (FirstNonBridge == NumNodes) {
736 return RETURN_UNSUPPORTED;
737 }
738
739 //
740 // Parse the OFW nodes starting with the first non-bridge node.
741 //
742 PciDevFun[1] = 0;
743 NumEntries = ARRAY_SIZE (PciDevFun);
745 OfwNode[FirstNonBridge].UnitAddress,
746 PciDevFun,
747 &NumEntries
748 ) != RETURN_SUCCESS
749 )
750 {
751 return RETURN_UNSUPPORTED;
752 }
753
754 if ((NumNodes >= FirstNonBridge + 3) &&
755 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&
756 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
757 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
758 )
759 {
760 //
761 // OpenFirmware device path (IDE disk, IDE CD-ROM):
762 //
763 // /pci@i0cf8/ide@1,1/drive@0/disk@0
764 // ^ ^ ^ ^ ^
765 // | | | | master or slave
766 // | | | primary or secondary
767 // | PCI slot & function holding IDE controller
768 // PCI root at system bus port, PIO
769 //
770 // UEFI device path:
771 //
772 // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
773 // ^
774 // fixed LUN
775 //
776 UINT64 Secondary;
777 UINT64 Slave;
778
779 NumEntries = 1;
781 OfwNode[FirstNonBridge + 1].UnitAddress,
782 &Secondary,
783 &NumEntries
784 ) != RETURN_SUCCESS) ||
785 (Secondary > 1) ||
787 OfwNode[FirstNonBridge + 2].UnitAddress,
788 &Slave,
789 &NumEntries // reuse after previous single-element call
790 ) != RETURN_SUCCESS) ||
791 (Slave > 1)
792 )
793 {
794 return RETURN_UNSUPPORTED;
795 }
796
797 Written = UnicodeSPrintAsciiFormat (
798 Translated,
799 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
800 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
801 PciRoot,
802 Bridges,
803 PciDevFun[0],
804 PciDevFun[1],
805 Secondary ? "Secondary" : "Primary",
806 Slave ? "Slave" : "Master"
807 );
808 } else if ((NumNodes >= FirstNonBridge + 3) &&
809 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&
810 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
811 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
812 )
813 {
814 //
815 // OpenFirmware device path (Q35 SATA disk and CD-ROM):
816 //
817 // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
818 // ^ ^ ^ ^ ^
819 // | | | | device number (fixed 0)
820 // | | | channel (port) number
821 // | PCI slot & function holding SATA HBA
822 // PCI root at system bus port, PIO
823 //
824 // UEFI device path:
825 //
826 // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
827 // ^ ^ ^
828 // | | LUN (always 0 on Q35)
829 // | port multiplier port number,
830 // | always 0xFFFF on Q35
831 // channel (port) number
832 //
833 UINT64 Channel;
834
835 NumEntries = 1;
836 if (RETURN_ERROR (
838 OfwNode[FirstNonBridge + 1].UnitAddress,
839 &Channel,
840 &NumEntries
841 )
842 ))
843 {
844 return RETURN_UNSUPPORTED;
845 }
846
847 Written = UnicodeSPrintAsciiFormat (
848 Translated,
849 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
850 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
851 PciRoot,
852 Bridges,
853 PciDevFun[0],
854 PciDevFun[1],
855 Channel
856 );
857 } else if ((NumNodes >= FirstNonBridge + 3) &&
858 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&
859 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&
860 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")
861 )
862 {
863 //
864 // OpenFirmware device path (floppy disk):
865 //
866 // /pci@i0cf8/isa@1/fdc@03f0/floppy@0
867 // ^ ^ ^ ^
868 // | | | A: or B:
869 // | | ISA controller io-port (hex)
870 // | PCI slot holding ISA controller
871 // PCI root at system bus port, PIO
872 //
873 // UEFI device path:
874 //
875 // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
876 // ^
877 // ACPI UID
878 //
879 UINT64 AcpiUid;
880
881 NumEntries = 1;
883 OfwNode[FirstNonBridge + 2].UnitAddress,
884 &AcpiUid,
885 &NumEntries
886 ) != RETURN_SUCCESS) ||
887 (AcpiUid > 1)
888 )
889 {
890 return RETURN_UNSUPPORTED;
891 }
892
893 Written = UnicodeSPrintAsciiFormat (
894 Translated,
895 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
896 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
897 PciRoot,
898 Bridges,
899 PciDevFun[0],
900 PciDevFun[1],
901 AcpiUid
902 );
903 } else if ((NumNodes >= FirstNonBridge + 2) &&
904 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
905 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")
906 )
907 {
908 //
909 // OpenFirmware device path (virtio-blk disk):
910 //
911 // /pci@i0cf8/scsi@6[,3]/disk@0,0
912 // ^ ^ ^ ^ ^
913 // | | | fixed
914 // | | PCI function corresponding to disk (optional)
915 // | PCI slot holding disk
916 // PCI root at system bus port, PIO
917 //
918 // UEFI device path prefix:
919 //
920 // PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
921 // PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
922 //
923 Written = UnicodeSPrintAsciiFormat (
924 Translated,
925 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
926 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
927 PciRoot,
928 Bridges,
929 PciDevFun[0],
930 PciDevFun[1]
931 );
932 } else if ((NumNodes >= FirstNonBridge + 3) &&
933 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
934 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&
935 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
936 )
937 {
938 //
939 // OpenFirmware device path (virtio-scsi disk):
940 //
941 // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
942 // ^ ^ ^ ^ ^
943 // | | | | LUN
944 // | | | target
945 // | | channel (unused, fixed 0)
946 // | PCI slot[, function] holding SCSI controller
947 // PCI root at system bus port, PIO
948 //
949 // UEFI device path prefix:
950 //
951 // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
952 // -- if PCI function is 0 or absent
953 // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
954 // -- if PCI function is present and nonzero
955 //
956 UINT64 TargetLun[2];
957
958 TargetLun[1] = 0;
959 NumEntries = ARRAY_SIZE (TargetLun);
961 OfwNode[FirstNonBridge + 2].UnitAddress,
962 TargetLun,
963 &NumEntries
964 ) != RETURN_SUCCESS
965 )
966 {
967 return RETURN_UNSUPPORTED;
968 }
969
970 Written = UnicodeSPrintAsciiFormat (
971 Translated,
972 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
973 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
974 PciRoot,
975 Bridges,
976 PciDevFun[0],
977 PciDevFun[1],
978 TargetLun[0],
979 TargetLun[1]
980 );
981 } else if ((NumNodes >= FirstNonBridge + 2) &&
982 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&
983 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")
984 )
985 {
986 //
987 // OpenFirmware device path (NVMe device):
988 //
989 // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
990 // ^ ^ ^ ^ ^
991 // | | | | Extended Unique Identifier
992 // | | | | (EUI-64), big endian interp.
993 // | | | namespace ID
994 // | PCI slot & function holding NVMe controller
995 // PCI root at system bus port, PIO
996 //
997 // UEFI device path:
998 //
999 // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
1000 // ^ ^
1001 // | octets of the EUI-64
1002 // | in address order
1003 // namespace ID
1004 //
1005 UINT64 Namespace[2];
1006 UINTN RequiredEntries;
1007 UINT8 *Eui64;
1008
1009 RequiredEntries = ARRAY_SIZE (Namespace);
1010 NumEntries = RequiredEntries;
1012 OfwNode[FirstNonBridge + 1].UnitAddress,
1013 Namespace,
1014 &NumEntries
1015 ) != RETURN_SUCCESS) ||
1016 (NumEntries != RequiredEntries) ||
1017 (Namespace[0] == 0) ||
1018 (Namespace[0] >= MAX_UINT32)
1019 )
1020 {
1021 return RETURN_UNSUPPORTED;
1022 }
1023
1024 Eui64 = (UINT8 *)&Namespace[1];
1025 Written = UnicodeSPrintAsciiFormat (
1026 Translated,
1027 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1028 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
1029 "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
1030 PciRoot,
1031 Bridges,
1032 PciDevFun[0],
1033 PciDevFun[1],
1034 Namespace[0],
1035 Eui64[7],
1036 Eui64[6],
1037 Eui64[5],
1038 Eui64[4],
1039 Eui64[3],
1040 Eui64[2],
1041 Eui64[1],
1042 Eui64[0]
1043 );
1044 } else if ((NumNodes >= FirstNonBridge + 2) &&
1045 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "usb") &&
1046 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "storage"))
1047 {
1048 //
1049 // OpenFirmware device path (usb-storage device in XHCI port):
1050 //
1051 // /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0
1052 // ^ ^ ^ ^ ^ ^ ^
1053 // | | | | fixed fixed
1054 // | | | XHCI port number, 1-based
1055 // | | PCI function corresponding to XHCI (optional)
1056 // | PCI slot holding XHCI
1057 // PCI root at system bus port, PIO
1058 //
1059 // UEFI device path prefix:
1060 //
1061 // PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)
1062 // ^ ^
1063 // | XHCI port number in 0-based notation
1064 // 0x0 if PCI function is 0, or absent from OFW
1065 //
1066 RETURN_STATUS ParseStatus;
1067 UINT64 OneBasedXhciPort;
1068
1069 NumEntries = 1;
1070 ParseStatus = ParseUnitAddressHexList (
1071 OfwNode[FirstNonBridge + 1].UnitAddress,
1072 &OneBasedXhciPort,
1073 &NumEntries
1074 );
1075 if (RETURN_ERROR (ParseStatus) || (OneBasedXhciPort == 0)) {
1076 return RETURN_UNSUPPORTED;
1077 }
1078
1079 Written = UnicodeSPrintAsciiFormat (
1080 Translated,
1081 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1082 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",
1083 PciRoot,
1084 Bridges,
1085 PciDevFun[0],
1086 PciDevFun[1],
1087 OneBasedXhciPort - 1
1088 );
1089 } else {
1090 //
1091 // Generic OpenFirmware device path for PCI devices:
1092 //
1093 // /pci@i0cf8/ethernet@3[,2]
1094 // ^ ^
1095 // | PCI slot[, function] holding Ethernet card
1096 // PCI root at system bus port, PIO
1097 //
1098 // UEFI device path prefix (dependent on presence of nonzero PCI function):
1099 //
1100 // PciRoot(0x0)/Pci(0x3,0x0)
1101 // PciRoot(0x0)/Pci(0x3,0x2)
1102 //
1103 Written = UnicodeSPrintAsciiFormat (
1104 Translated,
1105 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1106 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
1107 PciRoot,
1108 Bridges,
1109 PciDevFun[0],
1110 PciDevFun[1]
1111 );
1112 }
1113
1114 //
1115 // There's no way to differentiate between "completely used up without
1116 // truncation" and "truncated", so treat the former as the latter, and return
1117 // success only for "some room left unused".
1118 //
1119 if (Written + 1 < *TranslatedSize) {
1120 *TranslatedSize = Written;
1121 return RETURN_SUCCESS;
1122 }
1123
1125}
1126
1127//
1128// A type providing easy raw access to the base address of a virtio-mmio
1129// transport.
1130//
1131typedef union {
1132 UINT64 Uint64;
1133 UINT8 Raw[8];
1135
1168STATIC
1169RETURN_STATUS
1171 IN CONST OFW_NODE *OfwNode,
1172 IN UINTN NumNodes,
1173 OUT CHAR16 *Translated,
1174 IN OUT UINTN *TranslatedSize
1175 )
1176{
1177 VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;
1178 CHAR16 VenHwString[60 + 1];
1179 UINTN NumEntries;
1180 UINTN Written;
1181
1182 //
1183 // Get the base address of the virtio-mmio transport.
1184 //
1185 if ((NumNodes < REQUIRED_MMIO_OFW_NODES) ||
1186 !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")
1187 )
1188 {
1189 return RETURN_UNSUPPORTED;
1190 }
1191
1192 NumEntries = 1;
1194 OfwNode[0].UnitAddress,
1195 &VirtioMmioBase.Uint64,
1196 &NumEntries
1197 ) != RETURN_SUCCESS
1198 )
1199 {
1200 return RETURN_UNSUPPORTED;
1201 }
1202
1204 VenHwString,
1205 sizeof VenHwString,
1206 "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)",
1207 &gVirtioMmioTransportGuid,
1208 VirtioMmioBase.Raw[0],
1209 VirtioMmioBase.Raw[1],
1210 VirtioMmioBase.Raw[2],
1211 VirtioMmioBase.Raw[3],
1212 VirtioMmioBase.Raw[4],
1213 VirtioMmioBase.Raw[5],
1214 VirtioMmioBase.Raw[6],
1215 VirtioMmioBase.Raw[7]
1216 );
1217
1218 if ((NumNodes >= 2) &&
1219 SubstringEq (OfwNode[1].DriverName, "disk"))
1220 {
1221 //
1222 // OpenFirmware device path (virtio-blk disk):
1223 //
1224 // /virtio-mmio@000000000a003c00/disk@0,0
1225 // ^ ^ ^
1226 // | fixed
1227 // base address of virtio-mmio register block
1228 //
1229 // UEFI device path prefix:
1230 //
1231 // <VenHwString>
1232 //
1233 Written = UnicodeSPrintAsciiFormat (
1234 Translated,
1235 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1236 "%s",
1237 VenHwString
1238 );
1239 } else if ((NumNodes >= 3) &&
1240 SubstringEq (OfwNode[1].DriverName, "channel") &&
1241 SubstringEq (OfwNode[2].DriverName, "disk"))
1242 {
1243 //
1244 // OpenFirmware device path (virtio-scsi disk):
1245 //
1246 // /virtio-mmio@000000000a003a00/channel@0/disk@2,3
1247 // ^ ^ ^ ^
1248 // | | | LUN
1249 // | | target
1250 // | channel (unused, fixed 0)
1251 // base address of virtio-mmio register block
1252 //
1253 // UEFI device path prefix:
1254 //
1255 // <VenHwString>/Scsi(0x2,0x3)
1256 //
1257 UINT64 TargetLun[2];
1258
1259 TargetLun[1] = 0;
1260 NumEntries = ARRAY_SIZE (TargetLun);
1262 OfwNode[2].UnitAddress,
1263 TargetLun,
1264 &NumEntries
1265 ) != RETURN_SUCCESS
1266 )
1267 {
1268 return RETURN_UNSUPPORTED;
1269 }
1270
1271 Written = UnicodeSPrintAsciiFormat (
1272 Translated,
1273 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1274 "%s/Scsi(0x%Lx,0x%Lx)",
1275 VenHwString,
1276 TargetLun[0],
1277 TargetLun[1]
1278 );
1279 } else if ((NumNodes >= 2) &&
1280 SubstringEq (OfwNode[1].DriverName, "ethernet-phy"))
1281 {
1282 //
1283 // OpenFirmware device path (virtio-net NIC):
1284 //
1285 // /virtio-mmio@000000000a003e00/ethernet-phy@0
1286 // ^ ^
1287 // | fixed
1288 // base address of virtio-mmio register block
1289 //
1290 // UEFI device path prefix:
1291 //
1292 // <VenHwString>
1293 //
1294 Written = UnicodeSPrintAsciiFormat (
1295 Translated,
1296 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1297 "%s",
1298 VenHwString
1299 );
1300 } else {
1301 return RETURN_UNSUPPORTED;
1302 }
1303
1304 //
1305 // There's no way to differentiate between "completely used up without
1306 // truncation" and "truncated", so treat the former as the latter, and return
1307 // success only for "some room left unused".
1308 //
1309 if (Written + 1 < *TranslatedSize) {
1310 *TranslatedSize = Written;
1311 return RETURN_SUCCESS;
1312 }
1313
1315}
1316
1358STATIC
1359RETURN_STATUS
1361 IN CONST OFW_NODE *OfwNode,
1362 IN UINTN NumNodes,
1363 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
1364 OUT CHAR16 *Translated,
1365 IN OUT UINTN *TranslatedSize
1366 )
1367{
1368 RETURN_STATUS Status;
1369
1370 Status = RETURN_UNSUPPORTED;
1371
1372 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
1373 Status = TranslatePciOfwNodes (
1374 OfwNode,
1375 NumNodes,
1376 ExtraPciRoots,
1377 Translated,
1378 TranslatedSize
1379 );
1380 }
1381
1382 if ((Status == RETURN_UNSUPPORTED) &&
1383 FeaturePcdGet (PcdQemuBootOrderMmioTranslation))
1384 {
1385 Status = TranslateMmioOfwNodes (
1386 OfwNode,
1387 NumNodes,
1388 Translated,
1389 TranslatedSize
1390 );
1391 }
1392
1393 return Status;
1394}
1395
1455STATIC
1456RETURN_STATUS
1458 IN OUT CONST CHAR8 **Ptr,
1459 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
1460 OUT CHAR16 *Translated,
1461 IN OUT UINTN *TranslatedSize
1462 )
1463{
1464 UINTN NumNodes;
1465 RETURN_STATUS Status;
1466 OFW_NODE Node[EXAMINED_OFW_NODES];
1467 BOOLEAN IsFinal;
1468 OFW_NODE Skip;
1469
1470 IsFinal = FALSE;
1471 NumNodes = 0;
1472 if (AsciiStrCmp (*Ptr, "HALT") == 0) {
1473 *Ptr += 4;
1474 Status = RETURN_NOT_FOUND;
1475 } else {
1476 Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);
1477 }
1478
1479 if (Status == RETURN_NOT_FOUND) {
1480 DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __func__));
1481 return RETURN_NOT_FOUND;
1482 }
1483
1484 while (Status == RETURN_SUCCESS && !IsFinal) {
1485 ++NumNodes;
1486 Status = ParseOfwNode (
1487 Ptr,
1488 (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,
1489 &IsFinal
1490 );
1491 }
1492
1493 switch (Status) {
1494 case RETURN_SUCCESS:
1495 ++NumNodes;
1496 break;
1497
1499 DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __func__));
1501
1502 default:
1503 ASSERT (0);
1504 }
1505
1506 Status = TranslateOfwNodes (
1507 Node,
1508 NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,
1509 ExtraPciRoots,
1510 Translated,
1511 TranslatedSize
1512 );
1513 switch (Status) {
1514 case RETURN_SUCCESS:
1515 DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __func__, Translated));
1516 break;
1517
1519 DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __func__));
1520 break;
1521
1522 case RETURN_UNSUPPORTED:
1523 DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __func__));
1524 break;
1525
1527 DEBUG ((
1528 DEBUG_VERBOSE,
1529 "%a: logic error / system state mismatch\n",
1530 __func__
1531 ));
1532 break;
1533
1534 default:
1535 ASSERT (0);
1536 }
1537
1538 return Status;
1539}
1540
1569RETURN_STATUS
1570EFIAPI
1572 VOID
1573 )
1574{
1575 RETURN_STATUS Status;
1576 FIRMWARE_CONFIG_ITEM FwCfgItem;
1577 UINTN FwCfgSize;
1578 CHAR8 *FwCfg;
1579 EFI_STATUS EfiStatus;
1580 EXTRA_ROOT_BUS_MAP *ExtraPciRoots;
1581 CONST CHAR8 *FwCfgPtr;
1582 UINTN NumConnected;
1583 UINTN TranslatedSize;
1584 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
1585
1586 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
1587 if (RETURN_ERROR (Status)) {
1588 return Status;
1589 }
1590
1591 if (FwCfgSize == 0) {
1592 return RETURN_NOT_FOUND;
1593 }
1594
1595 FwCfg = AllocatePool (FwCfgSize);
1596 if (FwCfg == NULL) {
1598 }
1599
1600 QemuFwCfgSelectItem (FwCfgItem);
1601 QemuFwCfgReadBytes (FwCfgSize, FwCfg);
1602 if (FwCfg[FwCfgSize - 1] != '\0') {
1603 Status = RETURN_INVALID_PARAMETER;
1604 goto FreeFwCfg;
1605 }
1606
1607 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __func__));
1608 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
1609 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __func__));
1610
1611 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
1612 EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);
1613 if (EFI_ERROR (EfiStatus)) {
1614 Status = (RETURN_STATUS)EfiStatus;
1615 goto FreeFwCfg;
1616 }
1617 } else {
1618 ExtraPciRoots = NULL;
1619 }
1620
1621 //
1622 // Translate each OpenFirmware path to a UEFI devpath prefix.
1623 //
1624 FwCfgPtr = FwCfg;
1625 NumConnected = 0;
1626 TranslatedSize = ARRAY_SIZE (Translated);
1627 Status = TranslateOfwPath (
1628 &FwCfgPtr,
1629 ExtraPciRoots,
1630 Translated,
1631 &TranslatedSize
1632 );
1633 while (!RETURN_ERROR (Status)) {
1634 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1635 EFI_HANDLE Controller;
1636
1637 //
1638 // Convert the UEFI devpath prefix to binary representation.
1639 //
1640 ASSERT (Translated[TranslatedSize] == L'\0');
1641 DevicePath = ConvertTextToDevicePath (Translated);
1642 if (DevicePath == NULL) {
1643 Status = RETURN_OUT_OF_RESOURCES;
1644 goto FreeExtraPciRoots;
1645 }
1646
1647 //
1648 // Advance along DevicePath, connecting the nodes individually, and asking
1649 // drivers not to produce sibling nodes. Retrieve the controller handle
1650 // associated with the full DevicePath -- this is the device that QEMU's
1651 // OFW devpath refers to.
1652 //
1653 EfiStatus = EfiBootManagerConnectDevicePath (DevicePath, &Controller);
1654 FreePool (DevicePath);
1655 if (EFI_ERROR (EfiStatus)) {
1656 Status = (RETURN_STATUS)EfiStatus;
1657 goto FreeExtraPciRoots;
1658 }
1659
1660 //
1661 // Because QEMU's OFW devpaths have lesser expressive power than UEFI
1662 // devpaths (i.e., DevicePath is considered a prefix), connect the tree
1663 // rooted at Controller, recursively. If no children are produced
1664 // (EFI_NOT_FOUND), that's OK.
1665 //
1666 EfiStatus = gBS->ConnectController (Controller, NULL, NULL, TRUE);
1667 if (EFI_ERROR (EfiStatus) && (EfiStatus != EFI_NOT_FOUND)) {
1668 Status = (RETURN_STATUS)EfiStatus;
1669 goto FreeExtraPciRoots;
1670 }
1671
1672 ++NumConnected;
1673 //
1674 // Move to the next OFW devpath.
1675 //
1676 TranslatedSize = ARRAY_SIZE (Translated);
1677 Status = TranslateOfwPath (
1678 &FwCfgPtr,
1679 ExtraPciRoots,
1680 Translated,
1681 &TranslatedSize
1682 );
1683 }
1684
1685 if ((Status == RETURN_NOT_FOUND) && (NumConnected > 0)) {
1686 DEBUG ((
1687 DEBUG_INFO,
1688 "%a: %Lu OpenFirmware device path(s) connected\n",
1689 __func__,
1690 (UINT64)NumConnected
1691 ));
1692 Status = RETURN_SUCCESS;
1693 }
1694
1695FreeExtraPciRoots:
1696 if (ExtraPciRoots != NULL) {
1697 DestroyExtraRootBusMap (ExtraPciRoots);
1698 }
1699
1700FreeFwCfg:
1701 FreePool (FwCfg);
1702
1703 return Status;
1704}
1705
1714VOID
1715EFIAPI
1717 VOID
1718 )
1719{
1720 RETURN_STATUS Status;
1721 FIRMWARE_CONFIG_ITEM FwCfgItem;
1722 UINTN FwCfgSize;
1723 CHAR8 *FwCfg;
1724 EFI_STATUS EfiStatus;
1725 EXTRA_ROOT_BUS_MAP *ExtraPciRoots;
1726 CONST CHAR8 *FwCfgPtr;
1727 UINTN TranslatedSize;
1728 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
1729 UINTN VariableIndex = 0;
1730 CHAR16 VariableName[20];
1731
1732 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
1733 if (RETURN_ERROR (Status)) {
1734 return;
1735 }
1736
1737 if (FwCfgSize == 0) {
1738 return;
1739 }
1740
1741 FwCfg = AllocatePool (FwCfgSize);
1742 if (FwCfg == NULL) {
1743 return;
1744 }
1745
1746 QemuFwCfgSelectItem (FwCfgItem);
1747 QemuFwCfgReadBytes (FwCfgSize, FwCfg);
1748 if (FwCfg[FwCfgSize - 1] != '\0') {
1749 Status = RETURN_INVALID_PARAMETER;
1750 goto FreeFwCfg;
1751 }
1752
1753 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __func__));
1754 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
1755 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __func__));
1756
1757 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
1758 EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);
1759 if (EFI_ERROR (EfiStatus)) {
1760 Status = (RETURN_STATUS)EfiStatus;
1761 goto FreeFwCfg;
1762 }
1763 } else {
1764 ExtraPciRoots = NULL;
1765 }
1766
1767 //
1768 // Translate each OpenFirmware path to a UEFI devpath prefix.
1769 //
1770 FwCfgPtr = FwCfg;
1771 TranslatedSize = ARRAY_SIZE (Translated);
1772 Status = TranslateOfwPath (
1773 &FwCfgPtr,
1774 ExtraPciRoots,
1775 Translated,
1776 &TranslatedSize
1777 );
1778 while (Status == EFI_SUCCESS ||
1779 Status == EFI_UNSUPPORTED)
1780 {
1781 if (Status == EFI_SUCCESS) {
1782 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1783
1784 //
1785 // Convert the UEFI devpath prefix to binary representation.
1786 //
1787 ASSERT (Translated[TranslatedSize] == L'\0');
1788 DevicePath = ConvertTextToDevicePath (Translated);
1789 if (DevicePath == NULL) {
1790 Status = RETURN_OUT_OF_RESOURCES;
1791 goto FreeExtraPciRoots;
1792 }
1793
1795 VariableName,
1796 sizeof (VariableName),
1797 L"VMMBootOrder%04x",
1798 VariableIndex++
1799 );
1800 DEBUG ((DEBUG_INFO, "%a: %s = %s\n", __func__, VariableName, Translated));
1801 gRT->SetVariable (
1802 VariableName,
1803 &gVMMBootOrderGuid,
1804 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1805 GetDevicePathSize (DevicePath),
1806 DevicePath
1807 );
1808 FreePool (DevicePath);
1809 }
1810
1811 //
1812 // Move to the next OFW devpath.
1813 //
1814 TranslatedSize = ARRAY_SIZE (Translated);
1815 Status = TranslateOfwPath (
1816 &FwCfgPtr,
1817 ExtraPciRoots,
1818 Translated,
1819 &TranslatedSize
1820 );
1821 }
1822
1823FreeExtraPciRoots:
1824 if (ExtraPciRoots != NULL) {
1825 DestroyExtraRootBusMap (ExtraPciRoots);
1826 }
1827
1828FreeFwCfg:
1829 FreePool (FwCfg);
1830}
1831
1855STATIC
1856BOOLEAN
1858 IN CONST CHAR16 *Translated,
1859 IN UINTN TranslatedLength,
1860 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
1861 )
1862{
1863 CHAR16 *Converted;
1864 BOOLEAN Result;
1865 VOID *FileBuffer;
1866 UINTN FileSize;
1867 EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;
1868 CHAR16 *AbsConverted;
1869 BOOLEAN Shortform;
1871
1872 Converted = ConvertDevicePathToText (
1873 DevicePath,
1874 FALSE, // DisplayOnly
1875 FALSE // AllowShortcuts
1876 );
1877 if (Converted == NULL) {
1878 return FALSE;
1879 }
1880
1881 Result = FALSE;
1882 Shortform = FALSE;
1883 //
1884 // Expand the short-form device path to full device path
1885 //
1886 if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
1887 (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP))
1888 {
1889 //
1890 // Harddrive shortform device path
1891 //
1892 Shortform = TRUE;
1893 } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
1894 (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP))
1895 {
1896 //
1897 // File-path shortform device path
1898 //
1899 Shortform = TRUE;
1900 } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
1901 (DevicePathSubType (DevicePath) == MSG_URI_DP))
1902 {
1903 //
1904 // URI shortform device path
1905 //
1906 Shortform = TRUE;
1907 } else {
1908 for ( Node = DevicePath
1909 ; !IsDevicePathEnd (Node)
1910 ; Node = NextDevicePathNode (Node)
1911 )
1912 {
1913 if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
1914 ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||
1916 {
1917 Shortform = TRUE;
1918 break;
1919 }
1920 }
1921 }
1922
1923 //
1924 // Attempt to expand any relative UEFI device path to
1925 // an absolute device path first.
1926 //
1927 if (Shortform) {
1929 DevicePath,
1930 &AbsDevicePath,
1931 &FileSize
1932 );
1933 if (FileBuffer == NULL) {
1934 goto Exit;
1935 }
1936
1937 FreePool (FileBuffer);
1938 AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);
1939 FreePool (AbsDevicePath);
1940 if (AbsConverted == NULL) {
1941 goto Exit;
1942 }
1943
1944 DEBUG ((
1945 DEBUG_VERBOSE,
1946 "%a: expanded relative device path \"%s\" for prefix matching\n",
1947 __func__,
1948 Converted
1949 ));
1950 FreePool (Converted);
1951 Converted = AbsConverted;
1952 }
1953
1954 //
1955 // Is Translated a prefix of Converted?
1956 //
1957 Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
1958 DEBUG ((
1959 DEBUG_VERBOSE,
1960 "%a: against \"%s\": %a\n",
1961 __func__,
1962 Converted,
1963 Result ? "match" : "no match"
1964 ));
1965Exit:
1966 FreePool (Converted);
1967 return Result;
1968}
1969
1996STATIC
1997RETURN_STATUS
1999 IN OUT BOOT_ORDER *BootOrder,
2000 IN OUT ACTIVE_OPTION *ActiveOption,
2001 IN UINTN ActiveCount
2002 )
2003{
2004 RETURN_STATUS Status;
2005 UINTN Idx;
2006
2007 Status = RETURN_SUCCESS;
2008 Idx = 0;
2009 while (!RETURN_ERROR (Status) && Idx < ActiveCount) {
2010 if (!ActiveOption[Idx].Appended) {
2013
2014 Current = ActiveOption[Idx].BootOption;
2015 FirstNode = Current->FilePath;
2016 if (FirstNode != NULL) {
2017 CHAR16 *Converted;
2018 STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";
2019 BOOLEAN Keep;
2020
2021 Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);
2022 if (Converted == NULL) {
2023 Converted = ConvFallBack;
2024 }
2025
2026 Keep = TRUE;
2027 if ((DevicePathType (FirstNode) == MEDIA_DEVICE_PATH) &&
2028 (DevicePathSubType (FirstNode) == MEDIA_HARDDRIVE_DP))
2029 {
2030 //
2031 // drop HD()
2032 //
2033 Keep = FALSE;
2034 } else if ((DevicePathType (FirstNode) == ACPI_DEVICE_PATH) &&
2035 (DevicePathSubType (FirstNode) == ACPI_DP))
2036 {
2038
2039 Acpi = (ACPI_HID_DEVICE_PATH *)FirstNode;
2040 if (((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) &&
2041 (EISA_ID_TO_NUM (Acpi->HID) == 0x0a03))
2042 {
2043 //
2044 // drop PciRoot() if we enabled the user to select PCI-like boot
2045 // options, by providing translation for such OFW device path
2046 // fragments
2047 //
2048 Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);
2049 }
2050 } else if ((DevicePathType (FirstNode) == HARDWARE_DEVICE_PATH) &&
2051 (DevicePathSubType (FirstNode) == HW_VENDOR_DP))
2052 {
2053 VENDOR_DEVICE_PATH *VenHw;
2054
2055 VenHw = (VENDOR_DEVICE_PATH *)FirstNode;
2056 if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {
2057 //
2058 // drop virtio-mmio if we enabled the user to select boot options
2059 // referencing such device paths
2060 //
2061 Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);
2062 }
2063 }
2064
2065 if (Keep) {
2066 Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);
2067 if (!RETURN_ERROR (Status)) {
2068 DEBUG ((
2069 DEBUG_VERBOSE,
2070 "%a: keeping \"%s\"\n",
2071 __func__,
2072 Converted
2073 ));
2074 }
2075 } else {
2076 DEBUG ((
2077 DEBUG_VERBOSE,
2078 "%a: dropping \"%s\"\n",
2079 __func__,
2080 Converted
2081 ));
2082 }
2083
2084 if (Converted != ConvFallBack) {
2085 FreePool (Converted);
2086 }
2087 }
2088 }
2089
2090 ++Idx;
2091 }
2092
2093 return Status;
2094}
2095
2107STATIC
2108VOID
2110 IN CONST ACTIVE_OPTION *ActiveOption,
2111 IN UINTN ActiveCount
2112 )
2113{
2114 UINTN Idx;
2115
2116 for (Idx = 0; Idx < ActiveCount; ++Idx) {
2117 if (!ActiveOption[Idx].Appended) {
2118 CHAR16 VariableName[9];
2119
2121 VariableName,
2122 sizeof VariableName,
2123 "Boot%04x",
2124 ActiveOption[Idx].BootOption->OptionNumber
2125 );
2126
2127 //
2128 // "The space consumed by the deleted variable may not be available until
2129 // the next power cycle", but that's good enough.
2130 //
2131 gRT->SetVariable (
2132 VariableName,
2133 &gEfiGlobalVariableGuid,
2134 0, // Attributes, 0 means deletion
2135 0, // DataSize, 0 means deletion
2136 NULL // Data
2137 );
2138 }
2139 }
2140}
2141
2170RETURN_STATUS
2171EFIAPI
2173 VOID
2174 )
2175{
2176 RETURN_STATUS Status;
2177 FIRMWARE_CONFIG_ITEM FwCfgItem;
2178 UINTN FwCfgSize;
2179 CHAR8 *FwCfg;
2180 CONST CHAR8 *FwCfgPtr;
2181
2182 BOOT_ORDER BootOrder;
2183 ACTIVE_OPTION *ActiveOption;
2184 UINTN ActiveCount;
2185
2186 EXTRA_ROOT_BUS_MAP *ExtraPciRoots;
2187
2188 UINTN TranslatedSize;
2189 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
2190 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
2191 UINTN BootOptionCount;
2192
2193 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
2194 if (Status != RETURN_SUCCESS) {
2195 return Status;
2196 }
2197
2198 if (FwCfgSize == 0) {
2199 return RETURN_NOT_FOUND;
2200 }
2201
2202 FwCfg = AllocatePool (FwCfgSize);
2203 if (FwCfg == NULL) {
2205 }
2206
2207 QemuFwCfgSelectItem (FwCfgItem);
2208 QemuFwCfgReadBytes (FwCfgSize, FwCfg);
2209 if (FwCfg[FwCfgSize - 1] != '\0') {
2210 Status = RETURN_INVALID_PARAMETER;
2211 goto ErrorFreeFwCfg;
2212 }
2213
2214 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __func__));
2215 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
2216 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __func__));
2217 FwCfgPtr = FwCfg;
2218
2219 BootOrder.Produced = 0;
2220 BootOrder.Allocated = 1;
2221 BootOrder.Data = AllocatePool (
2222 BootOrder.Allocated * sizeof (*BootOrder.Data)
2223 );
2224 if (BootOrder.Data == NULL) {
2225 Status = RETURN_OUT_OF_RESOURCES;
2226 goto ErrorFreeFwCfg;
2227 }
2228
2229 BootOptions = EfiBootManagerGetLoadOptions (
2230 &BootOptionCount,
2231 LoadOptionTypeBoot
2232 );
2233 if (BootOptions == NULL) {
2234 Status = RETURN_NOT_FOUND;
2235 goto ErrorFreeBootOrder;
2236 }
2237
2238 Status = CollectActiveOptions (
2239 BootOptions,
2240 BootOptionCount,
2241 &ActiveOption,
2242 &ActiveCount
2243 );
2244 if (RETURN_ERROR (Status)) {
2245 goto ErrorFreeBootOptions;
2246 }
2247
2248 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
2249 Status = CreateExtraRootBusMap (&ExtraPciRoots);
2250 if (EFI_ERROR (Status)) {
2251 goto ErrorFreeActiveOption;
2252 }
2253 } else {
2254 ExtraPciRoots = NULL;
2255 }
2256
2257 //
2258 // translate each OpenFirmware path
2259 //
2260 TranslatedSize = ARRAY_SIZE (Translated);
2261 Status = TranslateOfwPath (
2262 &FwCfgPtr,
2263 ExtraPciRoots,
2264 Translated,
2265 &TranslatedSize
2266 );
2267 while (Status == RETURN_SUCCESS ||
2268 Status == RETURN_UNSUPPORTED ||
2269 Status == RETURN_PROTOCOL_ERROR ||
2270 Status == RETURN_BUFFER_TOO_SMALL)
2271 {
2272 if (Status == RETURN_SUCCESS) {
2273 UINTN Idx;
2274
2275 //
2276 // match translated OpenFirmware path against all active boot options
2277 //
2278 for (Idx = 0; Idx < ActiveCount; ++Idx) {
2279 if (!ActiveOption[Idx].Appended &&
2280 Match (
2281 Translated,
2282 TranslatedSize, // contains length, not size, in CHAR16's here
2283 ActiveOption[Idx].BootOption->FilePath
2284 )
2285 )
2286 {
2287 //
2288 // match found, store ID and continue with next OpenFirmware path
2289 //
2290 Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
2291 if (Status != RETURN_SUCCESS) {
2292 goto ErrorFreeExtraPciRoots;
2293 }
2294 }
2295 } // scanned all active boot options
2296 } // translation successful
2297
2298 TranslatedSize = ARRAY_SIZE (Translated);
2299 Status = TranslateOfwPath (
2300 &FwCfgPtr,
2301 ExtraPciRoots,
2302 Translated,
2303 &TranslatedSize
2304 );
2305 } // scanning of OpenFirmware paths done
2306
2307 if ((Status == RETURN_NOT_FOUND) && (BootOrder.Produced > 0)) {
2308 //
2309 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
2310 // Some of the active boot options that have not been selected over fw_cfg
2311 // should be preserved at the end of the boot order.
2312 //
2313 Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
2314 if (RETURN_ERROR (Status)) {
2315 goto ErrorFreeExtraPciRoots;
2316 }
2317
2318 //
2319 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
2320 // attributes.
2321 //
2322 Status = gRT->SetVariable (
2323 L"BootOrder",
2324 &gEfiGlobalVariableGuid,
2326 EFI_VARIABLE_BOOTSERVICE_ACCESS |
2327 EFI_VARIABLE_RUNTIME_ACCESS,
2328 BootOrder.Produced * sizeof (*BootOrder.Data),
2329 BootOrder.Data
2330 );
2331 if (EFI_ERROR (Status)) {
2332 DEBUG ((
2333 DEBUG_ERROR,
2334 "%a: setting BootOrder: %r\n",
2335 __func__,
2336 Status
2337 ));
2338 goto ErrorFreeExtraPciRoots;
2339 }
2340
2341 DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __func__));
2342 PruneBootVariables (ActiveOption, ActiveCount);
2343 }
2344
2345ErrorFreeExtraPciRoots:
2346 if (ExtraPciRoots != NULL) {
2347 DestroyExtraRootBusMap (ExtraPciRoots);
2348 }
2349
2350ErrorFreeActiveOption:
2351 FreePool (ActiveOption);
2352
2353ErrorFreeBootOptions:
2354 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
2355
2356ErrorFreeBootOrder:
2357 FreePool (BootOrder.Data);
2358
2359ErrorFreeFwCfg:
2360 FreePool (FwCfg);
2361
2362 return Status;
2363}
2364
2371UINT16
2372EFIAPI
2374 VOID
2375 )
2376{
2377 FIRMWARE_CONFIG_ITEM BootMenuWaitItem;
2378 UINTN BootMenuWaitSize;
2379 UINT16 Timeout = PcdGet16 (PcdPlatformBootTimeOut);
2380
2381 if (!QemuFwCfgIsAvailable ()) {
2382 return Timeout;
2383 }
2384
2385 QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);
2386 if (QemuFwCfgRead16 () == 0) {
2387 //
2388 // The user specified "-boot menu=off", or didn't specify "-boot
2389 // menu=(on|off)" at all. Return the platform default.
2390 //
2391 return PcdGet16 (PcdPlatformBootTimeOut);
2392 }
2393
2394 if (RETURN_ERROR (
2396 "etc/boot-menu-wait",
2397 &BootMenuWaitItem,
2398 &BootMenuWaitSize
2399 )
2400 ) ||
2401 (BootMenuWaitSize != sizeof (UINT16)))
2402 {
2403 //
2404 // "-boot menu=on" was specified without "splash-time=N". In this case,
2405 // return three seconds if the platform default would cause us to skip the
2406 // front page, and return the platform default otherwise.
2407 //
2408 if (Timeout == 0) {
2409 Timeout = 3;
2410 }
2411
2412 return Timeout;
2413 }
2414
2415 //
2416 // "-boot menu=on,splash-time=N" was specified, where N is in units of
2417 // milliseconds. The Intel BDS Front Page progress bar only supports whole
2418 // seconds, round N up.
2419 //
2420 QemuFwCfgSelectItem (BootMenuWaitItem);
2421 return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);
2422}
UINT64 UINTN
INTN EFIAPI AsciiStrCmp(IN CONST CHAR8 *FirstString, IN CONST CHAR8 *SecondString)
Definition: String.c:716
INTN EFIAPI StrnCmp(IN CONST CHAR16 *FirstString, IN CONST CHAR16 *SecondString, IN UINTN Length)
Definition: String.c:162
UINT64 EFIAPI LShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: LShiftU64.c:28
BOOLEAN EFIAPI CompareGuid(IN CONST GUID *Guid1, IN CONST GUID *Guid2)
Definition: MemLibGuid.c:73
VOID *EFIAPI ScanMem8(IN CONST VOID *Buffer, IN UINTN Length, IN UINT8 Value)
#define MEDIA_FILEPATH_DP
Definition: DevicePath.h:1098
#define HARDWARE_DEVICE_PATH
Definition: DevicePath.h:68
#define HW_VENDOR_DP
Definition: DevicePath.h:133
#define MSG_URI_DP
Definition: DevicePath.h:879
#define ACPI_DEVICE_PATH
Definition: DevicePath.h:190
#define MEDIA_HARDDRIVE_DP
Definition: DevicePath.h:1014
#define MSG_USB_WWID_DP
Definition: DevicePath.h:467
#define ACPI_DP
Definition: DevicePath.h:195
#define MSG_USB_CLASS_DP
Definition: DevicePath.h:434
#define MESSAGING_DEVICE_PATH
Definition: DevicePath.h:321
UINT8 EFIAPI DevicePathType(IN CONST VOID *Node)
UINT8 EFIAPI DevicePathSubType(IN CONST VOID *Node)
BOOLEAN EFIAPI IsDevicePathEnd(IN CONST VOID *Node)
EFI_DEVICE_PATH_PROTOCOL *EFIAPI NextDevicePathNode(IN CONST VOID *Node)
CHAR16 *EFIAPI ConvertDevicePathToText(IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN BOOLEAN DisplayOnly, IN BOOLEAN AllowShortcuts)
UINTN EFIAPI GetDevicePathSize(IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath)
EFI_DEVICE_PATH_PROTOCOL *EFIAPI ConvertTextToDevicePath(IN CONST CHAR16 *TextDevicePath)
VOID *EFIAPI ReallocatePool(IN UINTN OldSize, IN UINTN NewSize, IN VOID *OldBuffer OPTIONAL)
VOID EFIAPI FreePool(IN VOID *Buffer)
VOID DestroyExtraRootBusMap(IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap)
EFI_STATUS MapRootBusPosToBusNr(IN CONST EXTRA_ROOT_BUS_MAP *ExtraRootBusMap, IN UINT64 RootBusPos, OUT UINT32 *RootBusNr)
EFI_STATUS CreateExtraRootBusMap(OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap)
UINTN EFIAPI UnicodeSPrint(OUT CHAR16 *StartOfBuffer, IN UINTN BufferSize, IN CONST CHAR16 *FormatString,...)
Definition: PrintLib.c:408
UINTN EFIAPI UnicodeSPrintAsciiFormat(OUT CHAR16 *StartOfBuffer, IN UINTN BufferSize, IN CONST CHAR8 *FormatString,...)
Definition: PrintLib.c:583
EFI_RUNTIME_SERVICES * gRT
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define RETURN_BUFFER_TOO_SMALL
Definition: Base.h:1093
#define STATIC
Definition: Base.h:264
#define RETURN_PROTOCOL_ERROR
Definition: Base.h:1192
#define RETURN_ERROR(StatusCode)
Definition: Base.h:1061
#define RETURN_NOT_FOUND
Definition: Base.h:1142
#define RETURN_UNSUPPORTED
Definition: Base.h:1081
#define RETURN_OUT_OF_RESOURCES
Definition: Base.h:1114
#define RETURN_SUCCESS
Definition: Base.h:1066
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define ARRAY_SIZE(Array)
Definition: Base.h:1393
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define RETURN_INVALID_PARAMETER
Definition: Base.h:1076
#define DEBUG(Expression)
Definition: DebugLib.h:434
#define PcdGet16(TokenName)
Definition: PcdLib.h:349
#define FeaturePcdGet(TokenName)
Definition: PcdLib.h:50
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
#define REQUIRED_PCI_OFW_NODES
#define TRANSLATION_OUTPUT_SIZE
RETURN_STATUS EFIAPI SetBootOrderFromQemu(VOID)
STATIC RETURN_STATUS TranslateOfwNodes(IN CONST OFW_NODE *OfwNode, IN UINTN NumNodes, IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots, OUT CHAR16 *Translated, IN OUT UINTN *TranslatedSize)
STATIC RETURN_STATUS ParseUnitAddressHexList(IN SUBSTRING UnitAddress, OUT UINT64 *Result, IN OUT UINTN *NumResults)
STATIC BOOLEAN Match(IN CONST CHAR16 *Translated, IN UINTN TranslatedLength, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath)
STATIC RETURN_STATUS BootOrderAppend(IN OUT BOOT_ORDER *BootOrder, IN OUT ACTIVE_OPTION *ActiveOption)
STATIC VOID PruneBootVariables(IN CONST ACTIVE_OPTION *ActiveOption, IN UINTN ActiveCount)
STATIC RETURN_STATUS TranslatePciOfwNodes(IN CONST OFW_NODE *OfwNode, IN UINTN NumNodes, IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots, OUT CHAR16 *Translated, IN OUT UINTN *TranslatedSize)
STATIC RETURN_STATUS ParseOfwNode(IN OUT CONST CHAR8 **Ptr, OUT OFW_NODE *OfwNode, OUT BOOLEAN *IsFinal)
#define BRIDGE_TRANSLATION_OUTPUT_SIZE
STATIC RETURN_STATUS TranslateOfwPath(IN OUT CONST CHAR8 **Ptr, IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots, OUT CHAR16 *Translated, IN OUT UINTN *TranslatedSize)
STATIC RETURN_STATUS TranslateMmioOfwNodes(IN CONST OFW_NODE *OfwNode, IN UINTN NumNodes, OUT CHAR16 *Translated, IN OUT UINTN *TranslatedSize)
STATIC BOOLEAN SubstringEq(IN SUBSTRING Substring, IN CONST CHAR8 *String)
STATIC RETURN_STATUS CollectActiveOptions(IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions, IN UINTN BootOptionCount, OUT ACTIVE_OPTION **ActiveOption, OUT UINTN *Count)
VOID EFIAPI StoreQemuBootOrder(VOID)
RETURN_STATUS EFIAPI ConnectDevicesFromQemu(VOID)
UINT16 EFIAPI GetFrontPageTimeoutFromQemu(VOID)
STATIC BOOLEAN IsAlnum(IN CHAR8 Chr)
STATIC RETURN_STATUS BootOrderComplete(IN OUT BOOT_ORDER *BootOrder, IN OUT ACTIVE_OPTION *ActiveOption, IN UINTN ActiveCount)
UINT16 EFIAPI QemuFwCfgRead16(VOID)
Definition: QemuFwCfgLib.c:186
RETURN_STATUS EFIAPI QemuFwCfgFindFile(IN CONST CHAR8 *Name, OUT FIRMWARE_CONFIG_ITEM *Item, OUT UINTN *Size)
Definition: QemuFwCfgLib.c:250
VOID EFIAPI QemuFwCfgReadBytes(IN UINTN Size, IN VOID *Buffer OPTIONAL)
Definition: QemuFwCfgNull.c:66
VOID EFIAPI QemuFwCfgSelectItem(IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem)
Definition: QemuFwCfgLib.c:33
BOOLEAN EFIAPI QemuFwCfgIsAvailable(VOID)
Definition: QemuFwCfgDxe.c:44
VOID EFIAPI Exit(IN EFI_STATUS Status)
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
VOID *EFIAPI EfiBootManagerGetLoadOptionBuffer(IN EFI_DEVICE_PATH_PROTOCOL *FilePath, OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, OUT UINTN *FileSize)
Definition: BmBoot.c:1607
EFI_BOOT_MANAGER_LOAD_OPTION *EFIAPI EfiBootManagerGetLoadOptions(OUT UINTN *LoadOptionCount, IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType)
EFI_STATUS EFIAPI EfiBootManagerFreeLoadOptions(IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions, IN UINTN LoadOptionCount)
EFI_STATUS EFIAPI EfiBootManagerConnectDevicePath(IN EFI_DEVICE_PATH_PROTOCOL *DevicePathToConnect, OUT EFI_HANDLE *MatchingHandle OPTIONAL)
Definition: BmConnect.c:108
EFI_BOOT_SERVICES * gBS
#define EFI_VARIABLE_NON_VOLATILE