summaryrefslogtreecommitdiffstats
path: root/rust/macros/module.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/macros/module.rs')
-rw-r--r--rust/macros/module.rs653
1 files changed, 653 insertions, 0 deletions
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
new file mode 100644
index 000000000..06c18e207
--- /dev/null
+++ b/rust/macros/module.rs
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use std::ffi::CString;
+
+use proc_macro2::{
+ Literal,
+ TokenStream, //
+};
+use quote::{
+ format_ident,
+ quote, //
+};
+use syn::{
+ braced,
+ bracketed,
+ ext::IdentExt,
+ parse::{
+ Parse,
+ ParseStream, //
+ },
+ parse_quote,
+ punctuated::Punctuated,
+ Error,
+ Expr,
+ Ident,
+ LitStr,
+ Path,
+ Result,
+ Token,
+ Type, //
+};
+
+use crate::helpers::*;
+
+struct ModInfoBuilder<'a> {
+ module: &'a str,
+ counter: usize,
+ ts: TokenStream,
+ param_ts: TokenStream,
+}
+
+impl<'a> ModInfoBuilder<'a> {
+ fn new(module: &'a str) -> Self {
+ ModInfoBuilder {
+ module,
+ counter: 0,
+ ts: TokenStream::new(),
+ param_ts: TokenStream::new(),
+ }
+ }
+
+ fn emit_base(&mut self, field: &str, content: &str, builtin: bool, param: bool) {
+ let string = if builtin {
+ // Built-in modules prefix their modinfo strings by `module.`.
+ format!("{module}.{field}={content}\0", module = self.module)
+ } else {
+ // Loadable modules' modinfo strings go as-is.
+ format!("{field}={content}\0")
+ };
+ let length = string.len();
+ let string = Literal::byte_string(string.as_bytes());
+ let cfg = if builtin {
+ quote!(#[cfg(not(MODULE))])
+ } else {
+ quote!(#[cfg(MODULE)])
+ };
+
+ let counter = format_ident!(
+ "__{module}_{counter}",
+ module = self.module.to_uppercase(),
+ counter = self.counter
+ );
+ let item = quote! {
+ #cfg
+ #[cfg_attr(not(target_os = "macos"), link_section = ".modinfo")]
+ #[used(compiler)]
+ pub static #counter: [u8; #length] = *#string;
+ };
+
+ if param {
+ self.param_ts.extend(item);
+ } else {
+ self.ts.extend(item);
+ }
+
+ self.counter += 1;
+ }
+
+ fn emit_only_builtin(&mut self, field: &str, content: &str, param: bool) {
+ self.emit_base(field, content, true, param)
+ }
+
+ fn emit_only_loadable(&mut self, field: &str, content: &str, param: bool) {
+ self.emit_base(field, content, false, param)
+ }
+
+ fn emit(&mut self, field: &str, content: &str) {
+ self.emit_internal(field, content, false);
+ }
+
+ fn emit_internal(&mut self, field: &str, content: &str, param: bool) {
+ self.emit_only_builtin(field, content, param);
+ self.emit_only_loadable(field, content, param);
+ }
+
+ fn emit_param(&mut self, field: &str, param: &str, content: &str) {
+ let content = format!("{param}:{content}");
+ self.emit_internal(field, &content, true);
+ }
+
+ fn emit_params(&mut self, info: &ModuleInfo) {
+ let Some(params) = &info.params else {
+ return;
+ };
+
+ for param in params {
+ let param_name_str = param.name.to_string();
+ let param_type_str = param.ptype.to_string();
+
+ let ops = param_ops_path(&param_type_str);
+
+ // Note: The spelling of these fields is dictated by the user space
+ // tool `modinfo`.
+ self.emit_param("parmtype", &param_name_str, &param_type_str);
+ self.emit_param("parm", &param_name_str, &param.description.value());
+
+ let static_name = format_ident!("__{}_{}_struct", self.module, param.name);
+ let param_name_cstr =
+ CString::new(param_name_str).expect("name contains NUL-terminator");
+ let param_name_cstr_with_module =
+ CString::new(format!("{}.{}", self.module, param.name))
+ .expect("name contains NUL-terminator");
+
+ let param_name = &param.name;
+ let param_type = &param.ptype;
+ let param_default = &param.default;
+
+ self.param_ts.extend(quote! {
+ #[allow(non_upper_case_globals)]
+ pub(crate) static #param_name:
+ ::kernel::module_param::ModuleParamAccess<#param_type> =
+ ::kernel::module_param::ModuleParamAccess::new(#param_default);
+
+ const _: () = {
+ #[allow(non_upper_case_globals)]
+ #[link_section = "__param"]
+ #[used(compiler)]
+ static #static_name:
+ ::kernel::module_param::KernelParam =
+ ::kernel::module_param::KernelParam::new(
+ ::kernel::bindings::kernel_param {
+ name: kernel::str::as_char_ptr_in_const_context(
+ if ::core::cfg!(MODULE) {
+ #param_name_cstr
+ } else {
+ #param_name_cstr_with_module
+ }
+ ),
+ // 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 {
+ core::ptr::from_ref(&::kernel::bindings::__this_module)
+ .cast_mut()
+ },
+ #[cfg(not(MODULE))]
+ mod_: ::core::ptr::null_mut(),
+ ops: core::ptr::from_ref(&#ops),
+ perm: 0, // Will not appear in sysfs
+ level: -1,
+ flags: 0,
+ __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {
+ arg: #param_name.as_void_ptr()
+ },
+ }
+ );
+ };
+ });
+ }
+ }
+}
+
+fn param_ops_path(param_type: &str) -> Path {
+ match param_type {
+ "i8" => parse_quote!(::kernel::module_param::PARAM_OPS_I8),
+ "u8" => parse_quote!(::kernel::module_param::PARAM_OPS_U8),
+ "i16" => parse_quote!(::kernel::module_param::PARAM_OPS_I16),
+ "u16" => parse_quote!(::kernel::module_param::PARAM_OPS_U16),
+ "i32" => parse_quote!(::kernel::module_param::PARAM_OPS_I32),
+ "u32" => parse_quote!(::kernel::module_param::PARAM_OPS_U32),
+ "i64" => parse_quote!(::kernel::module_param::PARAM_OPS_I64),
+ "u64" => parse_quote!(::kernel::module_param::PARAM_OPS_U64),
+ "isize" => parse_quote!(::kernel::module_param::PARAM_OPS_ISIZE),
+ "usize" => parse_quote!(::kernel::module_param::PARAM_OPS_USIZE),
+ t => panic!("Unsupported parameter type {}", t),
+ }
+}
+
+/// Parse fields that are required to use a specific order.
+///
+/// As fields must follow a specific order, we *could* just parse fields one by one by peeking.
+/// However the error message generated when implementing that way is not very friendly.
+///
+/// So instead we parse fields in an arbitrary order, but only enforce the ordering after parsing,
+/// and if the wrong order is used, the proper order is communicated to the user with error message.
+///
+/// Usage looks like this:
+/// ```ignore
+/// parse_ordered_fields! {
+/// from input;
+///
+/// // This will extract "foo: <field>" into a variable named "foo".
+/// // The variable will have type `Option<_>`.
+/// foo => <expression that parses the field>,
+///
+/// // If you need the variable name to be different than the key name.
+/// // This extracts "baz: <field>" into a variable named "bar".
+/// // You might want this if "baz" is a keyword.
+/// baz as bar => <expression that parse the field>,
+///
+/// // You can mark a key as required, and the variable will no longer be `Option`.
+/// // foobar will be of type `Expr` instead of `Option<Expr>`.
+/// foobar [required] => input.parse::<Expr>()?,
+/// }
+/// ```
+macro_rules! parse_ordered_fields {
+ (@gen
+ [$input:expr]
+ [$([$name:ident; $key:ident; $parser:expr])*]
+ [$([$req_name:ident; $req_key:ident])*]
+ ) => {
+ $(let mut $name = None;)*
+
+ const EXPECTED_KEYS: &[&str] = &[$(stringify!($key),)*];
+ const REQUIRED_KEYS: &[&str] = &[$(stringify!($req_key),)*];
+
+ let span = $input.span();
+ let mut seen_keys = Vec::new();
+
+ while !$input.is_empty() {
+ let key = $input.call(Ident::parse_any)?;
+
+ if seen_keys.contains(&key) {
+ Err(Error::new_spanned(
+ &key,
+ format!(r#"duplicated key "{key}". Keys can only be specified once."#),
+ ))?
+ }
+
+ $input.parse::<Token![:]>()?;
+
+ match &*key.to_string() {
+ $(
+ stringify!($key) => $name = Some($parser),
+ )*
+ _ => {
+ Err(Error::new_spanned(
+ &key,
+ format!(r#"unknown key "{key}". Valid keys are: {EXPECTED_KEYS:?}."#),
+ ))?
+ }
+ }
+
+ $input.parse::<Token![,]>()?;
+ seen_keys.push(key);
+ }
+
+ for key in REQUIRED_KEYS {
+ if !seen_keys.iter().any(|e| e == key) {
+ Err(Error::new(span, format!(r#"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 {
+ Err(Error::new(
+ span,
+ format!(r#"keys are not ordered as expected. Order them like: {ordered_keys:?}."#),
+ ))?
+ }
+
+ $(let $req_name = $req_name.expect("required field");)*
+ };
+
+ // Handle required fields.
+ (@gen
+ [$input:expr] [$($tok:tt)*] [$($req:tt)*]
+ $key:ident as $name:ident [required] => $parser:expr,
+ $($rest:tt)*
+ ) => {
+ parse_ordered_fields!(
+ @gen [$input] [$($tok)* [$name; $key; $parser]] [$($req)* [$name; $key]] $($rest)*
+ )
+ };
+ (@gen
+ [$input:expr] [$($tok:tt)*] [$($req:tt)*]
+ $name:ident [required] => $parser:expr,
+ $($rest:tt)*
+ ) => {
+ parse_ordered_fields!(
+ @gen [$input] [$($tok)* [$name; $name; $parser]] [$($req)* [$name; $name]] $($rest)*
+ )
+ };
+
+ // Handle optional fields.
+ (@gen
+ [$input:expr] [$($tok:tt)*] [$($req:tt)*]
+ $key:ident as $name:ident => $parser:expr,
+ $($rest:tt)*
+ ) => {
+ parse_ordered_fields!(
+ @gen [$input] [$($tok)* [$name; $key; $parser]] [$($req)*] $($rest)*
+ )
+ };
+ (@gen
+ [$input:expr] [$($tok:tt)*] [$($req:tt)*]
+ $name:ident => $parser:expr,
+ $($rest:tt)*
+ ) => {
+ parse_ordered_fields!(
+ @gen [$input] [$($tok)* [$name; $name; $parser]] [$($req)*] $($rest)*
+ )
+ };
+
+ (from $input:expr; $($tok:tt)*) => {
+ parse_ordered_fields!(@gen [$input] [] [] $($tok)*)
+ }
+}
+
+struct Parameter {
+ name: Ident,
+ ptype: Ident,
+ default: Expr,
+ description: LitStr,
+}
+
+impl Parse for Parameter {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ let name = input.parse()?;
+ input.parse::<Token![:]>()?;
+ let ptype = input.parse()?;
+
+ let fields;
+ braced!(fields in input);
+
+ parse_ordered_fields! {
+ from fields;
+ default [required] => fields.parse()?,
+ description [required] => fields.parse()?,
+ }
+
+ Ok(Self {
+ name,
+ ptype,
+ default,
+ description,
+ })
+ }
+}
+
+pub(crate) struct ModuleInfo {
+ type_: Type,
+ license: AsciiLitStr,
+ name: AsciiLitStr,
+ authors: Option<Punctuated<AsciiLitStr, Token![,]>>,
+ description: Option<LitStr>,
+ alias: Option<Punctuated<AsciiLitStr, Token![,]>>,
+ firmware: Option<Punctuated<AsciiLitStr, Token![,]>>,
+ imports_ns: Option<Punctuated<AsciiLitStr, Token![,]>>,
+ params: Option<Punctuated<Parameter, Token![,]>>,
+}
+
+impl Parse for ModuleInfo {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ parse_ordered_fields!(
+ from input;
+ type as type_ [required] => input.parse()?,
+ name [required] => input.parse()?,
+ authors => {
+ let list;
+ bracketed!(list in input);
+ Punctuated::parse_terminated(&list)?
+ },
+ description => input.parse()?,
+ license [required] => input.parse()?,
+ alias => {
+ let list;
+ bracketed!(list in input);
+ Punctuated::parse_terminated(&list)?
+ },
+ firmware => {
+ let list;
+ bracketed!(list in input);
+ Punctuated::parse_terminated(&list)?
+ },
+ imports_ns => {
+ let list;
+ bracketed!(list in input);
+ Punctuated::parse_terminated(&list)?
+ },
+ params => {
+ let list;
+ braced!(list in input);
+ Punctuated::parse_terminated(&list)?
+ },
+ );
+
+ Ok(ModuleInfo {
+ type_,
+ license,
+ name,
+ authors,
+ description,
+ alias,
+ firmware,
+ imports_ns,
+ params,
+ })
+ }
+}
+
+pub(crate) fn module(info: ModuleInfo) -> Result<TokenStream> {
+ let ModuleInfo {
+ type_,
+ license,
+ name,
+ authors,
+ description,
+ alias,
+ firmware,
+ imports_ns,
+ params: _,
+ } = &info;
+
+ // Rust does not allow hyphens in identifiers, use underscore instead.
+ let ident = name.value().replace('-', "_");
+ let mut modinfo = ModInfoBuilder::new(ident.as_ref());
+ if let Some(authors) = authors {
+ for author in authors {
+ modinfo.emit("author", &author.value());
+ }
+ }
+ if let Some(description) = description {
+ modinfo.emit("description", &description.value());
+ }
+ modinfo.emit("license", &license.value());
+ if let Some(aliases) = alias {
+ for alias in aliases {
+ modinfo.emit("alias", &alias.value());
+ }
+ }
+ if let Some(firmware) = firmware {
+ for fw in firmware {
+ modinfo.emit("firmware", &fw.value());
+ }
+ }
+ if let Some(imports) = imports_ns {
+ for ns in imports {
+ modinfo.emit("import_ns", &ns.value());
+ }
+ }
+
+ // 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, false);
+
+ modinfo.emit_params(&info);
+
+ let modinfo_ts = modinfo.ts;
+ let params_ts = modinfo.param_ts;
+
+ let ident_init = format_ident!("__{ident}_init");
+ let ident_exit = format_ident!("__{ident}_exit");
+ let ident_initcall = format_ident!("__{ident}_initcall");
+ let initcall_section = ".initcall6.init";
+
+ let global_asm = format!(
+ r#".section "{initcall_section}", "a"
+ __{ident}_initcall:
+ .long __{ident}_init - .
+ .previous
+ "#
+ );
+
+ let name_cstr = CString::new(name.value()).expect("name contains NUL-terminator");
+
+ Ok(quote! {
+ /// The module name.
+ ///
+ /// Used by the printing macros, e.g. [`info!`].
+ const __LOG_PREFIX: &[u8] = #name_cstr.to_bytes_with_nul();
+
+ // 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 {
+ extern "C" {
+ static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
+ };
+
+ ::kernel::ThisModule::from_ptr(__this_module.get())
+ };
+
+ #[cfg(not(MODULE))]
+ static THIS_MODULE: ::kernel::ThisModule = unsafe {
+ ::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
+ };
+
+ /// The `LocalModule` type is the type of the module created by `module!`,
+ /// `module_pci_driver!`, `module_platform_driver!`, etc.
+ type LocalModule = #type_;
+
+ impl ::kernel::ModuleMetadata for #type_ {
+ const NAME: &'static ::kernel::str::CStr = #name_cstr;
+ }
+
+ // Double nested modules, since then nobody can access the public items inside.
+ #[doc(hidden)]
+ mod __module_init {
+ mod __module_init {
+ use pin_init::PinInit;
+
+ /// The "Rust loadable module" mark.
+ //
+ // This may be best done another way later on, e.g. as a new modinfo
+ // key or a new section. For the moment, keep it simple.
+ #[cfg(MODULE)]
+ #[used(compiler)]
+ static __IS_RUST_MODULE: () = ();
+
+ static mut __MOD: ::core::mem::MaybeUninit<super::super::LocalModule> =
+ ::core::mem::MaybeUninit::uninit();
+
+ // Loadable modules need to export the `{init,cleanup}_module` identifiers.
+ /// # Safety
+ ///
+ /// This function must not be called after module initialization, because it may be
+ /// freed after that completes.
+ #[cfg(MODULE)]
+ #[no_mangle]
+ #[link_section = ".init.text"]
+ pub unsafe extern "C" fn init_module() -> ::kernel::ffi::c_int {
+ // SAFETY: This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // unique name.
+ unsafe { __init() }
+ }
+
+ #[cfg(MODULE)]
+ #[used(compiler)]
+ #[link_section = ".init.data"]
+ static __UNIQUE_ID___addressable_init_module: unsafe extern "C" fn() -> i32 =
+ init_module;
+
+ #[cfg(MODULE)]
+ #[no_mangle]
+ #[link_section = ".exit.text"]
+ pub extern "C" fn cleanup_module() {
+ // SAFETY:
+ // - This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // unique name,
+ // - furthermore it is only called after `init_module` has returned `0`
+ // (which delegates to `__init`).
+ unsafe { __exit() }
+ }
+
+ #[cfg(MODULE)]
+ #[used(compiler)]
+ #[link_section = ".exit.data"]
+ static __UNIQUE_ID___addressable_cleanup_module: extern "C" fn() = cleanup_module;
+
+ // Built-in modules are initialized through an initcall pointer
+ // and the identifiers need to be unique.
+ #[cfg(not(MODULE))]
+ #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
+ #[link_section = #initcall_section]
+ #[used(compiler)]
+ pub static #ident_initcall: extern "C" fn() ->
+ ::kernel::ffi::c_int = #ident_init;
+
+ #[cfg(not(MODULE))]
+ #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
+ ::core::arch::global_asm!(#global_asm);
+
+ #[cfg(not(MODULE))]
+ #[no_mangle]
+ pub extern "C" fn #ident_init() -> ::kernel::ffi::c_int {
+ // SAFETY: This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // placement above in the initcall section.
+ unsafe { __init() }
+ }
+
+ #[cfg(not(MODULE))]
+ #[no_mangle]
+ pub extern "C" fn #ident_exit() {
+ // SAFETY:
+ // - This function is inaccessible to the outside due to the double
+ // module wrapping it. It is called exactly once by the C side via its
+ // unique name,
+ // - furthermore it is only called after `#ident_init` has
+ // returned `0` (which delegates to `__init`).
+ unsafe { __exit() }
+ }
+
+ /// # Safety
+ ///
+ /// This function must only be called once.
+ unsafe fn __init() -> ::kernel::ffi::c_int {
+ let initer = <super::super::LocalModule as ::kernel::InPlaceModule>::init(
+ &super::super::THIS_MODULE
+ );
+ // SAFETY: No data race, since `__MOD` can only be accessed by this module
+ // and there only `__init` and `__exit` access it. These functions are only
+ // called once and `__exit` cannot be called before or during `__init`.
+ match unsafe { initer.__pinned_init(__MOD.as_mut_ptr()) } {
+ Ok(m) => 0,
+ Err(e) => e.to_errno(),
+ }
+ }
+
+ /// # Safety
+ ///
+ /// This function must
+ /// - only be called once,
+ /// - be called after `__init` has been called and returned `0`.
+ unsafe fn __exit() {
+ // SAFETY: No data race, since `__MOD` can only be accessed by this module
+ // and there only `__init` and `__exit` access it. These functions are only
+ // called once and `__init` was already called.
+ unsafe {
+ // Invokes `drop()` on `__MOD`, which should be used for cleanup.
+ __MOD.assume_init_drop();
+ }
+ }
+
+ #modinfo_ts
+ }
+ }
+
+ mod module_parameters {
+ #params_ts
+ }
+ })
+}