Skip to content

Commit ca9a64b

Browse files
committed
feat(tdx): initialize TDX VMs with supported CPUID features
Signed-off-by: Changyuan Lyu <changyuanl@google.com>
1 parent c523bca commit ca9a64b

File tree

8 files changed

+197
-26
lines changed

8 files changed

+197
-26
lines changed

alioth/src/board/board_x86_64/board_x86_64.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
mod sev;
16+
mod tdx;
1617

1718
use std::arch::x86_64::{__cpuid, CpuidResult};
1819
use std::collections::HashMap;
@@ -321,7 +322,7 @@ where
321322
match coco {
322323
Coco::AmdSev { policy } => self.sev_init(*policy, memory)?,
323324
Coco::AmdSnp { policy } => self.snp_init(*policy, memory)?,
324-
Coco::IntelTdx { attr } => todo!("Intel TDX {attr:?}"),
325+
Coco::IntelTdx { attr } => self.tdx_init(*attr, memory)?,
325326
}
326327
Ok(())
327328
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use std::sync::Arc;
16+
17+
use crate::arch::tdx::TdAttr;
18+
use crate::board::{Board, Result};
19+
use crate::hv::{Vm, VmMemory};
20+
use crate::mem::MarkPrivateMemory;
21+
22+
impl<V> Board<V>
23+
where
24+
V: Vm,
25+
{
26+
pub(crate) fn tdx_init(&self, attr: TdAttr, memory: Arc<dyn VmMemory>) -> Result<()> {
27+
self.vm.tdx_init_vm(attr, &self.arch.cpuids)?;
28+
let mark_private_memory = Box::new(MarkPrivateMemory { memory });
29+
self.memory.register_change_callback(mark_private_memory)?;
30+
Ok(())
31+
}
32+
}

alioth/src/hv/hv.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,9 @@ pub trait Vm {
390390
#[cfg(target_arch = "x86_64")]
391391
fn snp_launch_finish(&self) -> Result<()>;
392392

393+
#[cfg(target_arch = "x86_64")]
394+
fn tdx_init_vm(&self, attr: TdAttr, cpuids: &HashMap<CpuidIn, CpuidResult>) -> Result<()>;
395+
393396
#[cfg(target_arch = "aarch64")]
394397
type GicV2: GicV2;
395398
#[cfg(target_arch = "aarch64")]

alioth/src/hv/kvm/kvm_x86_64.rs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@ use crate::sys::kvm::{
2424
KvmCpuidFeature, KvmX2apicApiFlag, kvm_get_supported_cpuid,
2525
};
2626

27+
impl From<KvmCpuidEntry2> for (CpuidIn, CpuidResult) {
28+
fn from(value: KvmCpuidEntry2) -> Self {
29+
let in_ = CpuidIn {
30+
func: value.function,
31+
index: if value.flags.contains(KvmCpuid2Flag::SIGNIFCANT_INDEX) || value.index > 0 {
32+
Some(value.index)
33+
} else {
34+
None
35+
},
36+
};
37+
let result = CpuidResult {
38+
eax: value.eax,
39+
ebx: value.ebx,
40+
ecx: value.ecx,
41+
edx: value.edx,
42+
};
43+
(in_, result)
44+
}
45+
}
46+
2747
impl Kvm {
2848
pub fn get_supported_cpuids(&self) -> Result<HashMap<CpuidIn, CpuidResult>> {
2949
let mut kvm_cpuid2 = KvmCpuid2 {
@@ -32,29 +52,12 @@ impl Kvm {
3252
entries: [KvmCpuidEntry2::default(); KVM_MAX_CPUID_ENTRIES],
3353
};
3454
unsafe { kvm_get_supported_cpuid(&self.fd, &mut kvm_cpuid2) }.context(error::GuestCpuid)?;
35-
let map_f = |e: &KvmCpuidEntry2| {
36-
let in_ = CpuidIn {
37-
func: e.function,
38-
index: if e.flags.contains(KvmCpuid2Flag::SIGNIFCANT_INDEX) {
39-
Some(e.index)
40-
} else {
41-
None
42-
},
43-
};
44-
let out = CpuidResult {
45-
eax: e.eax,
46-
ebx: e.ebx,
47-
ecx: e.ecx,
48-
edx: e.edx,
49-
};
50-
(in_, out)
51-
};
5255
let mut cpuids: HashMap<_, _> = kvm_cpuid2
5356
.entries
54-
.iter()
57+
.into_iter()
5558
.filter(|e| e.eax != 0 || e.ebx != 0 || e.ecx != 0 || e.edx != 0)
5659
.take(kvm_cpuid2.nent as usize)
57-
.map(map_f)
60+
.map(From::from)
5861
.collect();
5962

6063
let leaf_features = CpuidIn {

alioth/src/hv/kvm/kvm_x86_64_test.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::arch::x86_64::CpuidResult;
16+
17+
use rstest::rstest;
18+
19+
use crate::arch::cpuid::CpuidIn;
1520
use crate::hv::kvm::{Kvm, KvmConfig};
16-
use crate::sys::kvm::KVM_CPUID_SIGNATURE;
21+
use crate::sys::kvm::{KVM_CPUID_SIGNATURE, KvmCpuid2Flag, KvmCpuidEntry2};
1722

1823
#[test]
1924
#[cfg_attr(not(feature = "test-hv"), ignore)]
@@ -32,3 +37,60 @@ fn test_get_supported_cpuid() {
3237
}
3338
assert!(kvm_cpuid_exist);
3439
}
40+
41+
#[rstest]
42+
#[case(
43+
KvmCpuidEntry2 {
44+
function: 0,
45+
index: 0,
46+
flags: KvmCpuid2Flag::empty(),
47+
eax: 0x10,
48+
ebx: u32::from_le_bytes(*b"Auth"),
49+
ecx: u32::from_le_bytes(*b"cAMD"),
50+
edx: u32::from_le_bytes(*b"enti"),
51+
padding: [0; 3],
52+
},
53+
(
54+
CpuidIn {
55+
func: 0,
56+
index: None,
57+
},
58+
CpuidResult {
59+
eax: 0x10,
60+
ebx: u32::from_le_bytes(*b"Auth"),
61+
ecx: u32::from_le_bytes(*b"cAMD"),
62+
edx: u32::from_le_bytes(*b"enti"),
63+
}
64+
)
65+
)]
66+
#[case(
67+
KvmCpuidEntry2 {
68+
function: 0xb,
69+
index: 0,
70+
flags: KvmCpuid2Flag::SIGNIFCANT_INDEX,
71+
eax: 0x0,
72+
ebx: 0x0,
73+
ecx: 0x0,
74+
edx: 0x6d,
75+
padding: [0; 3],
76+
},
77+
(
78+
CpuidIn {
79+
func: 0xb,
80+
index: Some(0),
81+
},
82+
CpuidResult {
83+
eax: 0x0,
84+
ebx: 0x0,
85+
ecx: 0x0,
86+
edx: 0x6d,
87+
}
88+
)
89+
)]
90+
fn test_convert_cpuid_entry(
91+
#[case] value: KvmCpuidEntry2,
92+
#[case] expected: (CpuidIn, CpuidResult),
93+
) {
94+
let got: (CpuidIn, CpuidResult) = value.into();
95+
assert_eq!(got, expected)
96+
}

