diff --git a/.gitignore b/.gitignore index 11235cc..dfbc8d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -/kernel/target +target +/kernel/Cargo.lock /build /binutils /gcc diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index f84cf70..24dedf2 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -3,4 +3,8 @@ name = "kernel" version = "0.1.0" edition = "2021" +[workspace] +members = ["std"] + [dependencies] +std = { path = "std" } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 2f11094..f1e4fe9 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,22 +1,16 @@ #![no_std] #![no_main] +use std::println; + #[no_mangle] pub extern "C" fn kernel_main() -> ! { - let vga_buffer = 0xb8000 as *mut u8; - - let string: &[u8] = b":3"; - for (i, &byte) in string.iter().enumerate() { - unsafe { - *vga_buffer.offset(i as isize * 2) = byte; - *vga_buffer.offset(i as isize * 2 + 1) = 0xf; - } - } - - + let a = 4; + println!("Hiii :3"); + println!("{}", a); loop {} } - + #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} diff --git a/kernel/Cargo.lock b/kernel/std/Cargo.lock similarity index 89% rename from kernel/Cargo.lock rename to kernel/std/Cargo.lock index 0dd1539..24a4fde 100644 --- a/kernel/Cargo.lock +++ b/kernel/std/Cargo.lock @@ -3,5 +3,5 @@ version = 3 [[package]] -name = "kernel" +name = "std" version = "0.1.0" diff --git a/kernel/std/Cargo.toml b/kernel/std/Cargo.toml new file mode 100644 index 0000000..ffbadca --- /dev/null +++ b/kernel/std/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "std" +version = "0.1.0" +edition = "2021" + +[dependencies] +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +spin = "0.9.8" diff --git a/kernel/std/src/lib.rs b/kernel/std/src/lib.rs new file mode 100644 index 0000000..56afba1 --- /dev/null +++ b/kernel/std/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +pub mod vga_buffer; diff --git a/kernel/std/src/vga_buffer.rs b/kernel/std/src/vga_buffer.rs new file mode 100644 index 0000000..60bfe80 --- /dev/null +++ b/kernel/std/src/vga_buffer.rs @@ -0,0 +1,132 @@ +use core::fmt::{self, Write}; + +use lazy_static::lazy_static; +use spin::Mutex; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum Color { + Black = 0, + Blue = 1, + Green = 2, + Cyan = 3, + Red = 4, + Magenta = 5, + Brown = 6, + LightGray = 7, + DarkGray = 8, + LightBlue = 9, + LightGreen = 10, + LightCyan = 11, + LightRed = 12, + Pink = 13, + Yellow = 14, + White = 15, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct ColorCode(u8); + +impl ColorCode { + pub fn new(foreground: Color, background: Color) -> ColorCode { + ColorCode((background as u8) << 4 | (foreground as u8)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +struct ScreenChar { + ascii_character: u8, + color_code: ColorCode, +} + +const BUFFER_HEIGHT: usize = 25; +const BUFFER_WIDTH: usize = 80; + +#[repr(transparent)] +struct Buffer { + chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT], +} + +pub struct Writer { + col: usize, + row: usize, + buffer: &'static mut Buffer, +} + +impl Writer { + pub fn write_byte(&mut self, byte: u8) { + self.write_byte_color(byte, ColorCode::new(Color::White, Color::Black)); + } + + pub fn write_byte_color(&mut self, byte: u8, color: ColorCode) { + match byte { + b'\n' => self.new_line(), + byte => { + if self.col >= BUFFER_WIDTH { + self.new_line(); + } + + let row = self.row; + let col = self.col; + + let color_code = color; + self.buffer.chars[row][col] = ScreenChar { + ascii_character: byte, + color_code, + }; + self.col += 1; + } + } + } + + pub fn write_string(&mut self, s: &str) { + self.write_string_color(s, ColorCode::new(Color::White, Color::Black)); + } + + pub fn write_string_color(&mut self, s: &str, color: ColorCode) { + for char in s.bytes() { + match char { + 0x20..=0x7e | b'\n' => self.write_byte_color(char, color), + _ => self.write_byte(0xfe), + } + } + } + + fn new_line(&mut self) { + self.row += 1; + self.col = 0; + } +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_string(s); + Ok(()) + } +} + +lazy_static! { + pub static ref WRITER: Mutex = Mutex::new(Writer { + col: 0, + row: 0, + buffer: unsafe { &mut *(0xB8000 as *mut Buffer) }, + }); +} + +#[doc(hidden)] +pub fn _print(args: fmt::Arguments) { + WRITER.lock().write_fmt(args).unwrap() +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); +} diff --git a/log/2024-03-26.md b/log/2024-03-26.md index 09c5bd6..a57607a 100644 --- a/log/2024-03-26.md +++ b/log/2024-03-26.md @@ -350,6 +350,15 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { # Building the kernel To build the kernel I just run `cargo build` in the `kernel` directory. This will produce a binary called `kernel/target/i686-bare-metal/debug/kernel.elf`. I now have a multiboot enabled kernel :) +# Grub +Now to turn the kernel into a ISO file with a bootloader, I used grub. To configure grub I needed to create a directory for it. I made `build/isodir/boot/grub/grub.cfg` containing this: +``` +menuentry "geos" { + multiboot /boot/geos.bin +} +``` +Then I copied `kernel/target/i686-bare-metal/debug/kernel.elf` to `isodir/boot/geos.bin`. Then I ran `grub-mkrescue -o build/geos.iso build/isodir` to make an ISO containing my OS and grub. Yippee + # File structure ``` geos/ diff --git a/log/2024-03-27.md b/log/2024-03-27.md new file mode 100644 index 0000000..fa15430 --- /dev/null +++ b/log/2024-03-27.md @@ -0,0 +1,189 @@ +# VGA text +Hello. Today I succesfully made a `println!` macro. + +## std +To following conventions I decided to make a `std` library. To do this I made a workspace in `kernel/`. + +`kernel/Cargo.toml` +```toml +[package] +name = "kernel" +version = "0.1.0" +edition = "2021" + +[workspace] +members = ["std"] + +[dependencies] +std = { path = "std" } +``` + +## lib.rs +To not have one big spaghetti file I separated out all the individal modules into different files. +`kernel/std/src/lib.rs` +```rust +#![no_std] + +pub mod vga_buffer; +``` + +## vga_buffer.rs +First step for a VGA text driver is to be able to represent data. + +`kernel/std/src/lib.rs` +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum Color { + Black = 0, + Blue = 1, + Green = 2, + Cyan = 3, + Red = 4, + Magenta = 5, + Brown = 6, + LightGray = 7, + DarkGray = 8, + LightBlue = 9, + LightGreen = 10, + LightCyan = 11, + LightRed = 12, + Pink = 13, + Yellow = 14, + White = 15, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] // Store this as a u8 +pub struct ColorCode(u8); + +impl ColorCode { + pub fn new(foreground: Color, background: Color) -> ColorCode { + ColorCode((background as u8) << 4 | (foreground as u8)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] // Tells rust to store this like a struct in C, i.e. store this as 2 bytes +struct ScreenChar { + ascii_character: u8, + color_code: ColorCode, +} + +const BUFFER_HEIGHT: usize = 25; +const BUFFER_WIDTH: usize = 80; + +#[repr(transparent)] +struct Buffer { + chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT], +} +``` + +Now I can represent the VGA text buffer. To write to this I want to make a writer struct that implements `fmt::Write`. +```rust +pub struct Writer { + col: usize, + row: usize, + buffer: &'static mut Buffer, +} + +impl Writer { + pub fn write_byte(&mut self, byte: u8) { + self.write_byte_color(byte, ColorCode::new(Color::White, Color::Black)); + } + + pub fn write_byte_color(&mut self, byte: u8, color: ColorCode) { + match byte { + b'\n' => self.new_line(), + byte => { + if self.col >= BUFFER_WIDTH { + self.new_line(); + } + + let row = self.row; + let col = self.col; + + let color_code = color; + self.buffer.chars[row][col] = ScreenChar { + ascii_character: byte, + color_code, + }; + self.col += 1; + } + } + } + + pub fn write_string(&mut self, s: &str) { + self.write_string_color(s, ColorCode::new(Color::White, Color::Black)); + } + + pub fn write_string_color(&mut self, s: &str, color: ColorCode) { + for char in s.bytes() { + match char { + 0x20..=0x7e | b'\n' => self.write_byte_color(char, color), // Ascii Character + _ => self.write_byte(0xfe), // Non ascii + } + } + } + + fn new_line(&mut self) { + self.row += 1; + self.col = 0; + } +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_string(s); + Ok(()) + } +} +``` + +The problem now becomes that I can create a `Writer` but I don't have a global position, so it will just overwrite what I wrote last time. To solve this I can use a static variable. But a static variable won't be mutable. This can be solved with `Mutex` in the spin crate and `lazy_static`. + +`kernel/std/Cargo.toml` +```toml +[package] +name = "std" +version = "0.1.0" +edition = "2021" + +[dependencies] +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +spin = "0.9.8" +``` + +and to make a static variable I wrote (copied from [here](https://os.phil-opp.com/vga-text-mode/)) this +```rust +lazy_static! { + pub static ref WRITER: Mutex = Mutex::new(Writer { + col: 0, + row: 0, + buffer: unsafe { &mut *(0xB8000 as *mut Buffer) }, + }); +} +``` +in `kernel/std/src/vga_buffer.rs`. + +Last step is just to add a function and 2 macros +```rust +#[doc(hidden)] +pub fn _print(args: fmt::Arguments) { + WRITER.lock().write_fmt(args).unwrap() +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); +} +``` + +Huge thanks to @phil-opp for [os.phil-opp.com](https://os.phil-opp.com/) +