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