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