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