diff --git a/tools/trace-parser/rustfmt.toml b/.rustfmt.toml similarity index 100% rename from tools/trace-parser/rustfmt.toml rename to .rustfmt.toml diff --git a/lisa/_assets/kmodules/lisa/Makefile b/lisa/_assets/kmodules/lisa/Makefile index 8db88eade79004231b98013b9fb99ceb594d7f14..857ef2eb4f8ae5101124a811e83d06f0e6e5fa04 100644 --- a/lisa/_assets/kmodules/lisa/Makefile +++ b/lisa/_assets/kmodules/lisa/Makefile @@ -50,6 +50,7 @@ RUST_C_SHIMS_DIR := $(RUST_BUILD_DIR)/rust_c_shims RUST_OBJECT_RAW := $(RUST_BUILD_DIR)/rust.raw.o RUST_SYMBOLS := $(RUST_BUILD_DIR)/exported.list RUST_SYMBOLS_CLI := $(RUST_BUILD_DIR)/exported.cli +RUST_START_STOP_LDS := $(RUST_BUILD_DIR)/start_stop.lds RUST_CBINDGEN_H := $(RUST_SRC)/cbindgen.h RUST_CBINDGEN_BIN := $(CARGO_HOME)/bin/cbindgen @@ -86,8 +87,15 @@ $(RUST_OBJECT_RAW): $(RUST_BUILD_DIR) $(CARGO_TARGET_DIR) $(RUST_OBJECT): $(RUST_BUILD_DIR) $(RUST_OBJECT_RAW) - # Get the list of exported symbols - python3 "$(MODULE_SRC)/rust_exported_symbols.py" --rust-object $(RUST_OBJECT_RAW) --out-symbols-plain $(RUST_SYMBOLS) --out-symbols-cli $(RUST_SYMBOLS_CLI) + # Get: + # * The list of exported symbols + # * A linker script with __start_SECNAME and __stop_SECNAME symbols so + # that libraries relying on such encapsulation symbols like linkme + # work, despite the fact that a kernel module is never linked into an + # executable or DSO (a .ko is a relocatable object file) and + # therefore never getting those symbols created by the linker + # automatically. + python3 "$(MODULE_SRC)/rust_exported_symbols.py" --rust-object $(RUST_OBJECT_RAW) --out-symbols-plain $(RUST_SYMBOLS) --out-symbols-cli $(RUST_SYMBOLS_CLI) --out-start-stop-lds $(RUST_START_STOP_LDS) # Garbage collect unused sections in the object file, after we have # extracted the binstore sections (otherwise they get discarded). @@ -96,7 +104,7 @@ $(RUST_OBJECT): $(RUST_BUILD_DIR) $(RUST_OBJECT_RAW) # EXTERN() command in a linker script since older GNU ld version seem # to ignore the EXTERN() command with --gc-sections. $(LD) --version - $(LD) $(KBUILD_LDFLAGS) $$(cat $(RUST_SYMBOLS_CLI)) --gc-sections -nostdlib -r -o $(RUST_OBJECT) $(RUST_OBJECT_RAW) + $(LD) $(KBUILD_LDFLAGS) $$(cat $(RUST_SYMBOLS_CLI)) -T $(RUST_LDS) -T $(RUST_START_STOP_LDS) --gc-sections -nostdlib -r -o $(RUST_OBJECT) $(RUST_OBJECT_RAW) # Only keep as GLOBAL symbols the ones that are to be exported (and the # undefined ones to be provided by C code) @@ -167,7 +175,7 @@ SYMBOL_NAMESPACES_H := $(GENERATED)/symbol_namespaces.h MODULE_VERSION_H := $(GENERATED)/module_version.h KALLSYMS := $(GENERATED)/kallsyms -ldflags-y += -T $(FEATURES_LDS) -T $(RUST_LDS) -T $(SYMBOLS_LDS) +ldflags-y += -T $(FEATURES_LDS) -T $(SYMBOLS_LDS) INTROSPECTION_DATA_H := $(GENERATED)/introspection_data.h diff --git a/lisa/_assets/kmodules/lisa/features.h b/lisa/_assets/kmodules/lisa/features.h index b03c5e48c0bcad9c855b2dbe0a29641dea2bb54f..b739d78bb18c4cf8494f00620fe8167fe2a55e24 100644 --- a/lisa/_assets/kmodules/lisa/features.h +++ b/lisa/_assets/kmodules/lisa/features.h @@ -62,22 +62,7 @@ int __placeholder_deinit(struct feature *feature); #define __FEATURE_NAME(name) __lisa_feature_##name -/* Weak definition, can be useful to deal with compiled-out features */ -#define __DEFINE_FEATURE_WEAK(feature_name) \ - __attribute__((weak)) DEFINE_MUTEX(__lisa_mutex_feature_##feature_name); \ - __attribute__((weak)) struct feature __FEATURE_NAME(feature_name) = { \ - .name = #feature_name, \ - .data = NULL, \ - .enabled = 0, \ - .__explicitly_enabled = 0, \ - .enable = __placeholder_init, \ - .disable = __placeholder_deinit, \ - .lock = &__lisa_mutex_feature_##feature_name, \ - .__internal = true, \ - .__enable_ret = 0, \ - }; - -#define __DEFINE_FEATURE_STRONG(feature_name, enable_f, disable_f, internal) \ +#define __DEFINE_FEATURE(feature_name, enable_f, disable_f, internal) \ DEFINE_MUTEX(__lisa_mutex_feature_##feature_name); \ struct feature __FEATURE_NAME(feature_name) __attribute__((unused,section(".__lisa_features"))) = { \ .name = #feature_name, \ @@ -102,7 +87,7 @@ int __placeholder_deinit(struct feature *feature); * DISABLE_FEATURE() on all the features that were enabled by ENABLE_FEATURE() * in enable_f() in order to keep accurate reference-counting. */ -#define DEFINE_FEATURE(feature_name, enable_f, disable_f) __DEFINE_FEATURE_STRONG(feature_name, enable_f, disable_f, false) +#define DEFINE_FEATURE(feature_name, enable_f, disable_f) __DEFINE_FEATURE(feature_name, enable_f, disable_f, false) /** * DEFINE_INTERNAL_FEATURE() - Same as DEFINE_FEATURE() but for internal features. @@ -112,23 +97,7 @@ int __placeholder_deinit(struct feature *feature); * multiple other features, e.g. to initialize and teardown the use of a kernel * API (workqueues, tracepoints etc). */ -#define DEFINE_INTERNAL_FEATURE(feature_name, enable_f, disable_f) __DEFINE_FEATURE_STRONG(feature_name, enable_f, disable_f, true) - -/** - * DECLARE_FEATURE() - Declare a feature to test for its presence dynamically. - * @feature_name: Name of the feature to declare. - * - * Very similar to DEFINE_FEATURE() but for user code that wants to deal with - * features that might be entirely compiled-out. If the feature is compiled-in, - * DECLARE_FEATURE() will essentially be a no-op. If the feature is - * compiled-out, DECLARE_FEATURE() will still allow making use of it so that its - * initialization fails with an error message, and its presence can be tested - * with FEATURE_IS_AVAILABLE(). - * - * Note that because of weak symbols limitations, a given compilation unit - * cannot contain both DECLARE_FEATURE() and DEFINE_FEATURE(). - */ -#define DECLARE_FEATURE(feature_name) __DEFINE_FEATURE_WEAK(feature_name) +#define DEFINE_INTERNAL_FEATURE(feature_name, enable_f, disable_f) __DEFINE_FEATURE(feature_name, enable_f, disable_f, true) /** * FEATURE() - Pointer the the struct feature @@ -141,17 +110,6 @@ int __placeholder_deinit(struct feature *feature); &__FEATURE_NAME(name); \ }) -/** - * FEATURE_IS_AVAILABLE() - Runtime check if a feature is available. - * @name: name of the feature - * - * Useful in conjunction with DECLARE_FEATURE() to test if a feature is available. - * Note that it's not necessary to use it before calling - * ENABLE_FEATURE()/DISABLE_FEATURE() in simple cases as they will fail with an - * appropriate error message if the feature is missing. - */ -#define FEATURE_IS_AVAILABLE(name) (FEATURE(name)->enable != &__placeholder_init) - /** * ENABLE_FEATURE() - Enable a feature * @feature_name: Name of the feature, as a C identifier. @@ -192,4 +150,31 @@ int init_features(char **selected, size_t selected_len); * Return: non-zero in case of errors. */ int deinit_features(void); + +/** + * feature_name() - Get feature's name + * @feature: Pointer to feature to get the name of. + * + */ +static inline const char *feature_name(struct feature* feature) { + return feature->name; +} + +/** + * feature_data() - Get feature's data pointer + * @feature: Pointer to feature to get the data of. + * + */ +static inline void *feature_data(struct feature* feature) { + return feature->data; +} + +/** + * feature_set_data() - Set feature's data pointer + * @feature: Pointer to feature to set the data of. + * + */ +static inline void feature_data_set(struct feature* feature, void *data) { + feature->data = data; +} #endif diff --git a/lisa/_assets/kmodules/lisa/pixel6.c b/lisa/_assets/kmodules/lisa/pixel6.c index 00db386483596ef000d28de6c01a4390b9e62c26..2f8934603c8b89c06859f46ecf7c6defef0c0884 100644 --- a/lisa/_assets/kmodules/lisa/pixel6.c +++ b/lisa/_assets/kmodules/lisa/pixel6.c @@ -128,7 +128,7 @@ static void read_and_process(char *buffer, unsigned int size, struct file *file, static int p6_emeter_worker(void *data) { struct feature *feature = data; - struct p6_emeter_data *p6_emeter_data = feature->data; + struct p6_emeter_data *p6_emeter_data = feature_data(feature); char content[POWER_METER_SAMPLE_FILE_MAX_SIZE]; read_and_process(content, ARRAY_SIZE(content), p6_emeter_data->sample_file_0, POWER_METER_SAMPLE_FILE_0, 0); @@ -150,9 +150,9 @@ static int enable_p6_emeter(struct feature *feature) { HANDLE_ERR(ENABLE_FEATURE(__workqueue)) data = kzalloc(sizeof(*data), GFP_KERNEL); - feature->data = data; if (!data) HANDLE_ERR(1); + feature_data_set(feature, data); /* Note that this is the hardware sampling rate. Software will only see *an updated value every 8 hardware periods @@ -185,7 +185,7 @@ finish: static int disable_p6_emeter(struct feature* feature) { int ret = 0; - struct p6_emeter_data *data = feature->data; + struct p6_emeter_data *data = feature_data(feature); if (data) free_p6_emeter_data(data); diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock index 9524268281769d998ef007ebb7d9dbd9f49e76c8..b99020f2a499ae0e59168d6306dd4f460d558158 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.164" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "lisakmod_macros" @@ -60,18 +60,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "syn" -version = "2.0.89" +version = "2.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" dependencies = [ "proc-macro2", "quote", diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/cffi.h b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/cffi.h index 09adb1711b4bdc4dcf44a398f4b51d7a87ced7ca..933540860ddca366e3c25150be554f74e499de83 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/cffi.h +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/cffi.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#include /// Types defined in this header have ABI compatibility with some types /// manipulated in lisakmod_macros::inlinec module. @@ -9,14 +8,23 @@ #ifndef _CFFI_H #define _CFFI_H +#include + +/* On recent kernels, kCFI is used instead of CFI and __cficanonical is therefore not + * defined anymore + */ +#ifndef __cficanonical +# define __cficanonical +#endif + struct slice_u8 { uint8_t *data; size_t len; -}; +} __no_randomize_layout; struct slice_const_u8 { const uint8_t *data; const size_t len; -}; +} __no_randomize_layout; #endif /* _CFFI_H */ diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.toml b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.toml index 6e2b94bb12965cb7bb65cbe71fc164c29cfb0596..e20f520626918266f05c5ee559b8ee6f8073a5ce 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.toml +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lisakmod_macros_proc" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] syn = { version = "2.0", default-features = false, features = ["proc-macro", "parsing", "printing", "full", "clone-impls"]} diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs index eb5b64b28bbdd71037a11b10c936967485ec0afe..5fe019a758670ffaa243226a2e8d07535737d7f3 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/inlinec.rs @@ -162,8 +162,36 @@ fn _make_c_func( ) -> Result<(TokenStream, TokenStream, TokenStream), Error> { let c_name_str: String = c_name.to_string(); let f_where = f_generics.map(|x| x.where_clause.clone()); - let c_proto = format!("{c_name_str}_proto"); - let c_ret_ty = format!("{c_name_str}_ret_ty"); + + let c_type_of = |ty| { + ( + quote! { + { + // Use a const fn to introduce f_generics and f_where + const fn get #f_generics() -> &'static str #f_where { + ( + <#ty as ::lisakmod_macros::inlinec::FfiType>::C_TYPE + ) + } + get() + } + }, + quote! { + { + // Use a const fn to introduce f_generics and f_where + const fn get #f_generics() -> &'static str #f_where { + ( + match <#ty as ::lisakmod_macros::inlinec::FfiType>::C_HEADER { + Some(c_header) => c_header, + None => "linux/types.h", + } + ) + } + get() + } + }, + ) + }; let (pre_c_code, c_code, post_c_code) = c_code; // TODO: Due to this issue, the span reported by syn is currently always going to have @@ -191,62 +219,60 @@ fn _make_c_func( }; let c_nr_args = f_args.len(); - let (arg_names, arg_tys): (Vec<_>, Vec<_>) = f_args.iter().cloned().unzip(); - let c_args_name: Vec<_> = arg_names.iter().map(|name| name.to_string()).collect(); + let (args_name, args_ty): (Vec<_>, Vec<_>) = f_args.iter().cloned().unzip(); + let c_args_name: Vec<_> = args_name.iter().map(|name| name.to_string()).collect(); let c_args_commas: Vec<_> = c_args_name .iter() .enumerate() .map(|(i, _)| if i == (c_nr_args - 1) { "" } else { ", " }) .collect(); - let (c_args_ty_macro, c_args_ty_typedef): (Vec<_>, Vec<_>) = arg_names - .iter() - .map(|arg| { - ( - format!("{c_name_str}_arg_ty_macro_{arg}"), - format!("{c_name_str}_arg_ty_typedef_{arg}"), - ) - }) - .unzip(); - - // Argument types are encoded as a function-like macro. Calling this macro with an - // identifier declares a variable (or function argument) of that type. This allows using - // any type, including more complex ones like arrays and function pointers. - let c_args_typedef = concatcp(quote! { - #( - "\n", - "#define ", #c_args_ty_macro, "(DECLARATOR)", - { - // Use a const fn to introduce f_generics and f_where - const fn get #f_generics() -> &'static str #f_where { - <#arg_tys as ::lisakmod_macros::inlinec::FfiType>::C_DECL - } - get() - }, - "\n", - "typedef ", #c_args_ty_macro, "(", #c_args_ty_typedef ,");" - ),* - })?; - let c_args = if c_nr_args == 0 { - quote! { "void "} + let (c_args, c_args_header) = if c_nr_args == 0 { + (quote! { "void" }, quote! { "" }) } else { - concatcp(quote! { - #( - #c_args_ty_typedef, " ", #c_args_name, - #c_args_commas - ),* - })? + let (c_args_ty, c_args_header): (Vec<_>, Vec<_>) = args_ty.iter().map(c_type_of).unzip(); + ( + concatcp(quote! { + #( + "__typeof__(" , #c_args_ty, ") ", #c_args_name, + #c_args_commas + ),* + })?, + concatcp(quote! { + #( + "#include \"" , #c_args_header, "\"\n" + ),* + })?, + ) }; let rust_extern_args = quote! { #( - #arg_names : <#arg_tys as ::lisakmod_macros::inlinec::FfiType>::FfiType + #args_name : <#args_ty as ::lisakmod_macros::inlinec::FfiType>::FfiType ),* }; let pre_c_code = pre_c_code.unwrap_or(quote! {""}); let post_c_code = post_c_code.unwrap_or(quote! {""}); + // Disabling CFI inside the function allows us to call anything we want, including Rust + // functions if needed. + c_attrs.push(quote! {"__nocfi"}); + + let (c_ret_ty, c_ret_header) = c_type_of(f_ret_ty); + let c_proto = concatcp(quote! { + #c_args_header, + "\n", + "#include \"", #c_ret_header, "\"", + "\n", + #c_ret_ty, " ", #(#c_attrs, " "),* , " ", #c_name_str, "(", #c_args , ")", + })?; + + let c_header_out = concatcp(quote! { + "\n#line ", #c_code_line, " \"", file!(), "\"\n", + #c_proto, ";\n", + })?; + let c_funcdef = match c_code { Some(c_code) => concatcp(quote! { #c_proto, @@ -257,47 +283,6 @@ fn _make_c_func( None => quote! {""}, }; - // Disabling CFI inside the function allows us to call anything we want, including Rust - // functions if needed. - c_attrs.push(quote!{"__nocfi"}); - - let c_header_out = concatcp(quote! { - r#" - #include - - #define BUILTIN_TY_DECL(ty, declarator) ty declarator - #define PTR_TY_DECL(declarator) (*declarator) - #define ARR_TY_DECL(N, declarator) ((declarator)[N]) - #define CONST_TY_DECL(declarator) const declarator - #define ATTR_TY_DECL(attributes, declarator) attributes declarator - #define FN_TY_DECL(args, declarator) ((declarator)args) - - /* On recent kernels, kCFI is used instead of CFI and __cficanonical is therefore not - * defined anymore - */ - #ifndef __cficanonical - # define __cficanonical - #endif - "#, - - "#line ", #c_code_line, " \"", file!(), "\"\n", - #c_args_typedef, - - // See comment on how arguments type are handled, as we do the same for the return - // type. - "\n#define ", #c_ret_ty, "(DECLARATOR)", - { - // Use a const fn to introduce f_generics and f_where - const fn get #f_generics() -> &'static str #f_where { - <#f_ret_ty as ::lisakmod_macros::inlinec::FfiType>::C_DECL - } - get() - }, - "\n#define ", #c_proto, " ", #c_ret_ty, "(FN_TY_DECL((", #c_args, "), ATTR_TY_DECL(", #(#c_attrs, " ",)* ", ", #c_name_str, ")))", - "\n#line ", #c_code_line, " \"", file!(), "\"\n", - #c_proto, ";\n", - })?; - let c_out = concatcp(quote! { "#line ", #pre_c_code_line, " \"", file!(), "\"\n", #pre_c_code, @@ -333,7 +318,7 @@ pub fn cfunc(_attrs: TokenStream, code: TokenStream) -> Result39}", get_random()); let shim_name = format_ident!("{}", shim_name_str); let c_out = make_c_func( Some(&shim_name), @@ -345,15 +330,15 @@ pub fn cfunc(_attrs: TokenStream, code: TokenStream) -> Result, Vec<_>) = f_args.into_iter().unzip(); + let (args_name, args_ty): (Vec<_>, Vec<_>) = f_args.into_iter().unzip(); let rust_args = quote! { #( - #arg_names : #arg_tys + #args_name : #args_ty ),* }; let rust_extern_call_args = quote! { #( - ::lisakmod_macros::inlinec::IntoFfi::into_ffi(#arg_names) + ::lisakmod_macros::inlinec::IntoFfi::into_ffi(#args_name) ),* }; @@ -557,7 +542,7 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result, Vec<_>) = f_args.iter().cloned().unzip(); + let (args_name, args_ty): (Vec<_>, Vec<_>) = f_args.iter().cloned().unzip(); fn ffi_ty(ty: Type) -> syn::Result { Ok(parse_quote! { @@ -588,7 +573,7 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result::into_ffi( #body @@ -596,7 +581,7 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result39}", get_random()); item_fn.sig.ident = rust_name.clone(); // Make the Rust function unsafe since we call FromFfi::from_ffi() @@ -621,7 +606,7 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result Result match attrs.no_mangle { true => name.to_string(), - false => format!("__lisa_c_shim_{name}_{}", get_random()), + false => format!("__lisa_c_shim_{name}_{:0>39}", get_random()), }, Some(name) => { export_markers.push(_export_symbol(format_ident!("{name}"))?); @@ -684,7 +669,7 @@ pub fn cexport(attrs: TokenStream, code: TokenStream) -> Result Result { let ident = syn::parse2::(args)?; - _export_symbol(ident).map(Into::into) + _export_symbol(ident) } fn _export_symbol(ident: Ident) -> Result { @@ -707,7 +692,7 @@ fn make_c_out(c_code: TokenStream, section: &str) -> Result // Store the C function in a section of the binary, that will be extracted by the // module Makefile and compiled separately as C code. - #[link_section = #section ] + #[unsafe(link_section = #section)] #[used] static CODE: [u8; CODE_LEN] = ::lisakmod_macros::private::misc::slice_to_array::<{CODE_LEN}>(CODE_SLICE); }; @@ -823,10 +808,19 @@ pub fn cstatic(attrs: TokenStream, code: TokenStream) -> Result match attrs.no_mangle { true => name.to_string(), - false => format!("__lisa_c_shim_{name}_{}", get_random()), + false => format!("__lisa_c_shim_{name}_{:0>39}", get_random()), }, Some(name) => name, }; + let c_typecheck_name = format!("{c_name}_typecheck_{:0>39}", get_random()); + + let c_type = quote! { <#ty as ::lisakmod_macros::inlinec::FfiType>::C_TYPE }; + let c_header = quote! { + match <#ty as ::lisakmod_macros::inlinec::FfiType>::C_HEADER { + Some(c_header) => c_header, + None => "linux/types.h", + } + }; let section = format!(".binstore.c.code.{}", c_name); let c_out = concatcp(quote! { @@ -834,13 +828,13 @@ pub fn cstatic(attrs: TokenStream, code: TokenStream) -> Result::C_DECL, " = &", #c_name, ";\n", - "#undef DECLARATOR", + "static __attribute__ ((unused)) __typeof__(", #c_type, ") *", #c_typecheck_name, " = &", #c_name, ";\n", "\n#line ", #post_c_code_line, " \"", file!(), "\"\n", #post_c_code, diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/misc.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/misc.rs index e15b53d262eb672ff687e7e656d324c534343d93..9dcb07948dad01c1298a4dd047031d0b4f73f596 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/misc.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/macros/src/misc.rs @@ -26,7 +26,7 @@ pub(crate) fn concatcp(args: TokenStream) -> Result { let item_names: Vec = items .iter() - .map(|_| format_ident!("__concat_item_{}", get_random())) + .map(|_| format_ident!("__concat_item_{:0>39}", get_random())) .collect(); let out = quote! { diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs index 0cbc512399b28cbf02960ed2f7ee34d29ed6313c..4224330e3cf5acbbfaea98303abc0b2e787ec4e0 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod-macros/src/inlinec.rs @@ -18,7 +18,8 @@ pub trait FfiType { // TODO: if and when Rust gains const trait methods, we can just define a const function to // build a type rather than providing a C macro body and C preprocessor machinery to build full // type names. - const C_DECL: &'static str; + const C_TYPE: &'static str; + const C_HEADER: Option<&'static str>; type FfiType; } @@ -101,7 +102,8 @@ macro_rules! impl_ptr { T: Sized, $nested_ptr: FfiType, { - const C_DECL: &'static str = <$nested_ptr as FfiType>::C_DECL; + const C_TYPE: &'static str = <$nested_ptr as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = <$nested_ptr as FfiType>::C_HEADER; type FfiType = <$nested_ptr as FfiType>::FfiType; } @@ -207,7 +209,8 @@ where T: ?Sized, ConstPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = as FfiType>::FfiType; } @@ -239,7 +242,8 @@ where T: ?Sized, ConstPtr>: FfiType, { - const C_DECL: &'static str = > as FfiType>::C_DECL; + const C_TYPE: &'static str = > as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = > as FfiType>::C_HEADER; type FfiType = > as FfiType>::FfiType; } @@ -248,7 +252,8 @@ where T: ?Sized, ConstPtr>: FfiType, { - const C_DECL: &'static str = > as FfiType>::C_DECL; + const C_TYPE: &'static str = > as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = > as FfiType>::C_HEADER; type FfiType = > as FfiType>::FfiType; } @@ -257,7 +262,8 @@ where T: ?Sized, MutPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = as FfiType>::FfiType; } @@ -289,7 +295,8 @@ where T: ?Sized, MutPtr>: FfiType, { - const C_DECL: &'static str = > as FfiType>::C_DECL; + const C_TYPE: &'static str = > as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = > as FfiType>::C_HEADER; type FfiType = > as FfiType>::FfiType; } @@ -298,7 +305,8 @@ where T: ?Sized, MutPtr>: FfiType, { - const C_DECL: &'static str = > as FfiType>::C_DECL; + const C_TYPE: &'static str = > as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = > as FfiType>::C_HEADER; type FfiType = > as FfiType>::FfiType; } @@ -330,17 +338,29 @@ impl PtrToMaybeSized for *mut T { } } +impl FfiType for Option<&T> +where + T: ?Sized, + *const T: PtrToMaybeSized, + ConstPtr: FfiType, +{ + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; + type FfiType = as FfiType>::FfiType; +} + impl FfiType for &T where T: ?Sized, *const T: PtrToMaybeSized, ConstPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = as FfiType>::FfiType; } -impl FromFfi for &T +impl FromFfi for Option<&T> where T: ?Sized, ConstPtr: FfiType + FromFfi, @@ -349,7 +369,33 @@ where unsafe fn from_ffi(x: Self::FfiType) -> Self { let ptr = as FromFfi>::from_ffi(x).0; assert!(PtrToMaybeSized::is_aligned(&ptr).unwrap_or(true)); - ptr.as_ref().expect("Unexpected NULL pointer") + ptr.as_ref() + } +} + +impl<'a, T> FromFfi for &'a T +where + T: ?Sized, + ConstPtr: FfiType + FromFfi, +{ + #[inline] + unsafe fn from_ffi(x: Self::FfiType) -> Self { + as FromFfi>::from_ffi(x).expect("Unexpected NULL pointer") + } +} + +impl IntoFfi for Option<&T> +where + // We need Sized here so that we can use core::ptr::null() + T: Sized, + ConstPtr: FfiType + IntoFfi, +{ + #[inline] + fn into_ffi(self) -> Self::FfiType { + match self { + Some(x) => x.into_ffi(), + None => null::().into_ffi(), + } } } @@ -364,16 +410,27 @@ where } } -impl FfiType for &mut T +impl FfiType for Option<&mut T> where T: ?Sized, MutPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = as FfiType>::FfiType; } -impl FromFfi for &mut T +impl<'a, T> FfiType for &'a mut T +where + T: ?Sized, + MutPtr: FfiType, +{ + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; + type FfiType = as FfiType>::FfiType; +} + +impl FromFfi for Option<&mut T> where T: ?Sized, *mut T: PtrToMaybeSized, @@ -383,7 +440,34 @@ where unsafe fn from_ffi(x: Self::FfiType) -> Self { let ptr = as FromFfi>::from_ffi(x).0; assert!(PtrToMaybeSized::is_aligned(&ptr).unwrap_or(true)); - ptr.as_mut().expect("Unexpected NULL pointer") + ptr.as_mut() + } +} + +impl<'a, T> FromFfi for &'a mut T +where + T: ?Sized, + *mut T: PtrToMaybeSized, + MutPtr: FfiType + FromFfi, +{ + #[inline] + unsafe fn from_ffi(x: Self::FfiType) -> Self { + as FromFfi>::from_ffi(x).expect("Unexpected NULL pointer") + } +} + +impl IntoFfi for Option<&mut T> +where + // We need Sized here so that we can use core::ptr::null_mut() + T: Sized, + MutPtr: FfiType + IntoFfi, +{ + #[inline] + fn into_ffi(self) -> Self::FfiType { + match self { + Some(x) => x.into_ffi(), + None => null_mut::().into_ffi(), + } } } @@ -406,7 +490,8 @@ where { // Expose as a mutable pointer for C code, since the whole point of UnsafeCell is to allow // mutation of T from a &UnsafeCell. - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; // Expose the pointer as *const for the FFI functions so that the signatures are compatible // with the other blanket implementations. This will effectively transmute the *const // UnsafeCell into *mut T in the IntoFfi implementation at the FFI boundary. @@ -418,7 +503,8 @@ where T: ?Sized, MutPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = *mut UnsafeCell; } @@ -428,7 +514,8 @@ macro_rules! impl_transparent_wrapper { where T: FfiType, { - const C_DECL: &'static str = ::C_DECL; + const C_TYPE: &'static str = ::C_TYPE; + const C_HEADER: Option<&'static str> = ::C_HEADER; type FfiType = ::FfiType; } @@ -436,7 +523,8 @@ macro_rules! impl_transparent_wrapper { where ConstPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = *const $wrapper; } @@ -444,7 +532,8 @@ macro_rules! impl_transparent_wrapper { where MutPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = *mut $wrapper; } }; @@ -481,43 +570,44 @@ where #[macro_export] macro_rules! __impl_primitive_ptr { - ($pointee:ty, $c_pointee:literal) => { + ($pointee:ty, $c_pointee:literal, $c_header:expr) => { $crate::inlinec::__impl_primitive_ptr!( - @impl, $pointee, $pointee, $c_pointee, - "", "" + @impl, $pointee, $pointee, $c_header, $c_pointee ); // TODO: These implementations are necessary since we cannot currently have a recursive - // implementation for ConstPtr>, because we cannot express the resulting C_DECL + // implementation for ConstPtr>, because we cannot express the resulting C_TYPE // (lack of const function in traits). We therefore unroll 2 level of pointers, since we // don't really need more in practice. $crate::inlinec::__impl_primitive_ptr!( @impl, $crate::inlinec::ConstPtr<$pointee>, <$crate::inlinec::ConstPtr<$pointee> as $crate::inlinec::FfiType>::FfiType, - $c_pointee, - "CONST_TY_DECL(PTR_TY_DECL(", "))" + $c_header, + "const __typeof__(", $c_pointee, ") *" ); $crate::inlinec::__impl_primitive_ptr!( @impl, $crate::inlinec::MutPtr<$pointee>, <$crate::inlinec::MutPtr<$pointee> as $crate::inlinec::FfiType>::FfiType, - $c_pointee, - "PTR_TY_DECL(", ")" + $c_header, + "__typeof__(", $c_pointee, ") *" ); }; - (@impl, $pointee:ty, $ffi_pointee:ty, $c_pointee:literal, $decl_pre:literal, $decl_post:literal) => { + (@impl, $pointee:ty, $ffi_pointee:ty, $c_header:expr, $($c_pointee:literal),*) => { impl $crate::inlinec::FfiType for $crate::inlinec::ConstPtr<$pointee> { - const C_DECL: &'static str = $crate::misc::concatcp!( - "BUILTIN_TY_DECL(", $c_pointee, ", ", $decl_pre, "CONST_TY_DECL(PTR_TY_DECL(DECLARATOR))", $decl_post, ")" + const C_TYPE: &'static str = $crate::misc::concatcp!( + "const __typeof__(", $($c_pointee),*, ") *" ); + const C_HEADER: Option<&'static str> = $c_header; type FfiType = *const $ffi_pointee; } impl $crate::inlinec::FfiType for $crate::inlinec::MutPtr<$pointee> { - const C_DECL: &'static str = $crate::misc::concatcp!( - "BUILTIN_TY_DECL(", $c_pointee, ", ", $decl_pre, "PTR_TY_DECL(DECLARATOR)", $decl_post, ")" + const C_TYPE: &'static str = $crate::misc::concatcp!( + "__typeof__(", $($c_pointee),*, ") *" ); + const C_HEADER: Option<&'static str> = $c_header; type FfiType = *mut $ffi_pointee; } } @@ -525,10 +615,10 @@ macro_rules! __impl_primitive_ptr { pub use crate::__impl_primitive_ptr; macro_rules! impl_primitive { - ($ty:ty, $c_name:literal) => { + ($ty:ty, $c_name:literal, $c_header:expr) => { impl FfiType for $ty { - const C_DECL: &'static str = - $crate::misc::concatcp!("BUILTIN_TY_DECL(", $c_name, ", DECLARATOR)"); + const C_TYPE: &'static str = $c_name; + const C_HEADER: Option<&'static str> = $c_header; type FfiType = $ty; } @@ -546,28 +636,29 @@ macro_rules! impl_primitive { } } - __impl_primitive_ptr!($ty, $c_name); + __impl_primitive_ptr!($ty, $c_name, $c_header); }; } -impl_primitive!(u8, "uint8_t"); -impl_primitive!(u16, "uint16_t"); -impl_primitive!(u32, "uint32_t"); -impl_primitive!(u64, "uint64_t"); -impl_primitive!(usize, "size_t"); +impl_primitive!(u8, "uint8_t", Some("linux/types.h")); +impl_primitive!(u16, "uint16_t", Some("linux/types.h")); +impl_primitive!(u32, "uint32_t", Some("linux/types.h")); +impl_primitive!(u64, "uint64_t", Some("linux/types.h")); +impl_primitive!(usize, "size_t", Some("linux/types.h")); -impl_primitive!(i8, "int8_t"); -impl_primitive!(i16, "int16_t"); -impl_primitive!(i32, "int32_t"); -impl_primitive!(i64, "int64_t"); -impl_primitive!(isize, "ssize_t"); +impl_primitive!(i8, "int8_t", Some("linux/types.h")); +impl_primitive!(i16, "int16_t", Some("linux/types.h")); +impl_primitive!(i32, "int32_t", Some("linux/types.h")); +impl_primitive!(i64, "int64_t", Some("linux/types.h")); +impl_primitive!(isize, "ssize_t", Some("linux/types.h")); -impl_primitive!(bool, "_Bool"); +impl_primitive!(bool, "_Bool", None); // This is used for function returning void exclusively. We never pass a void parameter to a // function. impl FfiType for () { - const C_DECL: &'static str = "BUILTIN_TY_DECL(void, DECLARATOR)"; + const C_TYPE: &'static str = "void"; + const C_HEADER: Option<&'static str> = None; type FfiType = (); } @@ -584,11 +675,12 @@ impl IntoFfi for () { // This is used for C void pointers exclusively. The usage is distinct from a function returning // void, which is covered by the unit type (). impl FfiType for c_void { - const C_DECL: &'static str = "BUILTIN_TY_DECL(void, DECLARATOR)"; + const C_TYPE: &'static str = "void"; + const C_HEADER: Option<&'static str> = None; type FfiType = c_void; } // Only implement FromFfi/IntoFfi for pointers to c_void, never for c_void itself -__impl_primitive_ptr!(c_void, "void"); +__impl_primitive_ptr!(c_void, "void", None); pub trait NullPtr { fn null_mut() -> *mut Self; @@ -617,7 +709,8 @@ where T: ?Sized, MutPtr: FfiType, { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = as FfiType>::FfiType; } @@ -658,7 +751,8 @@ where T: ?Sized, Option>: FfiType, { - const C_DECL: &'static str = > as FfiType>::C_DECL; + const C_TYPE: &'static str = > as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = > as FfiType>::C_HEADER; type FfiType = > as FfiType>::FfiType; } @@ -686,7 +780,8 @@ where } impl FfiType for Option<&CStr> { - const C_DECL: &'static str = <&'static c_char as FfiType>::C_DECL; + const C_TYPE: &'static str = <&'static c_char as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = <&'static c_char as FfiType>::C_HEADER; type FfiType = <&'static c_char as FfiType>::FfiType; } @@ -712,7 +807,8 @@ impl FromFfi for Option<&CStr> { } impl<'a> FfiType for &'a CStr { - const C_DECL: &'static str = as FfiType>::C_DECL; + const C_TYPE: &'static str = as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = as FfiType>::C_HEADER; type FfiType = as FfiType>::FfiType; } @@ -732,7 +828,8 @@ impl FromFfi for &CStr { } impl<'a> FfiType for &'a str { - const C_DECL: &'static str = <&'a CStr as FfiType>::C_DECL; + const C_TYPE: &'static str = <&'a CStr as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = <&'a CStr as FfiType>::C_HEADER; type FfiType = <&'a CStr as FfiType>::FfiType; } @@ -749,7 +846,8 @@ impl FromFfi for &str { // &CStr for that. impl<'a> FfiType for Option<&'a str> { - const C_DECL: &'static str = <&'a CStr as FfiType>::C_DECL; + const C_TYPE: &'static str = <&'a CStr as FfiType>::C_TYPE; + const C_HEADER: Option<&'static str> = <&'a CStr as FfiType>::C_HEADER; type FfiType = <&'a CStr as FfiType>::FfiType; } @@ -792,22 +890,27 @@ where } macro_rules! impl_slice { - ($ty:ty, $c_name_const:literal, $c_name_mut:literal) => { + ($ty:ty, $c_name_const:literal, $c_name_mut:literal, $c_header:expr) => { impl FfiType for ConstPtr<[$ty]> { - const C_DECL: &'static str = - $crate::misc::concatcp!("BUILTIN_TY_DECL(", $c_name_const, ", DECLARATOR)"); + const C_TYPE: &'static str = $c_name_const; + const C_HEADER: Option<&'static str> = Some($c_header); type FfiType = FfiSlice<*const $ty>; } impl FfiType for MutPtr<[$ty]> { - const C_DECL: &'static str = - $crate::misc::concatcp!("BUILTIN_TY_DECL(", $c_name_mut, ", DECLARATOR)"); + const C_TYPE: &'static str = $c_name_mut; + const C_HEADER: Option<&'static str> = Some($c_header); type FfiType = FfiSlice<*mut $ty>; } }; } -impl_slice!(u8, "struct slice_const_u8", "struct slice_u8"); +impl_slice!( + u8, + "struct slice_const_u8", + "struct slice_u8", + "rust/lisakmod-macros/cffi.h" +); impl IntoFfi for ConstPtr<[T]> where @@ -858,7 +961,8 @@ where } impl FfiType for Result<(), c_int> { - const C_DECL: &'static str = "BUILTIN_TY_DECL(int, DECLARATOR)"; + const C_TYPE: &'static str = "int"; + const C_HEADER: Option<&'static str> = None; type FfiType = c_int; } @@ -883,7 +987,8 @@ impl FromFfi for Result<(), c_int> { } impl FfiType for Result<(), Infallible> { - const C_DECL: &'static str = "BUILTIN_TY_DECL(void, DECLARATOR)"; + const C_TYPE: &'static str = "void"; + const C_HEADER: Option<&'static str> = None; type FfiType = (); } @@ -983,7 +1088,7 @@ pub trait Opaque { #[macro_export] macro_rules! __internal_opaque_type { - ($vis:vis struct $name:ident, $c_name:literal, $c_header:literal $(, $($opt_name:ident {$($opt:tt)*}),* $(,)?)?) => { + ($vis:vis struct $name:ident, $c_name:literal, $c_header:expr $(, $($opt_name:ident {$($opt:tt)*}),* $(,)?)?) => { // Model opaque types as recommended in the Rustonomicon: // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs // On top of that recipe, we add: @@ -1001,7 +1106,7 @@ macro_rules! __internal_opaque_type { () as $crate::inlinec::GetAlignedData< { match $crate::inlinec::cconstant!( - ("#include <", $c_header, ">"), + ("#include \"", $c_header, "\""), ("sizeof (", $c_name, ")"), ) { Some(x) => x, @@ -1010,7 +1115,7 @@ macro_rules! __internal_opaque_type { }, { match $crate::inlinec::cconstant!( - ("#include <", $c_header, ">"), + ("#include \"", $c_header, "\""), ("_Alignof (", $c_name, ")"), ) { Some(x) => x, @@ -1048,13 +1153,14 @@ macro_rules! __internal_opaque_type { ); }; - $crate::inlinec::__impl_primitive_ptr!($name, $c_name); + $crate::inlinec::__impl_primitive_ptr!($name, $c_name, Some($c_header)); use $crate::inlinec::Opaque as _; impl $crate::inlinec::Opaque for $name {} impl $crate::inlinec::FfiType for $name { type FfiType = $name; - const C_DECL: &'static str = $crate::misc::concatcp!("BUILTIN_TY_DECL(", $c_name, ", DECLARATOR)"); + const C_TYPE: &'static str = $c_name; + const C_HEADER: Option<&'static str> = Some($c_header); } impl $crate::inlinec::FromFfi for $name { @@ -1077,7 +1183,7 @@ macro_rules! __internal_opaque_type { #[$crate::inlinec::cfunc] fn get(this: &$name) -> &$attr_ty { $crate::misc::concatcp!( - "#include <", $c_header, ">\n" + "#include \"", $c_header, "\"\n" ); $crate::misc::concatcp!( @@ -1095,7 +1201,7 @@ macro_rules! __internal_opaque_type { #[$crate::inlinec::cfunc] fn get(this: &mut $name) -> &mut $attr_ty { $crate::misc::concatcp!( - "#include <", $c_header, ">\n" + "#include \"", $c_header, "\"\n" ); $crate::misc::concatcp!( @@ -1113,7 +1219,7 @@ macro_rules! __internal_opaque_type { #[$crate::inlinec::cfunc] fn get(this: &$name) -> $attr_ty { $crate::misc::concatcp!( - "#include <", $c_header, ">\n" + "#include \"", $c_header, "\"\n" ); $crate::misc::concatcp!( diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock b/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock index 08322245ee4e018316923fd9779721fbf4f7f439..40e3bc50cb9a910c131a6e32083487eb10992605 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.lock @@ -2,12 +2,24 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "getrandom" version = "0.2.15" @@ -19,16 +31,48 @@ dependencies = [ "wasi", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "libc" -version = "0.2.164" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linkme" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566336154b9e58a4f055f6dd4cbab62c7dc0826ce3c0a04e63b2d2ecd784cdae" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "edbe595006d355eaf9ae11db92707d4338cd2384d16866131cc1afdbdd35d8d9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "lisakmod" version = "0.1.0" dependencies = [ + "anyhow", + "itertools", + "linkme", "lisakmod_macros", ] @@ -67,18 +111,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "syn" -version = "2.0.89" +version = "2.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" dependencies = [ "proc-macro2", "quote", diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.toml b/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.toml index 030518f152fe038c654e4d316ecdd0db614fb73c..dac8d0ad629fb61d71b210073de0365e0121d85c 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.toml +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["edition2024"] - [package] name = "lisakmod" version = "0.1.0" @@ -9,6 +7,9 @@ edition = "2024" crate-type = ["staticlib"] [dependencies] +anyhow = { version = "1.0", default-features = false } +itertools = {version = "0.13", default-features = false, features = ["use_alloc"]} +linkme = "0.3.31" # hashbrown = "0.15" lisakmod_macros = { path = "../lisakmod-macros" } diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/bindings.h b/lisa/_assets/kmodules/lisa/rust/lisakmod/bindings.h index 6048dcaedeb38e337ae9acf2c469fba915190f74..7d2f524aa82b03652a74f07bbe33486e44a96a7b 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/bindings.h +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/bindings.h @@ -1,6 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RUST_BINDINGS_H +#define _RUST_BINDINGS_H + +// cbindgen relies on ISO C types such as uint32_t +#include #include "rust/lisakmod/cbindgen.h" + +// rust_c_shims.h relies on types defined in cffi.h #include "rust/lisakmod-macros/cffi.h" #include "generated/rust/rust_c_shims.h" +#endif /* _RUST_BINDINGS_H */ diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/cbindgen.h b/lisa/_assets/kmodules/lisa/rust/lisakmod/cbindgen.h index 97fac2acd2a2eeae071a2d75b007121653e037bc..360d86c03daf0d23b7d7d2f94b25cb738d321c2f 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/cbindgen.h +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/cbindgen.h @@ -4,6 +4,5 @@ /* Warning, this file is autogenerated by Rust cbindgen. Don't modify this manually. */ - extern uint64_t myc_callback(uint64_t x); diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/runtime.c b/lisa/_assets/kmodules/lisa/rust/lisakmod/runtime.c index ee2b8f15db50660c170676077587a9896d3b2fd1..4e8cc8d278b6d8c830a5b1a401c0684aaff3db29 100644 --- a/lisa/_assets/kmodules/lisa/rust/lisakmod/runtime.c +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/runtime.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#include "main.h" -#include "rust/lisakmod/bindings.h" /* Basic glue between Rust runtime and kernel functions */ +#include "rust/lisakmod/bindings.h" /* C shims generated by the lisakmod_macros::inlinec module */ -#include "rust/lisakmod-macros/cffi.h" #include "generated/rust/rust_c_shims.c" diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/error.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..0f82cbddcf5af5c0e6c891c02fe7ba598fea5f5c --- /dev/null +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/error.rs @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +use alloc::{sync::Arc, vec::Vec}; +use core::{error::Error as StdError, fmt}; + +use anyhow; + +/// Cloneable error type suitable for memoization +#[derive(Clone)] +pub struct Error { + inner: Arc, +} + +macro_rules! error { + ($($args:tt)*) => { ::core::convert::Into::<$crate::error::Error>::into(::anyhow::anyhow!($($args)*)) } +} + +pub(crate) use error; + +impl Error { + #[inline] + pub fn new(err: E) -> Self + where + E: StdError + Send + Sync + 'static, + { + Error { + inner: Arc::new(err.into()), + } + } + + #[inline] + fn context(self, context: C) -> Self + where + C: fmt::Display + Send + Sync + 'static, + { + as anyhow::Context<_, _>>::context(Err(self), context) + .unwrap_err() + .into() + } +} + +impl From for Error { + fn from(err: anyhow::Error) -> Error { + Error { + inner: Arc::new(err), + } + } +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} +impl fmt::Debug for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl StdError for Error { + #[inline] + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.inner.source() + } + + // Unstable API + // #[inline] + // fn provide<'a>(&'a self, request: &mut core::Error::Request<'a>) { + // self.inner.provide() + // } +} + +/// Mirror the anyhow::Context API, but returns the original Result type rather than +/// Result +pub trait ContextExt { + fn context(self, context: C) -> Self + where + C: fmt::Display + Send + Sync + 'static; + + fn with_context(self, f: F) -> Self + where + C: fmt::Display + Send + Sync + 'static, + F: FnOnce() -> C; +} + +impl ContextExt for Result +where + E: From + Send + Sync + StdError + 'static, +{ + #[inline] + fn context(self, context: C) -> Self + where + C: fmt::Display + Send + Sync + 'static, + { + Ok(>::context(self, context)?) + } + + #[inline] + fn with_context(self, f: F) -> Self + where + C: fmt::Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + Ok(>::with_context(self, f)?) + } +} + +/// Combine multiple errors in a single value, so they can later be inspected or combined together. +#[derive(Debug)] +pub struct AllErrors { + errors: Vec, +} + +impl AllErrors +where + E: core::fmt::Display + Send + Sync + 'static, +{ + #[inline] + pub fn combine(self, err: Error) -> Result<(), Error> { + let errors = self.errors; + if errors.is_empty() { + Ok(()) + } else { + Err(errors.into_iter().fold(err, Error::context)) + } + } +} + +impl FromIterator> for AllErrors { + fn from_iter(iter: I) -> Self + where + I: IntoIterator>, + { + let mut errors = Vec::new(); + for x in iter { + if let Err(err) = x { + errors.push(err); + } + } + AllErrors { errors } + } +} + +impl FromIterator for AllErrors { + #[inline] + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + AllErrors { + errors: iter.into_iter().collect(), + } + } +} diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/features/events/mod.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/features/events/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e5081556a8fbd3f029a3e48b072cc6b80954b0d --- /dev/null +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/features/events/mod.rs @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +macro_rules! define_event_feature { + (struct $type:ident) => { + $crate::features::define_feature! { + struct $type, + name: concat!("event__", stringify!($type)), + visibility: Public, + Service: (), + Config: (), + dependencies: [], + init: |_| { + Ok($crate::lifecycle::new_lifecycle!(|services| { + $crate::runtime::printk::pr_info!("Enabling ftrace event: {}", stringify!($type)); + yield_!(Ok(::alloc::sync::Arc::new(()))); + $crate::runtime::printk::pr_info!("Disabling ftrace event: {}", stringify!($type)); + Ok(()) + })) + }, + } + }; +} +pub(crate) use define_event_feature; diff --git a/lisa/_assets/kmodules/lisa/rust/lisakmod/src/features/mod.rs b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/features/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..4e8dfd2242a1c348b6f06711154d12061099a8e6 --- /dev/null +++ b/lisa/_assets/kmodules/lisa/rust/lisakmod/src/features/mod.rs @@ -0,0 +1,532 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +pub mod events; +pub mod pixel6; + +use alloc::{sync::Arc, vec::Vec}; +use core::{ + any::{Any, TypeId, type_name}, + ffi::c_int, + fmt::Debug, +}; + +use linkme::distributed_slice; + +use crate::{ + error::{AllErrors, Error}, + graph::{Cursor, DfsPostTraversal, Graph, TraversalDirection}, + lifecycle::{self, LifeCycle, new_lifecycle}, + runtime::{ + printk::{pr_debug, pr_err}, + sync::Lock, + }, + typemap, +}; + +typemap::make_index!(pub FeaturesConfigIndex, Send); +impl typemap::KeyOf for Feat +where + Feat: 'static + Feature, +{ + type Value = Vec<::Config>; +} +pub type FeaturesConfig = typemap::TypeMap; + +typemap::make_index!(pub FeaturesServiceIndex, Send); +impl typemap::KeyOf for Feat +where + Feat: 'static + Feature, +{ + type Value = Arc<::Service>; +} +pub type FeaturesService = typemap::TypeMap; + +type LifeCycleAlias = LifeCycle::Service>, Error>; + +// Tie together a LifeCycle for a Feature and its associated Service type to guide type inference +// and allow impl blocks, unlike LifeCycleAlias. +pub struct FeatureLifeCycle { + inner: LifeCycleAlias, +} + +impl FeatureLifeCycle { + fn new(lifecycle: LifeCycleAlias) -> Self { + FeatureLifeCycle { inner: lifecycle } + } +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] +pub enum Visibility { + Public, + Private, +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct FeatureId { + type_id: TypeId, + type_name: &'static str, +} + +impl FeatureId { + fn new() -> Self { + FeatureId { + type_id: TypeId::of::(), + type_name: type_name::(), + } + } +} + +mod private { + use alloc::{boxed::Box, format, sync::Arc, vec::Vec}; + + use super::*; + use crate::{ + error::{ContextExt, Error, error}, + runtime::sync::Mutex, + }; + + pub struct LiveFeature { + lifecycle: Mutex>, + feature: Arc, + children_config: FeaturesConfig, + } + + // Split some methods in a super trait so we can have a blanket implementation for those, so the + // user only needs to implement the fundamental methods on the concrete types. + #[diagnostic::on_unimplemented( + message = "Implement the Feature trait for `{Self}` and it will gain a blanket implementation of BlanketFeature automatically" + )] + #[doc(hidden)] + pub trait BlanketFeature { + fn __configure( + self: Arc, + parents: &mut dyn Iterator, Error>>, + from_user: &FeaturesConfig, + ) -> Result; + + fn __start( + &self, + live: &LiveFeature, + parents_service: &mut FeaturesService, + start_children: &mut dyn FnMut(&mut FeaturesService) -> Result<(), Error>, + ) -> Result<(), Error>; + + fn __stop( + &self, + live: &LiveFeature, + stop_parents: &mut dyn FnMut() -> Result<(), Error>, + ) -> Result<(), Error>; + + fn __id(&self) -> FeatureId; + } + + impl BlanketFeature for Feat + where + Feat: 'static + Feature + Send + Sync, + { + fn __configure( + self: Arc, + parents: &mut dyn Iterator, Error>>, + from_user: &FeaturesConfig, + ) -> Result { + let name = self.name(); + let mut from_parents = Vec::new(); + for parent in parents { + match parent { + Ok(Some(parent)) => { + if let Some(configs) = parent.children_config.get::() { + from_parents.extend(configs.iter()) + } + } + Ok(None) => {} + Err(_) => { + return Err(error!("Could not configure parent of feature {name}")); + } + } + } + let empty = Vec::new(); + let from_user = from_user.get::().unwrap_or(&empty); + let mut for_us = from_user.iter().chain(from_parents); + let (children_config, lifecycle) = self + .configure(&mut for_us) + .with_context(|| format!("Failed to configure feature {name}"))?; + let lifecycle = FeatureLifeCycle::::new(lifecycle); + let lifecycle = Mutex::new(Box::new(lifecycle) as Box); + + Ok(LiveFeature { + feature: self as Arc, + lifecycle, + children_config, + }) + } + fn __id(&self) -> FeatureId { + FeatureId::new::() + } + + fn __start( + &self, + live: &LiveFeature, + parents_service: &mut FeaturesService, + start_children: &mut dyn FnMut(&mut FeaturesService) -> Result<(), Error>, + ) -> Result<(), Error> { + let mut lifecycle = live.lifecycle.lock(); + let lifecycle: &mut FeatureLifeCycle = lifecycle + .downcast_mut() + .expect("A LiveFeature for the wrong Feature was given"); + let lifecycle = &mut lifecycle.inner; + + let mut register_service = |service: &_| { + parents_service.insert::(Arc::clone(service)); + }; + + match lifecycle.state() { + lifecycle::State::Init | lifecycle::State::Finished(Ok(_)) => { + let mut children_service = FeaturesService::new(); + start_children(&mut children_service)?; + + // INVARIANT: if a child failed to start, then we do not attempt starting the + // current level. That means that a started node is always reachable from the + // leaves by following a path of other started nodes. Therefore, a + // partially-started graph can always be stopped by traversing it from the + // leaves. + let service = lifecycle + .start(children_service) + .with_context(|| format!("Failed to start feature {}", self.name()))?; + + register_service(service); + Ok(()) + } + lifecycle::State::Started(Ok(service)) => { + register_service(service); + Ok(()) + } + lifecycle::State::Started(Err(err)) => Err(err), + // Disallow re-starting a feature that failed to finish properly, as external + // resources may be in an unknown state. + lifecycle::State::Finished(Err(err)) => Err(err), + } + } + + fn __stop( + &self, + live: &LiveFeature, + stop_parents: &mut dyn FnMut() -> Result<(), Error>, + ) -> Result<(), Error> { + let lifecycle = &mut *live.lifecycle.lock(); + let lifecycle: &mut FeatureLifeCycle = lifecycle + .downcast_mut() + .expect("A LiveFeature for the wrong Feature was given"); + let lifecycle = &mut lifecycle.inner; + + match lifecycle.state() { + // We rely on the invariant that if a feature was not started, none of its parent + // can be started either so we don't need to explore that way. + lifecycle::State::Started(_) => { + // Bail out if we cannot stop the parents, as it would be unsafe to stop ourselves if a + // parent has failed to stop and might still be relying on us. + stop_parents()?; + + pr_debug!("Stopping feature {}", self.name()); + lifecycle + .stop() + .with_context(|| format!("Failed to stop feature {}", self.name()))?; + pr_debug!("Stopped feature {}", self.name()); + Ok(()) + } + lifecycle::State::Init => Ok(()), + lifecycle::State::Finished(res) => res, + } + } + } + + pub fn start_features(graph: &Graph>) -> Result<(), Error> { + fn process( + cursor: &Cursor<'_, Option>, + parents_service: &mut FeaturesService, + ) -> Result<(), Error> { + match cursor.value() { + Some(live) => { + let mut start_children = |children_service: &mut _| { + cursor.children().try_for_each(|child| { + process(&child, children_service).with_context(|| { + format!( + "Failed to start feature {} as child of feature {}", + match child.value() { + Some(child) => child.feature.name(), + None => "", + }, + live.feature.name() + ) + }) + }) + }; + live.feature + .__start(live, parents_service, &mut start_children) + } + None => cursor + .children() + .try_for_each(|child| process(&child, &mut FeaturesService::new())), + } + } + + let mut parents_service = FeaturesService::new(); + graph + .roots() + .try_for_each(|root| process(&root, &mut parents_service)) + } + + pub fn stop_features(graph: &Graph>) -> Result<(), Error> { + fn process(cursor: Cursor>) -> Result<(), Error> { + match cursor.value() { + Some(live) => { + let mut stop_parents = || { + cursor + .parents() + .map(process) + // Stop all the features we can and only then propagate all the issues we + // encountered. + .collect::>() + .combine(error!( + "Error while stopping children of feature {}", + &live.feature.name() + )) + }; + live.feature.__stop(live, &mut stop_parents) + } + None => cursor + .parents() + .map(process) + .collect::>() + .combine(error!("Error while stopping features",)), + } + } + + graph + .leaves() + .map(process) + .collect::>() + .combine(error!("Error while stopping features")) + } +} + +pub trait Feature: private::BlanketFeature { + // Add Self: Sized bound for all associated types so that Feature is dyn-usable. + type Service: Send + Sync + Debug + where + Self: Sized; + type Config: Send + where + Self: Sized; + + fn name(&self) -> &str; + fn visibility(&self) -> Visibility; + + // This associated method allows getting a FeatureId when specifying the dependencies without + // having a live instance of the feature. + fn id() -> FeatureId + where + Self: 'static + Sized, + { + FeatureId::new::() + } + + fn configure( + &self, + configs: &mut dyn Iterator, + ) -> Result< + ( + FeaturesConfig, + LifeCycle, Error>, + ), + Error, + > + where + Self: Sized; + + fn dependencies(&self) -> Vec { + Vec::new() + } +} + +pub fn features_lifecycle