1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 //! FSP (Foundation Security Processor) interface for Hopper/Blackwell GPUs. 5 //! 6 //! Hopper/Blackwell use a simplified firmware boot sequence: FMC, then FSP, then GSP. 7 //! Unlike Turing/Ampere/Ada, there is no SEC2 (Security Engine 2) usage. 8 //! FSP handles secure boot directly using FMC firmware and Chain of Trust. 9 10 use kernel::{ 11 device, 12 io::poll::read_poll_timeout, 13 prelude::*, 14 time::Delta, 15 transmute::{ 16 AsBytes, 17 FromBytes, // 18 }, 19 }; 20 21 use crate::{ 22 driver::Bar0, 23 falcon::{ 24 fsp::Fsp as FspEngine, 25 Falcon, // 26 }, 27 firmware::fsp::FspFirmware, 28 gpu::Chipset, 29 mctp::{ 30 MctpHeader, 31 NvdmHeader, 32 NvdmType, // 33 }, 34 regs, // 35 }; 36 37 mod hal; 38 39 /// FSP command response payload (`NVDM_PAYLOAD_COMMAND_RESPONSE`). 40 #[repr(C, packed)] 41 #[derive(Clone, Copy)] 42 struct NvdmPayloadCommandResponse { 43 task_id: u32, 44 command_nvdm_type: u32, 45 error_code: u32, 46 } 47 48 /// Complete FSP response structure with MCTP and NVDM headers. 49 #[repr(C, packed)] 50 #[derive(Clone, Copy)] 51 struct FspResponse { 52 mctp_header: MctpHeader, 53 nvdm_header: NvdmHeader, 54 response: NvdmPayloadCommandResponse, 55 } 56 57 // SAFETY: FspResponse is a packed C struct with only integral fields. 58 unsafe impl FromBytes for FspResponse {} 59 60 /// Trait implemented by types representing a message to send to FSP. 61 /// 62 /// This provides [`Fsp::send_sync_fsp`] with the information it needs to send 63 /// a given message, following the same pattern as GSP's `CommandToGsp`. 64 trait MessageToFsp: AsBytes { 65 /// NVDM type identifying this message to FSP. 66 const NVDM_TYPE: NvdmType; 67 } 68 69 /// FSP interface for Hopper/Blackwell GPUs. 70 /// 71 /// An `Fsp` is produced by [`Fsp::wait_secure_boot`], which only returns once FSP secure boot 72 /// has completed. It owns the FSP falcon and the FMC firmware, which are used for the subsequent 73 /// Chain of Trust boot. 74 pub(crate) struct Fsp { 75 falcon: Falcon<FspEngine>, 76 #[expect(dead_code)] 77 fsp_fw: FspFirmware, 78 } 79 80 impl Fsp { 81 /// Waits for FSP secure boot completion, then returns the [`Fsp`] interface. 82 /// 83 /// Polls the thermal scratch register until FSP signals boot completion or the timeout 84 /// elapses. Returning an [`Fsp`] only on success guarantees, at the API level, that the 85 /// interface is not used before secure boot has completed. 86 pub(crate) fn wait_secure_boot( 87 dev: &device::Device<device::Bound>, 88 bar: &Bar0, 89 chipset: Chipset, 90 fsp_fw: FspFirmware, 91 ) -> Result<Fsp> { 92 /// FSP secure boot completion timeout in milliseconds. 93 const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000; 94 95 let hal = hal::fsp_hal(chipset).ok_or(ENOTSUPP)?; 96 let falcon = Falcon::<FspEngine>::new(dev, chipset)?; 97 98 read_poll_timeout( 99 || Ok(hal.fsp_boot_status(bar)), 100 |&status| status == regs::NV_THERM_I2CS_SCRATCH_FSP_BOOT_COMPLETE_STATUS_SUCCESS, 101 Delta::from_millis(10), 102 Delta::from_millis(FSP_SECURE_BOOT_TIMEOUT_MS), 103 ) 104 .inspect_err(|e| { 105 dev_err!(dev, "FSP secure boot completion error: {:?}\n", e); 106 })?; 107 108 Ok(Fsp { falcon, fsp_fw }) 109 } 110 111 /// Sends a message to FSP and waits for the response. 112 #[expect(dead_code)] 113 fn send_sync_fsp<M>(&mut self, dev: &device::Device, bar: &Bar0, msg: &M) -> Result 114 where 115 M: MessageToFsp, 116 { 117 self.falcon.send_msg(bar, msg.as_bytes())?; 118 119 let response_buf = self.falcon.recv_msg(bar).inspect_err(|e| { 120 dev_err!(dev, "FSP response error: {:?}\n", e); 121 })?; 122 123 let (response, _) = FspResponse::from_bytes_prefix(&response_buf[..]).ok_or_else(|| { 124 dev_err!(dev, "FSP response too small: {}\n", response_buf.len()); 125 EIO 126 })?; 127 128 let mctp_header = response.mctp_header; 129 let nvdm_header = response.nvdm_header; 130 let command_nvdm_type = response.response.command_nvdm_type; 131 let error_code = response.response.error_code; 132 133 if !mctp_header.is_single_packet() { 134 dev_err!( 135 dev, 136 "Unexpected MCTP header in FSP reply: {:x?}\n", 137 mctp_header, 138 ); 139 return Err(EIO); 140 } 141 142 if !nvdm_header.validate(NvdmType::FspResponse) { 143 dev_err!( 144 dev, 145 "Unexpected NVDM header in FSP reply: {:x?}\n", 146 nvdm_header, 147 ); 148 return Err(EIO); 149 } 150 151 if command_nvdm_type != u8::from(M::NVDM_TYPE).into() { 152 dev_err!( 153 dev, 154 "Expected NVDM type {:?} in reply, got {:#x}\n", 155 M::NVDM_TYPE, 156 command_nvdm_type 157 ); 158 return Err(EIO); 159 } 160 161 if error_code != 0 { 162 dev_err!( 163 dev, 164 "NVDM command {:?} failed with error {:#x}\n", 165 M::NVDM_TYPE, 166 error_code 167 ); 168 return Err(EIO); 169 } 170 171 Ok(()) 172 } 173 } 174