Add static_call support by mirroring how C does. When the platform does not support static calls (right now, that means that it is not x86), then the function pointer is loaded from a global and called. Otherwise, we generate a call to a trampoline function, and objtool is used to make these calls patchable at runtime.
Signed-off-by: Alice Ryhl <alicer...@google.com> --- rust/kernel/lib.rs | 1 + rust/kernel/static_call.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index fbd91a48ff8b..d534b1178955 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -38,6 +38,7 @@ pub mod prelude; pub mod print; mod static_assert; +pub mod static_call; #[doc(hidden)] pub mod std_vendor; pub mod str; diff --git a/rust/kernel/static_call.rs b/rust/kernel/static_call.rs new file mode 100644 index 000000000000..f7b8ba7bf1fb --- /dev/null +++ b/rust/kernel/static_call.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Logic for static calls. + +#[macro_export] +#[doc(hidden)] +macro_rules! ty_underscore_for { + ($arg:expr) => { + _ + }; +} + +#[doc(hidden)] +#[repr(transparent)] +pub struct AddressableStaticCallKey { + _ptr: *const bindings::static_call_key, +} +unsafe impl Sync for AddressableStaticCallKey {} +impl AddressableStaticCallKey { + pub const fn new(ptr: *const bindings::static_call_key) -> Self { + Self { _ptr: ptr } + } +} + +#[cfg(CONFIG_HAVE_STATIC_CALL)] +#[doc(hidden)] +#[macro_export] +macro_rules! _static_call { + ($name:ident($($args:expr),* $(,)?)) => {{ + // Symbol mangling will give this symbol a unique name. + #[cfg(CONFIG_HAVE_STATIC_CALL_INLINE)] + #[link_section = ".discard.addressable"] + #[used] + static __ADDRESSABLE: $crate::static_call::AddressableStaticCallKey = unsafe { + $crate::static_call::AddressableStaticCallKey::new(::core::ptr::addr_of!( + $crate::macros::paste! { $crate::bindings:: [<__SCK__ $name >]; } + )) + }; + + let fn_ptr: unsafe extern "C" fn($($crate::static_call::ty_underscore_for!($args)),*) -> _ = + $crate::macros::paste! { $crate::bindings:: [<__SCT__ $name >]; }; + (fn_ptr)($($args),*) + }}; +} + +#[cfg(not(CONFIG_HAVE_STATIC_CALL))] +#[doc(hidden)] +#[macro_export] +macro_rules! _static_call { + ($name:ident($($args:expr),* $(,)?)) => {{ + let void_ptr_fn: *mut ::core::ffi::c_void = + $crate::macros::paste! { $crate::bindings:: [<__SCK__ $name >]; }.func; + + let fn_ptr: unsafe extern "C" fn($($crate::static_call::ty_underscore_for!($args)),*) -> _ = + if true { + ::core::mem::transmute(void_ptr_fn) + } else { + // This is dead code, but it influences type inference on `fn_ptr` so that we + // transmute the function pointer to the right type. + $crate::macros::paste! { $crate::bindings:: [<__SCT__ $name >]; } + }; + + (fn_ptr)($($args),*) + }}; +} + +/// Statically call a global function. +/// +/// # Safety +/// +/// This macro will call the provided function. It is up to the caller to uphold the safety +/// guarantees of the function. +/// +/// # Examples +/// +/// ```ignore +/// fn call_static() { +/// unsafe { +/// static_call! { your_static_call() }; +/// } +/// } +/// ``` +#[macro_export] +macro_rules! static_call { + // Forward to the real implementation. Separated like this so that we don't have to duplicate + // the documentation. + ($($args:tt)*) => { $crate::static_call::_static_call! { $($args)* } }; +} + +pub use {_static_call, static_call, ty_underscore_for}; -- 2.45.2.505.gda0bf45e8d-goog