2022-11-10 17:09:38

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 00/28] Rust core additions

This patch series is the first batch of changes to upstream the rest
of the Rust support.

In this instance, all the facilities introduced are part of the "Rust
core". They do not interact with the C side in new major ways (no new
C types used; only `strlen`, `memchr`, additional error codes and some
more `printk` format strings).

After this series, `str.rs` and `print.rs` are in sync with downstream
and all remaining proc macros have been added. `error.rs` is fairly
complete too, though a few more `pub(crate)` features will come later
(to avoid leaving dead code in-between series).

Note that a few temporary `#[allow(dead_code)]` attributes are used in
order to have a bit more freedom organizing the patches while keeping
all of them buildable without warnings/errors, but at the end of the
series there is none remaining (of the temporary ones).

Virtually all the code has been in linux-next for months and was part
of the Rust patch series before the trimming down (i.e. up to v8).

Each patch has been built-tested on a Rust-enabled `defconfig` with
`CLIPPY=1` on the `all`, `rustfmtcheck`, `rustdoc` and `rusttest`
targets.

Björn Roy Baron (1):
rust: macros: add `concat_idents!` proc macro

Finn Behrens (1):
rust: error: declare errors using macro

Gary Guo (9):
rust: macros: add `#[vtable]` proc macro
rust: macros: take string literals in `module!`
rust: str: add `BStr` type
rust: str: add `b_str!` macro
rust: str: add `CStr` type
rust: str: implement several traits for `CStr`
rust: str: add `c_str!` macro
rust: add `build_error` crate
rust: build_assert: add `build_{error,assert}!` macros

Miguel Ojeda (7):
rust: prelude: split re-exports into groups
rust: print: add more `pr_*!` levels
rust: print: add `pr_cont!` macro
rust: samples: add `rust_print` example
rust: alloc: add `RawVec::try_with_capacity_in()` constructor
rust: alloc: add `Vec::try_with_capacity{,_in}()` constructors
rust: static_assert: add `static_assert!` macro

Milan Landaverde (1):
rust: str: add `CStr` unit tests

Niklas Mohrin (1):
rust: std_vendor: add `dbg!` macro based on `std`'s one

Viktor Garske (1):
rust: error: add codes from `errno-base.h`

Wedson Almeida Filho (7):
rust: error: add `From` implementations for `Error`
rust: prelude: add `error::code::*` constant items
rust: str: add `Formatter` type
rust: str: add `CString` type
rust: str: add `fmt!` macro
rust: types: add `Either` type
rust: types: add `Opaque` type

lib/Kconfig.debug | 16 +
rust/Makefile | 22 +-
rust/alloc/raw_vec.rs | 33 +-
rust/alloc/vec/mod.rs | 89 +++++
rust/build_error.rs | 24 ++
rust/exports.c | 5 +
rust/kernel/build_assert.rs | 82 +++++
rust/kernel/error.rs | 90 ++++-
rust/kernel/lib.rs | 9 +
rust/kernel/prelude.rs | 20 +-
rust/kernel/print.rs | 214 +++++++++++-
rust/kernel/static_assert.rs | 34 ++
rust/kernel/std_vendor.rs | 160 +++++++++
rust/kernel/str.rs | 532 +++++++++++++++++++++++++++++-
rust/kernel/types.rs | 37 +++
rust/macros/concat_idents.rs | 23 ++
rust/macros/helpers.rs | 24 +-
rust/macros/lib.rs | 108 +++++-
rust/macros/module.rs | 10 +-
rust/macros/vtable.rs | 95 ++++++
samples/rust/Kconfig | 10 +
samples/rust/Makefile | 1 +
samples/rust/rust_minimal.rs | 8 +-
samples/rust/rust_print.rs | 54 +++
scripts/generate_rust_analyzer.py | 8 +-
25 files changed, 1666 insertions(+), 42 deletions(-)
create mode 100644 rust/build_error.rs
create mode 100644 rust/kernel/build_assert.rs
create mode 100644 rust/kernel/static_assert.rs
create mode 100644 rust/kernel/std_vendor.rs
create mode 100644 rust/kernel/types.rs
create mode 100644 rust/macros/concat_idents.rs
create mode 100644 rust/macros/vtable.rs
create mode 100644 samples/rust/rust_print.rs


base-commit: f0c4d9fc9cc9462659728d168387191387e903cc
--
2.38.1



2022-11-10 17:09:50

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 03/28] rust: print: add `pr_cont!` macro

This level is a bit different from the rest since it does not
pass the module name to the `_printk()` call.

Thus add a new parameter to the general `print_macro!` to
handle it differently.

Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Co-developed-by: Gary Guo <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/print.rs | 72 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 63 insertions(+), 9 deletions(-)

diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
index 694f51c6da5c..29bf9c2e8aee 100644
--- a/rust/kernel/print.rs
+++ b/rust/kernel/print.rs
@@ -81,6 +81,7 @@ pub mod format_strings {
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`].
@@ -111,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.
@@ -120,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
@@ -134,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
@@ -174,7 +202,7 @@ 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)*)
)
);

@@ -198,7 +226,7 @@ macro_rules! pr_emerg (
#[macro_export]
macro_rules! pr_alert (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::ALERT, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::ALERT, false, $($arg)*)
)
);

@@ -222,7 +250,7 @@ macro_rules! pr_alert (
#[macro_export]
macro_rules! pr_crit (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::CRIT, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::CRIT, false, $($arg)*)
)
);

@@ -246,7 +274,7 @@ macro_rules! pr_crit (
#[macro_export]
macro_rules! pr_err (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::ERR, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::ERR, false, $($arg)*)
)
);

@@ -270,7 +298,7 @@ macro_rules! pr_err (
#[macro_export]
macro_rules! pr_warn (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::WARNING, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::WARNING, false, $($arg)*)
)
);

@@ -294,7 +322,7 @@ macro_rules! pr_warn (
#[macro_export]
macro_rules! pr_notice (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::NOTICE, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::NOTICE, false, $($arg)*)
)
);

@@ -319,7 +347,7 @@ macro_rules! pr_notice (
#[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)*)
)
);

@@ -346,7 +374,33 @@ macro_rules! pr_info (
macro_rules! pr_debug (
($($arg:tt)*) => (
if cfg!(debug_assertions) {
- $crate::print_macro!($crate::print::format_strings::DEBUG, $($arg)*)
+ $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)*)
+ )
+);
--
2.38.1


2022-11-10 17:09:53

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 07/28] rust: macros: take string literals in `module!`

From: Gary Guo <[email protected]>

Instead of taking binary string literals, take string ones instead,
making it easier for users to define a module, i.e. instead of
calling `module!` like:

module! {
...
name: b"rust_minimal",
...
}

now it is called as:

module! {
...
name: "rust_minimal",
...
}

Module names, aliases and license strings are restricted to
ASCII only. However, the author and the description allows UTF-8.

For simplicity (avoid parsing), escape sequences and raw string
literals are not yet handled.

Link: https://github.com/Rust-for-Linux/linux/issues/252
Link: https://lore.kernel.org/lkml/[email protected]/
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/macros/helpers.rs | 24 ++++++++++++++++++------
rust/macros/lib.rs | 12 ++++++------
rust/macros/module.rs | 10 +++++-----
samples/rust/rust_minimal.rs | 8 ++++----
samples/rust/rust_print.rs | 8 ++++----
5 files changed, 37 insertions(+), 25 deletions(-)

diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs
index cdc7dc6135d2..cf7ad950dc1e 100644
--- a/rust/macros/helpers.rs
+++ b/rust/macros/helpers.rs
@@ -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) {
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index e40caaf0a656..c1d385e345b9 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -25,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",
/// },
/// },
/// }
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 186a5b8be23c..a7e363c2b044 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -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
diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
index 54ad17685742..dc05f4bbe27e 100644
--- a/samples/rust/rust_minimal.rs
+++ b/samples/rust/rust_minimal.rs
@@ -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 {
diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
index 09f737790f3f..8b39d9cef6d1 100644
--- a/samples/rust/rust_print.rs
+++ b/samples/rust/rust_print.rs
@@ -7,10 +7,10 @@ use kernel::prelude::*;

module! {
type: RustPrint,
- name: b"rust_print",
- author: b"Rust for Linux Contributors",
- description: b"Rust printing macros sample",
- license: b"GPL",
+ name: "rust_print",
+ author: "Rust for Linux Contributors",
+ description: "Rust printing macros sample",
+ license: "GPL",
}

struct RustPrint;
--
2.38.1


2022-11-10 17:10:54

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 17/28] rust: str: implement several traits for `CStr`

From: Gary Guo <[email protected]>

Implement `Debug`, `Display`, `Deref` (into `BStr`), `AsRef<BStr>`
and a set of `Index<...>` traits.

This makes it `CStr` more convenient to use (and closer to `str`).

Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Morgan Bartlett <[email protected]>
Signed-off-by: Morgan Bartlett <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 123 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index d66565f92f71..11d297c1a61c 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -2,7 +2,8 @@

//! String representations.

-use core::fmt;
+use core::fmt::{self, Write};
+use core::ops::{self, Deref, Index};

use crate::{
bindings,
@@ -199,6 +200,127 @@ impl CStr {
}
}

+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]
+ }
+}
+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
///
/// It does not fail if callers write past the end of the buffer so that they can calculate the
--
2.38.1


2022-11-10 17:10:56

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 04/28] rust: samples: add `rust_print` example

Add example to exercise the printing macros (`pr_*!`) introduced
in the previous patches.

Signed-off-by: Miguel Ojeda <[email protected]>
---
samples/rust/Kconfig | 10 +++++++
samples/rust/Makefile | 1 +
samples/rust/rust_print.rs | 54 ++++++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
create mode 100644 samples/rust/rust_print.rs

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 841e0906e943..b0f74a81c8f9 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL

If unsure, say N.

+config SAMPLE_RUST_PRINT
+ tristate "Printing macros"
+ help
+ This option builds the Rust printing macros sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_print.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 1daba5f8658a..03086dabbea4 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -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
diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
new file mode 100644
index 000000000000..09f737790f3f
--- /dev/null
+++ b/samples/rust/rust_print.rs
@@ -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: b"rust_print",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust printing macros sample",
+ license: b"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");
+ }
+}
--
2.38.1


2022-11-10 17:11:06

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 10/28] rust: error: add `From` implementations for `Error`

From: Wedson Almeida Filho <[email protected]>

Add a set of `From` implementations for the `Error` kernel type.

These implementations allow to easily convert from standard Rust
error types to the usual kernel errors based on one of the `E*`
integer codes.

On top of that, the question mark Rust operator (`?`) implicitly
performs a conversion on the error value using the `From` trait
when propagating. Thus it is extra convenient to use.

For instance, a kernel function that needs to convert a `i64` into
a `i32` and to bubble up the error as a kernel error may write:

fn f(x: i64) -> Result<...> {
...
let y = i32::try_from(x)?;
...
}

which will transform the `TryFromIntError` into an `Err(EINVAL)`.

Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Nándor István Krácser <[email protected]>
Signed-off-by: Nándor István Krácser <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/error.rs | 45 +++++++++++++++++++++++++++++++++++++++++++-
rust/kernel/lib.rs | 1 +
2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 861746f2422d..5b9751d7ff1d 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -4,7 +4,14 @@
//!
//! 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 {
@@ -71,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.
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index abd46261d385..ffc6626a6d29 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -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;
--
2.38.1


2022-11-10 17:11:12

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 28/28] rust: types: add `Opaque` type

From: Wedson Almeida Filho <[email protected]>

Add the `Opaque` type, which is meant to be used with FFI objects
that are never interpreted by Rust code, e.g.:

struct Waiter {
completion: Opaque<bindings::completion>,
next: *mut Waiter,
}

It has the advantage that the objects don't have to be
zero-initialised before calling their init functions, making
the code performance closer to C.

Signed-off-by: Wedson Almeida Filho <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/types.rs | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index 3b0c44769708..e84e51ec9716 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -2,6 +2,31 @@

//! 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`.
--
2.38.1


2022-11-10 17:11:26

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 06/28] rust: macros: add `#[vtable]` proc macro

From: Gary Guo <[email protected]>

This procedural macro attribute provides a simple way to declare
a trait with a set of operations that later users can partially
implement, providing compile-time `HAS_*` boolean associated
constants that indicate whether a particular operation was overridden.

This is useful as the Rust counterpart to structs like
`file_operations` where some pointers may be `NULL`, indicating
an operation is not provided.

For instance:

#[vtable]
trait Operations {
fn read(...) -> Result<usize> {
Err(EINVAL)
}

fn write(...) -> Result<usize> {
Err(EINVAL)
}
}

#[vtable]
impl Operations for S {
fn read(...) -> Result<usize> {
...
}
}

assert_eq!(<S as Operations>::HAS_READ, true);
assert_eq!(<S as Operations>::HAS_WRITE, false);

Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 2 +-
rust/macros/lib.rs | 52 +++++++++++++++++++++++
rust/macros/vtable.rs | 95 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 148 insertions(+), 1 deletion(-)
create mode 100644 rust/macros/vtable.rs

diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 6a1c6b38327f..7c4c35bf3c66 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -15,7 +15,7 @@ pub use core::pin::Pin;

pub use alloc::{boxed::Box, vec::Vec};

-pub use macros::module;
+pub use macros::{module, vtable};

pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};

diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 15555e7ff487..e40caaf0a656 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -5,6 +5,7 @@
mod concat_idents;
mod helpers;
mod module;
+mod vtable;

use proc_macro::TokenStream;

@@ -72,6 +73,57 @@ 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
diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs
new file mode 100644
index 000000000000..34d5e7fb5768
--- /dev/null
+++ b/rust/macros/vtable.rs
@@ -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()
+}
--
2.38.1


2022-11-10 17:11:27

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 24/28] rust: static_assert: add `static_assert!` macro

Add the `static_assert!` macro, which is a compile-time assert, similar
to the C11 `_Static_assert` and C++11 `static_assert` declarations [1,2].
Do so in a new module, called `static_assert`.

For instance:

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);

Link: https://en.cppreference.com/w/c/language/_Static_assert [1]
Link: https://en.cppreference.com/w/cpp/language/static_assert [2]
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/lib.rs | 1 +
rust/kernel/prelude.rs | 2 ++
rust/kernel/static_assert.rs | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 37 insertions(+)
create mode 100644 rust/kernel/static_assert.rs

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index d6371c9c8453..9b83ef736298 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -26,6 +26,7 @@ mod allocator;
pub mod error;
pub mod prelude;
pub mod print;
+mod static_assert;
#[doc(hidden)]
pub mod std_vendor;
pub mod str;
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 345fc9075d1f..178fe8e6cb6d 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -19,6 +19,8 @@ pub use macros::{module, vtable};

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};
diff --git a/rust/kernel/static_assert.rs b/rust/kernel/static_assert.rs
new file mode 100644
index 000000000000..3115ee0ba8e9
--- /dev/null
+++ b/rust/kernel/static_assert.rs
@@ -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);
+ };
+}
--
2.38.1


2022-11-10 17:12:03

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 15/28] rust: str: add `b_str!` macro

From: Gary Guo <[email protected]>

Add the `b_str!` macro, which creates a new `BStr` from
a string literal.

It is usable in const contexts, for instance:

const X: &BStr = b_str!("Example");

Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 3aa1a0cb9bf8..95eb757c619d 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -9,6 +9,27 @@ use core::fmt;
/// `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
+ }};
+}
+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
///
/// It does not fail if callers write past the end of the buffer so that they can calculate the
--
2.38.1


2022-11-10 17:12:19

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 27/28] rust: types: add `Either` type

From: Wedson Almeida Filho <[email protected]>

Introduce the new `types` module of the `kernel` crate with
`Either` as its first type.

`Either<L, R>` is a sum type that always holds either a value
of type `L` (`Left` variant) or `R` (`Right` variant).

For instance:

struct Executor {
queue: Either<BoxedQueue, &'static Queue>,
}

Signed-off-by: Wedson Almeida Filho <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/lib.rs | 1 +
rust/kernel/types.rs | 12 ++++++++++++
2 files changed, 13 insertions(+)
create mode 100644 rust/kernel/types.rs

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index a3abc110ff97..53040fa9e897 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -31,6 +31,7 @@ mod static_assert;
#[doc(hidden)]
pub mod std_vendor;
pub mod str;
+pub mod types;

#[doc(hidden)]
pub use bindings;
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
new file mode 100644
index 000000000000..3b0c44769708
--- /dev/null
+++ b/rust/kernel/types.rs
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Kernel types.
+
+/// 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),
+}
--
2.38.1


2022-11-10 17:13:12

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 02/28] rust: print: add more `pr_*!` levels

