Replace "logger" with a TTY. Added serial and UEFI TTYs.

master
James T. Martin 2020-07-16 22:31:03 -07:00
parent 42af83395b
commit 5445ead12b
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
7 changed files with 180 additions and 111 deletions

View File

@ -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};

View File

@ -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();

View File

@ -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<SerialTty> = None;
pub static mut STDERR: Option<SerialTty> = 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);
}

View File

@ -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();
}
}

49
src/graphics/tty/uefi.rs Normal file
View File

@ -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<u16> = 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();
}
}

View File

@ -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<uefi::prelude::Boot>),
}
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);
}

View File

@ -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<Boot>, _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<Boot>) -> 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<Boot>) -> 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)
}