hyperlight_common/virtq/msg.rs
1/*
2Copyright 2026 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17//! Wire format header for all virtqueue messages.
18//!
19//! Every payload on both the G2H and H2G queues starts with this
20//! fixed 8-byte header, enabling message type discrimination and
21//! request/response correlation.
22
23use bitflags::bitflags;
24
25/// Message types for the virtqueue wire protocol.
26#[repr(u8)]
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum MsgKind {
29 /// A function call request (FunctionCall payload follows).
30 Request = 0x01,
31 /// A function call response (FunctionCallResult payload follows).
32 Response = 0x02,
33 /// A stream data chunk.
34 StreamChunk = 0x03,
35 /// End-of-stream marker.
36 StreamEnd = 0x04,
37 /// Cancel a pending request.
38 Cancel = 0x05,
39 /// A guest log message (GuestLogData payload follows).
40 Log = 0x06,
41}
42
43impl TryFrom<u8> for MsgKind {
44 type Error = u8;
45
46 fn try_from(value: u8) -> Result<Self, Self::Error> {
47 match value {
48 0x01 => Ok(Self::Request),
49 0x02 => Ok(Self::Response),
50 0x03 => Ok(Self::StreamChunk),
51 0x04 => Ok(Self::StreamEnd),
52 0x05 => Ok(Self::Cancel),
53 0x06 => Ok(Self::Log),
54 other => Err(other),
55 }
56 }
57}
58
59bitflags! {
60 #[repr(transparent)]
61 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
62 pub struct MsgFlags: u8 {
63 /// More descriptors follow for this message.
64 const MORE = 1 << 0;
65 }
66}
67
68/// Wire header for all virtqueue messages
69#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
70#[repr(C)]
71pub struct VirtqMsgHeader {
72 /// Discriminates the message type.
73 pub kind: u8,
74 /// Per-message flags (see [`MsgFlags`]).
75 pub flags: u8,
76 /// Caller-assigned correlation ID. Responses echo the request's ID.
77 pub req_id: u16,
78 /// Byte length of the payload following this header in this descriptor.
79 pub payload_len: u32,
80}
81
82impl VirtqMsgHeader {
83 pub const SIZE: usize = core::mem::size_of::<Self>();
84
85 /// Create a new message header with no flags set.
86 pub const fn new(kind: MsgKind, req_id: u16, payload_len: u32) -> Self {
87 Self {
88 kind: kind as u8,
89 flags: 0,
90 req_id,
91 payload_len,
92 }
93 }
94
95 /// Create a new header with flags.
96 pub const fn with_flags(kind: MsgKind, flags: MsgFlags, req_id: u16, payload_len: u32) -> Self {
97 Self {
98 kind: kind as u8,
99 flags: flags.bits(),
100 req_id,
101 payload_len,
102 }
103 }
104
105 /// Parse the kind field into a [`MsgKind`] enum.
106 pub fn msg_kind(&self) -> Result<MsgKind, u8> {
107 MsgKind::try_from(self.kind)
108 }
109
110 /// Interpret the raw flags field as [`MsgFlags`].
111 pub fn msg_flags(&self) -> MsgFlags {
112 MsgFlags::from_bits_truncate(self.flags)
113 }
114
115 /// Returns true if [`MsgFlags::MORE`] is set, indicating more
116 /// descriptors follow for this message.
117 pub const fn has_more(&self) -> bool {
118 self.flags & MsgFlags::MORE.bits() != 0
119 }
120}