hnvram: Support x86 compiler & changing target_mtd

 - Some minor type changes to support x86 compiler
 - Adds option to read/write to file instead of flash

These changes together allow for unit testing which has been added.

Depends on go/fibercl/XXXXX

In reference to b/31822982

Change-Id: Ica9bfc28fcb6d32361427c9f5ebc56cf0aa6c14c
diff --git a/libupgrade/Makefile b/libupgrade/Makefile
index df9c334..b829f46 100644
--- a/libupgrade/Makefile
+++ b/libupgrade/Makefile
@@ -9,7 +9,7 @@
 RANLIB:=$(CROSS_PREFIX)ranlib
 STRIP:=$(CROSS_PREFIX)strip
 
-CFLAGS = -g -Os -Wall -Werror
+CFLAGS = -g -Os -Wall -Werror -I.
 
 SRCS = hmx_upgrade_flash.c hmx_upgrade_nvram.c
 INCS = hmx_upgrade_nvram.h hnvram_dlist.h
@@ -20,8 +20,19 @@
 .c.o:
 	$(CC) $(CFLAGS) -c $< -o $@
 
+unit_test: test
+test: hmx_upgrade_flash_test hmx_upgrade_nvram_test
+	./test/hmx_upgrade_flash_test
+	./test/hmx_upgrade_nvram_test
+
 libhmxupgrade.a: $(OBJS) $(INCS)
 	$(AR) rcs $@ $(OBJS)
 
+hmx_upgrade_flash_test:
+	$(CPP) $(CFLAGS) ./test/hmx_upgrade_flash_test.cc hmx_upgrade_flash.c -o ./test/$@ -lgtest -lpthread
+
+hmx_upgrade_nvram_test:
+	$(CPP) $(CFLAGS) -std=gnu++11 ./test/hmx_upgrade_nvram_test.cc -o ./test/$@ -lgtest -lpthread
+
 clean:
-	rm -f libhmxupgrade.a *.o
+	rm -f ./test/hmx_upgrade_nvram_test ./test/hmx_upgrade_flash_test libhmxupgrade.a *.o
diff --git a/libupgrade/hmx_upgrade_flash.c b/libupgrade/hmx_upgrade_flash.c
index 86833af..d01f8b2 100644
--- a/libupgrade/hmx_upgrade_flash.c
+++ b/libupgrade/hmx_upgrade_flash.c
@@ -81,8 +81,7 @@
 /****************************** define *****************************/
 /*******************************************************************/
 /* Start #define */
-/* this is for test */
-#define TARGET_NVRAM_MTD        "/dev/mtd/hnvram"
+#define HNVRAM_PARTITION_DEFAULT        "/dev/mtd/hnvram"
 /* End #define */
 
 /*******************************************************************/
@@ -104,12 +103,27 @@
 /************************ static variables *************************/
 /*******************************************************************/
 /* Start static variable */
+static const char *hnvram_partition = HNVRAM_PARTITION_DEFAULT;
 /* End static variable */
 
 /*******************************************************************/
 /************************ static funtions **************************/
 /*******************************************************************/
