Skip to content
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

Parameter support #7

Merged
merged 4 commits into from
Sep 19, 2020
Merged
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
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
[workspace]
members = [
"rust/shlex",
"rust/module",
"rust/kernel",
"drivers/char/rust_example",
]
2 changes: 1 addition & 1 deletion drivers/char/rust_example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
29 changes: 23 additions & 6 deletions drivers/char/rust_example/src/lib.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,26 @@

use kernel::prelude::*;

module!{
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,
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<Self> {
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"
);
1 change: 1 addition & 0 deletions rust/kernel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ publish = false

[dependencies]
bitflags = "1"
module = { path = "../module" }

[build-dependencies]
bindgen = "0.54"
2 changes: 2 additions & 0 deletions rust/kernel/build.rs
Original file line number Diff line number Diff line change
@@ -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
125 changes: 0 additions & 125 deletions rust/kernel/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<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;

// 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
3 changes: 2 additions & 1 deletion rust/kernel/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -7,8 +7,9 @@ pub use alloc::{
borrow::ToOwned,
};

pub use module::module;

pub use super::{
kernel_module,
println,
KernelResult,
KernelModule,
12 changes: 12 additions & 0 deletions rust/module/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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

355 changes: 355 additions & 0 deletions rust/module/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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_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!(
"
{cfg}
#[link_section = \".modinfo\"]
#[used]
pub static {variable}: [u8; {length}] = *b\"{string}\\0\";
",
cfg = if builtin { "#[cfg(not(MODULE))]" } else { "#[cfg(MODULE)]" },
variable = variable,
length = string.len() + 1,
string = 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 `type` 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!{
/// type: 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<Self> {
/// Ok(MyKernelModule)
/// }
/// }
/// ```
#[proc_macro]
pub fn module(ts: TokenStream) -> TokenStream {
let mut it = ts.into_iter();

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");
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",
t => panic!("Unrecognized type {}", t),
};

params_modinfo.push_str(&build_modinfo_string_param(&name, "parmtype", &param_name, &param_kernel_type));
params_modinfo.push_str(&build_modinfo_string_param(&name, "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};
// 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 {{
}}
#[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,
)
);
}

let file = std::env::var("RUST_MODFILE").unwrap();

format!(
"
static mut __MOD: Option<{type_}> = 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 = \"{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 \"{initcall_section}\", \"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 <{type_} 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}
",
type_ = type_,
name = name,
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()
}

5 changes: 5 additions & 0 deletions scripts/Makefile.lib
Original file line number Diff line number Diff line change
@@ -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) \