Currently, only `pr_info!` (for the minimal sample) and
`pr_emerg!` (for the panic handler) are there.

Add the other levels as new macros, i.e. `pr_alert!`, `pr_crit!`,
`pr_err!`, `pr_warn!`, `pr_notice!` and `pr_debug!`.

Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Co-developed-by: Gary Guo <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 2 +-
rust/kernel/print.rs | 154 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index f8219285d8c0..6a1c6b38327f 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -17,7 +17,7 @@ pub use alloc::{boxed::Box, vec::Vec};

pub use macros::module;

-pub use super::{pr_emerg, pr_info};
+pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};

pub use super::error::{Error, Result};

diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
index 55db5a1ba752..694f51c6da5c 100644
--- a/rust/kernel/print.rs
+++ b/rust/kernel/print.rs
@@ -74,7 +74,13 @@ 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);
}

/// Prints a message via the kernel's [`_printk`].
@@ -172,6 +178,126 @@ macro_rules! pr_emerg (
)
);

+/// 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, $($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, $($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, $($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, $($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, $($arg)*)
+ )
+);
+
/// Prints an info-level message (level 6).
///
/// Use this level for informational messages.
@@ -196,3 +322,31 @@ macro_rules! pr_info (
$crate::print_macro!($crate::print::format_strings::INFO, $($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, $($arg)*)
+ }
+ )
+);
--
2.38.1


2022-11-10 17:13:19

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 16/28] rust: str: add `CStr` type

From: Gary Guo <[email protected]>

Add the `CStr` type, which is a borrowed string that is guaranteed
to have exactly one `NUL` byte, which is at the end.

It is used for interoperability with kernel APIs that take C strings.

Add it to the prelude too.

Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Milan Landaverde <[email protected]>
Signed-off-by: Milan Landaverde <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 2 +-
rust/kernel/str.rs | 169 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 170 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 1e08b08e9420..89c2c9f4e7a7 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -21,4 +21,4 @@ pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notic

pub use super::error::{code::*, Error, Result};

-pub use super::ThisModule;
+pub use super::{str::CStr, ThisModule};
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 95eb757c619d..d66565f92f71 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -4,6 +4,11 @@

use core::fmt;

+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.
@@ -30,6 +35,170 @@ macro_rules! b_str {
}};
}

+/// 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()) }
+ }
+}
+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
///
/// It does not fail if callers write past the end of the buffer so that they can calculate the
--
2.38.1


2022-11-10 17:13:43

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 08/28] rust: error: declare errors using macro

From: Finn Behrens <[email protected]>

Add a macro to declare errors, which simplifies the work needed to
add each one, avoids repetition of the code and makes it easier to
change the way they are declared.

Signed-off-by: Finn Behrens <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/error.rs | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 466b2a8fe569..b843f3445483 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -8,8 +8,16 @@ use alloc::collections::TryReserveError;

/// 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!(ENOMEM, "Out of memory.");
}

/// Generic integer kernel error.
--
2.38.1


2022-11-10 17:14:25

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 21/28] rust: str: add `CString` type

From: Wedson Almeida Filho <[email protected]>

Add the `CString` type, which is an owned string that is guaranteed
to have exactly one `NUL` byte at the end, i.e. the owned equivalent
to `CStr` introduced earlier.

It is used for interoperability with kernel APIs that take C strings.

In order to do so, implement the `RawFormatter::new()` constructor
and the `RawFormatter::bytes_written()` method as well.

Signed-off-by: Wedson Almeida Filho <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 89 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index db6473db31c6..877148b77e71 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -2,6 +2,7 @@

//! String representations.

+use alloc::vec::Vec;
use core::fmt::{self, Write};
use core::ops::{self, Deref, Index};

@@ -393,13 +394,22 @@ mod tests {
/// 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
@@ -438,6 +448,11 @@ impl RawFormatter {
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 {
@@ -478,7 +493,6 @@ impl Formatter {
///
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
/// for the lifetime of the returned [`Formatter`].
- #[allow(dead_code)]
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) })
@@ -505,3 +519,76 @@ impl fmt::Write for Formatter {
}
}
}
+
+/// 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()) }
+ }
+}
--
2.38.1


2022-11-10 17:29:11

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 26/28] rust: build_assert: add `build_{error,assert}!` macros

From: Gary Guo <[email protected]>

Add the `build_error!` and `build_assert!` macros which leverage
the previously introduced `build_error` crate. Do so in a new
module, called `build_assert`.

The former fails the build if the code path calling it can possibly
be executed. The latter asserts that a boolean expression is `true`
at compile time.

In particular, `build_assert!` can be used in some contexts where
`static_assert!` cannot:

