1use crate::{
7 capability::{get_capability_manager, CapId, CapPermissions, Capability, ResourceType},
8 hardware::storage::{ahci, virtio_block},
9 ipc::port::{self, PortId},
10 memory::{UserSliceRead, UserSliceWrite},
11 process::{current_task_clone, task::Task, TaskId},
12 sync::{FixedQueue, SpinLock},
13 syscall::error::SyscallError,
14};
15use alloc::{
16 boxed::Box,
17 collections::BTreeMap,
18 string::{String, ToString},
19 sync::Arc,
20 vec::Vec,
21};
22use core::sync::atomic::{AtomicU64, Ordering};
23use heapless::Vec as HVec;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
30#[repr(u8)]
31pub enum SiloTier {
32 Critical = 0,
33 System = 1,
34 User = 2,
35}
36
37#[repr(C)]
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
39pub struct SiloId {
40 pub sid: u32,
41 pub tier: SiloTier,
42}
43
44impl SiloId {
45 pub const fn new(sid: u32) -> Self {
47 let tier = match sid {
48 1..=9 => SiloTier::Critical,
49 10..=999 => SiloTier::System,
50 _ => SiloTier::User,
51 };
52 Self { sid, tier }
53 }
54
55 pub fn as_u64(&self) -> u64 {
57 self.sid as u64
58 }
59}
60
61#[cfg(feature = "selftest")]
62pub mod silo_test;
63
64use bitflags::bitflags;
65
66bitflags! {
67 #[repr(transparent)]
68 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
69 pub struct ControlMode: u8 {
70 const LIST = 0b100;
71 const STOP = 0b010;
72 const SPAWN = 0b001;
73 }
74}
75
76bitflags! {
77 #[repr(transparent)]
78 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
79 pub struct HardwareMode: u8 {
80 const INTERRUPT = 0b100;
81 const IO = 0b010;
82 const DMA = 0b001;
83 }
84}
85
86bitflags! {
87 #[repr(transparent)]
88 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
89 pub struct RegistryMode: u8 {
90 const LOOKUP = 0b100;
91 const BIND = 0b010;
92 const PROXY = 0b001;
93 }
94}
95
96#[repr(C)]
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct OctalMode {
99 pub control: ControlMode,
100 pub hardware: HardwareMode,
101 pub registry: RegistryMode,
102}
103
104impl OctalMode {
105 pub const fn from_octal(val: u16) -> Self {
107 Self {
108 control: ControlMode::from_bits_truncate(((val >> 6) & 0o7) as u8),
109 hardware: HardwareMode::from_bits_truncate(((val >> 3) & 0o7) as u8),
110 registry: RegistryMode::from_bits_truncate((val & 0o7) as u8),
111 }
112 }
113
114 pub const fn is_subset_of(&self, other: &OctalMode) -> bool {
116 (self.control.bits() & !other.control.bits() == 0)
117 && (self.hardware.bits() & !other.hardware.bits() == 0)
118 && (self.registry.bits() & !other.registry.bits() == 0)
119 }
120
121 pub fn pledge(&mut self, new_mode: OctalMode) -> Result<(), SyscallError> {
123 if !new_mode.is_subset_of(self) {
124 return Err(SyscallError::PermissionDenied); }
126 *self = new_mode;
127 Ok(())
128 }
129}
130
131pub fn sys_silo_pledge(mode_val: u64) -> Result<u64, SyscallError> {
133 let new_mode = OctalMode::from_octal(mode_val as u16);
134 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
135
136 let mut mgr = SILO_MANAGER.lock();
137 if let Some(silo_id) = mgr.silo_for_task(task.id) {
138 if let Ok(silo) = mgr.get_mut(silo_id) {
139 silo.mode.pledge(new_mode)?;
140 silo.config.mode = mode_val as u16;
143
144 mgr.push_event(SiloEvent {
145 silo_id: silo_id as u64,
146 kind: SiloEventKind::Started, data0: mode_val,
148 data1: 0,
149 tick: crate::process::scheduler::ticks(),
150 });
151 return Ok(0);
152 }
153 }
154 Err(SyscallError::BadHandle)
155}
156
157pub fn sys_silo_unveil(
159 path_ptr: u64,
160 path_len: u64,
161 rights_bits: u64,
162) -> Result<u64, SyscallError> {
163 const MAX_UNVEIL_PATH: usize = 1024;
164 const MAX_UNVEIL_RULES: usize = 128;
165
166 if path_ptr == 0 {
167 return Err(SyscallError::Fault);
168 }
169 let len = usize::try_from(path_len).map_err(|_| SyscallError::InvalidArgument)?;
170 if len == 0 || len > MAX_UNVEIL_PATH {
171 return Err(SyscallError::InvalidArgument);
172 }
173 let user = UserSliceRead::new(path_ptr, len)?;
174 let raw = user.read_to_vec();
175 let path = core::str::from_utf8(&raw).map_err(|_| SyscallError::InvalidArgument)?;
176 if path.is_empty() || !path.starts_with('/') || path.as_bytes().iter().any(|b| *b == 0) {
177 return Err(SyscallError::InvalidArgument);
178 }
179 let path = normalize_unveil_path(path)?;
180 let rights = UnveilRights::from_bits(rights_bits)?;
181
182 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
183 let mut mgr = SILO_MANAGER.lock();
184 let silo_id = mgr.silo_for_task(task.id).ok_or(SyscallError::BadHandle)?;
185 let silo = mgr.get_mut(silo_id)?;
186
187 if let Some(rule) = silo.unveil_rules.iter_mut().find(|r| r.path == path) {
188 rule.rights = rule.rights.intersect(rights);
189 return Ok(0);
190 }
191 if silo.unveil_rules.len() >= MAX_UNVEIL_RULES {
192 return Err(SyscallError::QueueFull);
193 }
194 let _ = silo.unveil_rules.push(UnveilRule { path, rights });
196 Ok(0)
197}
198
199pub fn sys_silo_enter_sandbox() -> Result<u64, SyscallError> {
201 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
202 let mut mgr = SILO_MANAGER.lock();
203 let silo_id = mgr.silo_for_task(task.id).ok_or(SyscallError::BadHandle)?;
204 let silo = mgr.get_mut(silo_id)?;
205 if silo.sandboxed {
206 return Ok(0);
207 }
208 silo.sandboxed = true;
209 silo.mode.registry = RegistryMode::empty();
210 silo.config.mode =
211 ((silo.mode.control.bits() as u16) << 6) | ((silo.mode.hardware.bits() as u16) << 3);
212 Ok(0)
213}
214
215pub fn sys_silo_rename(handle: u64, label_ptr: u64, label_len: u64) -> Result<u64, SyscallError> {
220 require_silo_admin()?;
221 const MAX_LABEL: usize = 31;
222 let len = label_len as usize;
223 if len == 0 || len > MAX_LABEL {
224 return Err(SyscallError::InvalidArgument);
225 }
226 let user_slice = UserSliceRead::new(label_ptr, len)?;
227 let raw = user_slice.read_to_vec();
228 let label = core::str::from_utf8(&raw).map_err(|_| SyscallError::InvalidArgument)?;
229 if !is_valid_label(label) {
230 return Err(SyscallError::InvalidArgument);
231 }
232 let silo_id = resolve_silo_handle(handle, CapPermissions::read_write())?;
233 let mut mgr = SILO_MANAGER.lock();
234 if mgr
236 .silos
237 .values()
238 .any(|s| s.id.sid != silo_id && s.strate_label.as_deref() == Some(label))
239 {
240 return Err(SyscallError::AlreadyExists);
241 }
242 let silo = mgr.get_mut(silo_id)?;
243 silo.strate_label = Some(String::from(label));
244 Ok(0)
245}
246
247pub fn enforce_silo_may_grant() -> Result<(), SyscallError> {
249 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
250 if is_admin_task(&task) {
251 return Ok(());
252 }
253 let mgr = SILO_MANAGER.lock();
254 let Some(silo_id) = mgr.silo_for_task(task.id) else {
255 return Ok(());
256 };
257 let silo = mgr.get(silo_id)?;
258 if silo.sandboxed {
259 return Err(SyscallError::PermissionDenied);
260 }
261 Ok(())
262}
263
264fn normalize_unveil_path(path: &str) -> Result<String, SyscallError> {
273 if !path.starts_with('/') {
274 return Err(SyscallError::InvalidArgument);
275 }
276 let mut out = String::new();
277 let mut prev_slash = false;
278 let mut cur_segment = String::new();
279 for ch in path.chars() {
280 if ch == '/' {
281 if !prev_slash {
282 if cur_segment == "." || cur_segment == ".." {
283 return Err(SyscallError::InvalidArgument);
284 }
285 out.push('/');
286 cur_segment.clear();
287 }
288 prev_slash = true;
289 continue;
290 }
291 if ch == '\0' {
292 return Err(SyscallError::InvalidArgument);
293 }
294 prev_slash = false;
295 cur_segment.push(ch);
296 out.push(ch);
297 }
298 if cur_segment == "." || cur_segment == ".." {
299 return Err(SyscallError::InvalidArgument);
300 }
301 while out.len() > 1 && out.ends_with('/') {
302 out.pop();
303 }
304 if out.is_empty() {
305 out.push('/');
306 }
307 Ok(out)
308}
309
310fn path_rule_matches(rule: &str, path: &str) -> bool {
312 if rule == "/" {
313 return true;
314 }
315 if path == rule {
316 return true;
317 }
318 if !path.starts_with(rule) {
319 return false;
320 }
321 let bytes = path.as_bytes();
322 let idx = rule.len();
323 idx < bytes.len() && bytes[idx] == b'/'
324}
325
326pub fn enforce_path_for_current_task(
328 path: &str,
329 want_read: bool,
330 want_write: bool,
331 want_execute: bool,
332) -> Result<(), SyscallError> {
333 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
334 if is_admin_task(&task) {
335 return Ok(());
336 }
337 let path = normalize_unveil_path(path)?;
338 let mgr = SILO_MANAGER.lock();
339 let Some(silo_id) = mgr.silo_for_task(task.id) else {
340 return Ok(());
341 };
342 let silo = mgr.get(silo_id)?;
343 if silo.sandboxed {
344 return Err(SyscallError::PermissionDenied);
345 }
346 if silo.unveil_rules.is_empty() {
347 return Ok(());
348 }
349 for rule in &silo.unveil_rules {
350 if !path_rule_matches(&rule.path, &path) {
351 continue;
352 }
353 if (!want_read || rule.rights.read)
354 && (!want_write || rule.rights.write)
355 && (!want_execute || rule.rights.execute)
356 {
357 return Ok(());
358 }
359 }
360 Err(SyscallError::PermissionDenied)
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
364#[repr(C)]
365struct UnveilRights {
366 read: bool,
367 write: bool,
368 execute: bool,
369}
370
371impl UnveilRights {
372 fn from_bits(bits: u64) -> Result<Self, SyscallError> {
374 if bits & !0x7 != 0 {
375 return Err(SyscallError::InvalidArgument);
376 }
377 Ok(Self {
378 read: (bits & 0x1) != 0,
379 write: (bits & 0x2) != 0,
380 execute: (bits & 0x4) != 0,
381 })
382 }
383
384 fn intersect(self, other: Self) -> Self {
386 Self {
387 read: self.read && other.read,
388 write: self.write && other.write,
389 execute: self.execute && other.execute,
390 }
391 }
392}
393
394#[derive(Debug, Clone)]
395struct UnveilRule {
396 path: String,
397 rights: UnveilRights,
398}
399
400#[repr(u8)]
401#[derive(Debug, Clone, Copy, PartialEq, Eq)]
402pub enum StrateFamily {
403 SYS = 0,
404 DRV = 1,
405 FS = 2,
406 NET = 3,
407 WASM = 4,
408 USR = 5,
409}
410
411#[repr(u32)]
412#[derive(Debug, Clone, Copy, PartialEq, Eq)]
413pub enum SiloState {
414 Created = 0,
415 Loading = 1,
416 Ready = 2,
417 Running = 3,
418 Paused = 4,
419 Stopping = 5,
420 Stopped = 6,
421 Crashed = 7,
422 Zombie = 8,
423 Destroyed = 9,
424}
425
426pub const SILO_FLAG_ADMIN: u64 = 1 << 0;
427pub const SILO_FLAG_GRAPHICS: u64 = 1 << 1;
428pub const SILO_FLAG_WEBRTC_NATIVE: u64 = 1 << 2;
429pub const SILO_FLAG_GRAPHICS_READ_ONLY: u64 = 1 << 3;
430pub const SILO_FLAG_WEBRTC_TURN_FORCE: u64 = 1 << 4;
431
432#[repr(C)]
433#[derive(Debug, Clone, Copy)]
434pub struct SiloConfig {
435 pub mem_min: u64,
436 pub mem_max: u64,
437 pub cpu_shares: u32,
438 pub cpu_quota_us: u64,
439 pub cpu_period_us: u64,
440 pub cpu_affinity_mask: u64,
441 pub max_tasks: u32,
442 pub io_bw_read: u64,
443 pub io_bw_write: u64,
444 pub caps_ptr: u64,
445 pub caps_len: u64,
446 pub flags: u64,
447 pub sid: u32,
448 pub mode: u16,
449 pub family: u8,
450 pub cpu_features_required: u64,
452 pub cpu_features_allowed: u64,
454 pub xcr0_mask: u64,
456 pub graphics_max_sessions: u16,
458 pub graphics_session_ttl_sec: u32,
460 pub graphics_reserved: u16,
462}
463
464impl Default for SiloConfig {
465 fn default() -> Self {
466 SiloConfig {
467 mem_min: 0,
468 mem_max: 0,
469 cpu_shares: 0,
470 cpu_quota_us: 0,
471 cpu_period_us: 0,
472 cpu_affinity_mask: 0,
473 max_tasks: 0,
474 io_bw_read: 0,
475 io_bw_write: 0,
476 caps_ptr: 0,
477 caps_len: 0,
478 flags: 0,
479 sid: 42,
480 mode: 0,
481 family: StrateFamily::USR as u8,
482 cpu_features_required: 0,
483 cpu_features_allowed: u64::MAX,
484 xcr0_mask: 0,
485 graphics_max_sessions: 0,
486 graphics_session_ttl_sec: 0,
487 graphics_reserved: 0,
488 }
489 }
490}
491
492impl SiloConfig {
493 fn validate(&self) -> Result<(), SyscallError> {
495 if self.mem_min > self.mem_max && self.mem_max != 0 {
496 return Err(SyscallError::InvalidArgument);
497 }
498 if self.cpu_quota_us > 0 && self.cpu_period_us == 0 {
499 return Err(SyscallError::InvalidArgument);
500 }
501 if self.caps_len > MAX_SILO_CAPS as u64 {
502 return Err(SyscallError::InvalidArgument);
503 }
504 if self.caps_len > 0 && self.caps_ptr == 0 {
505 return Err(SyscallError::InvalidArgument);
506 }
507 if self.flags & SILO_FLAG_WEBRTC_NATIVE != 0 && self.flags & SILO_FLAG_GRAPHICS == 0 {
508 return Err(SyscallError::InvalidArgument);
509 }
510 if self.flags & SILO_FLAG_GRAPHICS == 0 {
511 if self.graphics_max_sessions != 0 || self.graphics_session_ttl_sec != 0 {
512 return Err(SyscallError::InvalidArgument);
513 }
514 } else {
515 if self.graphics_max_sessions == 0 {
516 return Err(SyscallError::InvalidArgument);
517 }
518 if self.graphics_session_ttl_sec == 0 {
519 return Err(SyscallError::InvalidArgument);
520 }
521 }
522 Ok(())
523 }
524}
525
526#[repr(C, packed)]
527#[derive(Clone, Copy)]
528pub struct Strat9ModuleHeader {
529 pub magic: [u8; 4], pub version: u16,
531 pub cpu_arch: u8, pub flags: u32,
533 pub code_offset: u64,
534 pub code_size: u64,
535 pub data_offset: u64,
536 pub data_size: u64,
537 pub bss_size: u64,
538 pub entry_point: u64,
539 pub export_table_offset: u64,
540 pub import_table_offset: u64,
541 pub relocation_table_offset: u64,
542 pub key_id: [u8; 8],
543 pub signature: [u8; 64],
544 pub cpu_features_required: u64,
546 pub reserved: [u8; 48],
547}
548
549impl core::fmt::Debug for Strat9ModuleHeader {
550 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
552 let version = unsafe { core::ptr::addr_of!(self.version).read_unaligned() };
554 let flags = unsafe { core::ptr::addr_of!(self.flags).read_unaligned() };
555 let entry = unsafe { core::ptr::addr_of!(self.entry_point).read_unaligned() };
556 let code_sz = unsafe { core::ptr::addr_of!(self.code_size).read_unaligned() };
557 let data_sz = unsafe { core::ptr::addr_of!(self.data_size).read_unaligned() };
558 f.debug_struct("Strat9ModuleHeader")
559 .field("magic", &self.magic)
560 .field("version", &version)
561 .field("cpu_arch", &self.cpu_arch)
562 .field("flags", &flags)
563 .field("entry_point", &entry)
564 .field("code_size", &code_sz)
565 .field("data_size", &data_sz)
566 .finish_non_exhaustive()
567 }
568}
569
570#[repr(C)]
571#[derive(Debug, Clone, Copy)]
572pub struct ModuleInfo {
573 pub id: u64,
574 pub format: u32, pub flags: u32,
576 pub version: u16,
577 pub cpu_arch: u8,
578 pub reserved: u8,
579 pub code_size: u64,
580 pub data_size: u64,
581 pub bss_size: u64,
582 pub entry_point: u64,
583 pub total_size: u64,
584}
585
586#[repr(u32)]
587#[derive(Debug, Clone, Copy, PartialEq, Eq)]
588pub enum SiloEventKind {
589 Started = 1,
590 Stopped = 2,
591 Killed = 3,
592 Crashed = 4,
593 Paused = 5,
594 Resumed = 6,
595}
596
597#[repr(u64)]
598#[derive(Debug, Clone, Copy, PartialEq, Eq)]
599pub enum SiloFaultReason {
600 PageFault = 1,
601 GeneralProtection = 2,
602 InvalidOpcode = 3,
603}
604
605#[repr(C)]
606#[derive(Debug, Clone, Copy)]
607pub struct SiloEvent {
608 pub silo_id: u64,
609 pub kind: SiloEventKind,
610 pub data0: u64,
611 pub data1: u64,
612 pub tick: u64,
613}
614
615pub const FAULT_SUBCODE_SHIFT: u64 = 16;
620
621pub fn pack_fault(reason: SiloFaultReason, subcode: u64) -> u64 {
623 (reason as u64) | (subcode << FAULT_SUBCODE_SHIFT)
624}
625
626#[derive(Debug)]
631struct Silo {
632 id: SiloId,
633 name: String,
634 strate_label: Option<String>,
635 state: SiloState,
636 config: SiloConfig,
637 mode: OctalMode,
638 family: StrateFamily,
639 mem_usage_bytes: u64,
642 flags: u32,
643 module_id: Option<u64>,
644 tasks: HVec<TaskId, 64>,
645 granted_caps: HVec<u64, 64>,
646 granted_resources: HVec<GrantedResource, 32>,
647 unveil_rules: HVec<UnveilRule, 128>,
648 sandboxed: bool,
649 event_seq: u64,
650 output_buf: Option<Box<SiloOutputBuf>>,
652}
653
654const SILO_OUTPUT_CAPACITY: usize = 4096;
655
656struct SiloOutputBuf {
657 buf: [u8; SILO_OUTPUT_CAPACITY],
658 head: usize,
659 count: usize,
660}
661
662impl core::fmt::Debug for SiloOutputBuf {
663 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
664 f.debug_struct("SiloOutputBuf")
665 .field("count", &self.count)
666 .finish()
667 }
668}
669
670impl SiloOutputBuf {
671 const fn new() -> Self {
672 Self {
673 buf: [0; SILO_OUTPUT_CAPACITY],
674 head: 0,
675 count: 0,
676 }
677 }
678
679 fn push(&mut self, data: &[u8]) {
680 for &b in data {
681 let tail = (self.head + self.count) % SILO_OUTPUT_CAPACITY;
682 self.buf[tail] = b;
683 if self.count < SILO_OUTPUT_CAPACITY {
684 self.count += 1;
685 } else {
686 self.head = (self.head + 1) % SILO_OUTPUT_CAPACITY;
687 }
688 }
689 }
690
691 fn drain(&mut self) -> Vec<u8> {
692 let mut out = Vec::with_capacity(self.count);
693 for i in 0..self.count {
694 out.push(self.buf[(self.head + i) % SILO_OUTPUT_CAPACITY]);
695 }
696 self.head = 0;
697 self.count = 0;
698 out
699 }
700}
701
702#[derive(Debug, Clone)]
703pub struct SiloSnapshot {
704 pub id: u32,
705 pub tier: SiloTier,
706 pub name: String,
707 pub strate_label: Option<String>,
708 pub state: SiloState,
709 pub task_count: usize,
710 pub mem_usage_bytes: u64,
711 pub mem_min_bytes: u64,
712 pub mem_max_bytes: u64,
713 pub mode: u16,
714 pub graphics_flags: u64,
715 pub graphics_max_sessions: u16,
716 pub graphics_session_ttl_sec: u32,
717}
718
719#[derive(Debug, Clone)]
720pub struct SiloDetailSnapshot {
721 pub base: SiloSnapshot,
722 pub family: StrateFamily,
723 pub sandboxed: bool,
724 pub cpu_shares: u32,
725 pub cpu_affinity_mask: u64,
726 pub max_tasks: u32,
727 pub task_ids: Vec<u64>,
728 pub unveil_rules: Vec<(String, u8)>,
729 pub granted_caps_count: usize,
730 pub cpu_features_required: u64,
731 pub cpu_features_allowed: u64,
732 pub xcr0_mask: u64,
733 pub graphics_flags: u64,
734 pub graphics_max_sessions: u16,
735 pub graphics_session_ttl_sec: u32,
736}
737
738#[derive(Debug, Clone)]
739pub struct SiloEventSnapshot {
740 pub silo_id: u64,
741 pub kind: SiloEventKind,
742 pub data0: u64,
743 pub data1: u64,
744 pub tick: u64,
745}
746
747struct SiloManager {
748 silos: BTreeMap<u32, Box<Silo>>,
749 events: FixedQueue<SiloEvent, SILO_EVENTS_CAPACITY>,
750 task_to_silo: BTreeMap<TaskId, u32>,
751}
752
753const SILO_EVENTS_CAPACITY: usize = 256;
754
755impl SiloManager {
756 const fn new() -> Self {
758 SiloManager {
759 silos: BTreeMap::new(),
760 events: FixedQueue::new(),
761 task_to_silo: BTreeMap::new(),
762 }
763 }
764
765 fn create_silo(&mut self, config: &SiloConfig) -> Result<SiloId, SyscallError> {
767 let id = SiloId::new(config.sid);
768 if self.silos.contains_key(&id.sid) {
769 return Err(SyscallError::AlreadyExists);
770 }
771
772 kernel_check_spawn_invariants(&id, &OctalMode::from_octal(config.mode))?;
773
774 let mut name = String::from("silo-");
775 name.push_str(&id.sid.to_string());
776
777 let family = decode_family(config.family)?;
778
779 let silo = Silo {
780 id,
781 name,
782 strate_label: None,
783 state: SiloState::Created,
784 config: *config,
785 mode: OctalMode::from_octal(config.mode),
786 family,
787 mem_usage_bytes: 0,
788 flags: config.flags as u32,
789 module_id: None,
790 tasks: HVec::new(),
791 granted_caps: HVec::new(),
792 granted_resources: HVec::new(),
793 unveil_rules: HVec::new(),
794 sandboxed: false,
795 event_seq: 0,
796 output_buf: None,
797 };
798
799 self.silos.insert(id.sid, Box::new(silo));
800 Ok(id)
801 }
802
803 fn get_mut(&mut self, id: u32) -> Result<&mut Silo, SyscallError> {
805 self.silos
806 .get_mut(&id)
807 .map(Box::as_mut)
808 .ok_or(SyscallError::BadHandle)
809 }
810
811 fn get(&self, id: u32) -> Result<&Silo, SyscallError> {
813 self.silos
814 .get(&id)
815 .map(Box::as_ref)
816 .ok_or(SyscallError::BadHandle)
817 }
818
819 fn push_event(&mut self, ev: SiloEvent) {
821 if self.events.is_full() {
822 let _ = self.events.pop_front();
823 }
824 self.events
825 .push_back(ev)
826 .expect("silo event queue push must succeed after dropping oldest entry");
827 }
828
829 fn map_task(&mut self, task_id: TaskId, silo_id: u32) {
831 self.task_to_silo.insert(task_id, silo_id);
832 }
833
834 fn unmap_task(&mut self, task_id: TaskId) {
836 self.task_to_silo.remove(&task_id);
837 }
838
839 fn silo_for_task(&self, task_id: TaskId) -> Option<u32> {
841 if let Some(silo_id) = self.task_to_silo.get(&task_id).copied() {
842 return Some(silo_id);
843 }
844
845 self.silos
849 .iter()
850 .find_map(|(sid, silo)| silo.tasks.iter().any(|tid| *tid == task_id).then_some(*sid))
851 }
852}
853
854pub fn kernel_check_spawn_invariants(id: &SiloId, mode: &OctalMode) -> Result<(), SyscallError> {
856 if id.tier == SiloTier::User && !mode.hardware.is_empty() {
857 return Err(SyscallError::PermissionDenied);
858 }
859 if id.tier == SiloTier::User && !mode.control.is_empty() {
860 return Err(SyscallError::PermissionDenied);
861 }
862 Ok(())
863}
864
865fn decode_family(raw: u8) -> Result<StrateFamily, SyscallError> {
867 match raw {
868 0 => Ok(StrateFamily::SYS),
869 1 => Ok(StrateFamily::DRV),
870 2 => Ok(StrateFamily::FS),
871 3 => Ok(StrateFamily::NET),
872 4 => Ok(StrateFamily::WASM),
873 5 => Ok(StrateFamily::USR),
874 _ => Err(SyscallError::InvalidArgument),
875 }
876}
877
878static SILO_MANAGER: SpinLock<SiloManager> = SpinLock::new(SiloManager::new());
879static BOOT_REG_IN_PROGRESS: core::sync::atomic::AtomicBool =
880 core::sync::atomic::AtomicBool::new(false);
881
882const SILO_ADMIN_RESOURCE: usize = 0;
883const MAX_SILO_CAPS: usize = 64;
884const MAX_MODULE_BLOB_LEN: usize = 64 * 1024 * 1024; const IPC_STREAM_DATA: u32 = 0xFFFF_FFFE;
886const IPC_STREAM_EOF: u32 = 0xFFFF_FFFF;
887const MODULE_FLAG_SIGNED: u32 = 1 << 0;
888const MODULE_FLAG_KERNEL: u32 = 1 << 1;
889
890fn read_user_config(ptr: u64) -> Result<SiloConfig, SyscallError> {
892 if ptr == 0 {
893 return Err(SyscallError::Fault);
894 }
895 const SIZE: usize = core::mem::size_of::<SiloConfig>();
896 let user = UserSliceRead::new(ptr, SIZE)?;
897 let mut buf = [0u8; SIZE];
898 user.copy_to(&mut buf);
899 let config = unsafe { core::ptr::read_unaligned(buf.as_ptr() as *const SiloConfig) };
901 Ok(config)
902}
903
904fn read_caps_list(ptr: u64, len: u64) -> Result<Vec<u64>, SyscallError> {
906 if len == 0 {
907 return Ok(Vec::new());
908 }
909 if len > MAX_SILO_CAPS as u64 {
910 return Err(SyscallError::InvalidArgument);
911 }
912 let byte_len = len as usize * core::mem::size_of::<u64>();
913 let user = UserSliceRead::new(ptr, byte_len)?;
914 let bytes = user.read_to_vec();
915 let mut out = Vec::with_capacity(len as usize);
916 for chunk in bytes.chunks_exact(8) {
917 let mut arr = [0u8; 8];
918 arr.copy_from_slice(chunk);
919 out.push(u64::from_le_bytes(arr));
920 }
921 Ok(out)
922}
923
924fn read_module_stream_from_port(
926 port: &alloc::sync::Arc<port::Port>,
927) -> Result<Vec<u8>, SyscallError> {
928 let mut out = Vec::new();
929 loop {
930 let msg = port.recv().map_err(|_| SyscallError::BadHandle)?;
931
932 if msg.msg_type == IPC_STREAM_EOF {
933 break;
934 }
935 if msg.msg_type != IPC_STREAM_DATA {
936 return Err(SyscallError::InvalidArgument);
937 }
938 if msg.flags != 0 {
939 return Err(SyscallError::InvalidArgument);
940 }
941
942 let chunk_len = u16::from_le_bytes([msg.payload[0], msg.payload[1]]) as usize;
943 if chunk_len == 0 {
944 break;
945 }
946 if chunk_len > msg.payload.len() - 2 {
947 return Err(SyscallError::InvalidArgument);
948 }
949 if out.len().saturating_add(chunk_len) > MAX_MODULE_BLOB_LEN {
950 return Err(SyscallError::InvalidArgument);
951 }
952
953 out.extend_from_slice(&msg.payload[2..2 + chunk_len]);
954 }
955 Ok(out)
956}
957
958fn parse_module_header(data: &[u8]) -> Result<Option<Strat9ModuleHeader>, SyscallError> {
960 const MAGIC: [u8; 4] = *b"CMOD";
961 let header_size = core::mem::size_of::<Strat9ModuleHeader>();
962
963 if data.len() < MAGIC.len() {
964 return Ok(None);
965 }
966 if data[0..4] != MAGIC {
967 return Ok(None);
968 }
969 if data.len() < header_size {
970 return Err(SyscallError::InvalidArgument);
971 }
972
973 let header = unsafe { core::ptr::read_unaligned(data.as_ptr() as *const Strat9ModuleHeader) };
975
976 if header.version != 1 && header.version != 2 {
977 return Err(SyscallError::InvalidArgument);
978 }
979 if header.cpu_arch != 0 {
980 return Err(SyscallError::InvalidArgument);
981 }
982
983 let version = unsafe { core::ptr::addr_of!(header.version).read_unaligned() };
984 let req = if version >= 2 {
985 unsafe { core::ptr::addr_of!(header.cpu_features_required).read_unaligned() }
986 } else {
987 0
988 };
989 if req != 0 {
990 let host = crate::arch::cpuid::host();
991 let required = crate::arch::cpuid::CpuFeatures::from_bits_truncate(req);
992 if !host.features.contains(required) {
993 log::warn!(
994 "[cmod] module requires CPU features {:#x} but host has {:#x}",
995 req,
996 host.features.bits()
997 );
998 return Err(SyscallError::InvalidArgument);
999 }
1000 }
1001
1002 let data_len = data.len() as u64;
1003 let code_end = header
1004 .code_offset
1005 .checked_add(header.code_size)
1006 .ok_or(SyscallError::InvalidArgument)?;
1007 let data_end = header
1008 .data_offset
1009 .checked_add(header.data_size)
1010 .ok_or(SyscallError::InvalidArgument)?;
1011 if code_end > data_len || data_end > data_len {
1012 return Err(SyscallError::InvalidArgument);
1013 }
1014 if header.entry_point >= header.code_size && header.code_size != 0 {
1015 return Err(SyscallError::InvalidArgument);
1016 }
1017 if header.export_table_offset > data_len
1018 || header.import_table_offset > data_len
1019 || header.relocation_table_offset > data_len
1020 {
1021 return Err(SyscallError::InvalidArgument);
1022 }
1023
1024 const PAGE_SIZE: u64 = 4096;
1026 if header.code_size > 0 {
1027 if header.code_offset % PAGE_SIZE != 0 || header.code_size % PAGE_SIZE != 0 {
1028 return Err(SyscallError::InvalidArgument);
1029 }
1030 }
1031 if header.data_size > 0 {
1032 if header.data_offset % PAGE_SIZE != 0 || header.data_size % PAGE_SIZE != 0 {
1033 return Err(SyscallError::InvalidArgument);
1034 }
1035 }
1036 let code_range = header.code_offset..code_end;
1037 let data_range = header.data_offset..data_end;
1038 if code_range.start < data_range.end && data_range.start < code_range.end {
1039 return Err(SyscallError::InvalidArgument);
1040 }
1041
1042 if header.flags & MODULE_FLAG_SIGNED != 0 {
1049 let key_id_offset = core::mem::offset_of!(Strat9ModuleHeader, key_id);
1051 let sig_offset = core::mem::offset_of!(Strat9ModuleHeader, signature);
1052 let header_size = core::mem::size_of::<Strat9ModuleHeader>();
1053
1054 let payload = if data.len() > header_size {
1057 &data[header_size..]
1058 } else {
1059 return Err(SyscallError::InvalidArgument);
1060 };
1061
1062 let result = crate::crypto::verify_cmod_signature(data, key_id_offset, sig_offset, payload);
1063
1064 match result {
1065 crate::crypto::VerifyResult::Valid => {
1066 log::info!("[cmod] Ed25519 signature verified");
1067 }
1068 crate::crypto::VerifyResult::Unsigned => {
1069 log::warn!("[cmod] MODULE_FLAG_SIGNED set but signature is zero");
1070 return Err(SyscallError::PermissionDenied);
1071 }
1072 crate::crypto::VerifyResult::KeyNotFound => {
1073 log::warn!("[cmod] no trusted key for id {:02x?}", header.key_id);
1074 return Err(SyscallError::PermissionDenied);
1075 }
1076 crate::crypto::VerifyResult::InvalidSignature => {
1077 log::warn!("[cmod] Ed25519 verification FAILED");
1078 return Err(SyscallError::PermissionDenied);
1079 }
1080 }
1081 }
1082 if header.flags & MODULE_FLAG_KERNEL != 0 {
1083 }
1085
1086 Ok(Some(header))
1087}
1088
1089fn read_u32_le(data: &[u8], offset: usize) -> Result<u32, SyscallError> {
1091 if offset + 4 > data.len() {
1092 return Err(SyscallError::InvalidArgument);
1093 }
1094 let mut buf = [0u8; 4];
1095 buf.copy_from_slice(&data[offset..offset + 4]);
1096 Ok(u32::from_le_bytes(buf))
1097}
1098
1099fn read_u64_le(data: &[u8], offset: usize) -> Result<u64, SyscallError> {
1101 if offset + 8 > data.len() {
1102 return Err(SyscallError::InvalidArgument);
1103 }
1104 let mut buf = [0u8; 8];
1105 buf.copy_from_slice(&data[offset..offset + 8]);
1106 Ok(u64::from_le_bytes(buf))
1107}
1108
1109fn resolve_export_offset(module: &ModuleImage, ordinal: u64) -> Result<u64, SyscallError> {
1111 let header = module.header.ok_or(SyscallError::InvalidArgument)?;
1112 if header.export_table_offset == 0 {
1113 return Err(SyscallError::NotFound);
1114 }
1115 let table_off = header.export_table_offset as usize;
1116 let count = read_u32_le(module.data.as_slice(), table_off)? as u64;
1117 if ordinal >= count {
1118 return Err(SyscallError::InvalidArgument);
1119 }
1120 let entries_off = table_off + 8;
1122 let entry_off = entries_off + (ordinal as usize * 8);
1123 let rva = read_u64_le(module.data.as_slice(), entry_off)?;
1124 Ok(rva)
1125}
1126
1127pub fn require_silo_admin() -> Result<(), SyscallError> {
1129 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1130 let caps = unsafe { &*task.process.capabilities.get() };
1132 let required = CapPermissions {
1133 read: false,
1134 write: false,
1135 execute: false,
1136 grant: true,
1137 revoke: false,
1138 };
1139
1140 if caps.has_resource_with_permissions(ResourceType::Silo, SILO_ADMIN_RESOURCE, required) {
1141 Ok(())
1142 } else {
1143 Err(SyscallError::PermissionDenied)
1144 }
1145}
1146
1147fn resolve_silo_handle(handle: u64, required: CapPermissions) -> Result<u32, SyscallError> {
1149 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1150 let caps = unsafe { &*task.process.capabilities.get() };
1151 let cap_id = CapId::from_raw(handle);
1152 let cap = caps.get(cap_id).ok_or(SyscallError::BadHandle)?;
1153
1154 if cap.resource_type != ResourceType::Silo {
1156 return Err(SyscallError::BadHandle);
1157 }
1158
1159 if (!required.read || cap.permissions.read)
1160 && (!required.write || cap.permissions.write)
1161 && (!required.execute || cap.permissions.execute)
1162 && (!required.grant || cap.permissions.grant)
1163 && (!required.revoke || cap.permissions.revoke)
1164 {
1165 Ok(cap.resource as u32)
1166 } else {
1167 Err(SyscallError::PermissionDenied)
1168 }
1169}
1170
1171#[derive(Debug, Clone)]
1176enum ModuleData {
1177 Owned(Arc<[u8]>),
1178 Static(&'static [u8]),
1179}
1180
1181impl ModuleData {
1182 fn as_slice(&self) -> &[u8] {
1183 match self {
1184 ModuleData::Owned(data) => data,
1185 ModuleData::Static(data) => data,
1186 }
1187 }
1188
1189 fn len(&self) -> usize {
1190 self.as_slice().len()
1191 }
1192}
1193
1194#[derive(Debug)]
1195struct ModuleImage {
1196 id: u64,
1197 data: ModuleData,
1198 header: Option<Strat9ModuleHeader>,
1199}
1200
1201struct ModuleRegistry {
1202 modules: BTreeMap<u64, ModuleImage>,
1203 next_id: u64,
1204}
1205
1206impl ModuleRegistry {
1207 const fn new() -> Self {
1209 ModuleRegistry {
1210 modules: BTreeMap::new(),
1211 next_id: 1,
1212 }
1213 }
1214
1215 fn allocate_id(&mut self) -> Result<u64, SyscallError> {
1218 let id = self.next_id;
1219 self.next_id = id.checked_add(1).ok_or(SyscallError::OutOfMemory)?;
1220 Ok(id)
1221 }
1222
1223 fn register(&mut self, data: Vec<u8>) -> Result<u64, SyscallError> {
1225 let header = parse_module_header(&data)?;
1226 let id = self.allocate_id()?;
1227 self.modules.insert(
1228 id,
1229 ModuleImage {
1230 id,
1231 data: ModuleData::Owned(Arc::from(data.into_boxed_slice())),
1232 header,
1233 },
1234 );
1235 Ok(id)
1236 }
1237
1238 fn register_static(&mut self, data: &'static [u8]) -> Result<u64, SyscallError> {
1239 let header = parse_module_header(data)?;
1240 let id = self.allocate_id()?;
1241 self.modules.insert(
1242 id,
1243 ModuleImage {
1244 id,
1245 data: ModuleData::Static(data),
1246 header,
1247 },
1248 );
1249 Ok(id)
1250 }
1251
1252 fn get(&self, id: u64) -> Option<&ModuleImage> {
1254 self.modules.get(&id)
1255 }
1256
1257 fn remove(&mut self, id: u64) -> Option<ModuleImage> {
1259 self.modules.remove(&id)
1260 }
1261}
1262
1263static MODULE_REGISTRY: SpinLock<ModuleRegistry> = SpinLock::new(ModuleRegistry::new());
1264
1265fn charge_task_silo_memory(task_id: TaskId, bytes: u64) -> Result<(), SyscallError> {
1274 if bytes == 0 {
1275 return Ok(());
1276 }
1277 let Some(mut mgr) = SILO_MANAGER.try_lock() else {
1278 return Ok(());
1279 };
1280 let Some(silo_id) = mgr.silo_for_task(task_id) else {
1281 return Ok(());
1282 };
1283 let silo = mgr.get_mut(silo_id)?;
1284 let next = silo
1285 .mem_usage_bytes
1286 .checked_add(bytes)
1287 .ok_or(SyscallError::OutOfMemory)?;
1288 if silo.config.mem_max != 0 && next > silo.config.mem_max {
1289 return Err(SyscallError::OutOfMemory);
1290 }
1291 silo.mem_usage_bytes = next;
1292 Ok(())
1293}
1294
1295fn release_task_silo_memory(task_id: TaskId, bytes: u64) {
1299 if bytes == 0 {
1300 return;
1301 }
1302 let Some(mut mgr) = SILO_MANAGER.try_lock() else {
1303 return;
1304 };
1305 let Some(silo_id) = mgr.silo_for_task(task_id) else {
1306 return;
1307 };
1308 if let Ok(silo) = mgr.get_mut(silo_id) {
1309 silo.mem_usage_bytes = silo.mem_usage_bytes.saturating_sub(bytes);
1310 }
1311}
1312
1313pub fn charge_current_task_memory(bytes: u64) -> Result<(), SyscallError> {
1318 let Some(task) = crate::process::scheduler::current_task_clone_try() else {
1319 return Ok(());
1322 };
1323 charge_task_silo_memory(task.id, bytes)
1324}
1325
1326pub fn release_current_task_memory(bytes: u64) {
1330 if let Some(task) = crate::process::scheduler::current_task_clone_try() {
1331 release_task_silo_memory(task.id, bytes);
1332 }
1333}
1334
1335fn extract_strate_label(path: &str) -> Option<String> {
1337 let prefix = "/srv/strate-fs-";
1338 let rest = path.strip_prefix(prefix)?;
1339 let mut parts = rest.split('/').filter(|p| !p.is_empty());
1340 let _strate_type = parts.next()?;
1341 let label = parts.next()?;
1342 if label.is_empty() || parts.next().is_some() {
1343 return None;
1344 }
1345 Some(String::from(label))
1346}
1347
1348fn sanitize_label(raw: &str) -> String {
1350 let mut out = String::new();
1351 for b in raw.bytes().take(31) {
1352 let ok = (b as char).is_ascii_alphanumeric() || b == b'-' || b == b'_' || b == b'.';
1353 out.push(if ok { b as char } else { '_' });
1354 }
1355 if out.is_empty() {
1356 String::from("default")
1357 } else {
1358 out
1359 }
1360}
1361
1362fn is_valid_label(raw: &str) -> bool {
1364 if raw.is_empty() || raw.len() > 31 {
1365 return false;
1366 }
1367 raw.bytes()
1368 .all(|b| (b as char).is_ascii_alphanumeric() || b == b'-' || b == b'_' || b == b'.')
1369}
1370
1371pub fn set_current_silo_label_from_path(path: &str) -> Result<(), SyscallError> {
1373 let Some(label) = extract_strate_label(path) else {
1374 return Ok(());
1375 };
1376 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1377 let mut mgr = SILO_MANAGER.lock();
1378 let Some(silo_id) = mgr.silo_for_task(task.id) else {
1379 return Ok(());
1380 };
1381 let silo = mgr.get_mut(silo_id)?;
1382 if silo.strate_label.is_none() {
1385 silo.strate_label = Some(label);
1386 }
1387 Ok(())
1388}
1389
1390pub fn current_task_silo_label() -> Option<String> {
1392 let task = current_task_clone()?;
1393 let mgr = SILO_MANAGER.lock();
1394 let silo_id = mgr.silo_for_task(task.id)?;
1395 let silo = mgr.get(silo_id).ok()?;
1396 silo.strate_label.clone()
1397}
1398
1399pub fn list_silos_snapshot() -> Vec<SiloSnapshot> {
1401 let mgr = SILO_MANAGER.lock();
1402 mgr.silos
1403 .values()
1404 .map(|s| SiloSnapshot {
1405 id: s.id.sid,
1406 tier: s.id.tier,
1407 name: s.name.clone(),
1408 strate_label: s.strate_label.clone(),
1409 state: s.state,
1410 task_count: s.tasks.len(),
1411 mem_usage_bytes: s.mem_usage_bytes,
1412 mem_min_bytes: s.config.mem_min,
1413 mem_max_bytes: s.config.mem_max,
1414 mode: s.config.mode,
1415 graphics_flags: s.config.flags
1416 & (SILO_FLAG_GRAPHICS
1417 | SILO_FLAG_WEBRTC_NATIVE
1418 | SILO_FLAG_GRAPHICS_READ_ONLY
1419 | SILO_FLAG_WEBRTC_TURN_FORCE),
1420 graphics_max_sessions: s.config.graphics_max_sessions,
1421 graphics_session_ttl_sec: s.config.graphics_session_ttl_sec,
1422 })
1423 .collect()
1424}
1425
1426pub fn try_silo_id_for_task(task_id: TaskId) -> Option<u32> {
1441 SILO_MANAGER.try_lock()?.silo_for_task(task_id)
1442}
1443
1444pub fn silo_info_for_task(task_id: TaskId) -> Option<(u32, Option<String>, u64, u64, u64)> {
1445 let mgr = SILO_MANAGER.lock();
1446 let silo_id = mgr.silo_for_task(task_id)?;
1447 let silo = mgr.get(silo_id).ok()?;
1448 Some((
1449 silo.id.sid,
1450 silo.strate_label.clone(),
1451 silo.mem_usage_bytes,
1452 silo.config.mem_min,
1453 silo.config.mem_max,
1454 ))
1455}
1456
1457fn resolve_volume_resource_from_dev_path(dev_path: &str) -> Result<usize, SyscallError> {
1459 match dev_path {
1460 "/dev/sda" => ahci::get_device()
1461 .map(|d| d as *const _ as usize)
1462 .ok_or(SyscallError::NotFound),
1463 "/dev/vda" => virtio_block::get_device()
1464 .map(|d| d as *const _ as usize)
1465 .ok_or(SyscallError::NotFound),
1466 _ => Err(SyscallError::NotFound),
1467 }
1468}
1469
1470fn compute_silo_xcr0(config: &SiloConfig) -> u64 {
1472 use crate::arch::cpuid::{xcr0_for_features, CpuFeatures};
1473 let allowed = CpuFeatures::from_bits_truncate(config.cpu_features_allowed);
1474 xcr0_for_features(allowed)
1475}
1476
1477pub fn kernel_spawn_strate(
1479 elf_data: &[u8],
1480 label: Option<&str>,
1481 dev_path: Option<&str>,
1482) -> Result<u32, SyscallError> {
1483 let module_id = {
1484 let mut registry = MODULE_REGISTRY.lock();
1485 registry.register(elf_data.to_vec())?
1486 };
1487
1488 let silo_id = {
1489 let mut mgr = SILO_MANAGER.lock();
1490 let mut sid = 1000u32;
1493 while mgr.silos.contains_key(&sid) {
1494 sid = sid.checked_add(1).ok_or(SyscallError::OutOfMemory)?;
1495 }
1496
1497 let id = SiloId::new(sid);
1498 let requested_label = label
1499 .map(sanitize_label)
1500 .unwrap_or_else(|| alloc::format!("inst-{}", id.sid));
1501
1502 if mgr
1503 .silos
1504 .values()
1505 .any(|s| s.strate_label.as_deref() == Some(requested_label.as_str()))
1506 {
1507 return Err(SyscallError::AlreadyExists);
1508 }
1509
1510 let mut cfg = SiloConfig {
1511 sid: id.sid,
1512 mode: 0o000,
1513 family: StrateFamily::USR as u8,
1514 ..SiloConfig::default()
1515 };
1516 cfg.xcr0_mask = compute_silo_xcr0(&cfg);
1517
1518 let silo = Silo {
1519 id,
1520 name: alloc::format!("silo-{}", id.sid),
1521 strate_label: Some(requested_label),
1522 state: SiloState::Ready,
1523 config: cfg,
1524 mode: OctalMode::from_octal(0),
1525 family: StrateFamily::USR,
1526 mem_usage_bytes: 0,
1527 flags: 0,
1528 module_id: Some(module_id),
1529 tasks: HVec::new(),
1530 granted_caps: HVec::new(),
1531 granted_resources: HVec::new(),
1532 unveil_rules: HVec::new(),
1533 sandboxed: false,
1534 event_seq: 0,
1535 output_buf: None,
1536 };
1537
1538 mgr.silos.insert(id.sid, Box::new(silo));
1539 id.sid
1540 };
1541
1542 let module_data = {
1543 let registry = MODULE_REGISTRY.lock();
1544 let module = registry.get(module_id).ok_or(SyscallError::BadHandle)?;
1545 module.data.clone()
1546 };
1547
1548 let mut seed_caps = Vec::new();
1549 seed_caps.push(create_silo_admin_capability());
1550 if let Some(path) = dev_path {
1551 let resource = resolve_volume_resource_from_dev_path(path)?;
1552 let cap = get_capability_manager().create_capability(
1553 ResourceType::Volume,
1554 resource,
1555 CapPermissions {
1556 read: true,
1557 write: true,
1558 execute: false,
1559 grant: true,
1560 revoke: true,
1561 },
1562 );
1563 seed_caps.push(cap);
1564 }
1565
1566 let display = {
1567 let mgr = SILO_MANAGER.lock();
1568 let silo = mgr.get(silo_id)?;
1569 silo.strate_label
1570 .clone()
1571 .unwrap_or_else(|| alloc::format!("silo-{}", silo.id.sid))
1572 };
1573 let task_name: &'static str =
1574 Box::leak(alloc::format!("silo-{}/strate-admin-{}", silo_id, display).into_boxed_str());
1575 let task =
1576 crate::process::elf::load_elf_task_with_caps(module_data.as_slice(), task_name, &seed_caps)
1577 .map_err(|_| SyscallError::InvalidArgument)?;
1578 let task_id = task.id;
1579
1580 let mut mgr = SILO_MANAGER.lock();
1581 {
1582 let silo = mgr.get_mut(silo_id)?;
1583 if let Err(task) = silo.tasks.push(task_id) {
1584 log::error!(
1585 "[silo] tasks full (capacity=64), task {} not registered",
1586 task.0
1587 );
1588 }
1589 silo.state = SiloState::Running;
1590 let fpu_xcr0 = unsafe { (*task.fpu_state.get()).xcr0_mask };
1591 let effective_xcr0 = (silo.config.xcr0_mask & fpu_xcr0).max(0x3);
1592 task.xcr0_mask
1593 .store(effective_xcr0, core::sync::atomic::Ordering::Relaxed);
1594 let affinity = silo.config.cpu_affinity_mask;
1596 if affinity != 0 {
1597 task.affinity_mask
1598 .store(affinity, core::sync::atomic::Ordering::Relaxed);
1599 }
1600 }
1601 mgr.map_task(task_id, silo_id);
1602 mgr.push_event(SiloEvent {
1603 silo_id: silo_id.into(),
1604 kind: SiloEventKind::Started,
1605 data0: 0,
1606 data1: 0,
1607 tick: crate::process::scheduler::ticks(),
1608 });
1609 drop(mgr);
1610 crate::process::add_task(task);
1611 Ok(silo_id)
1612}
1613
1614fn resolve_selector_to_silo_id(selector: &str, mgr: &SiloManager) -> Result<u32, SyscallError> {
1616 if let Ok(id) = selector.parse::<u32>() {
1617 if mgr.silos.contains_key(&id) {
1618 return Ok(id);
1619 }
1620 return Err(SyscallError::NotFound);
1621 }
1622 let mut found: Option<u32> = None;
1623 for s in mgr.silos.values() {
1624 if s.strate_label.as_deref() == Some(selector) {
1625 if found.is_some() {
1626 return Err(SyscallError::InvalidArgument);
1627 }
1628 found = Some(s.id.sid);
1629 }
1630 }
1631 found.ok_or(SyscallError::NotFound)
1632}
1633
1634pub fn kernel_stop_silo(selector: &str, force_kill: bool) -> Result<u32, SyscallError> {
1636 let (silo_id, tasks) = {
1637 let mut mgr = SILO_MANAGER.lock();
1638 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
1639 let mut tasks = HVec::<TaskId, 64>::new();
1640 {
1641 let silo = mgr.get_mut(silo_id)?;
1642 match silo.state {
1643 SiloState::Running | SiloState::Paused => {
1644 tasks = silo.tasks.clone();
1645 silo.tasks.clear();
1646 silo.state = if force_kill {
1647 SiloState::Stopped
1648 } else {
1649 SiloState::Stopping
1650 };
1651 }
1652 SiloState::Stopping => {
1653 if force_kill {
1654 silo.state = SiloState::Stopped;
1655 }
1656 }
1657 SiloState::Stopped | SiloState::Created | SiloState::Ready => {}
1658 _ => return Err(SyscallError::InvalidArgument),
1659 }
1660 }
1661 for tid in &tasks {
1662 mgr.unmap_task(*tid);
1663 }
1664 mgr.push_event(SiloEvent {
1665 silo_id: silo_id as u64,
1666 kind: if force_kill {
1667 SiloEventKind::Killed
1668 } else {
1669 SiloEventKind::Stopped
1670 },
1671 data0: 0,
1672 data1: 0,
1673 tick: crate::process::scheduler::ticks(),
1674 });
1675 (silo_id, tasks)
1676 };
1677 for tid in tasks {
1678 crate::process::kill_task(tid);
1679 }
1680 Ok(silo_id)
1681}
1682
1683pub fn kernel_start_silo(selector: &str) -> Result<u32, SyscallError> {
1685 let silo_id = {
1686 let mgr = SILO_MANAGER.lock();
1687 resolve_selector_to_silo_id(selector, &mgr)?
1688 };
1689 let _ = start_silo_by_id(silo_id)?;
1690 Ok(silo_id)
1691}
1692
1693pub fn kernel_destroy_silo(selector: &str) -> Result<u32, SyscallError> {
1695 let (silo_id, module_id) = {
1696 let mut mgr = SILO_MANAGER.lock();
1697 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
1698 let module_id = {
1699 let silo = mgr.get(silo_id)?;
1700 if !silo.tasks.is_empty() {
1701 return Err(SyscallError::InvalidArgument);
1702 }
1703 match silo.state {
1704 SiloState::Stopped | SiloState::Created | SiloState::Ready | SiloState::Crashed => {
1705 }
1706 _ => return Err(SyscallError::InvalidArgument),
1707 }
1708 silo.module_id
1709 };
1710 let _ = mgr.silos.remove(&silo_id);
1711 (silo_id, module_id)
1712 };
1713 if let Some(mid) = module_id {
1714 let mut reg = MODULE_REGISTRY.lock();
1715 let _ = reg.remove(mid);
1716 }
1717 Ok(silo_id)
1718}
1719
1720pub fn kernel_rename_silo_label(selector: &str, new_label: &str) -> Result<u32, SyscallError> {
1722 if !is_valid_label(new_label) {
1723 return Err(SyscallError::InvalidArgument);
1724 }
1725 let mut mgr = SILO_MANAGER.lock();
1726 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
1727 if mgr
1728 .silos
1729 .values()
1730 .any(|s| s.id.sid != silo_id && s.strate_label.as_deref() == Some(new_label))
1731 {
1732 return Err(SyscallError::AlreadyExists);
1733 }
1734 let silo = mgr.get_mut(silo_id)?;
1735 match silo.state {
1736 SiloState::Stopped | SiloState::Created | SiloState::Ready | SiloState::Crashed => {
1737 silo.strate_label = Some(String::from(new_label));
1738 Ok(silo_id)
1739 }
1740 _ => Err(SyscallError::InvalidArgument),
1741 }
1742}
1743
1744pub fn register_boot_strate_task(task_id: TaskId, label: &str) -> Result<u32, SyscallError> {
1746 BOOT_REG_IN_PROGRESS.store(true, Ordering::Relaxed);
1747 let result = (|| -> Result<u32, SyscallError> {
1748 let sanitized = sanitize_label(label);
1749 let mgr = SILO_MANAGER.lock();
1750 let mut sid = 1u32;
1751 while mgr.silos.contains_key(&sid) {
1752 sid = sid.checked_add(1).ok_or(SyscallError::OutOfMemory)?;
1753 }
1754 if mgr
1755 .silos
1756 .values()
1757 .any(|s| s.strate_label.as_deref() == Some(sanitized.as_str()))
1758 {
1759 return Err(SyscallError::AlreadyExists);
1760 }
1761 drop(mgr);
1762
1763 let id = SiloId::new(sid);
1764 let silo = Silo {
1765 id,
1766 name: alloc::format!("silo-{}", id.sid),
1767 strate_label: Some(sanitized),
1768 state: SiloState::Running,
1769 config: SiloConfig {
1770 sid: id.sid,
1771 mode: 0o777,
1772 family: StrateFamily::SYS as u8,
1773 ..SiloConfig::default()
1774 },
1775 mode: OctalMode::from_octal(0o777),
1776 family: StrateFamily::SYS,
1777 mem_usage_bytes: 0,
1778 flags: 0,
1779 module_id: None,
1780 tasks: {
1781 let mut v = HVec::new();
1782 let _ = v.push(task_id);
1783 v
1784 },
1785 granted_caps: HVec::new(),
1786 granted_resources: HVec::new(),
1787 unveil_rules: HVec::new(),
1788 sandboxed: false,
1789 event_seq: 0,
1790 output_buf: None,
1791 };
1792
1793 let mut mgr = SILO_MANAGER.lock();
1794 if mgr.silos.contains_key(&id.sid) {
1795 return Err(SyscallError::Again);
1796 }
1797 if mgr
1798 .silos
1799 .values()
1800 .any(|s| s.strate_label.as_deref() == silo.strate_label.as_deref())
1801 {
1802 return Err(SyscallError::AlreadyExists);
1803 }
1804 mgr.silos.insert(id.sid, Box::new(silo));
1805 drop(mgr);
1806 Ok(id.sid)
1807 })();
1808 BOOT_REG_IN_PROGRESS.store(false, Ordering::Relaxed);
1809 result
1810}
1811
1812pub fn debug_boot_reg_active() -> bool {
1814 BOOT_REG_IN_PROGRESS.load(Ordering::Relaxed)
1815}
1816
1817fn resolve_module_handle(handle: u64, required: CapPermissions) -> Result<u64, SyscallError> {
1819 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1820 let caps = unsafe { &*task.process.capabilities.get() };
1821 let cap_id = CapId::from_raw(handle);
1822 let cap = caps.get(cap_id).ok_or(SyscallError::BadHandle)?;
1823
1824 if cap.resource_type != ResourceType::Module {
1825 return Err(SyscallError::BadHandle);
1826 }
1827
1828 if (!required.read || cap.permissions.read)
1829 && (!required.write || cap.permissions.write)
1830 && (!required.execute || cap.permissions.execute)
1831 && (!required.grant || cap.permissions.grant)
1832 && (!required.revoke || cap.permissions.revoke)
1833 {
1834 Ok(cap.resource as u64)
1835 } else {
1836 Err(SyscallError::PermissionDenied)
1837 }
1838}
1839
1840pub fn create_silo_admin_capability() -> Capability {
1844 get_capability_manager().create_capability(
1845 ResourceType::Silo,
1846 SILO_ADMIN_RESOURCE,
1847 CapPermissions::all(),
1848 )
1849}
1850
1851pub fn grant_silo_admin_to_task(task: &alloc::sync::Arc<Task>) -> CapId {
1852 let cap = create_silo_admin_capability();
1853 unsafe { (&mut *task.process.capabilities.get()).insert(cap) }
1855}
1856
1857pub fn sys_module_load(fd_or_ptr: u64, len: u64) -> Result<u64, SyscallError> {
1863 require_silo_admin()?;
1865
1866 if len != 0 {
1868 let len = len as usize;
1869 if len == 0 || len > MAX_MODULE_BLOB_LEN {
1870 return Err(SyscallError::InvalidArgument);
1871 }
1872
1873 if len <= 4096 {
1874 let user = UserSliceRead::new(fd_or_ptr, len)?;
1875 if matches!(user.read_u8(0), Ok(b'/')) {
1876 let path_buf = user.read_to_vec();
1877 let path =
1878 core::str::from_utf8(&path_buf).map_err(|_| SyscallError::InvalidArgument)?;
1879 let data = crate::vfs::get_initfs_file_bytes(path).ok_or_else(|| {
1880 log::warn!("module_load: initfs path not found: '{}'", path);
1881 SyscallError::NotFound
1882 })?;
1883 let mut registry = MODULE_REGISTRY.lock();
1884 let id = registry.register_static(data)?;
1885 drop(registry);
1886
1887 let cap = get_capability_manager().create_capability(
1888 ResourceType::Module,
1889 id as usize,
1890 CapPermissions::all(),
1891 );
1892
1893 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1894 let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1895 return Ok(cap_id.as_u64());
1896 }
1897 }
1898
1899 let user = UserSliceRead::new(fd_or_ptr, len)?;
1900 let data = user.read_to_vec();
1901 if data.len() >= 4 {
1902 log::debug!(
1903 "module_load: len={} magic={:02x}{:02x}{:02x}{:02x}",
1904 data.len(),
1905 data[0],
1906 data[1],
1907 data[2],
1908 data[3]
1909 );
1910 } else {
1911 log::debug!("module_load: len={} (too small)", data.len());
1912 }
1913
1914 let mut registry = MODULE_REGISTRY.lock();
1915 let id = registry.register(data)?;
1916 drop(registry);
1917
1918 let cap = get_capability_manager().create_capability(
1919 ResourceType::Module,
1920 id as usize,
1921 CapPermissions::all(),
1922 );
1923
1924 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1925 let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1926
1927 return Ok(cap_id.as_u64());
1928 }
1929
1930 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1939 let caps = unsafe { &*task.process.capabilities.get() };
1940 let required = CapPermissions {
1941 read: true,
1942 write: false,
1943 execute: false,
1944 grant: false,
1945 revoke: false,
1946 };
1947 let cap = caps
1948 .get_with_permissions(CapId::from_raw(fd_or_ptr), required)
1949 .ok_or(SyscallError::PermissionDenied)?;
1950 let data = match cap.resource_type {
1951 ResourceType::File => {
1952 let fd = u32::try_from(cap.resource).map_err(|_| SyscallError::BadHandle)?;
1953 crate::vfs::read_all(fd)?
1954 }
1955 ResourceType::IpcPort => {
1956 let port_id = PortId::from_u64(cap.resource as u64);
1957 let port = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
1958 read_module_stream_from_port(&port)?
1959 }
1960 _ => return Err(SyscallError::BadHandle),
1961 };
1962 if data.len() > MAX_MODULE_BLOB_LEN {
1963 return Err(SyscallError::InvalidArgument);
1964 }
1965
1966 let mut registry = MODULE_REGISTRY.lock();
1967 let id = registry.register(data)?;
1968 drop(registry);
1969
1970 let cap = get_capability_manager().create_capability(
1971 ResourceType::Module,
1972 id as usize,
1973 CapPermissions::all(),
1974 );
1975
1976 let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1977
1978 Ok(cap_id.as_u64())
1979}
1980
1981pub fn sys_module_unload(handle: u64) -> Result<u64, SyscallError> {
1983 require_silo_admin()?;
1984 let required = CapPermissions {
1985 read: false,
1986 write: false,
1987 execute: false,
1988 grant: false,
1989 revoke: true,
1990 };
1991 let module_id = resolve_module_handle(handle, required)?;
1992 let mut registry = MODULE_REGISTRY.lock();
1993 registry.remove(module_id);
1994 Ok(0)
1995}
1996
1997pub fn sys_module_get_symbol(handle: u64, _ordinal: u64) -> Result<u64, SyscallError> {
1999 let required = CapPermissions {
2000 read: true,
2001 write: false,
2002 execute: false,
2003 grant: false,
2004 revoke: false,
2005 };
2006 let module_id = resolve_module_handle(handle, required)?;
2007 let registry = MODULE_REGISTRY.lock();
2008 let module = registry.get(module_id).ok_or(SyscallError::BadHandle)?;
2009
2010 let rva = resolve_export_offset(module, _ordinal)?;
2012 let header = module.header.ok_or(SyscallError::InvalidArgument)?;
2013 Ok(header.code_offset.saturating_add(rva))
2014}
2015
2016pub fn sys_module_query(handle: u64, out_ptr: u64) -> Result<u64, SyscallError> {
2018 let required = CapPermissions {
2019 read: true,
2020 write: false,
2021 execute: false,
2022 grant: false,
2023 revoke: false,
2024 };
2025 let module_id = resolve_module_handle(handle, required)?;
2026 if out_ptr == 0 {
2027 return Err(SyscallError::Fault);
2028 }
2029
2030 let registry = MODULE_REGISTRY.lock();
2031 let module = registry.get(module_id).ok_or(SyscallError::BadHandle)?;
2032
2033 let (format, flags, version, cpu_arch, code_size, data_size, bss_size, entry_point) =
2034 if let Some(header) = module.header {
2035 (
2036 1u32,
2037 header.flags,
2038 header.version,
2039 header.cpu_arch,
2040 header.code_size,
2041 header.data_size,
2042 header.bss_size,
2043 header.entry_point,
2044 )
2045 } else {
2046 (0u32, 0u32, 0u16, 0u8, 0u64, 0u64, 0u64, 0u64)
2047 };
2048
2049 let info = ModuleInfo {
2050 id: module.id,
2051 format,
2052 flags,
2053 version,
2054 cpu_arch,
2055 reserved: 0,
2056 code_size,
2057 data_size,
2058 bss_size,
2059 entry_point,
2060 total_size: module.data.len() as u64,
2061 };
2062
2063 const INFO_SIZE: usize = core::mem::size_of::<ModuleInfo>();
2064 let user = UserSliceWrite::new(out_ptr, INFO_SIZE)?;
2065 let src =
2066 unsafe { core::slice::from_raw_parts(&info as *const ModuleInfo as *const u8, INFO_SIZE) };
2067 user.copy_from(src);
2068 Ok(0)
2069}
2070
2071pub fn sys_silo_create(config_ptr: u64) -> Result<u64, SyscallError> {
2077 require_silo_admin()?;
2078 let config = read_user_config(config_ptr)?;
2079 config.validate()?;
2080
2081 let mut mgr = SILO_MANAGER.lock();
2082 let id = mgr.create_silo(&config)?;
2083 drop(mgr);
2084
2085 let cap = get_capability_manager().create_capability(
2086 ResourceType::Silo,
2087 id.sid as usize,
2088 CapPermissions::all(),
2089 );
2090
2091 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2092 let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
2093
2094 Ok(cap_id.as_u64())
2095}
2096
2097pub fn sys_silo_config(handle: u64, res_ptr: u64) -> Result<u64, SyscallError> {
2099 require_silo_admin()?;
2100 let config = read_user_config(res_ptr)?;
2101 config.validate()?;
2102 let family = decode_family(config.family)?;
2103
2104 let mut granted_caps = HVec::<u64, 64>::new();
2105 let mut granted_resources = HVec::<GrantedResource, 32>::new();
2106 if config.caps_len > 0 {
2107 let caps_list = read_caps_list(config.caps_ptr, config.caps_len)?;
2108 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2109 let caps = unsafe { &*task.process.capabilities.get() };
2110
2111 for cap_handle in caps_list {
2112 let cap = caps
2113 .get(CapId::from_raw(cap_handle))
2114 .ok_or(SyscallError::BadHandle)?;
2115 if !cap.permissions.grant {
2116 return Err(SyscallError::PermissionDenied);
2117 }
2118 if !is_delegated_resource(cap.resource_type) {
2119 return Err(SyscallError::InvalidArgument);
2120 }
2121 if !granted_caps.contains(&cap_handle) {
2122 if let Err(cap) = granted_caps.push(cap_handle) {
2123 log::warn!(
2124 "[silo] granted_caps full (capacity=64), cap {} dropped",
2125 cap
2126 );
2127 }
2128 }
2129 add_or_merge_granted_resource(
2130 &mut granted_resources,
2131 GrantedResource {
2132 resource_type: cap.resource_type,
2133 resource: cap.resource,
2134 permissions: cap.permissions,
2135 },
2136 );
2137 }
2138 }
2139
2140 let sid = resolve_silo_handle(handle, CapPermissions::read_write())?;
2141 let mut mgr = SILO_MANAGER.lock();
2142 let silo = mgr.get_mut(sid as u32)?;
2143
2144 let requested_mode = OctalMode::from_octal(config.mode);
2145 kernel_check_spawn_invariants(&silo.id, &requested_mode)?;
2146 if silo.sandboxed && !requested_mode.is_subset_of(&silo.mode) {
2147 return Err(SyscallError::PermissionDenied);
2148 }
2149 if silo.sandboxed && !requested_mode.registry.is_empty() {
2150 return Err(SyscallError::PermissionDenied);
2151 }
2152
2153 let mut config = config;
2156 config.xcr0_mask = compute_silo_xcr0(&config);
2157
2158 silo.config = config;
2159 silo.mode = requested_mode;
2160 silo.family = family;
2161 silo.flags = config.flags as u32;
2162 silo.granted_caps = granted_caps;
2163 silo.granted_resources = granted_resources;
2164 Ok(0)
2165}
2166
2167pub fn sys_silo_attach_module(handle: u64, module_handle: u64) -> Result<u64, SyscallError> {
2169 require_silo_admin()?;
2170 let silo_id = resolve_silo_handle(handle, CapPermissions::read_write())?;
2171
2172 let required = CapPermissions {
2173 read: true,
2174 write: false,
2175 execute: false,
2176 grant: false,
2177 revoke: false,
2178 };
2179 let module_id = resolve_module_handle(module_handle, required)?;
2180
2181 let mut mgr = SILO_MANAGER.lock();
2182 let silo = mgr.get_mut(silo_id)?;
2183
2184 match silo.state {
2185 SiloState::Created | SiloState::Stopped | SiloState::Ready => {
2186 silo.module_id = Some(module_id);
2187 silo.state = SiloState::Ready;
2188 Ok(0)
2189 }
2190 SiloState::Running | SiloState::Paused => {
2191 silo.module_id = Some(module_id);
2192 Ok(0)
2193 }
2194 _ => Err(SyscallError::InvalidArgument),
2195 }
2196}
2197
2198fn start_silo_by_id(silo_id: u32) -> Result<u64, SyscallError> {
2200 let (
2201 module_id,
2202 granted_caps,
2203 granted_resources,
2204 silo_flags,
2205 previous_state,
2206 can_start,
2207 within_task_limit,
2208 silo_name,
2209 silo_label,
2210 ) = {
2211 let mut mgr = SILO_MANAGER.lock();
2212 let silo = mgr.get_mut(silo_id)?;
2213 let previous_state = silo.state;
2214 let can_start = matches!(
2215 previous_state,
2216 SiloState::Ready | SiloState::Stopped | SiloState::Running
2217 );
2218 let within_task_limit = match silo.config.max_tasks {
2219 0 => true, max => silo.tasks.len() < max as usize,
2221 };
2222 let module_id = silo.module_id;
2223 let granted_caps = silo.granted_caps.clone();
2224 let granted_resources = silo.granted_resources.clone();
2225 let silo_flags = silo.config.flags;
2226 let silo_name = silo.name.clone();
2227 let silo_label = silo.strate_label.clone();
2228 if can_start && within_task_limit {
2229 silo.state = SiloState::Loading;
2230 }
2231 (
2232 module_id,
2233 granted_caps,
2234 granted_resources,
2235 silo_flags,
2236 previous_state,
2237 can_start,
2238 within_task_limit,
2239 silo_name,
2240 silo_label,
2241 )
2242 };
2243
2244 if !can_start {
2245 return Err(SyscallError::InvalidArgument);
2246 }
2247 if !within_task_limit {
2248 return Err(SyscallError::QueueFull);
2249 }
2250
2251 let rollback_loading = |state: SiloState| {
2252 let mut mgr = SILO_MANAGER.lock();
2253 if let Ok(silo) = mgr.get_mut(silo_id) {
2254 if matches!(silo.state, SiloState::Loading) {
2255 silo.state = state;
2256 }
2257 }
2258 };
2259
2260 let module_id = match module_id {
2261 Some(id) => id,
2262 None => {
2263 rollback_loading(previous_state);
2264 return Err(SyscallError::InvalidArgument);
2265 }
2266 };
2267
2268 let mut seed_caps = {
2269 let task = match current_task_clone() {
2270 Some(t) => t,
2271 None => {
2272 rollback_loading(previous_state);
2273 return Err(SyscallError::PermissionDenied);
2274 }
2275 };
2276 let caps = unsafe { &mut *task.process.capabilities.get() };
2277 let mut out = Vec::with_capacity(granted_caps.len());
2278 for handle in granted_caps {
2279 if !silo_has_capability(&task, handle) {
2281 rollback_loading(previous_state);
2282 return Err(SyscallError::PermissionDenied);
2283 }
2284 let Some(dup) = caps.duplicate(CapId::from_raw(handle)) else {
2285 rollback_loading(previous_state);
2286 return Err(SyscallError::PermissionDenied);
2287 };
2288 let authorized = granted_resources
2295 .iter()
2296 .any(|g| g.resource_type == dup.resource_type && g.resource == dup.resource);
2297 if !authorized {
2298 log::warn!(
2299 "[silo] start sid={}: cap handle {} does not match any \
2300 granted resource (type={:?}, resource={:#x}) - denied",
2301 silo_id,
2302 handle,
2303 dup.resource_type,
2304 dup.resource
2305 );
2306 rollback_loading(previous_state);
2307 return Err(SyscallError::PermissionDenied);
2308 }
2309 out.push(dup);
2310 }
2311 out
2312 };
2313 if silo_flags & SILO_FLAG_ADMIN != 0 {
2314 seed_caps.push(create_silo_admin_capability());
2315 }
2316
2317 let display = silo_label.unwrap_or(silo_name);
2318 let task_name_owned = if silo_flags & SILO_FLAG_ADMIN != 0 {
2319 alloc::format!("silo-{}/strate-admin-{}", silo_id, display)
2320 } else {
2321 alloc::format!("silo-{}/strate-{}", silo_id, display)
2322 };
2323 let task_name: &'static str = Box::leak(task_name_owned.into_boxed_str());
2326
2327 let module_data = {
2328 let registry = MODULE_REGISTRY.lock();
2329 match registry.get(module_id) {
2330 Some(module) => module.data.clone(),
2331 None => {
2332 rollback_loading(previous_state);
2333 return Err(SyscallError::BadHandle);
2334 }
2335 }
2336 };
2337
2338 let load_result =
2339 crate::process::elf::load_elf_task_with_caps(module_data.as_slice(), task_name, &seed_caps)
2340 .map_err(|err| {
2341 log::warn!(
2342 "silo_start: sid={} module={} task='{}' load failed: {}",
2343 silo_id,
2344 module_id,
2345 task_name,
2346 err
2347 );
2348 map_elf_start_error(err)
2349 });
2350
2351 let task = match load_result {
2352 Ok(task) => task,
2353 Err(e) => {
2354 rollback_loading(previous_state);
2355 return Err(e);
2356 }
2357 };
2358 let task_id = task.id;
2359 let task_pid = task.pid;
2360
2361 let bg_stdin = crate::vfs::create_background_stdin();
2365 let fd_table = unsafe { &mut *task.process.fd_table.get() };
2366 fd_table.insert_at(crate::vfs::STDIN, bg_stdin);
2367
2368 let mut mgr = SILO_MANAGER.lock();
2369 {
2370 let silo = match mgr.get_mut(silo_id) {
2371 Ok(silo) => silo,
2372 Err(e) => {
2373 return Err(e);
2374 }
2375 };
2376 if let Err(task) = silo.tasks.push(task_id) {
2377 log::error!(
2378 "[silo] tasks full (capacity=64), task {} not registered",
2379 task.0
2380 );
2381 }
2382 silo.state = SiloState::Running;
2383 let fpu_xcr0 = unsafe { (*task.fpu_state.get()).xcr0_mask };
2384 let effective_xcr0 = (silo.config.xcr0_mask & fpu_xcr0).max(0x3);
2385 task.xcr0_mask
2386 .store(effective_xcr0, core::sync::atomic::Ordering::Relaxed);
2387 let affinity = silo.config.cpu_affinity_mask;
2389 if affinity != 0 {
2390 task.affinity_mask
2391 .store(affinity, core::sync::atomic::Ordering::Relaxed);
2392 }
2393 }
2394 mgr.map_task(task_id, silo_id);
2395 mgr.push_event(SiloEvent {
2396 silo_id: silo_id.into(),
2397 kind: SiloEventKind::Started,
2398 data0: 0,
2399 data1: 0,
2400 tick: crate::process::scheduler::ticks(),
2401 });
2402 drop(mgr);
2403 crate::process::add_task(task);
2404 Ok(task_pid as u64)
2405}
2406
2407pub fn sys_silo_start(handle: u64) -> Result<u64, SyscallError> {
2409 require_silo_admin()?;
2410 let required = CapPermissions {
2411 read: false,
2412 write: false,
2413 execute: true,
2414 grant: false,
2415 revoke: false,
2416 };
2417 let silo_id = resolve_silo_handle(handle, required)?;
2418 start_silo_by_id(silo_id)
2419}
2420
2421pub fn on_task_terminated(task_id: TaskId) {
2426 let mut mgr = SILO_MANAGER.lock();
2427 let silo_id = match mgr.silo_for_task(task_id) {
2428 Some(id) => id,
2429 None => return,
2430 };
2431 mgr.unmap_task(task_id);
2432
2433 let mut emit_stopped = false;
2434 if let Ok(silo) = mgr.get_mut(silo_id) {
2435 if let Some(pos) = silo.tasks.iter().position(|tid| *tid == task_id) {
2436 silo.tasks.swap_remove(pos);
2437 }
2438 if silo.tasks.is_empty() {
2439 match silo.state {
2440 SiloState::Running | SiloState::Paused | SiloState::Stopping => {
2441 silo.state = SiloState::Stopped;
2442 silo.event_seq = silo.event_seq.wrapping_add(1);
2443 emit_stopped = true;
2444 }
2445 _ => {}
2446 }
2447 }
2448 }
2449
2450 if emit_stopped {
2451 mgr.push_event(SiloEvent {
2452 silo_id: silo_id.into(),
2453 kind: SiloEventKind::Stopped,
2454 data0: 0,
2455 data1: 0,
2456 tick: crate::process::scheduler::ticks(),
2457 });
2458 }
2459}
2460
2461fn stop_or_kill_silo_by_id(
2463 silo_id: u32,
2464 force_kill: bool,
2465 require_running: bool,
2466) -> Result<HVec<TaskId, 64>, SyscallError> {
2467 let mut mgr = SILO_MANAGER.lock();
2468 let tasks = {
2469 let silo = mgr.get_mut(silo_id)?;
2470 if force_kill {
2471 silo.state = SiloState::Stopped;
2472 let tasks = silo.tasks.clone();
2473 silo.tasks.clear();
2474 tasks
2475 } else {
2476 match silo.state {
2477 SiloState::Running | SiloState::Paused => {
2478 silo.state = SiloState::Stopping;
2479 let tasks = silo.tasks.clone();
2480 silo.tasks.clear();
2481 tasks
2482 }
2483 _ if require_running => return Err(SyscallError::InvalidArgument),
2484 _ => HVec::new(),
2485 }
2486 }
2487 };
2488
2489 for tid in &tasks {
2490 mgr.unmap_task(*tid);
2491 }
2492 if !force_kill {
2493 if let Ok(silo) = mgr.get_mut(silo_id) {
2494 silo.state = SiloState::Stopped;
2495 }
2496 }
2497 mgr.push_event(SiloEvent {
2498 silo_id: silo_id.into(),
2499 kind: if force_kill {
2500 SiloEventKind::Killed
2501 } else {
2502 SiloEventKind::Stopped
2503 },
2504 data0: 0,
2505 data1: 0,
2506 tick: crate::process::scheduler::ticks(),
2507 });
2508
2509 Ok(tasks)
2510}
2511
2512pub fn sys_silo_stop(handle: u64) -> Result<u64, SyscallError> {
2514 require_silo_admin()?;
2515 let required = CapPermissions {
2516 read: false,
2517 write: false,
2518 execute: true,
2519 grant: false,
2520 revoke: false,
2521 };
2522 let silo_id = resolve_silo_handle(handle, required)?;
2523 let tasks = stop_or_kill_silo_by_id(silo_id, false, true)?;
2524
2525 for tid in tasks {
2526 crate::process::kill_task(tid);
2527 }
2528 Ok(0)
2529}
2530
2531pub fn sys_silo_kill(handle: u64) -> Result<u64, SyscallError> {
2533 require_silo_admin()?;
2534 let required = CapPermissions {
2535 read: false,
2536 write: false,
2537 execute: true,
2538 grant: false,
2539 revoke: false,
2540 };
2541 let silo_id = resolve_silo_handle(handle, required)?;
2542 let tasks = stop_or_kill_silo_by_id(silo_id, true, false)?;
2543
2544 for tid in tasks {
2545 crate::process::kill_task(tid);
2546 }
2547 Ok(0)
2548}
2549
2550fn silo_has_capability(task: &Task, cap_id: u64) -> bool {
2552 let caps = unsafe { &*task.process.capabilities.get() };
2553 caps.get(CapId::from_raw(cap_id)).is_some()
2554}
2555
2556fn is_delegated_resource(rt: ResourceType) -> bool {
2558 matches!(
2559 rt,
2560 ResourceType::Nic
2561 | ResourceType::FileSystem
2562 | ResourceType::Console
2563 | ResourceType::Keyboard
2564 | ResourceType::Volume
2565 | ResourceType::Namespace
2566 | ResourceType::Device
2567 | ResourceType::File
2568 | ResourceType::IoPortRange
2569 | ResourceType::InterruptLine
2570 )
2571}
2572
2573fn is_admin_task(task: &Task) -> bool {
2575 let caps = unsafe { &*task.process.capabilities.get() };
2576 let required = CapPermissions {
2577 read: false,
2578 write: false,
2579 execute: false,
2580 grant: true,
2581 revoke: false,
2582 };
2583 caps.has_resource_with_permissions(ResourceType::Silo, SILO_ADMIN_RESOURCE, required)
2584}
2585
2586fn map_elf_start_error(err: &'static str) -> SyscallError {
2588 if err.contains("allocate")
2589 || err.contains("Out of memory")
2590 || err.contains("No virtual range")
2591 || err.contains("Failed to map page")
2592 {
2593 return SyscallError::OutOfMemory;
2594 }
2595 if err.contains("ELF")
2596 || err.contains("PT_")
2597 || err.contains("entry")
2598 || err.contains("relocation")
2599 || err.contains("Program header")
2600 || err.contains("x86_64")
2601 || err.contains("Unsupported")
2602 {
2603 return SyscallError::ExecFormatError;
2604 }
2605 SyscallError::InvalidArgument
2606}
2607
2608#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2609struct GrantedResource {
2610 resource_type: ResourceType,
2611 resource: usize,
2612 permissions: CapPermissions,
2613}
2614
2615fn merge_permissions(a: CapPermissions, b: CapPermissions) -> CapPermissions {
2617 CapPermissions {
2618 read: a.read || b.read,
2619 write: a.write || b.write,
2620 execute: a.execute || b.execute,
2621 grant: a.grant || b.grant,
2622 revoke: a.revoke || b.revoke,
2623 }
2624}
2625
2626fn permissions_subset(requested: CapPermissions, allowed: CapPermissions) -> bool {
2628 (!requested.read || allowed.read)
2629 && (!requested.write || allowed.write)
2630 && (!requested.execute || allowed.execute)
2631 && (!requested.grant || allowed.grant)
2632 && (!requested.revoke || allowed.revoke)
2633}
2634
2635fn add_or_merge_granted_resource(list: &mut HVec<GrantedResource, 32>, grant: GrantedResource) {
2637 for existing in list.iter_mut() {
2638 if existing.resource_type == grant.resource_type && existing.resource == grant.resource {
2639 existing.permissions = merge_permissions(existing.permissions, grant.permissions);
2640 return;
2641 }
2642 }
2643 if list.push(grant).is_err() {
2644 log::warn!("[silo] granted_resources full (capacity=32), resource dropped");
2645 }
2646}
2647
2648pub fn register_current_task_granted_resource(
2650 resource_type: ResourceType,
2651 resource: usize,
2652 permissions: CapPermissions,
2653) -> Result<(), SyscallError> {
2654 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2655 let mut mgr = SILO_MANAGER.lock();
2656 let silo_id = mgr
2657 .silo_for_task(task.id)
2658 .ok_or(SyscallError::PermissionDenied)?;
2659 let silo = mgr.get_mut(silo_id)?;
2660 add_or_merge_granted_resource(
2661 &mut silo.granted_resources,
2662 GrantedResource {
2663 resource_type,
2664 resource,
2665 permissions,
2666 },
2667 );
2668 Ok(())
2669}
2670
2671pub fn enforce_cap_for_current_task(handle: u64) -> Result<(), SyscallError> {
2673 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2674
2675 if is_admin_task(&task) {
2677 return Ok(());
2678 }
2679
2680 let caps = unsafe { &*task.process.capabilities.get() };
2681 let cap = caps
2682 .get(CapId::from_raw(handle))
2683 .ok_or(SyscallError::BadHandle)?;
2684
2685 if !is_delegated_resource(cap.resource_type) {
2686 return Ok(());
2687 }
2688
2689 let mgr = SILO_MANAGER.lock();
2690 if let Some(silo_id) = mgr.silo_for_task(task.id) {
2691 if let Ok(silo) = mgr.get(silo_id) {
2692 for grant in &silo.granted_resources {
2693 if grant.resource_type == cap.resource_type && grant.resource == cap.resource {
2694 if permissions_subset(cap.permissions, grant.permissions) {
2695 return Ok(());
2696 }
2697 return Err(SyscallError::PermissionDenied);
2698 }
2699 }
2700 }
2701 }
2702
2703 Err(SyscallError::PermissionDenied)
2704}
2705
2706pub fn enforce_registry_bind_for_current_task() -> Result<(), SyscallError> {
2708 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2709 if is_admin_task(&task) {
2710 return Ok(());
2711 }
2712 let mgr = SILO_MANAGER.lock();
2713 let silo_id = mgr
2714 .silo_for_task(task.id)
2715 .ok_or(SyscallError::PermissionDenied)?;
2716 let silo = mgr.get(silo_id)?;
2717 if silo.sandboxed {
2718 return Err(SyscallError::PermissionDenied);
2719 }
2720 if silo.mode.registry.contains(RegistryMode::BIND) {
2721 Ok(())
2722 } else {
2723 Err(SyscallError::PermissionDenied)
2724 }
2725}
2726
2727pub fn enforce_console_access() -> Result<(), SyscallError> {
2732 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2733 if is_admin_task(&task) {
2734 return Ok(());
2735 }
2736 let mgr = SILO_MANAGER.lock();
2737 if let Some(silo_id) = mgr.silo_for_task(task.id) {
2738 if let Ok(silo) = mgr.get(silo_id) {
2739 if matches!(silo.family, StrateFamily::SYS | StrateFamily::NET) {
2740 return Ok(());
2741 }
2742 }
2743 }
2744 drop(mgr);
2745 let caps = unsafe { &*task.process.capabilities.get() };
2746 let required = CapPermissions {
2747 read: false,
2748 write: true,
2749 execute: false,
2750 grant: false,
2751 revoke: false,
2752 };
2753 if caps.has_resource_type_with_permissions(ResourceType::Console, required) {
2754 Ok(())
2755 } else {
2756 Err(SyscallError::PermissionDenied)
2757 }
2758}
2759
2760pub fn sys_silo_event_next(_event_ptr: u64) -> Result<u64, SyscallError> {
2762 require_silo_admin()?;
2763 if _event_ptr == 0 {
2764 return Err(SyscallError::Fault);
2765 }
2766
2767 let event = {
2768 let mut mgr = SILO_MANAGER.lock();
2769 mgr.events.pop_front()
2770 };
2771
2772 let event = match event {
2773 Some(e) => e,
2774 None => return Err(SyscallError::Again),
2775 };
2776
2777 const EVT_SIZE: usize = core::mem::size_of::<SiloEvent>();
2778 let user = UserSliceWrite::new(_event_ptr, EVT_SIZE)?;
2779 let src =
2780 unsafe { core::slice::from_raw_parts(&event as *const SiloEvent as *const u8, EVT_SIZE) };
2781 user.copy_from(src);
2782 Ok(0)
2783}
2784
2785pub fn sys_silo_suspend(handle: u64) -> Result<u64, SyscallError> {
2787 require_silo_admin()?;
2788 let required = CapPermissions {
2789 read: false,
2790 write: false,
2791 execute: true,
2792 grant: false,
2793 revoke: false,
2794 };
2795 let silo_id = resolve_silo_handle(handle, required)?;
2796
2797 let tasks = {
2801 let mut mgr = SILO_MANAGER.lock();
2802 let silo = mgr.get_mut(silo_id)?;
2803 match silo.state {
2804 SiloState::Running => {
2805 silo.state = SiloState::Paused;
2806 silo.tasks.clone()
2807 }
2808 _ => return Err(SyscallError::InvalidArgument),
2809 }
2810 };
2811
2812 for tid in &tasks {
2813 crate::process::suspend_task(*tid);
2814 }
2815
2816 let mut mgr = SILO_MANAGER.lock();
2817 mgr.push_event(SiloEvent {
2818 silo_id: silo_id.into(),
2819 kind: SiloEventKind::Paused,
2820 data0: 0,
2821 data1: 0,
2822 tick: crate::process::scheduler::ticks(),
2823 });
2824
2825 Ok(0)
2826}
2827
2828pub fn sys_silo_resume(handle: u64) -> Result<u64, SyscallError> {
2830 require_silo_admin()?;
2831 let required = CapPermissions {
2832 read: false,
2833 write: false,
2834 execute: true,
2835 grant: false,
2836 revoke: false,
2837 };
2838 let silo_id = resolve_silo_handle(handle, required)?;
2839
2840 let tasks = {
2841 let mut mgr = SILO_MANAGER.lock();
2842 let silo = mgr.get_mut(silo_id)?;
2843 match silo.state {
2844 SiloState::Paused => {
2845 silo.state = SiloState::Running;
2846 silo.tasks.clone()
2847 }
2848 _ => return Err(SyscallError::InvalidArgument),
2849 }
2850 };
2851
2852 for tid in &tasks {
2854 crate::process::resume_task(*tid);
2855 }
2856
2857 let mut mgr = SILO_MANAGER.lock();
2858 mgr.push_event(SiloEvent {
2859 silo_id: silo_id.into(),
2860 kind: SiloEventKind::Resumed,
2861 data0: 0,
2862 data1: 0,
2863 tick: crate::process::scheduler::ticks(),
2864 });
2865
2866 Ok(0)
2867}
2868
2869pub fn kernel_suspend_silo(selector: &str) -> Result<u32, SyscallError> {
2874 let (silo_id, tasks) = {
2875 let mut mgr = SILO_MANAGER.lock();
2876 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
2877 let silo = mgr.get_mut(silo_id)?;
2878 match silo.state {
2879 SiloState::Running => {
2880 silo.state = SiloState::Paused;
2881 let t = silo.tasks.clone();
2882 (silo_id, t)
2883 }
2884 _ => return Err(SyscallError::InvalidArgument),
2885 }
2886 };
2887 for tid in &tasks {
2888 crate::process::suspend_task(*tid);
2889 }
2890 let mut mgr = SILO_MANAGER.lock();
2891 mgr.push_event(SiloEvent {
2892 silo_id: silo_id.into(),
2893 kind: SiloEventKind::Paused,
2894 data0: 0,
2895 data1: 0,
2896 tick: crate::process::scheduler::ticks(),
2897 });
2898 Ok(silo_id)
2899}
2900
2901pub fn kernel_resume_silo(selector: &str) -> Result<u32, SyscallError> {
2902 let (silo_id, tasks) = {
2903 let mut mgr = SILO_MANAGER.lock();
2904 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
2905 let silo = mgr.get_mut(silo_id)?;
2906 match silo.state {
2907 SiloState::Paused => {
2908 silo.state = SiloState::Running;
2909 let t = silo.tasks.clone();
2910 (silo_id, t)
2911 }
2912 _ => return Err(SyscallError::InvalidArgument),
2913 }
2914 };
2915 for tid in &tasks {
2916 crate::process::resume_task(*tid);
2917 }
2918 let mut mgr = SILO_MANAGER.lock();
2919 mgr.push_event(SiloEvent {
2920 silo_id: silo_id.into(),
2921 kind: SiloEventKind::Resumed,
2922 data0: 0,
2923 data1: 0,
2924 tick: crate::process::scheduler::ticks(),
2925 });
2926 Ok(silo_id)
2927}
2928
2929pub fn silo_detail_snapshot(selector: &str) -> Result<SiloDetailSnapshot, SyscallError> {
2930 let mgr = SILO_MANAGER.lock();
2931 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
2932 let s = mgr.get(silo_id)?;
2933 Ok(SiloDetailSnapshot {
2934 base: SiloSnapshot {
2935 id: s.id.sid,
2936 tier: s.id.tier,
2937 name: s.name.clone(),
2938 strate_label: s.strate_label.clone(),
2939 state: s.state,
2940 task_count: s.tasks.len(),
2941 mem_usage_bytes: s.mem_usage_bytes,
2942 mem_min_bytes: s.config.mem_min,
2943 mem_max_bytes: s.config.mem_max,
2944 mode: s.config.mode,
2945 graphics_flags: s.config.flags
2946 & (SILO_FLAG_GRAPHICS
2947 | SILO_FLAG_WEBRTC_NATIVE
2948 | SILO_FLAG_GRAPHICS_READ_ONLY
2949 | SILO_FLAG_WEBRTC_TURN_FORCE),
2950 graphics_max_sessions: s.config.graphics_max_sessions,
2951 graphics_session_ttl_sec: s.config.graphics_session_ttl_sec,
2952 },
2953 family: s.family,
2954 sandboxed: s.sandboxed,
2955 cpu_shares: s.config.cpu_shares,
2956 cpu_affinity_mask: s.config.cpu_affinity_mask,
2957 max_tasks: s.config.max_tasks,
2958 task_ids: s.tasks.iter().map(|t| t.as_u64()).collect(),
2959 unveil_rules: s
2960 .unveil_rules
2961 .iter()
2962 .map(|r| {
2963 let bits = (if r.rights.read { 4 } else { 0 })
2964 | (if r.rights.write { 2 } else { 0 })
2965 | (if r.rights.execute { 1 } else { 0 });
2966 (r.path.clone(), bits)
2967 })
2968 .collect(),
2969 granted_caps_count: s.granted_caps.len(),
2970 cpu_features_required: s.config.cpu_features_required,
2971 cpu_features_allowed: s.config.cpu_features_allowed,
2972 xcr0_mask: s.config.xcr0_mask,
2973 graphics_flags: s.config.flags
2974 & (SILO_FLAG_GRAPHICS
2975 | SILO_FLAG_WEBRTC_NATIVE
2976 | SILO_FLAG_GRAPHICS_READ_ONLY
2977 | SILO_FLAG_WEBRTC_TURN_FORCE),
2978 graphics_max_sessions: s.config.graphics_max_sessions,
2979 graphics_session_ttl_sec: s.config.graphics_session_ttl_sec,
2980 })
2981}
2982
2983pub fn list_events_snapshot() -> Vec<SiloEventSnapshot> {
2984 let mgr = SILO_MANAGER.lock();
2985 mgr.events
2986 .iter()
2987 .map(|e| SiloEventSnapshot {
2988 silo_id: e.silo_id,
2989 kind: e.kind,
2990 data0: e.data0,
2991 data1: e.data1,
2992 tick: e.tick,
2993 })
2994 .collect()
2995}
2996
2997pub fn list_events_for_silo(selector: &str) -> Result<Vec<SiloEventSnapshot>, SyscallError> {
2998 let mgr = SILO_MANAGER.lock();
2999 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
3000 let sid64 = silo_id as u64;
3001 Ok(mgr
3002 .events
3003 .iter()
3004 .filter(|e| e.silo_id == sid64)
3005 .map(|e| SiloEventSnapshot {
3006 silo_id: e.silo_id,
3007 kind: e.kind,
3008 data0: e.data0,
3009 data1: e.data1,
3010 tick: e.tick,
3011 })
3012 .collect())
3013}
3014
3015pub fn kernel_pledge_silo(selector: &str, mode_val: u16) -> Result<(u16, u16), SyscallError> {
3016 let new_mode = OctalMode::from_octal(mode_val);
3017 let mut mgr = SILO_MANAGER.lock();
3018 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
3019 let silo = mgr.get_mut(silo_id)?;
3020 let old_raw = silo.config.mode;
3021 silo.mode.pledge(new_mode)?;
3022 silo.config.mode = mode_val;
3023 Ok((old_raw, mode_val))
3024}
3025
3026pub fn kernel_unveil_silo(
3027 selector: &str,
3028 path: &str,
3029 rights_str: &str,
3030) -> Result<u32, SyscallError> {
3031 let rights = UnveilRights {
3032 read: rights_str.contains('r'),
3033 write: rights_str.contains('w'),
3034 execute: rights_str.contains('x'),
3035 };
3036 let mut mgr = SILO_MANAGER.lock();
3037 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
3038 let silo = mgr.get_mut(silo_id)?;
3039 if let Some(rule) = silo.unveil_rules.iter_mut().find(|r| r.path == path) {
3040 rule.rights = rights;
3041 } else {
3042 if silo
3043 .unveil_rules
3044 .push(UnveilRule {
3045 path: String::from(path),
3046 rights,
3047 })
3048 .is_err()
3049 {
3050 log::warn!("[silo] unveil_rules full (capacity=128), rule dropped");
3051 }
3052 }
3053 Ok(silo_id)
3054}
3055
3056pub fn kernel_sandbox_silo(selector: &str) -> Result<u32, SyscallError> {
3057 let mut mgr = SILO_MANAGER.lock();
3058 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
3059 let silo = mgr.get_mut(silo_id)?;
3060 silo.sandboxed = true;
3061 crate::audit::log(
3062 crate::audit::AuditCategory::Security,
3063 0,
3064 silo_id,
3065 alloc::format!("silo sandboxed"),
3066 );
3067 Ok(silo_id)
3068}
3069
3070pub fn task_silo_id(task_id: TaskId) -> Option<u32> {
3072 SILO_MANAGER.lock().silo_for_task(task_id)
3073}
3074
3075pub fn silo_output_write(silo_id: u32, data: &[u8]) {
3077 let mut mgr = SILO_MANAGER.lock();
3078 if let Ok(silo) = mgr.get_mut(silo_id) {
3079 let buf = silo
3080 .output_buf
3081 .get_or_insert_with(|| Box::new(SiloOutputBuf::new()));
3082 buf.push(data);
3083 }
3084}
3085
3086pub fn silo_output_drain(selector: &str) -> Result<Vec<u8>, SyscallError> {
3088 let mut mgr = SILO_MANAGER.lock();
3089 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
3090 let silo = mgr.get_mut(silo_id)?;
3091 let mut buf = match silo.output_buf.take() {
3092 Some(buf) => buf,
3093 None => return Ok(Vec::new()),
3094 };
3095 let out = buf.drain();
3096 silo.output_buf = Some(buf);
3097 Ok(out)
3098}
3099
3100pub fn kernel_limit_silo(selector: &str, key: &str, value: u64) -> Result<u32, SyscallError> {
3105 let mut mgr = SILO_MANAGER.lock();
3106 let silo_id = resolve_selector_to_silo_id(selector, &mgr)?;
3107 let silo = mgr.get_mut(silo_id)?;
3108 let mut next_mem_min = silo.config.mem_min;
3109 let mut next_mem_max = silo.config.mem_max;
3110 let mut next_max_tasks = silo.config.max_tasks;
3111 let mut next_cpu_shares = silo.config.cpu_shares;
3112 match key {
3113 "mem_max" => next_mem_max = value,
3114 "mem_min" => next_mem_min = value,
3115 "max_tasks" => {
3116 if value > u32::MAX as u64 {
3117 return Err(SyscallError::InvalidArgument);
3118 }
3119 next_max_tasks = value as u32;
3120 }
3121 "cpu_shares" => {
3122 if value > u32::MAX as u64 {
3123 return Err(SyscallError::InvalidArgument);
3124 }
3125 next_cpu_shares = value as u32;
3126 }
3127 _ => return Err(SyscallError::InvalidArgument),
3128 }
3129 if next_mem_max != 0 && next_mem_min > next_mem_max {
3130 return Err(SyscallError::InvalidArgument);
3131 }
3132 silo.config.mem_min = next_mem_min;
3133 silo.config.mem_max = next_mem_max;
3134 silo.config.max_tasks = next_max_tasks;
3135 silo.config.cpu_shares = next_cpu_shares;
3136 crate::audit::log(
3137 crate::audit::AuditCategory::Security,
3138 0,
3139 silo_id,
3140 alloc::format!("silo limit: {}={}", key, value),
3141 );
3142 Ok(silo_id)
3143}
3144
3145fn dump_user_fault(task_id: TaskId, reason: SiloFaultReason, extra: u64, subcode: u64, rip: u64) {
3151 let task_meta = crate::process::get_task_by_id(task_id).map(|task| {
3152 let state = task.get_state();
3153 let as_ref = task.process.address_space_arc();
3154 (
3155 task.pid,
3156 task.tid,
3157 task.name,
3158 state,
3159 as_ref.cr3().as_u64(),
3160 as_ref.is_kernel(),
3161 )
3162 });
3163
3164 if let Some((pid, tid, name, state, as_cr3, as_is_kernel)) = task_meta {
3165 crate::serial_force_println!(
3166 "\x1b[31m[handle_user_fault]\x1b[0m task={} \x1b[36mpid={}\x1b[0m tid={} name='{}' state={:?} reason={:?} \x1b[35mrip={:#x}\x1b[0m \x1b[35mextra={:#x}\x1b[0m subcode={:#x} as_cr3={:#x} as_kernel={}",
3167 task_id.as_u64(),
3168 pid,
3169 tid,
3170 name,
3171 state,
3172 reason,
3173 rip,
3174 extra,
3175 subcode,
3176 as_cr3,
3177 as_is_kernel
3178 );
3179 } else {
3180 crate::serial_force_println!(
3181 "\x1b[31m[handle_user_fault]\x1b[0m task={} reason={:?} \x1b[35mrip={:#x}\x1b[0m \x1b[35mextra={:#x}\x1b[0m subcode={:#x} (task metadata unavailable)",
3182 task_id.as_u64(),
3183 reason,
3184 rip,
3185 extra,
3186 subcode
3187 );
3188 }
3189
3190 if reason == SiloFaultReason::PageFault {
3191 let present = (subcode & 0x1) != 0;
3192 let write = (subcode & 0x2) != 0;
3193 let user = (subcode & 0x4) != 0;
3194 let reserved = (subcode & 0x8) != 0;
3195 let instr_fetch = (subcode & 0x10) != 0;
3196 let pkey = (subcode & 0x20) != 0;
3197 let shadow_stack = (subcode & 0x40) != 0;
3198 let sgx = (subcode & 0x8000) != 0;
3199 crate::serial_force_println!(
3200 "\x1b[31m[handle_user_fault]\x1b[0m \x1b[31mpagefault\x1b[0m \x1b[35maddr={:#x}\x1b[0m \x1b[35mrip={:#x}\x1b[0m ec={:#x} present={} write={} user={} reserved={} ifetch={} pkey={} shadow_stack={} sgx={}",
3201 extra,
3202 rip,
3203 subcode,
3204 present,
3205 write,
3206 user,
3207 reserved,
3208 instr_fetch,
3209 pkey,
3210 shadow_stack,
3211 sgx
3212 );
3213 if user && extra < 0x1000 {
3214 crate::serial_force_println!(
3215 "\x1b[31m[handle_user_fault]\x1b[0m \x1b[33mhint: low user address fault ({:#x}) -> probable NULL/near-NULL dereference\x1b[0m",
3216 extra
3217 );
3218 }
3219 } else {
3220 crate::serial_force_println!(
3221 "\x1b[31m[handle_user_fault]\x1b[0m \x1b[31mfault detail\x1b[0m \x1b[35mrip={:#x}\x1b[0m code={:#x}",
3222 rip,
3223 subcode
3224 );
3225 }
3226}
3227
3228pub fn handle_user_fault(
3230 task_id: TaskId,
3231 reason: SiloFaultReason,
3232 extra: u64,
3233 subcode: u64,
3234 rip: u64,
3235) {
3236 crate::serial_force_println!(
3238 "\x1b[31;1m[handle_user_fault] CRITICAL FAULT\x1b[0m: tid={} reason={:?} rip={:#x} addr={:#x} err={:#x}",
3239 task_id.as_u64(),
3240 reason,
3241 rip,
3242 extra,
3243 subcode
3244 );
3245
3246 dump_user_fault(task_id, reason, extra, subcode, rip);
3247
3248 let tasks = {
3250 let mut mgr = SILO_MANAGER.lock();
3251 let silo_id = match mgr.silo_for_task(task_id) {
3252 Some(id) => id,
3253 None => {
3254 crate::serial_force_println!(
3255 "[handle_user_fault] Non-silo task {} crashed (reason={:?})! Killing it.",
3256 task_id.as_u64(),
3257 reason
3258 );
3259 drop(mgr);
3260 crate::process::kill_task(task_id);
3261 return;
3262 }
3263 };
3264 let mut tasks = HVec::<TaskId, 64>::new();
3265 {
3266 if let Ok(silo) = mgr.get_mut(silo_id) {
3267 silo.state = SiloState::Crashed;
3268 tasks = silo.tasks.clone();
3269 silo.tasks.clear();
3270 silo.event_seq = silo.event_seq.wrapping_add(1);
3271 }
3272 }
3273 for tid in &tasks {
3274 mgr.unmap_task(*tid);
3275 }
3276 mgr.push_event(SiloEvent {
3277 silo_id: silo_id.into(),
3278 kind: SiloEventKind::Crashed,
3279 data0: pack_fault(reason, subcode),
3280 data1: extra,
3281 tick: crate::process::scheduler::ticks(),
3282 });
3283 tasks
3284 };
3285
3286 for tid in &tasks {
3287 crate::process::kill_task(*tid);
3288 }
3289 if !tasks.contains(&task_id) {
3290 crate::process::kill_task(task_id);
3291 }
3292}