Replace "logger" with a TTY. Added serial and UEFI TTYs.
parent
42af83395b
commit
5445ead12b
|
@ -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};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
101
src/logger.rs
101
src/logger.rs
|
@ -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);
|
||||
}
|
20
src/main.rs
20
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<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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue