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