From 586e37da15c695ed4d156edacf51eec9d18f0a42 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Mon, 7 Dec 2020 17:15:56 +0000 Subject: [PATCH] Add support for misc devices. Signed-off-by: Wedson Almeida Filho --- drivers/char/rust_example/src/lib.rs | 17 ++++++ rust/kernel/src/bindings_helper.h | 1 + rust/kernel/src/lib.rs | 1 + rust/kernel/src/miscdev.rs | 78 ++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 rust/kernel/src/miscdev.rs diff --git a/drivers/char/rust_example/src/lib.rs b/drivers/char/rust_example/src/lib.rs index fe74656d977dd5..c4f412022575a7 100644 --- a/drivers/char/rust_example/src/lib.rs +++ b/drivers/char/rust_example/src/lib.rs @@ -3,7 +3,12 @@ #![no_std] #![feature(global_asm)] +extern crate alloc; + +use alloc::boxed::Box; +use core::pin::Pin; use kernel::prelude::*; +use kernel::{cstr, file_operations::FileOperations, miscdev}; module! { type: RustExample, @@ -25,8 +30,18 @@ module! { }, } +struct RustFile; + +impl FileOperations for RustFile { + fn open() -> KernelResult { + println!("rust file was opened!"); + Ok(Self) + } +} + struct RustExample { message: String, + _dev: Pin>, } impl KernelModule for RustExample { @@ -36,8 +51,10 @@ impl KernelModule for RustExample { println!("Parameters:"); println!(" my_bool: {}", my_bool.read()); println!(" my_i32: {}", my_i32.read()); + Ok(RustExample { message: "on the heap!".to_owned(), + _dev: miscdev::Registration::new_pinned::(cstr!("rust_miscdev"), None)?, }) } } diff --git a/rust/kernel/src/bindings_helper.h b/rust/kernel/src/bindings_helper.h index b5b0b2e1d18d88..c58bb81bca6c45 100644 --- a/rust/kernel/src/bindings_helper.h +++ b/rust/kernel/src/bindings_helper.h @@ -7,6 +7,7 @@ #include #include #include +#include // bindgen gets confused at certain things const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; diff --git a/rust/kernel/src/lib.rs b/rust/kernel/src/lib.rs index 64df3c81337e2d..027cac7179ff7a 100644 --- a/rust/kernel/src/lib.rs +++ b/rust/kernel/src/lib.rs @@ -25,6 +25,7 @@ pub mod c_types; pub mod chrdev; mod error; pub mod file_operations; +pub mod miscdev; pub mod prelude; pub mod printk; pub mod random; diff --git a/rust/kernel/src/miscdev.rs b/rust/kernel/src/miscdev.rs new file mode 100644 index 00000000000000..6d907972e785b2 --- /dev/null +++ b/rust/kernel/src/miscdev.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::error::{Error, KernelResult}; +use crate::file_operations::{FileOperations, FileOperationsVtable}; +use crate::{bindings, c_types, CStr}; +use alloc::boxed::Box; +use core::marker::PhantomPinned; +use core::pin::Pin; + +/// A registration of a misc device. +pub struct Registration { + mdev: Option, + _pin: PhantomPinned, +} + +impl Registration { + /// Initialises a new registration but does not register it yet. It is allowed to move. + pub fn new() -> Self { + Self { + mdev: None, + _pin: PhantomPinned, + } + } + + /// Registers a new misc device. On success, it returns a pinned heap-allocated representation + /// of the registration. + pub fn new_pinned( + name: CStr<'static>, + minor: Option, + ) -> KernelResult>> { + let mut r = crate::try_alloc_pinned(Self::new())?; + r.as_mut().register::(name, minor)?; + Ok(r) + } + + /// Attempts to actually register the misc device with the rest of the kernel. It must be + /// pinned because the memory block that represents the registration is self-referential. If a + /// minor is not given, the kernel allocates a new one if possible. + pub fn register( + self: Pin<&mut Self>, + name: CStr<'static>, + minor: Option, + ) -> KernelResult<()> { + // SAFETY: we must ensure that we never move out of `this`. + let this = unsafe { self.get_unchecked_mut() }; + if this.mdev.is_some() { + // Already registered. + return Err(Error::EINVAL); + } + + this.mdev = Some(bindings::miscdevice::default()); + let dev = this.mdev.as_mut().unwrap(); + dev.fops = &FileOperationsVtable::::VTABLE; + dev.name = name.as_ptr() as *const c_types::c_char; + dev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32); + let ret = unsafe { bindings::misc_register(dev) }; + if ret < 0 { + this.mdev = None; + return Err(Error::from_kernel_errno(ret)); + } + Ok(()) + } +} + +// SAFETY: The only method is `register`, which requires a (pinned) mutable `Registration`, so it +// is safe to pass `&Registration` to multiple threads because it offers no interior mutability. +unsafe impl Sync for Registration {} + +impl Drop for Registration { + /// Removes the registration from the kernel if it has completed successfully before. + fn drop(&mut self) { + if let Some(ref mut dev) = self.mdev { + unsafe { + bindings::misc_deregister(dev); + } + } + } +}