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