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_ident:ident, $output_ident:ident) => {{
171 mshv_root_hvcall {
172 code: $code_expr as u16,
173 in_sz: std::mem::size_of_val(&$input_ident) as u16,
174 out_sz: std::mem::size_of_val(&$output_ident) as u16,
175 in_ptr: std::ptr::addr_of!($input_ident) as u64,
176 out_ptr: std::ptr::addr_of!($output_ident) 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}