This series adds RISC-V vm migration support.
Yifei Jiang (6): target/riscv: raise exception to HS mode at get_physical_address target/riscv: Add sifive_plic vmstate target/riscv: Support riscv cpu vmstate target/riscv: Add kvm_riscv_get/put_regs_timer target/riscv: Implement virtual time adjusting with vm state changing target/riscv: Support virtual time context synchronization
hw/riscv/sifive_plic.c | 24 +++++++++- include/hw/riscv/sifive_plic.h | 1 + target/riscv/cpu.c | 31 +++++++++++- target/riscv/cpu.h | 7 +++ target/riscv/cpu_helper.c | 12 ++++- target/riscv/kvm.c | 86 ++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 5 deletions(-)
VS-stage translation at get_physical_address needs to translate pte address by G-stage translation. But the G-stage translation error can not be distinguished from VS-stage translation error in riscv_cpu_tlb_fill. On migration, destination needs to rebuild pte, and this G-stage translation error must be handled by HS-mode. So introduce TRANSLATE_STAGE2_FAIL so that riscv_cpu_tlb_fill could distinguish and raise it to HS-mode.
Signed-off-by: Yifei Jiang jiangyifei@huawei.com Signed-off-by: Yipeng Yin yinyipeng1@huawei.com --- target/riscv/cpu.h | 1 + target/riscv/cpu_helper.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f69f38df75..a7042e122e 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -81,6 +81,7 @@ enum { #define TRANSLATE_FAIL 1 #define TRANSLATE_SUCCESS 0 #define MMU_USER_IDX 3 +#define TRANSLATE_G_STAGE_FAIL 4
#define MAX_RISCV_PMPS (16)
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index eccd80cfef..5b861a47f1 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -449,7 +449,10 @@ restart: mmu_idx, false, true);
if (vbase_ret != TRANSLATE_SUCCESS) { - return vbase_ret; + env->guest_phys_fault_addr = (base | + (addr & + (TARGET_PAGE_SIZE - 1))) >> 2; + return TRANSLATE_G_STAGE_FAIL; }
pte_addr = vbase + idx * ptesize; @@ -724,12 +727,17 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true, true);
+ if (ret == TRANSLATE_G_STAGE_FAIL) { + first_stage_error = false; + access_type = MMU_DATA_LOAD; + } + qemu_log_mask(CPU_LOG_MMU, "%s 1st-stage address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot);
- if (ret != TRANSLATE_FAIL) { + if (ret != TRANSLATE_FAIL && ret != TRANSLATE_G_STAGE_FAIL) { /* Second stage lookup */ im_address = pa;
Add sifive_plic vmstate for supporting sifive_plic migration. Current vmstate framework only supports one structure parameter as num field to describe variable length arrays, so introduce num_enables.
Signed-off-by: Yifei Jiang jiangyifei@huawei.com Signed-off-by: Yipeng Yin yinyipeng1@huawei.com --- hw/riscv/sifive_plic.c | 24 +++++++++++++++++++++++- include/hw/riscv/sifive_plic.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index be2fb0706e..4ed5fd43bf 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -31,6 +31,7 @@ #include "hw/riscv/sifive_plic.h" #include "sysemu/kvm.h" #include "kvm_riscv.h" +#include "migration/vmstate.h"
#define RISCV_DEBUG_PLIC 0
@@ -452,11 +453,12 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) TYPE_SIFIVE_PLIC, plic->aperture_size); parse_hart_config(plic); plic->bitfield_words = (plic->num_sources + 31) >> 5; + plic->num_enables = plic->bitfield_words * plic->num_addrs; plic->source_priority = g_new0(uint32_t, plic->num_sources); plic->target_priority = g_new(uint32_t, plic->num_addrs); plic->pending = g_new0(uint32_t, plic->bitfield_words); plic->claimed = g_new0(uint32_t, plic->bitfield_words); - plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); + plic->enable = g_new0(uint32_t, plic->num_enables); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
@@ -476,12 +478,32 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; }
+static const VMStateDescription vmstate_sifive_plic = { + .name = "riscv_sifive_plic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState, num_sources, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState, num_addrs, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_END_OF_LIST() + } +}; + static void sifive_plic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass);
device_class_set_props(dc, sifive_plic_properties); dc->realize = sifive_plic_realize; + dc->vmsd = &vmstate_sifive_plic; }
static const TypeInfo sifive_plic_info = { diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h index ace76d0f1b..96a792cd04 100644 --- a/include/hw/riscv/sifive_plic.h +++ b/include/hw/riscv/sifive_plic.h @@ -50,6 +50,7 @@ typedef struct SiFivePLICState { uint32_t num_addrs; uint32_t num_harts; uint32_t bitfield_words; + uint32_t num_enables; PLICAddr *addr_config; uint32_t *source_priority; uint32_t *target_priority;
Describe gpr, fpr and csr in vmstate_riscv_cpu.
Signed-off-by: Yifei Jiang jiangyifei@huawei.com Signed-off-by: Yipeng Yin yinyipeng1@huawei.com --- target/riscv/cpu.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b7de444fbf..bbc9726a20 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/qdev-properties.h" -#include "migration/vmstate.h" +#include "migration/cpu.h" #include "fpu/softfloat-helpers.h" #include "kvm_riscv.h"
@@ -473,7 +473,23 @@ static void riscv_cpu_init(Object *obj)
static const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .unmigratable = 1, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), + VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32), + VMSTATE_UINTTL(env.pc, RISCVCPU), + VMSTATE_UINTTL(env.mstatus, RISCVCPU), + VMSTATE_UINTTL(env.mie, RISCVCPU), + VMSTATE_UINTTL(env.stvec, RISCVCPU), + VMSTATE_UINTTL(env.sscratch, RISCVCPU), + VMSTATE_UINTTL(env.sepc, RISCVCPU), + VMSTATE_UINTTL(env.scause, RISCVCPU), + VMSTATE_UINTTL(env.sbadaddr, RISCVCPU), + VMSTATE_UINTTL(env.mip, RISCVCPU), + VMSTATE_UINTTL(env.satp, RISCVCPU), + VMSTATE_END_OF_LIST() + } };
static Property riscv_cpu_properties[] = {
Add kvm_riscv_get/put_regs_timer to synchronize virtual time context from KVM. The frequency of virtual time is not supported by KVM_SET_ONE_REG, So it's useless to synchronize the frequency of virtual time.
To set register of RISCV_TIMER_REG(state) will occur a error from KVM on kvm_timer_state == 0. It's better to adapt in KVM, but it doesn't matter that adaping in QEMU.
Signed-off-by: Yifei Jiang jiangyifei@huawei.com Signed-off-by: Yipeng Yin yinyipeng1@huawei.com --- target/riscv/cpu.h | 6 ++++ target/riscv/kvm.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index a7042e122e..d3e10ea92f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -211,6 +211,12 @@ struct CPURISCVState {
hwaddr loader_start; hwaddr fdt_start; + + /* kvm timer */ + bool kvm_timer_dirty; + uint64_t kvm_timer_time; + uint64_t kvm_timer_compare; + uint64_t kvm_timer_state; };
#define RISCV_CPU_CLASS(klass) \ diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index d0d02aad5c..7466787d79 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -59,6 +59,9 @@ static __u64 kvm_riscv_reg_id(__u64 type, __u64 idx) #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) @@ -294,6 +297,75 @@ static int kvm_riscv_put_regs_fp(CPUState *cs) 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
We hope that virtual time adjusts with vm state changing. When a vm is stopped, guest virtual time should stop counting and kvm_timer should be stopped. When the vm is resumed, guest virtual time should continue to count and kvm_timer should be restored.
Signed-off-by: Yifei Jiang jiangyifei@huawei.com Signed-off-by: Yipeng Yin yinyipeng1@huawei.com --- target/riscv/kvm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c index 7466787d79..0ab5de43d3 100644 --- a/target/riscv/kvm.c +++ b/target/riscv/kvm.c @@ -40,6 +40,7 @@ #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) { @@ -436,6 +437,17 @@ 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) { } @@ -447,6 +459,8 @@ int kvm_arch_init_vcpu(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); __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) {
Add virtual time context description to vmstate_riscv_cpu. After cpu being loaded, virtual time context is updated to KVM.
Signed-off-by: Yifei Jiang jiangyifei@huawei.com Signed-off-by: Yipeng Yin yinyipeng1@huawei.com --- target/riscv/cpu.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index bbc9726a20..9891d4012b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -471,10 +471,18 @@ static void riscv_cpu_init(Object *obj) cpu_set_cpustate_pointers(cpu); }
+static int cpu_post_load(void *opaque, int version_id) +{ + RISCVCPU *cpu = opaque; + cpu->env.kvm_timer_dirty = true; + return 0; +} + static 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), @@ -488,6 +496,9 @@ static const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.sbadaddr, RISCVCPU), VMSTATE_UINTTL(env.mip, RISCVCPU), VMSTATE_UINTTL(env.satp, 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() } };