Skip to content

Commit 32fab5f

Browse files
Andreas Hindborgmcgrof
authored andcommitted
rust: add parameter support to the module! macro
This patch includes changes required for Rust kernel modules to utilize module parameters. This code implements read only support for integer types without `sysfs` support. Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org> Acked-by: Petr Pavlu <petr.pavlu@suse.com> # from modules perspective
1 parent 7c619f9 commit 32fab5f

File tree

6 files changed

+466
-18
lines changed

6 files changed

+466
-18
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub mod jump_label;
5858
pub mod kunit;
5959
pub mod list;
6060
pub mod miscdevice;
61+
pub mod module_param;
6162
#[cfg(CONFIG_NET)]
6263
pub mod net;
6364
pub mod of;

rust/kernel/module_param.rs

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Types for module parameters.
4+
//!
5+
//! C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
6+
7+
use crate::prelude::*;
8+
use crate::str::BStr;
9+
10+
/// Newtype to make `bindings::kernel_param` [`Sync`].
11+
#[repr(transparent)]
12+
#[doc(hidden)]
13+
pub struct RacyKernelParam(pub ::kernel::bindings::kernel_param);
14+
15+
// SAFETY: C kernel handles serializing access to this type. We never access
16+
// from Rust module.
17+
unsafe impl Sync for RacyKernelParam {}
18+
19+
/// Types that can be used for module parameters.
20+
///
21+
/// Note that displaying the type in `sysfs` will fail if
22+
/// [`Display`](core::fmt::Display) implementation would write more than
23+
/// [`PAGE_SIZE`] - 1 bytes.
24+
///
25+
/// [`PAGE_SIZE`]: `bindings::PAGE_SIZE`
26+
pub trait ModuleParam: Sized {
27+
/// The [`ModuleParam`] will be used by the kernel module through this type.
28+
///
29+
/// This may differ from `Self` if, for example, `Self` needs to track
30+
/// ownership without exposing it or allocate extra space for other possible
31+
/// parameter values.
32+
// This is required to support string parameters in the future.
33+
type Value: ?Sized;
34+
35+
/// Parse a parameter argument into the parameter value.
36+
///
37+
/// `Err(_)` should be returned when parsing of the argument fails.
38+
///
39+
/// Parameters passed at boot time will be set before [`kmalloc`] is
40+
/// available (even if the module is loaded at a later time). However, in
41+
/// this case, the argument buffer will be valid for the entire lifetime of
42+
/// the kernel. So implementations of this method which need to allocate
43+
/// should first check that the allocator is available (with
44+
/// [`crate::bindings::slab_is_available`]) and when it is not available
45+
/// provide an alternative implementation which doesn't allocate. In cases
46+
/// where the allocator is not available it is safe to save references to
47+
/// `arg` in `Self`, but in other cases a copy should be made.
48+
///
49+
/// [`kmalloc`]: srctree/include/linux/slab.h
50+
fn try_from_param_arg(arg: &'static [u8]) -> Result<Self>;
51+
}
52+
53+
/// Set the module parameter from a string.
54+
///
55+
/// Used to set the parameter value at kernel initialization, when loading
56+
/// the module or when set through `sysfs`.
57+
///
58+
/// `param.arg` is a pointer to `*mut T` as set up by the [`module!`]
59+
/// macro.
60+
///
61+
/// See `struct kernel_param_ops.set`.
62+
///
63+
/// # Safety
64+
///
65+
/// If `val` is non-null then it must point to a valid null-terminated
66+
/// string. The `arg` field of `param` must be an instance of `T`.
67+
///
68+
/// # Invariants
69+
///
70+
/// Currently, we only support read-only parameters that are not readable
71+
/// from `sysfs`. Thus, this function is only called at kernel
72+
/// initialization time, or at module load time, and we have exclusive
73+
/// access to the parameter for the duration of the function.
74+
///
75+
/// [`module!`]: macros::module
76+
unsafe extern "C" fn set_param<T>(
77+
val: *const kernel::ffi::c_char,
78+
param: *const crate::bindings::kernel_param,
79+
) -> core::ffi::c_int
80+
where
81+
T: ModuleParam,
82+
{
83+
// NOTE: If we start supporting arguments without values, val _is_ allowed
84+
// to be null here.
85+
if val.is_null() {
86+
// TODO: Use pr_warn_once available.
87+
crate::pr_warn!("Null pointer passed to `module_param::set_param`");
88+
return crate::error::code::EINVAL.to_errno();
89+
}
90+
91+
// SAFETY: By function safety requirement, val is non-null and
92+
// null-terminated. By C API contract, `val` is live and valid for reads
93+
// for the duration of this function.
94+
let arg = unsafe { CStr::from_char_ptr(val).as_bytes() };
95+
96+
crate::error::from_result(|| {
97+
let new_value = T::try_from_param_arg(arg)?;
98+
99+
// SAFETY: `param` is guaranteed to be valid by C API contract
100+
// and `arg` is guaranteed to point to an instance of `T`.
101+
let old_value = unsafe { (*param).__bindgen_anon_1.arg as *mut T };
102+
103+
// SAFETY: `old_value` is valid for writes, as we have exclusive
104+
// access. `old_value` is pointing to an initialized static, and
105+
// so it is properly initialized.
106+
unsafe { core::ptr::replace(old_value, new_value) };
107+
Ok(0)
108+
})
109+
}
110+
111+
/// Drop the parameter.
112+
///
113+
/// Called when unloading a module.
114+
///
115+
/// # Safety
116+
///
117+
/// The `arg` field of `param` must be an initialized instance of `T`.
118+
unsafe extern "C" fn free<T>(arg: *mut core::ffi::c_void)
119+
where
120+
T: ModuleParam,
121+
{
122+
// SAFETY: By function safety requirement, `arg` is an initialized
123+
// instance of `T`. By C API contract, `arg` will not be used after
124+
// this function returns.
125+
unsafe { core::ptr::drop_in_place(arg as *mut T) };
126+
}
127+
128+
macro_rules! impl_int_module_param {
129+
($ty:ident) => {
130+
impl ModuleParam for $ty {
131+
type Value = $ty;
132+
133+
fn try_from_param_arg(arg: &'static [u8]) -> Result<Self> {
134+
let bstr = BStr::from_bytes(arg);
135+
<$ty as crate::str::parse_int::ParseInt>::from_str(bstr)
136+
}
137+
}
138+
};
139+
}
140+
141+
impl_int_module_param!(i8);
142+
impl_int_module_param!(u8);
143+
impl_int_module_param!(i16);
144+
impl_int_module_param!(u16);
145+
impl_int_module_param!(i32);
146+
impl_int_module_param!(u32);
147+
impl_int_module_param!(i64);
148+
impl_int_module_param!(u64);
149+
impl_int_module_param!(isize);
150+
impl_int_module_param!(usize);
151+
152+
/// A wrapper for kernel parameters.
153+
///
154+
/// This type is instantiated by the [`module!`] macro when module parameters are
155+
/// defined. You should never need to instantiate this type directly.
156+
#[repr(transparent)]
157+
pub struct ModuleParamAccess<T> {
158+
data: core::cell::UnsafeCell<T>,
159+
}
160+
161+
// SAFETY: We only create shared references to the contents of this container,
162+
// so if `T` is `Sync`, so is `ModuleParamAccess`.
163+
unsafe impl<T: Sync> Sync for ModuleParamAccess<T> {}
164+
165+
impl<T> ModuleParamAccess<T> {
166+
#[doc(hidden)]
167+
pub const fn new(value: T) -> Self {
168+
Self {
169+
data: core::cell::UnsafeCell::new(value),
170+
}
171+
}
172+
173+
/// Get a shared reference to the parameter value.
174+
// Note: When sysfs access to parameters are enabled, we have to pass in a
175+
// held lock guard here.
176+
pub fn get(&self) -> &T {
177+
// SAFETY: As we only support read only parameters with no sysfs
178+
// exposure, the kernel will not touch the parameter data after module
179+
// initialization.
180+
unsafe { &*self.data.get() }
181+
}
182+
183+
/// Get a mutable pointer to the parameter value.
184+
pub const fn as_mut_ptr(&self) -> *mut T {
185+
self.data.get()
186+
}
187+
}
188+
189+
#[doc(hidden)]
190+
#[macro_export]
191+
/// Generate a static [`kernel_param_ops`](srctree/include/linux/moduleparam.h) struct.
192+
///
193+
/// # Examples
194+
///
195+
/// ```ignore
196+
/// make_param_ops!(
197+
/// /// Documentation for new param ops.
198+
/// PARAM_OPS_MYTYPE, // Name for the static.
199+
/// MyType // A type which implements [`ModuleParam`].
200+
/// );
201+
/// ```
202+
macro_rules! make_param_ops {
203+
($ops:ident, $ty:ty) => {
204+
///
205+
/// Static [`kernel_param_ops`](srctree/include/linux/moduleparam.h)
206+
/// struct generated by `make_param_ops`
207+
#[doc = concat!("for [`", stringify!($ty), "`].")]
208+
pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops {
209+
flags: 0,
210+
set: Some(set_param::<$ty>),
211+
get: None,
212+
free: Some(free::<$ty>),
213+
};
214+
};
215+
}
216+
217+
make_param_ops!(PARAM_OPS_I8, i8);
218+
make_param_ops!(PARAM_OPS_U8, u8);
219+
make_param_ops!(PARAM_OPS_I16, i16);
220+
make_param_ops!(PARAM_OPS_U16, u16);
221+
make_param_ops!(PARAM_OPS_I32, i32);
222+
make_param_ops!(PARAM_OPS_U32, u32);
223+
make_param_ops!(PARAM_OPS_I64, i64);
224+
make_param_ops!(PARAM_OPS_U64, u64);
225+
make_param_ops!(PARAM_OPS_ISIZE, isize);
226+
make_param_ops!(PARAM_OPS_USIZE, usize);

rust/macros/helpers.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
1010
}
1111
}
1212

