diff --git a/Cargo.toml b/Cargo.toml index c22ca9a..01b167e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,7 @@ -[package] -name = "shadeos" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["staticlib"] - -[dependencies] -lazy_static = {version = "1.4.0", features = ["spin_no_std"]} -spin = "0.9.4" +[workspace] +members = [ + "shadeos-kernel" +] [profile.dev] panic = "abort" diff --git a/Makefile b/Makefile index 594d353..45d0006 100644 --- a/Makefile +++ b/Makefile @@ -10,13 +10,15 @@ CARGO_EXTRA_ARG := --release .PHONY = target clean run rustc build debug default: build +all: build + clean: cargo clean build: target/shade.iso rustc: - RUST_TARGET_PATH=$(shell pwd) cargo build $(CARGO_EXTRA_ARG) + RUST_TARGET_PATH=$(shell pwd) cargo build -p shadeos-kernel $(CARGO_EXTRA_ARG) run: target/shade.iso qemu-system-x86_64 -cdrom target/shade.iso @@ -37,9 +39,9 @@ target/llb.a: target/llb/mb2_header.o target/llb/entry32.o mkdir -p target/llb ar rvs target/llb.a target/llb/mb2_header.o target/llb/entry32.o -target/llb/mb2_header.o: src/llb/mb2_header.asm +target/llb/mb2_header.o: shadeos-llb/src mkdir -p target/llb - $(ASM) $(ASMARGS) src/llb/mb2_header.asm -o target/llb/mb2_header.o -target/llb/entry32.o: src/llb/entry32.asm + $(ASM) $(ASMARGS) shadeos-llb/src/mb2_header.asm -o target/llb/mb2_header.o +target/llb/entry32.o: shadeos-llb/src mkdir -p target/llb - $(ASM) $(ASMARGS) src/llb/entry32.asm -o target/llb/entry32.o \ No newline at end of file + $(ASM) $(ASMARGS) shadeos-llb/src/entry32.asm -o target/llb/entry32.o \ No newline at end of file diff --git a/shadeos-kernel/Cargo.toml b/shadeos-kernel/Cargo.toml new file mode 100644 index 0000000..0586f9d --- /dev/null +++ b/shadeos-kernel/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "shadeos-kernel" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +spin = "0.9.7" +lazy_static = { version = "1.0", features = ["spin_no_std"] } + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" +[profile.release] +panic = "abort" \ No newline at end of file diff --git a/src/drivers/mod.rs b/shadeos-kernel/src/drivers/mod.rs similarity index 100% rename from src/drivers/mod.rs rename to shadeos-kernel/src/drivers/mod.rs diff --git a/shadeos-kernel/src/drivers/tty.rs b/shadeos-kernel/src/drivers/tty.rs new file mode 100644 index 0000000..d4137b3 --- /dev/null +++ b/shadeos-kernel/src/drivers/tty.rs @@ -0,0 +1,139 @@ +use core::fmt; +use core::ops::{Deref, DerefMut}; +use core::ptr::write_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::White, Color::Black), + buffer: unsafe { &mut *(0xb8000 as *mut Buffer) } + }); +} + +#[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)) + } +} +impl Default for ColorCode { + fn default() -> Self { + ColorCode::new(Color::White, Color::Black) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct ScreenChar { + ascii_character: u8, + color_code: ColorCode +} + +pub const BUFFER_HEIGHT: usize = 25; +pub const BUFFER_WIDTH: usize = 80; + +#[repr(transparent)] +pub struct Buffer { + chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] +} + +pub struct Writer { + pub(crate) column_position: usize, + pub(crate) color_code: ColorCode, + pub(crate) buffer: &'static mut Buffer +} +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; + + unsafe { + write_volatile((&mut self.buffer.chars[row][col] as *mut ScreenChar), ScreenChar { + ascii_character: 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]; + unsafe { + write_volatile((&mut self.buffer.chars[row-1][col] as *mut ScreenChar), character); + } + } + } + self.clear_row(BUFFER_HEIGHT - 1); + self.column_position = 0; + } + + fn clear_row(&mut self, row: usize) { + let blank = ScreenChar { + ascii_character: b' ', + color_code: self.color_code, + }; + for col in 0..BUFFER_WIDTH { + unsafe { + write_volatile((&mut self.buffer.chars[row][col] as *mut ScreenChar), blank); + } + } + } +} +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_string(s); + Ok(()) + } +} \ No newline at end of file diff --git a/src/lib.rs b/shadeos-kernel/src/lib.rs similarity index 70% rename from src/lib.rs rename to shadeos-kernel/src/lib.rs index 9c89f74..b984ad6 100644 --- a/src/lib.rs +++ b/shadeos-kernel/src/lib.rs @@ -4,40 +4,31 @@ #[macro_use] pub mod macros; -pub mod tty; pub mod drivers; -pub mod unsafe_utils; use core::panic::PanicInfo; -use lazy_static::lazy_static; +use core::fmt::Write; +use crate::drivers::tty::{Buffer, Color, ColorCode, Writer}; -use crate::tty::{ROWS_IN_TTY, TTY}; /// This is our kpanic function. This will eventually use any functioning video driver to dump the kernel, but for now it just hangs #[panic_handler] fn kpanic(_info: &PanicInfo) -> ! { // Completley reinitialize the tty system to ensure we can get the kpanic log out to vga. + println!(); println!("==== start kernel panic ===="); println!("{}", _info); println!("==== end kernel panic ===="); + loop {} } -use spin::Mutex; - -lazy_static! { - pub static ref ttys: Mutex> = Mutex::new(TTY::new()); -} - #[no_mangle] pub extern fn kmain() -> ! { - // initialize ttys and the kernel buffer - // just one for now - ttys.lock().select(0); - println!("hello from println"); + println!("hello from println pt2"); panic!("kpanic test"); diff --git a/shadeos-kernel/src/macros.rs b/shadeos-kernel/src/macros.rs new file mode 100644 index 0000000..4a6dc5d --- /dev/null +++ b/shadeos-kernel/src/macros.rs @@ -0,0 +1,17 @@ +use core::fmt; +use crate::drivers::tty::WRITER; + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::macros::_print(format_args!($($arg)*))) +} +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))) +} + +pub fn _print(args: fmt::Arguments) { + use core::fmt::Write; + WRITER.lock().write_fmt(args).unwrap(); +} \ No newline at end of file diff --git a/src/llb/entry32.asm b/shadeos-llb/src/entry32.asm similarity index 100% rename from src/llb/entry32.asm rename to shadeos-llb/src/entry32.asm diff --git a/src/llb/mb2_header.asm b/shadeos-llb/src/mb2_header.asm similarity index 100% rename from src/llb/mb2_header.asm rename to shadeos-llb/src/mb2_header.asm diff --git a/src/drivers/tty/mod.rs b/src/drivers/tty/mod.rs deleted file mode 100644 index 9b00c35..0000000 --- a/src/drivers/tty/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod vga; \ No newline at end of file diff --git a/src/drivers/tty/vga.rs b/src/drivers/tty/vga.rs deleted file mode 100644 index 3330f50..0000000 --- a/src/drivers/tty/vga.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::tty::{TTYCharacter, TTYDriver, TTYDriverError}; -use crate::unsafe_utils::writes::write_dword; - -/// Implementation of TTYDriver for 0xb8000 VGA Text Mode - -pub struct VGATTYDriver {} -impl TTYDriver for VGATTYDriver { - fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError> { - if row > 25 { - return Err(TTYDriverError::RowOutOfBounds { got: row }) - } - if col > 80 { - return Err(TTYDriverError::ColOutOfBounds { got: col }) - } - // calculate the memory offset - let write_location = 0xb8000 + (row * 80 + col) * 2; - - // convert the TTYCharacter into a raw u16 to be written to memory - let raw_char = (char.format as u16) << 8 | char.char as u16; - - // and then write it using unsafe_utils - unsafe { - write_dword(raw_char, write_location); - } - - Ok(()) - } -} \ No newline at end of file diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 1fa14af..0000000 --- a/src/macros.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => ($crate::tty::_print(format_args!($($arg)*))) -} -#[macro_export] -macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))) -} \ No newline at end of file diff --git a/src/tty.rs b/src/tty.rs deleted file mode 100644 index e182840..0000000 --- a/src/tty.rs +++ /dev/null @@ -1,221 +0,0 @@ -use core::error::Error; -use core::fmt; -use core::fmt::{Display, Formatter, Write}; -use crate::drivers::tty::vga::VGATTYDriver; -use crate::ttys; - -pub fn _print(args: fmt::Arguments) { - ttys.lock().write_fmt(args).unwrap(); - ttys.lock().update().unwrap(); -} - -pub struct TTY { - pub ttys: [VirtualTTY; C], - pub selected: usize, - driver: VGATTYDriver -} -impl TTY { - pub fn new() -> TTY { - Self { - ttys: [ - VirtualTTY::new(); C - ], - selected: 0, - driver: VGATTYDriver {} - } - } - - pub fn select(&mut self, new: usize) { self.selected = new; } - - pub fn update(&self) -> Result<(), TTYDriverError> { - let selected_tty = &self.ttys[self.selected]; - for (row_num, row) in selected_tty.screen_buffer.iter().enumerate() { - for (col_num, col) in row.iter().enumerate() { - self.driver.set_char(row_num, col_num, *col)?; - } - } - Ok(()) - } - - pub fn handle(&self) -> &VirtualTTY { - &self.ttys[self.selected] - } - - pub fn handle_mut(&mut self) -> &mut VirtualTTY { - &mut self.ttys[self.selected] - } - - pub fn select_and_update(&mut self, new: usize) -> Result<(), TTYDriverError> { - self.select(new); - self.update() - } -} - - -#[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(Copy, Clone)] -pub struct TTYCharacter { - pub format: u8, - pub char: char -} - -impl TTYCharacter { - pub fn blank() -> Self { Self { format: 0u8, char: ' ' } } - pub fn from(char: char) -> Self { Self { format: 0x0fu8, char }} - pub fn new(char: char, fg: Color, bg: Color) -> Self { - Self { - format: (bg as u8) << 4 | fg as u8, - char - } - } -} - -pub const ROWS_ON_SCREEN: usize = 25; -pub const COLS: usize = 80; - -pub const ROWS_IN_TTY: usize = 80; - -#[derive(Copy, Clone)] -pub struct VirtualTTY { - full_buffer: VirtualTTYBuffer, - full_buffer_copy: VirtualTTYBuffer, - screen_buffer: VirtualTTYBuffer, - line_offset: usize, - cursor_col: usize, - cursor_row: usize, - current_color_fg: Color, - current_color_bg: Color -} -impl Default for VirtualTTY { - fn default() -> Self { - Self { - full_buffer: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY], - full_buffer_copy: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY], - screen_buffer: [[TTYCharacter::blank(); COLS]; ROWS_ON_SCREEN], - line_offset: ROWS_IN_TTY - ROWS_ON_SCREEN, // assume we are scrolled all the way down - cursor_row: ROWS_IN_TTY - ROWS_ON_SCREEN, - current_color_fg: Color::White, - cursor_col: 0, - current_color_bg: Color::Black, - } - } -} -impl VirtualTTY { - pub fn new() -> Self { - Self::default() - } - - pub fn set_color(&mut self, fg: Color, bg: Color) { - self.current_color_bg = bg; - self.current_color_fg = fg; - } - - pub fn setcur(&mut self, cursor_x: usize, cursor_y: usize) { - self.cursor_row = cursor_y; - self.cursor_col = cursor_x; - } - - pub fn putchar(&mut self, character: char) { - match character { - '\n' => { - self.print_newline(); - self.cursor_col = 0; - self.cursor_row += 1; - }, - _ => { - if self.cursor_col > COLS { - self.print_newline(); - self.cursor_col = 0; - self.cursor_row += 1; - } - self._putchar(character); - self.cursor_col += 1; - } - } - } - - pub fn putstr(&mut self, str: &str) { - for char in str.chars() { - self.putchar(char); - } - } - - fn _putchar(&mut self, character: char) { - self.full_buffer[self.cursor_row][self.cursor_col] = TTYCharacter::new(character, self.current_color_fg, self.current_color_bg); - self.update_screenbuf() - } - - // shifts everything up one - fn print_newline(&mut self) { - for (line_no, line) in self.full_buffer.iter().skip(1).enumerate() { - self.full_buffer_copy[line_no] = *line; - } - self.full_buffer = self.full_buffer_copy; - } - - pub fn set_screen_offset(&mut self, new_offset: usize) -> Result<(), ()> { - if new_offset > (ROWS_IN_TTY - ROWS_ON_SCREEN) { - return Err(()) - } - self.line_offset = new_offset; - self.update_screenbuf(); - Ok(()) - } - - fn update_screenbuf(&mut self) { - for (line_no, line) in self.full_buffer.iter().skip(self.line_offset).enumerate() { - self.screen_buffer[line_no] = *line; - } - } -} - -type VirtualTTYBuffer = [[TTYCharacter; C]; R]; - -#[derive(Debug)] -pub enum TTYDriverError { - RowOutOfBounds { got: usize }, - ColOutOfBounds { got: usize }, - CannotScrollTooFar -} -impl Display for TTYDriverError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - Self::RowOutOfBounds { got } => write!(f, "Character write out of bounds: row {} too high", got), - Self::ColOutOfBounds { got } => write!(f, "Character write out of bounds: col {} too high", got), - Self::CannotScrollTooFar => write!(f, "Cannot scroll outside line buffer") - } - } -} -impl Error for TTYDriverError {} - -pub trait TTYDriver { - fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError>; -} - -impl Write for TTY { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.handle_mut().putstr(s); - Ok(()) - } -} \ No newline at end of file diff --git a/src/unsafe_utils/mod.rs b/src/unsafe_utils/mod.rs deleted file mode 100644 index 740e959..0000000 --- a/src/unsafe_utils/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -/// Unsafe utility functions -pub mod writes; \ No newline at end of file diff --git a/src/unsafe_utils/writes.rs b/src/unsafe_utils/writes.rs deleted file mode 100644 index 1312efd..0000000 --- a/src/unsafe_utils/writes.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Write the given dword to any arbitrary memory location. This is an inherently unsafe operation; hence the function is unsafe -pub unsafe fn write_dword(dword: u16, at: usize) { - let ptr = at as *mut u16; - *ptr = dword; -} \ No newline at end of file