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 x86defs::X64_LARGE_PAGE_SIZE;
24use x86defs::tdx::RESET_VECTOR_PAGE;
25
26fn report_os_id(guest_os_id: u64) {
28 tdcall_wrmsr(
29 &mut TdcallInstruction,
30 hvdef::HV_X64_MSR_GUEST_OS_ID,
31 guest_os_id,
32 )
33 .unwrap();
34}
35
36pub fn initialize_hypercalls(guest_os_id: u64, io: &TdxHypercallPage) {
38 report_os_id(guest_os_id);
41
42 let hypercall_page_range = MemoryRange::new(io.base()..io.base() + X64_LARGE_PAGE_SIZE);
44 change_page_visibility(hypercall_page_range, true);
45}
46
47pub fn uninitialize_hypercalls(io: TdxHypercallPage) {
49 report_os_id(0);
50
51 let hypercall_page_range = MemoryRange::new(io.base()..io.base() + X64_LARGE_PAGE_SIZE);
52 tdx_unshare_large_page(io);
53
54 change_page_visibility(hypercall_page_range, false);
56 accept_pages(hypercall_page_range).expect("pages previously accepted by the bootshim should be reaccepted without failure when sharing permissions are changed");
57
58 unsafe {
60 asm! {
61 "mov rax, cr3",
62 "mov cr3, rax",
63 out("rax") _,
64 }
65 }
66}
67
68fn tdcall(input: TdcallInput) -> TdcallOutput {
70 let rax: u64;
71 let rcx;
72 let rdx;
73 let r8;
74 let r10;
75 let r11;
76
77 unsafe {
86 asm! {
87 "tdcall",
88 inout("rax") input.leaf.0 => rax,
89 inout("rcx") input.rcx => rcx,
90 inout("rdx") input.rdx => rdx,
91 inout("r8") input.r8 => r8,
92 inout("r9") input.r9 => _,
93 inout("r10") input.r10 => r10,
94 inout("r11") input.r11 => r11,
95 inout("r12") input.r12 => _,
96 inout("r13") input.r13 => _,
97 inout("r14") input.r14 => _,
98 inout("r15") input.r15 => _,
99 }
100 }
101
102 TdcallOutput {
103 rax: rax.into(),
104 rcx,
105 rdx,
106 r8,
107 r10,
108 r11,
109 }
110}
111
112pub struct TdcallInstruction;
113
114impl Tdcall for TdcallInstruction {
115 fn tdcall(&mut self, input: TdcallInput) -> TdcallOutput {
116 tdcall(input)
117 }
118}
119
120pub fn accept_pages(range: MemoryRange) -> Result<(), AcceptPagesError> {
122 tdcall::accept_pages(
123 &mut TdcallInstruction,
124 range,
125 tdcall::AcceptPagesAttributes::None,
126 )
127}
128
129pub fn change_page_visibility(range: MemoryRange, host_visible: bool) {
132 if let Err(err) = tdcall_map_gpa(&mut TdcallInstruction, range, host_visible) {
133 panic!(
134 "failed to change page visibility for {range}, host_visible = {host_visible}: {err:?}"
135 );
136 }
137}
138
139pub struct TdxIoAccess;
141
142impl minimal_rt::arch::IoAccess for TdxIoAccess {
143 unsafe fn inb(&self, port: u16) -> u8 {
144 tdcall::tdcall_io_in(&mut TdcallInstruction, port, 1).unwrap() as u8
145 }
146
147 unsafe fn outb(&self, port: u16, data: u8) {
148 let _ = tdcall::tdcall_io_out(&mut TdcallInstruction, port, data as u32, 1);
149 }
150}
151
152pub fn invoke_tdcall_hypercall(
154 control: hvdef::hypercall::Control,
155 io: &TdxHypercallPage,
156) -> hvdef::hypercall::HypercallOutput {
157 tdcall_hypercall(&mut TdcallInstruction, control, io.input(), io.output())
158}
159
160static TSC_FREQUENCY: SingleThreaded<Cell<u64>> = SingleThreaded(Cell::new(0));
162
163pub fn get_tdx_tsc_reftime() -> Option<u64> {
165 if TSC_FREQUENCY.get() == 0 {
168 const TDX_FREQ_MULTIPLIER: u64 = 25 * 1000 * 1000;
170 const CPUID_LEAF_TDX_TSC_FREQ: u32 = 0x15;
171 TSC_FREQUENCY.set(cpuid(CPUID_LEAF_TDX_TSC_FREQ, 0x0).ebx as u64 * TDX_FREQ_MULTIPLIER);
172 }
173
174 if TSC_FREQUENCY.get() != 0 {
175 let tsc = safe_intrinsics::rdtsc();
176 let count_100ns = (tsc as u128 * 10000000) / TSC_FREQUENCY.get() as u128;
177 return Some(count_100ns as u64);
178 }
179 None
180}
181
182pub fn tdx_prepare_ap_trampoline() {
186 let context_ptr: *mut TdxTrampolineContext = RESET_VECTOR_PAGE as *mut TdxTrampolineContext;
187 let tdxcontext: &mut TdxTrampolineContext = unsafe { context_ptr.as_mut().unwrap() };
189 tdxcontext.gdtr_limit = 0;
190 tdxcontext.idtr_limit = 0;
191 tdxcontext.code_selector = 0;
192 tdxcontext.task_selector = 0;
193 tdxcontext.cr0 |= x86defs::X64_CR0_PG | x86defs::X64_CR0_PE | x86defs::X64_CR0_NE;
194 tdxcontext.cr4 |= x86defs::X64_CR4_PAE | x86defs::X64_CR4_MCE;
195}
196
197pub fn setup_vtl2_vp(partition_info: &PartitionInfo) {
198 tdx_prepare_ap_trampoline();
200
201 for cpu in 1..partition_info.cpus.len() {
202 hvcall()
203 .tdx_enable_vp_vtl2(cpu as u32)
204 .expect("enabling vp should not fail");
205 }
206
207 for cpu in 1..partition_info.cpus.len() {
209 hvcall()
210 .tdx_start_vp(cpu as u32)
211 .expect("start vp should not fail");
212 }
213}