rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/2] Rust abstractions for Device & Firmware
@ 2024-06-18 15:48 Danilo Krummrich
  2024-06-18 15:48 ` [PATCH v4 1/2] rust: add abstraction for struct device Danilo Krummrich
  2024-06-18 15:48 ` [PATCH v4 2/2] rust: add firmware abstractions Danilo Krummrich
  0 siblings, 2 replies; 13+ messages in thread
From: Danilo Krummrich @ 2024-06-18 15:48 UTC (permalink / raw)
  To: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, gary, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude
  Cc: rust-for-linux, linux-kernel, Danilo Krummrich

Hi,

as agreed in [1] this is the separate series for the device and firmware
abstractions to unblock the inclusion of Fujita's PHY driver.

Originally, those patches were part of the patch series [2][3].

Changes in v4
=============
- add invariant of unmodified backing buffer to `Firmware` (Boqun)

Changes in v3
=============
- fix safety comments for `Send` and `Sync` for `Device` (Boqun)
- add corresponding invariant and safety requirement for `Device::from_raw`
  (Boqun)
- drop `Firmware::request_platform` and `Firmware::request_direct` (Greg)
- fix missing Kconfig entry for `Firmware` (Greg)
- fix doctest compilation for `Firmware`

Changes in v2
=============
- use the 'srctree/' notation
- expand the existing documentation and make it more unambiguous
- use `NonNull` in `Firmware`
- generalize the `Firmware` request functions
- add missing safety comments to `Firmware`

[1] https://lore.kernel.org/rust-for-linux/2024060745-palatable-dragging-32d1@gregkh/
[2] https://lore.kernel.org/rust-for-linux/20240520172554.182094-1-dakr@redhat.com/
[3] https://lore.kernel.org/rust-for-linux/20240520172059.181256-1-dakr@redhat.com/


Danilo Krummrich (2):
  rust: add abstraction for struct device
  rust: add firmware abstractions

 drivers/base/firmware_loader/Kconfig |   7 ++
 rust/bindings/bindings_helper.h      |   1 +
 rust/helpers.c                       |   1 +
 rust/kernel/device.rs                | 102 +++++++++++++++++++++++++++
 rust/kernel/firmware.rs              | 101 ++++++++++++++++++++++++++
 rust/kernel/lib.rs                   |   3 +
 6 files changed, 215 insertions(+)
 create mode 100644 rust/kernel/device.rs
 create mode 100644 rust/kernel/firmware.rs


base-commit: 1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0
-- 
2.45.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 1/2] rust: add abstraction for struct device
  2024-06-18 15:48 [PATCH v4 0/2] Rust abstractions for Device & Firmware Danilo Krummrich
@ 2024-06-18 15:48 ` Danilo Krummrich
  2024-06-19  8:53   ` Benno Lossin
  2024-06-20 13:37   ` Gary Guo
  2024-06-18 15:48 ` [PATCH v4 2/2] rust: add firmware abstractions Danilo Krummrich
  1 sibling, 2 replies; 13+ messages in thread
From: Danilo Krummrich @ 2024-06-18 15:48 UTC (permalink / raw)
  To: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, gary, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude
  Cc: rust-for-linux, linux-kernel, Danilo Krummrich

Add an (always) reference-counted abstraction for a generic C `struct
device`. This abstraction encapsulates existing `struct device` instances
and manages its reference count.

Subsystems may use this abstraction as a base to abstract subsystem
specific device instances based on a generic `struct device`, such as
`struct pci_dev`.

Co-developed-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Danilo Krummrich <dakr@redhat.com>
---
 rust/helpers.c        |   1 +
 rust/kernel/device.rs | 102 ++++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs    |   1 +
 3 files changed, 104 insertions(+)
 create mode 100644 rust/kernel/device.rs

diff --git a/rust/helpers.c b/rust/helpers.c
index 2c37a0f5d7a8..0e02b2c64c72 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -23,6 +23,7 @@
 #include <kunit/test-bug.h>
 #include <linux/bug.h>
 #include <linux/build_bug.h>
