sidecar/arch/x86_64/
mod.rs1#![cfg(target_arch = "x86_64")]
7#![expect(unsafe_code)]
9
10mod init;
11mod temporary_map;
12mod vp;
13
14use core::fmt::Write;
15use core::sync::atomic::AtomicBool;
16use core::sync::atomic::Ordering::Acquire;
17use hvdef::HvError;
18use hvdef::HypercallCode;
19use minimal_rt::arch::Serial;
20use minimal_rt::arch::msr::write_msr;
21use x86defs::Exception;
22use zerocopy::FromBytes;
23use zerocopy::IntoBytes;
24
25mod addr_space {
26 use super::VpGlobals;
27 use memory_range::MemoryRange;
28
29 const PTE_SELF: usize = 0;
31 const PTE_HYPERCALL_INPUT: usize = 1;
32 const PTE_HYPERCALL_OUTPUT: usize = 2;
33 const PTE_COMMAND_PAGE: usize = 3;
34 const PTE_GLOBALS: usize = 4;
35 const PTE_ASSIST_PAGE: usize = 5;
36 const PTE_CONTROL_PAGE: usize = 6;
37 const PTE_REGISTER_PAGE: usize = 7;
38 const PTE_TEMPORARY_MAP: usize = 256;
39 const PTE_STACK: usize = PTE_STACK_END - sidecar_defs::STACK_PAGES;
40 const PTE_STACK_END: usize = 512;
41
42 const PAGE_SIZE: u64 = 0x1000;
43
44 unsafe extern "C" {
45 static __ehdr_start: u8;
46 }
47
48 fn pte_data(addr: u64) -> x86defs::Pte {
49 x86defs::Pte::new()
50 .with_address(addr)
51 .with_read_write(true)
52 .with_present(true)
53 .with_no_execute(true)
54 }
55
56 pub fn init_ap(
58 pt: &mut [x86defs::Pte; 512],
59 pt_pa: u64,
60 control_page_pa: u64,
61 command_page_pa: u64,
62 reg_page_pa: u64,
63 memory: &mut impl Iterator<Item = u64>,
64 ) -> u64 {
65 pt.fill(x86defs::Pte::new());
66 for i in 0..sidecar_defs::STACK_PAGES {
67 pt[PTE_STACK + i] = pte_data(memory.next().unwrap());
68 }
69 pt[PTE_SELF] = pte_data(pt_pa);
70 pt[PTE_COMMAND_PAGE] = pte_data(command_page_pa);
71 let globals_pa = memory.next().unwrap();
72 pt[PTE_GLOBALS] = pte_data(globals_pa);
73 pt[PTE_ASSIST_PAGE] = pte_data(memory.next().unwrap());
74 pt[PTE_HYPERCALL_INPUT] = pte_data(memory.next().unwrap());
75 pt[PTE_HYPERCALL_OUTPUT] = pte_data(memory.next().unwrap());
76 pt[PTE_CONTROL_PAGE] = pte_data(control_page_pa);
77 pt[PTE_REGISTER_PAGE] = pte_data(reg_page_pa);
78 globals_pa
79 }
80
81 fn base_address() -> usize {
82 core::ptr::addr_of!(__ehdr_start) as usize
83 }
84
85 fn per_vp(page: usize) -> usize {
86 base_address() + 0x200000 + page * PAGE_SIZE as usize
87 }
88
89 fn pte(page: usize) -> *mut x86defs::Pte {
90 (per_vp(PTE_SELF) as *mut x86defs::Pte).wrapping_add(page)
91 }
92
93 pub fn temporary_map() -> usize {
94 per_vp(PTE_TEMPORARY_MAP)
95 }
96
97 pub fn temp_ptes() -> *mut x86defs::Pte {
98 pte(PTE_TEMPORARY_MAP)
99 }
100
101 pub fn stack() -> MemoryRange {
102 MemoryRange::new(per_vp(PTE_STACK) as u64..per_vp(PTE_STACK_END) as u64)
103 }
104
105 pub fn stack_base_pa() -> usize {
106 unsafe { pte(PTE_STACK).read() }.address() as usize
108 }
109
110 pub fn command_page() -> *mut sidecar_defs::CommandPage {
111 per_vp(PTE_COMMAND_PAGE) as *mut _
112 }
113
114 pub fn globals() -> *mut VpGlobals {
115 (per_vp(PTE_GLOBALS)) as *mut _
116 }
117
118 pub fn assist_page() -> *mut hvdef::HvVpAssistPage {
119 (per_vp(PTE_ASSIST_PAGE)) as *mut _
120 }
121
122 pub fn assist_page_pa() -> u64 {
123 unsafe { pte(PTE_ASSIST_PAGE).read() }.address()
125 }
126
127 pub fn register_page() -> *mut hvdef::HvX64RegisterPage {
128 (per_vp(PTE_REGISTER_PAGE)) as *mut _
129 }
130
131 pub fn register_page_pa() -> u64 {
132 unsafe { pte(PTE_REGISTER_PAGE).read() }.address()
134 }
135
136 pub fn hypercall_input() -> *mut [u8; 4096] {
137 (per_vp(PTE_HYPERCALL_INPUT)) as *mut [u8; 4096]
138 }
139
140 pub fn hypercall_input_pa() -> u64 {
141 unsafe { pte(PTE_HYPERCALL_INPUT).read() }.address()
143 }
144
145 pub fn hypercall_output() -> *mut [u8; 4096] {
146 (per_vp(PTE_HYPERCALL_OUTPUT)) as *mut [u8; 4096]
147 }
148
149 pub fn hypercall_output_pa() -> u64 {
150 unsafe { pte(PTE_HYPERCALL_OUTPUT).read() }.address()
152 }
153
154 pub fn control_page() -> *const sidecar_defs::ControlPage {
155 (per_vp(PTE_CONTROL_PAGE)) as *const _
156 }
157}
158
159struct VpGlobals {
160 hv_vp_index: u32,
161 node_cpu_index: u32,
162 overlays_mapped: bool,
163 register_page_mapped: bool,
164}
165
166const _: () = assert!(size_of::<VpGlobals>() <= 0x1000);
167
168static mut VTL_RETURN_OFFSET: u16 = 0;
169static mut VSM_CAPABILITIES: hvdef::HvRegisterVsmCapabilities =
170 hvdef::HvRegisterVsmCapabilities::new();
171static AFTER_INIT: AtomicBool = AtomicBool::new(false);
172static ENABLE_LOG: AtomicBool = AtomicBool::new(false);
173static SERIAL: spin::Mutex<Serial<InstrIoAccess>> = spin::Mutex::new(Serial::new(InstrIoAccess));
174
175macro_rules! log {
176 () => {};
177 ($($arg:tt)*) => {
178 if $crate::arch::x86_64::ENABLE_LOG.load(core::sync::atomic::Ordering::Relaxed) {
179 $crate::arch::x86_64::log_fmt(format_args!($($arg)*));
180 }
181 };
182}
183use core::mem::size_of;
184use hvdef::HvRegisterName;
185use hvdef::HvRegisterValue;
186use hvdef::hypercall::HvInputVtl;
187pub(crate) use log;
188use minimal_rt::arch::InstrIoAccess;
189
190fn log_fmt(args: core::fmt::Arguments<'_>) {
191 if ENABLE_LOG.load(Acquire) {
192 let mut serial = SERIAL.lock();
193 if AFTER_INIT.load(Acquire) {
194 let vp_index = unsafe { &*addr_space::globals() }.hv_vp_index;
197 let _ = writeln!(serial, "sidecar#{vp_index}: {}", args);
198 } else {
199 let _ = writeln!(serial, "sidecar: {}", args);
200 }
201 }
202}
203
204#[cfg_attr(minimal_rt, panic_handler)]
205#[cfg_attr(not(minimal_rt), expect(dead_code))]
206fn panic(panic: &core::panic::PanicInfo<'_>) -> ! {
207 let stack_va_to_pa = |ptr| {
208 addr_space::stack()
209 .offset_of(ptr as u64)
210 .map(|offset| addr_space::stack_base_pa() + offset as usize)
211 };
212 minimal_rt::enlightened_panic::report(*b"SIDECARK", panic, stack_va_to_pa);
213 if !AFTER_INIT.load(Acquire) {
214 if let Some(mut serial) = SERIAL.try_lock() {
215 let _ = writeln!(serial, "{panic}");
216 } else {
217 let _ = writeln!(Serial::new(InstrIoAccess), "{panic}");
220 }
221 }
222 minimal_rt::arch::fault();
223}
224
225struct CommandErrorWriter<'a>(&'a mut sidecar_defs::CommandError);
226
227impl Write for CommandErrorWriter<'_> {
228 fn write_str(&mut self, s: &str) -> core::fmt::Result {
229 let s = s.as_bytes();
230 let buf = &mut self.0.buf[self.0.len as usize..];
231 let n = buf.len().min(s.len());
232 buf[..n].copy_from_slice(&s[..n]);
233 self.0.len += n as u8;
234 Ok(())
235 }
236}
237
238fn hypercall(code: HypercallCode, rep_count: usize) -> Result<(), HvError> {
239 let control = hvdef::hypercall::Control::new()
240 .with_code(code.0)
241 .with_rep_count(rep_count);
242
243 unsafe {
246 minimal_rt::arch::hypercall::invoke_hypercall(
247 control,
248 addr_space::hypercall_input_pa(),
249 addr_space::hypercall_output_pa(),
250 )
251 .result()
252 }
253}
254
255fn get_hv_vp_register(
256 target_vtl: HvInputVtl,
257 name: HvRegisterName,
258) -> Result<HvRegisterValue, HvError> {
259 {
260 let input = unsafe { &mut *addr_space::hypercall_input() };
262
263 hvdef::hypercall::GetSetVpRegisters {
264 partition_id: hvdef::HV_PARTITION_ID_SELF,
265 vp_index: hvdef::HV_VP_INDEX_SELF,
266 target_vtl,
267 rsvd: [0; 3],
268 }
269 .write_to_prefix(input)
270 .unwrap();
271
272 name.write_to_prefix(&mut input[size_of::<hvdef::hypercall::GetSetVpRegisters>()..])
273 .unwrap();
274 }
275
276 hypercall(HypercallCode::HvCallGetVpRegisters, 1)?;
277 let output = unsafe { &*addr_space::hypercall_output() };
279 Ok(HvRegisterValue::read_from_prefix(output).unwrap().0) }
281
282fn set_hv_vp_register(
283 target_vtl: HvInputVtl,
284 name: HvRegisterName,
285 value: HvRegisterValue,
286) -> Result<(), HvError> {
287 {
288 let input = unsafe { &mut *addr_space::hypercall_input() };
290
291 hvdef::hypercall::GetSetVpRegisters {
292 partition_id: hvdef::HV_PARTITION_ID_SELF,
293 vp_index: hvdef::HV_VP_INDEX_SELF,
294 target_vtl,
295 rsvd: [0; 3],
296 }
297 .write_to_prefix(input)
298 .unwrap();
299
300 hvdef::hypercall::HvRegisterAssoc {
301 name,
302 pad: [0; 3],
303 value,
304 }
305 .write_to_prefix(&mut input[size_of::<hvdef::hypercall::GetSetVpRegisters>()..])
306 .unwrap();
307 }
308
309 hypercall(HypercallCode::HvCallSetVpRegisters, 1)?;
310 Ok(())
311}
312
313fn eoi() {
314 unsafe {
316 write_msr(x86defs::apic::ApicRegister::EOI.x2apic_msr(), 0);
317 }
318}
319
320#[cfg_attr(not(minimal_rt), expect(dead_code))]
321extern "C" fn irq_handler() {
322 eoi();
323}
324
325#[cfg_attr(not(minimal_rt), expect(dead_code))]
326extern "C" fn exception_handler(exception: Exception, rsp: u64) -> ! {
327 let cr2 = unsafe {
329 let cr2: u64;
330 core::arch::asm!("mov {}, cr2", out(reg) cr2);
331 cr2
332 };
333 panic!("unexpected exception {exception:?} cr2 = {cr2:#x} rsp = {rsp:#x}");
334}
335
336#[cfg(minimal_rt)]
337core::arch::global_asm! {
338 include_str!("entry.S"),
339 start = sym init::start,
340 relocate = sym minimal_rt::reloc::relocate,
341 irq_handler = sym irq_handler,
342 exception_handler = sym exception_handler,
343}