diff options
| author | John Hubbard <jhubbard@nvidia.com> | 2026-06-03 16:30:21 +0900 |
|---|---|---|
| committer | Alexandre Courbot <acourbot@nvidia.com> | 2026-06-03 22:12:49 +0900 |
| commit | e9e2a24d9493f50a3cc73e112b0dbdf91f319851 (patch) | |
| tree | 78709c49a82bc5dd350094974066187579e7626b /drivers/gpu/nova-core | |
| parent | 944a0fedca7ed226685d3b965dd1371ff34b3017 (diff) | |
| download | lwn-e9e2a24d9493f50a3cc73e112b0dbdf91f319851.tar.gz lwn-e9e2a24d9493f50a3cc73e112b0dbdf91f319851.zip | |
gpu: nova-core: Hopper/Blackwell: add FSP send/receive messaging
FSP exchanges are request/response: the driver sends an MCTP/NVDM
message and must match the reply against the request before acting on
it. Add the synchronous send-and-wait path that validates the response
transport and message headers and confirms the reply corresponds to the
request that was sent.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
Link: https://patch.msgid.link/20260603-b4-blackwell-v13-4-d9f3a06939e0@nvidia.com
[acourbot: make `MessageToFsp` private.]
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Diffstat (limited to 'drivers/gpu/nova-core')
| -rw-r--r-- | drivers/gpu/nova-core/falcon/fsp.rs | 2 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/fsp.rs | 105 |
2 files changed, 103 insertions, 4 deletions
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs index 8fa47a8abb83..d322b81d7345 100644 --- a/drivers/gpu/nova-core/falcon/fsp.rs +++ b/drivers/gpu/nova-core/falcon/fsp.rs @@ -122,7 +122,6 @@ impl Falcon<Fsp> { /// Writes `packet` to FSP EMEM and updates the queue pointers to notify FSP. /// /// Returns `EINVAL` if `packet` is empty or its length is not 4-byte aligned. - #[expect(dead_code)] pub(crate) fn send_msg(&mut self, bar: &Bar0, packet: &[u8]) -> Result { if packet.is_empty() { return Err(EINVAL); @@ -149,7 +148,6 @@ impl Falcon<Fsp> { /// /// Returns `ETIMEDOUT` if no message was available until timeout, or a regular error code if a /// memory allocation error occurred. - #[expect(dead_code)] pub(crate) fn recv_msg(&mut self, bar: &Bar0) -> Result<KVec<u8>> { let msg_size = read_poll_timeout( || Ok(self.poll_msgq(bar)), diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index 908dc112aa6f..f1f74832bc40 100644 --- a/drivers/gpu/nova-core/fsp.rs +++ b/drivers/gpu/nova-core/fsp.rs @@ -11,7 +11,11 @@ use kernel::{ device, io::poll::read_poll_timeout, prelude::*, - time::Delta, // + time::Delta, + transmute::{ + AsBytes, + FromBytes, // + }, }; use crate::{ @@ -22,18 +26,52 @@ use crate::{ }, firmware::fsp::FspFirmware, gpu::Chipset, + mctp::{ + MctpHeader, + NvdmHeader, + NvdmType, // + }, regs, // }; mod hal; +/// FSP command response payload (`NVDM_PAYLOAD_COMMAND_RESPONSE`). +#[repr(C, packed)] +#[derive(Clone, Copy)] +struct NvdmPayloadCommandResponse { + task_id: u32, + command_nvdm_type: u32, + error_code: u32, +} + +/// Complete FSP response structure with MCTP and NVDM headers. +#[repr(C, packed)] +#[derive(Clone, Copy)] +struct FspResponse { + mctp_header: MctpHeader, + nvdm_header: NvdmHeader, + response: NvdmPayloadCommandResponse, +} + +// SAFETY: FspResponse is a packed C struct with only integral fields. +unsafe impl FromBytes for FspResponse {} + +/// Trait implemented by types representing a message to send to FSP. +/// +/// This provides [`Fsp::send_sync_fsp`] with the information it needs to send +/// a given message, following the same pattern as GSP's `CommandToGsp`. +trait MessageToFsp: AsBytes { + /// NVDM type identifying this message to FSP. + const NVDM_TYPE: NvdmType; +} + /// FSP interface for Hopper/Blackwell GPUs. /// /// An `Fsp` is produced by [`Fsp::wait_secure_boot`], which only returns once FSP secure boot /// has completed. It owns the FSP falcon and the FMC firmware, which are used for the subsequent /// Chain of Trust boot. pub(crate) struct Fsp { - #[expect(dead_code)] falcon: Falcon<FspEngine>, #[expect(dead_code)] fsp_fw: FspFirmware, @@ -69,4 +107,67 @@ impl Fsp { Ok(Fsp { falcon, fsp_fw }) } + + /// Sends a message to FSP and waits for the response. + #[expect(dead_code)] + fn send_sync_fsp<M>(&mut self, dev: &device::Device, bar: &Bar0, msg: &M) -> Result + where + M: MessageToFsp, + { + self.falcon.send_msg(bar, msg.as_bytes())?; + + let response_buf = self.falcon.recv_msg(bar).inspect_err(|e| { + dev_err!(dev, "FSP response error: {:?}\n", e); + })?; + + let (response, _) = FspResponse::from_bytes_prefix(&response_buf[..]).ok_or_else(|| { + dev_err!(dev, "FSP response too small: {}\n", response_buf.len()); + EIO + })?; + + let mctp_header = response.mctp_header; + let nvdm_header = response.nvdm_header; + let command_nvdm_type = response.response.command_nvdm_type; + let error_code = response.response.error_code; + + if !mctp_header.is_single_packet() { + dev_err!( + dev, + "Unexpected MCTP header in FSP reply: {:x?}\n", + mctp_header, + ); + return Err(EIO); + } + + if !nvdm_header.validate(NvdmType::FspResponse) { + dev_err!( + dev, + "Unexpected NVDM header in FSP reply: {:x?}\n", + nvdm_header, + ); + return Err(EIO); + } + + if command_nvdm_type != u8::from(M::NVDM_TYPE).into() { + dev_err!( + dev, + "Expected NVDM type {:?} in reply, got {:#x}\n", + M::NVDM_TYPE, + command_nvdm_type + ); + return Err(EIO); + } + + if error_code != 0 { + dev_err!( + dev, + "NVDM command {:?} failed with error {:#x}\n", + M::NVDM_TYPE, + error_code + ); + return Err(EIO); + } + + Ok(()) + } } |
