From 493d0d6440128a42b81fa4797212bf5966b9f54f Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 12 Sep 2020 14:38:29 +0200 Subject: [PATCH 1/4] Parameter support To have more flexibility, switch to a procedural macro instead. Signed-off-by: Miguel Ojeda --- Cargo.lock | 5 + Cargo.toml | 1 + drivers/char/rust_example/src/lib.rs | 29 ++- rust/kernel/Cargo.toml | 1 + rust/kernel/build.rs | 2 + rust/kernel/src/lib.rs | 125 ---------- rust/kernel/src/prelude.rs | 3 +- rust/module/Cargo.toml | 12 + rust/module/src/lib.rs | 355 +++++++++++++++++++++++++++ scripts/Makefile.lib | 5 + 10 files changed, 406 insertions(+), 132 deletions(-) create mode 100644 rust/module/Cargo.toml create mode 100644 rust/module/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 414ef254f79e66..c358a6a4fbdca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,7 @@ version = "0.1.0" dependencies = [ "bindgen", "bitflags", + "module", "shlex 0.1.1", ] @@ -195,6 +196,10 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "module" +version = "0.1.0" + [[package]] name = "nom" version = "5.1.2" diff --git a/Cargo.toml b/Cargo.toml index 26c9c7516ea0d4..914306648fe844 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ [workspace] members = [ "rust/shlex", + "rust/module", "rust/kernel", "drivers/char/rust_example", ] diff --git a/drivers/char/rust_example/src/lib.rs b/drivers/char/rust_example/src/lib.rs index 56562b840d9c77..e058124bb662ad 100644 --- a/drivers/char/rust_example/src/lib.rs +++ b/drivers/char/rust_example/src/lib.rs @@ -5,6 +5,26 @@ use kernel::prelude::*; +module!{ + typename: RustExample, + name: b"rust_example", + author: b"Rust for Linux Contributors", + description: b"An example kernel module written in Rust", + license: b"GPL v2", + params: { + my_bool: bool { + default_: true, + permissions: 0, + description: b"Example of bool", + }, + my_i32: i32 { + default_: 42, + permissions: 0o644, + description: b"Example of i32", + }, + }, +} + struct RustExample { message: String, } @@ -13,6 +33,9 @@ impl KernelModule for RustExample { fn init() -> KernelResult { println!("Rust Example (init)"); println!("Am I built-in? {}", !cfg!(MODULE)); + println!("Parameters:"); + println!(" my_bool: {}", my_bool.read()); + println!(" my_i32: {}", my_i32.read()); Ok(RustExample { message: "on the heap!".to_owned(), }) @@ -26,9 +49,3 @@ impl Drop for RustExample { } } -kernel_module!( - RustExample, - author: b"Rust for Linux Contributors", - description: b"An example kernel module written in Rust", - license: b"GPL v2" -); diff --git a/rust/kernel/Cargo.toml b/rust/kernel/Cargo.toml index d7f6b5aa375dee..fca60a4635ca14 100644 --- a/rust/kernel/Cargo.toml +++ b/rust/kernel/Cargo.toml @@ -9,6 +9,7 @@ publish = false [dependencies] bitflags = "1" +module = { path = "../module" } [build-dependencies] bindgen = "0.54" diff --git a/rust/kernel/build.rs b/rust/kernel/build.rs index b9085a6367b407..5a3794fdc99511 100644 --- a/rust/kernel/build.rs +++ b/rust/kernel/build.rs @@ -47,6 +47,8 @@ const INCLUDED_VARS: &[&str] = &[ "SEEK_CUR", "SEEK_END", "O_NONBLOCK", + "param_ops_bool", + "param_ops_int", ]; const OPAQUE_TYPES: &[&str] = &[ // These need to be opaque because they're both packed and aligned, which rustc diff --git a/rust/kernel/src/lib.rs b/rust/kernel/src/lib.rs index 18d553c094ffaf..d8d40ff06137d9 100644 --- a/rust/kernel/src/lib.rs +++ b/rust/kernel/src/lib.rs @@ -26,131 +26,6 @@ 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 { -/// 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; - - // Built-in modules are initialized through an initcall pointer - // - // TODO: should we compile a C file on the fly to avoid duplication? - #[cfg(not(MODULE))] - #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] - #[link_section = ".initcall6.init"] - #[used] - pub static __initcall: extern "C" fn() -> $crate::c_types::c_int = init_module; - - #[cfg(not(MODULE))] - #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] - global_asm!( - r#".section ".initcall6.init", "a" - __initcall: - .long init_module - . - .previous - "# - ); - - // TODO: pass the kernel module name here to generate a unique, - // helpful symbol name (the name would also useful for the `modinfo` - // issue below). - #[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 diff --git a/rust/kernel/src/prelude.rs b/rust/kernel/src/prelude.rs index d88baa99c902dd..75a5017488a481 100644 --- a/rust/kernel/src/prelude.rs +++ b/rust/kernel/src/prelude.rs @@ -7,8 +7,9 @@ pub use alloc::{ borrow::ToOwned, }; +pub use module::module; + pub use super::{ - kernel_module, println, KernelResult, KernelModule, diff --git a/rust/module/Cargo.toml b/rust/module/Cargo.toml new file mode 100644 index 00000000000000..c61e6d2c192cb3 --- /dev/null +++ b/rust/module/Cargo.toml @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 + +[package] +name = "module" +version = "0.1.0" +authors = ["Rust for Linux Contributors"] +edition = "2018" +publish = false + +[lib] +proc-macro = true + diff --git a/rust/module/src/lib.rs b/rust/module/src/lib.rs new file mode 100644 index 00000000000000..721f9337c3b542 --- /dev/null +++ b/rust/module/src/lib.rs @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implements the `module!` macro magic + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, Group, Delimiter, token_stream}; + +fn expect_ident(it: &mut token_stream::IntoIter) -> String { + if let TokenTree::Ident(ident) = it.next().unwrap() { + ident.to_string() + } else { + panic!("Expected Ident"); + } +} + +fn expect_punct(it: &mut token_stream::IntoIter) -> char { + if let TokenTree::Punct(punct) = it.next().unwrap() { + punct.as_char() + } else { + panic!("Expected Punct"); + } +} + +fn expect_literal(it: &mut token_stream::IntoIter) -> String { + if let TokenTree::Literal(literal) = it.next().unwrap() { + literal.to_string() + } else { + panic!("Expected Literal"); + } +} + +fn expect_group(it: &mut token_stream::IntoIter) -> Group { + if let TokenTree::Group(group) = it.next().unwrap() { + group + } else { + panic!("Expected Group"); + } +} + +fn expect_end(it: &mut token_stream::IntoIter) { + if let None = it.next() { + } else { + panic!("Expected end"); + } +} + +fn get_ident(it: &mut token_stream::IntoIter, expected_name: &str) -> String { + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let ident = expect_ident(it); + assert_eq!(expect_punct(it), ','); + ident +} + +fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let literal = expect_literal(it); + assert_eq!(expect_punct(it), ','); + literal +} + +fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> Group { + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let group = expect_group(it); + assert_eq!(expect_punct(it), ','); + group +} + +fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String { + let byte_string = get_literal(it, expected_name); + + assert!(byte_string.starts_with("b\"")); + assert!(byte_string.ends_with("\"")); + + byte_string[2..byte_string.len() - 1].to_string() +} + +fn build_modinfo_builtin_string(module_name: &str, name: &str, string: &str) -> String { + format!( + " + // Built-in modules prefix their modinfo strings by `module_name.` + #[cfg(not(MODULE))] + #[link_section = \".modinfo\"] + #[used] + pub static {name}: [u8; {length}] = *b\"{module_name}.{string}\\0\"; + ", + module_name = module_name, + name = name, + length = module_name.len() + 1 + string.len() + 1, + string = string, + ) +} + +fn build_modinfo_string(module_name: &str, name: &str, string: &str) -> String { + format!( + " + // Loadable modules' modinfo strings go as-is + #[cfg(MODULE)] + #[link_section = \".modinfo\"] + #[used] + pub static {name}: [u8; {length}] = *b\"{string}\\0\"; + ", + name = name, + length = string.len() + 1, + string = string, + ) + &build_modinfo_builtin_string(module_name, name, string) +} + +/// Declares a kernel module. +/// +/// The `typename` argument should be a type which implements the [`KernelModule`] trait. +/// Also accepts various forms of kernel metadata. +/// +/// Example: +/// ```rust,no_run +/// use kernel::prelude::*; +/// +/// module!{ +/// typename: MyKernelModule, +/// name: b"my_kernel_module", +/// author: b"Rust for Linux Contributors", +/// description: b"My very own kernel module!", +/// license: b"GPL v2", +/// params: {}, +/// } +/// +/// struct MyKernelModule; +/// +/// impl KernelModule for MyKernelModule { +/// fn init() -> KernelResult { +/// Ok(MyKernelModule) +/// } +/// } +/// ``` +#[proc_macro] +pub fn module(ts: TokenStream) -> TokenStream { + let mut it = ts.into_iter(); + + let typename = get_ident(&mut it, "typename"); + let name = get_byte_string(&mut it, "name"); + let author = get_byte_string(&mut it, "author"); + let description = get_byte_string(&mut it, "description"); + let license = get_byte_string(&mut it, "license"); + let params = get_group(&mut it, "params"); + + expect_end(&mut it); + + assert_eq!(params.delimiter(), Delimiter::Brace); + + let mut it = params.stream().into_iter(); + + let mut params_modinfo = String::new(); + + loop { + let param_name = match it.next() { + Some(TokenTree::Ident(ident)) => ident.to_string(), + Some(_) => panic!("Expected Ident or end"), + None => break, + }; + + assert_eq!(expect_punct(&mut it), ':'); + let param_type = expect_ident(&mut it); + let group = expect_group(&mut it); + assert_eq!(expect_punct(&mut it), ','); + + assert_eq!(group.delimiter(), Delimiter::Brace); + + let mut param_it = group.stream().into_iter(); + let param_default = if param_type == "bool" { + get_ident(&mut param_it, "default_") + } else { + get_literal(&mut param_it, "default_") + }; + let param_permissions = get_literal(&mut param_it, "permissions"); + let param_description = get_byte_string(&mut param_it, "description"); + expect_end(&mut param_it); + + // TODO: more primitive types + // TODO: other kinds: arrays, unsafes, etc. + let param_kernel_type = match param_type.as_ref() { + "bool" => "bool", + "i32" => "int", + _ => panic!("Unrecognized type"), + }; + + params_modinfo.push_str(&build_modinfo_string( + &name, + &format!("__{}_{}_PARMTYPE", name, param_name), + &format!("parmtype={}:{}", param_name, param_kernel_type) + )); + params_modinfo.push_str(&build_modinfo_string( + &name, + &format!("__{}_{}_PARM", name, param_name), + &format!("parm={}:{}", param_name, param_description) + )); + params_modinfo.push_str( + &format!( + " + static mut __{name}_{param_name}_VALUE: {param_type} = {param_default}; + + struct __{name}_{param_name}; + + impl __{name}_{param_name} {{ + fn read(&self) -> {param_type} {{ + unsafe {{ __{name}_{param_name}_VALUE }} + }} + }} + + const {param_name}: __{name}_{param_name} = __{name}_{param_name}; + + // FIXME: does the `align` do the right thing here? + // `core::mem::size_of(usize)` + #[repr(C,align(8))] + struct __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param); + + unsafe impl Sync for __{name}_{param_name}_RacyKernelParam {{ + }} + + #[cfg(not(MODULE))] + const __{name}_{param_name}_NAME: *const kernel::c_types::c_char = b\"{name}.{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; + + #[cfg(MODULE)] + const __{name}_{param_name}_NAME: *const kernel::c_types::c_char = b\"{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; + + #[link_section = \"__param\"] + #[used] + static __{name}_{param_name}_STRUCT: __{name}_{param_name}_RacyKernelParam = __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param {{ + name: __{name}_{param_name}_NAME, + // TODO: `THIS_MODULE` + mod_: core::ptr::null_mut(), + ops: unsafe {{ &kernel::bindings::param_ops_{param_kernel_type} }} as *const kernel::bindings::kernel_param_ops, + perm: {permissions}, + level: -1, + flags: 0, + __bindgen_anon_1: kernel::bindings::kernel_param__bindgen_ty_1 {{ + arg: unsafe {{ &__{name}_{param_name}_VALUE }} as *const _ as *mut kernel::c_types::c_void, + }}, + }}); + ", + name = name, + param_type = param_type, + param_kernel_type = param_kernel_type, + param_default = param_default, + param_name = param_name, + permissions = param_permissions, + ) + ); + } + + format!( + " + static mut __MOD: Option<{typename}> = None; + + // Loadable modules need to export the `{{init,cleanup}}_module` identifiers + #[cfg(MODULE)] + #[no_mangle] + pub extern \"C\" fn init_module() -> kernel::c_types::c_int {{ + __init() + }} + + #[cfg(MODULE)] + #[no_mangle] + pub extern \"C\" fn cleanup_module() {{ + __exit() + }} + + // Built-in modules are initialized through an initcall pointer + // and the identifiers need to be unique + #[cfg(not(MODULE))] + #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] + #[link_section = \".initcall6.init\"] + #[used] + pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init; + + #[cfg(not(MODULE))] + #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] + global_asm!( + r#\".section \".initcall6.init\", \"a\" + __{name}_initcall: + .long __{name}_init - . + .previous + \"# + ); + + #[cfg(not(MODULE))] + #[no_mangle] + pub extern \"C\" fn __{name}_init() -> kernel::c_types::c_int {{ + __init() + }} + + #[cfg(not(MODULE))] + #[no_mangle] + pub extern \"C\" fn __{name}_exit() {{ + __exit() + }} + + fn __init() -> kernel::c_types::c_int {{ + match <{typename} as KernelModule>::init() {{ + Ok(m) => {{ + unsafe {{ + __MOD = Some(m); + }} + return 0; + }} + Err(e) => {{ + return e.to_kernel_errno(); + }} + }} + }} + + fn __exit() {{ + unsafe {{ + // Invokes `drop()` on `__MOD`, which should be used for cleanup. + __MOD = None; + }} + }} + + {author} + {description} + {license} + + // Built-in modules also export the `file` modinfo string + {file} + + {params_modinfo} + ", + typename = typename, + name = name, + author = &build_modinfo_string( + &name, + &format!("__{}_AUTHOR", name), + &format!("author={}", author), + ), + description = &build_modinfo_string( + &name, + &format!("__{}_DESCRIPTION", name), + &format!("description={}", description), + ), + license = &build_modinfo_string( + &name, + &format!("__{}_LICENSE", name), + &format!("license={}", license), + ), + file = &build_modinfo_builtin_string( + &name, + &format!("__{}_FILE", name), + &format!("file={}", std::env::var("RUST_MODFILE").unwrap()), + ), + params_modinfo = params_modinfo, + ).parse().unwrap() +} + diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index a74898ff131aee..daf920fa95512f 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -237,6 +237,11 @@ rustc_flags = $(_rustc_flags) $(modkern_rustcflags) $(rustc_cfg_flags) RUSTFLAGS = $(rustc_flags) export RUSTFLAGS +# For the `module!` macro +# TODO: should be `$(modfile)`, but it is not correct for us +RUST_MODFILE = $(obj)/$(notdir $(obj)) +export RUST_MODFILE + cargo_flags = $(_cargo_flags) $(modkern_cargoflags) a_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \ From 089abbc88ba94590ce1d1d95b4e8580c485db18c Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 18 Sep 2020 18:03:59 +0200 Subject: [PATCH 2/4] Alex's review Signed-off-by: Miguel Ojeda --- drivers/char/rust_example/src/lib.rs | 6 +- rust/module/src/lib.rs | 131 +++++++++++++-------------- 2 files changed, 66 insertions(+), 71 deletions(-) diff --git a/drivers/char/rust_example/src/lib.rs b/drivers/char/rust_example/src/lib.rs index e058124bb662ad..e8405f12ce444a 100644 --- a/drivers/char/rust_example/src/lib.rs +++ b/drivers/char/rust_example/src/lib.rs @@ -6,19 +6,19 @@ use kernel::prelude::*; module!{ - typename: RustExample, + type: RustExample, name: b"rust_example", author: b"Rust for Linux Contributors", description: b"An example kernel module written in Rust", license: b"GPL v2", params: { my_bool: bool { - default_: true, + default: true, permissions: 0, description: b"Example of bool", }, my_i32: i32 { - default_: 42, + default: 42, permissions: 0o644, description: b"Example of i32", }, diff --git a/rust/module/src/lib.rs b/rust/module/src/lib.rs index 721f9337c3b542..bb15188a6abb00 100644 --- a/rust/module/src/lib.rs +++ b/rust/module/src/lib.rs @@ -78,40 +78,56 @@ fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> Stri byte_string[2..byte_string.len() - 1].to_string() } -fn build_modinfo_builtin_string(module_name: &str, name: &str, string: &str) -> String { +fn __build_modinfo_string_base(module: &str, field: &str, content: &str, variable: &str, builtin: bool) -> String { + let string = if builtin { + // Built-in modules prefix their modinfo strings by `module.` + format!("{module}.{field}={content}", module=module, field=field, content=content) + } else { + // Loadable modules' modinfo strings go as-is + format!("{field}={content}", field=field, content=content) + }; + format!( " - // Built-in modules prefix their modinfo strings by `module_name.` - #[cfg(not(MODULE))] + {cfg} #[link_section = \".modinfo\"] #[used] - pub static {name}: [u8; {length}] = *b\"{module_name}.{string}\\0\"; + pub static {variable}: [u8; {length}] = *b\"{string}\\0\"; ", - module_name = module_name, - name = name, - length = module_name.len() + 1 + string.len() + 1, + cfg = if builtin { "#[cfg(not(MODULE))]" } else { "#[cfg(MODULE)]" }, + variable = variable, + length = string.len() + 1, string = string, ) } -fn build_modinfo_string(module_name: &str, name: &str, string: &str) -> String { - format!( - " - // Loadable modules' modinfo strings go as-is - #[cfg(MODULE)] - #[link_section = \".modinfo\"] - #[used] - pub static {name}: [u8; {length}] = *b\"{string}\\0\"; - ", - name = name, - length = string.len() + 1, - string = string, - ) + &build_modinfo_builtin_string(module_name, name, string) +fn __build_modinfo_string_variable(module: &str, field: &str) -> String { + format!("__{module}_{field}", module=module, field=field) +} + +fn build_modinfo_string_only_builtin(module: &str, field: &str, content: &str) -> String { + __build_modinfo_string_base(module, field, content, &__build_modinfo_string_variable(module, field), true) +} + +fn build_modinfo_string_only_loadable(module: &str, field: &str, content: &str) -> String { + __build_modinfo_string_base(module, field, content, &__build_modinfo_string_variable(module, field), false) +} + +fn build_modinfo_string(module: &str, field: &str, content: &str) -> String { + build_modinfo_string_only_builtin(module, field, content) + + &build_modinfo_string_only_loadable(module, field, content) +} + +fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &str) -> String { + let variable = format!("__{module}_{field}_{param}", module=module, field=field, param=param); + let content = format!("{param}:{content}", param=param, content=content); + __build_modinfo_string_base(module, field, &content, &variable, true) + + &__build_modinfo_string_base(module, field, &content, &variable, false) } /// Declares a kernel module. /// -/// The `typename` argument should be a type which implements the [`KernelModule`] trait. +/// The `type` argument should be a type which implements the [`KernelModule`] trait. /// Also accepts various forms of kernel metadata. /// /// Example: @@ -119,7 +135,7 @@ fn build_modinfo_string(module_name: &str, name: &str, string: &str) -> String { /// use kernel::prelude::*; /// /// module!{ -/// typename: MyKernelModule, +/// type: MyKernelModule, /// name: b"my_kernel_module", /// author: b"Rust for Linux Contributors", /// description: b"My very own kernel module!", @@ -139,7 +155,7 @@ fn build_modinfo_string(module_name: &str, name: &str, string: &str) -> String { pub fn module(ts: TokenStream) -> TokenStream { let mut it = ts.into_iter(); - let typename = get_ident(&mut it, "typename"); + let type_ = get_ident(&mut it, "type"); let name = get_byte_string(&mut it, "name"); let author = get_byte_string(&mut it, "author"); let description = get_byte_string(&mut it, "description"); @@ -170,9 +186,9 @@ pub fn module(ts: TokenStream) -> TokenStream { let mut param_it = group.stream().into_iter(); let param_default = if param_type == "bool" { - get_ident(&mut param_it, "default_") + get_ident(&mut param_it, "default") } else { - get_literal(&mut param_it, "default_") + get_literal(&mut param_it, "default") }; let param_permissions = get_literal(&mut param_it, "permissions"); let param_description = get_byte_string(&mut param_it, "description"); @@ -183,29 +199,21 @@ pub fn module(ts: TokenStream) -> TokenStream { let param_kernel_type = match param_type.as_ref() { "bool" => "bool", "i32" => "int", - _ => panic!("Unrecognized type"), + t => panic!("Unrecognized type {}", t), }; - params_modinfo.push_str(&build_modinfo_string( - &name, - &format!("__{}_{}_PARMTYPE", name, param_name), - &format!("parmtype={}:{}", param_name, param_kernel_type) - )); - params_modinfo.push_str(&build_modinfo_string( - &name, - &format!("__{}_{}_PARM", name, param_name), - &format!("parm={}:{}", param_name, param_description) - )); + params_modinfo.push_str(&build_modinfo_string_param(&name, "parmtype", ¶m_name, ¶m_kernel_type)); + params_modinfo.push_str(&build_modinfo_string_param(&name, "parm", ¶m_name, ¶m_description)); params_modinfo.push_str( &format!( " - static mut __{name}_{param_name}_VALUE: {param_type} = {param_default}; + static mut __{name}_{param_name}_value: {param_type} = {param_default}; struct __{name}_{param_name}; impl __{name}_{param_name} {{ fn read(&self) -> {param_type} {{ - unsafe {{ __{name}_{param_name}_VALUE }} + unsafe {{ __{name}_{param_name}_value }} }} }} @@ -220,15 +228,15 @@ pub fn module(ts: TokenStream) -> TokenStream { }} #[cfg(not(MODULE))] - const __{name}_{param_name}_NAME: *const kernel::c_types::c_char = b\"{name}.{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; + const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{name}.{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; #[cfg(MODULE)] - const __{name}_{param_name}_NAME: *const kernel::c_types::c_char = b\"{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; + const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{param_name}\\0\" as *const _ as *const kernel::c_types::c_char; #[link_section = \"__param\"] #[used] - static __{name}_{param_name}_STRUCT: __{name}_{param_name}_RacyKernelParam = __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param {{ - name: __{name}_{param_name}_NAME, + static __{name}_{param_name}_struct: __{name}_{param_name}_RacyKernelParam = __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param {{ + name: __{name}_{param_name}_name, // TODO: `THIS_MODULE` mod_: core::ptr::null_mut(), ops: unsafe {{ &kernel::bindings::param_ops_{param_kernel_type} }} as *const kernel::bindings::kernel_param_ops, @@ -236,7 +244,7 @@ pub fn module(ts: TokenStream) -> TokenStream { level: -1, flags: 0, __bindgen_anon_1: kernel::bindings::kernel_param__bindgen_ty_1 {{ - arg: unsafe {{ &__{name}_{param_name}_VALUE }} as *const _ as *mut kernel::c_types::c_void, + arg: unsafe {{ &__{name}_{param_name}_value }} as *const _ as *mut kernel::c_types::c_void, }}, }}); ", @@ -250,9 +258,11 @@ pub fn module(ts: TokenStream) -> TokenStream { ); } + let file = std::env::var("RUST_MODFILE").unwrap(); + format!( " - static mut __MOD: Option<{typename}> = None; + static mut __MOD: Option<{type_}> = None; // Loadable modules need to export the `{{init,cleanup}}_module` identifiers #[cfg(MODULE)] @@ -271,14 +281,14 @@ pub fn module(ts: TokenStream) -> TokenStream { // and the identifiers need to be unique #[cfg(not(MODULE))] #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] - #[link_section = \".initcall6.init\"] + #[link_section = \"{initcall_section}\"] #[used] pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init; #[cfg(not(MODULE))] #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] global_asm!( - r#\".section \".initcall6.init\", \"a\" + r#\".section \"{initcall_section}\", \"a\" __{name}_initcall: .long __{name}_init - . .previous @@ -298,7 +308,7 @@ pub fn module(ts: TokenStream) -> TokenStream { }} fn __init() -> kernel::c_types::c_int {{ - match <{typename} as KernelModule>::init() {{ + match <{type_} as KernelModule>::init() {{ Ok(m) => {{ unsafe {{ __MOD = Some(m); @@ -327,29 +337,14 @@ pub fn module(ts: TokenStream) -> TokenStream { {params_modinfo} ", - typename = typename, + type_ = type_, name = name, - author = &build_modinfo_string( - &name, - &format!("__{}_AUTHOR", name), - &format!("author={}", author), - ), - description = &build_modinfo_string( - &name, - &format!("__{}_DESCRIPTION", name), - &format!("description={}", description), - ), - license = &build_modinfo_string( - &name, - &format!("__{}_LICENSE", name), - &format!("license={}", license), - ), - file = &build_modinfo_builtin_string( - &name, - &format!("__{}_FILE", name), - &format!("file={}", std::env::var("RUST_MODFILE").unwrap()), - ), + author = &build_modinfo_string(&name, "author", &author), + description = &build_modinfo_string(&name, "description", &description), + license = &build_modinfo_string(&name, "license", &license), + file = &build_modinfo_string_only_builtin(&name, "file", &file), params_modinfo = params_modinfo, + initcall_section = ".initcall6.init" ).parse().unwrap() } From c3f1cedbf23a467c34cd699eeb3a7734f5a89af9 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 18 Sep 2020 20:17:22 +0200 Subject: [PATCH 3/4] Fix missed rename Signed-off-by: Miguel Ojeda --- drivers/char/rust_example/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/rust_example/Cargo.toml b/drivers/char/rust_example/Cargo.toml index 500476526018d8..27232fd0634758 100644 --- a/drivers/char/rust_example/Cargo.toml +++ b/drivers/char/rust_example/Cargo.toml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # No need for details like `authors` here -- that goes in the modinfo -# via the `kernel_module!` macro +# via the `module!` macro [package] name = "rust_example" version = "0.1.0" From 60567cfb44249250ce3f86b8131a07b37c7ef55b Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 18 Sep 2020 22:25:01 +0200 Subject: [PATCH 4/4] Ignore the alignment requirement on `kernel_param` Signed-off-by: Miguel Ojeda --- rust/module/src/lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rust/module/src/lib.rs b/rust/module/src/lib.rs index bb15188a6abb00..82dac6780b9ccf 100644 --- a/rust/module/src/lib.rs +++ b/rust/module/src/lib.rs @@ -219,9 +219,14 @@ pub fn module(ts: TokenStream) -> TokenStream { const {param_name}: __{name}_{param_name} = __{name}_{param_name}; - // FIXME: does the `align` do the right thing here? - // `core::mem::size_of(usize)` - #[repr(C,align(8))] + // Note: the C macro that generates the static structs for the `__param` section + // asks for them to be `aligned(sizeof(void *))`. However, that was put in place + // in 2003 in commit 38d5b085d2 (\"[PATCH] Fix over-alignment problem on x86-64\") + // to undo GCC over-alignment of static structs of >32 bytes. It seems that is + // not the case anymore, so we simplify to a transparent representation here + // in the expectation that it is not needed anymore. + // TODO: revisit this to confirm the above comment and remove it if it happened + #[repr(transparent)] struct __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param); unsafe impl Sync for __{name}_{param_name}_RacyKernelParam {{