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, 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/GenericBdsLib.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 BDS_COMMON_OPTION *BootOption; // reference only, no ownership
257   BOOLEAN                 Appended;    // has been added to a BOOT_ORDER?
258 } ACTIVE_OPTION;
259 
260 
261 /**
262 
263   Append an active boot option to BootOrder, reallocating the latter if needed.
264 
265   @param[in out] BootOrder     The structure pointing to the array and holding
266                                allocation and usage counters.
267 
268   @param[in]     ActiveOption  The active boot option whose ID should be
269                                appended to the array.
270 
271 
272   @retval RETURN_SUCCESS           ID of ActiveOption appended.
273 
274   @retval RETURN_OUT_OF_RESOURCES  Memory reallocation failed.
275 
276 **/
277 STATIC
278 RETURN_STATUS
BootOrderAppend(IN OUT BOOT_ORDER * BootOrder,IN OUT ACTIVE_OPTION * ActiveOption)279 BootOrderAppend (
280   IN OUT  BOOT_ORDER    *BootOrder,
281   IN OUT  ACTIVE_OPTION *ActiveOption
282   )
283 {
284   if (BootOrder->Produced == BootOrder->Allocated) {
285     UINTN  AllocatedNew;
286     UINT16 *DataNew;
287 
288     ASSERT (BootOrder->Allocated > 0);
289     AllocatedNew = BootOrder->Allocated * 2;
290     DataNew = ReallocatePool (
291                 BootOrder->Allocated * sizeof (*BootOrder->Data),
292                 AllocatedNew         * sizeof (*DataNew),
293                 BootOrder->Data
294                 );
295     if (DataNew == NULL) {
296       return RETURN_OUT_OF_RESOURCES;
297     }
298     BootOrder->Allocated = AllocatedNew;
299     BootOrder->Data      = DataNew;
300   }
301 
302   BootOrder->Data[BootOrder->Produced++] =
303                                          ActiveOption->BootOption->BootCurrent;
304   ActiveOption->Appended = TRUE;
305   return RETURN_SUCCESS;
306 }
307 
308 
309 /**
310 
311   Create an array of ACTIVE_OPTION elements for a boot option list.
312 
313   @param[in]  BootOptionList  A boot option list, created with
314                               BdsLibEnumerateAllBootOption().
315 
316   @param[out] ActiveOption    Pointer to the first element in the new array.
317                               The caller is responsible for freeing the array
318                               with FreePool() after use.
319 
320   @param[out] Count           Number of elements in the new array.
321 
322 
323   @retval RETURN_SUCCESS           The ActiveOption array has been created.
324 
325   @retval RETURN_NOT_FOUND         No active entry has been found in
326                                    BootOptionList.
327 
328   @retval RETURN_OUT_OF_RESOURCES  Memory allocation failed.
329 
330 **/
331 STATIC
332 RETURN_STATUS
CollectActiveOptions(IN CONST LIST_ENTRY * BootOptionList,OUT ACTIVE_OPTION ** ActiveOption,OUT UINTN * Count)333 CollectActiveOptions (
334   IN   CONST LIST_ENTRY *BootOptionList,
335   OUT  ACTIVE_OPTION    **ActiveOption,
336   OUT  UINTN            *Count
337   )
338 {
339   UINTN ScanMode;
340 
341   *ActiveOption = NULL;
342 
343   //
344   // Scan the list twice:
345   // - count active entries,
346   // - store links to active entries.
347   //
348   for (ScanMode = 0; ScanMode < 2; ++ScanMode) {
349     CONST LIST_ENTRY *Link;
350 
351     Link = BootOptionList->ForwardLink;
352     *Count = 0;
353     while (Link != BootOptionList) {
354       CONST BDS_COMMON_OPTION *Current;
355 
356       Current = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);
357       if (IS_LOAD_OPTION_TYPE (Current->Attribute, LOAD_OPTION_ACTIVE)) {
358         if (ScanMode == 1) {
359           (*ActiveOption)[*Count].BootOption = Current;
360           (*ActiveOption)[*Count].Appended   = FALSE;
361         }
362         ++*Count;
363       }
364       Link = Link->ForwardLink;
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 = sizeof (PciDevFun) / sizeof (PciDevFun[0]);
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,0x0,0x0)
790     //                                   ^   ^   ^
791     //                                   |   |   LUN (always 0 on Q35)
792     //                                   |   port multiplier port number,
793     //                                   |   always 0 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,0x0,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)/HD( -- if PCI function is 0 or absent
876     //   PciRoot(0x0)/Pci(0x6,0x3)/HD( -- 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)/HD(",
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 = sizeof (TargetLun) / sizeof (TargetLun[0]);
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 {
935     //
936     // Generic OpenFirmware device path for PCI devices:
937     //
938     //   /pci@i0cf8/ethernet@3[,2]
939     //        ^              ^
940     //        |              PCI slot[, function] holding Ethernet card
941     //        PCI root at system bus port, PIO
942     //
943     // UEFI device path prefix (dependent on presence of nonzero PCI function):
944     //
945     //   PciRoot(0x0)/Pci(0x3,0x0)
946     //   PciRoot(0x0)/Pci(0x3,0x2)
947     //
948     Written = UnicodeSPrintAsciiFormat (
949       Translated,
950       *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
951       "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
952       PciRoot,
953       Bridges,
954       PciDevFun[0],
955       PciDevFun[1]
956       );
957   }
958 
959   //
960   // There's no way to differentiate between "completely used up without
961   // truncation" and "truncated", so treat the former as the latter, and return
962   // success only for "some room left unused".
963   //
964   if (Written + 1 < *TranslatedSize) {
965     *TranslatedSize = Written;
966     return RETURN_SUCCESS;
967   }
968 
969   return RETURN_BUFFER_TOO_SMALL;
970 }
971 
972 
973 //
974 // A type providing easy raw access to the base address of a virtio-mmio
975 // transport.
976 //
977 typedef union {
978   UINT64 Uint64;
979   UINT8  Raw[8];
980 } VIRTIO_MMIO_BASE_ADDRESS;
981 
982 
983 /**
984 
985   Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
986   path fragment.
987 
988   @param[in]     OfwNode         Array of OpenFirmware device nodes to
989                                  translate, constituting the beginning of an
990                                  OpenFirmware device path.
991 
992   @param[in]     NumNodes        Number of elements in OfwNode.
993 
994   @param[out]    Translated      Destination array receiving the UEFI path
995                                  fragment, allocated by the caller. If the
996                                  return value differs from RETURN_SUCCESS, its
997                                  contents is indeterminate.
998 
999   @param[in out] TranslatedSize  On input, the number of CHAR16's in
1000                                  Translated. On RETURN_SUCCESS this parameter
1001                                  is assigned the number of non-NUL CHAR16's
1002                                  written to Translated. In case of other return
1003                                  values, TranslatedSize is indeterminate.
1004 
1005 
1006   @retval RETURN_SUCCESS           Translation successful.
1007 
1008   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
1009                                    of bytes provided.
1010 
1011   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
1012                                    be translated in the current implementation.
1013 
1014 **/
1015 STATIC
1016 RETURN_STATUS
TranslateMmioOfwNodes(IN CONST OFW_NODE * OfwNode,IN UINTN NumNodes,OUT CHAR16 * Translated,IN OUT UINTN * TranslatedSize)1017 TranslateMmioOfwNodes (
1018   IN      CONST OFW_NODE *OfwNode,
1019   IN      UINTN          NumNodes,
1020   OUT     CHAR16         *Translated,
1021   IN OUT  UINTN          *TranslatedSize
1022   )
1023 {
1024   VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;
1025   CHAR16                   VenHwString[60 + 1];
1026   UINTN                    NumEntries;
1027   UINTN                    Written;
1028 
1029   //
1030   // Get the base address of the virtio-mmio transport.
1031   //
1032   if (NumNodes < REQUIRED_MMIO_OFW_NODES ||
1033       !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")
1034       ) {
1035     return RETURN_UNSUPPORTED;
1036   }
1037   NumEntries = 1;
1038   if (ParseUnitAddressHexList (
1039         OfwNode[0].UnitAddress,
1040         &VirtioMmioBase.Uint64,
1041         &NumEntries
1042         ) != RETURN_SUCCESS
1043       ) {
1044     return RETURN_UNSUPPORTED;
1045   }
1046 
1047   UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,
1048     "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,
1049     VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],
1050     VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],
1051     VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);
1052 
1053   if (NumNodes >= 2 &&
1054       SubstringEq (OfwNode[1].DriverName, "disk")) {
1055     //
1056     // OpenFirmware device path (virtio-blk disk):
1057     //
1058     //   /virtio-mmio@000000000a003c00/disk@0,0
1059     //                ^                     ^ ^
1060     //                |                     fixed
1061     //                base address of virtio-mmio register block
1062     //
1063     // UEFI device path prefix:
1064     //
1065     //   <VenHwString>/HD(
1066     //
1067     Written = UnicodeSPrintAsciiFormat (
1068                 Translated,
1069                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1070                 "%s/HD(",
1071                 VenHwString
1072                 );
1073   } else if (NumNodes >= 3 &&
1074              SubstringEq (OfwNode[1].DriverName, "channel") &&
1075              SubstringEq (OfwNode[2].DriverName, "disk")) {
1076     //
1077     // OpenFirmware device path (virtio-scsi disk):
1078     //
1079     //   /virtio-mmio@000000000a003a00/channel@0/disk@2,3
1080     //                ^                        ^      ^ ^
1081     //                |                        |      | LUN
1082     //                |                        |      target
1083     //                |                        channel (unused, fixed 0)
1084     //                base address of virtio-mmio register block
1085     //
1086     // UEFI device path prefix:
1087     //
1088     //   <VenHwString>/Scsi(0x2,0x3)
1089     //
1090     UINT64 TargetLun[2];
1091 
1092     TargetLun[1] = 0;
1093     NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);
1094     if (ParseUnitAddressHexList (
1095           OfwNode[2].UnitAddress,
1096           TargetLun,
1097           &NumEntries
1098           ) != RETURN_SUCCESS
1099         ) {
1100       return RETURN_UNSUPPORTED;
1101     }
1102 
1103     Written = UnicodeSPrintAsciiFormat (
1104                 Translated,
1105                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1106                 "%s/Scsi(0x%Lx,0x%Lx)",
1107                 VenHwString,
1108                 TargetLun[0],
1109                 TargetLun[1]
1110                 );
1111   } else if (NumNodes >= 2 &&
1112              SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {
1113     //
1114     // OpenFirmware device path (virtio-net NIC):
1115     //
1116     //   /virtio-mmio@000000000a003e00/ethernet-phy@0
1117     //                ^                             ^
1118     //                |                             fixed
1119     //                base address of virtio-mmio register block
1120     //
1121     // UEFI device path prefix (dependent on presence of nonzero PCI function):
1122     //
1123     //   <VenHwString>/MAC(
1124     //
1125     Written = UnicodeSPrintAsciiFormat (
1126                 Translated,
1127                 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
1128                 "%s/MAC(",
1129                 VenHwString
1130                 );
1131   } else {
1132     return RETURN_UNSUPPORTED;
1133   }
1134 
1135   //
1136   // There's no way to differentiate between "completely used up without
1137   // truncation" and "truncated", so treat the former as the latter, and return
1138   // success only for "some room left unused".
1139   //
1140   if (Written + 1 < *TranslatedSize) {
1141     *TranslatedSize = Written;
1142     return RETURN_SUCCESS;
1143   }
1144 
1145   return RETURN_BUFFER_TOO_SMALL;
1146 }
1147 
1148 
1149 /**
1150 
1151   Translate an array of OpenFirmware device nodes to a UEFI device path
1152   fragment.
1153 
1154   @param[in]     OfwNode         Array of OpenFirmware device nodes to
1155                                  translate, constituting the beginning of an
1156                                  OpenFirmware device path.
1157 
1158   @param[in]     NumNodes        Number of elements in OfwNode.
1159 
1160   @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
1161                                  CreateExtraRootBusMap(), to be used for
1162                                  translating positions of extra root buses to
1163                                  bus numbers.
1164 
1165   @param[out]    Translated      Destination array receiving the UEFI path
1166                                  fragment, allocated by the caller. If the
1167                                  return value differs from RETURN_SUCCESS, its
1168                                  contents is indeterminate.
1169 
1170   @param[in out] TranslatedSize  On input, the number of CHAR16's in
1171                                  Translated. On RETURN_SUCCESS this parameter
1172                                  is assigned the number of non-NUL CHAR16's
1173                                  written to Translated. In case of other return
1174                                  values, TranslatedSize is indeterminate.
1175 
1176 
1177   @retval RETURN_SUCCESS           Translation successful.
1178 
1179   @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
1180                                    of bytes provided.
1181 
1182   @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
1183                                    be translated in the current implementation.
1184 
1185   @retval RETURN_PROTOCOL_ERROR    The array of OpenFirmware device nodes has
1186                                    been (partially) recognized, but it contains
1187                                    a logic error / doesn't match system state.
1188 
1189 **/
1190 STATIC
1191 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)1192 TranslateOfwNodes (
1193   IN      CONST OFW_NODE           *OfwNode,
1194   IN      UINTN                    NumNodes,
1195   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
1196   OUT     CHAR16                   *Translated,
1197   IN OUT  UINTN                    *TranslatedSize
1198   )
1199 {
1200   RETURN_STATUS Status;
1201 
1202   Status = RETURN_UNSUPPORTED;
1203 
1204   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
1205     Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,
1206                Translated, TranslatedSize);
1207   }
1208   if (Status == RETURN_UNSUPPORTED &&
1209       FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {
1210     Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,
1211                TranslatedSize);
1212   }
1213   return Status;
1214 }
1215 
1216 /**
1217 
1218   Translate an OpenFirmware device path fragment to a UEFI device path
1219   fragment, and advance in the input string.
1220 
1221   @param[in out] Ptr             Address of the pointer pointing to the start
1222                                  of the path string. After successful
1223                                  translation (RETURN_SUCCESS) or at least
1224                                  successful parsing (RETURN_UNSUPPORTED,
1225                                  RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
1226                                  byte immediately following the consumed
1227                                  characters. In other error cases, it points to
1228                                  the byte that caused the error.
1229 
1230   @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
1231                                  CreateExtraRootBusMap(), to be used for
1232                                  translating positions of extra root buses to
1233                                  bus numbers.
1234 
1235   @param[out]    Translated      Destination array receiving the UEFI path
1236                                  fragment, allocated by the caller. If the
1237                                  return value differs from RETURN_SUCCESS, its
1238                                  contents is indeterminate.
1239 
1240   @param[in out] TranslatedSize  On input, the number of CHAR16's in
1241                                  Translated. On RETURN_SUCCESS this parameter
1242                                  is assigned the number of non-NUL CHAR16's
1243                                  written to Translated. In case of other return
1244                                  values, TranslatedSize is indeterminate.
1245 
1246 
1247   @retval RETURN_SUCCESS            Translation successful.
1248 
1249   @retval RETURN_BUFFER_TOO_SMALL   The OpenFirmware device path was parsed
1250                                     successfully, but its translation did not
1251                                     fit into the number of bytes provided.
1252                                     Further calls to this function are
1253                                     possible.
1254 
1255   @retval RETURN_UNSUPPORTED        The OpenFirmware device path was parsed
1256                                     successfully, but it can't be translated in
1257                                     the current implementation. Further calls
1258                                     to this function are possible.
1259 
1260   @retval RETURN_PROTOCOL_ERROR     The OpenFirmware device path has been
1261                                     (partially) recognized, but it contains a
1262                                     logic error / doesn't match system state.
1263                                     Further calls to this function are
1264                                     possible.
1265 
1266   @retval RETURN_NOT_FOUND          Translation terminated. On input, *Ptr was
1267                                     pointing to the empty string or "HALT". On
1268                                     output, *Ptr points to the empty string
1269                                     (ie. "HALT" is consumed transparently when
1270                                     present).
1271 
1272   @retval RETURN_INVALID_PARAMETER  Parse error. This is a permanent error.
1273 
1274 **/
1275 STATIC
1276 RETURN_STATUS
TranslateOfwPath(IN OUT CONST CHAR8 ** Ptr,IN CONST EXTRA_ROOT_BUS_MAP * ExtraPciRoots,OUT CHAR16 * Translated,IN OUT UINTN * TranslatedSize)1277 TranslateOfwPath (
1278   IN OUT  CONST CHAR8              **Ptr,
1279   IN      CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,
1280   OUT     CHAR16                   *Translated,
1281   IN OUT  UINTN                    *TranslatedSize
1282   )
1283 {
1284   UINTN         NumNodes;
1285   RETURN_STATUS Status;
1286   OFW_NODE      Node[EXAMINED_OFW_NODES];
1287   BOOLEAN       IsFinal;
1288   OFW_NODE      Skip;
1289 
1290   IsFinal = FALSE;
1291   NumNodes = 0;
1292   if (AsciiStrCmp (*Ptr, "HALT") == 0) {
1293     *Ptr += 4;
1294     Status = RETURN_NOT_FOUND;
1295   } else {
1296     Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);
1297   }
1298 
1299   if (Status == RETURN_NOT_FOUND) {
1300     DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));
1301     return RETURN_NOT_FOUND;
1302   }
1303 
1304   while (Status == RETURN_SUCCESS && !IsFinal) {
1305     ++NumNodes;
1306     Status = ParseOfwNode (
1307                Ptr,
1308                (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,
1309                &IsFinal
1310                );
1311   }
1312 
1313   switch (Status) {
1314   case RETURN_SUCCESS:
1315     ++NumNodes;
1316     break;
1317 
1318   case RETURN_INVALID_PARAMETER:
1319     DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));
1320     return RETURN_INVALID_PARAMETER;
1321 
1322   default:
1323     ASSERT (0);
1324   }
1325 
1326   Status = TranslateOfwNodes (
1327              Node,
1328              NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,
1329              ExtraPciRoots,
1330              Translated,
1331              TranslatedSize);
1332   switch (Status) {
1333   case RETURN_SUCCESS:
1334     DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));
1335     break;
1336 
1337   case RETURN_BUFFER_TOO_SMALL:
1338     DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));
1339     break;
1340 
1341   case RETURN_UNSUPPORTED:
1342     DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));
1343     break;
1344 
1345   case RETURN_PROTOCOL_ERROR:
1346     DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",
1347       __FUNCTION__));
1348     break;
1349 
1350   default:
1351     ASSERT (0);
1352   }
1353   return Status;
1354 }
1355 
1356 
1357 /**
1358 
1359   Convert the UEFI DevicePath to full text representation with DevPathToText,
1360   then match the UEFI device path fragment in Translated against it.
1361 
1362   @param[in] Translated        UEFI device path fragment, translated from
1363                                OpenFirmware format, to search for.
1364 
1365   @param[in] TranslatedLength  The length of Translated in CHAR16's.
1366 
1367   @param[in] DevicePath        Boot option device path whose textual rendering
1368                                to search in.
1369 
1370   @param[in] DevPathToText  Binary-to-text conversion protocol for DevicePath.
1371 
1372 
1373   @retval TRUE   If Translated was found at the beginning of DevicePath after
1374                  converting the latter to text.
1375 
1376   @retval FALSE  If DevicePath was NULL, or it could not be converted, or there
1377                  was no match.
1378 
1379 **/
1380 STATIC
1381 BOOLEAN
Match(IN CONST CHAR16 * Translated,IN UINTN TranslatedLength,IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath)1382 Match (
1383   IN  CONST CHAR16                           *Translated,
1384   IN  UINTN                                  TranslatedLength,
1385   IN  CONST EFI_DEVICE_PATH_PROTOCOL         *DevicePath
1386   )
1387 {
1388   CHAR16  *Converted;
1389   BOOLEAN Result;
1390 
1391   Converted = ConvertDevicePathToText (
1392                 DevicePath,
1393                 FALSE, // DisplayOnly
1394                 FALSE  // AllowShortcuts
1395                 );
1396   if (Converted == NULL) {
1397     return FALSE;
1398   }
1399 
1400   //
1401   // Attempt to expand any relative UEFI device path starting with HD() to an
1402   // absolute device path first. The logic imitates BdsLibBootViaBootOption().
1403   // We don't have to free the absolute device path,
1404   // BdsExpandPartitionPartialDevicePathToFull() has internal caching.
1405   //
1406   Result = FALSE;
1407   if (DevicePathType (DevicePath) == MEDIA_DEVICE_PATH &&
1408       DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP) {
1409     EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;
1410     CHAR16                   *AbsConverted;
1411 
1412     AbsDevicePath = BdsExpandPartitionPartialDevicePathToFull (
1413                       (HARDDRIVE_DEVICE_PATH *) DevicePath);
1414     if (AbsDevicePath == NULL) {
1415       goto Exit;
1416     }
1417     AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);
1418     if (AbsConverted == NULL) {
1419       goto Exit;
1420     }
1421     DEBUG ((DEBUG_VERBOSE,
1422       "%a: expanded relative device path \"%s\" for prefix matching\n",
1423       __FUNCTION__, Converted));
1424     FreePool (Converted);
1425     Converted = AbsConverted;
1426   }
1427 
1428   //
1429   // Is Translated a prefix of Converted?
1430   //
1431   Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
1432   DEBUG ((
1433     DEBUG_VERBOSE,
1434     "%a: against \"%s\": %a\n",
1435     __FUNCTION__,
1436     Converted,
1437     Result ? "match" : "no match"
1438     ));
1439 Exit:
1440   FreePool (Converted);
1441   return Result;
1442 }
1443 
1444 
1445 /**
1446   Append some of the unselected active boot options to the boot order.
1447 
1448   This function should accommodate any further policy changes in "boot option
1449   survival". Currently we're adding back everything that starts with neither
1450   PciRoot() nor HD() nor a virtio-mmio VenHw() node.
1451 
1452   @param[in,out] BootOrder     The structure holding the boot order to
1453                                complete. The caller is responsible for
1454                                initializing (and potentially populating) it
1455                                before calling this function.
1456 
1457   @param[in,out] ActiveOption  The array of active boot options to scan.
1458                                Entries marked as Appended will be skipped.
1459                                Those of the rest that satisfy the survival
1460                                policy will be added to BootOrder with
1461                                BootOrderAppend().
1462 
1463   @param[in]     ActiveCount   Number of elements in ActiveOption.
1464 
1465 
1466   @retval RETURN_SUCCESS  BootOrder has been extended with any eligible boot
1467                           options.
1468 
1469   @return                 Error codes returned by BootOrderAppend().
1470 **/
1471 STATIC
1472 RETURN_STATUS
BootOrderComplete(IN OUT BOOT_ORDER * BootOrder,IN OUT ACTIVE_OPTION * ActiveOption,IN UINTN ActiveCount)1473 BootOrderComplete (
1474   IN OUT  BOOT_ORDER    *BootOrder,
1475   IN OUT  ACTIVE_OPTION *ActiveOption,
1476   IN      UINTN         ActiveCount
1477   )
1478 {
1479   RETURN_STATUS Status;
1480   UINTN         Idx;
1481 
1482   Status = RETURN_SUCCESS;
1483   Idx = 0;
1484   while (!RETURN_ERROR (Status) && Idx < ActiveCount) {
1485     if (!ActiveOption[Idx].Appended) {
1486       CONST BDS_COMMON_OPTION        *Current;
1487       CONST EFI_DEVICE_PATH_PROTOCOL *FirstNode;
1488 
1489       Current = ActiveOption[Idx].BootOption;
1490       FirstNode = Current->DevicePath;
1491       if (FirstNode != NULL) {
1492         CHAR16        *Converted;
1493         STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";
1494         BOOLEAN       Keep;
1495 
1496         Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);
1497         if (Converted == NULL) {
1498           Converted = ConvFallBack;
1499         }
1500 
1501         Keep = TRUE;
1502         if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&
1503             DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {
1504           //
1505           // drop HD()
1506           //
1507           Keep = FALSE;
1508         } else if (DevicePathType(FirstNode) == ACPI_DEVICE_PATH &&
1509                    DevicePathSubType(FirstNode) == ACPI_DP) {
1510           ACPI_HID_DEVICE_PATH *Acpi;
1511 
1512           Acpi = (ACPI_HID_DEVICE_PATH *) FirstNode;
1513           if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST &&
1514               EISA_ID_TO_NUM (Acpi->HID) == 0x0a03) {
1515             //
1516             // drop PciRoot() if we enabled the user to select PCI-like boot
1517             // options, by providing translation for such OFW device path
1518             // fragments
1519             //
1520             Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);
1521           }
1522         } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&
1523                    DevicePathSubType(FirstNode) == HW_VENDOR_DP) {
1524           VENDOR_DEVICE_PATH *VenHw;
1525 
1526           VenHw = (VENDOR_DEVICE_PATH *)FirstNode;
1527           if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {
1528             //
1529             // drop virtio-mmio if we enabled the user to select boot options
1530             // referencing such device paths
1531             //
1532             Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);
1533           }
1534         }
1535 
1536         if (Keep) {
1537           Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);
1538           if (!RETURN_ERROR (Status)) {
1539             DEBUG ((DEBUG_VERBOSE, "%a: keeping \"%s\"\n", __FUNCTION__,
1540               Converted));
1541           }
1542         } else {
1543           DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,
1544             Converted));
1545         }
1546 
1547         if (Converted != ConvFallBack) {
1548           FreePool (Converted);
1549         }
1550       }
1551     }
1552     ++Idx;
1553   }
1554   return Status;
1555 }
1556 
1557 
1558 /**
1559   Delete Boot#### variables that stand for such active boot options that have
1560   been dropped (ie. have not been selected by either matching or "survival
1561   policy").
1562 
1563   @param[in]  ActiveOption  The array of active boot options to scan. Each
1564                             entry not marked as appended will trigger the
1565                             deletion of the matching Boot#### variable.
1566 
1567   @param[in]  ActiveCount   Number of elements in ActiveOption.
1568 **/
1569 STATIC
1570 VOID
PruneBootVariables(IN CONST ACTIVE_OPTION * ActiveOption,IN UINTN ActiveCount)1571 PruneBootVariables (
1572   IN  CONST ACTIVE_OPTION *ActiveOption,
1573   IN  UINTN               ActiveCount
1574   )
1575 {
1576   UINTN Idx;
1577 
1578   for (Idx = 0; Idx < ActiveCount; ++Idx) {
1579     if (!ActiveOption[Idx].Appended) {
1580       CHAR16 VariableName[9];
1581 
1582       UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",
1583         ActiveOption[Idx].BootOption->BootCurrent);
1584 
1585       //
1586       // "The space consumed by the deleted variable may not be available until
1587       // the next power cycle", but that's good enough.
1588       //
1589       gRT->SetVariable (VariableName, &gEfiGlobalVariableGuid,
1590              0,   // Attributes, 0 means deletion
1591              0,   // DataSize, 0 means deletion
1592              NULL // Data
1593              );
1594     }
1595   }
1596 }
1597 
1598 
1599 /**
1600 
1601   Set the boot order based on configuration retrieved from QEMU.
1602 
1603   Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
1604   OpenFirmware device paths therein to UEFI device path fragments. Match the
1605   translated fragments against BootOptionList, and rewrite the BootOrder NvVar
1606   so that it corresponds to the order described in fw_cfg.
1607 
1608   @param[in] BootOptionList  A boot option list, created with
1609                              BdsLibEnumerateAllBootOption ().
1610 
1611 
1612   @retval RETURN_SUCCESS            BootOrder NvVar rewritten.
1613 
1614   @retval RETURN_UNSUPPORTED        QEMU's fw_cfg is not supported.
1615 
1616   @retval RETURN_NOT_FOUND          Empty or nonexistent "bootorder" fw_cfg
1617                                     file, or no match found between the
1618                                     "bootorder" fw_cfg file and BootOptionList.
1619 
1620   @retval RETURN_INVALID_PARAMETER  Parse error in the "bootorder" fw_cfg file.
1621 
1622   @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.
1623 
1624   @return                           Values returned by gBS->LocateProtocol ()
1625                                     or gRT->SetVariable ().
1626 
1627 **/
1628 RETURN_STATUS
SetBootOrderFromQemu(IN CONST LIST_ENTRY * BootOptionList)1629 SetBootOrderFromQemu (
1630   IN  CONST LIST_ENTRY *BootOptionList
1631   )
1632 {
1633   RETURN_STATUS                    Status;
1634   FIRMWARE_CONFIG_ITEM             FwCfgItem;
1635   UINTN                            FwCfgSize;
1636   CHAR8                            *FwCfg;
1637   CONST CHAR8                      *FwCfgPtr;
1638 
1639   BOOT_ORDER                       BootOrder;
1640   ACTIVE_OPTION                    *ActiveOption;
1641   UINTN                            ActiveCount;
1642 
1643   EXTRA_ROOT_BUS_MAP               *ExtraPciRoots;
1644 
1645   UINTN                            TranslatedSize;
1646   CHAR16                           Translated[TRANSLATION_OUTPUT_SIZE];
1647 
1648   Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
1649   if (Status != RETURN_SUCCESS) {
1650     return Status;
1651   }
1652 
1653   if (FwCfgSize == 0) {
1654     return RETURN_NOT_FOUND;
1655   }
1656 
1657   FwCfg = AllocatePool (FwCfgSize);
1658   if (FwCfg == NULL) {
1659     return RETURN_OUT_OF_RESOURCES;
1660   }
1661 
1662   QemuFwCfgSelectItem (FwCfgItem);
1663   QemuFwCfgReadBytes (FwCfgSize, FwCfg);
1664   if (FwCfg[FwCfgSize - 1] != '\0') {
1665     Status = RETURN_INVALID_PARAMETER;
1666     goto ErrorFreeFwCfg;
1667   }
1668 
1669   DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
1670   DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
1671   DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
1672   FwCfgPtr = FwCfg;
1673 
1674   BootOrder.Produced  = 0;
1675   BootOrder.Allocated = 1;
1676   BootOrder.Data = AllocatePool (
1677                      BootOrder.Allocated * sizeof (*BootOrder.Data)
1678                      );
1679   if (BootOrder.Data == NULL) {
1680     Status = RETURN_OUT_OF_RESOURCES;
1681     goto ErrorFreeFwCfg;
1682   }
1683 
1684   Status = CollectActiveOptions (BootOptionList, &ActiveOption, &ActiveCount);
1685   if (RETURN_ERROR (Status)) {
1686     goto ErrorFreeBootOrder;
1687   }
1688 
1689   if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
1690     Status = CreateExtraRootBusMap (&ExtraPciRoots);
1691     if (EFI_ERROR (Status)) {
1692       goto ErrorFreeActiveOption;
1693     }
1694   } else {
1695     ExtraPciRoots = NULL;
1696   }
1697 
1698   //
1699   // translate each OpenFirmware path
1700   //
1701   TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
1702   Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
1703              &TranslatedSize);
1704   while (Status == RETURN_SUCCESS ||
1705          Status == RETURN_UNSUPPORTED ||
1706          Status == RETURN_PROTOCOL_ERROR ||
1707          Status == RETURN_BUFFER_TOO_SMALL) {
1708     if (Status == RETURN_SUCCESS) {
1709       UINTN Idx;
1710 
1711       //
1712       // match translated OpenFirmware path against all active boot options
1713       //
1714       for (Idx = 0; Idx < ActiveCount; ++Idx) {
1715         if (Match (
1716               Translated,
1717               TranslatedSize, // contains length, not size, in CHAR16's here
1718               ActiveOption[Idx].BootOption->DevicePath
1719               )
1720             ) {
1721           //
1722           // match found, store ID and continue with next OpenFirmware path
1723           //
1724           Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
1725           if (Status != RETURN_SUCCESS) {
1726             goto ErrorFreeExtraPciRoots;
1727           }
1728           break;
1729         }
1730       } // scanned all active boot options
1731     }   // translation successful
1732 
1733     TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
1734     Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,
1735                &TranslatedSize);
1736   } // scanning of OpenFirmware paths done
1737 
1738   if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {
1739     //
1740     // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
1741     // Some of the active boot options that have not been selected over fw_cfg
1742     // should be preserved at the end of the boot order.
1743     //
1744     Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
1745     if (RETURN_ERROR (Status)) {
1746       goto ErrorFreeExtraPciRoots;
1747     }
1748 
1749     //
1750     // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
1751     // attributes.
1752     //
1753     Status = gRT->SetVariable (
1754                     L"BootOrder",
1755                     &gEfiGlobalVariableGuid,
1756                     EFI_VARIABLE_NON_VOLATILE |
1757                       EFI_VARIABLE_BOOTSERVICE_ACCESS |
1758                       EFI_VARIABLE_RUNTIME_ACCESS,
1759                     BootOrder.Produced * sizeof (*BootOrder.Data),
1760                     BootOrder.Data
1761                     );
1762     if (EFI_ERROR (Status)) {
1763       DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));
1764       goto ErrorFreeExtraPciRoots;
1765     }
1766 
1767     DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));
1768     PruneBootVariables (ActiveOption, ActiveCount);
1769   }
1770 
1771 ErrorFreeExtraPciRoots:
1772   if (ExtraPciRoots != NULL) {
1773     DestroyExtraRootBusMap (ExtraPciRoots);
1774   }
1775 
1776 ErrorFreeActiveOption:
1777   FreePool (ActiveOption);
1778 
1779 ErrorFreeBootOrder:
1780   FreePool (BootOrder.Data);
1781 
1782 ErrorFreeFwCfg:
1783   FreePool (FwCfg);
1784 
1785   return Status;
1786 }
1787 
1788 
1789 /**
1790   Calculate the number of seconds we should be showing the FrontPage progress
1791   bar for.
1792 
1793   @return  The TimeoutDefault argument for PlatformBdsEnterFrontPage().
1794 **/
1795 UINT16
GetFrontPageTimeoutFromQemu(VOID)1796 GetFrontPageTimeoutFromQemu (
1797   VOID
1798   )
1799 {
1800   FIRMWARE_CONFIG_ITEM BootMenuWaitItem;
1801   UINTN                BootMenuWaitSize;
1802 
1803   QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);
1804   if (QemuFwCfgRead16 () == 0) {
1805     //
1806     // The user specified "-boot menu=off", or didn't specify "-boot
1807     // menu=(on|off)" at all. Return the platform default.
1808     //
1809     return PcdGet16 (PcdPlatformBootTimeOut);
1810   }
1811 
1812   if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,
1813                       &BootMenuWaitSize)) ||
1814       BootMenuWaitSize != sizeof (UINT16)) {
1815     //
1816     // "-boot menu=on" was specified without "splash-time=N". In this case,
1817     // return three seconds if the platform default would cause us to skip the
1818     // front page, and return the platform default otherwise.
1819     //
1820     UINT16 Timeout;
1821 
1822     Timeout = PcdGet16 (PcdPlatformBootTimeOut);
1823     if (Timeout == 0) {
1824       Timeout = 3;
1825     }
1826     return Timeout;
1827   }
1828 
1829   //
1830   // "-boot menu=on,splash-time=N" was specified, where N is in units of
1831   // milliseconds. The Intel BDS Front Page progress bar only supports whole
1832   // seconds, round N up.
1833   //
1834   QemuFwCfgSelectItem (BootMenuWaitItem);
1835   return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);
1836 }
1837