[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/lib/norfs/include/lib/norfs.h b/src/bsp/lk/lib/norfs/include/lib/norfs.h
new file mode 100644
index 0000000..190307d
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/include/lib/norfs.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2013 Heather Lee Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __NORFS_H
+#define __NORFS_H
+
+#include <iovec.h>
+#include <sys/types.h>
+#include <stdint.h>
+
+/*
+ * File system must be mounted prior to usage.
+ */
+status_t norfs_mount_fs(uint32_t offset);
+
+/*
+ * File system can be unmounted.  All data is stored.
+ */
+void norfs_unmount_fs(void);
+
+/*
+ * Remove an object from filesystem.
+ */
+status_t norfs_remove_obj(uint32_t key);
+
+/*
+ * Put an IO vector object in filesystem.  Previous versions of the object will
+ * be overwritten.
+ */
+status_t norfs_put_obj_iovec(uint32_t key, const iovec_t *obj_iov, uint32_t iov_count,
+		uint8_t flags);
+
+/*
+ * Put an object in filesystem.  Previous versions of the object will be
+ * overwritten.
+ */
+status_t norfs_put_obj(uint32_t key, unsigned char *obj, uint16_t obj_len,
+				 uint8_t flags);
+
+
+/*
+ * Read object into an iovec buffer.  Will fail with ERR_TOO_BIG if
+ * iovec does not have enough space to store entire object.  Will
+ * fail with ERR_CRC_FAIL if CRC fails. obj_len field will return
+ * the amount of bytes in the object as per the object header.
+ */
+status_t norfs_read_obj_iovec(uint32_t key, iovec_t *obj_iov, uint32_t iov_count,
+						size_t *bytes_read, uint8_t flags);
+
+/*
+ * Equivalent to read_obj_iovec, but objects read into a single buffer.
+ */
+status_t norfs_read_obj(uint32_t key, unsigned char *buffer, uint16_t buffer_len,
+				  size_t *bytes_read, uint8_t flags);
+
+/*
+ * Wipe NVRAM.  Leaves filesystem unmounted.
+ */
+void norfs_wipe_fs(void);
+
+#endif
diff --git a/src/bsp/lk/lib/norfs/include/lib/norfs_config.h b/src/bsp/lk/lib/norfs/include/lib/norfs_config.h
new file mode 100644
index 0000000..032efa8
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/include/lib/norfs_config.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013 Heather Lee Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __NORFS_CONFIG_H
+#define __NORFS_CONFIG_H
+
+#include <platform/flash_nor_config.h>
+
+#define NORFS_NUM_BLOCKS 8
+#define NORFS_BLOCK_HEADER_SIZE 8
+#define NORFS_NVRAM_SIZE (FLASH_PAGE_SIZE * NORFS_NUM_BLOCKS)
+#define NORFS_MAX_OBJ_LEN (FLASH_PAGE_SIZE/2 - NORFS_OBJ_OFFSET)
+#define NORFS_BANK 0
+
+#define NORFS_FLASH_SIZE(obj_size) (uint16_t)(obj_size + NORFS_OBJ_OFFSET)
+
+#define NORFS_AVAILABLE_SPACE ((NORFS_NVRAM_SIZE - NORFS_NUM_BLOCKS * NORFS_BLOCK_HEADER_SIZE) / 2)
+#define NORFS_MIN_FREE_BLOCKS 1
+
+#define NORFS_KEY_OFFSET 0
+#define NORFS_VERSION_OFFSET 4
+#define NORFS_LENGTH_OFFSET 6
+#define NORFS_FLAGS_OFFSET 8
+#define NORFS_CHECKSUM_OFFSET 10
+#define NORFS_OBJ_OFFSET 12
+
+#define NORFS_DELETED_MASK 1
+
+#endif
diff --git a/src/bsp/lk/lib/norfs/include/lib/norfs_inode.h b/src/bsp/lk/lib/norfs/include/lib/norfs_inode.h
new file mode 100644
index 0000000..08fa312
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/include/lib/norfs_inode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013 Heather Lee Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __NORFS_INODE_H
+#define __NORFS_INODE_H
+
+#include <list.h>
+#include <stdint.h>
+
+struct norfs_inode {
+	struct list_node lnode;
+	uint32_t location;
+	uint32_t reference_count;
+};
+
+#endif
diff --git a/src/bsp/lk/lib/norfs/norfs.c b/src/bsp/lk/lib/norfs/norfs.c
new file mode 100644
index 0000000..6cda8f5
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/norfs.c
@@ -0,0 +1,980 @@
+/*
+ * Copyright (c) 2013 Heather Lee Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <dev/flash_nor.h>
+#include <lib/norfs.h>
+#include <lib/norfs_inode.h>
+#include <lib/norfs_config.h>
+#include <iovec.h>
+#include <stdlib.h>
+#include <err.h>
+#include <string.h>
+#include <platform.h>
+#include <lib/cksum.h>
+#include <platform/flash_nor_config.h>
+#include <list.h>
+#include <debug.h>
+
+/* FRIEND_TEST non-static if unit testing, in order to
+ * allow functions to be exposed by a test header file.
+ */
+#ifdef WITH_LIB_UNITTEST
+#define FRIEND_TEST
+#else
+#define FRIEND_TEST static
+#endif
+
+#define member_size(type, member) sizeof(((type *)0)->member)
+
+
+/* Handle rolling over of version field beyond max(uint16_t) by assuming that
+ * the difference between the smallest and largest versions are no more than
+ * half the range of uint16_t.  By subtracting one unsigned uint16_t from
+ * another, then casting the delta to int16_t, if the delta is greater than
+ * 1/2 * max(uint16_t), then the most significant bit will be a 1, causing the
+ * signed integer to be negative and VERSION_GREATER_THAN to return false.
+ */
+#define VERSION_GREATER_THAN(a, b) ((int16_t)((a) - (b)) > 0)
+
+struct norfs_header {
+	uint32_t key;
+	uint16_t version;
+	uint16_t len;
+	uint8_t flags;
+	uint16_t crc;
+};
+
+/* Block header written after successful erase. */
+FRIEND_TEST const unsigned char NORFS_BLOCK_HEADER[4] = {'T', 'O', 'F', 'U'};
+/* Block header to indicate garbage collection has started. */
+FRIEND_TEST  const unsigned char NORFS_BLOCK_GC_STARTED_HEADER[2] = {'S', 'O'};
+/* Block header to indicate block was not interrupted during garbage
+ * collection.
+ */
+FRIEND_TEST const unsigned char NORFS_BLOCK_GC_FINISHED_HEADER[2] = {'U', 'P'};
+
+FRIEND_TEST uint32_t write_pointer = 0;
+FRIEND_TEST uint32_t total_remaining_space = NORFS_AVAILABLE_SPACE;
+FRIEND_TEST uint8_t num_free_blocks = 0;
+static bool fs_mounted = false;
+FRIEND_TEST uint32_t norfs_nvram_offset;
+static struct list_node inode_list;
+
+static bool block_free[NORFS_NUM_BLOCKS];
+
+static status_t collect_garbage(void);
+static status_t load_and_verify_obj(uint32_t *ptr, struct norfs_header *header);
+
+FRIEND_TEST uint8_t block_num(uint32_t flash_pointer)
+{
+	return flash_pointer/FLASH_PAGE_SIZE;
+}
+
+/* Update pointer to a free block.  If no free blocks, return error. */
+FRIEND_TEST status_t find_free_block(uint32_t *ptr)
+{
+	uint8_t i = block_num(*ptr) + 1;
+	uint8_t imod;
+	for (uint8_t j = 0;  j < NORFS_NUM_BLOCKS; i++, j++) {
+		imod  = i % NORFS_NUM_BLOCKS;
+		/* If is a free block, return. */
+		if (block_free[imod]) {
+			*ptr = imod * FLASH_PAGE_SIZE + sizeof(NORFS_BLOCK_HEADER);
+			return NO_ERROR;
+		}
+	}
+	/* A free block could not be found. */
+	return ERR_NO_MEMORY;
+}
+
+static uint32_t curr_block_free_space(uint32_t pointer)
+{
+	return (block_num(pointer) + 1) * FLASH_PAGE_SIZE - pointer;
+}
+
+static bool block_full(uint8_t block, uint32_t ptr)
+{
+	if (block != block_num(ptr)) {
+		return true;
+	}
+	return curr_block_free_space(ptr) < NORFS_OBJ_OFFSET;
+}
+
+static uint8_t select_garbage_block(uint32_t ptr)
+{
+	return (block_num(ptr) + 1) % 8;
+}
+
+static ssize_t nvram_read(size_t offset, size_t length, void *ptr)
+{
+	return flash_nor_read(NORFS_BANK, offset + norfs_nvram_offset, length, ptr);
+}
+
+static ssize_t nvram_write(size_t offset, size_t length, const void *ptr)
+{
+	return flash_nor_write(NORFS_BANK, offset + norfs_nvram_offset, length,
+	                       ptr);
+}
+
+static ssize_t nvram_erase_pages(size_t offset, size_t length)
+{
+	return flash_nor_erase_pages(NORFS_BANK, offset + norfs_nvram_offset,
+	                             length);
+}
+
+unsigned char* nvram_flash_pointer(uint32_t loc)
+{
+	return FLASH_PTR(flash_nor_get_bank(NORFS_BANK), loc + norfs_nvram_offset);
+}
+
+FRIEND_TEST bool get_inode(uint32_t key, struct norfs_inode **inode)
+{
+	struct list_node *curr_lnode;
+	struct norfs_inode *curr_inode;
+	uint32_t curr_key;
+
+	if (!inode)
+		return false;
+
+	*inode = NULL;
+	list_for_every(&inode_list, curr_lnode) {
+		curr_inode = containerof(curr_lnode, struct norfs_inode, lnode);
+		nvram_read(curr_inode->location + NORFS_KEY_OFFSET,
+		           sizeof(curr_key), &curr_key);
+		if (curr_key == key) {
+			*inode = curr_inode;
+			return true;
+		}
+	}
+	return false;
+}
+
+static uint16_t calculate_header_crc(uint32_t key, uint16_t version,
+                                     uint16_t len, uint8_t flags)
+{
+	uint16_t crc = crc16((unsigned char *) &key, sizeof(key));
+	crc = update_crc16(crc, (unsigned char *) &version, sizeof(version));
+	crc = update_crc16(crc, (unsigned char *) &len, sizeof(len));
+	crc = update_crc16(crc, (unsigned char *) &flags, sizeof(flags));
+	return crc;
+}
+
+/* Read header into parameter buffers. Return bytes written. */
+static ssize_t read_header(uint32_t ptr, struct norfs_header *header)
+{
+	ssize_t total_bytes_read = 0;
+	ssize_t bytes_read = 0;
+	bytes_read = nvram_read(ptr + NORFS_KEY_OFFSET,
+	                        sizeof(header->key), &header->key);
+	if (bytes_read < 0) {
+		return bytes_read;
+	}
+	total_bytes_read += bytes_read;
+	bytes_read = nvram_read(ptr + NORFS_VERSION_OFFSET,
+	                        sizeof(header->version), &header->version);
+	if (bytes_read < 0) {
+		return bytes_read;
+	}
+	total_bytes_read += bytes_read;
+	bytes_read = nvram_read(ptr + NORFS_LENGTH_OFFSET,
+	                        sizeof(header->len), &header->len);
+	if (bytes_read < 0) {
+		return bytes_read;
+	}
+	total_bytes_read += bytes_read;
+	bytes_read = nvram_read(ptr + NORFS_FLAGS_OFFSET,
+	                        sizeof(header->flags), &header->flags);
+	if (bytes_read < 0) {
+		return bytes_read;
+	}
+	total_bytes_read += bytes_read;
+
+	/* Increment pointer by one byte to account for unused padding. */
+	total_bytes_read += 1;
+	bytes_read = nvram_read(ptr + NORFS_CHECKSUM_OFFSET,
+	                        sizeof(header->crc), &header->crc);
+	if (bytes_read < 0) {
+		return bytes_read;
+	}
+	total_bytes_read += bytes_read;
+
+	return total_bytes_read;
+}
+
+status_t norfs_read_obj_iovec(uint32_t key, iovec_t *obj_iov,
+                              uint32_t iov_count, size_t *bytes_read, uint8_t flags)
+{
+	if (!fs_mounted)
+		return ERR_NOT_MOUNTED;
+
+	uint32_t read_ptr;
+	uint16_t to_read, total_to_read;
+	struct norfs_inode *inode;
+	struct norfs_header stored_header;
+	int16_t bytes = 0;
+	uint8_t i = 0;
+	ssize_t iov_size = iovec_size(obj_iov, iov_count);
+
+	if (bytes_read) {
+		*bytes_read = 0;
+	}
+
+	if (!get_inode(key, &inode)) {
+		return ERR_NOT_FOUND;
+	}
+	read_ptr = inode->location;
+	bytes = read_header(read_ptr, &stored_header);
+
+	total_to_read = MIN(stored_header.len, iov_size);
+	if (bytes < 0) {
+		TRACEF("Error reading header. Status: %d\n", bytes);
+		return bytes;
+	}
+	read_ptr += bytes;
+
+	if (stored_header.flags & NORFS_DELETED_MASK) {
+		TRACEF("Object is already deleted.\n");
+		return ERR_NOT_FOUND;
+	}
+
+	while (*bytes_read < total_to_read) {
+		to_read = MIN(total_to_read - *bytes_read, obj_iov[i].iov_len);
+		bytes = nvram_read(read_ptr + *bytes_read,
+		                   to_read, obj_iov[i].iov_base);
+		if (bytes < 0) {
+			TRACEF("Read failed with error: %d\n", bytes);
+			return bytes;
+		}
+		if (bytes_read) {
+			*bytes_read += bytes;
+		}
+		i++;
+	}
+
+	return NO_ERROR;
+}
+
+static status_t write_obj_header(uint32_t *ptr, uint32_t key, uint16_t version,
+                                 uint16_t len, uint8_t flags, uint16_t crc)
+{
+	unsigned char buff[WORD_SIZE];
+	int bytes_written;
+
+	bytes_written = nvram_write(*ptr, sizeof(key), &key);
+	if (bytes_written < 0) {
+		return bytes_written;
+	}
+	*ptr += bytes_written;
+
+	memcpy(buff, &version, sizeof(version));
+	memcpy(buff + sizeof(version), &len, sizeof(len));
+	bytes_written = nvram_write(*ptr, sizeof(buff), &buff);
+	if (bytes_written < 0) {
+		return bytes_written;
+	}
+	*ptr += bytes_written;
+
+	memcpy(buff, &flags, sizeof(flags));
+	memset(buff + 1, 1, sizeof(flags));
+	memcpy(buff + 2, &crc, sizeof(crc));
+	bytes_written = nvram_write(*ptr, sizeof(buff), &buff);
+	if (bytes_written < 0) {
+		return bytes_written;
+	}
+	*ptr += bytes_written;
+	return NO_ERROR;
+}
+
+static status_t copy_iovec_to_disk(const struct iovec *iov, uint16_t iov_count,
+                                   uint32_t *location, uint16_t *crc)
+{
+	unsigned char word[4] = {0};
+	uint16_t iov_ptr = 0;
+	uint16_t word_ptr = 0;
+	uint8_t word_size = sizeof(word);
+	int bytes_written;
+	for (uint16_t i = 0; i < iov_count; i++) {
+		while ((uint16_t)iov[i].iov_len - iov_ptr > word_size - word_ptr) {
+			memcpy(word + word_ptr,
+			       (unsigned char *)iov[i].iov_base + iov_ptr, word_size -
+			       word_ptr);
+			iov_ptr += word_size - word_ptr;
+
+			bytes_written = nvram_write(*location, sizeof(word),
+			                            &word);
+			if (bytes_written < 0) {
+				TRACEF("Error while writing: %d\n", bytes_written);
+				return bytes_written;
+			}
+
+			*location += bytes_written;
+			*crc = update_crc16(*crc, word, sizeof(word));
+			memset(word, 0, sizeof(word));
+			word_ptr = 0;
+		}
+
+		memcpy((unsigned char *)word + word_ptr,
+		       (unsigned char *)iov[i].iov_base + iov_ptr,
+		       iov[i].iov_len - iov_ptr);
+		word_ptr += iov[i].iov_len - iov_ptr;
+
+		iov_ptr = 0;
+	}
+
+	if (word_ptr > 0) {
+		bytes_written = nvram_write(*location, sizeof(word),
+		                            &word);
+
+		if (bytes_written < 0) {
+			TRACEF("Error writing: %d\n", bytes_written);
+			return bytes_written;
+		}
+
+		*location += bytes_written;
+		*crc = update_crc16(*crc, word, word_ptr);
+	}
+	return NO_ERROR;
+}
+
+static status_t initialize_next_block(uint32_t *ptr)
+{
+	uint32_t header_pointer;
+	ssize_t bytes_written;
+	status_t status;
+
+	/* Update write pointer. */
+	status = find_free_block(ptr);
+	if (status) {
+		TRACEF("Error finding free block.  Status: %d\n", status);
+		return status;
+	}
+
+	num_free_blocks--;
+	block_free[block_num(*ptr)] = false;
+	bytes_written = nvram_write(*ptr,
+	                            sizeof(NORFS_BLOCK_GC_STARTED_HEADER), &NORFS_BLOCK_GC_STARTED_HEADER);
+
+	if (bytes_written < 0) {
+		TRACEF("Error writing header.\n");
+		return bytes_written;
+	}
+
+	header_pointer = *ptr + bytes_written;
+	*ptr += sizeof(NORFS_BLOCK_GC_STARTED_HEADER) +
+	        sizeof(NORFS_BLOCK_GC_FINISHED_HEADER);
+
+	if (num_free_blocks < NORFS_MIN_FREE_BLOCKS) {
+		status = collect_garbage();
+		if (status) {
+			TRACEF("Failed to collection garbage.  Error: %d\n.",
+			       status);
+			return status;
+		}
+	}
+
+	status = nvram_write(header_pointer,
+	                     sizeof(NORFS_BLOCK_GC_FINISHED_HEADER),
+	                     &NORFS_BLOCK_GC_FINISHED_HEADER);
+	if (status < 0) {
+		TRACEF("Failed to write header.\n");
+		return status;
+	}
+
+	return NO_ERROR;
+}
+
+status_t write_obj_iovec(const iovec_t *iov, uint iov_count, uint *location,
+                         uint32_t key, uint16_t version, uint8_t flags)
+{
+	uint16_t crc = 0;
+	uint16_t len = iovec_size(iov, iov_count);
+	status_t status;
+
+	uint32_t header_loc = *location;
+	*location += NORFS_OBJ_OFFSET;
+	crc = calculate_header_crc(key, version, len, flags);
+
+	if (!(flags & NORFS_DELETED_MASK)) {
+		/* If object is not a deletion header, write object bytes to disk. */
+		status = copy_iovec_to_disk(iov, iov_count, location, &crc);
+		if (status) {
+			TRACEF("Error writing.  Status: %d\n", status);
+			return status;
+		}
+	}
+
+	status = write_obj_header(&header_loc, key, version, len, flags, crc);
+	if (status) {
+		TRACEF("Error writing.  Status: %d\n", status);
+		return status;
+	}
+
+	return NO_ERROR;
+}
+
+status_t norfs_read_obj(uint32_t key, unsigned char *buffer,
+                        uint16_t buffer_len, size_t *bytes_read,
+                        uint8_t flags)
+{
+	if (!fs_mounted)
+		return ERR_NOT_MOUNTED;
+
+	struct iovec vec[1];
+	vec->iov_base = buffer;
+	vec->iov_len = buffer_len;
+	status_t status = norfs_read_obj_iovec(key, vec, 1, bytes_read, flags);
+
+	return status;
+}
+
+status_t norfs_put_obj(uint32_t key, unsigned char *obj, uint16_t len,
+                       uint8_t flags)
+{
+	if (!fs_mounted)
+		return ERR_NOT_MOUNTED;
+
+	if (key == 0xFFFF) {
+		return ERR_INVALID_ARGS;
+	}
+	status_t status;
+	struct iovec const vec[1] = {{obj, len}};
+	status = norfs_put_obj_iovec(key, vec, 1, flags);
+	return status;
+}
+
+bool is_deleted(uint32_t loc)
+{
+	uint8_t flags;
+	nvram_read(loc + NORFS_FLAGS_OFFSET, sizeof(flags), &flags);
+	return NORFS_DELETED_MASK & flags;
+}
+
+status_t norfs_remove_obj(uint32_t key)
+{
+	if (!fs_mounted)
+		return ERR_NOT_MOUNTED;
+
+	struct norfs_inode *inode;
+	uint16_t prior_len;
+	struct iovec iov[1];
+	status_t status;
+	bool success = get_inode(key, &inode);
+	if (!success || is_deleted(inode->location))
+		return ERR_NOT_FOUND;
+
+	status = nvram_read(inode->location + NORFS_LENGTH_OFFSET,
+	                    sizeof(uint16_t), &prior_len);
+	if (status < 0) {
+		TRACEF("Failed to read during norfs_remove_obj.  Status: %d\n", status);
+		return status;
+	}
+
+	iov->iov_base = NULL;
+	iov->iov_len = 0;
+
+	/*
+	 * Write a deleted object by passing a null iovec pointer.  Only header
+	 * will be written.
+	 */
+	status = norfs_put_obj_iovec(key, iov, 0, NORFS_DELETED_MASK);
+	if (status)
+		TRACEF("Error putting object. %d\n", status);
+
+	return status;
+}
+
+static status_t find_space_for_object(uint16_t obj_len, uint32_t *ptr)
+{
+	status_t status;
+	uint8_t initial_block_num = block_num(*ptr);
+	while (curr_block_free_space(*ptr) < (uint16_t) NORFS_FLASH_SIZE(obj_len)) {
+		status = initialize_next_block(ptr);
+		if (status)
+			return status;
+		/* If we have already tried and failed to write to this block, exit. */
+		if (block_num(*ptr) == initial_block_num)
+			return ERR_NO_MEMORY;
+	}
+	return NO_ERROR;
+}
+
+/*
+ * Store an object in flash.  Moves write_pointer to new block and garbage
+ * collects if needed.  If write fails, will reattempt.
+ * How to handle write failures is not fully defined - at the moment I stop once
+ * find_free_block is attempting to rewrite to a block it has already failed to
+ * write to - after a full loop in a round-robin style garbage selection of
+ * blocks.  Which is a lot of write attempts.
+ */
+status_t norfs_put_obj_iovec(uint32_t key, const iovec_t *iov,
+                             uint32_t iov_count, uint8_t flags)
+{
+	if (!fs_mounted)
+		return ERR_NOT_MOUNTED;
+
+	if (key == 0xFFFF) {
+		return ERR_INVALID_ARGS;
+	}
+
+	uint8_t block_num_to_write;
+	struct norfs_inode *inode;
+	uint16_t len = iovec_size(iov, iov_count);
+	status_t status;
+	uint8_t stored_flags;
+	uint16_t version = 0;
+	uint32_t header_loc;
+	bool deletion = flags & NORFS_DELETED_MASK;
+	bool obj_preexists = get_inode(key, &inode);
+	if (obj_preexists) {
+		nvram_read(inode->location + NORFS_FLAGS_OFFSET,
+		           sizeof(stored_flags), &stored_flags);
+		if (stored_flags & flags & NORFS_DELETED_MASK) {
+			/* Attempting to delete an object no longer in filesystem. */
+			TRACEF("The object attempting to be removed has already been \
+			deleted. stored_flags: 0x%x\n", stored_flags);
+			return ERR_NOT_FOUND;
+		}
+
+		nvram_read(inode->location + NORFS_VERSION_OFFSET,
+		           sizeof(version), &version);
+		version++;
+	} else if (deletion) {
+		/* Attempting to delete a non-existent object. */
+		TRACEF("Attempting to remove an object not in filesystem.\n");
+		return ERR_NOT_FOUND;
+	} else {
+		inode = malloc(sizeof(struct norfs_inode));
+		inode->reference_count = 1;
+	}
+
+	flash_nor_begin(NORFS_BANK);
+
+	if (NORFS_FLASH_SIZE(len) > NORFS_MAX_OBJ_LEN) {
+		TRACEF("Object too big.  Not adding.\n");
+		flash_nor_end(NORFS_BANK);
+		return ERR_TOO_BIG;
+	}
+
+	if (!deletion && (NORFS_FLASH_SIZE(len) > total_remaining_space)) {
+		TRACEF("Not enough remaining space.  Remaining space:  %d\tObject len: \
+			    %d\n", total_remaining_space, len);
+		flash_nor_end(NORFS_BANK);
+		return ERR_NO_MEMORY;
+	}
+
+	status = find_space_for_object(len, &write_pointer);
+	if (status) {
+		TRACEF("Error finding space for object.  Status: %d\n", status);
+		flash_nor_end(NORFS_BANK);
+		return ERR_IO;
+	}
+
+	block_num_to_write = block_num(write_pointer);
+	header_loc = write_pointer;
+	status = write_obj_iovec(iov, iov_count, &write_pointer, key,
+	                         version, flags);
+	if (!status) {
+		if (!obj_preexists) {
+			list_add_tail(&inode_list, &inode->lnode);
+		} else {
+			/* If object preexists, remove outdated version from remaining space. */
+			uint16_t prior_len;
+			nvram_read(inode->location + NORFS_LENGTH_OFFSET,
+			           sizeof(uint16_t), &prior_len);
+			total_remaining_space += NORFS_FLASH_SIZE(prior_len);
+			inode->reference_count++;
+		}
+		inode->location = header_loc;
+		total_remaining_space -= NORFS_FLASH_SIZE(len);
+	} else {
+		TRACEF("Error writing object. Status: %d\n", status);
+	}
+
+	/* If write error, or if fell off block, find new block. */
+	if (status < 0 || block_num(write_pointer) != block_num_to_write) {
+		initialize_next_block(&write_pointer);
+	}
+
+	flash_nor_end(NORFS_BANK);
+	return status;
+}
+
+static void remove_inode(struct norfs_inode *inode)
+{
+	if (!inode)
+		return;
+	list_delete(&inode->lnode);
+	free(inode);
+	inode = NULL;
+}
+
+/*  Verifies objects, and copies to new block if it is the latest version. */
+static status_t collect_garbage_object(uint32_t *garbage_read_pointer,
+                                       uint32_t *garbage_write_pointer)
+{
+	struct norfs_inode *inode;
+	struct norfs_header header;
+	bool inode_found;
+	status_t status;
+	struct iovec iov[1];
+	uint32_t new_obj_loc;
+	uint32_t garb_obj_loc = *garbage_read_pointer;
+	status = load_and_verify_obj(garbage_read_pointer, &header);
+	if (status) {
+		TRACEF("Failed to load garbage_obj at %d\n", *garbage_read_pointer);
+		return status;
+	}
+	inode_found = get_inode(header.key, &inode);
+	if (inode_found) {
+		if (garb_obj_loc == inode->location) {
+			/* Object in garbage block is latest version. */
+			if (header.flags & NORFS_DELETED_MASK && (inode->reference_count == 1)) {
+				/* If last version of object, remove. */
+				remove_inode(inode);
+				total_remaining_space += NORFS_OBJ_OFFSET;
+				return NO_ERROR;
+			}
+			iov->iov_base = nvram_flash_pointer(garb_obj_loc + NORFS_OBJ_OFFSET);
+			iov->iov_len = header.len;
+			new_obj_loc = *garbage_write_pointer;
+			status = write_obj_iovec(iov, 1, garbage_write_pointer, header.key,
+			                         header.version + 1, header.flags);
+			if (status) {
+				TRACEF("Failed to copy garbage object.  Status: %d\n", status);
+				return status;
+			}
+			inode->location = new_obj_loc;
+			return NO_ERROR;
+		} else {
+			inode->reference_count--;
+			return NO_ERROR;
+		}
+	}
+
+	if (is_deleted(*garbage_read_pointer)) {
+		return NO_ERROR;
+	}
+	return ERR_NOT_FOUND;
+}
+
+static status_t erase_block(uint8_t block)
+{
+	ssize_t bytes_erased;
+	ssize_t bytes_written;
+	status_t status;
+	uint32_t loc = block * FLASH_PAGE_SIZE;
+
+	/* Block must fall within range of actual number of NVRAM blocks. */
+	if (block > NORFS_NUM_BLOCKS) {
+		TRACEF("Invalid block number: %d.\n", block);
+		return ERR_INVALID_ARGS;
+	}
+	status = flash_nor_begin(NORFS_BANK);
+	if (status) {
+		flash_nor_end(NORFS_BANK);
+		return status;
+	}
+
+	bytes_erased = nvram_erase_pages(loc, FLASH_PAGE_SIZE);
+	if (bytes_erased != FLASH_PAGE_SIZE) {
+		flash_nor_end(NORFS_BANK);
+		TRACEF("Did not erase exactly one flash page.  Something went \
+				wrong.\n");
+		return ERR_IO;
+	}
+
+	bytes_written = nvram_write(loc, sizeof(NORFS_BLOCK_HEADER),
+	                            &NORFS_BLOCK_HEADER);
+
+	flash_nor_end(NORFS_BANK);
+	if (bytes_written < 0) {
+		TRACEF("Error during nvram_write.  Status: %d\n", bytes_written);
+		return bytes_written;
+	}
+	block_free[block] = true;
+	num_free_blocks++;
+
+	return NO_ERROR;
+}
+
+FRIEND_TEST status_t collect_block(uint32_t garbage_block,
+                                   uint32_t *garbage_write_ptr)
+{
+	status_t status;
+	uint32_t garbage_read_ptr = garbage_block * FLASH_PAGE_SIZE +
+	                            NORFS_BLOCK_HEADER_SIZE;
+
+	while (!(block_full(garbage_block, garbage_read_ptr))) {
+		status = collect_garbage_object(&garbage_read_ptr, garbage_write_ptr);
+		if (status) {
+			break;
+		}
+	}
+	return erase_block(garbage_block);
+}
+
+static status_t collect_garbage(void)
+{
+	status_t status;
+	uint8_t garbage_read_block = select_garbage_block(write_pointer);
+	status = collect_block(garbage_read_block, &write_pointer);
+
+	return status;
+}
+
+/*
+ * Load object into buffer and verify object's integrity via crc.  ptr parameter
+ * is updated upon successful verification.
+ */
+static status_t load_and_verify_obj(uint32_t *ptr, struct norfs_header *header)
+{
+	uint16_t calculated_crc;
+	ssize_t bytes;
+	ssize_t total_bytes_read = 0;
+	bytes = read_header(*ptr, header);
+	unsigned char *obj_ptr;
+
+	if (bytes < 0) {
+		TRACEF("Reading header failed at location: %d. Err: %d.\n", *ptr, bytes);
+		return bytes;
+	}
+
+	total_bytes_read += bytes;
+
+	/* If object is longer than the remaining space in current block, fail.
+	 * When attempting to verify unwritten file space, will fail here. */
+	if (block_num(*ptr + header->len - 1) != block_num(*ptr)) {
+		return ERR_IO;
+	}
+
+	calculated_crc = calculate_header_crc(header->key, header->version,
+	                                      header->len, header->flags);
+
+
+	obj_ptr = nvram_flash_pointer(*ptr + total_bytes_read);
+	calculated_crc = update_crc16(calculated_crc, obj_ptr, header->len);
+
+	total_bytes_read += header->len;
+	if (calculated_crc != header->crc) {
+		TRACEF("CRC check failed.  Calculated: 0x%x\tActual: 0x%x\n",
+		       calculated_crc, header->crc);
+		return ERR_CRC_FAIL;
+	}
+	*ptr += total_bytes_read;
+	*ptr = ROUNDUP(*ptr, WORD_SIZE);
+	return NO_ERROR;
+}
+
+status_t read_block_verification(uint32_t *ptr)
+{
+	unsigned char block_header[sizeof(NORFS_BLOCK_HEADER) +
+	                           sizeof(NORFS_BLOCK_GC_STARTED_HEADER) +
+	                           sizeof(NORFS_BLOCK_GC_FINISHED_HEADER)];
+	int bytes_read = nvram_read(*ptr, sizeof(block_header),
+	                            block_header);
+
+	if (bytes_read < 0) {
+		TRACEF("Error reading while verifying block.  Location: %d\n", *ptr);
+		return ERR_BAD_STATE;
+	}
+
+	for (uint8_t i = 0; i < sizeof(NORFS_BLOCK_HEADER); i++) {
+		if (block_header[i] != *(NORFS_BLOCK_HEADER + i)) {
+			return ERR_BAD_STATE;
+		}
+	}
+
+	bool valid_free_block = true;
+	for (uint8_t i = sizeof(NORFS_BLOCK_HEADER);
+	        i < sizeof(NORFS_BLOCK_HEADER) +
+	        sizeof(NORFS_BLOCK_GC_STARTED_HEADER) +
+	        sizeof(NORFS_BLOCK_GC_FINISHED_HEADER);
+	        i++) {
+		valid_free_block = valid_free_block && (block_header[i] == 0xFF);
+	}
+
+	/* This block has been successfully erased but has not been written to. */
+	if (valid_free_block) {
+		return ERR_NOT_CONFIGURED;
+	}
+
+	if (block_header[4] != NORFS_BLOCK_GC_STARTED_HEADER[0] ||
+	        block_header[5] != NORFS_BLOCK_GC_STARTED_HEADER[1] ||
+	        block_header[6] != NORFS_BLOCK_GC_FINISHED_HEADER[0] ||
+	        block_header[7] != NORFS_BLOCK_GC_FINISHED_HEADER[1]) {
+		TRACEF("Garbage collection header invalid.\n");
+		return ERR_BAD_STATE;
+	}
+
+	*ptr += bytes_read;
+	return NO_ERROR;
+}
+
+static status_t mount_next_obj(void)
+{
+	uint16_t curr_obj_loc;
+	uint16_t inode_version, inode_len;
+	curr_obj_loc = write_pointer;
+	struct norfs_inode *inode;
+	struct norfs_header header;
+	status_t status;
+
+	status = load_and_verify_obj(&write_pointer, &header);
+	if (status) {
+		return status;
+	}
+	if (get_inode(header.key, &inode)) {
+		nvram_read(inode->location + NORFS_VERSION_OFFSET,
+		           sizeof(inode_version), &inode_version);
+		if (VERSION_GREATER_THAN(header.version, inode_version)) {
+			/* This is a newer version of object than the version
+			   currently being linked to in the inode. */
+			nvram_read(inode->location + NORFS_LENGTH_OFFSET,
+			           sizeof(inode_len), &inode_len);
+			total_remaining_space += inode_len;
+			total_remaining_space -= header.len;
+			inode->location = curr_obj_loc;
+		}
+		inode->reference_count += 1;
+	} else {
+		/* Object not yet held in memory.  Create new inode. */
+		inode = malloc(sizeof(struct norfs_inode));
+		inode->location = curr_obj_loc;
+
+		inode->reference_count = 1;
+
+		list_add_tail(&inode_list, &inode->lnode);
+		total_remaining_space -= NORFS_FLASH_SIZE(header.len);
+	}
+
+	return NO_ERROR;
+}
+
+/*
+ * Inodes for deleted objects need to be maintained during mounting, in case
+ * references show up in later blocks.  However, these references need to be
+ * pruned prior to usage.
+ */
+static void purge_unreferenced_inodes(void)
+{
+	struct list_node *curr_lnode, *temp_node;
+	struct norfs_inode *curr_inode;
+	list_for_every_safe(&inode_list, curr_lnode, temp_node) {
+		curr_inode = containerof(curr_lnode, struct norfs_inode, lnode);
+		if (curr_inode->reference_count == 0) {
+			remove_inode(curr_inode);
+		}
+	}
+}
+
+status_t norfs_mount_fs(uint32_t offset)
+{
+	if (fs_mounted) {
+		TRACEF("Filesystem already mounted.\n");
+		return ERR_ALREADY_MOUNTED;
+	}
+	status_t status = 0;
+	norfs_nvram_offset = offset;
+
+	list_initialize(&inode_list);
+	flash_nor_begin(NORFS_BANK);
+	srand(current_time());
+
+	total_remaining_space = NORFS_AVAILABLE_SPACE;
+	num_free_blocks = 0;
+	TRACEF("Mounting NOR file system.\n");
+	for (uint8_t i = 0; i < NORFS_NUM_BLOCKS; i++) {
+		write_pointer = i * FLASH_PAGE_SIZE;
+		status = read_block_verification(&write_pointer);
+		if (status == ERR_BAD_STATE) {
+			erase_block(i);
+			continue;
+		} else if (status == ERR_NOT_CONFIGURED) {
+			/* Valid empty block. */
+			block_free[i] = true;
+			num_free_blocks++;
+			continue;
+		} else if (status != NO_ERROR) {
+			TRACEF("Unexpected status: %d.  Exiting.\n", status);
+			flash_nor_end(NORFS_BANK);
+			return status;
+		}
+		block_free[i] = false;
+		while (!block_full(i, write_pointer)) {
+			status = mount_next_obj();
+			if (status)
+				break;
+		}
+	}
+
+	purge_unreferenced_inodes();
+
+	write_pointer = rand() % NORFS_NVRAM_SIZE;
+	status = initialize_next_block(&write_pointer);
+	if (status) {
+		TRACEF("Failed to find free block after mount.\n");
+		flash_nor_end(NORFS_BANK);
+		return status;
+	}
+
+	TRACEF("NOR filesystem successfully mounted.\n");
+	flash_nor_end(NORFS_BANK);
+	fs_mounted = true;
+	return NO_ERROR;
+}
+
+void norfs_unmount_fs(void)
+{
+	TRACEF("Unmounting NOR file system\n");
+	struct list_node *curr_lnode;
+	struct norfs_inode *curr_inode;
+	struct list_node *temp_node;
+
+	if (!fs_mounted) {
+		TRACEF("Filesystem not mounted.\n");
+		return;
+	}
+	if (!list_is_empty(&inode_list)) {
+		list_for_every_safe(&inode_list, curr_lnode, temp_node) {
+			if (curr_lnode) {
+				curr_inode = containerof(curr_lnode, struct norfs_inode, lnode);
+				remove_inode(curr_inode);
+			}
+		}
+	}
+	write_pointer = rand() % NORFS_NVRAM_SIZE;
+	total_remaining_space = NORFS_AVAILABLE_SPACE;
+	num_free_blocks = 0;
+	for (uint8_t i = 0; i < NORFS_NUM_BLOCKS; i++) {
+		block_free[i] = false;
+	}
+	fs_mounted = false;
+}
+
+void norfs_wipe_fs(void)
+{
+	norfs_unmount_fs();
+	flash_nor_begin(0);
+	nvram_erase_pages(0, 8 * FLASH_PAGE_SIZE);
+	flash_nor_end(0);
+	norfs_mount_fs(norfs_nvram_offset);
+}
diff --git a/src/bsp/lk/lib/norfs/rules.mk b/src/bsp/lk/lib/norfs/rules.mk
new file mode 100644
index 0000000..b37b880
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/rules.mk
@@ -0,0 +1,12 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_DEPS := \
+	lib/iovec \
+	lib/cksum
+
+MODULE_SRCS := \
+	$(LOCAL_DIR)/norfs.c
+
+include make/module.mk
diff --git a/src/bsp/lk/lib/norfs/test/include/norfs_test_helper.h b/src/bsp/lk/lib/norfs/test/include/norfs_test_helper.h
new file mode 100644
index 0000000..e6973b1
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/test/include/norfs_test_helper.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 Heather Lee Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __NORFS_TEST_HELPER_H
+#define __NORFS_TEST_HELPER_H
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <lib/norfs_inode.h>
+
+/* Tools to help test. */
+
+uint32_t write_pointer;
+uint32_t norfs_nvram_offset;
+
+status_t find_free_block(uint32_t *ptr);
+uint8_t block_num(uint32_t flash_pointer);
+void dump_bank(void);
+void wipe_fs(void);
+status_t collect_block(uint32_t garbage_block, uint32_t *garbage_write_ptr);
+bool get_inode(uint32_t key, struct norfs_inode **inode);
+
+#endif
diff --git a/src/bsp/lk/lib/norfs/test/norfs_test.c b/src/bsp/lk/lib/norfs/test/norfs_test.c
new file mode 100644
index 0000000..56c18f4
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/test/norfs_test.c
@@ -0,0 +1,718 @@
+/*
+ * Copyright (c) 2013 Heather Lee Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <unittest.h>
+#include <lib/norfs.h>
+#include <lib/norfs_inode.h>
+#include <lib/norfs_config.h>
+#include <norfs_test_helper.h>
+#include <dev/flash_nor.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <platform/flash_nor_config.h>
+#include <platform.h>
+#include <rand.h>
+
+extern uint32_t total_remaining_space;
+extern uint8_t num_free_blocks;
+
+static uint8_t *norfs_test_bank;
+static uint8_t norfs_test_bank_len;
+extern uint32_t norfs_nvram_offset = 0;
+
+static const unsigned char erase_header[4] = {'T', 'O', 'F', 'U'};
+static const unsigned char gc_header[4] = {'S', 'O', 'U', 'P'};
+/* Object with a good checksum. key = 2; version = 0; obj = {0, 1, 2, 3} */
+static const unsigned char good_object[16] = {2, 0, 0, 0, 0, 0, 4, 0, 0, 0xFF,
+											  0x3d, 0x02, 0, 1, 2, 3};
+/* Object with a good checksum. key = 2; version = 1; obj = {5, 4, 3, 2} */
+static const unsigned char good_object_v1[16] = {2, 0, 0, 0, 1, 0, 4, 0, 0, 0xFF,
+												0xbb, 0x9d, 5, 4, 3, 2};
+/* A much later version of good_object to test wrapping of versions. */
+static const unsigned char good_object_v127[16] = {2, 0, 0, 0, 0xFF, 0x7F, 4,
+													0, 0, 0xFF, 0x76, 0x3c, 6,
+													7, 8, 9};
+/* A much later version of good_object to test wrapping of versions. */
+static const unsigned char good_object_v128[16] = {2, 0, 0, 0, 0x00, 0x80, 4,
+													 0, 0, 0xFF, 0x04, 0x24, 6,
+													 7, 8, 9};
+/* Deleted object. key = 2; version = 2 */
+static const unsigned char good_object_deleted_v2[12] = {2, 0, 0, 0, 2, 0, 0, 0,
+														 1, 0xFF, 0xb7, 0x8a};
+/* Deleted object. key = 2; version = 3 */
+static const unsigned char good_object_deleted_v3[12] = {2, 0, 0, 0, 3, 0, 0, 0,
+														 1, 0xFF, 0xe6, 0x20};
+/* Object with a bad checksum. key = 4; version = 2; obj = {3, 2, 1, 0} */
+static const unsigned char bad_object[16] = {4, 0, 0, 0, 2, 0, 4, 0, 0, 0xFF, 4,
+											 7, 3, 2, 1, 0};
+/* Object with a good checksum. key = 0; version = 0; obj = {9, 8, 7, 6} */
+static const unsigned char another_good_object[16] = {0, 0, 0, 0, 0, 0, 4, 0, 0,
+													  0xFF, 0x4d, 0x4a, 9, 8, 7,
+													  6};
+
+const unsigned char NORFS_BLOCK_HEADER[4] = {'T', 'O', 'F', 'U'};
+const unsigned char NORFS_BLOCK_GC_STARTED_HEADER[2] = {'S', 'O'};
+const unsigned char NORFS_BLOCK_GC_FINISHED_HEADER[2] = {'U', 'P'};
+const int NORFS_OBJ_HEADER_SIZE = 12;
+
+/* Stores the current bank for future retrieval. */
+static void save_bank(void)
+{
+	free(norfs_test_bank);
+	norfs_test_bank = malloc(8 * FLASH_PAGE_SIZE);
+	const struct flash_nor_bank *bank;
+	bank = flash_nor_get_bank(0);
+	memcpy(norfs_test_bank, (void *)bank->base, 8 * FLASH_PAGE_SIZE);
+	norfs_test_bank_len = bank->len;
+}
+
+static void write_block_header(uint *ptr)
+{
+	*ptr += flash_nor_write(0, *ptr, sizeof(NORFS_BLOCK_HEADER),
+							NORFS_BLOCK_HEADER);
+	*ptr += flash_nor_write(0, *ptr, sizeof(NORFS_BLOCK_GC_STARTED_HEADER),
+							NORFS_BLOCK_GC_STARTED_HEADER);
+	*ptr += flash_nor_write(0, *ptr, sizeof(NORFS_BLOCK_GC_FINISHED_HEADER),
+							NORFS_BLOCK_GC_FINISHED_HEADER);
+}
+
+static bool test_basic_read_write(void)
+{
+	BEGIN_TEST;
+	unsigned char char_array[5] = {4, 6, 7, 2, 1};
+	uint32_t char_key = 47;
+
+	int big_int = 2000000000;
+	uint32_t int_key = 21;
+	size_t char_array_bytes_read;
+	int big_int_buffer;
+	size_t big_int_bytes_read;
+	unsigned char *char_array_buffer = malloc(sizeof(unsigned char)*6);
+	status_t status;
+	unsigned char alpha[2] = {'a', 'b'};
+	unsigned char beta[3] = {'c', 'd', 'e'};
+	size_t obj_len;
+
+	int iov_in_count = 2;
+	iovec_t *iov_in = malloc(iov_in_count*sizeof(iovec_t));
+	iov_in[0].iov_len = 2;
+	iov_in[0].iov_base = alpha;
+	iov_in[1].iov_len = 3;
+	iov_in[1].iov_base = beta;
+
+	int iov_out_count = 3;
+	iovec_t *iov_out = malloc(iov_out_count*sizeof(iovec_t));
+	iov_out[0].iov_len = 1;
+	iov_out[0].iov_base = malloc(sizeof(unsigned char));
+	iov_out[1].iov_len = 3;
+	iov_out[1].iov_base = malloc(iov_out_count*sizeof(unsigned char));
+	iov_out[2].iov_len = 1;
+	iov_out[2].iov_base = malloc(sizeof(unsigned char));
+
+	wipe_fs();
+
+	EXPECT_EQ(ERR_NOT_MOUNTED, norfs_put_obj(char_key, char_array, sizeof(char_array),
+			  0), "Calls to file system should fail when not mounted");
+	EXPECT_EQ(ERR_NOT_MOUNTED, norfs_read_obj(char_key, char_array_buffer,
+			  sizeof(unsigned char)*6, &char_array_bytes_read, 0),
+			  "Calls to file system should fail when not mounted");
+	EXPECT_EQ(ERR_NOT_MOUNTED, norfs_put_obj_iovec(17, iov_in, iov_in_count, 0),
+			  "Calls to file system should fail when not mounted");
+	EXPECT_EQ(ERR_NOT_MOUNTED, norfs_read_obj_iovec(17, iov_out, iov_out_count,
+			  &obj_len, 0),
+			  "Calls to file system should fail when not mounted");
+	EXPECT_EQ(ERR_NOT_MOUNTED, norfs_remove_obj(17),
+			  "Calls to file system should fail when not mounted");
+
+	EXPECT_EQ(NO_ERROR, norfs_mount_fs(norfs_nvram_offset), "Error during mount");
+
+	status = norfs_put_obj(char_key, char_array, sizeof(char_array), 0);
+	EXPECT_EQ(NO_ERROR, status, "norfs_put_obj returned error");
+	status = norfs_put_obj(int_key, (unsigned char *)&big_int, sizeof(big_int), 0);
+	EXPECT_EQ(NO_ERROR, status, "norfs_put_obj returned error");
+
+	status = norfs_read_obj(char_key, char_array_buffer, sizeof(unsigned char)*6,
+					  &char_array_bytes_read, 0);
+	EXPECT_EQ(NO_ERROR, status, "norfs_read_obj returned error");
+	if (status)
+		return false;
+
+	status = norfs_read_obj(int_key, (unsigned char *)&big_int_buffer,
+					  sizeof(big_int_buffer), &big_int_bytes_read, 0);
+	EXPECT_EQ(NO_ERROR, status, "norfs_read_obj returned error");
+	if (status)
+		return false;
+
+	if (!status) {
+		EXPECT_EQ(sizeof(char_array), char_array_bytes_read,
+				  "Not all bytes of object read");
+		EXPECT_EQ(sizeof(big_int), big_int_bytes_read,
+				  "Not all bytes of object read");
+		for (int i = 0; i < 5; i++) {
+			EXPECT_EQ(char_array[i], char_array_buffer[i],
+			          "Did not read object from NVRAM correctly");
+		}
+
+		EXPECT_EQ(big_int, big_int_buffer,
+		          "Did not read object from NVRAM correctly");
+	}
+
+	norfs_put_obj_iovec(17, iov_in, iov_in_count, 0);
+
+	status = norfs_read_obj_iovec(17, iov_out, iov_out_count, &obj_len, 0);
+	EXPECT_EQ(NO_ERROR, status, "norfs_read_obj returned error for iovec");
+
+	if (!status) {
+		EXPECT_EQ('a', *((unsigned char *)iov_out[0].iov_base),
+				  "Iovec not retrieved correctly");
+		EXPECT_EQ('b', *((unsigned char *)iov_out[1].iov_base),
+				  "Iovec not retrieved correctly");
+		EXPECT_EQ('c', *((unsigned char *)iov_out[1].iov_base + 1),
+				  "Iovec not retrieved correctly");
+		EXPECT_EQ('d', *((unsigned char *)iov_out[1].iov_base + 2),
+				  "Iovec not retrieved correctly");
+		EXPECT_EQ('e', *((unsigned char *)iov_out[2].iov_base),
+				  "Iovec not retrieved correctly");
+	} else {
+		return false;
+	}
+
+	EXPECT_EQ(5, obj_len, "Iovec did not return length correctly");
+
+	/* Ensure static data is cleared when you remount. */
+	EXPECT_EQ(norfs_mount_fs(norfs_nvram_offset), ERR_ALREADY_MOUNTED, "Remounting should fail");
+	save_bank();
+	norfs_unmount_fs();
+	EXPECT_EQ(NO_ERROR, norfs_mount_fs(norfs_nvram_offset), "Error during mount");
+	memset(char_array_buffer, 0, sizeof(char_array_buffer));
+	big_int_buffer = 0;
+
+	status = norfs_read_obj(char_key, char_array_buffer, sizeof(unsigned char)*6,
+					  &char_array_bytes_read, 0);
+	EXPECT_EQ(NO_ERROR, status, "norfs_read_obj returned error");
+	if (status)
+		return false;
+	status = norfs_read_obj(int_key, (unsigned char *)&big_int_buffer,
+					  sizeof(big_int_buffer), &big_int_bytes_read, 0);
+	EXPECT_EQ(NO_ERROR, status, "norfs_read_obj returned error");
+	if (status)
+		return false;
+
+	if (!status) {
+		EXPECT_EQ(char_array_bytes_read, sizeof(char_array),
+				  "Not all bytes of object read");
+		EXPECT_EQ(big_int_bytes_read, sizeof(big_int),
+				  "Not all bytes of object read");
+		for (int i = 0; i < 5; i++) {
+			EXPECT_EQ(char_array[i], char_array_buffer[i],
+			          "Did not read object from NVRAM correctly");
+		}
+
+		EXPECT_EQ(big_int, big_int_buffer,
+		          "Did not read object from NVRAM correctly");
+	}
+
+	status = norfs_read_obj_iovec(17, iov_out, iov_out_count - 1, &obj_len, 0);
+
+	EXPECT_EQ(NO_ERROR, status, "norfs_read_obj returned error for iovec");
+
+	if (!status) {
+		EXPECT_EQ('a', *((unsigned char *)iov_out[0].iov_base),
+				  "Iovec not retrieved correctly");
+		EXPECT_EQ('b', *((unsigned char *)iov_out[1].iov_base),
+				  "Iovec not retrieved correctly");
+		EXPECT_EQ('c', *((unsigned char *)iov_out[1].iov_base + 1),
+				  "Iovec not retrieved correctly");
+		EXPECT_EQ('d', *((unsigned char *)iov_out[1].iov_base + 2),
+				  "Iovec not retrieved correctly");
+	} else {
+		return false;
+	}
+
+	EXPECT_EQ(4, obj_len, "Iovec did not return length correctly");
+
+	status = norfs_remove_obj(17);
+	EXPECT_EQ(NO_ERROR, status, "Error when removing object");
+
+	status = norfs_read_obj_iovec(17, iov_out, 2, &obj_len, 0);
+	EXPECT_EQ(ERR_NOT_FOUND, status, "Found object when unexpected");
+
+	status = norfs_remove_obj(17);
+	EXPECT_EQ(ERR_NOT_FOUND, status, "Allowed redundant delete of object");
+
+	wipe_fs();
+	END_TEST;
+}
+
+/* This test may need to be revised if no longer using round-robin garbage
+ * collection, since there will be no guarantee the block containing the
+ * object with key 4 has been collected.
+ */
+static bool test_garbage_collection(void)
+{
+	BEGIN_TEST;
+	int size = FLASH_PAGE_SIZE/16;
+	unsigned char array[size];
+	size_t bytes_read;
+	status_t status = NO_ERROR;
+
+	norfs_mount_fs(norfs_nvram_offset);
+
+	/* Store an object that will not be overwritten. */
+	memset(array, 4, size);
+	status = norfs_put_obj(4, array, size, 0);
+	EXPECT_EQ(NO_ERROR, status,
+			  "Error putting object");
+	/* Store enough versions of objects to ensure block containing
+	 * first object is collected.
+	 */
+	for (int i = 0; i < 51; i++) {
+		for (int j = 0; j < 4; j++) {
+			memset(array, j, size);
+			status = norfs_put_obj(j, array, size, 0);
+			EXPECT_EQ(NO_ERROR, status,
+					  "Error putting object");
+			if (status)
+				break;
+		}
+		if (status)
+			break;
+	}
+
+	status = norfs_read_obj(4, array, size, &bytes_read, 0);
+	EXPECT_EQ(NO_ERROR, status,
+			  "Error reading object");
+	/* Verify first object still intact. */
+	for(int i = 0; i < size; i++) {
+		EXPECT_EQ(4, array[i], "Bad value for object.\n");
+	}
+
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_total_remaining_space(void)
+{
+	BEGIN_TEST;
+	uint32_t prev_remaining_space;
+
+	unsigned char array[4];
+
+	wipe_fs();
+	norfs_mount_fs(norfs_nvram_offset);
+
+	norfs_put_obj(0, array, 3, 0);
+	norfs_put_obj(0, array, 4, 0);
+
+	prev_remaining_space = total_remaining_space;
+
+	norfs_unmount_fs();
+	norfs_mount_fs(norfs_nvram_offset);
+
+	EXPECT_EQ(total_remaining_space, prev_remaining_space,
+			  "Remaining space incorrectly maintained");
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_deletion(void)
+{
+	BEGIN_TEST;
+
+	status_t status;
+	struct norfs_inode *inode = NULL;
+	uint ptr = 0;
+
+	wipe_fs();
+	ptr += flash_nor_write(0, ptr, sizeof(erase_header), erase_header);
+	ptr += flash_nor_write(0, ptr, sizeof(gc_header), gc_header);
+	ptr += flash_nor_write(0, ptr, sizeof(good_object), good_object);
+	ptr += flash_nor_write(0, ptr, sizeof(good_object_deleted_v2),
+						   good_object_deleted_v2);
+
+	EXPECT_EQ(NO_ERROR, norfs_mount_fs(norfs_nvram_offset), "Error during mount");
+	status = get_inode(2, &inode);
+	EXPECT_EQ(true, status,
+			  "Inode should be in list since references remain");
+	if (!status)
+		return false;
+	EXPECT_EQ(2, (inode)->reference_count,
+			  "Reference count should count one non-deleted version of object");
+	EXPECT_EQ(24, (inode)->location, "Not pointing to current verison");
+	write_pointer = FLASH_PAGE_SIZE + sizeof(erase_header);
+	write_pointer += flash_nor_write(0, write_pointer, sizeof(erase_header),
+									 erase_header);
+	EXPECT_EQ(NO_ERROR, collect_block(0, &write_pointer),
+			  "Error when collecting block");
+	EXPECT_EQ(false, get_inode(2, &inode),
+			  "Inode still in list after references have been collected");
+
+	wipe_fs();
+	ptr = 0;
+	ptr += flash_nor_write(0, ptr, sizeof(erase_header), erase_header);
+	ptr += flash_nor_write(0, ptr, sizeof(gc_header), gc_header);
+	ptr += flash_nor_write(0, ptr, sizeof(good_object_deleted_v2),
+						   good_object_deleted_v2);
+	EXPECT_EQ(NO_ERROR, norfs_mount_fs(norfs_nvram_offset), "Error during mount");
+	EXPECT_EQ(true, get_inode(2, &inode),
+			  "Inode should be in list");
+
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_corruption(void)
+{
+	BEGIN_TEST;
+
+	unsigned char buff[4];
+	size_t bytes_read;
+	wipe_fs();
+	norfs_mount_fs(norfs_nvram_offset);
+
+	uint ptr = 4;
+	ptr += flash_nor_write(0, ptr, 4, gc_header);
+	ptr += flash_nor_write(0, ptr, 16, good_object);
+	EXPECT_EQ(24, ptr, "Write failed");
+	ptr += flash_nor_write(0, ptr, 16, bad_object);
+	EXPECT_EQ(40, ptr, "Write failed");
+	ptr += flash_nor_write(0, ptr, 16, good_object_v1);
+	EXPECT_EQ(56, ptr, "Write failed");
+	ptr += flash_nor_write(0, ptr, 16, another_good_object);
+	EXPECT_EQ(72, ptr, "Write failed");
+
+	norfs_unmount_fs();
+	norfs_mount_fs(norfs_nvram_offset);
+
+	status_t status = norfs_read_obj(2, buff, 4, &bytes_read, 0);
+	EXPECT_EQ(NO_ERROR, status,
+			  "Object prior to corruption should still be readable");
+
+	/* good_object_v1, appearing after the corruption, should be discarded. */
+	for (int i = 0; i < 4; i++) {
+		EXPECT_EQ(i, buff[i],
+				  "First version of object not correctly retrieved");
+	}
+
+	status = norfs_read_obj(4, buff, 4, &bytes_read, 0);
+	EXPECT_EQ(ERR_NOT_FOUND, status, "Corrupted object should not be found");
+	status = norfs_read_obj(0, buff, 4, &bytes_read, 0);
+	EXPECT_EQ(ERR_NOT_FOUND, status,
+			  "Object after corrupted object should not be found");
+
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_deletion_objects_retained_during_gc(void)
+{
+	BEGIN_TEST;
+	static const uint16_t BLOCK_HEADER_SIZE = 8, OBJ_HEADER_LENGTH = 12;
+
+	/* Define an object size that will tightly fill a block with objects. */
+	uint16_t obj_len = (FLASH_PAGE_SIZE - BLOCK_HEADER_SIZE) / 20
+		- OBJ_HEADER_LENGTH;
+	status_t status;
+	unsigned char obj[obj_len];
+	uint32_t key = 17;
+	size_t bytes_read;
+
+	memset(obj, 7, obj_len);
+
+	wipe_fs();
+	EXPECT_EQ(NO_ERROR, norfs_mount_fs(norfs_nvram_offset), "Error during mount");
+
+	for (int i = 0; i < 20; i++) {
+		status = norfs_put_obj(key, obj, obj_len, 0);
+		EXPECT_EQ(NO_ERROR, status, "Error putting object");
+	}
+
+	status = norfs_remove_obj(key);
+	EXPECT_EQ(status, NO_ERROR, "Error removing object");
+
+	status = norfs_read_obj(key, obj, obj_len, &bytes_read, 0);
+	EXPECT_EQ(ERR_NOT_FOUND, status, "Object not removed");
+
+	find_free_block(&write_pointer);
+
+	status = norfs_read_obj(key, obj, obj_len, &bytes_read, 0);
+	EXPECT_EQ(ERR_NOT_FOUND, status,
+			  "Error: Object removal lost after garbage collection");
+
+	status = norfs_put_obj(key, obj, obj_len, 0);
+	EXPECT_EQ(NO_ERROR, status, "Error putting object");
+
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_garbage_collection_interruption(void)
+{
+	BEGIN_TEST;
+	wipe_fs();
+	size_t obj_len;
+	uint32_t ptr = 0;
+
+	unsigned char buffer[16];
+	/* Write partial block header, indicating interrupted garbage collection. */
+	ptr += flash_nor_write(0, ptr, sizeof(NORFS_BLOCK_HEADER),
+						   NORFS_BLOCK_HEADER);
+	ptr += flash_nor_write(0, ptr, sizeof(NORFS_BLOCK_GC_STARTED_HEADER),
+						   NORFS_BLOCK_GC_STARTED_HEADER);
+	ptr += sizeof(NORFS_BLOCK_GC_FINISHED_HEADER);
+	flash_nor_write(0, ptr, sizeof(good_object), good_object);
+
+	/* Move to second block. */
+	ptr = FLASH_PAGE_SIZE;
+	write_block_header(&ptr);
+	flash_nor_write(0, ptr, sizeof(another_good_object), another_good_object);
+
+	EXPECT_EQ(NO_ERROR, norfs_mount_fs(norfs_nvram_offset), "Error during mount");
+
+	EXPECT_EQ(NO_ERROR, norfs_read_obj(0, buffer, sizeof(buffer), &obj_len, 0),
+			  "Failed to read object in block with correct header");
+	EXPECT_EQ(ERR_NOT_FOUND, norfs_read_obj(2, buffer, sizeof(buffer), &obj_len, 0),
+			  "Object in corrupt block should not be readable");
+
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_thrash_fs(void)
+{
+	status_t status;
+	size_t bytes_read;
+	int int_buff;
+	int state[16];
+
+	for(int i = 0; i < 16; i++) {
+		state[i] = -1;
+	}
+
+	BEGIN_TEST;
+	wipe_fs();
+	norfs_mount_fs(norfs_nvram_offset);
+	srand(current_time());
+	iovec_t *iov = malloc(sizeof(iovec_t));
+	iov->iov_len = sizeof(int);
+
+	for (int i = 0; i < 10000; i++) {
+		int action = rand() % 6;
+		uint16_t key = (uint16_t)(rand() % 16);
+		switch(action) {
+			case 0:
+				status = norfs_remove_obj(key);
+				if (state[key] == -1) {
+					EXPECT_EQ(ERR_NOT_FOUND, status,
+							  "Removed object not returning correctly from read");
+				} else {
+					EXPECT_EQ(NO_ERROR, status,
+						      "Object not returning correctly from remove");
+					if (status) {
+						dump_bank();
+						return false;
+					}
+				}
+				state[key] = -1;
+				break;
+			case 1:
+				iov->iov_base = &i;
+				status = norfs_put_obj_iovec(key, iov, 1, 0);
+				EXPECT_EQ(NO_ERROR, status,
+						  "Object not returning correctly from put");
+				state[key] = i;
+				break;
+			case 2:
+				status = norfs_put_obj(key, (unsigned char*)&i, sizeof(i), 0);
+				EXPECT_EQ(NO_ERROR, status,
+						  "Object not returning correctly from put");
+				state[key] = i;
+				break;
+			case 3:
+				iov->iov_base = &int_buff;
+				status = norfs_read_obj_iovec(key, iov, 1, &bytes_read, 0);
+				if (state[key] == -1) {
+					EXPECT_EQ(ERR_NOT_FOUND, status,
+							  "Removed object not returning correctly from read");
+				} else {
+					EXPECT_EQ(NO_ERROR, status,
+							  "Object not returning correctly from read");
+					EXPECT_EQ(state[key], *(int*)iov->iov_base, "Object not correct value");
+					if (status || state[key] != *(int*)iov->iov_base) {
+						dump_bank();
+						return false;
+					}
+				}
+				break;
+			case 4:
+				status = norfs_read_obj(key, (unsigned char*)&int_buff, sizeof(int),
+								  &bytes_read, 0);
+				if (state[key] == -1) {
+					EXPECT_EQ(ERR_NOT_FOUND, status,
+							  "Removed object not returning correctly from read");
+				} else {
+					EXPECT_EQ(NO_ERROR, status,
+							  "Object not returning correctly from read");
+					EXPECT_EQ(state[key], int_buff, "Object not correct value");
+					if (status || state[key] != int_buff) {
+						dump_bank();
+						return false;
+					}
+				}
+				break;
+			case 5:
+				norfs_unmount_fs();
+				norfs_mount_fs(norfs_nvram_offset);
+				break;
+			default:
+				/* Should not end up here. */
+				return false;
+		}
+		EXPECT_GE(8 * FLASH_PAGE_SIZE - 1, write_pointer,
+				  "Write pointer has overflowed NVRAM");
+	}
+	for (int i = 0; i < 16; i++) {
+		status = norfs_read_obj(i, (unsigned char*)&int_buff, sizeof(int), &bytes_read, 0);
+		if (state[i] == -1) {
+			EXPECT_EQ(ERR_NOT_FOUND, status,
+					  "Removed object not returning correctly from read");
+		} else {
+			EXPECT_EQ(NO_ERROR, status,
+					  "Object not returning correctly from read");
+			EXPECT_EQ(state[i], int_buff, "Object not correct value");
+		}
+	}
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_overflow_filesystem(void)
+{
+	BEGIN_TEST;
+
+	wipe_fs();
+	norfs_mount_fs(norfs_nvram_offset);
+
+	int i = 0;
+	bool filesystem_full;
+	status_t status;
+	unsigned char obj = 0b01010101;
+	while(!filesystem_full) {
+		status = norfs_put_obj(i, &obj, sizeof(obj), 0);
+		if(status != NO_ERROR) {
+			EXPECT_EQ(ERR_NO_MEMORY, status, "put_obj returned unexpected error");
+			EXPECT_LE(sizeof(obj) + NORFS_OBJ_HEADER_SIZE, total_remaining_space,
+					   "Returning memory error when filesystem not full");
+			if(status != ERR_TOO_BIG) {
+				return false;
+			}
+			filesystem_full = true;
+		}
+		i++;
+	}
+
+	for(int j = i - 1; j >= 0; j--) {
+		status = norfs_remove_obj(j);
+		if(total_remaining_space >= sizeof(obj) + NORFS_OBJ_HEADER_SIZE) {
+			status = norfs_put_obj(i, &obj, sizeof(obj), 0);
+			EXPECT_EQ(NO_ERROR, status, "Error after clearing space for new objects");
+			i++;
+		}
+	}
+
+	wipe_fs();
+	END_TEST;
+}
+
+static bool test_wrapping(void)
+{
+	BEGIN_TEST;
+	size_t bytes_read;
+	unsigned char buffer[4];
+	uint ptr = 0;
+
+	wipe_fs();
+	flash_nor_begin(0);
+
+	write_block_header(&ptr);
+	flash_nor_write(0, ptr, sizeof(good_object), good_object);
+	ptr = FLASH_PAGE_SIZE;
+	write_block_header(&ptr);
+	flash_nor_write(0, ptr, sizeof(good_object_v127), good_object_v127);
+
+	flash_nor_end(0);
+	norfs_mount_fs(norfs_nvram_offset);
+
+	EXPECT_EQ(NO_ERROR, norfs_read_obj(2, buffer, sizeof(buffer), &bytes_read, 0),
+			  "Error during read");
+	for (int i = 0; i < 4; i++) {
+		EXPECT_EQ(*(buffer + i), *(good_object_v127 + NORFS_OBJ_OFFSET + i),
+				  "Object not read correctly");
+	}
+
+	wipe_fs();
+
+	flash_nor_begin(0);
+	ptr = 0;
+
+	write_block_header(&ptr);
+	flash_nor_write(0, ptr, sizeof(good_object), good_object);
+	ptr = FLASH_PAGE_SIZE;
+	write_block_header(&ptr);
+	flash_nor_write(0, ptr, sizeof(good_object_v128), good_object_v128);
+
+	flash_nor_end(0);
+
+	norfs_mount_fs(norfs_nvram_offset);
+
+	EXPECT_EQ(NO_ERROR, norfs_read_obj(2, buffer, sizeof(buffer), &bytes_read,
+			  0),
+			  "Error during read");
+	for (int i = 0; i < 4; i++) {
+		EXPECT_EQ(*(buffer + i), *(good_object + NORFS_OBJ_OFFSET + i),
+				  "Object not read correctly when wrapped");
+	}
+
+	wipe_fs();
+	END_TEST;
+}
+
+static void init_tests(void)
+{
+	platform_init();
+	wipe_fs();
+}
+
+BEGIN_TEST_CASE(norfs_tests);
+init_tests();
+RUN_TEST(test_basic_read_write);
+RUN_TEST(test_garbage_collection);
+RUN_TEST(test_deletion);
+RUN_TEST(test_corruption);
+RUN_TEST(test_deletion_objects_retained_during_gc);
+RUN_TEST(test_garbage_collection_interruption);
+RUN_TEST(test_total_remaining_space);
+RUN_TEST(test_thrash_fs);
+RUN_TEST(test_wrapping);
+RUN_TEST(test_overflow_filesystem);
+END_TEST_CASE(norfs_tests);
diff --git a/src/bsp/lk/lib/norfs/test/norfs_test_helper.c b/src/bsp/lk/lib/norfs/test/norfs_test_helper.c
new file mode 100644
index 0000000..7d466e6
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/test/norfs_test_helper.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 Heather Lee Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <norfs_test_helper.h>
+#include <lib/norfs.h>
+#include <dev/flash_nor.h>
+#include <platform/flash_nor_config.h>
+#include <debug.h>
+
+void dump_bank(void)
+{
+	const struct flash_nor_bank *bank;
+	bank = flash_nor_get_bank(0);
+	printf("\n\n**DUMP BANK**\n");
+	uint8_t *addr = (uint8_t *)bank->base;
+	for (int i = 0; i < 8 * FLASH_PAGE_SIZE; i++) {
+		if (i%4 == 0) printf("  ");
+		if (i%FLASH_PAGE_SIZE == 0)
+			printf("\n\n\n\n\n\ni: %d\n", i);
+		printf("%02X ", *(addr + i));
+	}
+}
+
+void wipe_fs(void)
+{
+	norfs_unmount_fs();
+	flash_nor_begin(0);
+	flash_nor_erase_pages(0, 0 + norfs_nvram_offset, 8 * FLASH_PAGE_SIZE);
+	flash_nor_end(0);
+}
diff --git a/src/bsp/lk/lib/norfs/test/rules.mk b/src/bsp/lk/lib/norfs/test/rules.mk
new file mode 100644
index 0000000..c0c0d10
--- /dev/null
+++ b/src/bsp/lk/lib/norfs/test/rules.mk
@@ -0,0 +1,13 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_DEPS := \
+	lib/norfs \
+	lib/unittest
+
+MODULE_SRCS := \
+	$(LOCAL_DIR)/norfs_test.c \
+	$(LOCAL_DIR)/norfs_test_helper.c
+
+include make/module.mk