Documentation/virt/kvm/api.rst | 2 +- arch/loongarch/kvm/vcpu.c | 85 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-)
This ioctl can be used by the userspace applications to determine which
(special) registers are get/set-able in a meaningful way.
This can be very useful for cross-platform VMMs so that they do not have
to hardcode register indices for each supported architectures.
Signed-off-by: Zixing Liu <liushuyu@aosc.io>
---
Documentation/virt/kvm/api.rst | 2 +-
arch/loongarch/kvm/vcpu.c | 85 ++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 01a3abef8abb..f46dd8be282f 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -3603,7 +3603,7 @@ VCPU matching underlying host.
---------------------
:Capability: basic
-:Architectures: arm64, mips, riscv, x86 (if KVM_CAP_ONE_REG)
+:Architectures: arm64, loongarch, mips, riscv, x86 (if KVM_CAP_ONE_REG)
:Type: vcpu ioctl
:Parameters: struct kvm_reg_list (in/out)
:Returns: 0 on success; -1 on error
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 656b954c1134..ed11438f4544 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -1186,6 +1186,73 @@ static int kvm_loongarch_vcpu_set_attr(struct kvm_vcpu *vcpu,
return ret;
}
+static int kvm_loongarch_walk_csrs(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+ unsigned int i, count;
+
+ for (i = 0, count = 0; i < CSR_MAX_NUMS; i++) {
+ if (!(get_gcsr_flag(i) & (SW_GCSR | HW_GCSR)))
+ continue;
+ const u64 reg = KVM_IOC_CSRID(i);
+ if (uindices && put_user(reg, uindices++))
+ return -EFAULT;
+ count++;
+ }
+
+ return count;
+}
+
+static unsigned long kvm_loongarch_num_lbt_regs(void)
+{
+ /* +1 for the LBT_FTOP flag (inside arch.fpu) */
+ return sizeof(struct loongarch_lbt) / sizeof(unsigned long) + 1;
+}
+
+static unsigned long kvm_loongarch_num_regs(struct kvm_vcpu *vcpu)
+{
+ /* +1 for the KVM_REG_LOONGARCH_COUNTER register */
+ unsigned long res =
+ kvm_loongarch_walk_csrs(vcpu, NULL) + KVM_MAX_CPUCFG_REGS + 1;
+
+ if (kvm_guest_has_lbt(&vcpu->arch))
+ res += kvm_loongarch_num_lbt_regs();
+
+ return res;
+}
+
+static int kvm_loongarch_copy_reg_indices(struct kvm_vcpu *vcpu,
+ u64 __user *uindices)
+{
+ u64 reg;
+ unsigned int i;
+
+ i = kvm_loongarch_walk_csrs(vcpu, uindices);
+ if (i < 0)
+ return i;
+ uindices += i;
+
+ for (i = 0; i < KVM_MAX_CPUCFG_REGS; i++) {
+ reg = KVM_IOC_CPUCFG(i);
+ if (put_user(reg, uindices++))
+ return -EFAULT;
+ }
+
+ reg = KVM_REG_LOONGARCH_COUNTER;
+ if (put_user(reg, uindices++))
+ return -EFAULT;
+
+ if (!kvm_guest_has_lbt(&vcpu->arch))
+ return 0;
+
+ for (i = 1; i <= kvm_loongarch_num_lbt_regs(); i++) {
+ reg = (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | i);
+ if (put_user(reg, uindices++))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -1251,6 +1318,24 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
r = kvm_loongarch_vcpu_set_attr(vcpu, &attr);
break;
}
+ case KVM_GET_REG_LIST: {
+ struct kvm_reg_list __user *user_list = argp;
+ struct kvm_reg_list reg_list;
+ unsigned n;
+
+ r = -EFAULT;
+ if (copy_from_user(®_list, user_list, sizeof(reg_list)))
+ break;
+ n = reg_list.n;
+ reg_list.n = kvm_loongarch_num_regs(vcpu);
+ if (copy_to_user(user_list, ®_list, sizeof(reg_list)))
+ break;
+ r = -E2BIG;
+ if (n < reg_list.n)
+ break;
+ r = kvm_loongarch_copy_reg_indices(vcpu, user_list->reg);
+ break;
+ }
default:
r = -ENOIOCTLCMD;
break;
--
2.52.0
Hi, Zixing,
On Sat, Jan 31, 2026 at 2:07 PM Zixing Liu <liushuyu@aosc.io> wrote:
>
> This ioctl can be used by the userspace applications to determine which
> (special) registers are get/set-able in a meaningful way.
>
> This can be very useful for cross-platform VMMs so that they do not have
> to hardcode register indices for each supported architectures.
>
> Signed-off-by: Zixing Liu <liushuyu@aosc.io>
> ---
> Documentation/virt/kvm/api.rst | 2 +-
> arch/loongarch/kvm/vcpu.c | 85 ++++++++++++++++++++++++++++++++++
> 2 files changed, 86 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index 01a3abef8abb..f46dd8be282f 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -3603,7 +3603,7 @@ VCPU matching underlying host.
> ---------------------
>
> :Capability: basic
> -:Architectures: arm64, mips, riscv, x86 (if KVM_CAP_ONE_REG)
> +:Architectures: arm64, loongarch, mips, riscv, x86 (if KVM_CAP_ONE_REG)
> :Type: vcpu ioctl
> :Parameters: struct kvm_reg_list (in/out)
> :Returns: 0 on success; -1 on error
> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> index 656b954c1134..ed11438f4544 100644
> --- a/arch/loongarch/kvm/vcpu.c
> +++ b/arch/loongarch/kvm/vcpu.c
> @@ -1186,6 +1186,73 @@ static int kvm_loongarch_vcpu_set_attr(struct kvm_vcpu *vcpu,
> return ret;
> }
>
> +static int kvm_loongarch_walk_csrs(struct kvm_vcpu *vcpu, u64 __user *uindices)
> +{
> + unsigned int i, count;
> +
> + for (i = 0, count = 0; i < CSR_MAX_NUMS; i++) {
> + if (!(get_gcsr_flag(i) & (SW_GCSR | HW_GCSR)))
> + continue;
> + const u64 reg = KVM_IOC_CSRID(i);
> + if (uindices && put_user(reg, uindices++))
> + return -EFAULT;
> + count++;
> + }
> +
> + return count;
> +}
> +
> +static unsigned long kvm_loongarch_num_lbt_regs(void)
> +{
> + /* +1 for the LBT_FTOP flag (inside arch.fpu) */
> + return sizeof(struct loongarch_lbt) / sizeof(unsigned long) + 1;
> +}
This function has only one line, I think it is better to embed it into
the caller.
Huacai
> +
> +static unsigned long kvm_loongarch_num_regs(struct kvm_vcpu *vcpu)
> +{
> + /* +1 for the KVM_REG_LOONGARCH_COUNTER register */
> + unsigned long res =
> + kvm_loongarch_walk_csrs(vcpu, NULL) + KVM_MAX_CPUCFG_REGS + 1;
> +
> + if (kvm_guest_has_lbt(&vcpu->arch))
> + res += kvm_loongarch_num_lbt_regs();
> +
> + return res;
> +}
> +
> +static int kvm_loongarch_copy_reg_indices(struct kvm_vcpu *vcpu,
> + u64 __user *uindices)
> +{
> + u64 reg;
> + unsigned int i;
> +
> + i = kvm_loongarch_walk_csrs(vcpu, uindices);
> + if (i < 0)
> + return i;
> + uindices += i;
> +
> + for (i = 0; i < KVM_MAX_CPUCFG_REGS; i++) {
> + reg = KVM_IOC_CPUCFG(i);
> + if (put_user(reg, uindices++))
> + return -EFAULT;
> + }
> +
> + reg = KVM_REG_LOONGARCH_COUNTER;
> + if (put_user(reg, uindices++))
> + return -EFAULT;
> +
> + if (!kvm_guest_has_lbt(&vcpu->arch))
> + return 0;
> +
> + for (i = 1; i <= kvm_loongarch_num_lbt_regs(); i++) {
> + reg = (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | i);
> + if (put_user(reg, uindices++))
> + return -EFAULT;
> + }
> +
> + return 0;
> +}
> +
> long kvm_arch_vcpu_ioctl(struct file *filp,
> unsigned int ioctl, unsigned long arg)
> {
> @@ -1251,6 +1318,24 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
> r = kvm_loongarch_vcpu_set_attr(vcpu, &attr);
> break;
> }
> + case KVM_GET_REG_LIST: {
> + struct kvm_reg_list __user *user_list = argp;
> + struct kvm_reg_list reg_list;
> + unsigned n;
> +
> + r = -EFAULT;
> + if (copy_from_user(®_list, user_list, sizeof(reg_list)))
> + break;
> + n = reg_list.n;
> + reg_list.n = kvm_loongarch_num_regs(vcpu);
> + if (copy_to_user(user_list, ®_list, sizeof(reg_list)))
> + break;
> + r = -E2BIG;
> + if (n < reg_list.n)
> + break;
> + r = kvm_loongarch_copy_reg_indices(vcpu, user_list->reg);
> + break;
> + }
> default:
> r = -ENOIOCTLCMD;
> break;
> --
> 2.52.0
>
Hi Huacai,
> Hi, Zixing,
>
> On Sat, Jan 31, 2026 at 2:07 PM Zixing Liu <liushuyu@aosc.io> wrote:
>> This ioctl can be used by the userspace applications to determine which
>> (special) registers are get/set-able in a meaningful way.
>>
>> This can be very useful for cross-platform VMMs so that they do not have
>> to hardcode register indices for each supported architectures.
>>
>> Signed-off-by: Zixing Liu <liushuyu@aosc.io>
>> ---
>> Documentation/virt/kvm/api.rst | 2 +-
>> arch/loongarch/kvm/vcpu.c | 85 ++++++++++++++++++++++++++++++++++
>> 2 files changed, 86 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
>> index 01a3abef8abb..f46dd8be282f 100644
>> --- a/Documentation/virt/kvm/api.rst
>> +++ b/Documentation/virt/kvm/api.rst
>> @@ -3603,7 +3603,7 @@ VCPU matching underlying host.
>> ---------------------
>>
>> :Capability: basic
>> -:Architectures: arm64, mips, riscv, x86 (if KVM_CAP_ONE_REG)
>> +:Architectures: arm64, loongarch, mips, riscv, x86 (if KVM_CAP_ONE_REG)
>> :Type: vcpu ioctl
>> :Parameters: struct kvm_reg_list (in/out)
>> :Returns: 0 on success; -1 on error
>> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
>> index 656b954c1134..ed11438f4544 100644
>> --- a/arch/loongarch/kvm/vcpu.c
>> +++ b/arch/loongarch/kvm/vcpu.c
>> @@ -1186,6 +1186,73 @@ static int kvm_loongarch_vcpu_set_attr(struct kvm_vcpu *vcpu,
>> return ret;
>> }
>>
>> +static int kvm_loongarch_walk_csrs(struct kvm_vcpu *vcpu, u64 __user *uindices)
>> +{
>> + unsigned int i, count;
>> +
>> + for (i = 0, count = 0; i < CSR_MAX_NUMS; i++) {
>> + if (!(get_gcsr_flag(i) & (SW_GCSR | HW_GCSR)))
>> + continue;
>> + const u64 reg = KVM_IOC_CSRID(i);
>> + if (uindices && put_user(reg, uindices++))
>> + return -EFAULT;
>> + count++;
>> + }
>> +
>> + return count;
>> +}
>> +
>> +static unsigned long kvm_loongarch_num_lbt_regs(void)
>> +{
>> + /* +1 for the LBT_FTOP flag (inside arch.fpu) */
>> + return sizeof(struct loongarch_lbt) / sizeof(unsigned long) + 1;
>> +}
> This function has only one line, I think it is better to embed it into
> the caller.
This function was used twice in this patch. If the hardware gained more
LBT registers that are not recorded in `struct loongarch_lbt` in the
future, I am afraid changing manually inlined logic will be a bit
error-prone.
Maybe one compromise would be making this a `const` variable somewhere
in this file instead of a function? What do you think?
> Huacai
Thanks,
Zixing
>> +
>> +static unsigned long kvm_loongarch_num_regs(struct kvm_vcpu *vcpu)
>> +{
>> + /* +1 for the KVM_REG_LOONGARCH_COUNTER register */
>> + unsigned long res =
>> + kvm_loongarch_walk_csrs(vcpu, NULL) + KVM_MAX_CPUCFG_REGS + 1;
>> +
>> + if (kvm_guest_has_lbt(&vcpu->arch))
>> + res += kvm_loongarch_num_lbt_regs();
>> +
>> + return res;
>> +}
>> +
>> +static int kvm_loongarch_copy_reg_indices(struct kvm_vcpu *vcpu,
>> + u64 __user *uindices)
>> +{
>> + u64 reg;
>> + unsigned int i;
>> +
>> + i = kvm_loongarch_walk_csrs(vcpu, uindices);
>> + if (i < 0)
>> + return i;
>> + uindices += i;
>> +
>> + for (i = 0; i < KVM_MAX_CPUCFG_REGS; i++) {
>> + reg = KVM_IOC_CPUCFG(i);
>> + if (put_user(reg, uindices++))
>> + return -EFAULT;
>> + }
>> +
>> + reg = KVM_REG_LOONGARCH_COUNTER;
>> + if (put_user(reg, uindices++))
>> + return -EFAULT;
>> +
>> + if (!kvm_guest_has_lbt(&vcpu->arch))
>> + return 0;
>> +
>> + for (i = 1; i <= kvm_loongarch_num_lbt_regs(); i++) {
>> + reg = (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | i);
>> + if (put_user(reg, uindices++))
>> + return -EFAULT;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> long kvm_arch_vcpu_ioctl(struct file *filp,
>> unsigned int ioctl, unsigned long arg)
>> {
>> @@ -1251,6 +1318,24 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
>> r = kvm_loongarch_vcpu_set_attr(vcpu, &attr);
>> break;
>> }
>> + case KVM_GET_REG_LIST: {
>> + struct kvm_reg_list __user *user_list = argp;
>> + struct kvm_reg_list reg_list;
>> + unsigned n;
>> +
>> + r = -EFAULT;
>> + if (copy_from_user(®_list, user_list, sizeof(reg_list)))
>> + break;
>> + n = reg_list.n;
>> + reg_list.n = kvm_loongarch_num_regs(vcpu);
>> + if (copy_to_user(user_list, ®_list, sizeof(reg_list)))
>> + break;
>> + r = -E2BIG;
>> + if (n < reg_list.n)
>> + break;
>> + r = kvm_loongarch_copy_reg_indices(vcpu, user_list->reg);
>> + break;
>> + }
>> default:
>> r = -ENOIOCTLCMD;
>> break;
>> --
>> 2.52.0
>>
On Sat, Jan 31, 2026 at 10:09 PM liushuyu <liushuyu@aosc.io> wrote:
>
> Hi Huacai,
>
> > Hi, Zixing,
> >
> > On Sat, Jan 31, 2026 at 2:07 PM Zixing Liu <liushuyu@aosc.io> wrote:
> >> This ioctl can be used by the userspace applications to determine which
> >> (special) registers are get/set-able in a meaningful way.
> >>
> >> This can be very useful for cross-platform VMMs so that they do not have
> >> to hardcode register indices for each supported architectures.
> >>
> >> Signed-off-by: Zixing Liu <liushuyu@aosc.io>
> >> ---
> >> Documentation/virt/kvm/api.rst | 2 +-
> >> arch/loongarch/kvm/vcpu.c | 85 ++++++++++++++++++++++++++++++++++
> >> 2 files changed, 86 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> >> index 01a3abef8abb..f46dd8be282f 100644
> >> --- a/Documentation/virt/kvm/api.rst
> >> +++ b/Documentation/virt/kvm/api.rst
> >> @@ -3603,7 +3603,7 @@ VCPU matching underlying host.
> >> ---------------------
> >>
> >> :Capability: basic
> >> -:Architectures: arm64, mips, riscv, x86 (if KVM_CAP_ONE_REG)
> >> +:Architectures: arm64, loongarch, mips, riscv, x86 (if KVM_CAP_ONE_REG)
> >> :Type: vcpu ioctl
> >> :Parameters: struct kvm_reg_list (in/out)
> >> :Returns: 0 on success; -1 on error
> >> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
> >> index 656b954c1134..ed11438f4544 100644
> >> --- a/arch/loongarch/kvm/vcpu.c
> >> +++ b/arch/loongarch/kvm/vcpu.c
> >> @@ -1186,6 +1186,73 @@ static int kvm_loongarch_vcpu_set_attr(struct kvm_vcpu *vcpu,
> >> return ret;
> >> }
> >>
> >> +static int kvm_loongarch_walk_csrs(struct kvm_vcpu *vcpu, u64 __user *uindices)
> >> +{
> >> + unsigned int i, count;
> >> +
> >> + for (i = 0, count = 0; i < CSR_MAX_NUMS; i++) {
> >> + if (!(get_gcsr_flag(i) & (SW_GCSR | HW_GCSR)))
> >> + continue;
> >> + const u64 reg = KVM_IOC_CSRID(i);
> >> + if (uindices && put_user(reg, uindices++))
> >> + return -EFAULT;
> >> + count++;
> >> + }
> >> +
> >> + return count;
> >> +}
> >> +
> >> +static unsigned long kvm_loongarch_num_lbt_regs(void)
> >> +{
> >> + /* +1 for the LBT_FTOP flag (inside arch.fpu) */
> >> + return sizeof(struct loongarch_lbt) / sizeof(unsigned long) + 1;
> >> +}
> > This function has only one line, I think it is better to embed it into
> > the caller.
>
> This function was used twice in this patch. If the hardware gained more
> LBT registers that are not recorded in `struct loongarch_lbt` in the
> future, I am afraid changing manually inlined logic will be a bit
> error-prone.
If this really happens, how to keep compatibility?
>
> Maybe one compromise would be making this a `const` variable somewhere
> in this file instead of a function? What do you think?
I think the best way is
#define NUM_LBT_REGS 6
in this file.
Huacai
>
> > Huacai
>
> Thanks,
>
> Zixing
>
> >> +
> >> +static unsigned long kvm_loongarch_num_regs(struct kvm_vcpu *vcpu)
> >> +{
> >> + /* +1 for the KVM_REG_LOONGARCH_COUNTER register */
> >> + unsigned long res =
> >> + kvm_loongarch_walk_csrs(vcpu, NULL) + KVM_MAX_CPUCFG_REGS + 1;
> >> +
> >> + if (kvm_guest_has_lbt(&vcpu->arch))
> >> + res += kvm_loongarch_num_lbt_regs();
> >> +
> >> + return res;
> >> +}
> >> +
> >> +static int kvm_loongarch_copy_reg_indices(struct kvm_vcpu *vcpu,
> >> + u64 __user *uindices)
> >> +{
> >> + u64 reg;
> >> + unsigned int i;
> >> +
> >> + i = kvm_loongarch_walk_csrs(vcpu, uindices);
> >> + if (i < 0)
> >> + return i;
> >> + uindices += i;
> >> +
> >> + for (i = 0; i < KVM_MAX_CPUCFG_REGS; i++) {
> >> + reg = KVM_IOC_CPUCFG(i);
> >> + if (put_user(reg, uindices++))
> >> + return -EFAULT;
> >> + }
> >> +
> >> + reg = KVM_REG_LOONGARCH_COUNTER;
> >> + if (put_user(reg, uindices++))
> >> + return -EFAULT;
> >> +
> >> + if (!kvm_guest_has_lbt(&vcpu->arch))
> >> + return 0;
> >> +
> >> + for (i = 1; i <= kvm_loongarch_num_lbt_regs(); i++) {
> >> + reg = (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | i);
> >> + if (put_user(reg, uindices++))
> >> + return -EFAULT;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> long kvm_arch_vcpu_ioctl(struct file *filp,
> >> unsigned int ioctl, unsigned long arg)
> >> {
> >> @@ -1251,6 +1318,24 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
> >> r = kvm_loongarch_vcpu_set_attr(vcpu, &attr);
> >> break;
> >> }
> >> + case KVM_GET_REG_LIST: {
> >> + struct kvm_reg_list __user *user_list = argp;
> >> + struct kvm_reg_list reg_list;
> >> + unsigned n;
> >> +
> >> + r = -EFAULT;
> >> + if (copy_from_user(®_list, user_list, sizeof(reg_list)))
> >> + break;
> >> + n = reg_list.n;
> >> + reg_list.n = kvm_loongarch_num_regs(vcpu);
> >> + if (copy_to_user(user_list, ®_list, sizeof(reg_list)))
> >> + break;
> >> + r = -E2BIG;
> >> + if (n < reg_list.n)
> >> + break;
> >> + r = kvm_loongarch_copy_reg_indices(vcpu, user_list->reg);
> >> + break;
> >> + }
> >> default:
> >> r = -ENOIOCTLCMD;
> >> break;
> >> --
> >> 2.52.0
> >>
>
© 2016 - 2026 Red Hat, Inc.