230 lines
6.2 KiB
Rust

use core::{any::Any, mem::MaybeUninit, ops::Deref};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use libk_mm::PageProvider;
use yggdrasil_abi::error::Error;
use crate::{
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
vfs::{CommonImpl, NodeRef},
};
pub mod cache;
pub mod partition;
struct Chunked {
bs: usize,
bpr: usize,
position: u64,
offset: usize,
lba: u64,
remaining_lba: usize,
remaining: usize,
}
impl Chunked {
pub fn begin<D: BlockDevice + ?Sized>(
device: &D,
position: u64,
len: usize,
) -> Option<(Self, usize)> {
let bc = device.block_count();
let bs = device.block_size();
let bpr = device.max_blocks_per_request();
let start_lba = position / bs as u64;
let end_lba = (position + len as u64).div_ceil(bs as u64).min(bc);
if start_lba >= end_lba {
return None;
}
let remaining_lba = (end_lba - start_lba) as usize;
let remaining = len;
let max_lba_count = remaining_lba.min(bpr);
Some((
Self {
bs,
bpr,
position,
lba: start_lba,
remaining_lba,
remaining,
offset: 0,
},
max_lba_count,
))
}
}
impl Iterator for Chunked {
// 0: lba
// 1: lba_count
// 2: offset in block
// 3: offset in buffer
// 4: amount
type Item = (u64, usize, usize, usize, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
assert_eq!(self.remaining_lba, 0);
return None;
}
let block_count = self.remaining_lba.min(self.bpr);
let block_offset = (self.position % self.bs as u64) as usize;
let amount = (block_count * self.bs - block_offset).min(self.remaining);
let item = (self.lba, block_count, block_offset, self.offset, amount);
self.lba += block_count as u64;
self.remaining_lba -= block_count;
self.remaining -= amount;
self.offset += amount;
Some(item)
}
}
#[async_trait]
pub trait BlockDevice: Device + PageProvider {
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error>;
async fn read_aligned(
&self,
position: u64,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<(), Error> {
let _ = (position, buffer);
Err(Error::NotImplemented)
}
async fn write_aligned(&self, position: u64, buffer: DmaSlice<'_, u8>) -> Result<(), Error> {
let _ = (position, buffer);
Err(Error::NotImplemented)
}
async fn read(&self, position: u64, buffer: &mut [u8]) -> Result<usize, Error> {
let bs = self.block_size();
let Some((iter, max_lba_count)) = Chunked::begin(self, position, buffer.len()) else {
return Ok(0);
};
let mut read_buffer = self.allocate_buffer(max_lba_count * bs)?;
// let mut read_buffer = PageBox::new_uninit_slice(max_lba_count * bs)?;
let mut total = 0;
for (lba, block_count, block_offset, offset, amount) in iter {
let slice = read_buffer.slice_mut(0..block_count * bs);
self.read_aligned(lba * bs as u64, slice).await?;
let src = unsafe {
MaybeUninit::slice_assume_init_ref(
&read_buffer[block_offset..block_offset + amount],
)
};
let dst = &mut buffer[offset..offset + amount];
dst.copy_from_slice(src);
total += amount;
}
Ok(total)
}
async fn write(&self, mut pos: u64, mut buf: &[u8]) -> Result<usize, Error> {
let bs = self.block_size();
let len = buf.len();
let mut remaining = buf.len();
while remaining != 0 {
let block_offset = (pos % bs as u64) as usize;
let lba = pos / bs as u64;
let amount = core::cmp::min(bs - block_offset, buf.len());
let mut dma_buffer = self.allocate_buffer(bs)?;
if amount != bs {
// Need to read the block first -- it's modified partially
self.read_aligned(lba * bs as u64, dma_buffer.slice_mut(0..bs))
.await?;
}
let mut block = unsafe { DmaBuffer::assume_init_slice(dma_buffer) };
block[block_offset..block_offset + amount].copy_from_slice(&buf[..amount]);
// Write the block back
self.write_aligned(lba * bs as u64, block.slice(0..bs))
.await?;
buf = &buf[amount..];
remaining -= amount;
pos += amount as u64;
}
Ok(len)
}
async fn read_exact(&self, position: u64, buffer: &mut [u8]) -> Result<(), Error> {
match self.read(position, buffer).await {
Ok(len) if len == buffer.len() => Ok(()),
Ok(_) => Err(Error::InvalidArgument),
Err(error) => Err(error),
}
}
async fn write_exact(&self, position: u64, buffer: &[u8]) -> Result<(), Error> {
match self.write(position, buffer).await {
Ok(len) if len == buffer.len() => Ok(()),
Ok(_) => Err(Error::InvalidArgument),
Err(error) => Err(error),
}
}
fn is_readable(&self) -> bool {
true
}
fn is_writeable(&self) -> bool {
true
}
fn block_size(&self) -> usize;
fn block_count(&self) -> u64;
fn max_blocks_per_request(&self) -> usize;
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
let _ = option;
let _ = buffer;
let _ = len;
Err(Error::InvalidOperation)
}
}
#[derive(Clone)]
pub struct BlockDeviceFile(pub(crate) Arc<dyn BlockDevice>);
impl CommonImpl for BlockDeviceFile {
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
let _ = node;
Ok(self.0.block_count() * self.0.block_size() as u64)
}
fn as_any(&self) -> &dyn Any {
self as _
}
}
impl Deref for BlockDeviceFile {
type Target = dyn BlockDevice;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}