[PATCH v12 06/24] KVM: x86: Introduce KVM_{G,S}ET_ONE_REG uAPIs support

Chao Gao posted 24 patches 1 month, 3 weeks ago
There is a newer version of this series
[PATCH v12 06/24] KVM: x86: Introduce KVM_{G,S}ET_ONE_REG uAPIs support
Posted by Chao Gao 1 month, 3 weeks ago
From: Yang Weijiang <weijiang.yang@intel.com>

Enable KVM_{G,S}ET_ONE_REG uAPIs so that userspace can access HW MSR or
KVM synthetic MSR through it.

In CET KVM series [1], KVM "steals" an MSR from PV MSR space and access
it via KVM_{G,S}ET_MSRs uAPIs, but the approach pollutes PV MSR space
and hides the difference of synthetic MSRs and normal HW defined MSRs.

Now carve out a separate room in KVM-customized MSR address space for
synthetic MSRs. The synthetic MSRs are not exposed to userspace via
KVM_GET_MSR_INDEX_LIST, instead userspace complies with KVM's setup and
composes the uAPI params. KVM synthetic MSR indices start from 0 and
increase linearly. Userspace caller should tag MSR type correctly in
order to access intended HW or synthetic MSR.

Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Yang Weijiang <weijiang.yang@intel.com>
Link: https://lore.kernel.org/all/20240219074733.122080-18-weijiang.yang@intel.com/ [1]
Tested-by: Mathias Krause <minipli@grsecurity.net>
Tested-by: John Allen <john.allen@amd.com>
Signed-off-by: Chao Gao <chao.gao@intel.com>
---
 arch/x86/include/uapi/asm/kvm.h | 10 +++++
 arch/x86/kvm/x86.c              | 66 +++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 0f15d683817d..e72d9e6c1739 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -411,6 +411,16 @@ struct kvm_xcrs {
 	__u64 padding[16];
 };
 
+#define KVM_X86_REG_MSR			(1 << 2)
+#define KVM_X86_REG_SYNTHETIC		(1 << 3)
+
+struct kvm_x86_reg_id {
+	__u32 index;
+	__u8 type;
+	__u8 rsvd;
+	__u16 rsvd16;
+};
+
 #define KVM_SYNC_X86_REGS      (1UL << 0)
 #define KVM_SYNC_X86_SREGS     (1UL << 1)
 #define KVM_SYNC_X86_EVENTS    (1UL << 2)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0010aa45bf9f..f77949c3e9dd 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2241,6 +2241,31 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)
 	return kvm_set_msr_ignored_check(vcpu, index, *data, true);
 }
 
+static int kvm_get_one_msr(struct kvm_vcpu *vcpu, u32 msr, u64 __user *value)
+{
+	u64 val;
+	int r;
+
+	r = do_get_msr(vcpu, msr, &val);
+	if (r)
+		return r;
+
+	if (put_user(val, value))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int kvm_set_one_msr(struct kvm_vcpu *vcpu, u32 msr, u64 __user *value)
+{
+	u64 val;
+
+	if (get_user(val, value))
+		return -EFAULT;
+
+	return do_set_msr(vcpu, msr, &val);
+}
+
 #ifdef CONFIG_X86_64
 struct pvclock_clock {
 	int vclock_mode;
@@ -5902,6 +5927,11 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
 	}
 }
 
+static int kvm_translate_synthetic_msr(struct kvm_x86_reg_id *reg)
+{
+	return -EINVAL;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -6018,6 +6048,42 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 		srcu_read_unlock(&vcpu->kvm->srcu, idx);
 		break;
 	}
