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