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