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