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