⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/
print.rs

1//! Kernel printing macros.
2//!
3//! Provides `print!` and `println!` macros that delegate to the
4//! architecture-specific serial/UART output. On x86_64 this writes to
5//! the VGA text buffer, on AArch64 to the PL011 UART, and on RISC-V
6//! to the 16550 UART via SBI.
7//!
8//! Also provides `kprintln!` / `kprint!` / `kprint_num!` macros that
9//! work uniformly across all architectures, including AArch64 where
10//! the standard `println!` is a no-op due to an LLVM loop-compilation
11//! bug. The `kprintln!` macro handles arch dispatch internally,
12//! eliminating the need for per-call-site `cfg(target_arch)` blocks.
13
14// Host target (x86_64-unknown-linux-gnu, for coverage/unit tests):
15// Kernel serial/UART hardware is not available; use no-op to avoid SIGSEGV.
16#[cfg(not(target_os = "none"))]
17#[macro_export]
18macro_rules! print {
19    ($($arg:tt)*) => ({ let _ = format_args!($($arg)*); });
20}
21
22#[cfg(not(target_os = "none"))]
23#[macro_export]
24macro_rules! println {
25    () => ({});
26    ($($arg:tt)*) => ({ let _ = format_args!($($arg)*); });
27}
28
29// x86_64 bare-metal — triple output: serial + framebuffer console + capture
30// buffer
31#[cfg(all(target_arch = "x86_64", target_os = "none"))]
32#[macro_export]
33macro_rules! print {
34    ($($arg:tt)*) => ({
35        $crate::serial::_serial_print(format_args!($($arg)*));
36        $crate::graphics::fbcon::_fbcon_print(format_args!($($arg)*));
37        $crate::print_capture::_capture_print(format_args!($($arg)*));
38    });
39}
40
41#[cfg(all(target_arch = "x86_64", target_os = "none"))]
42#[macro_export]
43macro_rules! println {
44    () => ($crate::print!("\n"));
45    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
46}
47
48// AArch64 implementation - uses DirectUartWriter (assembly-based byte output)
49// to bypass LLVM loop-compilation bug. The write_str() implementation calls
50// uart_write_bytes_asm() which is a pure assembly loop, so LLVM's optimizer
51// cannot miscompile the critical byte-output path. The core::fmt machinery
52// drives the formatting but each write_str() call goes through safe assembly.
53#[cfg(target_arch = "aarch64")]
54#[macro_export]
55macro_rules! print {
56    ($($arg:tt)*) => ({
57        use core::fmt::Write;
58        let mut _w = $crate::arch::aarch64::direct_uart::DirectUartWriter;
59        let _ = _w.write_fmt(format_args!($($arg)*));
60    });
61}
62
63#[cfg(target_arch = "aarch64")]
64#[macro_export]
65macro_rules! println {
66    () => ($crate::print!("\n"));
67    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
68}
69
70// RISC-V implementation
71#[cfg(target_arch = "riscv64")]
72#[macro_export]
73macro_rules! print {
74    ($($arg:tt)*) => ($crate::serial::_serial_print(format_args!($($arg)*)));
75}
76
77#[cfg(target_arch = "riscv64")]
78#[macro_export]
79macro_rules! println {
80    () => ($crate::print!("\n"));
81    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
82}
83
84// ---------------------------------------------------------------------------
85// Unified kprintln! / kprint! / kprint_num! macros
86//
87// These macros handle the AArch64 LLVM limitation *inside* the macro
88// definition, so call-sites don't need any cfg(target_arch) blocks.
89//
90// * Literal-only path (`kprintln!("hello")`) works on ALL architectures
91//   including AArch64 (via direct_uart assembly).
92// * Formatted path (`kprintln!("x={}", 42)`) works on x86_64 and RISC-V; on
93//   AArch64 it is a silent no-op (same as current `println!` behavior, but
94//   centralized).
95// ---------------------------------------------------------------------------
96
97/// Print a string literal to the kernel console (all architectures).
98/// For formatted output, works on x86_64 and RISC-V only; no-op on AArch64.
99#[macro_export]
100macro_rules! kprint {
101    // Literal-only: works on all architectures
102    ($lit:literal) => {{
103        #[cfg(target_arch = "aarch64")]
104        $crate::arch::aarch64::direct_uart::direct_print_str($lit);
105        #[cfg(not(target_arch = "aarch64"))]
106        $crate::print!($lit);
107    }};
108    // Formatted: x86_64/RISC-V only; silent no-op on AArch64
109    ($($arg:tt)*) => {{
110        #[cfg(not(target_arch = "aarch64"))]
111        $crate::print!($($arg)*);
112    }};
113}
114
115/// Print a string literal followed by newline (all architectures).
116/// For formatted output, works on x86_64 and RISC-V only; no-op on AArch64.
117#[macro_export]
118macro_rules! kprintln {
119    // No args: just a newline
120    () => {{
121        #[cfg(target_arch = "aarch64")]
122        $crate::arch::aarch64::direct_uart::direct_print_str("\n");
123        #[cfg(not(target_arch = "aarch64"))]
124        $crate::println!();
125    }};
126    // Literal-only: works on all architectures including AArch64
127    ($lit:literal) => {{
128        #[cfg(target_arch = "aarch64")]
129        $crate::arch::aarch64::direct_uart::direct_print_str(concat!($lit, "\n"));
130        #[cfg(not(target_arch = "aarch64"))]
131        $crate::println!($lit);
132    }};
133    // Formatted: x86_64/RISC-V only; silent no-op on AArch64
134    ($($arg:tt)*) => {{
135        #[cfg(not(target_arch = "aarch64"))]
136        $crate::println!($($arg)*);
137    }};
138}
139
140/// Print a literal prefix followed by a number on all architectures.
141/// On AArch64, uses direct_uart assembly-based number printing.
142#[macro_export]
143macro_rules! kprint_num {
144    ($prefix:literal, $n:expr) => {{
145        #[cfg(target_arch = "aarch64")]
146        {
147            $crate::arch::aarch64::direct_uart::direct_print_str($prefix);
148            $crate::arch::aarch64::direct_uart::direct_print_num($n as u64);
149            $crate::arch::aarch64::direct_uart::direct_print_str("\n");
150        }
151        #[cfg(not(target_arch = "aarch64"))]
152        $crate::println!("{}{}", $prefix, $n);
153    }};
154}
155
156/// Print a runtime &str expression (not just a literal) on all architectures.
157/// On AArch64, uses direct_uart; on x86_64/RISC-V, uses serial print.
158#[macro_export]
159macro_rules! kprint_rt {
160    ($s:expr) => {{
161        #[cfg(target_arch = "aarch64")]
162        $crate::arch::aarch64::direct_uart::direct_print_str($s);
163        #[cfg(not(target_arch = "aarch64"))]
164        $crate::print!("{}", $s);
165    }};
166}
167
168/// Print a u64 number without newline on all architectures.
169/// On AArch64, uses direct_uart assembly; on x86_64/RISC-V, uses serial.
170#[macro_export]
171macro_rules! kprint_u64 {
172    ($n:expr) => {{
173        #[cfg(target_arch = "aarch64")]
174        $crate::arch::aarch64::direct_uart::direct_print_num($n as u64);
175        #[cfg(not(target_arch = "aarch64"))]
176        $crate::print!("{}", $n);
177    }};
178}
179
180// Legacy macros (kept for backward compatibility, prefer kprintln!/kprint!)
181
182/// Bootstrap-safe println (legacy - prefer kprintln!)
183#[macro_export]
184macro_rules! boot_println {
185    () => { $crate::kprintln!(); };
186    ($s:literal) => { $crate::kprintln!($s); };
187    ($($arg:tt)*) => { $crate::kprintln!($($arg)*); };
188}
189
190/// Number printing (legacy - prefer kprint_num!)
191#[macro_export]
192macro_rules! boot_print_num {
193    ($prefix:literal, $n:expr) => {
194        $crate::kprint_num!($prefix, $n);
195    };
196}