+#include <linux/device.h>
 #include <linux/err.h>
 #include <linux/errname.h>
 #include <linux/mutex.h>
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
new file mode 100644
index 000000000000..e445e87fb7d7
--- /dev/null
+++ b/rust/kernel/device.rs
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Generic devices that are part of the kernel's driver model.
+//!
+//! C header: [`include/linux/device.h`](srctree/include/linux/device.h)
+
+use crate::{
+    bindings,
+    types::{ARef, Opaque},
+};
+use core::ptr;
+
+/// A reference-counted device.
+///
+/// This structure represents the Rust abstraction for a C `struct device`. This implementation
+/// abstracts the usage of an already existing C `struct device` within Rust code that we get
+/// passed from the C side.
+///
+/// An instance of this abstraction can be obtained temporarily or permanent.
+///
+/// A temporary one is bound to the lifetime of the C `struct device` pointer used for creation.
+/// A permanent instance is always reference-counted and hence not restricted by any lifetime
+/// boundaries.
+///
+/// For subsystems it is recommended to create a permanent instance to wrap into a subsystem
+/// specific device structure (e.g. `pci::Device`). This is useful for passing it to drivers in
+/// `T::probe()`, such that a driver can store the `ARef<Device>` (equivalent to storing a
+/// `struct device` pointer in a C driver) for arbitrary purposes, e.g. allocating DMA coherent
+/// memory.
+///
+/// # Invariants
+///
+/// The pointer stored in `Self` is non-null and valid for the lifetime of the `ARef` instance. In
+/// particular, the `ARef` instance owns an increment on the underlying object’s reference count.
+///
+/// `bindings::device::release` is valid to be called from any thread, hence `ARef<Device>` can be
+/// dropped from any thread.
+#[repr(transparent)]
+pub struct Device(Opaque<bindings::device>);
+
+impl Device {
+    /// Creates a new reference-counted abstraction instance of an existing `struct device` pointer.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count,
+    /// i.e. it must be ensured that the reference count of the C `struct device` `ptr` points to
+    /// can't drop to zero, for the duration of this function call.
+    ///
+    /// It must also be ensured that `bindings::device::release` can be called from any thread.
+    /// While not officially documented, this should be the case for any `struct device`.
+    pub unsafe fn from_raw(ptr: *mut bindings::device) -> ARef<Self> {
+        // SAFETY: By the safety requirements, ptr is valid.
+        // Initially increase the reference count by one to compensate for the final decrement once
+        // this newly created `ARef<Device>` instance is dropped.
+        unsafe { bindings::get_device(ptr) };
+
+        // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::device`.
+        let ptr = ptr.cast::<Self>();
+
+        // SAFETY: By the safety requirements, ptr is valid.
+        unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(ptr)) }
+    }
+
+    /// Obtain the raw `struct device *`.
+    pub(crate) fn as_raw(&self) -> *mut bindings::device {
+        self.0.get()
+    }
+
+    /// Convert a raw C `struct device` pointer to a `&'a Device`.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count,
+    /// i.e. it must be ensured that the reference count of the C `struct device` `ptr` points to
+    /// can't drop to zero, for the duration of this function call and the entire duration when the
+    /// returned reference exists.
+    pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) -> &'a Self {
+        // SAFETY: Guaranteed by the safety requirements of the function.
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl crate::types::AlwaysRefCounted for Device {
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+        unsafe { bindings::get_device(self.as_raw()) };
+    }
+
+    unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::put_device(obj.cast().as_ptr()) }
+    }
+}
+
+// SAFETY: As by the type invariant `Device` can be sent to any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: `Device` can be shared among threads because all immutable methods are protected by the
+// synchronization in `struct device`.
+unsafe impl Sync for Device {}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index fbd91a48ff8b..dd1207f1a873 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -28,6 +28,7 @@
 
 pub mod alloc;
 mod build_assert;
+pub mod device;
 pub mod error;
 pub mod init;
 pub mod ioctl;
-- 
2.45.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-18 15:48 [PATCH v4 0/2] Rust abstractions for Device & Firmware Danilo Krummrich
  2024-06-18 15:48 ` [PATCH v4 1/2] rust: add abstraction for struct device Danilo Krummrich
@ 2024-06-18 15:48 ` Danilo Krummrich
  2024-06-18 16:27   ` Boqun Feng
                     ` (2 more replies)
  1 sibling, 3 replies; 13+ messages in thread
