diff options
| -rw-r--r-- | drivers/gpu/nova-core/firmware.rs | 203 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/firmware/fwsec.rs | 46 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/vbios.rs | 64 |
3 files changed, 249 insertions, 64 deletions
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/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<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_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<FalconLoadTarget> { - // 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<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) }?; @@ -303,7 +294,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(), ) }?; @@ -317,7 +308,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(), ) }?; @@ -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<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() @@ -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::<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 @@ -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::<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. |
