hv1_hypercall/
support.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Support for parsing and handling hypercalls.
5
6use 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/// A hypercall definition.
24#[derive(Copy, Clone)]
25pub enum HypercallData {
26    /// A non-rep hypercall.
27    Simple {
28        /// The fixed input size.
29        input_size: usize,
30        /// The fixed output size.
31        output_size: usize,
32        /// If true, the input is variable sized.
33        is_variable: bool,
34    },
35    /// A rep hypercall.
36    Rep {
37        /// The fixed input size.
38        header_size: usize,
39        /// The input element size.
40        input_element_size: usize,
41        /// The output element size.
42        output_element_size: usize,
43        /// If true, the input is variable sized.
44        is_variable: bool,
45    },
46    /// A VTL switch hypercall.
47    Vtl,
48}
49
50/// Parameters to pass to a hypercall dispatch function.
51pub struct HypercallParameters<'a> {
52    control: Control,
53    input: &'a [u8],
54    output: &'a mut [u8],
55}
56
57/// `[u64; 2]` buffer aligned to 16 bytes for hypercall inputs.
58#[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
70/// A helper for dispatching hypercalls.
71struct 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
104/// Trait for getting the handler from the `handler` parameter passed to
105/// [`Dispatcher::dispatch`].
106///
107/// This is useful when the handler parameter is a wrapper that applies a
108/// specific hypercall calling convention.
109pub trait AsHandler<H> {
110    /// Gets the inner handler.
111    fn as_handler(&mut self) -> &mut H;
112}
113
114impl<'a, T: HypercallIo> InnerDispatcher<'a, T> {
115    /// Creates a new dispatcher.
116    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    /// Gets the hypercall code being dispatched.
125    fn code(&self) -> HypercallCode {
126        HypercallCode(self.control.code())
127    }
128
129    /// Logs an unsupported hypercall and returns the appropriate error.
130    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    /// Complete hypercall handling.
136    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            // Read in the input.
241            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                    // There are some number of completed output pairs, and one partial pair.
250                    // Copy the partial register pair from the previous output to the appropriate
251                    // location in the output buffer.
252                    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            // For rep hypercalls, always write back the completed number of reps (which may be 0).
271            // For simple hypercalls, on success write back all output. On failure (and timeout,
272            // which is handled as a failure), nothing is written back.
273            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            // Only need to write back output regpairs that were not previously completely written
284            // out, at the new output location.
285            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                // All IO must fit within a single page.
291                if (len as u64) > (HV_PAGE_SIZE - gpa % HV_PAGE_SIZE) {
292                    return Err(HvError::from(HypercallParseError::Straddle));
293                }
294
295                // The buffer must be 8 byte aligned.
296                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            // FUTURE: consider copying only the header and entries after
310            // `rep_start` for rep hypercalls.
311            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            // For rep hypercalls, always write back the completed number of reps (which may be 0).
327            // For simple hypercalls, on success write back all output. On failure (and timeout,
328            // which is handled as a failure), nothing is written back.
329            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
355/// Provides input and output parameters for a hypercall.
356pub trait HypercallIo {
357    /// Advances the instruction pointer for a completed hypercall.
358    ///
359    /// Either `advance_ip` or `retry` will be called.
360    fn advance_ip(&mut self);
361
362    /// Retains the instruction pointer at the hypercall point so that the
363    /// hypercall will be retried.
364    ///
365    /// Either `advance_ip` or `retry` will be called.
366    /// `control` is the updated hypercall input value to use in the retry.
367    fn retry(&mut self, control: u64);
368
369    /// The hypercall input value.
370    fn control(&mut self) -> u64;
371
372    /// The guest address of the hypercall input.
373    fn input_gpa(&mut self) -> u64;
374
375    /// The guest address of the hypercall output.
376    fn output_gpa(&mut self) -> u64;
377
378    /// Returns the maximum number of fast register pairs.
379    fn fast_register_pair_count(&mut self) -> usize;
380
381    /// Returns whether extended fast hypercall input/output is allowed.
382    fn extended_fast_hypercalls_ok(&mut self) -> bool;
383
384    /// Fills the buffer with fast input parameters. Given an output size in
385    /// register pairs, returns the index of the first output register pair.
386    fn fast_input(&mut self, buf: &mut [[u64; 2]], output_register_pairs: usize) -> usize;
387
388    /// Writes fast output registers from the buffer.
389    fn fast_output(&mut self, starting_pair_index: usize, buf: &[[u64; 2]]);
390
391    /// The VTL switch hypercall input parameter.
392    fn vtl_input(&mut self) -> u64;
393
394    /// Sets the hypercall result.
395    fn set_result(&mut self, n: u64);
396
397    /// Reads fast input/output registers into a buffer, given the starting pair index.
398    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
451/// A trait defined on dummy objects to provide metadata for a hypercall.
452pub trait HypercallDefinition {
453    /// The hypercall code.
454    const CODE: HypercallCode;
455    /// The associated hypercall metadata.
456    const DATA: HypercallData;
457}
458
459/// A trait to dispatch an individual hypercall.
460pub trait HypercallDispatch<T> {
461    /// Dispatch this hypercall.
462    fn dispatch(&mut self, params: HypercallParameters<'_>) -> HypercallOutput;
463}
464
465/// A simple, non-variable hypercall.
466pub 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    /// Parses the hypercall parameters to input and output types.
474    pub fn parse(params: HypercallParameters<'_>) -> (&In, &mut Out) {
475        (
476            FromBytes::ref_from_prefix(params.input).unwrap().0, // TODO: zerocopy: ref-from-prefix: use-rest-of-range, err (https://github.com/microsoft/openvmm/issues/759)
477            FromBytes::mut_from_prefix(params.output).unwrap().0, // TODO: zerocopy: mut-from-prefix: use-rest-of-range, err (https://github.com/microsoft/openvmm/issues/759)
478        )
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
506/// A simple variable hypercall.
507pub 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    /// Parses the hypercall parameters to input and output types.
515    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(), //TODO: zerocopy: err (https://github.com/microsoft/openvmm/issues/759)
520            Out::mut_from_prefix(params.output).unwrap().0, //TODO: zerocopy: err (https://github.com/microsoft/openvmm/issues/759)
521        )
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
549/// A rep hypercall.
550pub struct RepHypercall<Hdr, In, Out, const CODE: u16>(PhantomData<(Hdr, In, Out)>);
551
552/// Hypervisor result type for rep hypercalls. These hypercalls have either no or only rep output
553/// data, which is passed separately from the result. The error is an a tuple consisting of an
554/// `HvError` and the number of elements successfully processed prior to the error being returned.
555/// An `Ok` result implies that all input elements were processed successfully.
556pub 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    /// Parses the hypercall parameters to input and output types.
565    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            // TODO: zerocopy: err (https://github.com/microsoft/openvmm/issues/759)
571            &<[In]>::ref_from_bytes(rest).unwrap()[params.control.rep_start()..]
572        };
573        let output = if size_of::<Out>() == 0 {
574            &mut []
575        } else {
576            // TODO: zerocopy: err (https://github.com/microsoft/openvmm/issues/759)
577            &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
618/// A variable rep hypercall.
619pub 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    /// Parses the hypercall parameters to input and output types.
628    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            // TODO: zerocopy: err (https://github.com/microsoft/openvmm/issues/759)
642            &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
684/// A VTL switch hypercall.
685pub 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/// Creates a hypercall dispatcher, where the dispatcher can support any of the
705/// list of provided hypercalls.
706///
707/// ```ignore
708/// hv1_hypercall::dispatcher!(
709///     Self,
710///     &guest_memory,
711///     [
712///         hv1_hypercall::HvPostMessage,
713///         hv1_hypercall::HvSignalEvent,
714///         #[cfg(guest_arch = "x86_64")]
715///         hv1_hypercall::HvX64StartVirtualProcessor,
716///     ],
717/// );
718/// ```
719#[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
736/// Hypercall dispatcher.
737///
738/// Construct with [`dispatcher!`].
739pub 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    /// Dispatches a hypercall.
768    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}