openhcl_boot/arch/x86_64/
tdx.rs1use crate::arch::x86_64::address_space::TdxHypercallPage;
7use crate::arch::x86_64::address_space::tdx_unshare_large_page;
8use crate::host_params::PartitionInfo;
9use crate::hvcall;
10use crate::single_threaded::SingleThreaded;
11use core::arch::asm;
12use core::cell::Cell;
13use loader_defs::shim::TdxTrampolineContext;
14use memory_range::MemoryRange;
15use safe_intrinsics::cpuid;
16use tdcall::AcceptPagesError;
17use tdcall::Tdcall;
18use tdcall::TdcallInput;
19use tdcall::TdcallOutput;
20use tdcall::tdcall_hypercall;
21use tdcall::tdcall_map_gpa;
22use tdcall::tdcall_wrmsr;
23use tdx_guest_device::protocol::TdReport;
24use x86defs::X64_LARGE_PAGE_SIZE;
25use x86defs::tdx::RESET_VECTOR_PAGE;
26use x86defs::tdx::TdCallResult;
27use x86defs::tdx::TdVmCallR10Result;
28
29fn report_os_id(guest_os_id: u64) {
31 tdcall_wrmsr(
32 &mut TdcallInstruction,
33 hvdef::HV_X64_MSR_GUEST_OS_ID,
34 guest_os_id,
35 )
36 .unwrap();
37}
38
39pub fn initialize_hypercalls(guest_os_id: u64, io: &TdxHypercallPage) {
41 report_os_id(guest_os_id);
44
45 let hypercall_page_range = MemoryRange::new(io.base()..io.base() + X64_LARGE_PAGE_SIZE);
47 change_page_visibility(hypercall_page_range, true);
48}
49
50pub fn uninitialize_hypercalls(io: TdxHypercallPage) {
52 report_os_id(0);
53
54 let hypercall_page_range = MemoryRange::new(io.base()..io.base() + X64_LARGE_PAGE_SIZE);
55 tdx_unshare_large_page(io);
56
57 change_page_visibility(hypercall_page_range, false);
59 accept_pages(hypercall_page_range).expect("pages previously accepted by the bootshim should be reaccepted without failure when sharing permissions are changed");
60
61 unsafe {
63 asm! {
64 "mov rax, cr3",
65 "mov cr3, rax",
66 out("rax") _,
67 }
68 }
69}
70
71fn tdcall(input: TdcallInput) -> TdcallOutput {
73 let rax: u64;
74 let rcx;
75 let rdx;
76 let r8;
77 let r10;
78 let r11;
79
80 unsafe {
89 asm! {
90 "tdcall",
91 inout("rax") input.leaf.0 => rax,
92 inout("rcx") input.rcx => rcx,
93 inout("rdx") input.rdx => rdx,
94 inout("r8") input.r8 => r8,
95 inout("r9") input.r9 => _,
96 inout("r10") input.r10 => r10,
97 inout("r11") input.r11 => r11,
98 inout("r12") input.r12 => _,
99 inout("r13") input.r13 => _,
100 inout("r14") input.r14 => _,
101 inout("r15") input.r15 => _,
102 }
103 }
104
105 TdcallOutput {
106 rax: rax.into(),
107 rcx,
108 rdx,
109 r8,
110 r10,
111 r11,
112 }
113}
114
115pub struct TdcallInstruction;
116
117impl Tdcall for TdcallInstruction {
118 fn tdcall(&mut self, input: TdcallInput) -> TdcallOutput {
119 tdcall(input)
120 }
121}
122
123pub fn accept_pages(range: MemoryRange) -> Result<(), AcceptPagesError> {
125 tdcall::accept_pages(
126 &mut TdcallInstruction,
127 range,
128 tdcall::AcceptPagesAttributes::None,
129 )
130}
131
132pub fn change_page_visibility(range: MemoryRange, host_visible: bool) {
135 if let Err(err) = tdcall_map_gpa(&mut TdcallInstruction, range, host_visible) {
136 panic!(
137 "failed to change page visibility for {range}, host_visible = {host_visible}: {err:?}"
138 );
139 }
140}
141
142pub struct TdxIoAccess;
144
145impl minimal_rt::arch::IoAccess for TdxIoAccess {
146 unsafe fn inb(&self, port: u16) -> u8 {
147 tdcall::tdcall_io_in(&mut TdcallInstruction, port, 1).unwrap() as u8
148 }
149
150 unsafe fn outb(&self, port: u16, data: u8) {
151 let _ = tdcall::tdcall_io_out(&mut TdcallInstruction, port, data as u32, 1);
152 }
153}
154
155pub fn invoke_tdcall_hypercall(
157 control: hvdef::hypercall::Control,
158 io: &TdxHypercallPage,
159) -> hvdef::hypercall::HypercallOutput {
160 let result = tdcall_hypercall(&mut TdcallInstruction, control, io.input(), io.output());
161 match result {
162 Ok(()) => 0.into(),
163 Err(val) => {
164 let TdVmCallR10Result(return_code) = val;
165 return_code.into()
166 }
167 }
168}
169
170static TSC_FREQUENCY: SingleThreaded<Cell<u64>> = SingleThreaded(Cell::new(0));
172
173pub fn get_tdx_tsc_reftime() -> Option<u64> {
175 if TSC_FREQUENCY.get() == 0 {
178 const TDX_FREQ_MULTIPLIER: u64 = 25 * 1000 * 1000;
180 const CPUID_LEAF_TDX_TSC_FREQ: u32 = 0x15;
181 TSC_FREQUENCY.set(cpuid(CPUID_LEAF_TDX_TSC_FREQ, 0x0).ebx as u64 * TDX_FREQ_MULTIPLIER);
182 }
183
184 if TSC_FREQUENCY.get() != 0 {
185 let tsc = safe_intrinsics::rdtsc();
186 let count_100ns = (tsc as u128 * 10000000) / TSC_FREQUENCY.get() as u128;
187 return Some(count_100ns as u64);
188 }
189 None
190}
191
192pub fn tdx_prepare_ap_trampoline() {
196 let context_ptr: *mut TdxTrampolineContext = RESET_VECTOR_PAGE as *mut TdxTrampolineContext;
197 let tdxcontext: &mut TdxTrampolineContext = unsafe { context_ptr.as_mut().unwrap() };
199 tdxcontext.gdtr_limit = 0;
200 tdxcontext.idtr_limit = 0;
201 tdxcontext.code_selector = 0;
202 tdxcontext.task_selector = 0;
203 tdxcontext.cr0 |= x86defs::X64_CR0_PG | x86defs::X64_CR0_PE | x86defs::X64_CR0_NE;
204 tdxcontext.cr4 |= x86defs::X64_CR4_PAE | x86defs::X64_CR4_MCE;
205}
206
207pub fn setup_vtl2_vp(partition_info: &PartitionInfo) {
208 for cpu in 1..partition_info.cpus.len() {
209 hvcall()
210 .tdx_enable_vp_vtl2(cpu as u32)
211 .expect("enabling vp should not fail");
212 }
213
214 for cpu in 1..partition_info.cpus.len() {
216 hvcall()
217 .tdx_start_vp(cpu as u32)
218 .expect("start vp should not fail");
219 }
220
221 tdx_prepare_ap_trampoline();
223}
224
225pub fn get_tdreport(report: &mut TdReport) -> Result<(), TdCallResult> {
227 tdcall::tdcall_mr_report(&mut TdcallInstruction, report)
228}