Skip to main content

hcl/
vmbus.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Support for the `/dev/mshv_sint` device.
5
6use crate::ioctl::HypercallError;
7use crate::ioctl::IoctlError;
8use std::fs::File;
9use std::os::unix::prelude::*;
10
11mod ioctl {
12    use nix::ioctl_write_ptr;
13    use std::os::unix::prelude::*;
14
15    const MSHV_IOCTL: u8 = 0xb8;
16    const MSHV_SINT_SIGNAL_EVENT: u16 = 0x22;
17    const MSHV_SINT_POST_MESSAGE: u16 = 0x23;
18    const MSHV_SINT_SET_EVENTFD: u16 = 0x24;
19    const MSHV_SINT_PAUSE_MESSAGE_STREAM: u16 = 0x25;
20
21    #[repr(C)]
22    #[derive(Copy, Clone, Debug)]
23    pub struct hcl_post_message {
24        pub message_type: u64,
25        pub connection_id: u32,
26        pub payload_size: u32,
27        pub payload: *const u8,
28    }
29
30    #[repr(C)]
31    #[derive(Copy, Clone, Debug)]
32    pub struct hcl_signal_event {
33        pub connection_id: u32,
34        pub flag: u32,
35    }
36
37    #[repr(C)]
38    #[derive(Copy, Clone, Debug)]
39    pub struct hcl_set_eventfd {
40        pub fd: RawFd,
41        pub flag: u32,
42    }
43
44    #[repr(C)]
45    #[derive(Copy, Clone, Debug, Default)]
46    pub struct hcl_pause_message_stream {
47        pub pause: u8,
48        pub _reserved: [u8; 7],
49    }
50
51    ioctl_write_ptr!(
52        hcl_post_message,
53        MSHV_IOCTL,
54        MSHV_SINT_POST_MESSAGE,
55        hcl_post_message
56    );
57
58    ioctl_write_ptr!(
59        hcl_signal_event,
60        MSHV_IOCTL,
61        MSHV_SINT_SIGNAL_EVENT,
62        hcl_signal_event
63    );
64
65    ioctl_write_ptr!(
66        hcl_set_eventfd,
67        MSHV_IOCTL,
68        MSHV_SINT_SET_EVENTFD,
69        hcl_set_eventfd
70    );
71
72    ioctl_write_ptr!(
73        hcl_pause_message_stream,
74        MSHV_IOCTL,
75        MSHV_SINT_PAUSE_MESSAGE_STREAM,
76        hcl_pause_message_stream
77    );
78}
79
80/// Device used to interact with a synic sint.
81pub struct HclVmbus {
82    file: File,
83}
84
85impl HclVmbus {
86    /// Opens a new instance.
87    pub fn new() -> std::io::Result<Self> {
88        let file = std::fs::OpenOptions::new()
89            .read(true)
90            .write(true)
91            .open("/dev/mshv_sint")?;
92
93        Ok(Self { file })
94    }
95
96    /// Returns the backing file.
97    pub fn into_inner(self) -> File {
98        self.file
99    }
100
101    /// Attempts to post a message to a given connection ID using the HvPostMessage hypercall.
102    pub fn post_message(
103        &self,
104        connection_id: u32,
105        message_type: u64,
106        message: &[u8],
107    ) -> Result<(), HypercallError> {
108        tracing::trace!(connection_id, "posting message");
109
110        let post_message = ioctl::hcl_post_message {
111            message_type,
112            connection_id,
113            payload_size: message.len() as u32,
114            payload: message.as_ptr(),
115        };
116
117        // SAFETY: calling IOCTL as documented, with no special requirements.
118        let result = unsafe { ioctl::hcl_post_message(self.file.as_raw_fd(), &post_message) };
119        HypercallError::check(result)
120    }
121
122    /// Attempts to signal a given event connection ID using the HvSignalEvent hypercall.
123    pub fn signal_event(&self, connection_id: u32, flag: u32) -> Result<(), HypercallError> {
124        tracing::trace!(connection_id, flag, "signaling event");
125
126        let signal_event = ioctl::hcl_signal_event {
127            connection_id,
128            flag,
129        };
130
131        // SAFETY: calling IOCTL as documented, with no special requirements.
132        let result = unsafe { ioctl::hcl_signal_event(self.file.as_raw_fd(), &signal_event) };
133        HypercallError::check(result)
134    }
135
136    /// Sets an eventfd to be signaled when event `flag` is signaled by the
137    /// hypervisor on SINT 7.
138    pub fn set_eventfd(&self, flag: u32, event: Option<BorrowedFd<'_>>) -> Result<(), IoctlError> {
139        tracing::trace!(flag, ?event, "setting event fd");
140
141        let set_eventfd = ioctl::hcl_set_eventfd {
142            flag,
143            fd: event.map_or(-1, |e| e.as_raw_fd()),
144        };
145
146        // SAFETY: Event is either None or a valid and open fd.
147        unsafe { ioctl::hcl_set_eventfd(self.file.as_raw_fd(), &set_eventfd).map_err(IoctlError) }?;
148        Ok(())
149    }
150
151    /// Indicate whether new messages should be accepted from the host.
152    ///
153    /// The primary purpose of this is to prevent new messages from arriving when saving.
154    ///
155    /// When paused, the SINT will be masked, preventing the host from sending new messages. Reading
156    /// from the device will return messages already in the slot, and then return EOF once all
157    /// messages are cleared.
158    ///
159    /// When resumed, the SINT is unmasked and reading from the message slot will block until new
160    /// messages arrive.
161    pub fn pause_message_stream(&self, pause: bool) -> Result<(), IoctlError> {
162        tracing::trace!(?pause, "pausing message stream");
163
164        let pause_message_stream = ioctl::hcl_pause_message_stream {
165            pause: pause.into(),
166            _reserved: [0; 7],
167        };
168
169        // SAFETY: ioctl has no prerequisites.
170        unsafe {
171            ioctl::hcl_pause_message_stream(self.file.as_raw_fd(), &pause_message_stream)
172                .map_err(IoctlError)?;
173        }
174
175        Ok(())
176    }
177}