Skip to content

Commit

Permalink
Add ModuleParam trait and new int params
Browse files Browse the repository at this point in the history
  • Loading branch information
adamrk committed Feb 15, 2021
1 parent 711070f commit bb701e9
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 12 deletions.
2 changes: 1 addition & 1 deletion rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ $(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h
quiet_cmd_exports = EXPORTS $@
cmd_exports = \
$(NM) -p --defined-only $< \
| grep -F ' T ' | cut -d ' ' -f 3 | grep -E '^(__rust_|_R)' \
| grep -E '( T | R )' | cut -d ' ' -f 3 | grep -E '^(__rust_|_R)' \
| xargs -n1 -Isymbol \
echo 'EXPORT_SYMBOL$(exports_target_type)(symbol);' > $@

Expand Down
31 changes: 31 additions & 0 deletions rust/kernel/buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use core::fmt;

pub struct Buffer<'a> {
slice: &'a mut [u8],
pos: usize,
}

impl<'a> Buffer<'a> {
pub fn new(slice: &'a mut [u8]) -> Self {
Buffer {
slice,
pos: 0,
}
}

pub fn bytes_written(&self) -> usize {
self.pos
}
}

impl<'a> fmt::Write for Buffer<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
if s.len() > self.slice.len() - self.pos {
Err(fmt::Error)
} else {
self.slice[self.pos..self.pos + s.len()].copy_from_slice(s.as_bytes());
self.pos += s.len();
Ok(())
}
}
}
4 changes: 4 additions & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ use core::panic::PanicInfo;

mod allocator;
pub mod bindings;
mod buffer;
pub mod c_types;
pub mod chrdev;
mod error;
pub mod file_operations;
pub mod miscdev;
pub mod module_param;
pub mod prelude;
pub mod printk;
pub mod random;
Expand All @@ -32,6 +34,8 @@ pub mod user_ptr;
pub use crate::error::{Error, KernelResult};
pub use crate::types::{CStr, Mode};

pub const PAGE_SIZE: usize = 1 << bindings::PAGE_SHIFT;

/// KernelModule is the top level entrypoint to implementing a kernel module. Your kernel module
/// should implement the `init` method on it, which maps to the `module_init` macro in Linux C API.
/// You can use this method to do whatever setup or registration your module should do. For any
Expand Down
137 changes: 137 additions & 0 deletions rust/kernel/module_param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use core::fmt::Write;

/// Types that can be used for module parameters.
/// Note that displaying the type in `sysfs` will fail if `to_string` returns
/// more than 4K bytes (including an additional null terminator).
pub trait ModuleParam : core::fmt::Display + core::marker::Sized {
fn try_from_param_arg(arg: &[u8]) -> Option<Self>;

/// # Safety
///
/// `val` 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 = crate::c_types::c_string_bytes(val);
match Self::try_from_param_arg(arg) {
Some(new_value) => {
let old_value = (*param).__bindgen_anon_1.arg as *mut Self;
let _ = core::ptr::replace(old_value, new_value);
0
}
None => crate::error::Error::EINVAL.to_kernel_errno()
}
}

/// # 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 {
let slice = core::slice::from_raw_parts_mut(buf as *mut u8, crate::PAGE_SIZE);
let mut buf = crate::buffer::Buffer::new(slice);
match write!(buf, "{}\0", *((*param).__bindgen_anon_1.arg as *mut Self)) {
Err(_) => crate::error::Error::EINVAL.to_kernel_errno(),
Ok(()) => buf.bytes_written() as crate::c_types::c_int,
}
}

/// # Safety
///
/// The `arg` field of `param` must be an instance of `Self`.
unsafe extern "C" fn free(arg: *mut crate::c_types::c_void) {
core::ptr::drop_in_place(arg as *mut Self);
}
}

