[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, &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, &reg) < 0)
> > +                             if ((ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &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