hw/loongarch/virt: Rename filename acpi-build with virt-acpi-build
authorBibo Mao <maobibo@loongson.cn>
Sat, 8 Feb 2025 07:06:53 +0000 (15:06 +0800)
committerBibo Mao <maobibo@loongson.cn>
Wed, 12 Feb 2025 01:50:20 +0000 (09:50 +0800)
File acpi-build.c is relative with virt machine type, rename it with
virt-acpi-build.c

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
hw/loongarch/acpi-build.c [deleted file]
hw/loongarch/meson.build
hw/loongarch/virt-acpi-build.c [new file with mode: 0644]

diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c
deleted file mode 100644 (file)
index fdd62ac..0000000
+++ /dev/null
@@ -1,713 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Support for generating ACPI tables and passing them to Guests
- *
- * Copyright (C) 2021 Loongson Technology Corporation Limited
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qemu/error-report.h"
-#include "qemu/bitmap.h"
-#include "hw/pci/pci.h"
-#include "hw/core/cpu.h"
-#include "target/loongarch/cpu.h"
-#include "hw/acpi/acpi-defs.h"
-#include "hw/acpi/acpi.h"
-#include "hw/nvram/fw_cfg.h"
-#include "hw/acpi/bios-linker-loader.h"
-#include "migration/vmstate.h"
-#include "hw/mem/memory-device.h"
-#include "system/reset.h"
-
-/* Supported chipsets: */
-#include "hw/pci-host/ls7a.h"
-#include "hw/loongarch/virt.h"
-
-#include "hw/acpi/utils.h"
-#include "hw/acpi/pci.h"
-
-#include "qom/qom-qobject.h"
-
-#include "hw/acpi/generic_event_device.h"
-#include "hw/pci-host/gpex.h"
-#include "system/system.h"
-#include "system/tpm.h"
-#include "hw/platform-bus.h"
-#include "hw/acpi/aml-build.h"
-#include "hw/acpi/hmat.h"
-
-#define ACPI_BUILD_ALIGN_SIZE             0x1000
-#define ACPI_BUILD_TABLE_SIZE             0x20000
-
-#ifdef DEBUG_ACPI_BUILD
-#define ACPI_BUILD_DPRINTF(fmt, ...)        \
-    do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
-#else
-#define ACPI_BUILD_DPRINTF(fmt, ...)
-#endif
-
-/* build FADT */
-static void init_common_fadt_data(AcpiFadtData *data)
-{
-    AcpiFadtData fadt = {
-        /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */
-        .rev = 5,
-        .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) |
-                  (1 << ACPI_FADT_F_RESET_REG_SUP)),
-
-        /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */
-        .sleep_ctl = {
-            .space_id = AML_AS_SYSTEM_MEMORY,
-            .bit_width = 8,
-            .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL,
-        },
-        .sleep_sts = {
-            .space_id = AML_AS_SYSTEM_MEMORY,
-            .bit_width = 8,
-            .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS,
-        },
-
-        /* ACPI 5.0: 4.8.3.6 Reset Register */
-        .reset_reg = {
-            .space_id = AML_AS_SYSTEM_MEMORY,
-            .bit_width = 8,
-            .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET,
-        },
-        .reset_val = ACPI_GED_RESET_VALUE,
-    };
-    *data = fadt;
-}
-
-static void acpi_align_size(GArray *blob, unsigned align)
-{
-    /*
-     * Align size to multiple of given size. This reduces the chance
-     * we need to change size in the future (breaking cross version migration).
-     */
-    g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
-}
-
-/* build FACS */
-static void
-build_facs(GArray *table_data)
-{
-    const char *sig = "FACS";
-    const uint8_t reserved[40] = {};
-
-    g_array_append_vals(table_data, sig, 4); /* Signature */
-    build_append_int_noprefix(table_data, 64, 4); /* Length */
-    build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */
-    build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */
-    build_append_int_noprefix(table_data, 0, 4); /* Global Lock */
-    build_append_int_noprefix(table_data, 0, 4); /* Flags */
-    g_array_append_vals(table_data, reserved, 40); /* Reserved */
-}
-
-/* build MADT */
-static void
-build_madt(GArray *table_data, BIOSLinker *linker,
-           LoongArchVirtMachineState *lvms)
-{
-    MachineState *ms = MACHINE(lvms);
-    MachineClass *mc = MACHINE_GET_CLASS(ms);
-    const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
-    int i, arch_id;
-    AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id,
-                        .oem_table_id = lvms->oem_table_id };
-
-    acpi_table_begin(&table, table_data);
-
-    /* Local APIC Address */
-    build_append_int_noprefix(table_data, 0, 4);
-    build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
-
-    for (i = 0; i < arch_ids->len; i++) {
-        /* Processor Core Interrupt Controller Structure */
-        arch_id = arch_ids->cpus[i].arch_id;
-
-        build_append_int_noprefix(table_data, 17, 1);    /* Type */
-        build_append_int_noprefix(table_data, 15, 1);    /* Length */
-        build_append_int_noprefix(table_data, 1, 1);     /* Version */
-        build_append_int_noprefix(table_data, i, 4);     /* ACPI Processor ID */
-        build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */
-        build_append_int_noprefix(table_data, 1, 4);     /* Flags */
-    }
-
-    /* Extend I/O Interrupt Controller Structure */
-    build_append_int_noprefix(table_data, 20, 1);        /* Type */
-    build_append_int_noprefix(table_data, 13, 1);        /* Length */
-    build_append_int_noprefix(table_data, 1, 1);         /* Version */
-    build_append_int_noprefix(table_data, 3, 1);         /* Cascade */
-    build_append_int_noprefix(table_data, 0, 1);         /* Node */
-    build_append_int_noprefix(table_data, 0xffff, 8);    /* Node map */
-
-    /* MSI Interrupt Controller Structure */
-    build_append_int_noprefix(table_data, 21, 1);        /* Type */
-    build_append_int_noprefix(table_data, 19, 1);        /* Length */
-    build_append_int_noprefix(table_data, 1, 1);         /* Version */
-    build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */
-    build_append_int_noprefix(table_data, 0x40, 4);      /* Start */
-    build_append_int_noprefix(table_data, 0xc0, 4);      /* Count */
-
-    /* Bridge I/O Interrupt Controller Structure */
-    build_append_int_noprefix(table_data, 22, 1);        /* Type */
-    build_append_int_noprefix(table_data, 17, 1);        /* Length */
-    build_append_int_noprefix(table_data, 1, 1);         /* Version */
-    build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */
-    build_append_int_noprefix(table_data, 0x1000, 2);    /* Size */
-    build_append_int_noprefix(table_data, 0, 2);         /* Id */
-    build_append_int_noprefix(table_data, 0x40, 2);      /* Base */
-
-    acpi_table_end(linker, &table);
-}
-
-/* build SRAT */
-static void
-build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
-{
-    int i, arch_id, node_id;
-    hwaddr len, base, gap;
-    NodeInfo *numa_info;
-    int nodes, nb_numa_nodes = machine->numa_state->num_nodes;
-    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
-    MachineClass *mc = MACHINE_GET_CLASS(lvms);
-    const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine);
-    AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id,
-                        .oem_table_id = lvms->oem_table_id };
-
-    acpi_table_begin(&table, table_data);
-    build_append_int_noprefix(table_data, 1, 4); /* Reserved */
-    build_append_int_noprefix(table_data, 0, 8); /* Reserved */
-
-    for (i = 0; i < arch_ids->len; ++i) {
-        arch_id = arch_ids->cpus[i].arch_id;
-        node_id = arch_ids->cpus[i].props.node_id;
-
-        /* Processor Local APIC/SAPIC Affinity Structure */
-        build_append_int_noprefix(table_data, 0, 1);  /* Type  */
-        build_append_int_noprefix(table_data, 16, 1); /* Length */
-        /* Proximity Domain [7:0] */
-        build_append_int_noprefix(table_data, node_id, 1);
-        build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */
-        /* Flags, Table 5-36 */
-        build_append_int_noprefix(table_data, 1, 4);
-        build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */
-        /* Proximity Domain [31:8] */
-        build_append_int_noprefix(table_data, 0, 3);
-        build_append_int_noprefix(table_data, 0, 4); /* Reserved */
-    }
-
-    base = VIRT_LOWMEM_BASE;
-    gap = VIRT_LOWMEM_SIZE;
-    numa_info = machine->numa_state->nodes;
-    nodes = nb_numa_nodes;
-    if (!nodes) {
-        nodes = 1;
-    }
-
-    for (i = 0; i < nodes; i++) {
-        if (nb_numa_nodes) {
-            len = numa_info[i].node_mem;
-        } else {
-            len = machine->ram_size;
-        }
-
-        /*
-         * memory for the node splited into two part
-         *   lowram:  [base, +gap)
-         *   highram: [VIRT_HIGHMEM_BASE, +(len - gap))
-         */
-        if (len >= gap) {
-            build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED);
-            len -= gap;
-            base = VIRT_HIGHMEM_BASE;
-            gap = machine->ram_size - VIRT_LOWMEM_SIZE;
-        }
-
-        if (len) {
-            build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED);
-            base += len;
-            gap  -= len;
-        }
-    }
-
-    if (machine->device_memory) {
-        build_srat_memory(table_data, machine->device_memory->base,
-                          memory_region_size(&machine->device_memory->mr),
-                          nodes - 1,
-                          MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
-    }
-
-    acpi_table_end(linker, &table);
-}
-
-/*
- * Serial Port Console Redirection Table (SPCR)
- * https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table
- */
-static void
-spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine)
-{
-    LoongArchVirtMachineState *lvms;
-    AcpiSpcrData serial = {
-        .interface_type = 0,       /* 16550 compatible */
-        .base_addr.id = AML_AS_SYSTEM_MEMORY,
-        .base_addr.width = 32,
-        .base_addr.offset = 0,
-        .base_addr.size = 1,
-        .base_addr.addr = VIRT_UART_BASE,
-        .interrupt_type = 0,       /* Interrupt not supported */
-        .pc_interrupt = 0,
-        .interrupt = VIRT_UART_IRQ,
-        .baud_rate = 7,            /* 115200 */
-        .parity = 0,
-        .stop_bits = 1,
-        .flow_control = 0,
-        .terminal_type = 3,        /* ANSI */
-        .language = 0,             /* Language */
-        .pci_device_id = 0xffff,   /* not a PCI device*/
-        .pci_vendor_id = 0xffff,   /* not a PCI device*/
-        .pci_bus = 0,
-        .pci_device = 0,
-        .pci_function = 0,
-        .pci_flags = 0,
-        .pci_segment = 0,
-    };
-
-    lvms = LOONGARCH_VIRT_MACHINE(machine);
-    /*
-     * Passing NULL as the SPCR Table for Revision 2 doesn't support
-     * NameSpaceString.
-     */
-    build_spcr(table_data, linker, &serial, 2, lvms->oem_id,
-               lvms->oem_table_id, NULL);
-}
-
-typedef
-struct AcpiBuildState {
-    /* Copy of table in RAM (for patching). */
-    MemoryRegion *table_mr;
-    /* Is table patched? */
-    uint8_t patched;
-    void *rsdp;
-    MemoryRegion *rsdp_mr;
-    MemoryRegion *linker_mr;
-} AcpiBuildState;
-
-static void build_uart_device_aml(Aml *table, int index)
-{
-    Aml *dev;
-    Aml *crs;
-    Aml *pkg0, *pkg1, *pkg2;
-    Aml *scope;
-    uint32_t uart_irq;
-    uint64_t base;
-
-    uart_irq = VIRT_UART_IRQ + index;
-    base = VIRT_UART_BASE + index * VIRT_UART_SIZE;
-    scope = aml_scope("_SB");
-    dev = aml_device("COM%d", index);
-    aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501")));
-    aml_append(dev, aml_name_decl("_UID", aml_int(index)));
-    aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
-    crs = aml_resource_template();
-    aml_append(crs,
-        aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
-                         AML_NON_CACHEABLE, AML_READ_WRITE,
-                         0, base, base + VIRT_UART_SIZE - 1,
-                         0, VIRT_UART_SIZE));
-    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
-                                  AML_SHARED, &uart_irq, 1));
-    aml_append(dev, aml_name_decl("_CRS", crs));
-    pkg0 = aml_package(0x2);
-    aml_append(pkg0, aml_int(0x05F5E100));
-    aml_append(pkg0, aml_string("clock-frenquency"));
-    pkg1 = aml_package(0x1);
-    aml_append(pkg1, pkg0);
-    pkg2 = aml_package(0x2);
-    aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
-    aml_append(pkg2, pkg1);
-    aml_append(dev, aml_name_decl("_DSD", pkg2));
-    aml_append(scope, dev);
-    aml_append(table, scope);
-}
-
-static void
-build_la_ged_aml(Aml *dsdt, MachineState *machine)
-{
-    uint32_t event;
-    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
-
-    build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
-                  HOTPLUG_HANDLER(lvms->acpi_ged),
-                  VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
-                  VIRT_GED_EVT_ADDR);
-    event = object_property_get_uint(OBJECT(lvms->acpi_ged),
-                                     "ged-event", &error_abort);
-    if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
-        build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL,
-                                 AML_SYSTEM_MEMORY,
-                                 VIRT_GED_MEM_ADDR);
-    }
-    acpi_dsdt_add_power_button(dsdt);
-}
-
-static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms)
-{
-    struct GPEXConfig cfg = {
-        .mmio64.base = VIRT_PCI_MEM_BASE,
-        .mmio64.size = VIRT_PCI_MEM_SIZE,
-        .pio.base    = VIRT_PCI_IO_BASE,
-        .pio.size    = VIRT_PCI_IO_SIZE,
-        .ecam.base   = VIRT_PCI_CFG_BASE,
-        .ecam.size   = VIRT_PCI_CFG_SIZE,
-        .irq         = VIRT_GSI_BASE + VIRT_DEVICE_IRQS,
-        .bus         = lvms->pci_bus,
-    };
-
-    acpi_dsdt_add_gpex(scope, &cfg);
-}
-
-static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms)
-{
-    Aml *dev, *crs;
-    MemoryRegion *flash_mem;
-
-    hwaddr flash0_base;
-    hwaddr flash0_size;
-
-    hwaddr flash1_base;
-    hwaddr flash1_size;
-
-    flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
-    flash0_base = flash_mem->addr;
-    flash0_size = memory_region_size(flash_mem);
-
-    flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
-    flash1_base = flash_mem->addr;
-    flash1_size = memory_region_size(flash_mem);
-
-    dev = aml_device("FLS0");
-    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
-    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
-
-    crs = aml_resource_template();
-    aml_append(crs, aml_memory32_fixed(flash0_base, flash0_size,
-                                       AML_READ_WRITE));
-    aml_append(dev, aml_name_decl("_CRS", crs));
-    aml_append(scope, dev);
-
-    dev = aml_device("FLS1");
-    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
-    aml_append(dev, aml_name_decl("_UID", aml_int(1)));
-
-    crs = aml_resource_template();
-    aml_append(crs, aml_memory32_fixed(flash1_base, flash1_size,
-                                       AML_READ_WRITE));
-    aml_append(dev, aml_name_decl("_CRS", crs));
-    aml_append(scope, dev);
-}
-
-#ifdef CONFIG_TPM
-static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms)
-{
-    PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
-    hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS;
-    SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find());
-    MemoryRegion *sbdev_mr;
-    hwaddr tpm_base;
-
-    if (!sbdev) {
-        return;
-    }
-
-    tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
-    assert(tpm_base != -1);
-
-    tpm_base += pbus_base;
-
-    sbdev_mr = sysbus_mmio_get_region(sbdev, 0);
-
-    Aml *dev = aml_device("TPM0");
-    aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
-    aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device")));
-    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
-
-    Aml *crs = aml_resource_template();
-    aml_append(crs,
-               aml_memory32_fixed(tpm_base,
-                                  (uint32_t)memory_region_size(sbdev_mr),
-                                  AML_READ_WRITE));
-    aml_append(dev, aml_name_decl("_CRS", crs));
-    aml_append(scope, dev);
-}
-#endif
-
-/* build DSDT */
-static void
-build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
-{
-    int i;
-    Aml *dsdt, *scope, *pkg;
-    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
-    AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id,
-                        .oem_table_id = lvms->oem_table_id };
-
-    acpi_table_begin(&table, table_data);
-    dsdt = init_aml_allocator();
-    for (i = 0; i < VIRT_UART_COUNT; i++) {
-        build_uart_device_aml(dsdt, i);
-    }
-    build_pci_device_aml(dsdt, lvms);
-    build_la_ged_aml(dsdt, machine);
-    build_flash_aml(dsdt, lvms);
-#ifdef CONFIG_TPM
-    acpi_dsdt_add_tpm(dsdt, lvms);
-#endif
-    /* System State Package */
-    scope = aml_scope("\\");
-    pkg = aml_package(4);
-    aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5));
-    aml_append(pkg, aml_int(0)); /* ignored */
-    aml_append(pkg, aml_int(0)); /* reserved */
-    aml_append(pkg, aml_int(0)); /* reserved */
-    aml_append(scope, aml_name_decl("_S5", pkg));
-    aml_append(dsdt, scope);
-    /* Copy AML table into ACPI tables blob and patch header there */
-    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
-    acpi_table_end(linker, &table);
-    free_aml_allocator();
-}
-
-static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
-{
-    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
-    GArray *table_offsets;
-    AcpiFadtData fadt_data;
-    unsigned facs, rsdt, dsdt;
-    uint8_t *u;
-    GArray *tables_blob = tables->table_data;
-
-    init_common_fadt_data(&fadt_data);
-
-    table_offsets = g_array_new(false, true, sizeof(uint32_t));
-    ACPI_BUILD_DPRINTF("init ACPI tables\n");
-
-    bios_linker_loader_alloc(tables->linker,
-                             ACPI_BUILD_TABLE_FILE, tables_blob,
-                             64, false);
-
-    /*
-     * FACS is pointed to by FADT.
-     * We place it first since it's the only table that has alignment
-     * requirements.
-     */
-    facs = tables_blob->len;
-    build_facs(tables_blob);
-
-    /* DSDT is pointed to by FADT */
-    dsdt = tables_blob->len;
-    build_dsdt(tables_blob, tables->linker, machine);
-
-    /* ACPI tables pointed to by RSDT */
-    acpi_add_table(table_offsets, tables_blob);
-    fadt_data.facs_tbl_offset = &facs;
-    fadt_data.dsdt_tbl_offset = &dsdt;
-    fadt_data.xdsdt_tbl_offset = &dsdt;
-    build_fadt(tables_blob, tables->linker, &fadt_data,
-               lvms->oem_id, lvms->oem_table_id);
-
-    acpi_add_table(table_offsets, tables_blob);
-    build_madt(tables_blob, tables->linker, lvms);
-
-    acpi_add_table(table_offsets, tables_blob);
-    build_pptt(tables_blob, tables->linker, machine,
-               lvms->oem_id, lvms->oem_table_id);
-
-    acpi_add_table(table_offsets, tables_blob);
-    build_srat(tables_blob, tables->linker, machine);
-    acpi_add_table(table_offsets, tables_blob);
-    spcr_setup(tables_blob, tables->linker, machine);
-
-    if (machine->numa_state->num_nodes) {
-        if (machine->numa_state->have_numa_distance) {
-            acpi_add_table(table_offsets, tables_blob);
-            build_slit(tables_blob, tables->linker, machine, lvms->oem_id,
-                       lvms->oem_table_id);
-        }
-        if (machine->numa_state->hmat_enabled) {
-            acpi_add_table(table_offsets, tables_blob);
-            build_hmat(tables_blob, tables->linker, machine->numa_state,
-                       lvms->oem_id, lvms->oem_table_id);
-        }
-    }
-
-    acpi_add_table(table_offsets, tables_blob);
-    {
-        AcpiMcfgInfo mcfg = {
-           .base = cpu_to_le64(VIRT_PCI_CFG_BASE),
-           .size = cpu_to_le64(VIRT_PCI_CFG_SIZE),
-        };
-        build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id,
-                   lvms->oem_table_id);
-    }
-
-#ifdef CONFIG_TPM
-    /* TPM info */
-    if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) {
-        acpi_add_table(table_offsets, tables_blob);
-        build_tpm2(tables_blob, tables->linker,
-                   tables->tcpalog, lvms->oem_id,
-                   lvms->oem_table_id);
-    }
-#endif
-    /* Add tables supplied by user (if any) */
-    for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
-        unsigned len = acpi_table_len(u);
-
-        acpi_add_table(table_offsets, tables_blob);
-        g_array_append_vals(tables_blob, u, len);
-    }
-
-    /* RSDT is pointed to by RSDP */
-    rsdt = tables_blob->len;
-    build_rsdt(tables_blob, tables->linker, table_offsets,
-               lvms->oem_id, lvms->oem_table_id);
-
-    /* RSDP is in FSEG memory, so allocate it separately */
-    {
-        AcpiRsdpData rsdp_data = {
-            .revision = 0,
-            .oem_id = lvms->oem_id,
-            .xsdt_tbl_offset = NULL,
-            .rsdt_tbl_offset = &rsdt,
-        };
-        build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
-    }
-
-    /*
-     * The align size is 128, warn if 64k is not enough therefore
-     * the align size could be resized.
-     */
-    if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
-        warn_report("ACPI table size %u exceeds %d bytes,"
-                    " migration may not work",
-                    tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
-        error_printf("Try removing CPUs, NUMA nodes, memory slots"
-                     " or PCI bridges.\n");
-    }
-
-    acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE);
-
-    /* Cleanup memory that's no longer used. */
-    g_array_free(table_offsets, true);
-}
-
-static void acpi_ram_update(MemoryRegion *mr, GArray *data)
-{
-    uint32_t size = acpi_data_len(data);
-
-    /*
-     * Make sure RAM size is correct - in case it got changed
-     * e.g. by migration
-     */
-    memory_region_ram_resize(mr, size, &error_abort);
-
-    memcpy(memory_region_get_ram_ptr(mr), data->data, size);
-    memory_region_set_dirty(mr, 0, size);
-}
-
-static void acpi_build_update(void *build_opaque)
-{
-    AcpiBuildState *build_state = build_opaque;
-    AcpiBuildTables tables;
-
-    /* No state to update or already patched? Nothing to do. */
-    if (!build_state || build_state->patched) {
-        return;
-    }
-    build_state->patched = 1;
-
-    acpi_build_tables_init(&tables);
-
-    acpi_build(&tables, MACHINE(qdev_get_machine()));
-
-    acpi_ram_update(build_state->table_mr, tables.table_data);
-    acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
-    acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
-
-    acpi_build_tables_cleanup(&tables, true);
-}
-
-static void acpi_build_reset(void *build_opaque)
-{
-    AcpiBuildState *build_state = build_opaque;
-    build_state->patched = 0;
-}
-
-static const VMStateDescription vmstate_acpi_build = {
-    .name = "acpi_build",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (const VMStateField[]) {
-        VMSTATE_UINT8(patched, AcpiBuildState),
-        VMSTATE_END_OF_LIST()
-    },
-};
-
-static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
-{
-    if (lvms->acpi == ON_OFF_AUTO_OFF) {
-        return false;
-    }
-    return true;
-}
-
-void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
-{
-    AcpiBuildTables tables;
-    AcpiBuildState *build_state;
-
-    if (!lvms->fw_cfg) {
-        ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
-        return;
-    }
-
-    if (!loongarch_is_acpi_enabled(lvms)) {
-        ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
-        return;
-    }
-
-    build_state = g_malloc0(sizeof *build_state);
-
-    acpi_build_tables_init(&tables);
-    acpi_build(&tables, MACHINE(lvms));
-
-    /* Now expose it all to Guest */
-    build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
-                                              build_state, tables.table_data,
-                                              ACPI_BUILD_TABLE_FILE);
-    assert(build_state->table_mr != NULL);
-
-    build_state->linker_mr =
-        acpi_add_rom_blob(acpi_build_update, build_state,
-                          tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE);
-
-    build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update,
-                                             build_state, tables.rsdp,
-                                             ACPI_BUILD_RSDP_FILE);
-
-    fw_cfg_add_file(lvms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data,
-                    acpi_data_len(tables.tcpalog));
-
-    qemu_register_reset(acpi_build_reset, build_state);
-    acpi_build_reset(build_state);
-    vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
-
-    /*
-     * Cleanup tables but don't free the memory: we track it
-     * in build_state.
-     */
-    acpi_build_tables_cleanup(&tables, false);
-}
index 005f017e211db2338662b9074dd263fa032f0fc2..3f020de7dc6ded06d55ea61b76f908dd3ff25c4d 100644 (file)
@@ -4,6 +4,6 @@ loongarch_ss.add(files(
 ))
 common_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('fw_cfg.c'))
 loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c'))
-loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c'))
+loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
 
 hw_arch += {'loongarch': loongarch_ss}
diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c
new file mode 100644 (file)
index 0000000..fdd62ac
--- /dev/null
@@ -0,0 +1,713 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for generating ACPI tables and passing them to Guests
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/bitmap.h"
+#include "hw/pci/pci.h"
+#include "hw/core/cpu.h"
+#include "target/loongarch/cpu.h"
+#include "hw/acpi/acpi-defs.h"
+#include "hw/acpi/acpi.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "migration/vmstate.h"
+#include "hw/mem/memory-device.h"
+#include "system/reset.h"
+
+/* Supported chipsets: */
+#include "hw/pci-host/ls7a.h"
+#include "hw/loongarch/virt.h"
+
+#include "hw/acpi/utils.h"
+#include "hw/acpi/pci.h"
+
+#include "qom/qom-qobject.h"
+
+#include "hw/acpi/generic_event_device.h"
+#include "hw/pci-host/gpex.h"
+#include "system/system.h"
+#include "system/tpm.h"
+#include "hw/platform-bus.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/hmat.h"
+
+#define ACPI_BUILD_ALIGN_SIZE             0x1000
+#define ACPI_BUILD_TABLE_SIZE             0x20000
+
+#ifdef DEBUG_ACPI_BUILD
+#define ACPI_BUILD_DPRINTF(fmt, ...)        \
+    do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define ACPI_BUILD_DPRINTF(fmt, ...)
+#endif
+
+/* build FADT */
+static void init_common_fadt_data(AcpiFadtData *data)
+{
+    AcpiFadtData fadt = {
+        /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */
+        .rev = 5,
+        .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) |
+                  (1 << ACPI_FADT_F_RESET_REG_SUP)),
+
+        /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */
+        .sleep_ctl = {
+            .space_id = AML_AS_SYSTEM_MEMORY,
+            .bit_width = 8,
+            .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL,
+        },
+        .sleep_sts = {
+            .space_id = AML_AS_SYSTEM_MEMORY,
+            .bit_width = 8,
+            .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS,
+        },
+
+        /* ACPI 5.0: 4.8.3.6 Reset Register */
+        .reset_reg = {
+            .space_id = AML_AS_SYSTEM_MEMORY,
+            .bit_width = 8,
+            .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET,
+        },
+        .reset_val = ACPI_GED_RESET_VALUE,
+    };
+    *data = fadt;
+}
+
+static void acpi_align_size(GArray *blob, unsigned align)
+{
+    /*
+     * Align size to multiple of given size. This reduces the chance
+     * we need to change size in the future (breaking cross version migration).
+     */
+    g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
+}
+
+/* build FACS */
+static void
+build_facs(GArray *table_data)
+{
+    const char *sig = "FACS";
+    const uint8_t reserved[40] = {};
+
+    g_array_append_vals(table_data, sig, 4); /* Signature */
+    build_append_int_noprefix(table_data, 64, 4); /* Length */
+    build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */
+    build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */
+    build_append_int_noprefix(table_data, 0, 4); /* Global Lock */
+    build_append_int_noprefix(table_data, 0, 4); /* Flags */
+    g_array_append_vals(table_data, reserved, 40); /* Reserved */
+}
+
+/* build MADT */
+static void
+build_madt(GArray *table_data, BIOSLinker *linker,
+           LoongArchVirtMachineState *lvms)
+{
+    MachineState *ms = MACHINE(lvms);
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
+    int i, arch_id;
+    AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id,
+                        .oem_table_id = lvms->oem_table_id };
+
+    acpi_table_begin(&table, table_data);
+
+    /* Local APIC Address */
+    build_append_int_noprefix(table_data, 0, 4);
+    build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
+
+    for (i = 0; i < arch_ids->len; i++) {
+        /* Processor Core Interrupt Controller Structure */
+        arch_id = arch_ids->cpus[i].arch_id;
+
+        build_append_int_noprefix(table_data, 17, 1);    /* Type */
+        build_append_int_noprefix(table_data, 15, 1);    /* Length */
+        build_append_int_noprefix(table_data, 1, 1);     /* Version */
+        build_append_int_noprefix(table_data, i, 4);     /* ACPI Processor ID */
+        build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */
+        build_append_int_noprefix(table_data, 1, 4);     /* Flags */
+    }
+
+    /* Extend I/O Interrupt Controller Structure */
+    build_append_int_noprefix(table_data, 20, 1);        /* Type */
+    build_append_int_noprefix(table_data, 13, 1);        /* Length */
+    build_append_int_noprefix(table_data, 1, 1);         /* Version */
+    build_append_int_noprefix(table_data, 3, 1);         /* Cascade */
+    build_append_int_noprefix(table_data, 0, 1);         /* Node */
+    build_append_int_noprefix(table_data, 0xffff, 8);    /* Node map */
+
+    /* MSI Interrupt Controller Structure */
+    build_append_int_noprefix(table_data, 21, 1);        /* Type */
+    build_append_int_noprefix(table_data, 19, 1);        /* Length */
+    build_append_int_noprefix(table_data, 1, 1);         /* Version */
+    build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */
+    build_append_int_noprefix(table_data, 0x40, 4);      /* Start */
+    build_append_int_noprefix(table_data, 0xc0, 4);      /* Count */
+
+    /* Bridge I/O Interrupt Controller Structure */
+    build_append_int_noprefix(table_data, 22, 1);        /* Type */
+    build_append_int_noprefix(table_data, 17, 1);        /* Length */
+    build_append_int_noprefix(table_data, 1, 1);         /* Version */
+    build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */
+    build_append_int_noprefix(table_data, 0x1000, 2);    /* Size */
+    build_append_int_noprefix(table_data, 0, 2);         /* Id */
+    build_append_int_noprefix(table_data, 0x40, 2);      /* Base */
+
+    acpi_table_end(linker, &table);
+}
+
+/* build SRAT */
+static void
+build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
+{
+    int i, arch_id, node_id;
+    hwaddr len, base, gap;
+    NodeInfo *numa_info;
+    int nodes, nb_numa_nodes = machine->numa_state->num_nodes;
+    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
+    MachineClass *mc = MACHINE_GET_CLASS(lvms);
+    const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine);
+    AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id,
+                        .oem_table_id = lvms->oem_table_id };
+
+    acpi_table_begin(&table, table_data);
+    build_append_int_noprefix(table_data, 1, 4); /* Reserved */
+    build_append_int_noprefix(table_data, 0, 8); /* Reserved */
+
+    for (i = 0; i < arch_ids->len; ++i) {
+        arch_id = arch_ids->cpus[i].arch_id;
+        node_id = arch_ids->cpus[i].props.node_id;
+
+        /* Processor Local APIC/SAPIC Affinity Structure */
+        build_append_int_noprefix(table_data, 0, 1);  /* Type  */
+        build_append_int_noprefix(table_data, 16, 1); /* Length */
+        /* Proximity Domain [7:0] */
+        build_append_int_noprefix(table_data, node_id, 1);
+        build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */
+        /* Flags, Table 5-36 */
+        build_append_int_noprefix(table_data, 1, 4);
+        build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */
+        /* Proximity Domain [31:8] */
+        build_append_int_noprefix(table_data, 0, 3);
+        build_append_int_noprefix(table_data, 0, 4); /* Reserved */
+    }
+
+    base = VIRT_LOWMEM_BASE;
+    gap = VIRT_LOWMEM_SIZE;
+    numa_info = machine->numa_state->nodes;
+    nodes = nb_numa_nodes;
+    if (!nodes) {
+        nodes = 1;
+    }
+
+    for (i = 0; i < nodes; i++) {
+        if (nb_numa_nodes) {
+            len = numa_info[i].node_mem;
+        } else {
+            len = machine->ram_size;
+        }
+
+        /*
+         * memory for the node splited into two part
+         *   lowram:  [base, +gap)
+         *   highram: [VIRT_HIGHMEM_BASE, +(len - gap))
+         */
+        if (len >= gap) {
+            build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED);
+            len -= gap;
+            base = VIRT_HIGHMEM_BASE;
+            gap = machine->ram_size - VIRT_LOWMEM_SIZE;
+        }
+
+        if (len) {
+            build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED);
+            base += len;
+            gap  -= len;
+        }
+    }
+
+    if (machine->device_memory) {
+        build_srat_memory(table_data, machine->device_memory->base,
+                          memory_region_size(&machine->device_memory->mr),
+                          nodes - 1,
+                          MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
+    }
+
+    acpi_table_end(linker, &table);
+}
+
+/*
+ * Serial Port Console Redirection Table (SPCR)
+ * https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table
+ */
+static void
+spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine)
+{
+    LoongArchVirtMachineState *lvms;
+    AcpiSpcrData serial = {
+        .interface_type = 0,       /* 16550 compatible */
+        .base_addr.id = AML_AS_SYSTEM_MEMORY,
+        .base_addr.width = 32,
+        .base_addr.offset = 0,
+        .base_addr.size = 1,
+        .base_addr.addr = VIRT_UART_BASE,
+        .interrupt_type = 0,       /* Interrupt not supported */
+        .pc_interrupt = 0,
+        .interrupt = VIRT_UART_IRQ,
+        .baud_rate = 7,            /* 115200 */
+        .parity = 0,
+        .stop_bits = 1,
+        .flow_control = 0,
+        .terminal_type = 3,        /* ANSI */
+        .language = 0,             /* Language */
+        .pci_device_id = 0xffff,   /* not a PCI device*/
+        .pci_vendor_id = 0xffff,   /* not a PCI device*/
+        .pci_bus = 0,
+        .pci_device = 0,
+        .pci_function = 0,
+        .pci_flags = 0,
+        .pci_segment = 0,
+    };
+
+    lvms = LOONGARCH_VIRT_MACHINE(machine);
+    /*
+     * Passing NULL as the SPCR Table for Revision 2 doesn't support
+     * NameSpaceString.
+     */
+    build_spcr(table_data, linker, &serial, 2, lvms->oem_id,
+               lvms->oem_table_id, NULL);
+}
+
+typedef
+struct AcpiBuildState {
+    /* Copy of table in RAM (for patching). */
+    MemoryRegion *table_mr;
+    /* Is table patched? */
+    uint8_t patched;
+    void *rsdp;
+    MemoryRegion *rsdp_mr;
+    MemoryRegion *linker_mr;
+} AcpiBuildState;
+
+static void build_uart_device_aml(Aml *table, int index)
+{
+    Aml *dev;
+    Aml *crs;
+    Aml *pkg0, *pkg1, *pkg2;
+    Aml *scope;
+    uint32_t uart_irq;
+    uint64_t base;
+
+    uart_irq = VIRT_UART_IRQ + index;
+    base = VIRT_UART_BASE + index * VIRT_UART_SIZE;
+    scope = aml_scope("_SB");
+    dev = aml_device("COM%d", index);
+    aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(index)));
+    aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
+    crs = aml_resource_template();
+    aml_append(crs,
+        aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+                         AML_NON_CACHEABLE, AML_READ_WRITE,
+                         0, base, base + VIRT_UART_SIZE - 1,
+                         0, VIRT_UART_SIZE));
+    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+                                  AML_SHARED, &uart_irq, 1));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    pkg0 = aml_package(0x2);
+    aml_append(pkg0, aml_int(0x05F5E100));
+    aml_append(pkg0, aml_string("clock-frenquency"));
+    pkg1 = aml_package(0x1);
+    aml_append(pkg1, pkg0);
+    pkg2 = aml_package(0x2);
+    aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
+    aml_append(pkg2, pkg1);
+    aml_append(dev, aml_name_decl("_DSD", pkg2));
+    aml_append(scope, dev);
+    aml_append(table, scope);
+}
+
+static void
+build_la_ged_aml(Aml *dsdt, MachineState *machine)
+{
+    uint32_t event;
+    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
+
+    build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
+                  HOTPLUG_HANDLER(lvms->acpi_ged),
+                  VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
+                  VIRT_GED_EVT_ADDR);
+    event = object_property_get_uint(OBJECT(lvms->acpi_ged),
+                                     "ged-event", &error_abort);
+    if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
+        build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL,
+                                 AML_SYSTEM_MEMORY,
+                                 VIRT_GED_MEM_ADDR);
+    }
+    acpi_dsdt_add_power_button(dsdt);
+}
+
+static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms)
+{
+    struct GPEXConfig cfg = {
+        .mmio64.base = VIRT_PCI_MEM_BASE,
+        .mmio64.size = VIRT_PCI_MEM_SIZE,
+        .pio.base    = VIRT_PCI_IO_BASE,
+        .pio.size    = VIRT_PCI_IO_SIZE,
+        .ecam.base   = VIRT_PCI_CFG_BASE,
+        .ecam.size   = VIRT_PCI_CFG_SIZE,
+        .irq         = VIRT_GSI_BASE + VIRT_DEVICE_IRQS,
+        .bus         = lvms->pci_bus,
+    };
+
+    acpi_dsdt_add_gpex(scope, &cfg);
+}
+
+static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms)
+{
+    Aml *dev, *crs;
+    MemoryRegion *flash_mem;
+
+    hwaddr flash0_base;
+    hwaddr flash0_size;
+
+    hwaddr flash1_base;
+    hwaddr flash1_size;
+
+    flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
+    flash0_base = flash_mem->addr;
+    flash0_size = memory_region_size(flash_mem);
+
+    flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
+    flash1_base = flash_mem->addr;
+    flash1_size = memory_region_size(flash_mem);
+
+    dev = aml_device("FLS0");
+    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+    crs = aml_resource_template();
+    aml_append(crs, aml_memory32_fixed(flash0_base, flash0_size,
+                                       AML_READ_WRITE));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+
+    dev = aml_device("FLS1");
+    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+
+    crs = aml_resource_template();
+    aml_append(crs, aml_memory32_fixed(flash1_base, flash1_size,
+                                       AML_READ_WRITE));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+}
+
+#ifdef CONFIG_TPM
+static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms)
+{
+    PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
+    hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS;
+    SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find());
+    MemoryRegion *sbdev_mr;
+    hwaddr tpm_base;
+
+    if (!sbdev) {
+        return;
+    }
+
+    tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+    assert(tpm_base != -1);
+
+    tpm_base += pbus_base;
+
+    sbdev_mr = sysbus_mmio_get_region(sbdev, 0);
+
+    Aml *dev = aml_device("TPM0");
+    aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
+    aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+    Aml *crs = aml_resource_template();
+    aml_append(crs,
+               aml_memory32_fixed(tpm_base,
+                                  (uint32_t)memory_region_size(sbdev_mr),
+                                  AML_READ_WRITE));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+}
+#endif
+
+/* build DSDT */
+static void
+build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
+{
+    int i;
+    Aml *dsdt, *scope, *pkg;
+    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
+    AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id,
+                        .oem_table_id = lvms->oem_table_id };
+
+    acpi_table_begin(&table, table_data);
+    dsdt = init_aml_allocator();
+    for (i = 0; i < VIRT_UART_COUNT; i++) {
+        build_uart_device_aml(dsdt, i);
+    }
+    build_pci_device_aml(dsdt, lvms);
+    build_la_ged_aml(dsdt, machine);
+    build_flash_aml(dsdt, lvms);
+#ifdef CONFIG_TPM
+    acpi_dsdt_add_tpm(dsdt, lvms);
+#endif
+    /* System State Package */
+    scope = aml_scope("\\");
+    pkg = aml_package(4);
+    aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5));
+    aml_append(pkg, aml_int(0)); /* ignored */
+    aml_append(pkg, aml_int(0)); /* reserved */
+    aml_append(pkg, aml_int(0)); /* reserved */
+    aml_append(scope, aml_name_decl("_S5", pkg));
+    aml_append(dsdt, scope);
+    /* Copy AML table into ACPI tables blob and patch header there */
+    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
+    acpi_table_end(linker, &table);
+    free_aml_allocator();
+}
+
+static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
+{
+    LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
+    GArray *table_offsets;
+    AcpiFadtData fadt_data;
+    unsigned facs, rsdt, dsdt;
+    uint8_t *u;
+    GArray *tables_blob = tables->table_data;
+
+    init_common_fadt_data(&fadt_data);
+
+    table_offsets = g_array_new(false, true, sizeof(uint32_t));
+    ACPI_BUILD_DPRINTF("init ACPI tables\n");
+
+    bios_linker_loader_alloc(tables->linker,
+                             ACPI_BUILD_TABLE_FILE, tables_blob,
+                             64, false);
+
+    /*
+     * FACS is pointed to by FADT.
+     * We place it first since it's the only table that has alignment
+     * requirements.
+     */
+    facs = tables_blob->len;
+    build_facs(tables_blob);
+
+    /* DSDT is pointed to by FADT */
+    dsdt = tables_blob->len;
+    build_dsdt(tables_blob, tables->linker, machine);
+
+    /* ACPI tables pointed to by RSDT */
+    acpi_add_table(table_offsets, tables_blob);
+    fadt_data.facs_tbl_offset = &facs;
+    fadt_data.dsdt_tbl_offset = &dsdt;
+    fadt_data.xdsdt_tbl_offset = &dsdt;
+    build_fadt(tables_blob, tables->linker, &fadt_data,
+               lvms->oem_id, lvms->oem_table_id);
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_madt(tables_blob, tables->linker, lvms);
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_pptt(tables_blob, tables->linker, machine,
+               lvms->oem_id, lvms->oem_table_id);
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_srat(tables_blob, tables->linker, machine);
+    acpi_add_table(table_offsets, tables_blob);
+    spcr_setup(tables_blob, tables->linker, machine);
+
+    if (machine->numa_state->num_nodes) {
+        if (machine->numa_state->have_numa_distance) {
+            acpi_add_table(table_offsets, tables_blob);
+            build_slit(tables_blob, tables->linker, machine, lvms->oem_id,
+                       lvms->oem_table_id);
+        }
+        if (machine->numa_state->hmat_enabled) {
+            acpi_add_table(table_offsets, tables_blob);
+            build_hmat(tables_blob, tables->linker, machine->numa_state,
+                       lvms->oem_id, lvms->oem_table_id);
+        }
+    }
+
+    acpi_add_table(table_offsets, tables_blob);
+    {
+        AcpiMcfgInfo mcfg = {
+           .base = cpu_to_le64(VIRT_PCI_CFG_BASE),
+           .size = cpu_to_le64(VIRT_PCI_CFG_SIZE),
+        };
+        build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id,
+                   lvms->oem_table_id);
+    }
+
+#ifdef CONFIG_TPM
+    /* TPM info */
+    if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) {
+        acpi_add_table(table_offsets, tables_blob);
+        build_tpm2(tables_blob, tables->linker,
+                   tables->tcpalog, lvms->oem_id,
+                   lvms->oem_table_id);
+    }
+#endif
+    /* Add tables supplied by user (if any) */
+    for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
+        unsigned len = acpi_table_len(u);
+
+        acpi_add_table(table_offsets, tables_blob);
+        g_array_append_vals(tables_blob, u, len);
+    }
+
+    /* RSDT is pointed to by RSDP */
+    rsdt = tables_blob->len;
+    build_rsdt(tables_blob, tables->linker, table_offsets,
+               lvms->oem_id, lvms->oem_table_id);
+
+    /* RSDP is in FSEG memory, so allocate it separately */
+    {
+        AcpiRsdpData rsdp_data = {
+            .revision = 0,
+            .oem_id = lvms->oem_id,
+            .xsdt_tbl_offset = NULL,
+            .rsdt_tbl_offset = &rsdt,
+        };
+        build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
+    }
+
+    /*
+     * The align size is 128, warn if 64k is not enough therefore
+     * the align size could be resized.
+     */
+    if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
+        warn_report("ACPI table size %u exceeds %d bytes,"
+                    " migration may not work",
+                    tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
+        error_printf("Try removing CPUs, NUMA nodes, memory slots"
+                     " or PCI bridges.\n");
+    }
+
+    acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE);
+
+    /* Cleanup memory that's no longer used. */
+    g_array_free(table_offsets, true);
+}
+
+static void acpi_ram_update(MemoryRegion *mr, GArray *data)
+{
+    uint32_t size = acpi_data_len(data);
+
+    /*
+     * Make sure RAM size is correct - in case it got changed
+     * e.g. by migration
+     */
+    memory_region_ram_resize(mr, size, &error_abort);
+
+    memcpy(memory_region_get_ram_ptr(mr), data->data, size);
+    memory_region_set_dirty(mr, 0, size);
+}
+
+static void acpi_build_update(void *build_opaque)
+{
+    AcpiBuildState *build_state = build_opaque;
+    AcpiBuildTables tables;
+
+    /* No state to update or already patched? Nothing to do. */
+    if (!build_state || build_state->patched) {
+        return;
+    }
+    build_state->patched = 1;
+
+    acpi_build_tables_init(&tables);
+
+    acpi_build(&tables, MACHINE(qdev_get_machine()));
+
+    acpi_ram_update(build_state->table_mr, tables.table_data);
+    acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
+    acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
+
+    acpi_build_tables_cleanup(&tables, true);
+}
+
+static void acpi_build_reset(void *build_opaque)
+{
+    AcpiBuildState *build_state = build_opaque;
+    build_state->patched = 0;
+}
+
+static const VMStateDescription vmstate_acpi_build = {
+    .name = "acpi_build",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT8(patched, AcpiBuildState),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
+{
+    if (lvms->acpi == ON_OFF_AUTO_OFF) {
+        return false;
+    }
+    return true;
+}
+
+void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
+{
+    AcpiBuildTables tables;
+    AcpiBuildState *build_state;
+
+    if (!lvms->fw_cfg) {
+        ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
+        return;
+    }
+
+    if (!loongarch_is_acpi_enabled(lvms)) {
+        ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
+        return;
+    }
+
+    build_state = g_malloc0(sizeof *build_state);
+
+    acpi_build_tables_init(&tables);
+    acpi_build(&tables, MACHINE(lvms));
+
+    /* Now expose it all to Guest */
+    build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
+                                              build_state, tables.table_data,
+                                              ACPI_BUILD_TABLE_FILE);
+    assert(build_state->table_mr != NULL);
+
+    build_state->linker_mr =
+        acpi_add_rom_blob(acpi_build_update, build_state,
+                          tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE);
+
+    build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update,
+                                             build_state, tables.rsdp,
+                                             ACPI_BUILD_RSDP_FILE);
+
+    fw_cfg_add_file(lvms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data,
+                    acpi_data_len(tables.tcpalog));
+
+    qemu_register_reset(acpi_build_reset, build_state);
+    acpi_build_reset(build_state);
+    vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
+
+    /*
+     * Cleanup tables but don't free the memory: we track it
+     * in build_state.
+     */
+    acpi_build_tables_cleanup(&tables, false);
+}