[PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization

Rick Edgecombe posted 25 patches 3 weeks, 4 days ago
[PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Rick Edgecombe 3 weeks, 4 days ago
From: Isaku Yamahata <isaku.yamahata@intel.com>

TDX KVM needs system-wide information about the TDX module. Generate the
data based on tdx_sysinfo td_conf CPUID data.

Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
Co-developed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Co-developed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
Signed-off-by: Tony Lindgren <tony.lindgren@linux.intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
---
uAPI breakout v2:
 - Update stale patch description (Binbin)
 - Add KVM_TDX_CAPABILITIES where it's first used (Binbin)
 - Drop Drop unused KVM_TDX_CPUID_NO_SUBLEAF (Chao)
 - Drop mmu.h, it's only needed in later patches (Binbin)
 - Fold in Xiaoyao's capabilities changes (Tony)
 - Generate data without struct kvm_tdx_caps (Tony)
 - Use struct kvm_cpuid_entry2 as suggested (Binbin)
 - Use helpers for phys_addr_bits (Paolo)
 - Check TDX and KVM capabilities on _tdx_bringup() (Xiaoyao)
 - Change code around cpuid_config_value since
   struct tdx_cpuid_config_value {} is removed (Kai)

uAPI breakout v1:
 - Mention about hardware_unsetup(). (Binbin)
 - Added Reviewed-by. (Binbin)
 - Eliminated tdx_md_read(). (Kai)
 - Include "x86_ops.h" to tdx.c as the patch to initialize TDX module
   doesn't include it anymore.
 - Introduce tdx_vm_ioctl() as the first tdx func in x86_ops.h

v19:
 - Added features0
 - Use tdx_sys_metadata_read()
 - Fix error recovery path by Yuan

Change v18:
 - Newly Added
---
 arch/x86/include/uapi/asm/kvm.h |   9 +++
 arch/x86/kvm/vmx/tdx.c          | 137 ++++++++++++++++++++++++++++++++
 2 files changed, 146 insertions(+)

diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index b6cb87f2b477..0630530af334 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -928,6 +928,8 @@ struct kvm_hyperv_eventfd {
 
 /* Trust Domain eXtension sub-ioctl() commands. */
 enum kvm_tdx_cmd_id {
+	KVM_TDX_CAPABILITIES = 0,
+
 	KVM_TDX_CMD_NR_MAX,
 };
 
@@ -950,4 +952,11 @@ struct kvm_tdx_cmd {
 	__u64 hw_error;
 };
 
+struct kvm_tdx_capabilities {
+	__u64 supported_attrs;
+	__u64 supported_xfam;
+	__u64 reserved[254];
+	struct kvm_cpuid2 cpuid;
+};
+
 #endif /* _ASM_X86_KVM_H */
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 76655d82f749..253debbe685f 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -30,6 +30,134 @@ static enum cpuhp_state tdx_cpuhp_state;
 
 static const struct tdx_sys_info *tdx_sysinfo;
 
+#define KVM_SUPPORTED_TD_ATTRS (TDX_TD_ATTR_SEPT_VE_DISABLE)
+
+static u64 tdx_get_supported_attrs(const struct tdx_sys_info_td_conf *td_conf)
+{
+	u64 val = KVM_SUPPORTED_TD_ATTRS;
+
+	if ((val & td_conf->attributes_fixed1) != td_conf->attributes_fixed1)
+		return 0;
+
+	val &= td_conf->attributes_fixed0;
+
+	return val;
+}
+
+static u64 tdx_get_supported_xfam(const struct tdx_sys_info_td_conf *td_conf)
+{
+	u64 val = kvm_caps.supported_xcr0 | kvm_caps.supported_xss;
+
+	/*
+	 * PT and CET can be exposed to TD guest regardless of KVM's XSS, PT
+	 * and, CET support.
+	 */
+	val |= XFEATURE_MASK_PT | XFEATURE_MASK_CET_USER |
+	       XFEATURE_MASK_CET_KERNEL;
+
+	if ((val & td_conf->xfam_fixed1) != td_conf->xfam_fixed1)
+		return 0;
+
+	val &= td_conf->xfam_fixed0;
+
+	return val;
+}
+
+static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
+{
+	return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
+}
+
+#define KVM_TDX_CPUID_NO_SUBLEAF	((__u32)-1)
+
+static void td_init_cpuid_entry2(struct kvm_cpuid_entry2 *entry, unsigned char idx)
+{
+	const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
+
+	entry->function = (u32)td_conf->cpuid_config_leaves[idx];
+	entry->index = td_conf->cpuid_config_leaves[idx] >> 32;
+	entry->eax = (u32)td_conf->cpuid_config_values[idx][0];
+	entry->ebx = td_conf->cpuid_config_values[idx][0] >> 32;
+	entry->ecx = (u32)td_conf->cpuid_config_values[idx][1];
+	entry->edx = td_conf->cpuid_config_values[idx][1] >> 32;
+
+	if (entry->index == KVM_TDX_CPUID_NO_SUBLEAF)
+		entry->index = 0;
+
+	/* Work around missing support on old TDX modules */
+	if (entry->function == 0x80000008)
+		entry->eax = tdx_set_guest_phys_addr_bits(entry->eax, 0xff);
+}
+
+static int init_kvm_tdx_caps(const struct tdx_sys_info_td_conf *td_conf,
+			     struct kvm_tdx_capabilities *caps)
+{
+	int i;
+
+	caps->supported_attrs = tdx_get_supported_attrs(td_conf);
+	if (!caps->supported_attrs)
+		return -EIO;
+
+	caps->supported_xfam = tdx_get_supported_xfam(td_conf);
+	if (!caps->supported_xfam)
+		return -EIO;
+
+	caps->cpuid.nent = td_conf->num_cpuid_config;
+
+	for (i = 0; i < td_conf->num_cpuid_config; i++)
+		td_init_cpuid_entry2(&caps->cpuid.entries[i], i);
+
+	return 0;
+}
+
+static int tdx_get_capabilities(struct kvm_tdx_cmd *cmd)
+{
+	const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
+	struct kvm_tdx_capabilities __user *user_caps;
+	struct kvm_tdx_capabilities *caps = NULL;
+	int ret = 0;
+
+	/* flags is reserved for future use */
+	if (cmd->flags)
+		return -EINVAL;
+
+	caps = kmalloc(sizeof(*caps) +
+		       sizeof(struct kvm_cpuid_entry2) * td_conf->num_cpuid_config,
+		       GFP_KERNEL);
+	if (!caps)
+		return -ENOMEM;
+
+	user_caps = u64_to_user_ptr(cmd->data);
+	if (copy_from_user(caps, user_caps, sizeof(*caps))) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	if (caps->cpuid.nent < td_conf->num_cpuid_config) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	ret = init_kvm_tdx_caps(td_conf, caps);
+	if (ret)
+		goto out;
+
+	if (copy_to_user(user_caps, caps, sizeof(*caps))) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	if (copy_to_user(user_caps->cpuid.entries, caps->cpuid.entries,
+			 caps->cpuid.nent *
+			 sizeof(caps->cpuid.entries[0])))
+		ret = -EFAULT;
+
+out:
+	/* kfree() accepts NULL. */
+	kfree(caps);
+	return ret;
+}
+
 int tdx_vm_ioctl(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_tdx_cmd tdx_cmd;
@@ -48,6 +176,9 @@ int tdx_vm_ioctl(struct kvm *kvm, void __user *argp)
 	mutex_lock(&kvm->lock);
 
 	switch (tdx_cmd.id) {
+	case KVM_TDX_CAPABILITIES:
+		r = tdx_get_capabilities(&tdx_cmd);
+		break;
 	default:
 		r = -EINVAL;
 		goto out;
@@ -147,11 +278,17 @@ static int __init __tdx_bringup(void)
 		goto get_sysinfo_err;
 	}
 
+	/* Check TDX module and KVM capabilities */
+	if (!tdx_get_supported_attrs(&tdx_sysinfo->td_conf) ||
+	    !tdx_get_supported_xfam(&tdx_sysinfo->td_conf))
+		goto get_sysinfo_err;
+
 	/*
 	 * Leave hardware virtualization enabled after TDX is enabled
 	 * successfully.  TDX CPU hotplug depends on this.
 	 */
 	return 0;
+
 get_sysinfo_err:
 	__do_tdx_cleanup();
 tdx_bringup_err:
-- 
2.47.0
Re: [PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Binbin Wu 3 weeks, 3 days ago


On 10/31/2024 3:00 AM, Rick Edgecombe wrote:
[...]
> +static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
> +{
> +	return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
> +}
> +
> +#define KVM_TDX_CPUID_NO_SUBLEAF	((__u32)-1)
> +
> +static void td_init_cpuid_entry2(struct kvm_cpuid_entry2 *entry, unsigned char idx)
> +{
> +	const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
> +
> +	entry->function = (u32)td_conf->cpuid_config_leaves[idx];
> +	entry->index = td_conf->cpuid_config_leaves[idx] >> 32;
> +	entry->eax = (u32)td_conf->cpuid_config_values[idx][0];
> +	entry->ebx = td_conf->cpuid_config_values[idx][0] >> 32;
> +	entry->ecx = (u32)td_conf->cpuid_config_values[idx][1];
> +	entry->edx = td_conf->cpuid_config_values[idx][1] >> 32;
> +
> +	if (entry->index == KVM_TDX_CPUID_NO_SUBLEAF)
> +		entry->index = 0;
> +
> +	/* Work around missing support on old TDX modules */
> +	if (entry->function == 0x80000008)
> +		entry->eax = tdx_set_guest_phys_addr_bits(entry->eax, 0xff);
Is it necessary to set bit 16~23 to 0xff?
It seems that when userspace wants to retrieve the value, the GPAW will
be set in tdx_read_cpuid() anyway.

> +}
> +
>
[...]
Re: [PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Xiaoyao Li 3 weeks, 3 days ago
On 10/31/2024 5:09 PM, Binbin Wu wrote:
> 
> 
> 
> On 10/31/2024 3:00 AM, Rick Edgecombe wrote:
> [...]
>> +static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
>> +{
>> +    return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
>> +}
>> +
>> +#define KVM_TDX_CPUID_NO_SUBLEAF    ((__u32)-1)
>> +
>> +static void td_init_cpuid_entry2(struct kvm_cpuid_entry2 *entry, 
>> unsigned char idx)
>> +{
>> +    const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
>> +
>> +    entry->function = (u32)td_conf->cpuid_config_leaves[idx];
>> +    entry->index = td_conf->cpuid_config_leaves[idx] >> 32;
>> +    entry->eax = (u32)td_conf->cpuid_config_values[idx][0];
>> +    entry->ebx = td_conf->cpuid_config_values[idx][0] >> 32;
>> +    entry->ecx = (u32)td_conf->cpuid_config_values[idx][1];
>> +    entry->edx = td_conf->cpuid_config_values[idx][1] >> 32;
>> +
>> +    if (entry->index == KVM_TDX_CPUID_NO_SUBLEAF)
>> +        entry->index = 0;
>> +
>> +    /* Work around missing support on old TDX modules */
>> +    if (entry->function == 0x80000008)
>> +        entry->eax = tdx_set_guest_phys_addr_bits(entry->eax, 0xff);
> Is it necessary to set bit 16~23 to 0xff?
> It seems that when userspace wants to retrieve the value, the GPAW will
> be set in tdx_read_cpuid() anyway.

here it is to initialize the configurable CPUID bits that get reported 
to userspace. Though TDX module doesn't allow them to be set in TD_PARAM 
for KVM_TDX_INIT_VM, they get set to 0xff because KVM reuse these bits 
EBX[23:16] as the interface for userspace to configure GPAW of TD guest 
(implemented in setup_tdparams_eptp_controls() in patch 19). That's why 
they need to be set as all-1 to allow userspace to configure.

And the comment above it is wrong and vague. we need to change it to 
something like

	/*
          * Though TDX module doesn't allow the configuration of guest
          * phys addr bits (EBX[23:16]), KVM uses it as the interface for
          * userspace to configure the GPAW. So need to report these bits
          * as configurable to userspace.
          */
>> +}
>> +
>>
> [...]

Re: [PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Tony Lindgren 3 weeks, 3 days ago
On Thu, Oct 31, 2024 at 05:23:57PM +0800, Xiaoyao Li wrote:
> here it is to initialize the configurable CPUID bits that get reported to
> userspace. Though TDX module doesn't allow them to be set in TD_PARAM for
> KVM_TDX_INIT_VM, they get set to 0xff because KVM reuse these bits
> EBX[23:16] as the interface for userspace to configure GPAW of TD guest
> (implemented in setup_tdparams_eptp_controls() in patch 19). That's why they
> need to be set as all-1 to allow userspace to configure.
> 
> And the comment above it is wrong and vague. we need to change it to
> something like
> 
> 	/*
>          * Though TDX module doesn't allow the configuration of guest
>          * phys addr bits (EBX[23:16]), KVM uses it as the interface for
>          * userspace to configure the GPAW. So need to report these bits
>          * as configurable to userspace.
>          */

That sounds good to me.

Hmm so care to check if we can also just leave out another "old module"
comment in tdx_read_cpuid()?

Regards,

Tony
Re: [PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Xiaoyao Li 3 weeks, 3 days ago
On 10/31/2024 5:37 PM, Tony Lindgren wrote:
> On Thu, Oct 31, 2024 at 05:23:57PM +0800, Xiaoyao Li wrote:
>> here it is to initialize the configurable CPUID bits that get reported to
>> userspace. Though TDX module doesn't allow them to be set in TD_PARAM for
>> KVM_TDX_INIT_VM, they get set to 0xff because KVM reuse these bits
>> EBX[23:16] as the interface for userspace to configure GPAW of TD guest
>> (implemented in setup_tdparams_eptp_controls() in patch 19). That's why they
>> need to be set as all-1 to allow userspace to configure.
>>
>> And the comment above it is wrong and vague. we need to change it to
>> something like
>>
>> 	/*
>>           * Though TDX module doesn't allow the configuration of guest
>>           * phys addr bits (EBX[23:16]), KVM uses it as the interface for
>>           * userspace to configure the GPAW. So need to report these bits
>>           * as configurable to userspace.
>>           */
> 
> That sounds good to me.
> 
> Hmm so care to check if we can also just leave out another "old module"
> comment in tdx_read_cpuid()?

That one did relate to old module, the module that without 
TDX_CONFIG_FLAGS_MAXGPA_VIRT reported in tdx_feature0.

I will sent an follow up patch to complement the handling if TDX module 
supports TDX_CONFIG_FLAGS_MAXGPA_VIRT.

> Regards,
> 
> Tony
Re: [PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Tony Lindgren 3 weeks, 2 days ago
On Thu, Oct 31, 2024 at 10:27:11PM +0800, Xiaoyao Li wrote:
> On 10/31/2024 5:37 PM, Tony Lindgren wrote:
> > On Thu, Oct 31, 2024 at 05:23:57PM +0800, Xiaoyao Li wrote:
> > > here it is to initialize the configurable CPUID bits that get reported to
> > > userspace. Though TDX module doesn't allow them to be set in TD_PARAM for
> > > KVM_TDX_INIT_VM, they get set to 0xff because KVM reuse these bits
> > > EBX[23:16] as the interface for userspace to configure GPAW of TD guest
> > > (implemented in setup_tdparams_eptp_controls() in patch 19). That's why they
> > > need to be set as all-1 to allow userspace to configure.
> > > 
> > > And the comment above it is wrong and vague. we need to change it to
> > > something like
> > > 
> > > 	/*
> > >           * Though TDX module doesn't allow the configuration of guest
> > >           * phys addr bits (EBX[23:16]), KVM uses it as the interface for
> > >           * userspace to configure the GPAW. So need to report these bits
> > >           * as configurable to userspace.
> > >           */
> > 
> > That sounds good to me.
> > 
> > Hmm so care to check if we can also just leave out another "old module"
> > comment in tdx_read_cpuid()?
> 
> That one did relate to old module, the module that without
> TDX_CONFIG_FLAGS_MAXGPA_VIRT reported in tdx_feature0.

OK thanks for checking.

> I will sent an follow up patch to complement the handling if TDX module
> supports TDX_CONFIG_FLAGS_MAXGPA_VIRT.

OK

Regards,

Tony
Re: [PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Tony Lindgren 3 weeks, 3 days ago
On Thu, Oct 31, 2024 at 05:09:17PM +0800, Binbin Wu wrote:
> 
> 
> 
> On 10/31/2024 3:00 AM, Rick Edgecombe wrote:
> [...]
> > +static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
> > +{
> > +	return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
> > +}
> > +
> > +#define KVM_TDX_CPUID_NO_SUBLEAF	((__u32)-1)
> > +
> > +static void td_init_cpuid_entry2(struct kvm_cpuid_entry2 *entry, unsigned char idx)
> > +{
> > +	const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
> > +
> > +	entry->function = (u32)td_conf->cpuid_config_leaves[idx];
> > +	entry->index = td_conf->cpuid_config_leaves[idx] >> 32;
> > +	entry->eax = (u32)td_conf->cpuid_config_values[idx][0];
> > +	entry->ebx = td_conf->cpuid_config_values[idx][0] >> 32;
> > +	entry->ecx = (u32)td_conf->cpuid_config_values[idx][1];
> > +	entry->edx = td_conf->cpuid_config_values[idx][1] >> 32;
> > +
> > +	if (entry->index == KVM_TDX_CPUID_NO_SUBLEAF)
> > +		entry->index = 0;
> > +
> > +	/* Work around missing support on old TDX modules */
> > +	if (entry->function == 0x80000008)
> > +		entry->eax = tdx_set_guest_phys_addr_bits(entry->eax, 0xff);
> Is it necessary to set bit 16~23 to 0xff?
> It seems that when userspace wants to retrieve the value, the GPAW will
> be set in tdx_read_cpuid() anyway.

Leaving it out currently produces:

qemu-system-x86_64: KVM_TDX_INIT_VM failed: Invalid argument

Regards,

Tony
Re: [PATCH v2 16/25] KVM: TDX: Get system-wide info about TDX module on initialization
Posted by Binbin Wu 3 weeks, 3 days ago


On 10/31/2024 5:18 PM, Tony Lindgren wrote:
> On Thu, Oct 31, 2024 at 05:09:17PM +0800, Binbin Wu wrote:
>>
>>
>> On 10/31/2024 3:00 AM, Rick Edgecombe wrote:
>> [...]
>>> +static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
>>> +{
>>> +	return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
>>> +}
>>> +
>>> +#define KVM_TDX_CPUID_NO_SUBLEAF	((__u32)-1)
>>> +
>>> +static void td_init_cpuid_entry2(struct kvm_cpuid_entry2 *entry, unsigned char idx)
>>> +{
>>> +	const struct tdx_sys_info_td_conf *td_conf = &tdx_sysinfo->td_conf;
>>> +
>>> +	entry->function = (u32)td_conf->cpuid_config_leaves[idx];
>>> +	entry->index = td_conf->cpuid_config_leaves[idx] >> 32;
>>> +	entry->eax = (u32)td_conf->cpuid_config_values[idx][0];
>>> +	entry->ebx = td_conf->cpuid_config_values[idx][0] >> 32;
>>> +	entry->ecx = (u32)td_conf->cpuid_config_values[idx][1];
>>> +	entry->edx = td_conf->cpuid_config_values[idx][1] >> 32;
>>> +
>>> +	if (entry->index == KVM_TDX_CPUID_NO_SUBLEAF)
>>> +		entry->index = 0;
>>> +
>>> +	/* Work around missing support on old TDX modules */
>>> +	if (entry->function == 0x80000008)
>>> +		entry->eax = tdx_set_guest_phys_addr_bits(entry->eax, 0xff);
>> Is it necessary to set bit 16~23 to 0xff?
>> It seems that when userspace wants to retrieve the value, the GPAW will
>> be set in tdx_read_cpuid() anyway.
> Leaving it out currently produces:
>
> qemu-system-x86_64: KVM_TDX_INIT_VM failed: Invalid argument
Yes, I forgot that userspace would use the value as the mask to filter cpuid.


>
> Regards,
>
> Tony