Skip to content

Fix EINVAL from KVM_TDX_INIT_VCPU by not filtering vCPU CPUID - #17

Merged
guzongmin merged 1 commit into
mainfrom
zhoul1/dev/cpuid_1
Sep 4, 2026
Merged

Fix EINVAL from KVM_TDX_INIT_VCPU by not filtering vCPU CPUID#17
guzongmin merged 1 commit into
mainfrom
zhoul1/dev/cpuid_1

Conversation

@liangzhou121

Copy link
Copy Markdown
Contributor

KVM_TDX_CAPABILITIES only reports the CPUID bits the TDX module considers "configurable". Leaves such as 0x8000_0008 (MAXPHYADDR) are guest-visible but not part of that configurable set, so running the shared guest CPUID through tdx_filter_cpuid() before handing it to the per-vCPU KVM_SET_CPUID2 ioctl silently dropped them.

Without 0x8000_0008 in KVM's local CPUID cache, vcpu->arch.maxphyaddr stayed 0, which made the kernel's reserved-GPA-bits check reject the x2APIC MSR value tdx_vcpu_init() forces on the vCPU, so KVM_TDX_INIT_VCPU failed with EINVAL before the TDX module SEAMCALL was ever attempted.

Stop filtering the CPUID used for KVM_SET_CPUID2 and instead filter only a copy of it when building the KVM_TDX_INIT_VM request.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

It introduces/retains a Rust FFI unsoundness around using *const for an ioctl-filled buffer (tdx_command/Capabilities path), which should be corrected before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR fixes a TDX vCPU initialization failure (KVM_TDX_INIT_VCPU returning EINVAL) by ensuring guest-visible CPUID leaves that are not “configurable” (per KVM_TDX_CAPABILITIES) are no longer dropped from the CPUID programmed via per-vCPU KVM_SET_CPUID2, while still filtering CPUID appropriately for KVM_TDX_INIT_VM.

Changes:

  • Filter only a copy of CPUID for KVM_TDX_INIT_VM, keeping the shared/per-vCPU CPUID unfiltered for guest-visible leaves.
  • Improve TDX command ioctl handling by passing a mutable command struct and logging the returned hw_error on failure.
  • Adjust KVM feature masking for TDX builds by clearing additional feature bits.
File summaries
File Description
hypervisor/src/kvm/mod.rs Stops filtering the shared CPUID used for per-vCPU programming; filters only the KVM_TDX_INIT_VM CPUID copy and improves TDX ioctl error reporting.
arch/src/x86_64/mod.rs Removes TDX CPUID filtering during common CPUID generation and updates the set of KVM feature bits cleared for TDX.
Review details

Suppressed comments (1)

hypervisor/src/kvm/mod.rs:1107

  • tdx_command() takes data: *const c_void and stores it in cmd.data, but at least TdxCommand::Capabilities expects the kernel to write results into the pointed-to buffer (see tdx_capabilities() returning the populated data). Passing a shared reference/*const to memory that is mutated by the kernel is unsound in Rust and can lead to undefined behavior; the API should take a mutable pointer for any command that may write into data.
fn tdx_command(
    fd: RawFd,
    command: TdxCommand,
    flags: u32,
    data: *const libc::c_void,
) -> std::result::Result<(), std::io::Error> {
    const TDX_COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
    let mut cmd = KvmTdxCmd {
        id: command,
        flags,
        data: data as _,
        hw_error: 0,
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread arch/src/x86_64/mod.rs
Comment thread hypervisor/src/kvm/mod.rs

@guzongmin guzongmin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread arch/src/x86_64/mod.rs
@guzongmin
guzongmin merged commit 4f51863 into main Sep 4, 2026
16 of 41 checks passed
KVM_TDX_CAPABILITIES only reports the CPUID bits the TDX module
considers "configurable". Leaves such as 0x8000_0008 (MAXPHYADDR)
are guest-visible but not part of that configurable set, so running
the shared guest CPUID through tdx_filter_cpuid() before handing it
to the per-vCPU KVM_SET_CPUID2 ioctl silently dropped them.

Without 0x8000_0008 in KVM's local CPUID cache, vcpu->arch.maxphyaddr
stayed 0, which made the kernel's reserved-GPA-bits check reject the
x2APIC MSR value tdx_vcpu_init() forces on the vCPU, so
KVM_TDX_INIT_VCPU failed with EINVAL before the TDX module SEAMCALL
was ever attempted.

Stop filtering the CPUID used for KVM_SET_CPUID2 and instead filter
only a copy of it when building the KVM_TDX_INIT_VM request.

Signed-off-by: liangzhou121 <zhouliang121@126.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants