From cb3eb649536c46ebbef7c24e66f4b661636f035e Mon Sep 17 00:00:00 2001
From: Ryo Shimizu <ryo@nerv.org>
Date: Sat, 18 Mar 2023 14:45:00 +0900
Subject: [PATCH 04/13] WIP: XXX: squash: add support NVMM aarch64 acceleration

---
 accel/nvmm/nvmm-accel-ops.c  |   2 +-
 accel/nvmm/nvmm-all.c        |  63 +++++--
 include/system/nvmm.h        |  11 ++
 meson.build                  |   3 +-
 target/arm/meson.build       |   1 +
 target/arm/nvmm/meson.build  |   5 +
 target/arm/nvmm/nvmm.c       | 329 +++++++++++++++++++++++++++++++++++
 target/i386/nvmm/meson.build |   2 +-
 target/i386/nvmm/nvmm.c      |  15 --
 9 files changed, 396 insertions(+), 35 deletions(-)
 create mode 100644 target/arm/nvmm/meson.build
 create mode 100644 target/arm/nvmm/nvmm.c

diff --git a/accel/nvmm/nvmm-accel-ops.c b/accel/nvmm/nvmm-accel-ops.c
index ffb82cf7bf..da5ad35454 100644
--- a/accel/nvmm/nvmm-accel-ops.c
+++ b/accel/nvmm/nvmm-accel-ops.c
@@ -8,7 +8,7 @@
  */
 
 #include "qemu/osdep.h"
-#include "system/kvm_int.h"
+#include "system/nvmm_int.h"
 #include "qemu/main-loop.h"
 #include "accel/accel-cpu-ops.h"
 #include "system/cpus.h"
diff --git a/accel/nvmm/nvmm-all.c b/accel/nvmm/nvmm-all.c
index 315029ff31..5be5a90cbf 100644
--- a/accel/nvmm/nvmm-all.c
+++ b/accel/nvmm/nvmm-all.c
@@ -9,12 +9,13 @@
 
 #include "qemu/osdep.h"
 #include "cpu.h"
-#include "exec/address-spaces.h"
-#include "exec/ioport.h"
+#include "system/address-spaces.h"
+#include "system/ioport.h"
 #include "qemu/accel.h"