From: Danilo Krummrich @ 2024-06-18 15:48 UTC (permalink / raw)
  To: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, gary, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude
  Cc: rust-for-linux, linux-kernel, Danilo Krummrich

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 <dakr@redhat.com>
---
 drivers/base/firmware_loader/Kconfig |   7 ++
 rust/bindings/bindings_helper.h      |   1 +
 rust/kernel/firmware.rs              | 101 +++++++++++++++++++++++++++
 rust/kernel/lib.rs                   |   2 +
 4 files changed, 111 insertions(+)
 create mode 100644 rust/kernel/firmware.rs

diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
index 5ca00e02fe82..a03701674265 100644
--- a/drivers/base/firmware_loader/Kconfig
+++ b/drivers/base/firmware_loader/Kconfig
@@ -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
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index ddb5644d4fd9..18a3f05115cb 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -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>
diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
new file mode 100644
index 000000000000..b55ea1b45368
--- /dev/null
+++ b/rust/kernel/firmware.rs
@@ -0,0 +1,101 @@
+// 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 {}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index dd1207f1a873..7707cb013ce9 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -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)]
-- 
2.45.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-18 15:48 ` [PATCH v4 2/2] rust: add firmware abstractions Danilo Krummrich
@ 2024-06-18 16:27   ` Boqun Feng
  2024-06-19  8:58   ` Benno Lossin
  2024-06-20 13:36   ` Gary Guo
  2 siblings, 0 replies; 13+ messages in thread
From: Boqun Feng @ 2024-06-18 16:27 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	gary, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl, airlied,
	fujita.tomonori, pstanner, ajanulgu, lyude, rust-for-linux,
	linux-kernel

On Tue, Jun 18, 2024 at 05:48:35PM +0200, Danilo Krummrich wrote:
> 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 <dakr@redhat.com>

Acked-by: Boqun Feng <boqun.feng@gmail.com>

Thanks!

Regards,
Boqun

