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