// SPDX-License-Identifier: GPL-2.012use core::{3cmp,4mem, //5};67use kernel::{8device,9dma::{10CoherentAllocation,11DmaAddress, //12},13dma_write,14io::poll::read_poll_timeout,15prelude::*,16sync::aref::ARef,17time::Delta,18transmute::{19AsBytes,20FromBytes, //21},22};2324use crate::{25driver::Bar0,26gsp::{27fw::{28GspMsgElement,29MsgFunction,30MsgqRxHeader,31MsgqTxHeader, //32},33PteArray,34GSP_PAGE_SHIFT,35GSP_PAGE_SIZE, //36},37num,38regs,39sbuffer::SBufferIter, //40};4142/// Trait implemented by types representing a command to send to the GSP.43///44/// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it45/// needs to send a given command.46///47/// [`CommandToGsp::init`] in particular is responsible for initializing the command directly48/// into the space reserved for it in the command queue buffer.49///50/// Some commands may be followed by a variable-length payload. For these, the51/// [`CommandToGsp::variable_payload_len`] and [`CommandToGsp::init_variable_payload`] need to be52/// defined as well.53pub(crate) trait CommandToGsp {54/// Function identifying this command to the GSP.55const FUNCTION: MsgFunction;5657/// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer.58type Command: FromBytes + AsBytes;5960/// Error type returned by [`CommandToGsp::init`].61type InitError;6263/// In-place command initializer responsible for filling the command in the command queue64/// buffer.65fn init(&self) -> impl Init<Self::Command, Self::InitError>;6667/// Size of the variable-length payload following the command structure generated by68/// [`CommandToGsp::init`].69///70/// Most commands don't have a variable-length payload, so this is zero by default.71fn variable_payload_len(&self) -> usize {72073}7475/// Method initializing the variable-length payload.76///77/// The command buffer is circular, which means that we may need to jump back to its beginning78/// while in the middle of a command. For this reason, the variable-length payload is79/// initialized using a [`SBufferIter`].80///81/// This method will receive a buffer of the length returned by82/// [`CommandToGsp::variable_payload_len`], and must write every single byte of it. Leaving83/// unwritten space will lead to an error.84///85/// Most commands don't have a variable-length payload, so this does nothing by default.86fn init_variable_payload(87&self,88_dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,89) -> Result {90Ok(())91}92}9394/// Trait representing messages received from the GSP.95///96/// This trait tells [`Cmdq::receive_msg`] how it can receive a given type of message.97pub(crate) trait MessageFromGsp: Sized {98/// Function identifying this message from the GSP.99const FUNCTION: MsgFunction;100101/// Error type returned by [`MessageFromGsp::read`].102type InitError;103104/// Type containing the raw message to be read from the message queue.105type Message: FromBytes;106107/// Method reading the message from the message queue and returning it.108///109/// From a `Self::Message` and a [`SBufferIter`], constructs an instance of `Self` and returns110/// it.111fn read(112msg: &Self::Message,113sbuffer: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>,114) -> Result<Self, Self::InitError>;115}116117/// Number of GSP pages making the [`Msgq`].118pub(crate) const MSGQ_NUM_PAGES: u32 = 0x3f;119120/// Circular buffer of a [`Msgq`].121///122/// This area of memory is to be shared between the driver and the GSP to exchange commands or123/// messages.124#[repr(C, align(0x1000))]125#[derive(Debug)]126struct MsgqData {127data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)],128}129130// Annoyingly we are forced to use a literal to specify the alignment of131// `MsgqData`, so check that it corresponds to the actual GSP page size here.132static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);133134/// Unidirectional message queue.135///136/// Contains the data for a message queue, that either the driver or GSP writes to.137///138/// Note that while the write pointer of `tx` corresponds to the `msgq` of the same instance, the139/// read pointer of `rx` actually refers to the `Msgq` owned by the other side.140/// This design ensures that only the driver or GSP ever writes to a given instance of this struct.141#[repr(C)]142// There is no struct defined for this in the open-gpu-kernel-source headers.143// Instead it is defined by code in `GspMsgQueuesInit()`.144// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.145pub(super) struct Msgq {146/// Header for sending messages, including the write pointer.147pub(super) tx: MsgqTxHeader,148/// Header for receiving messages, including the read pointer.149pub(super) rx: MsgqRxHeader,150/// The message queue proper.151msgq: MsgqData,152}153154/// Structure shared between the driver and the GSP and containing the command and message queues.155#[repr(C)]156// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.157pub(super) struct GspMem {158/// Self-mapping page table entries.159ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,160/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the161/// write and read pointers that the CPU updates.162///163/// This member is read-only for the GSP.164pub(super) cpuq: Msgq,165/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the166/// write and read pointers that the GSP updates.167///168/// This member is read-only for the driver.169pub(super) gspq: Msgq,170}171172impl GspMem {173const PTE_ARRAY_SIZE: usize = GSP_PAGE_SIZE / size_of::<u64>();174}175176// SAFETY: These structs don't meet the no-padding requirements of AsBytes but177// that is not a problem because they are not used outside the kernel.178unsafe impl AsBytes for GspMem {}179180// SAFETY: These structs don't meet the no-padding requirements of FromBytes but181// that is not a problem because they are not used outside the kernel.182unsafe impl FromBytes for GspMem {}183184/// Wrapper around [`GspMem`] to share it with the GPU using a [`CoherentAllocation`].185///186/// This provides the low-level functionality to communicate with the GSP, including allocation of187/// queue space to write messages to and management of read/write pointers.188///189/// This is shared with the GSP, with clear ownership rules regarding the command queues:190///191/// * The driver owns (i.e. can write to) the part of the CPU message queue between the CPU write192/// pointer and the GSP read pointer. This region is returned by [`Self::driver_write_area`].193/// * The driver owns (i.e. can read from) the part of the GSP message queue between the CPU read194/// pointer and the GSP write pointer. This region is returned by [`Self::driver_read_area`].195struct DmaGspMem(CoherentAllocation<GspMem>);196197impl DmaGspMem {198/// Allocate a new instance and map it for `dev`.199fn new(dev: &device::Device<device::Bound>) -> Result<Self> {200const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();201const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();202203let gsp_mem =204CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;205206let start = gsp_mem.dma_handle();207// Write values one by one to avoid an on-stack instance of `PteArray`.208for i in 0..GspMem::PTE_ARRAY_SIZE {209dma_write!(gsp_mem, [0]?.ptes.0[i], PteArray::<0>::entry(start, i)?);210}211212dma_write!(213gsp_mem,214[0]?.cpuq.tx,215MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES)216);217dma_write!(gsp_mem, [0]?.cpuq.rx, MsgqRxHeader::new());218219Ok(Self(gsp_mem))220}221222/// Returns the region of the CPU message queue that the driver is currently allowed to write223/// to.224///225/// As the message queue is a circular buffer, the region may be discontiguous in memory. In226/// that case the second slice will have a non-zero length.227fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {228let tx = self.cpu_write_ptr() as usize;229let rx = self.gsp_read_ptr() as usize;230231// SAFETY:232// - The `CoherentAllocation` contains exactly one object.233// - We will only access the driver-owned part of the shared memory.234// - Per the safety statement of the function, no concurrent access will be performed.235let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0];236// PANIC: per the invariant of `cpu_write_ptr`, `tx` is `<= MSGQ_NUM_PAGES`.237let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx);238239if rx <= tx {240// The area from `tx` up to the end of the ring, and from the beginning of the ring up241// to `rx`, minus one unit, belongs to the driver.242if rx == 0 {243let last = after_tx.len() - 1;244(&mut after_tx[..last], &mut before_tx[0..0])245} else {246(after_tx, &mut before_tx[..rx])247}248} else {249// The area from `tx` to `rx`, minus one unit, belongs to the driver.250//251// PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are252// `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`.253(after_tx.split_at_mut(rx - tx).0, &mut before_tx[0..0])254}255}256257/// Returns the region of the GSP message queue that the driver is currently allowed to read258/// from.259///260/// As the message queue is a circular buffer, the region may be discontiguous in memory. In261/// that case the second slice will have a non-zero length.262fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {263let tx = self.gsp_write_ptr() as usize;264let rx = self.cpu_read_ptr() as usize;265266// SAFETY:267// - The `CoherentAllocation` contains exactly one object.268// - We will only access the driver-owned part of the shared memory.269// - Per the safety statement of the function, no concurrent access will be performed.270let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0];271// PANIC: per the invariant of `cpu_read_ptr`, `xx` is `<= MSGQ_NUM_PAGES`.272let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx);273274match tx.cmp(&rx) {275cmp::Ordering::Equal => (&after_rx[0..0], &after_rx[0..0]),276cmp::Ordering::Greater => (&after_rx[..tx], &before_rx[0..0]),277cmp::Ordering::Less => (after_rx, &before_rx[..tx]),278}279}280281/// Allocates a region on the command queue that is large enough to send a command of `size`282/// bytes.283///284/// This returns a [`GspCommand`] ready to be written to by the caller.285///286/// # Errors287///288/// - `EAGAIN` if the driver area is too small to hold the requested command.289/// - `EIO` if the command header is not properly aligned.290fn allocate_command(&mut self, size: usize) -> Result<GspCommand<'_>> {291// Get the current writable area as an array of bytes.292let (slice_1, slice_2) = {293let (slice_1, slice_2) = self.driver_write_area();294295#[allow(clippy::incompatible_msrv)]296(slice_1.as_flattened_mut(), slice_2.as_flattened_mut())297};298299// If the GSP is still processing previous messages the shared region300// may be full in which case we will have to retry once the GSP has301// processed the existing commands.302if size_of::<GspMsgElement>() + size > slice_1.len() + slice_2.len() {303return Err(EAGAIN);304}305306// Extract area for the `GspMsgElement`.307let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;308309// Create the contents area.310let (slice_1, slice_2) = if slice_1.len() > size {311// Contents fits entirely in `slice_1`.312(&mut slice_1[..size], &mut slice_2[0..0])313} else {314// Need all of `slice_1` and some of `slice_2`.315let slice_2_len = size - slice_1.len();316(slice_1, &mut slice_2[..slice_2_len])317};318319Ok(GspCommand {320header,321contents: (slice_1, slice_2),322})323}324325// Returns the index of the memory page the GSP will write the next message to.326//327// # Invariants328//329// - The returned value is between `0` and `MSGQ_NUM_PAGES`.330fn gsp_write_ptr(&self) -> u32 {331super::fw::gsp_mem::gsp_write_ptr(&self.0)332}333334// Returns the index of the memory page the GSP will read the next command from.335//336// # Invariants337//338// - The returned value is between `0` and `MSGQ_NUM_PAGES`.339fn gsp_read_ptr(&self) -> u32 {340super::fw::gsp_mem::gsp_read_ptr(&self.0)341}342343// Returns the index of the memory page the CPU can read the next message from.344//345// # Invariants346//347// - The returned value is between `0` and `MSGQ_NUM_PAGES`.348fn cpu_read_ptr(&self) -> u32 {349super::fw::gsp_mem::cpu_read_ptr(&self.0)350}351352// Informs the GSP that it can send `elem_count` new pages into the message queue.353fn advance_cpu_read_ptr(&mut self, elem_count: u32) {354super::fw::gsp_mem::advance_cpu_read_ptr(&self.0, elem_count)355}356357// Returns the index of the memory page the CPU can write the next command to.358//359// # Invariants360//361// - The returned value is between `0` and `MSGQ_NUM_PAGES`.362fn cpu_write_ptr(&self) -> u32 {363super::fw::gsp_mem::cpu_write_ptr(&self.0)364}365366// Informs the GSP that it can process `elem_count` new pages from the command queue.367fn advance_cpu_write_ptr(&mut self, elem_count: u32) {368super::fw::gsp_mem::advance_cpu_write_ptr(&self.0, elem_count)369}370}371372/// A command ready to be sent on the command queue.373///374/// This is the type returned by [`DmaGspMem::allocate_command`].375struct GspCommand<'a> {376// Writable reference to the header of the command.377header: &'a mut GspMsgElement,378// Writable slices to the contents of the command. The second slice is zero unless the command379// loops over the command queue.380contents: (&'a mut [u8], &'a mut [u8]),381}382383/// A message ready to be processed from the message queue.384///385/// This is the type returned by [`Cmdq::wait_for_msg`].386struct GspMessage<'a> {387// Reference to the header of the message.388header: &'a GspMsgElement,389// Slices to the contents of the message. The second slice is zero unless the message loops390// over the message queue.391contents: (&'a [u8], &'a [u8]),392}393394/// GSP command queue.395///396/// Provides the ability to send commands and receive messages from the GSP using a shared memory397/// area.398pub(crate) struct Cmdq {399/// Device this command queue belongs to.400dev: ARef<device::Device>,401/// Current command sequence number.402seq: u32,403/// Memory area shared with the GSP for communicating commands and messages.404gsp_mem: DmaGspMem,405}406407impl Cmdq {408/// Offset of the data after the PTEs.409const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq);410411/// Offset of command queue ring buffer.412pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq)413+ core::mem::offset_of!(Msgq, msgq)414- Self::POST_PTE_OFFSET;415416/// Offset of message queue ring buffer.417pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq)418+ core::mem::offset_of!(Msgq, msgq)419- Self::POST_PTE_OFFSET;420421/// Number of page table entries for the GSP shared region.422pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT;423424/// Creates a new command queue for `dev`.425pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> {426let gsp_mem = DmaGspMem::new(dev)?;427428Ok(Cmdq {429dev: dev.into(),430seq: 0,431gsp_mem,432})433}434435/// Computes the checksum for the message pointed to by `it`.436///437/// A message is made of several parts, so `it` is an iterator over byte slices representing438/// these parts.439fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 {440let sum64 = it441.enumerate()442.map(|(idx, byte)| (((idx % 8) * 8) as u32, byte))443.fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol));444445((sum64 >> 32) as u32) ^ (sum64 as u32)446}447448/// Notifies the GSP that we have updated the command queue pointers.449fn notify_gsp(bar: &Bar0) {450regs::NV_PGSP_QUEUE_HEAD::default()451.set_address(0)452.write(bar);453}454455/// Sends `command` to the GSP.456///457/// # Errors458///459/// - `EAGAIN` if there was not enough space in the command queue to send the command.460/// - `EIO` if the variable payload requested by the command has not been entirely461/// written to by its [`CommandToGsp::init_variable_payload`] method.462///463/// Error codes returned by the command initializers are propagated as-is.464pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result465where466M: CommandToGsp,467// This allows all error types, including `Infallible`, to be used for `M::InitError`.468Error: From<M::InitError>,469{470let command_size = size_of::<M::Command>() + command.variable_payload_len();471let dst = self.gsp_mem.allocate_command(command_size)?;472473// Extract area for the command itself.474let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;475476// Fill the header and command in-place.477let msg_element = GspMsgElement::init(self.seq, command_size, M::FUNCTION);478// SAFETY: `msg_header` and `cmd` are valid references, and not touched if the initializer479// fails.480unsafe {481msg_element.__init(core::ptr::from_mut(dst.header))?;482command.init().__init(core::ptr::from_mut(cmd))?;483}484485// Fill the variable-length payload.486if command_size > size_of::<M::Command>() {487let mut sbuffer =488SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]);489command.init_variable_payload(&mut sbuffer)?;490491if !sbuffer.is_empty() {492return Err(EIO);493}494}495496// Compute checksum now that the whole message is ready.497dst.header498.set_checksum(Cmdq::calculate_checksum(SBufferIter::new_reader([499dst.header.as_bytes(),500dst.contents.0,501dst.contents.1,502])));503504dev_dbg!(505&self.dev,506"GSP RPC: send: seq# {}, function={}, length=0x{:x}\n",507self.seq,508M::FUNCTION,509dst.header.length(),510);511512// All set - update the write pointer and inform the GSP of the new command.513let elem_count = dst.header.element_count();514self.seq += 1;515self.gsp_mem.advance_cpu_write_ptr(elem_count);516Cmdq::notify_gsp(bar);517518Ok(())519}520521/// Wait for a message to become available on the message queue.522///523/// This works purely at the transport layer and does not interpret or validate the message524/// beyond the advertised length in its [`GspMsgElement`].525///526/// This method returns:527///528/// - A reference to the [`GspMsgElement`] of the message,529/// - Two byte slices with the contents of the message. The second slice is empty unless the530/// message loops across the message queue.531///532/// # Errors533///534/// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.535/// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the536/// message queue.537///538/// Error codes returned by the message constructor are propagated as-is.539fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {540// Wait for a message to arrive from the GSP.541let (slice_1, slice_2) = read_poll_timeout(542|| Ok(self.gsp_mem.driver_read_area()),543|driver_area| !driver_area.0.is_empty(),544Delta::from_millis(1),545timeout,546)547.map(|(slice_1, slice_2)| {548#[allow(clippy::incompatible_msrv)]549(slice_1.as_flattened(), slice_2.as_flattened())550})?;551552// Extract the `GspMsgElement`.553let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?;554555dev_dbg!(556self.dev,557"GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n",558header.sequence(),559header.function(),560header.length(),561);562563let payload_length = header.payload_length();564565// Check that the driver read area is large enough for the message.566if slice_1.len() + slice_2.len() < payload_length {567return Err(EIO);568}569570// Cut the message slices down to the actual length of the message.571let (slice_1, slice_2) = if slice_1.len() > payload_length {572// PANIC: we checked above that `slice_1` is at least as long as `payload_length`.573(slice_1.split_at(payload_length).0, &slice_2[0..0])574} else {575(576slice_1,577// PANIC: we checked above that `slice_1.len() + slice_2.len()` is at least as578// large as `payload_length`.579slice_2.split_at(payload_length - slice_1.len()).0,580)581};582583// Validate checksum.584if Cmdq::calculate_checksum(SBufferIter::new_reader([585header.as_bytes(),586slice_1,587slice_2,588])) != 0589{590dev_err!(591self.dev,592"GSP RPC: receive: Call {} - bad checksum\n",593header.sequence()594);595return Err(EIO);596}597598Ok(GspMessage {599header,600contents: (slice_1, slice_2),601})602}603604/// Receive a message from the GSP.605///606/// `init` is a closure tasked with processing the message. It receives a reference to the607/// message in the message queue, and a [`SBufferIter`] pointing to its variable-length608/// payload, if any.609///610/// The expected message is specified using the `M` generic parameter. If the pending message611/// is different, `EAGAIN` is returned and the unexpected message is dropped.612///613/// This design is by no means final, but it is simple and will let us go through GSP614/// initialization.615///616/// # Errors617///618/// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.619/// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the620/// message queue.621/// - `EINVAL` if the function of the message was unrecognized.622pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M>623where624// This allows all error types, including `Infallible`, to be used for `M::InitError`.625Error: From<M::InitError>,626{627let message = self.wait_for_msg(timeout)?;628let function = message.header.function().map_err(|_| EINVAL)?;629630// Extract the message. Store the result as we want to advance the read pointer even in631// case of failure.632let result = if function == M::FUNCTION {633let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?;634let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]);635636M::read(cmd, &mut sbuffer).map_err(|e| e.into())637} else {638Err(ERANGE)639};640641// Advance the read pointer past this message.642self.gsp_mem.advance_cpu_read_ptr(u32::try_from(643message.header.length().div_ceil(GSP_PAGE_SIZE),644)?);645646result647}648649/// Returns the DMA handle of the command queue's shared memory region.650pub(crate) fn dma_handle(&self) -> DmaAddress {651self.gsp_mem.0.dma_handle()652}653}654655656