Skip to content
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
8 changes: 7 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ jobs:
timeout-minutes: 20

strategy:
fail-fast: false
matrix:
arch: [x86_64, arm64]
toolchain: [gcc, clang, llvm]
Expand Down Expand Up @@ -156,7 +157,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_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 @@ -169,6 +170,11 @@ jobs:
- run: "grep -F '] [3] my_i32: 345543' qemu-stdout.log"
- run: "grep -F '] [4] my_i32: 456654' 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"
- run: "grep '\\] \\[4\\] my_str: default str val\\s*$' qemu-stdout.log"

- run: grep -F '] [3] Rust Example (exit)' qemu-stdout.log
- run: grep -F '] [4] Rust Example (exit)' 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,6 +1,6 @@
#!/bin/sh

busybox insmod rust_example_3.ko my_i32=345543
busybox insmod rust_example_3.ko my_i32=345543 my_str=🦀mod
busybox insmod rust_example_4.ko my_i32=456654
busybox rmmod rust_example_3.ko
busybox rmmod rust_example_4.ko
Expand Down
9 changes: 9 additions & 0 deletions drivers/char/rust_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ module! {
permissions: 0o644,
description: b"Example of i32",
},
my_str: CopyString {
default: "default str val",
permissions: 0o644,
description: b"Example of a string param",
},
},
}

Expand All @@ -51,6 +56,10 @@ impl KernelModule for RustExample {
println!("Parameters:");
println!(" my_bool: {}", my_bool.read());
println!(" my_i32: {}", my_i32.read());
println!(
" my_str: {}",
my_str.read().expect("Expected valid UTF8 parameter")
);

Ok(RustExample {
message: "on the heap!".to_owned(),
Expand Down
9 changes: 9 additions & 0 deletions drivers/char/rust_example_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ module! {
permissions: 0o644,
description: b"Example of i32",
},
my_str: CopyString {
default: "default str val",
permissions: 0o644,
description: b"Example of a string param",
},
},
}

Expand All @@ -36,6 +41,10 @@ impl KernelModule for RustExample2 {
println!("[2] Parameters:");
println!("[2] my_bool: {}", my_bool.read());
println!("[2] my_i32: {}", my_i32.read());
println!(
"[2] my_str: {}",
my_str.read().expect("Expected valid UTF8 parameter")
);
Ok(RustExample2 {
message: "on the heap!".to_owned(),
})
Expand Down
10 changes: 10 additions & 0 deletions drivers/char/rust_example_3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ module! {
permissions: 0o644,
description: b"Example of i32",
},
my_str: CopyString {
default: "default str val",
permissions: 0o644,
description: b"Example of a string param",
},
},
}

Expand All @@ -36,6 +41,11 @@ impl KernelModule for RustExample3 {
println!("[3] Parameters:");
println!("[3] my_bool: {}", my_bool.read());
println!("[3] my_i32: {}", my_i32.read());
println!(
"[3] my_str: {}",
my_str.read().expect("Expected valid UTF8 parameter")
);

Ok(RustExample3 {
message: "on the heap!".to_owned(),
})
Expand Down
9 changes: 9 additions & 0 deletions drivers/char/rust_example_4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ module! {
permissions: 0o644,
description: b"Example of i32",
},
my_str: CopyString {
default: "default str val",
permissions: 0o644,
description: b"Example of a string param",
},
},
}

