Remove the last of the Matcha stuff from the tock tree.

Change-Id: I6f94241778708ec772e4b711493405a39076b1ee
diff --git a/Cargo.toml b/Cargo.toml
index 09c0d5b..4970e92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -42,7 +42,6 @@
     "chips/stm32f412g",
     "chips/stm32f4xx",
     "kernel",
-    "libraries/blob_fs",
     "libraries/enum_primitive",
     "libraries/riscv-csr",
     "libraries/tock-cells",
diff --git a/capsules/Cargo.toml b/capsules/Cargo.toml
index ce77686..f98c797 100644
--- a/capsules/Cargo.toml
+++ b/capsules/Cargo.toml
@@ -7,4 +7,3 @@
 [dependencies]
 kernel = { path = "../kernel" }
 enum_primitive = { path = "../libraries/enum_primitive" }
-blob_fs = { path = "../libraries/blob_fs" }
diff --git a/capsules/src/debug_uart.rs b/capsules/src/debug_uart.rs
deleted file mode 100644
index a9ec8a4..0000000
--- a/capsules/src/debug_uart.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-//! Trivial capsule to provide minimal debug printing support for userspace.
-//!
-//! The default TockOS Console class does heavier-weight stuff with UART
-//! multiplexing and asynchronous sending and such, which is usually _not_ what
-//! is wanted in a debug serial port. This capsule implements only one command,
-//! which dumps data from an allow'ed buffer directly to a memory-mapped UART
-//! peripheral.
-//!
-//! Instantiation:
-//!   let debug_uart = static_init!(
-//!     DebugUart,
-//!     DebugUart {
-//!         tx_busy: StaticRef::new(TX_BUSY_ADDR as *const ReadOnly<u32>),
-//!         tx_port: StaticRef::new(TX_PORT_ADDR as *const WriteOnly<u32>),
-//!         app_data_grant: board_kernel.create_grant(&memory_allocation_cap)
-//!     }
-//!   );
-//!
-//! where TX_BUSY_ADDR is a register whose low bit is 1 if the UART's fifo is
-//! full and TX_PORT_ADDR is the register we write bytes to.
-//!
-//! Usage - send buffer directly to UART:
-//!   let driver_num = capsules::debug_uart::DRIVER_NUM;
-//!   let allow = syscalls::allow(driver_num, 0, &mut buffer);
-//!   let result = syscalls::command(driver_num, 0, buffer.len(), 0);
-//!   drop(allow);
-
-use crate::driver;
-use kernel::common::registers::{ReadOnly, WriteOnly};
-use kernel::common::StaticRef;
-use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
-
-pub const DRIVER_NUM: usize = driver::NUM::DebugUart as usize;
-
-#[derive(Default)]
-pub struct AppData {
-    pub buffer: Option<AppSlice<Shared, u8>>,
-}
-
-pub struct DebugUart {
-    pub tx_busy: StaticRef<ReadOnly<u32>>,
-    pub tx_port: StaticRef<WriteOnly<u32>>,
-    pub app_data_grant: Grant<AppData>,
-}
-
-impl Driver for DebugUart {
-    fn subscribe(&self, _: usize, _: Option<Callback>, _: AppId) -> ReturnCode {
-        ReturnCode::EINVAL
-    }
-
-    fn command(&self, minor_num: usize, r2: usize, _: usize, app_id: AppId) -> ReturnCode {
-        if minor_num != 0 {
-            return ReturnCode::EINVAL;
-        }
-
-        let _ = self.app_data_grant.enter(app_id, |app_data, _| {
-            if let Some(buf) = &app_data.buffer {
-                for i in 0..r2 {
-                    while (self.tx_busy.get() & 1) != 0 {}
-                    self.tx_port.set(buf.as_ref()[i] as u32);
-                }
-            }
-        });
-        return ReturnCode::SUCCESS;
-    }
-
-    fn allow(&self, app_id: AppId, _: usize, slice: Option<AppSlice<Shared, u8>>) -> ReturnCode {
-        let _ = self.app_data_grant.enter(app_id, |app_data, _| {
-            app_data.buffer = slice;
-        });
-        return ReturnCode::SUCCESS;
-    }
-}
diff --git a/capsules/src/lib.rs b/capsules/src/lib.rs
index c7743f1..1b32616 100644
--- a/capsules/src/lib.rs
+++ b/capsules/src/lib.rs
@@ -22,7 +22,6 @@
 pub mod crc;
 pub mod dac;
 pub mod debug_process_restart;
-pub mod debug_uart;
 pub mod driver;
 pub mod fm25cl;
 pub mod ft6x06;
