]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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
d63242bd
GH
435 BOOLEAN AcceptSlash = FALSE;\r
436\r
6b40e66a
RN
437 //\r
438 // A leading slash is expected. End of string is tolerated.\r
439 //\r
440 switch (**Ptr) {\r
ac0a286f
MK
441 case '\0':\r
442 return RETURN_NOT_FOUND;\r
6b40e66a 443\r
ac0a286f
MK
444 case '/':\r
445 ++*Ptr;\r
446 break;\r
6b40e66a 447\r
ac0a286f
MK
448 default:\r
449 return RETURN_INVALID_PARAMETER;\r
6b40e66a
RN
450 }\r
451\r
452 //\r
453 // driver-name\r
454 //\r
455 OfwNode->DriverName.Ptr = *Ptr;\r
456 OfwNode->DriverName.Len = 0;\r
457 while (OfwNode->DriverName.Len < 32 &&\r
458 (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))\r
ac0a286f
MK
459 )\r
460 {\r
6b40e66a
RN
461 ++*Ptr;\r
462 ++OfwNode->DriverName.Len;\r
463 }\r
464\r
ac0a286f 465 if ((OfwNode->DriverName.Len == 0) || (OfwNode->DriverName.Len == 32)) {\r
6b40e66a
RN
466 return RETURN_INVALID_PARAMETER;\r
467 }\r
468\r
d63242bd
GH
469 if (SubstringEq (OfwNode->DriverName, "rom")) {\r
470 //\r
471 // bug compatibility hack\r
472 //\r
473 // qemu passes fw_cfg filenames as rom unit address.\r
474 // The filenames have slashes:\r
475 // /rom@genroms/linuxboot_dma.bin\r
476 //\r
477 // Alow slashes in the unit address to avoid the parser trip up,\r
478 // so we can successfully parse the following lines (the rom\r
479 // entries themself are ignored).\r
480 //\r
481 AcceptSlash = TRUE;\r
482 }\r
483\r
6b40e66a
RN
484 //\r
485 // unit-address\r
486 //\r
487 if (**Ptr != '@') {\r
488 return RETURN_INVALID_PARAMETER;\r
489 }\r
ac0a286f 490\r
6b40e66a
RN
491 ++*Ptr;\r
492\r
493 OfwNode->UnitAddress.Ptr = *Ptr;\r
494 OfwNode->UnitAddress.Len = 0;\r
d63242bd 495 while (IsPrintNotDelim (**Ptr) || (AcceptSlash && **Ptr == '/')) {\r
6b40e66a
RN
496 ++*Ptr;\r
497 ++OfwNode->UnitAddress.Len;\r
498 }\r
499\r
500 if (OfwNode->UnitAddress.Len == 0) {\r
501 return RETURN_INVALID_PARAMETER;\r
502 }\r
503\r
6b40e66a
RN
504 //\r
505 // device-arguments, may be omitted\r
506 //\r
507 OfwNode->DeviceArguments.Len = 0;\r
508 if (**Ptr == ':') {\r
509 ++*Ptr;\r
510 OfwNode->DeviceArguments.Ptr = *Ptr;\r
511\r
512 while (IsPrintNotDelim (**Ptr)) {\r
513 ++*Ptr;\r
514 ++OfwNode->DeviceArguments.Len;\r
515 }\r
516\r
517 if (OfwNode->DeviceArguments.Len == 0) {\r
518 return RETURN_INVALID_PARAMETER;\r
519 }\r
ac0a286f 520 } else {\r
6b40e66a
RN
521 OfwNode->DeviceArguments.Ptr = NULL;\r
522 }\r
523\r
524 switch (**Ptr) {\r
ac0a286f
MK
525 case '\n':\r
526 ++*Ptr;\r
6b40e66a
RN
527 //\r
528 // fall through\r
529 //\r
530\r
ac0a286f
MK
531 case '\0':\r
532 *IsFinal = TRUE;\r
533 break;\r
6b40e66a 534\r
ac0a286f
MK
535 case '/':\r
536 *IsFinal = FALSE;\r
537 break;\r
6b40e66a 538\r
ac0a286f
MK
539 default:\r
540 return RETURN_INVALID_PARAMETER;\r
6b40e66a
RN
541 }\r
542\r
543 DEBUG ((\r
544 DEBUG_VERBOSE,\r
545 "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",\r
546 __FUNCTION__,\r
ac0a286f
MK
547 OfwNode->DriverName.Len,\r
548 OfwNode->DriverName.Ptr,\r
549 OfwNode->UnitAddress.Len,\r
550 OfwNode->UnitAddress.Ptr,\r
6b40e66a
RN
551 OfwNode->DeviceArguments.Len,\r
552 OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr\r
553 ));\r
554 return RETURN_SUCCESS;\r
555}\r
556\r
6b40e66a
RN
557/**\r
558\r
559 Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path\r
560 fragment.\r
561\r
562 @param[in] OfwNode Array of OpenFirmware device nodes to\r
563 translate, constituting the beginning of an\r
564 OpenFirmware device path.\r
565\r
566 @param[in] NumNodes Number of elements in OfwNode.\r
567\r
568 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
569 CreateExtraRootBusMap(), to be used for\r
570 translating positions of extra root buses to\r
571 bus numbers.\r
572\r
573 @param[out] Translated Destination array receiving the UEFI path\r
574 fragment, allocated by the caller. If the\r
575 return value differs from RETURN_SUCCESS, its\r
576 contents is indeterminate.\r
577\r
578 @param[in out] TranslatedSize On input, the number of CHAR16's in\r
579 Translated. On RETURN_SUCCESS this parameter\r
580 is assigned the number of non-NUL CHAR16's\r
581 written to Translated. In case of other return\r
582 values, TranslatedSize is indeterminate.\r
583\r
584\r
585 @retval RETURN_SUCCESS Translation successful.\r
586\r
587 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
588 of bytes provided.\r
589\r
590 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
591 be translated in the current implementation.\r
592\r
593 @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an\r
594 extra PCI root bus (by serial number) that\r
595 is invalid according to ExtraPciRoots.\r
596\r
597**/\r
598STATIC\r
599RETURN_STATUS\r
600TranslatePciOfwNodes (\r
ac0a286f
MK
601 IN CONST OFW_NODE *OfwNode,\r
602 IN UINTN NumNodes,\r
603 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
604 OUT CHAR16 *Translated,\r
605 IN OUT UINTN *TranslatedSize\r
6b40e66a
RN
606 )\r
607{\r
ac0a286f
MK
608 UINT32 PciRoot;\r
609 CHAR8 *Comma;\r
610 UINTN FirstNonBridge;\r
611 CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];\r
612 UINTN BridgesLen;\r
613 UINT64 PciDevFun[2];\r
614 UINTN NumEntries;\r
615 UINTN Written;\r
6b40e66a
RN
616\r
617 //\r
618 // Resolve the PCI root bus number.\r
619 //\r
620 // The initial OFW node for the main root bus (ie. bus number 0) is:\r
621 //\r
622 // /pci@i0cf8\r
623 //\r
624 // For extra root buses, the initial OFW node is\r
625 //\r
626 // /pci@i0cf8,4\r
627 // ^\r
628 // root bus serial number (not PCI bus number)\r
629 //\r
ac0a286f 630 if ((NumNodes < REQUIRED_PCI_OFW_NODES) ||\r
6b40e66a 631 !SubstringEq (OfwNode[0].DriverName, "pci")\r
ac0a286f
MK
632 )\r
633 {\r
6b40e66a
RN
634 return RETURN_UNSUPPORTED;\r
635 }\r
636\r
637 PciRoot = 0;\r
ac0a286f
MK
638 Comma = ScanMem8 (\r
639 OfwNode[0].UnitAddress.Ptr,\r
640 OfwNode[0].UnitAddress.Len,\r
641 ','\r
642 );\r
6b40e66a 643 if (Comma != NULL) {\r
ac0a286f
MK
644 SUBSTRING PciRootSerialSubString;\r
645 UINT64 PciRootSerial;\r
6b40e66a
RN
646\r
647 //\r
648 // Parse the root bus serial number from the unit address after the comma.\r
649 //\r
650 PciRootSerialSubString.Ptr = Comma + 1;\r
651 PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -\r
652 (PciRootSerialSubString.Ptr -\r
653 OfwNode[0].UnitAddress.Ptr);\r
654 NumEntries = 1;\r
ac0a286f
MK
655 if (RETURN_ERROR (\r
656 ParseUnitAddressHexList (\r
657 PciRootSerialSubString,\r
658 &PciRootSerial,\r
659 &NumEntries\r
660 )\r
661 ))\r
662 {\r
6b40e66a
RN
663 return RETURN_UNSUPPORTED;\r
664 }\r
665\r
666 //\r
667 // Map the extra root bus's serial number to its actual bus number.\r
668 //\r
ac0a286f
MK
669 if (EFI_ERROR (\r
670 MapRootBusPosToBusNr (\r
671 ExtraPciRoots,\r
672 PciRootSerial,\r
673 &PciRoot\r
674 )\r
675 ))\r
676 {\r
6b40e66a
RN
677 return RETURN_PROTOCOL_ERROR;\r
678 }\r
679 }\r
680\r
681 //\r
682 // Translate a sequence of PCI bridges. For each bridge, the OFW node is:\r
683 //\r
684 // pci-bridge@1e[,0]\r
685 // ^ ^\r
686 // PCI slot & function on the parent, holding the bridge\r
687 //\r
688 // and the UEFI device path node is:\r
689 //\r
690 // Pci(0x1E,0x0)\r
691 //\r
692 FirstNonBridge = 1;\r
ac0a286f
MK
693 Bridges[0] = L'\0';\r
694 BridgesLen = 0;\r
6b40e66a 695 do {\r
ac0a286f
MK
696 UINT64 BridgeDevFun[2];\r
697 UINTN BridgesFreeBytes;\r
6b40e66a
RN
698\r
699 if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {\r
700 break;\r
701 }\r
702\r
703 BridgeDevFun[1] = 0;\r
ac0a286f
MK
704 NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];\r
705 if (ParseUnitAddressHexList (\r
706 OfwNode[FirstNonBridge].UnitAddress,\r
707 BridgeDevFun,\r
708 &NumEntries\r
709 ) != RETURN_SUCCESS)\r
710 {\r
6b40e66a
RN
711 return RETURN_UNSUPPORTED;\r
712 }\r
713\r
714 BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];\r
ac0a286f
MK
715 Written = UnicodeSPrintAsciiFormat (\r
716 Bridges + BridgesLen,\r
717 BridgesFreeBytes,\r
718 "/Pci(0x%Lx,0x%Lx)",\r
719 BridgeDevFun[0],\r
720 BridgeDevFun[1]\r
721 );\r
6b40e66a
RN
722 BridgesLen += Written;\r
723\r
724 //\r
725 // There's no way to differentiate between "completely used up without\r
726 // truncation" and "truncated", so treat the former as the latter.\r
727 //\r
728 if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {\r
729 return RETURN_UNSUPPORTED;\r
730 }\r
731\r
732 ++FirstNonBridge;\r
733 } while (FirstNonBridge < NumNodes);\r
734\r
735 if (FirstNonBridge == NumNodes) {\r
736 return RETURN_UNSUPPORTED;\r
737 }\r
738\r
739 //\r
740 // Parse the OFW nodes starting with the first non-bridge node.\r
741 //\r
742 PciDevFun[1] = 0;\r
ac0a286f 743 NumEntries = ARRAY_SIZE (PciDevFun);\r
6b40e66a
RN
744 if (ParseUnitAddressHexList (\r
745 OfwNode[FirstNonBridge].UnitAddress,\r
746 PciDevFun,\r
747 &NumEntries\r
748 ) != RETURN_SUCCESS\r
ac0a286f
MK
749 )\r
750 {\r
6b40e66a
RN
751 return RETURN_UNSUPPORTED;\r
752 }\r
753\r
ac0a286f 754 if ((NumNodes >= FirstNonBridge + 3) &&\r
6b40e66a
RN
755 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&\r
756 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
757 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
ac0a286f
MK
758 )\r
759 {\r
6b40e66a
RN
760 //\r
761 // OpenFirmware device path (IDE disk, IDE CD-ROM):\r
762 //\r
763 // /pci@i0cf8/ide@1,1/drive@0/disk@0\r
764 // ^ ^ ^ ^ ^\r
765 // | | | | master or slave\r
766 // | | | primary or secondary\r
767 // | PCI slot & function holding IDE controller\r
768 // PCI root at system bus port, PIO\r
769 //\r
770 // UEFI device path:\r
771 //\r
772 // PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)\r
773 // ^\r
774 // fixed LUN\r
775 //\r
ac0a286f
MK
776 UINT64 Secondary;\r
777 UINT64 Slave;\r
6b40e66a
RN
778\r
779 NumEntries = 1;\r
ac0a286f
MK
780 if ((ParseUnitAddressHexList (\r
781 OfwNode[FirstNonBridge + 1].UnitAddress,\r
782 &Secondary,\r
783 &NumEntries\r
784 ) != RETURN_SUCCESS) ||\r
785 (Secondary > 1) ||\r
786 (ParseUnitAddressHexList (\r
787 OfwNode[FirstNonBridge + 2].UnitAddress,\r
788 &Slave,\r
789 &NumEntries // reuse after previous single-element call\r
790 ) != RETURN_SUCCESS) ||\r
791 (Slave > 1)\r
792 )\r
793 {\r
6b40e66a
RN
794 return RETURN_UNSUPPORTED;\r
795 }\r
796\r
797 Written = UnicodeSPrintAsciiFormat (\r
ac0a286f
MK
798 Translated,\r
799 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
800 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",\r
801 PciRoot,\r
802 Bridges,\r
803 PciDevFun[0],\r
804 PciDevFun[1],\r
805 Secondary ? "Secondary" : "Primary",\r
806 Slave ? "Slave" : "Master"\r
807 );\r
808 } else if ((NumNodes >= FirstNonBridge + 3) &&\r
809 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&\r
810 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
811 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
812 )\r
813 {\r
6b40e66a
RN
814 //\r
815 // OpenFirmware device path (Q35 SATA disk and CD-ROM):\r
816 //\r
817 // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0\r
818 // ^ ^ ^ ^ ^\r
819 // | | | | device number (fixed 0)\r
820 // | | | channel (port) number\r
821 // | PCI slot & function holding SATA HBA\r
822 // PCI root at system bus port, PIO\r
823 //\r
824 // UEFI device path:\r
825 //\r
d5aee61b
LE
826 // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)\r
827 // ^ ^ ^\r
828 // | | LUN (always 0 on Q35)\r
6b40e66a 829 // | port multiplier port number,\r
d5aee61b 830 // | always 0xFFFF on Q35\r
6b40e66a
RN
831 // channel (port) number\r
832 //\r
ac0a286f 833 UINT64 Channel;\r
6b40e66a
RN
834\r
835 NumEntries = 1;\r
ac0a286f
MK
836 if (RETURN_ERROR (\r
837 ParseUnitAddressHexList (\r
838 OfwNode[FirstNonBridge + 1].UnitAddress,\r
839 &Channel,\r
840 &NumEntries\r
841 )\r
842 ))\r
843 {\r
6b40e66a
RN
844 return RETURN_UNSUPPORTED;\r
845 }\r
846\r
847 Written = UnicodeSPrintAsciiFormat (\r
ac0a286f
MK
848 Translated,\r
849 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
850 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",\r
851 PciRoot,\r
852 Bridges,\r
853 PciDevFun[0],\r
854 PciDevFun[1],\r
855 Channel\r
856 );\r
857 } else if ((NumNodes >= FirstNonBridge + 3) &&\r
6b40e66a
RN
858 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&\r
859 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&\r
860 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")\r
ac0a286f
MK
861 )\r
862 {\r
6b40e66a
RN
863 //\r
864 // OpenFirmware device path (floppy disk):\r
865 //\r
866 // /pci@i0cf8/isa@1/fdc@03f0/floppy@0\r
867 // ^ ^ ^ ^\r
868 // | | | A: or B:\r
869 // | | ISA controller io-port (hex)\r
870 // | PCI slot holding ISA controller\r
871 // PCI root at system bus port, PIO\r
872 //\r
873 // UEFI device path:\r
874 //\r
875 // PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)\r
876 // ^\r
877 // ACPI UID\r
878 //\r
ac0a286f 879 UINT64 AcpiUid;\r
6b40e66a
RN
880\r
881 NumEntries = 1;\r
ac0a286f
MK
882 if ((ParseUnitAddressHexList (\r
883 OfwNode[FirstNonBridge + 2].UnitAddress,\r
884 &AcpiUid,\r
885 &NumEntries\r
886 ) != RETURN_SUCCESS) ||\r
887 (AcpiUid > 1)\r
888 )\r
889 {\r
6b40e66a
RN
890 return RETURN_UNSUPPORTED;\r
891 }\r
892\r
893 Written = UnicodeSPrintAsciiFormat (\r
ac0a286f
MK
894 Translated,\r
895 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
896 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
897 PciRoot,\r
898 Bridges,\r
899 PciDevFun[0],\r
900 PciDevFun[1],\r
901 AcpiUid\r
902 );\r
903 } else if ((NumNodes >= FirstNonBridge + 2) &&\r
6b40e66a
RN
904 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
905 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")\r
ac0a286f
MK
906 )\r
907 {\r
6b40e66a
RN
908 //\r
909 // OpenFirmware device path (virtio-blk disk):\r
910 //\r
911 // /pci@i0cf8/scsi@6[,3]/disk@0,0\r
912 // ^ ^ ^ ^ ^\r
913 // | | | fixed\r
914 // | | PCI function corresponding to disk (optional)\r
915 // | PCI slot holding disk\r
916 // PCI root at system bus port, PIO\r
917 //\r
918 // UEFI device path prefix:\r
919 //\r
d796d33f
LE
920 // PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent\r
921 // PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero\r
6b40e66a
RN
922 //\r
923 Written = UnicodeSPrintAsciiFormat (\r
ac0a286f
MK
924 Translated,\r
925 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
926 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
927 PciRoot,\r
928 Bridges,\r
929 PciDevFun[0],\r
930 PciDevFun[1]\r
931 );\r
932 } else if ((NumNodes >= FirstNonBridge + 3) &&\r
6b40e66a
RN
933 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
934 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&\r
935 SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
ac0a286f
MK
936 )\r
937 {\r
6b40e66a
RN
938 //\r
939 // OpenFirmware device path (virtio-scsi disk):\r
940 //\r
941 // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3\r
942 // ^ ^ ^ ^ ^\r
943 // | | | | LUN\r
944 // | | | target\r
945 // | | channel (unused, fixed 0)\r
946 // | PCI slot[, function] holding SCSI controller\r
947 // PCI root at system bus port, PIO\r
948 //\r
949 // UEFI device path prefix:\r
950 //\r
951 // PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)\r
952 // -- if PCI function is 0 or absent\r
953 // PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)\r
954 // -- if PCI function is present and nonzero\r
955 //\r
ac0a286f 956 UINT64 TargetLun[2];\r
6b40e66a
RN
957\r
958 TargetLun[1] = 0;\r
ac0a286f 959 NumEntries = ARRAY_SIZE (TargetLun);\r
6b40e66a
RN
960 if (ParseUnitAddressHexList (\r
961 OfwNode[FirstNonBridge + 2].UnitAddress,\r
962 TargetLun,\r
963 &NumEntries\r
964 ) != RETURN_SUCCESS\r
ac0a286f
MK
965 )\r
966 {\r
6b40e66a
RN
967 return RETURN_UNSUPPORTED;\r
968 }\r
969\r
970 Written = UnicodeSPrintAsciiFormat (\r
ac0a286f
MK
971 Translated,\r
972 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
973 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
974 PciRoot,\r
975 Bridges,\r
976 PciDevFun[0],\r
977 PciDevFun[1],\r
978 TargetLun[0],\r
979 TargetLun[1]\r
980 );\r
981 } else if ((NumNodes >= FirstNonBridge + 2) &&\r
982 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&\r
983 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")\r
984 )\r
985 {\r
6b40e66a
RN
986 //\r
987 // OpenFirmware device path (NVMe device):\r
988 //\r
989 // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0\r
990 // ^ ^ ^ ^ ^\r
991 // | | | | Extended Unique Identifier\r
992 // | | | | (EUI-64), big endian interp.\r
993 // | | | namespace ID\r
994 // | PCI slot & function holding NVMe controller\r
995 // PCI root at system bus port, PIO\r
996 //\r
997 // UEFI device path:\r
998 //\r
999 // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)\r
1000 // ^ ^\r
1001 // | octets of the EUI-64\r
1002 // | in address order\r
1003 // namespace ID\r
1004 //\r
ac0a286f
MK
1005 UINT64 Namespace[2];\r
1006 UINTN RequiredEntries;\r
1007 UINT8 *Eui64;\r
6b40e66a 1008\r
10a82f7f 1009 RequiredEntries = ARRAY_SIZE (Namespace);\r
ac0a286f
MK
1010 NumEntries = RequiredEntries;\r
1011 if ((ParseUnitAddressHexList (\r
1012 OfwNode[FirstNonBridge + 1].UnitAddress,\r
1013 Namespace,\r
1014 &NumEntries\r
1015 ) != RETURN_SUCCESS) ||\r
1016 (NumEntries != RequiredEntries) ||\r
1017 (Namespace[0] == 0) ||\r
1018 (Namespace[0] >= MAX_UINT32)\r
1019 )\r
1020 {\r
6b40e66a
RN
1021 return RETURN_UNSUPPORTED;\r
1022 }\r
1023\r
ac0a286f 1024 Eui64 = (UINT8 *)&Namespace[1];\r
6b40e66a 1025 Written = UnicodeSPrintAsciiFormat (\r
ac0a286f
MK
1026 Translated,\r
1027 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
1028 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"\r
1029 "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",\r
1030 PciRoot,\r
1031 Bridges,\r
1032 PciDevFun[0],\r
1033 PciDevFun[1],\r
1034 Namespace[0],\r
1035 Eui64[7],\r
1036 Eui64[6],\r
1037 Eui64[5],\r
1038 Eui64[4],\r
1039 Eui64[3],\r
1040 Eui64[2],\r
1041 Eui64[1],\r
1042 Eui64[0]\r
1043 );\r
1044 } else if ((NumNodes >= FirstNonBridge + 2) &&\r
f9c59fa4 1045 SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "usb") &&\r
ac0a286f
MK
1046 SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "storage"))\r
1047 {\r
f9c59fa4
LE
1048 //\r
1049 // OpenFirmware device path (usb-storage device in XHCI port):\r
1050 //\r
1051 // /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0\r
1052 // ^ ^ ^ ^ ^ ^ ^\r
1053 // | | | | fixed fixed\r
1054 // | | | XHCI port number, 1-based\r
1055 // | | PCI function corresponding to XHCI (optional)\r
1056 // | PCI slot holding XHCI\r
1057 // PCI root at system bus port, PIO\r
1058 //\r
1059 // UEFI device path prefix:\r
1060 //\r
1061 // PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)\r
1062 // ^ ^\r
1063 // | XHCI port number in 0-based notation\r
1064 // 0x0 if PCI function is 0, or absent from OFW\r
1065 //\r
ac0a286f
MK
1066 RETURN_STATUS ParseStatus;\r
1067 UINT64 OneBasedXhciPort;\r
f9c59fa4 1068\r
ac0a286f 1069 NumEntries = 1;\r
f9c59fa4
LE
1070 ParseStatus = ParseUnitAddressHexList (\r
1071 OfwNode[FirstNonBridge + 1].UnitAddress,\r
1072 &OneBasedXhciPort,\r
1073 &NumEntries\r
1074 );\r
ac0a286f 1075 if (RETURN_ERROR (ParseStatus) || (OneBasedXhciPort == 0)) {\r
f9c59fa4
LE
1076 return RETURN_UNSUPPORTED;\r
1077 }\r
1078\r
1079 Written = UnicodeSPrintAsciiFormat (\r
1080 Translated,\r
1081 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
1082 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",\r
1083 PciRoot,\r
1084 Bridges,\r
1085 PciDevFun[0],\r
1086 PciDevFun[1],\r
1087 OneBasedXhciPort - 1\r
1088 );\r
6b40e66a
RN
1089 } else {\r
1090 //\r
1091 // Generic OpenFirmware device path for PCI devices:\r
1092 //\r
1093 // /pci@i0cf8/ethernet@3[,2]\r
1094 // ^ ^\r
1095 // | PCI slot[, function] holding Ethernet card\r
1096 // PCI root at system bus port, PIO\r
1097 //\r
1098 // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
1099 //\r
1100 // PciRoot(0x0)/Pci(0x3,0x0)\r
1101 // PciRoot(0x0)/Pci(0x3,0x2)\r
1102 //\r
1103 Written = UnicodeSPrintAsciiFormat (\r
ac0a286f
MK
1104 Translated,\r
1105 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
1106 "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
1107 PciRoot,\r
1108 Bridges,\r
1109 PciDevFun[0],\r
1110 PciDevFun[1]\r
1111 );\r
6b40e66a
RN
1112 }\r
1113\r
1114 //\r
1115 // There's no way to differentiate between "completely used up without\r
1116 // truncation" and "truncated", so treat the former as the latter, and return\r
1117 // success only for "some room left unused".\r
1118 //\r
1119 if (Written + 1 < *TranslatedSize) {\r
1120 *TranslatedSize = Written;\r
1121 return RETURN_SUCCESS;\r
1122 }\r
1123\r
1124 return RETURN_BUFFER_TOO_SMALL;\r
1125}\r
1126\r
6b40e66a
RN
1127//\r
1128// A type providing easy raw access to the base address of a virtio-mmio\r
1129// transport.\r
1130//\r
1131typedef union {\r
ac0a286f
MK
1132 UINT64 Uint64;\r
1133 UINT8 Raw[8];\r
6b40e66a
RN
1134} VIRTIO_MMIO_BASE_ADDRESS;\r
1135\r
6b40e66a
RN
1136/**\r
1137\r
1138 Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device\r
1139 path fragment.\r
1140\r
1141 @param[in] OfwNode Array of OpenFirmware device nodes to\r
1142 translate, constituting the beginning of an\r
1143 OpenFirmware device path.\r
1144\r
1145 @param[in] NumNodes Number of elements in OfwNode.\r
1146\r
1147 @param[out] Translated Destination array receiving the UEFI path\r
1148 fragment, allocated by the caller. If the\r
1149 return value differs from RETURN_SUCCESS, its\r
1150 contents is indeterminate.\r
1151\r
1152 @param[in out] TranslatedSize On input, the number of CHAR16's in\r
1153 Translated. On RETURN_SUCCESS this parameter\r
1154 is assigned the number of non-NUL CHAR16's\r
1155 written to Translated. In case of other return\r
1156 values, TranslatedSize is indeterminate.\r
1157\r
1158\r
1159 @retval RETURN_SUCCESS Translation successful.\r
1160\r
1161 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
1162 of bytes provided.\r
1163\r
1164 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
1165 be translated in the current implementation.\r
1166\r
1167**/\r
1168STATIC\r
1169RETURN_STATUS\r
1170TranslateMmioOfwNodes (\r
ac0a286f
MK
1171 IN CONST OFW_NODE *OfwNode,\r
1172 IN UINTN NumNodes,\r
1173 OUT CHAR16 *Translated,\r
1174 IN OUT UINTN *TranslatedSize\r
6b40e66a
RN
1175 )\r
1176{\r
ac0a286f
MK
1177 VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;\r
1178 CHAR16 VenHwString[60 + 1];\r
1179 UINTN NumEntries;\r
1180 UINTN Written;\r
6b40e66a
RN
1181\r
1182 //\r
1183 // Get the base address of the virtio-mmio transport.\r
1184 //\r
ac0a286f 1185 if ((NumNodes < REQUIRED_MMIO_OFW_NODES) ||\r
6b40e66a 1186 !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")\r
ac0a286f
MK
1187 )\r
1188 {\r
6b40e66a
RN
1189 return RETURN_UNSUPPORTED;\r
1190 }\r
ac0a286f 1191\r
6b40e66a
RN
1192 NumEntries = 1;\r
1193 if (ParseUnitAddressHexList (\r
1194 OfwNode[0].UnitAddress,\r
1195 &VirtioMmioBase.Uint64,\r
1196 &NumEntries\r
1197 ) != RETURN_SUCCESS\r
ac0a286f
MK
1198 )\r
1199 {\r
6b40e66a
RN
1200 return RETURN_UNSUPPORTED;\r
1201 }\r
1202\r
ac0a286f
MK
1203 UnicodeSPrintAsciiFormat (\r
1204 VenHwString,\r
1205 sizeof VenHwString,\r
1206 "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)",\r
1207 &gVirtioMmioTransportGuid,\r
1208 VirtioMmioBase.Raw[0],\r
1209 VirtioMmioBase.Raw[1],\r
1210 VirtioMmioBase.Raw[2],\r
1211 VirtioMmioBase.Raw[3],\r
1212 VirtioMmioBase.Raw[4],\r
1213 VirtioMmioBase.Raw[5],\r
1214 VirtioMmioBase.Raw[6],\r
1215 VirtioMmioBase.Raw[7]\r
1216 );\r
1217\r
1218 if ((NumNodes >= 2) &&\r
1219 SubstringEq (OfwNode[1].DriverName, "disk"))\r
1220 {\r
6b40e66a
RN
1221 //\r
1222 // OpenFirmware device path (virtio-blk disk):\r
1223 //\r
1224 // /virtio-mmio@000000000a003c00/disk@0,0\r
1225 // ^ ^ ^\r
1226 // | fixed\r
1227 // base address of virtio-mmio register block\r
1228 //\r
1229 // UEFI device path prefix:\r
1230 //\r
d796d33f 1231 // <VenHwString>\r
6b40e66a
RN
1232 //\r
1233 Written = UnicodeSPrintAsciiFormat (\r
1234 Translated,\r
1235 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
d796d33f 1236 "%s",\r
6b40e66a
RN
1237 VenHwString\r
1238 );\r
ac0a286f 1239 } else if ((NumNodes >= 3) &&\r
6b40e66a 1240 SubstringEq (OfwNode[1].DriverName, "channel") &&\r
ac0a286f
MK
1241 SubstringEq (OfwNode[2].DriverName, "disk"))\r
1242 {\r
6b40e66a
RN
1243 //\r
1244 // OpenFirmware device path (virtio-scsi disk):\r
1245 //\r
1246 // /virtio-mmio@000000000a003a00/channel@0/disk@2,3\r
1247 // ^ ^ ^ ^\r
1248 // | | | LUN\r
1249 // | | target\r
1250 // | channel (unused, fixed 0)\r
1251 // base address of virtio-mmio register block\r
1252 //\r
1253 // UEFI device path prefix:\r
1254 //\r
1255 // <VenHwString>/Scsi(0x2,0x3)\r
1256 //\r
ac0a286f 1257 UINT64 TargetLun[2];\r
6b40e66a
RN
1258\r
1259 TargetLun[1] = 0;\r
ac0a286f 1260 NumEntries = ARRAY_SIZE (TargetLun);\r
6b40e66a
RN
1261 if (ParseUnitAddressHexList (\r
1262 OfwNode[2].UnitAddress,\r
1263 TargetLun,\r
1264 &NumEntries\r
1265 ) != RETURN_SUCCESS\r
ac0a286f
MK
1266 )\r
1267 {\r
6b40e66a
RN
1268 return RETURN_UNSUPPORTED;\r
1269 }\r
1270\r
1271 Written = UnicodeSPrintAsciiFormat (\r
1272 Translated,\r
1273 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
1274 "%s/Scsi(0x%Lx,0x%Lx)",\r
1275 VenHwString,\r
1276 TargetLun[0],\r
1277 TargetLun[1]\r
1278 );\r
ac0a286f
MK
1279 } else if ((NumNodes >= 2) &&\r
1280 SubstringEq (OfwNode[1].DriverName, "ethernet-phy"))\r
1281 {\r
6b40e66a
RN
1282 //\r
1283 // OpenFirmware device path (virtio-net NIC):\r
1284 //\r
1285 // /virtio-mmio@000000000a003e00/ethernet-phy@0\r
1286 // ^ ^\r
1287 // | fixed\r
1288 // base address of virtio-mmio register block\r
1289 //\r
90f09188 1290 // UEFI device path prefix:\r
6b40e66a 1291 //\r
90f09188 1292 // <VenHwString>\r
6b40e66a
RN
1293 //\r
1294 Written = UnicodeSPrintAsciiFormat (\r
1295 Translated,\r
1296 *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
90f09188 1297 "%s",\r
6b40e66a
RN
1298 VenHwString\r
1299 );\r
1300 } else {\r
1301 return RETURN_UNSUPPORTED;\r
1302 }\r
1303\r
1304 //\r
1305 // There's no way to differentiate between "completely used up without\r
1306 // truncation" and "truncated", so treat the former as the latter, and return\r
1307 // success only for "some room left unused".\r
1308 //\r
1309 if (Written + 1 < *TranslatedSize) {\r
1310 *TranslatedSize = Written;\r
1311 return RETURN_SUCCESS;\r
1312 }\r
1313\r
1314 return RETURN_BUFFER_TOO_SMALL;\r
1315}\r
1316\r
6b40e66a
RN
1317/**\r
1318\r
1319 Translate an array of OpenFirmware device nodes to a UEFI device path\r
1320 fragment.\r
1321\r
1322 @param[in] OfwNode Array of OpenFirmware device nodes to\r
1323 translate, constituting the beginning of an\r
1324 OpenFirmware device path.\r
1325\r
1326 @param[in] NumNodes Number of elements in OfwNode.\r
1327\r
1328 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
1329 CreateExtraRootBusMap(), to be used for\r
1330 translating positions of extra root buses to\r
1331 bus numbers.\r
1332\r
1333 @param[out] Translated Destination array receiving the UEFI path\r
1334 fragment, allocated by the caller. If the\r
1335 return value differs from RETURN_SUCCESS, its\r
1336 contents is indeterminate.\r
1337\r
1338 @param[in out] TranslatedSize On input, the number of CHAR16's in\r
1339 Translated. On RETURN_SUCCESS this parameter\r
1340 is assigned the number of non-NUL CHAR16's\r
1341 written to Translated. In case of other return\r
1342 values, TranslatedSize is indeterminate.\r
1343\r
1344\r
1345 @retval RETURN_SUCCESS Translation successful.\r
1346\r
1347 @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
1348 of bytes provided.\r
1349\r
1350 @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
1351 be translated in the current implementation.\r
1352\r
1353 @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has\r
1354 been (partially) recognized, but it contains\r
1355 a logic error / doesn't match system state.\r
1356\r
1357**/\r
1358STATIC\r
1359RETURN_STATUS\r
1360TranslateOfwNodes (\r
ac0a286f
MK
1361 IN CONST OFW_NODE *OfwNode,\r
1362 IN UINTN NumNodes,\r
1363 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
1364 OUT CHAR16 *Translated,\r
1365 IN OUT UINTN *TranslatedSize\r
6b40e66a
RN
1366 )\r
1367{\r
ac0a286f 1368 RETURN_STATUS Status;\r
6b40e66a
RN
1369\r
1370 Status = RETURN_UNSUPPORTED;\r
1371\r
1372 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
ac0a286f
MK
1373 Status = TranslatePciOfwNodes (\r
1374 OfwNode,\r
1375 NumNodes,\r
1376 ExtraPciRoots,\r
1377 Translated,\r
1378 TranslatedSize\r
1379 );\r
6b40e66a 1380 }\r
ac0a286f
MK
1381\r
1382 if ((Status == RETURN_UNSUPPORTED) &&\r
1383 FeaturePcdGet (PcdQemuBootOrderMmioTranslation))\r
1384 {\r
1385 Status = TranslateMmioOfwNodes (\r
1386 OfwNode,\r
1387 NumNodes,\r
1388 Translated,\r
1389 TranslatedSize\r
1390 );\r
6b40e66a 1391 }\r
ac0a286f 1392\r
6b40e66a
RN
1393 return Status;\r
1394}\r
1395\r
1396/**\r
1397\r
1398 Translate an OpenFirmware device path fragment to a UEFI device path\r
1399 fragment, and advance in the input string.\r
1400\r
1401 @param[in out] Ptr Address of the pointer pointing to the start\r
1402 of the path string. After successful\r
1403 translation (RETURN_SUCCESS) or at least\r
1404 successful parsing (RETURN_UNSUPPORTED,\r
1405 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the\r
1406 byte immediately following the consumed\r
1407 characters. In other error cases, it points to\r
1408 the byte that caused the error.\r
1409\r
1410 @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
1411 CreateExtraRootBusMap(), to be used for\r
1412 translating positions of extra root buses to\r
1413 bus numbers.\r
1414\r
1415 @param[out] Translated Destination array receiving the UEFI path\r
1416 fragment, allocated by the caller. If the\r
1417 return value differs from RETURN_SUCCESS, its\r
1418 contents is indeterminate.\r
1419\r
1420 @param[in out] TranslatedSize On input, the number of CHAR16's in\r
1421 Translated. On RETURN_SUCCESS this parameter\r
1422 is assigned the number of non-NUL CHAR16's\r
1423 written to Translated. In case of other return\r
1424 values, TranslatedSize is indeterminate.\r
1425\r
1426\r
1427 @retval RETURN_SUCCESS Translation successful.\r
1428\r
1429 @retval RETURN_BUFFER_TOO_SMALL The OpenFirmware device path was parsed\r
1430 successfully, but its translation did not\r
1431 fit into the number of bytes provided.\r
1432 Further calls to this function are\r
1433 possible.\r
1434\r
1435 @retval RETURN_UNSUPPORTED The OpenFirmware device path was parsed\r
1436 successfully, but it can't be translated in\r
1437 the current implementation. Further calls\r
1438 to this function are possible.\r
1439\r
1440 @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been\r
1441 (partially) recognized, but it contains a\r
1442 logic error / doesn't match system state.\r
1443 Further calls to this function are\r
1444 possible.\r
1445\r
1446 @retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was\r
1447 pointing to the empty string or "HALT". On\r
1448 output, *Ptr points to the empty string\r
1449 (ie. "HALT" is consumed transparently when\r
1450 present).\r
1451\r
1452 @retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.\r
1453\r
1454**/\r
1455STATIC\r
1456RETURN_STATUS\r
1457TranslateOfwPath (\r
ac0a286f
MK
1458 IN OUT CONST CHAR8 **Ptr,\r
1459 IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
1460 OUT CHAR16 *Translated,\r
1461 IN OUT UINTN *TranslatedSize\r
6b40e66a
RN
1462 )\r
1463{\r
ac0a286f
MK
1464 UINTN NumNodes;\r
1465 RETURN_STATUS Status;\r
1466 OFW_NODE Node[EXAMINED_OFW_NODES];\r
1467 BOOLEAN IsFinal;\r
1468 OFW_NODE Skip;\r
6b40e66a 1469\r
ac0a286f 1470 IsFinal = FALSE;\r
6b40e66a
RN
1471 NumNodes = 0;\r
1472 if (AsciiStrCmp (*Ptr, "HALT") == 0) {\r
ac0a286f 1473 *Ptr += 4;\r
6b40e66a
RN
1474 Status = RETURN_NOT_FOUND;\r
1475 } else {\r
1476 Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);\r
1477 }\r
1478\r
1479 if (Status == RETURN_NOT_FOUND) {\r
1480 DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));\r
1481 return RETURN_NOT_FOUND;\r
1482 }\r
1483\r
1484 while (Status == RETURN_SUCCESS && !IsFinal) {\r
1485 ++NumNodes;\r
1486 Status = ParseOfwNode (\r
1487 Ptr,\r
1488 (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,\r
1489 &IsFinal\r
1490 );\r
1491 }\r
1492\r
1493 switch (Status) {\r
ac0a286f
MK
1494 case RETURN_SUCCESS:\r
1495 ++NumNodes;\r
1496 break;\r
6b40e66a 1497\r
ac0a286f
MK
1498 case RETURN_INVALID_PARAMETER:\r
1499 DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));\r
1500 return RETURN_INVALID_PARAMETER;\r
6b40e66a 1501\r
ac0a286f
MK
1502 default:\r
1503 ASSERT (0);\r
6b40e66a
RN
1504 }\r
1505\r
1506 Status = TranslateOfwNodes (\r
1507 Node,\r
1508 NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,\r
1509 ExtraPciRoots,\r
1510 Translated,\r
ac0a286f
MK
1511 TranslatedSize\r
1512 );\r
6b40e66a 1513 switch (Status) {\r
ac0a286f
MK
1514 case RETURN_SUCCESS:\r
1515 DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));\r
1516 break;\r
6b40e66a 1517\r
ac0a286f
MK
1518 case RETURN_BUFFER_TOO_SMALL:\r
1519 DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));\r
1520 break;\r
6b40e66a 1521\r
ac0a286f
MK
1522 case RETURN_UNSUPPORTED:\r
1523 DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
1524 break;\r
6b40e66a 1525\r
ac0a286f
MK
1526 case RETURN_PROTOCOL_ERROR:\r
1527 DEBUG ((\r
1528 DEBUG_VERBOSE,\r
1529 "%a: logic error / system state mismatch\n",\r
1530 __FUNCTION__\r
1531 ));\r
1532 break;\r
6b40e66a 1533\r
ac0a286f
MK
1534 default:\r
1535 ASSERT (0);\r
6b40e66a 1536 }\r
ac0a286f 1537\r
6b40e66a
RN
1538 return Status;\r
1539}\r
1540\r
51bd1f76
LE
1541/**\r
1542 Connect devices based on the boot order retrieved from QEMU.\r
1543\r
1544 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the\r
1545 OpenFirmware device paths therein to UEFI device path fragments. Connect the\r
1546 devices identified by the UEFI devpath prefixes as narrowly as possible, then\r
1547 connect all their child devices, recursively.\r
1548\r
1549 If this function fails, then platform BDS should fall back to\r
1550 EfiBootManagerConnectAll(), or some other method for connecting any expected\r
1551 boot devices.\r
1552\r
1553 @retval RETURN_SUCCESS The "bootorder" fw_cfg file has been\r
1554 parsed, and the referenced device-subtrees\r
1555 have been connected.\r
1556\r
1557 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.\r
1558\r
1559 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg\r
1560 file.\r
1561\r
1562 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.\r
1563\r
1564 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
1565\r
1566 @return Error statuses propagated from underlying\r
1567 functions.\r
1568**/\r
1569RETURN_STATUS\r
1570EFIAPI\r
1571ConnectDevicesFromQemu (\r
1572 VOID\r
1573 )\r
1574{\r
ac0a286f
MK
1575 RETURN_STATUS Status;\r
1576 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
1577 UINTN FwCfgSize;\r
1578 CHAR8 *FwCfg;\r
1579 EFI_STATUS EfiStatus;\r
1580 EXTRA_ROOT_BUS_MAP *ExtraPciRoots;\r
1581 CONST CHAR8 *FwCfgPtr;\r
1582 UINTN NumConnected;\r
1583 UINTN TranslatedSize;\r
1584 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];\r
51bd1f76
LE
1585\r
1586 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
1587 if (RETURN_ERROR (Status)) {\r
1588 return Status;\r
1589 }\r
1590\r
1591 if (FwCfgSize == 0) {\r
1592 return RETURN_NOT_FOUND;\r
1593 }\r
1594\r
1595 FwCfg = AllocatePool (FwCfgSize);\r
1596 if (FwCfg == NULL) {\r
1597 return RETURN_OUT_OF_RESOURCES;\r
1598 }\r
1599\r
1600 QemuFwCfgSelectItem (FwCfgItem);\r
1601 QemuFwCfgReadBytes (FwCfgSize, FwCfg);\r
1602 if (FwCfg[FwCfgSize - 1] != '\0') {\r
1603 Status = RETURN_INVALID_PARAMETER;\r
1604 goto FreeFwCfg;\r
1605 }\r
ac0a286f 1606\r
51bd1f76
LE
1607 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));\r
1608 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));\r
1609 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));\r
1610\r
1611 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
1612 EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);\r
1613 if (EFI_ERROR (EfiStatus)) {\r
1614 Status = (RETURN_STATUS)EfiStatus;\r
1615 goto FreeFwCfg;\r
1616 }\r
1617 } else {\r
1618 ExtraPciRoots = NULL;\r
1619 }\r
1620\r
1621 //\r
1622 // Translate each OpenFirmware path to a UEFI devpath prefix.\r
1623 //\r
ac0a286f
MK
1624 FwCfgPtr = FwCfg;\r
1625 NumConnected = 0;\r
51bd1f76 1626 TranslatedSize = ARRAY_SIZE (Translated);\r
ac0a286f
MK
1627 Status = TranslateOfwPath (\r
1628 &FwCfgPtr,\r
1629 ExtraPciRoots,\r
1630 Translated,\r
1631 &TranslatedSize\r
1632 );\r
51bd1f76 1633 while (!RETURN_ERROR (Status)) {\r
ac0a286f
MK
1634 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
1635 EFI_HANDLE Controller;\r
51bd1f76
LE
1636\r
1637 //\r
1638 // Convert the UEFI devpath prefix to binary representation.\r
1639 //\r
1640 ASSERT (Translated[TranslatedSize] == L'\0');\r
1641 DevicePath = ConvertTextToDevicePath (Translated);\r
1642 if (DevicePath == NULL) {\r
1643 Status = RETURN_OUT_OF_RESOURCES;\r
1644 goto FreeExtraPciRoots;\r
1645 }\r
ac0a286f 1646\r
51bd1f76
LE
1647 //\r
1648 // Advance along DevicePath, connecting the nodes individually, and asking\r
1649 // drivers not to produce sibling nodes. Retrieve the controller handle\r
1650 // associated with the full DevicePath -- this is the device that QEMU's\r
1651 // OFW devpath refers to.\r
1652 //\r
1653 EfiStatus = EfiBootManagerConnectDevicePath (DevicePath, &Controller);\r
1654 FreePool (DevicePath);\r
1655 if (EFI_ERROR (EfiStatus)) {\r
1656 Status = (RETURN_STATUS)EfiStatus;\r
1657 goto FreeExtraPciRoots;\r
1658 }\r
ac0a286f 1659\r
51bd1f76
LE
1660 //\r
1661 // Because QEMU's OFW devpaths have lesser expressive power than UEFI\r
1662 // devpaths (i.e., DevicePath is considered a prefix), connect the tree\r
1663 // rooted at Controller, recursively. If no children are produced\r
1664 // (EFI_NOT_FOUND), that's OK.\r
1665 //\r
1666 EfiStatus = gBS->ConnectController (Controller, NULL, NULL, TRUE);\r
ac0a286f 1667 if (EFI_ERROR (EfiStatus) && (EfiStatus != EFI_NOT_FOUND)) {\r
51bd1f76
LE
1668 Status = (RETURN_STATUS)EfiStatus;\r
1669 goto FreeExtraPciRoots;\r
1670 }\r
ac0a286f 1671\r
51bd1f76
LE
1672 ++NumConnected;\r
1673 //\r
1674 // Move to the next OFW devpath.\r
1675 //\r
1676 TranslatedSize = ARRAY_SIZE (Translated);\r
ac0a286f
MK
1677 Status = TranslateOfwPath (\r
1678 &FwCfgPtr,\r
1679 ExtraPciRoots,\r
1680 Translated,\r
1681 &TranslatedSize\r
1682 );\r
51bd1f76
LE
1683 }\r
1684\r
ac0a286f
MK
1685 if ((Status == RETURN_NOT_FOUND) && (NumConnected > 0)) {\r
1686 DEBUG ((\r
1687 DEBUG_INFO,\r
1688 "%a: %Lu OpenFirmware device path(s) connected\n",\r
1689 __FUNCTION__,\r
1690 (UINT64)NumConnected\r
1691 ));\r
51bd1f76
LE
1692 Status = RETURN_SUCCESS;\r
1693 }\r
1694\r
1695FreeExtraPciRoots:\r
1696 if (ExtraPciRoots != NULL) {\r
1697 DestroyExtraRootBusMap (ExtraPciRoots);\r
1698 }\r
1699\r
1700FreeFwCfg:\r
1701 FreePool (FwCfg);\r
1702\r
1703 return Status;\r
1704}\r
1705\r
5eeb088a
GH
1706/**\r
1707 Write qemu boot order to uefi variables.\r
1708\r
1709 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate\r
1710 the OpenFirmware device paths therein to UEFI device path fragments.\r
1711\r
406ad058 1712 On Success store the device path in VMMBootOrderNNNN variables.\r
5eeb088a
GH
1713**/\r
1714VOID\r
1715EFIAPI\r
1716StoreQemuBootOrder (\r
1717 VOID\r
1718 )\r
1719{\r
1720 RETURN_STATUS Status;\r
1721 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
1722 UINTN FwCfgSize;\r
1723 CHAR8 *FwCfg;\r
1724 EFI_STATUS EfiStatus;\r
1725 EXTRA_ROOT_BUS_MAP *ExtraPciRoots;\r
1726 CONST CHAR8 *FwCfgPtr;\r
1727 UINTN TranslatedSize;\r
1728 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];\r
1729 UINTN VariableIndex = 0;\r
1730 CHAR16 VariableName[20];\r
1731\r
1732 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
1733 if (RETURN_ERROR (Status)) {\r
1734 return;\r
1735 }\r
1736\r
1737 if (FwCfgSize == 0) {\r
1738 return;\r
1739 }\r
1740\r
1741 FwCfg = AllocatePool (FwCfgSize);\r
1742 if (FwCfg == NULL) {\r
1743 return;\r
1744 }\r
1745\r
1746 QemuFwCfgSelectItem (FwCfgItem);\r
1747 QemuFwCfgReadBytes (FwCfgSize, FwCfg);\r
1748 if (FwCfg[FwCfgSize - 1] != '\0') {\r
1749 Status = RETURN_INVALID_PARAMETER;\r
1750 goto FreeFwCfg;\r
1751 }\r
1752\r
1753 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));\r
1754 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));\r
1755 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));\r
1756\r
1757 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
1758 EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);\r
1759 if (EFI_ERROR (EfiStatus)) {\r
1760 Status = (RETURN_STATUS)EfiStatus;\r
1761 goto FreeFwCfg;\r
1762 }\r
1763 } else {\r
1764 ExtraPciRoots = NULL;\r
1765 }\r
1766\r
1767 //\r
1768 // Translate each OpenFirmware path to a UEFI devpath prefix.\r
1769 //\r
1770 FwCfgPtr = FwCfg;\r
1771 TranslatedSize = ARRAY_SIZE (Translated);\r
1772 Status = TranslateOfwPath (\r
1773 &FwCfgPtr,\r
1774 ExtraPciRoots,\r
1775 Translated,\r
1776 &TranslatedSize\r
1777 );\r
b7213bbd
GH
1778 while (Status == EFI_SUCCESS ||\r
1779 Status == EFI_UNSUPPORTED)\r
1780 {\r
1781 if (Status == EFI_SUCCESS) {\r
1782 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
1783\r
1784 //\r
1785 // Convert the UEFI devpath prefix to binary representation.\r
1786 //\r
1787 ASSERT (Translated[TranslatedSize] == L'\0');\r
1788 DevicePath = ConvertTextToDevicePath (Translated);\r
1789 if (DevicePath == NULL) {\r
1790 Status = RETURN_OUT_OF_RESOURCES;\r
1791 goto FreeExtraPciRoots;\r
1792 }\r
5eeb088a 1793\r
b7213bbd
GH
1794 UnicodeSPrint (\r
1795 VariableName,\r
1796 sizeof (VariableName),\r
406ad058 1797 L"VMMBootOrder%04x",\r
b7213bbd
GH
1798 VariableIndex++\r
1799 );\r
1800 DEBUG ((DEBUG_INFO, "%a: %s = %s\n", __FUNCTION__, VariableName, Translated));\r
1801 gRT->SetVariable (\r
1802 VariableName,\r
406ad058 1803 &gVMMBootOrderGuid,\r
b7213bbd
GH
1804 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
1805 GetDevicePathSize (DevicePath),\r
1806 DevicePath\r
1807 );\r
1808 FreePool (DevicePath);\r
5eeb088a
GH
1809 }\r
1810\r
5eeb088a
GH
1811 //\r
1812 // Move to the next OFW devpath.\r
1813 //\r
1814 TranslatedSize = ARRAY_SIZE (Translated);\r
1815 Status = TranslateOfwPath (\r
1816 &FwCfgPtr,\r
1817 ExtraPciRoots,\r
1818 Translated,\r
1819 &TranslatedSize\r
1820 );\r
1821 }\r
1822\r
1823FreeExtraPciRoots:\r
1824 if (ExtraPciRoots != NULL) {\r
1825 DestroyExtraRootBusMap (ExtraPciRoots);\r
1826 }\r
1827\r
1828FreeFwCfg:\r
1829 FreePool (FwCfg);\r
1830}\r
1831\r
6b40e66a
RN
1832/**\r
1833\r
1834 Convert the UEFI DevicePath to full text representation with DevPathToText,\r
1835 then match the UEFI device path fragment in Translated against it.\r
1836\r
1837 @param[in] Translated UEFI device path fragment, translated from\r
1838 OpenFirmware format, to search for.\r
1839\r
1840 @param[in] TranslatedLength The length of Translated in CHAR16's.\r
1841\r
1842 @param[in] DevicePath Boot option device path whose textual rendering\r
1843 to search in.\r
1844\r
1845 @param[in] DevPathToText Binary-to-text conversion protocol for DevicePath.\r
1846\r
1847\r
1848 @retval TRUE If Translated was found at the beginning of DevicePath after\r
1849 converting the latter to text.\r
1850\r
1851 @retval FALSE If DevicePath was NULL, or it could not be converted, or there\r
1852 was no match.\r
1853\r
1854**/\r
1855STATIC\r
1856BOOLEAN\r
1857Match (\r
ac0a286f
MK
1858 IN CONST CHAR16 *Translated,\r
1859 IN UINTN TranslatedLength,\r
1860 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
6b40e66a
RN
1861 )\r
1862{\r
ac0a286f
MK
1863 CHAR16 *Converted;\r
1864 BOOLEAN Result;\r
1865 VOID *FileBuffer;\r
1866 UINTN FileSize;\r
1867 EFI_DEVICE_PATH_PROTOCOL *AbsDevicePath;\r
1868 CHAR16 *AbsConverted;\r
1869 BOOLEAN Shortform;\r
1870 EFI_DEVICE_PATH_PROTOCOL *Node;\r
6b40e66a
RN
1871\r
1872 Converted = ConvertDevicePathToText (\r
1873 DevicePath,\r
1874 FALSE, // DisplayOnly\r
1875 FALSE // AllowShortcuts\r
1876 );\r
1877 if (Converted == NULL) {\r
1878 return FALSE;\r
1879 }\r
1880\r
ac0a286f 1881 Result = FALSE;\r
d27ec22d 1882 Shortform = FALSE;\r
6b40e66a 1883 //\r
d27ec22d 1884 // Expand the short-form device path to full device path\r
6b40e66a 1885 //\r
d27ec22d 1886 if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
ac0a286f
MK
1887 (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP))\r
1888 {\r
d27ec22d
RN
1889 //\r
1890 // Harddrive shortform device path\r
1891 //\r
1892 Shortform = TRUE;\r
1893 } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
ac0a286f
MK
1894 (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP))\r
1895 {\r
d27ec22d
RN
1896 //\r
1897 // File-path shortform device path\r
1898 //\r
1899 Shortform = TRUE;\r
1900 } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
ac0a286f
MK
1901 (DevicePathSubType (DevicePath) == MSG_URI_DP))\r
1902 {\r
d27ec22d
RN
1903 //\r
1904 // URI shortform device path\r
1905 //\r
1906 Shortform = TRUE;\r
1907 } else {\r
1908 for ( Node = DevicePath\r
ac0a286f
MK
1909 ; !IsDevicePathEnd (Node)\r
1910 ; Node = NextDevicePathNode (Node)\r
1911 )\r
1912 {\r
d27ec22d
RN
1913 if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
1914 ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||\r
ac0a286f
MK
1915 (DevicePathSubType (Node) == MSG_USB_WWID_DP)))\r
1916 {\r
d27ec22d
RN
1917 Shortform = TRUE;\r
1918 break;\r
1919 }\r
1920 }\r
1921 }\r
1922\r
1923 //\r
1924 // Attempt to expand any relative UEFI device path to\r
1925 // an absolute device path first.\r
1926 //\r
1927 if (Shortform) {\r
1928 FileBuffer = EfiBootManagerGetLoadOptionBuffer (\r
ac0a286f
MK
1929 DevicePath,\r
1930 &AbsDevicePath,\r
1931 &FileSize\r
d27ec22d
RN
1932 );\r
1933 if (FileBuffer == NULL) {\r
6b40e66a
RN
1934 goto Exit;\r
1935 }\r
ac0a286f 1936\r
d27ec22d 1937 FreePool (FileBuffer);\r
6b40e66a 1938 AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);\r
d27ec22d 1939 FreePool (AbsDevicePath);\r
6b40e66a
RN
1940 if (AbsConverted == NULL) {\r
1941 goto Exit;\r
1942 }\r
ac0a286f
MK
1943\r
1944 DEBUG ((\r
1945 DEBUG_VERBOSE,\r
6b40e66a 1946 "%a: expanded relative device path \"%s\" for prefix matching\n",\r
ac0a286f
MK
1947 __FUNCTION__,\r
1948 Converted\r
1949 ));\r
6b40e66a
RN
1950 FreePool (Converted);\r
1951 Converted = AbsConverted;\r
1952 }\r
1953\r
1954 //\r
1955 // Is Translated a prefix of Converted?\r
1956 //\r
1957 Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);\r
1958 DEBUG ((\r
1959 DEBUG_VERBOSE,\r
1960 "%a: against \"%s\": %a\n",\r
1961 __FUNCTION__,\r
1962 Converted,\r
1963 Result ? "match" : "no match"\r
1964 ));\r
1965Exit:\r
1966 FreePool (Converted);\r
1967 return Result;\r
1968}\r
1969\r
6b40e66a
RN
1970/**\r
1971 Append some of the unselected active boot options to the boot order.\r
1972\r
1973 This function should accommodate any further policy changes in "boot option\r
1974 survival". Currently we're adding back everything that starts with neither\r
1975 PciRoot() nor HD() nor a virtio-mmio VenHw() node.\r
1976\r
1977 @param[in,out] BootOrder The structure holding the boot order to\r
1978 complete. The caller is responsible for\r
1979 initializing (and potentially populating) it\r
1980 before calling this function.\r
1981\r
1982 @param[in,out] ActiveOption The array of active boot options to scan.\r
1983 Entries marked as Appended will be skipped.\r
1984 Those of the rest that satisfy the survival\r
1985 policy will be added to BootOrder with\r
1986 BootOrderAppend().\r
1987\r
1988 @param[in] ActiveCount Number of elements in ActiveOption.\r
1989\r
1990\r
1991 @retval RETURN_SUCCESS BootOrder has been extended with any eligible boot\r
1992 options.\r
1993\r
1994 @return Error codes returned by BootOrderAppend().\r
1995**/\r
1996STATIC\r
1997RETURN_STATUS\r
1998BootOrderComplete (\r
ac0a286f
MK
1999 IN OUT BOOT_ORDER *BootOrder,\r
2000 IN OUT ACTIVE_OPTION *ActiveOption,\r
2001 IN UINTN ActiveCount\r
6b40e66a
RN
2002 )\r
2003{\r
ac0a286f
MK
2004 RETURN_STATUS Status;\r
2005 UINTN Idx;\r
6b40e66a
RN
2006\r
2007 Status = RETURN_SUCCESS;\r
ac0a286f 2008 Idx = 0;\r
6b40e66a
RN
2009 while (!RETURN_ERROR (Status) && Idx < ActiveCount) {\r
2010 if (!ActiveOption[Idx].Appended) {\r
ac0a286f
MK
2011 CONST EFI_BOOT_MANAGER_LOAD_OPTION *Current;\r
2012 CONST EFI_DEVICE_PATH_PROTOCOL *FirstNode;\r
6b40e66a 2013\r
ac0a286f 2014 Current = ActiveOption[Idx].BootOption;\r
d27ec22d 2015 FirstNode = Current->FilePath;\r
6b40e66a 2016 if (FirstNode != NULL) {\r
ac0a286f
MK
2017 CHAR16 *Converted;\r
2018 STATIC CHAR16 ConvFallBack[] = L"<unable to convert>";\r
2019 BOOLEAN Keep;\r
6b40e66a
RN
2020\r
2021 Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);\r
2022 if (Converted == NULL) {\r
2023 Converted = ConvFallBack;\r
2024 }\r
2025\r
2026 Keep = TRUE;\r
ac0a286f
MK
2027 if ((DevicePathType (FirstNode) == MEDIA_DEVICE_PATH) &&\r
2028 (DevicePathSubType (FirstNode) == MEDIA_HARDDRIVE_DP))\r
2029 {\r
6b40e66a
RN
2030 //\r
2031 // drop HD()\r
2032 //\r
2033 Keep = FALSE;\r
ac0a286f
MK
2034 } else if ((DevicePathType (FirstNode) == ACPI_DEVICE_PATH) &&\r
2035 (DevicePathSubType (FirstNode) == ACPI_DP))\r
2036 {\r
2037 ACPI_HID_DEVICE_PATH *Acpi;\r
2038\r
2039 Acpi = (ACPI_HID_DEVICE_PATH *)FirstNode;\r
2040 if (((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) &&\r
2041 (EISA_ID_TO_NUM (Acpi->HID) == 0x0a03))\r
2042 {\r
6b40e66a
RN
2043 //\r
2044 // drop PciRoot() if we enabled the user to select PCI-like boot\r
2045 // options, by providing translation for such OFW device path\r
2046 // fragments\r
2047 //\r
2048 Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);\r
2049 }\r
ac0a286f
MK
2050 } else if ((DevicePathType (FirstNode) == HARDWARE_DEVICE_PATH) &&\r
2051 (DevicePathSubType (FirstNode) == HW_VENDOR_DP))\r
2052 {\r
2053 VENDOR_DEVICE_PATH *VenHw;\r
6b40e66a
RN
2054\r
2055 VenHw = (VENDOR_DEVICE_PATH *)FirstNode;\r
2056 if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {\r
2057 //\r
2058 // drop virtio-mmio if we enabled the user to select boot options\r
2059 // referencing such device paths\r
2060 //\r
2061 Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);\r
2062 }\r
2063 }\r
2064\r
2065 if (Keep) {\r
2066 Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);\r
2067 if (!RETURN_ERROR (Status)) {\r
ac0a286f
MK
2068 DEBUG ((\r
2069 DEBUG_VERBOSE,\r
2070 "%a: keeping \"%s\"\n",\r
2071 __FUNCTION__,\r
2072 Converted\r
2073 ));\r
6b40e66a
RN
2074 }\r
2075 } else {\r
ac0a286f
MK
2076 DEBUG ((\r
2077 DEBUG_VERBOSE,\r
2078 "%a: dropping \"%s\"\n",\r
2079 __FUNCTION__,\r
2080 Converted\r
2081 ));\r
6b40e66a
RN
2082 }\r
2083\r
2084 if (Converted != ConvFallBack) {\r
2085 FreePool (Converted);\r
2086 }\r
2087 }\r
2088 }\r
ac0a286f 2089\r
6b40e66a
RN
2090 ++Idx;\r
2091 }\r
ac0a286f 2092\r
6b40e66a
RN
2093 return Status;\r
2094}\r
2095\r
6b40e66a
RN
2096/**\r
2097 Delete Boot#### variables that stand for such active boot options that have\r
2098 been dropped (ie. have not been selected by either matching or "survival\r
2099 policy").\r
2100\r
2101 @param[in] ActiveOption The array of active boot options to scan. Each\r
2102 entry not marked as appended will trigger the\r
2103 deletion of the matching Boot#### variable.\r
2104\r
2105 @param[in] ActiveCount Number of elements in ActiveOption.\r
2106**/\r
2107STATIC\r
2108VOID\r
2109PruneBootVariables (\r
ac0a286f
MK
2110 IN CONST ACTIVE_OPTION *ActiveOption,\r
2111 IN UINTN ActiveCount\r
6b40e66a
RN
2112 )\r
2113{\r
ac0a286f 2114 UINTN Idx;\r
6b40e66a
RN
2115\r
2116 for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
2117 if (!ActiveOption[Idx].Appended) {\r
ac0a286f 2118 CHAR16 VariableName[9];\r
6b40e66a 2119\r
ac0a286f
MK
2120 UnicodeSPrintAsciiFormat (\r
2121 VariableName,\r
2122 sizeof VariableName,\r
2123 "Boot%04x",\r
2124 ActiveOption[Idx].BootOption->OptionNumber\r
2125 );\r
6b40e66a
RN
2126\r
2127 //\r
2128 // "The space consumed by the deleted variable may not be available until\r
2129 // the next power cycle", but that's good enough.\r
2130 //\r
ac0a286f
MK
2131 gRT->SetVariable (\r
2132 VariableName,\r
2133 &gEfiGlobalVariableGuid,\r
6b40e66a
RN
2134 0, // Attributes, 0 means deletion\r
2135 0, // DataSize, 0 means deletion\r
2136 NULL // Data\r
2137 );\r
2138 }\r
2139 }\r
2140}\r
2141\r
6b40e66a
RN
2142/**\r
2143\r
2144 Set the boot order based on configuration retrieved from QEMU.\r
2145\r
2146 Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the\r
2147 OpenFirmware device paths therein to UEFI device path fragments. Match the\r
2542feea
LE
2148 translated fragments against the current list of boot options, and rewrite\r
2149 the BootOrder NvVar so that it corresponds to the order described in fw_cfg.\r
6b40e66a 2150\r
51bd1f76
LE
2151 Platform BDS should call this function after connecting any expected boot\r
2152 devices and calling EfiBootManagerRefreshAllBootOption ().\r
6b40e66a
RN
2153\r
2154 @retval RETURN_SUCCESS BootOrder NvVar rewritten.\r
2155\r
2156 @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported.\r
2157\r
2158 @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg\r
2159 file, or no match found between the\r
2160 "bootorder" fw_cfg file and BootOptionList.\r
2161\r
2162 @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file.\r
2163\r
2164 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
2165\r
2166 @return Values returned by gBS->LocateProtocol ()\r
2167 or gRT->SetVariable ().\r
2168\r
2169**/\r
2170RETURN_STATUS\r
5851b825 2171EFIAPI\r
6b40e66a 2172SetBootOrderFromQemu (\r
2542feea 2173 VOID\r
6b40e66a
RN
2174 )\r
2175{\r
ac0a286f
MK
2176 RETURN_STATUS Status;\r
2177 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
2178 UINTN FwCfgSize;\r
2179 CHAR8 *FwCfg;\r
2180 CONST CHAR8 *FwCfgPtr;\r
6b40e66a 2181\r
ac0a286f
MK
2182 BOOT_ORDER BootOrder;\r
2183 ACTIVE_OPTION *ActiveOption;\r
2184 UINTN ActiveCount;\r
6b40e66a 2185\r
ac0a286f 2186 EXTRA_ROOT_BUS_MAP *ExtraPciRoots;\r
6b40e66a 2187\r
ac0a286f
MK
2188 UINTN TranslatedSize;\r
2189 CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];\r
2190 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2191 UINTN BootOptionCount;\r
d27ec22d 2192\r
6b40e66a
RN
2193 Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);\r
2194 if (Status != RETURN_SUCCESS) {\r
2195 return Status;\r
2196 }\r
2197\r
2198 if (FwCfgSize == 0) {\r
2199 return RETURN_NOT_FOUND;\r
2200 }\r
2201\r
2202 FwCfg = AllocatePool (FwCfgSize);\r
2203 if (FwCfg == NULL) {\r
2204 return RETURN_OUT_OF_RESOURCES;\r
2205 }\r
2206\r
2207 QemuFwCfgSelectItem (FwCfgItem);\r
2208 QemuFwCfgReadBytes (FwCfgSize, FwCfg);\r
2209 if (FwCfg[FwCfgSize - 1] != '\0') {\r
2210 Status = RETURN_INVALID_PARAMETER;\r
2211 goto ErrorFreeFwCfg;\r
2212 }\r
2213\r
2214 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));\r
2215 DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));\r
2216 DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));\r
2217 FwCfgPtr = FwCfg;\r
2218\r
2219 BootOrder.Produced = 0;\r
2220 BootOrder.Allocated = 1;\r
ac0a286f
MK
2221 BootOrder.Data = AllocatePool (\r
2222 BootOrder.Allocated * sizeof (*BootOrder.Data)\r
2223 );\r
6b40e66a
RN
2224 if (BootOrder.Data == NULL) {\r
2225 Status = RETURN_OUT_OF_RESOURCES;\r
2226 goto ErrorFreeFwCfg;\r
2227 }\r
2228\r
d27ec22d 2229 BootOptions = EfiBootManagerGetLoadOptions (\r
ac0a286f
MK
2230 &BootOptionCount,\r
2231 LoadOptionTypeBoot\r
d27ec22d
RN
2232 );\r
2233 if (BootOptions == NULL) {\r
2234 Status = RETURN_NOT_FOUND;\r
6b40e66a
RN
2235 goto ErrorFreeBootOrder;\r
2236 }\r
2237\r
d27ec22d 2238 Status = CollectActiveOptions (\r
ac0a286f
MK
2239 BootOptions,\r
2240 BootOptionCount,\r
2241 &ActiveOption,\r
2242 &ActiveCount\r
d27ec22d
RN
2243 );\r
2244 if (RETURN_ERROR (Status)) {\r
2245 goto ErrorFreeBootOptions;\r
2246 }\r
2247\r
6b40e66a
RN
2248 if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
2249 Status = CreateExtraRootBusMap (&ExtraPciRoots);\r
2250 if (EFI_ERROR (Status)) {\r
2251 goto ErrorFreeActiveOption;\r
2252 }\r
2253 } else {\r
2254 ExtraPciRoots = NULL;\r
2255 }\r
2256\r
2257 //\r
2258 // translate each OpenFirmware path\r
2259 //\r
10a82f7f 2260 TranslatedSize = ARRAY_SIZE (Translated);\r
ac0a286f
MK
2261 Status = TranslateOfwPath (\r
2262 &FwCfgPtr,\r
2263 ExtraPciRoots,\r
2264 Translated,\r
2265 &TranslatedSize\r
2266 );\r
6b40e66a
RN
2267 while (Status == RETURN_SUCCESS ||\r
2268 Status == RETURN_UNSUPPORTED ||\r
2269 Status == RETURN_PROTOCOL_ERROR ||\r
ac0a286f
MK
2270 Status == RETURN_BUFFER_TOO_SMALL)\r
2271 {\r
6b40e66a 2272 if (Status == RETURN_SUCCESS) {\r
ac0a286f 2273 UINTN Idx;\r
6b40e66a
RN
2274\r
2275 //\r
2276 // match translated OpenFirmware path against all active boot options\r
2277 //\r
2278 for (Idx = 0; Idx < ActiveCount; ++Idx) {\r
a0e761b1
LE
2279 if (!ActiveOption[Idx].Appended &&\r
2280 Match (\r
6b40e66a
RN
2281 Translated,\r
2282 TranslatedSize, // contains length, not size, in CHAR16's here\r
d27ec22d 2283 ActiveOption[Idx].BootOption->FilePath\r
6b40e66a 2284 )\r
ac0a286f
MK
2285 )\r
2286 {\r
6b40e66a
RN
2287 //\r
2288 // match found, store ID and continue with next OpenFirmware path\r
2289 //\r
2290 Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
2291 if (Status != RETURN_SUCCESS) {\r
2292 goto ErrorFreeExtraPciRoots;\r
2293 }\r
6b40e66a
RN
2294 }\r
2295 } // scanned all active boot options\r
2296 } // translation successful\r
2297\r
10a82f7f 2298 TranslatedSize = ARRAY_SIZE (Translated);\r
ac0a286f
MK
2299 Status = TranslateOfwPath (\r
2300 &FwCfgPtr,\r
2301 ExtraPciRoots,\r
2302 Translated,\r
2303 &TranslatedSize\r
2304 );\r
6b40e66a
RN
2305 } // scanning of OpenFirmware paths done\r
2306\r
ac0a286f 2307 if ((Status == RETURN_NOT_FOUND) && (BootOrder.Produced > 0)) {\r
6b40e66a
RN
2308 //\r
2309 // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.\r
2310 // Some of the active boot options that have not been selected over fw_cfg\r
2311 // should be preserved at the end of the boot order.\r
2312 //\r
2313 Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
2314 if (RETURN_ERROR (Status)) {\r
2315 goto ErrorFreeExtraPciRoots;\r
2316 }\r
2317\r
2318 //\r
2319 // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required\r
2320 // attributes.\r
2321 //\r
2322 Status = gRT->SetVariable (\r
2323 L"BootOrder",\r
2324 &gEfiGlobalVariableGuid,\r
2325 EFI_VARIABLE_NON_VOLATILE |\r
ac0a286f
MK
2326 EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
2327 EFI_VARIABLE_RUNTIME_ACCESS,\r
6b40e66a
RN
2328 BootOrder.Produced * sizeof (*BootOrder.Data),\r
2329 BootOrder.Data\r
2330 );\r
2331 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
2332 DEBUG ((\r
2333 DEBUG_ERROR,\r
2334 "%a: setting BootOrder: %r\n",\r
2335 __FUNCTION__,\r
2336 Status\r
2337 ));\r
6b40e66a
RN
2338 goto ErrorFreeExtraPciRoots;\r
2339 }\r
2340\r
2341 DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
2342 PruneBootVariables (ActiveOption, ActiveCount);\r
2343 }\r
2344\r
2345ErrorFreeExtraPciRoots:\r
2346 if (ExtraPciRoots != NULL) {\r
2347 DestroyExtraRootBusMap (ExtraPciRoots);\r
2348 }\r
2349\r
2350ErrorFreeActiveOption:\r
2351 FreePool (ActiveOption);\r
2352\r
d27ec22d
RN
2353ErrorFreeBootOptions:\r
2354 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2355\r
6b40e66a
RN
2356ErrorFreeBootOrder:\r
2357 FreePool (BootOrder.Data);\r
2358\r
2359ErrorFreeFwCfg:\r
2360 FreePool (FwCfg);\r
2361\r
2362 return Status;\r
2363}\r
2364\r
6b40e66a
RN
2365/**\r
2366 Calculate the number of seconds we should be showing the FrontPage progress\r
2367 bar for.\r
2368\r
2369 @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().\r
2370**/\r
2371UINT16\r
5851b825 2372EFIAPI\r
6b40e66a
RN
2373GetFrontPageTimeoutFromQemu (\r
2374 VOID\r
2375 )\r
2376{\r
ac0a286f
MK
2377 FIRMWARE_CONFIG_ITEM BootMenuWaitItem;\r
2378 UINTN BootMenuWaitSize;\r
43f3cfce
SB
2379 UINT16 Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
2380\r
2381 if (!QemuFwCfgIsAvailable ()) {\r
2382 return Timeout;\r
2383 }\r
6b40e66a
RN
2384\r
2385 QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);\r
2386 if (QemuFwCfgRead16 () == 0) {\r
2387 //\r
2388 // The user specified "-boot menu=off", or didn't specify "-boot\r
2389 // menu=(on|off)" at all. Return the platform default.\r
2390 //\r
2391 return PcdGet16 (PcdPlatformBootTimeOut);\r
2392 }\r
2393\r
ac0a286f
MK
2394 if (RETURN_ERROR (\r
2395 QemuFwCfgFindFile (\r
2396 "etc/boot-menu-wait",\r
2397 &BootMenuWaitItem,\r
2398 &BootMenuWaitSize\r
2399 )\r
2400 ) ||\r
2401 (BootMenuWaitSize != sizeof (UINT16)))\r
2402 {\r
6b40e66a
RN
2403 //\r
2404 // "-boot menu=on" was specified without "splash-time=N". In this case,\r
2405 // return three seconds if the platform default would cause us to skip the\r
2406 // front page, and return the platform default otherwise.\r
2407 //\r
6b40e66a
RN
2408 if (Timeout == 0) {\r
2409 Timeout = 3;\r
2410 }\r
ac0a286f 2411\r
6b40e66a
RN
2412 return Timeout;\r
2413 }\r
2414\r
2415 //\r
2416 // "-boot menu=on,splash-time=N" was specified, where N is in units of\r
2417 // milliseconds. The Intel BDS Front Page progress bar only supports whole\r
2418 // seconds, round N up.\r
2419 //\r
2420 QemuFwCfgSelectItem (BootMenuWaitItem);\r
2421 return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);\r
2422}\r