From b63be6801f6ae3b2bc89777f20c37c540c5a5e20 Mon Sep 17 00:00:00 2001 From: Nick Hudson Date: Mon, 25 Aug 2025 09:08:51 +0000 Subject: [PATCH 09/13] WIP. Interrupts get delivers to the point I see [ 1.0000040] ld4: GPT GUID: fb606200-5450-11f0-8000-000000000000 and evcnt type 1: armgic (cpu0) irq 33 = 2 evcnt type 1: armgic (cpu0) irq 79 = 5 evcnt type 1: armgic (cpu0) irq 36 = 2 Timer interrupts are missing --- accel/nvmm/nvmm-accel-ops.c | 20 ++++ accel/nvmm/nvmm-all.c | 34 ++++++- hw/arm/virt.c | 3 +- target/arm/cpu.c | 12 ++- target/arm/cpu64.c | 1 - target/arm/nvmm/nvmm.c | 182 +++++++++++++++++++++++++++++++++--- target/i386/nvmm/nvmm.c | 2 + 7 files changed, 238 insertions(+), 16 deletions(-) diff --git a/accel/nvmm/nvmm-accel-ops.c b/accel/nvmm/nvmm-accel-ops.c index da5ad35454..16adf08b38 100644 --- a/accel/nvmm/nvmm-accel-ops.c +++ b/accel/nvmm/nvmm-accel-ops.c @@ -35,6 +35,7 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg) fprintf(stderr, "nvmm_init_vcpu failed: %s\n", strerror(-r)); exit(1); } + // nvmm_init_cpu_signals(cpu); // XXXNH seems KVM only /* signal CPU creation */ cpu_thread_signal_created(cpu); @@ -92,12 +93,31 @@ static void nvmm_accel_ops_class_init(ObjectClass *oc, const void *data) ops->synchronize_pre_loadvm = nvmm_cpu_synchronize_pre_loadvm; } +#if 0 + ops->create_vcpu_thread = kvm_start_vcpu_thread; + ops->cpu_thread_is_idle = kvm_vcpu_thread_is_idle; + ops->cpus_are_resettable = kvm_cpus_are_resettable; + ops->synchronize_post_reset = kvm_cpu_synchronize_post_reset; + ops->synchronize_post_init = kvm_cpu_synchronize_post_init; + ops->synchronize_state = kvm_cpu_synchronize_state; + ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; + +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG + ops->update_guest_debug = kvm_update_guest_debug_ops; + ops->supports_guest_debug = kvm_supports_guest_debug; + ops->insert_breakpoint = kvm_insert_breakpoint; + ops->remove_breakpoint = kvm_remove_breakpoint; + ops->remove_all_breakpoints = kvm_remove_all_breakpoints; +#endif +#endif + static const TypeInfo nvmm_accel_ops_type = { .name = ACCEL_OPS_NAME("nvmm"), .parent = TYPE_ACCEL_OPS, .class_init = nvmm_accel_ops_class_init, .abstract = true, + }; static void nvmm_accel_ops_register_types(void) diff --git a/accel/nvmm/nvmm-all.c b/accel/nvmm/nvmm-all.c index 5be5a90cbf..4b66414f96 100644 --- a/accel/nvmm/nvmm-all.c +++ b/accel/nvmm/nvmm-all.c @@ -199,7 +199,7 @@ nvmm_vcpu_exec(CPUState *cpu) { int ret, fatal; - while (1) { + while (true) { if (cpu->exception_index >= EXCP_INTERRUPT) { ret = cpu->exception_index; cpu->exception_index = -1; @@ -248,12 +248,29 @@ nvmm_process_section(MemoryRegionSection *section, int add) MemoryRegion *mr = section->mr; hwaddr start_pa = section->offset_within_address_space; ram_addr_t size = int128_get64(section->size); + bool writable = !mr->readonly && !mr->rom_device; unsigned int delta; uintptr_t hva; + if (!memory_region_is_ram(mr)) { + if (writable /* || !kvm_readonly_mem_allowed */) { + return; + } else if (!mr->romd_mode) { + /* If the memory device is not in romd_mode, then we actually want + * to remove the kvm memory slot so all accesses will trap. */ + return; +// add = false; + } + } + + + + +#if 0 if (!memory_region_is_ram(mr)) { return; } +#endif /* Adjust start_pa and size so that they are page-aligned. */ delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask()); @@ -271,6 +288,8 @@ nvmm_process_section(MemoryRegionSection *section, int add) hva = (uintptr_t)memory_region_get_ram_ptr(mr) + section->offset_within_region + delta; +printf("%s:%d pa %#lx size %#lx hva %#lx %s rom/'%s'\n", + __func__, __LINE__, start_pa, size, hva, memory_region_is_rom(mr) ? "is" : "not", mr->name); nvmm_update_mapping(start_pa, size, hva, add, memory_region_is_rom(mr), mr->name); } @@ -280,6 +299,19 @@ nvmm_region_add(MemoryListener *listener, MemoryRegionSection *section) { memory_region_ref(section->mr); nvmm_process_section(section, 1); + +#if 0 + if (!memory_region_is_ram(mr)) { + if (writable || !kvm_readonly_mem_allowed) { + return; + } else if (!mr->romd_mode) { + /* If the memory device is not in romd_mode, then we actually want + * to remove the kvm memory slot so all accesses will trap. */ + add = false; + } + } +#endif + } static void diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 7ca5be812e..2cd77ef4f9 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2145,6 +2145,7 @@ static void virt_post_cpus_gic_realized(VirtMachineState *vms, steal_time = object_property_get_bool(OBJECT(first_cpu), "kvm-steal-time", NULL); + // XXXNH what's this? if (kvm_enabled()) { hwaddr pvtime_reg_base = vms->memmap[VIRT_PVTIME].base; hwaddr pvtime_reg_size = vms->memmap[VIRT_PVTIME].size; @@ -2328,7 +2329,7 @@ static void machvirt_init(MachineState *machine) exit(1); } - if (vms->mte && hvf_enabled()) { + if (vms->mte && (hvf_enabled() || nvmm_enabled())) { error_report("mach-virt: %s does not support providing " "MTE to the guest CPU", current_accel_name()); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 39292fb9bc..e1f08582df 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -565,6 +565,11 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) if (kvm_enabled()) { kvm_arm_reset_vcpu(cpu); } +#if 0 + if (nvmm_enabled()) { + nvmm_arm_reset_vcpu(cpu); + } +#endif #endif if (tcg_enabled()) { @@ -1511,6 +1516,7 @@ static void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(cpu), &arm_cpu_gt_cntfrq_property); } + // XXXNH if (kvm_enabled()) { kvm_arm_add_vcpu_properties(cpu); } @@ -1600,6 +1606,7 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) } } + // XXXNH if (kvm_enabled()) { kvm_arm_steal_time_finalize(cpu, &local_err); if (local_err != NULL) { @@ -1628,8 +1635,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * this is the first point where we can report it. */ if (cpu->host_cpu_probe_failed) { - if (!kvm_enabled() && !hvf_enabled()) { - error_setg(errp, "The 'host' CPU type can only be used with KVM or HVF"); + if (!kvm_enabled() && !hvf_enabled() && !nvmm_enabled()) { + error_setg(errp, "The 'host' CPU type can only be used with KVM, HVF, or NVMM"); } else { error_setg(errp, "Failed to retrieve host CPU features"); } @@ -1978,6 +1985,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) if (arm_feature(env, ARM_FEATURE_PMU)) { pmu_init(cpu); + // XXXNH if (!kvm_enabled()) { arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0); arm_register_el_change_hook(cpu, &pmu_post_el_change, 0); diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 3f1a1dc089..529ebb8c0e 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -805,7 +805,6 @@ static void aarch64_max_initfn(Object *obj) { if (kvm_enabled() || hvf_enabled() || nvmm_enabled()) { /* With KVM, HVF or NVMM, '-cpu max' is identical to '-cpu host' */ - aarch64_host_initfn(obj); return; } diff --git a/target/arm/nvmm/nvmm.c b/target/arm/nvmm/nvmm.c index 889fe53a97..d5f0459f8f 100644 --- a/target/arm/nvmm/nvmm.c +++ b/target/arm/nvmm/nvmm.c @@ -334,7 +334,8 @@ void nvmm_set_registers(CPUState *cpu) { ARMCPU *arm_cpu = ARM_CPU(cpu); - CPUArchState *env = cpu_env(cpu); + CPUArchState *env = cpu_env(cpu); // &cpu->env; + struct nvmm_machine *mach = get_nvmm_mach(); AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -445,26 +446,107 @@ nvmm_get_registers(CPUState *cpu) assert(write_list_to_cpustate(arm_cpu)); } + +/* + * Called before the VCPU is run. We inject events generated by the I/O + * thread. + */ static void nvmm_vcpu_pre_run(CPUState *cpu) { - //XXXXXXXXXX +// ARMCPU *arm_cpu = ARM_CPU(cpu); +// CPUArchState *env = cpu_env(cpu); + struct nvmm_machine *mach = get_nvmm_mach(); + AccelCPUState *qcpu = cpu->accel; + struct nvmm_vcpu *vcpu = &qcpu->vcpu; +// struct nvmm_aarch64_state *state = vcpu->state; + + struct nvmm_vcpu_event *event = vcpu->event; + bool has_event = false; + int ret; + + // XXXNH needed? I don't think so as I've deleted the pic call + bql_lock(); + +#if 0 + /* + * Force the VCPU out of its inner loop to process any INIT requests. XXXNH + */ + if (cpu->interrupt_request & CPU_INTERRUPT_INIT) { + cpu->exit_request = 1; + } + +#endif + + if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_FIQ)) { + cpu->interrupt_request &= ~CPU_INTERRUPT_FIQ; + event->type = NVMM_VCPU_EVENT_FIQ; + has_event = true; + } + + if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + event->type = NVMM_VCPU_EVENT_IRQ; + has_event = true; + } + + + if (has_event) { + ret = nvmm_vcpu_inject(mach, vcpu); + if (ret == -1) { + error_report("NVMM: Failed to inject event," + " error=%d", errno); + } + } + + bql_unlock(); } +/* + * Called after the VCPU ran. + */ static void nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit) { - //XXXXXXXXXX +#if 0 + AccelCPUState *qcpu = cpu->accel; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint64_t tpr; + + env->eflags = exit->exitstate.rflags; + qcpu->int_shadow = exit->exitstate.int_shadow; + qcpu->int_window_exit = exit->exitstate.int_window_exiting; + qcpu->nmi_window_exit = exit->exitstate.nmi_window_exiting; + + tpr = exit->exitstate.cr8; + if (qcpu->tpr != tpr) { + qcpu->tpr = tpr; + bql_lock(); + cpu_set_apic_tpr(x86_cpu->apic_state, qcpu->tpr); + bql_unlock(); + } +#endif } +/* -------------------------------------------------------------------------- */ + static int nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu, struct nvmm_vcpu_exit *exit) { - fprintf(stderr, "%s:%d: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n", __func__, __LINE__); - abort(); + int ret = 0; + + bql_lock(); + if (!(cpu->interrupt_request & CPU_INTERRUPT_HARD) && + !(cpu->interrupt_request & CPU_INTERRUPT_FIQ)) { + cpu->exception_index = EXCP_HLT; + cpu->halted = true; + ret = 1; + } + bql_unlock(); - return 0; + return ret; } static void @@ -477,13 +559,61 @@ nvmm_wfi(CPUState *cpu) return; } -//XXXXX - return; - // qatomic_mb_set(&cpu->thread_kicked, false); // qemu_mutex_unlock_iothread(); // pselect(0, 0, 0, 0, NULL, &cpu->hvf->unblock_ipi_mask); // qemu_mutex_lock_iothread(); + + + +#if 0 + + + + + r = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTV_CTL_EL0, &ctl); + assert_hvf_ok(r); + + if (!(ctl & 1) || (ctl & 2)) { + /* Timer disabled or masked, just wait for an IPI. */ + hvf_wait_for_ipi(cpu, NULL); + return; + } + + r = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTV_CVAL_EL0, &cval); + assert_hvf_ok(r); + + ticks_to_sleep = cval - hvf_vtimer_val(); + if (ticks_to_sleep < 0) { + return; + } + + cntfrq = gt_cntfrq_period_ns(arm_cpu); + seconds = muldiv64(ticks_to_sleep, cntfrq, NANOSECONDS_PER_SECOND); + ticks_to_sleep -= muldiv64(seconds, NANOSECONDS_PER_SECOND, cntfrq); + nanos = ticks_to_sleep * cntfrq; + + /* + * Don't sleep for less than the time a context switch would take, + * so that we can satisfy fast timer requests on the same CPU. + * Measurements on M1 show the sweet spot to be ~2ms. + */ + if (!seconds && nanos < (2 * SCALE_MS)) { + return; + } + + ts = (struct timespec) { seconds, nanos }; + hvf_wait_for_ipi(cpu, &ts); +#endif + + + + + + + + + } int @@ -500,10 +630,14 @@ nvmm_vcpu_loop(CPUState *cpu) if (cpu->interrupt_request & CPU_INTERRUPT_FIQ) { vcpu->event->type = NVMM_VCPU_EVENT_FIQ; nvmm_vcpu_inject(mach, vcpu); + cpu->halted = false; +fprintf(stderr, "%s:%d fiq\n", __func__, __LINE__); } if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { vcpu->event->type = NVMM_VCPU_EVENT_IRQ; nvmm_vcpu_inject(mach, vcpu); + cpu->halted = false; +fprintf(stderr, "%s:%d irq\n", __func__, __LINE__); } if (cpu->halted) { @@ -513,6 +647,8 @@ nvmm_vcpu_loop(CPUState *cpu) bql_unlock(); cpu_exec_start(cpu); + // XXXNH cf. hvf_vcpu_exec + /* * Inner VCPU loop. */ @@ -529,8 +665,32 @@ nvmm_vcpu_loop(CPUState *cpu) break; } +#if 0 + //XXXXXXXXXX: nvmm cannot send multiple events at the same time? + if (cpu->interrupt_request & CPU_INTERRUPT_FIQ) { + vcpu->event->type = NVMM_VCPU_EVENT_FIQ; + nvmm_vcpu_inject(mach, vcpu); + cpu->halted = false; +fprintf(stderr, "%s:%d fiq\n", __func__, __LINE__); + } + if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + vcpu->event->type = NVMM_VCPU_EVENT_IRQ; + nvmm_vcpu_inject(mach, vcpu); + cpu->halted = false; +fprintf(stderr, "%s:%d irq\n", __func__, __LINE__); + } +#endif nvmm_vcpu_pre_run(cpu); + if (qatomic_read(&cpu->exit_request)) { +#if NVMM_USER_VERSION >= 2 + nvmm_vcpu_stop(vcpu); +#else + qemu_cpu_kick_self(); +#endif + } + + //XXXNH wtf? /* Read exit_request before the kernel reads the immediate exit flag */ smp_rmb(); ret = nvmm_vcpu_run(mach, vcpu); @@ -556,7 +716,6 @@ nvmm_vcpu_loop(CPUState *cpu) case NVMM_VCPU_EXIT_MEMORY: ret = nvmm_handle_mem(mach, vcpu); break; - case NVMM_VCPU_EXIT_MRS: error_report("XXXXXX: NOTYET: NVMM_VCPU_EXIT_MRS\n"); abort(); @@ -579,12 +738,12 @@ nvmm_vcpu_loop(CPUState *cpu) case NVMM_VCPU_EXIT_WFE: error_report("XXXXXX: NOTYET: NVMM_VCPU_EXIT_WFE\n"); break; - case NVMM_VCPU_EXIT_HALTED: ret = nvmm_handle_halted(mach, cpu, exit); break; case NVMM_VCPU_EXIT_SHUTDOWN: qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + // XXXNH wtf? cpu->exception_index = EXCP_INTERRUPT; ret = 1; break; @@ -628,6 +787,7 @@ nvmm_ipi_signal(int sigcpu) } } + void nvmm_init_cpu_signals(void) { diff --git a/target/i386/nvmm/nvmm.c b/target/i386/nvmm/nvmm.c index 561778dcfb..03c4854080 100644 --- a/target/i386/nvmm/nvmm.c +++ b/target/i386/nvmm/nvmm.c @@ -749,6 +749,7 @@ static Error *nvmm_migration_blocker; * and another thread signaling the vCPU thread to exit. */ +// XXXNH retain as common between x86/arm void nvmm_ipi_signal(int sigcpu) { @@ -763,6 +764,7 @@ nvmm_ipi_signal(int sigcpu) } } +// XXXNH retain as common between x86/arm void nvmm_init_cpu_signals(void) { -- 2.54.0 (Apple Git-157)