use alloc::sync::Arc;
use core::{any::Any, mem::size_of};
use ax_runtime::hal::cpu::asm::user_copy;
use axfs_ng_vfs::{DeviceId, VfsError, VfsResult};
use linux_raw_sys::general::O_CLOEXEC;
use crate::{
file::{add_file_like, close_file_like, dmabuf::DmaBufFile},
pseudofs::DeviceOps,
};
pub const DMA_HEAP_DEVICE_ID: DeviceId = DeviceId::new(0xF1, 0x20);
pub const HEAP_NAMES: &[&str] = &[
"system",
"system-uncached",
"system-dma32",
"system-uncached-dma32",
"cma",
"cma-uncached",
"cma-dma32",
"cma-uncached-dma32",
];
const DMA_HEAP_IOCTL_ALLOC: u32 = 0xC018_4800;
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct DmaHeapAllocData {
len: u64,
fd: u32,
fd_flags: u32,
heap_flags: u64,
}
pub struct DmaHeap;
impl DeviceOps for DmaHeap {
fn read_at(&self, _buf: &mut [u8], _offset: u64) -> VfsResult<usize> {
Ok(0)
}
fn write_at(&self, buf: &[u8], _offset: u64) -> VfsResult<usize> {
Ok(buf.len())
}
fn as_any(&self) -> &dyn Any {
self
}
fn ioctl(&self, cmd: u32, arg: usize) -> VfsResult<usize> {
if cmd != DMA_HEAP_IOCTL_ALLOC {
return Err(VfsError::NotATty);
}
if arg == 0 {
return Err(VfsError::InvalidInput);
}
let mut data = DmaHeapAllocData::default();
copy_in(&mut data, arg)?;
if data.len == 0 {
return Err(VfsError::InvalidInput);
}
let buf = DmaBufFile::alloc(data.len as usize)?;
let cloexec = data.fd_flags & O_CLOEXEC != 0;
let fd = add_file_like(Arc::new(buf), cloexec)?;
data.fd = fd as u32;
if let Err(e) = copy_out(&data, arg) {
let _ = close_file_like(fd);
return Err(e);
}
Ok(0)
}
}
fn copy_in(dst: &mut DmaHeapAllocData, uaddr: usize) -> VfsResult<()> {
let ret = unsafe {
user_copy(
dst as *mut DmaHeapAllocData as *mut u8,
uaddr as *const u8,
size_of::<DmaHeapAllocData>(),
)
};
if ret != 0 {
return Err(VfsError::InvalidData);
}
Ok(())
}
fn copy_out(src: &DmaHeapAllocData, uaddr: usize) -> VfsResult<()> {
let ret = unsafe {
user_copy(
uaddr as *mut u8,
src as *const DmaHeapAllocData as *const u8,
size_of::<DmaHeapAllocData>(),
)
};
if ret != 0 {
return Err(VfsError::InvalidData);
}
Ok(())
}