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`.
27pub fn sys_transport_create(dst_silo_val: u64, _config_flags: u64) -> Result<u64, SyscallError> {
28    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
29    let src_silo_id = silo::try_silo_id_for_task(task.id).ok_or(SyscallError::BadHandle)?;
30    let src_silo = SiloId::new(src_silo_id);
31    let dst_silo = SiloId::new(dst_silo_val as u32);
32
33    let config = TransportConfig {
34        min_level: TransportLevel::LockFree,
35        ring_capacity: Some(256),
36        slot_size: Some(2048),
37    };
38
39    let result = TRANSPORT_MANAGER
40        .establish(src_silo, dst_silo, config)
41        .map_err(|_| SyscallError::NotSupported)?;
42
43    let cap = get_capability_manager().create_capability(
44        ResourceType::IpcTransport,
45        result.id.as_u64() as usize,
46        CapPermissions::read_write(),
47    );
48
49    let mut caps = unsafe { (*task.process.capabilities.get()).clone() };
50    let cap_id = caps.insert(cap);
51    unsafe {
52        *task.process.capabilities.get() = caps;
53    }
54
55    Ok(cap_id.as_u64())
56}
57
58/// SYS_TRANSPORT_SEND: send a message on a transport handle.
59pub fn sys_transport_send(handle: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
60    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
61    let caps = unsafe { &*task.process.capabilities.get() };
62
63    let cap = caps
64        .get_with_permissions(CapId::from_raw(handle), CapPermissions::read_write())
65        .ok_or(SyscallError::BadHandle)?;
66
67    if cap.resource_type != ResourceType::IpcTransport {
68        return Err(SyscallError::BadHandle);
69    }
70
71    let len = buf_len as usize;
72    let user_buf = UserSliceRead::new(buf_ptr, len)?;
73    let data = user_buf.read_to_vec();
74
75    let tid = TransportId::from_u64(cap.resource as u64);
76    let endpoint = TRANSPORT_MANAGER
77        .get_endpoint(tid)
78        .ok_or(SyscallError::BadHandle)?;
79
80    endpoint
81        .send(&data)
82        .map_err(|e| transport_to_syscall_err(e))?;
83
84    // Increment sent stats
85    TRANSPORT_MANAGER.stats.lock().sent += 1;
86
87    log::trace!("transport_send: handle={} len={}", handle, len);
88    Ok(len as u64)
89}
90
91/// SYS_TRANSPORT_RECV: receive a message from a transport handle.
92pub fn sys_transport_recv(handle: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
93    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
94    let caps = unsafe { &*task.process.capabilities.get() };
95
96    let cap = caps
97        .get_with_permissions(CapId::from_raw(handle), CapPermissions::read_write())
98        .ok_or(SyscallError::BadHandle)?;
99
100    if cap.resource_type != ResourceType::IpcTransport {
101        return Err(SyscallError::BadHandle);
102    }
103
104    if buf_len == 0 {
105        return Err(SyscallError::Fault);
106    }
107
108    let tid = TransportId::from_u64(cap.resource as u64);
109    let endpoint = TRANSPORT_MANAGER
110        .get_endpoint(tid)
111        .ok_or(SyscallError::BadHandle)?;
112
113    let mut user_buf = UserSliceWrite::new(buf_ptr, buf_len as usize)?;
114    let mut kbuf = vec![0u8; buf_len as usize];
115    let n = endpoint
116        .recv(&mut kbuf)
117        .map_err(|e| transport_to_syscall_err(e))?;
118    user_buf.copy_from(&kbuf[..n]);
119
120    // Increment received stats
121    TRANSPORT_MANAGER.stats.lock().received += 1;
122
123    log::trace!("transport_recv: handle={} n={}", handle, n);
124    Ok(n as u64)
125}
126
127/// SYS_TRANSPORT_CLOSE: close a transport handle and release resources.
128pub fn sys_transport_close(handle: u64) -> Result<u64, SyscallError> {
129    use crate::capability::release_capability;
130
131    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
132    let cap_id = CapId::from_raw(handle);
133    let caps = unsafe { &mut *task.process.capabilities.get() };
134    if let Some(cap) = caps.remove(cap_id) {
135        release_capability(&cap, Some(task.id));
136        log::trace!("transport_close: handle={}", handle);
137        Ok(0)
138    } else {
139        Err(SyscallError::BadHandle)
140    }
141}
142
143/// SYS_TRANSPORT_INFO: query transport level and statistics.
144pub fn sys_transport_info(handle: u64, out_ptr: u64) -> Result<u64, SyscallError> {
145    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
146    let caps = unsafe { &*task.process.capabilities.get() };
147
148    let _cap = caps
149        .get_with_permissions(CapId::from_raw(handle), CapPermissions::read_write())
150        .ok_or(SyscallError::BadHandle)?;
151
152    if out_ptr == 0 {
153        return Err(SyscallError::Fault);
154    }
155
156    let tid = TransportId::from_u64(_cap.resource as u64);
157    let endpoint = TRANSPORT_MANAGER
158        .get_endpoint(tid)
159        .ok_or(SyscallError::BadHandle)?;
160    let level = endpoint.level() as u64;
161    let out = UserSliceWrite::new(out_ptr, 8)?;
162    out.copy_from(&level.to_ne_bytes());
163    Ok(8)
164}
165
166/// Convert an IpcError to a SyscallError.
167fn transport_to_syscall_err(e: IpcError) -> SyscallError {
168    match e {
169        IpcError::WouldBlock => SyscallError::Again,
170        IpcError::Disconnected => SyscallError::Pipe,
171        IpcError::MessageTooLarge => SyscallError::Fault,
172        IpcError::BufferTooSmall => SyscallError::Fault,
173        IpcError::TransportNotFound => SyscallError::BadHandle,
174        IpcError::PermissionDenied => SyscallError::PermissionDenied,
175        IpcError::TransportFailed => SyscallError::IoError,
176    }
177}