moros 0.6.0

MOROS: Obscure Rust Operating System
Documentation
diff --git a/src/main.rs b/src/main.rs
index 585e43e..8fbcf4e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,6 +11,28 @@ entry_point!(main);
 
 fn main(boot_info: &'static BootInfo) -> ! {
     moros::init(boot_info);
+
+    use x86_64::VirtAddr;
+    let mut mapper = unsafe { sys::mem::mapper(VirtAddr::new(boot_info.physical_memory_offset)) };
+    let mut frame_allocator = unsafe { sys::mem::BootInfoFrameAllocator::init(&boot_info.memory_map) };
+    let process = sys::process::Process::create(
+        &mut mapper,
+        &mut frame_allocator,
+        &[
+            // Infinite nop
+            // 0x90, 0x90, 0x90, 0xEB, 0xFB
+
+            // Infinite syscall to sleep (1.0 second at a time)
+            0xb8, 0x00, 0x00, 0x00, 0x00,
+            0x48, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f,
+            0xbe, 0x00, 0x00, 0x00, 0x00,
+            0xba, 0x00, 0x00, 0x00, 0x00,
+            0xcd, 0x80,
+            0xeb, 0xe3,
+        ]
+    );
+    process.switch();
+
     loop {
         let bootrc = "/ini/boot.sh";
         if sys::fs::File::open(bootrc).is_some() {
diff --git a/src/sys/gdt.rs b/src/sys/gdt.rs
index e1cce94..32cb956 100644
--- a/src/sys/gdt.rs
+++ b/src/sys/gdt.rs
@@ -1,17 +1,31 @@
 use lazy_static::lazy_static;
 use x86_64::VirtAddr;
-use x86_64::instructions::segmentation::{CS, Segment};
+use x86_64::instructions::segmentation::{CS, DS, Segment};
 use x86_64::instructions::tables::load_tss;
-use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
+use x86_64::structures::gdt::{Descriptor, DescriptorFlags, GlobalDescriptorTable, SegmentSelector};
 use x86_64::structures::tss::TaskStateSegment;
 
+const STACK_SIZE: usize = 4096;
 pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
+pub const PAGE_FAULT_IST_INDEX: u16 = 1;
+pub const GENERAL_PROTECTION_FAULT_IST_INDEX: u16 = 2;
 
 lazy_static! {
     static ref TSS: TaskStateSegment = {
         let mut tss = TaskStateSegment::new();
+        tss.privilege_stack_table[0] = {
+            static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
+            VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
+        };
         tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
-            const STACK_SIZE: usize = 4096;
+            static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
+            VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
+        };
+        tss.interrupt_stack_table[PAGE_FAULT_IST_INDEX as usize] = {
+            static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
+            VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
+        };
+        tss.interrupt_stack_table[GENERAL_PROTECTION_FAULT_IST_INDEX as usize] = {
             static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
             VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
         };
@@ -20,29 +34,33 @@ lazy_static! {
 }
 
 lazy_static! {
-    static ref GDT: (GlobalDescriptorTable, Selectors) = {
+    pub static ref GDT: (GlobalDescriptorTable, Selectors) = {
         let mut gdt = GlobalDescriptorTable::new();
-        let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
-        let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
-        (
-            gdt,
-            Selectors {
-                code_selector,
-                tss_selector,
-            },
-        )
+        let kernel_data_flags = DescriptorFlags::USER_SEGMENT | DescriptorFlags::PRESENT | DescriptorFlags::WRITABLE;
+
+        let code = gdt.add_entry(Descriptor::kernel_code_segment());
+        let data = gdt.add_entry(Descriptor::UserSegment(kernel_data_flags.bits()));
+        let tss = gdt.add_entry(Descriptor::tss_segment(&TSS));
+        let user_code = gdt.add_entry(Descriptor::user_code_segment());
+        let user_data = gdt.add_entry(Descriptor::user_data_segment());
+
+        (gdt, Selectors { code, data, tss, user_code, user_data })
     };
 }
 
-struct Selectors {
-    code_selector: SegmentSelector,
-    tss_selector: SegmentSelector,
+pub struct Selectors {
+    code: SegmentSelector,
+    data: SegmentSelector,
+    tss: SegmentSelector,
+    pub user_code: SegmentSelector,
+    pub user_data: SegmentSelector,
 }
 
 pub fn init() {
     GDT.0.load();
     unsafe {
-        CS::set_reg(GDT.1.code_selector);
-        load_tss(GDT.1.tss_selector);
+        CS::set_reg(GDT.1.code);
+        DS::set_reg(GDT.1.data);
+        load_tss(GDT.1.tss);
     }
 }
diff --git a/src/sys/idt.rs b/src/sys/idt.rs
index 58f18f3..9060344 100644
--- a/src/sys/idt.rs
+++ b/src/sys/idt.rs
@@ -3,7 +3,7 @@ use lazy_static::lazy_static;
 use spin::Mutex;
 use x86_64::instructions::interrupts;
 use x86_64::instructions::port::Port;
-use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
+use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
 
 const PIC1: u16 = 0x21;
 const PIC2: u16 = 0xA1;
@@ -27,7 +27,19 @@ lazy_static! {
         let mut idt = InterruptDescriptorTable::new();
         idt.breakpoint.set_handler_fn(breakpoint_handler);
         unsafe {
-            idt.double_fault.set_handler_fn(double_fault_handler).set_stack_index(sys::gdt::DOUBLE_FAULT_IST_INDEX);
+            idt.double_fault.
+                set_handler_fn(double_fault_handler).
+                set_stack_index(sys::gdt::DOUBLE_FAULT_IST_INDEX);
+            idt.page_fault.
+                set_handler_fn(page_fault_handler).
+                set_stack_index(sys::gdt::PAGE_FAULT_IST_INDEX);
+            idt.general_protection_fault.
+                set_handler_fn(general_protection_fault_handler).
+                set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST_INDEX);
+            idt[0x80].
+                set_handler_fn(core::mem::transmute(wrapped_syscall_handler as *mut fn())).
+                set_stack_index(sys::gdt::DOUBLE_FAULT_IST_INDEX).
+                set_privilege_level(x86_64::PrivilegeLevel::Ring3);
         }
         idt[interrupt_index(0) as usize].set_handler_fn(irq0_handler);
         idt[interrupt_index(1) as usize].set_handler_fn(irq1_handler);
@@ -45,7 +57,8 @@ lazy_static! {
         idt[interrupt_index(13) as usize].set_handler_fn(irq13_handler);
         idt[interrupt_index(14) as usize].set_handler_fn(irq14_handler);
         idt[interrupt_index(15) as usize].set_handler_fn(irq15_handler);
-        idt[0x80].set_handler_fn(unsafe { core::mem::transmute(wrapped_syscall_handler as *mut fn()) });
+        idt.stack_segment_fault.set_handler_fn(stack_segment_fault_handler);
+        idt.segment_not_present.set_handler_fn(segment_not_present_handler);
         idt
     };
 }
@@ -85,6 +98,25 @@ extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame,
     panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
 }
 
+extern "x86-interrupt" fn page_fault_handler(stack_frame: InterruptStackFrame, error_code: PageFaultErrorCode) {
+    let ip = stack_frame.instruction_pointer.as_ptr();
+    let inst: [u8; 8] = unsafe { core::ptr::read(ip) };
+    println!("Code: {:?}", inst);
+    panic!("EXCEPTION: PAGE FAULT\n{:#?}\n{:#?}", stack_frame, error_code);
+}
+
+extern "x86-interrupt" fn general_protection_fault_handler(stack_frame: InterruptStackFrame, _error_code: u64) {
+    panic!("EXCEPTION: GENERAL PROTECTION FAULT\n{:#?}", stack_frame);
+}
+
+extern "x86-interrupt" fn stack_segment_fault_handler(stack_frame: InterruptStackFrame, _error_code: u64) {
+    panic!("EXCEPTION: STACK SEGMENT FAULT\n{:#?}", stack_frame);
+}
+
+extern "x86-interrupt" fn segment_not_present_handler(stack_frame: InterruptStackFrame, _error_code: u64) {
+    panic!("EXCEPTION: SEGMENT NOT PRESENT\n{:#?}", stack_frame);
+}
+
 // See: https://github.com/xfoxfu/rust-xos/blob/8a07a69ef/kernel/src/interrupts/handlers.rs#L92
 #[repr(align(8), C)]
 #[derive(Debug, Clone, Default)]
@@ -167,6 +199,7 @@ extern "sysv64" fn syscall_handler(_stack_frame: &mut InterruptStackFrame, regs:
     let arg1 = regs.rdi;
     let arg2 = regs.rsi;
     let arg3 = regs.rdx;
+    printk!("DEBUG: syscall({}, {}, {}, {})\n", n, arg1, arg2, arg3);
     regs.rax = sys::syscall::dispatcher(n, arg1, arg2, arg3);
     unsafe { sys::pic::PICS.lock().notify_end_of_interrupt(0x80) };
 }
diff --git a/src/sys/process.rs b/src/sys/process.rs
index d7fd2ae..8c92de1 100644
--- a/src/sys/process.rs
+++ b/src/sys/process.rs
@@ -6,17 +6,17 @@ use spin::Mutex;
 
 lazy_static! {
     pub static ref PIDS: AtomicUsize = AtomicUsize::new(0);
-    pub static ref PROCESS: Mutex<Process> = Mutex::new(Process::new("/", None)); // TODO
+    pub static ref PROCESS: Mutex<ProcessData> = Mutex::new(ProcessData::new("/", None)); // TODO
 }
 
-pub struct Process {
+pub struct ProcessData {
     id: usize,
     env: BTreeMap<String, String>,
     dir: String,
     user: Option<String>,
 }
 
-impl Process {
+impl ProcessData {
     pub fn new(dir: &str, user: Option<&str>) -> Self {
         let id = PIDS.fetch_add(1, Ordering::SeqCst);
         let env = BTreeMap::new();
@@ -57,3 +57,96 @@ pub fn set_dir(dir: &str) {
 pub fn set_user(user: &str) {
     PROCESS.lock().user = Some(user.into())
 }
+
+
+
+use x86_64::VirtAddr;
+use x86_64::instructions::interrupts;
+use x86_64::structures::paging::{
+    Page, PageTableFlags,
+};
+use x86_64::structures::{
+    paging::{Mapper, FrameAllocator, Size4KiB},
+};
+use core::sync::atomic::AtomicU64;
+use crate::sys::gdt::GDT;
+
+// TODO: use virtual memory better, i.e don't map all
+// processes in the same page table directory
+static STACK_ADDR: AtomicU64 = AtomicU64::new(0x600_000);
+static CODE_ADDR: AtomicU64 = AtomicU64::new(0x400_000);
+
+pub struct Process {
+    stack_addr: u64,
+    code_addr: u64,
+}
+
+impl Process {
+    pub fn create(mapper: &mut impl Mapper<Size4KiB>, frame_alloc: &mut impl FrameAllocator<Size4KiB>, asm: &[u8]) -> Process {
+        const PAGE_SIZE: u64 = 1024 * 4; 
+        let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE;
+
+        let stack = STACK_ADDR.fetch_add(PAGE_SIZE, Ordering::SeqCst);
+        let frame = frame_alloc.allocate_frame().unwrap();
+        let page = Page::containing_address(VirtAddr::new(stack));
+        unsafe {
+            mapper.map_to(page, frame, flags, frame_alloc).unwrap().flush();
+        }
+
+        let code = CODE_ADDR.fetch_add(PAGE_SIZE, Ordering::SeqCst);
+        let frame = frame_alloc.allocate_frame().unwrap();
+        let page = Page::containing_address(VirtAddr::new(code));
+        unsafe {
+            mapper.map_to(page, frame, flags, frame_alloc).unwrap().flush();
+        }
+
+        unsafe {
+            let code = code as *mut u8;
+            for (i, op) in asm.iter().enumerate() {
+                core::ptr::write(code.add(i), *op);
+            }
+        }
+
+        Process {
+            stack_addr: stack,
+            code_addr: code,
+        }
+    }
+
+    pub fn switch(&self) {
+        crate::println!("DEBUG: switching to userspace");
+        let data = GDT.1.user_data.0;
+        let code = GDT.1.user_code.0;
+
+        unsafe {
+            interrupts::disable();
+
+            asm!(
+                //"mov ds, ax",
+                //"mov es, ax",
+                //"mov fs, ax",
+                //"mov gs, ax",
+
+                "push rax",
+                "push rsi",
+                "push 0x200",
+                
+                // Reenable interrupts in userspace
+                //"pushf", // Get EFLAGS
+                //"pop rax",
+                //"or rax, 0x200", // Set IF
+                //"push rax",
+                
+                //"push rcx",
+                "push rdx",
+                "push rdi",
+                "iretq",
+                in("rax") data,
+                in("rsi") self.stack_addr,
+                in("rdx") code,
+                in("rdi") self.code_addr,
+            );
+        }
+    }
+}
+
diff --git a/src/sys/syscall/service.rs b/src/sys/syscall/service.rs
index ea6204d..9a6f24f 100644
--- a/src/sys/syscall/service.rs
+++ b/src/sys/syscall/service.rs
@@ -2,6 +2,7 @@ use crate::sys;
 
 pub fn sleep(seconds: f64) {
     unsafe { asm!("sti") }; // Restore interrupts
+    printk!("DEBUG: sleep({:.2})\n", seconds);
     sys::time::sleep(seconds);
     unsafe { asm!("cli") }; // Disable interrupts
 }