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
46 Simple character classification routines, corresponding to POSIX class names
55 return (('0' <= Chr
&& Chr
<= '9') ||
56 ('A' <= Chr
&& Chr
<= 'Z') ||
57 ('a' <= Chr
&& Chr
<= 'z')
68 return (Chr
== ',' || Chr
== '.' || Chr
== '_' ||
69 Chr
== '+' || Chr
== '-'
80 return (32 <= Chr
&& Chr
<= 126 &&
81 Chr
!= '/' && Chr
!= '@' && Chr
!= ':');
86 Utility types and functions.
89 CONST CHAR8
*Ptr
; // not necessarily NUL-terminated
90 UINTN Len
; // number of non-NUL characters
96 Check if Substring and String have identical contents.
98 The function relies on the restriction that a SUBSTRING cannot have embedded
101 @param[in] Substring The SUBSTRING input to the comparison.
103 @param[in] String The ASCII string input to the comparison.
106 @return Whether the inputs have identical contents.
112 IN SUBSTRING Substring
,
113 IN CONST CHAR8
*String
122 while (Pos
< Substring
.Len
&& Substring
.Ptr
[Pos
] == *Chr
) {
127 return (BOOLEAN
)(Pos
== Substring
.Len
&& *Chr
== '\0');
133 Parse a comma-separated list of hexadecimal integers into the elements of an
136 Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
137 or an empty string are not allowed; they are rejected.
139 The function relies on ASCII encoding.
141 @param[in] UnitAddress The substring to parse.
143 @param[out] Result The array, allocated by the caller, to receive
144 the parsed values. This parameter may be NULL if
145 NumResults is zero on input.
147 @param[in out] NumResults On input, the number of elements allocated for
148 Result. On output, the number of elements it has
149 taken (or would have taken) to parse the string
153 @retval RETURN_SUCCESS UnitAddress has been fully parsed.
154 NumResults is set to the number of parsed
155 values; the corresponding elements have
156 been set in Result. The rest of Result's
157 elements are unchanged.
159 @retval RETURN_BUFFER_TOO_SMALL UnitAddress has been fully parsed.
160 NumResults is set to the number of parsed
161 values, but elements have been stored only
162 up to the input value of NumResults, which
163 is less than what has been parsed.
165 @retval RETURN_INVALID_PARAMETER Parse error. The contents of Results is
166 indeterminate. NumResults has not been
172 ParseUnitAddressHexList (
173 IN SUBSTRING UnitAddress
,
175 IN OUT UINTN
*NumResults
178 UINTN Entry
; // number of entry currently being parsed
179 UINT64 EntryVal
; // value being constructed for current entry
180 CHAR8 PrevChr
; // UnitAddress character previously checked
181 UINTN Pos
; // current position within UnitAddress
182 RETURN_STATUS Status
;
188 for (Pos
= 0; Pos
< UnitAddress
.Len
; ++Pos
) {
192 Chr
= UnitAddress
.Ptr
[Pos
];
193 Val
= ('a' <= Chr
&& Chr
<= 'f') ? (Chr
- 'a' + 10) :
194 ('A' <= Chr
&& Chr
<= 'F') ? (Chr
- 'A' + 10) :
195 ('0' <= Chr
&& Chr
<= '9') ? (Chr
- '0' ) :
199 if (EntryVal
> 0xFFFFFFFFFFFFFFFull
) {
200 return RETURN_INVALID_PARAMETER
;
202 EntryVal
= LShiftU64 (EntryVal
, 4) | Val
;
203 } else if (Chr
== ',') {
204 if (PrevChr
== ',') {
205 return RETURN_INVALID_PARAMETER
;
207 if (Entry
< *NumResults
) {
208 Result
[Entry
] = EntryVal
;
213 return RETURN_INVALID_PARAMETER
;
219 if (PrevChr
== ',') {
220 return RETURN_INVALID_PARAMETER
;
222 if (Entry
< *NumResults
) {
223 Result
[Entry
] = EntryVal
;
224 Status
= RETURN_SUCCESS
;
226 Status
= RETURN_BUFFER_TOO_SMALL
;
236 A simple array of Boot Option ID's.
246 Array element tracking an enumerated boot option that has the
247 LOAD_OPTION_ACTIVE attribute.
250 CONST EFI_BOOT_MANAGER_LOAD_OPTION
*BootOption
; // reference only, no
252 BOOLEAN Appended
; // has been added to a
259 Append an active boot option to BootOrder, reallocating the latter if needed.
261 @param[in out] BootOrder The structure pointing to the array and holding
262 allocation and usage counters.
264 @param[in] ActiveOption The active boot option whose ID should be
265 appended to the array.
268 @retval RETURN_SUCCESS ID of ActiveOption appended.
270 @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.
276 IN OUT BOOT_ORDER
*BootOrder
,
277 IN OUT ACTIVE_OPTION
*ActiveOption
280 if (BootOrder
->Produced
== BootOrder
->Allocated
) {
284 ASSERT (BootOrder
->Allocated
> 0);
285 AllocatedNew
= BootOrder
->Allocated
* 2;
286 DataNew
= ReallocatePool (
287 BootOrder
->Allocated
* sizeof (*BootOrder
->Data
),
288 AllocatedNew
* sizeof (*DataNew
),
291 if (DataNew
== NULL
) {
292 return RETURN_OUT_OF_RESOURCES
;
294 BootOrder
->Allocated
= AllocatedNew
;
295 BootOrder
->Data
= DataNew
;
298 BootOrder
->Data
[BootOrder
->Produced
++] =
299 (UINT16
) ActiveOption
->BootOption
->OptionNumber
;
300 ActiveOption
->Appended
= TRUE
;
301 return RETURN_SUCCESS
;
307 Create an array of ACTIVE_OPTION elements for a boot option array.
309 @param[in] BootOptions A boot option array, created with
310 EfiBootManagerRefreshAllBootOption () and
311 EfiBootManagerGetLoadOptions ().
313 @param[in] BootOptionCount The number of elements in BootOptions.
315 @param[out] ActiveOption Pointer to the first element in the new array.
316 The caller is responsible for freeing the array
317 with FreePool() after use.
319 @param[out] Count Number of elements in the new array.
322 @retval RETURN_SUCCESS The ActiveOption array has been created.
324 @retval RETURN_NOT_FOUND No active entry has been found in
327 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
332 CollectActiveOptions (
333 IN CONST EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
,
334 IN UINTN BootOptionCount
,
335 OUT ACTIVE_OPTION
**ActiveOption
,
342 *ActiveOption
= NULL
;
345 // Scan the list twice:
346 // - count active entries,
347 // - store links to active entries.
349 for (ScanMode
= 0; ScanMode
< 2; ++ScanMode
) {
351 for (Index
= 0; Index
< BootOptionCount
; Index
++) {
352 if ((BootOptions
[Index
].Attributes
& LOAD_OPTION_ACTIVE
) != 0) {
354 (*ActiveOption
)[*Count
].BootOption
= &BootOptions
[Index
];
355 (*ActiveOption
)[*Count
].Appended
= FALSE
;
363 return RETURN_NOT_FOUND
;
365 *ActiveOption
= AllocatePool (*Count
* sizeof **ActiveOption
);
366 if (*ActiveOption
== NULL
) {
367 return RETURN_OUT_OF_RESOURCES
;
371 return RETURN_SUCCESS
;
376 OpenFirmware device path node
379 SUBSTRING DriverName
;
380 SUBSTRING UnitAddress
;
381 SUBSTRING DeviceArguments
;
387 Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
388 structure, and advance in the input string.
390 The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"
391 (a leading slash is expected and not returned):
393 /driver-name@unit-address[:device-arguments][<LF>]
395 A single trailing <LF> character is consumed but not returned. A trailing
396 <LF> or NUL character terminates the device path.
398 The function relies on ASCII encoding.
400 @param[in out] Ptr Address of the pointer pointing to the start of the
401 node string. After successful parsing *Ptr is set to
402 the byte immediately following the consumed
403 characters. On error it points to the byte that
404 caused the error. The input string is never modified.
406 @param[out] OfwNode The members of this structure point into the input
407 string, designating components of the node.
408 Separators are never included. If "device-arguments"
409 is missing, then DeviceArguments.Ptr is set to NULL.
410 All components that are present have nonzero length.
412 If the call doesn't succeed, the contents of this
413 structure is indeterminate.
415 @param[out] IsFinal In case of successful parsing, this parameter signals
416 whether the node just parsed is the final node in the
417 device path. The call after a final node will attempt
418 to start parsing the next path. If the call doesn't
419 succeed, then this parameter is not changed.
422 @retval RETURN_SUCCESS Parsing successful.
424 @retval RETURN_NOT_FOUND Parsing terminated. *Ptr was (and is)
425 pointing to an empty string.
427 @retval RETURN_INVALID_PARAMETER Parse error.
433 IN OUT CONST CHAR8
**Ptr
,
434 OUT OFW_NODE
*OfwNode
,
439 // A leading slash is expected. End of string is tolerated.
443 return RETURN_NOT_FOUND
;
450 return RETURN_INVALID_PARAMETER
;
456 OfwNode
->DriverName
.Ptr
= *Ptr
;
457 OfwNode
->DriverName
.Len
= 0;
458 while (OfwNode
->DriverName
.Len
< 32 &&
459 (IsAlnum (**Ptr
) || IsDriverNamePunct (**Ptr
))
462 ++OfwNode
->DriverName
.Len
;
465 if (OfwNode
->DriverName
.Len
== 0 || OfwNode
->DriverName
.Len
== 32) {
466 return RETURN_INVALID_PARAMETER
;
474 return RETURN_INVALID_PARAMETER
;
478 OfwNode
->UnitAddress
.Ptr
= *Ptr
;
479 OfwNode
->UnitAddress
.Len
= 0;
480 while (IsPrintNotDelim (**Ptr
)) {
482 ++OfwNode
->UnitAddress
.Len
;
485 if (OfwNode
->UnitAddress
.Len
== 0) {
486 return RETURN_INVALID_PARAMETER
;
491 // device-arguments, may be omitted
493 OfwNode
->DeviceArguments
.Len
= 0;
496 OfwNode
->DeviceArguments
.Ptr
= *Ptr
;
498 while (IsPrintNotDelim (**Ptr
)) {
500 ++OfwNode
->DeviceArguments
.Len
;
503 if (OfwNode
->DeviceArguments
.Len
== 0) {
504 return RETURN_INVALID_PARAMETER
;
508 OfwNode
->DeviceArguments
.Ptr
= NULL
;
527 return RETURN_INVALID_PARAMETER
;
532 "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
534 OfwNode
->DriverName
.Len
, OfwNode
->DriverName
.Ptr
,
535 OfwNode
->UnitAddress
.Len
, OfwNode
->UnitAddress
.Ptr
,
536 OfwNode
->DeviceArguments
.Len
,
537 OfwNode
->DeviceArguments
.Ptr
== NULL
? "" : OfwNode
->DeviceArguments
.Ptr
539 return RETURN_SUCCESS
;
545 Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
548 @param[in] OfwNode Array of OpenFirmware device nodes to
549 translate, constituting the beginning of an
550 OpenFirmware device path.
552 @param[in] NumNodes Number of elements in OfwNode.
554 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
555 CreateExtraRootBusMap(), to be used for
556 translating positions of extra root buses to
559 @param[out] Translated Destination array receiving the UEFI path
560 fragment, allocated by the caller. If the
561 return value differs from RETURN_SUCCESS, its
562 contents is indeterminate.
564 @param[in out] TranslatedSize On input, the number of CHAR16's in
565 Translated. On RETURN_SUCCESS this parameter
566 is assigned the number of non-NUL CHAR16's
567 written to Translated. In case of other return
568 values, TranslatedSize is indeterminate.
571 @retval RETURN_SUCCESS Translation successful.
573 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
576 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
577 be translated in the current implementation.
579 @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an
580 extra PCI root bus (by serial number) that
581 is invalid according to ExtraPciRoots.
586 TranslatePciOfwNodes (
587 IN CONST OFW_NODE
*OfwNode
,
589 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
590 OUT CHAR16
*Translated
,
591 IN OUT UINTN
*TranslatedSize
596 UINTN FirstNonBridge
;
597 CHAR16 Bridges
[BRIDGE_TRANSLATION_OUTPUT_SIZE
];
604 // Resolve the PCI root bus number.
606 // The initial OFW node for the main root bus (ie. bus number 0) is:
610 // For extra root buses, the initial OFW node is
614 // root bus serial number (not PCI bus number)
616 if (NumNodes
< REQUIRED_PCI_OFW_NODES
||
617 !SubstringEq (OfwNode
[0].DriverName
, "pci")
619 return RETURN_UNSUPPORTED
;
623 Comma
= ScanMem8 (OfwNode
[0].UnitAddress
.Ptr
, OfwNode
[0].UnitAddress
.Len
,
626 SUBSTRING PciRootSerialSubString
;
627 UINT64 PciRootSerial
;
630 // Parse the root bus serial number from the unit address after the comma.
632 PciRootSerialSubString
.Ptr
= Comma
+ 1;
633 PciRootSerialSubString
.Len
= OfwNode
[0].UnitAddress
.Len
-
634 (PciRootSerialSubString
.Ptr
-
635 OfwNode
[0].UnitAddress
.Ptr
);
637 if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString
,
638 &PciRootSerial
, &NumEntries
))) {
639 return RETURN_UNSUPPORTED
;
643 // Map the extra root bus's serial number to its actual bus number.
645 if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots
, PciRootSerial
,
647 return RETURN_PROTOCOL_ERROR
;
652 // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
656 // PCI slot & function on the parent, holding the bridge
658 // and the UEFI device path node is:
666 UINT64 BridgeDevFun
[2];
667 UINTN BridgesFreeBytes
;
669 if (!SubstringEq (OfwNode
[FirstNonBridge
].DriverName
, "pci-bridge")) {
674 NumEntries
= sizeof BridgeDevFun
/ sizeof BridgeDevFun
[0];
675 if (ParseUnitAddressHexList (OfwNode
[FirstNonBridge
].UnitAddress
,
676 BridgeDevFun
, &NumEntries
) != RETURN_SUCCESS
) {
677 return RETURN_UNSUPPORTED
;
680 BridgesFreeBytes
= sizeof Bridges
- BridgesLen
* sizeof Bridges
[0];
681 Written
= UnicodeSPrintAsciiFormat (Bridges
+ BridgesLen
, BridgesFreeBytes
,
682 "/Pci(0x%Lx,0x%Lx)", BridgeDevFun
[0], BridgeDevFun
[1]);
683 BridgesLen
+= Written
;
686 // There's no way to differentiate between "completely used up without
687 // truncation" and "truncated", so treat the former as the latter.
689 if (BridgesLen
+ 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE
) {
690 return RETURN_UNSUPPORTED
;
694 } while (FirstNonBridge
< NumNodes
);
696 if (FirstNonBridge
== NumNodes
) {
697 return RETURN_UNSUPPORTED
;
701 // Parse the OFW nodes starting with the first non-bridge node.
704 NumEntries
= ARRAY_SIZE (PciDevFun
);
705 if (ParseUnitAddressHexList (
706 OfwNode
[FirstNonBridge
].UnitAddress
,
711 return RETURN_UNSUPPORTED
;
714 if (NumNodes
>= FirstNonBridge
+ 3 &&
715 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "ide") &&
716 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
717 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
720 // OpenFirmware device path (IDE disk, IDE CD-ROM):
722 // /pci@i0cf8/ide@1,1/drive@0/disk@0
724 // | | | | master or slave
725 // | | | primary or secondary
726 // | PCI slot & function holding IDE controller
727 // PCI root at system bus port, PIO
731 // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
739 if (ParseUnitAddressHexList (
740 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
743 ) != RETURN_SUCCESS
||
745 ParseUnitAddressHexList (
746 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
748 &NumEntries
// reuse after previous single-element call
749 ) != RETURN_SUCCESS
||
752 return RETURN_UNSUPPORTED
;
755 Written
= UnicodeSPrintAsciiFormat (
757 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
758 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
763 Secondary
? "Secondary" : "Primary",
764 Slave
? "Slave" : "Master"
766 } else if (NumNodes
>= FirstNonBridge
+ 3 &&
767 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,2922") &&
768 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
769 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
772 // OpenFirmware device path (Q35 SATA disk and CD-ROM):
774 // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
776 // | | | | device number (fixed 0)
777 // | | | channel (port) number
778 // | PCI slot & function holding SATA HBA
779 // PCI root at system bus port, PIO
783 // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
785 // | | LUN (always 0 on Q35)
786 // | port multiplier port number,
787 // | always 0xFFFF on Q35
788 // channel (port) number
793 if (RETURN_ERROR (ParseUnitAddressHexList (
794 OfwNode
[FirstNonBridge
+ 1].UnitAddress
, &Channel
,
796 return RETURN_UNSUPPORTED
;
799 Written
= UnicodeSPrintAsciiFormat (
801 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
802 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
809 } else if (NumNodes
>= FirstNonBridge
+ 3 &&
810 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "isa") &&
811 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "fdc") &&
812 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "floppy")
815 // OpenFirmware device path (floppy disk):
817 // /pci@i0cf8/isa@1/fdc@03f0/floppy@0
820 // | | ISA controller io-port (hex)
821 // | PCI slot holding ISA controller
822 // PCI root at system bus port, PIO
826 // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
833 if (ParseUnitAddressHexList (
834 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
837 ) != RETURN_SUCCESS
||
840 return RETURN_UNSUPPORTED
;
843 Written
= UnicodeSPrintAsciiFormat (
845 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
846 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
853 } else if (NumNodes
>= FirstNonBridge
+ 2 &&
854 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
855 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "disk")
858 // OpenFirmware device path (virtio-blk disk):
860 // /pci@i0cf8/scsi@6[,3]/disk@0,0
863 // | | PCI function corresponding to disk (optional)
864 // | PCI slot holding disk
865 // PCI root at system bus port, PIO
867 // UEFI device path prefix:
869 // PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
870 // PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
872 Written
= UnicodeSPrintAsciiFormat (
874 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
875 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
881 } else if (NumNodes
>= FirstNonBridge
+ 3 &&
882 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
883 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "channel") &&
884 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
887 // OpenFirmware device path (virtio-scsi disk):
889 // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
893 // | | channel (unused, fixed 0)
894 // | PCI slot[, function] holding SCSI controller
895 // PCI root at system bus port, PIO
897 // UEFI device path prefix:
899 // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
900 // -- if PCI function is 0 or absent
901 // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
902 // -- if PCI function is present and nonzero
907 NumEntries
= ARRAY_SIZE (TargetLun
);
908 if (ParseUnitAddressHexList (
909 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
914 return RETURN_UNSUPPORTED
;
917 Written
= UnicodeSPrintAsciiFormat (
919 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
920 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
928 } else if (NumNodes
>= FirstNonBridge
+ 2 &&
929 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,5845") &&
930 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "namespace")
933 // OpenFirmware device path (NVMe device):
935 // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
937 // | | | | Extended Unique Identifier
938 // | | | | (EUI-64), big endian interp.
939 // | | | namespace ID
940 // | PCI slot & function holding NVMe controller
941 // PCI root at system bus port, PIO
945 // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
947 // | octets of the EUI-64
948 // | in address order
952 UINTN RequiredEntries
;
955 RequiredEntries
= ARRAY_SIZE (Namespace
);
956 NumEntries
= RequiredEntries
;
957 if (ParseUnitAddressHexList (
958 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
961 ) != RETURN_SUCCESS
||
962 NumEntries
!= RequiredEntries
||
964 Namespace
[0] >= MAX_UINT32
966 return RETURN_UNSUPPORTED
;
969 Eui64
= (UINT8
*)&Namespace
[1];
970 Written
= UnicodeSPrintAsciiFormat (
972 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
973 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
974 "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
980 Eui64
[7], Eui64
[6], Eui64
[5], Eui64
[4],
981 Eui64
[3], Eui64
[2], Eui64
[1], Eui64
[0]
983 } else if (NumNodes
>= FirstNonBridge
+ 2 &&
984 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "usb") &&
985 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "storage")) {
987 // OpenFirmware device path (usb-storage device in XHCI port):
989 // /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0
991 // | | | | fixed fixed
992 // | | | XHCI port number, 1-based
993 // | | PCI function corresponding to XHCI (optional)
994 // | PCI slot holding XHCI
995 // PCI root at system bus port, PIO
997 // UEFI device path prefix:
999 // PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)
1001 // | XHCI port number in 0-based notation
1002 // 0x0 if PCI function is 0, or absent from OFW
1004 RETURN_STATUS ParseStatus
;
1005 UINT64 OneBasedXhciPort
;
1008 ParseStatus
= ParseUnitAddressHexList (
1009 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
1013 if (RETURN_ERROR (ParseStatus
) || OneBasedXhciPort
== 0) {
1014 return RETURN_UNSUPPORTED
;
1017 Written
= UnicodeSPrintAsciiFormat (
1019 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1020 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",
1025 OneBasedXhciPort
- 1
1029 // Generic OpenFirmware device path for PCI devices:
1031 // /pci@i0cf8/ethernet@3[,2]
1033 // | PCI slot[, function] holding Ethernet card
1034 // PCI root at system bus port, PIO
1036 // UEFI device path prefix (dependent on presence of nonzero PCI function):
1038 // PciRoot(0x0)/Pci(0x3,0x0)
1039 // PciRoot(0x0)/Pci(0x3,0x2)
1041 Written
= UnicodeSPrintAsciiFormat (
1043 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1044 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
1053 // There's no way to differentiate between "completely used up without
1054 // truncation" and "truncated", so treat the former as the latter, and return
1055 // success only for "some room left unused".
1057 if (Written
+ 1 < *TranslatedSize
) {
1058 *TranslatedSize
= Written
;
1059 return RETURN_SUCCESS
;
1062 return RETURN_BUFFER_TOO_SMALL
;
1067 // A type providing easy raw access to the base address of a virtio-mmio
1073 } VIRTIO_MMIO_BASE_ADDRESS
;
1078 Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
1081 @param[in] OfwNode Array of OpenFirmware device nodes to
1082 translate, constituting the beginning of an
1083 OpenFirmware device path.
1085 @param[in] NumNodes Number of elements in OfwNode.
1087 @param[out] Translated Destination array receiving the UEFI path
1088 fragment, allocated by the caller. If the
1089 return value differs from RETURN_SUCCESS, its
1090 contents is indeterminate.
1092 @param[in out] TranslatedSize On input, the number of CHAR16's in
1093 Translated. On RETURN_SUCCESS this parameter
1094 is assigned the number of non-NUL CHAR16's
1095 written to Translated. In case of other return
1096 values, TranslatedSize is indeterminate.
1099 @retval RETURN_SUCCESS Translation successful.
1101 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1104 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1105 be translated in the current implementation.
1110 TranslateMmioOfwNodes (
1111 IN CONST OFW_NODE
*OfwNode
,
1113 OUT CHAR16
*Translated
,
1114 IN OUT UINTN
*TranslatedSize
1117 VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase
;
1118 CHAR16 VenHwString
[60 + 1];
1123 // Get the base address of the virtio-mmio transport.
1125 if (NumNodes
< REQUIRED_MMIO_OFW_NODES
||
1126 !SubstringEq (OfwNode
[0].DriverName
, "virtio-mmio")
1128 return RETURN_UNSUPPORTED
;
1131 if (ParseUnitAddressHexList (
1132 OfwNode
[0].UnitAddress
,
1133 &VirtioMmioBase
.Uint64
,
1137 return RETURN_UNSUPPORTED
;
1140 UnicodeSPrintAsciiFormat (VenHwString
, sizeof VenHwString
,
1141 "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid
,
1142 VirtioMmioBase
.Raw
[0], VirtioMmioBase
.Raw
[1], VirtioMmioBase
.Raw
[2],
1143 VirtioMmioBase
.Raw
[3], VirtioMmioBase
.Raw
[4], VirtioMmioBase
.Raw
[5],
1144 VirtioMmioBase
.Raw
[6], VirtioMmioBase
.Raw
[7]);
1146 if (NumNodes
>= 2 &&
1147 SubstringEq (OfwNode
[1].DriverName
, "disk")) {
1149 // OpenFirmware device path (virtio-blk disk):
1151 // /virtio-mmio@000000000a003c00/disk@0,0
1154 // base address of virtio-mmio register block
1156 // UEFI device path prefix:
1160 Written
= UnicodeSPrintAsciiFormat (
1162 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1166 } else if (NumNodes
>= 3 &&
1167 SubstringEq (OfwNode
[1].DriverName
, "channel") &&
1168 SubstringEq (OfwNode
[2].DriverName
, "disk")) {
1170 // OpenFirmware device path (virtio-scsi disk):
1172 // /virtio-mmio@000000000a003a00/channel@0/disk@2,3
1176 // | channel (unused, fixed 0)
1177 // base address of virtio-mmio register block
1179 // UEFI device path prefix:
1181 // <VenHwString>/Scsi(0x2,0x3)
1183 UINT64 TargetLun
[2];
1186 NumEntries
= ARRAY_SIZE (TargetLun
);
1187 if (ParseUnitAddressHexList (
1188 OfwNode
[2].UnitAddress
,
1193 return RETURN_UNSUPPORTED
;
1196 Written
= UnicodeSPrintAsciiFormat (
1198 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1199 "%s/Scsi(0x%Lx,0x%Lx)",
1204 } else if (NumNodes
>= 2 &&
1205 SubstringEq (OfwNode
[1].DriverName
, "ethernet-phy")) {
1207 // OpenFirmware device path (virtio-net NIC):
1209 // /virtio-mmio@000000000a003e00/ethernet-phy@0
1212 // base address of virtio-mmio register block
1214 // UEFI device path prefix:
1218 Written
= UnicodeSPrintAsciiFormat (
1220 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1225 return RETURN_UNSUPPORTED
;
1229 // There's no way to differentiate between "completely used up without
1230 // truncation" and "truncated", so treat the former as the latter, and return
1231 // success only for "some room left unused".
1233 if (Written
+ 1 < *TranslatedSize
) {
1234 *TranslatedSize
= Written
;
1235 return RETURN_SUCCESS
;
1238 return RETURN_BUFFER_TOO_SMALL
;
1244 Translate an array of OpenFirmware device nodes to a UEFI device path
1247 @param[in] OfwNode Array of OpenFirmware device nodes to
1248 translate, constituting the beginning of an
1249 OpenFirmware device path.
1251 @param[in] NumNodes Number of elements in OfwNode.
1253 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1254 CreateExtraRootBusMap(), to be used for
1255 translating positions of extra root buses to
1258 @param[out] Translated Destination array receiving the UEFI path
1259 fragment, allocated by the caller. If the
1260 return value differs from RETURN_SUCCESS, its
1261 contents is indeterminate.
1263 @param[in out] TranslatedSize On input, the number of CHAR16's in
1264 Translated. On RETURN_SUCCESS this parameter
1265 is assigned the number of non-NUL CHAR16's
1266 written to Translated. In case of other return
1267 values, TranslatedSize is indeterminate.
1270 @retval RETURN_SUCCESS Translation successful.
1272 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1275 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1276 be translated in the current implementation.
1278 @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has
1279 been (partially) recognized, but it contains
1280 a logic error / doesn't match system state.
1286 IN CONST OFW_NODE
*OfwNode
,
1288 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1289 OUT CHAR16
*Translated
,
1290 IN OUT UINTN
*TranslatedSize
1293 RETURN_STATUS Status
;
1295 Status
= RETURN_UNSUPPORTED
;
1297 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1298 Status
= TranslatePciOfwNodes (OfwNode
, NumNodes
, ExtraPciRoots
,
1299 Translated
, TranslatedSize
);
1301 if (Status
== RETURN_UNSUPPORTED
&&
1302 FeaturePcdGet (PcdQemuBootOrderMmioTranslation
)) {
1303 Status
= TranslateMmioOfwNodes (OfwNode
, NumNodes
, Translated
,
1311 Translate an OpenFirmware device path fragment to a UEFI device path
1312 fragment, and advance in the input string.
1314 @param[in out] Ptr Address of the pointer pointing to the start
1315 of the path string. After successful
1316 translation (RETURN_SUCCESS) or at least
1317 successful parsing (RETURN_UNSUPPORTED,
1318 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
1319 byte immediately following the consumed
1320 characters. In other error cases, it points to
1321 the byte that caused the error.
1323 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1324 CreateExtraRootBusMap(), to be used for
1325 translating positions of extra root buses to
1328 @param[out] Translated Destination array receiving the UEFI path
1329 fragment, allocated by the caller. If the
1330 return value differs from RETURN_SUCCESS, its
1331 contents is indeterminate.
1333 @param[in out] TranslatedSize On input, the number of CHAR16's in
1334 Translated. On RETURN_SUCCESS this parameter
1335 is assigned the number of non-NUL CHAR16's
1336 written to Translated. In case of other return
1337 values, TranslatedSize is indeterminate.
1340 @retval RETURN_SUCCESS Translation successful.
1342 @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed
1343 successfully, but its translation did not
1344 fit into the number of bytes provided.
1345 Further calls to this function are
1348 @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed
1349 successfully, but it can't be translated in
1350 the current implementation. Further calls
1351 to this function are possible.
1353 @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been
1354 (partially) recognized, but it contains a
1355 logic error / doesn't match system state.
1356 Further calls to this function are
1359 @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was
1360 pointing to the empty string or "HALT". On
1361 output, *Ptr points to the empty string
1362 (ie. "HALT" is consumed transparently when
1365 @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.
1371 IN OUT CONST CHAR8
**Ptr
,
1372 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1373 OUT CHAR16
*Translated
,
1374 IN OUT UINTN
*TranslatedSize
1378 RETURN_STATUS Status
;
1379 OFW_NODE Node
[EXAMINED_OFW_NODES
];
1385 if (AsciiStrCmp (*Ptr
, "HALT") == 0) {
1387 Status
= RETURN_NOT_FOUND
;
1389 Status
= ParseOfwNode (Ptr
, &Node
[NumNodes
], &IsFinal
);
1392 if (Status
== RETURN_NOT_FOUND
) {
1393 DEBUG ((DEBUG_VERBOSE
, "%a: no more nodes\n", __FUNCTION__
));
1394 return RETURN_NOT_FOUND
;
1397 while (Status
== RETURN_SUCCESS
&& !IsFinal
) {
1399 Status
= ParseOfwNode (
1401 (NumNodes
< EXAMINED_OFW_NODES
) ? &Node
[NumNodes
] : &Skip
,
1407 case RETURN_SUCCESS
:
1411 case RETURN_INVALID_PARAMETER
:
1412 DEBUG ((DEBUG_VERBOSE
, "%a: parse error\n", __FUNCTION__
));
1413 return RETURN_INVALID_PARAMETER
;
1419 Status
= TranslateOfwNodes (
1421 NumNodes
< EXAMINED_OFW_NODES
? NumNodes
: EXAMINED_OFW_NODES
,
1426 case RETURN_SUCCESS
:
1427 DEBUG ((DEBUG_VERBOSE
, "%a: success: \"%s\"\n", __FUNCTION__
, Translated
));
1430 case RETURN_BUFFER_TOO_SMALL
:
1431 DEBUG ((DEBUG_VERBOSE
, "%a: buffer too small\n", __FUNCTION__
));
1434 case RETURN_UNSUPPORTED
:
1435 DEBUG ((DEBUG_VERBOSE
, "%a: unsupported\n", __FUNCTION__
));
1438 case RETURN_PROTOCOL_ERROR
:
1439 DEBUG ((DEBUG_VERBOSE
, "%a: logic error / system state mismatch\n",
1451 Connect devices based on the boot order retrieved from QEMU.
1453 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
1454 OpenFirmware device paths therein to UEFI device path fragments. Connect the
1455 devices identified by the UEFI devpath prefixes as narrowly as possible, then
1456 connect all their child devices, recursively.
1458 If this function fails, then platform BDS should fall back to
1459 EfiBootManagerConnectAll(), or some other method for connecting any expected
1462 @retval RETURN_SUCCESS The "bootorder" fw_cfg file has been
1463 parsed, and the referenced device-subtrees
1464 have been connected.
1466 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
1468 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
1471 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
1473 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
1475 @return Error statuses propagated from underlying
1480 ConnectDevicesFromQemu (
1484 RETURN_STATUS Status
;
1485 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1488 EFI_STATUS EfiStatus
;
1489 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
1490 CONST CHAR8
*FwCfgPtr
;
1492 UINTN TranslatedSize
;
1493 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
1495 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
1496 if (RETURN_ERROR (Status
)) {
1500 if (FwCfgSize
== 0) {
1501 return RETURN_NOT_FOUND
;
1504 FwCfg
= AllocatePool (FwCfgSize
);
1505 if (FwCfg
== NULL
) {
1506 return RETURN_OUT_OF_RESOURCES
;
1509 QemuFwCfgSelectItem (FwCfgItem
);
1510 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
1511 if (FwCfg
[FwCfgSize
- 1] != '\0') {
1512 Status
= RETURN_INVALID_PARAMETER
;
1515 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
1516 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
1517 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
1519 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1520 EfiStatus
= CreateExtraRootBusMap (&ExtraPciRoots
);
1521 if (EFI_ERROR (EfiStatus
)) {
1522 Status
= (RETURN_STATUS
)EfiStatus
;
1526 ExtraPciRoots
= NULL
;
1530 // Translate each OpenFirmware path to a UEFI devpath prefix.
1534 TranslatedSize
= ARRAY_SIZE (Translated
);
1535 Status
= TranslateOfwPath (&FwCfgPtr
, ExtraPciRoots
, Translated
,
1537 while (!RETURN_ERROR (Status
)) {
1538 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1539 EFI_HANDLE Controller
;
1542 // Convert the UEFI devpath prefix to binary representation.
1544 ASSERT (Translated
[TranslatedSize
] == L
'\0');
1545 DevicePath
= ConvertTextToDevicePath (Translated
);
1546 if (DevicePath
== NULL
) {
1547 Status
= RETURN_OUT_OF_RESOURCES
;
1548 goto FreeExtraPciRoots
;
1551 // Advance along DevicePath, connecting the nodes individually, and asking
1552 // drivers not to produce sibling nodes. Retrieve the controller handle
1553 // associated with the full DevicePath -- this is the device that QEMU's
1554 // OFW devpath refers to.
1556 EfiStatus
= EfiBootManagerConnectDevicePath (DevicePath
, &Controller
);
1557 FreePool (DevicePath
);
1558 if (EFI_ERROR (EfiStatus
)) {
1559 Status
= (RETURN_STATUS
)EfiStatus
;
1560 goto FreeExtraPciRoots
;
1563 // Because QEMU's OFW devpaths have lesser expressive power than UEFI
1564 // devpaths (i.e., DevicePath is considered a prefix), connect the tree
1565 // rooted at Controller, recursively. If no children are produced
1566 // (EFI_NOT_FOUND), that's OK.
1568 EfiStatus
= gBS
->ConnectController (Controller
, NULL
, NULL
, TRUE
);
1569 if (EFI_ERROR (EfiStatus
) && EfiStatus
!= EFI_NOT_FOUND
) {
1570 Status
= (RETURN_STATUS
)EfiStatus
;
1571 goto FreeExtraPciRoots
;
1575 // Move to the next OFW devpath.
1577 TranslatedSize
= ARRAY_SIZE (Translated
);
1578 Status
= TranslateOfwPath (&FwCfgPtr
, ExtraPciRoots
, Translated
,
1582 if (Status
== RETURN_NOT_FOUND
&& NumConnected
> 0) {
1583 DEBUG ((DEBUG_INFO
, "%a: %Lu OpenFirmware device path(s) connected\n",
1584 __FUNCTION__
, (UINT64
)NumConnected
));
1585 Status
= RETURN_SUCCESS
;
1589 if (ExtraPciRoots
!= NULL
) {
1590 DestroyExtraRootBusMap (ExtraPciRoots
);
1602 Convert the UEFI DevicePath to full text representation with DevPathToText,
1603 then match the UEFI device path fragment in Translated against it.
1605 @param[in] Translated UEFI device path fragment, translated from
1606 OpenFirmware format, to search for.
1608 @param[in] TranslatedLength The length of Translated in CHAR16's.
1610 @param[in] DevicePath Boot option device path whose textual rendering
1613 @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.
1616 @retval TRUE If Translated was found at the beginning of DevicePath after
1617 converting the latter to text.
1619 @retval FALSE If DevicePath was NULL, or it could not be converted, or there
1626 IN CONST CHAR16
*Translated
,
1627 IN UINTN TranslatedLength
,
1628 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1635 EFI_DEVICE_PATH_PROTOCOL
*AbsDevicePath
;
1636 CHAR16
*AbsConverted
;
1638 EFI_DEVICE_PATH_PROTOCOL
*Node
;
1640 Converted
= ConvertDevicePathToText (
1642 FALSE
, // DisplayOnly
1643 FALSE
// AllowShortcuts
1645 if (Converted
== NULL
) {
1652 // Expand the short-form device path to full device path
1654 if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1655 (DevicePathSubType (DevicePath
) == MEDIA_HARDDRIVE_DP
)) {
1657 // Harddrive shortform device path
1660 } else if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1661 (DevicePathSubType (DevicePath
) == MEDIA_FILEPATH_DP
)) {
1663 // File-path shortform device path
1666 } else if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
1667 (DevicePathSubType (DevicePath
) == MSG_URI_DP
)) {
1669 // URI shortform device path
1673 for ( Node
= DevicePath
1674 ; !IsDevicePathEnd (Node
)
1675 ; Node
= NextDevicePathNode (Node
)
1677 if ((DevicePathType (Node
) == MESSAGING_DEVICE_PATH
) &&
1678 ((DevicePathSubType (Node
) == MSG_USB_CLASS_DP
) ||
1679 (DevicePathSubType (Node
) == MSG_USB_WWID_DP
))) {
1687 // Attempt to expand any relative UEFI device path to
1688 // an absolute device path first.
1691 FileBuffer
= EfiBootManagerGetLoadOptionBuffer (
1692 DevicePath
, &AbsDevicePath
, &FileSize
1694 if (FileBuffer
== NULL
) {
1697 FreePool (FileBuffer
);
1698 AbsConverted
= ConvertDevicePathToText (AbsDevicePath
, FALSE
, FALSE
);
1699 FreePool (AbsDevicePath
);
1700 if (AbsConverted
== NULL
) {
1703 DEBUG ((DEBUG_VERBOSE
,
1704 "%a: expanded relative device path \"%s\" for prefix matching\n",
1705 __FUNCTION__
, Converted
));
1706 FreePool (Converted
);
1707 Converted
= AbsConverted
;
1711 // Is Translated a prefix of Converted?
1713 Result
= (BOOLEAN
)(StrnCmp (Converted
, Translated
, TranslatedLength
) == 0);
1716 "%a: against \"%s\": %a\n",
1719 Result
? "match" : "no match"
1722 FreePool (Converted
);
1728 Append some of the unselected active boot options to the boot order.
1730 This function should accommodate any further policy changes in "boot option
1731 survival". Currently we're adding back everything that starts with neither
1732 PciRoot() nor HD() nor a virtio-mmio VenHw() node.
1734 @param[in,out] BootOrder The structure holding the boot order to
1735 complete. The caller is responsible for
1736 initializing (and potentially populating) it
1737 before calling this function.
1739 @param[in,out] ActiveOption The array of active boot options to scan.
1740 Entries marked as Appended will be skipped.
1741 Those of the rest that satisfy the survival
1742 policy will be added to BootOrder with
1745 @param[in] ActiveCount Number of elements in ActiveOption.
1748 @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot
1751 @return Error codes returned by BootOrderAppend().
1756 IN OUT BOOT_ORDER
*BootOrder
,
1757 IN OUT ACTIVE_OPTION
*ActiveOption
,
1758 IN UINTN ActiveCount
1761 RETURN_STATUS Status
;
1764 Status
= RETURN_SUCCESS
;
1766 while (!RETURN_ERROR (Status
) && Idx
< ActiveCount
) {
1767 if (!ActiveOption
[Idx
].Appended
) {
1768 CONST EFI_BOOT_MANAGER_LOAD_OPTION
*Current
;
1769 CONST EFI_DEVICE_PATH_PROTOCOL
*FirstNode
;
1771 Current
= ActiveOption
[Idx
].BootOption
;
1772 FirstNode
= Current
->FilePath
;
1773 if (FirstNode
!= NULL
) {
1775 STATIC CHAR16 ConvFallBack
[] = L
"<unable to convert>";
1778 Converted
= ConvertDevicePathToText (FirstNode
, FALSE
, FALSE
);
1779 if (Converted
== NULL
) {
1780 Converted
= ConvFallBack
;
1784 if (DevicePathType(FirstNode
) == MEDIA_DEVICE_PATH
&&
1785 DevicePathSubType(FirstNode
) == MEDIA_HARDDRIVE_DP
) {
1790 } else if (DevicePathType(FirstNode
) == ACPI_DEVICE_PATH
&&
1791 DevicePathSubType(FirstNode
) == ACPI_DP
) {
1792 ACPI_HID_DEVICE_PATH
*Acpi
;
1794 Acpi
= (ACPI_HID_DEVICE_PATH
*) FirstNode
;
1795 if ((Acpi
->HID
& PNP_EISA_ID_MASK
) == PNP_EISA_ID_CONST
&&
1796 EISA_ID_TO_NUM (Acpi
->HID
) == 0x0a03) {
1798 // drop PciRoot() if we enabled the user to select PCI-like boot
1799 // options, by providing translation for such OFW device path
1802 Keep
= !FeaturePcdGet (PcdQemuBootOrderPciTranslation
);
1804 } else if (DevicePathType(FirstNode
) == HARDWARE_DEVICE_PATH
&&
1805 DevicePathSubType(FirstNode
) == HW_VENDOR_DP
) {
1806 VENDOR_DEVICE_PATH
*VenHw
;
1808 VenHw
= (VENDOR_DEVICE_PATH
*)FirstNode
;
1809 if (CompareGuid (&VenHw
->Guid
, &gVirtioMmioTransportGuid
)) {
1811 // drop virtio-mmio if we enabled the user to select boot options
1812 // referencing such device paths
1814 Keep
= !FeaturePcdGet (PcdQemuBootOrderMmioTranslation
);
1819 Status
= BootOrderAppend (BootOrder
, &ActiveOption
[Idx
]);
1820 if (!RETURN_ERROR (Status
)) {
1821 DEBUG ((DEBUG_VERBOSE
, "%a: keeping \"%s\"\n", __FUNCTION__
,
1825 DEBUG ((DEBUG_VERBOSE
, "%a: dropping \"%s\"\n", __FUNCTION__
,
1829 if (Converted
!= ConvFallBack
) {
1830 FreePool (Converted
);
1841 Delete Boot#### variables that stand for such active boot options that have
1842 been dropped (ie. have not been selected by either matching or "survival
1845 @param[in] ActiveOption The array of active boot options to scan. Each
1846 entry not marked as appended will trigger the
1847 deletion of the matching Boot#### variable.
1849 @param[in] ActiveCount Number of elements in ActiveOption.
1853 PruneBootVariables (
1854 IN CONST ACTIVE_OPTION
*ActiveOption
,
1855 IN UINTN ActiveCount
1860 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
1861 if (!ActiveOption
[Idx
].Appended
) {
1862 CHAR16 VariableName
[9];
1864 UnicodeSPrintAsciiFormat (VariableName
, sizeof VariableName
, "Boot%04x",
1865 ActiveOption
[Idx
].BootOption
->OptionNumber
);
1868 // "The space consumed by the deleted variable may not be available until
1869 // the next power cycle", but that's good enough.
1871 gRT
->SetVariable (VariableName
, &gEfiGlobalVariableGuid
,
1872 0, // Attributes, 0 means deletion
1873 0, // DataSize, 0 means deletion
1883 Set the boot order based on configuration retrieved from QEMU.
1885 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
1886 OpenFirmware device paths therein to UEFI device path fragments. Match the
1887 translated fragments against the current list of boot options, and rewrite
1888 the BootOrder NvVar so that it corresponds to the order described in fw_cfg.
1890 Platform BDS should call this function after connecting any expected boot
1891 devices and calling EfiBootManagerRefreshAllBootOption ().
1893 @retval RETURN_SUCCESS BootOrder NvVar rewritten.
1895 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
1897 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
1898 file, or no match found between the
1899 "bootorder" fw_cfg file and BootOptionList.
1901 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
1903 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
1905 @return Values returned by gBS->LocateProtocol ()
1906 or gRT->SetVariable ().
1911 SetBootOrderFromQemu (
1915 RETURN_STATUS Status
;
1916 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1919 CONST CHAR8
*FwCfgPtr
;
1921 BOOT_ORDER BootOrder
;
1922 ACTIVE_OPTION
*ActiveOption
;
1925 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
1927 UINTN TranslatedSize
;
1928 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
1929 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
1930 UINTN BootOptionCount
;
1932 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
1933 if (Status
!= RETURN_SUCCESS
) {
1937 if (FwCfgSize
== 0) {
1938 return RETURN_NOT_FOUND
;
1941 FwCfg
= AllocatePool (FwCfgSize
);
1942 if (FwCfg
== NULL
) {
1943 return RETURN_OUT_OF_RESOURCES
;
1946 QemuFwCfgSelectItem (FwCfgItem
);
1947 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
1948 if (FwCfg
[FwCfgSize
- 1] != '\0') {
1949 Status
= RETURN_INVALID_PARAMETER
;
1950 goto ErrorFreeFwCfg
;
1953 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
1954 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
1955 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
1958 BootOrder
.Produced
= 0;
1959 BootOrder
.Allocated
= 1;
1960 BootOrder
.Data
= AllocatePool (
1961 BootOrder
.Allocated
* sizeof (*BootOrder
.Data
)
1963 if (BootOrder
.Data
== NULL
) {
1964 Status
= RETURN_OUT_OF_RESOURCES
;
1965 goto ErrorFreeFwCfg
;
1968 BootOptions
= EfiBootManagerGetLoadOptions (
1969 &BootOptionCount
, LoadOptionTypeBoot
1971 if (BootOptions
== NULL
) {
1972 Status
= RETURN_NOT_FOUND
;
1973 goto ErrorFreeBootOrder
;
1976 Status
= CollectActiveOptions (
1977 BootOptions
, BootOptionCount
, &ActiveOption
, &ActiveCount
1979 if (RETURN_ERROR (Status
)) {
1980 goto ErrorFreeBootOptions
;
1983 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1984 Status
= CreateExtraRootBusMap (&ExtraPciRoots
);
1985 if (EFI_ERROR (Status
)) {
1986 goto ErrorFreeActiveOption
;
1989 ExtraPciRoots
= NULL
;
1993 // translate each OpenFirmware path
1995 TranslatedSize
= ARRAY_SIZE (Translated
);
1996 Status
= TranslateOfwPath (&FwCfgPtr
, ExtraPciRoots
, Translated
,
1998 while (Status
== RETURN_SUCCESS
||
1999 Status
== RETURN_UNSUPPORTED
||
2000 Status
== RETURN_PROTOCOL_ERROR
||
2001 Status
== RETURN_BUFFER_TOO_SMALL
) {
2002 if (Status
== RETURN_SUCCESS
) {
2006 // match translated OpenFirmware path against all active boot options
2008 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
2009 if (!ActiveOption
[Idx
].Appended
&&
2012 TranslatedSize
, // contains length, not size, in CHAR16's here
2013 ActiveOption
[Idx
].BootOption
->FilePath
2017 // match found, store ID and continue with next OpenFirmware path
2019 Status
= BootOrderAppend (&BootOrder
, &ActiveOption
[Idx
]);
2020 if (Status
!= RETURN_SUCCESS
) {
2021 goto ErrorFreeExtraPciRoots
;
2024 } // scanned all active boot options
2025 } // translation successful
2027 TranslatedSize
= ARRAY_SIZE (Translated
);
2028 Status
= TranslateOfwPath (&FwCfgPtr
, ExtraPciRoots
, Translated
,
2030 } // scanning of OpenFirmware paths done
2032 if (Status
== RETURN_NOT_FOUND
&& BootOrder
.Produced
> 0) {
2034 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
2035 // Some of the active boot options that have not been selected over fw_cfg
2036 // should be preserved at the end of the boot order.
2038 Status
= BootOrderComplete (&BootOrder
, ActiveOption
, ActiveCount
);
2039 if (RETURN_ERROR (Status
)) {
2040 goto ErrorFreeExtraPciRoots
;
2044 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
2047 Status
= gRT
->SetVariable (
2049 &gEfiGlobalVariableGuid
,
2050 EFI_VARIABLE_NON_VOLATILE
|
2051 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
2052 EFI_VARIABLE_RUNTIME_ACCESS
,
2053 BootOrder
.Produced
* sizeof (*BootOrder
.Data
),
2056 if (EFI_ERROR (Status
)) {
2057 DEBUG ((DEBUG_ERROR
, "%a: setting BootOrder: %r\n", __FUNCTION__
,
2059 goto ErrorFreeExtraPciRoots
;
2062 DEBUG ((DEBUG_INFO
, "%a: setting BootOrder: success\n", __FUNCTION__
));
2063 PruneBootVariables (ActiveOption
, ActiveCount
);
2066 ErrorFreeExtraPciRoots
:
2067 if (ExtraPciRoots
!= NULL
) {
2068 DestroyExtraRootBusMap (ExtraPciRoots
);
2071 ErrorFreeActiveOption
:
2072 FreePool (ActiveOption
);
2074 ErrorFreeBootOptions
:
2075 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
2078 FreePool (BootOrder
.Data
);
2088 Calculate the number of seconds we should be showing the FrontPage progress
2091 @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().
2095 GetFrontPageTimeoutFromQemu (
2099 FIRMWARE_CONFIG_ITEM BootMenuWaitItem
;
2100 UINTN BootMenuWaitSize
;
2102 QemuFwCfgSelectItem (QemuFwCfgItemBootMenu
);
2103 if (QemuFwCfgRead16 () == 0) {
2105 // The user specified "-boot menu=off", or didn't specify "-boot
2106 // menu=(on|off)" at all. Return the platform default.
2108 return PcdGet16 (PcdPlatformBootTimeOut
);
2111 if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem
,
2112 &BootMenuWaitSize
)) ||
2113 BootMenuWaitSize
!= sizeof (UINT16
)) {
2115 // "-boot menu=on" was specified without "splash-time=N". In this case,
2116 // return three seconds if the platform default would cause us to skip the
2117 // front page, and return the platform default otherwise.
2121 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
2129 // "-boot menu=on,splash-time=N" was specified, where N is in units of
2130 // milliseconds. The Intel BDS Front Page progress bar only supports whole
2131 // seconds, round N up.
2133 QemuFwCfgSelectItem (BootMenuWaitItem
);
2134 return (UINT16
)((QemuFwCfgRead16 () + 999) / 1000);