#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
use core::sync::atomic::{AtomicBool, Ordering};
use log::{Level, Log, Metadata, Record};
mod io {
use core::ffi::c_void;
#[repr(C)]
struct Iovec {
iov_base: *const c_void,
iov_len: usize,
}
#[cfg(feature = "lk")]
const SYS_WRITEV: usize = 0x01;
#[cfg(all(not(feature = "lk"), target_arch = "aarch64"))]
const SYS_WRITEV: usize = 66;
#[cfg(all(not(feature = "lk"), target_arch = "x86_64"))]
const SYS_WRITEV: usize = 20;
#[cfg(target_arch = "aarch64")]
pub unsafe fn write(fd: i32, data: &[u8]) {
let iov = Iovec { iov_base: data.as_ptr() as *const c_void, iov_len: data.len() };
#[cfg(feature = "lk")]
core::arch::asm!(
"svc #0",
in("x12") SYS_WRITEV,
inlateout("x0") fd => _,
in("x1") &iov,
in("x2") 1usize,
options(nostack)
);
#[cfg(not(feature = "lk"))]
core::arch::asm!(
"svc #0",
in("x8") SYS_WRITEV,
inlateout("x0") fd => _,
in("x1") &iov,
in("x2") 1usize,
options(nostack)
);
}
#[cfg(target_arch = "x86_64")]
pub unsafe fn write(fd: i32, data: &[u8]) {
let iov = Iovec { iov_base: data.as_ptr() as *const c_void, iov_len: data.len() };
core::arch::asm!(
"syscall",
inlateout("rax") SYS_WRITEV => _,
in("rdi") fd as usize,
in("rsi") &iov,
in("rdx") 1usize,
out("rcx") _,
out("r11") _,
options(nostack)
);
}
}
pub type FormatFn = alloc::boxed::Box<dyn Fn(&Record) -> alloc::string::String + Sync + Send>;
extern crate alloc;
pub struct TrustyLoggerConfig {
log_level: log::Level,
custom_format: Option<FormatFn>,
}
impl TrustyLoggerConfig {
pub const fn new() -> Self {
TrustyLoggerConfig { log_level: Level::Info, custom_format: None }
}
pub fn with_min_level(mut self, level: log::Level) -> Self {
self.log_level = level;
self
}
pub fn format<F>(mut self, format: F) -> Self
where
F: Fn(&Record) -> alloc::string::String + Sync + Send + 'static,
{
self.custom_format = Some(alloc::boxed::Box::new(format));
self
}
}
impl Default for TrustyLoggerConfig {
fn default() -> Self {
TrustyLoggerConfig::new()
}
}
pub struct TrustyLogger {
config: TrustyLoggerConfig,
}
impl TrustyLogger {
pub const fn new(config: TrustyLoggerConfig) -> Self {
TrustyLogger { config }
}
}
impl Log for TrustyLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= self.config.log_level
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let message = match &self.config.custom_format {
Some(fmt_fn) => fmt_fn(record),
None => alloc::format!("{} - {}\n", record.level(), record.args()),
};
unsafe {
io::write(2, message.as_bytes());
}
}
}
fn flush(&self) {}
}
static LOGGER_INITIALIZED: AtomicBool = AtomicBool::new(false);
pub fn init() {
init_with_config(TrustyLoggerConfig::default());
}
pub fn init_with_config(config: TrustyLoggerConfig) {
if LOGGER_INITIALIZED.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_ok()
{
let log_level_filter = config.log_level.to_level_filter();
let logger = alloc::boxed::Box::leak(alloc::boxed::Box::new(TrustyLogger::new(config)));
log::set_logger(logger).expect("Could not set global logger");
log::set_max_level(log_level_filter);
}
}