From 4021d947dea69506d11b148486e1565961a85e53 Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 26 Feb 2021 20:50:54 +0100 Subject: [PATCH 01/10] Add ModuleParam trait and new int params --- rust/Makefile | 2 +- rust/kernel/buffer.rs | 31 ++++++++ rust/kernel/lib.rs | 4 ++ rust/kernel/module_param.rs | 137 ++++++++++++++++++++++++++++++++++++ rust/module.rs | 30 +++++--- 5 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 rust/kernel/buffer.rs create mode 100644 rust/kernel/module_param.rs diff --git a/rust/Makefile b/rust/Makefile index 54d2dbcd1a4f67..2085288b8d11cd 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -72,7 +72,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);' > $@ diff --git a/rust/kernel/buffer.rs b/rust/kernel/buffer.rs new file mode 100644 index 00000000000000..e5eb0e406dba33 --- /dev/null +++ b/rust/kernel/buffer.rs @@ -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(()) + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e40a7970422dd0..49a457dc443bad 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -30,11 +30,13 @@ mod allocator; #[doc(hidden)] 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; @@ -48,6 +50,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; + /// The top level entrypoint to implementing a kernel module. /// /// For any teardown or cleanup operations, your type may implement [`Drop`]. diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs new file mode 100644 index 00000000000000..b07e70eb6b5390 --- /dev/null +++ b/rust/kernel/module_param.rs @@ -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 `kernel::PAGE_SIZE` bytes (including an additional null terminator). +pub trait ModuleParam : core::fmt::Display + core::marker::Sized { + fn try_from_param_arg(arg: &[u8]) -> Option; + + /// # 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; + 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! make_param_ops { + ($ops:ident, $ty:ident) => { + impl ModuleParam for $ty { + fn try_from_param_arg(arg: &[u8]) -> Option { + 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); diff --git a/rust/module.rs b/rust/module.rs index 792f6020d840ae..3d2cfb882ea5b2 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -230,12 +230,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 returns a byte /// slice. /// @@ -286,15 +290,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), }; @@ -421,7 +429,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, @@ -431,9 +439,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, ) From 0fbeb9fc7e16d2ababedc9cc182fe7e6921c205a Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 12 Feb 2021 23:32:24 +0100 Subject: [PATCH 02/10] Add usize param to examples --- .github/workflows/ci.yaml | 7 ++++++- .github/workflows/qemu-init.sh | 2 +- drivers/char/rust_example.rs | 6 ++++++ drivers/char/rust_example_2.rs | 6 ++++++ drivers/char/rust_example_3.rs | 6 ++++++ drivers/char/rust_example_4.rs | 6 ++++++ 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f6810fa8aa695e..809dccf5383ca6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -156,7 +156,7 @@ jobs: # Run - run: ${{ env.BUILD_DIR }}usr/gen_init_cpio .github/workflows/qemu-initramfs.desc > qemu-initramfs.img - - run: qemu-system-${{ env.QEMU_ARCH }} -kernel ${{ env.BUILD_DIR }}${{ env.IMAGE_PATH }} -initrd qemu-initramfs.img -M ${{ env.QEMU_MACHINE }} -cpu ${{ env.QEMU_CPU }} -smp 2 -nographic -no-reboot -append '${{ env.QEMU_APPEND }} rust_example.my_i32=123321 rust_example.my_str=🦀mod rust_example.my_invbool=y rust_example_2.my_i32=234432' | tee qemu-stdout.log + - run: qemu-system-${{ env.QEMU_ARCH }} -kernel ${{ env.BUILD_DIR }}${{ env.IMAGE_PATH }} -initrd qemu-initramfs.img -M ${{ env.QEMU_MACHINE }} -cpu ${{ env.QEMU_CPU }} -smp 2 -nographic -no-reboot -append '${{ env.QEMU_APPEND }} rust_example.my_i32=123321 rust_example.my_str=🦀mod rust_example_2.my_i32=234432' | tee qemu-stdout.log # Check - run: grep -F '] Rust Example (init)' qemu-stdout.log @@ -169,6 +169,11 @@ jobs: - run: "grep -F '] [3] my_i32: 345543' qemu-stdout.log" - run: "grep -F '] [4] my_i32: 456654' qemu-stdout.log" + - run: "grep -F '] my_usize: 42' qemu-stdout.log" + - run: "grep -F '] [2] my_usize: 42' qemu-stdout.log" + - run: "grep -F '] [3] my_usize: 42' qemu-stdout.log" + - run: "grep -F '] [4] my_usize: 84' qemu-stdout.log" + - run: "grep '\\] my_str: 🦀mod\\s*$' qemu-stdout.log" - run: "grep '\\] \\[2\\] my_str: default str val\\s*$' qemu-stdout.log" - run: "grep '\\] \\[3\\] my_str: 🦀mod\\s*$' qemu-stdout.log" diff --git a/.github/workflows/qemu-init.sh b/.github/workflows/qemu-init.sh index a0d547af61e7bd..666630c1798f26 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -1,7 +1,7 @@ #!/bin/sh busybox insmod rust_example_3.ko my_i32=345543 my_str=🦀mod -busybox insmod rust_example_4.ko my_i32=456654 +busybox insmod rust_example_4.ko my_i32=456654 my_usize=84 busybox rmmod rust_example_3.ko busybox rmmod rust_example_4.ko diff --git a/drivers/char/rust_example.rs b/drivers/char/rust_example.rs index f8233000c7a1c7..91ba6bc353a987 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -33,6 +33,11 @@ module! { permissions: 0o644, description: b"Example of a string param", }, + my_usize: usize { + default: 42, + permissions: 0o644, + description: b"Example of usize", + }, }, } @@ -66,6 +71,7 @@ impl KernelModule for RustExample { " my_str: {}", core::str::from_utf8(my_str.read(&lock))? ); + println!(" my_usize: {}", my_usize.read(&lock)); } // Including this large variable on the stack will trigger diff --git a/drivers/char/rust_example_2.rs b/drivers/char/rust_example_2.rs index 9db5d84f681e0a..3f0630b286476a 100644 --- a/drivers/char/rust_example_2.rs +++ b/drivers/char/rust_example_2.rs @@ -28,6 +28,11 @@ module! { permissions: 0o644, description: b"Example of a string param", }, + my_usize: usize { + default: 42, + permissions: 0o644, + description: b"Example of usize", + }, }, } @@ -48,6 +53,7 @@ impl KernelModule for RustExample2 { "[2] my_str: {}", core::str::from_utf8(my_str.read(&lock))? ); + println!("[2] my_usize: {}", my_usize.read(&lock)); } // Including this large variable on the stack will trigger diff --git a/drivers/char/rust_example_3.rs b/drivers/char/rust_example_3.rs index bc7f44d176f00d..5fa88b3c969382 100644 --- a/drivers/char/rust_example_3.rs +++ b/drivers/char/rust_example_3.rs @@ -28,6 +28,11 @@ module! { permissions: 0o644, description: b"Example of a string param", }, + my_usize: usize { + default: 42, + permissions: 0o644, + description: b"Example of usize", + }, }, } @@ -48,6 +53,7 @@ impl KernelModule for RustExample3 { "[3] my_str: {}", core::str::from_utf8(my_str.read(&lock))? ); + println!("[3] my_usize: {}", my_usize.read(&lock)); } // Including this large variable on the stack will trigger diff --git a/drivers/char/rust_example_4.rs b/drivers/char/rust_example_4.rs index 2ee356690330e6..9b7230d01d1905 100644 --- a/drivers/char/rust_example_4.rs +++ b/drivers/char/rust_example_4.rs @@ -28,6 +28,11 @@ module! { permissions: 0o644, description: b"Example of a string param", }, + my_usize: usize { + default: 42, + permissions: 0o644, + description: b"Example of usize", + }, }, } @@ -48,6 +53,7 @@ impl KernelModule for RustExample4 { "[4] my_str: {}", core::str::from_utf8(my_str.read(&lock))? ); + println!("[4] my_usize: {}", my_usize.read(&lock)); } // Including this large variable on the stack will trigger From 901cd01046d086cbdf025db61b2634a41a7332f8 Mon Sep 17 00:00:00 2001 From: adamrk Date: Tue, 16 Feb 2021 21:54:27 +0100 Subject: [PATCH 03/10] Add NO_ARG and bool ops --- rust/kernel/module_param.rs | 62 +++++++++++++++++++++++++++++++------ rust/module.rs | 2 +- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index b07e70eb6b5390..b98f7089296bba 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -4,14 +4,21 @@ use core::fmt::Write; /// Note that displaying the type in `sysfs` will fail if `to_string` returns /// more than `kernel::PAGE_SIZE` bytes (including an additional null terminator). pub trait ModuleParam : core::fmt::Display + core::marker::Sized { - fn try_from_param_arg(arg: &[u8]) -> Option; + /// 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; + + /// `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(_)`. + fn try_from_param_arg(arg: Option<&[u8]>) -> Option; /// # Safety /// - /// `val` must point to a valid null-terminated string. The `arg` field of - /// `param` must be an instance of `Self`. + /// 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 = crate::c_types::c_string_bytes(val); + let arg = if val.is_null() { None } else { Some(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; @@ -107,17 +114,28 @@ impl_parse_int!(u64); impl_parse_int!(isize); impl_parse_int!(usize); -macro_rules! make_param_ops { - ($ops:ident, $ty:ident) => { +macro_rules! impl_module_param { + ($ty:ident) => { impl ModuleParam for $ty { - fn try_from_param_arg(arg: &[u8]) -> Option { - let utf8 = core::str::from_utf8(arg).ok()?; + const NOARG_ALLOWED: bool = false; + + fn try_from_param_arg(arg: Option<&[u8]>) -> Option { + let bytes = arg?; + let utf8 = core::str::from_utf8(bytes).ok()?; <$ty as crate::module_param::ParseInt>::from_str(utf8) } } + } +} +macro_rules! make_param_ops { + ($ops:ident, $ty:ident) => { pub static $ops: crate::bindings::kernel_param_ops = crate::bindings::kernel_param_ops { - flags: 0, + 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), @@ -125,6 +143,17 @@ macro_rules! make_param_ops { } } +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!(PARAM_OPS_I8, i8); make_param_ops!(PARAM_OPS_U8, u8); make_param_ops!(PARAM_OPS_I16, i16); @@ -135,3 +164,18 @@ 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); + +impl ModuleParam for bool { + const NOARG_ALLOWED: bool = true; + + fn try_from_param_arg(arg: Option<&[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, + } + } +} + +make_param_ops!(PARAM_OPS_BOOL, bool); diff --git a/rust/module.rs b/rust/module.rs index 3d2cfb882ea5b2..a7925f3ac1961d 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -291,7 +291,7 @@ pub fn module(ts: TokenStream) -> TokenStream { // TODO: more primitive types // TODO: other kinds: arrays, unsafes, etc. let (param_kernel_type, ops) = match param_type.as_ref() { - "bool" => ("bool", "kernel::bindings::param_ops_bool"), + "bool" => ("bool", "kernel::module_param::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"), From 8c8b4793f4f278a0a21aabc7ab8cebe5805a4607 Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 26 Feb 2021 21:55:11 +0100 Subject: [PATCH 04/10] Add docs --- rust/kernel/lib.rs | 1 + rust/kernel/module_param.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 49a457dc443bad..7fd2220ead804f 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -50,6 +50,7 @@ pub mod user_ptr; pub use crate::error::{Error, KernelResult}; pub use crate::types::{CStr, Mode}; +/// Page size defined in terms of the `PAGE_SHIFT` macro from C. pub const PAGE_SIZE: usize = 1 << bindings::PAGE_SHIFT; /// The top level entrypoint to implementing a kernel module. diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index b98f7089296bba..0131a2f001532d 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Types for module parameters. +//! +//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) + use core::fmt::Write; /// Types that can be used for module parameters. @@ -130,6 +136,7 @@ macro_rules! impl_module_param { macro_rules! make_param_ops { ($ops:ident, $ty:ident) => { + /// Generated 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 From 923540b6fbfdd32aab5b81746d521671de2de688 Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 26 Feb 2021 22:41:01 +0100 Subject: [PATCH 05/10] rustfmt --- rust/kernel/buffer.rs | 5 +---- rust/kernel/module_param.rs | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/rust/kernel/buffer.rs b/rust/kernel/buffer.rs index e5eb0e406dba33..ef1525f5b45d78 100644 --- a/rust/kernel/buffer.rs +++ b/rust/kernel/buffer.rs @@ -7,10 +7,7 @@ pub struct Buffer<'a> { impl<'a> Buffer<'a> { pub fn new(slice: &'a mut [u8]) -> Self { - Buffer { - slice, - pos: 0, - } + Buffer { slice, pos: 0 } } pub fn bytes_written(&self) -> usize { diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 0131a2f001532d..23823e608a7f37 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -9,7 +9,7 @@ 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 `kernel::PAGE_SIZE` bytes (including an additional null terminator). -pub trait ModuleParam : core::fmt::Display + core::marker::Sized { +pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// 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; @@ -23,15 +23,22 @@ pub trait ModuleParam : core::fmt::Display + core::marker::Sized { /// /// 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(crate::c_types::c_string_bytes(val)) }; + 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(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() + None => crate::error::Error::EINVAL.to_kernel_errno(), } } @@ -39,7 +46,10 @@ pub trait ModuleParam : core::fmt::Display + core::marker::Sized { /// /// `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 { + 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)) { @@ -61,7 +71,7 @@ pub trait ModuleParam : core::fmt::Display + core::marker::Sized { /// 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 { +trait ParseInt: Sized { fn from_str_radix(src: &str, radix: u32) -> Result; fn checked_neg(self) -> Option; @@ -106,7 +116,7 @@ macro_rules! impl_parse_int { self.checked_neg() } } - } + }; } impl_parse_int!(i8); @@ -131,7 +141,7 @@ macro_rules! impl_module_param { <$ty as crate::module_param::ParseInt>::from_str(utf8) } } - } + }; } macro_rules! make_param_ops { @@ -143,11 +153,11 @@ macro_rules! make_param_ops { } else { 0 }, - set: Some(<$ty as crate::module_param::ModuleParam>::set_param), + 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); From edf5833ef8f09661bd04a181d6d3c1190b087327 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 27 Feb 2021 17:42:11 +0100 Subject: [PATCH 06/10] Improve docs --- rust/kernel/buffer.rs | 15 ++++ rust/kernel/lib.rs | 2 +- rust/kernel/module_param.rs | 143 +++++++++++++++++++++++++++++++----- 3 files changed, 140 insertions(+), 20 deletions(-) diff --git a/rust/kernel/buffer.rs b/rust/kernel/buffer.rs index ef1525f5b45d78..34b83a9e36091d 100644 --- a/rust/kernel/buffer.rs +++ b/rust/kernel/buffer.rs @@ -1,15 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Struct for writing to a pre-allocated buffer with the [`write!`] macro. +//! +//! [`write!`]: https://doc.rust-lang.org/core/macro.write.html + use core::fmt; +/// A pre-allocated buffer that implements [`core::fmt::Write`]. +/// +/// Consequtive writes will append to what has already been written. +/// Writes that don't fit in the buffer will fail. +/// +/// [`core::fmt::Write`]: https://doc.rust-lang.org/core/fmt/trait.Write.html pub struct Buffer<'a> { slice: &'a mut [u8], pos: usize, } impl<'a> Buffer<'a> { + /// Create a new buffer from an existing array. pub fn new(slice: &'a mut [u8]) -> Self { Buffer { slice, pos: 0 } } + /// Number of bytes that have already been written to the buffer. + /// This will always be less than the length of the original array. pub fn bytes_written(&self) -> usize { self.pos } diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 7fd2220ead804f..93f0d0979567ef 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -30,7 +30,7 @@ mod allocator; #[doc(hidden)] pub mod bindings; -mod buffer; +pub mod buffer; pub mod c_types; pub mod chrdev; mod error; diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 23823e608a7f37..2ef7d1d9b8aa5b 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -7,18 +7,34 @@ 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 `kernel::PAGE_SIZE` bytes (including an additional null terminator). +/// +/// 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 `kernel::PAGE_SIZE` +/// bytes (including an additional null terminator). +/// +/// [`alloc::string::ToString::to_string`]: https://doc.rust-lang.org/alloc/string/trait.ToString.html#tymethod.to_string +/// [`core::fmt::Display`]: https://doc.rust-lang.org/core/fmt/trait.Display.html pub trait ModuleParam: core::fmt::Display + core::marker::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(_)`. fn try_from_param_arg(arg: Option<&[u8]>) -> Option; + /// 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 @@ -42,6 +58,10 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { } } + /// 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 @@ -58,6 +78,10 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { } } + /// Drop the parameter. + /// + /// Called when unloading a module. + /// /// # Safety /// /// The `arg` field of `param` must be an instance of `Self`. @@ -66,11 +90,16 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { } } -/// 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 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. +/// +/// [`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; @@ -144,9 +173,30 @@ macro_rules! impl_module_param { }; } +#[macro_export] +/// Generate a static [`kernel_param_ops`](../../../include/linux/moduleparam.h) struct. +/// +/// # Example +/// ```rust +/// 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:ident) => { - /// Generated param ops. + make_param_ops!( + #[doc=""] + $ops, + $ty + ); + }; + ($(#[$meta:meta])* $ops:ident, $ty:ident) => { + $(#[$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 @@ -171,16 +221,66 @@ impl_module_param!(u64); impl_module_param!(isize); impl_module_param!(usize); -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); +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 { const NOARG_ALLOWED: bool = true; @@ -195,4 +295,9 @@ impl ModuleParam for bool { } } -make_param_ops!(PARAM_OPS_BOOL, bool); +make_param_ops!( + /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) + /// for [`bool`]. + PARAM_OPS_BOOL, + bool +); From fce463d6f3e3658f638e34e3f15371a91b5fe9b1 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 27 Feb 2021 21:14:56 +0100 Subject: [PATCH 07/10] clean up export symbol regex --- rust/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Makefile b/rust/Makefile index 2085288b8d11cd..5be6212c5a9294 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -72,7 +72,7 @@ $(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h quiet_cmd_exports = EXPORTS $@ cmd_exports = \ $(NM) -p --defined-only $< \ - | grep -E '( T | R )' | 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);' > $@ From 5d8e7af679beadcb56bb7a82adffc48910f254e3 Mon Sep 17 00:00:00 2001 From: adamrk Date: Tue, 2 Mar 2021 21:45:05 +0100 Subject: [PATCH 08/10] remove doc.rust-lang.org links --- rust/kernel/buffer.rs | 4 ---- rust/kernel/module_param.rs | 3 --- 2 files changed, 7 deletions(-) diff --git a/rust/kernel/buffer.rs b/rust/kernel/buffer.rs index 34b83a9e36091d..60e6a04a37699e 100644 --- a/rust/kernel/buffer.rs +++ b/rust/kernel/buffer.rs @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 //! Struct for writing to a pre-allocated buffer with the [`write!`] macro. -//! -//! [`write!`]: https://doc.rust-lang.org/core/macro.write.html use core::fmt; @@ -10,8 +8,6 @@ use core::fmt; /// /// Consequtive writes will append to what has already been written. /// Writes that don't fit in the buffer will fail. -/// -/// [`core::fmt::Write`]: https://doc.rust-lang.org/core/fmt/trait.Write.html pub struct Buffer<'a> { slice: &'a mut [u8], pos: usize, diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 2ef7d1d9b8aa5b..553aad1c294d2d 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -12,9 +12,6 @@ use core::fmt::Write; /// [`alloc::string::ToString::to_string`] (as implemented through the /// [`core::fmt::Display`] trait) writes more than `kernel::PAGE_SIZE` /// bytes (including an additional null terminator). -/// -/// [`alloc::string::ToString::to_string`]: https://doc.rust-lang.org/alloc/string/trait.ToString.html#tymethod.to_string -/// [`core::fmt::Display`]: https://doc.rust-lang.org/core/fmt/trait.Display.html pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// Whether the parameter is allowed to be set without an argument. /// From 41325ef55efd5081e8d92fabbc8483c8e4aacd1f Mon Sep 17 00:00:00 2001 From: adamrk Date: Tue, 2 Mar 2021 21:50:11 +0100 Subject: [PATCH 09/10] fix PAGE_SIZE doc link --- rust/kernel/module_param.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 553aad1c294d2d..776458773c0821 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -10,8 +10,10 @@ use core::fmt::Write; /// /// 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 `kernel::PAGE_SIZE` +/// [`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 { /// Whether the parameter is allowed to be set without an argument. /// From 3ec22aa971d4acc397355171a7bf69a2b43905e9 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Tue, 2 Mar 2021 22:01:14 +0100 Subject: [PATCH 10/10] add link to PAGE_SHIFT in C Co-authored-by: Miguel Ojeda --- rust/kernel/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 93f0d0979567ef..6ef12992f50a81 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -51,6 +51,8 @@ pub use crate::error::{Error, KernelResult}; pub use crate::types::{CStr, Mode}; /// Page size defined in terms of the `PAGE_SHIFT` macro from C. +/// +/// [`PAGE_SHIFT`]: ../../../include/asm-generic/page.h pub const PAGE_SIZE: usize = 1 << bindings::PAGE_SHIFT; /// The top level entrypoint to implementing a kernel module.