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