diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
index 91663946de460c40f126184b34a3d5cea2a57daa..983c3c132e80561104ee56eb1695e9c15c893306 100644
--- a/.gitlab-ci.d/buildtest.yml
+++ b/.gitlab-ci.d/buildtest.yml
@@ -579,6 +579,9 @@ build-tci:
- make check-tcg
# Check our reduced build configurations
+# requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel,
+# mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64
+# does not build without boards: i386, s390x, sh4, sh4eb, x86_64
build-without-defaults:
extends: .native_build_job_template
needs:
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 8077630825b6f374c4377b588fc32453c3dbed0c..79d5671841b4855b84d29d35960a92be882192fd 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -141,7 +141,6 @@ static QemuMutex kml_slots_lock;
#define kvm_slots_unlock() qemu_mutex_unlock(&kml_slots_lock)
static void kvm_slot_init_dirty_bitmap(KVMSlot *mem);
-static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id);
static inline void kvm_resample_fd_remove(int gsi)
{
@@ -334,39 +333,58 @@ void kvm_park_vcpu(CPUState *cpu)
{
struct KVMParkedVcpu *vcpu;
+ trace_kvm_park_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
+
vcpu = g_malloc0(sizeof(*vcpu));
vcpu->vcpu_id = kvm_arch_vcpu_id(cpu);
vcpu->kvm_fd = cpu->kvm_fd;
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
}
+int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id)
+{
+ struct KVMParkedVcpu *cpu;
+ int kvm_fd = -ENOENT;
+
+ QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) {
+ if (cpu->vcpu_id == vcpu_id) {
+ QLIST_REMOVE(cpu, node);
+ kvm_fd = cpu->kvm_fd;
+ g_free(cpu);
+ break;
+ }
+ }
+
+ trace_kvm_unpark_vcpu(vcpu_id, kvm_fd > 0 ? "unparked" : "!found parked");
+
+ return kvm_fd;
+}
+
int kvm_create_vcpu(CPUState *cpu)
{
- unsigned long vcpu_id = cpu->cpu_index;
+ unsigned long vcpu_id = kvm_arch_vcpu_id(cpu);
KVMState *s = kvm_state;
- int ret;
-
- DPRINTF("kvm_create_vcpu\n");
+ int kvm_fd;
/* check if the KVM vCPU already exist but is parked */
- ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu));
- if (ret > 0) {
- goto found;
- }
-
- /* create a new KVM vcpu */
- ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id);
- if (ret < 0) {
- return ret;
+ kvm_fd = kvm_unpark_vcpu(s, vcpu_id);
+ if (kvm_fd < 0) {
+ /* vCPU not parked: create a new KVM vCPU */
+ kvm_fd = kvm_vm_ioctl(s, KVM_CREATE_VCPU, vcpu_id);
+ if (kvm_fd < 0) {
+ error_report("KVM_CREATE_VCPU IOCTL failed for vCPU %lu", vcpu_id);
+ return kvm_fd;
+ }
}
-found:
- cpu->vcpu_dirty = true;
- cpu->kvm_fd = ret;
+ cpu->kvm_fd = kvm_fd;
cpu->kvm_state = s;
+ cpu->vcpu_dirty = true;
cpu->dirty_pages = 0;
cpu->throttle_us_per_full = 0;
+ trace_kvm_create_vcpu(cpu->cpu_index, vcpu_id, kvm_fd);
+
return 0;
}
@@ -376,7 +394,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
long mmap_size;
int ret = 0;
- DPRINTF("kvm_destroy_vcpu\n");
+ trace_kvm_destroy_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
ret = kvm_arch_destroy_vcpu(cpu);
if (ret < 0) {
@@ -415,24 +433,6 @@ void kvm_destroy_vcpu(CPUState *cpu)
}
}
-static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id)
-{
- struct KVMParkedVcpu *cpu;
-
- QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) {
- if (cpu->vcpu_id == vcpu_id) {
- int kvm_fd;
-
- QLIST_REMOVE(cpu, node);
- kvm_fd = cpu->kvm_fd;
- g_free(cpu);
- return kvm_fd;
- }
- }
-
- return -1;
-}
-
int kvm_init_vcpu(CPUState *cpu, Error **errp)
{
KVMState *s = kvm_state;
diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events
index 399aaeb0ec757cf50ba44943d4ab4d30e02c1a0d..9c880fdcf4f67b985dfb94cea39428a291c9d7e3 100644
--- a/accel/kvm/trace-events
+++ b/accel/kvm/trace-events
@@ -9,6 +9,10 @@ kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
+kvm_create_vcpu(int cpu_index, unsigned long arch_cpu_id, int kvm_fd) "index: %d, id: %lu, kvm fd: %d"
+kvm_destroy_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
+kvm_park_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
+kvm_unpark_vcpu(unsigned long arch_cpu_id, const char *msg) "id: %lu %s"
kvm_irqchip_commit_routes(void) ""
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
@@ -26,3 +30,10 @@ kvm_dirty_ring_reap(uint64_t count, int64_t t) "reaped %"PRIu64" pages (took %"P
kvm_dirty_ring_reaper_kick(const char *reason) "%s"
kvm_dirty_ring_flush(int finished) "%d"
+kvm_failed_get_vcpu_mmap_size(void) ""
+kvm_cpu_exec(void) ""
+kvm_interrupt_exit_request(void) ""
+kvm_io_window_exit(void) ""
+kvm_run_exit_system_event(int cpu_index, uint32_t event_type) "cpu_index %d, system_even_type %"PRIu32
+kvm_convert_memory(uint64_t start, uint64_t size, const char *msg) "start 0x%" PRIx64 " size 0x%" PRIx64 " %s"
+kvm_memory_fault(uint64_t start, uint64_t size, uint64_t flags) "start 0x%" PRIx64 " size 0x%" PRIx64 " flags 0x%" PRIx64
diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak
index 928bc117ef781a5f64512b972b459751d08717ce..ffe705836fde47b47f82db9baa8d1b8073eec4df 100644
--- a/configs/devices/loongarch64-softmmu/default.mak
+++ b/configs/devices/loongarch64-softmmu/default.mak
@@ -1,3 +1,7 @@
# Default configuration for loongarch64-softmmu
-CONFIG_LOONGARCH_VIRT=y
+# Uncomment the following lines to disable these optional devices:
+# CONFIG_PCI_DEVICES=n
+
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_LOONGARCH_VIRT=n
diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak
index f23780fdd89945b82078b1680273bfa93d9f2ff1..0034c336200fb0629775e713982da70309464a54 100644
--- a/configs/targets/loongarch64-softmmu.mak
+++ b/configs/targets/loongarch64-softmmu.mak
@@ -1,5 +1,6 @@
TARGET_ARCH=loongarch64
TARGET_BASE_ARCH=loongarch
+TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml
TARGET_NEED_FDT=y
diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst
index 0bd3f9399fee04bc7829f6f5936a398720dc7eeb..3acd6fcd8b8fd84492391f702643b29af935b2c7 100644
--- a/docs/specs/acpi_hw_reduced_hotplug.rst
+++ b/docs/specs/acpi_hw_reduced_hotplug.rst
@@ -64,7 +64,8 @@ GED IO interface (4 byte access)
0: Memory hotplug event
1: System power down event
2: NVDIMM hotplug event
- 3-31: Reserved
+ 3: CPU hotplug event
+ 4-31: Reserved
**write_access:**
diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst
index c37268b404d4d2580439abd2e31b6e3492f765de..aa4719d4bdffd6636984cf67641b9fc5bde4f179 100644
--- a/docs/system/loongarch/virt.rst
+++ b/docs/system/loongarch/virt.rst
@@ -28,6 +28,37 @@ The ``qemu-system-loongarch64`` provides emulation for virt
machine. You can specify the machine type ``virt`` and
cpu type ``la464``.
+CPU Topology
+------------
+
+The ``LA464`` type CPUs have the concept of Socket Core and Thread.
+
+For example:
+
+``-smp 1,maxcpus=M,sockets=S,cores=C,threads=T``
+
+The above parameters indicate that the machine has a maximum of ``M`` vCPUs and
+``S`` sockets, each socket has ``C`` cores, each core has ``T`` threads,
+and each thread corresponds to a vCPU.
+
+Then ``M`` ``S`` ``C`` ``T`` has the following relationship:
+
+``M = S * C * T``
+
+In the CPU topology relationship, When we know the ``socket_id`` ``core_id``
+and ``thread_id`` of the CPU, we can calculate its ``arch_id``:
+
+``arch_id = (socket_id * S) + (core_id * C) + (thread_id * T)``
+
+Similarly, when we know the ``arch_id`` of the CPU,
+we can also get its ``socket_id`` ``core_id`` and ``thread_id``:
+
+``socket_id = arch_id / (C * T)``
+
+``core_id = (arch_id / T) % C``
+
+``thread_id = arch_id % T``
+
Boot options
------------
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index f16006d2a86e9e9910be347c0a0d787502075e6e..31c3dae52540f0f803a75d9f191c59a4dfe69852 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -584,8 +584,15 @@ void gdb_register_coprocessor(CPUState *cpu,
void gdb_unregister_coprocessor_all(CPUState *cpu)
{
+ /*
+ * Safe to nuke everything. GDBRegisterState::xml is static const char so
+ * it won't be freed
+ */
g_array_free(cpu->gdb_regs, true);
+
cpu->gdb_regs = NULL;
+ cpu->gdb_num_regs = 0;
+ cpu->gdb_num_g_regs = 0;
}
static void gdb_process_breakpoint_remove_all(GDBProcess *p)
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index bf9c59f5448f7938a99ddba355d099c3ed3b1e00..b0b68efa02ea239a2422b4834dff2457d05003af 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -2016,6 +2016,59 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags,
}
}
+void build_spcr(GArray *table_data, BIOSLinker *linker,
+ const AcpiSpcrData *f, const uint8_t rev,
+ const char *oem_id, const char *oem_table_id)
+{
+ AcpiTable table = { .sig = "SPCR", .rev = rev, .oem_id = oem_id,
+ .oem_table_id = oem_table_id };
+
+ acpi_table_begin(&table, table_data);
+ /* Interface type */
+ build_append_int_noprefix(table_data, f->interface_type, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 3);
+ /* Base Address */
+ build_append_gas(table_data, f->base_addr.id, f->base_addr.width,
+ f->base_addr.offset, f->base_addr.size,
+ f->base_addr.addr);
+ /* Interrupt type */
+ build_append_int_noprefix(table_data, f->interrupt_type, 1);
+ /* IRQ */
+ build_append_int_noprefix(table_data, f->pc_interrupt, 1);
+ /* Global System Interrupt */
+ build_append_int_noprefix(table_data, f->interrupt, 4);
+ /* Baud Rate */
+ build_append_int_noprefix(table_data, f->baud_rate, 1);
+ /* Parity */
+ build_append_int_noprefix(table_data, f->parity, 1);
+ /* Stop Bits */
+ build_append_int_noprefix(table_data, f->stop_bits, 1);
+ /* Flow Control */
+ build_append_int_noprefix(table_data, f->flow_control, 1);
+ /* Language */
+ build_append_int_noprefix(table_data, f->language, 1);
+ /* Terminal Type */
+ build_append_int_noprefix(table_data, f->terminal_type, 1);
+ /* PCI Device ID */
+ build_append_int_noprefix(table_data, f->pci_device_id, 2);
+ /* PCI Vendor ID */
+ build_append_int_noprefix(table_data, f->pci_vendor_id, 2);
+ /* PCI Bus Number */
+ build_append_int_noprefix(table_data, f->pci_bus, 1);
+ /* PCI Device Number */
+ build_append_int_noprefix(table_data, f->pci_device, 1);
+ /* PCI Function Number */
+ build_append_int_noprefix(table_data, f->pci_function, 1);
+ /* PCI Flags */
+ build_append_int_noprefix(table_data, f->pci_flags, 4);
+ /* PCI Segment */
+ build_append_int_noprefix(table_data, f->pci_segment, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 4);
+
+ acpi_table_end(linker, &table);
+}
/*
* ACPI spec, Revision 6.3
* 5.2.29.2 Cache Type Structure (Type 1)
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
index 292e1daca24fb79fe66a5f31fa265cacb52ad3ad..5e9093991e78adcecf899b7f50eb785a344f3ee2 100644
--- a/hw/acpi/cpu.c
+++ b/hw/acpi/cpu.c
@@ -392,11 +392,13 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
aml_name_decl("_UID", aml_string("CPU Hotplug resources")));
aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0));
+ assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY));
+
crs = aml_resource_template();
if (rs == AML_SYSTEM_IO) {
aml_append(crs, aml_io(AML_DECODE16, base_addr, base_addr, 1,
ACPI_CPU_HOTPLUG_REG_LEN));
- } else {
+ } else if (rs == AML_SYSTEM_MEMORY) {
aml_append(crs, aml_memory32_fixed(base_addr,
ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE));
}
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 4731a614a349fa0e6e08cf3792b01ef3d690dc54..755653dc265c7c2242a68ee144052c7b74b47b22 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -203,9 +203,9 @@ static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data,
switch (addr) {
case ACPI_GED_REG_SLEEP_CTL:
- slp_typ = (data >> 2) & 0x07;
- slp_en = (data >> 5) & 0x01;
- if (slp_en && slp_typ == 5) {
+ slp_typ = (data >> ACPI_GED_SLP_TYP_POS) & ACPI_GED_SLP_TYP_MASK;
+ slp_en = !!(data & ACPI_GED_SLP_EN);
+ if (slp_en && slp_typ == ACPI_GED_SLP_TYP_S5) {
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
return;
@@ -397,6 +397,42 @@ static const VMStateDescription vmstate_acpi_ged = {
}
};
+static void acpi_ged_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AcpiGedState *s = ACPI_GED(dev);
+ uint32_t ged_events;
+ int i;
+
+ ged_events = ctpop32(s->ged_event_bitmap);
+
+ for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) {
+ uint32_t event = s->ged_event_bitmap & ged_supported_events[i];
+
+ if (!event) {
+ continue;
+ }
+
+ switch (event) {
+ case ACPI_GED_CPU_HOTPLUG_EVT:
+ /* initialize CPU Hotplug related regions */
+ memory_region_init(&s->container_cpuhp, OBJECT(dev),
+ "cpuhp container",
+ ACPI_CPU_HOTPLUG_REG_LEN);
+ sysbus_init_mmio(sbd, &s->container_cpuhp);
+ cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev),
+ &s->cpuhp_state, 0);
+ break;
+ }
+ ged_events--;
+ }
+
+ if (ged_events) {
+ error_report("Unsupported events specified");
+ abort();
+ }
+}
+
static void acpi_ged_initfn(Object *obj)
{
DeviceState *dev = DEVICE(obj);
@@ -447,6 +483,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data)
dc->desc = "ACPI Generic Event Device";
device_class_set_props(dc, acpi_ged_properties);
dc->vmsd = &vmstate_acpi_ged;
+ dc->realize = acpi_ged_realize;
hc->plug = acpi_ged_device_plug_cb;
hc->unplug_request = acpi_ged_unplug_request_cb;
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 179600d4fe783d4a5e9b82cc4f1ceb83bb10c56d..bc637b861937609b064ec3480d15b03729f45651 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -550,48 +550,34 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
* Rev: 1.07
*/
static void
-build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
+spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
{
- AcpiTable table = { .sig = "SPCR", .rev = 2, .oem_id = vms->oem_id,
- .oem_table_id = vms->oem_table_id };
-
- acpi_table_begin(&table, table_data);
-
- /* Interface Type */
- build_append_int_noprefix(table_data, 3, 1); /* ARM PL011 UART */
- build_append_int_noprefix(table_data, 0, 3); /* Reserved */
- /* Base Address */
- build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 32, 0, 3,
- vms->memmap[VIRT_UART].base);
- /* Interrupt Type */
- build_append_int_noprefix(table_data,
- (1 << 3) /* Bit[3] ARMH GIC interrupt */, 1);
- build_append_int_noprefix(table_data, 0, 1); /* IRQ */
- /* Global System Interrupt */
- build_append_int_noprefix(table_data,
- vms->irqmap[VIRT_UART] + ARM_SPI_BASE, 4);
- build_append_int_noprefix(table_data, 3 /* 9600 */, 1); /* Baud Rate */
- build_append_int_noprefix(table_data, 0 /* No Parity */, 1); /* Parity */
- /* Stop Bits */
- build_append_int_noprefix(table_data, 1 /* 1 Stop bit */, 1);
- /* Flow Control */
- build_append_int_noprefix(table_data,
- (1 << 1) /* RTS/CTS hardware flow control */, 1);
- /* Terminal Type */
- build_append_int_noprefix(table_data, 0 /* VT100 */, 1);
- build_append_int_noprefix(table_data, 0, 1); /* Language */
- /* PCI Device ID */
- build_append_int_noprefix(table_data, 0xffff /* not a PCI device*/, 2);
- /* PCI Vendor ID */
- build_append_int_noprefix(table_data, 0xffff /* not a PCI device*/, 2);
- build_append_int_noprefix(table_data, 0, 1); /* PCI Bus Number */
- build_append_int_noprefix(table_data, 0, 1); /* PCI Device Number */
- build_append_int_noprefix(table_data, 0, 1); /* PCI Function Number */
- build_append_int_noprefix(table_data, 0, 4); /* PCI Flags */
- build_append_int_noprefix(table_data, 0, 1); /* PCI Segment */
- build_append_int_noprefix(table_data, 0, 4); /* Reserved */
+ AcpiSpcrData serial = {
+ .interface_type = 3, /* ARM PL011 UART */
+ .base_addr.id = AML_AS_SYSTEM_MEMORY,
+ .base_addr.width = 32,
+ .base_addr.offset = 0,
+ .base_addr.size = 3,
+ .base_addr.addr = vms->memmap[VIRT_UART].base,
+ .interrupt_type = (1 << 3),/* Bit[3] ARMH GIC interrupt*/
+ .pc_interrupt = 0, /* IRQ */
+ .interrupt = (vms->irqmap[VIRT_UART] + ARM_SPI_BASE),
+ .baud_rate = 3, /* 9600 */
+ .parity = 0, /* No Parity */
+ .stop_bits = 1, /* 1 Stop bit */
+ .flow_control = 1 << 1, /* RTS/CTS hardware flow control */
+ .terminal_type = 0, /* VT100 */
+ .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,
+ };
- acpi_table_end(linker, &table);
+ build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id);
}
/*
@@ -1149,7 +1135,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
}
acpi_add_table(table_offsets, tables_blob);
- build_spcr(tables_blob, tables->linker, vms);
+ spcr_setup(tables_blob, tables->linker, vms);
acpi_add_table(table_offsets, tables_blob);
build_dbg2(tables_blob, tables->linker, vms);
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 82dae51a550b52b836fe9802784e6d50fb71c337..e36ca2c207093a41ff4554dc41941fe424edad25 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -262,6 +262,10 @@ static void cpu_common_finalize(Object *obj)
{
CPUState *cpu = CPU(obj);
+ /* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */
+ if (cpu->gdb_regs) {
+ g_array_free(cpu->gdb_regs, TRUE);
+ }
qemu_lockcnt_destroy(&cpu->in_ioctl_lock);
qemu_mutex_destroy(&cpu->work_mutex);
}
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index 97d550b06b77d16d08410183af798c50a1535bb0..91c7aa668e3bf908c3f86d242dd0403066eb8dc4 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -93,10 +93,16 @@ config NIOS2_VIC
config LOONGARCH_IPI
bool
+config LOONGARCH_IPI_KVM
+ bool
+
config LOONGARCH_PCH_PIC
bool
select UNIMP
+config LOONGARCH_PCH_PIC_KVM
+ bool
+
config LOONGARCH_PCH_MSI
select MSI_NONBROKEN
bool
@@ -104,3 +110,6 @@ config LOONGARCH_PCH_MSI
config LOONGARCH_EXTIOI
bool
+
+config LOONGARCH_EXTIOI_KVM
+ bool
diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c
new file mode 100644
index 0000000000000000000000000000000000000000..f5bbc3325556903e6482d8517bed23ae0b861e8e
--- /dev/null
+++ b/hw/intc/loongarch_extioi_kvm.c
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch kvm extioi interrupt support
+ *
+ * Copyright (C) 2024 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "qemu/typedefs.h"
+#include "hw/intc/loongarch_extioi.h"
+#include "hw/sysbus.h"
+#include "linux/kvm.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "sysemu/kvm.h"
+
+static void kvm_extioi_access_regs(int fd, uint64_t addr,
+ void *val, int is_write)
+{
+ kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS,
+ addr, val, is_write, &error_abort);
+}
+
+static int kvm_loongarch_extioi_pre_save(void *opaque)
+{
+ KVMLoongArchExtIOI *s = (KVMLoongArchExtIOI *)opaque;
+ KVMLoongArchExtIOIClass *class = KVM_LOONGARCH_EXTIOI_GET_CLASS(s);
+ int fd = class->dev_fd;
+
+ kvm_extioi_access_regs(fd, EXTIOI_NODETYPE_START,
+ (void *)s->nodetype, false);
+ kvm_extioi_access_regs(fd, EXTIOI_IPMAP_START, (void *)s->ipmap, false);
+ kvm_extioi_access_regs(fd, EXTIOI_ENABLE_START, (void *)s->enable, false);
+ kvm_extioi_access_regs(fd, EXTIOI_BOUNCE_START, (void *)s->bounce, false);
+ kvm_extioi_access_regs(fd, EXTIOI_ISR_START, (void *)s->isr, false);
+ kvm_extioi_access_regs(fd, EXTIOI_COREMAP_START,
+ (void *)s->coremap, false);
+ kvm_extioi_access_regs(fd, EXTIOI_SW_COREMAP_FLAG,
+ (void *)s->sw_coremap, false);
+ kvm_extioi_access_regs(fd, EXTIOI_COREISR_START,
+ (void *)s->coreisr, false);
+
+ return 0;
+}
+
+static int kvm_loongarch_extioi_post_load(void *opaque, int version_id)
+{
+ KVMLoongArchExtIOI *s = (KVMLoongArchExtIOI *)opaque;
+ KVMLoongArchExtIOIClass *class = KVM_LOONGARCH_EXTIOI_GET_CLASS(s);
+ int fd = class->dev_fd;
+
+ kvm_extioi_access_regs(fd, EXTIOI_NODETYPE_START,
+ (void *)s->nodetype, true);
+ kvm_extioi_access_regs(fd, EXTIOI_IPMAP_START, (void *)s->ipmap, true);
+ kvm_extioi_access_regs(fd, EXTIOI_ENABLE_START, (void *)s->enable, true);
+ kvm_extioi_access_regs(fd, EXTIOI_BOUNCE_START, (void *)s->bounce, true);
+ kvm_extioi_access_regs(fd, EXTIOI_ISR_START, (void *)s->isr, true);
+ kvm_extioi_access_regs(fd, EXTIOI_COREMAP_START, (void *)s->coremap, true);
+ kvm_extioi_access_regs(fd, EXTIOI_SW_COREMAP_FLAG,
+ (void *)s->sw_coremap, true);
+ kvm_extioi_access_regs(fd, EXTIOI_COREISR_START, (void *)s->coreisr, true);
+
+ return 0;
+}
+
+static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp)
+{
+ KVMLoongArchExtIOIClass *extioi_class = KVM_LOONGARCH_EXTIOI_GET_CLASS(dev);
+ struct kvm_create_device cd = {0};
+ Error *err = NULL;
+ int ret,i;
+
+ extioi_class->parent_realize(dev, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (!extioi_class->is_created) {
+ cd.type = KVM_DEV_TYPE_LA_EXTIOI;
+ ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
+ if (ret < 0) {
+ error_setg_errno(errp, errno,
+ "Creating the KVM extioi device failed");
+ return;
+ }
+ extioi_class->is_created = true;
+ extioi_class->dev_fd = cd.fd;
+ fprintf(stdout, "Create LoongArch extioi irqchip in KVM done!\n");
+ }
+
+ kvm_async_interrupts_allowed = true;
+ kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
+ if (kvm_has_gsi_routing()) {
+ for (i = 0; i < 64; ++i) {
+ kvm_irqchip_add_irq_route(kvm_state, i, 0, i);
+ }
+ kvm_gsi_routing_allowed = true;
+ }
+}
+
+static const VMStateDescription vmstate_kvm_extioi_core = {
+ .name = "kvm-extioi-single",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = kvm_loongarch_extioi_pre_save,
+ .post_load = kvm_loongarch_extioi_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(nodetype, KVMLoongArchExtIOI,
+ EXTIOI_IRQS_NODETYPE_COUNT / 2),
+ VMSTATE_UINT32_ARRAY(bounce, KVMLoongArchExtIOI,
+ EXTIOI_IRQS_GROUP_COUNT),
+ VMSTATE_UINT32_ARRAY(isr, KVMLoongArchExtIOI, EXTIOI_IRQS / 32),
+ VMSTATE_UINT32_2DARRAY(coreisr, KVMLoongArchExtIOI, EXTIOI_CPUS,
+ EXTIOI_IRQS_GROUP_COUNT),
+ VMSTATE_UINT32_ARRAY(enable, KVMLoongArchExtIOI, EXTIOI_IRQS / 32),
+ VMSTATE_UINT32_ARRAY(ipmap, KVMLoongArchExtIOI,
+ EXTIOI_IRQS_IPMAP_SIZE / 4),
+ VMSTATE_UINT32_ARRAY(coremap, KVMLoongArchExtIOI, EXTIOI_IRQS / 4),
+ VMSTATE_UINT8_ARRAY(sw_coremap, KVMLoongArchExtIOI, EXTIOI_IRQS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void kvm_loongarch_extioi_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ KVMLoongArchExtIOIClass *extioi_class = KVM_LOONGARCH_EXTIOI_CLASS(oc);
+
+ extioi_class->parent_realize = dc->realize;
+ dc->realize = kvm_loongarch_extioi_realize;
+ extioi_class->is_created = false;
+ dc->vmsd = &vmstate_kvm_extioi_core;
+}
+
+static const TypeInfo kvm_loongarch_extioi_info = {
+ .name = TYPE_KVM_LOONGARCH_EXTIOI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(KVMLoongArchExtIOI),
+ .class_size = sizeof(KVMLoongArchExtIOIClass),
+ .class_init = kvm_loongarch_extioi_class_init,
+};
+
+static void kvm_loongarch_extioi_register_types(void)
+{
+ type_register_static(&kvm_loongarch_extioi_info);
+}
+
+type_init(kvm_loongarch_extioi_register_types)
diff --git a/hw/intc/loongarch_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c
new file mode 100644
index 0000000000000000000000000000000000000000..fd308eb0c0433892609ef35e9d7e80967b7f7e8e
--- /dev/null
+++ b/hw/intc/loongarch_ipi_kvm.c
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch kvm ipi interrupt support
+ *
+ * Copyright (C) 2024 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "qemu/typedefs.h"
+#include "hw/intc/loongarch_ipi.h"
+#include "hw/sysbus.h"
+#include "linux/kvm.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "sysemu/kvm.h"
+
+#define IPI_DEV_FD_UNDEF -1
+
+static void kvm_ipi_access_regs(int fd, uint64_t addr,
+ uint32_t *val, int is_write)
+{
+ kvm_device_access(fd, KVM_DEV_LOONGARCH_IPI_GRP_REGS,
+ addr, val, is_write, &error_abort);
+}
+
+static int kvm_loongarch_ipi_pre_save(void *opaque)
+{
+ KVMLoongArchIPI *ipi = (KVMLoongArchIPI *)opaque;
+ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_GET_CLASS(ipi);
+ IPICore *cpu;
+ uint64_t attr;
+ int cpu_id = 0;
+ int fd = ipi_class->dev_fd;
+
+ for (cpu_id = 0; cpu_id < ipi->num_cpu; cpu_id++) {
+ cpu = &ipi->cpu[cpu_id];
+ attr = (cpu_id << 16) | CORE_STATUS_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->status, false);
+
+ attr = (cpu_id << 16) | CORE_EN_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->en, false);
+
+ attr = (cpu_id << 16) | CORE_SET_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->set, false);
+
+ attr = (cpu_id << 16) | CORE_CLEAR_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->clear, false);
+
+ attr = (cpu_id << 16) | CORE_BUF_20;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[0], false);
+
+ attr = (cpu_id << 16) | CORE_BUF_28;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[2], false);
+
+ attr = (cpu_id << 16) | CORE_BUF_30;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[4], false);
+
+ attr = (cpu_id << 16) | CORE_BUF_38;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[6], false);
+ }
+
+ return 0;
+}
+
+static int kvm_loongarch_ipi_post_load(void *opaque, int version_id)
+{
+ KVMLoongArchIPI *ipi = (KVMLoongArchIPI *)opaque;
+ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_GET_CLASS(ipi);
+ IPICore *cpu;
+ uint64_t attr;
+ int cpu_id = 0;
+ int fd = ipi_class->dev_fd;
+
+ for (cpu_id = 0; cpu_id < ipi->num_cpu; cpu_id++) {
+ cpu = &ipi->cpu[cpu_id];
+ attr = (cpu_id << 16) | CORE_STATUS_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->status, true);
+
+ attr = (cpu_id << 16) | CORE_EN_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->en, true);
+
+ attr = (cpu_id << 16) | CORE_SET_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->set, true);
+
+ attr = (cpu_id << 16) | CORE_CLEAR_OFF;
+ kvm_ipi_access_regs(fd, attr, &cpu->clear, true);
+
+ attr = (cpu_id << 16) | CORE_BUF_20;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[0], true);
+
+ attr = (cpu_id << 16) | CORE_BUF_28;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[2], true);
+
+ attr = (cpu_id << 16) | CORE_BUF_30;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[4], true);
+
+ attr = (cpu_id << 16) | CORE_BUF_38;
+ kvm_ipi_access_regs(fd, attr, &cpu->buf[6], true);
+ }
+
+ return 0;
+}
+
+static void kvm_loongarch_ipi_realize(DeviceState *dev, Error **errp)
+{
+ KVMLoongArchIPI *ipi = KVM_LOONGARCH_IPI(dev);
+ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_GET_CLASS(dev);
+ struct kvm_create_device cd = {0};
+ Error *err = NULL;
+ int ret;
+
+ if (ipi->num_cpu == 0) {
+ error_setg(errp, "num-cpu must be at least 1");
+ return;
+ }
+
+ ipi_class->parent_realize(dev, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ ipi->cpu = g_new0(IPICore, ipi->num_cpu);
+ if (ipi->cpu == NULL) {
+ error_setg(errp, "Memory allocation for ExtIOICore faile");
+ return;
+ }
+
+ if (!ipi_class->is_created) {
+ cd.type = KVM_DEV_TYPE_LA_IPI;
+ ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
+ if (ret < 0) {
+ error_setg_errno(errp, errno, "Creating the KVM device failed");
+ return;
+ }
+ ipi_class->is_created = true;
+ ipi_class->dev_fd = cd.fd;
+ fprintf(stdout, "Create LoongArch IPI irqchip in KVM done!\n");
+ }
+
+ assert(ipi_class->dev_fd != IPI_DEV_FD_UNDEF);
+}
+
+static Property kvm_loongarch_ipi_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", KVMLoongArchIPI, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static const VMStateDescription vmstate_kvm_ipi_core = {
+ .name = "kvm-ipi-single",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(status, IPICore),
+ VMSTATE_UINT32(en, IPICore),
+ VMSTATE_UINT32(set, IPICore),
+ VMSTATE_UINT32(clear, IPICore),
+ VMSTATE_UINT32_ARRAY(buf, IPICore, 8),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_kvm_loongarch_ipi = {
+ .name = TYPE_KVM_LOONGARCH_IPI,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = kvm_loongarch_ipi_pre_save,
+ .post_load = kvm_loongarch_ipi_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, KVMLoongArchIPI, num_cpu,
+ vmstate_kvm_ipi_core, IPICore),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void kvm_loongarch_ipi_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_CLASS(oc);
+
+ ipi_class->parent_realize = dc->realize;
+ dc->realize = kvm_loongarch_ipi_realize;
+
+ ipi_class->is_created = false;
+ ipi_class->dev_fd = IPI_DEV_FD_UNDEF;
+
+ device_class_set_props(dc, kvm_loongarch_ipi_properties);
+
+ dc->vmsd = &vmstate_kvm_loongarch_ipi;
+}
+
+static const TypeInfo kvm_loongarch_ipi_info = {
+ .name = TYPE_KVM_LOONGARCH_IPI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(KVMLoongArchIPI),
+ .class_size = sizeof(KVMLoongArchIPIClass),
+ .class_init = kvm_loongarch_ipi_class_init,
+};
+
+static void kvm_loongarch_ipi_register_types(void)
+{
+ type_register_static(&kvm_loongarch_ipi_info);
+}
+
+type_init(kvm_loongarch_ipi_register_types)
diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c
index ecf3ed0267e4fb79e0614fcdc941b7e121e2e644..901c2c21bef33d6d46ff5347e755d9d716cb3d8b 100644
--- a/hw/intc/loongarch_pch_msi.c
+++ b/hw/intc/loongarch_pch_msi.c
@@ -14,6 +14,8 @@
#include "hw/misc/unimp.h"
#include "migration/vmstate.h"
#include "trace.h"
+#include "sysemu/kvm.h"
+#include "hw/loongarch/virt.h"
static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size)
{
@@ -26,14 +28,24 @@ static void loongarch_msi_mem_write(void *opaque, hwaddr addr,
LoongArchPCHMSI *s = (LoongArchPCHMSI *)opaque;
int irq_num;
- /*
- * vector number is irq number from upper extioi intc
- * need subtract irq base to get msi vector offset
- */
- irq_num = (val & 0xff) - s->irq_base;
- trace_loongarch_msi_set_irq(irq_num);
- assert(irq_num < s->irq_num);
- qemu_set_irq(s->pch_msi_irq[irq_num], 1);
+ MSIMessage msg = {
+ .address = addr,
+ .data = val,
+ };
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_irqchip_send_msi(kvm_state, msg);
+ } else {
+ /*
+ * vector number is irq number from upper extioi intc
+ * need subtract irq base to get msi vector offset
+ */
+ irq_num = (val & 0xff) - s->irq_base;
+ trace_loongarch_msi_set_irq(irq_num);
+ assert(irq_num < s->irq_num);
+
+ qemu_set_irq(s->pch_msi_irq[irq_num], 1);
+ }
}
static const MemoryRegionOps loongarch_pch_msi_ops = {
@@ -46,7 +58,16 @@ static void pch_msi_irq_handler(void *opaque, int irq, int level)
{
LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque);
- qemu_set_irq(s->pch_msi_irq[irq], level);
+ MSIMessage msg = {
+ .address = 0,
+ .data = irq,
+ };
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_irqchip_send_msi(kvm_state, msg);
+ } else {
+ qemu_set_irq(s->pch_msi_irq[irq], level);
+ }
}
static void loongarch_pch_msi_realize(DeviceState *dev, Error **errp)
diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c
index 6aa4cadfa4afd936d61a96e600b31947a62bb9b9..beb4ac188d8f97912856194bdfa00dfc90b00214 100644
--- a/hw/intc/loongarch_pch_pic.c
+++ b/hw/intc/loongarch_pch_pic.c
@@ -16,19 +16,28 @@
#include "migration/vmstate.h"
#include "trace.h"
#include "qapi/error.h"
+#include "sysemu/kvm.h"
static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
{
uint64_t val;
int irq;
+ int kvm_irq;
if (level) {
val = mask & s->intirr & ~s->int_mask;
if (val) {
irq = ctz64(val);
s->intisr |= MAKE_64BIT_MASK(irq, 1);
- qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
- }
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_irq = (
+ KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT)
+ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | s->htmsi_vector[irq];
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+ } else {
+ qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
+ }
+ }
} else {
/*
* intirr means requested pending irq
@@ -38,8 +47,15 @@ static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
if (val) {
irq = ctz64(val);
s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
- qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
- }
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_irq = (
+ KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT)
+ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | s->htmsi_vector[irq];
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+ } else {
+ qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
+ }
+ }
}
}
diff --git a/hw/intc/loongarch_pch_pic_kvm.c b/hw/intc/loongarch_pch_pic_kvm.c
new file mode 100644
index 0000000000000000000000000000000000000000..8f66d9a01fe1b153b13f3df1ec557005c0482923
--- /dev/null
+++ b/hw/intc/loongarch_pch_pic_kvm.c
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch kvm pch pic interrupt support
+ *
+ * Copyright (C) 2024 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "qemu/typedefs.h"
+#include "hw/intc/loongarch_pch_pic.h"
+#include "hw/sysbus.h"
+#include "linux/kvm.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "sysemu/kvm.h"
+#include "hw/loongarch/virt.h"
+#include "hw/pci-host/ls7a.h"
+#include "qemu/error-report.h"
+
+static void kvm_pch_pic_access_regs(int fd, uint64_t addr,
+ void *val, int is_write)
+{
+ kvm_device_access(fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS,
+ addr, val, is_write, &error_abort);
+}
+
+static int kvm_loongarch_pch_pic_pre_save(void *opaque)
+{
+ KVMLoongArchPCHPIC *s = (KVMLoongArchPCHPIC *)opaque;
+ KVMLoongArchPCHPICClass *class = KVM_LOONGARCH_PCH_PIC_GET_CLASS(s);
+ int fd = class->dev_fd;
+
+ kvm_pch_pic_access_regs(fd, PCH_PIC_MASK_START,
+ (void *)&s->int_mask, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_EN_START,
+ (void *)&s->htmsi_en, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_EDGE_START,
+ (void *)&s->intedge, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL0_START,
+ (void *)&s->auto_crtl0, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL1_START,
+ (void *)&s->auto_crtl1, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_ROUTE_ENTRY_START,
+ (void *)s->route_entry, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_VEC_START,
+ (void *)s->htmsi_vector, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_IRR_START,
+ (void *)&s->intirr, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_ISR_START,
+ (void *)&s->intisr, false);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_POLARITY_START,
+ (void *)&s->int_polarity, false);
+
+ return 0;
+}
+
+static int kvm_loongarch_pch_pic_post_load(void *opaque, int version_id)
+{
+ KVMLoongArchPCHPIC *s = (KVMLoongArchPCHPIC *)opaque;
+ KVMLoongArchPCHPICClass *class = KVM_LOONGARCH_PCH_PIC_GET_CLASS(s);
+ int fd = class->dev_fd;
+
+ kvm_pch_pic_access_regs(fd, PCH_PIC_MASK_START,
+ (void *)&s->int_mask, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_EN_START,
+ (void *)&s->htmsi_en, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_EDGE_START,
+ (void *)&s->intedge, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL0_START,
+ (void *)&s->auto_crtl0, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL1_START,
+ (void *)&s->auto_crtl1, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_ROUTE_ENTRY_START,
+ (void *)s->route_entry, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_VEC_START,
+ (void *)s->htmsi_vector, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_IRR_START,
+ (void *)&s->intirr, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_ISR_START,
+ (void *)&s->intisr, true);
+ kvm_pch_pic_access_regs(fd, PCH_PIC_POLARITY_START,
+ (void *)&s->int_polarity, true);
+
+ return 0;
+}
+
+static void kvm_pch_pic_handler(void *opaque, int irq, int level)
+{
+ int kvm_irq;
+
+ if (kvm_enabled()) {
+ kvm_irq = \
+ (KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT)
+ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | irq;
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+ }
+}
+
+static void kvm_loongarch_pch_pic_realize(DeviceState *dev, Error **errp)
+{
+ KVMLoongArchPCHPICClass *pch_pic_class =
+ KVM_LOONGARCH_PCH_PIC_GET_CLASS(dev);
+ struct kvm_create_device cd = {0};
+ uint64_t pch_pic_base = VIRT_PCH_REG_BASE;
+ Error *err = NULL;
+ int ret;
+
+ pch_pic_class->parent_realize(dev, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (!pch_pic_class->is_created) {
+ cd.type = KVM_DEV_TYPE_LA_PCH_PIC;
+ ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
+ if (ret < 0) {
+ error_setg_errno(errp, errno,
+ "Creating the KVM pch pic device failed");
+ return;
+ }
+ pch_pic_class->is_created = true;
+ pch_pic_class->dev_fd = cd.fd;
+ fprintf(stdout, "Create LoongArch pch pic irqchip in KVM done!\n");
+
+ ret = kvm_device_access(cd.fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL,
+ KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT,
+ &pch_pic_base, true, NULL);
+ if (ret < 0) {
+ error_report(
+ "KVM EXTIOI: failed to set the base address of EXTIOI");
+ exit(1);
+ }
+
+ qdev_init_gpio_in(dev, kvm_pch_pic_handler, VIRT_PCH_PIC_IRQ_NUM);
+ }
+}
+
+static const VMStateDescription vmstate_kvm_loongarch_pch_pic = {
+ .name = TYPE_LOONGARCH_PCH_PIC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = kvm_loongarch_pch_pic_pre_save,
+ .post_load = kvm_loongarch_pch_pic_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT64(int_mask, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(htmsi_en, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intedge, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intclr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(auto_crtl0, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(auto_crtl1, KVMLoongArchPCHPIC),
+ VMSTATE_UINT8_ARRAY(route_entry, KVMLoongArchPCHPIC, 64),
+ VMSTATE_UINT8_ARRAY(htmsi_vector, KVMLoongArchPCHPIC, 64),
+ VMSTATE_UINT64(last_intirr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intirr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(intisr, KVMLoongArchPCHPIC),
+ VMSTATE_UINT64(int_polarity, KVMLoongArchPCHPIC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static void kvm_loongarch_pch_pic_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ KVMLoongArchPCHPICClass *pch_pic_class = KVM_LOONGARCH_PCH_PIC_CLASS(oc);
+
+ pch_pic_class->parent_realize = dc->realize;
+ dc->realize = kvm_loongarch_pch_pic_realize;
+ pch_pic_class->is_created = false;
+ dc->vmsd = &vmstate_kvm_loongarch_pch_pic;
+
+}
+
+static const TypeInfo kvm_loongarch_pch_pic_info = {
+ .name = TYPE_KVM_LOONGARCH_PCH_PIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(KVMLoongArchPCHPIC),
+ .class_size = sizeof(KVMLoongArchPCHPICClass),
+ .class_init = kvm_loongarch_pch_pic_class_init,
+};
+
+static void kvm_loongarch_pch_pic_register_types(void)
+{
+ type_register_static(&kvm_loongarch_pch_pic_info);
+}
+
+type_init(kvm_loongarch_pch_pic_register_types)
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index ed355941d174193633f1601d45208eddf8f4963d..49b450131556911967c3b7881041f48c1aca95b9 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -70,6 +70,9 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c'))
+specific_ss.add(when: 'CONFIG_LOONGARCH_IPI_KVM', if_true: files('loongarch_ipi_kvm.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c'))
+specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI_KVM', if_true: files('loongarch_extioi_kvm.c'))
+specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC_KVM', if_true: files('loongarch_pch_pic_kvm.c'))
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index 5727efed6d8442bee0217d2dcd0f2e6c1cd8c52e..16c854c0d595f54aa4a9bcf194f19cbbbccd9331 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -1,10 +1,12 @@
config LOONGARCH_VIRT
bool
+ default y
+ depends on LOONGARCH64
select PCI
select PCI_EXPRESS_GENERIC_BRIDGE
- imply VIRTIO_VGA
imply PCI_DEVICES
imply NVDIMM
+ imply TPM_TIS_SYSBUS
select SERIAL
select VIRTIO_PCI
select PLATFORM_BUS
@@ -12,8 +14,12 @@ config LOONGARCH_VIRT
select LOONGARCH_PCH_PIC
select LOONGARCH_PCH_MSI
select LOONGARCH_EXTIOI
+ select LOONGARCH_IPI_KVM if KVM
+ select LOONGARCH_PCH_PIC_KVM if KVM
+ select LOONGARCH_EXTIOI_KVM if KVM
select LS7A_RTC
select SMBIOS
+ select ACPI_CPU_HOTPLUG
select ACPI_PCI
select ACPI_HW_REDUCED
select FW_CFG_DMA
diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c
index f990405d04a92e09e9cce2c20ef8561078163e2f..a54c5e0e70622e0e1212505df2cb6b47deef82d9 100644
--- a/hw/loongarch/acpi-build.c
+++ b/hw/loongarch/acpi-build.c
@@ -31,6 +31,7 @@
#include "hw/acpi/generic_event_device.h"
#include "hw/pci-host/gpex.h"
+#include "sysemu/sysemu.h"
#include "sysemu/tpm.h"
#include "hw/platform-bus.h"
#include "hw/acpi/aml-build.h"
@@ -46,6 +47,22 @@
#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)
{
@@ -105,14 +122,15 @@ build_facs(GArray *table_data)
/* build MADT */
static void
-build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams)
+build_madt(GArray *table_data, BIOSLinker *linker,
+ LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
MachineClass *mc = MACHINE_GET_CLASS(ms);
const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
int i, arch_id;
- AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id,
- .oem_table_id = lams->oem_table_id };
+ AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id,
+ .oem_table_id = lvms->oem_table_id };
acpi_table_begin(&table, table_data);
@@ -121,15 +139,17 @@ build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams)
build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
for (i = 0; i < arch_ids->len; i++) {
+ uint32_t flags;
+
/* 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, 1, 4); /* Flags */
+ build_append_int_noprefix(table_data, flags, 4); /* Flags */
}
/* Extend I/O Interrupt Controller Structure */
@@ -165,13 +185,14 @@ static void
build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
{
int i, arch_id, node_id;
- uint64_t mem_len, mem_base;
- int nb_numa_nodes = machine->numa_state->num_nodes;
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ 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 = lams->oem_id,
- .oem_table_id = lams->oem_table_id };
+ 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 */
@@ -195,41 +216,88 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
}
- /* Node0 */
- build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE,
- 0, MEM_AFFINITY_ENABLED);
- mem_base = VIRT_HIGHMEM_BASE;
- if (!nb_numa_nodes) {
- mem_len = machine->ram_size - VIRT_LOWMEM_SIZE;
- } else {
- mem_len = machine->numa_state->nodes[0].node_mem - VIRT_LOWMEM_SIZE;
+ base = VIRT_LOWMEM_BASE;
+ gap = VIRT_LOWMEM_SIZE;
+ numa_info = machine->numa_state->nodes;
+ nodes = nb_numa_nodes;
+ if (!nodes) {
+ nodes = 1;
}
- if (mem_len)
- build_srat_memory(table_data, mem_base, mem_len, 0, MEM_AFFINITY_ENABLED);
-
- /* Node1 - Nodemax */
- if (nb_numa_nodes) {
- mem_base += mem_len;
- for (i = 1; i < nb_numa_nodes; ++i) {
- if (machine->numa_state->nodes[i].node_mem > 0) {
- build_srat_memory(table_data, mem_base,
- machine->numa_state->nodes[i].node_mem, i,
- MEM_AFFINITY_ENABLED);
- mem_base += machine->numa_state->nodes[i].node_mem;
- }
+
+ 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),
- nb_numa_nodes - 1,
+ 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);
+ build_spcr(table_data, linker, &serial, 2, lvms->oem_id,
+ lvms->oem_table_id);
+}
+
typedef
struct AcpiBuildState {
/* Copy of table in RAM (for patching). */
@@ -241,23 +309,27 @@ struct AcpiBuildState {
MemoryRegion *linker_mr;
} AcpiBuildState;
-static void build_uart_device_aml(Aml *table)
+static void build_uart_device_aml(Aml *table, int index)
{
Aml *dev;
Aml *crs;
Aml *pkg0, *pkg1, *pkg2;
- uint32_t uart_irq = VIRT_UART_IRQ;
-
- Aml *scope = aml_scope("_SB");
- dev = aml_device("COMA");
+ 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(0)));
+ 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, VIRT_UART_BASE, VIRT_UART_BASE + VIRT_UART_SIZE - 1,
+ 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));
@@ -279,23 +351,36 @@ static void
build_la_ged_aml(Aml *dsdt, MachineState *machine)
{
uint32_t event;
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
+ CPUHotplugFeatures opts;
build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
- HOTPLUG_HANDLER(lams->acpi_ged),
+ HOTPLUG_HANDLER(lvms->acpi_ged),
VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
VIRT_GED_EVT_ADDR);
- event = object_property_get_uint(OBJECT(lams->acpi_ged),
+ 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, NULL,
+ VIRT_GED_CPUHP_ADDR, "\\_SB",
+ NULL, AML_SYSTEM_MEMORY);
+ }
+
acpi_dsdt_add_power_button(dsdt);
}
-static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams)
+static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms)
{
struct GPEXConfig cfg = {
.mmio64.base = VIRT_PCI_MEM_BASE,
@@ -305,13 +390,13 @@ static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams)
.ecam.base = VIRT_PCI_CFG_BASE,
.ecam.size = VIRT_PCI_CFG_SIZE,
.irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS,
- .bus = lams->pci_bus,
+ .bus = lvms->pci_bus,
};
acpi_dsdt_add_gpex(scope, &cfg);
}
-static void build_flash_aml(Aml *scope, LoongArchMachineState *lams)
+static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms)
{
Aml *dev, *crs;
MemoryRegion *flash_mem;
@@ -322,11 +407,11 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams)
hwaddr flash1_base;
hwaddr flash1_size;
- flash_mem = pflash_cfi01_get_memory(lams->flash[0]);
+ 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(lams->flash[1]);
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
flash1_base = flash_mem->addr;
flash1_size = memory_region_size(flash_mem);
@@ -352,7 +437,7 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams)
}
#ifdef CONFIG_TPM
-static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms)
+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;
@@ -390,19 +475,21 @@ static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms)
static void
build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
{
+ int i;
Aml *dsdt, *scope, *pkg;
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
- AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id,
- .oem_table_id = lams->oem_table_id };
+ 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();
- build_uart_device_aml(dsdt);
- build_pci_device_aml(dsdt, lams);
+ 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, lams);
+ build_flash_aml(dsdt, lvms);
#ifdef CONFIG_TPM
- acpi_dsdt_add_tpm(dsdt, lams);
+ acpi_dsdt_add_tpm(dsdt, lvms);
#endif
/* System State Package */
scope = aml_scope("\\");
@@ -421,7 +508,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
GArray *table_offsets;
AcpiFadtData fadt_data;
unsigned facs, rsdt, dsdt;
@@ -455,28 +542,30 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
fadt_data.dsdt_tbl_offset = &dsdt;
fadt_data.xdsdt_tbl_offset = &dsdt;
build_fadt(tables_blob, tables->linker, &fadt_data,
- lams->oem_id, lams->oem_table_id);
+ lvms->oem_id, lvms->oem_table_id);
acpi_add_table(table_offsets, tables_blob);
- build_madt(tables_blob, tables->linker, lams);
+ build_madt(tables_blob, tables->linker, lvms);
acpi_add_table(table_offsets, tables_blob);
build_pptt(tables_blob, tables->linker, machine,
- lams->oem_id, lams->oem_table_id);
+ 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, lams->oem_id,
- lams->oem_table_id);
+ 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,
- lams->oem_id, lams->oem_table_id);
+ lvms->oem_id, lvms->oem_table_id);
}
}
@@ -486,8 +575,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
.base = cpu_to_le64(VIRT_PCI_CFG_BASE),
.size = cpu_to_le64(VIRT_PCI_CFG_SIZE),
};
- build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id,
- lams->oem_table_id);
+ build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id,
+ lvms->oem_table_id);
}
#ifdef CONFIG_TPM
@@ -495,8 +584,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
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, lams->oem_id,
- lams->oem_table_id);
+ tables->tcpalog, lvms->oem_id,
+ lvms->oem_table_id);
}
#endif
/* Add tables supplied by user (if any) */
@@ -510,13 +599,13 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
/* RSDT is pointed to by RSDP */
rsdt = tables_blob->len;
build_rsdt(tables_blob, tables->linker, table_offsets,
- lams->oem_id, lams->oem_table_id);
+ lvms->oem_id, lvms->oem_table_id);
/* RSDP is in FSEG memory, so allocate it separately */
{
AcpiRsdpData rsdp_data = {
.revision = 0,
- .oem_id = lams->oem_id,
+ .oem_id = lvms->oem_id,
.xsdt_tbl_offset = NULL,
.rsdt_tbl_offset = &rsdt,
};
@@ -593,17 +682,25 @@ static const VMStateDescription vmstate_acpi_build = {
},
};
-void loongarch_acpi_setup(LoongArchMachineState *lams)
+static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
+{
+ if (lvms->acpi == ON_OFF_AUTO_OFF) {
+ return false;
+ }
+ return true;
+}
+
+void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
{
AcpiBuildTables tables;
AcpiBuildState *build_state;
- if (!lams->fw_cfg) {
+ if (!lvms->fw_cfg) {
ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
return;
}
- if (!loongarch_is_acpi_enabled(lams)) {
+ if (!loongarch_is_acpi_enabled(lvms)) {
ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
return;
}
@@ -611,7 +708,7 @@ void loongarch_acpi_setup(LoongArchMachineState *lams)
build_state = g_malloc0(sizeof *build_state);
acpi_build_tables_init(&tables);
- acpi_build(&tables, MACHINE(lams));
+ acpi_build(&tables, MACHINE(lvms));
/* Now expose it all to Guest */
build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
@@ -627,6 +724,9 @@ void loongarch_acpi_setup(LoongArchMachineState *lams)
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);
diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c
new file mode 100644
index 0000000000000000000000000000000000000000..53dcefbb55a7a932b5cc0a5b26c9a32886fb07a6
--- /dev/null
+++ b/hw/loongarch/boot.c
@@ -0,0 +1,338 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch boot helper functions.
+ *
+ * Copyright (c) 2023 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "target/loongarch/cpu.h"
+#include "hw/loongarch/virt.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "qemu/error-report.h"
+#include "sysemu/reset.h"
+#include "sysemu/qtest.h"
+
+struct memmap_entry *memmap_table;
+unsigned memmap_entries;
+
+ram_addr_t initrd_offset;
+uint64_t initrd_size;
+
+static const unsigned int slave_boot_code[] = {
+ /* Configure reset ebase. */
+ 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */
+
+ /* Disable interrupt. */
+ 0x0380100c, /* ori $t0, $zero,0x4 */
+ 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */
+
+ /* Clear mailbox. */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
+ 0x06481da0, /* iocsrwr.d $zero, $t1 */
+
+ /* Enable IPI interrupt. */
+ 0x1400002c, /* lu12i.w $t0, 1(0x1) */
+ 0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */
+ 0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */
+ 0x064819ac, /* iocsrwr.w $t0, $t1 */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
+
+ /* Wait for wakeup <.L11>: */
+ 0x06488000, /* idle 0x0 */
+ 0x03400000, /* andi $zero, $zero, 0x0 */
+ 0x064809ac, /* iocsrrd.w $t0, $t1 */
+ 0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */
+
+ /* Read and clear IPI interrupt. */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x064809ac, /* iocsrrd.w $t0, $t1 */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */
+ 0x064819ac, /* iocsrwr.w $t0, $t1 */
+
+ /* Disable IPI interrupt. */
+ 0x1400002c, /* lu12i.w $t0, 1(0x1) */
+ 0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */
+
+ /* Read mail buf and jump to specified entry */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
+ 0x06480dac, /* iocsrrd.d $t0, $t1 */
+ 0x00150181, /* move $ra, $t0 */
+ 0x4c000020, /* jirl $zero, $ra,0 */
+};
+
+static inline void *guidcpy(void *dst, const void *src)
+{
+ return memcpy(dst, src, sizeof(efi_guid_t));
+}
+
+static void init_efi_boot_memmap(struct efi_system_table *systab,
+ void *p, void *start)
+{
+ unsigned i;
+ struct efi_boot_memmap *boot_memmap = p;
+ efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID;
+
+ /* efi_configuration_table 1 */
+ guidcpy(&systab->tables[0].guid, &tbl_guid);
+ systab->tables[0].table = (struct efi_configuration_table *)(p - start);
+ systab->nr_tables = 1;
+
+ boot_memmap->desc_size = sizeof(efi_memory_desc_t);
+ boot_memmap->desc_ver = 1;
+ boot_memmap->map_size = 0;
+
+ efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap);
+ for (i = 0; i < memmap_entries; i++) {
+ map = (void *)boot_memmap + sizeof(*map);
+ map[i].type = memmap_table[i].type;
+ map[i].phys_addr = ROUND_UP(memmap_table[i].address, 64 * KiB);
+ map[i].num_pages = ROUND_DOWN(memmap_table[i].address +
+ memmap_table[i].length - map[i].phys_addr, 64 * KiB);
+ p += sizeof(efi_memory_desc_t);
+ }
+}
+
+static void init_efi_initrd_table(struct efi_system_table *systab,
+ void *p, void *start)
+{
+ efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID;
+ struct efi_initrd *initrd_table = p;
+
+ /* efi_configuration_table 2 */
+ guidcpy(&systab->tables[1].guid, &tbl_guid);
+ systab->tables[1].table = (struct efi_configuration_table *)(p - start);
+ systab->nr_tables = 2;
+
+ initrd_table->base = initrd_offset;
+ initrd_table->size = initrd_size;
+}
+
+static void init_efi_fdt_table(struct efi_system_table *systab)
+{
+ efi_guid_t tbl_guid = DEVICE_TREE_GUID;
+
+ /* efi_configuration_table 3 */
+ guidcpy(&systab->tables[2].guid, &tbl_guid);
+ systab->tables[2].table = (void *)FDT_BASE;
+ systab->nr_tables = 3;
+}
+
+static void init_systab(struct loongarch_boot_info *info, void *p, void *start)
+{
+ void *bp_tables_start;
+ struct efi_system_table *systab = p;
+
+ info->a2 = p - start;
+
+ systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE;
+ systab->hdr.revision = EFI_SPECIFICATION_VERSION;
+ systab->hdr.revision = sizeof(struct efi_system_table),
+ systab->fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8;
+ systab->runtime = 0;
+ systab->boottime = 0;
+ systab->nr_tables = 0;
+
+ p += ROUND_UP(sizeof(struct efi_system_table), 64 * KiB);
+
+ systab->tables = p;
+ bp_tables_start = p;
+
+ init_efi_boot_memmap(systab, p, start);
+ p += ROUND_UP(sizeof(struct efi_boot_memmap) +
+ sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB);
+ init_efi_initrd_table(systab, p, start);
+ p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB);
+ init_efi_fdt_table(systab);
+
+ systab->tables = (struct efi_configuration_table *)(bp_tables_start - start);
+}
+
+static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start)
+{
+ hwaddr cmdline_addr = p - start;
+
+ info->a0 = 1;
+ info->a1 = cmdline_addr;
+
+ g_strlcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE);
+}
+
+static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
+{
+ return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS);
+}
+
+static int64_t load_kernel_info(struct loongarch_boot_info *info)
+{
+ uint64_t kernel_entry, kernel_low, kernel_high;
+ ssize_t kernel_size;
+
+ kernel_size = load_elf(info->kernel_filename, NULL,
+ cpu_loongarch_virt_to_phys, NULL,
+ &kernel_entry, &kernel_low,
+ &kernel_high, NULL, 0,
+ EM_LOONGARCH, 1, 0);
+
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s': %s",
+ info->kernel_filename,
+ load_elf_strerror(kernel_size));
+ exit(1);
+ }
+
+ if (info->initrd_filename) {
+ initrd_size = get_image_size(info->initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB);
+
+ if (initrd_offset + initrd_size > info->ram_size) {
+ error_report("memory too small for initial ram disk '%s'",
+ info->initrd_filename);
+ exit(1);
+ }
+
+ initrd_size = load_image_targphys(info->initrd_filename, initrd_offset,
+ info->ram_size - initrd_offset);
+ }
+
+ if (initrd_size == (target_ulong)-1) {
+ error_report("could not load initial ram disk '%s'",
+ info->initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_size = 0;
+ }
+
+ return kernel_entry;
+}
+
+void reset_load_elf(void *opaque)
+{
+ LoongArchCPU *cpu = opaque;
+ CPULoongArchState *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+ if (env->load_elf) {
+ if (cpu == LOONGARCH_CPU(first_cpu)) {
+ env->gpr[4] = env->boot_info->a0;
+ env->gpr[5] = env->boot_info->a1;
+ env->gpr[6] = env->boot_info->a2;
+ }
+ cpu_set_pc(CPU(cpu), env->elf_address);
+ }
+}
+
+static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info,
+ FWCfgState *fw_cfg)
+{
+ /*
+ * Expose the kernel, the command line, and the initrd in fw_cfg.
+ * We don't process them here at all, it's all left to the
+ * firmware.
+ */
+ load_image_to_fw_cfg(fw_cfg,
+ FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
+ info->kernel_filename,
+ false);
+
+ if (info->initrd_filename) {
+ load_image_to_fw_cfg(fw_cfg,
+ FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
+ info->initrd_filename, false);
+ }
+
+ if (info->kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(info->kernel_cmdline) + 1);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
+ info->kernel_cmdline);
+ }
+}
+
+static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms,
+ struct loongarch_boot_info *info)
+{
+ fw_cfg_add_kernel_info(info, lvms->fw_cfg);
+}
+
+static void init_boot_rom(struct loongarch_boot_info *info, void *p)
+{
+ void *start = p;
+
+ init_cmdline(info, p, start);
+ p += COMMAND_LINE_SIZE;
+
+ init_systab(info, p, start);
+}
+
+static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info)
+{
+ void *p, *bp;
+ int64_t kernel_addr = VIRT_FLASH0_BASE;
+ LoongArchCPU *lacpu;
+ CPUState *cs;
+
+ if (info->kernel_filename) {
+ kernel_addr = load_kernel_info(info);
+ } else {
+ if(!qtest_enabled()) {
+ warn_report("No kernel provided, booting from flash drive.");
+ }
+ }
+
+ /* Load cmdline and system tables at [0 - 1 MiB] */
+ p = g_malloc0(1 * MiB);
+ bp = p;
+ init_boot_rom(info, p);
+ rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory);
+
+ /* Load slave boot code at pflash0 . */
+ void *boot_code = g_malloc0(VIRT_FLASH0_SIZE);
+ memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code));
+ rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE);
+
+ CPU_FOREACH(cs) {
+ lacpu = LOONGARCH_CPU(cs);
+ lacpu->env.load_elf = true;
+ if (cs == first_cpu) {
+ lacpu->env.elf_address = kernel_addr;
+ } else {
+ lacpu->env.elf_address = VIRT_FLASH0_BASE;
+ }
+ lacpu->env.boot_info = info;
+ }
+
+ g_free(boot_code);
+ g_free(bp);
+}
+
+void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info)
+{
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms);
+ int i;
+
+ /* register reset function */
+ for (i = 0; i < ms->smp.cpus; i++) {
+ qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i)));
+ }
+
+ info->kernel_filename = ms->kernel_filename;
+ info->kernel_cmdline = ms->kernel_cmdline;
+ info->initrd_filename = ms->initrd_filename;
+
+ if (lvms->bios_loaded) {
+ loongarch_firmware_boot(lvms, info);
+ } else {
+ loongarch_direct_kernel_boot(info);
+ }
+}
diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c
index f15a17416c4814100ac9d45ff23b0abacc856eed..35aeb2decbd1ed87da6689672b134b6684321d62 100644
--- a/hw/loongarch/fw_cfg.c
+++ b/hw/loongarch/fw_cfg.c
@@ -17,7 +17,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device,
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
-FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms)
+FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms)
{
FWCfgState *fw_cfg;
int max_cpus = ms->smp.max_cpus;
diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h
index 7c0de4db4ad44a582fddd1cf3e44e140b2a0d8b3..27ee68286ed115f5e3f99b27c8d3ad2ec1d1550f 100644
--- a/hw/loongarch/fw_cfg.h
+++ b/hw/loongarch/fw_cfg.h
@@ -11,5 +11,5 @@
#include "hw/boards.h"
#include "hw/nvram/fw_cfg.h"
-FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms);
+FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms);
#endif
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
index c0421502abab9a8ca9c3751b37969d9e6a8caf3d..d306d82c2ee0e6a671f8180c6554a7185d47c98e 100644
--- a/hw/loongarch/meson.build
+++ b/hw/loongarch/meson.build
@@ -1,6 +1,7 @@
loongarch_ss = ss.source_set()
loongarch_ss.add(files(
'fw_cfg.c',
+ 'boot.c',
))
loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt])
loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c'))
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index eca3b945814e007988f1293d53cf6dbccb62038c..0c24e632bb84d261b0982a8528b946bccf6e7b42 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -10,6 +10,7 @@
#include "qapi/error.h"
#include "hw/boards.h"
#include "hw/char/serial.h"
+#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "sysemu/qtest.h"
#include "sysemu/runstate.h"
@@ -46,19 +47,13 @@
#include "sysemu/tpm.h"
#include "sysemu/block-backend.h"
#include "hw/block/flash.h"
+#include "hw/virtio/virtio-iommu.h"
#include "qemu/error-report.h"
+#include "qemu/guest-random.h"
-
-struct loaderparams {
- uint64_t ram_size;
- const char *kernel_filename;
- const char *kernel_cmdline;
- const char *initrd_filename;
-};
-
-static bool virt_is_veiointc_enabled(LoongArchMachineState *lams)
+static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms)
{
- if (lams->veiointc == ON_OFF_AUTO_OFF) {
+ if (lvms->veiointc == ON_OFF_AUTO_OFF) {
return false;
}
return true;
@@ -67,8 +62,8 @@ static bool virt_is_veiointc_enabled(LoongArchMachineState *lams)
static void virt_get_veiointc(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
- OnOffAuto veiointc = lams->veiointc;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
+ OnOffAuto veiointc = lvms->veiointc;
visit_type_OnOffAuto(v, name, &veiointc, errp);
}
@@ -76,12 +71,12 @@ static void virt_get_veiointc(Object *obj, Visitor *v, const char *name,
static void virt_set_veiointc(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
- visit_type_OnOffAuto(v, name, &lams->veiointc, errp);
+ visit_type_OnOffAuto(v, name, &lvms->veiointc, errp);
}
-static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams,
+static PFlashCFI01 *virt_flash_create1(LoongArchVirtMachineState *lvms,
const char *name,
const char *alias_prop_name)
{
@@ -96,16 +91,16 @@ static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams,
qdev_prop_set_uint16(dev, "id2", 0x00);
qdev_prop_set_uint16(dev, "id3", 0x00);
qdev_prop_set_string(dev, "name", name);
- object_property_add_child(OBJECT(lams), name, OBJECT(dev));
- object_property_add_alias(OBJECT(lams), alias_prop_name,
+ object_property_add_child(OBJECT(lvms), name, OBJECT(dev));
+ object_property_add_alias(OBJECT(lvms), alias_prop_name,
OBJECT(dev), "drive");
return PFLASH_CFI01(dev);
}
-static void virt_flash_create(LoongArchMachineState *lams)
+static void virt_flash_create(LoongArchVirtMachineState *lvms)
{
- lams->flash[0] = virt_flash_create1(lams, "virt.flash0", "pflash0");
- lams->flash[1] = virt_flash_create1(lams, "virt.flash1", "pflash1");
+ lvms->flash[0] = virt_flash_create1(lvms, "virt.flash0", "pflash0");
+ lvms->flash[1] = virt_flash_create1(lvms, "virt.flash1", "pflash1");
}
static void virt_flash_map1(PFlashCFI01 *flash,
@@ -131,19 +126,114 @@ static void virt_flash_map1(PFlashCFI01 *flash,
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
}
-static void virt_flash_map(LoongArchMachineState *lams,
+static void virt_flash_map(LoongArchVirtMachineState *lvms,
MemoryRegion *sysmem)
{
- PFlashCFI01 *flash0 = lams->flash[0];
- PFlashCFI01 *flash1 = lams->flash[1];
+ PFlashCFI01 *flash0 = lvms->flash[0];
+ PFlashCFI01 *flash1 = lvms->flash[1];
virt_flash_map1(flash0, VIRT_FLASH0_BASE, VIRT_FLASH0_SIZE, sysmem);
virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem);
}
-static void fdt_add_flash_node(LoongArchMachineState *lams)
+static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms,
+ uint32_t *cpuintc_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+
+ *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/cpuic");
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,cpu-interrupt-controller");
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
+ g_free(nodename);
+}
+
+static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms,
+ uint32_t *cpuintc_phandle,
+ uint32_t *eiointc_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr extioi_base = APIC_BASE;
+ hwaddr extioi_size = EXTIOI_SIZE;
+
+ *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,ls2k2000-eiointc");
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *cpuintc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0,
+ extioi_base, 0x0, extioi_size);
+ g_free(nodename);
+}
+
+static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms,
+ uint32_t *eiointc_phandle,
+ uint32_t *pch_pic_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr pch_pic_base = VIRT_PCH_REG_BASE;
+ hwaddr pch_pic_size = VIRT_PCH_REG_SIZE;
+
+ *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,pch-pic-1.0");
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0,
+ pch_pic_base, 0, pch_pic_size);
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *eiointc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0);
+ g_free(nodename);
+}
+
+static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms,
+ uint32_t *eiointc_phandle,
+ uint32_t *pch_msi_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW;
+ hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE;
+
+ *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,pch-msi-1.0");
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg",
+ 0, pch_msi_base,
+ 0, pch_msi_size);
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *eiointc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec",
+ VIRT_PCH_PIC_IRQ_NUM);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs",
+ EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM);
+ g_free(nodename);
+}
+
+static void fdt_add_flash_node(LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
char *nodename;
MemoryRegion *flash_mem;
@@ -153,11 +243,11 @@ static void fdt_add_flash_node(LoongArchMachineState *lams)
hwaddr flash1_base;
hwaddr flash1_size;
- flash_mem = pflash_cfi01_get_memory(lams->flash[0]);
+ 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(lams->flash[1]);
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
flash1_base = flash_mem->addr;
flash1_size = memory_region_size(flash_mem);
@@ -171,41 +261,91 @@ static void fdt_add_flash_node(LoongArchMachineState *lams)
g_free(nodename);
}
-static void fdt_add_rtc_node(LoongArchMachineState *lams)
+static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle)
{
char *nodename;
hwaddr base = VIRT_RTC_REG_BASE;
hwaddr size = VIRT_RTC_LEN;
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/rtc@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "loongson,ls7a-rtc");
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,ls7a-rtc");
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
+ VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *pch_pic_phandle);
g_free(nodename);
}
-static void fdt_add_uart_node(LoongArchMachineState *lams)
+static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms)
+{
+ char *name;
+ uint32_t ged_handle;
+ MachineState *ms = MACHINE(lvms);
+ hwaddr base = VIRT_GED_REG_ADDR;
+ hwaddr size = ACPI_GED_REG_COUNT;
+
+ ged_handle = qemu_fdt_alloc_phandle(ms->fdt);
+ name = g_strdup_printf("/ged@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, name);
+ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon");
+ qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size);
+ /* 8 bit registers */
+ qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0);
+ qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1);
+ qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle);
+ ged_handle = qemu_fdt_get_phandle(ms->fdt, name);
+ g_free(name);
+
+ name = g_strdup_printf("/reboot");
+ qemu_fdt_add_subnode(ms->fdt, name);
+ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot");
+ qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
+ qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET);
+ qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE);
+ g_free(name);
+
+ name = g_strdup_printf("/poweroff");
+ qemu_fdt_add_subnode(ms->fdt, name);
+ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff");
+ qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
+ qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL);
+ qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN |
+ (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS));
+ g_free(name);
+}
+
+static void fdt_add_uart_node(LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle, hwaddr base,
+ int irq, bool chosen)
{
char *nodename;
- hwaddr base = VIRT_UART_BASE;
hwaddr size = VIRT_UART_SIZE;
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/serial@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a");
qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size);
qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000);
- qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
+ if (chosen)
+ qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *pch_pic_phandle);
g_free(nodename);
}
-static void create_fdt(LoongArchMachineState *lams)
+static void create_fdt(LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
+ uint8_t rng_seed[32];
- ms->fdt = create_device_tree(&lams->fdt_size);
+ ms->fdt = create_device_tree(&lvms->fdt_size);
if (!ms->fdt) {
error_report("create_device_tree() failed");
exit(1);
@@ -217,12 +357,16 @@ static void create_fdt(LoongArchMachineState *lams)
qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2);
qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2);
qemu_fdt_add_subnode(ms->fdt, "/chosen");
+
+ /* Pass seed to RNG */
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+ qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
}
-static void fdt_add_cpu_nodes(const LoongArchMachineState *lams)
+static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms)
{
int num;
- const MachineState *ms = MACHINE(lams);
+ const MachineState *ms = MACHINE(lvms);
int smp_cpus = ms->smp.cpus;
qemu_fdt_add_subnode(ms->fdt, "/cpus");
@@ -276,11 +420,11 @@ static void fdt_add_cpu_nodes(const LoongArchMachineState *lams)
}
}
-static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams)
+static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms)
{
char *nodename;
hwaddr base = VIRT_FWCFG_BASE;
- const MachineState *ms = MACHINE(lams);
+ const MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
@@ -292,7 +436,62 @@ static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams)
g_free(nodename);
}
-static void fdt_add_pcie_node(const LoongArchMachineState *lams)
+static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
+ char *nodename,
+ uint32_t *pch_pic_phandle)
+{
+ int pin, dev;
+ uint32_t irq_map_stride = 0;
+ uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {};
+ uint32_t *irq_map = full_irq_map;
+ const MachineState *ms = MACHINE(lvms);
+
+ /* This code creates a standard swizzle of interrupts such that
+ * each device's first interrupt is based on it's PCI_SLOT number.
+ * (See pci_swizzle_map_irq_fn())
+ *
+ * We only need one entry per interrupt in the table (not one per
+ * possible slot) seeing the interrupt-map-mask will allow the table
+ * to wrap to any number of devices.
+ */
+
+ for (dev = 0; dev < GPEX_NUM_IRQS; dev++) {
+ int devfn = dev * 0x8;
+
+ for (pin = 0; pin < GPEX_NUM_IRQS; pin++) {
+ int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS);
+ int i = 0;
+
+ /* Fill PCI address cells */
+ irq_map[i] = cpu_to_be32(devfn << 8);
+ i += 3;
+
+ /* Fill PCI Interrupt cells */
+ irq_map[i] = cpu_to_be32(pin + 1);
+ i += 1;
+
+ /* Fill interrupt controller phandle and cells */
+ irq_map[i++] = cpu_to_be32(*pch_pic_phandle);
+ irq_map[i++] = cpu_to_be32(irq_nr);
+
+ if (!irq_map_stride) {
+ irq_map_stride = i;
+ }
+ irq_map += irq_map_stride;
+ }
+ }
+
+
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map,
+ GPEX_NUM_IRQS * GPEX_NUM_IRQS *
+ irq_map_stride * sizeof(uint32_t));
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask",
+ 0x1800, 0, 0, 0x7);
+}
+
+static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle,
+ uint32_t *pch_msi_phandle)
{
char *nodename;
hwaddr base_mmio = VIRT_PCI_MEM_BASE;
@@ -303,7 +502,7 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams)
hwaddr size_pcie = VIRT_PCI_CFG_SIZE;
hwaddr base = base_pcie;
- const MachineState *ms = MACHINE(lams);
+ const MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/pcie@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
@@ -323,34 +522,11 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams)
2, base_pio, 2, size_pio,
1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
2, base_mmio, 2, size_mmio);
- g_free(nodename);
-}
-
-static void fdt_add_irqchip_node(LoongArchMachineState *lams)
-{
- MachineState *ms = MACHINE(lams);
- char *nodename;
- uint32_t irqchip_phandle;
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map",
+ 0, *pch_msi_phandle, 0, 0x10000);
- irqchip_phandle = qemu_fdt_alloc_phandle(ms->fdt);
- qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", irqchip_phandle);
+ fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle);
- nodename = g_strdup_printf("/intc@%lx", VIRT_IOAPIC_REG_BASE);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3);
- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2);
- qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0);
-
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- "loongarch,ls7a");
-
- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
- 2, VIRT_IOAPIC_REG_BASE,
- 2, PCH_PIC_ROUTE_ENTRY_OFFSET);
-
- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", irqchip_phandle);
g_free(nodename);
}
@@ -371,15 +547,57 @@ static void fdt_add_memory_node(MachineState *ms,
g_free(nodename);
}
-static void virt_build_smbios(LoongArchMachineState *lams)
+static void fdt_add_memory_nodes(MachineState *ms)
+{
+ hwaddr base, size, ram_size, gap;
+ int i, nb_numa_nodes, nodes;
+ NodeInfo *numa_info;
+
+ ram_size = ms->ram_size;
+ base = VIRT_LOWMEM_BASE;
+ gap = VIRT_LOWMEM_SIZE;
+ nodes = nb_numa_nodes = ms->numa_state->num_nodes;
+ numa_info = ms->numa_state->nodes;
+ if (!nodes) {
+ nodes = 1;
+ }
+
+ for (i = 0; i < nodes; i++) {
+ if (nb_numa_nodes) {
+ size = numa_info[i].node_mem;
+ } else {
+ size = ram_size;
+ }
+
+ /*
+ * memory for the node splited into two part
+ * lowram: [base, +gap)
+ * highram: [VIRT_HIGHMEM_BASE, +(len - gap))
+ */
+ if (size >= gap) {
+ fdt_add_memory_node(ms, base, gap, i);
+ size -= gap;
+ base = VIRT_HIGHMEM_BASE;
+ gap = ram_size - VIRT_LOWMEM_SIZE;
+ }
+
+ if (size) {
+ fdt_add_memory_node(ms, base, size, i);
+ base += size;
+ gap -= size;
+ }
+ }
+}
+
+static void virt_build_smbios(LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ MachineState *ms = MACHINE(lvms);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
uint8_t *smbios_tables, *smbios_anchor;
size_t smbios_tables_len, smbios_anchor_len;
const char *product = "QEMU Virtual Machine";
- if (!lams->fw_cfg) {
+ if (!lvms->fw_cfg) {
return;
}
@@ -390,39 +608,29 @@ static void virt_build_smbios(LoongArchMachineState *lams)
&smbios_anchor, &smbios_anchor_len, &error_fatal);
if (smbios_anchor) {
- fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-tables",
+ fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-tables",
smbios_tables, smbios_tables_len);
- fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor",
+ fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-anchor",
smbios_anchor, smbios_anchor_len);
}
}
-static void virt_machine_done(Notifier *notifier, void *data)
+static void virt_done(Notifier *notifier, void *data)
{
- LoongArchMachineState *lams = container_of(notifier,
- LoongArchMachineState, machine_done);
- virt_build_smbios(lams);
- loongarch_acpi_setup(lams);
+ LoongArchVirtMachineState *lvms = container_of(notifier,
+ LoongArchVirtMachineState, machine_done);
+ virt_build_smbios(lvms);
+ loongarch_acpi_setup(lvms);
}
static void virt_powerdown_req(Notifier *notifier, void *opaque)
{
- LoongArchMachineState *s = container_of(notifier,
- LoongArchMachineState, powerdown_notifier);
+ LoongArchVirtMachineState *s;
+ s = container_of(notifier, LoongArchVirtMachineState, powerdown_notifier);
acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS);
}
-struct memmap_entry {
- uint64_t address;
- uint64_t length;
- uint32_t type;
- uint32_t reserved;
-};
-
-static struct memmap_entry *memmap_table;
-static unsigned memmap_entries;
-
static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type)
{
/* Ensure there are no duplicate entries. */
@@ -439,40 +647,22 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type)
memmap_entries++;
}
-static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
-{
- return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS);
-}
-
-static int64_t load_kernel_info(const struct loaderparams *loaderparams)
-{
- uint64_t kernel_entry, kernel_low, kernel_high;
- ssize_t kernel_size;
-
- kernel_size = load_elf(loaderparams->kernel_filename, NULL,
- cpu_loongarch_virt_to_phys, NULL,
- &kernel_entry, &kernel_low,
- &kernel_high, NULL, 0,
- EM_LOONGARCH, 1, 0);
-
- if (kernel_size < 0) {
- error_report("could not load kernel '%s': %s",
- loaderparams->kernel_filename,
- load_elf_strerror(kernel_size));
- exit(1);
- }
- return kernel_entry;
-}
-
-static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams)
+static DeviceState *create_acpi_ged(DeviceState *pch_pic,
+ LoongArchVirtMachineState *lvms)
{
DeviceState *dev;
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
uint32_t event = ACPI_GED_PWR_DOWN_EVT;
if (ms->ram_slots) {
event |= ACPI_GED_MEM_HOTPLUG_EVT;
}
+
+ if (mc->has_hotpluggable_cpus) {
+ event |= ACPI_GED_CPU_HOTPLUG_EVT;
+ }
+
dev = qdev_new(TYPE_ACPI_GED);
qdev_prop_set_uint32(dev, "ged-event", event);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
@@ -484,6 +674,10 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState
/* ged regs used for reset and power down */
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR);
+ if (mc->has_hotpluggable_cpus) {
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, VIRT_GED_CPUHP_ADDR);
+ }
+
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE));
return dev;
@@ -514,9 +708,12 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic)
return dev;
}
-static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *lams)
+static void virt_devices_init(DeviceState *pch_pic,
+ LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle,
+ uint32_t *pch_msi_phandle)
{
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
DeviceState *gpex_dev;
SysBusDevice *d;
PCIBus *pci_bus;
@@ -528,7 +725,7 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *
d = SYS_BUS_DEVICE(gpex_dev);
sysbus_realize_and_unref(d, &error_fatal);
pci_bus = PCI_HOST_BRIDGE(gpex_dev)->bus;
- lams->pci_bus = pci_bus;
+ lvms->pci_bus = pci_bus;
/* Map only part size_ecam bytes of ECAM space */
ecam_alias = g_new0(MemoryRegion, 1);
@@ -560,11 +757,21 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *
gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i);
}
- serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0,
- qdev_get_gpio_in(pch_pic,
- VIRT_UART_IRQ - VIRT_GSI_BASE),
- 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
- fdt_add_uart_node(lams);
+ /* Add pcie node */
+ fdt_add_pcie_node(lvms, pch_pic_phandle, pch_msi_phandle);
+
+ /*
+ * Create uart fdt node in reverse order so that they appear
+ * in the finished device tree lowest address first
+ */
+ for (i = VIRT_UART_COUNT; i --> 0;) {
+ hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE;
+ int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE;
+ serial_mm_init(get_system_memory(), base, 0,
+ qdev_get_gpio_in(pch_pic, irq),
+ 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN);
+ fdt_add_uart_node(lvms, pch_pic_phandle, base, irq, i == 0);
+ }
/* Network init */
for (i = 0; i < nb_nics; i++) {
@@ -579,17 +786,18 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *
sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE,
qdev_get_gpio_in(pch_pic,
VIRT_RTC_IRQ - VIRT_GSI_BASE));
- fdt_add_rtc_node(lams);
+ fdt_add_rtc_node(lvms, pch_pic_phandle);
+ fdt_add_ged_reset(lvms);
/* acpi ged */
- lams->acpi_ged = create_acpi_ged(pch_pic, lams);
+ lvms->acpi_ged = create_acpi_ged(pch_pic, lvms);
/* platform bus */
- lams->platform_bus_dev = create_platform_bus(pch_pic);
+ lvms->platform_bus_dev = create_platform_bus(pch_pic);
}
-static void loongarch_irq_init(LoongArchMachineState *lams)
+static void virt_irq_init(LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
DeviceState *pch_pic, *pch_msi, *cpudev;
DeviceState *ipi, *extioi;
SysBusDevice *d;
@@ -597,6 +805,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
CPULoongArchState *env;
CPUState *cpu_state;
int cpu, pin, i, start, num;
+ uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle;
/*
* The connection of interrupts:
@@ -620,112 +829,147 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
* +--------+ +---------+ +---------+
*/
- /* Create IPI device */
- ipi = qdev_new(TYPE_LOONGARCH_IPI);
- qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus);
- sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal);
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ ipi = qdev_new(TYPE_KVM_LOONGARCH_IPI);
+ qdev_prop_set_int32(ipi, "num-cpu", ms->smp.max_cpus);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal);
+ } else {
+ ipi = qdev_new(TYPE_LOONGARCH_IPI);
+ qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.max_cpus);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal);
+
+ /* IPI iocsr memory region */
+ memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0));
+ memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1));
+ for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
+ cpu_state = qemu_get_cpu(cpu);
+ cpudev = DEVICE(cpu_state);
+
+ /* connect ipi irq to cpu irq */
+ qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI));
+ }
+ }
- /* IPI iocsr memory region */
- memory_region_add_subregion(&lams->system_iocsr, SMP_IPI_MAILBOX,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0));
- memory_region_add_subregion(&lams->system_iocsr, MAIL_SEND_ADDR,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1));
+ /* Add cpu interrupt-controller */
+ fdt_add_cpuic_node(lvms, &cpuintc_phandle);
for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
cpu_state = qemu_get_cpu(cpu);
cpudev = DEVICE(cpu_state);
lacpu = LOONGARCH_CPU(cpu_state);
env = &(lacpu->env);
- env->address_space_iocsr = &lams->as_iocsr;
-
- /* connect ipi irq to cpu irq */
- qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI));
+ env->address_space_iocsr = &lvms->as_iocsr;
env->ipistate = ipi;
}
+ lvms->ipi = ipi;
+
/* Create EXTIOI device */
- extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
- qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus);
- if (virt_is_veiointc_enabled(lams)) {
- qdev_prop_set_bit(extioi, "has-virtualization-extension", true);
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ extioi = qdev_new(TYPE_KVM_LOONGARCH_EXTIOI);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal);
+ } else {
+ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
+ qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.max_cpus);
+ if (virt_is_veiointc_enabled(lvms)) {
+ qdev_prop_set_bit(extioi, "has-virtualization-extension", true);
+ }
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal);
+ memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0));
+ if (virt_is_veiointc_enabled(lvms)) {
+ memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1));
+ }
+ /*
+ * connect ext irq to the cpu irq
+ * cpu_pin[9:2] <= intc_pin[7:0]
+ */
+ for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
+ cpudev = DEVICE(qemu_get_cpu(cpu));
+ for (pin = 0; pin < LS3A_INTC_IP; pin++) {
+ qdev_connect_gpio_out(extioi, (cpu * 8 + pin),
+ qdev_get_gpio_in(cpudev, pin + 2));
+ }
+ }
}
- sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal);
- memory_region_add_subregion(&lams->system_iocsr, APIC_BASE,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0));
- if (virt_is_veiointc_enabled(lams)) {
- memory_region_add_subregion(&lams->system_iocsr, EXTIOI_VIRT_BASE,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1));
- }
- lams->extioi = extioi;
+ lvms->extioi = extioi;
- /*
- * connect ext irq to the cpu irq
- * cpu_pin[9:2] <= intc_pin[7:0]
- */
- for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
- cpudev = DEVICE(qemu_get_cpu(cpu));
- for (pin = 0; pin < LS3A_INTC_IP; pin++) {
- qdev_connect_gpio_out(extioi, (cpu * 8 + pin),
- qdev_get_gpio_in(cpudev, pin + 2));
- }
- }
+ /* Add Extend I/O Interrupt Controller node */
+ fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle);
- pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC);
- num = VIRT_PCH_PIC_IRQ_NUM;
- qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num);
- d = SYS_BUS_DEVICE(pch_pic);
- sysbus_realize_and_unref(d, &error_fatal);
- memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE,
- sysbus_mmio_get_region(d, 0));
- memory_region_add_subregion(get_system_memory(),
+ /* Add PCH PIC node */
+ fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle);
+
+ /* Add PCH MSI node */
+ fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle);
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ pch_pic = qdev_new(TYPE_KVM_LOONGARCH_PCH_PIC);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(pch_pic), &error_fatal);
+ } else {
+ pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC);
+ num = VIRT_PCH_PIC_IRQ_NUM;
+ qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num);
+ d = SYS_BUS_DEVICE(pch_pic);
+ sysbus_realize_and_unref(d, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE,
+ sysbus_mmio_get_region(d, 0));
+ memory_region_add_subregion(get_system_memory(),
VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET,
sysbus_mmio_get_region(d, 1));
- memory_region_add_subregion(get_system_memory(),
- VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO,
- sysbus_mmio_get_region(d, 2));
-
- /* Connect pch_pic irqs to extioi */
- for (i = 0; i < num; i++) {
- qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i));
+ memory_region_add_subregion(get_system_memory(),
+ VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO,
+ sysbus_mmio_get_region(d, 2));
+ /* Connect pch_pic irqs to extioi */
+ for (i = 0; i < num; i++) {
+ qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i));
+ }
}
pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI);
+ num = VIRT_PCH_PIC_IRQ_NUM;
start = num;
num = EXTIOI_IRQS - start;
qdev_prop_set_uint32(pch_msi, "msi_irq_base", start);
qdev_prop_set_uint32(pch_msi, "msi_irq_num", num);
d = SYS_BUS_DEVICE(pch_msi);
sysbus_realize_and_unref(d, &error_fatal);
- sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW);
- for (i = 0; i < num; i++) {
+
+ if (!(kvm_enabled() && kvm_irqchip_in_kernel())) {
/* Connect pch_msi irqs to extioi */
- qdev_connect_gpio_out(DEVICE(d), i,
- qdev_get_gpio_in(extioi, i + start));
+ for (i = 0; i < num; i++) {
+ qdev_connect_gpio_out(DEVICE(d), i,
+ qdev_get_gpio_in(extioi, i + start));
+ }
}
- loongarch_devices_init(pch_pic, lams);
+ sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW);
+ virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle);
}
-static void loongarch_firmware_init(LoongArchMachineState *lams)
+static void virt_firmware_init(LoongArchVirtMachineState *lvms)
{
- char *filename = MACHINE(lams)->firmware;
+ char *filename = MACHINE(lvms)->firmware;
char *bios_name = NULL;
int bios_size, i;
BlockBackend *pflash_blk0;
MemoryRegion *mr;
- lams->bios_loaded = false;
+ lvms->bios_loaded = false;
/* Map legacy -drive if=pflash to machine properties */
- for (i = 0; i < ARRAY_SIZE(lams->flash); i++) {
- pflash_cfi01_legacy_drive(lams->flash[i],
+ for (i = 0; i < ARRAY_SIZE(lvms->flash); i++) {
+ pflash_cfi01_legacy_drive(lvms->flash[i],
drive_get(IF_PFLASH, 0, i));
}
- virt_flash_map(lams, get_system_memory());
+ virt_flash_map(lvms, get_system_memory());
- pflash_blk0 = pflash_cfi01_get_blk(lams->flash[0]);
+ pflash_blk0 = pflash_cfi01_get_blk(lvms->flash[0]);
if (pflash_blk0) {
if (filename) {
@@ -733,7 +977,7 @@ static void loongarch_firmware_init(LoongArchMachineState *lams)
"options at once");
exit(1);
}
- lams->bios_loaded = true;
+ lvms->bios_loaded = true;
return;
}
@@ -744,92 +988,31 @@ static void loongarch_firmware_init(LoongArchMachineState *lams)
exit(1);
}
- mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lams->flash[0]), 0);
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lvms->flash[0]), 0);
bios_size = load_image_mr(bios_name, mr);
if (bios_size < 0) {
error_report("Could not load ROM image '%s'", bios_name);
exit(1);
}
g_free(bios_name);
- lams->bios_loaded = true;
- }
-}
-
-static void reset_load_elf(void *opaque)
-{
- LoongArchCPU *cpu = opaque;
- CPULoongArchState *env = &cpu->env;
-
- cpu_reset(CPU(cpu));
- if (env->load_elf) {
- cpu_set_pc(CPU(cpu), env->elf_address);
- }
-}
-
-static void fw_cfg_add_kernel_info(const struct loaderparams *loaderparams,
- FWCfgState *fw_cfg)
-{
- /*
- * Expose the kernel, the command line, and the initrd in fw_cfg.
- * We don't process them here at all, it's all left to the
- * firmware.
- */
- load_image_to_fw_cfg(fw_cfg,
- FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
- loaderparams->kernel_filename,
- false);
-
- if (loaderparams->initrd_filename) {
- load_image_to_fw_cfg(fw_cfg,
- FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
- loaderparams->initrd_filename, false);
- }
-
- if (loaderparams->kernel_cmdline) {
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
- strlen(loaderparams->kernel_cmdline) + 1);
- fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
- loaderparams->kernel_cmdline);
+ lvms->bios_loaded = true;
}
}
-static void loongarch_firmware_boot(LoongArchMachineState *lams,
- const struct loaderparams *loaderparams)
-{
- fw_cfg_add_kernel_info(loaderparams, lams->fw_cfg);
-}
-
-static void loongarch_direct_kernel_boot(LoongArchMachineState *lams,
- const struct loaderparams *loaderparams)
-{
- MachineState *machine = MACHINE(lams);
- int64_t kernel_addr = 0;
- LoongArchCPU *lacpu;
- int i;
-
- kernel_addr = load_kernel_info(loaderparams);
- if (!machine->firmware) {
- for (i = 0; i < machine->smp.cpus; i++) {
- lacpu = LOONGARCH_CPU(qemu_get_cpu(i));
- lacpu->env.load_elf = true;
- lacpu->env.elf_address = kernel_addr;
- }
- }
-}
-static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val,
- unsigned size, MemTxAttrs attrs)
+static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size, MemTxAttrs attrs)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque);
uint64_t features;
switch (addr) {
case MISC_FUNC_REG:
- if (!virt_is_veiointc_enabled(lams)) {
+ if (!virt_is_veiointc_enabled(lvms)) {
return MEMTX_OK;
}
- features = address_space_ldl(&lams->as_iocsr,
+ features = address_space_ldl(&lvms->as_iocsr,
EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
attrs, NULL);
if (val & BIT_ULL(IOCSRM_EXTIOI_EN)) {
@@ -839,7 +1022,7 @@ static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val,
features |= BIT(EXTIOI_ENABLE_INT_ENCODE);
}
- address_space_stl(&lams->as_iocsr,
+ address_space_stl(&lvms->as_iocsr,
EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
features, attrs, NULL);
}
@@ -847,11 +1030,11 @@ static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val,
return MEMTX_OK;
}
-static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr,
- uint64_t *data,
- unsigned size, MemTxAttrs attrs)
+static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr,
+ uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque);
uint64_t ret = 0;
int features;
@@ -860,10 +1043,9 @@ static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr,
ret = 0x11ULL;
break;
case FEATURE_REG:
- ret = 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI |
- 1ULL << IOCSRF_CSRIPI;
+ ret = BIT(IOCSRF_MSI) | BIT(IOCSRF_EXTIOI) | BIT(IOCSRF_CSRIPI);
if (kvm_enabled()) {
- ret |= 1ULL << IOCSRF_VM;
+ ret |= BIT(IOCSRF_VM);
}
break;
case VENDOR_REG:
@@ -873,12 +1055,12 @@ static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr,
ret = 0x303030354133ULL; /* "3A5000" */
break;
case MISC_FUNC_REG:
- if (!virt_is_veiointc_enabled(lams)) {
+ if (!virt_is_veiointc_enabled(lvms)) {
ret |= BIT_ULL(IOCSRM_EXTIOI_EN);
break;
}
- features = address_space_ldl(&lams->as_iocsr,
+ features = address_space_ldl(&lvms->as_iocsr,
EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
attrs, NULL);
if (features & BIT(EXTIOI_ENABLE)) {
@@ -889,15 +1071,17 @@ static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr,
ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE);
}
break;
+ default:
+ g_assert_not_reached();
}
*data = ret;
return MEMTX_OK;
}
-static const MemoryRegionOps loongarch_qemu_ops = {
- .read_with_attrs = loongarch_qemu_read,
- .write_with_attrs = loongarch_qemu_write,
+static const MemoryRegionOps virt_iocsr_misc_ops = {
+ .read_with_attrs = virt_iocsr_misc_read,
+ .write_with_attrs = virt_iocsr_misc_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
@@ -909,95 +1093,146 @@ static const MemoryRegionOps loongarch_qemu_ops = {
},
};
-static void loongarch_init(MachineState *machine)
+static void fw_cfg_add_memory(MachineState *ms)
+{
+ hwaddr base, size, ram_size, gap;
+ int nb_numa_nodes, nodes;
+ NodeInfo *numa_info;
+
+ ram_size = ms->ram_size;
+ base = VIRT_LOWMEM_BASE;
+ gap = VIRT_LOWMEM_SIZE;
+ nodes = nb_numa_nodes = ms->numa_state->num_nodes;
+ numa_info = ms->numa_state->nodes;
+ if (!nodes) {
+ nodes = 1;
+ }
+
+ /* add fw_cfg memory map of node0 */
+ if (nb_numa_nodes) {
+ size = numa_info[0].node_mem;
+ } else {
+ size = ram_size;
+ }
+
+ if (size >= gap) {
+ memmap_add_entry(base, gap, 1);
+ size -= gap;
+ base = VIRT_HIGHMEM_BASE;
+ }
+
+ if (size) {
+ memmap_add_entry(base, size, 1);
+ base += size;
+ }
+
+ if (nodes < 2) {
+ return;
+ }
+
+ /* add fw_cfg memory map of other nodes */
+ if (numa_info[0].node_mem < gap && ram_size > gap) {
+ /*
+ * memory map for the maining nodes splited into two part
+ * lowram: [base, +(gap - numa_info[0].node_mem))
+ * highram: [VIRT_HIGHMEM_BASE, +(ram_size - gap))
+ */
+ memmap_add_entry(base, gap - numa_info[0].node_mem, 1);
+ size = ram_size - gap;
+ base = VIRT_HIGHMEM_BASE;
+ } else {
+ size = ram_size - numa_info[0].node_mem;
+ }
+
+ if (size)
+ memmap_add_entry(base, size, 1);
+}
+
+static void virt_init(MachineState *machine)
{
LoongArchCPU *lacpu;
const char *cpu_model = machine->cpu_type;
- ram_addr_t offset = 0;
- ram_addr_t ram_size = machine->ram_size;
- uint64_t highram_size = 0, phyAddr = 0;
MemoryRegion *address_space_mem = get_system_memory();
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
- int nb_numa_nodes = machine->numa_state->num_nodes;
- NodeInfo *numa_info = machine->numa_state->nodes;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
int i;
- hwaddr fdt_base;
- const CPUArchIdList *possible_cpus;
+ hwaddr base, size, ram_size = machine->ram_size;
MachineClass *mc = MACHINE_GET_CLASS(machine);
- CPUState *cpu;
- struct loaderparams loaderparams = { };
if (!cpu_model) {
cpu_model = LOONGARCH_CPU_TYPE_NAME("la464");
}
- if (ram_size < 1 * GiB) {
- error_report("ram_size must be greater than 1G.");
- exit(1);
- }
- create_fdt(lams);
+ create_fdt(lvms);
/* Create IOCSR space */
- memory_region_init_io(&lams->system_iocsr, OBJECT(machine), NULL,
+ memory_region_init_io(&lvms->system_iocsr, OBJECT(machine), NULL,
machine, "iocsr", UINT64_MAX);
- address_space_init(&lams->as_iocsr, &lams->system_iocsr, "IOCSR");
- memory_region_init_io(&lams->iocsr_mem, OBJECT(machine),
- &loongarch_qemu_ops,
+ address_space_init(&lvms->as_iocsr, &lvms->system_iocsr, "IOCSR");
+ memory_region_init_io(&lvms->iocsr_mem, OBJECT(machine),
+ &virt_iocsr_misc_ops,
machine, "iocsr_misc", 0x428);
- memory_region_add_subregion(&lams->system_iocsr, 0, &lams->iocsr_mem);
+ memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem);
/* Init CPUs */
- possible_cpus = mc->possible_cpu_arch_ids(machine);
- for (i = 0; i < possible_cpus->len; i++) {
- cpu = cpu_create(machine->cpu_type);
- cpu->cpu_index = i;
- machine->possible_cpus->cpus[i].cpu = OBJECT(cpu);
- lacpu = LOONGARCH_CPU(cpu);
+ mc->possible_cpu_arch_ids(machine);
+ for (i = 0; i < machine->smp.cpus; i++) {
+ Object *cpuobj;
+ cpuobj = object_new(machine->cpu_type);
+ lacpu = LOONGARCH_CPU(cpuobj);
+
lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id;
+ object_property_set_int(cpuobj, "socket-id",
+ machine->possible_cpus->cpus[i].props.socket_id,
+ NULL);
+ object_property_set_int(cpuobj, "core-id",
+ machine->possible_cpus->cpus[i].props.core_id,
+ NULL);
+ object_property_set_int(cpuobj, "thread-id",
+ machine->possible_cpus->cpus[i].props.thread_id,
+ NULL);
+ /*
+ * The CPU in place at the time of machine startup will also enter
+ * the CPU hot-plug process when it is created, but at this time,
+ * the GED device has not been created, resulting in exit in the CPU
+ * hot-plug process, which can avoid the incumbent CPU repeatedly
+ * applying for resources.
+ *
+ * The interrupt resource of the in-place CPU will be requested at
+ * the current function call loongarch_irq_init().
+ *
+ * The interrupt resource of the subsequently inserted CPU will be
+ * requested in the CPU hot-plug process.
+ */
+ qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
+ object_unref(cpuobj);
}
- fdt_add_cpu_nodes(lams);
+
+ fdt_add_cpu_nodes(lvms);
+ fdt_add_memory_nodes(machine);
+ fw_cfg_add_memory(machine);
/* Node0 memory */
- memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1);
- fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0);
- memory_region_init_alias(&lams->lowmem, NULL, "loongarch.node0.lowram",
- machine->ram, offset, VIRT_LOWMEM_SIZE);
- memory_region_add_subregion(address_space_mem, phyAddr, &lams->lowmem);
-
- offset += VIRT_LOWMEM_SIZE;
- if (nb_numa_nodes > 0) {
- assert(numa_info[0].node_mem > VIRT_LOWMEM_SIZE);
- highram_size = numa_info[0].node_mem - VIRT_LOWMEM_SIZE;
- } else {
- highram_size = ram_size - VIRT_LOWMEM_SIZE;
+ size = ram_size;
+ base = VIRT_LOWMEM_BASE;
+ if (size > VIRT_LOWMEM_SIZE) {
+ size = VIRT_LOWMEM_SIZE;
}
- phyAddr = VIRT_HIGHMEM_BASE;
- memmap_add_entry(phyAddr, highram_size, 1);
- fdt_add_memory_node(machine, phyAddr, highram_size, 0);
- memory_region_init_alias(&lams->highmem, NULL, "loongarch.node0.highram",
- machine->ram, offset, highram_size);
- memory_region_add_subregion(address_space_mem, phyAddr, &lams->highmem);
-
- /* Node1 - Nodemax memory */
- offset += highram_size;
- phyAddr += highram_size;
-
- for (i = 1; i < nb_numa_nodes; i++) {
- MemoryRegion *nodemem = g_new(MemoryRegion, 1);
- g_autofree char *ramName = g_strdup_printf("loongarch.node%d.ram", i);
- memory_region_init_alias(nodemem, NULL, ramName, machine->ram,
- offset, numa_info[i].node_mem);
- memory_region_add_subregion(address_space_mem, phyAddr, nodemem);
- memmap_add_entry(phyAddr, numa_info[i].node_mem, 1);
- fdt_add_memory_node(machine, phyAddr, numa_info[i].node_mem, i);
- offset += numa_info[i].node_mem;
- phyAddr += numa_info[i].node_mem;
+
+ memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.lowram",
+ machine->ram, base, size);
+ memory_region_add_subregion(address_space_mem, base, &lvms->lowmem);
+ base += size;
+ if (ram_size - size) {
+ base = VIRT_HIGHMEM_BASE;
+ memory_region_init_alias(&lvms->highmem, NULL, "loongarch.highram",
+ machine->ram, VIRT_LOWMEM_BASE + size, ram_size - size);
+ memory_region_add_subregion(address_space_mem, base, &lvms->highmem);
+ base += ram_size - size;
}
/* initialize device memory address space */
if (machine->ram_size < machine->maxram_size) {
ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size;
- hwaddr device_mem_base;
if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) {
error_report("unsupported amount of memory slots: %"PRIu64,
@@ -1011,55 +1246,35 @@ static void loongarch_init(MachineState *machine)
"%d bytes", TARGET_PAGE_SIZE);
exit(EXIT_FAILURE);
}
- /* device memory base is the top of high memory address. */
- device_mem_base = ROUND_UP(VIRT_HIGHMEM_BASE + highram_size, 1 * GiB);
- machine_memory_devices_init(machine, device_mem_base, device_mem_size);
+ machine_memory_devices_init(machine, base, device_mem_size);
}
/* load the BIOS image. */
- loongarch_firmware_init(lams);
+ virt_firmware_init(lvms);
/* fw_cfg init */
- lams->fw_cfg = loongarch_fw_cfg_init(ram_size, machine);
- rom_set_fw(lams->fw_cfg);
- if (lams->fw_cfg != NULL) {
- fw_cfg_add_file(lams->fw_cfg, "etc/memmap",
+ lvms->fw_cfg = virt_fw_cfg_init(ram_size, machine);
+ rom_set_fw(lvms->fw_cfg);
+ if (lvms->fw_cfg != NULL) {
+ fw_cfg_add_file(lvms->fw_cfg, "etc/memmap",
memmap_table,
sizeof(struct memmap_entry) * (memmap_entries));
}
- fdt_add_fw_cfg_node(lams);
- loaderparams.ram_size = ram_size;
- loaderparams.kernel_filename = machine->kernel_filename;
- loaderparams.kernel_cmdline = machine->kernel_cmdline;
- loaderparams.initrd_filename = machine->initrd_filename;
- /* load the kernel. */
- if (loaderparams.kernel_filename) {
- if (lams->bios_loaded) {
- loongarch_firmware_boot(lams, &loaderparams);
- } else {
- loongarch_direct_kernel_boot(lams, &loaderparams);
- }
- }
- fdt_add_flash_node(lams);
- /* register reset function */
- for (i = 0; i < machine->smp.cpus; i++) {
- lacpu = LOONGARCH_CPU(qemu_get_cpu(i));
- qemu_register_reset(reset_load_elf, lacpu);
- }
+ fdt_add_fw_cfg_node(lvms);
+ fdt_add_flash_node(lvms);
+
/* Initialize the IO interrupt subsystem */
- loongarch_irq_init(lams);
- fdt_add_irqchip_node(lams);
- platform_bus_add_all_fdt_nodes(machine->fdt, "/intc",
+ virt_irq_init(lvms);
+ platform_bus_add_all_fdt_nodes(machine->fdt, "/platic",
VIRT_PLATFORM_BUS_BASEADDRESS,
VIRT_PLATFORM_BUS_SIZE,
VIRT_PLATFORM_BUS_IRQ);
- lams->machine_done.notify = virt_machine_done;
- qemu_add_machine_init_done_notifier(&lams->machine_done);
+ lvms->machine_done.notify = virt_done;
+ qemu_add_machine_init_done_notifier(&lvms->machine_done);
/* connect powerdown request */
- lams->powerdown_notifier.notify = virt_powerdown_req;
- qemu_register_powerdown_notifier(&lams->powerdown_notifier);
+ lvms->powerdown_notifier.notify = virt_powerdown_req;
+ qemu_register_powerdown_notifier(&lvms->powerdown_notifier);
- fdt_add_pcie_node(lams);
/*
* Since lowmem region starts from 0 and Linux kernel legacy start address
* at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer
@@ -1067,47 +1282,241 @@ static void loongarch_init(MachineState *machine)
* Put the FDT into the memory map as a ROM image: this will ensure
* the FDT is copied again upon reset, even if addr points into RAM.
*/
- fdt_base = 1 * MiB;
- qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size);
- rom_add_blob_fixed("fdt", machine->fdt, lams->fdt_size, fdt_base);
+ qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size);
+ rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE,
+ &address_space_memory);
+ qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds,
+ rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size));
+
+ lvms->bootinfo.ram_size = ram_size;
+ loongarch_load_kernel(machine, &lvms->bootinfo);
}
-bool loongarch_is_acpi_enabled(LoongArchMachineState *lams)
+static void virt_get_acpi(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
- if (lams->acpi == ON_OFF_AUTO_OFF) {
- return false;
- }
- return true;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
+ OnOffAuto acpi = lvms->acpi;
+
+ visit_type_OnOffAuto(v, name, &acpi, errp);
}
-static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name,
+static void virt_set_acpi(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
- OnOffAuto acpi = lams->acpi;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
- visit_type_OnOffAuto(v, name, &acpi, errp);
+ visit_type_OnOffAuto(v, name, &lvms->acpi, errp);
}
-static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
+static void virt_initfn(Object *obj)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
- visit_type_OnOffAuto(v, name, &lams->acpi, errp);
+ if (tcg_enabled()) {
+ lvms->veiointc = ON_OFF_AUTO_OFF;
+ }
+ lvms->acpi = ON_OFF_AUTO_AUTO;
+ lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
+ lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
+ virt_flash_create(lvms);
}
-static void loongarch_machine_initfn(Object *obj)
+static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+ int arch_id, sock_vcpu_num, core_vcpu_num;
- if (tcg_enabled()) {
- lams->veiointc = ON_OFF_AUTO_OFF;
+ /*
+ * calculate total logical cpus across socket/core/thread.
+ * For more information on how to calculate the arch_id,
+ * you can refer to the CPU Topology chapter of the
+ * docs/system/loongarch/virt.rst document.
+ */
+ sock_vcpu_num = topo->socket_id * (ms->smp.threads * ms->smp.cores);
+ core_vcpu_num = topo->core_id * ms->smp.threads;
+
+ /* get vcpu-id(logical cpu index) for this vcpu from this topology */
+ arch_id = (sock_vcpu_num + core_vcpu_num) + topo->thread_id;
+
+ assert(arch_id >= 0 && arch_id < ms->possible_cpus->len);
+
+ return arch_id;
+}
+
+/* find cpu slot in machine->possible_cpus by arch_id */
+static CPUArchId *virt_find_cpu_slot(MachineState *ms, int arch_id, int *index)
+{
+ int n;
+ for (n = 0; n < ms->possible_cpus->len; n++) {
+ if (ms->possible_cpus->cpus[n].arch_id == arch_id) {
+ if (index) {
+ *index = n;
+ }
+ return &ms->possible_cpus->cpus[n];
+ }
}
- lams->acpi = ON_OFF_AUTO_AUTO;
- lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
- lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
- virt_flash_create(lams);
+
+ return NULL;
+}
+
+static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ MachineState *ms = MACHINE(OBJECT(hotplug_dev));
+ MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev);
+ LoongArchCPU *cpu = LOONGARCH_CPU(dev);
+ CPUState *cs = CPU(dev);
+ CPUArchId *cpu_slot;
+ Error *local_err = NULL;
+ LoongArchCPUTopo topo;
+ int arch_id, index;
+
+ if (dev->hotplugged && !mc->has_hotpluggable_cpus) {
+ error_setg(&local_err, "CPU hotplug not supported for this machine");
+ goto out;
+ }
+
+ /* sanity check the cpu */
+ if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
+ error_setg(&local_err, "Invalid CPU type, expected cpu type: '%s'",
+ ms->cpu_type);
+ goto out;
+ }
+
+ if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) {
+ error_setg(&local_err,
+ "Invalid thread-id %u specified, must be in range 1:%u",
+ cpu->thread_id, ms->smp.threads - 1);
+ goto out;
+ }
+
+ if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) {
+ error_setg(&local_err,
+ "Invalid core-id %u specified, must be in range 1:%u",
+ cpu->core_id, ms->smp.cores - 1);
+ goto out;
+ }
+
+ if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) {
+ error_setg(&local_err,
+ "Invalid socket-id %u specified, must be in range 1:%u",
+ cpu->socket_id, ms->smp.sockets - 1);
+ goto out;
+ }
+
+ topo.socket_id = cpu->socket_id;
+ topo.core_id = cpu->core_id;
+ topo.thread_id = cpu->thread_id;
+ arch_id = virt_get_arch_id_from_topo(ms, &topo);
+ cpu_slot = virt_find_cpu_slot(ms, arch_id, &index);
+ if (CPU(cpu_slot->cpu)) {
+ error_setg(&local_err,
+ "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists",
+ cs->cpu_index, cpu->socket_id, cpu->core_id,
+ cpu->thread_id, cpu_slot->arch_id);
+ goto out;
+ }
+ cpu->phy_id = arch_id;
+ /*
+ * update cpu_index calculation method since it is easily used as index
+ * with possible_cpus array by function virt_cpu_index_to_props
+ */
+ cs->cpu_index = index;
+ numa_cpu_pre_plug(cpu_slot, dev, &local_err);
+ return ;
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
+ Error *local_err = NULL;
+ HotplugHandlerClass *hhc;
+ LoongArchCPU *cpu = LOONGARCH_CPU(dev);
+ CPUState *cs = CPU(dev);
+
+ if (!lvms->acpi_ged) {
+ error_setg(&local_err, "CPU hot unplug not supported without ACPI");
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (cs->cpu_index == 0) {
+ error_setg(&local_err,
+ "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported",
+ cs->cpu_index, cpu->socket_id,
+ cpu->core_id, cpu->thread_id);
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lvms->acpi_ged);
+ hhc->unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &local_err);
+}
+
+static void virt_cpu_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ CPUArchId *cpu_slot;
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+ LoongArchCPU *cpu = LOONGARCH_CPU(dev);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lvms->acpi_ged);
+ hhc->unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id, NULL);
+ cpu_slot->cpu = NULL;
+ return;
+}
+
+static void virt_cpu_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ CPUArchId *cpu_slot;
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+ LoongArchCPU *cpu = LOONGARCH_CPU(dev);
+ CPUState *cs = CPU(cpu);
+ CPULoongArchState *env;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
+ int pin;
+
+ if (lvms->acpi_ged) {
+ env = &(cpu->env);
+ env->address_space_iocsr = &lvms->as_iocsr;
+
+ qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(cs->cpu_index)));
+ env->ipistate = lvms->ipi;
+ if (!(kvm_enabled() && kvm_irqchip_in_kernel())) {
+ /* connect ipi irq to cpu irq, logic cpu index used here */
+ qdev_connect_gpio_out(lvms->ipi, cs->cpu_index,
+ qdev_get_gpio_in(dev, IRQ_IPI));
+
+ for (pin = 0; pin < LS3A_INTC_IP; pin++) {
+ qdev_connect_gpio_out(lvms->extioi, (cs->cpu_index * 8 + pin),
+ qdev_get_gpio_in(dev, pin + 2));
+ }
+ }
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lvms->acpi_ged);
+ hhc->plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id, NULL);
+ cpu_slot->cpu = OBJECT(dev);
+ return;
}
static bool memhp_type_supported(DeviceState *dev)
@@ -1123,92 +1532,112 @@ static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
}
-static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev,
+static void virt_device_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
virt_mem_pre_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ virt_cpu_pre_plug(hotplug_dev, dev, errp);
}
}
static void virt_mem_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
/* the acpi ged is always exist */
- hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev,
+ hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev,
errp);
}
-static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev,
+static void virt_device_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
virt_mem_unplug_request(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ virt_cpu_unplug_request(hotplug_dev, dev, errp);
}
}
static void virt_mem_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
- hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp);
- pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams));
+ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp);
+ pc_dimm_unplug(PC_DIMM(dev), MACHINE(lvms));
qdev_unrealize(dev);
}
-static void virt_machine_device_unplug(HotplugHandler *hotplug_dev,
+static void virt_device_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
virt_mem_unplug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ virt_cpu_unplug(hotplug_dev, dev, errp);
}
}
static void virt_mem_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
- pc_dimm_plug(PC_DIMM(dev), MACHINE(lams));
- hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged),
+ pc_dimm_plug(PC_DIMM(dev), MACHINE(lvms));
+ hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged),
dev, &error_abort);
}
-static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev,
+static void virt_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
+ PlatformBusDevice *pbus;
if (device_is_dynamic_sysbus(mc, dev)) {
- if (lams->platform_bus_dev) {
- platform_bus_link_device(PLATFORM_BUS_DEVICE(lams->platform_bus_dev),
- SYS_BUS_DEVICE(dev));
+ if (lvms->platform_bus_dev) {
+ pbus = PLATFORM_BUS_DEVICE(lvms->platform_bus_dev);
+ platform_bus_link_device(pbus, SYS_BUS_DEVICE(dev));
}
} else if (memhp_type_supported(dev)) {
virt_mem_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ virt_cpu_plug(hotplug_dev, dev, errp);
}
}
-static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
- DeviceState *dev)
+static HotplugHandler *virt_get_hotplug_handler(MachineState *machine,
+ DeviceState *dev)
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
if (device_is_dynamic_sysbus(mc, dev) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) ||
memhp_type_supported(dev)) {
return HOTPLUG_HANDLER(machine);
}
return NULL;
}
+static void virt_get_cpu_topo_from_index(MachineState *ms,
+ LoongArchCPUTopo *topo, int index)
+{
+ topo->socket_id = index / (ms->smp.cores * ms->smp.threads);
+ topo->core_id = index / ms->smp.threads % ms->smp.cores;
+ topo->thread_id = index % ms->smp.threads;
+}
+
static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
{
int n;
unsigned int max_cpus = ms->smp.max_cpus;
+ LoongArchCPUTopo topo;
if (ms->possible_cpus) {
assert(ms->possible_cpus->len == max_cpus);
@@ -1219,23 +1648,24 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
sizeof(CPUArchId) * max_cpus);
ms->possible_cpus->len = max_cpus;
for (n = 0; n < ms->possible_cpus->len; n++) {
+ ms->possible_cpus->cpus[n].vcpus_count = ms->smp.threads;
ms->possible_cpus->cpus[n].type = ms->cpu_type;
- ms->possible_cpus->cpus[n].arch_id = n;
+ virt_get_cpu_topo_from_index(ms, &topo, n);
ms->possible_cpus->cpus[n].props.has_socket_id = true;
- ms->possible_cpus->cpus[n].props.socket_id =
- n / (ms->smp.cores * ms->smp.threads);
+ ms->possible_cpus->cpus[n].props.socket_id = topo.socket_id;
ms->possible_cpus->cpus[n].props.has_core_id = true;
- ms->possible_cpus->cpus[n].props.core_id =
- n / ms->smp.threads % ms->smp.cores;
+ ms->possible_cpus->cpus[n].props.core_id = topo.core_id;
ms->possible_cpus->cpus[n].props.has_thread_id = true;
- ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads;
+ ms->possible_cpus->cpus[n].props.thread_id = topo.thread_id;
+ ms->possible_cpus->cpus[n].arch_id =
+ virt_get_arch_id_from_topo(ms, &topo);
}
return ms->possible_cpus;
}
-static CpuInstanceProperties
-virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
+static CpuInstanceProperties virt_cpu_index_to_props(MachineState *ms,
+ unsigned cpu_index)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
@@ -1246,27 +1676,25 @@ virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx)
{
- int64_t nidx = 0;
+ int64_t socket_id;
if (ms->numa_state->num_nodes) {
- nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes);
- if (ms->numa_state->num_nodes <= nidx) {
- nidx = ms->numa_state->num_nodes - 1;
- }
+ socket_id = ms->possible_cpus->cpus[idx].props.socket_id;
+ return socket_id % ms->numa_state->num_nodes;
+ } else {
+ return 0;
}
- return nidx;
}
-static void loongarch_class_init(ObjectClass *oc, void *data)
+static void virt_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
- mc->desc = "Loongson-3A5000 LS7A1000 machine";
- mc->init = loongarch_init;
- mc->default_ram_size = 1 * GiB;
+ mc->init = virt_init;
mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464");
mc->default_ram_id = "loongarch.ram";
+ mc->desc = "QEMU LoongArch Virtual Machine";
mc->max_cpus = LOONGARCH_MAX_CPUS;
mc->is_default = 1;
mc->default_kernel_irqchip_split = false;
@@ -1279,15 +1707,16 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
mc->numa_mem_supported = true;
mc->auto_enable_numa_with_memhp = true;
mc->auto_enable_numa_with_memdev = true;
- mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
+ mc->has_hotpluggable_cpus = true;
+ mc->get_hotplug_handler = virt_get_hotplug_handler;
mc->default_nic = "virtio-net-pci";
- hc->plug = loongarch_machine_device_plug_cb;
- hc->pre_plug = virt_machine_device_pre_plug;
- hc->unplug_request = virt_machine_device_unplug_request;
- hc->unplug = virt_machine_device_unplug;
+ hc->plug = virt_device_plug_cb;
+ hc->pre_plug = virt_device_pre_plug;
+ hc->unplug_request = virt_device_unplug_request;
+ hc->unplug = virt_device_unplug;
object_class_property_add(oc, "acpi", "OnOffAuto",
- loongarch_get_acpi, loongarch_set_acpi,
+ virt_get_acpi, virt_set_acpi,
NULL, NULL);
object_class_property_set_description(oc, "acpi",
"Enable ACPI");
@@ -1301,13 +1730,13 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
#endif
}
-static const TypeInfo loongarch_machine_types[] = {
+static const TypeInfo virt_machine_types[] = {
{
- .name = TYPE_LOONGARCH_MACHINE,
+ .name = TYPE_LOONGARCH_VIRT_MACHINE,
.parent = TYPE_MACHINE,
- .instance_size = sizeof(LoongArchMachineState),
- .class_init = loongarch_class_init,
- .instance_init = loongarch_machine_initfn,
+ .instance_size = sizeof(LoongArchVirtMachineState),
+ .class_init = virt_class_init,
+ .instance_init = virt_initfn,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ }
@@ -1315,4 +1744,4 @@ static const TypeInfo loongarch_machine_types[] = {
}
};
-DEFINE_TYPES(loongarch_machine_types)
+DEFINE_TYPES(virt_machine_types)
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index d123b838c26d8c1636e29665e30dfdacf2e25fc2..e2e8dff051dcbbbb269cc3a7631153259453ab9c 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -39,6 +39,11 @@ typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
void gdb_register_coprocessor(CPUState *cpu,
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
int num_regs, const char *xml, int g_pos);
+
+/**
+ * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers
+ * @cpu - the CPU associated with registers
+ */
void gdb_unregister_coprocessor_all(CPUState *cpu);
/**
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index b1f389fb4b1a4eb4e48cfc4ab3bbc606601652f8..7a8b708cdaadb44b850f39a147aa2ba6c70e2fc4 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -90,6 +90,39 @@ typedef struct AcpiFadtData {
unsigned *xdsdt_tbl_offset;
} AcpiFadtData;
+typedef struct AcpiGas {
+ uint8_t id; /* Address space ID */
+ uint8_t width; /* Register bit width */
+ uint8_t offset; /* Register bit offset */
+ uint8_t size; /* Access size */
+ uint64_t addr; /* Address */
+} AcpiGas;
+
+/* SPCR (Serial Port Console Redirection table) */
+typedef struct AcpiSpcrData {
+ uint8_t interface_type;
+ uint8_t reserved[3];
+ struct AcpiGas base_addr;
+ uint8_t interrupt_type;
+ uint8_t pc_interrupt;
+ uint32_t interrupt; /* Global system interrupt */
+ uint8_t baud_rate;
+ uint8_t parity;
+ uint8_t stop_bits;
+ uint8_t flow_control;
+ uint8_t terminal_type;
+ uint8_t language;
+ uint8_t reserved1;
+ uint16_t pci_device_id; /* Must be 0xffff if not PCI device */
+ uint16_t pci_vendor_id; /* Must be 0xffff if not PCI device */
+ uint8_t pci_bus;
+ uint8_t pci_device;
+ uint8_t pci_function;
+ uint32_t pci_flags;
+ uint8_t pci_segment;
+ uint32_t reserved2;
+} AcpiSpcrData;
+
#define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0)
#define ACPI_FADT_ARM_PSCI_USE_HVC (1 << 1)
diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
index 7281c281f6d74ecc4fa1e28125ad8f47273b381a..c0b852cbd2921cfaaef40d644dc47d32ef21653d 100644
--- a/include/hw/acpi/aml-build.h
+++ b/include/hw/acpi/aml-build.h
@@ -548,4 +548,8 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog,
const char *oem_id, const char *oem_table_id);
+
+void build_spcr(GArray *table_data, BIOSLinker *linker,
+ const AcpiSpcrData *f, const uint8_t rev,
+ const char *oem_id, const char *oem_table_id);
#endif
diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h
index fced952152298c081e374359d6e1873506750065..fa5b5e5f0136e617461593dda2a1c7539b7e16d5 100644
--- a/include/hw/acpi/cpu.h
+++ b/include/hw/acpi/cpu.h
@@ -18,6 +18,8 @@
#include "hw/boards.h"
#include "hw/hotplug.h"
+#define ACPI_CPU_HOTPLUG_REG_LEN 12
+
typedef struct AcpiCpuStatus {
CPUState *cpu;
uint64_t arch_id;
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
index 90fc41cbb8c82abb10d4218c6ff314806475682c..d1df3c12e535a4ca0d1e1f63f69aa107d5c7adfa 100644
--- a/include/hw/acpi/generic_event_device.h
+++ b/include/hw/acpi/generic_event_device.h
@@ -63,6 +63,7 @@
#include "hw/acpi/cpu_hotplug.h"
#include "hw/acpi/memory_hotplug.h"
#include "hw/acpi/ghes.h"
+#include "hw/acpi/cpu.h"
#include "qom/object.h"
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
@@ -81,8 +82,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED)
/* ACPI_GED_REG_RESET value for reset*/
#define ACPI_GED_RESET_VALUE 0x42
-/* ACPI_GED_REG_SLEEP_CTL.SLP_TYP value for S5 (aka poweroff) */
-#define ACPI_GED_SLP_TYP_S5 0x05
+/* [ACPI 5.0 Chapter 4.8.3.7] Sleep Control and Status Register */
+#define ACPI_GED_SLP_TYP_POS 0x2 /* SLP_TYPx Bit Offset */
+#define ACPI_GED_SLP_TYP_MASK 0x07 /* SLP_TYPx 3-bit mask */
+#define ACPI_GED_SLP_TYP_S5 0x05 /* System _S5 State (Soft Off) */
+#define ACPI_GED_SLP_EN 0x20 /* SLP_EN write-only bit */
#define GED_DEVICE "GED"
#define AML_GED_EVT_REG "EREG"
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index ee04ee44c2387d65a0f8b8bb0f82ff80e4e1ae10..37f3a469c897e09f73fc23a1fa999bee7f834238 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -495,8 +495,8 @@ struct CPUState {
QemuMutex work_mutex;
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
- CPUAddressSpace *cpu_ases;
- int cpu_ases_ref_count;
+ struct CPUAddressSpace *cpu_ases;
+ int cpu_ases_count;
int num_ases;
AddressSpace *as;
MemoryRegion *memory;
diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h
index 98f348c49d47dec33c5f0a286c26829599510c33..9966cd98d327b2b354d81047f7d69a6b6f14c881 100644
--- a/include/hw/intc/loongarch_extioi.h
+++ b/include/hw/intc/loongarch_extioi.h
@@ -15,7 +15,7 @@
#define EXTIOI_IRQS (256)
#define EXTIOI_IRQS_BITMAP_SIZE (256 / 8)
/* irq from EXTIOI is routed to no more than 4 cpus */
-#define EXTIOI_CPUS (4)
+#define EXTIOI_CPUS (256)
/* map to ipnum per 32 irqs */
#define EXTIOI_IRQS_IPMAP_SIZE (256 / 32)
#define EXTIOI_IRQS_COREMAP_SIZE 256
@@ -39,6 +39,7 @@
#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET)
#define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET)
#define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET)
+#define EXTIOI_SIZE 0x800
#define EXTIOI_VIRT_BASE (0x40000000)
#define EXTIOI_VIRT_SIZE (0x1000)
@@ -58,13 +59,17 @@
#define EXTIOI_VIRT_COREMAP_START (0x40)
#define EXTIOI_VIRT_COREMAP_END (0x240)
+#define EXTIOI_SW_COREMAP_FLAG (1 << 0)
+
typedef struct ExtIOICore {
uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT];
DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS);
qemu_irq parent_irq[LS3A_INTC_IP];
} ExtIOICore;
-#define TYPE_LOONGARCH_EXTIOI "loongarch.extioi"
+#define TYPE_LOONGARCH_EXTIOI "loongarch-extioi"
+#define TYPE_KVM_LOONGARCH_EXTIOI "loongarch-kvm-extioi"
+
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI)
struct LoongArchExtIOI {
SysBusDevice parent_obj;
@@ -86,4 +91,32 @@ struct LoongArchExtIOI {
MemoryRegion extioi_system_mem;
MemoryRegion virt_extend;
};
+
+struct KVMLoongArchExtIOI {
+ SysBusDevice parent_obj;
+ /* hardware state */
+ uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2];
+ uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT];
+ uint32_t isr[EXTIOI_IRQS / 32];
+ uint32_t coreisr[EXTIOI_CPUS][EXTIOI_IRQS_GROUP_COUNT];
+ uint32_t enable[EXTIOI_IRQS / 32];
+ uint32_t ipmap[EXTIOI_IRQS_IPMAP_SIZE / 4];
+ uint32_t coremap[EXTIOI_IRQS / 4];
+ uint8_t sw_coremap[EXTIOI_IRQS];
+};
+typedef struct KVMLoongArchExtIOI KVMLoongArchExtIOI;
+DECLARE_INSTANCE_CHECKER(KVMLoongArchExtIOI, KVM_LOONGARCH_EXTIOI,
+ TYPE_KVM_LOONGARCH_EXTIOI)
+
+struct KVMLoongArchExtIOIClass {
+ SysBusDeviceClass parent_class;
+ DeviceRealize parent_realize;
+
+ bool is_created;
+ int dev_fd;
+};
+typedef struct KVMLoongArchExtIOIClass KVMLoongArchExtIOIClass;
+DECLARE_CLASS_CHECKERS(KVMLoongArchExtIOIClass, KVM_LOONGARCH_EXTIOI,
+ TYPE_KVM_LOONGARCH_EXTIOI)
+
#endif /* LOONGARCH_EXTIOI_H */
diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h
index 1c1e834849e00c6c43c10787d56f01e611928a2b..601b4f18a7b835457ad12578d836c001ca23797b 100644
--- a/include/hw/intc/loongarch_ipi.h
+++ b/include/hw/intc/loongarch_ipi.h
@@ -32,6 +32,7 @@
#define TYPE_LOONGARCH_IPI "loongarch_ipi"
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI)
+#define TYPE_KVM_LOONGARCH_IPI "loongarch-ipi-kvm"
typedef struct IPICore {
uint32_t status;
@@ -51,4 +52,26 @@ struct LoongArchIPI {
IPICore *cpu;
};
+struct KVMLoongArchIPI {
+ SysBusDevice parent_obj;
+ uint32_t num_cpu;
+ IPICore *cpu;
+};
+typedef struct KVMLoongArchIPI KVMLoongArchIPI;
+DECLARE_INSTANCE_CHECKER(KVMLoongArchIPI, KVM_LOONGARCH_IPI,
+ TYPE_KVM_LOONGARCH_IPI)
+
+struct KVMLoongArchIPIClass {
+ SysBusDeviceClass parent_class;
+ DeviceRealize parent_realize;
+
+ bool is_created;
+ int dev_fd;
+
+};
+typedef struct KVMLoongArchIPIClass KVMLoongArchIPIClass;
+DECLARE_CLASS_CHECKERS(KVMLoongArchIPIClass, KVM_LOONGARCH_IPI,
+ TYPE_KVM_LOONGARCH_IPI)
+
+
#endif
diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h
index b8586fb3b6f6b170915d4b126ad4d562dec1890a..fd4ea97a83b3e7032efec0126179e5f0eb73ba2a 100644
--- a/include/hw/intc/loongarch_pch_msi.h
+++ b/include/hw/intc/loongarch_pch_msi.h
@@ -7,7 +7,7 @@
#include "hw/sysbus.h"
-#define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi"
+#define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi"
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHMSI, LOONGARCH_PCH_MSI)
/* MSI irq start from 32 to 255 */
diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h
index d5437e88f289880b1ab4d93f143e32eb4608c743..77f4cd74a129755a3f580b742655a1f6e915f26e 100644
--- a/include/hw/intc/loongarch_pch_pic.h
+++ b/include/hw/intc/loongarch_pch_pic.h
@@ -7,7 +7,8 @@
#include "hw/sysbus.h"
-#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic"
+#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic"
+#define TYPE_KVM_LOONGARCH_PCH_PIC "loongarch_kvm_pch_pic"
#define PCH_PIC_NAME(name) TYPE_LOONGARCH_PCH_PIC#name
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC)
@@ -37,6 +38,19 @@ OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC)
#define PCH_PIC_INT_POL_LO 0x3e0
#define PCH_PIC_INT_POL_HI 0x3e4
+#define PCH_PIC_INT_ID_START PCH_PIC_INT_ID_LO
+#define PCH_PIC_MASK_START PCH_PIC_INT_MASK_LO
+#define PCH_PIC_HTMSI_EN_START PCH_PIC_HTMSI_EN_LO
+#define PCH_PIC_EDGE_START PCH_PIC_INT_EDGE_LO
+#define PCH_PIC_CLEAR_START PCH_PIC_INT_CLEAR_LO
+#define PCH_PIC_AUTO_CTRL0_START PCH_PIC_AUTO_CTRL0_LO
+#define PCH_PIC_AUTO_CTRL1_START PCH_PIC_AUTO_CTRL1_LO
+#define PCH_PIC_ROUTE_ENTRY_START PCH_PIC_ROUTE_ENTRY_OFFSET
+#define PCH_PIC_HTMSI_VEC_START PCH_PIC_HTMSI_VEC_OFFSET
+#define PCH_PIC_INT_IRR_START 0x380
+#define PCH_PIC_INT_ISR_START PCH_PIC_INT_STATUS_LO
+#define PCH_PIC_POLARITY_START PCH_PIC_INT_POL_LO
+
#define STATUS_LO_START 0
#define STATUS_HI_START 0x4
#define POL_LO_START 0x40
@@ -67,3 +81,38 @@ struct LoongArchPCHPIC {
MemoryRegion iomem8;
unsigned int irq_num;
};
+
+struct KVMLoongArchPCHPIC {
+ SysBusDevice parent_obj;
+ uint64_t int_mask; /*0x020 interrupt mask register*/
+ uint64_t htmsi_en; /*0x040 1=msi*/
+ uint64_t intedge; /*0x060 edge=1 level =0*/
+ uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/
+ uint64_t auto_crtl0; /*0x0c0*/
+ uint64_t auto_crtl1; /*0x0e0*/
+ uint64_t last_intirr; /* edge detection */
+ uint64_t intirr; /* 0x380 interrupt request register */
+ uint64_t intisr; /* 0x3a0 interrupt service register */
+ /*
+ * 0x3e0 interrupt level polarity selection
+ * register 0 for high level trigger
+ */
+ uint64_t int_polarity;
+
+ uint8_t route_entry[64]; /*0x100 - 0x138*/
+ uint8_t htmsi_vector[64]; /*0x200 - 0x238*/
+};
+typedef struct KVMLoongArchPCHPIC KVMLoongArchPCHPIC;
+DECLARE_INSTANCE_CHECKER(KVMLoongArchPCHPIC, KVM_LOONGARCH_PCH_PIC,
+ TYPE_KVM_LOONGARCH_PCH_PIC)
+
+struct KVMLoongArchPCHPICClass {
+ SysBusDeviceClass parent_class;
+ DeviceRealize parent_realize;
+
+ bool is_created;
+ int dev_fd;
+};
+typedef struct KVMLoongArchPCHPICClass KVMLoongArchPCHPICClass;
+DECLARE_CLASS_CHECKERS(KVMLoongArchPCHPICClass, KVM_LOONGARCH_PCH_PIC,
+ TYPE_KVM_LOONGARCH_PCH_PIC)
diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h
new file mode 100644
index 0000000000000000000000000000000000000000..b3b870df1f097662dff7fc7c173f152da583d374
--- /dev/null
+++ b/include/hw/loongarch/boot.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Definitions for LoongArch boot.
+ *
+ * Copyright (C) 2023 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LOONGARCH_BOOT_H
+#define HW_LOONGARCH_BOOT_H
+
+/* UEFI 2.10 */
+#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249
+#define EFI_2_100_SYSTEM_TABLE_REVISION ((2<<16) | (100))
+#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION
+#define EFI_SYSTEM_TABLE_REVISION EFI_2_100_SYSTEM_TABLE_REVISION
+
+#define FW_VERSION 0x1
+#define FW_PATCHLEVEL 0x0
+
+typedef struct {
+ uint8_t b[16];
+} efi_guid_t QEMU_ALIGNED(8);
+
+#define EFI_GUID(a, b, c, d...) (efi_guid_t){ { \
+ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+ (b) & 0xff, ((b) >> 8) & 0xff, \
+ (c) & 0xff, ((c) >> 8) & 0xff, d } }
+
+#define LINUX_EFI_BOOT_MEMMAP_GUID \
+ EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, \
+ 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4)
+
+#define LINUX_EFI_INITRD_MEDIA_GUID \
+ EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, \
+ 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68)
+
+#define DEVICE_TREE_GUID \
+ EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, \
+ 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
+
+struct efi_config_table {
+ efi_guid_t guid;
+ uint64_t *ptr;
+ const char name[16];
+};
+
+typedef struct {
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t headersize;
+ uint32_t crc32;
+ uint32_t reserved;
+} efi_table_hdr_t;
+
+struct efi_configuration_table {
+ efi_guid_t guid;
+ void *table;
+};
+
+struct efi_system_table {
+ efi_table_hdr_t hdr;
+ uint64_t fw_vendor; /* physical addr of CHAR16 vendor string */
+ uint32_t fw_revision;
+ uint64_t con_in_handle;
+ uint64_t *con_in;
+ uint64_t con_out_handle;
+ uint64_t *con_out;
+ uint64_t stderr_handle;
+ uint64_t stderr_placeholder;
+ uint64_t *runtime;
+ uint64_t *boottime;
+ uint64_t nr_tables;
+ struct efi_configuration_table *tables;
+};
+
+typedef struct {
+ uint32_t type;
+ uint32_t pad;
+ uint64_t phys_addr;
+ uint64_t virt_addr;
+ uint64_t num_pages;
+ uint64_t attribute;
+} efi_memory_desc_t;
+
+struct efi_boot_memmap {
+ uint64_t map_size;
+ uint64_t desc_size;
+ uint32_t desc_ver;
+ uint64_t map_key;
+ uint64_t buff_size;
+ efi_memory_desc_t map[32];
+};
+
+struct efi_initrd {
+ uint64_t base;
+ uint64_t size;
+};
+
+struct loongarch_boot_info {
+ uint64_t ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+ uint64_t a0, a1, a2;
+};
+
+extern struct memmap_entry *memmap_table;
+extern unsigned memmap_entries;
+
+struct memmap_entry {
+ uint64_t address;
+ uint64_t length;
+ uint32_t type;
+ uint32_t reserved;
+};
+
+void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info);
+
+#endif /* HW_LOONGARCH_BOOT_H */
diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h
index 99447fd1d6f71d9d54e3e7e85f6055c442a16a94..a79ad4166378eaed6190c1267812e625d5ac2bf1 100644
--- a/include/hw/loongarch/virt.h
+++ b/include/hw/loongarch/virt.h
@@ -13,6 +13,7 @@
#include "qemu/queue.h"
#include "hw/intc/loongarch_ipi.h"
#include "hw/block/flash.h"
+#include "hw/loongarch/boot.h"
#define LOONGARCH_MAX_CPUS 256
@@ -31,8 +32,28 @@
#define VIRT_GED_EVT_ADDR 0x100e0000
#define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN)
#define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN)
+#define VIRT_GED_CPUHP_ADDR (VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT)
-struct LoongArchMachineState {
+#define COMMAND_LINE_SIZE 512
+
+#define FDT_BASE 0x100000
+
+/* KVM_IRQ_LINE irq field index values */
+#define KVM_LOONGARCH_IRQ_TYPE_SHIFT 24
+#define KVM_LOONGARCH_IRQ_TYPE_MASK 0xff
+#define KVM_LOONGARCH_IRQ_VCPU_SHIFT 16
+#define KVM_LOONGARCH_IRQ_VCPU_MASK 0xff
+#define KVM_LOONGARCH_IRQ_NUM_SHIFT 0
+#define KVM_LOONGARCH_IRQ_NUM_MASK 0xffff
+
+/* irq_type field */
+#define KVM_LOONGARCH_IRQ_TYPE_CPU_IP 0
+#define KVM_LOONGARCH_IRQ_TYPE_CPU_IO 1
+#define KVM_LOONGARCH_IRQ_TYPE_HT 2
+#define KVM_LOONGARCH_IRQ_TYPE_MSI 3
+#define KVM_LOONGARCH_IRQ_TYPE_IOAPIC 4
+
+struct LoongArchVirtMachineState {
/*< private >*/
MachineState parent_obj;
@@ -58,10 +79,12 @@ struct LoongArchMachineState {
MemoryRegion iocsr_mem;
AddressSpace as_iocsr;
int features;
+ struct loongarch_boot_info bootinfo;
+ DeviceState *ipi;
};
-#define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt")
-OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_MACHINE)
-bool loongarch_is_acpi_enabled(LoongArchMachineState *lams);
-void loongarch_acpi_setup(LoongArchMachineState *lams);
+#define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
+OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE)
+void loongarch_acpi_setup(LoongArchVirtMachineState *lvms);
+void reset_load_elf(void *opaque);
#endif
diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
index e753449593bf035d0b82cd99e6a0d338556aba45..79d4ea85012747bf27449b7d02c29d9a577d49c9 100644
--- a/include/hw/pci-host/ls7a.h
+++ b/include/hw/pci-host/ls7a.h
@@ -24,6 +24,8 @@
#define VIRT_PCH_REG_BASE 0x10000000UL
#define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE)
#define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL
+#define VIRT_PCH_REG_SIZE 0x400
+#define VIRT_PCH_MSI_SIZE 0x8
/*
* GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot
@@ -34,17 +36,18 @@
#define VIRT_PCH_PIC_IRQ_NUM 32
#define VIRT_GSI_BASE 64
#define VIRT_DEVICE_IRQS 16
+#define VIRT_UART_COUNT 4
#define VIRT_UART_IRQ (VIRT_GSI_BASE + 2)
#define VIRT_UART_BASE 0x1fe001e0
-#define VIRT_UART_SIZE 0X100
-#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 3)
+#define VIRT_UART_SIZE 0x100
+#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 6)
#define VIRT_MISC_REG_BASE (VIRT_PCH_REG_BASE + 0x00080000)
#define VIRT_RTC_REG_BASE (VIRT_MISC_REG_BASE + 0x00050100)
#define VIRT_RTC_LEN 0x100
-#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 4)
+#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 7)
#define VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000
#define VIRT_PLATFORM_BUS_SIZE 0x2000000
#define VIRT_PLATFORM_BUS_NUM_IRQS 2
-#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 5)
+#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 8)
#endif
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index 31af5f0e247a1e909fe0c1e5897c4182bbba7e41..7ffb5e4992642f43c4d30d9022e9a2480e98d788 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -319,6 +319,31 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test);
*/
bool kvm_device_supported(int vmfd, uint64_t type);
+/**
+ * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU
+ * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created.
+ *
+ * @returns: 0 when success, errno (<0) when failed.
+ */
+int kvm_create_vcpu(CPUState *cpu);
+
+/**
+ * kvm_park_vcpu - Park QEMU KVM vCPU context
+ * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked.
+ *
+ * @returns: none
+ */
+void kvm_park_vcpu(CPUState *cpu);
+
+/**
+ * kvm_unpark_vcpu - unpark QEMU KVM vCPU context
+ * @s: KVM State
+ * @vcpu_id: Architecture vCPU ID of the parked vCPU
+ *
+ * @returns: KVM fd
+ */
+int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id);
+
/* Arch specific hooks */
extern const KVMCapabilityInfo kvm_arch_required_capabilities[];
@@ -440,8 +465,6 @@ void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len);
int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr,
hwaddr *phys_addr);
-int kvm_create_vcpu(CPUState *cpu);
-void kvm_park_vcpu(CPUState *cpu);
#endif /* NEED_CPU_H */
diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h
index 81fec85f0ab8703d03602c6e5f112e9915981aa7..13c1280662ae45b5cbf7f14ee55c7121e0ddf339 100644
--- a/linux-headers/asm-loongarch/kvm.h
+++ b/linux-headers/asm-loongarch/kvm.h
@@ -19,6 +19,7 @@
#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
#define KVM_DIRTY_LOG_PAGE_OFFSET 64
+#define __KVM_HAVE_IRQ_LINE
#define KVM_GUESTDBG_USE_SW_BP 0x00010000
/*
@@ -66,6 +67,7 @@ struct kvm_fpu {
#define KVM_REG_LOONGARCH_KVM (KVM_REG_LOONGARCH | 0x20000ULL)
#define KVM_REG_LOONGARCH_FPSIMD (KVM_REG_LOONGARCH | 0x30000ULL)
#define KVM_REG_LOONGARCH_CPUCFG (KVM_REG_LOONGARCH | 0x40000ULL)
+#define KVM_REG_LOONGARCH_LBT (KVM_REG_LOONGARCH | 0x50000ULL)
#define KVM_REG_LOONGARCH_MASK (KVM_REG_LOONGARCH | 0x70000ULL)
#define KVM_CSR_IDX_MASK 0x7fff
#define KVM_CPUCFG_IDX_MASK 0x7fff
@@ -79,13 +81,34 @@ struct kvm_fpu {
/* Debugging: Special instruction for software breakpoint */
#define KVM_REG_LOONGARCH_DEBUG_INST (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 3)
+/* LBT registers */
+#define KVM_REG_LOONGARCH_LBT_SCR0 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 1)
+#define KVM_REG_LOONGARCH_LBT_SCR1 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 2)
+#define KVM_REG_LOONGARCH_LBT_SCR2 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 3)
+#define KVM_REG_LOONGARCH_LBT_SCR3 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 4)
+#define KVM_REG_LOONGARCH_LBT_EFLAGS (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 5)
+#define KVM_REG_LOONGARCH_LBT_FTOP (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 6)
+
#define LOONGARCH_REG_SHIFT 3
#define LOONGARCH_REG_64(TYPE, REG) (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT))
#define KVM_IOC_CSRID(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CSR, REG)
#define KVM_IOC_CPUCFG(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CPUCFG, REG)
+
+/* Device Control API on vm fd */
+#define KVM_LOONGARCH_VM_FEAT_CTRL 0
+#define KVM_LOONGARCH_VM_FEAT_LSX 0
+#define KVM_LOONGARCH_VM_FEAT_LASX 1
+#define KVM_LOONGARCH_VM_FEAT_X86BT 2
+#define KVM_LOONGARCH_VM_FEAT_ARMBT 3
+#define KVM_LOONGARCH_VM_FEAT_MIPSBT 4
+#define KVM_LOONGARCH_VM_FEAT_PMU 5
+#define KVM_LOONGARCH_VM_FEAT_PV_IPI 6
+#define KVM_LOONGARCH_VM_FEAT_PV_STEALTIME 7
+
+/* Device Control API on vcpu fd */
#define KVM_LOONGARCH_VCPU_CPUCFG 0
#define KVM_LOONGARCH_VCPU_PVTIME_CTRL 1
-#define KVM_LOONGARCH_VCPU_PVTIME_GPA 0
+#define KVM_LOONGARCH_VCPU_PVTIME_GPA 0
struct kvm_debug_exit_arch {
};
@@ -112,4 +135,15 @@ struct kvm_iocsr_entry {
#define KVM_IRQCHIP_NUM_PINS 64
#define KVM_MAX_CORES 256
+#define KVM_LOONGARCH_VM_HAVE_IRQCHIP 0x40000001
+
+#define KVM_DEV_LOONGARCH_IPI_GRP_REGS 0x40000002
+
+#define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000003
+
+#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000004
+#define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0
+
+#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS 0x40000005
+
#endif /* __UAPI_ASM_LOONGARCH_KVM_H */
diff --git a/linux-headers/asm-loongarch/unistd.h b/linux-headers/asm-loongarch/unistd.h
index fcb668984f0336fa77740deb7b40b410450cd885..b344b1f917153b6d70ab7d434f05598b116d3642 100644
--- a/linux-headers/asm-loongarch/unistd.h
+++ b/linux-headers/asm-loongarch/unistd.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#define __ARCH_WANT_NEW_STAT
#define __ARCH_WANT_SYS_CLONE
#define __ARCH_WANT_SYS_CLONE3
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index eb30402c2d01e272fdc7c575c036fe8d4877c05c..887f8268e7572f6b61549196c392c584e0e43934 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -1470,6 +1470,12 @@ enum kvm_device_type {
#define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME
KVM_DEV_TYPE_RISCV_AIA,
#define KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_RISCV_AIA
+ KVM_DEV_TYPE_LA_PCH_PIC = 0x100,
+#define KVM_DEV_TYPE_LA_PCH_PIC KVM_DEV_TYPE_LA_PCH_PIC
+ KVM_DEV_TYPE_LA_IPI,
+#define KVM_DEV_TYPE_LA_IPI KVM_DEV_TYPE_LA_IPI
+ KVM_DEV_TYPE_LA_EXTIOI,
+#define KVM_DEV_TYPE_LA_EXTIOI KVM_DEV_TYPE_LA_EXTIOI
KVM_DEV_TYPE_MAX,
};
diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index 34295c0fe55b72bbf4db013c4e96262fa1ebfeac..88c76b8f69b07e9b7b587e27e87578686f99a7a9 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -156,6 +156,10 @@ for arch in $ARCHLIST; do
cp_portable "$tmpdir/bootparam.h" \
"$output/include/standard-headers/asm-$arch"
fi
+ if [ $arch = loongarch ]; then
+ cp "$hdrdir/include/asm/kvm_para.h" "$output/linux-headers/asm-loongarch/"
+ cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-loongarch/"
+ fi
done
rm -rf "$output/linux-headers/linux"
diff --git a/system/physmem.c b/system/physmem.c
index 2c8b83f811eb4c745a785a2887b0d87e84f58983..c50ac24786ed627c7c289e8cfc3a48844b3e2ddd 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -761,7 +761,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
if (!cpu->cpu_ases) {
cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases);
- cpu->cpu_ases_ref_count = cpu->num_ases;
+ cpu->cpu_ases_count = cpu->num_ases;
}
newas = &cpu->cpu_ases[asidx];
@@ -779,24 +779,28 @@ void cpu_address_space_destroy(CPUState *cpu, int asidx)
{
CPUAddressSpace *cpuas;
- assert(asidx < cpu->num_ases);
- assert(asidx == 0 || !kvm_enabled());
assert(cpu->cpu_ases);
+ assert(asidx >= 0 && asidx < cpu->num_ases);
+ /* KVM cannot currently support multiple address spaces. */
+ assert(asidx == 0 || !kvm_enabled());
cpuas = &cpu->cpu_ases[asidx];
if (tcg_enabled()) {
memory_listener_unregister(&cpuas->tcg_as_listener);
}
- cpuas->as->free_in_rcu = true;
address_space_destroy(cpuas->as);
+ g_free_rcu(cpuas->as, rcu);
- if (cpu->cpu_ases_ref_count == 1) {
+ if (asidx == 0) {
+ /* reset the convenience alias for address space 0 */
+ cpu->as = NULL;
+ }
+
+ if (--cpu->cpu_ases_count == 0) {
g_free(cpu->cpu_ases);
cpu->cpu_ases = NULL;
}
-
- cpu->cpu_ases_ref_count--;
}
AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx)
diff --git a/target/loongarch/arch_dump.c b/target/loongarch/arch_dump.c
new file mode 100644
index 0000000000000000000000000000000000000000..d9e1120333ce1a345e0153118cd6f18f54e7308b
--- /dev/null
+++ b/target/loongarch/arch_dump.c
@@ -0,0 +1,163 @@
+/*
+ * Support for writing ELF notes for LoongArch architectures
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "elf.h"
+#include "sysemu/dump.h"
+#include "internals.h"
+
+/* struct user_pt_regs from arch/loongarch/include/uapi/asm/ptrace.h */
+struct loongarch_user_regs {
+ uint64_t gpr[32];
+ uint64_t pad1[1];
+ /* Special CSR registers. */
+ uint64_t csr_era;
+ uint64_t csr_badv;
+ uint64_t pad2[10];
+} QEMU_PACKED;
+
+QEMU_BUILD_BUG_ON(sizeof(struct loongarch_user_regs) != 360);
+
+/* struct elf_prstatus from include/uapi/linux/elfcore.h */
+struct loongarch_elf_prstatus {
+ char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */
+ uint32_t pr_pid;
+ /*
+ * 76 == offsetof(struct elf_prstatus, pr_reg) -
+ * offsetof(struct elf_prstatus, pr_ppid)
+ */
+ char pad2[76];
+ struct loongarch_user_regs pr_reg;
+ uint32_t pr_fpvalid;
+ char pad3[4];
+} QEMU_PACKED;
+
+QEMU_BUILD_BUG_ON(sizeof(struct loongarch_elf_prstatus) != 480);
+
+/* struct user_fp_state from arch/loongarch/include/uapi/asm/ptrace.h */
+struct loongarch_fpu_struct {
+ uint64_t fpr[32];
+ uint64_t fcc;
+ unsigned int fcsr;
+} QEMU_PACKED;
+
+QEMU_BUILD_BUG_ON(sizeof(struct loongarch_fpu_struct) != 268);
+
+struct loongarch_note {
+ Elf64_Nhdr hdr;
+ char name[8]; /* align_up(sizeof("CORE"), 4) */
+ union {
+ struct loongarch_elf_prstatus prstatus;
+ struct loongarch_fpu_struct fpu;
+ };
+} QEMU_PACKED;
+
+#define LOONGARCH_NOTE_HEADER_SIZE offsetof(struct loongarch_note, prstatus)
+#define LOONGARCH_PRSTATUS_NOTE_SIZE \
+ (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_elf_prstatus))
+#define LOONGARCH_PRFPREG_NOTE_SIZE \
+ (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_fpu_struct))
+
+static void loongarch_note_init(struct loongarch_note *note, DumpState *s,
+ const char *name, Elf64_Word namesz,
+ Elf64_Word type, Elf64_Word descsz)
+{
+ memset(note, 0, sizeof(*note));
+
+ note->hdr.n_namesz = cpu_to_dump32(s, namesz);
+ note->hdr.n_descsz = cpu_to_dump32(s, descsz);
+ note->hdr.n_type = cpu_to_dump32(s, type);
+
+ memcpy(note->name, name, namesz);
+}
+
+static int loongarch_write_elf64_fprpreg(WriteCoreDumpFunction f,
+ CPULoongArchState *env, int cpuid,
+ DumpState *s)
+{
+ struct loongarch_note note;
+ int ret, i;
+
+ loongarch_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.fpu));
+ note.fpu.fcsr = cpu_to_dump64(s, env->fcsr0);
+ note.fpu.fcc = cpu_to_dump64(s, read_fcc(env));
+
+ for (i = 0; i < 32; ++i) {
+ note.fpu.fpr[i] = cpu_to_dump64(s, env->fpr[i].vreg.UD[0]);
+ }
+
+ ret = f(¬e, LOONGARCH_PRFPREG_NOTE_SIZE, s);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
+ int cpuid, DumpState *s)
+{
+ struct loongarch_note note;
+ CPULoongArchState *env = &LOONGARCH_CPU(cs)->env;
+ int ret, i;
+
+ loongarch_note_init(¬e, s, "CORE", 5, NT_PRSTATUS,
+ sizeof(note.prstatus));
+ note.prstatus.pr_pid = cpu_to_dump32(s, cpuid);
+ note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1);
+
+ for (i = 0; i < 32; ++i) {
+ note.prstatus.pr_reg.gpr[i] = cpu_to_dump64(s, env->gpr[i]);
+ }
+ note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, env->CSR_ERA);
+ note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, env->CSR_BADV);
+ ret = f(¬e, LOONGARCH_PRSTATUS_NOTE_SIZE, s);
+ if (ret < 0) {
+ return -1;
+ }
+
+ ret = loongarch_write_elf64_fprpreg(f, env, cpuid, s);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return ret;
+}
+
+int cpu_get_dump_info(ArchDumpInfo *info,
+ const GuestPhysBlockList *guest_phys_blocks)
+{
+ info->d_machine = EM_LOONGARCH;
+ info->d_endian = ELFDATA2LSB;
+ info->d_class = ELFCLASS64;
+
+ return 0;
+}
+
+ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
+{
+ size_t note_size = 0;
+
+ if (class == ELFCLASS64) {
+ note_size = LOONGARCH_PRSTATUS_NOTE_SIZE + LOONGARCH_PRFPREG_NOTE_SIZE;
+ }
+
+ return note_size * nr_cpus;
+}
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index f7b5dae7edd4118598b66b3355448ff1cda92f62..ee764f0bc7b0105a7c4ae5837cdfe7b704c8f3d0 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -17,6 +17,7 @@
#include "kvm/kvm_loongarch.h"
#include "exec/exec-all.h"
#include "cpu.h"
+#include "hw/qdev-properties.h"
#include "internals.h"
#include "fpu/softfloat-helpers.h"
#include "cpu-csr.h"
@@ -472,6 +473,18 @@ static void loongarch_la464_initfn(Object *obj)
env->cpucfg[20] = data;
env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
+
+ env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8);
+ env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f);
+ env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7);
+
+ env->CSR_PRCFG2 = 0x3ffff000;
+
+ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
+ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
+ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
+ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
+
loongarch_cpu_post_init(obj);
}
@@ -536,17 +549,19 @@ static void loongarch_cpu_reset_hold(Object *obj)
lacc->parent_phases.hold(obj);
}
+#ifdef CONFIG_TCG
env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3;
+#endif
env->fcsr0 = 0x0;
int n;
- /* Set csr registers value after reset */
+ /* Set csr registers value after reset, see the manual 6.4. */
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0);
- env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 1);
- env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 1);
+ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 0);
+ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 0);
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, FPE, 0);
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, SXE, 0);
@@ -566,11 +581,20 @@ static void loongarch_cpu_reset_hold(Object *obj)
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0);
env->CSR_TID = cs->cpu_index;
-
- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
+ /*
+ * Workaround for edk2-stable202408, CSR PGD register is set only if
+ * its value is equal to zero for boot cpu, it causes reboot issue.
+ *
+ * Here clear CSR registers relative with TLB.
+ */
+ env->CSR_PGDH = 0;
+ env->CSR_PGDL = 0;
+ env->CSR_PWCL = 0;
+ env->CSR_PWCH = 0;
+ env->CSR_STLBPS = 0;
+ env->CSR_EENTRY = 0;
+ env->CSR_TLBRENTRY = 0;
+ env->CSR_MERRENTRY = 0;
for (n = 0; n < 4; n++) {
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0);
@@ -581,9 +605,11 @@ static void loongarch_cpu_reset_hold(Object *obj)
#ifndef CONFIG_USER_ONLY
env->pc = 0x1c000000;
+#ifdef CONFIG_TCG
memset(env->tlb, 0, sizeof(env->tlb));
+#endif
if (kvm_enabled()) {
- kvm_arch_reset_vcpu(env);
+ kvm_arch_reset_vcpu(cs);
}
#endif
@@ -618,6 +644,17 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
lacc->parent_realize(dev, errp);
}
+static void loongarch_cpu_unrealizefn(DeviceState *dev)
+{
+ LoongArchCPUClass *mcc = LOONGARCH_CPU_GET_CLASS(dev);
+
+#ifndef CONFIG_USER_ONLY
+ cpu_remove_sync(CPU(dev));
+#endif
+
+ mcc->parent_unrealize(dev);
+}
+
static bool loongarch_get_lsx(Object *obj, Error **errp)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
@@ -670,71 +707,54 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp)
}
}
-static bool loongarch_get_pmu(Object *obj, Error **errp)
+static bool loongarch_get_lbt(Object *obj, Error **errp)
{
- LoongArchCPU *cpu = LOONGARCH_CPU(obj);
-
- return !!(FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMP));
+ return LOONGARCH_CPU(obj)->lbt != ON_OFF_AUTO_OFF;
}
-static void loongarch_set_pmu(Object *obj, bool value, Error **errp)
+static void loongarch_set_lbt(Object *obj, bool value, Error **errp)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMP, value);
+ cpu->lbt = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
}
-static void loongarch_get_pmnum(Object *obj, Visitor *v,
- const char *name, void *opaque,
- Error **errp)
+static bool loongarch_get_pmu(Object *obj, Error **errp)
{
- LoongArchCPU *cpu = LOONGARCH_CPU(obj);
- uint32_t value = FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMNUM);
-
- visit_type_uint32(v, name, &value, errp);
+ return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF;
}
-static void loongarch_set_pmnum(Object *obj, Visitor *v,
- const char *name, void *opaque,
- Error **errp)
+static void loongarch_set_pmu(Object *obj, bool value, Error **errp)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
- uint32_t *value= opaque;
- if (!visit_type_uint32(v, name, value, errp)) {
- return;
- }
- if ((*value <= PMNUM_MAX) && (*value > 0)) {
- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMNUM, *value -1);
- } else {
- error_report("Performance counter number need be in [1- %d]\n", PMNUM_MAX);
- exit(EXIT_FAILURE);
- }
+ cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
}
void loongarch_cpu_post_init(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
- if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) {
- object_property_add_bool(obj, "lsx", loongarch_get_lsx,
- loongarch_set_lsx);
- }
- if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) {
- object_property_add_bool(obj, "lasx", loongarch_get_lasx,
- loongarch_set_lasx);
- }
+ object_property_add_bool(obj, "lsx", loongarch_get_lsx,
+ loongarch_set_lsx);
+ object_property_add_bool(obj, "lasx", loongarch_get_lasx,
+ loongarch_set_lasx);
if (kvm_enabled()) {
+ cpu->lbt = ON_OFF_AUTO_AUTO;
+ object_property_add_bool(obj, "lbt", loongarch_get_lbt,
+ loongarch_set_lbt);
+ object_property_set_description(obj, "lbt",
+ "Set off to disable Binary Tranlation.");
+
+ cpu->pmu = ON_OFF_AUTO_AUTO;
object_property_add_bool(obj, "pmu", loongarch_get_pmu,
loongarch_set_pmu);
- if (FIELD_EX32(cpu->env.cpucfg[6], CPUCFG6, PMP)) {
- uint32_t value = 4;
- object_property_add(obj, "pmnum", "uint32",
- loongarch_get_pmnum,
- loongarch_set_pmnum, NULL,
- (void *)&value);
- }
+ object_property_set_description(obj, "pmu",
+ "Set off to performance monitor unit.");
+
+ } else {
+ cpu->lbt = ON_OFF_AUTO_OFF;
}
}
@@ -778,8 +798,7 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
int i;
qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc);
- qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0,
- get_float_exception_flags(&env->fp_status));
+ qemu_fprintf(f, " FCSR0 0x%08x\n", env->fcsr0);
/* gpr */
for (i = 0; i < 32; i++) {
@@ -841,6 +860,7 @@ static struct TCGCPUOps loongarch_tcg_ops = {
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps loongarch_sysemu_ops = {
+ .write_elf64_note = loongarch_cpu_write_elf64_note,
.get_phys_page_debug = loongarch_cpu_get_phys_page_debug,
};
@@ -852,6 +872,15 @@ static int64_t loongarch_cpu_get_arch_id(CPUState *cs)
}
#endif
+static Property loongarch_cpu_properties[] = {
+ DEFINE_PROP_INT32("socket-id", LoongArchCPU, socket_id, 0),
+ DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, 0),
+ DEFINE_PROP_INT32("thread-id", LoongArchCPU, thread_id, 0),
+ DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
+
+ DEFINE_PROP_END_OF_LIST()
+};
+
static void loongarch_cpu_class_init(ObjectClass *c, void *data)
{
LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c);
@@ -859,8 +888,11 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
DeviceClass *dc = DEVICE_CLASS(c);
ResettableClass *rc = RESETTABLE_CLASS(c);
+ device_class_set_props(dc, loongarch_cpu_properties);
device_class_set_parent_realize(dc, loongarch_cpu_realizefn,
&lacc->parent_realize);
+ device_class_set_parent_unrealize(dc, loongarch_cpu_unrealizefn,
+ &lacc->parent_unrealize);
resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL,
&lacc->parent_phases);
@@ -882,6 +914,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
#ifdef CONFIG_TCG
cc->tcg_ops = &loongarch_tcg_ops;
#endif
+ dc->user_creatable = true;
}
static const gchar *loongarch32_gdb_arch_name(CPUState *cs)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 0ed24051af97bc19db79ac40d673c9fc7804665b..9af622aba5c77fb213b11b422c67f95fe87f3624 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -18,6 +18,7 @@
#endif
#include "cpu-csr.h"
#include "cpu-qom.h"
+#include "qapi/qapi-types-common.h"
#define IOCSRF_TEMP 0
#define IOCSRF_NODECNT 1
@@ -155,6 +156,7 @@ FIELD(CPUCFG2, LLFTP_VER, 15, 3)
FIELD(CPUCFG2, LBT_X86, 18, 1)
FIELD(CPUCFG2, LBT_ARM, 19, 1)
FIELD(CPUCFG2, LBT_MIPS, 20, 1)
+FIELD(CPUCFG2, LBT_ALL, 18, 3)
FIELD(CPUCFG2, LSPW, 21, 1)
FIELD(CPUCFG2, LAM, 22, 1)
@@ -275,6 +277,7 @@ union fpr_t {
VReg vreg;
};
+#ifdef CONFIG_TCG
struct LoongArchTLB {
uint64_t tlb_misc;
/* Fields corresponding to CSR_TLBELO0/1 */
@@ -282,23 +285,35 @@ struct LoongArchTLB {
uint64_t tlb_entry1;
};
typedef struct LoongArchTLB LoongArchTLB;
+#endif
+
+enum loongarch_features {
+ LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */
+ LOONGARCH_FEATURE_PMU,
+};
+
+typedef struct LoongArchBT {
+ /* scratch registers */
+ uint64_t scr0;
+ uint64_t scr1;
+ uint64_t scr2;
+ uint64_t scr3;
+ /* loongarch eflags */
+ uint32_t eflags;
+ uint32_t ftop;
+} lbt_t;
typedef struct CPUArchState {
uint64_t gpr[32];
uint64_t pc;
fpr_t fpr[32];
- float_status fp_status;
bool cf[8];
-
uint32_t fcsr0;
- uint32_t fcsr0_mask;
+ lbt_t lbt;
uint32_t cpucfg[21];
- uint64_t lladdr; /* LL virtual address compared against SC */
- uint64_t llval;
-
/* LoongArch CSRs */
uint64_t CSR_CRMD;
uint64_t CSR_PRMD;
@@ -354,9 +369,20 @@ typedef struct CPUArchState {
uint64_t CSR_DBG;
uint64_t CSR_DERA;
uint64_t CSR_DSAVE;
+ struct {
+ uint64_t guest_addr;
+ } stealtime;
+#ifdef CONFIG_TCG
+ float_status fp_status;
+ uint32_t fcsr0_mask;
+ uint64_t lladdr; /* LL virtual address compared against SC */
+ uint64_t llval;
+#endif
#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_TCG
LoongArchTLB tlb[LOONGARCH_TLB_MAX];
+#endif
AddressSpace *address_space_iocsr;
bool load_elf;
@@ -364,12 +390,20 @@ typedef struct CPUArchState {
uint32_t mp_state;
/* Store ipistate to access from this struct */
DeviceState *ipistate;
+
+ struct loongarch_boot_info *boot_info;
#endif
struct {
uint64_t guest_addr;
} st;
} CPULoongArchState;
+typedef struct LoongArchCPUTopo {
+ int32_t socket_id; /* socket-id of this VCPU */
+ int32_t core_id; /* core-id of this VCPU */
+ int32_t thread_id; /* thread-id of this VCPU */
+} LoongArchCPUTopo;
+
/**
* LoongArchCPU:
* @env: #CPULoongArchState
@@ -382,6 +416,12 @@ struct ArchCPU {
CPULoongArchState env;
QEMUTimer timer;
uint32_t phy_id;
+ OnOffAuto lbt;
+ OnOffAuto pmu;
+ int32_t socket_id; /* socket-id of this VCPU */
+ int32_t core_id; /* core-id of this VCPU */
+ int32_t thread_id; /* thread-id of this VCPU */
+ int32_t node_id; /* NUMA node this CPU belongs to */
/* 'compatible' string for this CPU for Linux device trees */
const char *dtb_compatible;
@@ -400,6 +440,7 @@ struct LoongArchCPUClass {
CPUClass parent_class;
DeviceRealize parent_realize;
+ DeviceUnrealize parent_unrealize;
ResettablePhases parent_phases;
};
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index f68d63f466e38a28c02a14c0da156c9caea61d4d..39037eecb4bd566b838e808ad2aa3875f1eebd02 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -11,6 +11,7 @@
#include "internals.h"
#include "cpu-csr.h"
+#ifdef CONFIG_TCG
static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
int access_type, int index, int mmu_idx)
@@ -154,6 +155,14 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
return TLBRET_NOMATCH;
}
+#else
+static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
+ int *prot, target_ulong address,
+ MMUAccessType access_type, int mmu_idx)
+{
+ return TLBRET_NOMATCH;
+}
+#endif
static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va,
target_ulong dmw)
diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c
index 5fc2f19e965e101a269c923a101c9be05d2bd2ac..cc72680c38b282bdcc2889537fa902483510089b 100644
--- a/target/loongarch/gdbstub.c
+++ b/target/loongarch/gdbstub.c
@@ -33,28 +33,29 @@ void write_fcc(CPULoongArchState *env, uint64_t val)
int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
- LoongArchCPU *cpu = LOONGARCH_CPU(cs);
- CPULoongArchState *env = &cpu->env;
- uint64_t val;
-
- if (0 <= n && n < 32) {
- val = env->gpr[n];
- } else if (n == 32) {
- /* orig_a0 */
- val = 0;
- } else if (n == 33) {
- val = env->pc;
- } else if (n == 34) {
- val = env->CSR_BADV;
- }
+ CPULoongArchState *env = cpu_env(cs);
if (0 <= n && n <= 34) {
+ uint64_t val;
+
+ if (n < 32) {
+ val = env->gpr[n];
+ } else if (n == 32) {
+ /* orig_a0 */
+ val = 0;
+ } else if (n == 33) {
+ val = env->pc;
+ } else /* if (n == 34) */ {
+ val = env->CSR_BADV;
+ }
+
if (is_la64(env)) {
return gdb_get_reg64(mem_buf, val);
} else {
return gdb_get_reg32(mem_buf, val);
}
}
+
return 0;
}
@@ -67,10 +68,10 @@ int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
int length = 0;
if (is_la64(env)) {
- tmp = ldq_p(mem_buf);
+ tmp = ldq_le_p(mem_buf);
read_length = 8;
} else {
- tmp = ldl_p(mem_buf);
+ tmp = ldl_le_p(mem_buf);
read_length = 4;
}
@@ -103,13 +104,13 @@ static int loongarch_gdb_set_fpu(CPULoongArchState *env,
int length = 0;
if (0 <= n && n < 32) {
- env->fpr[n].vreg.D(0) = ldq_p(mem_buf);
+ env->fpr[n].vreg.D(0) = ldq_le_p(mem_buf);
length = 8;
} else if (32 <= n && n < 40) {
env->cf[n - 32] = ldub_p(mem_buf);
length = 1;
} else if (n == 40) {
- env->fcsr0 = ldl_p(mem_buf);
+ env->fcsr0 = ldl_le_p(mem_buf);
length = 4;
}
return length;
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index 944153b180e48fdc5786a20b6c4981b08b26f046..1a02427627d8e968a1b829bdb7915a212860856e 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -72,5 +72,7 @@ void write_fcc(CPULoongArchState *env, uint64_t val);
int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n);
int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n);
void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs);
+int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu,
+ int cpuid, DumpState *s);
#endif
diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
index 5c882701327f97b46735e805e554a9a6d4c62ee5..0acdd5c4c166aebb1c52635e606b1b9feec6d228 100644
--- a/target/loongarch/kvm/kvm.c
+++ b/target/loongarch/kvm/kvm.c
@@ -9,6 +9,7 @@
#include
#include
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
@@ -34,6 +35,55 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
KVM_CAP_LAST_INFO
};
+static int kvm_get_stealtime(CPUState *cs)
+{
+ CPULoongArchState *env = cpu_env(cs);
+ int err;
+ struct kvm_device_attr attr = {
+ .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL,
+ .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA,
+ .addr = (uint64_t)&env->stealtime.guest_addr,
+ };
+
+ err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr);
+ if (err) {
+ return 0;
+ }
+
+ err = kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, attr);
+ if (err) {
+ error_report("PVTIME: KVM_GET_DEVICE_ATTR: %s", strerror(errno));
+ return err;
+ }
+
+ return 0;
+}
+
+static int kvm_set_stealtime(CPUState *cs)
+{
+ CPULoongArchState *env = cpu_env(cs);
+ int err;
+ struct kvm_device_attr attr = {
+ .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL,
+ .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA,
+ .addr = (uint64_t)&env->stealtime.guest_addr,
+ };
+
+ err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr);
+ if (err) {
+ return 0;
+ }
+
+ err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, attr);
+ if (err) {
+ error_report("PVTIME: KVM_SET_DEVICE_ATTR %s with gpa "TARGET_FMT_lx,
+ strerror(errno), env->stealtime.guest_addr);
+ return err;
+ }
+
+ return 0;
+}
+
static int kvm_loongarch_get_regs_core(CPUState *cs)
{
int ret = 0;
@@ -485,9 +535,64 @@ static int kvm_loongarch_put_regs_fp(CPUState *cs)
return ret;
}
-void kvm_arch_reset_vcpu(CPULoongArchState *env)
+static int kvm_loongarch_put_lbt(CPUState *cs)
+{
+ CPULoongArchState *env = cpu_env(cs);
+ uint64_t val;
+ int ret;
+
+ /* check whether vm support LBT firstly */
+ if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LBT_ALL) != 7) {
+ return 0;
+ }
+
+ /* set six LBT registers including scr0-scr3, eflags, ftop */
+ ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR0, &env->lbt.scr0);
+ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR1, &env->lbt.scr1);
+ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR2, &env->lbt.scr2);
+ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR3, &env->lbt.scr3);
+ /*
+ * Be cautious, KVM_REG_LOONGARCH_LBT_FTOP is defined as 64-bit however
+ * lbt.ftop is 32-bit; the same with KVM_REG_LOONGARCH_LBT_EFLAGS register
+ */
+ val = env->lbt.eflags;
+ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_EFLAGS, &val);
+ val = env->lbt.ftop;
+ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_FTOP, &val);
+
+ return ret;
+}
+
+static int kvm_loongarch_get_lbt(CPUState *cs)
{
+ CPULoongArchState *env = cpu_env(cs);
+ uint64_t val;
+ int ret;
+
+ /* check whether vm support LBT firstly */
+ if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LBT_ALL) != 7) {
+ return 0;
+ }
+
+ /* get six LBT registers including scr0-scr3, eflags, ftop */
+ ret = kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR0, &env->lbt.scr0);
+ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR1, &env->lbt.scr1);
+ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR2, &env->lbt.scr2);
+ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR3, &env->lbt.scr3);
+ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_EFLAGS, &val);
+ env->lbt.eflags = (uint32_t)val;
+ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_FTOP, &val);
+ env->lbt.ftop = (uint32_t)val;
+
+ return ret;
+}
+
+void kvm_arch_reset_vcpu(CPUState *cs)
+{
+ CPULoongArchState *env = cpu_env(cs);
+
env->mp_state = KVM_MP_STATE_RUNNABLE;
+ kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, 0);
}
static int kvm_loongarch_get_mpstate(CPUState *cs)
@@ -579,53 +684,6 @@ static int kvm_check_cpucfg2(CPUState *cs)
return ret;
}
-static int kvm_check_cpucfg6(CPUState *cs)
-{
- int ret;
- uint64_t val;
- struct kvm_device_attr attr = {
- .group = KVM_LOONGARCH_VCPU_CPUCFG,
- .attr = 6,
- .addr = (uint64_t)&val,
- };
- LoongArchCPU *cpu = LOONGARCH_CPU(cs);
- CPULoongArchState *env = &cpu->env;
-
- ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr);
- if (!ret) {
- kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr);
-
- if (FIELD_EX32(env->cpucfg[6], CPUCFG6, PMP)) {
- /* Check PMP */
- if (!FIELD_EX32(val, CPUCFG6, PMP)) {
- error_report("'pmu' feature not supported by KVM on this host"
- " Please disable 'pmu' with "
- "'... -cpu XXX,pmu=off ...'\n");
- exit(EXIT_FAILURE);
- }
- /* Check PMNUM */
- int guest_pmnum = FIELD_EX32(env->cpucfg[6], CPUCFG6, PMNUM);
- int host_pmnum = FIELD_EX32(val, CPUCFG6, PMNUM);
- if (guest_pmnum > host_pmnum){
- warn_report("The guest pmnum %d larger than KVM support %d\n",
- guest_pmnum, host_pmnum);
- env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6,
- PMNUM, host_pmnum);
- }
- /* Check PMBITS */
- int guest_pmbits = FIELD_EX32(env->cpucfg[6], CPUCFG6, PMBITS);
- int host_pmbits = FIELD_EX32(val, CPUCFG6, PMBITS);
- if (guest_pmbits != host_pmbits) {
- warn_report("The host not support PMBITS %d\n", guest_pmbits);
- env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6,
- PMBITS, host_pmbits);
- }
- }
- }
-
- return ret;
-}
-
static int kvm_loongarch_put_cpucfg(CPUState *cs)
{
int i, ret = 0;
@@ -640,12 +698,6 @@ static int kvm_loongarch_put_cpucfg(CPUState *cs)
return ret;
}
}
- if (i == 6) {
- ret = kvm_check_cpucfg6(cs);
- if (ret) {
- return ret;
- }
- }
val = env->cpucfg[i];
ret = kvm_set_one_reg(cs, KVM_IOC_CPUCFG(i), &val);
if (ret < 0) {
@@ -655,56 +707,6 @@ static int kvm_loongarch_put_cpucfg(CPUState *cs)
return ret;
}
-int kvm_loongarch_put_pvtime(LoongArchCPU *cpu)
-{
- CPULoongArchState *env = &cpu->env;
- int err;
- struct kvm_device_attr attr = {
- .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL,
- .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA,
- .addr = (uint64_t)&env->st.guest_addr,
- };
-
- err = kvm_vcpu_ioctl(CPU(cpu), KVM_HAS_DEVICE_ATTR, attr);
- if (err != 0) {
- /* It's ok even though kvm has not such attr */
- return 0;
- }
-
- err = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_DEVICE_ATTR, attr);
- if (err != 0) {
- error_report("PVTIME IPA: KVM_SET_DEVICE_ATTR: %s", strerror(-err));
- return err;
- }
-
- return 0;
-}
-
-int kvm_loongarch_get_pvtime(LoongArchCPU *cpu)
-{
- CPULoongArchState *env = &cpu->env;
- int err;
- struct kvm_device_attr attr = {
- .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL,
- .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA,
- .addr = (uint64_t)&env->st.guest_addr,
- };
-
- err = kvm_vcpu_ioctl(CPU(cpu), KVM_HAS_DEVICE_ATTR, attr);
- if (err != 0) {
- /* It's ok even though kvm has not such attr */
- return 0;
- }
-
- err = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_DEVICE_ATTR, attr);
- if (err != 0) {
- error_report("PVTIME IPA: KVM_GET_DEVICE_ATTR: %s", strerror(-err));
- return err;
- }
-
- return 0;
-}
-
int kvm_arch_get_registers(CPUState *cs)
{
int ret;
@@ -714,6 +716,11 @@ int kvm_arch_get_registers(CPUState *cs)
return ret;
}
+ ret = kvm_loongarch_get_cpucfg(cs);
+ if (ret) {
+ return ret;
+ }
+
ret = kvm_loongarch_get_csr(cs);
if (ret) {
return ret;
@@ -724,12 +731,17 @@ int kvm_arch_get_registers(CPUState *cs)
return ret;
}
- ret = kvm_loongarch_get_mpstate(cs);
+ ret = kvm_loongarch_get_lbt(cs);
if (ret) {
return ret;
}
- ret = kvm_loongarch_get_cpucfg(cs);
+ ret = kvm_get_stealtime(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_loongarch_get_mpstate(cs);
return ret;
}
@@ -742,6 +754,11 @@ int kvm_arch_put_registers(CPUState *cs, int level)
return ret;
}
+ ret = kvm_loongarch_put_cpucfg(cs);
+ if (ret) {
+ return ret;
+ }
+
ret = kvm_loongarch_put_csr(cs, level);
if (ret) {
return ret;
@@ -752,12 +769,23 @@ int kvm_arch_put_registers(CPUState *cs, int level)
return ret;
}
- ret = kvm_loongarch_put_mpstate(cs);
+ ret = kvm_loongarch_put_lbt(cs);
if (ret) {
return ret;
}
- ret = kvm_loongarch_put_cpucfg(cs);
+ if (level >= KVM_PUT_FULL_STATE) {
+ /*
+ * only KVM_PUT_FULL_STATE is required, kvm kernel will clear
+ * guest_addr for KVM_PUT_RESET_STATE
+ */
+ ret = kvm_set_stealtime(cs);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ ret = kvm_loongarch_put_mpstate(cs);
return ret;
}
@@ -783,17 +811,112 @@ static void kvm_loongarch_vm_stage_change(void *opaque, bool running,
}
}
+static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature)
+{
+ int ret;
+ struct kvm_device_attr attr;
+
+ switch (feature) {
+ case LOONGARCH_FEATURE_LBT:
+ /*
+ * Return all if all the LBT features are supported such as:
+ * KVM_LOONGARCH_VM_FEAT_X86BT
+ * KVM_LOONGARCH_VM_FEAT_ARMBT
+ * KVM_LOONGARCH_VM_FEAT_MIPSBT
+ */
+ attr.group = KVM_LOONGARCH_VM_FEAT_CTRL;
+ attr.attr = KVM_LOONGARCH_VM_FEAT_X86BT;
+ ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr);
+ attr.attr = KVM_LOONGARCH_VM_FEAT_ARMBT;
+ ret |= kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr);
+ attr.attr = KVM_LOONGARCH_VM_FEAT_MIPSBT;
+ ret |= kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr);
+ return (ret == 0);
+
+ case LOONGARCH_FEATURE_PMU:
+ attr.group = KVM_LOONGARCH_VM_FEAT_CTRL;
+ attr.attr = KVM_LOONGARCH_VM_FEAT_PMU;
+ ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr);
+ return (ret == 0);
+
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+static int kvm_cpu_check_lbt(CPUState *cs, Error **errp)
+{
+ CPULoongArchState *env = cpu_env(cs);
+ LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+ bool kvm_supported;
+
+ kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_LBT);
+ if (cpu->lbt == ON_OFF_AUTO_ON) {
+ if (kvm_supported) {
+ env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LBT_ALL, 7);
+ } else {
+ error_setg(errp, "'lbt' feature not supported by KVM on this host");
+ return -ENOTSUP;
+ }
+ } else if ((cpu->lbt == ON_OFF_AUTO_AUTO) && kvm_supported) {
+ env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LBT_ALL, 7);
+ }
+
+ return 0;
+}
+
+static int kvm_cpu_check_pmu(CPUState *cs, Error **errp)
+{
+ LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+ CPULoongArchState *env = cpu_env(cs);
+ bool kvm_supported;
+
+ kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PMU);
+ if (cpu->pmu == ON_OFF_AUTO_ON) {
+ if (!kvm_supported) {
+ error_setg(errp, "'pmu' feature not supported by KVM on the host");
+ return -ENOTSUP;
+ }
+ } else if (cpu->pmu != ON_OFF_AUTO_AUTO) {
+ /* disable pmu if ON_OFF_AUTO_OFF is set */
+ kvm_supported = false;
+ }
+
+ if (kvm_supported) {
+ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMP, 1);
+ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMNUM, 3);
+ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMBITS, 63);
+ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, UPM, 1);
+ }
+ return 0;
+}
+
int kvm_arch_init_vcpu(CPUState *cs)
{
uint64_t val;
+ int ret;
+ Error *local_err = NULL;
+ ret = 0;
qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs);
if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) {
brk_insn = val;
}
- return 0;
+ ret = kvm_cpu_check_lbt(cs, &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ }
+
+ ret = kvm_cpu_check_pmu(cs, &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ }
+
+ return ret;
}
int kvm_arch_destroy_vcpu(CPUState *cs)
@@ -840,6 +963,10 @@ int kvm_arch_get_default_type(MachineState *ms)
int kvm_arch_init(MachineState *ms, KVMState *s)
{
cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
+ if(!kvm_vm_check_attr(kvm_state, KVM_LOONGARCH_VM_HAVE_IRQCHIP, KVM_LOONGARCH_VM_HAVE_IRQCHIP)) {
+ s->kernel_irqchip_allowed = false;
+ }
+
return 0;
}
diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h
index 551878a725c10f6ac9a4cc9218b0d362f6df741c..1051a341ec2633647e55f3c37ad5998c4f02ba06 100644
--- a/target/loongarch/kvm/kvm_loongarch.h
+++ b/target/loongarch/kvm/kvm_loongarch.h
@@ -11,8 +11,6 @@
#define QEMU_KVM_LOONGARCH_H
int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level);
-void kvm_arch_reset_vcpu(CPULoongArchState *env);
-int kvm_loongarch_put_pvtime(LoongArchCPU *cpu);
-int kvm_loongarch_get_pvtime(LoongArchCPU *cpu);
+void kvm_arch_reset_vcpu(CPUState *cs);
#endif
diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c
index 2612f43de97dd4673f9f79db2d032c69e2390c3c..dc78a3ffa22b90f73f695f24b2d22b8f5221f13e 100644
--- a/target/loongarch/loongarch-qmp-cmds.c
+++ b/target/loongarch/loongarch-qmp-cmds.c
@@ -42,7 +42,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
}
static const char *cpu_model_advertised_features[] = {
- "lsx", "lasx", "pmu", "pmnum", NULL
+ "lsx", "lasx", "lbt", "pmu", NULL
};
CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c
index ec5abe56db6aae771631bbf8ec4711c8a2ed8014..57abdddc09d4f129a1a9dd89e73e8eed340a2129 100644
--- a/target/loongarch/machine.c
+++ b/target/loongarch/machine.c
@@ -8,6 +8,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "migration/cpu.h"
+#include "sysemu/tcg.h"
#include "vec.h"
#include "kvm/kvm_loongarch.h"
#include "sysemu/kvm.h"
@@ -111,9 +112,38 @@ static const VMStateDescription vmstate_lasx = {
},
};
+static bool lbt_needed(void *opaque)
+{
+ LoongArchCPU *cpu = opaque;
+
+ return !!FIELD_EX64(cpu->env.cpucfg[2], CPUCFG2, LBT_ALL);
+}
+
+static const VMStateDescription vmstate_lbt = {
+ .name = "cpu/lbt",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .needed = lbt_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT64(env.lbt.scr0, LoongArchCPU),
+ VMSTATE_UINT64(env.lbt.scr1, LoongArchCPU),
+ VMSTATE_UINT64(env.lbt.scr2, LoongArchCPU),
+ VMSTATE_UINT64(env.lbt.scr3, LoongArchCPU),
+ VMSTATE_UINT32(env.lbt.eflags, LoongArchCPU),
+ VMSTATE_UINT32(env.lbt.ftop, LoongArchCPU),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+static bool tlb_needed(void *opaque)
+{
+ return tcg_enabled();
+}
+
/* TLB state */
-const VMStateDescription vmstate_tlb = {
- .name = "cpu/tlb",
+static const VMStateDescription vmstate_tlb_entry = {
+ .name = "cpu/tlb_entry",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
@@ -124,32 +154,25 @@ const VMStateDescription vmstate_tlb = {
}
};
-static int cpu_post_load(void *opaque, int version_id)
-{
-#ifdef CONFIG_KVM
- LoongArchCPU *cpu = opaque;
- kvm_loongarch_put_pvtime(cpu);
-#endif
- return 0;
-}
-
-static int cpu_pre_save(void *opaque)
-{
-#ifdef CONFIG_KVM
- LoongArchCPU *cpu = opaque;
- kvm_loongarch_get_pvtime(cpu);
+static const VMStateDescription vmstate_tlb = {
+ .name = "cpu/tlb",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .needed = tlb_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX,
+ 0, vmstate_tlb_entry, LoongArchTLB),
+ VMSTATE_END_OF_LIST()
+ }
+};
#endif
- return 0;
-}
/* LoongArch CPU state */
const VMStateDescription vmstate_loongarch_cpu = {
.name = "cpu",
- .version_id = 1,
- .minimum_version_id = 1,
- .post_load = cpu_post_load,
- .pre_save = cpu_pre_save,
- .fields = (VMStateField[]) {
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .fields = (const VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32),
VMSTATE_UINTTL(env.pc, LoongArchCPU),
@@ -212,11 +235,10 @@ const VMStateDescription vmstate_loongarch_cpu = {
VMSTATE_UINT64(env.CSR_DBG, LoongArchCPU),
VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU),
VMSTATE_UINT64(env.CSR_DSAVE, LoongArchCPU),
- /* TLB */
- VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX,
- 0, vmstate_tlb, LoongArchTLB),
VMSTATE_UINT64(kvm_state_counter, LoongArchCPU),
+ /* PV steal time */
+ VMSTATE_UINT64(env.stealtime.guest_addr, LoongArchCPU),
VMSTATE_END_OF_LIST()
},
@@ -224,6 +246,10 @@ const VMStateDescription vmstate_loongarch_cpu = {
&vmstate_fpu,
&vmstate_lsx,
&vmstate_lasx,
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+ &vmstate_tlb,
+#endif
+ &vmstate_lbt,
NULL
}
};
diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
index e002e9aaf65698f79fb5ab094342c4f858cda9dd..7817318287d0f8c460218bbc63ea60614efae50c 100644
--- a/target/loongarch/meson.build
+++ b/target/loongarch/meson.build
@@ -8,6 +8,7 @@ loongarch_ss.add(files(
loongarch_system_ss = ss.source_set()
loongarch_system_ss.add(files(
+ 'arch_dump.c',
'cpu_helper.c',
'loongarch-qmp-cmds.c',
'machine.c',
diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc
index 2f4bd6ff28819eb4c665a3775e6867f37294e6a7..377307785aab4837bc181f1691632e7970a9889d 100644
--- a/target/loongarch/tcg/insn_trans/trans_shift.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc
@@ -67,19 +67,9 @@ static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2)
tcg_gen_rotr_tl(dest, src1, t0);
}
-static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a)
+static void gen_sari_w(TCGv dest, TCGv src1, target_long imm)
{
- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
- TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO);
-
- if (!avail_64(ctx)) {
- return false;
- }
-
- tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm);
- gen_set_gpr(a->rd, dest, EXT_NONE);
-
- return true;
+ tcg_gen_sextract_tl(dest, src1, imm, 32 - imm);
}
TRANS(sll_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w)
@@ -94,6 +84,7 @@ TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl)
TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl)
TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl)
TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl)
+TRANS(srai_w, ALL, gen_rri_c, EXT_NONE, EXT_NONE, gen_sari_w)
TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl)
TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w)
TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl)
diff --git a/tests/qtest/libqos/loongarch-virt-machine.c b/tests/qtest/libqos/loongarch-virt-machine.c
new file mode 100644
index 0000000000000000000000000000000000000000..c12089c015df6d7f18144e3fe99b588354e45329
--- /dev/null
+++ b/tests/qtest/libqos/loongarch-virt-machine.c
@@ -0,0 +1,114 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ */
+
+#include "qemu/osdep.h"
+#include "../libqtest.h"
+#include "qemu/module.h"
+#include "libqos-malloc.h"
+#include "qgraph.h"
+#include "virtio-mmio.h"
+#include "generic-pcihost.h"
+#include "hw/pci/pci_regs.h"
+
+#define LOONGARCH_PAGE_SIZE 0x1000
+#define LOONGARCH_VIRT_RAM_ADDR 0x100000
+#define LOONGARCH_VIRT_RAM_SIZE 0xFF00000
+
+#define LOONGARCH_VIRT_PIO_BASE 0x18000000
+#define LOONGARCH_VIRT_PCIE_PIO_OFFSET 0x4000
+#define LOONGARCH_VIRT_PCIE_PIO_LIMIT 0x10000
+#define LOONGARCH_VIRT_PCIE_ECAM_BASE 0x20000000
+#define LOONGARCH_VIRT_PCIE_MMIO32_BASE 0x40000000
+#define LOONGARCH_VIRT_PCIE_MMIO32_LIMIT 0x80000000
+
+typedef struct QVirtMachine QVirtMachine;
+
+struct QVirtMachine {
+ QOSGraphObject obj;
+ QGuestAllocator alloc;
+ QVirtioMMIODevice virtio_mmio;
+ QGenericPCIHost bridge;
+};
+
+static void virt_destructor(QOSGraphObject *obj)
+{
+ QVirtMachine *machine = (QVirtMachine *) obj;
+ alloc_destroy(&machine->alloc);
+}
+
+static void *virt_get_driver(void *object, const char *interface)
+{
+ QVirtMachine *machine = object;
+ if (!g_strcmp0(interface, "memory")) {
+ return &machine->alloc;
+ }
+
+ fprintf(stderr, "%s not present in loongarch/virtio\n", interface);
+ g_assert_not_reached();
+}
+
+static QOSGraphObject *virt_get_device(void *obj, const char *device)
+{
+ QVirtMachine *machine = obj;
+ if (!g_strcmp0(device, "generic-pcihost")) {
+ return &machine->bridge.obj;
+ } else if (!g_strcmp0(device, "virtio-mmio")) {
+ return &machine->virtio_mmio.obj;
+ }
+
+ fprintf(stderr, "%s not present in loongarch/virt\n", device);
+ g_assert_not_reached();
+}
+
+static void loongarch_config_qpci_bus(QGenericPCIBus *qpci)
+{
+ qpci->gpex_pio_base = LOONGARCH_VIRT_PIO_BASE;
+ qpci->bus.pio_alloc_ptr = LOONGARCH_VIRT_PCIE_PIO_OFFSET;
+ qpci->bus.pio_limit = LOONGARCH_VIRT_PCIE_PIO_LIMIT;
+ qpci->bus.mmio_alloc_ptr = LOONGARCH_VIRT_PCIE_MMIO32_BASE;
+ qpci->bus.mmio_limit = LOONGARCH_VIRT_PCIE_MMIO32_LIMIT;
+ qpci->ecam_alloc_ptr = LOONGARCH_VIRT_PCIE_ECAM_BASE;
+}
+
+static void *qos_create_machine_loongarch_virt(QTestState *qts)
+{
+ QVirtMachine *machine = g_new0(QVirtMachine, 1);
+
+ alloc_init(&machine->alloc, 0,
+ LOONGARCH_VIRT_RAM_ADDR,
+ LOONGARCH_VIRT_RAM_ADDR + LOONGARCH_VIRT_RAM_SIZE,
+ LOONGARCH_PAGE_SIZE);
+
+ qos_create_generic_pcihost(&machine->bridge, qts, &machine->alloc);
+ loongarch_config_qpci_bus(&machine->bridge.pci);
+
+ machine->obj.get_device = virt_get_device;
+ machine->obj.get_driver = virt_get_driver;
+ machine->obj.destructor = virt_destructor;
+ return machine;
+}
+
+static void virt_machine_register_nodes(void)
+{
+ qos_node_create_machine_args("loongarch64/virt",
+ qos_create_machine_loongarch_virt,
+ " -cpu la464");
+ qos_node_contains("loongarch64/virt", "generic-pcihost", NULL);
+}
+
+libqos_init(virt_machine_register_nodes);
diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
index 90aae42a22535f0dd8fe2fe7f9a615dd1b9a805b..482c9b2aab99dc0ef0dc4d1c7a2ce6468ecb640a 100644
--- a/tests/qtest/libqos/meson.build
+++ b/tests/qtest/libqos/meson.build
@@ -60,6 +60,7 @@ libqos_srcs = files(
'arm-xilinx-zynq-a9-machine.c',
'ppc64_pseries-machine.c',
'x86_64_pc-machine.c',
+ 'loongarch-virt-machine.c',
)
if have_virtfs