diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..f95e6aa --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,9 @@ +[unstable] +build-std-features = ["compiler-builtins-mem"] +build-std = ["core", "compiler_builtins"] + +[build] +target = "x86_64-totos.json" + +[target.'cfg(target_os = "none")'] +runner = "bootimage runner" diff --git a/Cargo.toml b/Cargo.toml index 60e9fc5..6cf02d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,10 @@ panic = "abort" # disable stack unwinding on panic panic = "abort" # disable stack unwinding on panic [dependencies] +bootloader = "0.9" +volatile = "0.2.6" +spin = "0.5.2" + +[dependencies.lazy_static] +version = "1.0" +features = ["spin_no_std"] diff --git a/README.md b/README.md index 16eedd4..45cb86e 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ +### ABOUT + trying to make a toy OS in rust following [this](https://os.phil-opp.com) + +### DEPENDENCIES + +- qemu +- [cargo bootimage](https://github.com/rust-osdev/bootimage) + +### HOWTO + +to run this just install the [dependencies](#dependencies) and do: + +```shell +cargo run +``` diff --git a/src/main.rs b/src/main.rs index 132c4cc..83bce51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,11 +3,19 @@ use core::panic::PanicInfo; +mod vga_buffer; + +static HELLO: &[u8] = b"Hello toto :3"; + // this function is the entry point since the linker // looks for a function named `_start` by default #[unsafe(no_mangle)] pub extern "C" fn _start() -> ! { - loop{} + use core::fmt::Write; + vga_buffer::WRITER.lock().write_str("TOTOTO !!!! :3").unwrap(); + write!(vga_buffer::WRITER.lock(), ", smoking cigarette in the shower when they get wet i just light another :{}", 3).unwrap(); + + loop {} } // called on panic diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs new file mode 100644 index 0000000..8f71171 --- /dev/null +++ b/src/vga_buffer.rs @@ -0,0 +1,138 @@ +use core::fmt; +use volatile::Volatile; +use lazy_static::lazy_static; +use spin::Mutex; + +lazy_static! { + pub static ref WRITER: Mutex = Mutex::new(Writer { + column_position: 0, + color_code: ColorCode::new(Color::Pink, Color::Black), + buffer: unsafe { &mut *(0xB8000 as *mut Buffer) }, + }); +} + +#[allow(dead_code)] +#[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)] +struct ColorCode(u8); + +impl ColorCode { + 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_carachter: u8, + color_code: ColorCode, +} + +const BUFFER_HEIGHT: usize = 25; +const BUFFER_WIDTH: usize = 80; + +#[repr(transparent)] +struct Buffer { + // wrapping the `ScreenChar` in a generic `Volatile` + // type prevents the compiler from optimizing writes + // away since the program only writes to it but never + // reads from it. + // by making it volatile we signal to the compiler + // that our write call have side-effects + // to interact with this type the `write()` and `read()` + // methods must be used now + chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT], +} + +pub struct Writer { + column_position: usize, + color_code: ColorCode, + buffer: &'static mut Buffer, +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_string(s); + Ok(()) + } +} + +impl Writer { + pub fn write_byte(&mut self, byte: u8) { + match byte { + b'\n' => self.new_line(), + byte => { + if self.column_position >= BUFFER_WIDTH { + self.new_line(); + } + + let row = BUFFER_HEIGHT - 1; + let col = self.column_position; + + let color_code = self.color_code; + self.buffer.chars[row][col].write(ScreenChar { + ascii_carachter: byte, + color_code, + }); + self.column_position += 1; + } + } + } + + pub fn write_string(&mut self, s: &str) { + for byte in s.bytes() { + match byte { + // printable ascii byte or newline + 0x20..=0x7E | b'\n' => self.write_byte(byte), + // not part of printable ascii range + _ => self.write_byte(0xFE), + } + } + } + + fn new_line(&mut self) { + for row in 1..BUFFER_HEIGHT { + for col in 0..BUFFER_WIDTH { + let character = self.buffer.chars[row][col].read(); + self.buffer.chars[row - 1][col].write(character); + } + } + self.clear_row(BUFFER_HEIGHT - 1); + self.column_position = 0; + } + + // this method clears a row by overwriting all of its + // characters with a space character. + fn clear_row(&mut self, row: usize) { + let blank = ScreenChar { + ascii_carachter: b' ', + color_code: self.color_code, + }; + + for col in 0..BUFFER_WIDTH { + self.buffer.chars[row][col].write(blank); + } + } +} diff --git a/x86_64-totos.json b/x86_64-totos.json new file mode 100644 index 0000000..669ca9d --- /dev/null +++ b/x86_64-totos.json @@ -0,0 +1,16 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": 32, + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float", + "rustc-abi": "x86-softfloat" +}