diff options
| author | Dave Airlie <airlied@redhat.com> | 2025-03-13 06:03:43 +1000 | 
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2025-03-13 06:03:55 +1000 | 
| commit | 4e64a62032ac41e42457870c59a3c7985d594a0e (patch) | |
| tree | 9cba218f63e028836ef1952b0510457388d707af /rust/kernel | |
| parent | 626fb115662c9fd44fcbdd744d96a45b0427b504 (diff) | |
| parent | b28786b190d1ae2df5e6a5181ad78c6f226ea3e1 (diff) | |
Merge tag 'nova-next-6.15-2025-03-09' of gitlab.freedesktop.org:drm/nova into drm-next
Nova changes for v6.15
nova-core:
  - initial skeleton driver
  - documentation
    - project guidelines
    - task (todo) list
firmware:
  - `module_firmware!` macro
  - `firmware::ModInfoBuilder`
Rust:
  - `LocalModule` type alias
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Danilo Krummrich <dakr@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/Z84dHHEn6xfvlRxk@cassiopeiae
Diffstat (limited to 'rust/kernel')
| -rw-r--r-- | rust/kernel/firmware.rs | 216 | 
1 files changed, 216 insertions, 0 deletions
| diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs index c5162fdc95ff..f04b058b09b2 100644 --- a/rust/kernel/firmware.rs +++ b/rust/kernel/firmware.rs @@ -115,3 +115,219 @@ 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 {} + +/// Create firmware .modinfo entries. +/// +/// This macro is the counterpart of the C macro `MODULE_FIRMWARE()`, but instead of taking a +/// simple string literals, which is already covered by the `firmware` field of +/// [`crate::prelude::module!`], it allows the caller to pass a builder type, based on the +/// [`ModInfoBuilder`], which can create the firmware modinfo strings in a more flexible way. +/// +/// Drivers should extend the [`ModInfoBuilder`] with their own driver specific builder type. +/// +/// The `builder` argument must be a type which implements the following function. +/// +/// `const fn create(module_name: &'static CStr) -> ModInfoBuilder` +/// +/// `create` should pass the `module_name` to the [`ModInfoBuilder`] and, with the help of +/// it construct the corresponding firmware modinfo. +/// +/// Typically, such contracts would be enforced by a trait, however traits do not (yet) support +/// const functions. +/// +/// # Example +/// +/// ``` +/// # mod module_firmware_test { +/// # use kernel::firmware; +/// # use kernel::prelude::*; +/// # +/// # struct MyModule; +/// # +/// # impl kernel::Module for MyModule { +/// #     fn init(_module: &'static ThisModule) -> Result<Self> { +/// #         Ok(Self) +/// #     } +/// # } +/// # +/// # +/// struct Builder<const N: usize>; +/// +/// impl<const N: usize> Builder<N> { +///     const DIR: &'static str = "vendor/chip/"; +///     const FILES: [&'static str; 3] = [ "foo", "bar", "baz" ]; +/// +///     const fn create(module_name: &'static kernel::str::CStr) -> firmware::ModInfoBuilder<N> { +///         let mut builder = firmware::ModInfoBuilder::new(module_name); +/// +///         let mut i = 0; +///         while i < Self::FILES.len() { +///             builder = builder.new_entry() +///                 .push(Self::DIR) +///                 .push(Self::FILES[i]) +///                 .push(".bin"); +/// +///                 i += 1; +///         } +/// +///         builder +///      } +/// } +/// +/// module! { +///    type: MyModule, +///    name: "module_firmware_test", +///    author: "Rust for Linux", +///    description: "module_firmware! test module", +///    license: "GPL", +/// } +/// +/// kernel::module_firmware!(Builder); +/// # } +/// ``` +#[macro_export] +macro_rules! module_firmware { +    // The argument is the builder type without the const generic, since it's deferred from within +    // this macro. Hence, we can neither use `expr` nor `ty`. +    ($($builder:tt)*) => { +        const _: () = { +            const __MODULE_FIRMWARE_PREFIX: &'static $crate::str::CStr = if cfg!(MODULE) { +                $crate::c_str!("") +            } else { +                <LocalModule as $crate::ModuleMetadata>::NAME +            }; + +            #[link_section = ".modinfo"] +            #[used] +            static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX) +                .build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build(); +        }; +    }; +} + +/// Builder for firmware module info. +/// +/// [`ModInfoBuilder`] is a helper component to flexibly compose firmware paths strings for the +/// .modinfo section in const context. +/// +/// Therefore the [`ModInfoBuilder`] provides the methods [`ModInfoBuilder::new_entry`] and +/// [`ModInfoBuilder::push`], where the latter is used to push path components and the former to +/// mark the beginning of a new path string. +/// +/// [`ModInfoBuilder`] is meant to be used in combination with [`kernel::module_firmware!`]. +/// +/// The const generic `N` as well as the `module_name` parameter of [`ModInfoBuilder::new`] is an +/// internal implementation detail and supplied through the above macro. +pub struct ModInfoBuilder<const N: usize> { +    buf: [u8; N], +    n: usize, +    module_name: &'static CStr, +} + +impl<const N: usize> ModInfoBuilder<N> { +    /// Create an empty builder instance. +    pub const fn new(module_name: &'static CStr) -> Self { +        Self { +            buf: [0; N], +            n: 0, +            module_name, +        } +    } + +    const fn push_internal(mut self, bytes: &[u8]) -> Self { +        let mut j = 0; + +        if N == 0 { +            self.n += bytes.len(); +            return self; +        } + +        while j < bytes.len() { +            if self.n < N { +                self.buf[self.n] = bytes[j]; +            } +            self.n += 1; +            j += 1; +        } +        self +    } + +    /// Push an additional path component. +    /// +    /// Append path components to the [`ModInfoBuilder`] instance. Paths need to be separated +    /// with [`ModInfoBuilder::new_entry`]. +    /// +    /// # Example +    /// +    /// ``` +    /// use kernel::firmware::ModInfoBuilder; +    /// +    /// # const DIR: &str = "vendor/chip/"; +    /// # const fn no_run<const N: usize>(builder: ModInfoBuilder<N>) { +    /// let builder = builder.new_entry() +    ///     .push(DIR) +    ///     .push("foo.bin") +    ///     .new_entry() +    ///     .push(DIR) +    ///     .push("bar.bin"); +    /// # } +    /// ``` +    pub const fn push(self, s: &str) -> Self { +        // Check whether there has been an initial call to `next_entry()`. +        if N != 0 && self.n == 0 { +            crate::build_error!("Must call next_entry() before push()."); +        } + +        self.push_internal(s.as_bytes()) +    } + +    const fn push_module_name(self) -> Self { +        let mut this = self; +        let module_name = this.module_name; + +        if !this.module_name.is_empty() { +            this = this.push_internal(module_name.as_bytes_with_nul()); + +            if N != 0 { +                // Re-use the space taken by the NULL terminator and swap it with the '.' separator. +                this.buf[this.n - 1] = b'.'; +            } +        } + +        this +    } + +    /// Prepare the [`ModInfoBuilder`] for the next entry. +    /// +    /// This method acts as a separator between module firmware path entries. +    /// +    /// Must be called before constructing a new entry with subsequent calls to +    /// [`ModInfoBuilder::push`]. +    /// +    /// See [`ModInfoBuilder::push`] for an example. +    pub const fn new_entry(self) -> Self { +        self.push_internal(b"\0") +            .push_module_name() +            .push_internal(b"firmware=") +    } + +    /// Build the byte array. +    pub const fn build(self) -> [u8; N] { +        // Add the final NULL terminator. +        let this = self.push_internal(b"\0"); + +        if this.n == N { +            this.buf +        } else { +            crate::build_error!("Length mismatch."); +        } +    } +} + +impl ModInfoBuilder<0> { +    /// Return the length of the byte array to build. +    pub const fn build_length(self) -> usize { +        // Compensate for the NULL terminator added by `build`. +        self.n + 1 +    } +} | 
