xref: /openbmc/qemu/hw/loongarch/acpi-build.c (revision ff5b5739)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Support for generating ACPI tables and passing them to Guests
4  *
5  * Copyright (C) 2021 Loongson Technology Corporation Limited
6  */
7 
8 #include "qemu/osdep.h"
9 #include "qapi/error.h"
10 #include "qemu/error-report.h"
11 #include "qemu/bitmap.h"
12 #include "hw/pci/pci.h"
13 #include "hw/core/cpu.h"
14 #include "target/loongarch/cpu.h"
15 #include "hw/acpi/acpi-defs.h"
16 #include "hw/acpi/acpi.h"
17 #include "hw/nvram/fw_cfg.h"
18 #include "hw/acpi/bios-linker-loader.h"
19 #include "migration/vmstate.h"
20 #include "hw/mem/memory-device.h"
21 #include "sysemu/reset.h"
22 
23 /* Supported chipsets: */
24 #include "hw/pci-host/ls7a.h"
25 #include "hw/loongarch/virt.h"
26 
27 #include "hw/acpi/utils.h"
28 #include "hw/acpi/pci.h"
29 
30 #include "qom/qom-qobject.h"
31 
32 #include "hw/acpi/generic_event_device.h"
33 #include "hw/pci-host/gpex.h"
34 #include "sysemu/tpm.h"
35 #include "hw/platform-bus.h"
36 #include "hw/acpi/aml-build.h"
37 #include "hw/acpi/hmat.h"
38 
39 #define ACPI_BUILD_ALIGN_SIZE             0x1000
40 #define ACPI_BUILD_TABLE_SIZE             0x20000
41 
42 #ifdef DEBUG_ACPI_BUILD
43 #define ACPI_BUILD_DPRINTF(fmt, ...)        \
44     do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
45 #else
46 #define ACPI_BUILD_DPRINTF(fmt, ...)
47 #endif
48 
49 /* build FADT */
50 static void init_common_fadt_data(AcpiFadtData *data)
51 {
52     AcpiFadtData fadt = {
53         /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */
54         .rev = 5,
55         .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) |
56                   (1 << ACPI_FADT_F_RESET_REG_SUP)),
57 
58         /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */
59         .sleep_ctl = {
60             .space_id = AML_AS_SYSTEM_MEMORY,
61             .bit_width = 8,
62             .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL,
63         },
64         .sleep_sts = {
65             .space_id = AML_AS_SYSTEM_MEMORY,
66             .bit_width = 8,
67             .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS,
68         },
69 
70         /* ACPI 5.0: 4.8.3.6 Reset Register */
71         .reset_reg = {
72             .space_id = AML_AS_SYSTEM_MEMORY,
73             .bit_width = 8,
74             .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET,
75         },
76         .reset_val = ACPI_GED_RESET_VALUE,
77     };
78     *data = fadt;
79 }
80 
81 static void acpi_align_size(GArray *blob, unsigned align)
82 {
83     /*
84      * Align size to multiple of given size. This reduces the chance
85      * we need to change size in the future (breaking cross version migration).
86      */
87     g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
88 }
89 
90 /* build FACS */
91 static void
92 build_facs(GArray *table_data)
93 {
94     const char *sig = "FACS";
95     const uint8_t reserved[40] = {};
96 
97     g_array_append_vals(table_data, sig, 4); /* Signature */
98     build_append_int_noprefix(table_data, 64, 4); /* Length */
99     build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */
100     build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */
101     build_append_int_noprefix(table_data, 0, 4); /* Global Lock */
102     build_append_int_noprefix(table_data, 0, 4); /* Flags */
103     g_array_append_vals(table_data, reserved, 40); /* Reserved */
104 }
105 
106 /* build MADT */
107 static void
108 build_madt(GArray *table_data, BIOSLinker *linker,
109            LoongArchVirtMachineState *lvms)
110 {
111     MachineState *ms = MACHINE(lvms);
112     MachineClass *mc = MACHINE_GET_CLASS(ms);
113     const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
114     int i, arch_id;
115     AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id,
116                         .oem_table_id = lvms->oem_table_id };
117 
118     acpi_table_begin(&table, table_data);
119 
120     /* Local APIC Address */
121     build_append_int_noprefix(table_data, 0, 4);
122     build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
123 
124     for (i = 0; i < arch_ids->len; i++) {
125         /* Processor Core Interrupt Controller Structure */
126         arch_id = arch_ids->cpus[i].arch_id;
127 
128         build_append_int_noprefix(table_data, 17, 1);    /* Type */
129         build_append_int_noprefix(table_data, 15, 1);    /* Length */
130         build_append_int_noprefix(table_data, 1, 1);     /* Version */
131         build_append_int_noprefix(table_data, i, 4);     /* ACPI Processor ID */
132         build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */
133         build_append_int_noprefix(table_data, 1, 4);     /* Flags */
134     }
135 
136     /* Extend I/O Interrupt Controller Structure */
137     build_append_int_noprefix(table_data, 20, 1);        /* Type */
138     build_append_int_noprefix(table_data, 13, 1);        /* Length */
139     build_append_int_noprefix(table_data, 1, 1);         /* Version */
140     build_append_int_noprefix(table_data, 3, 1);         /* Cascade */
141     build_append_int_noprefix(table_data, 0, 1);         /* Node */
142     build_append_int_noprefix(table_data, 0xffff, 8);    /* Node map */
143 
144     /* MSI Interrupt Controller Structure */
145     build_append_int_noprefix(table_data, 21, 1);        /* Type */
146     build_append_int_noprefix(table_data, 19, 1);        /* Length */
147     build_append_int_noprefix(table_data, 1, 1);         /* Version */
148     build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */
149     build_append_int_noprefix(table_data, 0x40, 4);      /* Start */
150     build_append_int_noprefix(table_data, 0xc0, 4);      /* Count */
151 
152     /* Bridge I/O Interrupt Controller Structure */
153     build_append_int_noprefix(table_data, 22, 1);        /* Type */
154     build_append_int_noprefix(table_data, 17, 1);        /* Length */
155     build_append_int_noprefix(table_data, 1, 1);         /* Version */
156     build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */
157     build_append_int_noprefix(table_data, 0x1000, 2);    /* Size */
158     build_append_int_noprefix(table_data, 0, 2);         /* Id */
159     build_append_int_noprefix(table_data, 0x40, 2);      /* Base */
160 
161     acpi_table_end(linker, &table);
162 }
163 
164 /* build SRAT */
165 static void
166 build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
167 {
168     int i, arch_id, node_id;
169     uint64_t mem_len, mem_base;
170     int nb_numa_nodes = machine->numa_state->num_nodes;
171     LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
172     MachineClass *mc = MACHINE_GET_CLASS(lvms);
173     const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine);
174     AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id,
175                         .oem_table_id = lvms->oem_table_id };
176 
177     acpi_table_begin(&table, table_data);
178     build_append_int_noprefix(table_data, 1, 4); /* Reserved */
179     build_append_int_noprefix(table_data, 0, 8); /* Reserved */
180 
181     for (i = 0; i < arch_ids->len; ++i) {
182         arch_id = arch_ids->cpus[i].arch_id;
183         node_id = arch_ids->cpus[i].props.node_id;
184 
185         /* Processor Local APIC/SAPIC Affinity Structure */
186         build_append_int_noprefix(table_data, 0, 1);  /* Type  */
187         build_append_int_noprefix(table_data, 16, 1); /* Length */
188         /* Proximity Domain [7:0] */
189         build_append_int_noprefix(table_data, node_id, 1);
190         build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */
191         /* Flags, Table 5-36 */
192         build_append_int_noprefix(table_data, 1, 4);
193         build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */
194         /* Proximity Domain [31:8] */
195         build_append_int_noprefix(table_data, 0, 3);
196         build_append_int_noprefix(table_data, 0, 4); /* Reserved */
197     }
198 
199     /* Node0 */
200     build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE,
201                       0, MEM_AFFINITY_ENABLED);
202     mem_base = VIRT_HIGHMEM_BASE;
203     if (!nb_numa_nodes) {
204         mem_len = machine->ram_size - VIRT_LOWMEM_SIZE;
205     } else {
206         mem_len = machine->numa_state->nodes[0].node_mem - VIRT_LOWMEM_SIZE;
207     }
208     if (mem_len)
209         build_srat_memory(table_data, mem_base, mem_len, 0, MEM_AFFINITY_ENABLED);
210 
211     /* Node1 - Nodemax */
212     if (nb_numa_nodes) {
213         mem_base += mem_len;
214         for (i = 1; i < nb_numa_nodes; ++i) {
215             if (machine->numa_state->nodes[i].node_mem > 0) {
216                 build_srat_memory(table_data, mem_base,
217                                   machine->numa_state->nodes[i].node_mem, i,
218                                   MEM_AFFINITY_ENABLED);
219                 mem_base += machine->numa_state->nodes[i].node_mem;
220             }
221         }
222     }
223 
224     if (machine->device_memory) {
225         build_srat_memory(table_data, machine->device_memory->base,
226                           memory_region_size(&machine->device_memory->mr),
227                           nb_numa_nodes - 1,
228                           MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
229     }
230 
231     acpi_table_end(linker, &table);
232 }
233 
234 typedef
235 struct AcpiBuildState {
236     /* Copy of table in RAM (for patching). */
237     MemoryRegion *table_mr;
238     /* Is table patched? */
239     uint8_t patched;
240     void *rsdp;
241     MemoryRegion *rsdp_mr;
242     MemoryRegion *linker_mr;
243 } AcpiBuildState;
244 
245 static void build_uart_device_aml(Aml *table)
246 {
247     Aml *dev;
248     Aml *crs;
249     Aml *pkg0, *pkg1, *pkg2;
250     uint32_t uart_irq = VIRT_UART_IRQ;
251 
252     Aml *scope = aml_scope("_SB");
253     dev = aml_device("COMA");
254     aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501")));
255     aml_append(dev, aml_name_decl("_UID", aml_int(0)));
256     aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
257     crs = aml_resource_template();
258     aml_append(crs,
259         aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
260                          AML_NON_CACHEABLE, AML_READ_WRITE,
261                          0, VIRT_UART_BASE, VIRT_UART_BASE + VIRT_UART_SIZE - 1,
262                          0, VIRT_UART_SIZE));
263     aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
264                                   AML_SHARED, &uart_irq, 1));
265     aml_append(dev, aml_name_decl("_CRS", crs));
266     pkg0 = aml_package(0x2);
267     aml_append(pkg0, aml_int(0x05F5E100));
268     aml_append(pkg0, aml_string("clock-frenquency"));
269     pkg1 = aml_package(0x1);
270     aml_append(pkg1, pkg0);
271     pkg2 = aml_package(0x2);
272     aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
273     aml_append(pkg2, pkg1);
274     aml_append(dev, aml_name_decl("_DSD", pkg2));
275     aml_append(scope, dev);
276     aml_append(table, scope);
277 }
278 
279 static void
280 build_la_ged_aml(Aml *dsdt, MachineState *machine)
281 {
282     uint32_t event;
283     LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
284 
285     build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
286                   HOTPLUG_HANDLER(lvms->acpi_ged),
287                   VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
288                   VIRT_GED_EVT_ADDR);
289     event = object_property_get_uint(OBJECT(lvms->acpi_ged),
290                                      "ged-event", &error_abort);
291     if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
292         build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL,
293                                  AML_SYSTEM_MEMORY,
294                                  VIRT_GED_MEM_ADDR);
295     }
296     acpi_dsdt_add_power_button(dsdt);
297 }
298 
299 static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms)
300 {
301     struct GPEXConfig cfg = {
302         .mmio64.base = VIRT_PCI_MEM_BASE,
303         .mmio64.size = VIRT_PCI_MEM_SIZE,
304         .pio.base    = VIRT_PCI_IO_BASE,
305         .pio.size    = VIRT_PCI_IO_SIZE,
306         .ecam.base   = VIRT_PCI_CFG_BASE,
307         .ecam.size   = VIRT_PCI_CFG_SIZE,
308         .irq         = VIRT_GSI_BASE + VIRT_DEVICE_IRQS,
309         .bus         = lvms->pci_bus,
310     };
311 
312     acpi_dsdt_add_gpex(scope, &cfg);
313 }
314 
315 static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms)
316 {
317     Aml *dev, *crs;
318     MemoryRegion *flash_mem;
319 
320     hwaddr flash0_base;
321     hwaddr flash0_size;
322 
323     hwaddr flash1_base;
324     hwaddr flash1_size;
325 
326     flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
327     flash0_base = flash_mem->addr;
328     flash0_size = memory_region_size(flash_mem);
329 
330     flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
331     flash1_base = flash_mem->addr;
332     flash1_size = memory_region_size(flash_mem);
333 
334     dev = aml_device("FLS0");
335     aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
336     aml_append(dev, aml_name_decl("_UID", aml_int(0)));
337 
338     crs = aml_resource_template();
339     aml_append(crs, aml_memory32_fixed(flash0_base, flash0_size,
340                                        AML_READ_WRITE));
341     aml_append(dev, aml_name_decl("_CRS", crs));
342     aml_append(scope, dev);
343 
344     dev = aml_device("FLS1");
345     aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
346     aml_append(dev, aml_name_decl("_UID", aml_int(1)));
347 
348     crs = aml_resource_template();
349     aml_append(crs, aml_memory32_fixed(flash1_base, flash1_size,
350                                        AML_READ_WRITE));
351     aml_append(dev, aml_name_decl("_CRS", crs));
352     aml_append(scope, dev);
353 }
354 
355 #ifdef CONFIG_TPM
356 static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms)
357 {
358     PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
359     hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS;
360     SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find());
361     MemoryRegion *sbdev_mr;
362     hwaddr tpm_base;
363 
364     if (!sbdev) {
365         return;
366     }
367 
368     tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
369     assert(tpm_base != -1);
370 
371     tpm_base += pbus_base;
372 
373     sbdev_mr = sysbus_mmio_get_region(sbdev, 0);
374 
375     Aml *dev = aml_device("TPM0");
376     aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
377     aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device")));
378     aml_append(dev, aml_name_decl("_UID", aml_int(0)));
379 
380     Aml *crs = aml_resource_template();
381     aml_append(crs,
382                aml_memory32_fixed(tpm_base,
383                                   (uint32_t)memory_region_size(sbdev_mr),
384                                   AML_READ_WRITE));
385     aml_append(dev, aml_name_decl("_CRS", crs));
386     aml_append(scope, dev);
387 }
388 #endif
389 
390 /* build DSDT */
391 static void
392 build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
393 {
394     Aml *dsdt, *scope, *pkg;
395     LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
396     AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id,
397                         .oem_table_id = lvms->oem_table_id };
398 
399     acpi_table_begin(&table, table_data);
400     dsdt = init_aml_allocator();
401     build_uart_device_aml(dsdt);
402     build_pci_device_aml(dsdt, lvms);
403     build_la_ged_aml(dsdt, machine);
404     build_flash_aml(dsdt, lvms);
405 #ifdef CONFIG_TPM
406     acpi_dsdt_add_tpm(dsdt, lvms);
407 #endif
408     /* System State Package */
409     scope = aml_scope("\\");
410     pkg = aml_package(4);
411     aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5));
412     aml_append(pkg, aml_int(0)); /* ignored */
413     aml_append(pkg, aml_int(0)); /* reserved */
414     aml_append(pkg, aml_int(0)); /* reserved */
415     aml_append(scope, aml_name_decl("_S5", pkg));
416     aml_append(dsdt, scope);
417     /* Copy AML table into ACPI tables blob and patch header there */
418     g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
419     acpi_table_end(linker, &table);
420     free_aml_allocator();
421 }
422 
423 static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
424 {
425     LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
426     GArray *table_offsets;
427     AcpiFadtData fadt_data;
428     unsigned facs, rsdt, dsdt;
429     uint8_t *u;
430     GArray *tables_blob = tables->table_data;
431 
432     init_common_fadt_data(&fadt_data);
433 
434     table_offsets = g_array_new(false, true, sizeof(uint32_t));
435     ACPI_BUILD_DPRINTF("init ACPI tables\n");
436 
437     bios_linker_loader_alloc(tables->linker,
438                              ACPI_BUILD_TABLE_FILE, tables_blob,
439                              64, false);
440 
441     /*
442      * FACS is pointed to by FADT.
443      * We place it first since it's the only table that has alignment
444      * requirements.
445      */
446     facs = tables_blob->len;
447     build_facs(tables_blob);
448 
449     /* DSDT is pointed to by FADT */
450     dsdt = tables_blob->len;
451     build_dsdt(tables_blob, tables->linker, machine);
452 
453     /* ACPI tables pointed to by RSDT */
454     acpi_add_table(table_offsets, tables_blob);
455     fadt_data.facs_tbl_offset = &facs;
456     fadt_data.dsdt_tbl_offset = &dsdt;
457     fadt_data.xdsdt_tbl_offset = &dsdt;
458     build_fadt(tables_blob, tables->linker, &fadt_data,
459                lvms->oem_id, lvms->oem_table_id);
460 
461     acpi_add_table(table_offsets, tables_blob);
462     build_madt(tables_blob, tables->linker, lvms);
463 
464     acpi_add_table(table_offsets, tables_blob);
465     build_pptt(tables_blob, tables->linker, machine,
466                lvms->oem_id, lvms->oem_table_id);
467 
468     acpi_add_table(table_offsets, tables_blob);
469     build_srat(tables_blob, tables->linker, machine);
470 
471     if (machine->numa_state->num_nodes) {
472         if (machine->numa_state->have_numa_distance) {
473             acpi_add_table(table_offsets, tables_blob);
474             build_slit(tables_blob, tables->linker, machine, lvms->oem_id,
475                        lvms->oem_table_id);
476         }
477         if (machine->numa_state->hmat_enabled) {
478             acpi_add_table(table_offsets, tables_blob);
479             build_hmat(tables_blob, tables->linker, machine->numa_state,
480                        lvms->oem_id, lvms->oem_table_id);
481         }
482     }
483 
484     acpi_add_table(table_offsets, tables_blob);
485     {
486         AcpiMcfgInfo mcfg = {
487            .base = cpu_to_le64(VIRT_PCI_CFG_BASE),
488            .size = cpu_to_le64(VIRT_PCI_CFG_SIZE),
489         };
490         build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id,
491                    lvms->oem_table_id);
492     }
493 
494 #ifdef CONFIG_TPM
495     /* TPM info */
496     if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) {
497         acpi_add_table(table_offsets, tables_blob);
498         build_tpm2(tables_blob, tables->linker,
499                    tables->tcpalog, lvms->oem_id,
500                    lvms->oem_table_id);
501     }
502 #endif
503     /* Add tables supplied by user (if any) */
504     for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
505         unsigned len = acpi_table_len(u);
506 
507         acpi_add_table(table_offsets, tables_blob);
508         g_array_append_vals(tables_blob, u, len);
509     }
510 
511     /* RSDT is pointed to by RSDP */
512     rsdt = tables_blob->len;
513     build_rsdt(tables_blob, tables->linker, table_offsets,
514                lvms->oem_id, lvms->oem_table_id);
515 
516     /* RSDP is in FSEG memory, so allocate it separately */
517     {
518         AcpiRsdpData rsdp_data = {
519             .revision = 0,
520             .oem_id = lvms->oem_id,
521             .xsdt_tbl_offset = NULL,
522             .rsdt_tbl_offset = &rsdt,
523         };
524         build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
525     }
526 
527     /*
528      * The align size is 128, warn if 64k is not enough therefore
529      * the align size could be resized.
530      */
531     if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
532         warn_report("ACPI table size %u exceeds %d bytes,"
533                     " migration may not work",
534                     tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
535         error_printf("Try removing CPUs, NUMA nodes, memory slots"
536                      " or PCI bridges.\n");
537     }
538 
539     acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE);
540 
541     /* Cleanup memory that's no longer used. */
542     g_array_free(table_offsets, true);
543 }
544 
545 static void acpi_ram_update(MemoryRegion *mr, GArray *data)
546 {
547     uint32_t size = acpi_data_len(data);
548 
549     /*
550      * Make sure RAM size is correct - in case it got changed
551      * e.g. by migration
552      */
553     memory_region_ram_resize(mr, size, &error_abort);
554 
555     memcpy(memory_region_get_ram_ptr(mr), data->data, size);
556     memory_region_set_dirty(mr, 0, size);
557 }
558 
559 static void acpi_build_update(void *build_opaque)
560 {
561     AcpiBuildState *build_state = build_opaque;
562     AcpiBuildTables tables;
563 
564     /* No state to update or already patched? Nothing to do. */
565     if (!build_state || build_state->patched) {
566         return;
567     }
568     build_state->patched = 1;
569 
570     acpi_build_tables_init(&tables);
571 
572     acpi_build(&tables, MACHINE(qdev_get_machine()));
573 
574     acpi_ram_update(build_state->table_mr, tables.table_data);
575     acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
576     acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
577 
578     acpi_build_tables_cleanup(&tables, true);
579 }
580 
581 static void acpi_build_reset(void *build_opaque)
582 {
583     AcpiBuildState *build_state = build_opaque;
584     build_state->patched = 0;
585 }
586 
587 static const VMStateDescription vmstate_acpi_build = {
588     .name = "acpi_build",
589     .version_id = 1,
590     .minimum_version_id = 1,
591     .fields = (const VMStateField[]) {
592         VMSTATE_UINT8(patched, AcpiBuildState),
593         VMSTATE_END_OF_LIST()
594     },
595 };
596 
597 static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
598 {
599     if (lvms->acpi == ON_OFF_AUTO_OFF) {
600         return false;
601     }
602     return true;
603 }
604 
605 void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
606 {
607     AcpiBuildTables tables;
608     AcpiBuildState *build_state;
609 
610     if (!lvms->fw_cfg) {
611         ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
612         return;
613     }
614 
615     if (!loongarch_is_acpi_enabled(lvms)) {
616         ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
617         return;
618     }
619 
620     build_state = g_malloc0(sizeof *build_state);
621 
622     acpi_build_tables_init(&tables);
623     acpi_build(&tables, MACHINE(lvms));
624 
625     /* Now expose it all to Guest */
626     build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
627                                               build_state, tables.table_data,
628                                               ACPI_BUILD_TABLE_FILE);
629     assert(build_state->table_mr != NULL);
630 
631     build_state->linker_mr =
632         acpi_add_rom_blob(acpi_build_update, build_state,
633                           tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE);
634 
635     build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update,
636                                              build_state, tables.rsdp,
637                                              ACPI_BUILD_RSDP_FILE);
638 
639     qemu_register_reset(acpi_build_reset, build_state);
640     acpi_build_reset(build_state);
641     vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
642 
643     /*
644      * Cleanup tables but don't free the memory: we track it
645      * in build_state.
646      */
647     acpi_build_tables_cleanup(&tables, false);
648 }
649