vmbus_serial_protocol/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Protocol definitions for a VMBUS based serial device. Today this serial device is only offered to VTL2.
5
6#![forbid(unsafe_code)]
7
8use core::fmt::Debug;
9use guid::Guid;
10use open_enum::open_enum;
11use static_assertions::const_assert_eq;
12use zerocopy::FromBytes;
13use zerocopy::FromZeros;
14use zerocopy::Immutable;
15use zerocopy::IntoBytes;
16use zerocopy::KnownLayout;
17
18/// Maximum message size for all messages.
19pub const MAX_MESSAGE_SIZE: usize = 512;
20
21// {8b60ccf6-709f-4c11-90b5-229c959a9e6a}
22/// VMBUS Interface Type GUID
23pub const UART_INTERFACE_TYPE: Guid = guid::guid!("8b60ccf6-709f-4c11-90b5-229c959a9e6a");
24
25// {700df40e-b947-4776-b839-d1b0a35af034}
26/// VMBUS Instance GUID for COM1
27pub const UART_INTERFACE_INSTANCE_COM1: Guid = guid::guid!("700df40e-b947-4776-b839-d1b0a35af034");
28
29// {7e55f4b8-af84-4e98-9f1a-8e8d0bde3744}
30/// VMBUS Instance GUID for COM2
31pub const UART_INTERFACE_INSTANCE_COM2: Guid = guid::guid!("7e55f4b8-af84-4e98-9f1a-8e8d0bde3744");
32
33// {3f158fa1-b0aa-45e9-ba54-9fc73f6c59ec}
34/// VMBUS Instance GUID for COM3
35pub const UART_INTERFACE_INSTANCE_COM3: Guid = guid::guid!("3f158fa1-b0aa-45e9-ba54-9fc73f6c59ec");
36
37// {8688a06f-9b53-48ce-b408-7581626228c5}
38/// VMBUS Instance GUID for COM4
39pub const UART_INTERFACE_INSTANCE_COM4: Guid = guid::guid!("8688a06f-9b53-48ce-b408-7581626228c5");
40
41const fn make_version(major: u16, minor: u16) -> u32 {
42    (minor as u32) | ((major as u32) << 16)
43}
44
45open_enum! {
46    /// Protocol versions.
47    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
48    pub enum ProtocolVersions: u32 {
49        /// Represents the MANGANESE protocol version.
50        MANGANESE = make_version(1, 0),
51    }
52}
53
54open_enum! {
55    /// Header message versions.
56    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
57    pub enum MessageVersions: u8 {
58        /// Invalid version.
59        INVALID          = 0,
60        /// Version 1
61        HEADER_VERSION_1 = 1,
62    }
63}
64
65open_enum! {
66    /// Enum for the different message types.
67    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
68    pub enum MessageTypes: u8 {
69        /// Invalid message type.
70        INVALID            = 0,
71        /// A [`HostNotifications`](crate::HostNotifications) message.
72        HOST_NOTIFICATION  = 1,
73        /// A [`HostRequests`](crate::HostRequests) message.
74        HOST_REQUEST       = 2,
75        /// A response to a [`HostRequests`](crate::HostRequests) message.
76        HOST_RESPONSE      = 3,
77        /// A [`GuestNotifications`](crate::GuestNotifications) message.
78        GUEST_NOTIFICATION = 4,
79    }
80}
81
82open_enum! {
83    /// Enum for the different host notification messages.
84    /// These are aysynchronous messages sent by the HCL to the Host.
85    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
86    pub enum HostNotifications: u8 {
87        /// Invalid message.
88        INVALID           = 0,
89        /// Clear the associated RX buffer on the host side.
90        RX_CLEAR_BUFFER   = 1,
91        /// TX data from the guest, specified by [`TxDataAvailableMessage`](crate::TxDataAvailableMessage).
92        TX_DATA_AVAILABLE = 2,
93    }
94}
95
96open_enum! {
97    /// Enum for the different guest notification messages.
98    /// These are asynchronous messages sent by the Host to the HCL.
99    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
100    pub enum GuestNotifications: u8 {
101        /// Invalid message.
102        INVALID           = 0,
103        /// RX data available to be requested from the host.
104        RX_DATA_AVAILABLE = 1,
105        /// UART modem status bits have changed.
106        SET_MODEM_STATUS  = 2,
107        /// The TX specified by a [`HostNotifications::TX_DATA_AVAILABLE`] message has finished and another can now
108        /// be sent.
109        TX_COMPLETED      = 3,
110    }
111}
112
113open_enum! {
114    /// Enum for the different host request and response messages.
115    /// These are synchronous messages sent by the HCL to the host.
116    /// Note that the host response shares the same enum.
117    /// (Each request has a response of the same ID)
118    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
119    pub enum HostRequests: u16 {
120        /// Invalid message.
121        INVALID     = 0,
122        /// Negotiate protocol version specified by the [`VersionRequestMessage`] with response
123        /// [`VersionRequestResponse`].
124        VERSION     = 1,
125        /// Get RX data from the host with response [`RxDataResponse`].
126        GET_RX_DATA = 2,
127    }
128}
129
130/// A wrapper type for a protocol message id union.
131///
132/// NOTE: In C/C++, this is represented as a union like the following Rust definition:
133///
134/// ```ignore
135/// #[repr(C)]
136/// union MessageId {
137///     as_u16: u16,
138///     host_notification: HostNotifications,
139///     host_request: HostRequests,
140///     guest_notification: GuestNotifications,
141/// }
142/// ```
143///
144/// However, using unions in Rust requires unsafe code. Instead, since the upper byte is
145/// currently unused for anything, just have a wrapper struct with accessor methods for
146/// individual field types.
147#[repr(transparent)]
148#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
149pub struct MessageId(pub u16);
150
151impl Debug for MessageId {
152    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153        f.debug_struct("MessageId")
154            .field("as_u16", &self.0)
155            .field("host_notification", &self.host_notification())
156            .field("host_request", &self.host_request())
157            .field("guest_notification", &self.guest_notification())
158            .finish()
159    }
160}
161
162impl MessageId {
163    fn guest_notification(&self) -> GuestNotifications {
164        GuestNotifications(self.0 as u8)
165    }
166
167    fn host_request(&self) -> HostRequests {
168        HostRequests(self.0)
169    }
170
171    fn host_notification(&self) -> HostNotifications {
172        HostNotifications(self.0 as u8)
173    }
174
175    fn new_guest_notification(guest_notification: GuestNotifications) -> Self {
176        MessageId(guest_notification.0 as u16)
177    }
178
179    fn new_host_request(host_request: HostRequests) -> Self {
180        MessageId(host_request.0)
181    }
182
183    fn new_host_notification(host_notification: HostNotifications) -> Self {
184        MessageId(host_notification.0 as u16)
185    }
186}
187
188/// A protocol message header.
189#[repr(C)]
190#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
191pub struct Header {
192    /// The message version.
193    pub message_version: MessageVersions,
194    /// The message type.
195    pub message_type: MessageTypes,
196    /// The message id.
197    pub message_id: MessageId,
198}
199
200impl Debug for Header {
201    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
202        let host_notification = self.message_id.host_notification();
203        let host_request = self.message_id.host_request();
204        let guest_notification = self.message_id.guest_notification();
205
206        f.debug_struct("Header")
207            .field("message_version", &self.message_version)
208            .field("message_type", &self.message_type)
209            .field(
210                "message_id",
211                match self.message_type {
212                    MessageTypes::HOST_NOTIFICATION => &host_notification,
213                    MessageTypes::HOST_REQUEST => &host_request,
214                    MessageTypes::HOST_RESPONSE => &host_request,
215                    MessageTypes::GUEST_NOTIFICATION => &guest_notification,
216                    _ => &self.message_id.0,
217                },
218            )
219            .finish()
220    }
221}
222
223impl Default for Header {
224    fn default() -> Self {
225        Self {
226            message_version: MessageVersions::INVALID,
227            message_type: MessageTypes::INVALID,
228            message_id: MessageId::new_zeroed(),
229        }
230    }
231}
232
233impl Header {
234    /// Create a new header for a guest notification message.
235    pub fn new_guest_notification(guest_notification: GuestNotifications) -> Self {
236        Self {
237            message_version: MessageVersions::HEADER_VERSION_1,
238            message_type: MessageTypes::GUEST_NOTIFICATION,
239            message_id: MessageId::new_guest_notification(guest_notification),
240        }
241    }
242
243    /// The associated guest notification message id, if any.
244    pub fn guest_notification(&self) -> Option<GuestNotifications> {
245        if self.message_type == MessageTypes::GUEST_NOTIFICATION {
246            Some(self.message_id.guest_notification())
247        } else {
248            None
249        }
250    }
251
252    /// The associated host response message id, if any.
253    pub fn host_response(&self) -> Option<HostRequests> {
254        // NOTE: Host responses are encoded in the host_request field,
255        //       but have message type HostResponse.
256        if self.message_type == MessageTypes::HOST_RESPONSE {
257            Some(self.message_id.host_request())
258        } else {
259            None
260        }
261    }
262
263    /// Create a new header for a host response message.
264    pub fn new_host_response(host_response: HostRequests) -> Self {
265        Self {
266            message_version: MessageVersions::HEADER_VERSION_1,
267            message_type: MessageTypes::HOST_RESPONSE,
268            message_id: MessageId::new_host_request(host_response),
269        }
270    }
271
272    /// The associated host request message id, if any.
273    pub fn host_request(&self) -> Option<HostRequests> {
274        if self.message_type == MessageTypes::HOST_REQUEST {
275            Some(self.message_id.host_request())
276        } else {
277            None
278        }
279    }
280
281    /// Create a new header for a host request message.
282    pub fn new_host_request(host_request: HostRequests) -> Self {
283        Self {
284            message_version: MessageVersions::HEADER_VERSION_1,
285            message_type: MessageTypes::HOST_REQUEST,
286            message_id: MessageId::new_host_request(host_request),
287        }
288    }
289
290    /// Create a new header for a host notification message.
291    pub fn new_host_notification(host_notification: HostNotifications) -> Self {
292        Self {
293            message_version: MessageVersions::HEADER_VERSION_1,
294            message_type: MessageTypes::HOST_NOTIFICATION,
295            message_id: MessageId::new_host_notification(host_notification),
296        }
297    }
298
299    /// The associated host notification message id, if any.
300    pub fn host_notification(&self) -> Option<HostNotifications> {
301        if self.message_type == MessageTypes::HOST_NOTIFICATION {
302            Some(self.message_id.host_notification())
303        } else {
304            None
305        }
306    }
307}
308
309const_assert_eq!(4, size_of::<Header>());
310
311/// The maximum data size for a TX or RX.
312pub const UART_MSG_MAX_PAYLOAD: usize = 64;
313
314// Each protocol message starts with a header, and has potential additional data.
315// Messages that do not have additional structures present below are header only messages.
316
317// Host Notifications
318
319/// Host notification message that TX data is available.
320#[repr(C)]
321#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
322pub struct TxDataAvailableMessage {
323    /// The message header.
324    pub header: Header,
325    /// The number of bytes valid in buffer.
326    pub buffer_length: u8,
327    /// The TX data buffer.
328    pub buffer: [u8; UART_MSG_MAX_PAYLOAD],
329    /// Padding that must be zero.
330    pub pad: u8,
331}
332
333impl Default for TxDataAvailableMessage {
334    fn default() -> Self {
335        Self {
336            header: Header::default(),
337            buffer_length: 0,
338            buffer: [0; UART_MSG_MAX_PAYLOAD],
339            pad: 0,
340        }
341    }
342}
343
344const_assert_eq!(70, size_of::<TxDataAvailableMessage>());
345
346// Guest Notifications
347
348/// Guest notification that the connection state of the serial port has changed.
349#[repr(C)]
350#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
351pub struct SetModumStatusMessage {
352    /// The message header.
353    pub header: Header,
354    /// 16550-style modem status. Ignored.
355    pub modem_status: u8,
356    /// A boolean indicating if the modem is connected.
357    pub is_connected: u8,
358}
359
360const_assert_eq!(6, size_of::<SetModumStatusMessage>());
361
362// Host Requests and Responses
363
364/// A version negotiation request sent from the guest to host.
365#[repr(C)]
366#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
367pub struct VersionRequestMessage {
368    /// The message header.
369    pub header: Header,
370    /// The requested protocol version to use.
371    pub requested_version: ProtocolVersions,
372}
373
374impl Default for VersionRequestMessage {
375    fn default() -> Self {
376        Self {
377            header: Header::default(),
378            requested_version: ProtocolVersions(0),
379        }
380    }
381}
382
383const_assert_eq!(8, size_of::<VersionRequestMessage>());
384
385/// A version negotiation response sent from host to guest.
386#[repr(C)]
387#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
388pub struct VersionRequestResponse {
389    /// The message header.
390    pub header: Header,
391    /// 1 if the host accepted the version requested. 0 if the version was rejected.
392    pub version_accepted: u8,
393    /// Padding that must be zero.
394    pub pad: u8,
395}
396
397const_assert_eq!(6, size_of::<VersionRequestResponse>());
398
399/// A response to an RX Data host request message that contains RX data.
400#[repr(C)]
401#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
402pub struct RxDataResponse {
403    /// The message header.
404    pub header: Header,
405    /// The number of valid data bytes in buffer.
406    pub buffer_length: u8,
407    /// 1 if more RX data is available on the host. 0 if no further data is available.
408    pub more_data_available: u8,
409    /// The RX data buffer.
410    pub buffer: [u8; UART_MSG_MAX_PAYLOAD],
411}
412
413const_assert_eq!(70, size_of::<RxDataResponse>());
414
415impl Default for RxDataResponse {
416    fn default() -> Self {
417        Self {
418            header: Header::default(),
419            buffer_length: 0,
420            more_data_available: 0,
421            buffer: [0; UART_MSG_MAX_PAYLOAD],
422        }
423    }
424}