Merge "hnvram: allow adding variables in RO partition"
diff --git a/hnvram/hnvram_main.c b/hnvram/hnvram_main.c
index 4fbbf2f..a406caa 100644
--- a/hnvram/hnvram_main.c
+++ b/hnvram/hnvram_main.c
@@ -33,13 +33,14 @@
 }
 
 void usage(const char* progname) {
-  printf("Usage: %s [-d | [-q|-b] [-r|-k] VARNAME] [ [-n] -w VARNAME=value]\n", progname);
+  printf("Usage: %s [-d | [-q|-b] [-r|-k] VARNAME] [ [-n [-p [RO|RW]]] -w VARNAME=value]\n", progname);
   printf("\t-d : dump all NVRAM variables\n");
   printf("\t-r VARNAME : read VARNAME from NVRAM\n");
   printf("\t-q : quiet mode, suppress the variable name and equal sign\n");
   printf("\t-b : read VARNAME from NVRAM in raw binary format, e.g. dumping a binary key\n");
   printf("\t-w VARNAME=value : write value to VARNAME in NVRAM.\n");
   printf("\t-n : toggles whether -w can create new variables. Default is off\n");
+  printf("\t-p [RW|RO] : toggles what partition new writes (-n) used. Default is RW\n");
   printf("\t-k VARNAME : delete existing key/value pair from NVRAM.\n");
 }
 
@@ -190,7 +191,14 @@
   return (int)ret;
 }
 
-char* read_nvram(const char* name, char* output, int outlen, int quiet) {
+// name - name of key to be read
+// output - buffer for value of key
+// outlen - length of buffer
+// quiet - whether buffer is KEY=VAL or VAL
+// part_used - in the case of dynamically added variables (is_field = false),
+//     returns what partition we found the key in
+char* read_nvram(const char* name, char* output, int outlen, int quiet,
+                 HMX_NVRAM_PARTITION_E* part_used) {
   const hnvram_field_t* field = get_nvram_field(name);
   int is_field = (field != NULL);
 
@@ -205,10 +213,18 @@
     }
   } else {
     format_type = HNVRAM_STRING;
-    DRV_Error e = HMX_NVRAM_Read(HMX_NVRAM_PARTITION_RW, (unsigned char*)name,
-                                 0, data, sizeof(data), &data_len);
+
+    // Try both partitions
+    *part_used = HMX_NVRAM_PARTITION_RW;
+    DRV_Error e = HMX_NVRAM_Read(*part_used, (unsigned char*)name, 0, data,
+                                 sizeof(data), &data_len);
     if (e != DRV_OK) {
-      return NULL;
+      *part_used = HMX_NVRAM_PARTITION_RO;
+      e = HMX_NVRAM_Read(*part_used, (unsigned char*)name, 0, data,
+                         sizeof(data), &data_len);
+      if (e != DRV_OK) {
+        return NULL;
+      }
     }
   }
   char formatbuf[NVRAM_MAX_DATA * 2];
@@ -354,25 +370,24 @@
 }
 
 DRV_Error clear_nvram(char* optarg) {
-  DRV_Error e = HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_RW,
-                                 (unsigned char*)optarg);
-  if (e == DRV_ERR) {
-    // Avoid throwing error message if variable already cleared
+  DRV_Error err1 = HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_RW,
+                                    (unsigned char*)optarg);
+  DRV_Error err2 = HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_RO,
+                                    (unsigned char*)optarg);
+
+  // Avoid throwing error message if variable already cleared
+  if ((err1 == DRV_ERR || err1 == DRV_OK) &&
+      (err2 == DRV_ERR || err2 == DRV_OK)) {
     return DRV_OK;
   }
-  return e;
+
+  fprintf(stderr, "Error while deleting key %s. RW: %d RO: %d.\n", optarg,
+          err1, err2);
+  return DRV_ERR;
 }
 
