xref: /linux/drivers/gpu/nova-core/gsp/cmdq.rs (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
175f6b1deSAlistair Popple // SPDX-License-Identifier: GPL-2.0
275f6b1deSAlistair Popple 
375f6b1deSAlistair Popple use core::{
475f6b1deSAlistair Popple     cmp,
575f6b1deSAlistair Popple     mem,
675f6b1deSAlistair Popple     sync::atomic::{
775f6b1deSAlistair Popple         fence,
875f6b1deSAlistair Popple         Ordering, //
975f6b1deSAlistair Popple     }, //
1075f6b1deSAlistair Popple };
1175f6b1deSAlistair Popple 
1275f6b1deSAlistair Popple use kernel::{
1375f6b1deSAlistair Popple     device,
14*4fd4acd9SAlistair Popple     dma::{
15*4fd4acd9SAlistair Popple         CoherentAllocation,
16*4fd4acd9SAlistair Popple         DmaAddress, //
17*4fd4acd9SAlistair Popple     },
1875f6b1deSAlistair Popple     dma_write,
1975f6b1deSAlistair Popple     io::poll::read_poll_timeout,
2075f6b1deSAlistair Popple     prelude::*,
2175f6b1deSAlistair Popple     sync::aref::ARef,
2275f6b1deSAlistair Popple     time::Delta,
2375f6b1deSAlistair Popple     transmute::{
2475f6b1deSAlistair Popple         AsBytes,
2575f6b1deSAlistair Popple         FromBytes, //
2675f6b1deSAlistair Popple     },
2775f6b1deSAlistair Popple };
2875f6b1deSAlistair Popple 
2975f6b1deSAlistair Popple use crate::{
3075f6b1deSAlistair Popple     driver::Bar0,
3175f6b1deSAlistair Popple     gsp::{
3275f6b1deSAlistair Popple         fw::{
3375f6b1deSAlistair Popple             GspMsgElement,
3475f6b1deSAlistair Popple             MsgFunction,
3575f6b1deSAlistair Popple             MsgqRxHeader,
3675f6b1deSAlistair Popple             MsgqTxHeader, //
3775f6b1deSAlistair Popple         },
3875f6b1deSAlistair Popple         PteArray,
39*4fd4acd9SAlistair Popple         GSP_PAGE_SHIFT,
4075f6b1deSAlistair Popple         GSP_PAGE_SIZE, //
4175f6b1deSAlistair Popple     },
4275f6b1deSAlistair Popple     num,
4375f6b1deSAlistair Popple     regs,
4475f6b1deSAlistair Popple     sbuffer::SBufferIter, //
4575f6b1deSAlistair Popple };
4675f6b1deSAlistair Popple 
4775f6b1deSAlistair Popple /// Trait implemented by types representing a command to send to the GSP.
4875f6b1deSAlistair Popple ///
4975f6b1deSAlistair Popple /// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it
5075f6b1deSAlistair Popple /// needs to send a given command.
5175f6b1deSAlistair Popple ///
5275f6b1deSAlistair Popple /// [`CommandToGsp::init`] in particular is responsible for initializing the command directly
5375f6b1deSAlistair Popple /// into the space reserved for it in the command queue buffer.
5475f6b1deSAlistair Popple ///
5575f6b1deSAlistair Popple /// Some commands may be followed by a variable-length payload. For these, the
5675f6b1deSAlistair Popple /// [`CommandToGsp::variable_payload_len`] and [`CommandToGsp::init_variable_payload`] need to be
5775f6b1deSAlistair Popple /// defined as well.
5875f6b1deSAlistair Popple pub(crate) trait CommandToGsp {
5975f6b1deSAlistair Popple     /// Function identifying this command to the GSP.
6075f6b1deSAlistair Popple     const FUNCTION: MsgFunction;
6175f6b1deSAlistair Popple 
6275f6b1deSAlistair Popple     /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer.
6375f6b1deSAlistair Popple     type Command: FromBytes + AsBytes;
6475f6b1deSAlistair Popple 
6575f6b1deSAlistair Popple     /// Error type returned by [`CommandToGsp::init`].
6675f6b1deSAlistair Popple     type InitError;
6775f6b1deSAlistair Popple 
6875f6b1deSAlistair Popple     /// In-place command initializer responsible for filling the command in the command queue
6975f6b1deSAlistair Popple     /// buffer.
7075f6b1deSAlistair Popple     fn init(&self) -> impl Init<Self::Command, Self::InitError>;
7175f6b1deSAlistair Popple 
7275f6b1deSAlistair Popple     /// Size of the variable-length payload following the command structure generated by
7375f6b1deSAlistair Popple     /// [`CommandToGsp::init`].
7475f6b1deSAlistair Popple     ///
7575f6b1deSAlistair Popple     /// Most commands don't have a variable-length payload, so this is zero by default.
7675f6b1deSAlistair Popple     fn variable_payload_len(&self) -> usize {
7775f6b1deSAlistair Popple         0
7875f6b1deSAlistair Popple     }
7975f6b1deSAlistair Popple 
8075f6b1deSAlistair Popple     /// Method initializing the variable-length payload.
8175f6b1deSAlistair Popple     ///
8275f6b1deSAlistair Popple     /// The command buffer is circular, which means that we may need to jump back to its beginning
8375f6b1deSAlistair Popple     /// while in the middle of a command. For this reason, the variable-length payload is
8475f6b1deSAlistair Popple     /// initialized using a [`SBufferIter`].
8575f6b1deSAlistair Popple     ///
8675f6b1deSAlistair Popple     /// This method will receive a buffer of the length returned by
8775f6b1deSAlistair Popple     /// [`CommandToGsp::variable_payload_len`], and must write every single byte of it. Leaving
8875f6b1deSAlistair Popple     /// unwritten space will lead to an error.
8975f6b1deSAlistair Popple     ///
9075f6b1deSAlistair Popple     /// Most commands don't have a variable-length payload, so this does nothing by default.
9175f6b1deSAlistair Popple     fn init_variable_payload(
9275f6b1deSAlistair Popple         &self,
9375f6b1deSAlistair Popple         _dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
9475f6b1deSAlistair Popple     ) -> Result {
9575f6b1deSAlistair Popple         Ok(())
9675f6b1deSAlistair Popple     }
9775f6b1deSAlistair Popple }
9875f6b1deSAlistair Popple 
9975f6b1deSAlistair Popple /// Trait representing messages received from the GSP.
10075f6b1deSAlistair Popple ///
10175f6b1deSAlistair Popple /// This trait tells [`Cmdq::receive_msg`] how it can receive a given type of message.
10275f6b1deSAlistair Popple pub(crate) trait MessageFromGsp: Sized {
10375f6b1deSAlistair Popple     /// Function identifying this message from the GSP.
10475f6b1deSAlistair Popple     const FUNCTION: MsgFunction;
10575f6b1deSAlistair Popple 
10675f6b1deSAlistair Popple     /// Error type returned by [`MessageFromGsp::read`].
10775f6b1deSAlistair Popple     type InitError;
10875f6b1deSAlistair Popple 
10975f6b1deSAlistair Popple     /// Type containing the raw message to be read from the message queue.
11075f6b1deSAlistair Popple     type Message: FromBytes;
11175f6b1deSAlistair Popple 
11275f6b1deSAlistair Popple     /// Method reading the message from the message queue and returning it.
11375f6b1deSAlistair Popple     ///
11475f6b1deSAlistair Popple     /// From a `Self::Message` and a [`SBufferIter`], constructs an instance of `Self` and returns
11575f6b1deSAlistair Popple     /// it.
11675f6b1deSAlistair Popple     fn read(
11775f6b1deSAlistair Popple         msg: &Self::Message,
11875f6b1deSAlistair Popple         sbuffer: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>,
11975f6b1deSAlistair Popple     ) -> Result<Self, Self::InitError>;
12075f6b1deSAlistair Popple }
12175f6b1deSAlistair Popple 
12275f6b1deSAlistair Popple /// Number of GSP pages making the [`Msgq`].
12375f6b1deSAlistair Popple pub(crate) const MSGQ_NUM_PAGES: u32 = 0x3f;
12475f6b1deSAlistair Popple 
12575f6b1deSAlistair Popple /// Circular buffer of a [`Msgq`].
12675f6b1deSAlistair Popple ///
12775f6b1deSAlistair Popple /// This area of memory is to be shared between the driver and the GSP to exchange commands or
12875f6b1deSAlistair Popple /// messages.
12975f6b1deSAlistair Popple #[repr(C, align(0x1000))]
13075f6b1deSAlistair Popple #[derive(Debug)]
13175f6b1deSAlistair Popple struct MsgqData {
13275f6b1deSAlistair Popple     data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)],
13375f6b1deSAlistair Popple }
13475f6b1deSAlistair Popple 
13575f6b1deSAlistair Popple // Annoyingly we are forced to use a literal to specify the alignment of
13675f6b1deSAlistair Popple // `MsgqData`, so check that it corresponds to the actual GSP page size here.
13775f6b1deSAlistair Popple static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
13875f6b1deSAlistair Popple 
13975f6b1deSAlistair Popple /// Unidirectional message queue.
14075f6b1deSAlistair Popple ///
14175f6b1deSAlistair Popple /// Contains the data for a message queue, that either the driver or GSP writes to.
14275f6b1deSAlistair Popple ///
14375f6b1deSAlistair Popple /// Note that while the write pointer of `tx` corresponds to the `msgq` of the same instance, the
14475f6b1deSAlistair Popple /// read pointer of `rx` actually refers to the `Msgq` owned by the other side.
14575f6b1deSAlistair Popple /// This design ensures that only the driver or GSP ever writes to a given instance of this struct.
14675f6b1deSAlistair Popple #[repr(C)]
14775f6b1deSAlistair Popple // There is no struct defined for this in the open-gpu-kernel-source headers.
14875f6b1deSAlistair Popple // Instead it is defined by code in `GspMsgQueuesInit()`.
14975f6b1deSAlistair Popple struct Msgq {
15075f6b1deSAlistair Popple     /// Header for sending messages, including the write pointer.
15175f6b1deSAlistair Popple     tx: MsgqTxHeader,
15275f6b1deSAlistair Popple     /// Header for receiving messages, including the read pointer.
15375f6b1deSAlistair Popple     rx: MsgqRxHeader,
15475f6b1deSAlistair Popple     /// The message queue proper.
15575f6b1deSAlistair Popple     msgq: MsgqData,
15675f6b1deSAlistair Popple }
15775f6b1deSAlistair Popple 
15875f6b1deSAlistair Popple /// Structure shared between the driver and the GSP and containing the command and message queues.
15975f6b1deSAlistair Popple #[repr(C)]
16075f6b1deSAlistair Popple struct GspMem {
16175f6b1deSAlistair Popple     /// Self-mapping page table entries.
16275f6b1deSAlistair Popple     ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>,
16375f6b1deSAlistair Popple     /// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
16475f6b1deSAlistair Popple     /// write and read pointers that the CPU updates.
16575f6b1deSAlistair Popple     ///
16675f6b1deSAlistair Popple     /// This member is read-only for the GSP.
16775f6b1deSAlistair Popple     cpuq: Msgq,
16875f6b1deSAlistair Popple     /// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
16975f6b1deSAlistair Popple     /// write and read pointers that the GSP updates.
17075f6b1deSAlistair Popple     ///
17175f6b1deSAlistair Popple     /// This member is read-only for the driver.
17275f6b1deSAlistair Popple     gspq: Msgq,
17375f6b1deSAlistair Popple }
17475f6b1deSAlistair Popple 
17575f6b1deSAlistair Popple // SAFETY: These structs don't meet the no-padding requirements of AsBytes but
17675f6b1deSAlistair Popple // that is not a problem because they are not used outside the kernel.
17775f6b1deSAlistair Popple unsafe impl AsBytes for GspMem {}
17875f6b1deSAlistair Popple 
17975f6b1deSAlistair Popple // SAFETY: These structs don't meet the no-padding requirements of FromBytes but
18075f6b1deSAlistair Popple // that is not a problem because they are not used outside the kernel.
18175f6b1deSAlistair Popple unsafe impl FromBytes for GspMem {}
18275f6b1deSAlistair Popple 
18375f6b1deSAlistair Popple /// Wrapper around [`GspMem`] to share it with the GPU using a [`CoherentAllocation`].
18475f6b1deSAlistair Popple ///
18575f6b1deSAlistair Popple /// This provides the low-level functionality to communicate with the GSP, including allocation of
18675f6b1deSAlistair Popple /// queue space to write messages to and management of read/write pointers.
18775f6b1deSAlistair Popple ///
18875f6b1deSAlistair Popple /// This is shared with the GSP, with clear ownership rules regarding the command queues:
18975f6b1deSAlistair Popple ///
19075f6b1deSAlistair Popple /// * The driver owns (i.e. can write to) the part of the CPU message queue between the CPU write
19175f6b1deSAlistair Popple ///   pointer and the GSP read pointer. This region is returned by [`Self::driver_write_area`].
19275f6b1deSAlistair Popple /// * The driver owns (i.e. can read from) the part of the GSP message queue between the CPU read
19375f6b1deSAlistair Popple ///   pointer and the GSP write pointer. This region is returned by [`Self::driver_read_area`].
19475f6b1deSAlistair Popple struct DmaGspMem(CoherentAllocation<GspMem>);
19575f6b1deSAlistair Popple 
19675f6b1deSAlistair Popple impl DmaGspMem {
19775f6b1deSAlistair Popple     /// Allocate a new instance and map it for `dev`.
19875f6b1deSAlistair Popple     fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
19975f6b1deSAlistair Popple         const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();
20075f6b1deSAlistair Popple         const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();
20175f6b1deSAlistair Popple 
20275f6b1deSAlistair Popple         let gsp_mem =
20375f6b1deSAlistair Popple             CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
20475f6b1deSAlistair Popple         dma_write!(gsp_mem[0].ptes = PteArray::new(gsp_mem.dma_handle())?)?;
20575f6b1deSAlistair Popple         dma_write!(gsp_mem[0].cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES))?;
20675f6b1deSAlistair Popple         dma_write!(gsp_mem[0].cpuq.rx = MsgqRxHeader::new())?;
20775f6b1deSAlistair Popple 
20875f6b1deSAlistair Popple         Ok(Self(gsp_mem))
20975f6b1deSAlistair Popple     }
21075f6b1deSAlistair Popple 
21175f6b1deSAlistair Popple     /// Returns the region of the CPU message queue that the driver is currently allowed to write
21275f6b1deSAlistair Popple     /// to.
21375f6b1deSAlistair Popple     ///
21475f6b1deSAlistair Popple     /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
21575f6b1deSAlistair Popple     /// that case the second slice will have a non-zero length.
21675f6b1deSAlistair Popple     fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {
21775f6b1deSAlistair Popple         let tx = self.cpu_write_ptr() as usize;
21875f6b1deSAlistair Popple         let rx = self.gsp_read_ptr() as usize;
21975f6b1deSAlistair Popple 
22075f6b1deSAlistair Popple         // SAFETY:
22175f6b1deSAlistair Popple         // - The `CoherentAllocation` contains exactly one object.
22275f6b1deSAlistair Popple         // - We will only access the driver-owned part of the shared memory.
22375f6b1deSAlistair Popple         // - Per the safety statement of the function, no concurrent access will be performed.
22475f6b1deSAlistair Popple         let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0];
22575f6b1deSAlistair Popple         // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `<= MSGQ_NUM_PAGES`.
22675f6b1deSAlistair Popple         let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx);
22775f6b1deSAlistair Popple 
22875f6b1deSAlistair Popple         if rx <= tx {
22975f6b1deSAlistair Popple             // The area from `tx` up to the end of the ring, and from the beginning of the ring up
23075f6b1deSAlistair Popple             // to `rx`, minus one unit, belongs to the driver.
23175f6b1deSAlistair Popple             if rx == 0 {
23275f6b1deSAlistair Popple                 let last = after_tx.len() - 1;
23375f6b1deSAlistair Popple                 (&mut after_tx[..last], &mut before_tx[0..0])
23475f6b1deSAlistair Popple             } else {
23575f6b1deSAlistair Popple                 (after_tx, &mut before_tx[..rx])
23675f6b1deSAlistair Popple             }
23775f6b1deSAlistair Popple         } else {
23875f6b1deSAlistair Popple             // The area from `tx` to `rx`, minus one unit, belongs to the driver.
23975f6b1deSAlistair Popple             //
24075f6b1deSAlistair Popple             // PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are
24175f6b1deSAlistair Popple             // `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`.
24275f6b1deSAlistair Popple             (after_tx.split_at_mut(rx - tx).0, &mut before_tx[0..0])
24375f6b1deSAlistair Popple         }
24475f6b1deSAlistair Popple     }
24575f6b1deSAlistair Popple 
24675f6b1deSAlistair Popple     /// Returns the region of the GSP message queue that the driver is currently allowed to read
24775f6b1deSAlistair Popple     /// from.
24875f6b1deSAlistair Popple     ///
24975f6b1deSAlistair Popple     /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
25075f6b1deSAlistair Popple     /// that case the second slice will have a non-zero length.
25175f6b1deSAlistair Popple     fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {
25275f6b1deSAlistair Popple         let tx = self.gsp_write_ptr() as usize;
25375f6b1deSAlistair Popple         let rx = self.cpu_read_ptr() as usize;
25475f6b1deSAlistair Popple 
25575f6b1deSAlistair Popple         // SAFETY:
25675f6b1deSAlistair Popple         // - The `CoherentAllocation` contains exactly one object.
25775f6b1deSAlistair Popple         // - We will only access the driver-owned part of the shared memory.
25875f6b1deSAlistair Popple         // - Per the safety statement of the function, no concurrent access will be performed.
25975f6b1deSAlistair Popple         let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0];
26075f6b1deSAlistair Popple         // PANIC: per the invariant of `cpu_read_ptr`, `xx` is `<= MSGQ_NUM_PAGES`.
26175f6b1deSAlistair Popple         let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx);
26275f6b1deSAlistair Popple 
26375f6b1deSAlistair Popple         match tx.cmp(&rx) {
26475f6b1deSAlistair Popple             cmp::Ordering::Equal => (&after_rx[0..0], &after_rx[0..0]),
26575f6b1deSAlistair Popple             cmp::Ordering::Greater => (&after_rx[..tx], &before_rx[0..0]),
26675f6b1deSAlistair Popple             cmp::Ordering::Less => (after_rx, &before_rx[..tx]),
26775f6b1deSAlistair Popple         }
26875f6b1deSAlistair Popple     }
26975f6b1deSAlistair Popple 
27075f6b1deSAlistair Popple     /// Allocates a region on the command queue that is large enough to send a command of `size`
27175f6b1deSAlistair Popple     /// bytes.
27275f6b1deSAlistair Popple     ///
27375f6b1deSAlistair Popple     /// This returns a [`GspCommand`] ready to be written to by the caller.
27475f6b1deSAlistair Popple     ///
27575f6b1deSAlistair Popple     /// # Errors
27675f6b1deSAlistair Popple     ///
27775f6b1deSAlistair Popple     /// - `EAGAIN` if the driver area is too small to hold the requested command.
27875f6b1deSAlistair Popple     /// - `EIO` if the command header is not properly aligned.
27975f6b1deSAlistair Popple     fn allocate_command(&mut self, size: usize) -> Result<GspCommand<'_>> {
28075f6b1deSAlistair Popple         // Get the current writable area as an array of bytes.
28175f6b1deSAlistair Popple         let (slice_1, slice_2) = {
28275f6b1deSAlistair Popple             let (slice_1, slice_2) = self.driver_write_area();
28375f6b1deSAlistair Popple 
28475f6b1deSAlistair Popple             #[allow(clippy::incompatible_msrv)]
28575f6b1deSAlistair Popple             (slice_1.as_flattened_mut(), slice_2.as_flattened_mut())
28675f6b1deSAlistair Popple         };
28775f6b1deSAlistair Popple 
28875f6b1deSAlistair Popple         // If the GSP is still processing previous messages the shared region
28975f6b1deSAlistair Popple         // may be full in which case we will have to retry once the GSP has
29075f6b1deSAlistair Popple         // processed the existing commands.
29175f6b1deSAlistair Popple         if size_of::<GspMsgElement>() + size > slice_1.len() + slice_2.len() {
29275f6b1deSAlistair Popple             return Err(EAGAIN);
29375f6b1deSAlistair Popple         }
29475f6b1deSAlistair Popple 
29575f6b1deSAlistair Popple         // Extract area for the `GspMsgElement`.
29675f6b1deSAlistair Popple         let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
29775f6b1deSAlistair Popple 
29875f6b1deSAlistair Popple         // Create the contents area.
29975f6b1deSAlistair Popple         let (slice_1, slice_2) = if slice_1.len() > size {
30075f6b1deSAlistair Popple             // Contents fits entirely in `slice_1`.
30175f6b1deSAlistair Popple             (&mut slice_1[..size], &mut slice_2[0..0])
30275f6b1deSAlistair Popple         } else {
30375f6b1deSAlistair Popple             // Need all of `slice_1` and some of `slice_2`.
30475f6b1deSAlistair Popple             let slice_2_len = size - slice_1.len();
30575f6b1deSAlistair Popple             (slice_1, &mut slice_2[..slice_2_len])
30675f6b1deSAlistair Popple         };
30775f6b1deSAlistair Popple 
30875f6b1deSAlistair Popple         Ok(GspCommand {
30975f6b1deSAlistair Popple             header,
31075f6b1deSAlistair Popple             contents: (slice_1, slice_2),
31175f6b1deSAlistair Popple         })
31275f6b1deSAlistair Popple     }
31375f6b1deSAlistair Popple 
31475f6b1deSAlistair Popple     // Returns the index of the memory page the GSP will write the next message to.
31575f6b1deSAlistair Popple     //
31675f6b1deSAlistair Popple     // # Invariants
31775f6b1deSAlistair Popple     //
31875f6b1deSAlistair Popple     // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
31975f6b1deSAlistair Popple     fn gsp_write_ptr(&self) -> u32 {
32075f6b1deSAlistair Popple         let gsp_mem = self.0.start_ptr();
32175f6b1deSAlistair Popple 
32275f6b1deSAlistair Popple         // SAFETY:
32375f6b1deSAlistair Popple         //  - The 'CoherentAllocation' contains at least one object.
32475f6b1deSAlistair Popple         //  - By the invariants of `CoherentAllocation` the pointer is valid.
32575f6b1deSAlistair Popple         (unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES)
32675f6b1deSAlistair Popple     }
32775f6b1deSAlistair Popple 
32875f6b1deSAlistair Popple     // Returns the index of the memory page the GSP will read the next command from.
32975f6b1deSAlistair Popple     //
33075f6b1deSAlistair Popple     // # Invariants
33175f6b1deSAlistair Popple     //
33275f6b1deSAlistair Popple     // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
33375f6b1deSAlistair Popple     fn gsp_read_ptr(&self) -> u32 {
33475f6b1deSAlistair Popple         let gsp_mem = self.0.start_ptr();
33575f6b1deSAlistair Popple 
33675f6b1deSAlistair Popple         // SAFETY:
33775f6b1deSAlistair Popple         //  - The 'CoherentAllocation' contains at least one object.
33875f6b1deSAlistair Popple         //  - By the invariants of `CoherentAllocation` the pointer is valid.
33975f6b1deSAlistair Popple         (unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES)
34075f6b1deSAlistair Popple     }
34175f6b1deSAlistair Popple 
34275f6b1deSAlistair Popple     // Returns the index of the memory page the CPU can read the next message from.
34375f6b1deSAlistair Popple     //
34475f6b1deSAlistair Popple     // # Invariants
34575f6b1deSAlistair Popple     //
34675f6b1deSAlistair Popple     // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
34775f6b1deSAlistair Popple     fn cpu_read_ptr(&self) -> u32 {
34875f6b1deSAlistair Popple         let gsp_mem = self.0.start_ptr();
34975f6b1deSAlistair Popple 
35075f6b1deSAlistair Popple         // SAFETY:
35175f6b1deSAlistair Popple         //  - The ['CoherentAllocation'] contains at least one object.
35275f6b1deSAlistair Popple         //  - By the invariants of CoherentAllocation the pointer is valid.
35375f6b1deSAlistair Popple         (unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES)
35475f6b1deSAlistair Popple     }
35575f6b1deSAlistair Popple 
35675f6b1deSAlistair Popple     // Informs the GSP that it can send `elem_count` new pages into the message queue.
35775f6b1deSAlistair Popple     fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
35875f6b1deSAlistair Popple         let rptr = self.cpu_read_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES;
35975f6b1deSAlistair Popple 
36075f6b1deSAlistair Popple         // Ensure read pointer is properly ordered.
36175f6b1deSAlistair Popple         fence(Ordering::SeqCst);
36275f6b1deSAlistair Popple 
36375f6b1deSAlistair Popple         let gsp_mem = self.0.start_ptr_mut();
36475f6b1deSAlistair Popple 
36575f6b1deSAlistair Popple         // SAFETY:
36675f6b1deSAlistair Popple         //  - The 'CoherentAllocation' contains at least one object.
36775f6b1deSAlistair Popple         //  - By the invariants of `CoherentAllocation` the pointer is valid.
36875f6b1deSAlistair Popple         unsafe { (*gsp_mem).cpuq.rx.set_read_ptr(rptr) };
36975f6b1deSAlistair Popple     }
37075f6b1deSAlistair Popple 
37175f6b1deSAlistair Popple     // Returns the index of the memory page the CPU can write the next command to.
37275f6b1deSAlistair Popple     //
37375f6b1deSAlistair Popple     // # Invariants
37475f6b1deSAlistair Popple     //
37575f6b1deSAlistair Popple     // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
37675f6b1deSAlistair Popple     fn cpu_write_ptr(&self) -> u32 {
37775f6b1deSAlistair Popple         let gsp_mem = self.0.start_ptr();
37875f6b1deSAlistair Popple 
37975f6b1deSAlistair Popple         // SAFETY:
38075f6b1deSAlistair Popple         //  - The 'CoherentAllocation' contains at least one object.
38175f6b1deSAlistair Popple         //  - By the invariants of `CoherentAllocation` the pointer is valid.
38275f6b1deSAlistair Popple         (unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES)
38375f6b1deSAlistair Popple     }
38475f6b1deSAlistair Popple 
38575f6b1deSAlistair Popple     // Informs the GSP that it can process `elem_count` new pages from the command queue.
38675f6b1deSAlistair Popple     fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
38775f6b1deSAlistair Popple         let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES;
38875f6b1deSAlistair Popple         let gsp_mem = self.0.start_ptr_mut();
38975f6b1deSAlistair Popple 
39075f6b1deSAlistair Popple         // SAFETY:
39175f6b1deSAlistair Popple         //  - The 'CoherentAllocation' contains at least one object.
39275f6b1deSAlistair Popple         //  - By the invariants of `CoherentAllocation` the pointer is valid.
39375f6b1deSAlistair Popple         unsafe { (*gsp_mem).cpuq.tx.set_write_ptr(wptr) };
39475f6b1deSAlistair Popple 
39575f6b1deSAlistair Popple         // Ensure all command data is visible before triggering the GSP read.
39675f6b1deSAlistair Popple         fence(Ordering::SeqCst);
39775f6b1deSAlistair Popple     }
39875f6b1deSAlistair Popple }
39975f6b1deSAlistair Popple 
40075f6b1deSAlistair Popple /// A command ready to be sent on the command queue.
40175f6b1deSAlistair Popple ///
40275f6b1deSAlistair Popple /// This is the type returned by [`DmaGspMem::allocate_command`].
40375f6b1deSAlistair Popple struct GspCommand<'a> {
40475f6b1deSAlistair Popple     // Writable reference to the header of the command.
40575f6b1deSAlistair Popple     header: &'a mut GspMsgElement,
40675f6b1deSAlistair Popple     // Writable slices to the contents of the command. The second slice is zero unless the command
40775f6b1deSAlistair Popple     // loops over the command queue.
40875f6b1deSAlistair Popple     contents: (&'a mut [u8], &'a mut [u8]),
40975f6b1deSAlistair Popple }
41075f6b1deSAlistair Popple 
41175f6b1deSAlistair Popple /// A message ready to be processed from the message queue.
41275f6b1deSAlistair Popple ///
41375f6b1deSAlistair Popple /// This is the type returned by [`Cmdq::wait_for_msg`].
41475f6b1deSAlistair Popple struct GspMessage<'a> {
41575f6b1deSAlistair Popple     // Reference to the header of the message.
41675f6b1deSAlistair Popple     header: &'a GspMsgElement,
41775f6b1deSAlistair Popple     // Slices to the contents of the message. The second slice is zero unless the message loops
41875f6b1deSAlistair Popple     // over the message queue.
41975f6b1deSAlistair Popple     contents: (&'a [u8], &'a [u8]),
42075f6b1deSAlistair Popple }
42175f6b1deSAlistair Popple 
42275f6b1deSAlistair Popple /// GSP command queue.
42375f6b1deSAlistair Popple ///
42475f6b1deSAlistair Popple /// Provides the ability to send commands and receive messages from the GSP using a shared memory
42575f6b1deSAlistair Popple /// area.
42675f6b1deSAlistair Popple pub(crate) struct Cmdq {
42775f6b1deSAlistair Popple     /// Device this command queue belongs to.
42875f6b1deSAlistair Popple     dev: ARef<device::Device>,
42975f6b1deSAlistair Popple     /// Current command sequence number.
43075f6b1deSAlistair Popple     seq: u32,
43175f6b1deSAlistair Popple     /// Memory area shared with the GSP for communicating commands and messages.
43275f6b1deSAlistair Popple     gsp_mem: DmaGspMem,
43375f6b1deSAlistair Popple }
43475f6b1deSAlistair Popple 
43575f6b1deSAlistair Popple impl Cmdq {
436*4fd4acd9SAlistair Popple     /// Offset of the data after the PTEs.
437*4fd4acd9SAlistair Popple     const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq);
438*4fd4acd9SAlistair Popple 
439*4fd4acd9SAlistair Popple     /// Offset of command queue ring buffer.
440*4fd4acd9SAlistair Popple     pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq)
441*4fd4acd9SAlistair Popple         + core::mem::offset_of!(Msgq, msgq)
442*4fd4acd9SAlistair Popple         - Self::POST_PTE_OFFSET;
443*4fd4acd9SAlistair Popple 
444*4fd4acd9SAlistair Popple     /// Offset of message queue ring buffer.
445*4fd4acd9SAlistair Popple     pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq)
446*4fd4acd9SAlistair Popple         + core::mem::offset_of!(Msgq, msgq)
447*4fd4acd9SAlistair Popple         - Self::POST_PTE_OFFSET;
448*4fd4acd9SAlistair Popple 
449*4fd4acd9SAlistair Popple     /// Number of page table entries for the GSP shared region.
450*4fd4acd9SAlistair Popple     pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT;
451*4fd4acd9SAlistair Popple 
45275f6b1deSAlistair Popple     /// Creates a new command queue for `dev`.
45375f6b1deSAlistair Popple     pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> {
45475f6b1deSAlistair Popple         let gsp_mem = DmaGspMem::new(dev)?;
45575f6b1deSAlistair Popple 
45675f6b1deSAlistair Popple         Ok(Cmdq {
45775f6b1deSAlistair Popple             dev: dev.into(),
45875f6b1deSAlistair Popple             seq: 0,
45975f6b1deSAlistair Popple             gsp_mem,
46075f6b1deSAlistair Popple         })
46175f6b1deSAlistair Popple     }
46275f6b1deSAlistair Popple 
46375f6b1deSAlistair Popple     /// Computes the checksum for the message pointed to by `it`.
46475f6b1deSAlistair Popple     ///
46575f6b1deSAlistair Popple     /// A message is made of several parts, so `it` is an iterator over byte slices representing
46675f6b1deSAlistair Popple     /// these parts.
46775f6b1deSAlistair Popple     fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 {
46875f6b1deSAlistair Popple         let sum64 = it
46975f6b1deSAlistair Popple             .enumerate()
47075f6b1deSAlistair Popple             .map(|(idx, byte)| (((idx % 8) * 8) as u32, byte))
47175f6b1deSAlistair Popple             .fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol));
47275f6b1deSAlistair Popple 
47375f6b1deSAlistair Popple         ((sum64 >> 32) as u32) ^ (sum64 as u32)
47475f6b1deSAlistair Popple     }
47575f6b1deSAlistair Popple 
47675f6b1deSAlistair Popple     /// Notifies the GSP that we have updated the command queue pointers.
47775f6b1deSAlistair Popple     fn notify_gsp(bar: &Bar0) {
47875f6b1deSAlistair Popple         regs::NV_PGSP_QUEUE_HEAD::default()
47975f6b1deSAlistair Popple             .set_address(0)
48075f6b1deSAlistair Popple             .write(bar);
48175f6b1deSAlistair Popple     }
48275f6b1deSAlistair Popple 
48375f6b1deSAlistair Popple     /// Sends `command` to the GSP.
48475f6b1deSAlistair Popple     ///
48575f6b1deSAlistair Popple     /// # Errors
48675f6b1deSAlistair Popple     ///
48775f6b1deSAlistair Popple     /// - `EAGAIN` if there was not enough space in the command queue to send the command.
48875f6b1deSAlistair Popple     /// - `EIO` if the variable payload requested by the command has not been entirely
48975f6b1deSAlistair Popple     ///   written to by its [`CommandToGsp::init_variable_payload`] method.
49075f6b1deSAlistair Popple     ///
49175f6b1deSAlistair Popple     /// Error codes returned by the command initializers are propagated as-is.
49275f6b1deSAlistair Popple     pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result
49375f6b1deSAlistair Popple     where
49475f6b1deSAlistair Popple         M: CommandToGsp,
49575f6b1deSAlistair Popple         // This allows all error types, including `Infallible`, to be used for `M::InitError`.
49675f6b1deSAlistair Popple         Error: From<M::InitError>,
49775f6b1deSAlistair Popple     {
49875f6b1deSAlistair Popple         let command_size = size_of::<M::Command>() + command.variable_payload_len();
49975f6b1deSAlistair Popple         let dst = self.gsp_mem.allocate_command(command_size)?;
50075f6b1deSAlistair Popple 
50175f6b1deSAlistair Popple         // Extract area for the command itself.
50275f6b1deSAlistair Popple         let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;
50375f6b1deSAlistair Popple 
50475f6b1deSAlistair Popple         // Fill the header and command in-place.
50575f6b1deSAlistair Popple         let msg_element = GspMsgElement::init(self.seq, command_size, M::FUNCTION);
50675f6b1deSAlistair Popple         // SAFETY: `msg_header` and `cmd` are valid references, and not touched if the initializer
50775f6b1deSAlistair Popple         // fails.
50875f6b1deSAlistair Popple         unsafe {
50975f6b1deSAlistair Popple             msg_element.__init(core::ptr::from_mut(dst.header))?;
51075f6b1deSAlistair Popple             command.init().__init(core::ptr::from_mut(cmd))?;
51175f6b1deSAlistair Popple         }
51275f6b1deSAlistair Popple 
51375f6b1deSAlistair Popple         // Fill the variable-length payload.
51475f6b1deSAlistair Popple         if command_size > size_of::<M::Command>() {
51575f6b1deSAlistair Popple             let mut sbuffer =
51675f6b1deSAlistair Popple                 SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]);
51775f6b1deSAlistair Popple             command.init_variable_payload(&mut sbuffer)?;
51875f6b1deSAlistair Popple 
51975f6b1deSAlistair Popple             if !sbuffer.is_empty() {
52075f6b1deSAlistair Popple                 return Err(EIO);
52175f6b1deSAlistair Popple             }
52275f6b1deSAlistair Popple         }
52375f6b1deSAlistair Popple 
52475f6b1deSAlistair Popple         // Compute checksum now that the whole message is ready.
52575f6b1deSAlistair Popple         dst.header
52675f6b1deSAlistair Popple             .set_checksum(Cmdq::calculate_checksum(SBufferIter::new_reader([
52775f6b1deSAlistair Popple                 dst.header.as_bytes(),
52875f6b1deSAlistair Popple                 dst.contents.0,
52975f6b1deSAlistair Popple                 dst.contents.1,
53075f6b1deSAlistair Popple             ])));
53175f6b1deSAlistair Popple 
53275f6b1deSAlistair Popple         dev_dbg!(
53375f6b1deSAlistair Popple             &self.dev,
53475f6b1deSAlistair Popple             "GSP RPC: send: seq# {}, function={}, length=0x{:x}\n",
53575f6b1deSAlistair Popple             self.seq,
53675f6b1deSAlistair Popple             M::FUNCTION,
53775f6b1deSAlistair Popple             dst.header.length(),
53875f6b1deSAlistair Popple         );
53975f6b1deSAlistair Popple 
54075f6b1deSAlistair Popple         // All set - update the write pointer and inform the GSP of the new command.
54175f6b1deSAlistair Popple         let elem_count = dst.header.element_count();
54275f6b1deSAlistair Popple         self.seq += 1;
54375f6b1deSAlistair Popple         self.gsp_mem.advance_cpu_write_ptr(elem_count);
54475f6b1deSAlistair Popple         Cmdq::notify_gsp(bar);
54575f6b1deSAlistair Popple 
54675f6b1deSAlistair Popple         Ok(())
54775f6b1deSAlistair Popple     }
54875f6b1deSAlistair Popple 
54975f6b1deSAlistair Popple     /// Wait for a message to become available on the message queue.
55075f6b1deSAlistair Popple     ///
55175f6b1deSAlistair Popple     /// This works purely at the transport layer and does not interpret or validate the message
55275f6b1deSAlistair Popple     /// beyond the advertised length in its [`GspMsgElement`].
55375f6b1deSAlistair Popple     ///
55475f6b1deSAlistair Popple     /// This method returns:
55575f6b1deSAlistair Popple     ///
55675f6b1deSAlistair Popple     /// - A reference to the [`GspMsgElement`] of the message,
55775f6b1deSAlistair Popple     /// - Two byte slices with the contents of the message. The second slice is empty unless the
55875f6b1deSAlistair Popple     ///   message loops across the message queue.
55975f6b1deSAlistair Popple     ///
56075f6b1deSAlistair Popple     /// # Errors
56175f6b1deSAlistair Popple     ///
56275f6b1deSAlistair Popple     /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
56375f6b1deSAlistair Popple     /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the
56475f6b1deSAlistair Popple     ///   message queue.
56575f6b1deSAlistair Popple     ///
56675f6b1deSAlistair Popple     /// Error codes returned by the message constructor are propagated as-is.
56775f6b1deSAlistair Popple     fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
56875f6b1deSAlistair Popple         // Wait for a message to arrive from the GSP.
56975f6b1deSAlistair Popple         let (slice_1, slice_2) = read_poll_timeout(
57075f6b1deSAlistair Popple             || Ok(self.gsp_mem.driver_read_area()),
57175f6b1deSAlistair Popple             |driver_area| !driver_area.0.is_empty(),
57275f6b1deSAlistair Popple             Delta::from_millis(1),
57375f6b1deSAlistair Popple             timeout,
57475f6b1deSAlistair Popple         )
57575f6b1deSAlistair Popple         .map(|(slice_1, slice_2)| {
57675f6b1deSAlistair Popple             #[allow(clippy::incompatible_msrv)]
57775f6b1deSAlistair Popple             (slice_1.as_flattened(), slice_2.as_flattened())
57875f6b1deSAlistair Popple         })?;
57975f6b1deSAlistair Popple 
58075f6b1deSAlistair Popple         // Extract the `GspMsgElement`.
58175f6b1deSAlistair Popple         let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?;
58275f6b1deSAlistair Popple 
58375f6b1deSAlistair Popple         dev_dbg!(
58475f6b1deSAlistair Popple             self.dev,
58575f6b1deSAlistair Popple             "GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n",
58675f6b1deSAlistair Popple             header.sequence(),
58775f6b1deSAlistair Popple             header.function(),
58875f6b1deSAlistair Popple             header.length(),
58975f6b1deSAlistair Popple         );
59075f6b1deSAlistair Popple 
59175f6b1deSAlistair Popple         // Check that the driver read area is large enough for the message.
59275f6b1deSAlistair Popple         if slice_1.len() + slice_2.len() < header.length() {
59375f6b1deSAlistair Popple             return Err(EIO);
59475f6b1deSAlistair Popple         }
59575f6b1deSAlistair Popple 
59675f6b1deSAlistair Popple         // Cut the message slices down to the actual length of the message.
59775f6b1deSAlistair Popple         let (slice_1, slice_2) = if slice_1.len() > header.length() {
59875f6b1deSAlistair Popple             // PANIC: we checked above that `slice_1` is at least as long as `msg_header.length()`.
59975f6b1deSAlistair Popple             (slice_1.split_at(header.length()).0, &slice_2[0..0])
60075f6b1deSAlistair Popple         } else {
60175f6b1deSAlistair Popple             (
60275f6b1deSAlistair Popple                 slice_1,
60375f6b1deSAlistair Popple                 // PANIC: we checked above that `slice_1.len() + slice_2.len()` is at least as
60475f6b1deSAlistair Popple                 // large as `msg_header.length()`.
60575f6b1deSAlistair Popple                 slice_2.split_at(header.length() - slice_1.len()).0,
60675f6b1deSAlistair Popple             )
60775f6b1deSAlistair Popple         };
60875f6b1deSAlistair Popple 
60975f6b1deSAlistair Popple         // Validate checksum.
61075f6b1deSAlistair Popple         if Cmdq::calculate_checksum(SBufferIter::new_reader([
61175f6b1deSAlistair Popple             header.as_bytes(),
61275f6b1deSAlistair Popple             slice_1,
61375f6b1deSAlistair Popple             slice_2,
61475f6b1deSAlistair Popple         ])) != 0
61575f6b1deSAlistair Popple         {
61675f6b1deSAlistair Popple             dev_err!(
61775f6b1deSAlistair Popple                 self.dev,
61875f6b1deSAlistair Popple                 "GSP RPC: receive: Call {} - bad checksum",
61975f6b1deSAlistair Popple                 header.sequence()
62075f6b1deSAlistair Popple             );
62175f6b1deSAlistair Popple             return Err(EIO);
62275f6b1deSAlistair Popple         }
62375f6b1deSAlistair Popple 
62475f6b1deSAlistair Popple         Ok(GspMessage {
62575f6b1deSAlistair Popple             header,
62675f6b1deSAlistair Popple             contents: (slice_1, slice_2),
62775f6b1deSAlistair Popple         })
62875f6b1deSAlistair Popple     }
62975f6b1deSAlistair Popple 
63075f6b1deSAlistair Popple     /// Receive a message from the GSP.
63175f6b1deSAlistair Popple     ///
63275f6b1deSAlistair Popple     /// `init` is a closure tasked with processing the message. It receives a reference to the
63375f6b1deSAlistair Popple     /// message in the message queue, and a [`SBufferIter`] pointing to its variable-length
63475f6b1deSAlistair Popple     /// payload, if any.
63575f6b1deSAlistair Popple     ///
63675f6b1deSAlistair Popple     /// The expected message is specified using the `M` generic parameter. If the pending message
63775f6b1deSAlistair Popple     /// is different, `EAGAIN` is returned and the unexpected message is dropped.
63875f6b1deSAlistair Popple     ///
63975f6b1deSAlistair Popple     /// This design is by no means final, but it is simple and will let us go through GSP
64075f6b1deSAlistair Popple     /// initialization.
64175f6b1deSAlistair Popple     ///
64275f6b1deSAlistair Popple     /// # Errors
64375f6b1deSAlistair Popple     ///
64475f6b1deSAlistair Popple     /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
64575f6b1deSAlistair Popple     /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the
64675f6b1deSAlistair Popple     ///   message queue.
64775f6b1deSAlistair Popple     /// - `EINVAL` if the function of the message was unrecognized.
64875f6b1deSAlistair Popple     pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M>
64975f6b1deSAlistair Popple     where
65075f6b1deSAlistair Popple         // This allows all error types, including `Infallible`, to be used for `M::InitError`.
65175f6b1deSAlistair Popple         Error: From<M::InitError>,
65275f6b1deSAlistair Popple     {
65375f6b1deSAlistair Popple         let message = self.wait_for_msg(timeout)?;
65475f6b1deSAlistair Popple         let function = message.header.function().map_err(|_| EINVAL)?;
65575f6b1deSAlistair Popple 
65675f6b1deSAlistair Popple         // Extract the message. Store the result as we want to advance the read pointer even in
65775f6b1deSAlistair Popple         // case of failure.
65875f6b1deSAlistair Popple         let result = if function == M::FUNCTION {
65975f6b1deSAlistair Popple             let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?;
66075f6b1deSAlistair Popple             let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]);
66175f6b1deSAlistair Popple 
66275f6b1deSAlistair Popple             M::read(cmd, &mut sbuffer).map_err(|e| e.into())
66375f6b1deSAlistair Popple         } else {
66475f6b1deSAlistair Popple             Err(ERANGE)
66575f6b1deSAlistair Popple         };
66675f6b1deSAlistair Popple 
66775f6b1deSAlistair Popple         // Advance the read pointer past this message.
66875f6b1deSAlistair Popple         self.gsp_mem.advance_cpu_read_ptr(u32::try_from(
66975f6b1deSAlistair Popple             message.header.length().div_ceil(GSP_PAGE_SIZE),
67075f6b1deSAlistair Popple         )?);
67175f6b1deSAlistair Popple 
67275f6b1deSAlistair Popple         result
67375f6b1deSAlistair Popple     }
674*4fd4acd9SAlistair Popple 
675*4fd4acd9SAlistair Popple     /// Returns the DMA handle of the command queue's shared memory region.
676*4fd4acd9SAlistair Popple     pub(crate) fn dma_handle(&self) -> DmaAddress {
677*4fd4acd9SAlistair Popple         self.gsp_mem.0.dma_handle()
678*4fd4acd9SAlistair Popple     }
67975f6b1deSAlistair Popple }
680