1 /** @file 2 OVMF ACPI support using QEMU's fw-cfg interface 3 4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR> 5 Copyright (C) 2012-2014, Red Hat, Inc. 6 7 This program and the accompanying materials 8 are licensed and made available under the terms and conditions of the BSD License 9 which accompanies this 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, 13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15 **/ 16 17 #include "AcpiPlatform.h" 18 #include "QemuLoader.h" 19 #include <Library/BaseMemoryLib.h> 20 #include <Library/MemoryAllocationLib.h> 21 #include <Library/QemuFwCfgLib.h> 22 #include <Library/DxeServicesTableLib.h> 23 #include <Library/PcdLib.h> 24 #include <Library/OrderedCollectionLib.h> 25 #include <IndustryStandard/Acpi.h> 26 27 28 // 29 // The user structure for the ordered collection that will track the fw_cfg 30 // blobs under processing. 31 // 32 typedef struct { 33 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg 34 // blob. This is the ordering / search 35 // key. 36 UINTN Size; // The number of bytes in this blob. 37 UINT8 *Base; // Pointer to the blob data. 38 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to 39 // only contain data that is directly 40 // part of ACPI tables. 41 } BLOB; 42 43 44 /** 45 Compare a standalone key against a user structure containing an embedded key. 46 47 @param[in] StandaloneKey Pointer to the bare key. 48 49 @param[in] UserStruct Pointer to the user structure with the embedded 50 key. 51 52 @retval <0 If StandaloneKey compares less than UserStruct's key. 53 54 @retval 0 If StandaloneKey compares equal to UserStruct's key. 55 56 @retval >0 If StandaloneKey compares greater than UserStruct's key. 57 **/ 58 STATIC 59 INTN 60 EFIAPI 61 BlobKeyCompare ( 62 IN CONST VOID *StandaloneKey, 63 IN CONST VOID *UserStruct 64 ) 65 { 66 CONST BLOB *Blob; 67 68 Blob = UserStruct; 69 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File); 70 } 71 72 73 /** 74 Comparator function for two user structures. 75 76 @param[in] UserStruct1 Pointer to the first user structure. 77 78 @param[in] UserStruct2 Pointer to the second user structure. 79 80 @retval <0 If UserStruct1 compares less than UserStruct2. 81 82 @retval 0 If UserStruct1 compares equal to UserStruct2. 83 84 @retval >0 If UserStruct1 compares greater than UserStruct2. 85 **/ 86 STATIC 87 INTN 88 EFIAPI 89 BlobCompare ( 90 IN CONST VOID *UserStruct1, 91 IN CONST VOID *UserStruct2 92 ) 93 { 94 CONST BLOB *Blob1; 95 96 Blob1 = UserStruct1; 97 return BlobKeyCompare (Blob1->File, UserStruct2); 98 } 99 100 101 /** 102 Process a QEMU_LOADER_ALLOCATE command. 103 104 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process. 105 106 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user 107 structures created thus far. 108 109 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been 110 allocated for the blob contents, and the 111 contents have been saved. A BLOB object (user 112 structure) has been allocated from pool memory, 113 referencing the blob contents. The BLOB user 114 structure has been linked into Tracker. 115 116 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in 117 Allocate, or the Allocate command references a 118 file that is already known by Tracker. 119 120 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in 121 Allocate. 122 123 @retval EFI_OUT_OF_RESOURCES Pool allocation failed. 124 125 @return Error codes from QemuFwCfgFindFile() and 126 gBS->AllocatePages(). 127 **/ 128 STATIC 129 EFI_STATUS 130 EFIAPI 131 ProcessCmdAllocate ( 132 IN CONST QEMU_LOADER_ALLOCATE *Allocate, 133 IN OUT ORDERED_COLLECTION *Tracker 134 ) 135 { 136 FIRMWARE_CONFIG_ITEM FwCfgItem; 137 UINTN FwCfgSize; 138 EFI_STATUS Status; 139 UINTN NumPages; 140 EFI_PHYSICAL_ADDRESS Address; 141 BLOB *Blob; 142 143 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { 144 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); 145 return EFI_PROTOCOL_ERROR; 146 } 147 148 if (Allocate->Alignment > EFI_PAGE_SIZE) { 149 DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__, 150 Allocate->Alignment)); 151 return EFI_UNSUPPORTED; 152 } 153 154 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize); 155 if (EFI_ERROR (Status)) { 156 DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__, 157 Allocate->File, Status)); 158 return Status; 159 } 160 161 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize); 162 Address = 0xFFFFFFFF; 163 Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages, 164 &Address); 165 if (EFI_ERROR (Status)) { 166 return Status; 167 } 168 169 Blob = AllocatePool (sizeof *Blob); 170 if (Blob == NULL) { 171 Status = EFI_OUT_OF_RESOURCES; 172 goto FreePages; 173 } 174 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE); 175 Blob->Size = FwCfgSize; 176 Blob->Base = (VOID *)(UINTN)Address; 177 Blob->HostsOnlyTableData = TRUE; 178 179 Status = OrderedCollectionInsert (Tracker, NULL, Blob); 180 if (Status == RETURN_ALREADY_STARTED) { 181 DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__, 182 Allocate->File)); 183 Status = EFI_PROTOCOL_ERROR; 184 } 185 if (EFI_ERROR (Status)) { 186 goto FreeBlob; 187 } 188 189 QemuFwCfgSelectItem (FwCfgItem); 190 QemuFwCfgReadBytes (FwCfgSize, Blob->Base); 191 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size); 192 193 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx " 194 "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment, 195 Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base)); 196 return EFI_SUCCESS; 197 198 FreeBlob: 199 FreePool (Blob); 200 201 FreePages: 202 gBS->FreePages (Address, NumPages); 203 204 return Status; 205 } 206 207 208 /** 209 Process a QEMU_LOADER_ADD_POINTER command. 210 211 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process. 212 213 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user 214 structures created thus far. 215 216 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in 217 AddPointer, or the AddPointer command references 218 a file unknown to Tracker, or the pointer to 219 relocate has invalid location, size, or value, or 220 the relocated pointer value is not representable 221 in the given pointer size. 222 223 @retval EFI_SUCCESS The pointer field inside the pointer blob has 224 been relocated. 225 **/ 226 STATIC 227 EFI_STATUS 228 EFIAPI 229 ProcessCmdAddPointer ( 230 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, 231 IN CONST ORDERED_COLLECTION *Tracker 232 ) 233 { 234 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; 235 BLOB *Blob, *Blob2; 236 UINT8 *PointerField; 237 UINT64 PointerValue; 238 239 if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' || 240 AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { 241 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); 242 return EFI_PROTOCOL_ERROR; 243 } 244 245 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile); 246 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile); 247 if (TrackerEntry == NULL || TrackerEntry2 == NULL) { 248 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n", 249 __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile)); 250 return EFI_PROTOCOL_ERROR; 251 } 252 253 Blob = OrderedCollectionUserStruct (TrackerEntry); 254 Blob2 = OrderedCollectionUserStruct (TrackerEntry2); 255 if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 && 256 AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) || 257 Blob->Size < AddPointer->PointerSize || 258 Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) { 259 DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n", 260 __FUNCTION__, AddPointer->PointerFile)); 261 return EFI_PROTOCOL_ERROR; 262 } 263 264 PointerField = Blob->Base + AddPointer->PointerOffset; 265 PointerValue = 0; 266 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize); 267 if (PointerValue >= Blob2->Size) { 268 DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__, 269 AddPointer->PointerFile)); 270 return EFI_PROTOCOL_ERROR; 271 } 272 273 // 274 // The memory allocation system ensures that the address of the byte past the 275 // last byte of any allocated object is expressible (no wraparound). 276 // 277 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size); 278 279 PointerValue += (UINT64)(UINTN)Blob2->Base; 280 if (RShiftU64 ( 281 RShiftU64 (PointerValue, AddPointer->PointerSize * 8 - 1), 1) != 0) { 282 DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in " 283 "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile)); 284 return EFI_PROTOCOL_ERROR; 285 } 286 287 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize); 288 289 DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" " 290 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__, 291 AddPointer->PointerFile, AddPointer->PointeeFile, 292 AddPointer->PointerOffset, AddPointer->PointerSize)); 293 return EFI_SUCCESS; 294 } 295 296 297 /** 298 Process a QEMU_LOADER_ADD_CHECKSUM command. 299 300 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process. 301 302 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user 303 structures created thus far. 304 305 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in 306 AddChecksum, or the AddChecksum command 307 references a file unknown to Tracker, or the 308 range to checksum is invalid. 309 310 @retval EFI_SUCCESS The requested range has been checksummed. 311 **/ 312 STATIC 313 EFI_STATUS 314 EFIAPI 315 ProcessCmdAddChecksum ( 316 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum, 317 IN CONST ORDERED_COLLECTION *Tracker 318 ) 319 { 320 ORDERED_COLLECTION_ENTRY *TrackerEntry; 321 BLOB *Blob; 322 323 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { 324 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); 325 return EFI_PROTOCOL_ERROR; 326 } 327 328 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File); 329 if (TrackerEntry == NULL) { 330 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__, 331 AddChecksum->File)); 332 return EFI_PROTOCOL_ERROR; 333 } 334 335 Blob = OrderedCollectionUserStruct (TrackerEntry); 336 if (Blob->Size <= AddChecksum->ResultOffset || 337 Blob->Size < AddChecksum->Length || 338 Blob->Size - AddChecksum->Length < AddChecksum->Start) { 339 DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n", 340 __FUNCTION__, AddChecksum->File)); 341 return EFI_PROTOCOL_ERROR; 342 } 343 344 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 ( 345 Blob->Base + AddChecksum->Start, 346 AddChecksum->Length 347 ); 348 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x " 349 "Length=0x%x\n", __FUNCTION__, AddChecksum->File, 350 AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length)); 351 return EFI_SUCCESS; 352 } 353 354 355 // 356 // We'll be saving the keys of installed tables so that we can roll them back 357 // in case of failure. 128 tables should be enough for anyone (TM). 358 // 359 #define INSTALLED_TABLES_MAX 128 360 361 /** 362 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte 363 array is an ACPI table, and if so, install it. 364 365 This function assumes that the entire QEMU linker/loader command file has 366 been processed successfully in a prior first pass. 367 368 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process. 369 370 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user 371 structures. 372 373 @param[in] AcpiProtocol The ACPI table protocol used to install tables. 374 375 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN 376 elements, allocated by the caller. On output, 377 the function will have stored (appended) the 378 AcpiProtocol-internal key of the ACPI table that 379 the function has installed, if the AddPointer 380 command identified an ACPI table that is 381 different from RSDT and XSDT. 382 383 @param[in,out] NumInstalled On input, the number of entries already used in 384 InstalledKey; it must be in [0, 385 INSTALLED_TABLES_MAX] inclusive. On output, the 386 parameter is incremented if the AddPointer 387 command identified an ACPI table that is 388 different from RSDT and XSDT. 389 390 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on 391 input. 392 393 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI 394 table different from RSDT and XSDT, but there 395 was no more room in InstalledKey. 396 397 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI 398 table different from RSDT and XSDT has been 399 installed (reflected by InstalledKey and 400 NumInstalled), or RSDT or XSDT has been 401 identified but not installed, or the fw_cfg 402 blob pointed-into by AddPointer has been 403 marked as hosting something else than just 404 direct ACPI table contents. 405 406 @return Error codes returned by 407 AcpiProtocol->InstallAcpiTable(). 408 **/ 409 STATIC 410 EFI_STATUS 411 EFIAPI 412 Process2ndPassCmdAddPointer ( 413 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, 414 IN CONST ORDERED_COLLECTION *Tracker, 415 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol, 416 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX], 417 IN OUT INT32 *NumInstalled 418 ) 419 { 420 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry; 421 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2; 422 CONST BLOB *Blob; 423 BLOB *Blob2; 424 CONST UINT8 *PointerField; 425 UINT64 PointerValue; 426 UINTN Blob2Remaining; 427 UINTN TableSize; 428 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; 429 CONST EFI_ACPI_DESCRIPTION_HEADER *Header; 430 EFI_STATUS Status; 431 432 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) { 433 return EFI_INVALID_PARAMETER; 434 } 435 436 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile); 437 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile); 438 Blob = OrderedCollectionUserStruct (TrackerEntry); 439 Blob2 = OrderedCollectionUserStruct (TrackerEntry2); 440 PointerField = Blob->Base + AddPointer->PointerOffset; 441 PointerValue = 0; 442 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize); 443 444 // 445 // We assert that PointerValue falls inside Blob2's contents. This is ensured 446 // by the Blob2->Size check and later checks in ProcessCmdAddPointer(). 447 // 448 Blob2Remaining = (UINTN)Blob2->Base; 449 ASSERT(PointerValue >= Blob2Remaining); 450 Blob2Remaining += Blob2->Size; 451 ASSERT (PointerValue < Blob2Remaining); 452 453 Blob2Remaining -= (UINTN) PointerValue; 454 DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx " 455 "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile, 456 PointerValue, (UINT64)Blob2Remaining)); 457 458 TableSize = 0; 459 460 // 461 // To make our job simple, the FACS has a custom header. Sigh. 462 // 463 if (sizeof *Facs <= Blob2Remaining) { 464 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue; 465 466 if (Facs->Length >= sizeof *Facs && 467 Facs->Length <= Blob2Remaining && 468 Facs->Signature == 469 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) { 470 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n", 471 (CONST CHAR8 *)&Facs->Signature, Facs->Length)); 472 TableSize = Facs->Length; 473 } 474 } 475 476 // 477 // check for the uniform tables 478 // 479 if (TableSize == 0 && sizeof *Header <= Blob2Remaining) { 480 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue; 481 482 if (Header->Length >= sizeof *Header && 483 Header->Length <= Blob2Remaining && 484 CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) { 485 // 486 // This looks very much like an ACPI table from QEMU: 487 // - Length field consistent with both ACPI and containing blob size 488 // - checksum is correct 489 // 490 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n", 491 (CONST CHAR8 *)&Header->Signature, Header->Length)); 492 TableSize = Header->Length; 493 494 // 495 // Skip RSDT and XSDT because those are handled by 496 // EFI_ACPI_TABLE_PROTOCOL automatically. 497 if (Header->Signature == 498 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE || 499 Header->Signature == 500 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) { 501 return EFI_SUCCESS; 502 } 503 } 504 } 505 506 if (TableSize == 0) { 507 DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n")); 508 Blob2->HostsOnlyTableData = FALSE; 509 return EFI_SUCCESS; 510 } 511 512 if (*NumInstalled == INSTALLED_TABLES_MAX) { 513 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n", 514 __FUNCTION__, INSTALLED_TABLES_MAX)); 515 return EFI_OUT_OF_RESOURCES; 516 } 517 518 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, 519 (VOID *)(UINTN)PointerValue, TableSize, 520 &InstalledKey[*NumInstalled]); 521 if (EFI_ERROR (Status)) { 522 DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__, 523 Status)); 524 return Status; 525 } 526 ++*NumInstalled; 527 return EFI_SUCCESS; 528 } 529 530 531 /** 532 Download, process, and install ACPI table data from the QEMU loader 533 interface. 534 535 @param[in] AcpiProtocol The ACPI table protocol used to install tables. 536 537 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU 538 loader command with unsupported parameters 539 has been found. 540 541 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg 542 files. 543 544 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than 545 INSTALLED_TABLES_MAX tables found. 546 547 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents. 548 549 @return Status codes returned by 550 AcpiProtocol->InstallAcpiTable(). 551 552 **/ 553 EFI_STATUS 554 EFIAPI 555 InstallQemuFwCfgTables ( 556 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol 557 ) 558 { 559 EFI_STATUS Status; 560 FIRMWARE_CONFIG_ITEM FwCfgItem; 561 UINTN FwCfgSize; 562 QEMU_LOADER_ENTRY *LoaderStart; 563 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd; 564 ORIGINAL_ATTRIBUTES *OriginalPciAttributes; 565 UINTN OriginalPciAttributesCount; 566 ORDERED_COLLECTION *Tracker; 567 UINTN *InstalledKey; 568 INT32 Installed; 569 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; 570 571 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize); 572 if (EFI_ERROR (Status)) { 573 return Status; 574 } 575 if (FwCfgSize % sizeof *LoaderEntry != 0) { 576 DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n", 577 __FUNCTION__, (UINT64)FwCfgSize)); 578 return EFI_PROTOCOL_ERROR; 579 } 580 581 LoaderStart = AllocatePool (FwCfgSize); 582 if (LoaderStart == NULL) { 583 return EFI_OUT_OF_RESOURCES; 584 } 585 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount); 586 QemuFwCfgSelectItem (FwCfgItem); 587 QemuFwCfgReadBytes (FwCfgSize, LoaderStart); 588 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount); 589 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; 590 591 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare); 592 if (Tracker == NULL) { 593 Status = EFI_OUT_OF_RESOURCES; 594 goto FreeLoader; 595 } 596 597 // 598 // first pass: process the commands 599 // 600 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { 601 switch (LoaderEntry->Type) { 602 case QemuLoaderCmdAllocate: 603 Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker); 604 break; 605 606 case QemuLoaderCmdAddPointer: 607 Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer, 608 Tracker); 609 break; 610 611 case QemuLoaderCmdAddChecksum: 612 Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum, 613 Tracker); 614 break; 615 616 default: 617 DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n", 618 __FUNCTION__, LoaderEntry->Type)); 619 break; 620 } 621 622 if (EFI_ERROR (Status)) { 623 goto FreeTracker; 624 } 625 } 626 627 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey); 628 if (InstalledKey == NULL) { 629 Status = EFI_OUT_OF_RESOURCES; 630 goto FreeTracker; 631 } 632 633 // 634 // second pass: identify and install ACPI tables 635 // 636 Installed = 0; 637 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { 638 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) { 639 Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer, 640 Tracker, AcpiProtocol, InstalledKey, &Installed); 641 if (EFI_ERROR (Status)) { 642 break; 643 } 644 } 645 } 646 647 if (EFI_ERROR (Status)) { 648 // 649 // roll back partial installation 650 // 651 while (Installed > 0) { 652 --Installed; 653 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]); 654 } 655 } else { 656 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed)); 657 } 658 659 FreePool (InstalledKey); 660 661 FreeTracker: 662 // 663 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in 664 // place only if we're exiting with success and the blob hosts data that is 665 // not directly part of some ACPI table. 666 // 667 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL; 668 TrackerEntry = TrackerEntry2) { 669 VOID *UserStruct; 670 BLOB *Blob; 671 672 TrackerEntry2 = OrderedCollectionNext (TrackerEntry); 673 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct); 674 Blob = UserStruct; 675 676 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) { 677 DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__, 678 Blob->File)); 679 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size)); 680 } 681 FreePool (Blob); 682 } 683 OrderedCollectionUninit (Tracker); 684 685 FreeLoader: 686 FreePool (LoaderStart); 687 688 return Status; 689 } 690