mirror of https://github.com/torvalds/linux.git
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
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQS2q/xV6QjXAdC7k+1FlHeO1qrKLgUCZ84SdgAKCRBFlHeO1qrK
Lgp6AQCMx43G1praGv+BJZn8FG0l/VsVYeRTZf7vCT1EWyN0ogEAm2sGwqHP0977
3KghBZ8SeIPabuLP84Dw0TjBuPWTGgw=
=IVPn
-----END PGP SIGNATURE-----
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
This commit is contained in:
commit
4e64a62032
|
|
@ -25,6 +25,7 @@ GPU Driver Documentation
|
||||||
panfrost
|
panfrost
|
||||||
panthor
|
panthor
|
||||||
zynqmp
|
zynqmp
|
||||||
|
nova/index
|
||||||
|
|
||||||
.. only:: subproject and html
|
.. only:: subproject and html
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||||
|
|
||||||
|
==========
|
||||||
|
Guidelines
|
||||||
|
==========
|
||||||
|
|
||||||
|
This documents contains the guidelines for nova-core. Additionally, all common
|
||||||
|
guidelines of the Nova project do apply.
|
||||||
|
|
||||||
|
Driver API
|
||||||
|
==========
|
||||||
|
|
||||||
|
One main purpose of nova-core is to implement the abstraction around the
|
||||||
|
firmware interface of GSP and provide a firmware (version) independent API for
|
||||||
|
2nd level drivers, such as nova-drm or the vGPU manager VFIO driver.
|
||||||
|
|
||||||
|
Therefore, it is not permitted to leak firmware (version) specifics, through the
|
||||||
|
driver API, to 2nd level drivers.
|
||||||
|
|
||||||
|
Acceptance Criteria
|
||||||
|
===================
|
||||||
|
|
||||||
|
- To the extend possible, patches submitted to nova-core must be tested for
|
||||||
|
regressions with all 2nd level drivers.
|
||||||
|
|
@ -0,0 +1,446 @@
|
||||||
|
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||||
|
|
||||||
|
=========
|
||||||
|
Task List
|
||||||
|
=========
|
||||||
|
|
||||||
|
Tasks may have the following fields:
|
||||||
|
|
||||||
|
- ``Complexity``: Describes the required familiarity with Rust and / or the
|
||||||
|
corresponding kernel APIs or subsystems. There are four different complexities,
|
||||||
|
``Beginner``, ``Intermediate``, ``Advanced`` and ``Expert``.
|
||||||
|
- ``Reference``: References to other tasks.
|
||||||
|
- ``Link``: Links to external resources.
|
||||||
|
- ``Contact``: The person that can be contacted for further information about
|
||||||
|
the task.
|
||||||
|
|
||||||
|
Enablement (Rust)
|
||||||
|
=================
|
||||||
|
|
||||||
|
Tasks that are not directly related to nova-core, but are preconditions in terms
|
||||||
|
of required APIs.
|
||||||
|
|
||||||
|
FromPrimitive API
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Sometimes the need arises to convert a number to a value of an enum or a
|
||||||
|
structure.
|
||||||
|
|
||||||
|
A good example from nova-core would be the ``Chipset`` enum type, which defines
|
||||||
|
the value ``AD102``. When probing the GPU the value ``0x192`` can be read from a
|
||||||
|
certain register indication the chipset AD102. Hence, the enum value ``AD102``
|
||||||
|
should be derived from the number ``0x192``. Currently, nova-core uses a custom
|
||||||
|
implementation (``Chipset::from_u32`` for this.
|
||||||
|
|
||||||
|
Instead, it would be desirable to have something like the ``FromPrimitive``
|
||||||
|
trait [1] from the num crate.
|
||||||
|
|
||||||
|
Having this generalization also helps with implementing a generic macro that
|
||||||
|
automatically generates the corresponding mappings between a value and a number.
|
||||||
|
|
||||||
|
| Complexity: Beginner
|
||||||
|
| Link: https://docs.rs/num/latest/num/trait.FromPrimitive.html
|
||||||
|
|
||||||
|
Generic register abstraction
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Work out how register constants and structures can be automatically generated
|
||||||
|
through generalized macros.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
register!(BOOT0, 0x0, u32, pci::Bar<SIZE>, Fields [
|
||||||
|
MINOR_REVISION(3:0, RO),
|
||||||
|
MAJOR_REVISION(7:4, RO),
|
||||||
|
REVISION(7:0, RO), // Virtual register combining major and minor rev.
|
||||||
|
])
|
||||||
|
|
||||||
|
This could expand to something like:
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
const BOOT0_OFFSET: usize = 0x00000000;
|
||||||
|
const BOOT0_MINOR_REVISION_SHIFT: u8 = 0;
|
||||||
|
const BOOT0_MINOR_REVISION_MASK: u32 = 0x0000000f;
|
||||||
|
const BOOT0_MAJOR_REVISION_SHIFT: u8 = 4;
|
||||||
|
const BOOT0_MAJOR_REVISION_MASK: u32 = 0x000000f0;
|
||||||
|
const BOOT0_REVISION_SHIFT: u8 = BOOT0_MINOR_REVISION_SHIFT;
|
||||||
|
const BOOT0_REVISION_MASK: u32 = BOOT0_MINOR_REVISION_MASK | BOOT0_MAJOR_REVISION_MASK;
|
||||||
|
|
||||||
|
struct Boot0(u32);
|
||||||
|
|
||||||
|
impl Boot0 {
|
||||||
|
#[inline]
|
||||||
|
fn read(bar: &RevocableGuard<'_, pci::Bar<SIZE>>) -> Self {
|
||||||
|
Self(bar.readl(BOOT0_OFFSET))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn minor_revision(&self) -> u32 {
|
||||||
|
(self.0 & BOOT0_MINOR_REVISION_MASK) >> BOOT0_MINOR_REVISION_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn major_revision(&self) -> u32 {
|
||||||
|
(self.0 & BOOT0_MAJOR_REVISION_MASK) >> BOOT0_MAJOR_REVISION_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn revision(&self) -> u32 {
|
||||||
|
(self.0 & BOOT0_REVISION_MASK) >> BOOT0_REVISION_SHIFT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
let bar = bar.try_access().ok_or(ENXIO)?;
|
||||||
|
|
||||||
|
let boot0 = Boot0::read(&bar);
|
||||||
|
pr_info!("Revision: {}\n", boot0.revision());
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
|
||||||
|
Delay / Sleep abstractions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Rust abstractions for the kernel's delay() and sleep() functions.
|
||||||
|
|
||||||
|
FUJITA Tomonori plans to work on abstractions for read_poll_timeout_atomic()
|
||||||
|
(and friends) [1].
|
||||||
|
|
||||||
|
| Complexity: Beginner
|
||||||
|
| Link: https://lore.kernel.org/netdev/20250228.080550.354359820929821928.fujita.tomonori@gmail.com/ [1]
|
||||||
|
|
||||||
|
IRQ abstractions
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Rust abstractions for IRQ handling.
|
||||||
|
|
||||||
|
There is active ongoing work from Daniel Almeida [1] for the "core" abstractions
|
||||||
|
to request IRQs.
|
||||||
|
|
||||||
|
Besides optional review and testing work, the required ``pci::Device`` code
|
||||||
|
around those core abstractions needs to be worked out.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Link: https://lore.kernel.org/lkml/20250122163932.46697-1-daniel.almeida@collabora.com/ [1]
|
||||||
|
| Contact: Daniel Almeida
|
||||||
|
|
||||||
|
Page abstraction for foreign pages
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Rust abstractions for pages not created by the Rust page abstraction without
|
||||||
|
direct ownership.
|
||||||
|
|
||||||
|
There is active onging work from Abdiel Janulgue [1] and Lina [2].
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
| Link: https://lore.kernel.org/linux-mm/20241119112408.779243-1-abdiel.janulgue@gmail.com/ [1]
|
||||||
|
| Link: https://lore.kernel.org/rust-for-linux/20250202-rust-page-v1-0-e3170d7fe55e@asahilina.net/ [2]
|
||||||
|
|
||||||
|
Scatterlist / sg_table abstractions
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Rust abstractions for scatterlist / sg_table.
|
||||||
|
|
||||||
|
There is preceding work from Abdiel Janulgue, which hasn't made it to the
|
||||||
|
mailing list yet.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Contact: Abdiel Janulgue
|
||||||
|
|
||||||
|
ELF utils
|
||||||
|
---------
|
||||||
|
|
||||||
|
Rust implementation of ELF header representation to retrieve section header
|
||||||
|
tables, names, and data from an ELF-formatted images.
|
||||||
|
|
||||||
|
There is preceding work from Abdiel Janulgue, which hasn't made it to the
|
||||||
|
mailing list yet.
|
||||||
|
|
||||||
|
| Complexity: Beginner
|
||||||
|
| Contact: Abdiel Janulgue
|
||||||
|
|
||||||
|
PCI MISC APIs
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Extend the existing PCI device / driver abstractions by SR-IOV, config space,
|
||||||
|
capability, MSI API abstractions.
|
||||||
|
|
||||||
|
| Complexity: Beginner
|
||||||
|
|
||||||
|
Auxiliary bus abstractions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Rust abstraction for the auxiliary bus APIs.
|
||||||
|
|
||||||
|
This is needed to connect nova-core to the nova-drm driver.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
|
||||||
|
Debugfs abstractions
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Rust abstraction for debugfs APIs.
|
||||||
|
|
||||||
|
| Reference: Export GSP log buffers
|
||||||
|
| Complexity: Intermediate
|
||||||
|
|
||||||
|
Vec extensions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Implement ``Vec::truncate`` and ``Vec::resize``.
|
||||||
|
|
||||||
|
Currently this is used for some experimental code to parse the vBIOS.
|
||||||
|
|
||||||
|
| Reference vBIOS support
|
||||||
|
| Complexity: Beginner
|
||||||
|
|
||||||
|
GPU (general)
|
||||||
|
=============
|
||||||
|
|
||||||
|
Parse firmware headers
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Parse ELF headers from the firmware files loaded from the filesystem.
|
||||||
|
|
||||||
|
| Reference: ELF utils
|
||||||
|
| Complexity: Beginner
|
||||||
|
| Contact: Abdiel Janulgue
|
||||||
|
|
||||||
|
Build radix3 page table
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Build the radix3 page table to map the firmware.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Contact: Abdiel Janulgue
|
||||||
|
|
||||||
|
vBIOS support
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Parse the vBIOS and probe the structures required for driver initialization.
|
||||||
|
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
| Reference: Vec extensions
|
||||||
|
| Complexity: Intermediate
|
||||||
|
|
||||||
|
Initial Devinit support
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Implement BIOS Device Initialization, i.e. memory sizing, waiting, PLL
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
| Complexity: Beginner
|
||||||
|
|
||||||
|
Boot Falcon controller
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Infrastructure to load and execute falcon (sec2) firmware images; handle the
|
||||||
|
GSP falcon processor and fwsec loading.
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
GPU Timer support
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Support for the GPU's internal timer peripheral.
|
||||||
|
|
||||||
|
| Complexity: Beginner
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
MMU / PT management
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Work out the architecture for MMU / page table management.
|
||||||
|
|
||||||
|
We need to consider that nova-drm will need rather fine-grained control,
|
||||||
|
especially in terms of locking, in order to be able to implement asynchronous
|
||||||
|
Vulkan queues.
|
||||||
|
|
||||||
|
While generally sharing the corresponding code is desirable, it needs to be
|
||||||
|
evaluated how (and if at all) sharing the corresponding code is expedient.
|
||||||
|
|
||||||
|
| Complexity: Expert
|
||||||
|
|
||||||
|
VRAM memory allocator
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Investigate options for a VRAM memory allocator.
|
||||||
|
|
||||||
|
Some possible options:
|
||||||
|
- Rust abstractions for
|
||||||
|
- RB tree (interval tree) / drm_mm
|
||||||
|
- maple_tree
|
||||||
|
- native Rust collections
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
|
||||||
|
Instance Memory
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Implement support for instmem (bar2) used to store page tables.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
GPU System Processor (GSP)
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Export GSP log buffers
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Recent patches from Timur Tabi [1] added support to expose GSP-RM log buffers
|
||||||
|
(even after failure to probe the driver) through debugfs.
|
||||||
|
|
||||||
|
This is also an interesting feature for nova-core, especially in the early days.
|
||||||
|
|
||||||
|
| Link: https://lore.kernel.org/nouveau/20241030202952.694055-2-ttabi@nvidia.com/ [1]
|
||||||
|
| Reference: Debugfs abstractions
|
||||||
|
| Complexity: Intermediate
|
||||||
|
|
||||||
|
GSP firmware abstraction
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The GSP-RM firmware API is unstable and may incompatibly change from version to
|
||||||
|
version, in terms of data structures and semantics.
|
||||||
|
|
||||||
|
This problem is one of the big motivations for using Rust for nova-core, since
|
||||||
|
it turns out that Rust's procedural macro feature provides a rather elegant way
|
||||||
|
to address this issue:
|
||||||
|
|
||||||
|
1. generate Rust structures from the C headers in a separate namespace per version
|
||||||
|
2. build abstraction structures (within a generic namespace) that implement the
|
||||||
|
firmware interfaces; annotate the differences in implementation with version
|
||||||
|
identifiers
|
||||||
|
3. use a procedural macro to generate the actual per version implementation out
|
||||||
|
of this abstraction
|
||||||
|
4. instantiate the correct version type one on runtime (can be sure that all
|
||||||
|
have the same interface because it's defined by a common trait)
|
||||||
|
|
||||||
|
There is a PoC implementation of this pattern, in the context of the nova-core
|
||||||
|
PoC driver.
|
||||||
|
|
||||||
|
This task aims at refining the feature and ideally generalize it, to be usable
|
||||||
|
by other drivers as well.
|
||||||
|
|
||||||
|
| Complexity: Expert
|
||||||
|
|
||||||
|
GSP message queue
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Implement low level GSP message queue (command, status) for communication
|
||||||
|
between the kernel driver and GSP.
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
Bootstrap GSP
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Call the boot firmware to boot the GSP processor; execute initial control
|
||||||
|
messages.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
Client / Device APIs
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Implement the GSP message interface for client / device allocation and the
|
||||||
|
corresponding client and device allocation APIs.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
Bar PDE handling
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Synchronize page table handling for BARs between the kernel driver and GSP.
|
||||||
|
|
||||||
|
| Complexity: Beginner
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
FIFO engine
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Implement support for the FIFO engine, i.e. the corresponding GSP message
|
||||||
|
interface and provide an API for chid allocation and channel handling.
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
GR engine
|
||||||
|
---------
|
||||||
|
|
||||||
|
Implement support for the graphics engine, i.e. the corresponding GSP message
|
||||||
|
interface and provide an API for (golden) context creation and promotion.
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
CE engine
|
||||||
|
---------
|
||||||
|
|
||||||
|
Implement support for the copy engine, i.e. the corresponding GSP message
|
||||||
|
interface.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
VFN IRQ controller
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Support for the VFN interrupt controller.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
| Contact: Dave Airlie
|
||||||
|
|
||||||
|
External APIs
|
||||||
|
=============
|
||||||
|
|
||||||
|
nova-core base API
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Work out the common pieces of the API to connect 2nd level drivers, i.e. vGPU
|
||||||
|
manager and nova-drm.
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
|
||||||
|
vGPU manager API
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Work out the API parts required by the vGPU manager, which are not covered by
|
||||||
|
the base API.
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
|
||||||
|
nova-core C API
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Implement a C wrapper for the APIs required by the vGPU manager driver.
|
||||||
|
|
||||||
|
| Complexity: Intermediate
|
||||||
|
|
||||||
|
Testing
|
||||||
|
=======
|
||||||
|
|
||||||
|
CI pipeline
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Investigate option for continuous integration testing.
|
||||||
|
|
||||||
|
This can go from as simple as running KUnit tests over running (graphics) CTS to
|
||||||
|
booting up (multiple) guest VMs to test VFIO use-cases.
|
||||||
|
|
||||||
|
It might also be worth to consider the introduction of a new test suite directly
|
||||||
|
sitting on top of the uAPI for more targeted testing and debugging. There may be
|
||||||
|
options for collaboration / shared code with the Mesa project.
|
||||||
|
|
||||||
|
| Complexity: Advanced
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||||
|
|
||||||
|
==========
|
||||||
|
Guidelines
|
||||||
|
==========
|
||||||
|
|
||||||
|
This document describes the general project guidelines that apply to nova-core
|
||||||
|
and nova-drm.
|
||||||
|
|
||||||
|
Language
|
||||||
|
========
|
||||||
|
|
||||||
|
The Nova project uses the Rust programming language. In this context, all rules
|
||||||
|
of the Rust for Linux project as documented in
|
||||||
|
:doc:`../../rust/general-information` apply. Additionally, the following rules
|
||||||
|
apply.
|
||||||
|
|
||||||
|
- Unless technically necessary otherwise (e.g. uAPI), any driver code is written
|
||||||
|
in Rust.
|
||||||
|
|
||||||
|
- Unless technically necessary, unsafe Rust code must be avoided. In case of
|
||||||
|
technical necessity, unsafe code should be isolated in a separate component
|
||||||
|
providing a safe API for other driver code to use.
|
||||||
|
|
||||||
|
Style
|
||||||
|
-----
|
||||||
|
|
||||||
|
All rules of the Rust for Linux project as documented in
|
||||||
|
:doc:`../../rust/coding-guidelines` apply.
|
||||||
|
|
||||||
|
For a submit checklist, please also see the `Rust for Linux Submit checklist
|
||||||
|
addendum <https://rust-for-linux.com/contributing#submit-checklist-addendum>`_.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
The availability of proper documentation is essential in terms of scalability,
|
||||||
|
accessibility for new contributors and maintainability of a project in general,
|
||||||
|
but especially for a driver running as complex hardware as Nova is targeting.
|
||||||
|
|
||||||
|
Hence, adding documentation of any kind is very much encouraged by the project.
|
||||||
|
|
||||||
|
Besides that, there are some minimum requirements.
|
||||||
|
|
||||||
|
- Every non-private structure needs at least a brief doc comment explaining the
|
||||||
|
semantical sense of the structure, as well as potential locking and lifetime
|
||||||
|
requirements. It is encouraged to have the same minimum documentation for
|
||||||
|
non-trivial private structures.
|
||||||
|
|
||||||
|
- uAPIs must be fully documented with kernel-doc comments; additionally, the
|
||||||
|
semantical behavior must be explained including potential special or corner
|
||||||
|
cases.
|
||||||
|
|
||||||
|
- The APIs connecting the 1st level driver (nova-core) with 2nd level drivers
|
||||||
|
must be fully documented. This includes doc comments, potential locking and
|
||||||
|
lifetime requirements, as well as example code if applicable.
|
||||||
|
|
||||||
|
- Abbreviations must be explained when introduced; terminology must be uniquely
|
||||||
|
defined.
|
||||||
|
|
||||||
|
- Register addresses, layouts, shift values and masks must be defined properly;
|
||||||
|
unless obvious, the semantical sense must be documented. This only applies if
|
||||||
|
the author is able to obtain the corresponding information.
|
||||||
|
|
||||||
|
Acceptance Criteria
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Patches must only be applied if reviewed by at least one other person on the
|
||||||
|
mailing list; this also applies for maintainers.
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||||
|
|
||||||
|
=======================
|
||||||
|
nova NVIDIA GPU drivers
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The nova driver project consists out of two separate drivers nova-core and
|
||||||
|
nova-drm and intends to supersede the nouveau driver for NVIDIA GPUs based on
|
||||||
|
the GPU System Processor (GSP).
|
||||||
|
|
||||||
|
The following documents apply to both nova-core and nova-drm.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
|
||||||
|
guidelines
|
||||||
|
|
||||||
|
nova-core
|
||||||
|
=========
|
||||||
|
|
||||||
|
The nova-core driver is the core driver for NVIDIA GPUs based on GSP. nova-core,
|
||||||
|
as the 1st level driver, provides an abstraction around the GPUs hard- and
|
||||||
|
firmware interfaces providing a common base for 2nd level drivers, such as the
|
||||||
|
vGPU manager VFIO driver and the nova-drm driver.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
|
||||||
|
core/guidelines
|
||||||
|
core/todo
|
||||||
11
MAINTAINERS
11
MAINTAINERS
|
|
@ -7457,6 +7457,17 @@ T: git https://gitlab.freedesktop.org/drm/nouveau.git
|
||||||
F: drivers/gpu/drm/nouveau/
|
F: drivers/gpu/drm/nouveau/
|
||||||
F: include/uapi/drm/nouveau_drm.h
|
F: include/uapi/drm/nouveau_drm.h
|
||||||
|
|
||||||
|
CORE DRIVER FOR NVIDIA GPUS [RUST]
|
||||||
|
M: Danilo Krummrich <dakr@kernel.org>
|
||||||
|
L: nouveau@lists.freedesktop.org
|
||||||
|
S: Supported
|
||||||
|
Q: https://patchwork.freedesktop.org/project/nouveau/
|
||||||
|
B: https://gitlab.freedesktop.org/drm/nova/-/issues
|
||||||
|
C: irc://irc.oftc.net/nouveau
|
||||||
|
T: git https://gitlab.freedesktop.org/drm/nova.git nova-next
|
||||||
|
F: Documentation/gpu/nova/
|
||||||
|
F: drivers/gpu/nova-core/
|
||||||
|
|
||||||
DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS
|
DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS
|
||||||
M: Stefan Mavrodiev <stefan@olimex.com>
|
M: Stefan Mavrodiev <stefan@olimex.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@
|
||||||
obj-y += host1x/ drm/ vga/
|
obj-y += host1x/ drm/ vga/
|
||||||
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
|
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
|
||||||
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
|
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
|
||||||
|
obj-$(CONFIG_NOVA_CORE) += nova-core/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
config NOVA_CORE
|
||||||
|
tristate "Nova Core GPU driver"
|
||||||
|
depends on PCI
|
||||||
|
depends on RUST
|
||||||
|
depends on RUST_FW_LOADER_ABSTRACTIONS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Choose this if you want to build the Nova Core driver for Nvidia
|
||||||
|
GPUs based on the GPU System Processor (GSP). This is true for Turing
|
||||||
|
and later GPUs.
|
||||||
|
|
||||||
|
This driver is work in progress and may not be functional.
|
||||||
|
|
||||||
|
If M is selected, the module will be called nova_core.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
obj-$(CONFIG_NOVA_CORE) += nova_core.o
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
use kernel::{bindings, c_str, pci, prelude::*};
|
||||||
|
|
||||||
|
use crate::gpu::Gpu;
|
||||||
|
|
||||||
|
#[pin_data]
|
||||||
|
pub(crate) struct NovaCore {
|
||||||
|
#[pin]
|
||||||
|
pub(crate) gpu: Gpu,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BAR0_SIZE: usize = 8;
|
||||||
|
pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
|
||||||
|
|
||||||
|
kernel::pci_device_table!(
|
||||||
|
PCI_TABLE,
|
||||||
|
MODULE_PCI_TABLE,
|
||||||
|
<NovaCore as pci::Driver>::IdInfo,
|
||||||
|
[(
|
||||||
|
pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as _),
|
||||||
|
()
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
|
||||||
|
impl pci::Driver for NovaCore {
|
||||||
|
type IdInfo = ();
|
||||||
|
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
|
||||||
|
|
||||||
|
fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
|
||||||
|
dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n");
|
||||||
|
|
||||||
|
pdev.enable_device_mem()?;
|
||||||
|
pdev.set_master();
|
||||||
|
|
||||||
|
let bar = pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0"))?;
|
||||||
|
|
||||||
|
let this = KBox::pin_init(
|
||||||
|
try_pin_init!(Self {
|
||||||
|
gpu <- Gpu::new(pdev, bar)?,
|
||||||
|
}),
|
||||||
|
GFP_KERNEL,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
use crate::gpu;
|
||||||
|
use kernel::firmware;
|
||||||
|
|
||||||
|
pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
|
||||||
|
|
||||||
|
impl<const N: usize> ModInfoBuilder<N> {
|
||||||
|
const VERSION: &'static str = "535.113.01";
|
||||||
|
|
||||||
|
const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
|
||||||
|
ModInfoBuilder(
|
||||||
|
self.0
|
||||||
|
.new_entry()
|
||||||
|
.push("nvidia/")
|
||||||
|
.push(chipset)
|
||||||
|
.push("/gsp/")
|
||||||
|
.push(fw)
|
||||||
|
.push("-")
|
||||||
|
.push(Self::VERSION)
|
||||||
|
.push(".bin"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn make_entry_chipset(self, chipset: &str) -> Self {
|
||||||
|
self.make_entry_file(chipset, "booter_load")
|
||||||
|
.make_entry_file(chipset, "booter_unload")
|
||||||
|
.make_entry_file(chipset, "bootloader")
|
||||||
|
.make_entry_file(chipset, "gsp")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn create(
|
||||||
|
module_name: &'static kernel::str::CStr,
|
||||||
|
) -> firmware::ModInfoBuilder<N> {
|
||||||
|
let mut this = Self(firmware::ModInfoBuilder::new(module_name));
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
while i < gpu::Chipset::NAMES.len() {
|
||||||
|
this = this.make_entry_chipset(gpu::Chipset::NAMES[i]);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
use kernel::{
|
||||||
|
device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::driver::Bar0;
|
||||||
|
use crate::regs;
|
||||||
|
use crate::util;
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
macro_rules! define_chipset {
|
||||||
|
({ $($variant:ident = $value:expr),* $(,)* }) =>
|
||||||
|
{
|
||||||
|
/// Enum representation of the GPU chipset.
|
||||||
|
#[derive(fmt::Debug)]
|
||||||
|
pub(crate) enum Chipset {
|
||||||
|
$($variant = $value),*,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chipset {
|
||||||
|
pub(crate) const ALL: &'static [Chipset] = &[
|
||||||
|
$( Chipset::$variant, )*
|
||||||
|
];
|
||||||
|
|
||||||
|
pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [
|
||||||
|
$( util::const_bytes_to_str(
|
||||||
|
util::to_lowercase_bytes::<{ stringify!($variant).len() }>(
|
||||||
|
stringify!($variant)
|
||||||
|
).as_slice()
|
||||||
|
), )*
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO replace with something like derive(FromPrimitive)
|
||||||
|
impl TryFrom<u32> for Chipset {
|
||||||
|
type Error = kernel::error::Error;
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
$( $value => Ok(Chipset::$variant), )*
|
||||||
|
_ => Err(ENODEV),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_chipset!({
|
||||||
|
// Turing
|
||||||
|
TU102 = 0x162,
|
||||||
|
TU104 = 0x164,
|
||||||
|
TU106 = 0x166,
|
||||||
|
TU117 = 0x167,
|
||||||
|
TU116 = 0x168,
|
||||||
|
// Ampere
|
||||||
|
GA102 = 0x172,
|
||||||
|
GA103 = 0x173,
|
||||||
|
GA104 = 0x174,
|
||||||
|
GA106 = 0x176,
|
||||||
|
GA107 = 0x177,
|
||||||
|
// Ada
|
||||||
|
AD102 = 0x192,
|
||||||
|
AD103 = 0x193,
|
||||||
|
AD104 = 0x194,
|
||||||
|
AD106 = 0x196,
|
||||||
|
AD107 = 0x197,
|
||||||
|
});
|
||||||
|
|
||||||
|
impl Chipset {
|
||||||
|
pub(crate) fn arch(&self) -> Architecture {
|
||||||
|
match self {
|
||||||
|
Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
|
||||||
|
Architecture::Turing
|
||||||
|
}
|
||||||
|
Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
|
||||||
|
Architecture::Ampere
|
||||||
|
}
|
||||||
|
Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
|
||||||
|
Architecture::Ada
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//
|
||||||
|
// The resulting strings are used to generate firmware paths, hence the
|
||||||
|
// generated strings have to be stable.
|
||||||
|
//
|
||||||
|
// Hence, replace with something like strum_macros derive(Display).
|
||||||
|
//
|
||||||
|
// For now, redirect to fmt::Debug for convenience.
|
||||||
|
impl fmt::Display for Chipset {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representation of the GPU generation.
|
||||||
|
#[derive(fmt::Debug)]
|
||||||
|
pub(crate) enum Architecture {
|
||||||
|
Turing,
|
||||||
|
Ampere,
|
||||||
|
Ada,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Revision {
|
||||||
|
major: u8,
|
||||||
|
minor: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Revision {
|
||||||
|
fn from_boot0(boot0: regs::Boot0) -> Self {
|
||||||
|
Self {
|
||||||
|
major: boot0.major_rev(),
|
||||||
|
minor: boot0.minor_rev(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Revision {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:x}.{:x}", self.major, self.minor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure holding the metadata of the GPU.
|
||||||
|
pub(crate) struct Spec {
|
||||||
|
chipset: Chipset,
|
||||||
|
/// The revision of the chipset.
|
||||||
|
revision: Revision,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spec {
|
||||||
|
fn new(bar: &Devres<Bar0>) -> Result<Spec> {
|
||||||
|
let bar = bar.try_access().ok_or(ENXIO)?;
|
||||||
|
let boot0 = regs::Boot0::read(&bar);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
chipset: boot0.chipset().try_into()?,
|
||||||
|
revision: Revision::from_boot0(boot0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure encapsulating the firmware blobs required for the GPU to operate.
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub(crate) struct Firmware {
|
||||||
|
booter_load: firmware::Firmware,
|
||||||
|
booter_unload: firmware::Firmware,
|
||||||
|
bootloader: firmware::Firmware,
|
||||||
|
gsp: firmware::Firmware,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Firmware {
|
||||||
|
fn new(dev: &device::Device, spec: &Spec, ver: &str) -> Result<Firmware> {
|
||||||
|
let mut chip_name = CString::try_from_fmt(fmt!("{}", spec.chipset))?;
|
||||||
|
chip_name.make_ascii_lowercase();
|
||||||
|
|
||||||
|
let request = |name_| {
|
||||||
|
CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
|
||||||
|
.and_then(|path| firmware::Firmware::request(&path, dev))
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Firmware {
|
||||||
|
booter_load: request("booter_load")?,
|
||||||
|
booter_unload: request("booter_unload")?,
|
||||||
|
bootloader: request("bootloader")?,
|
||||||
|
gsp: request("gsp")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure holding the resources required to operate the GPU.
|
||||||
|
#[pin_data]
|
||||||
|
pub(crate) struct Gpu {
|
||||||
|
spec: Spec,
|
||||||
|
/// MMIO mapping of PCI BAR 0
|
||||||
|
bar: Devres<Bar0>,
|
||||||
|
fw: Firmware,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpu {
|
||||||
|
pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<Self>> {
|
||||||
|
let spec = Spec::new(&bar)?;
|
||||||
|
let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?;
|
||||||
|
|
||||||
|
dev_info!(
|
||||||
|
pdev.as_ref(),
|
||||||
|
"NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
|
||||||
|
spec.chipset,
|
||||||
|
spec.chipset.arch(),
|
||||||
|
spec.revision
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(pin_init!(Self { spec, bar, fw }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
//! Nova Core GPU Driver
|
||||||
|
|
||||||
|
mod driver;
|
||||||
|
mod firmware;
|
||||||
|
mod gpu;
|
||||||
|
mod regs;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
kernel::module_pci_driver! {
|
||||||
|
type: driver::NovaCore,
|
||||||
|
name: "NovaCore",
|
||||||
|
author: "Danilo Krummrich",
|
||||||
|
description: "Nova Core GPU driver",
|
||||||
|
license: "GPL v2",
|
||||||
|
firmware: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel::module_firmware!(firmware::ModInfoBuilder);
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
use crate::driver::Bar0;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//
|
||||||
|
// Create register definitions via generic macros. See task "Generic register
|
||||||
|
// abstraction" in Documentation/gpu/nova/core/todo.rst.
|
||||||
|
|
||||||
|
const BOOT0_OFFSET: usize = 0x00000000;
|
||||||
|
|
||||||
|
// 3:0 - chipset minor revision
|
||||||
|
const BOOT0_MINOR_REV_SHIFT: u8 = 0;
|
||||||
|
const BOOT0_MINOR_REV_MASK: u32 = 0x0000000f;
|
||||||
|
|
||||||
|
// 7:4 - chipset major revision
|
||||||
|
const BOOT0_MAJOR_REV_SHIFT: u8 = 4;
|
||||||
|
const BOOT0_MAJOR_REV_MASK: u32 = 0x000000f0;
|
||||||
|
|
||||||
|
// 23:20 - chipset implementation Identifier (depends on architecture)
|
||||||
|
const BOOT0_IMPL_SHIFT: u8 = 20;
|
||||||
|
const BOOT0_IMPL_MASK: u32 = 0x00f00000;
|
||||||
|
|
||||||
|
// 28:24 - chipset architecture identifier
|
||||||
|
const BOOT0_ARCH_MASK: u32 = 0x1f000000;
|
||||||
|
|
||||||
|
// 28:20 - chipset identifier (virtual register field combining BOOT0_IMPL and
|
||||||
|
// BOOT0_ARCH)
|
||||||
|
const BOOT0_CHIPSET_SHIFT: u8 = BOOT0_IMPL_SHIFT;
|
||||||
|
const BOOT0_CHIPSET_MASK: u32 = BOOT0_IMPL_MASK | BOOT0_ARCH_MASK;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) struct Boot0(u32);
|
||||||
|
|
||||||
|
impl Boot0 {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn read(bar: &Bar0) -> Self {
|
||||||
|
Self(bar.readl(BOOT0_OFFSET))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn chipset(&self) -> u32 {
|
||||||
|
(self.0 & BOOT0_CHIPSET_MASK) >> BOOT0_CHIPSET_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn minor_rev(&self) -> u8 {
|
||||||
|
((self.0 & BOOT0_MINOR_REV_MASK) >> BOOT0_MINOR_REV_SHIFT) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn major_rev(&self) -> u8 {
|
||||||
|
((self.0 & BOOT0_MAJOR_REV_MASK) >> BOOT0_MAJOR_REV_SHIFT) as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
pub(crate) const fn to_lowercase_bytes<const N: usize>(s: &str) -> [u8; N] {
|
||||||
|
let src = s.as_bytes();
|
||||||
|
let mut dst = [0; N];
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
while i < src.len() && i < N {
|
||||||
|
dst[i] = (src[i] as char).to_ascii_lowercase() as u8;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn const_bytes_to_str(bytes: &[u8]) -> &str {
|
||||||
|
match core::str::from_utf8(bytes) {
|
||||||
|
Ok(string) => string,
|
||||||
|
Err(_) => kernel::build_error!("Bytes are not valid UTF-8."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,6 +39,7 @@ source "drivers/gpu/vga/Kconfig"
|
||||||
|
|
||||||
source "drivers/gpu/host1x/Kconfig"
|
source "drivers/gpu/host1x/Kconfig"
|
||||||
source "drivers/gpu/ipu-v3/Kconfig"
|
source "drivers/gpu/ipu-v3/Kconfig"
|
||||||
|
source "drivers/gpu/nova-core/Kconfig"
|
||||||
|
|
||||||
source "drivers/gpu/drm/Kconfig"
|
source "drivers/gpu/drm/Kconfig"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
// SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, references to which are safe to
|
||||||
// be used from any thread.
|
// be used from any thread.
|
||||||
unsafe impl Sync for Firmware {}
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,10 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
||||||
kernel::ThisModule::from_ptr(core::ptr::null_mut())
|
kernel::ThisModule::from_ptr(core::ptr::null_mut())
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
/// The `LocalModule` type is the type of the module created by `module!`,
|
||||||
|
/// `module_pci_driver!`, `module_platform_driver!`, etc.
|
||||||
|
type LocalModule = {type_};
|
||||||
|
|
||||||
impl kernel::ModuleMetadata for {type_} {{
|
impl kernel::ModuleMetadata for {type_} {{
|
||||||
const NAME: &'static kernel::str::CStr = kernel::c_str!(\"{name}\");
|
const NAME: &'static kernel::str::CStr = kernel::c_str!(\"{name}\");
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue