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