From 45b4930c064bf9834d609be9360c8e23a019230c Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 20 Feb 2021 08:29:49 +0100 Subject: [PATCH] New printing support Features: - Adds macros like `info!` (with newline) and `pr_info!` (without newline) for the usual levels that the kernel support. - Includes the "continuation" level. - Includes automatically the module name in the string. - Includes, hopefully, pretty detailed logs. - Includes aliases to make it even easier to find the equivalent to `println!` for newcomers. Implementation: - Stack usage should be 1 KiB at most, regardless of the number of invocations of the macro in a single function. This should make it possible to use even when optimizations are not enabled. See code comments for the details. - The formatting string is compile-time, shared as a `static`, and always the same. We could perhaps pre-generate them per-module to avoid another `%.*s` in `printk()`, but I don't think it is worth the complexity at the moment, and it would increase memory usage anyway. Naming: - The `pr_*` names are the same as the kernel ones, and behave similarly, i.e. no newline at the end. - The non-`pr_*` ones are the same but without the prefix. They look better (shorter, no underscore), have the newline like `println!`, and they are what people should almost always use except if they need continuation. And they look pretty similar to the Rust `log` crate ones, too! - I think we can afford these short names (in the prelude, even!) since this is new code and nobody should be defining macros in the kernel with such short names, ever! Future: - I will open an issue with a few things that can be added on top, similar to the kernel parameters feature. - When we have tidy support (or custom clippy lints), we can add one to find the pattern: pr_*("...\n", ...); and suggest the non-prefixed, non-newline version instead: *("...", "..."); Signed-off-by: Miguel Ojeda --- .github/workflows/ci.yaml | 130 ++-- .github/workflows/kernel-arm64-debug.config | 1 + .github/workflows/kernel-arm64-release.config | 1 + .github/workflows/kernel-ppc64le-debug.config | 1 + .../workflows/kernel-ppc64le-release.config | 1 + .github/workflows/kernel-x86_64-debug.config | 1 + .../workflows/kernel-x86_64-release.config | 1 + .github/workflows/qemu-init.sh | 3 + .github/workflows/qemu-initramfs.desc | 1 + kernel/printk/printk.c | 2 + rust/kernel/lib.rs | 7 +- rust/kernel/prelude.rs | 7 +- rust/kernel/print.rs | 709 ++++++++++++++++++ rust/kernel/printk.rs | 85 --- rust/kernel/sync/mod.rs | 2 +- rust/module.rs | 9 +- samples/rust/Kconfig | 10 + samples/rust/Makefile | 1 + samples/rust/rust_chrdev.rs | 6 +- samples/rust/rust_minimal.rs | 8 +- samples/rust/rust_miscdev.rs | 4 +- samples/rust/rust_module_parameters.rs | 16 +- samples/rust/rust_print.rs | 102 +++ samples/rust/rust_stack_probing.rs | 6 +- samples/rust/rust_sync.rs | 8 +- 25 files changed, 964 insertions(+), 158 deletions(-) create mode 100644 rust/kernel/print.rs delete mode 100644 rust/kernel/printk.rs create mode 100644 samples/rust/rust_print.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 89bd0ba5abe9bfd..850c5e35f24f44d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -166,11 +166,6 @@ jobs: cp samples/rust/rust_module_parameters.rs samples/rust/rust_module_parameters_loadable_default.rs cp samples/rust/rust_module_parameters.rs samples/rust/rust_module_parameters_loadable_custom.rs - sed -i 's:" my_:" built-in default my_:g' samples/rust/rust_module_parameters_builtin_default.rs - sed -i 's:" my_:" built-in custom my_:g' samples/rust/rust_module_parameters_builtin_custom.rs - sed -i 's:" my_:" loadable default my_:g' samples/rust/rust_module_parameters_loadable_default.rs - sed -i 's:" my_:" loadable custom my_:g' samples/rust/rust_module_parameters_loadable_custom.rs - sed -i 's:rust_module_parameters:rust_module_parameters_builtin_default:g' samples/rust/rust_module_parameters_builtin_default.rs sed -i 's:rust_module_parameters:rust_module_parameters_builtin_custom:g' samples/rust/rust_module_parameters_builtin_custom.rs sed -i 's:rust_module_parameters:rust_module_parameters_loadable_default:g' samples/rust/rust_module_parameters_loadable_default.rs @@ -217,43 +212,94 @@ jobs: # Check - run: | - grep '] Rust minimal sample (init)$' qemu-stdout.log - grep '] Rust minimal sample (exit)$' qemu-stdout.log - - grep '] Rust module parameters sample (init)$' qemu-stdout.log - grep '] built-in default my_bool: true$' qemu-stdout.log - grep '] built-in default my_i32: 42$' qemu-stdout.log - grep '] built-in default my_str: default str val$' qemu-stdout.log - grep '] built-in default my_usize: 42$' qemu-stdout.log - grep '] built-in default my_array: \[0, 1]$' qemu-stdout.log - grep '] built-in custom my_bool: false$' qemu-stdout.log - grep '] built-in custom my_i32: 345543$' qemu-stdout.log - grep '] built-in custom my_str: 🦀mod$' qemu-stdout.log - grep '] built-in custom my_usize: 84$' qemu-stdout.log - grep '] built-in custom my_array: \[1, 2, 3]$' qemu-stdout.log - grep '] loadable default my_bool: true$' qemu-stdout.log - grep '] loadable default my_i32: 42$' qemu-stdout.log - grep '] loadable default my_str: default str val$' qemu-stdout.log - grep '] loadable default my_usize: 42$' qemu-stdout.log - grep '] loadable default my_array: \[0, 1]$' qemu-stdout.log - grep '] loadable custom my_bool: false$' qemu-stdout.log - grep '] loadable custom my_i32: 345543$' qemu-stdout.log - grep '] loadable custom my_str: 🦀mod$' qemu-stdout.log - grep '] loadable custom my_usize: 84$' qemu-stdout.log - grep '] loadable custom my_array: \[1, 2, 3]$' qemu-stdout.log - grep '] Rust module parameters sample (exit)$' qemu-stdout.log - - grep '] Rust synchronisation primitives sample (init)$' qemu-stdout.log - grep '] Rust synchronisation primitives sample (exit)$' qemu-stdout.log - - grep '] Rust character device sample (init)$' qemu-stdout.log - grep '] Rust character device sample (exit)$' qemu-stdout.log - - grep '] Rust miscellaneous device sample (init)$' qemu-stdout.log - grep '] Rust miscellaneous device sample (exit)$' qemu-stdout.log - - grep '] Rust stack probing sample (init)$' qemu-stdout.log - grep '] Rust stack probing sample (exit)$' qemu-stdout.log + grep '] rust_minimal: Rust minimal sample (init)$' qemu-stdout.log + grep '] rust_minimal: Rust minimal sample (exit)$' qemu-stdout.log + + - run: | + grep '] rust_print: Rust printing macros sample (init)$' qemu-stdout.log + + grep '] rust_print: Emergency message (level 0) with newline w/o args$' qemu-stdout.log + grep '] rust_print: Alert message (level 1) with newline w/o args$' qemu-stdout.log + grep '] rust_print: Critical message (level 2) with newline w/o args$' qemu-stdout.log + grep '] rust_print: Error message (level 3) with newline w/o args$' qemu-stdout.log + grep '] rust_print: Warning message (level 4) with newline w/o args$' qemu-stdout.log + grep '] rust_print: Notice message (level 5) with newline w/o args$' qemu-stdout.log + grep '] rust_print: Info message (level 6) with newline w/o args$' qemu-stdout.log + grep '] rust_print: A line that is continued with newline w/o args$' qemu-stdout.log + + grep '] rust_print: Emergency message (level 0) w/o newline w/o args$' qemu-stdout.log + grep '] rust_print: Alert message (level 1) w/o newline w/o args$' qemu-stdout.log + grep '] rust_print: Critical message (level 2) w/o newline w/o args$' qemu-stdout.log + grep '] rust_print: Error message (level 3) w/o newline w/o args$' qemu-stdout.log + grep '] rust_print: Warning message (level 4) w/o newline w/o args$' qemu-stdout.log + grep '] rust_print: Notice message (level 5) w/o newline w/o args$' qemu-stdout.log + grep '] rust_print: Info message (level 6) w/o newline w/o args$' qemu-stdout.log + grep '] rust_print: A line that is continued w/o newline w/o args$' qemu-stdout.log + + grep '] rust_print: Emergency message (level 0) with newline with args$' qemu-stdout.log + grep '] rust_print: Alert message (level 1) with newline with args$' qemu-stdout.log + grep '] rust_print: Critical message (level 2) with newline with args$' qemu-stdout.log + grep '] rust_print: Error message (level 3) with newline with args$' qemu-stdout.log + grep '] rust_print: Warning message (level 4) with newline with args$' qemu-stdout.log + grep '] rust_print: Notice message (level 5) with newline with args$' qemu-stdout.log + grep '] rust_print: Info message (level 6) with newline with args$' qemu-stdout.log + grep '] rust_print: A line that is continued with newline with args$' qemu-stdout.log + + grep '] rust_print: Emergency message (level 0) w/o newline with args$' qemu-stdout.log + grep '] rust_print: Alert message (level 1) w/o newline with args$' qemu-stdout.log + grep '] rust_print: Critical message (level 2) w/o newline with args$' qemu-stdout.log + grep '] rust_print: Error message (level 3) w/o newline with args$' qemu-stdout.log + grep '] rust_print: Warning message (level 4) w/o newline with args$' qemu-stdout.log + grep '] rust_print: Notice message (level 5) w/o newline with args$' qemu-stdout.log + grep '] rust_print: Info message (level 6) w/o newline with args$' qemu-stdout.log + grep '] rust_print: A line that is continued w/o newline with args$' qemu-stdout.log + + grep '] rust_print: Rust printing macros sample (exit)$' qemu-stdout.log + + - run: | + grep '] rust_module_parameters: Rust module parameters sample (init)$' qemu-stdout.log + + grep '] rust_module_parameters_builtin_default: my_bool: true$' qemu-stdout.log + grep '] rust_module_parameters_builtin_default: my_i32: 42$' qemu-stdout.log + grep '] rust_module_parameters_builtin_default: my_str: default str val$' qemu-stdout.log + grep '] rust_module_parameters_builtin_default: my_usize: 42$' qemu-stdout.log + grep '] rust_module_parameters_builtin_default: my_array: \[0, 1]$' qemu-stdout.log + + grep '] rust_module_parameters_builtin_custom: my_bool: false$' qemu-stdout.log + grep '] rust_module_parameters_builtin_custom: my_i32: 345543$' qemu-stdout.log + grep '] rust_module_parameters_builtin_custom: my_str: 🦀mod$' qemu-stdout.log + grep '] rust_module_parameters_builtin_custom: my_usize: 84$' qemu-stdout.log + grep '] rust_module_parameters_builtin_custom: my_array: \[1, 2, 3]$' qemu-stdout.log + + grep '] rust_module_parameters_loadable_default: my_bool: true$' qemu-stdout.log + grep '] rust_module_parameters_loadable_default: my_i32: 42$' qemu-stdout.log + grep '] rust_module_parameters_loadable_default: my_str: default str val$' qemu-stdout.log + grep '] rust_module_parameters_loadable_default: my_usize: 42$' qemu-stdout.log + grep '] rust_module_parameters_loadable_default: my_array: \[0, 1]$' qemu-stdout.log + + grep '] rust_module_parameters_loadable_custom: my_bool: false$' qemu-stdout.log + grep '] rust_module_parameters_loadable_custom: my_i32: 345543$' qemu-stdout.log + grep '] rust_module_parameters_loadable_custom: my_str: 🦀mod$' qemu-stdout.log + grep '] rust_module_parameters_loadable_custom: my_usize: 84$' qemu-stdout.log + grep '] rust_module_parameters_loadable_custom: my_array: \[1, 2, 3]$' qemu-stdout.log + + grep '] rust_module_parameters: Rust module parameters sample (exit)$' qemu-stdout.log + + - run: | + grep '] rust_sync: Rust synchronisation primitives sample (init)$' qemu-stdout.log + grep '] rust_sync: Rust synchronisation primitives sample (exit)$' qemu-stdout.log + + - run: | + grep '] rust_chrdev: Rust character device sample (init)$' qemu-stdout.log + grep '] rust_chrdev: Rust character device sample (exit)$' qemu-stdout.log + + - run: | + grep '] rust_miscdev: Rust miscellaneous device sample (init)$' qemu-stdout.log + grep '] rust_miscdev: Rust miscellaneous device sample (exit)$' qemu-stdout.log + + - run: | + grep '] rust_stack_probing: Rust stack probing sample (init)$' qemu-stdout.log + grep '] rust_stack_probing: Rust stack probing sample (exit)$' qemu-stdout.log # Report - run: | diff --git a/.github/workflows/kernel-arm64-debug.config b/.github/workflows/kernel-arm64-debug.config index 30fe069751bbbb1..983a4f0dab3451a 100644 --- a/.github/workflows/kernel-arm64-debug.config +++ b/.github/workflows/kernel-arm64-debug.config @@ -1421,6 +1421,7 @@ CONFIG_SAMPLES=y # CONFIG_SAMPLE_WATCHDOG is not set CONFIG_SAMPLES_RUST=y CONFIG_SAMPLE_RUST_MINIMAL=m +CONFIG_SAMPLE_RUST_PRINT=m CONFIG_SAMPLE_RUST_MODULE_PARAMETERS=m CONFIG_SAMPLE_RUST_SYNC=m CONFIG_SAMPLE_RUST_CHRDEV=m diff --git a/.github/workflows/kernel-arm64-release.config b/.github/workflows/kernel-arm64-release.config index ba4b6aae3f8ed04..a60446a0703f092 100644 --- a/.github/workflows/kernel-arm64-release.config +++ b/.github/workflows/kernel-arm64-release.config @@ -1339,6 +1339,7 @@ CONFIG_SAMPLES=y # CONFIG_SAMPLE_WATCHDOG is not set CONFIG_SAMPLES_RUST=y CONFIG_SAMPLE_RUST_MINIMAL=m +CONFIG_SAMPLE_RUST_PRINT=m CONFIG_SAMPLE_RUST_MODULE_PARAMETERS=m CONFIG_SAMPLE_RUST_SYNC=m CONFIG_SAMPLE_RUST_CHRDEV=m diff --git a/.github/workflows/kernel-ppc64le-debug.config b/.github/workflows/kernel-ppc64le-debug.config index 00efc00703b24cf..e160b88d25f5a3a 100644 --- a/.github/workflows/kernel-ppc64le-debug.config +++ b/.github/workflows/kernel-ppc64le-debug.config @@ -1481,6 +1481,7 @@ CONFIG_SAMPLES=y # CONFIG_SAMPLE_WATCHDOG is not set CONFIG_SAMPLES_RUST=y CONFIG_SAMPLE_RUST_MINIMAL=m +CONFIG_SAMPLE_RUST_PRINT=m CONFIG_SAMPLE_RUST_MODULE_PARAMETERS=m CONFIG_SAMPLE_RUST_SYNC=m CONFIG_SAMPLE_RUST_CHRDEV=m diff --git a/.github/workflows/kernel-ppc64le-release.config b/.github/workflows/kernel-ppc64le-release.config index b51481ac435a8c3..f94190476afe56c 100644 --- a/.github/workflows/kernel-ppc64le-release.config +++ b/.github/workflows/kernel-ppc64le-release.config @@ -1443,6 +1443,7 @@ CONFIG_SAMPLES=y # CONFIG_SAMPLE_WATCHDOG is not set CONFIG_SAMPLES_RUST=y CONFIG_SAMPLE_RUST_MINIMAL=m +CONFIG_SAMPLE_RUST_PRINT=m CONFIG_SAMPLE_RUST_MODULE_PARAMETERS=m CONFIG_SAMPLE_RUST_SYNC=m CONFIG_SAMPLE_RUST_CHRDEV=m diff --git a/.github/workflows/kernel-x86_64-debug.config b/.github/workflows/kernel-x86_64-debug.config index 43e31543bcdfbfc..a906d1baab2444c 100644 --- a/.github/workflows/kernel-x86_64-debug.config +++ b/.github/workflows/kernel-x86_64-debug.config @@ -1433,6 +1433,7 @@ CONFIG_SAMPLES=y # CONFIG_SAMPLE_WATCHDOG is not set CONFIG_SAMPLES_RUST=y CONFIG_SAMPLE_RUST_MINIMAL=m +CONFIG_SAMPLE_RUST_PRINT=m CONFIG_SAMPLE_RUST_MODULE_PARAMETERS=m CONFIG_SAMPLE_RUST_SYNC=m CONFIG_SAMPLE_RUST_CHRDEV=m diff --git a/.github/workflows/kernel-x86_64-release.config b/.github/workflows/kernel-x86_64-release.config index aa9f8bb816f031e..ff5ce673f1fdf2f 100644 --- a/.github/workflows/kernel-x86_64-release.config +++ b/.github/workflows/kernel-x86_64-release.config @@ -1381,6 +1381,7 @@ CONFIG_SAMPLES=y # CONFIG_SAMPLE_WATCHDOG is not set CONFIG_SAMPLES_RUST=y CONFIG_SAMPLE_RUST_MINIMAL=m +CONFIG_SAMPLE_RUST_PRINT=m CONFIG_SAMPLE_RUST_MODULE_PARAMETERS=m CONFIG_SAMPLE_RUST_SYNC=m CONFIG_SAMPLE_RUST_CHRDEV=m diff --git a/.github/workflows/qemu-init.sh b/.github/workflows/qemu-init.sh index 3a0d66a784242d6..5a742b9e733679e 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -3,6 +3,9 @@ busybox insmod rust_minimal.ko busybox rmmod rust_minimal.ko +busybox insmod rust_print.ko +busybox rmmod rust_print.ko + busybox insmod rust_module_parameters.ko busybox rmmod rust_module_parameters.ko diff --git a/.github/workflows/qemu-initramfs.desc b/.github/workflows/qemu-initramfs.desc index 346084ca832126a..ab30716a8beace4 100644 --- a/.github/workflows/qemu-initramfs.desc +++ b/.github/workflows/qemu-initramfs.desc @@ -6,6 +6,7 @@ slink /bin/sh /bin/busybox 0755 0 0 file /init .github/workflows/qemu-init.sh 0755 0 0 file /rust_minimal.ko samples/rust/rust_minimal.ko 0755 0 0 +file /rust_print.ko samples/rust/rust_print.ko 0755 0 0 file /rust_module_parameters.ko samples/rust/rust_module_parameters.ko 0755 0 0 file /rust_sync.ko samples/rust/rust_sync.ko 0755 0 0 file /rust_chrdev.ko samples/rust/rust_chrdev.ko 0755 0 0 diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 575a34b88936f90..d13be89530c4441 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -410,6 +410,8 @@ static u64 clear_seq; #else #define PREFIX_MAX 32 #endif + +/* Keep in sync with rust/kernel/print.rs */ #define LOG_LINE_MAX (1024 - PREFIX_MAX) #define LOG_LEVEL(v) ((v) & 0x07) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b49789c227aa342..06f9b559aa0f550 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -17,6 +17,7 @@ alloc_error_handler, const_fn, const_mut_refs, + const_panic, try_reserve )] #![deny(clippy::complexity)] @@ -47,7 +48,7 @@ pub mod miscdev; pub mod module_param; pub mod prelude; -pub mod printk; +pub mod print; pub mod random; mod static_assert; pub mod sync; @@ -152,7 +153,7 @@ fn panic(_info: &PanicInfo) -> ! { /// /// fn test() { /// // This prints `8`. -/// println!("{}", offset_of!(Test, b)); +/// info!("{}", offset_of!(Test, b)); /// } /// ``` #[macro_export] @@ -193,7 +194,7 @@ macro_rules! offset_of { /// let b_ptr = &test.b; /// let test_alias = unsafe { container_of!(b_ptr, Test, b) }; /// // This prints `true`. -/// println!("{}", core::ptr::eq(&test, test_alias)); +/// info!("{}", core::ptr::eq(&test, test_alias)); /// } /// ``` #[macro_export] diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index 6ebdab011b3e0ae..c692caa55e86ce4 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -15,4 +15,9 @@ pub use alloc::{borrow::ToOwned, string::String}; pub use module::module; -pub use super::{println, static_assert, KernelModule, KernelResult}; +pub use super::{alert, cont, crit, emerg, err, info, notice, warn}; +pub use super::{pr_alert, pr_cont, pr_crit, pr_emerg, pr_err, pr_info, pr_notice, pr_warn}; + +pub use super::static_assert; + +pub use super::{KernelModule, KernelResult}; diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs new file mode 100644 index 000000000000000..48305937d362dd3 --- /dev/null +++ b/rust/kernel/print.rs @@ -0,0 +1,709 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Printing facilities. +//! +//! C header: [`include/linux/printk.h`](../../../../include/linux/printk.h) +//! +//! Reference: + +use core::cmp; +use core::fmt; + +use crate::bindings; +use crate::c_types::c_int; + +/// Format strings. +/// +/// Public but hidden since it should only be used from public macros. +#[doc(hidden)] +pub mod format_strings { + use crate::bindings; + + /// The length we copy from the `KERN_*` kernel prefixes. + const LENGTH_PREFIX: usize = 2; + + /// The length of the fixed format strings. + pub const LENGTH: usize = 11; + + /// Generates a fixed format string for the kernel's [`printk`]. + /// + /// The format string is always the same for a given level, i.e. for a + /// given `prefix`, which are the kernel's `KERN_*` constants. + /// + /// [`printk`]: ../../../../include/linux/printk.h + const fn generate(is_cont: bool, prefix: &[u8; 3]) -> [u8; LENGTH] { + // Ensure the `KERN_*` macros are what we expect. + 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"%.*s\0\0\0\0\0" + } else { + b"%s: %.*s\0" + }; + + [ + prefix[0], prefix[1], suffix[0], suffix[1], suffix[2], suffix[3], suffix[4], suffix[5], + suffix[6], suffix[7], suffix[8], + ] + } + + // Generate the format strings at compile-time. + // + // This avoids the compiler generating the contents on the fly in the stack. + // + // Furthermore, `static` instead of `const` is used to share the strings + // for all the kernel. + 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); +} + +/// Prints a message via the kernel's [`printk`]. +/// +/// Public but hidden since it should only be used from public macros. +/// +/// # Safety +/// +/// The format string must be one of the ones in [`format_strings`], and +/// the module name must be null-terminated. +/// +/// [`printk`]: ../../../../include/linux/printk.h +#[doc(hidden)] +pub unsafe fn call_printk( + format_string: &[u8; format_strings::LENGTH], + module_name: &[u8], + string: &[u8], +) { + // `printk` does not seem to fail in any path. + bindings::printk( + format_string.as_ptr() as _, + module_name.as_ptr(), + string.len() as c_int, + string.as_ptr(), + ); +} + +/// Prints a message via the kernel's [`printk`] for the `CONT` level. +/// +/// Public but hidden since it should only be used from public macros. +/// +/// [`printk`]: ../../../../include/linux/printk.h +#[doc(hidden)] +pub fn call_printk_cont(string: &[u8]) { + // `printk` does not seem to fail in any path. + // + // SAFETY: The format string is fixed. + unsafe { + bindings::printk( + format_strings::CONT.as_ptr() as _, + string.len() as c_int, + string.as_ptr(), + ); + } +} + +/// The maximum size of a log line in the kernel. +/// +/// From `kernel/printk/printk.c`. +const LOG_LINE_MAX: usize = 1024 - 32; + +/// The maximum size of a log line in our side. +/// +/// FIXME: We should be smarter than this, but for the moment, to reduce stack +/// usage, we only allow this much which should work for most purposes. +const LOG_LINE_SIZE: usize = 300; +crate::static_assert!(LOG_LINE_SIZE <= LOG_LINE_MAX); + +/// Public but hidden since it should only be used from public macros. +#[doc(hidden)] +pub struct LogLineWriter { + data: [u8; LOG_LINE_SIZE], + pos: usize, +} + +impl LogLineWriter { + /// Creates a new [`LogLineWriter`]. + pub fn new() -> LogLineWriter { + LogLineWriter { + data: [0u8; LOG_LINE_SIZE], + pos: 0, + } + } + + /// Returns the internal buffer as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + &self.data[..self.pos] + } +} + +impl Default for LogLineWriter { + fn default() -> Self { + Self::new() + } +} + +impl fmt::Write for LogLineWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + let copy_len = cmp::min(LOG_LINE_SIZE - self.pos, s.as_bytes().len()); + self.data[self.pos..self.pos + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]); + self.pos += copy_len; + Ok(()) + } +} + +/// Helper function for the [`print_macro!`] to reduce stack usage. +/// +/// Public but hidden since it should only be used from public macros. +/// +/// # Safety +/// +/// The format string must be one of the ones in [`format_strings`], and +/// the module name must be null-terminated. +#[doc(hidden)] +pub unsafe fn format_and_call( + format_string: &[u8; format_strings::LENGTH], + module_name: &[u8], + args: fmt::Arguments, +) { + // Careful: this object takes quite a bit of stack. + let mut writer = LogLineWriter::new(); + + match fmt::write(&mut writer, args) { + Ok(_) => { + if CONT { + call_printk_cont(writer.as_bytes()); + } else { + call_printk(format_string, module_name, writer.as_bytes()); + } + } + + Err(_) => { + call_printk( + &format_strings::CRIT, + module_name, + b"Failure to format string.\n", + ); + } + }; +} + +/// Performs formatting and forwards the string to [`call_printk`]. +/// +/// Public but hidden since it should only be used from public macros. +#[doc(hidden)] +#[macro_export] +macro_rules! print_macro ( + // Without extra arguments: no need to format anything. + ($format_string:path, false, $suffix:literal, $fmt:expr) => ( + // SAFETY: This hidden macro should only be called by the documented + // printing macros which ensure the format string is one of the fixed + // ones. All `__MODULE_NAME`s are null-terminated as they are generated + // by the `module!` proc macro. + unsafe { + kernel::print::call_printk( + &$format_string, + crate::__MODULE_NAME, + concat!($fmt, $suffix).as_bytes(), + ); + } + ); + + // Without extra arguments: no need to format anything (`CONT` case). + ($format_string:path, true, $suffix:literal, $fmt:expr) => ( + kernel::print::call_printk_cont( + concat!($fmt, $suffix).as_bytes(), + ); + ); + + // With extra arguments: we need to perform formatting. + ($format_string:path, $cont:literal, $suffix:literal, $fmt:expr, $($arg:tt)*) => ( + // Forwarding the call to a function to perform the formatting + // is needed here to avoid stack overflows in non-optimized builds when + // invoking the printing macros a lot of times in the same function. + // Without it, the compiler reserves one `LogLineWriter` per macro + // invocation, which is a huge type. + // + // We could use an immediately-invoked closure for this, which + // seems to lower even more the stack usage at `opt-level=0` because + // `fmt::Arguments` objects do not pile up. However, that breaks + // the `?` operator if used in one of the arguments. + // + // At `opt-level=2`, the generated code is basically the same for + // all alternatives. + // + // SAFETY: This hidden macro should only be called by the documented + // printing macros which ensure the format string is one of the fixed + // ones. All `__MODULE_NAME`s are null-terminated as they are generated + // by the `module!` proc macro. + unsafe { + kernel::print::format_and_call::<$cont>( + &$format_string, + crate::__MODULE_NAME, + format_args!(concat!($fmt, $suffix), $($arg)*), + ); + } + ); +); + +// We could use a macro to generate these macros. However, doing so ends +// up being a bit ugly: it requires the dollar token trick to escape `$` as +// well as playing with the `doc` attribute. Furthermore, they cannot be easily +// imported in the prelude due to [1]. So, for the moment, we just write them +// manually, like in the C side; while keeping most of the logic in another +// macro, i.e. [`print_macro`]. +// +// [1]: https://github.com/rust-lang/rust/issues/52234 + +/// Prints an emergency-level message (level 0). +/// +/// Use [`emerg!`] unless you need to continue the message with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use this level if the system is unusable. +/// +/// Equivalent to the kernel's [`pr_emerg`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_emerg`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_emerg +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_emerg!("hello {} ", "there"); +/// cont!("with arguments"); +/// ``` +#[macro_export] +macro_rules! pr_emerg ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::EMERG, false, "", $($arg)*) + ) +); + +/// Prints an alert-level message (level 1). +/// +/// Use [`alert!`] unless you need to continue the message with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use this level if action must be taken immediately. +/// +/// Equivalent to the kernel's [`pr_alert`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_alert`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_alert +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_alert!("hello {} ", "there"); +/// cont!("with arguments"); +/// ``` +#[macro_export] +macro_rules! pr_alert ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::ALERT, false, "", $($arg)*) + ) +); + +/// Prints a critical-level message (level 2). +/// +/// Use [`crit!`] unless you need to continue the message with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use this level for critical conditions. +/// +/// Equivalent to the kernel's [`pr_crit`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_crit`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_crit +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_crit!("hello {} ", "there"); +/// cont!("with arguments"); +/// ``` +#[macro_export] +macro_rules! pr_crit ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::CRIT, false, "", $($arg)*) + ) +); + +/// Prints an error-level message (level 3). +/// +/// Use [`err!`] unless you need to continue the message with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use this level for error conditions. +/// +/// Equivalent to the kernel's [`pr_err`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_err`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_err +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_err!("hello {} ", "there"); +/// cont!("with arguments"); +/// ``` +#[macro_export] +macro_rules! pr_err ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::ERR, false, "", $($arg)*) + ) +); + +/// Prints a warning-level message (level 4). +/// +/// Use [`warn!`] unless you need to continue the message with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use this level for warning conditions. +/// +/// Equivalent to the kernel's [`pr_warn`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_warn`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_warn +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_warn!("hello {} ", "there"); +/// cont!("with arguments"); +/// ``` +#[macro_export] +macro_rules! pr_warn ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::WARNING, false, "", $($arg)*) + ) +); + +/// Prints a notice-level message (level 5). +/// +/// Use [`notice!`] unless you need to continue the message with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use this level for normal but significant conditions. +/// +/// Equivalent to the kernel's [`pr_notice`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_notice`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_notice +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_notice!("hello {} ", "there"); +/// cont!("with arguments"); +/// ``` +#[macro_export] +macro_rules! pr_notice ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::NOTICE, false, "", $($arg)*) + ) +); + +/// Prints an info-level message (level 6). +/// +/// Use [`info!`] unless you need to continue the message with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use this level for informational messages. +/// +/// Equivalent to the kernel's [`pr_info`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_info`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_info +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_info!("hello {} ", "there"); +/// cont!("with arguments"); +/// ``` +#[macro_export] +#[doc(alias = "print")] +macro_rules! pr_info ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::INFO, false, "", $($arg)*) + ) +); + +/// Continues a previous log message in the same line. +/// +/// Use [`cont!`] unless you need to continue the message (again) with [`cont!`] +/// or [`pr_cont!`]. +/// +/// Use only when continuing a previous `pr_*!` macro (e.g. [`pr_info!`]). +/// +/// Equivalent to the kernel's [`pr_cont`] macro. +/// +/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_cont`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_cont +/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html +/// +/// # Examples +/// +/// ``` +/// pr_info!("hello "); +/// pr_cont!("th"); +/// cont!("ere!"); +/// +/// pr_info!("format "); +/// pr_cont!("{} ", "some"); +/// cont!("{}", "arguments"); +/// ``` +#[macro_export] +macro_rules! pr_cont ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::CONT, true, "", $($arg)*) + ) +); + +/// Prints an emergency-level message, with a newline (level 0). +/// +/// Use this level if the system is unusable. +/// +/// Similar to the kernel's [`pr_emerg`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_emerg`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_emerg +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// emerg!("hello there!"); +/// emerg!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[doc(alias = "pr_emerg")] +macro_rules! emerg ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::EMERG, false, "\n", $($arg)*) + ) +); + +/// Prints an alert-level message, with a newline (level 1). +/// +/// Use this level if action must be taken immediately. +/// +/// Similar to the kernel's [`pr_alert`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_alert`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_alert +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// alert!("hello there!"); +/// alert!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[doc(alias = "pr_alert")] +macro_rules! alert ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::ALERT, false, "\n", $($arg)*) + ) +); + +/// Prints a critical-level message, with a newline (level 2). +/// +/// Use this level for critical conditions. +/// +/// Similar to the kernel's [`pr_crit`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_crit`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_crit +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// pr_crit!("hello there!"); +/// pr_crit!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[doc(alias = "pr_crit")] +macro_rules! crit ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::CRIT, false, "\n", $($arg)*) + ) +); + +/// Prints an error-level message, with a newline (level 3). +/// +/// Use this level for error conditions. +/// +/// Similar to the kernel's [`pr_err`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_err`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_err +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// err!("hello there!"); +/// err!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[doc(alias = "pr_err")] +macro_rules! err ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::ERR, false, "\n", $($arg)*) + ) +); + +/// Prints a warning-level message, with a newline (level 4). +/// +/// Use this level for warning conditions. +/// +/// Similar to the kernel's [`pr_warn`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_warn`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_warn +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// warn!("hello there!"); +/// warn!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[doc(alias = "pr_warn")] +macro_rules! warn ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::WARNING, false, "\n", $($arg)*) + ) +); + +/// Prints a notice-level message, with a newline (level 5). +/// +/// Use this level for normal but significant conditions. +/// +/// Similar to the kernel's [`pr_notice`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_notice`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_notice +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// notice!("hello there!"); +/// notice!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[doc(alias = "pr_notice")] +macro_rules! notice ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::NOTICE, false, "\n", $($arg)*) + ) +); + +/// Prints an info-level message, with a newline (level 6). +/// +/// Use this level for informational messages. +/// +/// Similar to the kernel's [`pr_info`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_info`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_info +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// info!("hello there!"); +/// info!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[doc(alias = "pr_info")] +#[doc(alias = "println")] +macro_rules! info ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::INFO, false, "\n", $($arg)*) + ) +); + +/// Continues a previous log message in the same line, with a newline. +/// +/// Use only when continuing a previous `pr_*!` macro (e.g. [`pr_info!`]). +/// +/// Similar to the kernel's [`pr_cont`] macro. +/// +/// Mimics the interface of [`std::println!`]. See [`core::fmt`] and +/// [`alloc::format!`] for information about the formatting syntax. +/// +/// [`pr_cont`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_cont +/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html +/// +/// # Examples +/// +/// ``` +/// pr_info!("hello "); +/// cont!("there!"); +/// +/// pr_info!("format {}", "some"); +/// cont!(" {}", "arguments"); +/// ``` +#[macro_export] +#[doc(alias = "pr_cont")] +macro_rules! cont ( + ($($arg:tt)*) => ( + $crate::print_macro!($crate::print::format_strings::CONT, true, "\n", $($arg)*) + ) +); diff --git a/rust/kernel/printk.rs b/rust/kernel/printk.rs deleted file mode 100644 index aef05c2c06e1fa4..000000000000000 --- a/rust/kernel/printk.rs +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! Printing facilities. -//! -//! C header: [`include/linux/printk.h`](../../../../include/linux/printk.h) -//! -//! Reference: - -use core::cmp; -use core::fmt; - -use crate::bindings; -use crate::c_types::c_int; - -#[doc(hidden)] -pub fn printk(s: &[u8]) { - // Do not copy the trailing `NUL` from `KERN_INFO`. - let mut fmt_str = [0; bindings::KERN_INFO.len() - 1 + b"%.*s\0".len()]; - fmt_str[..bindings::KERN_INFO.len() - 1] - .copy_from_slice(&bindings::KERN_INFO[..bindings::KERN_INFO.len() - 1]); - fmt_str[bindings::KERN_INFO.len() - 1..].copy_from_slice(b"%.*s\0"); - - // TODO: I believe `printk` never fails. - unsafe { bindings::printk(fmt_str.as_ptr() as _, s.len() as c_int, s.as_ptr()) }; -} - -// From `kernel/print/printk.c`. -const LOG_LINE_MAX: usize = 1024 - 32; - -#[doc(hidden)] -pub struct LogLineWriter { - data: [u8; LOG_LINE_MAX], - pos: usize, -} - -impl LogLineWriter { - /// Creates a new [`LogLineWriter`]. - pub fn new() -> LogLineWriter { - LogLineWriter { - data: [0u8; LOG_LINE_MAX], - pos: 0, - } - } - - /// Returns the internal buffer as a byte slice. - pub fn as_bytes(&self) -> &[u8] { - &self.data[..self.pos] - } -} - -impl Default for LogLineWriter { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Write for LogLineWriter { - fn write_str(&mut self, s: &str) -> fmt::Result { - let copy_len = cmp::min(LOG_LINE_MAX - self.pos, s.as_bytes().len()); - self.data[self.pos..self.pos + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]); - self.pos += copy_len; - Ok(()) - } -} - -/// Prints to the kernel console at `KERN_INFO` level. -/// -/// Mimics the interface of [`std::println!`]. -/// -/// [`std::println!`]: https://doc.rust-lang.org/std/macro.println.html -#[macro_export] -macro_rules! println { - () => ({ - $crate::printk::printk("\n".as_bytes()); - }); - ($fmt:expr) => ({ - $crate::printk::printk(concat!($fmt, "\n").as_bytes()); - }); - ($fmt:expr, $($arg:tt)*) => ({ - use ::core::fmt; - let mut writer = $crate::printk::LogLineWriter::new(); - let _ = fmt::write(&mut writer, format_args!(concat!($fmt, "\n"), $($arg)*)).unwrap(); - $crate::printk::printk(writer.as_bytes()); - }); -} diff --git a/rust/kernel/sync/mod.rs b/rust/kernel/sync/mod.rs index 59fdad84d464a19..625c22600e19c61 100644 --- a/rust/kernel/sync/mod.rs +++ b/rust/kernel/sync/mod.rs @@ -13,7 +13,7 @@ //! let data = alloc::sync::Arc::pin(unsafe { Mutex::new(0) }); //! mutex_init!(data.as_ref(), "test::data"); //! *data.lock() = 10; -//! kernel::println!("{}", *data.lock()); +//! info!("{}", *data.lock()); //! } //! ``` diff --git a/rust/module.rs b/rust/module.rs index d87c0d2433f9e3f..fd20c23aa69d8a0 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -355,11 +355,11 @@ fn generated_array_ops_name(vals: &str, max_length: usize) -> String { /// // taken to read the parameter: /// { /// let lock = THIS_MODULE.kernel_param_lock(); -/// println!("i32 param is: {}", writeable_i32.read(&lock)); +/// info!("i32 param is: {}", writeable_i32.read(&lock)); /// } /// // If the parameter is read only, it can be read without locking /// // the kernel parameters: -/// println!("i32 param is: {}", my_i32.read()); +/// info!("i32 param is: {}", my_i32.read()); /// Ok(MyKernelModule) /// } /// } @@ -584,6 +584,11 @@ pub fn module(ts: TokenStream) -> TokenStream { format!( " + /// The module name. + /// + /// Used by the printing macros, e.g. [`info!`]. + const __MODULE_NAME: &[u8] = b\"{name}\\0\"; + static mut __MOD: Option<{type_}> = None; // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 6aee6ff6765ac4d..5260507d2a66ba4 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL If unsure, say N. +config SAMPLE_RUST_PRINT + tristate "Printing macros" + help + This option builds the Rust printing macros sample. + + To compile this as a module, choose M here: + the module will be called rust_print. + + If unsure, say N. + config SAMPLE_RUST_MODULE_PARAMETERS tristate "Module parameters" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index fceefb5a1e1574b..47bf362c1e6ca99 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o +obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o obj-$(CONFIG_SAMPLE_RUST_MODULE_PARAMETERS) += rust_module_parameters.o obj-$(CONFIG_SAMPLE_RUST_SYNC) += rust_sync.o obj-$(CONFIG_SAMPLE_RUST_CHRDEV) += rust_chrdev.o diff --git a/samples/rust/rust_chrdev.rs b/samples/rust/rust_chrdev.rs index 6b51652f409fc39..c98e209c3a94e20 100644 --- a/samples/rust/rust_chrdev.rs +++ b/samples/rust/rust_chrdev.rs @@ -28,7 +28,7 @@ struct RustFile; impl FileOpener<()> for RustFile { fn open(_ctx: &()) -> KernelResult { - println!("rust file was opened!"); + info!("rust file was opened!"); Ok(Box::try_new(Self)?) } } @@ -45,7 +45,7 @@ struct RustChrdev { impl KernelModule for RustChrdev { fn init() -> KernelResult { - println!("Rust character device sample (init)"); + info!("Rust character device sample (init)"); let mut chrdev_reg = chrdev::Registration::new_pinned(cstr!("rust_chrdev"), 0, &THIS_MODULE)?; @@ -62,6 +62,6 @@ impl KernelModule for RustChrdev { impl Drop for RustChrdev { fn drop(&mut self) { - println!("Rust character device sample (exit)"); + info!("Rust character device sample (exit)"); } } diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 2b86c8b1412afcb..f919328d82e94a7 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -24,8 +24,8 @@ struct RustMinimal { impl KernelModule for RustMinimal { fn init() -> KernelResult { - println!("Rust minimal sample (init)"); - println!("Am I built-in? {}", !cfg!(MODULE)); + info!("Rust minimal sample (init)"); + info!("Am I built-in? {}", !cfg!(MODULE)); Ok(RustMinimal { message: "on the heap!".to_owned(), @@ -35,7 +35,7 @@ impl KernelModule for RustMinimal { impl Drop for RustMinimal { fn drop(&mut self) { - println!("My message is {}", self.message); - println!("Rust minimal sample (exit)"); + info!("My message is {}", self.message); + info!("Rust minimal sample (exit)"); } } diff --git a/samples/rust/rust_miscdev.rs b/samples/rust/rust_miscdev.rs index 6bdc280fc980b8d..45f71ce00950532 100644 --- a/samples/rust/rust_miscdev.rs +++ b/samples/rust/rust_miscdev.rs @@ -129,7 +129,7 @@ struct RustMiscdev { impl KernelModule for RustMiscdev { fn init() -> KernelResult { - println!("Rust miscellaneous device sample (init)"); + info!("Rust miscellaneous device sample (init)"); let state = SharedState::try_new()?; @@ -141,6 +141,6 @@ impl KernelModule for RustMiscdev { impl Drop for RustMiscdev { fn drop(&mut self) { - println!("Rust miscellaneous device sample (exit)"); + info!("Rust miscellaneous device sample (exit)"); } } diff --git a/samples/rust/rust_module_parameters.rs b/samples/rust/rust_module_parameters.rs index feb71ee8ea44093..a9d442f599792fe 100644 --- a/samples/rust/rust_module_parameters.rs +++ b/samples/rust/rust_module_parameters.rs @@ -47,19 +47,19 @@ struct RustModuleParameters; impl KernelModule for RustModuleParameters { fn init() -> KernelResult { - println!("Rust module parameters sample (init)"); + info!("Rust module parameters sample (init)"); { let lock = THIS_MODULE.kernel_param_lock(); - println!("Parameters:"); - println!(" my_bool: {}", my_bool.read()); - println!(" my_i32: {}", my_i32.read(&lock)); - println!( + info!("Parameters:"); + info!(" my_bool: {}", my_bool.read()); + info!(" my_i32: {}", my_i32.read(&lock)); + info!( " my_str: {}", core::str::from_utf8(my_str.read(&lock))? ); - println!(" my_usize: {}", my_usize.read(&lock)); - println!(" my_array: {:?}", my_array.read()); + info!(" my_usize: {}", my_usize.read(&lock)); + info!(" my_array: {:?}", my_array.read()); } Ok(RustModuleParameters) @@ -68,6 +68,6 @@ impl KernelModule for RustModuleParameters { impl Drop for RustModuleParameters { fn drop(&mut self) { - println!("Rust module parameters sample (exit)"); + info!("Rust module parameters sample (exit)"); } } diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs new file mode 100644 index 000000000000000..9f11e8722df6c54 --- /dev/null +++ b/samples/rust/rust_print.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust printing macros sample + +#![no_std] +#![feature(allocator_api, global_asm)] + +use kernel::prelude::*; + +module! { + type: RustPrint, + name: b"rust_print", + author: b"Rust for Linux Contributors", + description: b"Rust printing macros sample", + license: b"GPL v2", + params: { + }, +} + +struct RustPrint; + +impl KernelModule for RustPrint { + fn init() -> KernelResult { + info!("Rust printing macros sample (init)"); + + emerg!("Emergency message (level 0) with newline w/o args"); + alert!("Alert message (level 1) with newline w/o args"); + crit!("Critical message (level 2) with newline w/o args"); + err!("Error message (level 3) with newline w/o args"); + warn!("Warning message (level 4) with newline w/o args"); + notice!("Notice message (level 5) with newline w/o args"); + info!("Info message (level 6) with newline w/o args"); + + pr_info!("A line that"); + pr_cont!(" is continued"); + cont!(" with newline w/o args"); + + pr_emerg!("Emergency message (level 0) w/o newline w/o args\n"); + pr_alert!("Alert message (level 1) w/o newline w/o args\n"); + pr_crit!("Critical message (level 2) w/o newline w/o args\n"); + pr_err!("Error message (level 3) w/o newline w/o args\n"); + pr_warn!("Warning message (level 4) w/o newline w/o args\n"); + pr_notice!("Notice message (level 5) w/o newline w/o args\n"); + pr_info!("Info message (level 6) w/o newline w/o args\n"); + + pr_info!("A line that"); + pr_cont!(" is continued"); + pr_cont!(" w/o newline w/o args\n"); + + emerg!( + "{} message (level {}) with newline with args", + "Emergency", + 0 + ); + alert!("{} message (level {}) with newline with args", "Alert", 1); + crit!( + "{} message (level {}) with newline with args", + "Critical", + 2 + ); + err!("{} message (level {}) with newline with args", "Error", 3); + warn!("{} message (level {}) with newline with args", "Warning", 4); + notice!("{} message (level {}) with newline with args", "Notice", 5); + info!("{} message (level {}) with newline with args", "Info", 6); + + pr_info!("A {} that", "line"); + pr_cont!(" is {}", "continued"); + cont!(" with {} with args", "newline"); + + pr_emerg!( + "{} message (level {}) w/o newline with args\n", + "Emergency", + 0 + ); + pr_alert!("{} message (level {}) w/o newline with args\n", "Alert", 1); + pr_crit!( + "{} message (level {}) w/o newline with args\n", + "Critical", + 2 + ); + pr_err!("{} message (level {}) w/o newline with args\n", "Error", 3); + pr_warn!( + "{} message (level {}) w/o newline with args\n", + "Warning", + 4 + ); + pr_notice!("{} message (level {}) w/o newline with args\n", "Notice", 5); + pr_info!("{} message (level {}) w/o newline with args\n", "Info", 6); + + pr_info!("A {} that", "line"); + pr_cont!(" is {}", "continued"); + pr_cont!(" w/o {} with args\n", "newline"); + + Ok(RustPrint) + } +} + +impl Drop for RustPrint { + fn drop(&mut self) { + info!("Rust printing macros sample (exit)"); + } +} diff --git a/samples/rust/rust_stack_probing.rs b/samples/rust/rust_stack_probing.rs index 3d5608c0fb73c76..2f6fe2c3aece9e6 100644 --- a/samples/rust/rust_stack_probing.rs +++ b/samples/rust/rust_stack_probing.rs @@ -22,14 +22,14 @@ struct RustStackProbing; impl KernelModule for RustStackProbing { fn init() -> KernelResult { - println!("Rust stack probing sample (init)"); + info!("Rust stack probing sample (init)"); // Including this large variable on the stack will trigger // stack probing on the supported archs. // This will verify that stack probing does not lead to // any errors if we need to link `__rust_probestack`. let x: [u64; 514] = core::hint::black_box([5; 514]); - println!("Large array has length: {}", x.len()); + info!("Large array has length: {}", x.len()); Ok(RustStackProbing) } @@ -37,6 +37,6 @@ impl KernelModule for RustStackProbing { impl Drop for RustStackProbing { fn drop(&mut self) { - println!("Rust stack probing sample (exit)"); + info!("Rust stack probing sample (exit)"); } } diff --git a/samples/rust/rust_sync.rs b/samples/rust/rust_sync.rs index 8f72c411de2b94c..26dbf05bcf34dcf 100644 --- a/samples/rust/rust_sync.rs +++ b/samples/rust/rust_sync.rs @@ -28,7 +28,7 @@ struct RustSync; impl KernelModule for RustSync { fn init() -> KernelResult { - println!("Rust synchronisation primitives sample (init)"); + info!("Rust synchronisation primitives sample (init)"); // Test mutexes. { @@ -36,7 +36,7 @@ impl KernelModule for RustSync { let data = Pin::from(Box::try_new(unsafe { Mutex::new(0) })?); mutex_init!(data.as_ref(), "RustSync::init::data1"); *data.lock() = 10; - println!("Value: {}", *data.lock()); + info!("Value: {}", *data.lock()); // SAFETY: `init` is called below. let cv = Pin::from(Box::try_new(unsafe { CondVar::new() })?); @@ -58,7 +58,7 @@ impl KernelModule for RustSync { let data = Pin::from(Box::try_new(unsafe { SpinLock::new(0) })?); spinlock_init!(data.as_ref(), "RustSync::init::data2"); *data.lock() = 10; - println!("Value: {}", *data.lock()); + info!("Value: {}", *data.lock()); // SAFETY: `init` is called below. let cv = Pin::from(Box::try_new(unsafe { CondVar::new() })?); @@ -80,6 +80,6 @@ impl KernelModule for RustSync { impl Drop for RustSync { fn drop(&mut self) { - println!("Rust synchronisation primitives sample (exit)"); + info!("Rust synchronisation primitives sample (exit)"); } }