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