diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c
index 97a1a27a9acde09bb2a58dba4340f2f4f6ef7349..a419ca3a3c0981be000eb467aa36dd9624a817bf 100644
--- a/hw/intc/sifive_plic.c
+++ b/hw/intc/sifive_plic.c
@@ -31,6 +31,8 @@
#include "target/riscv/cpu.h"
#include "sysemu/sysemu.h"
#include "migration/vmstate.h"
+#include "sysemu/kvm.h"
+#include "kvm_riscv.h"
#define RISCV_DEBUG_PLIC 0
@@ -147,15 +149,26 @@ static void sifive_plic_update(SiFivePLICState *plic)
continue;
}
int level = sifive_plic_irqs_pending(plic, addrid);
- switch (mode) {
- case PLICMode_M:
- riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
- break;
- case PLICMode_S:
- riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level));
- break;
- default:
- break;
+ if (kvm_enabled()) {
+ if (mode == PLICMode_M) {
+ continue;
+ }
+#ifdef CONFIG_KVM
+ kvm_riscv_set_irq(RISCV_CPU(cpu), IRQ_S_EXT, level);
+#endif
+ } else {
+ switch (mode) {
+ case PLICMode_M:
+ riscv_cpu_update_mip(RISCV_CPU(cpu),
+ MIP_MEIP, BOOL_TO_MASK(level));
+ break;
+ case PLICMode_S:
+ riscv_cpu_update_mip(RISCV_CPU(cpu),
+ MIP_SEIP, BOOL_TO_MASK(level));
+ break;
+ default:
+ break;
+ }
}
}
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 25cea7aa674b6205b28c387bdeb5408737e73dde..47b701819315afd075b6a6151566e4d84f9be4eb 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -42,6 +42,7 @@
#include "sysemu/sysemu.h"
#include "hw/pci/pci.h"
#include "hw/pci-host/gpex.h"
+#include "sysemu/kvm.h"
#if defined(TARGET_RISCV32)
# define BIOS_FILENAME "opensbi-riscv32-generic-fw_dynamic.bin"
@@ -511,6 +512,7 @@ static void virt_machine_init(MachineState *machine)
uint64_t kernel_entry;
DeviceState *mmio_plic, *virtio_plic, *pcie_plic;
int i, j, base_hartid, hart_count;
+ CPUState *cs;
/* Check socket count limit */
if (VIRT_SOCKETS_MAX < riscv_socket_count(machine)) {
@@ -660,6 +662,12 @@ static void virt_machine_init(MachineState *machine)
virt_memmap[VIRT_MROM].size, kernel_entry,
fdt_load_addr, s->fdt);
+ for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
+ RISCVCPU *riscv_cpu = RISCV_CPU(cs);
+ riscv_cpu->env.kernel_addr = kernel_entry;
+ riscv_cpu->env.fdt_addr = fdt_load_addr;
+ }
+
/* SiFive Test MMIO device */
sifive_test_create(memmap[VIRT_TEST].base);
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 56ce14ad209f7f47615d0940c980321bc492c079..ddeedcf3df9646486ac4e6f2557068a041f5e9bb 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -250,6 +250,7 @@ struct kvm_hyperv_exit {
#define KVM_EXIT_ARM_NISV 28
#define KVM_EXIT_X86_RDMSR 29
#define KVM_EXIT_X86_WRMSR 30
+#define KVM_EXIT_RISCV_SBI 31
/* For KVM_EXIT_INTERNAL_ERROR */
/* Emulate instruction failed. */
@@ -426,6 +427,13 @@ struct kvm_run {
__u32 index; /* kernel -> user */
__u64 data; /* kernel <-> user */
} msr;
+ /* KVM_EXIT_RISCV_SBI */
+ struct {
+ unsigned long extension_id;
+ unsigned long function_id;
+ unsigned long args[6];
+ unsigned long ret[2];
+ } riscv_sbi;
/* Fix the size of the union. */
char padding[256];
};
diff --git a/meson.build b/meson.build
index e3386196ba4106a973edb7f9d07ce9576c11981c..14728b77ab0ef81145b0de8dbdaf4b2fd522b2b2 100644
--- a/meson.build
+++ b/meson.build
@@ -69,6 +69,8 @@ elif cpu in ['ppc', 'ppc64']
kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
elif cpu in ['mips', 'mips64']
kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu']
+elif cpu in ['riscv32', 'riscv64']
+ kvm_targets = ['riscv32-softmmu', 'riscv64-softmmu']
else
kvm_targets = []
endif
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 6a0264fc6b194358138ff23bc246e55c728b7599..439dc89ee754f686cb7ced3337fb69e7be758339 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -29,6 +29,7 @@
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "fpu/softfloat-helpers.h"
+#include "kvm_riscv.h"
/* RISC-V CPU definitions */
@@ -185,6 +186,10 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
#endif
+static void riscv_host_cpu_init(Object *obj)
+{
+}
+
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
{
ObjectClass *oc;
@@ -330,6 +335,9 @@ static void riscv_cpu_reset(DeviceState *dev)
cs->exception_index = EXCP_NONE;
env->load_res = -1;
set_default_nan_mode(1, &env->fp_status);
+#ifdef CONFIG_KVM
+ kvm_riscv_reset_vcpu(cpu);
+#endif
}
static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
@@ -637,10 +645,12 @@ static const TypeInfo riscv_cpu_type_infos[] = {
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rvxx_sifive_e_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32_imafcu_nommu_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rvxx_sifive_u_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_HOST, riscv_host_cpu_init),
#elif defined(TARGET_RISCV64)
DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rvxx_sifive_e_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rvxx_sifive_u_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_HOST, riscv_host_cpu_init),
#endif
};
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index c0a326c8430c8f3a28d5469cd9b68f6a01d892ec..16d6050ead45255d035472382683af9eca238b65 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -43,6 +43,7 @@
#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51")
#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34")
#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54")
+#define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host")
#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2))
#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2))
@@ -233,6 +234,15 @@ struct CPURISCVState {
/* Fields from here on are preserved across CPU reset. */
QEMUTimer *timer; /* Internal timer */
+
+ hwaddr kernel_addr;
+ hwaddr fdt_addr;
+
+ /* kvm timer */
+ bool kvm_timer_dirty;
+ uint64_t kvm_timer_time;
+ uint64_t kvm_timer_compare;
+ uint64_t kvm_timer_state;
};
OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c
new file mode 100644
index 0000000000000000000000000000000000000000..1e16d24544a0d520ce9db6622869499c0ef1584f
--- /dev/null
+++ b/target/riscv/kvm.c
@@ -0,0 +1,601 @@
+/*
+ * RISC-V implementation of KVM hooks
+ *
+ * Copyright (c) 2020 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "qemu/osdep.h"
+#include
+
+#include
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "sysemu/kvm_int.h"
+#include "cpu.h"
+#include "trace.h"
+#include "hw/pci/pci.h"
+#include "exec/memattrs.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "hw/irq.h"
+#include "qemu/log.h"
+#include "hw/loader.h"
+#include "kvm_riscv.h"
+#include "sbi_ecall_interface.h"
+#include "chardev/char-fe.h"
+#include "sysemu/runstate.h"
+
+static __u64 kvm_riscv_reg_id(__u64 type, __u64 idx)
+{
+ __u64 id = KVM_REG_RISCV | type | idx;
+
+#if defined(TARGET_RISCV32)
+ id |= KVM_REG_SIZE_U32;
+#elif defined(TARGET_RISCV64)
+ id |= KVM_REG_SIZE_U64;
+#endif
+ return id;
+}
+
+#define RISCV_CORE_REG(name) kvm_riscv_reg_id(KVM_REG_RISCV_CORE, \
+ KVM_REG_RISCV_CORE_REG(name))
+
+#define RISCV_CSR_REG(name) kvm_riscv_reg_id(KVM_REG_RISCV_CSR, \
+ KVM_REG_RISCV_CSR_REG(name))
+
+#define RISCV_TIMER_REG(name) kvm_riscv_reg_id(KVM_REG_RISCV_TIMER, \
+ KVM_REG_RISCV_TIMER_REG(name))
+
+#define RISCV_FP_F_REG(idx) kvm_riscv_reg_id(KVM_REG_RISCV_FP_F, idx)
+
+#define RISCV_FP_D_REG(idx) kvm_riscv_reg_id(KVM_REG_RISCV_FP_D, idx)
+
+static int kvm_riscv_get_regs_core(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ target_ulong reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ ret = kvm_get_one_reg(cs, RISCV_CORE_REG(regs.pc), ®);
+ if (ret) {
+ return ret;
+ }
+ env->pc = reg;
+
+ for (i = 1; i < 32; i++) {
+ __u64 id = kvm_riscv_reg_id(KVM_REG_RISCV_CORE, i);
+ ret = kvm_get_one_reg(cs, id, ®);
+ if (ret) {
+ return ret;
+ }
+ env->gpr[i] = reg;
+ }
+
+ return ret;
+}
+
+static int kvm_riscv_put_regs_core(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ target_ulong reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ reg = env->pc;
+ ret = kvm_set_one_reg(cs, RISCV_CORE_REG(regs.pc), ®);
+ if (ret) {
+ return ret;
+ }
+
+ for (i = 1; i < 32; i++) {
+ __u64 id = kvm_riscv_reg_id(KVM_REG_RISCV_CORE, i);
+ reg = env->gpr[i];
+ ret = kvm_set_one_reg(cs, id, ®);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int kvm_riscv_get_regs_csr(CPUState *cs)
+{
+ int ret = 0;
+ target_ulong reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(sstatus), ®);
+ if (ret) {
+ return ret;
+ }
+ env->mstatus = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(sie), ®);
+ if (ret) {
+ return ret;
+ }
+ env->mie = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(stvec), ®);
+ if (ret) {
+ return ret;
+ }
+ env->stvec = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(sscratch), ®);
+ if (ret) {
+ return ret;
+ }
+ env->sscratch = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(sepc), ®);
+ if (ret) {
+ return ret;
+ }
+ env->sepc = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(scause), ®);
+ if (ret) {
+ return ret;
+ }
+ env->scause = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(stval), ®);
+ if (ret) {
+ return ret;
+ }
+ env->sbadaddr = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(sip), ®);
+ if (ret) {
+ return ret;
+ }
+ env->mip = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_CSR_REG(satp), ®);
+ if (ret) {
+ return ret;
+ }
+ env->satp = reg;
+
+ return ret;
+}
+
+static int kvm_riscv_put_regs_csr(CPUState *cs)
+{
+ int ret = 0;
+ target_ulong reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ reg = env->mstatus;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(sstatus), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->mie;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(sie), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->stvec;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(stvec), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->sscratch;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(sscratch), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->sepc;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(sepc), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->scause;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(scause), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->sbadaddr;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(stval), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->mip;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(sip), ®);
+ if (ret) {
+ return ret;
+ }
+
+ reg = env->satp;
+ ret = kvm_set_one_reg(cs, RISCV_CSR_REG(satp), ®);
+ if (ret) {
+ return ret;
+ }
+
+ return ret;
+}
+
+
+static int kvm_riscv_get_regs_fp(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (riscv_has_ext(env, RVD)) {
+ uint64_t reg;
+ for (i = 0; i < 32; i++) {
+ ret = kvm_get_one_reg(cs, RISCV_FP_D_REG(i), ®);
+ if (ret) {
+ return ret;
+ }
+ env->fpr[i] = reg;
+ }
+ return ret;
+ }
+
+ if (riscv_has_ext(env, RVF)) {
+ uint32_t reg;
+ for (i = 0; i < 32; i++) {
+ ret = kvm_get_one_reg(cs, RISCV_FP_F_REG(i), ®);
+ if (ret) {
+ return ret;
+ }
+ env->fpr[i] = reg;
+ }
+ return ret;
+ }
+
+ return ret;
+}
+
+static int kvm_riscv_put_regs_fp(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (riscv_has_ext(env, RVD)) {
+ uint64_t reg;
+ for (i = 0; i < 32; i++) {
+ reg = env->fpr[i];
+ ret = kvm_set_one_reg(cs, RISCV_FP_D_REG(i), ®);
+ if (ret) {
+ return ret;
+ }
+ }
+ return ret;
+ }
+
+ if (riscv_has_ext(env, RVF)) {
+ uint32_t reg;
+ for (i = 0; i < 32; i++) {
+ reg = env->fpr[i];
+ ret = kvm_set_one_reg(cs, RISCV_FP_F_REG(i), ®);
+ if (ret) {
+ return ret;
+ }
+ }
+ return ret;
+ }
+
+ return ret;
+}
+
+static void kvm_riscv_get_regs_timer(CPUState *cs)
+{
+ int ret;
+ uint64_t reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (env->kvm_timer_dirty) {
+ return;
+ }
+
+ ret = kvm_get_one_reg(cs, RISCV_TIMER_REG(time), ®);
+ if (ret) {
+ abort();
+ }
+ env->kvm_timer_time = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_TIMER_REG(compare), ®);
+ if (ret) {
+ abort();
+ }
+ env->kvm_timer_compare = reg;
+
+ ret = kvm_get_one_reg(cs, RISCV_TIMER_REG(state), ®);
+ if (ret) {
+ abort();
+ }
+ env->kvm_timer_state = reg;
+
+ env->kvm_timer_dirty = true;
+}
+
+static void kvm_riscv_put_regs_timer(CPUState *cs)
+{
+ int ret;
+ uint64_t reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (!env->kvm_timer_dirty) {
+ return;
+ }
+
+ reg = env->kvm_timer_time;
+ ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(time), ®);
+ if (ret) {
+ abort();
+ }
+
+ reg = env->kvm_timer_compare;
+ ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(compare), ®);
+ if (ret) {
+ abort();
+ }
+
+ /*
+ * To set register of RISCV_TIMER_REG(state) will occur a error from KVM
+ * on env->kvm_timer_state == 0, It's better to adapt in KVM, but it
+ * doesn't matter that adaping in QEMU now.
+ * TODO If KVM changes, adapt here.
+ */
+ if (env->kvm_timer_state) {
+ reg = env->kvm_timer_state;
+ ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(state), ®);
+ if (ret) {
+ abort();
+ }
+ }
+
+ env->kvm_timer_dirty = false;
+}
+
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_LAST_INFO
+};
+
+int kvm_arch_get_registers(CPUState *cs)
+{
+ int ret = 0;
+
+ ret = kvm_riscv_get_regs_core(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_get_regs_csr(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_get_regs_fp(cs);
+ if (ret) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int kvm_arch_put_registers(CPUState *cs, int level)
+{
+ int ret = 0;
+
+ ret = kvm_riscv_put_regs_core(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_put_regs_csr(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_put_regs_fp(cs);
+ if (ret) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int kvm_arch_release_virq_post(int virq)
+{
+ return 0;
+}
+
+int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
+ uint64_t address, uint32_t data, PCIDevice *dev)
+{
+ return 0;
+}
+
+int kvm_arch_destroy_vcpu(CPUState *cs)
+{
+ return 0;
+}
+
+unsigned long kvm_arch_vcpu_id(CPUState *cpu)
+{
+ return cpu->cpu_index;
+}
+
+static void kvm_riscv_vm_state_change(void *opaque, int running, RunState state)
+{
+ CPUState *cs = opaque;
+
+ if (running) {
+ kvm_riscv_put_regs_timer(cs);
+ } else {
+ kvm_riscv_get_regs_timer(cs);
+ }
+}
+
+void kvm_arch_init_irq_routing(KVMState *s)
+{
+}
+
+int kvm_arch_init_vcpu(CPUState *cs)
+{
+ int ret = 0;
+ target_ulong isa;
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ __u64 id;
+
+ qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs);
+
+ id = kvm_riscv_reg_id(KVM_REG_RISCV_CONFIG, KVM_REG_RISCV_CONFIG_REG(isa));
+ ret = kvm_get_one_reg(cs, id, &isa);
+ if (ret) {
+ return ret;
+ }
+ env->misa = isa;
+
+ return ret;
+}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ abort();
+}
+
+int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route,
+ int vector, PCIDevice *dev)
+{
+ return 0;
+}
+
+int kvm_arch_init(MachineState *ms, KVMState *s)
+{
+ return 0;
+}
+
+int kvm_arch_irqchip_create(KVMState *s)
+{
+ return 0;
+}
+
+int kvm_arch_process_async_events(CPUState *cs)
+{
+ return 0;
+}
+
+void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
+{
+}
+
+MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
+{
+ return MEMTXATTRS_UNSPECIFIED;
+}
+
+bool kvm_arch_stop_on_emulation_error(CPUState *cs)
+{
+ return true;
+}
+
+static int kvm_riscv_handle_sbi(struct kvm_run *run)
+{
+ int ret = 0;
+ unsigned char ch;
+ switch (run->riscv_sbi.extension_id) {
+ case SBI_EXT_0_1_CONSOLE_PUTCHAR:
+ ch = run->riscv_sbi.args[0];
+ qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch));
+ break;
+ case SBI_EXT_0_1_CONSOLE_GETCHAR:
+ ret = qemu_chr_fe_read_all(serial_hd(0)->be, &ch, sizeof(ch));
+ if (ret == sizeof(ch)) {
+ run->riscv_sbi.args[0] = ch;
+ } else {
+ run->riscv_sbi.args[0] = -1;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: un-handled SBI EXIT, specific reasons is %lu\n",
+ __func__, run->riscv_sbi.extension_id);
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
+{
+ int ret = 0;
+ switch (run->exit_reason) {
+ case KVM_EXIT_RISCV_SBI:
+ ret = kvm_riscv_handle_sbi(run);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n",
+ __func__, run->exit_reason);
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+void kvm_riscv_reset_vcpu(RISCVCPU *cpu)
+{
+ CPURISCVState *env = &cpu->env;
+
+ if (!kvm_enabled()) {
+ return;
+ }
+ env->pc = cpu->env.kernel_addr;
+ env->gpr[10] = kvm_arch_vcpu_id(CPU(cpu)); /* a0 */
+ env->gpr[11] = cpu->env.fdt_addr; /* a1 */
+ env->satp = 0;
+}
+
+void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level)
+{
+ int ret;
+ unsigned virq = level ? KVM_INTERRUPT_SET : KVM_INTERRUPT_UNSET;
+
+ if (irq != IRQ_S_EXT) {
+ return;
+ }
+
+ if (!kvm_enabled()) {
+ return;
+ }
+
+ ret = kvm_vcpu_ioctl(CPU(cpu), KVM_INTERRUPT, &virq);
+ if (ret < 0) {
+ perror("Set irq failed");
+ abort();
+ }
+}
diff --git a/target/riscv/kvm_riscv.h b/target/riscv/kvm_riscv.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed281bdce02839524aef9ab0cb91447944cd6e39
--- /dev/null
+++ b/target/riscv/kvm_riscv.h
@@ -0,0 +1,25 @@
+/*
+ * QEMU KVM support -- RISC-V specific functions.
+ *
+ * Copyright (c) 2020 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#ifndef QEMU_KVM_RISCV_H
+#define QEMU_KVM_RISCV_H
+
+void kvm_riscv_reset_vcpu(RISCVCPU *cpu);
+void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level);
+
+#endif
diff --git a/target/riscv/machine.c b/target/riscv/machine.c
index 44d4015bd675989c5c8b366dce544d74cc8202f3..ef2d5395a837c221647d51d632a0fafd98a4108c 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -138,10 +138,20 @@ static const VMStateDescription vmstate_hyper = {
}
};
+static int cpu_post_load(void *opaque, int version_id)
+{
+ RISCVCPU *cpu = opaque;
+ CPURISCVState *env = &cpu->env;
+
+ env->kvm_timer_dirty = true;
+ return 0;
+}
+
const VMStateDescription vmstate_riscv_cpu = {
.name = "cpu",
.version_id = 1,
.minimum_version_id = 1,
+ .post_load = cpu_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32),
VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32),
@@ -185,6 +195,10 @@ const VMStateDescription vmstate_riscv_cpu = {
VMSTATE_UINT64(env.mtohost, RISCVCPU),
VMSTATE_UINT64(env.timecmp, RISCVCPU),
+ VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU),
+ VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU),
+ VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU),
+
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * []) {
diff --git a/target/riscv/meson.build b/target/riscv/meson.build
index 14a5c62daced89abef1c0c6d9fe882db6675de82..12e9c074afa643a7d9a978fe2720955b754b36fb 100644
--- a/target/riscv/meson.build
+++ b/target/riscv/meson.build
@@ -23,6 +23,7 @@ riscv_ss.add(files(
'vector_helper.c',
'translate.c',
))
+riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'))
riscv_softmmu_ss = ss.source_set()
riscv_softmmu_ss.add(files(
diff --git a/target/riscv/sbi_ecall_interface.h b/target/riscv/sbi_ecall_interface.h
new file mode 100644
index 0000000000000000000000000000000000000000..fb1a3fa8f23f47be34560ce10d2aca13e7b420c9
--- /dev/null
+++ b/target/riscv/sbi_ecall_interface.h
@@ -0,0 +1,72 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel
+ */
+
+#ifndef __SBI_ECALL_INTERFACE_H__
+#define __SBI_ECALL_INTERFACE_H__
+
+/* clang-format off */
+
+/* SBI Extension IDs */
+#define SBI_EXT_0_1_SET_TIMER 0x0
+#define SBI_EXT_0_1_CONSOLE_PUTCHAR 0x1
+#define SBI_EXT_0_1_CONSOLE_GETCHAR 0x2
+#define SBI_EXT_0_1_CLEAR_IPI 0x3
+#define SBI_EXT_0_1_SEND_IPI 0x4
+#define SBI_EXT_0_1_REMOTE_FENCE_I 0x5
+#define SBI_EXT_0_1_REMOTE_SFENCE_VMA 0x6
+#define SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID 0x7
+#define SBI_EXT_0_1_SHUTDOWN 0x8
+#define SBI_EXT_BASE 0x10
+#define SBI_EXT_TIME 0x54494D45
+#define SBI_EXT_IPI 0x735049
+#define SBI_EXT_RFENCE 0x52464E43
+#define SBI_EXT_HSM 0x48534D
+
+/* SBI function IDs for BASE extension*/
+#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0
+#define SBI_EXT_BASE_GET_IMP_ID 0x1
+#define SBI_EXT_BASE_GET_IMP_VERSION 0x2
+#define SBI_EXT_BASE_PROBE_EXT 0x3
+#define SBI_EXT_BASE_GET_MVENDORID 0x4
+#define SBI_EXT_BASE_GET_MARCHID 0x5
+#define SBI_EXT_BASE_GET_MIMPID 0x6
+
+/* SBI function IDs for TIME extension*/
+#define SBI_EXT_TIME_SET_TIMER 0x0
+
+/* SBI function IDs for IPI extension*/
+#define SBI_EXT_IPI_SEND_IPI 0x0
+
+/* SBI function IDs for RFENCE extension*/
+#define SBI_EXT_RFENCE_REMOTE_FENCE_I 0x0
+#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA 0x1
+#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID 0x2
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA 0x3
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID 0x4
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA 0x5
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID 0x6
+
+/* SBI function IDs for HSM extension */
+#define SBI_EXT_HSM_HART_START 0x0
+#define SBI_EXT_HSM_HART_STOP 0x1
+#define SBI_EXT_HSM_HART_GET_STATUS 0x2
+
+#define SBI_HSM_HART_STATUS_STARTED 0x0
+#define SBI_HSM_HART_STATUS_STOPPED 0x1
+#define SBI_HSM_HART_STATUS_START_PENDING 0x2
+#define SBI_HSM_HART_STATUS_STOP_PENDING 0x3
+
+#define SBI_SPEC_VERSION_MAJOR_OFFSET 24
+#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
+#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff
+#define SBI_EXT_VENDOR_START 0x09000000
+#define SBI_EXT_VENDOR_END 0x09FFFFFF
+/* clang-format on */
+
+#endif