Add system variable management in u-boot

Change-Id: I862eec11795940fc3e516eb63654708ab333fb1f
diff --git a/common/cmd_sysvar.c b/common/cmd_sysvar.c
new file mode 100755
index 0000000..a5dac7f
--- /dev/null
+++ b/common/cmd_sysvar.c
@@ -0,0 +1,711 @@
+/* Copyright 2012 Google Inc. All Rights Reserved.
+ * Author: weixiaofeng@google.com (Xiaofeng Wei)
+ */
+
+#include <common.h>
+#include <spi_flash.h>
+#include <asm/io.h>
+
+#include "sysvar.h"
+
+#define PAGE_SIZE           256
+#define SYSVAR_VALUE        256
+
+#define SYSVAR_RO_MEM       0x00000100
+#define SYSVAR_RW_MEM       SYSVAR_RO_MEM + SYSVAR_BLOCK_SIZE
+
+struct spi_flash *sf_dev = NULL;
+struct sysvar_buf ro_buf;
+struct sysvar_buf rw_buf;
+static const long sysvar_offset[SYSVAR_SPI_BLOCK] = {
+  SYSVAR_RW_OFFSET0, SYSVAR_RW_OFFSET1, SYSVAR_RO_OFFSET0, SYSVAR_RO_OFFSET1
+};
+
+static int data_recovery(struct sysvar_buf *buf, int idx);
+static int data_load(struct sysvar_buf *buf, int idx);
+static int data_save(struct sysvar_buf *buf, int *idx);
+
+static int open_sf(bool load);
+static void close_sf(void);
+
+static int loadvar(void);
+static int savevar(struct sysvar_buf *buf, int idx);
+static int getvar(char *name, char *value, int len);
+static int setvar(struct sysvar_buf *buf, int idx, char *name, char *value);
+
+/*
+ * print_msg - print the message string
+ */
+static void print_msg(char *msg, int idx) {
+  if (idx < 0) {
+    printf("SV: %s\n", msg);
+  } else {
+    printf("SV: System variables(%s) has been %s\n",
+          (idx < SYSVAR_RO_BUF) ? "RW" : "RO", msg);
+  }
+}
+
+/*
+ * print_err - print the error message
+ */
+static void print_err(char *err, int idx) {
+  if (idx < 0) {
+    printf("## Error: %s\n", err);
+  } else {
+    printf("## Error: failed to %s system variables(%s)\n",
+           err, (idx < SYSVAR_RO_BUF) ? "RW" : "RO");
+  }
+}
+
+/*
+ * data_recovery - system variables recovering routine
+ */
+static int data_recovery(struct sysvar_buf *buf, int idx) {
+  int i, j, ret;
+
+  /* load the system variables */
+  printf("SV: Recovering data");
+  for (i = idx, j = idx + 1; i < idx + 2; i++, j--) {
+    /* read the data from SPI flash */
+    if (spi_flash_read(sf_dev, sysvar_offset[i], buf->data_len, buf->data))
+      continue;
+
+    /* check crc32 and wc32 (write count) */
+    if (check_var(buf, SYSVAR_LOAD_MODE) == SYSVAR_SUCCESS) {
+      /* erase SPI flash */
+      ret = spi_flash_erase(sf_dev, sysvar_offset[j], buf->data_len);
+      if (ret) {
+        print_err("erase", j);
+        goto recovery_err;
+      }
+
+      /* check crc32 and wc32 (write count) */
+      if (check_var(buf, SYSVAR_SAVE_MODE))
+        goto recovery_err;
+
+      /* write system variables(RW) to SPI flash */
+      ret = spi_flash_write(sf_dev, sysvar_offset[j],
+                            buf->data_len, buf->data);
+      printf("\n");
+      if (ret) {
+        print_err("write", j);
+        goto recovery_err;
+      }
+
+      buf->loaded = true;
+      print_msg("Data recovery was completed", SYSVAR_MESSAGE);
+      return 0;
+    }
+  }
+
+recovery_err:
+  clear_buf(buf);
+
+  printf("\n");
+  print_err("recovery", idx);
+  return 0;
+}
+
+/*
+ * data_load - load the data from SPI flash to data buffer
+ */
+static int data_load(struct sysvar_buf *buf, int idx) {
+  int i, j;
+
+  /* load the system variables */
+  for (i = idx, j = 0; i < idx + 2; i++, j++) {
+    buf->failed[j] = false;
+
+    /* read the data from SPI flash */
+    if (spi_flash_read(sf_dev, sysvar_offset[i], buf->data_len, buf->data))
+      buf->failed[j] = true;
+
+    /* check crc32 and wc32 (write count) */
+    if (check_var(buf, SYSVAR_LOAD_MODE))
+      buf->failed[j] = true;
+  }
+
+load_err:
+  if (buf->failed[0] || buf->failed[1])
+    return data_recovery(buf, idx);
+  return 0;
+}
+
+/*
+ * data_save - save the data from data buffer to SPI flash
+ */
+static int data_save(struct sysvar_buf *buf, int *idx) {
+  int i, j, ret;
+
+  /* save the system variables */
+  for (j = 0; j < 2; j++) {
+    i = idx[j];
+    printf("SV: Erasing SPI flash 0x%08lx - 0x%08lx\n",
+           sysvar_offset[i], sysvar_offset[i] + buf->data_len);
+    ret = spi_flash_erase(sf_dev, sysvar_offset[i], buf->data_len);
+    if (ret) {
+      print_err("erase", i);
+      return 1;
+    }
+
+    /* check crc32 and wc32 (write count) */
+    if (check_var(buf, SYSVAR_SAVE_MODE)) {
+      print_err("save", SYSVAR_RO_BUF);
+      return 1;
+    }
+
+    /* write system variables to SPI flash */
+    printf("SV: Writing to SPI flash");
+    ret = spi_flash_write(sf_dev, sysvar_offset[i], buf->data_len, buf->data);
+    printf("\n");
+    if (ret) {
+      print_err("write", i);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/*
+ * open_sf - open SPI flash and read the data to buffer
+ */
+static int open_sf(bool load) {
+  /* check SPI flash */
+  if (sf_dev != NULL)
+    return 0;
+
+  memset(&rw_buf, 0, sizeof(rw_buf));
+  memset(&ro_buf, 0, sizeof(ro_buf));
+
+  /* probe SPI flash */
+  sf_dev = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
+                           CONFIG_ENV_SPI_MAX_HZ, SPI_MODE_3);
+  if (sf_dev == NULL) {
+    print_err("failed to initialize SPI flash", SYSVAR_MESSAGE);
+    return 1;
+  }
+
+  /* allocate data buffers */
+  rw_buf.data = (uchar *)map_physmem(SYSVAR_RW_MEM,
+                                     SYSVAR_BLOCK_SIZE, MAP_WRBACK);
+  ro_buf.data = (uchar *)map_physmem(SYSVAR_RO_MEM,
+                                     SYSVAR_BLOCK_SIZE, MAP_WRBACK);
+  if (!rw_buf.data || !ro_buf.data) {
+    print_err("failed to map physical memory", SYSVAR_MESSAGE);
+    goto open_err;
+  }
+
+  /* allocate data lists */
+  rw_buf.list = (struct sysvar_list *)malloc(sizeof(struct sysvar_list));
+  ro_buf.list = (struct sysvar_list *)malloc(sizeof(struct sysvar_list));
+  if (!rw_buf.list || !ro_buf.list) {
+    print_err("failed to allocate data list", SYSVAR_MESSAGE);
+    goto open_err;
+  }
+
+  rw_buf.data_len = SYSVAR_BLOCK_SIZE;
+  rw_buf.total_len = SYSVAR_BLOCK_SIZE - SYSVAR_HEAD;
+  rw_buf.free_len = rw_buf.total_len;
+  rw_buf.readonly = false;
+
+  strncpy(rw_buf.list->name, "rw", SYSVAR_NAME);
+  rw_buf.list->value = NULL;
+  rw_buf.list->len = SYSVAR_NAME + 2;
+  rw_buf.list->next = NULL;
+
+  ro_buf.data_len = SYSVAR_BLOCK_SIZE;
+  ro_buf.total_len = SYSVAR_BLOCK_SIZE - SYSVAR_HEAD;
+  ro_buf.free_len = ro_buf.total_len;
+  ro_buf.readonly = true;
+
+  strncpy(rw_buf.list->name, "ro", SYSVAR_NAME);
+  ro_buf.list->value = NULL;
+  ro_buf.list->len = SYSVAR_NAME + 2;
+  ro_buf.list->next = NULL;
+
+  /* load data from SPI flash to data buffer */
+  if (load) {
+    if (loadvar())
+      goto open_err;
+  }
+  return 0;
+
+open_err:
+  close_sf();
+  return 1;
+}
+
+/*
+ * close_sf - close SPI flash and release the data buffer
+ */
+static void close_sf(void) {
+  /* release data lists */
+  if (rw_buf.list != NULL) {
+    clear_var(&rw_buf);
+    free(rw_buf.list);
+  }
+  if (ro_buf.list != NULL) {
+    clear_var(&ro_buf);
+    free(ro_buf.list);
+  }
+
+  /* release data buffers */
+  if (rw_buf.data != NULL)
+    unmap_physmem(rw_buf.data, rw_buf.data_len);
+  if (ro_buf.data != NULL)
+    unmap_physmem(ro_buf.data, ro_buf.data_len);
+
+  sf_dev = NULL;
+}
+
+/*
+ * loadvar - load the data from SPI flash to data buffer
+ */
+static int loadvar(void) {
+  if (data_load(&rw_buf, SYSVAR_RW_BUF))
+    return 1;
+
+  /* move the data from data buffer to data list */
+  if (load_var(&rw_buf))
+    return 1;
+
+  rw_buf.loaded = true;
+  print_msg("loaded", SYSVAR_RW_BUF);
+
+  if (data_load(&ro_buf, SYSVAR_RO_BUF))
+    return 1;
+
+  /* move the data from data buffer to data list */
+  if (load_var(&ro_buf))
+    return 1;
+
+  ro_buf.loaded = true;
+  print_msg("loaded", SYSVAR_RO_BUF);
+  return 0;
+}
+
+/*
+ * savevar - save the data from data buffer to SPI flash
+ */
+static int savevar(struct sysvar_buf *buf, int idx) {
+  int save_idx[2];
+
+  if (open_sf(true))
+    return 1;
+
+  /* move the data from data list to data buffer */
+  if (save_var(buf))
+    return 1;
+
+  /* erase failed partition first
+   *  part0   part1       erase
+   *  -----   -----       -----
+   *    ok      ok        0, 1
+   *  failed    ok        0, 1
+   *    ok    failed      1, 0
+   *  failed  failed      0, 1
+   */
+  if (buf->failed[1]) {
+    save_idx[0] = idx + 1;
+    save_idx[1] = idx;
+  } else {
+    save_idx[0] = idx;
+    save_idx[1] = idx + 1;
+  }
+
+  /* save the data from data buffer to SPI flash */
+  if (data_save(buf, save_idx))
+    return 1;
+
+  printf("\n");
+  print_msg("saved", idx);
+  return 0;
+}
+
+/*
+ * getvar - get or print the system variable from data list
+ */
+static int getvar(char *name, char *value, int len) {
+  struct sysvar_list *var = NULL;
+
+  if (open_sf(true))
+    return 1;
+
+  if (name == NULL) {
+    /* print all system variables(RO) */
+    print_var(&ro_buf);
+    /* print all system variables(RW) */
+    print_var(&rw_buf);
+    return 0;
+  }
+
+  /* find the system variable(RO) */
+  var = find_var(&ro_buf, name);
+  if (var != NULL)
+    goto get_data;
+
+  /* find the system variable(RW) */
+  var = find_var(&rw_buf, name);
+  if (var != NULL)
+    goto get_data;
+
+  /* system variable not found */
+  printf("## Error: '%s' not found\n", name);
+  return 1;
+
+get_data:
+  return get_var(var, name, value, len);
+}
+
+/*
+ * setvar - add or delete the system variable in data list
+ */
+static int setvar(struct sysvar_buf *buf, int idx, char *name, char *value) {
+  struct sysvar_list *var = NULL;
+  int ret = 0;
+
+  if (open_sf(true))
+    return 1;
+
+  if (name != NULL) {
+    if (idx < SYSVAR_RO_BUF) {
+      /* read only variable? */
+      var = find_var(&ro_buf, name);
+    } else {
+      /* variable existed? */
+      var = find_var(&rw_buf, name);
+    }
+
+    if (var != NULL) {
+      if (idx < SYSVAR_RO_BUF) {
+        printf("## Error: '%s' is read only variable\n", name);
+        return 1;
+      } else {
+        printf("## Error: '%s' is not read only variable\n", name);
+        return 1;
+      }
+    }
+
+    var = find_var(buf, name);
+    if (var != NULL) {
+      /* delete system variable */
+      ret = delete_var(buf, var);
+      if (ret != SYSVAR_SUCCESS) {
+        printf("## Error: failed to delete '%s'\n", name);
+        return 1;
+      }
+
+      /* add system variable */
+      if (value != NULL) {
+        ret = set_var(buf, name, value);
+        if (ret == 0)
+          print_msg("added", idx);
+      } else {
+        print_msg("deleted", idx);
+      }
+    } else {
+      /* add system variable */
+      if (value != NULL) {
+        ret = set_var(buf, name, value);
+        if (ret == 0)
+          print_msg("added", idx);
+      } else {
+        printf("## Error: '%s' not found\n", name);
+        ret = 1;
+      }
+    }
+  } else {
+    /* delete all of system variables */
+    ret = clear_var(buf);
+    if (ret == 0)
+      print_msg("deleted", idx);
+  }
+  return ret;
+}
+
+/*
+ * do_loadvar - load system variables from SPI flash
+ *
+ * loadvar command:
+ *    loadvar - load system variables from persistent storage
+ */
+static int do_loadvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
+  if (open_sf(false))
+    return 1;
+
+  /* load system variables from SPI flash */
+  return loadvar();
+}
+
+U_BOOT_CMD(
+  loadvar, 1, 0, do_loadvar,
+  "load system variables",
+  "\n    - load system variables from SPI flash\n"
+);
+
+/*
+ * do_savevar - save system variables(RW) to SPI flash
+ *
+ * savevar command:
+ *    savevar - save system variables(RW) to SPI flash
+ */
+static int do_savevar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+  /* save system variables(RW) */
+  return savevar(&rw_buf, SYSVAR_RW_BUF);
+}
+
+U_BOOT_CMD(
+  savevar, 1, 0, do_savevar,
+  "save system variables(RW)",
+  "\n    - save system variables(RW) to SPI flash\n"
+);
+
+/*
+ * do_printvar - print system variables
+ *
+ * printvar command:
+ *    printvar name - print system variable with name
+ *    printvar      - print all system variables
+ */
+static int do_printvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
+  char value[SYSVAR_VALUE];
+
+  if (open_sf(true))
+    return 1;
+
+  if (argv[1] == NULL) {
+    /* print all system variables */
+    getvar(NULL, NULL, 0);
+
+    printf("\nSV: System Variables(RO): %d/%d bytes\n",
+      ro_buf.used_len, ro_buf.total_len);
+    printf("SV: System Variables(RW): %d/%d bytes\n",
+      rw_buf.used_len, rw_buf.total_len);
+  } else {
+    /* get a system variable */
+    if (getvar(argv[1], value, SYSVAR_VALUE) == 0) {
+      printf("%s=%s\n", argv[1], value);
+      printf("\nSV: System Variable: %d bytes\n",
+        (int)(SYSVAR_NAME + 2 + strlen(value)));
+    }
+  }
+  return 0;
+}
+
+U_BOOT_CMD(
+  printvar, 2, 0, do_printvar,
+  "print system variables",
+  "\n    - print values of all system variables\n"
+  "printvar name ...\n"
+  "    - print value of system variable 'name'\n"
+);
+
+/*
+ * do_setvar - add or delete system variables(RW)
+ *
+ * setvar command:
+ *    setvar name value - add system variable with name:value
+ *    setvar name       - delete system variable with name
+ *    setvar            - delete all system variables
+ */
+static int do_setvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
+  int ret = 0;
+
+  if (argc == 3) {
+    /* add a system variable(RW) */
+    ret = setvar(&rw_buf, SYSVAR_RW_BUF, argv[1], argv[2]);
+  } else if (argc == 2) {
+    /* delete a system variable(RW) */
+    ret = setvar(&rw_buf, SYSVAR_RW_BUF, argv[1], NULL);
+  } else {
+    /* delete all system variables(RW) */
+    ret = setvar(&rw_buf, SYSVAR_RW_BUF, NULL, NULL);
+  }
+  return ret;
+}
+
+U_BOOT_CMD(
+  setvar, 3, 0, do_setvar,
+  "set system variables(RW)",
+  "name value ...\n"
+  "    - set system variable(RW) 'name' to 'value ...'\n"
+  "setvar name\n"
+  "    - delete system variable(RW) 'name'\n"
+  "setvar\n"
+  "    - delete all system variables(RW)\n"
+);
+
+/*
+ * sysvar_dump - dump the data buffer in binary/ascii format
+ */
+static void sysvar_dump(struct sysvar_buf *buf, int idx, bool load) {
+  extern char console_buffer[];
+  int start = 0;
+
+  if (open_sf(load))
+    return 1;
+
+  printf("System Variables(%s):\n", (idx < SYSVAR_RO_BUF) ? "RW" : "RO");
+  printf("offset : 0x%08lx\n", sysvar_offset[idx]);
+  printf("size   : %d bytes\n", buf->data_len);
+  printf("total  : %d bytes\n", buf->total_len);
+  printf("used   : %d bytes\n", buf->used_len);
+  printf("wc32   : 0x%08lx\n", get_wc32(buf));
+  printf("crc32  : 0x%08lx\n", get_crc32(buf));
+
+  while (1) {
+    /* dump one page data in data buffer */
+    dump_buf(buf, start, PAGE_SIZE);
+    /* continue to dump...? */
+    readline("(n)ext, (p)rev, (f)irst, (l)ast ? >> ");
+    if (strcmp(console_buffer, "n") == 0) {
+      start += PAGE_SIZE;    /* go to next page */
+      if (start >= buf->data_len)
+        return;
+    } else if (strcmp(console_buffer, "p") == 0) {
+      start -= PAGE_SIZE;    /* go to previous page */
+      if (start < 0)
+        return;
+    } else if (strcmp(console_buffer, "f") == 0) {
+      if (start == 0)
+        return;
+      start = 0;  /* go to first page */
+    } else if (strcmp(console_buffer, "l") == 0) {
+      if (start == buf->data_len - PAGE_SIZE)
+        return;
+      start = buf->data_len - PAGE_SIZE;  /* go to last page */
+    } else {
+      return;
+    }
+  }
+}
+
+/*
+ * sysvar_io - SPI flash IO operations
+ */
+static int sysvar_io(int argc, char *argv[]) {
+  struct sysvar_buf *buf;
+  int i, idx, ret = 0;
+
+  if (open_sf(false))
+    return 1;
+
+  if (strcmp(argv[1], "0") == 0) {
+    idx = 0;
+    buf = &rw_buf;
+  } else if (strcmp(argv[1], "1") == 0) {
+    idx = 1;
+    buf = &rw_buf;
+  } else if (strcmp(argv[1], "2") == 0) {
+    idx = 2;
+    buf = &ro_buf;
+  } else if (strcmp(argv[1], "3") == 0) {
+    idx = 3;
+    buf = &ro_buf;
+  } else {
+    print_err("invalid SPI flash device", SYSVAR_MESSAGE);
+    return 1;
+  }
+
+  if (strcmp(argv[0], "write") == 0) {
+    /* fill data to data buffer */
+    for (i = 0; i < buf->data_len; i++)
+      buf->data[i] = i;
+
+    /* write the data buffer to spi_flash */
+    ret = spi_flash_write(sf_dev, sysvar_offset[idx],
+                          buf->data_len, buf->data);
+    printf("\n");
+  } else if (strcmp(argv[0], "erase") == 0) {
+    /* erase spi_flash */
+    ret = spi_flash_erase(sf_dev, sysvar_offset[idx], buf->data_len);
+  }
+
+  if (ret == 0) {
+    ret = spi_flash_read(sf_dev, sysvar_offset[idx],
+                         buf->data_len, buf->data);
+    if (ret == 0)
+      sysvar_dump(buf, idx, false);
+  }
+
+  if (ret != 0)
+    printf("## Error: SPI flash %s failed at 0x%08lx\n",
+           argv[0], sysvar_offset[idx]);
+
+  rw_buf.loaded = false;
+  ro_buf.loaded = false;
+  return ret;
+}
+
+/*
+ * do_sysvar - system variable debug functions
+ */
+static int do_sysvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
+  if (argc < 2)
+    goto usage;
+
+  if (strcmp(argv[1], "set") == 0) {
+    if (argc == 4) {
+      /* add a system variable(RO) */
+      return setvar(&ro_buf, SYSVAR_RO_BUF, argv[2], argv[3]);
+    } else if (argc == 3) {
+      /* delete a system variable(RO) */
+      return setvar(&ro_buf, SYSVAR_RO_BUF, argv[2], NULL);
+    } else if (argc == 2) {
+      /* delete all system variables(RO) */
+      return setvar(&ro_buf, SYSVAR_RO_BUF, NULL, NULL);
+    } else {
+      goto usage;
+    }
+  }
+
+  if (strcmp(argv[1], "save") == 0 && argc == 2) {
+    /* save system variables(RO) */
+    return savevar(&ro_buf, SYSVAR_RO_BUF);
+  }
+
+  if (strcmp(argv[1], "dump") == 0 && argc == 3) {
+    if (strcmp(argv[2], "rw") == 0) {
+      /* dump data in data buffer(RW) */
+      sysvar_dump(&rw_buf, SYSVAR_RW_BUF, true);
+    } else if (strcmp(argv[2], "ro") == 0) {
+      /* dump data in data buffer(RO) */
+      sysvar_dump(&ro_buf, SYSVAR_RO_BUF, true);
+    } else {
+      goto usage;
+    }
+    return 0;
+  }
+
+  if ((strcmp(argv[1], "read") == 0 && argc == 3) ||
+      (strcmp(argv[1], "write") == 0 && argc == 3) ||
+      (strcmp(argv[1], "erase") == 0 && argc == 3))
+    return sysvar_io(argc - 1, argv + 1);
+
+usage:
+  cmd_usage(cmdtp);
+  return 1;
+}
+
+U_BOOT_CMD(
+  sysvar, 4, 0, do_sysvar,
+  "system variable debug functions",
+  "set name value\n"
+  "    - set system variable(RO) 'name' to 'value ...'\n"
+  "sysvar set name\n"
+  "    - delete system variable(RO) 'name'\n"
+  "sysvar set\n"
+  "    - delete all system variables(RO)\n"
+  "sysvar save\n"
+  "    - save system variables(RO) to SPI flash\n"
+  "sysvar dump rw|ro\n"
+  "    - dump data in data buffer\n"
+  "sysvar read 0|1|2|3\n"
+  "    - read data from SPI flash 0|1|2|3 to data buffer\n"
+  "sysvar write 0|1|2|3\n"
+  "    - write data from data buffer to SPI flash 0|1|2|3\n"
+  "sysvar erase 0|1|2|3\n"
+  "    - erase data on SPI flash 0|1|2|3\n"
+);
+
diff --git a/common/sysvar.c b/common/sysvar.c
new file mode 100755
index 0000000..55cd162
--- /dev/null
+++ b/common/sysvar.c
@@ -0,0 +1,475 @@
+/* Copyright 2012 Google Inc. All Rights Reserved.
+ * Author: weixiaofeng@google.com (Xiaofeng Wei)
+ */
+
+#include "sysvar.h"
+
+static const unsigned long crc_table[256] = {
+  0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+  0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+  0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+  0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+  0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+  0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+  0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+  0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+  0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+  0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+  0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+  0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+  0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+  0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+  0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+  0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+  0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+  0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+  0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+  0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+  0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+  0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+  0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+  0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+  0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+  0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+  0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+  0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+  0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+  0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+  0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+  0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+  0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+  0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+  0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+  0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+  0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+  0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+  0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+  0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+  0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+  0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+  0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+  0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+  0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+  0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+  0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+  0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+  0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+  0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+  0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+  0x2d02ef8dL
+};
+
+#define INIT_CRC  0xffffffffL
+
+#define DO1(buf)  do {crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);} while(0);
+#define DO2(buf)  do {DO1(buf); DO1(buf);} while(0);
+#define DO4(buf)  do {DO2(buf); DO2(buf);} while(0);
+#define DO8(buf)  do {DO4(buf); DO4(buf);} while(0);
+
+/*
+ * sysvar_crc - calculate the checksum of data buffer
+ */
+static unsigned long sysvar_crc(unsigned char *buf, int len) {
+  unsigned long crc = 0;
+
+  crc = crc ^ INIT_CRC;
+  while (len >= 8) {
+    DO8(buf);
+    len -= 8;
+  }
+  if (len) {
+      do {
+        DO1(buf);
+    } while (--len);
+  }
+  return crc ^ INIT_CRC;
+}
+
+/*
+ * sysvar_len - calculate the used bytes in data buffer
+ */
+static void sysvar_len(struct sysvar_buf *buf, int len) {
+  buf->used_len += len;
+
+  if (buf->used_len < 0)
+    buf->used_len = 0;
+
+  if (buf->used_len > buf->total_len)
+    buf->used_len = buf->total_len;
+
+  buf->free_len = buf->total_len - buf->used_len;
+}
+
+/*
+ * sysvar_copy - copy data from/to the data buffer
+ */
+static void sysvar_copy(char *dst, char *src, int len, char end) {
+  int i;
+
+  if (end == SYSVAR_STR_TO_BUF) {
+    /* copy a string to data buffer */
+    memset(dst, 0xff, len);
+  } else if (end == SYSVAR_BUF_TO_STR) {
+    /* copy data buffer to a string */
+    memset(dst, 0, len + 1);
+  } else {
+    return;
+  }
+
+  for (i = 0; i < len; i++) {
+    if (src[i] == end)
+      break;
+    dst[i] = src[i];
+  }
+}
+
+/*
+ * get_wc32 - return the write counter of data buffer
+ */
+unsigned long get_wc32(struct sysvar_buf *buf) {
+  int len = buf->total_len;
+  unsigned long x = buf->data[len + 3];
+
+  x = (x << 8) + buf->data[len + 2];
+  x = (x << 8) + buf->data[len + 1];
+  x = (x << 8) + buf->data[len];
+  return x;
+}
+
+/*
+ * set_wc32 - update the write counter of data buffer
+ */
+void set_wc32(struct sysvar_buf *buf) {
+  int len = buf->total_len;
+  unsigned long wc = get_wc32(buf) + 1;
+
+  buf->data[len + 3] = (unsigned char)(wc >> 24);
+  buf->data[len + 2] = (unsigned char)(wc >> 16);
+  buf->data[len + 1] = (unsigned char)(wc >> 8);
+  buf->data[len] = (unsigned char)(wc);
+}
+
+/*
+ * get_crc32 - check and return checksum of data buffer
+ */
+unsigned long get_crc32(struct sysvar_buf *buf) {
+  int len = buf->total_len + SYSVAR_WC32;
+  unsigned long x = buf->data[len + 3];
+
+  x = (x << 8) + buf->data[len + 2];
+  x = (x << 8) + buf->data[len + 1];
+  x = (x << 8) + buf->data[len];
+  return x;
+}
+
+/*
+ * set_crc32 - update checksum of data buffer
+ */
+void set_crc32(struct sysvar_buf *buf) {
+  int len = buf->total_len + SYSVAR_WC32;
+  unsigned long crc = sysvar_crc(buf->data, buf->total_len);
+
+  buf->data[len + 3] = (unsigned char)(crc >> 24);
+  buf->data[len + 2] = (unsigned char)(crc >> 16);
+  buf->data[len + 1] = (unsigned char)(crc >> 8);
+  buf->data[len] = (unsigned char)(crc);
+}
+
+/*
+ * load_var - move data from data buffer to data list
+ */
+int load_var(struct sysvar_buf *buf) {
+  int i, len, ret;
+  char name[SYSVAR_NAME + 1];
+  char *value;
+
+  /* clear system variables in data list */
+  ret = clear_var(buf);
+
+  /* system variable: name(32) + len(2) + value ... */
+  for (i = 0; i < buf->total_len && ret == SYSVAR_SUCCESS; i += len) {
+    if (buf->data[i] == 0xff)
+      break;
+
+    /* name of system variable */
+    sysvar_copy(name, (char *)&buf->data[i], SYSVAR_NAME, SYSVAR_BUF_TO_STR);
+    i += SYSVAR_NAME;
+
+    /* length of system variable */
+    len = (buf->data[i] << 8) + buf->data[i + 1];
+    if (len < 0 || len > buf->free_len)
+      return SYSVAR_PARAM_ERR;
+
+    value = (char *)malloc(len + 1);
+    if (value == NULL)
+      return SYSVAR_MEMORY_ERR;
+
+    i += 2;
+    /* copy system variable */
+    sysvar_copy(value, (char *)&buf->data[i], len, SYSVAR_BUF_TO_STR);
+
+    /* add system variable to data list */
+    ret = set_var(buf, name, value);
+
+    free(value);
+  }
+  return ret;
+}
+
+/*
+ * save_var - move data from data list to data buffer
+ */
+int save_var(struct sysvar_buf *buf) {
+  int i, len;
+  struct sysvar_list *curr = buf->list->next;
+
+  /* clear data buffer */
+  memset(buf->data, 0xff, buf->data_len);
+
+  for (i = 0; i < buf->total_len && curr != NULL; i += len) {
+    /* name of system variable */
+    sysvar_copy((char *)&buf->data[i], curr->name,
+                 SYSVAR_NAME, SYSVAR_STR_TO_BUF);
+    i += SYSVAR_NAME;
+
+    /* length of system variable */
+    len = strlen(curr->value);
+    if (len < 0 || len > buf->used_len)
+      return SYSVAR_PARAM_ERR;
+
+    buf->data[i] = (unsigned char)(len >> 8);
+    buf->data[i + 1] = (unsigned char)len;
+    i += 2;
+
+    /* copy system variable */
+    sysvar_copy((char *)&buf->data[i], curr->value, len, SYSVAR_STR_TO_BUF);
+
+    curr = curr->next;
+  }
+
+  return check_var(buf, SYSVAR_SET_MODE);
+}
+
+/*
+ * get_var - return the system variable from data list
+ */
+int get_var(struct sysvar_list *var, char *name, char *value, int len) {
+  if (len <= 0)
+    goto get_err;
+
+  /* copy variable name */
+  strncpy(name, var->name, SYSVAR_NAME);
+
+  /* copy variable value */
+  if (var->value == NULL)
+    goto get_err;
+
+  strncpy(value, var->value, len);
+  return SYSVAR_SUCCESS;
+
+get_err:
+  value[0] = '\0';
+  return SYSVAR_GET_ERR;
+}
+
+/*
+ * set_var - add the system variable from data buffer
+ */
+int set_var(struct sysvar_buf *buf, char *name, char *value) {
+  struct sysvar_list *curr = buf->list;
+  struct sysvar_list *var = NULL;
+  int name_len = strlen(name);
+  int value_len = strlen(value);
+
+  /* system variable: name(32) + len(2) + value ... */
+  if (name_len > SYSVAR_NAME)
+    return SYSVAR_PARAM_ERR;
+
+  var = (struct sysvar_list *)malloc(sizeof(struct sysvar_list));
+  if (var == NULL)
+    return SYSVAR_MEMORY_ERR;
+
+  var->value = (char *)malloc(value_len + 1);
+  if (var->value == NULL) {
+    free(var);
+    return SYSVAR_MEMORY_ERR;
+  }
+
+  strncpy(var->name, name, SYSVAR_NAME);
+  strncpy(var->value, value, value_len);
+  var->value[value_len] = '\0';
+  var->len = SYSVAR_NAME + 2 + value_len;
+  var->next = NULL;
+
+  /* add system variable */
+  while (curr->next != NULL)
+    curr = curr->next;
+  curr->next = var;
+
+  /* update the used bytes in data buffer */
+  sysvar_len(buf, var->len);
+  return SYSVAR_SUCCESS;
+}
+
+/*
+ * delete_var - delete system variable from data list
+ */
+int delete_var(struct sysvar_buf *buf, struct sysvar_list *var) {
+  struct sysvar_list *curr = buf->list->next;
+
+  /* last system variable? */
+  if (curr == var) {
+    buf->list->next = var->next;
+  } else {
+    /* go to next system variable */
+    while (curr != NULL) {
+      if (curr->next == var) {
+        curr->next = var->next;
+        goto delete_ok;
+      }
+      curr = curr->next;
+    }
+    return SYSVAR_DELETE_ERR;
+  }
+
+delete_ok:
+  /* update the used bytes in data buffer */
+  sysvar_len(buf, -var->len);
+
+  if (var->value != NULL)
+    free(var->value);
+  free(var);
+
+  return SYSVAR_SUCCESS;
+}
+
+/*
+ * clear_var - delete all system variables in data list
+ */
+int clear_var(struct sysvar_buf *buf) {
+  struct sysvar_list *curr = buf->list->next;
+  struct sysvar_list *var;
+  int ret = SYSVAR_SUCCESS;
+
+  while (curr != NULL && ret == SYSVAR_SUCCESS) {
+    /* store next position of variable */
+    var = curr->next;
+    /* delete last variable */
+    ret = delete_var(buf, curr);
+    /* restore next position of variable */
+    curr = var;
+  }
+  return ret;
+}
+
+/*
+ * find_var - find the system variable in data list
+ */
+struct sysvar_list *find_var(struct sysvar_buf *buf, char *name) {
+  struct sysvar_list *curr = buf->list->next;
+
+  if (name == NULL)
+    return NULL;
+
+  /* search the data list */
+  while (curr != NULL) {
+    if (strncmp(curr->name, name, SYSVAR_NAME) == 0)
+      break;
+    curr = curr->next;
+  }
+  return curr;
+}
+
+/*
+ * check_var - check/update checksum and write counter in data buffer
+ */
+int check_var(struct sysvar_buf *buf, int mode) {
+  unsigned long crc[2];
+  int ret = SYSVAR_SUCCESS;
+
+  switch (mode) {
+    case SYSVAR_LOAD_MODE:
+      crc[0] = get_crc32(buf);
+      crc[1] = sysvar_crc(buf->data, buf->total_len);
+      if (crc[0] == crc[1])
+        buf->modified = false;
+      else
+        ret = SYSVAR_CRC_ERR;
+      break;
+    case SYSVAR_SAVE_MODE:
+      set_wc32(buf);
+      buf->modified = false;
+      break;
+    case SYSVAR_SET_MODE:
+      set_crc32(buf);
+      buf->modified = true;
+      break;
+    default:
+      ret = SYSVAR_PARAM_ERR;
+      break;
+  }
+  return ret;
+}
+
+/*
+ * print_var - print the system variables
+ */
+void print_var(struct sysvar_buf *buf) {
+  struct sysvar_list *curr = buf->list->next;
+
+  while (curr != NULL) {
+    /* print variable name */
+    printf("%s(%s)=%s\n", curr->name,
+           buf->readonly ? "RO" : "RW", curr->value);
+    curr = curr->next;
+  }
+}
+
+/*
+ * clear_buf - clear the data buffer
+ */
+void clear_buf(struct sysvar_buf *buf) {
+  if (buf->data != NULL) {
+    memset(buf->data, 0xff, buf->data_len);
+    buf->loaded = true;
+
+    set_wc32(buf);
+    set_crc32(buf);
+  }
+}
+
+/*
+ * dump_buf - dump the data buffer in binary/ascii format
+ */
+void dump_buf(struct sysvar_buf *buf, int start, int len) {
+  char c;
+  int i, j;
+
+  for (i = start; i < start + len; i += 16) {
+    printf("[%08x] ", start + i);
+
+    /* binary format */
+    for (j = 0; j < 16; j++) {
+      if (i + j < buf->data_len)
+        printf("%02x ", buf->data[i + j]);
+      else
+        printf("   ");
+    }
+
+    /* ascii format */
+    for (j = 0; j < 16; j++) {
+      if (i + j < buf->data_len) {
+        c = buf->data[i + j];
+        if (c >= 32 && c < 127) {
+          printf("%c", c);
+        } else {
+          printf(".");
+        }
+      } else {
+        printf(" ");
+      }
+    }
+    printf("\n");
+  }
+}
+
diff --git a/include/sysvar.h b/include/sysvar.h
new file mode 100755
index 0000000..4e73b1f
--- /dev/null
+++ b/include/sysvar.h
@@ -0,0 +1,114 @@
+/* Copyright 2012 Google Inc. All Rights Reserved.
+ * Author: weixiaofeng@google.com (Xiaofeng Wei)
+ */
+
+#ifndef _SYSVAR_H_
+#define _SYSVAR_H_
+
+#define SYSVAR_UBOOT
+
+#ifndef SYSVAR_UBOOT
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#else
+typedef enum _bool{false, true} bool;
+#endif
+#include <malloc.h>
+
+#define SYSVAR_WC32         4
+#define SYSVAR_CRC32        4
+#define SYSVAR_HEAD         (SYSVAR_WC32 + SYSVAR_CRC32)
+#define SYSVAR_NAME         32
+#define SYSVAR_MESSAGE      -1
+
+#define SYSVAR_STR_TO_BUF   0x00
+#define SYSVAR_BUF_TO_STR   0xff
+
+#define SYSVAR_BLOCK_SIZE   0x00010000  /* size of system variables (64K) */
+
+#define SYSVAR_SPI_BLOCK    4           /* number of SPI flash blocks */
+#define SYSVAR_RW_OFFSET0   0x00100000  /* location of system variables(RW) */
+#define SYSVAR_RW_OFFSET1   0x00120000  /* location of system variables(RW backup) */
+#define SYSVAR_RO_OFFSET0   0x00140000  /* location of system variables(RO) */
+#define SYSVAR_RO_OFFSET1   0x00160000  /* location of system variables(RO backup) */
+
+#define SYSVAR_MTD_DEVICE   4           /* number of MTD devices */
+#define SYSVAR_RW_NAME0     "/dev/mtd2" /* MTD device of system variables(RW) */
+#define SYSVAR_RW_NAME1     "/dev/mtd3" /* MTD device of system variables(RW backup) */
+#define SYSVAR_RO_NAME0     "/dev/mtd4" /* MTD device of system variables(RO) */
+#define SYSVAR_RO_NAME1     "/dev/mtd5" /* MTD device of system variables(RO backup) */
+
+#define SYSVAR_RW_DATA0     0
+#define SYSVAR_RW_DATA1     1
+#define SYSVAR_RO_DATA0     2
+#define SYSVAR_RO_DATA1     3
+
+#define SYSVAR_RW_BUF       0
+#define SYSVAR_RO_BUF       2
+
+#define SYSVAR_GET_MODE     0
+#define SYSVAR_SET_MODE     1
+#define SYSVAR_LOAD_MODE    2
+#define SYSVAR_SAVE_MODE    3
+
+#define SYSVAR_SUCCESS      0
+#define SYSVAR_MEMORY_ERR   -1
+#define SYSVAR_OPEN_ERR     -2
+#define SYSVAR_READ_ERR     -3
+#define SYSVAR_WRITE_ERR    -4
+#define SYSVAR_ERASE_ERR    -5
+#define SYSVAR_LOAD_ERR     -6
+#define SYSVAR_SAVE_ERR     -7
+#define SYSVAR_GET_ERR      -8
+#define SYSVAR_SET_ERR      -9
+#define SYSVAR_DELETE_ERR   -10
+#define SYSVAR_PARAM_ERR    -11
+#define SYSVAR_CRC_ERR      -12
+#define SYSVAR_READONLY_ERR -13
+#define SYSVAR_EXISTED_ERR  -14
+
+struct sysvar_list {
+  char name[SYSVAR_NAME + 1]; /* name of system variable */
+  char *value;                /* value of system variable */
+  int len;                    /* length of system variable */
+
+  struct sysvar_list *next;
+};
+
+struct sysvar_buf {
+  unsigned char *data;  /* data buffer to store system variables */
+  int data_len;         /* buffer size */
+
+  int total_len;        /* total space = buffer size - buffer header */
+  int free_len;         /* free space in data buffer */
+  int used_len;         /* total bytes of variables */
+
+  bool loaded;          /* data buffer has been loaded from SPI flash */
+  bool modified;        /* data modified in the data buffer */
+  bool readonly;        /* read only system variables */
+  bool failed[2];       /* failed to read data from SPI flash */
+
+  struct sysvar_list *list;
+};
+
+extern unsigned long get_wc32(struct sysvar_buf *buf);
+extern void set_wc32(struct sysvar_buf *buf);
+extern unsigned long get_crc32(struct sysvar_buf *buf);
+extern void set_crc32(struct sysvar_buf *buf);
+
+extern int load_var(struct sysvar_buf *buf);
+extern int save_var(struct sysvar_buf *buf);
+extern int get_var(struct sysvar_list *var, char *name, char *value, int len);
+extern int set_var(struct sysvar_buf *buf, char *name, char *value);
+extern int delete_var(struct sysvar_buf *buf, struct sysvar_list *var);
+extern int clear_var(struct sysvar_buf *buf);
+extern struct sysvar_list *find_var(struct sysvar_buf *buf, char *name);
+extern int check_var(struct sysvar_buf *buf, int mode);
+extern void print_var(struct sysvar_buf *buf);
+
+extern void clear_buf(struct sysvar_buf *buf);
+extern void dump_buf(struct sysvar_buf *buf, int start, int len);
+
+#endif  /* _SYSVAR_H_ */