> ---
>  drivers/base/firmware_loader/Kconfig |   7 ++
>  rust/bindings/bindings_helper.h      |   1 +
>  rust/kernel/firmware.rs              | 101 +++++++++++++++++++++++++++
>  rust/kernel/lib.rs                   |   2 +
>  4 files changed, 111 insertions(+)
>  create mode 100644 rust/kernel/firmware.rs
> 
> diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
> index 5ca00e02fe82..a03701674265 100644
> --- a/drivers/base/firmware_loader/Kconfig
> +++ b/drivers/base/firmware_loader/Kconfig
> @@ -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
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index ddb5644d4fd9..18a3f05115cb 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -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>
> diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
> new file mode 100644
> index 000000000000..b55ea1b45368
> --- /dev/null
> +++ b/rust/kernel/firmware.rs
> @@ -0,0 +1,101 @@
> +// 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 {}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index dd1207f1a873..7707cb013ce9 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -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)]
> -- 
> 2.45.1
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 1/2] rust: add abstraction for struct device
  2024-06-18 15:48 ` [PATCH v4 1/2] rust: add abstraction for struct device Danilo Krummrich
@ 2024-06-19  8:53   ` Benno Lossin
  2024-06-20 13:37   ` Gary Guo
  1 sibling, 0 replies; 13+ messages in thread
From: Benno Lossin @ 2024-06-19  8:53 UTC (permalink / raw)
  To: Danilo Krummrich, gregkh, rafael, mcgrof, russ.weight, ojeda,
	alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh, a.hindborg,
	aliceryhl, airlied, fujita.tomonori, pstanner, ajanulgu, lyude
  Cc: rust-for-linux, linux-kernel

On 18.06.24 17:48, Danilo Krummrich wrote:
> Add an (always) reference-counted abstraction for a generic C `struct
> device`. This abstraction encapsulates existing `struct device` instances
> and manages its reference count.
> 
> Subsystems may use this abstraction as a base to abstract subsystem
> specific device instances based on a generic `struct device`, such as
> `struct pci_dev`.
> 
> Co-developed-by: Wedson Almeida Filho <wedsonaf@gmail.com>
> Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
> Signed-off-by: Danilo Krummrich <dakr@redhat.com>
> ---
>  rust/helpers.c        |   1 +
>  rust/kernel/device.rs | 102 ++++++++++++++++++++++++++++++++++++++++++
>  rust/kernel/lib.rs    |   1 +
>  3 files changed, 104 insertions(+)
>  create mode 100644 rust/kernel/device.rs
> 
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 2c37a0f5d7a8..0e02b2c64c72 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -23,6 +23,7 @@
>  #include <kunit/test-bug.h>
>  #include <linux/bug.h>
>  #include <linux/build_bug.h>
> +#include <linux/device.h>
>  #include <linux/err.h>
>  #include <linux/errname.h>
>  #include <linux/mutex.h>
> diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
> new file mode 100644
> index 000000000000..e445e87fb7d7
> --- /dev/null
> +++ b/rust/kernel/device.rs
> @@ -0,0 +1,102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Generic devices that are part of the kernel's driver model.
> +//!
> +//! C header: [`include/linux/device.h`](srctree/include/linux/device.h)
> +
> +use crate::{
> +    bindings,
> +    types::{ARef, Opaque},
> +};
> +use core::ptr;
> +
> +/// A reference-counted device.
> +///
> +/// This structure represents the Rust abstraction for a C `struct device`. This implementation
> +/// abstracts the usage of an already existing C `struct device` within Rust code that we get
> +/// passed from the C side.
> +///
> +/// An instance of this abstraction can be obtained temporarily or permanent.
> +///
> +/// A temporary one is bound to the lifetime of the C `struct device` pointer used for creation.
> +/// A permanent instance is always reference-counted and hence not restricted by any lifetime
> +/// boundaries.
> +///
> +/// For subsystems it is recommended to create a permanent instance to wrap into a subsystem
> +/// specific device structure (e.g. `pci::Device`). This is useful for passing it to drivers in
> +/// `T::probe()`, such that a driver can store the `ARef<Device>` (equivalent to storing a
> +/// `struct device` pointer in a C driver) for arbitrary purposes, e.g. allocating DMA coherent
> +/// memory.
> +///
> +/// # Invariants
> +///
> +/// The pointer stored in `Self` is non-null and valid for the lifetime of the `ARef` instance. In

There is no pointer stored in `Self` directly. I think you can just
remove the first sentence.

The second sentence can also be improved, see `Task` in
`rust/kernel/task.rs:42`.

> +/// particular, the `ARef` instance owns an increment on the underlying object’s reference count.
> +///
> +/// `bindings::device::release` is valid to be called from any thread, hence `ARef<Device>` can be
> +/// dropped from any thread.
> +#[repr(transparent)]
> +pub struct Device(Opaque<bindings::device>);
> +
> +impl Device {
> +    /// Creates a new reference-counted abstraction instance of an existing `struct device` pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count,
> +    /// i.e. it must be ensured that the reference count of the C `struct device` `ptr` points to
> +    /// can't drop to zero, for the duration of this function call.
> +    ///
> +    /// It must also be ensured that `bindings::device::release` can be called from any thread.
> +    /// While not officially documented, this should be the case for any `struct device`.
> +    pub unsafe fn from_raw(ptr: *mut bindings::device) -> ARef<Self> {
> +        // SAFETY: By the safety requirements, ptr is valid.
> +        // Initially increase the reference count by one to compensate for the final decrement once
> +        // this newly created `ARef<Device>` instance is dropped.
> +        unsafe { bindings::get_device(ptr) };
> +
> +        // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::device`.
> +        let ptr = ptr.cast::<Self>();
> +
> +        // SAFETY: By the safety requirements, ptr is valid.

That is not the only requirement on `from_raw`, you also need to own
a refcount (which you do) on `ptr`.

---
Cheers,
Benno

> +        unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(ptr)) }
> +    }
> +
> +    /// Obtain the raw `struct device *`.
> +    pub(crate) fn as_raw(&self) -> *mut bindings::device {
> +        self.0.get()
> +    }
> +
> +    /// Convert a raw C `struct device` pointer to a `&'a Device`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count,
> +    /// i.e. it must be ensured that the reference count of the C `struct device` `ptr` points to
> +    /// can't drop to zero, for the duration of this function call and the entire duration when the
> +    /// returned reference exists.
> +    pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) -> &'a Self {
> +        // SAFETY: Guaranteed by the safety requirements of the function.
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: Instances of `Device` are always reference-counted.
> +unsafe impl crate::types::AlwaysRefCounted for Device {
> +    fn inc_ref(&self) {
> +        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
> +        unsafe { bindings::get_device(self.as_raw()) };
> +    }
> +
> +    unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
> +        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
> +        unsafe { bindings::put_device(obj.cast().as_ptr()) }
> +    }
> +}
> +
> +// SAFETY: As by the type invariant `Device` can be sent to any thread.
> +unsafe impl Send for Device {}
> +
> +// SAFETY: `Device` can be shared among threads because all immutable methods are protected by the
> +// synchronization in `struct device`.
> +unsafe impl Sync for Device {}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index fbd91a48ff8b..dd1207f1a873 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -28,6 +28,7 @@
> 
>  pub mod alloc;
>  mod build_assert;
> +pub mod device;
>  pub mod error;
>  pub mod init;
>  pub mod ioctl;
> --
> 2.45.1
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-18 15:48 ` [PATCH v4 2/2] rust: add firmware abstractions Danilo Krummrich
  2024-06-18 16:27   ` Boqun Feng
