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