13+
pub(crate) fn try_sign(it: &mut token_stream::IntoIter) -> Option<char> {
14+
let peek = it.clone().next();
15+
match peek {
16+
Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => {
17+
let _ = it.next();
18+
Some(punct.as_char())
19+
}
20+
_ => None,
21+
}
22+
}
23+
1324
pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
1425
if let Some(TokenTree::Literal(literal)) = it.next() {
1526
Some(literal.to_string())
@@ -107,6 +118,20 @@ pub(crate) struct Generics {
107118
pub(crate) ty_generics: Vec<TokenTree>,
108119
}
109120

121+
/// Parse a token stream of the form `expected_name: "value",` and return the
122+
/// string in the position of "value".
123+
///
124+
/// # Panics
125+
///
126+
/// - On parse error.
127+
pub(crate) fn expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
128+
assert_eq!(expect_ident(it), expected_name);
129+
assert_eq!(expect_punct(it), ':');
130+
let string = expect_string(it);
131+
assert_eq!(expect_punct(it), ',');
132+
string
133+
}
134+
110135
/// Parses the given `TokenStream` into `Generics` and the rest.
111136
///
112137
/// The generics are not present in the rest, but a where clause might remain.

rust/macros/lib.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ use proc_macro::TokenStream;
2424
/// The `type` argument should be a type which implements the [`Module`]
2525
/// trait. Also accepts various forms of kernel metadata.
2626
///
27+
/// The `params` field describe module parameters. Each entry has the form
28+
///
29+
/// ```ignore
30+
/// parameter_name: type {
31+
/// default: default_value,
32+
/// description: "Description",
33+
/// }
34+
/// ```
35+
///
36+
/// `type` may be one of
37+
///
38+
/// - [`i8`]
39+
/// - [`u8`]
40+
/// - [`i8`]
41+
/// - [`u8`]
42+
/// - [`i16`]
43+
/// - [`u16`]
44+
/// - [`i32`]
45+
/// - [`u32`]
46+
/// - [`i64`]
47+
/// - [`u64`]
48+
/// - [`isize`]
49+
/// - [`usize`]
50+
///
2751
/// C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
2852
///
2953
/// [`Module`]: ../kernel/trait.Module.html
@@ -40,6 +64,12 @@ use proc_macro::TokenStream;
4064
/// description: "My very own kernel module!",
4165
/// license: "GPL",
4266
/// alias: ["alternate_module_name"],
67+
/// params: {
68+
/// my_parameter: i64 {
69+
/// default: 1,
70+
/// description: "This parameter has a default of 1",
71+
/// },
72+
/// },
4373
/// }
4474
///
4575
/// struct MyModule(i32);
@@ -48,6 +78,7 @@ use proc_macro::TokenStream;
4878
/// fn init(_module: &'static ThisModule) -> Result<Self> {
4979
/// let foo: i32 = 42;
5080
/// pr_info!("I contain: {}\n", foo);
81+
/// pr_info!("i32 param is: {}\n", module_parameters::my_parameter.read());
5182
/// Ok(Self(foo))
5283
/// }
5384
/// }

0 commit comments

Comments
 (0)