diff --git a/drivers/android/rust_binder.rs b/drivers/android/rust_binder.rs index 617f7a8cab832b..939b6f223bf1b6 100644 --- a/drivers/android/rust_binder.rs +++ b/drivers/android/rust_binder.rs @@ -31,10 +31,10 @@ use {context::Context, thread::Thread}; module! { type: BinderModule, - name: b"rust_binder", - author: b"Wedson Almeida Filho", - description: b"Android Binder", - license: b"GPL v2", + name: "rust_binder", + author: "Wedson Almeida Filho", + description: "Android Binder", + license: "GPL v2", } enum Either { diff --git a/drivers/char/hw_random/bcm2835_rng_rust.rs b/drivers/char/hw_random/bcm2835_rng_rust.rs index 9ae5395172c317..3dc452df9e5655 100644 --- a/drivers/char/hw_random/bcm2835_rng_rust.rs +++ b/drivers/char/hw_random/bcm2835_rng_rust.rs @@ -13,10 +13,10 @@ use kernel::{c_str, platdev}; module! { type: RngModule, - name: b"bcm2835_rng_rust", - author: b"Rust for Linux Contributors", - description: b"BCM2835 Random Number Generator (RNG) driver", - license: b"GPL v2", + name: "bcm2835_rng_rust", + author: "Rust for Linux Contributors", + description: "BCM2835 Random Number Generator (RNG) driver", + license: "GPL v2", } struct RngModule { diff --git a/rust/Makefile b/rust/Makefile index 85c1904b674b78..4a03693bdc9e22 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_RUST) += core.o compiler_builtins.o helpers.o extra-$(CONFIG_RUST) += exports_core_generated.h -extra-$(CONFIG_RUST) += libmodule.so +extra-$(CONFIG_RUST) += libmacros.so extra-$(CONFIG_RUST) += bindings_generated.rs obj-$(CONFIG_RUST) += alloc.o kernel.o @@ -35,11 +35,11 @@ quiet_cmd_rustdoc = RUSTDOC $< --output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \ -Fmissing-docs @$(objtree)/include/generated/rustc_cfg $< -rustdoc: rustdoc-module rustdoc-compiler_builtins rustdoc-kernel +rustdoc: rustdoc-macros rustdoc-compiler_builtins rustdoc-kernel -rustdoc-module: private rustdoc_target_flags = --crate-type proc-macro \ +rustdoc-macros: private rustdoc_target_flags = --crate-type proc-macro \ --extern proc_macro -rustdoc-module: $(srctree)/rust/module.rs FORCE +rustdoc-macros: $(srctree)/rust/macros/lib.rs FORCE $(call if_changed,rustdoc_host) rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE @@ -47,9 +47,9 @@ rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE rustdoc-kernel: private rustdoc_target_flags = --extern alloc \ --extern build_error \ - --extern module=$(objtree)/rust/libmodule.so -rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \ - $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE + --extern macros=$(objtree)/rust/libmacros.so +rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-macros \ + $(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE $(call if_changed,rustdoc) ifdef CONFIG_CC_IS_CLANG @@ -126,7 +126,7 @@ quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@ mv $(objtree)/rust/$(patsubst lib%.so,%,$(notdir $@)).d $(depfile); \ sed -i '/^\#/d' $(depfile) -$(objtree)/rust/libmodule.so: $(srctree)/rust/module.rs FORCE +$(objtree)/rust/libmacros.so: $(srctree)/rust/macros/lib.rs FORCE $(call if_changed_dep,rustc_procmacro) quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@ @@ -166,11 +166,11 @@ $(objtree)/rust/build_error.o: $(srctree)/rust/build_error.rs \ $(objtree)/rust/compiler_builtins.o FORCE $(call if_changed_dep,rustc_library) -# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935 +# ICE on `--extern macros`: https://github.com/rust-lang/rust/issues/56935 $(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \ --extern build_error \ - --extern module=$(objtree)/rust/libmodule.so + --extern macros=$(objtree)/rust/libmacros.so $(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \ $(objtree)/rust/build_error.o \ - $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE + $(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE $(call if_changed_dep,rustc_library) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index c70f61367347f3..fc6a6b01c588db 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -52,7 +52,7 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// Get the current value of the parameter for use in the kernel module. /// /// This function should not be used directly. Instead use the wrapper - /// `read` which will be generated by [`module::module`]. + /// `read` which will be generated by [`macros::module`]. fn value(&self) -> &Self::Value; /// Set the module parameter from a string. @@ -428,7 +428,7 @@ impl ModuleParam /// A C-style string parameter. /// /// The Rust version of the [`charp`] parameter. This type is meant to be -/// used by the [`module::module`] macro, not handled directly. Instead use the +/// used by the [`macros::module`] macro, not handled directly. Instead use the /// `read` method generated by that macro. /// /// [`charp`]: ../../../include/linux/moduleparam.h diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index fad94708fa6f2d..96e39bbc8063f2 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -15,7 +15,7 @@ pub use alloc::{borrow::ToOwned, string::String}; pub use super::build_assert; -pub use module::{module, module_misc_device}; +pub use macros::{module, module_misc_device}; pub use super::{pr_alert, pr_cont, pr_crit, pr_emerg, pr_err, pr_info, pr_notice, pr_warn}; diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs new file mode 100644 index 00000000000000..03e39009e8d0d6 --- /dev/null +++ b/rust/macros/lib.rs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Crate for all kernel procedural macros. +mod module; +mod syn; + +use proc_macro::TokenStream; + +/// Declares a kernel module. +/// +/// The `type` argument should be a type which implements the [`KernelModule`] +/// trait. Also accepts various forms of kernel metadata. +/// +/// C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) +/// +/// [`KernelModule`]: ../kernel/trait.KernelModule.html +/// +/// # Examples +/// +/// ```rust,no_run +/// use kernel::prelude::*; +/// +/// module!{ +/// type: MyKernelModule, +/// name: "my_kernel_module", +/// author: "Rust for Linux Contributors", +/// description: "My very own kernel module!", +/// license: "GPL v2", +/// params: { +/// my_i32: i32 { +/// default: 42, +/// permissions: 0o000, +/// description: "Example of i32", +/// }, +/// writeable_i32: i32 { +/// default: 42, +/// permissions: 0o644, +/// description: "Example of i32", +/// }, +/// }, +/// } +/// +/// struct MyKernelModule; +/// +/// impl KernelModule for MyKernelModule { +/// fn init() -> Result { +/// // 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()); +/// Ok(MyKernelModule) +/// } +/// } +/// ``` +/// +/// # Supported argument types +/// - `type`: type which implements the [`KernelModule`] 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. +/// - `alias_rtnl_link`: byte array of the `rtnl_link_alias` of the kernel module (mutually exclusive with `alias`). +/// - `params`: parameters for the kernel module, as described below. +/// +/// # Supported parameter types +/// +/// - `bool`: Corresponds to C `bool` param type. +/// - `i8`: No equivalent C param type. +/// - `u8`: Corresponds to C `char` param type. +/// - `i16`: Corresponds to C `short` param type. +/// - `u16`: Corresponds to C `ushort` param type. +/// - `i32`: Corresponds to C `int` param type. +/// - `u32`: Corresponds to C `uint` param type. +/// - `i64`: No equivalent C param type. +/// - `u64`: Corresponds to C `ullong` param type. +/// - `isize`: No equivalent C param type. +/// - `usize`: No equivalent C param type. +/// - `str`: Corresponds to C `charp` param type. Reading returns a byte slice. +/// - `ArrayParam`: Corresponds to C parameters created using `module_param_array`. An array +/// of `T`'s of length at **most** `N`. +/// +/// `invbool` is unsupported: it was only ever used in a few modules. +/// Consider using a `bool` and inverting the logic instead. +#[proc_macro] +pub fn module(ts: TokenStream) -> TokenStream { + module::module(ts) +} + +/// Declares a kernel module that exposes a single misc device. +/// +/// The `type` argument should be a type which implements the [`FileOpener`] trait. Also accepts +/// various forms of kernel metadata. +/// +/// C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) +/// +/// [`FileOpener`]: ../kernel/file_operations/trait.FileOpener.html +/// +/// # Examples +/// +/// ```rust,no_run +/// use kernel::prelude::*; +/// +/// module_misc_device! { +/// type: MyFile, +/// name: "my_miscdev_kernel_module", +/// author: "Rust for Linux Contributors", +/// description: "My very own misc device kernel module!", +/// license: "GPL v2", +/// } +/// +/// #[derive(Default)] +/// struct MyFile; +/// +/// impl kernel::file_operations::FileOperations for MyFile { +/// kernel::declare_file_operations!(); +/// } +/// ``` +#[proc_macro] +pub fn module_misc_device(ts: TokenStream) -> TokenStream { + module::module_misc_device(ts) +} diff --git a/rust/macros/module.rs b/rust/macros/module.rs new file mode 100644 index 00000000000000..8a89f83fb1eb0c --- /dev/null +++ b/rust/macros/module.rs @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![deny(clippy::complexity)] +#![deny(clippy::correctness)] +#![deny(clippy::perf)] +#![deny(clippy::style)] + +use crate::syn::{ + buffer::{Cursor, TokenBuffer}, + Lit, +}; +use proc_macro::{Delimiter, Literal, TokenStream}; + +fn expect_ident(it: &mut Cursor<'_>) -> String { + let (ident, next) = it.ident().expect("Expected Ident"); + *it = next; + ident.to_string() +} + +fn expect_punct(it: &mut Cursor<'_>) -> char { + let (punct, next) = it.punct().expect("Expected Punct"); + *it = next; + punct.as_char() +} + +fn expect_literal(it: &mut Cursor<'_>) -> Literal { + let (lit, next) = it.literal().expect("Expected Literal"); + *it = next; + lit +} + +fn expect_group<'a>(it: &mut Cursor<'a>, delim: Delimiter) -> Cursor<'a> { + let (inner, _, next) = it.group(delim).expect("Expected Group"); + *it = next; + inner +} + +fn expect_string(it: &mut Cursor<'_>) -> String { + let lit = expect_literal(it); + let lit = Lit::new(lit); + match &lit { + Lit::Str(s) => { + assert!(s.suffix().is_empty(), "Unexpected suffix"); + s.value() + } + _ => panic!("Expected string"), + } +} + +#[derive(Clone, PartialEq)] +enum ParamType { + Ident(String), + Array { vals: String, max_length: usize }, +} + +fn expect_array_fields(it: &mut Cursor<'_>) -> ParamType { + assert_eq!(expect_punct(it), '<'); + let vals = expect_ident(it); + assert_eq!(expect_punct(it), ','); + let max_length_str = expect_literal(it).to_string(); + let max_length = max_length_str + .parse::() + .expect("Expected usize length"); + assert_eq!(expect_punct(it), '>'); + ParamType::Array { vals, max_length } +} + +fn expect_type(it: &mut Cursor<'_>) -> ParamType { + let (ident, next) = it.ident().expect("Expected Param Type"); + *it = next; + match ident.to_string().as_ref() { + "ArrayParam" => expect_array_fields(it), + _ => ParamType::Ident(ident.to_string()), + } +} + +fn expect_end(it: &mut Cursor<'_>) { + assert!(it.eof(), "Expected end"); +} + +fn parse_list( + it: &mut Cursor<'_>, + delim: Delimiter, + f: impl Fn(&mut Cursor<'_>) -> T, +) -> Vec { + let mut inner = expect_group(it, delim); + let mut vec = Vec::new(); + while !inner.eof() { + let item = f(&mut inner); + vec.push(item); + if inner.eof() { + break; + } + assert_eq!(expect_punct(&mut inner), ','); + } + assert!(inner.eof(), "Expected end"); + vec +} + +fn parse_item_or_list( + it: &mut Cursor<'_>, + delim: Delimiter, + f: impl Fn(&mut Cursor<'_>) -> T, +) -> Vec { + if it.group(delim).is_some() { + parse_list(it, delim, f) + } else { + vec![f(it)] + } +} + +fn get_literal(it: &mut Cursor<'_>, expected_name: &str) -> Literal { + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let literal = expect_literal(it); + assert_eq!(expect_punct(it), ','); + literal +} + +fn get_string(it: &mut Cursor<'_>, expected_name: &str) -> String { + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let byte_string = expect_string(it); + assert_eq!(expect_punct(it), ','); + byte_string +} + +struct ModInfoBuilder<'a> { + module: &'a str, + counter: usize, + buffer: String, +} + +impl<'a> ModInfoBuilder<'a> { + fn new(module: &'a str) -> Self { + ModInfoBuilder { + module, + counter: 0, + buffer: String::new(), + } + } + + fn emit_base(&mut self, field: &str, content: &str, builtin: bool) { + use std::fmt::Write; + + let string = if builtin { + // Built-in modules prefix their modinfo strings by `module.`. + format!( + "{module}.{field}={content}\0", + module = self.module, + field = field, + content = content + ) + } else { + // Loadable modules' modinfo strings go as-is. + format!("{field}={content}\0", field = field, content = content) + }; + + write!( + &mut self.buffer, + " + {cfg} + #[link_section = \".modinfo\"] + #[used] + pub static __{module}_{counter}: [u8; {length}] = *{string}; + ", + cfg = if builtin { + "#[cfg(not(MODULE))]" + } else { + "#[cfg(MODULE)]" + }, + module = self.module, + counter = self.counter, + length = string.len(), + string = Literal::byte_string(string.as_bytes()), + ) + .unwrap(); + + self.counter += 1; + } + + fn emit_only_builtin(&mut self, field: &str, content: &str) { + self.emit_base(field, content, true) + } + + fn emit_only_loadable(&mut self, field: &str, content: &str) { + self.emit_base(field, content, false) + } + + fn emit(&mut self, field: &str, content: &str) { + self.emit_only_builtin(field, content); + self.emit_only_loadable(field, content); + } + + fn emit_optional(&mut self, field: &str, content: Option<&str>) { + if let Some(content) = content { + self.emit(field, content); + } + } + + fn emit_param(&mut self, field: &str, param: &str, content: &str) { + let content = format!("{param}:{content}", param = param, content = content); + self.emit(field, &content); + } +} + +fn permissions_are_readonly(perms: u32) -> bool { + perms & 0o222 == 0 +} + +fn param_ops_path(param_type: &str) -> &'static str { + match param_type { + "bool" => "kernel::module_param::PARAM_OPS_BOOL", + "i8" => "kernel::module_param::PARAM_OPS_I8", + "u8" => "kernel::module_param::PARAM_OPS_U8", + "i16" => "kernel::module_param::PARAM_OPS_I16", + "u16" => "kernel::module_param::PARAM_OPS_U16", + "i32" => "kernel::module_param::PARAM_OPS_I32", + "u32" => "kernel::module_param::PARAM_OPS_U32", + "i64" => "kernel::module_param::PARAM_OPS_I64", + "u64" => "kernel::module_param::PARAM_OPS_U64", + "isize" => "kernel::module_param::PARAM_OPS_ISIZE", + "usize" => "kernel::module_param::PARAM_OPS_USIZE", + "str" => "kernel::module_param::PARAM_OPS_STR", + t => panic!("Unrecognized type {}", t), + } +} + +fn parse_expr(it: &mut Cursor<'_>) -> TokenStream { + let mut tt = Vec::new(); + while !it.eof() { + if let Some((punct, _)) = it.punct() { + if let ',' | ';' = punct.as_char() { + break; + } + } + let (token, next) = it.token_tree().unwrap(); + tt.push(token); + *it = next; + } + if tt.is_empty() { + panic!("Expected expression"); + } + tt.into_iter().collect() +} + +fn expect_simple_param_val(param_type: &str) -> Box) -> String> { + match param_type { + "str" => Box::new(|param_it| { + let s = expect_string(param_it); + format!( + "kernel::module_param::StringParam::Ref({})", + Literal::byte_string(s.as_bytes()) + ) + }), + _ => Box::new(|param_it| parse_expr(param_it).to_string()), + } +} + +fn get_default(param_type: &ParamType, param_it: &mut Cursor<'_>) -> String { + let expect_param_val = match param_type { + ParamType::Ident(ref param_type) + | ParamType::Array { + vals: ref param_type, + max_length: _, + } => expect_simple_param_val(param_type), + }; + assert_eq!(expect_ident(param_it), "default"); + assert_eq!(expect_punct(param_it), ':'); + let default = match param_type { + ParamType::Ident(_) => expect_param_val(param_it), + ParamType::Array { + vals: _, + max_length: _, + } => { + let default_vals = parse_list(param_it, Delimiter::Bracket, expect_param_val); + let mut default_array = "kernel::module_param::ArrayParam::create(&[".to_string(); + default_array.push_str( + &default_vals + .iter() + .map(|val| val.to_string()) + .collect::>() + .join(","), + ); + default_array.push_str("])"); + default_array + } + }; + assert_eq!(expect_punct(param_it), ','); + default +} + +fn generated_array_ops_name(vals: &str, max_length: usize) -> String { + format!( + "__generated_array_ops_{vals}_{max_length}", + vals = vals, + max_length = max_length + ) +} + +struct ParamInfo { + name: String, + type_: ParamType, + default: String, + permission: u32, + description: String, +} + +impl ParamInfo { + fn parse(it: &mut Cursor<'_>) -> Self { + let param_name = expect_ident(it); + + assert_eq!(expect_punct(it), ':'); + let param_type = expect_type(it); + let mut param_it = expect_group(it, Delimiter::Brace); + + let param_default = get_default(¶m_type, &mut param_it); + let param_permissions = match Lit::new(get_literal(&mut param_it, "permissions")) { + Lit::Int(i) => i.base10_digits().parse::().unwrap(), + _ => panic!("Permission is expected to be an integer literal"), + }; + let param_description = get_string(&mut param_it, "description"); + expect_end(&mut param_it); + + ParamInfo { + name: param_name, + type_: param_type, + default: param_default, + permission: param_permissions, + description: param_description, + } + } +} + +#[derive(Default)] +struct ModuleInfo { + type_: String, + license: String, + name: String, + author: Vec, + description: Option, + alias: Vec, + params: Vec, +} + +impl ModuleInfo { + fn parse(it: &mut Cursor<'_>) -> Self { + let mut info = ModuleInfo::default(); + + const EXPECTED_KEYS: &[&str] = &[ + "type", + "name", + "author", + "description", + "license", + "alias", + "alias_rtnl_link", + "params", + ]; + const REQUIRED_KEYS: &[&str] = &["type", "name", "license"]; + let mut seen_keys = Vec::new(); + + loop { + if it.eof() { + break; + } + + let key = expect_ident(it); + + if seen_keys.contains(&key) { + panic!( + "Duplicated key \"{}\". Keys can only be specified once.", + key + ); + } + + assert_eq!(expect_punct(it), ':'); + + match key.as_str() { + "type" => info.type_ = expect_ident(it), + "name" => info.name = expect_string(it), + "author" => info.author = parse_item_or_list(it, Delimiter::Bracket, expect_string), + "description" => info.description = Some(expect_string(it)), + "license" => info.license = expect_string(it), + "alias" => info.alias = parse_item_or_list(it, Delimiter::Bracket, expect_string), + "alias_rtnl_link" => { + info.alias = parse_item_or_list(it, Delimiter::Bracket, expect_string) + .into_iter() + .map(|x| format!("rtnl-link-{}", x)) + .collect() + } + "params" => info.params = parse_list(it, Delimiter::Brace, ParamInfo::parse), + _ => panic!( + "Unknown key \"{}\". Valid keys are: {:?}.", + key, EXPECTED_KEYS + ), + } + + assert_eq!(expect_punct(it), ','); + + seen_keys.push(key); + } + + expect_end(it); + + for key in REQUIRED_KEYS { + if !seen_keys.iter().any(|e| e == key) { + panic!("Missing required key \"{}\".", key); + } + } + + let mut ordered_keys: Vec<&str> = Vec::new(); + for key in EXPECTED_KEYS { + if seen_keys.iter().any(|e| e == key) { + ordered_keys.push(key); + } + } + + if seen_keys != ordered_keys { + panic!( + "Keys are not ordered as expected. Order them like: {:?}.", + ordered_keys + ); + } + + info + } + + fn generate(&self) -> TokenStream { + let mut modinfo = ModInfoBuilder::new(&self.name); + for author in self.author.iter() { + modinfo.emit("author", author); + } + modinfo.emit_optional("description", self.description.as_deref()); + modinfo.emit("license", &self.license); + for alias in self.alias.iter() { + modinfo.emit("alias", alias); + } + + // Built-in modules also export the `file` modinfo string + let file = std::env::var("RUST_MODFILE") + .expect("Unable to fetch RUST_MODFILE environmental variable"); + modinfo.emit_only_builtin("file", &file); + + let mut array_types_to_generate = Vec::new(); + for param in self.params.iter() { + // TODO: more primitive types + // TODO: other kinds: unsafes, etc. + let (param_kernel_type, ops): (String, _) = match param.type_ { + ParamType::Ident(ref param_type) => ( + param_type.to_string(), + param_ops_path(¶m_type).to_string(), + ), + ParamType::Array { + ref vals, + max_length, + } => { + array_types_to_generate.push((vals.clone(), max_length)); + ( + format!("__rust_array_param_{}_{}", vals, max_length), + generated_array_ops_name(vals, max_length), + ) + } + }; + + modinfo.emit_param("parmtype", ¶m.name, ¶m_kernel_type); + modinfo.emit_param("parm", ¶m.name, ¶m.description); + let param_type_internal = match param.type_ { + ParamType::Ident(ref param_type) => match param_type.as_ref() { + "str" => "kernel::module_param::StringParam".to_string(), + other => other.to_string(), + }, + ParamType::Array { + ref vals, + max_length, + } => format!( + "kernel::module_param::ArrayParam<{vals}, {max_length}>", + vals = vals, + max_length = max_length + ), + }; + let read_func = if permissions_are_readonly(param.permission) { + format!( + " + fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ + // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} + }} + ", + name = self.name, + param_name = param.name, + param_type_internal = param_type_internal, + ) + } else { + format!( + " + fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ + // SAFETY: Parameters are locked by `KParamGuard`. + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} + }} + ", + name = self.name, + param_name = param.name, + param_type_internal = param_type_internal, + ) + }; + let kparam = format!( + " + kernel::bindings::kernel_param__bindgen_ty_1 {{ + arg: unsafe {{ &__{name}_{param_name}_value }} as *const _ as *mut kernel::c_types::c_void, + }}, + ", + name = self.name, + param_name = param.name, + ); + modinfo.buffer.push_str( + &format!( + " + static mut __{name}_{param_name}_value: {param_type_internal} = {param_default}; + + struct __{name}_{param_name}; + + impl __{name}_{param_name} {{ {read_func} }} + + const {param_name}: __{name}_{param_name} = __{name}_{param_name}; + + // Note: the C macro that generates the static structs for the `__param` section + // asks for them to be `aligned(sizeof(void *))`. However, that was put in place + // in 2003 in commit 38d5b085d2 (\"[PATCH] Fix over-alignment problem on x86-64\") + // to undo GCC over-alignment of static structs of >32 bytes. It seems that is + // not the case anymore, so we simplify to a transparent representation here + // in the expectation that it is not needed anymore. + // TODO: revisit this to confirm the above comment and remove it if it happened + #[repr(transparent)] + struct __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param); + + unsafe impl Sync for __{name}_{param_name}_RacyKernelParam {{ + }} + + #[cfg(not(MODULE))] + const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{name}.{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; + + #[cfg(MODULE)] + const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; + + #[link_section = \"__param\"] + #[used] + static __{name}_{param_name}_struct: __{name}_{param_name}_RacyKernelParam = __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param {{ + name: __{name}_{param_name}_name, + // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. + #[cfg(MODULE)] + mod_: unsafe {{ &kernel::bindings::__this_module as *const _ as *mut _ }}, + #[cfg(not(MODULE))] + mod_: core::ptr::null_mut(), + ops: unsafe {{ &{ops} }} as *const kernel::bindings::kernel_param_ops, + perm: {permissions}, + level: -1, + flags: 0, + __bindgen_anon_1: {kparam} + }}); + ", + name = self.name, + param_type_internal = param_type_internal, + read_func = read_func, + param_default = param.default, + param_name = param.name, + ops = ops, + permissions = param.permission, + kparam = kparam, + ) + ); + } + + let mut generated_array_types = String::new(); + + for (vals, max_length) in array_types_to_generate { + let ops_name = generated_array_ops_name(&vals, max_length); + generated_array_types.push_str(&format!( + " + kernel::make_param_ops!( + {ops_name}, + kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}> + ); + ", + ops_name = ops_name, + vals = vals, + max_length = max_length, + )); + } + + format!( + " + /// The module name. + /// + /// Used by the printing macros, e.g. [`info!`]. + const __LOG_PREFIX: &[u8] = b\"{name}\\0\"; + + static mut __MOD: Option<{type_}> = None; + + // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. + #[cfg(MODULE)] + static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(&kernel::bindings::__this_module as *const _ as *mut _) }}; + #[cfg(not(MODULE))] + static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(core::ptr::null_mut()) }}; + + // Loadable modules need to export the `{{init,cleanup}}_module` identifiers + #[cfg(MODULE)] + #[no_mangle] + pub extern \"C\" fn init_module() -> kernel::c_types::c_int {{ + __init() + }} + + #[cfg(MODULE)] + #[no_mangle] + pub extern \"C\" fn cleanup_module() {{ + __exit() + }} + + // 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))] + #[link_section = \"{initcall_section}\"] + #[used] + pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init; + + #[cfg(not(MODULE))] + #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] + global_asm!( + r#\".section \"{initcall_section}\", \"a\" + __{name}_initcall: + .long __{name}_init - . + .previous + \"# + ); + + #[cfg(not(MODULE))] + #[no_mangle] + pub extern \"C\" fn __{name}_init() -> kernel::c_types::c_int {{ + __init() + }} + + #[cfg(not(MODULE))] + #[no_mangle] + pub extern \"C\" fn __{name}_exit() {{ + __exit() + }} + + fn __init() -> kernel::c_types::c_int {{ + match <{type_} as kernel::KernelModule>::init() {{ + Ok(m) => {{ + unsafe {{ + __MOD = Some(m); + }} + return 0; + }} + Err(e) => {{ + return e.to_kernel_errno(); + }} + }} + }} + + fn __exit() {{ + unsafe {{ + // Invokes `drop()` on `__MOD`, which should be used for cleanup. + __MOD = None; + }} + }} + + {modinfo} + + {generated_array_types} + ", + type_ = self.type_, + name = self.name, + modinfo = modinfo.buffer, + generated_array_types = generated_array_types, + initcall_section = ".initcall6.init" + ).parse().expect("Error parsing formatted string into token stream.") + } +} + +pub fn module(ts: TokenStream) -> TokenStream { + let buffer = TokenBuffer::new(ts); + let mut it = buffer.begin(); + + let info = ModuleInfo::parse(&mut it); + info.generate() +} + +pub fn module_misc_device(ts: TokenStream) -> TokenStream { + let buffer = TokenBuffer::new(ts); + let mut it = buffer.begin(); + + let mut info = ModuleInfo::parse(&mut it); + let type_ = info.type_; + let module = format!("__internal_ModuleFor{}", type_); + info.type_ = module.clone(); + + let extra = format!( + " + #[doc(hidden)] + struct {module} {{ + _dev: core::pin::Pin>, + }} + + impl kernel::KernelModule for {module} {{ + fn init() -> kernel::Result {{ + Ok(Self {{ + _dev: kernel::miscdev::Registration::new_pinned::<{type_}>( + kernel::c_str!({name}), + None, + (), + )?, + }}) + }} + }} + ", + module = module, + type_ = type_, + name = Literal::string(&info.name), + ) + .parse() + .expect("Error parsing formatted string into token stream."); + + vec![extra, info.generate()].into_iter().collect() +} diff --git a/rust/macros/syn/buffer.rs b/rust/macros/syn/buffer.rs new file mode 100644 index 00000000000000..2c86512e51f392 --- /dev/null +++ b/rust/macros/syn/buffer.rs @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 + +// Adapted from https://github.com/dtolnay/syn/blob/1.0.72/src/buffer.rs +// Changes made compared to upstream: +// * Removed code that depends on proc_macro2 +// * Removed lifetime parsing which depends on types defined elsewhere in syn. + +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Span, TokenStream, TokenTree}; +use std::marker::PhantomData; +use std::ptr; + +/// Internal type which is used instead of `TokenTree` to represent a token tree +/// within a `TokenBuffer`. +enum Entry { + // Mimicking types from proc-macro. + Group(Group, TokenBuffer), + Ident(Ident), + Punct(Punct), + Literal(Literal), + // End entries contain a raw pointer to the entry from the containing + // token tree, or null if this is the outermost level. + End(*const Entry), +} + +/// A buffer that can be efficiently traversed multiple times, unlike +/// `TokenStream` which requires a deep copy in order to traverse more than +/// once. +/// +/// *This type is available only if Syn is built with the `"parsing"` feature.* +pub struct TokenBuffer { + // NOTE: Do not derive clone on this - there are raw pointers inside which + // will be messed up. Moving the `TokenBuffer` itself is safe as the actual + // backing slices won't be moved. + data: Box<[Entry]>, +} + +impl TokenBuffer { + // NOTE: DO NOT MUTATE THE `Vec` RETURNED FROM THIS FUNCTION ONCE IT + // RETURNS, THE ADDRESS OF ITS BACKING MEMORY MUST REMAIN STABLE. + fn inner_new(stream: TokenStream, up: *const Entry) -> TokenBuffer { + // Build up the entries list, recording the locations of any Groups + // in the list to be processed later. + let mut entries = Vec::new(); + let mut seqs = Vec::new(); + for tt in stream { + match tt { + TokenTree::Ident(sym) => { + entries.push(Entry::Ident(sym)); + } + TokenTree::Punct(op) => { + entries.push(Entry::Punct(op)); + } + TokenTree::Literal(l) => { + entries.push(Entry::Literal(l)); + } + TokenTree::Group(g) => { + // Record the index of the interesting entry, and store an + // `End(null)` there temporarially. + seqs.push((entries.len(), g)); + entries.push(Entry::End(ptr::null())); + } + } + } + // Add an `End` entry to the end with a reference to the enclosing token + // stream which was passed in. + entries.push(Entry::End(up)); + + // NOTE: This is done to ensure that we don't accidentally modify the + // length of the backing buffer. The backing buffer must remain at a + // constant address after this point, as we are going to store a raw + // pointer into it. + let mut entries = entries.into_boxed_slice(); + for (idx, group) in seqs { + // We know that this index refers to one of the temporary + // `End(null)` entries, and we know that the last entry is + // `End(up)`, so the next index is also valid. + let seq_up = &entries[idx + 1] as *const Entry; + + // The end entry stored at the end of this Entry::Group should + // point to the Entry which follows the Group in the list. + let inner = Self::inner_new(group.stream(), seq_up); + entries[idx] = Entry::Group(group, inner); + } + + TokenBuffer { data: entries } + } + + /// Creates a `TokenBuffer` containing all the tokens from the input + /// `TokenStream`. + pub fn new(stream: TokenStream) -> TokenBuffer { + Self::inner_new(stream, ptr::null()) + } + + /// Creates a cursor referencing the first token in the buffer and able to + /// traverse until the end of the buffer. + pub fn begin(&self) -> Cursor { + unsafe { Cursor::create(&self.data[0], &self.data[self.data.len() - 1]) } + } +} + +/// A cheaply copyable cursor into a `TokenBuffer`. +/// +/// This cursor holds a shared reference into the immutable data which is used +/// internally to represent a `TokenStream`, and can be efficiently manipulated +/// and copied around. +/// +/// An empty `Cursor` can be created directly, or one may create a `TokenBuffer` +/// object and get a cursor to its first token with `begin()`. +/// +/// Two cursors are equal if they have the same location in the same input +/// stream, and have the same scope. +/// +/// *This type is available only if Syn is built with the `"parsing"` feature.* +pub struct Cursor<'a> { + // The current entry which the `Cursor` is pointing at. + ptr: *const Entry, + // This is the only `Entry::End(..)` object which this cursor is allowed to + // point at. All other `End` objects are skipped over in `Cursor::create`. + scope: *const Entry, + // Cursor is covariant in 'a. This field ensures that our pointers are still + // valid. + marker: PhantomData<&'a Entry>, +} + +impl<'a> Cursor<'a> { + /// Creates a cursor referencing a static empty TokenStream. + pub fn empty() -> Self { + // It's safe in this situation for us to put an `Entry` object in global + // storage, despite it not actually being safe to send across threads + // (`Ident` is a reference into a thread-local table). This is because + // this entry never includes a `Ident` object. + // + // This wrapper struct allows us to break the rules and put a `Sync` + // object in global storage. + struct UnsafeSyncEntry(Entry); + unsafe impl Sync for UnsafeSyncEntry {} + static EMPTY_ENTRY: UnsafeSyncEntry = UnsafeSyncEntry(Entry::End(0 as *const Entry)); + + Cursor { + ptr: &EMPTY_ENTRY.0, + scope: &EMPTY_ENTRY.0, + marker: PhantomData, + } + } + + /// This create method intelligently exits non-explicitly-entered + /// `None`-delimited scopes when the cursor reaches the end of them, + /// allowing for them to be treated transparently. + unsafe fn create(mut ptr: *const Entry, scope: *const Entry) -> Self { + // NOTE: If we're looking at a `End(..)`, we want to advance the cursor + // past it, unless `ptr == scope`, which means that we're at the edge of + // our cursor's scope. We should only have `ptr != scope` at the exit + // from None-delimited groups entered with `ignore_none`. + while let Entry::End(exit) = *ptr { + if ptr == scope { + break; + } + ptr = exit; + } + + Cursor { + ptr, + scope, + marker: PhantomData, + } + } + + /// Get the current entry. + fn entry(self) -> &'a Entry { + unsafe { &*self.ptr } + } + + /// Bump the cursor to point at the next token after the current one. This + /// is undefined behavior if the cursor is currently looking at an + /// `Entry::End`. + unsafe fn bump(self) -> Cursor<'a> { + Cursor::create(self.ptr.offset(1), self.scope) + } + + /// While the cursor is looking at a `None`-delimited group, move it to look + /// at the first token inside instead. If the group is empty, this will move + /// the cursor past the `None`-delimited group. + /// + /// WARNING: This mutates its argument. + fn ignore_none(&mut self) { + while let Entry::Group(group, buf) = self.entry() { + if group.delimiter() == Delimiter::None { + // NOTE: We call `Cursor::create` here to make sure that + // situations where we should immediately exit the span after + // entering it are handled correctly. + unsafe { + *self = Cursor::create(&buf.data[0], self.scope); + } + } else { + break; + } + } + } + + /// Checks whether the cursor is currently pointing at the end of its valid + /// scope. + pub fn eof(self) -> bool { + // We're at eof if we're at the end of our scope. + self.ptr == self.scope + } + + /// If the cursor is pointing at a `Group` with the given delimiter, returns + /// a cursor into that group and one pointing to the next `TokenTree`. + pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, Span, Cursor<'a>)> { + // If we're not trying to enter a none-delimited group, we want to + // ignore them. We have to make sure to _not_ ignore them when we want + // to enter them, of course. For obvious reasons. + if delim != Delimiter::None { + self.ignore_none(); + } + + if let Entry::Group(group, buf) = self.entry() { + if group.delimiter() == delim { + return Some((buf.begin(), group.span(), unsafe { self.bump() })); + } + } + + None + } + + /// If the cursor is pointing at a `Ident`, returns it along with a cursor + /// pointing at the next `TokenTree`. + pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Ident(ident) => Some((ident.clone(), unsafe { self.bump() })), + _ => None, + } + } + + /// If the cursor is pointing at an `Punct`, returns it along with a cursor + /// pointing at the next `TokenTree`. + pub fn punct(mut self) -> Option<(Punct, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Punct(op) if op.as_char() != '\'' => Some((op.clone(), unsafe { self.bump() })), + _ => None, + } + } + + /// If the cursor is pointing at a `Literal`, return it along with a cursor + /// pointing at the next `TokenTree`. + pub fn literal(mut self) -> Option<(Literal, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Literal(lit) => Some((lit.clone(), unsafe { self.bump() })), + _ => None, + } + } + + /// Copies all remaining tokens visible from this cursor into a + /// `TokenStream`. + pub fn token_stream(self) -> TokenStream { + let mut tts = Vec::new(); + let mut cursor = self; + while let Some((tt, rest)) = cursor.token_tree() { + tts.push(tt); + cursor = rest; + } + tts.into_iter().collect() + } + + /// If the cursor is pointing at a `TokenTree`, returns it along with a + /// cursor pointing at the next `TokenTree`. + /// + /// Returns `None` if the cursor has reached the end of its stream. + /// + /// This method does not treat `None`-delimited groups as transparent, and + /// will return a `Group(None, ..)` if the cursor is looking at one. + pub fn token_tree(self) -> Option<(TokenTree, Cursor<'a>)> { + let tree = match self.entry() { + Entry::Group(group, _) => group.clone().into(), + Entry::Literal(lit) => lit.clone().into(), + Entry::Ident(ident) => ident.clone().into(), + Entry::Punct(op) => op.clone().into(), + Entry::End(..) => { + return None; + } + }; + + Some((tree, unsafe { self.bump() })) + } + + /// Returns the `Span` of the current token, or `Span::call_site()` if this + /// cursor points to eof. + pub fn span(self) -> Span { + match self.entry() { + Entry::Group(group, _) => group.span(), + Entry::Literal(l) => l.span(), + Entry::Ident(t) => t.span(), + Entry::Punct(o) => o.span(), + Entry::End(..) => Span::call_site(), + } + } +} + +impl<'a> Copy for Cursor<'a> {} + +impl<'a> Clone for Cursor<'a> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a> Eq for Cursor<'a> {} + +impl<'a> PartialEq for Cursor<'a> { + fn eq(&self, other: &Self) -> bool { + let Cursor { ptr, scope, marker } = self; + let _ = marker; + *ptr == other.ptr && *scope == other.scope + } +} diff --git a/rust/macros/syn/lit.rs b/rust/macros/syn/lit.rs new file mode 100644 index 00000000000000..0daf3097dc357a --- /dev/null +++ b/rust/macros/syn/lit.rs @@ -0,0 +1,883 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 + +// Adapted from https://github.com/dtolnay/syn/blob/1.0.72/src/lit.rs +// Changes made compared to upstream: +// * Removed code that depends on types and macros defined elsewhere in syn. +// * Removed dependency on `BigInt` and `unicode-xid`. +// * Removed `From` and `new` from `LitInt` and `LitFloat` which are +// unlikely to be used by us. + +use proc_macro::{Literal, Span}; + +/// A Rust literal such as a string or integer or boolean. +/// +/// # Syntax tree enum +/// +/// This type is a [syntax tree enum]. +/// +/// [syntax tree enum]: crate::Expr#syntax-tree-enums +pub enum Lit { + /// A UTF-8 string literal: `"foo"`. + Str(LitStr), + + /// A byte string literal: `b"foo"`. + ByteStr(LitByteStr), + + /// A byte literal: `b'f'`. + Byte(LitByte), + + /// A character literal: `'a'`. + Char(LitChar), + + /// An integer literal: `1` or `1u16`. + Int(LitInt), + + /// A floating point literal: `1f64` or `1.0e10f64`. + /// + /// Must be finite. May not be infinite or NaN. + Float(LitFloat), + + /// A boolean literal: `true` or `false`. + Bool(LitBool), + + /// A raw token literal not interpreted by Syn. + Verbatim(Literal), +} + +/// A UTF-8 string literal: `"foo"`. +pub struct LitStr { + repr: Box, +} + +/// A byte string literal: `b"foo"`. +pub struct LitByteStr { + repr: Box, +} + +/// A byte literal: `b'f'`. +pub struct LitByte { + repr: Box, +} + +/// A character literal: `'a'`. +pub struct LitChar { + repr: Box, +} + +struct LitRepr { + token: Literal, + suffix: Box, +} + +/// An integer literal: `1` or `1u16`. +pub struct LitInt { + repr: Box, +} + +struct LitIntRepr { + token: Literal, + digits: Box, + suffix: Box, +} + +/// A floating point literal: `1f64` or `1.0e10f64`. +/// +/// Must be finite. May not be infinite or NaN. +pub struct LitFloat { + repr: Box, +} + +struct LitFloatRepr { + token: Literal, + digits: Box, + suffix: Box, +} + +/// A boolean literal: `true` or `false`. +pub struct LitBool { + pub value: bool, + pub span: Span, +} + +impl LitStr { + pub fn new(value: &str, span: Span) -> Self { + let mut token = Literal::string(value); + token.set_span(span); + LitStr { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> String { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_str(&repr); + String::from(value) + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitByteStr { + pub fn new(value: &[u8], span: Span) -> Self { + let mut token = Literal::byte_string(value); + token.set_span(span); + LitByteStr { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> Vec { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_byte_str(&repr); + value + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitByte { + pub fn new(value: u8, span: Span) -> Self { + let mut token = Literal::u8_suffixed(value); + token.set_span(span); + LitByte { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> u8 { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_byte(&repr); + value + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitChar { + pub fn new(value: char, span: Span) -> Self { + let mut token = Literal::character(value); + token.set_span(span); + LitChar { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> char { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_char(&repr); + value + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitInt { + pub fn base10_digits(&self) -> &str { + &self.repr.digits + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } +} + +impl LitFloat { + pub fn suffix(&self) -> &str { + &self.repr.suffix + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } +} + +impl LitBool { + pub fn new(value: bool, span: Span) -> Self { + LitBool { value, span } + } + + pub fn value(&self) -> bool { + self.value + } + + pub fn span(&self) -> Span { + self.span + } + + pub fn set_span(&mut self, span: Span) { + self.span = span; + } +} + +mod value { + use super::*; + use std::char; + use std::ops::{Index, RangeFrom}; + + impl Lit { + /// Interpret a Syn literal from a proc-macro2 literal. + pub fn new(token: Literal) -> Self { + let repr = token.to_string(); + + match byte(&repr, 0) { + b'"' | b'r' => { + let (_, suffix) = parse_lit_str(&repr); + return Lit::Str(LitStr { + repr: Box::new(LitRepr { token, suffix }), + }); + } + b'b' => match byte(&repr, 1) { + b'"' | b'r' => { + let (_, suffix) = parse_lit_byte_str(&repr); + return Lit::ByteStr(LitByteStr { + repr: Box::new(LitRepr { token, suffix }), + }); + } + b'\'' => { + let (_, suffix) = parse_lit_byte(&repr); + return Lit::Byte(LitByte { + repr: Box::new(LitRepr { token, suffix }), + }); + } + _ => {} + }, + b'\'' => { + let (_, suffix) = parse_lit_char(&repr); + return Lit::Char(LitChar { + repr: Box::new(LitRepr { token, suffix }), + }); + } + b'0'..=b'9' | b'-' => { + if let Some((digits, suffix)) = parse_lit_int(&repr) { + return Lit::Int(LitInt { + repr: Box::new(LitIntRepr { + token, + digits, + suffix, + }), + }); + } + if let Some((digits, suffix)) = parse_lit_float(&repr) { + return Lit::Float(LitFloat { + repr: Box::new(LitFloatRepr { + token, + digits, + suffix, + }), + }); + } + } + b't' | b'f' => { + if repr == "true" || repr == "false" { + return Lit::Bool(LitBool { + value: repr == "true", + span: token.span(), + }); + } + } + _ => {} + } + + panic!("Unrecognized literal: `{}`", repr); + } + + pub fn suffix(&self) -> &str { + match self { + Lit::Str(lit) => lit.suffix(), + Lit::ByteStr(lit) => lit.suffix(), + Lit::Byte(lit) => lit.suffix(), + Lit::Char(lit) => lit.suffix(), + Lit::Int(lit) => lit.suffix(), + Lit::Float(lit) => lit.suffix(), + Lit::Bool(_) | Lit::Verbatim(_) => "", + } + } + + pub fn span(&self) -> Span { + match self { + Lit::Str(lit) => lit.span(), + Lit::ByteStr(lit) => lit.span(), + Lit::Byte(lit) => lit.span(), + Lit::Char(lit) => lit.span(), + Lit::Int(lit) => lit.span(), + Lit::Float(lit) => lit.span(), + Lit::Bool(lit) => lit.span, + Lit::Verbatim(lit) => lit.span(), + } + } + + pub fn set_span(&mut self, span: Span) { + match self { + Lit::Str(lit) => lit.set_span(span), + Lit::ByteStr(lit) => lit.set_span(span), + Lit::Byte(lit) => lit.set_span(span), + Lit::Char(lit) => lit.set_span(span), + Lit::Int(lit) => lit.set_span(span), + Lit::Float(lit) => lit.set_span(span), + Lit::Bool(lit) => lit.span = span, + Lit::Verbatim(lit) => lit.set_span(span), + } + } + } + + /// Get the byte at offset idx, or a default of `b'\0'` if we're looking + /// past the end of the input buffer. + pub fn byte + ?Sized>(s: &S, idx: usize) -> u8 { + let s = s.as_ref(); + if idx < s.len() { + s[idx] + } else { + 0 + } + } + + fn next_chr(s: &str) -> char { + s.chars().next().unwrap_or('\0') + } + + // Returns (content, suffix). + pub fn parse_lit_str(s: &str) -> (Box, Box) { + match byte(s, 0) { + b'"' => parse_lit_str_cooked(s), + b'r' => parse_lit_str_raw(s), + _ => unreachable!(), + } + } + + // Clippy false positive + // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 + #[allow(clippy::needless_continue)] + fn parse_lit_str_cooked(mut s: &str) -> (Box, Box) { + assert_eq!(byte(s, 0), b'"'); + s = &s[1..]; + + let mut content = String::new(); + 'outer: loop { + let ch = match byte(s, 0) { + b'"' => break, + b'\\' => { + let b = byte(s, 1); + s = &s[2..]; + match b { + b'x' => { + let (byte, rest) = backslash_x(s); + s = rest; + assert!(byte <= 0x80, "Invalid \\x byte in string literal"); + char::from_u32(u32::from(byte)).unwrap() + } + b'u' => { + let (chr, rest) = backslash_u(s); + s = rest; + chr + } + b'n' => '\n', + b'r' => '\r', + b't' => '\t', + b'\\' => '\\', + b'0' => '\0', + b'\'' => '\'', + b'"' => '"', + b'\r' | b'\n' => loop { + let ch = next_chr(s); + if ch.is_whitespace() { + s = &s[ch.len_utf8()..]; + } else { + continue 'outer; + } + }, + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + b'\r' => { + assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string"); + s = &s[2..]; + '\n' + } + _ => { + let ch = next_chr(s); + s = &s[ch.len_utf8()..]; + ch + } + }; + content.push(ch); + } + + assert!(s.starts_with('"')); + let content = content.into_boxed_str(); + let suffix = s[1..].to_owned().into_boxed_str(); + (content, suffix) + } + + fn parse_lit_str_raw(mut s: &str) -> (Box, Box) { + assert_eq!(byte(s, 0), b'r'); + s = &s[1..]; + + let mut pounds = 0; + while byte(s, pounds) == b'#' { + pounds += 1; + } + assert_eq!(byte(s, pounds), b'"'); + let close = s.rfind('"').unwrap(); + for end in s[close + 1..close + 1 + pounds].bytes() { + assert_eq!(end, b'#'); + } + + let content = s[pounds + 1..close].to_owned().into_boxed_str(); + let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str(); + (content, suffix) + } + + // Returns (content, suffix). + pub fn parse_lit_byte_str(s: &str) -> (Vec, Box) { + assert_eq!(byte(s, 0), b'b'); + match byte(s, 1) { + b'"' => parse_lit_byte_str_cooked(s), + b'r' => parse_lit_byte_str_raw(s), + _ => unreachable!(), + } + } + + // Clippy false positive + // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 + #[allow(clippy::needless_continue)] + fn parse_lit_byte_str_cooked(mut s: &str) -> (Vec, Box) { + assert_eq!(byte(s, 0), b'b'); + assert_eq!(byte(s, 1), b'"'); + s = &s[2..]; + + // We're going to want to have slices which don't respect codepoint boundaries. + let mut v = s.as_bytes(); + + let mut out = Vec::new(); + 'outer: loop { + let byte = match byte(v, 0) { + b'"' => break, + b'\\' => { + let b = byte(v, 1); + v = &v[2..]; + match b { + b'x' => { + let (b, rest) = backslash_x(v); + v = rest; + b + } + b'n' => b'\n', + b'r' => b'\r', + b't' => b'\t', + b'\\' => b'\\', + b'0' => b'\0', + b'\'' => b'\'', + b'"' => b'"', + b'\r' | b'\n' => loop { + let byte = byte(v, 0); + let ch = char::from_u32(u32::from(byte)).unwrap(); + if ch.is_whitespace() { + v = &v[1..]; + } else { + continue 'outer; + } + }, + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + b'\r' => { + assert_eq!(byte(v, 1), b'\n', "Bare CR not allowed in string"); + v = &v[2..]; + b'\n' + } + b => { + v = &v[1..]; + b + } + }; + out.push(byte); + } + + assert_eq!(byte(v, 0), b'"'); + let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); + (out, suffix) + } + + fn parse_lit_byte_str_raw(s: &str) -> (Vec, Box) { + assert_eq!(byte(s, 0), b'b'); + let (value, suffix) = parse_lit_str_raw(&s[1..]); + (String::from(value).into_bytes(), suffix) + } + + // Returns (value, suffix). + pub fn parse_lit_byte(s: &str) -> (u8, Box) { + assert_eq!(byte(s, 0), b'b'); + assert_eq!(byte(s, 1), b'\''); + + // We're going to want to have slices which don't respect codepoint boundaries. + let mut v = s[2..].as_bytes(); + + let b = match byte(v, 0) { + b'\\' => { + let b = byte(v, 1); + v = &v[2..]; + match b { + b'x' => { + let (b, rest) = backslash_x(v); + v = rest; + b + } + b'n' => b'\n', + b'r' => b'\r', + b't' => b'\t', + b'\\' => b'\\', + b'0' => b'\0', + b'\'' => b'\'', + b'"' => b'"', + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + b => { + v = &v[1..]; + b + } + }; + + assert_eq!(byte(v, 0), b'\''); + let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); + (b, suffix) + } + + // Returns (value, suffix). + pub fn parse_lit_char(mut s: &str) -> (char, Box) { + assert_eq!(byte(s, 0), b'\''); + s = &s[1..]; + + let ch = match byte(s, 0) { + b'\\' => { + let b = byte(s, 1); + s = &s[2..]; + match b { + b'x' => { + let (byte, rest) = backslash_x(s); + s = rest; + assert!(byte <= 0x80, "Invalid \\x byte in string literal"); + char::from_u32(u32::from(byte)).unwrap() + } + b'u' => { + let (chr, rest) = backslash_u(s); + s = rest; + chr + } + b'n' => '\n', + b'r' => '\r', + b't' => '\t', + b'\\' => '\\', + b'0' => '\0', + b'\'' => '\'', + b'"' => '"', + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + _ => { + let ch = next_chr(s); + s = &s[ch.len_utf8()..]; + ch + } + }; + assert_eq!(byte(s, 0), b'\''); + let suffix = s[1..].to_owned().into_boxed_str(); + (ch, suffix) + } + + fn backslash_x(s: &S) -> (u8, &S) + where + S: Index, Output = S> + AsRef<[u8]> + ?Sized, + { + let mut ch = 0; + let b0 = byte(s, 0); + let b1 = byte(s, 1); + ch += 0x10 + * match b0 { + b'0'..=b'9' => b0 - b'0', + b'a'..=b'f' => 10 + (b0 - b'a'), + b'A'..=b'F' => 10 + (b0 - b'A'), + _ => panic!("unexpected non-hex character after \\x"), + }; + ch += match b1 { + b'0'..=b'9' => b1 - b'0', + b'a'..=b'f' => 10 + (b1 - b'a'), + b'A'..=b'F' => 10 + (b1 - b'A'), + _ => panic!("unexpected non-hex character after \\x"), + }; + (ch, &s[2..]) + } + + fn backslash_u(mut s: &str) -> (char, &str) { + if byte(s, 0) != b'{' { + panic!("{}", "expected { after \\u"); + } + s = &s[1..]; + + let mut ch = 0; + let mut digits = 0; + loop { + let b = byte(s, 0); + let digit = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => 10 + b - b'a', + b'A'..=b'F' => 10 + b - b'A', + b'_' if digits > 0 => { + s = &s[1..]; + continue; + } + b'}' if digits == 0 => panic!("invalid empty unicode escape"), + b'}' => break, + _ => panic!("unexpected non-hex character after \\u"), + }; + if digits == 6 { + panic!("overlong unicode escape (must have at most 6 hex digits)"); + } + ch *= 0x10; + ch += u32::from(digit); + digits += 1; + s = &s[1..]; + } + assert!(byte(s, 0) == b'}'); + s = &s[1..]; + + if let Some(ch) = char::from_u32(ch) { + (ch, s) + } else { + panic!("character code {:x} is not a valid unicode character", ch); + } + } + + // Returns base 10 digits and suffix. + pub fn parse_lit_int(mut s: &str) -> Option<(Box, Box)> { + let negative = byte(s, 0) == b'-'; + if negative { + s = &s[1..]; + } + + let base = match (byte(s, 0), byte(s, 1)) { + (b'0', b'x') => { + s = &s[2..]; + 16 + } + (b'0', b'o') => { + s = &s[2..]; + 8 + } + (b'0', b'b') => { + s = &s[2..]; + 2 + } + (b'0'..=b'9', _) => 10, + _ => return None, + }; + + let mut value = 0u128; + 'outer: loop { + let b = byte(s, 0); + let digit = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' if base > 10 => b - b'a' + 10, + b'A'..=b'F' if base > 10 => b - b'A' + 10, + b'_' => { + s = &s[1..]; + continue; + } + // If looking at a floating point literal, we don't want to + // consider it an integer. + b'.' if base == 10 => return None, + b'e' | b'E' if base == 10 => { + let mut has_exp = false; + for (i, b) in s[1..].bytes().enumerate() { + match b { + b'_' => {} + b'-' | b'+' => return None, + b'0'..=b'9' => has_exp = true, + _ => { + let _suffix = &s[1 + i..]; + if has_exp { + return None; + } else { + break 'outer; + } + } + } + } + if has_exp { + return None; + } else { + break; + } + } + _ => break, + }; + + if digit >= base { + return None; + } + + value *= base as u128; + value += digit as u128; + s = &s[1..]; + } + + let suffix = s; + let mut repr = value.to_string(); + if negative { + repr.insert(0, '-'); + } + Some((repr.into_boxed_str(), suffix.to_owned().into_boxed_str())) + } + + // Returns base 10 digits and suffix. + pub fn parse_lit_float(input: &str) -> Option<(Box, Box)> { + // Rust's floating point literals are very similar to the ones parsed by + // the standard library, except that rust's literals can contain + // ignorable underscores. Let's remove those underscores. + + let mut bytes = input.to_owned().into_bytes(); + + let start = (*bytes.get(0)? == b'-') as usize; + match bytes.get(start)? { + b'0'..=b'9' => {} + _ => return None, + } + + let mut read = start; + let mut write = start; + let mut has_dot = false; + let mut has_e = false; + let mut has_sign = false; + let mut has_exponent = false; + while read < bytes.len() { + match bytes[read] { + b'_' => { + // Don't increase write + read += 1; + continue; + } + b'0'..=b'9' => { + if has_e { + has_exponent = true; + } + bytes[write] = bytes[read]; + } + b'.' => { + if has_e || has_dot { + return None; + } + has_dot = true; + bytes[write] = b'.'; + } + b'e' | b'E' => { + match bytes[read + 1..] + .iter() + .find(|b| **b != b'_') + .unwrap_or(&b'\0') + { + b'-' | b'+' | b'0'..=b'9' => {} + _ => break, + } + if has_e { + if has_exponent { + break; + } else { + return None; + } + } + has_e = true; + bytes[write] = b'e'; + } + b'-' | b'+' => { + if has_sign || has_exponent || !has_e { + return None; + } + has_sign = true; + if bytes[read] == b'-' { + bytes[write] = bytes[read]; + } else { + // Omit '+' + read += 1; + continue; + } + } + _ => break, + } + read += 1; + write += 1; + } + + if has_e && !has_exponent { + return None; + } + + let mut digits = String::from_utf8(bytes).unwrap(); + let suffix = digits.split_off(read); + digits.truncate(write); + Some((digits.into_boxed_str(), suffix.into_boxed_str())) + } +} diff --git a/rust/macros/syn/mod.rs b/rust/macros/syn/mod.rs new file mode 100644 index 00000000000000..f7afda5a2ce4fe --- /dev/null +++ b/rust/macros/syn/mod.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 + +//! Shared parsing functions for use in procedural macros. +//! +//! These code are from [syn 1.0.72](https://github.com/dtolnay/syn). + +#![allow(dead_code)] + +pub mod buffer; +mod lit; + +pub use lit::{Lit, LitByteStr, LitStr}; diff --git a/rust/module.rs b/rust/module.rs deleted file mode 100644 index 9a7009644d3cd7..00000000000000 --- a/rust/module.rs +++ /dev/null @@ -1,860 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! Proc macro crate implementing the [`module!`] magic. -//! -//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) - -#![deny(clippy::complexity)] -#![deny(clippy::correctness)] -#![deny(clippy::perf)] -#![deny(clippy::style)] - -use proc_macro::{token_stream, Delimiter, Group, TokenStream, TokenTree}; - -fn try_ident(it: &mut token_stream::IntoIter) -> Option { - if let Some(TokenTree::Ident(ident)) = it.next() { - Some(ident.to_string()) - } else { - None - } -} - -fn try_literal(it: &mut token_stream::IntoIter) -> Option { - if let Some(TokenTree::Literal(literal)) = it.next() { - Some(literal.to_string()) - } else { - None - } -} - -fn try_byte_string(it: &mut token_stream::IntoIter) -> Option { - 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()) - } else { - None - } - }) -} - -fn expect_ident(it: &mut token_stream::IntoIter) -> String { - try_ident(it).expect("Expected Ident") -} - -fn expect_punct(it: &mut token_stream::IntoIter) -> char { - if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") { - punct.as_char() - } else { - panic!("Expected Punct"); - } -} - -fn expect_literal(it: &mut token_stream::IntoIter) -> String { - try_literal(it).expect("Expected Literal") -} - -fn expect_group(it: &mut token_stream::IntoIter) -> Group { - if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") { - group - } else { - panic!("Expected Group"); - } -} - -fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { - try_byte_string(it).expect("Expected byte string") -} - -#[derive(Clone, PartialEq)] -enum ParamType { - Ident(String), - Array { vals: String, max_length: usize }, -} - -fn expect_array_fields(it: &mut token_stream::IntoIter) -> ParamType { - assert_eq!(expect_punct(it), '<'); - let vals = expect_ident(it); - assert_eq!(expect_punct(it), ','); - let max_length_str = expect_literal(it); - let max_length = max_length_str - .parse::() - .expect("Expected usize length"); - assert_eq!(expect_punct(it), '>'); - ParamType::Array { vals, max_length } -} - -fn expect_type(it: &mut token_stream::IntoIter) -> ParamType { - if let TokenTree::Ident(ident) = it - .next() - .expect("Reached end of token stream for param type") - { - match ident.to_string().as_ref() { - "ArrayParam" => expect_array_fields(it), - _ => ParamType::Ident(ident.to_string()), - } - } else { - panic!("Expected Param Type") - } -} - -fn expect_end(it: &mut token_stream::IntoIter) { - if it.next().is_some() { - panic!("Expected end"); - } -} - -fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { - assert_eq!(expect_ident(it), expected_name); - assert_eq!(expect_punct(it), ':'); - let literal = expect_literal(it); - assert_eq!(expect_punct(it), ','); - literal -} - -fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String { - assert_eq!(expect_ident(it), expected_name); - assert_eq!(expect_punct(it), ':'); - let byte_string = expect_byte_string(it); - assert_eq!(expect_punct(it), ','); - byte_string -} - -fn __build_modinfo_string_base( - module: &str, - field: &str, - content: &str, - variable: &str, - builtin: bool, -) -> String { - let string = if builtin { - // Built-in modules prefix their modinfo strings by `module.`. - format!( - "{module}.{field}={content}", - module = module, - field = field, - content = content - ) - } else { - // Loadable modules' modinfo strings go as-is. - format!("{field}={content}", field = field, content = content) - }; - - format!( - " - {cfg} - #[link_section = \".modinfo\"] - #[used] - pub static {variable}: [u8; {length}] = *b\"{string}\\0\"; - ", - cfg = if builtin { - "#[cfg(not(MODULE))]" - } else { - "#[cfg(MODULE)]" - }, - variable = variable, - length = string.len() + 1, - string = string, - ) -} - -fn __build_modinfo_string_variable(module: &str, field: &str) -> String { - format!("__{module}_{field}", module = module, field = field) -} - -fn build_modinfo_string_only_builtin(module: &str, field: &str, content: &str) -> String { - __build_modinfo_string_base( - module, - field, - content, - &__build_modinfo_string_variable(module, field), - true, - ) -} - -fn build_modinfo_string_only_loadable(module: &str, field: &str, content: &str) -> String { - __build_modinfo_string_base( - module, - field, - content, - &__build_modinfo_string_variable(module, field), - false, - ) -} - -fn build_modinfo_string(module: &str, field: &str, content: &str) -> String { - build_modinfo_string_only_builtin(module, field, content) - + &build_modinfo_string_only_loadable(module, field, content) -} - -fn build_modinfo_string_optional(module: &str, field: &str, content: Option<&str>) -> String { - if let Some(content) = content { - build_modinfo_string(module, field, content) - } else { - "".to_string() - } -} - -fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &str) -> String { - let variable = format!( - "__{module}_{field}_{param}", - module = module, - field = field, - param = param - ); - let content = format!("{param}:{content}", param = param, content = content); - __build_modinfo_string_base(module, field, &content, &variable, true) - + &__build_modinfo_string_base(module, field, &content, &variable, false) -} - -fn permissions_are_readonly(perms: &str) -> bool { - let (radix, digits) = if let Some(n) = perms.strip_prefix("0x") { - (16, n) - } else if let Some(n) = perms.strip_prefix("0o") { - (8, n) - } else if let Some(n) = perms.strip_prefix("0b") { - (2, n) - } else { - (10, perms) - }; - match u32::from_str_radix(digits, radix) { - Ok(perms) => perms & 0o222 == 0, - Err(_) => false, - } -} - -fn param_ops_path(param_type: &str) -> &'static str { - match param_type { - "bool" => "kernel::module_param::PARAM_OPS_BOOL", - "i8" => "kernel::module_param::PARAM_OPS_I8", - "u8" => "kernel::module_param::PARAM_OPS_U8", - "i16" => "kernel::module_param::PARAM_OPS_I16", - "u16" => "kernel::module_param::PARAM_OPS_U16", - "i32" => "kernel::module_param::PARAM_OPS_I32", - "u32" => "kernel::module_param::PARAM_OPS_U32", - "i64" => "kernel::module_param::PARAM_OPS_I64", - "u64" => "kernel::module_param::PARAM_OPS_U64", - "isize" => "kernel::module_param::PARAM_OPS_ISIZE", - "usize" => "kernel::module_param::PARAM_OPS_USIZE", - "str" => "kernel::module_param::PARAM_OPS_STR", - t => panic!("Unrecognized type {}", t), - } -} - -fn try_simple_param_val( - param_type: &str, -) -> Box Option> { - match param_type { - "bool" => Box::new(|param_it| try_ident(param_it)), - "str" => Box::new(|param_it| { - try_byte_string(param_it) - .map(|s| format!("kernel::module_param::StringParam::Ref(b\"{}\")", s)) - }), - _ => Box::new(|param_it| try_literal(param_it)), - } -} - -fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> String { - let try_param_val = match param_type { - ParamType::Ident(ref param_type) - | ParamType::Array { - vals: ref param_type, - max_length: _, - } => try_simple_param_val(param_type), - }; - assert_eq!(expect_ident(param_it), "default"); - assert_eq!(expect_punct(param_it), ':'); - let default = match param_type { - ParamType::Ident(_) => try_param_val(param_it).expect("Expected default param value"), - ParamType::Array { - vals: _, - max_length: _, - } => { - let group = expect_group(param_it); - assert_eq!(group.delimiter(), Delimiter::Bracket); - let mut default_vals = Vec::new(); - let mut it = group.stream().into_iter(); - - while let Some(default_val) = try_param_val(&mut it) { - default_vals.push(default_val); - match it.next() { - Some(TokenTree::Punct(punct)) => assert_eq!(punct.as_char(), ','), - None => break, - _ => panic!("Expected ',' or end of array default values"), - } - } - - let mut default_array = "kernel::module_param::ArrayParam::create(&[".to_string(); - default_array.push_str( - &default_vals - .iter() - .map(|val| val.to_string()) - .collect::>() - .join(","), - ); - default_array.push_str("])"); - default_array - } - }; - assert_eq!(expect_punct(param_it), ','); - default -} - -fn generated_array_ops_name(vals: &str, max_length: usize) -> String { - format!( - "__generated_array_ops_{vals}_{max_length}", - vals = vals, - max_length = max_length - ) -} - -#[derive(Debug, Default)] -struct ModuleInfo { - type_: String, - license: String, - name: String, - author: Option, - description: Option, - alias: Option, - params: Option, -} - -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", - "alias_rtnl_link", - "params", - ]; - const REQUIRED_KEYS: &[&str] = &["type", "name", "license"]; - let mut seen_keys = Vec::new(); - - loop { - let key = match it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - Some(_) => panic!("Expected Ident or end"), - None => break, - }; - - if seen_keys.contains(&key) { - panic!( - "Duplicated key \"{}\". Keys can only be specified once.", - key - ); - } - - assert_eq!(expect_punct(it), ':'); - - 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)), - "alias_rtnl_link" => { - info.alias = Some(format!("rtnl-link-{}", expect_byte_string(it))) - } - "params" => info.params = Some(expect_group(it)), - _ => panic!( - "Unknown key \"{}\". Valid keys are: {:?}.", - key, EXPECTED_KEYS - ), - } - - assert_eq!(expect_punct(it), ','); - - seen_keys.push(key); - } - - expect_end(it); - - for key in REQUIRED_KEYS { - if !seen_keys.iter().any(|e| e == key) { - panic!("Missing required key \"{}\".", key); - } - } - - let mut ordered_keys: Vec<&str> = Vec::new(); - for key in EXPECTED_KEYS { - if seen_keys.iter().any(|e| e == key) { - ordered_keys.push(key); - } - } - - if seen_keys != ordered_keys { - panic!( - "Keys are not ordered as expected. Order them like: {:?}.", - ordered_keys - ); - } - - info - } -} - -/// Declares a kernel module. -/// -/// The `type` argument should be a type which implements the [`KernelModule`] -/// trait. Also accepts various forms of kernel metadata. -/// -/// [`KernelModule`]: ../kernel/trait.KernelModule.html -/// -/// # Examples -/// -/// ```rust,no_run -/// use kernel::prelude::*; -/// -/// module!{ -/// type: MyKernelModule, -/// name: b"my_kernel_module", -/// author: b"Rust for Linux Contributors", -/// description: b"My very own kernel module!", -/// license: b"GPL v2", -/// params: { -/// my_i32: i32 { -/// default: 42, -/// permissions: 0o000, -/// description: b"Example of i32", -/// }, -/// writeable_i32: i32 { -/// default: 42, -/// permissions: 0o644, -/// description: b"Example of i32", -/// }, -/// }, -/// } -/// -/// struct MyKernelModule; -/// -/// impl KernelModule for MyKernelModule { -/// fn init() -> Result { -/// // 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()); -/// Ok(MyKernelModule) -/// } -/// } -/// ``` -/// -/// # Supported argument types -/// - `type`: type which implements the [`KernelModule`] 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. -/// - `alias_rtnl_link`: byte array of the `rtnl_link_alias` of the kernel module (mutually exclusive with `alias`). -/// - `params`: parameters for the kernel module, as described below. -/// -/// # Supported parameter types -/// -/// - `bool`: Corresponds to C `bool` param type. -/// - `i8`: No equivalent C param type. -/// - `u8`: Corresponds to C `char` param type. -/// - `i16`: Corresponds to C `short` param type. -/// - `u16`: Corresponds to C `ushort` param type. -/// - `i32`: Corresponds to C `int` param type. -/// - `u32`: Corresponds to C `uint` param type. -/// - `i64`: No equivalent C param type. -/// - `u64`: Corresponds to C `ullong` param type. -/// - `isize`: No equivalent C param type. -/// - `usize`: No equivalent C param type. -/// - `str`: Corresponds to C `charp` param type. Reading returns a byte slice. -/// - `ArrayParam`: Corresponds to C parameters created using `module_param_array`. An array -/// of `T`'s of length at **most** `N`. -/// -/// `invbool` is unsupported: it was only ever used in a few modules. -/// Consider using a `bool` and inverting the logic instead. -#[proc_macro] -pub fn module(ts: TokenStream) -> TokenStream { - let mut it = ts.into_iter(); - - let info = ModuleInfo::parse(&mut it); - - let name = info.name.clone(); - - let mut array_types_to_generate = Vec::new(); - let mut params_modinfo = String::new(); - if let Some(params) = info.params { - assert_eq!(params.delimiter(), Delimiter::Brace); - - let mut it = params.stream().into_iter(); - - loop { - let param_name = match it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - Some(_) => panic!("Expected Ident or end"), - None => break, - }; - - assert_eq!(expect_punct(&mut it), ':'); - let param_type = expect_type(&mut it); - let group = expect_group(&mut it); - assert_eq!(expect_punct(&mut it), ','); - - assert_eq!(group.delimiter(), Delimiter::Brace); - - let mut param_it = group.stream().into_iter(); - let param_default = get_default(¶m_type, &mut param_it); - let param_permissions = get_literal(&mut param_it, "permissions"); - let param_description = get_byte_string(&mut param_it, "description"); - expect_end(&mut param_it); - - // TODO: more primitive types - // TODO: other kinds: unsafes, etc. - let (param_kernel_type, ops): (String, _) = match param_type { - ParamType::Ident(ref param_type) => ( - param_type.to_string(), - param_ops_path(¶m_type).to_string(), - ), - ParamType::Array { - ref vals, - max_length, - } => { - array_types_to_generate.push((vals.clone(), max_length)); - ( - format!("__rust_array_param_{}_{}", vals, max_length), - generated_array_ops_name(vals, max_length), - ) - } - }; - - params_modinfo.push_str(&build_modinfo_string_param( - &name, - "parmtype", - ¶m_name, - ¶m_kernel_type, - )); - params_modinfo.push_str(&build_modinfo_string_param( - &name, - "parm", - ¶m_name, - ¶m_description, - )); - let param_type_internal = match param_type { - ParamType::Ident(ref param_type) => match param_type.as_ref() { - "str" => "kernel::module_param::StringParam".to_string(), - other => other.to_string(), - }, - ParamType::Array { - ref vals, - max_length, - } => format!( - "kernel::module_param::ArrayParam<{vals}, {max_length}>", - vals = vals, - max_length = max_length - ), - }; - let read_func = if permissions_are_readonly(¶m_permissions) { - format!( - " - fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ - // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. - unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} - }} - ", - name = name, - param_name = param_name, - param_type_internal = param_type_internal, - ) - } else { - format!( - " - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ - // SAFETY: Parameters are locked by `KParamGuard`. - unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} - }} - ", - name = name, - param_name = param_name, - param_type_internal = param_type_internal, - ) - }; - let kparam = format!( - " - kernel::bindings::kernel_param__bindgen_ty_1 {{ - arg: unsafe {{ &__{name}_{param_name}_value }} as *const _ as *mut kernel::c_types::c_void, - }}, - ", - name = name, - param_name = param_name, - ); - params_modinfo.push_str( - &format!( - " - static mut __{name}_{param_name}_value: {param_type_internal} = {param_default}; - - struct __{name}_{param_name}; - - impl __{name}_{param_name} {{ {read_func} }} - - const {param_name}: __{name}_{param_name} = __{name}_{param_name}; - - // Note: the C macro that generates the static structs for the `__param` section - // asks for them to be `aligned(sizeof(void *))`. However, that was put in place - // in 2003 in commit 38d5b085d2 (\"[PATCH] Fix over-alignment problem on x86-64\") - // to undo GCC over-alignment of static structs of >32 bytes. It seems that is - // not the case anymore, so we simplify to a transparent representation here - // in the expectation that it is not needed anymore. - // TODO: revisit this to confirm the above comment and remove it if it happened - #[repr(transparent)] - struct __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param); - - unsafe impl Sync for __{name}_{param_name}_RacyKernelParam {{ - }} - - #[cfg(not(MODULE))] - const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{name}.{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; - - #[cfg(MODULE)] - const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; - - #[link_section = \"__param\"] - #[used] - static __{name}_{param_name}_struct: __{name}_{param_name}_RacyKernelParam = __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param {{ - name: __{name}_{param_name}_name, - // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. - #[cfg(MODULE)] - mod_: unsafe {{ &kernel::bindings::__this_module as *const _ as *mut _ }}, - #[cfg(not(MODULE))] - mod_: core::ptr::null_mut(), - ops: unsafe {{ &{ops} }} as *const kernel::bindings::kernel_param_ops, - perm: {permissions}, - level: -1, - flags: 0, - __bindgen_anon_1: {kparam} - }}); - ", - name = name, - param_type_internal = param_type_internal, - read_func = read_func, - param_default = param_default, - param_name = param_name, - ops = ops, - permissions = param_permissions, - kparam = kparam, - ) - ); - } - } - - let mut generated_array_types = String::new(); - - for (vals, max_length) in array_types_to_generate { - let ops_name = generated_array_ops_name(&vals, max_length); - generated_array_types.push_str(&format!( - " - kernel::make_param_ops!( - {ops_name}, - kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}> - ); - ", - ops_name = ops_name, - vals = vals, - max_length = max_length, - )); - } - - let file = - std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable"); - - format!( - " - /// The module name. - /// - /// Used by the printing macros, e.g. [`info!`]. - const __LOG_PREFIX: &[u8] = b\"{name}\\0\"; - - static mut __MOD: Option<{type_}> = None; - - // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. - #[cfg(MODULE)] - static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(&kernel::bindings::__this_module as *const _ as *mut _) }}; - #[cfg(not(MODULE))] - static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(core::ptr::null_mut()) }}; - - // Loadable modules need to export the `{{init,cleanup}}_module` identifiers - #[cfg(MODULE)] - #[no_mangle] - pub extern \"C\" fn init_module() -> kernel::c_types::c_int {{ - __init() - }} - - #[cfg(MODULE)] - #[no_mangle] - pub extern \"C\" fn cleanup_module() {{ - __exit() - }} - - // 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))] - #[link_section = \"{initcall_section}\"] - #[used] - pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init; - - #[cfg(not(MODULE))] - #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] - global_asm!( - r#\".section \"{initcall_section}\", \"a\" - __{name}_initcall: - .long __{name}_init - . - .previous - \"# - ); - - #[cfg(not(MODULE))] - #[no_mangle] - pub extern \"C\" fn __{name}_init() -> kernel::c_types::c_int {{ - __init() - }} - - #[cfg(not(MODULE))] - #[no_mangle] - pub extern \"C\" fn __{name}_exit() {{ - __exit() - }} - - fn __init() -> kernel::c_types::c_int {{ - match <{type_} as kernel::KernelModule>::init() {{ - Ok(m) => {{ - unsafe {{ - __MOD = Some(m); - }} - return 0; - }} - Err(e) => {{ - return e.to_kernel_errno(); - }} - }} - }} - - fn __exit() {{ - unsafe {{ - // Invokes `drop()` on `__MOD`, which should be used for cleanup. - __MOD = None; - }} - }} - - {author} - {description} - {license} - {alias} - - // Built-in modules also export the `file` modinfo string - {file} - - {params_modinfo} - - {generated_array_types} - ", - type_ = info.type_, - name = info.name, - author = &build_modinfo_string_optional(&name, "author", info.author.as_deref()), - description = &build_modinfo_string_optional(&name, "description", info.description.as_deref()), - license = &build_modinfo_string(&name, "license", &info.license), - alias = &build_modinfo_string_optional(&name, "alias", info.alias.as_deref()), - file = &build_modinfo_string_only_builtin(&name, "file", &file), - params_modinfo = params_modinfo, - generated_array_types = generated_array_types, - initcall_section = ".initcall6.init" - ).parse().expect("Error parsing formatted string into token stream.") -} - -/// Declares a kernel module that exposes a single misc device. -/// -/// The `type` argument should be a type which implements the [`FileOpener`] trait. Also accepts -/// various forms of kernel metadata. -/// -/// [`FileOpener`]: ../kernel/file_operations/trait.FileOpener.html -/// -/// # Examples -/// -/// ```rust,no_run -/// use kernel::prelude::*; -/// -/// module_misc_device! { -/// type: MyFile, -/// name: b"my_miscdev_kernel_module", -/// author: b"Rust for Linux Contributors", -/// description: b"My very own misc device kernel module!", -/// license: b"GPL v2", -/// } -/// -/// #[derive(Default)] -/// struct MyFile; -/// -/// impl kernel::file_operations::FileOperations for MyFile { -/// kernel::declare_file_operations!(); -/// } -/// ``` -#[proc_macro] -pub fn module_misc_device(ts: TokenStream) -> TokenStream { - let mut it = ts.into_iter(); - - let info = ModuleInfo::parse(&mut it); - - let module = format!("__internal_ModuleFor{}", info.type_); - - format!( - " - #[doc(hidden)] - struct {module} {{ - _dev: core::pin::Pin>, - }} - - impl kernel::KernelModule for {module} {{ - fn init() -> kernel::Result {{ - Ok(Self {{ - _dev: kernel::miscdev::Registration::new_pinned::<{type_}>( - kernel::c_str!(\"{name}\"), - None, - (), - )?, - }}) - }} - }} - - kernel::prelude::module! {{ - type: {module}, - name: b\"{name}\", - {author} - {description} - license: b\"{license}\", - {alias} - }} - ", - module = module, - type_ = info.type_, - name = info.name, - author = info - .author - .map(|v| format!("author: b\"{}\",", v)) - .unwrap_or_else(|| "".to_string()), - description = info - .description - .map(|v| format!("description: b\"{}\",", v)) - .unwrap_or_else(|| "".to_string()), - alias = info - .alias - .map(|v| format!("alias: b\"{}\",", v)) - .unwrap_or_else(|| "".to_string()), - license = info.license - ) - .parse() - .expect("Error parsing formatted string into token stream.") -} diff --git a/samples/rust/rust_chrdev.rs b/samples/rust/rust_chrdev.rs index 0e8151dd783940..f32fcf2c44b4bd 100644 --- a/samples/rust/rust_chrdev.rs +++ b/samples/rust/rust_chrdev.rs @@ -12,10 +12,10 @@ use kernel::{c_str, chrdev, file_operations::FileOperations}; module! { type: RustChrdev, - name: b"rust_chrdev", - author: b"Rust for Linux Contributors", - description: b"Rust character device sample", - license: b"GPL v2", + name: "rust_chrdev", + author: "Rust for Linux Contributors", + description: "Rust character device sample", + license: "GPL v2", } #[derive(Default)] diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 84a227021aafdd..539738c6414991 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -9,10 +9,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 v2", + name: "rust_minimal", + author: "Rust for Linux Contributors", + description: "Rust minimal sample", + license: "GPL v2", } struct RustMinimal { diff --git a/samples/rust/rust_miscdev.rs b/samples/rust/rust_miscdev.rs index f3293b4800904b..5b04cbab4809fb 100644 --- a/samples/rust/rust_miscdev.rs +++ b/samples/rust/rust_miscdev.rs @@ -20,10 +20,10 @@ use kernel::{ module! { type: RustMiscdev, - name: b"rust_miscdev", - author: b"Rust for Linux Contributors", - description: b"Rust miscellaneous device sample", - license: b"GPL v2", + name: "rust_miscdev", + author: "Rust for Linux Contributors", + description: "Rust miscellaneous device sample", + license: "GPL v2", } const MAX_TOKENS: usize = 3; diff --git a/samples/rust/rust_module_parameters.rs b/samples/rust/rust_module_parameters.rs index 57e59e80502759..db735497fd2561 100644 --- a/samples/rust/rust_module_parameters.rs +++ b/samples/rust/rust_module_parameters.rs @@ -9,35 +9,35 @@ use kernel::prelude::*; module! { type: RustModuleParameters, - name: b"rust_module_parameters", - author: b"Rust for Linux Contributors", - description: b"Rust module parameters sample", - license: b"GPL v2", + name: "rust_module_parameters", + author: "Rust for Linux Contributors", + description: "Rust module parameters sample", + license: "GPL v2", params: { my_bool: bool { default: true, permissions: 0, - description: b"Example of bool", + description: "Example of bool", }, my_i32: i32 { default: 42, permissions: 0o644, - description: b"Example of i32", + description: "Example of i32", }, my_str: str { - default: b"default str val", + default: "default str val", permissions: 0o644, - description: b"Example of a string param", + description: "Example of a string param", }, my_usize: usize { default: 42, permissions: 0o644, - description: b"Example of usize", + description: "Example of usize", }, my_array: ArrayParam { default: [0, 1], permissions: 0, - description: b"Example of array", + description: "Example of array", }, }, } diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs index a457e1e87fe50c..572fdd1e807e12 100644 --- a/samples/rust/rust_print.rs +++ b/samples/rust/rust_print.rs @@ -9,10 +9,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 v2", + name: "rust_print", + author: "Rust for Linux Contributors", + description: "Rust printing macros sample", + license: "GPL v2", } struct RustPrint; diff --git a/samples/rust/rust_random.rs b/samples/rust/rust_random.rs index 2b42956073c6d8..2e6aa3b2f3a47e 100644 --- a/samples/rust/rust_random.rs +++ b/samples/rust/rust_random.rs @@ -54,8 +54,8 @@ impl FileOperations for RandomFile { module_misc_device! { type: RandomFile, - name: b"rust_random", - author: b"Rust for Linux Contributors", - description: b"Just use /dev/urandom: Now with early-boot safety", - license: b"GPL v2", + name: "rust_random", + author: "Rust for Linux Contributors", + description: "Just use /dev/urandom: Now with early-boot safety", + license: "GPL v2", } diff --git a/samples/rust/rust_semaphore.rs b/samples/rust/rust_semaphore.rs index b3843381f7c968..0ca6b954461a54 100644 --- a/samples/rust/rust_semaphore.rs +++ b/samples/rust/rust_semaphore.rs @@ -36,10 +36,10 @@ use kernel::{ module! { type: RustSemaphore, - name: b"rust_semaphore", - author: b"Rust for Linux Contributors", - description: b"Rust semaphore sample", - license: b"GPL v2", + name: "rust_semaphore", + author: "Rust for Linux Contributors", + description: "Rust semaphore sample", + license: "GPL v2", } struct SemaphoreInner { diff --git a/samples/rust/rust_stack_probing.rs b/samples/rust/rust_stack_probing.rs index 56f96e1e05b17e..516bc39cd9cf2f 100644 --- a/samples/rust/rust_stack_probing.rs +++ b/samples/rust/rust_stack_probing.rs @@ -10,10 +10,10 @@ use kernel::prelude::*; module! { type: RustStackProbing, - name: b"rust_stack_probing", - author: b"Rust for Linux Contributors", - description: b"Rust stack probing sample", - license: b"GPL v2", + name: "rust_stack_probing", + author: "Rust for Linux Contributors", + description: "Rust stack probing sample", + license: "GPL v2", } struct RustStackProbing; diff --git a/samples/rust/rust_sync.rs b/samples/rust/rust_sync.rs index de11da04216ca0..d31beaadb1695c 100644 --- a/samples/rust/rust_sync.rs +++ b/samples/rust/rust_sync.rs @@ -15,10 +15,10 @@ use kernel::{ module! { type: RustSync, - name: b"rust_sync", - author: b"Rust for Linux Contributors", - description: b"Rust synchronisation primitives sample", - license: b"GPL v2", + name: "rust_sync", + author: "Rust for Linux Contributors", + description: "Rust synchronisation primitives sample", + license: "GPL v2", } struct RustSync; diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py index e88d4fc9eb3000..2a7b22be642bbe 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -63,13 +63,13 @@ def append_crate(display_name, root_module, is_workspace_member, deps, cfg): ) append_crate( - "module", - srctree / "rust" / "module.rs", + "macros", + srctree / "rust" / "macros" / "lib.rs", True, [], [], ) - crates[-1]["proc_macro_dylib_path"] = "rust/libmodule.so" + crates[-1]["proc_macro_dylib_path"] = "rust/libmacros.so" append_crate( "build_error", @@ -83,7 +83,7 @@ def append_crate(display_name, root_module, is_workspace_member, deps, cfg): "kernel", srctree / "rust" / "kernel" / "lib.rs", True, - ["core", "alloc", "module", "build_error"], + ["core", "alloc", "macros", "build_error"], cfg, ) crates[-1]["env"]["RUST_BINDINGS_FILE"] = str(bindings_file.resolve(True))