Use `log` crate instead of ad-hoc print macros.

master
James T. Martin 2020-07-21 13:48:39 -07:00
parent f2db3d581d
commit ae7e7d3a80
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
8 changed files with 91 additions and 131 deletions

9
Cargo.lock generated
View File

@ -29,6 +29,7 @@ name = "bootproof"
version = "0.1.0"
dependencies = [
"compiler_builtins",
"log",
"num-integer",
"uefi",
"x86_64",
@ -75,9 +76,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.18"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
dependencies = [
"unicode-xid",
]
@ -93,9 +94,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.34"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b"
checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
dependencies = [
"proc-macro2",
"quote",

View File

@ -12,6 +12,10 @@ compiler_builtins = { git = "https://github.com/rust-lang/compiler-builtins" }
uefi = "0.4.7"
x86_64 = "0.11.1"
[dependencies.log]
version = "0.4.11"
default-features = false
[dependencies.num-integer]
version = "0.1.36"
default-features = false

View File

@ -1,5 +1,3 @@
use crate::graphics::tty::Tty;
use crate::println;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new();
@ -12,5 +10,5 @@ pub fn load() {
}
extern "x86-interrupt" fn breakpoint_handler(_: &mut InterruptStackFrame) {
println!("Breakpoint reached!");
log::info!("Breakpoint reached!");
}

View File

@ -2,5 +2,4 @@ pub mod color;
pub mod display;
pub mod font;
pub mod terminal;
#[macro_use]
pub mod tty;

View File

@ -125,7 +125,6 @@ impl Glyph for PSFGlyph {
fn get(&self, x: usize, y: usize) -> bool {
if x > self.width || y > self.height {
use crate::graphics::tty::Tty;
crate::panic!("Glyph pixel index out of bounds.");
}

View File

@ -1,115 +1,9 @@
pub mod serial;
pub mod terminal;
use crate::graphics::tty::serial::SerialTty;
pub trait Tty {
fn putc(&mut self, c: char);
fn puts(&mut self, s: &str);
fn clear(&mut self);
fn flush(&mut self);
}
pub static mut STDOUT: Option<SerialTty> = None;
pub static mut STDERR: Option<SerialTty> = None;
// HACK: These macros are horribly repetitive. There's got to be a better way...
#[macro_export]
macro_rules! print {
// These additional single-argument cases are necessary because `format!` requires allocation,
// which I don't necessarily *have* (not to mention it's inefficient).
($s:expr) => {{
let mut tty;
unsafe {
tty = crate::graphics::tty::STDOUT.clone().unwrap();
}
tty.puts($s);
tty.flush();
}};
($($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 {
($s:expr) => {{
let mut tty;
unsafe {
tty = crate::graphics::tty::STDOUT.clone().unwrap();
}
tty.puts($s);
tty.putc('\n');
tty.flush();
}};
($($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 {
($s:expr) => {{
let mut tty;
unsafe {
tty = crate::graphics::tty::STDERR.clone().unwrap();
}
tty.puts($s);
tty.flush();
}};
($($arg:expr),*) => {{
let mut tty;
unsafe {
tty = crate::graphics::tty::STDERR.clone().unwrap();
}
tty.puts(&alloc::format!($($arg),*));
tty.flush();
}}
}
#[macro_export]
macro_rules! eprintln {
($s:expr) => {{
let mut tty;
unsafe {
tty = crate::graphics::tty::STDERR.clone().unwrap();
}
tty.puts($s);
tty.putc('\n');
tty.flush();
}};
($($arg:expr),*) => {{
let mut tty;
unsafe {
tty = crate::graphics::tty::STDERR.clone().unwrap();
}
tty.puts(&alloc::format!($($arg),*));
tty.putc('\n');
tty.flush();
}}
}
#[macro_export]
macro_rules! panic {
($($arg:expr),*) => {{
crate::eprintln!($($arg),*);
crate::arch::x86_64::halt()
}}
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
panic!("{}", info);
}

63
src/logger.rs Normal file
View File

@ -0,0 +1,63 @@
use alloc::format;
use core::cell::UnsafeCell;
use crate::graphics::tty::Tty;
use crate::graphics::tty::serial::SerialTty;
use log::{Record, LevelFilter, Metadata, SetLoggerError};
enum GlobalLogger {
None,
// Hardcoding as SerialTty for now.
// I can worry about dealing with other implementation types when necessary.
Tty(UnsafeCell<SerialTty>),
}
use GlobalLogger::*;
impl log::Log for GlobalLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
match self {
None => false,
_ => true
}
}
fn log(&self, record: &Record) {
match self {
None => {},
Tty(tty) => unsafe {
// TODO: Lose the dependency on the `format!` macro
// so we don't have to allocate a String here.
(*tty.get()).puts(&format!("{} - {}", record.level(), record.args()));
},
}
}
fn flush(&self) {
match self {
None => {},
Tty(tty) => unsafe {
(*tty.get()).flush();
},
}
}
}
// The logger is not thread-safe, but for now we only use one processor.
// FIXME: Support multiple processors.
unsafe impl Sync for GlobalLogger {}
unsafe impl Send for GlobalLogger {}
static mut LOGGER: GlobalLogger = GlobalLogger::None;
pub fn init() -> Result<(), SetLoggerError> {
unsafe {
log::set_logger(&LOGGER)
.map(|()| log::set_max_level(LevelFilter::Info))
}
}
pub fn set_tty(tty: SerialTty) {
unsafe {
LOGGER = GlobalLogger::Tty(UnsafeCell::new(tty));
}
}

View File

@ -9,11 +9,11 @@ extern crate alloc;
mod allocator;
mod arch;
#[macro_use]
mod graphics;
mod logger;
use alloc::vec::Vec;
use crate::graphics::tty::{Tty, STDOUT, STDERR};
use crate::graphics::tty::serial::SerialTty;
use uefi::prelude::*;
@ -36,20 +36,8 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
// which is undefined behavior.
ALLOCATOR = GlobalAllocator::Uefi(UefiAllocator::new(st_boot.unsafe_clone()));
// Although serial ports don't physically exist on modern devices,
// they're still supposed by emulators (for QEMU you can set `-serial stdio`),
// and they're extremely useful for debugging
// because they don't require any setup and are trivial to use.
STDOUT = Some({
let mut stdout = SerialTty::new(0x3F8);
stdout.clear();
stdout
});
STDERR = Some({
let mut stderr = SerialTty::new(0x3F8);
stderr.clear();
stderr
});
logger::set_tty(SerialTty::new(0x3F8));
logger::init().unwrap();
}
// Our first task is to exit the UEFI boot services.
@ -119,3 +107,17 @@ fn main(_st: SystemTable<uefi::table::Runtime>) -> ! {
// We do *not* disable interrupts to allow for testing the interrupt handlers.
loop { x86_64::instructions::hlt(); }
}
#[macro_export]
macro_rules! panic {
($($arg:expr),*) => {{
log::error!($($arg),*);
// FIXME: Panic shouldn't depend on an architecture-specific function.
crate::arch::x86_64::halt()
}}
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
panic!("{}", info);
}