Skip to content

Commit 3f9cd0c

Browse files
jingzhangosoupton
authored andcommitted
KVM: arm64: Allow userspace to get the writable masks for feature ID registers
While the Feature ID range is well defined and pretty large, it isn't inconceivable that the architecture will eventually grow some other ranges that will need to similarly be described to userspace. Add a VM ioctl to allow userspace to get writable masks for feature ID registers in below system register space: op0 = 3, op1 = {0, 1, 3}, CRn = 0, CRm = {0 - 7}, op2 = {0 - 7} This is used to support mix-and-match userspace and kernels for writable ID registers, where userspace may want to know upfront whether it can actually tweak the contents of an idreg or not. Add a new capability (KVM_CAP_ARM_SUPPORTED_FEATURE_ID_RANGES) that returns a bitmap of the valid ranges, which can subsequently be retrieved, one at a time by setting the index of the set bit as the range identifier. Suggested-by: Marc Zyngier <[email protected]> Suggested-by: Cornelia Huck <[email protected]> Signed-off-by: Jing Zhang <[email protected]> Reviewed-by: Cornelia Huck <[email protected]> Reviewed-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Oliver Upton <[email protected]>
1 parent 6465e26 commit 3f9cd0c

File tree

5 files changed

+112
-0
lines changed

5 files changed

+112
-0
lines changed

arch/arm64/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,8 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
10781078
struct kvm_arm_copy_mte_tags *copy_tags);
10791079
int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
10801080
struct kvm_arm_counter_offset *offset);
1081+
int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm,
1082+
struct reg_mask_range *range);
10811083

10821084
/* Guest/host FPSIMD coordination helpers */
10831085
int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);

arch/arm64/include/uapi/asm/kvm.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,38 @@ struct kvm_smccc_filter {
505505
#define KVM_HYPERCALL_EXIT_SMC (1U << 0)
506506
#define KVM_HYPERCALL_EXIT_16BIT (1U << 1)
507507

508+
/*
509+
* Get feature ID registers userspace writable mask.
510+
*
511+
* From DDI0487J.a, D19.2.66 ("ID_AA64MMFR2_EL1, AArch64 Memory Model
512+
* Feature Register 2"):
513+
*
514+
* "The Feature ID space is defined as the System register space in
515+
* AArch64 with op0==3, op1=={0, 1, 3}, CRn==0, CRm=={0-7},
516+
* op2=={0-7}."
517+
*
518+
* This covers all currently known R/O registers that indicate
519+
* anything useful feature wise, including the ID registers.
520+
*
521+
* If we ever need to introduce a new range, it will be described as
522+
* such in the range field.
523+
*/
524+
#define KVM_ARM_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm, op2) \
525+
({ \
526+
__u64 __op1 = (op1) & 3; \
527+
__op1 -= (__op1 == 3); \
528+
(__op1 << 6 | ((crm) & 7) << 3 | (op2)); \
529+
})
530+
531+
#define KVM_ARM_FEATURE_ID_RANGE 0
532+
#define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8)
533+
534+
struct reg_mask_range {
535+
__u64 addr; /* Pointer to mask array */
536+
__u32 range; /* Requested range */
537+
__u32 reserved[13];
538+
};
539+
508540
#endif
509541

510542
#endif /* __ARM_KVM_H__ */

arch/arm64/kvm/arm.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
317317
case KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:
318318
r = kvm_supported_block_sizes();
319319
break;
320+
case KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES:
321+
r = BIT(0);
322+
break;
320323
default:
321324
r = 0;
322325
}
@@ -1629,6 +1632,13 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
16291632

16301633
return kvm_vm_set_attr(kvm, &attr);
16311634
}
1635+
case KVM_ARM_GET_REG_WRITABLE_MASKS: {
1636+
struct reg_mask_range range;
1637+
1638+
if (copy_from_user(&range, argp, sizeof(range)))
1639+
return -EFAULT;
1640+
return kvm_vm_ioctl_get_reg_writable_masks(kvm, &range);
1641+
}
16321642
default:
16331643
return -EINVAL;
16341644
}