-int write_nvram(char* optarg) {
-  char* equal = strchr(optarg, '=');
-  if (equal == NULL) {
-    return -1;
-  }
 
-  char* name = optarg;
-  *equal = '\0';
-  char* value = ++equal;
-
+int write_nvram(char* name, char* value, HMX_NVRAM_PARTITION_E desired_part) {
   const hnvram_field_t* field = get_nvram_field(name);
   int is_field = (field != NULL);
 
@@ -397,22 +412,61 @@
 
   if (!is_field) {
     char tmp[NVRAM_MAX_DATA] = {0};
-    int key_exists = (read_nvram(name, tmp, NVRAM_MAX_DATA, 1) != NULL);
-    if (!can_add_flag && !key_exists) {
-      fprintf(stderr, "Key not found in NVRAM. Add -n to allow creation %s\n",
-              name);
-      return -3;
+    HMX_NVRAM_PARTITION_E part_used;
+    if (read_nvram(name, tmp, NVRAM_MAX_DATA, 1, &part_used) == NULL) {
+      return -3; // Write failed: Variable not found
     }
-    DRV_Error er = HMX_NVRAM_Write(HMX_NVRAM_PARTITION_RW, (unsigned char*)name,
-                                   0, nvram_value, nvram_len);
-    if (er != DRV_OK) {
+
+    if (desired_part != HMX_NVRAM_PARTITION_UNSPECIFIED &&
+        desired_part != part_used) {
+      fprintf(stderr, "Variable already exists in other partition: %s\n", name);
       return -4;
     }
-  } else {
-    if (HMX_NVRAM_SetField(field->nvram_type, 0,
-                           nvram_value, nvram_len) != DRV_OK) {
+
+    DRV_Error er = HMX_NVRAM_Write(part_used, (unsigned char*)name, 0,
+                                   nvram_value, nvram_len);
+    if (er != DRV_OK) {
       return -5;
     }
+  } else {
+    if (desired_part != HMX_NVRAM_PARTITION_UNSPECIFIED) {
+      fprintf(stderr, "Partition was specified (%d) on a field variable: %s\n",
+              desired_part, name);
+      return -6;
+    }
+    if (HMX_NVRAM_SetField(field->nvram_type, 0,
+                           nvram_value, nvram_len) != DRV_OK) {
+      return -7;
+    }
+  }
+
+  return 0;
+}
+
+// Adds new variable to HNVRAM in desired_partition as STRING
+int write_nvram_new(char* name, char* value,
+                    HMX_NVRAM_PARTITION_E desired_part) {
+  char tmp[NVRAM_MAX_DATA] = {0};
+  unsigned char nvram_value[NVRAM_MAX_DATA];
+  unsigned int nvram_len = sizeof(nvram_value);
+  if (parse_nvram(HNVRAM_STRING, value, nvram_value, &nvram_len) == NULL) {
+    return -1;
+  }
+
+  if (!can_add_flag) {
+    fprintf(stderr, "Key not found in NVRAM. Add -n to allow creation %s\n",
+            name);
+    return -2;
+  }
+
+  if (desired_part == HMX_NVRAM_PARTITION_UNSPECIFIED) {
+    desired_part = HMX_NVRAM_PARTITION_RW;
+  }
+
+  DRV_Error er = HMX_NVRAM_Write(desired_part, (unsigned char*)name, 0,
+                                 nvram_value, nvram_len);
+  if (er != DRV_OK) {
+    return -3;
   }
 
   return 0;
@@ -432,9 +486,11 @@
   int op_cnt = 0;  // operation
   int q_flag = 0;  // quiet: don't output name of variable.
   int b_flag = 0;  // binary: output the binary format
+  // Desired partition for new writes.
+  HMX_NVRAM_PARTITION_E desired_part = HMX_NVRAM_PARTITION_UNSPECIFIED;
   char output[NVRAM_MAX_DATA];
   int c;
-  while ((c = getopt(argc, argv, "dbqrnw:k:")) != -1) {
+  while ((c = getopt(argc, argv, "dbqrnp:w:k:")) != -1) {
     switch(c) {
       case 'b':
         b_flag = 1;
@@ -445,10 +501,35 @@
       case 'n':
         can_add_flag = 1;
         break;
+      case 'p':
+        if (strcmp(optarg, "RO") == 0) {
+          desired_part = HMX_NVRAM_PARTITION_RO;
+        } else if (strcmp(optarg, "RW") == 0) {
+          desired_part = HMX_NVRAM_PARTITION_RW;
+        } else {
+          fprintf(stderr, "Invalid partition: %s. Use RW or RO\n", optarg);
+          exit(1);
+        }
+        break;
       case 'w':
         {
           char* duparg = strdup(optarg);
-          if (write_nvram(duparg) != 0) {
+          char* equal = strchr(duparg, '=');
+          if (equal == NULL) {
+            return -1;
+          }
+
+          char* name = duparg;
+          *equal = '\0';
+          char* value = equal + 1;
+
+          int ret = write_nvram(name, value, desired_part);
+          if (ret == -3 && can_add_flag) {
+            // key not found, and we are authorized to add a new one
+            ret = write_nvram_new(name, value, desired_part);
+          }
+
+          if (ret != 0) {
             fprintf(stderr, "Unable to write %s\n", duparg);
             free(duparg);
             exit(1);
@@ -510,7 +591,8 @@
           }
           fwrite(output, 1, len, stdout);
         } else {
-          if (read_nvram(argv[optind], output, sizeof(output), q_flag) == NULL) {
+          HMX_NVRAM_PARTITION_E part_used;
+          if (read_nvram(argv[optind], output, sizeof(output), q_flag, &part_used) == NULL) {
             fprintf(stderr, "Unable to read %s\n", argv[optind]);
             exit(1);
           }
diff --git a/hnvram/hnvram_test.cc b/hnvram/hnvram_test.cc
index 39f7e4a..643a436 100644
--- a/hnvram/hnvram_test.cc
+++ b/hnvram/hnvram_test.cc
@@ -8,16 +8,31 @@
 
 int libupgrade_verbose = 1;
 
-char* HMX_NVRAM_Read_Data = NULL;
+char* HMX_NVRAM_Read_Data_RO = NULL;
+char* HMX_NVRAM_Read_Data_RW = NULL;
+
+char* get_Read_Data(HMX_NVRAM_PARTITION_E partition) {
+  if (partition == HMX_NVRAM_PARTITION_RO) {
+    return HMX_NVRAM_Read_Data_RO;
+  } else {
+    return HMX_NVRAM_Read_Data_RW;
+  }
+}
+
 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) {
-  if (HMX_NVRAM_Read_Data == NULL) {
+  if (get_Read_Data(partition) == NULL) {
     return DRV_ERR;
+  }
+  if (partition == HMX_NVRAM_PARTITION_RO) {
+    snprintf((char*)pValue, ulSize, "%s", HMX_NVRAM_Read_Data_RO);
+    *pLen = strlen(HMX_NVRAM_Read_Data_RO);
+    return DRV_OK;
   } else {
-    snprintf((char*)pValue, ulSize, "%s", HMX_NVRAM_Read_Data);
-    *pLen = strlen(HMX_NVRAM_Read_Data);
+    snprintf((char*)pValue, ulSize, "%s", HMX_NVRAM_Read_Data_RW);
+    *pLen = strlen(HMX_NVRAM_Read_Data_RW);
     return DRV_OK;
   }
 }
@@ -25,17 +40,26 @@
 DRV_Error HMX_NVRAM_Write(HMX_NVRAM_PARTITION_E partition,
                          unsigned char* pName, unsigned int offset,
                          unsigned char* pValue, unsigned int ulSize) {
-  HMX_NVRAM_Read_Data = (char*)malloc(ulSize);
-  snprintf(HMX_NVRAM_Read_Data, sizeof(pValue), "%s", (char*)pValue);
+  if (partition == HMX_NVRAM_PARTITION_RO) {
+    HMX_NVRAM_Read_Data_RO = (char*)malloc(ulSize);
+    snprintf(HMX_NVRAM_Read_Data_RO, sizeof(pValue), "%s", (char*)pValue);
+  } else {
+    HMX_NVRAM_Read_Data_RW = (char*)malloc(ulSize);
+    snprintf(HMX_NVRAM_Read_Data_RW, sizeof(pValue), "%s", (char*)pValue);
+  }
   return DRV_OK;
 }
 
 DRV_Error HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_E partition,
                            unsigned char* pName) {
-  if (HMX_NVRAM_Read_Data == NULL) {
+  if (get_Read_Data(partition) == NULL) {
     return DRV_ERR;
   }
-  HMX_NVRAM_Read_Data = NULL;
+  if (partition == HMX_NVRAM_PARTITION_RO) {
+    HMX_NVRAM_Read_Data_RO = NULL;
+  } else {
+    HMX_NVRAM_Read_Data_RW = NULL;
+  }
   return DRV_OK;
 }
 
@@ -84,7 +108,8 @@
     virtual ~HnvramTest() {}
 
     virtual void SetUp() {
-      HMX_NVRAM_Read_Data = NULL;
+      HMX_NVRAM_Read_Data_RO = NULL;
+      HMX_NVRAM_Read_Data_RW = NULL;
       HMX_NVRAM_GetField_Data = NULL;
       HMX_NVRAM_SetField_Data = NULL;
       HMX_NVRAM_SetField_Len = -1;
@@ -184,104 +209,145 @@
 
 TEST_F(HnvramTest, TestReadFieldNvram) {
   char output[256];
+  HMX_NVRAM_PARTITION_E part;
   HMX_NVRAM_GetField_Data = "TestSystemId";
   EXPECT_STREQ("SYSTEM_ID=TestSystemId",
-               read_nvram("SYSTEM_ID", output, sizeof(output), 0));
+               read_nvram("SYSTEM_ID", output, sizeof(output), 0, &part));
   EXPECT_STREQ("TestSystemId",
-               read_nvram("SYSTEM_ID", output, sizeof(output), 1));
+               read_nvram("SYSTEM_ID", output, sizeof(output), 1, &part));
   HMX_NVRAM_GetField_Data = NULL;
-  EXPECT_EQ(NULL, read_nvram("FAKE_SYSTEM_ID", output, sizeof(output), 1));
+  EXPECT_EQ(NULL, read_nvram("FAKE_SYSTEM_ID", output, sizeof(output), 1,
+                             &part));
 }
 
 TEST_F(HnvramTest, TestReadVariableNvram) {
   char output[256];
-  HMX_NVRAM_Read_Data = strdup("ABC123");
+  HMX_NVRAM_PARTITION_E part;
+  HMX_NVRAM_Read_Data_RW = strdup("ABC123");
   EXPECT_STREQ("TEST_VARIABLE=ABC123",
-               read_nvram("TEST_VARIABLE", output, sizeof(output), 0));
+               read_nvram("TEST_VARIABLE", output, sizeof(output), 0, &part));
+  EXPECT_EQ((int)HMX_NVRAM_PARTITION_RW, part);
   EXPECT_STREQ("ABC123",
-               read_nvram("TEST_VARIABLE", output, sizeof(output), 1));
-  HMX_NVRAM_Read_Data = NULL;
-  EXPECT_STREQ(NULL, read_nvram("TEST_VARIABLE", output, sizeof(output), 1));
+               read_nvram("TEST_VARIABLE", output, sizeof(output), 1, &part));
+  EXPECT_EQ((int)HMX_NVRAM_PARTITION_RW, part);
+  HMX_NVRAM_Read_Data_RW = NULL;
+  EXPECT_STREQ(NULL, read_nvram("TEST_VARIABLE", output, sizeof(output), 1,
+                                &part));
 }
 
 TEST_F(HnvramTest, TestWriteFieldNvram) {
   // Type integer
-  char* testdata = strdup("ACTIVATED_KERNEL_NUM=1");
-  EXPECT_EQ(DRV_OK, write_nvram(testdata));
+  char* key = strdup("ACTIVATED_KERNEL_NUM");
+  char* val = strdup("1");
+  EXPECT_EQ(DRV_OK, write_nvram(key, val, HMX_NVRAM_PARTITION_UNSPECIFIED));
   EXPECT_EQ(0x01, *HMX_NVRAM_SetField_Data);
   EXPECT_EQ(1, HMX_NVRAM_SetField_Len);
 
   // Type string
-  testdata = strdup("ACTIVATED_KERNEL_NAME=kernel1");
-  EXPECT_EQ(DRV_OK, write_nvram(testdata));
+  key = strdup("ACTIVATED_KERNEL_NAME");
+  val = strdup("kernel1");
+  EXPECT_EQ(DRV_OK, write_nvram(key, val, HMX_NVRAM_PARTITION_UNSPECIFIED));
   EXPECT_STREQ("kernel1", (char*)HMX_NVRAM_SetField_Data);
   EXPECT_EQ(7, HMX_NVRAM_SetField_Len);
 
   // Make sure it called SetField and not HMX_NVRAM_Write
-  EXPECT_EQ (NULL, HMX_NVRAM_Read_Data);
+  EXPECT_EQ (NULL, HMX_NVRAM_Read_Data_RW);
+  EXPECT_EQ (NULL, HMX_NVRAM_Read_Data_RO);
 
   // Should fail trying to change value of non-exsting field
-  testdata = strdup("FAKE_FIELD=abc123");
-  EXPECT_NE(0, write_nvram(testdata));
-  free(testdata);
+  key = strdup("FAKE_FIELD");
+  val = strdup("abc123");
+  EXPECT_NE(0, write_nvram(key, val, HMX_NVRAM_PARTITION_UNSPECIFIED));
+  free(key);
+  free(val);
 }
 
-TEST_F(HnvramTest, TestWriteVariableNvram) {
+void testWriteVariableNvram(HMX_NVRAM_PARTITION_E partition, HMX_NVRAM_PARTITION_E other) {
   char* key = strdup("TEST_FIELD");
   char* val = strdup("abc123");
-  char* keyval = strdup("TEST_FIELD=abc123");
 
   // Fail to add new one without -n
-  EXPECT_NE(0, write_nvram(strdup(keyval)));
+  EXPECT_NE(0, write_nvram(key, val, HMX_NVRAM_PARTITION_UNSPECIFIED));
+  EXPECT_NE(0, write_nvram(key, val, HMX_NVRAM_PARTITION_RW));
+  EXPECT_NE(0, write_nvram(key, val, HMX_NVRAM_PARTITION_RO));
 
-  // Add new one successfully
   can_add_flag = 1;
-  EXPECT_EQ(0, write_nvram(keyval));
-  EXPECT_STREQ(val,HMX_NVRAM_Read_Data);
+  EXPECT_EQ(-3, write_nvram(key, val, HMX_NVRAM_PARTITION_UNSPECIFIED));
+  EXPECT_EQ(-3, write_nvram(key, val, HMX_NVRAM_PARTITION_RO));
+  EXPECT_EQ(-3, write_nvram(key, val, HMX_NVRAM_PARTITION_RW));
+  // Add new one successfully
+  EXPECT_EQ(0, write_nvram_new(key, val, partition));
+  EXPECT_STREQ(val, get_Read_Data(partition));
 
   // Should be able to read value
   char output[256];
-  EXPECT_STREQ(val, read_nvram(key, output, sizeof(output), 1));
+  HMX_NVRAM_PARTITION_E part_used;
+  EXPECT_STREQ(val, read_nvram(key, output, sizeof(output), 1, &part_used));
+
+  // Make sure read came from right partition
+  EXPECT_EQ(partition, part_used);
 
   char* val2 = strdup("987def");
-  char* keyval2 = strdup("TEST_FIELD=987def");
 
   // Should be able to change value
-  EXPECT_EQ(0, write_nvram(keyval2));
-  EXPECT_STREQ(val2,HMX_NVRAM_Read_Data);
+  EXPECT_EQ(0, write_nvram(key, val2, HMX_NVRAM_PARTITION_UNSPECIFIED));
+  EXPECT_STREQ(val2, get_Read_Data(partition));
+
+  // And back again, this time with correct partition specified
+  EXPECT_EQ(0, write_nvram(key, val, partition));
+  EXPECT_STREQ(val, get_Read_Data(partition));
+
+  // Should fail when specifying wrong partition
+  EXPECT_EQ(-4, write_nvram(key, val2, other));
+  EXPECT_EQ(-4, write_nvram(key, val2, HMX_NVRAM_PARTITION_W_RAWFS));
 
   free(key);
   free(val);
-  free(keyval);
   free(val2);
-  free(keyval2);
 }
 
-TEST_F(HnvramTest, TestClearNvram) {
+TEST_F(HnvramTest, TestWriteVariableNvramRO) {
+  testWriteVariableNvram(HMX_NVRAM_PARTITION_RO, HMX_NVRAM_PARTITION_RW);
+}
+
+TEST_F(HnvramTest, TestWriteVariableNvramRW) {
+  testWriteVariableNvram(HMX_NVRAM_PARTITION_RW, HMX_NVRAM_PARTITION_RO);
+}
+
+void testClearNvram(HMX_NVRAM_PARTITION_E partition) {
   char* key = strdup("TEST_FIELD2");
   char* val = strdup("abc123");
-  char* keyval = strdup("TEST_FIELD2=abc123");
   // No error if variable already cleared
   EXPECT_EQ(DRV_OK, clear_nvram(key));
 
-  // Create new one
+  // Create new var
   can_add_flag = 1;
-  EXPECT_EQ(0, write_nvram(keyval));
-  EXPECT_STREQ(val, HMX_NVRAM_Read_Data);
+  EXPECT_EQ(-3, write_nvram(key, val, HMX_NVRAM_PARTITION_UNSPECIFIED));
+  EXPECT_EQ(0, write_nvram_new(key, val, partition));
+  EXPECT_STREQ(val, get_Read_Data(partition));
 
   // Should be able to read value
   char output[256];
-  EXPECT_STREQ(val, read_nvram(key, output, sizeof(output), 1));
+  HMX_NVRAM_PARTITION_E part_used;
+  EXPECT_STREQ(val, read_nvram(key, output, sizeof(output), 1, &part_used));
+  EXPECT_EQ((int)partition, part_used);
 
   // Should be able to kill it
   EXPECT_EQ(DRV_OK, clear_nvram(key));
 
   // Should fail reading value
-  EXPECT_STREQ(NULL, read_nvram(key, output, sizeof(output), 1));
+  EXPECT_STREQ(NULL, read_nvram(key, output, sizeof(output), 1, &part_used));
 
   free(key);
   free(val);
-  free(keyval);
+}
+
+TEST_F(HnvramTest, TestClearNvramRO) {
+  testClearNvram(HMX_NVRAM_PARTITION_RO);
+}
+
+TEST_F(HnvramTest, TestClearNvramRW) {
+  testClearNvram(HMX_NVRAM_PARTITION_RW);
 }
 
 int main(int argc, char** argv) {