diff --git a/assets/helloworld.elf b/assets/helloworld.elf index fd8c741..bb6ee7d 100755 Binary files a/assets/helloworld.elf and b/assets/helloworld.elf differ diff --git a/build_doomgeneric.sh b/build_doomgeneric.sh index da37b45..d825ab7 100644 --- a/build_doomgeneric.sh +++ b/build_doomgeneric.sh @@ -1,6 +1,6 @@ bash build_libxunil.sh cd user/apps/doomgeneric/doomgeneric rm -r ./build -make -j16 -f Makefile.xunil +make LD=/usr/bin/ld -j16 -f Makefile.xunil cp doomgeneric ../../../../assets/doomgeneric cd ../../../.. diff --git a/kernel/src/arch/arch.rs b/kernel/src/arch/arch.rs index 2e19b47..5292fb9 100644 --- a/kernel/src/arch/arch.rs +++ b/kernel/src/arch/arch.rs @@ -21,13 +21,13 @@ pub fn init<'a>( } #[cfg(target_arch = "x86_64")] -pub fn enter_usermode(user_rip: u64, user_rsp: u64) { - enter_usermode_x86_64(user_rip, user_rsp); +pub fn enter_usermode(user_rip: u64, user_rsp: u64, should_swapgs: bool) { + enter_usermode_x86_64(user_rip, user_rsp, should_swapgs); } #[cfg(target_arch = "x86_64")] -pub fn run_elf(file_bytes: &[u8]) { - run_elf_x86_64(file_bytes); +pub fn run_elf(file_bytes: &[u8], should_swapgs: bool) { + run_elf_x86_64(file_bytes, should_swapgs); } pub fn get_allocator<'a>() -> &'static impl GlobalAlloc { diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs index 5449393..2a8d7ef 100644 --- a/kernel/src/arch/syscall.rs +++ b/kernel/src/arch/syscall.rs @@ -1,28 +1,27 @@ #![allow(dead_code)] -use core::{ - alloc::{GlobalAlloc, Layout}, - ptr::null_mut, -}; - +use alloc::vec; use x86_64::{ - VirtAddr, + PhysAddr, VirtAddr, instructions::interrupts, - structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB}, + structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB}, }; use crate::{ - arch::arch::{FRAME_ALLOCATOR, get_allocator, infinite_idle, sleep}, + arch::arch::{FRAME_ALLOCATOR, run_elf, sleep}, driver::{ fs::vfs::{vfs_close, vfs_lseek, vfs_open, vfs_read}, - graphics::framebuffer::with_framebuffer, + graphics::framebuffer::{FRAMEBUFFER, USER_FB_BASE, with_framebuffer}, keyboard::{KeyboardEvent, process_scancodes}, timer::TIMER, }, mm::usercopy::{copy_cstr_from_user, copy_to_user}, - print, println, - task::scheduler::{SCHEDULER, current_pid}, - util::align_up, + print, + task::{ + process::ProcessState, + scheduler::{SCHEDULER, current_pid}, + }, + util::{align_down, align_up}, }; const READ: usize = 0; @@ -51,43 +50,61 @@ const SLEEP: usize = 909090; // zzz haha 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 { - 1 - } else { - align.next_power_of_two() - }; - let layout = match Layout::from_size_align(size, align) { - Ok(l) => l, - Err(_) => return null_mut(), - }; - - unsafe { GlobalAlloc::alloc(get_allocator(), layout) } -} - -pub unsafe fn free(ptr: *mut u8, size: usize, align: usize) { - if ptr.is_null() { - // very important, do not double free - return; - } - - let align = if align < 1 { - 1 - } else { - align.next_power_of_two() - }; - - if let Ok(layout) = Layout::from_size_align(size.max(1), align.max(1)) { - unsafe { GlobalAlloc::dealloc(get_allocator(), ptr, layout) } - } -} - -pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) { - unsafe { core::ptr::write_bytes(ptr, val, count) }; -} - fn map_framebuffer() -> isize { - 0 + let pid = current_pid().unwrap_or(0); + if pid == 0 { + return -1; + } + + let framebuffer = FRAMEBUFFER.lock(); + let fb = match framebuffer.as_ref() { + Some(fb) => fb, + None => return -1, + }; + + let struct_phys = fb.meta.struct_phys; + let buf_phys = fb.meta.buf_phys; + let buf_size = (fb.meta.buf_len * size_of::()) as u64; + let pixel_map_start = align_down(buf_phys, 4096); + let pixel_map_end = align_up(buf_phys + buf_size, 4096); + drop(framebuffer); + + let mut frame_allocator = FRAME_ALLOCATOR.lock(); + + SCHEDULER + .with_process(pid, |process| { + let address_space = match process.address_space.as_mut() { + Some(a) => a, + None => return -1, + }; + + let mut map_page = |virt: u64, phys: u64| unsafe { + let frame = PhysFrame::::containing_address(PhysAddr::new(phys)); + let page = Page::::containing_address(VirtAddr::new(virt)); + address_space + .mapper + .map_to( + page, + frame, + PageTableFlags::PRESENT + | PageTableFlags::WRITABLE + | PageTableFlags::USER_ACCESSIBLE + | PageTableFlags::NO_EXECUTE, + &mut *frame_allocator, + ) + .unwrap() + .flush(); + }; + + map_page(USER_FB_BASE, struct_phys); + + for offset in (0..pixel_map_end - pixel_map_start).step_by(4096) { + map_page(USER_FB_BASE + 0x1000 + offset, pixel_map_start + offset); + } + + 0 + }) + .unwrap_or(-1) } fn read(ptr: isize, size: isize, nmemb: isize, fd: isize) -> isize { @@ -102,19 +119,22 @@ fn read(ptr: isize, size: isize, nmemb: isize, fd: isize) -> isize { 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; - }; + if let Some(address_space) = process.address_space.as_mut() { + 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; + return (read_ptr.1 as isize) / size; + } else { + return -1; + } } else { return -1; } @@ -130,7 +150,7 @@ fn open(path: isize, mode: isize) -> isize { SCHEDULER .with_process(pid, |process| { - let address_space = process.address_space.as_mut().unwrap(); + let address_space = process.address_space.as_mut().ok_or::(-1)?; 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)?; @@ -159,18 +179,21 @@ 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 address_space.mapper, - user_ptr as *mut u8, - process.kbd_buffer.as_ptr() as *const u8, - to_copy * size_of::(), - ) { - process.kbd_buffer.drain(0..to_copy); - return to_copy as isize; + if let Some(address_space) = process.address_space.as_mut() { + if let Ok(_) = copy_to_user( + &mut address_space.mapper, + user_ptr as *mut u8, + process.kbd_buffer.as_ptr() as *const u8, + to_copy * size_of::(), + ) { + process.kbd_buffer.drain(0..to_copy); + return to_copy as isize; + } else { + return -1; + }; } else { return -1; - }; + } }) .unwrap_or(-1); } @@ -214,23 +237,26 @@ 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 { - address_space - .mapper - .map_to( - page, - frame, - PageTableFlags::PRESENT - | PageTableFlags::WRITABLE - | PageTableFlags::USER_ACCESSIBLE - | PageTableFlags::NO_EXECUTE, - &mut *frame_allocator, - ) - .unwrap() - .flush(); + if let Some(address_space) = process.address_space.as_mut() { + unsafe { + address_space + .mapper + .map_to( + page, + frame, + PageTableFlags::PRESENT + | PageTableFlags::WRITABLE + | PageTableFlags::USER_ACCESSIBLE + | PageTableFlags::NO_EXECUTE, + &mut *frame_allocator, + ) + .unwrap() + .flush(); - core::ptr::write_bytes(virt_addr.as_mut_ptr::(), 0, 4096); + core::ptr::write_bytes(virt_addr.as_mut_ptr::(), 0, 4096); + } + } else { + return -1; } } else { return -1; @@ -246,6 +272,86 @@ pub unsafe fn sbrk(increment: isize) -> isize { .unwrap_or(-1); } +pub fn exec(arg0: isize) -> isize { + let pid = current_pid().unwrap_or(0); + if pid == 0 { + return -1; + } + + let path = match SCHEDULER.with_process(pid, |process| { + let address_space = process.address_space.as_mut().ok_or::(-1)?; + copy_cstr_from_user(&mut address_space.mapper, arg0 as *const u8, 256) + }) { + Some(Ok(p)) => p, + _ => return -1, + }; + + let fd = vfs_open(path.as_str(), "r"); + if fd < 0 { + return -1; + } + + const SEEK_SET: i32 = 0; + const SEEK_CUR: i32 = 1; + const SEEK_END: i32 = 2; + + if vfs_lseek(fd, 0, SEEK_END) < 0 { + vfs_close(fd); + return -1; + } + let size = vfs_lseek(fd, 0, SEEK_CUR); + if size <= 0 { + vfs_close(fd); + return -1; + } + if vfs_lseek(fd, 0, SEEK_SET) < 0 { + vfs_close(fd); + return -1; + } + + let mut buf = vec![0u8; size as usize]; + let mut off = 0usize; + + while off < buf.len() { + let want = buf.len() - off; + let Some((src, n)) = vfs_read(fd, want) else { + break; + }; + if n == 0 { + break; + } + unsafe { + core::ptr::copy_nonoverlapping(src, buf.as_mut_ptr().add(off), n); + } + off += n; + } + + vfs_close(fd); + + if off != buf.len() { + return -1; + } + + run_elf(&buf, true); + 0 +} + +pub fn set_reschedule(should_reschedule: bool) { + let pid = current_pid().unwrap_or(0); + + if pid == 0 { + return; + } + + let mut scheduler = SCHEDULER.lock(); + + if let Some(process) = scheduler.processes.get_mut(&pid) { + process.should_reschedule = should_reschedule; + } + + drop(scheduler); +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn syscall_dispatch( num: usize, @@ -257,6 +363,25 @@ pub unsafe extern "C" fn syscall_dispatch( arg5: isize, ) -> isize { interrupts::enable(); + + set_reschedule(match num { + BRK => false, + READ => true, + WRITE => false, + OPEN => true, + CLOSE => true, + LSEEK => true, + EXIT => true, + SLEEP => true, + CLOCK_GETTIME => false, + MAP_FRAMEBUFFER => false, + KBD_READ => true, + FRAMEBUFFER_SWAP => true, + GETPID => false, + EXECVE => true, + _ => false, + }); + match num { BRK => unsafe { sbrk(arg0) }, READ => read(arg0, arg1, arg2, arg3) as isize, @@ -275,27 +400,60 @@ 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()); - infinite_idle(); + let pid = current_pid().unwrap_or(0); + if pid == 0 { + return 0; + } + + let next_pid = { + let mut sched = SCHEDULER.lock(); + + sched.processes.remove(&pid); + + sched + .processes + .iter() + .find_map(|(other, proc)| { + if *other != pid && matches!(proc.state, ProcessState::Ready) { + Some(*other) + } else { + None + } + }) + .unwrap_or(0) + }; + + if next_pid != 0 { + SCHEDULER.switch_to(next_pid, false); + } + + crate::arch::arch::infinite_idle(); } SLEEP => { sleep(arg0 as u64); 0 } + EXECVE => exec(arg0), CLOCK_GETTIME => TIMER.now().elapsed() as isize, MAP_FRAMEBUFFER => map_framebuffer(), KBD_READ => kbd_read(arg0 as *mut KeyboardEvent, arg1), + GETPID => { + let pid = current_pid().unwrap_or(0); + + match pid { + 0 => return -1, + _ => return pid as isize, + } + } FRAMEBUFFER_SWAP => { with_framebuffer(|fb| { - fb.swap(); + fb.present(); }); 0 } diff --git a/kernel/src/arch/x86_64/elf.rs b/kernel/src/arch/x86_64/elf.rs index 525a171..025f3f9 100644 --- a/kernel/src/arch/x86_64/elf.rs +++ b/kernel/src/arch/x86_64/elf.rs @@ -9,7 +9,7 @@ use crate::{ println, task::scheduler::SCHEDULER, }; -pub fn run_elf_x86_64(file_bytes: &[u8]) { +pub fn run_elf_x86_64(file_bytes: &[u8], should_swapgs: bool) { let stack_base: u64 = 0x0000_7fff_0000_0000; let page_count = 4096; // 16 mib let page_size = 0x1000u64; @@ -57,7 +57,7 @@ pub fn run_elf_x86_64(file_bytes: &[u8]) { process.address_space = Some(address_space) }); - SCHEDULER.run_process(process_pid, entry_point); + SCHEDULER.switch_to(process_pid, should_swapgs); } else { return; }; diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 21728fa..1177381 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,6 +1,11 @@ +use core::sync::atomic::Ordering; + use crate::{ arch::x86_64::{gdt, mouse::mouse_interrupt}, - driver::{keyboard::push_scancode, mouse::MOUSE, timer::TIMER}, + driver::{ + graphics::framebuffer::with_framebuffer, keyboard::push_scancode, mouse::MOUSE, + serial::with_serial_console, timer::TIMER, + }, println, }; use lazy_static::lazy_static; @@ -95,6 +100,14 @@ pub extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: InterruptStack extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { TIMER.interrupt(); + let t = TIMER.interrupt_count.load(Ordering::Relaxed); + if t % 60 == 0 { + with_framebuffer(|fb| { + with_serial_console(|serial_console| serial_console.render(fb)); + fb.present(); + }); + } + unsafe { PICS.lock() .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); diff --git a/kernel/src/arch/x86_64/syscall.rs b/kernel/src/arch/x86_64/syscall.rs index e1d9eb0..ec89269 100644 --- a/kernel/src/arch/x86_64/syscall.rs +++ b/kernel/src/arch/x86_64/syscall.rs @@ -2,7 +2,10 @@ use core::arch::asm; use x86_64::instructions::tlb::flush_all; -use crate::arch::x86_64::gdt::{GDT, TSS}; +use crate::{ + arch::x86_64::gdt::{GDT, TSS}, + task::scheduler::{SCHEDULER, current_pid}, +}; const IA32_EFER: u32 = 0xC0000080; const IA32_EFER_SCE: u64 = 1; // system call enable bit @@ -10,6 +13,7 @@ const IA32_STAR: u32 = 0xC0000081; const IA32_LSTAR: u32 = 0xC0000082; const IA32_FMASK: u32 = 0xC0000084; const IA32_KERNEL_GS_BASE: u32 = 0xC0000102; +const IA32_GS_BASE: u32 = 0xC0000101; #[repr(C)] pub struct PerCpuData { @@ -69,6 +73,8 @@ pub fn init_syscalls() { wrmsr(IA32_FMASK, 1 << 9); // Mask interupts during syscalls + wrmsr(IA32_GS_BASE, 0); + let kernel_cs = (GDT.1.0.0 & !0b11) as u64; let user_ds = (GDT.1.3.0 & !0b11) as u64; @@ -79,57 +85,124 @@ pub fn init_syscalls() { } } +#[unsafe(no_mangle)] +unsafe extern "C" fn check_and_reschedule() -> usize { + let pid = current_pid().unwrap_or(0); + + if pid == 0 { + return 0; + } + + let should = SCHEDULER + .with_process(pid, |process| process.should_reschedule) + .unwrap_or(false); + + if !should { + return 0; + } + + let next_task = SCHEDULER.next_task(); + + if next_task == pid { + return 0; + } + + SCHEDULER.switch_to(next_task, false); + + 1 +} + #[unsafe(naked)] #[unsafe(no_mangle)] unsafe extern "C" fn syscall_entry() { core::arch::naked_asm!( r#" - .intel_syntax noprefix - swapgs - - // save user stack mov gs:[0], rsp - - // use kernel stack mov rsp, gs:[8] - push r11 - push rcx - push rax - push rdi - push rsi - push rdx - push r10 - push r8 - push r9 + sub rsp, 128 - mov rdi, rax - mov rsi, [rsp + 40] - mov rdx, [rsp + 32] - mov rcx, [rsp + 24] - mov r8, [rsp + 16] - mov r9, [rsp + 8] - push qword ptr [rsp] + mov qword ptr [rsp + 0], r15 + mov qword ptr [rsp + 8], r14 + mov qword ptr [rsp + 16], r13 + mov qword ptr [rsp + 24], r12 + mov qword ptr [rsp + 32], r11 + mov qword ptr [rsp + 40], r10 + mov qword ptr [rsp + 48], r9 + mov qword ptr [rsp + 56], r8 + mov qword ptr [rsp + 64], rsi + mov qword ptr [rsp + 72], rdi + mov qword ptr [rsp + 80], rbp + mov qword ptr [rsp + 88], rdx + mov qword ptr [rsp + 96], rcx + mov qword ptr [rsp + 104], rbx + mov qword ptr [rsp + 112], rax + mov rax, qword ptr gs:[0] + mov qword ptr [rsp + 120], rax + + mov rbx, rsp // rbx = frame base + + // ctx_save(frame) + mov rdi, rbx + lea rsp, [rbx - 8] + call ctx_save + mov rsp, rbx + + // syscall_dispatch(num,arg0..arg5) + mov rdi, qword ptr [rbx + 112] + mov rsi, qword ptr [rbx + 72] + mov rdx, qword ptr [rbx + 64] + mov rcx, qword ptr [rbx + 88] + mov r8, qword ptr [rbx + 40] + mov r9, qword ptr [rbx + 56] + + lea rsp, [rbx - 24] // rsp%16==8 before call + mov rax, qword ptr [rbx + 48] + mov qword ptr [rsp + 0], rax call syscall_dispatch + mov rsp, rbx - add rsp, 8 // fix alignment + // save return value into frame (so normal restore uses it) + mov qword ptr [rbx + 112], rax - pop r9 - pop r8 - pop r10 - pop rdx - pop rsi - pop rdi - add rsp, 8 + // ctx_save(frame) again so saved_ctx.rax reflects return value + mov rdi, rbx + lea rsp, [rbx - 8] + call ctx_save + mov rsp, rbx - pop rcx - pop r11 + // maybe switch tasks (this should not return if it switches) + lea rsp, [rbx - 8] + call check_and_reschedule + mov rsp, rbx - mov rsp, gs:[0] + test rax, rax + jnz .done + // restore registers from frame + mov rax, qword ptr [rsp + 112] + mov rbx, qword ptr [rsp + 104] + mov rcx, qword ptr [rsp + 96] + mov rdx, qword ptr [rsp + 88] + mov rbp, qword ptr [rsp + 80] + mov rdi, qword ptr [rsp + 72] + mov rsi, qword ptr [rsp + 64] + mov r8, qword ptr [rsp + 56] + mov r9, qword ptr [rsp + 48] + mov r10, qword ptr [rsp + 40] + mov r11, qword ptr [rsp + 32] // rflags — sysretq loads RFLAGS from r11 + mov r12, qword ptr [rsp + 24] + mov r13, qword ptr [rsp + 16] + mov r14, qword ptr [rsp + 8] + mov r15, qword ptr [rsp + 0] + // don't restore rsp from frame — use the user rsp we saved in gs:[0] + mov rsp, qword ptr gs:[0] swapgs - sysretq"#, + sysretq + .done: + ud2 + "#, ); } diff --git a/kernel/src/arch/x86_64/usermode.rs b/kernel/src/arch/x86_64/usermode.rs index c28afc3..80386b3 100644 --- a/kernel/src/arch/x86_64/usermode.rs +++ b/kernel/src/arch/x86_64/usermode.rs @@ -10,7 +10,7 @@ fn with_rpl3(ss: SegmentSelector) -> u64 { } // entry point and stack -pub fn enter_usermode_x86_64(user_rip: u64, user_rsp: u64) -> ! { +pub fn enter_usermode_x86_64(user_rip: u64, user_rsp: u64, should_swapgs: bool) -> ! { let user_cs = with_rpl3(user_code_selector()); let user_ss = with_rpl3(user_data_selector()); @@ -22,8 +22,11 @@ pub fn enter_usermode_x86_64(user_rip: u64, user_rsp: u64) -> ! { let rflags: u64 = 0x202; unsafe { + core::arch::asm!("cli"); + if should_swapgs { + core::arch::asm!("swapgs"); + } core::arch::asm!( - "cli", "push {user_ss}", "push {user_rsp}", "push {rflags}", diff --git a/kernel/src/driver/elf/loader.rs b/kernel/src/driver/elf/loader.rs index 8659b79..b98387e 100644 --- a/kernel/src/driver/elf/loader.rs +++ b/kernel/src/driver/elf/loader.rs @@ -2,17 +2,10 @@ use core::ptr::null; use x86_64::structures::paging::OffsetPageTable; -use crate::{ - arch::syscall::{malloc, memset}, - driver::elf::{ - header::{ - ET_DYN, ET_EXEC, ET_REL, Elf64Ehdr, Elf64Rel, Elf64Shdr, SHF_ALLOC, SHT_NOBITS, SHT_REL, - }, - program::load_program, - reloc::elf_do_reloc, - section::elf_sheader, - validation::validate_elf, - }, +use crate::driver::elf::{ + header::{ET_DYN, ET_EXEC, ET_REL, Elf64Ehdr}, + program::load_program, + validation::validate_elf, }; pub fn load_file(mapper: &mut OffsetPageTable, elf_bytes: &[u8]) -> (*const u8, u64) { @@ -37,70 +30,3 @@ pub fn load_file(mapper: &mut OffsetPageTable, elf_bytes: &[u8]) -> (*const u8, _ => return (null(), 0), }; } - -// TODO: make ET_REL work -pub unsafe fn elf_load_stage1(hdr: *const Elf64Ehdr) { - let shdr = unsafe { elf_sheader(hdr) } as *mut Elf64Shdr; - let mut i: u16 = 0; - let shnum = unsafe { (*hdr).e_shnum }; - - while i < shnum { - let section: &mut Elf64Shdr = unsafe { &mut *(shdr.add(i as usize)) }; - - if section.sh_type == SHT_NOBITS { - if section.sh_size == 0 { - continue; - } - - if (section.sh_flags & SHF_ALLOC) != 1 { - let mem = - unsafe { malloc(section.sh_size as usize, section.sh_addralign as usize) }; - - if mem.is_null() { - continue; // handle OOM - } - - unsafe { - // zero the memory - memset(mem, 0, section.sh_size as usize); - } - section.sh_offset = mem.addr() as u64; - } - } - - i += 1; - } -} - -pub unsafe fn elf_load_stage2(hdr: *const Elf64Ehdr) -> i8 { - let shdr = unsafe { elf_sheader(hdr) } as *mut Elf64Shdr; - let mut i: u16 = 0; - let mut idx: u64; - let shnum = unsafe { (*hdr).e_shnum }; - - while i < shnum { - let section: &mut Elf64Shdr = unsafe { &mut *(shdr.add(i as usize)) }; - - if section.sh_type == SHT_REL { - idx = 0; - while idx < section.sh_size / section.sh_entsize { - let reltab: *const Elf64Rel = unsafe { - ((hdr as *const u8).add(section.sh_offset as usize) as *const Elf64Rel) - .add(idx as usize) - }; - - let result: i8 = unsafe { elf_do_reloc(hdr, reltab, section) }; - - if result == -1 { - return -1; - } - - idx += 1; - } - } - - i += 1; - } - - return 0; -} diff --git a/kernel/src/driver/elf/mod.rs b/kernel/src/driver/elf/mod.rs index 55fbb13..d9afbad 100644 --- a/kernel/src/driver/elf/mod.rs +++ b/kernel/src/driver/elf/mod.rs @@ -1,6 +1,4 @@ mod header; pub mod loader; mod program; -mod reloc; -mod section; pub mod validation; diff --git a/kernel/src/driver/elf/program.rs b/kernel/src/driver/elf/program.rs index 4c61239..4da1486 100644 --- a/kernel/src/driver/elf/program.rs +++ b/kernel/src/driver/elf/program.rs @@ -1,7 +1,4 @@ -use core::{ - ffi::CStr, - ptr::{null, null_mut}, -}; +use core::ptr::{null, null_mut}; use alloc::vec::Vec; use x86_64::{ @@ -12,7 +9,7 @@ use x86_64::{ }; use crate::{ - arch::{arch::FRAME_ALLOCATOR, syscall::memset}, + arch::arch::FRAME_ALLOCATOR, driver::elf::header::{ DT_JMPREL, DT_NEEDED, DT_NULL, DT_PLTREL, DT_PLTRELSZ, DT_RELA, DT_RELASZ, DT_STRSZ, DT_STRTAB, DT_SYMTAB, Elf64Dyn, Elf64Ehdr, Elf64Phdr, Elf64Rela, Elf64Sym, PF_X, @@ -123,29 +120,29 @@ pub unsafe fn load_program( } } -fn cstr_from_strtab( - strtab_ptr: *const u8, - strtab_size: u64, - off: u32, -) -> Option<&'static core::ffi::CStr> { - let off = off as u64; - if strtab_ptr.is_null() || off >= strtab_size { - return None; - } +// fn cstr_from_strtab( +// strtab_ptr: *const u8, +// strtab_size: u64, +// off: u32, +// ) -> Option<&'static core::ffi::CStr> { +// let off = off as u64; +// if strtab_ptr.is_null() || off >= strtab_size { +// return None; +// } - let mut i = off; +// let mut i = off; - while i < strtab_size { - let b = unsafe { *strtab_ptr.add(i as usize) }; - if b == 0 { - let start = unsafe { strtab_ptr.add(off as usize) } as *const core::ffi::c_char; - return Some(unsafe { CStr::from_ptr(start as *const i8) }); - } - i += 1; - } +// while i < strtab_size { +// let b = unsafe { *strtab_ptr.add(i as usize) }; +// if b == 0 { +// let start = unsafe { strtab_ptr.add(off as usize) } as *const core::ffi::c_char; +// return Some(unsafe { CStr::from_ptr(start as *const i8) }); +// } +// i += 1; +// } - None -} +// None +// } pub fn dyn_get_symaddr( strtab_ptr: *const u8, @@ -159,7 +156,7 @@ pub fn dyn_get_symaddr( return Ok(load_bias + sym.st_value); } - let name = cstr_from_strtab(strtab_ptr, strtab_size, idx as u32); + // let name = cstr_from_strtab(strtab_ptr, strtab_size, idx as u32); let bind = sym.st_info >> 4; if bind == STB_WEAK { Ok(0) } else { Err(()) } @@ -340,7 +337,7 @@ pub fn load_segment_to_memory( core::ptr::copy_nonoverlapping(src, dst, file_size as usize); if mem_size > file_size { - memset( + core::ptr::write_bytes( dst.add(file_size as usize), 0, (mem_size - file_size) as usize, diff --git a/kernel/src/driver/elf/reloc.rs b/kernel/src/driver/elf/reloc.rs deleted file mode 100644 index fbf9986..0000000 --- a/kernel/src/driver/elf/reloc.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::driver::elf::{ - header::{Elf64Ehdr, Elf64Rel, Elf64Shdr, R_X86_64_64}, - section::{elf_get_symval, elf_section}, -}; - -pub unsafe fn elf_do_reloc( - hdr: *const Elf64Ehdr, - rel: *const Elf64Rel, - reltab: *mut Elf64Shdr, -) -> i8 { - let target = unsafe { elf_section(hdr, (*reltab).sh_info as usize) }; - let addr = unsafe { (hdr as *const u8).add((*target).sh_offset as usize) }; - let reference = unsafe { addr.add((*rel).r_offset as usize) as *mut u64 }; - - let symval; - - if unsafe { ((*rel).r_info) >> 32 } != 0 { - symval = unsafe { elf_get_symval(hdr, (*reltab).sh_link as u64, (*rel).r_info >> 32) }; - if symval == 1 { - return -1; - } - - match unsafe { ((*rel).r_info & 0xffff_ffff) as u32 } { - x if x == R_X86_64_64 as u32 => unsafe { *reference = symval.wrapping_add(*reference) }, - _ => return -1, - } - - return symval as i8; - } - - return 0; -} diff --git a/kernel/src/driver/elf/section.rs b/kernel/src/driver/elf/section.rs deleted file mode 100644 index 64aa46a..0000000 --- a/kernel/src/driver/elf/section.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::driver::elf::header::{Elf64Ehdr, Elf64Shdr, Elf64Sym, SHN_ABS, SHN_UNDEF, STB_WEAK}; -use core::ffi::CStr; - -// TODO: make ET_REL work - -pub unsafe fn elf_sheader(hdr: *const Elf64Ehdr) -> *const Elf64Shdr { - unsafe { (hdr as *const u8).add((*hdr).e_shoff as usize) as *const Elf64Shdr } -} - -pub unsafe fn elf_section(hdr: *const Elf64Ehdr, idx: usize) -> *const Elf64Shdr { - unsafe { elf_sheader(hdr).add(idx) } -} - -pub unsafe fn elf_str_table(hdr: *const Elf64Ehdr) -> *const u8 { - if unsafe { (*hdr).e_shstrndx == SHN_UNDEF } { - return core::ptr::null(); - } - - let shdr = unsafe { elf_section(hdr, (*hdr).e_shstrndx as usize) }; - - unsafe { (hdr as *const u8).add((*shdr).sh_offset as usize) } -} - -fn elf_lookup_string<'a>(hdr: *const Elf64Ehdr, offset: usize) -> &'a str { - let strtab: *const u8 = unsafe { elf_str_table(hdr) }; - let mut len = 0; - - let start = unsafe { strtab.add(offset) }; - - unsafe { - while *start.add(len) != 0 { - len += 1; - } - core::str::from_raw_parts(start, len) - } -} - -pub fn elf_lookup_symbol(name: &CStr) -> *const u8 { - return core::ptr::null(); -} - -pub unsafe fn elf_get_symval(hdr: *const Elf64Ehdr, table: u64, idx: u64) -> u64 { - if table == SHN_UNDEF as u64 || idx == SHN_UNDEF as u64 { - return 0; - } - - let symtab: *const Elf64Shdr = unsafe { elf_section(hdr, table as usize) }; - - if unsafe { (*symtab).sh_entsize == 0 } { - return 255; // TODO: replace with error - } - - let symtab_entries: u64 = unsafe { (*symtab).sh_size / (*symtab).sh_entsize }; - - if idx >= symtab_entries { - return 255; // TODO: replace with error - } - - let symaddr = unsafe { (hdr as *const u8).add((*symtab).sh_offset as usize) }; - let sym_table = symaddr as *const Elf64Sym; - let symbol: &Elf64Sym = unsafe { &*sym_table.add(idx as usize) }; - - if symbol.st_shndx == SHN_UNDEF { - // the symbol is external - let strtab: *const Elf64Shdr = unsafe { elf_section(hdr, (*symtab).sh_link as usize) }; - let name_pointer: *const u8 = unsafe { - (hdr as *const u8) - .add((*strtab).sh_offset as usize) - .add((*symbol).st_name as usize) - }; - let name = unsafe { CStr::from_ptr(name_pointer as *const core::ffi::c_char) }; - - let target = elf_lookup_symbol(name); - if target == core::ptr::null() { - if (symbol.st_info >> 4) == STB_WEAK { - return 0; // Weak symbol initialized as 0 - } else { - return 255; // // TODO: replace with error Undefined External Symbol - } - } else { - return target as u64; - } - } else if symbol.st_shndx == SHN_ABS { - return symbol.st_value; // absolute symbol easy - } else { - let target: *const Elf64Shdr = unsafe { elf_section(hdr, symbol.st_shndx as usize) }; - return unsafe { - ((hdr as *const u8).add((symbol.st_value + (*target).sh_offset) as usize)) as u64 - }; - } -} diff --git a/kernel/src/driver/graphics/framebuffer.rs b/kernel/src/driver/graphics/framebuffer.rs index cd13306..69138f4 100644 --- a/kernel/src/driver/graphics/framebuffer.rs +++ b/kernel/src/driver/graphics/framebuffer.rs @@ -1,18 +1,35 @@ -use alloc::vec; -use alloc::vec::Vec; use limine::framebuffer::Framebuffer as LimineFramebuffer; use spin::Mutex; #[cfg(target_arch = "x86_64")] use x86_64::instructions::interrupts::without_interrupts; +use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB}; + +use crate::arch::arch::FRAME_ALLOCATOR; + +#[repr(C)] +pub struct UserFrameBuffer { + pub buf_virt: *mut u32, + pub width: usize, + pub height: usize, + pub pitch: usize, +} pub struct Framebuffer { addr: *mut u32, - back_buffer: Vec, pub width: usize, pub height: usize, pitch: usize, - back_buffer_len: usize, + pub user_fb: UserFrameBuffer, + pub meta: FramebufferMeta, +} + +pub const USER_FB_BASE: u64 = 0x0000_7F00_0000_0000; + +pub struct FramebufferMeta { + pub buf_phys: u64, + pub buf_len: usize, + pub struct_phys: u64, } impl Framebuffer { @@ -20,15 +37,53 @@ impl Framebuffer { let width = limine_fb.width() as usize; let height = limine_fb.height() as usize; let pitch = limine_fb.pitch() as usize / 4; - let back_buffer_len = width * height; + let buf_len = pitch * height; + let byte_len = buf_len * core::mem::size_of::(); + let pixel_frames = (byte_len + 4095) / 4096; + + let mut fa = FRAME_ALLOCATOR.lock(); + + let struct_frame: PhysFrame = + fa.allocate_frame().expect("framebuffer struct frame"); + let struct_phys = struct_frame.start_address().as_u64(); + let struct_virt = (struct_phys + fa.hhdm_offset) as *mut UserFrameBuffer; + + let first_pixel_frame: PhysFrame = + fa.allocate_frame().expect("framebuffer pixel frame 0"); + let buf_phys = first_pixel_frame.start_address().as_u64(); + for _ in 1..pixel_frames { + fa.allocate_frame().expect("framebuffer pixel frame"); + } + let buf_virt_kernel = (buf_phys + fa.hhdm_offset) as *mut u32; + drop(fa); + + unsafe { core::ptr::write_bytes(buf_virt_kernel, 0, buf_len) }; + + unsafe { + struct_virt.write(UserFrameBuffer { + buf_virt: (USER_FB_BASE + 0x1000) as *mut u32, + width, + height, + pitch, + }); + } Framebuffer { addr: limine_fb.addr().cast::(), - back_buffer: vec![0u32; width * height], width, height, pitch, - back_buffer_len, + user_fb: UserFrameBuffer { + buf_virt: buf_virt_kernel, + width, + height, + pitch, + }, + meta: FramebufferMeta { + buf_phys, + buf_len, + struct_phys, + }, } } @@ -37,13 +92,11 @@ impl Framebuffer { if x >= self.width || y >= self.height { return; } - - let idx = y.saturating_mul(self.pitch).saturating_add(x); - if idx >= self.back_buffer_len { + let idx = y * self.pitch + x; + if idx >= self.meta.buf_len { return; } - - self.back_buffer[idx] = color; + unsafe { self.user_fb.buf_virt.add(idx).write(color) }; } #[inline(always)] @@ -51,47 +104,30 @@ impl Framebuffer { if y >= self.height || x >= self.width || len == 0 { return; } - let max_len = self.width - x; - let len = core::cmp::min(len, max_len); + let len = core::cmp::min(len, self.width - x); let start = y * self.pitch + x; - let end = start + len; unsafe { - self.back_buffer.get_unchecked_mut(start..end).fill(color); + let slice = core::slice::from_raw_parts_mut(self.user_fb.buf_virt.add(start), len); + slice.fill(color); } } - pub fn swap(&mut self) { + pub fn present(&mut self) { unsafe { - core::ptr::copy_nonoverlapping( - self.back_buffer.as_ptr(), - self.addr, - self.back_buffer_len, - ); - } - } - - pub unsafe fn load_from_ptr( - &mut self, - src_ptr: *const u32, - src_width: usize, - src_height: usize, - ) { - let h = core::cmp::min(src_height, self.height); - let w = core::cmp::min(src_width, self.width); - - for y in 0..h { - let src_row = unsafe { src_ptr.add(y * src_width) }; - let dst_row = unsafe { self.back_buffer.as_mut_ptr().add(y * self.pitch) }; - unsafe { core::ptr::copy_nonoverlapping(src_row, dst_row, w) }; + core::ptr::copy_nonoverlapping(self.user_fb.buf_virt, self.addr, self.meta.buf_len); } } pub fn clear(&mut self, color: u32) { - self.back_buffer.fill(color); + unsafe { + let slice = core::slice::from_raw_parts_mut(self.user_fb.buf_virt, self.meta.buf_len); + slice.fill(color); + } } } unsafe impl Send for Framebuffer {} +unsafe impl Send for UserFrameBuffer {} pub static FRAMEBUFFER: Mutex> = Mutex::new(None); diff --git a/kernel/src/driver/keyboard.rs b/kernel/src/driver/keyboard.rs index def764f..f587364 100644 --- a/kernel/src/driver/keyboard.rs +++ b/kernel/src/driver/keyboard.rs @@ -65,11 +65,11 @@ pub fn process_scancodes() { }; if let Some(kbd_event) = process_scancode(scancode) { - let pid = current_pid().unwrap_or(0); - if pid == 0 { - continue; + let mut scheduler = SCHEDULER.lock(); + for process in scheduler.processes.values_mut() { + process.kbd_buffer.push(kbd_event); } - SCHEDULER.with_process(pid, |process| process.kbd_buffer.push(kbd_event)); + drop(scheduler); } } } diff --git a/kernel/src/driver/serial.rs b/kernel/src/driver/serial.rs index bca1bf6..2b77598 100644 --- a/kernel/src/driver/serial.rs +++ b/kernel/src/driver/serial.rs @@ -1,6 +1,7 @@ use crate::driver::graphics::font_render::render_text; use crate::driver::graphics::framebuffer::Framebuffer; use crate::{driver::graphics::base::rgb, util::serial_print}; +use alloc::string::String; use core::fmt::{self, Write}; use spin::Mutex; @@ -16,61 +17,53 @@ pub struct ConsoleWriter<'a> { impl Write for ConsoleWriter<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { serial_print(s); - self.console.render_text(self.fb, s, 2, false); + self.console.print(s, self.fb); Ok(()) } } pub struct SerialConsole { - start_x: usize, - pub current_x: usize, - current_y: usize, + text: String, + font_size: usize, + dirty: bool, } impl SerialConsole { - pub fn new(start_x: usize, start_y: usize) -> SerialConsole { + pub fn new() -> SerialConsole { SerialConsole { - start_x, - current_x: start_x, - current_y: start_y, + text: String::new(), + font_size: 2, + dirty: false, } } - pub fn render_text( - &mut self, - fb: &mut Framebuffer, - text: &str, - font_size: usize, - should_center: bool, - ) { - let (new_x, new_y) = render_text( - fb, - if should_center { - self.current_x - (text.len() - text.matches('\n').count()) * (font_size * 4) - } else { - self.current_x - }, - self.current_y, - text, - font_size, - rgb(255, 255, 255), - self.start_x, - ); - self.current_x = new_x; - self.current_y = new_y; + pub fn print(&mut self, text: &str, fb: &mut Framebuffer) { + let max_height = fb.height / (12 * self.font_size); + if self.text.split('\n').count() > max_height { + self.clear(); + } + self.text.push_str(text); + self.dirty = true; } - pub fn clear(&mut self, start_x: usize, start_y: usize) { - self.start_x = start_x; - self.current_x = start_x; - self.current_y = start_y; + pub fn render(&mut self, fb: &mut Framebuffer) { + if self.dirty { + fb.clear(rgb(0, 0, 0)); + self.dirty = false; + render_text(fb, 0, 0, &self.text, self.font_size, rgb(255, 255, 255), 0); + } + } + + pub fn clear(&mut self) { + self.dirty = true; + self.text.clear(); } } pub static SERIAL_CONSOLE: Mutex> = Mutex::new(None); -pub fn init_serial_console(start_x: usize, start_y: usize) { - *SERIAL_CONSOLE.lock() = Some(SerialConsole::new(start_x, start_y)); +pub fn init_serial_console() { + *SERIAL_CONSOLE.lock() = Some(SerialConsole::new()); } pub fn with_serial_console(f: F) { diff --git a/kernel/src/driver/timer.rs b/kernel/src/driver/timer.rs index 393086e..ab150cd 100644 --- a/kernel/src/driver/timer.rs +++ b/kernel/src/driver/timer.rs @@ -27,6 +27,17 @@ impl Timer { } pub fn interrupt(&self) { + let mut scheduler = without_interrupts(|| SCHEDULER.lock()); + for process in scheduler.processes.values_mut() { + if let Some(wake_tick) = process.info.wake_tick { + if self.interrupt_count.load(Ordering::Relaxed) >= wake_tick { + process.info.wake_tick = None; + process.state = crate::task::process::ProcessState::Ready; + } + } + } + drop(scheduler); + self.interrupt_count.fetch_add(1, Ordering::Relaxed); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 0b648f5..0d293e2 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] #![feature(abi_x86_interrupt)] -#![feature(str_from_raw_parts)] +#![feature(naked_functions_rustic_abi)] extern crate alloc; use core::fmt::Write; @@ -17,7 +17,6 @@ pub mod task; pub mod util; use crate::arch::arch::{infinite_idle, init, kernel_crash, run_elf}; -use crate::driver::elf::loader::load_file; use crate::driver::graphics::base::rgb; use crate::driver::graphics::framebuffer::{init_framebuffer, with_framebuffer}; use crate::driver::keyboard::init_keyboard; @@ -118,7 +117,7 @@ unsafe extern "C" fn kmain() -> ! { } } - init_serial_console(0, 0); + init_serial_console(); init_keyboard(); @@ -128,9 +127,7 @@ unsafe extern "C" fn kmain() -> ! { println!("Could not get date at boot. Will default to 0.") } - with_framebuffer(|fb| fb.swap()); - - run_elf(INIT_ELF_BYTES); + run_elf(INIT_ELF_BYTES, false); loop {} } @@ -141,7 +138,7 @@ fn rust_panic(_info: &core::panic::PanicInfo) -> ! { fb.clear(rgb(180, 0, 0)); with_serial_console(|console| { - console.clear(5, 5); + console.clear(); let mut writer = ConsoleWriter { fb: &mut fb, @@ -151,7 +148,6 @@ fn rust_panic(_info: &core::panic::PanicInfo) -> ! { let _ = writer.write_str("KERNEL PANIC\n\n"); let _ = writer.write_fmt(core::format_args!("{}", _info)); - fb.swap(); }); }); diff --git a/kernel/src/mm/address_space.rs b/kernel/src/mm/address_space.rs index 7031921..3e0cc35 100644 --- a/kernel/src/mm/address_space.rs +++ b/kernel/src/mm/address_space.rs @@ -58,6 +58,6 @@ impl AddressSpace { } } -unsafe fn physical_to_virt_pointer(phys_addr: PhysAddr, hhdm_offset: u64) -> *mut u64 { +pub unsafe fn physical_to_virt_pointer(phys_addr: PhysAddr, hhdm_offset: u64) -> *mut u64 { (hhdm_offset + phys_addr.as_u64()) as *mut u64 } diff --git a/kernel/src/task/context.rs b/kernel/src/task/context.rs index e69de29..a5dc2b7 100644 --- a/kernel/src/task/context.rs +++ b/kernel/src/task/context.rs @@ -0,0 +1,34 @@ +use crate::task::scheduler::{SCHEDULER, current_pid}; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct UserContext { + //general purpose data regs + pub r15: u64, + pub r14: u64, + pub r13: u64, + pub r12: u64, + pub r11: u64, // rflags + pub r10: u64, + pub r9: u64, + pub r8: u64, + pub rsi: u64, + pub rdi: u64, + pub rbp: u64, + pub rdx: u64, + pub rcx: u64, // rip + pub rbx: u64, + pub rax: u64, + pub rsp: u64, // user rsp +} + +#[unsafe(no_mangle)] +pub extern "C" fn ctx_save(regs: *const UserContext) { + if let Some(pid) = current_pid() { + let mut guard = SCHEDULER.lock(); + if let Some(process) = guard.processes.get_mut(&pid) { + let saved_ctx = unsafe { core::ptr::read(regs) }; + process.saved_ctx = Some(saved_ctx); + } + } +} diff --git a/kernel/src/task/process.rs b/kernel/src/task/process.rs index 2223db9..8375cd6 100644 --- a/kernel/src/task/process.rs +++ b/kernel/src/task/process.rs @@ -1,6 +1,8 @@ use alloc::vec::Vec; -use crate::{driver::keyboard::KeyboardEvent, mm::address_space::AddressSpace}; +use crate::{ + driver::keyboard::KeyboardEvent, mm::address_space::AddressSpace, task::context::UserContext, +}; pub enum ProcessState { Ready, @@ -9,6 +11,12 @@ pub enum ProcessState { Zombie, } +pub struct ProcessInfo { + pub exit_code: usize, + pub parent: usize, + pub wake_tick: Option, +} + pub struct Process { pub pid: u64, pub state: ProcessState, @@ -17,7 +25,10 @@ pub struct Process { pub heap_end: u64, pub kbd_buffer: Vec, pub address_space: Option, + pub saved_ctx: Option, + pub should_reschedule: bool, pub user_entry: u64, + pub info: ProcessInfo, } impl Process { pub fn new( @@ -35,7 +46,14 @@ impl Process { heap_end, kbd_buffer: Vec::new(), address_space: None, + saved_ctx: None, + should_reschedule: false, user_entry, + info: ProcessInfo { + exit_code: 0, + parent: 0, + wake_tick: None, + }, } } } diff --git a/kernel/src/task/scheduler.rs b/kernel/src/task/scheduler.rs index 8a7d0de..9ebbdc3 100644 --- a/kernel/src/task/scheduler.rs +++ b/kernel/src/task/scheduler.rs @@ -1,9 +1,16 @@ use core::sync::atomic::{AtomicU64, Ordering}; -use alloc::collections::btree_map::BTreeMap; +use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use x86_64::instructions::interrupts::without_interrupts; -use crate::{arch::arch::enter_usermode, task::process::Process, util::Locked}; +use crate::{ + arch::arch::enter_usermode, + task::{ + context::UserContext, + process::{Process, ProcessState}, + }, + util::Locked, +}; pub static CURRENT_PID: AtomicU64 = AtomicU64::new(0); @@ -45,14 +52,76 @@ impl Locked { Some(pid) } - pub fn run_process(&self, pid: u64, entry_point: *const u8) { - let stack_top = { - let guard = without_interrupts(|| self.lock()); - guard.processes[&pid].stack_top + pub fn next_task(&self) -> u64 { + if let Some(previous_pid) = current_pid() { + let mut guard = without_interrupts(|| self.lock()); + + if let Some(process) = guard.processes.get_mut(&previous_pid) { + if matches!(process.state, ProcessState::Running) { + process.state = ProcessState::Ready; + } + } + + let ready_pids: Vec = guard + .processes + .iter() + .filter(|(_, process)| matches!(process.state, ProcessState::Ready)) + .map(|(&pid, _)| pid) + .collect(); + + if ready_pids.is_empty() { + return previous_pid; + } + + let current_index = ready_pids.iter().position(|&pid| pid == previous_pid); + + return match current_index { + Some(i) => { + let next_index = (i + 1) % ready_pids.len(); + ready_pids[next_index] + } + None => ready_pids[0], + }; + } else { + panic!("Could not get current PID when switching to next task") + }; + } + + pub fn switch_to(&self, pid: u64, should_swapgs: bool) { + let (ctx_opt, entry, stack_top) = { + let mut guard = without_interrupts(|| self.lock()); + + if let Some(previous_pid) = current_pid() { + if let Some(old_process) = guard.processes.get_mut(&previous_pid) { + if matches!(old_process.state, ProcessState::Running) { + old_process.state = ProcessState::Ready; + } + } else { + // no previous process + } + } + + let new_process = guard.processes.get_mut(&pid).expect("Cant get new process"); + new_process.state = ProcessState::Running; + if let Some(address_space) = new_process.address_space.as_mut() { + address_space.use_address_space(); + }; + + ( + new_process.saved_ctx, + new_process.user_entry, + new_process.stack_top, + ) }; set_current_pid(Some(pid)); - enter_usermode(entry_point as u64, (stack_top & !0xF) - 8); + + match ctx_opt { + Some(saved_ctx) => unsafe { + run_next((&saved_ctx) as *const UserContext, saved_ctx.rsp) + }, + None => enter_usermode(entry as u64, (stack_top & !0xF) - 8, should_swapgs), + } } pub fn with_process(&self, index: u64, f: F) -> Option @@ -66,3 +135,30 @@ impl Locked { } pub static SCHEDULER: Locked = Locked::new(Scheduler::new()); + +#[unsafe(naked)] +#[unsafe(no_mangle)] +unsafe fn run_next(ctx: *const UserContext, user_rsp: u64) { + core::arch::naked_asm!( + "mov gs:[0], rsi", // store new user rsp + "mov rsp, rdi", + "mov r15, qword ptr [rsp + 0]", + "mov r14, qword ptr [rsp + 8]", + "mov r13, qword ptr [rsp + 16]", + "mov r12, qword ptr [rsp + 24]", + "mov r11, qword ptr [rsp + 32]", // rflags + "mov r10, qword ptr [rsp + 40]", + "mov r9, qword ptr [rsp + 48]", + "mov r8, qword ptr [rsp + 56]", + "mov rsi, qword ptr [rsp + 64]", + "mov rdi, qword ptr [rsp + 72]", + "mov rbp, qword ptr [rsp + 80]", + "mov rdx, qword ptr [rsp + 88]", + "mov rcx, qword ptr [rsp + 96]", // rip + "mov rbx, qword ptr [rsp + 104]", + "mov rax, qword ptr [rsp + 112]", + "mov rsp, qword ptr [rsp + 120]", // user rsp + "swapgs", + "sysretq", + ); +} diff --git a/user/apps/doomgeneric b/user/apps/doomgeneric index 8a1cf38..703b2e6 160000 --- a/user/apps/doomgeneric +++ b/user/apps/doomgeneric @@ -1 +1 @@ -Subproject commit 8a1cf38d710cb47b110bf9ed21a548f386bc2a5f +Subproject commit 703b2e6e219044c815bef1d2900f7b4f2924d94f diff --git a/user/init b/user/init index 3fe910f..4690716 160000 --- a/user/init +++ b/user/init @@ -1 +1 @@ -Subproject commit 3fe910f33af9fe1bfbd01aeff6abdaae847f54b8 +Subproject commit 469071622757e58287d1c3fc9fe12d505951f89d diff --git a/user/libxunil b/user/libxunil index 222374a..da6f056 160000 --- a/user/libxunil +++ b/user/libxunil @@ -1 +1 @@ -Subproject commit 222374a639d73bfdfc54b2a87136c6704b16d76c +Subproject commit da6f056359e682655623723873c54f7f4c59bcdb