• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
3 
4   Copyright (C) 2012 - 2014, Red Hat, Inc.
5   Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
6 
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
11 
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.
14 **/
15 
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>
29 
30 #include "ExtraRootBusMap.h"
31 
32 /**
33   OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
34 **/
35 #define TRANSLATION_OUTPUT_SIZE 0x100
36 
37 /**
38   Output buffer size for OpenFirmware to UEFI device path fragment translation,
39   in CHAR16's, for a sequence of PCI bridges.
40 **/
41 #define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40
42 
43 /**
44   Numbers of nodes in OpenFirmware device paths that are required and examined.
45 **/
46 #define REQUIRED_PCI_OFW_NODES  2
47 #define REQUIRED_MMIO_OFW_NODES 1
48 #define EXAMINED_OFW_NODES      6
49 
50 
51 /**
52   Simple character classification routines, corresponding to POSIX class names
53   and ASCII encoding.
54 **/
55 STATIC
56 BOOLEAN
IsAlnum(IN CHAR8 Chr)57 IsAlnum (
58   IN  CHAR8 Chr
59   )
60 {
61   return (('0' <= Chr && Chr <= '9') ||
62           ('A' <= Chr && Chr <= 'Z') ||
63           ('a' <= Chr && Chr <= 'z')
64           );
65 }
66 
67 
68 STATIC
69 BOOLEAN
IsDriverNamePunct(IN CHAR8 Chr)70 IsDriverNamePunct (
71   IN  CHAR8 Chr
72   )
73 {
74   return (Chr == ',' ||  Chr == '.' || Chr == '_' ||
75           Chr == '+' || Chr == '-'
76           );
77 }
78 
79 
80 STATIC
81 BOOLEAN
IsPrintNotDelim(IN CHAR8 Chr)82 IsPrintNotDelim (
83   IN  CHAR8 Chr
84   )
85 {
86   return (32 <= Chr && Chr <= 126 &&
87           Chr != '/' && Chr != '@' && Chr != ':');
88 }
89 
90 
91 /**
92   Utility types and functions.
93 **/
94 typedef struct {
95   CONST CHAR8 *Ptr; // not necessarily NUL-terminated
96   UINTN       Len;  // number of non-NUL characters
97 } SUBSTRING;
98 
99 
100 /**
101 
102   Check if Substring and String have identical contents.
103 
104   The function relies on the restriction that a SUBSTRING cannot have embedded
105   NULs either.
106 
107   @param[in] Substring  The SUBSTRING input to the comparison.
108 
109   @param[in] String     The ASCII string input to the comparison.
110 
111 
112   @return  Whether the inputs have identical contents.
113 
114 **/
115 STATIC
116 BOOLEAN
SubstringEq(IN SUBSTRING Substring,IN CONST CHAR8 * String)117 SubstringEq (
118   IN  SUBSTRING   Substring,
119   IN  CONST CHAR8 *String
120   )
121 {
122   UINTN       Pos;
123   CONST CHAR8 *Chr;
124 
125   Pos = 0;
126   Chr = String;
127 
128   while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {
129     ++Pos;
130     ++Chr;
131   }
132 
133   return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');
134 }
135 
136 
137 /**
138 
139   Parse a comma-separated list of hexadecimal integers into the elements of an
140   UINT64 array.
141 
142   Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
143   or an empty string are not allowed; they are rejected.
144 
145   The function relies on ASCII encoding.
146 
147   @param[in]     UnitAddress  The substring to parse.
148 
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.
152 
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
156                               fully.
157 
158 
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.
164 
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.
170 
171   @retval RETURN_INVALID_PARAMETER  Parse error. The contents of Results is
172                                     indeterminate. NumResults has not been
173                                     changed.
174 
175 **/
176 STATIC
177 RETURN_STATUS
ParseUnitAddressHexList(IN SUBSTRING UnitAddress,OUT UINT64 * Result,IN OUT UINTN * NumResults)178 ParseUnitAddressHexList (
179   IN      SUBSTRING  UnitAddress,
180   OUT     UINT64     *Result,
181   IN OUT  UINTN      *NumResults
182   )
183 {
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;
189 
190   Entry    = 0;
191   EntryVal = 0;
192   PrevChr  = ',';
193 
194   for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {
195     CHAR8 Chr;
196     INT8  Val;
197 
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'     ) :
202           -1;
203 
204     if (Val >= 0) {
205       if (EntryVal > 0xFFFFFFFFFFFFFFFull) {
206         return RETURN_INVALID_PARAMETER;
207       }
208       EntryVal = LShiftU64 (EntryVal, 4) | Val;
209     } else if (Chr == ',') {
210       if (PrevChr == ',') {
211         return RETURN_INVALID_PARAMETER;
212       }
213       if (Entry < *NumResults) {
214         Result[Entry] = EntryVal;
215       }
216       ++Entry;
217       EntryVal = 0;
218     } else {
219       return RETURN_INVALID_PARAMETER;
220     }
221 
222     PrevChr = Chr;
223   }
224 
225   if (PrevChr == ',') {
226     return RETURN_INVALID_PARAMETER;
227   }
228   if (Entry < *NumResults) {
229     Result[Entry] = EntryVal;
230     Status = RETURN_SUCCESS;
231   } else {
232     Status = RETURN_BUFFER_TOO_SMALL;
233   }
234   ++Entry;
235 
236   *NumResults = Entry;
237   return Status;
238 }
239 
240 
241 /**
242   A simple array of Boot Option ID's.
243 **/
244 typedef struct {
245   UINT16 *Data;
246   UINTN  Allocated;
247   UINTN  Produced;
248 } BOOT_ORDER;
249 
250 
251 /**
252   Array element tracking an enumerated boot option that has the
253   LOAD_OPTION_ACTIVE attribute.
254 **/
255 typedef struct {
256   CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; // reference only, no
257                                                   //   ownership
258   BOOLEAN                            Appended;    // has been added to a
259                                                   //   BOOT_ORDER?
260 } ACTIVE_OPTION;
261 
262 
263 /**
264 
265   Append an active boot option to BootOrder, reallocating the latter if needed.
266 
267   @param[in out] BootOrder     The structure pointing to the array and holding
268                                allocation and usage counters.
269 
270   @param[in]     ActiveOption  The active boot option whose ID should be
271                                appended to the array.
272 
273 
274   @retval RETURN_SUCCESS           ID of ActiveOption appended.
275 
276   @retval RETURN_OUT_OF_RESOURCES  Memory reallocation failed.
277 
278 **/
279 STATIC
280 RETURN_STATUS
BootOrderAppend(IN OUT BOOT_ORDER * BootOrder,IN OUT ACTIVE_OPTION * ActiveOption)281 BootOrderAppend (
282   IN OUT  BOOT_ORDER    *BootOrder,
283   IN OUT  ACTIVE_OPTION *ActiveOption
284   )
285 {
286   if (BootOrder->Produced == BootOrder->Allocated) {
287     UINTN  AllocatedNew;
288     UINT16 *DataNew;
289 
290     ASSERT (BootOrder->Allocated > 0);
291     AllocatedNew = BootOrder->Allocated * 2;
292     DataNew = ReallocatePool (
293                 BootOrder->Allocated * sizeof (*BootOrder->Data),
294                 AllocatedNew         * sizeof (*DataNew),
295                 BootOrder->Data
296                 );
297     if (DataNew == NULL) {
298       return RETURN_OUT_OF_RESOURCES;
299     }
300     BootOrder->Allocated = AllocatedNew;
301     BootOrder->Data      = DataNew;
302   }
303 
304   BootOrder->Data[BootOrder->Produced++] =
305                                (UINT16) ActiveOption->BootOption->OptionNumber;
306   ActiveOption->Appended = TRUE;
307   return RETURN_SUCCESS;
308 }
309 
310 
311 /**
312 
313   Create an array of ACTIVE_OPTION elements for a boot option array.
314 
315   @param[in]  BootOptions      A boot option array, created with
316                                EfiBootManagerRefreshAllBootOption () and
317                                EfiBootManagerGetLoadOptions ().
318 
319   @param[in]  BootOptionCount  The number of elements in BootOptions.
320 
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.
324 
325   @param[out] Count            Number of elements in the new array.
326 
327 
328   @retval RETURN_SUCCESS           The ActiveOption array has been created.
329 
330   @retval RETURN_NOT_FOUND         No active entry has been found in
331                                    BootOptions.
332 
333   @retval RETURN_OUT_OF_RESOURCES  Memory allocation failed.
334 
335 **/
336 STATIC
337 RETURN_STATUS
CollectActiveOptions(IN CONST EFI_BOOT_MANAGER_LOAD_OPTION * BootOptions,IN UINTN BootOptionCount,OUT ACTIVE_OPTION ** ActiveOption,OUT UINTN * Count)338 CollectActiveOptions (
339   IN   CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
340   IN   UINTN                              BootOptionCount,
341   OUT  ACTIVE_OPTION                      **ActiveOption,
342   OUT  UINTN                              *Count
343   )
344 {
345   UINTN Index;
346   UINTN ScanMode;
347 
348   *ActiveOption = NULL;
349 
350   //
351   // Scan the list twice:
352   // - count active entries,
353   // - store links to active entries.
354   //
355   for (ScanMode = 0; ScanMode < 2; ++ScanMode) {
356     *Count = 0;
357     for (Index = 0; Index < BootOptionCount; Index++) {
358       if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {
359         if (ScanMode == 1) {
360           (*ActiveOption)[*Count].BootOption = &BootOptions[Index];
361           (*ActiveOption)[*Count].Appended   = FALSE;
362         }
363         ++*Count;
364       }
365     }
366 
367     if (ScanMode == 0) {
368       if (*Count == 0) {
369         return RETURN_NOT_FOUND;
370       }
371       *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);
372       if (*ActiveOption == NULL) {
373         return RETURN_OUT_OF_RESOURCES;
374       }
375     }
376   }
377   return RETURN_SUCCESS;
378 }
379 
380 
381 /**
382   OpenFirmware device path node
383 **/
384 typedef struct {
385   SUBSTRING DriverName;
386   SUBSTRING UnitAddress;
387   SUBSTRING DeviceArguments;
388 } OFW_NODE;
389 
390 
391 /**
392 
393   Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
394   structure, and advance in the input string.
395 
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):
398 
399     /driver-name@unit-address[:device-arguments][<LF>]
400 
401   A single trailing <LF> character is consumed but not returned. A trailing
402   <LF> or NUL character terminates the device path.
403 
404   The function relies on ASCII encoding.
405 
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.
411 
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.
417 
418                           If the call doesn't succeed, the contents of this
419                           structure is indeterminate.
420 
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.
426 
427 
428   @retval RETURN_SUCCESS            Parsing successful.
429 
430   @retval RETURN_NOT_FOUND          Parsing terminated. *Ptr was (and is)
431                                     pointing to an empty string.
432 
433   @retval RETURN_INVALID_PARAMETER  Parse error.
434 
435 **/
436 STATIC
437 RETURN_STATUS
ParseOfwNode(IN OUT CONST CHAR8 ** Ptr,OUT OFW_NODE * OfwNode,OUT BOOLEAN * IsFinal)438 ParseOfwNode (
439   IN OUT  CONST CHAR8 **Ptr,
440   OUT     OFW_NODE    *OfwNode,
441   OUT     BOOLEAN     *IsFinal
442   )
443 {
444   //
445   // A leading slash is expected. End of string is tolerated.
446   //
447   switch (**Ptr) {
448   case '\0':
449     return RETURN_NOT_FOUND;
450 
451   case '/':
452     ++*Ptr;
453     break;
454 
455   default:
456     return RETURN_INVALID_PARAMETER;
457   }
458 
459   //
460   // driver-name
461   //
462   OfwNode->DriverName.Ptr = *Ptr;
463   OfwNode->DriverName.Len = 0;
464   while (OfwNode->DriverName.Len < 32 &&
465          (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))
466          ) {
467     ++*Ptr;
468     ++OfwNode->DriverName.Len;
469   }
470 
471   if (OfwNode->DriverName.Len == 0 || OfwNode->DriverName.Len == 32) {
472     return RETURN_INVALID_PARAMETER;
473   }
474 
475 
476   //
477   // unit-address
478   //
479   if (**Ptr != '@') {
480     return RETURN_INVALID_PARAMETER;
481   }
482   ++*Ptr;
483 
484   OfwNode->UnitAddress.Ptr = *Ptr;
485   OfwNode->UnitAddress.Len = 0;
486   while (IsPrintNotDelim (**Ptr)) {
487     ++*Ptr;
488     ++OfwNode->UnitAddress.Len;
489   }
490 
491   if (OfwNode->UnitAddress.Len == 0) {
492     return RETURN_INVALID_PARAMETER;
493   }
494 
495 
496   //
497   // device-arguments, may be omitted
498   //
499   OfwNode->DeviceArguments.Len = 0;
500   if (**Ptr == ':') {
501     ++*Ptr;
502     OfwNode->DeviceArguments.Ptr = *Ptr;
503 
504     while (IsPrintNotDelim (**Ptr)) {
505       ++*Ptr;
506       ++OfwNode->DeviceArguments.Len;
507     }
508 
509     if (OfwNode->DeviceArguments.Len == 0) {
510       return RETURN_INVALID_PARAMETER;
511     }
512   }
513   else {
514     OfwNode->DeviceArguments.Ptr = NULL;
515   }
516 
517   switch (**Ptr) {
518   case '\n':
519     ++*Ptr;
520     //
521     // fall through
522     //
523 
524   case '\0':
525     *IsFinal = TRUE;
526     break;
527 
528   case '/':
529     *IsFinal = FALSE;
530     break;
531 
532   default:
533     return RETURN_INVALID_PARAMETER;
534   }
535 
536   DEBUG ((
537     DEBUG_VERBOSE,
538     "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
539     __FUNCTION__,
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
544     ));
545   return RETURN_SUCCESS;
546 }
547 
548 
549 /**
550 
551   Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
552   fragment.
553 
554   @param[in]     OfwNode         Array of OpenFirmware device nodes to
555                                  translate, constituting the beginning of an
556                                  OpenFirmware device path.
557 
558   @param[in]     NumNodes        Number of elements in OfwNode.
559 
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
563                                  bus numbers.
564 
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.
569 
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.
575 
576 
577   @retval RETURN_SUCCESS           Translation successful.
578 
579   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
580                                    of bytes provided.
581 
582   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
583                                    be translated in the current implementation.
584 
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.
588 
589 **/
590 STATIC
591 RETURN_STATUS
TranslatePciOfwNodes(IN CONST OFW_NODE * OfwNode,IN UINTN NumNodes,IN CONST EXTRA_ROOT_BUS_MAP * ExtraPciRoots,OUT CHAR16 * Translated,IN OUT UINTN * TranslatedSize)592 TranslatePciOfwNodes (
593   IN      CONST OFW_NODE           *OfwNode,
594   IN      UINTN                    NumNodes,
595   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
596   OUT     CHAR16                   *Translated,
597   IN OUT  UINTN                    *TranslatedSize
598   )
599 {
600   UINT32 PciRoot;
601   CHAR8  *Comma;
602   UINTN  FirstNonBridge;
603   CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];
604   UINTN  BridgesLen;
605   UINT64 PciDevFun[2];
606   UINTN  NumEntries;
607   UINTN  Written;
608 
609   //
610   // Resolve the PCI root bus number.
611   //
612   // The initial OFW node for the main root bus (ie. bus number 0) is:
613   //
614   //   /pci@i0cf8
615   //
616   // For extra root buses, the initial OFW node is
617   //
618   //   /pci@i0cf8,4
619   //              ^
620   //              root bus serial number (not PCI bus number)
621   //
622   if (NumNodes < REQUIRED_PCI_OFW_NODES ||
623       !SubstringEq (OfwNode[0].DriverName, "pci")
624       ) {
625     return RETURN_UNSUPPORTED;
626   }
627 
628   PciRoot = 0;
629   Comma = ScanMem8 (OfwNode[0].UnitAddress.Ptr, OfwNode[0].UnitAddress.Len,
630             ',');
631   if (Comma != NULL) {
632     SUBSTRING PciRootSerialSubString;
633     UINT64    PciRootSerial;
634 
635     //
636     // Parse the root bus serial number from the unit address after the comma.
637     //
638     PciRootSerialSubString.Ptr = Comma + 1;
639     PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -
640                                  (PciRootSerialSubString.Ptr -
641                                   OfwNode[0].UnitAddress.Ptr);
642     NumEntries = 1;
643     if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString,
644                       &PciRootSerial, &NumEntries))) {
645       return RETURN_UNSUPPORTED;
646     }
647 
648     //
649     // Map the extra root bus's serial number to its actual bus number.
650     //
651     if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots, PciRootSerial,
652                      &PciRoot))) {
653       return RETURN_PROTOCOL_ERROR;
654     }
655   }
656 
657   //
658   // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
659   //
660   //   pci-bridge@1e[,0]
661   //              ^   ^
662   //              PCI slot & function on the parent, holding the bridge
663   //
664   // and the UEFI device path node is:
665   //
666   //   Pci(0x1E,0x0)
667   //
668   FirstNonBridge = 1;
669   Bridges[0] = L'\0';
670   BridgesLen = 0;
671   do {
672     UINT64 BridgeDevFun[2];
673     UINTN  BridgesFreeBytes;
674 
675     if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {
676       break;
677     }
678 
679     BridgeDevFun[1] = 0;
680     NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];
681     if (ParseUnitAddressHexList (OfwNode[FirstNonBridge].UnitAddress,
682           BridgeDevFun, &NumEntries) != RETURN_SUCCESS) {
683       return RETURN_UNSUPPORTED;
684     }
685 
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;
690 
691     //
692     // There's no way to differentiate between "completely used up without
693     // truncation" and "truncated", so treat the former as the latter.
694     //
695     if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {
696       return RETURN_UNSUPPORTED;
697     }
698 
699     ++FirstNonBridge;
700   } while (FirstNonBridge < NumNodes);
701 
702   if (FirstNonBridge == NumNodes) {
703     return RETURN_UNSUPPORTED;
704   }
705 
706   //
707   // Parse the OFW nodes starting with the first non-bridge node.
708   //
709   PciDevFun[1] = 0;
710   NumEntries = ARRAY_SIZE (PciDevFun);
711   if (ParseUnitAddressHexList (
712         OfwNode[FirstNonBridge].UnitAddress,
713         PciDevFun,
714         &NumEntries
715         ) != RETURN_SUCCESS
716       ) {
717     return RETURN_UNSUPPORTED;
718   }
719 
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")
724       ) {
725     //
726     // OpenFirmware device path (IDE disk, IDE CD-ROM):
727     //
728     //   /pci@i0cf8/ide@1,1/drive@0/disk@0
729     //        ^         ^ ^       ^      ^
730     //        |         | |       |      master or slave
731     //        |         | |       primary or secondary
732     //        |         PCI slot & function holding IDE controller
733     //        PCI root at system bus port, PIO
734     //
735     // UEFI device path:
736     //
737     //   PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
738     //                                                ^
739     //                                                fixed LUN
740     //
741     UINT64 Secondary;
742     UINT64 Slave;
743 
744     NumEntries = 1;
745     if (ParseUnitAddressHexList (
746           OfwNode[FirstNonBridge + 1].UnitAddress,
747           &Secondary,
748           &NumEntries
749           ) != RETURN_SUCCESS ||
750         Secondary > 1 ||
751         ParseUnitAddressHexList (
752           OfwNode[FirstNonBridge + 2].UnitAddress,
753           &Slave,
754           &NumEntries // reuse after previous single-element call
755           ) != RETURN_SUCCESS ||
756         Slave > 1
757         ) {
758       return RETURN_UNSUPPORTED;
759     }
760 
761     Written = UnicodeSPrintAsciiFormat (
762       Translated,
763       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
764       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
765       PciRoot,
766       Bridges,
767       PciDevFun[0],
768       PciDevFun[1],
769       Secondary ? "Secondary" : "Primary",
770       Slave ? "Slave" : "Master"
771       );
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")
776       ) {
777     //
778     // OpenFirmware device path (Q35 SATA disk and CD-ROM):
779     //
780     //   /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
781     //        ^                  ^  ^       ^      ^
782     //        |                  |  |       |      device number (fixed 0)
783     //        |                  |  |       channel (port) number
784     //        |                  PCI slot & function holding SATA HBA
785     //        PCI root at system bus port, PIO
786     //
787     // UEFI device path:
788     //
789     //   PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
790     //                                   ^   ^      ^
791     //                                   |   |      LUN (always 0 on Q35)
792     //                                   |   port multiplier port number,
793     //                                   |   always 0xFFFF on Q35
794     //                                   channel (port) number
795     //
796     UINT64 Channel;
797 
798     NumEntries = 1;
799     if (RETURN_ERROR (ParseUnitAddressHexList (
800                         OfwNode[FirstNonBridge + 1].UnitAddress, &Channel,
801                         &NumEntries))) {
802       return RETURN_UNSUPPORTED;
803     }
804 
805     Written = UnicodeSPrintAsciiFormat (
806       Translated,
807       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
808       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
809       PciRoot,
810       Bridges,
811       PciDevFun[0],
812       PciDevFun[1],
813       Channel
814       );
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")
819              ) {
820     //
821     // OpenFirmware device path (floppy disk):
822     //
823     //   /pci@i0cf8/isa@1/fdc@03f0/floppy@0
824     //        ^         ^     ^           ^
825     //        |         |     |           A: or B:
826     //        |         |     ISA controller io-port (hex)
827     //        |         PCI slot holding ISA controller
828     //        PCI root at system bus port, PIO
829     //
830     // UEFI device path:
831     //
832     //   PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
833     //                                    ^
834     //                                    ACPI UID
835     //
836     UINT64 AcpiUid;
837 
838     NumEntries = 1;
839     if (ParseUnitAddressHexList (
840           OfwNode[FirstNonBridge + 2].UnitAddress,
841           &AcpiUid,
842           &NumEntries
843           ) != RETURN_SUCCESS ||
844         AcpiUid > 1
845         ) {
846       return RETURN_UNSUPPORTED;
847     }
848 
849     Written = UnicodeSPrintAsciiFormat (
850       Translated,
851       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
852       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
853       PciRoot,
854       Bridges,
855       PciDevFun[0],
856       PciDevFun[1],
857       AcpiUid
858       );
859   } else if (NumNodes >= FirstNonBridge + 2 &&
860              SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
861              SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")
862              ) {
863     //
864     // OpenFirmware device path (virtio-blk disk):
865     //
866     //   /pci@i0cf8/scsi@6[,3]/disk@0,0
867     //        ^          ^  ^       ^ ^
868     //        |          |  |       fixed
869     //        |          |  PCI function corresponding to disk (optional)
870     //        |          PCI slot holding disk
871     //        PCI root at system bus port, PIO
872     //
873     // UEFI device path prefix:
874     //
875     //   PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
876     //   PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
877     //
878     Written = UnicodeSPrintAsciiFormat (
879       Translated,
880       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
881       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
882       PciRoot,
883       Bridges,
884       PciDevFun[0],
885       PciDevFun[1]
886       );
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")
891              ) {
892     //
893     // OpenFirmware device path (virtio-scsi disk):
894     //
895     //   /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
896     //        ^          ^             ^      ^ ^
897     //        |          |             |      | LUN
898     //        |          |             |      target
899     //        |          |             channel (unused, fixed 0)
900     //        |          PCI slot[, function] holding SCSI controller
901     //        PCI root at system bus port, PIO
902     //
903     // UEFI device path prefix:
904     //
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
909     //
910     UINT64 TargetLun[2];
911 
912     TargetLun[1] = 0;
913     NumEntries = ARRAY_SIZE (TargetLun);
914     if (ParseUnitAddressHexList (
915           OfwNode[FirstNonBridge + 2].UnitAddress,
916           TargetLun,
917           &NumEntries
918           ) != RETURN_SUCCESS
919         ) {
920       return RETURN_UNSUPPORTED;
921     }
922 
923     Written = UnicodeSPrintAsciiFormat (
924       Translated,
925       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
926       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
927       PciRoot,
928       Bridges,
929       PciDevFun[0],
930       PciDevFun[1],
931       TargetLun[0],
932       TargetLun[1]
933       );
934   } else if (NumNodes >= FirstNonBridge + 2 &&
935       SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&
936       SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")
937       ) {
938     //
939     // OpenFirmware device path (NVMe device):
940     //
941     //   /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
942     //        ^                  ^  ^            ^ ^
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
948     //
949     // UEFI device path:
950     //
951     //   PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
952     //                                  ^   ^
953     //                                  |   octets of the EUI-64
954     //                                  |   in address order
955     //                                  namespace ID
956     //
957     UINT64 Namespace[2];
958     UINTN  RequiredEntries;
959     UINT8  *Eui64;
960 
961     RequiredEntries = ARRAY_SIZE (Namespace);
962     NumEntries = RequiredEntries;
963     if (ParseUnitAddressHexList (
964           OfwNode[FirstNonBridge + 1].UnitAddress,
965           Namespace,
966           &NumEntries
967           ) != RETURN_SUCCESS ||
968         NumEntries != RequiredEntries ||
969         Namespace[0] == 0 ||
970         Namespace[0] >= MAX_UINT32
971         ) {
972       return RETURN_UNSUPPORTED;
973     }
974 
975     Eui64 = (UINT8 *)&Namespace[1];
976     Written = UnicodeSPrintAsciiFormat (
977       Translated,
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)",
981       PciRoot,
982       Bridges,
983       PciDevFun[0],
984       PciDevFun[1],
985       Namespace[0],
986       Eui64[7], Eui64[6], Eui64[5], Eui64[4],
987       Eui64[3], Eui64[2], Eui64[1], Eui64[0]
988       );
989   } else {
990     //
991     // Generic OpenFirmware device path for PCI devices:
992     //
993     //   /pci@i0cf8/ethernet@3[,2]
994     //        ^              ^
995     //        |              PCI slot[, function] holding Ethernet card
996     //        PCI root at system bus port, PIO
997     //
998     // UEFI device path prefix (dependent on presence of nonzero PCI function):
999     //
1000     //   PciRoot(0x0)/Pci(0x3,0x0)
1001     //   PciRoot(0x0)/Pci(0x3,0x2)
1002     //
1003     Written = UnicodeSPrintAsciiFormat (
1004       Translated,
1005       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1006       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
1007       PciRoot,
1008       Bridges,
1009       PciDevFun[0],
1010       PciDevFun[1]
1011       );
1012   }
1013 
1014   //
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".
1018   //
1019   if (Written + 1 < *TranslatedSize) {
1020     *TranslatedSize = Written;
1021     return RETURN_SUCCESS;
1022   }
1023 
1024   return RETURN_BUFFER_TOO_SMALL;
1025 }
1026 
1027 
1028 //
1029 // A type providing easy raw access to the base address of a virtio-mmio
1030 // transport.
1031 //
1032 typedef union {
1033   UINT64 Uint64;
1034   UINT8  Raw[8];
1035 } VIRTIO_MMIO_BASE_ADDRESS;
1036 
1037 
1038 /**
1039 
1040   Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
1041   path fragment.
1042 
1043   @param[in]     OfwNode         Array of OpenFirmware device nodes to
1044                                  translate, constituting the beginning of an
1045                                  OpenFirmware device path.
1046 
1047   @param[in]     NumNodes        Number of elements in OfwNode.
1048 
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.
1053 
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.
1059 
1060 
1061   @retval RETURN_SUCCESS           Translation successful.
1062 
1063   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
1064                                    of bytes provided.
1065 
1066   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
1067                                    be translated in the current implementation.
1068 
1069 **/
1070 STATIC
1071 RETURN_STATUS
TranslateMmioOfwNodes(IN CONST OFW_NODE * OfwNode,IN UINTN NumNodes,OUT CHAR16 * Translated,IN OUT UINTN * TranslatedSize)1072 TranslateMmioOfwNodes (
1073   IN      CONST OFW_NODE *OfwNode,
1074   IN      UINTN          NumNodes,
1075   OUT     CHAR16         *Translated,
1076   IN OUT  UINTN          *TranslatedSize
1077   )
1078 {
1079   VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;
1080   CHAR16                   VenHwString[60 + 1];
1081   UINTN                    NumEntries;
1082   UINTN                    Written;
1083 
1084   //
1085   // Get the base address of the virtio-mmio transport.
1086   //
1087   if (NumNodes < REQUIRED_MMIO_OFW_NODES ||
1088       !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")
1089       ) {
1090     return RETURN_UNSUPPORTED;
1091   }
1092   NumEntries = 1;
1093   if (ParseUnitAddressHexList (
1094         OfwNode[0].UnitAddress,
1095         &VirtioMmioBase.Uint64,
1096         &NumEntries
1097         ) != RETURN_SUCCESS
1098       ) {
1099     return RETURN_UNSUPPORTED;
1100   }
1101 
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]);
1107 
1108   if (NumNodes >= 2 &&
1109       SubstringEq (OfwNode[1].DriverName, "disk")) {
1110     //
1111     // OpenFirmware device path (virtio-blk disk):
1112     //
1113     //   /virtio-mmio@000000000a003c00/disk@0,0
1114     //                ^                     ^ ^
1115     //                |                     fixed
1116     //                base address of virtio-mmio register block
1117     //
1118     // UEFI device path prefix:
1119     //
1120     //   <VenHwString>
1121     //
1122     Written = UnicodeSPrintAsciiFormat (
1123                 Translated,
1124                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1125                 "%s",
1126                 VenHwString
1127                 );
1128   } else if (NumNodes >= 3 &&
1129              SubstringEq (OfwNode[1].DriverName, "channel") &&
1130              SubstringEq (OfwNode[2].DriverName, "disk")) {
1131     //
1132     // OpenFirmware device path (virtio-scsi disk):
1133     //
1134     //   /virtio-mmio@000000000a003a00/channel@0/disk@2,3
1135     //                ^                        ^      ^ ^
1136     //                |                        |      | LUN
1137     //                |                        |      target
1138     //                |                        channel (unused, fixed 0)
1139     //                base address of virtio-mmio register block
1140     //
1141     // UEFI device path prefix:
1142     //
1143     //   <VenHwString>/Scsi(0x2,0x3)
1144     //
1145     UINT64 TargetLun[2];
1146 
1147     TargetLun[1] = 0;
1148     NumEntries = ARRAY_SIZE (TargetLun);
1149     if (ParseUnitAddressHexList (
1150           OfwNode[2].UnitAddress,
1151           TargetLun,
1152           &NumEntries
1153           ) != RETURN_SUCCESS
1154         ) {
1155       return RETURN_UNSUPPORTED;
1156     }
1157 
1158     Written = UnicodeSPrintAsciiFormat (
1159                 Translated,
1160                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1161                 "%s/Scsi(0x%Lx,0x%Lx)",
1162                 VenHwString,
1163                 TargetLun[0],
1164                 TargetLun[1]
1165                 );
1166   } else if (NumNodes >= 2 &&
1167              SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {
1168     //
1169     // OpenFirmware device path (virtio-net NIC):
1170     //
1171     //   /virtio-mmio@000000000a003e00/ethernet-phy@0
1172     //                ^                             ^
1173     //                |                             fixed
1174     //                base address of virtio-mmio register block
1175     //
1176     // UEFI device path prefix (dependent on presence of nonzero PCI function):
1177     //
1178     //   <VenHwString>/MAC(
1179     //
1180     Written = UnicodeSPrintAsciiFormat (
1181                 Translated,
1182                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1183                 "%s/MAC(",
1184                 VenHwString
1185                 );
1186   } else {
1187     return RETURN_UNSUPPORTED;
1188   }
1189 
1190   //
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".
1194   //
1195   if (Written + 1 < *TranslatedSize) {
1196     *TranslatedSize = Written;
1197     return RETURN_SUCCESS;
1198   }
1199 
1200   return RETURN_BUFFER_TOO_SMALL;
1201 }
1202 
1203 
1204 /**
1205 
1206   Translate an array of OpenFirmware device nodes to a UEFI device path
1207   fragment.
1208 
1209   @param[in]     OfwNode         Array of OpenFirmware device nodes to
1210                                  translate, constituting the beginning of an
1211                                  OpenFirmware device path.
1212 
1213   @param[in]     NumNodes        Number of elements in OfwNode.
1214 
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
1218                                  bus numbers.
1219 
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.
1224 
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.
1230 
1231 
1232   @retval RETURN_SUCCESS           Translation successful.
1233 
1234   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
1235                                    of bytes provided.
1236 
1237   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
1238                                    be translated in the current implementation.
1239 
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.
1243 
1244 **/
1245 STATIC
1246 RETURN_STATUS
TranslateOfwNodes(IN CONST OFW_NODE * OfwNode,IN UINTN NumNodes,IN CONST EXTRA_ROOT_BUS_MAP * ExtraPciRoots,OUT CHAR16 * Translated,IN OUT UINTN * TranslatedSize)1247 TranslateOfwNodes (
1248   IN      CONST OFW_NODE           *OfwNode,
1249   IN      UINTN                    NumNodes,
1250   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
1251   OUT     CHAR16                   *Translated,
1252   IN OUT  UINTN                    *TranslatedSize
1253   )
1254 {
1255   RETURN_STATUS Status;
1256 
1257   Status = RETURN_UNSUPPORTED;
1258 
1259   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
1260     Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,
1261                Translated, TranslatedSize);
1262   }
1263   if (Status == RETURN_UNSUPPORTED &&
1264       FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {
1265     Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,
1266                TranslatedSize);
1267   }
1268   return Status;
1269 }
1270 
1271 /**
1272 
1273   Translate an OpenFirmware device path fragment to a UEFI device path
1274   fragment, and advance in the input string.
1275 
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.
1284 
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
1288                                  bus numbers.
1289 
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.
1294 
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.
1300 
1301 
1302   @retval RETURN_SUCCESS            Translation successful.
1303 
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
1308                                     possible.
1309 
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.
1314 
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
1319                                     possible.
1320 
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
1325                                     present).
1326 
1327   @retval RETURN_INVALID_PARAMETER  Parse error. This is a permanent error.
1328 
1329 **/
1330 STATIC
1331 RETURN_STATUS
TranslateOfwPath(IN OUT CONST CHAR8 ** Ptr,IN CONST EXTRA_ROOT_BUS_MAP * ExtraPciRoots,OUT CHAR16 * Translated,IN OUT UINTN * TranslatedSize)1332 TranslateOfwPath (
1333   IN OUT  CONST CHAR8              **Ptr,
1334   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
1335   OUT     CHAR16                   *Translated,
1336   IN OUT  UINTN                    *TranslatedSize
1337   )
1338 {
1339   UINTN         NumNodes;
1340   RETURN_STATUS Status;
1341   OFW_NODE      Node[EXAMINED_OFW_NODES];
1342   BOOLEAN       IsFinal;
1343   OFW_NODE      Skip;
1344 
1345   IsFinal = FALSE;
1346   NumNodes = 0;
1347   if (AsciiStrCmp (*Ptr, "HALT") == 0) {
1348     *Ptr += 4;
1349     Status = RETURN_NOT_FOUND;
1350   } else {
1351     Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);
1352   }
1353 
1354   if (Status == RETURN_NOT_FOUND) {
1355     DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));
1356     return RETURN_NOT_FOUND;
1357   }
1358 
1359   while (Status == RETURN_SUCCESS && !IsFinal) {
1360     ++NumNodes;
1361     Status = ParseOfwNode (
1362                Ptr,
1363                (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,
1364                &IsFinal
1365                );
1366   }
1367 
1368   switch (Status) {
1369   case RETURN_SUCCESS:
1370     ++NumNodes;
1371     break;
1372 
1373   case RETURN_INVALID_PARAMETER:
1374     DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));
1375     return RETURN_INVALID_PARAMETER;
1376 
1377   default:
1378     ASSERT (0);
1379   }
1380 
1381   Status = TranslateOfwNodes (
1382              Node,
1383              NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,
1384              ExtraPciRoots,
1385              Translated,
1386              TranslatedSize);
1387   switch (Status) {
1388   case RETURN_SUCCESS:
1389     DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));
1390     break;
1391 
1392   case RETURN_BUFFER_TOO_SMALL:
1393     DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));
1394     break;
1395 
1396   case RETURN_UNSUPPORTED:
1397     DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));
1398     break;
1399 
1400   case RETURN_PROTOCOL_ERROR:
1401     DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",
1402       __FUNCTION__));
1403     break;
1404 
1405   default:
1406     ASSERT (0);
1407   }
1408   return Status;
1409 }
1410 
1411 
1412 /**
1413 
1414   Convert the UEFI DevicePath to full text representation with DevPathToText,
1415   then match the UEFI device path fragment in Translated against it.
1416 
1417   @param[in] Translated        UEFI device path fragment, translated from
1418                                OpenFirmware format, to search for.
1419 
1420   @param[in] TranslatedLength  The length of Translated in CHAR16's.
1421 
1422   @param[in] DevicePath        Boot option device path whose textual rendering
1423                                to search in.
1424 
1425   @param[in] DevPathToText  Binary-to-text conversion protocol for DevicePath.
1426 
1427 
1428   @retval TRUE   If Translated was found at the beginning of DevicePath after
1429                  converting the latter to text.
1430 
1431   @retval FALSE  If DevicePath was NULL, or it could not be converted, or there
1432                  was no match.
1433 
1434 **/
1435 STATIC
1436 BOOLEAN
Match(IN CONST CHAR16 * Translated,IN UINTN TranslatedLength,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)1437 Match (
1438   IN  CONST CHAR16                           *Translated,
1439   IN  UINTN                                  TranslatedLength,
1440   IN  EFI_DEVICE_PATH_PROTOCOL               *DevicePath
1441   )
1442 {
1443   CHAR16                   *Converted;
1444   BOOLEAN                  Result;
1445   VOID                     *FileBuffer;
1446   UINTN                    FileSize;
1447   EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;
1448   CHAR16                   *AbsConverted;
1449   BOOLEAN                  Shortform;
1450   EFI_DEVICE_PATH_PROTOCOL *Node;
1451 
1452   Converted = ConvertDevicePathToText (
1453                 DevicePath,
1454                 FALSE, // DisplayOnly
1455                 FALSE  // AllowShortcuts
1456                 );
1457   if (Converted == NULL) {
1458     return FALSE;
1459   }
1460 
1461   Result = FALSE;
1462   Shortform = FALSE;
1463   //
1464   // Expand the short-form device path to full device path
1465   //
1466   if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
1467       (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {
1468     //
1469     // Harddrive shortform device path
1470     //
1471     Shortform = TRUE;
1472   } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
1473              (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {
1474     //
1475     // File-path shortform device path
1476     //
1477     Shortform = TRUE;
1478   } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
1479              (DevicePathSubType (DevicePath) == MSG_URI_DP)) {
1480     //
1481     // URI shortform device path
1482     //
1483     Shortform = TRUE;
1484   } else {
1485     for ( Node = DevicePath
1486         ; !IsDevicePathEnd (Node)
1487         ; Node = NextDevicePathNode (Node)
1488         ) {
1489       if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
1490           ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||
1491            (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {
1492         Shortform = TRUE;
1493         break;
1494       }
1495     }
1496   }
1497 
1498   //
1499   // Attempt to expand any relative UEFI device path to
1500   // an absolute device path first.
1501   //
1502   if (Shortform) {
1503     FileBuffer = EfiBootManagerGetLoadOptionBuffer (
1504                    DevicePath, &AbsDevicePath, &FileSize
1505                    );
1506     if (FileBuffer == NULL) {
1507       goto Exit;
1508     }
1509     FreePool (FileBuffer);
1510     AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);
1511     FreePool (AbsDevicePath);
1512     if (AbsConverted == NULL) {
1513       goto Exit;
1514     }
1515     DEBUG ((DEBUG_VERBOSE,
1516       "%a: expanded relative device path \"%s\" for prefix matching\n",
1517       __FUNCTION__, Converted));
1518     FreePool (Converted);
1519     Converted = AbsConverted;
1520   }
1521 
1522   //
1523   // Is Translated a prefix of Converted?
1524   //
1525   Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
1526   DEBUG ((
1527     DEBUG_VERBOSE,
1528     "%a: against \"%s\": %a\n",
1529     __FUNCTION__,
1530     Converted,
1531     Result ? "match" : "no match"
1532     ));
1533 Exit:
1534   FreePool (Converted);
1535   return Result;
1536 }
1537 
1538 
1539 /**
1540   Append some of the unselected active boot options to the boot order.
1541 
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.
1545 
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.
1550 
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
1555                                BootOrderAppend().
1556 
1557   @param[in]     ActiveCount   Number of elements in ActiveOption.
1558 
1559 
1560   @retval RETURN_SUCCESS  BootOrder has been extended with any eligible boot
1561                           options.
1562 
1563   @return                 Error codes returned by BootOrderAppend().
1564 **/
1565 STATIC
1566 RETURN_STATUS
BootOrderComplete(IN OUT BOOT_ORDER * BootOrder,IN OUT ACTIVE_OPTION * ActiveOption,IN UINTN ActiveCount)1567 BootOrderComplete (
1568   IN OUT  BOOT_ORDER    *BootOrder,
1569   IN OUT  ACTIVE_OPTION *ActiveOption,
1570   IN      UINTN         ActiveCount
1571   )
1572 {
1573   RETURN_STATUS Status;
1574   UINTN         Idx;
1575 
1576   Status = RETURN_SUCCESS;
1577   Idx = 0;
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;
1582 
1583       Current = ActiveOption[Idx].BootOption;
1584       FirstNode = Current->FilePath;
1585       if (FirstNode != NULL) {
1586         CHAR16        *Converted;
1587         STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";
1588         BOOLEAN       Keep;
1589 
1590         Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);
1591         if (Converted == NULL) {
1592           Converted = ConvFallBack;
1593         }
1594 
1595         Keep = TRUE;
1596         if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&
1597             DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {
1598           //
1599           // drop HD()
1600           //
1601           Keep = FALSE;
1602         } else if (DevicePathType(FirstNode) == ACPI_DEVICE_PATH &&
1603                    DevicePathSubType(FirstNode) == ACPI_DP) {
1604           ACPI_HID_DEVICE_PATH *Acpi;
1605 
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) {
1609             //
1610             // drop PciRoot() if we enabled the user to select PCI-like boot
1611             // options, by providing translation for such OFW device path
1612             // fragments
1613             //
1614             Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);
1615           }
1616         } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&
1617                    DevicePathSubType(FirstNode) == HW_VENDOR_DP) {
1618           VENDOR_DEVICE_PATH *VenHw;
1619 
1620           VenHw = (VENDOR_DEVICE_PATH *)FirstNode;
1621           if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {
1622             //
1623             // drop virtio-mmio if we enabled the user to select boot options
1624             // referencing such device paths
1625             //
1626             Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);
1627           }
1628         }
1629 
1630         if (Keep) {
1631           Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);
1632           if (!RETURN_ERROR (Status)) {
1633             DEBUG ((DEBUG_VERBOSE, "%a: keeping \"%s\"\n", __FUNCTION__,
1634               Converted));
1635           }
1636         } else {
1637           DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,
1638             Converted));
1639         }
1640 
1641         if (Converted != ConvFallBack) {
1642           FreePool (Converted);
1643         }
1644       }
1645     }
1646     ++Idx;
1647   }
1648   return Status;
1649 }
1650 
1651 
1652 /**
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
1655   policy").
1656 
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.
1660 
1661   @param[in]  ActiveCount   Number of elements in ActiveOption.
1662 **/
1663 STATIC
1664 VOID
PruneBootVariables(IN CONST ACTIVE_OPTION * ActiveOption,IN UINTN ActiveCount)1665 PruneBootVariables (
1666   IN  CONST ACTIVE_OPTION *ActiveOption,
1667   IN  UINTN               ActiveCount
1668   )
1669 {
1670   UINTN Idx;
1671 
1672   for (Idx = 0; Idx < ActiveCount; ++Idx) {
1673     if (!ActiveOption[Idx].Appended) {
1674       CHAR16 VariableName[9];
1675 
1676       UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",
1677         ActiveOption[Idx].BootOption->OptionNumber);
1678 
1679       //
1680       // "The space consumed by the deleted variable may not be available until
1681       // the next power cycle", but that's good enough.
1682       //
1683       gRT->SetVariable (VariableName, &gEfiGlobalVariableGuid,
1684              0,   // Attributes, 0 means deletion
1685              0,   // DataSize, 0 means deletion
1686              NULL // Data
1687              );
1688     }
1689   }
1690 }
1691 
1692 
1693 /**
1694 
1695   Set the boot order based on configuration retrieved from QEMU.
1696 
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 the current list of boot options, and rewrite
1700   the BootOrder NvVar so that it corresponds to the order described in fw_cfg.
1701 
1702   Platform BDS should call this function after EfiBootManagerConnectAll () and
1703   EfiBootManagerRefreshAllBootOption () return.
1704 
1705   @retval RETURN_SUCCESS            BootOrder NvVar rewritten.
1706 
1707   @retval RETURN_UNSUPPORTED        QEMU's fw_cfg is not supported.
1708 
1709   @retval RETURN_NOT_FOUND          Empty or nonexistent "bootorder" fw_cfg
1710                                     file, or no match found between the
1711                                     "bootorder" fw_cfg file and BootOptionList.
1712 
1713   @retval RETURN_INVALID_PARAMETER  Parse error in the "bootorder" fw_cfg file.
1714 
1715   @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.
1716 
1717   @return                           Values returned by gBS->LocateProtocol ()
1718                                     or gRT->SetVariable ().
1719 
1720 **/
1721 RETURN_STATUS
SetBootOrderFromQemu(VOID)1722 SetBootOrderFromQemu (
1723   VOID
1724   )
1725 {
1726   RETURN_STATUS                    Status;
1727   FIRMWARE_CONFIG_ITEM             FwCfgItem;
1728   UINTN                            FwCfgSize;
1729   CHAR8                            *FwCfg;
1730   CONST CHAR8                      *FwCfgPtr;
1731 
1732   BOOT_ORDER                       BootOrder;
1733   ACTIVE_OPTION                    *ActiveOption;
1734   UINTN                            ActiveCount;
1735 
1736   EXTRA_ROOT_BUS_MAP               *ExtraPciRoots;
1737 
1738   UINTN                            TranslatedSize;
1739   CHAR16                           Translated[TRANSLATION_OUTPUT_SIZE];
1740   EFI_BOOT_MANAGER_LOAD_OPTION     *BootOptions;
1741   UINTN                            BootOptionCount;
1742 
1743   Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
1744   if (Status != RETURN_SUCCESS) {
1745     return Status;
1746   }
1747 
1748   if (FwCfgSize == 0) {
1749     return RETURN_NOT_FOUND;
1750   }
1751 
1752   FwCfg = AllocatePool (FwCfgSize);
1753   if (FwCfg == NULL) {
1754     return RETURN_OUT_OF_RESOURCES;
1755   }
1756 
1757   QemuFwCfgSelectItem (FwCfgItem);
1758   QemuFwCfgReadBytes (FwCfgSize, FwCfg);
1759   if (FwCfg[FwCfgSize - 1] != '\0') {
1760     Status = RETURN_INVALID_PARAMETER;
1761     goto ErrorFreeFwCfg;
1762   }
1763 
1764   DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
1765   DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
1766   DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
1767   FwCfgPtr = FwCfg;
1768 
1769   BootOrder.Produced  = 0;
1770   BootOrder.Allocated = 1;
1771   BootOrder.Data = AllocatePool (
1772                      BootOrder.Allocated * sizeof (*BootOrder.Data)
1773                      );
1774   if (BootOrder.Data == NULL) {
1775     Status = RETURN_OUT_OF_RESOURCES;
1776     goto ErrorFreeFwCfg;
1777   }
1778 
1779   BootOptions = EfiBootManagerGetLoadOptions (
1780                   &BootOptionCount, LoadOptionTypeBoot
1781                   );
1782   if (BootOptions == NULL) {
1783     Status = RETURN_NOT_FOUND;
1784     goto ErrorFreeBootOrder;
1785   }
1786 
1787   Status = CollectActiveOptions (
1788              BootOptions, BootOptionCount, &ActiveOption, &ActiveCount
1789              );
1790   if (RETURN_ERROR (Status)) {
1791     goto ErrorFreeBootOptions;
1792   }
1793 
1794   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
1795     Status = CreateExtraRootBusMap (&ExtraPciRoots);
1796     if (EFI_ERROR (Status)) {
1797       goto ErrorFreeActiveOption;
1798     }
1799   } else {
1800     ExtraPciRoots = NULL;
1801   }
1802 
1803   //
1804   // translate each OpenFirmware path
1805   //
1806   TranslatedSize = ARRAY_SIZE (Translated);
1807   Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
1808              &TranslatedSize);
1809   while (Status == RETURN_SUCCESS ||
1810          Status == RETURN_UNSUPPORTED ||
1811          Status == RETURN_PROTOCOL_ERROR ||
1812          Status == RETURN_BUFFER_TOO_SMALL) {
1813     if (Status == RETURN_SUCCESS) {
1814       UINTN Idx;
1815 
1816       //
1817       // match translated OpenFirmware path against all active boot options
1818       //
1819       for (Idx = 0; Idx < ActiveCount; ++Idx) {
1820         if (Match (
1821               Translated,
1822               TranslatedSize, // contains length, not size, in CHAR16's here
1823               ActiveOption[Idx].BootOption->FilePath
1824               )
1825             ) {
1826           //
1827           // match found, store ID and continue with next OpenFirmware path
1828           //
1829           Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
1830           if (Status != RETURN_SUCCESS) {
1831             goto ErrorFreeExtraPciRoots;
1832           }
1833           break;
1834         }
1835       } // scanned all active boot options
1836     }   // translation successful
1837 
1838     TranslatedSize = ARRAY_SIZE (Translated);
1839     Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
1840                &TranslatedSize);
1841   } // scanning of OpenFirmware paths done
1842 
1843   if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {
1844     //
1845     // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
1846     // Some of the active boot options that have not been selected over fw_cfg
1847     // should be preserved at the end of the boot order.
1848     //
1849     Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
1850     if (RETURN_ERROR (Status)) {
1851       goto ErrorFreeExtraPciRoots;
1852     }
1853 
1854     //
1855     // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
1856     // attributes.
1857     //
1858     Status = gRT->SetVariable (
1859                     L"BootOrder",
1860                     &gEfiGlobalVariableGuid,
1861                     EFI_VARIABLE_NON_VOLATILE |
1862                       EFI_VARIABLE_BOOTSERVICE_ACCESS |
1863                       EFI_VARIABLE_RUNTIME_ACCESS,
1864                     BootOrder.Produced * sizeof (*BootOrder.Data),
1865                     BootOrder.Data
1866                     );
1867     if (EFI_ERROR (Status)) {
1868       DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));
1869       goto ErrorFreeExtraPciRoots;
1870     }
1871 
1872     DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));
1873     PruneBootVariables (ActiveOption, ActiveCount);
1874   }
1875 
1876 ErrorFreeExtraPciRoots:
1877   if (ExtraPciRoots != NULL) {
1878     DestroyExtraRootBusMap (ExtraPciRoots);
1879   }
1880 
1881 ErrorFreeActiveOption:
1882   FreePool (ActiveOption);
1883 
1884 ErrorFreeBootOptions:
1885   EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
1886 
1887 ErrorFreeBootOrder:
1888   FreePool (BootOrder.Data);
1889 
1890 ErrorFreeFwCfg:
1891   FreePool (FwCfg);
1892 
1893   return Status;
1894 }
1895 
1896 
1897 /**
1898   Calculate the number of seconds we should be showing the FrontPage progress
1899   bar for.
1900 
1901   @return  The TimeoutDefault argument for PlatformBdsEnterFrontPage().
1902 **/
1903 UINT16
GetFrontPageTimeoutFromQemu(VOID)1904 GetFrontPageTimeoutFromQemu (
1905   VOID
1906   )
1907 {
1908   FIRMWARE_CONFIG_ITEM BootMenuWaitItem;
1909   UINTN                BootMenuWaitSize;
1910 
1911   QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);
1912   if (QemuFwCfgRead16 () == 0) {
1913     //
1914     // The user specified "-boot menu=off", or didn't specify "-boot
1915     // menu=(on|off)" at all. Return the platform default.
1916     //
1917     return PcdGet16 (PcdPlatformBootTimeOut);
1918   }
1919 
1920   if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,
1921                       &BootMenuWaitSize)) ||
1922       BootMenuWaitSize != sizeof (UINT16)) {
1923     //
1924     // "-boot menu=on" was specified without "splash-time=N". In this case,
1925     // return three seconds if the platform default would cause us to skip the
1926     // front page, and return the platform default otherwise.
1927     //
1928     UINT16 Timeout;
1929 
1930     Timeout = PcdGet16 (PcdPlatformBootTimeOut);
1931     if (Timeout == 0) {
1932       Timeout = 3;
1933     }
1934     return Timeout;
1935   }
1936 
1937   //
1938   // "-boot menu=on,splash-time=N" was specified, where N is in units of
1939   // milliseconds. The Intel BDS Front Page progress bar only supports whole
1940   // seconds, round N up.
1941   //
1942   QemuFwCfgSelectItem (BootMenuWaitItem);
1943   return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);
1944 }
1945