fn f1<const N: usize>() {
static_assert!(N > 1);` // Error.
build_assert!(N > 1); // Build-time check.
assert!(N > 1); // Run-time check.
}

#[inline]
fn f2(n: usize) {
static_assert!(n > 1); // Error.
build_assert!(n > 1); // Build-time check.
assert!(n > 1); // Run-time check.
}

Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/build_assert.rs | 82 +++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 4 ++
rust/kernel/prelude.rs | 2 +
3 files changed, 88 insertions(+)
create mode 100644 rust/kernel/build_assert.rs

diff --git a/rust/kernel/build_assert.rs b/rust/kernel/build_assert.rs
new file mode 100644
index 000000000000..659542393c09
--- /dev/null
+++ b/rust/kernel/build_assert.rs
@@ -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);
+ }
+ }};
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9b83ef736298..a3abc110ff97 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -23,6 +23,7 @@ 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;
@@ -35,6 +36,9 @@ pub mod str;
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";

diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 178fe8e6cb6d..7a90249ee9b9 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -17,6 +17,8 @@ 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;
--
2.38.1


2022-11-10 17:31:14

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 18/28] rust: str: add `CStr` unit tests

From: Milan Landaverde <[email protected]>

Add unit tests for `CStr::from_bytes_with_nul()` and
`CStr::from_bytes_with_nul_unchecked()`.

These serve as an example of the first unit tests for Rust code
(i.e. different from documentation tests).

Signed-off-by: Milan Landaverde <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 11d297c1a61c..3ed685cb5a3c 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -321,6 +321,35 @@ where
}
}

+#[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.
///
/// It does not fail if callers write past the end of the buffer so that they can calculate the
--
2.38.1


2022-11-10 17:32:12

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 19/28] rust: str: add `c_str!` macro

From: Gary Guo <[email protected]>

Add `c_str!`, which is a convenience macro that creates a new `CStr`
from a string literal.

It is designed to be similar to a `str` in usage, and it is usable
in const contexts, for instance:

const X: &CStr = c_str!("Example");

Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 3ed685cb5a3c..3fb73b888dce 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -128,6 +128,18 @@ impl CStr {
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
}

+ /// Creates a [`CStr`] from a `[u8]`, panic if input is not valid.
+ ///
+ /// This function is only meant to be used by `c_str!` macro, so
+ /// crates using `c_str!` macro don't have to enable `const_panic` feature.
+ #[doc(hidden)]
+ pub const fn from_bytes_with_nul_unwrap(bytes: &[u8]) -> &Self {
+ match Self::from_bytes_with_nul(bytes) {
+ Ok(v) => v,
+ Err(_) => panic!("string contains interior NUL"),
+ }
+ }
+
/// Creates a [`CStr`] from a `[u8]` without performing any additional
/// checks.
///
@@ -321,6 +333,26 @@ where
}
}

+/// 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 = $crate::str::CStr::from_bytes_with_nul_unwrap(S.as_bytes());
+ C
+ }};
+}
+
#[cfg(test)]
mod tests {
use super::*;
--
2.38.1


2022-11-10 17:33:24

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 05/28] rust: macros: add `concat_idents!` proc macro

From: Björn Roy Baron <[email protected]>

This macro provides similar functionality to the unstable feature
`concat_idents` without having to rely on it.

For instance:

let x_1 = 42;
let x_2 = concat_idents!(x, _1);
assert!(x_1 == x_2);

It has different behavior with respect to macro hygiene. Unlike
the unstable `concat_idents!` macro, it allows, for example,
referring to local variables by taking the span of the second
macro as span for the output identifier.

Signed-off-by: Björn Roy Baron <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/macros/concat_idents.rs | 23 +++++++++++++++++++
rust/macros/lib.rs | 44 ++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100644 rust/macros/concat_idents.rs

diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs
new file mode 100644
index 000000000000..3b5a9dd70e8a
--- /dev/null
+++ b/rust/macros/concat_idents.rs
@@ -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(&(a.to_string() + &b.to_string()), b.span());
+ TokenStream::from_iter([TokenTree::Ident(res)])
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 91764bfb1f89..15555e7ff487 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -2,6 +2,7 @@

//! Crate for all kernel procedural macros.

+mod concat_idents;
mod helpers;
mod module;

@@ -70,3 +71,46 @@ use proc_macro::TokenStream;
pub fn module(ts: TokenStream) -> TokenStream {
module::module(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)
+}
--
2.38.1


2022-11-10 17:59:44

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 22/28] rust: str: add `fmt!` macro

From: Wedson Almeida Filho <[email protected]>

Add the `fmt!` macro, which is a convenience alias for the Rust
`core::format_args!` macro.

For instance, it may be used to create a `CString`:

CString::try_from_fmt(fmt!("{}{}", "abc", 42))?

Signed-off-by: Wedson Almeida Filho <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 877148b77e71..ffac633423db 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -592,3 +592,9 @@ impl Deref for CString {
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)*) )
+}
--
2.38.1


2022-11-10 18:00:52

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 14/28] rust: str: add `BStr` type

From: Gary Guo <[email protected]>

Add the `BStr` type, which is a byte string without UTF-8
validity guarantee.

It is simply an alias to `[u8]`, but has a more evident
semantical meaning.

Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index e45ff220ae50..3aa1a0cb9bf8 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -4,6 +4,11 @@

use core::fmt;

+/// 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];
+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
///
/// It does not fail if callers write past the end of the buffer so that they can calculate the
--
2.38.1


2022-11-10 18:01:59

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 13/28] rust: alloc: add `Vec::try_with_capacity{,_in}()` constructors

Add `Vec::try_with_capacity()` and `Vec::try_with_capacity_in()` as
the fallible versions of `Vec::with_capacity()` and
`Vec::with_capacity_in()`, respectively.

The implementations follow the originals and use the previously
added `RawVec::try_with_capacity_in()`.

In turn, `Vec::try_with_capacity()` will be used to implement
the `CString` type (which wraps a `Vec<u8>`) in a later patch.

Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/alloc/raw_vec.rs | 1 -
rust/alloc/vec/mod.rs | 89 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/rust/alloc/raw_vec.rs b/rust/alloc/raw_vec.rs
index c342f3843972..eb77db5def55 100644
--- a/rust/alloc/raw_vec.rs
+++ b/rust/alloc/raw_vec.rs
@@ -135,7 +135,6 @@ impl<T, A: Allocator> RawVec<T, A> {

/// Like `try_with_capacity`, but parameterized over the choice of
/// allocator for the returned `RawVec`.
- #[allow(dead_code)]
#[inline]
pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)
diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs
index 540787804cc2..8ac6c1e3b2a8 100644
--- a/rust/alloc/vec/mod.rs
+++ b/rust/alloc/vec/mod.rs
@@ -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
--
2.38.1


2022-11-10 18:03:19

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 25/28] rust: add `build_error` crate

From: Gary Guo <[email protected]>

The `build_error` crate provides a function `build_error` which
will panic at compile-time if executed in const context and,
by default, will cause a build error if not executed at compile
time and the optimizer does not optimise away the call.

The `CONFIG_RUST_BUILD_ASSERT_ALLOW` kernel option allows to
relax the default build failure and convert it to a runtime
check. If the runtime check fails, `panic!` will be called.

Its functionality will be exposed to users as a couple macros in
the `kernel` crate in the following patch, thus some documentation
here refers to them for simplicity.

Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
lib/Kconfig.debug | 16 ++++++++++++++++
rust/Makefile | 22 +++++++++++++++++-----
rust/build_error.rs | 24 ++++++++++++++++++++++++
rust/exports.c | 5 +++++
scripts/generate_rust_analyzer.py | 8 +++++++-
5 files changed, 69 insertions(+), 6 deletions(-)
create mode 100644 rust/build_error.rs

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 29280072dc0e..452c9f06c2bc 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2801,6 +2801,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"
diff --git a/rust/Makefile b/rust/Makefile
index 7700d3853404..ff70c4c916f8 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -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)

diff --git a/rust/build_error.rs b/rust/build_error.rs
new file mode 100644
index 000000000000..0ff6b33059aa
--- /dev/null
+++ b/rust/build_error.rs
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Build-time error.
+//!
+//! This crate provides a function `build_error`, which will panic in
+//! compile-time if executed in 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, but those
+//! functions could still be called in the runtime).
+
+#![no_std]
+
+/// Panics if executed in const context, or triggers a build error if not.
+#[inline(never)]
+#[cold]
+#[export_name = "rust_build_error"]
+#[track_caller]
+pub const fn build_error(msg: &'static str) -> ! {
+ panic!("{}", msg);
+}
diff --git a/rust/exports.c b/rust/exports.c
index bb7cc64cecd0..83e2a7070cae 100644
--- a/rust/exports.c
+++ b/rust/exports.c
@@ -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
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index 75bb611bd751..ecc7ea9a4dcf 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -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"] = {
--
2.38.1


2022-11-10 18:03:29

by Miguel Ojeda

[permalink] [raw]
Subject: [PATCH v1 01/28] rust: prelude: split re-exports into groups

Split the prelude re-exports into groups: first the ones coming
from the `core` crate, then `alloc`, then our own crates and
finally the ones from modules from `kernel` itself (i.e. `super`).

We are doing this manually for the moment, but ideally, long-term,
this could be automated via `rustfmt` with options such as
`group_imports` and `imports_granularity` (both currently unstable).

Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 495e22250726..f8219285d8c0 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -11,10 +11,14 @@
//! 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 alloc::{boxed::Box, vec::Vec};
+
pub use macros::module;
+
+pub use super::{pr_emerg, pr_info};
+
+pub use super::error::{Error, Result};
+
+pub use super::ThisModule;
--
2.38.1


2022-11-10 18:38:38

by Boqun Feng

[permalink] [raw]
Subject: Re: [PATCH v1 02/28] rust: print: add more `pr_*!` levels

On Thu, Nov 10, 2022 at 05:41:14PM +0100, Miguel Ojeda wrote:
> Currently, only `pr_info!` (for the minimal sample) and
> `pr_emerg!` (for the panic handler) are there.
>
> Add the other levels as new macros, i.e. `pr_alert!`, `pr_crit!`,
> `pr_err!`, `pr_warn!`, `pr_notice!` and `pr_debug!`.
>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Co-developed-by: Gary Guo <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Boqun Feng <[email protected]>

Regards,
Boqun

> ---
> rust/kernel/prelude.rs | 2 +-
> rust/kernel/print.rs | 154 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 155 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index f8219285d8c0..6a1c6b38327f 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -17,7 +17,7 @@ pub use alloc::{boxed::Box, vec::Vec};
>
> pub use macros::module;
>
> -pub use super::{pr_emerg, pr_info};
> +pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
>
> pub use super::error::{Error, Result};
>
> diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
> index 55db5a1ba752..694f51c6da5c 100644
> --- a/rust/kernel/print.rs
> +++ b/rust/kernel/print.rs
> @@ -74,7 +74,13 @@ 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);
> }
>
> /// Prints a message via the kernel's [`_printk`].
> @@ -172,6 +178,126 @@ macro_rules! pr_emerg (
> )
> );
>
> +/// 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, $($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, $($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, $($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, $($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, $($arg)*)
> + )
> +);
> +
> /// Prints an info-level message (level 6).
> ///
> /// Use this level for informational messages.
> @@ -196,3 +322,31 @@ macro_rules! pr_info (
> $crate::print_macro!($crate::print::format_strings::INFO, $($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, $($arg)*)
> + }
> + )
> +);
> --
> 2.38.1
>

2022-11-10 18:41:40

by Boqun Feng

[permalink] [raw]
Subject: Re: [PATCH v1 01/28] rust: prelude: split re-exports into groups

On Thu, Nov 10, 2022 at 05:41:13PM +0100, Miguel Ojeda wrote:
> Split the prelude re-exports into groups: first the ones coming
> from the `core` crate, then `alloc`, then our own crates and
> finally the ones from modules from `kernel` itself (i.e. `super`).
>
> We are doing this manually for the moment, but ideally, long-term,
> this could be automated via `rustfmt` with options such as
> `group_imports` and `imports_granularity` (both currently unstable).
>
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Boqun Feng <[email protected]>

Regards,
Boqun

> ---
> rust/kernel/prelude.rs | 14 +++++++++-----
> 1 file changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index 495e22250726..f8219285d8c0 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -11,10 +11,14 @@
> //! 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 alloc::{boxed::Box, vec::Vec};
> +
> pub use macros::module;
> +
> +pub use super::{pr_emerg, pr_info};
> +
> +pub use super::error::{Error, Result};
> +
> +pub use super::ThisModule;
> --
> 2.38.1
>

2022-11-11 09:58:37

by Finn Behrens

[permalink] [raw]
Subject: Re: [PATCH v1 04/28] rust: samples: add `rust_print` example

On 10 Nov 2022, at 17:41, Miguel Ojeda wrote:

> Add example to exercise the printing macros (`pr_*!`) introduced
> in the previous patches.
>
> Signed-off-by: Miguel Ojeda <[email protected]>
Reviewed-by: Finn Behrens <[email protected]>

> ---
> samples/rust/Kconfig | 10 +++++++
> samples/rust/Makefile | 1 +
> samples/rust/rust_print.rs | 54 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 65 insertions(+)
> create mode 100644 samples/rust/rust_print.rs
>
> diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
> index 841e0906e943..b0f74a81c8f9 100644
> --- a/samples/rust/Kconfig
> +++ b/samples/rust/Kconfig
> @@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL
>
> If unsure, say N.
>
> +config SAMPLE_RUST_PRINT
> + tristate "Printing macros"
> + help
> + This option builds the Rust printing macros sample.
> +
> + To compile this as a module, choose M here:
> + the module will be called rust_print.
> +
> + If unsure, say N.
> +
> config SAMPLE_RUST_HOSTPROGS
> bool "Host programs"
> help
> diff --git a/samples/rust/Makefile b/samples/rust/Makefile
> index 1daba5f8658a..03086dabbea4 100644
> --- a/samples/rust/Makefile
> +++ b/samples/rust/Makefile
> @@ -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
> diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
> new file mode 100644
> index 000000000000..09f737790f3f
> --- /dev/null
> +++ b/samples/rust/rust_print.rs
> @@ -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: b"rust_print",
> + author: b"Rust for Linux Contributors",
> + description: b"Rust printing macros sample",
> + license: b"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");
Could be nice, to also have an int or similar as arg, but as internal the same macro is used as above, not really of interest.

Regards,
Finn
> +
> + Ok(RustPrint)
> + }
> +}
> +
> +impl Drop for RustPrint {
> + fn drop(&mut self) {
> + pr_info!("Rust printing macros sample (exit)\n");
> + }
> +}
> --
> 2.38.1

2022-11-11 10:17:26

by Finn Behrens

[permalink] [raw]
Subject: Re: [PATCH v1 05/28] rust: macros: add `concat_idents!` proc macro

On 10 Nov 2022, at 17:41, Miguel Ojeda wrote:

> From: Björn Roy Baron <[email protected]>
>
> This macro provides similar functionality to the unstable feature
> `concat_idents` without having to rely on it.
>
> For instance:
>
> let x_1 = 42;
> let x_2 = concat_idents!(x, _1);
> assert!(x_1 == x_2);
>
> It has different behavior with respect to macro hygiene. Unlike
> the unstable `concat_idents!` macro, it allows, for example,
> referring to local variables by taking the span of the second
> macro as span for the output identifier.
>
> Signed-off-by: Björn Roy Baron <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
Reviewed-by: Finn Behrens <[email protected]>

Regards,
Finn

> ---
> rust/macros/concat_idents.rs | 23 +++++++++++++++++++
> rust/macros/lib.rs | 44 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 67 insertions(+)
> create mode 100644 rust/macros/concat_idents.rs
>
> diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs
> new file mode 100644
> index 000000000000..3b5a9dd70e8a
> --- /dev/null
> +++ b/rust/macros/concat_idents.rs
> @@ -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(&(a.to_string() + &b.to_string()), b.span());
> + TokenStream::from_iter([TokenTree::Ident(res)])
> +}
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 91764bfb1f89..15555e7ff487 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -2,6 +2,7 @@
>
> //! Crate for all kernel procedural macros.
>
> +mod concat_idents;
> mod helpers;
> mod module;
>
> @@ -70,3 +71,46 @@ use proc_macro::TokenStream;
> pub fn module(ts: TokenStream) -> TokenStream {
> module::module(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)
> +}
> --
> 2.38.1

2022-11-11 10:38:45

by Finn Behrens

[permalink] [raw]
Subject: Re: [PATCH v1 10/28] rust: error: add `From` implementations for `Error`

On 10 Nov 2022, at 17:41, Miguel Ojeda wrote:

> From: Wedson Almeida Filho <[email protected]>
>
> Add a set of `From` implementations for the `Error` kernel type.
>
> These implementations allow to easily convert from standard Rust
> error types to the usual kernel errors based on one of the `E*`
> integer codes.
>
> On top of that, the question mark Rust operator (`?`) implicitly
> performs a conversion on the error value using the `From` trait
> when propagating. Thus it is extra convenient to use.
>
> For instance, a kernel function that needs to convert a `i64` into
> a `i32` and to bubble up the error as a kernel error may write:
>
> fn f(x: i64) -> Result<...> {
> ...
> let y = i32::try_from(x)?;
> ...
> }
>
> which will transform the `TryFromIntError` into an `Err(EINVAL)`.
>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Nándor István Krácser <[email protected]>
> Signed-off-by: Nándor István Krácser <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
Reviewed-by: Finn Behrens <[email protected]>

Regards,
Finn
> ---
> rust/kernel/error.rs | 45 +++++++++++++++++++++++++++++++++++++++++++-
> rust/kernel/lib.rs | 1 +
> 2 files changed, 45 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 861746f2422d..5b9751d7ff1d 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -4,7 +4,14 @@
> //!
> //! 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 {
> @@ -71,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.
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index abd46261d385..ffc6626a6d29 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -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;
> --
> 2.38.1

2022-11-11 18:09:17

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v1 04/28] rust: samples: add `rust_print` example

On Fri, Nov 11, 2022 at 10:40 AM Finn Behrens <[email protected]> wrote:
>
> Could be nice, to also have an int or similar as arg, but as internal the same macro is used as above, not really of interest.

Yeah, there are some integers above, but we can add more examples
later on, e.g. with custom kernel types too.

Thanks for the reviews in several patches, by the way!

Cheers,
Miguel

2022-11-14 14:43:05

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v1 05/28] rust: macros: add `concat_idents!` proc macro

On Thu, 10 Nov 2022 17:41:17 +0100
Miguel Ojeda <[email protected]> wrote:

> +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(&(a.to_string() + &b.to_string()), b.span());

Probably clearer to write `Ident::new(&format!("{a}{b}"), b.span())`
here?

Best,
Gary

2022-11-14 14:44:21

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v1 19/28] rust: str: add `c_str!` macro

On Thu, 10 Nov 2022 17:41:31 +0100
Miguel Ojeda <[email protected]> wrote:

> From: Gary Guo <[email protected]>
>
> Add `c_str!`, which is a convenience macro that creates a new `CStr`
> from a string literal.
>
> It is designed to be similar to a `str` in usage, and it is usable
> in const contexts, for instance:
>
> const X: &CStr = c_str!("Example");
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> rust/kernel/str.rs | 32 ++++++++++++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> index 3ed685cb5a3c..3fb73b888dce 100644
> --- a/rust/kernel/str.rs
> +++ b/rust/kernel/str.rs
> @@ -128,6 +128,18 @@ impl CStr {
> Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
> }
>
> + /// Creates a [`CStr`] from a `[u8]`, panic if input is not valid.
> + ///
> + /// This function is only meant to be used by `c_str!` macro, so
> + /// crates using `c_str!` macro don't have to enable `const_panic` feature.

I wrote this code when `const_panic` was not yet stable. Now we have
stable const panic this function could be removed.

> + #[doc(hidden)]
> + pub const fn from_bytes_with_nul_unwrap(bytes: &[u8]) -> &Self {
> + match Self::from_bytes_with_nul(bytes) {
> + Ok(v) => v,
> + Err(_) => panic!("string contains interior NUL"),
> + }
> + }
> +
> /// Creates a [`CStr`] from a `[u8]` without performing any additional
> /// checks.
> ///
> @@ -321,6 +333,26 @@ where
> }
> }
>
> +/// 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 = $crate::str::CStr::from_bytes_with_nul_unwrap(S.as_bytes());
> + C
> + }};
> +}

+#[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::*;


2022-11-14 14:46:37

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 04/28] rust: samples: add `rust_print` example

On Thu, Nov 10, 2022 at 05:41:16PM +0100, Miguel Ojeda wrote:
> Add example to exercise the printing macros (`pr_*!`) introduced
> in the previous patches.
>
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Wei Liu <[email protected]>

2022-11-14 14:59:33

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 07/28] rust: macros: take string literals in `module!`

On Thu, Nov 10, 2022 at 05:41:19PM +0100, Miguel Ojeda wrote:
> From: Gary Guo <[email protected]>
>
> Instead of taking binary string literals, take string ones instead,
> making it easier for users to define a module, i.e. instead of
> calling `module!` like:
>
> module! {
> ...
> name: b"rust_minimal",
> ...
> }
>
> now it is called as:
>
> module! {
> ...
> name: "rust_minimal",
> ...
> }
>
> Module names, aliases and license strings are restricted to
> ASCII only. However, the author and the description allows UTF-8.

What's the rationale behind allowing UTF-8? Why not stick with ASCII
only?

Thanks,
Wei.

2022-11-14 15:04:20

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 01/28] rust: prelude: split re-exports into groups

On Thu, Nov 10, 2022 at 05:41:13PM +0100, Miguel Ojeda wrote:
> Split the prelude re-exports into groups: first the ones coming
> from the `core` crate, then `alloc`, then our own crates and
> finally the ones from modules from `kernel` itself (i.e. `super`).
>
> We are doing this manually for the moment, but ideally, long-term,
> this could be automated via `rustfmt` with options such as
> `group_imports` and `imports_granularity` (both currently unstable).
>
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Wei Liu <[email protected]>

2022-11-14 15:04:36

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 02/28] rust: print: add more `pr_*!` levels

On Thu, Nov 10, 2022 at 05:41:14PM +0100, Miguel Ojeda wrote:
> Currently, only `pr_info!` (for the minimal sample) and
> `pr_emerg!` (for the panic handler) are there.
>
> Add the other levels as new macros, i.e. `pr_alert!`, `pr_crit!`,
> `pr_err!`, `pr_warn!`, `pr_notice!` and `pr_debug!`.
>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Co-developed-by: Gary Guo <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Wei Liu <[email protected]>

2022-11-14 15:04:46

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 27/28] rust: types: add `Either` type

On Thu, Nov 10, 2022 at 05:41:39PM +0100, Miguel Ojeda wrote:
> From: Wedson Almeida Filho <[email protected]>
>
> Introduce the new `types` module of the `kernel` crate with
> `Either` as its first type.
>
> `Either<L, R>` is a sum type that always holds either a value
> of type `L` (`Left` variant) or `R` (`Right` variant).
>
> For instance:
>
> struct Executor {
> queue: Either<BoxedQueue, &'static Queue>,
> }
>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Wei Liu <[email protected]>

2022-11-14 15:05:40

by Björn Roy Baron

[permalink] [raw]
Subject: Re: [PATCH v1 05/28] rust: macros: add `concat_idents!` proc macro

On Monday, November 14th, 2022 at 15:26, Gary Guo <[email protected]> wrote:


> On Thu, 10 Nov 2022 17:41:17 +0100
> Miguel Ojeda [email protected] wrote:
>
> > +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(&(a.to_string() + &b.to_string()), b.span());
>
>
> Probably clearer to write `Ident::new(&format!("{a}{b}"), b.span())`
> here?
>
> Best,
> Gary

I agree that is clearer. I hadn't considered that Ident implements Display when I wrote it.

Cheers,
Björn

2022-11-14 15:06:30

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 03/28] rust: print: add `pr_cont!` macro

On Thu, Nov 10, 2022 at 05:41:15PM +0100, Miguel Ojeda wrote:
> This level is a bit different from the rest since it does not
> pass the module name to the `_printk()` call.
>
> Thus add a new parameter to the general `print_macro!` to
> handle it differently.
>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Co-developed-by: Gary Guo <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Wei Liu <[email protected]>

2022-11-14 15:06:34

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v1 13/28] rust: alloc: add `Vec::try_with_capacity{,_in}()` constructors

On Thu, 10 Nov 2022 17:41:25 +0100
Miguel Ojeda <[email protected]> wrote:

> Add `Vec::try_with_capacity()` and `Vec::try_with_capacity_in()` as
> the fallible versions of `Vec::with_capacity()` and
> `Vec::with_capacity_in()`, respectively.
>
> The implementations follow the originals and use the previously
> added `RawVec::try_with_capacity_in()`.
>
> In turn, `Vec::try_with_capacity()` will be used to implement
> the `CString` type (which wraps a `Vec<u8>`) in a later patch.
>
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Gary Guo <[email protected]>

> ---
> rust/alloc/raw_vec.rs | 1 -
> rust/alloc/vec/mod.rs | 89 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 89 insertions(+), 1 deletion(-)
>
> diff --git a/rust/alloc/raw_vec.rs b/rust/alloc/raw_vec.rs
> index c342f3843972..eb77db5def55 100644
> --- a/rust/alloc/raw_vec.rs
> +++ b/rust/alloc/raw_vec.rs
> @@ -135,7 +135,6 @@ impl<T, A: Allocator> RawVec<T, A> {
>
> /// Like `try_with_capacity`, but parameterized over the choice of
> /// allocator for the returned `RawVec`.
> - #[allow(dead_code)]
> #[inline]
> pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
> Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)
> diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs
> index 540787804cc2..8ac6c1e3b2a8 100644
> --- a/rust/alloc/vec/mod.rs
> +++ b/rust/alloc/vec/mod.rs
> @@ -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


2022-11-14 15:07:26

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 25/28] rust: add `build_error` crate

On Thu, Nov 10, 2022 at 05:41:37PM +0100, Miguel Ojeda wrote:
> From: Gary Guo <[email protected]>
[...]
> diff --git a/rust/build_error.rs b/rust/build_error.rs
> new file mode 100644
> index 000000000000..0ff6b33059aa
> --- /dev/null
> +++ b/rust/build_error.rs
> @@ -0,0 +1,24 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Build-time error.
> +//!
> +//! This crate provides a function `build_error`, which will panic in

a const function `build_error`

Without this I read it as a "normal non-const function".

> +//! compile-time if executed in const context, and will cause a build error
> +//! if not executed at compile time and the optimizer does not optimise away the
> +//! call.
> +//!

I can work out what the code does, but I also happen to know what Rust's
const means and its behaviour in non-const context. Other kernel
developers may not. Even so the description is a bit difficult for me to
parse.

Maybe a few sentences about const and its behaviours can help?

Thanks,
Wei.

> +//! 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, but those
> +//! functions could still be called in the runtime).
> +
> +#![no_std]
> +
> +/// Panics if executed in const context, or triggers a build error if not.
> +#[inline(never)]
> +#[cold]
> +#[export_name = "rust_build_error"]
> +#[track_caller]
> +pub const fn build_error(msg: &'static str) -> ! {
> + panic!("{}", msg);
> +}

2022-11-14 15:08:46

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v1 28/28] rust: types: add `Opaque` type

On Thu, 10 Nov 2022 17:41:40 +0100
Miguel Ojeda <[email protected]> wrote:

> From: Wedson Almeida Filho <[email protected]>
>
> Add the `Opaque` type, which is meant to be used with FFI objects
> that are never interpreted by Rust code, e.g.:
>
> struct Waiter {
> completion: Opaque<bindings::completion>,
> next: *mut Waiter,
> }
>
> It has the advantage that the objects don't have to be
> zero-initialised before calling their init functions, making
> the code performance closer to C.
>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> rust/kernel/types.rs | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> index 3b0c44769708..e84e51ec9716 100644
> --- a/rust/kernel/types.rs
> +++ b/rust/kernel/types.rs
> @@ -2,6 +2,31 @@
>
> //! 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>>);

Shouldn't this be `UnsafeCell<MaybeUninit<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`.


2022-11-14 15:21:03

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v1 08/28] rust: error: declare errors using macro

On Thu, 10 Nov 2022 17:41:20 +0100
Miguel Ojeda <[email protected]> wrote:

> From: Finn Behrens <[email protected]>
>
> Add a macro to declare errors, which simplifies the work needed to
> add each one, avoids repetition of the code and makes it easier to
> change the way they are declared.
>
> Signed-off-by: Finn Behrens <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Gary Guo <[email protected]>

> ---
> rust/kernel/error.rs | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 466b2a8fe569..b843f3445483 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -8,8 +8,16 @@ use alloc::collections::TryReserveError;
>
> /// 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!(ENOMEM, "Out of memory.");
> }
>
> /// Generic integer kernel error.


2022-11-14 15:21:54

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v1 22/28] rust: str: add `fmt!` macro

On Thu, 10 Nov 2022 17:41:34 +0100
Miguel Ojeda <[email protected]> wrote:

> From: Wedson Almeida Filho <[email protected]>
>
> Add the `fmt!` macro, which is a convenience alias for the Rust
> `core::format_args!` macro.
>
> For instance, it may be used to create a `CString`:
>
> CString::try_from_fmt(fmt!("{}{}", "abc", 42))?
>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>

Reviewed-by: Gary Guo <[email protected]>

> ---
> rust/kernel/str.rs | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> index 877148b77e71..ffac633423db 100644
> --- a/rust/kernel/str.rs
> +++ b/rust/kernel/str.rs
> @@ -592,3 +592,9 @@ impl Deref for CString {
> 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)*) )
> +}


2022-11-14 15:23:05

by Gary Guo

[permalink] [raw]
Subject: Re: [PATCH v1 21/28] rust: str: add `CString` type

On Thu, 10 Nov 2022 17:41:33 +0100
Miguel Ojeda <[email protected]> wrote:

> From: Wedson Almeida Filho <[email protected]>
>
> Add the `CString` type, which is an owned string that is guaranteed
> to have exactly one `NUL` byte at the end, i.e. the owned equivalent
> to `CStr` introduced earlier.
>
> It is used for interoperability with kernel APIs that take C strings.
>
> In order to do so, implement the `RawFormatter::new()` constructor
> and the `RawFormatter::bytes_written()` method as well.
>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> rust/kernel/str.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 89 insertions(+), 2 deletions(-)
>
> diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> index db6473db31c6..877148b77e71 100644
> --- a/rust/kernel/str.rs
> +++ b/rust/kernel/str.rs
> @@ -2,6 +2,7 @@
>
> //! String representations.
>
> +use alloc::vec::Vec;
> use core::fmt::{self, Write};
> use core::ops::{self, Deref, Index};
>
> @@ -393,13 +394,22 @@ mod tests {
> /// 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
> @@ -438,6 +448,11 @@ impl RawFormatter {
> 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 {
> @@ -478,7 +493,6 @@ impl Formatter {
> ///
> /// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
> /// for the lifetime of the returned [`Formatter`].
> - #[allow(dead_code)]
> 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) })
> @@ -505,3 +519,76 @@ impl fmt::Write for Formatter {
> }
> }
> }
> +
> +/// 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")?;

I haven't checked the assembly, so this is possibly optimized out
already, but I feel that this line could be removed and we just use
`f.bytes_written() + 1` instead on the following line.

> + 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()) };

`f.bytes_written() <= size` does not always hold. It holds here because
otherwise the `?` operator above would return an error early
(guaranteed by `impl Write for Formatter`). I feel that this fact is
not clearly stated in the safety comment but I don't have a good way to
rephrase this either.

> +
> + // 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 _) };

How about just use `if buf[..(f.bytes_written() - 1)].contains(&0) {`
here? libcore specialises `<[u8]>::contains` to use a faster search
than `.iter().any(...)`.

> + 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()) }
> + }
> +}


2022-11-14 17:23:37

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v1 07/28] rust: macros: take string literals in `module!`

On Mon, Nov 14, 2022 at 3:47 PM Wei Liu <[email protected]> wrote:
>
> What's the rationale behind allowing UTF-8? Why not stick with ASCII
> only?

The reason is that there are already some cases on the C side.

For authors, there are about 158 non-ASCII in the kernel tree (if I
grepped correctly), e.g.:

MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <[email protected]>");
MODULE_AUTHOR("Martin Povišer <[email protected]>");
MODULE_AUTHOR("Jérôme Pouiller <[email protected]>");
MODULE_AUTHOR("Uwe Kleine-König <[email protected]>");

There are also a few descriptions too, e.g.:

MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
MODULE_DESCRIPTION("NHPoly1305 ε-almost-∆-universal hash function");

Cheers,
Miguel

2022-11-14 17:53:23

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 07/28] rust: macros: take string literals in `module!`

On Mon, Nov 14, 2022 at 05:46:05PM +0100, Miguel Ojeda wrote:
> On Mon, Nov 14, 2022 at 3:47 PM Wei Liu <[email protected]> wrote:
> >
> > What's the rationale behind allowing UTF-8? Why not stick with ASCII
> > only?
>
> The reason is that there are already some cases on the C side.
>
> For authors, there are about 158 non-ASCII in the kernel tree (if I
> grepped correctly), e.g.:
>
> MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <[email protected]>");
> MODULE_AUTHOR("Martin Povišer <[email protected]>");
> MODULE_AUTHOR("Jérôme Pouiller <[email protected]>");
> MODULE_AUTHOR("Uwe Kleine-König <[email protected]>");
>
> There are also a few descriptions too, e.g.:
>
> MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
> MODULE_DESCRIPTION("NHPoly1305 ε-almost-∆-universal hash function");

Okay. That's fair enough.

Thanks,
Wei.

>
> Cheers,
> Miguel

2022-11-14 17:53:37

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v1 05/28] rust: macros: add `concat_idents!` proc macro

On Mon, Nov 14, 2022 at 3:26 PM Gary Guo <[email protected]> wrote:
>
> Probably clearer to write `Ident::new(&format!("{a}{b}"), b.span())`
> here?

Same as Björn, it also looks better to me. I will update it.

Cheers,
Miguel

2022-11-14 18:35:58

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v1 25/28] rust: add `build_error` crate

On Mon, Nov 14, 2022 at 3:30 PM Wei Liu <[email protected]> wrote:
>
> a const function `build_error`
>
> Without this I read it as a "normal non-const function".

Sounds good to me, especially given the sentence that goes after that.

(I think "function" does not imply one or the other in principle. To
me, `const` ones are normal too, in the sense that they can be used as
any other function.)

> I can work out what the code does, but I also happen to know what Rust's
> const means and its behaviour in non-const context. Other kernel
> developers may not. Even so the description is a bit difficult for me to
> parse.
>
> Maybe a few sentences about const and its behaviours can help?

Not sure if we should explain language details in the documentation of
particular functions. But I agree this case is a bit special.

What about linking the Rust reference/book/... (so that those terms
are clickable when rendered)?

Thanks a lot for the reviews, Wei!

Cheers,
Miguel

2022-11-14 18:52:25

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v1 19/28] rust: str: add `c_str!` macro

On Mon, Nov 14, 2022 at 3:39 PM Gary Guo <[email protected]> wrote:
>
> I wrote this code when `const_panic` was not yet stable. Now we have
> stable const panic this function could be removed.

Indeed, we also took advantage of it in `static_assert!` when it got
stabilized but not here. I will update it.

Cheers,
Miguel

2022-11-14 20:18:12

by Sergio González Collado

[permalink] [raw]
Subject: Re: [PATCH v1 02/28] rust: print: add more `pr_*!` levels

On Mon, 14 Nov 2022 at 09:43, Wei Liu <[email protected]> wrote:
>
> On Thu, Nov 10, 2022 at 05:41:14PM +0100, Miguel Ojeda wrote:
> > Currently, only `pr_info!` (for the minimal sample) and
> > `pr_emerg!` (for the panic handler) are there.
> >
> > Add the other levels as new macros, i.e. `pr_alert!`, `pr_crit!`,
> > `pr_err!`, `pr_warn!`, `pr_notice!` and `pr_debug!`.
> >
> > Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> > Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> > Co-developed-by: Wedson Almeida Filho <[email protected]>
> > Signed-off-by: Wedson Almeida Filho <[email protected]>
> > Co-developed-by: Gary Guo <[email protected]>
> > Signed-off-by: Gary Guo <[email protected]>
> > Signed-off-by: Miguel Ojeda <[email protected]>
>
> Reviewed-by: Wei Liu <[email protected]>

Reviewed-by: Sergio Gonzalez Collado <[email protected]>

2022-11-14 20:36:50

by Sergio González Collado

[permalink] [raw]
Subject: Re: [PATCH v1 03/28] rust: print: add `pr_cont!` macro

On Thu, 10 Nov 2022 at 11:43, Miguel Ojeda <[email protected]> wrote:
>
> This level is a bit different from the rest since it does not
> pass the module name to the `_printk()` call.
>
> Thus add a new parameter to the general `print_macro!` to
> handle it differently.
>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Co-developed-by: Gary Guo <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> rust/kernel/print.rs | 72 ++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 63 insertions(+), 9 deletions(-)
>
> diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
> index 694f51c6da5c..29bf9c2e8aee 100644
> --- a/rust/kernel/print.rs
> +++ b/rust/kernel/print.rs
> @@ -81,6 +81,7 @@ pub mod format_strings {
> 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`].
> @@ -111,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.
> @@ -120,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
> @@ -134,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
> @@ -174,7 +202,7 @@ 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)*)
> )
> );
>
> @@ -198,7 +226,7 @@ macro_rules! pr_emerg (
> #[macro_export]
> macro_rules! pr_alert (
> ($($arg:tt)*) => (
> - $crate::print_macro!($crate::print::format_strings::ALERT, $($arg)*)
> + $crate::print_macro!($crate::print::format_strings::ALERT, false, $($arg)*)
> )
> );
>
> @@ -222,7 +250,7 @@ macro_rules! pr_alert (
> #[macro_export]
> macro_rules! pr_crit (
> ($($arg:tt)*) => (
> - $crate::print_macro!($crate::print::format_strings::CRIT, $($arg)*)
> + $crate::print_macro!($crate::print::format_strings::CRIT, false, $($arg)*)
> )
> );
>
> @@ -246,7 +274,7 @@ macro_rules! pr_crit (
> #[macro_export]
> macro_rules! pr_err (
> ($($arg:tt)*) => (
> - $crate::print_macro!($crate::print::format_strings::ERR, $($arg)*)
> + $crate::print_macro!($crate::print::format_strings::ERR, false, $($arg)*)
> )
> );
>
> @@ -270,7 +298,7 @@ macro_rules! pr_err (
> #[macro_export]
> macro_rules! pr_warn (
> ($($arg:tt)*) => (
> - $crate::print_macro!($crate::print::format_strings::WARNING, $($arg)*)
> + $crate::print_macro!($crate::print::format_strings::WARNING, false, $($arg)*)
> )
> );
>
> @@ -294,7 +322,7 @@ macro_rules! pr_warn (
> #[macro_export]
> macro_rules! pr_notice (
> ($($arg:tt)*) => (
> - $crate::print_macro!($crate::print::format_strings::NOTICE, $($arg)*)
> + $crate::print_macro!($crate::print::format_strings::NOTICE, false, $($arg)*)
> )
> );
>
> @@ -319,7 +347,7 @@ macro_rules! pr_notice (
> #[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)*)
> )
> );
>
> @@ -346,7 +374,33 @@ macro_rules! pr_info (
> macro_rules! pr_debug (
> ($($arg:tt)*) => (
> if cfg!(debug_assertions) {
> - $crate::print_macro!($crate::print::format_strings::DEBUG, $($arg)*)
> + $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)*)
> + )
> +);
> --
> 2.38.1
>
Reviewed-by: Sergio González Collado <[email protected]>

2022-11-14 20:38:01

by Sergio González Collado

[permalink] [raw]
Subject: Re: [PATCH v1 06/28] rust: macros: add `#[vtable]` proc macro

On Thu, 10 Nov 2022 at 11:43, Miguel Ojeda <[email protected]> wrote:
>
> From: Gary Guo <[email protected]>
>
> This procedural macro attribute provides a simple way to declare
> a trait with a set of operations that later users can partially
> implement, providing compile-time `HAS_*` boolean associated
> constants that indicate whether a particular operation was overridden.
>
> This is useful as the Rust counterpart to structs like
> `file_operations` where some pointers may be `NULL`, indicating
> an operation is not provided.
>
> For instance:
>
> #[vtable]
> trait Operations {
> fn read(...) -> Result<usize> {
> Err(EINVAL)
> }
>
> fn write(...) -> Result<usize> {
> Err(EINVAL)
> }
> }
>
> #[vtable]
> impl Operations for S {
> fn read(...) -> Result<usize> {
> ...
> }
> }
>
> assert_eq!(<S as Operations>::HAS_READ, true);
> assert_eq!(<S as Operations>::HAS_WRITE, false);
>
> Signed-off-by: Gary Guo <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> rust/kernel/prelude.rs | 2 +-
> rust/macros/lib.rs | 52 +++++++++++++++++++++++
> rust/macros/vtable.rs | 95 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 148 insertions(+), 1 deletion(-)
> create mode 100644 rust/macros/vtable.rs
>
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index 6a1c6b38327f..7c4c35bf3c66 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -15,7 +15,7 @@ pub use core::pin::Pin;
>
> pub use alloc::{boxed::Box, vec::Vec};
>
> -pub use macros::module;
> +pub use macros::{module, vtable};
>
> pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
>
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 15555e7ff487..e40caaf0a656 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -5,6 +5,7 @@
> mod concat_idents;
> mod helpers;
> mod module;
> +mod vtable;
>
> use proc_macro::TokenStream;
>
> @@ -72,6 +73,57 @@ 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
> diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs
> new file mode 100644
> index 000000000000..34d5e7fb5768
> --- /dev/null
> +++ b/rust/macros/vtable.rs
> @@ -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()
> +}
> --
> 2.38.1
>
Reviewed-by: Sergio González Collado <[email protected]>

2022-11-14 21:59:38

by Wei Liu

[permalink] [raw]
Subject: Re: [PATCH v1 25/28] rust: add `build_error` crate

On Mon, Nov 14, 2022 at 07:22:02PM +0100, Miguel Ojeda wrote:
> On Mon, Nov 14, 2022 at 3:30 PM Wei Liu <[email protected]> wrote:
> >
> > a const function `build_error`
> >
> > Without this I read it as a "normal non-const function".
>
> Sounds good to me, especially given the sentence that goes after that.
>
> (I think "function" does not imply one or the other in principle. To
> me, `const` ones are normal too, in the sense that they can be used as
> any other function.)
>
> > I can work out what the code does, but I also happen to know what Rust's
> > const means and its behaviour in non-const context. Other kernel
> > developers may not. Even so the description is a bit difficult for me to
> > parse.
> >
> > Maybe a few sentences about const and its behaviours can help?
>
> Not sure if we should explain language details in the documentation of
> particular functions. But I agree this case is a bit special.
>
> What about linking the Rust reference/book/... (so that those terms
> are clickable when rendered)?

I think this works too.

>
> Thanks a lot for the reviews, Wei!
>

You're welcome.

Thanks,
Wei.

> Cheers,
> Miguel

2022-11-21 23:02:53

by Sergio González Collado

[permalink] [raw]
Subject: Re: [PATCH v1 04/28] rust: samples: add `rust_print` example

On Thu, 10 Nov 2022 at 17:43, Miguel Ojeda <[email protected]> wrote:
>
> Add example to exercise the printing macros (`pr_*!`) introduced
> in the previous patches.
>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> samples/rust/Kconfig | 10 +++++++
> samples/rust/Makefile | 1 +
> samples/rust/rust_print.rs | 54 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 65 insertions(+)
> create mode 100644 samples/rust/rust_print.rs
>
> diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
> index 841e0906e943..b0f74a81c8f9 100644
> --- a/samples/rust/Kconfig
> +++ b/samples/rust/Kconfig
> @@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL
>
> If unsure, say N.
>
> +config SAMPLE_RUST_PRINT
> + tristate "Printing macros"
> + help
> + This option builds the Rust printing macros sample.
> +
> + To compile this as a module, choose M here:
> + the module will be called rust_print.
> +
> + If unsure, say N.
> +
> config SAMPLE_RUST_HOSTPROGS
> bool "Host programs"
> help
> diff --git a/samples/rust/Makefile b/samples/rust/Makefile
> index 1daba5f8658a..03086dabbea4 100644
> --- a/samples/rust/Makefile
> +++ b/samples/rust/Makefile
> @@ -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
> diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
> new file mode 100644
> index 000000000000..09f737790f3f
> --- /dev/null
> +++ b/samples/rust/rust_print.rs
> @@ -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: b"rust_print",
> + author: b"Rust for Linux Contributors",
> + description: b"Rust printing macros sample",
> + license: b"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");
> + }
> +}
> --
> 2.38.1
>

Tested-by: Sergio González Collado <[email protected]>