@ 2024-06-19  8:58   ` Benno Lossin
  2024-06-19  9:44     ` Danilo Krummrich
  2024-06-20 13:36   ` Gary Guo
  2 siblings, 1 reply; 13+ messages in thread
From: Benno Lossin @ 2024-06-19  8:58 UTC (permalink / raw)
  To: Danilo Krummrich, gregkh, rafael, mcgrof, russ.weight, ojeda,
	alex.gaynor, wedsonaf, boqun.feng, gary, bjorn3_gh, a.hindborg,
	aliceryhl, airlied, fujita.tomonori, pstanner, ajanulgu, lyude
  Cc: rust-for-linux, linux-kernel

On 18.06.24 17:48, Danilo Krummrich wrote:
> 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 <dakr@redhat.com>
> ---
>  drivers/base/firmware_loader/Kconfig |   7 ++
>  rust/bindings/bindings_helper.h      |   1 +
>  rust/kernel/firmware.rs              | 101 +++++++++++++++++++++++++++
>  rust/kernel/lib.rs                   |   2 +
>  4 files changed, 111 insertions(+)
>  create mode 100644 rust/kernel/firmware.rs
> 
> diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
> index 5ca00e02fe82..a03701674265 100644
> --- a/drivers/base/firmware_loader/Kconfig
> +++ b/drivers/base/firmware_loader/Kconfig
> @@ -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
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index ddb5644d4fd9..18a3f05115cb 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -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>
> diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
> new file mode 100644
> index 000000000000..b55ea1b45368
> --- /dev/null
> +++ b/rust/kernel/firmware.rs
> @@ -0,0 +1,101 @@
> +// 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.

This can simply be "The `firmware`'s backing buffer is not modified."
Since I interpret "Once requested" as "Once created" and you are allowed
to break invariants as long as nobody can observe that.

> +///
> +/// # 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

I would not write "Safe by ...", since it is important to know what is
guaranteed by what. Instead I would write "self.as_raw() is valid by the
type invariant.".

> +        // 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.

Ditto.

---
Cheers,
Benno

> +        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 {}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index dd1207f1a873..7707cb013ce9 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -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)]
> --
> 2.45.1
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-19  8:58   ` Benno Lossin
@ 2024-06-19  9:44     ` Danilo Krummrich
  2024-06-19 10:43       ` Greg KH
  0 siblings, 1 reply; 13+ messages in thread
From: Danilo Krummrich @ 2024-06-19  9:44 UTC (permalink / raw)
  To: Benno Lossin, gregkh
  Cc: rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, gary, bjorn3_gh, a.hindborg, aliceryhl, airlied,
	fujita.tomonori, pstanner, ajanulgu, lyude, rust-for-linux,
	linux-kernel

Greg,

Benno's comments provide some nice hints to further improve the safety comments.
Since I was notified that those patches hit your tree already, how do you want
to proceed?

- Danilo

