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