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