[kvmtool PATCH 09/10] riscv: Add cpu-type command-line option
Anup Patel
apatel at ventanamicro.com
Thu Apr 24 05:57:35 PDT 2025
On Sat, Apr 12, 2025 at 6:45 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Wed, Mar 26, 2025 at 12:26:43PM +0530, Anup Patel wrote:
> > Currently, the KVMTOOL always creates a VM with all available
> > ISA extensions virtualized by the in-kernel KVM module.
> >
> > For better flexibility, add cpu-type command-line option using
> > which users can select one of the available CPU types for VM.
> >
> > There are two CPU types supported at the moment namely "min"
> > and "max". The "min" CPU type implies VCPU with rv[64|32]imafdc
> > ISA whereas the "max" CPU type implies VCPU with all available
> > ISA extensions.
> >
> > Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> > ---
> > riscv/aia.c | 2 +-
> > riscv/fdt.c | 220 +++++++++++++++++-----------
> > riscv/include/kvm/kvm-arch.h | 2 +
> > riscv/include/kvm/kvm-config-arch.h | 5 +
> > riscv/kvm.c | 2 +
> > 5 files changed, 143 insertions(+), 88 deletions(-)
> >
> > diff --git a/riscv/aia.c b/riscv/aia.c
> > index 21d9704..cad53d4 100644
> > --- a/riscv/aia.c
> > +++ b/riscv/aia.c
> > @@ -209,7 +209,7 @@ void aia__create(struct kvm *kvm)
> > .flags = 0,
> > };
> >
> > - if (kvm->cfg.arch.ext_disabled[KVM_RISCV_ISA_EXT_SSAIA])
> > + if (riscv__isa_extension_disabled(kvm, KVM_RISCV_ISA_EXT_SSAIA))
> > return;
> >
> > err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &aia_device);
> > diff --git a/riscv/fdt.c b/riscv/fdt.c
> > index 46efb47..4c018c8 100644
> > --- a/riscv/fdt.c
> > +++ b/riscv/fdt.c
> > @@ -13,82 +13,134 @@ struct isa_ext_info {
> > const char *name;
> > unsigned long ext_id;
> > bool multi_letter;
> > + bool min_cpu_included;
> > };
> >
> > struct isa_ext_info isa_info_arr[] = {
> > - /* single-letter */
> > - {"a", KVM_RISCV_ISA_EXT_A, false},
> > - {"c", KVM_RISCV_ISA_EXT_C, false},
> > - {"d", KVM_RISCV_ISA_EXT_D, false},
> > - {"f", KVM_RISCV_ISA_EXT_F, false},
> > - {"h", KVM_RISCV_ISA_EXT_H, false},
> > - {"i", KVM_RISCV_ISA_EXT_I, false},
> > - {"m", KVM_RISCV_ISA_EXT_M, false},
> > - {"v", KVM_RISCV_ISA_EXT_V, false},
> > + /* single-letter ordered canonically as "IEMAFDQCLBJTPVNSUHKORWXYZG" */
>
> The comment change and the tabulation should go in the previous patch.
Okay, I will update.
>
> > + {"i", KVM_RISCV_ISA_EXT_I, false, true},
> > + {"m", KVM_RISCV_ISA_EXT_M, false, true},
> > + {"a", KVM_RISCV_ISA_EXT_A, false, true},
> > + {"f", KVM_RISCV_ISA_EXT_F, false, true},
> > + {"d", KVM_RISCV_ISA_EXT_D, false, true},
> > + {"c", KVM_RISCV_ISA_EXT_C, false, true},
> > + {"v", KVM_RISCV_ISA_EXT_V, false, false},
> > + {"h", KVM_RISCV_ISA_EXT_H, false, false},
>
> To avoid keeping track of which boolean means what (and taking my .misa
> suggestion from the last patch), we can write these as, e.g.
>
> {"c", KVM_RISCV_ISA_EXT_C, .misa = true, .min_enabled = true },
> {"v", KVM_RISCV_ISA_EXT_V, .misa = true, },
>
> (Also renamed min_cpu_included to min_enabled since it better matches
> the enabled/disabled concept we use everywhere else.)
>
> And we don't need to change any of the multi-letter extension entries
> since we're adding something false for them.
Okay, I will update.
>
> > /* multi-letter sorted alphabetically */
> > - {"smnpm", KVM_RISCV_ISA_EXT_SMNPM, true},
> ...
> > };
> >
> > +static bool __isa_ext_disabled(struct kvm *kvm, struct isa_ext_info *info)
> > +{
> > + if (!strncmp(kvm->cfg.arch.cpu_type, "min", 3) &&
>
> kvm->cfg.arch.cpu_type can never be anything other than NULL, "min",
> "max", so we can just use strcmp.
Okay, I will update.
>
> > + !info->min_cpu_included)
> > + return true;
>
> If 'min_cpu_included' (or 'min_enabled') is all we plan to check for
> whether or not an extension is enabled for the 'min' cpu type, then
> we should write this as
>
> if (!strcmp(kvm->cfg.arch.cpu_type, "min"))
> return !info->min_enabled;
>
> Otherwise when min_enabled is true we'd still check
> kvm->cfg.arch.ext_disabled[info->ext_id].
The extensions part of "min" cpu_type can be disabled using
"--disable-xyz" command-line options hence the current approach.
>
> > +
> > + return kvm->cfg.arch.ext_disabled[info->ext_id];
> > +}
> > +
> > +static bool __isa_ext_warn_disable_failure(struct kvm *kvm, struct isa_ext_info *info)
> > +{
> > + if (!strncmp(kvm->cfg.arch.cpu_type, "min", 3) &&
>
> same strcmp comment
Okay, I will update.
>
> > + !info->min_cpu_included)
> > + return false;
> > +
> > + return true;
> > +}
> > +
> > +bool riscv__isa_extension_disabled(struct kvm *kvm, unsigned long isa_ext_id)
> > +{
> > + struct isa_ext_info *info = NULL;
> > + unsigned long i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(isa_info_arr); i++) {
> > + if (isa_info_arr[i].ext_id == isa_ext_id) {
> > + info = &isa_info_arr[i];
> > + break;
> > + }
> > + }
> > + if (!info)
> > + return true;
> > +
> > + return __isa_ext_disabled(kvm, info);
> > +}
> > +
> > +int riscv__cpu_type_parser(const struct option *opt, const char *arg, int unset)
> > +{
> > + struct kvm *kvm = opt->ptr;
> > +
> > + if ((strncmp(arg, "min", 3) && strncmp(arg, "max", 3)) || strlen(arg) != 3)
> > + die("Invalid CPU type %s\n", arg);
> > +
> > + if (!strncmp(arg, "max", 3))
> > + kvm->cfg.arch.cpu_type = "max";
> > +
> > + if (!strncmp(arg, "min", 3))
>
> We can use strcmp for these two checks since we already checked strlen
> above. We also already know it's either 'min' or 'max' so we can just
> check one and default to the other.
Okay, I will update.
>
> > + kvm->cfg.arch.cpu_type = "min";
> > +
> > + return 0;
> > +}
> > +
> > static void dump_fdt(const char *dtb_file, void *fdt)
> > {
> > int count, fd;
> > @@ -108,10 +160,8 @@ static void dump_fdt(const char *dtb_file, void *fdt)
> > #define CPU_NAME_MAX_LEN 15
> > static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
> > {
> > - int cpu, pos, i, index, valid_isa_len;
> > - const char *valid_isa_order = "IEMAFDQCLBJTPVNSUHKORWXYZG";
> > - int arr_sz = ARRAY_SIZE(isa_info_arr);
> > unsigned long cbom_blksz = 0, cboz_blksz = 0, satp_mode = 0;
> > + int i, cpu, pos, arr_sz = ARRAY_SIZE(isa_info_arr);
> >
> > _FDT(fdt_begin_node(fdt, "cpus"));
> > _FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
> > @@ -131,18 +181,8 @@ static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
> >
> > snprintf(cpu_isa, CPU_ISA_MAX_LEN, "rv%ld", vcpu->riscv_xlen);
> > pos = strlen(cpu_isa);
> > - valid_isa_len = strlen(valid_isa_order);
> > - for (i = 0; i < valid_isa_len; i++) {
> > - index = valid_isa_order[i] - 'A';
> > - if (vcpu->riscv_isa & (1 << (index)))
> > - cpu_isa[pos++] = 'a' + index;
> > - }
> >
> > for (i = 0; i < arr_sz; i++) {
> > - /* Skip single-letter extensions since these are taken care */
> > - if (!isa_info_arr[i].multi_letter)
> > - continue;
> > -
> > reg.id = RISCV_ISA_EXT_REG(isa_info_arr[i].ext_id);
> > reg.addr = (unsigned long)&isa_ext_out;
> > if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, ®) < 0)
> > @@ -151,9 +191,10 @@ static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
> > /* This extension is not available in hardware */
> > continue;
> >
> > - if (kvm->cfg.arch.ext_disabled[isa_info_arr[i].ext_id]) {
> > + if (__isa_ext_disabled(kvm, &isa_info_arr[i])) {
> > isa_ext_out = 0;
> > - if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0)
> > + if ((ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) &&
>
> Unnecessary () around the first expression.
Okay, I will update.
>
> > + __isa_ext_warn_disable_failure(kvm, &isa_info_arr[i]))
> > pr_warning("Failed to disable %s ISA exension\n",
> > isa_info_arr[i].name);
> > continue;
> > @@ -178,8 +219,13 @@ static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
> > isa_info_arr[i].name);
> > break;
> > }
> > - pos += snprintf(cpu_isa + pos, CPU_ISA_MAX_LEN - pos, "_%s",
> > - isa_info_arr[i].name);
> > +
> > + if (isa_info_arr[i].multi_letter)
> > + pos += snprintf(cpu_isa + pos, CPU_ISA_MAX_LEN - pos, "_%s",
> > + isa_info_arr[i].name);
> > + else
> > + pos += snprintf(cpu_isa + pos, CPU_ISA_MAX_LEN - pos, "%s",
> > + isa_info_arr[i].name);
>
> Can write this as
Okay, I will update.
>
> pos += snprintf(cpu_isa + pos, CPU_ISA_MAX_LEN - pos, "%s%s",
> isa_info_arr[i].misa ? "" : "_",
> isa_info_arr[i].name);
>
> > }
> > cpu_isa[pos] = '\0';
> >
> > diff --git a/riscv/include/kvm/kvm-arch.h b/riscv/include/kvm/kvm-arch.h
> > index f0f469f..1bb2d32 100644
> > --- a/riscv/include/kvm/kvm-arch.h
> > +++ b/riscv/include/kvm/kvm-arch.h
> > @@ -90,6 +90,8 @@ enum irqchip_type {
> > IRQCHIP_AIA
> > };
> >
> > +bool riscv__isa_extension_disabled(struct kvm *kvm, unsigned long ext_id);
> > +
> > extern enum irqchip_type riscv_irqchip;
> > extern bool riscv_irqchip_inkernel;
> > extern void (*riscv_irqchip_trigger)(struct kvm *kvm, int irq,
> > diff --git a/riscv/include/kvm/kvm-config-arch.h b/riscv/include/kvm/kvm-config-arch.h
> > index 7e54d8a..26b1b50 100644
> > --- a/riscv/include/kvm/kvm-config-arch.h
> > +++ b/riscv/include/kvm/kvm-config-arch.h
> > @@ -4,6 +4,7 @@
> > #include "kvm/parse-options.h"
> >
> > struct kvm_config_arch {
> > + const char *cpu_type;
> > const char *dump_dtb_filename;
> > u64 suspend_seconds;
> > u64 custom_mvendorid;
> > @@ -13,8 +14,12 @@ struct kvm_config_arch {
> > bool sbi_ext_disabled[KVM_RISCV_SBI_EXT_MAX];
> > };
> >
> > +int riscv__cpu_type_parser(const struct option *opt, const char *arg, int unset);
> > +
> > #define OPT_ARCH_RUN(pfx, cfg) \
> > pfx, \
> > + OPT_CALLBACK('\0', "cpu-type", kvm, "min or max", \
> > + "Choose the cpu type (default is max).", riscv__cpu_type_parser, kvm),\
>
> I had to look ahead at the next patch to understand why we're setting kvm
> as the opt pointer here. I think it should be added in the next patch
> where it's used. Also, we don't use opt->value so we cna set that to NULL.
We are indeed using opt->ptr in this patch so we should be passing
kvm as opt-ptr.
We certainly don't need opt->value so we can pass NULL for this.
>
> > OPT_STRING('\0', "dump-dtb", &(cfg)->dump_dtb_filename, \
> > ".dtb file", "Dump generated .dtb to specified file"),\
> > OPT_U64('\0', "suspend-seconds", \
> > diff --git a/riscv/kvm.c b/riscv/kvm.c
> > index 1d49479..6a1b154 100644
> > --- a/riscv/kvm.c
> > +++ b/riscv/kvm.c
> > @@ -20,6 +20,8 @@ u64 kvm__arch_default_ram_address(void)
> >
> > void kvm__arch_validate_cfg(struct kvm *kvm)
> > {
> > + if (!kvm->cfg.arch.cpu_type)
> > + kvm->cfg.arch.cpu_type = "max";
> > }
>
> Hmm, seems like we're missing the right place for this. A validate
> function shouln't be setting defaults. I think we should rename
> kvm__arch_default_ram_address() to
>
> void kvm__arch_set_defaults(struct kvm_config *cfg)
>
> and set cfg->ram_addr inside it. Then add the cpu_type default
> setting to riscv's impl.
Renaming kvm__arch_default_ram_address() is certainly not
the right way because it has to be done after parsing options
so that we set the default value of cpu_type only if it is not
set by command-line options. Due to this reason, the
kvm__arch_validate_cfg() is the best place to set default
value of cpu_type.
Although, I do agree that the function name of
kvm__arch_validate_cfg() is misleading.
Maybe kvm__arch_validate_and_init_cfg() is a better name ?
Regards,
Anup
More information about the kvm-riscv
mailing list