diff options
Diffstat (limited to 'rust/kernel/src')
-rw-r--r-- | rust/kernel/src/.gitignore | 3 | ||||
-rw-r--r-- | rust/kernel/src/allocator.rs | 26 | ||||
-rw-r--r-- | rust/kernel/src/bindings.rs | 16 | ||||
-rw-r--r-- | rust/kernel/src/bindings_helper.h | 12 | ||||
-rw-r--r-- | rust/kernel/src/c_types.rs | 25 | ||||
-rw-r--r-- | rust/kernel/src/chrdev.rs | 101 | ||||
-rw-r--r-- | rust/kernel/src/error.rs | 32 | ||||
-rw-r--r-- | rust/kernel/src/file_operations.rs | 219 | ||||
-rw-r--r-- | rust/kernel/src/filesystem.rs | 82 | ||||
-rw-r--r-- | rust/kernel/src/lib.rs | 161 | ||||
-rw-r--r-- | rust/kernel/src/prelude.rs | 16 | ||||
-rw-r--r-- | rust/kernel/src/printk.rs | 72 | ||||
-rw-r--r-- | rust/kernel/src/random.rs | 43 | ||||
-rw-r--r-- | rust/kernel/src/sysctl.rs | 175 | ||||
-rw-r--r-- | rust/kernel/src/types.rs | 54 | ||||
-rw-r--r-- | rust/kernel/src/user_ptr.rs | 176 |
16 files changed, 1213 insertions, 0 deletions
diff --git a/rust/kernel/src/.gitignore b/rust/kernel/src/.gitignore new file mode 100644 index 000000000000..61552b03b12d --- /dev/null +++ b/rust/kernel/src/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +bindings_gen.rs diff --git a/rust/kernel/src/allocator.rs b/rust/kernel/src/allocator.rs new file mode 100644 index 000000000000..27647be92b51 --- /dev/null +++ b/rust/kernel/src/allocator.rs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr; + +use crate::bindings; +use crate::c_types; + +pub struct KernelAllocator; + +unsafe impl GlobalAlloc for KernelAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // krealloc is used instead of kmalloc because kmalloc is an inline function and can't be + // bound to as a result + bindings::krealloc(ptr::null(), layout.size(), bindings::GFP_KERNEL) as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + bindings::kfree(ptr as *const c_types::c_void); + } +} + +#[alloc_error_handler] +fn oom(_layout: Layout) -> ! { + panic!("Out of memory!"); +} diff --git a/rust/kernel/src/bindings.rs b/rust/kernel/src/bindings.rs new file mode 100644 index 000000000000..cfb004a6d786 --- /dev/null +++ b/rust/kernel/src/bindings.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow( + clippy::all, + non_camel_case_types, + non_upper_case_globals, + non_snake_case, + improper_ctypes +)] +mod bindings_raw { + use crate::c_types; + include!("bindings_gen.rs"); +} +pub use bindings_raw::*; + +pub const GFP_KERNEL: gfp_t = BINDINGS_GFP_KERNEL; diff --git a/rust/kernel/src/bindings_helper.h b/rust/kernel/src/bindings_helper.h new file mode 100644 index 000000000000..b5b0b2e1d18d --- /dev/null +++ b/rust/kernel/src/bindings_helper.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/version.h> + +// bindgen gets confused at certain things +const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; diff --git a/rust/kernel/src/c_types.rs b/rust/kernel/src/c_types.rs new file mode 100644 index 000000000000..35776920c99d --- /dev/null +++ b/rust/kernel/src/c_types.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![allow(non_camel_case_types)] + +#[cfg(target_arch = "x86_64")] +mod c { + use core::ffi; + + pub type c_int = i32; + pub type c_char = i8; + pub type c_long = i64; + pub type c_longlong = i64; + pub type c_short = i16; + pub type c_uchar = u8; + pub type c_uint = u32; + pub type c_ulong = u64; + pub type c_ulonglong = u64; + pub type c_ushort = u16; + pub type c_schar = i8; + pub type c_size_t = usize; + pub type c_ssize_t = isize; + pub type c_void = ffi::c_void; +} + +pub use c::*; diff --git a/rust/kernel/src/chrdev.rs b/rust/kernel/src/chrdev.rs new file mode 100644 index 000000000000..4606a7bd5a73 --- /dev/null +++ b/rust/kernel/src/chrdev.rs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::convert::TryInto; +use core::mem; +use core::ops::Range; + +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; + +use crate::bindings; +use crate::c_types; +use crate::error::{Error, KernelResult}; +use crate::file_operations; +use crate::types::CStr; + +pub fn builder(name: &'static CStr, minors: Range<u16>) -> KernelResult<Builder> { + Ok(Builder { + name, + minors, + file_ops: vec![], + }) +} + +pub struct Builder { + name: &'static CStr, + minors: Range<u16>, + file_ops: Vec<&'static bindings::file_operations>, +} + +impl Builder { + pub fn register_device<T: file_operations::FileOperations>(mut self) -> Builder { + if self.file_ops.len() >= self.minors.len() { + panic!("More devices registered than minor numbers allocated.") + } + self.file_ops + .push(&file_operations::FileOperationsVtable::<T>::VTABLE); + self + } + + pub fn build(self) -> KernelResult<Registration> { + let mut dev: bindings::dev_t = 0; + let res = unsafe { + bindings::alloc_chrdev_region( + &mut dev, + self.minors.start.into(), + self.minors.len().try_into()?, + self.name.as_ptr() as *const c_types::c_char, + ) + }; + if res != 0 { + return Err(Error::from_kernel_errno(res)); + } + + // Turn this into a boxed slice immediately because the kernel stores pointers into it, and + // so that data should never be moved. + let mut cdevs = vec![unsafe { mem::zeroed() }; self.file_ops.len()].into_boxed_slice(); + for (i, file_op) in self.file_ops.iter().enumerate() { + unsafe { + bindings::cdev_init(&mut cdevs[i], *file_op); + // TODO: proper `THIS_MODULE` handling + cdevs[i].owner = core::ptr::null_mut(); + let rc = bindings::cdev_add(&mut cdevs[i], dev + i as bindings::dev_t, 1); + if rc != 0 { + // Clean up the ones that were allocated. + for j in 0..=i { + bindings::cdev_del(&mut cdevs[j]); + } + bindings::unregister_chrdev_region(dev, self.minors.len() as _); + return Err(Error::from_kernel_errno(rc)); + } + } + } + + Ok(Registration { + dev, + count: self.minors.len(), + cdevs, + }) + } +} + +pub struct Registration { + dev: bindings::dev_t, + count: usize, + cdevs: Box<[bindings::cdev]>, +} + +// This is safe because Registration doesn't actually expose any methods. +unsafe impl Sync for Registration {} + +impl Drop for Registration { + fn drop(&mut self) { + unsafe { + for dev in self.cdevs.iter_mut() { + bindings::cdev_del(dev); + } + bindings::unregister_chrdev_region(self.dev, self.count as _); + } + } +} diff --git a/rust/kernel/src/error.rs b/rust/kernel/src/error.rs new file mode 100644 index 000000000000..95e322e39a88 --- /dev/null +++ b/rust/kernel/src/error.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::num::TryFromIntError; + +use crate::bindings; +use crate::c_types; + +pub struct Error(c_types::c_int); + +impl Error { + pub const EINVAL: Self = Error(-(bindings::EINVAL as i32)); + pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32)); + pub const EFAULT: Self = Error(-(bindings::EFAULT as i32)); + pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32)); + pub const EAGAIN: Self = Error(-(bindings::EAGAIN as i32)); + + pub fn from_kernel_errno(errno: c_types::c_int) -> Error { + Error(errno) + } + + pub fn to_kernel_errno(&self) -> c_types::c_int { + self.0 + } +} + +impl From<TryFromIntError> for Error { + fn from(_: TryFromIntError) -> Error { + Error::EINVAL + } +} + +pub type KernelResult<T> = Result<T, Error>; diff --git a/rust/kernel/src/file_operations.rs b/rust/kernel/src/file_operations.rs new file mode 100644 index 000000000000..100fb62281e9 --- /dev/null +++ b/rust/kernel/src/file_operations.rs @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::convert::{TryFrom, TryInto}; +use core::{marker, mem, ptr}; + +use alloc::boxed::Box; + +use crate::bindings; +use crate::c_types; +use crate::error::{Error, KernelResult}; +use crate::user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter}; + +bitflags::bitflags! { + pub struct FileFlags: c_types::c_uint { + const NONBLOCK = bindings::O_NONBLOCK; + } +} + +pub struct File { + ptr: *const bindings::file, +} + +impl File { + unsafe fn from_ptr(ptr: *const bindings::file) -> File { + File { ptr } + } + + pub fn pos(&self) -> u64 { + unsafe { (*self.ptr).f_pos as u64 } + } + + pub fn flags(&self) -> FileFlags { + FileFlags::from_bits_truncate(unsafe { (*self.ptr).f_flags }) + } +} + +// Matches std::io::SeekFrom in the Rust stdlib +pub enum SeekFrom { + Start(u64), + End(i64), + Current(i64), +} + +unsafe extern "C" fn open_callback<T: FileOperations>( + _inode: *mut bindings::inode, + file: *mut bindings::file, +) -> c_types::c_int { + let f = match T::open() { + Ok(f) => Box::new(f), + Err(e) => return e.to_kernel_errno(), + }; + (*file).private_data = Box::into_raw(f) as *mut c_types::c_void; + 0 +} + +unsafe extern "C" fn read_callback<T: FileOperations>( + file: *mut bindings::file, + buf: *mut c_types::c_char, + len: c_types::c_size_t, + offset: *mut bindings::loff_t, +) -> c_types::c_ssize_t { + let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) { + Ok(ptr) => ptr.writer(), + Err(e) => return e.to_kernel_errno().try_into().unwrap(), + }; + let f = &*((*file).private_data as *const T); + // No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63). + // See discussion in #113 + let positive_offset = match (*offset).try_into() { + Ok(v) => v, + Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(), + }; + let read = T::READ.unwrap(); + match read(f, &File::from_ptr(file), &mut data, positive_offset) { + Ok(()) => { + let written = len - data.len(); + (*offset) += bindings::loff_t::try_from(written).unwrap(); + written.try_into().unwrap() + } + Err(e) => e.to_kernel_errno().try_into().unwrap(), + } +} + +unsafe extern "C" fn write_callback<T: FileOperations>( + file: *mut bindings::file, + buf: *const c_types::c_char, + len: c_types::c_size_t, + offset: *mut bindings::loff_t, +) -> c_types::c_ssize_t { + let mut data = match UserSlicePtr::new(buf as *mut c_types::c_void, len) { + Ok(ptr) => ptr.reader(), + Err(e) => return e.to_kernel_errno().try_into().unwrap(), + }; + let f = &*((*file).private_data as *const T); + // No FMODE_UNSIGNED_OFFSET support, so offset must be in [0, 2^63). + // See discussion in #113 + let positive_offset = match (*offset).try_into() { + Ok(v) => v, + Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(), + }; + let write = T::WRITE.unwrap(); + match write(f, &mut data, positive_offset) { + Ok(()) => { + let read = len - data.len(); + (*offset) += bindings::loff_t::try_from(read).unwrap(); + read.try_into().unwrap() + } + Err(e) => e.to_kernel_errno().try_into().unwrap(), + } +} + +unsafe extern "C" fn release_callback<T: FileOperations>( + _inode: *mut bindings::inode, + file: *mut bindings::file, +) -> c_types::c_int { + let ptr = mem::replace(&mut (*file).private_data, ptr::null_mut()); + drop(Box::from_raw(ptr as *mut T)); + 0 +} + +unsafe extern "C" fn llseek_callback<T: FileOperations>( + file: *mut bindings::file, + offset: bindings::loff_t, + whence: c_types::c_int, +) -> bindings::loff_t { + let off = match whence as u32 { + bindings::SEEK_SET => match offset.try_into() { + Ok(v) => SeekFrom::Start(v), + Err(_) => return Error::EINVAL.to_kernel_errno().into(), + }, + bindings::SEEK_CUR => SeekFrom::Current(offset), + bindings::SEEK_END => SeekFrom::End(offset), + _ => return Error::EINVAL.to_kernel_errno().into(), + }; + let f = &*((*file).private_data as *const T); + let seek = T::SEEK.unwrap(); + match seek(f, &File::from_ptr(file), off) { + Ok(off) => off as bindings::loff_t, + Err(e) => e.to_kernel_errno().into(), + } +} + +pub(crate) struct FileOperationsVtable<T>(marker::PhantomData<T>); + +impl<T: FileOperations> FileOperationsVtable<T> { + pub(crate) const VTABLE: bindings::file_operations = bindings::file_operations { + open: Some(open_callback::<T>), + release: Some(release_callback::<T>), + read: if let Some(_) = T::READ { + Some(read_callback::<T>) + } else { + None + }, + write: if let Some(_) = T::WRITE { + Some(write_callback::<T>) + } else { + None + }, + llseek: if let Some(_) = T::SEEK { + Some(llseek_callback::<T>) + } else { + None + }, + + check_flags: None, + compat_ioctl: None, + copy_file_range: None, + fallocate: None, + fadvise: None, + fasync: None, + flock: None, + flush: None, + fsync: None, + get_unmapped_area: None, + iterate: None, + iterate_shared: None, + iopoll: None, + lock: None, + mmap: None, + mmap_supported_flags: 0, + owner: ptr::null_mut(), + poll: None, + read_iter: None, + remap_file_range: None, + sendpage: None, + setlease: None, + show_fdinfo: None, + splice_read: None, + splice_write: None, + unlocked_ioctl: None, + write_iter: None, + }; +} + +pub type ReadFn<T> = Option<fn(&T, &File, &mut UserSlicePtrWriter, u64) -> KernelResult<()>>; +pub type WriteFn<T> = Option<fn(&T, &mut UserSlicePtrReader, u64) -> KernelResult<()>>; +pub type SeekFn<T> = Option<fn(&T, &File, SeekFrom) -> KernelResult<u64>>; + +/// `FileOperations` corresponds to the kernel's `struct file_operations`. You +/// implement this trait whenever you'd create a `struct file_operations`. +/// File descriptors may be used from multiple threads (or processes) +/// concurrently, so your type must be `Sync`. +pub trait FileOperations: Sync + Sized { + /// Creates a new instance of this file. Corresponds to the `open` function + /// pointer in `struct file_operations`. + fn open() -> KernelResult<Self>; + + /// Reads data from this file to userspace. Corresponds to the `read` + /// function pointer in `struct file_operations`. + const READ: ReadFn<Self> = None; + + /// Writes data from userspace o this file. Corresponds to the `write` + /// function pointer in `struct file_operations`. + const WRITE: WriteFn<Self> = None; + + /// Changes the position of the file. Corresponds to the `llseek` function + /// pointer in `struct file_operations`. + const SEEK: SeekFn<Self> = None; +} diff --git a/rust/kernel/src/filesystem.rs b/rust/kernel/src/filesystem.rs new file mode 100644 index 000000000000..11b415008f68 --- /dev/null +++ b/rust/kernel/src/filesystem.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 + +use alloc::boxed::Box; +use core::default::Default; +use core::marker; + +use crate::bindings; +use crate::c_types; +use crate::error; +use crate::types::CStr; + +pub struct Registration<T: FileSystem> { + _phantom: marker::PhantomData<T>, + ptr: Box<bindings::file_system_type>, +} + +// This is safe because Registration doesn't actually expose any methods. +unsafe impl<T> Sync for Registration<T> where T: FileSystem {} + +impl<T: FileSystem> Drop for Registration<T> { + fn drop(&mut self) { + unsafe { bindings::unregister_filesystem(&mut *self.ptr) }; + } +} + +pub trait FileSystem: Sync { + const NAME: &'static CStr; + const FLAGS: FileSystemFlags; +} + +bitflags::bitflags! { + pub struct FileSystemFlags: c_types::c_int { + const REQUIRES_DEV = bindings::FS_REQUIRES_DEV as c_types::c_int; + const BINARY_MOUNTDATA = bindings::FS_BINARY_MOUNTDATA as c_types::c_int; + const HAS_SUBTYPE = bindings::FS_HAS_SUBTYPE as c_types::c_int; + const USERNS_MOUNT = bindings::FS_USERNS_MOUNT as c_types::c_int; + const RENAME_DOES_D_MOVE = bindings::FS_RENAME_DOES_D_MOVE as c_types::c_int; + } +} + +extern "C" fn fill_super_callback<T: FileSystem>( + _sb: *mut bindings::super_block, + _data: *mut c_types::c_void, + _silent: c_types::c_int, +) -> c_types::c_int { + // T::fill_super(...) + // This should actually create an object that gets dropped by + // file_system_registration::kill_sb. You can point to it with + // sb->s_fs_info. + unimplemented!(); +} + +extern "C" fn mount_callback<T: FileSystem>( + fs_type: *mut bindings::file_system_type, + flags: c_types::c_int, + _dev_name: *const c_types::c_char, + data: *mut c_types::c_void, +) -> *mut bindings::dentry { + unsafe { bindings::mount_nodev(fs_type, flags, data, Some(fill_super_callback::<T>)) } +} + +pub fn register<T: FileSystem>() -> error::KernelResult<Registration<T>> { + let mut fs_registration = Registration { + ptr: Box::new(bindings::file_system_type { + name: T::NAME.as_ptr() as *const i8, + // TODO: proper `THIS_MODULE` handling + owner: core::ptr::null_mut(), + fs_flags: T::FLAGS.bits(), + mount: Some(mount_callback::<T>), + kill_sb: Some(bindings::kill_litter_super), + + ..Default::default() + }), + _phantom: marker::PhantomData, + }; + let result = unsafe { bindings::register_filesystem(&mut *fs_registration.ptr) }; + if result != 0 { + return Err(error::Error::from_kernel_errno(result)); + } + + Ok(fs_registration) +} diff --git a/rust/kernel/src/lib.rs b/rust/kernel/src/lib.rs new file mode 100644 index 000000000000..bdb070cca953 --- /dev/null +++ b/rust/kernel/src/lib.rs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! The `kernel` crate + +#![no_std] +#![feature(allocator_api, alloc_error_handler, const_raw_ptr_deref)] + +extern crate alloc; + +use core::panic::PanicInfo; + +mod allocator; +pub mod bindings; +pub mod c_types; +pub mod chrdev; +mod error; +pub mod file_operations; +pub mod filesystem; +pub mod prelude; +pub mod printk; +pub mod random; +pub mod sysctl; +mod types; +pub mod user_ptr; + +pub use crate::error::{Error, KernelResult}; +pub use crate::types::{CStr, Mode}; + +/// Declares the entrypoint for a kernel module. The first argument should be a type which +/// implements the [`KernelModule`] trait. Also accepts various forms of kernel metadata. +/// +/// Example: +/// ```rust,no_run +/// use kernel::prelude::*; +/// +/// struct MyKernelModule; +/// impl KernelModule for MyKernelModule { +/// fn init() -> KernelResult<Self> { +/// Ok(MyKernelModule) +/// } +/// } +/// +/// kernel_module!( +/// MyKernelModule, +/// author: b"Rust for Linux Contributors", +/// description: b"My very own kernel module!", +/// license: b"GPL" +/// ); +#[macro_export] +macro_rules! kernel_module { + ($module:ty, $($name:ident : $value:expr),*) => { + static mut __MOD: Option<$module> = None; + + // TODO: find a proper way to emulate the C macro, including + // dealing with `HAVE_ARCH_PREL32_RELOCATIONS` + #[cfg(not(module))] + #[link_section = ".initcall6.init"] + #[used] + pub static __initcall: extern "C" fn() -> $crate::c_types::c_int = init_module; + + #[no_mangle] + pub extern "C" fn init_module() -> $crate::c_types::c_int { + match <$module as $crate::KernelModule>::init() { + Ok(m) => { + unsafe { + __MOD = Some(m); + } + return 0; + } + Err(e) => { + return e.to_kernel_errno(); + } + } + } + + #[no_mangle] + pub extern "C" fn cleanup_module() { + unsafe { + // Invokes drop() on __MOD, which should be used for cleanup. + __MOD = None; + } + } + + $( + $crate::kernel_module!(@attribute $name, $value); + )* + }; + + // TODO: The modinfo attributes below depend on the compiler placing + // the variables in order in the .modinfo section, so that you end up + // with b"key=value\0" in order in the section. This is a reasonably + // standard trick in C, but I'm not sure that rustc guarantees it. + // + // Ideally we'd be able to use concat_bytes! + stringify_bytes! + + // some way of turning a string literal (or at least a string + // literal token) into a bytes literal, and get a single static + // [u8; * N] with the whole thing, but those don't really exist yet. + // Most of the alternatives (e.g. .as_bytes() as a const fn) give + // you a pointer, not an array, which isn't right. + + // TODO: `modules.builtin.modinfo` etc. is missing the prefix (module name) + (@attribute author, $value:expr) => { + #[link_section = ".modinfo"] + #[used] + pub static AUTHOR_KEY: [u8; 7] = *b"author="; + #[link_section = ".modinfo"] + #[used] + pub static AUTHOR_VALUE: [u8; $value.len()] = *$value; + #[link_section = ".modinfo"] + #[used] + pub static AUTHOR_NUL: [u8; 1] = *b"\0"; + }; + + (@attribute description, $value:expr) => { + #[link_section = ".modinfo"] + #[used] + pub static DESCRIPTION_KEY: [u8; 12] = *b"description="; + #[link_section = ".modinfo"] + #[used] + pub static DESCRIPTION_VALUE: [u8; $value.len()] = *$value; + #[link_section = ".modinfo"] + #[used] + pub static DESCRIPTION_NUL: [u8; 1] = *b"\0"; + }; + + (@attribute license, $value:expr) => { + #[link_section = ".modinfo"] + #[used] + pub static LICENSE_KEY: [u8; 8] = *b"license="; + #[link_section = ".modinfo"] + #[used] + pub static LICENSE_VALUE: [u8; $value.len()] = *$value; + #[link_section = ".modinfo"] + #[used] + pub static LICENSE_NUL: [u8; 1] = *b"\0"; + }; +} + +/// 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 +/// teardown or cleanup operations, your type may implement [`Drop`]. +/// +/// [`Drop`]: https://doc.rust-lang.org/stable/core/ops/trait.Drop.html +pub trait KernelModule: Sized + Sync { + fn init() -> KernelResult<Self>; +} + +extern "C" { + fn rust_helper_BUG() -> !; +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + unsafe { + rust_helper_BUG(); + } +} + +#[global_allocator] +static ALLOCATOR: allocator::KernelAllocator = allocator::KernelAllocator; diff --git a/rust/kernel/src/prelude.rs b/rust/kernel/src/prelude.rs new file mode 100644 index 000000000000..d88baa99c902 --- /dev/null +++ b/rust/kernel/src/prelude.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! The `kernel` prelude + +pub use alloc::{ + string::String, + borrow::ToOwned, +}; + +pub use super::{ + kernel_module, + println, + KernelResult, + KernelModule, +}; + diff --git a/rust/kernel/src/printk.rs b/rust/kernel/src/printk.rs new file mode 100644 index 000000000000..1a27c034258a --- /dev/null +++ b/rust/kernel/src/printk.rs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::cmp; +use core::fmt; + +use crate::bindings; +use crate::c_types::c_int; + +#[doc(hidden)] +pub fn printk(s: &[u8]) { + // Don't copy the trailing NUL from `KERN_INFO`. + let mut fmt_str = [0; bindings::KERN_INFO.len() - 1 + b"%.*s\0".len()]; + fmt_str[..bindings::KERN_INFO.len() - 1] + .copy_from_slice(&bindings::KERN_INFO[..bindings::KERN_INFO.len() - 1]); + fmt_str[bindings::KERN_INFO.len() - 1..].copy_from_slice(b"%.*s\0"); + + // TODO: I believe printk never fails + unsafe { bindings::printk(fmt_str.as_ptr() as _, s.len() as c_int, s.as_ptr()) }; +} + +// From kernel/print/printk.c +const LOG_LINE_MAX: usize = 1024 - 32; + +#[doc(hidden)] +pub struct LogLineWriter { + data: [u8; LOG_LINE_MAX], + pos: usize, +} + +#[allow(clippy::new_without_default)] +impl LogLineWriter { + pub fn new() -> LogLineWriter { + LogLineWriter { + data: [0u8; LOG_LINE_MAX], + pos: 0, + } + } + + pub fn as_bytes(&self) -> &[u8] { + &self.data[..self.pos] + } +} + +impl fmt::Write for LogLineWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + let copy_len = cmp::min(LOG_LINE_MAX - self.pos, s.as_bytes().len()); + self.data[self.pos..self.pos + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]); + self.pos += copy_len; + Ok(()) + } +} + +/// [`println!`] functions the same as it does in `std`, except instead of +/// printing to `stdout`, it writes to the kernel console at the `KERN_INFO` +/// level. +/// +/// [`println!`]: https://doc.rust-lang.org/stable/std/macro.println.html +#[macro_export] +macro_rules! println { + () => ({ + $crate::printk::printk("\n".as_bytes()); + }); + ($fmt:expr) => ({ + $crate::printk::printk(concat!($fmt, "\n").as_bytes()); + }); + ($fmt:expr, $($arg:tt)*) => ({ + use ::core::fmt; + let mut writer = $crate::printk::LogLineWriter::new(); + let _ = fmt::write(&mut writer, format_args!(concat!($fmt, "\n"), $($arg)*)).unwrap(); + $crate::printk::printk(writer.as_bytes()); + }); +} diff --git a/rust/kernel/src/random.rs b/rust/kernel/src/random.rs new file mode 100644 index 000000000000..bd80c835749d --- /dev/null +++ b/rust/kernel/src/random.rs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::convert::TryInto; + +use crate::{bindings, c_types, error}; + +/// Fills `dest` with random bytes generated from the kernel's CSPRNG. Ensures +/// that the CSPRNG has been seeded before generating any random bytes, and +/// will block until it's ready. +pub fn getrandom(dest: &mut [u8]) -> error::KernelResult<()> { + let res = unsafe { bindings::wait_for_random_bytes() }; + if res != 0 { + return Err(error::Error::from_kernel_errno(res)); + } + + unsafe { + bindings::get_random_bytes( + dest.as_mut_ptr() as *mut c_types::c_void, + dest.len().try_into()?, + ); + } + Ok(()) +} + +/// Fills `dest` with random bytes generated from the kernel's CSPRNG. If the +/// CSPRNG is not yet seeded, returns an `Err(EAGAIN)` immediately. +pub fn getrandom_nonblock(dest: &mut [u8]) -> error::KernelResult<()> { + if !unsafe { bindings::rng_is_initialized() } { + return Err(error::Error::EAGAIN); + } + getrandom(dest) +} + +/// Contributes the contents of `data` to the kernel's entropy pool. Does _not_ +/// credit the kernel entropy counter though. +pub fn add_randomness(data: &[u8]) { + unsafe { + bindings::add_device_randomness( + data.as_ptr() as *const c_types::c_void, + data.len().try_into().unwrap(), + ); + } +} diff --git a/rust/kernel/src/sysctl.rs b/rust/kernel/src/sysctl.rs new file mode 100644 index 000000000000..654b7122ba3b --- /dev/null +++ b/rust/kernel/src/sysctl.rs @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 + +use alloc::boxed::Box; +use alloc::vec; +use core::mem; +use core::ptr; +use core::sync::atomic; + +use crate::bindings; +use crate::c_types; +use crate::error; +use crate::types; +use crate::user_ptr::{UserSlicePtr, UserSlicePtrWriter}; + +pub trait SysctlStorage: Sync { + fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>); + fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>); +} + +fn trim_whitespace(mut data: &[u8]) -> &[u8] { + while !data.is_empty() && (data[0] == b' ' || data[0] == b'\t' || data[0] == b'\n') { + data = &data[1..]; + } + while !data.is_empty() + && (data[data.len() - 1] == b' ' + || data[data.len() - 1] == b'\t' + || data[data.len() - 1] == b'\n') + { + data = &data[..data.len() - 1]; + } + data +} + +impl<T> SysctlStorage for &T +where + T: SysctlStorage, +{ + fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) { + (*self).store_value(data) + } + + fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>) { + (*self).read_value(data) + } +} + +impl SysctlStorage for atomic::AtomicBool { + fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) { + let result = match trim_whitespace(data) { + b"0" => { + self.store(false, atomic::Ordering::Relaxed); + Ok(()) + } + b"1" => { + self.store(true, atomic::Ordering::Relaxed); + Ok(()) + } + _ => Err(error::Error::EINVAL), + }; + (data.len(), result) + } + + fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>) { + let value = if self.load(atomic::Ordering::Relaxed) { + b"1\n" + } else { + b"0\n" + }; + (value.len(), data.write(value)) + } +} + +pub struct Sysctl<T: SysctlStorage> { + inner: Box<T>, + // Responsible for keeping the ctl_table alive. + _table: Box<[bindings::ctl_table]>, + header: *mut bindings::ctl_table_header, +} + +// This is safe because the only public method we have is get(), which returns +// &T, and T: Sync. Any new methods must adhere to this requirement. +unsafe impl<T: SysctlStorage> Sync for Sysctl<T> {} + +unsafe extern "C" fn proc_handler<T: SysctlStorage>( + ctl: *mut bindings::ctl_table, + write: c_types::c_int, + buffer: *mut c_types::c_void, + len: *mut usize, + ppos: *mut bindings::loff_t, +) -> c_types::c_int { + // If we're reading from some offset other than the beginning of the file, + // return an empty read to signal EOF. |