Add correct sleep which works now because interrupts are re-enabled

using sti in syscalls, remove a bunch of old imports and code, move
keyboard scancode handling to keyboard.rs, add a new KeyboardEvent based
layout where press/release and unicode is handled, add a kbd_read
syscall which writes kbd events to a userspace buffer, add a usercopy
file which provides safe copying functions to userspace
This commit is contained in:
csd4ni3l
2026-04-10 12:48:00 +02:00
parent d60f80c8a4
commit c81bed2a4e
22 changed files with 198 additions and 126 deletions

View File

@@ -1,18 +1,13 @@
#[cfg(target_arch = "x86_64")]
pub use crate::arch::x86_64::paging::FRAME_ALLOCATOR_X86_64 as FRAME_ALLOCATOR;
use crate::{driver::timer::TIMER, util::serial_print};
use alloc::string::ToString;
use core::{alloc::GlobalAlloc, arch::asm, sync::atomic::Ordering};
use crate::driver::timer::TIMER;
use core::{alloc::GlobalAlloc, arch::asm};
use limine::response::{HhdmResponse, MemoryMapResponse};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::{
elf::run_elf_x86_64,
heap::ALLOCATOR,
init::init_x86_64,
paging::{FRAME_ALLOCATOR_X86_64, XunilFrameAllocator},
usermode::enter_usermode_x86_64,
elf::run_elf_x86_64, heap::ALLOCATOR, init::init_x86_64, usermode::enter_usermode_x86_64,
};
#[cfg(target_arch = "x86_64")]
use x86_64::structures::paging::OffsetPageTable;
@@ -21,7 +16,7 @@ use x86_64::structures::paging::OffsetPageTable;
pub fn init<'a>(
hhdm_response: &HhdmResponse,
memory_map_response: &'a MemoryMapResponse,
) -> (OffsetPageTable<'static>) {
) -> OffsetPageTable<'static> {
return init_x86_64(hhdm_response, memory_map_response);
}
@@ -51,11 +46,10 @@ pub fn idle() {
}
pub fn sleep(ticks: u64) {
// let start = TIMER.now();
// while start.ticks_since() < ticks {
// serial_print(start.ticks_since().to_string().as_str());
// core::hint::spin_loop();
// }
let start = TIMER.now();
while start.ticks_since() < ticks {
idle();
}
}
pub fn infinite_idle() -> ! {

View File

@@ -1,18 +1,10 @@
use alloc::vec::Vec;
use x86_64::{
VirtAddr,
structures::paging::{
FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, PhysFrame, Size4KiB,
},
structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB},
};
use crate::{
arch::{
arch::FRAME_ALLOCATOR,
x86_64::{paging::XunilFrameAllocator, usermode::enter_usermode_x86_64},
},
task::{process::Process, scheduler::SCHEDULER},
};
use crate::{arch::arch::FRAME_ALLOCATOR, task::scheduler::SCHEDULER};
pub fn run_elf_x86_64(entry_point: *const u8, heap_base: u64) {
let stack_base: u64 = 0x0000_7fff_0000_0000;

View File

@@ -1,5 +1,5 @@
use crate::arch::x86_64::paging::{FRAME_ALLOCATOR_X86_64, XunilFrameAllocator};
use crate::util::{Locked, serial_print};
use crate::arch::x86_64::paging::FRAME_ALLOCATOR_X86_64;
use crate::util::Locked;
use core::{
alloc::{GlobalAlloc, Layout},
ptr::null_mut,

View File

@@ -5,7 +5,6 @@ use crate::{
mouse::setup_mouse,
},
driver::mouse::MOUSE,
util::serial_print,
};
use limine::response::{HhdmResponse, MemoryMapResponse};
use x86_64::{
@@ -23,7 +22,7 @@ const PIT_DIVISOR: u16 = (1_193_182_u32 / TIMER_PRECISION_HZ) as u16;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::{
heap::init_heap,
paging::{FRAME_ALLOCATOR_X86_64, XunilFrameAllocator, initialize_paging},
paging::{FRAME_ALLOCATOR_X86_64, initialize_paging},
};
#[cfg(target_arch = "x86_64")]
use x86_64::{VirtAddr, structures::paging::OffsetPageTable};
@@ -61,6 +60,7 @@ pub fn init_x86_64<'a>(
load_gdt_x86_64();
unsafe {
// setup SSE (SIMD = Single Instruction, Multiple Data)
let mut cr0 = Cr0::read();
cr0.remove(Cr0Flags::EMULATE_COPROCESSOR);
cr0.insert(Cr0Flags::MONITOR_COPROCESSOR);

View File

@@ -1,15 +1,9 @@
use crate::{
arch::x86_64::{gdt, mouse::mouse_interrupt},
driver::{
keyboard::{KEYBOARD_STATE, KeyboardEvent},
mouse::MOUSE,
timer::TIMER,
},
driver::{keyboard::process_keyboard_event, mouse::MOUSE, timer::TIMER},
println,
util::serial_print,
};
use lazy_static::lazy_static;
use pc_keyboard::DecodedKey;
use pic8259::ChainedPics;
use spin::Mutex;
use x86_64::{
@@ -19,8 +13,8 @@ use x86_64::{
structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode},
};
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
pub const PIC_1_OFFSET: u8 = 32; // master
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; // slave
pub static PICS: Mutex<ChainedPics> =
Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
@@ -134,20 +128,8 @@ extern "x86-interrupt" fn mouse_interrupt_handler(_stack_frame: InterruptStackFr
pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
let mut keyboard_state = KEYBOARD_STATE.lock();
if let Ok(Some(key_event)) = keyboard_state.keyboard.add_byte(scancode) {
if let Some(key) = keyboard_state.keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => keyboard_state
.event_queue
.push_back(KeyboardEvent::Unicode(character)),
DecodedKey::RawKey(key) => keyboard_state
.event_queue
.push_back(KeyboardEvent::RawKey(key)),
}
}
}
process_keyboard_event(scancode);
unsafe {
PICS.lock()
@@ -175,6 +157,7 @@ unsafe extern "C" fn syscall_interrupt_handler() {
"push rdi",
"push rax",
"sub rsp, 8",
"sti", // allow IRQ interrupts
"mov rcx, rdx", // arg2
"mov rdx, rsi", // arg1
"mov rsi, rdi", // arg0

View File

@@ -1,4 +1,3 @@
use alloc::string::ToString;
use spin::mutex::Mutex;
use x86_64::{
PhysAddr, VirtAddr,
@@ -8,7 +7,7 @@ use x86_64::{
use limine::memory_map::{Entry, EntryType};
use crate::util::{align_up, serial_print};
use crate::util::align_up;
unsafe fn active_level_4_table(mem_offset: VirtAddr) -> &'static mut PageTable {
let (level_4_table, _) = Cr3::read();

View File

@@ -1,6 +1,5 @@
use core::ptr::null;
use alloc::boxed::Box;
use x86_64::structures::paging::OffsetPageTable;
use crate::driver::{

View File

@@ -1,5 +1,5 @@
use crate::driver::elf::{
header::{Elf64Ehdr, Elf64Rel, Elf64Rela, Elf64Shdr, R_X86_64_64},
header::{Elf64Ehdr, Elf64Rel, Elf64Shdr, R_X86_64_64},
section::{elf_get_symval, elf_section},
};

View File

@@ -80,9 +80,9 @@ impl Framebuffer {
let w = core::cmp::min(src_width, self.width);
for y in 0..h {
let src_row = src_ptr.add(y * src_width);
let dst_row = self.back_buffer.as_mut_ptr().add(y * self.pitch);
core::ptr::copy_nonoverlapping(src_row, dst_row, w);
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) };
}
}

View File

@@ -1,35 +1,79 @@
use alloc::collections::VecDeque;
use lazy_static::lazy_static;
use pc_keyboard::{HandleControl, Keyboard, ScancodeSet1, layouts};
use pc_keyboard::{DecodedKey, HandleControl, KeyState, Keyboard, ScancodeSet2, layouts};
use spin::mutex::Mutex;
use x86_64::instructions::interrupts::without_interrupts;
#[derive(Debug, Clone)]
pub enum KeyboardEvent {
Unicode(char),
RawKey(pc_keyboard::KeyCode),
use crate::task::scheduler::SCHEDULER;
#[repr(C)]
#[derive(Clone, Debug, Copy, Default)]
pub struct KeyboardEvent {
pub state: u8,
pub _pad1: u8,
pub key: u16,
pub mods: u16,
pub _pad2: u16,
pub unicode: u32,
}
pub struct KeyboardState {
pub keyboard: Keyboard<layouts::Us104Key, ScancodeSet1>,
pub event_queue: VecDeque<KeyboardEvent>,
}
impl KeyboardState {
pub fn new() -> KeyboardState {
KeyboardState {
keyboard: Keyboard::new(
ScancodeSet1::new(),
layouts::Us104Key,
HandleControl::Ignore,
),
event_queue: VecDeque::new(),
pub fn process_keyboard_event(scancode: u8) {
let mut keyboard = without_interrupts(|| KEYBOARD.lock());
let scheduler = without_interrupts(|| SCHEDULER.lock());
let pid = scheduler.current_process;
if pid < 0 {
return;
}
drop(scheduler);
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
let keycode = key_event.code;
let keystate = key_event.state;
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => {
SCHEDULER.with_process(pid as u64, |process| {
process.kbd_buffer.push(KeyboardEvent {
state: if keystate == KeyState::Down { 1 } else { 0 },
_pad1: 0,
key: keycode as u16,
mods: 0,
_pad2: 0,
unicode: character as u32,
})
});
}
DecodedKey::RawKey(_) => {
SCHEDULER.with_process(pid as u64, |process| {
process.kbd_buffer.push(KeyboardEvent {
state: if keystate == KeyState::Down { 1 } else { 0 },
_pad1: 0,
key: keycode as u16,
mods: 0,
_pad2: 0,
unicode: 0,
})
});
}
}
} else {
SCHEDULER.with_process(pid as u64, |process| {
process.kbd_buffer.push(KeyboardEvent {
state: if keystate == KeyState::Down { 1 } else { 0 },
_pad1: 0,
key: keycode as u16,
mods: 0,
_pad2: 0,
unicode: 0,
})
});
}
}
}
lazy_static! {
pub static ref KEYBOARD_STATE: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
}
pub fn pop_event() -> Option<KeyboardEvent> {
KEYBOARD_STATE.lock().event_queue.pop_front()
}
pub static KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet2>> = Mutex::new(Keyboard::new(
ScancodeSet2::new(),
layouts::Us104Key,
HandleControl::Ignore,
));

View File

@@ -16,7 +16,7 @@ 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.render_text(self.fb, s, 2, false);
Ok(())
}
}

View File

@@ -3,9 +3,9 @@ use core::{
ptr::null_mut,
};
use alloc::string::ToString;
use x86_64::{
VirtAddr,
instructions::interrupts::without_interrupts,
structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB},
};
@@ -13,11 +13,13 @@ use crate::{
arch::arch::{FRAME_ALLOCATOR, get_allocator, infinite_idle, sleep},
driver::{
graphics::{framebuffer::with_framebuffer, primitives::rectangle_filled},
keyboard::KeyboardEvent,
timer::TIMER,
},
mm::usercopy::copy_to_user,
print, println,
task::scheduler::SCHEDULER,
util::{align_up, serial_print},
util::align_up,
};
const READ: usize = 0;
@@ -41,10 +43,11 @@ const UNLINK: usize = 87;
const GETDENTS64: usize = 217;
const CLOCK_GETTIME: usize = 228;
const EXIT_GROUP: usize = 231;
const KBD_READ: usize = 666;
const SLEEP: usize = 909090; // zzz haha
const DRAW_PIXEL: usize = 5555;
const FRAMEBUFFER_SWAP: usize = 6666;
pub const DRAW_BUFFER: usize = 7777;
const DRAW_BUFFER: usize = 7777;
pub unsafe fn malloc(size: usize, align: usize) -> *mut u8 {
let align = if align < 1 {
@@ -81,8 +84,40 @@ pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) {
unsafe { core::ptr::write_bytes(ptr, val, count) };
}
fn kbd_read(user_ptr: *mut KeyboardEvent, max_events: isize) -> isize {
if max_events <= 0 || user_ptr.is_null() {
return -1;
}
let pid = without_interrupts(|| {
let scheduler = SCHEDULER.lock();
scheduler.current_process
});
if pid < 0 {
return -1;
}
return SCHEDULER
.with_process(pid as u64, |process| {
let to_copy = (max_events as usize).min(process.kbd_buffer.len());
if let Ok(_) = copy_to_user(
&mut process.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;
};
})
.unwrap_or(-1);
}
pub unsafe fn sbrk(increment: isize) -> isize {
let mut scheduler = SCHEDULER.lock();
let scheduler = without_interrupts(|| SCHEDULER.lock());
if scheduler.current_process == -1 {
return -1;
}
@@ -91,7 +126,7 @@ pub unsafe fn sbrk(increment: isize) -> isize {
let mut frame_allocator = FRAME_ALLOCATOR.lock();
return SCHEDULER
.with_process(pid as u64, |mut process| {
.with_process(pid as u64, |process| {
let (heap_end, heap_base, stack_top) =
(process.heap_end, process.heap_base, process.stack_top);
@@ -161,7 +196,7 @@ pub unsafe extern "C" fn syscall_dispatch(
arg2: isize,
) -> isize {
match num {
BRK => sbrk(arg0),
BRK => unsafe { sbrk(arg0) },
WRITE => {
let buf_ptr = arg1 as *const u8;
let len = arg2 as usize;
@@ -185,10 +220,6 @@ pub unsafe extern "C" fn syscall_dispatch(
with_framebuffer(|fb| fb.swap());
infinite_idle();
}
67 => {
println!("{:?}", arg1);
0
}
SLEEP => {
sleep(arg0 as u64);
0
@@ -201,12 +232,13 @@ pub unsafe extern "C" fn syscall_dispatch(
0
}
DRAW_BUFFER => {
with_framebuffer(|mut fb| {
fb.load_from_ptr(arg0 as *const u32, arg1 as usize, arg2 as usize);
with_framebuffer(|fb| {
unsafe { fb.load_from_ptr(arg0 as *const u32, arg1 as usize, arg2 as usize) };
fb.swap();
});
0
}
KBD_READ => kbd_read(arg0 as *mut KeyboardEvent, arg1),
FRAMEBUFFER_SWAP => {
with_framebuffer(|fb| {
fb.swap();

View File

@@ -3,8 +3,6 @@ use core::{
sync::atomic::{AtomicU64, Ordering},
};
use crate::util::serial_print;
pub static TIMER: Timer = Timer::new();
pub struct Timer {

View File

@@ -2,7 +2,6 @@
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(str_from_raw_parts)]
#![allow(warnings)]
extern crate alloc;
use core::fmt::Write;
@@ -24,7 +23,6 @@ use crate::driver::graphics::framebuffer::{init_framebuffer, with_framebuffer};
use crate::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console};
use crate::driver::timer::TIMER;
use crate::userspace_stub::userspace_init;
use crate::util::serial_print;
/// Sets the base revision to the latest revision supported by the crate.
/// See specification for further info.
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.

View File

@@ -4,10 +4,7 @@ use x86_64::{
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
};
use crate::{
arch::{arch::FRAME_ALLOCATOR, x86_64::paging::XunilFrameAllocator},
driver::syscall::memset,
};
use crate::arch::arch::FRAME_ALLOCATOR;
pub struct AddressSpace {
cr3_frame: PhysFrame<Size4KiB>,
@@ -24,7 +21,7 @@ impl AddressSpace {
core::ptr::write_bytes(new_pml4_ptr, 0, 512);
}
let (cur_pml4, pml4_flags) = Cr3::read();
let (cur_pml4, _) = Cr3::read();
unsafe {
let cur_pml4_ptr =
@@ -38,7 +35,7 @@ impl AddressSpace {
}
}
let mut mapper = unsafe {
let mapper = unsafe {
let addr = frame_allocator.hhdm_offset + new_pml4.start_address().as_u64();
let virtual_addr = VirtAddr::new(addr);
let level_4_table: *mut PageTable = virtual_addr.as_mut_ptr();

View File

@@ -1 +1,2 @@
pub mod address_space;
pub mod usercopy;

39
kernel/src/mm/usercopy.rs Normal file
View File

@@ -0,0 +1,39 @@
use x86_64::{
VirtAddr,
structures::paging::{OffsetPageTable, PageTableFlags, Translate, mapper::TranslateResult},
};
pub fn copy_to_user(
mapper: &mut OffsetPageTable,
buf: *mut u8,
src: *const u8,
len: usize,
) -> Result<(), isize> {
let start = buf as u64;
let end = start + len as u64;
let mut page_addr = start & !0xFFF;
while page_addr < end {
let translate_result = mapper.translate(VirtAddr::new(page_addr));
#[allow(non_shorthand_field_patterns)]
if let TranslateResult::Mapped {
frame: _,
offset: _,
flags: flags,
} = translate_result
{
if flags.contains(PageTableFlags::USER_ACCESSIBLE)
&& flags.contains(PageTableFlags::WRITABLE)
{
} else {
return Err(-13);
}
} else {
return Err(-1);
}
page_addr += 0x1000;
}
unsafe { core::ptr::copy_nonoverlapping(src, buf, len) };
Ok(())
}

View File

@@ -1,6 +1,8 @@
use crate::{arch::x86_64::paging::XunilFrameAllocator, mm::address_space::AddressSpace};
use alloc::vec::Vec;
enum ProcessState {
use crate::{driver::keyboard::KeyboardEvent, mm::address_space::AddressSpace};
pub enum ProcessState {
Ready,
Running,
Blocked,
@@ -10,10 +12,10 @@ enum ProcessState {
pub struct Process {
pub pid: u64,
pub state: ProcessState,
// cpu_ctx: &[u8],
pub stack_top: u64,
pub heap_base: u64,
pub heap_end: u64,
pub kbd_buffer: Vec<KeyboardEvent>,
pub address_space: AddressSpace,
pub user_entry: u64,
}
@@ -33,6 +35,7 @@ impl Process {
state: ProcessState::Ready,
heap_base,
heap_end,
kbd_buffer: Vec::new(),
address_space,
user_entry,
})

View File

@@ -1,11 +1,7 @@
use alloc::collections::btree_map::BTreeMap;
use lazy_static::lazy_static;
use x86_64::instructions::interrupts::without_interrupts;
use crate::{
arch::{arch::enter_usermode, x86_64::paging::XunilFrameAllocator},
task::process::Process,
util::Locked,
};
use crate::{arch::arch::enter_usermode, task::process::Process, util::Locked};
pub struct Scheduler {
pub processes: BTreeMap<u64, Process>,
@@ -25,7 +21,7 @@ impl Scheduler {
impl Locked<Scheduler> {
pub fn spawn_process(&self, entry_point: u64, stack_top: u64, heap_base: u64) -> Option<u64> {
let mut guard = self.lock();
let mut guard = without_interrupts(|| self.lock());
let pid = guard.next_pid;
guard.next_pid += 1;
let process = Process::new(pid, entry_point, stack_top, heap_base, heap_base)?;
@@ -35,7 +31,7 @@ impl Locked<Scheduler> {
}
pub fn run_process(&self, pid: u64, entry_point: *const u8) {
let mut guard = self.lock();
let mut guard = without_interrupts(|| self.lock());
let stack_top = guard.processes[&pid].stack_top;
guard.current_process = pid as i64;
@@ -48,7 +44,7 @@ impl Locked<Scheduler> {
where
F: FnOnce(&mut Process) -> R,
{
let mut guard = self.lock();
let mut guard = without_interrupts(|| self.lock());
let process = guard.processes.get_mut(&index)?;
Some(f(process))
}

View File

@@ -2,10 +2,7 @@ use alloc::string::ToString;
use x86_64::structures::paging::OffsetPageTable;
use crate::{
arch::{
arch::{FRAME_ALLOCATOR, run_elf, sleep},
x86_64::paging::XunilFrameAllocator,
},
arch::arch::{run_elf, sleep},
driver::{
elf::loader::load_file,
graphics::{