alioth/src/hv/kvm/vm/vm.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ mod aarch64;
1919
#[path = "vm_x86_64/vm_x86_64.rs"]
2020
mod x86_64;
2121

22+
#[cfg(target_arch = "x86_64")]
23+
use std::arch::x86_64::CpuidResult;
2224
use std::collections::HashMap;
2325
use std::fmt::{self, Display, Formatter};
2426
use std::io::ErrorKind;
@@ -35,8 +37,12 @@ use libc::{EFD_CLOEXEC, EFD_NONBLOCK, SIGRTMIN, eventfd};
3537
use parking_lot::{Mutex, RwLock};
3638
use snafu::ResultExt;
3739

40+
#[cfg(target_arch = "x86_64")]
41+
use crate::arch::cpuid::CpuidIn;
3842
#[cfg(target_arch = "x86_64")]
3943
use crate::arch::sev::{SevPolicy, SnpPageType, SnpPolicy};
44+
#[cfg(target_arch = "x86_64")]
45+
use crate::arch::tdx::TdAttr;
4046
use crate::ffi;
4147
#[cfg(not(target_arch = "x86_64"))]
4248
use crate::hv::IrqSender;
@@ -755,6 +761,11 @@ impl Vm for KvmVm {
755761
KvmVm::snp_launch_finish(self)
756762
}
757763

