]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c
OvmfPkg: remove PlatformBdsLib instance
[mirror_edk2.git] / OvmfPkg / Library / QemuBootOrderLib / QemuBootOrderLib.c
CommitLineData
2cd086a6 1/** @file\r
2 Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.\r
3\r
cca7475b 4 Copyright (C) 2012 - 2014, Red Hat, Inc.\r
32a22f09 5 Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
2cd086a6 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
19#include <Library/GenericBdsLib.h>\r
20#include <Library/UefiBootServicesTableLib.h>\r
21#include <Library/UefiRuntimeServicesTableLib.h>\r
22#include <Library/BaseLib.h>\r
23#include <Library/PrintLib.h>\r
863986b3 24#include <Library/DevicePathLib.h>\r
cca7475b 25#include <Library/QemuBootOrderLib.h>\r
43336916 26#include <Library/BaseMemoryLib.h>\r
2cd086a6 27#include <Guid/GlobalVariable.h>\r
43336916 28#include <Guid/VirtioMmioTransport.h>\r
2cd086a6 29\r
4fc18df9 30#include "ExtraRootBusMap.h"\r
2cd086a6 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
feca17fa
LE
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
2cd086a6 42\r
43/**\r
ec01afdb 44 Numbers of nodes in OpenFirmware device paths that are required and examined.\r
2cd086a6 45**/\r
2f9c55cc 46#define REQUIRED_PCI_OFW_NODES 2\r
43336916 47#define REQUIRED_MMIO_OFW_NODES 1\r
feca17fa 48#define EXAMINED_OFW_NODES 6\r
2cd086a6 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
dccf7678 133 return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');\r
2cd086a6 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
ca0d7c98 140 UINT64 array.\r
2cd086a6 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
ca0d7c98 180 OUT UINT64 *Result,\r
2cd086a6 181 IN OUT UINTN *NumResults\r
182 )\r
183{\r
184 UINTN Entry; // number of entry currently being parsed\r
ca0d7c98 185 UINT64 EntryVal; // value being constructed for current entry\r
2cd086a6 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
ca0d7c98 205 if (EntryVal > 0xFFFFFFFFFFFFFFFull) {\r
2cd086a6 206 return RETURN_INVALID_PARAMETER;\r
207 }\r
ca0d7c98 208 EntryVal = LShiftU64 (EntryVal, 4) | Val;\r
2cd086a6 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
32a22f09
LE
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
256 CONST BDS_COMMON_OPTION *BootOption; // reference only, no ownership\r
e13be08e 257 BOOLEAN Appended; // has been added to a BOOT_ORDER?\r
32a22f09
LE
258} ACTIVE_OPTION;\r
259\r
260\r
2cd086a6 261/**\r
262\r
e13be08e 263 Append an active boot option to BootOrder, reallocating the latter if needed.\r
2cd086a6 264\r
265 @param[in out] BootOrder The structure pointing to the array and holding\r
266 allocation and usage counters.\r
267\r
e13be08e
LE
268 @param[in] ActiveOption The active boot option whose ID should be\r
269 appended to the array.\r
2cd086a6 270\r
271\r
e13be08e 272 @retval RETURN_SUCCESS ID of ActiveOption appended.\r
2cd086a6 273\r
274 @retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.\r
275\r
276**/\r
277STATIC\r
278RETURN_STATUS\r
279BootOrderAppend (\r
e13be08e
LE
280 IN OUT BOOT_ORDER *BootOrder,\r
281 IN OUT ACTIVE_OPTION *ActiveOption\r
2cd086a6 282 )\r
283{\r
284 if (BootOrder->Produced == BootOrder->Allocated) {\r
285 UINTN AllocatedNew;\r
286 UINT16 *DataNew;\r
287\r
288 ASSERT (BootOrder->Allocated > 0);\r
289 AllocatedNew = BootOrder->Allocated * 2;\r
290 DataNew = ReallocatePool (\r
291 BootOrder->Allocated * sizeof (*BootOrder->Data),\r
292 AllocatedNew * sizeof (*DataNew),\r
293 BootOrder->Data\r
294 );\r
295 if (DataNew == NULL) {\r
296 return RETURN_OUT_OF_RESOURCES;\r
297 }\r
298 BootOrder->Allocated = AllocatedNew;\r
299 BootOrder->Data = DataNew;\r
300 }\r
301\r
e13be08e
LE
302 BootOrder->Data[BootOrder->Produced++] =\r
303 ActiveOption->BootOption->BootCurrent;\r
304 ActiveOption->Appended = TRUE;\r
2cd086a6 305 return RETURN_SUCCESS;\r
306}\r
307\r
308\r
32a22f09
LE
309/**\r
310\r
311 Create an array of ACTIVE_OPTION elements for a boot option list.\r
312\r
313 @param[in] BootOptionList A boot option list, created with\r
314 BdsLibEnumerateAllBootOption().\r
315\r
316 @param[out] ActiveOption Pointer to the first element in the new array.\r
317 The caller is responsible for freeing the array\r
318 with FreePool() after use.\r
319\r
320 @param[out] Count Number of elements in the new array.\r
321\r
322\r
323 @retval RETURN_SUCCESS The ActiveOption array has been created.\r
324\r
325 @retval RETURN_NOT_FOUND No active entry has been found in\r
326 BootOptionList.\r
327\r
328 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
329\r
330**/\r
331STATIC\r
332RETURN_STATUS\r
333CollectActiveOptions (\r
334 IN CONST LIST_ENTRY *BootOptionList,\r
335 OUT ACTIVE_OPTION **ActiveOption,\r
336 OUT UINTN *Count\r
337 )\r
338{\r
339 UINTN ScanMode;\r
340\r
341 *ActiveOption = NULL;\r
342\r
343 //\r
344 // Scan the list twice:\r
345 // - count active entries,\r
346 // - store links to active entries.\r
347 //\r
348 for (ScanMode = 0; ScanMode < 2; ++ScanMode) {\r
349 CONST LIST_ENTRY *Link;\r
350\r
351 Link = BootOptionList->ForwardLink;\r
352 *Count = 0;\r
353 while (Link != BootOptionList) {\r
354 CONST BDS_COMMON_OPTION *Current;\r
355\r
356 Current = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);\r
357 if (IS_LOAD_OPTION_TYPE (Current->Attribute, LOAD_OPTION_ACTIVE)) {\r
358 if (ScanMode == 1) {\r
359 (*ActiveOption)[*Count].BootOption = Current;\r
e13be08e 360 (*ActiveOption)[*Count].Appended = FALSE;\r
32a22f09
LE
361 }\r
362 ++*Count;\r
363 }\r
364 Link = Link->ForwardLink;\r
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
2cd086a6 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
2f9c55cc 551 Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path\r
2cd086a6 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
4fc18df9
LE
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
2cd086a6 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
4fc18df9
LE
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
2cd086a6 589**/\r
590STATIC\r
591RETURN_STATUS\r
2f9c55cc 592TranslatePciOfwNodes (\r
4fc18df9
LE
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
2cd086a6 598 )\r
599{\r
4fc18df9
LE
600 UINT32 PciRoot;\r
601 CHAR8 *Comma;\r
feca17fa
LE
602 UINTN FirstNonBridge;\r
603 CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];\r
604 UINTN BridgesLen;\r
ca0d7c98 605 UINT64 PciDevFun[2];\r
2cd086a6 606 UINTN NumEntries;\r
607 UINTN Written;\r
608\r
609 //\r
4fc18df9
LE
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
2cd086a6 621 //\r
2f9c55cc 622 if (NumNodes < REQUIRED_PCI_OFW_NODES ||\r
2cd086a6 623 !SubstringEq (OfwNode[0].DriverName, "pci")\r
624 ) {\r
625 return RETURN_UNSUPPORTED;\r
626 }\r
feca17fa 627\r
4fc18df9
LE
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
feca17fa
LE
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
2cd086a6 709 PciDevFun[1] = 0;\r
710 NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
711 if (ParseUnitAddressHexList (\r
feca17fa 712 OfwNode[FirstNonBridge].UnitAddress,\r
2cd086a6 713 PciDevFun,\r
714 &NumEntries\r
715 ) != RETURN_SUCCESS\r
716 ) {\r
717 return RETURN_UNSUPPORTED;\r
718 }\r
719\r
feca17fa
LE
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
2cd086a6 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
ca0d7c98
LE
741 UINT64 Secondary;\r
742 UINT64 Slave;\r
2cd086a6 743\r
744 NumEntries = 1;\r
745 if (ParseUnitAddressHexList (\r
feca17fa 746 OfwNode[FirstNonBridge + 1].UnitAddress,\r
2cd086a6 747 &Secondary,\r
748 &NumEntries\r
749 ) != RETURN_SUCCESS ||\r
750 Secondary > 1 ||\r
751 ParseUnitAddressHexList (\r
feca17fa 752 OfwNode[FirstNonBridge + 2].UnitAddress,\r
2cd086a6 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
4fc18df9
LE
764 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",\r
765 PciRoot,\r
feca17fa 766 Bridges,\r
2cd086a6 767 PciDevFun[0],\r
768 PciDevFun[1],\r
769 Secondary ? "Secondary" : "Primary",\r
770 Slave ? "Slave" : "Master"\r
771 );\r
0febef91
LE
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
1da76166
LE
789 // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)\r
790 // ^ ^ ^\r
791 // | | LUN (always 0 on Q35)\r
0febef91 792 // | port multiplier port number,\r
1da76166 793 // | always 0xFFFF on Q35\r
0febef91
LE
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
1da76166 808 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",\r
0febef91
LE
809 PciRoot,\r
810 Bridges,\r
811 PciDevFun[0],\r
812 PciDevFun[1],\r
813 Channel\r
814 );\r
feca17fa
LE
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
2cd086a6 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
ca0d7c98 836 UINT64 AcpiUid;\r
2cd086a6 837\r
838 NumEntries = 1;\r
839 if (ParseUnitAddressHexList (\r
feca17fa 840 OfwNode[FirstNonBridge + 2].UnitAddress,\r
2cd086a6 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
4fc18df9
LE
852 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
853 PciRoot,\r
feca17fa 854 Bridges,\r
2cd086a6 855 PciDevFun[0],\r
856 PciDevFun[1],\r
857 AcpiUid\r
858 );\r
feca17fa
LE
859 } else if (NumNodes >= FirstNonBridge + 2 &&\r
860 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
861 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")\r
e06a4cd1 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
875 // PciRoot(0x0)/Pci(0x6,0x0)/HD( -- if PCI function is 0 or absent\r
876 // PciRoot(0x0)/Pci(0x6,0x3)/HD( -- if PCI function is present and nonzero\r
877 //\r
878 Written = UnicodeSPrintAsciiFormat (\r
879 Translated,\r
880 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
4fc18df9
LE
881 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/HD(",\r
882 PciRoot,\r
feca17fa 883 Bridges,\r
e06a4cd1 884 PciDevFun[0],\r
885 PciDevFun[1]\r
886 );\r
feca17fa
LE
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
d2bf9913 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
ca0d7c98 910 UINT64 TargetLun[2];\r
d2bf9913 911\r
912 TargetLun[1] = 0;\r
913 NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
914 if (ParseUnitAddressHexList (\r
feca17fa 915 OfwNode[FirstNonBridge + 2].UnitAddress,\r
d2bf9913 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
4fc18df9
LE
926 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
927 PciRoot,\r
feca17fa 928 Bridges,\r
d2bf9913 929 PciDevFun[0],\r
930 PciDevFun[1],\r
931 TargetLun[0],\r
932 TargetLun[1]\r
933 );\r
d7c0dfae
LE
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
961 RequiredEntries = sizeof (Namespace) / sizeof (Namespace[0]);\r
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
3f4b1489 989 } else {\r
e7a7e480 990 //\r
3f4b1489 991 // Generic OpenFirmware device path for PCI devices:\r
e7a7e480 992 //\r
3f4b1489
PB
993 // /pci@i0cf8/ethernet@3[,2]\r
994 // ^ ^\r
e7a7e480 995 // | PCI slot[, function] holding Ethernet card\r
996 // PCI root at system bus port, PIO\r
997 //\r
998 // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
999 //\r
3f4b1489
PB
1000 // PciRoot(0x0)/Pci(0x3,0x0)\r
1001 // PciRoot(0x0)/Pci(0x3,0x2)\r
e7a7e480 1002 //\r
1003 Written = UnicodeSPrintAsciiFormat (\r
1004 Translated,\r
1005 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
4fc18df9
LE
1006 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
1007 PciRoot,\r
feca17fa 1008 Bridges,\r
e7a7e480 1009 PciDevFun[0],\r
1010 PciDevFun[1]\r
1011 );\r
cdde6ddf 1012 }\r
2cd086a6 1013\r
1014 //\r
1015 // There's no way to differentiate between "completely used up without\r
1016 // truncation" and "truncated", so treat the former as the latter, and return\r
1017 // success only for "some room left unused".\r
1018 //\r
1019 if (Written + 1 < *TranslatedSize) {\r
1020 *TranslatedSize = Written;\r
1021 return RETURN_SUCCESS;\r
1022 }\r
1023\r
1024 return RETURN_BUFFER_TOO_SMALL;\r
1025}\r
1026\r
1027\r
43336916
LE
1028//\r
1029// A type providing easy raw access to the base address of a virtio-mmio\r
1030// transport.\r
1031//\r
1032typedef union {\r
1033 UINT64 Uint64;\r
1034 UINT8 Raw[8];\r
1035} VIRTIO_MMIO_BASE_ADDRESS;\r
1036\r
1037\r
1038/**\r
1039\r
1040 Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device\r
1041 path fragment.\r
1042\r
1043 @param[in] OfwNode Array of OpenFirmware device nodes to\r
1044 translate, constituting the beginning of an\r
1045 OpenFirmware device path.\r
1046\r
1047 @param[in] NumNodes Number of elements in OfwNode.\r
1048\r
1049 @param[out] Translated Destination array receiving the UEFI path\r
1050 fragment, allocated by the caller. If the\r
1051 return value differs from RETURN_SUCCESS, its\r
1052 contents is indeterminate.\r
1053\r
1054 @param[in out] TranslatedSize On input, the number of CHAR16's in\r
1055 Translated. On RETURN_SUCCESS this parameter\r
1056 is assigned the number of non-NUL CHAR16's\r
1057 written to Translated. In case of other return\r
1058 values, TranslatedSize is indeterminate.\r
1059\r
1060\r
1061 @retval RETURN_SUCCESS Translation successful.\r
1062\r
1063 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
1064 of bytes provided.\r
1065\r
1066 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
1067 be translated in the current implementation.\r
1068\r
1069**/\r
1070STATIC\r
1071RETURN_STATUS\r
1072TranslateMmioOfwNodes (\r
1073 IN CONST OFW_NODE *OfwNode,\r
1074 IN UINTN NumNodes,\r
1075 OUT CHAR16 *Translated,\r
1076 IN OUT UINTN *TranslatedSize\r
1077 )\r
1078{\r
1079 VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;\r
1080 CHAR16 VenHwString[60 + 1];\r
1081 UINTN NumEntries;\r
1082 UINTN Written;\r
1083\r
1084 //\r
1085 // Get the base address of the virtio-mmio transport.\r
1086 //\r
1087 if (NumNodes < REQUIRED_MMIO_OFW_NODES ||\r
1088 !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")\r
1089 ) {\r
1090 return RETURN_UNSUPPORTED;\r
1091 }\r
1092 NumEntries = 1;\r
1093 if (ParseUnitAddressHexList (\r
1094 OfwNode[0].UnitAddress,\r
1095 &VirtioMmioBase.Uint64,\r
1096 &NumEntries\r
1097 ) != RETURN_SUCCESS\r
1098 ) {\r
1099 return RETURN_UNSUPPORTED;\r
1100 }\r
1101\r
1102 UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,\r
1103 "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,\r
1104 VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],\r
1105 VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],\r
1106 VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);\r
1107\r
1108 if (NumNodes >= 2 &&\r
1109 SubstringEq (OfwNode[1].DriverName, "disk")) {\r
1110 //\r
1111 // OpenFirmware device path (virtio-blk disk):\r
1112 //\r
1113 // /virtio-mmio@000000000a003c00/disk@0,0\r
1114 // ^ ^ ^\r
1115 // | fixed\r
1116 // base address of virtio-mmio register block\r
1117 //\r
1118 // UEFI device path prefix:\r
1119 //\r
1120 // <VenHwString>/HD(\r
1121 //\r
1122 Written = UnicodeSPrintAsciiFormat (\r
1123 Translated,\r
1124 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
1125 "%s/HD(",\r
1126 VenHwString\r
1127 );\r
1128 } else if (NumNodes >= 3 &&\r
1129 SubstringEq (OfwNode[1].DriverName, "channel") &&\r
1130 SubstringEq (OfwNode[2].DriverName, "disk")) {\r
1131 //\r
1132 // OpenFirmware device path (virtio-scsi disk):\r
1133 //\r
1134 // /virtio-mmio@000000000a003a00/channel@0/disk@2,3\r
1135 // ^ ^ ^ ^\r
1136 // | | | LUN\r
1137 // | | target\r
1138 // | channel (unused, fixed 0)\r
1139 // base address of virtio-mmio register block\r
1140 //\r
1141 // UEFI device path prefix:\r
1142 //\r
1143 // <VenHwString>/Scsi(0x2,0x3)\r
1144 //\r
1145 UINT64 TargetLun[2];\r
1146\r
1147 TargetLun[1] = 0;\r
1148 NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
1149 if (ParseUnitAddressHexList (\r
1150 OfwNode[2].UnitAddress,\r
1151 TargetLun,\r
1152 &NumEntries\r
1153 ) != RETURN_SUCCESS\r
1154 ) {\r
1155 return RETURN_UNSUPPORTED;\r
1156 }\r
1157\r
1158 Written = UnicodeSPrintAsciiFormat (\r
1159 Translated,\r
1160 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
1161 "%s/Scsi(0x%Lx,0x%Lx)",\r
1162 VenHwString,\r
1163 TargetLun[0],\r
1164 TargetLun[1]\r
1165 );\r
1166 } else if (NumNodes >= 2 &&\r
1167 SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {\r
1168 //\r
1169 // OpenFirmware device path (virtio-net NIC):\r
1170 //\r
1171 // /virtio-mmio@000000000a003e00/ethernet-phy@0\r
1172 // ^ ^\r
1173 // | fixed\r
1174 // base address of virtio-mmio register block\r
1175 //\r
1176 // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
1177 //\r
1178 // <VenHwString>/MAC(\r
1179 //\r
1180 Written = UnicodeSPrintAsciiFormat (\r
1181 Translated,\r
1182 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
1183 "%s/MAC(",\r
1184 VenHwString\r
1185 );\r
1186 } else {\r
1187 return RETURN_UNSUPPORTED;\r
1188 }\r
1189\r
1190 //\r
1191 // There's no way to differentiate between "completely used up without\r
1192 // truncation" and "truncated", so treat the former as the latter, and return\r
1193 // success only for "some room left unused".\r
1194 //\r
1195 if (Written + 1 < *TranslatedSize) {\r
1196 *TranslatedSize = Written;\r
1197 return RETURN_SUCCESS;\r
1198 }\r
1199\r
1200 return RETURN_BUFFER_TOO_SMALL;\r
1201}\r
1202\r
1203\r
2f9c55cc
LE
1204/**\r
1205\r
1206 Translate an array of OpenFirmware device nodes to a UEFI device path\r
1207 fragment.\r
1208\r
1209 @param[in] OfwNode Array of OpenFirmware device nodes to\r
1210 translate, constituting the beginning of an\r
1211 OpenFirmware device path.\r
1212\r
1213 @param[in] NumNodes Number of elements in OfwNode.\r
1214\r
4fc18df9
LE
1215 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
1216 CreateExtraRootBusMap(), to be used for\r
1217 translating positions of extra root buses to\r
1218 bus numbers.\r
1219\r
2f9c55cc
LE
1220 @param[out] Translated Destination array receiving the UEFI path\r
1221 fragment, allocated by the caller. If the\r
1222 return value differs from RETURN_SUCCESS, its\r
1223 contents is indeterminate.\r
1224\r
1225 @param[in out] TranslatedSize On input, the number of CHAR16's in\r
1226 Translated. On RETURN_SUCCESS this parameter\r
1227 is assigned the number of non-NUL CHAR16's\r
1228 written to Translated. In case of other return\r
1229 values, TranslatedSize is indeterminate.\r
1230\r
1231\r
1232 @retval RETURN_SUCCESS Translation successful.\r
1233\r
1234 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
1235 of bytes provided.\r
1236\r
1237 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
1238 be translated in the current implementation.\r
1239\r
4fc18df9
LE
1240 @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has\r
1241 been (partially) recognized, but it contains\r
1242 a logic error / doesn't match system state.\r
1243\r
2f9c55cc
LE
1244**/\r
1245STATIC\r
1246RETURN_STATUS\r
1247TranslateOfwNodes (\r
4fc18df9
LE
1248 IN CONST OFW_NODE *OfwNode,\r
1249 IN UINTN NumNodes,\r
1250 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
1251 OUT CHAR16 *Translated,\r
1252 IN OUT UINTN *TranslatedSize\r
2f9c55cc
LE
1253 )\r
1254{\r
1255 RETURN_STATUS Status;\r
1256\r
1257 Status = RETURN_UNSUPPORTED;\r
1258\r
1259 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
4fc18df9
LE
1260 Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,\r
1261 Translated, TranslatedSize);\r
2f9c55cc 1262 }\r
43336916
LE
1263 if (Status == RETURN_UNSUPPORTED &&\r
1264 FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {\r
1265 Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,\r
1266 TranslatedSize);\r
1267 }\r
2f9c55cc
LE
1268 return Status;\r
1269}\r
1270\r
2cd086a6 1271/**\r
1272\r
1273 Translate an OpenFirmware device path fragment to a UEFI device path\r
1274 fragment, and advance in the input string.\r
1275\r
1276 @param[in out] Ptr Address of the pointer pointing to the start\r
1277 of the path string. After successful\r
1278 translation (RETURN_SUCCESS) or at least\r
1279 successful parsing (RETURN_UNSUPPORTED,\r
1280 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the\r
1281 byte immediately following the consumed\r
1282 characters. In other error cases, it points to\r
1283 the byte that caused the error.\r
1284\r
4fc18df9
LE
1285 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
1286 CreateExtraRootBusMap(), to be used for\r
1287 translating positions of extra root buses to\r
1288 bus numbers.\r
1289\r
2cd086a6 1290 @param[out] Translated Destination array receiving the UEFI path\r
1291 fragment, allocated by the caller. If the\r
1292 return value differs from RETURN_SUCCESS, its\r
1293 contents is indeterminate.\r
1294\r
1295 @param[in out] TranslatedSize On input, the number of CHAR16's in\r
1296 Translated. On RETURN_SUCCESS this parameter\r
1297 is assigned the number of non-NUL CHAR16's\r
1298 written to Translated. In case of other return\r
1299 values, TranslatedSize is indeterminate.\r
1300\r
1301\r
1302 @retval RETURN_SUCCESS Translation successful.\r
1303\r
1304 @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed\r
1305 successfully, but its translation did not\r
1306 fit into the number of bytes provided.\r
1307 Further calls to this function are\r
1308 possible.\r
1309\r
1310 @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed\r
1311 successfully, but it can't be translated in\r
1312 the current implementation. Further calls\r
1313 to this function are possible.\r
1314\r
4fc18df9
LE
1315 @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been\r
1316 (partially) recognized, but it contains a\r
1317 logic error / doesn't match system state.\r
1318 Further calls to this function are\r
1319 possible.\r
1320\r
c3cf8daa
LE
1321 @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was\r
1322 pointing to the empty string or "HALT". On\r
1323 output, *Ptr points to the empty string\r
1324 (ie. "HALT" is consumed transparently when\r
1325 present).\r
2cd086a6 1326\r
1327 @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.\r
1328\r
1329**/\r
1330STATIC\r
1331RETURN_STATUS\r
1332TranslateOfwPath (\r
4fc18df9
LE
1333 IN OUT CONST CHAR8 **Ptr,\r
1334 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
1335 OUT CHAR16 *Translated,\r
1336 IN OUT UINTN *TranslatedSize\r
2cd086a6 1337 )\r
1338{\r
1339 UINTN NumNodes;\r
1340 RETURN_STATUS Status;\r
ec01afdb 1341 OFW_NODE Node[EXAMINED_OFW_NODES];\r
2cd086a6 1342 BOOLEAN IsFinal;\r
1343 OFW_NODE Skip;\r
1344\r
489c3142 1345 IsFinal = FALSE;\r
2cd086a6 1346 NumNodes = 0;\r
c3cf8daa
LE
1347 if (AsciiStrCmp (*Ptr, "HALT") == 0) {\r
1348 *Ptr += 4;\r
1349 Status = RETURN_NOT_FOUND;\r
1350 } else {\r
1351 Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);\r
1352 }\r
2cd086a6 1353\r
1354 if (Status == RETURN_NOT_FOUND) {\r
1355 DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));\r
1356 return RETURN_NOT_FOUND;\r
1357 }\r
1358\r
1359 while (Status == RETURN_SUCCESS && !IsFinal) {\r
1360 ++NumNodes;\r
1361 Status = ParseOfwNode (\r
1362 Ptr,\r
ec01afdb 1363 (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,\r
2cd086a6 1364 &IsFinal\r
1365 );\r
1366 }\r
1367\r
1368 switch (Status) {\r
1369 case RETURN_SUCCESS:\r
1370 ++NumNodes;\r
1371 break;\r
1372\r
1373 case RETURN_INVALID_PARAMETER:\r
1374 DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));\r
1375 return RETURN_INVALID_PARAMETER;\r
1376\r
1377 default:\r
1378 ASSERT (0);\r
1379 }\r
1380\r
1381 Status = TranslateOfwNodes (\r
1382 Node,\r
ec01afdb 1383 NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,\r
4fc18df9 1384 ExtraPciRoots,\r
2cd086a6 1385 Translated,\r
1386 TranslatedSize);\r
1387 switch (Status) {\r
1388 case RETURN_SUCCESS:\r
1389 DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));\r
1390 break;\r
1391\r
1392 case RETURN_BUFFER_TOO_SMALL:\r
1393 DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));\r
1394 break;\r
1395\r
1396 case RETURN_UNSUPPORTED:\r
1397 DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
1398 break;\r
1399\r
4fc18df9
LE
1400 case RETURN_PROTOCOL_ERROR:\r
1401 DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",\r
1402 __FUNCTION__));\r
1403 break;\r
1404\r
2cd086a6 1405 default:\r
1406 ASSERT (0);\r
1407 }\r
1408 return Status;\r
1409}\r
1410\r
1411\r
1412/**\r
1413\r
1414 Convert the UEFI DevicePath to full text representation with DevPathToText,\r
1415 then match the UEFI device path fragment in Translated against it.\r
1416\r
1417 @param[in] Translated UEFI device path fragment, translated from\r
1418 OpenFirmware format, to search for.\r
1419\r
1420 @param[in] TranslatedLength The length of Translated in CHAR16's.\r
1421\r
1422 @param[in] DevicePath Boot option device path whose textual rendering\r
1423 to search in.\r
1424\r
1425 @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.\r
1426\r
1427\r
1428 @retval TRUE If Translated was found at the beginning of DevicePath after\r
1429 converting the latter to text.\r
1430\r
1431 @retval FALSE If DevicePath was NULL, or it could not be converted, or there\r
1432 was no match.\r
1433\r
1434**/\r
1435STATIC\r
1436BOOLEAN\r
1437Match (\r
1438 IN CONST CHAR16 *Translated,\r
1439 IN UINTN TranslatedLength,\r
863986b3 1440 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
2cd086a6 1441 )\r
1442{\r
1443 CHAR16 *Converted;\r
1444 BOOLEAN Result;\r
1445\r
863986b3
RN
1446 Converted = ConvertDevicePathToText (\r
1447 DevicePath,\r
1448 FALSE, // DisplayOnly\r
1449 FALSE // AllowShortcuts\r
1450 );\r
2cd086a6 1451 if (Converted == NULL) {\r
1452 return FALSE;\r
1453 }\r
1454\r
64378ce1
LE
1455 //\r
1456 // Attempt to expand any relative UEFI device path starting with HD() to an\r
1457 // absolute device path first. The logic imitates BdsLibBootViaBootOption().\r
1458 // We don't have to free the absolute device path,\r
1459 // BdsExpandPartitionPartialDevicePathToFull() has internal caching.\r
1460 //\r
1461 Result = FALSE;\r
1462 if (DevicePathType (DevicePath) == MEDIA_DEVICE_PATH &&\r
1463 DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP) {\r
1464 EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;\r
1465 CHAR16 *AbsConverted;\r
1466\r
1467 AbsDevicePath = BdsExpandPartitionPartialDevicePathToFull (\r
1468 (HARDDRIVE_DEVICE_PATH *) DevicePath);\r
1469 if (AbsDevicePath == NULL) {\r
1470 goto Exit;\r
1471 }\r
1472 AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);\r
1473 if (AbsConverted == NULL) {\r
1474 goto Exit;\r
1475 }\r
1476 DEBUG ((DEBUG_VERBOSE,\r
1477 "%a: expanded relative device path \"%s\" for prefix matching\n",\r
1478 __FUNCTION__, Converted));\r
1479 FreePool (Converted);\r
1480 Converted = AbsConverted;\r
1481 }\r
1482\r
2cd086a6 1483 //\r
1484 // Is Translated a prefix of Converted?\r
1485 //\r
dccf7678 1486 Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);\r
2cd086a6 1487 DEBUG ((\r
1488 DEBUG_VERBOSE,\r
1489 "%a: against \"%s\": %a\n",\r
1490 __FUNCTION__,\r
1491 Converted,\r
1492 Result ? "match" : "no match"\r
1493 ));\r
64378ce1 1494Exit:\r
2cd086a6 1495 FreePool (Converted);\r
1496 return Result;\r
1497}\r
1498\r
1499\r
838b5b00
LE
1500/**\r
1501 Append some of the unselected active boot options to the boot order.\r
1502\r
1503 This function should accommodate any further policy changes in "boot option\r
1504 survival". Currently we're adding back everything that starts with neither\r
43336916 1505 PciRoot() nor HD() nor a virtio-mmio VenHw() node.\r
838b5b00
LE
1506\r
1507 @param[in,out] BootOrder The structure holding the boot order to\r
1508 complete. The caller is responsible for\r
1509 initializing (and potentially populating) it\r
1510 before calling this function.\r
1511\r
1512 @param[in,out] ActiveOption The array of active boot options to scan.\r
1513 Entries marked as Appended will be skipped.\r
1514 Those of the rest that satisfy the survival\r
1515 policy will be added to BootOrder with\r
1516 BootOrderAppend().\r
1517\r
1518 @param[in] ActiveCount Number of elements in ActiveOption.\r
1519\r
1520\r
1521 @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot\r
1522 options.\r
1523\r
1524 @return Error codes returned by BootOrderAppend().\r
1525**/\r
1526STATIC\r
1527RETURN_STATUS\r
1528BootOrderComplete (\r
1529 IN OUT BOOT_ORDER *BootOrder,\r
1530 IN OUT ACTIVE_OPTION *ActiveOption,\r
1531 IN UINTN ActiveCount\r
1532 )\r
1533{\r
1534 RETURN_STATUS Status;\r
1535 UINTN Idx;\r
1536\r
1537 Status = RETURN_SUCCESS;\r
1538 Idx = 0;\r
1539 while (!RETURN_ERROR (Status) && Idx < ActiveCount) {\r
1540 if (!ActiveOption[Idx].Appended) {\r
1541 CONST BDS_COMMON_OPTION *Current;\r
1542 CONST EFI_DEVICE_PATH_PROTOCOL *FirstNode;\r
1543\r
1544 Current = ActiveOption[Idx].BootOption;\r
1545 FirstNode = Current->DevicePath;\r
1546 if (FirstNode != NULL) {\r
1547 CHAR16 *Converted;\r
1548 STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";\r
1549 BOOLEAN Keep;\r
1550\r
1551 Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);\r
1552 if (Converted == NULL) {\r
1553 Converted = ConvFallBack;\r
1554 }\r
1555\r
1556 Keep = TRUE;\r
1557 if (DevicePathType(FirstNode) == MEDIA_DEVICE_PATH &&\r
1558 DevicePathSubType(FirstNode) == MEDIA_HARDDRIVE_DP) {\r
1559 //\r
1560 // drop HD()\r
1561 //\r
1562 Keep = FALSE;\r
1563 } else if (DevicePathType(FirstNode) == ACPI_DEVICE_PATH &&\r
1564 DevicePathSubType(FirstNode) == ACPI_DP) {\r
1565 ACPI_HID_DEVICE_PATH *Acpi;\r
1566\r
1567 Acpi = (ACPI_HID_DEVICE_PATH *) FirstNode;\r
1568 if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST &&\r
1569 EISA_ID_TO_NUM (Acpi->HID) == 0x0a03) {\r
1570 //\r
2f9c55cc
LE
1571 // drop PciRoot() if we enabled the user to select PCI-like boot\r
1572 // options, by providing translation for such OFW device path\r
1573 // fragments\r
838b5b00 1574 //\r
2f9c55cc 1575 Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);\r
838b5b00 1576 }\r
43336916
LE
1577 } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&\r
1578 DevicePathSubType(FirstNode) == HW_VENDOR_DP) {\r
1579 VENDOR_DEVICE_PATH *VenHw;\r
1580\r
1581 VenHw = (VENDOR_DEVICE_PATH *)FirstNode;\r
1582 if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {\r
1583 //\r
1584 // drop virtio-mmio if we enabled the user to select boot options\r
1585 // referencing such device paths\r
1586 //\r
1587 Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);\r
1588 }\r
838b5b00
LE
1589 }\r
1590\r
1591 if (Keep) {\r
1592 Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);\r
1593 if (!RETURN_ERROR (Status)) {\r
1594 DEBUG ((DEBUG_VERBOSE, "%a: keeping \"%s\"\n", __FUNCTION__,\r
1595 Converted));\r
1596 }\r
1597 } else {\r
1598 DEBUG ((DEBUG_VERBOSE, "%a: dropping \"%s\"\n", __FUNCTION__,\r
1599 Converted));\r
1600 }\r
1601\r
1602 if (Converted != ConvFallBack) {\r
1603 FreePool (Converted);\r
1604 }\r
1605 }\r
1606 }\r
1607 ++Idx;\r
1608 }\r
1609 return Status;\r
1610}\r
1611\r
1612\r
1c9135a2
LE
1613/**\r
1614 Delete Boot#### variables that stand for such active boot options that have\r
1615 been dropped (ie. have not been selected by either matching or "survival\r
1616 policy").\r
1617\r
1618 @param[in] ActiveOption The array of active boot options to scan. Each\r
1619 entry not marked as appended will trigger the\r
1620 deletion of the matching Boot#### variable.\r
1621\r
1622 @param[in] ActiveCount Number of elements in ActiveOption.\r
1623**/\r
1624STATIC\r
1625VOID\r
1626PruneBootVariables (\r
1627 IN CONST ACTIVE_OPTION *ActiveOption,\r
1628 IN UINTN ActiveCount\r
1629 )\r
1630{\r
1631 UINTN Idx;\r
1632\r
1633 for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
1634 if (!ActiveOption[Idx].Appended) {\r
1635 CHAR16 VariableName[9];\r
1636\r
1637 UnicodeSPrintAsciiFormat (VariableName, sizeof VariableName, "Boot%04x",\r
1638 ActiveOption[Idx].BootOption->BootCurrent);\r
1639\r
1640 //\r
1641 // "The space consumed by the deleted variable may not be available until\r
1642 // the next power cycle", but that's good enough.\r
1643 //\r
1644 gRT->SetVariable (VariableName, &gEfiGlobalVariableGuid,\r
1645 0, // Attributes, 0 means deletion\r
1646 0, // DataSize, 0 means deletion\r
1647 NULL // Data\r
1648 );\r
1649 }\r
1650 }\r
1651}\r
1652\r
1653\r
2cd086a6 1654/**\r
1655\r
1656 Set the boot order based on configuration retrieved from QEMU.\r
1657\r
1658 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the\r
1659 OpenFirmware device paths therein to UEFI device path fragments. Match the\r
1660 translated fragments against BootOptionList, and rewrite the BootOrder NvVar\r
1661 so that it corresponds to the order described in fw_cfg.\r
1662\r
1663 @param[in] BootOptionList A boot option list, created with\r
1664 BdsLibEnumerateAllBootOption ().\r
1665\r
1666\r
1667 @retval RETURN_SUCCESS BootOrder NvVar rewritten.\r
1668\r
1669 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.\r
1670\r
1671 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg\r
1672 file, or no match found between the\r
1673 "bootorder" fw_cfg file and BootOptionList.\r
1674\r
1675 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.\r
1676\r
1677 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
1678\r
1679 @return Values returned by gBS->LocateProtocol ()\r
1680 or gRT->SetVariable ().\r
1681\r
1682**/\r
1683RETURN_STATUS\r
1684SetBootOrderFromQemu (\r
1685 IN CONST LIST_ENTRY *BootOptionList\r
1686 )\r
1687{\r
1688 RETURN_STATUS Status;\r
2cd086a6 1689 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
1690 UINTN FwCfgSize;\r
1691 CHAR8 *FwCfg;\r
1692 CONST CHAR8 *FwCfgPtr;\r
1693\r
1694 BOOT_ORDER BootOrder;\r
32a22f09
LE
1695 ACTIVE_OPTION *ActiveOption;\r
1696 UINTN ActiveCount;\r
2cd086a6 1697\r
4fc18df9
LE
1698 EXTRA_ROOT_BUS_MAP *ExtraPciRoots;\r
1699\r
2cd086a6 1700 UINTN TranslatedSize;\r
1701 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];\r
1702\r
2cd086a6 1703 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
1704 if (Status != RETURN_SUCCESS) {\r
1705 return Status;\r
1706 }\r
1707\r
1708 if (FwCfgSize == 0) {\r
1709 return RETURN_NOT_FOUND;\r
1710 }\r
1711\r
1712 FwCfg = AllocatePool (FwCfgSize);\r
1713 if (FwCfg == NULL) {\r
1714 return RETURN_OUT_OF_RESOURCES;\r
1715 }\r
1716\r
1717 QemuFwCfgSelectItem (FwCfgItem);\r
1718 QemuFwCfgReadBytes (FwCfgSize, FwCfg);\r
1719 if (FwCfg[FwCfgSize - 1] != '\0') {\r
1720 Status = RETURN_INVALID_PARAMETER;\r
1721 goto ErrorFreeFwCfg;\r
1722 }\r
1723\r
1724 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));\r
1725 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));\r
1726 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));\r
1727 FwCfgPtr = FwCfg;\r
1728\r
1729 BootOrder.Produced = 0;\r
1730 BootOrder.Allocated = 1;\r
1731 BootOrder.Data = AllocatePool (\r
1732 BootOrder.Allocated * sizeof (*BootOrder.Data)\r
1733 );\r
1734 if (BootOrder.Data == NULL) {\r
1735 Status = RETURN_OUT_OF_RESOURCES;\r
1736 goto ErrorFreeFwCfg;\r
1737 }\r
1738\r
32a22f09
LE
1739 Status = CollectActiveOptions (BootOptionList, &ActiveOption, &ActiveCount);\r
1740 if (RETURN_ERROR (Status)) {\r
1741 goto ErrorFreeBootOrder;\r
1742 }\r
1743\r
4fc18df9
LE
1744 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
1745 Status = CreateExtraRootBusMap (&ExtraPciRoots);\r
1746 if (EFI_ERROR (Status)) {\r
1747 goto ErrorFreeActiveOption;\r
1748 }\r
1749 } else {\r
1750 ExtraPciRoots = NULL;\r
1751 }\r
1752\r
2cd086a6 1753 //\r
1754 // translate each OpenFirmware path\r
1755 //\r
1756 TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
4fc18df9
LE
1757 Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
1758 &TranslatedSize);\r
2cd086a6 1759 while (Status == RETURN_SUCCESS ||\r
1760 Status == RETURN_UNSUPPORTED ||\r
4fc18df9 1761 Status == RETURN_PROTOCOL_ERROR ||\r
2cd086a6 1762 Status == RETURN_BUFFER_TOO_SMALL) {\r
1763 if (Status == RETURN_SUCCESS) {\r
32a22f09 1764 UINTN Idx;\r
2cd086a6 1765\r
1766 //\r
32a22f09 1767 // match translated OpenFirmware path against all active boot options\r
2cd086a6 1768 //\r
32a22f09
LE
1769 for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
1770 if (Match (\r
2cd086a6 1771 Translated,\r
1772 TranslatedSize, // contains length, not size, in CHAR16's here\r
32a22f09 1773 ActiveOption[Idx].BootOption->DevicePath\r
2cd086a6 1774 )\r
1775 ) {\r
1776 //\r
1777 // match found, store ID and continue with next OpenFirmware path\r
1778 //\r
e13be08e 1779 Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
2cd086a6 1780 if (Status != RETURN_SUCCESS) {\r
4fc18df9 1781 goto ErrorFreeExtraPciRoots;\r
2cd086a6 1782 }\r
1783 break;\r
1784 }\r
32a22f09 1785 } // scanned all active boot options\r
2cd086a6 1786 } // translation successful\r
1787\r
1788 TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
4fc18df9
LE
1789 Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
1790 &TranslatedSize);\r
2cd086a6 1791 } // scanning of OpenFirmware paths done\r
1792\r
1793 if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {\r
1794 //\r
1795 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.\r
838b5b00
LE
1796 // Some of the active boot options that have not been selected over fw_cfg\r
1797 // should be preserved at the end of the boot order.\r
1798 //\r
1799 Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
1800 if (RETURN_ERROR (Status)) {\r
4fc18df9 1801 goto ErrorFreeExtraPciRoots;\r
838b5b00
LE
1802 }\r
1803\r
1804 //\r
2cd086a6 1805 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required\r
1806 // attributes.\r
1807 //\r
1808 Status = gRT->SetVariable (\r
1809 L"BootOrder",\r
1810 &gEfiGlobalVariableGuid,\r
1811 EFI_VARIABLE_NON_VOLATILE |\r
1812 EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
1813 EFI_VARIABLE_RUNTIME_ACCESS,\r
1814 BootOrder.Produced * sizeof (*BootOrder.Data),\r
1815 BootOrder.Data\r
1816 );\r
1c9135a2
LE
1817 if (EFI_ERROR (Status)) {\r
1818 DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));\r
4fc18df9 1819 goto ErrorFreeExtraPciRoots;\r
1c9135a2
LE
1820 }\r
1821\r
1822 DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
1823 PruneBootVariables (ActiveOption, ActiveCount);\r
2cd086a6 1824 }\r
1825\r
4fc18df9
LE
1826ErrorFreeExtraPciRoots:\r
1827 if (ExtraPciRoots != NULL) {\r
1828 DestroyExtraRootBusMap (ExtraPciRoots);\r
1829 }\r
1830\r
32a22f09
LE
1831ErrorFreeActiveOption:\r
1832 FreePool (ActiveOption);\r
1833\r
2cd086a6 1834ErrorFreeBootOrder:\r
1835 FreePool (BootOrder.Data);\r
1836\r
1837ErrorFreeFwCfg:\r
1838 FreePool (FwCfg);\r
1839\r
1840 return Status;\r
1841}\r
9253c14d
LE
1842\r
1843\r
1844/**\r
1845 Calculate the number of seconds we should be showing the FrontPage progress\r
1846 bar for.\r
1847\r
1848 @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().\r
1849**/\r
1850UINT16\r
1851GetFrontPageTimeoutFromQemu (\r
1852 VOID\r
1853 )\r
1854{\r
1855 FIRMWARE_CONFIG_ITEM BootMenuWaitItem;\r
1856 UINTN BootMenuWaitSize;\r
1857\r
1858 QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);\r
1859 if (QemuFwCfgRead16 () == 0) {\r
1860 //\r
1861 // The user specified "-boot menu=off", or didn't specify "-boot\r
1862 // menu=(on|off)" at all. Return the platform default.\r
1863 //\r
1864 return PcdGet16 (PcdPlatformBootTimeOut);\r
1865 }\r
1866\r
1867 if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,\r
1868 &BootMenuWaitSize)) ||\r
1869 BootMenuWaitSize != sizeof (UINT16)) {\r
1870 //\r
1871 // "-boot menu=on" was specified without "splash-time=N". In this case,\r
1872 // return three seconds if the platform default would cause us to skip the\r
1873 // front page, and return the platform default otherwise.\r
1874 //\r
1875 UINT16 Timeout;\r
1876\r
1877 Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
1878 if (Timeout == 0) {\r
1879 Timeout = 3;\r
1880 }\r
1881 return Timeout;\r
1882 }\r
1883\r
1884 //\r
1885 // "-boot menu=on,splash-time=N" was specified, where N is in units of\r
1886 // milliseconds. The Intel BDS Front Page progress bar only supports whole\r
1887 // seconds, round N up.\r
1888 //\r
1889 QemuFwCfgSelectItem (BootMenuWaitItem);\r
1890 return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);\r
1891}\r