1use guestmem::GuestMemory;
7use guestmem::GuestMemoryError;
8use hvdef::HV_PAGE_SIZE;
9use hvdef::HV_PAGE_SIZE_USIZE;
10use hvdef::HvError;
11use hvdef::HvResult;
12use hvdef::HypercallCode;
13use hvdef::hypercall::Control;
14use hvdef::hypercall::HypercallOutput;
15use std::marker::PhantomData;
16use thiserror::Error;
17use zerocopy::FromBytes;
18use zerocopy::Immutable;
19use zerocopy::IntoBytes;
20use zerocopy::KnownLayout;
21use zerocopy::Ref;
22
23#[derive(Copy, Clone)]
25pub enum HypercallData {
26 Simple {
28 input_size: usize,
30 output_size: usize,
32 is_variable: bool,
34 },
35 Rep {
37 header_size: usize,
39 input_element_size: usize,
41 output_element_size: usize,
43 is_variable: bool,
45 },
46 Vtl,
48}
49
50pub struct HypercallParameters<'a> {
52 control: Control,
53 input: &'a [u8],
54 output: &'a mut [u8],
55}
56
57#[repr(C, align(16))]
59#[derive(Copy, Clone)]
60struct HypercallAlignedBuf128<const N: usize>([[u64; 2]; N]);
61
62impl<const N: usize> HypercallAlignedBuf128<N> {
63 fn new_zeroed() -> Self {
64 Self([[0, 0]; N])
65 }
66}
67
68type HypercallAlignedPage = HypercallAlignedBuf128<{ HV_PAGE_SIZE_USIZE / 16 }>;
69
70struct InnerDispatcher<'a, T> {
72 control: Control,
73 guest_memory: &'a GuestMemory,
74 handler: T,
75}
76
77#[derive(Debug, Error)]
78enum HypercallParseError {
79 #[error("invalid control: {0:?}")]
80 InvalidControl(Control),
81 #[error("hypercall input too large for fast hypercall")]
82 TooBigForFast,
83 #[error("input/output straddles a page boundary")]
84 Straddle,
85 #[error("memory access error")]
86 Access(#[source] GuestMemoryError),
87 #[error("unaligned memory access")]
88 Unaligned,
89}
90
91impl From<HypercallParseError> for HvError {
92 fn from(err: HypercallParseError) -> Self {
93 tracing::warn!(
94 error = &err as &dyn std::error::Error,
95 "hypercall parse failure"
96 );
97 match err {
98 HypercallParseError::Unaligned => Self::InvalidAlignment,
99 _ => Self::InvalidHypercallInput,
100 }
101 }
102}
103
104pub trait AsHandler<H> {
110 fn as_handler(&mut self) -> &mut H;
112}
113
114impl<'a, T: HypercallIo> InnerDispatcher<'a, T> {
115 fn new(guest_memory: &'a GuestMemory, mut handler: T) -> Self {
117 Self {
118 control: handler.control().into(),
119 guest_memory,
120 handler,
121 }
122 }
123
124 fn code(&self) -> HypercallCode {
126 HypercallCode(self.control.code())
127 }
128
129 fn unhandled(&self) -> Option<HypercallOutput> {
131 tracelimit::warn_ratelimited!(code = ?self.code(), "no handler for hypercall code");
132 Some(HvError::InvalidHypercallCode.into())
133 }
134
135 fn complete(&mut self, output: Option<HypercallOutput>) {
137 if let Some(output) = output {
138 if output.call_status() == Err(HvError::Timeout).into() {
139 self.handler.retry(
140 self.control
141 .with_rep_start(output.elements_processed())
142 .into(),
143 );
144 } else {
145 self.handler.set_result(output.into());
146 self.handler.advance_ip();
147 }
148 }
149 }
150
151 fn dispatch_dyn<H>(
152 &mut self,
153 data: &HypercallData,
154 dispatch: fn(&mut H, HypercallParameters<'_>) -> HypercallOutput,
155 ) -> Option<HypercallOutput>
156 where
157 T: AsHandler<H>,
158 {
159 self.dispatch_inner(data, dispatch)
160 .unwrap_or_else(|err| Some(err.into()))
161 }
162
163 fn dispatch_inner<H>(
164 &mut self,
165 data: &HypercallData,
166 dispatch: fn(&mut H, HypercallParameters<'_>) -> HypercallOutput,
167 ) -> Result<Option<HypercallOutput>, HvError>
168 where
169 T: AsHandler<H>,
170 {
171 tracing::trace!(code = ?self.code(), "hypercall");
172 let control = self.control;
173
174 let (input_len, output_start, output_len, out_elem_size) = match *data {
175 HypercallData::Vtl => {
176 let input = self.handler.vtl_input();
177 let _ = (dispatch)(
178 self.handler.as_handler(),
179 HypercallParameters {
180 control,
181 input: input.as_bytes(),
182 output: &mut [],
183 },
184 );
185 return Ok(None);
186 }
187 HypercallData::Simple {
188 input_size,
189 output_size,
190 is_variable,
191 } => {
192 if control.rep_count() != 0
193 || control.rep_start() != 0
194 || (!is_variable && control.variable_header_size() != 0)
195 {
196 return Err(HypercallParseError::InvalidControl(control).into());
197 }
198
199 let input_size = input_size + control.variable_header_size() * 8;
200 (input_size, 0, output_size, 0)
201 }
202 HypercallData::Rep {
203 header_size,
204 input_element_size,
205 output_element_size,
206 is_variable,
207 } => {
208 if control.rep_count() == 0
209 || (!is_variable && control.variable_header_size() != 0)
210 || control.rep_start() >= control.rep_count()
211 {
212 return Err(HypercallParseError::InvalidControl(control).into());
213 }
214
215 let input_len = header_size
216 + control.variable_header_size() * 8
217 + input_element_size * control.rep_count();
218 let output_start = output_element_size * control.rep_start();
219 let output_len = output_element_size * control.rep_count();
220 (input_len, output_start, output_len, output_element_size)
221 }
222 };
223
224 let mut input_buffer = HypercallAlignedPage::new_zeroed();
225 let mut output_buffer = HypercallAlignedPage::new_zeroed();
226
227 let ret = if control.fast() {
228 let input_regpairs = input_len.div_ceil(16);
229 let output_regpairs = output_len.div_ceil(16);
230 if self.handler.fast_register_pair_count() < input_regpairs
231 || self.handler.fast_register_pair_count() - input_regpairs < output_regpairs
232 || (output_regpairs > 0 && !self.handler.extended_fast_hypercalls_ok())
233 {
234 return Err(HypercallParseError::TooBigForFast.into());
235 }
236
237 let input = &mut input_buffer.0[..input_regpairs];
238 let output = &mut output_buffer.0[..output_regpairs];
239
240 let output_start_index = self.handler.fast_input(input, output_regpairs);
242 let completed_output_pairs = output_start / 16;
243 let (new_output_index, completed_output_pairs) = match output_start % 16 {
244 0 => (
245 output_start_index + completed_output_pairs,
246 completed_output_pairs,
247 ),
248 _ => {
249 let partial_output_index = output_start_index + completed_output_pairs;
253 self.handler.fast_regs(
254 partial_output_index,
255 &mut output[completed_output_pairs..completed_output_pairs + 1],
256 );
257 (partial_output_index, completed_output_pairs)
258 }
259 };
260
261 let ret = (dispatch)(
262 self.handler.as_handler(),
263 HypercallParameters {
264 control,
265 input: &input.as_bytes()[..input_len],
266 output: &mut output.as_mut_bytes()[..output_len],
267 },
268 );
269
270 let output_end = if out_elem_size > 0 {
274 out_elem_size * ret.elements_processed()
275 } else if ret.call_status().is_ok() {
276 output_len
277 } else {
278 0
279 };
280
281 let output_regpairs = output_end.div_ceil(16);
282
283 let output = &output[completed_output_pairs..output_regpairs];
286 self.handler.fast_output(new_output_index, output);
287 ret
288 } else {
289 let check_buffer = |gpa: u64, len: usize| {
290 if (len as u64) > (HV_PAGE_SIZE - gpa % HV_PAGE_SIZE) {
292 return Err(HvError::from(HypercallParseError::Straddle));
293 }
294
295 if len != 0 && gpa % 8 != 0 {
297 return Err(HvError::from(HypercallParseError::Unaligned));
298 }
299
300 Ok(())
301 };
302
303 check_buffer(self.handler.input_gpa(), input_len)?;
304 check_buffer(self.handler.output_gpa(), output_len)?;
305
306 let input = &mut input_buffer.0.as_mut_bytes()[..input_len];
307 let output = &mut output_buffer.0.as_mut_bytes()[..output_len];
308
309 self.guest_memory
312 .read_at(self.handler.input_gpa(), input)
313 .map_err(HypercallParseError::Access)?;
314
315 let output_gpa = self.handler.output_gpa();
316
317 let ret = (dispatch)(
318 self.handler.as_handler(),
319 HypercallParameters {
320 control,
321 input,
322 output,
323 },
324 );
325
326 let output_end = if out_elem_size > 0 {
330 out_elem_size * ret.elements_processed()
331 } else if ret.call_status().is_ok() {
332 output_len
333 } else {
334 0
335 };
336
337 self.guest_memory
338 .write_at(
339 output_gpa.wrapping_add(output_start as u64),
340 &output[output_start..output_end],
341 )
342 .map_err(HypercallParseError::Access)?;
343
344 ret
345 };
346
347 if ret.call_status().is_ok() {
348 debug_assert_eq!(ret.elements_processed(), control.rep_count());
349 }
350
351 Ok(Some(ret))
352 }
353}
354
355pub trait HypercallIo {
357 fn advance_ip(&mut self);
361
362 fn retry(&mut self, control: u64);
368
369 fn control(&mut self) -> u64;
371
372 fn input_gpa(&mut self) -> u64;
374
375 fn output_gpa(&mut self) -> u64;
377
378 fn fast_register_pair_count(&mut self) -> usize;
380
381 fn extended_fast_hypercalls_ok(&mut self) -> bool;
383
384 fn fast_input(&mut self, buf: &mut [[u64; 2]], output_register_pairs: usize) -> usize;
387
388 fn fast_output(&mut self, starting_pair_index: usize, buf: &[[u64; 2]]);
390
391 fn vtl_input(&mut self) -> u64;
393
394 fn set_result(&mut self, n: u64);
396
397 fn fast_regs(&mut self, starting_pair_index: usize, buf: &mut [[u64; 2]]);
399}
400
401impl<T: HypercallIo> HypercallIo for &mut T {
402 fn advance_ip(&mut self) {
403 (**self).advance_ip()
404 }
405
406 fn retry(&mut self, control: u64) {
407 (**self).retry(control)
408 }
409
410 fn control(&mut self) -> u64 {
411 (**self).control()
412 }
413
414 fn input_gpa(&mut self) -> u64 {
415 (**self).input_gpa()
416 }
417
418 fn output_gpa(&mut self) -> u64 {
419 (**self).output_gpa()
420 }
421
422 fn fast_register_pair_count(&mut self) -> usize {
423 (**self).fast_register_pair_count()
424 }
425
426 fn extended_fast_hypercalls_ok(&mut self) -> bool {
427 (**self).extended_fast_hypercalls_ok()
428 }
429
430 fn fast_input(&mut self, buf: &mut [[u64; 2]], output_register_pairs: usize) -> usize {
431 (**self).fast_input(buf, output_register_pairs)
432 }
433
434 fn fast_output(&mut self, starting_pair_index: usize, buf: &[[u64; 2]]) {
435 (**self).fast_output(starting_pair_index, buf)
436 }
437
438 fn vtl_input(&mut self) -> u64 {
439 (**self).vtl_input()
440 }
441
442 fn set_result(&mut self, n: u64) {
443 (**self).set_result(n)
444 }
445
446 fn fast_regs(&mut self, starting_pair_index: usize, buf: &mut [[u64; 2]]) {
447 (**self).fast_regs(starting_pair_index, buf)
448 }
449}
450
451pub trait HypercallDefinition {
453 const CODE: HypercallCode;
455 const DATA: HypercallData;
457}
458
459pub trait HypercallDispatch<T> {
461 fn dispatch(&mut self, params: HypercallParameters<'_>) -> HypercallOutput;
463}
464
465pub struct SimpleHypercall<In, Out, const CODE: u16>(PhantomData<(In, Out)>);
467
468impl<In, Out, const CODE: u16> SimpleHypercall<In, Out, CODE>
469where
470 In: IntoBytes + FromBytes + Immutable + KnownLayout,
471 Out: IntoBytes + FromBytes + Immutable + KnownLayout,
472{
473 pub fn parse(params: HypercallParameters<'_>) -> (&In, &mut Out) {
475 (
476 FromBytes::ref_from_prefix(params.input).unwrap().0, FromBytes::mut_from_prefix(params.output).unwrap().0, )
479 }
480
481 pub fn run(
482 params: HypercallParameters<'_>,
483 f: impl FnOnce(&In) -> HvResult<Out>,
484 ) -> HypercallOutput {
485 let (input, output) = Self::parse(params);
486 match f(input) {
487 Ok(r) => {
488 *output = r;
489 HypercallOutput::SUCCESS
490 }
491 Err(e) => HypercallOutput::from(e),
492 }
493 }
494}
495
496impl<In, Out, const CODE: u16> HypercallDefinition for SimpleHypercall<In, Out, CODE> {
497 const CODE: HypercallCode = HypercallCode(CODE);
498
499 const DATA: HypercallData = HypercallData::Simple {
500 input_size: size_of::<In>(),
501 output_size: size_of::<Out>(),
502 is_variable: false,
503 };
504}
505
506pub struct VariableHypercall<In, Out, const CODE: u16>(PhantomData<(In, Out)>);
508
509impl<In, Out, const CODE: u16> VariableHypercall<In, Out, CODE>
510where
511 In: IntoBytes + FromBytes + Immutable + KnownLayout,
512 Out: IntoBytes + FromBytes + Immutable + KnownLayout,
513{
514 pub fn parse(params: HypercallParameters<'_>) -> (&In, &[u64], &mut Out) {
516 let (input, rest) = Ref::<_, In>::from_prefix(params.input).unwrap();
517 (
518 Ref::into_ref(input),
519 <[u64]>::ref_from_bytes(rest).unwrap(), Out::mut_from_prefix(params.output).unwrap().0, )
522 }
523
524 pub fn run(
525 params: HypercallParameters<'_>,
526 f: impl FnOnce(&In, &[u64]) -> HvResult<Out>,
527 ) -> HypercallOutput {
528 let (input, var_header, output) = Self::parse(params);
529 match f(input, var_header) {
530 Ok(r) => {
531 *output = r;
532 HypercallOutput::SUCCESS
533 }
534 Err(e) => HypercallOutput::from(e),
535 }
536 }
537}
538
539impl<In, Out, const CODE: u16> HypercallDefinition for VariableHypercall<In, Out, CODE> {
540 const CODE: HypercallCode = HypercallCode(CODE);
541
542 const DATA: HypercallData = HypercallData::Simple {
543 input_size: size_of::<In>(),
544 output_size: size_of::<Out>(),
545 is_variable: true,
546 };
547}
548
549pub struct RepHypercall<Hdr, In, Out, const CODE: u16>(PhantomData<(Hdr, In, Out)>);
551
552pub type HvRepResult = Result<(), (HvError, usize)>;
557
558impl<Hdr, In, Out, const CODE: u16> RepHypercall<Hdr, In, Out, CODE>
559where
560 Hdr: IntoBytes + FromBytes + Immutable + KnownLayout,
561 In: IntoBytes + FromBytes + Immutable + KnownLayout,
562 Out: IntoBytes + FromBytes + Immutable + KnownLayout,
563{
564 pub fn parse(params: HypercallParameters<'_>) -> (&Hdr, &[In], &mut [Out]) {
566 let (header, rest) = Ref::<_, Hdr>::from_prefix(params.input).unwrap();
567 let input = if size_of::<In>() == 0 {
568 &[]
569 } else {
570 &<[In]>::ref_from_bytes(rest).unwrap()[params.control.rep_start()..]
572 };
573 let output = if size_of::<Out>() == 0 {
574 &mut []
575 } else {
576 &mut <[Out]>::mut_from_prefix_with_elems(
578 params.output,
579 params.output.len() / size_of::<Out>(),
580 )
581 .unwrap()
582 .0[params.control.rep_start()..]
583 };
584
585 (Ref::into_ref(header), input, output)
586 }
587
588 pub fn run(
589 params: HypercallParameters<'_>,
590 f: impl FnOnce(&Hdr, &[In], &mut [Out]) -> HvRepResult,
591 ) -> HypercallOutput {
592 let control = params.control;
593 let (header, input, output) = Self::parse(params);
594 match f(header, input, output) {
595 Ok(()) => HypercallOutput::SUCCESS.with_elements_processed(control.rep_count()),
596 Err((e, reps)) => {
597 assert!(
598 control.rep_start() + reps < control.rep_count(),
599 "more reps processed than requested"
600 );
601 HypercallOutput::from(e).with_elements_processed(control.rep_start() + reps)
602 }
603 }
604 }
605}
606
607impl<Hdr, In, Out, const CODE: u16> HypercallDefinition for RepHypercall<Hdr, In, Out, CODE> {
608 const CODE: HypercallCode = HypercallCode(CODE);
609
610 const DATA: HypercallData = HypercallData::Rep {
611 header_size: size_of::<Hdr>(),
612 input_element_size: size_of::<In>(),
613 output_element_size: size_of::<Out>(),
614 is_variable: false,
615 };
616}
617
618pub struct VariableRepHypercall<Hdr, In, Out, const CODE: u16>(PhantomData<(Hdr, In, Out)>);
620
621impl<Hdr, In, Out, const CODE: u16> VariableRepHypercall<Hdr, In, Out, CODE>
622where
623 Hdr: IntoBytes + FromBytes + Immutable + KnownLayout,
624 In: IntoBytes + FromBytes + Immutable + KnownLayout,
625 Out: IntoBytes + FromBytes + Immutable + KnownLayout,
626{
627 pub fn parse(params: HypercallParameters<'_>) -> (&Hdr, &[u64], &[In], &mut [Out]) {
629 let (header, rest) = Ref::<_, Hdr>::from_prefix(params.input).unwrap();
630 let (var_header, rest) =
631 <[u64]>::ref_from_prefix_with_elems(rest, params.control.variable_header_size())
632 .unwrap();
633 let input = if size_of::<In>() == 0 {
634 &[]
635 } else {
636 &<[In]>::ref_from_bytes(rest).unwrap()[params.control.rep_start()..]
637 };
638 let output = if size_of::<Out>() == 0 {
639 &mut []
640 } else {
641 &mut <[Out]>::mut_from_prefix_with_elems(
643 params.output,
644 params.output.len() / size_of::<Out>(),
645 )
646 .unwrap()
647 .0[params.control.rep_start()..]
648 };
649 (Ref::into_ref(header), var_header, input, output)
650 }
651
652 pub fn run(
653 params: HypercallParameters<'_>,
654 f: impl FnOnce(&Hdr, &[u64], &[In], &mut [Out]) -> HvRepResult,
655 ) -> HypercallOutput {
656 let control = params.control;
657 let (header, var_header, input, output) = Self::parse(params);
658 match f(header, var_header, input, output) {
659 Ok(()) => HypercallOutput::SUCCESS.with_elements_processed(control.rep_count()),
660 Err((e, reps)) => {
661 assert!(
662 control.rep_start() + reps < control.rep_count(),
663 "more reps processed than requested"
664 );
665 HypercallOutput::from(e).with_elements_processed(control.rep_start() + reps)
666 }
667 }
668 }
669}
670
671impl<Hdr, In, Out, const CODE: u16> HypercallDefinition
672 for VariableRepHypercall<Hdr, In, Out, CODE>
673{
674 const CODE: HypercallCode = HypercallCode(CODE);
675
676 const DATA: HypercallData = HypercallData::Rep {
677 header_size: size_of::<Hdr>(),
678 input_element_size: size_of::<In>(),
679 output_element_size: size_of::<Out>(),
680 is_variable: true,
681 };
682}
683
684pub struct VtlHypercall<const CODE: u16>(());
686
687impl<const CODE: u16> VtlHypercall<CODE> {
688 pub fn parse(params: HypercallParameters<'_>) -> (u64, Control) {
689 (u64::read_from_bytes(params.input).unwrap(), params.control)
690 }
691
692 pub fn run(params: HypercallParameters<'_>, f: impl FnOnce(u64, Control)) -> HypercallOutput {
693 let (input, control) = Self::parse(params);
694 f(input, control);
695 HypercallOutput::SUCCESS
696 }
697}
698
699impl<const CODE: u16> HypercallDefinition for VtlHypercall<CODE> {
700 const CODE: HypercallCode = HypercallCode(CODE);
701 const DATA: HypercallData = HypercallData::Vtl;
702}
703
704#[macro_export]
720macro_rules! dispatcher {
721 ($handler:ty, [ $($(#[$a:meta])* $hc:ty),* $(,)? ] $(,)?) => {
722 {
723 use $crate::{Dispatcher, HypercallDefinition, HypercallHandler};
724
725 Dispatcher::<$handler>::new(|hc| match hc {
726 $(
727 $(#[$a])*
728 <$hc as HypercallDefinition>::CODE => Some(HypercallHandler::new::<$hc>()),
729 )*
730 _ => None,
731 })
732 }
733 };
734}
735
736pub struct Dispatcher<H> {
740 lookup: fn(HypercallCode) -> Option<HypercallHandler<H>>,
741}
742
743#[doc(hidden)]
744pub struct HypercallHandler<H> {
745 data: &'static HypercallData,
746 f: fn(&mut H, HypercallParameters<'_>) -> HypercallOutput,
747}
748
749impl<H> HypercallHandler<H> {
750 pub fn new<C: HypercallDefinition>() -> Self
751 where
752 H: HypercallDispatch<C>,
753 {
754 Self {
755 data: &C::DATA,
756 f: H::dispatch,
757 }
758 }
759}
760
761impl<H> Dispatcher<H> {
762 #[doc(hidden)]
763 pub const fn new(lookup: fn(HypercallCode) -> Option<HypercallHandler<H>>) -> Self {
764 Self { lookup }
765 }
766
767 pub fn dispatch(&self, guest_memory: &GuestMemory, handler: impl HypercallIo + AsHandler<H>) {
769 let mut dispatcher = InnerDispatcher::new(guest_memory, handler);
770 let result = match (self.lookup)(dispatcher.code()) {
771 Some(x) => dispatcher.dispatch_dyn(x.data, x.f),
772 None => dispatcher.unhandled(),
773 };
774 dispatcher.complete(result);
775 }
776}