diff --git a/libraries/blob_fs/.gitignore b/libraries/blob_fs/.gitignore
deleted file mode 100644
index a9d37c5..0000000
--- a/libraries/blob_fs/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-target
-Cargo.lock
diff --git a/libraries/blob_fs/Cargo.toml b/libraries/blob_fs/Cargo.toml
deleted file mode 100644
index b99aa17..0000000
--- a/libraries/blob_fs/Cargo.toml
+++ /dev/null
@@ -1,4 +0,0 @@
-[package]
-name = "blob_fs"
-version = "0.1.0"
-edition = "2018"
diff --git a/libraries/blob_fs/src/bit_vector.rs b/libraries/blob_fs/src/bit_vector.rs
deleted file mode 100644
index 336b4a2..0000000
--- a/libraries/blob_fs/src/bit_vector.rs
+++ /dev/null
@@ -1,293 +0,0 @@
-use crate::errors::*;
-use crate::utils::*;
-
-pub struct BitVector<'a> {
-    pub bits: &'a mut [u32],
-}
-
-impl<'a> BitVector<'a> {
-    pub fn new(bits: &'a mut [u32]) -> Self {
-        return BitVector { bits: bits };
-    }
-
-    pub fn check_pos(&self, pos: usize) -> Result<(), BFSErr> {
-        dcheck!(pos <= self.bits.len() * 32, BFSErr::OutOfBounds);
-        return Ok(());
-    }
-
-    pub fn check_range(&self, begin: usize, end: usize) -> Result<(), BFSErr> {
-        self.check_pos(begin)?;
-        self.check_pos(end)?;
-        dcheck!(end >= begin, BFSErr::OutOfBounds);
-        return Ok(());
-    }
-
-    pub fn clear_all(&mut self) {
-        for i in 0..self.bits.len() {
-            self.bits[i] = 0x00000000;
-        }
-    }
-
-    pub fn set_all(&mut self) {
-        for i in 0..self.bits.len() {
-            self.bits[i] = 0xFFFFFFFF;
-        }
-    }
-
-    pub fn get_bit(&self, pos: usize) -> Result<u32, BFSErr> {
-        self.check_pos(pos)?;
-        return Ok((self.bits[pos >> 5] >> (pos & 31)) & 1);
-    }
-
-    pub fn set_bit(&mut self, pos: usize) -> Result<(), BFSErr> {
-        self.check_pos(pos)?;
-        self.bits[pos >> 5] |= 1 << (pos & 31);
-        return Ok(());
-    }
-
-    pub fn clear_bit(&mut self, pos: usize) -> Result<(), BFSErr> {
-        self.check_pos(pos)?;
-        self.bits[pos >> 5] &= !(1 << (pos & 31));
-        return Ok(());
-    }
-
-    fn bit_mask(head: usize, tail: usize) -> Result<u32, BFSErr> {
-        dcheck!(head < 32, BFSErr::OutOfBounds);
-        dcheck!(tail < 32, BFSErr::OutOfBounds);
-
-        let a = 0xFFFFFFFF << head;
-        let b = 0xFFFFFFFF >> (32 - tail - 1);
-        return Ok(a & b);
-    }
-
-    pub fn set_range(&mut self, begin: usize, end: usize) -> Result<(), BFSErr> {
-        self.check_range(begin, end)?;
-
-        let block_head = begin >> 5;
-        let block_tail = (end - 1) >> 5;
-        let bit_head = begin & 31;
-        let bit_tail = (end - 1) & 31;
-
-        if block_head == block_tail {
-            let mask = BitVector::bit_mask(bit_head, bit_tail)?;
-            self.bits[block_head as usize] |= mask;
-        } else {
-            let mask_head = BitVector::bit_mask(bit_head, 31)?;
-            self.bits[block_head as usize] |= mask_head;
-
-            for i in block_head + 1..block_tail {
-                self.bits[i as usize] = 0xFFFFFFFF;
-            }
-
-            let mask_tail = BitVector::bit_mask(0, bit_tail)?;
-            self.bits[block_tail as usize] |= mask_tail;
-        }
-
-        return Ok(());
-    }
-
-    pub fn clear_range(&mut self, begin: usize, end: usize) -> Result<(), BFSErr> {
-        self.check_range(begin, end)?;
-
-        let block_head = begin >> 5;
-        let block_tail = (end - 1) >> 5;
-        let bit_head = begin & 31;
-        let bit_tail = (end - 1) & 31;
-
-        if block_head == block_tail {
-            let mask = BitVector::bit_mask(bit_head, bit_tail)?;
-            self.bits[block_head] &= !mask;
-        } else {
-            let mask_head = BitVector::bit_mask(bit_head, 31)?;
-            self.bits[block_head] &= !mask_head;
-
-            for i in block_head + 1..block_tail {
-                self.bits[i] = 0x00000000;
-            }
-
-            let mask_tail = BitVector::bit_mask(0, bit_tail)?;
-            self.bits[block_tail] &= !mask_tail;
-        }
-
-        return Ok(());
-    }
-
-    pub fn count_range(&mut self, begin: usize, end: usize) -> Result<usize, BFSErr> {
-        self.check_range(begin, end)?;
-
-        let block_head = begin >> 5;
-        let block_tail = (end - 1) >> 5;
-        let bit_head = begin & 31;
-        let bit_tail = (end - 1) & 31;
-
-        let mut count: usize = 0;
-
-        if block_head == block_tail {
-            let mask = BitVector::bit_mask(bit_head, bit_tail)?;
-            count += (self.bits[block_head] & mask).count_ones() as usize;
-        } else {
-            let mask_head = BitVector::bit_mask(bit_head, 31)?;
-            count += (self.bits[block_head] & mask_head).count_ones() as usize;
-
-            for i in block_head + 1..block_tail {
-                count += self.bits[i].count_ones() as usize;
-            }
-
-            let mask_tail = BitVector::bit_mask(0, bit_tail)?;
-            count += (self.bits[block_tail] & mask_tail).count_ones() as usize;
-        }
-
-        return Ok(count);
-    }
-
-    pub fn find_hole(&self, mut begin: usize, end: usize, width: usize) -> Result<usize, BFSErr> {
-        self.check_range(begin, end)?;
-        dcheck!(width <= end - begin, BFSErr::NotFound);
-
-        let mut skip = false;
-        let mut block_head = begin >> 5;
-        let block_tail = (end - 1) >> 5;
-
-        while (block_head <= block_tail) && (self.bits[block_head] == 0xFFFFFFFF) {
-            skip = true;
-            block_head = block_head + 1;
-        }
-
-        if block_head > block_tail {
-            return Err(BFSErr::NotFound);
-        }
-
-        if skip {
-            begin = block_head << 5;
-        }
-
-        for mut i in begin..=(end - width) {
-            skip = false;
-            for j in (i..i + width).rev() {
-                if self.get_bit(j)? == 1 {
-                    i = j + 1;
-                    skip = true;
-                    break;
-                }
-            }
-            if !skip {
-                return Ok(i);
-            }
-        }
-
-        return Err(BFSErr::NotFound);
-    }
-
-    pub fn find_span(&self, mut begin: usize, end: usize, width: usize) -> Result<usize, BFSErr> {
-        self.check_range(begin, end)?;
-        dcheck!(width <= end - begin, BFSErr::NotFound);
-
-        let mut skip = false;
-        let mut block_head = begin >> 5;
-        let block_tail = (end - 1) >> 5;
-
-        while (block_head <= block_tail) && (self.bits[block_head] == 0x00000000) {
-            skip = true;
-            block_head = block_head + 1;
-        }
-
-        if block_head > block_tail {
-            return Err(BFSErr::NotFound);
-        }
-
-        if skip {
-            begin = block_head << 5;
-        }
-
-        for mut i in begin..=(end - width) {
-            skip = false;
-            for j in (i..i + width).rev() {
-                if self.get_bit(j)? == 0 {
-                    i = j + 1;
-                    skip = true;
-                    break;
-                }
-            }
-            if !skip {
-                return Ok(i);
-            }
-        }
-
-        return Err(BFSErr::NotFound);
-    }
-}
-
-/// For all vector sizes up to <N> and all possible hole sizes & positions in
-/// that range, create a vector consisting of set bits outside the hole and
-/// cleared bits inside the hole.
-///
-/// Verify that findHole() always finds holes equal to or smaller than the one
-/// punched and fails to find holes larger than the one punched.
-///
-/// (This test is a bit slow, so we limit maximum bit vector size to 96 and run
-/// tests in this crate in optimized mode)
-
-#[test]
-fn test_find_hole() {
-    for size in 1..=96 as usize {
-        let block_count = (size + 31) / 32;
-        let mut bits: Vec<u32> = vec![0xFFFFFFFF as u32; block_count];
-        let mut bit_vec = BitVector::new(bits.as_mut());
-        for width in 1..=size - 1 {
-            for begin in 0..=(size - width) {
-                // Punch a hole in the bit vector.
-                let end = begin + width;
-                assert_ok!(bit_vec.clear_range(begin, end));
-
-                // We should be able to find the hole if we look for it.
-                assert_ok!(bit_vec.find_hole(0, size, width));
-
-                // We should be able to find a hole smaller than the one we punched.
-                assert_ok!(bit_vec.find_hole(0, size, width - 1));
-
-                // If we look for a hole larger than the one we punched, we should
-                // find nothing.
-                assert_err!(bit_vec.find_hole(0, size, width + 1));
-
-                // Fill the hole back up.
-                bit_vec.set_range(begin, end).unwrap();
-
-                // We should no longer be able to find it.
-                assert_err!(bit_vec.find_hole(0, size, width));
-            }
-        }
-    }
-}
-
-/// Same as above, but set bits (spans) instead of holes.
-#[test]
-fn test_find_span() {
-    for size in 1..=96 {
-        let block_count = (size + 31) / 32;
-        let mut bits: Vec<u32> = vec![0x00000000 as u32; block_count];
-        let mut bit_vec = BitVector::new(bits.as_mut());
-        for width in 1..=size - 1 {
-            for begin in 0..=(size - width) {
-                // Create a span in the bit vector.
-                let end = begin + width;
-                assert_ok!(bit_vec.set_range(begin, end));
-
-                // We should be able to find the hole if we look for it.
-                assert_ok!(bit_vec.find_span(0, size, width));
-
-                // We should be able to find a hole smaller than the one we punched.
-                assert_ok!(bit_vec.find_span(0, size, width - 1));
-
-                // If we look for a hole larger than the one we punched, we should
-                // find nothing.
-                assert_err!(bit_vec.find_span(0, size, width + 1));
-
-                // Erase the span
-                bit_vec.clear_range(begin, end).unwrap();
-
-                // We should no longer be able to find it.
-                assert_err!(bit_vec.find_span(0, size, width));
-            }
-        }
-    }
-}
diff --git a/libraries/blob_fs/src/blob_device.rs b/libraries/blob_fs/src/blob_device.rs
deleted file mode 100644
index 0a6c48f..0000000
--- a/libraries/blob_fs/src/blob_device.rs
+++ /dev/null
@@ -1,206 +0,0 @@
-/// A wrapper around BlockDevice that adds helper methods for navigating a
-/// BlobFS filesystem.
-use crate::bit_vector::*;
-use crate::block_device::*;
-use crate::errors::*;
-use crate::structs::*;
-use crate::utils::*;
-
-use core::mem;
-
-pub struct BlobDevice<'a> {
-    pub bd: &'a mut dyn BlockDevice,
-}
-
-impl<'a> BlobDevice<'a> {
-    const MAGIC_0: u64 = 0xac2153479e694d21;
-    const MAGIC_1: u64 = 0x985000d4d4d3d314;
-
-    pub fn new(bd: &'a mut dyn BlockDevice) -> Self {
-        return BlobDevice { bd: bd };
-    }
-
-    pub fn sanity_check(&self) -> Result<(), BFSErr> {
-        return Ok(());
-    }
-
-    /// Base address fetchers
-
-    pub fn superblock_base(&self) -> usize {
-        return self.bd.geom().block_size * 0;
-    }
-
-    pub fn blockmap_base(&self) -> usize {
-        return self.bd.geom().block_size * 1;
-    }
-
-    pub fn nodemap_base(&self) -> usize {
-        return self.bd.geom().block_size * 2;
-    }
-
-    pub fn journal_base(&self) -> usize {
-        return self.bd.geom().block_size * 3;
-    }
-
-    pub fn block_base(&self) -> usize {
-        return self.bd.geom().block_size * 4;
-    }
-
-    /// Format a whole device to support BlobFS.
-
-    pub fn format(&mut self) -> Result<(), BFSErr> {
-        // Erase the whole device.
-        for i in 0..self.bd.geom().block_count {
-            self.bd.erase_block(i)?;
-        }
-
-        // Write the initial superblock.
-        self.format_superblock()?;
-
-        // Write the initial blockmap.
-        self.format_blockmap()?;
-
-        return Ok(());
-    }
-
-    pub fn read_superblock(&self, superblock: &mut Superblock) -> Result<(), BFSErr> {
-        let blob = as_unsafe_blob_mut(superblock);
-        self.bd.read_range(0, blob)?;
-
-        // Sanity check the superblock
-        dcheck!(superblock.magic0 == BlobDevice::MAGIC_0, BFSErr::Corrupt);
-        dcheck!(superblock.magic1 == BlobDevice::MAGIC_1, BFSErr::Corrupt);
-
-        return Ok(());
-    }
-
-    pub fn write_superblock(&mut self, superblock: &Superblock) -> Result<(), BFSErr> {
-        // Sanity check the superblock
-        dcheck!(superblock.magic0 == BlobDevice::MAGIC_0, BFSErr::Corrupt);
-        dcheck!(superblock.magic1 == BlobDevice::MAGIC_1, BFSErr::Corrupt);
-
-        let blob = as_unsafe_blob(superblock);
-        self.bd.write_range(0, blob)?;
-        return Ok(());
-    }
-
-    pub fn format_superblock(&mut self) -> Result<(), BFSErr> {
-        let block_size = self.bd.geom().block_size;
-        let block_count = self.bd.geom().block_count;
-
-        let superblock_count = 1;
-        let bitmap_block_count = 1;
-        let node_block_count = 1;
-        let journal_block_count = 1;
-        let data_block_count = block_count
-            - superblock_count
-            - bitmap_block_count
-            - node_block_count
-            - journal_block_count;
-
-        let inodes_per_block = block_size / mem::size_of::<Inode>();
-
-        // Write the superblock to offset 0
-        let superblock = Superblock {
-            magic0: BlobDevice::MAGIC_0,
-            magic1: BlobDevice::MAGIC_1,
-            version: 0,
-            flags: 0,
-            block_size: block_size as u32,
-            unused: 0,
-            data_block_count: data_block_count as u64,
-            journal_block_count: journal_block_count as u64,
-            inode_count: inodes_per_block as u64,
-            alloc_block_count: 0,
-            alloc_inode_count: 0,
-        };
-        self.write_superblock(&superblock)?;
-        return Ok(());
-    }
-
-    pub fn format_blockmap(&mut self) -> Result<(), BFSErr> {
-        let bits: [u8; 1] = [0xF0];
-        self.bd.write_range(self.blockmap_base(), &bits)?;
-        return Ok(());
-    }
-
-    pub fn read_blockmap(&self, bitmap: &mut BitVector) -> Result<(), BFSErr> {
-        self.bd.read_range(self.blockmap_base(), slice_as_unsafe_blob_mut(bitmap.bits))?;
-        return Ok(());
-    }
-
-    pub fn write_blockmap(&mut self, bitmap: &mut BitVector) -> Result<(), BFSErr> {
-        self.bd.write_range(self.blockmap_base(), slice_as_unsafe_blob(bitmap.bits))?;
-        return Ok(());
-    }
-
-    pub fn read_node_header(&self, index: usize, node: &mut NodeHeader) -> Result<(), BFSErr> {
-        let base = self.nodemap_base();
-        let offset = index * mem::size_of::<Inode>() * index;
-        let blob = as_unsafe_blob_mut(node);
-        self.bd.read_range(base + offset, blob)?;
-        return Ok(());
-    }
-
-    pub fn read_inode(&self, index: usize, inode: &mut Inode) -> Result<(), BFSErr> {
-        let base = self.nodemap_base();
-        let offset = index * mem::size_of::<Inode>();
-        let blob = as_unsafe_blob_mut(inode);
-        self.bd.read_range(base + offset, blob)?;
-        return Ok(());
-    }
-
-    pub fn write_inode(&mut self, index: usize, inode: &Inode) -> Result<(), BFSErr> {
-        dcheck!((inode.header.flags & NodeHeader::FLAG_INODE) != 0, BFSErr::InvalidArg);
-        let base = self.nodemap_base();
-        let offset = index * mem::size_of::<Inode>();
-        let blob = as_unsafe_blob(inode);
-        self.bd.write_range(base + offset, blob)?;
-        return Ok(());
-    }
-
-    /// Invalidate an inode by zeroing out its header. The inode cannot be reused
-    /// until the block containing it is erased.
-    pub fn invalidate_inode(&mut self, index: usize) -> Result<(), BFSErr> {
-        let base = self.nodemap_base();
-        let offset = index * mem::size_of::<Inode>();
-        let blob = [0; mem::size_of::<NodeHeader>()];
-        self.bd.overwrite_range(base + offset, &blob)?;
-        return Ok(());
-    }
-
-    pub fn blob_size_in_blocks(&self, blob: &[u8]) -> u16 {
-        let block_size = self.bd.geom().block_size;
-        let result = (blob.len() + block_size + 1) / block_size;
-        assert!(result < u16::MAX as usize);
-        return result as u16;
-    }
-
-    pub fn read_blob(&self, extent: Extent, blob_out: &mut [u8]) -> Result<(), BFSErr> {
-        let block_size = self.bd.geom().block_size;
-        let mut cursor = extent.offset();
-        for chunk in blob_out.chunks_mut(block_size) {
-            self.bd.read_block(cursor, chunk)?;
-            cursor = cursor + 1;
-        }
-        return Ok(());
-    }
-
-    pub fn write_blob(&mut self, extent: Extent, blob_in: &[u8]) -> Result<(), BFSErr> {
-        let block_size = self.bd.geom().block_size;
-        let mut cursor = extent.offset();
-        for chunk in blob_in.chunks(block_size) {
-            self.bd.write_block(cursor, &chunk)?;
-            cursor = cursor + 1;
-        }
-        return Ok(());
-    }
-
-    pub fn delete_blob(&mut self, extent: Extent) -> Result<(), BFSErr> {
-        let offset = extent.offset();
-        for i in 0..extent.size as usize {
-            self.bd.erase_block(offset + i)?;
-        }
-        return Ok(());
-    }
-}
diff --git a/libraries/blob_fs/src/blob_fs.rs b/libraries/blob_fs/src/blob_fs.rs
deleted file mode 100644
index 72e72d5..0000000
--- a/libraries/blob_fs/src/blob_fs.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-use crate::bit_vector::*;
-use crate::blob_device::*;
-use crate::block_device::*;
-use crate::errors::*;
-use crate::structs::*;
-use crate::utils::*;
-
-pub struct BlobFS<'a> {
-    pub bd: BlobDevice<'a>,
-    pub superblock: Superblock,
-    pub blockmap: BitVector<'a>,
-}
-
-// Public impl
-
-impl<'a> BlobFS<'a> {
-    pub fn new(bd: &'a mut dyn BlockDevice, blockmap_bits: &'a mut [u32]) -> Self {
-        let result = BlobFS {
-            bd: BlobDevice::new(bd),
-            superblock: Superblock::default(),
-            blockmap: BitVector::new(blockmap_bits),
-        };
-
-        return result;
-    }
-
-    pub fn format(&mut self) -> Result<(), BFSErr> {
-        // Format the device.
-        self.bd.format()?;
-
-        return Ok(());
-    }
-
-    pub fn mount(&mut self) -> Result<(), BFSErr> {
-        // Read the superblock from the device.
-        self.bd.read_superblock(&mut self.superblock)?;
-
-        // Read the allocation bitmap from the device
-        self.bd.read_blockmap(&mut self.blockmap)?;
-
-        return Ok(());
-    }
-
-    pub fn sanity_check(&self) -> Result<(), BFSErr> {
-        return Ok(());
-    }
-
-    pub fn get_blob_size(&self, hash: u64) -> Result<usize, BFSErr> {
-        let result = self.find_inode(hash)?;
-        let inode = result.1;
-        return Ok(inode.blob_size as usize);
-    }
-
-    pub fn get_blob(&self, hash: u64, blob_out: &mut [u8]) -> Result<(), BFSErr> {
-        let result = self.find_inode(hash)?;
-        let inode = result.1;
-        dcheck!(inode.blob_size as usize <= blob_out.len(), BFSErr::OutOfBounds);
-
-        let dst = &mut blob_out[..inode.blob_size as usize];
-        self.bd.read_blob(inode.inline_extent, dst)?;
-
-        return Ok(());
-    }
-
-    pub fn put_blob(&mut self, hash: u64, blob_in: &[u8]) -> Result<(), BFSErr> {
-        if self.find_inode(hash).is_ok() {
-            return Err(BFSErr::Duplicate);
-        }
-
-        let blob_block_count = self.bd.blob_size_in_blocks(blob_in);
-
-        // Find a place to put the blob.
-        let extent = self.find_extent(blob_block_count)?;
-
-        // Copy the blob to the block device.
-        self.bd.write_blob(extent, blob_in)?;
-
-        // Set the corresponding bits in our local bitmap
-        self.blockmap.clear_range(extent.offset(), extent.offset() + extent.size as usize)?;
-        // FIXME flush bitmap to disk?
-
-        // Create the inode for the new blob
-        let inode = Inode {
-            header: NodeHeader {
-                flags: NodeHeader::FLAG_INODE,
-                version: 0x0,
-                next_node: 0xFFFFFFFF, // FIXME
-            },
-            hash0: hash, // FIXME just using 64-bit hash
-            hash1: hash,
-            hash2: hash,
-            hash3: hash,
-            blob_size: blob_in.len() as u64,
-            block_count: blob_block_count as u32,
-            extent_count: 1,
-            padding: 0xFFFF,
-            inline_extent: extent,
-        };
-
-        // Put the inode in the inode table
-        let inode_idx = self.find_free_inode()?;
-        self.bd.write_inode(inode_idx, &inode)?;
-
-        return Ok(());
-    }
-
-    pub fn delete_blob(&mut self, hash: u64) -> Result<(), BFSErr> {
-        let result = self.find_inode(hash)?;
-        let inode_idx = result.0;
-        let extent = result.1.inline_extent;
-
-        self.bd.invalidate_inode(inode_idx)?;
-        self.blockmap.set_range(extent.offset(), extent.offset() + extent.size as usize)?;
-        self.bd.delete_blob(extent)?;
-        self.bd.delete_blob(extent)?;
-        return Ok(());
-    }
-}
-
-// Private impl
-
-impl<'a> BlobFS<'a> {
-    fn find_inode(&self, hash: u64) -> Result<(usize, Inode), BFSErr> {
-        for i in 0..self.superblock.inode_count as usize {
-            let mut inode = Inode::default();
-            self.bd.read_inode(i, &mut inode)?;
-            if inode.header.flags == NodeHeader::FLAG_INODE && inode.hash0 == hash {
-                return Ok((i, inode));
-            }
-        }
-        return Err(BFSErr::NotFound);
-    }
-
-    // FIXME quick and dirty scan entire inode table for empty slot
-
-    fn find_free_inode(&self) -> Result<usize, BFSErr> {
-        for i in 0..self.superblock.inode_count as usize {
-            let mut header = NodeHeader::default();
-            self.bd.read_node_header(i, &mut header)?;
-            if header.flags == u16::MAX {
-                return Ok(i);
-            }
-        }
-        assert!(false);
-        return Err(BFSErr::NotFound);
-    }
-
-    #[allow(dead_code)]
-    fn count_inodes(&self) -> Result<usize, BFSErr> {
-        let mut count = 0;
-        for i in 0..self.superblock.inode_count as usize {
-            let mut header = NodeHeader::default();
-            self.bd.read_node_header(i, &mut header)?;
-            if header.flags == NodeHeader::FLAG_INODE {
-                count = count + 1;
-            }
-        }
-        return Ok(count);
-    }
-
-    fn find_extent(&self, blob_block_count: u16) -> Result<Extent, BFSErr> {
-        let block_count = self.bd.bd.geom().block_count;
-        let offset = self.blockmap.find_span(0, block_count, blob_block_count as usize)?;
-
-        return Ok(Extent {
-            size: blob_block_count as u16,
-            //offset_hi: (offset >> 32) as u16,
-            offset_hi: 0 as u16,
-            offset_lo: offset as u32,
-        });
-    }
-}
-
-// Unit tests
-
-#[test]
-fn test_basic() {
-    use crate::test_device::*;
-    const BLOCK_SIZE: usize = 8192;
-    const BLOCK_COUNT: usize = 32;
-
-    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 blockmap_bits: [u32; BLOCK_COUNT / 32] = [0xFFFFFFFF; BLOCK_COUNT / 32];
-
-    let mut fs = BlobFS::new(bd, blockmap_bits.as_mut());
-
-    assert_ok!(fs.format());
-    assert_ok!(fs.mount());
-    assert_ok!(fs.sanity_check());
-
-    // Block map should start with 4 reserved blocks for metadata
-    assert_eq!(fs.blockmap.count_range(0, BLOCK_COUNT).unwrap(), BLOCK_COUNT - 4);
-
-    // Store a small blob in the filesystem
-    let blob_hash: u64 = 0xDEADBEEFF00DCAFE;
-    let blob_text = "This is the contents of a blob";
-    let blob_contents = blob_text.as_bytes();
-    assert_ok!(fs.put_blob(blob_hash, blob_contents));
-
-    // Block map should have lost one free block
-    assert_eq!(fs.blockmap.count_range(0, BLOCK_COUNT).unwrap(), BLOCK_COUNT - 5);
-
-    // Storing it a second time should fail.
-    assert_err!(fs.put_blob(blob_hash, blob_contents));
-
-    // Read it back out
-    let blob_len = fs.get_blob_size(blob_hash).unwrap();
-    let mut blob_contents: Vec<u8> = vec![0; blob_len];
-    assert_ok!(fs.get_blob(blob_hash, &mut blob_contents));
-
-    // Contents should match.
-    let new_blob_text = core::str::from_utf8(&blob_contents).unwrap();
-    assert_eq!(blob_text, new_blob_text);
-
-    // Delete it and lookups should fail
-    assert_ok!(fs.delete_blob(blob_hash));
-    assert_err!(fs.get_blob_size(blob_hash));
-
-    // Deleting it a second time should also fail.
-    assert_err!(fs.delete_blob(blob_hash));
-
-    // Block map should have gained one free block
-    assert_eq!(fs.blockmap.count_range(0, BLOCK_COUNT).unwrap(), BLOCK_COUNT - 4);
-
-    // Reading a non-existent blob should cause an error
-    let bad_hash: u64 = 0xAAAAAAAAAAAAAAAA;
-    assert_err!(fs.get_blob_size(bad_hash));
-    let mut blob_contents: [u8; 256] = [0; 256];
-    assert_err!(fs.get_blob(bad_hash, &mut blob_contents));
-}
diff --git a/libraries/blob_fs/src/block_device.rs b/libraries/blob_fs/src/block_device.rs
deleted file mode 100644
index 71c76f2..0000000
--- a/libraries/blob_fs/src/block_device.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::errors::*;
-
-#[derive(Debug, Copy, Clone)]
-pub struct BlockDeviceGeometry {
-    pub block_size: usize,
-    pub block_count: usize,
-}
-
-pub trait BlockDevice {
-    fn geom(&self) -> BlockDeviceGeometry;
-    fn read_block(&self, block: usize, block: &mut [u8]) -> Result<(), BFSErr>;
-    fn write_block(&mut self, block: usize, block: &[u8]) -> Result<(), BFSErr>;
-    fn erase_block(&mut self, block: usize) -> Result<(), BFSErr>;
-
-    fn read_range(&self, addr: usize, data: &mut [u8]) -> Result<(), BFSErr>;
-    fn write_range(&mut self, addr: usize, data: &[u8]) -> Result<(), BFSErr>;
-    fn overwrite_range(&mut self, addr: usize, data: &[u8]) -> Result<(), BFSErr>;
-}
diff --git a/libraries/blob_fs/src/errors.rs b/libraries/blob_fs/src/errors.rs
deleted file mode 100644
index cf92156..0000000
--- a/libraries/blob_fs/src/errors.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-#[derive(Debug, PartialEq)]
-pub enum BFSErr {
-    DeviceErr,    // The underlying block device had an error.
-    NotFound,     // The resource was not found.
-    CleanRead,    // Tried to read a byte that had never been written.
-    DirtyWrite,   // Tried to write a byte that had already been written.
-    OutOfBounds,  // Tried to read off the end of the device.
-    BadOverwrite, // Tried to change a bit from 0->1 without an erase.
-    BadErase,     // Tried to erase a clean block
-    Corrupt,      // Something was corrupt...
-    Full,         // No free blocks left.
-    Duplicate,    // Tried to add a blob that already existed.
-    InvalidArg,   // Bad argument passed to API
-}
diff --git a/libraries/blob_fs/src/lib.rs b/libraries/blob_fs/src/lib.rs
deleted file mode 100644
index ce93b78..0000000
--- a/libraries/blob_fs/src/lib.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-#![cfg_attr(not(test), no_std)]
-#![macro_use]
-
-pub mod bit_vector;
-pub mod blob_device;
-pub mod blob_fs;
-pub mod block_device;
-pub mod errors;
-pub mod memmap_device;
-pub mod structs;
-pub mod utils;
-
-#[cfg(test)]
-pub mod test_device;
diff --git a/libraries/blob_fs/src/memmap_device.rs b/libraries/blob_fs/src/memmap_device.rs
deleted file mode 100644
index 3e570d5..0000000
--- a/libraries/blob_fs/src/memmap_device.rs
+++ /dev/null
@@ -1,276 +0,0 @@
-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));
-    }
-}
diff --git a/libraries/blob_fs/src/structs.rs b/libraries/blob_fs/src/structs.rs
deleted file mode 100644
index 6bcd1f7..0000000
--- a/libraries/blob_fs/src/structs.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-#[repr(C, packed)]
-#[derive(Debug, Copy, Clone, Default)]
-pub struct Superblock {
-    pub magic0: u64,
-    pub magic1: u64,
-    pub version: u32,
-    pub flags: u32,
-    pub block_size: u32,
-    pub unused: u32,
-    pub data_block_count: u64,
-    pub journal_block_count: u64,
-    pub inode_count: u64,
-    pub alloc_block_count: u64,
-    pub alloc_inode_count: u64,
-}
-
-#[repr(C, packed)]
-#[derive(Debug, Copy, Clone, Default)]
-pub struct Extent {
-    pub size: u16,
-    pub offset_hi: u16,
-    pub offset_lo: u32,
-}
-
-impl Extent {
-    pub fn offset(&self) -> usize {
-        //let hi = self.offset_hi as usize;
-        //let lo = self.offset_lo as usize;
-        //return (hi << 32) | lo;
-        return self.offset_lo as usize;
-    }
-}
-
-#[repr(C, packed)]
-#[derive(Debug, Copy, Clone, Default)]
-pub struct NodeHeader {
-    pub flags: u16,
-    pub version: u16,
-    pub next_node: u32,
-}
-
-impl NodeHeader {
-    pub const FLAG_INODE: u16 = 0b0000000000000001;
-    pub const FLAG_EXTENT: u16 = 0b0000000000000010;
-}
-
-#[repr(C, packed)]
-#[derive(Debug, Copy, Clone, Default)]
-pub struct Inode {
-    pub header: NodeHeader,
-    pub hash0: u64,
-    pub hash1: u64,
-    pub hash2: u64,
-    pub hash3: u64,
-    pub blob_size: u64,
-    pub block_count: u32,
-    pub extent_count: u16,
-    pub padding: u16,
-    pub inline_extent: Extent,
-}
-
-#[repr(C, packed)]
-#[derive(Debug, Copy, Clone)]
-pub struct ExtentContainer {
-    pub header: NodeHeader,
-    pub previous_node: u32,
-    pub extent_count: u16,
-    pub reserved: u16,
-    pub extents: [Extent; 6],
-}
-
-#[repr(C, packed)]
-#[derive(Debug, Copy, Clone)]
-pub struct Transaction {
-    pub op: u16,
-    pub size: u16,
-    pub addr: u32,
-}
-
-#[test]
-fn size_check() {
-    use core::mem::*;
-    assert_eq!(size_of::<Extent>(), 8);
-    assert_eq!(size_of::<Inode>(), 64);
-    assert_eq!(size_of::<ExtentContainer>(), 64);
-}
diff --git a/libraries/blob_fs/src/test_device.rs b/libraries/blob_fs/src/test_device.rs
deleted file mode 100644
index 69fbf2e..0000000
--- a/libraries/blob_fs/src/test_device.rs
+++ /dev/null
@@ -1,341 +0,0 @@
-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));
-    }
-}
diff --git a/libraries/blob_fs/src/utils.rs b/libraries/blob_fs/src/utils.rs
deleted file mode 100644
index e0c9814..0000000
--- a/libraries/blob_fs/src/utils.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-use core::mem;
-use core::slice;
-
-/// Trigger an assertion if "cond" fails in debug builds, return an error in
-/// release builds.
-macro_rules! dcheck {
-    ($cond:expr, $err:expr) => {
-        let cond = $cond;
-        debug_assert!(cond);
-        if !(cond) {
-            return Err($err);
-        }
-    };
-}
-pub(crate) use dcheck;
-
-/// Assert that a Result is_ok(). Why is this not in the standard library?
-#[cfg(test)]
-macro_rules! assert_ok {
-    ($cond:expr) => {
-        let result = $cond;
-        assert!(result.is_ok());
-    };
-}
-#[cfg(test)]
-pub(crate) use assert_ok;
-
-/// Assert that a Result is_err(). Why is this not in the standard library?
-#[cfg(test)]
-macro_rules! assert_err {
-    ($cond:expr) => {
-        let result = $cond;
-        assert!(result.is_err());
-    };
-}
-#[cfg(test)]
-pub(crate) use assert_err;
-
-// FIXME these are quick and dirty hacks to make serialization of POD types
-// easier
-
-pub fn as_unsafe_blob<T: Sized>(p: &T) -> &[u8] {
-    unsafe {
-        let tp: *const T = p as *const T;
-        let pp: *const u8 = tp as *const u8;
-        slice::from_raw_parts(pp, mem::size_of::<T>())
-    }
-}
-
-pub fn as_unsafe_blob_mut<T: Sized>(p: &mut T) -> &mut [u8] {
-    unsafe {
-        let tp: *mut T = p as *mut T;
-        let pp: *mut u8 = tp as *mut u8;
-        slice::from_raw_parts_mut(pp, mem::size_of::<T>())
-    }
-}
-
-pub fn from_unsafe_blob<T: Sized>(p: &[u8]) -> &T {
-    unsafe {
-        let pp: *const u8 = p.as_ptr();
-        let tp: *const T = pp as *const T;
-        return &*tp;
-    }
-}
-
-pub fn from_unsafe_blob_mut<T: Sized>(p: &mut [u8]) -> &mut T {
-    unsafe {
-        let pp: *mut u8 = p.as_mut_ptr();
-        let tp: *mut T = pp as *mut T;
-        return &mut *tp;
-    }
-}
-
-pub fn slice_as_unsafe_blob_mut<T: Sized>(p: &mut [T]) -> &mut [u8] {
-    unsafe {
-        let tp: *mut T = p.as_mut_ptr();
-        let pp: *mut u8 = tp as *mut u8;
-        slice::from_raw_parts_mut(pp, mem::size_of::<T>())
-    }
-}
-
-pub fn slice_as_unsafe_blob<T: Sized>(p: &[T]) -> &[u8] {
-    unsafe {
-        let tp: *const T = p.as_ptr();
-        let pp: *const u8 = tp as *const u8;
-        slice::from_raw_parts(pp, mem::size_of::<T>())
-    }
-}