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