diff options
Diffstat (limited to 'target/riscv/kvm/kvm-cpu.c')
| -rw-r--r-- | target/riscv/kvm/kvm-cpu.c | 250 |
1 files changed, 200 insertions, 50 deletions
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 680a729cd8..422e4f121c 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -86,6 +86,27 @@ static uint64_t kvm_riscv_reg_id_u64(uint64_t type, uint64_t idx) return KVM_REG_RISCV | KVM_REG_SIZE_U64 | type | idx; } +static uint64_t kvm_encode_reg_size_id(uint64_t id, size_t size_b) +{ + uint64_t size_ctz = __builtin_ctz(size_b); + + return id | (size_ctz << KVM_REG_SIZE_SHIFT); +} + +static uint64_t kvm_riscv_vector_reg_id(RISCVCPU *cpu, + uint64_t idx) +{ + uint64_t id; + size_t size_b; + + g_assert(idx < 32); + + id = KVM_REG_RISCV | KVM_REG_RISCV_VECTOR | KVM_REG_RISCV_VECTOR_REG(idx); + size_b = cpu->cfg.vlenb; + + return kvm_encode_reg_size_id(id, size_b); +} + #define RISCV_CORE_REG(env, name) \ kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, \ KVM_REG_RISCV_CORE_REG(name)) @@ -145,7 +166,7 @@ typedef struct KVMCPUConfig { const char *name; const char *description; target_ulong offset; - int kvm_reg_id; + uint64_t kvm_reg_id; bool user_set; bool supported; } KVMCPUConfig; @@ -352,29 +373,12 @@ static KVMCPUConfig kvm_cboz_blocksize = { .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) }; -static void kvm_cpu_set_cbomz_blksize(Object *obj, Visitor *v, - const char *name, - void *opaque, Error **errp) -{ - KVMCPUConfig *cbomz_cfg = opaque; - RISCVCPU *cpu = RISCV_CPU(obj); - uint16_t value, *host_val; - - if (!visit_type_uint16(v, name, &value, errp)) { - return; - } - - host_val = kvmconfig_get_cfg_addr(cpu, cbomz_cfg); - - if (value != *host_val) { - error_report("Unable to set %s to a different value than " - "the host (%u)", - cbomz_cfg->name, *host_val); - exit(EXIT_FAILURE); - } - - cbomz_cfg->user_set = true; -} +static KVMCPUConfig kvm_v_vlenb = { + .name = "vlenb", + .offset = CPU_CFG_OFFSET(vlenb), + .kvm_reg_id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_VECTOR | + KVM_REG_RISCV_VECTOR_CSR_REG(vlenb) +}; static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) { @@ -493,14 +497,6 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) NULL, multi_cfg); } - object_property_add(cpu_obj, "cbom_blocksize", "uint16", - NULL, kvm_cpu_set_cbomz_blksize, - NULL, &kvm_cbom_blocksize); - - object_property_add(cpu_obj, "cboz_blocksize", "uint16", - NULL, kvm_cpu_set_cbomz_blksize, - NULL, &kvm_cboz_blocksize); - riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_extensions); riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_vendor_exts); riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_experimental_exts); @@ -716,9 +712,11 @@ static void kvm_riscv_put_regs_timer(CPUState *cs) static int kvm_riscv_get_regs_vector(CPUState *cs) { - CPURISCVState *env = &RISCV_CPU(cs)->env; + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; target_ulong reg; - int ret = 0; + uint64_t vreg_id; + int vreg_idx, ret = 0; if (!riscv_has_ext(env, RVV)) { return 0; @@ -742,14 +740,39 @@ static int kvm_riscv_get_regs_vector(CPUState *cs) } env->vtype = reg; + if (kvm_v_vlenb.supported) { + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vlenb), ®); + if (ret) { + return ret; + } + cpu->cfg.vlenb = reg; + + for (int i = 0; i < 32; i++) { + /* + * vreg[] is statically allocated using RV_VLEN_MAX. + * Use it instead of vlenb to calculate vreg_idx for + * simplicity. + */ + vreg_idx = i * RV_VLEN_MAX / 64; + vreg_id = kvm_riscv_vector_reg_id(cpu, i); + + ret = kvm_get_one_reg(cs, vreg_id, &env->vreg[vreg_idx]); + if (ret) { + return ret; + } + } + } + return 0; } static int kvm_riscv_put_regs_vector(CPUState *cs) { - CPURISCVState *env = &RISCV_CPU(cs)->env; + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; target_ulong reg; - int ret = 0; + uint64_t vreg_id; + int vreg_idx, ret = 0; if (!riscv_has_ext(env, RVV)) { return 0; @@ -769,6 +792,29 @@ static int kvm_riscv_put_regs_vector(CPUState *cs) reg = env->vtype; ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vtype), ®); + if (ret) { + return ret; + } + + if (kvm_v_vlenb.supported) { + reg = cpu->cfg.vlenb; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vlenb), ®); + + for (int i = 0; i < 32; i++) { + /* + * vreg[] is statically allocated using RV_VLEN_MAX. + * Use it instead of vlenb to calculate vreg_idx for + * simplicity. + */ + vreg_idx = i * RV_VLEN_MAX / 64; + vreg_id = kvm_riscv_vector_reg_id(cpu, i); + + ret = kvm_set_one_reg(cs, vreg_id, &env->vreg[vreg_idx]); + if (ret) { + return ret; + } + } + } return ret; } @@ -953,6 +999,33 @@ static int uint64_cmp(const void *a, const void *b) return 0; } +static void kvm_riscv_read_vlenb(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, + struct kvm_reg_list *reglist) +{ + struct kvm_one_reg reg; + struct kvm_reg_list *reg_search; + uint64_t val; + int ret; + + reg_search = bsearch(&kvm_v_vlenb.kvm_reg_id, reglist->reg, reglist->n, + sizeof(uint64_t), uint64_cmp); + + if (reg_search) { + reg.id = kvm_v_vlenb.kvm_reg_id; + reg.addr = (uint64_t)&val; + + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to read vlenb register, error code: %s", + strerrorname_np(errno)); + exit(EXIT_FAILURE); + } + + kvm_v_vlenb.supported = true; + cpu->cfg.vlenb = val; + } +} + static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { KVMCPUConfig *multi_ext_cfg; @@ -1027,6 +1100,10 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) if (cpu->cfg.ext_zicboz) { kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); } + + if (riscv_has_ext(&cpu->env, RVV)) { + kvm_riscv_read_vlenb(cpu, kvmcpu, reglist); + } } static void riscv_init_kvm_registers(Object *cpu_obj) @@ -1559,19 +1636,10 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, static void kvm_cpu_instance_init(CPUState *cs) { Object *obj = OBJECT(RISCV_CPU(cs)); - DeviceState *dev = DEVICE(obj); riscv_init_kvm_registers(obj); kvm_riscv_add_cpu_user_properties(obj); - - for (Property *prop = riscv_cpu_options; prop && prop->name; prop++) { - /* Check if we have a specific KVM handler for the option */ - if (object_property_find(obj, prop->name)) { - continue; - } - qdev_property_add_static(dev, prop); - } } /* @@ -1598,6 +1666,88 @@ static bool kvm_cpu_realize(CPUState *cs, Error **errp) return true; } +void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +{ + CPURISCVState *env = &cpu->env; + KVMScratchCPU kvmcpu; + struct kvm_one_reg reg; + uint64_t val; + int ret; + + /* short-circuit without spinning the scratch CPU */ + if (!cpu->cfg.ext_zicbom && !cpu->cfg.ext_zicboz && + !riscv_has_ext(env, RVV)) { + return; + } + + if (!kvm_riscv_create_scratch_vcpu(&kvmcpu)) { + error_setg(errp, "Unable to create scratch KVM cpu"); + return; + } + + if (cpu->cfg.ext_zicbom && + riscv_cpu_option_set(kvm_cbom_blocksize.name)) { + + reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + kvm_cbom_blocksize.kvm_reg_id); + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_setg(errp, "Unable to read cbom_blocksize, error %d", errno); + return; + } + + if (cpu->cfg.cbom_blocksize != val) { + error_setg(errp, "Unable to set cbom_blocksize to a different " + "value than the host (%lu)", val); + return; + } + } + + if (cpu->cfg.ext_zicboz && + riscv_cpu_option_set(kvm_cboz_blocksize.name)) { + + reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + kvm_cboz_blocksize.kvm_reg_id); + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_setg(errp, "Unable to read cboz_blocksize, error %d", errno); + return; + } + + if (cpu->cfg.cboz_blocksize != val) { + error_setg(errp, "Unable to set cboz_blocksize to a different " + "value than the host (%lu)", val); + return; + } + } + + /* Users are setting vlen, not vlenb */ + if (riscv_has_ext(env, RVV) && riscv_cpu_option_set("vlen")) { + if (!kvm_v_vlenb.supported) { + error_setg(errp, "Unable to set 'vlenb': register not supported"); + return; + } + + reg.id = kvm_v_vlenb.kvm_reg_id; + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_setg(errp, "Unable to read vlenb register, error %d", errno); + return; + } + + if (cpu->cfg.vlenb != val) { + error_setg(errp, "Unable to set 'vlen' to a different " + "value than the host (%lu)", val * 8); + return; + } + } + + kvm_riscv_destroy_scratch_vcpu(&kvmcpu); +} + static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); @@ -1619,14 +1769,14 @@ static void kvm_cpu_accel_register_types(void) } type_init(kvm_cpu_accel_register_types); -static void riscv_host_cpu_init(Object *obj) +static void riscv_host_cpu_class_init(ObjectClass *c, void *data) { - CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); #if defined(TARGET_RISCV32) - env->misa_mxl_max = env->misa_mxl = MXL_RV32; + mcc->misa_mxl_max = MXL_RV32; #elif defined(TARGET_RISCV64) - env->misa_mxl_max = env->misa_mxl = MXL_RV64; + mcc->misa_mxl_max = MXL_RV64; #endif } @@ -1634,7 +1784,7 @@ static const TypeInfo riscv_kvm_cpu_type_infos[] = { { .name = TYPE_RISCV_CPU_HOST, .parent = TYPE_RISCV_CPU, - .instance_init = riscv_host_cpu_init, + .class_init = riscv_host_cpu_class_init, } }; |