xref: /linux/drivers/gpu/nova-core/fsp.rs (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
182eaa14eSJohn Hubbard // SPDX-License-Identifier: GPL-2.0
282eaa14eSJohn Hubbard // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
382eaa14eSJohn Hubbard 
482eaa14eSJohn Hubbard //! FSP (Foundation Security Processor) interface for Hopper/Blackwell GPUs.
582eaa14eSJohn Hubbard //!
682eaa14eSJohn Hubbard //! Hopper/Blackwell use a simplified firmware boot sequence: FMC, then FSP, then GSP.
782eaa14eSJohn Hubbard //! Unlike Turing/Ampere/Ada, there is no SEC2 (Security Engine 2) usage.
882eaa14eSJohn Hubbard //! FSP handles secure boot directly using FMC firmware and Chain of Trust.
982eaa14eSJohn Hubbard 
1082eaa14eSJohn Hubbard use kernel::{
1182eaa14eSJohn Hubbard     device,
12d317e458SJohn Hubbard     dma::Coherent,
1382eaa14eSJohn Hubbard     io::poll::read_poll_timeout,
1482eaa14eSJohn Hubbard     prelude::*,
15d317e458SJohn Hubbard     ptr::{
16d317e458SJohn Hubbard         Alignable,
17d317e458SJohn Hubbard         Alignment, //
18d317e458SJohn Hubbard     },
19d317e458SJohn Hubbard     sizes::SZ_2M,
20e9e2a24dSJohn Hubbard     time::Delta,
21e9e2a24dSJohn Hubbard     transmute::{
22e9e2a24dSJohn Hubbard         AsBytes,
23e9e2a24dSJohn Hubbard         FromBytes, //
24e9e2a24dSJohn Hubbard     },
2582eaa14eSJohn Hubbard };
2682eaa14eSJohn Hubbard 
2782eaa14eSJohn Hubbard use crate::{
2882eaa14eSJohn Hubbard     driver::Bar0,
2982eaa14eSJohn Hubbard     falcon::{
3082eaa14eSJohn Hubbard         fsp::Fsp as FspEngine,
3182eaa14eSJohn Hubbard         Falcon, //
3282eaa14eSJohn Hubbard     },
33d317e458SJohn Hubbard     fb::FbLayout,
34d317e458SJohn Hubbard     firmware::fsp::{
35d317e458SJohn Hubbard         FmcSignatures,
36d317e458SJohn Hubbard         FspFirmware, //
37d317e458SJohn Hubbard     },
3882eaa14eSJohn Hubbard     gpu::Chipset,
39d317e458SJohn Hubbard     gsp::GspFmcBootParams,
40e9e2a24dSJohn Hubbard     mctp::{
41e9e2a24dSJohn Hubbard         MctpHeader,
42e9e2a24dSJohn Hubbard         NvdmHeader,
43e9e2a24dSJohn Hubbard         NvdmType, //
44e9e2a24dSJohn Hubbard     },
45d317e458SJohn Hubbard     num,
4682eaa14eSJohn Hubbard     regs, //
4782eaa14eSJohn Hubbard };
4882eaa14eSJohn Hubbard 
4982eaa14eSJohn Hubbard mod hal;
5082eaa14eSJohn Hubbard 
51e9e2a24dSJohn Hubbard /// FSP command response payload (`NVDM_PAYLOAD_COMMAND_RESPONSE`).
52e9e2a24dSJohn Hubbard #[repr(C, packed)]
53e9e2a24dSJohn Hubbard #[derive(Clone, Copy)]
54e9e2a24dSJohn Hubbard struct NvdmPayloadCommandResponse {
55e9e2a24dSJohn Hubbard     task_id: u32,
56e9e2a24dSJohn Hubbard     command_nvdm_type: u32,
57e9e2a24dSJohn Hubbard     error_code: u32,
58e9e2a24dSJohn Hubbard }
59e9e2a24dSJohn Hubbard 
60e9e2a24dSJohn Hubbard /// Complete FSP response structure with MCTP and NVDM headers.
61e9e2a24dSJohn Hubbard #[repr(C, packed)]
62e9e2a24dSJohn Hubbard #[derive(Clone, Copy)]
63e9e2a24dSJohn Hubbard struct FspResponse {
64e9e2a24dSJohn Hubbard     mctp_header: MctpHeader,
65e9e2a24dSJohn Hubbard     nvdm_header: NvdmHeader,
66e9e2a24dSJohn Hubbard     response: NvdmPayloadCommandResponse,
67e9e2a24dSJohn Hubbard }
68e9e2a24dSJohn Hubbard 
69e9e2a24dSJohn Hubbard // SAFETY: FspResponse is a packed C struct with only integral fields.
70e9e2a24dSJohn Hubbard unsafe impl FromBytes for FspResponse {}
71e9e2a24dSJohn Hubbard 
72e9e2a24dSJohn Hubbard /// Trait implemented by types representing a message to send to FSP.
73e9e2a24dSJohn Hubbard ///
74e9e2a24dSJohn Hubbard /// This provides [`Fsp::send_sync_fsp`] with the information it needs to send
75e9e2a24dSJohn Hubbard /// a given message, following the same pattern as GSP's `CommandToGsp`.
76e9e2a24dSJohn Hubbard trait MessageToFsp: AsBytes {
77e9e2a24dSJohn Hubbard     /// NVDM type identifying this message to FSP.
78e9e2a24dSJohn Hubbard     const NVDM_TYPE: NvdmType;
79e9e2a24dSJohn Hubbard }
80e9e2a24dSJohn Hubbard 
81d317e458SJohn Hubbard /// NVDM (NVIDIA Data Model) CoT (Chain of Trust) payload, the main
82d317e458SJohn Hubbard /// message body sent to FSP for Chain of Trust boot.
83d317e458SJohn Hubbard #[repr(C, packed)]
84d317e458SJohn Hubbard #[derive(Clone, Copy, Zeroable)]
85d317e458SJohn Hubbard struct NvdmPayloadCot {
86d317e458SJohn Hubbard     version: u16,
87d317e458SJohn Hubbard     size: u16,
88d317e458SJohn Hubbard     gsp_fmc_sysmem_offset: u64,
89d317e458SJohn Hubbard     frts_sysmem_offset: u64,
90d317e458SJohn Hubbard     frts_sysmem_size: u32,
91d317e458SJohn Hubbard     frts_vidmem_offset: u64,
92d317e458SJohn Hubbard     frts_vidmem_size: u32,
93d317e458SJohn Hubbard     sigs: FmcSignatures,
94d317e458SJohn Hubbard     gsp_boot_args_sysmem_offset: u64,
95d317e458SJohn Hubbard }
96d317e458SJohn Hubbard 
97d317e458SJohn Hubbard /// Complete FSP message structure with MCTP and NVDM headers.
98d317e458SJohn Hubbard #[repr(C)]
99d317e458SJohn Hubbard #[derive(Clone, Copy)]
100d317e458SJohn Hubbard struct FspMessage {
101d317e458SJohn Hubbard     mctp_header: MctpHeader,
102d317e458SJohn Hubbard     nvdm_header: NvdmHeader,
103d317e458SJohn Hubbard     cot: NvdmPayloadCot,
104d317e458SJohn Hubbard }
105d317e458SJohn Hubbard 
106d317e458SJohn Hubbard impl FspMessage {
107d317e458SJohn Hubbard     /// Returns an in-place initializer for [`FspMessage`].
108d317e458SJohn Hubbard     fn new<'a>(
109d317e458SJohn Hubbard         fb_layout: &FbLayout,
110d317e458SJohn Hubbard         fsp_fw: &'a FspFirmware,
111d317e458SJohn Hubbard         args: &'a FmcBootArgs,
112d317e458SJohn Hubbard     ) -> Result<impl Init<Self> + 'a> {
113d317e458SJohn Hubbard         // frts_offset is relative to FB end: FRTS_location = FB_END - frts_offset
114d317e458SJohn Hubbard         let frts_vidmem_offset = if !args.resume {
115d317e458SJohn Hubbard             let frts_reserved_size = fb_layout.heap.len() + u64::from(fb_layout.pmu_reserved_size);
116d317e458SJohn Hubbard 
117d317e458SJohn Hubbard             frts_reserved_size
118d317e458SJohn Hubbard                 .align_up(Alignment::new::<SZ_2M>())
119d317e458SJohn Hubbard                 .ok_or(EINVAL)?
120d317e458SJohn Hubbard         } else {
121d317e458SJohn Hubbard             0
122d317e458SJohn Hubbard         };
123d317e458SJohn Hubbard 
124d317e458SJohn Hubbard         let frts_size: u32 = if !args.resume {
125d317e458SJohn Hubbard             fb_layout.frts.len().try_into()?
126d317e458SJohn Hubbard         } else {
127d317e458SJohn Hubbard             0
128d317e458SJohn Hubbard         };
129d317e458SJohn Hubbard 
130d317e458SJohn Hubbard         let version = hal::fsp_hal(args.chipset).ok_or(ENOTSUPP)?.cot_version();
131d317e458SJohn Hubbard         let size = num::usize_into_u16::<{ core::mem::size_of::<NvdmPayloadCot>() }>();
132d317e458SJohn Hubbard 
133d317e458SJohn Hubbard         Ok(init!(Self {
134d317e458SJohn Hubbard             mctp_header: MctpHeader::single_packet(),
135d317e458SJohn Hubbard             nvdm_header: NvdmHeader::new(NvdmType::Cot),
136d317e458SJohn Hubbard             // The payload is packed, so we cannot use `init!`. Initialize it member-by-member using
137d317e458SJohn Hubbard             // `chain`.
138d317e458SJohn Hubbard             cot <- pin_init::init_zeroed(),
139d317e458SJohn Hubbard         })
140d317e458SJohn Hubbard         .chain(move |msg| {
141d317e458SJohn Hubbard             msg.cot.version = version;
142d317e458SJohn Hubbard             msg.cot.size = size;
143d317e458SJohn Hubbard             msg.cot.gsp_fmc_sysmem_offset = fsp_fw.fmc_image.dma_handle();
144d317e458SJohn Hubbard             msg.cot.frts_vidmem_offset = frts_vidmem_offset;
145d317e458SJohn Hubbard             msg.cot.frts_vidmem_size = frts_size;
146d317e458SJohn Hubbard             // frts_sysmem_* intentionally left at zero for now, but will be needed for e.g.
147d317e458SJohn Hubbard             // systems without VRAM.
148d317e458SJohn Hubbard             msg.cot.gsp_boot_args_sysmem_offset = args.fmc_boot_params.dma_handle();
149d317e458SJohn Hubbard             msg.cot.sigs = *fsp_fw.fmc_sigs;
150d317e458SJohn Hubbard 
151d317e458SJohn Hubbard             Ok(())
152d317e458SJohn Hubbard         }))
153d317e458SJohn Hubbard     }
154d317e458SJohn Hubbard }
155d317e458SJohn Hubbard 
156d317e458SJohn Hubbard // SAFETY: `FspMessage` is `#[repr(C)]` with no padding, so all of its
157d317e458SJohn Hubbard // bytes are initialized.
158d317e458SJohn Hubbard unsafe impl AsBytes for FspMessage {}
159d317e458SJohn Hubbard 
160d317e458SJohn Hubbard impl MessageToFsp for FspMessage {
161d317e458SJohn Hubbard     const NVDM_TYPE: NvdmType = NvdmType::Cot;
162d317e458SJohn Hubbard }
163d317e458SJohn Hubbard 
164d317e458SJohn Hubbard /// Bundled arguments for FMC boot via FSP Chain of Trust.
165d317e458SJohn Hubbard pub(crate) struct FmcBootArgs {
166d317e458SJohn Hubbard     chipset: Chipset,
167d317e458SJohn Hubbard     fmc_boot_params: Coherent<GspFmcBootParams>,
168d317e458SJohn Hubbard     resume: bool,
169d317e458SJohn Hubbard }
170d317e458SJohn Hubbard 
171d317e458SJohn Hubbard impl FmcBootArgs {
172d317e458SJohn Hubbard     /// Builds FMC boot arguments, allocating the DMA-coherent boot parameter
173d317e458SJohn Hubbard     /// structure that FSP will read.
174d317e458SJohn Hubbard     pub(crate) fn new(
175d317e458SJohn Hubbard         dev: &device::Device<device::Bound>,
176d317e458SJohn Hubbard         chipset: Chipset,
177d317e458SJohn Hubbard         wpr_meta_addr: u64,
178d317e458SJohn Hubbard         libos_addr: u64,
179d317e458SJohn Hubbard         resume: bool,
180d317e458SJohn Hubbard     ) -> Result<Self> {
181d317e458SJohn Hubbard         let init = GspFmcBootParams::new(wpr_meta_addr, libos_addr);
182d317e458SJohn Hubbard 
183d317e458SJohn Hubbard         Ok(Self {
184d317e458SJohn Hubbard             chipset,
185d317e458SJohn Hubbard             fmc_boot_params: Coherent::<GspFmcBootParams>::init(dev, GFP_KERNEL, init)?,
186d317e458SJohn Hubbard             resume,
187d317e458SJohn Hubbard         })
188d317e458SJohn Hubbard     }
189a69a9e23SJohn Hubbard 
190a69a9e23SJohn Hubbard     /// DMA address of the FMC boot parameters, needed after boot for lockdown
191a69a9e23SJohn Hubbard     /// release polling.
192a69a9e23SJohn Hubbard     pub(crate) fn boot_params_dma_handle(&self) -> u64 {
193a69a9e23SJohn Hubbard         self.fmc_boot_params.dma_handle()
194a69a9e23SJohn Hubbard     }
195d317e458SJohn Hubbard }
196d317e458SJohn Hubbard 
19782eaa14eSJohn Hubbard /// FSP interface for Hopper/Blackwell GPUs.
19882eaa14eSJohn Hubbard ///
19982eaa14eSJohn Hubbard /// An `Fsp` is produced by [`Fsp::wait_secure_boot`], which only returns once FSP secure boot
20082eaa14eSJohn Hubbard /// has completed. It owns the FSP falcon and the FMC firmware, which are used for the subsequent
20182eaa14eSJohn Hubbard /// Chain of Trust boot.
20282eaa14eSJohn Hubbard pub(crate) struct Fsp {
20382eaa14eSJohn Hubbard     falcon: Falcon<FspEngine>,
20482eaa14eSJohn Hubbard     fsp_fw: FspFirmware,
20582eaa14eSJohn Hubbard }
20682eaa14eSJohn Hubbard 
20782eaa14eSJohn Hubbard impl Fsp {
20882eaa14eSJohn Hubbard     /// Waits for FSP secure boot completion, then returns the [`Fsp`] interface.
20982eaa14eSJohn Hubbard     ///
21082eaa14eSJohn Hubbard     /// Polls the thermal scratch register until FSP signals boot completion or the timeout
21182eaa14eSJohn Hubbard     /// elapses. Returning an [`Fsp`] only on success guarantees, at the API level, that the
21282eaa14eSJohn Hubbard     /// interface is not used before secure boot has completed.
21382eaa14eSJohn Hubbard     pub(crate) fn wait_secure_boot(
21482eaa14eSJohn Hubbard         dev: &device::Device<device::Bound>,
215*99676aedSGary Guo         bar: Bar0<'_>,
21682eaa14eSJohn Hubbard         chipset: Chipset,
21782eaa14eSJohn Hubbard         fsp_fw: FspFirmware,
21882eaa14eSJohn Hubbard     ) -> Result<Fsp> {
21982eaa14eSJohn Hubbard         /// FSP secure boot completion timeout in milliseconds.
22082eaa14eSJohn Hubbard         const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000;
22182eaa14eSJohn Hubbard 
22282eaa14eSJohn Hubbard         let hal = hal::fsp_hal(chipset).ok_or(ENOTSUPP)?;
22382eaa14eSJohn Hubbard         let falcon = Falcon::<FspEngine>::new(dev, chipset)?;
22482eaa14eSJohn Hubbard 
22582eaa14eSJohn Hubbard         read_poll_timeout(
22682eaa14eSJohn Hubbard             || Ok(hal.fsp_boot_status(bar)),
22782eaa14eSJohn Hubbard             |&status| status == regs::NV_THERM_I2CS_SCRATCH_FSP_BOOT_COMPLETE_STATUS_SUCCESS,
22882eaa14eSJohn Hubbard             Delta::from_millis(10),
22982eaa14eSJohn Hubbard             Delta::from_millis(FSP_SECURE_BOOT_TIMEOUT_MS),
23082eaa14eSJohn Hubbard         )
23182eaa14eSJohn Hubbard         .inspect_err(|e| {
23282eaa14eSJohn Hubbard             dev_err!(dev, "FSP secure boot completion error: {:?}\n", e);
23382eaa14eSJohn Hubbard         })?;
23482eaa14eSJohn Hubbard 
23582eaa14eSJohn Hubbard         Ok(Fsp { falcon, fsp_fw })
23682eaa14eSJohn Hubbard     }
237e9e2a24dSJohn Hubbard 
238e9e2a24dSJohn Hubbard     /// Sends a message to FSP and waits for the response.
239*99676aedSGary Guo     fn send_sync_fsp<M>(&mut self, dev: &device::Device, bar: Bar0<'_>, msg: &M) -> Result
240e9e2a24dSJohn Hubbard     where
241e9e2a24dSJohn Hubbard         M: MessageToFsp,
242e9e2a24dSJohn Hubbard     {
243e9e2a24dSJohn Hubbard         self.falcon.send_msg(bar, msg.as_bytes())?;
244e9e2a24dSJohn Hubbard 
245e9e2a24dSJohn Hubbard         let response_buf = self.falcon.recv_msg(bar).inspect_err(|e| {
246e9e2a24dSJohn Hubbard             dev_err!(dev, "FSP response error: {:?}\n", e);
247e9e2a24dSJohn Hubbard         })?;
248e9e2a24dSJohn Hubbard 
249e9e2a24dSJohn Hubbard         let (response, _) = FspResponse::from_bytes_prefix(&response_buf[..]).ok_or_else(|| {
250e9e2a24dSJohn Hubbard             dev_err!(dev, "FSP response too small: {}\n", response_buf.len());
251e9e2a24dSJohn Hubbard             EIO
252e9e2a24dSJohn Hubbard         })?;
253e9e2a24dSJohn Hubbard 
254e9e2a24dSJohn Hubbard         let mctp_header = response.mctp_header;
255e9e2a24dSJohn Hubbard         let nvdm_header = response.nvdm_header;
256e9e2a24dSJohn Hubbard         let command_nvdm_type = response.response.command_nvdm_type;
257e9e2a24dSJohn Hubbard         let error_code = response.response.error_code;
258e9e2a24dSJohn Hubbard 
259e9e2a24dSJohn Hubbard         if !mctp_header.is_single_packet() {
260e9e2a24dSJohn Hubbard             dev_err!(
261e9e2a24dSJohn Hubbard                 dev,
262e9e2a24dSJohn Hubbard                 "Unexpected MCTP header in FSP reply: {:x?}\n",
263e9e2a24dSJohn Hubbard                 mctp_header,
264e9e2a24dSJohn Hubbard             );
265e9e2a24dSJohn Hubbard             return Err(EIO);
266e9e2a24dSJohn Hubbard         }
267e9e2a24dSJohn Hubbard 
268e9e2a24dSJohn Hubbard         if !nvdm_header.validate(NvdmType::FspResponse) {
269e9e2a24dSJohn Hubbard             dev_err!(
270e9e2a24dSJohn Hubbard                 dev,
271e9e2a24dSJohn Hubbard                 "Unexpected NVDM header in FSP reply: {:x?}\n",
272e9e2a24dSJohn Hubbard                 nvdm_header,
273e9e2a24dSJohn Hubbard             );
274e9e2a24dSJohn Hubbard             return Err(EIO);
275e9e2a24dSJohn Hubbard         }
276e9e2a24dSJohn Hubbard 
277e9e2a24dSJohn Hubbard         if command_nvdm_type != u8::from(M::NVDM_TYPE).into() {
278e9e2a24dSJohn Hubbard             dev_err!(
279e9e2a24dSJohn Hubbard                 dev,
280e9e2a24dSJohn Hubbard                 "Expected NVDM type {:?} in reply, got {:#x}\n",
281e9e2a24dSJohn Hubbard                 M::NVDM_TYPE,
282e9e2a24dSJohn Hubbard                 command_nvdm_type
283e9e2a24dSJohn Hubbard             );
284e9e2a24dSJohn Hubbard             return Err(EIO);
285e9e2a24dSJohn Hubbard         }
286e9e2a24dSJohn Hubbard 
287e9e2a24dSJohn Hubbard         if error_code != 0 {
288e9e2a24dSJohn Hubbard             dev_err!(
289e9e2a24dSJohn Hubbard                 dev,
290e9e2a24dSJohn Hubbard                 "NVDM command {:?} failed with error {:#x}\n",
291e9e2a24dSJohn Hubbard                 M::NVDM_TYPE,
292e9e2a24dSJohn Hubbard                 error_code
293e9e2a24dSJohn Hubbard             );
294e9e2a24dSJohn Hubbard             return Err(EIO);
295e9e2a24dSJohn Hubbard         }
296e9e2a24dSJohn Hubbard 
297e9e2a24dSJohn Hubbard         Ok(())
298e9e2a24dSJohn Hubbard     }
299d317e458SJohn Hubbard 
300d317e458SJohn Hubbard     /// Boots GSP FMC via FSP Chain of Trust.
301d317e458SJohn Hubbard     ///
302d317e458SJohn Hubbard     /// Builds the CoT message from the pre-configured [`FmcBootArgs`], sends it
303d317e458SJohn Hubbard     /// to FSP, and waits for the response.
304d317e458SJohn Hubbard     pub(crate) fn boot_fmc(
305d317e458SJohn Hubbard         &mut self,
306d317e458SJohn Hubbard         dev: &device::Device<device::Bound>,
307*99676aedSGary Guo         bar: Bar0<'_>,
308d317e458SJohn Hubbard         fb_layout: &FbLayout,
309d317e458SJohn Hubbard         args: &FmcBootArgs,
310d317e458SJohn Hubbard     ) -> Result {
311d317e458SJohn Hubbard         dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset);
312d317e458SJohn Hubbard 
313d317e458SJohn Hubbard         let msg = KBox::init(FspMessage::new(fb_layout, &self.fsp_fw, args)?, GFP_KERNEL)?;
314d317e458SJohn Hubbard 
315d317e458SJohn Hubbard         self.send_sync_fsp(dev, bar, &*msg)?;
316d317e458SJohn Hubbard 
317d317e458SJohn Hubbard         dev_dbg!(dev, "FSP Chain of Trust completed successfully\n");
318d317e458SJohn Hubbard         Ok(())
319d317e458SJohn Hubbard     }
32082eaa14eSJohn Hubbard }
321