1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Bootloader support for the FWSEC firmware. 4 //! 5 //! On Turing, the FWSEC firmware is not loaded directly, but is instead loaded through a small 6 //! bootloader program that performs the required DMA operations. This bootloader itself needs to 7 //! be loaded using PIO. 8 9 use kernel::{ 10 alloc::KVec, 11 device::{ 12 self, 13 Device, // 14 }, 15 dma::Coherent, 16 io::{ 17 register::WithBase, // 18 Io, 19 }, 20 prelude::*, 21 ptr::{ 22 Alignable, 23 Alignment, // 24 }, 25 sizes, 26 transmute::{ 27 AsBytes, 28 FromBytes, // 29 }, 30 }; 31 32 use crate::{ 33 driver::Bar0, 34 falcon::{ 35 self, 36 gsp::Gsp, 37 Falcon, 38 FalconBromParams, 39 FalconDmaLoadable, 40 FalconFbifMemType, 41 FalconFbifTarget, 42 FalconFirmware, 43 FalconPioDmemLoadTarget, 44 FalconPioImemLoadTarget, 45 FalconPioLoadable, // 46 }, 47 firmware::{ 48 fwsec::FwsecFirmware, 49 request_firmware, 50 BinHdr, 51 FIRMWARE_VERSION, // 52 }, 53 gpu::Chipset, 54 num::FromSafeCast, 55 regs, 56 }; 57 58 /// Descriptor used by RM to figure out the requirements of the boot loader. 59 /// 60 /// Most of its fields appear to be legacy and carry incorrect values, so they are left unused. 61 #[repr(C)] 62 #[derive(Debug, Clone)] 63 struct BootloaderDesc { 64 /// Starting tag of bootloader. 65 start_tag: u32, 66 /// DMEM load offset - unused here as we always load at offset `0`. 67 _dmem_load_off: u32, 68 /// Offset of code section in the image. Unused as there is only one section in the bootloader 69 /// binary. 70 _code_off: u32, 71 /// Size of code section in the image. 72 code_size: u32, 73 /// Offset of data section in the image. Unused as we build the data section ourselves. 74 _data_off: u32, 75 /// Size of data section in the image. Unused as we build the data section ourselves. 76 _data_size: u32, 77 } 78 // SAFETY: any byte sequence is valid for this struct. 79 unsafe impl FromBytes for BootloaderDesc {} 80 81 /// Structure used by the boot-loader to load the rest of the code. 82 /// 83 /// This has to be filled by the GPU driver and copied into DMEM at offset 84 /// [`BootloaderDesc.dmem_load_off`]. 85 #[repr(C, packed)] 86 #[derive(Debug, Clone)] 87 struct BootloaderDmemDescV2 { 88 /// Reserved, should always be first element. 89 reserved: [u32; 4], 90 /// 16B signature for secure code, 0s if no secure code. 91 signature: [u32; 4], 92 /// DMA context used by the bootloader while loading code/data. 93 ctx_dma: u32, 94 /// 256B-aligned physical FB address where code is located. 95 code_dma_base: u64, 96 /// Offset from `code_dma_base` where the non-secure code is located. 97 /// 98 /// Also used as destination IMEM offset of non-secure code as the DMA firmware object is 99 /// expected to be a mirror image of its loaded state. 100 /// 101 /// Must be multiple of 256. 102 non_sec_code_off: u32, 103 /// Size of the non-secure code part. 104 non_sec_code_size: u32, 105 /// Offset from `code_dma_base` where the secure code is located (must be multiple of 256). 106 /// 107 /// Also used as destination IMEM offset of secure code as the DMA firmware object is expected 108 /// to be a mirror image of its loaded state. 109 /// 110 /// Must be multiple of 256. 111 sec_code_off: u32, 112 /// Size of the secure code part. 113 sec_code_size: u32, 114 /// Code entry point invoked by the bootloader after code is loaded. 115 code_entry_point: u32, 116 /// 256B-aligned physical FB address where data is located. 117 data_dma_base: u64, 118 /// Size of data block (should be multiple of 256B). 119 data_size: u32, 120 /// Number of arguments to be passed to the target firmware being loaded. 121 argc: u32, 122 /// Arguments to be passed to the target firmware being loaded. 123 argv: u32, 124 } 125 // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 126 unsafe impl AsBytes for BootloaderDmemDescV2 {} 127 128 /// Wrapper for [`FwsecFirmware`] that includes the bootloader performing the actual load 129 /// operation. 130 pub(crate) struct FwsecFirmwareWithBl { 131 /// DMA object the bootloader will copy the firmware from. 132 _firmware_dma: Coherent<[u8]>, 133 /// Code of the bootloader to be loaded into non-secure IMEM. 134 ucode: KVec<u8>, 135 /// Descriptor to be loaded into DMEM for the bootloader to read. 136 dmem_desc: BootloaderDmemDescV2, 137 /// Range-validated start offset of the firmware code in IMEM. 138 imem_dst_start: u16, 139 /// BROM parameters of the loaded firmware. 140 brom_params: FalconBromParams, 141 /// Range-validated `desc.start_tag`. 142 start_tag: u16, 143 } 144 145 impl FwsecFirmwareWithBl { 146 /// Loads the bootloader firmware for `dev` and `chipset`, and wrap `firmware` so it can be 147 /// loaded using it. 148 pub(crate) fn new( 149 firmware: FwsecFirmware, 150 dev: &Device<device::Bound>, 151 chipset: Chipset, 152 ) -> Result<Self> { 153 let fw = request_firmware(dev, chipset, "gen_bootloader", FIRMWARE_VERSION)?; 154 let hdr = fw 155 .data() 156 .get(0..size_of::<BinHdr>()) 157 .and_then(BinHdr::from_bytes_copy) 158 .ok_or(EINVAL)?; 159 160 let desc = { 161 let desc_offset = usize::from_safe_cast(hdr.header_offset); 162 163 fw.data() 164 .get(desc_offset..) 165 .and_then(BootloaderDesc::from_bytes_copy_prefix) 166 .ok_or(EINVAL)? 167 .0 168 }; 169 170 let ucode = { 171 let ucode_start = usize::from_safe_cast(hdr.data_offset); 172 let code_size = usize::from_safe_cast(desc.code_size); 173 // Align to falcon block size (256 bytes). 174 let aligned_code_size = code_size 175 .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>()) 176 .ok_or(EINVAL)?; 177 178 let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?; 179 ucode.extend_from_slice( 180 fw.data() 181 .get(ucode_start..ucode_start + code_size) 182 .ok_or(EINVAL)?, 183 GFP_KERNEL, 184 )?; 185 ucode.resize(aligned_code_size, 0, GFP_KERNEL)?; 186 187 ucode 188 }; 189 190 // `BootloaderDmemDescV2` expects the source to be a mirror image of the destination and 191 // uses the same offset parameter for both. 192 // 193 // Thus, the start of the source object needs to be padded with the difference between the 194 // destination and source offsets. 195 // 196 // In practice, this is expected to always be zero but is required for code correctness. 197 let (align_padding, firmware_dma) = { 198 let align_padding = { 199 let imem_sec = firmware.imem_sec_load_params(); 200 201 imem_sec 202 .dst_start 203 .checked_sub(imem_sec.src_start) 204 .map(usize::from_safe_cast) 205 .ok_or(EOVERFLOW)? 206 }; 207 208 let mut firmware_obj = KVVec::new(); 209 firmware_obj.extend_with(align_padding, 0u8, GFP_KERNEL)?; 210 firmware_obj.extend_from_slice(firmware.ucode.0.as_slice(), GFP_KERNEL)?; 211 212 ( 213 align_padding, 214 Coherent::from_slice(dev, firmware_obj.as_slice(), GFP_KERNEL)?, 215 ) 216 }; 217 218 let dmem_desc = { 219 // Bootloader payload is in non-coherent system memory. 220 const FALCON_DMAIDX_PHYS_SYS_NCOH: u32 = 4; 221 222 let imem_sec = firmware.imem_sec_load_params(); 223 let imem_ns = firmware.imem_ns_load_params().ok_or(EINVAL)?; 224 let dmem = firmware.dmem_load_params(); 225 226 // The bootloader does not have a data destination offset field and copies the data at 227 // the start of DMEM, so it can only be used if the destination offset of the firmware 228 // is 0. 229 if dmem.dst_start != 0 { 230 return Err(EINVAL); 231 } 232 233 BootloaderDmemDescV2 { 234 reserved: [0; 4], 235 signature: [0; 4], 236 ctx_dma: FALCON_DMAIDX_PHYS_SYS_NCOH, 237 code_dma_base: firmware_dma.dma_handle(), 238 // `dst_start` is also valid as the source offset since the firmware DMA object is 239 // a mirror image of the target IMEM layout. 240 non_sec_code_off: imem_ns.dst_start, 241 non_sec_code_size: imem_ns.len, 242 // `dst_start` is also valid as the source offset since the firmware DMA object is 243 // a mirror image of the target IMEM layout. 244 sec_code_off: imem_sec.dst_start, 245 sec_code_size: imem_sec.len, 246 code_entry_point: 0, 247 // Start of data section is the added padding + the DMEM `src_start` field. 248 data_dma_base: firmware_dma 249 .dma_handle() 250 .checked_add(u64::from_safe_cast(align_padding)) 251 .and_then(|offset| offset.checked_add(dmem.src_start.into())) 252 .ok_or(EOVERFLOW)?, 253 data_size: dmem.len, 254 argc: 0, 255 argv: 0, 256 } 257 }; 258 259 // The bootloader's code must be loaded in the area right below the first 64K of IMEM. 260 const BOOTLOADER_LOAD_CEILING: usize = sizes::SZ_64K; 261 let imem_dst_start = BOOTLOADER_LOAD_CEILING 262 .checked_sub(ucode.len()) 263 .ok_or(EOVERFLOW)?; 264 265 Ok(Self { 266 _firmware_dma: firmware_dma, 267 ucode, 268 dmem_desc, 269 brom_params: firmware.brom_params(), 270 imem_dst_start: u16::try_from(imem_dst_start)?, 271 start_tag: u16::try_from(desc.start_tag)?, 272 }) 273 } 274 275 /// Loads the bootloader into `falcon` and execute it. 276 /// 277 /// The bootloader will load the FWSEC firmware and then execute it. This function returns 278 /// after FWSEC has reached completion. 279 pub(crate) fn run( 280 &self, 281 dev: &Device<device::Bound>, 282 falcon: &Falcon<Gsp>, 283 bar: &Bar0, 284 ) -> Result<()> { 285 // Reset falcon, load the firmware, and run it. 286 falcon 287 .reset(bar) 288 .inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?; 289 falcon 290 .pio_load(bar, self) 291 .inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?; 292 293 // Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory. 294 bar.update( 295 regs::NV_PFALCON_FBIF_TRANSCFG::of::<Gsp>() 296 .try_at(usize::from_safe_cast(self.dmem_desc.ctx_dma)) 297 .ok_or(EINVAL)?, 298 |v| { 299 v.with_target(FalconFbifTarget::CoherentSysmem) 300 .with_mem_type(FalconFbifMemType::Physical) 301 }, 302 ); 303 304 let (mbox0, _) = falcon 305 .boot(bar, Some(0), None) 306 .inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?; 307 if mbox0 != 0 { 308 dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0); 309 Err(EIO) 310 } else { 311 Ok(()) 312 } 313 } 314 } 315 316 impl FalconFirmware for FwsecFirmwareWithBl { 317 type Target = Gsp; 318 319 fn brom_params(&self) -> FalconBromParams { 320 self.brom_params.clone() 321 } 322 323 fn boot_addr(&self) -> u32 { 324 // On V2 platforms, the boot address is extracted from the generic bootloader, because the 325 // gbl is what actually copies FWSEC into memory, so that is what needs to be booted. 326 u32::from(self.start_tag) << 8 327 } 328 } 329 330 impl FalconPioLoadable for FwsecFirmwareWithBl { 331 fn imem_sec_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> { 332 None 333 } 334 335 fn imem_ns_load_params(&self) -> Option<FalconPioImemLoadTarget<'_>> { 336 Some(FalconPioImemLoadTarget { 337 data: self.ucode.as_ref(), 338 dst_start: self.imem_dst_start, 339 secure: false, 340 start_tag: self.start_tag, 341 }) 342 } 343 344 fn dmem_load_params(&self) -> FalconPioDmemLoadTarget<'_> { 345 FalconPioDmemLoadTarget { 346 data: self.dmem_desc.as_bytes(), 347 dst_start: 0, 348 } 349 } 350 } 351