/// Trait for parsing integers. Strings begining 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.
trait ParseInt : Sized {
fn from_str_radix(src: &str, radix: u32) -> Result<Self, core::num::ParseIntError>;
fn checked_neg(self) -> Option<Self>;

fn from_str_unsigned(src: &str) -> Result<Self, core::num::ParseIntError> {
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<Self> {
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<Self, core::num::ParseIntError> {
$ty::from_str_radix(src, radix)
}
fn checked_neg(self) -> Option<Self> {
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! make_param_ops {
($ops:ident, $ty:ident) => {
impl ModuleParam for $ty {
fn try_from_param_arg(arg: &[u8]) -> Option<Self> {
let utf8 = core::str::from_utf8(arg).ok()?;
<$ty as crate::module_param::ParseInt>::from_str(utf8)
}
}

pub static $ops: crate::bindings::kernel_param_ops = crate::bindings::kernel_param_ops {
flags: 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),
};
}
}

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);
30 changes: 19 additions & 11 deletions rust/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,16 @@ fn permissions_are_readonly(perms: &str) -> bool {
/// # Supported Parameter Types
///
/// - `bool` - Corresponds to C `bool` param type.
/// - `i8` - No equivalent C param type.
/// - `u8` - Corresponds to C `char` param type.
/// - `i16` - Corresponds to C `short` param type.
/// - `u16` - Corresponds to C `ushort` param type.
/// - `i32` - Corresponds to C `int` param type.
/// - `u32` - Corresponds to C `uint` param type.
/// - `i64` - No equivalent C param type.
/// - `u64` - Corresponds to C `ullong` param type.
/// - `isize` - No equivalent C param type.
/// - `usize` - No equivalent C param type.
/// - `str` - Corresponds to C `charp` param type.
/// Reading the param returns a `&[u8]`.
///
Expand Down Expand Up @@ -277,15 +281,19 @@ pub fn module(ts: TokenStream) -> TokenStream {

// TODO: more primitive types
// TODO: other kinds: arrays, unsafes, etc.
let param_kernel_type = match param_type.as_ref() {
"bool" => "bool",
"u8" => "char",
"i16" => "short",
"u16" => "ushort",
"i32" => "int",
"u32" => "uint",
"u64" => "ullong",
"str" => "charp",
let (param_kernel_type, ops) = match param_type.as_ref() {
"bool" => ("bool", "kernel::bindings::param_ops_bool"),
"i8" => ("i8", "kernel::module_param::PARAM_OPS_I8"),
"u8" => ("u8", "kernel::module_param::PARAM_OPS_U8"),
"i16" => ("i16", "kernel::module_param::PARAM_OPS_I16"),
"u16" => ("u16", "kernel::module_param::PARAM_OPS_U16"),
"i32" => ("i32", "kernel::module_param::PARAM_OPS_I32"),
"u32" => ("u32", "kernel::module_param::PARAM_OPS_U32"),
"i64" => ("i64", "kernel::module_param::PARAM_OPS_I64"),
"u64" => ("u64", "kernel::module_param::PARAM_OPS_U64"),
"isize" => ("isize", "kernel::module_param::PARAM_OPS_ISIZE"),
"usize" => ("usize", "kernel::module_param::PARAM_OPS_USIZE"),
"str" => ("charp", "kernel::bindings::param_ops_charp"),
t => panic!("Unrecognized type {}", t),
};

Expand Down Expand Up @@ -409,7 +417,7 @@ pub fn module(ts: TokenStream) -> TokenStream {
mod_: unsafe {{ &kernel::bindings::__this_module as *const _ as *mut _ }},
#[cfg(not(MODULE))]
mod_: core::ptr::null_mut(),
ops: unsafe {{ &kernel::bindings::param_ops_{param_kernel_type} }} as *const kernel::bindings::kernel_param_ops,
ops: unsafe {{ &{ops} }} as *const kernel::bindings::kernel_param_ops,
perm: {permissions},
level: -1,
flags: 0,
Expand All @@ -419,9 +427,9 @@ pub fn module(ts: TokenStream) -> TokenStream {
name = name,
param_type_internal = param_type_internal,
read_func = read_func,
param_kernel_type = param_kernel_type,
param_default = param_default,
param_name = param_name,
ops = ops,
permissions = param_permissions,
kparam = kparam,
)
Expand Down

0 comments on commit bb701e9

Please sign in to comment.