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