Skip to content

Commit 4104105

Browse files
committed
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: Danilo Krummrich <[email protected]>
1 parent d4db1a3 commit 4104105

File tree

4 files changed

+111
-0
lines changed

4 files changed

+111
-0
lines changed

drivers/base/firmware_loader/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ config FW_LOADER_DEBUG
3737
SHA256 checksums to the kernel log for each firmware file that is
3838
loaded.
3939

40+
config RUST_FW_LOADER_ABSTRACTIONS
41+
bool "Rust Firmware Loader abstractions"
42+
depends on RUST
43+
depends on FW_LOADER=y
44+
help
45+
This enables the Rust abstractions for the firmware loader API.
46+
4047
if FW_LOADER
4148

4249
config FW_LOADER_PAGED_BUF

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <kunit/test.h>
1010
#include <linux/errname.h>
1111
#include <linux/ethtool.h>
12+
#include <linux/firmware.h>
1213
#include <linux/jiffies.h>
1314
#include <linux/mdio.h>
1415
#include <linux/phy.h>

rust/kernel/firmware.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Firmware abstraction
4+
//!
5+
//! C header: [`include/linux/firmware.h`](srctree/include/linux/firmware.h")
6+
7+
use crate::{bindings, device::Device, error::Error, error::Result, str::CStr};
8+
use core::ptr::NonNull;
9+
10+
// One of the following: `bindings::request_firmware`, `bindings::firmware_request_nowarn`,
11+
// `firmware_request_platform`, `bindings::request_firmware_direct`
12+
type FwFunc =
13+
unsafe extern "C" fn(*mut *const bindings::firmware, *const i8, *mut bindings::device) -> i32;
14+
15+
/// Abstraction around a C `struct firmware`.
16+
///
17+
/// This is a simple abstraction around the C firmware API. Just like with the C API, firmware can
18+
/// be requested. Once requested the abstraction provides direct access to the firmware buffer as
19+
/// `&[u8]`. The firmware is released once [`Firmware`] is dropped.
20+
///
21+
/// # Invariants
22+
///
23+
/// The pointer is valid, and has ownership over the instance of `struct firmware`.
24+
///
25+
/// Once requested, the `Firmware` backing buffer is not modified until it is freed when `Firmware`
26+
/// is dropped.
27+
///
28+
/// # Examples
29+
///
30+
/// ```
31+
/// # use kernel::{c_str, device::Device, firmware::Firmware};
32+
///
33+
/// # // SAFETY: *NOT* safe, just for the example to get an `ARef<Device>` instance
34+
/// # let dev = unsafe { Device::from_raw(core::ptr::null_mut()) };
35+
///
36+
/// let fw = Firmware::request(c_str!("path/to/firmware.bin"), &dev).unwrap();
37+
/// let blob = fw.data();
38+
/// ```
39+
pub struct Firmware(NonNull<bindings::firmware>);
40+
41+
impl Firmware {
42+
fn request_internal(name: &CStr, dev: &Device, func: FwFunc) -> Result<Self> {
43+
let mut fw: *mut bindings::firmware = core::ptr::null_mut();
44+
let pfw: *mut *mut bindings::firmware = &mut fw;
45+
46+
// SAFETY: `pfw` is a valid pointer to a NULL initialized `bindings::firmware` pointer.
47+
// `name` and `dev` are valid as by their type invariants.
48+
let ret = unsafe { func(pfw as _, name.as_char_ptr(), dev.as_raw()) };
49+
if ret != 0 {
50+
return Err(Error::from_errno(ret));
51+
}
52+
53+
// SAFETY: `func` not bailing out with a non-zero error code, guarantees that `fw` is a
54+
// valid pointer to `bindings::firmware`.
55+
Ok(Firmware(unsafe { NonNull::new_unchecked(fw) }))
56+
}
57+
58+
/// Send a firmware request and wait for it. See also `bindings::request_firmware`.
59+
pub fn request(name: &CStr, dev: &Device) -> Result<Self> {
60+
Self::request_internal(name, dev, bindings::request_firmware)
61+
}
62+
63+
/// Send a request for an optional firmware module. See also
64+
/// `bindings::firmware_request_nowarn`.
65+
pub fn request_nowarn(name: &CStr, dev: &Device) -> Result<Self> {
66+
Self::request_internal(name, dev, bindings::firmware_request_nowarn)
67+
}
68+
69+
fn as_raw(&self) -> *mut bindings::firmware {
70+
self.0.as_ptr()
71+
}
72+
73+
/// Returns the size of the requested firmware in bytes.
74+
pub fn size(&self) -> usize {
75+
// SAFETY: Safe by the type invariant.
76+
unsafe { (*self.as_raw()).size }
77+
}
78+
79+
/// Returns the requested firmware as `&[u8]`.
80+
pub fn data(&self) -> &[u8] {
81+
// SAFETY: Safe by the type invariant. Additionally, `bindings::firmware` guarantees, if
82+
// successfully requested, that `bindings::firmware::data` has a size of
83+
// `bindings::firmware::size` bytes.
84+
unsafe { core::slice::from_raw_parts((*self.as_raw()).data, self.size()) }
85+
}
86+
}
87+
88+
impl Drop for Firmware {
89+
fn drop(&mut self) {
90+
// SAFETY: Safe by the type invariant.
91+
unsafe { bindings::release_firmware(self.as_raw()) };
92+
}
93+
}
94+
95+
// SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, which is safe to be used from
96+
// any thread.
97+
unsafe impl Send for Firmware {}
98+
99+
// SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, references to which are safe to
100+
// be used from any thread.
101+
unsafe impl Sync for Firmware {}

rust/kernel/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub mod alloc;
3030
mod build_assert;
3131
pub mod device;
3232
pub mod error;
33+
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
34+
pub mod firmware;
3335
pub mod init;
3436
pub mod ioctl;
3537
#[cfg(CONFIG_KUNIT)]

0 commit comments

Comments
 (0)