+	case KVM_GET_ONE_REG:
+	case KVM_SET_ONE_REG: {
+		struct kvm_x86_reg_id *id;
+		struct kvm_one_reg reg;
+		u64 __user *value;
+
+		r = -EFAULT;
+		if (copy_from_user(&reg, argp, sizeof(reg)))
+			break;
+
+		r = -EINVAL;
+		id = (struct kvm_x86_reg_id *)&reg.id;
+		if (id->rsvd || id->rsvd16)
+			break;
+
+		if (id->type != KVM_X86_REG_MSR &&
+		    id->type != KVM_X86_REG_SYNTHETIC)
+			break;
+
+		if (id->type == KVM_X86_REG_SYNTHETIC) {
+			r = kvm_translate_synthetic_msr(id);
+			if (r)
+				break;
+		}
+
+		r = -EINVAL;
+		if (id->type != KVM_X86_REG_MSR)
+			break;
+
+		value = u64_to_user_ptr(reg.addr);
+		if (ioctl == KVM_GET_ONE_REG)
+			r = kvm_get_one_msr(vcpu, id->index, value);
+		else
+			r = kvm_set_one_msr(vcpu, id->index, value);
+		break;
+	}
 	case KVM_TPR_ACCESS_REPORTING: {
 		struct kvm_tpr_access_ctl tac;
 
-- 
2.47.1
Re: [PATCH v12 06/24] KVM: x86: Introduce KVM_{G,S}ET_ONE_REG uAPIs support
Posted by Sean Christopherson 1 month, 2 weeks ago
On Mon, Aug 11, 2025, Chao Gao wrote:
> From: Yang Weijiang <weijiang.yang@intel.com>
> 
> Enable KVM_{G,S}ET_ONE_REG uAPIs so that userspace can access HW MSR or
> KVM synthetic MSR through it.
> 
> In CET KVM series [1], KVM "steals" an MSR from PV MSR space and access
> it via KVM_{G,S}ET_MSRs uAPIs, but the approach pollutes PV MSR space
> and hides the difference of synthetic MSRs and normal HW defined MSRs.
> 
> Now carve out a separate room in KVM-customized MSR address space for
> synthetic MSRs. The synthetic MSRs are not exposed to userspace via
> KVM_GET_MSR_INDEX_LIST, instead userspace complies with KVM's setup and
> composes the uAPI params. KVM synthetic MSR indices start from 0 and
> increase linearly. Userspace caller should tag MSR type correctly in
> order to access intended HW or synthetic MSR.
> 
> Suggested-by: Sean Christopherson <seanjc@google.com>
> Signed-off-by: Yang Weijiang <weijiang.yang@intel.com>
> Link: https://lore.kernel.org/all/20240219074733.122080-18-weijiang.yang@intel.com/ [1]
> Tested-by: Mathias Krause <minipli@grsecurity.net>
> Tested-by: John Allen <john.allen@amd.com>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> ---
>  arch/x86/include/uapi/asm/kvm.h | 10 +++++
>  arch/x86/kvm/x86.c              | 66 +++++++++++++++++++++++++++++++++
>  2 files changed, 76 insertions(+)
> 
> diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
> index 0f15d683817d..e72d9e6c1739 100644
> --- a/arch/x86/include/uapi/asm/kvm.h
> +++ b/arch/x86/include/uapi/asm/kvm.h
> @@ -411,6 +411,16 @@ struct kvm_xcrs {
>  	__u64 padding[16];
>  };
>  
> +#define KVM_X86_REG_MSR			(1 << 2)
> +#define KVM_X86_REG_SYNTHETIC		(1 << 3)
> +
> +struct kvm_x86_reg_id {
> +	__u32 index;
> +	__u8 type;
> +	__u8 rsvd;
> +	__u16 rsvd16;
> +};

Some feedback from a while back never got addressed[*].  That feedback still
looks sane/good, so this for the uAPI:

--
#define KVM_X86_REG_TYPE_MSR	2ull

#define KVM_x86_REG_TYPE_SIZE(type) 						\
{(										\
	__u64 type_size = type;							\
										\
	type_size |= type == KVM_X86_REG_TYPE_MSR ? KVM_REG_SIZE_U64 :		\
		     type == KVM_X86_REG_TYPE_SYNTHETIC_MSR ? KVM_REG_SIZE_U64 :\
		     0;								\
	type_size;								\
})

#define KVM_X86_REG_ENCODE(type, index)				\
	(KVM_REG_X86 | KVM_X86_REG_TYPE_SIZE(type) | index)

#define KVM_X86_REG_MSR(index) KVM_X86_REG_ENCODE(KVM_X86_REG_TYPE_MSR, index)
--

And then the kernel-only struct overlay becomes:

--
struct kvm_x86_reg_id {
	__u32 index;
	__u8  type;
	__u8  rsvd;
	__u8  rsvd4:4;
	__u8  size:4;
	__u8  x86;
}
--

[*] https://lore.kernel.org/all/ZuGpJtEPv1NtdYwM@google.com
Re: [PATCH v12 06/24] KVM: x86: Introduce KVM_{G,S}ET_ONE_REG uAPIs support
Posted by Chao Gao 1 month, 2 weeks ago
>> +#define KVM_X86_REG_MSR			(1 << 2)
>> +#define KVM_X86_REG_SYNTHETIC		(1 << 3)
>> +
>> +struct kvm_x86_reg_id {
>> +	__u32 index;
>> +	__u8 type;
>> +	__u8 rsvd;
>> +	__u16 rsvd16;
>> +};
>
>Some feedback from a while back never got addressed[*].  That feedback still
>looks sane/good, so this for the uAPI:

I missed that comment. Below is the diff I end up with. I moved struct
kvm_x86_reg_id to x86.c and added checks for ARCH (i.e., x86) and size.

diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index e72d9e6c1739..bb17b7a85159 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -411,15 +411,23 @@ struct kvm_xcrs {
	__u64 padding[16];
 };
 
-#define KVM_X86_REG_MSR			(1 << 2)
-#define KVM_X86_REG_SYNTHETIC		(1 << 3)
-
-struct kvm_x86_reg_id {
-	__u32 index;
-	__u8 type;
-	__u8 rsvd;
-	__u16 rsvd16;
-};
+#define KVM_X86_REG_TYPE_MSR		2
+#define KVM_X86_REG_TYPE_SYNTHETIC_MSR	3
+
+#define KVM_x86_REG_TYPE_SIZE(type)						\
+{(										\
+	__u64 type_size = type;							\
+										\
+	type_size |= type == KVM_X86_REG_TYPE_MSR ? KVM_REG_SIZE_U64 :		\
+		     type == KVM_X86_REG_TYPE_SYNTHETIC_MSR ? KVM_REG_SIZE_U64 :\
+		     0;								\
+	type_size;								\
+})
+
+#define KVM_X86_REG_ENCODE(type, index)				\
+	(KVM_REG_X86 | KVM_X86_REG_TYPE_SIZE(type) | index)
+
+#define KVM_X86_REG_MSR(index) KVM_X86_REG_ENCODE(KVM_X86_REG_TYPE_MSR, index)
 
 #define KVM_SYNC_X86_REGS      (1UL << 0)
 #define KVM_SYNC_X86_SREGS     (1UL << 1)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 3bf098a1183a..28e33269c1e9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5940,6 +5940,15 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
	}
 }
 
