Commit 96f42635 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rust-6.2' of https://github.com/Rust-for-Linux/linux

Pull rust updates from Miguel Ojeda:
 "The first set of changes after the merge, the major ones being:

   - String and formatting: new types 'CString', 'CStr', 'BStr' and
     'Formatter'; new macros 'c_str!', 'b_str!' and 'fmt!'.

   - Errors: the rest of the error codes from 'errno-base.h', as well as
     some 'From' trait implementations for the 'Error' type.

   - Printing: the rest of the 'pr_*!' levels and the continuation one
     'pr_cont!', as well as a new sample.

   - 'alloc' crate: new constructors 'try_with_capacity()' and
     'try_with_capacity_in()' for 'RawVec' and 'Vec'.

   - Procedural macros: new macros '#[vtable]' and 'concat_idents!', as
     well as better ergonomics for 'module!' users.

   - Asserting: new macros 'static_assert!', 'build_error!' and
     'build_assert!', as well as a new crate 'build_error' to support
     them.

   - Vocabulary types: new types 'Opaque' and 'Either'.

   - Debugging: new macro 'dbg!'"

* tag 'rust-6.2' of https://github.com/Rust-for-Linux/linux: (28 commits)
  rust: types: add `Opaque` type
  rust: types: add `Either` type
  rust: build_assert: add `build_{error,assert}!` macros
  rust: add `build_error` crate
  rust: static_assert: add `static_assert!` macro
  rust: std_vendor: add `dbg!` macro based on `std`'s one
  rust: str: add `fmt!` macro
  rust: str: add `CString` type
  rust: str: add `Formatter` type
  rust: str: add `c_str!` macro
  rust: str: add `CStr` unit tests
  rust: str: implement several traits for `CStr`
  rust: str: add `CStr` type
  rust: str: add `b_str!` macro
  rust: str: add `BStr` type
  rust: alloc: add `Vec::try_with_capacity{,_in}()` constructors
  rust: alloc: add `RawVec::try_with_capacity_in()` constructor
  rust: prelude: add `error::code::*` constant items
  rust: error: add `From` implementations for `Error`
  rust: error: add codes from `errno-base.h`
  ...
