mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-10 02:54:58 -06:00

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