blob: 69fbf2e6bd5141fa0ac9786b566c9b347777732e [file] [log] [blame]
use crate::bit_vector::*;
use crate::block_device::*;
use crate::errors::*;
use crate::utils::*;
pub struct TestDevice<'a> {
pub geom: BlockDeviceGeometry,
pub flash_base: *mut u8,
pub dirty: BitVector<'a>,
}
impl<'a> TestDevice<'a> {
pub fn new(geom: BlockDeviceGeometry, flash_base: *mut u8, dirty_bits: &'a mut [u32]) -> Self {
assert_eq!(geom.block_size * geom.block_count, dirty_bits.len() * 32);
let result =
TestDevice { geom: geom, flash_base: flash_base, dirty: BitVector::new(dirty_bits) };
return result;
}
pub fn dirty_count(&self, addr: usize, size: usize) -> Result<usize, BFSErr> {
let mut result: usize = 0;
for i in addr..addr + size {
result += self.dirty.get_bit(i)? as usize;
}
return Ok(result);
}
pub fn mark_dirty(&mut self, addr: usize, size: usize) -> Result<(), BFSErr> {
self.dirty.set_range(addr, addr + size)?;
return Ok(());
}
pub fn mark_clean(&mut self, addr: usize, size: usize) -> Result<(), BFSErr> {
self.dirty.clear_range(addr, addr + size)?;
return Ok(());
}
}
/// Preconditions for TestDevice.
/// FIXME - I loosened these up because they were annoying, not sure how strict
/// we should be...
impl<'a> TestDevice<'a> {
pub fn check_dirty(&self, addr: usize, size: usize) -> Result<(), BFSErr> {
for i in addr..addr + size {
dcheck!(self.dirty.get_bit(i)? == 1, BFSErr::CleanRead);
}
return Ok(());
}
pub fn check_clean(&self, addr: usize, size: usize) -> Result<(), BFSErr> {
for i in addr..addr + size {
dcheck!(self.dirty.get_bit(i)? == 0, BFSErr::DirtyWrite);
}
return Ok(());
}
pub fn check_is_block_sized(&self, data: &[u8]) -> Result<(), BFSErr> {
dcheck!(data.len() == self.geom.block_size, BFSErr::OutOfBounds);
return Ok(());
}
pub fn check_block_index(&self, iblock: usize) -> Result<(), BFSErr> {
dcheck!(iblock < self.geom.block_count, BFSErr::OutOfBounds);
return Ok(());
}
pub fn check_fits_in_block(&self, addr: usize, data: &[u8]) -> Result<(), BFSErr> {
let block_a = addr / self.geom.block_size;
let block_b = (addr + data.len() - 1) / self.geom.block_size;
dcheck!(block_a == block_b, BFSErr::OutOfBounds);
return Ok(());
}
pub fn check_read_block(&self, iblock: usize, block: &[u8]) -> Result<(), BFSErr> {
self.check_block_index(iblock)?;
self.check_fits_in_block(iblock * self.geom.block_size, block)?;
//self.check_dirty(iblock * self.geom.block_size, block.len())?;
return Ok(());
}
pub fn check_write_block(&self, iblock: usize, block: &[u8]) -> Result<(), BFSErr> {
self.check_block_index(iblock)?;
self.check_fits_in_block(iblock * self.geom.block_size, block)?;
self.check_clean(iblock * self.geom.block_size, block.len())?;
return Ok(());
}
pub fn check_erase_block(&self, iblock: usize) -> Result<(), BFSErr> {
self.check_block_index(iblock)?;
return Ok(());
}
pub fn check_erase_dirty_block(&self, iblock: usize) -> Result<(), BFSErr> {
self.check_erase_block(iblock)?;
let dirty_count = self.dirty_count(iblock * self.geom.block_size, self.geom.block_size)?;
dcheck!(dirty_count > 0, BFSErr::BadErase);
return Ok(());
}
pub fn check_range(&self, addr: usize, size: usize) -> Result<(), BFSErr> {
let bs = self.geom.block_size;
let bc = self.geom.block_count;
dcheck!(addr + size < (bc * bs), BFSErr::OutOfBounds);
return Ok(());
}
pub fn check_read_range(&self, addr: usize, data: &mut [u8]) -> Result<(), BFSErr> {
self.check_range(addr, data.len())?;
//self.check_dirty(addr, data.len())?;
return Ok(());
}
pub fn check_write_range(&self, addr: usize, data: &[u8]) -> Result<(), BFSErr> {
self.check_range(addr, data.len())?;
self.check_clean(addr, data.len())?;
return Ok(());
}
/// Check that this overwrite only changes bits from 1->0
pub fn check_overwrite_range(&self, addr: usize, data: &[u8]) -> Result<(), BFSErr> {
self.check_range(addr, data.len())?;
for i in 0..data.len() {
let src = data[i];
unsafe {
let dst = self.flash_base.add(addr + i).read();
dcheck!((src & !dst) == 0, BFSErr::BadOverwrite);
}
}
return Ok(());
}
}
impl<'a> BlockDevice for TestDevice<'a> {
fn geom(&self) -> BlockDeviceGeometry {
self.geom
}
/// Read a chunk of flash contained in a single block.
fn read_block(&self, iblock: usize, block: &mut [u8]) -> Result<(), BFSErr> {
self.check_read_block(iblock, block)?;
let bs = self.geom.block_size;
unsafe {
self.flash_base.add(iblock * bs).copy_to(block.as_mut_ptr(), block.len());
}
return Ok(());
}
/// Write a a chunk of flash contained in a single block.
fn write_block(&mut self, iblock: usize, block: &[u8]) -> Result<(), BFSErr> {
self.check_write_block(iblock, block)?;
let bs = self.geom.block_size;
unsafe {
self.flash_base.add(iblock * bs).copy_from(block.as_ptr(), bs);
}
self.mark_dirty(iblock * bs, bs)?;
return Ok(());
}
/// Erase an entire block of flash.
fn erase_block(&mut self, iblock: usize) -> Result<(), BFSErr> {
self.check_erase_block(iblock)?;
let bs = self.geom.block_size;
self.mark_clean(iblock * bs, bs)?;
unsafe {
self.flash_base.add(iblock * bs).write_bytes(0xFF, bs);
}
return Ok(());
}
/// Read a range of bytes in flash, checking first that those bytes have
/// been written since they were erased.
fn read_range(&self, addr: usize, data: &mut [u8]) -> Result<(), BFSErr> {
self.check_read_range(addr, data)?;
unsafe {
self.flash_base.add(addr).copy_to(data.as_mut_ptr(), data.len());
}
return Ok(());
}
/// Write to a range of bytes in flash, checking first that those bytes have
/// not been written since they were erased.
fn write_range(&mut self, addr: usize, data: &[u8]) -> Result<(), BFSErr> {
self.check_write_range(addr, data)?;
unsafe {
self.flash_base.add(addr).copy_from(data.as_ptr(), data.len());
}
self.mark_dirty(addr, data.len())?;
return Ok(());
}
/// Write to a range of bytes in flash, but _do_ allow writing over dirty
/// bytes - the result in flash will be the logical AND of the old and new
/// data.
fn overwrite_range(&mut self, addr: usize, data: &[u8]) -> Result<(), BFSErr> {
self.check_overwrite_range(addr, data)?;
unsafe {
for i in 0..data.len() {
let ptr = self.flash_base.add(addr + i);
ptr.write(ptr.read() & data[i]);
}
}
self.mark_dirty(addr, data.len())?;
return Ok(());
}
}
/// Overwriting existing data should write the logical AND of the old and new
/// data.
#[test]
fn test_overwrite() {
const BLOCK_SIZE: usize = 16;
const BLOCK_COUNT: usize = 4;
let geom = BlockDeviceGeometry { block_size: BLOCK_SIZE, block_count: BLOCK_COUNT };
let mut buf: [u8; BLOCK_SIZE * BLOCK_COUNT] = [0; BLOCK_SIZE * BLOCK_COUNT];
let mut dirty_bits = [0; BLOCK_SIZE * BLOCK_COUNT / 32];
let bd: &mut dyn BlockDevice = &mut TestDevice::new(geom, buf.as_mut_ptr(), &mut dirty_bits);
let write1: u32 = 0xFF0FF0FF;
let write2: u32 = 0x0F00F000;
assert_ok!(bd.write_range(0, &write1.to_le_bytes()));
assert_ok!(bd.overwrite_range(0, &write2.to_le_bytes()));
let mut result: u32 = 0;
assert_ok!(bd.read_range(0, as_unsafe_blob_mut(&mut result)));
assert_eq!(result, 0xFF0FF0FF & 0x0FF0FF00);
}
/// Trying to set bits that have already been cleared by a previous write should
/// cause a panic in the test device.
#[test]
#[should_panic]
fn test_bad_overwrite() {
const BLOCK_SIZE: usize = 16;
const BLOCK_COUNT: usize = 4;
let geom = BlockDeviceGeometry { block_size: BLOCK_SIZE, block_count: BLOCK_COUNT };
let mut buf: [u8; BLOCK_SIZE * BLOCK_COUNT] = [0; BLOCK_SIZE * BLOCK_COUNT];
let mut dirty_bits = [0; BLOCK_SIZE * BLOCK_COUNT / 32];
let bd: &mut dyn BlockDevice = &mut TestDevice::new(geom, buf.as_mut_ptr(), &mut dirty_bits);
let all_1: u32 = 0xFFFFFFFF;
let all_0: u32 = 0x00000000;
assert_ok!(bd.write_range(0, &all_0.to_le_bytes()));
assert_err!(bd.overwrite_range(0, &all_1.to_le_bytes()));
}
/// Trying to read blocks that have never been written should cause a panic in
/// the test device.
#[test]
#[should_panic]
fn test_read_unwritten_block() {
const BLOCK_SIZE: usize = 16;
const BLOCK_COUNT: usize = 4;
let geom = BlockDeviceGeometry { block_size: BLOCK_SIZE, block_count: BLOCK_COUNT };
let mut buf: [u8; BLOCK_SIZE * BLOCK_COUNT] = [0; BLOCK_SIZE * BLOCK_COUNT];
let mut dirty_bits = [0; BLOCK_SIZE * BLOCK_COUNT / 32];
let bd: &mut dyn BlockDevice = &mut TestDevice::new(geom, buf.as_mut_ptr(), &mut dirty_bits);
let mut dst_block = [0; BLOCK_SIZE];
assert_err!(bd.read_block(0, &mut dst_block));
}
/// Trying to read blocks that have been written and then erased should cause a
/// panic in the test device.
#[test]
#[should_panic]
fn test_read_erased_block() {
const BLOCK_SIZE: usize = 16;
const BLOCK_COUNT: usize = 4;
let geom = BlockDeviceGeometry { block_size: BLOCK_SIZE, block_count: BLOCK_COUNT };
let mut buf: [u8; BLOCK_SIZE * BLOCK_COUNT] = [0; BLOCK_SIZE * BLOCK_COUNT];
let mut dirty_bits = [0; BLOCK_SIZE * BLOCK_COUNT / 32];
let bd: &mut dyn BlockDevice = &mut TestDevice::new(geom, buf.as_mut_ptr(), &mut dirty_bits);
let mut src_block = [0; BLOCK_SIZE];
let mut dst_block = [0; BLOCK_SIZE];
for i in 0..geom.block_size {
src_block[i] = i as u8;
}
assert_ok!(bd.write_block(0, &mut src_block));
assert_ok!(bd.erase_block(0));
assert_err!(bd.read_block(0, &mut dst_block));
}
/// All blocks in a device should be readable and writable.
#[test]
fn test_read_write() {
const BLOCK_SIZE: usize = 16;
const BLOCK_COUNT: usize = 4;
let geom = BlockDeviceGeometry { block_size: BLOCK_SIZE, block_count: BLOCK_COUNT };
let mut buf: [u8; BLOCK_SIZE * BLOCK_COUNT] = [0; BLOCK_SIZE * BLOCK_COUNT];
let mut dirty_bits = [0; BLOCK_SIZE * BLOCK_COUNT / 32];
let bd: &mut dyn BlockDevice = &mut TestDevice::new(geom, buf.as_mut_ptr(), &mut dirty_bits);
let mut src_block = [0; BLOCK_SIZE];
let mut dst_block = [0; BLOCK_SIZE];
for i in 0..geom.block_size {
src_block[i] = i as u8;
}
// Writing clean blocks should succeed.
for i in 0..geom.block_count {
assert_ok!(bd.write_block(i, &src_block));
}
// Reading those blocks back should succeed, and the contents should match
// the original block.
for i in 0..geom.block_count {
assert_ok!(bd.read_block(i, &mut dst_block));
for j in 0..geom.block_size {
assert_eq!(src_block[j], dst_block[j]);
}
}
// Erasing blocks should succeed.
for i in 0..geom.block_count {
assert_ok!(bd.erase_block(i));
}
}