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