From b63be6801f6ae3b2bc89777f20c37c540c5a5e20 Mon Sep 17 00:00:00 2001
From: Nick Hudson <skrll@netbsd.org>
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)

