mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00
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
|
||||
panthor
|
||||
zynqmp
|
||||
nova/index
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
24
Documentation/gpu/nova/core/guidelines.rst
Normal file
24
Documentation/gpu/nova/core/guidelines.rst
Normal file
@ -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.
|
446
Documentation/gpu/nova/core/todo.rst
Normal file
446
Documentation/gpu/nova/core/todo.rst
Normal file
@ -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
|
69
Documentation/gpu/nova/guidelines.rst
Normal file
69
Documentation/gpu/nova/guidelines.rst
Normal file
@ -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.
|
30
Documentation/gpu/nova/index.rst
Normal file
30
Documentation/gpu/nova/index.rst
Normal file
@ -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: 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
|
||||
M: Stefan Mavrodiev <stefan@olimex.com>
|
||||
S: Maintained
|
||||
|
@ -5,3 +5,4 @@
|
||||
obj-y += host1x/ drm/ vga/
|
||||
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
|
||||
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
|
||||
obj-$(CONFIG_NOVA_CORE) += nova-core/
|
||||
|
14
drivers/gpu/nova-core/Kconfig
Normal file
14
drivers/gpu/nova-core/Kconfig
Normal file
@ -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.
|
3
drivers/gpu/nova-core/Makefile
Normal file
3
drivers/gpu/nova-core/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_NOVA_CORE) += nova_core.o
|
47
drivers/gpu/nova-core/driver.rs
Normal file
47
drivers/gpu/nova-core/driver.rs
Normal file
@ -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)
|
||||
}
|
||||
}
|
45
drivers/gpu/nova-core/firmware.rs
Normal file
45
drivers/gpu/nova-core/firmware.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
199
drivers/gpu/nova-core/gpu.rs
Normal file
199
drivers/gpu/nova-core/gpu.rs
Normal file
@ -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 }))
|
||||
}
|
||||
}
|
20
drivers/gpu/nova-core/nova_core.rs
Normal file
20
drivers/gpu/nova-core/nova_core.rs
Normal file
@ -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);
|
55
drivers/gpu/nova-core/regs.rs
Normal file
55
drivers/gpu/nova-core/regs.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
21
drivers/gpu/nova-core/util.rs
Normal file
21
drivers/gpu/nova-core/util.rs
Normal file
@ -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/ipu-v3/Kconfig"
|
||||
source "drivers/gpu/nova-core/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
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +228,10 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
|
||||
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_} {{
|
||||
const NAME: &'static kernel::str::CStr = kernel::c_str!(\"{name}\");
|
||||
}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user