// SPDX-License-Identifier: GPL-2.0 //! Types for module parameters. //! //! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) use crate::error::{code::*, from_kernel_result}; use crate::str::{CStr, Formatter}; use core::fmt::Write; /// Types that can be used for module parameters. /// /// Note that displaying the type in `sysfs` will fail if /// [`alloc::string::ToString::to_string`] (as implemented through the /// [`core::fmt::Display`] trait) writes more than [`PAGE_SIZE`] /// bytes (including an additional null terminator). /// /// [`PAGE_SIZE`]: `crate::PAGE_SIZE` pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// The `ModuleParam` will be used by the kernel module through this type. /// /// This may differ from `Self` if, for example, `Self` needs to track /// ownership without exposing it or allocate extra space for other possible /// parameter values. See [`StringParam`] or [`ArrayParam`] for examples. type Value: ?Sized; /// Whether the parameter is allowed to be set without an argument. /// /// Setting this to `true` allows the parameter to be passed without an /// argument (e.g. just `module.param` instead of `module.param=foo`). const NOARG_ALLOWED: bool; /// Convert a parameter argument into the parameter value. /// /// `None` should be returned when parsing of the argument fails. /// `arg == None` indicates that the parameter was passed without an /// argument. If `NOARG_ALLOWED` is set to `false` then `arg` is guaranteed /// to always be `Some(_)`. /// /// Parameters passed at boot time will be set before [`kmalloc`] is /// available (even if the module is loaded at a later time). However, in /// this case, the argument buffer will be valid for the entire lifetime of /// the kernel. So implementations of this method which need to allocate /// should first check that the allocator is available (with /// [`crate::bindings::slab_is_available`]) and when it is not available /// provide an alternative implementation which doesn't allocate. In cases /// where the allocator is not available it is safe to save references to /// `arg` in `Self`, but in other cases a copy should be made. /// /// [`kmalloc`]: ../../../include/linux/slab.h fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option; /// Get the current value of the parameter for use in the kernel module. /// /// This function should not be used directly. Instead use the wrapper /// `read` which will be generated by [`macros::module`]. fn value(&self) -> &Self::Value; /// Set the module parameter from a string. /// /// Used to set the parameter value when loading the module or when set /// through `sysfs`. /// /// # Safety /// /// If `val` is non-null then it must point to a valid null-terminated /// string. The `arg` field of `param` must be an instance of `Self`. unsafe extern "C" fn set_param( val: *const crate::c_types::c_char, param: *const crate::bindings::kernel_param, ) -> crate::c_types::c_int { let arg = if val.is_null() { None } else { Some(unsafe { CStr::from_char_ptr(val).as_bytes() }) }; match Self::try_from_param_arg(arg) { Some(new_value) => { let old_value = unsafe { (*param).__bindgen_anon_1.arg as *mut Self }; let _ = unsafe { core::ptr::replace(old_value, new_value) }; 0 } None => EINVAL.to_kernel_errno(), } } /// Write a string representation of the current parameter value to `buf`. /// /// Used for displaying the current parameter value in `sysfs`. /// /// # Safety /// /// `buf` must be a buffer of length at least `kernel::PAGE_SIZE` that is /// writeable. The `arg` field of `param` must be an instance of `Self`. unsafe extern "C" fn get_param( buf: *mut crate::c_types::c_char, param: *const crate::bindings::kernel_param, ) -> crate::c_types::c_int { from_kernel_result! { // SAFETY: The C contracts guarantees that the buffer is at least `PAGE_SIZE` bytes. let mut f = unsafe { Formatter::from_buffer(buf.cast(), crate::PAGE_SIZE) }; unsafe { write!(f, "{}\0", *((*param).__bindgen_anon_1.arg as *mut Self)) }?; Ok(f.bytes_written().try_into()?) } } /// Drop the parameter. /// /// Called when unloading a module. /// /// # Safety /// /// The `arg` field of `param` must be an instance of `Self`. unsafe extern "C" fn free(arg: *mut crate::c_types::c_void) { unsafe { core::ptr::drop_in_place(arg as *mut Self) }; } } /// Trait for parsing integers. /// /// Strings beginning with `0x`, `0o`, or `0b` are parsed as hex, octal, or /// binary respectively. Strings beginning with `0` otherwise are parsed as /// octal. Anything else is parsed as decimal. A leading `+` or `-` is also /// permitted. Any string parsed by [`kstrtol()`] or [`kstrtoul()`] will be /// successfully parsed. /// /// [`kstrtol()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtol /// [`kstrtoul()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtoul trait ParseInt: Sized { fn from_str_radix(src: &str, radix: u32) -> Result; fn checked_neg(self) -> Option; fn from_str_unsigned(src: &str) -> Result { let (radix, digits) = if let Some(n) = src.strip_prefix("0x") { (16, n) } else if let Some(n) = src.strip_prefix("0X") { (16, n) } else if let Some(n) = src.strip_prefix("0o") { (8, n) } else if let Some(n) = src.strip_prefix("0O") { (8, n) } else if let Some(n) = src.strip_prefix("0b") { (2, n) } else if let Some(n) = src.strip_prefix("0B") { (2, n) } else if src.starts_with('0') { (8, src) } else { (10, src) }; Self::from_str_radix(digits, radix) } fn from_str(src: &str) -> Option { match src.bytes().next() { None => None, Some(b'-') => Self::from_str_unsigned(&src[1..]).ok()?.checked_neg(), Some(b'+') => Some(Self::from_str_unsigned(&src[1..]).ok()?), Some(_) => Some(Self::from_str_unsigned(src).ok()?), } } } macro_rules! impl_parse_int { ($ty:ident) => { impl ParseInt for $ty { fn from_str_radix(src: &str, radix: u32) -> Result { $ty::from_str_radix(src, radix) } fn checked_neg(self) -> Option { self.checked_neg() } } }; } impl_parse_int!(i8); impl_parse_int!(u8); impl_parse_int!(i16); impl_parse_int!(u16); impl_parse_int!(i32); impl_parse_int!(u32); impl_parse_int!(i64); impl_parse_int!(u64); impl_parse_int!(isize); impl_parse_int!(usize); macro_rules! impl_module_param { ($ty:ident) => { impl ModuleParam for $ty { type Value = $ty; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { let bytes = arg?; let utf8 = core::str::from_utf8(bytes).ok()?; <$ty as crate::module_param::ParseInt>::from_str(utf8) } fn value(&self) -> &Self::Value { self } } }; } #[doc(hidden)] #[macro_export] /// Generate a static [`kernel_param_ops`](../../../include/linux/moduleparam.h) struct. /// /// # Example /// ```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) => { $crate::make_param_ops!( #[doc=""] $ops, $ty ); }; ($(#[$meta:meta])* $ops:ident, $ty:ty) => { $(#[$meta])* /// /// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// struct generated by [`make_param_ops`]. pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops { flags: if <$ty as $crate::module_param::ModuleParam>::NOARG_ALLOWED { $crate::bindings::KERNEL_PARAM_OPS_FL_NOARG } else { 0 }, set: Some(<$ty as $crate::module_param::ModuleParam>::set_param), get: Some(<$ty as $crate::module_param::ModuleParam>::get_param), free: Some(<$ty as $crate::module_param::ModuleParam>::free), }; }; } impl_module_param!(i8); impl_module_param!(u8); impl_module_param!(i16); impl_module_param!(u16); impl_module_param!(i32); impl_module_param!(u32); impl_module_param!(i64); impl_module_param!(u64); impl_module_param!(isize); impl_module_param!(usize); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i8`]. PARAM_OPS_I8, i8 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u8`]. PARAM_OPS_U8, u8 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i16`]. PARAM_OPS_I16, i16 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u16`]. PARAM_OPS_U16, u16 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i32`]. PARAM_OPS_I32, i32 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u32`]. PARAM_OPS_U32, u32 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i64`]. PARAM_OPS_I64, i64 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u64`]. PARAM_OPS_U64, u64 ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`isize`]. PARAM_OPS_ISIZE, isize ); make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`usize`]. PARAM_OPS_USIZE, usize ); impl ModuleParam for bool { type Value = bool; const NOARG_ALLOWED: bool = true; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { match arg { None => Some(true), Some(b"y") | Some(b"Y") | Some(b"1") | Some(b"true") => Some(true), Some(b"n") | Some(b"N") | Some(b"0") | Some(b"false") => Some(false), _ => None, } } fn value(&self) -> &Self::Value { self } } make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`bool`]. PARAM_OPS_BOOL, bool ); /// An array of at __most__ `N` values. /// /// # Invariant /// /// The first `self.used` elements of `self.values` are initialized. pub struct ArrayParam { values: [core::mem::MaybeUninit; N], used: usize, } impl ArrayParam { fn values(&self) -> &[T] { // SAFETY: The invariant maintained by `ArrayParam` allows us to cast // the first `self.used` elements to `T`. unsafe { &*(&self.values[0..self.used] as *const [core::mem::MaybeUninit] as *const [T]) } } } impl ArrayParam { const fn new() -> Self { // INVARIANT: The first `self.used` elements of `self.values` are // initialized. ArrayParam { values: [core::mem::MaybeUninit::uninit(); N], used: 0, } } const fn push(&mut self, val: T) { if self.used < N { // INVARIANT: The first `self.used` elements of `self.values` are // initialized. self.values[self.used] = core::mem::MaybeUninit::new(val); self.used += 1; } } /// Create an instance of `ArrayParam` initialized with `vals`. /// /// This function is only meant to be used in the [`module::module`] macro. pub const fn create(vals: &[T]) -> Self { let mut result = ArrayParam::new(); let mut i = 0; while i < vals.len() { result.push(vals[i]); i += 1; } result } } impl core::fmt::Display for ArrayParam { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { for val in self.values() { write!(f, "{},", val)?; } Ok(()) } } impl ModuleParam for ArrayParam { type Value = [T]; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { arg.and_then(|args| { let mut result = Self::new(); for arg in args.split(|b| *b == b',') { result.push(T::try_from_param_arg(Some(arg))?); } Some(result) }) } fn value(&self) -> &Self::Value { self.values() } } /// A C-style string parameter. /// /// The Rust version of the [`charp`] parameter. This type is meant to be /// used by the [`macros::module`] macro, not handled directly. Instead use the /// `read` method generated by that macro. /// /// [`charp`]: ../../../include/linux/moduleparam.h pub enum StringParam { /// A borrowed parameter value. /// /// Either the default value (which is static in the module) or borrowed /// from the original argument buffer used to set the value. Ref(&'static [u8]), /// A value that was allocated when the parameter was set. /// /// The value needs to be freed when the parameter is reset or the module is /// unloaded. Owned(alloc::vec::Vec), } impl StringParam { fn bytes(&self) -> &[u8] { match self { StringParam::Ref(bytes) => *bytes, StringParam::Owned(vec) => &vec[..], } } } impl core::fmt::Display for StringParam { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let bytes = self.bytes(); match core::str::from_utf8(bytes) { Ok(utf8) => write!(f, "{}", utf8), Err(_) => write!(f, "{:?}", bytes), } } } impl ModuleParam for StringParam { type Value = [u8]; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { // SAFETY: It is always safe to call [`slab_is_available`](../../../include/linux/slab.h). let slab_available = unsafe { crate::bindings::slab_is_available() }; arg.and_then(|arg| { if slab_available { let mut vec = alloc::vec::Vec::new(); vec.try_extend_from_slice(arg).ok()?; Some(StringParam::Owned(vec)) } else { Some(StringParam::Ref(arg)) } }) } fn value(&self) -> &Self::Value { self.bytes() } } make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`StringParam`]. PARAM_OPS_STR, StringParam );