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