]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/PlatformBdsLib/QemuBootOrder.c
OvmfPkg: QemuBootOrder: whitespace fix
[mirror_edk2.git] / OvmfPkg / Library / PlatformBdsLib / QemuBootOrder.c
1 /** @file
2 Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
3
4 Copyright (C) 2012, Red Hat, Inc.
5
6 This program and the accompanying materials are licensed and made available
7 under the terms and conditions of the BSD License which accompanies this
8 distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
12 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 **/
14
15 #include <Library/QemuFwCfgLib.h>
16 #include <Library/DebugLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/GenericBdsLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/UefiRuntimeServicesTableLib.h>
21 #include <Library/BaseLib.h>
22 #include <Library/PrintLib.h>
23 #include <Protocol/DevicePathToText.h>
24 #include <Guid/GlobalVariable.h>
25
26
27 /**
28 OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
29 **/
30 #define TRANSLATION_OUTPUT_SIZE 0x100
31
32
33 /**
34 Number of nodes in OpenFirmware device paths that is required and examined.
35 **/
36 #define FIXED_OFW_NODES 4
37
38
39 /**
40 Simple character classification routines, corresponding to POSIX class names
41 and ASCII encoding.
42 **/
43 STATIC
44 BOOLEAN
45 IsAlnum (
46 IN CHAR8 Chr
47 )
48 {
49 return (('0' <= Chr && Chr <= '9') ||
50 ('A' <= Chr && Chr <= 'Z') ||
51 ('a' <= Chr && Chr <= 'z')
52 );
53 }
54
55
56 STATIC
57 BOOLEAN
58 IsDriverNamePunct (
59 IN CHAR8 Chr
60 )
61 {
62 return (Chr == ',' || Chr == '.' || Chr == '_' ||
63 Chr == '+' || Chr == '-'
64 );
65 }
66
67
68 STATIC
69 BOOLEAN
70 IsPrintNotDelim (
71 IN CHAR8 Chr
72 )
73 {
74 return (32 <= Chr && Chr <= 126 &&
75 Chr != '/' && Chr != '@' && Chr != ':');
76 }
77
78
79 /**
80 Utility types and functions.
81 **/
82 typedef struct {
83 CONST CHAR8 *Ptr; // not necessarily NUL-terminated
84 UINTN Len; // number of non-NUL characters
85 } SUBSTRING;
86
87
88 /**
89
90 Check if Substring and String have identical contents.
91
92 The function relies on the restriction that a SUBSTRING cannot have embedded
93 NULs either.
94
95 @param[in] Substring The SUBSTRING input to the comparison.
96
97 @param[in] String The ASCII string input to the comparison.
98
99
100 @return Whether the inputs have identical contents.
101
102 **/
103 STATIC
104 BOOLEAN
105 SubstringEq (
106 IN SUBSTRING Substring,
107 IN CONST CHAR8 *String
108 )
109 {
110 UINTN Pos;
111 CONST CHAR8 *Chr;
112
113 Pos = 0;
114 Chr = String;
115
116 while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {
117 ++Pos;
118 ++Chr;
119 }
120
121 return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');
122 }
123
124
125 /**
126
127 Parse a comma-separated list of hexadecimal integers into the elements of an
128 UINT32 array.
129
130 Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
131 or an empty string are not allowed; they are rejected.
132
133 The function relies on ASCII encoding.
134
135 @param[in] UnitAddress The substring to parse.
136
137 @param[out] Result The array, allocated by the caller, to receive
138 the parsed values. This parameter may be NULL if
139 NumResults is zero on input.
140
141 @param[in out] NumResults On input, the number of elements allocated for
142 Result. On output, the number of elements it has
143 taken (or would have taken) to parse the string
144 fully.
145
146
147 @retval RETURN_SUCCESS UnitAddress has been fully parsed.
148 NumResults is set to the number of parsed
149 values; the corresponding elements have
150 been set in Result. The rest of Result's
151 elements are unchanged.
152
153 @retval RETURN_BUFFER_TOO_SMALL UnitAddress has been fully parsed.
154 NumResults is set to the number of parsed
155 values, but elements have been stored only
156 up to the input value of NumResults, which
157 is less than what has been parsed.
158
159 @retval RETURN_INVALID_PARAMETER Parse error. The contents of Results is
160 indeterminate. NumResults has not been
161 changed.
162
163 **/
164 STATIC
165 RETURN_STATUS
166 ParseUnitAddressHexList (
167 IN SUBSTRING UnitAddress,
168 OUT UINT32 *Result,
169 IN OUT UINTN *NumResults
170 )
171 {
172 UINTN Entry; // number of entry currently being parsed
173 UINT32 EntryVal; // value being constructed for current entry
174 CHAR8 PrevChr; // UnitAddress character previously checked
175 UINTN Pos; // current position within UnitAddress
176 RETURN_STATUS Status;
177
178 Entry = 0;
179 EntryVal = 0;
180 PrevChr = ',';
181
182 for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {
183 CHAR8 Chr;
184 INT8 Val;
185
186 Chr = UnitAddress.Ptr[Pos];
187 Val = ('a' <= Chr && Chr <= 'f') ? (Chr - 'a' + 10) :
188 ('A' <= Chr && Chr <= 'F') ? (Chr - 'A' + 10) :
189 ('0' <= Chr && Chr <= '9') ? (Chr - '0' ) :
190 -1;
191
192 if (Val >= 0) {
193 if (EntryVal > 0xFFFFFFF) {
194 return RETURN_INVALID_PARAMETER;
195 }
196 EntryVal = (EntryVal << 4) | Val;
197 } else if (Chr == ',') {
198 if (PrevChr == ',') {
199 return RETURN_INVALID_PARAMETER;
200 }
201 if (Entry < *NumResults) {
202 Result[Entry] = EntryVal;
203 }
204 ++Entry;
205 EntryVal = 0;
206 } else {
207 return RETURN_INVALID_PARAMETER;
208 }
209
210 PrevChr = Chr;
211 }
212
213 if (PrevChr == ',') {
214 return RETURN_INVALID_PARAMETER;
215 }
216 if (Entry < *NumResults) {
217 Result[Entry] = EntryVal;
218 Status = RETURN_SUCCESS;
219 } else {
220 Status = RETURN_BUFFER_TOO_SMALL;
221 }
222 ++Entry;
223
224 *NumResults = Entry;
225 return Status;
226 }
227
228
229 /**
230 A simple array of Boot Option ID's.
231 **/
232 typedef struct {
233 UINT16 *Data;
234 UINTN Allocated;
235 UINTN Produced;
236 } BOOT_ORDER;
237
238
239 /**
240
241 Append BootOptionId to BootOrder, reallocating the latter if needed.
242
243 @param[in out] BootOrder The structure pointing to the array and holding
244 allocation and usage counters.
245
246 @param[in] BootOptionId The value to append to the array.
247
248
249 @retval RETURN_SUCCESS BootOptionId appended.
250
251 @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.
252
253 **/
254 STATIC
255 RETURN_STATUS
256 BootOrderAppend (
257 IN OUT BOOT_ORDER *BootOrder,
258 IN UINT16 BootOptionId
259 )
260 {
261 if (BootOrder->Produced == BootOrder->Allocated) {
262 UINTN AllocatedNew;
263 UINT16 *DataNew;
264
265 ASSERT (BootOrder->Allocated > 0);
266 AllocatedNew = BootOrder->Allocated * 2;
267 DataNew = ReallocatePool (
268 BootOrder->Allocated * sizeof (*BootOrder->Data),
269 AllocatedNew * sizeof (*DataNew),
270 BootOrder->Data
271 );
272 if (DataNew == NULL) {
273 return RETURN_OUT_OF_RESOURCES;
274 }
275 BootOrder->Allocated = AllocatedNew;
276 BootOrder->Data = DataNew;
277 }
278
279 BootOrder->Data[BootOrder->Produced++] = BootOptionId;
280 return RETURN_SUCCESS;
281 }
282
283
284 /**
285 OpenFirmware device path node
286 **/
287 typedef struct {
288 SUBSTRING DriverName;
289 SUBSTRING UnitAddress;
290 SUBSTRING DeviceArguments;
291 } OFW_NODE;
292
293
294 /**
295
296 Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
297 structure, and advance in the input string.
298
299 The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"
300 (a leading slash is expected and not returned):
301
302 /driver-name@unit-address[:device-arguments][<LF>]
303
304 A single trailing <LF> character is consumed but not returned. A trailing
305 <LF> or NUL character terminates the device path.
306
307 The function relies on ASCII encoding.
308
309 @param[in out] Ptr Address of the pointer pointing to the start of the
310 node string. After successful parsing *Ptr is set to
311 the byte immediately following the consumed
312 characters. On error it points to the byte that
313 caused the error. The input string is never modified.
314
315 @param[out] OfwNode The members of this structure point into the input
316 string, designating components of the node.
317 Separators are never included. If "device-arguments"
318 is missing, then DeviceArguments.Ptr is set to NULL.
319 All components that are present have nonzero length.
320
321 If the call doesn't succeed, the contents of this
322 structure is indeterminate.
323
324 @param[out] IsFinal In case of successul parsing, this parameter signals
325 whether the node just parsed is the final node in the
326 device path. The call after a final node will attempt
327 to start parsing the next path. If the call doesn't
328 succeed, then this parameter is not changed.
329
330
331 @retval RETURN_SUCCESS Parsing successful.
332
333 @retval RETURN_NOT_FOUND Parsing terminated. *Ptr was (and is)
334 pointing to an empty string.
335
336 @retval RETURN_INVALID_PARAMETER Parse error.
337
338 **/
339 STATIC
340 RETURN_STATUS
341 ParseOfwNode (
342 IN OUT CONST CHAR8 **Ptr,
343 OUT OFW_NODE *OfwNode,
344 OUT BOOLEAN *IsFinal
345 )
346 {
347 //
348 // A leading slash is expected. End of string is tolerated.
349 //
350 switch (**Ptr) {
351 case '\0':
352 return RETURN_NOT_FOUND;
353
354 case '/':
355 ++*Ptr;
356 break;
357
358 default:
359 return RETURN_INVALID_PARAMETER;
360 }
361
362 //
363 // driver-name
364 //
365 OfwNode->DriverName.Ptr = *Ptr;
366 OfwNode->DriverName.Len = 0;
367 while (OfwNode->DriverName.Len < 32 &&
368 (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))
369 ) {
370 ++*Ptr;
371 ++OfwNode->DriverName.Len;
372 }
373
374 if (OfwNode->DriverName.Len == 0 || OfwNode->DriverName.Len == 32) {
375 return RETURN_INVALID_PARAMETER;
376 }
377
378
379 //
380 // unit-address
381 //
382 if (**Ptr != '@') {
383 return RETURN_INVALID_PARAMETER;
384 }
385 ++*Ptr;
386
387 OfwNode->UnitAddress.Ptr = *Ptr;
388 OfwNode->UnitAddress.Len = 0;
389 while (IsPrintNotDelim (**Ptr)) {
390 ++*Ptr;
391 ++OfwNode->UnitAddress.Len;
392 }
393
394 if (OfwNode->UnitAddress.Len == 0) {
395 return RETURN_INVALID_PARAMETER;
396 }
397
398
399 //
400 // device-arguments, may be omitted
401 //
402 OfwNode->DeviceArguments.Len = 0;
403 if (**Ptr == ':') {
404 ++*Ptr;
405 OfwNode->DeviceArguments.Ptr = *Ptr;
406
407 while (IsPrintNotDelim (**Ptr)) {
408 ++*Ptr;
409 ++OfwNode->DeviceArguments.Len;
410 }
411
412 if (OfwNode->DeviceArguments.Len == 0) {
413 return RETURN_INVALID_PARAMETER;
414 }
415 }
416 else {
417 OfwNode->DeviceArguments.Ptr = NULL;
418 }
419
420 switch (**Ptr) {
421 case '\n':
422 ++*Ptr;
423 //
424 // fall through
425 //
426
427 case '\0':
428 *IsFinal = TRUE;
429 break;
430
431 case '/':
432 *IsFinal = FALSE;
433 break;
434
435 default:
436 return RETURN_INVALID_PARAMETER;
437 }
438
439 DEBUG ((
440 DEBUG_VERBOSE,
441 "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
442 __FUNCTION__,
443 OfwNode->DriverName.Len, OfwNode->DriverName.Ptr,
444 OfwNode->UnitAddress.Len, OfwNode->UnitAddress.Ptr,
445 OfwNode->DeviceArguments.Len,
446 OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr
447 ));
448 return RETURN_SUCCESS;
449 }
450
451
452 /**
453
454 Translate an array of OpenFirmware device nodes to a UEFI device path
455 fragment.
456
457 @param[in] OfwNode Array of OpenFirmware device nodes to
458 translate, constituting the beginning of an
459 OpenFirmware device path.
460
461 @param[in] NumNodes Number of elements in OfwNode.
462
463 @param[out] Translated Destination array receiving the UEFI path
464 fragment, allocated by the caller. If the
465 return value differs from RETURN_SUCCESS, its
466 contents is indeterminate.
467
468 @param[in out] TranslatedSize On input, the number of CHAR16's in
469 Translated. On RETURN_SUCCESS this parameter
470 is assigned the number of non-NUL CHAR16's
471 written to Translated. In case of other return
472 values, TranslatedSize is indeterminate.
473
474
475 @retval RETURN_SUCCESS Translation successful.
476
477 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number
478 of bytes provided.
479
480 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't
481 be translated in the current implementation.
482
483 **/
484 STATIC
485 RETURN_STATUS
486 TranslateOfwNodes (
487 IN CONST OFW_NODE *OfwNode,
488 IN UINTN NumNodes,
489 OUT CHAR16 *Translated,
490 IN OUT UINTN *TranslatedSize
491 )
492 {
493 UINT32 PciDevFun[2];
494 UINTN NumEntries;
495 UINTN Written;
496
497 //
498 // Get PCI device and optional PCI function. Assume a single PCI root.
499 //
500 if (NumNodes < FIXED_OFW_NODES ||
501 !SubstringEq (OfwNode[0].DriverName, "pci")
502 ) {
503 return RETURN_UNSUPPORTED;
504 }
505 PciDevFun[1] = 0;
506 NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);
507 if (ParseUnitAddressHexList (
508 OfwNode[1].UnitAddress,
509 PciDevFun,
510 &NumEntries
511 ) != RETURN_SUCCESS
512 ) {
513 return RETURN_UNSUPPORTED;
514 }
515
516 if (SubstringEq (OfwNode[1].DriverName, "ide") &&
517 SubstringEq (OfwNode[2].DriverName, "drive") &&
518 SubstringEq (OfwNode[3].DriverName, "disk")
519 ) {
520 //
521 // OpenFirmware device path (IDE disk, IDE CD-ROM):
522 //
523 // /pci@i0cf8/ide@1,1/drive@0/disk@0
524 // ^ ^ ^ ^ ^
525 // | | | | master or slave
526 // | | | primary or secondary
527 // | PCI slot & function holding IDE controller
528 // PCI root at system bus port, PIO
529 //
530 // UEFI device path:
531 //
532 // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
533 // ^
534 // fixed LUN
535 //
536 UINT32 Secondary;
537 UINT32 Slave;
538
539 NumEntries = 1;
540 if (ParseUnitAddressHexList (
541 OfwNode[2].UnitAddress,
542 &Secondary,
543 &NumEntries
544 ) != RETURN_SUCCESS ||
545 Secondary > 1 ||
546 ParseUnitAddressHexList (
547 OfwNode[3].UnitAddress,
548 &Slave,
549 &NumEntries // reuse after previous single-element call
550 ) != RETURN_SUCCESS ||
551 Slave > 1
552 ) {
553 return RETURN_UNSUPPORTED;
554 }
555
556 Written = UnicodeSPrintAsciiFormat (
557 Translated,
558 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
559 "PciRoot(0x0)/Pci(0x%x,0x%x)/Ata(%a,%a,0x0)",
560 PciDevFun[0],
561 PciDevFun[1],
562 Secondary ? "Secondary" : "Primary",
563 Slave ? "Slave" : "Master"
564 );
565 } else if (SubstringEq (OfwNode[1].DriverName, "isa") &&
566 SubstringEq (OfwNode[2].DriverName, "fdc") &&
567 SubstringEq (OfwNode[3].DriverName, "floppy")
568 ) {
569 //
570 // OpenFirmware device path (floppy disk):
571 //
572 // /pci@i0cf8/isa@1/fdc@03f0/floppy@0
573 // ^ ^ ^ ^
574 // | | | A: or B:
575 // | | ISA controller io-port (hex)
576 // | PCI slot holding ISA controller
577 // PCI root at system bus port, PIO
578 //
579 // UEFI device path:
580 //
581 // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
582 // ^
583 // ACPI UID
584 //
585 UINT32 AcpiUid;
586
587 NumEntries = 1;
588 if (ParseUnitAddressHexList (
589 OfwNode[3].UnitAddress,
590 &AcpiUid,
591 &NumEntries
592 ) != RETURN_SUCCESS ||
593 AcpiUid > 1
594 ) {
595 return RETURN_UNSUPPORTED;
596 }
597
598 Written = UnicodeSPrintAsciiFormat (
599 Translated,
600 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
601 "PciRoot(0x0)/Pci(0x%x,0x%x)/Floppy(0x%x)",
602 PciDevFun[0],
603 PciDevFun[1],
604 AcpiUid
605 );
606 } else {
607 return RETURN_UNSUPPORTED;
608 }
609
610 //
611 // There's no way to differentiate between "completely used up without
612 // truncation" and "truncated", so treat the former as the latter, and return
613 // success only for "some room left unused".
614 //
615 if (Written + 1 < *TranslatedSize) {
616 *TranslatedSize = Written;
617 return RETURN_SUCCESS;
618 }
619
620 return RETURN_BUFFER_TOO_SMALL;
621 }
622
623
624 /**
625
626 Translate an OpenFirmware device path fragment to a UEFI device path
627 fragment, and advance in the input string.
628
629 @param[in out] Ptr Address of the pointer pointing to the start
630 of the path string. After successful
631 translation (RETURN_SUCCESS) or at least
632 successful parsing (RETURN_UNSUPPORTED,
633 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
634 byte immediately following the consumed
635 characters. In other error cases, it points to
636 the byte that caused the error.
637
638 @param[out] Translated Destination array receiving the UEFI path
639 fragment, allocated by the caller. If the
640 return value differs from RETURN_SUCCESS, its
641 contents is indeterminate.
642
643 @param[in out] TranslatedSize On input, the number of CHAR16's in
644 Translated. On RETURN_SUCCESS this parameter
645 is assigned the number of non-NUL CHAR16's
646 written to Translated. In case of other return
647 values, TranslatedSize is indeterminate.
648
649
650 @retval RETURN_SUCCESS Translation successful.
651
652 @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed
653 successfully, but its translation did not
654 fit into the number of bytes provided.
655 Further calls to this function are
656 possible.
657
658 @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed
659 successfully, but it can't be translated in
660 the current implementation. Further calls
661 to this function are possible.
662
663 @retval RETURN_NOT_FOUND Translation terminated, *Ptr was (and is)
664 pointing to an empty string.
665
666 @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.
667
668 **/
669 STATIC
670 RETURN_STATUS
671 TranslateOfwPath (
672 IN OUT CONST CHAR8 **Ptr,
673 OUT CHAR16 *Translated,
674 IN OUT UINTN *TranslatedSize
675 )
676 {
677 UINTN NumNodes;
678 RETURN_STATUS Status;
679 OFW_NODE Node[FIXED_OFW_NODES];
680 BOOLEAN IsFinal;
681 OFW_NODE Skip;
682
683 NumNodes = 0;
684 Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);
685
686 if (Status == RETURN_NOT_FOUND) {
687 DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));
688 return RETURN_NOT_FOUND;
689 }
690
691 while (Status == RETURN_SUCCESS && !IsFinal) {
692 ++NumNodes;
693 Status = ParseOfwNode (
694 Ptr,
695 (NumNodes < FIXED_OFW_NODES) ? &Node[NumNodes] : &Skip,
696 &IsFinal
697 );
698 }
699
700 switch (Status) {
701 case RETURN_SUCCESS:
702 ++NumNodes;
703 break;
704
705 case RETURN_INVALID_PARAMETER:
706 DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));
707 return RETURN_INVALID_PARAMETER;
708
709 default:
710 ASSERT (0);
711 }
712
713 Status = TranslateOfwNodes (
714 Node,
715 NumNodes < FIXED_OFW_NODES ? NumNodes : FIXED_OFW_NODES,
716 Translated,
717 TranslatedSize);
718 switch (Status) {
719 case RETURN_SUCCESS:
720 DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));
721 break;
722
723 case RETURN_BUFFER_TOO_SMALL:
724 DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));
725 break;
726
727 case RETURN_UNSUPPORTED:
728 DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));
729 break;
730
731 default:
732 ASSERT (0);
733 }
734 return Status;
735 }
736
737
738 /**
739
740 Convert the UEFI DevicePath to full text representation with DevPathToText,
741 then match the UEFI device path fragment in Translated against it.
742
743 @param[in] Translated UEFI device path fragment, translated from
744 OpenFirmware format, to search for.
745
746 @param[in] TranslatedLength The length of Translated in CHAR16's.
747
748 @param[in] DevicePath Boot option device path whose textual rendering
749 to search in.
750
751 @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.
752
753
754 @retval TRUE If Translated was found at the beginning of DevicePath after
755 converting the latter to text.
756
757 @retval FALSE If DevicePath was NULL, or it could not be converted, or there
758 was no match.
759
760 **/
761 STATIC
762 BOOLEAN
763 Match (
764 IN CONST CHAR16 *Translated,
765 IN UINTN TranslatedLength,
766 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
767 IN CONST EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText
768 )
769 {
770 CHAR16 *Converted;
771 BOOLEAN Result;
772
773 Converted = DevPathToText->ConvertDevicePathToText (
774 DevicePath,
775 FALSE, // DisplayOnly
776 FALSE // AllowShortcuts
777 );
778 if (Converted == NULL) {
779 return FALSE;
780 }
781
782 //
783 // Is Translated a prefix of Converted?
784 //
785 Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
786 DEBUG ((
787 DEBUG_VERBOSE,
788 "%a: against \"%s\": %a\n",
789 __FUNCTION__,
790 Converted,
791 Result ? "match" : "no match"
792 ));
793 FreePool (Converted);
794 return Result;
795 }
796
797
798 /**
799
800 Set the boot order based on configuration retrieved from QEMU.
801
802 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
803 OpenFirmware device paths therein to UEFI device path fragments. Match the
804 translated fragments against BootOptionList, and rewrite the BootOrder NvVar
805 so that it corresponds to the order described in fw_cfg.
806
807 @param[in] BootOptionList A boot option list, created with
808 BdsLibEnumerateAllBootOption ().
809
810
811 @retval RETURN_SUCCESS BootOrder NvVar rewritten.
812
813 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.
814
815 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg
816 file, or no match found between the
817 "bootorder" fw_cfg file and BootOptionList.
818
819 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.
820
821 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
822
823 @return Values returned by gBS->LocateProtocol ()
824 or gRT->SetVariable ().
825
826 **/
827 RETURN_STATUS
828 SetBootOrderFromQemu (
829 IN CONST LIST_ENTRY *BootOptionList
830 )
831 {
832 RETURN_STATUS Status;
833
834 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;
835
836 FIRMWARE_CONFIG_ITEM FwCfgItem;
837 UINTN FwCfgSize;
838 CHAR8 *FwCfg;
839 CONST CHAR8 *FwCfgPtr;
840
841 BOOT_ORDER BootOrder;
842
843 UINTN TranslatedSize;
844 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
845
846 Status = gBS->LocateProtocol (
847 &gEfiDevicePathToTextProtocolGuid,
848 NULL, // optional registration key
849 (VOID **) &DevPathToText
850 );
851 if (Status != EFI_SUCCESS) {
852 return Status;
853 }
854
855 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
856 if (Status != RETURN_SUCCESS) {
857 return Status;
858 }
859
860 if (FwCfgSize == 0) {
861 return RETURN_NOT_FOUND;
862 }
863
864 FwCfg = AllocatePool (FwCfgSize);
865 if (FwCfg == NULL) {
866 return RETURN_OUT_OF_RESOURCES;
867 }
868
869 QemuFwCfgSelectItem (FwCfgItem);
870 QemuFwCfgReadBytes (FwCfgSize, FwCfg);
871 if (FwCfg[FwCfgSize - 1] != '\0') {
872 Status = RETURN_INVALID_PARAMETER;
873 goto ErrorFreeFwCfg;
874 }
875
876 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
877 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
878 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
879 FwCfgPtr = FwCfg;
880
881 BootOrder.Produced = 0;
882 BootOrder.Allocated = 1;
883 BootOrder.Data = AllocatePool (
884 BootOrder.Allocated * sizeof (*BootOrder.Data)
885 );
886 if (BootOrder.Data == NULL) {
887 Status = RETURN_OUT_OF_RESOURCES;
888 goto ErrorFreeFwCfg;
889 }
890
891 //
892 // translate each OpenFirmware path
893 //
894 TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
895 Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);
896 while (Status == RETURN_SUCCESS ||
897 Status == RETURN_UNSUPPORTED ||
898 Status == RETURN_BUFFER_TOO_SMALL) {
899 if (Status == RETURN_SUCCESS) {
900 CONST LIST_ENTRY *Link;
901
902 //
903 // match translated OpenFirmware path against all enumerated boot options
904 //
905 for (Link = BootOptionList->ForwardLink; Link != BootOptionList;
906 Link = Link->ForwardLink) {
907 CONST BDS_COMMON_OPTION *BootOption;
908
909 BootOption = CR (
910 Link,
911 BDS_COMMON_OPTION,
912 Link,
913 BDS_LOAD_OPTION_SIGNATURE
914 );
915 if (IS_LOAD_OPTION_TYPE (BootOption->Attribute, LOAD_OPTION_ACTIVE) &&
916 Match (
917 Translated,
918 TranslatedSize, // contains length, not size, in CHAR16's here
919 BootOption->DevicePath,
920 DevPathToText
921 )
922 ) {
923 //
924 // match found, store ID and continue with next OpenFirmware path
925 //
926 Status = BootOrderAppend (&BootOrder, BootOption->BootCurrent);
927 if (Status != RETURN_SUCCESS) {
928 goto ErrorFreeBootOrder;
929 }
930 break;
931 }
932 } // scanned all enumerated boot options
933 } // translation successful
934
935 TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
936 Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);
937 } // scanning of OpenFirmware paths done
938
939 if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {
940 //
941 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
942 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
943 // attributes.
944 //
945 Status = gRT->SetVariable (
946 L"BootOrder",
947 &gEfiGlobalVariableGuid,
948 EFI_VARIABLE_NON_VOLATILE |
949 EFI_VARIABLE_BOOTSERVICE_ACCESS |
950 EFI_VARIABLE_RUNTIME_ACCESS,
951 BootOrder.Produced * sizeof (*BootOrder.Data),
952 BootOrder.Data
953 );
954 DEBUG ((
955 DEBUG_INFO,
956 "%a: setting BootOrder: %a\n",
957 __FUNCTION__,
958 Status == EFI_SUCCESS ? "success" : "error"
959 ));
960 }
961
962 ErrorFreeBootOrder:
963 FreePool (BootOrder.Data);
964
965 ErrorFreeFwCfg:
966 FreePool (FwCfg);
967
968 return Status;
969 }