use crate::{
ffi::{c_char, c_void},
fmt,
prelude::*,
str::RawFormatter,
sync::atomic::{
Atomic,
AtomicType,
Relaxed, },
};
#[expect(clippy::missing_safety_doc)]
#[export]
unsafe extern "C" fn rust_fmt_argument(
buf: *mut c_char,
end: *mut c_char,
ptr: *const c_void,
) -> *mut c_char {
use fmt::Write;
let mut w = unsafe { RawFormatter::from_ptrs(buf.cast(), end.cast()) };
let _ = w.write_fmt(unsafe { *ptr.cast::<fmt::Arguments<'_>>() });
w.pos().cast()
}
#[doc(hidden)]
pub mod format_strings {
const LENGTH_PREFIX: usize = 2;
pub const LENGTH: usize = 10;
const fn generate(is_cont: bool, prefix: &[u8; 3]) -> [u8; LENGTH] {
assert!(prefix[0] == b'\x01');
if is_cont {
assert!(prefix[1] == b'c');
} else {
assert!(prefix[1] >= b'0' && prefix[1] <= b'7');
}
assert!(prefix[2] == b'\x00');
let suffix: &[u8; LENGTH - LENGTH_PREFIX] = if is_cont {
b"%pA\0\0\0\0\0"
} else {
b"%s: %pA\0"
};
[
prefix[0], prefix[1], suffix[0], suffix[1], suffix[2], suffix[3], suffix[4], suffix[5],
suffix[6], suffix[7],
]
}
pub static EMERG: [u8; LENGTH] = generate(false, bindings::KERN_EMERG);
pub static ALERT: [u8; LENGTH] = generate(false, bindings::KERN_ALERT);
pub static CRIT: [u8; LENGTH] = generate(false, bindings::KERN_CRIT);
pub static ERR: [u8; LENGTH] = generate(false, bindings::KERN_ERR);
pub static WARNING: [u8; LENGTH] = generate(false, bindings::KERN_WARNING);
pub static NOTICE: [u8; LENGTH] = generate(false, bindings::KERN_NOTICE);
pub static INFO: [u8; LENGTH] = generate(false, bindings::KERN_INFO);
pub static DEBUG: [u8; LENGTH] = generate(false, bindings::KERN_DEBUG);
pub static CONT: [u8; LENGTH] = generate(true, bindings::KERN_CONT);
}
#[doc(hidden)]
#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
pub unsafe fn call_printk(
format_string: &[u8; format_strings::LENGTH],
module_name: &[u8],
args: fmt::Arguments<'_>,
) {
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_printk(
format_string.as_ptr(),
module_name.as_ptr(),
core::ptr::from_ref(&args).cast::<c_void>(),
);
}
}
#[doc(hidden)]
#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
pub fn call_printk_cont(args: fmt::Arguments<'_>) {
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_printk(
format_strings::CONT.as_ptr(),
core::ptr::from_ref(&args).cast::<c_void>(),
);
}
}
#[doc(hidden)]
#[cfg(not(testlib))]
#[macro_export]
#[expect(clippy::crate_in_macro_def)]
macro_rules! print_macro (
($format_string:path, false, $($arg:tt)+) => (
match $crate::prelude::fmt!($($arg)+) {
args => unsafe {
$crate::print::call_printk(
&$format_string,
crate::__LOG_PREFIX,
args,
);
}
}
);
($format_string:path, true, $($arg:tt)+) => (
$crate::print::call_printk_cont(
$crate::prelude::fmt!($($arg)+),
);
);
);
#[cfg(testlib)]
#[macro_export]
macro_rules! print_macro (
($format_string:path, $e:expr, $($arg:tt)+) => (
()
);
);
#[macro_export]
macro_rules! pr_emerg (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::EMERG, false, $($arg)*)
)
);
#[macro_export]
macro_rules! pr_alert (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::ALERT, false, $($arg)*)
)
);
#[macro_export]
macro_rules! pr_crit (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::CRIT, false, $($arg)*)
)
);
#[macro_export]
macro_rules! pr_err (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::ERR, false, $($arg)*)
)
);
#[macro_export]
macro_rules! pr_warn (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::WARNING, false, $($arg)*)
)
);
#[macro_export]
macro_rules! pr_notice (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::NOTICE, false, $($arg)*)
)
);
#[macro_export]
#[doc(alias = "print")]
macro_rules! pr_info (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::INFO, false, $($arg)*)
)
);
#[macro_export]
#[doc(alias = "print")]
macro_rules! pr_debug (
($($arg:tt)*) => (
if cfg!(debug_assertions) {
$crate::print_macro!($crate::print::format_strings::DEBUG, false, $($arg)*)
}
)
);
#[macro_export]
macro_rules! pr_cont (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*)
)
);
pub struct OnceLite(Atomic<State>);
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
enum State {
Incomplete = 0,
Complete = 1,
}
unsafe impl AtomicType for State {
type Repr = i32;
}
impl OnceLite {
#[inline(always)]
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
OnceLite(Atomic::new(State::Incomplete))
}
pub fn call_once<F>(&self, f: F) -> bool
where
F: FnOnce(),
{
let old = self.0.load(Relaxed);
if old == State::Complete {
return false;
}
let old = self.0.xchg(State::Complete, Relaxed);
if old == State::Complete {
return false;
}
f();
true
}
}
#[macro_export]
macro_rules! do_once_lite {
{ $($e:tt)* } => {{
#[link_section = ".data..once"]
static ONCE: $crate::print::OnceLite = $crate::print::OnceLite::new();
ONCE.call_once(|| { $($e)* });
}};
}
#[macro_export]
macro_rules! pr_emerg_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_emerg!($($arg)*) }
)
);
#[macro_export]
macro_rules! pr_alert_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_alert!($($arg)*) }
)
);
#[macro_export]
macro_rules! pr_crit_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_crit!($($arg)*) }
)
);
#[macro_export]
macro_rules! pr_err_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_err!($($arg)*) }
)
);
#[macro_export]
macro_rules! pr_warn_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_warn!($($arg)*) }
)
);
#[macro_export]
macro_rules! pr_notice_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_notice!($($arg)*) }
)
);
#[macro_export]
macro_rules! pr_info_once (
($($arg:tt)*) => (
$crate::do_once_lite! { $crate::pr_info!($($arg)*) }
)
);