mod bit_field_macros;
mod struct_macros;
pub mod structured;
use std::fmt::Debug;
use std::io::{self, ErrorKind};
use std::marker::PhantomData;
use std::mem;
use std::ops::{Bound, Range, RangeBounds};
use std::sync::Arc;
use crate::device::PciDeviceInternal;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Permissions {
Read,
Write,
ReadWrite,
}
impl Permissions {
pub fn new(can_read: bool, can_write: bool) -> Option<Permissions> {
match (can_read, can_write) {
(false, false) => None,
(true, false) => Some(Permissions::Read),
(false, true) => Some(Permissions::Write),
(true, true) => Some(Permissions::ReadWrite),
}
}
pub fn can_read(&self) -> bool {
match self {
Permissions::Read => true,
Permissions::Write => false,
Permissions::ReadWrite => true,
}
}
pub fn can_write(&self) -> bool {
match self {
Permissions::Read => false,
Permissions::Write => true,
Permissions::ReadWrite => true,
}
}
}
pub(crate) use private::Sealed;
mod private {
pub trait Sealed {}
}
#[allow(clippy::len_without_is_empty)]
pub trait PciRegion: Debug + Send + Sync + Sealed {
fn len(&self) -> u64;
fn permissions(&self) -> Permissions;
fn as_ptr(&self) -> Option<*const u8>;
fn as_mut_ptr(&self) -> Option<*mut u8>;
fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> io::Result<()>;
fn read_u8(&self, offset: u64) -> io::Result<u8>;
fn write_u8(&self, offset: u64, value: u8) -> io::Result<()>;
fn read_le_u16(&self, offset: u64) -> io::Result<u16>;
fn write_le_u16(&self, offset: u64, value: u16) -> io::Result<()>;
fn read_le_u32(&self, offset: u64) -> io::Result<u32>;
fn write_le_u32(&self, offset: u64, value: u32) -> io::Result<()>;
}
macro_rules! impl_delegating_pci_region {
($type:ty) => {
impl $crate::regions::Sealed for $type {}
impl $crate::regions::PciRegion for $type {
fn len(&self) -> u64 {
$crate::regions::PciRegion::len(&self)
}
fn permissions(&self) -> $crate::regions::Permissions {
$crate::regions::PciRegion::permissions(&self)
}
fn as_ptr(&self) -> ::std::option::Option<*const u8> {
$crate::regions::PciRegion::as_ptr(&self)
}
fn as_mut_ptr(&self) -> ::std::option::Option<*mut u8> {
$crate::regions::PciRegion::as_mut_ptr(&self)
}
fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> ::std::io::Result<()> {
$crate::regions::PciRegion::read_bytes(&self, offset, buffer)
}
fn read_u8(&self, offset: u64) -> ::std::io::Result<u8> {
$crate::regions::PciRegion::read_u8(&self, offset)
}
fn write_u8(&self, offset: u64, value: u8) -> ::std::io::Result<()> {
$crate::regions::PciRegion::write_u8(&self, offset, value)
}
fn read_le_u16(&self, offset: u64) -> ::std::io::Result<u16> {
$crate::regions::PciRegion::read_le_u16(&self, offset)
}
fn write_le_u16(&self, offset: u64, value: u16) -> ::std::io::Result<()> {
$crate::regions::PciRegion::write_le_u16(&self, offset, value)
}
fn read_le_u32(&self, offset: u64) -> ::std::io::Result<u32> {
$crate::regions::PciRegion::read_le_u32(&self, offset)
}
fn write_le_u32(&self, offset: u64, value: u32) -> ::std::io::Result<()> {
$crate::regions::PciRegion::write_le_u32(&self, offset, value)
}
}
};
}
#[derive(Clone, Copy, Debug)]
pub struct PciSubregion<'a> {
region: &'a dyn PciRegion,
offset: u64,
length: u64,
}
impl<'a> PciSubregion<'a> {
pub fn underlying_region(&self) -> &'a dyn PciRegion {
self.region
}
pub fn offset_in_underlying_region(&self) -> u64 {
self.offset
}
fn validate_access(&self, offset: u64, len: usize) -> io::Result<()> {
let len = len as u64;
if offset + len > self.length {
return Err(io::Error::new(
ErrorKind::InvalidInput,
format!(
"Tried to access region range [{:#x}, {:#x}), must be within [0x0, {:#x})",
offset,
offset + len,
self.length
),
));
}
Ok(())
}
}
pub trait AsPciSubregion<'a> {
fn as_subregion(&self) -> PciSubregion<'a>;
fn subregion(&self, range: impl RangeBounds<u64>) -> PciSubregion<'a> {
let subregion = Self::as_subregion(self);
let range = clamp_range(range, subregion.len());
PciSubregion {
region: subregion.underlying_region(),
offset: subregion.offset_in_underlying_region() + range.start,
length: range.end - range.start,
}
}
}
impl<'a, 'b, T> AsPciSubregion<'a> for &'b T
where
T: AsPciSubregion<'a>,
{
fn as_subregion(&self) -> PciSubregion<'a> {
T::as_subregion(*self)
}
}
impl<'a> AsPciSubregion<'a> for &'a dyn PciRegion {
fn as_subregion(&self) -> PciSubregion<'a> {
PciSubregion {
region: *self,
offset: 0,
length: PciRegion::len(*self),
}
}
}
impl<'a> AsPciSubregion<'a> for PciSubregion<'a> {
fn as_subregion(&self) -> PciSubregion<'a> {
*self
}
}
impl<'a, T> Sealed for T where T: AsPciSubregion<'a> + Debug + Send + Sync {}
impl<'a, T> PciRegion for T
where
T: AsPciSubregion<'a> + Debug + Send + Sync,
{
fn len(&self) -> u64 {
let subregion = T::as_subregion(self);
subregion.length
}
fn permissions(&self) -> Permissions {
let subregion = T::as_subregion(self);
subregion.region.permissions()
}
fn as_ptr(&self) -> Option<*const u8> {
let subregion = T::as_subregion(self);
let ptr = subregion.region.as_ptr()?;
Some(unsafe { ptr.add(subregion.offset as usize) })
}
fn as_mut_ptr(&self) -> Option<*mut u8> {
let subregion = T::as_subregion(self);
let ptr = subregion.region.as_mut_ptr()?;
Some(unsafe { ptr.add(subregion.offset as usize) })
}
fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> io::Result<()> {
let subregion = T::as_subregion(self);
subregion.validate_access(offset, buffer.len())?;
subregion
.region
.read_bytes(subregion.offset + offset, buffer)
}
fn read_u8(&self, offset: u64) -> io::Result<u8> {
let subregion = T::as_subregion(self);
subregion.validate_access(offset, mem::size_of::<u8>())?;
subregion.region.read_u8(subregion.offset + offset)
}
fn write_u8(&self, offset: u64, value: u8) -> io::Result<()> {
let subregion = T::as_subregion(self);
subregion.validate_access(offset, mem::size_of::<u8>())?;
subregion.region.write_u8(subregion.offset + offset, value)
}
fn read_le_u16(&self, offset: u64) -> io::Result<u16> {
let subregion = T::as_subregion(self);
subregion.validate_access(offset, mem::size_of::<u16>())?;
subregion.region.read_le_u16(subregion.offset + offset)
}
fn write_le_u16(&self, offset: u64, value: u16) -> io::Result<()> {
let subregion = T::as_subregion(self);
subregion.validate_access(offset, mem::size_of::<u16>())?;
subregion
.region
.write_le_u16(subregion.offset + offset, value)
}
fn read_le_u32(&self, offset: u64) -> io::Result<u32> {
let subregion = T::as_subregion(self);
subregion.validate_access(offset, mem::size_of::<u32>())?;
subregion.region.read_le_u32(subregion.offset + offset)
}
fn write_le_u32(&self, offset: u64, value: u32) -> io::Result<()> {
let subregion = T::as_subregion(self);
subregion.validate_access(offset, mem::size_of::<u32>())?;
subregion
.region
.write_le_u32(subregion.offset + offset, value)
}
}
#[allow(dead_code)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum RegionIdentifier {
Bar(usize),
Rom,
}
#[derive(Debug)]
pub struct OwningPciRegion {
device: Arc<dyn PciDeviceInternal>,
region: Arc<dyn PciRegion>,
offset: u64,
length: u64,
identifier: RegionIdentifier,
is_mappable: bool,
}
impl OwningPciRegion {
#[allow(dead_code)] pub(crate) fn new(
device: Arc<dyn PciDeviceInternal>,
region: Arc<dyn PciRegion>,
identifier: RegionIdentifier,
is_mappable: bool,
) -> OwningPciRegion {
let offset = 0;
let length = region.len();
OwningPciRegion {
device,
region,
offset,
length,
identifier,
is_mappable,
}
}
pub fn is_mappable(&self) -> bool {
self.is_mappable
}
pub fn owning_subregion(&self, range: impl RangeBounds<u64>) -> OwningPciRegion {
let range = clamp_range(range, self.length);
OwningPciRegion {
device: Arc::clone(&self.device),
region: Arc::clone(&self.region),
offset: self.offset + range.start,
length: range.end - range.start,
identifier: self.identifier,
is_mappable: self.is_mappable,
}
}
pub fn map(
&self,
range: impl RangeBounds<u64>,
permissions: Permissions,
) -> io::Result<MappedOwningPciRegion> {
let range = clamp_range(range, self.region.len());
if range.end - range.start > usize::MAX as u64 {
return Err(io::Error::new(
ErrorKind::InvalidInput,
"Range length exceeds usize::MAX",
));
}
if (permissions.can_read() && !self.permissions().can_read())
|| (permissions.can_write() && !self.permissions().can_write())
{
return Err(io::Error::new(
ErrorKind::InvalidInput,
"Requested incompatible permissions",
));
}
let length = (range.end - range.start) as usize;
let ptr = self.device.region_map(
self.identifier,
self.offset + range.start,
length,
permissions,
)?;
let mapped_region = unsafe { PciMemoryRegion::new_raw(ptr, length, permissions) };
Ok(MappedOwningPciRegion {
device: Arc::clone(&self.device),
region: mapped_region,
identifier: self.identifier,
ptr,
length,
})
}
}
impl_delegating_pci_region! { OwningPciRegion }
impl<'a> AsPciSubregion<'a> for &'a OwningPciRegion {
fn as_subregion(&self) -> PciSubregion<'a> {
(&*self.region).subregion(self.offset..self.offset + self.length)
}
}
#[derive(Debug)]
pub struct MappedOwningPciRegion {
device: Arc<dyn PciDeviceInternal>,
region: PciMemoryRegion<'static>,
identifier: RegionIdentifier,
ptr: *mut u8,
length: usize,
}
unsafe impl Send for MappedOwningPciRegion {}
unsafe impl Sync for MappedOwningPciRegion {}
#[allow(clippy::len_without_is_empty)]
impl MappedOwningPciRegion {
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
pub fn as_mut_ptr(&self) -> *mut u8 {
self.ptr
}
pub fn len(&self) -> usize {
self.length
}
}
impl_delegating_pci_region! { MappedOwningPciRegion }
impl<'a> AsPciSubregion<'a> for &'a MappedOwningPciRegion {
fn as_subregion(&self) -> PciSubregion<'a> {
(&self.region).as_subregion()
}
}
impl Drop for MappedOwningPciRegion {
fn drop(&mut self) {
unsafe {
self.device
.region_unmap(self.identifier, self.ptr, self.length)
};
}
}
#[derive(Clone, Copy, Debug)]
pub struct PciMemoryRegion<'a> {
ptr: *mut u8,
length: usize,
permissions: Permissions,
phantom: PhantomData<&'a ()>,
}
unsafe impl Send for PciMemoryRegion<'_> {}
unsafe impl Sync for PciMemoryRegion<'_> {}
impl PciMemoryRegion<'_> {
pub fn new(data: &[u8]) -> PciMemoryRegion {
PciMemoryRegion {
ptr: data.as_ptr() as *mut _,
length: data.len(),
permissions: Permissions::Read,
phantom: PhantomData,
}
}
pub fn new_mut(data: &mut [u8]) -> PciMemoryRegion {
PciMemoryRegion {
ptr: data.as_mut_ptr(),
length: data.len(),
permissions: Permissions::ReadWrite,
phantom: PhantomData,
}
}
pub unsafe fn new_raw<'a>(
data: *mut u8,
length: usize,
permissions: Permissions,
) -> PciMemoryRegion<'a> {
PciMemoryRegion {
ptr: data,
length,
permissions,
phantom: PhantomData,
}
}
fn get_ptr<T>(&self, offset: u64) -> io::Result<*mut T> {
let size = std::mem::size_of::<T>() as u64;
if offset + size > self.length as u64 {
return Err(io::Error::new(
ErrorKind::InvalidInput,
"Access falls outside region",
));
}
if offset % size != 0 {
return Err(io::Error::new(ErrorKind::InvalidInput, "Unaligned access"));
}
Ok(unsafe { self.ptr.add(offset as usize).cast::<T>() })
}
}
impl Sealed for PciMemoryRegion<'_> {}
impl PciRegion for PciMemoryRegion<'_> {
fn len(&self) -> u64 {
self.length as u64
}
fn permissions(&self) -> Permissions {
self.permissions
}
fn as_ptr(&self) -> Option<*const u8> {
Some(self.ptr)
}
fn as_mut_ptr(&self) -> Option<*mut u8> {
Some(self.ptr)
}
fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> io::Result<()> {
let end = offset + buffer.len() as u64;
if end > self.length as u64 {
return Err(io::Error::new(
ErrorKind::InvalidInput,
format!(
"Invalid configuration space range [{:#x}, {:#x}), must be within [0x0, {:#x})",
offset,
end,
self.len()
),
));
}
for (off, byte) in (offset..).zip(buffer) {
*byte = unsafe { self.get_ptr::<u8>(off)?.read_volatile() };
}
Ok(())
}
fn read_u8(&self, offset: u64) -> io::Result<u8> {
let v = unsafe { self.get_ptr::<u8>(offset)?.read_volatile() };
Ok(v)
}
fn write_u8(&self, offset: u64, value: u8) -> io::Result<()> {
unsafe { self.get_ptr::<u8>(offset)?.write_volatile(value) };
Ok(())
}
fn read_le_u16(&self, offset: u64) -> io::Result<u16> {
let v = unsafe { self.get_ptr::<u16>(offset)?.read_volatile() };
Ok(u16::from_le(v))
}
fn write_le_u16(&self, offset: u64, value: u16) -> io::Result<()> {
unsafe { self.get_ptr::<u16>(offset)?.write_volatile(value.to_le()) };
Ok(())
}
fn read_le_u32(&self, offset: u64) -> io::Result<u32> {
let v = unsafe { self.get_ptr::<u32>(offset)?.read_volatile() };
Ok(u32::from_le(v))
}
fn write_le_u32(&self, offset: u64, value: u32) -> io::Result<()> {
unsafe { self.get_ptr::<u32>(offset)?.write_volatile(value.to_le()) };
Ok(())
}
}
impl<'a> AsPciSubregion<'a> for &'a PciMemoryRegion<'_> {
fn as_subregion(&self) -> PciSubregion<'a> {
let region: &dyn PciRegion = *self;
<&dyn PciRegion>::as_subregion(®ion)
}
}
#[derive(Clone, Debug)]
pub struct PciRegionSnapshot {
buffer: Box<[u8]>,
region: PciMemoryRegion<'static>,
}
impl PciRegionSnapshot {
pub fn take<'a>(as_subregion: impl AsPciSubregion<'a>) -> io::Result<PciRegionSnapshot> {
let subregion = as_subregion.as_subregion();
if subregion.len() > isize::MAX as u64 {
return Err(io::Error::new(ErrorKind::Other, "TODO"));
}
let mut buffer = vec![0u8; subregion.len() as usize];
subregion.read_bytes(0, &mut buffer)?;
let mut buffer = buffer.into_boxed_slice();
let region = unsafe {
PciMemoryRegion::new_raw(buffer.as_mut_ptr(), buffer.len(), Permissions::ReadWrite)
};
Ok(PciRegionSnapshot { buffer, region })
}
}
impl_delegating_pci_region! { PciRegionSnapshot }
impl<'a> AsPciSubregion<'a> for &'a PciRegionSnapshot {
fn as_subregion(&self) -> PciSubregion<'a> {
(&self.region).as_subregion()
}
}
impl From<PciRegionSnapshot> for Box<[u8]> {
fn from(snapshot: PciRegionSnapshot) -> Self {
snapshot.buffer
}
}
impl From<PciRegionSnapshot> for Vec<u8> {
fn from(snapshot: PciRegionSnapshot) -> Self {
Vec::from(snapshot.buffer)
}
}
pub trait BackedByPciSubregion<'a> {
fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> Self;
}
fn clamp_range(range: impl RangeBounds<u64>, max_length: u64) -> Range<u64> {
let start = match range.start_bound() {
Bound::Included(&b) => b,
Bound::Excluded(&b) => b + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&b) => b + 1,
Bound::Excluded(&b) => b,
Bound::Unbounded => max_length,
};
Range {
start: start.min(max_length),
end: end.max(start).min(max_length),
}
}