| // 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, |
| field = field, |
| content = content |
| ) |
| } 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}", param = param, content = 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(¶m_type_str); |
| |
| // Note: The spelling of these fields is dictated by the user space |
| // tool `modinfo`. |
| self.emit_param("parmtype", ¶m_name_str, ¶m_type_str); |
| self.emit_param("parm", ¶m_name_str, ¶m.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 = ¶m.name; |
| let param_type = ¶m.ptype; |
| let param_default = ¶m.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 |
| } |
| }) |
| } |