diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..bd674f4 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,2 @@ +target remote localhost:1234 + diff --git a/Cargo.lock b/Cargo.lock index 157aaf9..9393da9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,24 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "autocfg" version = "1.3.0" @@ -26,6 +44,47 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "derive_more" version = "0.99.17" @@ -34,7 +93,17 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", ] [[package]] @@ -42,6 +111,8 @@ name = "kernel" version = "0.1.0" dependencies = [ "bitflags 2.5.0", + "crossbeam", + "hashbrown", "lazy_static", "log", "multiboot2", @@ -99,6 +170,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60bfe75a40f755f162b794140436c57845cb106fd1467598631c76c6fff08e28" +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "pc-keyboard" version = "0.7.0" @@ -140,7 +217,7 @@ checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -199,6 +276,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "talc" version = "4.4.1" @@ -242,6 +330,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "volatile" version = "0.4.6" @@ -270,3 +364,23 @@ dependencies = [ "rustversion", "volatile", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/boot/boot.s b/boot/boot.s index dc3291b..baae3f7 100644 --- a/boot/boot.s +++ b/boot/boot.s @@ -1,6 +1,11 @@ global _start extern long_mode_start +%define ERR_NO_MB "0" +%define ERR_NO_CPUID "1" +%define ERR_NO_LONG_MODE "2" +%define ERR_NO_V86 "3" + section .text bits 32 _start: @@ -19,6 +24,7 @@ _start: call set_up_page_tables call enable_paging + call enable_sce ; load the 64-bit GDT lgdt [gdt64.pointer] @@ -32,12 +38,29 @@ _start: mov dword [0xb8000], 0x2f4b2f4f hlt +detect_v86: + smsw ax + and eax,1 ;CR0.PE bit + cmp eax, 1 + je .no_v86 + ret +.no_v86: + mov al, ERR_NO_V86 + jmp error + +enable_sce: ; System Call Extensions + mov ecx, 0xC0000080 + rdmsr + or eax, 1 + wrmsr + ret + check_multiboot: cmp eax, 0x36d76289 jne .no_multiboot ret .no_multiboot: - mov al, "0" + mov al, ERR_NO_MB jmp error check_cpuid: @@ -73,7 +96,7 @@ check_cpuid: je .no_cpuid ret .no_cpuid: - mov al, "1" + mov al, ERR_NO_CPUID jmp error check_long_mode: @@ -90,7 +113,7 @@ check_long_mode: jz .no_long_mode ; If it's not set, there is no long mode ret .no_long_mode: - mov al, "2" + mov al, ERR_NO_LONG_MODE jmp error set_up_page_tables: diff --git a/boot/grub.cfg b/boot/grub.cfg index a396edd..82433aa 100644 --- a/boot/grub.cfg +++ b/boot/grub.cfg @@ -1,7 +1,9 @@ set timeout=0 set default=0 - +insmod all_video menuentry "poppin" { multiboot2 /boot/poppin.bin + set gfxpayload=1024x768x32 + boot } diff --git a/build.sh b/build.sh index d998715..5742933 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,14 @@ function pre_build() { function build_kernel() { echo "INFO: Building poppin-kernel" pushd kernel > /dev/null - cargo build --release --target-dir ../${BUILD_DIR} + case $1 in + dev) + cargo build --target-dir ../${BUILD_DIR} + ;; + *) + cargo build --release --target-dir ../${BUILD_DIR} + ;; + esac popd > /dev/null } @@ -46,7 +53,7 @@ function link(){ ${BUILD_DIR}/multiboot.o \ ${BUILD_DIR}/boot.o \ ${BUILD_DIR}/long_mode.o \ - ${BUILD_DIR}/x86_64-unknown-none/release/libkernel.a + $1 # kernel path } function check_mb() { @@ -71,6 +78,15 @@ function run_qemu() { -cdrom $ISO \ -serial stdio \ -device VGA + # -d int \ +} + +function run_qemu_gdb { + qemu-system-x86_64 \ + -cdrom $ISO \ + -device VGA \ + -d int \ + -s -S -monitor stdio } function print_help() { @@ -84,11 +100,21 @@ function build() { pre_build build_kernel build_boot - link + link ${BUILD_DIR}/x86_64-unknown-none/release/libkernel.a check_mb make_iso } +function build_dev() { + pre_build + build_kernel dev + build_boot + link ${BUILD_DIR}/x86_64-unknown-none/debug/libkernel.a + check_mb + make_iso + +} + case $1 in run-qemu) @@ -98,6 +124,13 @@ case $1 in build) build ;; + build-dev) + build_dev + ;; + run-qemu-gdb) + build + run_qemu_gdb + ;; *) if [ $# -gt 0 ]; then echo "ERROR: Unknown option '$1'" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 6e8c03a..46f350a 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -10,8 +10,13 @@ test=false crate-type=["staticlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +strip = false + [dependencies] bitflags = "2.5.0" +crossbeam = { version = "0.8.4", default-features = false, features = ["alloc"] } +hashbrown = "0.14.5" lazy_static = { version = "1.4.0", features = ["spin_no_std"] } log = "0.4.21" multiboot2 = "0.20.2" diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/kernel/src/events/mod.rs b/kernel/src/events/mod.rs index 0536cbc..205f16b 100644 --- a/kernel/src/events/mod.rs +++ b/kernel/src/events/mod.rs @@ -7,7 +7,6 @@ mod types; use spin::Mutex; pub use types::*; - const MAX_EVENT_LISTENERS: usize = 255; lazy_static!( diff --git a/kernel/src/events/types.rs b/kernel/src/events/types.rs index a983df6..0a151d1 100644 --- a/kernel/src/events/types.rs +++ b/kernel/src/events/types.rs @@ -1,10 +1,10 @@ -use pc_keyboard::DecodedKey; +use pc_keyboard::KeyEvent; #[allow(dead_code)] #[derive(Debug)] pub enum Event { TimerInterrupt(Option), // trash unused value - Ps2KeyPress(Option) + Ps2KeyEvent(Option), } #[macro_export] diff --git a/kernel/src/gdt/mod.rs b/kernel/src/gdt/mod.rs index df6d187..20e8d06 100644 --- a/kernel/src/gdt/mod.rs +++ b/kernel/src/gdt/mod.rs @@ -1,50 +1,90 @@ use core::ptr::addr_of; - -use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; +use x86_64::instructions::segmentation::{CS, DS, Segment}; +use x86_64::structures::gdt::{Descriptor, DescriptorFlags, GlobalDescriptorTable, SegmentSelector}; use x86_64::VirtAddr; use x86_64::structures::tss::TaskStateSegment; use lazy_static::lazy_static; + +pub const STACK_SIZE: usize = 4096 * 5; +pub static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; +pub static mut PRIV_TSS_STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; + + + pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; lazy_static! { static ref TSS: TaskStateSegment = { let mut tss = TaskStateSegment::new(); tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { - const STACK_SIZE: usize = 4096 * 5; - static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; - + + #[allow(unused_unsafe)] let stack_start = VirtAddr::from_ptr(unsafe { addr_of!(STACK) }); let stack_end = stack_start + STACK_SIZE as u64; stack_end }; + tss.privilege_stack_table[0] = { + #[allow(unused_unsafe)] + let stack_start = VirtAddr::from_ptr(unsafe { addr_of!(PRIV_TSS_STACK) }); + let stack_end = stack_start + STACK_SIZE as u64; + stack_end + }; tss }; } lazy_static! { - static ref GDT: (GlobalDescriptorTable, Selectors) = { + pub static ref GDT: (GlobalDescriptorTable, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); - let code_selector = gdt.append(Descriptor::kernel_code_segment()); - let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); - (gdt, Selectors { code_selector, tss_selector }) + let kernel_data_flags = DescriptorFlags::USER_SEGMENT | DescriptorFlags::PRESENT | DescriptorFlags::WRITABLE; + + let code_sel = gdt.append(Descriptor::kernel_code_segment()); + let data_sel = gdt.append(Descriptor::UserSegment(kernel_data_flags.bits())); + + let tss_sel = gdt.append(Descriptor::tss_segment(&TSS)); + let user_data_sel = gdt.append(Descriptor::user_data_segment()); // user data segment + let user_code_sel = gdt.append(Descriptor::user_code_segment()); // user code segment + (gdt, Selectors {code_sel, data_sel, tss_sel, user_data_sel, user_code_sel }) }; } -struct Selectors { - code_selector: SegmentSelector, - tss_selector: SegmentSelector, +#[allow(dead_code)] +#[derive(Debug)] +pub struct Selectors { + pub code_sel: SegmentSelector, + pub data_sel: SegmentSelector, + pub tss_sel: SegmentSelector, + pub user_data_sel: SegmentSelector, + pub user_code_sel: SegmentSelector } pub fn init() { - log::info!("GDT init"); use x86_64::instructions::tables::load_tss; - use x86_64::instructions::segmentation::{CS, Segment}; - + #[allow(unused_unsafe)] + let stack = unsafe { addr_of!(STACK) as *const _ }; + #[allow(unused_unsafe)] + let user_stack = unsafe { addr_of!(PRIV_TSS_STACK) as *const _ }; + log::info!("gdt init"); + log::debug!("gdt = {:?}", &GDT.0 as *const _ ); + log::debug!("tss = {:?}", &*TSS as *const _ ); + log::debug!("kernel_stack = {:?}", stack); + log::debug!("user_stack = {:?}", user_stack); + log::debug!("kernel_code_sel = {:?}", GDT.1.code_sel); + log::debug!("kernel_data_sel = {:?}", GDT.1.data_sel); + log::debug!("tss_sel = {:?}", GDT.1.tss_sel); + log::debug!("user_code_sel = {:?}", GDT.1.user_code_sel); + log::debug!("user_code_sel = {:?}", GDT.1.user_data_sel); + + GDT.0.load(); unsafe { - CS::set_reg(GDT.1.code_selector); - load_tss(GDT.1.tss_selector); + DS::set_reg(GDT.1.data_sel); + CS::set_reg(GDT.1.code_sel); + load_tss(GDT.1.tss_sel); } } + + + diff --git a/kernel/src/interrupts/handlers/mod.rs b/kernel/src/interrupts/handlers/mod.rs index 3601c09..4b2c4cb 100644 --- a/kernel/src/interrupts/handlers/mod.rs +++ b/kernel/src/interrupts/handlers/mod.rs @@ -12,7 +12,7 @@ pub extern "x86-interrupt" fn breakpoint(sf: InterruptStackFrame){ pub extern "x86-interrupt" fn timer_interrupt(_: InterruptStackFrame){ //log::debug!("INT: TIMER\n{:#?}", sf); // print!("."); - EVENTMAN.lock().dispatch(&crate::events::Event::TimerInterrupt(None)); + // EVENTMAN.lock().dispatch(&crate::events::Event::TimerInterrupt(None)); unsafe { super::PICS.lock() .notify_end_of_interrupt(InterruptIndex::Timer as u8); diff --git a/kernel/src/interrupts/handlers/ps2_keyboard.rs b/kernel/src/interrupts/handlers/ps2_keyboard.rs index 416d2b7..af174b5 100644 --- a/kernel/src/interrupts/handlers/ps2_keyboard.rs +++ b/kernel/src/interrupts/handlers/ps2_keyboard.rs @@ -1,4 +1,6 @@ -use pc_keyboard::DecodedKey; +use hashbrown::HashSet; +use pc_keyboard::{DecodedKey, KeyState}; +use spin::Mutex; use x86_64::structures::idt::InterruptStackFrame; use crate::{events::EVENTMAN, interrupts::PICS}; @@ -16,15 +18,13 @@ pub extern "x86-interrupt" fn ps2_kb_int(_sf: InterruptStackFrame){ layouts::Us104Key, HandleControl::Ignore) ); } - + let mut keyboard = KEYBOARD.lock(); let mut port = Port::new(0x60); let scancode: u8 = unsafe { port.read() }; if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { - if let Some(key) = keyboard.process_keyevent(key_event) { - EVENTMAN.lock().dispatch(&crate::events::Event::Ps2KeyPress(Some(key))); - } + EVENTMAN.lock().dispatch(&crate::events::Event::Ps2KeyEvent(Some(key_event))); } unsafe { PICS.lock() diff --git a/kernel/src/interrupts/mod.rs b/kernel/src/interrupts/mod.rs index 9dcf1e7..7a3207f 100644 --- a/kernel/src/interrupts/mod.rs +++ b/kernel/src/interrupts/mod.rs @@ -14,7 +14,6 @@ lazy_static::lazy_static! { idt.breakpoint.set_handler_fn(handlers::breakpoint); idt.page_fault.set_handler_fn(handlers::page_fault); - idt[InterruptIndex::Timer as u8] .set_handler_fn(handlers::timer_interrupt); idt[InterruptIndex::Keyboard as u8] diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index d22310b..303a616 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -6,7 +6,13 @@ #![feature(ptr_internals)] #![feature(allocator_api)] +use core::ptr::addr_of; + use alloc::vec::Vec; +use pc_keyboard::{DecodedKey, KeyCode}; +use x86_64::instructions::nop; + +use crate::{gdt::PRIV_TSS_STACK, scheduler::{executor::Executor, Task}, utils::jmp_to_usermode}; extern crate alloc; @@ -18,7 +24,8 @@ mod utils; #[macro_use] mod events; mod mem; - +mod scheduler; +mod std; @@ -30,19 +37,28 @@ extern "C" fn kmain(mb2_info_addr: usize) -> ! { interrupts::init_idt(); utils::enable_nxe_bit(); utils::enable_write_protect_bit(); + std::init(); - mem::init(mb2_info_addr); + let active_table = mem::init(mb2_info_addr); + + //unsafe { + // jmp_to_usermode(active_table, usermode_code as *const () as usize, addr_of!(PRIV_TSS_STACK) as *const () as usize); + //} - let mut a = Vec::new(); + //let mut executor = Executor::new(); + //executor.spawn(Task::new(usermode_code())); + //executor.run(); + + //let mut a = Vec::new(); - a.push(6); - a.push(9); - a.push(4); - a.push(2); - a.push(0); + //a.push(6); + //a.push(9); + //a.push(4); + //a.push(2); + //a.push(0); - log::error!("{:?}", a); + //log::error!("{:?}", a); @@ -56,8 +72,12 @@ extern "C" fn kmain(mb2_info_addr: usize) -> ! { // let (level_4_page_table, _) = Cr3::read(); // println!("Level 4 page table at: {:?}", level_4_page_table.start_address()); log::info!("end of work"); - - utils::hcf(); + loop { + if std::input::is_key_down(KeyCode::A) { + log::info!(":3"); + } + } + // utils::hcf(); } @@ -68,3 +88,6 @@ fn panic(pi: &core::panic::PanicInfo) -> ! { } +async fn usermode_code() { + log::info!("Hello from usermode!"); +} diff --git a/kernel/src/mem/heap/mod.rs b/kernel/src/mem/heap/mod.rs index a1442db..14a100a 100644 --- a/kernel/src/mem/heap/mod.rs +++ b/kernel/src/mem/heap/mod.rs @@ -1,46 +1,46 @@ -use core::{alloc::{AllocError, GlobalAlloc, Layout}, sync::atomic::{AtomicUsize, Ordering}}; +//use core::{alloc::{AllocError, GlobalAlloc, Layout}, sync::atomic::{AtomicUsize, Ordering}}; -use x86_64::align_up; +//use x86_64::align_up; -pub const HEAP_START: usize = 0o_000_001_000_000_0000; +pub const HEAP_START: usize = 0o_000_001_000_000_0000; // 0x40000000 - 0x40019000 pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB -/// A simple allocator that allocates memory linearly and ignores freed memory. -#[derive(Debug)] -pub struct BumpAllocator { - heap_start: usize, - heap_end: usize, - next: AtomicUsize, -} +// A simple allocator that allocates memory linearly and ignores freed memory. +//#[derive(Debug)] +//pub struct BumpAllocator { +// heap_start: usize, +// heap_end: usize, +// next: AtomicUsize, +//} -impl BumpAllocator { - pub const fn new(heap_start: usize, heap_end: usize) -> Self { - Self { heap_start, heap_end, next: AtomicUsize::new(heap_start) } - } -} +//impl BumpAllocator { +// pub const fn new(heap_start: usize, heap_end: usize) -> Self { +// Self { heap_start, heap_end, next: AtomicUsize::new(heap_start) } +// } +//} -unsafe impl GlobalAlloc for BumpAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - loop { - // load current state of the `next` field - let current_next = self.next.load(Ordering::Relaxed); - let alloc_start = align_up(current_next as u64, layout.align() as u64); - let alloc_end = alloc_start.saturating_add(layout.size() as u64); +//unsafe impl GlobalAlloc for BumpAllocator { +// unsafe fn alloc(&self, layout: Layout) -> *mut u8 { +// loop { +// // load current state of the `next` field +// let current_next = self.next.load(Ordering::Relaxed); +// let alloc_start = align_up(current_next as u64, layout.align() as u64); +// let alloc_end = alloc_start.saturating_add(layout.size() as u64); +// +// if alloc_end <= self.heap_end as u64 { +// // update the `next` pointer if it still has the value `current_next` +// let next_now = self.next.compare_exchange(current_next, alloc_end as usize, Ordering::Relaxed, Ordering::Relaxed).unwrap(); +// if next_now == current_next { +// // next address was successfully updated, allocation succeeded +// return alloc_start as *mut u8; +// } +// } else { +// panic!("OUT OF MEMORY"); +// } +// } +// } - if alloc_end <= self.heap_end as u64 { - // update the `next` pointer if it still has the value `current_next` - let next_now = self.next.compare_exchange(current_next, alloc_end as usize, Ordering::Relaxed, Ordering::Relaxed).unwrap(); - if next_now == current_next { - // next address was successfully updated, allocation succeeded - return alloc_start as *mut u8; - } - } else { - panic!("OUT OF MEMORY"); - } - } - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // do nothing, leak memory - } -} +// unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { +// // do nothing, leak memory +// } +//} diff --git a/kernel/src/mem/mod.rs b/kernel/src/mem/mod.rs index e2188a7..30272a5 100644 --- a/kernel/src/mem/mod.rs +++ b/kernel/src/mem/mod.rs @@ -161,7 +161,7 @@ impl MemInfo<'_> { } } -pub fn init(mb_ptr: usize) { +pub fn init(mb_ptr: usize) -> paging::ActivePageTable { once::assert_has_not_been_called!("mem::init must be called only once"); let mem_info = MemInfo::load(mb_ptr); let mut frame_alloc = area_frame_alloc::AreaFrameAllocator::new(&mem_info); @@ -179,4 +179,5 @@ pub fn init(mb_ptr: usize) { unsafe { GLOBAL_ALLOCATOR.lock().claim(Span::from_base_size(HEAP_START as *mut u8, HEAP_SIZE)).unwrap(); } + active_table } diff --git a/kernel/src/mem/paging/mod.rs b/kernel/src/mem/paging/mod.rs index 79f9097..470bc4c 100644 --- a/kernel/src/mem/paging/mod.rs +++ b/kernel/src/mem/paging/mod.rs @@ -189,7 +189,9 @@ pub fn remap_the_kernel(allocator: &mut A, mem_info: &MemInfo) -> ActivePageT continue; } - log::debug!("mapping section '{}' at addr: {:#x}, size: {:#x}", section.name().unwrap_or("NONE"), section.start_address(), section.size()); + //log::debug!("mapping section '{}' at addr: {:#x}, size: {:#x}", + // section.name().unwrap_or("NONE"), section.start_address(), section.size()); + assert!(section.start_address() as usize % PAGE_SIZE == 0, "sections need to be page aligned"); diff --git a/kernel/src/scheduler/executor.rs b/kernel/src/scheduler/executor.rs new file mode 100644 index 0000000..3779e5b --- /dev/null +++ b/kernel/src/scheduler/executor.rs @@ -0,0 +1,98 @@ +use core::task::{Context, Poll, Waker}; + +use super::{Task, TaskId}; +use alloc::{collections::BTreeMap, sync::Arc, task::Wake}; +use crossbeam::queue::ArrayQueue; + +pub struct Executor { + tasks: BTreeMap, + task_queue: Arc>, + waker_cache: BTreeMap, +} + +impl Executor { + pub fn new() -> Self { + Self { + tasks: BTreeMap::new(), + task_queue: Arc::new(ArrayQueue::new(100)), + waker_cache: BTreeMap::new(), + } + } + + pub fn spawn(&mut self, task: Task) { + let task_id = task.id; + if self.tasks.insert(task.id, task).is_some() { + panic!("task with same ID already in tasks"); + } + self.task_queue.push(task_id).expect("queue full"); + } + + + + pub fn run(&mut self) { + loop { + if self.task_queue.len() == 0 { + log::debug!("Task queue is empty, scheduler exiting"); + break; + } + self.run_ready_tasks(); + } + } + fn run_ready_tasks(&mut self) { + // destructure `self` to avoid borrow checker errors + let Self { + tasks, + task_queue, + waker_cache, + } = self; + + while let Some(task_id) = task_queue.pop() { + let task = match tasks.get_mut(&task_id) { + Some(task) => task, + None => continue, // task no longer exists + }; + let waker = waker_cache + .entry(task_id) + .or_insert_with(|| TaskWaker::new(task_id, task_queue.clone())); + let mut context = Context::from_waker(waker); + match task.poll(&mut context) { + Poll::Ready(()) => { + // task done -> remove it and its cached waker + tasks.remove(&task_id); + waker_cache.remove(&task_id); + } + Poll::Pending => {} + } + } + } +} + + +struct TaskWaker { + task_id: TaskId, + task_queue: Arc>, +} + +impl TaskWaker { + fn wake_task(&self) { + self.task_queue.push(self.task_id).expect("task_queue full"); + } + fn new(task_id: TaskId, task_queue: Arc>) -> Waker { + Waker::from(Arc::new(TaskWaker { + task_id, + task_queue, + })) + } +} + +impl Wake for TaskWaker { + fn wake(self: Arc) { + self.wake_task(); + } + + fn wake_by_ref(self: &Arc) { + self.wake_task(); + } +} + + diff --git a/kernel/src/scheduler/mod.rs b/kernel/src/scheduler/mod.rs new file mode 100644 index 0000000..ff09c65 --- /dev/null +++ b/kernel/src/scheduler/mod.rs @@ -0,0 +1,32 @@ +use core::{future::Future, pin::Pin, sync::atomic::{AtomicU64, Ordering}, task::{Context, Poll}}; +use alloc::boxed::Box; + +pub mod executor; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct TaskId(u64); + +pub struct Task { + id: TaskId, + future: Pin>>, +} + +impl Task { + pub fn new(f: impl Future + 'static) -> Self { + Self { + id: TaskId::new(), + future: Box::pin(f) + } + } + fn poll(&mut self, context: &mut Context) -> Poll<()> { + self.future.as_mut().poll(context) + } +} + +impl TaskId { + fn new() -> Self { + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed)) + } +} + diff --git a/kernel/src/std/input.rs b/kernel/src/std/input.rs new file mode 100644 index 0000000..d7e340a --- /dev/null +++ b/kernel/src/std/input.rs @@ -0,0 +1,50 @@ +use alloc::{boxed::Box, collections::{btree_set::BTreeSet, vec_deque::VecDeque}, sync::Arc, vec::Vec}; +use hashbrown::HashSet; +use lazy_static::lazy_static; +use pc_keyboard::{DecodedKey, KeyCode, KeyEvent, KeyState}; +use spin::Mutex; +use x86_64::instructions::nop; + +use crate::events::{Event, EVENTMAN}; +static mut KEYS_PRESSED: Mutex> = Mutex::new(BTreeSet::new()); +static mut KEY_BUF: VecDeque = VecDeque::new(); +const KEY_BUF_MAX_LEN: usize = 32; + +pub(super) unsafe fn init() { + EVENTMAN.lock().add_listener(crate::events::Event::Ps2KeyEvent(None), |e| { + let Event::Ps2KeyEvent(ke) = e else {panic!()}; + if let Some(ke) = ke { + if KEY_BUF.len() > KEY_BUF_MAX_LEN { + KEY_BUF.pop_front(); + } + KEY_BUF.push_back(ke.clone()); + } + + Ok(()) + }) +} + +// TODO: It is possible for `KEY_BUF` to get overfilled and lose data and make keys stuck. +// Possibly run this every now an again somehow +unsafe fn process_events() { + while !KEY_BUF.is_empty() { + if let Some(ke) = KEY_BUF.pop_front() { + match ke.state { + KeyState::Down => { + KEYS_PRESSED.lock().insert(ke.code); + } + KeyState::Up => { + KEYS_PRESSED.lock().remove(&ke.code); + } + _ => () + } + } + } +} + +pub fn is_key_down(key: KeyCode) -> bool { + unsafe { + process_events(); + return KEYS_PRESSED.lock().contains(&key); + } +} diff --git a/kernel/src/std/mod.rs b/kernel/src/std/mod.rs new file mode 100644 index 0000000..e9f79e0 --- /dev/null +++ b/kernel/src/std/mod.rs @@ -0,0 +1,8 @@ +pub mod input; + + + + +pub fn init() { + unsafe { input::init(); }; +} diff --git a/kernel/src/syscalls/mod.rs b/kernel/src/syscalls/mod.rs new file mode 100644 index 0000000..c57e0fb --- /dev/null +++ b/kernel/src/syscalls/mod.rs @@ -0,0 +1,13 @@ + + +// register for address of syscall handler +const MSR_STAR: usize = 0xC0000081; + + +pub fn init() { + asm!("\ + xor rax, rax + mov rdx, 0x230008 // use seg selectors 8, 16 for syscall and 43, 51 for sysret + wrmsr" :: "{rcx}"(MSR_STAR) : "rax", "rdx" : "intel", "volatile"); + +} diff --git a/kernel/src/utils.rs b/kernel/src/utils.rs index c9d91b1..e9114b7 100644 --- a/kernel/src/utils.rs +++ b/kernel/src/utils.rs @@ -1,5 +1,9 @@ +use core::arch::asm; + use x86::msr::{rdmsr, wrmsr, IA32_EFER}; -use x86_64::{instructions, registers::control::{Cr0, Cr0Flags}}; +use x86_64::{instructions, registers::{control::{Cr0, Cr0Flags}, segmentation::{Segment, DS}}, PrivilegeLevel}; + +use crate::mem::paging::{ActivePageTable, VirtualAddress}; @@ -22,3 +26,31 @@ pub fn enable_write_protect_bit() { unsafe { Cr0::write(Cr0::read() | Cr0Flags::WRITE_PROTECT) }; } + + +pub unsafe fn set_usermode_segments() -> (u16, u16) { + // set ds and tss, return cs and ds + let (mut cs, mut ds) = (crate::gdt::GDT.1.user_code_sel, crate::gdt::GDT.1.user_data_sel); + cs.0 |= PrivilegeLevel::Ring3 as u16; + ds.0 |= PrivilegeLevel::Ring3 as u16; + DS::set_reg(ds); + (cs.0, ds.0) +} + + +#[no_mangle] +pub unsafe fn jmp_to_usermode(apt: ActivePageTable, code: VirtualAddress, stack_end: VirtualAddress) { + let (cs_idx, ds_idx) = set_usermode_segments(); + x86_64::instructions::tlb::flush_all(); // flush the TLB after address-space switch + let code_addr = apt.translate(code).expect("Invalid virt addr"); + let sa_addr = apt.translate(stack_end).expect("Invalid virt addr"); + + asm!("push {ds:x}", // stack segment + "push {sa}", // rsp + "push 0x200", // rflags (only interrupt bit set) + "push {cs:x}", // code segment + "push {code}", // ret to virtual addr + "iretq", + code = in(reg) code_addr, sa = in(reg) sa_addr, cs = in(reg) cs_idx, ds = in(reg) ds_idx); +} + diff --git a/linker.ld b/linker.ld index c5abdd5..f8c8842 100644 --- a/linker.ld +++ b/linker.ld @@ -56,5 +56,5 @@ SECTIONS { .gcc_except_table : ALIGN(4K) { *(.gcc_except_table) . = ALIGN(4K); - }` + } }