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::{ 9 device, 10 firmware, 11 prelude::*, 12 str::CString, 13 transmute::FromBytes, // 14 }; 15 16 use crate::{ 17 dma::DmaObject, 18 falcon::FalconFirmware, 19 gpu, 20 num::{ 21 FromSafeCast, 22 IntoSafeCast, // 23 }, 24 }; 25 26 pub(crate) mod booter; 27 pub(crate) mod fwsec; 28 pub(crate) mod gsp; 29 pub(crate) mod riscv; 30 31 pub(crate) const FIRMWARE_VERSION: &str = "570.144"; 32 33 /// Requests the GPU firmware `name` suitable for `chipset`, with version `ver`. 34 fn request_firmware( 35 dev: &device::Device, 36 chipset: gpu::Chipset, 37 name: &str, 38 ver: &str, 39 ) -> Result<firmware::Firmware> { 40 let chip_name = chipset.name(); 41 42 CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name}-{ver}.bin")) 43 .and_then(|path| firmware::Firmware::request(&path, dev)) 44 } 45 46 /// Structure used to describe some firmwares, notably FWSEC-FRTS. 47 #[repr(C)] 48 #[derive(Debug, Clone)] 49 pub(crate) struct FalconUCodeDescV3 { 50 /// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM. 51 hdr: u32, 52 /// Stored size of the ucode after the header. 53 stored_size: u32, 54 /// Offset in `DMEM` at which the signature is expected to be found. 55 pub(crate) pkc_data_offset: u32, 56 /// Offset after the code segment at which the app headers are located. 57 pub(crate) interface_offset: u32, 58 /// Base address at which to load the code segment into `IMEM`. 59 pub(crate) imem_phys_base: u32, 60 /// Size in bytes of the code to copy into `IMEM`. 61 pub(crate) imem_load_size: u32, 62 /// Virtual `IMEM` address (i.e. `tag`) at which the code should start. 63 pub(crate) imem_virt_base: u32, 64 /// Base address at which to load the data segment into `DMEM`. 65 pub(crate) dmem_phys_base: u32, 66 /// Size in bytes of the data to copy into `DMEM`. 67 pub(crate) dmem_load_size: u32, 68 /// Mask of the falcon engines on which this firmware can run. 69 pub(crate) engine_id_mask: u16, 70 /// ID of the ucode used to infer a fuse register to validate the signature. 71 pub(crate) ucode_id: u8, 72 /// Number of signatures in this firmware. 73 pub(crate) signature_count: u8, 74 /// Versions of the signatures, used to infer a valid signature to use. 75 pub(crate) signature_versions: u16, 76 _reserved: u16, 77 } 78 79 impl FalconUCodeDescV3 { 80 /// Returns the size in bytes of the header. 81 pub(crate) fn size(&self) -> usize { 82 const HDR_SIZE_SHIFT: u32 = 16; 83 const HDR_SIZE_MASK: u32 = 0xffff0000; 84 85 ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast() 86 } 87 } 88 89 /// Trait implemented by types defining the signed state of a firmware. 90 trait SignedState {} 91 92 /// Type indicating that the firmware must be signed before it can be used. 93 struct Unsigned; 94 impl SignedState for Unsigned {} 95 96 /// Type indicating that the firmware is signed and ready to be loaded. 97 struct Signed; 98 impl SignedState for Signed {} 99 100 /// A [`DmaObject`] containing a specific microcode ready to be loaded into a falcon. 101 /// 102 /// This is module-local and meant for sub-modules to use internally. 103 /// 104 /// After construction, a firmware is [`Unsigned`], and must generally be patched with a signature 105 /// before it can be loaded (with an exception for development hardware). The 106 /// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the 107 /// firmware to its [`Signed`] state. 108 struct FirmwareDmaObject<F: FalconFirmware, S: SignedState>(DmaObject, PhantomData<(F, S)>); 109 110 /// Trait for signatures to be patched directly into a given firmware. 111 /// 112 /// This is module-local and meant for sub-modules to use internally. 113 trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {} 114 115 impl<F: FalconFirmware> FirmwareDmaObject<F, Unsigned> { 116 /// Patches the firmware at offset `sig_base_img` with `signature`. 117 fn patch_signature<S: FirmwareSignature<F>>( 118 mut self, 119 signature: &S, 120 sig_base_img: usize, 121 ) -> Result<FirmwareDmaObject<F, Signed>> { 122 let signature_bytes = signature.as_ref(); 123 if sig_base_img + signature_bytes.len() > self.0.size() { 124 return Err(EINVAL); 125 } 126 127 // SAFETY: We are the only user of this object, so there cannot be any race. 128 let dst = unsafe { self.0.start_ptr_mut().add(sig_base_img) }; 129 130 // SAFETY: `signature` and `dst` are valid, properly aligned, and do not overlap. 131 unsafe { 132 core::ptr::copy_nonoverlapping(signature_bytes.as_ptr(), dst, signature_bytes.len()) 133 }; 134 135 Ok(FirmwareDmaObject(self.0, PhantomData)) 136 } 137 138 /// Mark the firmware as signed without patching it. 139 /// 140 /// This method is used to explicitly confirm that we do not need to sign the firmware, while 141 /// allowing us to continue as if it was. This is typically only needed for development 142 /// hardware. 143 fn no_patch_signature(self) -> FirmwareDmaObject<F, Signed> { 144 FirmwareDmaObject(self.0, PhantomData) 145 } 146 } 147 148 /// Header common to most firmware files. 149 #[repr(C)] 150 #[derive(Debug, Clone)] 151 struct BinHdr { 152 /// Magic number, must be `0x10de`. 153 bin_magic: u32, 154 /// Version of the header. 155 bin_ver: u32, 156 /// Size in bytes of the binary (to be ignored). 157 bin_size: u32, 158 /// Offset of the start of the application-specific header. 159 header_offset: u32, 160 /// Offset of the start of the data payload. 161 data_offset: u32, 162 /// Size in bytes of the data payload. 163 data_size: u32, 164 } 165 166 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. 167 unsafe impl FromBytes for BinHdr {} 168 169 // A firmware blob starting with a `BinHdr`. 170 struct BinFirmware<'a> { 171 hdr: BinHdr, 172 fw: &'a [u8], 173 } 174 175 impl<'a> BinFirmware<'a> { 176 /// Interpret `fw` as a firmware image starting with a [`BinHdr`], and returns the 177 /// corresponding [`BinFirmware`] that can be used to extract its payload. 178 fn new(fw: &'a firmware::Firmware) -> Result<Self> { 179 const BIN_MAGIC: u32 = 0x10de; 180 let fw = fw.data(); 181 182 fw.get(0..size_of::<BinHdr>()) 183 // Extract header. 184 .and_then(BinHdr::from_bytes_copy) 185 // Validate header. 186 .and_then(|hdr| { 187 if hdr.bin_magic == BIN_MAGIC { 188 Some(hdr) 189 } else { 190 None 191 } 192 }) 193 .map(|hdr| Self { hdr, fw }) 194 .ok_or(EINVAL) 195 } 196 197 /// Returns the data payload of the firmware, or `None` if the data range is out of bounds of 198 /// the firmware image. 199 fn data(&self) -> Option<&[u8]> { 200 let fw_start = usize::from_safe_cast(self.hdr.data_offset); 201 let fw_size = usize::from_safe_cast(self.hdr.data_size); 202 203 self.fw.get(fw_start..fw_start + fw_size) 204 } 205 } 206 207 pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>); 208 209 impl<const N: usize> ModInfoBuilder<N> { 210 const fn make_entry_file(self, chipset: &str, fw: &str) -> Self { 211 ModInfoBuilder( 212 self.0 213 .new_entry() 214 .push("nvidia/") 215 .push(chipset) 216 .push("/gsp/") 217 .push(fw) 218 .push("-") 219 .push(FIRMWARE_VERSION) 220 .push(".bin"), 221 ) 222 } 223 224 const fn make_entry_chipset(self, chipset: &str) -> Self { 225 self.make_entry_file(chipset, "booter_load") 226 .make_entry_file(chipset, "booter_unload") 227 .make_entry_file(chipset, "bootloader") 228 .make_entry_file(chipset, "gsp") 229 } 230 231 pub(crate) const fn create( 232 module_name: &'static kernel::str::CStr, 233 ) -> firmware::ModInfoBuilder<N> { 234 let mut this = Self(firmware::ModInfoBuilder::new(module_name)); 235 let mut i = 0; 236 237 while i < gpu::Chipset::ALL.len() { 238 this = this.make_entry_chipset(gpu::Chipset::ALL[i].name()); 239 i += 1; 240 } 241 242 this.0 243 } 244 } 245