1use super::error::SyscallError;
7use crate::{
8 capability::{get_capability_manager, CapId, CapPermissions, ResourceType},
9 ipc::{
10 message::IpcMessage,
11 port::{self, PortId},
12 reply,
13 },
14 memory::{UserSliceRead, UserSliceWrite},
15 process::current_task_clone,
16 silo,
17};
18
19pub fn sys_ipc_create_port(_flags: u64) -> Result<u64, SyscallError> {
21 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
22 let port_id = port::create_port(task.id);
23 let cap = get_capability_manager().create_capability(
24 ResourceType::IpcPort,
25 port_id.as_u64() as usize,
26 CapPermissions::all(),
27 );
28 let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
29 Ok(cap_id.as_u64())
30}
31
32pub fn sys_ipc_send(port_handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
34 silo::enforce_cap_for_current_task(port_handle)?;
35 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
36 let caps = unsafe { &*task.process.capabilities.get() };
37 let required = CapPermissions {
38 read: false,
39 write: true,
40 execute: false,
41 grant: false,
42 revoke: false,
43 };
44 let cap = caps
45 .get_with_permissions(CapId::from_raw(port_handle), required)
46 .ok_or(SyscallError::PermissionDenied)?;
47 if cap.resource_type != ResourceType::IpcPort {
48 return Err(SyscallError::BadHandle);
49 }
50
51 const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
52 let user = UserSliceRead::new(msg_ptr, MSG_SIZE)?;
53 let mut buf = [0u8; MSG_SIZE];
54 user.copy_to(&mut buf);
55 let mut msg = crate::ipc::message::ipc_message_from_raw(&buf);
56
57 msg.sender = task.id.as_u64();
59
60 if msg.flags == 0 {
61 if let Some((sid, _label, _mem_used, _mem_min, _mem_max)) =
62 silo::silo_info_for_task(task.id)
63 {
64 if let Some(snapshot) = silo::list_silos_snapshot()
65 .into_iter()
66 .find(|s| s.id == sid)
67 {
68 let structured_label = crate::ipc::message::IpcLabel {
69 tier: snapshot.tier as u8,
70 family: 5,
71 compartment: sid as u16,
72 };
73 msg.flags = unsafe { core::mem::transmute(structured_label) };
74 }
75 }
76 }
77
78 let port_id = PortId::from_u64(cap.resource as u64);
79 let port_obj = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
80 port_obj.send(msg).map_err(SyscallError::from)?;
81 Ok(0)
82}
83
84pub fn sys_ipc_recv(port_handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
86 silo::enforce_cap_for_current_task(port_handle)?;
87 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
88 let caps = unsafe { &*task.process.capabilities.get() };
89 let required = CapPermissions {
90 read: true,
91 write: false,
92 execute: false,
93 grant: false,
94 revoke: false,
95 };
96 let cap = caps
97 .get_with_permissions(CapId::from_raw(port_handle), required)
98 .ok_or(SyscallError::PermissionDenied)?;
99 if cap.resource_type != ResourceType::IpcPort {
100 return Err(SyscallError::BadHandle);
101 }
102
103 let port_id = PortId::from_u64(cap.resource as u64);
104 let port_obj = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
105 let mut msg = port_obj.recv().map_err(SyscallError::from)?;
106
107 if msg.flags != 0 {
109 let sender_id = crate::process::TaskId::from_u64(msg.sender);
110 let sender = crate::process::get_task_by_id(sender_id).ok_or(SyscallError::BadHandle)?;
111 let sender_caps = unsafe { &mut *sender.process.capabilities.get() };
112 let dup = sender_caps
113 .duplicate(CapId::from_raw(msg.flags as u64))
114 .ok_or(SyscallError::PermissionDenied)?;
115
116 let receiver = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
117 let receiver_caps = unsafe { &mut *receiver.process.capabilities.get() };
118 let new_id = super::dispatcher::insert_capability_with_retention(receiver_caps, dup)?;
119 if new_id.as_u64() > u32::MAX as u64 {
120 return Err(SyscallError::InvalidArgument);
121 }
122 msg.flags = new_id.as_u64() as u32;
123 }
124
125 const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
126 let mut buf = [0u8; MSG_SIZE];
127 crate::ipc::message::ipc_message_to_raw(&msg, &mut buf);
128 let user = UserSliceWrite::new(msg_ptr, MSG_SIZE)?;
129 user.copy_from(&buf);
130 Ok(0)
131}
132
133pub fn sys_ipc_try_recv(port_handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
135 silo::enforce_cap_for_current_task(port_handle)?;
136 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
137 let caps = unsafe { &*task.process.capabilities.get() };
138 let required = CapPermissions {
139 read: true,
140 write: false,
141 execute: false,
142 grant: false,
143 revoke: false,
144 };
145 let cap = caps
146 .get_with_permissions(CapId::from_raw(port_handle), required)
147 .ok_or(SyscallError::PermissionDenied)?;
148 if cap.resource_type != ResourceType::IpcPort {
149 return Err(SyscallError::BadHandle);
150 }
151
152 let port_id = PortId::from_u64(cap.resource as u64);
153 let port_obj = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
154 let msg_opt = port_obj.try_recv().map_err(SyscallError::from)?;
155
156 let mut msg = match msg_opt {
157 Some(m) => m,
158 None => return Err(SyscallError::Again),
159 };
160
161 if msg.flags != 0 {
163 let sender_id = crate::process::TaskId::from_u64(msg.sender);
164 let sender = crate::process::get_task_by_id(sender_id).ok_or(SyscallError::BadHandle)?;
165 let sender_caps = unsafe { &mut *sender.process.capabilities.get() };
166 let dup = sender_caps
167 .duplicate(CapId::from_raw(msg.flags as u64))
168 .ok_or(SyscallError::PermissionDenied)?;
169
170 let receiver = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
171 let receiver_caps = unsafe { &mut *receiver.process.capabilities.get() };
172 let new_id = super::dispatcher::insert_capability_with_retention(receiver_caps, dup)?;
173 if new_id.as_u64() > u32::MAX as u64 {
174 return Err(SyscallError::InvalidArgument);
175 }
176 msg.flags = new_id.as_u64() as u32;
177 }
178
179 const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
180 let mut buf = [0u8; MSG_SIZE];
181 crate::ipc::message::ipc_message_to_raw(&msg, &mut buf);
182 let user = UserSliceWrite::new(msg_ptr, MSG_SIZE)?;
183 user.copy_from(&buf);
184 Ok(0)
185}
186
187pub fn sys_ipc_connect(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
189 if path_ptr == 0 || path_len == 0 {
190 return Err(SyscallError::Fault);
191 }
192 const MAX_PATH_LEN: usize = 4096;
193 if path_len as usize > MAX_PATH_LEN {
194 return Err(SyscallError::InvalidArgument);
195 }
196 let user = UserSliceRead::new(path_ptr, path_len as usize)?;
197 let bytes = user.read_to_vec();
198 let path = core::str::from_utf8(&bytes).map_err(SyscallError::from)?;
199
200 let (port_raw, _remaining) = crate::namespace::resolve(path).ok_or(SyscallError::NotFound)?;
201 let port_id = PortId::from_u64(port_raw);
202 if port::get_port(port_id).is_none() {
203 return Err(SyscallError::BadHandle);
204 }
205
206 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
207 let cap = get_capability_manager().create_capability(
208 ResourceType::IpcPort,
209 port_raw as usize,
210 CapPermissions {
211 read: true,
212 write: true,
213 execute: false,
214 grant: false,
215 revoke: false,
216 },
217 );
218 let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
219 Ok(cap_id.as_u64())
220}
221
222pub fn sys_ipc_call(port_handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
224 silo::enforce_cap_for_current_task(port_handle)?;
225 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
226 let caps = unsafe { &*task.process.capabilities.get() };
227 let required = CapPermissions {
228 read: false,
229 write: true,
230 execute: false,
231 grant: false,
232 revoke: false,
233 };
234 let cap = caps
235 .get_with_permissions(CapId::from_raw(port_handle), required)
236 .ok_or(SyscallError::PermissionDenied)?;
237 if cap.resource_type != ResourceType::IpcPort {
238 return Err(SyscallError::BadHandle);
239 }
240
241 const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
242 let user = UserSliceRead::new(msg_ptr, MSG_SIZE)?;
243 let mut buf = [0u8; MSG_SIZE];
244 user.copy_to(&mut buf);
245 let mut msg = crate::ipc::message::ipc_message_from_raw(&buf);
246 msg.sender = task.id.as_u64();
247 if msg.flags != 0 {
248 let transfer_required = CapPermissions {
249 read: false,
250 write: false,
251 execute: false,
252 grant: true,
253 revoke: false,
254 };
255 if caps
256 .get_with_permissions(CapId::from_raw(msg.flags as u64), transfer_required)
257 .is_none()
258 {
259 return Err(SyscallError::PermissionDenied);
260 }
261 }
262
263 let port_id = PortId::from_u64(cap.resource as u64);
264 let port_obj = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
265 let port_owner = port_obj.owner;
266 port_obj.send(msg).map_err(SyscallError::from)?;
267
268 let reply_msg = reply::wait_for_reply(task.id, port_owner);
269 let mut out_buf = [0u8; MSG_SIZE];
270 crate::ipc::message::ipc_message_to_raw(&reply_msg, &mut out_buf);
271 let user = UserSliceWrite::new(msg_ptr, MSG_SIZE)?;
272 user.copy_from(&out_buf);
273 Ok(0)
274}
275
276pub fn sys_ipc_reply(msg_ptr: u64) -> Result<u64, SyscallError> {
278 if msg_ptr == 0 {
279 return Err(SyscallError::Fault);
280 }
281 const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
282 let user = UserSliceRead::new(msg_ptr, MSG_SIZE)?;
283 let mut buf = [0u8; MSG_SIZE];
284 user.copy_to(&mut buf);
285 let msg = crate::ipc::message::ipc_message_from_raw(&buf);
286
287 let target = crate::process::TaskId::from_u64(msg.sender);
288 let mut msg = msg;
289 if msg.flags != 0 {
290 let sender = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
291 let sender_caps = unsafe { &mut *sender.process.capabilities.get() };
292 let dup = sender_caps
293 .duplicate(CapId::from_raw(msg.flags as u64))
294 .ok_or(SyscallError::PermissionDenied)?;
295
296 let receiver = crate::process::get_task_by_id(target).ok_or(SyscallError::BadHandle)?;
297 let receiver_caps = unsafe { &mut *receiver.process.capabilities.get() };
298 let new_id = super::dispatcher::insert_capability_with_retention(receiver_caps, dup)?;
299 if new_id.as_u64() > u32::MAX as u64 {
300 return Err(SyscallError::InvalidArgument);
301 }
302 msg.flags = new_id.as_u64() as u32;
303 }
304
305 reply::deliver_reply(target, msg).map_err(|_| SyscallError::BadHandle)?;
306 Ok(0)
307}
308
309pub fn sys_ipc_bind_port(
311 port_handle: u64,
312 path_ptr: u64,
313 path_len: u64,
314) -> Result<u64, SyscallError> {
315 silo::enforce_registry_bind_for_current_task()?;
316 silo::enforce_cap_for_current_task(port_handle)?;
317 if path_ptr == 0 || path_len == 0 {
318 return Err(SyscallError::Fault);
319 }
320 const MAX_PATH_LEN: usize = 4096;
321 if path_len as usize > MAX_PATH_LEN {
322 return Err(SyscallError::InvalidArgument);
323 }
324 let user = UserSliceRead::new(path_ptr, path_len as usize)?;
325 let bytes = user.read_to_vec();
326 let path = core::str::from_utf8(&bytes).map_err(SyscallError::from)?;
327
328 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
329 let caps = unsafe { &*task.process.capabilities.get() };
330 let cap = caps
331 .get_with_permissions(
332 CapId::from_raw(port_handle),
333 CapPermissions {
334 read: true,
335 write: true,
336 execute: false,
337 grant: true,
338 revoke: false,
339 },
340 )
341 .ok_or(SyscallError::PermissionDenied)?;
342 if cap.resource_type != ResourceType::IpcPort {
343 return Err(SyscallError::BadHandle);
344 }
345
346 crate::vfs::mount(
347 path,
348 alloc::sync::Arc::new(crate::vfs::IpcScheme::new(PortId::from_u64(
349 cap.resource as u64,
350 ))),
351 )?;
352 let _ = crate::namespace::bind(path, cap.resource as u64);
353 let _ = silo::set_current_silo_label_from_path(path);
354
355 let should_bootstrap = path == "/" || path.starts_with("/srv/strate-fs-");
358 if should_bootstrap {
359 let mut seeded_handle: u32 = 0;
360 if let Some(device) = crate::hardware::storage::virtio_block::get_device() {
361 let volume_resource = device as *const _ as usize;
362 let volume_perms = CapPermissions {
363 read: true,
364 write: true,
365 execute: false,
366 grant: true,
367 revoke: true,
368 };
369 let volume_cap = get_capability_manager().create_capability(
370 ResourceType::Volume,
371 volume_resource,
372 volume_perms,
373 );
374 let task_caps = unsafe { &mut *task.process.capabilities.get() };
375 let id = task_caps.insert(volume_cap);
376 let _ = silo::register_current_task_granted_resource(
377 ResourceType::Volume,
378 volume_resource,
379 volume_perms,
380 );
381 if id.as_u64() <= u32::MAX as u64 {
382 seeded_handle = id.as_u64() as u32;
383 }
384 log::info!(
385 "ipc_bind_port('/'): seeded volume capability handle={} for task {:?}",
386 id.as_u64(),
387 task.id
388 );
389 }
390
391 const BOOTSTRAP_MSG_TYPE: u32 = 0x10;
393 let mut boot_msg = IpcMessage::new(BOOTSTRAP_MSG_TYPE);
394 boot_msg.sender = task.id.as_u64();
395 boot_msg.flags = seeded_handle;
396 let label_owned = silo::current_task_silo_label().unwrap_or_else(|| {
397 if path == "/" {
398 alloc::string::String::from("root")
399 } else {
400 alloc::string::String::from(
401 path.rsplit('/')
402 .find(|part| !part.is_empty())
403 .unwrap_or("default"),
404 )
405 }
406 });
407 let label_bytes = label_owned.as_bytes();
408 let max_len = boot_msg.payload.len().saturating_sub(1);
409 let copy_len = core::cmp::min(label_bytes.len(), max_len);
410 boot_msg.payload[0] = copy_len as u8;
411 if copy_len > 0 {
412 boot_msg.payload[1..1 + copy_len].copy_from_slice(&label_bytes[..copy_len]);
413 }
414
415 let port_id = PortId::from_u64(cap.resource as u64);
416 if let Some(p) = port::get_port(port_id) {
417 if p.send(boot_msg).is_ok() {
418 log::info!(
419 "ipc_bind_port('{}'): queued bootstrap message (handle={}, label={})",
420 path,
421 seeded_handle,
422 label_owned
423 );
424 } else {
425 log::warn!(
426 "ipc_bind_port('{}'): failed to queue bootstrap message",
427 path
428 );
429 }
430 } else {
431 log::warn!(
432 "ipc_bind_port('{}'): bound port disappeared before bootstrap",
433 path
434 );
435 }
436 }
437 Ok(0)
438}
439
440pub fn sys_ipc_unbind_port(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
442 silo::require_silo_admin()?;
443 if path_ptr == 0 || path_len == 0 {
444 return Err(SyscallError::Fault);
445 }
446 const MAX_PATH_LEN: usize = 4096;
447 if path_len as usize > MAX_PATH_LEN {
448 return Err(SyscallError::InvalidArgument);
449 }
450 let user = UserSliceRead::new(path_ptr, path_len as usize)?;
451 let bytes = user.read_to_vec();
452 let path = core::str::from_utf8(&bytes).map_err(SyscallError::from)?;
453 let _ = crate::namespace::unbind(path);
454 crate::vfs::unmount(path)?;
455 Ok(0)
456}