mshv_bindings/
hvcall.rs

1// Copyright © 2024, Microsoft Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4//
5
6use crate::bindings::*;
7use std::mem::size_of;
8use std::vec::Vec;
9
10/// This file contains helper functions for the MSHV_ROOT_HVCALL ioctl.
11/// MSHV_ROOT_HVCALL is basically a 'passthrough' hypercall. The kernel makes a
12/// hypercall on behalf of the VMM without interpreting the arguments or result
13/// or changing any state in the kernel.
14///
15/// RepInput<T> wraps a buffer containing the input for a "rep"[1] hypercall.
16/// Rep hypercalls have rep-eated data, i.e. a variable length array as part of
17/// the input structure e.g.:
18/// ```
19/// use mshv_bindings::bindings::*;
20/// #[repr(C, packed)]
21/// struct hv_input_foo {
22///    some_field: __u64,
23///    variable_array_field: __IncompleteArrayField<__u64>,
24/// }
25/// ```
26/// The struct cannot be used as-is because it can't store anything in the
27/// __IncompleteArrayField<T> field.
28///
29/// RepInput<T> abstracts a rep hypercall input by wrapping a Vec<T>, where T
30/// is the hv_input_* struct type. The buffer backing the Vec<T> has enough
31/// space to store both the hv_input_* struct (at index 0), and the rep data
32/// immediately following it.
33///
34/// Note also that the length of the variable length array field is not stored in
35/// this struct. Rather, it is passed to the hypercall in the 'rep count' field
36/// of the hypercall args (mshv_root_hvcall.reps). RepInput<T> stores this count,
37/// along with the size of the entire input data.
38///
39/// RepInput<T> is intended to be created with make_rep_input!() and used with
40/// make_rep_args!() below.
41///
42/// [1] HyperV TLFS describing the hypercall interface and rep hypercalls:
43///   https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/hypercall-interface
44///
45pub struct RepInput<T> {
46    vec: Vec<T>,
47    size: usize,
48    rep_count: usize,
49}
50
51impl<T: Default> RepInput<T> {
52    /// Create a RepInput<T> for a rep hypercall
53    ///
54    /// # Arguments
55    ///
56    /// * `vec` - Vec<T> from input_with_arr_field_as_vec(). T is hv_input_* struct
57    /// * `size` - Size of the hypercall input, including the rep data
58    /// * `rep_count` - number of reps
59    pub fn new(vec: Vec<T>, size: usize, rep_count: usize) -> Self {
60        Self {
61            vec,
62            size,
63            rep_count,
64        }
65    }
66    pub fn as_mut_struct_ref(&mut self) -> &mut T {
67        &mut self.vec[0]
68    }
69    pub fn as_struct_ptr(&self) -> *const T {
70        &self.vec[0]
71    }
72    pub fn rep_count(&self) -> usize {
73        self.rep_count
74    }
75    pub fn size(&self) -> usize {
76        self.size
77    }
78    /// Make `Vec<T>` with at least enough space for `count` entries of
79    /// `entry_size`, plus one additional entry
80    /// Populate the first element of the Vec with the T. The rest will hold
81    /// elements of size `entry_size` (note the Vec cannot be used normally to
82    /// modify these since size_of::<T>() isn't necessarily the same as entry_size)
83    pub fn input_with_arr_field_as_vec(t: T, entry_size: usize, count: usize) -> Vec<T> {
84        let element_space = count * entry_size;
85        let vec_size_bytes = size_of::<T>() + element_space;
86        let rounded_size = vec_size_bytes.div_ceil(size_of::<T>());
87        let mut v = Vec::with_capacity(rounded_size);
88        v.resize_with(rounded_size, T::default);
89        v[0] = t;
90        v
91    }
92}
93
94impl<U> __IncompleteArrayField<U> {
95    /// Utility for casting __IncompleteArrayField<T> as the interior type
96    pub fn as_entry_ptr_mut(ptr: *mut __IncompleteArrayField<U>) -> *mut U {
97        ptr as *mut U
98    }
99    /// Utility for getting the size of the interior type
100    /// Note we must use a raw pointer rather than a reference here, because the
101    /// compiler thinks the field itself may be unaligned due to the struct being packed
102    pub fn entry_size(_: *const __IncompleteArrayField<U>) -> usize {
103        size_of::<U>()
104    }
105}
106
107/// Assemble a RepInput<T> from a hypercall input struct and an array of rep data
108/// Arguments:
109///     1. The hv_input_* struct with the input data
110///     2. Name of the __IncompleteArrayField<T> in the struct
111///     3. An array or slice containing the rep data
112#[macro_export]
113macro_rules! make_rep_input {
114    ($struct_expr:expr, $field_ident:ident, $arr_expr:expr) => {{
115        let s = $struct_expr;
116        let a = $arr_expr;
117        let el_size = __IncompleteArrayField::entry_size(std::ptr::addr_of!(s.$field_ident));
118        let struct_size = std::mem::size_of_val(&s);
119        let mut vec = RepInput::input_with_arr_field_as_vec(s, el_size, a.len());
120        let ptr =
121            __IncompleteArrayField::as_entry_ptr_mut(std::ptr::addr_of_mut!(vec[0].$field_ident));
122        for (i, el) in a.iter().enumerate() {
123            // SAFETY: we know the vector is large enough to hold the data
124            unsafe {
125                let mut p = ptr.add(i);
126                p.write_unaligned(*el);
127            };
128        }
129        RepInput::new(vec, struct_size + el_size * a.len(), a.len())
130    }};
131}
132
133/// Create a mshv_root_hvcall populated with rep hypercall parameters
134/// Arguments:
135///     1. hypercall code
136///     2. RepInput<T> structure, where T is hv_input_*. See make_rep_input!()
137///     3. Slice of the correct type for output data (optional)
138#[macro_export]
139macro_rules! make_rep_args {
140    ($code_expr:expr, $input_ident:ident, $output_slice_ident:ident) => {{
141        mshv_root_hvcall {
142            code: $code_expr as u16,
143            reps: $input_ident.rep_count() as u16,
144            in_sz: $input_ident.size() as u16,
145            out_sz: (std::mem::size_of_val(&$output_slice_ident[0]) * $output_slice_ident.len())
146                as u16,
147            in_ptr: $input_ident.as_struct_ptr() as u64,
148            out_ptr: std::ptr::addr_of_mut!($output_slice_ident[0]) as u64,
149            ..Default::default()
150        }
151    }};
152    ($code_expr:expr, $input_ident:ident) => {{
153        mshv_root_hvcall {
154            code: $code_expr as u16,
155            reps: $input_ident.rep_count() as u16,
156            in_sz: $input_ident.size() as u16,
157            in_ptr: $input_ident.as_struct_ptr() as u64,
158            ..Default::default()
159        }
160    }};
161}
162
163/// Create a mshv_root_hvcall populated with hypercall parameters
164/// Arguments:
165///     1. hypercall code
166///     2. hv_input_* structure
167///     3. hv_output_* structure (optional)
168#[macro_export]
169macro_rules! make_args {
170    ($code_expr:expr, $input_expr:expr, $output_expr:expr) => {{
171        mshv_root_hvcall {
172            code: $code_expr as u16,
173            in_sz: std::mem::size_of_val(&($input_expr)) as u16,
174            out_sz: std::mem::size_of_val(&($output_expr)) as u16,
175            in_ptr: std::ptr::addr_of!($input_expr) as u64,
176            out_ptr: std::ptr::addr_of_mut!($output_expr) as u64,
177            ..Default::default()
178        }
179    }};
180    ($code_expr:expr, $input_ident:ident) => {{
181        mshv_root_hvcall {
182            code: $code_expr as u16,
183            in_sz: std::mem::size_of_val(&$input_ident) as u16,
184            in_ptr: std::ptr::addr_of!($input_ident) as u64,
185            ..Default::default()
186        }
187    }};
188}