xref: /linux/drivers/gpu/nova-core/firmware/booter.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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