⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/ipc/
message.rs

1//! IPC message format definitions
2//!
3//! This module defines the message structures used for IPC communication.
4//! Small messages (≤64 bytes) are passed via registers for optimal performance,
5//! while large messages use shared memory for zero-copy transfers.
6
7// Core IPC message types
8
9use core::mem::size_of;
10
11/// Maximum size for register-based small messages
12pub const SMALL_MESSAGE_MAX_SIZE: usize = 64;
13
14/// Number of data registers available for small messages
15pub const DATA_REGISTERS: usize = 4;
16
17/// Small message for register-based transfers (≤64 bytes)
18///
19/// This structure is optimized for fast register-based IPC where the entire
20/// message can be passed in CPU registers without memory access.
21#[repr(C)]
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct SmallMessage {
24    /// Capability token for the operation
25    pub capability: u64,
26    /// Message type/operation code
27    pub opcode: u32,
28    /// Message flags
29    pub flags: u32,
30    /// Payload (up to 4 registers)
31    pub data: [u64; DATA_REGISTERS],
32}
33
34impl SmallMessage {
35    /// Create a new small message
36    pub const fn new(capability: u64, opcode: u32) -> Self {
37        Self {
38            capability,
39            opcode,
40            flags: 0,
41            data: [0; DATA_REGISTERS],
42        }
43    }
44
45    /// Set message flags
46    pub fn with_flags(mut self, flags: u32) -> Self {
47        self.flags = flags;
48        self
49    }
50
51    /// Set data at specific index
52    pub fn with_data(mut self, index: usize, value: u64) -> Self {
53        if index < DATA_REGISTERS {
54            self.data[index] = value;
55        }
56        self
57    }
58
59    /// Get the total size of the message
60    pub const fn size() -> usize {
61        size_of::<Self>()
62    }
63}
64
65/// Message header for large messages
66#[repr(C)]
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct MessageHeader {
69    /// Capability token
70    pub capability: u64,
71    /// Operation code
72    pub opcode: u32,
73    /// Message flags
74    pub flags: u32,
75    /// Total size of the message including payload
76    pub total_size: u64,
77    /// Optional checksum for integrity
78    pub checksum: u32,
79    /// Reserved for alignment
80    _reserved: u32,
81}
82
83impl MessageHeader {
84    /// Create a new message header
85    pub const fn new(capability: u64, opcode: u32, total_size: u64) -> Self {
86        Self {
87            capability,
88            opcode,
89            flags: 0,
90            total_size,
91            checksum: 0,
92            _reserved: 0,
93        }
94    }
95}
96
97/// Memory region descriptor for shared memory transfers
98#[repr(C)]
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct MemoryRegion {
101    /// Base virtual address
102    pub base_addr: u64,
103    /// Size of the region in bytes
104    pub size: u64,
105    /// Memory permissions (read/write/execute)
106    pub permissions: u32,
107    /// Cache policy (write-back, write-through, uncached)
108    pub cache_policy: u32,
109}
110
111impl MemoryRegion {
112    /// Create a new memory region descriptor
113    pub const fn new(base_addr: u64, size: u64) -> Self {
114        Self {
115            base_addr,
116            size,
117            permissions: 0,
118            cache_policy: 0,
119        }
120    }
121
122    /// Set permissions for the region
123    pub fn with_permissions(mut self, permissions: u32) -> Self {
124        self.permissions = permissions;
125        self
126    }
127
128    /// Set cache policy for the region
129    pub fn with_cache_policy(mut self, policy: u32) -> Self {
130        self.cache_policy = policy;
131        self
132    }
133}
134
135/// Large message for memory-based transfers
136///
137/// Used when message size exceeds register capacity or when zero-copy
138/// semantics are required for performance.
139#[repr(C)]
140#[derive(Debug, Clone, Copy)]
141pub struct LargeMessage {
142    /// Message header with metadata
143    pub header: MessageHeader,
144    /// Shared memory region descriptor
145    pub memory_region: MemoryRegion,
146    /// Optional inline data for hybrid transfers
147    pub inline_data: [u8; SMALL_MESSAGE_MAX_SIZE],
148}
149
150impl LargeMessage {
151    /// Create a new large message
152    pub fn new(capability: u64, opcode: u32, region: MemoryRegion) -> Self {
153        let total_size = region.size + size_of::<Self>() as u64;
154        Self {
155            header: MessageHeader::new(capability, opcode, total_size),
156            memory_region: region,
157            inline_data: [0; SMALL_MESSAGE_MAX_SIZE],
158        }
159    }
160
161    /// Set inline data for hybrid transfers
162    pub fn with_inline_data(mut self, data: &[u8]) -> Self {
163        let len = data.len().min(SMALL_MESSAGE_MAX_SIZE);
164        self.inline_data[..len].copy_from_slice(&data[..len]);
165        self
166    }
167}
168
169/// Unified message type that can represent both small and large messages
170#[derive(Debug, Clone, Copy)]
171pub enum Message {
172    /// Small register-based message
173    Small(SmallMessage),
174    /// Large memory-based message
175    Large(LargeMessage),
176}
177
178impl Message {
179    /// Create a small message
180    pub const fn small(capability: u64, opcode: u32) -> Self {
181        Message::Small(SmallMessage::new(capability, opcode))
182    }
183
184    /// Create a large message
185    pub fn large(capability: u64, opcode: u32, region: MemoryRegion) -> Self {
186        Message::Large(LargeMessage::new(capability, opcode, region))
187    }
188
189    /// Get the capability token from the message
190    pub fn capability(&self) -> u64 {
191        match self {
192            Message::Small(msg) => msg.capability,
193            Message::Large(msg) => msg.header.capability,
194        }
195    }
196
197    /// Get the operation code from the message
198    pub fn opcode(&self) -> u32 {
199        match self {
200            Message::Small(msg) => msg.opcode,
201            Message::Large(msg) => msg.header.opcode,
202        }
203    }
204
205    /// Get message flags
206    pub fn flags(&self) -> u32 {
207        match self {
208            Message::Small(msg) => msg.flags,
209            Message::Large(msg) => msg.header.flags,
210        }
211    }
212
213    /// Set message flags
214    pub fn set_flags(&mut self, flags: u32) {
215        match self {
216            Message::Small(msg) => msg.flags = flags,
217            Message::Large(msg) => msg.header.flags = flags,
218        }
219    }
220}
221
222/// Message flags
223pub mod flags {
224    /// Message requires immediate delivery
225    pub const URGENT: u32 = 1 << 0;
226    /// Message can be delivered out of order
227    pub const UNORDERED: u32 = 1 << 1;
228    /// Message requires acknowledgment
229    pub const NEEDS_ACK: u32 = 1 << 2;
230    /// Message is a reply to a previous message
231    pub const IS_REPLY: u32 = 1 << 3;
232    /// Message contains capability transfer
233    pub const HAS_CAPABILITY: u32 = 1 << 4;
234}
235
236/// Memory permissions
237pub mod permissions {
238    /// Region is readable
239    pub const READ: u32 = 1 << 0;
240    /// Region is writable
241    pub const WRITE: u32 = 1 << 1;
242    /// Region is executable
243    pub const EXECUTE: u32 = 1 << 2;
244    /// Region is shared between processes
245    pub const SHARED: u32 = 1 << 3;
246}
247
248/// Cache policies
249pub mod cache_policy {
250    /// Write-back caching (default)
251    pub const WRITE_BACK: u32 = 0;
252    /// Write-through caching
253    pub const WRITE_THROUGH: u32 = 1;
254    /// Uncached (device memory)
255    pub const UNCACHED: u32 = 2;
256    /// Write-combining (for framebuffers)
257    pub const WRITE_COMBINING: u32 = 3;
258}
259
260#[cfg(all(test, not(target_os = "none")))]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn test_small_message_size() {
266        assert_eq!(SmallMessage::size(), 48); // 8 + 4 + 4 + (8 * 4)
267    }
268
269    #[test]
270    fn test_message_creation() {
271        let small = Message::small(0x1234, 42);
272        assert_eq!(small.capability(), 0x1234);
273        assert_eq!(small.opcode(), 42);
274
275        let region = MemoryRegion::new(0x1000, 4096);
276        let large = Message::large(0x5678, 84, region);
277        assert_eq!(large.capability(), 0x5678);
278        assert_eq!(large.opcode(), 84);
279    }
280}