diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c index f5bbc3325556903e6482d8517bed23ae0b861e8e..2e7c764b7cbc95d67c7af505aa09e6ec26568186 100644 --- a/hw/intc/loongarch_extioi_kvm.c +++ b/hw/intc/loongarch_extioi_kvm.c @@ -18,8 +18,32 @@ 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); + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS, + addr, val, is_write, &error_abort); +} + +static void kvm_extioi_access_sw_status(int fd, uint64_t addr, + void *val, bool is_write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS, + addr, val, is_write, &error_abort); +} + +static void kvm_extioi_save_load_sw_status(void *opaque, bool is_write) +{ + KVMLoongArchExtIOI *s = (KVMLoongArchExtIOI *)opaque; + KVMLoongArchExtIOIClass *class = KVM_LOONGARCH_EXTIOI_GET_CLASS(s); + int fd = class->dev_fd; + int addr; + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU; + kvm_extioi_access_sw_status(fd, addr, (void *)&s->num_cpu, is_write); + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE; + kvm_extioi_access_sw_status(fd, addr, (void *)&s->features, is_write); + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE; + kvm_extioi_access_sw_status(fd, addr, (void *)&s->status, is_write); } static int kvm_loongarch_extioi_pre_save(void *opaque) @@ -41,6 +65,8 @@ static int kvm_loongarch_extioi_pre_save(void *opaque) kvm_extioi_access_regs(fd, EXTIOI_COREISR_START, (void *)s->coreisr, false); + kvm_extioi_save_load_sw_status(opaque, false); + return 0; } @@ -61,12 +87,19 @@ static int kvm_loongarch_extioi_post_load(void *opaque, int version_id) (void *)s->sw_coremap, true); kvm_extioi_access_regs(fd, EXTIOI_COREISR_START, (void *)s->coreisr, true); + kvm_extioi_save_load_sw_status(opaque, true); + + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED, + NULL, true, &error_abort); + return 0; } static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) { KVMLoongArchExtIOIClass *extioi_class = KVM_LOONGARCH_EXTIOI_GET_CLASS(dev); + KVMLoongArchExtIOI *s = KVM_LOONGARCH_EXTIOI(dev); struct kvm_create_device cd = {0}; Error *err = NULL; int ret,i; @@ -77,6 +110,10 @@ static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) return; } + if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { + s->features |= EXTIOI_VIRT_HAS_FEATURES; + } + if (!extioi_class->is_created) { cd.type = KVM_DEV_TYPE_LA_EXTIOI; ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); @@ -87,6 +124,15 @@ static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) } extioi_class->is_created = true; extioi_class->dev_fd = cd.fd; + + kvm_device_access(cd.fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU, + &s->num_cpu, true, NULL); + + kvm_device_access(cd.fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE, + &s->features, true, NULL); + fprintf(stdout, "Create LoongArch extioi irqchip in KVM done!\n"); } @@ -102,8 +148,8 @@ static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_kvm_extioi_core = { .name = "kvm-extioi-single", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .pre_save = kvm_loongarch_extioi_pre_save, .post_load = kvm_loongarch_extioi_post_load, .fields = (VMStateField[]) { @@ -119,10 +165,20 @@ static const VMStateDescription vmstate_kvm_extioi_core = { EXTIOI_IRQS_IPMAP_SIZE / 4), VMSTATE_UINT32_ARRAY(coremap, KVMLoongArchExtIOI, EXTIOI_IRQS / 4), VMSTATE_UINT8_ARRAY(sw_coremap, KVMLoongArchExtIOI, EXTIOI_IRQS), + VMSTATE_UINT32(num_cpu, KVMLoongArchExtIOI), + VMSTATE_UINT32(features, KVMLoongArchExtIOI), + VMSTATE_UINT32(status, KVMLoongArchExtIOI), VMSTATE_END_OF_LIST() } }; +static Property extioi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", KVMLoongArchExtIOI, num_cpu, 1), + DEFINE_PROP_BIT("has-virtualization-extension", KVMLoongArchExtIOI, + features, EXTIOI_HAS_VIRT_EXTENSION, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void kvm_loongarch_extioi_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -131,6 +187,7 @@ static void kvm_loongarch_extioi_class_init(ObjectClass *oc, void *data) extioi_class->parent_realize = dc->realize; dc->realize = kvm_loongarch_extioi_realize; extioi_class->is_created = false; + device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_kvm_extioi_core; } diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 53dcefbb55a7a932b5cc0a5b26c9a32886fb07a6..39c4a6d8c6db76d84a1c1e03b71433115f0f0c51 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -171,6 +171,48 @@ 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 void find_initrd_loadoffset(struct loongarch_boot_info *info, + uint64_t kernel_high, ssize_t kernel_size) +{ + hwaddr base, size, gap, low_end; + ram_addr_t initrd_end, initrd_start; + + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + initrd_start = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); + initrd_end = initrd_start + initrd_size; + + size = info->ram_size; + low_end = base + MIN(size, gap); + if (initrd_end <= low_end) { + initrd_offset = initrd_start; + return; + } + + if (size <= gap) { + error_report("The low memory too small for initial ram disk '%s'," + "You need to expand the memory space", + info->initrd_filename); + exit(1); + } + + /* + * Try to load initrd in the high memory + */ + size -= gap; + base = VIRT_HIGHMEM_BASE; + initrd_start = ROUND_UP(base, 64 * KiB); + if (initrd_size <= size) { + initrd_offset = initrd_start; + return; + } + + error_report("The high memory too small for initial ram disk '%s'," + "You need to expand the memory space", + info->initrd_filename); + exit(1); +} + static int64_t load_kernel_info(struct loongarch_boot_info *info) { uint64_t kernel_entry, kernel_low, kernel_high; @@ -192,16 +234,9 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) 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); - } - + find_initrd_loadoffset(info, kernel_high, kernel_size); initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, - info->ram_size - initrd_offset); + initrd_size); } if (initrd_size == (target_ulong)-1) { diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 0c24e632bb84d261b0982a8528b946bccf6e7b42..233297d78f397a5201a7b2a90d92315bd835a1ce 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -51,6 +51,11 @@ #include "qemu/error-report.h" #include "qemu/guest-random.h" +#define FDT_IRQ_FLAGS_EDGE_LO_HI 1 +#define FDT_IRQ_FLAGS_EDGE_HI_LO 2 +#define FDT_IRQ_FLAGS_LEVEL_HI 4 +#define FDT_IRQ_FLAGS_LEVEL_LO 8 + static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) { if (lvms->veiointc == ON_OFF_AUTO_OFF) { @@ -275,7 +280,7 @@ static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms, "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); + VIRT_RTC_IRQ - VIRT_GSI_BASE , FDT_IRQ_FLAGS_EDGE_LO_HI); qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", *pch_pic_phandle); g_free(nodename); @@ -334,7 +339,7 @@ static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); 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_cells(ms->fdt, nodename, "interrupts", irq, FDT_IRQ_FLAGS_LEVEL_HI); qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", *pch_pic_phandle); g_free(nodename); @@ -869,6 +874,8 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) /* Create EXTIOI device */ if (kvm_enabled() && kvm_irqchip_in_kernel()) { extioi = qdev_new(TYPE_KVM_LOONGARCH_EXTIOI); + qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.max_cpus); + qdev_prop_set_bit(extioi, "has-virtualization-extension", true); sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); } else { extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index 1f9e38a735bd75886908e7009f14a26ca1e6261d..be9546c8505d7af32603b39a9df63c295530e66d 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -145,20 +145,22 @@ static void toymatch_write(LS7ARtcState *s, uint64_t val, int num) now = qemu_clock_get_ms(rtc_clock); toymatch_val_to_time(s, val, &tm); expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; - timer_mod(s->toy_timer[num], expire_time); + if (expire_time > now) + timer_mod(s->toy_timer[num], expire_time); } } static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num) { - uint64_t expire_ns; + int64_t expire_ns; /* it do not support write when toy disabled */ if (rtc_enabled(s)) { s->rtcmatch[num] = val; /* calculate expire time */ expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc); - timer_mod_ns(s->rtc_timer[num], expire_ns); + if (expire_ns > 0) + timer_mod_ns(s->rtc_timer[num], expire_ns); } } @@ -185,7 +187,7 @@ static void ls7a_rtc_stop(LS7ARtcState *s) static void ls7a_toy_start(LS7ARtcState *s) { int i; - uint64_t expire_time, now; + int64_t expire_time, now; struct tm tm = {}; now = qemu_clock_get_ms(rtc_clock); @@ -194,19 +196,21 @@ static void ls7a_toy_start(LS7ARtcState *s) for (i = 0; i < TIMER_NUMS; i++) { toymatch_val_to_time(s, s->toymatch[i], &tm); expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; - timer_mod(s->toy_timer[i], expire_time); + if (expire_time > now) + timer_mod(s->toy_timer[i], expire_time); } } static void ls7a_rtc_start(LS7ARtcState *s) { int i; - uint64_t expire_time; + int64_t expire_time; /* recalculate expire time and enable timer */ for (i = 0; i < TIMER_NUMS; i++) { expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc); - timer_mod_ns(s->rtc_timer[i], expire_time); + if (expire_time > 0) + timer_mod_ns(s->rtc_timer[i], expire_time); } } @@ -370,7 +374,7 @@ static void toy_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (toy_enabled(s)) { - qemu_irq_raise(s->irq); + qemu_irq_pulse(s->irq); } } @@ -379,7 +383,7 @@ static void rtc_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (rtc_enabled(s)) { - qemu_irq_raise(s->irq); + qemu_irq_pulse(s->irq); } } diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 9966cd98d327b2b354d81047f7d69a6b6f14c881..92b38d5c38bea88f7b7c107199b910201d72281d 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -94,6 +94,10 @@ struct LoongArchExtIOI { struct KVMLoongArchExtIOI { SysBusDevice parent_obj; + uint32_t num_cpu; + uint32_t features; + uint32_t status; + /* hardware state */ uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h index 13c1280662ae45b5cbf7f14ee55c7121e0ddf339..34abd659397a6e4a2de1e9159d27622e4a007217 100644 --- a/linux-headers/asm-loongarch/kvm.h +++ b/linux-headers/asm-loongarch/kvm.h @@ -141,6 +141,16 @@ struct kvm_iocsr_entry { #define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000003 +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS 0x40000006 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE 0x2 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL 0x40000007 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED 0x3 + #define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000004 #define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ee764f0bc7b0105a7c4ae5837cdfe7b704c8f3d0..561566f3a04971760acea2c48c6f1ec325b706e6 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -573,7 +573,7 @@ static void loongarch_cpu_reset_hold(Object *obj) env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, VS, 0); env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, LIE, 0); - env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2)); + env->CSR_ESTAT = 0; env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0); env->CSR_CPUID = cs->cpu_index; env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); @@ -638,8 +638,8 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) loongarch_cpu_register_gdb_regs_for_features(cs); - cpu_reset(cs); qemu_init_vcpu(cs); + cpu_reset(cs); lacc->parent_realize(dev, errp); } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9af622aba5c77fb213b11b422c67f95fe87f3624..6cc717c5eaffb1dec9bec4917acb66c55bb4524b 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -427,6 +427,7 @@ struct ArchCPU { const char *dtb_compatible; /* used by KVM_REG_LOONGARCH_COUNTER ioctl to access guest time counters */ uint64_t kvm_state_counter; + VMChangeStateEntry *vmsentry; }; /** diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 0acdd5c4c166aebb1c52635e606b1b9feec6d228..f6e008a5177d1b38026c850f755f64e4d3242a9a 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -590,9 +590,16 @@ static int kvm_loongarch_get_lbt(CPUState *cs) void kvm_arch_reset_vcpu(CPUState *cs) { CPULoongArchState *env = cpu_env(cs); + int ret = 0; + uint64_t unused = 0; env->mp_state = KVM_MP_STATE_RUNNABLE; - kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, 0); + ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, &unused); + if (ret) { + error_report("Failed to set KVM_REG_LOONGARCH_VCPU_RESET: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } } static int kvm_loongarch_get_mpstate(CPUState *cs) @@ -898,9 +905,10 @@ int kvm_arch_init_vcpu(CPUState *cs) uint64_t val; int ret; Error *local_err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(cs); ret = 0; - qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + cpu->vmsentry = 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; @@ -921,6 +929,8 @@ int kvm_arch_init_vcpu(CPUState *cs) int kvm_arch_destroy_vcpu(CPUState *cs) { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + qemu_del_vm_change_state_handler(cpu->vmsentry); return 0; }