projects/linux·blob master · refs

241 lines · 7.7 KiB

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
8use core::marker::PhantomData;
9
10use kernel::{
11 device,
12 dma::Coherent,
13 prelude::*, //
14};
15
16use 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`].
41struct BooterSignature<'a>(&'a [u8]);
42
43impl<'a> AsRef<[u8]> for BooterSignature<'a> {
44 fn as_ref(&self) -> &[u8] {
45 self.0
46 }
47}
48
49impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}
50
51/// The `Booter` loader firmware, responsible for loading the GSP.
52pub(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
66impl 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)]
76pub(crate) enum BooterKind {
77 Loader,
78 Unloader,
79}
80
81impl 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
209impl 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
227impl 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}