mod continuation;
use core::mem;
use kernel::{
device,
dma::{
Coherent,
DmaAddress, },
dma_write,
io::{
poll::read_poll_timeout,
Io, },
new_mutex,
prelude::*,
ptr,
sync::{
aref::ARef,
Mutex, },
time::Delta,
transmute::{
AsBytes,
FromBytes, },
};
use continuation::{
ContinuationRecord,
SplitState, };
use pin_init::pin_init_scope;
use crate::{
driver::Bar0,
gsp::{
fw::{
GspMsgElement,
MsgFunction,
MsgqRxHeader,
MsgqTxHeader,
GSP_MSG_QUEUE_ELEMENT_SIZE_MAX, },
PteArray,
GSP_PAGE_SHIFT,
GSP_PAGE_SIZE, },
num,
regs,
sbuffer::SBufferIter, };
pub(crate) struct NoReply;
pub(crate) trait CommandToGsp {
const FUNCTION: MsgFunction;
type Command: FromBytes + AsBytes;
type Reply;
type InitError;
fn init(&self) -> impl Init<Self::Command, Self::InitError>;
fn variable_payload_len(&self) -> usize {
0
}
fn init_variable_payload(
&self,
_dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
) -> Result {
Ok(())
}
fn size(&self) -> usize {
size_of::<Self::Command>() + self.variable_payload_len()
}
}
pub(crate) trait MessageFromGsp: Sized {
const FUNCTION: MsgFunction;
type InitError;
type Message: FromBytes;
fn read(
msg: &Self::Message,
sbuffer: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>,
) -> Result<Self, Self::InitError>;
}
pub(crate) const MSGQ_NUM_PAGES: u32 = 0x3f;
#[repr(C, align(0x1000))]
#[derive(Debug)]
struct MsgqData {
data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)],
}
static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
#[repr(C)]
pub(super) struct Msgq {
pub(super) tx: MsgqTxHeader,
pub(super) rx: MsgqRxHeader,
msgq: MsgqData,
}
#[repr(C)]
pub(super) struct GspMem {
ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,
pub(super) cpuq: Msgq,
pub(super) gspq: Msgq,
}
impl GspMem {
const PTE_ARRAY_SIZE: usize = GSP_PAGE_SIZE / size_of::<u64>();
}
unsafe impl AsBytes for GspMem {}
unsafe impl FromBytes for GspMem {}
struct DmaGspMem(Coherent<GspMem>);
impl DmaGspMem {
fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();
const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();
let gsp_mem = Coherent::<GspMem>::zeroed(dev, GFP_KERNEL)?;
let start = gsp_mem.dma_handle();
for i in 0..GspMem::PTE_ARRAY_SIZE {
dma_write!(gsp_mem, .ptes.0[i], PteArray::<0>::entry(start, i)?);
}
dma_write!(
gsp_mem,
.cpuq.tx,
MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES)
);
dma_write!(gsp_mem, .cpuq.rx, MsgqRxHeader::new());
Ok(Self(gsp_mem))
}
fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {
let tx = self.cpu_write_ptr();
let rx = self.gsp_read_ptr();
let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[0]);
let (tail_end, wrap_end) = if rx == 0 {
(MSGQ_NUM_PAGES - 1, 0)
} else if rx <= tx {
(MSGQ_NUM_PAGES, rx - 1)
} else {
(rx - 1, 0)
};
unsafe {
(
core::slice::from_raw_parts_mut(
data.add(num::u32_as_usize(tx)),
num::u32_as_usize(tail_end - tx),
),
core::slice::from_raw_parts_mut(data, num::u32_as_usize(wrap_end)),
)
}
}
fn driver_write_area_size(&self) -> usize {
let tx = self.cpu_write_ptr();
let rx = self.gsp_read_ptr();
let slots = (rx + MSGQ_NUM_PAGES - tx - 1) % MSGQ_NUM_PAGES;
num::u32_as_usize(slots) * GSP_PAGE_SIZE
}
fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {
let tx = self.gsp_write_ptr();
let rx = self.cpu_read_ptr();
let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[0]);
let (tail_end, wrap_end) = if rx <= tx {
(tx, 0)
} else {
(MSGQ_NUM_PAGES, tx)
};
unsafe {
(
core::slice::from_raw_parts(
data.add(num::u32_as_usize(rx)),
num::u32_as_usize(tail_end - rx),
),
core::slice::from_raw_parts(data, num::u32_as_usize(wrap_end)),
)
}
}
fn allocate_command(&mut self, size: usize, timeout: Delta) -> Result<GspCommand<'_>> {
if size_of::<GspMsgElement>() + size > GSP_MSG_QUEUE_ELEMENT_SIZE_MAX {
return Err(EMSGSIZE);
}
read_poll_timeout(
|| Ok(self.driver_write_area_size()),
|available_bytes| *available_bytes >= size_of::<GspMsgElement>() + size,
Delta::from_micros(1),
timeout,
)?;
let (slice_1, slice_2) = {
let (slice_1, slice_2) = self.driver_write_area();
(slice_1.as_flattened_mut(), slice_2.as_flattened_mut())
};
let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
let (slice_1, slice_2) = if slice_1.len() > size {
(&mut slice_1[..size], &mut slice_2[0..0])
} else {
let slice_2_len = size - slice_1.len();
(slice_1, &mut slice_2[..slice_2_len])
};
Ok(GspCommand {
header,
contents: (slice_1, slice_2),
})
}
fn gsp_write_ptr(&self) -> u32 {
super::fw::gsp_mem::gsp_write_ptr(&self.0)
}
fn gsp_read_ptr(&self) -> u32 {
super::fw::gsp_mem::gsp_read_ptr(&self.0)
}
fn cpu_read_ptr(&self) -> u32 {
super::fw::gsp_mem::cpu_read_ptr(&self.0)
}
fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
super::fw::gsp_mem::advance_cpu_read_ptr(&self.0, elem_count)
}
fn cpu_write_ptr(&self) -> u32 {
super::fw::gsp_mem::cpu_write_ptr(&self.0)
}
fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
super::fw::gsp_mem::advance_cpu_write_ptr(&self.0, elem_count)
}
}
struct GspCommand<'a> {
header: &'a mut GspMsgElement,
contents: (&'a mut [u8], &'a mut [u8]),
}
struct GspMessage<'a> {
header: &'a GspMsgElement,
contents: (&'a [u8], &'a [u8]),
}
#[pin_data]
pub(crate) struct Cmdq {
#[pin]
inner: Mutex<CmdqInner>,
pub(super) dma_handle: DmaAddress,
}
impl Cmdq {
const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq);
pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq)
+ core::mem::offset_of!(Msgq, msgq)
- Self::POST_PTE_OFFSET;
pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq)
+ core::mem::offset_of!(Msgq, msgq)
- Self::POST_PTE_OFFSET;
pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT;
pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5);
pub(crate) fn new(dev: &device::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ {
pin_init_scope(move || {
let gsp_mem = DmaGspMem::new(dev)?;
Ok(try_pin_init!(Self {
dma_handle: gsp_mem.0.dma_handle(),
inner <- new_mutex!(CmdqInner {
dev: dev.into(),
gsp_mem,
seq: 0,
}),
}))
})
}
fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 {
let sum64 = it
.enumerate()
.map(|(idx, byte)| (((idx % 8) * 8) as u32, byte))
.fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol));
((sum64 >> 32) as u32) ^ (sum64 as u32)
}
fn notify_gsp(bar: &Bar0) {
bar.write_reg(regs::NV_PGSP_QUEUE_HEAD::zeroed().with_address(0u32));
}
pub(crate) fn send_command<M>(&self, bar: &Bar0, command: M) -> Result<M::Reply>
where
M: CommandToGsp,
M::Reply: MessageFromGsp,
Error: From<M::InitError>,
Error: From<<M::Reply as MessageFromGsp>::InitError>,
{
let mut inner = self.inner.lock();
inner.send_command(bar, command)?;
loop {
match inner.receive_msg::<M::Reply>(Self::RECEIVE_TIMEOUT) {
Ok(reply) => break Ok(reply),
Err(ERANGE) => continue,
Err(e) => break Err(e),
}
}
}
pub(crate) fn send_command_no_wait<M>(&self, bar: &Bar0, command: M) -> Result
where
M: CommandToGsp<Reply = NoReply>,
Error: From<M::InitError>,
{
self.inner.lock().send_command(bar, command)
}
pub(crate) fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M>
where
Error: From<M::InitError>,
{
self.inner.lock().receive_msg(timeout)
}
}
struct CmdqInner {
dev: ARef<device::Device>,
seq: u32,
gsp_mem: DmaGspMem,
}
impl CmdqInner {
const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1);
fn send_single_command<M>(&mut self, bar: &Bar0, command: M) -> Result
where
M: CommandToGsp,
Error: From<M::InitError>,
{
let size_in_bytes = command.size();
let dst = self
.gsp_mem
.allocate_command(size_in_bytes, Self::ALLOCATE_TIMEOUT)?;
let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;
let msg_element = GspMsgElement::init(self.seq, size_in_bytes, M::FUNCTION);
unsafe {
msg_element.__init(core::ptr::from_mut(dst.header))?;
command.init().__init(core::ptr::from_mut(cmd))?;
}
let mut sbuffer = SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]);
command.init_variable_payload(&mut sbuffer)?;
if !sbuffer.is_empty() {
return Err(EIO);
}
drop(sbuffer);
dst.header
.set_checksum(Cmdq::calculate_checksum(SBufferIter::new_reader([
dst.header.as_bytes(),
dst.contents.0,
dst.contents.1,
])));
dev_dbg!(
&self.dev,
"GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
self.seq,
M::FUNCTION,
dst.header.length(),
);
let elem_count = dst.header.element_count();
self.seq += 1;
self.gsp_mem.advance_cpu_write_ptr(elem_count);
Cmdq::notify_gsp(bar);
Ok(())
}
fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result
where
M: CommandToGsp,
Error: From<M::InitError>,
{
match SplitState::new(command)? {
SplitState::Single(command) => self.send_single_command(bar, command),
SplitState::Split(command, mut continuations) => {
self.send_single_command(bar, command)?;
while let Some(continuation) = continuations.next() {
self.send_single_command::<ContinuationRecord<'_>>(bar, continuation)?;
}
Ok(())
}
}
}
fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
let (slice_1, slice_2) = read_poll_timeout(
|| Ok(self.gsp_mem.driver_read_area()),
|driver_area| !driver_area.0.is_empty(),
Delta::from_millis(1),
timeout,
)
.map(|(slice_1, slice_2)| (slice_1.as_flattened(), slice_2.as_flattened()))?;
let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?;
dev_dbg!(
&self.dev,
"GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n",
header.sequence(),
header.function(),
header.length(),
);
let payload_length = header.payload_length();
if slice_1.len() + slice_2.len() < payload_length {
return Err(EIO);
}
let (slice_1, slice_2) = if slice_1.len() > payload_length {
(slice_1.split_at(payload_length).0, &slice_2[0..0])
} else {
(
slice_1,
slice_2.split_at(payload_length - slice_1.len()).0,
)
};
if Cmdq::calculate_checksum(SBufferIter::new_reader([
header.as_bytes(),
slice_1,
slice_2,
])) != 0
{
dev_err!(
&self.dev,
"GSP RPC: receive: Call {} - bad checksum\n",
header.sequence()
);
return Err(EIO);
}
Ok(GspMessage {
header,
contents: (slice_1, slice_2),
})
}
fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M>
where
Error: From<M::InitError>,
{
let message = self.wait_for_msg(timeout)?;
let function = message.header.function().map_err(|_| EINVAL)?;
let result = if function == M::FUNCTION {
let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?;
let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]);
M::read(cmd, &mut sbuffer)
.map_err(|e| e.into())
.inspect(|_| {
if !sbuffer.is_empty() {
dev_warn!(
&self.dev,
"GSP message {:?} has unprocessed data\n",
function
);
}
})
} else {
Err(ERANGE)
};
self.gsp_mem.advance_cpu_read_ptr(u32::try_from(
message.header.length().div_ceil(GSP_PAGE_SIZE),
)?);
result
}
}