Skip to content

Commit db8a646

Browse files
committed
Implement array and string parameters
1 parent a116223 commit db8a646

File tree

4 files changed

+401
-96
lines changed

4 files changed

+401
-96
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//! do so first instead of bypassing this crate.
1313
1414
#![no_std]
15-
#![feature(allocator_api, alloc_error_handler)]
15+
#![feature(allocator_api, alloc_error_handler, const_fn, const_mut_refs)]
1616
#![deny(clippy::complexity)]
1717
#![deny(clippy::correctness)]
1818
#![deny(clippy::perf)]

rust/kernel/module_param.rs

Lines changed: 183 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ use core::fmt::Write;
1515
///
1616
/// [`PAGE_SIZE`]: `crate::PAGE_SIZE`
1717
pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
18+
/// The `ModuleParam` will be used by the kernel module through this type.
19+
///
20+
/// This may differ from `Self` if, for example, `Self` needs to track
21+
/// ownership without exposing it or allocate extra space for other possible
22+
/// parameter values. See [`StringParam`] or [`ArrayParam`] for examples.
23+
type Value: ?Sized;
24+
1825
/// Whether the parameter is allowed to be set without an argument.
1926
///
2027
/// Setting this to `true` allows the parameter to be passed without an
@@ -27,7 +34,13 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
2734
/// `arg == None` indicates that the parameter was passed without an
2835
/// argument. If `NOARG_ALLOWED` is set to `false` then `arg` is guaranteed
2936
/// to always be `Some(_)`.
30-
fn try_from_param_arg(arg: Option<&[u8]>) -> Option<Self>;
37+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self>;
38+
39+
/// Get the current value of the parameter for use in the kernel module.
40+
///
41+
/// This function should not be used directly. Instead use the wrapper
42+
/// `read` which will be generated by [`module::module`].
43+
fn value(&self) -> &Self::Value;
3144

3245
/// Set the module parameter from a string.
3346
///
@@ -161,13 +174,18 @@ impl_parse_int!(usize);
161174
macro_rules! impl_module_param {
162175
($ty:ident) => {
163176
impl ModuleParam for $ty {
177+
type Value = $ty;
164178
const NOARG_ALLOWED: bool = false;
165179

166-
fn try_from_param_arg(arg: Option<&[u8]>) -> Option<Self> {
180+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
167181
let bytes = arg?;
168182
let utf8 = core::str::from_utf8(bytes).ok()?;
169183
<$ty as crate::module_param::ParseInt>::from_str(utf8)
170184
}
185+
186+
fn value(&self) -> &Self::Value {
187+
self
188+
}
171189
}
172190
};
173191
}
@@ -184,27 +202,27 @@ macro_rules! impl_module_param {
184202
/// );
185203
/// ```
186204
macro_rules! make_param_ops {
187-
($ops:ident, $ty:ident) => {
205+
($ops:ident, $ty:ty) => {
188206
make_param_ops!(
189207
#[doc=""]
190208
$ops,
191209
$ty
192210
);
193211
};
194-
($(#[$meta:meta])* $ops:ident, $ty:ident) => {
212+
($(#[$meta:meta])* $ops:ident, $ty:ty) => {
195213
$(#[$meta])*
196214
///
197215
/// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h)
198216
/// struct generated by [`make_param_ops`].
199-
pub static $ops: crate::bindings::kernel_param_ops = crate::bindings::kernel_param_ops {
200-
flags: if <$ty as crate::module_param::ModuleParam>::NOARG_ALLOWED {
201-
crate::bindings::KERNEL_PARAM_OPS_FL_NOARG
217+
pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops {
218+
flags: if <$ty as $crate::module_param::ModuleParam>::NOARG_ALLOWED {
219+
$crate::bindings::KERNEL_PARAM_OPS_FL_NOARG
202220
} else {
203221
0
204222
},
205-
set: Some(<$ty as crate::module_param::ModuleParam>::set_param),
206-
get: Some(<$ty as crate::module_param::ModuleParam>::get_param),
207-
free: Some(<$ty as crate::module_param::ModuleParam>::free),
223+
set: Some(<$ty as $crate::module_param::ModuleParam>::set_param),
224+
get: Some(<$ty as $crate::module_param::ModuleParam>::get_param),
225+
free: Some(<$ty as $crate::module_param::ModuleParam>::free),
208226
};
209227
};
210228
}
@@ -282,16 +300,21 @@ make_param_ops!(
282300
);
283301

284302
impl ModuleParam for bool {
303+
type Value = bool;
285304
const NOARG_ALLOWED: bool = true;
286305

287-
fn try_from_param_arg(arg: Option<&[u8]>) -> Option<Self> {
306+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
288307
match arg {
289308
None => Some(true),
290309
Some(b"y") | Some(b"Y") | Some(b"1") | Some(b"true") => Some(true),
291310
Some(b"n") | Some(b"N") | Some(b"0") | Some(b"false") => Some(false),
292311
_ => None,
293312
}
294313
}
314+
315+
fn value(&self) -> &Self::Value {
316+
self
317+
}
295318
}
296319

297320
make_param_ops!(
@@ -300,3 +323,152 @@ make_param_ops!(
300323
PARAM_OPS_BOOL,
301324
bool
302325
);
326+
327+
/// An array of at __most__ `N` values.
328+
pub struct ArrayParam<T, const N: usize> {
329+
values: [core::mem::MaybeUninit<T>; N],
330+
used: usize,
331+
}
332+
333+
impl<T, const N: usize> ArrayParam<T, { N }> {
334+
fn values(&self) -> &[T] {
335+
// SAFETY: `self` can only be generated and modified by the methods
336+
// [`new`], and [`push`]. These maintain the invariant that the first
337+
// `self.used` elements of `self.values` are initialized.
338+
unsafe {
339+
&*(&self.values[0..self.used] as *const [core::mem::MaybeUninit<T>] as *const [T])
340+
}
341+
}
342+
}
343+
344+
impl<T: Copy, const N: usize> ArrayParam<T, { N }> {
345+
const fn new() -> Self {
346+
// Invariant:
347+
// The first `self.used` elements of `self.values` are initialized.
348+
ArrayParam {
349+
values: [core::mem::MaybeUninit::uninit(); N],
350+
used: 0,
351+
}
352+
}
353+
354+
const fn push(&mut self, val: T) {
355+
if self.used < N {
356+
// Invariant:
357+
// The first `self.used` elements of `self.values` are initialized.
358+
self.values[self.used] = core::mem::MaybeUninit::new(val);
359+
self.used += 1;
360+
}
361+
}
362+
363+
/// Create an instance of `ArrayParam` initialized with `vals`.
364+
///
365+
/// This function is only meant to be used in the [`module::module`] macro.
366+
pub const fn create(vals: &[T]) -> Self {
367+
let mut result = ArrayParam::new();
368+
let mut i = 0;
369+
while i < vals.len() {
370+
result.push(vals[i]);
371+
i += 1;
372+
}
373+
result
374+
}
375+
}
376+
377+
impl<T: core::fmt::Display, const N: usize> core::fmt::Display for ArrayParam<T, { N }> {
378+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
379+
for val in self.values() {
380+
write!(f, "{},", val)?;
381+
}
382+
Ok(())
383+
}
384+
}
385+
386+
impl<T: Copy + core::fmt::Display + ModuleParam, const N: usize> ModuleParam
387+
for ArrayParam<T, { N }>
388+
{
389+
type Value = [T];
390+
const NOARG_ALLOWED: bool = false;
391+
392+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
393+
arg.and_then(|args| {
394+
let mut result = Self::new();
395+
for arg in args.split(|b| *b == b',') {
396+
result.push(T::try_from_param_arg(Some(arg))?);
397+
}
398+
Some(result)
399+
})
400+
}
401+
402+
fn value(&self) -> &Self::Value {
403+
self.values()
404+
}
405+
}
406+
407+
/// A C-style string parameter.
408+
///
409+
/// The Rust version of the [`charp`] parameter. This type is meant to be
410+
/// used by the [`module::module`] macro, not handled directly. Instead use the
411+
/// `read` method generated by that macro.
412+
///
413+
///[`charp`]: ../../../include/linux/moduleparam.h
414+
pub enum StringParam {
415+
/// A borrowed parameter value.
416+
///
417+
/// Either the default value (which is static in the module) or borrowed
418+
/// from the original argument buffer used to set the value.
419+
Ref(&'static [u8]),
420+
/// A value that was allocated when the parameter was set.
421+
///
422+
/// The value needs to be freed when the parameter is reset or the module is
423+
/// unloaded.
424+
Owned(alloc::vec::Vec<u8>),
425+
}
426+
427+
impl StringParam {
428+
fn bytes(&self) -> &[u8] {
429+
match self {
430+
StringParam::Ref(bytes) => *bytes,
431+
StringParam::Owned(vec) => &vec[..],
432+
}
433+
}
434+
}
435+
436+
impl core::fmt::Display for StringParam {
437+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
438+
let bytes = self.bytes();
439+
match core::str::from_utf8(bytes) {
440+
Ok(utf8) => write!(f, "{}", utf8),
441+
Err(_) => write!(f, "{:?}", bytes),
442+
}
443+
}
444+
}
445+
446+
impl ModuleParam for StringParam {
447+
type Value = [u8];
448+
const NOARG_ALLOWED: bool = false;
449+
450+
fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
451+
// Safety: It is always safe to call [`slab_is_available`](../../../include/linux/slab.h).
452+
let slab_available = unsafe { crate::bindings::slab_is_available() };
453+
arg.map(|arg| {
454+
if slab_available {
455+
let mut vec = alloc::vec::Vec::new();
456+
vec.extend_from_slice(arg);
457+
StringParam::Owned(vec)
458+
} else {
459+
StringParam::Ref(arg)
460+
}
461+
})
462+
}
463+
464+
fn value(&self) -> &Self::Value {
465+
self.bytes()
466+
}
467+
}
468+
469+
make_param_ops!(
470+
/// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
471+
/// for [`StringParam`].
472+
PARAM_OPS_STR,
473+
StringParam
474+
);

rust/kernel/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ pub use alloc::{borrow::ToOwned, string::String};
1515

1616
pub use module::module;
1717

18-
pub use super::{println, static_assert, KernelModule, KernelResult};
18+
pub use super::{make_param_ops, println, static_assert, KernelModule, KernelResult};

0 commit comments

Comments
 (0)