1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 //! Support for loading and patching the `Booter` firmware. `Booter` is a Heavy Secured firmware 5 //! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon 6 //! (and optionally unload it through a separate firmware image). 7 8 use core::marker::PhantomData; 9 10 use kernel::{ 11 device, 12 dma::Coherent, 13 prelude::*, // 14 }; 15 16 use crate::{ 17 falcon::{ 18 sec2::Sec2, 19 Falcon, 20 FalconBromParams, 21 FalconDmaLoadTarget, 22 FalconDmaLoadable, 23 FalconFirmware, // 24 }, 25 firmware::{ 26 tlv::{ 27 request_tlv, // 28 Tlv, 29 }, 30 FirmwareObject, 31 FirmwareSignature, 32 Signed, 33 Unsigned, // 34 }, 35 gpu::Chipset, 36 num::IntoSafeCast, 37 }; 38 39 /// Signature for Booter firmware. Their size is encoded into the header and not known a compile 40 /// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`]. 41 struct BooterSignature<'a>(&'a [u8]); 42 43 impl<'a> AsRef<[u8]> for BooterSignature<'a> { 44 fn as_ref(&self) -> &[u8] { 45 self.0 46 } 47 } 48 49 impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {} 50 51 /// The `Booter` loader firmware, responsible for loading the GSP. 52 pub(crate) struct BooterFirmware { 53 // Load parameters for Secure `IMEM` falcon memory. 54 imem_sec_load_target: FalconDmaLoadTarget, 55 // Load parameters for Non-Secure `IMEM` falcon memory, 56 // used only on Turing and GA100 57 imem_ns_load_target: Option<FalconDmaLoadTarget>, 58 // Load parameters for `DMEM` falcon memory. 59 dmem_load_target: FalconDmaLoadTarget, 60 // BROM falcon parameters. 61 brom_params: FalconBromParams, 62 // Device-mapped firmware image. 63 ucode: FirmwareObject<Self, Signed>, 64 } 65 66 impl FirmwareObject<BooterFirmware, Unsigned> { 67 fn new_booter(data: &[u8]) -> Result<Self> { 68 let mut ucode = KVVec::new(); 69 ucode.extend_from_slice(data, GFP_KERNEL)?; 70 71 Ok(Self(ucode, PhantomData)) 72 } 73 } 74 75 #[derive(Copy, Clone, Debug, PartialEq)] 76 pub(crate) enum BooterKind { 77 Loader, 78 Unloader, 79 } 80 81 impl BooterFirmware { 82 /// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is 83 /// ready to be loaded and run on `falcon`. 84 pub(crate) fn new( 85 dev: &device::Device<device::Bound>, 86 kind: BooterKind, 87 chipset: Chipset, 88 falcon: &Falcon<'_, <Self as FalconFirmware>::Target>, 89 ) -> Result<Self> { 90 let fw_name = match kind { 91 BooterKind::Loader => "booter_load", 92 BooterKind::Unloader => "booter_unload", 93 }; 94 let fw = request_tlv(dev, chipset, fw_name)?; 95 let tlv = Tlv::new(fw.data())?; 96 dev_dbg!( 97 dev, 98 "loaded {} firmware v{}\n", 99 fw_name, 100 tlv.get_string(b"VERS")? 101 ); 102 103 let os_data_offset = tlv.get_u32(b"DAOF")?; 104 let os_data_size = tlv.get_u32(b"DASZ")?; 105 let os_code_offset = tlv.get_u32(b"CDOF")?; 106 let os_code_size = tlv.get_u32(b"CDSZ")?; 107 let patch_loc = tlv.get_u32(b"PLOC")?; 108 let fuse_version: usize = tlv.get_u32(b"FUSE")?.into_safe_cast(); 109 let engine_id = tlv.get_u32(b"ENID")?; 110 let ucode_id = tlv.get_u32(b"UCID")?; 111 let app0_code_offset = tlv.get_u32(b"A0CO")?; 112 let app0_code_size = tlv.get_u32(b"A0CS")?; 113 114 let brom_params = FalconBromParams { 115 // `os_data_offset` is an absolute index, but `pkc_data_offset` is from the 116 // signature patch location. 117 pkc_data_offset: patch_loc.checked_sub(os_data_offset).ok_or(EINVAL)?, 118 engine_id_mask: u16::try_from(engine_id).map_err(|_| EINVAL)?, 119 ucode_id: u8::try_from(ucode_id).map_err(|_| EINVAL)?, 120 }; 121 122 let ucode = tlv 123 .get_bytes(b"BLOB") 124 .and_then(FirmwareObject::<Self, _>::new_booter)?; 125 126 // Obtain the version from the fuse register, and extract the corresponding 127 // signature. 128 let reg_fuse_version: usize = falcon 129 .signature_reg_fuse_version(brom_params.engine_id_mask, brom_params.ucode_id)? 130 .into_safe_cast(); 131 132 const FUSE_VERSION_USE_LAST_SIG: usize = 0; 133 134 let index = match reg_fuse_version { 135 // `0` means the last signature should be used. 136 FUSE_VERSION_USE_LAST_SIG => None, 137 // Otherwise, hardware fuse version needs to be subtracted to obtain the index. 138 _ => Some(fuse_version.checked_sub(reg_fuse_version).ok_or(EINVAL)?), 139 }; 140 141 // Extract the nth signature. Booter is always signed. 142 let sig_chunk = tlv.get_signature(index)?; 143 144 let signature = BooterSignature(sig_chunk); 145 let ucode_signed = ucode.patch_signature(&signature, patch_loc.into_safe_cast())?; 146 147 // There are two versions of Booter, one for Turing/GA100, and another for 148 // GA102+. The extraction of the IMEM sections differs between the two 149 // versions. Unfortunately, the file names are the same, and the headers 150 // don't indicate the versions. The only way to differentiate is by the Chipset. 151 let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 { 152 ( 153 app0_code_offset, 154 Some(FalconDmaLoadTarget { 155 src_start: 0, 156 dst_start: os_code_offset, 157 len: os_code_size, 158 }), 159 ) 160 } else { 161 (0, None) 162 }; 163 164 Ok(Self { 165 imem_sec_load_target: FalconDmaLoadTarget { 166 src_start: app0_code_offset, 167 dst_start: imem_sec_dst_start, 168 len: app0_code_size, 169 }, 170 imem_ns_load_target, 171 dmem_load_target: FalconDmaLoadTarget { 172 src_start: os_data_offset, 173 dst_start: 0, 174 len: os_data_size, 175 }, 176 brom_params, 177 ucode: ucode_signed, 178 }) 179 } 180 181 /// Load and run the booter firmware on SEC2. 182 /// 183 /// Resets SEC2, loads this firmware image, then boots with the WPR metadata 184 /// address passed via the SEC2 mailboxes. 185 pub(crate) fn run<T>( 186 &self, 187 dev: &device::Device<device::Bound>, 188 sec2_falcon: &Falcon<'_, Sec2>, 189 wpr_meta: &Coherent<T>, 190 ) -> Result { 191 sec2_falcon.reset()?; 192 sec2_falcon.load(self)?; 193 let wpr_dma_address = wpr_meta.dma_address(); 194 let (mbox0, mbox1) = sec2_falcon.boot( 195 Some(wpr_dma_address as u32), 196 Some((wpr_dma_address >> 32) as u32), 197 )?; 198 dev_dbg!(dev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); 199 200 if mbox0 != 0 { 201 dev_err!(dev, "Booter-load failed with error {:#x}\n", mbox0); 202 return Err(ENODEV); 203 } 204 205 Ok(()) 206 } 207 } 208 209 impl FalconDmaLoadable for BooterFirmware { 210 fn as_slice(&self) -> &[u8] { 211 self.ucode.0.as_slice() 212 } 213 214 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { 215 self.imem_sec_load_target.clone() 216 } 217 218 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> { 219 self.imem_ns_load_target.clone() 220 } 221 222 fn dmem_load_params(&self) -> FalconDmaLoadTarget { 223 self.dmem_load_target.clone() 224 } 225 } 226 227 impl FalconFirmware for BooterFirmware { 228 type Target = Sec2; 229 230 fn brom_params(&self) -> FalconBromParams { 231 self.brom_params.clone() 232 } 233 234 fn boot_addr(&self) -> u32 { 235 if let Some(ns_target) = &self.imem_ns_load_target { 236 ns_target.dst_start 237 } else { 238 self.imem_sec_load_target.src_start 239 } 240 } 241 } 242