blob: 3fe74ec46b85e19437e5e5b4e1ce81295745b935 [file] [log] [blame]
// 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));
}
}