blob: dbd4e77b5f39220adedaf7df708766875f7e7c91 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
// Copyright 2022-2023 Google LLC
// Author: Ard Biesheuvel <ardb@google.com>
use core::fmt::Write;
use core::mem::MaybeUninit;
use log::{Metadata, Record};
use mmio::{Allow, Deny, VolBox};
use once_cell::race::OnceRef;
use spinning_top::Spinlock;
pub struct DumbSerialConsole {
out: Spinlock<VolBox<u32, Deny, Allow>>,
}
struct DumbSerialConsoleWriter<'a> {
console: &'a DumbSerialConsole,
}
pub fn init(base: usize) -> &'static DumbSerialConsole {
// Statically allocated so we can init the console before the heap
static mut _CON: MaybeUninit<DumbSerialConsole> = MaybeUninit::uninit();
OUT.get_or_init(|| unsafe {
let v = VolBox::<u32, Deny, Allow>::new(base as *mut u32);
_CON.write(DumbSerialConsole {
out: Spinlock::new(v),
})
})
}
impl DumbSerialConsole {
fn puts(&self, s: &str) {
let mut out = self.out.lock();
for b in s.as_bytes().iter() {
if *b == b'\n' {
out.write(b'\r' as u32);
}
out.write(*b as u32)
}
}
pub fn write_wchar_array(&self, s: *const u16) {
let mut out = self.out.lock();
let mut offset: isize = 0;
loop {
match unsafe { *s.offset(offset) } {
0 => break,
0x80.. => (),
w => {
if w == b'\n' as u16 {
out.write(b'\r' as u32);
}
out.write(w as u32);
}
}
offset += 1;
}
}
}
impl Write for DumbSerialConsoleWriter<'_> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
Ok(self.console.puts(s))
}
}
impl log::Log for DumbSerialConsole {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::max_level()
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let mut out = DumbSerialConsoleWriter { console: &self };
write!(&mut out, "efilite {} - {}", record.level(), record.args()).ok();
}
}
fn flush(&self) {}
}
pub static OUT: OnceRef<DumbSerialConsole> = OnceRef::new();