Skip to content

Rust int types #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,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
Expand All @@ -166,6 +166,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"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/qemu-init.sh
Original file line number Diff line number Diff line change
@@ -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

Expand Down
6 changes: 6 additions & 0 deletions drivers/char/rust_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ module! {
permissions: 0o644,
description: b"Example of a string param",
},
my_usize: usize {
default: 42,
permissions: 0o644,
description: b"Example of usize",
},
},
}

Expand Down Expand Up @@ -63,6 +68,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
Expand Down
6 changes: 6 additions & 0 deletions drivers/char/rust_example_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
}

Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions drivers/char/rust_example_3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
}

Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions drivers/char/rust_example_4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
}

Expand All @@ -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
Expand Down
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
67 changes: 67 additions & 0 deletions rust/kernel/module_param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
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);
}
}

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()?;
utf8.parse::<$ty>().ok()
}
}

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_I64, i64);
make_param_ops!(PARAM_OPS_USIZE, usize);
make_param_ops!(PARAM_OPS_ISIZE, isize);
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::bindings::param_ops_char"),
"i16" => ("i16", "kernel::bindings::param_ops_short"),
"u16" => ("u16", "kernel::bindings::param_ops_ushort"),
"i32" => ("i32", "kernel::bindings::param_ops_int"),
"u32" => ("u32", "kernel::bindings::param_ops_uint"),
"i64" => ("i64", "kernel::module_param::PARAM_OPS_I64"),
"u64" => ("u64", "kernel::bindings::param_ops_ullong"),
"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