Add correct framebuffer mapping, so Doom works again, remove unneded

code, add cooperative scheduling on syscalls determined by the
reschedule flag, make serial console automatically clear and be a single
render, make process_scancodes push to all processes, add a user-facing
framebuffer to be mapped, automatically swap buffers inside timer
interrupt, update all submodules
This commit is contained in:
csd4ni3l
2026-05-11 19:09:20 +02:00
parent 925b2bc8d2
commit 85e0d45d3c
25 changed files with 695 additions and 466 deletions
+4 -4
View File
@@ -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 {
+250 -92
View File
@@ -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::<u32>()) 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::<Size4KiB>::containing_address(PhysAddr::new(phys));
let page = Page::<Size4KiB>::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::<isize>(-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::<KeyboardEvent>(),
) {
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::<KeyboardEvent>(),
) {
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::<Size4KiB>::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::<u8>(), 0, 4096);
core::ptr::write_bytes(virt_addr.as_mut_ptr::<u8>(), 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::<isize>(-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
}
+2 -2
View File
@@ -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;
};
+14 -1
View File
@@ -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());
+108 -35
View File
@@ -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
"#,
);
}
+5 -2
View File
@@ -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}",
+4 -78
View File
@@ -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;
}
-2
View File
@@ -1,6 +1,4 @@
mod header;
pub mod loader;
mod program;
mod reloc;
mod section;
pub mod validation;
+24 -27
View File
@@ -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,
-32
View File
@@ -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;
}
-91
View File
@@ -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
};
}
}
+75 -39
View File
@@ -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<u32>,
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::<u32>();
let pixel_frames = (byte_len + 4095) / 4096;
let mut fa = FRAME_ALLOCATOR.lock();
let struct_frame: PhysFrame<Size4KiB> =
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<Size4KiB> =
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::<u32>(),
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<Option<Framebuffer>> = Mutex::new(None);
+4 -4
View File
@@ -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);
}
}
}
+29 -36
View File
@@ -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<Option<SerialConsole>> = 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: FnOnce(&mut SerialConsole)>(f: F) {
+11
View File
@@ -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);
}
+4 -8
View File
@@ -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();
});
});
+1 -1
View File
@@ -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
}
+34
View File
@@ -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);
}
}
}
+19 -1
View File
@@ -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<u64>,
}
pub struct Process {
pub pid: u64,
pub state: ProcessState,
@@ -17,7 +25,10 @@ pub struct Process {
pub heap_end: u64,
pub kbd_buffer: Vec<KeyboardEvent>,
pub address_space: Option<AddressSpace>,
pub saved_ctx: Option<UserContext>,
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,
},
}
}
}
+103 -7
View File
@@ -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<Scheduler> {
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<u64> = 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<F, R>(&self, index: u64, f: F) -> Option<R>
@@ -66,3 +135,30 @@ impl Locked<Scheduler> {
}
pub static SCHEDULER: Locked<Scheduler> = 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",
);
}