1use crate::api::process::ExitCode;
2use crate::sys::console::Console;
3use crate::sys::fs::{Device, Resource};
4use crate::sys;
5use crate::sys::gdt::GDT;
6use crate::sys::mem::{phys_mem_offset, with_frame_allocator};
7
8use alloc::boxed::Box;
9use alloc::collections::btree_map::BTreeMap;
10use alloc::string::{String, ToString};
11use alloc::sync::Arc;
12use alloc::vec::Vec;
13use core::alloc::{GlobalAlloc, Layout};
14use core::arch::asm;
15use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
16use lazy_static::lazy_static;
17use linked_list_allocator::LockedHeap;
18use object::{Object, ObjectSegment};
19use spin::RwLock;
20use x86_64::registers::control::Cr3;
21use x86_64::structures::idt::InterruptStackFrameValue;
22use x86_64::structures::paging::{
23 FrameAllocator, FrameDeallocator, OffsetPageTable, PageTable, PhysFrame,
24 Translate, PageTableFlags, mapper::TranslateResult
26};
27use x86_64::VirtAddr;
28
29const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
30const BIN_MAGIC: [u8; 4] = [0x7F, b'B', b'I', b'N'];
31
32const MAX_HANDLES: usize = 64;
33const MAX_PROCS: usize = 4; const MAX_PROC_SIZE: usize = 10 << 20; static USER_ADDR: u64 = 0x800000;
40
41static CODE_ADDR: AtomicU64 = AtomicU64::new(0);
42pub static PID: AtomicUsize = AtomicUsize::new(0);
43pub static MAX_PID: AtomicUsize = AtomicUsize::new(1);
44
45lazy_static! {
46 pub static ref PROCESS_TABLE: RwLock<[Box<Process>; MAX_PROCS]> = {
47 RwLock::new([(); MAX_PROCS].map(|_| Box::new(Process::new())))
48 };
49}
50
51pub fn init_process_addr(addr: u64) {
53 sys::process::CODE_ADDR.store(addr, Ordering::SeqCst);
54}
55
56#[repr(align(8), C)]
57#[derive(Debug, Clone, Copy, Default)]
58pub struct Registers {
59 pub r11: usize,
61 pub r10: usize,
62 pub r9: usize,
63 pub r8: usize,
64 pub rdi: usize,
65 pub rsi: usize,
66 pub rdx: usize,
67 pub rcx: usize,
68 pub rax: usize,
69}
70
71#[derive(Clone, Debug)]
72pub struct ProcessData {
73 env: BTreeMap<String, String>,
74 dir: String,
75 user: Option<String>,
76 handles: [Option<Box<Resource>>; MAX_HANDLES],
77}
78
79impl ProcessData {
80 pub fn new(dir: &str, user: Option<&str>) -> Self {
81 let env = BTreeMap::new();
82 let dir = dir.to_string();
83 let user = user.map(String::from);
84
85 let mut handles = [(); MAX_HANDLES].map(|_| None);
86 let stdin = Resource::Device(Device::Console(Console::new()));
87 let stdout = Resource::Device(Device::Console(Console::new()));
88 let stderr = Resource::Device(Device::Console(Console::new()));
89 let stdnull = Resource::Device(Device::Null);
90 handles[0] = Some(Box::new(stdin));
91 handles[1] = Some(Box::new(stdout));
92 handles[2] = Some(Box::new(stderr));
93 handles[3] = Some(Box::new(stdnull));
94
95 Self { env, dir, user, handles }
96 }
97}
98
99pub fn id() -> usize {
100 PID.load(Ordering::SeqCst)
101}
102
103pub fn set_id(id: usize) {
104 PID.store(id, Ordering::SeqCst)
105}
106
107pub fn env(key: &str) -> Option<String> {
108 let table = PROCESS_TABLE.read();
109 let proc = &table[id()];
110 proc.data.env.get(key).cloned()
111}
112
113pub fn envs() -> BTreeMap<String, String> {
114 let table = PROCESS_TABLE.read();
115 let proc = &table[id()];
116 proc.data.env.clone()
117}
118
119pub fn dir() -> String {
120 let table = PROCESS_TABLE.read();
121 let proc = &table[id()];
122 proc.data.dir.clone()
123}
124
125pub fn user() -> Option<String> {
126 let table = PROCESS_TABLE.read();
127 let proc = &table[id()];
128 proc.data.user.clone()
129}
130
131pub fn set_env(key: &str, val: &str) {
132 let mut table = PROCESS_TABLE.write();
133 let proc = &mut table[id()];
134 proc.data.env.insert(key.into(), val.into());
135}
136
137pub fn set_dir(dir: &str) {
138 let mut table = PROCESS_TABLE.write();
139 let proc = &mut table[id()];
140 proc.data.dir = dir.into();
141}
142
143pub fn set_user(user: &str) {
144 let mut table = PROCESS_TABLE.write();
145 let proc = &mut table[id()];
146 proc.data.user = Some(user.into())
147}
148
149pub fn create_handle(file: Resource) -> Result<usize, ()> {
150 let mut table = PROCESS_TABLE.write();
151 let proc = &mut table[id()];
152 let min = 4; let max = MAX_HANDLES;
154 for handle in min..max {
155 if proc.data.handles[handle].is_none() {
156 proc.data.handles[handle] = Some(Box::new(file));
157 return Ok(handle);
158 }
159 }
160 debug!("Could not create handle");
161 Err(())
162}
163
164pub fn update_handle(handle: usize, file: Resource) {
165 let mut table = PROCESS_TABLE.write();
166 let proc = &mut table[id()];
167 proc.data.handles[handle] = Some(Box::new(file));
168}
169
170pub fn delete_handle(handle: usize) {
171 let mut table = PROCESS_TABLE.write();
172 let proc = &mut table[id()];
173 proc.data.handles[handle] = None;
174}
175
176pub fn handle(handle: usize) -> Option<Box<Resource>> {
177 let table = PROCESS_TABLE.read();
178 let proc = &table[id()];
179 proc.data.handles[handle].clone()
180}
181
182pub fn handles() -> Vec<Option<Box<Resource>>> {
183 let table = PROCESS_TABLE.read();
184 let proc = &table[id()];
185 proc.data.handles.to_vec()
186}
187
188pub fn code_addr() -> u64 {
189 let table = PROCESS_TABLE.read();
190 let proc = &table[id()];
191 proc.code_addr
192}
193
194pub fn set_code_addr(addr: u64) {
195 let mut table = PROCESS_TABLE.write();
196 let proc = &mut table[id()];
197 proc.code_addr = addr;
198}
199
200pub fn ptr_from_addr(addr: u64) -> *mut u8 {
201 let base = code_addr();
202 if addr < base {
203 (base + addr) as *mut u8
204 } else {
205 addr as *mut u8
206 }
207}
208
209pub fn registers() -> Registers {
210 let table = PROCESS_TABLE.read();
211 let proc = &table[id()];
212 proc.registers
213}
214
215pub fn set_registers(regs: Registers) {
216 let mut table = PROCESS_TABLE.write();
217 let proc = &mut table[id()];
218 proc.registers = regs
219}
220
221pub fn stack_frame() -> InterruptStackFrameValue {
222 let table = PROCESS_TABLE.read();
223 let proc = &table[id()];
224 proc.stack_frame.unwrap()
225}
226
227pub fn set_stack_frame(stack_frame: InterruptStackFrameValue) {
228 let mut table = PROCESS_TABLE.write();
229 let proc = &mut table[id()];
230 proc.stack_frame = Some(stack_frame);
231}
232
233pub fn is_userspace(addr: u64) -> bool {
235 USER_ADDR <= addr && addr <= USER_ADDR + MAX_PROC_SIZE as u64
236}
237
238pub fn exit() {
239 let table = PROCESS_TABLE.read();
240 let proc = &table[id()];
241
242 MAX_PID.fetch_sub(1, Ordering::SeqCst);
243 set_id(proc.parent_id);
244
245 proc.free_pages();
246 unsafe {
247 let (_, flags) = Cr3::read();
248 Cr3::write(page_table_frame(), flags);
249
250 with_frame_allocator(|allocator| {
251 allocator.deallocate_frame(proc.page_table_frame);
252 });
253 }
254}
255
256unsafe fn page_table_frame() -> PhysFrame {
257 let table = PROCESS_TABLE.read();
258 let proc = &table[id()];
259 proc.page_table_frame
260}
261
262pub unsafe fn page_table() -> &'static mut PageTable {
263 sys::mem::create_page_table(page_table_frame())
264}
265
266pub unsafe fn alloc(layout: Layout) -> *mut u8 {
267 let table = PROCESS_TABLE.read();
268 let proc = &table[id()];
269 proc.allocator.alloc(layout)
270}
271
272pub unsafe fn free(ptr: *mut u8, layout: Layout) {
273 let table = PROCESS_TABLE.read();
274 let proc = &table[id()];
275 let bottom = proc.allocator.lock().bottom();
276 let top = proc.allocator.lock().top();
277 if bottom <= ptr && ptr < top {
278 proc.allocator.dealloc(ptr, layout);
279 } else { }
284}
285
286#[derive(Clone)]
287pub struct Process {
288 id: usize,
289 parent_id: usize,
290 code_addr: u64,
291 stack_addr: u64,
292 entry_point_addr: u64,
293 page_table_frame: PhysFrame,
294 stack_frame: Option<InterruptStackFrameValue>,
295 registers: Registers,
296 data: ProcessData,
297 allocator: Arc<LockedHeap>,
298}
299
300impl Process {
301 pub fn new() -> Self {
302 Self {
303 id: 0,
304 parent_id: 0,
305 code_addr: 0,
306 stack_addr: 0,
307 entry_point_addr: 0,
308 stack_frame: None,
309 page_table_frame: Cr3::read().0,
310 registers: Registers::default(),
311 data: ProcessData::new("/", None),
312 allocator: Arc::new(LockedHeap::empty()),
313 }
314 }
315
316 pub fn spawn(
317 bin: &[u8],
318 args_ptr: usize,
319 args_len: usize
320 ) -> Result<(), ExitCode> {
321 if let Ok(id) = Self::create(bin) {
322 let proc = {
323 let table = PROCESS_TABLE.read();
324 table[id].clone()
325 };
326 proc.exec(args_ptr, args_len);
327 unreachable!(); } else {
329 Err(ExitCode::ExecError)
330 }
331 }
332
333 fn create(bin: &[u8]) -> Result<usize, ()> {
334 if MAX_PID.load(Ordering::SeqCst) >= MAX_PROCS {
335 return Err(());
336 }
337
338 let page_table_frame = sys::mem::with_frame_allocator(|frame_allocator| {
339 frame_allocator.allocate_frame().expect("frame allocation failed")
340 });
341
342 let page_table = unsafe {
343 sys::mem::create_page_table(page_table_frame)
344 };
345
346 let kernel_page_table = unsafe {
347 sys::mem::active_page_table()
348 };
349
350 let pages = page_table.iter_mut().zip(kernel_page_table.iter());
352 for (user_page, kernel_page) in pages {
353 *user_page = kernel_page.clone();
354 }
355
356 let mut mapper = unsafe {
357 OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset()))
358 };
359
360 let proc_size = MAX_PROC_SIZE as u64;
361 let code_addr = CODE_ADDR.fetch_add(proc_size, Ordering::SeqCst);
362 let stack_addr = code_addr + proc_size - 4096;
363
364 let mut entry_point_addr = 0;
365
366 if bin.get(0..4) == Some(&ELF_MAGIC) { if let Ok(obj) = object::File::parse(bin) {
369 entry_point_addr = obj.entry();
370
371 for segment in obj.segments() {
372 if let Ok(data) = segment.data() {
373 let addr = code_addr + segment.address();
378 let size = segment.size() as usize;
379 load_binary(&mut mapper, addr, size, data)?;
387 }
388 }
389 }
390 } else if bin.get(0..4) == Some(&BIN_MAGIC) { load_binary(&mut mapper, code_addr, bin.len() - 4, &bin[4..])?;
392 } else {
393 return Err(());
394 }
395
396 let parent = {
397 let process_table = PROCESS_TABLE.read();
398 process_table[id()].clone()
399 };
400
401 let data = parent.data.clone();
402 let registers = parent.registers;
403 let stack_frame = parent.stack_frame;
404
405 let allocator = Arc::new(LockedHeap::empty());
406
407 let id = MAX_PID.fetch_add(1, Ordering::SeqCst);
408 let parent_id = parent.id;
409 let proc = Process {
410 id,
411 parent_id,
412 code_addr,
413 stack_addr,
414 entry_point_addr,
415 page_table_frame,
416 data,
417 stack_frame,
418 registers,
419 allocator,
420 };
421
422 let mut process_table = PROCESS_TABLE.write();
423 process_table[id] = Box::new(proc);
424
425 Ok(id)
426 }
427
428 fn exec(&self, args_ptr: usize, args_len: usize) {
430 let page_table = unsafe { sys::process::page_table() };
431 let mut mapper = unsafe {
432 OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset()))
433 };
434
435 let args_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2;
437 sys::mem::alloc_pages(&mut mapper, args_addr, 1).
438 expect("proc args alloc");
439 let args: &[&str] = unsafe {
440 let ptr = ptr_from_addr(args_ptr as u64) as usize;
441 core::slice::from_raw_parts(ptr as *const &str, args_len)
442 };
443 let mut addr = args_addr;
444 let vec: Vec<&str> = args.iter().map(|arg| {
445 let ptr = addr as *mut u8;
446 addr += arg.len() as u64;
447 unsafe {
448 let s = core::slice::from_raw_parts_mut(ptr, arg.len());
449 s.copy_from_slice(arg.as_bytes());
450 core::str::from_utf8_unchecked(s)
451 }
452 }).collect();
453 let align = core::mem::align_of::<&str>() as u64;
454 addr += align - (addr % align);
455 let args = vec.as_slice();
456 let ptr = addr as *mut &str;
457 let args: &[&str] = unsafe {
458 let s = core::slice::from_raw_parts_mut(ptr, args.len());
459 s.copy_from_slice(args);
460 s
461 };
462 let args_ptr = args.as_ptr() as u64;
463
464 let heap_addr = addr + 4096;
465 let heap_size = ((self.stack_addr - heap_addr) / 2) as usize;
466 unsafe {
467 self.allocator.lock().init(heap_addr as *mut u8, heap_size);
468 }
469
470 set_id(self.id); unsafe {
477 let (_, flags) = Cr3::read();
478 Cr3::write(self.page_table_frame, flags);
479
480 asm!(
481 "cli", "push {:r}", "push {:r}", "push 0x200", "push {:r}", "push {:r}", "iretq",
488 in(reg) GDT.1.user_data.0,
489 in(reg) self.stack_addr,
490 in(reg) GDT.1.user_code.0,
491 in(reg) self.code_addr + self.entry_point_addr,
492 in("rdi") args_ptr,
493 in("rsi") args_len,
494 );
495 }
496 }
497
498 fn mapper(&self) -> OffsetPageTable<'_> {
499 let page_table = unsafe {
500 sys::mem::create_page_table(self.page_table_frame)
501 };
502 unsafe {
503 OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset()))
504 }
505 }
506
507 fn free_pages(&self) {
508 let mut mapper = self.mapper();
509
510 let size = MAX_PROC_SIZE;
511 sys::mem::free_pages(&mut mapper, self.code_addr, size);
512
513 let addr = USER_ADDR;
514 match mapper.translate(VirtAddr::new(addr)) {
515 TranslateResult::Mapped { frame: _, offset: _, flags } => {
516 if flags.contains(PageTableFlags::USER_ACCESSIBLE) {
517 sys::mem::free_pages(&mut mapper, addr, size);
518 }
519 }
520 _ => {}
521 }
522 }
523}
524
525fn load_binary(
526 mapper: &mut OffsetPageTable, addr: u64, size: usize, buf: &[u8]
527) -> Result<(), ()> {
528 debug_assert!(size >= buf.len());
529 sys::mem::alloc_pages(mapper, addr, size)?;
530 let src = buf.as_ptr();
531 let dst = addr as *mut u8;
532 unsafe {
533 core::ptr::copy_nonoverlapping(src, dst, buf.len());
534 if size > buf.len() {
535 core::ptr::write_bytes(dst.add(buf.len()), 0, size - buf.len());
536 }
537 }
538 Ok(())
539}