On Wed, Jun 19, 2024 at 08:58:02AM +0000, Benno Lossin wrote:
> On 18.06.24 17:48, Danilo Krummrich wrote:
> > 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 <dakr@redhat.com>
> > ---
> >  drivers/base/firmware_loader/Kconfig |   7 ++
> >  rust/bindings/bindings_helper.h      |   1 +
> >  rust/kernel/firmware.rs              | 101 +++++++++++++++++++++++++++
> >  rust/kernel/lib.rs                   |   2 +
> >  4 files changed, 111 insertions(+)
> >  create mode 100644 rust/kernel/firmware.rs
> > 
> > diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
> > index 5ca00e02fe82..a03701674265 100644
> > --- a/drivers/base/firmware_loader/Kconfig
> > +++ b/drivers/base/firmware_loader/Kconfig
> > @@ -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
> > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> > index ddb5644d4fd9..18a3f05115cb 100644
> > --- a/rust/bindings/bindings_helper.h
> > +++ b/rust/bindings/bindings_helper.h
> > @@ -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>
> > diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
> > new file mode 100644
> > index 000000000000..b55ea1b45368
> > --- /dev/null
> > +++ b/rust/kernel/firmware.rs
> > @@ -0,0 +1,101 @@
> > +// 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.
> 
> This can simply be "The `firmware`'s backing buffer is not modified."
> Since I interpret "Once requested" as "Once created" and you are allowed
> to break invariants as long as nobody can observe that.
> 
> > +///
> > +/// # 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
> 
> I would not write "Safe by ...", since it is important to know what is
> guaranteed by what. Instead I would write "self.as_raw() is valid by the
> type invariant.".
> 
> > +        // 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.
> 
> Ditto.
> 
> ---
> Cheers,
> Benno
> 
> > +        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 {}
> > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> > index dd1207f1a873..7707cb013ce9 100644
> > --- a/rust/kernel/lib.rs
> > +++ b/rust/kernel/lib.rs
> > @@ -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)]
> > --
> > 2.45.1
> > 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-19  9:44     ` Danilo Krummrich
@ 2024-06-19 10:43       ` Greg KH
  2024-06-19 11:33         ` Danilo Krummrich
  0 siblings, 1 reply; 13+ messages in thread
From: Greg KH @ 2024-06-19 10:43 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Benno Lossin, rafael, mcgrof, russ.weight, ojeda, alex.gaynor,
	wedsonaf, boqun.feng, gary, bjorn3_gh, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude,
	rust-for-linux, linux-kernel

On Wed, Jun 19, 2024 at 11:44:23AM +0200, Danilo Krummrich wrote:
> Greg,
> 
> Benno's comments provide some nice hints to further improve the safety comments.
> Since I was notified that those patches hit your tree already, how do you want
> to proceed?

Please start by not top-posting :)

Anyway, patches on top of what is in my tree is fine, these are just
comment updates, not any real broken issue to prevent the existing stuff
from existing.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-19 10:43       ` Greg KH
@ 2024-06-19 11:33         ` Danilo Krummrich
  0 siblings, 0 replies; 13+ messages in thread
From: Danilo Krummrich @ 2024-06-19 11:33 UTC (permalink / raw)
  To: Greg KH
  Cc: Benno Lossin, rafael, mcgrof, russ.weight, ojeda, alex.gaynor,
	wedsonaf, boqun.feng, gary, bjorn3_gh, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude,
	rust-for-linux, linux-kernel

On Wed, Jun 19, 2024 at 12:43:55PM +0200, Greg KH wrote:
> On Wed, Jun 19, 2024 at 11:44:23AM +0200, Danilo Krummrich wrote:
> > Greg,
> > 
> > Benno's comments provide some nice hints to further improve the safety comments.
> > Since I was notified that those patches hit your tree already, how do you want
> > to proceed?
> 
> Please start by not top-posting :)

Well, I guess it kinda made sense in this case, since I wasn't replying to any
of the comments specifically.

> 
> Anyway, patches on top of what is in my tree is fine, these are just
> comment updates, not any real broken issue to prevent the existing stuff
> from existing.

Ok, I'll send you another series for this including the changes for the
MAINTAINERS file.

- Danilo

> 
> thanks,
> 
> greg k-h
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-18 15:48 ` [PATCH v4 2/2] rust: add firmware abstractions Danilo Krummrich
  2024-06-18 16:27   ` Boqun Feng
  2024-06-19  8:58   ` Benno Lossin
@ 2024-06-20 13:36   ` Gary Guo
  2024-06-20 13:43     ` Danilo Krummrich
  2 siblings, 1 reply; 13+ messages in thread
From: Gary Guo @ 2024-06-20 13:36 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude,
	rust-for-linux, linux-kernel

On Tue, 18 Jun 2024 17:48:35 +0200
Danilo Krummrich <dakr@redhat.com> wrote:

> 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 <dakr@redhat.com>
> ---
>  drivers/base/firmware_loader/Kconfig |   7 ++
>  rust/bindings/bindings_helper.h      |   1 +
>  rust/kernel/firmware.rs              | 101 +++++++++++++++++++++++++++
>  rust/kernel/lib.rs                   |   2 +
>  4 files changed, 111 insertions(+)
>  create mode 100644 rust/kernel/firmware.rs
> 
> diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
> index 5ca00e02fe82..a03701674265 100644
> --- a/drivers/base/firmware_loader/Kconfig
> +++ b/drivers/base/firmware_loader/Kconfig
> @@ -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
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index ddb5644d4fd9..18a3f05115cb 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -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>
> diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
> new file mode 100644
> index 000000000000..b55ea1b45368
> --- /dev/null
> +++ b/rust/kernel/firmware.rs
> @@ -0,0 +1,101 @@
> +// 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()) };

This is line is unsound if this function is called with an arbitrary
FwFunc, therefore the safety comment should also mention that `func`
cannot be an arbitrary function and it must be one of
`request_firmware`, `firmware_request_nowarn`,
`firmware_request_platform`, `request_firmware_direct`, and this is
true because the function is not public and all users in the file
satisfy this safety precondition.


> +        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 {}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index dd1207f1a873..7707cb013ce9 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -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)]


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 1/2] rust: add abstraction for struct device
  2024-06-18 15:48 ` [PATCH v4 1/2] rust: add abstraction for struct device Danilo Krummrich
  2024-06-19  8:53   ` Benno Lossin
@ 2024-06-20 13:37   ` Gary Guo
  1 sibling, 0 replies; 13+ messages in thread
From: Gary Guo @ 2024-06-20 13:37 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude,
	rust-for-linux, linux-kernel

On Tue, 18 Jun 2024 17:48:34 +0200
Danilo Krummrich <dakr@redhat.com> wrote:

> Add an (always) reference-counted abstraction for a generic C `struct
> device`. This abstraction encapsulates existing `struct device` instances
> and manages its reference count.
> 
> Subsystems may use this abstraction as a base to abstract subsystem
> specific device instances based on a generic `struct device`, such as
> `struct pci_dev`.
> 
> Co-developed-by: Wedson Almeida Filho <wedsonaf@gmail.com>
> Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
> Signed-off-by: Danilo Krummrich <dakr@redhat.com>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
>  rust/helpers.c        |   1 +
>  rust/kernel/device.rs | 102 ++++++++++++++++++++++++++++++++++++++++++
>  rust/kernel/lib.rs    |   1 +
>  3 files changed, 104 insertions(+)
>  create mode 100644 rust/kernel/device.rs



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-20 13:36   ` Gary Guo
@ 2024-06-20 13:43     ` Danilo Krummrich
  2024-06-27 10:36       ` Gary Guo
  0 siblings, 1 reply; 13+ messages in thread
From: Danilo Krummrich @ 2024-06-20 13:43 UTC (permalink / raw)
  To: Gary Guo
  Cc: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude,
	rust-for-linux, linux-kernel

On 6/20/24 15:36, Gary Guo wrote:
> On Tue, 18 Jun 2024 17:48:35 +0200
> Danilo Krummrich <dakr@redhat.com> wrote:
> 
>> 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 <dakr@redhat.com>
>> ---
>>   drivers/base/firmware_loader/Kconfig |   7 ++
>>   rust/bindings/bindings_helper.h      |   1 +
>>   rust/kernel/firmware.rs              | 101 +++++++++++++++++++++++++++
>>   rust/kernel/lib.rs                   |   2 +
>>   4 files changed, 111 insertions(+)
>>   create mode 100644 rust/kernel/firmware.rs
>>
>> diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
>> index 5ca00e02fe82..a03701674265 100644
>> --- a/drivers/base/firmware_loader/Kconfig
>> +++ b/drivers/base/firmware_loader/Kconfig
>> @@ -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
>> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
>> index ddb5644d4fd9..18a3f05115cb 100644
>> --- a/rust/bindings/bindings_helper.h
>> +++ b/rust/bindings/bindings_helper.h
>> @@ -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>
>> diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
>> new file mode 100644
>> index 000000000000..b55ea1b45368
>> --- /dev/null
>> +++ b/rust/kernel/firmware.rs
>> @@ -0,0 +1,101 @@
>> +// 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()) };
> 
> This is line is unsound if this function is called with an arbitrary
> FwFunc, therefore the safety comment should also mention that `func`
> cannot be an arbitrary function and it must be one of
> `request_firmware`, `firmware_request_nowarn`,
> `firmware_request_platform`, `request_firmware_direct`, and this is

