File Index
Files and directories
Sign in
FILE

booter.rs

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/nova-core/firmware/booter.rs RUST
  1// SPDX-License-Identifier: GPL-2.0
  2
  3//! Support for loading and patching the `Booter` firmware. `Booter` is a Heavy Secured firmware
  4//! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon
  5//! (and optionally unload it through a separate firmware image).
  6
  7use core::{
  8    marker::PhantomData,
  9    ops::Deref, //
 10};
 11
 12use kernel::{
 13    device,
 14    prelude::*,
 15    transmute::FromBytes, //
 16};
 17
 18use crate::{
 19    dma::DmaObject,
 20    driver::Bar0,
 21    falcon::{
 22        sec2::Sec2,
 23        Falcon,
 24        FalconBromParams,
 25        FalconFirmware,
 26        FalconLoadParams,
 27        FalconLoadTarget, //
 28    },
 29    firmware::{
 30        BinFirmware,
 31        FirmwareDmaObject,
 32        FirmwareSignature,
 33        Signed,
 34        Unsigned, //
 35    },
 36    gpu::Chipset,
 37    num::{
 38        FromSafeCast,
 39        IntoSafeCast, //
 40    },
 41};
 42
 43/// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at
 44/// `offset` in `slice`.
 45fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S> {
 46    slice
 47        .get(offset..offset + size_of::<S>())
 48        .and_then(S::from_bytes_copy)
 49        .ok_or(EINVAL)
 50}
 51
 52/// Heavy-Secured firmware header.
 53///
 54/// Such firmwares have an application-specific payload that needs to be patched with a given
 55/// signature.
 56#[repr(C)]
 57#[derive(Debug, Clone)]
 58struct HsHeaderV2 {
 59    /// Offset to the start of the signatures.
 60    sig_prod_offset: u32,
 61    /// Size in bytes of the signatures.
 62    sig_prod_size: u32,
 63    /// Offset to a `u32` containing the location at which to patch the signature in the microcode
 64    /// image.
 65    patch_loc_offset: u32,
 66    /// Offset to a `u32` containing the index of the signature to patch.
 67    patch_sig_offset: u32,
 68    /// Start offset to the signature metadata.
 69    meta_data_offset: u32,
 70    /// Size in bytes of the signature metadata.
 71    meta_data_size: u32,
 72    /// Offset to a `u32` containing the number of signatures in the signatures section.
 73    num_sig_offset: u32,
 74    /// Offset of the application-specific header.
 75    header_offset: u32,
 76    /// Size in bytes of the application-specific header.
 77    header_size: u32,
 78}
 79
 80// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
 81unsafe impl FromBytes for HsHeaderV2 {}
 82
 83/// Heavy-Secured Firmware image container.
 84///
 85/// This provides convenient access to the fields of [`HsHeaderV2`] that are actually indices to
 86/// read from in the firmware data.
 87struct HsFirmwareV2<'a> {
 88    hdr: HsHeaderV2,
 89    fw: &'a [u8],
 90}
 91
 92impl<'a> HsFirmwareV2<'a> {
 93    /// Interprets the header of `bin_fw` as a [`HsHeaderV2`] and returns an instance of
 94    /// `HsFirmwareV2` for further parsing.
 95    ///
 96    /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.
 97    fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> {
 98        frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset.into_safe_cast())
 99            .map(|hdr| Self { hdr, fw: bin_fw.fw })
100    }
101
102    /// Returns the location at which the signatures should be patched in the microcode image.
103    ///
104    /// Fails if the offset of the patch location is outside the bounds of the firmware
105    /// image.
106    fn patch_location(&self) -> Result<u32> {
107        frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset.into_safe_cast())
108    }
109
110    /// Returns an iterator to the signatures of the firmware. The iterator can be empty if the
111    /// firmware is unsigned.
112    ///
113    /// Fails if the pointed signatures are outside the bounds of the firmware image.
114    fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> {
115        let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset.into_safe_cast())?;
116        let iter = match self.hdr.sig_prod_size.checked_div(num_sig) {
117            // If there are no signatures, return an iterator that will yield zero elements.
118            None => (&[] as &[u8]).chunks_exact(1),
119            Some(sig_size) => {
120                let patch_sig =
121                    frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?;
122                let signatures_start = usize::from_safe_cast(self.hdr.sig_prod_offset + patch_sig);
123
124                self.fw
125                    // Get signatures range.
126                    .get(
127                        signatures_start
128                            ..signatures_start + usize::from_safe_cast(self.hdr.sig_prod_size),
129                    )
130                    .ok_or(EINVAL)?
131                    .chunks_exact(sig_size.into_safe_cast())
132            }
133        };
134
135        // Map the byte slices into signatures.
136        Ok(iter.map(BooterSignature))
137    }
138}
139
140/// Signature parameters, as defined in the firmware.
141#[repr(C)]
142struct HsSignatureParams {
143    /// Fuse version to use.
144    fuse_ver: u32,
145    /// Mask of engine IDs this firmware applies to.
146    engine_id_mask: u32,
147    /// ID of the microcode.
148    ucode_id: u32,
149}
150
151// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
152unsafe impl FromBytes for HsSignatureParams {}
153
154impl HsSignatureParams {
155    /// Returns the signature parameters contained in `hs_fw`.
156    ///
157    /// Fails if the meta data parameter of `hs_fw` is outside the bounds of the firmware image, or
158    /// if its size doesn't match that of [`HsSignatureParams`].
159    fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
160        let start = usize::from_safe_cast(hs_fw.hdr.meta_data_offset);
161        let end = start
162            .checked_add(hs_fw.hdr.meta_data_size.into_safe_cast())
163            .ok_or(EINVAL)?;
164
165        hs_fw
166            .fw
167            .get(start..end)
168            .and_then(Self::from_bytes_copy)
169            .ok_or(EINVAL)
170    }
171}
172
173/// Header for code and data load offsets.
174#[repr(C)]
175#[derive(Debug, Clone)]
176struct HsLoadHeaderV2 {
177    // Offset at which the code starts.
178    os_code_offset: u32,
179    // Total size of the code, for all apps.
180    os_code_size: u32,
181    // Offset at which the data starts.
182    os_data_offset: u32,
183    // Size of the data.
184    os_data_size: u32,
185    // Number of apps following this header. Each app is described by a [`HsLoadHeaderV2App`].
186    num_apps: u32,
187}
188
189// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
190unsafe impl FromBytes for HsLoadHeaderV2 {}
191
192impl HsLoadHeaderV2 {
193    /// Returns the load header contained in `hs_fw`.
194    ///
195    /// Fails if the header pointed at by `hs_fw` is not within the bounds of the firmware image.
196    fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
197        frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset.into_safe_cast())
198    }
199}
200
201/// Header for app code loader.
202#[repr(C)]
203#[derive(Debug, Clone)]
204struct HsLoadHeaderV2App {
205    /// Offset at which to load the app code.
206    offset: u32,
207    /// Length in bytes of the app code.
208    len: u32,
209}
210
211// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
212unsafe impl FromBytes for HsLoadHeaderV2App {}
213
214impl HsLoadHeaderV2App {
215    /// Returns the [`HsLoadHeaderV2App`] for app `idx` of `hs_fw`.
216    ///
217    /// Fails if `idx` is larger than the number of apps declared in `hs_fw`, or if the header is
218    /// not within the bounds of the firmware image.
219    fn new(hs_fw: &HsFirmwareV2<'_>, idx: u32) -> Result<Self> {
220        let load_hdr = HsLoadHeaderV2::new(hs_fw)?;
221        if idx >= load_hdr.num_apps {
222            Err(EINVAL)
223        } else {
224            frombytes_at::<Self>(
225                hs_fw.fw,
226                usize::from_safe_cast(hs_fw.hdr.header_offset)
227                    // Skip the load header...
228                    .checked_add(size_of::<HsLoadHeaderV2>())
229                    // ... and jump to app header `idx`.
230                    .and_then(|offset| {
231                        offset
232                            .checked_add(usize::from_safe_cast(idx).checked_mul(size_of::<Self>())?)
233                    })
234                    .ok_or(EINVAL)?,
235            )
236        }
237    }
238}
239
240/// Signature for Booter firmware. Their size is encoded into the header and not known a compile
241/// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`].
242struct BooterSignature<'a>(&'a [u8]);
243
244impl<'a> AsRef<[u8]> for BooterSignature<'a> {
245    fn as_ref(&self) -> &[u8] {
246        self.0
247    }
248}
249
250impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}
251
252/// The `Booter` loader firmware, responsible for loading the GSP.
253pub(crate) struct BooterFirmware {
254    // Load parameters for `IMEM` falcon memory.
255    imem_load_target: FalconLoadTarget,
256    // Load parameters for `DMEM` falcon memory.
257    dmem_load_target: FalconLoadTarget,
258    // BROM falcon parameters.
259    brom_params: FalconBromParams,
260    // Device-mapped firmware image.
261    ucode: FirmwareDmaObject<Self, Signed>,
262}
263
264impl FirmwareDmaObject<BooterFirmware, Unsigned> {
265    fn new_booter(dev: &device::Device<device::Bound>, data: &[u8]) -> Result<Self> {
266        DmaObject::from_data(dev, data).map(|ucode| Self(ucode, PhantomData))
267    }
268}
269
270#[derive(Copy, Clone, Debug, PartialEq)]
271pub(crate) enum BooterKind {
272    Loader,
273    #[expect(unused)]
274    Unloader,
275}
276
277impl BooterFirmware {
278    /// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is
279    /// ready to be loaded and run on `falcon`.
280    pub(crate) fn new(
281        dev: &device::Device<device::Bound>,
282        kind: BooterKind,
283        chipset: Chipset,
284        ver: &str,
285        falcon: &Falcon<<Self as FalconFirmware>::Target>,
286        bar: &Bar0,
287    ) -> Result<Self> {
288        let fw_name = match kind {
289            BooterKind::Loader => "booter_load",
290            BooterKind::Unloader => "booter_unload",
291        };
292        let fw = super::request_firmware(dev, chipset, fw_name, ver)?;
293        let bin_fw = BinFirmware::new(&fw)?;
294
295        // The binary firmware embeds a Heavy-Secured firmware.
296        let hs_fw = HsFirmwareV2::new(&bin_fw)?;
297
298        // The Heavy-Secured firmware embeds a firmware load descriptor.
299        let load_hdr = HsLoadHeaderV2::new(&hs_fw)?;
300
301        // Offset in `ucode` where to patch the signature.
302        let patch_loc = hs_fw.patch_location()?;
303
304        let sig_params = HsSignatureParams::new(&hs_fw)?;
305        let brom_params = FalconBromParams {
306            // `load_hdr.os_data_offset` is an absolute index, but `pkc_data_offset` is from the
307            // signature patch location.
308            pkc_data_offset: patch_loc
309                .checked_sub(load_hdr.os_data_offset)
310                .ok_or(EINVAL)?,
311            engine_id_mask: u16::try_from(sig_params.engine_id_mask).map_err(|_| EINVAL)?,
312            ucode_id: u8::try_from(sig_params.ucode_id).map_err(|_| EINVAL)?,
313        };
314        let app0 = HsLoadHeaderV2App::new(&hs_fw, 0)?;
315
316        // Object containing the firmware microcode to be signature-patched.
317        let ucode = bin_fw
318            .data()
319            .ok_or(EINVAL)
320            .and_then(|data| FirmwareDmaObject::<Self, _>::new_booter(dev, data))?;
321
322        let ucode_signed = {
323            let mut signatures = hs_fw.signatures_iter()?.peekable();
324
325            if signatures.peek().is_none() {
326                // If there are no signatures, then the firmware is unsigned.
327                ucode.no_patch_signature()
328            } else {
329                // Obtain the version from the fuse register, and extract the corresponding
330                // signature.
331                let reg_fuse_version = falcon.signature_reg_fuse_version(
332                    bar,
333                    brom_params.engine_id_mask,
334                    brom_params.ucode_id,
335                )?;
336
337                // `0` means the last signature should be used.
338                const FUSE_VERSION_USE_LAST_SIG: u32 = 0;
339                let signature = match reg_fuse_version {
340                    FUSE_VERSION_USE_LAST_SIG => signatures.last(),
341                    // Otherwise hardware fuse version needs to be subtracted to obtain the index.
342                    reg_fuse_version => {
343                        let Some(idx) = sig_params.fuse_ver.checked_sub(reg_fuse_version) else {
344                            dev_err!(dev, "invalid fuse version for Booter firmware\n");
345                            return Err(EINVAL);
346                        };
347                        signatures.nth(idx.into_safe_cast())
348                    }
349                }
350                .ok_or(EINVAL)?;
351
352                ucode.patch_signature(&signature, patch_loc.into_safe_cast())?
353            }
354        };
355
356        Ok(Self {
357            imem_load_target: FalconLoadTarget {
358                src_start: app0.offset,
359                dst_start: 0,
360                len: app0.len,
361            },
362            dmem_load_target: FalconLoadTarget {
363                src_start: load_hdr.os_data_offset,
364                dst_start: 0,
365                len: load_hdr.os_data_size,
366            },
367            brom_params,
368            ucode: ucode_signed,
369        })
370    }
371}
372
373impl FalconLoadParams for BooterFirmware {
374    fn imem_load_params(&self) -> FalconLoadTarget {
375        self.imem_load_target.clone()
376    }
377
378    fn dmem_load_params(&self) -> FalconLoadTarget {
379        self.dmem_load_target.clone()
380    }
381
382    fn brom_params(&self) -> FalconBromParams {
383        self.brom_params.clone()
384    }
385
386    fn boot_addr(&self) -> u32 {
387        self.imem_load_target.src_start
388    }
389}
390
391impl Deref for BooterFirmware {
392    type Target = DmaObject;
393
394    fn deref(&self) -> &Self::Target {
395        &self.ucode.0
396    }
397}
398
399impl FalconFirmware for BooterFirmware {
400    type Target = Sec2;
401}
~/ Read-only file preview.