Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+1207
+1
Documentation/gpu/drivers.rst
··· 25 25 panfrost 26 26 panthor 27 27 zynqmp 28 + nova/index 28 29 29 30 .. only:: subproject and html 30 31
+24
Documentation/gpu/nova/core/guidelines.rst
··· 1 + .. SPDX-License-Identifier: (GPL-2.0+ OR MIT) 2 + 3 + ========== 4 + Guidelines 5 + ========== 6 + 7 + This documents contains the guidelines for nova-core. Additionally, all common 8 + guidelines of the Nova project do apply. 9 + 10 + Driver API 11 + ========== 12 + 13 + One main purpose of nova-core is to implement the abstraction around the 14 + firmware interface of GSP and provide a firmware (version) independent API for 15 + 2nd level drivers, such as nova-drm or the vGPU manager VFIO driver. 16 + 17 + Therefore, it is not permitted to leak firmware (version) specifics, through the 18 + driver API, to 2nd level drivers. 19 + 20 + Acceptance Criteria 21 + =================== 22 + 23 + - To the extend possible, patches submitted to nova-core must be tested for 24 + regressions with all 2nd level drivers.
+446
Documentation/gpu/nova/core/todo.rst
··· 1 + .. SPDX-License-Identifier: (GPL-2.0+ OR MIT) 2 + 3 + ========= 4 + Task List 5 + ========= 6 + 7 + Tasks may have the following fields: 8 + 9 + - ``Complexity``: Describes the required familiarity with Rust and / or the 10 + corresponding kernel APIs or subsystems. There are four different complexities, 11 + ``Beginner``, ``Intermediate``, ``Advanced`` and ``Expert``. 12 + - ``Reference``: References to other tasks. 13 + - ``Link``: Links to external resources. 14 + - ``Contact``: The person that can be contacted for further information about 15 + the task. 16 + 17 + Enablement (Rust) 18 + ================= 19 + 20 + Tasks that are not directly related to nova-core, but are preconditions in terms 21 + of required APIs. 22 + 23 + FromPrimitive API 24 + ----------------- 25 + 26 + Sometimes the need arises to convert a number to a value of an enum or a 27 + structure. 28 + 29 + A good example from nova-core would be the ``Chipset`` enum type, which defines 30 + the value ``AD102``. When probing the GPU the value ``0x192`` can be read from a 31 + certain register indication the chipset AD102. Hence, the enum value ``AD102`` 32 + should be derived from the number ``0x192``. Currently, nova-core uses a custom 33 + implementation (``Chipset::from_u32`` for this. 34 + 35 + Instead, it would be desirable to have something like the ``FromPrimitive`` 36 + trait [1] from the num crate. 37 + 38 + Having this generalization also helps with implementing a generic macro that 39 + automatically generates the corresponding mappings between a value and a number. 40 + 41 + | Complexity: Beginner 42 + | Link: https://docs.rs/num/latest/num/trait.FromPrimitive.html 43 + 44 + Generic register abstraction 45 + ---------------------------- 46 + 47 + Work out how register constants and structures can be automatically generated 48 + through generalized macros. 49 + 50 + Example: 51 + 52 + .. code-block:: rust 53 + 54 + register!(BOOT0, 0x0, u32, pci::Bar<SIZE>, Fields [ 55 + MINOR_REVISION(3:0, RO), 56 + MAJOR_REVISION(7:4, RO), 57 + REVISION(7:0, RO), // Virtual register combining major and minor rev. 58 + ]) 59 + 60 + This could expand to something like: 61 + 62 + .. code-block:: rust 63 + 64 + const BOOT0_OFFSET: usize = 0x00000000; 65 + const BOOT0_MINOR_REVISION_SHIFT: u8 = 0; 66 + const BOOT0_MINOR_REVISION_MASK: u32 = 0x0000000f; 67 + const BOOT0_MAJOR_REVISION_SHIFT: u8 = 4; 68 + const BOOT0_MAJOR_REVISION_MASK: u32 = 0x000000f0; 69 + const BOOT0_REVISION_SHIFT: u8 = BOOT0_MINOR_REVISION_SHIFT; 70 + const BOOT0_REVISION_MASK: u32 = BOOT0_MINOR_REVISION_MASK | BOOT0_MAJOR_REVISION_MASK; 71 + 72 + struct Boot0(u32); 73 + 74 + impl Boot0 { 75 + #[inline] 76 + fn read(bar: &RevocableGuard<'_, pci::Bar<SIZE>>) -> Self { 77 + Self(bar.readl(BOOT0_OFFSET)) 78 + } 79 + 80 + #[inline] 81 + fn minor_revision(&self) -> u32 { 82 + (self.0 & BOOT0_MINOR_REVISION_MASK) >> BOOT0_MINOR_REVISION_SHIFT 83 + } 84 + 85 + #[inline] 86 + fn major_revision(&self) -> u32 { 87 + (self.0 & BOOT0_MAJOR_REVISION_MASK) >> BOOT0_MAJOR_REVISION_SHIFT 88 + } 89 + 90 + #[inline] 91 + fn revision(&self) -> u32 { 92 + (self.0 & BOOT0_REVISION_MASK) >> BOOT0_REVISION_SHIFT 93 + } 94 + } 95 + 96 + Usage: 97 + 98 + .. code-block:: rust 99 + 100 + let bar = bar.try_access().ok_or(ENXIO)?; 101 + 102 + let boot0 = Boot0::read(&bar); 103 + pr_info!("Revision: {}\n", boot0.revision()); 104 + 105 + | Complexity: Advanced 106 + 107 + Delay / Sleep abstractions 108 + -------------------------- 109 + 110 + Rust abstractions for the kernel's delay() and sleep() functions. 111 + 112 + FUJITA Tomonori plans to work on abstractions for read_poll_timeout_atomic() 113 + (and friends) [1]. 114 + 115 + | Complexity: Beginner 116 + | Link: https://lore.kernel.org/netdev/20250228.080550.354359820929821928.fujita.tomonori@gmail.com/ [1] 117 + 118 + IRQ abstractions 119 + ---------------- 120 + 121 + Rust abstractions for IRQ handling. 122 + 123 + There is active ongoing work from Daniel Almeida [1] for the "core" abstractions 124 + to request IRQs. 125 + 126 + Besides optional review and testing work, the required ``pci::Device`` code 127 + around those core abstractions needs to be worked out. 128 + 129 + | Complexity: Intermediate 130 + | Link: https://lore.kernel.org/lkml/20250122163932.46697-1-daniel.almeida@collabora.com/ [1] 131 + | Contact: Daniel Almeida 132 + 133 + Page abstraction for foreign pages 134 + ---------------------------------- 135 + 136 + Rust abstractions for pages not created by the Rust page abstraction without 137 + direct ownership. 138 + 139 + There is active onging work from Abdiel Janulgue [1] and Lina [2]. 140 + 141 + | Complexity: Advanced 142 + | Link: https://lore.kernel.org/linux-mm/20241119112408.779243-1-abdiel.janulgue@gmail.com/ [1] 143 + | Link: https://lore.kernel.org/rust-for-linux/20250202-rust-page-v1-0-e3170d7fe55e@asahilina.net/ [2] 144 + 145 + Scatterlist / sg_table abstractions 146 + ----------------------------------- 147 + 148 + Rust abstractions for scatterlist / sg_table. 149 + 150 + There is preceding work from Abdiel Janulgue, which hasn't made it to the 151 + mailing list yet. 152 + 153 + | Complexity: Intermediate 154 + | Contact: Abdiel Janulgue 155 + 156 + ELF utils 157 + --------- 158 + 159 + Rust implementation of ELF header representation to retrieve section header 160 + tables, names, and data from an ELF-formatted images. 161 + 162 + There is preceding work from Abdiel Janulgue, which hasn't made it to the 163 + mailing list yet. 164 + 165 + | Complexity: Beginner 166 + | Contact: Abdiel Janulgue 167 + 168 + PCI MISC APIs 169 + ------------- 170 + 171 + Extend the existing PCI device / driver abstractions by SR-IOV, config space, 172 + capability, MSI API abstractions. 173 + 174 + | Complexity: Beginner 175 + 176 + Auxiliary bus abstractions 177 + -------------------------- 178 + 179 + Rust abstraction for the auxiliary bus APIs. 180 + 181 + This is needed to connect nova-core to the nova-drm driver. 182 + 183 + | Complexity: Intermediate 184 + 185 + Debugfs abstractions 186 + -------------------- 187 + 188 + Rust abstraction for debugfs APIs. 189 + 190 + | Reference: Export GSP log buffers 191 + | Complexity: Intermediate 192 + 193 + Vec extensions 194 + -------------- 195 + 196 + Implement ``Vec::truncate`` and ``Vec::resize``. 197 + 198 + Currently this is used for some experimental code to parse the vBIOS. 199 + 200 + | Reference vBIOS support 201 + | Complexity: Beginner 202 + 203 + GPU (general) 204 + ============= 205 + 206 + Parse firmware headers 207 + ---------------------- 208 + 209 + Parse ELF headers from the firmware files loaded from the filesystem. 210 + 211 + | Reference: ELF utils 212 + | Complexity: Beginner 213 + | Contact: Abdiel Janulgue 214 + 215 + Build radix3 page table 216 + ----------------------- 217 + 218 + Build the radix3 page table to map the firmware. 219 + 220 + | Complexity: Intermediate 221 + | Contact: Abdiel Janulgue 222 + 223 + vBIOS support 224 + ------------- 225 + 226 + Parse the vBIOS and probe the structures required for driver initialization. 227 + 228 + | Contact: Dave Airlie 229 + | Reference: Vec extensions 230 + | Complexity: Intermediate 231 + 232 + Initial Devinit support 233 + ----------------------- 234 + 235 + Implement BIOS Device Initialization, i.e. memory sizing, waiting, PLL 236 + configuration. 237 + 238 + | Contact: Dave Airlie 239 + | Complexity: Beginner 240 + 241 + Boot Falcon controller 242 + ---------------------- 243 + 244 + Infrastructure to load and execute falcon (sec2) firmware images; handle the 245 + GSP falcon processor and fwsec loading. 246 + 247 + | Complexity: Advanced 248 + | Contact: Dave Airlie 249 + 250 + GPU Timer support 251 + ----------------- 252 + 253 + Support for the GPU's internal timer peripheral. 254 + 255 + | Complexity: Beginner 256 + | Contact: Dave Airlie 257 + 258 + MMU / PT management 259 + ------------------- 260 + 261 + Work out the architecture for MMU / page table management. 262 + 263 + We need to consider that nova-drm will need rather fine-grained control, 264 + especially in terms of locking, in order to be able to implement asynchronous 265 + Vulkan queues. 266 + 267 + While generally sharing the corresponding code is desirable, it needs to be 268 + evaluated how (and if at all) sharing the corresponding code is expedient. 269 + 270 + | Complexity: Expert 271 + 272 + VRAM memory allocator 273 + --------------------- 274 + 275 + Investigate options for a VRAM memory allocator. 276 + 277 + Some possible options: 278 + - Rust abstractions for 279 + - RB tree (interval tree) / drm_mm 280 + - maple_tree 281 + - native Rust collections 282 + 283 + | Complexity: Advanced 284 + 285 + Instance Memory 286 + --------------- 287 + 288 + Implement support for instmem (bar2) used to store page tables. 289 + 290 + | Complexity: Intermediate 291 + | Contact: Dave Airlie 292 + 293 + GPU System Processor (GSP) 294 + ========================== 295 + 296 + Export GSP log buffers 297 + ---------------------- 298 + 299 + Recent patches from Timur Tabi [1] added support to expose GSP-RM log buffers 300 + (even after failure to probe the driver) through debugfs. 301 + 302 + This is also an interesting feature for nova-core, especially in the early days. 303 + 304 + | Link: https://lore.kernel.org/nouveau/20241030202952.694055-2-ttabi@nvidia.com/ [1] 305 + | Reference: Debugfs abstractions 306 + | Complexity: Intermediate 307 + 308 + GSP firmware abstraction 309 + ------------------------ 310 + 311 + The GSP-RM firmware API is unstable and may incompatibly change from version to 312 + version, in terms of data structures and semantics. 313 + 314 + This problem is one of the big motivations for using Rust for nova-core, since 315 + it turns out that Rust's procedural macro feature provides a rather elegant way 316 + to address this issue: 317 + 318 + 1. generate Rust structures from the C headers in a separate namespace per version 319 + 2. build abstraction structures (within a generic namespace) that implement the 320 + firmware interfaces; annotate the differences in implementation with version 321 + identifiers 322 + 3. use a procedural macro to generate the actual per version implementation out 323 + of this abstraction 324 + 4. instantiate the correct version type one on runtime (can be sure that all 325 + have the same interface because it's defined by a common trait) 326 + 327 + There is a PoC implementation of this pattern, in the context of the nova-core 328 + PoC driver. 329 + 330 + This task aims at refining the feature and ideally generalize it, to be usable 331 + by other drivers as well. 332 + 333 + | Complexity: Expert 334 + 335 + GSP message queue 336 + ----------------- 337 + 338 + Implement low level GSP message queue (command, status) for communication 339 + between the kernel driver and GSP. 340 + 341 + | Complexity: Advanced 342 + | Contact: Dave Airlie 343 + 344 + Bootstrap GSP 345 + ------------- 346 + 347 + Call the boot firmware to boot the GSP processor; execute initial control 348 + messages. 349 + 350 + | Complexity: Intermediate 351 + | Contact: Dave Airlie 352 + 353 + Client / Device APIs 354 + -------------------- 355 + 356 + Implement the GSP message interface for client / device allocation and the 357 + corresponding client and device allocation APIs. 358 + 359 + | Complexity: Intermediate 360 + | Contact: Dave Airlie 361 + 362 + Bar PDE handling 363 + ---------------- 364 + 365 + Synchronize page table handling for BARs between the kernel driver and GSP. 366 + 367 + | Complexity: Beginner 368 + | Contact: Dave Airlie 369 + 370 + FIFO engine 371 + ----------- 372 + 373 + Implement support for the FIFO engine, i.e. the corresponding GSP message 374 + interface and provide an API for chid allocation and channel handling. 375 + 376 + | Complexity: Advanced 377 + | Contact: Dave Airlie 378 + 379 + GR engine 380 + --------- 381 + 382 + Implement support for the graphics engine, i.e. the corresponding GSP message 383 + interface and provide an API for (golden) context creation and promotion. 384 + 385 + | Complexity: Advanced 386 + | Contact: Dave Airlie 387 + 388 + CE engine 389 + --------- 390 + 391 + Implement support for the copy engine, i.e. the corresponding GSP message 392 + interface. 393 + 394 + | Complexity: Intermediate 395 + | Contact: Dave Airlie 396 + 397 + VFN IRQ controller 398 + ------------------ 399 + 400 + Support for the VFN interrupt controller. 401 + 402 + | Complexity: Intermediate 403 + | Contact: Dave Airlie 404 + 405 + External APIs 406 + ============= 407 + 408 + nova-core base API 409 + ------------------ 410 + 411 + Work out the common pieces of the API to connect 2nd level drivers, i.e. vGPU 412 + manager and nova-drm. 413 + 414 + | Complexity: Advanced 415 + 416 + vGPU manager API 417 + ---------------- 418 + 419 + Work out the API parts required by the vGPU manager, which are not covered by 420 + the base API. 421 + 422 + | Complexity: Advanced 423 + 424 + nova-core C API 425 + --------------- 426 + 427 + Implement a C wrapper for the APIs required by the vGPU manager driver. 428 + 429 + | Complexity: Intermediate 430 + 431 + Testing 432 + ======= 433 + 434 + CI pipeline 435 + ----------- 436 + 437 + Investigate option for continuous integration testing. 438 + 439 + This can go from as simple as running KUnit tests over running (graphics) CTS to 440 + booting up (multiple) guest VMs to test VFIO use-cases. 441 + 442 + It might also be worth to consider the introduction of a new test suite directly 443 + sitting on top of the uAPI for more targeted testing and debugging. There may be 444 + options for collaboration / shared code with the Mesa project. 445 + 446 + | Complexity: Advanced
+69
Documentation/gpu/nova/guidelines.rst
··· 1 + .. SPDX-License-Identifier: (GPL-2.0+ OR MIT) 2 + 3 + ========== 4 + Guidelines 5 + ========== 6 + 7 + This document describes the general project guidelines that apply to nova-core 8 + and nova-drm. 9 + 10 + Language 11 + ======== 12 + 13 + The Nova project uses the Rust programming language. In this context, all rules 14 + of the Rust for Linux project as documented in 15 + :doc:`../../rust/general-information` apply. Additionally, the following rules 16 + apply. 17 + 18 + - Unless technically necessary otherwise (e.g. uAPI), any driver code is written 19 + in Rust. 20 + 21 + - Unless technically necessary, unsafe Rust code must be avoided. In case of 22 + technical necessity, unsafe code should be isolated in a separate component 23 + providing a safe API for other driver code to use. 24 + 25 + Style 26 + ----- 27 + 28 + All rules of the Rust for Linux project as documented in 29 + :doc:`../../rust/coding-guidelines` apply. 30 + 31 + For a submit checklist, please also see the `Rust for Linux Submit checklist 32 + addendum <https://rust-for-linux.com/contributing#submit-checklist-addendum>`_. 33 + 34 + Documentation 35 + ============= 36 + 37 + The availability of proper documentation is essential in terms of scalability, 38 + accessibility for new contributors and maintainability of a project in general, 39 + but especially for a driver running as complex hardware as Nova is targeting. 40 + 41 + Hence, adding documentation of any kind is very much encouraged by the project. 42 + 43 + Besides that, there are some minimum requirements. 44 + 45 + - Every non-private structure needs at least a brief doc comment explaining the 46 + semantical sense of the structure, as well as potential locking and lifetime 47 + requirements. It is encouraged to have the same minimum documentation for 48 + non-trivial private structures. 49 + 50 + - uAPIs must be fully documented with kernel-doc comments; additionally, the 51 + semantical behavior must be explained including potential special or corner 52 + cases. 53 + 54 + - The APIs connecting the 1st level driver (nova-core) with 2nd level drivers 55 + must be fully documented. This includes doc comments, potential locking and 56 + lifetime requirements, as well as example code if applicable. 57 + 58 + - Abbreviations must be explained when introduced; terminology must be uniquely 59 + defined. 60 + 61 + - Register addresses, layouts, shift values and masks must be defined properly; 62 + unless obvious, the semantical sense must be documented. This only applies if 63 + the author is able to obtain the corresponding information. 64 + 65 + Acceptance Criteria 66 + =================== 67 + 68 + - Patches must only be applied if reviewed by at least one other person on the 69 + mailing list; this also applies for maintainers.
+30
Documentation/gpu/nova/index.rst
··· 1 + .. SPDX-License-Identifier: (GPL-2.0+ OR MIT) 2 + 3 + ======================= 4 + nova NVIDIA GPU drivers 5 + ======================= 6 + 7 + The nova driver project consists out of two separate drivers nova-core and 8 + nova-drm and intends to supersede the nouveau driver for NVIDIA GPUs based on 9 + the GPU System Processor (GSP). 10 + 11 + The following documents apply to both nova-core and nova-drm. 12 + 13 + .. toctree:: 14 + :titlesonly: 15 + 16 + guidelines 17 + 18 + nova-core 19 + ========= 20 + 21 + The nova-core driver is the core driver for NVIDIA GPUs based on GSP. nova-core, 22 + as the 1st level driver, provides an abstraction around the GPUs hard- and 23 + firmware interfaces providing a common base for 2nd level drivers, such as the 24 + vGPU manager VFIO driver and the nova-drm driver. 25 + 26 + .. toctree:: 27 + :titlesonly: 28 + 29 + core/guidelines 30 + core/todo
+11
MAINTAINERS
··· 7457 7457 F: drivers/gpu/drm/nouveau/ 7458 7458 F: include/uapi/drm/nouveau_drm.h 7459 7459 7460 + CORE DRIVER FOR NVIDIA GPUS [RUST] 7461 + M: Danilo Krummrich <dakr@kernel.org> 7462 + L: nouveau@lists.freedesktop.org 7463 + S: Supported 7464 + Q: https://patchwork.freedesktop.org/project/nouveau/ 7465 + B: https://gitlab.freedesktop.org/drm/nova/-/issues 7466 + C: irc://irc.oftc.net/nouveau 7467 + T: git https://gitlab.freedesktop.org/drm/nova.git nova-next 7468 + F: Documentation/gpu/nova/ 7469 + F: drivers/gpu/nova-core/ 7470 + 7460 7471 DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS 7461 7472 M: Stefan Mavrodiev <stefan@olimex.com> 7462 7473 S: Maintained
+1
drivers/gpu/Makefile
··· 5 5 obj-y += host1x/ drm/ vga/ 6 6 obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ 7 7 obj-$(CONFIG_TRACE_GPU_MEM) += trace/ 8 + obj-$(CONFIG_NOVA_CORE) += nova-core/
+14
drivers/gpu/nova-core/Kconfig
··· 1 + config NOVA_CORE 2 + tristate "Nova Core GPU driver" 3 + depends on PCI 4 + depends on RUST 5 + depends on RUST_FW_LOADER_ABSTRACTIONS 6 + default n 7 + help 8 + Choose this if you want to build the Nova Core driver for Nvidia 9 + GPUs based on the GPU System Processor (GSP). This is true for Turing 10 + and later GPUs. 11 + 12 + This driver is work in progress and may not be functional. 13 + 14 + If M is selected, the module will be called nova_core.
+3
drivers/gpu/nova-core/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + obj-$(CONFIG_NOVA_CORE) += nova_core.o
+47
drivers/gpu/nova-core/driver.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use kernel::{bindings, c_str, pci, prelude::*}; 4 + 5 + use crate::gpu::Gpu; 6 + 7 + #[pin_data] 8 + pub(crate) struct NovaCore { 9 + #[pin] 10 + pub(crate) gpu: Gpu, 11 + } 12 + 13 + const BAR0_SIZE: usize = 8; 14 + pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>; 15 + 16 + kernel::pci_device_table!( 17 + PCI_TABLE, 18 + MODULE_PCI_TABLE, 19 + <NovaCore as pci::Driver>::IdInfo, 20 + [( 21 + pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as _), 22 + () 23 + )] 24 + ); 25 + 26 + impl pci::Driver for NovaCore { 27 + type IdInfo = (); 28 + const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; 29 + 30 + fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { 31 + dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); 32 + 33 + pdev.enable_device_mem()?; 34 + pdev.set_master(); 35 + 36 + let bar = pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0"))?; 37 + 38 + let this = KBox::pin_init( 39 + try_pin_init!(Self { 40 + gpu <- Gpu::new(pdev, bar)?, 41 + }), 42 + GFP_KERNEL, 43 + )?; 44 + 45 + Ok(this) 46 + } 47 + }
+45
drivers/gpu/nova-core/firmware.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use crate::gpu; 4 + use kernel::firmware; 5 + 6 + pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>); 7 + 8 + impl<const N: usize> ModInfoBuilder<N> { 9 + const VERSION: &'static str = "535.113.01"; 10 + 11 + const fn make_entry_file(self, chipset: &str, fw: &str) -> Self { 12 + ModInfoBuilder( 13 + self.0 14 + .new_entry() 15 + .push("nvidia/") 16 + .push(chipset) 17 + .push("/gsp/") 18 + .push(fw) 19 + .push("-") 20 + .push(Self::VERSION) 21 + .push(".bin"), 22 + ) 23 + } 24 + 25 + const fn make_entry_chipset(self, chipset: &str) -> Self { 26 + self.make_entry_file(chipset, "booter_load") 27 + .make_entry_file(chipset, "booter_unload") 28 + .make_entry_file(chipset, "bootloader") 29 + .make_entry_file(chipset, "gsp") 30 + } 31 + 32 + pub(crate) const fn create( 33 + module_name: &'static kernel::str::CStr, 34 + ) -> firmware::ModInfoBuilder<N> { 35 + let mut this = Self(firmware::ModInfoBuilder::new(module_name)); 36 + let mut i = 0; 37 + 38 + while i < gpu::Chipset::NAMES.len() { 39 + this = this.make_entry_chipset(gpu::Chipset::NAMES[i]); 40 + i += 1; 41 + } 42 + 43 + this.0 44 + } 45 + }
+199
drivers/gpu/nova-core/gpu.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use kernel::{ 4 + device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString, 5 + }; 6 + 7 + use crate::driver::Bar0; 8 + use crate::regs; 9 + use crate::util; 10 + use core::fmt; 11 + 12 + macro_rules! define_chipset { 13 + ({ $($variant:ident = $value:expr),* $(,)* }) => 14 + { 15 + /// Enum representation of the GPU chipset. 16 + #[derive(fmt::Debug)] 17 + pub(crate) enum Chipset { 18 + $($variant = $value),*, 19 + } 20 + 21 + impl Chipset { 22 + pub(crate) const ALL: &'static [Chipset] = &[ 23 + $( Chipset::$variant, )* 24 + ]; 25 + 26 + pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [ 27 + $( util::const_bytes_to_str( 28 + util::to_lowercase_bytes::<{ stringify!($variant).len() }>( 29 + stringify!($variant) 30 + ).as_slice() 31 + ), )* 32 + ]; 33 + } 34 + 35 + // TODO replace with something like derive(FromPrimitive) 36 + impl TryFrom<u32> for Chipset { 37 + type Error = kernel::error::Error; 38 + 39 + fn try_from(value: u32) -> Result<Self, Self::Error> { 40 + match value { 41 + $( $value => Ok(Chipset::$variant), )* 42 + _ => Err(ENODEV), 43 + } 44 + } 45 + } 46 + } 47 + } 48 + 49 + define_chipset!({ 50 + // Turing 51 + TU102 = 0x162, 52 + TU104 = 0x164, 53 + TU106 = 0x166, 54 + TU117 = 0x167, 55 + TU116 = 0x168, 56 + // Ampere 57 + GA102 = 0x172, 58 + GA103 = 0x173, 59 + GA104 = 0x174, 60 + GA106 = 0x176, 61 + GA107 = 0x177, 62 + // Ada 63 + AD102 = 0x192, 64 + AD103 = 0x193, 65 + AD104 = 0x194, 66 + AD106 = 0x196, 67 + AD107 = 0x197, 68 + }); 69 + 70 + impl Chipset { 71 + pub(crate) fn arch(&self) -> Architecture { 72 + match self { 73 + Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => { 74 + Architecture::Turing 75 + } 76 + Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => { 77 + Architecture::Ampere 78 + } 79 + Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => { 80 + Architecture::Ada 81 + } 82 + } 83 + } 84 + } 85 + 86 + // TODO 87 + // 88 + // The resulting strings are used to generate firmware paths, hence the 89 + // generated strings have to be stable. 90 + // 91 + // Hence, replace with something like strum_macros derive(Display). 92 + // 93 + // For now, redirect to fmt::Debug for convenience. 94 + impl fmt::Display for Chipset { 95 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 96 + write!(f, "{:?}", self) 97 + } 98 + } 99 + 100 + /// Enum representation of the GPU generation. 101 + #[derive(fmt::Debug)] 102 + pub(crate) enum Architecture { 103 + Turing, 104 + Ampere, 105 + Ada, 106 + } 107 + 108 + pub(crate) struct Revision { 109 + major: u8, 110 + minor: u8, 111 + } 112 + 113 + impl Revision { 114 + fn from_boot0(boot0: regs::Boot0) -> Self { 115 + Self { 116 + major: boot0.major_rev(), 117 + minor: boot0.minor_rev(), 118 + } 119 + } 120 + } 121 + 122 + impl fmt::Display for Revision { 123 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 124 + write!(f, "{:x}.{:x}", self.major, self.minor) 125 + } 126 + } 127 + 128 + /// Structure holding the metadata of the GPU. 129 + pub(crate) struct Spec { 130 + chipset: Chipset, 131 + /// The revision of the chipset. 132 + revision: Revision, 133 + } 134 + 135 + impl Spec { 136 + fn new(bar: &Devres<Bar0>) -> Result<Spec> { 137 + let bar = bar.try_access().ok_or(ENXIO)?; 138 + let boot0 = regs::Boot0::read(&bar); 139 + 140 + Ok(Self { 141 + chipset: boot0.chipset().try_into()?, 142 + revision: Revision::from_boot0(boot0), 143 + }) 144 + } 145 + } 146 + 147 + /// Structure encapsulating the firmware blobs required for the GPU to operate. 148 + #[expect(dead_code)] 149 + pub(crate) struct Firmware { 150 + booter_load: firmware::Firmware, 151 + booter_unload: firmware::Firmware, 152 + bootloader: firmware::Firmware, 153 + gsp: firmware::Firmware, 154 + } 155 + 156 + impl Firmware { 157 + fn new(dev: &device::Device, spec: &Spec, ver: &str) -> Result<Firmware> { 158 + let mut chip_name = CString::try_from_fmt(fmt!("{}", spec.chipset))?; 159 + chip_name.make_ascii_lowercase(); 160 + 161 + let request = |name_| { 162 + CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver)) 163 + .and_then(|path| firmware::Firmware::request(&path, dev)) 164 + }; 165 + 166 + Ok(Firmware { 167 + booter_load: request("booter_load")?, 168 + booter_unload: request("booter_unload")?, 169 + bootloader: request("bootloader")?, 170 + gsp: request("gsp")?, 171 + }) 172 + } 173 + } 174 + 175 + /// Structure holding the resources required to operate the GPU. 176 + #[pin_data] 177 + pub(crate) struct Gpu { 178 + spec: Spec, 179 + /// MMIO mapping of PCI BAR 0 180 + bar: Devres<Bar0>, 181 + fw: Firmware, 182 + } 183 + 184 + impl Gpu { 185 + pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<Self>> { 186 + let spec = Spec::new(&bar)?; 187 + let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?; 188 + 189 + dev_info!( 190 + pdev.as_ref(), 191 + "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n", 192 + spec.chipset, 193 + spec.chipset.arch(), 194 + spec.revision 195 + ); 196 + 197 + Ok(pin_init!(Self { spec, bar, fw })) 198 + } 199 + }
+20
drivers/gpu/nova-core/nova_core.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! Nova Core GPU Driver 4 + 5 + mod driver; 6 + mod firmware; 7 + mod gpu; 8 + mod regs; 9 + mod util; 10 + 11 + kernel::module_pci_driver! { 12 + type: driver::NovaCore, 13 + name: "NovaCore", 14 + author: "Danilo Krummrich", 15 + description: "Nova Core GPU driver", 16 + license: "GPL v2", 17 + firmware: [], 18 + } 19 + 20 + kernel::module_firmware!(firmware::ModInfoBuilder);
+55
drivers/gpu/nova-core/regs.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use crate::driver::Bar0; 4 + 5 + // TODO 6 + // 7 + // Create register definitions via generic macros. See task "Generic register 8 + // abstraction" in Documentation/gpu/nova/core/todo.rst. 9 + 10 + const BOOT0_OFFSET: usize = 0x00000000; 11 + 12 + // 3:0 - chipset minor revision 13 + const BOOT0_MINOR_REV_SHIFT: u8 = 0; 14 + const BOOT0_MINOR_REV_MASK: u32 = 0x0000000f; 15 + 16 + // 7:4 - chipset major revision 17 + const BOOT0_MAJOR_REV_SHIFT: u8 = 4; 18 + const BOOT0_MAJOR_REV_MASK: u32 = 0x000000f0; 19 + 20 + // 23:20 - chipset implementation Identifier (depends on architecture) 21 + const BOOT0_IMPL_SHIFT: u8 = 20; 22 + const BOOT0_IMPL_MASK: u32 = 0x00f00000; 23 + 24 + // 28:24 - chipset architecture identifier 25 + const BOOT0_ARCH_MASK: u32 = 0x1f000000; 26 + 27 + // 28:20 - chipset identifier (virtual register field combining BOOT0_IMPL and 28 + // BOOT0_ARCH) 29 + const BOOT0_CHIPSET_SHIFT: u8 = BOOT0_IMPL_SHIFT; 30 + const BOOT0_CHIPSET_MASK: u32 = BOOT0_IMPL_MASK | BOOT0_ARCH_MASK; 31 + 32 + #[derive(Copy, Clone)] 33 + pub(crate) struct Boot0(u32); 34 + 35 + impl Boot0 { 36 + #[inline] 37 + pub(crate) fn read(bar: &Bar0) -> Self { 38 + Self(bar.readl(BOOT0_OFFSET)) 39 + } 40 + 41 + #[inline] 42 + pub(crate) fn chipset(&self) -> u32 { 43 + (self.0 & BOOT0_CHIPSET_MASK) >> BOOT0_CHIPSET_SHIFT 44 + } 45 + 46 + #[inline] 47 + pub(crate) fn minor_rev(&self) -> u8 { 48 + ((self.0 & BOOT0_MINOR_REV_MASK) >> BOOT0_MINOR_REV_SHIFT) as u8 49 + } 50 + 51 + #[inline] 52 + pub(crate) fn major_rev(&self) -> u8 { 53 + ((self.0 & BOOT0_MAJOR_REV_MASK) >> BOOT0_MAJOR_REV_SHIFT) as u8 54 + } 55 + }
+21
drivers/gpu/nova-core/util.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + pub(crate) const fn to_lowercase_bytes<const N: usize>(s: &str) -> [u8; N] { 4 + let src = s.as_bytes(); 5 + let mut dst = [0; N]; 6 + let mut i = 0; 7 + 8 + while i < src.len() && i < N { 9 + dst[i] = (src[i] as char).to_ascii_lowercase() as u8; 10 + i += 1; 11 + } 12 + 13 + dst 14 + } 15 + 16 + pub(crate) const fn const_bytes_to_str(bytes: &[u8]) -> &str { 17 + match core::str::from_utf8(bytes) { 18 + Ok(string) => string, 19 + Err(_) => kernel::build_error!("Bytes are not valid UTF-8."), 20 + } 21 + }
+1
drivers/video/Kconfig
··· 39 39 40 40 source "drivers/gpu/host1x/Kconfig" 41 41 source "drivers/gpu/ipu-v3/Kconfig" 42 + source "drivers/gpu/nova-core/Kconfig" 42 43 43 44 source "drivers/gpu/drm/Kconfig" 44 45
+216
rust/kernel/firmware.rs
··· 115 115 // SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, references to which are safe to 116 116 // be used from any thread. 117 117 unsafe impl Sync for Firmware {} 118 + 119 + /// Create firmware .modinfo entries. 120 + /// 121 + /// This macro is the counterpart of the C macro `MODULE_FIRMWARE()`, but instead of taking a 122 + /// simple string literals, which is already covered by the `firmware` field of 123 + /// [`crate::prelude::module!`], it allows the caller to pass a builder type, based on the 124 + /// [`ModInfoBuilder`], which can create the firmware modinfo strings in a more flexible way. 125 + /// 126 + /// Drivers should extend the [`ModInfoBuilder`] with their own driver specific builder type. 127 + /// 128 + /// The `builder` argument must be a type which implements the following function. 129 + /// 130 + /// `const fn create(module_name: &'static CStr) -> ModInfoBuilder` 131 + /// 132 + /// `create` should pass the `module_name` to the [`ModInfoBuilder`] and, with the help of 133 + /// it construct the corresponding firmware modinfo. 134 + /// 135 + /// Typically, such contracts would be enforced by a trait, however traits do not (yet) support 136 + /// const functions. 137 + /// 138 + /// # Example 139 + /// 140 + /// ``` 141 + /// # mod module_firmware_test { 142 + /// # use kernel::firmware; 143 + /// # use kernel::prelude::*; 144 + /// # 145 + /// # struct MyModule; 146 + /// # 147 + /// # impl kernel::Module for MyModule { 148 + /// # fn init(_module: &'static ThisModule) -> Result<Self> { 149 + /// # Ok(Self) 150 + /// # } 151 + /// # } 152 + /// # 153 + /// # 154 + /// struct Builder<const N: usize>; 155 + /// 156 + /// impl<const N: usize> Builder<N> { 157 + /// const DIR: &'static str = "vendor/chip/"; 158 + /// const FILES: [&'static str; 3] = [ "foo", "bar", "baz" ]; 159 + /// 160 + /// const fn create(module_name: &'static kernel::str::CStr) -> firmware::ModInfoBuilder<N> { 161 + /// let mut builder = firmware::ModInfoBuilder::new(module_name); 162 + /// 163 + /// let mut i = 0; 164 + /// while i < Self::FILES.len() { 165 + /// builder = builder.new_entry() 166 + /// .push(Self::DIR) 167 + /// .push(Self::FILES[i]) 168 + /// .push(".bin"); 169 + /// 170 + /// i += 1; 171 + /// } 172 + /// 173 + /// builder 174 + /// } 175 + /// } 176 + /// 177 + /// module! { 178 + /// type: MyModule, 179 + /// name: "module_firmware_test", 180 + /// author: "Rust for Linux", 181 + /// description: "module_firmware! test module", 182 + /// license: "GPL", 183 + /// } 184 + /// 185 + /// kernel::module_firmware!(Builder); 186 + /// # } 187 + /// ``` 188 + #[macro_export] 189 + macro_rules! module_firmware { 190 + // The argument is the builder type without the const generic, since it's deferred from within 191 + // this macro. Hence, we can neither use `expr` nor `ty`. 192 + ($($builder:tt)*) => { 193 + const _: () = { 194 + const __MODULE_FIRMWARE_PREFIX: &'static $crate::str::CStr = if cfg!(MODULE) { 195 + $crate::c_str!("") 196 + } else { 197 + <LocalModule as $crate::ModuleMetadata>::NAME 198 + }; 199 + 200 + #[link_section = ".modinfo"] 201 + #[used] 202 + static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX) 203 + .build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build(); 204 + }; 205 + }; 206 + } 207 + 208 + /// Builder for firmware module info. 209 + /// 210 + /// [`ModInfoBuilder`] is a helper component to flexibly compose firmware paths strings for the 211 + /// .modinfo section in const context. 212 + /// 213 + /// Therefore the [`ModInfoBuilder`] provides the methods [`ModInfoBuilder::new_entry`] and 214 + /// [`ModInfoBuilder::push`], where the latter is used to push path components and the former to 215 + /// mark the beginning of a new path string. 216 + /// 217 + /// [`ModInfoBuilder`] is meant to be used in combination with [`kernel::module_firmware!`]. 218 + /// 219 + /// The const generic `N` as well as the `module_name` parameter of [`ModInfoBuilder::new`] is an 220 + /// internal implementation detail and supplied through the above macro. 221 + pub struct ModInfoBuilder<const N: usize> { 222 + buf: [u8; N], 223 + n: usize, 224 + module_name: &'static CStr, 225 + } 226 + 227 + impl<const N: usize> ModInfoBuilder<N> { 228 + /// Create an empty builder instance. 229 + pub const fn new(module_name: &'static CStr) -> Self { 230 + Self { 231 + buf: [0; N], 232 + n: 0, 233 + module_name, 234 + } 235 + } 236 + 237 + const fn push_internal(mut self, bytes: &[u8]) -> Self { 238 + let mut j = 0; 239 + 240 + if N == 0 { 241 + self.n += bytes.len(); 242 + return self; 243 + } 244 + 245 + while j < bytes.len() { 246 + if self.n < N { 247 + self.buf[self.n] = bytes[j]; 248 + } 249 + self.n += 1; 250 + j += 1; 251 + } 252 + self 253 + } 254 + 255 + /// Push an additional path component. 256 + /// 257 + /// Append path components to the [`ModInfoBuilder`] instance. Paths need to be separated 258 + /// with [`ModInfoBuilder::new_entry`]. 259 + /// 260 + /// # Example 261 + /// 262 + /// ``` 263 + /// use kernel::firmware::ModInfoBuilder; 264 + /// 265 + /// # const DIR: &str = "vendor/chip/"; 266 + /// # const fn no_run<const N: usize>(builder: ModInfoBuilder<N>) { 267 + /// let builder = builder.new_entry() 268 + /// .push(DIR) 269 + /// .push("foo.bin") 270 + /// .new_entry() 271 + /// .push(DIR) 272 + /// .push("bar.bin"); 273 + /// # } 274 + /// ``` 275 + pub const fn push(self, s: &str) -> Self { 276 + // Check whether there has been an initial call to `next_entry()`. 277 + if N != 0 && self.n == 0 { 278 + crate::build_error!("Must call next_entry() before push()."); 279 + } 280 + 281 + self.push_internal(s.as_bytes()) 282 + } 283 + 284 + const fn push_module_name(self) -> Self { 285 + let mut this = self; 286 + let module_name = this.module_name; 287 + 288 + if !this.module_name.is_empty() { 289 + this = this.push_internal(module_name.as_bytes_with_nul()); 290 + 291 + if N != 0 { 292 + // Re-use the space taken by the NULL terminator and swap it with the '.' separator. 293 + this.buf[this.n - 1] = b'.'; 294 + } 295 + } 296 + 297 + this 298 + } 299 + 300 + /// Prepare the [`ModInfoBuilder`] for the next entry. 301 + /// 302 + /// This method acts as a separator between module firmware path entries. 303 + /// 304 + /// Must be called before constructing a new entry with subsequent calls to 305 + /// [`ModInfoBuilder::push`]. 306 + /// 307 + /// See [`ModInfoBuilder::push`] for an example. 308 + pub const fn new_entry(self) -> Self { 309 + self.push_internal(b"\0") 310 + .push_module_name() 311 + .push_internal(b"firmware=") 312 + } 313 + 314 + /// Build the byte array. 315 + pub const fn build(self) -> [u8; N] { 316 + // Add the final NULL terminator. 317 + let this = self.push_internal(b"\0"); 318 + 319 + if this.n == N { 320 + this.buf 321 + } else { 322 + crate::build_error!("Length mismatch."); 323 + } 324 + } 325 + } 326 + 327 + impl ModInfoBuilder<0> { 328 + /// Return the length of the byte array to build. 329 + pub const fn build_length(self) -> usize { 330 + // Compensate for the NULL terminator added by `build`. 331 + self.n + 1 332 + } 333 + }
+4
rust/macros/module.rs
··· 228 228 kernel::ThisModule::from_ptr(core::ptr::null_mut()) 229 229 }}; 230 230 231 + /// The `LocalModule` type is the type of the module created by `module!`, 232 + /// `module_pci_driver!`, `module_platform_driver!`, etc. 233 + type LocalModule = {type_}; 234 + 231 235 impl kernel::ModuleMetadata for {type_} {{ 232 236 const NAME: &'static kernel::str::CStr = kernel::c_str!(\"{name}\"); 233 237 }}