diff --git a/kernel/src/arch/arch.rs b/kernel/src/arch/arch.rs index de5e978..2e19b47 100644 --- a/kernel/src/arch/arch.rs +++ b/kernel/src/arch/arch.rs @@ -22,12 +22,12 @@ pub fn init<'a>( #[cfg(target_arch = "x86_64")] pub fn enter_usermode(user_rip: u64, user_rsp: u64) { - return enter_usermode_x86_64(user_rip, user_rsp); + enter_usermode_x86_64(user_rip, user_rsp); } #[cfg(target_arch = "x86_64")] -pub fn run_elf(entry_point: *const u8, heap_base: u64) { - run_elf_x86_64(entry_point, heap_base); +pub fn run_elf(file_bytes: &[u8]) { + run_elf_x86_64(file_bytes); } pub fn get_allocator<'a>() -> &'static impl GlobalAlloc { diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs index 81aafdd..5449393 100644 --- a/kernel/src/arch/syscall.rs +++ b/kernel/src/arch/syscall.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use core::{ alloc::{GlobalAlloc, Layout}, ptr::null_mut, @@ -12,11 +14,12 @@ use x86_64::{ use crate::{ arch::arch::{FRAME_ALLOCATOR, get_allocator, infinite_idle, sleep}, driver::{ - graphics::{framebuffer::with_framebuffer, primitives::rectangle_filled}, + fs::vfs::{vfs_close, vfs_lseek, vfs_open, vfs_read}, + graphics::framebuffer::with_framebuffer, keyboard::{KeyboardEvent, process_scancodes}, timer::TIMER, }, - mm::usercopy::copy_to_user, + mm::usercopy::{copy_cstr_from_user, copy_to_user}, print, println, task::scheduler::{SCHEDULER, current_pid}, util::align_up, @@ -29,7 +32,7 @@ const CLOSE: usize = 3; const STAT: usize = 4; const LSEEK: usize = 8; const MMAP: usize = 9; -const MUNMAP: usize = 9; +const MUNMAP: usize = 11; const BRK: usize = 12; const GETPID: usize = 39; const FORK: usize = 57; @@ -45,9 +48,8 @@ const CLOCK_GETTIME: usize = 228; const EXIT_GROUP: usize = 231; const KBD_READ: usize = 666; const SLEEP: usize = 909090; // zzz haha -const DRAW_PIXEL: usize = 5555; -const FRAMEBUFFER_SWAP: usize = 6666; -const DRAW_BUFFER: usize = 7777; +pub const MAP_FRAMEBUFFER: usize = 5555; +pub const FRAMEBUFFER_SWAP: usize = 6666; pub unsafe fn malloc(size: usize, align: usize) -> *mut u8 { let align = if align < 1 { @@ -84,6 +86,64 @@ pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) { unsafe { core::ptr::write_bytes(ptr, val, count) }; } +fn map_framebuffer() -> isize { + 0 +} + +fn read(ptr: isize, size: isize, nmemb: isize, fd: isize) -> isize { + let pid = current_pid().unwrap_or(0); + if pid == 0 { + return -1; + } + + SCHEDULER + .with_process(pid, |process| { + let len = size * nmemb; + let to_read = vfs_read(fd as i64, len as usize); + + if let Some(read_ptr) = to_read { + let address_space = process.address_space.as_mut().unwrap(); + if copy_to_user( + &mut address_space.mapper, + ptr as *mut u8, + read_ptr.0, + read_ptr.1, + ) + .is_err() + { + return -1; + }; + + return (read_ptr.1 as isize) / size; + } else { + return -1; + } + }) + .unwrap_or(-1) +} + +fn open(path: isize, mode: isize) -> isize { + let pid = current_pid().unwrap_or(0); + if pid == 0 { + return -1; + } + + SCHEDULER + .with_process(pid, |process| { + let address_space = process.address_space.as_mut().unwrap(); + let path = copy_cstr_from_user(&mut address_space.mapper, path as *const u8, 256)?; + let mode = copy_cstr_from_user(&mut address_space.mapper, mode as *const u8, 16)?; + + Ok::(vfs_open(&path, &mode) as isize) + }) + .unwrap_or(Err(-1)) + .unwrap_or(-1) +} + +fn close(fd: isize) -> isize { + vfs_close(fd as i64) as isize +} + fn kbd_read(user_ptr: *mut KeyboardEvent, max_events: isize) -> isize { process_scancodes(); if max_events <= 0 || user_ptr.is_null() { @@ -99,8 +159,9 @@ fn kbd_read(user_ptr: *mut KeyboardEvent, max_events: isize) -> isize { return SCHEDULER .with_process(pid as u64, |process| { let to_copy = (max_events as usize).min(process.kbd_buffer.len()); + let address_space = process.address_space.as_mut().unwrap(); if let Ok(_) = copy_to_user( - &mut process.address_space.mapper, + &mut address_space.mapper, user_ptr as *mut u8, process.kbd_buffer.as_ptr() as *const u8, to_copy * size_of::(), @@ -153,9 +214,9 @@ pub unsafe fn sbrk(increment: isize) -> isize { // TODO: do not use x86_64 only let virt_addr = VirtAddr::new(addr); let page = Page::::containing_address(virt_addr); + let address_space = process.address_space.as_mut().unwrap(); unsafe { - process - .address_space + address_space .mapper .map_to( page, @@ -198,6 +259,7 @@ pub unsafe extern "C" fn syscall_dispatch( interrupts::enable(); match num { BRK => unsafe { sbrk(arg0) }, + READ => read(arg0, arg1, arg2, arg3) as isize, WRITE => { let buf_ptr = arg1 as *const u8; let len = arg2 as usize; @@ -216,6 +278,9 @@ pub unsafe extern "C" fn syscall_dispatch( with_framebuffer(|fb| fb.swap()); 0 } + OPEN => open(arg0, arg1), + CLOSE => close(arg0), + LSEEK => vfs_lseek(arg0 as i64, arg1 as i64, arg2 as i32) as isize, EXIT => { println!("Program exit: {}", arg0); with_framebuffer(|fb| fb.swap()); @@ -226,19 +291,7 @@ pub unsafe extern "C" fn syscall_dispatch( 0 } CLOCK_GETTIME => TIMER.now().elapsed() as isize, - DRAW_PIXEL => { - with_framebuffer(|mut fb| { - rectangle_filled(&mut fb, arg0 as usize, arg1 as usize, 4, 4, arg2 as u32); - }); - 0 - } - DRAW_BUFFER => { - with_framebuffer(|fb| { - unsafe { fb.load_from_ptr(arg0 as *const u32, arg1 as usize, arg2 as usize) }; - fb.swap(); - }); - 0 - } + MAP_FRAMEBUFFER => map_framebuffer(), KBD_READ => kbd_read(arg0 as *mut KeyboardEvent, arg1), FRAMEBUFFER_SWAP => { with_framebuffer(|fb| { diff --git a/kernel/src/arch/x86_64/elf.rs b/kernel/src/arch/x86_64/elf.rs index e387208..525a171 100644 --- a/kernel/src/arch/x86_64/elf.rs +++ b/kernel/src/arch/x86_64/elf.rs @@ -4,36 +4,40 @@ use x86_64::{ structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB}, }; -use crate::{arch::arch::FRAME_ALLOCATOR, task::scheduler::SCHEDULER}; +use crate::{ + arch::arch::FRAME_ALLOCATOR, driver::elf::loader::load_file, mm::address_space::AddressSpace, + println, task::scheduler::SCHEDULER, +}; -pub fn run_elf_x86_64(entry_point: *const u8, heap_base: u64) { +pub fn run_elf_x86_64(file_bytes: &[u8]) { let stack_base: u64 = 0x0000_7fff_0000_0000; let page_count = 4096; // 16 mib let page_size = 0x1000u64; let stack_top = stack_base + (page_count as u64 * page_size); - let process_pid = SCHEDULER - .spawn_process(entry_point as u64, stack_top, heap_base) - .unwrap(); + if let Some(mut address_space) = AddressSpace::new() { + address_space.use_address_space(); - SCHEDULER.with_process(process_pid, |process| { - process.address_space.use_address_space() - }); + let (entry_point, heap_base) = load_file(&mut address_space.mapper, file_bytes); - let mut frames: Vec> = Vec::new(); - let mut frame_allocator = FRAME_ALLOCATOR.lock(); + println!("Entry point: {:?}", entry_point); - for i in 0..page_count { - let frame = frame_allocator.allocate_frame().unwrap(); - frames.push(frame); + let process_pid = SCHEDULER + .spawn_process(entry_point as u64, stack_top, heap_base) + .unwrap(); - let virt_addr = VirtAddr::new(stack_base + i as u64 * page_size); - let page = Page::::containing_address(virt_addr); + let mut frames: Vec> = Vec::new(); + let mut frame_allocator = FRAME_ALLOCATOR.lock(); - unsafe { - SCHEDULER.with_process(process_pid, |process| { - process - .address_space + for i in 0..page_count { + let frame = frame_allocator.allocate_frame().unwrap(); + frames.push(frame); + + let virt_addr = VirtAddr::new(stack_base + i as u64 * page_size); + let page = Page::::containing_address(virt_addr); + + unsafe { + address_space .mapper .map_to( page, @@ -45,10 +49,16 @@ pub fn run_elf_x86_64(entry_point: *const u8, heap_base: u64) { ) .unwrap() .flush(); - }); + } } - } - drop(frame_allocator); + drop(frame_allocator); - SCHEDULER.run_process(process_pid, entry_point); + SCHEDULER.with_process(process_pid, |process| { + process.address_space = Some(address_space) + }); + + SCHEDULER.run_process(process_pid, entry_point); + } else { + return; + }; } diff --git a/kernel/src/arch/x86_64/heap.rs b/kernel/src/arch/x86_64/heap.rs index 21db900..94f05ce 100644 --- a/kernel/src/arch/x86_64/heap.rs +++ b/kernel/src/arch/x86_64/heap.rs @@ -19,7 +19,7 @@ fn align_up(addr: usize, align: usize) -> usize { #[global_allocator] pub static ALLOCATOR: Locked = Locked::new(LinkedListAllocator::new()); -pub const HEAP_START: usize = 0x_4444_4444_0000; +pub const HEAP_START: usize = 0xffffffff90000000; pub const HEAP_SIZE: usize = 64 * 1024 * 1024; // 64 MiB pub struct LinkedNode { diff --git a/kernel/src/arch/x86_64/syscall.rs b/kernel/src/arch/x86_64/syscall.rs index 238a617..e1d9eb0 100644 --- a/kernel/src/arch/x86_64/syscall.rs +++ b/kernel/src/arch/x86_64/syscall.rs @@ -1,7 +1,6 @@ use core::arch::asm; -use lazy_static::lazy_static; -use x86_64::instructions::tlb::{self, flush_all}; +use x86_64::instructions::tlb::flush_all; use crate::arch::x86_64::gdt::{GDT, TSS}; diff --git a/kernel/src/driver/elf/header.rs b/kernel/src/driver/elf/header.rs index 5d1ed80..6c12b70 100644 --- a/kernel/src/driver/elf/header.rs +++ b/kernel/src/driver/elf/header.rs @@ -1,5 +1,7 @@ // this is not made by me, but AI, im not going to type these out... +#![allow(dead_code)] + // e_ident indices pub const EI_MAG0: usize = 0; pub const EI_MAG1: usize = 1; diff --git a/kernel/src/driver/elf/loader.rs b/kernel/src/driver/elf/loader.rs index 1195fe2..8659b79 100644 --- a/kernel/src/driver/elf/loader.rs +++ b/kernel/src/driver/elf/loader.rs @@ -30,7 +30,7 @@ pub fn load_file(mapper: &mut OffsetPageTable, elf_bytes: &[u8]) -> (*const u8, let elf_header_ptr = elf_bytes.as_ptr() as *const Elf64Ehdr; - return match unsafe { elf_header.e_type } { + return match elf_header.e_type { ET_EXEC => unsafe { load_program(mapper, elf_header_ptr, elf_bytes, false) }, ET_DYN => unsafe { load_program(mapper, elf_header_ptr, elf_bytes, true) }, ET_REL => return (null(), 0), diff --git a/kernel/src/driver/fs/mod.rs b/kernel/src/driver/fs/mod.rs new file mode 100644 index 0000000..10d397e --- /dev/null +++ b/kernel/src/driver/fs/mod.rs @@ -0,0 +1 @@ +pub mod vfs; diff --git a/kernel/src/driver/fs/vfs.rs b/kernel/src/driver/fs/vfs.rs new file mode 100644 index 0000000..247f9af --- /dev/null +++ b/kernel/src/driver/fs/vfs.rs @@ -0,0 +1,208 @@ +use core::ptr::{null, null_mut}; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FILE { + pub data: *const u8, // pointer to the file's data + pub size: usize, // total size + pub cursor: usize, // current position + pub writable: bool, // is this a write buffer? + pub write_buf: *mut u8, // for writable fake files + pub write_cap: usize, + pub fd: i64, +} + +impl FILE { + pub const fn zeroed() -> FILE { + FILE { + data: null(), + size: 0, + cursor: 0, + writable: false, + write_buf: null_mut(), + write_cap: 0, + fd: -1, + } + } +} + +struct FakeFileEntry { + name: &'static str, + data: &'static [u8], +} + +pub type Fd = i64; +const MAX_FD: usize = 16; + +fn fd_ok(fd: Fd) -> bool { + fd >= 0 && (fd as usize) < MAX_FD +} + +static DOOM_WAD: &[u8] = include_bytes!("../../../../assets/doom1.wad"); +static DOOM_ELF: &[u8] = include_bytes!("../../../../assets/doomgeneric"); +static HELLOWORLD_ELF: &[u8] = include_bytes!("../../../../assets/helloworld.elf"); + +static FILES: &[FakeFileEntry] = &[ + FakeFileEntry { + name: "testfile", + data: b"Hello, World!", + }, + FakeFileEntry { + name: "helloworld.elf", + data: HELLOWORLD_ELF, + }, + FakeFileEntry { + name: "doomgeneric", + data: DOOM_ELF, + }, + FakeFileEntry { + name: "doom1.wad", + data: DOOM_WAD, + }, + FakeFileEntry { + name: "default.cfg", + data: b"", + }, + FakeFileEntry { + name: "doom.cfg", + data: b"", + }, +]; + +static mut FILE_POOL: [FILE; 16] = [FILE::zeroed(); 16]; +static mut FILE_POOL_USED: [bool; 16] = [false; 16]; + +pub unsafe fn get_file_pool_slot() -> (*mut FILE, i64) { + unsafe { + for i in 0..16 { + if !FILE_POOL_USED[i] { + FILE_POOL_USED[i] = true; + return (&mut FILE_POOL[i], i as i64); + } + } + + (null_mut(), -1) + } +} + +unsafe fn file_mut(fd: Fd) -> Option<&'static mut FILE> { + if !fd_ok(fd) { + return None; + } + let idx = fd as usize; + + if unsafe { !FILE_POOL_USED[idx] } { + return None; + } + + return unsafe { Some(&mut FILE_POOL[idx]) }; +} + +#[unsafe(no_mangle)] +pub fn vfs_open(name: &str, _mode: &str) -> Fd { + for entry in FILES { + if entry.name.contains(name) { + let (slot, fd) = unsafe { get_file_pool_slot() }; + if slot.is_null() { + return -1; + } + + unsafe { + (*slot).data = entry.data.as_ptr(); + (*slot).size = entry.data.len(); + (*slot).cursor = 0; + (*slot).writable = false; + (*slot).write_buf = null_mut(); + (*slot).write_cap = 0; + (*slot).fd = fd; + } + return fd; + } + } + -1 +} + +#[unsafe(no_mangle)] +pub fn vfs_close(fd: Fd) -> i32 { + if !fd_ok(fd) { + return -1; + } + + unsafe { + let idx = fd as usize; + if !FILE_POOL_USED[idx] { + return -1; + } + FILE_POOL_USED[idx] = false; + FILE_POOL[idx] = FILE::zeroed(); + } + + 0 +} + +#[unsafe(no_mangle)] +pub fn vfs_write(ptr: *mut u8, size: usize, count: usize, fp: *mut FILE) -> usize { + if ptr.is_null() || fp.is_null() || unsafe { (*fp).fd < 0 || (*fp).fd >= 16 } { + return 0; + } + count +} + +#[unsafe(no_mangle)] +pub fn vfs_read(fd: Fd, len: usize) -> Option<(*const u8, usize)> { + unsafe { + let f = file_mut(fd)?; + if f.cursor > f.size { + return Some((f.data, 0)); + } + + let available = f.size - f.cursor; + let to_read = len.min(available); + + let src = f.data.add(f.cursor); + f.cursor = f.cursor.saturating_add(to_read); + + Some((src, to_read)) + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn vfs_lseek(fd: Fd, offset: i64, whence: i32) -> i64 { + let f = match unsafe { file_mut(fd) } { + Some(f) => f, + None => return -1, + }; + + let new_pos = match whence { + 0 => { + if offset < 0 { + return -1; + } + offset as usize + } + 1 => { + let cur = f.cursor as i64; + let pos = cur.saturating_add(offset); + if pos < 0 { + return -1; + } + pos as usize + } + 2 => { + let end = f.size as i64; + let pos = end.saturating_add(offset); + if pos < 0 { + return -1; + } + pos as usize + } + _ => return -1, + }; + + if new_pos > f.size { + return -1; + } + + f.cursor = new_pos; + f.cursor as i64 +} diff --git a/kernel/src/driver/mod.rs b/kernel/src/driver/mod.rs index 6f55fa4..65c28d7 100644 --- a/kernel/src/driver/mod.rs +++ b/kernel/src/driver/mod.rs @@ -1,4 +1,5 @@ pub mod elf; +pub mod fs; pub mod graphics; pub mod keyboard; pub mod mouse; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 663b759..0b648f5 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -102,11 +102,9 @@ unsafe extern "C" fn kmain() -> ! { // removed by the linker. assert!(BASE_REVISION.is_supported()); - let mut mapper; - if let Some(hhdm_response) = HHDM_REQUEST.get_response() { if let Some(memory_map_response) = MEMORY_MAP_REQUEST.get_response() { - mapper = init(hhdm_response, memory_map_response); + init(hhdm_response, memory_map_response); } else { kernel_crash(); // Could not get required info from Limine's memory map. } @@ -130,12 +128,9 @@ unsafe extern "C" fn kmain() -> ! { println!("Could not get date at boot. Will default to 0.") } - let (entry_point, heap_base) = load_file(&mut mapper, INIT_ELF_BYTES); - println!("Entry point: {:?}", entry_point); - with_framebuffer(|fb| fb.swap()); - run_elf(entry_point, heap_base); + run_elf(INIT_ELF_BYTES); loop {} } diff --git a/kernel/src/mm/address_space.rs b/kernel/src/mm/address_space.rs index 3e09ce9..7031921 100644 --- a/kernel/src/mm/address_space.rs +++ b/kernel/src/mm/address_space.rs @@ -29,7 +29,7 @@ impl AddressSpace { let new_pml4_ptr = physical_to_virt_pointer(new_pml4.start_address(), frame_allocator.hhdm_offset); - for i in 0..512 { + for i in 256..512 { let val = core::ptr::read(cur_pml4_ptr.add(i)); core::ptr::write(new_pml4_ptr.add(i), val); } diff --git a/kernel/src/mm/usercopy.rs b/kernel/src/mm/usercopy.rs index e81c107..21df0f0 100644 --- a/kernel/src/mm/usercopy.rs +++ b/kernel/src/mm/usercopy.rs @@ -1,3 +1,7 @@ +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; use x86_64::{ VirtAddr, structures::paging::{OffsetPageTable, PageTableFlags, Translate, mapper::TranslateResult}, @@ -37,3 +41,71 @@ pub fn copy_to_user( unsafe { core::ptr::copy_nonoverlapping(src, buf, len) }; Ok(()) } + +pub fn copy_from_user( + mapper: &mut OffsetPageTable, + buf: *mut u8, + src: *const u8, + len: usize, +) -> Result<(), isize> { + if len == 0 { + return Ok(()); + } + if buf.is_null() || src.is_null() { + return Err(-14); + } + + let start = src as u64; + let end = start + .checked_add(len as u64) + .ok_or(-1) + .map_err(|err| err as isize)?; + + let mut page_addr = start & !0xFFF; + + while page_addr < end { + let translate_result = mapper.translate(VirtAddr::new(page_addr)); + #[allow(non_shorthand_field_patterns)] + if let TranslateResult::Mapped { + frame: _, + offset: _, + flags: flags, + } = translate_result + { + if !flags.contains(PageTableFlags::USER_ACCESSIBLE) { + return Err(-13); + } + } else { + return Err(-1); + } + page_addr += 0x1000; + } + + unsafe { core::ptr::copy_nonoverlapping(src, buf, len) }; + Ok(()) +} + +pub fn copy_cstr_from_user( + mapper: &mut OffsetPageTable, + user_ptr: *const u8, + max_len: usize, +) -> Result { + if user_ptr.is_null() { + return Err(-14); + } + + let mut buf: Vec = Vec::with_capacity(64); + + for i in 0..max_len { + let mut byte = 0u8; + copy_from_user(mapper, &mut byte as *mut u8, unsafe { user_ptr.add(i) }, 1)?; + if byte == 0 { + return core::str::from_utf8(&buf) + .map(|s| s.to_string()) + .map_err(|_| -84); + } + buf.push(byte); + } + + Err(-36) +} diff --git a/kernel/src/task/process.rs b/kernel/src/task/process.rs index 2d003df..2223db9 100644 --- a/kernel/src/task/process.rs +++ b/kernel/src/task/process.rs @@ -16,7 +16,7 @@ pub struct Process { pub heap_base: u64, pub heap_end: u64, pub kbd_buffer: Vec, - pub address_space: AddressSpace, + pub address_space: Option, pub user_entry: u64, } impl Process { @@ -26,18 +26,16 @@ impl Process { stack_top: u64, heap_base: u64, heap_end: u64, - ) -> Option { - let address_space = AddressSpace::new()?; - - Some(Process { + ) -> Process { + Process { pid, stack_top, state: ProcessState::Ready, heap_base, heap_end, kbd_buffer: Vec::new(), - address_space, + address_space: None, user_entry, - }) + } } } diff --git a/kernel/src/task/scheduler.rs b/kernel/src/task/scheduler.rs index e7db1ee..8a7d0de 100644 --- a/kernel/src/task/scheduler.rs +++ b/kernel/src/task/scheduler.rs @@ -39,7 +39,7 @@ impl Locked { let mut guard = without_interrupts(|| self.lock()); let pid = guard.next_pid; guard.next_pid += 1; - let process = Process::new(pid, entry_point, stack_top, heap_base, heap_base)?; + let process = Process::new(pid, entry_point, stack_top, heap_base, heap_base); guard.processes.insert(pid, process); Some(pid) @@ -52,7 +52,6 @@ impl Locked { }; set_current_pid(Some(pid)); - enter_usermode(entry_point as u64, (stack_top & !0xF) - 8); } diff --git a/user/init b/user/init index 80dde53..3fe910f 160000 --- a/user/init +++ b/user/init @@ -1 +1 @@ -Subproject commit 80dde531baf5d33d716b679bcd458d2e36ae8dc3 +Subproject commit 3fe910f33af9fe1bfbd01aeff6abdaae847f54b8 diff --git a/user/libxunil b/user/libxunil index c508fee..222374a 160000 --- a/user/libxunil +++ b/user/libxunil @@ -1 +1 @@ -Subproject commit c508fee99eeb42c0ff8ba8686e2bfb962cae2edf +Subproject commit 222374a639d73bfdfc54b2a87136c6704b16d76c