acpi.c: Add XSDT on QEMU

Since d8f2dce "acpi.c: Swap XSDT and RSDT for adding/finding tables"
XSDT is primarily used to add new tables or to find the S3 resume vector.
However with QEMU coreboot does not generate most ACPI tables but takes
them from whatever QEMU provides. Qemu only creates an RSDT and lacks an
XSDT.

To keep the codebase simple with the assumption that XSDT is always
present, create an XSDT based on the existing RSDT and update the
address in RSDP.

Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Change-Id: Ia9b7f090f55e436de98afad6f23597c3d426bb88
Reviewed-on: https://review.coreboot.org/c/coreboot/+/77385
Reviewed-by: Tim Wawrzynczak <inforichland@gmail.com>
Reviewed-by: Eric Lai <eric_lai@quanta.corp-partner.google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c
index e357f6a..17cd24c 100644
--- a/src/acpi/acpi.c
+++ b/src/acpi/acpi.c
@@ -48,7 +48,7 @@
 {
 	int i, entries_num;
 	acpi_rsdt_t *rsdt;
-	acpi_xsdt_t *xsdt = NULL;
+	acpi_xsdt_t *xsdt;
 
 	/* The 32bit RSDT may not be valid if tables live above 4GiB */
 	rsdt = (acpi_rsdt_t *)(uintptr_t)rsdp->rsdt_address;
@@ -1408,10 +1408,33 @@
 		if (!rsdp)
 			return fw;
 
+		current = fw;
+		current = acpi_align_current(current);
+		if (rsdp->xsdt_address == 0) {
+			xsdt = (acpi_xsdt_t *)current;
+			current += sizeof(acpi_xsdt_t);
+			current = acpi_align_current(current);
+
+			/*
+			 * Qemu only creates an RSDT.
+			 * Add an XSDT based on the existing RSDT entries.
+			 */
+			acpi_rsdt_t *existing_rsdt = (acpi_rsdt_t *)(uintptr_t)rsdp->rsdt_address;
+			acpi_write_rsdp(rsdp, existing_rsdt, xsdt, oem_id);
+			acpi_write_xsdt(xsdt, oem_id, oem_table_id);
+			/*
+			 * Copy existing entries to the new XSDT. This will override existing
+			 * RSDT entries with the same value.
+			 */
+			for (int i = 0; existing_rsdt->entry[i]; i++)
+				acpi_add_table(rsdp, (void *)(uintptr_t)existing_rsdt->entry[i]);
+
+		}
+
 		/* Add BOOT0000 for Linux google firmware driver */
 		printk(BIOS_DEBUG, "ACPI:     * SSDT\n");
-		ssdt = (acpi_header_t *)fw;
-		current = (unsigned long)ssdt + sizeof(acpi_header_t);
+		ssdt = (acpi_header_t *)current;
+		current += sizeof(acpi_header_t);
 
 		memset((void *)ssdt, 0, sizeof(acpi_header_t));