Skip to content

Charp #6

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

Closed
wants to merge 5 commits into from
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: str {
default: b"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: str {
default: b"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: str {
default: b"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: str {
default: b"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
96 changes: 75 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 @@ -160,7 +160,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 +170,29 @@ 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!("i32 param is: {}", my_i32.read());
/// Ok(MyKernelModule)
/// }
/// }
///
/// ## Suported Parameter Types
///
/// - `i32`
/// - `bool`
/// - `str` - Corresponds to C `charp` param type. `read` signature is `read(&self) -> Result<&str, core::str::Utf8Error>`.
/// ```
#[proc_macro]
pub fn module(ts: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -215,10 +228,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"),
"str" => get_byte_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 +242,7 @@ pub fn module(ts: TokenStream) -> TokenStream {
let param_kernel_type = match param_type.as_ref() {
"bool" => "bool",
"i32" => "int",
"str" => "charp",
t => panic!("Unrecognized type {}", t),
};

Expand All @@ -244,18 +258,58 @@ pub fn module(ts: TokenStream) -> TokenStream {
&param_name,
&param_description,
));
let param_type_internal = match param_type.as_ref() {
"str" => "*mut u8".to_string(),
_ => param_type.clone(),
};
let param_default = match param_type.as_ref() {
"str" => format!("b\"{}\0\" as *const u8 as *mut u8", param_default),
_ => param_default,
};
let read_func = match param_type.as_ref() {
"str" => format!(
"
fn read(&self) -> Result<&str, core::str::Utf8Error> {{
unsafe {{
let mut count = 0;
while *__{name}_{param_name}_value.add(count) != b'\0' {{
count += 1;
}}
core::str::from_utf8(&core::slice::from_raw_parts(__{name}_{param_name}_value, count))
}}
}}
",
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 = format!(
"
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 +342,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,
}},
__bindgen_anon_1: {kparam}
}});
",
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 = kparam,
)
);
}
Expand Down Expand Up @@ -390,5 +444,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")
}