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