Skip to main content

strat9_kernel/syscall/
chan.rs

1//! Typed MPMC sync-channel syscall handlers.
2//!
3//! Provides bounded multi-producer, multi-consumer channels for
4//! inter-process message passing (IPC-02).
5
6use super::error::SyscallError;
7use crate::{
8    capability::{CapId, CapPermissions, ResourceType},
9    ipc::{
10        channel::{self, ChanId},
11        message::IpcMessage,
12    },
13    memory::{UserSliceRead, UserSliceWrite},
14    process::current_task_clone,
15};
16
17const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
18
19/// SYS_CHAN_CREATE (220): create a bounded sync-channel.
20pub fn sys_chan_create(capacity: u64) -> Result<u64, SyscallError> {
21    let cap = capacity.clamp(1, 1024) as usize;
22    let chan_id = channel::create_channel(cap);
23
24    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
25    let caps = unsafe { &mut *task.process.capabilities.get() };
26    let cap_id = caps.insert(crate::capability::Capability {
27        id: crate::capability::CapId::new(),
28        permissions: crate::capability::CapPermissions {
29            read: true,
30            write: true,
31            execute: false,
32            grant: true,
33            revoke: false,
34        },
35        resource_type: ResourceType::Channel,
36        resource: chan_id.as_u64() as usize,
37    });
38
39    log::debug!(
40        "syscall: CHAN_CREATE(cap={}) → chan={} handle={}",
41        cap,
42        chan_id,
43        cap_id.as_u64()
44    );
45    Ok(cap_id.as_u64())
46}
47
48/// SYS_CHAN_SEND (221): send one `IpcMessage` to a channel, blocking if full.
49pub fn sys_chan_send(handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
50    crate::silo::enforce_cap_for_current_task(handle)?;
51
52    let user_slice = UserSliceRead::new(msg_ptr, MSG_SIZE).map_err(SyscallError::from)?;
53    let mut msg = IpcMessage::new(0);
54    let n = user_slice.copy_to(unsafe {
55        core::slice::from_raw_parts_mut(&mut msg as *mut IpcMessage as *mut u8, MSG_SIZE)
56    });
57    if n != MSG_SIZE {
58        return Err(SyscallError::Fault);
59    }
60
61    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
62    msg.sender = task.id.as_u64();
63
64    let caps = unsafe { &*task.process.capabilities.get() };
65    let cap = caps
66        .get(CapId::from_raw(handle))
67        .ok_or(SyscallError::BadHandle)?;
68    if cap.resource_type != ResourceType::Channel || !cap.permissions.write {
69        return Err(SyscallError::PermissionDenied);
70    }
71    let chan_id = ChanId::from_u64(cap.resource as u64);
72
73    let chan = channel::get_channel(chan_id).ok_or(SyscallError::BadHandle)?;
74    chan.send(msg).map_err(SyscallError::from)?;
75
76    Ok(0)
77}
78
79/// SYS_CHAN_RECV (222): receive one `IpcMessage`, blocking if empty.
80pub fn sys_chan_recv(handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
81    crate::silo::enforce_cap_for_current_task(handle)?;
82
83    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
84    let caps = unsafe { &*task.process.capabilities.get() };
85    let cap = caps
86        .get(CapId::from_raw(handle))
87        .ok_or(SyscallError::BadHandle)?;
88    if cap.resource_type != ResourceType::Channel || !cap.permissions.read {
89        return Err(SyscallError::PermissionDenied);
90    }
91    let chan_id = ChanId::from_u64(cap.resource as u64);
92
93    let chan = channel::get_channel(chan_id).ok_or(SyscallError::BadHandle)?;
94    let msg = chan.recv().map_err(SyscallError::from)?;
95
96    let user_slice = UserSliceWrite::new(msg_ptr, MSG_SIZE).map_err(SyscallError::from)?;
97    let n = user_slice.copy_from(unsafe {
98        core::slice::from_raw_parts(&msg as *const IpcMessage as *const u8, MSG_SIZE)
99    });
100    if n != MSG_SIZE {
101        return Err(SyscallError::Fault);
102    }
103
104    Ok(0)
105}
106
107/// SYS_CHAN_TRY_RECV (223): non-blocking receive.
108pub fn sys_chan_try_recv(handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
109    crate::silo::enforce_cap_for_current_task(handle)?;
110
111    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
112    let caps = unsafe { &*task.process.capabilities.get() };
113    let cap = caps
114        .get(CapId::from_raw(handle))
115        .ok_or(SyscallError::BadHandle)?;
116    if cap.resource_type != ResourceType::Channel || !cap.permissions.read {
117        return Err(SyscallError::PermissionDenied);
118    }
119    let chan_id = ChanId::from_u64(cap.resource as u64);
120
121    let chan = channel::get_channel(chan_id).ok_or(SyscallError::BadHandle)?;
122    match chan.try_recv() {
123        Ok(msg) => {
124            let user_slice = UserSliceWrite::new(msg_ptr, MSG_SIZE).map_err(SyscallError::from)?;
125            let n = user_slice.copy_from(unsafe {
126                core::slice::from_raw_parts(&msg as *const IpcMessage as *const u8, MSG_SIZE)
127            });
128            if n != MSG_SIZE {
129                return Err(SyscallError::Fault);
130            }
131            Ok(0)
132        }
133        Err(e) => Err(SyscallError::from(e)),
134    }
135}
136
137/// SYS_CHAN_CLOSE (224): destroy a channel and wake all waiters.
138pub fn sys_chan_close(handle: u64) -> Result<u64, SyscallError> {
139    crate::silo::enforce_cap_for_current_task(handle)?;
140
141    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
142    let caps = unsafe { &mut *task.process.capabilities.get() };
143    let cap = caps
144        .get(CapId::from_raw(handle))
145        .ok_or(SyscallError::BadHandle)?;
146    if cap.resource_type != ResourceType::Channel {
147        return Err(SyscallError::BadHandle);
148    }
149    let chan_id = ChanId::from_u64(cap.resource as u64);
150    let cap = caps
151        .remove(CapId::from_raw(handle))
152        .ok_or(SyscallError::BadHandle)?;
153    debug_assert_eq!(cap.resource_type, ResourceType::Channel);
154    crate::capability::release_capability(&cap, Some(task.id));
155
156    log::debug!("syscall: CHAN_CLOSE(handle={}) → chan={}", handle, chan_id);
157    Ok(0)
158}