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