1e052daabSZhi Wang // SPDX-License-Identifier: GPL-2.0-only 2e052daabSZhi Wang 3e052daabSZhi Wang //! Abstractions for the fwctl subsystem. 4e052daabSZhi Wang //! 5e052daabSZhi Wang //! C header: `include/linux/fwctl.h` 6e052daabSZhi Wang 7e052daabSZhi Wang use crate::{ 8e052daabSZhi Wang bindings, 9e052daabSZhi Wang container_of, 10e052daabSZhi Wang device, 11e052daabSZhi Wang prelude::*, 12e052daabSZhi Wang sync::aref::{ 13e052daabSZhi Wang ARef, 14e052daabSZhi Wang AlwaysRefCounted, // 15e052daabSZhi Wang }, 16e052daabSZhi Wang types::Opaque, // 17e052daabSZhi Wang }; 18e052daabSZhi Wang use core::{ 19e052daabSZhi Wang alloc::Layout, 20e052daabSZhi Wang cell::UnsafeCell, 21e052daabSZhi Wang marker::PhantomData, 22e052daabSZhi Wang ptr::NonNull, 23e052daabSZhi Wang slice, // 24e052daabSZhi Wang }; 25e052daabSZhi Wang 26e052daabSZhi Wang /// Returns a kmalloc-compatible allocation size for `T`. 27e052daabSZhi Wang const fn kmalloc_aligned_size<T>() -> usize { 28e052daabSZhi Wang Layout::new::<T>().pad_to_align().size() 29e052daabSZhi Wang } 30e052daabSZhi Wang 31e052daabSZhi Wang /// Represents a fwctl device type. 32e052daabSZhi Wang /// 33e052daabSZhi Wang /// Corresponds to the C `enum fwctl_device_type`. All non-error UAPI values are represented so 34e052daabSZhi Wang /// Rust drivers can select a device type without passing an untyped integer, while 35e052daabSZhi Wang /// `FWCTL_DEVICE_TYPE_ERROR` remains unrepresentable. 36e052daabSZhi Wang #[repr(u32)] 37e052daabSZhi Wang #[derive(Copy, Clone, Debug, Eq, PartialEq)] 38e052daabSZhi Wang pub enum DeviceType { 39e052daabSZhi Wang /// Mellanox ConnectX (mlx5) device. 40e052daabSZhi Wang Mlx5 = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_MLX5, 41e052daabSZhi Wang /// CXL (Compute Express Link) device. 42e052daabSZhi Wang Cxl = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_CXL, 43e052daabSZhi Wang /// AMD/Pensando PDS device. 44e052daabSZhi Wang Pds = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_PDS, 45e052daabSZhi Wang /// Broadcom NetXtreme (bnxt) device. 46e052daabSZhi Wang Bnxt = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_BNXT, 47e052daabSZhi Wang } 48e052daabSZhi Wang 49e052daabSZhi Wang /// Scope of access for an RPC request. 50e052daabSZhi Wang /// 51e052daabSZhi Wang /// Corresponds to the C `enum fwctl_rpc_scope`. 52e052daabSZhi Wang #[repr(u32)] 53e052daabSZhi Wang #[derive(Copy, Clone, Debug, Eq, PartialEq)] 54e052daabSZhi Wang pub enum RpcScope { 55e052daabSZhi Wang /// Read/write access to device configuration. 56e052daabSZhi Wang Configuration = bindings::fwctl_rpc_scope_FWCTL_RPC_CONFIGURATION, 57e052daabSZhi Wang /// Read-only access to debug information. 58e052daabSZhi Wang DebugReadOnly = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_READ_ONLY, 59e052daabSZhi Wang /// Write access to lockdown-compatible debug information. 60e052daabSZhi Wang DebugWrite = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_WRITE, 61e052daabSZhi Wang /// Full read/write access to all debug information (requires `CAP_SYS_RAWIO`). 62e052daabSZhi Wang DebugWriteFull = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_WRITE_FULL, 63e052daabSZhi Wang } 64e052daabSZhi Wang 65e052daabSZhi Wang impl TryFrom<u32> for RpcScope { 66e052daabSZhi Wang type Error = Error; 67e052daabSZhi Wang 68e052daabSZhi Wang #[inline] 69e052daabSZhi Wang fn try_from(value: u32) -> Result<Self, Error> { 70e052daabSZhi Wang match value { 71e052daabSZhi Wang v if v == Self::Configuration as u32 => Ok(Self::Configuration), 72e052daabSZhi Wang v if v == Self::DebugReadOnly as u32 => Ok(Self::DebugReadOnly), 73e052daabSZhi Wang v if v == Self::DebugWrite as u32 => Ok(Self::DebugWrite), 74e052daabSZhi Wang v if v == Self::DebugWriteFull as u32 => Ok(Self::DebugWriteFull), 75e052daabSZhi Wang _ => Err(EINVAL), 76e052daabSZhi Wang } 77e052daabSZhi Wang } 78e052daabSZhi Wang } 79e052daabSZhi Wang 80e052daabSZhi Wang /// Response from a [`Operations::fw_rpc`] call. 81e052daabSZhi Wang pub enum FwRpcResponse { 82e052daabSZhi Wang /// Reuse the input buffer as the output, with the given output length. 83e052daabSZhi Wang /// 84e052daabSZhi Wang /// The callback returns `EINVAL` if the output length exceeds the input buffer length. 85e052daabSZhi Wang InPlace(usize), 86e052daabSZhi Wang /// Return a newly allocated buffer as the output. 87e052daabSZhi Wang NewBuffer(KVVec<u8>), 88e052daabSZhi Wang } 89e052daabSZhi Wang 90e052daabSZhi Wang /// Trait implemented by each Rust driver that integrates with the fwctl subsystem. 91e052daabSZhi Wang /// 92e052daabSZhi Wang /// The implementing type **is** the per-FD user context: one instance is 93e052daabSZhi Wang /// created for each `open()` call and dropped when the FD is closed. 94e052daabSZhi Wang /// 95e052daabSZhi Wang /// Each implementation corresponds to a specific device type and provides the 96e052daabSZhi Wang /// vtable used by the core `fwctl` layer to manage per-FD user contexts and 97e052daabSZhi Wang /// handle RPC requests. 98e052daabSZhi Wang pub trait Operations: Sized + Send + Sync + 'static { 99e052daabSZhi Wang /// Data owned by the [`Registration`] and accessible during callbacks. 100e052daabSZhi Wang /// 101e052daabSZhi Wang /// The lifetime `'a` is tied to the [`Registration`] scope (which lives within the parent bus 102e052daabSZhi Wang /// device binding scope). Drivers use it to store references to resources bound to this scope, 103e052daabSZhi Wang /// such as PCI BARs or typed bus device references. 104e052daabSZhi Wang type RegistrationData<'a>: Send + Sync + 'a 105e052daabSZhi Wang where 106e052daabSZhi Wang Self: 'a; 107e052daabSZhi Wang 108e052daabSZhi Wang /// fwctl device type identifier. 109e052daabSZhi Wang const DEVICE_TYPE: DeviceType; 110e052daabSZhi Wang 111e052daabSZhi Wang /// Called when a new user context is opened. 112e052daabSZhi Wang /// 113e052daabSZhi Wang /// Returns a [`PinInit`] initializer for `Self`. The instance is dropped 114e052daabSZhi Wang /// automatically when the FD is closed (after [`close`](Self::close)). 115e052daabSZhi Wang fn open<'a>( 116e052daabSZhi Wang device: &Device<Self>, 117e052daabSZhi Wang reg_data: &Self::RegistrationData<'a>, 118e052daabSZhi Wang ) -> impl PinInit<Self, Error>; 119e052daabSZhi Wang 120e052daabSZhi Wang /// Called when the user context is closed. 121e052daabSZhi Wang /// 122e052daabSZhi Wang /// The driver may perform additional cleanup here that requires access 123e052daabSZhi Wang /// to the owning [`Device`]. `Self` is dropped automatically after this 124e052daabSZhi Wang /// returns. 125e052daabSZhi Wang fn close<'a>( 126e052daabSZhi Wang _this: Pin<&mut Self>, 127e052daabSZhi Wang _device: &Device<Self>, 128e052daabSZhi Wang _reg_data: &Self::RegistrationData<'a>, 129e052daabSZhi Wang ) { 130e052daabSZhi Wang } 131e052daabSZhi Wang 132e052daabSZhi Wang /// Return device information to userspace. 133e052daabSZhi Wang /// 134e052daabSZhi Wang /// The default implementation returns no device-specific data. 135e052daabSZhi Wang fn info<'a>( 136e052daabSZhi Wang _this: Pin<&Self>, 137e052daabSZhi Wang _device: &Device<Self>, 138e052daabSZhi Wang _reg_data: &Self::RegistrationData<'a>, 139e052daabSZhi Wang ) -> Result<KVec<u8>, Error> { 140e052daabSZhi Wang Ok(KVec::new()) 141e052daabSZhi Wang } 142e052daabSZhi Wang 143e052daabSZhi Wang /// Handle a userspace RPC request. 144e052daabSZhi Wang /// 145e052daabSZhi Wang /// `max_output_len` is the size of the userspace output buffer. A driver may return a larger 146e052daabSZhi Wang /// response to report the required size; the fwctl core copies only the bytes that fit and 147e052daabSZhi Wang /// reports the full response length to userspace. 148e052daabSZhi Wang fn fw_rpc<'a>( 149e052daabSZhi Wang this: Pin<&Self>, 150e052daabSZhi Wang device: &Device<Self>, 151e052daabSZhi Wang reg_data: &Self::RegistrationData<'a>, 152e052daabSZhi Wang scope: RpcScope, 153e052daabSZhi Wang rpc_buf: &mut [u8], 154e052daabSZhi Wang max_output_len: usize, 155e052daabSZhi Wang ) -> Result<FwRpcResponse, Error>; 156e052daabSZhi Wang } 157e052daabSZhi Wang 158e052daabSZhi Wang /// A fwctl device. 159e052daabSZhi Wang /// 160e052daabSZhi Wang /// `#[repr(C)]` with the `fwctl_device` at offset 0, matching the C `fwctl_alloc_device()` layout 161e052daabSZhi Wang /// convention. Contains a pointer to the [`Registration`]'s data, set at registration time and 162e052daabSZhi Wang /// cleared on unregistration. 163e052daabSZhi Wang /// 164e052daabSZhi Wang /// # Invariants 165e052daabSZhi Wang /// 166e052daabSZhi Wang /// - `dev` is embedded at offset 0 and is initialised by fwctl. 167e052daabSZhi Wang /// - The fwctl refcount owns the allocation lifetime. 168e052daabSZhi Wang /// - `registration_data` is either [`NonNull::dangling()`] (before registration / after 169e052daabSZhi Wang /// unregistration) or points to valid data owned by the [`Registration`]. 170e052daabSZhi Wang #[repr(C)] 171e052daabSZhi Wang pub struct Device<T: Operations> { 172e052daabSZhi Wang dev: Opaque<bindings::fwctl_device>, 173e052daabSZhi Wang registration_data: UnsafeCell<NonNull<T::RegistrationData<'static>>>, 174e052daabSZhi Wang } 175e052daabSZhi Wang 176e052daabSZhi Wang impl<T: Operations> Device<T> { 177e052daabSZhi Wang /// Allocate a new fwctl device. 178e052daabSZhi Wang /// 179e052daabSZhi Wang /// Returns an [`ARef`] that can be passed to [`Registration::new()`] 180e052daabSZhi Wang /// to make the device visible to userspace. 181e052daabSZhi Wang pub fn new(parent: &device::Device<device::Bound>) -> Result<ARef<Self>> { 182e052daabSZhi Wang const_assert!( 183e052daabSZhi Wang core::mem::offset_of!(Self, dev) == 0, 184e052daabSZhi Wang "struct fwctl_device must be at offset 0" 185e052daabSZhi Wang ); 186e052daabSZhi Wang 187e052daabSZhi Wang let size = kmalloc_aligned_size::<Self>(); 188e052daabSZhi Wang let ops = core::ptr::from_ref::<bindings::fwctl_ops>(&VTable::<T>::VTABLE).cast_mut(); 189e052daabSZhi Wang 190e052daabSZhi Wang // SAFETY: `ops` is static, `parent` is bound, and `size` is padded so the allocation made 191e052daabSZhi Wang // by `_fwctl_alloc_device` satisfies the size and alignment required by `Device<T>`. 192e052daabSZhi Wang let raw = unsafe { bindings::_fwctl_alloc_device(parent.as_raw(), ops, size) }; 193e052daabSZhi Wang let this = NonNull::new(raw.cast::<Self>()).ok_or(ENOMEM)?; 194e052daabSZhi Wang 195e052daabSZhi Wang // INVARIANT: Set `registration_data` to dangling (no registration yet). 196e052daabSZhi Wang // SAFETY: `this` points to the allocation just returned by fwctl. 197e052daabSZhi Wang unsafe { 198e052daabSZhi Wang (&raw mut (*this.as_ptr()).registration_data) 199e052daabSZhi Wang .write(UnsafeCell::new(NonNull::dangling())); 200e052daabSZhi Wang }; 201e052daabSZhi Wang 202e052daabSZhi Wang // SAFETY: `this` owns the initial reference. 203e052daabSZhi Wang Ok(unsafe { ARef::from_raw(this) }) 204e052daabSZhi Wang } 205e052daabSZhi Wang 206e052daabSZhi Wang /// Returns the underlying `fwctl_device` pointer. 207e052daabSZhi Wang #[inline] 208e052daabSZhi Wang fn as_raw(&self) -> *mut bindings::fwctl_device { 209e052daabSZhi Wang self.dev.get() 210e052daabSZhi Wang } 211e052daabSZhi Wang 212e052daabSZhi Wang /// Borrows a Rust fwctl device from its raw C pointer. 213e052daabSZhi Wang /// 214e052daabSZhi Wang /// # Safety 215e052daabSZhi Wang /// 216e052daabSZhi Wang /// `ptr` must point to a valid `fwctl_device` embedded in a [`Device<T>`]. 217e052daabSZhi Wang #[inline] 218e052daabSZhi Wang unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_device) -> &'a Self { 219e052daabSZhi Wang // SAFETY: The caller upholds the offset-0 `Device<T>` invariant. 220e052daabSZhi Wang unsafe { &*ptr.cast() } 221e052daabSZhi Wang } 222e052daabSZhi Wang 223e052daabSZhi Wang /// Invokes `f` with the registration data. 224e052daabSZhi Wang /// 225e052daabSZhi Wang /// The higher-ranked callback prevents the erased registration lifetime from escaping and 226e052daabSZhi Wang /// permits registration data that is invariant over its lifetime parameter. 227e052daabSZhi Wang /// 228e052daabSZhi Wang /// # Safety 229e052daabSZhi Wang /// 230e052daabSZhi Wang /// The caller must ensure that the device is registered and that this is called from a fwctl 231e052daabSZhi Wang /// callback protected by `registration_lock`. 232e052daabSZhi Wang #[inline] 233e052daabSZhi Wang unsafe fn with_registration_data<R>( 234e052daabSZhi Wang &self, 235e052daabSZhi Wang f: impl for<'a> FnOnce(&Device<T>, &'a T::RegistrationData<'a>) -> R, 236e052daabSZhi Wang ) -> R { 237e052daabSZhi Wang // SAFETY: Caller guarantees the device is registered, so the pointer is valid. 238e052daabSZhi Wang // Lifetimes do not affect layout. The higher-ranked callback prevents the shortened 239e052daabSZhi Wang // lifetime from escaping or being selected by the caller. 240e052daabSZhi Wang let reg_data = unsafe { 241e052daabSZhi Wang (*self.registration_data.get()) 242e052daabSZhi Wang .cast::<T::RegistrationData<'_>>() 243e052daabSZhi Wang .as_ref() 244e052daabSZhi Wang }; 245e052daabSZhi Wang 246e052daabSZhi Wang f(self, reg_data) 247e052daabSZhi Wang } 248e052daabSZhi Wang } 249e052daabSZhi Wang 250e052daabSZhi Wang impl<T: Operations> AsRef<device::Device> for Device<T> { 251e052daabSZhi Wang #[inline] 252e052daabSZhi Wang fn as_ref(&self) -> &device::Device { 253e052daabSZhi Wang // SAFETY: `self` contains a live fwctl_device. 254e052daabSZhi Wang let dev = unsafe { &raw mut (*self.as_raw()).dev }; 255e052daabSZhi Wang // SAFETY: The embedded device is initialised by fwctl. 256e052daabSZhi Wang unsafe { device::Device::from_raw(dev) } 257e052daabSZhi Wang } 258e052daabSZhi Wang } 259e052daabSZhi Wang 260e052daabSZhi Wang // SAFETY: `fwctl_get` increments the refcount of a valid fwctl_device. 261e052daabSZhi Wang // `fwctl_put` decrements it and frees the device when it reaches zero. 262e052daabSZhi Wang unsafe impl<T: Operations> AlwaysRefCounted for Device<T> { 263e052daabSZhi Wang #[inline] 264e052daabSZhi Wang fn inc_ref(&self) { 265e052daabSZhi Wang // SAFETY: `self` holds a live reference. 266e052daabSZhi Wang unsafe { bindings::fwctl_get(self.as_raw()) }; 267e052daabSZhi Wang } 268e052daabSZhi Wang 269e052daabSZhi Wang #[inline] 270e052daabSZhi Wang unsafe fn dec_ref(obj: NonNull<Self>) { 271e052daabSZhi Wang // SAFETY: The caller owns a live reference. 272e052daabSZhi Wang unsafe { bindings::fwctl_put(obj.cast().as_ptr()) }; 273e052daabSZhi Wang } 274e052daabSZhi Wang } 275e052daabSZhi Wang 276e052daabSZhi Wang // SAFETY: `Device<T>` is refcounted by the fwctl core and may be released from any thread. 277e052daabSZhi Wang unsafe impl<T: Operations> Send for Device<T> {} 278e052daabSZhi Wang 279e052daabSZhi Wang // SAFETY: Shared access to the embedded `fwctl_device` is protected by the fwctl core. The 280e052daabSZhi Wang // `registration_data` field is only mutated before registration and after unregistration (both 281e052daabSZhi Wang // single-threaded with respect to callbacks). 282e052daabSZhi Wang unsafe impl<T: Operations> Sync for Device<T> {} 283e052daabSZhi Wang 284e052daabSZhi Wang /// A registered fwctl device. 285e052daabSZhi Wang /// 286e052daabSZhi Wang /// Owns the [`RegistrationData`](Operations::RegistrationData) made available to driver callbacks. 287e052daabSZhi Wang /// The parent device lifetime ensures that [`fwctl_unregister`] runs before the parent driver 288e052daabSZhi Wang /// unbinds. 289e052daabSZhi Wang /// 290e052daabSZhi Wang /// On drop the device is unregistered (all user contexts are closed and `ops` is set to `NULL`) 291e052daabSZhi Wang /// and the registration data is dropped. 292e052daabSZhi Wang /// 293e052daabSZhi Wang /// [`fwctl_unregister`]: srctree/drivers/fwctl/main.c 294e052daabSZhi Wang pub struct Registration<'a, T: Operations> { 295e052daabSZhi Wang dev: ARef<Device<T>>, 296e052daabSZhi Wang _reg_data: Pin<KBox<T::RegistrationData<'a>>>, 297e052daabSZhi Wang } 298e052daabSZhi Wang 299e052daabSZhi Wang impl<'a, T: Operations> Registration<'a, T> { 300e052daabSZhi Wang /// Register a previously allocated fwctl device with the given registration data. 301e052daabSZhi Wang /// 302e052daabSZhi Wang /// The `reg_data` is owned by the registration and accessible during callbacks. 303e052daabSZhi Wang /// 304e052daabSZhi Wang /// # Safety 305e052daabSZhi Wang /// 306e052daabSZhi Wang /// Callers must not `mem::forget()` the returned [`Registration`] or otherwise prevent its 307e052daabSZhi Wang /// [`Drop`] implementation from running, since `fwctl_unregister` must be called before the 308e052daabSZhi Wang /// parent device is unbound. 309e052daabSZhi Wang /// 310e052daabSZhi Wang /// `dev` must be an unregistered [`Device`] that is not associated with any live 311e052daabSZhi Wang /// [`Registration`], and no other thread may attempt to register the same device concurrently. 312e052daabSZhi Wang pub unsafe fn new( 313e052daabSZhi Wang parent: &'a device::Device<device::Bound>, 314e052daabSZhi Wang dev: &Device<T>, 315e052daabSZhi Wang reg_data: impl PinInit<T::RegistrationData<'a>, Error>, 316e052daabSZhi Wang ) -> Result<Self> { 317e052daabSZhi Wang let actual_parent = dev.as_ref().parent().ok_or(EINVAL)?; 318e052daabSZhi Wang let parent_device: &device::Device = parent; 319e052daabSZhi Wang if !core::ptr::eq(actual_parent, parent_device) { 320e052daabSZhi Wang return Err(EINVAL); 321e052daabSZhi Wang } 322e052daabSZhi Wang 323e052daabSZhi Wang let reg_data: Pin<KBox<T::RegistrationData<'a>>> = KBox::pin_init(reg_data, GFP_KERNEL)?; 324e052daabSZhi Wang 325e052daabSZhi Wang // Store the registration data pointer in the device before registration, so that it is 326e052daabSZhi Wang // visible once callbacks can be invoked. The `'static` type is only an erased storage 327e052daabSZhi Wang // handle; callbacks access the pointer through a higher-ranked closure. 328e052daabSZhi Wang let ptr: NonNull<T::RegistrationData<'static>> = 329e052daabSZhi Wang NonNull::from(Pin::get_ref(reg_data.as_ref())).cast(); 330e052daabSZhi Wang 331e052daabSZhi Wang // SAFETY: No concurrent access; the device is not yet registered. 332e052daabSZhi Wang unsafe { *dev.registration_data.get() = ptr }; 333e052daabSZhi Wang 334e052daabSZhi Wang // SAFETY: `dev` is a valid fwctl_device backed by an ARef. 335e052daabSZhi Wang let ret = unsafe { bindings::fwctl_register(dev.as_raw()) }; 336e052daabSZhi Wang if ret != 0 { 337e052daabSZhi Wang // SAFETY: No concurrent readers; registration failed. 338e052daabSZhi Wang unsafe { *dev.registration_data.get() = NonNull::dangling() }; 339e052daabSZhi Wang return Err(Error::from_errno(ret)); 340e052daabSZhi Wang } 341e052daabSZhi Wang 342e052daabSZhi Wang Ok(Self { 343e052daabSZhi Wang dev: dev.into(), 344e052daabSZhi Wang _reg_data: reg_data, 345e052daabSZhi Wang }) 346e052daabSZhi Wang } 347e052daabSZhi Wang } 348e052daabSZhi Wang 349e052daabSZhi Wang impl<T: Operations> Drop for Registration<'_, T> { 350e052daabSZhi Wang fn drop(&mut self) { 351e052daabSZhi Wang // SAFETY: The Registration lifetime guarantees that the parent device is still bound. 352e052daabSZhi Wang // `fwctl_unregister` takes the write lock, closes all user contexts, and sets ops=NULL. 353e052daabSZhi Wang // After it returns, no callbacks can be running or will run. 354e052daabSZhi Wang unsafe { bindings::fwctl_unregister(self.dev.as_raw()) }; 355e052daabSZhi Wang 356e052daabSZhi Wang // SAFETY: `fwctl_unregister` guarantees no concurrent readers. 357e052daabSZhi Wang unsafe { *self.dev.registration_data.get() = NonNull::dangling() }; 358e052daabSZhi Wang 359e052daabSZhi Wang // `self._reg_data` is dropped here, after callbacks have stopped. 360e052daabSZhi Wang } 361e052daabSZhi Wang } 362e052daabSZhi Wang 363e052daabSZhi Wang /// Internal per-FD user context wrapping `struct fwctl_uctx` and `T`. 364e052daabSZhi Wang /// 365e052daabSZhi Wang /// Not exposed to drivers; they work with `&T` / `Pin<&mut T>` directly. 366e052daabSZhi Wang #[repr(C)] 367e052daabSZhi Wang #[pin_data] 368e052daabSZhi Wang struct UserCtx<T: Operations> { 369e052daabSZhi Wang #[pin] 370e052daabSZhi Wang fwctl_uctx: Opaque<bindings::fwctl_uctx>, 371e052daabSZhi Wang #[pin] 372e052daabSZhi Wang uctx: T, 373e052daabSZhi Wang } 374e052daabSZhi Wang 375e052daabSZhi Wang impl<T: Operations> UserCtx<T> { 376e052daabSZhi Wang /// Borrows a pinned Rust user context from its raw C pointer. 377e052daabSZhi Wang /// 378e052daabSZhi Wang /// # Safety 379e052daabSZhi Wang /// 380e052daabSZhi Wang /// `ptr` must point to a `fwctl_uctx` embedded in a live, pinned `UserCtx<T>` that remains 381e052daabSZhi Wang /// valid and does not move for the duration of `'a`. 382e052daabSZhi Wang #[inline] 383e052daabSZhi Wang unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_uctx) -> Pin<&'a Self> { 384e052daabSZhi Wang // SAFETY: The caller upholds the `UserCtx<T>` embedding, lifetime, and pinning invariants. 385e052daabSZhi Wang unsafe { Pin::new_unchecked(&*container_of!(Opaque::cast_from(ptr), Self, fwctl_uctx)) } 386e052daabSZhi Wang } 387e052daabSZhi Wang 388e052daabSZhi Wang /// Mutably borrows a pinned Rust user context from its raw C pointer. 389e052daabSZhi Wang /// 390e052daabSZhi Wang /// # Safety 391e052daabSZhi Wang /// 392e052daabSZhi Wang /// - `ptr` must point to a `fwctl_uctx` embedded in a live, pinned `UserCtx<T>` that remains 393e052daabSZhi Wang /// valid and does not move for the duration of `'a`. 394e052daabSZhi Wang /// - The caller must ensure exclusive access to the `UserCtx<T>` for the duration of `'a`. 395e052daabSZhi Wang #[inline] 396e052daabSZhi Wang unsafe fn from_raw_mut<'a>(ptr: *mut bindings::fwctl_uctx) -> Pin<&'a mut Self> { 397e052daabSZhi Wang // SAFETY: The caller upholds the embedding, lifetime, pinning, and exclusivity invariants. 398e052daabSZhi Wang unsafe { 399e052daabSZhi Wang Pin::new_unchecked( 400e052daabSZhi Wang &mut *container_of!(Opaque::cast_from(ptr), Self, fwctl_uctx).cast_mut(), 401e052daabSZhi Wang ) 402e052daabSZhi Wang } 403e052daabSZhi Wang } 404e052daabSZhi Wang 405e052daabSZhi Wang /// Returns a reference to the fwctl [`Device`] that owns this context. 406e052daabSZhi Wang #[inline] 407e052daabSZhi Wang fn device(self: Pin<&Self>) -> &Device<T> { 408e052daabSZhi Wang // SAFETY: fwctl initialises this pointer before any driver callback. 409e052daabSZhi Wang let raw_fwctl = unsafe { (*self.fwctl_uctx.get()).fwctl }; 410e052daabSZhi Wang // SAFETY: Rust fwctl devices use the offset-0 `Device<T>` layout. 411e052daabSZhi Wang unsafe { Device::from_raw(raw_fwctl) } 412e052daabSZhi Wang } 413e052daabSZhi Wang 414e052daabSZhi Wang /// Returns a pinned reference to the driver context. 415e052daabSZhi Wang #[inline] 416e052daabSZhi Wang fn uctx(self: Pin<&Self>) -> Pin<&T> { 417e052daabSZhi Wang ::pin_init::assert_pinned!(UserCtx<T>, uctx, T, inline); 418e052daabSZhi Wang 419e052daabSZhi Wang // SAFETY: `uctx` is structurally pinned. 420e052daabSZhi Wang unsafe { self.map_unchecked(|ctx| &ctx.uctx) } 421e052daabSZhi Wang } 422e052daabSZhi Wang } 423e052daabSZhi Wang 424e052daabSZhi Wang /// Static vtable mapping Rust trait methods to C callbacks. 425e052daabSZhi Wang struct VTable<T: Operations>(PhantomData<T>); 426e052daabSZhi Wang 427e052daabSZhi Wang impl<T: Operations> VTable<T> { 428e052daabSZhi Wang /// The fwctl operations vtable for this driver type. 429e052daabSZhi Wang const VTABLE: bindings::fwctl_ops = bindings::fwctl_ops { 430e052daabSZhi Wang // CAST: `DeviceType` has the same `u32` representation as the C enum field. 431e052daabSZhi Wang device_type: T::DEVICE_TYPE as u32, 432e052daabSZhi Wang uctx_size: kmalloc_aligned_size::<UserCtx<T>>(), 433e052daabSZhi Wang open_uctx: Some(Self::open_uctx_callback), 434e052daabSZhi Wang close_uctx: Some(Self::close_uctx_callback), 435e052daabSZhi Wang info: Some(Self::info_callback), 436e052daabSZhi Wang fw_rpc: Some(Self::fw_rpc_callback), 437e052daabSZhi Wang }; 438e052daabSZhi Wang 439e052daabSZhi Wang /// Initialises a newly opened Rust user context. 440e052daabSZhi Wang /// 441e052daabSZhi Wang /// # Safety 442e052daabSZhi Wang /// 443e052daabSZhi Wang /// `uctx` must be a valid `fwctl_uctx` embedded in a `UserCtx<T>` with 444e052daabSZhi Wang /// sufficient allocated space for the uctx field. 445e052daabSZhi Wang unsafe extern "C" fn open_uctx_callback(uctx: *mut bindings::fwctl_uctx) -> ffi::c_int { 446e052daabSZhi Wang const_assert!( 447e052daabSZhi Wang core::mem::offset_of!(UserCtx<T>, fwctl_uctx) == 0, 448e052daabSZhi Wang "struct fwctl_uctx must be at offset 0" 449e052daabSZhi Wang ); 450e052daabSZhi Wang 451e052daabSZhi Wang // SAFETY: fwctl sets this pointer before calling `open_uctx`. 452e052daabSZhi Wang let raw_fwctl = unsafe { (*uctx).fwctl }; 453e052daabSZhi Wang // SAFETY: Rust fwctl devices use the offset-0 `Device<T>` layout. 454e052daabSZhi Wang let device = unsafe { Device::<T>::from_raw(raw_fwctl) }; 455e052daabSZhi Wang 456e052daabSZhi Wang let uctx_offset = core::mem::offset_of!(UserCtx<T>, uctx); 457e052daabSZhi Wang // SAFETY: `uctx_size` reserves space for the full `UserCtx<T>`. 458e052daabSZhi Wang let uctx_ptr: *mut T = unsafe { uctx.byte_add(uctx_offset).cast() }; 459e052daabSZhi Wang 460e052daabSZhi Wang // SAFETY: `open_uctx` is called under `registration_lock` read, so the device is 461e052daabSZhi Wang // registered. `uctx_ptr` addresses the uninitialised pinned context reserved by 462e052daabSZhi Wang // `uctx_size`. 463e052daabSZhi Wang unsafe { 464e052daabSZhi Wang device.with_registration_data(|device, reg_data| { 465*98f21c54SLinus Torvalds match pin_init::raw_try_init(uctx_ptr, T::open(device, reg_data)) { 466e052daabSZhi Wang Ok(()) => 0, 467e052daabSZhi Wang Err(e) => e.to_errno(), 468e052daabSZhi Wang } 469e052daabSZhi Wang }) 470e052daabSZhi Wang } 471e052daabSZhi Wang } 472e052daabSZhi Wang 473e052daabSZhi Wang /// Closes and drops an opened Rust user context. 474e052daabSZhi Wang /// 475e052daabSZhi Wang /// # Safety 476e052daabSZhi Wang /// 477e052daabSZhi Wang /// `uctx` must point to a fully initialised `UserCtx<T>`. 478e052daabSZhi Wang unsafe extern "C" fn close_uctx_callback(uctx: *mut bindings::fwctl_uctx) { 479e052daabSZhi Wang // SAFETY: fwctl keeps the owning device live for this callback. 480e052daabSZhi Wang let device = unsafe { Device::<T>::from_raw((*uctx).fwctl) }; 481e052daabSZhi Wang 482e052daabSZhi Wang // SAFETY: close is called for an opened Rust user context. 483e052daabSZhi Wang let mut ctx = unsafe { UserCtx::<T>::from_raw_mut(uctx) }; 484e052daabSZhi Wang 485e052daabSZhi Wang // SAFETY: `close_uctx` is called under `registration_lock` write (from 486e052daabSZhi Wang // `fwctl_unregister`) or read (from `fwctl_fops_release`), so the device is registered. 487e052daabSZhi Wang unsafe { 488e052daabSZhi Wang device.with_registration_data(|device, reg_data| { 489e052daabSZhi Wang T::close(ctx.as_mut().project().uctx, device, reg_data); 490e052daabSZhi Wang }); 491e052daabSZhi Wang } 492e052daabSZhi Wang 493e052daabSZhi Wang // SAFETY: close is the last callback before fwctl frees the allocation. 494e052daabSZhi Wang unsafe { core::ptr::drop_in_place(ctx.project().uctx.get_unchecked_mut()) }; 495e052daabSZhi Wang } 496e052daabSZhi Wang 497e052daabSZhi Wang /// Returns device-specific information for an opened Rust user context. 498e052daabSZhi Wang /// 499e052daabSZhi Wang /// # Safety 500e052daabSZhi Wang /// 501e052daabSZhi Wang /// - `uctx` must point to a fully initialised `UserCtx<T>`. 502e052daabSZhi Wang /// - `length` must be a valid pointer. 503e052daabSZhi Wang unsafe extern "C" fn info_callback( 504e052daabSZhi Wang uctx: *mut bindings::fwctl_uctx, 505e052daabSZhi Wang length: *mut usize, 506e052daabSZhi Wang ) -> *mut ffi::c_void { 507e052daabSZhi Wang // SAFETY: info is called for an opened Rust user context. 508e052daabSZhi Wang let ctx = unsafe { UserCtx::<T>::from_raw(uctx) }; 509e052daabSZhi Wang let device = ctx.device(); 510e052daabSZhi Wang 511e052daabSZhi Wang // SAFETY: `info` is called under `registration_lock` read, so the device is registered. 512e052daabSZhi Wang let result = unsafe { 513e052daabSZhi Wang device.with_registration_data(|device, reg_data| T::info(ctx.uctx(), device, reg_data)) 514e052daabSZhi Wang }; 515e052daabSZhi Wang 516e052daabSZhi Wang match result { 517e052daabSZhi Wang Ok(kvec) if kvec.is_empty() => { 518e052daabSZhi Wang // SAFETY: `length` is a valid out-parameter. 519e052daabSZhi Wang unsafe { *length = 0 }; 520e052daabSZhi Wang // Return NULL for empty data; kfree(NULL) is safe. 521e052daabSZhi Wang core::ptr::null_mut() 522e052daabSZhi Wang } 523e052daabSZhi Wang Ok(kvec) => { 524e052daabSZhi Wang let (ptr, len, _cap) = kvec.into_raw_parts(); 525e052daabSZhi Wang // SAFETY: `length` is a valid out-parameter. 526e052daabSZhi Wang unsafe { *length = len }; 527e052daabSZhi Wang ptr.cast::<ffi::c_void>() 528e052daabSZhi Wang } 529e052daabSZhi Wang Err(e) => Error::to_ptr(e), 530e052daabSZhi Wang } 531e052daabSZhi Wang } 532e052daabSZhi Wang 533e052daabSZhi Wang /// Dispatches a firmware RPC for an opened Rust user context. 534e052daabSZhi Wang /// 535e052daabSZhi Wang /// # Safety 536e052daabSZhi Wang /// 537e052daabSZhi Wang /// - `uctx` must point to a fully initialised `UserCtx<T>`. 538e052daabSZhi Wang /// - `rpc_in` must be valid, initialised, and exclusively accessible for `in_len` bytes. 539e052daabSZhi Wang /// - `out_len` must be valid for reading and writing an initialised `usize`. 540e052daabSZhi Wang unsafe extern "C" fn fw_rpc_callback( 541e052daabSZhi Wang uctx: *mut bindings::fwctl_uctx, 542e052daabSZhi Wang scope: u32, 543e052daabSZhi Wang rpc_in: *mut ffi::c_void, 544e052daabSZhi Wang in_len: usize, 545e052daabSZhi Wang out_len: *mut usize, 546e052daabSZhi Wang ) -> *mut ffi::c_void { 547e052daabSZhi Wang let scope = match RpcScope::try_from(scope) { 548e052daabSZhi Wang Ok(s) => s, 549e052daabSZhi Wang Err(e) => return Error::to_ptr(e), 550e052daabSZhi Wang }; 551e052daabSZhi Wang 552e052daabSZhi Wang // SAFETY: `out_len` points to an initialised `usize` supplied by fwctl. 553e052daabSZhi Wang let max_output_len = unsafe { *out_len }; 554e052daabSZhi Wang 555e052daabSZhi Wang // SAFETY: RPC is called for an opened Rust user context. 556e052daabSZhi Wang let ctx = unsafe { UserCtx::<T>::from_raw(uctx) }; 557e052daabSZhi Wang let device = ctx.device(); 558e052daabSZhi Wang 559e052daabSZhi Wang // SAFETY: fwctl passes an exclusively owned buffer that is valid and initialised for 560e052daabSZhi Wang // `in_len` bytes. It remains live for the duration of this callback. 561e052daabSZhi Wang let rpc_buf = unsafe { slice::from_raw_parts_mut(rpc_in.cast::<u8>(), in_len) }; 562e052daabSZhi Wang 563e052daabSZhi Wang // SAFETY: `fw_rpc` is called under `registration_lock` read, so the device is registered. 564e052daabSZhi Wang let result = unsafe { 565e052daabSZhi Wang device.with_registration_data(|device, reg_data| { 566e052daabSZhi Wang T::fw_rpc(ctx.uctx(), device, reg_data, scope, rpc_buf, max_output_len) 567e052daabSZhi Wang }) 568e052daabSZhi Wang }; 569e052daabSZhi Wang 570e052daabSZhi Wang let (response, response_len) = match result { 571e052daabSZhi Wang Ok(FwRpcResponse::InPlace(len)) => { 572e052daabSZhi Wang if len > in_len { 573e052daabSZhi Wang return Error::to_ptr(EINVAL); 574e052daabSZhi Wang } 575e052daabSZhi Wang 576e052daabSZhi Wang (rpc_in, len) 577e052daabSZhi Wang } 578e052daabSZhi Wang Ok(FwRpcResponse::NewBuffer(kvec)) if kvec.is_empty() => { 579e052daabSZhi Wang // Return NULL for empty data; kvfree(NULL) is safe. 580e052daabSZhi Wang (core::ptr::null_mut(), 0) 581e052daabSZhi Wang } 582e052daabSZhi Wang Ok(FwRpcResponse::NewBuffer(kvec)) => { 583e052daabSZhi Wang let (ptr, len, _cap) = kvec.into_raw_parts(); 584e052daabSZhi Wang (ptr.cast::<ffi::c_void>(), len) 585e052daabSZhi Wang } 586e052daabSZhi Wang Err(e) => return Error::to_ptr(e), 587e052daabSZhi Wang }; 588e052daabSZhi Wang 589e052daabSZhi Wang // SAFETY: `out_len` is a valid out-parameter. 590e052daabSZhi Wang unsafe { *out_len = response_len }; 591e052daabSZhi Wang response 592e052daabSZhi Wang } 593e052daabSZhi Wang } 594