1use crate::{
20 process::{current_pid, get_all_tasks, get_parent_pid},
21 silo,
22 syscall::error::SyscallError,
23 vfs::scheme::{
24 finalize_pseudo_stat, DirEntry, FileFlags, FileStat, OpenFlags, OpenResult, Scheme,
25 DEV_PROCFS, DT_DIR, DT_REG,
26 },
27};
28use alloc::{format, string::String, sync::Arc, vec::Vec};
29use core::fmt::Write;
30
31pub struct ProcScheme {
33 }
35
36impl ProcScheme {
37 pub fn new() -> Self {
39 ProcScheme {}
40 }
41
42 fn get_entry(&self, path: &str) -> Result<ProcEntry, SyscallError> {
44 if path == "self" || path == "self/" {
46 if let Some(pid) = current_pid() {
47 return Ok(ProcEntry::File(format!("{}\n", pid)));
48 }
49 return Err(SyscallError::NotFound);
50 }
51
52 if path.starts_with("self/") {
54 let subpath = &path[5..];
55 if let Some(pid) = current_pid() {
56 return self.get_process_entry(pid as u64, subpath);
57 }
58 return Err(SyscallError::NotFound);
59 }
60
61 if let Some(pid_end) = path.find('/') {
63 let pid_str = &path[..pid_end];
64 let subpath = &path[pid_end + 1..];
65 if let Ok(pid) = pid_str.parse::<u64>() {
66 return self.get_process_entry(pid, subpath);
67 }
68 }
69
70 if path.parse::<u64>().is_ok() {
72 return Ok(ProcEntry::Directory);
73 }
74
75 match path {
77 "" | "/" => Ok(ProcEntry::Directory),
78 "cpuinfo" => Ok(ProcEntry::File(self.get_cpuinfo())),
79 "meminfo" => Ok(ProcEntry::File(self.get_meminfo())),
80 "version" => Ok(ProcEntry::File(self.get_version())),
81 "silos" => Ok(ProcEntry::File(self.get_silos())),
82 _ => Err(SyscallError::NotFound),
83 }
84 }
85
86 fn get_process_entry(&self, pid: u64, subpath: &str) -> Result<ProcEntry, SyscallError> {
88 let tasks = get_all_tasks().ok_or(SyscallError::NotFound)?;
89 let task = tasks
90 .iter()
91 .find(|t| t.pid as u64 == pid)
92 .ok_or(SyscallError::NotFound)?;
93
94 match subpath {
95 "" | "/" => Ok(ProcEntry::Directory),
96 "status" => Ok(ProcEntry::File(self.get_process_status(task))),
97 "cmdline" => Ok(ProcEntry::File(format!("{}\n", task.name))),
98 _ => Err(SyscallError::NotFound),
99 }
100 }
101
102 fn get_cpuinfo(&self) -> String {
104 let mut output = String::new();
105 let cpu_count = crate::arch::x86_64::percpu::get_cpu_count();
106 let host = crate::arch::x86_64::cpuid::host();
107 let flags = crate::arch::x86_64::cpuid::features_to_flags_string(host.features);
108 let has_fpu = host
109 .features
110 .contains(crate::arch::x86_64::cpuid::CpuFeatures::FPU);
111
112 for i in 0..cpu_count {
113 let _ = writeln!(output, "processor\t: {}", i);
114 let _ = writeln!(output, "vendor_id\t: {}", host.vendor_string());
115 let _ = writeln!(output, "cpu family\t: {}", host.family);
116 let _ = writeln!(output, "model\t\t: {}", host.model);
117 let _ = writeln!(output, "model name\t: {}", host.model_name_str());
118 let _ = writeln!(output, "stepping\t: {}", host.stepping);
119 let _ = writeln!(output, "cpu MHz\t\t: 2400.000");
120 let _ = writeln!(output, "cache size\t: 4096 KB");
121 let _ = writeln!(output, "physical id\t: {}", i);
122 let _ = writeln!(output, "siblings\t: 1");
123 let _ = writeln!(output, "core id\t\t: {}", i);
124 let _ = writeln!(output, "cpu cores\t: 1");
125 let _ = writeln!(output, "apicid\t\t: {}", i);
126 let _ = writeln!(output, "fpu\t\t: {}", if has_fpu { "yes" } else { "no" });
127 let _ = writeln!(
128 output,
129 "fpu_exception\t: {}",
130 if has_fpu { "yes" } else { "no" }
131 );
132 let _ = writeln!(output, "cpuid level\t: 13");
133 let _ = writeln!(output, "wp\t\t: yes");
134 let _ = writeln!(output, "flags\t\t: {}", flags);
135 let _ = writeln!(output, "bogomips\t: 4800.00");
136 let _ = writeln!(output, "clflush size\t: 64");
137 let _ = writeln!(output, "cache_alignment\t: 64");
138 let _ = writeln!(output, "address sizes\t: 40 bits physical, 48 bits virtual");
139 let _ = writeln!(output, "power management:\n");
140 let _ = writeln!(output, "");
141 }
142
143 output
144 }
145
146 fn get_meminfo(&self) -> String {
148 let mut output = String::new();
150 let _ = writeln!(output, "MemTotal: {:>10} kB", 262144); let _ = writeln!(output, "MemFree: {:>10} kB", 131072);
152 let _ = writeln!(output, "MemUsed: {:>10} kB", 131072);
153 let _ = writeln!(output, "Buffers: 0 kB");
154 let _ = writeln!(output, "Cached: 0 kB");
155 let _ = writeln!(output, "SwapTotal: 0 kB");
156 let _ = writeln!(output, "SwapFree: 0 kB");
157 output
158 }
159
160 fn get_version(&self) -> String {
162 format!("Strat9-OS version 0.1.0 (Bedrock) #1 SMP x86_64 Strat9\n")
163 }
164
165 fn get_silos(&self) -> String {
167 let mut output = String::new();
168 let mut silos = silo::list_silos_snapshot();
169 silos.sort_by_key(|s| s.id);
170 let _ = writeln!(
171 output,
172 "id\tstate\ttasks\tmem_used\tmem_min\tmem_max\tgfx_flags\tgfx_sessions\tgfx_ttl\tlabel\tname"
173 );
174 for s in silos {
175 let label = s.strate_label.unwrap_or_else(|| String::from("-"));
176 let max = if s.mem_max_bytes == 0 {
177 String::from("unlimited")
178 } else {
179 format!("{}", s.mem_max_bytes)
180 };
181 let _ = writeln!(
182 output,
183 "{}\t{:?}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
184 s.id,
185 s.state,
186 s.task_count,
187 s.mem_usage_bytes,
188 s.mem_min_bytes,
189 max,
190 s.graphics_flags,
191 s.graphics_max_sessions,
192 s.graphics_session_ttl_sec,
193 label,
194 s.name
195 );
196 }
197 output
198 }
199
200 fn get_process_status(&self, task: &Arc<crate::process::task::Task>) -> String {
202 let mut output = String::new();
203
204 let _ = writeln!(output, "Name:\t{}", task.name);
205 let _ = writeln!(output, "State:\tR (running)");
206 let _ = writeln!(output, "Tgid:\t{}", task.tgid);
207 let _ = writeln!(output, "Pid:\t{}", task.pid);
208 let _ = writeln!(
209 output,
210 "PPid:\t{}",
211 get_parent_pid(task.id).map(|p| p as u64).unwrap_or(0)
212 );
213 let _ = writeln!(
214 output,
215 "Pgid:\t{}",
216 task.pgid.load(core::sync::atomic::Ordering::Relaxed)
217 );
218 let _ = writeln!(
219 output,
220 "Sid:\t{}",
221 task.sid.load(core::sync::atomic::Ordering::Relaxed)
222 );
223 let uid = task.uid.load(core::sync::atomic::Ordering::Relaxed);
224 let euid = task.euid.load(core::sync::atomic::Ordering::Relaxed);
225 let gid = task.gid.load(core::sync::atomic::Ordering::Relaxed);
226 let egid = task.egid.load(core::sync::atomic::Ordering::Relaxed);
227 let _ = writeln!(output, "Uid:\t{}\t{}\t{}\t{}", uid, euid, euid, euid);
228 let _ = writeln!(output, "Gid:\t{}\t{}\t{}\t{}", gid, egid, egid, egid);
229 let _ = writeln!(output, "Threads:\t1");
230 if let Some((silo_id, label, mem_used, mem_min, mem_max)) =
231 silo::silo_info_for_task(task.id)
232 {
233 let label = label.unwrap_or_else(|| String::from("-"));
234 let _ = writeln!(output, "SiloId:\t{}", silo_id);
235 let _ = writeln!(output, "SiloLabel:\t{}", label);
236 let _ = writeln!(output, "SiloMemUsed:\t{}", mem_used);
237 let _ = writeln!(output, "SiloMemMin:\t{}", mem_min);
238 if mem_max == 0 {
239 let _ = writeln!(output, "SiloMemMax:\tunlimited");
240 } else {
241 let _ = writeln!(output, "SiloMemMax:\t{}", mem_max);
242 }
243 }
244
245 output
246 }
247}
248
249#[allow(dead_code)]
251enum ProcEntry {
252 File(String),
253 Directory,
254}
255
256const KIND_PROC_DIR: u64 = 1; const KIND_PROC_STATUS: u64 = 2; const KIND_PROC_CMDLINE: u64 = 3; impl ProcScheme {
263 fn encode_id(kind: u64, pid: u64) -> u64 {
268 (kind << 32) | (pid & 0xFFFF_FFFF)
269 }
270
271 fn decode_id(id: u64) -> (u64, u64) {
273 (id >> 32, id & 0xFFFF_FFFF)
274 }
275}
276
277impl Scheme for ProcScheme {
278 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
280 let entry = self.get_entry(path)?;
281
282 let (flags, file_id) = match entry {
283 ProcEntry::File(_) => {
284 let id = match path {
285 "cpuinfo" => 10,
286 "meminfo" => 11,
287 "version" => 12,
288 "silos" => 13,
289 "self/status" => Self::encode_id(
290 KIND_PROC_STATUS,
291 current_pid().map(|p| p as u64).unwrap_or(0),
292 ),
293 "self/cmdline" => Self::encode_id(
294 KIND_PROC_CMDLINE,
295 current_pid().map(|p| p as u64).unwrap_or(0),
296 ),
297 _ if path.starts_with("self") => 1,
298 _ => {
299 if let Some(slash_idx) = path.find('/') {
300 let pid_str = &path[..slash_idx];
301 let sub = &path[slash_idx + 1..];
302 if let Ok(pid) = pid_str.parse::<u64>() {
303 match sub {
304 "status" => Self::encode_id(KIND_PROC_STATUS, pid),
305 "cmdline" => Self::encode_id(KIND_PROC_CMDLINE, pid),
306 _ => 0xFFFF,
307 }
308 } else {
309 0xFFFF
310 }
311 } else {
312 0xFFFF
313 }
314 }
315 };
316 (FileFlags::empty(), id)
317 }
318 ProcEntry::Directory => {
319 let id = if path.is_empty() || path == "/" {
320 0
321 } else if path == "self" || path == "self/" {
322 1
323 } else if let Ok(pid) = path.parse::<u64>() {
324 Self::encode_id(KIND_PROC_DIR, pid)
325 } else {
326 2
327 };
328 (FileFlags::DIRECTORY, id)
329 }
330 };
331
332 if file_id == 0xFFFF {
333 return Err(SyscallError::NotFound);
334 }
335
336 Ok(OpenResult {
337 file_id,
338 size: None,
339 flags,
340 })
341 }
342
343 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
345 let (kind, pid) = Self::decode_id(file_id);
346 let content = if file_id == 0 {
347 let mut list = String::from("self\ncpuinfo\nmeminfo\nversion\n");
349 if let Some(tasks) = get_all_tasks() {
350 for task in tasks {
351 let _ = writeln!(list, "{}", task.pid);
352 }
353 }
354 list
355 } else if file_id == 1 || kind == KIND_PROC_DIR {
356 String::from("status\ncmdline\n")
358 } else {
359 match (kind, file_id) {
362 (0, 10) => self.get_cpuinfo(),
363 (0, 11) => self.get_meminfo(),
364 (0, 12) => self.get_version(),
365 (0, 13) => self.get_silos(),
366 (KIND_PROC_STATUS, _) => {
367 let tasks = get_all_tasks().ok_or(SyscallError::NotFound)?;
368 let task = tasks
369 .iter()
370 .find(|t| t.pid as u64 == pid)
371 .ok_or(SyscallError::NotFound)?;
372 self.get_process_status(task)
373 }
374 (KIND_PROC_CMDLINE, _) => {
375 let tasks = get_all_tasks().ok_or(SyscallError::NotFound)?;
376 let task = tasks
377 .iter()
378 .find(|t| t.pid as u64 == pid)
379 .ok_or(SyscallError::NotFound)?;
380 format!("{}\n", task.name)
381 }
382 _ => return Err(SyscallError::IoError),
383 }
384 };
385
386 if offset >= content.len() as u64 {
387 return Ok(0);
388 }
389
390 let start = offset as usize;
391 let end = core::cmp::min(start + buf.len(), content.len());
392 let to_copy = end - start;
393
394 buf[..to_copy].copy_from_slice(&content.as_bytes()[start..end]);
395 Ok(to_copy)
396 }
397
398 fn write(&self, _file_id: u64, _offset: u64, _buf: &[u8]) -> Result<usize, SyscallError> {
400 Err(SyscallError::PermissionDenied)
401 }
402
403 fn close(&self, _file_id: u64) -> Result<(), SyscallError> {
405 Ok(())
406 }
407
408 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
410 let (kind, _pid) = Self::decode_id(file_id);
411 let is_dir = file_id == 0 || file_id == 1 || kind == KIND_PROC_DIR;
412 let mut st = if is_dir {
413 FileStat {
414 st_ino: file_id,
415 st_mode: 0o040555,
416 st_nlink: 2,
417 st_size: 0,
418 st_blksize: 512,
419 st_blocks: 0,
420 ..FileStat::zeroed()
421 }
422 } else {
423 FileStat {
424 st_ino: file_id,
425 st_mode: 0o100444,
426 st_nlink: 1,
427 st_size: 0,
428 st_blksize: 512,
429 st_blocks: 0,
430 ..FileStat::zeroed()
431 }
432 };
433
434 let (kind, pid) = Self::decode_id(file_id);
435 let per_process_node =
436 kind == KIND_PROC_DIR || kind == KIND_PROC_STATUS || kind == KIND_PROC_CMDLINE;
437 if per_process_node && pid != 0 {
438 if let Some(tasks) = get_all_tasks() {
439 if let Some(task) = tasks.iter().find(|t| t.pid as u64 == pid) {
440 st.st_uid = task.uid.load(core::sync::atomic::Ordering::Relaxed);
441 st.st_gid = task.gid.load(core::sync::atomic::Ordering::Relaxed);
442 }
443 }
444 }
445 Ok(finalize_pseudo_stat(st, DEV_PROCFS, 0))
446 }
447
448 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
450 let (kind, pid) = Self::decode_id(file_id);
451 if file_id == 0 {
452 let mut entries = Vec::new();
453 entries.push(DirEntry {
454 ino: 1,
455 file_type: DT_DIR,
456 name: String::from("self"),
457 });
458 entries.push(DirEntry {
459 ino: 10,
460 file_type: DT_REG,
461 name: String::from("cpuinfo"),
462 });
463 entries.push(DirEntry {
464 ino: 11,
465 file_type: DT_REG,
466 name: String::from("meminfo"),
467 });
468 entries.push(DirEntry {
469 ino: 12,
470 file_type: DT_REG,
471 name: String::from("version"),
472 });
473 entries.push(DirEntry {
474 ino: 13,
475 file_type: DT_REG,
476 name: String::from("silos"),
477 });
478 if let Some(tasks) = get_all_tasks() {
479 for task in tasks {
480 entries.push(DirEntry {
481 ino: Self::encode_id(KIND_PROC_DIR, task.pid as u64),
482 file_type: DT_DIR,
483 name: format!("{}", task.pid),
484 });
485 }
486 }
487 Ok(entries)
488 } else if file_id == 1 || kind == KIND_PROC_DIR {
489 let effective_pid = if file_id == 1 {
490 current_pid().map(|p| p as u64).unwrap_or(0)
491 } else {
492 pid
493 };
494 Ok(alloc::vec![
495 DirEntry {
496 ino: Self::encode_id(KIND_PROC_STATUS, effective_pid),
497 file_type: DT_REG,
498 name: String::from("status"),
499 },
500 DirEntry {
501 ino: Self::encode_id(KIND_PROC_CMDLINE, effective_pid),
502 file_type: DT_REG,
503 name: String::from("cmdline"),
504 },
505 ])
506 } else {
507 Err(SyscallError::InvalidArgument)
508 }
509 }
510}