From cb3eb649536c46ebbef7c24e66f4b661636f035e Mon Sep 17 00:00:00 2001 From: Ryo Shimizu 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 +#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 #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 + * + * 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)