--- /dev/null
+ // SPDX-License-Identifier: GPL-2.0
+
+ //! Support for module parameters.
+ //!
+ //! C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
+
+ use crate::prelude::*;
+ use crate::str::BStr;
+ use bindings;
+ use kernel::sync::SetOnce;
+
+ /// Newtype to make `bindings::kernel_param` [`Sync`].
+ #[repr(transparent)]
+ #[doc(hidden)]
+ pub struct KernelParam(bindings::kernel_param);
+
+ impl KernelParam {
+ #[doc(hidden)]
+ pub const fn new(val: bindings::kernel_param) -> Self {
+ Self(val)
+ }
+ }
+
+ // SAFETY: C kernel handles serializing access to this type. We never access it
+ // from Rust module.
+ unsafe impl Sync for KernelParam {}
+
+ /// Types that can be used for module parameters.
+ // NOTE: This trait is `Copy` because drop could produce unsoundness during teardown.
+ pub trait ModuleParam: Sized + Copy {
+ /// Parse a parameter argument into the parameter value.
+ fn try_from_param_arg(arg: &BStr) -> Result<Self>;
+ }
+
+ /// Set the module parameter from a string.
+ ///
+ /// Used to set the parameter value at kernel initialization, when loading
+ /// the module or when set through `sysfs`.
+ ///
+ /// See `struct kernel_param_ops.set`.
+ ///
+ /// # Safety
+ ///
+ /// - If `val` is non-null then it must point to a valid null-terminated string that must be valid
+ /// for reads for the duration of the call.
+ /// - `param` must be a pointer to a `bindings::kernel_param` initialized by the rust module macro.
+ /// The pointee must be valid for reads for the duration of the call.
+ ///
+ /// # Note
+ ///
+ /// - The safety requirements are satisfied by C API contract when this function is invoked by the
+ /// module subsystem C code.
+ /// - Currently, we only support read-only parameters that are not readable from `sysfs`. Thus, this
+ /// function is only called at kernel initialization time, or at module load time, and we have
+ /// exclusive access to the parameter for the duration of the function.
+ ///
+ /// [`module!`]: macros::module
+ unsafe extern "C" fn set_param<T>(val: *const c_char, param: *const bindings::kernel_param) -> c_int
+ where
+ T: ModuleParam,
+ {
+ // NOTE: If we start supporting arguments without values, val _is_ allowed
+ // to be null here.
+ if val.is_null() {
+ // TODO: Use pr_warn_once available.
+ crate::pr_warn!("Null pointer passed to `module_param::set_param`");
+ return EINVAL.to_errno();
+ }
+
+ // SAFETY: By function safety requirement, val is non-null, null-terminated
+ // and valid for reads for the duration of this function.
+ let arg = unsafe { CStr::from_char_ptr(val) };
++ let arg: &BStr = arg.as_ref();
+
+ crate::error::from_result(|| {
+ let new_value = T::try_from_param_arg(arg)?;
+
+ // SAFETY: By function safety requirements, this access is safe.
+ let container = unsafe { &*((*param).__bindgen_anon_1.arg.cast::<SetOnce<T>>()) };
+
+ container
+ .populate(new_value)
+ .then_some(0)
+ .ok_or(kernel::error::code::EEXIST)
+ })
+ }
+
+ macro_rules! impl_int_module_param {
+ ($ty:ident) => {
+ impl ModuleParam for $ty {
+ fn try_from_param_arg(arg: &BStr) -> Result<Self> {
+ <$ty as crate::str::parse_int::ParseInt>::from_str(arg)
+ }
+ }
+ };
+ }
+
+ impl_int_module_param!(i8);
+ impl_int_module_param!(u8);
+ impl_int_module_param!(i16);
+ impl_int_module_param!(u16);
+ impl_int_module_param!(i32);
+ impl_int_module_param!(u32);
+ impl_int_module_param!(i64);
+ impl_int_module_param!(u64);
+ impl_int_module_param!(isize);
+ impl_int_module_param!(usize);
+
+ /// A wrapper for kernel parameters.
+ ///
+ /// This type is instantiated by the [`module!`] macro when module parameters are
+ /// defined. You should never need to instantiate this type directly.
+ ///
+ /// Note: This type is `pub` because it is used by module crates to access
+ /// parameter values.
+ pub struct ModuleParamAccess<T> {
+ value: SetOnce<T>,
+ default: T,
+ }
+
+ // SAFETY: We only create shared references to the contents of this container,
+ // so if `T` is `Sync`, so is `ModuleParamAccess`.
+ unsafe impl<T: Sync> Sync for ModuleParamAccess<T> {}
+
+ impl<T> ModuleParamAccess<T> {
+ #[doc(hidden)]
+ pub const fn new(default: T) -> Self {
+ Self {
+ value: SetOnce::new(),
+ default,
+ }
+ }
+
+ /// Get a shared reference to the parameter value.
+ // Note: When sysfs access to parameters are enabled, we have to pass in a
+ // held lock guard here.
+ pub fn value(&self) -> &T {
+ self.value.as_ref().unwrap_or(&self.default)
+ }
+
+ /// Get a mutable pointer to `self`.
+ ///
+ /// NOTE: In most cases it is not safe deref the returned pointer.
+ pub const fn as_void_ptr(&self) -> *mut c_void {
+ core::ptr::from_ref(self).cast_mut().cast()
+ }
+ }
+
+ #[doc(hidden)]
+ /// Generate a static [`kernel_param_ops`](srctree/include/linux/moduleparam.h) struct.
+ ///
+ /// # Examples
+ ///
+ /// ```ignore
+ /// make_param_ops!(
+ /// /// Documentation for new param ops.
+ /// PARAM_OPS_MYTYPE, // Name for the static.
+ /// MyType // A type which implements [`ModuleParam`].
+ /// );
+ /// ```
+ macro_rules! make_param_ops {
+ ($ops:ident, $ty:ty) => {
+ #[doc(hidden)]
+ pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops {
+ flags: 0,
+ set: Some(set_param::<$ty>),
+ get: None,
+ free: None,
+ };
+ };
+ }
+
+ make_param_ops!(PARAM_OPS_I8, i8);
+ make_param_ops!(PARAM_OPS_U8, u8);
+ make_param_ops!(PARAM_OPS_I16, i16);
+ make_param_ops!(PARAM_OPS_U16, u16);
+ make_param_ops!(PARAM_OPS_I32, i32);
+ make_param_ops!(PARAM_OPS_U32, u32);
+ make_param_ops!(PARAM_OPS_I64, i64);
+ make_param_ops!(PARAM_OPS_U64, u64);
+ make_param_ops!(PARAM_OPS_ISIZE, isize);
+ make_param_ops!(PARAM_OPS_USIZE, usize);
}
fn emit(&mut self, field: &str, content: &str) {
- self.emit_only_builtin(field, content);
- self.emit_only_loadable(field, content);
+ 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 ops = param_ops_path(¶m.ptype);
+
+ // Note: The spelling of these fields is dictated by the user space
+ // tool `modinfo`.
+ self.emit_param("parmtype", ¶m.name, ¶m.ptype);
+ self.emit_param("parm", ¶m.name, ¶m.description);
+
+ write!(
+ self.param_buffer,
+ "
+ pub(crate) static {param_name}:
+ ::kernel::module_param::ModuleParamAccess<{param_type}> =
+ ::kernel::module_param::ModuleParamAccess::new({param_default});
+
+ const _: () = {{
+ #[link_section = \"__param\"]
+ #[used]
+ static __{module_name}_{param_name}_struct:
+ ::kernel::module_param::KernelParam =
+ ::kernel::module_param::KernelParam::new(
+ ::kernel::bindings::kernel_param {{
+ name: if ::core::cfg!(MODULE) {{
- ::kernel::c_str!(\"{param_name}\").as_bytes_with_nul()
++ ::kernel::c_str!(\"{param_name}\").to_bytes_with_nul()
+ }} else {{
+ ::kernel::c_str!(\"{module_name}.{param_name}\")
- .as_bytes_with_nul()
++ .to_bytes_with_nul()
+ }}.as_ptr(),
+ // 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()
+ }},
+ }}
+ );
+ }};
+ ",
+ module_name = info.name,
+ param_type = param.ptype,
+ param_default = param.default,
+ param_name = param.name,
+ ops = ops,
+ )
+ .unwrap();
+ }
+ }
+ }
+
+ fn param_ops_path(param_type: &str) -> &'static str {
+ match param_type {
+ "i8" => "::kernel::module_param::PARAM_OPS_I8",
+ "u8" => "::kernel::module_param::PARAM_OPS_U8",
+ "i16" => "::kernel::module_param::PARAM_OPS_I16",
+ "u16" => "::kernel::module_param::PARAM_OPS_U16",
+ "i32" => "::kernel::module_param::PARAM_OPS_I32",
+ "u32" => "::kernel::module_param::PARAM_OPS_U32",
+ "i64" => "::kernel::module_param::PARAM_OPS_I64",
+ "u64" => "::kernel::module_param::PARAM_OPS_U64",
+ "isize" => "::kernel::module_param::PARAM_OPS_ISIZE",
+ "usize" => "::kernel::module_param::PARAM_OPS_USIZE",
+ t => panic!("Unsupported parameter type {}", t),
}
}
description: Option<String>,
alias: Option<Vec<String>>,
firmware: Option<Vec<String>>,
+ imports_ns: Option<Vec<String>>,
+ params: Option<Vec<Parameter>>,
+ }
+
+ #[derive(Debug)]
+ struct Parameter {
+ name: String,
+ ptype: String,
+ default: String,
+ description: String,
+ }
+
+ fn expect_params(it: &mut token_stream::IntoIter) -> Vec<Parameter> {
+ let params = expect_group(it);
+ assert_eq!(params.delimiter(), Delimiter::Brace);
+ let mut it = params.stream().into_iter();
+ let mut parsed = Vec::new();
+
+ loop {
+ let param_name = match it.next() {
+ Some(TokenTree::Ident(ident)) => ident.to_string(),
+ Some(_) => panic!("Expected Ident or end"),
+ None => break,
+ };
+
+ assert_eq!(expect_punct(&mut it), ':');
+ let param_type = expect_ident(&mut it);
+ let group = expect_group(&mut it);
+ assert_eq!(group.delimiter(), Delimiter::Brace);
+ assert_eq!(expect_punct(&mut it), ',');
+
+ let mut param_it = group.stream().into_iter();
+ let param_default = expect_param_default(&mut param_it);
+ let param_description = expect_string_field(&mut param_it, "description");
+ expect_end(&mut param_it);
+
+ parsed.push(Parameter {
+ name: param_name,
+ ptype: param_type,
+ default: param_default,
+ description: param_description,
+ })
+ }
+
+ parsed
}
impl ModuleInfo {
"license",
"alias",
"firmware",
+ "imports_ns",
+ "params",
];
const REQUIRED_KEYS: &[&str] = &["type", "name", "license"];
let mut seen_keys = Vec::new();
"license" => info.license = expect_string_ascii(it),
"alias" => info.alias = Some(expect_string_array(it)),
"firmware" => info.firmware = Some(expect_string_array(it)),
+ "imports_ns" => info.imports_ns = Some(expect_string_array(it)),
+ "params" => info.params = Some(expect_params(it)),
_ => panic!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
}
// Rust does not allow hyphens in identifiers, use underscore instead.
let ident = info.name.replace('-', "_");
let mut modinfo = ModInfoBuilder::new(ident.as_ref());
- if let Some(authors) = info.authors {
+ if let Some(authors) = &info.authors {
for author in authors {
- modinfo.emit("author", &author);
+ modinfo.emit("author", author);
}
}
- if let Some(description) = info.description {
- modinfo.emit("description", &description);
+ if let Some(description) = &info.description {
+ modinfo.emit("description", description);
}
modinfo.emit("license", &info.license);
- if let Some(aliases) = info.alias {
+ if let Some(aliases) = &info.alias {
for alias in aliases {
- modinfo.emit("alias", &alias);
+ modinfo.emit("alias", alias);
}
}
- if let Some(firmware) = info.firmware {
+ if let Some(firmware) = &info.firmware {
for fw in firmware {
- modinfo.emit("firmware", &fw);
+ modinfo.emit("firmware", fw);
}
}
- if let Some(imports) = info.imports_ns {
++ if let Some(imports) = &info.imports_ns {
+ for ns in imports {
- modinfo.emit("import_ns", &ns);
++ modinfo.emit("import_ns", ns);
+ }
+ }
// Built-in modules also export the `file` modinfo string.
let file =