mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	rust: add macros crate
				
					
				
			This crate contains all the procedural macros ("proc macros")
shared by all the kernel.
Procedural macros allow to create syntax extensions. They run at
compile-time and can consume as well as produce Rust syntax.
For instance, the `module!` macro that is used by Rust modules
is implemented here. It allows to easily declare the equivalent
information to the `MODULE_*` macros in C modules, e.g.:
    module! {
        type: RustMinimal,
        name: b"rust_minimal",
        author: b"Rust for Linux Contributors",
        description: b"Rust minimal sample",
        license: b"GPL",
    }
Co-developed-by: Alex Gaynor <alex.gaynor@gmail.com>
Signed-off-by: Alex Gaynor <alex.gaynor@gmail.com>
Co-developed-by: Finn Behrens <me@kloenk.de>
Signed-off-by: Finn Behrens <me@kloenk.de>
Co-developed-by: Adam Bratschi-Kaye <ark.email@gmail.com>
Signed-off-by: Adam Bratschi-Kaye <ark.email@gmail.com>
Co-developed-by: Wedson Almeida Filho <wedsonaf@google.com>
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
Co-developed-by: Sumera Priyadarsini <sylphrenadin@gmail.com>
Signed-off-by: Sumera Priyadarsini <sylphrenadin@gmail.com>
Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
Co-developed-by: Matthew Bakhtiari <dev@mtbk.me>
Signed-off-by: Matthew Bakhtiari <dev@mtbk.me>
Co-developed-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Signed-off-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									db958dcf88
								
							
						
					
					
						commit
						1fbde52bde
					
				
					 3 changed files with 405 additions and 0 deletions
				
			
		
							
								
								
									
										51
									
								
								rust/macros/helpers.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								rust/macros/helpers.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| 
 | ||||
| use proc_macro::{token_stream, TokenTree}; | ||||
| 
 | ||||
| pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> { | ||||
|     if let Some(TokenTree::Ident(ident)) = it.next() { | ||||
|         Some(ident.to_string()) | ||||
|     } else { | ||||
|         None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> { | ||||
|     if let Some(TokenTree::Literal(literal)) = it.next() { | ||||
|         Some(literal.to_string()) | ||||
|     } else { | ||||
|         None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn try_byte_string(it: &mut token_stream::IntoIter) -> Option<String> { | ||||
|     try_literal(it).and_then(|byte_string| { | ||||
|         if byte_string.starts_with("b\"") && byte_string.ends_with('\"') { | ||||
|             Some(byte_string[2..byte_string.len() - 1].to_string()) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn expect_ident(it: &mut token_stream::IntoIter) -> String { | ||||
|     try_ident(it).expect("Expected Ident") | ||||
| } | ||||
| 
 | ||||
| pub(crate) 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"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { | ||||
|     try_byte_string(it).expect("Expected byte string") | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn expect_end(it: &mut token_stream::IntoIter) { | ||||
|     if it.next().is_some() { | ||||
|         panic!("Expected end"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										72
									
								
								rust/macros/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								rust/macros/lib.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| 
 | ||||
| //! Crate for all kernel procedural macros.
 | ||||
| 
 | ||||
| mod helpers; | ||||
| mod module; | ||||
| 
 | ||||
| use proc_macro::TokenStream; | ||||
| 
 | ||||
| /// Declares a kernel module.
 | ||||
| ///
 | ||||
| /// The `type` argument should be a type which implements the [`Module`]
 | ||||
| /// trait. Also accepts various forms of kernel metadata.
 | ||||
| ///
 | ||||
| /// C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
 | ||||
| ///
 | ||||
| /// [`Module`]: ../kernel/trait.Module.html
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```ignore
 | ||||
| /// use kernel::prelude::*;
 | ||||
| ///
 | ||||
| /// module!{
 | ||||
| ///     type: MyModule,
 | ||||
| ///     name: b"my_kernel_module",
 | ||||
| ///     author: b"Rust for Linux Contributors",
 | ||||
| ///     description: b"My very own kernel module!",
 | ||||
| ///     license: b"GPL",
 | ||||
| ///     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 MyModule;
 | ||||
| ///
 | ||||
| /// 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());
 | ||||
| ///         Ok(Self)
 | ||||
| ///     }
 | ||||
| /// }
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// # 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.
 | ||||
| #[proc_macro] | ||||
| pub fn module(ts: TokenStream) -> TokenStream { | ||||
|     module::module(ts) | ||||
| } | ||||
							
								
								
									
										282
									
								
								rust/macros/module.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								rust/macros/module.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,282 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| 
 | ||||
| use crate::helpers::*; | ||||
| use proc_macro::{token_stream, Literal, TokenStream, TokenTree}; | ||||
| use std::fmt::Write; | ||||
| 
 | ||||
| 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) { | ||||
|         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} | ||||
|                 #[doc(hidden)] | ||||
|                 #[link_section = \".modinfo\"]
 | ||||
|                 #[used] | ||||
|                 pub static __{module}_{counter}: [u8; {length}] = *{string}; | ||||
|             ",
 | ||||
|             cfg = if builtin { | ||||
|                 "#[cfg(not(MODULE))]" | ||||
|             } else { | ||||
|                 "#[cfg(MODULE)]" | ||||
|             }, | ||||
|             module = self.module.to_uppercase(), | ||||
|             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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Default)] | ||||
| struct ModuleInfo { | ||||
|     type_: String, | ||||
|     license: String, | ||||
|     name: String, | ||||
|     author: Option<String>, | ||||
|     description: Option<String>, | ||||
|     alias: Option<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 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)), | ||||
|                 _ => 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 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn module(ts: TokenStream) -> TokenStream { | ||||
|     let mut it = ts.into_iter(); | ||||
| 
 | ||||
|     let info = ModuleInfo::parse(&mut it); | ||||
| 
 | ||||
|     let mut modinfo = ModInfoBuilder::new(info.name.as_ref()); | ||||
|     if let Some(author) = info.author { | ||||
|         modinfo.emit("author", &author); | ||||
|     } | ||||
|     if let Some(description) = info.description { | ||||
|         modinfo.emit("description", &description); | ||||
|     } | ||||
|     modinfo.emit("license", &info.license); | ||||
|     if let Some(alias) = info.alias { | ||||
|         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); | ||||
| 
 | ||||
|     format!( | ||||
|         " | ||||
|             /// The module name.
 | ||||
|             ///
 | ||||
|             /// Used by the printing macros, e.g. [`info!`].
 | ||||
|             const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
 | ||||
| 
 | ||||
|             /// The \"Rust loadable module\" mark, for `scripts/is_rust_module.sh`.
 | ||||
|             //
 | ||||
|             // 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 _) | ||||
|             }}; | ||||
|             #[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)] | ||||
|             #[doc(hidden)] | ||||
|             #[no_mangle] | ||||
|             pub extern \"C\" fn init_module() -> core::ffi::c_int {{
 | ||||
|                 __init() | ||||
|             }} | ||||
| 
 | ||||
|             #[cfg(MODULE)] | ||||
|             #[doc(hidden)] | ||||
|             #[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))] | ||||
|             #[doc(hidden)] | ||||
|             #[link_section = \"{initcall_section}\"]
 | ||||
|             #[used] | ||||
|             pub static __{name}_initcall: extern \"C\" fn() -> core::ffi::c_int = __{name}_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(not(MODULE))] | ||||
|             #[doc(hidden)] | ||||
|             #[no_mangle] | ||||
|             pub extern \"C\" fn __{name}_init() -> core::ffi::c_int {{
 | ||||
|                 __init() | ||||
|             }} | ||||
| 
 | ||||
|             #[cfg(not(MODULE))] | ||||
|             #[doc(hidden)] | ||||
|             #[no_mangle] | ||||
|             pub extern \"C\" fn __{name}_exit() {{
 | ||||
|                 __exit() | ||||
|             }} | ||||
| 
 | ||||
|             fn __init() -> core::ffi::c_int {{ | ||||
|                 match <{type_} as kernel::Module>::init(&THIS_MODULE) {{ | ||||
|                     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} | ||||
|         ",
 | ||||
|         type_ = info.type_, | ||||
|         name = info.name, | ||||
|         modinfo = modinfo.buffer, | ||||
|         initcall_section = ".initcall6.init" | ||||
|     ) | ||||
|     .parse() | ||||
|     .expect("Error parsing formatted string into token stream.") | ||||
| } | ||||
		Loading…
	
		Reference in a new issue
	
	 Miguel Ojeda
						Miguel Ojeda