From 63c2f6b2728142247c06bf973257d0ed857817f9 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 20 Feb 2021 21:42:08 +0100 Subject: [PATCH 01/16] running into allocation issues --- rust/kernel/lib.rs | 2 +- rust/kernel/module_param.rs | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6ef12992f50a81..dd79751e51df43 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -12,7 +12,7 @@ //! do so first instead of bypassing this crate. #![no_std] -#![feature(allocator_api, alloc_error_handler)] +#![feature(allocator_api, alloc_error_handler, const_fn, const_mut_refs, maybe_uninit_ref)] #![deny(clippy::complexity)] #![deny(clippy::correctness)] #![deny(clippy::perf)] diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 776458773c0821..923ddca96059a1 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -300,3 +300,64 @@ make_param_ops!( PARAM_OPS_BOOL, bool ); + +struct ArrayParam { + values: [core::mem::MaybeUninit; N], + used: usize +} + +impl ArrayParam { + const fn new() -> Self { + ArrayParam { + values: [core::mem::MaybeUninit::uninit(); N], + used: 0 + } + } + + const fn push(&mut self, val: T) { + if self.used < N { + self.values[self.used] = core::mem::MaybeUninit::new(val); + self.used += 1; + } + } + + const fn set(&mut self, vals: &[T]) { + self.used = 0; + let mut inx = 0; + while inx < vals.len() { + self.push(vals[inx]); + inx += 1; + } + } + + fn values<'a>(&'a self) -> impl core::iter::Iterator + 'a { + unsafe { + self.values[0..self.used].iter().map(|val| val.assume_init_ref()) + } + } +} + +impl core::fmt::Display for ArrayParam { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + for val in self.values() { + write!(f, "{},", val)?; + } + Ok(()) + } +} + +impl ModuleParam for ArrayParam { + const NOARG_ALLOWED = false; + + fn try_from_param_arg(arg: Option<&[u8])>) -> Option { + if let Some(args) = arg { + for arg in args.split(|b| b = b',') { + let val = T::try_from_param_arg(arg)?; + + + } + } + + } + +} From 0773140a11d5e83f185acca53421700c9a4deb8a Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 26 Feb 2021 09:26:05 +0100 Subject: [PATCH 02/16] working max-length impl --- rust/kernel/module_param.rs | 43 +++++++++++++++---------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 923ddca96059a1..ae613bf9eb7794 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -307,6 +307,14 @@ struct ArrayParam { } impl ArrayParam { + fn values<'a>(&'a self) -> impl core::iter::Iterator + 'a { + unsafe { + self.values[0..self.used].iter().map(|val| val.assume_init_ref()) + } + } +} + +impl ArrayParam { const fn new() -> Self { ArrayParam { values: [core::mem::MaybeUninit::uninit(); N], @@ -320,21 +328,6 @@ impl ArrayParam { self.used += 1; } } - - const fn set(&mut self, vals: &[T]) { - self.used = 0; - let mut inx = 0; - while inx < vals.len() { - self.push(vals[inx]); - inx += 1; - } - } - - fn values<'a>(&'a self) -> impl core::iter::Iterator + 'a { - unsafe { - self.values[0..self.used].iter().map(|val| val.assume_init_ref()) - } - } } impl core::fmt::Display for ArrayParam { @@ -346,18 +339,16 @@ impl core::fmt::Display for ArrayParam ModuleParam for ArrayParam { - const NOARG_ALLOWED = false; - - fn try_from_param_arg(arg: Option<&[u8])>) -> Option { - if let Some(args) = arg { - for arg in args.split(|b| b = b',') { - let val = T::try_from_param_arg(arg)?; - +impl ModuleParam for ArrayParam { + const NOARG_ALLOWED: bool = false; + fn try_from_param_arg(arg: Option<&[u8]>) -> Option { + arg.and_then(|args| { + let mut result = Self::new(); + for arg in args.split(|b| *b == b',') { + result.push(T::try_from_param_arg(Some(arg))?); } - } - + Some(result) + }) } - } From 9d87c7fd71423b7089771b4449bd96596fd368bb Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 26 Feb 2021 20:42:20 +0100 Subject: [PATCH 03/16] in progress --- rust/module.rs | 88 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 19 deletions(-) diff --git a/rust/module.rs b/rust/module.rs index a7925f3ac1961d..ed94b6a6344f4c 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -43,6 +43,34 @@ fn expect_group(it: &mut token_stream::IntoIter) -> Group { } } +#[derive(Clone, PartialEq)] +enum ParamType { + Ident(String), + Array { + vals: String, + max_length: String, + } +} + +fn expect_array_type(it: &mut token_stream::IntoIter) -> ParamType { + let vals = expect_ident(it); + assert_eq!(expect_punct(it), ';'); + let max_length = expect_literal(it); + ParamType::Array { vals, max_length } +} + +fn expect_type(it: &mut token_stream::IntoIter) -> ParamType { + match it.next().unwrap() { + TokenTree::Ident(ident) => ParamType::Ident(ident.to_string()), + TokenTree::Group(group) => { + assert_eq!(group.delimiter(), Delimiter::Bracket); + let mut it = group.stream().into_iter(); + expect_array_type(&mut it) + } + _ => panic!("Expected Param Type") + } +} + fn expect_end(it: &mut token_stream::IntoIter) { if it.next().is_some() { panic!("Expected end"); @@ -177,6 +205,31 @@ fn permissions_are_readonly(perms: &str) -> bool { } } +fn param_ops_path(param_type: &str) -> &'static str { + match param_type { + "bool" => "kernel::module_param::PARAM_OPS_BOOL", + "i8" => "kernel::module_param::PARAM_OPS_I8", + "u8" => "kernel::module_param::PARAM_OPS_U8", + "i16" => "kernel::module_param::PARAM_OPS_I16", + "u16" => "kernel::module_param::PARAM_OPS_U16", + "i32" => "kernel::module_param::PARAM_OPS_I32", + "u32" => "kernel::module_param::PARAM_OPS_U32", + "i64" => "kernel::module_param::PARAM_OPS_I64", + "u64" => "kernel::module_param::PARAM_OPS_U64", + "isize" => "kernel::module_param::PARAM_OPS_ISIZE", + "usize" => "kernel::module_param::PARAM_OPS_USIZE", + "str" => "kernel::bindings::param_ops_charp", + t => panic!("Unrecognized type {}", t) + } +} + +fn kernel_type(param_type: &str) -> String { + match param_type { + "str" => "charp".to_string(), + other => other.to_string(), + } +} + /// Declares a kernel module. /// /// The `type` argument should be a type which implements the [`KernelModule`] @@ -264,6 +317,8 @@ pub fn module(ts: TokenStream) -> TokenStream { let mut params_modinfo = String::new(); + let mut array_types_to_generate = Vec::new(); + loop { let param_name = match it.next() { Some(TokenTree::Ident(ident)) => ident.to_string(), @@ -272,17 +327,20 @@ 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); 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 = 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_default = match param_type { + ParamType::Ident(param_type) => 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"), + } + ParamType::Array{..} => get_group(&mut param_it, "default").to_string(), }; let param_permissions = get_literal(&mut param_it, "permissions"); let param_description = get_byte_string(&mut param_it, "description"); @@ -290,20 +348,12 @@ pub fn module(ts: TokenStream) -> TokenStream { // TODO: more primitive types // TODO: other kinds: arrays, unsafes, etc. - let (param_kernel_type, ops) = match param_type.as_ref() { - "bool" => ("bool", "kernel::module_param::PARAM_OPS_BOOL"), - "i8" => ("i8", "kernel::module_param::PARAM_OPS_I8"), - "u8" => ("u8", "kernel::module_param::PARAM_OPS_U8"), - "i16" => ("i16", "kernel::module_param::PARAM_OPS_I16"), - "u16" => ("u16", "kernel::module_param::PARAM_OPS_U16"), - "i32" => ("i32", "kernel::module_param::PARAM_OPS_I32"), - "u32" => ("u32", "kernel::module_param::PARAM_OPS_U32"), - "i64" => ("i64", "kernel::module_param::PARAM_OPS_I64"), - "u64" => ("u64", "kernel::module_param::PARAM_OPS_U64"), - "isize" => ("isize", "kernel::module_param::PARAM_OPS_ISIZE"), - "usize" => ("usize", "kernel::module_param::PARAM_OPS_USIZE"), - "str" => ("charp", "kernel::bindings::param_ops_charp"), - t => panic!("Unrecognized type {}", t), + let (param_kernel_type, ops): (String, _) = match param_type { + ParamType::Ident(param_type) => (kernel_type(¶m_type), param_ops_path(¶m_type)), + array_type @ ParamType::Array{ vals, max_length } => { + array_types_to_generate.push(array_type); + (format!("rust_array_{}_{}", vals, max_length), generated_array_ops_name(array_type)) + } }; params_modinfo.push_str(&build_modinfo_string_param( From f4881012c4ee436e13c5f55e2790a8c1983abd18 Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 26 Feb 2021 22:30:58 +0100 Subject: [PATCH 04/16] figuring out default --- rust/module.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/rust/module.rs b/rust/module.rs index ed94b6a6344f4c..e81b80105640f8 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -368,16 +368,29 @@ pub fn module(ts: TokenStream) -> TokenStream { ¶m_name, ¶m_description, )); - let param_type_internal = match param_type.as_ref() { - "str" => "*mut kernel::c_types::c_char", - _ => ¶m_type, + let param_type_internal = match param_type { + ParamType::Ident(param_type) => match param_type.as_ref() { + "str" => "*mut kernel::c_types::c_char".to_string(), + other => other.to_string(), + } + ParamType::Array { vals, max_length } => format!( + "kernel::module_param::ArrayParam<{vals}, {max_length}>", + vals = vals, + max_length = max_length + ), }; - let param_default = match param_type.as_ref() { - "str" => format!( - "b\"{}\0\" as *const _ as *mut kernel::c_types::c_char", - param_default + let param_default = match param_type { + ParamType::Ident(param_type) => match param_type.as_ref() { + "str" => format!( + "b\"{}\0\" as *const _ as *mut kernel::c_types::c_char", + param_default + ), + _ => param_default, + } + ParamType::Array { vals, max_length } => format!( + "kernel::module_param::ArrayParam<{vals}, {max_length}>::" + ), - _ => param_default, }; let read_func = match (param_type.as_ref(), permissions_are_readonly(¶m_permissions)) { ("str", false) => format!( From 60c0dd02fd18a6c46c43db9ef7acaebff4d85a9b Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 27 Feb 2021 23:26:03 +0100 Subject: [PATCH 05/16] generated default array --- rust/kernel/module_param.rs | 6 +- rust/module.rs | 139 ++++++++++++++++++++++++++---------- 2 files changed, 104 insertions(+), 41 deletions(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index ae613bf9eb7794..cb5bc11afaf55b 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -301,9 +301,9 @@ make_param_ops!( bool ); -struct ArrayParam { - values: [core::mem::MaybeUninit; N], - used: usize +pub struct ArrayParam { + pub values: [core::mem::MaybeUninit; N], + pub used: usize } impl ArrayParam { diff --git a/rust/module.rs b/rust/module.rs index e81b80105640f8..ef041dde3b4c30 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -11,14 +11,36 @@ 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() { - ident.to_string() +fn try_ident(it: &mut token_stream::IntoIter) -> Option { + if let Some(TokenTree::Ident(ident)) = it.next() { + Some(ident.to_string()) + } else { + None + } +} + +fn try_literal(it: &mut token_stream::IntoIter) -> Option { + if let Some(TokenTree::Literal(literal)) = it.next() { + Some(literal.to_string()) } else { - panic!("Expected Ident"); + None } } +fn try_byte_string(it: &mut token_stream::IntoIter) -> Option { + try_literal(it).and_then(|byte_string| { + if !(byte_string.starts_with("b\"") && byte_string.ends_with('\"')) { + None + } else { + Some(byte_string[2..byte_string.len() - 1].to_string()) + } + }) +} + +fn expect_ident(it: &mut token_stream::IntoIter) -> String { + try_ident(it).expect("Expected Ident") +} + fn expect_punct(it: &mut token_stream::IntoIter) -> char { if let TokenTree::Punct(punct) = it.next().unwrap() { punct.as_char() @@ -28,11 +50,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() { - literal.to_string() - } else { - panic!("Expected Literal"); - } + try_literal(it).expect("Expected Literal") } fn expect_group(it: &mut token_stream::IntoIter) -> Group { @@ -43,19 +61,24 @@ fn expect_group(it: &mut token_stream::IntoIter) -> Group { } } +fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { + try_byte_string(it).expect("Expected byte string") +} + #[derive(Clone, PartialEq)] enum ParamType { Ident(String), Array { vals: String, - max_length: String, + max_length: usize, } } fn expect_array_type(it: &mut token_stream::IntoIter) -> ParamType { let vals = expect_ident(it); assert_eq!(expect_punct(it), ';'); - let max_length = expect_literal(it); + let max_length_str = expect_literal(it); + let max_length = max_length_str.parse::().expect("Expected usize length"); ParamType::Array { vals, max_length } } @@ -102,12 +125,11 @@ fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> 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() + assert_eq!(expect_ident(it), expected_name); + assert_eq!(expect_punct(it), ':'); + let byte_string = expect_byte_string(it); + assert_eq!(expect_punct(it), ','); + byte_string } fn __build_modinfo_string_base( @@ -230,6 +252,67 @@ fn kernel_type(param_type: &str) -> String { } } +fn try_simple_param_val(param_type: &str) -> Box Option> { + match param_type.as_ref() { + "bool" => Box::new(|param_it| try_ident(&mut param_it)), + "str" => Box::new(|param_it| try_byte_string(&mut param_it).map(|s| { + format!("b\"{}\0\" as *const _ as *mut kernel::c_types::c_char", s) + })), + _ => Box::new(|param_it| try_literal(&mut param_it)), + } +} + +fn get_default(param_type: ParamType, param_it: &mut token_stream::IntoIter) -> String { + let try_param_val = match param_type { + ParamType::Ident(param_type) + | ParamType::Array { vals: param_type, max_length: _ } => { + try_simple_param_val(¶m_type) + } + }; + assert_eq!(expect_ident(param_it), "default"); + assert_eq!(expect_punct(param_it), ':'); + let default = match param_type { + ParamType::Ident(param_type) => try_param_val(param_it).expect("Expected default param value"), + ParamType::Array{ vals, max_length } => { + let group = expect_group(param_it); + assert_eq!(group.delimiter(), Delimiter::Bracket); + let mut default_vals = Vec::new(); + let it = group.stream().into_iter(); + + while let Some(default_val) = try_param_val(&mut it) { + default_vals.push(default_val); + match it.next() { + Some(TokenTree::Punct(punct)) => assert_eq!(punct.as_char(), ','), + None => break, + _ => panic!("Expected ',' or end of array default values"), + } + }; + + let uninit_count = max_length - default_vals.len(); + let mut maybe_uninit_defaults = "[".to_string(); + for val in default_vals { + maybe_uninit_defaults.push_str(&format!("core::mem::MaybeUninit::new({}),", val)); + } + for _ in 0..uninit_count { + maybe_uninit_defaults.push_str("core::mem::MaybeUninit::uninit()"); + } + maybe_uninit_defaults.push_str("]"); + format!( + " + kernel::module_param::ArrayParam {{ + values: {maybe_uninit_defaults}, + used: {used}, + }} + ", + maybe_uninit_defaults = maybe_uninit_defaults, + used = default_vals.len(), + ) + } + }; + assert_eq!(expect_punct(param_it), ','); + default +} + /// Declares a kernel module. /// /// The `type` argument should be a type which implements the [`KernelModule`] @@ -334,14 +417,7 @@ pub fn module(ts: TokenStream) -> TokenStream { assert_eq!(group.delimiter(), Delimiter::Brace); let mut param_it = group.stream().into_iter(); - let param_default = match param_type { - ParamType::Ident(param_type) => 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"), - } - ParamType::Array{..} => get_group(&mut param_it, "default").to_string(), - }; + let param_default = get_default(param_type, &mut param_it); let param_permissions = get_literal(&mut param_it, "permissions"); let param_description = get_byte_string(&mut param_it, "description"); expect_end(&mut param_it); @@ -379,19 +455,6 @@ pub fn module(ts: TokenStream) -> TokenStream { max_length = max_length ), }; - let param_default = match param_type { - ParamType::Ident(param_type) => match param_type.as_ref() { - "str" => format!( - "b\"{}\0\" as *const _ as *mut kernel::c_types::c_char", - param_default - ), - _ => param_default, - } - ParamType::Array { vals, max_length } => format!( - "kernel::module_param::ArrayParam<{vals}, {max_length}>::" - - ), - }; let read_func = match (param_type.as_ref(), permissions_are_readonly(¶m_permissions)) { ("str", false) => format!( " From fd537bdb9db6abac24b38fca841678bb92fb4809 Mon Sep 17 00:00:00 2001 From: adamrk Date: Fri, 5 Mar 2021 21:46:35 +0100 Subject: [PATCH 06/16] maybe just generating structs now? --- rust/module.rs | 162 +++++++++++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 65 deletions(-) diff --git a/rust/module.rs b/rust/module.rs index ef041dde3b4c30..7a467691e25034 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -254,19 +254,19 @@ fn kernel_type(param_type: &str) -> String { fn try_simple_param_val(param_type: &str) -> Box Option> { match param_type.as_ref() { - "bool" => Box::new(|param_it| try_ident(&mut param_it)), - "str" => Box::new(|param_it| try_byte_string(&mut param_it).map(|s| { + "bool" => Box::new(|param_it| try_ident(param_it)), + "str" => Box::new(|param_it| try_byte_string(param_it).map(|s| { format!("b\"{}\0\" as *const _ as *mut kernel::c_types::c_char", s) })), - _ => Box::new(|param_it| try_literal(&mut param_it)), + _ => Box::new(|param_it| try_literal(param_it)), } } -fn get_default(param_type: ParamType, param_it: &mut token_stream::IntoIter) -> String { +fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> String { let try_param_val = match param_type { - ParamType::Ident(param_type) - | ParamType::Array { vals: param_type, max_length: _ } => { - try_simple_param_val(¶m_type) + ParamType::Ident(ref param_type) + | ParamType::Array { vals: ref param_type, max_length: _ } => { + try_simple_param_val(param_type) } }; assert_eq!(expect_ident(param_it), "default"); @@ -277,7 +277,7 @@ fn get_default(param_type: ParamType, param_it: &mut token_stream::IntoIter) -> let group = expect_group(param_it); assert_eq!(group.delimiter(), Delimiter::Bracket); let mut default_vals = Vec::new(); - let it = group.stream().into_iter(); + let mut it = group.stream().into_iter(); while let Some(default_val) = try_param_val(&mut it) { default_vals.push(default_val); @@ -290,6 +290,7 @@ fn get_default(param_type: ParamType, param_it: &mut token_stream::IntoIter) -> let uninit_count = max_length - default_vals.len(); let mut maybe_uninit_defaults = "[".to_string(); + let used = default_vals.len(); for val in default_vals { maybe_uninit_defaults.push_str(&format!("core::mem::MaybeUninit::new({}),", val)); } @@ -305,7 +306,7 @@ fn get_default(param_type: ParamType, param_it: &mut token_stream::IntoIter) -> }} ", maybe_uninit_defaults = maybe_uninit_defaults, - used = default_vals.len(), + used = used, ) } }; @@ -313,6 +314,10 @@ fn get_default(param_type: ParamType, param_it: &mut token_stream::IntoIter) -> default } +fn generated_array_ops_name(vals: &str, max_length: usize) -> String { + unimplemented!() +} + /// Declares a kernel module. /// /// The `type` argument should be a type which implements the [`KernelModule`] @@ -417,7 +422,7 @@ pub fn module(ts: TokenStream) -> TokenStream { assert_eq!(group.delimiter(), Delimiter::Brace); let mut param_it = group.stream().into_iter(); - let param_default = get_default(param_type, &mut param_it); + let param_default = get_default(¶m_type, &mut param_it); let param_permissions = get_literal(&mut param_it, "permissions"); let param_description = get_byte_string(&mut param_it, "description"); expect_end(&mut param_it); @@ -425,10 +430,10 @@ pub fn module(ts: TokenStream) -> TokenStream { // TODO: more primitive types // TODO: other kinds: arrays, unsafes, etc. let (param_kernel_type, ops): (String, _) = match param_type { - ParamType::Ident(param_type) => (kernel_type(¶m_type), param_ops_path(¶m_type)), - array_type @ ParamType::Array{ vals, max_length } => { - array_types_to_generate.push(array_type); - (format!("rust_array_{}_{}", vals, max_length), generated_array_ops_name(array_type)) + ParamType::Ident(ref param_type) => (kernel_type(¶m_type), param_ops_path(¶m_type).to_string()), + ParamType::Array{ ref vals, max_length } => { + array_types_to_generate.push(ParamType::Array{ vals: vals.clone(), max_length }); + (format!("rust_array_{}_{}", vals, max_length), generated_array_ops_name(vals, max_length)) } }; @@ -445,67 +450,94 @@ pub fn module(ts: TokenStream) -> TokenStream { ¶m_description, )); let param_type_internal = match param_type { - ParamType::Ident(param_type) => match param_type.as_ref() { + ParamType::Ident(ref param_type) => match param_type.as_ref() { "str" => "*mut kernel::c_types::c_char".to_string(), other => other.to_string(), } - ParamType::Array { vals, max_length } => format!( + ParamType::Array { ref vals, max_length } => format!( "kernel::module_param::ArrayParam<{vals}, {max_length}>", vals = vals, max_length = max_length ), }; - let read_func = match (param_type.as_ref(), permissions_are_readonly(¶m_permissions)) { - ("str", false) => format!( - " - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck [u8] {{ - // SAFETY: The pointer is provided either in `param_default` when building the module, - // or by the kernel through `param_set_charp`. Both will be valid C strings. - // Parameters are locked by `KParamGuard`. - unsafe {{ - kernel::c_types::c_string_bytes(__{name}_{param_name}_value) + let read_func = match param_type { + ParamType::Ident(param_type) => match (param_type.as_ref(), permissions_are_readonly(¶m_permissions)) { + ("str", false) => format!( + " + fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck [u8] {{ + // SAFETY: The pointer is provided either in `param_default` when building the module, + // or by the kernel through `param_set_charp`. Both will be valid C strings. + // Parameters are locked by `KParamGuard`. + unsafe {{ + kernel::c_types::c_string_bytes(__{name}_{param_name}_value) + }} }} - }} - ", - name = name, - param_name = param_name, - ), - ("str", true) => format!( - " - fn read(&self) -> &[u8] {{ - // SAFETY: The pointer is provided either in `param_default` when building the module, - // or by the kernel through `param_set_charp`. Both will be valid C strings. - // Parameters do not need to be locked because they are read only or sysfs is not enabled. - unsafe {{ - kernel::c_types::c_string_bytes(__{name}_{param_name}_value) + ", + name = name, + param_name = param_name, + ), + ("str", true) => format!( + " + fn read(&self) -> &[u8] {{ + // SAFETY: The pointer is provided either in `param_default` when building the module, + // or by the kernel through `param_set_charp`. Both will be valid C strings. + // Parameters do not need to be locked because they are read only or sysfs is not enabled. + unsafe {{ + kernel::c_types::c_string_bytes(__{name}_{param_name}_value) + }} }} - }} - ", - name = name, - param_name = param_name, - ), - (_, false) => format!( - " - // SAFETY: Parameters are locked by `KParamGuard`. - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck {param_type_internal} {{ - unsafe {{ &__{name}_{param_name}_value }} - }} - ", - name = name, - param_name = param_name, - param_type_internal = param_type_internal, - ), - (_, true) => format!( - " - // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. - fn read(&self) -> &{param_type_internal} {{ - unsafe {{ &__{name}_{param_name}_value }} - }} - ", - name = name, - param_name = param_name, - param_type_internal = param_type_internal, - ), + ", + name = name, + param_name = param_name, + ), + (_, false) => format!( + " + // SAFETY: Parameters are locked by `KParamGuard`. + fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck {param_type_internal} {{ + unsafe {{ &__{name}_{param_name}_value }} + }} + ", + name = name, + param_name = param_name, + param_type_internal = param_type_internal, + ), + (_, true) => format!( + " + // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. + fn read(&self) -> &{param_type_internal} {{ + unsafe {{ &__{name}_{param_name}_value }} + }} + ", + name = name, + param_name = param_name, + param_type_internal = param_type_internal, + ), + } + ParamType::Array{ ref vals, max_length: _ } => { + if permissions_are_readonly(¶m_permissions) { + format!( + " + fn read(&'a self) -> impl core::iter::Iterator + 'a {{ + unsafe {{ &__{name}_{param_name}_value.values() }} + }} + ", + vals = vals, + name = name, + param_name = param_name, + ) + } else { + format!( + " + fn read(&self, lock: &'lck kernel::KParamGuard) -> impl core::iter::Iterator + 'lck {{ + unsafe {{ &__{name}_{param_name}_value.values() }} + }} + ", + vals = vals, + name = name, + param_name = param_name, + ) + } + } }; let kparam = format!( " From 6a54f0ec868a95a37ce812da0439083122c2f21b Mon Sep 17 00:00:00 2001 From: adamrk Date: Mon, 8 Mar 2021 22:52:37 +0100 Subject: [PATCH 07/16] working rust example --- drivers/char/rust_example.rs | 7 ++++++ rust/kernel/module_param.rs | 49 +++++++++++++++++++++++++----------- rust/module.rs | 41 ++++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 20 deletions(-) diff --git a/drivers/char/rust_example.rs b/drivers/char/rust_example.rs index 91ba6bc353a987..ce474c3592c4c5 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -10,6 +10,7 @@ use alloc::boxed::Box; use core::pin::Pin; use kernel::prelude::*; use kernel::{chrdev, cstr, file_operations::FileOperations, miscdev}; +use kernel::make_param_ops; module! { type: RustExample, @@ -38,6 +39,11 @@ module! { permissions: 0o644, description: b"Example of usize", }, + my_array: [i32; 2] { + default: [0, 1], + permissions: 0, + description: b"Example of array", + }, }, } @@ -72,6 +78,7 @@ impl KernelModule for RustExample { core::str::from_utf8(my_str.read(&lock))? ); println!(" my_usize: {}", my_usize.read(&lock)); + println!(" my_array: {:?}", my_array.read()); } // Including this large variable on the stack will trigger diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index cb5bc11afaf55b..a70aba77272349 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -184,14 +184,33 @@ macro_rules! impl_module_param { /// ); /// ``` macro_rules! make_param_ops { - ($ops:ident, $ty:ident) => { + ($ops:ident, $ty:ty) => { make_param_ops!( #[doc=""] $ops, $ty ); }; - ($(#[$meta:meta])* $ops:ident, $ty:ident) => { + ($(#[$meta:meta])* $ops:ident, $ty:ty) => { + $(#[$meta])* + /// + /// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h) + /// struct generated by [`make_param_ops`]. + pub static $ops: kernel::bindings::kernel_param_ops = kernel::bindings::kernel_param_ops { + flags: if <$ty as kernel::module_param::ModuleParam>::NOARG_ALLOWED { + kernel::bindings::KERNEL_PARAM_OPS_FL_NOARG + } else { + 0 + }, + set: Some(<$ty as kernel::module_param::ModuleParam>::set_param), + get: Some(<$ty as kernel::module_param::ModuleParam>::get_param), + free: Some(<$ty as kernel::module_param::ModuleParam>::free), + }; + }; +} + +macro_rules! internal_make_param_ops { + ($(#[$meta:meta])* $ops:ident, $ty:ty) => { $(#[$meta])* /// /// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h) @@ -220,61 +239,61 @@ impl_module_param!(u64); impl_module_param!(isize); impl_module_param!(usize); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i8`]. PARAM_OPS_I8, i8 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u8`]. PARAM_OPS_U8, u8 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i16`]. PARAM_OPS_I16, i16 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u16`]. PARAM_OPS_U16, u16 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i32`]. PARAM_OPS_I32, i32 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u32`]. PARAM_OPS_U32, u32 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i64`]. PARAM_OPS_I64, i64 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u64`]. PARAM_OPS_U64, u64 ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`isize`]. PARAM_OPS_ISIZE, isize ); -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`usize`]. PARAM_OPS_USIZE, @@ -294,7 +313,7 @@ impl ModuleParam for bool { } } -make_param_ops!( +internal_make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`bool`]. PARAM_OPS_BOOL, @@ -307,9 +326,9 @@ pub struct ArrayParam { } impl ArrayParam { - fn values<'a>(&'a self) -> impl core::iter::Iterator + 'a { + pub fn values(&self) -> &[T] { unsafe { - self.values[0..self.used].iter().map(|val| val.assume_init_ref()) + core::mem::transmute::<&[core::mem::MaybeUninit], &[T]>(&self.values[0..self.used]) } } } diff --git a/rust/module.rs b/rust/module.rs index 7a467691e25034..5bc2ae0fc1b661 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -65,7 +65,7 @@ fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { try_byte_string(it).expect("Expected byte string") } -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] enum ParamType { Ident(String), Array { @@ -315,7 +315,11 @@ fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> } fn generated_array_ops_name(vals: &str, max_length: usize) -> String { - unimplemented!() + format!( + "__generated_array_ops_{vals}_{max_length}", + vals = vals, + max_length = max_length + ) } /// Declares a kernel module. @@ -407,15 +411,19 @@ pub fn module(ts: TokenStream) -> TokenStream { let mut array_types_to_generate = Vec::new(); + println!("Starting loop"); loop { + println!("Getting param"); let param_name = match it.next() { Some(TokenTree::Ident(ident)) => ident.to_string(), Some(_) => panic!("Expected Ident or end"), None => break, }; + println!("Got name {}", param_name); assert_eq!(expect_punct(&mut it), ':'); let param_type = expect_type(&mut it); + println!("Got name {:?}", param_type); let group = expect_group(&mut it); assert_eq!(expect_punct(&mut it), ','); @@ -432,7 +440,7 @@ pub fn module(ts: TokenStream) -> TokenStream { let (param_kernel_type, ops): (String, _) = match param_type { ParamType::Ident(ref param_type) => (kernel_type(¶m_type), param_ops_path(¶m_type).to_string()), ParamType::Array{ ref vals, max_length } => { - array_types_to_generate.push(ParamType::Array{ vals: vals.clone(), max_length }); + array_types_to_generate.push((vals.clone(), max_length)); (format!("rust_array_{}_{}", vals, max_length), generated_array_ops_name(vals, max_length)) } }; @@ -517,7 +525,7 @@ pub fn module(ts: TokenStream) -> TokenStream { if permissions_are_readonly(¶m_permissions) { format!( " - fn read(&'a self) -> impl core::iter::Iterator + 'a {{ + fn read<'a>(&'a self) -> &'a [{vals}] {{ unsafe {{ &__{name}_{param_name}_value.values() }} }} ", @@ -528,7 +536,7 @@ pub fn module(ts: TokenStream) -> TokenStream { } else { format!( " - fn read(&self, lock: &'lck kernel::KParamGuard) -> impl core::iter::Iterator + 'lck {{ + fn read(&self, lock: &'lck kernel::KParamGuard) -> &'lck [{vals}] {{ unsafe {{ &__{name}_{param_name}_value.values() }} }} ", @@ -606,6 +614,26 @@ pub fn module(ts: TokenStream) -> TokenStream { ); } + let mut generated_array_types = String::new(); + + for (vals, max_length) in array_types_to_generate { + let ops_name = generated_array_ops_name(&vals, max_length); + generated_array_types.push_str( + &format!( + " + /// Some doc + make_param_ops!( + {ops_name}, + kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}> + ); + ", + ops_name = ops_name, + vals = vals, + max_length = max_length, + ) + ); + } + let file = std::env::var("RUST_MODFILE").unwrap(); format!( @@ -690,6 +718,8 @@ pub fn module(ts: TokenStream) -> TokenStream { {file} {params_modinfo} + + {generated_array_types} ", type_ = type_, name = name, @@ -698,6 +728,7 @@ pub fn module(ts: TokenStream) -> TokenStream { license = &build_modinfo_string(&name, "license", &license), file = &build_modinfo_string_only_builtin(&name, "file", &file), params_modinfo = params_modinfo, + generated_array_types = generated_array_types, initcall_section = ".initcall6.init" ).parse().unwrap() } From 994c6978e30bcfb77a516b927e3277a7ebc8753f Mon Sep 17 00:00:00 2001 From: adamrk Date: Mon, 8 Mar 2021 23:36:04 +0100 Subject: [PATCH 08/16] add read method to ModuleParam trait --- rust/kernel/module_param.rs | 19 ++++++++++ rust/module.rs | 70 +++++++++++++------------------------ 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index a70aba77272349..4999c4a365efb7 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -15,6 +15,8 @@ use core::fmt::Write; /// /// [`PAGE_SIZE`]: `crate::PAGE_SIZE` pub trait ModuleParam: core::fmt::Display + core::marker::Sized { + type Read: ?Sized; + /// Whether the parameter is allowed to be set without an argument. /// /// Setting this to `true` allows the parameter to be passed without an @@ -29,6 +31,8 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// to always be `Some(_)`. fn try_from_param_arg(arg: Option<&[u8]>) -> Option; + unsafe fn read(&self) -> &Self::Read; + /// Set the module parameter from a string. /// /// Used to set the parameter value when loading the module or when set @@ -161,6 +165,7 @@ impl_parse_int!(usize); macro_rules! impl_module_param { ($ty:ident) => { impl ModuleParam for $ty { + type Read = $ty; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&[u8]>) -> Option { @@ -168,6 +173,10 @@ macro_rules! impl_module_param { let utf8 = core::str::from_utf8(bytes).ok()?; <$ty as crate::module_param::ParseInt>::from_str(utf8) } + + unsafe fn read(&self) -> &Self::Read { + self + } } }; } @@ -301,6 +310,7 @@ internal_make_param_ops!( ); impl ModuleParam for bool { + type Read = bool; const NOARG_ALLOWED: bool = true; fn try_from_param_arg(arg: Option<&[u8]>) -> Option { @@ -311,6 +321,10 @@ impl ModuleParam for bool { _ => None, } } + + unsafe fn read(&self) -> &Self::Read { + self + } } internal_make_param_ops!( @@ -359,6 +373,7 @@ impl core::fmt::Display for ArrayParam ModuleParam for ArrayParam { + type Read = [T]; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&[u8]>) -> Option { @@ -370,4 +385,8 @@ impl ModuleParam for Some(result) }) } + + unsafe fn read(&self) -> &Self::Read { + self.values() + } } diff --git a/rust/module.rs b/rust/module.rs index 5bc2ae0fc1b661..e58061f13effcc 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -468,14 +468,14 @@ pub fn module(ts: TokenStream) -> TokenStream { max_length = max_length ), }; - let read_func = match param_type { - ParamType::Ident(param_type) => match (param_type.as_ref(), permissions_are_readonly(¶m_permissions)) { - ("str", false) => format!( + let read_func = if param_type == ParamType::Ident("str".to_string()) { + if permissions_are_readonly(¶m_permissions) { + format!( " - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck [u8] {{ + fn read(&self) -> &[u8] {{ // SAFETY: The pointer is provided either in `param_default` when building the module, // or by the kernel through `param_set_charp`. Both will be valid C strings. - // Parameters are locked by `KParamGuard`. + // Parameters do not need to be locked because they are read only or sysfs is not enabled. unsafe {{ kernel::c_types::c_string_bytes(__{name}_{param_name}_value) }} @@ -483,13 +483,14 @@ pub fn module(ts: TokenStream) -> TokenStream { ", name = name, param_name = param_name, - ), - ("str", true) => format!( + ) + } else { + format!( " - fn read(&self) -> &[u8] {{ + fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck [u8] {{ // SAFETY: The pointer is provided either in `param_default` when building the module, // or by the kernel through `param_set_charp`. Both will be valid C strings. - // Parameters do not need to be locked because they are read only or sysfs is not enabled. + // Parameters are locked by `KParamGuard`. unsafe {{ kernel::c_types::c_string_bytes(__{name}_{param_name}_value) }} @@ -497,54 +498,33 @@ pub fn module(ts: TokenStream) -> TokenStream { ", name = name, param_name = param_name, - ), - (_, false) => format!( + ) + } + } else { + if permissions_are_readonly(¶m_permissions) { + format!( " - // SAFETY: Parameters are locked by `KParamGuard`. - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck {param_type_internal} {{ - unsafe {{ &__{name}_{param_name}_value }} + // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. + fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} }} ", name = name, param_name = param_name, param_type_internal = param_type_internal, - ), - (_, true) => format!( + ) + } else { + format!( " - // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. - fn read(&self) -> &{param_type_internal} {{ - unsafe {{ &__{name}_{param_name}_value }} + // SAFETY: Parameters are locked by `KParamGuard`. + fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} }} ", name = name, param_name = param_name, param_type_internal = param_type_internal, - ), - } - ParamType::Array{ ref vals, max_length: _ } => { - if permissions_are_readonly(¶m_permissions) { - format!( - " - fn read<'a>(&'a self) -> &'a [{vals}] {{ - unsafe {{ &__{name}_{param_name}_value.values() }} - }} - ", - vals = vals, - name = name, - param_name = param_name, - ) - } else { - format!( - " - fn read(&self, lock: &'lck kernel::KParamGuard) -> &'lck [{vals}] {{ - unsafe {{ &__{name}_{param_name}_value.values() }} - }} - ", - vals = vals, - name = name, - param_name = param_name, - ) - } + ) } }; let kparam = format!( From bc76d8672b8393bbf3b4aadcc97fdd7cfa372f67 Mon Sep 17 00:00:00 2001 From: adamrk Date: Mon, 8 Mar 2021 23:56:58 +0100 Subject: [PATCH 09/16] cleanup --- rust/module.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/module.rs b/rust/module.rs index e58061f13effcc..ebb6c506eb09fa 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -253,7 +253,7 @@ fn kernel_type(param_type: &str) -> String { } fn try_simple_param_val(param_type: &str) -> Box Option> { - match param_type.as_ref() { + match param_type { "bool" => Box::new(|param_it| try_ident(param_it)), "str" => Box::new(|param_it| try_byte_string(param_it).map(|s| { format!("b\"{}\0\" as *const _ as *mut kernel::c_types::c_char", s) @@ -272,8 +272,8 @@ fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> assert_eq!(expect_ident(param_it), "default"); assert_eq!(expect_punct(param_it), ':'); let default = match param_type { - ParamType::Ident(param_type) => try_param_val(param_it).expect("Expected default param value"), - ParamType::Array{ vals, max_length } => { + ParamType::Ident(_) => try_param_val(param_it).expect("Expected default param value"), + ParamType::Array{ vals:_, max_length } => { let group = expect_group(param_it); assert_eq!(group.delimiter(), Delimiter::Bracket); let mut default_vals = Vec::new(); @@ -297,7 +297,7 @@ fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> for _ in 0..uninit_count { maybe_uninit_defaults.push_str("core::mem::MaybeUninit::uninit()"); } - maybe_uninit_defaults.push_str("]"); + maybe_uninit_defaults.push(']'); format!( " kernel::module_param::ArrayParam {{ @@ -441,7 +441,7 @@ pub fn module(ts: TokenStream) -> TokenStream { ParamType::Ident(ref param_type) => (kernel_type(¶m_type), param_ops_path(¶m_type).to_string()), ParamType::Array{ ref vals, max_length } => { array_types_to_generate.push((vals.clone(), max_length)); - (format!("rust_array_{}_{}", vals, max_length), generated_array_ops_name(vals, max_length)) + (format!("__rust_array_param_{}_{}", vals, max_length), generated_array_ops_name(vals, max_length)) } }; From b77591c484a0fb97fbb311ef910ebed770edd915 Mon Sep 17 00:00:00 2001 From: adamrk Date: Tue, 9 Mar 2021 09:51:17 +0100 Subject: [PATCH 10/16] Use $crate in make_param_ops macro --- rust/kernel/module_param.rs | 53 ++++++++++++------------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 4999c4a365efb7..d6588bab7648e3 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -205,34 +205,15 @@ macro_rules! make_param_ops { /// /// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// struct generated by [`make_param_ops`]. - pub static $ops: kernel::bindings::kernel_param_ops = kernel::bindings::kernel_param_ops { - flags: if <$ty as kernel::module_param::ModuleParam>::NOARG_ALLOWED { - kernel::bindings::KERNEL_PARAM_OPS_FL_NOARG + pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops { + flags: if <$ty as $crate::module_param::ModuleParam>::NOARG_ALLOWED { + $crate::bindings::KERNEL_PARAM_OPS_FL_NOARG } else { 0 }, - set: Some(<$ty as kernel::module_param::ModuleParam>::set_param), - get: Some(<$ty as kernel::module_param::ModuleParam>::get_param), - free: Some(<$ty as kernel::module_param::ModuleParam>::free), - }; - }; -} - -macro_rules! internal_make_param_ops { - ($(#[$meta:meta])* $ops:ident, $ty:ty) => { - $(#[$meta])* - /// - /// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h) - /// struct generated by [`make_param_ops`]. - pub static $ops: crate::bindings::kernel_param_ops = crate::bindings::kernel_param_ops { - flags: if <$ty as crate::module_param::ModuleParam>::NOARG_ALLOWED { - crate::bindings::KERNEL_PARAM_OPS_FL_NOARG - } else { - 0 - }, - set: Some(<$ty as crate::module_param::ModuleParam>::set_param), - get: Some(<$ty as crate::module_param::ModuleParam>::get_param), - free: Some(<$ty as crate::module_param::ModuleParam>::free), + set: Some(<$ty as $crate::module_param::ModuleParam>::set_param), + get: Some(<$ty as $crate::module_param::ModuleParam>::get_param), + free: Some(<$ty as $crate::module_param::ModuleParam>::free), }; }; } @@ -248,61 +229,61 @@ impl_module_param!(u64); impl_module_param!(isize); impl_module_param!(usize); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i8`]. PARAM_OPS_I8, i8 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u8`]. PARAM_OPS_U8, u8 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i16`]. PARAM_OPS_I16, i16 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u16`]. PARAM_OPS_U16, u16 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i32`]. PARAM_OPS_I32, i32 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u32`]. PARAM_OPS_U32, u32 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`i64`]. PARAM_OPS_I64, i64 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`u64`]. PARAM_OPS_U64, u64 ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`isize`]. PARAM_OPS_ISIZE, isize ); -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`usize`]. PARAM_OPS_USIZE, @@ -327,7 +308,7 @@ impl ModuleParam for bool { } } -internal_make_param_ops!( +make_param_ops!( /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) /// for [`bool`]. PARAM_OPS_BOOL, From c1986fbb3b6d4e3c9481f35f2376756960aad0eb Mon Sep 17 00:00:00 2001 From: adamrk Date: Tue, 9 Mar 2021 10:23:21 +0100 Subject: [PATCH 11/16] create StringParam type --- rust/kernel/module_param.rs | 64 ++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index d6588bab7648e3..c182d9cc6b2325 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -29,7 +29,7 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// `arg == None` indicates that the parameter was passed without an /// argument. If `NOARG_ALLOWED` is set to `false` then `arg` is guaranteed /// to always be `Some(_)`. - fn try_from_param_arg(arg: Option<&[u8]>) -> Option; + fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option; unsafe fn read(&self) -> &Self::Read; @@ -168,7 +168,7 @@ macro_rules! impl_module_param { type Read = $ty; const NOARG_ALLOWED: bool = false; - fn try_from_param_arg(arg: Option<&[u8]>) -> Option { + fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { let bytes = arg?; let utf8 = core::str::from_utf8(bytes).ok()?; <$ty as crate::module_param::ParseInt>::from_str(utf8) @@ -294,7 +294,7 @@ impl ModuleParam for bool { type Read = bool; const NOARG_ALLOWED: bool = true; - fn try_from_param_arg(arg: Option<&[u8]>) -> Option { + fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { match arg { None => Some(true), Some(b"y") | Some(b"Y") | Some(b"1") | Some(b"true") => Some(true), @@ -357,7 +357,7 @@ impl ModuleParam for type Read = [T]; const NOARG_ALLOWED: bool = false; - fn try_from_param_arg(arg: Option<&[u8]>) -> Option { + fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { arg.and_then(|args| { let mut result = Self::new(); for arg in args.split(|b| *b == b',') { @@ -371,3 +371,59 @@ impl ModuleParam for self.values() } } + +enum StringParam { + Ref(&'static [u8]), + Owned(alloc::vec::Vec), +} + +impl StringParam { + fn bytes(&self) -> &[u8] { + match self { + StringParam::Ref(bytes) => *bytes, + StringParam::Owned(vec) => &vec[..], + } + } +} + +impl core::fmt::Display for StringParam { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let bytes = self.bytes(); + match core::str::from_utf8(bytes) { + Ok(utf8) => write!(f, "{}", utf8), + Err(_) => write!(f, "{:?}", bytes), + } + } +} + +impl ModuleParam for StringParam { + type Read = [u8]; + const NOARG_ALLOWED: bool = false; + + fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { + // TODO: unsafe doc + let slab_available = unsafe { + crate::bindings::slab_is_available() + }; + arg.map(|arg| { + if slab_available { + let mut vec = alloc::vec::Vec::new(); + vec.extend_from_slice(arg); + StringParam::Owned(vec) + } else { + StringParam::Ref(arg) + } + }) + } + + unsafe fn read(&self) -> &Self::Read { + self.bytes() + } +} + +make_param_ops!( + /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h) + /// for [`StringParam`]. + PARAM_OPS_STR, + StringParam +); From e316d8469700a7b5a5d16c29baf41a9552b32f12 Mon Sep 17 00:00:00 2001 From: adamrk Date: Tue, 9 Mar 2021 10:39:48 +0100 Subject: [PATCH 12/16] Use StringParam in module --- rust/kernel/module_param.rs | 2 +- rust/module.rs | 95 +++++++++++-------------------------- 2 files changed, 28 insertions(+), 69 deletions(-) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index c182d9cc6b2325..64d23784845acc 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -372,7 +372,7 @@ impl ModuleParam for } } -enum StringParam { +pub enum StringParam { Ref(&'static [u8]), Owned(alloc::vec::Vec), } diff --git a/rust/module.rs b/rust/module.rs index ebb6c506eb09fa..028cafa0f6145e 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -240,23 +240,16 @@ fn param_ops_path(param_type: &str) -> &'static str { "u64" => "kernel::module_param::PARAM_OPS_U64", "isize" => "kernel::module_param::PARAM_OPS_ISIZE", "usize" => "kernel::module_param::PARAM_OPS_USIZE", - "str" => "kernel::bindings::param_ops_charp", + "str" => "kernel::module_param::PARAM_OPS_STR", t => panic!("Unrecognized type {}", t) } } -fn kernel_type(param_type: &str) -> String { - match param_type { - "str" => "charp".to_string(), - other => other.to_string(), - } -} - fn try_simple_param_val(param_type: &str) -> Box Option> { match param_type { "bool" => Box::new(|param_it| try_ident(param_it)), "str" => Box::new(|param_it| try_byte_string(param_it).map(|s| { - format!("b\"{}\0\" as *const _ as *mut kernel::c_types::c_char", s) + format!("kernel::module_param::StringParam::Ref(b\"{}\")", s) })), _ => Box::new(|param_it| try_literal(param_it)), } @@ -438,7 +431,7 @@ pub fn module(ts: TokenStream) -> TokenStream { // TODO: more primitive types // TODO: other kinds: arrays, unsafes, etc. let (param_kernel_type, ops): (String, _) = match param_type { - ParamType::Ident(ref param_type) => (kernel_type(¶m_type), param_ops_path(¶m_type).to_string()), + ParamType::Ident(ref param_type) => (param_type.to_string(), param_ops_path(¶m_type).to_string()), ParamType::Array{ ref vals, max_length } => { array_types_to_generate.push((vals.clone(), max_length)); (format!("__rust_array_param_{}_{}", vals, max_length), generated_array_ops_name(vals, max_length)) @@ -459,7 +452,7 @@ pub fn module(ts: TokenStream) -> TokenStream { )); let param_type_internal = match param_type { ParamType::Ident(ref param_type) => match param_type.as_ref() { - "str" => "*mut kernel::c_types::c_char".to_string(), + "str" => "kernel::module_param::StringParam".to_string(), other => other.to_string(), } ParamType::Array { ref vals, max_length } => format!( @@ -468,64 +461,30 @@ pub fn module(ts: TokenStream) -> TokenStream { max_length = max_length ), }; - let read_func = if param_type == ParamType::Ident("str".to_string()) { - if permissions_are_readonly(¶m_permissions) { - format!( - " - fn read(&self) -> &[u8] {{ - // SAFETY: The pointer is provided either in `param_default` when building the module, - // or by the kernel through `param_set_charp`. Both will be valid C strings. - // Parameters do not need to be locked because they are read only or sysfs is not enabled. - unsafe {{ - kernel::c_types::c_string_bytes(__{name}_{param_name}_value) - }} - }} - ", - name = name, - param_name = param_name, - ) - } else { - format!( - " - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck [u8] {{ - // SAFETY: The pointer is provided either in `param_default` when building the module, - // or by the kernel through `param_set_charp`. Both will be valid C strings. - // Parameters are locked by `KParamGuard`. - unsafe {{ - kernel::c_types::c_string_bytes(__{name}_{param_name}_value) - }} - }} - ", - name = name, - param_name = param_name, - ) - } + let read_func = if permissions_are_readonly(¶m_permissions) { + format!( + " + // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. + fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} + }} + ", + name = name, + param_name = param_name, + param_type_internal = param_type_internal, + ) } else { - if permissions_are_readonly(¶m_permissions) { - format!( - " - // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. - fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ - unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} - }} - ", - name = name, - param_name = param_name, - param_type_internal = param_type_internal, - ) - } else { - format!( - " - // SAFETY: Parameters are locked by `KParamGuard`. - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ - unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} - }} - ", - name = name, - param_name = param_name, - param_type_internal = param_type_internal, - ) - } + format!( + " + // SAFETY: Parameters are locked by `KParamGuard`. + fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} + }} + ", + name = name, + param_name = param_name, + param_type_internal = param_type_internal, + ) }; let kparam = format!( " From a50a4750a7cafc89a4b67e987d8159312bb1a304 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 13 Mar 2021 20:40:07 +0100 Subject: [PATCH 13/16] cleanup --- drivers/char/rust_example.rs | 4 +- rust/kernel/lib.rs | 8 ++- rust/kernel/module_param.rs | 85 +++++++++++++++++++------ rust/module.rs | 118 ++++++++++++++++++----------------- 4 files changed, 135 insertions(+), 80 deletions(-) diff --git a/drivers/char/rust_example.rs b/drivers/char/rust_example.rs index ce474c3592c4c5..1a33189fd1be61 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -8,14 +8,14 @@ use alloc::boxed::Box; use core::pin::Pin; +use kernel::make_param_ops; use kernel::prelude::*; use kernel::{chrdev, cstr, file_operations::FileOperations, miscdev}; -use kernel::make_param_ops; module! { type: RustExample, name: b"rust_example", - author: b"Rust for Linux Contributors", + author: b"Rust for Linux Contributors-", description: b"An example kernel module written in Rust", license: b"GPL v2", params: { diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index dd79751e51df43..0c35854429fca5 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -12,7 +12,13 @@ //! do so first instead of bypassing this crate. #![no_std] -#![feature(allocator_api, alloc_error_handler, const_fn, const_mut_refs, maybe_uninit_ref)] +#![feature( + allocator_api, + alloc_error_handler, + const_fn, + const_mut_refs, + maybe_uninit_ref +)] #![deny(clippy::complexity)] #![deny(clippy::correctness)] #![deny(clippy::perf)] diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index 64d23784845acc..4ef99371c0f335 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -15,7 +15,12 @@ use core::fmt::Write; /// /// [`PAGE_SIZE`]: `crate::PAGE_SIZE` pub trait ModuleParam: core::fmt::Display + core::marker::Sized { - type Read: ?Sized; + /// The `ModuleParam` will be used by the kernel module through this type. + /// + /// This may differ from `Self` if, for example, `Self` needs to track + /// ownership without exposing it or allocate extra space for other possible + /// parameter values. See [`StringParam`] or [`ArrayParam`] for examples. + type Value: ?Sized; /// Whether the parameter is allowed to be set without an argument. /// @@ -31,7 +36,11 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// to always be `Some(_)`. fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option; - unsafe fn read(&self) -> &Self::Read; + /// Get the current value of the parameter for use in the kernel module. + /// + /// This function should not be used directly. Instead use the wrapper + /// `read` which will be generated by [`module::module`]. + fn value(&self) -> &Self::Value; /// Set the module parameter from a string. /// @@ -165,7 +174,7 @@ impl_parse_int!(usize); macro_rules! impl_module_param { ($ty:ident) => { impl ModuleParam for $ty { - type Read = $ty; + type Value = $ty; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { @@ -174,7 +183,7 @@ macro_rules! impl_module_param { <$ty as crate::module_param::ParseInt>::from_str(utf8) } - unsafe fn read(&self) -> &Self::Read { + fn value(&self) -> &Self::Value { self } } @@ -291,7 +300,7 @@ make_param_ops!( ); impl ModuleParam for bool { - type Read = bool; + type Value = bool; const NOARG_ALLOWED: bool = true; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { @@ -303,7 +312,7 @@ impl ModuleParam for bool { } } - unsafe fn read(&self) -> &Self::Read { + fn value(&self) -> &Self::Value { self } } @@ -315,33 +324,54 @@ make_param_ops!( bool ); +/// An array of at __most__ `N` values. pub struct ArrayParam { - pub values: [core::mem::MaybeUninit; N], - pub used: usize + values: [core::mem::MaybeUninit; N], + used: usize, } impl ArrayParam { - pub fn values(&self) -> &[T] { + fn values(&self) -> &[T] { + // SAFETY: `self` can only be generated and modified by the methods + // [`new`], and [`push`]. These maintain the invariant that the first + // `self.used` elements of `self.values` are initialized. unsafe { - core::mem::transmute::<&[core::mem::MaybeUninit], &[T]>(&self.values[0..self.used]) + &*(&self.values[0..self.used] as *const [core::mem::MaybeUninit] as *const [T]) } } } impl ArrayParam { const fn new() -> Self { + // Invariant: + // The first `self.used` elements of `self.values` are initialized. ArrayParam { values: [core::mem::MaybeUninit::uninit(); N], - used: 0 + used: 0, } } const fn push(&mut self, val: T) { if self.used < N { + // Invariant: + // The first `self.used` elements of `self.values` are initialized. self.values[self.used] = core::mem::MaybeUninit::new(val); self.used += 1; } } + + /// Create an instance of `ArrayParam` initialized with `vals`. + /// + /// This function is only meant to be used in the [`module::module`] macro. + pub const fn create(vals: &[T]) -> Self { + let mut result = ArrayParam::new(); + let mut i = 0; + while i < vals.len() { + result.push(vals[i]); + i += 1; + } + result + } } impl core::fmt::Display for ArrayParam { @@ -353,8 +383,10 @@ impl core::fmt::Display for ArrayParam ModuleParam for ArrayParam { - type Read = [T]; +impl ModuleParam + for ArrayParam +{ + type Value = [T]; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { @@ -367,13 +399,28 @@ impl ModuleParam for }) } - unsafe fn read(&self) -> &Self::Read { + fn value(&self) -> &Self::Value { self.values() } } +/// A C-style string parameter. +/// +/// The Rust version of the [`charp`] parameter. This type is meant to be +/// used by the [`module::module`] macro, not handled directly. Instead use the +/// `read` method generated by that macro. +/// +///[`charp`]: ../../../include/linux/moduleparam.h pub enum StringParam { + /// A borrowed parameter value. + /// + /// Either the default value (which is static in the module) or borrowed + /// from the original argument buffer used to set the value. Ref(&'static [u8]), + /// A value that was allocated when the parameter was set. + /// + /// The value needs to be freed when the parameter is reset or the module is + /// unloaded. Owned(alloc::vec::Vec), } @@ -397,14 +444,12 @@ impl core::fmt::Display for StringParam { } impl ModuleParam for StringParam { - type Read = [u8]; + type Value = [u8]; const NOARG_ALLOWED: bool = false; fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option { - // TODO: unsafe doc - let slab_available = unsafe { - crate::bindings::slab_is_available() - }; + // Safety: It is always safe to call `slab_is_available`. + let slab_available = unsafe { crate::bindings::slab_is_available() }; arg.map(|arg| { if slab_available { let mut vec = alloc::vec::Vec::new(); @@ -416,7 +461,7 @@ impl ModuleParam for StringParam { }) } - unsafe fn read(&self) -> &Self::Read { + fn value(&self) -> &Self::Value { self.bytes() } } diff --git a/rust/module.rs b/rust/module.rs index 028cafa0f6145e..3c8c9c1cf5ae2c 100644 --- a/rust/module.rs +++ b/rust/module.rs @@ -68,17 +68,16 @@ fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { #[derive(Debug, Clone, PartialEq)] enum ParamType { Ident(String), - Array { - vals: String, - max_length: usize, - } + Array { vals: String, max_length: usize }, } fn expect_array_type(it: &mut token_stream::IntoIter) -> ParamType { let vals = expect_ident(it); assert_eq!(expect_punct(it), ';'); let max_length_str = expect_literal(it); - let max_length = max_length_str.parse::().expect("Expected usize length"); + let max_length = max_length_str + .parse::() + .expect("Expected usize length"); ParamType::Array { vals, max_length } } @@ -90,7 +89,7 @@ fn expect_type(it: &mut token_stream::IntoIter) -> ParamType { let mut it = group.stream().into_iter(); expect_array_type(&mut it) } - _ => panic!("Expected Param Type") + _ => panic!("Expected Param Type"), } } @@ -241,16 +240,19 @@ fn param_ops_path(param_type: &str) -> &'static str { "isize" => "kernel::module_param::PARAM_OPS_ISIZE", "usize" => "kernel::module_param::PARAM_OPS_USIZE", "str" => "kernel::module_param::PARAM_OPS_STR", - t => panic!("Unrecognized type {}", t) + t => panic!("Unrecognized type {}", t), } } -fn try_simple_param_val(param_type: &str) -> Box Option> { +fn try_simple_param_val( + param_type: &str, +) -> Box Option> { match param_type { "bool" => Box::new(|param_it| try_ident(param_it)), - "str" => Box::new(|param_it| try_byte_string(param_it).map(|s| { - format!("kernel::module_param::StringParam::Ref(b\"{}\")", s) - })), + "str" => Box::new(|param_it| { + try_byte_string(param_it) + .map(|s| format!("kernel::module_param::StringParam::Ref(b\"{}\")", s)) + }), _ => Box::new(|param_it| try_literal(param_it)), } } @@ -258,15 +260,19 @@ fn try_simple_param_val(param_type: &str) -> Box String { let try_param_val = match param_type { ParamType::Ident(ref param_type) - | ParamType::Array { vals: ref param_type, max_length: _ } => { - try_simple_param_val(param_type) - } + | ParamType::Array { + vals: ref param_type, + max_length: _, + } => try_simple_param_val(param_type), }; assert_eq!(expect_ident(param_it), "default"); assert_eq!(expect_punct(param_it), ':'); let default = match param_type { ParamType::Ident(_) => try_param_val(param_it).expect("Expected default param value"), - ParamType::Array{ vals:_, max_length } => { + ParamType::Array { + vals: _, + max_length: _, + } => { let group = expect_group(param_it); assert_eq!(group.delimiter(), Delimiter::Bracket); let mut default_vals = Vec::new(); @@ -279,28 +285,18 @@ fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> None => break, _ => panic!("Expected ',' or end of array default values"), } - }; - - let uninit_count = max_length - default_vals.len(); - let mut maybe_uninit_defaults = "[".to_string(); - let used = default_vals.len(); - for val in default_vals { - maybe_uninit_defaults.push_str(&format!("core::mem::MaybeUninit::new({}),", val)); } - for _ in 0..uninit_count { - maybe_uninit_defaults.push_str("core::mem::MaybeUninit::uninit()"); - } - maybe_uninit_defaults.push(']'); - format!( - " - kernel::module_param::ArrayParam {{ - values: {maybe_uninit_defaults}, - used: {used}, - }} - ", - maybe_uninit_defaults = maybe_uninit_defaults, - used = used, - ) + + let mut default_array = "kernel::module_param::ArrayParam::create(&[".to_string(); + default_array.push_str( + &default_vals + .iter() + .map(|val| val.to_string()) + .collect::>() + .join(","), + ); + default_array.push_str("])"); + default_array } }; assert_eq!(expect_punct(param_it), ','); @@ -380,6 +376,8 @@ fn generated_array_ops_name(vals: &str, max_length: usize) -> String { /// - `usize`: No equivalent C param type. /// - `str`: Corresponds to C `charp` param type. Reading returns a byte /// slice. +/// - `[T; N]`: Corresponds to C parameters created using +/// `module_param_array`. An array of `T`'s of length at **most** `N`. /// /// `invbool` is unsupported: it was only ever used in a few modules. /// Consider using a `bool` and inverting the logic instead. @@ -404,19 +402,15 @@ pub fn module(ts: TokenStream) -> TokenStream { let mut array_types_to_generate = Vec::new(); - println!("Starting loop"); loop { - println!("Getting param"); let param_name = match it.next() { Some(TokenTree::Ident(ident)) => ident.to_string(), Some(_) => panic!("Expected Ident or end"), None => break, }; - println!("Got name {}", param_name); assert_eq!(expect_punct(&mut it), ':'); let param_type = expect_type(&mut it); - println!("Got name {:?}", param_type); let group = expect_group(&mut it); assert_eq!(expect_punct(&mut it), ','); @@ -431,10 +425,19 @@ pub fn module(ts: TokenStream) -> TokenStream { // TODO: more primitive types // TODO: other kinds: arrays, unsafes, etc. let (param_kernel_type, ops): (String, _) = match param_type { - ParamType::Ident(ref param_type) => (param_type.to_string(), param_ops_path(¶m_type).to_string()), - ParamType::Array{ ref vals, max_length } => { + ParamType::Ident(ref param_type) => ( + param_type.to_string(), + param_ops_path(¶m_type).to_string(), + ), + ParamType::Array { + ref vals, + max_length, + } => { array_types_to_generate.push((vals.clone(), max_length)); - (format!("__rust_array_param_{}_{}", vals, max_length), generated_array_ops_name(vals, max_length)) + ( + format!("__rust_array_param_{}_{}", vals, max_length), + generated_array_ops_name(vals, max_length), + ) } }; @@ -454,8 +457,11 @@ pub fn module(ts: TokenStream) -> TokenStream { ParamType::Ident(ref param_type) => match param_type.as_ref() { "str" => "kernel::module_param::StringParam".to_string(), other => other.to_string(), - } - ParamType::Array { ref vals, max_length } => format!( + }, + ParamType::Array { + ref vals, + max_length, + } => format!( "kernel::module_param::ArrayParam<{vals}, {max_length}>", vals = vals, max_length = max_length @@ -465,8 +471,8 @@ pub fn module(ts: TokenStream) -> TokenStream { format!( " // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled. - fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ - unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} + fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} }} ", name = name, @@ -477,8 +483,8 @@ pub fn module(ts: TokenStream) -> TokenStream { format!( " // SAFETY: Parameters are locked by `KParamGuard`. - fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Read {{ - unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::read(&__{name}_{param_name}_value) }} + fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ + unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} }} ", name = name, @@ -557,20 +563,18 @@ pub fn module(ts: TokenStream) -> TokenStream { for (vals, max_length) in array_types_to_generate { let ops_name = generated_array_ops_name(&vals, max_length); - generated_array_types.push_str( - &format!( - " + generated_array_types.push_str(&format!( + " /// Some doc make_param_ops!( {ops_name}, kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}> ); ", - ops_name = ops_name, - vals = vals, - max_length = max_length, - ) - ); + ops_name = ops_name, + vals = vals, + max_length = max_length, + )); } let file = std::env::var("RUST_MODFILE").unwrap(); From 36a2e2411eece8ef1a89045ba0fa681406b25dbd Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 13 Mar 2021 21:04:38 +0100 Subject: [PATCH 14/16] update ci --- .github/workflows/ci.yaml | 7 ++++++- .github/workflows/qemu-init.sh | 2 +- drivers/char/rust_example.rs | 2 +- drivers/char/rust_example_2.rs | 6 ++++++ drivers/char/rust_example_3.rs | 6 ++++++ drivers/char/rust_example_4.rs | 6 ++++++ 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 809dccf5383ca6..44a96a4199b27f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -156,7 +156,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.my_str=🦀mod 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 rust_example_2.my_array=1,2,3' | tee qemu-stdout.log # Check - run: grep -F '] Rust Example (init)' qemu-stdout.log @@ -179,6 +179,11 @@ jobs: - 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 '] my_array: [0,1]' qemu-stdout.log" + - run: "grep -F '] [2] my_array: [1,2,3]' qemu-stdout.log" + - run: "grep -F '] [3] my_array: [0,1]' qemu-stdout.log" + - run: "grep -F '] [4] my_array: [1,2,3]' 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 666630c1798f26..7cbb1c4441cd30 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -1,7 +1,7 @@ #!/bin/sh busybox insmod rust_example_3.ko my_i32=345543 my_str=🦀mod -busybox insmod rust_example_4.ko my_i32=456654 my_usize=84 +busybox insmod rust_example_4.ko my_i32=456654 my_usize=84 my_array=1,2,3 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 1a33189fd1be61..e15d258ac809c2 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -39,7 +39,7 @@ module! { permissions: 0o644, description: b"Example of usize", }, - my_array: [i32; 2] { + my_array: [i32; 3] { default: [0, 1], permissions: 0, description: b"Example of array", diff --git a/drivers/char/rust_example_2.rs b/drivers/char/rust_example_2.rs index 3f0630b286476a..0fca823294b40b 100644 --- a/drivers/char/rust_example_2.rs +++ b/drivers/char/rust_example_2.rs @@ -33,6 +33,11 @@ module! { permissions: 0o644, description: b"Example of usize", }, + my_array: [i32; 3] { + default: [0, 1], + permissions: 0, + description: b"Example of array", + }, }, } @@ -54,6 +59,7 @@ impl KernelModule for RustExample2 { core::str::from_utf8(my_str.read(&lock))? ); println!("[2] my_usize: {}", my_usize.read(&lock)); + println!("[2] my_array: {:?}", my_array.read()); } // Including this large variable on the stack will trigger diff --git a/drivers/char/rust_example_3.rs b/drivers/char/rust_example_3.rs index 5fa88b3c969382..6248ecf1407e27 100644 --- a/drivers/char/rust_example_3.rs +++ b/drivers/char/rust_example_3.rs @@ -33,6 +33,11 @@ module! { permissions: 0o644, description: b"Example of usize", }, + my_array: [i32; 3] { + default: [0, 1], + permissions: 0, + description: b"Example of array", + }, }, } @@ -54,6 +59,7 @@ impl KernelModule for RustExample3 { core::str::from_utf8(my_str.read(&lock))? ); println!("[3] my_usize: {}", my_usize.read(&lock)); + println!("[3] my_array: {:?}", my_array.read()); } // Including this large variable on the stack will trigger diff --git a/drivers/char/rust_example_4.rs b/drivers/char/rust_example_4.rs index 9b7230d01d1905..a4483bda55c1b3 100644 --- a/drivers/char/rust_example_4.rs +++ b/drivers/char/rust_example_4.rs @@ -33,6 +33,11 @@ module! { permissions: 0o644, description: b"Example of usize", }, + my_array: [i32; 3] { + default: [0, 1], + permissions: 0, + description: b"Example of array", + }, }, } @@ -54,6 +59,7 @@ impl KernelModule for RustExample4 { core::str::from_utf8(my_str.read(&lock))? ); println!("[4] my_usize: {}", my_usize.read(&lock)); + println!("[4] my_array: {:?}", my_array.read()); } // Including this large variable on the stack will trigger From c8220ba086c64edb47c14031cb7619422e756313 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 13 Mar 2021 21:24:43 +0100 Subject: [PATCH 15/16] add make_param_ops to prelude --- drivers/char/rust_example.rs | 1 - rust/kernel/prelude.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/char/rust_example.rs b/drivers/char/rust_example.rs index e15d258ac809c2..c854063b1299ed 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -8,7 +8,6 @@ use alloc::boxed::Box; use core::pin::Pin; -use kernel::make_param_ops; use kernel::prelude::*; use kernel::{chrdev, cstr, file_operations::FileOperations, miscdev}; diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index ab03a537c897ca..279b46d83dc265 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -15,4 +15,4 @@ pub use alloc::{borrow::ToOwned, string::String}; pub use module::module; -pub use super::{println, KernelModule, KernelResult}; +pub use super::{println, KernelModule, KernelResult, make_param_ops}; From 9dd57996614d79c8fc7ed2902a51b0825754f736 Mon Sep 17 00:00:00 2001 From: adamrk Date: Sat, 13 Mar 2021 21:36:09 +0100 Subject: [PATCH 16/16] ci fix --- .github/workflows/ci.yaml | 8 ++++---- rust/kernel/prelude.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 44a96a4199b27f..6f0d2c96cfedd5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -179,10 +179,10 @@ jobs: - 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 '] my_array: [0,1]' qemu-stdout.log" - - run: "grep -F '] [2] my_array: [1,2,3]' qemu-stdout.log" - - run: "grep -F '] [3] my_array: [0,1]' qemu-stdout.log" - - run: "grep -F '] [4] my_array: [1,2,3]' qemu-stdout.log" + - run: "grep -F '] my_array: [0, 1]' qemu-stdout.log" + - run: "grep -F '] [2] my_array: [1, 2, 3]' qemu-stdout.log" + - run: "grep -F '] [3] my_array: [0, 1]' qemu-stdout.log" + - run: "grep -F '] [4] my_array: [1, 2, 3]' 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/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index 279b46d83dc265..1c366684a8be57 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -15,4 +15,4 @@ pub use alloc::{borrow::ToOwned, string::String}; pub use module::module; -pub use super::{println, KernelModule, KernelResult, make_param_ops}; +pub use super::{make_param_ops, println, KernelModule, KernelResult};