This is documented in the type definition of `FwFunc`. We can link to this invariant though
and explicitly mark it as such. Does that make sense?

- Danilo

> true because the function is not public and all users in the file
> satisfy this safety precondition.
> 
> 
>> +        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 {}
>> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
>> index dd1207f1a873..7707cb013ce9 100644
>> --- a/rust/kernel/lib.rs
>> +++ b/rust/kernel/lib.rs
>> @@ -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)]
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 2/2] rust: add firmware abstractions
  2024-06-20 13:43     ` Danilo Krummrich
@ 2024-06-27 10:36       ` Gary Guo
  0 siblings, 0 replies; 13+ messages in thread
From: Gary Guo @ 2024-06-27 10:36 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: gregkh, rafael, mcgrof, russ.weight, ojeda, alex.gaynor, wedsonaf,
	boqun.feng, bjorn3_gh, benno.lossin, a.hindborg, aliceryhl,
	airlied, fujita.tomonori, pstanner, ajanulgu, lyude,
	rust-for-linux, linux-kernel

On Thu, 20 Jun 2024 15:43:52 +0200
Danilo Krummrich <dakr@redhat.com> wrote:

> On 6/20/24 15:36, Gary Guo wrote:
> > On Tue, 18 Jun 2024 17:48:35 +0200
> > Danilo Krummrich <dakr@redhat.com> wrote:
> >   
> >> 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 <dakr@redhat.com>
> >> ---
> >>   drivers/base/firmware_loader/Kconfig |   7 ++
> >>   rust/bindings/bindings_helper.h      |   1 +
> >>   rust/kernel/firmware.rs              | 101 +++++++++++++++++++++++++++
> >>   rust/kernel/lib.rs                   |   2 +
> >>   4 files changed, 111 insertions(+)
> >>   create mode 100644 rust/kernel/firmware.rs
> >>
> >> diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
> >> new file mode 100644
> >> index 000000000000..b55ea1b45368
> >> --- /dev/null
> >> +++ b/rust/kernel/firmware.rs
> >> @@ -0,0 +1,101 @@
> >> +// 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()) };  
> > 
> > This is line is unsound if this function is called with an arbitrary
> > FwFunc, therefore the safety comment should also mention that `func`
> > cannot be an arbitrary function and it must be one of
> > `request_firmware`, `firmware_request_nowarn`,
> > `firmware_request_platform`, `request_firmware_direct`, and this is  
> 
> This is documented in the type definition of `FwFunc`. We can link to this invariant though
> and explicitly mark it as such. Does that make sense?

You can't really attach an invariant to `FwFunc` because it's just a
type alias, although linking to the comment in `FwFunc` and mentioning
that all users are within the module is good to me.

Some other options are:
* New type over FwFunc and attach invariant
* Make this function unsafe and have this as a safety precondition

Both would make the safety comment making local reasoning rather than
file-level reasoning. Although I don't think either is necessary since
this is a small file. But we need to be explicit file-level reasoning
is used here.

Best,
Gary

> - Danilo
> 
> > true because the function is not public and all users in the file
> > satisfy this safety precondition.
> > 
> >   
> >> +        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 {}


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2024-06-27 10:36 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-18 15:48 [PATCH v4 0/2] Rust abstractions for Device & Firmware Danilo Krummrich
2024-06-18 15:48 ` [PATCH v4 1/2] rust: add abstraction for struct device Danilo Krummrich
2024-06-19  8:53   ` Benno Lossin
2024-06-20 13:37   ` Gary Guo
2024-06-18 15:48 ` [PATCH v4 2/2] rust: add firmware abstractions Danilo Krummrich
2024-06-18 16:27   ` Boqun Feng
2024-06-19  8:58   ` Benno Lossin
2024-06-19  9:44     ` Danilo Krummrich
2024-06-19 10:43       ` Greg KH
2024-06-19 11:33         ` Danilo Krummrich
2024-06-20 13:36   ` Gary Guo
2024-06-20 13:43     ` Danilo Krummrich
2024-06-27 10:36       ` Gary Guo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).