moros/sys/
process.rs

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, // Page, Size4KiB,
25    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; // TODO: Increase this
34const MAX_PROC_SIZE: usize = 10 << 20; // 10 MB
35
36// TODO: Remove this when the kernel is no longer at 0x200000 in userspace.
37// Currently this address must be used by the linker for user programs that
38// need to allocate memory to avoid using kernel memory.
39static 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
51// Called during kernel heap initialization
52pub 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    // Saved scratch registers
60    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; // The first 4 handles are reserved
153    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
233// TODO: Remove this when the kernel is no longer at 0x200000 in userspace
234pub 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 { // FIXME: Uncomment to see errors
280        //let size = layout.size();
281        //let plural = if size != 1 { "s" } else { "" };
282        //debug!("Could not free {} byte{} at {:#?}", size, plural, ptr);
283    }
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!(); // The kernel switched to the child process
328        } 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        // FIXME: for now we just copy everything
351        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        //debug!("Process memory:");
367        if bin.get(0..4) == Some(&ELF_MAGIC) { // ELF binary
368            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                        // NOTE: The size of the segment in memory can be
374                        // larger than on the disk because the object can
375                        // contain uninitialized sections like ".bss" that has
376                        // a length but no data.
377                        let addr = code_addr + segment.address();
378                        let size = segment.size() as usize;
379                        /*
380                        debug!(
381                            "{:#X}..{:#X}: {} bytes for a code segment ({:#X}..{:#X}: {} bytes)",
382                            addr, addr + data.len() as u64, data.len(),
383                            segment.address(), segment.address() + segment.size(), segment.size(),
384                        );
385                        */
386                        load_binary(&mut mapper, addr, size, data)?;
387                    }
388                }
389            }
390        } else if bin.get(0..4) == Some(&BIN_MAGIC) { // Flat binary
391            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    // Switch to user mode and execute the program
429    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        // Copy args to user memory
436        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        //debug!("{:#X}..{:#X}: {} bytes for the args", args_addr, args_addr + 4096, 4096); // FIXME: args size
471        //debug!("{:#X}..{:#X}: {} bytes for the heap", heap_addr, heap_addr + heap_size as u64, heap_size);
472        //debug!("{:#X}..{:#X}: {} bytes for the stack", self.stack_addr - heap_size as u64, self.stack_addr, heap_size);
473
474        set_id(self.id); // Change PID
475
476        unsafe {
477            let (_, flags) = Cr3::read();
478            Cr3::write(self.page_table_frame, flags);
479
480            asm!(
481                "cli",        // Disable interrupts
482                "push {:r}",  // Stack segment (SS)
483                "push {:r}",  // Stack pointer (RSP)
484                "push 0x200", // RFLAGS with interrupts enabled
485                "push {:r}",  // Code segment (CS)
486                "push {:r}",  // Instruction pointer (RIP)
487                "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}