1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Contains structures and functions dedicated to the parsing, building and patching of firmwares 4 //! to be loaded into a given execution unit. 5 6 use core::marker::PhantomData; 7 8 use kernel::device; 9 use kernel::firmware; 10 use kernel::prelude::*; 11 use kernel::str::CString; 12 13 use crate::dma::DmaObject; 14 use crate::falcon::FalconFirmware; 15 use crate::gpu; 16 use crate::gpu::Chipset; 17 18 pub(crate) mod fwsec; 19 20 pub(crate) const FIRMWARE_VERSION: &str = "535.113.01"; 21 22 /// Structure encapsulating the firmware blobs required for the GPU to operate. 23 #[expect(dead_code)] 24 pub(crate) struct Firmware { 25 booter_load: firmware::Firmware, 26 booter_unload: firmware::Firmware, 27 bootloader: firmware::Firmware, 28 gsp: firmware::Firmware, 29 } 30 31 impl Firmware { new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware>32 pub(crate) fn new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware> { 33 let mut chip_name = CString::try_from_fmt(fmt!("{chipset}"))?; 34 chip_name.make_ascii_lowercase(); 35 let chip_name = &*chip_name; 36 37 let request = |name_| { 38 CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name_}-{ver}.bin")) 39 .and_then(|path| firmware::Firmware::request(&path, dev)) 40 }; 41 42 Ok(Firmware { 43 booter_load: request("booter_load")?, 44 booter_unload: request("booter_unload")?, 45 bootloader: request("bootloader")?, 46 gsp: request("gsp")?, 47 }) 48 } 49 } 50 51 /// Structure used to describe some firmwares, notably FWSEC-FRTS. 52 #[repr(C)] 53 #[derive(Debug, Clone)] 54 pub(crate) struct FalconUCodeDescV3 { 55 /// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM. 56 hdr: u32, 57 /// Stored size of the ucode after the header. 58 stored_size: u32, 59 /// Offset in `DMEM` at which the signature is expected to be found. 60 pub(crate) pkc_data_offset: u32, 61 /// Offset after the code segment at which the app headers are located. 62 pub(crate) interface_offset: u32, 63 /// Base address at which to load the code segment into `IMEM`. 64 pub(crate) imem_phys_base: u32, 65 /// Size in bytes of the code to copy into `IMEM`. 66 pub(crate) imem_load_size: u32, 67 /// Virtual `IMEM` address (i.e. `tag`) at which the code should start. 68 pub(crate) imem_virt_base: u32, 69 /// Base address at which to load the data segment into `DMEM`. 70 pub(crate) dmem_phys_base: u32, 71 /// Size in bytes of the data to copy into `DMEM`. 72 pub(crate) dmem_load_size: u32, 73 /// Mask of the falcon engines on which this firmware can run. 74 pub(crate) engine_id_mask: u16, 75 /// ID of the ucode used to infer a fuse register to validate the signature. 76 pub(crate) ucode_id: u8, 77 /// Number of signatures in this firmware. 78 pub(crate) signature_count: u8, 79 /// Versions of the signatures, used to infer a valid signature to use. 80 pub(crate) signature_versions: u16, 81 _reserved: u16, 82 } 83 84 impl FalconUCodeDescV3 { 85 /// Returns the size in bytes of the header. size(&self) -> usize86 pub(crate) fn size(&self) -> usize { 87 const HDR_SIZE_SHIFT: u32 = 16; 88 const HDR_SIZE_MASK: u32 = 0xffff0000; 89 90 ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT) as usize 91 } 92 } 93 94 /// Trait implemented by types defining the signed state of a firmware. 95 trait SignedState {} 96 97 /// Type indicating that the firmware must be signed before it can be used. 98 struct Unsigned; 99 impl SignedState for Unsigned {} 100 101 /// Type indicating that the firmware is signed and ready to be loaded. 102 struct Signed; 103 impl SignedState for Signed {} 104 105 /// A [`DmaObject`] containing a specific microcode ready to be loaded into a falcon. 106 /// 107 /// This is module-local and meant for sub-modules to use internally. 108 /// 109 /// After construction, a firmware is [`Unsigned`], and must generally be patched with a signature 110 /// before it can be loaded (with an exception for development hardware). The 111 /// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the 112 /// firmware to its [`Signed`] state. 113 struct FirmwareDmaObject<F: FalconFirmware, S: SignedState>(DmaObject, PhantomData<(F, S)>); 114 115 /// Trait for signatures to be patched directly into a given firmware. 116 /// 117 /// This is module-local and meant for sub-modules to use internally. 118 trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {} 119 120 impl<F: FalconFirmware> FirmwareDmaObject<F, Unsigned> { 121 /// Patches the firmware at offset `sig_base_img` with `signature`. patch_signature<S: FirmwareSignature<F>>( mut self, signature: &S, sig_base_img: usize, ) -> Result<FirmwareDmaObject<F, Signed>>122 fn patch_signature<S: FirmwareSignature<F>>( 123 mut self, 124 signature: &S, 125 sig_base_img: usize, 126 ) -> Result<FirmwareDmaObject<F, Signed>> { 127 let signature_bytes = signature.as_ref(); 128 if sig_base_img + signature_bytes.len() > self.0.size() { 129 return Err(EINVAL); 130 } 131 132 // SAFETY: We are the only user of this object, so there cannot be any race. 133 let dst = unsafe { self.0.start_ptr_mut().add(sig_base_img) }; 134 135 // SAFETY: `signature` and `dst` are valid, properly aligned, and do not overlap. 136 unsafe { 137 core::ptr::copy_nonoverlapping(signature_bytes.as_ptr(), dst, signature_bytes.len()) 138 }; 139 140 Ok(FirmwareDmaObject(self.0, PhantomData)) 141 } 142 143 /// Mark the firmware as signed without patching it. 144 /// 145 /// This method is used to explicitly confirm that we do not need to sign the firmware, while 146 /// allowing us to continue as if it was. This is typically only needed for development 147 /// hardware. no_patch_signature(self) -> FirmwareDmaObject<F, Signed>148 fn no_patch_signature(self) -> FirmwareDmaObject<F, Signed> { 149 FirmwareDmaObject(self.0, PhantomData) 150 } 151 } 152 153 pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>); 154 155 impl<const N: usize> ModInfoBuilder<N> { make_entry_file(self, chipset: &str, fw: &str) -> Self156 const fn make_entry_file(self, chipset: &str, fw: &str) -> Self { 157 ModInfoBuilder( 158 self.0 159 .new_entry() 160 .push("nvidia/") 161 .push(chipset) 162 .push("/gsp/") 163 .push(fw) 164 .push("-") 165 .push(FIRMWARE_VERSION) 166 .push(".bin"), 167 ) 168 } 169 make_entry_chipset(self, chipset: &str) -> Self170 const fn make_entry_chipset(self, chipset: &str) -> Self { 171 self.make_entry_file(chipset, "booter_load") 172 .make_entry_file(chipset, "booter_unload") 173 .make_entry_file(chipset, "bootloader") 174 .make_entry_file(chipset, "gsp") 175 } 176 create( module_name: &'static kernel::str::CStr, ) -> firmware::ModInfoBuilder<N>177 pub(crate) const fn create( 178 module_name: &'static kernel::str::CStr, 179 ) -> firmware::ModInfoBuilder<N> { 180 let mut this = Self(firmware::ModInfoBuilder::new(module_name)); 181 let mut i = 0; 182 183 while i < gpu::Chipset::NAMES.len() { 184 this = this.make_entry_chipset(gpu::Chipset::NAMES[i]); 185 i += 1; 186 } 187 188 this.0 189 } 190 } 191