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