From aa5cd85b48963ac179c397c9748cbc50259826d6 Mon Sep 17 00:00:00 2001 From: csd4ni3l Date: Sat, 28 Mar 2026 21:03:13 +0100 Subject: [PATCH] Add a kernel crash function, add a timer using the timer interrupt and set PIT interval to 1000hz, use a vector inside Framebuffer, remove without_interrupts from the framebuffer and serial console, move to usize on primitives, add date at boot and turn on multithreading, add a boot animation and add a test_performance function with the new timer --- kernel/src/arch/arch.rs | 52 +++++++-- kernel/src/arch/x86_64/init.rs | 23 +++- kernel/src/arch/x86_64/interrupts.rs | 5 +- kernel/src/driver/graphics/font_render.rs | 1 - kernel/src/driver/graphics/framebuffer.rs | 36 ++++--- kernel/src/driver/graphics/primitives.rs | 106 +++++++------------ kernel/src/driver/serial.rs | 10 +- kernel/src/driver/timer.rs | 66 ++++++++++++ kernel/src/main.rs | 122 ++++++++++++++++++---- kernel/src/util.rs | 8 ++ 10 files changed, 301 insertions(+), 128 deletions(-) diff --git a/kernel/src/arch/arch.rs b/kernel/src/arch/arch.rs index 36a7835..88dca40 100644 --- a/kernel/src/arch/arch.rs +++ b/kernel/src/arch/arch.rs @@ -1,3 +1,4 @@ +use crate::driver::timer::TIMER; use core::arch::asm; use limine::response::{HhdmResponse, MemoryMapResponse}; @@ -14,15 +15,46 @@ pub fn init<'a>( return init_x86_64(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"); - } +pub fn idle() { + 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"); } } + +pub fn sleep(ticks: u64) { + let start = TIMER.now(); + while (TIMER.now() - start).elapsed() <= ticks { + idle(); + } +} + +pub fn infinite_idle() -> ! { + loop { + idle() + } +} + +#[inline(always)] +pub fn kernel_crash() -> ! { + #[cfg(target_arch = "x86_64")] + unsafe { + core::arch::asm!("ud2") + }; + + #[cfg(target_arch = "aarch64")] + unsafe { + core::arch::asm!("udf #0") + }; + + #[cfg(target_arch = "riscv64")] + unsafe { + core::arch::asm!("unimp") + }; + + loop {} // satisfies -> ! on unknown archs +} diff --git a/kernel/src/arch/x86_64/init.rs b/kernel/src/arch/x86_64/init.rs index 6670f02..da773f0 100644 --- a/kernel/src/arch/x86_64/init.rs +++ b/kernel/src/arch/x86_64/init.rs @@ -1,7 +1,11 @@ use crate::arch::x86_64::gdt::load_gdt_x86_64; -use crate::arch::x86_64::interrupts::{PICS, init_idt_x86_64}; +use crate::arch::x86_64::interrupts::{InterruptIndex, PICS, init_idt_x86_64}; use limine::response::{HhdmResponse, MemoryMapResponse}; -use x86_64::instructions::interrupts; +use x86_64::instructions::interrupts::without_interrupts; +use x86_64::instructions::{interrupts, port::Port}; + +const TIMER_PRECISION_HZ: u32 = 1000; +const PIT_DIVISOR: u16 = (1_193_182_u32 / TIMER_PRECISION_HZ) as u16; #[cfg(target_arch = "x86_64")] use crate::arch::x86_64::{ @@ -22,6 +26,19 @@ pub fn memory_management_init<'a>( (mapper, frame_allocator) } +pub fn set_pit_interval() { + without_interrupts(|| { + let mut command_port: Port = Port::new(0x43); + let mut data_port: Port = Port::new(0x40); + + unsafe { + command_port.write(0b00_11_011_0); + data_port.write((PIT_DIVISOR & 0xFF) as u8); // low byte + data_port.write(((PIT_DIVISOR >> 8) & 0xFF) as u8); // high byte + } + }); +} + pub fn init_x86_64<'a>( hhdm_response: &HhdmResponse, memory_map_response: &'a MemoryMapResponse, @@ -35,6 +52,8 @@ pub fn init_x86_64<'a>( pics.write_masks(0xFC, 0xFF); } + set_pit_interval(); + interrupts::enable(); let (mut mapper, mut frame_allocator) = diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 51e11e9..9a653e0 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,3 +1,4 @@ +use crate::driver::timer::TIMER; use crate::{arch::x86_64::gdt, driver::keyboard::keyboard_interrupt_handler, println}; use lazy_static::lazy_static; use pic8259::ChainedPics; @@ -18,7 +19,7 @@ pub static PICS: Mutex = #[repr(u8)] pub enum InterruptIndex { Timer = PIC_1_OFFSET, - Keyboard, // putting it below increments it by 1, so its offset + 1 + Keyboard = PIC_1_OFFSET + 1, } impl InterruptIndex { @@ -83,6 +84,8 @@ pub extern "x86-interrupt" fn gpf_handler(stack_frame: InterruptStackFrame, erro } extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { + TIMER.interrupt(); + unsafe { PICS.lock() .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); diff --git a/kernel/src/driver/graphics/font_render.rs b/kernel/src/driver/graphics/font_render.rs index 0b47205..51e1dcf 100644 --- a/kernel/src/driver/graphics/font_render.rs +++ b/kernel/src/driver/graphics/font_render.rs @@ -23,7 +23,6 @@ pub fn render_char( font_size, font_size, color, - false, ); } } diff --git a/kernel/src/driver/graphics/framebuffer.rs b/kernel/src/driver/graphics/framebuffer.rs index bbb40b2..81199a6 100644 --- a/kernel/src/driver/graphics/framebuffer.rs +++ b/kernel/src/driver/graphics/framebuffer.rs @@ -1,15 +1,14 @@ +use alloc::vec; +use alloc::vec::Vec; 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]; - pub struct Framebuffer { addr: *mut u32, - back_buffer: *mut u32, + back_buffer: Vec, pub width: usize, pub height: usize, pitch: usize, @@ -21,12 +20,11 @@ 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 needed = pitch.saturating_mul(height); - let back_buffer_len = core::cmp::min(needed, MAX_BACKBUFFER_PIXELS); + let back_buffer_len = width * height; Framebuffer { addr: limine_fb.addr().cast::(), - back_buffer: core::ptr::addr_of_mut!(BACK_BUFFER).cast::(), + back_buffer: vec![0u32; width * height], width, height, pitch, @@ -44,16 +42,22 @@ impl Framebuffer { return; } - unsafe { - *self.back_buffer.add(idx) = color; - } + self.back_buffer[idx] = color; } pub fn swap(&mut self) { unsafe { - core::ptr::copy_nonoverlapping(self.back_buffer, self.addr, self.back_buffer_len); + core::ptr::copy_nonoverlapping( + self.back_buffer.as_ptr(), + self.addr, + self.back_buffer_len, + ); } } + + pub fn clear(&mut self, color: u32) { + self.back_buffer.fill(color); + } } unsafe impl Send for Framebuffer {} @@ -65,10 +69,8 @@ pub fn init_framebuffer(raw: &LimineFramebuffer) { } pub fn with_framebuffer(f: F) { - without_interrupts(|| { - let mut guard = FRAMEBUFFER.lock(); - if let Some(fb) = guard.as_mut() { - f(fb); - } - }) + let mut guard = FRAMEBUFFER.lock(); + if let Some(fb) = guard.as_mut() { + f(fb); + } } diff --git a/kernel/src/driver/graphics/primitives.rs b/kernel/src/driver/graphics/primitives.rs index 2be03a7..1e195e7 100644 --- a/kernel/src/driver/graphics/primitives.rs +++ b/kernel/src/driver/graphics/primitives.rs @@ -1,43 +1,45 @@ -use crate::driver::graphics::framebuffer::Framebuffer; +use crate::driver::graphics::{base::rgb, font_render::render_text, framebuffer::Framebuffer}; +use alloc::{format, string::ToString}; use core::f32::consts::PI; use micromath::F32Ext; -pub fn line(framebuffer: &mut Framebuffer, mut x0: i64, mut y0: i64, x1: i64, y1: i64, color: u32) { - let mut dx = x1 - x0; - let mut dy = y1 - y0; +pub fn line(framebuffer: &mut Framebuffer, x0: usize, y0: usize, x1: usize, y1: usize, color: u32) { + let mut x0 = x0 as isize; + let mut y0 = y0 as isize; + let x1 = x1 as isize; + let y1 = y1 as isize; + + let mut dx: isize = x1 - x0; + let mut dy: isize = y1 - y0; if dx < 0 { dx = -dx; } - if dy < 0 { dy = -dy; } - let step_x = if x0 < x1 { 1 } else { -1 }; - let step_y = if y0 < y1 { 1 } else { -1 }; + let step_x: isize = if x0 < x1 { 1 } else { -1 }; + let step_y: isize = if y0 < y1 { 1 } else { -1 }; - let mut error = dx - dy; - let mut e2: i64; + let mut error: isize = dx - dy; loop { framebuffer.put_pixel(x0 as usize, y0 as usize, color); - if x0 == x1 && y0 == y1 { - break; - } - - e2 = 2 * error; - + let e2: isize = 2 * error; if e2 > -dy { error -= dy; x0 += step_x; } - if e2 < dx { error += dx; y0 += step_y; } + + if x0 == x1 && y0 == y1 { + break; + } } } @@ -51,31 +53,9 @@ pub fn triangle_outline( y3: usize, color: u32, ) { - line( - framebuffer, - x1 as i64, - y1 as i64, - x2 as i64, - y2 as i64, - color, - ); - line( - framebuffer, - x1 as i64, - y1 as i64, - x3 as i64, - y3 as i64, - color, - ); - line( - framebuffer, - x2 as i64, - y2 as i64, - x3 as i64, - y3 as i64, - color, - ); - framebuffer.swap(); + line(framebuffer, x1, y1, x2, y2, color); + line(framebuffer, x1, y1, x3, y3, color); + line(framebuffer, x2, y2, x3, y3, color); } pub fn circle_outline(framebuffer: &mut Framebuffer, x: usize, y: usize, radius: f32, color: u32) { @@ -92,42 +72,37 @@ pub fn circle_outline(framebuffer: &mut Framebuffer, x: usize, y: usize, radius: break; } } - framebuffer.swap(); } pub fn circle_filled(framebuffer: &mut Framebuffer, x0: usize, y0: usize, radius: f32, color: u32) { - let mut x = radius as i64; - let mut y: i64 = 0; - let mut x_change: i64 = 1 - (radius as i64 * 2); - let mut y_change: i64 = 0; - let mut radius_error: i64 = 0; + let mut x = radius as isize; + let mut y: isize = 0; + let mut x_change: isize = 1 - (radius as isize * 2); + let mut y_change: isize = 0; + let mut radius_error: isize = 0; while x >= y { - let mut i = x0 as i64 - x; - while i <= x0 as i64 + x { - framebuffer.put_pixel(i as usize, (y0 as i64 + y) as usize, color); - framebuffer.put_pixel(i as usize, (y0 as i64 - y) as usize, color); + let mut i = x0 as isize - x; + while i <= x0 as isize + x { + framebuffer.put_pixel(i as usize, (y0 as isize + y) as usize, color); + framebuffer.put_pixel(i as usize, (y0 as isize - y) as usize, color); i += 1; } - - let mut i = x0 as i64 - y; - while i <= x0 as i64 + y { - framebuffer.put_pixel(i as usize, (y0 as i64 + x) as usize, color); - framebuffer.put_pixel(i as usize, (y0 as i64 - x) as usize, color); + let mut i = x0 as isize - y; + while i <= x0 as isize + y { + framebuffer.put_pixel(i as usize, (y0 as isize + x) as usize, color); + framebuffer.put_pixel(i as usize, (y0 as isize - x) as usize, color); i += 1; } - y += 1; radius_error += y_change; y_change += 2; - if (radius_error * 2) + x_change > 0 { x -= 1; radius_error += x_change; x_change += 2; } } - framebuffer.swap(); } pub fn rectangle_filled( @@ -137,29 +112,24 @@ pub fn rectangle_filled( width: usize, height: usize, color: u32, - swap: bool, ) { for fb_x in x..x + width { for fb_y in y..y + height { framebuffer.put_pixel(fb_x, fb_y, color); } } - if swap { - framebuffer.swap(); - } } pub fn rectangle_outline( framebuffer: &mut Framebuffer, - x: i64, - y: i64, - width: i64, - height: i64, + x: usize, + y: usize, + width: usize, + height: usize, color: u32, ) { line(framebuffer, x, y, x + width, y, color); // bottomleft -> bottomright line(framebuffer, x, y + height, x + width, y + height, color); // topleft -> topright line(framebuffer, x, y, x, y + height, color); // bottomleft -> topleft line(framebuffer, x + width, y, x + width, y + height, color); // bottomright -> topright - framebuffer.swap(); } diff --git a/kernel/src/driver/serial.rs b/kernel/src/driver/serial.rs index d38e372..b9e486c 100644 --- a/kernel/src/driver/serial.rs +++ b/kernel/src/driver/serial.rs @@ -73,10 +73,8 @@ pub fn init_serial_console(start_x: usize, start_y: usize) { } pub fn with_serial_console(f: F) { - without_interrupts(|| { - let mut guard = SERIAL_CONSOLE.lock(); - if let Some(fb) = guard.as_mut() { - f(fb); - } - }) + let mut guard = SERIAL_CONSOLE.lock(); + if let Some(fb) = guard.as_mut() { + f(fb); + } } diff --git a/kernel/src/driver/timer.rs b/kernel/src/driver/timer.rs index 8b13789..55242eb 100644 --- a/kernel/src/driver/timer.rs +++ b/kernel/src/driver/timer.rs @@ -1 +1,67 @@ +use core::{ + ops::{Add, Sub}, + sync::atomic::{AtomicU64, Ordering}, +}; +pub static TIMER: Timer = Timer::new(); + +pub struct Timer { + pub interrupt_count: AtomicU64, + pub date_at_boot: AtomicU64, +} + +impl Timer { + pub const fn new() -> Self { + Self { + interrupt_count: AtomicU64::new(0), + date_at_boot: AtomicU64::new(0), + } + } + + pub fn set_date_at_boot(&self, date_at_boot: u64) { + self.date_at_boot.store(date_at_boot, Ordering::Relaxed); + } + + pub fn get_date_at_boot(&self) -> u64 { + self.date_at_boot.load(Ordering::Relaxed) + } + + pub fn interrupt(&self) { + self.interrupt_count.fetch_add(1, Ordering::Relaxed); + } + + pub fn now(&self) -> Time { + Time { + interrupt_count: self.interrupt_count.load(Ordering::Relaxed), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub struct Time { + pub interrupt_count: u64, +} + +impl Time { + pub fn new(interrupt_count: u64) -> Self { + Self { interrupt_count } + } + + pub fn elapsed(&self) -> u64 { + self.interrupt_count + } +} + +impl Add for Time { + type Output = Time; + fn add(self, other: Time) -> Time { + Time::new(self.interrupt_count + other.interrupt_count) + } +} + +impl Sub for Time { + type Output = Time; + fn sub(self, other: Time) -> Time { + Time::new(self.interrupt_count - other.interrupt_count) + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index e9984d7..5526e0a 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -7,20 +7,23 @@ use core::fmt::Write; use limine::BaseRevision; use limine::request::{ - FramebufferRequest, HhdmRequest, MemoryMapRequest, RequestsEndMarker, RequestsStartMarker, + DateAtBootRequest, FramebufferRequest, HhdmRequest, MemoryMapRequest, MpRequest, + RequestsEndMarker, RequestsStartMarker, }; pub mod arch; pub mod driver; pub mod util; -use crate::arch::arch::{idle, init}; +use crate::arch::arch::{infinite_idle, init, kernel_crash, sleep}; 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::driver::serial::{ConsoleWriter, init_serial_console, with_serial_console}; -use alloc::{boxed::Box, vec::Vec}; +use crate::driver::timer::TIMER; +use crate::util::test_performance; +use alloc::{boxed::Box, string::ToString, vec::Vec}; /// Sets the base revision to the latest revision supported by the crate. /// See specification for further info. @@ -42,6 +45,14 @@ static HHDM_REQUEST: HhdmRequest = HhdmRequest::new(); #[unsafe(link_section = ".requests")] static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new(); +#[used] +#[unsafe(link_section = ".requests")] +static DATE_AT_BOOT_REQUEST: DateAtBootRequest = DateAtBootRequest::new(); + +#[used] +#[unsafe(link_section = ".requests")] +static MP_REQUEST: MpRequest = MpRequest::new(); + /// Define the stand and end markers for Limine requests. #[used] #[unsafe(link_section = ".requests_start_marker")] @@ -88,6 +99,16 @@ unsafe extern "C" fn kmain() -> ! { // removed by the linker. assert!(BASE_REVISION.is_supported()); + if let Some(hhdm_response) = HHDM_REQUEST.get_response() { + if let Some(memory_map_response) = MEMORY_MAP_REQUEST.get_response() { + let (mapper, frame_allocator) = init(hhdm_response, memory_map_response); + } else { + kernel_crash(); // Could not get required info from Limine's memory map. + } + } else { + kernel_crash(); // Could not get required info from the Limine's higher-half direct mapping. + } + if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() { if let Some(limine_framebuffer) = framebuffer_response.framebuffers().next() { init_framebuffer(&limine_framebuffer); @@ -96,26 +117,13 @@ unsafe extern "C" fn kmain() -> ! { init_serial_console(0, 0); - if let Some(hhdm_response) = HHDM_REQUEST.get_response() { - if let Some(memory_map_response) = MEMORY_MAP_REQUEST.get_response() { - let (mapper, frame_allocator) = init(hhdm_response, memory_map_response); - } else { - panic!("Could not get required info from Limine's memory map. ") - } + if let Some(date_at_boot_response) = DATE_AT_BOOT_REQUEST.get_response() { + TIMER.set_date_at_boot(date_at_boot_response.timestamp().as_secs()); } else { - panic!("Could not get required info from the Limine's higher-half direct mapping. ") + println!("Could not get date at boot. Will default to 0.") } - 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(); let x = Box::new(41); let mut test_vec: Vec = Vec::new(); @@ -124,14 +132,82 @@ unsafe extern "C" fn kmain() -> ! { test_vec.push(9); println!("After: {:?}", test_vec); - idle(); + sleep(500); + + loop { + with_serial_console(|serial_console| serial_console.clear(0, 0)); + + with_framebuffer(|mut fb| { + fb.clear(rgb(253, 129, 0)); + + // rectangle_filled(&mut fb, 700, 400, 200, 200, rgb(0, 0, 0)); + // 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)); + }); + + let (hours, minutes, seconds) = + unix_to_hms(TIMER.get_date_at_boot() + (TIMER.now().elapsed()) / 1000); + + print!("{:?}:{:?}:{:?}", hours, minutes, seconds); + + sleep(16); + } +} + +fn boot_animation() { + let mut i = 1; + + while i < 10 { + let mut width = 0; + let mut height = 0; + + with_framebuffer(|fb| { + fb.clear(rgb(253, 129, 0)); + width = fb.width; + height = fb.height; + }); + + let text_width = ("XunilOS Loading".len() + ".".repeat(i).len()) * 4 * 2; + + with_serial_console(|serial_console| { + serial_console.clear(width / 2 - text_width / 2, height / 2) + }); + + println!( + "{}", + "XunilOS Loading".to_string() + &".".repeat(i).as_str() + ); + + i += 1; + + sleep(200); + } + + with_serial_console(|serial_console| { + serial_console.clear(0, 0); + }); + + with_framebuffer(|fb| { + fb.clear(rgb(253, 129, 0)); + }); +} + +fn unix_to_hms(timestamp: u64) -> (u64, u64, u64) { + let seconds = timestamp % 86400; + let h = seconds / 3600; + let m = (seconds % 3600) / 60; + let s = seconds % 60; + (h, m, s) } #[panic_handler] fn rust_panic(_info: &core::panic::PanicInfo) -> ! { with_framebuffer(|mut fb| { let (width, height) = (fb.width.clone(), fb.height.clone()); - rectangle_filled(&mut fb, 0, 0, width, height, rgb(180, 0, 0), true); + rectangle_filled(&mut fb, 0, 0, width, height, rgb(180, 0, 0)); with_serial_console(|console| { console.clear(5, 5); @@ -148,5 +224,5 @@ fn rust_panic(_info: &core::panic::PanicInfo) -> ! { }); }); - idle(); + infinite_idle(); } diff --git a/kernel/src/util.rs b/kernel/src/util.rs index 9f7786f..c5debe5 100644 --- a/kernel/src/util.rs +++ b/kernel/src/util.rs @@ -1,3 +1,4 @@ +use crate::{driver::timer::TIMER, println}; use spin::{Mutex, MutexGuard}; pub struct LinkedNode { @@ -34,3 +35,10 @@ impl Locked { self.inner.lock() } } + +pub fn test_performance(function: F) { + let start = TIMER.now(); + let ret = function(); + println!("took {} ms", (TIMER.now() - start).elapsed()); + ret +}