Rust changes for v6.2
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!`. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPjU5OPd5QIZ9jqqOGXyLc2htIW0FAmOVJTQACgkQGXyLc2ht IW3cLA//dn0/rN2sCJBsX8/mNQqRXQvM45QUP5ESd5w01fRXEpvHZ+MbfdcFZN8K oQEgZwTwTKvKY6V+xCPmEiUvk5jniCzdEjTFtDhVhA+qimxY/FMS3zozuJMQIlz2 zTiZ4aEM1zFAwoDHnnOmaCO+C0zw5d9UFIKO4nIvOSy3gD/eLgiFz3cyTh8Q2+BT lPyqeKg9+xKIl8tWa5zGYHgZASGguV0EpXFn4Ck4eBOH6O9ovWgakdzZp0BMJ9Ca UNIFpFjLMUkCwzZkPqIyI9IZEOzUYWTTfWU9S5JJ6IzC3aT8NPp3WeSYW9TgVnvO z5n6rsYOgvKeWCvGIgq82fgVbGMNaaP1MFxNLsdbWWj+9lfebpk62aQXSuWsvASq /W39/xEhOOLikyb3ObVHLW1r1lu9guSeP8eaMQ5ci/99kypHHBOXmB/nr9pxPkrr kovxuZedDbgEYunbVmwWGmvLg8dcjadfeXf6Dkc6bwDvyhiuX9W21z9ppT9nV5NW chYRAPROCHuBRu+txft9gIjyE1/V7G8CyeWiG36VWN8Tayc5iJEWOopk4GJcEpJi MS5tAJru7fBZcXjFausN3mdXyRwMLdilTZ2Qkp6MqzXi5zwVuKH1wsJ7CLkPBWQC tAPJts6krOonI2cd2JM8ds+Wj5Q1cDGQuF6Rj29/27aUBKH1w2Y= =iIcK -----END PGP SIGNATURE----- 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` ...
This commit is contained in:
commit
96f4263568
@ -2823,6 +2823,22 @@ config RUST_OVERFLOW_CHECKS
|
||||
|
||||
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"
|
||||
|
||||
source "Documentation/Kconfig"
|
||||
|
@ -19,6 +19,12 @@ obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o
|
||||
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_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
|
||||
|
||||
# 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
|
||||
$(call if_changed,rustdoc)
|
||||
|
||||
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
|
||||
rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \
|
||||
rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \
|
||||
@ -126,6 +132,9 @@ quiet_cmd_rustc_test_library = RUSTC TL $<
|
||||
-L$(objtree)/$(obj)/test \
|
||||
--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_test_library_proc = yes
|
||||
rusttestlib-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)
|
||||
|
||||
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 \
|
||||
rusttestlib-macros rusttestlib-bindings FORCE
|
||||
rusttestlib-build_error rusttestlib-macros rusttestlib-bindings FORCE
|
||||
$(call if_changed,rustc_test)
|
||||
$(call if_changed,rustc_test_library)
|
||||
|
||||
@ -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
|
||||
$(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)/compiler_builtins.o \
|
||||
$(obj)/bindings/bindings_generated.rs \
|
||||
@ -373,8 +385,8 @@ $(obj)/bindings.o: $(src)/bindings/lib.rs \
|
||||
$(call if_changed_dep,rustc_library)
|
||||
|
||||
$(obj)/kernel.o: private rustc_target_flags = --extern alloc \
|
||||
--extern macros --extern bindings
|
||||
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o \
|
||||
--extern build_error --extern macros --extern bindings
|
||||
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \
|
||||
$(obj)/libmacros.so $(obj)/bindings.o FORCE
|
||||
$(call if_changed_dep,rustc_library)
|
||||
|
||||
|
@ -20,11 +20,11 @@ use crate::collections::TryReserveErrorKind::*;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
enum AllocInit {
|
||||
/// The contents of the new memory are uninitialized.
|
||||
Uninitialized,
|
||||
/// The new memory is guaranteed to be zeroed.
|
||||
#[allow(dead_code)]
|
||||
Zeroed,
|
||||
}
|
||||
|
||||
@ -133,6 +133,13 @@ impl<T, A: Allocator> RawVec<T, A> {
|
||||
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
|
||||
/// of allocator for the returned `RawVec`.
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
@ -203,6 +210,30 @@ impl<T, A: Allocator> RawVec<T, A> {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -472,6 +472,48 @@ impl<T> Vec<T> {
|
||||
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.
|
||||
///
|
||||
/// # Safety
|
||||
@ -617,6 +659,53 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
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.
|
||||
///
|
||||
/// # Safety
|
||||
|
31
rust/build_error.rs
Normal file
31
rust/build_error.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// 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 @@
|
||||
#include "exports_alloc_generated.h"
|
||||
#include "exports_bindings_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
|
||||
|
82
rust/kernel/build_assert.rs
Normal file
82
rust/kernel/build_assert.rs
Normal file
@ -0,0 +1,82 @@
|
||||
// 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 @@
|
||||
//!
|
||||
//! 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.
|
||||
pub mod code {
|
||||
/// Out of memory.
|
||||
pub const ENOMEM: super::Error = super::Error(-(crate::bindings::ENOMEM as i32));
|
||||
macro_rules! declare_err {
|
||||
($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.
|
||||
@ -30,12 +78,48 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn from(_: TryReserveError) -> Error {
|
||||
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.
|
||||
///
|
||||
/// To be used as the return type for functions that may fail.
|
||||
|
@ -12,6 +12,7 @@
|
||||
//! do so first instead of bypassing this crate.
|
||||
|
||||
#![no_std]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(core_ffi_c)]
|
||||
|
||||
// Ensure conditional compilation based on the kernel configuration works;
|
||||
@ -22,15 +23,23 @@ compile_error!("Missing kernel configuration for conditional compilation");
|
||||
#[cfg(not(test))]
|
||||
#[cfg(not(testlib))]
|
||||
mod allocator;
|
||||
mod build_assert;
|
||||
pub mod error;
|
||||
pub mod prelude;
|
||||
pub mod print;
|
||||
mod static_assert;
|
||||
#[doc(hidden)]
|
||||
pub mod std_vendor;
|
||||
pub mod str;
|
||||
pub mod types;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use bindings;
|
||||
pub use macros;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use build_error::build_error;
|
||||
|
||||
/// Prefix to appear before log messages printed from within the `kernel` crate.
|
||||
const __LOG_PREFIX: &[u8] = b"rust_kernel\0";
|
||||
|
||||
|
@ -11,10 +11,18 @@
|
||||
//! 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 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 {
|
||||
// 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`].
|
||||
@ -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`].
|
||||
///
|
||||
/// Public but hidden since it should only be used from public macros.
|
||||
@ -114,7 +141,7 @@ pub unsafe fn call_printk(
|
||||
#[allow(clippy::crate_in_macro_def)]
|
||||
macro_rules! print_macro (
|
||||
// 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
|
||||
// printing macros which ensure the format string is one of the fixed
|
||||
// ones. All `__LOG_PREFIX`s are null-terminated as they are generated
|
||||
@ -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
|
||||
@ -168,7 +202,127 @@ macro_rules! print_macro (
|
||||
#[macro_export]
|
||||
macro_rules! pr_emerg (
|
||||
($($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 (
|
||||
#[doc(alias = "print")]
|
||||
macro_rules! pr_info (
|
||||
($($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)*)
|
||||
)
|
||||
);
|
||||
|
34
rust/kernel/static_assert.rs
Normal file
34
rust/kernel/static_assert.rs
Normal file
@ -0,0 +1,34 @@
|
||||
// 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);
|
||||
};
|
||||
}
|
163
rust/kernel/std_vendor.rs
Normal file
163
rust/kernel/std_vendor.rs
Normal file
@ -0,0 +1,163 @@
|
||||
// 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 @@
|
||||
|
||||
//! 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.
|
||||
///
|
||||
@ -15,13 +385,22 @@ use core::fmt;
|
||||
/// is less than `end`.
|
||||
pub(crate) struct RawFormatter {
|
||||
// Use `usize` to use `saturating_*` functions.
|
||||
#[allow(dead_code)]
|
||||
beg: usize,
|
||||
pos: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// # Safety
|
||||
@ -37,12 +416,34 @@ impl RawFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// N.B. It may point to invalid memory.
|
||||
pub(crate) fn pos(&self) -> *mut u8 {
|
||||
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 {
|
||||
@ -70,3 +471,121 @@ impl fmt::Write for RawFormatter {
|
||||
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)*) )
|
||||
}
|
||||
|
37
rust/kernel/types.rs
Normal file
37
rust/kernel/types.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// 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),
|
||||
}
|
23
rust/macros/concat_idents.rs
Normal file
23
rust/macros/concat_idents.rs
Normal file
@ -0,0 +1,23 @@
|
||||
// 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> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_byte_string(it: &mut token_stream::IntoIter) -> Option<String> {
|
||||
try_literal(it).and_then(|byte_string| {
|
||||
if byte_string.starts_with("b\"") && byte_string.ends_with('\"') {
|
||||
Some(byte_string[2..byte_string.len() - 1].to_string())
|
||||
pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option<String> {
|
||||
try_literal(it).and_then(|string| {
|
||||
if string.starts_with('\"') && string.ends_with('\"') {
|
||||
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 {
|
||||
None
|
||||
}
|
||||
@ -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 {
|
||||
try_byte_string(it).expect("Expected byte string")
|
||||
pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> 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) {
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
//! Crate for all kernel procedural macros.
|
||||
|
||||
mod concat_idents;
|
||||
mod helpers;
|
||||
mod module;
|
||||
mod vtable;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
@ -23,20 +25,20 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// module!{
|
||||
/// type: MyModule,
|
||||
/// name: b"my_kernel_module",
|
||||
/// author: b"Rust for Linux Contributors",
|
||||
/// description: b"My very own kernel module!",
|
||||
/// license: b"GPL",
|
||||
/// name: "my_kernel_module",
|
||||
/// author: "Rust for Linux Contributors",
|
||||
/// description: "My very own kernel module!",
|
||||
/// license: "GPL",
|
||||
/// params: {
|
||||
/// my_i32: i32 {
|
||||
/// default: 42,
|
||||
/// permissions: 0o000,
|
||||
/// description: b"Example of i32",
|
||||
/// description: "Example of i32",
|
||||
/// },
|
||||
/// writeable_i32: i32 {
|
||||
/// default: 42,
|
||||
/// permissions: 0o644,
|
||||
/// description: b"Example of i32",
|
||||
/// description: "Example of i32",
|
||||
/// },
|
||||
/// },
|
||||
/// }
|
||||
@ -70,3 +72,97 @@ use proc_macro::TokenStream;
|
||||
pub fn module(ts: TokenStream) -> TokenStream {
|
||||
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 @@ impl ModuleInfo {
|
||||
|
||||
match key.as_str() {
|
||||
"type" => info.type_ = expect_ident(it),
|
||||
"name" => info.name = expect_byte_string(it),
|
||||
"author" => info.author = Some(expect_byte_string(it)),
|
||||
"description" => info.description = Some(expect_byte_string(it)),
|
||||
"license" => info.license = expect_byte_string(it),
|
||||
"alias" => info.alias = Some(expect_byte_string(it)),
|
||||
"name" => info.name = expect_string_ascii(it),
|
||||
"author" => info.author = Some(expect_string(it)),
|
||||
"description" => info.description = Some(expect_string(it)),
|
||||
"license" => info.license = expect_string_ascii(it),
|
||||
"alias" => info.alias = Some(expect_string_ascii(it)),
|
||||
_ => panic!(
|
||||
"Unknown key \"{}\". Valid keys are: {:?}.",
|
||||
key, EXPECTED_KEYS
|
||||
|
95
rust/macros/vtable.rs
Normal file
95
rust/macros/vtable.rs
Normal file
@ -0,0 +1,95 @@
|
||||
// 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
|
||||
|
||||
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
|
||||
bool "Host programs"
|
||||
help
|
||||
|
@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
|
||||
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
|
||||
|
||||
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
|
||||
|
@ -6,10 +6,10 @@ use kernel::prelude::*;
|
||||
|
||||
module! {
|
||||
type: RustMinimal,
|
||||
name: b"rust_minimal",
|
||||
author: b"Rust for Linux Contributors",
|
||||
description: b"Rust minimal sample",
|
||||
license: b"GPL",
|
||||
name: "rust_minimal",
|
||||
author: "Rust for Linux Contributors",
|
||||
description: "Rust minimal sample",
|
||||
license: "GPL",
|
||||
}
|
||||
|
||||
struct RustMinimal {
|
||||
|
54
samples/rust/rust_print.rs
Normal file
54
samples/rust/rust_print.rs
Normal file
@ -0,0 +1,54 @@
|
||||
// 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):
|
||||
)
|
||||
crates[-1]["proc_macro_dylib_path"] = "rust/libmacros.so"
|
||||
|
||||
append_crate(
|
||||
"build_error",
|
||||
srctree / "rust" / "build_error.rs",
|
||||
["core", "compiler_builtins"],
|
||||
)
|
||||
|
||||
append_crate(
|
||||
"bindings",
|
||||
srctree / "rust"/ "bindings" / "lib.rs",
|
||||
@ -78,7 +84,7 @@ def generate_crates(srctree, objtree, sysroot_src):
|
||||
append_crate(
|
||||
"kernel",
|
||||
srctree / "rust" / "kernel" / "lib.rs",
|
||||
["core", "alloc", "macros", "bindings"],
|
||||
["core", "alloc", "macros", "build_error", "bindings"],
|
||||
cfg=cfg,
|
||||
)
|
||||
crates[-1]["source"] = {
|
||||
|
Loading…
Reference in New Issue
Block a user