+struct kvm_x86_reg_id {
+	__u32 index;
+	__u8  type;
+	__u8  rsvd;
+	__u8  rsvd4:4;
+	__u8  size:4;
+	__u8  x86;
+};
+
 static int kvm_translate_synthetic_msr(struct kvm_x86_reg_id *reg)
 {
	return -EINVAL;
@@ -6072,22 +6081,28 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
			break;
 
		r = -EINVAL;
+		if ((reg.id & KVM_REG_ARCH_MASK) != KVM_REG_X86)
+			break;
+
		id = (struct kvm_x86_reg_id *)&reg.id;
-		if (id->rsvd || id->rsvd16)
+		if (id->rsvd || id->rsvd4)
+			break;
+
+		if (id->type != KVM_X86_REG_TYPE_MSR &&
+		    id->type != KVM_X86_REG_TYPE_SYNTHETIC_MSR)
			break;
 
-		if (id->type != KVM_X86_REG_MSR &&
-		    id->type != KVM_X86_REG_SYNTHETIC)
+		if ((reg.id & KVM_REG_SIZE_MASK) != KVM_REG_SIZE_U64)
			break;
 
-		if (id->type == KVM_X86_REG_SYNTHETIC) {
+		if (id->type == KVM_X86_REG_TYPE_SYNTHETIC_MSR) {
			r = kvm_translate_synthetic_msr(id);
			if (r)
				break;
		}
 
		r = -EINVAL;
-		if (id->type != KVM_X86_REG_MSR)
+		if (id->type != KVM_X86_REG_TYPE_MSR)
			break;
 
		value = u64_to_user_ptr(reg.addr);


>
>--
>#define KVM_X86_REG_TYPE_MSR	2ull
>
>#define KVM_x86_REG_TYPE_SIZE(type) 						\
>{(										\
>	__u64 type_size = type;							\
>										\
>	type_size |= type == KVM_X86_REG_TYPE_MSR ? KVM_REG_SIZE_U64 :		\
>		     type == KVM_X86_REG_TYPE_SYNTHETIC_MSR ? KVM_REG_SIZE_U64 :\
>		     0;								\
>	type_size;								\
>})
>
>#define KVM_X86_REG_ENCODE(type, index)				\
>	(KVM_REG_X86 | KVM_X86_REG_TYPE_SIZE(type) | index)
>
>#define KVM_X86_REG_MSR(index) KVM_X86_REG_ENCODE(KVM_X86_REG_TYPE_MSR, index)
>--
>
>And then the kernel-only struct overlay becomes:
>
>--
>struct kvm_x86_reg_id {
>	__u32 index;
>	__u8  type;
>	__u8  rsvd;
>	__u8  rsvd4:4;
>	__u8  size:4;
>	__u8  x86;
>}
>--
>
>[*] https://lore.kernel.org/all/ZuGpJtEPv1NtdYwM@google.com
Re: [PATCH v12 06/24] KVM: x86: Introduce KVM_{G,S}ET_ONE_REG uAPIs support
Posted by Chao Gao 1 month, 2 weeks ago
On Wed, Aug 20, 2025 at 04:29:04PM +0800, Chao Gao wrote:
>>> +#define KVM_X86_REG_MSR			(1 << 2)
>>> +#define KVM_X86_REG_SYNTHETIC		(1 << 3)
>>> +
>>> +struct kvm_x86_reg_id {
>>> +	__u32 index;
>>> +	__u8 type;
>>> +	__u8 rsvd;
>>> +	__u16 rsvd16;
>>> +};
>>
>>Some feedback from a while back never got addressed[*].  That feedback still
>>looks sane/good, so this for the uAPI:
>
>I missed that comment. Below is the diff I end up with. I moved struct
>kvm_x86_reg_id to x86.c and added checks for ARCH (i.e., x86) and size.
>
>diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
>index e72d9e6c1739..bb17b7a85159 100644
>--- a/arch/x86/include/uapi/asm/kvm.h
>+++ b/arch/x86/include/uapi/asm/kvm.h
>@@ -411,15 +411,23 @@ struct kvm_xcrs {
>	__u64 padding[16];
> };
> 
>-#define KVM_X86_REG_MSR			(1 << 2)
>-#define KVM_X86_REG_SYNTHETIC		(1 << 3)
>-
>-struct kvm_x86_reg_id {
>-	__u32 index;
>-	__u8 type;
>-	__u8 rsvd;
>-	__u16 rsvd16;
>-};
>+#define KVM_X86_REG_TYPE_MSR		2
>+#define KVM_X86_REG_TYPE_SYNTHETIC_MSR	3
>+
>+#define KVM_x86_REG_TYPE_SIZE(type)						\
>+{(										\

There are two typos here. s/x86/X86 and s/{(/({

>+	__u64 type_size = type;							\

and this should be __u64 type_size = (__u64)type << 32; 

When I tried to add a kselftest for the new ioctls, I found this patch didn't
advertise KVM_CAP_ONE_REG support for x86.