-#include "sysemu/nvmm.h"
-
-#include <nvmm.h>
+#include "accel/accel-ops.h"
+#include "qapi/error.h"
+#include "system/nvmm.h"
+#include "qemu/error-report.h"
 
 struct qemu_machine {
     struct nvmm_capability cap;
@@ -23,10 +24,10 @@ struct qemu_machine {
 
 /* -------------------------------------------------------------------------- */
 
-static bool nvmm_allowed;
+bool nvmm_allowed;
 static struct qemu_machine qemu_mach;
 
-static struct nvmm_machine *
+struct nvmm_machine *
 get_nvmm_mach(void)
 {
     return &qemu_mach.mach;
@@ -63,7 +64,39 @@ nvmm_io_callback(struct nvmm_io *io)
 static void
 nvmm_mem_callback(struct nvmm_mem *mem)
 {
-    cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write);
+
+//    address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED,
+//                     val, req->size, rw);
+#if 0
+        trace_kvm_run_exit(cpu->cpu_index, run->exit_reason);
+        switch (run->exit_reason) {
+        case KVM_EXIT_IO:
+            /* Called outside BQL */
+            kvm_handle_io(run->io.port, attrs,
+                          (uint8_t *)run + run->io.data_offset,
+                          run->io.direction,
+                          run->io.size,
+                          run->io.count);
+            ret = 0;
+            break;
+        case KVM_EXIT_MMIO:
+            /* Called outside BQL */
+            address_space_rw(&address_space_memory,
+                             run->mmio.phys_addr, attrs,
+                             run->mmio.data,
+                             run->mmio.len,
+                             run->mmio.is_write);
+            ret = 0;
+            break;
+#endif
+//    cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write);
+
+    MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; // { 0 };
+
+    address_space_rw(&address_space_memory,
+                     mem->gpa,
+                     attrs,
+                     mem->data, mem->size, mem->write);
 
     /* Needed, otherwise infinite loop. */
     current_cpu->vcpu_dirty = false;
@@ -287,7 +320,7 @@ static MemoryListener nvmm_memory_listener = {
     .region_add = nvmm_region_add,
     .region_del = nvmm_region_del,
     .log_sync = nvmm_log_sync,
-    .priority = MEMORY_LISTENER_PRIORITY_ACCEL
+    .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
 };
 
 static void
@@ -314,8 +347,10 @@ static struct RAMBlockNotifier nvmm_ram_notifier = {
 /* -------------------------------------------------------------------------- */
 
 static int
-nvmm_accel_init(MachineState *ms)
+nvmm_accel_init(AccelState *as, MachineState *ms)
 {
+//    MachineClass *mc = MACHINE_GET_CLASS(ms);
+//    KVMState *s = KVM_STATE(as);
     int ret, err;
 
     ret = nvmm_init();
@@ -354,14 +389,8 @@ nvmm_accel_init(MachineState *ms)
     return 0;
 }
 
-int
-nvmm_enabled(void)
-{
-    return nvmm_allowed;
-}
-
 static void
-nvmm_accel_class_init(ObjectClass *oc, void *data)
+nvmm_accel_class_init(ObjectClass *oc, const void *data)
 {
     AccelClass *ac = ACCEL_CLASS(oc);
     ac->name = "NVMM";
diff --git a/include/system/nvmm.h b/include/system/nvmm.h
index 5ecff76cba..8e5730e9f6 100644
--- a/include/system/nvmm.h
+++ b/include/system/nvmm.h
@@ -12,6 +12,8 @@
 #ifndef QEMU_NVMM_H
 #define QEMU_NVMM_H
 
+#include "qemu/accel.h"
+
 #include <nvmm.h>
 
 #ifdef COMPILING_PER_TARGET
@@ -51,4 +53,13 @@ extern bool nvmm_allowed;
 #define nvmm_enabled() 0
 #endif /* !CONFIG_NVMM_IS_POSSIBLE */
 
+struct NVMMstate;
+
+#define TYPE_NVMM_ACCEL ACCEL_CLASS_NAME("nvmm")
+typedef struct NVMMState NVMMState;
+DECLARE_INSTANCE_CHECKER(NVMMState, NVMM_STATE,
+                         TYPE_NVMM_ACCEL)
+
+extern NVMMState *nvmm_state;
+
 #endif /* QEMU_NVMM_H */
diff --git a/meson.build b/meson.build
index 33028de33e..fada7cee03 100644
--- a/meson.build
+++ b/meson.build
@@ -325,7 +325,8 @@ accelerator_targets += { 'CONFIG_XEN': xen_targets }
 
 if cpu == 'aarch64'
   accelerator_targets += {
-    'CONFIG_HVF': ['aarch64-softmmu']
+    'CONFIG_HVF': ['aarch64-softmmu'],
+    'CONFIG_NVMM': ['aarch64-softmmu'],
   }
 elif cpu == 'x86_64'
   accelerator_targets += {
diff --git a/target/arm/meson.build b/target/arm/meson.build
index 3df7e03654..e2ed1ca411 100644
--- a/target/arm/meson.build
+++ b/target/arm/meson.build
@@ -59,6 +59,7 @@ arm_common_system_ss.add(files(
 ))
 
 subdir('hvf')
+subdir('nvmm')
 
 if 'CONFIG_TCG' in config_all_accel
    subdir('tcg')
diff --git a/target/arm/nvmm/meson.build b/target/arm/nvmm/meson.build
new file mode 100644
index 0000000000..dd8752ec8c
--- /dev/null
+++ b/target/arm/nvmm/meson.build
@@ -0,0 +1,5 @@
+arm_system_ss.add(when: [nvmm, 'CONFIG_NVMM'], if_true:
+  files(
+    'nvmm.c',
+  )
+)
diff --git a/target/arm/nvmm/nvmm.c b/target/arm/nvmm/nvmm.c
new file mode 100644
index 0000000000..56882d0fd4
--- /dev/null
+++ b/target/arm/nvmm/nvmm.c
@@ -0,0 +1,329 @@
+/*
+ * QEMU NetBSD Virtual Machine Monitor (NVMM) accelerator support for AARCH64
+ *
+ * Copyright (c) 2023 Ryo Shimizu <ryo@nerv.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "system/address-spaces.h"
+#include "system/ioport.h"
+#include "qemu/accel.h"
+#include "system/nvmm.h"
+#include "system/cpus.h"
+#include "system/runstate.h"
+#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qemu/queue.h"
+#include "migration/blocker.h"
+#include "strings.h"
+
+struct AccelCPUState {
+    struct nvmm_vcpu vcpu;
+    bool stop;
+};
+
+void
+nvmm_set_registers(CPUState *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;
+    int i, ret;
+
+    assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
+
+    /* GPRs. */
+    for (i = 0; i < 32; i++) {
+        state->gprs[NVMM_AARCH64_GPR_X0 + i ] = env->xregs[i];
+    }
+
+    /* SPRs. */
+    state->sprs[NVMM_AARCH64_SPR_PC] = env->pc;
+    //XXXXXXXXXXXXXXXXX more sysregs
+
+    ret = nvmm_vcpu_setstate(mach, vcpu, NVMM_AARCH64_STATE_ALL);
+    if (ret == -1) {
+        error_report("NVMM: Failed to set virtual processor context,"
+            " error=%d", errno);
+    }
+}
+
+void
+nvmm_get_registers(CPUState *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;
+    int i, ret;
+
+    assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
+
+    ret = nvmm_vcpu_getstate(mach, vcpu, NVMM_AARCH64_STATE_ALL);
+    if (ret == -1) {
+        error_report("NVMM: Failed to get virtual processor context,"
+            " error=%d", errno);
+    }
+
+    /* GPRs. */
+    for (i = 0; i < 32; i++) {
+        env->xregs[i] = state->gprs[NVMM_AARCH64_GPR_X0 + i ];
+    }
+
+    /* SPRs. */
+    env->pc = state->sprs[NVMM_AARCH64_SPR_PC];
+    //XXXXXXXXXXXXXXXXX more sysregs
+
+}
+
+static void
+nvmm_vcpu_pre_run(CPUState *cpu)
+{
+	//XXXXXXXXXX
+}
+
+static void
+nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit)
+{
+	//XXXXXXXXXX
+}
+
+static int
+nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu,
+    struct nvmm_vcpu_exit *exit)
+{
+	fprintf(stderr, "%s:%d: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n", __func__, __LINE__);
+
+	return 0;
+}
+
+
+int
+nvmm_vcpu_loop(CPUState *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_vcpu_exit *exit = vcpu->exit;
+    int ret;
+
+
+    //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);
+    }
+    if (cpu->interrupt_request & CPU_INTERRUPT_HARD) {
+        vcpu->event->type = NVMM_VCPU_EVENT_IRQ;
+        nvmm_vcpu_inject(mach, vcpu);
+    }
+
+    if (cpu->halted) {
+        return EXCP_HLT;
+    }
+
+    bql_unlock();
+    cpu_exec_start(cpu);
+
+    /*
+     * Inner VCPU loop.
+     */
+    do {
+        if (cpu->vcpu_dirty) {
+            nvmm_set_registers(cpu);
+            cpu->vcpu_dirty = false;
+        }
+
+        if (qcpu->stop) {
+            cpu->exception_index = EXCP_INTERRUPT;
+            qcpu->stop = false;
+            ret = 1;
+            break;
+        }
+
+        nvmm_vcpu_pre_run(cpu);
+
+        /* Read exit_request before the kernel reads the immediate exit flag */
+        smp_rmb();
+        ret = nvmm_vcpu_run(mach, vcpu);
+        if (ret == -1) {
+            error_report("NVMM: Failed to exec a virtual processor,"
+                " error=%d", errno);
+            break;
+        }
+
+        nvmm_vcpu_post_run(cpu, exit);
+
+        switch (exit->reason) {
+        case NVMM_VCPU_EXIT_NONE:
+            break;
+        case NVMM_VCPU_EXIT_STOPPED:
+            /*
+             * The kernel cleared the immediate exit flag; cpu->exit_request
+             * must be cleared after
+             */
+            smp_wmb();
+            qcpu->stop = true;
+            break;
+        case NVMM_VCPU_EXIT_MEMORY:
+            ret = nvmm_handle_mem(mach, vcpu);
+            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);
+            cpu->exception_index = EXCP_INTERRUPT;
+            ret = 1;
+            break;
+//        case NVMM_VCPU_EXIT_MSR:
+//            ret = nvmm_handle_rdmsr(mach, cpu, exit);
+//            break;
+//        case NVMM_VCPU_EXIT_MSR:
+//            ret = nvmm_handle_wrmsr(mach, cpu, exit);
+//            break;
+        default:
+            error_report("NVMM: Unexpected VM exit code"
+                " 0x%lx [hw=0x%lx, esr=0x%lx]",
+                exit->reason, exit->u.inv.hwcode, exit->esr);
+            nvmm_get_registers(cpu);
+            bql_lock();
+            qemu_system_guest_panicked(cpu_get_crash_info(cpu));
+            bql_unlock();
+            ret = -1;
+            break;
+        }
+    } while (ret == 0);
+
+    cpu_exec_end(cpu);
+    bql_lock();
+
+    qatomic_set(&cpu->exit_request, false);
+
+    return ret < 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static Error *nvmm_migration_blocker;
+
+/*
+ * The nvmm_vcpu_stop() mechanism breaks races between entering the VMM
+ * and another thread signaling the vCPU thread to exit.
+ */
+
+void
+nvmm_ipi_signal(int sigcpu)
+{
+    if (current_cpu) {
+        AccelCPUState *qcpu = current_cpu->accel;
+        struct nvmm_vcpu *vcpu = &qcpu->vcpu;
+        nvmm_vcpu_stop(vcpu);
+    }
+}
+
+void
+nvmm_init_cpu_signals(void)
+{
+    struct sigaction sigact;
+    sigset_t set;
+
+    /* Install the IPI handler. */
+    memset(&sigact, 0, sizeof(sigact));
+    sigact.sa_handler = nvmm_ipi_signal;
+    sigaction(SIG_IPI, &sigact, NULL);
+
+    /* Allow IPIs on the current thread. */
+    sigprocmask(SIG_BLOCK, NULL, &set);
+    sigdelset(&set, SIG_IPI);
+    pthread_sigmask(SIG_SETMASK, &set, NULL);
+}
+
+int
+nvmm_init_vcpu(CPUState *cpu)
+{
+    struct nvmm_machine *mach = get_nvmm_mach();
+//    struct nvmm_capability *cap = get_nvmm_cap();
+//    struct nvmm_vcpu_conf_cpuid cpuid;
+    Error *local_error = NULL;
+    AccelCPUState *qcpu;
+    int ret, err;
+
+    nvmm_init_cpu_signals();
+
+    if (nvmm_migration_blocker == NULL) {
+        error_setg(&nvmm_migration_blocker,
+            "NVMM: Migration not supported");
+
+        if (migrate_add_blocker(&nvmm_migration_blocker, &local_error) < 0) {
+            error_report_err(local_error);
+            error_free(nvmm_migration_blocker);
+            return -EINVAL;
+        }
+    }
+
+    qcpu = g_malloc0(sizeof(*qcpu));
+    if (qcpu == NULL) {
+        error_report("NVMM: Failed to allocate VCPU context.");
+        return -ENOMEM;
+    }
+
+    ret = nvmm_vcpu_create(mach, cpu->cpu_index, &qcpu->vcpu);
+    if (ret == -1) {
+        err = errno;
+        error_report("NVMM: Failed to create a virtual processor,"
+            " error=%d", err);
+        g_free(qcpu);
+        return -err;
+    }
+
+#if 0
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+// setup MPIDR_EL1
+    memset(&cpuid, 0, sizeof(cpuid));
+    cpuid.mask = 1;
+    cpuid.leaf = 0x00000001;
+    cpuid.u.mask.set.edx = CPUID_MCE | CPUID_MCA | CPUID_MTRR;
+    ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_CPUID,
+        &cpuid);
+    if (ret == -1) {
+        err = errno;
+        error_report("NVMM: Failed to configure a virtual processor,"
+            " error=%d", err);
+        g_free(qcpu);
+        return -err;
+    }
+#endif
+
+    ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_CALLBACKS,
+        &nvmm_callbacks);
+    if (ret == -1) {
+        err = errno;
+        error_report("NVMM: Failed to configure a virtual processor,"
+            " error=%d", err);
+        g_free(qcpu);
+        return -err;
+    }
+
+    cpu->vcpu_dirty = true;
+
+    return 0;
+}
+
+void
+nvmm_destroy_vcpu(CPUState *cpu)
+{
+    struct nvmm_machine *mach = get_nvmm_mach();
+    AccelCPUState *qcpu = cpu->accel;
+
+    nvmm_vcpu_destroy(mach, &qcpu->vcpu);
+}
diff --git a/target/i386/nvmm/meson.build b/target/i386/nvmm/meson.build
index b24fa0ff4d..0bbd66b104 100644
--- a/target/i386/nvmm/meson.build
+++ b/target/i386/nvmm/meson.build
@@ -1,5 +1,5 @@
 i386_system_ss.add(when: [nvmm, 'CONFIG_NVMM'], if_true:
   files(
-  'nvmm.c',
+    'nvmm.c',
   )
 )
diff --git a/target/i386/nvmm/nvmm.c b/target/i386/nvmm/nvmm.c
index 26ab2f1371..561778dcfb 100644
--- a/target/i386/nvmm/nvmm.c
+++ b/target/i386/nvmm/nvmm.c
@@ -867,21 +867,6 @@ nvmm_destroy_vcpu(CPUState *cpu)
     g_free(cpu->accel);
 }
 
-static void
-nvmm_accel_class_init(ObjectClass *oc, const void *data)
-{
-    AccelClass *ac = ACCEL_CLASS(oc);
-    ac->name = "NVMM";
-    ac->init_machine = nvmm_accel_init;
-    ac->allowed = &nvmm_allowed;
-}
-
-static const TypeInfo nvmm_accel_type = {
-    .name = ACCEL_CLASS_NAME("nvmm"),
-    .parent = TYPE_ACCEL,
-    .class_init = nvmm_accel_class_init,
-};
-
 static void nvmm_cpu_instance_init(CPUState *cs)
 {
     X86CPU *cpu = X86_CPU(cs);
-- 
2.54.0 (Apple Git-157)

