From: Paolo Bonzini <pbonzini@redhat.com>
To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org
Cc: seanjc@google.com, Yan Zhao <yan.y.zhao@intel.com>,
Rick Edgecombe <rick.p.edgecombe@intel.com>,
Xiaoyao Li <xiaoyao.li@intel.com>,
Tony Lindgren <tony.lindgren@linux.intel.com>
Subject: [PATCH 28/30] KVM: x86: Introduce KVM_TDX_GET_CPUID
Date: Thu, 20 Feb 2025 12:06:02 -0500 [thread overview]
Message-ID: <20250220170604.2279312-29-pbonzini@redhat.com> (raw)
In-Reply-To: <20250220170604.2279312-1-pbonzini@redhat.com>
From: Xiaoyao Li <xiaoyao.li@intel.com>
Implement an IOCTL to allow userspace to read the CPUID bit values for a
configured TD.
The TDX module doesn't provide the ability to set all CPUID bits. Instead
some are configured indirectly, or have fixed values. But it does allow
for the final resulting CPUID bits to be read. This information will be
useful for userspace to understand the configuration of the TD, and set
KVM's copy via KVM_SET_CPUID2.
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>
---
- Fix subleaf mask check (Binbin)
- Search all possible sub-leafs (Francesco Lavra)
- Reduce off-by-one error sensitve code (Francesco, Xiaoyao)
- Handle buffers too small from userspace (Xiaoyao)
- Read max CPUID from TD instead of using fixed values (Xiaoyao)
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
arch/x86/include/uapi/asm/kvm.h | 1 +
arch/x86/kvm/vmx/tdx.c | 191 ++++++++++++++++++++++++++++++++
arch/x86/kvm/vmx/tdx_arch.h | 5 +
arch/x86/kvm/vmx/tdx_errno.h | 1 +
4 files changed, 198 insertions(+)
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 9316afbd4a88..cd55484e3f0c 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -932,6 +932,7 @@ enum kvm_tdx_cmd_id {
KVM_TDX_CAPABILITIES = 0,
KVM_TDX_INIT_VM,
KVM_TDX_INIT_VCPU,
+ KVM_TDX_GET_CPUID,
KVM_TDX_CMD_NR_MAX,
};
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 7dfb0b486dd9..1c88f577ec2b 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -3,6 +3,7 @@
#include <asm/cpufeature.h>
#include <asm/tdx.h>
#include "capabilities.h"
+#include "mmu.h"
#include "x86_ops.h"
#include "lapic.h"
#include "tdx.h"
@@ -858,6 +859,103 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params,
return ret;
}
+static u64 tdx_td_metadata_field_read(struct kvm_tdx *tdx, u64 field_id,
+ u64 *data)
+{
+ u64 err;
+
+ err = tdh_mng_rd(&tdx->td, field_id, data);
+
+ return err;
+}
+
+#define TDX_MD_UNREADABLE_LEAF_MASK GENMASK(30, 7)
+#define TDX_MD_UNREADABLE_SUBLEAF_MASK GENMASK(31, 7)
+
+static int tdx_read_cpuid(struct kvm_vcpu *vcpu, u32 leaf, u32 sub_leaf,
+ bool sub_leaf_set, int *entry_index,
+ struct kvm_cpuid_entry2 *out)
+{
+ struct kvm_tdx *kvm_tdx = to_kvm_tdx(vcpu->kvm);
+ u64 field_id = TD_MD_FIELD_ID_CPUID_VALUES;
+ u64 ebx_eax, edx_ecx;
+ u64 err = 0;
+
+ if (sub_leaf > 0b1111111)
+ return -EINVAL;
+
+ if (*entry_index >= KVM_MAX_CPUID_ENTRIES)
+ return -EINVAL;
+
+ if (leaf & TDX_MD_UNREADABLE_LEAF_MASK ||
+ sub_leaf & TDX_MD_UNREADABLE_SUBLEAF_MASK)
+ return -EINVAL;
+
+ /*
+ * bit 23:17, REVSERVED: reserved, must be 0;
+ * bit 16, LEAF_31: leaf number bit 31;
+ * bit 15:9, LEAF_6_0: leaf number bits 6:0, leaf bits 30:7 are
+ * implicitly 0;
+ * bit 8, SUBLEAF_NA: sub-leaf not applicable flag;
+ * bit 7:1, SUBLEAF_6_0: sub-leaf number bits 6:0. If SUBLEAF_NA is 1,
+ * the SUBLEAF_6_0 is all-1.
+ * sub-leaf bits 31:7 are implicitly 0;
+ * bit 0, ELEMENT_I: Element index within field;
+ */
+ field_id |= ((leaf & 0x80000000) ? 1 : 0) << 16;
+ field_id |= (leaf & 0x7f) << 9;
+ if (sub_leaf_set)
+ field_id |= (sub_leaf & 0x7f) << 1;
+ else
+ field_id |= 0x1fe;
+
+ err = tdx_td_metadata_field_read(kvm_tdx, field_id, &ebx_eax);
+ if (err) //TODO check for specific errors
+ goto err_out;
+
+ out->eax = (u32) ebx_eax;
+ out->ebx = (u32) (ebx_eax >> 32);
+
+ field_id++;
+ err = tdx_td_metadata_field_read(kvm_tdx, field_id, &edx_ecx);
+ /*
+ * It's weird that reading edx_ecx fails while reading ebx_eax
+ * succeeded.
+ */
+ if (WARN_ON_ONCE(err))
+ goto err_out;
+
+ out->ecx = (u32) edx_ecx;
+ out->edx = (u32) (edx_ecx >> 32);
+
+ out->function = leaf;
+ out->index = sub_leaf;
+ out->flags |= sub_leaf_set ? KVM_CPUID_FLAG_SIGNIFCANT_INDEX : 0;
+
+ /*
+ * Work around missing support on old TDX modules, fetch
+ * guest maxpa from gfn_direct_bits.
+ */
+ if (leaf == 0x80000008) {
+ gpa_t gpa_bits = gfn_to_gpa(kvm_gfn_direct_bits(vcpu->kvm));
+ unsigned int g_maxpa = __ffs(gpa_bits) + 1;
+
+ out->eax = tdx_set_guest_phys_addr_bits(out->eax, g_maxpa);
+ }
+
+ (*entry_index)++;
+
+ return 0;
+
+err_out:
+ out->eax = 0;
+ out->ebx = 0;
+ out->ecx = 0;
+ out->edx = 0;
+
+ return -EIO;
+}
+
static int tdx_td_init(struct kvm *kvm, struct kvm_tdx_cmd *cmd)
{
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
@@ -1051,6 +1149,96 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx)
return ret;
}
+/* Sometimes reads multipple subleafs. Return how many enties were written. */
+static int tdx_vcpu_get_cpuid_leaf(struct kvm_vcpu *vcpu, u32 leaf, int *entry_index,
+ struct kvm_cpuid_entry2 *output_e)
+{
+ int sub_leaf = 0;
+ int ret;
+
+ /* First try without a subleaf */
+ ret = tdx_read_cpuid(vcpu, leaf, 0, false, entry_index, output_e);
+
+ /* If success, or invalid leaf, just give up */
+ if (ret != -EIO)
+ return ret;
+
+ /*
+ * If the try without a subleaf failed, try reading subleafs until
+ * failure. The TDX module only supports 6 bits of subleaf index.
+ */
+ while (1) {
+ /* Keep reading subleafs until there is a failure. */
+ if (tdx_read_cpuid(vcpu, leaf, sub_leaf, true, entry_index, output_e))
+ return !sub_leaf;
+
+ sub_leaf++;
+ output_e++;
+ }
+
+ return 0;
+}
+
+static int tdx_vcpu_get_cpuid(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd)
+{
+ struct kvm_cpuid2 __user *output, *td_cpuid;
+ int r = 0, i = 0, leaf;
+ u32 level;
+
+ output = u64_to_user_ptr(cmd->data);
+ td_cpuid = kzalloc(sizeof(*td_cpuid) +
+ sizeof(output->entries[0]) * KVM_MAX_CPUID_ENTRIES,
+ GFP_KERNEL);
+ if (!td_cpuid)
+ return -ENOMEM;
+
+ if (copy_from_user(td_cpuid, output, sizeof(*output))) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ /* Read max CPUID for normal range */
+ if (tdx_vcpu_get_cpuid_leaf(vcpu, 0, &i, &td_cpuid->entries[i])) {
+ r = -EIO;
+ goto out;
+ }
+ level = td_cpuid->entries[0].eax;
+
+ for (leaf = 1; leaf <= level; leaf++)
+ tdx_vcpu_get_cpuid_leaf(vcpu, leaf, &i, &td_cpuid->entries[i]);
+
+ /* Read max CPUID for extended range */
+ if (tdx_vcpu_get_cpuid_leaf(vcpu, 0x80000000, &i, &td_cpuid->entries[i])) {
+ r = -EIO;
+ goto out;
+ }
+ level = td_cpuid->entries[i - 1].eax;
+
+ for (leaf = 0x80000001; leaf <= level; leaf++)
+ tdx_vcpu_get_cpuid_leaf(vcpu, leaf, &i, &td_cpuid->entries[i]);
+
+ if (td_cpuid->nent < i)
+ r = -E2BIG;
+ td_cpuid->nent = i;
+
+ if (copy_to_user(output, td_cpuid, sizeof(*output))) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ if (r == -E2BIG)
+ goto out;
+
+ if (copy_to_user(output->entries, td_cpuid->entries,
+ td_cpuid->nent * sizeof(struct kvm_cpuid_entry2)))
+ r = -EFAULT;
+
+out:
+ kfree(td_cpuid);
+
+ return r;
+}
+
static int tdx_vcpu_init(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd)
{
u64 apic_base;
@@ -1100,6 +1288,9 @@ int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp)
case KVM_TDX_INIT_VCPU:
ret = tdx_vcpu_init(vcpu, &cmd);
break;
+ case KVM_TDX_GET_CPUID:
+ ret = tdx_vcpu_get_cpuid(vcpu, &cmd);
+ break;
default:
ret = -EINVAL;
break;
diff --git a/arch/x86/kvm/vmx/tdx_arch.h b/arch/x86/kvm/vmx/tdx_arch.h
index cb9a638fa398..b6fe458a4224 100644
--- a/arch/x86/kvm/vmx/tdx_arch.h
+++ b/arch/x86/kvm/vmx/tdx_arch.h
@@ -122,4 +122,9 @@ struct td_params {
#define MD_FIELD_ID_FEATURES0_TOPOLOGY_ENUM BIT_ULL(20)
+/*
+ * TD scope metadata field ID.
+ */
+#define TD_MD_FIELD_ID_CPUID_VALUES 0x9410000300000000ULL
+
#endif /* __KVM_X86_TDX_ARCH_H */
diff --git a/arch/x86/kvm/vmx/tdx_errno.h b/arch/x86/kvm/vmx/tdx_errno.h
index dc3fa2a58c2c..f9dbb3a065cc 100644
--- a/arch/x86/kvm/vmx/tdx_errno.h
+++ b/arch/x86/kvm/vmx/tdx_errno.h
@@ -23,6 +23,7 @@
#define TDX_FLUSHVP_NOT_DONE 0x8000082400000000ULL
#define TDX_EPT_WALK_FAILED 0xC0000B0000000000ULL
#define TDX_EPT_ENTRY_STATE_INCORRECT 0xC0000B0D00000000ULL
+#define TDX_METADATA_FIELD_NOT_READABLE 0xC0000C0200000000ULL
/*
* TDX module operand ID, appears in 31:0 part of error code as
--
2.43.5
next prev parent reply other threads:[~2025-02-20 17:06 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-20 17:05 [PATCH v3 00/30] TDX initialization + vCPU/VM creation Paolo Bonzini
2025-02-20 17:05 ` [PATCH 01/30] x86/virt/tdx: Add SEAMCALL wrappers for TDX KeyID management Paolo Bonzini
2025-02-20 17:05 ` [PATCH 02/30] x86/virt/tdx: Add SEAMCALL wrappers for TDX TD creation Paolo Bonzini
2025-02-20 17:05 ` [PATCH 03/30] x86/virt/tdx: Add SEAMCALL wrappers for TDX vCPU creation Paolo Bonzini
2025-02-20 17:05 ` [PATCH 04/30] x86/virt/tdx: Add SEAMCALL wrappers for TDX page cache management Paolo Bonzini
2025-02-20 17:05 ` [PATCH 05/30] x86/virt/tdx: Add SEAMCALL wrappers for TDX VM/vCPU field access Paolo Bonzini
2025-02-20 17:05 ` [PATCH 06/30] x86/virt/tdx: Add SEAMCALL wrappers for TDX flush operations Paolo Bonzini
2025-02-20 17:05 ` [PATCH 07/30] x86/virt/tdx: allocate tdx_sys_info in static memory Paolo Bonzini
2025-02-20 21:59 ` Huang, Kai
2025-02-20 23:37 ` Edgecombe, Rick P
2025-02-20 17:05 ` [PATCH 08/30] x86/virt/tdx: Read essential global metadata for KVM Paolo Bonzini
2025-02-20 17:05 ` [PATCH 09/30] x86/virt/tdx: Add tdx_guest_keyid_alloc/free() to alloc and free TDX guest KeyID Paolo Bonzini
2025-02-20 17:05 ` [PATCH 10/30] KVM: Export hardware virtualization enabling/disabling functions Paolo Bonzini
2025-02-20 17:05 ` [PATCH 11/30] KVM: VMX: Refactor VMX module init/exit functions Paolo Bonzini
2025-02-20 21:55 ` Huang, Kai
2025-02-20 17:05 ` [PATCH 12/30] KVM: VMX: Initialize TDX during KVM module load Paolo Bonzini
2025-02-20 23:27 ` Huang, Kai
2025-02-24 18:57 ` Paolo Bonzini
2025-02-24 21:31 ` Huang, Kai
2025-02-20 17:05 ` [PATCH 13/30] KVM: TDX: Get TDX global information Paolo Bonzini
2025-02-21 0:12 ` Huang, Kai
2025-02-20 17:05 ` [PATCH 14/30] KVM: TDX: Add placeholders for TDX VM/vCPU structures Paolo Bonzini
2025-02-20 17:05 ` [PATCH 15/30] KVM: TDX: Define TDX architectural definitions Paolo Bonzini
2025-02-20 17:05 ` [PATCH 16/30] KVM: TDX: Add TDX "architectural" error codes Paolo Bonzini
2025-02-20 17:05 ` [PATCH 17/30] KVM: TDX: Add helper functions to print TDX SEAMCALL error Paolo Bonzini
2025-02-20 17:05 ` [PATCH 18/30] KVM: TDX: Add place holder for TDX VM specific mem_enc_op ioctl Paolo Bonzini
2025-02-25 10:50 ` Huang, Kai
2025-02-20 17:05 ` [PATCH 19/30] KVM: TDX: Get system-wide info about TDX module on initialization Paolo Bonzini
2025-02-20 17:05 ` [PATCH 20/30] KVM: TDX: create/destroy VM structure Paolo Bonzini
2025-02-21 0:55 ` Sean Christopherson
2025-02-21 1:08 ` Sean Christopherson
2025-02-22 0:30 ` Edgecombe, Rick P
2025-02-22 1:38 ` Sean Christopherson
2025-02-24 8:32 ` Yan Zhao
2025-02-21 11:04 ` Yan Zhao
2025-02-21 19:43 ` Sean Christopherson
2025-02-21 12:25 ` Yan Zhao
2025-02-25 16:24 ` Xiaoyao Li
2025-02-20 17:05 ` [PATCH 21/30] KVM: TDX: Support per-VM KVM_CAP_MAX_VCPUS extension check Paolo Bonzini
2025-02-20 17:05 ` [PATCH 22/30] KVM: x86: expose cpuid_entry2_find for TDX Paolo Bonzini
2025-02-20 17:05 ` [PATCH 23/30] KVM: TDX: initialize VM with TDX specific parameters Paolo Bonzini
2025-02-21 2:31 ` Xiaoyao Li
2025-02-25 17:28 ` Paolo Bonzini
2025-02-20 17:05 ` [PATCH 24/30] KVM: TDX: Make pmu_intel.c ignore guest TD case Paolo Bonzini
2025-02-20 17:05 ` [PATCH 25/30] KVM: TDX: Don't offline the last cpu of one package when there's TDX guest Paolo Bonzini
2025-02-20 17:06 ` [PATCH 26/30] KVM: TDX: create/free TDX vcpu structure Paolo Bonzini
2025-02-20 17:06 ` [PATCH 27/30] KVM: TDX: Do TDX specific vcpu initialization Paolo Bonzini
2025-02-20 17:06 ` Paolo Bonzini [this message]
2025-02-20 17:06 ` [PATCH 29/30] KVM: x86/mmu: Taking guest pa into consideration when calculate tdp level Paolo Bonzini
2025-02-20 17:06 ` [PATCH 30/30] KVM: TDX: Register TDX host key IDs to cgroup misc controller Paolo Bonzini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250220170604.2279312-29-pbonzini@redhat.com \
--to=pbonzini@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rick.p.edgecombe@intel.com \
--cc=seanjc@google.com \
--cc=tony.lindgren@linux.intel.com \
--cc=xiaoyao.li@intel.com \
--cc=yan.y.zhao@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®