use crate::proto::console::text::Output;
use crate::system;
use core::fmt::{self, Write};
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
static LOGGER: Logger = Logger::new();
pub unsafe fn init() {
system::with_stdout(|stdout| unsafe {
LOGGER.set_output(stdout);
});
log::set_logger(&LOGGER).unwrap();
log::set_max_level(log::STATIC_MAX_LEVEL);
}
pub fn disable() {
LOGGER.disable();
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "log-debugcon"
))]
#[derive(Copy, Clone, Debug)]
struct DebugconWriter;
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "log-debugcon"
))]
impl DebugconWriter {
const IO_PORT: u16 = 0xe9;
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "log-debugcon"
))]
impl core::fmt::Write for DebugconWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
for &byte in s.as_bytes() {
unsafe {
core::arch::asm!("outb %al, %dx", in("al") byte, in("dx") Self::IO_PORT, options(att_syntax))
};
}
Ok(())
}
}
#[derive(Debug)]
pub struct Logger {
writer: AtomicPtr<Output>,
}
impl Logger {
#[must_use]
pub const fn new() -> Self {
Self {
writer: AtomicPtr::new(ptr::null_mut()),
}
}
#[must_use]
fn output(&self) -> *mut Output {
self.writer.load(Ordering::Acquire)
}
pub unsafe fn set_output(&self, output: *mut Output) {
self.writer.store(output, Ordering::Release);
}
pub fn disable(&self) {
unsafe { self.set_output(ptr::null_mut()) }
}
}
impl log::Log for Logger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
if let Some(writer) = unsafe { self.output().as_mut() } {
let _ = DecoratedLog::write(
writer,
record.level(),
record.args(),
record.file().unwrap_or("<unknown file>"),
record.line().unwrap_or(0),
);
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "log-debugcon"
))]
{
let _ = DecoratedLog::write(
&mut DebugconWriter,
record.level(),
record.args(),
record.file().unwrap_or("<unknown file>"),
record.line().unwrap_or(0),
);
}
}
fn flush(&self) {
}
}
unsafe impl Sync for Logger {}
unsafe impl Send for Logger {}
struct DecoratedLog<'writer, 'a, W: fmt::Write> {
writer: &'writer mut W,
log_level: log::Level,
at_line_start: bool,
file: &'a str,
line: u32,
}
impl<'writer, 'a, W: fmt::Write> DecoratedLog<'writer, 'a, W> {
fn write(
writer: &'writer mut W,
log_level: log::Level,
args: &fmt::Arguments,
file: &'a str,
line: u32,
) -> fmt::Result {
let mut decorated_writer = Self {
writer,
log_level,
at_line_start: true,
file,
line,
};
writeln!(decorated_writer, "{}", *args)
}
}
impl<W: fmt::Write> fmt::Write for DecoratedLog<'_, '_, W> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let mut lines = s.lines();
let first = lines.next().unwrap_or("");
if self.at_line_start {
write!(
self.writer,
"[{:>5}]: {:>12}@{:03}: ",
self.log_level, self.file, self.line
)?;
self.at_line_start = false;
}
write!(self.writer, "{first}")?;
for line in lines {
let level = self.log_level;
write!(self.writer, "\n{level}: {line}")?;
}
if s.ends_with('\n') {
writeln!(self.writer)?;
self.at_line_start = true;
}
Ok(())
}
}