arch/arm64/kvm/sys_regs.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,13 @@ static inline bool is_id_reg(u32 id)
13731373
sys_reg_CRm(id) < 8);
13741374
}
13751375

1376+
static inline bool is_aa32_id_reg(u32 id)
1377+
{
1378+
return (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&
1379+
sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 1 &&
1380+
sys_reg_CRm(id) <= 3);
1381+
}
1382+
13761383
static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
13771384
const struct sys_reg_desc *r)
13781385
{
@@ -3572,6 +3579,65 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
35723579
return write_demux_regids(uindices);
35733580
}
35743581

3582+
#define KVM_ARM_FEATURE_ID_RANGE_INDEX(r) \
3583+
KVM_ARM_FEATURE_ID_RANGE_IDX(sys_reg_Op0(r), \
3584+
sys_reg_Op1(r), \
3585+
sys_reg_CRn(r), \
3586+
sys_reg_CRm(r), \
3587+
sys_reg_Op2(r))
3588+
3589+
static bool is_feature_id_reg(u32 encoding)
3590+
{
3591+
return (sys_reg_Op0(encoding) == 3 &&
3592+
(sys_reg_Op1(encoding) < 2 || sys_reg_Op1(encoding) == 3) &&
3593+
sys_reg_CRn(encoding) == 0 &&
3594+
sys_reg_CRm(encoding) <= 7);
3595+
}
3596+
3597+
int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm, struct reg_mask_range *range)
3598+
{
3599+
const void *zero_page = page_to_virt(ZERO_PAGE(0));
3600+
u64 __user *masks = (u64 __user *)range->addr;
3601+
3602+
/* Only feature id range is supported, reserved[13] must be zero. */
3603+
if (range->range ||
3604+
memcmp(range->reserved, zero_page, sizeof(range->reserved)))
3605+
return -EINVAL;
3606+
3607+
/* Wipe the whole thing first */
3608+
if (clear_user(masks, KVM_ARM_FEATURE_ID_RANGE_SIZE * sizeof(__u64)))
3609+
return -EFAULT;
3610+
3611+
for (int i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
3612+
const struct sys_reg_desc *reg = &sys_reg_descs[i];
3613+
u32 encoding = reg_to_encoding(reg);
3614+
u64 val;
3615+
3616+
if (!is_feature_id_reg(encoding) || !reg->set_user)
3617+
continue;
3618+
3619+
/*
3620+
* For ID registers, we return the writable mask. Other feature
3621+
* registers return a full 64bit mask. That's not necessary
3622+
* compliant with a given revision of the architecture, but the
3623+
* RES0/RES1 definitions allow us to do that.
3624+
*/
3625+
if (is_id_reg(encoding)) {
3626+
if (!reg->val ||
3627+
(is_aa32_id_reg(encoding) && !kvm_supports_32bit_el0()))
3628+
continue;
3629+
val = reg->val;
3630+
} else {
3631+
val = ~0UL;
3632+
}
3633+
3634+
if (put_user(val, (masks + KVM_ARM_FEATURE_ID_RANGE_INDEX(encoding))))
3635+
return -EFAULT;
3636+
}
3637+
3638+
return 0;
3639+
}
3640+
35753641
int __init kvm_sys_reg_table_init(void)
35763642
{
35773643
struct sys_reg_params params;

include/uapi/linux/kvm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,7 @@ struct kvm_ppc_resize_hpt {
11921192
#define KVM_CAP_COUNTER_OFFSET 227
11931193
#define KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE 228
11941194
#define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229
1195+
#define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230
11951196

11961197
#ifdef KVM_CAP_IRQ_ROUTING
11971198

@@ -1562,6 +1563,7 @@ struct kvm_s390_ucas_mapping {
15621563
#define KVM_ARM_MTE_COPY_TAGS _IOR(KVMIO, 0xb4, struct kvm_arm_copy_mte_tags)
15631564
/* Available with KVM_CAP_COUNTER_OFFSET */
15641565
#define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset)
1566+
#define KVM_ARM_GET_REG_WRITABLE_MASKS _IOR(KVMIO, 0xb6, struct reg_mask_range)
15651567

15661568
/* ioctl for vm fd */
15671569
#define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)

0 commit comments

Comments
 (0)