#include "hw/acpi/acpi-defs.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/aml-build.h"
+#include "hw/acpi/pci.h"
#include "hw/acpi/utils.h"
+#include "hw/intc/riscv_aclint.h"
+#include "hw/nvram/fw_cfg_acpi.h"
+#include "hw/pci-host/gpex.h"
+#include "hw/riscv/virt.h"
+#include "hw/riscv/numa.h"
+#include "hw/virtio/virtio-acpi.h"
+#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "sysemu/reset.h"
-#include "migration/vmstate.h"
-#include "hw/riscv/virt.h"
-#include "hw/riscv/numa.h"
-#include "hw/intc/riscv_aclint.h"
#define ACPI_BUILD_TABLE_SIZE 0x20000
+#define ACPI_BUILD_INTC_ID(socket, index) ((socket << 24) | (index))
typedef struct AcpiBuildState {
/* Copy of table in RAM (for patching) */
static void riscv_acpi_madt_add_rintc(uint32_t uid,
const CPUArchIdList *arch_ids,
- GArray *entry)
+ GArray *entry,
+ RISCVVirtState *s)
{
+ uint8_t guest_index_bits = imsic_num_bits(s->aia_guests + 1);
uint64_t hart_id = arch_ids->cpus[uid].arch_id;
+ uint32_t imsic_size, local_cpu_id, socket_id;
+ uint64_t imsic_socket_addr, imsic_addr;
+ MachineState *ms = MACHINE(s);
+ socket_id = arch_ids->cpus[uid].props.node_id;
+ local_cpu_id = (arch_ids->cpus[uid].arch_id -
+ riscv_socket_first_hartid(ms, socket_id)) %
+ riscv_socket_hart_count(ms, socket_id);
+ imsic_socket_addr = s->memmap[VIRT_IMSIC_S].base +
+ (socket_id * VIRT_IMSIC_GROUP_MAX_SIZE);
+ imsic_size = IMSIC_HART_SIZE(guest_index_bits);
+ imsic_addr = imsic_socket_addr + local_cpu_id * imsic_size;
build_append_int_noprefix(entry, 0x18, 1); /* Type */
- build_append_int_noprefix(entry, 20, 1); /* Length */
+ build_append_int_noprefix(entry, 36, 1); /* Length */
build_append_int_noprefix(entry, 1, 1); /* Version */
build_append_int_noprefix(entry, 0, 1); /* Reserved */
build_append_int_noprefix(entry, 0x1, 4); /* Flags */
build_append_int_noprefix(entry, hart_id, 8); /* Hart ID */
build_append_int_noprefix(entry, uid, 4); /* ACPI Processor UID */
+ /* External Interrupt Controller ID */
+ if (s->aia_type == VIRT_AIA_TYPE_APLIC) {
+ build_append_int_noprefix(entry,
+ ACPI_BUILD_INTC_ID(
+ arch_ids->cpus[uid].props.node_id,
+ local_cpu_id),
+ 4);
+ } else if (s->aia_type == VIRT_AIA_TYPE_NONE) {
+ build_append_int_noprefix(entry,
+ ACPI_BUILD_INTC_ID(
+ arch_ids->cpus[uid].props.node_id,
+ 2 * local_cpu_id + 1),
+ 4);
+ } else {
+ build_append_int_noprefix(entry, 0, 4);
+ }
+
+ if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) {
+ /* IMSIC Base address */
+ build_append_int_noprefix(entry, imsic_addr, 8);
+ /* IMSIC Size */
+ build_append_int_noprefix(entry, imsic_size, 4);
+ } else {
+ build_append_int_noprefix(entry, 0, 8);
+ build_append_int_noprefix(entry, 0, 4);
+ }
}
static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s)
aml_int(arch_ids->cpus[i].arch_id)));
/* build _MAT object */
- riscv_acpi_madt_add_rintc(i, arch_ids, madt_buf);
+ riscv_acpi_madt_add_rintc(i, arch_ids, madt_buf, s);
aml_append(dev, aml_name_decl("_MAT",
aml_buffer(madt_buf->len,
(uint8_t *)madt_buf->data)));
}
}
-static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap)
+static void
+acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
+ uint32_t uart_irq)
{
- Aml *dev = aml_device("FWCF");
- aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
-
- /* device present, functioning, decoding, not shown in UI */
- aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
- aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
+ Aml *dev = aml_device("COM0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
Aml *crs = aml_resource_template();
- aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base,
- fw_cfg_memmap->size, AML_READ_WRITE));
+ aml_append(crs, aml_memory32_fixed(uart_memmap->base,
+ uart_memmap->size, AML_READ_WRITE));
+ aml_append(crs,
+ aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, &uart_irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
+
+ Aml *pkg = aml_package(2);
+ aml_append(pkg, aml_string("clock-frequency"));
+ aml_append(pkg, aml_int(3686400));
+
+ Aml *UUID = aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301");
+
+ Aml *pkg1 = aml_package(1);
+ aml_append(pkg1, pkg);
+
+ Aml *package = aml_package(2);
+ aml_append(package, UUID);
+ aml_append(package, pkg1);
+
+ aml_append(dev, aml_name_decl("_DSD", package));
aml_append(scope, dev);
}
* 5.2.36 RISC-V Hart Capabilities Table (RHCT)
* REF: https://github.com/riscv-non-isa/riscv-acpi/issues/16
* https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view
+ * https://drive.google.com/file/d/1sKbOa8m1UZw1JkquZYe3F1zQBN1xXsaf/view
*/
static void build_rhct(GArray *table_data,
BIOSLinker *linker,
MachineState *ms = MACHINE(s);
const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
size_t len, aligned_len;
- uint32_t isa_offset, num_rhct_nodes;
- RISCVCPU *cpu;
+ uint32_t isa_offset, num_rhct_nodes, cmo_offset = 0;
+ RISCVCPU *cpu = &s->soc[0].harts[0];
+ uint32_t mmu_offset = 0;
+ uint8_t satp_mode_max;
char *isa;
AcpiTable table = { .sig = "RHCT", .rev = 1, .oem_id = s->oem_id,
/* ISA + N hart info */
num_rhct_nodes = 1 + ms->smp.cpus;
+ if (cpu->cfg.ext_zicbom || cpu->cfg.ext_zicboz) {
+ num_rhct_nodes++;
+ }
+
+ if (cpu->cfg.satp_mode.supported != 0) {
+ num_rhct_nodes++;
+ }
/* Number of RHCT nodes*/
build_append_int_noprefix(table_data, num_rhct_nodes, 4);
isa_offset = table_data->len - table.table_offset;
build_append_int_noprefix(table_data, 0, 2); /* Type 0 */
- cpu = &s->soc[0].harts[0];
isa = riscv_isa_string(cpu);
len = 8 + strlen(isa) + 1;
aligned_len = (len % 2) ? (len + 1) : len;
build_append_int_noprefix(table_data, 0x0, 1); /* Optional Padding */
}
+ /* CMO node */
+ if (cpu->cfg.ext_zicbom || cpu->cfg.ext_zicboz) {
+ cmo_offset = table_data->len - table.table_offset;
+ build_append_int_noprefix(table_data, 1, 2); /* Type */
+ build_append_int_noprefix(table_data, 10, 2); /* Length */
+ build_append_int_noprefix(table_data, 0x1, 2); /* Revision */
+ build_append_int_noprefix(table_data, 0, 1); /* Reserved */
+
+ /* CBOM block size */
+ if (cpu->cfg.cbom_blocksize) {
+ build_append_int_noprefix(table_data,
+ __builtin_ctz(cpu->cfg.cbom_blocksize),
+ 1);
+ } else {
+ build_append_int_noprefix(table_data, 0, 1);
+ }
+
+ /* CBOP block size */
+ build_append_int_noprefix(table_data, 0, 1);
+
+ /* CBOZ block size */
+ if (cpu->cfg.cboz_blocksize) {
+ build_append_int_noprefix(table_data,
+ __builtin_ctz(cpu->cfg.cboz_blocksize),
+ 1);
+ } else {
+ build_append_int_noprefix(table_data, 0, 1);
+ }
+ }
+
+ /* MMU node structure */
+ if (cpu->cfg.satp_mode.supported != 0) {
+ satp_mode_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map);
+ mmu_offset = table_data->len - table.table_offset;
+ build_append_int_noprefix(table_data, 2, 2); /* Type */
+ build_append_int_noprefix(table_data, 8, 2); /* Length */
+ build_append_int_noprefix(table_data, 0x1, 2); /* Revision */
+ build_append_int_noprefix(table_data, 0, 1); /* Reserved */
+ /* MMU Type */
+ if (satp_mode_max == VM_1_10_SV57) {
+ build_append_int_noprefix(table_data, 2, 1); /* Sv57 */
+ } else if (satp_mode_max == VM_1_10_SV48) {
+ build_append_int_noprefix(table_data, 1, 1); /* Sv48 */
+ } else if (satp_mode_max == VM_1_10_SV39) {
+ build_append_int_noprefix(table_data, 0, 1); /* Sv39 */
+ } else {
+ assert(1);
+ }
+ }
+
/* Hart Info Node */
for (int i = 0; i < arch_ids->len; i++) {
+ len = 16;
+ int num_offsets = 1;
build_append_int_noprefix(table_data, 0xFFFF, 2); /* Type */
- build_append_int_noprefix(table_data, 16, 2); /* Length */
- build_append_int_noprefix(table_data, 0x1, 2); /* Revision */
- build_append_int_noprefix(table_data, 1, 2); /* Number of offsets */
- build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */
- build_append_int_noprefix(table_data, isa_offset, 4); /* Offsets[0] */
+
+ /* Length */
+ if (cmo_offset) {
+ len += 4;
+ num_offsets++;
+ }
+
+ if (mmu_offset) {
+ len += 4;
+ num_offsets++;
+ }
+
+ build_append_int_noprefix(table_data, len, 2);
+ build_append_int_noprefix(table_data, 0x1, 2); /* Revision */
+ /* Number of offsets */
+ build_append_int_noprefix(table_data, num_offsets, 2);
+ build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */
+ /* Offsets */
+ build_append_int_noprefix(table_data, isa_offset, 4);
+ if (cmo_offset) {
+ build_append_int_noprefix(table_data, cmo_offset, 4);
+ }
+
+ if (mmu_offset) {
+ build_append_int_noprefix(table_data, mmu_offset, 4);
+ }
}
acpi_table_end(linker, &table);
RISCVVirtState *s)
{
Aml *scope, *dsdt;
+ MachineState *ms = MACHINE(s);
+ uint8_t socket_count;
const MemMapEntry *memmap = s->memmap;
AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = s->oem_id,
.oem_table_id = s->oem_table_id };
scope = aml_scope("\\_SB");
acpi_dsdt_add_cpus(scope, s);
- acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
+ fw_cfg_acpi_dsdt_add(scope, &memmap[VIRT_FW_CFG]);
+
+ socket_count = riscv_socket_count(ms);
+
+ acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ);
+
+ if (socket_count == 1) {
+ virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
+ memmap[VIRT_VIRTIO].size,
+ VIRTIO_IRQ, 0, VIRTIO_COUNT);
+ acpi_dsdt_add_gpex_host(scope, PCIE_IRQ);
+ } else if (socket_count == 2) {
+ virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
+ memmap[VIRT_VIRTIO].size,
+ VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0,
+ VIRTIO_COUNT);
+ acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES);
+ } else {
+ virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
+ memmap[VIRT_VIRTIO].size,
+ VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0,
+ VIRTIO_COUNT);
+ acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES * 2);
+ }
aml_append(dsdt, scope);
* 5.2.12 Multiple APIC Description Table (MADT)
* REF: https://github.com/riscv-non-isa/riscv-acpi/issues/15
* https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view
+ * https://drive.google.com/file/d/1oMGPyOD58JaPgMl1pKasT-VKsIKia7zR/view
*/
static void build_madt(GArray *table_data,
BIOSLinker *linker,
MachineClass *mc = MACHINE_GET_CLASS(s);
MachineState *ms = MACHINE(s);
const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
+ uint8_t group_index_bits = imsic_num_bits(riscv_socket_count(ms));
+ uint8_t guest_index_bits = imsic_num_bits(s->aia_guests + 1);
+ uint16_t imsic_max_hart_per_socket = 0;
+ uint8_t hart_index_bits;
+ uint64_t aplic_addr;
+ uint32_t gsi_base;
+ uint8_t socket;
+
+ for (socket = 0; socket < riscv_socket_count(ms); socket++) {
+ if (imsic_max_hart_per_socket < s->soc[socket].num_harts) {
+ imsic_max_hart_per_socket = s->soc[socket].num_harts;
+ }
+ }
+
+ hart_index_bits = imsic_num_bits(imsic_max_hart_per_socket);
AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = s->oem_id,
.oem_table_id = s->oem_table_id };
/* RISC-V Local INTC structures per HART */
for (int i = 0; i < arch_ids->len; i++) {
- riscv_acpi_madt_add_rintc(i, arch_ids, table_data);
+ riscv_acpi_madt_add_rintc(i, arch_ids, table_data, s);
+ }
+
+ /* IMSIC */
+ if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) {
+ /* IMSIC */
+ build_append_int_noprefix(table_data, 0x19, 1); /* Type */
+ build_append_int_noprefix(table_data, 16, 1); /* Length */
+ build_append_int_noprefix(table_data, 1, 1); /* Version */
+ build_append_int_noprefix(table_data, 0, 1); /* Reserved */
+ build_append_int_noprefix(table_data, 0, 4); /* Flags */
+ /* Number of supervisor mode Interrupt Identities */
+ build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2);
+ /* Number of guest mode Interrupt Identities */
+ build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2);
+ /* Guest Index Bits */
+ build_append_int_noprefix(table_data, guest_index_bits, 1);
+ /* Hart Index Bits */
+ build_append_int_noprefix(table_data, hart_index_bits, 1);
+ /* Group Index Bits */
+ build_append_int_noprefix(table_data, group_index_bits, 1);
+ /* Group Index Shift */
+ build_append_int_noprefix(table_data, IMSIC_MMIO_GROUP_MIN_SHIFT, 1);
+ }
+
+ if (s->aia_type != VIRT_AIA_TYPE_NONE) {
+ /* APLICs */
+ for (socket = 0; socket < riscv_socket_count(ms); socket++) {
+ aplic_addr = s->memmap[VIRT_APLIC_S].base +
+ s->memmap[VIRT_APLIC_S].size * socket;
+ gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket;
+ build_append_int_noprefix(table_data, 0x1A, 1); /* Type */
+ build_append_int_noprefix(table_data, 36, 1); /* Length */
+ build_append_int_noprefix(table_data, 1, 1); /* Version */
+ build_append_int_noprefix(table_data, socket, 1); /* APLIC ID */
+ build_append_int_noprefix(table_data, 0, 4); /* Flags */
+ build_append_int_noprefix(table_data, 0, 8); /* Hardware ID */
+ /* Number of IDCs */
+ if (s->aia_type == VIRT_AIA_TYPE_APLIC) {
+ build_append_int_noprefix(table_data,
+ s->soc[socket].num_harts,
+ 2);
+ } else {
+ build_append_int_noprefix(table_data, 0, 2);
+ }
+ /* Total External Interrupt Sources Supported */
+ build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_SOURCES, 2);
+ /* Global System Interrupt Base */
+ build_append_int_noprefix(table_data, gsi_base, 4);
+ /* APLIC Address */
+ build_append_int_noprefix(table_data, aplic_addr, 8);
+ /* APLIC size */
+ build_append_int_noprefix(table_data,
+ s->memmap[VIRT_APLIC_S].size, 4);
+ }
+ } else {
+ /* PLICs */
+ for (socket = 0; socket < riscv_socket_count(ms); socket++) {
+ aplic_addr = s->memmap[VIRT_PLIC].base +
+ s->memmap[VIRT_PLIC].size * socket;
+ gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket;
+ build_append_int_noprefix(table_data, 0x1B, 1); /* Type */
+ build_append_int_noprefix(table_data, 36, 1); /* Length */
+ build_append_int_noprefix(table_data, 1, 1); /* Version */
+ build_append_int_noprefix(table_data, socket, 1); /* PLIC ID */
+ build_append_int_noprefix(table_data, 0, 8); /* Hardware ID */
+ /* Total External Interrupt Sources Supported */
+ build_append_int_noprefix(table_data,
+ VIRT_IRQCHIP_NUM_SOURCES - 1, 2);
+ build_append_int_noprefix(table_data, 0, 2); /* Max Priority */
+ build_append_int_noprefix(table_data, 0, 4); /* Flags */
+ /* PLIC Size */
+ build_append_int_noprefix(table_data, s->memmap[VIRT_PLIC].size, 4);
+ /* PLIC Address */
+ build_append_int_noprefix(table_data, aplic_addr, 8);
+ /* Global System Interrupt Vector Base */
+ build_append_int_noprefix(table_data, gsi_base, 4);
+ }
}
acpi_table_end(linker, &table);
acpi_add_table(table_offsets, tables_blob);
build_rhct(tables_blob, tables->linker, s);
+ acpi_add_table(table_offsets, tables_blob);
+ {
+ AcpiMcfgInfo mcfg = {
+ .base = s->memmap[VIRT_PCIE_MMIO].base,
+ .size = s->memmap[VIRT_PCIE_MMIO].size,
+ };
+ build_mcfg(tables_blob, tables->linker, &mcfg, s->oem_id,
+ s->oem_table_id);
+ }
+
/* XSDT is pointed to by RSDP */
xsdt = tables_blob->len;
build_xsdt(tables_blob, tables->linker, table_offsets, s->oem_id,
.name = "virt_acpi_build",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_BOOL(patched, AcpiBuildState),
VMSTATE_END_OF_LIST()
},