2 Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
4 Copyright (C) 2012 - 2014, Red Hat, Inc.
5 Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <Library/QemuFwCfgLib.h>
11 #include <Library/DebugLib.h>
12 #include <Library/MemoryAllocationLib.h>
13 #include <Library/UefiBootManagerLib.h>
14 #include <Library/UefiBootServicesTableLib.h>
15 #include <Library/UefiRuntimeServicesTableLib.h>
16 #include <Library/BaseLib.h>
17 #include <Library/PrintLib.h>
18 #include <Library/DevicePathLib.h>
19 #include <Library/QemuBootOrderLib.h>
20 #include <Library/BaseMemoryLib.h>
21 #include <Guid/GlobalVariable.h>
22 #include <Guid/VirtioMmioTransport.h>
24 #include "ExtraRootBusMap.h"
27 OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
29 #define TRANSLATION_OUTPUT_SIZE 0x100
32 Output buffer size for OpenFirmware to UEFI device path fragment translation,
33 in CHAR16's, for a sequence of PCI bridges.
35 #define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40
38 Numbers of nodes in OpenFirmware device paths that are required and examined.
40 #define REQUIRED_PCI_OFW_NODES 2
41 #define REQUIRED_MMIO_OFW_NODES 1
42 #define EXAMINED_OFW_NODES 6
45 Simple character classification routines, corresponding to POSIX class names
54 return (('0' <= Chr
&& Chr
<= '9') ||
55 ('A' <= Chr
&& Chr
<= 'Z') ||
56 ('a' <= Chr
&& Chr
<= 'z')
66 return (Chr
== ',' || Chr
== '.' || Chr
== '_' ||
67 Chr
== '+' || Chr
== '-'
77 return (32 <= Chr
&& Chr
<= 126 &&
78 Chr
!= '/' && Chr
!= '@' && Chr
!= ':');
82 Utility types and functions.
85 CONST CHAR8
*Ptr
; // not necessarily NUL-terminated
86 UINTN Len
; // number of non-NUL characters
91 Check if Substring and String have identical contents.
93 The function relies on the restriction that a SUBSTRING cannot have embedded
96 @param[in] Substring The SUBSTRING input to the comparison.
98 @param[in] String The ASCII string input to the comparison.
101 @return Whether the inputs have identical contents.
107 IN SUBSTRING Substring
,
108 IN CONST CHAR8
*String
117 while (Pos
< Substring
.Len
&& Substring
.Ptr
[Pos
] == *Chr
) {
122 return (BOOLEAN
)(Pos
== Substring
.Len
&& *Chr
== '\0');
127 Parse a comma-separated list of hexadecimal integers into the elements of an
130 Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
131 or an empty string are not allowed; they are rejected.
133 The function relies on ASCII encoding.
135 @param[in] UnitAddress The substring to parse.
137 @param[out] Result The array, allocated by the caller, to receive
138 the parsed values. This parameter may be NULL if
139 NumResults is zero on input.
141 @param[in out] NumResults On input, the number of elements allocated for
142 Result. On output, the number of elements it has
143 taken (or would have taken) to parse the string
147 @retval RETURN_SUCCESS UnitAddress has been fully parsed.
148 NumResults is set to the number of parsed
149 values; the corresponding elements have
150 been set in Result. The rest of Result's
151 elements are unchanged.
153 @retval RETURN_BUFFER_TOO_SMALL UnitAddress has been fully parsed.
154 NumResults is set to the number of parsed
155 values, but elements have been stored only
156 up to the input value of NumResults, which
157 is less than what has been parsed.
159 @retval RETURN_INVALID_PARAMETER Parse error. The contents of Results is
160 indeterminate. NumResults has not been
166 ParseUnitAddressHexList (
167 IN SUBSTRING UnitAddress
,
169 IN OUT UINTN
*NumResults
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
;
182 for (Pos
= 0; Pos
< UnitAddress
.Len
; ++Pos
) {
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') :
193 if (EntryVal
> 0xFFFFFFFFFFFFFFFull
) {
194 return RETURN_INVALID_PARAMETER
;
197 EntryVal
= LShiftU64 (EntryVal
, 4) | Val
;
198 } else if (Chr
== ',') {
199 if (PrevChr
== ',') {
200 return RETURN_INVALID_PARAMETER
;
203 if (Entry
< *NumResults
) {
204 Result
[Entry
] = EntryVal
;
210 return RETURN_INVALID_PARAMETER
;
216 if (PrevChr
== ',') {
217 return RETURN_INVALID_PARAMETER
;
220 if (Entry
< *NumResults
) {
221 Result
[Entry
] = EntryVal
;
222 Status
= RETURN_SUCCESS
;
224 Status
= RETURN_BUFFER_TOO_SMALL
;
234 A simple array of Boot Option ID's.
243 Array element tracking an enumerated boot option that has the
244 LOAD_OPTION_ACTIVE attribute.
247 CONST EFI_BOOT_MANAGER_LOAD_OPTION
*BootOption
; // reference only, no
249 BOOLEAN Appended
; // has been added to a
255 Append an active boot option to BootOrder, reallocating the latter if needed.
257 @param[in out] BootOrder The structure pointing to the array and holding
258 allocation and usage counters.
260 @param[in] ActiveOption The active boot option whose ID should be
261 appended to the array.
264 @retval RETURN_SUCCESS ID of ActiveOption appended.
266 @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.
272 IN OUT BOOT_ORDER
*BootOrder
,
273 IN OUT ACTIVE_OPTION
*ActiveOption
276 if (BootOrder
->Produced
== BootOrder
->Allocated
) {
280 ASSERT (BootOrder
->Allocated
> 0);
281 AllocatedNew
= BootOrder
->Allocated
* 2;
282 DataNew
= ReallocatePool (
283 BootOrder
->Allocated
* sizeof (*BootOrder
->Data
),
284 AllocatedNew
* sizeof (*DataNew
),
287 if (DataNew
== NULL
) {
288 return RETURN_OUT_OF_RESOURCES
;
291 BootOrder
->Allocated
= AllocatedNew
;
292 BootOrder
->Data
= DataNew
;
295 BootOrder
->Data
[BootOrder
->Produced
++] =
296 (UINT16
)ActiveOption
->BootOption
->OptionNumber
;
297 ActiveOption
->Appended
= TRUE
;
298 return RETURN_SUCCESS
;
303 Create an array of ACTIVE_OPTION elements for a boot option array.
305 @param[in] BootOptions A boot option array, created with
306 EfiBootManagerRefreshAllBootOption () and
307 EfiBootManagerGetLoadOptions ().
309 @param[in] BootOptionCount The number of elements in BootOptions.
311 @param[out] ActiveOption Pointer to the first element in the new array.
312 The caller is responsible for freeing the array
313 with FreePool() after use.
315 @param[out] Count Number of elements in the new array.
318 @retval RETURN_SUCCESS The ActiveOption array has been created.
320 @retval RETURN_NOT_FOUND No active entry has been found in
323 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
328 CollectActiveOptions (
329 IN CONST EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
,
330 IN UINTN BootOptionCount
,
331 OUT ACTIVE_OPTION
**ActiveOption
,
338 *ActiveOption
= NULL
;
341 // Scan the list twice:
342 // - count active entries,
343 // - store links to active entries.
345 for (ScanMode
= 0; ScanMode
< 2; ++ScanMode
) {
347 for (Index
= 0; Index
< BootOptionCount
; Index
++) {
348 if ((BootOptions
[Index
].Attributes
& LOAD_OPTION_ACTIVE
) != 0) {
350 (*ActiveOption
)[*Count
].BootOption
= &BootOptions
[Index
];
351 (*ActiveOption
)[*Count
].Appended
= FALSE
;
360 return RETURN_NOT_FOUND
;
363 *ActiveOption
= AllocatePool (*Count
* sizeof **ActiveOption
);
364 if (*ActiveOption
== NULL
) {
365 return RETURN_OUT_OF_RESOURCES
;
370 return RETURN_SUCCESS
;
374 OpenFirmware device path node
377 SUBSTRING DriverName
;
378 SUBSTRING UnitAddress
;
379 SUBSTRING DeviceArguments
;
384 Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
385 structure, and advance in the input string.
387 The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"
388 (a leading slash is expected and not returned):
390 /driver-name@unit-address[:device-arguments][<LF>]
392 A single trailing <LF> character is consumed but not returned. A trailing
393 <LF> or NUL character terminates the device path.
395 The function relies on ASCII encoding.
397 @param[in out] Ptr Address of the pointer pointing to the start of the
398 node string. After successful parsing *Ptr is set to
399 the byte immediately following the consumed
400 characters. On error it points to the byte that
401 caused the error. The input string is never modified.
403 @param[out] OfwNode The members of this structure point into the input
404 string, designating components of the node.
405 Separators are never included. If "device-arguments"
406 is missing, then DeviceArguments.Ptr is set to NULL.
407 All components that are present have nonzero length.
409 If the call doesn't succeed, the contents of this
410 structure is indeterminate.
412 @param[out] IsFinal In case of successful parsing, this parameter signals
413 whether the node just parsed is the final node in the
414 device path. The call after a final node will attempt
415 to start parsing the next path. If the call doesn't
416 succeed, then this parameter is not changed.
419 @retval RETURN_SUCCESS Parsing successful.
421 @retval RETURN_NOT_FOUND Parsing terminated. *Ptr was (and is)
422 pointing to an empty string.
424 @retval RETURN_INVALID_PARAMETER Parse error.
430 IN OUT CONST CHAR8
**Ptr
,
431 OUT OFW_NODE
*OfwNode
,
436 // A leading slash is expected. End of string is tolerated.
440 return RETURN_NOT_FOUND
;
447 return RETURN_INVALID_PARAMETER
;
453 OfwNode
->DriverName
.Ptr
= *Ptr
;
454 OfwNode
->DriverName
.Len
= 0;
455 while (OfwNode
->DriverName
.Len
< 32 &&
456 (IsAlnum (**Ptr
) || IsDriverNamePunct (**Ptr
))
460 ++OfwNode
->DriverName
.Len
;
463 if ((OfwNode
->DriverName
.Len
== 0) || (OfwNode
->DriverName
.Len
== 32)) {
464 return RETURN_INVALID_PARAMETER
;
471 return RETURN_INVALID_PARAMETER
;
476 OfwNode
->UnitAddress
.Ptr
= *Ptr
;
477 OfwNode
->UnitAddress
.Len
= 0;
478 while (IsPrintNotDelim (**Ptr
)) {
480 ++OfwNode
->UnitAddress
.Len
;
483 if (OfwNode
->UnitAddress
.Len
== 0) {
484 return RETURN_INVALID_PARAMETER
;
488 // device-arguments, may be omitted
490 OfwNode
->DeviceArguments
.Len
= 0;
493 OfwNode
->DeviceArguments
.Ptr
= *Ptr
;
495 while (IsPrintNotDelim (**Ptr
)) {
497 ++OfwNode
->DeviceArguments
.Len
;
500 if (OfwNode
->DeviceArguments
.Len
== 0) {
501 return RETURN_INVALID_PARAMETER
;
504 OfwNode
->DeviceArguments
.Ptr
= NULL
;
523 return RETURN_INVALID_PARAMETER
;
528 "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
530 OfwNode
->DriverName
.Len
,
531 OfwNode
->DriverName
.Ptr
,
532 OfwNode
->UnitAddress
.Len
,
533 OfwNode
->UnitAddress
.Ptr
,
534 OfwNode
->DeviceArguments
.Len
,
535 OfwNode
->DeviceArguments
.Ptr
== NULL
? "" : OfwNode
->DeviceArguments
.Ptr
537 return RETURN_SUCCESS
;
542 Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
545 @param[in] OfwNode Array of OpenFirmware device nodes to
546 translate, constituting the beginning of an
547 OpenFirmware device path.
549 @param[in] NumNodes Number of elements in OfwNode.
551 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
552 CreateExtraRootBusMap(), to be used for
553 translating positions of extra root buses to
556 @param[out] Translated Destination array receiving the UEFI path
557 fragment, allocated by the caller. If the
558 return value differs from RETURN_SUCCESS, its
559 contents is indeterminate.
561 @param[in out] TranslatedSize On input, the number of CHAR16's in
562 Translated. On RETURN_SUCCESS this parameter
563 is assigned the number of non-NUL CHAR16's
564 written to Translated. In case of other return
565 values, TranslatedSize is indeterminate.
568 @retval RETURN_SUCCESS Translation successful.
570 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
573 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
574 be translated in the current implementation.
576 @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an
577 extra PCI root bus (by serial number) that
578 is invalid according to ExtraPciRoots.
583 TranslatePciOfwNodes (
584 IN CONST OFW_NODE
*OfwNode
,
586 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
587 OUT CHAR16
*Translated
,
588 IN OUT UINTN
*TranslatedSize
593 UINTN FirstNonBridge
;
594 CHAR16 Bridges
[BRIDGE_TRANSLATION_OUTPUT_SIZE
];
601 // Resolve the PCI root bus number.
603 // The initial OFW node for the main root bus (ie. bus number 0) is:
607 // For extra root buses, the initial OFW node is
611 // root bus serial number (not PCI bus number)
613 if ((NumNodes
< REQUIRED_PCI_OFW_NODES
) ||
614 !SubstringEq (OfwNode
[0].DriverName
, "pci")
617 return RETURN_UNSUPPORTED
;
622 OfwNode
[0].UnitAddress
.Ptr
,
623 OfwNode
[0].UnitAddress
.Len
,
627 SUBSTRING PciRootSerialSubString
;
628 UINT64 PciRootSerial
;
631 // Parse the root bus serial number from the unit address after the comma.
633 PciRootSerialSubString
.Ptr
= Comma
+ 1;
634 PciRootSerialSubString
.Len
= OfwNode
[0].UnitAddress
.Len
-
635 (PciRootSerialSubString
.Ptr
-
636 OfwNode
[0].UnitAddress
.Ptr
);
639 ParseUnitAddressHexList (
640 PciRootSerialSubString
,
646 return RETURN_UNSUPPORTED
;
650 // Map the extra root bus's serial number to its actual bus number.
653 MapRootBusPosToBusNr (
660 return RETURN_PROTOCOL_ERROR
;
665 // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
669 // PCI slot & function on the parent, holding the bridge
671 // and the UEFI device path node is:
679 UINT64 BridgeDevFun
[2];
680 UINTN BridgesFreeBytes
;
682 if (!SubstringEq (OfwNode
[FirstNonBridge
].DriverName
, "pci-bridge")) {
687 NumEntries
= sizeof BridgeDevFun
/ sizeof BridgeDevFun
[0];
688 if (ParseUnitAddressHexList (
689 OfwNode
[FirstNonBridge
].UnitAddress
,
694 return RETURN_UNSUPPORTED
;
697 BridgesFreeBytes
= sizeof Bridges
- BridgesLen
* sizeof Bridges
[0];
698 Written
= UnicodeSPrintAsciiFormat (
699 Bridges
+ BridgesLen
,
705 BridgesLen
+= Written
;
708 // There's no way to differentiate between "completely used up without
709 // truncation" and "truncated", so treat the former as the latter.
711 if (BridgesLen
+ 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE
) {
712 return RETURN_UNSUPPORTED
;
716 } while (FirstNonBridge
< NumNodes
);
718 if (FirstNonBridge
== NumNodes
) {
719 return RETURN_UNSUPPORTED
;
723 // Parse the OFW nodes starting with the first non-bridge node.
726 NumEntries
= ARRAY_SIZE (PciDevFun
);
727 if (ParseUnitAddressHexList (
728 OfwNode
[FirstNonBridge
].UnitAddress
,
734 return RETURN_UNSUPPORTED
;
737 if ((NumNodes
>= FirstNonBridge
+ 3) &&
738 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "ide") &&
739 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
740 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
744 // OpenFirmware device path (IDE disk, IDE CD-ROM):
746 // /pci@i0cf8/ide@1,1/drive@0/disk@0
748 // | | | | master or slave
749 // | | | primary or secondary
750 // | PCI slot & function holding IDE controller
751 // PCI root at system bus port, PIO
755 // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
763 if ((ParseUnitAddressHexList (
764 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
767 ) != RETURN_SUCCESS
) ||
769 (ParseUnitAddressHexList (
770 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
772 &NumEntries
// reuse after previous single-element call
773 ) != RETURN_SUCCESS
) ||
777 return RETURN_UNSUPPORTED
;
780 Written
= UnicodeSPrintAsciiFormat (
782 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
783 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
788 Secondary
? "Secondary" : "Primary",
789 Slave
? "Slave" : "Master"
791 } else if ((NumNodes
>= FirstNonBridge
+ 3) &&
792 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,2922") &&
793 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
794 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
798 // OpenFirmware device path (Q35 SATA disk and CD-ROM):
800 // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
802 // | | | | device number (fixed 0)
803 // | | | channel (port) number
804 // | PCI slot & function holding SATA HBA
805 // PCI root at system bus port, PIO
809 // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
811 // | | LUN (always 0 on Q35)
812 // | port multiplier port number,
813 // | always 0xFFFF on Q35
814 // channel (port) number
820 ParseUnitAddressHexList (
821 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
827 return RETURN_UNSUPPORTED
;
830 Written
= UnicodeSPrintAsciiFormat (
832 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
833 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
840 } else if ((NumNodes
>= FirstNonBridge
+ 3) &&
841 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "isa") &&
842 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "fdc") &&
843 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "floppy")
847 // OpenFirmware device path (floppy disk):
849 // /pci@i0cf8/isa@1/fdc@03f0/floppy@0
852 // | | ISA controller io-port (hex)
853 // | PCI slot holding ISA controller
854 // PCI root at system bus port, PIO
858 // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
865 if ((ParseUnitAddressHexList (
866 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
869 ) != RETURN_SUCCESS
) ||
873 return RETURN_UNSUPPORTED
;
876 Written
= UnicodeSPrintAsciiFormat (
878 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
879 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
886 } else if ((NumNodes
>= FirstNonBridge
+ 2) &&
887 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
888 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "disk")
892 // OpenFirmware device path (virtio-blk disk):
894 // /pci@i0cf8/scsi@6[,3]/disk@0,0
897 // | | PCI function corresponding to disk (optional)
898 // | PCI slot holding disk
899 // PCI root at system bus port, PIO
901 // UEFI device path prefix:
903 // PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
904 // PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
906 Written
= UnicodeSPrintAsciiFormat (
908 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
909 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
915 } else if ((NumNodes
>= FirstNonBridge
+ 3) &&
916 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
917 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "channel") &&
918 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
922 // OpenFirmware device path (virtio-scsi disk):
924 // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
928 // | | channel (unused, fixed 0)
929 // | PCI slot[, function] holding SCSI controller
930 // PCI root at system bus port, PIO
932 // UEFI device path prefix:
934 // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
935 // -- if PCI function is 0 or absent
936 // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
937 // -- if PCI function is present and nonzero
942 NumEntries
= ARRAY_SIZE (TargetLun
);
943 if (ParseUnitAddressHexList (
944 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
950 return RETURN_UNSUPPORTED
;
953 Written
= UnicodeSPrintAsciiFormat (
955 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
956 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
964 } else if ((NumNodes
>= FirstNonBridge
+ 2) &&
965 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,5845") &&
966 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "namespace")
970 // OpenFirmware device path (NVMe device):
972 // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
974 // | | | | Extended Unique Identifier
975 // | | | | (EUI-64), big endian interp.
976 // | | | namespace ID
977 // | PCI slot & function holding NVMe controller
978 // PCI root at system bus port, PIO
982 // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
984 // | octets of the EUI-64
985 // | in address order
989 UINTN RequiredEntries
;
992 RequiredEntries
= ARRAY_SIZE (Namespace
);
993 NumEntries
= RequiredEntries
;
994 if ((ParseUnitAddressHexList (
995 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
998 ) != RETURN_SUCCESS
) ||
999 (NumEntries
!= RequiredEntries
) ||
1000 (Namespace
[0] == 0) ||
1001 (Namespace
[0] >= MAX_UINT32
)
1004 return RETURN_UNSUPPORTED
;
1007 Eui64
= (UINT8
*)&Namespace
[1];
1008 Written
= UnicodeSPrintAsciiFormat (
1010 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1011 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
1012 "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
1027 } else if ((NumNodes
>= FirstNonBridge
+ 2) &&
1028 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "usb") &&
1029 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "storage"))
1032 // OpenFirmware device path (usb-storage device in XHCI port):
1034 // /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0
1036 // | | | | fixed fixed
1037 // | | | XHCI port number, 1-based
1038 // | | PCI function corresponding to XHCI (optional)
1039 // | PCI slot holding XHCI
1040 // PCI root at system bus port, PIO
1042 // UEFI device path prefix:
1044 // PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)
1046 // | XHCI port number in 0-based notation
1047 // 0x0 if PCI function is 0, or absent from OFW
1049 RETURN_STATUS ParseStatus
;
1050 UINT64 OneBasedXhciPort
;
1053 ParseStatus
= ParseUnitAddressHexList (
1054 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
1058 if (RETURN_ERROR (ParseStatus
) || (OneBasedXhciPort
== 0)) {
1059 return RETURN_UNSUPPORTED
;
1062 Written
= UnicodeSPrintAsciiFormat (
1064 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1065 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",
1070 OneBasedXhciPort
- 1
1074 // Generic OpenFirmware device path for PCI devices:
1076 // /pci@i0cf8/ethernet@3[,2]
1078 // | PCI slot[, function] holding Ethernet card
1079 // PCI root at system bus port, PIO
1081 // UEFI device path prefix (dependent on presence of nonzero PCI function):
1083 // PciRoot(0x0)/Pci(0x3,0x0)
1084 // PciRoot(0x0)/Pci(0x3,0x2)
1086 Written
= UnicodeSPrintAsciiFormat (
1088 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1089 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
1098 // There's no way to differentiate between "completely used up without
1099 // truncation" and "truncated", so treat the former as the latter, and return
1100 // success only for "some room left unused".
1102 if (Written
+ 1 < *TranslatedSize
) {
1103 *TranslatedSize
= Written
;
1104 return RETURN_SUCCESS
;
1107 return RETURN_BUFFER_TOO_SMALL
;
1111 // A type providing easy raw access to the base address of a virtio-mmio
1117 } VIRTIO_MMIO_BASE_ADDRESS
;
1121 Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
1124 @param[in] OfwNode Array of OpenFirmware device nodes to
1125 translate, constituting the beginning of an
1126 OpenFirmware device path.
1128 @param[in] NumNodes Number of elements in OfwNode.
1130 @param[out] Translated Destination array receiving the UEFI path
1131 fragment, allocated by the caller. If the
1132 return value differs from RETURN_SUCCESS, its
1133 contents is indeterminate.
1135 @param[in out] TranslatedSize On input, the number of CHAR16's in
1136 Translated. On RETURN_SUCCESS this parameter
1137 is assigned the number of non-NUL CHAR16's
1138 written to Translated. In case of other return
1139 values, TranslatedSize is indeterminate.
1142 @retval RETURN_SUCCESS Translation successful.
1144 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1147 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1148 be translated in the current implementation.
1153 TranslateMmioOfwNodes (
1154 IN CONST OFW_NODE
*OfwNode
,
1156 OUT CHAR16
*Translated
,
1157 IN OUT UINTN
*TranslatedSize
1160 VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase
;
1161 CHAR16 VenHwString
[60 + 1];
1166 // Get the base address of the virtio-mmio transport.
1168 if ((NumNodes
< REQUIRED_MMIO_OFW_NODES
) ||
1169 !SubstringEq (OfwNode
[0].DriverName
, "virtio-mmio")
1172 return RETURN_UNSUPPORTED
;
1176 if (ParseUnitAddressHexList (
1177 OfwNode
[0].UnitAddress
,
1178 &VirtioMmioBase
.Uint64
,
1183 return RETURN_UNSUPPORTED
;
1186 UnicodeSPrintAsciiFormat (
1189 "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)",
1190 &gVirtioMmioTransportGuid
,
1191 VirtioMmioBase
.Raw
[0],
1192 VirtioMmioBase
.Raw
[1],
1193 VirtioMmioBase
.Raw
[2],
1194 VirtioMmioBase
.Raw
[3],
1195 VirtioMmioBase
.Raw
[4],
1196 VirtioMmioBase
.Raw
[5],
1197 VirtioMmioBase
.Raw
[6],
1198 VirtioMmioBase
.Raw
[7]
1201 if ((NumNodes
>= 2) &&
1202 SubstringEq (OfwNode
[1].DriverName
, "disk"))
1205 // OpenFirmware device path (virtio-blk disk):
1207 // /virtio-mmio@000000000a003c00/disk@0,0
1210 // base address of virtio-mmio register block
1212 // UEFI device path prefix:
1216 Written
= UnicodeSPrintAsciiFormat (
1218 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1222 } else if ((NumNodes
>= 3) &&
1223 SubstringEq (OfwNode
[1].DriverName
, "channel") &&
1224 SubstringEq (OfwNode
[2].DriverName
, "disk"))
1227 // OpenFirmware device path (virtio-scsi disk):
1229 // /virtio-mmio@000000000a003a00/channel@0/disk@2,3
1233 // | channel (unused, fixed 0)
1234 // base address of virtio-mmio register block
1236 // UEFI device path prefix:
1238 // <VenHwString>/Scsi(0x2,0x3)
1240 UINT64 TargetLun
[2];
1243 NumEntries
= ARRAY_SIZE (TargetLun
);
1244 if (ParseUnitAddressHexList (
1245 OfwNode
[2].UnitAddress
,
1251 return RETURN_UNSUPPORTED
;
1254 Written
= UnicodeSPrintAsciiFormat (
1256 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1257 "%s/Scsi(0x%Lx,0x%Lx)",
1262 } else if ((NumNodes
>= 2) &&
1263 SubstringEq (OfwNode
[1].DriverName
, "ethernet-phy"))
1266 // OpenFirmware device path (virtio-net NIC):
1268 // /virtio-mmio@000000000a003e00/ethernet-phy@0
1271 // base address of virtio-mmio register block
1273 // UEFI device path prefix:
1277 Written
= UnicodeSPrintAsciiFormat (
1279 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1284 return RETURN_UNSUPPORTED
;
1288 // There's no way to differentiate between "completely used up without
1289 // truncation" and "truncated", so treat the former as the latter, and return
1290 // success only for "some room left unused".
1292 if (Written
+ 1 < *TranslatedSize
) {
1293 *TranslatedSize
= Written
;
1294 return RETURN_SUCCESS
;
1297 return RETURN_BUFFER_TOO_SMALL
;
1302 Translate an array of OpenFirmware device nodes to a UEFI device path
1305 @param[in] OfwNode Array of OpenFirmware device nodes to
1306 translate, constituting the beginning of an
1307 OpenFirmware device path.
1309 @param[in] NumNodes Number of elements in OfwNode.
1311 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1312 CreateExtraRootBusMap(), to be used for
1313 translating positions of extra root buses to
1316 @param[out] Translated Destination array receiving the UEFI path
1317 fragment, allocated by the caller. If the
1318 return value differs from RETURN_SUCCESS, its
1319 contents is indeterminate.
1321 @param[in out] TranslatedSize On input, the number of CHAR16's in
1322 Translated. On RETURN_SUCCESS this parameter
1323 is assigned the number of non-NUL CHAR16's
1324 written to Translated. In case of other return
1325 values, TranslatedSize is indeterminate.
1328 @retval RETURN_SUCCESS Translation successful.
1330 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1333 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1334 be translated in the current implementation.
1336 @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has
1337 been (partially) recognized, but it contains
1338 a logic error / doesn't match system state.
1344 IN CONST OFW_NODE
*OfwNode
,
1346 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1347 OUT CHAR16
*Translated
,
1348 IN OUT UINTN
*TranslatedSize
1351 RETURN_STATUS Status
;
1353 Status
= RETURN_UNSUPPORTED
;
1355 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1356 Status
= TranslatePciOfwNodes (
1365 if ((Status
== RETURN_UNSUPPORTED
) &&
1366 FeaturePcdGet (PcdQemuBootOrderMmioTranslation
))
1368 Status
= TranslateMmioOfwNodes (
1381 Translate an OpenFirmware device path fragment to a UEFI device path
1382 fragment, and advance in the input string.
1384 @param[in out] Ptr Address of the pointer pointing to the start
1385 of the path string. After successful
1386 translation (RETURN_SUCCESS) or at least
1387 successful parsing (RETURN_UNSUPPORTED,
1388 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
1389 byte immediately following the consumed
1390 characters. In other error cases, it points to
1391 the byte that caused the error.
1393 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1394 CreateExtraRootBusMap(), to be used for
1395 translating positions of extra root buses to
1398 @param[out] Translated Destination array receiving the UEFI path
1399 fragment, allocated by the caller. If the
1400 return value differs from RETURN_SUCCESS, its
1401 contents is indeterminate.
1403 @param[in out] TranslatedSize On input, the number of CHAR16's in
1404 Translated. On RETURN_SUCCESS this parameter
1405 is assigned the number of non-NUL CHAR16's
1406 written to Translated. In case of other return
1407 values, TranslatedSize is indeterminate.
1410 @retval RETURN_SUCCESS Translation successful.
1412 @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed
1413 successfully, but its translation did not
1414 fit into the number of bytes provided.
1415 Further calls to this function are
1418 @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed
1419 successfully, but it can't be translated in
1420 the current implementation. Further calls
1421 to this function are possible.
1423 @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been
1424 (partially) recognized, but it contains a
1425 logic error / doesn't match system state.
1426 Further calls to this function are
1429 @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was
1430 pointing to the empty string or "HALT". On
1431 output, *Ptr points to the empty string
1432 (ie. "HALT" is consumed transparently when
1435 @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.
1441 IN OUT CONST CHAR8
**Ptr
,
1442 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1443 OUT CHAR16
*Translated
,
1444 IN OUT UINTN
*TranslatedSize
1448 RETURN_STATUS Status
;
1449 OFW_NODE Node
[EXAMINED_OFW_NODES
];
1455 if (AsciiStrCmp (*Ptr
, "HALT") == 0) {
1457 Status
= RETURN_NOT_FOUND
;
1459 Status
= ParseOfwNode (Ptr
, &Node
[NumNodes
], &IsFinal
);
1462 if (Status
== RETURN_NOT_FOUND
) {
1463 DEBUG ((DEBUG_VERBOSE
, "%a: no more nodes\n", __FUNCTION__
));
1464 return RETURN_NOT_FOUND
;
1467 while (Status
== RETURN_SUCCESS
&& !IsFinal
) {
1469 Status
= ParseOfwNode (
1471 (NumNodes
< EXAMINED_OFW_NODES
) ? &Node
[NumNodes
] : &Skip
,
1477 case RETURN_SUCCESS
:
1481 case RETURN_INVALID_PARAMETER
:
1482 DEBUG ((DEBUG_VERBOSE
, "%a: parse error\n", __FUNCTION__
));
1483 return RETURN_INVALID_PARAMETER
;
1489 Status
= TranslateOfwNodes (
1491 NumNodes
< EXAMINED_OFW_NODES
? NumNodes
: EXAMINED_OFW_NODES
,
1497 case RETURN_SUCCESS
:
1498 DEBUG ((DEBUG_VERBOSE
, "%a: success: \"%s\"\n", __FUNCTION__
, Translated
));
1501 case RETURN_BUFFER_TOO_SMALL
:
1502 DEBUG ((DEBUG_VERBOSE
, "%a: buffer too small\n", __FUNCTION__
));
1505 case RETURN_UNSUPPORTED
:
1506 DEBUG ((DEBUG_VERBOSE
, "%a: unsupported\n", __FUNCTION__
));
1509 case RETURN_PROTOCOL_ERROR
:
1512 "%a: logic error / system state mismatch\n",
1525 Connect devices based on the boot order retrieved from QEMU.
1527 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
1528 OpenFirmware device paths therein to UEFI device path fragments. Connect the
1529 devices identified by the UEFI devpath prefixes as narrowly as possible, then
1530 connect all their child devices, recursively.
1532 If this function fails, then platform BDS should fall back to
1533 EfiBootManagerConnectAll(), or some other method for connecting any expected
1536 @retval RETURN_SUCCESS The "bootorder" fw_cfg file has been
1537 parsed, and the referenced device-subtrees
1538 have been connected.
1540 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
1542 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
1545 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
1547 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
1549 @return Error statuses propagated from underlying
1554 ConnectDevicesFromQemu (
1558 RETURN_STATUS Status
;
1559 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1562 EFI_STATUS EfiStatus
;
1563 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
1564 CONST CHAR8
*FwCfgPtr
;
1566 UINTN TranslatedSize
;
1567 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
1569 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
1570 if (RETURN_ERROR (Status
)) {
1574 if (FwCfgSize
== 0) {
1575 return RETURN_NOT_FOUND
;
1578 FwCfg
= AllocatePool (FwCfgSize
);
1579 if (FwCfg
== NULL
) {
1580 return RETURN_OUT_OF_RESOURCES
;
1583 QemuFwCfgSelectItem (FwCfgItem
);
1584 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
1585 if (FwCfg
[FwCfgSize
- 1] != '\0') {
1586 Status
= RETURN_INVALID_PARAMETER
;
1590 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
1591 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
1592 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
1594 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1595 EfiStatus
= CreateExtraRootBusMap (&ExtraPciRoots
);
1596 if (EFI_ERROR (EfiStatus
)) {
1597 Status
= (RETURN_STATUS
)EfiStatus
;
1601 ExtraPciRoots
= NULL
;
1605 // Translate each OpenFirmware path to a UEFI devpath prefix.
1609 TranslatedSize
= ARRAY_SIZE (Translated
);
1610 Status
= TranslateOfwPath (
1616 while (!RETURN_ERROR (Status
)) {
1617 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1618 EFI_HANDLE Controller
;
1621 // Convert the UEFI devpath prefix to binary representation.
1623 ASSERT (Translated
[TranslatedSize
] == L
'\0');
1624 DevicePath
= ConvertTextToDevicePath (Translated
);
1625 if (DevicePath
== NULL
) {
1626 Status
= RETURN_OUT_OF_RESOURCES
;
1627 goto FreeExtraPciRoots
;
1631 // Advance along DevicePath, connecting the nodes individually, and asking
1632 // drivers not to produce sibling nodes. Retrieve the controller handle
1633 // associated with the full DevicePath -- this is the device that QEMU's
1634 // OFW devpath refers to.
1636 EfiStatus
= EfiBootManagerConnectDevicePath (DevicePath
, &Controller
);
1637 FreePool (DevicePath
);
1638 if (EFI_ERROR (EfiStatus
)) {
1639 Status
= (RETURN_STATUS
)EfiStatus
;
1640 goto FreeExtraPciRoots
;
1644 // Because QEMU's OFW devpaths have lesser expressive power than UEFI
1645 // devpaths (i.e., DevicePath is considered a prefix), connect the tree
1646 // rooted at Controller, recursively. If no children are produced
1647 // (EFI_NOT_FOUND), that's OK.
1649 EfiStatus
= gBS
->ConnectController (Controller
, NULL
, NULL
, TRUE
);
1650 if (EFI_ERROR (EfiStatus
) && (EfiStatus
!= EFI_NOT_FOUND
)) {
1651 Status
= (RETURN_STATUS
)EfiStatus
;
1652 goto FreeExtraPciRoots
;
1657 // Move to the next OFW devpath.
1659 TranslatedSize
= ARRAY_SIZE (Translated
);
1660 Status
= TranslateOfwPath (
1668 if ((Status
== RETURN_NOT_FOUND
) && (NumConnected
> 0)) {
1671 "%a: %Lu OpenFirmware device path(s) connected\n",
1673 (UINT64
)NumConnected
1675 Status
= RETURN_SUCCESS
;
1679 if (ExtraPciRoots
!= NULL
) {
1680 DestroyExtraRootBusMap (ExtraPciRoots
);
1691 Convert the UEFI DevicePath to full text representation with DevPathToText,
1692 then match the UEFI device path fragment in Translated against it.
1694 @param[in] Translated UEFI device path fragment, translated from
1695 OpenFirmware format, to search for.
1697 @param[in] TranslatedLength The length of Translated in CHAR16's.
1699 @param[in] DevicePath Boot option device path whose textual rendering
1702 @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.
1705 @retval TRUE If Translated was found at the beginning of DevicePath after
1706 converting the latter to text.
1708 @retval FALSE If DevicePath was NULL, or it could not be converted, or there
1715 IN CONST CHAR16
*Translated
,
1716 IN UINTN TranslatedLength
,
1717 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1724 EFI_DEVICE_PATH_PROTOCOL
*AbsDevicePath
;
1725 CHAR16
*AbsConverted
;
1727 EFI_DEVICE_PATH_PROTOCOL
*Node
;
1729 Converted
= ConvertDevicePathToText (
1731 FALSE
, // DisplayOnly
1732 FALSE
// AllowShortcuts
1734 if (Converted
== NULL
) {
1741 // Expand the short-form device path to full device path
1743 if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1744 (DevicePathSubType (DevicePath
) == MEDIA_HARDDRIVE_DP
))
1747 // Harddrive shortform device path
1750 } else if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1751 (DevicePathSubType (DevicePath
) == MEDIA_FILEPATH_DP
))
1754 // File-path shortform device path
1757 } else if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
1758 (DevicePathSubType (DevicePath
) == MSG_URI_DP
))
1761 // URI shortform device path
1765 for ( Node
= DevicePath
1766 ; !IsDevicePathEnd (Node
)
1767 ; Node
= NextDevicePathNode (Node
)
1770 if ((DevicePathType (Node
) == MESSAGING_DEVICE_PATH
) &&
1771 ((DevicePathSubType (Node
) == MSG_USB_CLASS_DP
) ||
1772 (DevicePathSubType (Node
) == MSG_USB_WWID_DP
)))
1781 // Attempt to expand any relative UEFI device path to
1782 // an absolute device path first.
1785 FileBuffer
= EfiBootManagerGetLoadOptionBuffer (
1790 if (FileBuffer
== NULL
) {
1794 FreePool (FileBuffer
);
1795 AbsConverted
= ConvertDevicePathToText (AbsDevicePath
, FALSE
, FALSE
);
1796 FreePool (AbsDevicePath
);
1797 if (AbsConverted
== NULL
) {
1803 "%a: expanded relative device path \"%s\" for prefix matching\n",
1807 FreePool (Converted
);
1808 Converted
= AbsConverted
;
1812 // Is Translated a prefix of Converted?
1814 Result
= (BOOLEAN
)(StrnCmp (Converted
, Translated
, TranslatedLength
) == 0);
1817 "%a: against \"%s\": %a\n",
1820 Result
? "match" : "no match"
1823 FreePool (Converted
);
1828 Append some of the unselected active boot options to the boot order.
1830 This function should accommodate any further policy changes in "boot option
1831 survival". Currently we're adding back everything that starts with neither
1832 PciRoot() nor HD() nor a virtio-mmio VenHw() node.
1834 @param[in,out] BootOrder The structure holding the boot order to
1835 complete. The caller is responsible for
1836 initializing (and potentially populating) it
1837 before calling this function.
1839 @param[in,out] ActiveOption The array of active boot options to scan.
1840 Entries marked as Appended will be skipped.
1841 Those of the rest that satisfy the survival
1842 policy will be added to BootOrder with
1845 @param[in] ActiveCount Number of elements in ActiveOption.
1848 @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot
1851 @return Error codes returned by BootOrderAppend().
1856 IN OUT BOOT_ORDER
*BootOrder
,
1857 IN OUT ACTIVE_OPTION
*ActiveOption
,
1858 IN UINTN ActiveCount
1861 RETURN_STATUS Status
;
1864 Status
= RETURN_SUCCESS
;
1866 while (!RETURN_ERROR (Status
) && Idx
< ActiveCount
) {
1867 if (!ActiveOption
[Idx
].Appended
) {
1868 CONST EFI_BOOT_MANAGER_LOAD_OPTION
*Current
;
1869 CONST EFI_DEVICE_PATH_PROTOCOL
*FirstNode
;
1871 Current
= ActiveOption
[Idx
].BootOption
;
1872 FirstNode
= Current
->FilePath
;
1873 if (FirstNode
!= NULL
) {
1875 STATIC CHAR16 ConvFallBack
[] = L
"<unable to convert>";
1878 Converted
= ConvertDevicePathToText (FirstNode
, FALSE
, FALSE
);
1879 if (Converted
== NULL
) {
1880 Converted
= ConvFallBack
;
1884 if ((DevicePathType (FirstNode
) == MEDIA_DEVICE_PATH
) &&
1885 (DevicePathSubType (FirstNode
) == MEDIA_HARDDRIVE_DP
))
1891 } else if ((DevicePathType (FirstNode
) == ACPI_DEVICE_PATH
) &&
1892 (DevicePathSubType (FirstNode
) == ACPI_DP
))
1894 ACPI_HID_DEVICE_PATH
*Acpi
;
1896 Acpi
= (ACPI_HID_DEVICE_PATH
*)FirstNode
;
1897 if (((Acpi
->HID
& PNP_EISA_ID_MASK
) == PNP_EISA_ID_CONST
) &&
1898 (EISA_ID_TO_NUM (Acpi
->HID
) == 0x0a03))
1901 // drop PciRoot() if we enabled the user to select PCI-like boot
1902 // options, by providing translation for such OFW device path
1905 Keep
= !FeaturePcdGet (PcdQemuBootOrderPciTranslation
);
1907 } else if ((DevicePathType (FirstNode
) == HARDWARE_DEVICE_PATH
) &&
1908 (DevicePathSubType (FirstNode
) == HW_VENDOR_DP
))
1910 VENDOR_DEVICE_PATH
*VenHw
;
1912 VenHw
= (VENDOR_DEVICE_PATH
*)FirstNode
;
1913 if (CompareGuid (&VenHw
->Guid
, &gVirtioMmioTransportGuid
)) {
1915 // drop virtio-mmio if we enabled the user to select boot options
1916 // referencing such device paths
1918 Keep
= !FeaturePcdGet (PcdQemuBootOrderMmioTranslation
);
1923 Status
= BootOrderAppend (BootOrder
, &ActiveOption
[Idx
]);
1924 if (!RETURN_ERROR (Status
)) {
1927 "%a: keeping \"%s\"\n",
1935 "%a: dropping \"%s\"\n",
1941 if (Converted
!= ConvFallBack
) {
1942 FreePool (Converted
);
1954 Delete Boot#### variables that stand for such active boot options that have
1955 been dropped (ie. have not been selected by either matching or "survival
1958 @param[in] ActiveOption The array of active boot options to scan. Each
1959 entry not marked as appended will trigger the
1960 deletion of the matching Boot#### variable.
1962 @param[in] ActiveCount Number of elements in ActiveOption.
1966 PruneBootVariables (
1967 IN CONST ACTIVE_OPTION
*ActiveOption
,
1968 IN UINTN ActiveCount
1973 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
1974 if (!ActiveOption
[Idx
].Appended
) {
1975 CHAR16 VariableName
[9];
1977 UnicodeSPrintAsciiFormat (
1979 sizeof VariableName
,
1981 ActiveOption
[Idx
].BootOption
->OptionNumber
1985 // "The space consumed by the deleted variable may not be available until
1986 // the next power cycle", but that's good enough.
1990 &gEfiGlobalVariableGuid
,
1991 0, // Attributes, 0 means deletion
1992 0, // DataSize, 0 means deletion
2001 Set the boot order based on configuration retrieved from QEMU.
2003 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
2004 OpenFirmware device paths therein to UEFI device path fragments. Match the
2005 translated fragments against the current list of boot options, and rewrite
2006 the BootOrder NvVar so that it corresponds to the order described in fw_cfg.
2008 Platform BDS should call this function after connecting any expected boot
2009 devices and calling EfiBootManagerRefreshAllBootOption ().
2011 @retval RETURN_SUCCESS BootOrder NvVar rewritten.
2013 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
2015 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
2016 file, or no match found between the
2017 "bootorder" fw_cfg file and BootOptionList.
2019 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
2021 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
2023 @return Values returned by gBS->LocateProtocol ()
2024 or gRT->SetVariable ().
2029 SetBootOrderFromQemu (
2033 RETURN_STATUS Status
;
2034 FIRMWARE_CONFIG_ITEM FwCfgItem
;
2037 CONST CHAR8
*FwCfgPtr
;
2039 BOOT_ORDER BootOrder
;
2040 ACTIVE_OPTION
*ActiveOption
;
2043 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
2045 UINTN TranslatedSize
;
2046 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
2047 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
2048 UINTN BootOptionCount
;
2050 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
2051 if (Status
!= RETURN_SUCCESS
) {
2055 if (FwCfgSize
== 0) {
2056 return RETURN_NOT_FOUND
;
2059 FwCfg
= AllocatePool (FwCfgSize
);
2060 if (FwCfg
== NULL
) {
2061 return RETURN_OUT_OF_RESOURCES
;
2064 QemuFwCfgSelectItem (FwCfgItem
);
2065 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
2066 if (FwCfg
[FwCfgSize
- 1] != '\0') {
2067 Status
= RETURN_INVALID_PARAMETER
;
2068 goto ErrorFreeFwCfg
;
2071 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
2072 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
2073 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
2076 BootOrder
.Produced
= 0;
2077 BootOrder
.Allocated
= 1;
2078 BootOrder
.Data
= AllocatePool (
2079 BootOrder
.Allocated
* sizeof (*BootOrder
.Data
)
2081 if (BootOrder
.Data
== NULL
) {
2082 Status
= RETURN_OUT_OF_RESOURCES
;
2083 goto ErrorFreeFwCfg
;
2086 BootOptions
= EfiBootManagerGetLoadOptions (
2090 if (BootOptions
== NULL
) {
2091 Status
= RETURN_NOT_FOUND
;
2092 goto ErrorFreeBootOrder
;
2095 Status
= CollectActiveOptions (
2101 if (RETURN_ERROR (Status
)) {
2102 goto ErrorFreeBootOptions
;
2105 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
2106 Status
= CreateExtraRootBusMap (&ExtraPciRoots
);
2107 if (EFI_ERROR (Status
)) {
2108 goto ErrorFreeActiveOption
;
2111 ExtraPciRoots
= NULL
;
2115 // translate each OpenFirmware path
2117 TranslatedSize
= ARRAY_SIZE (Translated
);
2118 Status
= TranslateOfwPath (
2124 while (Status
== RETURN_SUCCESS
||
2125 Status
== RETURN_UNSUPPORTED
||
2126 Status
== RETURN_PROTOCOL_ERROR
||
2127 Status
== RETURN_BUFFER_TOO_SMALL
)
2129 if (Status
== RETURN_SUCCESS
) {
2133 // match translated OpenFirmware path against all active boot options
2135 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
2136 if (!ActiveOption
[Idx
].Appended
&&
2139 TranslatedSize
, // contains length, not size, in CHAR16's here
2140 ActiveOption
[Idx
].BootOption
->FilePath
2145 // match found, store ID and continue with next OpenFirmware path
2147 Status
= BootOrderAppend (&BootOrder
, &ActiveOption
[Idx
]);
2148 if (Status
!= RETURN_SUCCESS
) {
2149 goto ErrorFreeExtraPciRoots
;
2152 } // scanned all active boot options
2153 } // translation successful
2155 TranslatedSize
= ARRAY_SIZE (Translated
);
2156 Status
= TranslateOfwPath (
2162 } // scanning of OpenFirmware paths done
2164 if ((Status
== RETURN_NOT_FOUND
) && (BootOrder
.Produced
> 0)) {
2166 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
2167 // Some of the active boot options that have not been selected over fw_cfg
2168 // should be preserved at the end of the boot order.
2170 Status
= BootOrderComplete (&BootOrder
, ActiveOption
, ActiveCount
);
2171 if (RETURN_ERROR (Status
)) {
2172 goto ErrorFreeExtraPciRoots
;
2176 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
2179 Status
= gRT
->SetVariable (
2181 &gEfiGlobalVariableGuid
,
2182 EFI_VARIABLE_NON_VOLATILE
|
2183 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
2184 EFI_VARIABLE_RUNTIME_ACCESS
,
2185 BootOrder
.Produced
* sizeof (*BootOrder
.Data
),
2188 if (EFI_ERROR (Status
)) {
2191 "%a: setting BootOrder: %r\n",
2195 goto ErrorFreeExtraPciRoots
;
2198 DEBUG ((DEBUG_INFO
, "%a: setting BootOrder: success\n", __FUNCTION__
));
2199 PruneBootVariables (ActiveOption
, ActiveCount
);
2202 ErrorFreeExtraPciRoots
:
2203 if (ExtraPciRoots
!= NULL
) {
2204 DestroyExtraRootBusMap (ExtraPciRoots
);
2207 ErrorFreeActiveOption
:
2208 FreePool (ActiveOption
);
2210 ErrorFreeBootOptions
:
2211 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
2214 FreePool (BootOrder
.Data
);
2223 Calculate the number of seconds we should be showing the FrontPage progress
2226 @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().
2230 GetFrontPageTimeoutFromQemu (
2234 FIRMWARE_CONFIG_ITEM BootMenuWaitItem
;
2235 UINTN BootMenuWaitSize
;
2237 QemuFwCfgSelectItem (QemuFwCfgItemBootMenu
);
2238 if (QemuFwCfgRead16 () == 0) {
2240 // The user specified "-boot menu=off", or didn't specify "-boot
2241 // menu=(on|off)" at all. Return the platform default.
2243 return PcdGet16 (PcdPlatformBootTimeOut
);
2248 "etc/boot-menu-wait",
2253 (BootMenuWaitSize
!= sizeof (UINT16
)))
2256 // "-boot menu=on" was specified without "splash-time=N". In this case,
2257 // return three seconds if the platform default would cause us to skip the
2258 // front page, and return the platform default otherwise.
2262 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
2271 // "-boot menu=on,splash-time=N" was specified, where N is in units of
2272 // milliseconds. The Intel BDS Front Page progress bar only supports whole
2273 // seconds, round N up.
2275 QemuFwCfgSelectItem (BootMenuWaitItem
);
2276 return (UINT16
)((QemuFwCfgRead16 () + 999) / 1000);