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
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. > +} > + > [...]
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. */ >> +} >> + >> > [...]
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
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
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
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
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
© 2016 - 2024 Red Hat, Inc.