Skip to main content

strat9_kernel/syscall/
transport.rs

1//! Syscall handlers for the IPC transport layer (N1/N2/N3).
2//!
3//! Provides userspace access to the TransportManager:
4//! - `SYS_TRANSPORT_CREATE` (260): create a transport between two silos
5//! - `SYS_TRANSPORT_SEND`   (261): send a message
6//! - `SYS_TRANSPORT_RECV`   (262): receive a message
7//! - `SYS_TRANSPORT_CLOSE`  (263): close a transport
8//! - `SYS_TRANSPORT_INFO`   (264): query transport info
9
10use crate::{
11    capability::{get_capability_manager, CapId, CapPermissions, ResourceType},
12    ipc::transport::{
13        IpcConsumer, IpcError, IpcProducer, IpcTransport, TransportConfig, TransportId,
14        TransportLevel, TransportManager,
15    },
16    memory::{UserSliceRead, UserSliceWrite},
17    process::current_task_clone,
18    silo::{self, SiloId},
19    syscall::error::SyscallError,
20};
21use alloc::vec;
22
23/// Global transport manager instance.
24pub(crate) static TRANSPORT_MANAGER: TransportManager = TransportManager::new();
25
26/// SYS_TRANSPORT_CREATE: create a transport between the caller's silo and `dst_silo`.
27///
28/// `config_flags` low 4 bits encode the desired minimum `TransportLevel`:
29///   1 = TypeSafe, 2 = LockFree, 3 = Mmu.  0 = use default (LockFree).
30/// Bits 8..=23 encode `ring_capacity` (0 = default 256).
31pub fn sys_transport_create(dst_silo_val: u64, config_flags: u64) -> Result<u64, SyscallError> {
32    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
33    let src_silo_id = silo::try_silo_id_for_task(task.id).ok_or(SyscallError::BadHandle)?;
34    let src_silo = SiloId::new(src_silo_id);
35    let dst_silo = SiloId::new(dst_silo_val as u32);
36
37    // Decode config_flags: bits [3:0] = min_level, bits [23:8] = ring_capacity.
38    let level_raw = (config_flags & 0xF) as u8;
39    let min_level = match level_raw {
40        1 => TransportLevel::TypeSafe,
41        2 => TransportLevel::LockFree,
42        3 => TransportLevel::Mmu,
43        _ => TransportLevel::LockFree, // default
44    };
45    let ring_capacity = {
46        let cap = ((config_flags >> 8) & 0xFFFF) as u32;
47        if cap == 0 {
48            256
49        } else {
50            cap
51        }
52    };
53
54    let config = TransportConfig {
55        min_level,
56        ring_capacity: Some(ring_capacity),
57        slot_size: Some(2048),
58    };
59
60    let result = TRANSPORT_MANAGER
61        .establish(src_silo, dst_silo, config)
62        .map_err(|_| SyscallError::NotSupported)?;
63
64    let cap = get_capability_manager().create_capability(
65        ResourceType::IpcTransport,
66        result.id.as_u64() as usize,
67        CapPermissions::read_write(),
68    );
69
70    let mut caps = unsafe { (*task.process.capabilities.get()).clone() };
71    let cap_id = caps.insert(cap);
72    unsafe {
73        *task.process.capabilities.get() = caps;
74    }
75
76    Ok(cap_id.as_u64())
77}
78
79/// SYS_TRANSPORT_SEND: send a message on a transport handle.
80pub fn sys_transport_send(handle: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
81    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
82    let caps = unsafe { &*task.process.capabilities.get() };
83
84    let cap = caps
85        .get_with_permissions(CapId::from_raw(handle), CapPermissions::read_write())
86        .ok_or(SyscallError::BadHandle)?;
87
88    if cap.resource_type != ResourceType::IpcTransport {
89        return Err(SyscallError::BadHandle);
90    }
91
92    let len = buf_len as usize;
93    let user_buf = UserSliceRead::new(buf_ptr, len)?;
94    let data = user_buf.read_to_vec();
95
96    let tid = TransportId::from_u64(cap.resource as u64);
97    let endpoint = TRANSPORT_MANAGER
98        .get_endpoint(tid)
99        .ok_or(SyscallError::BadHandle)?;
100
101    endpoint
102        .send(&data)
103        .map_err(|e| transport_to_syscall_err(e))?;
104
105    // Increment sent stats
106    TRANSPORT_MANAGER.stats.lock().sent += 1;
107
108    log::trace!("transport_send: handle={} len={}", handle, len);
109    Ok(len as u64)
110}
111
112/// SYS_TRANSPORT_RECV: receive a message from a transport handle.
113pub fn sys_transport_recv(handle: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
114    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
115    let caps = unsafe { &*task.process.capabilities.get() };
116
117    let cap = caps
118        .get_with_permissions(CapId::from_raw(handle), CapPermissions::read_write())
119        .ok_or(SyscallError::BadHandle)?;
120
121    if cap.resource_type != ResourceType::IpcTransport {
122        return Err(SyscallError::BadHandle);
123    }
124
125    if buf_len == 0 {
126        return Err(SyscallError::Fault);
127    }
128
129    let tid = TransportId::from_u64(cap.resource as u64);
130    let endpoint = TRANSPORT_MANAGER
131        .get_endpoint(tid)
132        .ok_or(SyscallError::BadHandle)?;
133
134    let user_buf = UserSliceWrite::new(buf_ptr, buf_len as usize)?;
135    let mut kbuf = vec![0u8; buf_len as usize];
136    let n = endpoint
137        .recv(&mut kbuf)
138        .map_err(|e| transport_to_syscall_err(e))?;
139    user_buf.copy_from(&kbuf[..n]);
140
141    // Increment received stats
142    TRANSPORT_MANAGER.stats.lock().received += 1;
143
144    log::trace!("transport_recv: handle={} n={}", handle, n);
145    Ok(n as u64)
146}
147
148/// SYS_TRANSPORT_CLOSE: close a transport handle and release resources.
149pub fn sys_transport_close(handle: u64) -> Result<u64, SyscallError> {
150    use crate::capability::release_capability;
151
152    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
153    let cap_id = CapId::from_raw(handle);
154    let caps = unsafe { &mut *task.process.capabilities.get() };
155    if let Some(cap) = caps.remove(cap_id) {
156        release_capability(&cap, Some(task.id));
157        log::trace!("transport_close: handle={}", handle);
158        Ok(0)
159    } else {
160        Err(SyscallError::BadHandle)
161    }
162}
163
164/// SYS_TRANSPORT_INFO: query transport level and statistics.
165pub fn sys_transport_info(handle: u64, out_ptr: u64) -> Result<u64, SyscallError> {
166    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
167    let caps = unsafe { &*task.process.capabilities.get() };
168
169    let _cap = caps
170        .get_with_permissions(CapId::from_raw(handle), CapPermissions::read_write())
171        .ok_or(SyscallError::BadHandle)?;
172
173    if out_ptr == 0 {
174        return Err(SyscallError::Fault);
175    }
176
177    let tid = TransportId::from_u64(_cap.resource as u64);
178    let endpoint = TRANSPORT_MANAGER
179        .get_endpoint(tid)
180        .ok_or(SyscallError::BadHandle)?;
181    let level = endpoint.level() as u64;
182    let out = UserSliceWrite::new(out_ptr, 8)?;
183    out.copy_from(&level.to_ne_bytes());
184    Ok(8)
185}
186
187/// Convert an IpcError to a SyscallError.
188fn transport_to_syscall_err(e: IpcError) -> SyscallError {
189    match e {
190        IpcError::WouldBlock => SyscallError::Again,
191        IpcError::Disconnected => SyscallError::Pipe,
192        IpcError::MessageTooLarge => SyscallError::Fault,
193        IpcError::BufferTooSmall => SyscallError::Fault,
194        IpcError::TransportNotFound => SyscallError::BadHandle,
195        IpcError::PermissionDenied => SyscallError::PermissionDenied,
196        IpcError::TransportFailed => SyscallError::IoError,
197        IpcError::InvalidRip => SyscallError::Fault,
198        IpcError::TimedOut => SyscallError::TimedOut,
199    }
200}