653 lines · 22.1 KB · Rust
RawDownload
// 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

Lines 1–500 of 653.