Alioth Code Coverage

tdx.rs0.00%

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
15use std::arch::x86_64::CpuidResult;
16use std::collections::HashMap;
17use std::mem::MaybeUninit;
18
19use crate::arch::cpuid::CpuidIn;
20use crate::arch::tdx::TdAttr;
21use crate::hv::Result;
22use crate::hv::kvm::vm::KvmVm;
23use crate::hv::kvm::x86_64::tdx::tdx_op;
24use crate::sys::kvm::{KvmCap, KvmCpuid2Flag, KvmCpuidEntry2, KvmHypercall};
25use crate::sys::tdx::{KvmTdxCapabilities, KvmTdxCmdId, KvmTdxInitVm};
26
27impl KvmVm {
28 pub fn tdx_init(&self) -> Result<()> {
29 let map_gpa_range = 1 << KvmHypercall::MAP_GPA_RANGE.raw();
30 self.vm.enable_cap(KvmCap::EXIT_HYPERCALL, map_gpa_range)?;
31 self.vm.enable_cap(KvmCap::X86_APIC_BUS_CYCLES_NS, 40)?;
32 Ok(())
33 }
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 // Work around the missing SIGNIFICANT_INDEX flag in KVM_TDX_CAPABILITIES
51 // (https://lore.kernel.org/all/20260223214336.722463-1-changyuanl@google.com/).
52 // Until this is fixed upstream, explicitly retain the index value from the
53 // KvmCpuidEntry2 struct.
54 in_.index = Some(e.index);
55 (in_, out)
56 };
57 let caps_cpuid = caps.cpuid.entries.iter().take(caps.cpuid.nent as usize);
58 let caps_cpuid: HashMap<_, _> = caps_cpuid.map(convert).collect();
59 for (in_, out) in cpuids {
60 let cap_cpuid_in = CpuidIn {
61 func: in_.func,
62 // Fall back to index 0 to work around the missing SIGNIFICANT_INDEX
63 // flag in KVM_TDX_CAPABILITIES.
64 index: in_.index.or(Some(0)),
65 };
66 let Some(cap_out) = caps_cpuid.get(&cap_cpuid_in) else {
67 continue;
68 };
69
70 let entry = &mut init.cpuid.entries[init.cpuid.nent as usize];
71 entry.function = in_.func;
72 entry.index = in_.index.unwrap_or(0);
73 entry.flags = if in_.index.is_some() {
74 KvmCpuid2Flag::SIGNIFCANT_INDEX
75 } else {
76 KvmCpuid2Flag::empty()
77 };
78 entry.eax = out.eax & cap_out.eax;
79 entry.ebx = out.ebx & cap_out.ebx;
80 entry.ecx = out.ecx & cap_out.ecx;
81 entry.edx = out.edx & cap_out.edx;
82
83 init.cpuid.nent += 1;
84 }
85
86 tdx_op(&self.vm.fd, KvmTdxCmdId::INIT_VM, 0, Some(&mut *init))?;
87 Ok(())
88 }
89
90 pub fn tdx_finalize_vm(&self) -> Result<()> {
91 tdx_op::<()>(&self.vm.fd, KvmTdxCmdId::FINALIZE_VM, 0, None)
92 }
93}
94