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