diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index a0eac43..3c015d6 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -10,6 +10,8 @@ dependencies = [ "lazy_static", "limine", "micromath", + "pc-keyboard", + "pic8259", "spin 0.10.0", "x86_64", ] @@ -71,6 +73,21 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" +[[package]] +name = "pc-keyboard" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca629cbb3f0d5b699c338f0129ff78c9bfd7ea8b1258ad529bff490dc8ed5a" + +[[package]] +name = "pic8259" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d9a86c292b165f757e47e7fd66855def189b2564609bc4203727b27c33db22" +dependencies = [ + "x86_64", +] + [[package]] name = "rustversion" version = "1.0.22" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index a6f35f3..2051cf7 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -13,7 +13,11 @@ font8x8 = { version = "0.3.1", default-features = false } lazy_static = { version = "1.5.0", features = ["spin_no_std"] } limine = "0.5" micromath = "2.1.0" +pc-keyboard = "0.8.0" +pic8259 = "0.11.0" spin = "0.10.0" + +[target.'cfg(target_arch = "x86_64")'.dependencies] x86_64 = "0.15.4" [profile.dev] diff --git a/kernel/src/arch/arch.rs b/kernel/src/arch/arch.rs new file mode 100644 index 0000000..8a0f63f --- /dev/null +++ b/kernel/src/arch/arch.rs @@ -0,0 +1,54 @@ +use core::arch::asm; +use limine::response::{HhdmResponse, MemoryMapResponse}; + +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::{ + init::init_x86_64, + paging::{XunilFrameAllocator, example_mapping, initialize_paging}, +}; +#[cfg(target_arch = "x86_64")] +use x86_64::{ + VirtAddr, registers::control::Cr3, structures::paging::OffsetPageTable, + structures::paging::Page, +}; + +#[cfg(target_arch = "x86_64")] +pub fn memory_management_init( + hhdm_response: &HhdmResponse, + memory_map_response: &MemoryMapResponse, +) -> OffsetPageTable<'static> { + let physical_offset = VirtAddr::new(hhdm_response.offset()); + let (frame, _) = Cr3::read(); + let mut mapper = unsafe { initialize_paging(physical_offset) }; + + let l4_virt = physical_offset + frame.start_address().as_u64() + 0xb8000; + let mut frame_allocator = XunilFrameAllocator::new(memory_map_response.entries()); + let page = Page::containing_address(l4_virt); + unsafe { + example_mapping(page, &mut mapper, &mut frame_allocator); + } + mapper +} + +#[cfg(target_arch = "x86_64")] +pub fn init( + hhdm_response: &HhdmResponse, + memory_map_response: &MemoryMapResponse, +) -> OffsetPageTable<'static> { + init_x86_64(); + + return memory_management_init(hhdm_response, memory_map_response); +} + +pub fn idle() -> ! { + loop { + unsafe { + #[cfg(target_arch = "x86_64")] + asm!("hlt"); + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + asm!("wfi"); + #[cfg(target_arch = "loongarch64")] + asm!("idle 0"); + } + } +} diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs index 51391f2..1a1477d 100644 --- a/kernel/src/arch/mod.rs +++ b/kernel/src/arch/mod.rs @@ -1,3 +1,3 @@ -pub mod serial; +pub mod arch; #[cfg(target_arch = "x86_64")] pub mod x86_64; diff --git a/kernel/src/arch/x86_64/heap.rs b/kernel/src/arch/x86_64/heap.rs new file mode 100644 index 0000000..e69de29 diff --git a/kernel/src/arch/x86_64/idt.rs b/kernel/src/arch/x86_64/idt.rs deleted file mode 100644 index d9fc462..0000000 --- a/kernel/src/arch/x86_64/idt.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::arch::x86_64::{ - gdt, - interrupts::{breakpoint_handler, double_fault_handler}, -}; -use lazy_static::lazy_static; -use x86_64::structures::idt::InterruptDescriptorTable; - -lazy_static! { - static ref IDT: InterruptDescriptorTable = { - let mut idt = InterruptDescriptorTable::new(); - idt.breakpoint.set_handler_fn(breakpoint_handler); - unsafe { - idt.double_fault - .set_handler_fn(double_fault_handler) - .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); - } - idt - }; -} - -pub fn init_idt_x86_64() { - IDT.load(); -} diff --git a/kernel/src/arch/x86_64/init.rs b/kernel/src/arch/x86_64/init.rs new file mode 100644 index 0000000..9fc9e62 --- /dev/null +++ b/kernel/src/arch/x86_64/init.rs @@ -0,0 +1,16 @@ +use crate::arch::x86_64::gdt::load_gdt_x86_64; +use crate::arch::x86_64::interrupts::{PICS, init_idt_x86_64}; +use x86_64::instructions::interrupts; + +pub fn init_x86_64() { + load_gdt_x86_64(); + init_idt_x86_64(); + + unsafe { + let mut pics = PICS.lock(); + pics.initialize(); + pics.write_masks(0xFC, 0xFF); + } + + interrupts::enable(); +} diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 48a5657..51e11e9 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,5 +1,56 @@ -use crate::println; -use x86_64::structures::idt::InterruptStackFrame; +use crate::{arch::x86_64::gdt, driver::keyboard::keyboard_interrupt_handler, println}; +use lazy_static::lazy_static; +use pic8259::ChainedPics; +use spin::Mutex; +pub use x86_64::instructions::interrupts::without_interrupts; +use x86_64::{ + registers::control::Cr2, + structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}, +}; + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +pub static PICS: Mutex = + Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum InterruptIndex { + Timer = PIC_1_OFFSET, + Keyboard, // putting it below increments it by 1, so its offset + 1 +} + +impl InterruptIndex { + pub fn as_u8(self) -> u8 { + self as u8 + } + + fn as_usize(self) -> usize { + usize::from(self.as_u8()) + } +} + +lazy_static! { + static ref IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + unsafe { + idt.double_fault + .set_handler_fn(double_fault_handler) + .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); + } + idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); + idt.page_fault.set_handler_fn(page_fault_handler); + idt.general_protection_fault.set_handler_fn(gpf_handler); + idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); + idt + }; +} + +pub fn init_idt_x86_64() { + IDT.load(); +} pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); @@ -11,3 +62,29 @@ pub extern "x86-interrupt" fn double_fault_handler( ) -> ! { panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); } + +pub extern "x86-interrupt" fn page_fault_handler( + stack_frame: InterruptStackFrame, + error_code: PageFaultErrorCode, +) { + panic!( + "EXCEPTION: PAGE FAULT\nAccessed Addresss: {:?}\nError Code: {:?}\n{:#?}", + Cr2::read(), + error_code, + stack_frame + ); +} + +pub extern "x86-interrupt" fn gpf_handler(stack_frame: InterruptStackFrame, error_code: u64) { + panic!( + "EXCEPTION: GENERAL PROTECTION FAULT\nError Code: {:?}\n{:#?}", + error_code, stack_frame + ); +} + +extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { + unsafe { + PICS.lock() + .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); + } +} diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 1e974d7..1118747 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -1,4 +1,4 @@ pub mod gdt; -pub mod idt; +pub mod init; pub mod interrupts; pub mod paging; diff --git a/kernel/src/arch/x86_64/paging.rs b/kernel/src/arch/x86_64/paging.rs index 8b13789..ac49dc4 100644 --- a/kernel/src/arch/x86_64/paging.rs +++ b/kernel/src/arch/x86_64/paging.rs @@ -1 +1,71 @@ +use x86_64::{ + PhysAddr, VirtAddr, + registers::control::Cr3, + structures::paging::{ + FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PageTableFlags as Flags, + PhysFrame, Size2MiB, Size4KiB, + }, +}; +use limine::memory_map::{Entry, EntryType}; + +unsafe fn active_level_4_table(mem_offset: VirtAddr) -> &'static mut PageTable { + let (level_4_table, _) = Cr3::read(); + + let physical_addr = level_4_table.start_address(); + let virtual_addr = mem_offset + physical_addr.as_u64(); + let page_table_ptr: *mut PageTable = virtual_addr.as_mut_ptr(); + + unsafe { &mut *page_table_ptr } +} + +pub unsafe fn initialize_paging(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { + unsafe { + let level_4_table = active_level_4_table(physical_memory_offset); + OffsetPageTable::new(level_4_table, physical_memory_offset) + } +} + +pub struct XunilFrameAllocator<'a> { + next: usize, + memory_map: &'a [&'a Entry], +} + +impl<'a> XunilFrameAllocator<'a> { + pub fn new(memory_map: &'a [&'a Entry]) -> Self { + Self { + next: 0, + memory_map, + } + } + + fn usable_frames(&self) -> impl Iterator { + let regions = self.memory_map.iter(); + let usable = regions.filter(|region| region.entry_type == EntryType::USABLE); + let ranges = usable + .map(|usable_region| usable_region.base..usable_region.base + usable_region.length); + let frame_addresses = ranges.flat_map(|r| r.step_by(4096)); + + frame_addresses + .map(|frame_address| PhysFrame::containing_address(PhysAddr::new(frame_address))) + } +} + +unsafe impl<'a> FrameAllocator for XunilFrameAllocator<'a> { + fn allocate_frame(&mut self) -> Option> { + let frame = self.usable_frames().nth(self.next); + self.next += 1; + frame + } +} + +pub unsafe fn example_mapping( + page: Page, + mapper: &mut OffsetPageTable, + frame_allocator: &mut impl FrameAllocator, +) { + let frame = PhysFrame::::containing_address(PhysAddr::new(0x0000_1234_4000_0000)); + let flags = Flags::PRESENT | Flags::WRITABLE; + let map_to_result = unsafe { mapper.map_to(page, frame, flags, frame_allocator) }; + map_to_result.expect("map_to failed").flush(); +} diff --git a/kernel/src/driver/graphics/framebuffer.rs b/kernel/src/driver/graphics/framebuffer.rs index 7226696..bbb40b2 100644 --- a/kernel/src/driver/graphics/framebuffer.rs +++ b/kernel/src/driver/graphics/framebuffer.rs @@ -1,6 +1,9 @@ use limine::framebuffer::Framebuffer as LimineFramebuffer; use spin::Mutex; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::interrupts::without_interrupts; + const MAX_BACKBUFFER_PIXELS: usize = 1920 * 1080; static mut BACK_BUFFER: [u32; MAX_BACKBUFFER_PIXELS] = [0; MAX_BACKBUFFER_PIXELS]; @@ -62,8 +65,10 @@ pub fn init_framebuffer(raw: &LimineFramebuffer) { } pub fn with_framebuffer(f: F) { - let mut guard = FRAMEBUFFER.lock(); - if let Some(fb) = guard.as_mut() { - f(fb); - } + without_interrupts(|| { + let mut guard = FRAMEBUFFER.lock(); + if let Some(fb) = guard.as_mut() { + f(fb); + } + }) } diff --git a/kernel/src/driver/keyboard.rs b/kernel/src/driver/keyboard.rs index 8b13789..bea3a87 100644 --- a/kernel/src/driver/keyboard.rs +++ b/kernel/src/driver/keyboard.rs @@ -1 +1,37 @@ +use crate::{ + arch::x86_64::interrupts::{InterruptIndex, PICS}, + print, +}; +use lazy_static::lazy_static; +use pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1, layouts}; +use spin::Mutex; +use x86_64::{instructions::port::Port, structures::idt::InterruptStackFrame}; +lazy_static! { + static ref KEYBOARD: Mutex> = + Mutex::new(Keyboard::new( + ScancodeSet1::new(), + layouts::Us104Key, + HandleControl::Ignore + )); +} + +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 = KEYBOARD.lock(); + + if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { + if let Some(key) = keyboard.process_keyevent(key_event) { + match key { + DecodedKey::Unicode(character) => print!("{}", character), + DecodedKey::RawKey(key) => print!("{:?}", key), + } + } + } + + unsafe { + PICS.lock() + .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8()); + } +} diff --git a/kernel/src/driver/mod.rs b/kernel/src/driver/mod.rs index 48edc24..d18d51b 100644 --- a/kernel/src/driver/mod.rs +++ b/kernel/src/driver/mod.rs @@ -1,3 +1,4 @@ pub mod graphics; pub mod keyboard; +pub mod serial; pub mod timer; diff --git a/kernel/src/arch/serial.rs b/kernel/src/driver/serial.rs similarity index 88% rename from kernel/src/arch/serial.rs rename to kernel/src/driver/serial.rs index 51aaa89..d38e372 100644 --- a/kernel/src/arch/serial.rs +++ b/kernel/src/driver/serial.rs @@ -4,6 +4,9 @@ use crate::driver::graphics::framebuffer::Framebuffer; use core::fmt::{self, Write}; use spin::Mutex; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::interrupts::without_interrupts; + pub struct ConsoleWriter<'a> { pub fb: &'a mut Framebuffer, pub console: &'a mut SerialConsole, @@ -70,8 +73,10 @@ pub fn init_serial_console(start_x: usize, start_y: usize) { } pub fn with_serial_console(f: F) { - let mut guard = SERIAL_CONSOLE.lock(); - if let Some(fb) = guard.as_mut() { - f(fb); - } + without_interrupts(|| { + let mut guard = SERIAL_CONSOLE.lock(); + if let Some(fb) = guard.as_mut() { + f(fb); + } + }) } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 784a48e..84feeec 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -2,25 +2,22 @@ #![no_main] #![feature(abi_x86_interrupt)] -use core::arch::asm; use core::fmt::Write; use limine::BaseRevision; -use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker}; - +use limine::request::{ + FramebufferRequest, HhdmRequest, MemoryMapRequest, RequestsEndMarker, RequestsStartMarker, +}; pub mod arch; pub mod driver; -use crate::arch::serial::{ConsoleWriter, init_serial_console, with_serial_console}; -#[cfg(target_arch = "x86_64")] -use crate::arch::x86_64::gdt::load_gdt_x86_64; +use crate::arch::arch::{idle, init}; use crate::driver::graphics::base::rgb; use crate::driver::graphics::framebuffer::{init_framebuffer, with_framebuffer}; use crate::driver::graphics::primitives::{ circle_filled, circle_outline, rectangle_filled, rectangle_outline, triangle_outline, }; - -use crate::arch::x86_64::idt::init_idt_x86_64; +use crate::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console}; /// Sets the base revision to the latest revision supported by the crate. /// See specification for further info. @@ -34,6 +31,14 @@ static BASE_REVISION: BaseRevision = BaseRevision::new(); #[unsafe(link_section = ".requests")] static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); +#[used] +#[unsafe(link_section = ".requests")] +static HHDM_REQUEST: HhdmRequest = HhdmRequest::new(); + +#[used] +#[unsafe(link_section = ".requests")] +static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new(); + /// Define the stand and end markers for Limine requests. #[used] #[unsafe(link_section = ".requests_start_marker")] @@ -80,28 +85,36 @@ unsafe extern "C" fn kmain() -> ! { // removed by the linker. assert!(BASE_REVISION.is_supported()); - #[cfg(target_arch = "x86_64")] - { - load_gdt_x86_64(); - init_idt_x86_64(); - } - if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() { if let Some(limine_framebuffer) = framebuffer_response.framebuffers().next() { init_framebuffer(&limine_framebuffer); - with_framebuffer(|mut fb| { - let (width, height) = (fb.width.clone(), fb.height.clone()); - init_serial_console(0, 0); - rectangle_filled(&mut fb, 0, 0, width, height, rgb(253, 129, 0), true); - rectangle_filled(&mut fb, 700, 400, 200, 200, rgb(0, 0, 0), true); - rectangle_outline(&mut fb, 400, 400, 100, 100, rgb(0, 0, 0)); - circle_filled(&mut fb, 200, 200, 100.0, rgb(0, 0, 0)); - circle_outline(&mut fb, 400, 200, 100.0, rgb(0, 0, 0)); - triangle_outline(&mut fb, 100, 400, 200, 400, 150, 600, rgb(0, 0, 0)); - }); + // boot_animation(); } } + if let Some(hhdm_response) = HHDM_REQUEST.get_response() { + if let Some(memory_map_response) = MEMORY_MAP_REQUEST.get_response() { + let mapper = init(hhdm_response, memory_map_response); + } else { + init_serial_console(0, 0); + panic!("Could not get required info from Limine's memory map. ") + } + } else { + init_serial_console(0, 0); + panic!("Could not get required info from the Limine's higher-half direct mapping. ") + } + + with_framebuffer(|mut fb| { + let (width, height) = (fb.width.clone(), fb.height.clone()); + init_serial_console(0, 0); + rectangle_filled(&mut fb, 0, 0, width, height, rgb(253, 129, 0), true); + rectangle_filled(&mut fb, 700, 400, 200, 200, rgb(0, 0, 0), true); + rectangle_outline(&mut fb, 400, 400, 100, 100, rgb(0, 0, 0)); + circle_filled(&mut fb, 200, 200, 100.0, rgb(0, 0, 0)); + circle_outline(&mut fb, 400, 200, 100.0, rgb(0, 0, 0)); + triangle_outline(&mut fb, 100, 400, 200, 400, 150, 600, rgb(0, 0, 0)); + }); + idle(); } @@ -128,16 +141,3 @@ fn rust_panic(_info: &core::panic::PanicInfo) -> ! { idle(); } - -fn idle() -> ! { - loop { - unsafe { - #[cfg(target_arch = "x86_64")] - asm!("hlt"); - #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] - asm!("wfi"); - #[cfg(target_arch = "loongarch64")] - asm!("idle 0"); - } - } -} diff --git a/kernel/src/mm/frame_alloc.rs b/kernel/src/mm/frame_alloc.rs deleted file mode 100644 index 8b13789..0000000 --- a/kernel/src/mm/frame_alloc.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/kernel/src/mm/heap.rs b/kernel/src/mm/heap.rs deleted file mode 100644 index 8b13789..0000000 --- a/kernel/src/mm/heap.rs +++ /dev/null @@ -1 +0,0 @@ -