1#![allow(unsafe_op_in_unsafe_fn)]
11#![no_std]
12
13use core::sync::atomic::{AtomicBool, Ordering};
14
15use log::{Level, Log, Metadata, Record};
16
17mod io {
20 use core::ffi::c_void;
21
22 #[repr(C)]
24 struct Iovec {
25 iov_base: *const c_void,
26 iov_len: usize,
27 }
28
29 #[cfg(feature = "lk")]
35 const SYS_WRITEV: usize = 0x01;
36 #[cfg(all(not(feature = "lk"), target_arch = "aarch64"))]
37 const SYS_WRITEV: usize = 66;
38 #[cfg(all(not(feature = "lk"), target_arch = "x86_64"))]
39 const SYS_WRITEV: usize = 20;
40
41 #[cfg(target_arch = "aarch64")]
47 pub unsafe fn write(fd: i32, data: &[u8]) {
48 let iov = Iovec { iov_base: data.as_ptr() as *const c_void, iov_len: data.len() };
49 #[cfg(feature = "lk")]
54 core::arch::asm!(
55 "svc #0",
56 in("x12") SYS_WRITEV,
57 inlateout("x0") fd => _,
58 in("x1") &iov,
59 in("x2") 1usize,
60 options(nostack)
61 );
62 #[cfg(not(feature = "lk"))]
63 core::arch::asm!(
64 "svc #0",
65 in("x8") SYS_WRITEV,
66 inlateout("x0") fd => _,
67 in("x1") &iov,
68 in("x2") 1usize,
69 options(nostack)
70 );
71 }
72
73 #[cfg(target_arch = "x86_64")]
79 pub unsafe fn write(fd: i32, data: &[u8]) {
80 let iov = Iovec { iov_base: data.as_ptr() as *const c_void, iov_len: data.len() };
81 core::arch::asm!(
85 "syscall",
86 inlateout("rax") SYS_WRITEV => _,
87 in("rdi") fd as usize,
88 in("rsi") &iov,
89 in("rdx") 1usize,
90 out("rcx") _,
91 out("r11") _,
92 options(nostack)
93 );
94 }
95}
96
97pub type FormatFn = alloc::boxed::Box<dyn Fn(&Record) -> alloc::string::String + Sync + Send>;
101
102extern crate alloc;
103
104pub struct TrustyLoggerConfig {
106 log_level: log::Level,
107 custom_format: Option<FormatFn>,
108}
109
110impl TrustyLoggerConfig {
111 pub const fn new() -> Self {
112 TrustyLoggerConfig { log_level: Level::Info, custom_format: None }
113 }
114
115 pub fn with_min_level(mut self, level: log::Level) -> Self {
116 self.log_level = level;
117 self
118 }
119
120 pub fn format<F>(mut self, format: F) -> Self
121 where
122 F: Fn(&Record) -> alloc::string::String + Sync + Send + 'static,
123 {
124 self.custom_format = Some(alloc::boxed::Box::new(format));
125 self
126 }
127}
128
129impl Default for TrustyLoggerConfig {
130 fn default() -> Self {
131 TrustyLoggerConfig::new()
132 }
133}
134
135pub struct TrustyLogger {
137 config: TrustyLoggerConfig,
138}
139
140impl TrustyLogger {
141 pub const fn new(config: TrustyLoggerConfig) -> Self {
142 TrustyLogger { config }
143 }
144}
145
146impl Log for TrustyLogger {
147 fn enabled(&self, metadata: &Metadata) -> bool {
148 metadata.level() <= self.config.log_level
149 }
150
151 fn log(&self, record: &Record) {
152 if self.enabled(record.metadata()) {
153 let message = match &self.config.custom_format {
154 Some(fmt_fn) => fmt_fn(record),
155 None => alloc::format!("{} - {}\n", record.level(), record.args()),
156 };
157 unsafe {
159 io::write(2, message.as_bytes());
160 }
161 }
162 }
163
164 fn flush(&self) {}
165}
166
167static LOGGER_INITIALIZED: AtomicBool = AtomicBool::new(false);
170
171pub fn init() {
173 init_with_config(TrustyLoggerConfig::default());
174}
175
176pub fn init_with_config(config: TrustyLoggerConfig) {
181 if LOGGER_INITIALIZED.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_ok()
182 {
183 let log_level_filter = config.log_level.to_level_filter();
184 let logger = alloc::boxed::Box::leak(alloc::boxed::Box::new(TrustyLogger::new(config)));
187 log::set_logger(logger).expect("Could not set global logger");
188 log::set_max_level(log_level_filter);
189 }
190}