Expand All @@ -36,6 +41,10 @@ impl KernelModule for RustExample4 {
println!("[4] Parameters:");
println!("[4] my_bool: {}", my_bool.read());
println!("[4] my_i32: {}", my_i32.read());
println!(
"[4] my_str: {}",
my_str.read().expect("Expected valid UTF8 parameter")
);
Ok(RustExample4 {
message: "on the heap!".to_owned(),
})
Expand Down
124 changes: 103 additions & 21 deletions rust/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,31 @@
use proc_macro::{token_stream, Delimiter, Group, TokenStream, TokenTree};

fn expect_ident(it: &mut token_stream::IntoIter) -> String {
if let TokenTree::Ident(ident) = it.next().unwrap() {
if let TokenTree::Ident(ident) = it.next().expect("Expected Ident") {
ident.to_string()
} else {
panic!("Expected Ident");
}
}

fn expect_punct(it: &mut token_stream::IntoIter) -> char {
if let TokenTree::Punct(punct) = it.next().unwrap() {
if let TokenTree::Punct(punct) = it.next().expect("Expected Punct") {
punct.as_char()
} else {
panic!("Expected Punct");
}
}

fn expect_literal(it: &mut token_stream::IntoIter) -> String {
if let TokenTree::Literal(literal) = it.next().unwrap() {
if let TokenTree::Literal(literal) = it.next().expect("Expected Literal") {
literal.to_string()
} else {
panic!("Expected Literal");
}
}

fn expect_group(it: &mut token_stream::IntoIter) -> Group {
if let TokenTree::Group(group) = it.next().unwrap() {
if let TokenTree::Group(group) = it.next().expect("Expected Group") {
group
} else {
panic!("Expected Group");
Expand Down Expand Up @@ -76,6 +76,15 @@ fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> Stri
byte_string[2..byte_string.len() - 1].to_string()
}

fn get_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
let string = get_literal(it, expected_name);

assert!(string.starts_with("\""));
assert!(string.ends_with("\""));

string[1..string.len() - 1].to_string()
}

fn __build_modinfo_string_base(
module: &str,
field: &str,
Expand Down Expand Up @@ -160,7 +169,7 @@ fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &
/// The `type` argument should be a type which implements the [`KernelModule`] trait.
/// Also accepts various forms of kernel metadata.
///
/// Example:
/// ## Example
/// ```rust,no_run
/// use kernel::prelude::*;
///
Expand All @@ -170,16 +179,33 @@ fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &
/// author: b"Rust for Linux Contributors",
/// description: b"My very own kernel module!",
/// license: b"GPL v2",
/// params: {},
/// params: {
/// my_i32: i32 {
/// default: 42,
/// permissions: 0o644,
/// description: b"Example of i32",
/// },
/// },
/// }
///
/// struct MyKernelModule;
///
/// impl KernelModule for MyKernelModule {
/// fn init() -> KernelResult<Self> {
/// println!("bool param is: {}", my_bool.read());
/// Ok(MyKernelModule)
/// }
/// }
///
/// ## Suported Parameter Types
/// | Type | `read(&self)` return type |
/// | ------------ | ------------------------------------ |
/// | `i32` | `i32` |
/// | `bool` | `bool` |
/// | `CopyString` | `Result<&str, core::str::Utf8Error>` |
///
/// For `CopyString` the parameter is copied into the buffer of the default value and
/// cannot be longer than the default value.
/// ```
#[proc_macro]
pub fn module(ts: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -215,10 +241,10 @@ pub fn module(ts: TokenStream) -> TokenStream {
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_default = match param_type.as_ref() {
"bool" => get_ident(&mut param_it, "default"),
"CopyString" => get_string(&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");
Expand All @@ -229,6 +255,7 @@ pub fn module(ts: TokenStream) -> TokenStream {
let param_kernel_type = match param_type.as_ref() {
"bool" => "bool",
"i32" => "int",
"CopyString" => "string",
t => panic!("Unrecognized type {}", t),
};

Expand All @@ -244,18 +271,73 @@ pub fn module(ts: TokenStream) -> TokenStream {
&param_name,
&param_description,
));
let param_type_internal = match param_type.as_ref() {
"CopyString" => format!("[u8; {}]", param_default.len() + 1),
_ => param_type.clone(),
};
let param_default = match param_type.as_ref() {
"CopyString" => format!("*b\"{}\0\"", param_default),
_ => param_default,
};
let read_func = match param_type.as_ref() {
"CopyString" => format!(
"
fn read(&self) -> Result<&str, core::str::Utf8Error> {{
unsafe {{
let nul = __{name}_{param_name}_value
.iter()
.position(|&b| b == b'\0')
.unwrap();
core::str::from_utf8(&__{name}_{param_name}_value[0..nul])
}}
}}
",
name = name,
param_name = param_name,
),
_ => format!(
"
fn read(&self) -> {param_type} {{
unsafe {{ __{name}_{param_name}_value }}
}}
",
name = name,
param_name = param_name,
param_type = param_type,
),
};
let kparam_ptr = match param_type.as_ref() {
"CopyString" => format!(
"
__bindgen_anon_1: kernel::bindings::kernel_param__bindgen_ty_1 {{
str_: &kernel::bindings::kparam_string {{
maxlen: unsafe {{ __{name}_{param_name}_value.len() }} as u32 + 1,
string: unsafe {{ (&__{name}_{param_name}_value).as_ptr() }}
as *mut kernel::c_types::c_char,
}} as *const _,
}},
",
name = name,
param_name = param_name,
),
_ => format!(
"
__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_name = param_name,
),
};
params_modinfo.push_str(
&format!(
"
static mut __{name}_{param_name}_value: {param_type} = {param_default};
static mut __{name}_{param_name}_value: {param_type_internal} = {param_default};

struct __{name}_{param_name};

impl __{name}_{param_name} {{
fn read(&self) -> {param_type} {{
unsafe {{ __{name}_{param_name}_value }}
}}
}}
impl __{name}_{param_name} {{ {read_func} }}

const {param_name}: __{name}_{param_name} = __{name}_{param_name};

Expand Down Expand Up @@ -288,17 +370,17 @@ pub fn module(ts: TokenStream) -> TokenStream {
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,
}},
{kparam_ptr}
}});
",
name = name,
param_type = param_type,
param_type_internal = param_type_internal,
read_func = read_func,
param_kernel_type = param_kernel_type,
param_default = param_default,
param_name = param_name,
permissions = param_permissions,
kparam_ptr = kparam_ptr,
)
);
}
Expand Down Expand Up @@ -390,5 +472,5 @@ pub fn module(ts: TokenStream) -> TokenStream {
file = &build_modinfo_string_only_builtin(&name, "file", &file),
params_modinfo = params_modinfo,
initcall_section = ".initcall6.init"
).parse().unwrap()
).parse().expect("Expected parsed token stream")
}