From: John Hubbard Date: Wed, 3 Jun 2026 07:30:19 +0000 (+0900) Subject: gpu: nova-core: Hopper/Blackwell: add FSP message infrastructure X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ab42c32347ef1229b4ebeae6a10d4202a61c4463;p=thirdparty%2Fkernel%2Flinux.git gpu: nova-core: Hopper/Blackwell: add FSP message infrastructure FSP communication uses a pair of non-circular queues in the FSP falcon's EMEM, one for messages from the driver to FSP and one for replies, with the driver polling for response data. Add the queue registers and the low-level helpers used by the higher-level FSP message layer. Signed-off-by: John Hubbard Reviewed-by: Eliot Courtney Link: https://patch.msgid.link/20260603-b4-blackwell-v13-2-d9f3a06939e0@nvidia.com [acourbot: align register fields names with OpenRM.] [acourbot: represent registers as arrays of 8 instances, as per OpenRM.] Signed-off-by: Alexandre Courbot --- diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs index 0956a75ef7aa5..8fa47a8abb830 100644 --- a/drivers/gpu/nova-core/falcon/fsp.rs +++ b/drivers/gpu/nova-core/falcon/fsp.rs @@ -8,13 +8,16 @@ use kernel::{ io::{ + poll::read_poll_timeout, register::{ + Array, RegisterBase, WithBase, // }, Io, // }, prelude::*, + time::Delta, }; use crate::{ @@ -25,9 +28,13 @@ use crate::{ PFalcon2Base, PFalconBase, // }, - regs, + num, + regs, // }; +/// FSP message timeout in milliseconds. +const FSP_MSG_TIMEOUT_MS: i64 = 2000; + /// Type specifying the `Fsp` falcon engine. Cannot be instantiated. pub(crate) struct Fsp(()); @@ -46,7 +53,6 @@ impl Falcon { /// /// `data` is interpreted as little-endian 32-bit words. Returns `EINVAL` /// if the `data` length is not 4-byte aligned. - #[expect(dead_code)] fn write_emem(&mut self, bar: &Bar0, data: &[u8]) -> Result { if data.len() % 4 != 0 { return Err(EINVAL); @@ -75,7 +81,6 @@ impl Falcon { /// /// `data` is stored as little-endian 32-bit words. Returns `EINVAL` if /// the `data` length is not 4-byte aligned. - #[expect(dead_code)] fn read_emem(&mut self, bar: &Bar0, data: &mut [u8]) -> Result { if data.len() % 4 != 0 { return Err(EINVAL); @@ -95,4 +100,74 @@ impl Falcon { Ok(()) } + + /// Poll FSP for incoming data. + /// + /// Returns the size of available data in bytes, or 0 if no data is available. + /// + /// 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. + fn poll_msgq(&self, bar: &Bar0) -> u32 { + let head = bar.read(regs::NV_PFSP_MSGQ_HEAD::at(0)).val(); + let tail = bar.read(regs::NV_PFSP_MSGQ_TAIL::at(0)).val(); + + if head == tail { + return 0; + } + + // TAIL points at last DWORD written, so add 4 to get total size. + tail.saturating_sub(head).saturating_add(4) + } + + /// 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); + } + + self.write_emem(bar, packet)?; + + // Update queue pointers. TAIL points at the last DWORD written. + let tail_offset = u32::try_from(packet.len() - 4).map_err(|_| EINVAL)?; + bar.write( + Array::at(0), + regs::NV_PFSP_QUEUE_TAIL::zeroed().with_address(tail_offset), + ); + bar.write( + Array::at(0), + regs::NV_PFSP_QUEUE_HEAD::zeroed().with_address(0), + ); + + Ok(()) + } + + /// Reads the next message from FSP EMEM into a newly-allocated buffer and resets the queue + /// pointers. + /// + /// 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> { + let msg_size = read_poll_timeout( + || Ok(self.poll_msgq(bar)), + |&size| size > 0, + Delta::from_millis(10), + Delta::from_millis(FSP_MSG_TIMEOUT_MS), + ) + .map(num::u32_as_usize)?; + + let mut buffer = KVec::::new(); + buffer.resize(msg_size, 0, GFP_KERNEL)?; + + self.read_emem(bar, &mut buffer)?; + + // Reset message queue pointers after reading. + bar.write(Array::at(0), regs::NV_PFSP_MSGQ_TAIL::zeroed().with_val(0)); + bar.write(Array::at(0), regs::NV_PFSP_MSGQ_HEAD::zeroed().with_val(0)); + + Ok(buffer) + } } diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index e602c7860459c..ce2392ef2f8b4 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -579,6 +579,27 @@ register! { } } +// FSP (Foundation Security Processor) queue registers for Hopper/Blackwell Chain of Trust. +// These registers manage falcon EMEM communication queues. + +register! { + pub(crate) NV_PFSP_QUEUE_HEAD(u32)[8] @ 0x008f2c00 { + 31:0 address => u32; + } + + pub(crate) NV_PFSP_QUEUE_TAIL(u32)[8] @ 0x008f2c04 { + 31:0 address => u32; + } + + pub(crate) NV_PFSP_MSGQ_HEAD(u32)[8] @ 0x008f2c80 { + 31:0 val => u32; + } + + pub(crate) NV_PFSP_MSGQ_TAIL(u32)[8] @ 0x008f2c84 { + 31:0 val => u32; + } +} + // The modules below provide registers that are not identical on all supported chips. They should // only be used in HAL modules.