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