265 lines
7.6 KiB
Rust
265 lines
7.6 KiB
Rust
use core::{
|
|
alloc::Layout,
|
|
fmt,
|
|
mem::{self, MaybeUninit},
|
|
ops::{Deref, DerefMut, Sub},
|
|
ptr::{self, NonNull},
|
|
};
|
|
|
|
use bytemuck::{Pod, Zeroable};
|
|
use device_api::dma::{DmaAllocation, DmaAllocator};
|
|
use libk_mm::{
|
|
address::{AsPhysicalAddress, PhysicalAddress, Virtualize},
|
|
phys, L3_PAGE_SIZE,
|
|
};
|
|
use yggdrasil_abi::error::Error;
|
|
|
|
pub struct DummyDmaAllocator;
|
|
|
|
impl DmaAllocator for DummyDmaAllocator {
|
|
fn allocate(&self, layout: Layout) -> Result<DmaAllocation, Error> {
|
|
if layout.align() > L3_PAGE_SIZE {
|
|
return Err(Error::InvalidMemoryOperation);
|
|
}
|
|
let page_count = layout.size().div_ceil(L3_PAGE_SIZE);
|
|
let host_physical = phys::alloc_pages_contiguous(page_count)?;
|
|
let host_virtual = unsafe {
|
|
NonNull::new_unchecked(ptr::with_exposed_provenance_mut(host_physical.virtualize()))
|
|
};
|
|
let bus_address = host_physical.into_u64();
|
|
|
|
Ok(DmaAllocation {
|
|
host_physical: host_physical.into_u64(),
|
|
host_virtual,
|
|
bus_address,
|
|
page_count,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub struct DmaBuffer<T: ?Sized> {
|
|
host_pointer: NonNull<T>,
|
|
host_physical: PhysicalAddress,
|
|
bus_address: u64,
|
|
page_count: usize,
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord, Pod, Zeroable)]
|
|
#[repr(transparent)]
|
|
pub struct BusAddress(u64);
|
|
|
|
impl<T> DmaBuffer<T> {
|
|
pub fn new(allocator: &dyn DmaAllocator, value: T) -> Result<DmaBuffer<T>, Error> {
|
|
let mut uninit = DmaBuffer::new_uninit(allocator)?;
|
|
uninit.write(value);
|
|
Ok(unsafe { DmaBuffer::assume_init(uninit) })
|
|
}
|
|
|
|
pub fn new_slice(
|
|
allocator: &dyn DmaAllocator,
|
|
value: T,
|
|
size: usize,
|
|
) -> Result<DmaBuffer<[T]>, Error>
|
|
where
|
|
T: Copy,
|
|
{
|
|
let mut uninit = DmaBuffer::new_uninit_slice(allocator, size)?;
|
|
for i in 0..size {
|
|
uninit[i].write(value);
|
|
}
|
|
Ok(unsafe { DmaBuffer::assume_init_slice(uninit) })
|
|
}
|
|
|
|
pub fn from_slice(allocator: &dyn DmaAllocator, source: &[T]) -> Result<DmaBuffer<[T]>, Error>
|
|
where
|
|
T: Copy,
|
|
{
|
|
let mut uninit = DmaBuffer::new_uninit_slice(allocator, source.len())?;
|
|
MaybeUninit::copy_from_slice(&mut uninit[..], source);
|
|
Ok(unsafe { DmaBuffer::assume_init_slice(uninit) })
|
|
}
|
|
|
|
pub fn new_slice_with<F: Fn(usize) -> T>(
|
|
allocator: &dyn DmaAllocator,
|
|
init: F,
|
|
size: usize,
|
|
) -> Result<DmaBuffer<[T]>, Error> {
|
|
let mut uninit = DmaBuffer::new_uninit_slice(allocator, size)?;
|
|
for i in 0..size {
|
|
uninit[i].write(init(i));
|
|
}
|
|
Ok(unsafe { DmaBuffer::assume_init_slice(uninit) })
|
|
}
|
|
|
|
pub fn new_uninit(allocator: &dyn DmaAllocator) -> Result<DmaBuffer<MaybeUninit<T>>, Error> {
|
|
let layout = Layout::new::<T>();
|
|
let allocation = allocator.allocate(layout)?;
|
|
let host_pointer = allocation.host_virtual.cast();
|
|
Ok(DmaBuffer {
|
|
host_pointer,
|
|
host_physical: PhysicalAddress::from_u64(allocation.host_physical),
|
|
bus_address: allocation.bus_address,
|
|
page_count: allocation.page_count,
|
|
})
|
|
}
|
|
|
|
pub fn new_uninit_slice(
|
|
allocator: &dyn DmaAllocator,
|
|
size: usize,
|
|
) -> Result<DmaBuffer<[MaybeUninit<T>]>, Error> {
|
|
let layout = Layout::array::<T>(size).map_err(|_| Error::InvalidMemoryOperation)?;
|
|
let allocation = allocator.allocate(layout)?;
|
|
let host_pointer = NonNull::slice_from_raw_parts(allocation.host_virtual.cast(), size);
|
|
Ok(DmaBuffer {
|
|
host_pointer,
|
|
host_physical: PhysicalAddress::from_u64(allocation.host_physical),
|
|
bus_address: allocation.bus_address,
|
|
page_count: allocation.page_count,
|
|
})
|
|
}
|
|
|
|
pub fn new_zeroed_slice(
|
|
allocator: &dyn DmaAllocator,
|
|
size: usize,
|
|
) -> Result<DmaBuffer<[MaybeUninit<T>]>, Error> {
|
|
let layout = Layout::array::<T>(size).map_err(|_| Error::InvalidMemoryOperation)?;
|
|
let allocation = allocator.allocate(layout)?;
|
|
unsafe {
|
|
let mut slice = NonNull::<[u8]>::slice_from_raw_parts(
|
|
allocation.host_virtual.cast(),
|
|
layout.size(),
|
|
);
|
|
|
|
slice.as_mut().fill(0);
|
|
}
|
|
let host_pointer = NonNull::slice_from_raw_parts(allocation.host_virtual.cast(), size);
|
|
Ok(DmaBuffer {
|
|
host_pointer,
|
|
host_physical: PhysicalAddress::from_u64(allocation.host_physical),
|
|
bus_address: allocation.bus_address,
|
|
page_count: allocation.page_count,
|
|
})
|
|
}
|
|
|
|
pub unsafe fn assume_init(buffer: DmaBuffer<MaybeUninit<T>>) -> DmaBuffer<T> {
|
|
let host_pointer = buffer.host_pointer;
|
|
let host_physical = buffer.host_physical;
|
|
let bus_address = buffer.bus_address;
|
|
let page_count = buffer.page_count;
|
|
|
|
mem::forget(buffer);
|
|
|
|
let host_pointer = host_pointer.cast();
|
|
|
|
DmaBuffer {
|
|
host_pointer,
|
|
host_physical,
|
|
bus_address,
|
|
page_count,
|
|
}
|
|
}
|
|
|
|
pub unsafe fn assume_init_slice(buffer: DmaBuffer<[MaybeUninit<T>]>) -> DmaBuffer<[T]> {
|
|
let host_pointer = buffer.host_pointer;
|
|
let host_physical = buffer.host_physical;
|
|
let bus_address = buffer.bus_address;
|
|
let page_count = buffer.page_count;
|
|
|
|
mem::forget(buffer);
|
|
|
|
let len = host_pointer.len();
|
|
let host_pointer = NonNull::slice_from_raw_parts(host_pointer.cast::<T>(), len);
|
|
|
|
DmaBuffer {
|
|
host_pointer,
|
|
host_physical,
|
|
bus_address,
|
|
page_count,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DmaBuffer<T> {
|
|
#[inline]
|
|
pub fn page_count(&self) -> usize {
|
|
self.page_count
|
|
}
|
|
|
|
#[inline]
|
|
pub fn bus_address(&self) -> BusAddress {
|
|
BusAddress(self.bus_address)
|
|
}
|
|
}
|
|
|
|
unsafe impl<T: ?Sized + Send> Send for DmaBuffer<T> {}
|
|
unsafe impl<T: ?Sized + Sync> Sync for DmaBuffer<T> {}
|
|
|
|
impl<T: ?Sized> Drop for DmaBuffer<T> {
|
|
fn drop(&mut self) {
|
|
log::trace!("Drop DmaBuffer @ {:#x}", self.host_physical);
|
|
unsafe {
|
|
ptr::drop_in_place(self.host_pointer.as_ptr());
|
|
for i in 0..self.page_count {
|
|
phys::free_page(self.host_physical.add(i * L3_PAGE_SIZE));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> AsPhysicalAddress for DmaBuffer<T> {
|
|
#[inline]
|
|
unsafe fn as_physical_address(&self) -> PhysicalAddress {
|
|
self.host_physical
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for DmaBuffer<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
unsafe { self.host_pointer.as_ref() }
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for DmaBuffer<T> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
unsafe { self.host_pointer.as_mut() }
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> fmt::Pointer for DmaBuffer<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "<dma@{:#x}>", self.bus_address)
|
|
}
|
|
}
|
|
|
|
impl BusAddress {
|
|
pub const ZERO: Self = Self(0);
|
|
|
|
pub const fn into_u64(self) -> u64 {
|
|
self.0
|
|
}
|
|
|
|
pub fn try_into_u32(self) -> Result<u32, Error> {
|
|
self.0.try_into().map_err(|_| Error::InvalidMemoryOperation)
|
|
}
|
|
|
|
pub const fn add(self, offset: usize) -> Self {
|
|
Self(self.0 + offset as u64)
|
|
}
|
|
}
|
|
|
|
impl Sub<BusAddress> for BusAddress {
|
|
type Output = usize;
|
|
|
|
fn sub(self, rhs: BusAddress) -> Self::Output {
|
|
(self.0 - rhs.0).try_into().unwrap()
|
|
}
|
|
}
|
|
|
|
impl fmt::LowerHex for BusAddress {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::LowerHex::fmt(&self.0, f)
|
|
}
|
|
}
|