summaryrefslogtreecommitdiff
path: root/rust/macros
diff options
context:
space:
mode:
Diffstat (limited to 'rust/macros')
-rw-r--r--rust/macros/export.rs29
-rw-r--r--rust/macros/helpers.rs108
-rw-r--r--rust/macros/kunit.rs182
-rw-r--r--rust/macros/lib.rs304
-rw-r--r--rust/macros/module.rs270
-rw-r--r--rust/macros/paste.rs15
-rw-r--r--rust/macros/pin_data.rs127
-rw-r--r--rust/macros/pinned_drop.rs49
-rw-r--r--rust/macros/quote.rs28
-rw-r--r--rust/macros/zeroable.rs72
10 files changed, 614 insertions, 570 deletions
diff --git a/rust/macros/export.rs b/rust/macros/export.rs
new file mode 100644
index 000000000000..a08f6337d5c8
--- /dev/null
+++ b/rust/macros/export.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use crate::helpers::function_name;
+use proc_macro::TokenStream;
+
+/// Please see [`crate::export`] for documentation.
+pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream {
+ let Some(name) = function_name(ts.clone()) else {
+ return "::core::compile_error!(\"The #[export] attribute must be used on a function.\");"
+ .parse::<TokenStream>()
+ .unwrap();
+ };
+
+ // This verifies that the function has the same signature as the declaration generated by
+ // bindgen. It makes use of the fact that all branches of an if/else must have the same type.
+ let signature_check = quote!(
+ const _: () = {
+ if true {
+ ::kernel::bindings::#name
+ } else {
+ #name
+ };
+ };
+ );
+
+ let no_mangle = quote!(#[no_mangle]);
+
+ TokenStream::from_iter([signature_check, no_mangle, ts])
+}
diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs
index afb0f2e3a36a..e2602be402c1 100644
--- a/rust/macros/helpers.rs
+++ b/rust/macros/helpers.rs
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-use proc_macro::{token_stream, Group, Punct, Spacing, TokenStream, TokenTree};
+use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree};
pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
if let Some(TokenTree::Ident(ident)) = it.next() {
@@ -70,86 +70,36 @@ pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
}
}
-pub(crate) struct Generics {
- pub(crate) impl_generics: Vec<TokenTree>,
- pub(crate) ty_generics: Vec<TokenTree>,
-}
-
-/// Parses the given `TokenStream` into `Generics` and the rest.
-///
-/// The generics are not present in the rest, but a where clause might remain.
-pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
- // `impl_generics`, the declared generics with their bounds.
- let mut impl_generics = vec![];
- // Only the names of the generics, without any bounds.
- let mut ty_generics = vec![];
- // Tokens not related to the generics e.g. the `where` token and definition.
- let mut rest = vec![];
- // The current level of `<`.
- let mut nesting = 0;
- let mut toks = input.into_iter();
- // If we are at the beginning of a generic parameter.
- let mut at_start = true;
- for tt in &mut toks {
- match tt.clone() {
- TokenTree::Punct(p) if p.as_char() == '<' => {
- if nesting >= 1 {
- // This is inside of the generics and part of some bound.
- impl_generics.push(tt);
- }
- nesting += 1;
- }
- TokenTree::Punct(p) if p.as_char() == '>' => {
- // This is a parsing error, so we just end it here.
- if nesting == 0 {
- break;
- } else {
- nesting -= 1;
- if nesting >= 1 {
- // We are still inside of the generics and part of some bound.
- impl_generics.push(tt);
- }
- if nesting == 0 {
- break;
- }
- }
- }
- tt => {
- if nesting == 1 {
- // Here depending on the token, it might be a generic variable name.
- match &tt {
- // Ignore const.
- TokenTree::Ident(i) if i.to_string() == "const" => {}
- TokenTree::Ident(_) if at_start => {
- ty_generics.push(tt.clone());
- // We also already push the `,` token, this makes it easier to append
- // generics.
- ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
- at_start = false;
- }
- TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
- // Lifetimes begin with `'`.
- TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
- ty_generics.push(tt.clone());
- }
- _ => {}
- }
- }
- if nesting >= 1 {
- impl_generics.push(tt);
- } else if nesting == 0 {
- // If we haven't entered the generics yet, we still want to keep these tokens.
- rest.push(tt);
+/// Given a function declaration, finds the name of the function.
+pub(crate) fn function_name(input: TokenStream) -> Option<Ident> {
+ let mut input = input.into_iter();
+ while let Some(token) = input.next() {
+ match token {
+ TokenTree::Ident(i) if i.to_string() == "fn" => {
+ if let Some(TokenTree::Ident(i)) = input.next() {
+ return Some(i);
}
+ return None;
}
+ _ => continue,
}
}
- rest.extend(toks);
- (
- Generics {
- impl_generics,
- ty_generics,
- },
- rest,
- )
+ None
+}
+
+pub(crate) fn file() -> String {
+ #[cfg(not(CONFIG_RUSTC_HAS_SPAN_FILE))]
+ {
+ proc_macro::Span::call_site()
+ .source_file()
+ .path()
+ .to_string_lossy()
+ .into_owned()
+ }
+
+ #[cfg(CONFIG_RUSTC_HAS_SPAN_FILE)]
+ #[allow(clippy::incompatible_msrv)]
+ {
+ proc_macro::Span::call_site().file()
+ }
}
diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs
new file mode 100644
index 000000000000..81d18149a0cc
--- /dev/null
+++ b/rust/macros/kunit.rs
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Procedural macro to run KUnit tests using a user-space like syntax.
+//!
+//! Copyright (c) 2023 José Expósito <jose.exposito89@gmail.com>
+
+use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
+use std::fmt::Write;
+
+pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
+ let attr = attr.to_string();
+
+ if attr.is_empty() {
+ panic!("Missing test name in `#[kunit_tests(test_name)]` macro")
+ }
+
+ if attr.len() > 255 {
+ panic!("The test suite name `{attr}` exceeds the maximum length of 255 bytes")
+ }
+
+ let mut tokens: Vec<_> = ts.into_iter().collect();
+
+ // Scan for the `mod` keyword.
+ tokens
+ .iter()
+ .find_map(|token| match token {
+ TokenTree::Ident(ident) => match ident.to_string().as_str() {
+ "mod" => Some(true),
+ _ => None,
+ },
+ _ => None,
+ })
+ .expect("`#[kunit_tests(test_name)]` attribute should only be applied to modules");
+
+ // 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 module"),
+ };
+
+ // Get the functions set as tests. Search for `[test]` -> `fn`.
+ let mut body_it = body.stream().into_iter();
+ let mut tests = Vec::new();
+ while let Some(token) = body_it.next() {
+ match token {
+ TokenTree::Group(ident) if ident.to_string() == "[test]" => match body_it.next() {
+ Some(TokenTree::Ident(ident)) if ident.to_string() == "fn" => {
+ let test_name = match body_it.next() {
+ Some(TokenTree::Ident(ident)) => ident.to_string(),
+ _ => continue,
+ };
+ tests.push(test_name);
+ }
+ _ => continue,
+ },
+ _ => (),
+ }
+ }
+
+ // Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration.
+ let config_kunit = "#[cfg(CONFIG_KUNIT=\"y\")]".to_owned().parse().unwrap();
+ tokens.insert(
+ 0,
+ TokenTree::Group(Group::new(Delimiter::None, config_kunit)),
+ );
+
+ // Generate the test KUnit test suite and a test case for each `#[test]`.
+ // The code generated for the following test module:
+ //
+ // ```
+ // #[kunit_tests(kunit_test_suit_name)]
+ // mod tests {
+ // #[test]
+ // fn foo() {
+ // assert_eq!(1, 1);
+ // }
+ //
+ // #[test]
+ // fn bar() {
+ // assert_eq!(2, 2);
+ // }
+ // }
+ // ```
+ //
+ // Looks like:
+ //
+ // ```
+ // unsafe extern "C" fn kunit_rust_wrapper_foo(_test: *mut ::kernel::bindings::kunit) { foo(); }
+ // unsafe extern "C" fn kunit_rust_wrapper_bar(_test: *mut ::kernel::bindings::kunit) { bar(); }
+ //
+ // static mut TEST_CASES: [::kernel::bindings::kunit_case; 3] = [
+ // ::kernel::kunit::kunit_case(::kernel::c_str!("foo"), kunit_rust_wrapper_foo),
+ // ::kernel::kunit::kunit_case(::kernel::c_str!("bar"), kunit_rust_wrapper_bar),
+ // ::kernel::kunit::kunit_case_null(),
+ // ];
+ //
+ // ::kernel::kunit_unsafe_test_suite!(kunit_test_suit_name, TEST_CASES);
+ // ```
+ let mut kunit_macros = "".to_owned();
+ let mut test_cases = "".to_owned();
+ let mut assert_macros = "".to_owned();
+ let path = crate::helpers::file();
+ for test in &tests {
+ let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}");
+ // An extra `use` is used here to reduce the length of the message.
+ let kunit_wrapper = format!(
+ "unsafe extern \"C\" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit) {{ use ::kernel::kunit::is_test_result_ok; assert!(is_test_result_ok({test}())); }}",
+ );
+ writeln!(kunit_macros, "{kunit_wrapper}").unwrap();
+ writeln!(
+ test_cases,
+ " ::kernel::kunit::kunit_case(::kernel::c_str!(\"{test}\"), {kunit_wrapper_fn_name}),"
+ )
+ .unwrap();
+ writeln!(
+ assert_macros,
+ r#"
+/// Overrides the usual [`assert!`] macro with one that calls KUnit instead.
+#[allow(unused)]
+macro_rules! assert {{
+ ($cond:expr $(,)?) => {{{{
+ kernel::kunit_assert!("{test}", "{path}", 0, $cond);
+ }}}}
+}}
+
+/// Overrides the usual [`assert_eq!`] macro with one that calls KUnit instead.
+#[allow(unused)]
+macro_rules! assert_eq {{
+ ($left:expr, $right:expr $(,)?) => {{{{
+ kernel::kunit_assert_eq!("{test}", "{path}", 0, $left, $right);
+ }}}}
+}}
+ "#
+ )
+ .unwrap();
+ }
+
+ writeln!(kunit_macros).unwrap();
+ writeln!(
+ kunit_macros,
+ "static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];",
+ tests.len() + 1
+ )
+ .unwrap();
+
+ writeln!(
+ kunit_macros,
+ "::kernel::kunit_unsafe_test_suite!({attr}, TEST_CASES);"
+ )
+ .unwrap();
+
+ // Remove the `#[test]` macros.
+ // We do this at a token level, in order to preserve span information.
+ let mut new_body = vec![];
+ let mut body_it = body.stream().into_iter();
+
+ while let Some(token) = body_it.next() {
+ match token {
+ TokenTree::Punct(ref c) if c.as_char() == '#' => match body_it.next() {
+ Some(TokenTree::Group(group)) if group.to_string() == "[test]" => (),
+ Some(next) => {
+ new_body.extend([token, next]);
+ }
+ _ => {
+ new_body.push(token);
+ }
+ },
+ _ => {
+ new_body.push(token);
+ }
+ }
+ }
+
+ let mut final_body = TokenStream::new();
+ final_body.extend::<TokenStream>(assert_macros.parse().unwrap());
+ final_body.extend(new_body);
+ final_body.extend::<TokenStream>(kunit_macros.parse().unwrap());
+
+ tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, final_body)));
+
+ tokens.into_iter().collect()
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index f489f3157383..fa847cf3a9b5 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -2,16 +2,24 @@
//! Crate for all kernel procedural macros.
+// When fixdep scans this, it will find this string `CONFIG_RUSTC_VERSION_TEXT`
+// and thus add a dependency on `include/config/RUSTC_VERSION_TEXT`, which is
+// touched by Kconfig when the version string from the compiler changes.
+
+// Stable since Rust 1.88.0 under a different name, `proc_macro_span_file`,
+// which was added in Rust 1.88.0. This is why `cfg_attr` is used here, i.e.
+// to avoid depending on the full `proc_macro_span` on Rust >= 1.88.0.
+#![cfg_attr(not(CONFIG_RUSTC_HAS_SPAN_FILE), feature(proc_macro_span))]
+
#[macro_use]
mod quote;
mod concat_idents;
+mod export;
mod helpers;
+mod kunit;
mod module;
mod paste;
-mod pin_data;
-mod pinned_drop;
mod vtable;
-mod zeroable;
use proc_macro::TokenStream;
@@ -26,54 +34,70 @@ use proc_macro::TokenStream;
///
/// # Examples
///
-/// ```ignore
+/// ```
/// use kernel::prelude::*;
///
/// module!{
/// type: MyModule,
/// name: "my_kernel_module",
-/// author: "Rust for Linux Contributors",
+/// authors: ["Rust for Linux Contributors"],
/// description: "My very own kernel module!",
/// license: "GPL",
-/// params: {
-/// my_i32: i32 {
-/// default: 42,
-/// permissions: 0o000,
-/// description: "Example of i32",
-/// },
-/// writeable_i32: i32 {
-/// default: 42,
-/// permissions: 0o644,
-/// description: "Example of i32",
-/// },
-/// },
+/// alias: ["alternate_module_name"],
/// }
///
-/// struct MyModule;
+/// struct MyModule(i32);
///
/// impl kernel::Module for MyModule {
-/// fn init() -> Result<Self> {
-/// // If the parameter is writeable, then the kparam lock must be
-/// // taken to read the parameter:
-/// {
-/// let lock = THIS_MODULE.kernel_param_lock();
-/// pr_info!("i32 param is: {}\n", writeable_i32.read(&lock));
-/// }
-/// // If the parameter is read only, it can be read without locking
-/// // the kernel parameters:
-/// pr_info!("i32 param is: {}\n", my_i32.read());
+/// fn init(_module: &'static ThisModule) -> Result<Self> {
+/// let foo: i32 = 42;
+/// pr_info!("I contain: {}\n", foo);
+/// Ok(Self(foo))
+/// }
+/// }
+/// # fn main() {}
+/// ```
+///
+/// ## Firmware
+///
+/// The following example shows how to declare a kernel module that needs
+/// to load binary firmware files. You need to specify the file names of
+/// the firmware in the `firmware` field. The information is embedded
+/// in the `modinfo` section of the kernel module. For example, a tool to
+/// build an initramfs uses this information to put the firmware files into
+/// the initramfs image.
+///
+/// ```
+/// use kernel::prelude::*;
+///
+/// module!{
+/// type: MyDeviceDriverModule,
+/// name: "my_device_driver_module",
+/// authors: ["Rust for Linux Contributors"],
+/// description: "My device driver requires firmware",
+/// license: "GPL",
+/// firmware: ["my_device_firmware1.bin", "my_device_firmware2.bin"],
+/// }
+///
+/// struct MyDeviceDriverModule;
+///
+/// impl kernel::Module for MyDeviceDriverModule {
+/// fn init(_module: &'static ThisModule) -> Result<Self> {
/// Ok(Self)
/// }
/// }
+/// # fn main() {}
/// ```
///
/// # Supported argument types
/// - `type`: type which implements the [`Module`] trait (required).
-/// - `name`: byte array of the name of the kernel module (required).
-/// - `author`: byte array of the author of the kernel module.
-/// - `description`: byte array of the description of the kernel module.
-/// - `license`: byte array of the license of the kernel module (required).
-/// - `alias`: byte array of alias name of the kernel module.
+/// - `name`: ASCII string literal of the name of the kernel module (required).
+/// - `authors`: array of ASCII string literals of the authors of the kernel module.
+/// - `description`: string literal of the description of the kernel module.
+/// - `license`: ASCII string literal of the license of the kernel module (required).
+/// - `alias`: array of ASCII string literals of the alias names of the kernel module.
+/// - `firmware`: array of ASCII string literals of the firmware files of
+/// the kernel module.
#[proc_macro]
pub fn module(ts: TokenStream) -> TokenStream {
module::module(ts)
@@ -103,12 +127,12 @@ pub fn module(ts: TokenStream) -> TokenStream {
/// used on the Rust side, it should not be possible to call the default
/// implementation. This is done to ensure that we call the vtable methods
/// through the C vtable, and not through the Rust vtable. Therefore, the
-/// default implementation should call `kernel::build_error`, which prevents
+/// default implementation should call `build_error!`, which prevents
/// calls to this function at compile time:
///
/// ```compile_fail
-/// # use kernel::error::VTABLE_DEFAULT_ERROR;
-/// kernel::build_error(VTABLE_DEFAULT_ERROR)
+/// # // Intentionally missing `use`s to simplify `rusttest`.
+/// build_error!(VTABLE_DEFAULT_ERROR)
/// ```
///
/// Note that you might need to import [`kernel::error::VTABLE_DEFAULT_ERROR`].
@@ -117,7 +141,7 @@ pub fn module(ts: TokenStream) -> TokenStream {
///
/// # Examples
///
-/// ```ignore
+/// ```
/// use kernel::error::VTABLE_DEFAULT_ERROR;
/// use kernel::prelude::*;
///
@@ -125,11 +149,11 @@ pub fn module(ts: TokenStream) -> TokenStream {
/// #[vtable]
/// pub trait Operations: Send + Sync + Sized {
/// fn foo(&self) -> Result<()> {
-/// kernel::build_error(VTABLE_DEFAULT_ERROR)
+/// build_error!(VTABLE_DEFAULT_ERROR)
/// }
///
/// fn bar(&self) -> Result<()> {
-/// kernel::build_error(VTABLE_DEFAULT_ERROR)
+/// build_error!(VTABLE_DEFAULT_ERROR)
/// }
/// }
///
@@ -154,6 +178,29 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
vtable::vtable(attr, ts)
}
+/// Export a function so that C code can call it via a header file.
+///
+/// Functions exported using this macro can be called from C code using the declaration in the
+/// appropriate header file. It should only be used in cases where C calls the function through a
+/// header file; cases where C calls into Rust via a function pointer in a vtable (such as
+/// `file_operations`) should not use this macro.
+///
+/// This macro has the following effect:
+///
+/// * Disables name mangling for this function.
+/// * Verifies at compile-time that the function signature matches the declaration in the header
+/// file.
+///
+/// You must declare the signature of the Rust function in a header file that is included by
+/// `rust/bindings/bindings_helper.h`.
+///
+/// This macro is *not* the same as the C macros `EXPORT_SYMBOL_*`. All Rust symbols are currently
+/// automatically exported with `EXPORT_SYMBOL_GPL`.
+#[proc_macro_attribute]
+pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream {
+ export::export(attr, ts)
+}
+
/// Concatenate two identifiers.
///
/// This is useful in macros that need to declare or reference items with names
@@ -162,12 +209,27 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
///
/// # Examples
///
-/// ```ignore
-/// use kernel::macro::concat_idents;
+/// ```
+/// # const binder_driver_return_protocol_BR_OK: u32 = 0;
+/// # const binder_driver_return_protocol_BR_ERROR: u32 = 1;
+/// # const binder_driver_return_protocol_BR_TRANSACTION: u32 = 2;
+/// # const binder_driver_return_protocol_BR_REPLY: u32 = 3;
+/// # const binder_driver_return_protocol_BR_DEAD_REPLY: u32 = 4;
+/// # const binder_driver_return_protocol_BR_TRANSACTION_COMPLETE: u32 = 5;
+/// # const binder_driver_return_protocol_BR_INCREFS: u32 = 6;
+/// # const binder_driver_return_protocol_BR_ACQUIRE: u32 = 7;
+/// # const binder_driver_return_protocol_BR_RELEASE: u32 = 8;
+/// # const binder_driver_return_protocol_BR_DECREFS: u32 = 9;
+/// # const binder_driver_return_protocol_BR_NOOP: u32 = 10;
+/// # const binder_driver_return_protocol_BR_SPAWN_LOOPER: u32 = 11;
+/// # const binder_driver_return_protocol_BR_DEAD_BINDER: u32 = 12;
+/// # const binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE: u32 = 13;
+/// # const binder_driver_return_protocol_BR_FAILED_REPLY: u32 = 14;
+/// use kernel::macros::concat_idents;
///
/// macro_rules! pub_no_prefix {
/// ($prefix:ident, $($newname:ident),+) => {
-/// $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+
+/// $(pub(crate) const $newname: u32 = concat_idents!($prefix, $newname);)+
/// };
/// }
///
@@ -197,82 +259,6 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream {
concat_idents::concat_idents(ts)
}
-/// Used to specify the pinning information of the fields of a struct.
-///
-/// This is somewhat similar in purpose as
-/// [pin-project-lite](https://crates.io/crates/pin-project-lite).
-/// Place this macro on a struct definition and then `#[pin]` in front of the attributes of each
-/// field you want to structurally pin.
-///
-/// This macro enables the use of the [`pin_init!`] macro. When pin-initializing a `struct`,
-/// then `#[pin]` directs the type of initializer that is required.
-///
-/// If your `struct` implements `Drop`, then you need to add `PinnedDrop` as arguments to this
-/// macro, and change your `Drop` implementation to `PinnedDrop` annotated with
-/// `#[`[`macro@pinned_drop`]`]`, since dropping pinned values requires extra care.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// #[pin_data]
-/// struct DriverData {
-/// #[pin]
-/// queue: Mutex<Vec<Command>>,
-/// buf: Box<[u8; 1024 * 1024]>,
-/// }
-/// ```
-///
-/// ```rust,ignore
-/// #[pin_data(PinnedDrop)]
-/// struct DriverData {
-/// #[pin]
-/// queue: Mutex<Vec<Command>>,
-/// buf: Box<[u8; 1024 * 1024]>,
-/// raw_info: *mut Info,
-/// }
-///
-/// #[pinned_drop]
-/// impl PinnedDrop for DriverData {
-/// fn drop(self: Pin<&mut Self>) {
-/// unsafe { bindings::destroy_info(self.raw_info) };
-/// }
-/// }
-/// ```
-///
-/// [`pin_init!`]: ../kernel/macro.pin_init.html
-// ^ cannot use direct link, since `kernel` is not a dependency of `macros`.
-#[proc_macro_attribute]
-pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
- pin_data::pin_data(inner, item)
-}
-
-/// Used to implement `PinnedDrop` safely.
-///
-/// Only works on structs that are annotated via `#[`[`macro@pin_data`]`]`.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// #[pin_data(PinnedDrop)]
-/// struct DriverData {
-/// #[pin]
-/// queue: Mutex<Vec<Command>>,
-/// buf: Box<[u8; 1024 * 1024]>,
-/// raw_info: *mut Info,
-/// }
-///
-/// #[pinned_drop]
-/// impl PinnedDrop for DriverData {
-/// fn drop(self: Pin<&mut Self>) {
-/// unsafe { bindings::destroy_info(self.raw_info) };
-/// }
-/// }
-/// ```
-#[proc_macro_attribute]
-pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
- pinned_drop::pinned_drop(args, input)
-}
-
/// Paste identifiers together.
///
/// Within the `paste!` macro, identifiers inside `[<` and `>]` are concatenated together to form a
@@ -282,14 +268,27 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
/// literals (lifetimes and documentation strings are not supported). There is a difference in
/// supported modifiers as well.
///
-/// # Example
-///
-/// ```ignore
-/// use kernel::macro::paste;
+/// # Examples
///
+/// ```
+/// # const binder_driver_return_protocol_BR_OK: u32 = 0;
+/// # const binder_driver_return_protocol_BR_ERROR: u32 = 1;
+/// # const binder_driver_return_protocol_BR_TRANSACTION: u32 = 2;
+/// # const binder_driver_return_protocol_BR_REPLY: u32 = 3;
+/// # const binder_driver_return_protocol_BR_DEAD_REPLY: u32 = 4;
+/// # const binder_driver_return_protocol_BR_TRANSACTION_COMPLETE: u32 = 5;
+/// # const binder_driver_return_protocol_BR_INCREFS: u32 = 6;
+/// # const binder_driver_return_protocol_BR_ACQUIRE: u32 = 7;
+/// # const binder_driver_return_protocol_BR_RELEASE: u32 = 8;
+/// # const binder_driver_return_protocol_BR_DECREFS: u32 = 9;
+/// # const binder_driver_return_protocol_BR_NOOP: u32 = 10;
+/// # const binder_driver_return_protocol_BR_SPAWN_LOOPER: u32 = 11;
+/// # const binder_driver_return_protocol_BR_DEAD_BINDER: u32 = 12;
+/// # const binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE: u32 = 13;
+/// # const binder_driver_return_protocol_BR_FAILED_REPLY: u32 = 14;
/// macro_rules! pub_no_prefix {
/// ($prefix:ident, $($newname:ident),+) => {
-/// paste! {
+/// ::kernel::macros::paste! {
/// $(pub(crate) const $newname: u32 = [<$prefix $newname>];)+
/// }
/// };
@@ -324,17 +323,30 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
///
/// Currently supported modifiers are:
/// * `span`: change the span of concatenated identifier to the span of the specified token. By
-/// default the span of the `[< >]` group is used.
+/// default the span of the `[< >]` group is used.
/// * `lower`: change the identifier to lower case.
/// * `upper`: change the identifier to upper case.
///
-/// ```ignore
-/// use kernel::macro::paste;
-///
+/// ```
+/// # const binder_driver_return_protocol_BR_OK: u32 = 0;
+/// # const binder_driver_return_protocol_BR_ERROR: u32 = 1;
+/// # const binder_driver_return_protocol_BR_TRANSACTION: u32 = 2;
+/// # const binder_driver_return_protocol_BR_REPLY: u32 = 3;
+/// # const binder_driver_return_protocol_BR_DEAD_REPLY: u32 = 4;
+/// # const binder_driver_return_protocol_BR_TRANSACTION_COMPLETE: u32 = 5;
+/// # const binder_driver_return_protocol_BR_INCREFS: u32 = 6;
+/// # const binder_driver_return_protocol_BR_ACQUIRE: u32 = 7;
+/// # const binder_driver_return_protocol_BR_RELEASE: u32 = 8;
+/// # const binder_driver_return_protocol_BR_DECREFS: u32 = 9;
+/// # const binder_driver_return_protocol_BR_NOOP: u32 = 10;
+/// # const binder_driver_return_protocol_BR_SPAWN_LOOPER: u32 = 11;
+/// # const binder_driver_return_protocol_BR_DEAD_BINDER: u32 = 12;
+/// # const binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE: u32 = 13;
+/// # const binder_driver_return_protocol_BR_FAILED_REPLY: u32 = 14;
/// macro_rules! pub_no_prefix {
/// ($prefix:ident, $($newname:ident),+) => {
-/// kernel::macros::paste! {
-/// $(pub(crate) const fn [<$newname:lower:span>]: u32 = [<$prefix $newname:span>];)+
+/// ::kernel::macros::paste! {
+/// $(pub(crate) const fn [<$newname:lower:span>]() -> u32 { [<$prefix $newname:span>] })+
/// }
/// };
/// }
@@ -365,10 +377,10 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
///
/// Literals can also be concatenated with other identifiers:
///
-/// ```ignore
+/// ```
/// macro_rules! create_numbered_fn {
/// ($name:literal, $val:literal) => {
-/// kernel::macros::paste! {
+/// ::kernel::macros::paste! {
/// fn [<some_ $name _fn $val>]() -> u32 { $val }
/// }
/// };
@@ -387,21 +399,29 @@ pub fn paste(input: TokenStream) -> TokenStream {
tokens.into_iter().collect()
}
-/// Derives the [`Zeroable`] trait for the given struct.
+/// Registers a KUnit test suite and its test cases using a user-space like syntax.
///
-/// This can only be used for structs where every field implements the [`Zeroable`] trait.
+/// This macro should be used on modules. If `CONFIG_KUNIT` (in `.config`) is `n`, the target module
+/// is ignored.
///
/// # Examples
///
-/// ```rust,ignore
-/// #[derive(Zeroable)]
-/// pub struct DriverData {
-/// id: i64,
-/// buf_ptr: *mut u8,
-/// len: usize,
+/// ```ignore
+/// # use kernel::prelude::*;
+/// #[kunit_tests(kunit_test_suit_name)]
+/// mod tests {
+/// #[test]
+/// fn foo() {
+/// assert_eq!(1, 1);
+/// }
+///
+/// #[test]
+/// fn bar() {
+/// assert_eq!(2, 2);
+/// }
/// }
/// ```
-#[proc_macro_derive(Zeroable)]
-pub fn derive_zeroable(input: TokenStream) -> TokenStream {
- zeroable::derive(input)
+#[proc_macro_attribute]
+pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
+ kunit::kunit_tests(attr, ts)
}
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 27979e582e4b..75efc6eeeafc 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -48,7 +48,7 @@ impl<'a> ModInfoBuilder<'a> {
)
} else {
// Loadable modules' modinfo strings go as-is.
- format!("{field}={content}\0", field = field, content = content)
+ format!("{field}={content}\0")
};
write!(
@@ -56,8 +56,8 @@ impl<'a> ModInfoBuilder<'a> {
"
{cfg}
#[doc(hidden)]
- #[link_section = \".modinfo\"]
- #[used]
+ #[cfg_attr(not(target_os = \"macos\"), link_section = \".modinfo\")]
+ #[used(compiler)]
pub static __{module}_{counter}: [u8; {length}] = *{string};
",
cfg = if builtin {
@@ -95,16 +95,26 @@ struct ModuleInfo {
license: String,
name: String,
author: Option<String>,
+ authors: Option<Vec<String>>,
description: Option<String>,
alias: Option<Vec<String>>,
+ firmware: Option<Vec<String>>,
}
impl ModuleInfo {
fn parse(it: &mut token_stream::IntoIter) -> Self {
let mut info = ModuleInfo::default();
- const EXPECTED_KEYS: &[&str] =
- &["type", "name", "author", "description", "license", "alias"];
+ const EXPECTED_KEYS: &[&str] = &[
+ "type",
+ "name",
+ "author",
+ "authors",
+ "description",
+ "license",
+ "alias",
+ "firmware",
+ ];
const REQUIRED_KEYS: &[&str] = &["type", "name", "license"];
let mut seen_keys = Vec::new();
@@ -116,10 +126,7 @@ impl ModuleInfo {
};
if seen_keys.contains(&key) {
- panic!(
- "Duplicated key \"{}\". Keys can only be specified once.",
- key
- );
+ panic!("Duplicated key \"{key}\". Keys can only be specified once.");
}
assert_eq!(expect_punct(it), ':');
@@ -128,13 +135,12 @@ impl ModuleInfo {
"type" => info.type_ = expect_ident(it),
"name" => info.name = expect_string_ascii(it),
"author" => info.author = Some(expect_string(it)),
+ "authors" => info.authors = Some(expect_string_array(it)),
"description" => info.description = Some(expect_string(it)),
"license" => info.license = expect_string_ascii(it),
"alias" => info.alias = Some(expect_string_array(it)),
- _ => panic!(
- "Unknown key \"{}\". Valid keys are: {:?}.",
- key, EXPECTED_KEYS
- ),
+ "firmware" => info.firmware = Some(expect_string_array(it)),
+ _ => panic!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
}
assert_eq!(expect_punct(it), ',');
@@ -146,7 +152,7 @@ impl ModuleInfo {
for key in REQUIRED_KEYS {
if !seen_keys.iter().any(|e| e == key) {
- panic!("Missing required key \"{}\".", key);
+ panic!("Missing required key \"{key}\".");
}
}
@@ -158,10 +164,7 @@ impl ModuleInfo {
}
if seen_keys != ordered_keys {
- panic!(
- "Keys are not ordered as expected. Order them like: {:?}.",
- ordered_keys
- );
+ panic!("Keys are not ordered as expected. Order them like: {ordered_keys:?}.");
}
info
@@ -173,10 +176,17 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
let info = ModuleInfo::parse(&mut it);
- let mut modinfo = ModInfoBuilder::new(info.name.as_ref());
+ // Rust does not allow hyphens in identifiers, use underscore instead.
+ let ident = info.name.replace('-', "_");
+ let mut modinfo = ModInfoBuilder::new(ident.as_ref());
if let Some(author) = info.author {
modinfo.emit("author", &author);
}
+ if let Some(authors) = info.authors {
+ for author in authors {
+ modinfo.emit("author", &author);
+ }
+ }
if let Some(description) = info.description {
modinfo.emit("description", &description);
}
@@ -186,6 +196,11 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
modinfo.emit("alias", &alias);
}
}
+ if let Some(firmware) = info.firmware {
+ for fw in firmware {
+ modinfo.emit("firmware", &fw);
+ }
+ }
// Built-in modules also export the `file` modinfo string.
let file =
@@ -199,106 +214,169 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
/// Used by the printing macros, e.g. [`info!`].
const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
- /// The \"Rust loadable module\" mark.
- //
- // This may be best done another way later on, e.g. as a new modinfo
- // key or a new section. For the moment, keep it simple.
- #[cfg(MODULE)]
- #[doc(hidden)]
- #[used]
- static __IS_RUST_MODULE: () = ();
-
- static mut __MOD: Option<{type_}> = None;
-
// SAFETY: `__this_module` is constructed by the kernel at load time and will not be
// freed until the module is unloaded.
#[cfg(MODULE)]
- static THIS_MODULE: kernel::ThisModule = unsafe {{
- kernel::ThisModule::from_ptr(&kernel::bindings::__this_module as *const _ as *mut _)
+ static THIS_MODULE: ::kernel::ThisModule = unsafe {{
+ extern \"C\" {{
+ static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
+ }}
+
+ ::kernel::ThisModule::from_ptr(__this_module.get())
}};
#[cfg(not(MODULE))]
- static THIS_MODULE: kernel::ThisModule = unsafe {{
- kernel::ThisModule::from_ptr(core::ptr::null_mut())
+ static THIS_MODULE: ::kernel::ThisModule = unsafe {{
+ ::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
}};
- // Loadable modules need to export the `{{init,cleanup}}_module` identifiers.
- /// # Safety
- ///
- /// This function must not be called after module initialization, because it may be
- /// freed after that completes.
- #[cfg(MODULE)]
- #[doc(hidden)]
- #[no_mangle]
- #[link_section = \".init.text\"]
- pub unsafe extern \"C\" fn init_module() -> core::ffi::c_int {{
- __init()
- }}
+ /// The `LocalModule` type is the type of the module created by `module!`,
+ /// `module_pci_driver!`, `module_platform_driver!`, etc.
+ type LocalModule = {type_};
- #[cfg(MODULE)]
- #[doc(hidden)]
- #[no_mangle]
- pub extern \"C\" fn cleanup_module() {{
- __exit()
+ impl ::kernel::ModuleMetadata for {type_} {{
+ const NAME: &'static ::kernel::str::CStr = ::kernel::c_str!(\"{name}\");
}}
- // Built-in modules are initialized through an initcall pointer
- // and the identifiers need to be unique.
- #[cfg(not(MODULE))]
- #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
- #[doc(hidden)]
- #[link_section = \"{initcall_section}\"]
- #[used]
- pub static __{name}_initcall: extern \"C\" fn() -> core::ffi::c_int = __{name}_init;
+ // Double nested modules, since then nobody can access the public items inside.
+ mod __module_init {{
+ mod __module_init {{
+ use super::super::{type_};
+ use pin_init::PinInit;
+
+ /// The \"Rust loadable module\" mark.
+ //
+ // This may be best done another way later on, e.g. as a new modinfo
+ // key or a new section. For the moment, keep it simple.
+ #[cfg(MODULE)]
+ #[doc(hidden)]
+ #[used(compiler)]
+ static __IS_RUST_MODULE: () = ();
+
+ static mut __MOD: ::core::mem::MaybeUninit<{type_}> =
+ ::core::mem::MaybeUninit::uninit();
+
+ // Loadable modules need to export the `{{init,cleanup}}_module` identifiers.
+ /// # Safety
+ ///
+ /// This function must not be called after module initialization, because it may be
+ /// freed after that completes.
+ #[cfg(MODULE)]
+ #[doc(hidden)]
+ #[no_mangle]
+ #[link_section = \".init.text\"]
+ pub unsafe extern \"C\" fn init_module() -> ::kernel::ffi::c_int {{
+ // SAFETY: This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // unique name.
+ unsafe {{ __init() }}
+ }}
- #[cfg(not(MODULE))]
- #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
- core::arch::global_asm!(
- r#\".section \"{initcall_section}\", \"a\"
- __{name}_initcall:
- .long __{name}_init - .
- .previous
- \"#
- );
+ #[cfg(MODULE)]
+ #[doc(hidden)]
+ #[used(compiler)]
+ #[link_section = \".init.data\"]
+ static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module;
+
+ #[cfg(MODULE)]
+ #[doc(hidden)]
+ #[no_mangle]
+ #[link_section = \".exit.text\"]
+ pub extern \"C\" fn cleanup_module() {{
+ // SAFETY:
+ // - This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // unique name,
+ // - furthermore it is only called after `init_module` has returned `0`
+ // (which delegates to `__init`).
+ unsafe {{ __exit() }}
+ }}
- #[cfg(not(MODULE))]
- #[doc(hidden)]
- #[no_mangle]
- pub extern \"C\" fn __{name}_init() -> core::ffi::c_int {{
- __init()
- }}
+ #[cfg(MODULE)]
+ #[doc(hidden)]
+ #[used(compiler)]
+ #[link_section = \".exit.data\"]
+ static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module;
+
+ // Built-in modules are initialized through an initcall pointer
+ // and the identifiers need to be unique.
+ #[cfg(not(MODULE))]
+ #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
+ #[doc(hidden)]
+ #[link_section = \"{initcall_section}\"]
+ #[used(compiler)]
+ pub static __{ident}_initcall: extern \"C\" fn() ->
+ ::kernel::ffi::c_int = __{ident}_init;
+
+ #[cfg(not(MODULE))]
+ #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
+ ::core::arch::global_asm!(
+ r#\".section \"{initcall_section}\", \"a\"
+ __{ident}_initcall:
+ .long __{ident}_init - .
+ .previous
+ \"#
+ );
+
+ #[cfg(not(MODULE))]
+ #[doc(hidden)]
+ #[no_mangle]
+ pub extern \"C\" fn __{ident}_init() -> ::kernel::ffi::c_int {{
+ // SAFETY: This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // placement above in the initcall section.
+ unsafe {{ __init() }}
+ }}
- #[cfg(not(MODULE))]
- #[doc(hidden)]
- #[no_mangle]
- pub extern \"C\" fn __{name}_exit() {{
- __exit()
- }}
+ #[cfg(not(MODULE))]
+ #[doc(hidden)]
+ #[no_mangle]
+ pub extern \"C\" fn __{ident}_exit() {{
+ // SAFETY:
+ // - This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // unique name,
+ // - furthermore it is only called after `__{ident}_init` has
+ // returned `0` (which delegates to `__init`).
+ unsafe {{ __exit() }}
+ }}
- fn __init() -> core::ffi::c_int {{
- match <{type_} as kernel::Module>::init(&THIS_MODULE) {{
- Ok(m) => {{
- unsafe {{
- __MOD = Some(m);
+ /// # Safety
+ ///
+ /// This function must only be called once.
+ unsafe fn __init() -> ::kernel::ffi::c_int {{
+ let initer =
+ <{type_} as ::kernel::InPlaceModule>::init(&super::super::THIS_MODULE);
+ // SAFETY: No data race, since `__MOD` can only be accessed by this module
+ // and there only `__init` and `__exit` access it. These functions are only
+ // called once and `__exit` cannot be called before or during `__init`.
+ match unsafe {{ initer.__pinned_init(__MOD.as_mut_ptr()) }} {{
+ Ok(m) => 0,
+ Err(e) => e.to_errno(),
}}
- return 0;
}}
- Err(e) => {{
- return e.to_errno();
+
+ /// # Safety
+ ///
+ /// This function must
+ /// - only be called once,
+ /// - be called after `__init` has been called and returned `0`.
+ unsafe fn __exit() {{
+ // SAFETY: No data race, since `__MOD` can only be accessed by this module
+ // and there only `__init` and `__exit` access it. These functions are only
+ // called once and `__init` was already called.
+ unsafe {{
+ // Invokes `drop()` on `__MOD`, which should be used for cleanup.
+ __MOD.assume_init_drop();
+ }}
}}
- }}
- }}
- fn __exit() {{
- unsafe {{
- // Invokes `drop()` on `__MOD`, which should be used for cleanup.
- __MOD = None;
+ {modinfo}
}}
}}
-
- {modinfo}
",
type_ = info.type_,
name = info.name,
+ ident = ident,
modinfo = modinfo.buffer,
initcall_section = ".initcall6.init"
)
diff --git a/rust/macros/paste.rs b/rust/macros/paste.rs
index f40d42b35b58..cce712d19855 100644
--- a/rust/macros/paste.rs
+++ b/rust/macros/paste.rs
@@ -2,7 +2,7 @@
use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
-fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
+fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> {
let mut tokens = tokens.iter();
let mut segments = Vec::new();
let mut span = None;
@@ -46,12 +46,21 @@ fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
};
segments.push((value, sp));
}
- _ => panic!("unexpected token in paste segments"),
+ Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => {
+ let tokens = group.stream().into_iter().collect::<Vec<TokenTree>>();
+ segments.append(&mut concat_helper(tokens.as_slice()));
+ }
+ token => panic!("unexpected token in paste segments: {token:?}"),
};
}
+ segments
+}
+
+fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
+ let segments = concat_helper(tokens);
let pasted: String = segments.into_iter().map(|x| x.0).collect();
- TokenTree::Ident(Ident::new(&pasted, span.unwrap_or(group_span)))
+ TokenTree::Ident(Ident::new(&pasted, group_span))
}
pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {
diff --git a/rust/macros/pin_data.rs b/rust/macros/pin_data.rs
deleted file mode 100644
index 6d58cfda9872..000000000000
--- a/rust/macros/pin_data.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0 OR MIT
-
-use crate::helpers::{parse_generics, Generics};
-use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree};
-
-pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
- // This proc-macro only does some pre-parsing and then delegates the actual parsing to
- // `kernel::__pin_data!`.
-
- let (
- Generics {
- impl_generics,
- ty_generics,
- },
- rest,
- ) = parse_generics(input);
- // The struct definition might contain the `Self` type. Since `__pin_data!` will define a new
- // type with the same generics and bounds, this poses a problem, since `Self` will refer to the
- // new type as opposed to this struct definition. Therefore we have to replace `Self` with the
- // concrete name.
-
- // Errors that occur when replacing `Self` with `struct_name`.
- let mut errs = TokenStream::new();
- // The name of the struct with ty_generics.
- let struct_name = rest
- .iter()
- .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i.to_string() == "struct"))
- .nth(1)
- .and_then(|tt| match tt {
- TokenTree::Ident(_) => {
- let tt = tt.clone();
- let mut res = vec![tt];
- if !ty_generics.is_empty() {
- // We add this, so it is maximally compatible with e.g. `Self::CONST` which
- // will be replaced by `StructName::<$generics>::CONST`.
- res.push(TokenTree::Punct(Punct::new(':', Spacing::Joint)));
- res.push(TokenTree::Punct(Punct::new(':', Spacing::Alone)));
- res.push(TokenTree::Punct(Punct::new('<', Spacing::Alone)));
- res.extend(ty_generics.iter().cloned());
- res.push(TokenTree::Punct(Punct::new('>', Spacing::Alone)));
- }
- Some(res)
- }
- _ => None,
- })
- .unwrap_or_else(|| {
- // If we did not find the name of the struct then we will use `Self` as the replacement
- // and add a compile error to ensure it does not compile.
- errs.extend(
- "::core::compile_error!(\"Could not locate type name.\");"
- .parse::<TokenStream>()
- .unwrap(),
- );
- "Self".parse::<TokenStream>().unwrap().into_iter().collect()
- });
- let impl_generics = impl_generics
- .into_iter()
- .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &mut errs))
- .collect::<Vec<_>>();
- let mut rest = rest
- .into_iter()
- .flat_map(|tt| {
- // We ignore top level `struct` tokens, since they would emit a compile error.
- if matches!(&tt, TokenTree::Ident(i) if i.to_string() == "struct") {
- vec![tt]
- } else {
- replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)
- }
- })
- .collect::<Vec<_>>();
- // This should be the body of the struct `{...}`.
- let last = rest.pop();
- let mut quoted = quote!(::kernel::__pin_data! {
- parse_input:
- @args(#args),
- @sig(#(#rest)*),
- @impl_generics(#(#impl_generics)*),
- @ty_generics(#(#ty_generics)*),
- @body(#last),
- });
- quoted.extend(errs);
- quoted
-}
-
-/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `struct` `union` and `impl`
-/// keywords.
-///
-/// The error is appended to `errs` to allow normal parsing to continue.
-fn replace_self_and_deny_type_defs(
- struct_name: &Vec<TokenTree>,
- tt: TokenTree,
- errs: &mut TokenStream,
-) -> Vec<TokenTree> {
- match tt {
- TokenTree::Ident(ref i)
- if i.to_string() == "enum"
- || i.to_string() == "trait"
- || i.to_string() == "struct"
- || i.to_string() == "union"
- || i.to_string() == "impl" =>
- {
- errs.extend(
- format!(
- "::core::compile_error!(\"Cannot use `{i}` inside of struct definition with \
- `#[pin_data]`.\");"
- )
- .parse::<TokenStream>()
- .unwrap()
- .into_iter()
- .map(|mut tok| {
- tok.set_span(tt.span());
- tok
- }),
- );
- vec![tt]
- }
- TokenTree::Ident(i) if i.to_string() == "Self" => struct_name.clone(),
- TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt],
- TokenTree::Group(g) => vec![TokenTree::Group(Group::new(
- g.delimiter(),
- g.stream()
- .into_iter()
- .flat_map(|tt| replace_self_and_deny_type_defs(struct_name, tt, errs))
- .collect(),
- ))],
- }
-}
diff --git a/rust/macros/pinned_drop.rs b/rust/macros/pinned_drop.rs
deleted file mode 100644
index 88fb72b20660..000000000000
--- a/rust/macros/pinned_drop.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0 OR MIT
-
-use proc_macro::{TokenStream, TokenTree};
-
-pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream {
- let mut toks = input.into_iter().collect::<Vec<_>>();
- assert!(!toks.is_empty());
- // Ensure that we have an `impl` item.
- assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() == "impl"));
- // Ensure that we are implementing `PinnedDrop`.
- let mut nesting: usize = 0;
- let mut pinned_drop_idx = None;
- for (i, tt) in toks.iter().enumerate() {
- match tt {
- TokenTree::Punct(p) if p.as_char() == '<' => {
- nesting += 1;
- }
- TokenTree::Punct(p) if p.as_char() == '>' => {
- nesting = nesting.checked_sub(1).unwrap();
- continue;
- }
- _ => {}
- }
- if i >= 1 && nesting == 0 {
- // Found the end of the generics, this should be `PinnedDrop`.
- assert!(
- matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"),
- "expected 'PinnedDrop', found: '{:?}'",
- tt
- );
- pinned_drop_idx = Some(i);
- break;
- }
- }
- let idx = pinned_drop_idx
- .unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`."));
- // Fully qualify the `PinnedDrop`, as to avoid any tampering.
- toks.splice(idx..idx, quote!(::kernel::init::));
- // Take the `{}` body and call the declarative macro.
- if let Some(TokenTree::Group(last)) = toks.pop() {
- let last = last.stream();
- quote!(::kernel::__pinned_drop! {
- @impl_sig(#(#toks)*),
- @impl_body(#last),
- })
- } else {
- TokenStream::from_iter(toks)
- }
-}
diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
index 33a199e4f176..92cacc4067c9 100644
--- a/rust/macros/quote.rs
+++ b/rust/macros/quote.rs
@@ -2,6 +2,7 @@
use proc_macro::{TokenStream, TokenTree};
+#[allow(dead_code)]
pub(crate) trait ToTokens {
fn to_tokens(&self, tokens: &mut TokenStream);
}
@@ -20,6 +21,12 @@ impl ToTokens for proc_macro::Group {
}
}
+impl ToTokens for proc_macro::Ident {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.extend([TokenTree::from(self.clone())]);
+ }
+}
+
impl ToTokens for TokenTree {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend([self.clone()]);
@@ -40,7 +47,7 @@ impl ToTokens for TokenStream {
/// `quote` crate but provides only just enough functionality needed by the current `macros` crate.
macro_rules! quote_spanned {
($span:expr => $($tt:tt)*) => {{
- let mut tokens;
+ let mut tokens: ::std::vec::Vec<::proc_macro::TokenTree>;
#[allow(clippy::vec_init_then_push)]
{
tokens = ::std::vec::Vec::new();
@@ -65,7 +72,8 @@ macro_rules! quote_spanned {
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => {
- let mut tokens = ::std::vec::Vec::new();
+ #[allow(unused_mut)]
+ let mut tokens = ::std::vec::Vec::<::proc_macro::TokenTree>::new();
quote_spanned!(@proc tokens $span $($inner)*);
$v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Parenthesis,
@@ -136,6 +144,22 @@ macro_rules! quote_spanned {
));
quote_spanned!(@proc $v $span $($tt)*);
};
+ (@proc $v:ident $span:ident = $($tt:tt)*) => {
+ $v.push(::proc_macro::TokenTree::Punct(
+ ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone)
+ ));
+ quote_spanned!(@proc $v $span $($tt)*);
+ };
+ (@proc $v:ident $span:ident # $($tt:tt)*) => {
+ $v.push(::proc_macro::TokenTree::Punct(
+ ::proc_macro::Punct::new('#', ::proc_macro::Spacing::Alone)
+ ));
+ quote_spanned!(@proc $v $span $($tt)*);
+ };
+ (@proc $v:ident $span:ident _ $($tt:tt)*) => {
+ $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new("_", $span)));
+ quote_spanned!(@proc $v $span $($tt)*);
+ };
(@proc $v:ident $span:ident $id:ident $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span)));
quote_spanned!(@proc $v $span $($tt)*);
diff --git a/rust/macros/zeroable.rs b/rust/macros/zeroable.rs
deleted file mode 100644
index 0d605c46ab3b..000000000000
--- a/rust/macros/zeroable.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-use crate::helpers::{parse_generics, Generics};
-use proc_macro::{TokenStream, TokenTree};
-
-pub(crate) fn derive(input: TokenStream) -> TokenStream {
- let (
- Generics {
- impl_generics,
- ty_generics,
- },
- mut rest,
- ) = parse_generics(input);
- // This should be the body of the struct `{...}`.
- let last = rest.pop();
- // Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`.
- let mut new_impl_generics = Vec::with_capacity(impl_generics.len());
- // Are we inside of a generic where we want to add `Zeroable`?
- let mut in_generic = !impl_generics.is_empty();
- // Have we already inserted `Zeroable`?
- let mut inserted = false;
- // Level of `<>` nestings.
- let mut nested = 0;
- for tt in impl_generics {
- match &tt {
- // If we find a `,`, then we have finished a generic/constant/lifetime parameter.
- TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => {
- if in_generic && !inserted {
- new_impl_generics.extend(quote! { : ::kernel::init::Zeroable });
- }
- in_generic = true;
- inserted = false;
- new_impl_generics.push(tt);
- }
- // If we find `'`, then we are entering a lifetime.
- TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => {
- in_generic = false;
- new_impl_generics.push(tt);
- }
- TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => {
- new_impl_generics.push(tt);
- if in_generic {
- new_impl_generics.extend(quote! { ::kernel::init::Zeroable + });
- inserted = true;
- }
- }
- TokenTree::Punct(p) if p.as_char() == '<' => {
- nested += 1;
- new_impl_generics.push(tt);
- }
- TokenTree::Punct(p) if p.as_char() == '>' => {
- assert!(nested > 0);
- nested -= 1;
- new_impl_generics.push(tt);
- }
- _ => new_impl_generics.push(tt),
- }
- }
- assert_eq!(nested, 0);
- if in_generic && !inserted {
- new_impl_generics.extend(quote! { : ::kernel::init::Zeroable });
- }
- quote! {
- ::kernel::__derive_zeroable!(
- parse_input:
- @sig(#(#rest)*),
- @impl_generics(#(#new_impl_generics)*),
- @ty_generics(#(#ty_generics)*),
- @body(#last),
- );
- }
-}