Commit de658283 authored by Danilo Krummrich's avatar Danilo Krummrich Committed by Greg Kroah-Hartman
Browse files

rust: add firmware abstractions



Add an abstraction around the kernels firmware API to request firmware
images. The abstraction provides functions to access the firmware's size
and backing buffer.

The firmware is released once the abstraction instance is dropped.

Signed-off-by: default avatarDanilo Krummrich <dakr@redhat.com>
Acked-by: default avatarBoqun Feng <boqun.feng@gmail.com>
Link: https://lore.kernel.org/r/20240618154841.6716-3-dakr@redhat.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a674fefd
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -37,6 +37,13 @@ config FW_LOADER_DEBUG
	  SHA256 checksums to the kernel log for each firmware file that is
	  loaded.

config RUST_FW_LOADER_ABSTRACTIONS
	bool "Rust Firmware Loader abstractions"
	depends on RUST
	depends on FW_LOADER=y
	help
	  This enables the Rust abstractions for the firmware loader API.

if FW_LOADER

config FW_LOADER_PAGED_BUF
+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <kunit/test.h>
#include <linux/errname.h>
#include <linux/ethtool.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <linux/mdio.h>
#include <linux/phy.h>
+101 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

//! Firmware abstraction
//!
//! C header: [`include/linux/firmware.h`](srctree/include/linux/firmware.h")

use crate::{bindings, device::Device, error::Error, error::Result, str::CStr};
use core::ptr::NonNull;

// One of the following: `bindings::request_firmware`, `bindings::firmware_request_nowarn`,
// `firmware_request_platform`, `bindings::request_firmware_direct`
type FwFunc =
    unsafe extern "C" fn(*mut *const bindings::firmware, *const i8, *mut bindings::device) -> i32;

/// Abstraction around a C `struct firmware`.
///
/// This is a simple abstraction around the C firmware API. Just like with the C API, firmware can
/// be requested. Once requested the abstraction provides direct access to the firmware buffer as
/// `&[u8]`. The firmware is released once [`Firmware`] is dropped.
///
/// # Invariants
///
/// The pointer is valid, and has ownership over the instance of `struct firmware`.
///
/// Once requested, the `Firmware` backing buffer is not modified until it is freed when `Firmware`
/// is dropped.
///
/// # Examples
///
/// ```
/// # use kernel::{c_str, device::Device, firmware::Firmware};
///
/// # // SAFETY: *NOT* safe, just for the example to get an `ARef<Device>` instance
/// # let dev = unsafe { Device::from_raw(core::ptr::null_mut()) };
///
/// let fw = Firmware::request(c_str!("path/to/firmware.bin"), &dev).unwrap();
/// let blob = fw.data();
/// ```
pub struct Firmware(NonNull<bindings::firmware>);

impl Firmware {
    fn request_internal(name: &CStr, dev: &Device, func: FwFunc) -> Result<Self> {
        let mut fw: *mut bindings::firmware = core::ptr::null_mut();
        let pfw: *mut *mut bindings::firmware = &mut fw;

        // SAFETY: `pfw` is a valid pointer to a NULL initialized `bindings::firmware` pointer.
        // `name` and `dev` are valid as by their type invariants.
        let ret = unsafe { func(pfw as _, name.as_char_ptr(), dev.as_raw()) };
        if ret != 0 {
            return Err(Error::from_errno(ret));
        }

        // SAFETY: `func` not bailing out with a non-zero error code, guarantees that `fw` is a
        // valid pointer to `bindings::firmware`.
        Ok(Firmware(unsafe { NonNull::new_unchecked(fw) }))
    }

    /// Send a firmware request and wait for it. See also `bindings::request_firmware`.
    pub fn request(name: &CStr, dev: &Device) -> Result<Self> {
        Self::request_internal(name, dev, bindings::request_firmware)
    }

    /// Send a request for an optional firmware module. See also
    /// `bindings::firmware_request_nowarn`.
    pub fn request_nowarn(name: &CStr, dev: &Device) -> Result<Self> {
        Self::request_internal(name, dev, bindings::firmware_request_nowarn)
    }

    fn as_raw(&self) -> *mut bindings::firmware {
        self.0.as_ptr()
    }

    /// Returns the size of the requested firmware in bytes.
    pub fn size(&self) -> usize {
        // SAFETY: Safe by the type invariant.
        unsafe { (*self.as_raw()).size }
    }

    /// Returns the requested firmware as `&[u8]`.
    pub fn data(&self) -> &[u8] {
        // SAFETY: Safe by the type invariant. Additionally, `bindings::firmware` guarantees, if
        // successfully requested, that `bindings::firmware::data` has a size of
        // `bindings::firmware::size` bytes.
        unsafe { core::slice::from_raw_parts((*self.as_raw()).data, self.size()) }
    }
}

impl Drop for Firmware {
    fn drop(&mut self) {
        // SAFETY: Safe by the type invariant.
        unsafe { bindings::release_firmware(self.as_raw()) };
    }
}

// SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, which is safe to be used from
// any thread.
unsafe impl Send for Firmware {}

// SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, references to which are safe to
// be used from any thread.
unsafe impl Sync for Firmware {}
+2 −0
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@
mod build_assert;
pub mod device;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
pub mod firmware;
pub mod init;
pub mod ioctl;
#[cfg(CONFIG_KUNIT)]