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
,
435 BOOLEAN AcceptSlash
= FALSE
;
438 // A leading slash is expected. End of string is tolerated.
442 return RETURN_NOT_FOUND
;
449 return RETURN_INVALID_PARAMETER
;
455 OfwNode
->DriverName
.Ptr
= *Ptr
;
456 OfwNode
->DriverName
.Len
= 0;
457 while (OfwNode
->DriverName
.Len
< 32 &&
458 (IsAlnum (**Ptr
) || IsDriverNamePunct (**Ptr
))
462 ++OfwNode
->DriverName
.Len
;
465 if ((OfwNode
->DriverName
.Len
== 0) || (OfwNode
->DriverName
.Len
== 32)) {
466 return RETURN_INVALID_PARAMETER
;
469 if (SubstringEq (OfwNode
->DriverName
, "rom")) {
471 // bug compatibility hack
473 // qemu passes fw_cfg filenames as rom unit address.
474 // The filenames have slashes:
475 // /rom@genroms/linuxboot_dma.bin
477 // Alow slashes in the unit address to avoid the parser trip up,
478 // so we can successfully parse the following lines (the rom
479 // entries themself are ignored).
488 return RETURN_INVALID_PARAMETER
;
493 OfwNode
->UnitAddress
.Ptr
= *Ptr
;
494 OfwNode
->UnitAddress
.Len
= 0;
495 while (IsPrintNotDelim (**Ptr
) || (AcceptSlash
&& **Ptr
== '/')) {
497 ++OfwNode
->UnitAddress
.Len
;
500 if (OfwNode
->UnitAddress
.Len
== 0) {
501 return RETURN_INVALID_PARAMETER
;
505 // device-arguments, may be omitted
507 OfwNode
->DeviceArguments
.Len
= 0;
510 OfwNode
->DeviceArguments
.Ptr
= *Ptr
;
512 while (IsPrintNotDelim (**Ptr
)) {
514 ++OfwNode
->DeviceArguments
.Len
;
517 if (OfwNode
->DeviceArguments
.Len
== 0) {
518 return RETURN_INVALID_PARAMETER
;
521 OfwNode
->DeviceArguments
.Ptr
= NULL
;
540 return RETURN_INVALID_PARAMETER
;
545 "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
547 OfwNode
->DriverName
.Len
,
548 OfwNode
->DriverName
.Ptr
,
549 OfwNode
->UnitAddress
.Len
,
550 OfwNode
->UnitAddress
.Ptr
,
551 OfwNode
->DeviceArguments
.Len
,
552 OfwNode
->DeviceArguments
.Ptr
== NULL
? "" : OfwNode
->DeviceArguments
.Ptr
554 return RETURN_SUCCESS
;
559 Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
562 @param[in] OfwNode Array of OpenFirmware device nodes to
563 translate, constituting the beginning of an
564 OpenFirmware device path.
566 @param[in] NumNodes Number of elements in OfwNode.
568 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
569 CreateExtraRootBusMap(), to be used for
570 translating positions of extra root buses to
573 @param[out] Translated Destination array receiving the UEFI path
574 fragment, allocated by the caller. If the
575 return value differs from RETURN_SUCCESS, its
576 contents is indeterminate.
578 @param[in out] TranslatedSize On input, the number of CHAR16's in
579 Translated. On RETURN_SUCCESS this parameter
580 is assigned the number of non-NUL CHAR16's
581 written to Translated. In case of other return
582 values, TranslatedSize is indeterminate.
585 @retval RETURN_SUCCESS Translation successful.
587 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
590 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
591 be translated in the current implementation.
593 @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an
594 extra PCI root bus (by serial number) that
595 is invalid according to ExtraPciRoots.
600 TranslatePciOfwNodes (
601 IN CONST OFW_NODE
*OfwNode
,
603 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
604 OUT CHAR16
*Translated
,
605 IN OUT UINTN
*TranslatedSize
610 UINTN FirstNonBridge
;
611 CHAR16 Bridges
[BRIDGE_TRANSLATION_OUTPUT_SIZE
];
618 // Resolve the PCI root bus number.
620 // The initial OFW node for the main root bus (ie. bus number 0) is:
624 // For extra root buses, the initial OFW node is
628 // root bus serial number (not PCI bus number)
630 if ((NumNodes
< REQUIRED_PCI_OFW_NODES
) ||
631 !SubstringEq (OfwNode
[0].DriverName
, "pci")
634 return RETURN_UNSUPPORTED
;
639 OfwNode
[0].UnitAddress
.Ptr
,
640 OfwNode
[0].UnitAddress
.Len
,
644 SUBSTRING PciRootSerialSubString
;
645 UINT64 PciRootSerial
;
648 // Parse the root bus serial number from the unit address after the comma.
650 PciRootSerialSubString
.Ptr
= Comma
+ 1;
651 PciRootSerialSubString
.Len
= OfwNode
[0].UnitAddress
.Len
-
652 (PciRootSerialSubString
.Ptr
-
653 OfwNode
[0].UnitAddress
.Ptr
);
656 ParseUnitAddressHexList (
657 PciRootSerialSubString
,
663 return RETURN_UNSUPPORTED
;
667 // Map the extra root bus's serial number to its actual bus number.
670 MapRootBusPosToBusNr (
677 return RETURN_PROTOCOL_ERROR
;
682 // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
686 // PCI slot & function on the parent, holding the bridge
688 // and the UEFI device path node is:
696 UINT64 BridgeDevFun
[2];
697 UINTN BridgesFreeBytes
;
699 if (!SubstringEq (OfwNode
[FirstNonBridge
].DriverName
, "pci-bridge")) {
704 NumEntries
= sizeof BridgeDevFun
/ sizeof BridgeDevFun
[0];
705 if (ParseUnitAddressHexList (
706 OfwNode
[FirstNonBridge
].UnitAddress
,
711 return RETURN_UNSUPPORTED
;
714 BridgesFreeBytes
= sizeof Bridges
- BridgesLen
* sizeof Bridges
[0];
715 Written
= UnicodeSPrintAsciiFormat (
716 Bridges
+ BridgesLen
,
722 BridgesLen
+= Written
;
725 // There's no way to differentiate between "completely used up without
726 // truncation" and "truncated", so treat the former as the latter.
728 if (BridgesLen
+ 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE
) {
729 return RETURN_UNSUPPORTED
;
733 } while (FirstNonBridge
< NumNodes
);
735 if (FirstNonBridge
== NumNodes
) {
736 return RETURN_UNSUPPORTED
;
740 // Parse the OFW nodes starting with the first non-bridge node.
743 NumEntries
= ARRAY_SIZE (PciDevFun
);
744 if (ParseUnitAddressHexList (
745 OfwNode
[FirstNonBridge
].UnitAddress
,
751 return RETURN_UNSUPPORTED
;
754 if ((NumNodes
>= FirstNonBridge
+ 3) &&
755 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "ide") &&
756 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
757 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
761 // OpenFirmware device path (IDE disk, IDE CD-ROM):
763 // /pci@i0cf8/ide@1,1/drive@0/disk@0
765 // | | | | master or slave
766 // | | | primary or secondary
767 // | PCI slot & function holding IDE controller
768 // PCI root at system bus port, PIO
772 // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
780 if ((ParseUnitAddressHexList (
781 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
784 ) != RETURN_SUCCESS
) ||
786 (ParseUnitAddressHexList (
787 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
789 &NumEntries
// reuse after previous single-element call
790 ) != RETURN_SUCCESS
) ||
794 return RETURN_UNSUPPORTED
;
797 Written
= UnicodeSPrintAsciiFormat (
799 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
800 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
805 Secondary
? "Secondary" : "Primary",
806 Slave
? "Slave" : "Master"
808 } else if ((NumNodes
>= FirstNonBridge
+ 3) &&
809 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,2922") &&
810 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
811 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
815 // OpenFirmware device path (Q35 SATA disk and CD-ROM):
817 // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
819 // | | | | device number (fixed 0)
820 // | | | channel (port) number
821 // | PCI slot & function holding SATA HBA
822 // PCI root at system bus port, PIO
826 // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
828 // | | LUN (always 0 on Q35)
829 // | port multiplier port number,
830 // | always 0xFFFF on Q35
831 // channel (port) number
837 ParseUnitAddressHexList (
838 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
844 return RETURN_UNSUPPORTED
;
847 Written
= UnicodeSPrintAsciiFormat (
849 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
850 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
857 } else if ((NumNodes
>= FirstNonBridge
+ 3) &&
858 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "isa") &&
859 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "fdc") &&
860 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "floppy")
864 // OpenFirmware device path (floppy disk):
866 // /pci@i0cf8/isa@1/fdc@03f0/floppy@0
869 // | | ISA controller io-port (hex)
870 // | PCI slot holding ISA controller
871 // PCI root at system bus port, PIO
875 // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
882 if ((ParseUnitAddressHexList (
883 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
886 ) != RETURN_SUCCESS
) ||
890 return RETURN_UNSUPPORTED
;
893 Written
= UnicodeSPrintAsciiFormat (
895 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
896 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
903 } else if ((NumNodes
>= FirstNonBridge
+ 2) &&
904 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
905 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "disk")
909 // OpenFirmware device path (virtio-blk disk):
911 // /pci@i0cf8/scsi@6[,3]/disk@0,0
914 // | | PCI function corresponding to disk (optional)
915 // | PCI slot holding disk
916 // PCI root at system bus port, PIO
918 // UEFI device path prefix:
920 // PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
921 // PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
923 Written
= UnicodeSPrintAsciiFormat (
925 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
926 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
932 } else if ((NumNodes
>= FirstNonBridge
+ 3) &&
933 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
934 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "channel") &&
935 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
939 // OpenFirmware device path (virtio-scsi disk):
941 // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
945 // | | channel (unused, fixed 0)
946 // | PCI slot[, function] holding SCSI controller
947 // PCI root at system bus port, PIO
949 // UEFI device path prefix:
951 // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
952 // -- if PCI function is 0 or absent
953 // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
954 // -- if PCI function is present and nonzero
959 NumEntries
= ARRAY_SIZE (TargetLun
);
960 if (ParseUnitAddressHexList (
961 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
967 return RETURN_UNSUPPORTED
;
970 Written
= UnicodeSPrintAsciiFormat (
972 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
973 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
981 } else if ((NumNodes
>= FirstNonBridge
+ 2) &&
982 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,5845") &&
983 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "namespace")
987 // OpenFirmware device path (NVMe device):
989 // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
991 // | | | | Extended Unique Identifier
992 // | | | | (EUI-64), big endian interp.
993 // | | | namespace ID
994 // | PCI slot & function holding NVMe controller
995 // PCI root at system bus port, PIO
999 // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
1001 // | octets of the EUI-64
1002 // | in address order
1005 UINT64 Namespace
[2];
1006 UINTN RequiredEntries
;
1009 RequiredEntries
= ARRAY_SIZE (Namespace
);
1010 NumEntries
= RequiredEntries
;
1011 if ((ParseUnitAddressHexList (
1012 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
1015 ) != RETURN_SUCCESS
) ||
1016 (NumEntries
!= RequiredEntries
) ||
1017 (Namespace
[0] == 0) ||
1018 (Namespace
[0] >= MAX_UINT32
)
1021 return RETURN_UNSUPPORTED
;
1024 Eui64
= (UINT8
*)&Namespace
[1];
1025 Written
= UnicodeSPrintAsciiFormat (
1027 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1028 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
1029 "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
1044 } else if ((NumNodes
>= FirstNonBridge
+ 2) &&
1045 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "usb") &&
1046 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "storage"))
1049 // OpenFirmware device path (usb-storage device in XHCI port):
1051 // /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0
1053 // | | | | fixed fixed
1054 // | | | XHCI port number, 1-based
1055 // | | PCI function corresponding to XHCI (optional)
1056 // | PCI slot holding XHCI
1057 // PCI root at system bus port, PIO
1059 // UEFI device path prefix:
1061 // PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)
1063 // | XHCI port number in 0-based notation
1064 // 0x0 if PCI function is 0, or absent from OFW
1066 RETURN_STATUS ParseStatus
;
1067 UINT64 OneBasedXhciPort
;
1070 ParseStatus
= ParseUnitAddressHexList (
1071 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
1075 if (RETURN_ERROR (ParseStatus
) || (OneBasedXhciPort
== 0)) {
1076 return RETURN_UNSUPPORTED
;
1079 Written
= UnicodeSPrintAsciiFormat (
1081 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1082 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",
1087 OneBasedXhciPort
- 1
1091 // Generic OpenFirmware device path for PCI devices:
1093 // /pci@i0cf8/ethernet@3[,2]
1095 // | PCI slot[, function] holding Ethernet card
1096 // PCI root at system bus port, PIO
1098 // UEFI device path prefix (dependent on presence of nonzero PCI function):
1100 // PciRoot(0x0)/Pci(0x3,0x0)
1101 // PciRoot(0x0)/Pci(0x3,0x2)
1103 Written
= UnicodeSPrintAsciiFormat (
1105 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1106 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
1115 // There's no way to differentiate between "completely used up without
1116 // truncation" and "truncated", so treat the former as the latter, and return
1117 // success only for "some room left unused".
1119 if (Written
+ 1 < *TranslatedSize
) {
1120 *TranslatedSize
= Written
;
1121 return RETURN_SUCCESS
;
1124 return RETURN_BUFFER_TOO_SMALL
;
1128 // A type providing easy raw access to the base address of a virtio-mmio
1134 } VIRTIO_MMIO_BASE_ADDRESS
;
1138 Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
1141 @param[in] OfwNode Array of OpenFirmware device nodes to
1142 translate, constituting the beginning of an
1143 OpenFirmware device path.
1145 @param[in] NumNodes Number of elements in OfwNode.
1147 @param[out] Translated Destination array receiving the UEFI path
1148 fragment, allocated by the caller. If the
1149 return value differs from RETURN_SUCCESS, its
1150 contents is indeterminate.
1152 @param[in out] TranslatedSize On input, the number of CHAR16's in
1153 Translated. On RETURN_SUCCESS this parameter
1154 is assigned the number of non-NUL CHAR16's
1155 written to Translated. In case of other return
1156 values, TranslatedSize is indeterminate.
1159 @retval RETURN_SUCCESS Translation successful.
1161 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1164 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1165 be translated in the current implementation.
1170 TranslateMmioOfwNodes (
1171 IN CONST OFW_NODE
*OfwNode
,
1173 OUT CHAR16
*Translated
,
1174 IN OUT UINTN
*TranslatedSize
1177 VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase
;
1178 CHAR16 VenHwString
[60 + 1];
1183 // Get the base address of the virtio-mmio transport.
1185 if ((NumNodes
< REQUIRED_MMIO_OFW_NODES
) ||
1186 !SubstringEq (OfwNode
[0].DriverName
, "virtio-mmio")
1189 return RETURN_UNSUPPORTED
;
1193 if (ParseUnitAddressHexList (
1194 OfwNode
[0].UnitAddress
,
1195 &VirtioMmioBase
.Uint64
,
1200 return RETURN_UNSUPPORTED
;
1203 UnicodeSPrintAsciiFormat (
1206 "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)",
1207 &gVirtioMmioTransportGuid
,
1208 VirtioMmioBase
.Raw
[0],
1209 VirtioMmioBase
.Raw
[1],
1210 VirtioMmioBase
.Raw
[2],
1211 VirtioMmioBase
.Raw
[3],
1212 VirtioMmioBase
.Raw
[4],
1213 VirtioMmioBase
.Raw
[5],
1214 VirtioMmioBase
.Raw
[6],
1215 VirtioMmioBase
.Raw
[7]
1218 if ((NumNodes
>= 2) &&
1219 SubstringEq (OfwNode
[1].DriverName
, "disk"))
1222 // OpenFirmware device path (virtio-blk disk):
1224 // /virtio-mmio@000000000a003c00/disk@0,0
1227 // base address of virtio-mmio register block
1229 // UEFI device path prefix:
1233 Written
= UnicodeSPrintAsciiFormat (
1235 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1239 } else if ((NumNodes
>= 3) &&
1240 SubstringEq (OfwNode
[1].DriverName
, "channel") &&
1241 SubstringEq (OfwNode
[2].DriverName
, "disk"))
1244 // OpenFirmware device path (virtio-scsi disk):
1246 // /virtio-mmio@000000000a003a00/channel@0/disk@2,3
1250 // | channel (unused, fixed 0)
1251 // base address of virtio-mmio register block
1253 // UEFI device path prefix:
1255 // <VenHwString>/Scsi(0x2,0x3)
1257 UINT64 TargetLun
[2];
1260 NumEntries
= ARRAY_SIZE (TargetLun
);
1261 if (ParseUnitAddressHexList (
1262 OfwNode
[2].UnitAddress
,
1268 return RETURN_UNSUPPORTED
;
1271 Written
= UnicodeSPrintAsciiFormat (
1273 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1274 "%s/Scsi(0x%Lx,0x%Lx)",
1279 } else if ((NumNodes
>= 2) &&
1280 SubstringEq (OfwNode
[1].DriverName
, "ethernet-phy"))
1283 // OpenFirmware device path (virtio-net NIC):
1285 // /virtio-mmio@000000000a003e00/ethernet-phy@0
1288 // base address of virtio-mmio register block
1290 // UEFI device path prefix:
1294 Written
= UnicodeSPrintAsciiFormat (
1296 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1301 return RETURN_UNSUPPORTED
;
1305 // There's no way to differentiate between "completely used up without
1306 // truncation" and "truncated", so treat the former as the latter, and return
1307 // success only for "some room left unused".
1309 if (Written
+ 1 < *TranslatedSize
) {
1310 *TranslatedSize
= Written
;
1311 return RETURN_SUCCESS
;
1314 return RETURN_BUFFER_TOO_SMALL
;
1319 Translate an array of OpenFirmware device nodes to a UEFI device path
1322 @param[in] OfwNode Array of OpenFirmware device nodes to
1323 translate, constituting the beginning of an
1324 OpenFirmware device path.
1326 @param[in] NumNodes Number of elements in OfwNode.
1328 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1329 CreateExtraRootBusMap(), to be used for
1330 translating positions of extra root buses to
1333 @param[out] Translated Destination array receiving the UEFI path
1334 fragment, allocated by the caller. If the
1335 return value differs from RETURN_SUCCESS, its
1336 contents is indeterminate.
1338 @param[in out] TranslatedSize On input, the number of CHAR16's in
1339 Translated. On RETURN_SUCCESS this parameter
1340 is assigned the number of non-NUL CHAR16's
1341 written to Translated. In case of other return
1342 values, TranslatedSize is indeterminate.
1345 @retval RETURN_SUCCESS Translation successful.
1347 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1350 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1351 be translated in the current implementation.
1353 @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has
1354 been (partially) recognized, but it contains
1355 a logic error / doesn't match system state.
1361 IN CONST OFW_NODE
*OfwNode
,
1363 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1364 OUT CHAR16
*Translated
,
1365 IN OUT UINTN
*TranslatedSize
1368 RETURN_STATUS Status
;
1370 Status
= RETURN_UNSUPPORTED
;
1372 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1373 Status
= TranslatePciOfwNodes (
1382 if ((Status
== RETURN_UNSUPPORTED
) &&
1383 FeaturePcdGet (PcdQemuBootOrderMmioTranslation
))
1385 Status
= TranslateMmioOfwNodes (
1398 Translate an OpenFirmware device path fragment to a UEFI device path
1399 fragment, and advance in the input string.
1401 @param[in out] Ptr Address of the pointer pointing to the start
1402 of the path string. After successful
1403 translation (RETURN_SUCCESS) or at least
1404 successful parsing (RETURN_UNSUPPORTED,
1405 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
1406 byte immediately following the consumed
1407 characters. In other error cases, it points to
1408 the byte that caused the error.
1410 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1411 CreateExtraRootBusMap(), to be used for
1412 translating positions of extra root buses to
1415 @param[out] Translated Destination array receiving the UEFI path
1416 fragment, allocated by the caller. If the
1417 return value differs from RETURN_SUCCESS, its
1418 contents is indeterminate.
1420 @param[in out] TranslatedSize On input, the number of CHAR16's in
1421 Translated. On RETURN_SUCCESS this parameter
1422 is assigned the number of non-NUL CHAR16's
1423 written to Translated. In case of other return
1424 values, TranslatedSize is indeterminate.
1427 @retval RETURN_SUCCESS Translation successful.
1429 @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed
1430 successfully, but its translation did not
1431 fit into the number of bytes provided.
1432 Further calls to this function are
1435 @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed
1436 successfully, but it can't be translated in
1437 the current implementation. Further calls
1438 to this function are possible.
1440 @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been
1441 (partially) recognized, but it contains a
1442 logic error / doesn't match system state.
1443 Further calls to this function are
1446 @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was
1447 pointing to the empty string or "HALT". On
1448 output, *Ptr points to the empty string
1449 (ie. "HALT" is consumed transparently when
1452 @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.
1458 IN OUT CONST CHAR8
**Ptr
,
1459 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1460 OUT CHAR16
*Translated
,
1461 IN OUT UINTN
*TranslatedSize
1465 RETURN_STATUS Status
;
1466 OFW_NODE Node
[EXAMINED_OFW_NODES
];
1472 if (AsciiStrCmp (*Ptr
, "HALT") == 0) {
1474 Status
= RETURN_NOT_FOUND
;
1476 Status
= ParseOfwNode (Ptr
, &Node
[NumNodes
], &IsFinal
);
1479 if (Status
== RETURN_NOT_FOUND
) {
1480 DEBUG ((DEBUG_VERBOSE
, "%a: no more nodes\n", __FUNCTION__
));
1481 return RETURN_NOT_FOUND
;
1484 while (Status
== RETURN_SUCCESS
&& !IsFinal
) {
1486 Status
= ParseOfwNode (
1488 (NumNodes
< EXAMINED_OFW_NODES
) ? &Node
[NumNodes
] : &Skip
,
1494 case RETURN_SUCCESS
:
1498 case RETURN_INVALID_PARAMETER
:
1499 DEBUG ((DEBUG_VERBOSE
, "%a: parse error\n", __FUNCTION__
));
1500 return RETURN_INVALID_PARAMETER
;
1506 Status
= TranslateOfwNodes (
1508 NumNodes
< EXAMINED_OFW_NODES
? NumNodes
: EXAMINED_OFW_NODES
,
1514 case RETURN_SUCCESS
:
1515 DEBUG ((DEBUG_VERBOSE
, "%a: success: \"%s\"\n", __FUNCTION__
, Translated
));
1518 case RETURN_BUFFER_TOO_SMALL
:
1519 DEBUG ((DEBUG_VERBOSE
, "%a: buffer too small\n", __FUNCTION__
));
1522 case RETURN_UNSUPPORTED
:
1523 DEBUG ((DEBUG_VERBOSE
, "%a: unsupported\n", __FUNCTION__
));
1526 case RETURN_PROTOCOL_ERROR
:
1529 "%a: logic error / system state mismatch\n",
1542 Connect devices based on the boot order retrieved from QEMU.
1544 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
1545 OpenFirmware device paths therein to UEFI device path fragments. Connect the
1546 devices identified by the UEFI devpath prefixes as narrowly as possible, then
1547 connect all their child devices, recursively.
1549 If this function fails, then platform BDS should fall back to
1550 EfiBootManagerConnectAll(), or some other method for connecting any expected
1553 @retval RETURN_SUCCESS The "bootorder" fw_cfg file has been
1554 parsed, and the referenced device-subtrees
1555 have been connected.
1557 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
1559 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
1562 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
1564 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
1566 @return Error statuses propagated from underlying
1571 ConnectDevicesFromQemu (
1575 RETURN_STATUS Status
;
1576 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1579 EFI_STATUS EfiStatus
;
1580 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
1581 CONST CHAR8
*FwCfgPtr
;
1583 UINTN TranslatedSize
;
1584 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
1586 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
1587 if (RETURN_ERROR (Status
)) {
1591 if (FwCfgSize
== 0) {
1592 return RETURN_NOT_FOUND
;
1595 FwCfg
= AllocatePool (FwCfgSize
);
1596 if (FwCfg
== NULL
) {
1597 return RETURN_OUT_OF_RESOURCES
;
1600 QemuFwCfgSelectItem (FwCfgItem
);
1601 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
1602 if (FwCfg
[FwCfgSize
- 1] != '\0') {
1603 Status
= RETURN_INVALID_PARAMETER
;
1607 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
1608 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
1609 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
1611 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1612 EfiStatus
= CreateExtraRootBusMap (&ExtraPciRoots
);
1613 if (EFI_ERROR (EfiStatus
)) {
1614 Status
= (RETURN_STATUS
)EfiStatus
;
1618 ExtraPciRoots
= NULL
;
1622 // Translate each OpenFirmware path to a UEFI devpath prefix.
1626 TranslatedSize
= ARRAY_SIZE (Translated
);
1627 Status
= TranslateOfwPath (
1633 while (!RETURN_ERROR (Status
)) {
1634 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1635 EFI_HANDLE Controller
;
1638 // Convert the UEFI devpath prefix to binary representation.
1640 ASSERT (Translated
[TranslatedSize
] == L
'\0');
1641 DevicePath
= ConvertTextToDevicePath (Translated
);
1642 if (DevicePath
== NULL
) {
1643 Status
= RETURN_OUT_OF_RESOURCES
;
1644 goto FreeExtraPciRoots
;
1648 // Advance along DevicePath, connecting the nodes individually, and asking
1649 // drivers not to produce sibling nodes. Retrieve the controller handle
1650 // associated with the full DevicePath -- this is the device that QEMU's
1651 // OFW devpath refers to.
1653 EfiStatus
= EfiBootManagerConnectDevicePath (DevicePath
, &Controller
);
1654 FreePool (DevicePath
);
1655 if (EFI_ERROR (EfiStatus
)) {
1656 Status
= (RETURN_STATUS
)EfiStatus
;
1657 goto FreeExtraPciRoots
;
1661 // Because QEMU's OFW devpaths have lesser expressive power than UEFI
1662 // devpaths (i.e., DevicePath is considered a prefix), connect the tree
1663 // rooted at Controller, recursively. If no children are produced
1664 // (EFI_NOT_FOUND), that's OK.
1666 EfiStatus
= gBS
->ConnectController (Controller
, NULL
, NULL
, TRUE
);
1667 if (EFI_ERROR (EfiStatus
) && (EfiStatus
!= EFI_NOT_FOUND
)) {
1668 Status
= (RETURN_STATUS
)EfiStatus
;
1669 goto FreeExtraPciRoots
;
1674 // Move to the next OFW devpath.
1676 TranslatedSize
= ARRAY_SIZE (Translated
);
1677 Status
= TranslateOfwPath (
1685 if ((Status
== RETURN_NOT_FOUND
) && (NumConnected
> 0)) {
1688 "%a: %Lu OpenFirmware device path(s) connected\n",
1690 (UINT64
)NumConnected
1692 Status
= RETURN_SUCCESS
;
1696 if (ExtraPciRoots
!= NULL
) {
1697 DestroyExtraRootBusMap (ExtraPciRoots
);
1707 Write qemu boot order to uefi variables.
1709 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate
1710 the OpenFirmware device paths therein to UEFI device path fragments.
1712 On Success store the device path in VMMBootOrderNNNN variables.
1716 StoreQemuBootOrder (
1720 RETURN_STATUS Status
;
1721 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1724 EFI_STATUS EfiStatus
;
1725 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
1726 CONST CHAR8
*FwCfgPtr
;
1727 UINTN TranslatedSize
;
1728 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
1729 UINTN VariableIndex
= 0;
1730 CHAR16 VariableName
[20];
1732 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
1733 if (RETURN_ERROR (Status
)) {
1737 if (FwCfgSize
== 0) {
1741 FwCfg
= AllocatePool (FwCfgSize
);
1742 if (FwCfg
== NULL
) {
1746 QemuFwCfgSelectItem (FwCfgItem
);
1747 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
1748 if (FwCfg
[FwCfgSize
- 1] != '\0') {
1749 Status
= RETURN_INVALID_PARAMETER
;
1753 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
1754 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
1755 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
1757 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1758 EfiStatus
= CreateExtraRootBusMap (&ExtraPciRoots
);
1759 if (EFI_ERROR (EfiStatus
)) {
1760 Status
= (RETURN_STATUS
)EfiStatus
;
1764 ExtraPciRoots
= NULL
;
1768 // Translate each OpenFirmware path to a UEFI devpath prefix.
1771 TranslatedSize
= ARRAY_SIZE (Translated
);
1772 Status
= TranslateOfwPath (
1778 while (Status
== EFI_SUCCESS
||
1779 Status
== EFI_UNSUPPORTED
)
1781 if (Status
== EFI_SUCCESS
) {
1782 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1785 // Convert the UEFI devpath prefix to binary representation.
1787 ASSERT (Translated
[TranslatedSize
] == L
'\0');
1788 DevicePath
= ConvertTextToDevicePath (Translated
);
1789 if (DevicePath
== NULL
) {
1790 Status
= RETURN_OUT_OF_RESOURCES
;
1791 goto FreeExtraPciRoots
;
1796 sizeof (VariableName
),
1797 L
"VMMBootOrder%04x",
1800 DEBUG ((DEBUG_INFO
, "%a: %s = %s\n", __FUNCTION__
, VariableName
, Translated
));
1804 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
1805 GetDevicePathSize (DevicePath
),
1808 FreePool (DevicePath
);
1812 // Move to the next OFW devpath.
1814 TranslatedSize
= ARRAY_SIZE (Translated
);
1815 Status
= TranslateOfwPath (
1824 if (ExtraPciRoots
!= NULL
) {
1825 DestroyExtraRootBusMap (ExtraPciRoots
);
1834 Convert the UEFI DevicePath to full text representation with DevPathToText,
1835 then match the UEFI device path fragment in Translated against it.
1837 @param[in] Translated UEFI device path fragment, translated from
1838 OpenFirmware format, to search for.
1840 @param[in] TranslatedLength The length of Translated in CHAR16's.
1842 @param[in] DevicePath Boot option device path whose textual rendering
1845 @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.
1848 @retval TRUE If Translated was found at the beginning of DevicePath after
1849 converting the latter to text.
1851 @retval FALSE If DevicePath was NULL, or it could not be converted, or there
1858 IN CONST CHAR16
*Translated
,
1859 IN UINTN TranslatedLength
,
1860 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1867 EFI_DEVICE_PATH_PROTOCOL
*AbsDevicePath
;
1868 CHAR16
*AbsConverted
;
1870 EFI_DEVICE_PATH_PROTOCOL
*Node
;
1872 Converted
= ConvertDevicePathToText (
1874 FALSE
, // DisplayOnly
1875 FALSE
// AllowShortcuts
1877 if (Converted
== NULL
) {
1884 // Expand the short-form device path to full device path
1886 if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1887 (DevicePathSubType (DevicePath
) == MEDIA_HARDDRIVE_DP
))
1890 // Harddrive shortform device path
1893 } else if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1894 (DevicePathSubType (DevicePath
) == MEDIA_FILEPATH_DP
))
1897 // File-path shortform device path
1900 } else if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
1901 (DevicePathSubType (DevicePath
) == MSG_URI_DP
))
1904 // URI shortform device path
1908 for ( Node
= DevicePath
1909 ; !IsDevicePathEnd (Node
)
1910 ; Node
= NextDevicePathNode (Node
)
1913 if ((DevicePathType (Node
) == MESSAGING_DEVICE_PATH
) &&
1914 ((DevicePathSubType (Node
) == MSG_USB_CLASS_DP
) ||
1915 (DevicePathSubType (Node
) == MSG_USB_WWID_DP
)))
1924 // Attempt to expand any relative UEFI device path to
1925 // an absolute device path first.
1928 FileBuffer
= EfiBootManagerGetLoadOptionBuffer (
1933 if (FileBuffer
== NULL
) {
1937 FreePool (FileBuffer
);
1938 AbsConverted
= ConvertDevicePathToText (AbsDevicePath
, FALSE
, FALSE
);
1939 FreePool (AbsDevicePath
);
1940 if (AbsConverted
== NULL
) {
1946 "%a: expanded relative device path \"%s\" for prefix matching\n",
1950 FreePool (Converted
);
1951 Converted
= AbsConverted
;
1955 // Is Translated a prefix of Converted?
1957 Result
= (BOOLEAN
)(StrnCmp (Converted
, Translated
, TranslatedLength
) == 0);
1960 "%a: against \"%s\": %a\n",
1963 Result
? "match" : "no match"
1966 FreePool (Converted
);
1971 Append some of the unselected active boot options to the boot order.
1973 This function should accommodate any further policy changes in "boot option
1974 survival". Currently we're adding back everything that starts with neither
1975 PciRoot() nor HD() nor a virtio-mmio VenHw() node.
1977 @param[in,out] BootOrder The structure holding the boot order to
1978 complete. The caller is responsible for
1979 initializing (and potentially populating) it
1980 before calling this function.
1982 @param[in,out] ActiveOption The array of active boot options to scan.
1983 Entries marked as Appended will be skipped.
1984 Those of the rest that satisfy the survival
1985 policy will be added to BootOrder with
1988 @param[in] ActiveCount Number of elements in ActiveOption.
1991 @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot
1994 @return Error codes returned by BootOrderAppend().
1999 IN OUT BOOT_ORDER
*BootOrder
,
2000 IN OUT ACTIVE_OPTION
*ActiveOption
,
2001 IN UINTN ActiveCount
2004 RETURN_STATUS Status
;
2007 Status
= RETURN_SUCCESS
;
2009 while (!RETURN_ERROR (Status
) && Idx
< ActiveCount
) {
2010 if (!ActiveOption
[Idx
].Appended
) {
2011 CONST EFI_BOOT_MANAGER_LOAD_OPTION
*Current
;
2012 CONST EFI_DEVICE_PATH_PROTOCOL
*FirstNode
;
2014 Current
= ActiveOption
[Idx
].BootOption
;
2015 FirstNode
= Current
->FilePath
;
2016 if (FirstNode
!= NULL
) {
2018 STATIC CHAR16 ConvFallBack
[] = L
"<unable to convert>";
2021 Converted
= ConvertDevicePathToText (FirstNode
, FALSE
, FALSE
);
2022 if (Converted
== NULL
) {
2023 Converted
= ConvFallBack
;
2027 if ((DevicePathType (FirstNode
) == MEDIA_DEVICE_PATH
) &&
2028 (DevicePathSubType (FirstNode
) == MEDIA_HARDDRIVE_DP
))
2034 } else if ((DevicePathType (FirstNode
) == ACPI_DEVICE_PATH
) &&
2035 (DevicePathSubType (FirstNode
) == ACPI_DP
))
2037 ACPI_HID_DEVICE_PATH
*Acpi
;
2039 Acpi
= (ACPI_HID_DEVICE_PATH
*)FirstNode
;
2040 if (((Acpi
->HID
& PNP_EISA_ID_MASK
) == PNP_EISA_ID_CONST
) &&
2041 (EISA_ID_TO_NUM (Acpi
->HID
) == 0x0a03))
2044 // drop PciRoot() if we enabled the user to select PCI-like boot
2045 // options, by providing translation for such OFW device path
2048 Keep
= !FeaturePcdGet (PcdQemuBootOrderPciTranslation
);
2050 } else if ((DevicePathType (FirstNode
) == HARDWARE_DEVICE_PATH
) &&
2051 (DevicePathSubType (FirstNode
) == HW_VENDOR_DP
))
2053 VENDOR_DEVICE_PATH
*VenHw
;
2055 VenHw
= (VENDOR_DEVICE_PATH
*)FirstNode
;
2056 if (CompareGuid (&VenHw
->Guid
, &gVirtioMmioTransportGuid
)) {
2058 // drop virtio-mmio if we enabled the user to select boot options
2059 // referencing such device paths
2061 Keep
= !FeaturePcdGet (PcdQemuBootOrderMmioTranslation
);
2066 Status
= BootOrderAppend (BootOrder
, &ActiveOption
[Idx
]);
2067 if (!RETURN_ERROR (Status
)) {
2070 "%a: keeping \"%s\"\n",
2078 "%a: dropping \"%s\"\n",
2084 if (Converted
!= ConvFallBack
) {
2085 FreePool (Converted
);
2097 Delete Boot#### variables that stand for such active boot options that have
2098 been dropped (ie. have not been selected by either matching or "survival
2101 @param[in] ActiveOption The array of active boot options to scan. Each
2102 entry not marked as appended will trigger the
2103 deletion of the matching Boot#### variable.
2105 @param[in] ActiveCount Number of elements in ActiveOption.
2109 PruneBootVariables (
2110 IN CONST ACTIVE_OPTION
*ActiveOption
,
2111 IN UINTN ActiveCount
2116 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
2117 if (!ActiveOption
[Idx
].Appended
) {
2118 CHAR16 VariableName
[9];
2120 UnicodeSPrintAsciiFormat (
2122 sizeof VariableName
,
2124 ActiveOption
[Idx
].BootOption
->OptionNumber
2128 // "The space consumed by the deleted variable may not be available until
2129 // the next power cycle", but that's good enough.
2133 &gEfiGlobalVariableGuid
,
2134 0, // Attributes, 0 means deletion
2135 0, // DataSize, 0 means deletion
2144 Set the boot order based on configuration retrieved from QEMU.
2146 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
2147 OpenFirmware device paths therein to UEFI device path fragments. Match the
2148 translated fragments against the current list of boot options, and rewrite
2149 the BootOrder NvVar so that it corresponds to the order described in fw_cfg.
2151 Platform BDS should call this function after connecting any expected boot
2152 devices and calling EfiBootManagerRefreshAllBootOption ().
2154 @retval RETURN_SUCCESS BootOrder NvVar rewritten.
2156 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
2158 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
2159 file, or no match found between the
2160 "bootorder" fw_cfg file and BootOptionList.
2162 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
2164 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
2166 @return Values returned by gBS->LocateProtocol ()
2167 or gRT->SetVariable ().
2172 SetBootOrderFromQemu (
2176 RETURN_STATUS Status
;
2177 FIRMWARE_CONFIG_ITEM FwCfgItem
;
2180 CONST CHAR8
*FwCfgPtr
;
2182 BOOT_ORDER BootOrder
;
2183 ACTIVE_OPTION
*ActiveOption
;
2186 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
2188 UINTN TranslatedSize
;
2189 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
2190 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
2191 UINTN BootOptionCount
;
2193 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
2194 if (Status
!= RETURN_SUCCESS
) {
2198 if (FwCfgSize
== 0) {
2199 return RETURN_NOT_FOUND
;
2202 FwCfg
= AllocatePool (FwCfgSize
);
2203 if (FwCfg
== NULL
) {
2204 return RETURN_OUT_OF_RESOURCES
;
2207 QemuFwCfgSelectItem (FwCfgItem
);
2208 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
2209 if (FwCfg
[FwCfgSize
- 1] != '\0') {
2210 Status
= RETURN_INVALID_PARAMETER
;
2211 goto ErrorFreeFwCfg
;
2214 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
2215 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
2216 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
2219 BootOrder
.Produced
= 0;
2220 BootOrder
.Allocated
= 1;
2221 BootOrder
.Data
= AllocatePool (
2222 BootOrder
.Allocated
* sizeof (*BootOrder
.Data
)
2224 if (BootOrder
.Data
== NULL
) {
2225 Status
= RETURN_OUT_OF_RESOURCES
;
2226 goto ErrorFreeFwCfg
;
2229 BootOptions
= EfiBootManagerGetLoadOptions (
2233 if (BootOptions
== NULL
) {
2234 Status
= RETURN_NOT_FOUND
;
2235 goto ErrorFreeBootOrder
;
2238 Status
= CollectActiveOptions (
2244 if (RETURN_ERROR (Status
)) {
2245 goto ErrorFreeBootOptions
;
2248 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
2249 Status
= CreateExtraRootBusMap (&ExtraPciRoots
);
2250 if (EFI_ERROR (Status
)) {
2251 goto ErrorFreeActiveOption
;
2254 ExtraPciRoots
= NULL
;
2258 // translate each OpenFirmware path
2260 TranslatedSize
= ARRAY_SIZE (Translated
);
2261 Status
= TranslateOfwPath (
2267 while (Status
== RETURN_SUCCESS
||
2268 Status
== RETURN_UNSUPPORTED
||
2269 Status
== RETURN_PROTOCOL_ERROR
||
2270 Status
== RETURN_BUFFER_TOO_SMALL
)
2272 if (Status
== RETURN_SUCCESS
) {
2276 // match translated OpenFirmware path against all active boot options
2278 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
2279 if (!ActiveOption
[Idx
].Appended
&&
2282 TranslatedSize
, // contains length, not size, in CHAR16's here
2283 ActiveOption
[Idx
].BootOption
->FilePath
2288 // match found, store ID and continue with next OpenFirmware path
2290 Status
= BootOrderAppend (&BootOrder
, &ActiveOption
[Idx
]);
2291 if (Status
!= RETURN_SUCCESS
) {
2292 goto ErrorFreeExtraPciRoots
;
2295 } // scanned all active boot options
2296 } // translation successful
2298 TranslatedSize
= ARRAY_SIZE (Translated
);
2299 Status
= TranslateOfwPath (
2305 } // scanning of OpenFirmware paths done
2307 if ((Status
== RETURN_NOT_FOUND
) && (BootOrder
.Produced
> 0)) {
2309 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
2310 // Some of the active boot options that have not been selected over fw_cfg
2311 // should be preserved at the end of the boot order.
2313 Status
= BootOrderComplete (&BootOrder
, ActiveOption
, ActiveCount
);
2314 if (RETURN_ERROR (Status
)) {
2315 goto ErrorFreeExtraPciRoots
;
2319 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
2322 Status
= gRT
->SetVariable (
2324 &gEfiGlobalVariableGuid
,
2325 EFI_VARIABLE_NON_VOLATILE
|
2326 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
2327 EFI_VARIABLE_RUNTIME_ACCESS
,
2328 BootOrder
.Produced
* sizeof (*BootOrder
.Data
),
2331 if (EFI_ERROR (Status
)) {
2334 "%a: setting BootOrder: %r\n",
2338 goto ErrorFreeExtraPciRoots
;
2341 DEBUG ((DEBUG_INFO
, "%a: setting BootOrder: success\n", __FUNCTION__
));
2342 PruneBootVariables (ActiveOption
, ActiveCount
);
2345 ErrorFreeExtraPciRoots
:
2346 if (ExtraPciRoots
!= NULL
) {
2347 DestroyExtraRootBusMap (ExtraPciRoots
);
2350 ErrorFreeActiveOption
:
2351 FreePool (ActiveOption
);
2353 ErrorFreeBootOptions
:
2354 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
2357 FreePool (BootOrder
.Data
);
2366 Calculate the number of seconds we should be showing the FrontPage progress
2369 @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().
2373 GetFrontPageTimeoutFromQemu (
2377 FIRMWARE_CONFIG_ITEM BootMenuWaitItem
;
2378 UINTN BootMenuWaitSize
;
2379 UINT16 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
2381 if (!QemuFwCfgIsAvailable ()) {
2385 QemuFwCfgSelectItem (QemuFwCfgItemBootMenu
);
2386 if (QemuFwCfgRead16 () == 0) {
2388 // The user specified "-boot menu=off", or didn't specify "-boot
2389 // menu=(on|off)" at all. Return the platform default.
2391 return PcdGet16 (PcdPlatformBootTimeOut
);
2396 "etc/boot-menu-wait",
2401 (BootMenuWaitSize
!= sizeof (UINT16
)))
2404 // "-boot menu=on" was specified without "splash-time=N". In this case,
2405 // return three seconds if the platform default would cause us to skip the
2406 // front page, and return the platform default otherwise.
2416 // "-boot menu=on,splash-time=N" was specified, where N is in units of
2417 // milliseconds. The Intel BDS Front Page progress bar only supports whole
2418 // seconds, round N up.
2420 QemuFwCfgSelectItem (BootMenuWaitItem
);
2421 return (UINT16
)((QemuFwCfgRead16 () + 999) / 1000);