From e06d519d17b40d57f47d0489cb7291eddbc65a81 Mon Sep 17 00:00:00 2001 From: jthulhu <adrien.lc.mathieu@gmail.com> Date: Thu, 23 Feb 2023 10:35:20 +0100 Subject: [PATCH 1/6] WIP --- kernel/src/.dir-locals.el | 3 +- kernel/src/basic_vga.rs | 240 ++++++++++++++++++++++++++++---------- kernel/src/main.rs | 90 +++++++------- 3 files changed, 226 insertions(+), 107 deletions(-) diff --git a/kernel/src/.dir-locals.el b/kernel/src/.dir-locals.el index 48154b9..7a36f6f 100644 --- a/kernel/src/.dir-locals.el +++ b/kernel/src/.dir-locals.el @@ -1,2 +1,3 @@ ((nil . ((lsp-rust-all-targets . nil) - (lsp-rust-analyzer-cargo-target "x86_64-unknown-none")))) + ;; (lsp-rust-analyzer-cargo-target "x86_64-unknown-none") + ))) diff --git a/kernel/src/basic_vga.rs b/kernel/src/basic_vga.rs index 2d5f90c..d5dd1c0 100644 --- a/kernel/src/basic_vga.rs +++ b/kernel/src/basic_vga.rs @@ -1,6 +1,57 @@ use bootloader_api::info::{FrameBuffer, FrameBufferInfo, PixelFormat}; -use core::{fmt, marker::PhantomData}; +use core::{ + fmt::{self, Arguments}, + marker::PhantomData, +}; + +type Color = [u8; 3]; + +static mut VGA: Option<Vga<'static>> = None; + +/// This initializes the console with a framebuffer. This is required to be subsequently able +/// to print things to the console. +/// +/// # Safety +/// +/// No thread-concurrent access must be made to the console, including the initialization. +pub unsafe fn init(frame_buffer: &'static mut FrameBuffer) { + let mut vga = Vga::new(frame_buffer); + let (width, height) = vga.window_size(); + vga.fill_rect(0, 0, width, height, Vga::BLACK); + VGA = Some(vga); +} + + +/// # Safety +/// +/// No thread-concurrent access must be made to the VGA. A single exclusive borrow can live at +/// any time. +pub unsafe fn vga<'a>() -> &'a mut Vga<'static> { + VGA.as_mut().unwrap() +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => { + // SAFETY: /!\ None so far /!\ + unsafe { + $crate::basic_vga::print(format_args!($($arg)*)) + } + }; +} + +#[macro_export] +macro_rules! println { + ($($arg:tt)*) => { + $crate::print!("{}\n", format_args!($($arg)*)) + }; +} + +pub unsafe fn print(args: Arguments) { + use core::fmt::Write; + vga().console().write_fmt(args).unwrap(); +} pub struct Vga<'a> { buffer_start: *mut u8, @@ -17,6 +68,12 @@ impl<'a> From<&'a mut FrameBuffer> for Vga<'a> { } impl<'a> Vga<'a> { + const BLACK: Color = [0; 3]; + const WHITE: Color = [255; 3]; + const RED: Color = [255, 0, 0]; + const GREEN: Color = [0, 255, 0]; + const BLUE: Color = [0, 0, 255]; + pub fn new(frame_buffer: &'a mut FrameBuffer) -> Self { Self { buffer_start: frame_buffer.buffer_mut().as_mut_ptr(), @@ -26,56 +83,91 @@ impl<'a> Vga<'a> { } } + #[inline] pub fn get_pixel(&self, x: usize, y: usize) -> [u8; 3] { - let info = self.info; - let offset = (info.stride * y + x) * info.bytes_per_pixel; - let buffer = self.buffer_start.wrapping_add(offset); - match info.pixel_format { - PixelFormat::Rgb => unsafe { - [ - buffer.wrapping_add(0).read_volatile(), - buffer.wrapping_add(1).read_volatile(), - buffer.wrapping_add(2).read_volatile(), - ] - }, - PixelFormat::Bgr => unsafe { - [ - buffer.wrapping_add(2).read_volatile(), - buffer.wrapping_add(1).read_volatile(), - buffer.wrapping_add(0).read_volatile(), - ] - }, + assert!(self.is_valid(x, y)); + + unsafe { self.get_pixel_uncheck(x, y) } + } + + /// Returns the color of a pixel. + /// + /// # Safety + /// + /// The safety conditions of this function are the same of [`pixel_at_unchecked`]. + pub unsafe fn get_pixel_uncheck(&self, x: usize, y: usize) -> [u8; 3] { + let buffer = self.pixel_at_unchecked(x, y); + match self.info.pixel_format { + PixelFormat::Rgb => [ + buffer.offset(0).read_volatile(), + buffer.offset(1).read_volatile(), + buffer.offset(2).read_volatile(), + ], + PixelFormat::Bgr => [ + buffer.offset(2).read_volatile(), + buffer.offset(1).read_volatile(), + buffer.offset(0).read_volatile(), + ], PixelFormat::U8 => { - let c = unsafe { buffer.read_volatile() }; + let c = buffer.read_volatile(); [c, c, c] } PixelFormat::Unknown { red_position, green_position, blue_position, - } => unsafe { - [ - buffer.wrapping_add(red_position as _).read_volatile(), - buffer.wrapping_add(green_position as _).read_volatile(), - buffer.wrapping_add(blue_position as _).read_volatile(), - ] - }, + } => [ + buffer.offset(red_position as _).read_volatile(), + buffer.offset(green_position as _).read_volatile(), + buffer.offset(blue_position as _).read_volatile(), + ], _ => [0, 0, 0], } } - pub fn put_pixel(&mut self, x: usize, y: usize, rgb: [u8; 3]) { - let info = self.info; - let offset = (info.stride * y + x) * info.bytes_per_pixel; - Self::color(rgb)(self.buffer_start.wrapping_add(offset), info.pixel_format); + /// Returns an address pointing to where the pixel lie in the actual memory, without checking + /// if the coordinates are within bounds. + /// + /// # Safety + /// + /// The caller of this function must ensure the coordinates are within the bounds of the + /// framebuffer. + pub unsafe fn pixel_at_unchecked(&self, x: usize, y: usize) -> *mut u8 { + let offset = (self.info.stride * y + x) * self.info.bytes_per_pixel; + self.buffer_start.offset(offset as isize) + } + + pub fn pixel_at(&mut self, x: usize, y: usize) -> *mut u8 { + assert!(self.is_valid(x, y)); + // SAFETY: We have ensured (see above) that the coordinates are within bounds. + unsafe { self.pixel_at_unchecked(x, y) } + } + + /// Set the color of a pixel, without checking of the coordinates are within bounds. + /// + /// # Safety + /// + /// The safety conditions of this function are the same of [`pixel_at_unchecked`]. + pub unsafe fn put_pixel_unchecked(&mut self, x: usize, y: usize, rgb: [u8; 3]) { + Self::color(rgb)(self.pixel_at_unchecked(x, y), self.info.pixel_format); + } + + /// Check is the coordinates are within the bounds of the framebuffer. + #[inline] + pub fn is_valid(&self, x: usize, y: usize) -> bool { + x < self.info.width && y < self.info.height } pub fn fill_rect(&mut self, x: usize, y: usize, dx: usize, dy: usize, rgb: [u8; 3]) { let info = self.info; + assert!( + dx == 0 || dy == 0 || (self.is_valid(x, y) && self.is_valid(x + dx - 1, y + dy - 1)) + ); for x in x..(x + dx) { for y in y..(y + dy) { - let offset = (info.stride * y + x) * info.bytes_per_pixel; - Self::color(rgb)(self.buffer_start.wrapping_add(offset), info.pixel_format); + // SAFETY: We have checked that the corners are within bounds, so the whole + // rectangle is within bounds. + Self::color(rgb)(unsafe { self.pixel_at_unchecked(x, y) }, info.pixel_format); } } } @@ -124,11 +216,10 @@ impl<'a> Vga<'a> { } pub fn load_default_font() -> &'static [u8; 4096] { - static FONT: &'static [u8; 4096] = include_bytes!("../../assets/font"); + static FONT: &[u8; 4096] = include_bytes!("../../assets/font"); FONT } - /// color : 0xRRGGBB pub fn write_char( &mut self, font: &[u8; 4096], @@ -137,21 +228,38 @@ impl<'a> Vga<'a> { y: usize, fg: [u8; 3], bg: [u8; 3], + ) { + assert!(self.is_valid(x + 7, y + 15)); + unsafe { self.write_char_unchecked(font, char, x, y, fg, bg) } + } + + /// color : 0xRRGGBB + pub unsafe fn write_char_unchecked( + &mut self, + font: &[u8; 4096], + char: u8, + x: usize, + y: usize, + fg: [u8; 3], + bg: [u8; 3], ) { let mask = [128, 64, 32, 16, 8, 4, 2, 1]; let glyph = char as usize * 16; - for cy in 0..16 { for cx in 0..8 { - self.put_pixel( - x + cx, - y + cy - 12, - if font[glyph + cy] & mask[cx] != 0 { - fg - } else { - bg - }, - ) + // SAFETY: the rectangle that contains the glyph has the outermost corner within + // bounds (see assert above), so all the pixels of the glyph are within bounds. + unsafe { + self.put_pixel_unchecked( + x + cx, + y + cy - 12, + if font[glyph + cy] & mask[cx] != 0 { + fg + } else { + bg + }, + ) + } } } } @@ -304,13 +412,22 @@ impl VgaConsole<'_, '_> { for y in 0..self.vga.info.height - 16 { for x in 0..self.vga.info.width { - self.vga.put_pixel(x, y, self.vga.get_pixel(x, y + 16)); + // SAFETY: we know that the coordinates are within the bounds, since the bounds + // defined above are exactly the framebuffer's. + unsafe { + self.vga + .put_pixel_unchecked(x, y, self.vga.get_pixel_uncheck(x, y + 16)); + } } } for y in 0..16 { for x in 0..self.vga.info.width { - self.vga - .put_pixel(x, self.vga.info.height - 16 + y, [0, 0, 0]); + // SAFETY: `x` is definitively within bounds + // /!\ WE DONT KNOW WHETHER THE FRAME BUFFER IS BIG ENOUGH /!\ + unsafe { + self.vga + .put_pixel_unchecked(x, self.vga.info.height - 16 + y, [0, 0, 0]); + } } } } @@ -318,20 +435,23 @@ impl VgaConsole<'_, '_> { impl fmt::Write for VgaConsole<'_, '_> { fn write_str(&mut self, s: &str) -> fmt::Result { - for c in s.as_bytes() { - if *c == '\n' as u8 { + for &c in s.as_bytes() { + if c == '\n' as u8 { self.newline(); continue; } - - self.vga.write_char( - self.font(), - *c, - self.column() * 8, - (self.line() + 1) * 16, - self.fg(), - self.bg(), - ); + // SAFETY: according to someone, the internal state of the console is ok, so we don't + // write outside of the bounds. /!\ THIS IS FISHY /!\ + unsafe { + self.vga.write_char_unchecked( + self.font(), + c, + self.column() * 8, + (self.line() + 1) * 16, + self.fg(), + self.bg(), + ); + } self.set_column(self.column() + 1); if self.column() == self.width() { self.newline(); @@ -340,3 +460,5 @@ impl fmt::Write for VgaConsole<'_, '_> { Ok(()) } } + +unsafe impl Sync for Vga<'_> {} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 0eabf19..929db6a 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -13,61 +13,57 @@ use crate::primitives::halt; entry_point!(main); fn main(boot_info: &'static mut BootInfo) -> ! { - #[allow(non_snake_case)] - let mut VGA = Vga::new(boot_info.framebuffer.as_mut().unwrap()); - let font = Vga::load_default_font(); - - let black = [0, 0, 0]; - let white = [255, 255, 255]; - - let (width, height) = VGA.window_size(); - let console_width = VGA.console().width(); - let console_height = VGA.console().height(); - VGA.fill_rect(0, 0, width, height, black); - - let mut console = VGA.console(); - console.set_line(2); - let col = console.column(); - write!(console, "abc").unwrap(); - console.set_column(console.column() + 1); - write!(console, "def").unwrap(); - console.set_column(col); - console.set_line(console.line() + 1); - write!(console, "hij").unwrap(); - console.set_column(console.column() + 1); - write!(console, "klm").unwrap(); + // SAFETY: I have no idea what I am doing + unsafe { + init(boot_info.framebuffer.as_mut().unwrap()); + } + println!("Hello, world!"); - VGA.write_bytes(font, b"Hello\nWorld!", 100, 100, white, black, 4); + // let mut console = VGA.console(); + // console.set_line(2); + // let col = console.column(); + // write!(console, "abc").unwrap(); + // console.set_column(console.column() + 1); + // write!(console, "def").unwrap(); + // console.set_column(col); + // console.set_line(console.line() + 1); + // write!(console, "hij").unwrap(); + // console.set_column(console.column() + 1); + // write!(console, "klm").unwrap(); - VGA.console().set_column(0); - VGA.console().set_line(0); + // VGA.write_bytes(font, b"Hello\nWorld!", 100, 100, white, black, 4); - write!( - VGA.console(), - "{}x{}|{}x{}", - width, - height, - console_width, - console_height - ) - .unwrap(); + // VGA.console().set_column(0); + // VGA.console().set_line(0); - let mut console = VGA.console(); - console.set_line(0); - for i in 0..55 { - console.set_column(100); - write!(console, "{}\n", i).unwrap(); - if i == 10 { - console.newline(); - } - } + // write!( + // VGA.console(), + // "{}x{}|{}x{}", + // width, + // height, + // console_width, + // console_height + // ) + // .unwrap(); - VGA.fill_rect(1200, 640, 80, 80, [255, 0, 0]); + // let mut console = VGA.console(); + // console.set_line(0); + // for i in 0..55 { + // console.set_column(100); + // write!(console, "{}\n", i).unwrap(); + // if i == 10 { + // console.newline(); + // } + // } - halt() + // VGA.fill_rect(1200, 640, 80, 80, [255, 0, 0]); + panic!("AHA"); + // halt() } #[panic_handler] -fn panic(_panic_info: &panic::PanicInfo) -> ! { +fn panic(panic_info: &panic::PanicInfo) -> ! { + println!("=== KERNEL PANIC ==="); + println!("{}", panic_info); halt() } -- GitLab From fe4750b948bd78740e809d930691ee0b72ef37dd Mon Sep 17 00:00:00 2001 From: Simon Dima <simon.dima@ens.psl.eu> Date: Thu, 23 Feb 2023 13:06:33 +0100 Subject: [PATCH 2/6] Fixed a few clippy lints --- .gitignore | 1 + kernel/src/basic_vga.rs | 27 +++++++++++---------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf..e9868bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.swp diff --git a/kernel/src/basic_vga.rs b/kernel/src/basic_vga.rs index d5dd1c0..a510294 100644 --- a/kernel/src/basic_vga.rs +++ b/kernel/src/basic_vga.rs @@ -22,7 +22,6 @@ pub unsafe fn init(frame_buffer: &'static mut FrameBuffer) { VGA = Some(vga); } - /// # Safety /// /// No thread-concurrent access must be made to the VGA. A single exclusive borrow can live at @@ -73,7 +72,7 @@ impl<'a> Vga<'a> { const RED: Color = [255, 0, 0]; const GREEN: Color = [0, 255, 0]; const BLUE: Color = [0, 0, 255]; - + pub fn new(frame_buffer: &'a mut FrameBuffer) -> Self { Self { buffer_start: frame_buffer.buffer_mut().as_mut_ptr(), @@ -134,7 +133,7 @@ impl<'a> Vga<'a> { /// framebuffer. pub unsafe fn pixel_at_unchecked(&self, x: usize, y: usize) -> *mut u8 { let offset = (self.info.stride * y + x) * self.info.bytes_per_pixel; - self.buffer_start.offset(offset as isize) + self.buffer_start.add(offset) } pub fn pixel_at(&mut self, x: usize, y: usize) -> *mut u8 { @@ -178,14 +177,14 @@ impl<'a> Vga<'a> { PixelFormat::Rgb => { for i in 0..3 { unsafe { - buffer.offset(i as isize).write_volatile(rgb[i]); + buffer.add(i).write_volatile(rgb[i]); } } } PixelFormat::Bgr => { for i in 0..3 { unsafe { - buffer.offset(i as isize).write_volatile(rgb[i]); + buffer.add(i).write_volatile(rgb[i]); } } } @@ -246,18 +245,14 @@ impl<'a> Vga<'a> { let mask = [128, 64, 32, 16, 8, 4, 2, 1]; let glyph = char as usize * 16; for cy in 0..16 { - for cx in 0..8 { + for (cx, msk) in mask.iter().enumerate() { // SAFETY: the rectangle that contains the glyph has the outermost corner within // bounds (see assert above), so all the pixels of the glyph are within bounds. unsafe { self.put_pixel_unchecked( x + cx, y + cy - 12, - if font[glyph + cy] & mask[cx] != 0 { - fg - } else { - bg - }, + if font[glyph + cy] & msk != 0 { fg } else { bg }, ) } } @@ -278,7 +273,7 @@ impl<'a> Vga<'a> { ) { let mut char_in_line = 0; for c in text { - if *c == '\n' as u8 { + if *c == b'\n' { x -= 8 * char_in_line; y += 16; char_in_line = 0; @@ -298,14 +293,14 @@ impl<'a> Vga<'a> { /// zeroes tab and writes the digit of the number in tab, returning a slice of it pub fn print_u32(mut n: u32, tab: &mut [u8; 20]) -> &[u8] { - *tab = [0 as u8; 20]; + *tab = [0_u8; 20]; if n == 0 { - tab[0] = '0' as u8; + tab[0] = b'0'; return &tab[0..1]; } let mut i = 0; while n != 0 { - tab[19 - i] = '0' as u8 + (n % 10) as u8; + tab[19 - i] = b'0' + (n % 10) as u8; n /= 10; i += 1; } @@ -436,7 +431,7 @@ impl VgaConsole<'_, '_> { impl fmt::Write for VgaConsole<'_, '_> { fn write_str(&mut self, s: &str) -> fmt::Result { for &c in s.as_bytes() { - if c == '\n' as u8 { + if c == b'\n' { self.newline(); continue; } -- GitLab From ad48b647ec246f98b0ff8036e12432248d3effcc Mon Sep 17 00:00:00 2001 From: jthulhu <adrien.lc.mathieu@gmail.com> Date: Thu, 23 Feb 2023 16:53:39 +0100 Subject: [PATCH 3/6] [kernel/{main,vga}] Remove UB!!!! (and also, it works). --- kernel/src/main.rs | 6 +- kernel/src/{basic_vga.rs => vga.rs} | 139 +++++++++++++++++----------- 2 files changed, 88 insertions(+), 57 deletions(-) rename kernel/src/{basic_vga.rs => vga.rs} (75%) diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 929db6a..4520031 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -3,12 +3,12 @@ use bootloader_api::{entry_point, BootInfo}; -use core::{fmt::Write, panic}; +use core::panic; -mod basic_vga; +mod vga; mod primitives; -use crate::basic_vga::*; +use crate::vga::*; use crate::primitives::halt; entry_point!(main); diff --git a/kernel/src/basic_vga.rs b/kernel/src/vga.rs similarity index 75% rename from kernel/src/basic_vga.rs rename to kernel/src/vga.rs index a510294..81ceeb9 100644 --- a/kernel/src/basic_vga.rs +++ b/kernel/src/vga.rs @@ -2,41 +2,44 @@ use bootloader_api::info::{FrameBuffer, FrameBufferInfo, PixelFormat}; use core::{ fmt::{self, Arguments}, - marker::PhantomData, + marker::PhantomData, sync::atomic::{AtomicUsize, AtomicU8, Ordering}, mem::MaybeUninit, }; type Color = [u8; 3]; -static mut VGA: Option<Vga<'static>> = None; +// This is only mutable for initialization, otherwise it's "read-only" +static mut VGA: MaybeUninit<Vga<'static>> = MaybeUninit::uninit(); -/// This initializes the console with a framebuffer. This is required to be subsequently able +/// Initialize the console with a framebuffer. This is required to be subsequently able /// to print things to the console. /// /// # Safety /// -/// No thread-concurrent access must be made to the console, including the initialization. +/// This function must be called exactly once, and it must return before the VGA structure is +/// accessed. pub unsafe fn init(frame_buffer: &'static mut FrameBuffer) { let mut vga = Vga::new(frame_buffer); let (width, height) = vga.window_size(); vga.fill_rect(0, 0, width, height, Vga::BLACK); - VGA = Some(vga); + VGA.write(vga); } -/// # Safety -/// -/// No thread-concurrent access must be made to the VGA. A single exclusive borrow can live at -/// any time. -pub unsafe fn vga<'a>() -> &'a mut Vga<'static> { - VGA.as_mut().unwrap() +/// Get a borrow of the VGA. +pub fn vga<'a>() -> &'a Vga<'static> { + // SAFETY: The VGA *must* have been initialized, it's literally the *first* thing that this + // whole kernel does. If it's not, use `git blame` to find the culprit and throw them out + // of a window. + // + // I've warned you... + unsafe { + VGA.assume_init_ref() + } } #[macro_export] macro_rules! print { ($($arg:tt)*) => { - // SAFETY: /!\ None so far /!\ - unsafe { - $crate::basic_vga::print(format_args!($($arg)*)) - } + $crate::vga::print(format_args!($($arg)*)) }; } @@ -47,7 +50,7 @@ macro_rules! println { }; } -pub unsafe fn print(args: Arguments) { +pub fn print(args: Arguments) { use core::fmt::Write; vga().console().write_fmt(args).unwrap(); } @@ -147,7 +150,7 @@ impl<'a> Vga<'a> { /// # Safety /// /// The safety conditions of this function are the same of [`pixel_at_unchecked`]. - pub unsafe fn put_pixel_unchecked(&mut self, x: usize, y: usize, rgb: [u8; 3]) { + pub unsafe fn put_pixel_unchecked(&self, x: usize, y: usize, rgb: [u8; 3]) { Self::color(rgb)(self.pixel_at_unchecked(x, y), self.info.pixel_format); } @@ -157,7 +160,7 @@ impl<'a> Vga<'a> { x < self.info.width && y < self.info.height } - pub fn fill_rect(&mut self, x: usize, y: usize, dx: usize, dy: usize, rgb: [u8; 3]) { + pub fn fill_rect(&self, x: usize, y: usize, dx: usize, dy: usize, rgb: [u8; 3]) { let info = self.info; assert!( dx == 0 || dy == 0 || (self.is_valid(x, y) && self.is_valid(x + dx - 1, y + dy - 1)) @@ -220,7 +223,7 @@ impl<'a> Vga<'a> { } pub fn write_char( - &mut self, + &self, font: &[u8; 4096], char: u8, x: usize, @@ -234,7 +237,7 @@ impl<'a> Vga<'a> { /// color : 0xRRGGBB pub unsafe fn write_char_unchecked( - &mut self, + &self, font: &[u8; 4096], char: u8, x: usize, @@ -262,7 +265,7 @@ impl<'a> Vga<'a> { // line length in characters, 0 means no limit // handle newline pub fn write_bytes( - &mut self, + &self, font: &[u8; 4096], text: &[u8], mut x: usize, @@ -307,28 +310,28 @@ impl<'a> Vga<'a> { &tab[20 - i..] } - pub fn console(&mut self) -> VgaConsole<'_, 'a> { + pub fn console(&self) -> VgaConsole<'_, 'a> { VgaConsole { vga: self } } } #[repr(C)] struct VgaConsoleState { - column: usize, - line: usize, - fg: [u8; 3], - bg: [u8; 3], - font: Option<&'static [u8; 4096]>, + column: AtomicUsize, + line: AtomicUsize, + fg: [AtomicU8; 3], + bg: [AtomicU8; 3], + font: &'static [u8; 4096], } impl VgaConsoleState { pub fn new() -> Self { Self { - column: 0, - line: 0, - fg: [255, 255, 255], - bg: [0, 0, 0], - font: None, + column: AtomicUsize::new(0), + line: AtomicUsize::new(0), + fg: [AtomicU8::new(255), AtomicU8::new(255), AtomicU8::new(255)], + bg: [AtomicU8::new(0), AtomicU8::new(0), AtomicU8::new(0)], + font: Vga::load_default_font(), } } } @@ -340,11 +343,11 @@ impl Default for VgaConsoleState { } pub struct VgaConsole<'a, 'b> { - vga: &'a mut Vga<'b>, + vga: &'a Vga<'b>, } impl VgaConsole<'_, '_> { - pub fn width(&mut self) -> usize { + pub fn width(&self) -> usize { self.vga.info.width / 8 } @@ -353,51 +356,79 @@ impl VgaConsole<'_, '_> { } pub fn column(&self) -> usize { - self.vga.console_state.column + self.vga.console_state.column.load(Ordering::Relaxed) } pub fn line(&self) -> usize { - self.vga.console_state.line + self.vga.console_state.line.load(Ordering::Relaxed) } - pub fn set_column(&mut self, column: usize) { - debug_assert!(column < self.width()); - self.vga.console_state.column = column; + /// # Safety + /// + /// `column` must be less than `self.width()`. For a safe counterpart, see [`set_column`] + pub unsafe fn set_column_unchecked(&self, column: usize) { + self.vga.console_state.column.store(column, Ordering::Relaxed); } - pub fn set_line(&mut self, line: usize) { - debug_assert!(line < self.height() - 1); - self.vga.console_state.line = line; + /// Change the column of the cursor. The column must be within bounds. + /// + /// # Panic + /// + /// This will panic if the column is not within bounds. + pub fn set_column(&self, column: usize) { + assert!(column < self.width()); + // SAFETY: We have ensured above that `column < self.width`. + unsafe { + self.set_column_unchecked(column) + } + } + + /// # Safety + /// + /// `line` must be less than `self.height - 1` + pub unsafe fn set_line_unchecked(&self, line: usize) { + self.vga.console_state.line.store(line, Ordering::Relaxed); + } + + pub fn set_line(&self, line: usize) { + assert!(line < self.height() - 1); + // SAFETY: We have ensured above that `line < self.height - 1`. + unsafe { + self.set_line_unchecked(line); + } } pub fn fg(&self) -> [u8; 3] { - self.vga.console_state.fg + [self.vga.console_state.fg[0].load(Ordering::Relaxed), + self.vga.console_state.fg[1].load(Ordering::Relaxed), + self.vga.console_state.fg[2].load(Ordering::Relaxed)] } pub fn bg(&self) -> [u8; 3] { - self.vga.console_state.bg + [self.vga.console_state.bg[0].load(Ordering::Relaxed), + self.vga.console_state.bg[1].load(Ordering::Relaxed), + self.vga.console_state.bg[2].load(Ordering::Relaxed)] } - pub fn set_fg(&mut self, color: [u8; 3]) { - self.vga.console_state.fg = color; + pub fn set_fg(&self, color: [u8; 3]) { + self.vga.console_state.fg[0].store(color[0], Ordering::Relaxed); + self.vga.console_state.fg[1].store(color[1], Ordering::Relaxed); + self.vga.console_state.fg[2].store(color[2], Ordering::Relaxed); } - pub fn set_bg(&mut self, color: [u8; 3]) { - self.vga.console_state.bg = color; + pub fn set_bg(&self, color: [u8; 3]) { + self.vga.console_state.bg[0].store(color[0], Ordering::Relaxed); + self.vga.console_state.bg[1].store(color[1], Ordering::Relaxed); + self.vga.console_state.bg[2].store(color[2], Ordering::Relaxed); } pub fn font(&self) -> &'static [u8; 4096] { self.vga .console_state .font - .unwrap_or(Vga::load_default_font()) - } - - pub fn set_font(&mut self, font: &'static [u8; 4096]) { - self.vga.console_state.font = Some(font); } - pub fn newline(&mut self) { + pub fn newline(&self) { self.set_column(0); if self.line() < self.height() - 2 { -- GitLab From 7a5b7834fec205895b6e9628873075d3a44c132d Mon Sep 17 00:00:00 2001 From: jthulhu <adrien.lc.mathieu@gmail.com> Date: Thu, 23 Feb 2023 17:21:40 +0100 Subject: [PATCH 4/6] [kernel/vga] Removed useless mutability. --- kernel/src/vga.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/vga.rs b/kernel/src/vga.rs index 81ceeb9..e7cc456 100644 --- a/kernel/src/vga.rs +++ b/kernel/src/vga.rs @@ -2,7 +2,7 @@ use bootloader_api::info::{FrameBuffer, FrameBufferInfo, PixelFormat}; use core::{ fmt::{self, Arguments}, - marker::PhantomData, sync::atomic::{AtomicUsize, AtomicU8, Ordering}, mem::MaybeUninit, + marker::PhantomData, mem::MaybeUninit, }; type Color = [u8; 3]; @@ -18,7 +18,7 @@ static mut VGA: MaybeUninit<Vga<'static>> = MaybeUninit::uninit(); /// This function must be called exactly once, and it must return before the VGA structure is /// accessed. pub unsafe fn init(frame_buffer: &'static mut FrameBuffer) { - let mut vga = Vga::new(frame_buffer); + let vga = Vga::new(frame_buffer); let (width, height) = vga.window_size(); vga.fill_rect(0, 0, width, height, Vga::BLACK); VGA.write(vga); -- GitLab From db8abea830bcc03fba1b936e86e8115ac9f47a54 Mon Sep 17 00:00:00 2001 From: Simon Dima <simon.dima@ens.psl.eu> Date: Thu, 23 Feb 2023 17:35:02 +0100 Subject: [PATCH 5/6] Added Color struct --- kernel/src/vga.rs | 224 +++++++++++++++++++++++++--------------------- 1 file changed, 121 insertions(+), 103 deletions(-) diff --git a/kernel/src/vga.rs b/kernel/src/vga.rs index 81ceeb9..47097b7 100644 --- a/kernel/src/vga.rs +++ b/kernel/src/vga.rs @@ -2,10 +2,17 @@ use bootloader_api::info::{FrameBuffer, FrameBufferInfo, PixelFormat}; use core::{ fmt::{self, Arguments}, - marker::PhantomData, sync::atomic::{AtomicUsize, AtomicU8, Ordering}, mem::MaybeUninit, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicU8, AtomicUsize, Ordering}, }; -type Color = [u8; 3]; +#[derive(Clone, Copy)] +pub struct Color { + red: u8, + green: u8, + blue: u8, +} // This is only mutable for initialization, otherwise it's "read-only" static mut VGA: MaybeUninit<Vga<'static>> = MaybeUninit::uninit(); @@ -18,7 +25,7 @@ static mut VGA: MaybeUninit<Vga<'static>> = MaybeUninit::uninit(); /// This function must be called exactly once, and it must return before the VGA structure is /// accessed. pub unsafe fn init(frame_buffer: &'static mut FrameBuffer) { - let mut vga = Vga::new(frame_buffer); + let vga = Vga::new(frame_buffer); let (width, height) = vga.window_size(); vga.fill_rect(0, 0, width, height, Vga::BLACK); VGA.write(vga); @@ -31,9 +38,7 @@ pub fn vga<'a>() -> &'a Vga<'static> { // of a window. // // I've warned you... - unsafe { - VGA.assume_init_ref() - } + unsafe { VGA.assume_init_ref() } } #[macro_export] @@ -70,11 +75,31 @@ impl<'a> From<&'a mut FrameBuffer> for Vga<'a> { } impl<'a> Vga<'a> { - const BLACK: Color = [0; 3]; - const WHITE: Color = [255; 3]; - const RED: Color = [255, 0, 0]; - const GREEN: Color = [0, 255, 0]; - const BLUE: Color = [0, 0, 255]; + const BLACK: Color = Color { + red: 0, + green: 0, + blue: 0, + }; + const WHITE: Color = Color { + red: 255, + green: 255, + blue: 255, + }; + const RED: Color = Color { + red: 255, + green: 0, + blue: 0, + }; + const GREEN: Color = Color { + red: 0, + green: 255, + blue: 0, + }; + const BLUE: Color = Color { + red: 0, + green: 0, + blue: 255, + }; pub fn new(frame_buffer: &'a mut FrameBuffer) -> Self { Self { @@ -86,10 +111,10 @@ impl<'a> Vga<'a> { } #[inline] - pub fn get_pixel(&self, x: usize, y: usize) -> [u8; 3] { + pub fn get_pixel(&self, x: usize, y: usize) -> Color { assert!(self.is_valid(x, y)); - unsafe { self.get_pixel_uncheck(x, y) } + unsafe { self.get_pixel_unchecked(x, y) } } /// Returns the color of a pixel. @@ -97,33 +122,37 @@ impl<'a> Vga<'a> { /// # Safety /// /// The safety conditions of this function are the same of [`pixel_at_unchecked`]. - pub unsafe fn get_pixel_uncheck(&self, x: usize, y: usize) -> [u8; 3] { + pub unsafe fn get_pixel_unchecked(&self, x: usize, y: usize) -> Color { let buffer = self.pixel_at_unchecked(x, y); match self.info.pixel_format { - PixelFormat::Rgb => [ - buffer.offset(0).read_volatile(), - buffer.offset(1).read_volatile(), - buffer.offset(2).read_volatile(), - ], - PixelFormat::Bgr => [ - buffer.offset(2).read_volatile(), - buffer.offset(1).read_volatile(), - buffer.offset(0).read_volatile(), - ], + PixelFormat::Rgb => Color { + red: buffer.offset(0).read_volatile(), + green: buffer.offset(1).read_volatile(), + blue: buffer.offset(2).read_volatile(), + }, + PixelFormat::Bgr => Color { + red: buffer.offset(2).read_volatile(), + green: buffer.offset(1).read_volatile(), + blue: buffer.offset(0).read_volatile(), + }, PixelFormat::U8 => { let c = buffer.read_volatile(); - [c, c, c] + Color { + red: c, + green: c, + blue: c, + } } PixelFormat::Unknown { red_position, green_position, blue_position, - } => [ - buffer.offset(red_position as _).read_volatile(), - buffer.offset(green_position as _).read_volatile(), - buffer.offset(blue_position as _).read_volatile(), - ], - _ => [0, 0, 0], + } => Color { + red: buffer.add(red_position as _).read_volatile(), + green: buffer.add(green_position as _).read_volatile(), + blue: buffer.add(blue_position as _).read_volatile(), + }, + _ => panic!("unknown pixel format"), } } @@ -150,8 +179,8 @@ impl<'a> Vga<'a> { /// # Safety /// /// The safety conditions of this function are the same of [`pixel_at_unchecked`]. - pub unsafe fn put_pixel_unchecked(&self, x: usize, y: usize, rgb: [u8; 3]) { - Self::color(rgb)(self.pixel_at_unchecked(x, y), self.info.pixel_format); + pub unsafe fn put_pixel_unchecked(&self, x: usize, y: usize, color: Color) { + self.color_pixel_unchecked(self.pixel_at_unchecked(x, y), color); } /// Check is the coordinates are within the bounds of the framebuffer. @@ -160,7 +189,7 @@ impl<'a> Vga<'a> { x < self.info.width && y < self.info.height } - pub fn fill_rect(&self, x: usize, y: usize, dx: usize, dy: usize, rgb: [u8; 3]) { + pub fn fill_rect(&self, x: usize, y: usize, dx: usize, dy: usize, color: Color) { let info = self.info; assert!( dx == 0 || dy == 0 || (self.is_valid(x, y) && self.is_valid(x + dx - 1, y + dy - 1)) @@ -169,46 +198,40 @@ impl<'a> Vga<'a> { for y in y..(y + dy) { // SAFETY: We have checked that the corners are within bounds, so the whole // rectangle is within bounds. - Self::color(rgb)(unsafe { self.pixel_at_unchecked(x, y) }, info.pixel_format); + unsafe { + self.color_pixel_unchecked(self.pixel_at_unchecked(x, y), color); + } } } } #[inline(always)] - fn color(rgb: [u8; 3]) -> impl Fn(*mut u8, PixelFormat) { - move |buffer, format| match format { + unsafe fn color_pixel_unchecked(&self, pix: *mut u8, col: Color) { + match self.info.pixel_format { PixelFormat::Rgb => { - for i in 0..3 { - unsafe { - buffer.add(i).write_volatile(rgb[i]); - } - } + pix.add(0).write_volatile(col.red); + pix.add(1).write_volatile(col.green); + pix.add(2).write_volatile(col.blue); } PixelFormat::Bgr => { - for i in 0..3 { - unsafe { - buffer.add(i).write_volatile(rgb[i]); - } - } + pix.add(0).write_volatile(col.blue); + pix.add(1).write_volatile(col.green); + pix.add(2).write_volatile(col.red); } PixelFormat::U8 => { - let c = 30 * rgb[0] as u16 + 59 * rgb[1] as u16 + 11 * rgb[2] as u16; - unsafe { - buffer.write_volatile((c / 100) as u8); - } + let c = 30 * col.red as u16 + 59 * col.green as u16 + 11 * col.blue as u16; + pix.write_volatile((c / 100) as u8); } PixelFormat::Unknown { red_position, green_position, blue_position, - } => unsafe { - buffer.offset(red_position as isize).write_volatile(rgb[0]); - buffer - .offset(green_position as isize) - .write_volatile(rgb[1]); - buffer.offset(blue_position as isize).write_volatile(rgb[2]); - }, - _ => {} + } => { + pix.add(red_position as usize).write_volatile(col.red); + pix.add(green_position as usize).write_volatile(col.green); + pix.add(blue_position as usize).write_volatile(col.blue); + } + _ => panic!("unknown pixel format"), } } @@ -222,17 +245,9 @@ impl<'a> Vga<'a> { FONT } - pub fn write_char( - &self, - font: &[u8; 4096], - char: u8, - x: usize, - y: usize, - fg: [u8; 3], - bg: [u8; 3], - ) { + pub fn write_char(&self, font: &[u8; 4096], chr: u8, x: usize, y: usize, fg: Color, bg: Color) { assert!(self.is_valid(x + 7, y + 15)); - unsafe { self.write_char_unchecked(font, char, x, y, fg, bg) } + unsafe { self.write_char_unchecked(font, chr, x, y, fg, bg) } } /// color : 0xRRGGBB @@ -242,8 +257,8 @@ impl<'a> Vga<'a> { char: u8, x: usize, y: usize, - fg: [u8; 3], - bg: [u8; 3], + fg: Color, + bg: Color, ) { let mask = [128, 64, 32, 16, 8, 4, 2, 1]; let glyph = char as usize * 16; @@ -270,8 +285,8 @@ impl<'a> Vga<'a> { text: &[u8], mut x: usize, mut y: usize, - fg: [u8; 3], - bg: [u8; 3], + fg: Color, + bg: Color, line_length: usize, ) { let mut char_in_line = 0; @@ -367,7 +382,10 @@ impl VgaConsole<'_, '_> { /// /// `column` must be less than `self.width()`. For a safe counterpart, see [`set_column`] pub unsafe fn set_column_unchecked(&self, column: usize) { - self.vga.console_state.column.store(column, Ordering::Relaxed); + self.vga + .console_state + .column + .store(column, Ordering::Relaxed); } /// Change the column of the cursor. The column must be within bounds. @@ -378,54 +396,54 @@ impl VgaConsole<'_, '_> { pub fn set_column(&self, column: usize) { assert!(column < self.width()); // SAFETY: We have ensured above that `column < self.width`. - unsafe { - self.set_column_unchecked(column) - } + unsafe { self.set_column_unchecked(column) } } /// # Safety /// /// `line` must be less than `self.height - 1` pub unsafe fn set_line_unchecked(&self, line: usize) { - self.vga.console_state.line.store(line, Ordering::Relaxed); + self.vga.console_state.line.store(line, Ordering::Relaxed); } - + pub fn set_line(&self, line: usize) { assert!(line < self.height() - 1); - // SAFETY: We have ensured above that `line < self.height - 1`. - unsafe { + // SAFETY: We have ensured above that `line < self.height - 1`. + unsafe { self.set_line_unchecked(line); - } + } } - pub fn fg(&self) -> [u8; 3] { - [self.vga.console_state.fg[0].load(Ordering::Relaxed), - self.vga.console_state.fg[1].load(Ordering::Relaxed), - self.vga.console_state.fg[2].load(Ordering::Relaxed)] + pub fn fg(&self) -> Color { + Color { + red: self.vga.console_state.fg[0].load(Ordering::Relaxed), + green: self.vga.console_state.fg[1].load(Ordering::Relaxed), + blue: self.vga.console_state.fg[2].load(Ordering::Relaxed), + } } - pub fn bg(&self) -> [u8; 3] { - [self.vga.console_state.bg[0].load(Ordering::Relaxed), - self.vga.console_state.bg[1].load(Ordering::Relaxed), - self.vga.console_state.bg[2].load(Ordering::Relaxed)] + pub fn bg(&self) -> Color { + Color { + red: self.vga.console_state.bg[0].load(Ordering::Relaxed), + green: self.vga.console_state.bg[1].load(Ordering::Relaxed), + blue: self.vga.console_state.bg[2].load(Ordering::Relaxed), + } } - pub fn set_fg(&self, color: [u8; 3]) { - self.vga.console_state.fg[0].store(color[0], Ordering::Relaxed); - self.vga.console_state.fg[1].store(color[1], Ordering::Relaxed); - self.vga.console_state.fg[2].store(color[2], Ordering::Relaxed); + pub fn set_fg(&self, color: Color) { + self.vga.console_state.fg[0].store(color.red, Ordering::Relaxed); + self.vga.console_state.fg[1].store(color.green, Ordering::Relaxed); + self.vga.console_state.fg[2].store(color.blue, Ordering::Relaxed); } - pub fn set_bg(&self, color: [u8; 3]) { - self.vga.console_state.bg[0].store(color[0], Ordering::Relaxed); - self.vga.console_state.bg[1].store(color[1], Ordering::Relaxed); - self.vga.console_state.bg[2].store(color[2], Ordering::Relaxed); + pub fn set_bg(&self, color: Color) { + self.vga.console_state.bg[0].store(color.red, Ordering::Relaxed); + self.vga.console_state.bg[1].store(color.green, Ordering::Relaxed); + self.vga.console_state.bg[2].store(color.blue, Ordering::Relaxed); } pub fn font(&self) -> &'static [u8; 4096] { - self.vga - .console_state - .font + self.vga.console_state.font } pub fn newline(&self) { @@ -442,7 +460,7 @@ impl VgaConsole<'_, '_> { // defined above are exactly the framebuffer's. unsafe { self.vga - .put_pixel_unchecked(x, y, self.vga.get_pixel_uncheck(x, y + 16)); + .put_pixel_unchecked(x, y, self.vga.get_pixel_unchecked(x, y + 16)); } } } @@ -452,7 +470,7 @@ impl VgaConsole<'_, '_> { // /!\ WE DONT KNOW WHETHER THE FRAME BUFFER IS BIG ENOUGH /!\ unsafe { self.vga - .put_pixel_unchecked(x, self.vga.info.height - 16 + y, [0, 0, 0]); + .put_pixel_unchecked(x, self.vga.info.height - 16 + y, Vga::BLACK); } } } -- GitLab From b3e0ea25d34f1f63b7b4a61ffd0b80253c34bf91 Mon Sep 17 00:00:00 2001 From: Simon Dima <simon.dima@gmail.com> Date: Wed, 1 Mar 2023 14:56:14 +0100 Subject: [PATCH 6/6] Print QEMU command being run --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 49d9a27..cfe77dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ fn main() { } // log interrupt and exception // cmd.arg("-d").arg("int"); + println!("running {cmd:?}"); let mut child = cmd.spawn().unwrap(); child.wait().unwrap(); } -- GitLab