},
new_mutex,
prelude::*,
+ ptr,
sync::{
aref::ARef,
Mutex, //
/// As the message queue is a circular buffer, the region may be discontiguous in memory. In
/// that case the second slice will have a non-zero length.
fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {
- let tx = self.cpu_write_ptr() as usize;
- let rx = self.gsp_read_ptr() as usize;
+ let tx = self.cpu_write_ptr();
+ let rx = self.gsp_read_ptr();
- // SAFETY:
- // - We will only access the driver-owned part of the shared memory.
- // - Per the safety statement of the function, no concurrent access will be performed.
- let gsp_mem = unsafe { &mut *self.0.as_mut() };
- // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`.
- let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx);
-
- // The area starting at `tx` and ending at `rx - 2` modulo MSGQ_NUM_PAGES, inclusive,
- // belongs to the driver for writing.
-
- if rx == 0 {
- // Since `rx` is zero, leave an empty slot at end of the buffer.
- let last = after_tx.len() - 1;
- (&mut after_tx[..last], &mut [])
+ // Pointer to the first entry of the CPU message queue.
+ let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[0]);
+
+ let (tail_end, wrap_end) = if rx == 0 {
+ // The write area is non-wrapping, and stops at the second-to-last entry of the command
+ // queue (to leave the last one empty).
+ (MSGQ_NUM_PAGES - 1, 0)
} else if rx <= tx {
- // The area is discontiguous and we leave an empty slot before `rx`.
- // PANIC:
- // - The index `rx - 1` is non-negative because `rx != 0` in this branch.
- // - The index does not exceed `before_tx.len()` (which equals `tx`) because
- // `rx <= tx` in this branch.
- (after_tx, &mut before_tx[..(rx - 1)])
+ // The write area wraps and continues until `rx - 1`.
+ (MSGQ_NUM_PAGES, rx - 1)
} else {
- // The area is contiguous and we leave an empty slot before `rx`.
- // PANIC:
- // - The index `rx - tx - 1` is non-negative because `rx > tx` in this branch.
- // - The index does not exceed `after_tx.len()` (which is `MSGQ_NUM_PAGES - tx`)
- // because `rx < MSGQ_NUM_PAGES` by the `gsp_read_ptr` invariant.
- (&mut after_tx[..(rx - tx - 1)], &mut [])
+ // The write area doesn't wrap and stops at `rx - 1`.
+ (rx - 1, 0)
+ };
+
+ // SAFETY:
+ // - `data` was created from a valid pointer, and `rx` and `tx` are in the
+ // `0..MSGQ_NUM_PAGES` range per the invariants of `cpu_write_ptr` and `gsp_read_ptr`,
+ // thus the created slices are valid.
+ // - The area starting at `tx` and ending at `rx - 2` modulo `MSGQ_NUM_PAGES`,
+ // inclusive, belongs to the driver for writing and is not accessed concurrently by
+ // the GSP.
+ // - The caller holds a reference to `self` for as long as the returned slices are live,
+ // meaning the CPU write pointer cannot be advanced and thus that the returned area
+ // remains exclusive to the CPU for the duration of the slices.
+ // - The created slices point to non-overlapping sub-ranges of `data` in all
+ // branches (in the `rx <= tx` case, the second slice ends at `rx - 1` which is strictly
+ // less than `tx` where the first slice starts; in the other cases the second slice is
+ // empty), so creating two `&mut` references from them does not violate aliasing rules.
+ 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)),
+ )
}
}
/// As the message queue is a circular buffer, the region may be discontiguous in memory. In
/// that case the second slice will have a non-zero length.
fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {
- let tx = self.gsp_write_ptr() as usize;
- let rx = self.cpu_read_ptr() as usize;
+ let tx = self.gsp_write_ptr();
+ let rx = self.cpu_read_ptr();
- // SAFETY:
- // - We will only access the driver-owned part of the shared memory.
- // - Per the safety statement of the function, no concurrent access will be performed.
- let gsp_mem = unsafe { &*self.0.as_ptr() };
- let data = &gsp_mem.gspq.msgq.data;
-
- // The area starting at `rx` and ending at `tx - 1` modulo MSGQ_NUM_PAGES, inclusive,
- // belongs to the driver for reading.
- // PANIC:
- // - per the invariant of `cpu_read_ptr`, `rx < MSGQ_NUM_PAGES`
- // - per the invariant of `gsp_write_ptr`, `tx < MSGQ_NUM_PAGES`
- if rx <= tx {
- // The area is contiguous.
- (&data[rx..tx], &[])
+ // Pointer to the first entry of the GSP message queue.
+ let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[0]);
+
+ let (tail_end, wrap_end) = if rx <= tx {
+ // Read area is non-wrapping and stops right before `tx`.
+ (tx, 0)
} else {
- // The area is discontiguous.
- (&data[rx..], &data[..tx])
+ // Read area is wrapping and stops right before `tx`.
+ (MSGQ_NUM_PAGES, tx)
+ };
+
+ // SAFETY:
+ // - `data` was created from a valid pointer, and `rx` and `tx` are in the
+ // `0..MSGQ_NUM_PAGES` range per the invariants of `gsp_write_ptr` and `cpu_read_ptr`,
+ // thus the created slices are valid.
+ // - The area starting at `rx` and ending at `tx - 1` modulo `MSGQ_NUM_PAGES`,
+ // inclusive, belongs to the driver for reading and is not accessed concurrently by
+ // the GSP.
+ // - The caller holds a reference to `self` for as long as the returned slices are live,
+ // meaning the CPU read pointer cannot be advanced and thus that the returned area
+ // remains exclusive to the CPU for the duration of the slices.
+ 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)),
+ )
}
}