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