Add send_sync_fsp() which sends an MCTP/NVDM message to FSP and waits for the response. This handles the low-level protocol details including header validation, error checking, and timeout handling.
Signed-off-by: John Hubbard <[email protected]> --- drivers/gpu/nova-core/fsp.rs | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index 311b6d4c6011..bb1e19c03c30 100644 --- a/drivers/gpu/nova-core/fsp.rs +++ b/drivers/gpu/nova-core/fsp.rs @@ -22,6 +22,9 @@ use crate::regs::FSP_BOOT_COMPLETE_SUCCESS; +/// FSP message timeout in milliseconds. +const FSP_MSG_TIMEOUT_MS: i64 = 2000; + /// FSP secure boot completion timeout in milliseconds. const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 4000; @@ -360,4 +363,103 @@ pub(crate) fn extract_fmc_signatures_static( Ok(signatures) } + + /// Send message to FSP and wait for response. + fn send_sync_fsp( + dev: &device::Device<device::Bound>, + bar: &crate::driver::Bar0, + fsp_falcon: &crate::falcon::Falcon<crate::falcon::fsp::Fsp>, + nvdm_type: u32, + packet: &[u8], + ) -> Result<()> { + // Send message + fsp_falcon.send_msg(bar, packet)?; + + // Wait for response + let timeout = Delta::from_millis(FSP_MSG_TIMEOUT_MS); + let packet_size = read_poll_timeout( + || Ok(fsp_falcon.poll_msgq(bar)), + |&size| size > 0, + Delta::ZERO, + timeout, + ) + .map_err(|_| { + dev_err!(dev, "FSP response timeout\n"); + ETIMEDOUT + })?; + + // Receive response + let packet_size = packet_size as usize; + let mut response_buf = KVec::<u8>::new(); + response_buf.resize(packet_size, 0, GFP_KERNEL)?; + fsp_falcon.recv_msg(bar, &mut response_buf, packet_size)?; + + // Parse response + if response_buf.len() < core::mem::size_of::<FspResponse>() { + dev_err!(dev, "FSP response too small: {}\n", response_buf.len()); + return Err(EIO); + } + + let response = FspResponse::from_bytes(&response_buf[..]).ok_or(EIO)?; + + // Copy packed struct fields to avoid alignment issues + 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; + + // Validate MCTP header + let mctp_som = (mctp_header >> 31) & 1; + let mctp_eom = (mctp_header >> 30) & 1; + if mctp_som != 1 || mctp_eom != 1 { + dev_err!( + dev, + "Unexpected MCTP header in FSP reply: {:#x}\n", + mctp_header + ); + return Err(EIO); + } + + // Validate NVDM header + let nvdm_msg_type = nvdm_header & 0x7f; + let nvdm_vendor_id = (nvdm_header >> 8) & 0xffff; + let nvdm_type_resp = (nvdm_header >> 24) & 0xff; + + if nvdm_msg_type != mctp::MSG_TYPE_VENDOR_PCI + || nvdm_vendor_id != mctp::VENDOR_ID_NV + || nvdm_type_resp != mctp::NVDM_TYPE_FSP_RESPONSE + { + dev_err!( + dev, + "Unexpected NVDM header in FSP reply: {:#x}\n", + nvdm_header + ); + return Err(EIO); + } + + // Check command type matches + if command_nvdm_type != nvdm_type { + dev_err!( + dev, + "Expected NVDM type {:#x} in reply, got {:#x}\n", + nvdm_type, + command_nvdm_type + ); + return Err(EIO); + } + + // Check for errors + if error_code != 0 { + dev_err!( + dev, + "NVDM command {:#x} failed with error {:#x}\n", + nvdm_type, + error_code + ); + return Err(EIO); + } + + dev_dbg!(dev, "FSP command {:#x} completed successfully\n", nvdm_type); + Ok(()) + } } -- 2.52.0
