From 23473bbf15c087fd15b854b7838b1cb4b1ccc37e Mon Sep 17 00:00:00 2001 From: adamrk Date: Wed, 20 Jan 2021 22:04:04 +0100 Subject: [PATCH 1/2] Add copy-string param --- .github/workflows/ci.yaml | 8 ++- .github/workflows/qemu-init.sh | 2 +- drivers/char/rust_example.rs | 9 +++ drivers/char/rust_example_2.rs | 9 +++ drivers/char/rust_example_3.rs | 10 +++ drivers/char/rust_example_4.rs | 9 +++ rust/module.rs | 121 +++++++++++++++++++++++++++------ 7 files changed, 146 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d9da916b6dc9da..fae3c4ffc9ca38 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,6 +9,7 @@ jobs: timeout-minutes: 20 strategy: + fail-fast: false matrix: arch: [x86_64, arm64] toolchain: [gcc, clang, llvm] @@ -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 @@ -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 diff --git a/.github/workflows/qemu-init.sh b/.github/workflows/qemu-init.sh index 5d623426b2a165..a0d547af61e7bd 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -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 diff --git a/drivers/char/rust_example.rs b/drivers/char/rust_example.rs index 47c4a7f722ef80..028f6e92eacdf5 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -25,6 +25,11 @@ module! { permissions: 0o644, description: b"Example of i32", }, + my_str: &str { + default: "default str val", + permissions: 0o644, + description: b"Example of a string param", + }, }, } @@ -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(), diff --git a/drivers/char/rust_example_2.rs b/drivers/char/rust_example_2.rs index b5ffb92870d191..9cf5b6e218f0cb 100644 --- a/drivers/char/rust_example_2.rs +++ b/drivers/char/rust_example_2.rs @@ -22,6 +22,11 @@ module! { permissions: 0o644, description: b"Example of i32", }, + my_str: &str { + default: "default str val", + permissions: 0o644, + description: b"Example of a string param", + }, }, } @@ -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(), }) diff --git a/drivers/char/rust_example_3.rs b/drivers/char/rust_example_3.rs index c6b867e527e9a9..00143e2ebb02fe 100644 --- a/drivers/char/rust_example_3.rs +++ b/drivers/char/rust_example_3.rs @@ -22,6 +22,11 @@ module! { permissions: 0o644, description: b"Example of i32", }, + my_str: &str { + default: "default str val", + permissions: 0o644, + description: b"Example of a string param", + }, }, } @@ -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(), }) diff --git a/drivers/char/rust_example_4.rs b/drivers/char/rust_example_4.rs index b658c55b4ba80b..a0e9b530fe8333 100644 --- a/drivers/char/rust_example_4.rs +++ b/drivers/char/rust_example_4.rs @@ -22,6 +22,11 @@ module! { permissions: 0o644, description: b"Example of i32", }, + my_str: &str { + default: "default str val", + permissions: 0o644, + description: b"Example of a string param", + }, }, } @@ -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(), }) diff --git a/rust/module.rs b/rust/module.rs index 051a58bac8da65..14283b0a4b459c 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -5,7 +5,7 @@ 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"); @@ -13,7 +13,7 @@ fn expect_ident(it: &mut token_stream::IntoIter) -> String { } 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"); @@ -21,7 +21,7 @@ fn expect_punct(it: &mut token_stream::IntoIter) -> char { } 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"); @@ -29,7 +29,7 @@ fn expect_literal(it: &mut token_stream::IntoIter) -> String { } 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"); @@ -43,6 +43,20 @@ fn expect_end(it: &mut token_stream::IntoIter) { } } +/// Only expected to parse types that are allowed for module parameters. +fn expect_type(it: &mut token_stream::IntoIter) -> String { + match it.next().expect("Expected Type") { + TokenTree::Punct(punct) => { + assert_eq!(punct.as_char(), '&'); + let ident = expect_ident(it); + assert_eq!(ident, "str"); + "&str".to_string() + } + TokenTree::Ident(ident) => ident.to_string(), + _ => panic!("Expected type"), + } +} + fn get_ident(it: &mut token_stream::IntoIter, expected_name: &str) -> String { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); @@ -60,6 +74,7 @@ fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { } fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> Group { + println!("Getting Group"); assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); let group = expect_group(it); @@ -76,6 +91,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, @@ -208,17 +232,18 @@ pub fn module(ts: TokenStream) -> TokenStream { }; assert_eq!(expect_punct(&mut it), ':'); - let param_type = expect_ident(&mut it); + let param_type = expect_type(&mut it); + println!("Got param type {}", param_type); 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_default = match param_type.as_ref() { + "bool" => get_ident(&mut param_it, "default"), + "&str" => 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"); @@ -229,6 +254,7 @@ pub fn module(ts: TokenStream) -> TokenStream { let param_kernel_type = match param_type.as_ref() { "bool" => "bool", "i32" => "int", + "&str" => "string", t => panic!("Unrecognized type {}", t), }; @@ -244,18 +270,73 @@ pub fn module(ts: TokenStream) -> TokenStream { ¶m_name, ¶m_description, )); + let param_type_internal = match param_type.as_ref() { + "&str" => format!("[u8; {}]", param_default.len() + 1), + _ => param_type.clone(), + }; + let param_default = match param_type.as_ref() { + "&str" => format!("*b\"{}\0\"", param_default), + _ => param_default, + }; + let read_func = match param_type.as_ref() { + "&str" => 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() { + "&str" => 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}; @@ -288,17 +369,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, ) ); } @@ -390,5 +471,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") } From 69b0dcb911e15ad22a9dea68d2fe658d1324ef23 Mon Sep 17 00:00:00 2001 From: adamrk Date: Thu, 21 Jan 2021 23:24:07 +0100 Subject: [PATCH 2/2] cleanup --- drivers/char/rust_example.rs | 2 +- drivers/char/rust_example_2.rs | 2 +- drivers/char/rust_example_3.rs | 2 +- drivers/char/rust_example_4.rs | 2 +- rust/module.rs | 51 +++++++++++++++++----------------- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/drivers/char/rust_example.rs b/drivers/char/rust_example.rs index 028f6e92eacdf5..a9b18c48019aa9 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -25,7 +25,7 @@ module! { permissions: 0o644, description: b"Example of i32", }, - my_str: &str { + my_str: CopyString { default: "default str val", permissions: 0o644, description: b"Example of a string param", diff --git a/drivers/char/rust_example_2.rs b/drivers/char/rust_example_2.rs index 9cf5b6e218f0cb..bfe93fb2d34035 100644 --- a/drivers/char/rust_example_2.rs +++ b/drivers/char/rust_example_2.rs @@ -22,7 +22,7 @@ module! { permissions: 0o644, description: b"Example of i32", }, - my_str: &str { + my_str: CopyString { default: "default str val", permissions: 0o644, description: b"Example of a string param", diff --git a/drivers/char/rust_example_3.rs b/drivers/char/rust_example_3.rs index 00143e2ebb02fe..f942fa9f446910 100644 --- a/drivers/char/rust_example_3.rs +++ b/drivers/char/rust_example_3.rs @@ -22,7 +22,7 @@ module! { permissions: 0o644, description: b"Example of i32", }, - my_str: &str { + my_str: CopyString { default: "default str val", permissions: 0o644, description: b"Example of a string param", diff --git a/drivers/char/rust_example_4.rs b/drivers/char/rust_example_4.rs index a0e9b530fe8333..76c38ce978af94 100644 --- a/drivers/char/rust_example_4.rs +++ b/drivers/char/rust_example_4.rs @@ -22,7 +22,7 @@ module! { permissions: 0o644, description: b"Example of i32", }, - my_str: &str { + my_str: CopyString { default: "default str val", permissions: 0o644, description: b"Example of a string param", diff --git a/rust/module.rs b/rust/module.rs index 14283b0a4b459c..2dce819986cde2 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -43,20 +43,6 @@ fn expect_end(it: &mut token_stream::IntoIter) { } } -/// Only expected to parse types that are allowed for module parameters. -fn expect_type(it: &mut token_stream::IntoIter) -> String { - match it.next().expect("Expected Type") { - TokenTree::Punct(punct) => { - assert_eq!(punct.as_char(), '&'); - let ident = expect_ident(it); - assert_eq!(ident, "str"); - "&str".to_string() - } - TokenTree::Ident(ident) => ident.to_string(), - _ => panic!("Expected type"), - } -} - fn get_ident(it: &mut token_stream::IntoIter, expected_name: &str) -> String { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); @@ -74,7 +60,6 @@ fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { } fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> Group { - println!("Getting Group"); assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); let group = expect_group(it); @@ -184,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::*; /// @@ -194,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 { +/// 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 { @@ -232,8 +234,7 @@ pub fn module(ts: TokenStream) -> TokenStream { }; assert_eq!(expect_punct(&mut it), ':'); - let param_type = expect_type(&mut it); - println!("Got param type {}", param_type); + let param_type = expect_ident(&mut it); let group = expect_group(&mut it); assert_eq!(expect_punct(&mut it), ','); @@ -242,7 +243,7 @@ pub fn module(ts: TokenStream) -> TokenStream { let mut param_it = group.stream().into_iter(); let param_default = match param_type.as_ref() { "bool" => get_ident(&mut param_it, "default"), - "&str" => get_string(&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"); @@ -254,7 +255,7 @@ pub fn module(ts: TokenStream) -> TokenStream { let param_kernel_type = match param_type.as_ref() { "bool" => "bool", "i32" => "int", - "&str" => "string", + "CopyString" => "string", t => panic!("Unrecognized type {}", t), }; @@ -271,15 +272,15 @@ pub fn module(ts: TokenStream) -> TokenStream { ¶m_description, )); let param_type_internal = match param_type.as_ref() { - "&str" => format!("[u8; {}]", param_default.len() + 1), + "CopyString" => format!("[u8; {}]", param_default.len() + 1), _ => param_type.clone(), }; let param_default = match param_type.as_ref() { - "&str" => format!("*b\"{}\0\"", param_default), + "CopyString" => format!("*b\"{}\0\"", param_default), _ => param_default, }; let read_func = match param_type.as_ref() { - "&str" => format!( + "CopyString" => format!( " fn read(&self) -> Result<&str, core::str::Utf8Error> {{ unsafe {{ @@ -306,7 +307,7 @@ pub fn module(ts: TokenStream) -> TokenStream { ), }; let kparam_ptr = match param_type.as_ref() { - "&str" => format!( + "CopyString" => format!( " __bindgen_anon_1: kernel::bindings::kernel_param__bindgen_ty_1 {{ str_: &kernel::bindings::kparam_string {{