diff --git a/src/graphics.rs b/src/graphics.rs index cf23d1f..46901af 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -2,6 +2,7 @@ pub mod color; pub mod display; pub mod font; pub mod terminal; +#[macro_use] pub mod tty; use crate::graphics::color::{COLOR_BLACK, COLOR_WHITE}; diff --git a/src/graphics/display/gop.rs b/src/graphics/display/gop.rs index c284438..eee1b39 100644 --- a/src/graphics/display/gop.rs +++ b/src/graphics/display/gop.rs @@ -1,5 +1,7 @@ +use crate::println; use crate::graphics::color::{Color, RGB}; use crate::graphics::display::Display; +use crate::graphics::tty::Tty; use uefi::proto::console::gop::{FrameBuffer, GraphicsOutput, ModeInfo, PixelFormat}; const PIXEL_WIDTH_BYTES: usize = 4; @@ -24,7 +26,7 @@ impl GopDisplay<'_> { } let mode = mode.expect("No usable pixel formats found."); let (width, height) = mode.info().resolution(); - crate::log!("Using mode: {}x{} {:?}", width, height, mode.info().pixel_format()); + println!("Using mode: {}x{} {:?}", width, height, mode.info().pixel_format()); gop.set_mode(&mode).expect("Failed to set UEFI Graphics Output mode.").unwrap(); let info = gop.current_mode_info(); diff --git a/src/graphics/tty.rs b/src/graphics/tty.rs index 8862731..7153844 100644 --- a/src/graphics/tty.rs +++ b/src/graphics/tty.rs @@ -1,4 +1,9 @@ +pub mod serial; pub mod terminal; +pub mod uefi; + +use alloc::boxed::Box; +use crate::graphics::tty::serial::SerialTty; pub trait Tty { fn putc(&mut self, c: char); @@ -6,3 +11,69 @@ pub trait Tty { fn clear(&mut self); fn flush(&mut self); } + +pub static mut STDOUT: Option = None; +pub static mut STDERR: Option = None; + +#[macro_export] +macro_rules! print { + ($( $arg:expr ),* ) => { + let mut tty; + unsafe { + tty = crate::graphics::tty::STDOUT.clone().unwrap(); + } + tty.puts(&alloc::format!($( $arg ),*)); + tty.flush(); + } +} + +#[macro_export] +macro_rules! println { + ($( $arg:expr ),* ) => { + let mut tty; + unsafe { + tty = crate::graphics::tty::STDOUT.clone().unwrap(); + } + tty.puts(&alloc::format!($( $arg ),*)); + tty.putc('\n'); + tty.flush(); + } +} + +#[macro_export] +macro_rules! eprint { + ($( $arg:expr ),* ) => { + let mut tty; + unsafe { + tty = crate::graphics::tty::STDOUT.clone().unwrap(); + } + tty.puts(&alloc::format!($( $arg ),*)); + tty.flush(); + } +} + +#[macro_export] +macro_rules! eprintln { + ($( $arg:expr ),* ) => { + let mut tty; + unsafe { + tty = crate::graphics::tty::STDOUT.clone().unwrap(); + } + tty.puts(&alloc::format!($( $arg ),*)); + tty.putc('\n'); + tty.flush(); + } +} + +#[macro_export] +macro_rules! panic { + ($( $arg:expr ),* ) => { + crate::eprintln!($( $arg ),*); + crate::misc::halt() + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + panic!("{}", info); +} diff --git a/src/graphics/tty/serial.rs b/src/graphics/tty/serial.rs new file mode 100644 index 0000000..5ddfb5c --- /dev/null +++ b/src/graphics/tty/serial.rs @@ -0,0 +1,45 @@ +use alloc::string::String; +use crate::graphics::tty::Tty; + +#[derive(Clone)] +pub struct SerialTty { + port: u16, + buffer: String, +} + +impl SerialTty { + pub unsafe fn new(port: u16) -> SerialTty { + SerialTty { + port: port, + buffer: String::new(), + } + } + + fn outb(&self, cmd: u8) { + unsafe { + asm!("out dx, al", in("dx") self.port, in("al") cmd); + } + } +} + +impl Tty for SerialTty { + fn putc(&mut self, c: char) { + self.buffer.push(c); + } + + fn puts(&mut self, s: &str) { + self.buffer.push_str(s); + } + + fn clear(&mut self) { + // VT100 escape code to reset the terminal: `ESC C`. + self.puts("\u{1B}c"); + } + + fn flush(&mut self) { + for b in self.buffer.bytes() { + self.outb(b); + } + self.buffer.clear(); + } +} diff --git a/src/graphics/tty/uefi.rs b/src/graphics/tty/uefi.rs new file mode 100644 index 0000000..b5ff06a --- /dev/null +++ b/src/graphics/tty/uefi.rs @@ -0,0 +1,49 @@ +use alloc::string::String; +use alloc::vec::Vec; +use crate::graphics::tty::Tty; +use uefi::proto::console::text::Output; + +struct UefiTty<'boot> { + // It is impossible to get ownership of an Output, + // so instead we must pass in the entire boot system table. + output: Output<'boot>, + buffer: String, +} + +impl UefiTty<'_> { + pub fn new<'boot>(output: Output<'boot>) -> UefiTty<'boot> { + UefiTty { + output: output, + buffer: String::new(), + } + } +} + +impl Tty for UefiTty<'_> { + fn putc(&mut self, c: char) { + self.buffer.push(c); + } + + fn puts(&mut self, s: &str) { + self.buffer.push_str(s); + } + + fn clear(&mut self) { + // VT100 escape code to reset the terminal: `ESC C`. + self.output.clear(); + } + + fn flush(&mut self) { + let mut codes: Vec = Vec::new(); + for c in self.buffer.chars() { + codes.push(c as u16); + } + codes.push(0); + + let s = uefi::CStr16::from_u16_with_nul(&codes) + .unwrap_or_else(|_| panic!("Failed to convert to UCS-2: {}", self.buffer)); + self.output.output_string(s).unwrap().unwrap(); + + self.buffer.clear(); + } +} diff --git a/src/logger.rs b/src/logger.rs deleted file mode 100644 index 18f5866..0000000 --- a/src/logger.rs +++ /dev/null @@ -1,101 +0,0 @@ -use core::fmt::Write; - -pub enum LoggerBackend { - None, - // It is impossible to get ownership of an Output, - // so instead we must pass in the entire boot system table. - UefiStdio(uefi::prelude::SystemTable), -} - -enum LoggerOutput { - Stdout, - Stderr -} - -pub struct Logger<'a> { - backend: &'a LoggerBackend, - output: LoggerOutput -} - -impl LoggerBackend { - pub fn stdout<'a>(&'a self) -> Logger<'a> { - Logger { - backend: self, - output: LoggerOutput::Stdout - } - } - - pub fn stderr<'a>(&'a self) -> Logger<'a> { - Logger { - backend: self, - output: LoggerOutput::Stderr - } - } -} - -impl Write for Logger<'_> { - fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { - match self.backend { - LoggerBackend::UefiStdio(st) => { - let output = match &self.output { - LoggerOutput::Stdout => st.stdout(), - LoggerOutput::Stderr => st.stderr() - }; - output.write_str(s) - }, - LoggerBackend::None => { - // There's pretty much no way to recover from a missing logger. - // What are we supposed to do-- log the error? - Ok(()) - }, - } - } -} - -pub static mut LOGGER_BACKEND: LoggerBackend = LoggerBackend::None; - -#[macro_export] -macro_rules! log { - ($( $arg:expr ),* ) => { - unsafe { - use core::fmt::Write; - core::writeln!(crate::logger::LOGGER_BACKEND.stderr(), $( $arg ),*).unwrap(); - } - } -} - -#[macro_export] -macro_rules! print { - ($( $arg:expr ),* ) => { - unsafe { - use core::fmt::Write; - core::write!(crate::logger::LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap(); - } - } -} - -#[macro_export] -macro_rules! println { - ($( $arg:expr ),* ) => { - unsafe { - use core::fmt::Write; - core::writeln!(crate::logger::LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap(); - } - } -} - -#[macro_export] -macro_rules! panic { - ($( $arg:expr ),* ) => { - { - use crate::misc::halt; - crate::log!($( $arg ),*); - halt() - } - } -} - -#[panic_handler] -fn panic(info: &core::panic::PanicInfo) -> ! { - panic!("{}", info); -} diff --git a/src/main.rs b/src/main.rs index cb85044..e95e6e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,16 +6,17 @@ extern crate alloc; mod allocator; +#[macro_use] mod graphics; -mod logger; mod misc; -use crate::allocator::{Allocator, ALLOCATOR}; -use crate::logger::{LoggerBackend, LOGGER_BACKEND}; -use crate::misc::halt; - +use alloc::boxed::Box; use core::mem; use core::slice; +use crate::allocator::{Allocator, ALLOCATOR}; +use crate::graphics::tty::{Tty, STDOUT, STDERR}; +use crate::graphics::tty::serial::SerialTty; +use crate::misc::halt; use uefi::prelude::*; use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType}; @@ -23,6 +24,7 @@ fn setup(st: &SystemTable, _handle: Handle) { st.stdout().reset(false).expect_success("Failed to reset UEFI stdout."); println!("Booting..."); + use core::fmt::Write; for entry in st.config_table() { use uefi::table::cfg::*; @@ -50,7 +52,8 @@ fn efi_main(handle: Handle, st_boot: SystemTable) -> Status { // Tasks that require the UEFI boot services. unsafe { - LOGGER_BACKEND = LoggerBackend::UefiStdio(st_boot.unsafe_clone()); + STDOUT = Some(SerialTty::new(0x3F8)); + STDERR = Some(SerialTty::new(0x3F8)); ALLOCATOR = Allocator::Uefi(st_boot.unsafe_clone()); } @@ -74,11 +77,10 @@ fn efi_main(handle: Handle, st_boot: SystemTable) -> Status { // Tasks that do not require the UEFI boot services. - // I do not currently have an adequate stdout for post-UEFI, but the UEFI one is now invalid. unsafe { - LOGGER_BACKEND = LoggerBackend::None; + // TODO: An allocator that works out of UEFI mode. ALLOCATOR = Allocator::None; } - main(st_runtime, mmap); + main(st_runtime, mmap) }