| // 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(¶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), | |
| "bool" => parse_quote!(::kernel::module_param::PARAM_OPS_BOOL), | |
| 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(); | |
| /// 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; | |
| #[cfg(MODULE)] | |
| const THIS_MODULE: ::kernel::ThisModule = { | |
| extern "C" { | |
| static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>; | |
| } | |
| // SAFETY: `__this_module` is constructed by the kernel at load time | |
| // and lives until the module is unloaded. | |
| unsafe { ::kernel::ThisModule::from_ptr(__this_module.get()) } | |
| }; | |
| #[cfg(not(MODULE))] | |
| const THIS_MODULE: ::kernel::ThisModule = unsafe { | |
| ::kernel::ThisModule::from_ptr(::core::ptr::null_mut()) | |
| }; | |
| } | |
| // 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( | |
| ::kernel::module::this_module::<super::super::LocalModule>() | |
| ); | |
| // 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 { ::pin_init::raw_try_init(__MOD.as_mut_ptr(), initer) } { | |
| 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 | |
| } | |
| }) | |
| } |