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.
Cc: Joel Fernandes <[email protected]> Signed-off-by: John Hubbard <[email protected]> --- drivers/gpu/nova-core/falcon/fsp.rs | 3 - drivers/gpu/nova-core/fsp.rs | 101 ++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs index 081b9fce9278..33d0408eb852 100644 --- a/drivers/gpu/nova-core/falcon/fsp.rs +++ b/drivers/gpu/nova-core/falcon/fsp.rs @@ -93,7 +93,6 @@ pub(crate) fn read_emem(&self, bar: &Bar0, offset: u32, data: &mut [u8]) -> Resu /// /// The FSP message queue is not circular - pointers are reset to 0 after each /// message exchange, so `tail >= head` is always true when data is present. - #[expect(unused)] pub(crate) fn poll_msgq(&self, bar: &Bar0) -> u32 { let head = regs::NV_PFSP_MSGQ_HEAD::read(bar).address(); let tail = regs::NV_PFSP_MSGQ_TAIL::read(bar).address(); @@ -116,7 +115,6 @@ pub(crate) fn poll_msgq(&self, bar: &Bar0) -> u32 { /// /// # Returns /// `Ok(())` on success, `Err(EINVAL)` if packet is empty or not 4-byte aligned - #[expect(unused)] pub(crate) fn send_msg(&self, bar: &Bar0, packet: &[u8]) -> Result { if packet.is_empty() { return Err(EINVAL); @@ -148,7 +146,6 @@ pub(crate) fn send_msg(&self, bar: &Bar0, packet: &[u8]) -> Result { /// /// # Returns /// `Ok(bytes_read)` on success, `Err(EINVAL)` if size is 0, exceeds buffer, or not aligned - #[expect(unused)] pub(crate) fn recv_msg(&self, bar: &Bar0, buffer: &mut [u8], size: usize) -> Result<usize> { if size == 0 || size > buffer.len() { return Err(EINVAL); diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs index 4f7d121f4240..dfd0ffe5a650 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; @@ -314,4 +317,102 @@ 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); + } + + Ok(()) + } } -- 2.52.0
