diff options
| author | Thomas Gleixner <tglx@kernel.org> | 2026-04-12 22:33:39 +0200 |
|---|---|---|
| committer | Thomas Gleixner <tglx@kernel.org> | 2026-04-12 22:33:39 +0200 |
| commit | 1655f6895a896eb632ca8a019259bc5d358a9712 (patch) | |
| tree | d34ad546baeed14bede485773b1be28ab8e6a289 /drivers/gpu/nova-core | |
| parent | 7eaf8e32de5f4ed4defda6fff81749041bb9d23f (diff) | |
| parent | 68ed094971b09ba530baf6f75cf1902df880a8d1 (diff) | |
Merge tag 'timers-v7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/daniel.lezcano/linux into timers/clocksource
- Added the DT bindings for the compatible string 'fsl,imx25-epit'
(Frank Li)
- Made the rttm_cs variable static for the rtl otto timer driver
(Krzysztof Kozlowski)
- Fixed error return code handling in the sun5i timer driver (Chen Ni)
- Made the timer-of and the mmio code compatible with modules (Daniel
Lezcano)
Link: https://lore.kernel.org/151feae1-39ba-4abd-a9f9-9bff377a2cd8@oss.qualcomm.com
Diffstat (limited to 'drivers/gpu/nova-core')
27 files changed, 879 insertions, 454 deletions
diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig index 20d3e6d0d796..527920f9c4d3 100644 --- a/drivers/gpu/nova-core/Kconfig +++ b/drivers/gpu/nova-core/Kconfig @@ -3,7 +3,7 @@ config NOVA_CORE depends on 64BIT depends on PCI depends on RUST - depends on RUST_FW_LOADER_ABSTRACTIONS + select RUST_FW_LOADER_ABSTRACTIONS select AUXILIARY_BUS default n help 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::<GPU_DMA_BITS>())? }; let bar = Arc::pin_init( - pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")), + pdev.iomap_region_sized::<BAR0_SIZE>(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 ), diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 82c661aef594..37bfee1d0949 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -8,12 +8,14 @@ use hal::FalconHal; use kernel::{ device, - dma::DmaAddress, + dma::{ + DmaAddress, + DmaMask, // + }, io::poll::read_poll_timeout, prelude::*, sync::aref::ARef, time::{ - delay::fsleep, Delta, // }, }; @@ -21,6 +23,7 @@ use kernel::{ use crate::{ dma::DmaObject, driver::Bar0, + falcon::hal::LoadMethod, gpu::Chipset, num::{ FromSafeCast, @@ -237,8 +240,11 @@ impl From<PeregrineCoreSelect> 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, + /// Non-Secure Instruction Memory. + #[expect(unused)] + ImemNonSecure, /// Data Memory. Dmem, } @@ -345,8 +351,12 @@ 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 Non-Secure `IMEM`, + /// used only on Turing and GA100. + fn imem_ns_load_params(&self) -> Option<FalconLoadTarget>; /// Returns the load parameters for `DMEM`. fn dmem_load_params(&self) -> FalconLoadTarget; @@ -388,48 +398,11 @@ impl<E: FalconEngine + 'static> Falcon<E> { 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()) @@ -448,7 +421,6 @@ impl<E: FalconEngine + 'static> Falcon<E> { fw: &F, target_mem: FalconMem, load_offsets: FalconLoadTarget, - sec: bool, ) -> Result { const DMA_LEN: u32 = 256; @@ -457,7 +429,9 @@ impl<E: FalconEngine + 'static> Falcon<E> { // // 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 | FalconMem::ImemNonSecure => { + (load_offsets.src_start, fw.dma_handle()) + } FalconMem::Dmem => ( 0, fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, @@ -466,12 +440,18 @@ impl<E: FalconEngine + 'static> Falcon<E> { 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); } + // 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); @@ -483,11 +463,11 @@ impl<E: FalconEngine + 'static> Falcon<E> { .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(_) => (), @@ -508,8 +488,7 @@ impl<E: FalconEngine + 'static> Falcon<E> { let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() .set_size(DmaTrfCmdSize::Size256B) - .set_imem(target_mem == FalconMem::Imem) - .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`. @@ -536,15 +515,22 @@ impl<E: FalconEngine + 'static> Falcon<E> { } /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. - pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { + fn dma_load<F: FalconFirmware<Target = E>>(&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) .set_mem_type(FalconFbifMemType::Physical) }); - self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_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())?; @@ -651,8 +637,15 @@ impl<E: FalconEngine + 'static> Falcon<E> { /// /// 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) + } + + // Load a firmware image into Falcon memory + pub(crate) fn load<F: FalconFirmware<Target = E>>(&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. diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs index 8dc56a28ad65..89babd5f9325 100644 --- a/drivers/gpu/nova-core/falcon/hal.rs +++ b/drivers/gpu/nova-core/falcon/hal.rs @@ -13,6 +13,16 @@ 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. /// @@ -37,6 +47,19 @@ pub(crate) trait FalconHal<E: FalconEngine>: Send + Sync { /// Program the boot ROM registers prior to starting a secure firmware. fn program_brom(&self, falcon: &Falcon<E>, 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 the method needed to load data into Falcon memory + fn load_method(&self) -> LoadMethod; } /// Returns a boxed falcon HAL adequate for `chipset`. @@ -50,6 +73,9 @@ pub(super) fn falcon_hal<E: FalconEngine + 'static>( use Chipset::*; let hal = match chipset { + TU102 | TU104 | TU106 | TU116 | TU117 => { + KBox::new(tu102::Tu102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>> + } GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => { KBox::new(ga102::Ga102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>> } diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index 69a7a95cac16..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, @@ -52,7 +53,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 +67,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); }; @@ -117,4 +118,42 @@ impl<E: FalconEngine> FalconHal<E> for Ga102<E> { fn program_brom(&self, _falcon: &Falcon<E>, bar: &Bar0, params: &FalconBromParams) -> Result { program_brom_ga102::<E>(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::reset_engine::<E>(bar); + self.reset_wait_mem_scrubbing(bar)?; + + 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 new file mode 100644 index 000000000000..7de6f24cc0a0 --- /dev/null +++ b/drivers/gpu/nova-core/falcon/hal/tu102.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::marker::PhantomData; + +use kernel::{ + io::poll::read_poll_timeout, + prelude::*, + time::Delta, // +}; + +use crate::{ + driver::Bar0, + falcon::{ + hal::LoadMethod, + Falcon, + FalconBromParams, + FalconEngine, // + }, + regs, // +}; + +use super::FalconHal; + +pub(super) struct Tu102<E: FalconEngine>(PhantomData<E>); + +impl<E: FalconEngine> Tu102<E> { + pub(super) fn new() -> Self { + Self(PhantomData) + } +} + +impl<E: FalconEngine> FalconHal<E> for Tu102<E> { + fn select_core(&self, _falcon: &Falcon<E>, _bar: &Bar0) -> Result { + Ok(()) + } + + fn signature_reg_fuse_version( + &self, + _falcon: &Falcon<E>, + _bar: &Bar0, + _engine_id_mask: u16, + _ucode_id: u8, + ) -> Result<u32> { + Ok(0) + } + + fn program_brom(&self, _falcon: &Falcon<E>, _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::reset_engine::<E>(bar); + self.reset_wait_mem_scrubbing(bar)?; + + Ok(()) + } + + fn load_method(&self) -> LoadMethod { + LoadMethod::Pio + } +} 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/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, @@ -46,6 +50,46 @@ fn request_firmware( /// 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)] pub(crate) struct FalconUCodeDescV3 { /// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM. hdr: u32, @@ -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<FalconLoadTarget>; + 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<FalconLoadTarget> { + 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<FalconLoadTarget> { + // 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/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs index f107f753214a..86556cee8e67 100644 --- a/drivers/gpu/nova-core/firmware/booter.rs +++ b/drivers/gpu/nova-core/firmware/booter.rs @@ -251,8 +251,11 @@ impl<'a> FirmwareSignature<BooterFirmware> 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 Non-Secure `IMEM` falcon memory, + // used only on Turing and GA100 + imem_ns_load_target: Option<FalconLoadTarget>, // Load parameters for `DMEM` falcon memory. dmem_load_target: FalconLoadTarget, // BROM falcon parameters. @@ -353,12 +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_load_target: FalconLoadTarget { + imem_sec_load_target: FalconLoadTarget { src_start: app0.offset, - dst_start: 0, + dst_start: imem_sec_dst_start, len: app0.len, }, + imem_ns_load_target, dmem_load_target: FalconLoadTarget { src_start: load_hdr.os_data_offset, dst_start: 0, @@ -371,8 +392,12 @@ 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 imem_ns_load_params(&self) -> Option<FalconLoadTarget> { + self.imem_ns_load_target.clone() } fn dmem_load_params(&self) -> FalconLoadTarget { @@ -384,7 +409,11 @@ impl FalconLoadParams for BooterFirmware { } fn boot_addr(&self) -> u32 { - self.imem_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 + } } } diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index b28e34d279f4..bfb7b06b13d1 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -12,7 +12,6 @@ use core::{ marker::PhantomData, - mem::size_of, ops::Deref, // }; @@ -40,7 +39,7 @@ use crate::{ FalconLoadTarget, // }, firmware::{ - FalconUCodeDescV3, + FalconUCodeDesc, FirmwareDmaObject, FirmwareSignature, Signed, @@ -218,33 +217,29 @@ unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>( /// 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<Self, Signed>, } impl FalconLoadParams for FwsecFirmware { - fn imem_load_params(&self) -> FalconLoadTarget { - FalconLoadTarget { - src_start: 0, - dst_start: self.desc.imem_phys_base, - len: self.desc.imem_load_size, - } + fn imem_sec_load_params(&self) -> FalconLoadTarget { + self.desc.imem_sec_load_params() + } + + fn imem_ns_load_params(&self) -> Option<FalconLoadTarget> { + 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(), } } @@ -268,10 +263,10 @@ impl FalconFirmware for FwsecFirmware { impl FirmwareDmaObject<FwsecFirmware, Unsigned> { fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> { 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) }?; @@ -298,7 +293,7 @@ impl FirmwareDmaObject<FwsecFirmware, Unsigned> { 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(), ) }?; @@ -312,7 +307,7 @@ impl FirmwareDmaObject<FwsecFirmware, Unsigned> { 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(), ) }?; @@ -359,11 +354,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", @@ -397,7 +393,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)? @@ -406,7 +402,7 @@ impl FwsecFirmware { }; Ok(FwsecFirmware { - desc: desc.clone(), + desc, ucode: ucode_signed, }) } @@ -423,7 +419,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/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 0549805282ab..9488a626352f 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -use core::mem::size_of_val; - use kernel::{ device, dma::{ @@ -34,11 +32,11 @@ use crate::{ /// that scheme before nova-core becomes stable, which means this module will eventually be /// removed. mod elf { - use core::mem::size_of; - - use kernel::bindings; - use kernel::str::CStr; - use kernel::transmute::FromBytes; + use kernel::{ + bindings, + prelude::*, + transmute::FromBytes, // + }; /// Newtype to provide a [`FromBytes`] implementation. #[repr(transparent)] @@ -93,10 +91,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. @@ -153,82 +148,93 @@ 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<device::Bound>, chipset: Chipset, - ver: &'b str, - ) -> Result<impl PinInit<Self, Error> + 'a> { - let fw = super::request_firmware(dev, chipset, "gsp", ver)?; + ver: &'a str, + ) -> impl PinInit<Self, Error> + 'a { + pin_init::pin_init_scope(move || { + let firmware = 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(firmware.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 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)?; + 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::<u8>::with_capacity( + fw.iter().count() * core::mem::size_of::<u64>(), + 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::<u8>::with_capacity( + level2.iter().count() * core::mem::size_of::<u64>(), + 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. - let bl = super::request_firmware(dev, chipset, "bootloader", ver)?; - let bootloader = RiscvFirmware::new(dev, &bl)?; + // Level 0 page table data. + let mut level0_data = kvec![0u8; GSP_PAGE_SIZE]?; - 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::<u8>::with_capacity( - fw.iter().count() * core::mem::size_of::<u64>(), - 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::<u8>::with_capacity( - level2.iter().count() * core::mem::size_of::<u64>(), - 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. + // 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()); - // Level 0 page table data. - let mut level0_data = kvec![0u8; GSP_PAGE_SIZE]?; + // Turn the level0 page table into a [`DmaObject`]. + DmaObject::from_data(dev, &level0_data)? + }, + 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", + }; - // 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()); + 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)?; - // Turn the level0 page table into a [`DmaObject`]. - DmaObject::from_data(dev, &level0_data)? - }, - size, - signatures, - bootloader, - })) + RiscvFirmware::new(dev, &bl)? + }, + })) + }) } /// Returns the DMA handle of the radix3 level 0 page table. diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs index 28dfef63657a..4bdd89bd0757 100644 --- a/drivers/gpu/nova-core/firmware/riscv.rs +++ b/drivers/gpu/nova-core/firmware/riscv.rs @@ -3,8 +3,6 @@ //! Support for firmware binaries designed to run on a RISC-V core. Such firmwares files have a //! dedicated header. -use core::mem::size_of; - use kernel::{ device, firmware::Firmware, diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 629c9d2dc994..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)?, @@ -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..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,48 +114,45 @@ pub(crate) struct Gsp { /// Command queue. pub(crate) cmdq: Cmdq, /// RM arguments. - rmargs: CoherentAllocation<GspArgumentsCached>, + rmargs: CoherentAllocation<GspArgumentsPadded>, } impl Gsp { // Creates an in-place initializer for a `Gsp` manager for `pdev`. - pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self, Error>> { - let dev = pdev.as_ref(); - let libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent( - dev, - GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(), - 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::<GspArgumentsCached>::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<device::Bound>) -> impl PinInit<Self, Error> + '_ { + pin_init::pin_init_scope(move || { + let dev = pdev.as_ref(); + + Ok(try_pin_init!(Self { + libos: CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent( + dev, + GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(), + GFP_KERNEL | __GFP_ZERO, + )?, + loginit: LogBuffer::new(dev)?, + logintr: LogBuffer::new(dev)?, + logrm: LogBuffer::new(dev)?, + cmdq: Cmdq::new(dev)?, + rmargs: CoherentAllocation::<GspArgumentsPadded>::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].inner = fw::GspArgumentsCached::new(cmdq))?; + dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; + }, + })) + }) } } diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 54937606b5b0..be427fe26a58 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 ); @@ -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); @@ -186,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, @@ -241,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/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 6f946d14868a..46819a82a51a 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -588,21 +588,23 @@ impl Cmdq { header.length(), ); + let payload_length = header.payload_length(); + // Check that the driver read area is large enough for the message. - if slice_1.len() + slice_2.len() < header.length() { + if slice_1.len() + slice_2.len() < payload_length { return Err(EIO); } // Cut the message slices down to the actual length of the message. - let (slice_1, slice_2) = if slice_1.len() > header.length() { - // PANIC: we checked above that `slice_1` is at least as long as `msg_header.length()`. - (slice_1.split_at(header.length()).0, &slice_2[0..0]) + let (slice_1, slice_2) = if slice_1.len() > payload_length { + // PANIC: we checked above that `slice_1` is at least as long as `payload_length`. + (slice_1.split_at(payload_length).0, &slice_2[0..0]) } else { ( slice_1, // PANIC: we checked above that `slice_1.len() + slice_2.len()` is at least as - // large as `msg_header.length()`. - slice_2.split_at(header.length() - slice_1.len()).0, + // large as `payload_length`. + slice_2.split_at(payload_length - slice_1.len()).0, ) }; @@ -615,7 +617,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/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 0425c65b5d6f..8f270eca33be 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::{ @@ -30,7 +32,6 @@ use crate::{ }, }, sbuffer::SBufferIter, - util, }; /// The `GspSetSystemInfo` command. @@ -142,7 +143,7 @@ impl CommandToGsp for SetRegistry { } /// Message type for GSP initialization done notification. -struct GspInitDone {} +struct GspInitDone; // SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it // trivially has no uninitialized bytes. @@ -151,13 +152,13 @@ unsafe impl FromBytes for GspInitDone {} impl MessageFromGsp for GspInitDone { const FUNCTION: MsgFunction = MsgFunction::GspInitDone; type InitError = Infallible; - type Message = GspInitDone; + type Message = (); fn read( _msg: &Self::Message, _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>, ) -> Result<Self, Self::InitError> { - Ok(GspInitDone {}) + Ok(GspInitDone) } } @@ -205,11 +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> { - util::str_from_null_terminated(&self.gpu_name) + /// 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) + .map_err(GpuNameError::NoNullTerminator)? + .to_str() + .map_err(GpuNameError::InvalidUtf8) } } diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index abffd6beec65..83ff91614e36 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -141,8 +141,8 @@ unsafe impl AsBytes for GspFwWprMeta {} // are valid. unsafe impl FromBytes for GspFwWprMeta {} -type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1; -type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1; +type GspFwWprMetaBootResumeInfo = bindings::GspFwWprMeta__bindgen_ty_1; +type GspFwWprMetaBootInfo = bindings::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1; impl GspFwWprMeta { /// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the @@ -150,8 +150,8 @@ impl GspFwWprMeta { pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self { Self(bindings::GspFwWprMeta { // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified. - magic: r570_144::GSP_FW_WPR_META_MAGIC as u64, - revision: u64::from(r570_144::GSP_FW_WPR_META_REVISION), + magic: bindings::GSP_FW_WPR_META_MAGIC as u64, + revision: u64::from(bindings::GSP_FW_WPR_META_REVISION), sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(), sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size), sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(), @@ -315,19 +315,19 @@ impl From<MsgFunction> for u32 { #[repr(u32)] pub(crate) enum SeqBufOpcode { // Core operation opcodes - CoreReset = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET, - CoreResume = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME, - CoreStart = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START, - CoreWaitForHalt = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT, + CoreReset = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET, + CoreResume = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME, + CoreStart = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START, + CoreWaitForHalt = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT, // Delay opcode - DelayUs = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US, + DelayUs = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US, // Register operation opcodes - RegModify = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY, - RegPoll = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL, - RegStore = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE, - RegWrite = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE, + RegModify = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY, + RegPoll = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL, + RegStore = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE, + RegWrite = bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE, } impl fmt::Display for SeqBufOpcode { @@ -351,25 +351,25 @@ impl TryFrom<u32> for SeqBufOpcode { fn try_from(value: u32) -> Result<SeqBufOpcode> { match value { - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET => { + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET => { Ok(SeqBufOpcode::CoreReset) } - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME => { + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME => { Ok(SeqBufOpcode::CoreResume) } - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START => { + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START => { Ok(SeqBufOpcode::CoreStart) } - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT => { + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT => { Ok(SeqBufOpcode::CoreWaitForHalt) } - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US => Ok(SeqBufOpcode::DelayUs), - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY => { + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US => Ok(SeqBufOpcode::DelayUs), + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY => { Ok(SeqBufOpcode::RegModify) } - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL => Ok(SeqBufOpcode::RegPoll), - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE => Ok(SeqBufOpcode::RegStore), - r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE => Ok(SeqBufOpcode::RegWrite), + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL => Ok(SeqBufOpcode::RegPoll), + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE => Ok(SeqBufOpcode::RegStore), + bindings::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE => Ok(SeqBufOpcode::RegWrite), _ => Err(EINVAL), } } @@ -385,7 +385,7 @@ impl From<SeqBufOpcode> for u32 { /// Wrapper for GSP sequencer register write payload. #[repr(transparent)] #[derive(Copy, Clone)] -pub(crate) struct RegWritePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_WRITE); +pub(crate) struct RegWritePayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_WRITE); impl RegWritePayload { /// Returns the register address. @@ -408,7 +408,7 @@ unsafe impl AsBytes for RegWritePayload {} /// Wrapper for GSP sequencer register modify payload. #[repr(transparent)] #[derive(Copy, Clone)] -pub(crate) struct RegModifyPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY); +pub(crate) struct RegModifyPayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY); impl RegModifyPayload { /// Returns the register address. @@ -436,7 +436,7 @@ unsafe impl AsBytes for RegModifyPayload {} /// Wrapper for GSP sequencer register poll payload. #[repr(transparent)] #[derive(Copy, Clone)] -pub(crate) struct RegPollPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_POLL); +pub(crate) struct RegPollPayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_POLL); impl RegPollPayload { /// Returns the register address. @@ -469,7 +469,7 @@ unsafe impl AsBytes for RegPollPayload {} /// Wrapper for GSP sequencer delay payload. #[repr(transparent)] #[derive(Copy, Clone)] -pub(crate) struct DelayUsPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_DELAY_US); +pub(crate) struct DelayUsPayload(bindings::GSP_SEQ_BUF_PAYLOAD_DELAY_US); impl DelayUsPayload { /// Returns the delay value in microseconds. @@ -487,7 +487,7 @@ unsafe impl AsBytes for DelayUsPayload {} /// Wrapper for GSP sequencer register store payload. #[repr(transparent)] #[derive(Copy, Clone)] -pub(crate) struct RegStorePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_STORE); +pub(crate) struct RegStorePayload(bindings::GSP_SEQ_BUF_PAYLOAD_REG_STORE); impl RegStorePayload { /// Returns the register address. @@ -510,7 +510,7 @@ unsafe impl AsBytes for RegStorePayload {} /// Wrapper for GSP sequencer buffer command. #[repr(transparent)] -pub(crate) struct SequencerBufferCmd(r570_144::GSP_SEQUENCER_BUFFER_CMD); +pub(crate) struct SequencerBufferCmd(bindings::GSP_SEQUENCER_BUFFER_CMD); impl SequencerBufferCmd { /// Returns the opcode as a `SeqBufOpcode` enum, or error if invalid. @@ -612,7 +612,7 @@ unsafe impl AsBytes for SequencerBufferCmd {} /// Wrapper for GSP run CPU sequencer RPC. #[repr(transparent)] -pub(crate) struct RunCpuSequencer(r570_144::rpc_run_cpu_sequencer_v17_00); +pub(crate) struct RunCpuSequencer(bindings::rpc_run_cpu_sequencer_v17_00); impl RunCpuSequencer { /// Returns the command index. @@ -797,13 +797,6 @@ impl bindings::rpc_message_header_v { } } -// SAFETY: We can't derive the Zeroable trait for this binding because the -// procedural macro doesn't support the syntax used by bindgen to create the -// __IncompleteArrayField types. So instead we implement it here, which is safe -// because these are explicitly padded structures only containing types for -// which any bit pattern, including all zeros, is valid. -unsafe impl Zeroable for bindings::rpc_message_header_v {} - /// GSP Message Element. /// /// This is essentially a message header expected to be followed by the message data. @@ -853,11 +846,16 @@ impl GspMsgElement { self.inner.checkSum = checksum; } - /// Returns the total length of the message. + /// Returns the length of the message's payload. + pub(crate) fn payload_length(&self) -> usize { + // `rpc.length` includes the length of the RPC message header. + num::u32_as_usize(self.inner.rpc.length) + .saturating_sub(size_of::<bindings::rpc_message_header_v>()) + } + + /// Returns the total length of the message, message and RPC headers included. pub(crate) fn length(&self) -> usize { - // `rpc.length` includes the length of the GspRpcHeader but not the message header. - size_of::<Self>() - size_of::<bindings::rpc_message_header_v>() - + num::u32_as_usize(self.inner.rpc.length) + size_of::<Self>() + self.payload_length() } // Returns the sequence number of the message. @@ -906,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::<bindings::GSP_ARGUMENTS_CACHED>()], +} + +// 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)] diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144.rs b/drivers/gpu/nova-core/gsp/fw/r570_144.rs index 048234d1a9d1..e99d315ae74c 100644 --- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs +++ b/drivers/gpu/nova-core/gsp/fw/r570_144.rs @@ -24,8 +24,11 @@ unreachable_pub, unsafe_op_in_unsafe_fn )] -use kernel::{ - ffi, - prelude::Zeroable, // -}; +use kernel::ffi; +use pin_init::MaybeZeroable; + include!("r570_144/bindings.rs"); + +// SAFETY: This type has a size of zero, so its inclusion into another type should not affect their +// ability to implement `Zeroable`. +unsafe impl<T> kernel::prelude::Zeroable for __IncompleteArrayField<T> {} diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs index 5bcfbcd1ad22..6d25fe0bffa9 100644 --- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs +++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs @@ -320,11 +320,12 @@ pub const NV_VGPU_MSG_EVENT_RECOVERY_ACTION: _bindgen_ty_3 = 4130; pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4131; pub type _bindgen_ty_3 = ffi::c_uint; #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS { pub totalVFs: u32_, pub firstVfOffset: u32_, pub vfFeatureMask: u32_, + pub __bindgen_padding_0: [u8; 4usize], pub FirstVFBar0Address: u64_, pub FirstVFBar1Address: u64_, pub FirstVFBar2Address: u64_, @@ -340,23 +341,26 @@ pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS { pub bClientRmAllocatedCtxBuffer: u8_, pub bNonPowerOf2ChannelCountSupported: u8_, pub bVfResizableBAR1Supported: u8_, + pub __bindgen_padding_1: [u8; 7usize], } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS { pub BoardID: u32_, pub chipSKU: [ffi::c_char; 9usize], pub chipSKUMod: [ffi::c_char; 5usize], + pub __bindgen_padding_0: [u8; 2usize], pub skuConfigVersion: u32_, pub project: [ffi::c_char; 5usize], pub projectSKU: [ffi::c_char; 5usize], pub CDP: [ffi::c_char; 6usize], pub projectSKUMod: [ffi::c_char; 2usize], + pub __bindgen_padding_1: [u8; 2usize], pub businessCycle: u32_, } pub type NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG = [u8_; 17usize]; #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO { pub base: u64_, pub limit: u64_, @@ -368,13 +372,14 @@ pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO { pub blackList: NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS { pub numFBRegions: u32_, + pub __bindgen_padding_0: [u8; 4usize], pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize], } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, MaybeZeroable)] pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS { pub index: u32_, pub flags: u32_, @@ -391,14 +396,14 @@ impl Default for NV2080_CTRL_GPU_GET_GID_INFO_PARAMS { } } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct DOD_METHOD_DATA { pub status: u32_, pub acpiIdListLen: u32_, pub acpiIdList: [u32_; 16usize], } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct JT_METHOD_DATA { pub status: u32_, pub jtCaps: u32_, @@ -407,14 +412,14 @@ pub struct JT_METHOD_DATA { pub __bindgen_padding_0: u8, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct MUX_METHOD_DATA_ELEMENT { pub acpiId: u32_, pub mode: u32_, pub status: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct MUX_METHOD_DATA { pub tableLen: u32_, pub acpiIdMuxModeTable: [MUX_METHOD_DATA_ELEMENT; 16usize], @@ -422,13 +427,13 @@ pub struct MUX_METHOD_DATA { pub acpiIdMuxStateTable: [MUX_METHOD_DATA_ELEMENT; 16usize], } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct CAPS_METHOD_DATA { pub status: u32_, pub optimusCaps: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct ACPI_METHOD_DATA { pub bValid: u8_, pub __bindgen_padding_0: [u8; 3usize], @@ -438,20 +443,20 @@ pub struct ACPI_METHOD_DATA { pub capsMethodData: CAPS_METHOD_DATA, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS { pub headIndex: u32_, pub maxHResolution: u32_, pub maxVResolution: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS { pub numHeads: u32_, pub maxNumHeads: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct BUSINFO { pub deviceID: u16_, pub vendorID: u16_, @@ -461,7 +466,7 @@ pub struct BUSINFO { pub __bindgen_padding_0: u8, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_VF_INFO { pub totalVFs: u32_, pub firstVFOffset: u32_, @@ -474,34 +479,37 @@ pub struct GSP_VF_INFO { pub __bindgen_padding_0: [u8; 5usize], } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_PCIE_CONFIG_REG { pub linkCap: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct EcidManufacturingInfo { pub ecidLow: u32_, pub ecidHigh: u32_, pub ecidExtended: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct FW_WPR_LAYOUT_OFFSET { pub nonWprHeapOffset: u64_, pub frtsOffset: u64_, } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, MaybeZeroable)] pub struct GspStaticConfigInfo_t { pub grCapsBits: [u8_; 23usize], + pub __bindgen_padding_0: u8, pub gidInfo: NV2080_CTRL_GPU_GET_GID_INFO_PARAMS, pub SKUInfo: NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS, + pub __bindgen_padding_1: [u8; 4usize], pub fbRegionInfoParams: NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS, pub sriovCaps: NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS, pub sriovMaxGfid: u32_, pub engineCaps: [u32_; 3usize], pub poisonFuseEnabled: u8_, + pub __bindgen_padding_2: [u8; 7usize], pub fb_length: u64_, pub fbio_mask: u64_, pub fb_bus_width: u32_, @@ -527,16 +535,20 @@ pub struct GspStaticConfigInfo_t { pub bIsMigSupported: u8_, pub RTD3GC6TotalBoardPower: u16_, pub RTD3GC6PerstDelay: u16_, + pub __bindgen_padding_3: [u8; 2usize], pub bar1PdeBase: u64_, pub bar2PdeBase: u64_, pub bVbiosValid: u8_, + pub __bindgen_padding_4: [u8; 3usize], pub vbiosSubVendor: u32_, pub vbiosSubDevice: u32_, pub bPageRetirementSupported: u8_, pub bSplitVasBetweenServerClientRm: u8_, pub bClRootportNeedsNosnoopWAR: u8_, + pub __bindgen_padding_5: u8, pub displaylessMaxHeads: VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS, pub displaylessMaxResolution: VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS, + pub __bindgen_padding_6: [u8; 4usize], pub displaylessMaxPixels: u64_, pub hInternalClient: u32_, pub hInternalDevice: u32_, @@ -558,7 +570,7 @@ impl Default for GspStaticConfigInfo_t { } } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GspSystemInfo { pub gpuPhysAddr: u64_, pub gpuPhysFbAddr: u64_, @@ -615,7 +627,7 @@ pub struct GspSystemInfo { pub hostPageSize: u64_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct MESSAGE_QUEUE_INIT_ARGUMENTS { pub sharedMemPhysAddr: u64_, pub pageTableEntryCount: u32_, @@ -624,7 +636,7 @@ pub struct MESSAGE_QUEUE_INIT_ARGUMENTS { pub statQueueOffset: u64_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_SR_INIT_ARGUMENTS { pub oldLevel: u32_, pub flags: u32_, @@ -632,7 +644,7 @@ pub struct GSP_SR_INIT_ARGUMENTS { pub __bindgen_padding_0: [u8; 3usize], } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_ARGUMENTS_CACHED { pub messageQueueInitArguments: MESSAGE_QUEUE_INIT_ARGUMENTS, pub srInitArguments: GSP_SR_INIT_ARGUMENTS, @@ -642,13 +654,13 @@ pub struct GSP_ARGUMENTS_CACHED { pub profilerArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_1, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 { pub pa: u64_, pub size: u64_, } #[repr(C)] -#[derive(Copy, Clone, Zeroable)] +#[derive(Copy, Clone, MaybeZeroable)] pub union rpc_message_rpc_union_field_v03_00 { pub spare: u32_, pub cpuRmGfid: u32_, @@ -664,6 +676,7 @@ impl Default for rpc_message_rpc_union_field_v03_00 { } pub type rpc_message_rpc_union_field_v = rpc_message_rpc_union_field_v03_00; #[repr(C)] +#[derive(MaybeZeroable)] pub struct rpc_message_header_v03_00 { pub header_version: u32_, pub signature: u32_, @@ -686,7 +699,7 @@ impl Default for rpc_message_header_v03_00 { } pub type rpc_message_header_v = rpc_message_header_v03_00; #[repr(C)] -#[derive(Copy, Clone, Zeroable)] +#[derive(Copy, Clone, MaybeZeroable)] pub struct GspFwWprMeta { pub magic: u64_, pub revision: u64_, @@ -721,19 +734,19 @@ pub struct GspFwWprMeta { pub verified: u64_, } #[repr(C)] -#[derive(Copy, Clone, Zeroable)] +#[derive(Copy, Clone, MaybeZeroable)] pub union GspFwWprMeta__bindgen_ty_1 { pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1, pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 { pub sysmemAddrOfSignature: u64_, pub sizeOfSignature: u64_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 { pub gspFwHeapFreeListWprOffset: u32_, pub unused0: u32_, @@ -749,13 +762,13 @@ impl Default for GspFwWprMeta__bindgen_ty_1 { } } #[repr(C)] -#[derive(Copy, Clone, Zeroable)] +#[derive(Copy, Clone, MaybeZeroable)] pub union GspFwWprMeta__bindgen_ty_2 { pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1, pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 { pub partitionRpcAddr: u64_, pub partitionRpcRequestOffset: u16_, @@ -767,7 +780,7 @@ pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 { pub lsUcodeVersion: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 { pub partitionRpcPadding: [u32_; 4usize], pub sysmemAddrOfCrashReportQueue: u64_, @@ -802,7 +815,7 @@ pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM: LibosMemoryRegion pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2; pub type LibosMemoryRegionLoc = ffi::c_uint; #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct LibosMemoryRegionInitArgument { pub id8: LibosAddress, pub pa: LibosAddress, @@ -812,7 +825,7 @@ pub struct LibosMemoryRegionInitArgument { pub __bindgen_padding_0: [u8; 6usize], } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct PACKED_REGISTRY_ENTRY { pub nameOffset: u32_, pub type_: u8_, @@ -821,14 +834,14 @@ pub struct PACKED_REGISTRY_ENTRY { pub length: u32_, } #[repr(C)] -#[derive(Debug, Default)] +#[derive(Debug, Default, MaybeZeroable)] pub struct PACKED_REGISTRY_TABLE { pub size: u32_, pub numEntries: u32_, pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct msgqTxHeader { pub version: u32_, pub size: u32_, @@ -840,13 +853,13 @@ pub struct msgqTxHeader { pub entryOff: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone, Zeroable)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct msgqRxHeader { pub readPtr: u32_, } #[repr(C)] #[repr(align(8))] -#[derive(Zeroable)] +#[derive(MaybeZeroable)] pub struct GSP_MSG_QUEUE_ELEMENT { pub authTagBuffer: [u8_; 16usize], pub aadBuffer: [u8_; 16usize], @@ -866,7 +879,7 @@ impl Default for GSP_MSG_QUEUE_ELEMENT { } } #[repr(C)] -#[derive(Debug, Default)] +#[derive(Debug, Default, MaybeZeroable)] pub struct rpc_run_cpu_sequencer_v17_00 { pub bufferSizeDWord: u32_, pub cmdIndex: u32_, @@ -884,20 +897,20 @@ pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT: GSP_SEQ_BUF_ pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME: GSP_SEQ_BUF_OPCODE = 8; pub type GSP_SEQ_BUF_OPCODE = ffi::c_uint; #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_SEQ_BUF_PAYLOAD_REG_WRITE { pub addr: u32_, pub val: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_SEQ_BUF_PAYLOAD_REG_MODIFY { pub addr: u32_, pub mask: u32_, pub val: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL { pub addr: u32_, pub mask: u32_, @@ -906,24 +919,24 @@ pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL { pub error: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_SEQ_BUF_PAYLOAD_DELAY_US { pub val: u32_, } #[repr(C)] -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, MaybeZeroable)] pub struct GSP_SEQ_BUF_PAYLOAD_REG_STORE { pub addr: u32_, pub index: u32_, } #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, MaybeZeroable)] pub struct GSP_SEQUENCER_BUFFER_CMD { pub opCode: GSP_SEQ_BUF_OPCODE, pub payload: GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1, } #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, MaybeZeroable)] pub union GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 { pub regWrite: GSP_SEQ_BUF_PAYLOAD_REG_WRITE, pub regModify: GSP_SEQ_BUF_PAYLOAD_REG_MODIFY, diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index 2d0369c49092..e415a2aa3203 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -2,24 +2,21 @@ //! GSP Sequencer implementation for Pre-hopper GSP boot sequence. -use core::{ - array, - mem::{ - size_of, - size_of_val, // - }, -}; +use core::array; use kernel::{ device, - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + Io, // + }, prelude::*, + sync::aref::ARef, time::{ delay::fsleep, Delta, // }, - transmute::FromBytes, - types::ARef, // + transmute::FromBytes, // }; use crate::{ @@ -121,7 +118,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 +317,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 +379,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 +387,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 +397,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/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 = <LocalModule as kernel::ModuleMetadata>::NAME; diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 82cc6c0790e5..ea0d32f5396c 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -7,15 +7,21 @@ #[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, FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, @@ -306,6 +312,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; }); @@ -325,6 +338,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; }); @@ -349,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<E: FalconEngine>(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; @@ -380,6 +413,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; diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs index fd1a815fa57d..ed624be1f39b 100644 --- a/drivers/gpu/nova-core/regs/macros.rs +++ b/drivers/gpu/nova-core/regs/macros.rs @@ -369,16 +369,18 @@ macro_rules! register { /// Read the register from its address in `io`. #[inline(always)] - pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + pub(crate) fn read<T, I>(io: &T) -> Self where + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, { Self(io.read32($offset)) } /// Write the value contained in `self` to the register address in `io`. #[inline(always)] - pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + pub(crate) fn write<T, I>(self, io: &T) where + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, { io.write32(self.0, $offset) } @@ -386,11 +388,12 @@ macro_rules! register { /// Read the register from its address in `io` and run `f` on its value to obtain a new /// value to write back. #[inline(always)] - pub(crate) fn update<const SIZE: usize, T, F>( + pub(crate) fn update<T, I, F>( io: &T, f: F, ) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, F: ::core::ops::FnOnce(Self) -> Self, { let reg = f(Self::read(io)); @@ -408,12 +411,13 @@ macro_rules! register { /// Read the register from `io`, using the base address provided by `base` and adding /// the register's offset to it. #[inline(always)] - pub(crate) fn read<const SIZE: usize, T, B>( + pub(crate) fn read<T, I, B>( io: &T, #[allow(unused_variables)] base: &B, ) -> Self where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, { const OFFSET: usize = $name::OFFSET; @@ -428,13 +432,14 @@ macro_rules! register { /// Write the value contained in `self` to `io`, using the base address provided by /// `base` and adding the register's offset to it. #[inline(always)] - pub(crate) fn write<const SIZE: usize, T, B>( + pub(crate) fn write<T, I, B>( self, io: &T, #[allow(unused_variables)] base: &B, ) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, { const OFFSET: usize = $name::OFFSET; @@ -449,12 +454,13 @@ macro_rules! register { /// the register's offset to it, then run `f` on its value to obtain a new value to /// write back. #[inline(always)] - pub(crate) fn update<const SIZE: usize, T, B, F>( + pub(crate) fn update<T, I, B, F>( io: &T, base: &B, f: F, ) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, F: ::core::ops::FnOnce(Self) -> Self, { @@ -474,11 +480,12 @@ macro_rules! register { /// Read the array register at index `idx` from its address in `io`. #[inline(always)] - pub(crate) fn read<const SIZE: usize, T>( + pub(crate) fn read<T, I>( io: &T, idx: usize, ) -> Self where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, { build_assert!(idx < Self::SIZE); @@ -490,12 +497,13 @@ macro_rules! register { /// Write the value contained in `self` to the array register with index `idx` in `io`. #[inline(always)] - pub(crate) fn write<const SIZE: usize, T>( + pub(crate) fn write<T, I>( self, io: &T, idx: usize ) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, { build_assert!(idx < Self::SIZE); @@ -507,12 +515,13 @@ macro_rules! register { /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a /// new value to write back. #[inline(always)] - pub(crate) fn update<const SIZE: usize, T, F>( + pub(crate) fn update<T, I, F>( io: &T, idx: usize, f: F, ) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, F: ::core::ops::FnOnce(Self) -> Self, { let reg = f(Self::read(io, idx)); @@ -524,11 +533,12 @@ macro_rules! register { /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_read<const SIZE: usize, T>( + pub(crate) fn try_read<T, I>( io: &T, idx: usize, ) -> ::kernel::error::Result<Self> where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, { if idx < Self::SIZE { Ok(Self::read(io, idx)) @@ -542,12 +552,13 @@ macro_rules! register { /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_write<const SIZE: usize, T>( + pub(crate) fn try_write<T, I>( self, io: &T, idx: usize, ) -> ::kernel::error::Result where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, { if idx < Self::SIZE { Ok(self.write(io, idx)) @@ -562,12 +573,13 @@ macro_rules! register { /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_update<const SIZE: usize, T, F>( + pub(crate) fn try_update<T, I, F>( io: &T, idx: usize, f: F, ) -> ::kernel::error::Result where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, F: ::core::ops::FnOnce(Self) -> Self, { if idx < Self::SIZE { @@ -593,13 +605,14 @@ macro_rules! register { /// Read the array register at index `idx` from `io`, using the base address provided /// by `base` and adding the register's offset to it. #[inline(always)] - pub(crate) fn read<const SIZE: usize, T, B>( + pub(crate) fn read<T, I, B>( io: &T, #[allow(unused_variables)] base: &B, idx: usize, ) -> Self where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, { build_assert!(idx < Self::SIZE); @@ -614,14 +627,15 @@ macro_rules! register { /// Write the value contained in `self` to `io`, using the base address provided by /// `base` and adding the offset of array register `idx` to it. #[inline(always)] - pub(crate) fn write<const SIZE: usize, T, B>( + pub(crate) fn write<T, I, B>( self, io: &T, #[allow(unused_variables)] base: &B, idx: usize ) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, { build_assert!(idx < Self::SIZE); @@ -636,13 +650,14 @@ macro_rules! register { /// by `base` and adding the register's offset to it, then run `f` on its value to /// obtain a new value to write back. #[inline(always)] - pub(crate) fn update<const SIZE: usize, T, B, F>( + pub(crate) fn update<T, I, B, F>( io: &T, base: &B, idx: usize, f: F, ) where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, F: ::core::ops::FnOnce(Self) -> Self, { @@ -656,12 +671,13 @@ macro_rules! register { /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_read<const SIZE: usize, T, B>( + pub(crate) fn try_read<T, I, B>( io: &T, base: &B, idx: usize, ) -> ::kernel::error::Result<Self> where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, { if idx < Self::SIZE { @@ -677,13 +693,14 @@ macro_rules! register { /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_write<const SIZE: usize, T, B>( + pub(crate) fn try_write<T, I, B>( self, io: &T, base: &B, idx: usize, ) -> ::kernel::error::Result where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, { if idx < Self::SIZE { @@ -700,13 +717,14 @@ macro_rules! register { /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_update<const SIZE: usize, T, B, F>( + pub(crate) fn try_update<T, I, B, F>( io: &T, base: &B, idx: usize, f: F, ) -> ::kernel::error::Result where - T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, + T: ::core::ops::Deref<Target = I>, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, B: crate::regs::macros::RegisterBase<$base>, F: ::core::ops::FnOnce(Self) -> Self, { diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs index 64758b7fae56..3a41d224c77a 100644 --- a/drivers/gpu/nova-core/sbuffer.rs +++ b/drivers/gpu/nova-core/sbuffer.rs @@ -2,10 +2,7 @@ use core::ops::Deref; -use kernel::{ - alloc::KVec, - prelude::*, // -}; +use kernel::prelude::*; /// A buffer abstraction for discontiguous byte slices. /// 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()) -} diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index abf423560ff4..3e3fa5b72524 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -6,19 +6,22 @@ use core::convert::TryFrom; use kernel::{ device, + io::Io, prelude::*, ptr::{ Alignable, Alignment, // }, + sync::aref::ARef, transmute::FromBytes, - types::ARef, }; use crate::{ driver::Bar0, firmware::{ fwsec::Bcrt30Rsa3kSignature, + FalconUCodeDesc, + FalconUCodeDescV2, FalconUCodeDescV3, // }, num::FromSafeCast, @@ -790,7 +793,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 })?; @@ -887,11 +890,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 }) } @@ -1003,20 +1001,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<FalconUCodeDesc> { // 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::<FalconUCodeDescV3>() > 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() @@ -1024,33 +1013,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::<FalconUCodeDescV3>` 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::<FalconUCodeDescV3>()) - }) } /// 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 @@ -1066,10 +1056,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::<FalconUCodeDescV2>(), + FalconUCodeDesc::V3(_v3) => core::mem::size_of::<FalconUCodeDescV3>(), + }; // The signatures data follows the descriptor. - let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>(); - 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::<Bcrt30Rsa3kSignature>(); // Make sure the data is within bounds. |
