From 8592764933edc9c176e1904527e2190273b41bde Mon Sep 17 00:00:00 2001 From: Shankari Anand Date: Sun, 23 Nov 2025 14:54:30 +0530 Subject: drivers: gpu: Update ARef imports from sync::aref Update call sites to import `ARef` from `sync::aref` instead of `types`. This aligns with the ongoing effort to move `ARef` and `AlwaysRefCounted` to sync. Suggested-by: Benno Lossin Link: https://github.com/Rust-for-Linux/linux/issues/1173 Signed-off-by: Shankari Anand Reviewed-by: Joel Fernandes Acked-by: Alexandre Courbot Link: https://patch.msgid.link/20251123092438.182251-3-shankari.ak0208@gmail.com [aliceryhl: keep trailing // at last import] Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/driver.rs | 2 +- drivers/gpu/nova-core/gsp/sequencer.rs | 4 ++-- drivers/gpu/nova-core/vbios.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 0389c558c036..264c2362237a 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -16,10 +16,10 @@ use kernel::prelude::*; use kernel::regulator; use kernel::regulator::Regulator; use kernel::sizes::SZ_2M; +use kernel::sync::aref::ARef; use kernel::sync::Arc; use kernel::sync::Mutex; use kernel::time; -use kernel::types::ARef; use crate::file::File; use crate::gem::TyrObject; diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index 2d0369c49092..d78a30fbb70f 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -14,12 +14,12 @@ use kernel::{ device, io::poll::read_poll_timeout, prelude::*, + sync::aref::ARef, time::{ delay::fsleep, Delta, // }, - transmute::FromBytes, - types::ARef, // + transmute::FromBytes, // }; use crate::{ diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index abf423560ff4..7c26e4a2d61c 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -11,8 +11,8 @@ use kernel::{ Alignable, Alignment, // }, + sync::aref::ARef, transmute::FromBytes, - types::ARef, }; use crate::{ -- cgit v1.2.3 From ee47c0ab23a041e0a34848488dee1a23c0940f21 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:20:05 +0100 Subject: gpu: nova: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Reviewed-by: Alexandre Courbot Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-nova-v1-1-0e2353d5debe@gmail.com [ Use 'nova' commit subject prefix; use kernel vertical import style. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nova/driver.rs | 18 +++++++++++++----- drivers/gpu/nova-core/driver.rs | 5 ++--- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index 2246d8e104e0..b1af0a099551 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -1,7 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 use kernel::{ - auxiliary, c_str, device::Core, drm, drm::gem, drm::ioctl, prelude::*, sync::aref::ARef, + auxiliary, + device::Core, + drm::{ + self, + gem, + ioctl, // + }, + prelude::*, + sync::aref::ARef, // }; use crate::file::File; @@ -24,12 +32,12 @@ const INFO: drm::DriverInfo = drm::DriverInfo { major: 0, minor: 0, patchlevel: 0, - name: c_str!("nova"), - desc: c_str!("Nvidia Graphics"), + name: c"nova", + desc: c"Nvidia Graphics", }; -const NOVA_CORE_MODULE_NAME: &CStr = c_str!("NovaCore"); -const AUXILIARY_NAME: &CStr = c_str!("nova-drm"); +const NOVA_CORE_MODULE_NAME: &CStr = c"NovaCore"; +const AUXILIARY_NAME: &CStr = c"nova-drm"; kernel::auxiliary_device_table!( AUX_TABLE, diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index b8b0cc0f2d93..5a4cc047bcfc 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -2,7 +2,6 @@ use kernel::{ auxiliary, - c_str, device::Core, devres::Devres, dma::Device, @@ -82,7 +81,7 @@ impl pci::Driver for NovaCore { unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::())? }; let bar = Arc::pin_init( - pdev.iomap_region_sized::(0, c_str!("nova-core/bar0")), + pdev.iomap_region_sized::(0, c"nova-core/bar0"), GFP_KERNEL, )?; @@ -90,7 +89,7 @@ impl pci::Driver for NovaCore { gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), _reg <- auxiliary::Registration::new( pdev.as_ref(), - c_str!("nova-drm"), + c"nova-drm", 0, // TODO[XARR]: Once it lands, use XArray; for now we don't use the ID. crate::MODULE_NAME ), -- cgit v1.2.3 From 51293e589bafb4281a597986b3416471f67a79c2 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:27:24 +0100 Subject: drm: tyr: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-tyr-v1-1-d88ff1a54ae9@gmail.com [ Change commit subject prefix to 'drm: tyr:'. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/tyr/driver.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 264c2362237a..f0da58932702 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 or MIT -use kernel::c_str; use kernel::clk::Clk; use kernel::clk::OptionalClk; use kernel::device::Bound; @@ -91,8 +90,8 @@ kernel::of_device_table!( MODULE_OF_TABLE, ::IdInfo, [ - (of::DeviceId::new(c_str!("rockchip,rk3588-mali")), ()), - (of::DeviceId::new(c_str!("arm,mali-valhall-csf")), ()) + (of::DeviceId::new(c"rockchip,rk3588-mali"), ()), + (of::DeviceId::new(c"arm,mali-valhall-csf"), ()) ] ); @@ -104,16 +103,16 @@ impl platform::Driver for TyrDriver { pdev: &platform::Device, _info: Option<&Self::IdInfo>, ) -> impl PinInit { - let core_clk = Clk::get(pdev.as_ref(), Some(c_str!("core")))?; - let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("stacks")))?; - let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("coregroup")))?; + let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; + let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?; + let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?; core_clk.prepare_enable()?; stacks_clk.prepare_enable()?; coregroup_clk.prepare_enable()?; - let mali_regulator = Regulator::::get(pdev.as_ref(), c_str!("mali"))?; - let sram_regulator = Regulator::::get(pdev.as_ref(), c_str!("sram"))?; + let mali_regulator = Regulator::::get(pdev.as_ref(), c"mali")?; + let sram_regulator = Regulator::::get(pdev.as_ref(), c"sram")?; let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; let iomem = Arc::pin_init(request.iomap_sized::(), GFP_KERNEL)?; @@ -174,8 +173,8 @@ const INFO: drm::DriverInfo = drm::DriverInfo { major: 1, minor: 5, patchlevel: 0, - name: c_str!("panthor"), - desc: c_str!("ARM Mali Tyr DRM driver"), + name: c"panthor", + desc: c"ARM Mali Tyr DRM driver", }; #[vtable] -- cgit v1.2.3 From 255153afbcfdcf30d20048fb76a6d9418537b5d9 Mon Sep 17 00:00:00 2001 From: Brendan Shephard Date: Mon, 15 Dec 2025 18:34:16 +1000 Subject: drm/nova: Align GEM memory allocation to system page size Use page::page_align for GEM object memory allocation to ensure the allocation is page aligned. This is important on systems where the default page size is not 4k. Such as 16k or 64k aarch64 systems. This change uses the updated page_align() function which returns Option for overflow safety. (See "rust: Return Option from page_align and ensure no usize overflow"). Signed-off-by: Brendan Shephard Link: https://patch.msgid.link/20251215083416.266469-1-bshephar@bne-home.net [ Import page module only. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nova/gem.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs index 2760ba4f3450..6ccfa5da5761 100644 --- a/drivers/gpu/drm/nova/gem.rs +++ b/drivers/gpu/drm/nova/gem.rs @@ -3,6 +3,7 @@ use kernel::{ drm, drm::{gem, gem::BaseObject}, + page, prelude::*, sync::aref::ARef, }; @@ -27,11 +28,10 @@ impl gem::DriverObject for NovaObject { impl NovaObject { /// Create a new DRM GEM object. pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result>> { - let aligned_size = size.next_multiple_of(1 << 12); - - if size == 0 || size > aligned_size { + if size == 0 { return Err(EINVAL); } + let aligned_size = page::page_align(size).ok_or(EINVAL)?; gem::Object::new(dev, aligned_size) } -- cgit v1.2.3 From db22fbc15a9cea7e3f74a53d36c381503b6ca43e Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 18 Dec 2025 16:50:48 +0100 Subject: gpu: nova-core: fw: get rid of redundant Result in GspFirmware::new() In GspFirmware::new(), utilize pin_init_scope() to get rid of the Result in the returned Result> which is unnecessarily redundant. Reviewed-by: Joel Fernandes Link: https://patch.msgid.link/20251218155239.25243-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/firmware/gsp.rs | 132 +++++++++++++++++----------------- drivers/gpu/nova-core/gsp/boot.rs | 5 +- 2 files changed, 68 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 0549805282ab..e034268be3c5 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -153,82 +153,84 @@ pub(crate) struct GspFirmware { impl GspFirmware { /// Loads the GSP firmware binaries, map them into `dev`'s address-space, and creates the page /// tables expected by the GSP bootloader to load it. - pub(crate) fn new<'a, 'b>( + pub(crate) fn new<'a>( dev: &'a device::Device, chipset: Chipset, - ver: &'b str, - ) -> Result + 'a> { - let fw = super::request_firmware(dev, chipset, "gsp", ver)?; + ver: &'a str, + ) -> impl PinInit + 'a { + pin_init::pin_init_scope(move || { + let fw = super::request_firmware(dev, chipset, "gsp", ver)?; - let fw_section = elf::elf64_section(fw.data(), ".fwimage").ok_or(EINVAL)?; + let fw_section = elf::elf64_section(fw.data(), ".fwimage").ok_or(EINVAL)?; - let sigs_section = match chipset.arch() { - Architecture::Ampere => ".fwsignature_ga10x", - Architecture::Ada => ".fwsignature_ad10x", - _ => return Err(ENOTSUPP), - }; - let signatures = elf::elf64_section(fw.data(), sigs_section) - .ok_or(EINVAL) - .and_then(|data| DmaObject::from_data(dev, data))?; + let sigs_section = match chipset.arch() { + Architecture::Ampere => ".fwsignature_ga10x", + Architecture::Ada => ".fwsignature_ad10x", + _ => return Err(ENOTSUPP), + }; + let signatures = elf::elf64_section(fw.data(), sigs_section) + .ok_or(EINVAL) + .and_then(|data| DmaObject::from_data(dev, data))?; - let size = fw_section.len(); + let size = fw_section.len(); - // Move the firmware into a vmalloc'd vector and map it into the device address - // space. - let fw_vvec = VVec::with_capacity(fw_section.len(), GFP_KERNEL) - .and_then(|mut v| { - v.extend_from_slice(fw_section, GFP_KERNEL)?; - Ok(v) - }) - .map_err(|_| ENOMEM)?; + // Move the firmware into a vmalloc'd vector and map it into the device address + // space. + let fw_vvec = VVec::with_capacity(fw_section.len(), GFP_KERNEL) + .and_then(|mut v| { + v.extend_from_slice(fw_section, GFP_KERNEL)?; + Ok(v) + }) + .map_err(|_| ENOMEM)?; - let bl = super::request_firmware(dev, chipset, "bootloader", ver)?; - let bootloader = RiscvFirmware::new(dev, &bl)?; + let bl = super::request_firmware(dev, chipset, "bootloader", ver)?; + let bootloader = RiscvFirmware::new(dev, &bl)?; - Ok(try_pin_init!(Self { - fw <- SGTable::new(dev, fw_vvec, DataDirection::ToDevice, GFP_KERNEL), - level2 <- { - // Allocate the level 2 page table, map the firmware onto it, and map it into the - // device address space. - VVec::::with_capacity( - fw.iter().count() * core::mem::size_of::(), - GFP_KERNEL, - ) - .map_err(|_| ENOMEM) - .and_then(|level2| map_into_lvl(&fw, level2)) - .map(|level2| SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL))? - }, - level1 <- { - // Allocate the level 1 page table, map the level 2 page table onto it, and map it - // into the device address space. - VVec::::with_capacity( - level2.iter().count() * core::mem::size_of::(), - GFP_KERNEL, - ) - .map_err(|_| ENOMEM) - .and_then(|level1| map_into_lvl(&level2, level1)) - .map(|level1| SGTable::new(dev, level1, DataDirection::ToDevice, GFP_KERNEL))? - }, - level0: { - // Allocate the level 0 page table as a device-visible DMA object, and map the - // level 1 page table onto it. + Ok(try_pin_init!(Self { + fw <- SGTable::new(dev, fw_vvec, DataDirection::ToDevice, GFP_KERNEL), + level2 <- { + // Allocate the level 2 page table, map the firmware onto it, and map it into + // the device address space. + VVec::::with_capacity( + fw.iter().count() * core::mem::size_of::(), + GFP_KERNEL, + ) + .map_err(|_| ENOMEM) + .and_then(|level2| map_into_lvl(&fw, level2)) + .map(|level2| SGTable::new(dev, level2, DataDirection::ToDevice, GFP_KERNEL))? + }, + level1 <- { + // Allocate the level 1 page table, map the level 2 page table onto it, and map + // it into the device address space. + VVec::::with_capacity( + level2.iter().count() * core::mem::size_of::(), + GFP_KERNEL, + ) + .map_err(|_| ENOMEM) + .and_then(|level1| map_into_lvl(&level2, level1)) + .map(|level1| SGTable::new(dev, level1, DataDirection::ToDevice, GFP_KERNEL))? + }, + level0: { + // Allocate the level 0 page table as a device-visible DMA object, and map the + // level 1 page table onto it. - // Level 0 page table data. - let mut level0_data = kvec![0u8; GSP_PAGE_SIZE]?; + // Level 0 page table data. + let mut level0_data = kvec![0u8; GSP_PAGE_SIZE]?; - // Fill level 1 page entry. - let level1_entry = level1.iter().next().ok_or(EINVAL)?; - let level1_entry_addr = level1_entry.dma_address(); - let dst = &mut level0_data[..size_of_val(&level1_entry_addr)]; - dst.copy_from_slice(&level1_entry_addr.to_le_bytes()); + // Fill level 1 page entry. + let level1_entry = level1.iter().next().ok_or(EINVAL)?; + let level1_entry_addr = level1_entry.dma_address(); + let dst = &mut level0_data[..size_of_val(&level1_entry_addr)]; + dst.copy_from_slice(&level1_entry_addr.to_le_bytes()); - // Turn the level0 page table into a [`DmaObject`]. - DmaObject::from_data(dev, &level0_data)? - }, - size, - signatures, - bootloader, - })) + // Turn the level0 page table into a [`DmaObject`]. + DmaObject::from_data(dev, &level0_data)? + }, + size, + signatures, + bootloader, + })) + }) } /// Returns the DMA handle of the radix3 level 0 page table. diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 54937606b5b0..a53d80620468 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -139,10 +139,7 @@ impl super::Gsp { let bios = Vbios::new(dev, bar)?; - let gsp_fw = KBox::pin_init( - GspFirmware::new(dev, chipset, FIRMWARE_VERSION)?, - GFP_KERNEL, - )?; + let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?; let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?; dev_dbg!(dev, "{:#x?}\n", fb_layout); -- cgit v1.2.3 From 423706aa1c469bfcc3c27a9e8c464f6b88921db7 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 18 Dec 2025 16:50:49 +0100 Subject: gpu: nova-core: fw: move appropriate code into pin initializer Relocate the code that technically fits in the pin initializer into the initializer itself. While, thanks to pin_init_scope(), it is also possible to keep it as is, moving appropriate code into the initializer has the advantage that it structures the dependencies of fields naturally. For instance, intermediate data that is only needed for a single field goes into the initializer block of this field, making it obvious that it is not needed by anything else. On the other hand, intermediate data that is needed for multiple fields to initialize remains above the initializer, naturally indicating that it is needed my multiple fields. Reviewed-by: Joel Fernandes Link: https://patch.msgid.link/20251218155239.25243-3-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/firmware/gsp.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index e034268be3c5..da97814cf859 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -159,18 +159,9 @@ impl GspFirmware { ver: &'a str, ) -> impl PinInit + 'a { pin_init::pin_init_scope(move || { - let fw = super::request_firmware(dev, chipset, "gsp", ver)?; + let firmware = super::request_firmware(dev, chipset, "gsp", ver)?; - let fw_section = elf::elf64_section(fw.data(), ".fwimage").ok_or(EINVAL)?; - - let sigs_section = match chipset.arch() { - Architecture::Ampere => ".fwsignature_ga10x", - Architecture::Ada => ".fwsignature_ad10x", - _ => return Err(ENOTSUPP), - }; - let signatures = elf::elf64_section(fw.data(), sigs_section) - .ok_or(EINVAL) - .and_then(|data| DmaObject::from_data(dev, data))?; + let fw_section = elf::elf64_section(firmware.data(), ".fwimage").ok_or(EINVAL)?; let size = fw_section.len(); @@ -183,9 +174,6 @@ impl GspFirmware { }) .map_err(|_| ENOMEM)?; - let bl = super::request_firmware(dev, chipset, "bootloader", ver)?; - let bootloader = RiscvFirmware::new(dev, &bl)?; - Ok(try_pin_init!(Self { fw <- SGTable::new(dev, fw_vvec, DataDirection::ToDevice, GFP_KERNEL), level2 <- { @@ -227,8 +215,22 @@ impl GspFirmware { DmaObject::from_data(dev, &level0_data)? }, size, - signatures, - bootloader, + signatures: { + let sigs_section = match chipset.arch() { + Architecture::Ampere => ".fwsignature_ga10x", + Architecture::Ada => ".fwsignature_ad10x", + _ => return Err(ENOTSUPP), + }; + + elf::elf64_section(firmware.data(), sigs_section) + .ok_or(EINVAL) + .and_then(|data| DmaObject::from_data(dev, data))? + }, + bootloader: { + let bl = super::request_firmware(dev, chipset, "bootloader", ver)?; + + RiscvFirmware::new(dev, &bl)? + }, })) }) } -- cgit v1.2.3 From 032a6772d663a26005f4c17be992a716457f095b Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 18 Dec 2025 16:50:50 +0100 Subject: gpu: nova-core: gsp: get rid of redundant Result in Gsp::new() In Gsp::new(), utilize pin_init_scope() to get rid of the Result in the returned Result> which is unnecessarily redundant. Reviewed-by: Joel Fernandes Link: https://patch.msgid.link/20251218155239.25243-4-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gpu.rs | 2 +- drivers/gpu/nova-core/gsp.rs | 78 +++++++++++++++++++++++--------------------- 2 files changed, 41 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 629c9d2dc994..50d76092fbdd 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -281,7 +281,7 @@ impl Gpu { sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?, - gsp <- Gsp::new(pdev)?, + gsp <- Gsp::new(pdev), _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? }, diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index fb6f74797178..8bc86e1bcac5 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -119,43 +119,45 @@ pub(crate) struct Gsp { impl Gsp { // Creates an in-place initializer for a `Gsp` manager for `pdev`. - pub(crate) fn new(pdev: &pci::Device) -> Result> { - let dev = pdev.as_ref(); - let libos = CoherentAllocation::::alloc_coherent( - dev, - GSP_PAGE_SIZE / size_of::(), - GFP_KERNEL | __GFP_ZERO, - )?; - - // Initialise the logging structures. The OpenRM equivalents are in: - // _kgspInitLibosLoggingStructures (allocates memory for buffers) - // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) - let loginit = LogBuffer::new(dev)?; - dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; - - let logintr = LogBuffer::new(dev)?; - dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; - - let logrm = LogBuffer::new(dev)?; - dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; - - let cmdq = Cmdq::new(dev)?; - - let rmargs = CoherentAllocation::::alloc_coherent( - dev, - 1, - GFP_KERNEL | __GFP_ZERO, - )?; - dma_write!(rmargs[0] = fw::GspArgumentsCached::new(&cmdq))?; - dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", &rmargs))?; - - Ok(try_pin_init!(Self { - libos, - loginit, - logintr, - logrm, - rmargs, - cmdq, - })) + pub(crate) fn new(pdev: &pci::Device) -> impl PinInit + '_ { + pin_init::pin_init_scope(move || { + let dev = pdev.as_ref(); + let libos = CoherentAllocation::::alloc_coherent( + dev, + GSP_PAGE_SIZE / size_of::(), + GFP_KERNEL | __GFP_ZERO, + )?; + + // Initialise the logging structures. The OpenRM equivalents are in: + // _kgspInitLibosLoggingStructures (allocates memory for buffers) + // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) + let loginit = LogBuffer::new(dev)?; + dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; + + let logintr = LogBuffer::new(dev)?; + dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; + + let logrm = LogBuffer::new(dev)?; + dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; + + let cmdq = Cmdq::new(dev)?; + + let rmargs = CoherentAllocation::::alloc_coherent( + dev, + 1, + GFP_KERNEL | __GFP_ZERO, + )?; + dma_write!(rmargs[0] = fw::GspArgumentsCached::new(&cmdq))?; + dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", &rmargs))?; + + Ok(try_pin_init!(Self { + libos, + loginit, + logintr, + logrm, + rmargs, + cmdq, + })) + }) } } -- cgit v1.2.3 From 7acc70476f14661149774ab88d3fe23d83ba4249 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 18 Dec 2025 16:50:51 +0100 Subject: gpu: nova-core: gsp: move appropriate code into pin initializer Relocate the code that technically fits in the pin initializer into the initializer itself. While, thanks to pin_init_scope(), it is also possible to keep it as is, moving appropriate code into the initializer has the advantage that it structures the dependencies of fields naturally. For instance, intermediate data that is only needed for a single field goes into the initializer block of this field, making it obvious that it is not needed by anything else. On the other hand, intermediate data that is needed for multiple fields to initialize remains above the initializer, naturally indicating that it is needed my multiple fields. Reviewed-by: Joel Fernandes Link: https://patch.msgid.link/20251218155239.25243-5-dakr@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gsp.rs | 61 ++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 8bc86e1bcac5..766fd9905358 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -122,41 +122,36 @@ impl Gsp { pub(crate) fn new(pdev: &pci::Device) -> impl PinInit + '_ { pin_init::pin_init_scope(move || { let dev = pdev.as_ref(); - let libos = CoherentAllocation::::alloc_coherent( - dev, - GSP_PAGE_SIZE / size_of::(), - GFP_KERNEL | __GFP_ZERO, - )?; - - // Initialise the logging structures. The OpenRM equivalents are in: - // _kgspInitLibosLoggingStructures (allocates memory for buffers) - // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) - let loginit = LogBuffer::new(dev)?; - dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; - - let logintr = LogBuffer::new(dev)?; - dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; - - let logrm = LogBuffer::new(dev)?; - dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; - - let cmdq = Cmdq::new(dev)?; - - let rmargs = CoherentAllocation::::alloc_coherent( - dev, - 1, - GFP_KERNEL | __GFP_ZERO, - )?; - dma_write!(rmargs[0] = fw::GspArgumentsCached::new(&cmdq))?; - dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", &rmargs))?; Ok(try_pin_init!(Self { - libos, - loginit, - logintr, - logrm, - rmargs, - cmdq, + libos: CoherentAllocation::::alloc_coherent( + dev, + GSP_PAGE_SIZE / size_of::(), + GFP_KERNEL | __GFP_ZERO, + )?, + loginit: LogBuffer::new(dev)?, + logintr: LogBuffer::new(dev)?, + logrm: LogBuffer::new(dev)?, + cmdq: Cmdq::new(dev)?, + rmargs: CoherentAllocation::::alloc_coherent( + dev, + 1, + GFP_KERNEL | __GFP_ZERO, + )?, + _: { + // Initialise the logging structures. The OpenRM equivalents are in: + // _kgspInitLibosLoggingStructures (allocates memory for buffers) + // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) + dma_write!( + libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0) + )?; + dma_write!( + libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0) + )?; + dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; + dma_write!(rmargs[0] = fw::GspArgumentsCached::new(cmdq))?; + dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; + }, })) }) } -- cgit v1.2.3 From da8264ce6c5c02b78f95d31021e942ab38d8dd39 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Mon, 5 Jan 2026 19:52:25 -0800 Subject: gpu: nova-core: use CStr::from_bytes_until_nul() and remove util.rs The util.rs module contained a single helper function, str_from_null_terminated(), which duplicated functionality that is now available in core::ffi::CStr. Specifically, CStr::from_bytes_until_nul() is available in the kernel's minimum supported Rust version (1.78.0), so it time to stop using this custom workaround. Reviewed-by: Joel Fernandes Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260106035226.48853-2-jhubbard@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gsp/commands.rs | 5 +++-- drivers/gpu/nova-core/nova_core.rs | 1 - drivers/gpu/nova-core/util.rs | 16 ---------------- 3 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 drivers/gpu/nova-core/util.rs (limited to 'drivers') diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 0425c65b5d6f..a11fe6018091 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -30,7 +30,6 @@ use crate::{ }, }, sbuffer::SBufferIter, - util, }; /// The `GspSetSystemInfo` command. @@ -209,7 +208,9 @@ impl GetGspStaticInfoReply { /// Returns the name of the GPU as a string, or `None` if the string given by the GSP was /// invalid. pub(crate) fn gpu_name(&self) -> Option<&str> { - util::str_from_null_terminated(&self.gpu_name) + CStr::from_bytes_until_nul(&self.gpu_name) + .ok() + .and_then(|cstr| cstr.to_str().ok()) } } diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index b98a1c03f13d..c1121e7c64c5 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -16,7 +16,6 @@ mod gsp; mod num; mod regs; mod sbuffer; -mod util; mod vbios; pub(crate) const MODULE_NAME: &kernel::str::CStr = ::NAME; diff --git a/drivers/gpu/nova-core/util.rs b/drivers/gpu/nova-core/util.rs deleted file mode 100644 index 4b503249a3ef..000000000000 --- a/drivers/gpu/nova-core/util.rs +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/// Converts a null-terminated byte slice to a string, or `None` if the array does not -/// contains any null byte or contains invalid characters. -/// -/// Contrary to [`kernel::str::CStr::from_bytes_with_nul`], the null byte can be anywhere in the -/// slice, and not only in the last position. -pub(crate) fn str_from_null_terminated(bytes: &[u8]) -> Option<&str> { - use kernel::str::CStr; - - bytes - .iter() - .position(|&b| b == 0) - .and_then(|null_pos| CStr::from_bytes_with_nul(&bytes[..=null_pos]).ok()) - .and_then(|cstr| cstr.to_str().ok()) -} -- cgit v1.2.3 From 2d7b4a44fb768e1887e7e4cdd8b86817ccd9c3bf Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Mon, 5 Jan 2026 19:52:26 -0800 Subject: gpu: nova-core: use CStr::from_bytes_until_nul() in elf64_section() Instead of open-coding the steps for extracting a null-terminated string, use the newly available CStr::from_bytes_until_nul(). Suggested-by: Joel Fernandes Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260106035226.48853-3-jhubbard@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/firmware/gsp.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index da97814cf859..1025b7f746eb 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -93,10 +93,7 @@ mod elf { // Get the start of the name. elf.get(name_idx..) - // Stop at the first `0`. - .and_then(|nstr| nstr.get(0..=nstr.iter().position(|b| *b == 0)?)) - // Convert into CStr. This should never fail because of the line above. - .and_then(|nstr| CStr::from_bytes_with_nul(nstr).ok()) + .and_then(|nstr| CStr::from_bytes_until_nul(nstr).ok()) // Convert into str. .and_then(|c_str| c_str.to_str().ok()) // Check that the name matches. -- cgit v1.2.3 From 8d6a8e79226d5e0907c7554235113afa06479029 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Wed, 7 Jan 2026 16:58:10 -0800 Subject: gpu: nova-core: preserve error information in gpu_name() Change gpu_name() to return a Result instead of an Option. This avoids silently discarding error information when parsing the GPU name string from the GSP. Update the callsite to log a warning with the error details on failure, rather than just displaying "invalid GPU name". Suggested-by: Danilo Krummrich Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260108005811.86014-2-jhubbard@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/gsp/boot.rs | 9 ++++----- drivers/gpu/nova-core/gsp/commands.rs | 28 ++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index a53d80620468..4a5c49a502f7 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -238,11 +238,10 @@ impl super::Gsp { // Obtain and display basic GPU information. let info = commands::get_gsp_info(&mut self.cmdq, bar)?; - dev_info!( - pdev.as_ref(), - "GPU name: {}\n", - info.gpu_name().unwrap_or("invalid GPU name") - ); + match info.gpu_name() { + Ok(name) => dev_info!(pdev.as_ref(), "GPU name: {}\n", name), + Err(e) => dev_warn!(pdev.as_ref(), "GPU name unavailable: {:?}\n", e), + } Ok(()) } diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index a11fe6018091..c8430a076269 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -2,7 +2,9 @@ use core::{ array, - convert::Infallible, // + convert::Infallible, + ffi::FromBytesUntilNulError, + str::Utf8Error, // }; use kernel::{ @@ -204,13 +206,27 @@ impl MessageFromGsp for GetGspStaticInfoReply { } } +/// Error type for [`GetGspStaticInfoReply::gpu_name`]. +#[derive(Debug)] +pub(crate) enum GpuNameError { + /// The GPU name string does not contain a null terminator. + NoNullTerminator(FromBytesUntilNulError), + + /// The GPU name string contains invalid UTF-8. + #[expect(dead_code)] + InvalidUtf8(Utf8Error), +} + impl GetGspStaticInfoReply { - /// Returns the name of the GPU as a string, or `None` if the string given by the GSP was - /// invalid. - pub(crate) fn gpu_name(&self) -> Option<&str> { + /// Returns the name of the GPU as a string. + /// + /// Returns an error if the string given by the GSP does not contain a null terminator or + /// contains invalid UTF-8. + pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> { CStr::from_bytes_until_nul(&self.gpu_name) - .ok() - .and_then(|cstr| cstr.to_str().ok()) + .map_err(GpuNameError::NoNullTerminator)? + .to_str() + .map_err(GpuNameError::InvalidUtf8) } } -- cgit v1.2.3 From 0cc83fc23debf3e2df19c4510a77fe2d60ab2693 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Wed, 7 Jan 2026 16:58:11 -0800 Subject: gpu: nova-core: don't print raw PMU table entries Remove the (large) raw form of the PMU table entries. The resulting PMULookupTable is still getting printed (in more useful form) later, anyway, so this was redundant, even for debugging. This output (the example is from an Ampere GPU) is what is being removed: NovaCore 0000:e1:00.0: PMU entry: [01, 01, 54, 54, 01, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [07, 06, e0, b7, 03, 00] NovaCore 0000:e1:00.0: PMU entry: [08, 01, bc, 56, 05, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [45, 07, 88, da, 01, 00] NovaCore 0000:e1:00.0: PMU entry: [85, 07, 34, c9, 02, 00] NovaCore 0000:e1:00.0: PMU entry: [49, 05, 7c, b3, 04, 00] NovaCore 0000:e1:00.0: PMU entry: [89, 05, 1c, 05, 05, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] NovaCore 0000:e1:00.0: PMU entry: [00, 00, 00, 00, 00, 00] And it is immediately followed by a more useful, interpreted list of selected PMU table data, which is *not* being removed as part of this commit. That looks like this: NovaCore 0000:e1:00.0: PmuLookupTableEntry desc: FalconUCodeDescV3 { hdr: 78381825, stored_size: 59904, pkc_data_offset: 1444, interface_offset: 28, imem_phys_base: 0, imem_load_size: 57856, imem_virt_base: 0, dmem_phys_base: 0, dmem_load_size: 2048, engine_id_mask: 1024, ucode_id: 9, signature_count: 3, signature_versions: 7, _reserved: 37449, } Signed-off-by: John Hubbard Acked-by: Joel Fernandes Link: https://patch.msgid.link/20260108005811.86014-3-jhubbard@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/vbios.rs | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index 7c26e4a2d61c..ac01eb195fb2 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -887,11 +887,6 @@ impl PmuLookupTable { ret }; - // Debug logging of entries (dumps the table data to dmesg) - for i in (header_len..required_bytes).step_by(entry_len) { - dev_dbg!(dev, "PMU entry: {:02x?}\n", &data[i..][..entry_len]); - } - Ok(PmuLookupTable { header, table_data }) } -- cgit v1.2.3 From 5cf76277cdec872aef9ff2e9008ae129bb303787 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 7 Jan 2026 14:16:46 -0600 Subject: gpu: nova-core: check for overflow to DMATRFBASE1 The NV_PFALCON_FALCON_DMATRFBASE/1 register pair supports DMA addresses up to 49 bits only, but the write to DMATRFBASE1 could exceed that. To mitigate, check first that the DMA address will fit. Reviewed-by: John Hubbard Reviewed-by: Joel Fernandes Fixes: 69f5cd67ce41 ("gpu: nova-core: add falcon register definitions and base code") Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260107201647.2490140-1-ttabi@nvidia.com [ Import ::kernel::dma::DmaMask. - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/falcon.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 82c661aef594..3ab33ea36d9c 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -8,7 +8,10 @@ use hal::FalconHal; use kernel::{ device, - dma::DmaAddress, + dma::{ + DmaAddress, + DmaMask, // + }, io::poll::read_poll_timeout, prelude::*, sync::aref::ARef, @@ -472,6 +475,12 @@ impl Falcon { return Err(EINVAL); } + // The DMATRFBASE/1 register pair only supports a 49-bit address. + if dma_start > DmaMask::new::<49>().value() { + dev_err!(self.dev, "DMA address {:#x} exceeds 49 bits\n", dma_start); + return Err(ERANGE); + } + // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we // need to perform. let num_transfers = load_offsets.len.div_ceil(DMA_LEN); -- cgit v1.2.3 From 654826aa4a8f25cf825ad9254f37e6cb5092098f Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 7 Jan 2026 14:16:47 -0600 Subject: gpu: nova-core: add missing newlines to several print strings Although the dev_xx!() macro calls do not technically require terminating newlines for the format strings, they should be added anyway to maintain consistency, both within Rust code and with the C versions. Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Link: https://patch.msgid.link/20260107201647.2490140-2-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/nova-core/falcon.rs | 6 +++--- drivers/gpu/nova-core/falcon/hal/ga102.rs | 4 ++-- drivers/gpu/nova-core/fb.rs | 2 +- drivers/gpu/nova-core/gpu.rs | 2 +- drivers/gpu/nova-core/gsp/boot.rs | 2 +- drivers/gpu/nova-core/gsp/cmdq.rs | 2 +- drivers/gpu/nova-core/gsp/sequencer.rs | 10 +++++----- drivers/gpu/nova-core/vbios.rs | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 3ab33ea36d9c..46b02c8a591e 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -469,7 +469,7 @@ impl Falcon { if dma_start % DmaAddress::from(DMA_LEN) > 0 { dev_err!( self.dev, - "DMA transfer start addresses must be a multiple of {}", + "DMA transfer start addresses must be a multiple of {}\n", DMA_LEN ); return Err(EINVAL); @@ -492,11 +492,11 @@ impl Falcon { .and_then(|size| size.checked_add(load_offsets.src_start)) { None => { - dev_err!(self.dev, "DMA transfer length overflow"); + dev_err!(self.dev, "DMA transfer length overflow\n"); return Err(EOVERFLOW); } Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => { - dev_err!(self.dev, "DMA transfer goes beyond range of DMA object"); + dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n"); return Err(EINVAL); } Some(_) => (), diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index 69a7a95cac16..0bdfe45a2d03 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -52,7 +52,7 @@ fn signature_reg_fuse_version_ga102( let ucode_idx = match usize::from(ucode_id) { ucode_id @ 1..=regs::NV_FUSE_OPT_FPF_SIZE => ucode_id - 1, _ => { - dev_err!(dev, "invalid ucode id {:#x}", ucode_id); + dev_err!(dev, "invalid ucode id {:#x}\n", ucode_id); return Err(EINVAL); } }; @@ -66,7 +66,7 @@ fn signature_reg_fuse_version_ga102( } else if engine_id_mask & 0x0400 != 0 { regs::NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION::read(bar, ucode_idx).data() } else { - dev_err!(dev, "unexpected engine_id_mask {:#x}", engine_id_mask); + dev_err!(dev, "unexpected engine_id_mask {:#x}\n", engine_id_mask); return Err(EINVAL); }; diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs index 3c9cf151786c..c62abcaed547 100644 --- a/drivers/gpu/nova-core/fb.rs +++ b/drivers/gpu/nova-core/fb.rs @@ -80,7 +80,7 @@ impl SysmemFlush { let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| { dev_warn!( &self.device, - "failed to unregister sysmem flush page: {:?}", + "failed to unregister sysmem flush page: {:?}\n", e ) }); diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 50d76092fbdd..9b042ef1a308 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -268,7 +268,7 @@ impl Gpu { // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. _: { gfw::wait_gfw_boot_completion(bar) - .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?; + .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete\n"))?; }, sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 4a5c49a502f7..581b412554dc 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -82,7 +82,7 @@ impl super::Gsp { if frts_status != 0 { dev_err!( dev, - "FWSEC-FRTS returned with error code {:#x}", + "FWSEC-FRTS returned with error code {:#x}\n", frts_status ); diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 6f946d14868a..3c01fc6e6b6a 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -615,7 +615,7 @@ impl Cmdq { { dev_err!( self.dev, - "GSP RPC: receive: Call {} - bad checksum", + "GSP RPC: receive: Call {} - bad checksum\n", header.sequence() ); return Err(EIO); diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index d78a30fbb70f..d6c489c39092 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -121,7 +121,7 @@ impl GspSeqCmd { }; if data.len() < size { - dev_err!(dev, "Data is not enough for command"); + dev_err!(dev, "Data is not enough for command\n"); return Err(EINVAL); } @@ -320,7 +320,7 @@ impl<'a> Iterator for GspSeqIter<'a> { cmd_result.map_or_else( |_err| { - dev_err!(self.dev, "Error parsing command at offset {}", offset); + dev_err!(self.dev, "Error parsing command at offset {}\n", offset); None }, |(cmd, size)| { @@ -382,7 +382,7 @@ impl<'a> GspSequencer<'a> { dev: params.dev, }; - dev_dbg!(sequencer.dev, "Running CPU Sequencer commands"); + dev_dbg!(sequencer.dev, "Running CPU Sequencer commands\n"); for cmd_result in sequencer.iter() { match cmd_result { @@ -390,7 +390,7 @@ impl<'a> GspSequencer<'a> { Err(e) => { dev_err!( sequencer.dev, - "Error running command at index {}", + "Error running command at index {}\n", sequencer.seq_info.cmd_index ); return Err(e); @@ -400,7 +400,7 @@ impl<'a> GspSequencer<'a> { dev_dbg!( sequencer.dev, - "CPU Sequencer commands completed successfully" + "CPU Sequencer commands completed successfully\n" ); Ok(()) } diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index ac01eb195fb2..e59eee2050a8 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -790,7 +790,7 @@ impl PciAtBiosImage { // read the 4 bytes at the offset specified in the token let offset = usize::from(token.data_offset); let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| { - dev_err!(self.base.dev, "Failed to convert data slice to array"); + dev_err!(self.base.dev, "Failed to convert data slice to array\n"); EINVAL })?; -- cgit v1.2.3 From 8304c44631c370646de35d343ddba078738d1719 Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Thu, 15 Jan 2026 11:38:43 -0800 Subject: drm/tyr: use generated bindings for GpuInfo Currently Tyr's struct GpuInfo is manually copied and updated from include/uapi/drm/panthor_drm.h. But an auto generated struct is available, so use that instead to avoid copy/paste errors and to stay up-to-date with the panthor uapi. Reviewed-by: Alice Ryhl Reviewed-by: Daniel Almeida Signed-off-by: Deborah Brouwer Link: https://patch.msgid.link/20260115193843.34878-1-deborah.brouwer@collabora.com Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/gpu.rs | 48 ++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/tyr/gpu.rs b/drivers/gpu/drm/tyr/gpu.rs index 6c582910dd5d..3072562e36e5 100644 --- a/drivers/gpu/drm/tyr/gpu.rs +++ b/drivers/gpu/drm/tyr/gpu.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 or MIT +use core::ops::Deref; +use core::ops::DerefMut; use kernel::bits::genmask_u32; use kernel::device::Bound; use kernel::device::Device; @@ -8,6 +10,7 @@ use kernel::platform; use kernel::prelude::*; use kernel::time; use kernel::transmute::AsBytes; +use kernel::uapi; use crate::driver::IoMem; use crate::regs; @@ -18,29 +21,9 @@ use crate::regs; /// # Invariants /// /// - The layout of this struct identical to the C `struct drm_panthor_gpu_info`. -#[repr(C)] -pub(crate) struct GpuInfo { - pub(crate) gpu_id: u32, - pub(crate) gpu_rev: u32, - pub(crate) csf_id: u32, - pub(crate) l2_features: u32, - pub(crate) tiler_features: u32, - pub(crate) mem_features: u32, - pub(crate) mmu_features: u32, - pub(crate) thread_features: u32, - pub(crate) max_threads: u32, - pub(crate) thread_max_workgroup_size: u32, - pub(crate) thread_max_barrier_size: u32, - pub(crate) coherency_features: u32, - pub(crate) texture_features: [u32; 4], - pub(crate) as_present: u32, - pub(crate) pad0: u32, - pub(crate) shader_present: u64, - pub(crate) l2_present: u64, - pub(crate) tiler_present: u64, - pub(crate) core_features: u32, - pub(crate) pad: u32, -} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub(crate) struct GpuInfo(pub(crate) uapi::drm_panthor_gpu_info); impl GpuInfo { pub(crate) fn new(dev: &Device, iomem: &Devres) -> Result { @@ -73,7 +56,7 @@ impl GpuInfo { let l2_present = u64::from(regs::GPU_L2_PRESENT_LO.read(dev, iomem)?); let l2_present = l2_present | u64::from(regs::GPU_L2_PRESENT_HI.read(dev, iomem)?) << 32; - Ok(Self { + Ok(Self(uapi::drm_panthor_gpu_info { gpu_id, gpu_rev, csf_id, @@ -95,7 +78,8 @@ impl GpuInfo { tiler_present, core_features, pad: 0, - }) + gpu_features: 0, + })) } pub(crate) fn log(&self, pdev: &platform::Device) { @@ -154,6 +138,20 @@ impl GpuInfo { } } +impl Deref for GpuInfo { + type Target = uapi::drm_panthor_gpu_info; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for GpuInfo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + // SAFETY: `GpuInfo`'s invariant guarantees that it is the same type that is // already exposed to userspace by the C driver. This implies that it fulfills // the requirements for `AsBytes`. -- cgit v1.2.3 From 793e8f7d52814e096f63373eca643d2672366a5a Mon Sep 17 00:00:00 2001 From: Dirk Behme Date: Mon, 19 Jan 2026 08:08:38 +0100 Subject: drm/tyr: fix register name in error print The `..IRQ..` register is printed here. Not the `..INT..` one. Correct this. Cc: stable@vger.kernel.org Fixes: cf4fd52e3236 ("rust: drm: Introduce the Tyr driver for Arm Mali GPUs") Link: https://lore.kernel.org/rust-for-linux/A04F0357-896E-4ACC-BC0E-DEE8608CE518@collabora.com/ Signed-off-by: Dirk Behme Link: https://patch.msgid.link/20260119070838.3219739-1-dirk.behme@de.bosch.com [aliceryhl: update commit message prefix] [aliceryhl: add cc stable as per Miguel's suggestion] Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index f0da58932702..434b80bb994b 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -75,7 +75,7 @@ fn issue_soft_reset(dev: &Device, iomem: &Devres) -> Result { dev_err!(dev, "GPU reset failed with errno\n"); dev_err!( dev, - "GPU_INT_RAWSTAT is {}\n", + "GPU_IRQ_RAWSTAT is {}\n", regs::GPU_IRQ_RAWSTAT.read(dev, iomem)? ); -- cgit v1.2.3 From 6ea52b6d8f33ae627f4dcf43b12b6e713a8b9331 Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Mon, 19 Jan 2026 12:26:45 -0800 Subject: drm/tyr: use read_poll_timeout The L2 power-on sequence and soft reset in Tyr previously relied on fixed sleeps followed by a single register check, since polling helpers were not available in Rust at the time. Now that read_poll_timeout() is available, poll the relevant registers until the hardware reports readiness or a timeout is reached. This avoids unnecessary delays on start-up. Signed-off-by: Deborah Brouwer Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260119202645.362457-1-deborah.brouwer@collabora.com Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/driver.rs | 22 ++++++++-------------- drivers/gpu/drm/tyr/gpu.rs | 18 +++++++++--------- 2 files changed, 17 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 434b80bb994b..2a45d0288825 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -8,6 +8,7 @@ use kernel::device::Device; use kernel::devres::Devres; use kernel::drm; use kernel::drm::ioctl; +use kernel::io::poll; use kernel::new_mutex; use kernel::of; use kernel::platform; @@ -67,20 +68,13 @@ unsafe impl Sync for TyrData {} fn issue_soft_reset(dev: &Device, iomem: &Devres) -> Result { regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?; - // TODO: We cannot poll, as there is no support in Rust currently, so we - // sleep. Change this when read_poll_timeout() is implemented in Rust. - kernel::time::delay::fsleep(time::Delta::from_millis(100)); - - if regs::GPU_IRQ_RAWSTAT.read(dev, iomem)? & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED == 0 { - dev_err!(dev, "GPU reset failed with errno\n"); - dev_err!( - dev, - "GPU_IRQ_RAWSTAT is {}\n", - regs::GPU_IRQ_RAWSTAT.read(dev, iomem)? - ); - - return Err(EIO); - } + poll::read_poll_timeout( + || regs::GPU_IRQ_RAWSTAT.read(dev, iomem), + |status| *status & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED != 0, + time::Delta::from_millis(1), + time::Delta::from_millis(100), + ) + .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?; Ok(()) } diff --git a/drivers/gpu/drm/tyr/gpu.rs b/drivers/gpu/drm/tyr/gpu.rs index 3072562e36e5..bfbf2a1d80e6 100644 --- a/drivers/gpu/drm/tyr/gpu.rs +++ b/drivers/gpu/drm/tyr/gpu.rs @@ -6,9 +6,10 @@ use kernel::bits::genmask_u32; use kernel::device::Bound; use kernel::device::Device; use kernel::devres::Devres; +use kernel::io::poll; use kernel::platform; use kernel::prelude::*; -use kernel::time; +use kernel::time::Delta; use kernel::transmute::AsBytes; use kernel::uapi; @@ -204,14 +205,13 @@ impl From for GpuId { pub(crate) fn l2_power_on(dev: &Device, iomem: &Devres) -> Result { regs::L2_PWRON_LO.write(dev, iomem, 1)?; - // TODO: We cannot poll, as there is no support in Rust currently, so we - // sleep. Change this when read_poll_timeout() is implemented in Rust. - kernel::time::delay::fsleep(time::Delta::from_millis(100)); - - if regs::L2_READY_LO.read(dev, iomem)? != 1 { - dev_err!(dev, "Failed to power on the GPU\n"); - return Err(EIO); - } + poll::read_poll_timeout( + || regs::L2_READY_LO.read(dev, iomem), + |status| *status == 1, + Delta::from_millis(1), + Delta::from_millis(100), + ) + .inspect_err(|_| dev_err!(dev, "Failed to power on the GPU."))?; Ok(()) } -- cgit v1.2.3 From 5ec66bbc74883b73d169ceb25dcb7a5cb22e275b Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Fri, 23 Jan 2026 09:52:35 -0800 Subject: drm/tyr: suppress unread field warnings Currently the rust compiler warns that certain fields in the TyrDriver are 'never read'. The fields are needed, but they are not read directly, they are only written into an 'impl PinInit' that is returned by probe. When warnings are compiled as errors, these warnings prevent Tyr from building. Suppress the warnings by adding underscores to the problematic variables. This allows Tyr to build again. Signed-off-by: Deborah Brouwer Link: https://patch.msgid.link/20260123175235.209092-1-deborah.brouwer@collabora.com Signed-off-by: Alice Ryhl --- drivers/gpu/drm/tyr/driver.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 2a45d0288825..568cb89aaed8 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -34,7 +34,7 @@ pub(crate) type TyrDevice = drm::Device; #[pin_data(PinnedDrop)] pub(crate) struct TyrDriver { - device: ARef, + _device: ARef, } #[pin_data(PinnedDrop)] @@ -127,8 +127,8 @@ impl platform::Driver for TyrDriver { coregroup: coregroup_clk, }), regulators <- new_mutex!(Regulators { - mali: mali_regulator, - sram: sram_regulator, + _mali: mali_regulator, + _sram: sram_regulator, }), gpu_info, }); @@ -136,7 +136,7 @@ impl platform::Driver for TyrDriver { let tdev: ARef = drm::Device::new(pdev.as_ref(), data)?; drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?; - let driver = TyrDriver { device: tdev }; + let driver = TyrDriver { _device: tdev }; // We need this to be dev_info!() because dev_dbg!() does not work at // all in Rust for now, and we need to see whether probe succeeded. @@ -193,6 +193,6 @@ struct Clocks { #[pin_data] struct Regulators { - mali: Regulator, - sram: Regulator, + _mali: Regulator, + _sram: Regulator, } -- cgit v1.2.3 From 0975002be52bd21196b868707ed1415cf1c45b98 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:36 -0600 Subject: gpu: nova-core: rename Imem to ImemSecure Rename FalconMem::Imem to ImemSecure to indicate that it references Secure Instruction Memory. This change has no functional impact. On Falcon cores, pages in instruction memory can be tagged as Secure or Non-Secure. For GA102 and later, only Secure is used, which is why FalconMem::Imem seems appropriate. However, Turing firmware images can also contain non-secure sections, and so FalconMem needs to support that. By renaming Imem to ImemSec now, future patches for Turing support will be simpler. Nouveau uses the term "IMEM" to refer both to the Instruction Memory block on Falcon cores as well as to the images of secure firmware uploaded to part of IMEM. OpenRM uses the terms "ImemSec" and "ImemNs" instead, and uses "IMEM" just to refer to the physical memory device. Renaming these terms allows us to align with OpenRM, avoid confusion between IMEM and ImemSec, and makes future patches simpler. Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-2-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 20 +++++++++++++------- drivers/gpu/nova-core/firmware/booter.rs | 12 ++++++------ drivers/gpu/nova-core/firmware/fwsec.rs | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 46b02c8a591e..310d4e75bad3 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -240,8 +240,8 @@ impl From for bool { /// Different types of memory present in a falcon core. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum FalconMem { - /// Instruction Memory. - Imem, + /// Secure Instruction Memory. + ImemSecure, /// Data Memory. Dmem, } @@ -348,8 +348,8 @@ pub(crate) struct FalconBromParams { /// Trait for providing load parameters of falcon firmwares. pub(crate) trait FalconLoadParams { - /// Returns the load parameters for `IMEM`. - fn imem_load_params(&self) -> FalconLoadTarget; + /// Returns the load parameters for Secure `IMEM`. + fn imem_sec_load_params(&self) -> FalconLoadTarget; /// Returns the load parameters for `DMEM`. fn dmem_load_params(&self) -> FalconLoadTarget; @@ -460,7 +460,7 @@ impl Falcon { // // For DMEM we can fold the start offset into the DMA handle. let (src_start, dma_start) = match target_mem { - FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()), + FalconMem::ImemSecure => (load_offsets.src_start, fw.dma_handle()), FalconMem::Dmem => ( 0, fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, @@ -517,7 +517,7 @@ impl Falcon { let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() .set_size(DmaTrfCmdSize::Size256B) - .set_imem(target_mem == FalconMem::Imem) + .set_imem(target_mem == FalconMem::ImemSecure) .set_sec(if sec { 1 } else { 0 }); for pos in (0..num_transfers).map(|i| i * DMA_LEN) { @@ -552,7 +552,13 @@ impl Falcon { .set_mem_type(FalconFbifMemType::Physical) }); - self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?; + self.dma_wr( + bar, + fw, + FalconMem::ImemSecure, + fw.imem_sec_load_params(), + true, + )?; self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?; self.hal.program_brom(self, bar, &fw.brom_params())?; diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index f107f753214a..096cd01dbc9d 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -251,8 +251,8 @@ impl<'a> FirmwareSignature for BooterSignature<'a> {} /// The `Booter` loader firmware, responsible for loading the GSP. pub(crate) struct BooterFirmware { - // Load parameters for `IMEM` falcon memory. - imem_load_target: FalconLoadTarget, + // Load parameters for Secure `IMEM` falcon memory. + imem_sec_load_target: FalconLoadTarget, // Load parameters for `DMEM` falcon memory. dmem_load_target: FalconLoadTarget, // BROM falcon parameters. @@ -354,7 +354,7 @@ impl BooterFirmware { }; Ok(Self { - imem_load_target: FalconLoadTarget { + imem_sec_load_target: FalconLoadTarget { src_start: app0.offset, dst_start: 0, len: app0.len, @@ -371,8 +371,8 @@ impl BooterFirmware { } impl FalconLoadParams for BooterFirmware { - fn imem_load_params(&self) -> FalconLoadTarget { - self.imem_load_target.clone() + fn imem_sec_load_params(&self) -> FalconLoadTarget { + self.imem_sec_load_target.clone() } fn dmem_load_params(&self) -> FalconLoadTarget { @@ -384,7 +384,7 @@ impl FalconLoadParams for BooterFirmware { } fn boot_addr(&self) -> u32 { - self.imem_load_target.src_start + self.imem_sec_load_target.src_start } } diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index b28e34d279f4..6a2f5a0d4b15 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -224,7 +224,7 @@ pub(crate) struct FwsecFirmware { } impl FalconLoadParams for FwsecFirmware { - fn imem_load_params(&self) -> FalconLoadTarget { + fn imem_sec_load_params(&self) -> FalconLoadTarget { FalconLoadTarget { src_start: 0, dst_start: self.desc.imem_phys_base, -- cgit v1.2.3 From b72cb7bcc20c3a0d7fc29590a6e85de3dfbe9eff Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:37 -0600 Subject: gpu: nova-core: add ImemNonSecure section infrastructure The GSP booter firmware in Turing and GA100 includes a third memory section called ImemNonSecure, which is non-secure IMEM. This section must be loaded separately from DMEM and secure IMEM, but only if it actually exists. Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-3-ttabi@nvidia.com [acourbot@nvidia.com: add `debug_assert`.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 20 ++++++++++++++++++-- drivers/gpu/nova-core/firmware/booter.rs | 9 +++++++++ drivers/gpu/nova-core/firmware/fwsec.rs | 5 +++++ 3 files changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 310d4e75bad3..1d902ec62a6e 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -242,6 +242,9 @@ impl From for bool { pub(crate) enum FalconMem { /// Secure Instruction Memory. ImemSecure, + /// Non-Secure Instruction Memory. + #[expect(unused)] + ImemNonSecure, /// Data Memory. Dmem, } @@ -351,6 +354,10 @@ pub(crate) trait FalconLoadParams { /// Returns the load parameters for Secure `IMEM`. fn imem_sec_load_params(&self) -> FalconLoadTarget; + /// Returns the load parameters for Non-Secure `IMEM`, + /// used only on Turing and GA100. + fn imem_ns_load_params(&self) -> Option; + /// Returns the load parameters for `DMEM`. fn dmem_load_params(&self) -> FalconLoadTarget; @@ -460,7 +467,9 @@ impl Falcon { // // For DMEM we can fold the start offset into the DMA handle. let (src_start, dma_start) = match target_mem { - FalconMem::ImemSecure => (load_offsets.src_start, fw.dma_handle()), + FalconMem::ImemSecure | FalconMem::ImemNonSecure => { + (load_offsets.src_start, fw.dma_handle()) + } FalconMem::Dmem => ( 0, fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, @@ -517,7 +526,7 @@ impl Falcon { let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() .set_size(DmaTrfCmdSize::Size256B) - .set_imem(target_mem == FalconMem::ImemSecure) + .set_imem(target_mem != FalconMem::Dmem) .set_sec(if sec { 1 } else { 0 }); for pos in (0..num_transfers).map(|i| i * DMA_LEN) { @@ -546,6 +555,13 @@ impl Falcon { /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. pub(crate) fn dma_load>(&self, bar: &Bar0, fw: &F) -> Result { + // The Non-Secure section only exists on firmware used by Turing and GA100, and + // those platforms do not use DMA. + if fw.imem_ns_load_params().is_some() { + debug_assert!(false); + return Err(EINVAL); + } + self.dma_reset(bar); regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| { v.set_target(FalconFbifTarget::CoherentSysmem) diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index 096cd01dbc9d..1b98bb47424c 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -253,6 +253,9 @@ impl<'a> FirmwareSignature for BooterSignature<'a> {} pub(crate) struct BooterFirmware { // Load parameters for Secure `IMEM` falcon memory. imem_sec_load_target: FalconLoadTarget, + // Load parameters for Non-Secure `IMEM` falcon memory, + // used only on Turing and GA100 + imem_ns_load_target: Option, // Load parameters for `DMEM` falcon memory. dmem_load_target: FalconLoadTarget, // BROM falcon parameters. @@ -359,6 +362,8 @@ impl BooterFirmware { dst_start: 0, len: app0.len, }, + // Exists only in the booter image for Turing and GA100 + imem_ns_load_target: None, dmem_load_target: FalconLoadTarget { src_start: load_hdr.os_data_offset, dst_start: 0, @@ -375,6 +380,10 @@ impl FalconLoadParams for BooterFirmware { self.imem_sec_load_target.clone() } + fn imem_ns_load_params(&self) -> Option { + self.imem_ns_load_target.clone() + } + fn dmem_load_params(&self) -> FalconLoadTarget { self.dmem_load_target.clone() } diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index 6a2f5a0d4b15..e4009faba6c5 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -232,6 +232,11 @@ impl FalconLoadParams for FwsecFirmware { } } + fn imem_ns_load_params(&self) -> Option { + // Only used on Turing and GA100, so return None for now + None + } + fn dmem_load_params(&self) -> FalconLoadTarget { FalconLoadTarget { src_start: self.desc.imem_load_size, -- cgit v1.2.3 From a65fc53d47c05802feb4ca98f26a54251ef65cfa Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:38 -0600 Subject: gpu: nova-core: support header parsing on Turing/GA100 The Turing/GA100 version of Booter is slightly different from the GA102+ version. The headers are the same, but different fields of the headers are used to identify the IMEM section. In addition, there is an NMEM section on Turing/GA100. Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-4-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/booter.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index 1b98bb47424c..86556cee8e67 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -356,14 +356,30 @@ impl BooterFirmware { } }; + // There are two versions of Booter, one for Turing/GA100, and another for + // GA102+. The extraction of the IMEM sections differs between the two + // versions. Unfortunately, the file names are the same, and the headers + // don't indicate the versions. The only way to differentiate is by the Chipset. + let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 { + ( + app0.offset, + Some(FalconLoadTarget { + src_start: 0, + dst_start: load_hdr.os_code_offset, + len: load_hdr.os_code_size, + }), + ) + } else { + (0, None) + }; + Ok(Self { imem_sec_load_target: FalconLoadTarget { src_start: app0.offset, - dst_start: 0, + dst_start: imem_sec_dst_start, len: app0.len, }, - // Exists only in the booter image for Turing and GA100 - imem_ns_load_target: None, + imem_ns_load_target, dmem_load_target: FalconLoadTarget { src_start: load_hdr.os_data_offset, dst_start: 0, @@ -393,7 +409,11 @@ impl FalconLoadParams for BooterFirmware { } fn boot_addr(&self) -> u32 { - self.imem_sec_load_target.src_start + if let Some(ns_target) = &self.imem_ns_load_target { + ns_target.dst_start + } else { + self.imem_sec_load_target.src_start + } } } -- cgit v1.2.3 From 121ea04cd9f22ae8fb405d4929f6a7faefefe553 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:39 -0600 Subject: gpu: nova-core: add support for Turing/GA100 fwsignature Turing and GA100 share the same GSP-RM firmware binary, but the signature ELF section is labeled either ".fwsignature_tu10x" or ".fwsignature_tu11x". Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-5-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware/gsp.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 1025b7f746eb..beabae9a1189 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -214,9 +214,16 @@ impl GspFirmware { size, signatures: { let sigs_section = match chipset.arch() { + Architecture::Turing + if matches!(chipset, Chipset::TU116 | Chipset::TU117) => + { + ".fwsignature_tu11x" + } + Architecture::Turing => ".fwsignature_tu10x", + // GA100 uses the same firmware as Turing + Architecture::Ampere if chipset == Chipset::GA100 => ".fwsignature_tu10x", Architecture::Ampere => ".fwsignature_ga10x", Architecture::Ada => ".fwsignature_ad10x", - _ => return Err(ENOTSUPP), }; elf::elf64_section(firmware.data(), sigs_section) -- cgit v1.2.3 From f6507640b0cddfd90d398e3996321b1a735f98be Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:40 -0600 Subject: gpu: nova-core: add NV_PFALCON_FALCON_DMATRFCMD::with_falcon_mem() The with_falcon_mem() method initializes the 'imem' and 'sec' fields of the NV_PFALCON_FALCON_DMATRFCMD register based on the value of the FalconMem type. Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-6-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 14 +++----------- drivers/gpu/nova-core/regs.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 1d902ec62a6e..34b36f7b3bfd 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -458,7 +458,6 @@ impl Falcon { fw: &F, target_mem: FalconMem, load_offsets: FalconLoadTarget, - sec: bool, ) -> Result { const DMA_LEN: u32 = 256; @@ -526,8 +525,7 @@ impl Falcon { let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() .set_size(DmaTrfCmdSize::Size256B) - .set_imem(target_mem != FalconMem::Dmem) - .set_sec(if sec { 1 } else { 0 }); + .with_falcon_mem(target_mem); for pos in (0..num_transfers).map(|i| i * DMA_LEN) { // Perform a transfer of size `DMA_LEN`. @@ -568,14 +566,8 @@ impl Falcon { .set_mem_type(FalconFbifMemType::Physical) }); - self.dma_wr( - bar, - fw, - FalconMem::ImemSecure, - fw.imem_sec_load_params(), - true, - )?; - self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?; + self.dma_wr(bar, fw, FalconMem::ImemSecure, fw.imem_sec_load_params())?; + self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params())?; self.hal.program_brom(self, bar, &fw.brom_params())?; diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 82cc6c0790e5..b8ddfe2e5ae7 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -16,6 +16,7 @@ use crate::{ FalconCoreRevSubversion, FalconFbifMemType, FalconFbifTarget, + FalconMem, FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, @@ -325,6 +326,14 @@ register!(NV_PFALCON_FALCON_DMATRFCMD @ PFalconBase[0x00000118] { 16:16 set_dmtag as u8; }); +impl NV_PFALCON_FALCON_DMATRFCMD { + /// Programs the `imem` and `sec` fields for the given FalconMem + pub(crate) fn with_falcon_mem(self, mem: FalconMem) -> Self { + self.set_imem(mem != FalconMem::Dmem) + .set_sec(if mem == FalconMem::ImemSecure { 1 } else { 0 }) + } +} + register!(NV_PFALCON_FALCON_DMATRFFBOFFS @ PFalconBase[0x0000011c] { 31:0 offs as u32; }); -- cgit v1.2.3 From 82ed3243219d160601fdb98742633bee7dc6f360 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:41 -0600 Subject: gpu: nova-core: move some functions into the HAL A few Falcon methods are actually GPU-specific, so move them into the HAL. Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-7-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 45 +++---------------------------- drivers/gpu/nova-core/falcon/hal.rs | 10 +++++++ drivers/gpu/nova-core/falcon/hal/ga102.rs | 41 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 34b36f7b3bfd..e43563068e75 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -16,7 +16,6 @@ use kernel::{ prelude::*, sync::aref::ARef, time::{ - delay::fsleep, Delta, // }, }; @@ -398,48 +397,11 @@ impl Falcon { regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); } - /// Wait for memory scrubbing to complete. - fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { - // TIMEOUT: memory scrubbing should complete in less than 20ms. - read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), - |r| r.mem_scrubbing_done(), - Delta::ZERO, - Delta::from_millis(20), - ) - .map(|_| ()) - } - - /// Reset the falcon engine. - fn reset_eng(&self, bar: &Bar0) -> Result { - let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); - - // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set - // RESET_READY so a non-failing timeout is used. - let _ = read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), - |r| r.reset_ready(), - Delta::ZERO, - Delta::from_micros(150), - ); - - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true)); - - // TIMEOUT: falcon engine should not take more than 10us to reset. - fsleep(Delta::from_micros(10)); - - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false)); - - self.reset_wait_mem_scrubbing(bar)?; - - Ok(()) - } - /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete. pub(crate) fn reset(&self, bar: &Bar0) -> Result { - self.reset_eng(bar)?; + self.hal.reset_eng(bar)?; self.hal.select_core(self, bar)?; - self.reset_wait_mem_scrubbing(bar)?; + self.hal.reset_wait_mem_scrubbing(bar)?; regs::NV_PFALCON_FALCON_RM::default() .set_value(regs::NV_PMC_BOOT_0::read(bar).into()) @@ -674,8 +636,7 @@ impl Falcon { /// /// Returns `true` if the RISC-V core is active, `false` otherwise. pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { - let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID); - cpuctl.active_stat() + self.hal.is_riscv_active(bar) } /// Write the application version to the OS register. diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs index 8dc56a28ad65..c77a1568ea96 100644 --- a/drivers/gpu/nova-core/falcon/hal.rs +++ b/drivers/gpu/nova-core/falcon/hal.rs @@ -37,6 +37,16 @@ pub(crate) trait FalconHal: Send + Sync { /// Program the boot ROM registers prior to starting a secure firmware. fn program_brom(&self, falcon: &Falcon, bar: &Bar0, params: &FalconBromParams) -> Result; + + /// Check if the RISC-V core is active. + /// Returns `true` if the RISC-V core is active, `false` otherwise. + fn is_riscv_active(&self, bar: &Bar0) -> bool; + + /// Wait for memory scrubbing to complete. + fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result; + + /// Reset the falcon engine. + fn reset_eng(&self, bar: &Bar0) -> Result; } /// Returns a boxed falcon HAL adequate for `chipset`. diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index 0bdfe45a2d03..61cc3d261196 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -6,6 +6,7 @@ use kernel::{ device, io::poll::read_poll_timeout, prelude::*, + time::delay::fsleep, time::Delta, // }; @@ -117,4 +118,44 @@ impl FalconHal for Ga102 { fn program_brom(&self, _falcon: &Falcon, bar: &Bar0, params: &FalconBromParams) -> Result { program_brom_ga102::(bar, params) } + + fn is_riscv_active(&self, bar: &Bar0) -> bool { + let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID); + cpuctl.active_stat() + } + + fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { + // TIMEOUT: memory scrubbing should complete in less than 20ms. + read_poll_timeout( + || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), + |r| r.mem_scrubbing_done(), + Delta::ZERO, + Delta::from_millis(20), + ) + .map(|_| ()) + } + + fn reset_eng(&self, bar: &Bar0) -> Result { + let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); + + // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set + // RESET_READY so a non-failing timeout is used. + let _ = read_poll_timeout( + || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), + |r| r.reset_ready(), + Delta::ZERO, + Delta::from_micros(150), + ); + + regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true)); + + // TIMEOUT: falcon engine should not take more than 10us to reset. + fsleep(Delta::from_micros(10)); + + regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false)); + + self.reset_wait_mem_scrubbing(bar)?; + + Ok(()) + } } -- cgit v1.2.3 From 954b38fd0a8f5feaa44a9c5c05ed771815949d95 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:42 -0600 Subject: gpu: nova-core: Add basic Turing HAL Add the basic HAL for recognizing Turing GPUs. This isn't enough to support booting GSP-RM on Turing, but it's a start. Note that GA100, which boots using the same method as Turing, is not supported yet. Signed-off-by: Timur Tabi Reviewed-by: John Hubbard Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-8-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon/hal.rs | 4 ++ drivers/gpu/nova-core/falcon/hal/tu102.rs | 79 +++++++++++++++++++++++++++++++ drivers/gpu/nova-core/regs.rs | 14 ++++++ 3 files changed, 97 insertions(+) create mode 100644 drivers/gpu/nova-core/falcon/hal/tu102.rs (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs index c77a1568ea96..c886ba03d1f6 100644 --- a/drivers/gpu/nova-core/falcon/hal.rs +++ b/drivers/gpu/nova-core/falcon/hal.rs @@ -13,6 +13,7 @@ use crate::{ }; mod ga102; +mod tu102; /// Hardware Abstraction Layer for Falcon cores. /// @@ -60,6 +61,9 @@ pub(super) fn falcon_hal( use Chipset::*; let hal = match chipset { + TU102 | TU104 | TU106 | TU116 | TU117 => { + KBox::new(tu102::Tu102::::new(), GFP_KERNEL)? as KBox> + } GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => { KBox::new(ga102::Ga102::::new(), GFP_KERNEL)? as KBox> } diff --git a/drivers/gpu/nova-core/falcon/hal/tu102.rs b/drivers/gpu/nova-core/falcon/hal/tu102.rs new file mode 100644 index 000000000000..586d5dc6b417 --- /dev/null +++ b/drivers/gpu/nova-core/falcon/hal/tu102.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::marker::PhantomData; + +use kernel::{ + io::poll::read_poll_timeout, + prelude::*, + time::delay::fsleep, + time::Delta, // +}; + +use crate::{ + driver::Bar0, + falcon::{ + Falcon, + FalconBromParams, + FalconEngine, // + }, + regs, // +}; + +use super::FalconHal; + +pub(super) struct Tu102(PhantomData); + +impl Tu102 { + pub(super) fn new() -> Self { + Self(PhantomData) + } +} + +impl FalconHal for Tu102 { + fn select_core(&self, _falcon: &Falcon, _bar: &Bar0) -> Result { + Ok(()) + } + + fn signature_reg_fuse_version( + &self, + _falcon: &Falcon, + _bar: &Bar0, + _engine_id_mask: u16, + _ucode_id: u8, + ) -> Result { + Ok(0) + } + + fn program_brom(&self, _falcon: &Falcon, _bar: &Bar0, _params: &FalconBromParams) -> Result { + Ok(()) + } + + fn is_riscv_active(&self, bar: &Bar0) -> bool { + let cpuctl = regs::NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS::read(bar, &E::ID); + cpuctl.active_stat() + } + + fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { + // TIMEOUT: memory scrubbing should complete in less than 10ms. + read_poll_timeout( + || Ok(regs::NV_PFALCON_FALCON_DMACTL::read(bar, &E::ID)), + |r| r.mem_scrubbing_done(), + Delta::ZERO, + Delta::from_millis(10), + ) + .map(|_| ()) + } + + fn reset_eng(&self, bar: &Bar0) -> Result { + regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true)); + + // TIMEOUT: falcon engine should not take more than 10us to reset. + fsleep(Delta::from_micros(10)); + + regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false)); + + self.reset_wait_mem_scrubbing(bar)?; + + Ok(()) + } +} diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index b8ddfe2e5ae7..cd7b7aa6fc2a 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -307,6 +307,13 @@ register!(NV_PFALCON_FALCON_DMACTL @ PFalconBase[0x0000010c] { 7:7 secure_stat as bool; }); +impl NV_PFALCON_FALCON_DMACTL { + /// Returns `true` if memory scrubbing is completed. + pub(crate) fn mem_scrubbing_done(self) -> bool { + !self.dmem_scrubbing() && !self.imem_scrubbing() + } +} + register!(NV_PFALCON_FALCON_DMATRFBASE @ PFalconBase[0x00000110] { 31:0 base as u32; }); @@ -389,6 +396,13 @@ register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] { // PRISCV +// RISC-V status register for debug (Turing and GA100 only). +// Reflects current RISC-V core status. +register!(NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS @ PFalcon2Base[0x00000240] { + 0:0 active_stat as bool, "RISC-V core active/inactive status"; +}); + +// GA102 and later register!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] { 0:0 halted as bool; 7:7 active_stat as bool; -- cgit v1.2.3 From a75718afc9a5c61d9266d5d0010228a6a3bb7233 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:43 -0600 Subject: gpu: nova-core: add NV_PFALCON_FALCON_ENGINE::reset_engine() Add a method for the NV_PFALCON_FALCON_ENGINE register that reset the Falcon, and update the reset_eng() HAL functions to use it. Signed-off-by: Timur Tabi Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-9-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon/hal/ga102.rs | 9 +-------- drivers/gpu/nova-core/falcon/hal/tu102.rs | 9 +-------- drivers/gpu/nova-core/regs.rs | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index 61cc3d261196..39863813a2bf 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -6,7 +6,6 @@ use kernel::{ device, io::poll::read_poll_timeout, prelude::*, - time::delay::fsleep, time::Delta, // }; @@ -147,13 +146,7 @@ impl FalconHal for Ga102 { Delta::from_micros(150), ); - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true)); - - // TIMEOUT: falcon engine should not take more than 10us to reset. - fsleep(Delta::from_micros(10)); - - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false)); - + regs::NV_PFALCON_FALCON_ENGINE::reset_engine::(bar); self.reset_wait_mem_scrubbing(bar)?; Ok(()) diff --git a/drivers/gpu/nova-core/falcon/hal/tu102.rs b/drivers/gpu/nova-core/falcon/hal/tu102.rs index 586d5dc6b417..23fbf6110572 100644 --- a/drivers/gpu/nova-core/falcon/hal/tu102.rs +++ b/drivers/gpu/nova-core/falcon/hal/tu102.rs @@ -5,7 +5,6 @@ use core::marker::PhantomData; use kernel::{ io::poll::read_poll_timeout, prelude::*, - time::delay::fsleep, time::Delta, // }; @@ -65,13 +64,7 @@ impl FalconHal for Tu102 { } fn reset_eng(&self, bar: &Bar0) -> Result { - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true)); - - // TIMEOUT: falcon engine should not take more than 10us to reset. - fsleep(Delta::from_micros(10)); - - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false)); - + regs::NV_PFALCON_FALCON_ENGINE::reset_engine::(bar); self.reset_wait_mem_scrubbing(bar)?; Ok(()) diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index cd7b7aa6fc2a..ea0d32f5396c 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -7,13 +7,18 @@ #[macro_use] pub(crate) mod macros; -use kernel::prelude::*; +use kernel::{ + prelude::*, + time, // +}; use crate::{ + driver::Bar0, falcon::{ DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, + FalconEngine, FalconFbifMemType, FalconFbifTarget, FalconMem, @@ -365,6 +370,18 @@ register!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] { 0:0 reset as bool; }); +impl NV_PFALCON_FALCON_ENGINE { + /// Resets the falcon + pub(crate) fn reset_engine(bar: &Bar0) { + Self::read(bar, &E::ID).set_reset(true).write(bar, &E::ID); + + // TIMEOUT: falcon engine should not take more than 10us to reset. + time::delay::fsleep(time::Delta::from_micros(10)); + + Self::read(bar, &E::ID).set_reset(false).write(bar, &E::ID); + } +} + register!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600[8]] { 1:0 target as u8 ?=> FalconFbifTarget; 2:2 mem_type as bool => FalconFbifMemType; -- cgit v1.2.3 From ab2aad252fe21347674cf969a5e9d44d69e403bb Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:44 -0600 Subject: gpu: nova-core: add Falcon HAL method load_method() Some GPUs do not support using DMA to transfer code/data from system memory to Falcon memory, and instead must use programmed I/O (PIO). Add a function to the Falcon HAL to indicate whether a given GPU's Falcons support DMA for this purpose. Signed-off-by: Timur Tabi Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-10-ttabi@nvidia.com [acourbot@nvidia.com: add short code to call into the HAL.] [acourbot@nvidia.com: make `dma_load` private as per feedback.] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/falcon.rs | 11 ++++++++++- drivers/gpu/nova-core/falcon/hal.rs | 12 ++++++++++++ drivers/gpu/nova-core/falcon/hal/ga102.rs | 5 +++++ drivers/gpu/nova-core/falcon/hal/tu102.rs | 5 +++++ drivers/gpu/nova-core/firmware/fwsec.rs | 2 +- drivers/gpu/nova-core/gsp/boot.rs | 2 +- 6 files changed, 34 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index e43563068e75..37bfee1d0949 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -23,6 +23,7 @@ use kernel::{ use crate::{ dma::DmaObject, driver::Bar0, + falcon::hal::LoadMethod, gpu::Chipset, num::{ FromSafeCast, @@ -514,7 +515,7 @@ impl Falcon { } /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. - pub(crate) fn dma_load>(&self, bar: &Bar0, fw: &F) -> Result { + fn dma_load>(&self, bar: &Bar0, fw: &F) -> Result { // The Non-Secure section only exists on firmware used by Turing and GA100, and // those platforms do not use DMA. if fw.imem_ns_load_params().is_some() { @@ -639,6 +640,14 @@ impl Falcon { self.hal.is_riscv_active(bar) } + // Load a firmware image into Falcon memory + pub(crate) fn load>(&self, bar: &Bar0, fw: &F) -> Result { + match self.hal.load_method() { + LoadMethod::Dma => self.dma_load(bar, fw), + LoadMethod::Pio => Err(ENOTSUPP), + } + } + /// Write the application version to the OS register. pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) { regs::NV_PFALCON_FALCON_OS::default() diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs index c886ba03d1f6..89babd5f9325 100644 --- a/drivers/gpu/nova-core/falcon/hal.rs +++ b/drivers/gpu/nova-core/falcon/hal.rs @@ -15,6 +15,15 @@ use crate::{ mod ga102; mod tu102; +/// Method used to load data into falcon memory. Some GPU architectures need +/// PIO and others can use DMA. +pub(crate) enum LoadMethod { + /// Programmed I/O + Pio, + /// Direct Memory Access + Dma, +} + /// Hardware Abstraction Layer for Falcon cores. /// /// Implements chipset-specific low-level operations. The trait is generic against [`FalconEngine`] @@ -48,6 +57,9 @@ pub(crate) trait FalconHal: Send + Sync { /// Reset the falcon engine. fn reset_eng(&self, bar: &Bar0) -> Result; + + /// returns the method needed to load data into Falcon memory + fn load_method(&self) -> LoadMethod; } /// Returns a boxed falcon HAL adequate for `chipset`. diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index 39863813a2bf..8f62df10da0a 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -12,6 +12,7 @@ use kernel::{ use crate::{ driver::Bar0, falcon::{ + hal::LoadMethod, Falcon, FalconBromParams, FalconEngine, @@ -151,4 +152,8 @@ impl FalconHal for Ga102 { Ok(()) } + + fn load_method(&self) -> LoadMethod { + LoadMethod::Dma + } } diff --git a/drivers/gpu/nova-core/falcon/hal/tu102.rs b/drivers/gpu/nova-core/falcon/hal/tu102.rs index 23fbf6110572..7de6f24cc0a0 100644 --- a/drivers/gpu/nova-core/falcon/hal/tu102.rs +++ b/drivers/gpu/nova-core/falcon/hal/tu102.rs @@ -11,6 +11,7 @@ use kernel::{ use crate::{ driver::Bar0, falcon::{ + hal::LoadMethod, Falcon, FalconBromParams, FalconEngine, // @@ -69,4 +70,8 @@ impl FalconHal for Tu102 { Ok(()) } + + fn load_method(&self) -> LoadMethod { + LoadMethod::Pio + } } diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index e4009faba6c5..6fc5a008bb47 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -428,7 +428,7 @@ impl FwsecFirmware { .reset(bar) .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?; falcon - .dma_load(bar, self) + .load(bar, self) .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; let (mbox0, _) = falcon .boot(bar, Some(0), None) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 581b412554dc..be427fe26a58 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -183,7 +183,7 @@ impl super::Gsp { ); sec2_falcon.reset(bar)?; - sec2_falcon.dma_load(bar, &booter_loader)?; + sec2_falcon.load(bar, &booter_loader)?; let wpr_handle = wpr_meta.dma_handle(); let (mbox0, mbox1) = sec2_falcon.boot( bar, -- cgit v1.2.3 From dbfb5aa41f16484e5a5971571739e7ae80f5e32c Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Jan 2026 16:28:45 -0600 Subject: gpu: nova-core: add FalconUCodeDescV2 support The FRTS firmware in Turing and GA100 VBIOS has an older header format (v2 instead of v3). To support both v2 and v3 at runtime, add the FalconUCodeDescV2 struct, and update code that references the FalconUCodeDescV3 directly with a FalconUCodeDesc enum that encapsulates both. Signed-off-by: Timur Tabi Reviewed-by: Joel Fernandes Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-11-ttabi@nvidia.com Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/firmware.rs | 203 +++++++++++++++++++++++++++++++- drivers/gpu/nova-core/firmware/fwsec.rs | 46 +++----- drivers/gpu/nova-core/vbios.rs | 64 +++++----- 3 files changed, 249 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 2d2008b33fb4..68779540aa28 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -4,6 +4,7 @@ //! to be loaded into a given execution unit. use core::marker::PhantomData; +use core::ops::Deref; use kernel::{ device, @@ -15,7 +16,10 @@ use kernel::{ use crate::{ dma::DmaObject, - falcon::FalconFirmware, + falcon::{ + FalconFirmware, + FalconLoadTarget, // + }, gpu, num::{ FromSafeCast, @@ -43,6 +47,46 @@ fn request_firmware( .and_then(|path| firmware::Firmware::request(&path, dev)) } +/// Structure used to describe some firmwares, notably FWSEC-FRTS. +#[repr(C)] +#[derive(Debug, Clone)] +pub(crate) struct FalconUCodeDescV2 { + /// Header defined by 'NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*' in OpenRM. + hdr: u32, + /// Stored size of the ucode after the header, compressed or uncompressed + stored_size: u32, + /// Uncompressed size of the ucode. If store_size == uncompressed_size, then the ucode + /// is not compressed. + pub(crate) uncompressed_size: u32, + /// Code entry point + pub(crate) virtual_entry: u32, + /// Offset after the code segment at which the Application Interface Table headers are located. + pub(crate) interface_offset: u32, + /// Base address at which to load the code segment into 'IMEM'. + pub(crate) imem_phys_base: u32, + /// Size in bytes of the code to copy into 'IMEM'. + pub(crate) imem_load_size: u32, + /// Virtual 'IMEM' address (i.e. 'tag') at which the code should start. + pub(crate) imem_virt_base: u32, + /// Virtual address of secure IMEM segment. + pub(crate) imem_sec_base: u32, + /// Size of secure IMEM segment. + pub(crate) imem_sec_size: u32, + /// Offset into stored (uncompressed) image at which DMEM begins. + pub(crate) dmem_offset: u32, + /// Base address at which to load the data segment into 'DMEM'. + pub(crate) dmem_phys_base: u32, + /// Size in bytes of the data to copy into 'DMEM'. + pub(crate) dmem_load_size: u32, + /// "Alternate" Size of data to load into IMEM. + pub(crate) alt_imem_load_size: u32, + /// "Alternate" Size of data to load into DMEM. + pub(crate) alt_dmem_load_size: u32, +} + +// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. +unsafe impl FromBytes for FalconUCodeDescV2 {} + /// Structure used to describe some firmwares, notably FWSEC-FRTS. #[repr(C)] #[derive(Debug, Clone)] @@ -76,13 +120,164 @@ pub(crate) struct FalconUCodeDescV3 { _reserved: u16, } -impl FalconUCodeDescV3 { +// SAFETY: all bit patterns are valid for this type, and it doesn't use +// interior mutability. +unsafe impl FromBytes for FalconUCodeDescV3 {} + +/// Enum wrapping the different versions of Falcon microcode descriptors. +/// +/// This allows handling both V2 and V3 descriptor formats through a +/// unified type, providing version-agnostic access to firmware metadata +/// via the [`FalconUCodeDescriptor`] trait. +#[derive(Debug, Clone)] +pub(crate) enum FalconUCodeDesc { + V2(FalconUCodeDescV2), + V3(FalconUCodeDescV3), +} + +impl Deref for FalconUCodeDesc { + type Target = dyn FalconUCodeDescriptor; + + fn deref(&self) -> &Self::Target { + match self { + FalconUCodeDesc::V2(v2) => v2, + FalconUCodeDesc::V3(v3) => v3, + } + } +} + +/// Trait providing a common interface for accessing Falcon microcode descriptor fields. +/// +/// This trait abstracts over the different descriptor versions ([`FalconUCodeDescV2`] and +/// [`FalconUCodeDescV3`]), allowing code to work with firmware metadata without needing to +/// know the specific descriptor version. Fields not present return zero. +pub(crate) trait FalconUCodeDescriptor { + fn hdr(&self) -> u32; + fn imem_load_size(&self) -> u32; + fn interface_offset(&self) -> u32; + fn dmem_load_size(&self) -> u32; + fn pkc_data_offset(&self) -> u32; + fn engine_id_mask(&self) -> u16; + fn ucode_id(&self) -> u8; + fn signature_count(&self) -> u8; + fn signature_versions(&self) -> u16; + /// Returns the size in bytes of the header. - pub(crate) fn size(&self) -> usize { + fn size(&self) -> usize { + let hdr = self.hdr(); + const HDR_SIZE_SHIFT: u32 = 16; const HDR_SIZE_MASK: u32 = 0xffff0000; + ((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast() + } - ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast() + fn imem_sec_load_params(&self) -> FalconLoadTarget; + fn imem_ns_load_params(&self) -> Option; + fn dmem_load_params(&self) -> FalconLoadTarget; +} + +impl FalconUCodeDescriptor for FalconUCodeDescV2 { + fn hdr(&self) -> u32 { + self.hdr + } + fn imem_load_size(&self) -> u32 { + self.imem_load_size + } + fn interface_offset(&self) -> u32 { + self.interface_offset + } + fn dmem_load_size(&self) -> u32 { + self.dmem_load_size + } + fn pkc_data_offset(&self) -> u32 { + 0 + } + fn engine_id_mask(&self) -> u16 { + 0 + } + fn ucode_id(&self) -> u8 { + 0 + } + fn signature_count(&self) -> u8 { + 0 + } + fn signature_versions(&self) -> u16 { + 0 + } + + fn imem_sec_load_params(&self) -> FalconLoadTarget { + FalconLoadTarget { + src_start: 0, + dst_start: self.imem_sec_base, + len: self.imem_sec_size, + } + } + + fn imem_ns_load_params(&self) -> Option { + Some(FalconLoadTarget { + src_start: 0, + dst_start: self.imem_phys_base, + len: self.imem_load_size.checked_sub(self.imem_sec_size)?, + }) + } + + fn dmem_load_params(&self) -> FalconLoadTarget { + FalconLoadTarget { + src_start: self.dmem_offset, + dst_start: self.dmem_phys_base, + len: self.dmem_load_size, + } + } +} + +impl FalconUCodeDescriptor for FalconUCodeDescV3 { + fn hdr(&self) -> u32 { + self.hdr + } + fn imem_load_size(&self) -> u32 { + self.imem_load_size + } + fn interface_offset(&self) -> u32 { + self.interface_offset + } + fn dmem_load_size(&self) -> u32 { + self.dmem_load_size + } + fn pkc_data_offset(&self) -> u32 { + self.pkc_data_offset + } + fn engine_id_mask(&self) -> u16 { + self.engine_id_mask + } + fn ucode_id(&self) -> u8 { + self.ucode_id + } + fn signature_count(&self) -> u8 { + self.signature_count + } + fn signature_versions(&self) -> u16 { + self.signature_versions + } + + fn imem_sec_load_params(&self) -> FalconLoadTarget { + FalconLoadTarget { + src_start: 0, + dst_start: self.imem_phys_base, + len: self.imem_load_size, + } + } + + fn imem_ns_load_params(&self) -> Option { + // Not used on V3 platforms + None + } + + fn dmem_load_params(&self) -> FalconLoadTarget { + FalconLoadTarget { + src_start: self.imem_load_size, + dst_start: self.dmem_phys_base, + len: self.dmem_load_size, + } } } diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index 6fc5a008bb47..a8ec08a500ac 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -40,7 +40,7 @@ use crate::{ FalconLoadTarget, // }, firmware::{ - FalconUCodeDescV3, + FalconUCodeDesc, FirmwareDmaObject, FirmwareSignature, Signed, @@ -218,38 +218,29 @@ unsafe fn transmute_mut( /// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow. pub(crate) struct FwsecFirmware { /// Descriptor of the firmware. - desc: FalconUCodeDescV3, + desc: FalconUCodeDesc, /// GPU-accessible DMA object containing the firmware. ucode: FirmwareDmaObject, } impl FalconLoadParams for FwsecFirmware { fn imem_sec_load_params(&self) -> FalconLoadTarget { - FalconLoadTarget { - src_start: 0, - dst_start: self.desc.imem_phys_base, - len: self.desc.imem_load_size, - } + self.desc.imem_sec_load_params() } fn imem_ns_load_params(&self) -> Option { - // Only used on Turing and GA100, so return None for now - None + self.desc.imem_ns_load_params() } fn dmem_load_params(&self) -> FalconLoadTarget { - FalconLoadTarget { - src_start: self.desc.imem_load_size, - dst_start: self.desc.dmem_phys_base, - len: self.desc.dmem_load_size, - } + self.desc.dmem_load_params() } fn brom_params(&self) -> FalconBromParams { FalconBromParams { - pkc_data_offset: self.desc.pkc_data_offset, - engine_id_mask: self.desc.engine_id_mask, - ucode_id: self.desc.ucode_id, + pkc_data_offset: self.desc.pkc_data_offset(), + engine_id_mask: self.desc.engine_id_mask(), + ucode_id: self.desc.ucode_id(), } } @@ -273,10 +264,10 @@ impl FalconFirmware for FwsecFirmware { impl FirmwareDmaObject { fn new_fwsec(dev: &Device, bios: &Vbios, cmd: FwsecCommand) -> Result { let desc = bios.fwsec_image().header()?; - let ucode = bios.fwsec_image().ucode(desc)?; + let ucode = bios.fwsec_image().ucode(&desc)?; let mut dma_object = DmaObject::from_data(dev, ucode)?; - let hdr_offset = usize::from_safe_cast(desc.imem_load_size + desc.interface_offset); + let hdr_offset = usize::from_safe_cast(desc.imem_load_size() + desc.interface_offset()); // SAFETY: we have exclusive access to `dma_object`. let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?; @@ -303,7 +294,7 @@ impl FirmwareDmaObject { let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe { transmute_mut( &mut dma_object, - (desc.imem_load_size + dmem_base).into_safe_cast(), + (desc.imem_load_size() + dmem_base).into_safe_cast(), ) }?; @@ -317,7 +308,7 @@ impl FirmwareDmaObject { let frts_cmd: &mut FrtsCmd = unsafe { transmute_mut( &mut dma_object, - (desc.imem_load_size + cmd_in_buffer_offset).into_safe_cast(), + (desc.imem_load_size() + cmd_in_buffer_offset).into_safe_cast(), ) }?; @@ -364,11 +355,12 @@ impl FwsecFirmware { // Patch signature if needed. let desc = bios.fwsec_image().header()?; - let ucode_signed = if desc.signature_count != 0 { - let sig_base_img = usize::from_safe_cast(desc.imem_load_size + desc.pkc_data_offset); - let desc_sig_versions = u32::from(desc.signature_versions); + let ucode_signed = if desc.signature_count() != 0 { + let sig_base_img = + usize::from_safe_cast(desc.imem_load_size() + desc.pkc_data_offset()); + let desc_sig_versions = u32::from(desc.signature_versions()); let reg_fuse_version = - falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?; + falcon.signature_reg_fuse_version(bar, desc.engine_id_mask(), desc.ucode_id())?; dev_dbg!( dev, "desc_sig_versions: {:#x}, reg_fuse_version: {}\n", @@ -402,7 +394,7 @@ impl FwsecFirmware { dev_dbg!(dev, "patching signature with index {}\n", signature_idx); let signature = bios .fwsec_image() - .sigs(desc) + .sigs(&desc) .and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?; ucode_dma.patch_signature(signature, sig_base_img)? @@ -411,7 +403,7 @@ impl FwsecFirmware { }; Ok(FwsecFirmware { - desc: desc.clone(), + desc, ucode: ucode_signed, }) } diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index e59eee2050a8..72cba8659a2d 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -19,6 +19,8 @@ use crate::{ driver::Bar0, firmware::{ fwsec::Bcrt30Rsa3kSignature, + FalconUCodeDesc, + FalconUCodeDescV2, FalconUCodeDescV3, // }, num::FromSafeCast, @@ -998,20 +1000,11 @@ impl FwSecBiosBuilder { } impl FwSecBiosImage { - /// Get the FwSec header ([`FalconUCodeDescV3`]). - pub(crate) fn header(&self) -> Result<&FalconUCodeDescV3> { + /// Get the FwSec header ([`FalconUCodeDesc`]). + pub(crate) fn header(&self) -> Result { // Get the falcon ucode offset that was found in setup_falcon_data. let falcon_ucode_offset = self.falcon_ucode_offset; - // Make sure the offset is within the data bounds. - if falcon_ucode_offset + core::mem::size_of::() > self.base.data.len() { - dev_err!( - self.base.dev, - "fwsec-frts header not contained within BIOS bounds\n" - ); - return Err(ERANGE); - } - // Read the first 4 bytes to get the version. let hdr_bytes: [u8; 4] = self.base.data[falcon_ucode_offset..falcon_ucode_offset + 4] .try_into() @@ -1019,33 +1012,34 @@ impl FwSecBiosImage { let hdr = u32::from_le_bytes(hdr_bytes); let ver = (hdr & 0xff00) >> 8; - if ver != 3 { - dev_err!(self.base.dev, "invalid fwsec firmware version: {:?}\n", ver); - return Err(EINVAL); + let data = self.base.data.get(falcon_ucode_offset..).ok_or(EINVAL)?; + match ver { + 2 => { + let v2 = FalconUCodeDescV2::from_bytes_copy_prefix(data) + .ok_or(EINVAL)? + .0; + Ok(FalconUCodeDesc::V2(v2)) + } + 3 => { + let v3 = FalconUCodeDescV3::from_bytes_copy_prefix(data) + .ok_or(EINVAL)? + .0; + Ok(FalconUCodeDesc::V3(v3)) + } + _ => { + dev_err!(self.base.dev, "invalid fwsec firmware version: {:?}\n", ver); + Err(EINVAL) + } } - - // Return a reference to the FalconUCodeDescV3 structure. - // - // SAFETY: We have checked that `falcon_ucode_offset + size_of::` is - // within the bounds of `data`. Also, this data vector is from ROM, and the `data` field - // in `BiosImageBase` is immutable after construction. - Ok(unsafe { - &*(self - .base - .data - .as_ptr() - .add(falcon_ucode_offset) - .cast::()) - }) } /// Get the ucode data as a byte slice - pub(crate) fn ucode(&self, desc: &FalconUCodeDescV3) -> Result<&[u8]> { + pub(crate) fn ucode(&self, desc: &FalconUCodeDesc) -> Result<&[u8]> { let falcon_ucode_offset = self.falcon_ucode_offset; // The ucode data follows the descriptor. let ucode_data_offset = falcon_ucode_offset + desc.size(); - let size = usize::from_safe_cast(desc.imem_load_size + desc.dmem_load_size); + let size = usize::from_safe_cast(desc.imem_load_size() + desc.dmem_load_size()); // Get the data slice, checking bounds in a single operation. self.base @@ -1061,10 +1055,14 @@ impl FwSecBiosImage { } /// Get the signatures as a byte slice - pub(crate) fn sigs(&self, desc: &FalconUCodeDescV3) -> Result<&[Bcrt30Rsa3kSignature]> { + pub(crate) fn sigs(&self, desc: &FalconUCodeDesc) -> Result<&[Bcrt30Rsa3kSignature]> { + let hdr_size = match desc { + FalconUCodeDesc::V2(_v2) => core::mem::size_of::(), + FalconUCodeDesc::V3(_v3) => core::mem::size_of::(), + }; // The signatures data follows the descriptor. - let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::(); - let sigs_count = usize::from(desc.signature_count); + let sigs_data_offset = self.falcon_ucode_offset + hdr_size; + let sigs_count = usize::from(desc.signature_count()); let sigs_size = sigs_count * core::mem::size_of::(); // Make sure the data is within bounds. -- cgit v1.2.3 From 58d26d42818c0f8c9b334cc7cf318b43046e675f Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 22 Jan 2026 16:28:46 -0600 Subject: gpu: nova-core: align LibosMemoryRegionInitArgument size to page size On Turing and GA100 (i.e. the versions that use Libos v2), GSP-RM insists that the 'size' parameter of the LibosMemoryRegionInitArgument struct be aligned to 4KB. The logging buffers are already aligned to that size, so only the GSP_ARGUMENTS_CACHED struct needs to be adjusted. Make that adjustment by adding padding to the end of the struct. Signed-off-by: Timur Tabi Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260122222848.2555890-12-ttabi@nvidia.com [acourbot@nvidia.com: GspArgumentsAligned -> GspArgumentsPadded] Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/gsp.rs | 8 ++++---- drivers/gpu/nova-core/gsp/fw.rs | 14 +++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 766fd9905358..174feaca0a6b 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -27,7 +27,7 @@ pub(crate) use fw::{ use crate::{ gsp::cmdq::Cmdq, gsp::fw::{ - GspArgumentsCached, + GspArgumentsPadded, LibosMemoryRegionInitArgument, // }, num, @@ -114,7 +114,7 @@ pub(crate) struct Gsp { /// Command queue. pub(crate) cmdq: Cmdq, /// RM arguments. - rmargs: CoherentAllocation, + rmargs: CoherentAllocation, } impl Gsp { @@ -133,7 +133,7 @@ impl Gsp { logintr: LogBuffer::new(dev)?, logrm: LogBuffer::new(dev)?, cmdq: Cmdq::new(dev)?, - rmargs: CoherentAllocation::::alloc_coherent( + rmargs: CoherentAllocation::::alloc_coherent( dev, 1, GFP_KERNEL | __GFP_ZERO, @@ -149,7 +149,7 @@ impl Gsp { libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0) )?; dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; - dma_write!(rmargs[0] = fw::GspArgumentsCached::new(cmdq))?; + dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(cmdq))?; dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; }, })) diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index caeb0d251fe5..83ff91614e36 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -904,9 +904,21 @@ impl GspArgumentsCached { // SAFETY: Padding is explicit and will not contain uninitialized data. unsafe impl AsBytes for GspArgumentsCached {} +/// On Turing and GA100, the entries in the `LibosMemoryRegionInitArgument` +/// must all be a multiple of GSP_PAGE_SIZE in size, so add padding to force it +/// to that size. +#[repr(C)] +pub(crate) struct GspArgumentsPadded { + pub(crate) inner: GspArgumentsCached, + _padding: [u8; GSP_PAGE_SIZE - core::mem::size_of::()], +} + +// SAFETY: Padding is explicit and will not contain uninitialized data. +unsafe impl AsBytes for GspArgumentsPadded {} + // SAFETY: This struct only contains integer types for which all bit patterns // are valid. -unsafe impl FromBytes for GspArgumentsCached {} +unsafe impl FromBytes for GspArgumentsPadded {} /// Init arguments for the message queue. #[repr(transparent)] -- cgit v1.2.3