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