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