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