764+
#[cfg(target_arch = "x86_64")]
765+
fn tdx_init_vm(&self, attr: TdAttr, cpuids: &HashMap<CpuidIn, CpuidResult>) -> Result<()> {
766+
KvmVm::tdx_init_vm(self, attr, cpuids)
767+
}
768+
758769
#[cfg(target_arch = "aarch64")]
759770
fn create_gic_v2(&self, distributor_base: u64, cpu_interface_base: u64) -> Result<Self::GicV2> {
760771
aarch64::KvmGicV2::new(self, distributor_base, cpu_interface_base)

alioth/src/hv/kvm/vm/vm_x86_64/tdx.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,17 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::arch::x86_64::CpuidResult;
16+
use std::collections::HashMap;
17+
use std::mem::MaybeUninit;
18+
19+
use crate::arch::cpuid::CpuidIn;
20+
use crate::arch::tdx::TdAttr;
1521
use crate::hv::Result;
22+
use crate::hv::kvm::tdx::tdx_op;
1623
use crate::hv::kvm::vm::KvmVm;
17-
use crate::sys::kvm::{KvmCap, KvmHypercall};
24+
use crate::sys::kvm::{KvmCap, KvmCpuid2Flag, KvmCpuidEntry2, KvmHypercall};
25+
use crate::sys::tdx::{KvmTdxCapabilities, KvmTdxCmdId, KvmTdxInitVm};
1826

1927
impl KvmVm {
2028
pub fn tdx_init(&self) -> Result<()> {
@@ -23,4 +31,55 @@ impl KvmVm {
2331
self.vm.enable_cap(KvmCap::X86_APIC_BUS_CYCLES_NS, 40)?;
2432
Ok(())
2533
}
34+
35+
fn tdx_get_capabilities(&self) -> Result<Box<KvmTdxCapabilities>> {
36+
let mut caps: Box<KvmTdxCapabilities> =
37+
Box::new(unsafe { MaybeUninit::zeroed().assume_init() });
38+
caps.cpuid.nent = caps.cpuid.entries.len() as u32;
39+
tdx_op(&self.vm.fd, KvmTdxCmdId::CAPABILITIES, 0, Some(&mut *caps))?;
40+
Ok(caps)
41+
}
42+
43+
pub fn tdx_init_vm(&self, attr: TdAttr, cpuids: &HashMap<CpuidIn, CpuidResult>) -> Result<()> {
44+
let mut init: Box<KvmTdxInitVm> = Box::new(unsafe { MaybeUninit::zeroed().assume_init() });
45+
init.attributes = attr;
46+
47+
let caps = self.tdx_get_capabilities()?;
48+
let convert = |e: &KvmCpuidEntry2| {
49+
let (mut in_, out) = From::from(*e);
50+
if in_.index.is_none() {
51+
in_.index = Some(0)
52+
}
53+
(in_, out)
54+
};
55+
let caps_cpuid = caps.cpuid.entries.iter().take(caps.cpuid.nent as usize);
56+
let caps_cpuid: HashMap<_, _> = caps_cpuid.map(convert).collect();
57+
for (in_, out) in cpuids {
58+
let cap_cpuid_in = CpuidIn {
59+
func: in_.func,
60+
index: in_.index.or(Some(0)),
61+
};
62+
let Some(cap_out) = caps_cpuid.get(&cap_cpuid_in) else {
63+
continue;
64+
};
65+
66+
let entry = &mut init.cpuid.entries[init.cpuid.nent as usize];
67+
entry.function = in_.func;
68+
entry.index = in_.index.unwrap_or(0);
69+
entry.flags = if in_.index.is_some() {
70+
KvmCpuid2Flag::SIGNIFCANT_INDEX
71+
} else {
72+
KvmCpuid2Flag::empty()
73+
};
74+
entry.eax = out.eax & cap_out.eax;
75+
entry.ebx = out.ebx & cap_out.ebx;
76+
entry.ecx = out.ecx & cap_out.ecx;
77+
entry.edx = out.edx & cap_out.edx;
78+
79+
init.cpuid.nent += 1;
80+
}
81+
82+
tdx_op(&self.vm.fd, KvmTdxCmdId::INIT_VM, 0, Some(&mut *init))?;
83+
Ok(())
84+
}
2685
}

alioth/src/sys/linux/tdx.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// limitations under the License.
1414

1515
use crate::arch::tdx::TdAttr;
16-
use crate::sys::kvm::KvmCpuid2;
16+
use crate::sys::kvm::{KVM_MAX_CPUID_ENTRIES, KvmCpuid2};
1717
use crate::{bitflags, consts};
1818

1919
consts! {
@@ -39,7 +39,7 @@ pub struct KvmTdxCmd {
3939

4040
#[repr(C)]
4141
#[derive(Debug, Clone)]
42-
pub struct KvmTdxCapabilities<const N: usize> {
42+
pub struct KvmTdxCapabilities<const N: usize = KVM_MAX_CPUID_ENTRIES> {
4343
pub supported_attrs: TdAttr,
4444
pub supported_xfam: u64,
4545
pub kernel_tdvmcallinfo_1_r11: u64,
@@ -52,8 +52,8 @@ pub struct KvmTdxCapabilities<const N: usize> {
5252

5353
#[repr(C)]
5454
#[derive(Debug, Clone)]
55-
pub struct KvmTdxInitVm<const N: usize> {
56-
pub attributes: u64,
55+
pub struct KvmTdxInitVm<const N: usize = KVM_MAX_CPUID_ENTRIES> {
56+
pub attributes: TdAttr,
5757
pub xfam: u64,
5858
pub mrconfigid: [u8; 48],
5959
pub mrowner: [u8; 48],

0 commit comments

Comments
 (0)