summaryrefslogtreecommitdiff
path: root/drivers/gpu/nova-core/firmware/fwsec.rs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nova-core/firmware/fwsec.rs')
-rw-r--r--drivers/gpu/nova-core/firmware/fwsec.rs188
1 files changed, 105 insertions, 83 deletions
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index 8edbb5c0572c..b28e34d279f4 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -10,20 +10,48 @@
//! - The command to be run, as this firmware can perform several tasks ;
//! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.
-use core::marker::PhantomData;
-use core::mem::{align_of, size_of};
-use core::ops::Deref;
-
-use kernel::device::{self, Device};
-use kernel::prelude::*;
-use kernel::transmute::FromBytes;
-
-use crate::dma::DmaObject;
-use crate::driver::Bar0;
-use crate::falcon::gsp::Gsp;
-use crate::falcon::{Falcon, FalconBromParams, FalconFirmware, FalconLoadParams, FalconLoadTarget};
-use crate::firmware::{FalconUCodeDescV3, FirmwareDmaObject, FirmwareSignature, Signed, Unsigned};
-use crate::vbios::Vbios;
+use core::{
+ marker::PhantomData,
+ mem::size_of,
+ ops::Deref, //
+};
+
+use kernel::{
+ device::{
+ self,
+ Device, //
+ },
+ prelude::*,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ dma::DmaObject,
+ driver::Bar0,
+ falcon::{
+ gsp::Gsp,
+ Falcon,
+ FalconBromParams,
+ FalconFirmware,
+ FalconLoadParams,
+ FalconLoadTarget, //
+ },
+ firmware::{
+ FalconUCodeDescV3,
+ FirmwareDmaObject,
+ FirmwareSignature,
+ Signed,
+ Unsigned, //
+ },
+ num::{
+ FromSafeCast,
+ IntoSafeCast, //
+ },
+ vbios::Vbios,
+};
const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;
@@ -35,7 +63,7 @@ struct FalconAppifHdrV1 {
entry_size: u8,
entry_count: u8,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FalconAppifHdrV1 {}
#[repr(C, packed)]
@@ -44,7 +72,7 @@ struct FalconAppifV1 {
id: u32,
dmem_base: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FalconAppifV1 {}
#[derive(Debug)]
@@ -68,8 +96,10 @@ struct FalconAppifDmemmapperV3 {
ucode_cmd_mask1: u32,
multi_tgt_tbl: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FalconAppifDmemmapperV3 {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for FalconAppifDmemmapperV3 {}
#[derive(Debug)]
#[repr(C, packed)]
@@ -80,8 +110,10 @@ struct ReadVbios {
size: u32,
flags: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for ReadVbios {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for ReadVbios {}
#[derive(Debug)]
#[repr(C, packed)]
@@ -92,8 +124,10 @@ struct FrtsRegion {
size: u32,
ftype: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FrtsRegion {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for FrtsRegion {}
const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;
@@ -102,8 +136,10 @@ struct FrtsCmd {
read_vbios: ReadVbios,
frts_region: FrtsRegion,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FrtsCmd {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for FrtsCmd {}
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19;
@@ -147,26 +183,15 @@ impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}
///
/// # Safety
///
-/// Callers must ensure that the region of memory returned is not written for as long as the
-/// returned reference is alive.
-///
-/// TODO[TRSM][COHA]: Remove this and `transmute_mut` once `CoherentAllocation::as_slice` is
-/// available and we have a way to transmute objects implementing FromBytes, e.g.:
-/// https://lore.kernel.org/lkml/20250330234039.29814-1-christiansantoslima21@gmail.com/
-unsafe fn transmute<'a, 'b, T: Sized + FromBytes>(
- fw: &'a DmaObject,
- offset: usize,
-) -> Result<&'b T> {
- if offset + size_of::<T>() > fw.size() {
- return Err(EINVAL);
- }
- if (fw.start_ptr() as usize + offset) % align_of::<T>() != 0 {
- return Err(EINVAL);
- }
-
- // SAFETY: we have checked that the pointer is properly aligned that its pointed memory is
- // large enough the contains an instance of `T`, which implements `FromBytes`.
- Ok(unsafe { &*(fw.start_ptr().add(offset).cast::<T>()) })
+/// * Callers must ensure that the device does not read/write to/from memory while the returned
+/// reference is live.
+/// * Callers must ensure that this call does not race with a write to the same region while
+/// the returned reference is live.
+unsafe fn transmute<T: Sized + FromBytes>(fw: &DmaObject, offset: usize) -> Result<&T> {
+ // SAFETY: The safety requirements of the function guarantee the device won't read
+ // or write to memory while the reference is alive and that this call won't race
+ // with writes to the same memory region.
+ T::from_bytes(unsafe { fw.as_slice(offset, size_of::<T>())? }).ok_or(EINVAL)
}
/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must
@@ -174,22 +199,18 @@ unsafe fn transmute<'a, 'b, T: Sized + FromBytes>(
///
/// # Safety
///
-/// Callers must ensure that the region of memory returned is not read or written for as long as
-/// the returned reference is alive.
-unsafe fn transmute_mut<'a, 'b, T: Sized + FromBytes>(
- fw: &'a mut DmaObject,
+/// * Callers must ensure that the device does not read/write to/from memory while the returned
+/// slice is live.
+/// * Callers must ensure that this call does not race with a read or write to the same region
+/// while the returned slice is live.
+unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>(
+ fw: &mut DmaObject,
offset: usize,
-) -> Result<&'b mut T> {
- if offset + size_of::<T>() > fw.size() {
- return Err(EINVAL);
- }
- if (fw.start_ptr_mut() as usize + offset) % align_of::<T>() != 0 {
- return Err(EINVAL);
- }
-
- // SAFETY: we have checked that the pointer is properly aligned that its pointed memory is
- // large enough the contains an instance of `T`, which implements `FromBytes`.
- Ok(unsafe { &mut *(fw.start_ptr_mut().add(offset).cast::<T>()) })
+) -> Result<&mut T> {
+ // SAFETY: The safety requirements of the function guarantee the device won't read
+ // or write to memory while the reference is alive and that this call won't race
+ // with writes or reads to the same memory region.
+ T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::<T>())? }).ok_or(EINVAL)
}
/// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon.
@@ -250,7 +271,7 @@ impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
let ucode = bios.fwsec_image().ucode(desc)?;
let mut dma_object = DmaObject::from_data(dev, ucode)?;
- let hdr_offset = (desc.imem_load_size + desc.interface_offset) as usize;
+ 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) }?;
@@ -259,61 +280,62 @@ impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
}
// Find the DMEM mapper section in the firmware.
- for i in 0..hdr.entry_count as usize {
- let app: &FalconAppifV1 =
+ for i in 0..usize::from(hdr.entry_count) {
// SAFETY: we have exclusive access to `dma_object`.
- unsafe {
+ let app: &FalconAppifV1 = unsafe {
transmute(
&dma_object,
- hdr_offset + hdr.header_size as usize + i * hdr.entry_size as usize
+ hdr_offset + usize::from(hdr.header_size) + i * usize::from(hdr.entry_size),
)
}?;
if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER {
continue;
}
+ let dmem_base = app.dmem_base;
// SAFETY: we have exclusive access to `dma_object`.
let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe {
transmute_mut(
&mut dma_object,
- (desc.imem_load_size + app.dmem_base) as usize,
+ (desc.imem_load_size + dmem_base).into_safe_cast(),
)
}?;
+ dmem_mapper.init_cmd = match cmd {
+ FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS,
+ FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB,
+ };
+ let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset;
+
// SAFETY: we have exclusive access to `dma_object`.
let frts_cmd: &mut FrtsCmd = unsafe {
transmute_mut(
&mut dma_object,
- (desc.imem_load_size + dmem_mapper.cmd_in_buffer_offset) as usize,
+ (desc.imem_load_size + cmd_in_buffer_offset).into_safe_cast(),
)
}?;
frts_cmd.read_vbios = ReadVbios {
ver: 1,
- hdr: size_of::<ReadVbios>() as u32,
+ hdr: u32::try_from(size_of::<ReadVbios>())?,
addr: 0,
size: 0,
flags: 2,
};
-
- dmem_mapper.init_cmd = match cmd {
- FwsecCommand::Frts {
- frts_addr,
- frts_size,
- } => {
- frts_cmd.frts_region = FrtsRegion {
- ver: 1,
- hdr: size_of::<FrtsRegion>() as u32,
- addr: (frts_addr >> 12) as u32,
- size: (frts_size >> 12) as u32,
- ftype: NVFW_FRTS_CMD_REGION_TYPE_FB,
- };
-
- NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS
- }
- FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB,
- };
+ if let FwsecCommand::Frts {
+ frts_addr,
+ frts_size,
+ } = cmd
+ {
+ frts_cmd.frts_region = FrtsRegion {
+ ver: 1,
+ hdr: u32::try_from(size_of::<FrtsRegion>())?,
+ addr: u32::try_from(frts_addr >> 12)?,
+ size: u32::try_from(frts_size >> 12)?,
+ ftype: NVFW_FRTS_CMD_REGION_TYPE_FB,
+ };
+ }
// Return early as we found and patched the DMEMMAPPER region.
return Ok(Self(dma_object, PhantomData));
@@ -338,7 +360,7 @@ impl FwsecFirmware {
// Patch signature if needed.
let desc = bios.fwsec_image().header()?;
let ucode_signed = if desc.signature_count != 0 {
- let sig_base_img = (desc.imem_load_size + desc.pkc_data_offset) as usize;
+ 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)?;
@@ -369,7 +391,7 @@ impl FwsecFirmware {
// Mask of the bits of `desc_sig_versions` to preserve.
let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1);
- (desc_sig_versions & reg_fuse_version_mask).count_ones() as usize
+ usize::from_safe_cast((desc_sig_versions & reg_fuse_version_mask).count_ones())
};
dev_dbg!(dev, "patching signature with index {}\n", signature_idx);