mirror of
https://github.com/XunilGroup/XunilOS.git
synced 2026-06-02 11:44:24 +02:00
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:
Binary file not shown.
@@ -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 ../../../..
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
mod header;
|
||||
pub mod loader;
|
||||
mod program;
|
||||
mod reloc;
|
||||
mod section;
|
||||
pub mod validation;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
Submodule user/apps/doomgeneric updated: 8a1cf38d71...703b2e6e21
+1
-1
Submodule user/init updated: 3fe910f33a...4690716227
+1
-1
Submodule user/libxunil updated: 222374a639...da6f056359
Reference in New Issue
Block a user