1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * Support for generating ACPI tables and passing them to Guests
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
8 #include "qemu/osdep.h"
9 #include "qapi/error.h"
10 #include "qemu/bitmap.h"
11 #include "hw/pci/pci.h"
12 #include "hw/core/cpu.h"
13 #include "target/loongarch/cpu.h"
14 #include "hw/acpi/acpi-defs.h"
15 #include "hw/acpi/acpi.h"
16 #include "hw/nvram/fw_cfg.h"
17 #include "hw/acpi/bios-linker-loader.h"
18 #include "migration/vmstate.h"
19 #include "hw/mem/memory-device.h"
20 #include "sysemu/reset.h"
22 /* Supported chipsets: */
23 #include "hw/pci-host/ls7a.h"
24 #include "hw/loongarch/virt.h"
25 #include "hw/acpi/aml-build.h"
27 #include "hw/acpi/utils.h"
28 #include "hw/acpi/pci.h"
30 #include "qom/qom-qobject.h"
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"
38 #define ACPI_BUILD_ALIGN_SIZE 0x1000
39 #define ACPI_BUILD_TABLE_SIZE 0x20000
41 #ifdef DEBUG_ACPI_BUILD
42 #define ACPI_BUILD_DPRINTF(fmt, ...) \
43 do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
45 #define ACPI_BUILD_DPRINTF(fmt, ...)
49 static void init_common_fadt_data(AcpiFadtData
*data
)
52 /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */
54 .flags
= ((1 << ACPI_FADT_F_HW_REDUCED_ACPI
) |
55 (1 << ACPI_FADT_F_RESET_REG_SUP
)),
57 /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */
59 .space_id
= AML_AS_SYSTEM_MEMORY
,
61 .address
= VIRT_GED_REG_ADDR
+ ACPI_GED_REG_SLEEP_CTL
,
64 .space_id
= AML_AS_SYSTEM_MEMORY
,
66 .address
= VIRT_GED_REG_ADDR
+ ACPI_GED_REG_SLEEP_STS
,
69 /* ACPI 5.0: 4.8.3.6 Reset Register */
71 .space_id
= AML_AS_SYSTEM_MEMORY
,
73 .address
= VIRT_GED_REG_ADDR
+ ACPI_GED_REG_RESET
,
75 .reset_val
= ACPI_GED_RESET_VALUE
,
80 static void acpi_align_size(GArray
*blob
, unsigned align
)
83 * Align size to multiple of given size. This reduces the chance
84 * we need to change size in the future (breaking cross version migration).
86 g_array_set_size(blob
, ROUND_UP(acpi_data_len(blob
), align
));
91 build_facs(GArray
*table_data
)
93 const char *sig
= "FACS";
94 const uint8_t reserved
[40] = {};
96 g_array_append_vals(table_data
, sig
, 4); /* Signature */
97 build_append_int_noprefix(table_data
, 64, 4); /* Length */
98 build_append_int_noprefix(table_data
, 0, 4); /* Hardware Signature */
99 build_append_int_noprefix(table_data
, 0, 4); /* Firmware Waking Vector */
100 build_append_int_noprefix(table_data
, 0, 4); /* Global Lock */
101 build_append_int_noprefix(table_data
, 0, 4); /* Flags */
102 g_array_append_vals(table_data
, reserved
, 40); /* Reserved */
107 build_madt(GArray
*table_data
, BIOSLinker
*linker
, LoongArchMachineState
*lams
)
109 MachineState
*ms
= MACHINE(lams
);
111 AcpiTable table
= { .sig
= "APIC", .rev
= 1, .oem_id
= lams
->oem_id
,
112 .oem_table_id
= lams
->oem_table_id
};
114 acpi_table_begin(&table
, table_data
);
116 /* Local APIC Address */
117 build_append_int_noprefix(table_data
, 0, 4);
118 build_append_int_noprefix(table_data
, 1 /* PCAT_COMPAT */, 4); /* Flags */
120 for (i
= 0; i
< ms
->smp
.cpus
; i
++) {
121 /* Processor Core Interrupt Controller Structure */
122 build_append_int_noprefix(table_data
, 17, 1); /* Type */
123 build_append_int_noprefix(table_data
, 15, 1); /* Length */
124 build_append_int_noprefix(table_data
, 1, 1); /* Version */
125 build_append_int_noprefix(table_data
, i
+ 1, 4); /* ACPI Processor ID */
126 build_append_int_noprefix(table_data
, i
, 4); /* Core ID */
127 build_append_int_noprefix(table_data
, 1, 4); /* Flags */
130 /* Extend I/O Interrupt Controller Structure */
131 build_append_int_noprefix(table_data
, 20, 1); /* Type */
132 build_append_int_noprefix(table_data
, 13, 1); /* Length */
133 build_append_int_noprefix(table_data
, 1, 1); /* Version */
134 build_append_int_noprefix(table_data
, 3, 1); /* Cascade */
135 build_append_int_noprefix(table_data
, 0, 1); /* Node */
136 build_append_int_noprefix(table_data
, 0xffff, 8); /* Node map */
138 /* MSI Interrupt Controller Structure */
139 build_append_int_noprefix(table_data
, 21, 1); /* Type */
140 build_append_int_noprefix(table_data
, 19, 1); /* Length */
141 build_append_int_noprefix(table_data
, 1, 1); /* Version */
142 build_append_int_noprefix(table_data
, VIRT_PCH_MSI_ADDR_LOW
, 8);/* Address */
143 build_append_int_noprefix(table_data
, 0x40, 4); /* Start */
144 build_append_int_noprefix(table_data
, 0xc0, 4); /* Count */
146 /* Bridge I/O Interrupt Controller Structure */
147 build_append_int_noprefix(table_data
, 22, 1); /* Type */
148 build_append_int_noprefix(table_data
, 17, 1); /* Length */
149 build_append_int_noprefix(table_data
, 1, 1); /* Version */
150 build_append_int_noprefix(table_data
, VIRT_PCH_REG_BASE
, 8);/* Address */
151 build_append_int_noprefix(table_data
, 0x1000, 2); /* Size */
152 build_append_int_noprefix(table_data
, 0, 2); /* Id */
153 build_append_int_noprefix(table_data
, 0x40, 2); /* Base */
155 acpi_table_end(linker
, &table
);
160 build_srat(GArray
*table_data
, BIOSLinker
*linker
, MachineState
*machine
)
163 LoongArchMachineState
*lams
= LOONGARCH_MACHINE(machine
);
164 MachineState
*ms
= MACHINE(lams
);
165 AcpiTable table
= { .sig
= "SRAT", .rev
= 1, .oem_id
= lams
->oem_id
,
166 .oem_table_id
= lams
->oem_table_id
};
168 acpi_table_begin(&table
, table_data
);
169 build_append_int_noprefix(table_data
, 1, 4); /* Reserved */
170 build_append_int_noprefix(table_data
, 0, 8); /* Reserved */
172 for (i
= 0; i
< ms
->smp
.cpus
; ++i
) {
173 /* Processor Local APIC/SAPIC Affinity Structure */
174 build_append_int_noprefix(table_data
, 0, 1); /* Type */
175 build_append_int_noprefix(table_data
, 16, 1); /* Length */
176 /* Proximity Domain [7:0] */
177 build_append_int_noprefix(table_data
, 0, 1);
178 build_append_int_noprefix(table_data
, i
, 1); /* APIC ID */
179 /* Flags, Table 5-36 */
180 build_append_int_noprefix(table_data
, 1, 4);
181 build_append_int_noprefix(table_data
, 0, 1); /* Local SAPIC EID */
182 /* Proximity Domain [31:8] */
183 build_append_int_noprefix(table_data
, 0, 3);
184 build_append_int_noprefix(table_data
, 0, 4); /* Reserved */
187 build_srat_memory(table_data
, VIRT_LOWMEM_BASE
, VIRT_LOWMEM_SIZE
,
188 0, MEM_AFFINITY_ENABLED
);
190 build_srat_memory(table_data
, VIRT_HIGHMEM_BASE
, machine
->ram_size
- VIRT_LOWMEM_SIZE
,
191 0, MEM_AFFINITY_ENABLED
);
193 if (ms
->device_memory
) {
194 build_srat_memory(table_data
, ms
->device_memory
->base
,
195 memory_region_size(&ms
->device_memory
->mr
),
196 0, MEM_AFFINITY_HOTPLUGGABLE
| MEM_AFFINITY_ENABLED
);
199 acpi_table_end(linker
, &table
);
203 struct AcpiBuildState
{
204 /* Copy of table in RAM (for patching). */
205 MemoryRegion
*table_mr
;
206 /* Is table patched? */
209 MemoryRegion
*rsdp_mr
;
210 MemoryRegion
*linker_mr
;
213 static void build_uart_device_aml(Aml
*table
)
217 Aml
*pkg0
, *pkg1
, *pkg2
;
218 uint32_t uart_irq
= VIRT_UART_IRQ
;
220 Aml
*scope
= aml_scope("_SB");
221 dev
= aml_device("COMA");
222 aml_append(dev
, aml_name_decl("_HID", aml_string("PNP0501")));
223 aml_append(dev
, aml_name_decl("_UID", aml_int(0)));
224 aml_append(dev
, aml_name_decl("_CCA", aml_int(1)));
225 crs
= aml_resource_template();
227 aml_qword_memory(AML_POS_DECODE
, AML_MIN_FIXED
, AML_MAX_FIXED
,
228 AML_NON_CACHEABLE
, AML_READ_WRITE
,
229 0, VIRT_UART_BASE
, VIRT_UART_BASE
+ VIRT_UART_SIZE
- 1,
231 aml_append(crs
, aml_interrupt(AML_CONSUMER
, AML_LEVEL
, AML_ACTIVE_HIGH
,
232 AML_SHARED
, &uart_irq
, 1));
233 aml_append(dev
, aml_name_decl("_CRS", crs
));
234 pkg0
= aml_package(0x2);
235 aml_append(pkg0
, aml_int(0x05F5E100));
236 aml_append(pkg0
, aml_string("clock-frenquency"));
237 pkg1
= aml_package(0x1);
238 aml_append(pkg1
, pkg0
);
239 pkg2
= aml_package(0x2);
240 aml_append(pkg2
, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
241 aml_append(pkg2
, pkg1
);
242 aml_append(dev
, aml_name_decl("_DSD", pkg2
));
243 aml_append(scope
, dev
);
244 aml_append(table
, scope
);
248 build_la_ged_aml(Aml
*dsdt
, MachineState
*machine
)
251 LoongArchMachineState
*lams
= LOONGARCH_MACHINE(machine
);
253 build_ged_aml(dsdt
, "\\_SB."GED_DEVICE
,
254 HOTPLUG_HANDLER(lams
->acpi_ged
),
255 VIRT_SCI_IRQ
, AML_SYSTEM_MEMORY
,
257 event
= object_property_get_uint(OBJECT(lams
->acpi_ged
),
258 "ged-event", &error_abort
);
259 if (event
& ACPI_GED_MEM_HOTPLUG_EVT
) {
260 build_memory_hotplug_aml(dsdt
, machine
->ram_slots
, "\\_SB", NULL
,
266 static void build_pci_device_aml(Aml
*scope
, LoongArchMachineState
*lams
)
268 struct GPEXConfig cfg
= {
269 .mmio64
.base
= VIRT_PCI_MEM_BASE
,
270 .mmio64
.size
= VIRT_PCI_MEM_SIZE
,
271 .pio
.base
= VIRT_PCI_IO_BASE
,
272 .pio
.size
= VIRT_PCI_IO_SIZE
,
273 .ecam
.base
= VIRT_PCI_CFG_BASE
,
274 .ecam
.size
= VIRT_PCI_CFG_SIZE
,
275 .irq
= PCH_PIC_IRQ_OFFSET
+ VIRT_DEVICE_IRQS
,
276 .bus
= lams
->pci_bus
,
279 acpi_dsdt_add_gpex(scope
, &cfg
);
282 static void build_flash_aml(Aml
*scope
, LoongArchMachineState
*lams
)
286 hwaddr flash_base
= VIRT_FLASH_BASE
;
287 hwaddr flash_size
= VIRT_FLASH_SIZE
;
289 dev
= aml_device("FLS0");
290 aml_append(dev
, aml_name_decl("_HID", aml_string("LNRO0015")));
291 aml_append(dev
, aml_name_decl("_UID", aml_int(0)));
293 crs
= aml_resource_template();
294 aml_append(crs
, aml_memory32_fixed(flash_base
, flash_size
, AML_READ_WRITE
));
295 aml_append(dev
, aml_name_decl("_CRS", crs
));
296 aml_append(scope
, dev
);
300 static void acpi_dsdt_add_tpm(Aml
*scope
, LoongArchMachineState
*vms
)
302 PlatformBusDevice
*pbus
= PLATFORM_BUS_DEVICE(vms
->platform_bus_dev
);
303 hwaddr pbus_base
= VIRT_PLATFORM_BUS_BASEADDRESS
;
304 SysBusDevice
*sbdev
= SYS_BUS_DEVICE(tpm_find());
305 MemoryRegion
*sbdev_mr
;
312 tpm_base
= platform_bus_get_mmio_addr(pbus
, sbdev
, 0);
313 assert(tpm_base
!= -1);
315 tpm_base
+= pbus_base
;
317 sbdev_mr
= sysbus_mmio_get_region(sbdev
, 0);
319 Aml
*dev
= aml_device("TPM0");
320 aml_append(dev
, aml_name_decl("_HID", aml_string("MSFT0101")));
321 aml_append(dev
, aml_name_decl("_STR", aml_string("TPM 2.0 Device")));
322 aml_append(dev
, aml_name_decl("_UID", aml_int(0)));
324 Aml
*crs
= aml_resource_template();
326 aml_memory32_fixed(tpm_base
,
327 (uint32_t)memory_region_size(sbdev_mr
),
329 aml_append(dev
, aml_name_decl("_CRS", crs
));
330 aml_append(scope
, dev
);
336 build_dsdt(GArray
*table_data
, BIOSLinker
*linker
, MachineState
*machine
)
338 Aml
*dsdt
, *scope
, *pkg
;
339 LoongArchMachineState
*lams
= LOONGARCH_MACHINE(machine
);
340 AcpiTable table
= { .sig
= "DSDT", .rev
= 1, .oem_id
= lams
->oem_id
,
341 .oem_table_id
= lams
->oem_table_id
};
343 acpi_table_begin(&table
, table_data
);
344 dsdt
= init_aml_allocator();
345 build_uart_device_aml(dsdt
);
346 build_pci_device_aml(dsdt
, lams
);
347 build_la_ged_aml(dsdt
, machine
);
348 build_flash_aml(dsdt
, lams
);
350 acpi_dsdt_add_tpm(dsdt
, lams
);
352 /* System State Package */
353 scope
= aml_scope("\\");
354 pkg
= aml_package(4);
355 aml_append(pkg
, aml_int(ACPI_GED_SLP_TYP_S5
));
356 aml_append(pkg
, aml_int(0)); /* ignored */
357 aml_append(pkg
, aml_int(0)); /* reserved */
358 aml_append(pkg
, aml_int(0)); /* reserved */
359 aml_append(scope
, aml_name_decl("_S5", pkg
));
360 aml_append(dsdt
, scope
);
361 /* Copy AML table into ACPI tables blob and patch header there */
362 g_array_append_vals(table_data
, dsdt
->buf
->data
, dsdt
->buf
->len
);
363 acpi_table_end(linker
, &table
);
364 free_aml_allocator();
367 static void acpi_build(AcpiBuildTables
*tables
, MachineState
*machine
)
369 LoongArchMachineState
*lams
= LOONGARCH_MACHINE(machine
);
370 GArray
*table_offsets
;
371 AcpiFadtData fadt_data
;
372 unsigned facs
, rsdt
, dsdt
;
374 GArray
*tables_blob
= tables
->table_data
;
376 init_common_fadt_data(&fadt_data
);
378 table_offsets
= g_array_new(false, true, sizeof(uint32_t));
379 ACPI_BUILD_DPRINTF("init ACPI tables\n");
381 bios_linker_loader_alloc(tables
->linker
,
382 ACPI_BUILD_TABLE_FILE
, tables_blob
,
386 * FACS is pointed to by FADT.
387 * We place it first since it's the only table that has alignment
390 facs
= tables_blob
->len
;
391 build_facs(tables_blob
);
393 /* DSDT is pointed to by FADT */
394 dsdt
= tables_blob
->len
;
395 build_dsdt(tables_blob
, tables
->linker
, machine
);
397 /* ACPI tables pointed to by RSDT */
398 acpi_add_table(table_offsets
, tables_blob
);
399 fadt_data
.facs_tbl_offset
= &facs
;
400 fadt_data
.dsdt_tbl_offset
= &dsdt
;
401 fadt_data
.xdsdt_tbl_offset
= &dsdt
;
402 build_fadt(tables_blob
, tables
->linker
, &fadt_data
,
403 lams
->oem_id
, lams
->oem_table_id
);
405 acpi_add_table(table_offsets
, tables_blob
);
406 build_madt(tables_blob
, tables
->linker
, lams
);
408 acpi_add_table(table_offsets
, tables_blob
);
409 build_srat(tables_blob
, tables
->linker
, machine
);
411 acpi_add_table(table_offsets
, tables_blob
);
413 AcpiMcfgInfo mcfg
= {
414 .base
= cpu_to_le64(VIRT_PCI_CFG_BASE
),
415 .size
= cpu_to_le64(VIRT_PCI_CFG_SIZE
),
417 build_mcfg(tables_blob
, tables
->linker
, &mcfg
, lams
->oem_id
,
423 if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0
) {
424 acpi_add_table(table_offsets
, tables_blob
);
425 build_tpm2(tables_blob
, tables
->linker
,
426 tables
->tcpalog
, lams
->oem_id
,
430 /* Add tables supplied by user (if any) */
431 for (u
= acpi_table_first(); u
; u
= acpi_table_next(u
)) {
432 unsigned len
= acpi_table_len(u
);
434 acpi_add_table(table_offsets
, tables_blob
);
435 g_array_append_vals(tables_blob
, u
, len
);
438 /* RSDT is pointed to by RSDP */
439 rsdt
= tables_blob
->len
;
440 build_rsdt(tables_blob
, tables
->linker
, table_offsets
,
441 lams
->oem_id
, lams
->oem_table_id
);
443 /* RSDP is in FSEG memory, so allocate it separately */
445 AcpiRsdpData rsdp_data
= {
447 .oem_id
= lams
->oem_id
,
448 .xsdt_tbl_offset
= NULL
,
449 .rsdt_tbl_offset
= &rsdt
,
451 build_rsdp(tables
->rsdp
, tables
->linker
, &rsdp_data
);
455 * The align size is 128, warn if 64k is not enough therefore
456 * the align size could be resized.
458 if (tables_blob
->len
> ACPI_BUILD_TABLE_SIZE
/ 2) {
459 warn_report("ACPI table size %u exceeds %d bytes,"
460 " migration may not work",
461 tables_blob
->len
, ACPI_BUILD_TABLE_SIZE
/ 2);
462 error_printf("Try removing CPUs, NUMA nodes, memory slots"
466 acpi_align_size(tables
->linker
->cmd_blob
, ACPI_BUILD_ALIGN_SIZE
);
468 /* Cleanup memory that's no longer used. */
469 g_array_free(table_offsets
, true);
472 static void acpi_ram_update(MemoryRegion
*mr
, GArray
*data
)
474 uint32_t size
= acpi_data_len(data
);
477 * Make sure RAM size is correct - in case it got changed
480 memory_region_ram_resize(mr
, size
, &error_abort
);
482 memcpy(memory_region_get_ram_ptr(mr
), data
->data
, size
);
483 memory_region_set_dirty(mr
, 0, size
);
486 static void acpi_build_update(void *build_opaque
)
488 AcpiBuildState
*build_state
= build_opaque
;
489 AcpiBuildTables tables
;
491 /* No state to update or already patched? Nothing to do. */
492 if (!build_state
|| build_state
->patched
) {
495 build_state
->patched
= 1;
497 acpi_build_tables_init(&tables
);
499 acpi_build(&tables
, MACHINE(qdev_get_machine()));
501 acpi_ram_update(build_state
->table_mr
, tables
.table_data
);
502 acpi_ram_update(build_state
->rsdp_mr
, tables
.rsdp
);
503 acpi_ram_update(build_state
->linker_mr
, tables
.linker
->cmd_blob
);
505 acpi_build_tables_cleanup(&tables
, true);
508 static void acpi_build_reset(void *build_opaque
)
510 AcpiBuildState
*build_state
= build_opaque
;
511 build_state
->patched
= 0;
514 static const VMStateDescription vmstate_acpi_build
= {
515 .name
= "acpi_build",
517 .minimum_version_id
= 1,
518 .fields
= (VMStateField
[]) {
519 VMSTATE_UINT8(patched
, AcpiBuildState
),
520 VMSTATE_END_OF_LIST()
524 void loongarch_acpi_setup(LoongArchMachineState
*lams
)
526 AcpiBuildTables tables
;
527 AcpiBuildState
*build_state
;
530 ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
534 if (!loongarch_is_acpi_enabled(lams
)) {
535 ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
539 build_state
= g_malloc0(sizeof *build_state
);
541 acpi_build_tables_init(&tables
);
542 acpi_build(&tables
, MACHINE(lams
));
544 /* Now expose it all to Guest */
545 build_state
->table_mr
= acpi_add_rom_blob(acpi_build_update
,
546 build_state
, tables
.table_data
,
547 ACPI_BUILD_TABLE_FILE
);
548 assert(build_state
->table_mr
!= NULL
);
550 build_state
->linker_mr
=
551 acpi_add_rom_blob(acpi_build_update
, build_state
,
552 tables
.linker
->cmd_blob
, ACPI_BUILD_LOADER_FILE
);
554 build_state
->rsdp_mr
= acpi_add_rom_blob(acpi_build_update
,
555 build_state
, tables
.rsdp
,
556 ACPI_BUILD_RSDP_FILE
);
558 qemu_register_reset(acpi_build_reset
, build_state
);
559 acpi_build_reset(build_state
);
560 vmstate_register(NULL
, 0, &vmstate_acpi_build
, build_state
);
563 * Cleanup tables but don't free the memory: we track it
566 acpi_build_tables_cleanup(&tables
, false);