| // Copyright 2022 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use crate::block_device::*; |
| use crate::errors::*; |
| use crate::utils::*; |
| |
| pub struct MemmapDevice { |
| pub geom: BlockDeviceGeometry, |
| pub flash_base: *mut u8, |
| } |
| |
| impl MemmapDevice { |
| pub fn new(geom: BlockDeviceGeometry, flash_base: *mut u8) -> Self { |
| let result = MemmapDevice { |
| geom: geom, |
| flash_base: flash_base, |
| }; |
| return result; |
| } |
| } |
| |
| impl MemmapDevice { |
| 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)?; |
| 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)?; |
| 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)?; |
| 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())?; |
| return Ok(()); |
| } |
| |
| pub fn check_write_range(&self, addr: usize, data: &[u8]) -> Result<(), BFSErr> { |
| self.check_range(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 BlockDevice for MemmapDevice { |
| 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); |
| } |
| |
| 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; |
| 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()); |
| } |
| |
| 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]); |
| } |
| } |
| |
| return Ok(()); |
| } |
| } |
| |
| /// Overwriting existing data should write the logical AND of the old and new |
| /// data. |
| |
| #[test] |
| fn test_overwrite() { |
| let geom = BlockDeviceGeometry { |
| block_size: 16, |
| block_count: 4, |
| }; |
| let mut buf: Vec<u8> = vec![0; geom.block_count * geom.block_size]; |
| let bd: &mut dyn BlockDevice = &mut MemmapDevice::new(geom, buf.as_mut_ptr()); |
| |
| 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() { |
| let geom = BlockDeviceGeometry { |
| block_size: 16, |
| block_count: 4, |
| }; |
| let mut buf: Vec<u8> = vec![0; geom.block_count * geom.block_size]; |
| let bd: &mut dyn BlockDevice = &mut MemmapDevice::new(geom, buf.as_mut_ptr()); |
| |
| 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() { |
| let geom = BlockDeviceGeometry { |
| block_size: 16, |
| block_count: 4, |
| }; |
| let mut buf: Vec<u8> = vec![0; geom.block_count * geom.block_size]; |
| let bd: &mut dyn BlockDevice = &mut MemmapDevice::new(geom, buf.as_mut_ptr()); |
| |
| let mut dst_block = vec![0; geom.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() { |
| let geom = BlockDeviceGeometry { |
| block_size: 16, |
| block_count: 4, |
| }; |
| let mut buf: Vec<u8> = vec![0; geom.block_count * geom.block_size]; |
| let bd: &mut dyn BlockDevice = &mut MemmapDevice::new(geom, buf.as_mut_ptr()); |
| |
| let mut src_block = vec![0; geom.block_size]; |
| let mut dst_block = vec![0; geom.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() { |
| let geom = BlockDeviceGeometry { |
| block_size: 16, |
| block_count: 4, |
| }; |
| let mut buf: Vec<u8> = vec![0; geom.block_count * geom.block_size]; |
| let bd: &mut dyn BlockDevice = &mut MemmapDevice::new(geom, buf.as_mut_ptr()); |
| |
| let mut src_block = vec![0; geom.block_size]; |
| let mut dst_block = vec![0; geom.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)); |
| } |
| } |