| 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)); |
| } |
| } |