-int HMX_UPGRADE_NVRAM_Write(unsigned long offset, unsigned char * data,
+void HMX_UPGRADE_NVRAM_Init(const char * location)
+{
+  if (location == NULL) {
+    hnvram_partition = HNVRAM_PARTITION_DEFAULT;
+  } else {
+    hnvram_partition = location;
+  }
+}
+
+const char* HMX_UPGRADE_NVRAM_Get_Partition_Path(void)
+{
+  return hnvram_partition;
+}
+
+int HMX_UPGRADE_NVRAM_Write(unsigned long offset, const unsigned char * data,
                             unsigned int size )
 {
   int ret;
@@ -127,7 +141,7 @@
     return -1;
   }
 
-  rpath = realpath(TARGET_NVRAM_MTD, NULL);
+  rpath = realpath(hnvram_partition, NULL);
   if (!rpath) {
     perror("realpath");
     free(buf);
@@ -154,7 +168,7 @@
   }
 
   /* Only write to flash if data is different from what is already there. */
-  if (ret == size && !memcmp(data, buf, size)) {
+  if ((unsigned int)ret == size && !memcmp(data, buf, size)) {
     ret = 0;
     goto out_close;
   }
@@ -193,15 +207,21 @@
   }
 
   /*
-   * Writes to /dev/mtdblockX are cached indefinitely until the last file
-   * descriptor is closed. Flush this cache after writing. The user depends on
-   * data being physically written to flash when this function returns.
-   * Requires CAP_SYS_ADMIN.
+   * BLKFLSBUF will fail if we are writing to a file (-f) instead of a device
+   * If realpath is under /dev, then we are writing to a device and can proceed
    */
-  ret = ioctl(fd_nvram, BLKFLSBUF, 0);
-  if (ret < 0) {
-    perror("ioctl(hnvram, BLKFLSBUF, 0)");
-    goto out_close;
+  if (strncmp(rpath, "/dev/", 5) == 0) {
+    /*
+     * Writes to /dev/mtdblockX are cached indefinitely until the last file
+     * descriptor is closed. Flush this cache after writing. The user depends on
+     * data being physically written to flash when this function returns.
+     * Requires CAP_SYS_ADMIN.
+     */
+    ret = ioctl(fd_nvram, BLKFLSBUF, 0);
+    if (ret < 0) {
+      perror("ioctl(hnvram, BLKFLSBUF, 0)");
+      goto out_close;
+    }
   }
 
   ret = 0;
@@ -224,7 +244,7 @@
   if (libupgrade_verbose) printf("[%s] offset %08lx, size %d, data = %02x\n",
                                  __FUNCTION__, offset, size, data[0] );
 
-  rpath = realpath(TARGET_NVRAM_MTD, NULL);
+  rpath = realpath(hnvram_partition, NULL);
   if (!rpath) {
     perror("realpath");
     return -1;
diff --git a/libupgrade/hmx_upgrade_nvram.c b/libupgrade/hmx_upgrade_nvram.c
index 315c5e3..6031444 100644
--- a/libupgrade/hmx_upgrade_nvram.c
+++ b/libupgrade/hmx_upgrade_nvram.c
@@ -239,11 +239,9 @@
 static int drv_NVRAM_Read(unsigned char *buffer, unsigned int offset,
                           unsigned int length)
 {
-  DRV_Error     drv_error;
-
-  drv_error = HMX_UPGRADE_NVRAM_Read(offset, buffer, length);
-  if (drv_error != 0) {
-    return -1;
+  int ret = HMX_UPGRADE_NVRAM_Read(offset, buffer, length);
+  if (ret != 0) {
+    return ret;
   }
 
   return length;
@@ -262,13 +260,11 @@
     *  Return value:
     *      number of bytes written, or -1 if error occured
     ********************************************************************* */
-static int drv_NVRAM_Write(unsigned char *buffer, unsigned int offset,
+static int drv_NVRAM_Write(const unsigned char *buffer, unsigned int offset,
                            unsigned int length)
 {
-  DRV_Error     drv_error;
-
-  drv_error = HMX_UPGRADE_NVRAM_Write(offset, buffer, length);
-  if (drv_error != 0) {
+  int ret = HMX_UPGRADE_NVRAM_Write(offset, buffer, length);
+  if (ret != 0) {
     return -1;
   }
 
@@ -474,7 +470,8 @@
     ********************************************************************* */
 static DRV_Error drv_NVRAM_SetEnv(HMX_NVRAM_PARTITION_E partition,
                                   const unsigned char *name,
-                                  unsigned char *value, unsigned int valueLen)
+                                  const unsigned char *value,
+                                  unsigned int valueLen)
 {
   NVRAM_EVNVAR_T *env;
   unsigned int namelen;
@@ -486,7 +483,8 @@
   }
 
   namelen = strlen((char*)name);
-  env = malloc(sizeof(NVRAM_EVNVAR_T) + namelen + 1 + valueLen);
+  env = (NVRAM_EVNVAR_T*)
+      malloc(sizeof(NVRAM_EVNVAR_T) + namelen + 1 + valueLen);
   if (!env) {
     return DRV_ERR_OUTOFMEMORY;
   }
@@ -531,13 +529,13 @@
   unsigned int recLen;
   unsigned int recType;
   unsigned int offset;
-  unsigned int retval;
+  DRV_Error retval;
   unsigned char fileName[MAX_NAME_LEN];
   unsigned char nameLen;
   unsigned int dataLen;
 
   nvram_size = size;
-  buffer = malloc(MAX_FIELD_LEN);
+  buffer = (unsigned char*)malloc(MAX_FIELD_LEN);
   if (buffer == NULL)
   {
     return DRV_ERR_OUTOFMEMORY;
@@ -677,7 +675,7 @@
   DRV_Error result;
 
   nvram_size = s_nvram_size[partition];
-  buffer = malloc(nvram_size);
+  buffer = (unsigned char*)malloc(nvram_size);
   if (buffer == NULL) {
     return DRV_ERR_OUTOFMEMORY;
   }
@@ -694,6 +692,7 @@
 
     if ((ptr + NVRAM_TYPE_LEN + NVRAM_RECORD_LENG_LEN + recLen) > buffer_end) {
       /* One or more variables don't fit into the partition. Fail the write. */
+      free(buffer);
       return DRV_ERR_OUTOFMEMORY;
     }
 
@@ -738,8 +737,6 @@
   return ((A << 24) | ((A << 8) &  0xFF0000) | ((A >> 8) &  0xFF00) |(A >> 24));
 }
 
-static unsigned long s_nvramSema;
-
 static DRV_Error drv_NVRAM_GetFieldInfo(const NVRAM_FIELD_T type,
                                         HMX_NVRAM_PARTITION_E *pPartition,
                                         unsigned char *pszFileName,
@@ -765,16 +762,17 @@
 }
 
 
-DRV_Error HMX_NVRAM_Init(void)
+DRV_Error HMX_NVRAM_Init(const char *location)
 {
+  HMX_UPGRADE_NVRAM_Init(location);
   drv_NVRAM_Load(HMX_NVRAM_PARTITION_RO);
   drv_NVRAM_Load(HMX_NVRAM_PARTITION_RW);
   return DRV_OK;
 }
 
 DRV_Error HMX_NVRAM_Write(HMX_NVRAM_PARTITION_E partition,
-                          unsigned char *pName, unsigned int offset,
-                          unsigned char *pValue, unsigned int ulSize)
+                          const unsigned char *pName, unsigned int offset,
+                          const unsigned char *pValue, unsigned int ulSize)
 {
   DRV_Error drv_error;
 
@@ -787,9 +785,10 @@
   return drv_error;
 }
 
-DRV_Error HMX_NVRAM_Read(HMX_NVRAM_PARTITION_E partition, unsigned char *pName,
-                         unsigned int offset, unsigned char *pValue,
-                         unsigned int ulSize, unsigned int *pLen)
+DRV_Error HMX_NVRAM_Read(HMX_NVRAM_PARTITION_E partition,
+                         const unsigned char *pName, unsigned int offset,
+                         unsigned char *pValue, unsigned int ulSize,
+                         unsigned int *pLen)
 {
   DRV_Error drv_error;
 
@@ -828,7 +827,8 @@
     case HMX_NVRAM_PARTITION_RO :
     case HMX_NVRAM_PARTITION_RW :
       if (defaultSize == 0) {
-        errCode = HMX_NVRAM_Read(partition, szFileName, offset, data, nDataSize, pDataSize);
+        errCode = HMX_NVRAM_Read(partition, szFileName, offset,
+                                 (unsigned char*)data, nDataSize, pDataSize);
         if (errCode != DRV_OK) {
           return errCode;
         }
@@ -838,10 +838,10 @@
       } else {
         unsigned char *pBuf;
 
-        pBuf = malloc(defaultSize);
+        pBuf = (unsigned char*)malloc(defaultSize);
         if (pBuf == NULL) {
           DEBUG_ERR("[HMX_NVRAM_GetField] malloc\n");
-          return 3;
+          return DRV_ERR_INITIALIZATION;
         }
         errCode = HMX_NVRAM_Read(partition, szFileName, 0, pBuf, defaultSize, pDataSize);
         if (errCode != DRV_OK) {
@@ -896,7 +896,8 @@
           systemId = NVRAM_SWAP32((unsigned int)*(unsigned int*)data);
           data = (void*)&systemId;
         }
-        errCode = HMX_NVRAM_Write(partition, szFileName, 0, data, nDataSize);
+        errCode = HMX_NVRAM_Write(partition, szFileName, 0,
+                                  (unsigned char*)data, nDataSize);
         if (errCode != DRV_OK) {
           DEBUG_ERR("[HMX_NVRAM_SetField] field(%d)-%s error(%08X) : "
                     "HMX_NVRAM_Write\n", field, szFileName, errCode);
@@ -905,7 +906,7 @@
       } else {
         unsigned char *pBuf;
 
-        pBuf = malloc(defaultSize);
+        pBuf = (unsigned char*)malloc(defaultSize);
         if (pBuf == NULL) {
           DEBUG_ERR("[HMX_NVRAM_SetField] malloc\n");
           return DRV_ERR_INITIALIZATION;
@@ -959,14 +960,14 @@
   return DRV_OK;
 }
 
-DRV_Error HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_E partition, 
-                           unsigned char *pName)
+DRV_Error HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_E partition,
+                           const unsigned char *pName)
 {
-  int result;
+  DRV_Error result;
 
   SEM_Get(s_nvramSema);
   result = drv_NVRAM_Delete(partition, pName);
-  if (result == 0) {
+  if (result == DRV_OK) {
     result = drv_NVRAM_Save(partition);
   }
   SEM_Release(s_nvramSema);
@@ -1005,7 +1006,7 @@
   int nameLen, valueLen;
   int idx;
 
-  value = malloc(MAX_DATA_LEN);
+  value = (char*)malloc(MAX_DATA_LEN);
   if (value == NULL)
   {
     return DRV_ERR_OUTOFMEMORY;
diff --git a/libupgrade/hmx_upgrade_nvram.h b/libupgrade/hmx_upgrade_nvram.h
index 209dc20..bfc54e3 100644
--- a/libupgrade/hmx_upgrade_nvram.h
+++ b/libupgrade/hmx_upgrade_nvram.h
@@ -146,7 +146,10 @@
 /*******************************************************************/
 /******************** global function prototype ********************/
 /*******************************************************************/
-extern int HMX_UPGRADE_NVRAM_Write(unsigned long offset, unsigned char * data,
+extern void HMX_UPGRADE_NVRAM_Init(const char * location);
+extern const char* HMX_UPGRADE_NVRAM_Get_Partition_Path(void);
+extern int HMX_UPGRADE_NVRAM_Write(unsigned long offset,
+                                   const unsigned char * data,
                                    unsigned int size );
 extern int HMX_UPGRADE_NVRAM_Read(unsigned long offset, unsigned char * data,
                                   unsigned int size );
@@ -154,12 +157,12 @@
 /*******************************************************************/
 /******************** global function prototype ********************/
 /*******************************************************************/
-DRV_Error       HMX_NVRAM_Init(void);
+DRV_Error       HMX_NVRAM_Init(const char *location);
 DRV_Error       HMX_NVRAM_Write(HMX_NVRAM_PARTITION_E partition,
-                                unsigned char *pName, unsigned int offset,
-                                unsigned char *pValue, unsigned int ulSize);
+                                const unsigned char *pName, unsigned int offset,
+                                const unsigned char *pValue, unsigned int ulSize);
 DRV_Error       HMX_NVRAM_Read(HMX_NVRAM_PARTITION_E partition,
-                               unsigned char *pName, unsigned int offset,
+                               const unsigned char *pName, unsigned int offset,
                                unsigned char *pValue, unsigned int ulSize,
                                unsigned int *pLen);
 DRV_Error       HMX_NVRAM_GetField(NVRAM_FIELD_T field, unsigned int offset,
@@ -168,7 +171,7 @@
                                    void *data, int nDataSize);
 DRV_Error       HMX_NVRAM_GetLength(NVRAM_FIELD_T field, unsigned int *pLen);
 DRV_Error       HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_E partition,
-                                 unsigned char *pName);
+                                 const unsigned char *pName);
 DRV_Error       HMX_NVRAM_Dir(void);
 /* End global function prototypes */
 
diff --git a/libupgrade/test/hmx_test_base.cc b/libupgrade/test/hmx_test_base.cc
new file mode 100644
index 0000000..484838d
--- /dev/null
+++ b/libupgrade/test/hmx_test_base.cc
@@ -0,0 +1,92 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+// Author: germuth@google.com (Aaron Germuth)
+
+// Humax Hnvram Base Testing Class
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include "gtest/gtest.h"
+
+#define TEST_MAIN
+
+// Tests can change this variable to temporarily write to another file
+// Value restored to staticFileName at end of test
+const char* hnvramFileName;
+const char* staticFileName;
+const int hnvramFileSize = 2048000;
+
+// Helper Testing Methods
+int hnvram_empty() {
+  unsigned char buff[hnvramFileSize + 1];
+  int fp = open(hnvramFileName, O_RDONLY);
+  EXPECT_GT(fp, 0);
+  EXPECT_EQ(hnvramFileSize, read(fp, buff, hnvramFileSize));
+  EXPECT_EQ(0, close(fp));
+
+  for(int i = 0; i < hnvramFileSize; i++) {
+    if (buff[i] != '\0') {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int delete_hnvram_file() {
+  char cmd[30];
+  sprintf(cmd, "rm -f %s", hnvramFileName);
+  int ret = system(cmd);
+  if (ret != 0) {
+    fprintf(stderr, "Failed to delete hnvram partition file: %s\n",
+            hnvramFileName);
+  }
+  return ret;
+}
+
+// Base testing class
+class HnvramTest : public ::testing::Test {
+  public:
+    HnvramTest() {}
+    virtual ~HnvramTest() {}
+
+    virtual void SetUp() {
+      hnvramFileName = staticFileName;
+
+      // Create new blank hnvram partition
+      char cmd[255];
+      sprintf(cmd, "dd if=/dev/zero of=%s bs=1024 count=2KB >& /dev/null",
+              hnvramFileName);
+      int ret = system(cmd);
+
+      if (ret != 0) {
+        fprintf(stderr, "Failed to create hnvram partition file: %s\n",
+                hnvramFileName);
+      }
+
+      EXPECT_TRUE(hnvram_empty());
+    }
+
+    virtual void TearDown() {
+      delete_hnvram_file();
+    }
+};
+
+class HnvramEnvironment : public ::testing::Environment {
+ public:
+  virtual ~HnvramEnvironment() {}
+  virtual void SetUp() {
+    // Get Unique File Name
+    // use mktemp instead of mkstemp, since we don't want to open file yet
+    char fileName[255] = "/tmp/hnvram-test-XXXXXX";
+    mktemp(fileName);
+    if (fileName[0] == '\0') {
+      fprintf(stderr, "Failed to create unique file name\n");
+    }
+    staticFileName = strdup(fileName);
+  }
+
+  virtual void TearDown() {
+    // Just incase test case was interrupted, delete file here too
+    delete_hnvram_file();
+  }
+};
diff --git a/libupgrade/test/hmx_upgrade_flash_test.cc b/libupgrade/test/hmx_upgrade_flash_test.cc
new file mode 100644
index 0000000..a0dcbe2
--- /dev/null
+++ b/libupgrade/test/hmx_upgrade_flash_test.cc
@@ -0,0 +1,142 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+// Author: germuth@google.com (Aaron Germuth)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include "gtest/gtest.h"
+
+#define TEST_MAIN
+#include "hmx_test_base.cc"
+#include "hmx_upgrade_nvram.c"
+
+// Tests Constants
+const unsigned char* writeData = (unsigned char*)"ABC123def";
+const unsigned int writeLen = 10;
+
+// Test Parameterization
+const unsigned int offsets[] = {NVRAM_RO_OFFSET, NVRAM_RW_OFFSET, NVRAM_RWB_OFFSET};
+unsigned int offset;
+
+class HnvramFlashTest : public HnvramTest,
+  public ::testing::WithParamInterface<unsigned int> {
+  public:
+    HnvramFlashTest() {}
+    virtual ~HnvramFlashTest() {}
+
+    virtual void SetUp() {
+      libupgrade_verbose = 0;
+      offset = GetParam();
+
+      HnvramTest::SetUp();
+    }
+
+    virtual void TearDown() {
+      HnvramTest::TearDown();
+      offset = 0;
+    }
+};
+
+TEST_P(HnvramFlashTest, TestHnvramInit) {
+  EXPECT_STREQ("/dev/mtd/hnvram", HMX_UPGRADE_NVRAM_Get_Partition_Path());
+
+  HMX_UPGRADE_NVRAM_Init("/tmp/abctest123");
+  EXPECT_STREQ("/tmp/abctest123", HMX_UPGRADE_NVRAM_Get_Partition_Path());
+
+  HMX_UPGRADE_NVRAM_Init(NULL);
+  EXPECT_STREQ("/dev/mtd/hnvram", HMX_UPGRADE_NVRAM_Get_Partition_Path());
+}
+
+TEST_P(HnvramFlashTest, TestFlashWrite) {
+  const unsigned char test[2] = "A";
+
+  // bad realpath
+  HMX_UPGRADE_NVRAM_Init("/fake/doesnotexist915");
+  EXPECT_EQ(-1, HMX_UPGRADE_NVRAM_Write(0, test, 1));
+
+  // Can't open file (because its a directory)
+  HMX_UPGRADE_NVRAM_Init("/tmp");
+  EXPECT_EQ(-1, HMX_UPGRADE_NVRAM_Write(0, test, 1));
+
+  // Can't flush device (BLKFLSBUF)
+  HMX_UPGRADE_NVRAM_Init("/dev/zero");
+  EXPECT_EQ(-1, HMX_UPGRADE_NVRAM_Write(0, test, 1));
+
+  // should succeed without actually writing, if there would is no difference
+  // write zeros to zero'd file
+  HMX_UPGRADE_NVRAM_Init(hnvramFileName);
+
+  // can check by comparing file mtime, before and after
+  struct stat statbuf;
+  EXPECT_NE(-1, stat(hnvramFileName, &statbuf));
+  time_t before = statbuf.st_mtime;
+
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Write(0, (unsigned char*)"\0\0\0", 3));
+  unsigned char buff[255];
+  HMX_UPGRADE_NVRAM_Read(0, buff, 10);
+  EXPECT_EQ(0, memcmp((char*)buff, "\0\0\0\0\0\0\0\0\0\0", 10));
+  memset(buff, 0, sizeof(buff));
+
+  EXPECT_NE(-1, stat(hnvramFileName, &statbuf));
+  time_t after = statbuf.st_mtime;
+
+  EXPECT_EQ(before, after);
+
+  // verify writes with read and HMX_Read
+  HMX_UPGRADE_NVRAM_Init(hnvramFileName);
+  int fp = open(hnvramFileName, O_RDONLY);
+
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Write(offset, writeData, writeLen));
+
+  EXPECT_EQ(offset, lseek(fp, offset, SEEK_SET));
+  EXPECT_EQ(writeLen, read(fp, buff, writeLen));
+  EXPECT_EQ(0, memcmp((char*)buff, (char*)writeData, writeLen));
+  memset(buff, 0, sizeof(buff));
+
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(offset, buff, writeLen));
+  EXPECT_EQ(0, memcmp((char*)buff, (char*)writeData, writeLen));
+
+  close(fp);
+}
+
+TEST_P(HnvramFlashTest, TestFlashRead) {
+  unsigned char read_buffer[10];
+  // bad realpath
+  HMX_UPGRADE_NVRAM_Init("/fake/doesnotexist915");
+  EXPECT_EQ(-1, HMX_UPGRADE_NVRAM_Read(0, read_buffer, 0));
+
+  // Can't open file (because its a directory)
+  HMX_UPGRADE_NVRAM_Init("/tmp");
+  EXPECT_EQ(-1, HMX_UPGRADE_NVRAM_Read(0, read_buffer, 0));
+
+  // Can't read file because permissions
+  HMX_UPGRADE_NVRAM_Init("/proc/version");
+  EXPECT_EQ(-1, HMX_UPGRADE_NVRAM_Read(0, read_buffer, 10000000));
+
+  // File should be completely empty
+  HMX_UPGRADE_NVRAM_Init(hnvramFileName);
+  unsigned char buff[255];
+
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(offset, buff, writeLen));
+  EXPECT_EQ(0, memcmp((char*)buff, "\0\0\0\0\0\0\0\0\0\0", writeLen));
+  memset((char*)buff, 0, sizeof(buff));
+
+  // Should be able to HMX_Read data from raw C writes
+  int fp = open(hnvramFileName, O_WRONLY);
+  EXPECT_EQ(offset, lseek(fp, offset, SEEK_SET));
+  EXPECT_EQ(writeLen, write(fp, writeData, writeLen));
+  close(fp);
+
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(offset, buff, writeLen));
+  EXPECT_EQ(0, memcmp((char*)buff, writeData, writeLen));
+}
+
+INSTANTIATE_TEST_CASE_P(TryAllOffsets, HnvramFlashTest,
+                        ::testing::ValuesIn(offsets));
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ::testing::AddGlobalTestEnvironment(new HnvramEnvironment());
+  return RUN_ALL_TESTS();
+}
diff --git a/libupgrade/test/hmx_upgrade_nvram_test.cc b/libupgrade/test/hmx_upgrade_nvram_test.cc
new file mode 100644
index 0000000..08fe339
--- /dev/null
+++ b/libupgrade/test/hmx_upgrade_nvram_test.cc
@@ -0,0 +1,700 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+// Author: germuth@google.com (Aaron Germuth)
+
+// Humax Hnvram Testing
+// Methods from hmx_upgrade_flash.c have been replaced with mock methods below.
+//
+// Fields are tested by trying to read/write two different fields:
+// MAC_ADDR and ACTIVATED_KERNEL_NAME, one for each partition.
+//
+// Variables are tested by trying to read/write the same variable, but
+// in each partition seperately (ReadOnly and ReadWrite)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <tuple>
+#include "gtest/gtest.h"
+
+#define TEST_MAIN
+#include "hmx_test_base.cc"
+#include "hmx_upgrade_nvram.c"
+
+using ::testing::TestWithParam;
+using Partition = HMX_NVRAM_PARTITION_E;
+
+// Field Parameters
+// partition, fieldType, fieldName, fieldVal1, fieldVal2, fieldValLen
+std::tuple<Partition, NVRAM_FIELD_T, unsigned char*, unsigned char*,
+    unsigned char*,unsigned int> field_tuples[] = {
+    std::make_tuple(HMX_NVRAM_PARTITION_RO, NVRAM_FIELD_MAC_ADDR,
+                    (unsigned char*)"MAC_ADDR",
+                    (unsigned char*)"\x02\x04:\xAE\x06:\xFF\xE2",
+                    (unsigned char*)"\x0\x40:\xDD\x16:\x20\x41",
+                    (unsigned int) 8),
+    std::make_tuple(HMX_NVRAM_PARTITION_RW, NVRAM_FIELD_ACTIVATED_KERNEL_NAME,
+                    (unsigned char*)"ACTIVATED_KERNEL_NAME",
+                    (unsigned char*)"kernel0",
+                    (unsigned char*)"kernel1",
+                    (unsigned int) 7)
+};
+
+// Variable Parameters
+// partition, partitionOther, partitionOffset
+std::tuple<Partition, Partition, int> var_tuples[] = {
+  std::make_tuple(HMX_NVRAM_PARTITION_RO, // Read only
+                  HMX_NVRAM_PARTITION_RW, NVRAM_RO_OFFSET),
+  std::make_tuple(HMX_NVRAM_PARTITION_RW, // Read Write
+                  HMX_NVRAM_PARTITION_RO, NVRAM_RW_OFFSET)
+};
+
+// Global Test Parameters, populated by var_tuples and field_tuples
+NVRAM_FIELD_T fieldType;
+unsigned char* fieldName;
+unsigned char* fieldVal;
+unsigned char* fieldValOther;
+unsigned int fieldValLen;
+Partition part;
+Partition partOther;
+int offset;
+
+// Global Test Constants
+const unsigned char* name = (unsigned char*)"test_name";
+const unsigned char* nameOther = (unsigned char*)"test_name2";
+const unsigned char* val = (unsigned char*)"TEST\0\x02\x11";
+const unsigned int nameLen = 9;
+const unsigned int nameOtherLen = 10;
+const unsigned int valLen = 7;
+
+// Helper Testing Methods
+int dlist_size(Partition part) {
+  int i = 0;
+  dlist_hdr_t *entry;
+  DLIST_FOR_EACH(&s_nvram_envvars[part], entry) {
+    i++;
+  }
+  return i;
+}
+
+int dlist_contains(Partition part, const unsigned char* name) {
+  dlist_hdr_t *entry;
+  NVRAM_EVNVAR_T *env;
+  DLIST_FOR_EACH(&s_nvram_envvars[part], entry) {
+    env = (NVRAM_EVNVAR_T *) entry;
+    if (strcmp((char*)env->name, (char*)name) == 0) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+void clear_dlist() {
+  drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RO, name);
+  drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RW, name);
+  drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RO, nameOther);
+  drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RW, nameOther);
+  drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RO, fieldName);
+  drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RW, fieldName);
+
+  // Ensure dlist empty
+  EXPECT_EQ(0, dlist_size(HMX_NVRAM_PARTITION_RO));
+  EXPECT_EQ(0, dlist_size(HMX_NVRAM_PARTITION_RW));
+}
+
+// Hnvram byte formatting - Used to verify tests
+// One byte to store name length, 4 bytes for value length
+const unsigned int recLen = 1 + nameLen + 4 + valLen;
+// Include 5 intro bytes (1 start byte + 4 bytes for recLen)
+const unsigned int totalLen = recLen + 5;
+char* raw_hnvram_bytes(char* bytes) {
+  // Start Byte
+  bytes[0] = '\x01';
+
+  // Record Length
+  bytes[1] = '\0';
+  bytes[2] = '\0';
+  bytes[3] = '\0';
+  bytes[4] = recLen;
+
+  // Name Length
+  bytes[5] = nameLen;
+
+  // Name
+  memcpy(bytes + 6, name, nameLen);
+
+  // Val Length
+  bytes[6 + nameLen] = '\0';
+  bytes[7 + nameLen] = '\0';
+  bytes[8 + nameLen] = '\0';
+  bytes[9 + nameLen] = valLen;
+
+  //Val
+  memcpy(bytes + 10 + nameLen, val, valLen);
+
+  return bytes;
+}
+
+// Replacement methods for hmx_upgrade_flash.c
+// We always use a file instead of flash memory
+const char* hnvram_location = NULL;
+void HMX_UPGRADE_NVRAM_Init(const char * location) {
+  return;
+}
+
+const char* HMX_UPGRADE_NVRAM_Get_Partition_Path(void) {
+  return "/dev/mtd/hnvram";
+}
+
+int HMX_UPGRADE_NVRAM_Write(unsigned long offset, const unsigned char * data,
+                            unsigned int size ) {
+  int fp = open(hnvramFileName, O_WRONLY);
+  if (fp < 0) {
+    return -1;
+  }
+  int ret = lseek(fp, offset, SEEK_SET);
+  if (ret < 0) {
+    return -1;
+  }
+  ret = write(fp, data, size);
+  if (ret < 0) {
+    return -1;
+  }
+  ret = close(fp);
+  if (ret < 0) {
+    return -1;
+  }
+  return 0;
+}
+
+int HMX_UPGRADE_NVRAM_Read(unsigned long offset, unsigned char * data,
+                           unsigned int size ) {
+  int fp = open(hnvramFileName, O_RDONLY);
+  if (fp < 0) {
+    return -1;
+  }
+  int ret = lseek(fp, offset, SEEK_SET);
+  if (ret < 0) {
+    return -1;
+  }
+  ret = read(fp, data, size);
+  if (ret < 0) {
+    return -1;
+  }
+  ret = close(fp);
+  if (ret < 0) {
+    return -1;
+  }
+  return 0;
+}
+
+class HnvramFieldTest :
+  public HnvramTest, public ::testing::WithParamInterface<
+                  std::tuple<Partition, NVRAM_FIELD_T, unsigned char*,
+                  unsigned char*, unsigned char*,unsigned int> > {
+  public:
+    virtual void SetUp() {
+      // Initialize test Parameters
+      part = std::get<0>(GetParam());
+      fieldType = std::get<1>(GetParam());
+      fieldName = std::get<2>(GetParam());
+      fieldVal = std::get<3>(GetParam());
+      fieldValOther = std::get<4>(GetParam());
+      fieldValLen = std::get<5>(GetParam());
+
+      HnvramTest::SetUp();
+
+      clear_dlist();
+      // Shouldn't be able to find anything in a empty partition
+      EXPECT_EQ(NULL, drv_NVRAM_FindEnv(HMX_NVRAM_PARTITION_RO, name));
+      EXPECT_EQ(NULL, drv_NVRAM_FindEnv(HMX_NVRAM_PARTITION_RW, name));
+    }
+
+    virtual void TearDown() {
+      clear_dlist();
+      HnvramTest::TearDown();
+
+      part = HMX_NVRAM_PARTITION_UNSPECIFIED;
+      fieldType = NVRAM_FIELD_DUMMY;
+      fieldName = NULL;
+      fieldVal = NULL;
+      fieldValOther = NULL;
+      fieldValLen = 0;
+    }
+};
+
+TEST_P(HnvramFieldTest, TestGetFieldInfo) {
+  unsigned char buff[255];
+  unsigned int defSize = 0;
+  HMX_NVRAM_PARTITION_E partUsed;
+
+  EXPECT_EQ(DRV_ERR,
+            drv_NVRAM_GetFieldInfo(NVRAM_FIELD_DUMMY,
+                                   &partUsed, buff, &defSize));
+
+  EXPECT_EQ(DRV_OK,
+            drv_NVRAM_GetFieldInfo(fieldType, &partUsed, buff, &defSize));
+  EXPECT_STREQ((char*) buff, (char*)fieldName);
+  EXPECT_EQ(part, partUsed);
+  EXPECT_EQ(defSize, 0);
+}
+
+TEST_P(HnvramFieldTest, TestGetFieldAndSize) {
+  unsigned char buff[255];
+  unsigned int dataSize = sizeof(buff);
+  unsigned int pDataSize = 0;
+
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_GetFieldAndSize(NVRAM_FIELD_DUMMY,
+                                      0, buff, dataSize, &pDataSize));
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_GetFieldAndSize(fieldType,
+                                      0, NULL, dataSize, &pDataSize));
+  // Nothing to Read
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_GetFieldAndSize(fieldType,
+                                      0, buff, dataSize, &pDataSize));
+  // Write MAC_ADDR
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_Write(part, fieldName, 0, fieldVal, fieldValLen));
+
+  // Read it back
+  EXPECT_EQ(DRV_OK,
+            HMX_NVRAM_GetFieldAndSize(fieldType,
+                                      0, buff, dataSize, &pDataSize));
+  EXPECT_EQ(0, memcmp((char*)buff, (char*)fieldVal, fieldValLen));
+  EXPECT_EQ(dataSize, 255);
+  EXPECT_EQ(pDataSize, fieldValLen);
+}
+
+TEST_P(HnvramFieldTest, TestSetField) {
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_SetField(NVRAM_FIELD_DUMMY, 0, fieldVal, fieldValLen));
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_SetField(fieldType, 0, NULL, fieldValLen));
+
+  // Should add variable
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_SetField(fieldType, 0, fieldVal, fieldValLen));
+  EXPECT_EQ(1, dlist_size(part));
+  EXPECT_EQ(1, dlist_contains(part, fieldName));
+  EXPECT_FALSE(hnvram_empty());
+
+  // Should be able to read it back
+  unsigned char buff[255];
+  unsigned int dataSize = sizeof(buff);
+
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_GetField(fieldType, 0, buff, dataSize));
+  EXPECT_EQ(0, memcmp((char*)buff, (char*)fieldVal, fieldValLen));
+
+  // Try changing value
+  EXPECT_EQ(DRV_OK,
+            HMX_NVRAM_SetField(fieldType, 0, fieldValOther, fieldValLen));
+  EXPECT_EQ(1, dlist_size(part));
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_GetField(fieldType, 0, buff, dataSize));
+  EXPECT_EQ(0, memcmp((char*)buff, (char*)fieldValOther, fieldValLen));
+
+  // Test with Offset
+  int offset = 3;
+  EXPECT_EQ(DRV_OK,
+            HMX_NVRAM_SetField(fieldType, offset, fieldVal, fieldValLen));
+  EXPECT_EQ(1, dlist_size(part));
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_GetField(fieldType, offset, buff, dataSize));
+  EXPECT_EQ(0, memcmp((char*)buff, (char*)(fieldVal + offset),
+                      fieldValLen - offset));
+}
+
+TEST_P(HnvramFieldTest, TestGetLength) {
+  unsigned int readLen;
+
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_GetLength(fieldType, NULL));
+  EXPECT_EQ(DRV_ERR,
+            HMX_NVRAM_GetLength(NVRAM_FIELD_DUMMY, &readLen));
+
+  // No field length to grab
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_GetLength(fieldType, &readLen));
+  EXPECT_EQ(readLen, 0);
+
+  // Should work after adding
+  EXPECT_EQ(DRV_OK,
+            HMX_NVRAM_SetField(fieldType, 0, fieldVal, fieldValLen));
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_GetLength(fieldType, &readLen));
+  EXPECT_EQ(readLen, fieldValLen);
+}
+
+class HnvramVariableTest :
+  public HnvramTest, public ::testing::WithParamInterface<
+                  std::tuple<Partition, Partition, int> > {
+  public:
+    virtual void SetUp() {
+      // Initialize test Parameters
+      part = std::get<0>(GetParam());
+      partOther = std::get<1>(GetParam());
+      offset = std::get<2>(GetParam());
+
+      HnvramTest::SetUp();
+
+      clear_dlist();
+      // Initialize test Parameters
+    }
+    virtual void TearDown() {
+      clear_dlist();
+      HnvramTest::TearDown();
+
+      part = HMX_NVRAM_PARTITION_UNSPECIFIED;
+      partOther = HMX_NVRAM_PARTITION_UNSPECIFIED;
+      offset = -1;
+    }
+};
+
+TEST_P(HnvramVariableTest, TestDrvRead) {
+  unsigned char read[30];
+  // Should be able to read 0s
+  EXPECT_EQ(10, drv_NVRAM_Read(read, offset, 10));
+  EXPECT_STREQ("\0\0\0\0\0\0\0\0\0\0", (char*)read);
+  EXPECT_EQ(22, drv_NVRAM_Read(read, offset, 22));
+  EXPECT_STREQ("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", (char*) read);
+
+  // Should be able to read back a raw write
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Write(offset, val, valLen));
+  EXPECT_EQ(valLen, drv_NVRAM_Read(read, offset, valLen));
+  EXPECT_STREQ((char*)val, (char*)read);
+}
+
+TEST_P(HnvramVariableTest, TestDrvWrite) {
+  unsigned char read[255];
+  EXPECT_EQ(valLen, drv_NVRAM_Write(val, offset, valLen));
+
+  // raw read should return same string
+  EXPECT_EQ(0, hnvram_empty());
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(offset, read, valLen));
+  EXPECT_EQ(0, memcmp((char*)read, (char*) val, valLen));
+}
+
+TEST_P(HnvramVariableTest, TestFindEnv) {
+  // Should be able to read after SetEnv
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+  NVRAM_EVNVAR_T* env = drv_NVRAM_FindEnv(part, name);
+  EXPECT_FALSE(NULL == env);
+  EXPECT_EQ(0, memcmp((char*)env->value, (char*)val, valLen));
+  EXPECT_EQ(1, dlist_size(part));
+  EXPECT_EQ(1, dlist_contains(part, name));
+
+  EXPECT_EQ(NULL, drv_NVRAM_FindEnv(partOther, name));
+  EXPECT_EQ(NULL, drv_NVRAM_FindEnv(part, nameOther));
+
+  // Don't mix up with 2nd one
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, nameOther, val, valLen));
+  env = drv_NVRAM_FindEnv(part, nameOther);
+  EXPECT_FALSE(NULL == env);
+  EXPECT_EQ(0, memcmp((char*)env->name, (char*)nameOther, nameOtherLen));
+
+  env = drv_NVRAM_FindEnv(part, name);
+  EXPECT_FALSE(NULL == env);
+  EXPECT_EQ(0, memcmp((char*)env->name, (char*)name, nameLen));
+}
+
+TEST_P(HnvramVariableTest, TestScanDir) {
+  unsigned char readName[255];
+  unsigned int readNameLen;
+  unsigned char readVal[255];
+  unsigned int readValLen;
+
+  // Ask for 5th var when empty
+  EXPECT_EQ(DRV_ERR, drv_NVRAM_ScanDir(part, 4, readName, &readNameLen,
+                                       readVal, &readValLen));
+
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+
+  // Ask for 5th again, still fail
+  EXPECT_EQ(DRV_ERR, drv_NVRAM_ScanDir(part, 4, readName, &readNameLen,
+                                       readVal, &readValLen));
+
+  // Get back 1st var, should suceed
+  EXPECT_EQ(DRV_OK, drv_NVRAM_ScanDir(part, 0, readName, &readNameLen,
+                                       readVal, &readValLen));
+  EXPECT_EQ(readNameLen, nameLen);
+  EXPECT_EQ(readValLen, valLen);
+  EXPECT_EQ(0, memcmp((char*)name, readName, nameLen));
+  EXPECT_EQ(0, memcmp((char*)val, readVal, valLen));
+
+  // Add second, make it doesn't mix them up
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, nameOther, val, valLen));
+
+  EXPECT_EQ(DRV_OK, drv_NVRAM_ScanDir(part, 1, readName, &readNameLen,
+                                       readVal, &readValLen));
+  EXPECT_EQ(readNameLen, nameOtherLen);
+  EXPECT_EQ(0, memcmp((char*)nameOther, readName, nameOtherLen));
+
+  EXPECT_EQ(DRV_OK, drv_NVRAM_ScanDir(part, 0, readName, &readNameLen,
+                                       readVal, &readValLen));
+  EXPECT_EQ(readNameLen, nameLen);
+  EXPECT_EQ(0, memcmp((char*)name, readName, nameLen));
+}
+
+TEST_P(HnvramVariableTest, TestDrvDelete) {
+  // Nothing to delete
+  EXPECT_EQ(DRV_ERR, drv_NVRAM_Delete(part, name));
+
+  // Add single variable to delete
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+  EXPECT_EQ(DRV_OK, drv_NVRAM_Delete(part, name));
+  EXPECT_EQ(0, dlist_size(part));
+
+  // Don't mix them up
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, nameOther, val, valLen));
+  EXPECT_EQ(2, dlist_size(part));
+
+  EXPECT_EQ(DRV_OK, drv_NVRAM_Delete(part, name));
+  EXPECT_EQ(1, dlist_size(part));
+  NVRAM_EVNVAR_T* env = drv_NVRAM_FindEnv(part, nameOther);
+  EXPECT_TRUE(NULL != env);
+  EXPECT_EQ(0, memcmp(env->name, nameOther, nameOtherLen));
+
+  EXPECT_EQ(DRV_OK, drv_NVRAM_Delete(part, nameOther));
+  EXPECT_EQ(0, dlist_size(part));
+}
+
+TEST_P(HnvramVariableTest, TestDrvGetEnv) {
+  unsigned char read[255];
+  unsigned int readLen;
+  unsigned int maxCopied = 10;
+
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            drv_NVRAM_GetEnv(part, NULL, 0, read, maxCopied, &readLen));
+
+  // Try to read empty dlist
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            drv_NVRAM_GetEnv(part, name, 0, read, maxCopied, &readLen));
+
+  // Should be able to read after SetEnv
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+  EXPECT_EQ(DRV_OK, drv_NVRAM_GetEnv(part, name, 0, read, maxCopied, &readLen));
+  EXPECT_EQ(0, memcmp((char*)val, read, readLen));
+
+  // Should work with offset (2)
+  EXPECT_EQ(DRV_OK, drv_NVRAM_GetEnv(part, name, 2, read, maxCopied, &readLen));
+  EXPECT_EQ(0, memcmp((char*)(val + 2), read, readLen));
+
+  // Should respect maxCopied, and not overwrite
+  read[3] = '\xAE';
+  maxCopied = 3;
+  EXPECT_EQ(DRV_OK, drv_NVRAM_GetEnv(part, name, 0, read, maxCopied, &readLen));
+  EXPECT_EQ(0, memcmp((char*)val, read, maxCopied));
+  EXPECT_EQ('\xAE', (char)read[3]);
+
+  // Shouldn't be able to find other variables
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            drv_NVRAM_GetEnv(part, nameOther, 0, read, maxCopied, &readLen));
+}
+
+TEST_P(HnvramVariableTest, TestDrvGetLength) {
+  unsigned int readLen;
+
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            drv_NVRAM_GetLength(part, NULL, &readLen));
+
+  // Try to read empty dlist
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            drv_NVRAM_GetLength(part, name, &readLen));
+
+  // Should be able to read after SetEnv
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+  EXPECT_EQ(DRV_OK, drv_NVRAM_GetLength(part, name, &readLen));
+  EXPECT_EQ(valLen, readLen);
+
+  // Shouldn't be able to find other variables
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            drv_NVRAM_GetLength(part, nameOther, &readLen));
+}
+
+TEST_P(HnvramVariableTest, TestSetEnv) {
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+
+  // Check dlists
+  EXPECT_EQ(1, dlist_size(part));
+  EXPECT_EQ(1, dlist_contains(part, name));
+  EXPECT_EQ(0, dlist_size(partOther));
+
+  // Should be findable by findEnv
+  NVRAM_EVNVAR_T* env = drv_NVRAM_FindEnv(part, name);
+  EXPECT_TRUE(env != NULL);
+
+  // Params should match
+  EXPECT_STREQ((char*)env->name, (char*)name);
+  EXPECT_EQ(0, memcmp((char*)env->value, (char*)val, valLen));
+
+  EXPECT_EQ(env->nameLen, nameLen);
+  EXPECT_EQ(env->valueLen, valLen);
+  EXPECT_EQ(env->recLen, nameLen + valLen + 5);
+
+  // Make sure just anything isn't findable
+  EXPECT_EQ(NULL, drv_NVRAM_FindEnv(part, nameOther));
+  EXPECT_EQ(NULL, drv_NVRAM_FindEnv(partOther, name));
+
+  // Check RAWFS, just to be thorough
+  EXPECT_EQ(0, dlist_size(HMX_NVRAM_PARTITION_W_RAWFS));
+  EXPECT_EQ(NULL, drv_NVRAM_FindEnv(HMX_NVRAM_PARTITION_W_RAWFS, name));
+  EXPECT_EQ(0, dlist_size(HMX_NVRAM_PARTITION_W_RAWFS));
+
+  // Make sure we can add more than one
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, nameOther, val, valLen));
+  EXPECT_EQ(2, dlist_size(part));
+  EXPECT_EQ(1, dlist_contains(part, name));
+  EXPECT_EQ(1, dlist_contains(part, nameOther));
+}
+
+TEST_P(HnvramVariableTest, TestLoadByAddress) {
+  int size;
+  if (part == HMX_NVRAM_PARTITION_RO) {
+      size = NVRAM_RO_SIZE;
+  } else {
+      size = NVRAM_RW_SIZE;
+  }
+
+  // Nothing to load
+  EXPECT_EQ(DRV_OK, drv_NVRAM_LoadByAddress(part, offset, size));
+  EXPECT_EQ(0, dlist_size(part));
+
+  // Corrupt Hnvram start bit
+  char rhb[255];
+  raw_hnvram_bytes(rhb);
+  rhb[0] = '\xAE';
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Write(offset, (unsigned char*)rhb, recLen));
+  EXPECT_EQ(DRV_ERR_EVENT_INITIALIZATION,
+            drv_NVRAM_LoadByAddress(part, offset, size));
+
+  // corrupt record length sizes
+  raw_hnvram_bytes(rhb);
+  rhb[1] = '\xFF';
+  rhb[2] = '\xFF';
+  rhb[3] = '\xFF';
+  rhb[4] = '\xFF';
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Write(offset, (unsigned char*)rhb, recLen));
+  EXPECT_EQ(DRV_ERR_EVENT_INITIALIZATION,
+            drv_NVRAM_LoadByAddress(part, offset, size));
+
+  raw_hnvram_bytes(rhb);
+  rhb[4] = '\0';
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Write(offset, (unsigned char*)rhb, recLen));
+  EXPECT_EQ(DRV_ERR_EVENT_INITIALIZATION,
+            drv_NVRAM_LoadByAddress(part, offset, size));
+
+  // Proper load
+  raw_hnvram_bytes(rhb);
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Write(offset, (unsigned char*)rhb, recLen));
+  EXPECT_EQ(DRV_OK, drv_NVRAM_LoadByAddress(part, offset, size));
+  EXPECT_EQ(1, dlist_size(part));
+  EXPECT_EQ(1, dlist_contains(part, name));
+
+  // Fail cleanly with no partition
+  hnvramFileName = (const char*)"/oops";
+  EXPECT_EQ(DRV_ERR_EXTERNAL_ERROR,
+            drv_NVRAM_LoadByAddress(part, offset, size));
+}
+
+TEST_P(HnvramVariableTest, TestSave) {
+  unsigned char valueLarge[hnvramFileSize + 10];
+  // Variable doesn't fit in partition (fake the value length)
+  EXPECT_EQ(DRV_OK,
+           drv_NVRAM_SetEnv(part, name, valueLarge, sizeof(valueLarge) - 10));
+  EXPECT_EQ(DRV_ERR_OUTOFMEMORY, drv_NVRAM_Save(part));
+  EXPECT_EQ(DRV_OK, drv_NVRAM_Delete(part, name));
+
+  // file should still be empty if dlist has nothing to save
+  EXPECT_EQ(DRV_OK, drv_NVRAM_Save(part));
+  EXPECT_EQ(0, dlist_size(part));
+  EXPECT_TRUE(hnvram_empty());
+
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+  EXPECT_EQ(DRV_OK, drv_NVRAM_Save(part));
+
+  // Raw read back partition and try to match
+  unsigned char read[255];
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(offset, read, totalLen));
+
+  char rhb[255];
+  raw_hnvram_bytes(rhb);
+  EXPECT_EQ(0, memcmp((char*)read, rhb, totalLen));
+
+  // Writes to RW should have also saved to RWB partition, other should be 0s
+  if (part == HMX_NVRAM_PARTITION_RW) {
+    EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(NVRAM_RWB_OFFSET, read, totalLen));
+    EXPECT_EQ(0, memcmp((char*)read, rhb, totalLen));
+  } else {
+    EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(NVRAM_RWB_OFFSET, read, 7));
+    EXPECT_EQ(0, memcmp((char*)read, "\0\0\0\0\0\0\0", 7));
+  }
+
+  // Should be null byte after var to indicate no more variables
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(offset + totalLen, read, 1));
+  EXPECT_EQ(0, memcmp((char*)read, "\0", 1));
+}
+
+TEST_P(HnvramVariableTest, TestHMXWrite) {
+  unsigned char read[255];
+
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_Write(part, name, 0, val, valLen));
+
+  // Write should have inserted into dlist
+  EXPECT_EQ(1, dlist_size(part));
+  EXPECT_EQ(1, dlist_contains(part, name));
+
+  // And saved bytes to file
+  EXPECT_FALSE(hnvram_empty());
+  EXPECT_EQ(0, HMX_UPGRADE_NVRAM_Read(offset, read, totalLen));
+
+  char rhb[255];
+  raw_hnvram_bytes(rhb);
+  EXPECT_EQ(0, memcmp((char*)read, rhb, totalLen));
+}
+
+TEST_P(HnvramVariableTest, TestHMXRead) {
+  unsigned char read[255];
+  unsigned int readLen;
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_Read(part, NULL, 0, NULL, 0, NULL));
+
+  // Should fail to find variable
+  EXPECT_EQ(DRV_ERR_INVALID_PARAMETER,
+            HMX_NVRAM_Read(part, name, 0, read, sizeof(read), NULL));
+
+  // Add val to dlist and make sure Read gets them correct
+  EXPECT_EQ(DRV_OK, drv_NVRAM_SetEnv(part, name, val, valLen));
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_Read(part, name, 0,
+                                   read, sizeof(read), &readLen));
+  EXPECT_EQ(readLen, valLen);
+  EXPECT_EQ(0, memcmp((char*)read, (char*)val, valLen));
+}
+
+TEST_P(HnvramVariableTest, TestRemove) {
+  // Nothing to delete
+  EXPECT_EQ(DRV_ERR, HMX_NVRAM_Remove(part, name));
+
+  // Write to file and make sure its removed
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_Write(part, name, 0, val, valLen));
+
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_Remove(part, name));
+  EXPECT_EQ(0, dlist_size(part));
+  // Remove operation doesn't 0 entire partition, only needs to clear start byte
+  // If LoadbyAddr doesn't add anything to dlist, partition effectively empty
+  int size;
+  if (part == HMX_NVRAM_PARTITION_RO) {
+      size = NVRAM_RO_SIZE;
+  } else {
+      size = NVRAM_RW_SIZE;
+  }
+  EXPECT_EQ(DRV_OK, drv_NVRAM_LoadByAddress(part, offset, size));
+  EXPECT_EQ(0, dlist_size(part));
+}
+
+INSTANTIATE_TEST_CASE_P(TryBothFields, HnvramFieldTest,
+                        ::testing::ValuesIn(field_tuples));
+
+INSTANTIATE_TEST_CASE_P(TryAllPartitions, HnvramVariableTest,
+                        ::testing::ValuesIn(var_tuples));
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ::testing::AddGlobalTestEnvironment(new HnvramEnvironment);
+  return RUN_ALL_TESTS();
+}