parents eb451153 b9ecf9b9
...@@ -2823,6 +2823,22 @@ config RUST_OVERFLOW_CHECKS ...@@ -2823,6 +2823,22 @@ config RUST_OVERFLOW_CHECKS
If unsure, say Y. If unsure, say Y.
config RUST_BUILD_ASSERT_ALLOW
bool "Allow unoptimized build-time assertions"
depends on RUST
help
Controls how are `build_error!` and `build_assert!` handled during build.
If calls to them exist in the binary, it may indicate a violated invariant
or that the optimizer failed to verify the invariant during compilation.
This should not happen, thus by default the build is aborted. However,
as an escape hatch, you can choose Y here to ignore them during build
and let the check be carried at runtime (with `panic!` being called if
the check fails).
If unsure, say N.
endmenu # "Rust" endmenu # "Rust"
source "Documentation/Kconfig" source "Documentation/Kconfig"
......
...@@ -19,6 +19,12 @@ obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o ...@@ -19,6 +19,12 @@ obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \ always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
exports_kernel_generated.h exports_kernel_generated.h
ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW
obj-$(CONFIG_RUST) += build_error.o
else
always-$(CONFIG_RUST) += build_error.o
endif
obj-$(CONFIG_RUST) += exports.o obj-$(CONFIG_RUST) += exports.o
# Avoids running `$(RUSTC)` for the sysroot when it may not be available. # Avoids running `$(RUSTC)` for the sysroot when it may not be available.
...@@ -108,7 +114,7 @@ rustdoc-alloc: $(src)/alloc/lib.rs rustdoc-core rustdoc-compiler_builtins FORCE ...@@ -108,7 +114,7 @@ rustdoc-alloc: $(src)/alloc/lib.rs rustdoc-core rustdoc-compiler_builtins FORCE
$(call if_changed,rustdoc) $(call if_changed,rustdoc)
rustdoc-kernel: private rustc_target_flags = --extern alloc \ rustdoc-kernel: private rustc_target_flags = --extern alloc \
--extern macros=$(objtree)/$(obj)/libmacros.so \ --extern build_error --extern macros=$(objtree)/$(obj)/libmacros.so \
--extern bindings --extern bindings
rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \ rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \
rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \ rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \
...@@ -126,6 +132,9 @@ quiet_cmd_rustc_test_library = RUSTC TL $< ...@@ -126,6 +132,9 @@ quiet_cmd_rustc_test_library = RUSTC TL $<
-L$(objtree)/$(obj)/test \ -L$(objtree)/$(obj)/test \
--crate-name $(subst rusttest-,,$(subst rusttestlib-,,$@)) $< --crate-name $(subst rusttest-,,$(subst rusttestlib-,,$@)) $<
rusttestlib-build_error: $(src)/build_error.rs rusttest-prepare FORCE
$(call if_changed,rustc_test_library)
rusttestlib-macros: private rustc_target_flags = --extern proc_macro rusttestlib-macros: private rustc_target_flags = --extern proc_macro
rusttestlib-macros: private rustc_test_library_proc = yes rusttestlib-macros: private rustc_test_library_proc = yes
rusttestlib-macros: $(src)/macros/lib.rs rusttest-prepare FORCE rusttestlib-macros: $(src)/macros/lib.rs rusttest-prepare FORCE
...@@ -216,9 +225,9 @@ rusttest-macros: $(src)/macros/lib.rs rusttest-prepare FORCE ...@@ -216,9 +225,9 @@ rusttest-macros: $(src)/macros/lib.rs rusttest-prepare FORCE
$(call if_changed,rustdoc_test) $(call if_changed,rustdoc_test)
rusttest-kernel: private rustc_target_flags = --extern alloc \ rusttest-kernel: private rustc_target_flags = --extern alloc \
--extern macros --extern bindings --extern build_error --extern macros --extern bindings
rusttest-kernel: $(src)/kernel/lib.rs rusttest-prepare \ rusttest-kernel: $(src)/kernel/lib.rs rusttest-prepare \
rusttestlib-macros rusttestlib-bindings FORCE rusttestlib-build_error rusttestlib-macros rusttestlib-bindings FORCE
$(call if_changed,rustc_test) $(call if_changed,rustc_test)
$(call if_changed,rustc_test_library) $(call if_changed,rustc_test_library)
...@@ -366,6 +375,9 @@ $(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs) ...@@ -366,6 +375,9 @@ $(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs)
$(obj)/alloc.o: $(src)/alloc/lib.rs $(obj)/compiler_builtins.o FORCE $(obj)/alloc.o: $(src)/alloc/lib.rs $(obj)/compiler_builtins.o FORCE
$(call if_changed_dep,rustc_library) $(call if_changed_dep,rustc_library)
$(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE
$(call if_changed_dep,rustc_library)
$(obj)/bindings.o: $(src)/bindings/lib.rs \ $(obj)/bindings.o: $(src)/bindings/lib.rs \
$(obj)/compiler_builtins.o \ $(obj)/compiler_builtins.o \
$(obj)/bindings/bindings_generated.rs \ $(obj)/bindings/bindings_generated.rs \
...@@ -373,8 +385,8 @@ $(obj)/bindings.o: $(src)/bindings/lib.rs \ ...@@ -373,8 +385,8 @@ $(obj)/bindings.o: $(src)/bindings/lib.rs \
$(call if_changed_dep,rustc_library) $(call if_changed_dep,rustc_library)
$(obj)/kernel.o: private rustc_target_flags = --extern alloc \ $(obj)/kernel.o: private rustc_target_flags = --extern alloc \
--extern macros --extern bindings --extern build_error --extern macros --extern bindings
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o \ $(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \
$(obj)/libmacros.so $(obj)/bindings.o FORCE $(obj)/libmacros.so $(obj)/bindings.o FORCE
$(call if_changed_dep,rustc_library) $(call if_changed_dep,rustc_library)
......
...@@ -20,11 +20,11 @@ ...@@ -20,11 +20,11 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
#[cfg(not(no_global_oom_handling))]
enum AllocInit { enum AllocInit {
/// The contents of the new memory are uninitialized. /// The contents of the new memory are uninitialized.
Uninitialized, Uninitialized,
/// The new memory is guaranteed to be zeroed. /// The new memory is guaranteed to be zeroed.
#[allow(dead_code)]
Zeroed, Zeroed,
} }
...@@ -133,6 +133,13 @@ pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { ...@@ -133,6 +133,13 @@ pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
Self::allocate_in(capacity, AllocInit::Uninitialized, alloc) Self::allocate_in(capacity, AllocInit::Uninitialized, alloc)
} }
/// Like `try_with_capacity`, but parameterized over the choice of
/// allocator for the returned `RawVec`.
#[inline]
pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)
}
/// Like `with_capacity_zeroed`, but parameterized over the choice /// Like `with_capacity_zeroed`, but parameterized over the choice
/// of allocator for the returned `RawVec`. /// of allocator for the returned `RawVec`.
#[cfg(not(no_global_oom_handling))] #[cfg(not(no_global_oom_handling))]
...@@ -203,6 +210,30 @@ fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self { ...@@ -203,6 +210,30 @@ fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self {
} }
} }
fn try_allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Result<Self, TryReserveError> {
// Don't allocate here because `Drop` will not deallocate when `capacity` is 0.
if mem::size_of::<T>() == 0 || capacity == 0 {
return Ok(Self::new_in(alloc));
}
let layout = Layout::array::<T>(capacity).map_err(|_| CapacityOverflow)?;
alloc_guard(layout.size())?;
let result = match init {
AllocInit::Uninitialized => alloc.allocate(layout),
AllocInit::Zeroed => alloc.allocate_zeroed(layout),
};
let ptr = result.map_err(|_| AllocError { layout, non_exhaustive: () })?;
// Allocators currently return a `NonNull<[u8]>` whose length
// matches the size requested. If that ever changes, the capacity
// here should change to `ptr.len() / mem::size_of::<T>()`.
Ok(Self {
ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) },
cap: capacity,
alloc,
})
}
/// Reconstitutes a `RawVec` from a pointer, capacity, and allocator. /// Reconstitutes a `RawVec` from a pointer, capacity, and allocator.
/// ///
/// # Safety /// # Safety
......
...@@ -472,6 +472,48 @@ pub fn with_capacity(capacity: usize) -> Self { ...@@ -472,6 +472,48 @@ pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_in(capacity, Global) Self::with_capacity_in(capacity, Global)
} }
/// Tries to construct a new, empty `Vec<T>` with the specified capacity.
///
/// The vector will be able to hold exactly `capacity` elements without
/// reallocating. If `capacity` is 0, the vector will not allocate.
///
/// It is important to note that although the returned vector has the
/// *capacity* specified, the vector will have a zero *length*. For an
/// explanation of the difference between length and capacity, see
/// *[Capacity and reallocation]*.
///
/// [Capacity and reallocation]: #capacity-and-reallocation
///
/// # Examples
///
/// ```
/// let mut vec = Vec::try_with_capacity(10).unwrap();
///
/// // The vector contains no items, even though it has capacity for more
/// assert_eq!(vec.len(), 0);
/// assert_eq!(vec.capacity(), 10);
///
/// // These are all done without reallocating...
/// for i in 0..10 {
/// vec.push(i);
/// }
/// assert_eq!(vec.len(), 10);
/// assert_eq!(vec.capacity(), 10);
///
/// // ...but this may make the vector reallocate
/// vec.push(11);
/// assert_eq!(vec.len(), 11);
/// assert!(vec.capacity() >= 11);
///
/// let mut result = Vec::try_with_capacity(usize::MAX);
/// assert!(result.is_err());
/// ```
#[inline]
#[stable(feature = "kernel", since = "1.0.0")]
pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
Self::try_with_capacity_in(capacity, Global)
}
/// Creates a `Vec<T>` directly from the raw components of another vector. /// Creates a `Vec<T>` directly from the raw components of another vector.
/// ///
/// # Safety /// # Safety
...@@ -617,6 +659,53 @@ pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { ...@@ -617,6 +659,53 @@ pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 }
} }
/// Tries to construct a new, empty `Vec<T, A>` with the specified capacity
/// with the provided allocator.
///
/// The vector will be able to hold exactly `capacity` elements without
/// reallocating. If `capacity` is 0, the vector will not allocate.
///
/// It is important to note that although the returned vector has the
/// *capacity* specified, the vector will have a zero *length*. For an
/// explanation of the difference between length and capacity, see
/// *[Capacity and reallocation]*.
///
/// [Capacity and reallocation]: #capacity-and-reallocation
///
/// # Examples
///
/// ```
/// #![feature(allocator_api)]
///
/// use std::alloc::System;
///
/// let mut vec = Vec::try_with_capacity_in(10, System).unwrap();
///
/// // The vector contains no items, even though it has capacity for more
/// assert_eq!(vec.len(), 0);
/// assert_eq!(vec.capacity(), 10);
///
/// // These are all done without reallocating...
/// for i in 0..10 {
/// vec.push(i);
/// }
/// assert_eq!(vec.len(), 10);
/// assert_eq!(vec.capacity(), 10);
///
/// // ...but this may make the vector reallocate
/// vec.push(11);
/// assert_eq!(vec.len(), 11);
/// assert!(vec.capacity() >= 11);
///
/// let mut result = Vec::try_with_capacity_in(usize::MAX, System);
/// assert!(result.is_err());
/// ```
#[inline]
#[stable(feature = "kernel", since = "1.0.0")]
pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 })
}
/// Creates a `Vec<T, A>` directly from the raw components of another vector. /// Creates a `Vec<T, A>` directly from the raw components of another vector.
/// ///
/// # Safety /// # Safety
......
// SPDX-License-Identifier: GPL-2.0
//! Build-time error.
//!
//! This crate provides a [const function][const-functions] `build_error`, which will panic in
//! compile-time if executed in [const context][const-context], and will cause a build error
//! if not executed at compile time and the optimizer does not optimise away the call.
//!
//! It is used by `build_assert!` in the kernel crate, allowing checking of
//! conditions that could be checked statically, but could not be enforced in
//! Rust yet (e.g. perform some checks in [const functions][const-functions], but those
//! functions could still be called in the runtime).
//!
//! For details on constant evaluation in Rust, please see the [Reference][const-eval].
//!
//! [const-eval]: https://doc.rust-lang.org/reference/const_eval.html
//! [const-functions]: https://doc.rust-lang.org/reference/const_eval.html#const-functions
//! [const-context]: https://doc.rust-lang.org/reference/const_eval.html#const-context
#![no_std]
/// Panics if executed in [const context][const-context], or triggers a build error if not.
///
/// [const-context]: https://doc.rust-lang.org/reference/const_eval.html#const-context
#[inline(never)]
#[cold]
#[export_name = "rust_build_error"]
#[track_caller]
pub const fn build_error(msg: &'static str) -> ! {
panic!("{}", msg);
}
...@@ -19,3 +19,8 @@ ...@@ -19,3 +19,8 @@
#include "exports_alloc_generated.h" #include "exports_alloc_generated.h"
#include "exports_bindings_generated.h" #include "exports_bindings_generated.h"
#include "exports_kernel_generated.h" #include "exports_kernel_generated.h"
// For modules using `rust/build_error.rs`.
#ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW
EXPORT_SYMBOL_RUST_GPL(rust_build_error);
#endif
// SPDX-License-Identifier: GPL-2.0
//! Build-time assert.
/// Fails the build if the code path calling `build_error!` can possibly be executed.
///
/// If the macro is executed in const context, `build_error!` will panic.
/// If the compiler or optimizer cannot guarantee that `build_error!` can never
/// be called, a build error will be triggered.
///
/// # Examples
///
/// ```
/// # use kernel::build_error;
/// #[inline]
/// fn foo(a: usize) -> usize {
/// a.checked_add(1).unwrap_or_else(|| build_error!("overflow"))
/// }
///
/// assert_eq!(foo(usize::MAX - 1), usize::MAX); // OK.
/// // foo(usize::MAX); // Fails to compile.
/// ```
#[macro_export]
macro_rules! build_error {
() => {{
$crate::build_error("")
}};
($msg:expr) => {{
$crate::build_error($msg)
}};
}
/// Asserts that a boolean expression is `true` at compile time.
///
/// If the condition is evaluated to `false` in const context, `build_assert!`
/// will panic. If the compiler or optimizer cannot guarantee the condition will
/// be evaluated to `true`, a build error will be triggered.
///
/// [`static_assert!`] should be preferred to `build_assert!` whenever possible.
///
/// # Examples
///
/// These examples show that different types of [`assert!`] will trigger errors
/// at different stage of compilation. It is preferred to err as early as
/// possible, so [`static_assert!`] should be used whenever possible.
/// ```ignore
/// fn foo() {
/// static_assert!(1 > 1); // Compile-time error
/// build_assert!(1 > 1); // Build-time error
/// assert!(1 > 1); // Run-time error
/// }
/// ```
///
/// When the condition refers to generic parameters or parameters of an inline function,
/// [`static_assert!`] cannot be used. Use `build_assert!` in this scenario.
/// ```
/// fn foo<const N: usize>() {
/// // `static_assert!(N > 1);` is not allowed
/// build_assert!(N > 1); // Build-time check
/// assert!(N > 1); // Run-time check
/// }
///
/// #[inline]
/// fn bar(n: usize) {
/// // `static_assert!(n > 1);` is not allowed
/// build_assert!(n > 1); // Build-time check
/// assert!(n > 1); // Run-time check
/// }
/// ```
#[macro_export]
macro_rules! build_assert {
($cond:expr $(,)?) => {{
if !$cond {
$crate::build_error(concat!("assertion failed: ", stringify!($cond)));
}
}};
($cond:expr, $msg:expr) => {{
if !$cond {
$crate::build_error($msg);
}
}};
}
...@@ -4,12 +4,60 @@ ...@@ -4,12 +4,60 @@
//! //!
//! C header: [`include/uapi/asm-generic/errno-base.h`](../../../include/uapi/asm-generic/errno-base.h) //! C header: [`include/uapi/asm-generic/errno-base.h`](../../../include/uapi/asm-generic/errno-base.h)
use alloc::collections::TryReserveError; use alloc::{
alloc::{AllocError, LayoutError},
collections::TryReserveError,
};
use core::convert::From;
use core::num::TryFromIntError;
use core::str::Utf8Error;
/// Contains the C-compatible error codes. /// Contains the C-compatible error codes.
pub mod code { pub mod code {
/// Out of memory. macro_rules! declare_err {
pub const ENOMEM: super::Error = super::Error(-(crate::bindings::ENOMEM as i32)); ($err:tt $(,)? $($doc:expr),+) => {
$(
#[doc = $doc]
)*
pub const $err: super::Error = super::Error(-(crate::bindings::$err as i32));
};
}
declare_err!(EPERM, "Operation not permitted.");
declare_err!(ENOENT, "No such file or directory.");
declare_err!(ESRCH, "No such process.");
declare_err!(EINTR, "Interrupted system call.");
declare_err!(EIO, "I/O error.");
declare_err!(ENXIO, "No such device or address.");
declare_err!(E2BIG, "Argument list too long.");
declare_err!(ENOEXEC, "Exec format error.");
declare_err!(EBADF, "Bad file number.");
declare_err!(ECHILD, "Exec format error.");
declare_err!(EAGAIN, "Try again.");
declare_err!(ENOMEM, "Out of memory.");
declare_err!(EACCES, "Permission denied.");
declare_err!(EFAULT, "Bad address.");
declare_err!(ENOTBLK, "Block device required.");
declare_err!(EBUSY, "Device or resource busy.");
declare_err!(EEXIST, "File exists.");
declare_err!(EXDEV, "Cross-device link.");
declare_err!(ENODEV, "No such device.");
declare_err!(ENOTDIR, "Not a directory.");
declare_err!(EISDIR, "Is a directory.");
declare_err!(EINVAL, "Invalid argument.");
declare_err!(ENFILE, "File table overflow.");
declare_err!(EMFILE, "Too many open files.");
declare_err!(ENOTTY, "Not a typewriter.");
declare_err!(ETXTBSY, "Text file busy.");
declare_err!(EFBIG, "File too large.");
declare_err!(ENOSPC, "No space left on device.");
declare_err!(ESPIPE, "Illegal seek.");
declare_err!(EROFS, "Read-only file system.");
declare_err!(EMLINK, "Too many links.");
declare_err!(EPIPE, "Broken pipe.");
declare_err!(EDOM, "Math argument out of domain of func.");
declare_err!(ERANGE, "Math result not representable.");
} }
/// Generic integer kernel error. /// Generic integer kernel error.
...@@ -30,12 +78,48 @@ pub fn to_kernel_errno(self) -> core::ffi::c_int { ...@@ -30,12 +78,48 @@ pub fn to_kernel_errno(self) -> core::ffi::c_int {
} }
} }
impl From<AllocError> for Error {
fn from(_: AllocError) -> Error {
code::ENOMEM
}
}
impl From<TryFromIntError> for Error {
fn from(_: TryFromIntError) -> Error {
code::EINVAL
}
}
impl From<Utf8Error> for Error {
fn from(_: Utf8Error) -> Error {
code::EINVAL
}
}
impl From<TryReserveError> for Error { impl From<TryReserveError> for Error {
fn from(_: TryReserveError) -> Error { fn from(_: TryReserveError) -> Error {
code::ENOMEM code::ENOMEM
} }
} }
impl From<LayoutError> for Error {
fn from(_: LayoutError) -> Error {
code::ENOMEM
}
}
impl From<core::fmt::Error> for Error {
fn from(_: core::fmt::Error) -> Error {
code::EINVAL
}
}
impl From<core::convert::Infallible> for Error {
fn from(e: core::convert::Infallible) -> Error {
match e {}
}
}
/// A [`Result`] with an [`Error`] error type. /// A [`Result`] with an [`Error`] error type.
/// ///
/// To be used as the return type for functions that may fail. /// To be used as the return type for functions that may fail.
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
//! do so first instead of bypassing this crate. //! do so first instead of bypassing this crate.
#![no_std] #![no_std]
#![feature(allocator_api)]
#![feature(core_ffi_c)] #![feature(core_ffi_c)]
// Ensure conditional compilation based on the kernel configuration works; // Ensure conditional compilation based on the kernel configuration works;
...@@ -22,15 +23,23 @@ ...@@ -22,15 +23,23 @@
#[cfg(not(test))] #[cfg(not(test))]
#[cfg(not(testlib))] #[cfg(not(testlib))]
mod allocator; mod allocator;
mod build_assert;
pub mod error; pub mod error;
pub mod prelude; pub mod prelude;
pub mod print; pub mod print;
mod static_assert;
#[doc(hidden)]
pub mod std_vendor;
pub mod str; pub mod str;
pub mod types;
#[doc(hidden)] #[doc(hidden)]
pub use bindings; pub use bindings;
pub use macros; pub use macros;
#[doc(hidden)]
pub use build_error::build_error;
/// Prefix to appear before log messages printed from within the `kernel` crate. /// Prefix to appear before log messages printed from within the `kernel` crate.
const __LOG_PREFIX: &[u8] = b"rust_kernel\0"; const __LOG_PREFIX: &[u8] = b"rust_kernel\0";
......
...@@ -11,10 +11,18 @@ ...@@ -11,10 +11,18 @@
//! use kernel::prelude::*; //! use kernel::prelude::*;
//! ``` //! ```
pub use super::{
error::{Error, Result},
pr_emerg, pr_info, ThisModule,
};
pub use alloc::{boxed::Box, vec::Vec};
pub use core::pin::Pin; pub use core::pin::Pin;
pub use macros::module;
pub use alloc::{boxed::Box, vec::Vec};
pub use macros::{module, vtable};
pub use super::build_assert;
pub use super::{dbg, pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
pub use super::static_assert;
pub use super::error::{code::*, Error, Result};
pub use super::{str::CStr, ThisModule};
...@@ -74,7 +74,14 @@ pub mod format_strings { ...@@ -74,7 +74,14 @@ pub mod format_strings {
// Furthermore, `static` instead of `const` is used to share the strings // Furthermore, `static` instead of `const` is used to share the strings
// for all the kernel. // for all the kernel.
pub static EMERG: [u8; LENGTH] = generate(false, bindings::KERN_EMERG); 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 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`]. /// Prints a message via the kernel's [`_printk`].
...@@ -105,6 +112,26 @@ pub unsafe fn call_printk( ...@@ -105,6 +112,26 @@ pub unsafe fn call_printk(
} }
} }
/// 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)]
#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
pub fn call_printk_cont(args: fmt::Arguments<'_>) {
// `_printk` does not seem to fail in any path.
//
// SAFETY: The format string is fixed.
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_printk(
format_strings::CONT.as_ptr() as _,
&args as *const _ as *const c_void,
);
}
}
/// Performs formatting and forwards the string to [`call_printk`]. /// Performs formatting and forwards the string to [`call_printk`].
/// ///
/// Public but hidden since it should only be used from public macros. /// Public but hidden since it should only be used from public macros.
...@@ -114,7 +141,7 @@ pub unsafe fn call_printk( ...@@ -114,7 +141,7 @@ pub unsafe fn call_printk(
#[allow(clippy::crate_in_macro_def)] #[allow(clippy::crate_in_macro_def)]
macro_rules! print_macro ( macro_rules! print_macro (
// The non-continuation cases (most of them, e.g. `INFO`). // The non-continuation cases (most of them, e.g. `INFO`).
($format_string:path, $($arg:tt)+) => ( ($format_string:path, false, $($arg:tt)+) => (
// SAFETY: This hidden macro should only be called by the documented // SAFETY: This hidden macro should only be called by the documented
// printing macros which ensure the format string is one of the fixed // printing macros which ensure the format string is one of the fixed
// ones. All `__LOG_PREFIX`s are null-terminated as they are generated // ones. All `__LOG_PREFIX`s are null-terminated as they are generated
...@@ -128,6 +155,13 @@ macro_rules! print_macro ( ...@@ -128,6 +155,13 @@ macro_rules! print_macro (
); );
} }
); );
// The `CONT` case.
($format_string:path, true, $($arg:tt)+) => (
$crate::print::call_printk_cont(
format_args!($($arg)+),
);
);
); );
/// Stub for doctests /// Stub for doctests
...@@ -168,7 +202,127 @@ macro_rules! print_macro ( ...@@ -168,7 +202,127 @@ macro_rules! print_macro (
#[macro_export] #[macro_export]
macro_rules! pr_emerg ( macro_rules! pr_emerg (
($($arg:tt)*) => ( ($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::EMERG, $($arg)*) $crate::print_macro!($crate::print::format_strings::EMERG, false, $($arg)*)
)
);
/// Prints an alert-level message (level 1).
///
/// 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 {}\n", "there");
/// ```
#[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 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 {}\n", "there");
/// ```
#[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 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 {}\n", "there");
/// ```
#[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 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 {}\n", "there");
/// ```
#[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 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 {}\n", "there");
/// ```
#[macro_export]
macro_rules! pr_notice (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::NOTICE, false, $($arg)*)
) )
); );
...@@ -193,6 +347,60 @@ macro_rules! pr_emerg ( ...@@ -193,6 +347,60 @@ macro_rules! pr_emerg (
#[doc(alias = "print")] #[doc(alias = "print")]
macro_rules! pr_info ( macro_rules! pr_info (
($($arg:tt)*) => ( ($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::INFO, $($arg)*) $crate::print_macro!($crate::print::format_strings::INFO, false, $($arg)*)
)
);
/// Prints a debug-level message (level 7).
///
/// Use this level for debug messages.
///
/// Equivalent to the kernel's [`pr_debug`] macro, except that it doesn't support dynamic debug
/// yet.
///
/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
/// `alloc::format!` for information about the formatting syntax.
///
/// [`pr_debug`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_debug
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// pr_debug!("hello {}\n", "there");
/// ```
#[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)*)
}
)
);
/// Continues a previous log message in the same line.
///
/// 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
///
/// ```
/// # use kernel::pr_cont;
/// pr_info!("hello");
/// pr_cont!(" {}\n", "there");
/// ```
#[macro_export]
macro_rules! pr_cont (
($($arg:tt)*) => (
$crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*)
) )
); );
// SPDX-License-Identifier: GPL-2.0
//! Static assert.
/// Static assert (i.e. compile-time assert).
///
/// Similar to C11 [`_Static_assert`] and C++11 [`static_assert`].
///
/// The feature may be added to Rust in the future: see [RFC 2790].
///
/// [`_Static_assert`]: https://en.cppreference.com/w/c/language/_Static_assert
/// [`static_assert`]: https://en.cppreference.com/w/cpp/language/static_assert
/// [RFC 2790]: https://github.com/rust-lang/rfcs/issues/2790
///
/// # Examples
///
/// ```
/// static_assert!(42 > 24);
/// static_assert!(core::mem::size_of::<u8>() == 1);
///
/// const X: &[u8] = b"bar";
/// static_assert!(X[1] == b'a');
///
/// const fn f(x: i32) -> i32 {
/// x + 2
/// }
/// static_assert!(f(40) == 42);
/// ```
#[macro_export]
macro_rules! static_assert {
($condition:expr) => {
const _: () = core::assert!($condition);
};
}
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! The contents of this file come from the Rust standard library, hosted in
//! the <https://github.com/rust-lang/rust> repository, licensed under
//! "Apache-2.0 OR MIT" and adapted for kernel use. For copyright details,
//! see <https://github.com/rust-lang/rust/blob/master/COPYRIGHT>.
/// [`std::dbg`], but using [`pr_info`] instead of [`eprintln`].
///
/// Prints and returns the value of a given expression for quick and dirty
/// debugging.
///
/// An example:
///
/// ```rust
/// let a = 2;
/// # #[allow(clippy::dbg_macro)]
/// let b = dbg!(a * 2) + 1;
/// // ^-- prints: [src/main.rs:2] a * 2 = 4
/// assert_eq!(b, 5);
/// ```
///
/// The macro works by using the `Debug` implementation of the type of
/// the given expression to print the value with [`printk`] along with the
/// source location of the macro invocation as well as the source code
/// of the expression.
///
/// Invoking the macro on an expression moves and takes ownership of it
/// before returning the evaluated expression unchanged. If the type
/// of the expression does not implement `Copy` and you don't want
/// to give up ownership, you can instead borrow with `dbg!(&expr)`
/// for some expression `expr`.
///
/// The `dbg!` macro works exactly the same in release builds.
/// This is useful when debugging issues that only occur in release
/// builds or when debugging in release mode is significantly faster.
///
/// Note that the macro is intended as a temporary debugging tool to be
/// used during development. Therefore, avoid committing `dbg!` macro
/// invocations into the kernel tree.
///
/// For debug output that is intended to be kept in the kernel tree,
/// use [`pr_debug`] and similar facilities instead.
///
/// # Stability
///
/// The exact output printed by this macro should not be relied upon
/// and is subject to future changes.
///
/// # Further examples
///
/// With a method call:
///
/// ```rust
/// # #[allow(clippy::dbg_macro)]
/// fn foo(n: usize) {
/// if dbg!(n.checked_sub(4)).is_some() {
/// // ...
/// }
/// }
///
/// foo(3)
/// ```
///
/// This prints to the kernel log:
///
/// ```text,ignore
/// [src/main.rs:4] n.checked_sub(4) = None
/// ```
///
/// Naive factorial implementation:
///
/// ```rust
/// # #[allow(clippy::dbg_macro)]
/// # {
/// fn factorial(n: u32) -> u32 {
/// if dbg!(n <= 1) {
/// dbg!(1)
/// } else {
/// dbg!(n * factorial(n - 1))
/// }
/// }
///
/// dbg!(factorial(4));
/// # }
/// ```
///
/// This prints to the kernel log:
///
/// ```text,ignore
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = false
/// [src/main.rs:3] n <= 1 = true
/// [src/main.rs:4] 1 = 1
/// [src/main.rs:5] n * factorial(n - 1) = 2
/// [src/main.rs:5] n * factorial(n - 1) = 6
/// [src/main.rs:5] n * factorial(n - 1) = 24
/// [src/main.rs:11] factorial(4) = 24
/// ```
///
/// The `dbg!(..)` macro moves the input:
///
/// ```ignore
/// /// A wrapper around `usize` which importantly is not Copyable.
/// #[derive(Debug)]
/// struct NoCopy(usize);
///
/// let a = NoCopy(42);
/// let _ = dbg!(a); // <-- `a` is moved here.
/// let _ = dbg!(a); // <-- `a` is moved again; error!
/// ```
///
/// You can also use `dbg!()` without a value to just print the
/// file and line whenever it's reached.
///
/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as
/// a tuple (and return it, too):
///
/// ```
/// # #[allow(clippy::dbg_macro)]
/// assert_eq!(dbg!(1usize, 2u32), (1, 2));
/// ```
///
/// However, a single argument with a trailing comma will still not be treated
/// as a tuple, following the convention of ignoring trailing commas in macro
/// invocations. You can use a 1-tuple directly if you need one:
///
/// ```
/// # #[allow(clippy::dbg_macro)]
/// # {
/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored
/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple
/// # }
/// ```
///
/// [`std::dbg`]: https://doc.rust-lang.org/std/macro.dbg.html
/// [`eprintln`]: https://doc.rust-lang.org/std/macro.eprintln.html
/// [`printk`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html
#[macro_export]
macro_rules! dbg {
// NOTE: We cannot use `concat!` to make a static string as a format argument
// of `pr_info!` because `file!` could contain a `{` or
// `$val` expression could be a block (`{ .. }`), in which case the `pr_info!`
// will be malformed.
() => {
$crate::pr_info!("[{}:{}]\n", ::core::file!(), ::core::line!())
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
$crate::pr_info!("[{}:{}] {} = {:#?}\n",
::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::dbg!($val)),+,)
};
}
...@@ -2,7 +2,377 @@ ...@@ -2,7 +2,377 @@
//! String representations. //! String representations.
use core::fmt; use alloc::vec::Vec;
use core::fmt::{self, Write};
use core::ops::{self, Deref, Index};
use crate::{
bindings,
error::{code::*, Error},
};
/// Byte string without UTF-8 validity guarantee.
///
/// `BStr` is simply an alias to `[u8]`, but has a more evident semantical meaning.
pub type BStr = [u8];
/// Creates a new [`BStr`] from a string literal.
///
/// `b_str!` converts the supplied string literal to byte string, so non-ASCII
/// characters can be included.
///
/// # Examples
///
/// ```
/// # use kernel::b_str;
/// # use kernel::str::BStr;
/// const MY_BSTR: &BStr = b_str!("My awesome BStr!");
/// ```
#[macro_export]
macro_rules! b_str {
($str:literal) => {{
const S: &'static str = $str;
const C: &'static $crate::str::BStr = S.as_bytes();
C
}};
}
/// Possible errors when using conversion functions in [`CStr`].
#[derive(Debug, Clone, Copy)]
pub enum CStrConvertError {
/// Supplied bytes contain an interior `NUL`.
InteriorNul,
/// Supplied bytes are not terminated by `NUL`.
NotNulTerminated,
}
impl From<CStrConvertError> for Error {
#[inline]
fn from(_: CStrConvertError) -> Error {
EINVAL
}
}
/// A string that is guaranteed to have exactly one `NUL` byte, which is at the
/// end.
///
/// Used for interoperability with kernel APIs that take C strings.
#[repr(transparent)]
pub struct CStr([u8]);
impl CStr {
/// Returns the length of this string excluding `NUL`.
#[inline]
pub const fn len(&self) -> usize {
self.len_with_nul() - 1
}
/// Returns the length of this string with `NUL`.
#[inline]
pub const fn len_with_nul(&self) -> usize {
// SAFETY: This is one of the invariant of `CStr`.
// We add a `unreachable_unchecked` here to hint the optimizer that
// the value returned from this function is non-zero.
if self.0.is_empty() {
unsafe { core::hint::unreachable_unchecked() };
}
self.0.len()
}
/// Returns `true` if the string only includes `NUL`.
#[inline]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
/// Wraps a raw C string pointer.
///
/// # Safety
///
/// `ptr` must be a valid pointer to a `NUL`-terminated C string, and it must
/// last at least `'a`. When `CStr` is alive, the memory pointed by `ptr`
/// must not be mutated.
#[inline]
pub unsafe fn from_char_ptr<'a>(ptr: *const core::ffi::c_char) -> &'a Self {
// SAFETY: The safety precondition guarantees `ptr` is a valid pointer
// to a `NUL`-terminated C string.
let len = unsafe { bindings::strlen(ptr) } + 1;
// SAFETY: Lifetime guaranteed by the safety precondition.
let bytes = unsafe { core::slice::from_raw_parts(ptr as _, len as _) };
// SAFETY: As `len` is returned by `strlen`, `bytes` does not contain interior `NUL`.
// As we have added 1 to `len`, the last byte is known to be `NUL`.
unsafe { Self::from_bytes_with_nul_unchecked(bytes) }
}
/// Creates a [`CStr`] from a `[u8]`.
///
/// The provided slice must be `NUL`-terminated, does not contain any
/// interior `NUL` bytes.
pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError> {
if bytes.is_empty() {
return Err(CStrConvertError::NotNulTerminated);
}
if bytes[bytes.len() - 1] != 0 {
return Err(CStrConvertError::NotNulTerminated);
}
let mut i = 0;
// `i + 1 < bytes.len()` allows LLVM to optimize away bounds checking,
// while it couldn't optimize away bounds checks for `i < bytes.len() - 1`.
while i + 1 < bytes.len() {
if bytes[i] == 0 {
return Err(CStrConvertError::InteriorNul);
}
i += 1;
}
// SAFETY: We just checked that all properties hold.
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
}
/// Creates a [`CStr`] from a `[u8]` without performing any additional
/// checks.
///
/// # Safety
///
/// `bytes` *must* end with a `NUL` byte, and should only have a single
/// `NUL` byte (or the string will be truncated).
#[inline]
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
// SAFETY: Properties of `bytes` guaranteed by the safety precondition.
unsafe { core::mem::transmute(bytes) }
}
/// Returns a C pointer to the string.
#[inline]
pub const fn as_char_ptr(&self) -> *const core::ffi::c_char {
self.0.as_ptr() as _
}
/// Convert the string to a byte slice without the trailing 0 byte.
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0[..self.len()]
}
/// Convert the string to a byte slice containing the trailing 0 byte.
#[inline]
pub const fn as_bytes_with_nul(&self) -> &[u8] {
&self.0
}
/// Yields a [`&str`] slice if the [`CStr`] contains valid UTF-8.
///
/// If the contents of the [`CStr`] are valid UTF-8 data, this
/// function will return the corresponding [`&str`] slice. Otherwise,
/// it will return an error with details of where UTF-8 validation failed.
///
/// # Examples
///
/// ```
/// # use kernel::str::CStr;
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").unwrap();
/// assert_eq!(cstr.to_str(), Ok("foo"));
/// ```
#[inline]
pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
core::str::from_utf8(self.as_bytes())
}
/// Unsafely convert this [`CStr`] into a [`&str`], without checking for
/// valid UTF-8.
///
/// # Safety
///
/// The contents must be valid UTF-8.
///
/// # Examples
///
/// ```
/// # use kernel::c_str;
/// # use kernel::str::CStr;
/// // SAFETY: String literals are guaranteed to be valid UTF-8
/// // by the Rust compiler.
/// let bar = c_str!("ツ");
/// assert_eq!(unsafe { bar.as_str_unchecked() }, "ツ");
/// ```
#[inline]
pub unsafe fn as_str_unchecked(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
}
}
impl fmt::Display for CStr {
/// Formats printable ASCII characters, escaping the rest.
///
/// ```
/// # use kernel::c_str;
/// # use kernel::str::CStr;
/// # use kernel::str::CString;
/// let penguin = c_str!("🐧");
/// let s = CString::try_from_fmt(fmt!("{}", penguin)).unwrap();
/// assert_eq!(s.as_bytes_with_nul(), "\\xf0\\x9f\\x90\\xa7\0".as_bytes());
///
/// let ascii = c_str!("so \"cool\"");
/// let s = CString::try_from_fmt(fmt!("{}", ascii)).unwrap();
/// assert_eq!(s.as_bytes_with_nul(), "so \"cool\"\0".as_bytes());
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &c in self.as_bytes() {
if (0x20..0x7f).contains(&c) {
// Printable character.
f.write_char(c as char)?;
} else {
write!(f, "\\x{:02x}", c)?;
}
}
Ok(())
}
}
impl fmt::Debug for CStr {
/// Formats printable ASCII characters with a double quote on either end, escaping the rest.
///
/// ```
/// # use kernel::c_str;
/// # use kernel::str::CStr;
/// # use kernel::str::CString;
/// let penguin = c_str!("🐧");
/// let s = CString::try_from_fmt(fmt!("{:?}", penguin)).unwrap();
/// assert_eq!(s.as_bytes_with_nul(), "\"\\xf0\\x9f\\x90\\xa7\"\0".as_bytes());
///
/// // Embedded double quotes are escaped.
/// let ascii = c_str!("so \"cool\"");
/// let s = CString::try_from_fmt(fmt!("{:?}", ascii)).unwrap();
/// assert_eq!(s.as_bytes_with_nul(), "\"so \\\"cool\\\"\"\0".as_bytes());
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\"")?;
for &c in self.as_bytes() {
match c {
// Printable characters.
b'\"' => f.write_str("\\\"")?,
0x20..=0x7e => f.write_char(c as char)?,
_ => write!(f, "\\x{:02x}", c)?,
}
}
f.write_str("\"")
}
}
impl AsRef<BStr> for CStr {
#[inline]
fn as_ref(&self) -> &BStr {
self.as_bytes()
}
}
impl Deref for CStr {
type Target = BStr;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
impl Index<ops::RangeFrom<usize>> for CStr {
type Output = CStr;
#[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &Self::Output {
// Delegate bounds checking to slice.
// Assign to _ to mute clippy's unnecessary operation warning.
let _ = &self.as_bytes()[index.start..];
// SAFETY: We just checked the bounds.
unsafe { Self::from_bytes_with_nul_unchecked(&self.0[index.start..]) }
}
}
impl Index<ops::RangeFull> for CStr {
type Output = CStr;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &Self::Output {
self
}
}
mod private {
use core::ops;
// Marker trait for index types that can be forward to `BStr`.
pub trait CStrIndex {}
impl CStrIndex for usize {}
impl CStrIndex for ops::Range<usize> {}
impl CStrIndex for ops::RangeInclusive<usize> {}
impl CStrIndex for ops::RangeToInclusive<usize> {}
}
impl<Idx> Index<Idx> for CStr
where
Idx: private::CStrIndex,
BStr: Index<Idx>,
{
type Output = <BStr as Index<Idx>>::Output;
#[inline]
fn index(&self, index: Idx) -> &Self::Output {
&self.as_bytes()[index]
}
}
/// Creates a new [`CStr`] from a string literal.
///
/// The string literal should not contain any `NUL` bytes.
///
/// # Examples
///
/// ```
/// # use kernel::c_str;
/// # use kernel::str::CStr;
/// const MY_CSTR: &CStr = c_str!("My awesome CStr!");
/// ```
#[macro_export]
macro_rules! c_str {
($str:expr) => {{
const S: &str = concat!($str, "\0");
const C: &$crate::str::CStr = match $crate::str::CStr::from_bytes_with_nul(S.as_bytes()) {
Ok(v) => v,
Err(_) => panic!("string contains interior NUL"),
};
C
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cstr_to_str() {
let good_bytes = b"\xf0\x9f\xa6\x80\0";
let checked_cstr = CStr::from_bytes_with_nul(good_bytes).unwrap();
let checked_str = checked_cstr.to_str().unwrap();
assert_eq!(checked_str, "🦀");
}
#[test]
#[should_panic]
fn test_cstr_to_str_panic() {
let bad_bytes = b"\xc3\x28\0";
let checked_cstr = CStr::from_bytes_with_nul(bad_bytes).unwrap();
checked_cstr.to_str().unwrap();
}
#[test]
fn test_cstr_as_str_unchecked() {
let good_bytes = b"\xf0\x9f\x90\xA7\0";
let checked_cstr = CStr::from_bytes_with_nul(good_bytes).unwrap();
let unchecked_str = unsafe { checked_cstr.as_str_unchecked() };
assert_eq!(unchecked_str, "🐧");
}
}
/// Allows formatting of [`fmt::Arguments`] into a raw buffer. /// Allows formatting of [`fmt::Arguments`] into a raw buffer.
/// ///
...@@ -15,13 +385,22 @@ ...@@ -15,13 +385,22 @@
/// is less than `end`. /// is less than `end`.
pub(crate) struct RawFormatter { pub(crate) struct RawFormatter {
// Use `usize` to use `saturating_*` functions. // Use `usize` to use `saturating_*` functions.
#[allow(dead_code)]
beg: usize, beg: usize,
pos: usize, pos: usize,
end: usize, end: usize,
} }
impl RawFormatter { impl RawFormatter {
/// Creates a new instance of [`RawFormatter`] with an empty buffer.
fn new() -> Self {
// INVARIANT: The buffer is empty, so the region that needs to be writable is empty.
Self {
beg: 0,
pos: 0,
end: 0,
}
}
/// Creates a new instance of [`RawFormatter`] with the given buffer pointers. /// Creates a new instance of [`RawFormatter`] with the given buffer pointers.
/// ///
/// # Safety /// # Safety
...@@ -37,12 +416,34 @@ pub(crate) unsafe fn from_ptrs(pos: *mut u8, end: *mut u8) -> Self { ...@@ -37,12 +416,34 @@ pub(crate) unsafe fn from_ptrs(pos: *mut u8, end: *mut u8) -> Self {
} }
} }
/// Creates a new instance of [`RawFormatter`] with the given buffer.
///
/// # Safety
///
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
/// for the lifetime of the returned [`RawFormatter`].
pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self {
let pos = buf as usize;
// INVARIANT: We ensure that `end` is never less then `buf`, and the safety requirements
// guarantees that the memory region is valid for writes.
Self {
pos,
beg: pos,
end: pos.saturating_add(len),
}
}
/// Returns the current insert position. /// Returns the current insert position.
/// ///
/// N.B. It may point to invalid memory. /// N.B. It may point to invalid memory.
pub(crate) fn pos(&self) -> *mut u8 { pub(crate) fn pos(&self) -> *mut u8 {
self.pos as _ self.pos as _
} }
/// Return the number of bytes written to the formatter.
pub(crate) fn bytes_written(&self) -> usize {
self.pos - self.beg
}
} }
impl fmt::Write for RawFormatter { impl fmt::Write for RawFormatter {
...@@ -70,3 +471,121 @@ fn write_str(&mut self, s: &str) -> fmt::Result { ...@@ -70,3 +471,121 @@ fn write_str(&mut self, s: &str) -> fmt::Result {
Ok(()) Ok(())
} }
} }
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
///
/// Fails if callers attempt to write more than will fit in the buffer.
pub(crate) struct Formatter(RawFormatter);
impl Formatter {
/// Creates a new instance of [`Formatter`] with the given buffer.
///
/// # Safety
///
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
/// for the lifetime of the returned [`Formatter`].
pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self {
// SAFETY: The safety requirements of this function satisfy those of the callee.
Self(unsafe { RawFormatter::from_buffer(buf, len) })
}
}
impl Deref for Formatter {
type Target = RawFormatter;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Write for Formatter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0.write_str(s)?;
// Fail the request if we go past the end of the buffer.
if self.0.pos > self.0.end {
Err(fmt::Error)
} else {
Ok(())
}
}
}
/// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end.
///
/// Used for interoperability with kernel APIs that take C strings.
///
/// # Invariants
///
/// The string is always `NUL`-terminated and contains no other `NUL` bytes.
///
/// # Examples
///
/// ```
/// use kernel::str::CString;
///
/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap();
/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes());
///
/// let tmp = "testing";
/// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123)).unwrap();
/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes());
///
/// // This fails because it has an embedded `NUL` byte.
/// let s = CString::try_from_fmt(fmt!("a\0b{}", 123));
/// assert_eq!(s.is_ok(), false);
/// ```
pub struct CString {
buf: Vec<u8>,
}
impl CString {
/// Creates an instance of [`CString`] from the given formatted arguments.
pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
// Calculate the size needed (formatted string plus `NUL` terminator).
let mut f = RawFormatter::new();
f.write_fmt(args)?;
f.write_str("\0")?;
let size = f.bytes_written();
// Allocate a vector with the required number of bytes, and write to it.
let mut buf = Vec::try_with_capacity(size)?;
// SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes.
let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) };
f.write_fmt(args)?;
f.write_str("\0")?;
// SAFETY: The number of bytes that can be written to `f` is bounded by `size`, which is
// `buf`'s capacity. The contents of the buffer have been initialised by writes to `f`.
unsafe { buf.set_len(f.bytes_written()) };
// Check that there are no `NUL` bytes before the end.
// SAFETY: The buffer is valid for read because `f.bytes_written()` is bounded by `size`
// (which the minimum buffer size) and is non-zero (we wrote at least the `NUL` terminator)
// so `f.bytes_written() - 1` doesn't underflow.
let ptr = unsafe { bindings::memchr(buf.as_ptr().cast(), 0, (f.bytes_written() - 1) as _) };
if !ptr.is_null() {
return Err(EINVAL);
}
// INVARIANT: We wrote the `NUL` terminator and checked above that no other `NUL` bytes
// exist in the buffer.
Ok(Self { buf })
}
}
impl Deref for CString {
type Target = CStr;
fn deref(&self) -> &Self::Target {
// SAFETY: The type invariants guarantee that the string is `NUL`-terminated and that no
// other `NUL` bytes exist.
unsafe { CStr::from_bytes_with_nul_unchecked(self.buf.as_slice()) }
}
}
/// A convenience alias for [`core::format_args`].
#[macro_export]
macro_rules! fmt {
($($f:tt)*) => ( core::format_args!($($f)*) )
}
// SPDX-License-Identifier: GPL-2.0
//! Kernel types.
use core::{cell::UnsafeCell, mem::MaybeUninit};
/// Stores an opaque value.
///
/// This is meant to be used with FFI objects that are never interpreted by Rust code.
#[repr(transparent)]
pub struct Opaque<T>(MaybeUninit<UnsafeCell<T>>);
impl<T> Opaque<T> {
/// Creates a new opaque value.
pub const fn new(value: T) -> Self {
Self(MaybeUninit::new(UnsafeCell::new(value)))
}
/// Creates an uninitialised value.
pub const fn uninit() -> Self {
Self(MaybeUninit::uninit())
}
/// Returns a raw pointer to the opaque data.
pub fn get(&self) -> *mut T {
UnsafeCell::raw_get(self.0.as_ptr())
}
}
/// A sum type that always holds either a value of type `L` or `R`.
pub enum Either<L, R> {
/// Constructs an instance of [`Either`] containing a value of type `L`.
Left(L),
/// Constructs an instance of [`Either`] containing a value of type `R`.
Right(R),
}
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{token_stream, Ident, TokenStream, TokenTree};
use crate::helpers::expect_punct;
fn expect_ident(it: &mut token_stream::IntoIter) -> Ident {
if let Some(TokenTree::Ident(ident)) = it.next() {
ident
} else {
panic!("Expected Ident")
}
}
pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream {
let mut it = ts.into_iter();
let a = expect_ident(&mut it);
assert_eq!(expect_punct(&mut it), ',');
let b = expect_ident(&mut it);
assert!(it.next().is_none(), "only two idents can be concatenated");
let res = Ident::new(&format!("{a}{b}"), b.span());
TokenStream::from_iter([TokenTree::Ident(res)])
}
...@@ -18,10 +18,16 @@ pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> { ...@@ -18,10 +18,16 @@ pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
} }
} }
pub(crate) fn try_byte_string(it: &mut token_stream::IntoIter) -> Option<String> { pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option<String> {
try_literal(it).and_then(|byte_string| { try_literal(it).and_then(|string| {
if byte_string.starts_with("b\"") && byte_string.ends_with('\"') { if string.starts_with('\"') && string.ends_with('\"') {
Some(byte_string[2..byte_string.len() - 1].to_string()) let content = &string[1..string.len() - 1];
if content.contains('\\') {
panic!("Escape sequences in string literals not yet handled");
}
Some(content.to_string())
} else if string.starts_with("r\"") {
panic!("Raw string literals are not yet handled");
} else { } else {
None None
} }
...@@ -40,8 +46,14 @@ pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char { ...@@ -40,8 +46,14 @@ pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char {
} }
} }
pub(crate) fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String {
try_byte_string(it).expect("Expected byte string") try_string(it).expect("Expected string")
}
pub(crate) fn expect_string_ascii(it: &mut token_stream::IntoIter) -> String {
let string = try_string(it).expect("Expected string");
assert!(string.is_ascii(), "Expected ASCII string");
string
} }
pub(crate) fn expect_end(it: &mut token_stream::IntoIter) { pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
......
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
//! Crate for all kernel procedural macros. //! Crate for all kernel procedural macros.
mod concat_idents;
mod helpers; mod helpers;
mod module; mod module;
mod vtable;
use proc_macro::TokenStream; use proc_macro::TokenStream;
...@@ -23,20 +25,20 @@ ...@@ -23,20 +25,20 @@
/// ///
/// module!{ /// module!{
/// type: MyModule, /// type: MyModule,
/// name: b"my_kernel_module", /// name: "my_kernel_module",
/// author: b"Rust for Linux Contributors", /// author: "Rust for Linux Contributors",
/// description: b"My very own kernel module!", /// description: "My very own kernel module!",
/// license: b"GPL", /// license: "GPL",
/// params: { /// params: {
/// my_i32: i32 { /// my_i32: i32 {
/// default: 42, /// default: 42,
/// permissions: 0o000, /// permissions: 0o000,
/// description: b"Example of i32", /// description: "Example of i32",
/// }, /// },
/// writeable_i32: i32 { /// writeable_i32: i32 {
/// default: 42, /// default: 42,
/// permissions: 0o644, /// permissions: 0o644,
/// description: b"Example of i32", /// description: "Example of i32",
/// }, /// },
/// }, /// },
/// } /// }
...@@ -70,3 +72,97 @@ ...@@ -70,3 +72,97 @@
pub fn module(ts: TokenStream) -> TokenStream { pub fn module(ts: TokenStream) -> TokenStream {
module::module(ts) module::module(ts)
} }
/// Declares or implements a vtable trait.
///
/// Linux's use of pure vtables is very close to Rust traits, but they differ
/// in how unimplemented functions are represented. In Rust, traits can provide
/// default implementation for all non-required methods (and the default
/// implementation could just return `Error::EINVAL`); Linux typically use C
/// `NULL` pointers to represent these functions.
///
/// This attribute is intended to close the gap. Traits can be declared and
/// implemented with the `#[vtable]` attribute, and a `HAS_*` associated constant
/// will be generated for each method in the trait, indicating if the implementor
/// has overridden a method.
///
/// This attribute is not needed if all methods are required.
///
/// # Examples
///
/// ```ignore
/// use kernel::prelude::*;
///
/// // Declares a `#[vtable]` trait
/// #[vtable]
/// pub trait Operations: Send + Sync + Sized {
/// fn foo(&self) -> Result<()> {
/// Err(EINVAL)
/// }
///
/// fn bar(&self) -> Result<()> {
/// Err(EINVAL)
/// }
/// }
///
/// struct Foo;
///
/// // Implements the `#[vtable]` trait
/// #[vtable]
/// impl Operations for Foo {
/// fn foo(&self) -> Result<()> {
/// # Err(EINVAL)
/// // ...
/// }
/// }
///
/// assert_eq!(<Foo as Operations>::HAS_FOO, true);
/// assert_eq!(<Foo as Operations>::HAS_BAR, false);
/// ```
#[proc_macro_attribute]
pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
vtable::vtable(attr, ts)
}
/// Concatenate two identifiers.
///
/// This is useful in macros that need to declare or reference items with names
/// starting with a fixed prefix and ending in a user specified name. The resulting
/// identifier has the span of the second argument.
///
/// # Examples
///
/// ```ignore
/// use kernel::macro::concat_idents;
///
/// macro_rules! pub_no_prefix {
/// ($prefix:ident, $($newname:ident),+) => {
/// $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+
/// };
/// }
///
/// pub_no_prefix!(
/// binder_driver_return_protocol_,
/// BR_OK,
/// BR_ERROR,
/// BR_TRANSACTION,
/// BR_REPLY,
/// BR_DEAD_REPLY,
/// BR_TRANSACTION_COMPLETE,
/// BR_INCREFS,
/// BR_ACQUIRE,
/// BR_RELEASE,
/// BR_DECREFS,
/// BR_NOOP,
/// BR_SPAWN_LOOPER,
/// BR_DEAD_BINDER,
/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
/// BR_FAILED_REPLY
/// );
///
/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
/// ```
#[proc_macro]
pub fn concat_idents(ts: TokenStream) -> TokenStream {
concat_idents::concat_idents(ts)
}
...@@ -108,11 +108,11 @@ fn parse(it: &mut token_stream::IntoIter) -> Self { ...@@ -108,11 +108,11 @@ fn parse(it: &mut token_stream::IntoIter) -> Self {
match key.as_str() { match key.as_str() {
"type" => info.type_ = expect_ident(it), "type" => info.type_ = expect_ident(it),
"name" => info.name = expect_byte_string(it), "name" => info.name = expect_string_ascii(it),
"author" => info.author = Some(expect_byte_string(it)), "author" => info.author = Some(expect_string(it)),
"description" => info.description = Some(expect_byte_string(it)), "description" => info.description = Some(expect_string(it)),
"license" => info.license = expect_byte_string(it), "license" => info.license = expect_string_ascii(it),
"alias" => info.alias = Some(expect_byte_string(it)), "alias" => info.alias = Some(expect_string_ascii(it)),
_ => panic!( _ => panic!(
"Unknown key \"{}\". Valid keys are: {:?}.", "Unknown key \"{}\". Valid keys are: {:?}.",
key, EXPECTED_KEYS key, EXPECTED_KEYS
......
// SPDX-License-Identifier: GPL-2.0
use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
use std::collections::HashSet;
use std::fmt::Write;
pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream {
let mut tokens: Vec<_> = ts.into_iter().collect();
// Scan for the `trait` or `impl` keyword.
let is_trait = tokens
.iter()
.find_map(|token| match token {
TokenTree::Ident(ident) => match ident.to_string().as_str() {
"trait" => Some(true),
"impl" => Some(false),
_ => None,
},
_ => None,
})
.expect("#[vtable] attribute should only be applied to trait or impl block");
// Retrieve the main body. The main body should be the last token tree.
let body = match tokens.pop() {
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group,
_ => panic!("cannot locate main body of trait or impl block"),
};
let mut body_it = body.stream().into_iter();
let mut functions = Vec::new();
let mut consts = HashSet::new();
while let Some(token) = body_it.next() {
match token {
TokenTree::Ident(ident) if ident.to_string() == "fn" => {
let fn_name = match body_it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
// Possibly we've encountered a fn pointer type instead.
_ => continue,
};
functions.push(fn_name);
}
TokenTree::Ident(ident) if ident.to_string() == "const" => {
let const_name = match body_it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
// Possibly we've encountered an inline const block instead.
_ => continue,
};
consts.insert(const_name);
}
_ => (),
}
}
let mut const_items;
if is_trait {
const_items = "
/// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
/// attribute when implementing this trait.
const USE_VTABLE_ATTR: ();
"
.to_owned();
for f in functions {
let gen_const_name = format!("HAS_{}", f.to_uppercase());
// Skip if it's declared already -- this allows user override.
if consts.contains(&gen_const_name) {
continue;
}
// We don't know on the implementation-site whether a method is required or provided
// so we have to generate a const for all methods.
write!(
const_items,
"/// Indicates if the `{f}` method is overridden by the implementor.
const {gen_const_name}: bool = false;",
)
.unwrap();
}
} else {
const_items = "const USE_VTABLE_ATTR: () = ();".to_owned();
for f in functions {
let gen_const_name = format!("HAS_{}", f.to_uppercase());
if consts.contains(&gen_const_name) {
continue;
}
write!(const_items, "const {gen_const_name}: bool = true;").unwrap();
}
}
let new_body = vec![const_items.parse().unwrap(), body.stream()]
.into_iter()
.collect();
tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, new_body)));
tokens.into_iter().collect()
}
...@@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL ...@@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL
If unsure, say N. 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_HOSTPROGS config SAMPLE_RUST_HOSTPROGS
bool "Host programs" bool "Host programs"
help help
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
module! { module! {
type: RustMinimal, type: RustMinimal,
name: b"rust_minimal", name: "rust_minimal",
author: b"Rust for Linux Contributors", author: "Rust for Linux Contributors",
description: b"Rust minimal sample", description: "Rust minimal sample",
license: b"GPL", license: "GPL",
} }
struct RustMinimal { struct RustMinimal {
......
// SPDX-License-Identifier: GPL-2.0
//! Rust printing macros sample.
use kernel::pr_cont;
use kernel::prelude::*;
module! {
type: RustPrint,
name: "rust_print",
author: "Rust for Linux Contributors",
description: "Rust printing macros sample",
license: "GPL",
}
struct RustPrint;
impl kernel::Module for RustPrint {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust printing macros sample (init)\n");
pr_emerg!("Emergency message (level 0) without args\n");
pr_alert!("Alert message (level 1) without args\n");
pr_crit!("Critical message (level 2) without args\n");
pr_err!("Error message (level 3) without args\n");
pr_warn!("Warning message (level 4) without args\n");
pr_notice!("Notice message (level 5) without args\n");
pr_info!("Info message (level 6) without args\n");
pr_info!("A line that");
pr_cont!(" is continued");
pr_cont!(" without args\n");
pr_emerg!("{} message (level {}) with args\n", "Emergency", 0);
pr_alert!("{} message (level {}) with args\n", "Alert", 1);
pr_crit!("{} message (level {}) with args\n", "Critical", 2);
pr_err!("{} message (level {}) with args\n", "Error", 3);
pr_warn!("{} message (level {}) with args\n", "Warning", 4);
pr_notice!("{} message (level {}) with args\n", "Notice", 5);
pr_info!("{} message (level {}) with args\n", "Info", 6);
pr_info!("A {} that", "line");
pr_cont!(" is {}", "continued");
pr_cont!(" with {}\n", "args");
Ok(RustPrint)
}
}
impl Drop for RustPrint {
fn drop(&mut self) {
pr_info!("Rust printing macros sample (exit)\n");
}
}
...@@ -67,6 +67,12 @@ def generate_crates(srctree, objtree, sysroot_src): ...@@ -67,6 +67,12 @@ def generate_crates(srctree, objtree, sysroot_src):
) )
crates[-1]["proc_macro_dylib_path"] = "rust/libmacros.so" crates[-1]["proc_macro_dylib_path"] = "rust/libmacros.so"
append_crate(
"build_error",
srctree / "rust" / "build_error.rs",
["core", "compiler_builtins"],
)
append_crate( append_crate(
"bindings", "bindings",
srctree / "rust"/ "bindings" / "lib.rs", srctree / "rust"/ "bindings" / "lib.rs",
...@@ -78,7 +84,7 @@ def generate_crates(srctree, objtree, sysroot_src): ...@@ -78,7 +84,7 @@ def generate_crates(srctree, objtree, sysroot_src):
append_crate( append_crate(
"kernel", "kernel",
srctree / "rust" / "kernel" / "lib.rs", srctree / "rust" / "kernel" / "lib.rs",
["core", "alloc", "macros", "bindings"], ["core", "alloc", "macros", "build_error", "bindings"],
cfg=cfg, cfg=cfg,
) )
crates[-1]["source"] = { crates[-1]["source"] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment