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 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include <Library/QemuFwCfgLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiBootManagerLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/UefiRuntimeServicesTableLib.h>
22 #include <Library/BaseLib.h>
23 #include <Library/PrintLib.h>
24 #include <Library/DevicePathLib.h>
25 #include <Library/QemuBootOrderLib.h>
26 #include <Library/BaseMemoryLib.h>
27 #include <Guid/GlobalVariable.h>
28 #include <Guid/VirtioMmioTransport.h>
30 #include "ExtraRootBusMap.h"
33 OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
35 #define TRANSLATION_OUTPUT_SIZE 0x100
38 Output buffer size for OpenFirmware to UEFI device path fragment translation,
39 in CHAR16's, for a sequence of PCI bridges.
41 #define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40
44 Numbers of nodes in OpenFirmware device paths that are required and examined.
46 #define REQUIRED_PCI_OFW_NODES 2
47 #define REQUIRED_MMIO_OFW_NODES 1
48 #define EXAMINED_OFW_NODES 6
52 Simple character classification routines, corresponding to POSIX class names
61 return (('0' <= Chr
&& Chr
<= '9') ||
62 ('A' <= Chr
&& Chr
<= 'Z') ||
63 ('a' <= Chr
&& Chr
<= 'z')
74 return (Chr
== ',' || Chr
== '.' || Chr
== '_' ||
75 Chr
== '+' || Chr
== '-'
86 return (32 <= Chr
&& Chr
<= 126 &&
87 Chr
!= '/' && Chr
!= '@' && Chr
!= ':');
92 Utility types and functions.
95 CONST CHAR8
*Ptr
; // not necessarily NUL-terminated
96 UINTN Len
; // number of non-NUL characters
102 Check if Substring and String have identical contents.
104 The function relies on the restriction that a SUBSTRING cannot have embedded
107 @param[in] Substring The SUBSTRING input to the comparison.
109 @param[in] String The ASCII string input to the comparison.
112 @return Whether the inputs have identical contents.
118 IN SUBSTRING Substring
,
119 IN CONST CHAR8
*String
128 while (Pos
< Substring
.Len
&& Substring
.Ptr
[Pos
] == *Chr
) {
133 return (BOOLEAN
)(Pos
== Substring
.Len
&& *Chr
== '\0');
139 Parse a comma-separated list of hexadecimal integers into the elements of an
142 Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
143 or an empty string are not allowed; they are rejected.
145 The function relies on ASCII encoding.
147 @param[in] UnitAddress The substring to parse.
149 @param[out] Result The array, allocated by the caller, to receive
150 the parsed values. This parameter may be NULL if
151 NumResults is zero on input.
153 @param[in out] NumResults On input, the number of elements allocated for
154 Result. On output, the number of elements it has
155 taken (or would have taken) to parse the string
159 @retval RETURN_SUCCESS UnitAddress has been fully parsed.
160 NumResults is set to the number of parsed
161 values; the corresponding elements have
162 been set in Result. The rest of Result's
163 elements are unchanged.
165 @retval RETURN_BUFFER_TOO_SMALL UnitAddress has been fully parsed.
166 NumResults is set to the number of parsed
167 values, but elements have been stored only
168 up to the input value of NumResults, which
169 is less than what has been parsed.
171 @retval RETURN_INVALID_PARAMETER Parse error. The contents of Results is
172 indeterminate. NumResults has not been
178 ParseUnitAddressHexList (
179 IN SUBSTRING UnitAddress
,
181 IN OUT UINTN
*NumResults
184 UINTN Entry
; // number of entry currently being parsed
185 UINT64 EntryVal
; // value being constructed for current entry
186 CHAR8 PrevChr
; // UnitAddress character previously checked
187 UINTN Pos
; // current position within UnitAddress
188 RETURN_STATUS Status
;
194 for (Pos
= 0; Pos
< UnitAddress
.Len
; ++Pos
) {
198 Chr
= UnitAddress
.Ptr
[Pos
];
199 Val
= ('a' <= Chr
&& Chr
<= 'f') ? (Chr
- 'a' + 10) :
200 ('A' <= Chr
&& Chr
<= 'F') ? (Chr
- 'A' + 10) :
201 ('0' <= Chr
&& Chr
<= '9') ? (Chr
- '0' ) :
205 if (EntryVal
> 0xFFFFFFFFFFFFFFFull
) {
206 return RETURN_INVALID_PARAMETER
;
208 EntryVal
= LShiftU64 (EntryVal
, 4) | Val
;
209 } else if (Chr
== ',') {
210 if (PrevChr
== ',') {
211 return RETURN_INVALID_PARAMETER
;
213 if (Entry
< *NumResults
) {
214 Result
[Entry
] = EntryVal
;
219 return RETURN_INVALID_PARAMETER
;
225 if (PrevChr
== ',') {
226 return RETURN_INVALID_PARAMETER
;
228 if (Entry
< *NumResults
) {
229 Result
[Entry
] = EntryVal
;
230 Status
= RETURN_SUCCESS
;
232 Status
= RETURN_BUFFER_TOO_SMALL
;
242 A simple array of Boot Option ID's.
252 Array element tracking an enumerated boot option that has the
253 LOAD_OPTION_ACTIVE attribute.
256 CONST EFI_BOOT_MANAGER_LOAD_OPTION
*BootOption
; // reference only, no
258 BOOLEAN Appended
; // has been added to a
265 Append an active boot option to BootOrder, reallocating the latter if needed.
267 @param[in out] BootOrder The structure pointing to the array and holding
268 allocation and usage counters.
270 @param[in] ActiveOption The active boot option whose ID should be
271 appended to the array.
274 @retval RETURN_SUCCESS ID of ActiveOption appended.
276 @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.
282 IN OUT BOOT_ORDER
*BootOrder
,
283 IN OUT ACTIVE_OPTION
*ActiveOption
286 if (BootOrder
->Produced
== BootOrder
->Allocated
) {
290 ASSERT (BootOrder
->Allocated
> 0);
291 AllocatedNew
= BootOrder
->Allocated
* 2;
292 DataNew
= ReallocatePool (
293 BootOrder
->Allocated
* sizeof (*BootOrder
->Data
),
294 AllocatedNew
* sizeof (*DataNew
),
297 if (DataNew
== NULL
) {
298 return RETURN_OUT_OF_RESOURCES
;
300 BootOrder
->Allocated
= AllocatedNew
;
301 BootOrder
->Data
= DataNew
;
304 BootOrder
->Data
[BootOrder
->Produced
++] =
305 (UINT16
) ActiveOption
->BootOption
->OptionNumber
;
306 ActiveOption
->Appended
= TRUE
;
307 return RETURN_SUCCESS
;
313 Create an array of ACTIVE_OPTION elements for a boot option array.
315 @param[in] BootOptions A boot option array, created with
316 EfiBootManagerRefreshAllBootOption () and
317 EfiBootManagerGetLoadOptions ().
319 @param[in] BootOptionCount The number of elements in BootOptions.
321 @param[out] ActiveOption Pointer to the first element in the new array.
322 The caller is responsible for freeing the array
323 with FreePool() after use.
325 @param[out] Count Number of elements in the new array.
328 @retval RETURN_SUCCESS The ActiveOption array has been created.
330 @retval RETURN_NOT_FOUND No active entry has been found in
333 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
338 CollectActiveOptions (
339 IN CONST EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
,
340 IN UINTN BootOptionCount
,
341 OUT ACTIVE_OPTION
**ActiveOption
,
348 *ActiveOption
= NULL
;
351 // Scan the list twice:
352 // - count active entries,
353 // - store links to active entries.
355 for (ScanMode
= 0; ScanMode
< 2; ++ScanMode
) {
357 for (Index
= 0; Index
< BootOptionCount
; Index
++) {
358 if ((BootOptions
[Index
].Attributes
& LOAD_OPTION_ACTIVE
) != 0) {
360 (*ActiveOption
)[*Count
].BootOption
= &BootOptions
[Index
];
361 (*ActiveOption
)[*Count
].Appended
= FALSE
;
369 return RETURN_NOT_FOUND
;
371 *ActiveOption
= AllocatePool (*Count
* sizeof **ActiveOption
);
372 if (*ActiveOption
== NULL
) {
373 return RETURN_OUT_OF_RESOURCES
;
377 return RETURN_SUCCESS
;
382 OpenFirmware device path node
385 SUBSTRING DriverName
;
386 SUBSTRING UnitAddress
;
387 SUBSTRING DeviceArguments
;
393 Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
394 structure, and advance in the input string.
396 The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"
397 (a leading slash is expected and not returned):
399 /driver-name@unit-address[:device-arguments][<LF>]
401 A single trailing <LF> character is consumed but not returned. A trailing
402 <LF> or NUL character terminates the device path.
404 The function relies on ASCII encoding.
406 @param[in out] Ptr Address of the pointer pointing to the start of the
407 node string. After successful parsing *Ptr is set to
408 the byte immediately following the consumed
409 characters. On error it points to the byte that
410 caused the error. The input string is never modified.
412 @param[out] OfwNode The members of this structure point into the input
413 string, designating components of the node.
414 Separators are never included. If "device-arguments"
415 is missing, then DeviceArguments.Ptr is set to NULL.
416 All components that are present have nonzero length.
418 If the call doesn't succeed, the contents of this
419 structure is indeterminate.
421 @param[out] IsFinal In case of successul parsing, this parameter signals
422 whether the node just parsed is the final node in the
423 device path. The call after a final node will attempt
424 to start parsing the next path. If the call doesn't
425 succeed, then this parameter is not changed.
428 @retval RETURN_SUCCESS Parsing successful.
430 @retval RETURN_NOT_FOUND Parsing terminated. *Ptr was (and is)
431 pointing to an empty string.
433 @retval RETURN_INVALID_PARAMETER Parse error.
439 IN OUT CONST CHAR8
**Ptr
,
440 OUT OFW_NODE
*OfwNode
,
445 // A leading slash is expected. End of string is tolerated.
449 return RETURN_NOT_FOUND
;
456 return RETURN_INVALID_PARAMETER
;
462 OfwNode
->DriverName
.Ptr
= *Ptr
;
463 OfwNode
->DriverName
.Len
= 0;
464 while (OfwNode
->DriverName
.Len
< 32 &&
465 (IsAlnum (**Ptr
) || IsDriverNamePunct (**Ptr
))
468 ++OfwNode
->DriverName
.Len
;
471 if (OfwNode
->DriverName
.Len
== 0 || OfwNode
->DriverName
.Len
== 32) {
472 return RETURN_INVALID_PARAMETER
;
480 return RETURN_INVALID_PARAMETER
;
484 OfwNode
->UnitAddress
.Ptr
= *Ptr
;
485 OfwNode
->UnitAddress
.Len
= 0;
486 while (IsPrintNotDelim (**Ptr
)) {
488 ++OfwNode
->UnitAddress
.Len
;
491 if (OfwNode
->UnitAddress
.Len
== 0) {
492 return RETURN_INVALID_PARAMETER
;
497 // device-arguments, may be omitted
499 OfwNode
->DeviceArguments
.Len
= 0;
502 OfwNode
->DeviceArguments
.Ptr
= *Ptr
;
504 while (IsPrintNotDelim (**Ptr
)) {
506 ++OfwNode
->DeviceArguments
.Len
;
509 if (OfwNode
->DeviceArguments
.Len
== 0) {
510 return RETURN_INVALID_PARAMETER
;
514 OfwNode
->DeviceArguments
.Ptr
= NULL
;
533 return RETURN_INVALID_PARAMETER
;
538 "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
540 OfwNode
->DriverName
.Len
, OfwNode
->DriverName
.Ptr
,
541 OfwNode
->UnitAddress
.Len
, OfwNode
->UnitAddress
.Ptr
,
542 OfwNode
->DeviceArguments
.Len
,
543 OfwNode
->DeviceArguments
.Ptr
== NULL
? "" : OfwNode
->DeviceArguments
.Ptr
545 return RETURN_SUCCESS
;
551 Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
554 @param[in] OfwNode Array of OpenFirmware device nodes to
555 translate, constituting the beginning of an
556 OpenFirmware device path.
558 @param[in] NumNodes Number of elements in OfwNode.
560 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
561 CreateExtraRootBusMap(), to be used for
562 translating positions of extra root buses to
565 @param[out] Translated Destination array receiving the UEFI path
566 fragment, allocated by the caller. If the
567 return value differs from RETURN_SUCCESS, its
568 contents is indeterminate.
570 @param[in out] TranslatedSize On input, the number of CHAR16's in
571 Translated. On RETURN_SUCCESS this parameter
572 is assigned the number of non-NUL CHAR16's
573 written to Translated. In case of other return
574 values, TranslatedSize is indeterminate.
577 @retval RETURN_SUCCESS Translation successful.
579 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
582 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
583 be translated in the current implementation.
585 @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an
586 extra PCI root bus (by serial number) that
587 is invalid according to ExtraPciRoots.
592 TranslatePciOfwNodes (
593 IN CONST OFW_NODE
*OfwNode
,
595 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
596 OUT CHAR16
*Translated
,
597 IN OUT UINTN
*TranslatedSize
602 UINTN FirstNonBridge
;
603 CHAR16 Bridges
[BRIDGE_TRANSLATION_OUTPUT_SIZE
];
610 // Resolve the PCI root bus number.
612 // The initial OFW node for the main root bus (ie. bus number 0) is:
616 // For extra root buses, the initial OFW node is
620 // root bus serial number (not PCI bus number)
622 if (NumNodes
< REQUIRED_PCI_OFW_NODES
||
623 !SubstringEq (OfwNode
[0].DriverName
, "pci")
625 return RETURN_UNSUPPORTED
;
629 Comma
= ScanMem8 (OfwNode
[0].UnitAddress
.Ptr
, OfwNode
[0].UnitAddress
.Len
,
632 SUBSTRING PciRootSerialSubString
;
633 UINT64 PciRootSerial
;
636 // Parse the root bus serial number from the unit address after the comma.
638 PciRootSerialSubString
.Ptr
= Comma
+ 1;
639 PciRootSerialSubString
.Len
= OfwNode
[0].UnitAddress
.Len
-
640 (PciRootSerialSubString
.Ptr
-
641 OfwNode
[0].UnitAddress
.Ptr
);
643 if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString
,
644 &PciRootSerial
, &NumEntries
))) {
645 return RETURN_UNSUPPORTED
;
649 // Map the extra root bus's serial number to its actual bus number.
651 if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots
, PciRootSerial
,
653 return RETURN_PROTOCOL_ERROR
;
658 // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
662 // PCI slot & function on the parent, holding the bridge
664 // and the UEFI device path node is:
672 UINT64 BridgeDevFun
[2];
673 UINTN BridgesFreeBytes
;
675 if (!SubstringEq (OfwNode
[FirstNonBridge
].DriverName
, "pci-bridge")) {
680 NumEntries
= sizeof BridgeDevFun
/ sizeof BridgeDevFun
[0];
681 if (ParseUnitAddressHexList (OfwNode
[FirstNonBridge
].UnitAddress
,
682 BridgeDevFun
, &NumEntries
) != RETURN_SUCCESS
) {
683 return RETURN_UNSUPPORTED
;
686 BridgesFreeBytes
= sizeof Bridges
- BridgesLen
* sizeof Bridges
[0];
687 Written
= UnicodeSPrintAsciiFormat (Bridges
+ BridgesLen
, BridgesFreeBytes
,
688 "/Pci(0x%Lx,0x%Lx)", BridgeDevFun
[0], BridgeDevFun
[1]);
689 BridgesLen
+= Written
;
692 // There's no way to differentiate between "completely used up without
693 // truncation" and "truncated", so treat the former as the latter.
695 if (BridgesLen
+ 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE
) {
696 return RETURN_UNSUPPORTED
;
700 } while (FirstNonBridge
< NumNodes
);
702 if (FirstNonBridge
== NumNodes
) {
703 return RETURN_UNSUPPORTED
;
707 // Parse the OFW nodes starting with the first non-bridge node.
710 NumEntries
= sizeof (PciDevFun
) / sizeof (PciDevFun
[0]);
711 if (ParseUnitAddressHexList (
712 OfwNode
[FirstNonBridge
].UnitAddress
,
717 return RETURN_UNSUPPORTED
;
720 if (NumNodes
>= FirstNonBridge
+ 3 &&
721 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "ide") &&
722 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
723 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
726 // OpenFirmware device path (IDE disk, IDE CD-ROM):
728 // /pci@i0cf8/ide@1,1/drive@0/disk@0
730 // | | | | master or slave
731 // | | | primary or secondary
732 // | PCI slot & function holding IDE controller
733 // PCI root at system bus port, PIO
737 // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
745 if (ParseUnitAddressHexList (
746 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
749 ) != RETURN_SUCCESS
||
751 ParseUnitAddressHexList (
752 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
754 &NumEntries
// reuse after previous single-element call
755 ) != RETURN_SUCCESS
||
758 return RETURN_UNSUPPORTED
;
761 Written
= UnicodeSPrintAsciiFormat (
763 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
764 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
769 Secondary
? "Secondary" : "Primary",
770 Slave
? "Slave" : "Master"
772 } else if (NumNodes
>= FirstNonBridge
+ 3 &&
773 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,2922") &&
774 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "drive") &&
775 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
778 // OpenFirmware device path (Q35 SATA disk and CD-ROM):
780 // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
782 // | | | | device number (fixed 0)
783 // | | | channel (port) number
784 // | PCI slot & function holding SATA HBA
785 // PCI root at system bus port, PIO
789 // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0x0,0x0)
791 // | | LUN (always 0 on Q35)
792 // | port multiplier port number,
794 // channel (port) number
799 if (RETURN_ERROR (ParseUnitAddressHexList (
800 OfwNode
[FirstNonBridge
+ 1].UnitAddress
, &Channel
,
802 return RETURN_UNSUPPORTED
;
805 Written
= UnicodeSPrintAsciiFormat (
807 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
808 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0x0,0x0)",
815 } else if (NumNodes
>= FirstNonBridge
+ 3 &&
816 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "isa") &&
817 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "fdc") &&
818 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "floppy")
821 // OpenFirmware device path (floppy disk):
823 // /pci@i0cf8/isa@1/fdc@03f0/floppy@0
826 // | | ISA controller io-port (hex)
827 // | PCI slot holding ISA controller
828 // PCI root at system bus port, PIO
832 // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
839 if (ParseUnitAddressHexList (
840 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
843 ) != RETURN_SUCCESS
||
846 return RETURN_UNSUPPORTED
;
849 Written
= UnicodeSPrintAsciiFormat (
851 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
852 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
859 } else if (NumNodes
>= FirstNonBridge
+ 2 &&
860 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
861 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "disk")
864 // OpenFirmware device path (virtio-blk disk):
866 // /pci@i0cf8/scsi@6[,3]/disk@0,0
869 // | | PCI function corresponding to disk (optional)
870 // | PCI slot holding disk
871 // PCI root at system bus port, PIO
873 // UEFI device path prefix:
875 // PciRoot(0x0)/Pci(0x6,0x0)/HD( -- if PCI function is 0 or absent
876 // PciRoot(0x0)/Pci(0x6,0x3)/HD( -- if PCI function is present and nonzero
878 Written
= UnicodeSPrintAsciiFormat (
880 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
881 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/HD(",
887 } else if (NumNodes
>= FirstNonBridge
+ 3 &&
888 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "scsi") &&
889 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "channel") &&
890 SubstringEq (OfwNode
[FirstNonBridge
+ 2].DriverName
, "disk")
893 // OpenFirmware device path (virtio-scsi disk):
895 // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
899 // | | channel (unused, fixed 0)
900 // | PCI slot[, function] holding SCSI controller
901 // PCI root at system bus port, PIO
903 // UEFI device path prefix:
905 // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
906 // -- if PCI function is 0 or absent
907 // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
908 // -- if PCI function is present and nonzero
913 NumEntries
= sizeof (TargetLun
) / sizeof (TargetLun
[0]);
914 if (ParseUnitAddressHexList (
915 OfwNode
[FirstNonBridge
+ 2].UnitAddress
,
920 return RETURN_UNSUPPORTED
;
923 Written
= UnicodeSPrintAsciiFormat (
925 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
926 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
934 } else if (NumNodes
>= FirstNonBridge
+ 2 &&
935 SubstringEq (OfwNode
[FirstNonBridge
+ 0].DriverName
, "pci8086,5845") &&
936 SubstringEq (OfwNode
[FirstNonBridge
+ 1].DriverName
, "namespace")
939 // OpenFirmware device path (NVMe device):
941 // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
943 // | | | | Extended Unique Identifier
944 // | | | | (EUI-64), big endian interp.
945 // | | | namespace ID
946 // | PCI slot & function holding NVMe controller
947 // PCI root at system bus port, PIO
951 // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
953 // | octets of the EUI-64
954 // | in address order
958 UINTN RequiredEntries
;
961 RequiredEntries
= sizeof (Namespace
) / sizeof (Namespace
[0]);
962 NumEntries
= RequiredEntries
;
963 if (ParseUnitAddressHexList (
964 OfwNode
[FirstNonBridge
+ 1].UnitAddress
,
967 ) != RETURN_SUCCESS
||
968 NumEntries
!= RequiredEntries
||
970 Namespace
[0] >= MAX_UINT32
972 return RETURN_UNSUPPORTED
;
975 Eui64
= (UINT8
*)&Namespace
[1];
976 Written
= UnicodeSPrintAsciiFormat (
978 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
979 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
980 "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
986 Eui64
[7], Eui64
[6], Eui64
[5], Eui64
[4],
987 Eui64
[3], Eui64
[2], Eui64
[1], Eui64
[0]
991 // Generic OpenFirmware device path for PCI devices:
993 // /pci@i0cf8/ethernet@3[,2]
995 // | PCI slot[, function] holding Ethernet card
996 // PCI root at system bus port, PIO
998 // UEFI device path prefix (dependent on presence of nonzero PCI function):
1000 // PciRoot(0x0)/Pci(0x3,0x0)
1001 // PciRoot(0x0)/Pci(0x3,0x2)
1003 Written
= UnicodeSPrintAsciiFormat (
1005 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1006 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
1015 // There's no way to differentiate between "completely used up without
1016 // truncation" and "truncated", so treat the former as the latter, and return
1017 // success only for "some room left unused".
1019 if (Written
+ 1 < *TranslatedSize
) {
1020 *TranslatedSize
= Written
;
1021 return RETURN_SUCCESS
;
1024 return RETURN_BUFFER_TOO_SMALL
;
1029 // A type providing easy raw access to the base address of a virtio-mmio
1035 } VIRTIO_MMIO_BASE_ADDRESS
;
1040 Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
1043 @param[in] OfwNode Array of OpenFirmware device nodes to
1044 translate, constituting the beginning of an
1045 OpenFirmware device path.
1047 @param[in] NumNodes Number of elements in OfwNode.
1049 @param[out] Translated Destination array receiving the UEFI path
1050 fragment, allocated by the caller. If the
1051 return value differs from RETURN_SUCCESS, its
1052 contents is indeterminate.
1054 @param[in out] TranslatedSize On input, the number of CHAR16's in
1055 Translated. On RETURN_SUCCESS this parameter
1056 is assigned the number of non-NUL CHAR16's
1057 written to Translated. In case of other return
1058 values, TranslatedSize is indeterminate.
1061 @retval RETURN_SUCCESS Translation successful.
1063 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1066 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1067 be translated in the current implementation.
1072 TranslateMmioOfwNodes (
1073 IN CONST OFW_NODE
*OfwNode
,
1075 OUT CHAR16
*Translated
,
1076 IN OUT UINTN
*TranslatedSize
1079 VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase
;
1080 CHAR16 VenHwString
[60 + 1];
1085 // Get the base address of the virtio-mmio transport.
1087 if (NumNodes
< REQUIRED_MMIO_OFW_NODES
||
1088 !SubstringEq (OfwNode
[0].DriverName
, "virtio-mmio")
1090 return RETURN_UNSUPPORTED
;
1093 if (ParseUnitAddressHexList (
1094 OfwNode
[0].UnitAddress
,
1095 &VirtioMmioBase
.Uint64
,
1099 return RETURN_UNSUPPORTED
;
1102 UnicodeSPrintAsciiFormat (VenHwString
, sizeof VenHwString
,
1103 "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid
,
1104 VirtioMmioBase
.Raw
[0], VirtioMmioBase
.Raw
[1], VirtioMmioBase
.Raw
[2],
1105 VirtioMmioBase
.Raw
[3], VirtioMmioBase
.Raw
[4], VirtioMmioBase
.Raw
[5],
1106 VirtioMmioBase
.Raw
[6], VirtioMmioBase
.Raw
[7]);
1108 if (NumNodes
>= 2 &&
1109 SubstringEq (OfwNode
[1].DriverName
, "disk")) {
1111 // OpenFirmware device path (virtio-blk disk):
1113 // /virtio-mmio@000000000a003c00/disk@0,0
1116 // base address of virtio-mmio register block
1118 // UEFI device path prefix:
1120 // <VenHwString>/HD(
1122 Written
= UnicodeSPrintAsciiFormat (
1124 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1128 } else if (NumNodes
>= 3 &&
1129 SubstringEq (OfwNode
[1].DriverName
, "channel") &&
1130 SubstringEq (OfwNode
[2].DriverName
, "disk")) {
1132 // OpenFirmware device path (virtio-scsi disk):
1134 // /virtio-mmio@000000000a003a00/channel@0/disk@2,3
1138 // | channel (unused, fixed 0)
1139 // base address of virtio-mmio register block
1141 // UEFI device path prefix:
1143 // <VenHwString>/Scsi(0x2,0x3)
1145 UINT64 TargetLun
[2];
1148 NumEntries
= sizeof (TargetLun
) / sizeof (TargetLun
[0]);
1149 if (ParseUnitAddressHexList (
1150 OfwNode
[2].UnitAddress
,
1155 return RETURN_UNSUPPORTED
;
1158 Written
= UnicodeSPrintAsciiFormat (
1160 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1161 "%s/Scsi(0x%Lx,0x%Lx)",
1166 } else if (NumNodes
>= 2 &&
1167 SubstringEq (OfwNode
[1].DriverName
, "ethernet-phy")) {
1169 // OpenFirmware device path (virtio-net NIC):
1171 // /virtio-mmio@000000000a003e00/ethernet-phy@0
1174 // base address of virtio-mmio register block
1176 // UEFI device path prefix (dependent on presence of nonzero PCI function):
1178 // <VenHwString>/MAC(
1180 Written
= UnicodeSPrintAsciiFormat (
1182 *TranslatedSize
* sizeof (*Translated
), // BufferSize in bytes
1187 return RETURN_UNSUPPORTED
;
1191 // There's no way to differentiate between "completely used up without
1192 // truncation" and "truncated", so treat the former as the latter, and return
1193 // success only for "some room left unused".
1195 if (Written
+ 1 < *TranslatedSize
) {
1196 *TranslatedSize
= Written
;
1197 return RETURN_SUCCESS
;
1200 return RETURN_BUFFER_TOO_SMALL
;
1206 Translate an array of OpenFirmware device nodes to a UEFI device path
1209 @param[in] OfwNode Array of OpenFirmware device nodes to
1210 translate, constituting the beginning of an
1211 OpenFirmware device path.
1213 @param[in] NumNodes Number of elements in OfwNode.
1215 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1216 CreateExtraRootBusMap(), to be used for
1217 translating positions of extra root buses to
1220 @param[out] Translated Destination array receiving the UEFI path
1221 fragment, allocated by the caller. If the
1222 return value differs from RETURN_SUCCESS, its
1223 contents is indeterminate.
1225 @param[in out] TranslatedSize On input, the number of CHAR16's in
1226 Translated. On RETURN_SUCCESS this parameter
1227 is assigned the number of non-NUL CHAR16's
1228 written to Translated. In case of other return
1229 values, TranslatedSize is indeterminate.
1232 @retval RETURN_SUCCESS Translation successful.
1234 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
1237 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
1238 be translated in the current implementation.
1240 @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has
1241 been (partially) recognized, but it contains
1242 a logic error / doesn't match system state.
1248 IN CONST OFW_NODE
*OfwNode
,
1250 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1251 OUT CHAR16
*Translated
,
1252 IN OUT UINTN
*TranslatedSize
1255 RETURN_STATUS Status
;
1257 Status
= RETURN_UNSUPPORTED
;
1259 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1260 Status
= TranslatePciOfwNodes (OfwNode
, NumNodes
, ExtraPciRoots
,
1261 Translated
, TranslatedSize
);
1263 if (Status
== RETURN_UNSUPPORTED
&&
1264 FeaturePcdGet (PcdQemuBootOrderMmioTranslation
)) {
1265 Status
= TranslateMmioOfwNodes (OfwNode
, NumNodes
, Translated
,
1273 Translate an OpenFirmware device path fragment to a UEFI device path
1274 fragment, and advance in the input string.
1276 @param[in out] Ptr Address of the pointer pointing to the start
1277 of the path string. After successful
1278 translation (RETURN_SUCCESS) or at least
1279 successful parsing (RETURN_UNSUPPORTED,
1280 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
1281 byte immediately following the consumed
1282 characters. In other error cases, it points to
1283 the byte that caused the error.
1285 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with
1286 CreateExtraRootBusMap(), to be used for
1287 translating positions of extra root buses to
1290 @param[out] Translated Destination array receiving the UEFI path
1291 fragment, allocated by the caller. If the
1292 return value differs from RETURN_SUCCESS, its
1293 contents is indeterminate.
1295 @param[in out] TranslatedSize On input, the number of CHAR16's in
1296 Translated. On RETURN_SUCCESS this parameter
1297 is assigned the number of non-NUL CHAR16's
1298 written to Translated. In case of other return
1299 values, TranslatedSize is indeterminate.
1302 @retval RETURN_SUCCESS Translation successful.
1304 @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed
1305 successfully, but its translation did not
1306 fit into the number of bytes provided.
1307 Further calls to this function are
1310 @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed
1311 successfully, but it can't be translated in
1312 the current implementation. Further calls
1313 to this function are possible.
1315 @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been
1316 (partially) recognized, but it contains a
1317 logic error / doesn't match system state.
1318 Further calls to this function are
1321 @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was
1322 pointing to the empty string or "HALT". On
1323 output, *Ptr points to the empty string
1324 (ie. "HALT" is consumed transparently when
1327 @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.
1333 IN OUT CONST CHAR8
**Ptr
,
1334 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
,
1335 OUT CHAR16
*Translated
,
1336 IN OUT UINTN
*TranslatedSize
1340 RETURN_STATUS Status
;
1341 OFW_NODE Node
[EXAMINED_OFW_NODES
];
1347 if (AsciiStrCmp (*Ptr
, "HALT") == 0) {
1349 Status
= RETURN_NOT_FOUND
;
1351 Status
= ParseOfwNode (Ptr
, &Node
[NumNodes
], &IsFinal
);
1354 if (Status
== RETURN_NOT_FOUND
) {
1355 DEBUG ((DEBUG_VERBOSE
, "%a: no more nodes\n", __FUNCTION__
));
1356 return RETURN_NOT_FOUND
;
1359 while (Status
== RETURN_SUCCESS
&& !IsFinal
) {
1361 Status
= ParseOfwNode (
1363 (NumNodes
< EXAMINED_OFW_NODES
) ? &Node
[NumNodes
] : &Skip
,
1369 case RETURN_SUCCESS
:
1373 case RETURN_INVALID_PARAMETER
:
1374 DEBUG ((DEBUG_VERBOSE
, "%a: parse error\n", __FUNCTION__
));
1375 return RETURN_INVALID_PARAMETER
;
1381 Status
= TranslateOfwNodes (
1383 NumNodes
< EXAMINED_OFW_NODES
? NumNodes
: EXAMINED_OFW_NODES
,
1388 case RETURN_SUCCESS
:
1389 DEBUG ((DEBUG_VERBOSE
, "%a: success: \"%s\"\n", __FUNCTION__
, Translated
));
1392 case RETURN_BUFFER_TOO_SMALL
:
1393 DEBUG ((DEBUG_VERBOSE
, "%a: buffer too small\n", __FUNCTION__
));
1396 case RETURN_UNSUPPORTED
:
1397 DEBUG ((DEBUG_VERBOSE
, "%a: unsupported\n", __FUNCTION__
));
1400 case RETURN_PROTOCOL_ERROR
:
1401 DEBUG ((DEBUG_VERBOSE
, "%a: logic error / system state mismatch\n",
1414 Convert the UEFI DevicePath to full text representation with DevPathToText,
1415 then match the UEFI device path fragment in Translated against it.
1417 @param[in] Translated UEFI device path fragment, translated from
1418 OpenFirmware format, to search for.
1420 @param[in] TranslatedLength The length of Translated in CHAR16's.
1422 @param[in] DevicePath Boot option device path whose textual rendering
1425 @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.
1428 @retval TRUE If Translated was found at the beginning of DevicePath after
1429 converting the latter to text.
1431 @retval FALSE If DevicePath was NULL, or it could not be converted, or there
1438 IN CONST CHAR16
*Translated
,
1439 IN UINTN TranslatedLength
,
1440 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1447 EFI_DEVICE_PATH_PROTOCOL
*AbsDevicePath
;
1448 CHAR16
*AbsConverted
;
1450 EFI_DEVICE_PATH_PROTOCOL
*Node
;
1452 Converted
= ConvertDevicePathToText (
1454 FALSE
, // DisplayOnly
1455 FALSE
// AllowShortcuts
1457 if (Converted
== NULL
) {
1464 // Expand the short-form device path to full device path
1466 if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1467 (DevicePathSubType (DevicePath
) == MEDIA_HARDDRIVE_DP
)) {
1469 // Harddrive shortform device path
1472 } else if ((DevicePathType (DevicePath
) == MEDIA_DEVICE_PATH
) &&
1473 (DevicePathSubType (DevicePath
) == MEDIA_FILEPATH_DP
)) {
1475 // File-path shortform device path
1478 } else if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
1479 (DevicePathSubType (DevicePath
) == MSG_URI_DP
)) {
1481 // URI shortform device path
1485 for ( Node
= DevicePath
1486 ; !IsDevicePathEnd (Node
)
1487 ; Node
= NextDevicePathNode (Node
)
1489 if ((DevicePathType (Node
) == MESSAGING_DEVICE_PATH
) &&
1490 ((DevicePathSubType (Node
) == MSG_USB_CLASS_DP
) ||
1491 (DevicePathSubType (Node
) == MSG_USB_WWID_DP
))) {
1499 // Attempt to expand any relative UEFI device path to
1500 // an absolute device path first.
1503 FileBuffer
= EfiBootManagerGetLoadOptionBuffer (
1504 DevicePath
, &AbsDevicePath
, &FileSize
1506 if (FileBuffer
== NULL
) {
1509 FreePool (FileBuffer
);
1510 AbsConverted
= ConvertDevicePathToText (AbsDevicePath
, FALSE
, FALSE
);
1511 FreePool (AbsDevicePath
);
1512 if (AbsConverted
== NULL
) {
1515 DEBUG ((DEBUG_VERBOSE
,
1516 "%a: expanded relative device path \"%s\" for prefix matching\n",
1517 __FUNCTION__
, Converted
));
1518 FreePool (Converted
);
1519 Converted
= AbsConverted
;
1523 // Is Translated a prefix of Converted?
1525 Result
= (BOOLEAN
)(StrnCmp (Converted
, Translated
, TranslatedLength
) == 0);
1528 "%a: against \"%s\": %a\n",
1531 Result
? "match" : "no match"
1534 FreePool (Converted
);
1540 Append some of the unselected active boot options to the boot order.
1542 This function should accommodate any further policy changes in "boot option
1543 survival". Currently we're adding back everything that starts with neither
1544 PciRoot() nor HD() nor a virtio-mmio VenHw() node.
1546 @param[in,out] BootOrder The structure holding the boot order to
1547 complete. The caller is responsible for
1548 initializing (and potentially populating) it
1549 before calling this function.
1551 @param[in,out] ActiveOption The array of active boot options to scan.
1552 Entries marked as Appended will be skipped.
1553 Those of the rest that satisfy the survival
1554 policy will be added to BootOrder with
1557 @param[in] ActiveCount Number of elements in ActiveOption.
1560 @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot
1563 @return Error codes returned by BootOrderAppend().
1568 IN OUT BOOT_ORDER
*BootOrder
,
1569 IN OUT ACTIVE_OPTION
*ActiveOption
,
1570 IN UINTN ActiveCount
1573 RETURN_STATUS Status
;
1576 Status
= RETURN_SUCCESS
;
1578 while (!RETURN_ERROR (Status
) && Idx
< ActiveCount
) {
1579 if (!ActiveOption
[Idx
].Appended
) {
1580 CONST EFI_BOOT_MANAGER_LOAD_OPTION
*Current
;
1581 CONST EFI_DEVICE_PATH_PROTOCOL
*FirstNode
;
1583 Current
= ActiveOption
[Idx
].BootOption
;
1584 FirstNode
= Current
->FilePath
;
1585 if (FirstNode
!= NULL
) {
1587 STATIC CHAR16 ConvFallBack
[] = L
"<unable to convert>";
1590 Converted
= ConvertDevicePathToText (FirstNode
, FALSE
, FALSE
);
1591 if (Converted
== NULL
) {
1592 Converted
= ConvFallBack
;
1596 if (DevicePathType(FirstNode
) == MEDIA_DEVICE_PATH
&&
1597 DevicePathSubType(FirstNode
) == MEDIA_HARDDRIVE_DP
) {
1602 } else if (DevicePathType(FirstNode
) == ACPI_DEVICE_PATH
&&
1603 DevicePathSubType(FirstNode
) == ACPI_DP
) {
1604 ACPI_HID_DEVICE_PATH
*Acpi
;
1606 Acpi
= (ACPI_HID_DEVICE_PATH
*) FirstNode
;
1607 if ((Acpi
->HID
& PNP_EISA_ID_MASK
) == PNP_EISA_ID_CONST
&&
1608 EISA_ID_TO_NUM (Acpi
->HID
) == 0x0a03) {
1610 // drop PciRoot() if we enabled the user to select PCI-like boot
1611 // options, by providing translation for such OFW device path
1614 Keep
= !FeaturePcdGet (PcdQemuBootOrderPciTranslation
);
1616 } else if (DevicePathType(FirstNode
) == HARDWARE_DEVICE_PATH
&&
1617 DevicePathSubType(FirstNode
) == HW_VENDOR_DP
) {
1618 VENDOR_DEVICE_PATH
*VenHw
;
1620 VenHw
= (VENDOR_DEVICE_PATH
*)FirstNode
;
1621 if (CompareGuid (&VenHw
->Guid
, &gVirtioMmioTransportGuid
)) {
1623 // drop virtio-mmio if we enabled the user to select boot options
1624 // referencing such device paths
1626 Keep
= !FeaturePcdGet (PcdQemuBootOrderMmioTranslation
);
1631 Status
= BootOrderAppend (BootOrder
, &ActiveOption
[Idx
]);
1632 if (!RETURN_ERROR (Status
)) {
1633 DEBUG ((DEBUG_VERBOSE
, "%a: keeping \"%s\"\n", __FUNCTION__
,
1637 DEBUG ((DEBUG_VERBOSE
, "%a: dropping \"%s\"\n", __FUNCTION__
,
1641 if (Converted
!= ConvFallBack
) {
1642 FreePool (Converted
);
1653 Delete Boot#### variables that stand for such active boot options that have
1654 been dropped (ie. have not been selected by either matching or "survival
1657 @param[in] ActiveOption The array of active boot options to scan. Each
1658 entry not marked as appended will trigger the
1659 deletion of the matching Boot#### variable.
1661 @param[in] ActiveCount Number of elements in ActiveOption.
1665 PruneBootVariables (
1666 IN CONST ACTIVE_OPTION
*ActiveOption
,
1667 IN UINTN ActiveCount
1672 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
1673 if (!ActiveOption
[Idx
].Appended
) {
1674 CHAR16 VariableName
[9];
1676 UnicodeSPrintAsciiFormat (VariableName
, sizeof VariableName
, "Boot%04x",
1677 ActiveOption
[Idx
].BootOption
->OptionNumber
);
1680 // "The space consumed by the deleted variable may not be available until
1681 // the next power cycle", but that's good enough.
1683 gRT
->SetVariable (VariableName
, &gEfiGlobalVariableGuid
,
1684 0, // Attributes, 0 means deletion
1685 0, // DataSize, 0 means deletion
1695 Set the boot order based on configuration retrieved from QEMU.
1697 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
1698 OpenFirmware device paths therein to UEFI device path fragments. Match the
1699 translated fragments against BootOptionList, and rewrite the BootOrder NvVar
1700 so that it corresponds to the order described in fw_cfg.
1702 @param[in] BootOptionList A boot option list, created with
1703 BdsLibEnumerateAllBootOption ().
1706 @retval RETURN_SUCCESS BootOrder NvVar rewritten.
1708 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
1710 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
1711 file, or no match found between the
1712 "bootorder" fw_cfg file and BootOptionList.
1714 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
1716 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
1718 @return Values returned by gBS->LocateProtocol ()
1719 or gRT->SetVariable ().
1723 SetBootOrderFromQemu (
1724 IN CONST LIST_ENTRY
*BootOptionList
1727 RETURN_STATUS Status
;
1728 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1731 CONST CHAR8
*FwCfgPtr
;
1733 BOOT_ORDER BootOrder
;
1734 ACTIVE_OPTION
*ActiveOption
;
1737 EXTRA_ROOT_BUS_MAP
*ExtraPciRoots
;
1739 UINTN TranslatedSize
;
1740 CHAR16 Translated
[TRANSLATION_OUTPUT_SIZE
];
1741 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
;
1742 UINTN BootOptionCount
;
1745 // The QemuBootOrderLib is linked by OvmfPkg and ArmVirtPkg.
1746 // OvmfPkg was changed to use the new BDS @ MdeModulePkg, so boot options
1747 // are no longer stored in linked list.
1748 // But we don't change the QemuBootOrderLib class interface because
1749 // ArmVirtPkg are still using old BDS @ IntelFrameworkModulePkg.
1751 ASSERT (BootOptionList
== NULL
);
1753 Status
= QemuFwCfgFindFile ("bootorder", &FwCfgItem
, &FwCfgSize
);
1754 if (Status
!= RETURN_SUCCESS
) {
1758 if (FwCfgSize
== 0) {
1759 return RETURN_NOT_FOUND
;
1762 FwCfg
= AllocatePool (FwCfgSize
);
1763 if (FwCfg
== NULL
) {
1764 return RETURN_OUT_OF_RESOURCES
;
1767 QemuFwCfgSelectItem (FwCfgItem
);
1768 QemuFwCfgReadBytes (FwCfgSize
, FwCfg
);
1769 if (FwCfg
[FwCfgSize
- 1] != '\0') {
1770 Status
= RETURN_INVALID_PARAMETER
;
1771 goto ErrorFreeFwCfg
;
1774 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg:\n", __FUNCTION__
));
1775 DEBUG ((DEBUG_VERBOSE
, "%a\n", FwCfg
));
1776 DEBUG ((DEBUG_VERBOSE
, "%a: FwCfg: <end>\n", __FUNCTION__
));
1779 BootOrder
.Produced
= 0;
1780 BootOrder
.Allocated
= 1;
1781 BootOrder
.Data
= AllocatePool (
1782 BootOrder
.Allocated
* sizeof (*BootOrder
.Data
)
1784 if (BootOrder
.Data
== NULL
) {
1785 Status
= RETURN_OUT_OF_RESOURCES
;
1786 goto ErrorFreeFwCfg
;
1789 BootOptions
= EfiBootManagerGetLoadOptions (
1790 &BootOptionCount
, LoadOptionTypeBoot
1792 if (BootOptions
== NULL
) {
1793 Status
= RETURN_NOT_FOUND
;
1794 goto ErrorFreeBootOrder
;
1797 Status
= CollectActiveOptions (
1798 BootOptions
, BootOptionCount
, &ActiveOption
, &ActiveCount
1800 if (RETURN_ERROR (Status
)) {
1801 goto ErrorFreeBootOptions
;
1804 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation
)) {
1805 Status
= CreateExtraRootBusMap (&ExtraPciRoots
);
1806 if (EFI_ERROR (Status
)) {
1807 goto ErrorFreeActiveOption
;
1810 ExtraPciRoots
= NULL
;
1814 // translate each OpenFirmware path
1816 TranslatedSize
= sizeof (Translated
) / sizeof (Translated
[0]);
1817 Status
= TranslateOfwPath (&FwCfgPtr
, ExtraPciRoots
, Translated
,
1819 while (Status
== RETURN_SUCCESS
||
1820 Status
== RETURN_UNSUPPORTED
||
1821 Status
== RETURN_PROTOCOL_ERROR
||
1822 Status
== RETURN_BUFFER_TOO_SMALL
) {
1823 if (Status
== RETURN_SUCCESS
) {
1827 // match translated OpenFirmware path against all active boot options
1829 for (Idx
= 0; Idx
< ActiveCount
; ++Idx
) {
1832 TranslatedSize
, // contains length, not size, in CHAR16's here
1833 ActiveOption
[Idx
].BootOption
->FilePath
1837 // match found, store ID and continue with next OpenFirmware path
1839 Status
= BootOrderAppend (&BootOrder
, &ActiveOption
[Idx
]);
1840 if (Status
!= RETURN_SUCCESS
) {
1841 goto ErrorFreeExtraPciRoots
;
1845 } // scanned all active boot options
1846 } // translation successful
1848 TranslatedSize
= sizeof (Translated
) / sizeof (Translated
[0]);
1849 Status
= TranslateOfwPath (&FwCfgPtr
, ExtraPciRoots
, Translated
,
1851 } // scanning of OpenFirmware paths done
1853 if (Status
== RETURN_NOT_FOUND
&& BootOrder
.Produced
> 0) {
1855 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
1856 // Some of the active boot options that have not been selected over fw_cfg
1857 // should be preserved at the end of the boot order.
1859 Status
= BootOrderComplete (&BootOrder
, ActiveOption
, ActiveCount
);
1860 if (RETURN_ERROR (Status
)) {
1861 goto ErrorFreeExtraPciRoots
;
1865 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
1868 Status
= gRT
->SetVariable (
1870 &gEfiGlobalVariableGuid
,
1871 EFI_VARIABLE_NON_VOLATILE
|
1872 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
1873 EFI_VARIABLE_RUNTIME_ACCESS
,
1874 BootOrder
.Produced
* sizeof (*BootOrder
.Data
),
1877 if (EFI_ERROR (Status
)) {
1878 DEBUG ((DEBUG_ERROR
, "%a: setting BootOrder: %r\n", __FUNCTION__
, Status
));
1879 goto ErrorFreeExtraPciRoots
;
1882 DEBUG ((DEBUG_INFO
, "%a: setting BootOrder: success\n", __FUNCTION__
));
1883 PruneBootVariables (ActiveOption
, ActiveCount
);
1886 ErrorFreeExtraPciRoots
:
1887 if (ExtraPciRoots
!= NULL
) {
1888 DestroyExtraRootBusMap (ExtraPciRoots
);
1891 ErrorFreeActiveOption
:
1892 FreePool (ActiveOption
);
1894 ErrorFreeBootOptions
:
1895 EfiBootManagerFreeLoadOptions (BootOptions
, BootOptionCount
);
1898 FreePool (BootOrder
.Data
);
1908 Calculate the number of seconds we should be showing the FrontPage progress
1911 @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().
1914 GetFrontPageTimeoutFromQemu (
1918 FIRMWARE_CONFIG_ITEM BootMenuWaitItem
;
1919 UINTN BootMenuWaitSize
;
1921 QemuFwCfgSelectItem (QemuFwCfgItemBootMenu
);
1922 if (QemuFwCfgRead16 () == 0) {
1924 // The user specified "-boot menu=off", or didn't specify "-boot
1925 // menu=(on|off)" at all. Return the platform default.
1927 return PcdGet16 (PcdPlatformBootTimeOut
);
1930 if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem
,
1931 &BootMenuWaitSize
)) ||
1932 BootMenuWaitSize
!= sizeof (UINT16
)) {
1934 // "-boot menu=on" was specified without "splash-time=N". In this case,
1935 // return three seconds if the platform default would cause us to skip the
1936 // front page, and return the platform default otherwise.
1940 Timeout
= PcdGet16 (PcdPlatformBootTimeOut
);
1948 // "-boot menu=on,splash-time=N" was specified, where N is in units of
1949 // milliseconds. The Intel BDS Front Page progress bar only supports whole
1950 // seconds, round N up.
1952 QemuFwCfgSelectItem (BootMenuWaitItem
);
1953 return (UINT16
)((QemuFwCfgRead16 () + 999) / 1000);