+\r
+/**\r
+ Download the ACPI table data file from QEMU and interpret it.\r
+\r
+ @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
+\r
+ @retval EFI_UNSUPPORTED Firmware configuration is unavailable.\r
+\r
+ @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
+ files.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @retval EFI_PROTOCOL_ERROR Found truncated or invalid ACPI table header\r
+ in the fw_cfg contents.\r
+\r
+ @return Status codes returned by\r
+ AcpiProtocol->InstallAcpiTable().\r
+\r
+**/\r
+\r
+//\r
+// We'll be saving the keys of installed tables so that we can roll them back\r
+// in case of failure. 128 tables should be enough for anyone (TM).\r
+//\r
+#define INSTALLED_TABLES_MAX 128\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+InstallQemuLinkedTables (\r
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ FIRMWARE_CONFIG_ITEM TablesFile;\r
+ UINTN TablesFileSize;\r
+ UINT8 *Tables;\r
+ UINTN *InstalledKey;\r
+ UINTN Processed;\r
+ INT32 Installed;\r
+\r
+ Status = QemuFwCfgFindFile ("etc/acpi/tables", &TablesFile, &TablesFileSize);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_INFO, "%a: \"etc/acpi/tables\" interface unavailable: %r\n",\r
+ __FUNCTION__, Status));\r
+ return Status;\r
+ }\r
+\r
+ Tables = AllocatePool (TablesFileSize);\r
+ if (Tables == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ QemuFwCfgSelectItem (TablesFile);\r
+ QemuFwCfgReadBytes (TablesFileSize, Tables);\r
+\r
+ InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
+ if (InstalledKey == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeTables;\r
+ }\r
+\r
+ Processed = 0;\r
+ Installed = 0;\r
+ while (Processed < TablesFileSize) {\r
+ UINTN Remaining;\r
+ EFI_ACPI_DESCRIPTION_HEADER *Probe;\r
+\r
+ Remaining = TablesFileSize - Processed;\r
+ if (Remaining < sizeof *Probe) {\r
+ Status = EFI_PROTOCOL_ERROR;\r
+ break;\r
+ }\r
+\r
+ Probe = (EFI_ACPI_DESCRIPTION_HEADER *) (Tables + Processed);\r
+ if (Remaining < Probe->Length || Probe->Length < sizeof *Probe) {\r
+ Status = EFI_PROTOCOL_ERROR;\r
+ break;\r
+ }\r
+\r
+ DEBUG ((EFI_D_VERBOSE, "%a: offset 0x%016Lx:"\r
+ " Signature=\"%-4.4a\" Length=0x%08x\n",\r
+ __FUNCTION__, (UINT64) Processed,\r
+ (CONST CHAR8 *) &Probe->Signature, Probe->Length));\r
+\r
+ //\r
+ // skip automatically handled "root" tables: RSDT, XSDT\r
+ //\r
+ if (Probe->Signature !=\r
+ EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE &&\r
+ Probe->Signature !=\r
+ EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
+ if (Installed == INSTALLED_TABLES_MAX) {\r
+ DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
+ __FUNCTION__, INSTALLED_TABLES_MAX));\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ break;\r
+ }\r
+\r
+ Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, Probe,\r
+ Probe->Length, &InstalledKey[Installed]);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR,\r
+ "%a: failed to install table \"%-4.4a\" at offset 0x%Lx: %r\n",\r
+ __FUNCTION__, (CONST CHAR8 *) &Probe->Signature, (UINT64) Processed,\r
+ Status));\r
+ break;\r
+ }\r
+\r
+ ++Installed;\r
+ }\r
+\r
+ Processed += Probe->Length;\r
+ }\r
+\r
+ //\r
+ // NUL-padding at the end is accepted\r
+ //\r
+ if (Status == EFI_PROTOCOL_ERROR) {\r
+ UINTN ErrorLocation;\r
+\r
+ ErrorLocation = Processed;\r
+ while (Processed < TablesFileSize && Tables[Processed] == '\0') {\r
+ ++Processed;\r
+ }\r
+ if (Processed < TablesFileSize) {\r
+ DEBUG ((EFI_D_ERROR, "%a: truncated or invalid ACPI table header at "\r
+ "offset 0x%Lx\n", __FUNCTION__, (UINT64) ErrorLocation));\r
+ }\r
+ }\r
+\r
+ if (Processed == TablesFileSize) {\r
+ DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ ASSERT (EFI_ERROR (Status));\r
+\r
+ //\r
+ // Roll back partial installation.\r
+ //\r
+ while (Installed > 0) {\r
+ --Installed;\r
+ AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
+ }\r
+ }\r
+\r
+ FreePool (InstalledKey);\r
+\r
+FreeTables:\r
+ FreePool (Tables);\r
+\r
+ return Status;\r
+}\r