[coreboot-gerrit] Change in coreboot[master]: util/vpd/: Add VPD tools

Marcello Sylvester Bauer (Code Review) gerrit at coreboot.org
Wed Apr 11 12:03:19 CEST 2018


Marcello Sylvester Bauer has uploaded this change for review. ( https://review.coreboot.org/25615


Change subject: util/vpd/: Add VPD tools
......................................................................

util/vpd/: Add VPD tools

Add the VPD tools from the ChromiumOS project.
https://chromium.googlesource.com/chromiumos/platform/vpd

Change-Id: Ib3b8de7d27236f3f0e216d5517da0c1f00682f52
Signed-off-by: Marcello Sylvester Bauer <info at marcellobauer.com>
---
A util/vpd/LICENSE
A util/vpd/Makefile
A util/vpd/README.md
A util/vpd/include/lib/flashrom.h
A util/vpd/include/lib/fmap.h
A util/vpd/include/lib/lib_smbios.h
A util/vpd/include/lib/lib_vpd.h
A util/vpd/include/lib/math.h
A util/vpd/include/lib/vpd.h
A util/vpd/include/lib/vpd_tables.h
A util/vpd/lib/flashrom.c
A util/vpd/lib/fmap.c
A util/vpd/lib/lib_smbios.c
A util/vpd/lib/lib_vpd_test.c
A util/vpd/lib/math.c
A util/vpd/lib/vpd_container.c
A util/vpd/lib/vpd_decode.c
A util/vpd/lib/vpd_encode.c
A util/vpd/vpd.c
19 files changed, 3,886 insertions(+), 0 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/15/25615/1

diff --git a/util/vpd/LICENSE b/util/vpd/LICENSE
new file mode 100644
index 0000000..d251496
--- /dev/null
+++ b/util/vpd/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/util/vpd/Makefile b/util/vpd/Makefile
new file mode 100644
index 0000000..0a42399
--- /dev/null
+++ b/util/vpd/Makefile
@@ -0,0 +1,65 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#    * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#    * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#    * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU General Public License ("GPL") version 2 as published by the Free
+# Software Foundation.
+
+CC?=cc
+VERSION := $(shell ./util/getversion.sh)
+CFLAGS+=-g -Iinclude/ -Iinclude/lib -Werror -Wall -D'VPD_VERSION="$(VERSION)"'
+LDFLAGS+=-luuid
+BINARY=vpd
+STATIC_BINARY=vpd_s
+BINS=$(BINARY) $(STATIC_BINARY)
+
+LIB_OBJS= \
+	lib/flashrom.o \
+	lib/fmap.o \
+	lib/lib_smbios.o \
+	lib/vpd_container.o \
+	lib/vpd_decode.o \
+	lib/vpd_encode.o \
+	lib/math.o
+
+OBJS = $(LIB_OBJS) vpd.o
+
+all: $(BINS)
+
+$(BINARY): $(OBJS)
+	$(CC) -o $@ $? $(LDFLAGS)
+
+$(STATIC_BINARY): $(OBJS)
+	$(CC) -static -o $@ $? $(LDFLAGS)
+
+clean:
+	rm -f $(OBJS) $(BINS)
+
+.PHONY: clean test all
diff --git a/util/vpd/README.md b/util/vpd/README.md
new file mode 100644
index 0000000..b105016
--- /dev/null
+++ b/util/vpd/README.md
@@ -0,0 +1,213 @@
+# ChromeOS Vital Product Data (VPD) Binary Blob 2.0
+
+[TOC]
+
+## Overview
+
+The [Chrome OS Platform VPD Reporting Specification](https://docs.google.com/a/google.com/document/d/1d2l5obmBYxgaJQvltEJD-oiLTb0M-RXG-pujG5VDa_A/edit?hl=en#)
+describes how a firmware image complies with the Chrome OS SMBIOS requirement.
+That document defines the required fields of type 0/1/127 tables and type 241
+binary blob pointers. However, the format of type 241 is left for each ODM to
+define. In most cases this is acceptable because Google is not involved in
+manufacturing and RMA processes.
+
+The type 241 data often consists of component IDs and other items that affect
+the OEM software setup. Component IDs include network device IDs, IDs for
+removable devices (storage, RAM, etc), and other arbitrary ID types. OEM
+software information includes the default country code, language code, and so
+on.
+
+Occasionally, modifying the vendor product data is required. Each OEM uses a
+proprietary format, which varies from model to model. To minimize variations,
+Google defined a standard format for the Vital Product Database (VPD) that is
+common, simple, and extensible.
+
+This document describes the standard format for the VPD on Chrome devices. The
+format provides:
+
+* An universal vendor product data format for all ODMs and OEMs
+* A method for tracking serial numbers for all removable or replaceable
+  components on a machine
+* A more efficient, usable approach for both manufacturing and Return Materials
+  Authorization (RMA) processes
+
+The format design principles were:
+
+* Simple encoding implementation (no XML)
+* Flexible enough for different hardware configurations
+
+## **Syntax**
+
+The basic syntax for database entries is `{type, key, value} * n`. Instead of
+using a null-terminated ASCII string for key and value, we prefix a length for
+binary-safe. The string size varies according to the length value that precedes
+it. Every byte is contiguous and back-to-back. Padding the value is field
+allowed. To avoid parsing the VPD structure, the hardcoded fixed address can be
+used to read VPD values directly.
+
+The following diagram illustrates the structure of the blob:
+![blob 2.0 diagram](_images/vpd_blob_2.0.png)
+
+The entire binary blob is an array of `{key, value}` pairs. At the end, a `0x00`
+(`VPD_TYPE_TERMINATOR`) or a `0xFF` (`VPD_TYPE_IMPLICIT_TERMINATOR`) indicates
+the end of the array and the binary blob.
+
+The combination of key and value can exceed 127 bytes. Setting the More bit to 1
+indicates the next byte has lower significant 7 bits in length.
+
+## **Encoding example**
+
+The following example shows how to encode strings of normal length:
+
+    <01> <04> "UUID"          <10> "0123456789ABCDEF"
+    <01> <07> "3G_IMEI"       <0E> "AABBBBBB-CC-DD"
+    <01> <0C> "ethernet_mac"  <06> <2A><02><03><B3><D5><7C>
+    <00>   // the terminator
+
+The following example shows how to encode longer strings, like "any=Very long
+long long...":
+
+    <01> <03> "any"     <84><82><01> "Very long long long"...(65793 bytes)
+    <00>
+
+The VPD supports 4 types:
+
+| Type | Name                           | Description                        |
+|------|--------------------------------|------------------------------------|
+| 0x00 | `VPD_TYPE_TERMINATOR`          | Terminates VPD data.               |
+| 0x01 | `VPD_TYPE_STRING`              | Encoded string length-value pair.  |
+| 0xFE | `VPD_TYPE_INFO`                | Key-value pair VPD info header.    |
+| 0xFF | `VPD_TYPE_IMPLICIT_TERMINATOR` | Same as VPD_TYPE_TERMINATOR.       |
+
+Additional information:
+
+`VPD_TYPE_STRING` is made up of a key pair and a value pair.
+
+`VPD_TYPE_INFO` is a key-value pair the AP Firmware (sometimes referred as BIOS)
+uses to parse the VPD. The search pattern is,"`gVpdInfo`",(see `VPD_INFO_MAGIC`
+in the code) and the 4-byte size of VPD data follows.
+
+For details of each type, search for `VPD_TYPE_*` in the code.
+
+## **Utility command examples**
+
+Chrome OS includes a utility for manipulating VPD data. The examples below show
+how to use the VPD utility to perform common tasks. Each task is described in
+the comment line preceding the example:
+
+```sh
+  # Dump all key-pairs in RO_VPD partition on the flash
+  % vpd -l
+    "serial_number"="20100915SKU0015566"
+    "SKU"="SKU0001"
+    "UUID"="0001-0203_04050607"
+    "ethernet_mac"="2A:45:29_66:D4:57"
+
+  # Dump RW_VPD partition
+  % vpd -l -i "RW_VPD"
+
+  # Rather using flashrom to access flash (usually slow), access a temp file.
+  % flashrom -r vpd.bin
+  % vpd -f vpd.bin -l
+
+  # Add a new key-value pair, which value is a string (-s)
+  % vpd -f vpd.bin -s "SKU"="0123456789ABCDEF"
+
+  # Assign padding length in value field. -p must be in front of the -s
+  # you want to take effected on.
+  % vpd -f vpd.bin -p 16 -s "SKU"="0123"
+  # In this case, the value of SKU is 16-byte long, which contains 12
+  # \0 at tail.
+
+  # Add tons of key-value pairs at one time.
+  % vpd -f vpd.bin -p 32 -s "serial_number"="20101214_MARIO_1122" \
+                   -p 16 -s "ethernet_mac"="11:22:33:44:55:66" \
+                         -s "mlb_serial_number"="2037291738734" \
+                   -p 8  -s "UUID"="2323-3524-2344364-133456" \
+                   ...
+
+  # By default, utility accesses "RO_VPD" on flash. You can specify -i
+  # parameter to access particular partition.
+  # PS. -O means overwriting any garbage in partition.
+  vpd -i "RW_VPD" -O -s "ActivateDate=2011/03/02 11:22:33"
+
+  # Read a specific key-value pair. Specially useful in shell script.
+  % vpd -g "mlb_serial_number"
+    MB20100914_012345
+  # no key string and no quotes in output.
+
+  # Delete a key-value pair
+  % vpd -f vpd.bin -d "3G_IMEI"
+```
+
+## Partition names
+
+The AP firmware image for Chrome OS has two VPD partitions. Each stores a
+specific type of data:
+
+* `RO_VPD` partition in the read-only area. Stores machine-specific information,
+  like the mainboard serial number and MAC address.
+* `RW_VPD` partition in the writeable area. Stores all data that will be updated
+  after a device leaves the factory.
+
+Note that the naming convention includes the underscore ( _ ) character.
+
+## VPD fields
+
+Although VPD 2.0 doesn't define a fixed data structure, Google requires partners
+to include specific VPD fields in the AP firmware image, for example
+`serial_number`. The list of VPD fields are available in the ChromeOS Partner
+Site document "VPD Field Requirements".
+
+In theory the VPD name can be arbitrary string, but in order to simplify
+processing, the utility only accepts names in CamelCase or lower_underline,
+i.e., in regular expression `[a-zA-Z0-9_]+`.
+
+## Appendix A
+
+This information is intended for a developer audience.
+
+### Type enumerates used in 'type' field.
+
+| Value | Type                     |
+|-------|--------------------------|
+| 0x00  | The terminator           |
+| 0x01  | String                   |
+| 0xFE  | Info header              |
+| 0xFF  | The implicit terminator. |
+
+On the flash media, a non-programmed byte is 0xFF. When decoder reads this
+type, it should assume no more pairs are present after this byte.
+
+### Values in Binary Blob Pointer (Type 241)
+
+| Offset  | Name                    | Length   | Value                                |
+|---------|-------------------------|----------|--------------------------------------|
+| 00h     | Type                    | BYTE     | 241                                  |
+| 01h     | Length                  | BYTE     | Varies                               |
+| 02h     | Handle                  | WORD     | Varies                               |
+| 04h     | Structure Major Version | BYTE     | 01h                                  |
+| 05h     | Structure Minor Version | BYTE     | 00h                                  |
+| 06h     | Blob Vendor             | BYTE     | 1 "Google"                           |
+| 07h     | Blob Description        | BYTE     | 2 "VPD 2.0"                          |
+| 08h     | Blob Major Version      | BYTE     | 2                                    |
+| 09h     | Blob Minor Version      | BYTE     | 0                                    |
+| 0Ah     | Blob Variant            | BYTE     | 3 ""                                 |
+| 0Bh-0Fh | Reserved                |          |                                      |
+| 10h-1Fh | Blob UUID               | 16 BYTES | 0a7c23d3-8a27-4252-99bf-7868a2e26b61 |
+| 20h-23h | Offset                  | DWORD    | Varies                               |
+| 24h-27h | Size                    | DWORD    | Varies                               |
+
+## Change Log
+| Version | Date       | Changes                                               |
+|---------|------------|-------------------------------------------------------|
+| 0.17    | 2014/02/27 | Added VPD_TYPE_INFO                                   |
+| 0.12    | 2011/04/20 | Reorganized content, updated available options        |
+| 0.7     | 2011/03/11 | Added VPD partition names and required field          |
+| 0.6     | 2011/03/08 | Made small refinement to Binary Blob Pointer values   |
+| 0.5     | 2011/03/02 | Added RW VPD example                                  |
+| 0.4     | 2010/12/13 | Fixed the command style                               |
+| 0.3     | 2010/12/14 | Added the implicit terminator (0xFF)                  |
+| 0.2     | 2010/12/08 | Added type field. Allowed padding.                    |
+| 0.1     | 2010/09/14 | Draft version.                                        |
+
diff --git a/util/vpd/include/lib/flashrom.h b/util/vpd/include/lib/flashrom.h
new file mode 100644
index 0000000..0849b6d
--- /dev/null
+++ b/util/vpd/include/lib/flashrom.h
@@ -0,0 +1,45 @@
+ /*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ */
+
+#ifndef __LIB_FLASHROM_H__
+#define __LIB_FLASHROM_H__
+
+#include <inttypes.h>
+
+enum {
+  FLASHROM_OK = 0,
+  FLASHROM_FAIL,
+};
+
+int flashromFullRead(const char* full_file);
+
+int flashromPartialRead(const char* part_file, const char* full_file,
+                        const char* partition_name);
+
+int flashromPartialWrite(const char* part_file, const char* full_file,
+                         const char* partition_name);
+
+#endif /* __LIB_FLASHROM_H__ */
diff --git a/util/vpd/include/lib/fmap.h b/util/vpd/include/lib/fmap.h
new file mode 100644
index 0000000..148ecc2
--- /dev/null
+++ b/util/vpd/include/lib/fmap.h
@@ -0,0 +1,129 @@
+ /*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * Ported from mosys project (http://code.google.com/p/mosys/)
+ *
+ * The fmap document can be found in
+ *     http://code.google.com/p/flashmap/wiki/FmapSpec.
+ */
+
+#ifndef __LIB_FMAP_H__
+#define __LIB_FMAP_H__
+
+#include <inttypes.h>
+
+#define FMAP_SIGNATURE    "__FMAP__"
+#define FMAP_VER_MAJOR    1  /* this header's FMAP minor version */
+#define FMAP_VER_MINOR    0  /* this header's FMAP minor version */
+#define FMAP_STRLEN    32    /* maximum length for strings, */
+                             /* including null-terminator */
+
+enum {
+  FMAP_OK = 0,
+  FMAP_FAIL,
+};
+
+enum fmap_flags {
+  FMAP_AREA_STATIC  = 1 << 0,
+  FMAP_AREA_COMPRESSED  = 1 << 1,
+};
+
+struct crypto_algo;  /* forward declaration */
+
+/* Mapping of volatile and static regions in firmware binary */
+struct fmap {
+  uint8_t signature[8];    /* "__FMAP__" (0x5F5F50414D465F5F) */
+  uint8_t ver_major;    /* major version */
+  uint8_t ver_minor;    /* minor version */
+  uint64_t base;      /* address of the firmware binary */
+  uint32_t size;      /* size of firmware binary in bytes */
+  char name[FMAP_STRLEN];  /* name of this firmware binary */
+  uint16_t nareas;    /* number of areas described by
+             fmap_areas[] below */
+  struct fmap_area {
+    uint32_t offset;    /* offset relative to base */
+    uint32_t size;      /* size in bytes */
+    char name[FMAP_STRLEN];  /* descriptive name */
+    uint16_t flags;      /* flags for this area */
+  }  __attribute__((packed)) areas[];
+} __attribute__((packed));
+
+/*
+ * fmapNormalizeAreaName - to protect malicious attack, this function
+ * replace not allowed chars into underline. Only following are allowed:
+ *
+ *   numbers
+ *   digits
+ *   space
+ *
+ * The allowing list should increase if fmap area name allows more chars.
+ *
+ * @name: point to the name.
+ */
+void fmapNormalizeAreaName(char *name);
+
+
+/*
+ * fmapFind - finds FMAP signature in a binary image
+ *
+ * @image:  binary image
+ * @len:  length of binary image
+ *
+ * This function does no error checking. The caller is responsible for
+ * verifying that the contents are sane.
+ *
+ * returns offset of FMAP signature to indicate success
+ * returns <0 to indicate failure
+ */
+off_t fmapFind(const uint8_t *image, size_t image_len);
+
+
+/*
+ * fmapGetArea - finds the area in a binary image
+ *
+ * @name: name to search
+ * @image: points to the start of image
+ * @image_len: length of the image
+ * @offset: pointer to store the offset of the found area
+ * @size: pointer to store the size of the found area.
+ *
+ * This function returns FMAP_OK if found. Returns FMAP_FAIL if not found.
+ *
+ */
+int fmapGetArea(const char *name, const struct fmap *fmap,
+                uint32_t *offset, uint32_t *size);
+
+
+#endif  /* __LIB_FMAP_H__*/
diff --git a/util/vpd/include/lib/lib_smbios.h b/util/vpd/include/lib/lib_smbios.h
new file mode 100644
index 0000000..1e1cef8
--- /dev/null
+++ b/util/vpd/include/lib/lib_smbios.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Ported from mosys project (http://code.google.com/p/mosys/)
+ */
+
+#ifndef __LIB_LIB_SMBIOS__
+#define __LIB_LIB_SMBIOS__
+
+#include <inttypes.h>
+#include "lib/vpd_tables.h"
+
+struct vpd_entry *vpd_create_eps(unsigned short structure_table_len,
+                                 unsigned short num_structures,
+                                 uint32_t eps_base);
+int vpd_append_type241(uint16_t handle, uint8_t **buf,
+                       size_t len, const char *uuid, uint32_t offset,
+                       uint32_t size, const char *vendor,
+                       const char *desc, const char *variant);
+int vpd_type241_size(struct vpd_header *header);
+int vpd_append_type127(uint16_t handle,
+                       uint8_t **buf, size_t len);
+
+#endif  /* __LIB_LIB_SMBIOS__ */
diff --git a/util/vpd/include/lib/lib_vpd.h b/util/vpd/include/lib/lib_vpd.h
new file mode 100644
index 0000000..d40c9a8
--- /dev/null
+++ b/util/vpd/include/lib/lib_vpd.h
@@ -0,0 +1,261 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google Inc.  All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+
+#ifndef __LIB_VPD__
+#define __LIB_VPD__
+
+#include <inttypes.h>
+
+enum vpd_err {
+   /* These error codes are returned to the shell as exit codes. If they are
+    * changed then callers such as Enterprise.VpdCheck from histograms.xml must
+    * be updated along with tests/functions.sh. If codes are added then they
+    * must not conflict with conventional exit codes:
+    * http://www.tldp.org/LDP/abs/html/exitcodes.html
+    */
+  VPD_OK              = 0,
+  VPD_FAIL            = 3,   /* generic vpd utility error */
+  VPD_ERR_SYSTEM      = 4,   /* system error (file error, out of memory, etc) */
+  VPD_ERR_ROM_READ    = 5,   /* error reading ROM (e.g. thru flashrom) */
+  VPD_ERR_ROM_WRITE   = 6,   /* error writing ROM (e.g. thru flashrom) */
+  VPD_ERR_SYNTAX      = 7,   /* command syntax error */
+  VPD_ERR_PARAM       = 8,   /* invalid parameter specified by user */
+  VPD_ERR_NOT_FOUND   = 9,   /* VPD not found */
+  VPD_ERR_OVERFLOW    = 10,  /* boundary exceeded */
+  VPD_ERR_INVALID     = 11,  /* error in VPD - possible corruption or bug */
+};
+
+typedef enum vpd_err vpd_err_t;
+
+enum {
+  VPD_TYPE_TERMINATOR = 0,
+  VPD_TYPE_STRING,
+  VPD_TYPE_INFO                = 0xfe,
+  VPD_TYPE_IMPLICIT_TERMINATOR = 0xff,
+};
+
+enum {
+  VPD_AS_LONG_AS = -1,
+};
+
+enum {  /* export_type */
+  VPD_EXPORT_KEY_VALUE = 1,
+  VPD_EXPORT_VALUE,
+  VPD_EXPORT_AS_PARAMETER,
+  VPD_EXPORT_NULL_TERMINATE,
+};
+
+/* Callback for decodeVpdString to invoke. */
+typedef vpd_err_t VpdDecodeCallback(const uint8_t *key, int32_t key_len,
+                                    const uint8_t *value, int32_t value_len,
+                                    void *arg);
+
+/* Container data types */
+struct StringPair {
+  uint8_t *key;
+  uint8_t *value;
+  int pad_len;
+  int filter_out;  /* TRUE means not exported. */
+  struct StringPair *next;
+};
+
+struct PairContainer {
+  struct StringPair *first;
+};
+
+
+/***********************************************************************
+ * Encode and decode VPD entries
+ ***********************************************************************/
+
+/* Encodes the len into multiple bytes with the following format.
+ *
+ *    7   6 ............ 0
+ *  +----+------------------+
+ *  |More|      Length      |  ...
+ *  +----+------------------+
+ *
+ * The encode_buf points to the actual position we are going to store.
+ * encoded_len will return the exact bytes we encoded in this function.
+ * Returns fail if the buffer is not long enough.
+ */
+vpd_err_t encodeLen(
+    const int32_t len,
+    uint8_t *encode_buf,
+    const int32_t max_len,
+    int32_t *encoded_len);
+
+/* Given an encoded string, this functions decodes the length field which varies
+ * from 1 byte to many bytes.
+ *
+ * The in points the actual byte going to be decoded. The *length returns
+ * the decoded length field. The number of consumed bytes will be stroed in
+ * decoded_len.
+ *
+ * Returns VPD_FAIL if more bit is 1, but actually reaches the end of string.
+ */
+vpd_err_t decodeLen(
+    const int32_t max_len,
+    const uint8_t *in,
+    int32_t *length,
+    int32_t *decoded_len);
+
+
+/* Encodes the terminator.
+ * When calling, the output_buf should point to the start of buffer while
+ * *generated_len should contain how many bytes exist in buffer now.
+ * After return, *generated_len would be plused the number of bytes generated
+ * in this function.
+ */
+vpd_err_t encodeVpdTerminator(
+    const int max_buffer_len,
+    uint8_t *output_buf,
+    int *generated_len);
+
+/* Encodes the given type/key/value pair into buffer.
+ *
+ * The pad_value_len is used to control the output value length.
+ * When pad_value_len > 0, the value is outputted into fixed length (pad \0
+ *                         if the value is shorter). Truncated if too long.
+ *      pad_value_len == VPD_NO_LIMIT, output the value as long as possible.
+ * This is useful when we want to fix the structure layout at beginning.
+ *
+ * The encoded string will be stored in output_buf. If it is longer than
+ * max_buffer_len, this function returns fail. If the buffer is long enough,
+ * the generated_len will be updated.
+ *
+ * When calling, the output_buf should point to the start of buffer while
+ * *generated_len should contain how many bytes exist in buffer now.
+ * After return, *generated_len would be plused the number of bytes generated
+ * in this function.
+ *
+ * The initial value of *generated_len can be non-zero, so that this value
+ * can be used between multiple calls to encodeVpd2Pair().
+ */
+vpd_err_t encodeVpdString(
+    const uint8_t *key,
+    const uint8_t *value,
+    const int pad_value_len,
+    const int max_buffer_len,
+    uint8_t *output_buf,
+    int *generated_len);
+
+
+/* Given the encoded string, this function invokes callback with extracted
+ * (key, value). The *consumed will be plused the number of bytes consumed in
+ * this function.
+ *
+ * The input_buf points to the first byte of the input buffer.
+ *
+ * The *consumed starts from 0, which is actually the next byte to be decoded.
+ * It can be non-zero to be used in multiple calls.
+ *
+ * If one entry is successfully decoded, sends it to callback and returns the
+ * result.
+ */
+vpd_err_t decodeVpdString(
+    const int32_t max_len,
+    const uint8_t *input_buf,
+    int32_t *consumed,
+    VpdDecodeCallback callback,
+    void *callback_arg);
+
+/***********************************************************************
+ * Container helpers
+ ***********************************************************************/
+void initContainer(struct PairContainer *container);
+
+struct StringPair *findString(struct PairContainer *container,
+                              const uint8_t *key,
+                              struct StringPair ***prev_next);
+
+/* If key is already existed in container, its value will be replaced.
+ * If not existed, creates new entry in container.
+ */
+void setString(struct PairContainer *container,
+               const uint8_t *key,
+               const uint8_t *value,
+               const int pad_len);
+
+/* merge all entries in src into dst. If key is duplicate, overwrite it.
+ */
+void mergeContainer(struct PairContainer *dst,
+                    const struct PairContainer *src);
+
+/* subtract src from dst.
+*/
+int subtractContainer(struct PairContainer *dst,
+                      const struct PairContainer *src);
+
+/* Given a container, encode its all entries into the buffer.
+ */
+vpd_err_t encodeContainer(const struct PairContainer *container,
+                          const int max_buf_len,
+                          uint8_t *buf,
+                          int *generated);
+
+/* Given a VPD blob, decode its entries and push into container.
+ */
+vpd_err_t decodeToContainer(struct PairContainer *container,
+                            const int32_t max_len,
+                            const uint8_t *input_buf,
+                            int32_t *consumed);
+
+/* Set filter for exporting functions.
+ * If filter is NULL, resets the filter so that everything can be exported.
+ */
+vpd_err_t setContainerFilter(struct PairContainer *container,
+                             const uint8_t *filter);
+
+/*
+ * Remove a key.
+ * Returns VPD_OK if deleted successfully. Otherwise, VPD_FAIL.
+ */
+vpd_err_t deleteKey(struct PairContainer *container,
+                    const uint8_t *key);
+
+/*
+ * Returns number of pairs in container.
+ */
+int lenOfContainer(const struct PairContainer *container);
+
+
+/*
+ * Export the value in raw format.
+ *
+ * The buf points to the first byte of buffer and *generated contains the number
+ * of bytes already existed in buffer.
+ *
+ * Afterward, the *generated will be plused on exact bytes this function has
+ * generated.
+ */
+vpd_err_t exportStringValue(const struct StringPair *str,
+                            const int max_buf_len,
+                            uint8_t *buf,
+                            int *generated);
+
+/*
+ * Export the container content with human-readable text.
+ *
+ * The buf points to the first byte of buffer and *generated contains the number
+ * of bytes already existed in buffer.
+ *
+ * Afterward, the *generated will be plused on exact bytes this function has
+ * generated.
+ */
+vpd_err_t exportContainer(const int export_type,
+                          const struct PairContainer *container,
+                          const int max_buf_len,
+                          uint8_t *buf,
+                          int *generated);
+
+void destroyContainer(struct PairContainer *container);
+
+#endif  /* __LIB_VPD__ */
diff --git a/util/vpd/include/lib/math.h b/util/vpd/include/lib/math.h
new file mode 100644
index 0000000..5fd50c8
--- /dev/null
+++ b/util/vpd/include/lib/math.h
@@ -0,0 +1,80 @@
+ /*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Google Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Ported from mosys project (http://code.google.com/p/mosys/).
+ */
+
+#ifndef __LIB_MATH_H__
+#define __LIB_MATH_H__
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/*
+ * Count the number of low-order 0 bits.
+ */
+extern int ctz(unsigned long long int u);
+
+/*
+ * Get the integral log2 of a number.
+ *
+ * If n is not a perfect power of 2, this function will return return the
+ * log2 of the largest power of 2 less than n.
+ *
+ * If n is negative, this functions will return the log of abs(n).
+ */
+extern int logbase2(int n);
+
+/*
+ * rolling8_csum  -  Bytewise rolling summation "checksum" of a buffer
+ *
+ * @buf:  buffer to sum
+ * @len:  length of buffer
+ */
+extern uint8_t rolling8_csum(uint8_t *buf, size_t len);
+
+/*
+  * zero8_csum - Calculates 8-bit zero-sum checksum
+  *
+  * @buf:  input buffer
+  * @len:  length of buffer
+  *
+  * The summation of the bytes in the array and the csum will equal zero
+  * for 8-bit data size.
+  *
+  * returns checksum to indicate success
+  */
+extern uint8_t zero8_csum(uint8_t *buf, size_t len);
+
+#ifndef __mask
+# define __mask(high, low) ((1ULL << (high)) + \
+                            (((1ULL << (high)) - 1) - ((1ULL << (low)) - 1)))
+#endif
+
+#ifndef __min
+# define __min(a, b)  ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef __max
+# define __max(a, b)  ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef __abs
+# define __abs(x) (x < 0 ? -x : x)
+#endif
+
+#endif /* __LIB_MATH_H__ */
diff --git a/util/vpd/include/lib/vpd.h b/util/vpd/include/lib/vpd.h
new file mode 100644
index 0000000..be59660
--- /dev/null
+++ b/util/vpd/include/lib/vpd.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Google Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Ported from mosys project (http://code.google.com/p/mosys/).
+ */
+
+#ifndef __LIB_VPD_H__
+#define __LIB_VPD_H__
+
+
+/* VPD Table Types */
+enum vpd_types {
+  VPD_TYPE_FIRMWARE = 0,
+  VPD_TYPE_SYSTEM,
+  VPD_TYPE_END                  = 127,
+  VPD_TYPE_BINARY_BLOB_POINTER  = 241,
+};
+
+
+#define GOOGLE_SPD_OFFSET 0x400
+#define GOOGLE_SPD_UUID "75f4926b-9e43-4b32-8979-eb20c0eda76a"
+#define GOOGLE_SPD_VENDOR "Google"
+#define GOOGLE_SPD_DESCRIPTION "Google SPD"
+#define GOOGLE_SPD_VARIANT ""
+
+#define GOOGLE_VPD_2_0_OFFSET 0x600
+#define GOOGLE_VPD_2_0_UUID "0a7c23d3-8a27-4252-99bf-7868a2e26b61"
+#define GOOGLE_VPD_2_0_VENDOR "Google"
+#define GOOGLE_VPD_2_0_DESCRIPTION "Google VPD 2.0"
+#define GOOGLE_VPD_2_0_VARIANT ""
+
+#define GOOGLE_VPD_1_2_OFFSET 0x100
+#define GOOGLE_VPD_1_2_UUID "08f8a2b0-15fd-4cfd-968f-8378f2c508ce"
+#define GOOGLE_VPD_1_2_VENDOR "Google"
+#define GOOGLE_VPD_1_2_DESCRIPTION "Google VPD 1.2"
+#define GOOGLE_VPD_1_2_VARIANT ""
+
+#define CONFIG_EPS_VPD_MAJOR_VERSION 2
+#define CONFIG_EPS_VPD_MINOR_VERSION 6
+
+#endif /* __LIB_VPD_H__ */
diff --git a/util/vpd/include/lib/vpd_tables.h b/util/vpd/include/lib/vpd_tables.h
new file mode 100644
index 0000000..f1060af
--- /dev/null
+++ b/util/vpd/include/lib/vpd_tables.h
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Google Inc.  All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Ported from mosys project (http://code.google.com/p/mosys/).
+ */
+
+#ifndef __LIB_VPD_TABLES_H__
+#define __LIB_VPD_TABLES_H__
+
+#include <inttypes.h>
+
+#define VPD_ENTRY_MAGIC    "_SM_"
+#define VPD_INFO_MAGIC     \
+  "\xfe"      /* type: VPD header */       \
+  "\x09"      /* key length, 9 = 1 + 8 */  \
+  "\x01"      /* info version, 1 */        \
+  "gVpdInfo"  /* signature, 8 bytes */ \
+  "\x04"      /* value length */
+
+/* Google specific VPD info */
+struct google_vpd_info {
+  union {
+    struct {
+      uint8_t type;
+      uint8_t key_len;
+      uint8_t info_ver;
+      uint8_t signature[8];
+      uint8_t value_len;
+    } tlv;
+    uint8_t magic[12];
+  } header;
+  uint32_t size;
+} __attribute__((packed));
+
+/* Entry */
+struct vpd_entry {
+  uint8_t anchor_string[4];
+  uint8_t entry_cksum;
+  uint8_t entry_length;
+  uint8_t major_ver;
+  uint8_t minor_ver;
+  uint16_t max_size;
+  uint8_t entry_rev;
+  uint8_t format_area[5];
+  uint8_t inter_anchor_string[5];
+  uint8_t inter_anchor_cksum;
+  uint16_t table_length;
+  uint32_t table_address;
+  uint16_t table_entry_count;
+  uint8_t bcd_revision;
+} __attribute__ ((packed));
+
+/* Header */
+struct vpd_header {
+  uint8_t type;
+  uint8_t length;
+  uint16_t handle;
+} __attribute__ ((packed));
+
+/* Type 0 - firmware information */
+struct vpd_table_firmware {
+  uint8_t vendor;
+  uint8_t version;
+  uint16_t start_address;
+  uint8_t release_date;
+  uint8_t rom_size_64k_blocks;
+  uint32_t characteristics;
+  uint8_t extension[2];  /* v2.4+ */
+  uint8_t major_ver;     /* v2.4+ */
+  uint8_t minor_ver;     /* v2.4+ */
+  uint8_t ec_major_ver;  /* v2.4+ */
+  uint8_t ec_minor_ver;  /* v2.4+ */
+} __attribute__ ((packed));
+
+/* Type 1 - system information */
+struct vpd_table_system {
+  uint8_t manufacturer;
+  uint8_t name;
+  uint8_t version;
+  uint8_t serial_number;
+  uint8_t uuid[16];
+  uint8_t wakeup_type;
+  uint8_t sku_number;  /* v2.4+ */
+  uint8_t family;      /* v2.4+ */
+} __attribute__ ((packed));
+
+/* Type 127 - end of table */
+struct vpd_table_eot {
+  struct vpd_header header;
+} __attribute__ ((packed));
+
+/* Type 241 - binary blob pointer */
+struct vpd_table_binary_blob_pointer {
+  uint8_t struct_major_version;
+  uint8_t struct_minor_version;
+  uint8_t vendor;
+  uint8_t description;
+  uint8_t major_version;
+  uint8_t minor_version;
+  uint8_t variant;
+  uint8_t reserved[5];
+  uint8_t uuid[16];
+  uint32_t offset;
+  uint32_t size;
+} __attribute__ ((packed));
+
+/* The length and number of strings defined here is not a limitation of VPD.
+ * These numbers were deemed good enough during development. */
+#define VPD_MAX_STRINGS 10
+#define VPD_MAX_STRING_LENGTH 64
+
+#endif /* __LIB_VPD_TABLES_H__ */
diff --git a/util/vpd/lib/flashrom.c b/util/vpd/lib/flashrom.c
new file mode 100644
index 0000000..67d346c
--- /dev/null
+++ b/util/vpd/lib/flashrom.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lib/flashrom.h"
+
+/* The best choice is PATH_MAX. However, it leads portibility issue.
+ * So, define a long enough length here. */
+#define CMD_BUF_SIZE (4096)
+
+static uint8_t flashrom_cmd[] = "flashrom";
+
+/* The argument for flashrom.
+ *   bus=spi: The VPD data are stored in BIOS flash, which is attached
+ *            to the SPI bus.
+ */
+static uint8_t flashrom_arguments[] = " -p host ";
+
+int flashromFullRead(const char* full_file) {
+  char cmd[CMD_BUF_SIZE];
+  int ret = 0;
+
+  snprintf(cmd, sizeof(cmd), "%s %s -r '%s' >/dev/null 2>&1",
+           flashrom_cmd, flashrom_arguments, full_file);
+  ret = system(cmd);
+  if (ret == 0)
+    return FLASHROM_OK;
+  else
+    return FLASHROM_FAIL;
+}
+
+int flashromPartialRead(const char* part_file, const char* full_file,
+                        const char* partition_name) {
+  char cmd[CMD_BUF_SIZE];
+  int ret = 0;
+
+  snprintf(cmd, sizeof(cmd), "%s %s -i FMAP -i '%s':'%s' "
+                             "-r '%s' >/dev/null 2>&1",
+           flashrom_cmd, flashrom_arguments,
+           partition_name, part_file, full_file);
+  ret = system(cmd);
+  if (ret == 0)
+    return FLASHROM_OK;
+  else
+    return FLASHROM_FAIL;
+}
+
+int flashromPartialWrite(const char* part_file, const char* full_file,
+                         const char* partition_name) {
+  char cmd[CMD_BUF_SIZE];
+  int ret = 0;
+
+  /* write it back */
+  snprintf(cmd, sizeof(cmd),
+           "%s %s -i '%s':'%s' -w '%s' --fast-verify >/dev/null 2>&1",
+           flashrom_cmd, flashrom_arguments,
+           partition_name, part_file, full_file);
+  ret = system(cmd);
+
+  if (ret == 0)
+    return FLASHROM_OK;
+  else
+    return FLASHROM_FAIL;
+}
diff --git a/util/vpd/lib/fmap.c b/util/vpd/lib/fmap.c
new file mode 100644
index 0000000..bb6919c
--- /dev/null
+++ b/util/vpd/lib/fmap.c
@@ -0,0 +1,109 @@
+/* Copyright 2010, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * Ported from mosys project (http://code.google.com/p/mosys/)
+ *
+ * The fmap document can be found in
+ *     https://github.com/dhendrix/flashmap/blob/wiki/FmapSpec.md
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "lib/lib_vpd.h"
+#include "lib/fmap.h"
+
+
+void fmapNormalizeAreaName(char *name) {
+  assert(name);
+
+  while (*name) {
+    if (!(isascii(*name) && isalnum(*name))) {
+      *name = '_';
+    }
+    name++;
+  }
+}
+
+off_t fmapFind(const uint8_t *image, size_t image_len) {
+  off_t offset;
+  uint64_t sig;
+  const struct fmap *header;
+
+  assert(sizeof(sig) == strlen(FMAP_SIGNATURE));
+  memcpy(&sig, FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
+
+  /* Find 4-byte aligned FMAP signature within image.
+   * According to fmap document, http://code.google.com/p/flashmap/wiki/FmapSpec
+   * fmap 1.01 changes to use 64-byte alignment, but 1.00 used 4-byte.
+   * For backward-compatible, uses 4-byte alignment to search.
+   */
+  for (offset = 0; offset + sizeof(*header) <= image_len; offset += 4) {
+    if (0 != memcmp(&image[offset], &sig, sizeof(sig))) {
+      continue;
+    }
+    header = (const struct fmap *)(&image[offset]);
+    if (header->ver_major != FMAP_VER_MAJOR) {
+      continue;
+    }
+    return offset;
+  }
+
+  return -1;
+}
+
+/*
+ *  TODO: need more sanity check for fmap.
+ */
+int fmapGetArea(const char *name, const struct fmap *fmap,
+                uint32_t *offset, uint32_t *size) {
+  int i;
+
+  assert(offset);
+  assert(size);
+
+  /* traverse whole table */
+  for (i = 0; i < fmap->nareas; i++) {
+    if (0 == strcmp(fmap->areas[i].name, name)) {
+      *offset = fmap->areas[i].offset;
+      *size = fmap->areas[i].size;
+      return FMAP_OK;
+    }
+  }
+
+  return FMAP_FAIL;
+}
diff --git a/util/vpd/lib/lib_smbios.c b/util/vpd/lib/lib_smbios.c
new file mode 100644
index 0000000..f27a647
--- /dev/null
+++ b/util/vpd/lib/lib_smbios.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2010 Google Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Ported from mosys project (http://code.google.com/p/mosys/)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <uuid/uuid.h>
+
+#include "lib/math.h"
+#include "lib/lib_smbios.h"
+#include "lib/vpd.h"
+#include "lib/vpd_tables.h"
+
+/*
+ * This function simply looks for the pattern of two adjacent NULL bytes
+ * following the table header.
+ */
+int vpd_sizeof_strings(void *table)
+{
+  uint8_t *p;
+  struct vpd_header *header = table;
+  size_t size = 0, offset = 0;
+  unsigned char cmp[2] = { '\0', '\0' };
+  uint8_t found = 0;
+
+  /*
+   * Search for double NULL. End of strings will be one byte before the
+   * final terminator which indicates end of structure.
+   */
+  for (p = table + header->length - 1; p; p++, offset++) {
+    if (!memcmp(p, cmp, 2)) {
+      found = 1;
+      break;
+    }
+  }
+
+  if (found)
+    size = offset;
+
+  return size;
+}
+
+/**
+ * vpd_crete_eps - create an entry point structure
+ *
+ * @structure_table_len:  structure table len
+ * @num_structures:    number of structures in structure table
+ *
+ * As per SMBIOS spec, the caller must place this structure on a 16-byte
+ * boundary so that the anchor strings can be found.
+ *
+ * returns pointer to newly allocated entry point structure if successful
+ * returns NULL to indicate failure
+ *
+ * FIXME: This function needs to be more intelligent about parsing tables and
+ * obtaining information on its own. These arguments need to go away.
+ */
+struct vpd_entry *vpd_create_eps(uint16_t structure_table_len,
+                                 uint16_t num_structures,
+                                 uint32_t eps_base) {
+  struct vpd_entry *eps = NULL;
+
+  /* size of structure only, no strings */
+  eps = malloc(sizeof(struct vpd_entry));
+  if (!eps)
+    return NULL;
+  memset(eps, 0, sizeof(*eps));
+
+  memcpy(eps->anchor_string, VPD_ENTRY_MAGIC, 4);
+  /* Note: entry point length should be 0x1F for v2.6 */
+  eps->entry_length = sizeof(struct vpd_entry);
+  eps->major_ver = CONFIG_EPS_VPD_MAJOR_VERSION;
+  eps->minor_ver = CONFIG_EPS_VPD_MINOR_VERSION;
+  /* EPS revision based on version 2.1 or later */
+  eps->entry_rev = 0;
+  /* note: nothing done with EPS formatted area */
+
+  /* Intermediate EPS (IEPS) stuff */
+  memcpy(eps->inter_anchor_string, "_DMI_", 5);
+
+  /* FIXME: implement vpd_table_length() and vpd_num_structures() */
+  eps->table_length = structure_table_len;
+
+  /* immediately follow the entry point structure */
+  eps->table_address = eps_base + eps->entry_length;
+
+#ifdef CONFIG_EPS_NUM_STRUCTURES
+  eps->table_entry_count = CONFIG_EPS_NUM_STRUCTURES;
+#else
+  eps->table_entry_count = num_structures;
+#endif
+  eps->bcd_revision = (CONFIG_EPS_VPD_MAJOR_VERSION << 4) |
+                      CONFIG_EPS_VPD_MINOR_VERSION;
+
+  /* calculate IEPS checksum first, then the EPS checksum */
+  eps->inter_anchor_cksum = zero8_csum(&eps->inter_anchor_string[0], 0xf);
+  eps->entry_cksum = zero8_csum((uint8_t *)eps, eps->entry_length);
+
+  return eps;
+}
+
+/**
+ * vpd_append_type127 - append type 127 (end of table) structure
+ *
+ * @handle:  handle for this structure
+ * @buf:  buffer to append to
+ * @len:  length of buffer
+ *
+ * returns total size of newly re-sized buffer if successful
+ * returns <0 to indicate failure
+ */
+int vpd_append_type127(uint16_t handle, uint8_t **buf, size_t len)
+{
+  struct vpd_table_eot *data;
+  size_t total_len, struct_len;
+
+  struct_len = sizeof(struct vpd_table_eot) + 2;  /* double terminator */
+  total_len = len + struct_len;
+  *buf = realloc(*buf, total_len);
+
+  data = (struct vpd_table_eot *)(*buf + len);
+  data->header.type = 127;
+  data->header.length = sizeof(*data);
+  data->header.handle = handle;
+
+  memset(*buf + len + sizeof(*data), 0, 2);  /* double terminator */
+
+  return total_len;
+}
+
+/**
+ * vpd_append_type241 - append type 241 (binary blob pointer) structure
+ *
+ * @handle:  handle for this structure
+ * @buf:  buffer to append to
+ * @len:  length of buffer
+ * @vendor:  blob vendor string
+ * @desc:  blob description string
+ * @variant:  blob variant string
+ *
+ * returns total size of newly re-sized buffer if successful
+ * returns <0 to indicate failure
+ */
+int vpd_append_type241(uint16_t handle, uint8_t **buf,
+                       size_t len, const char *uuid, uint32_t offset,
+                       uint32_t size, const char *vendor,
+                       const char *desc, const char *variant)
+{
+  struct vpd_header *header;
+  struct vpd_table_binary_blob_pointer *data;
+  char *string_ptr;
+  size_t struct_len, total_len;
+  int string_index = 1;
+
+  /* FIXME: Add sanity checking */
+  struct_len = sizeof(struct vpd_header) +
+               sizeof(struct vpd_table_binary_blob_pointer);
+  if (vendor)
+    struct_len += strlen(vendor) + 1;
+  if (desc)
+    struct_len += strlen(desc) + 1;
+  if (variant)
+    struct_len += strlen(variant) + 1;
+  struct_len += 1;  /* structure terminator */
+  total_len = len + struct_len;
+
+  *buf = realloc(*buf, total_len);
+  memset(*buf + len, 0, struct_len);
+
+  header = (struct vpd_header *)(*buf + len);
+  data = (struct vpd_table_binary_blob_pointer *)
+            ((uint8_t *)header + sizeof(*header));
+  string_ptr = (char *)data + sizeof(*data);
+
+  /* fill in structure header details */
+  header->type = VPD_TYPE_BINARY_BLOB_POINTER;
+  header->length = sizeof(*header) + sizeof(*data);
+  header->handle = handle;
+
+  data->struct_major_version = 1;
+  data->struct_minor_version = 0;
+
+  if (vendor) {
+    data->vendor = string_index;
+    string_index++;
+    sprintf(string_ptr, "%s%c", vendor, '\0');
+    string_ptr += strlen(vendor) + 1;
+  }
+
+  if (desc) {
+    data->description = string_index;
+    string_index++;
+    sprintf(string_ptr, "%s%c", desc, '\0');
+    string_ptr += strlen(desc) + 1;
+  }
+
+  data->major_version = 2;
+  data->minor_version = 0;
+
+  if (variant) {
+    data->variant = string_index;
+    string_index++;
+    sprintf(string_ptr, "%s%c", variant, '\0');
+    string_ptr += strlen(variant) + 1;
+  }
+
+  memset(&data->reserved[0], 0, 5);
+
+  if (uuid_parse(uuid, &data->uuid[0]) < 0) {
+    fprintf(stderr, "invalid UUID \"%s\" specified\n", uuid);
+    goto vpd_create_type241_fail;
+  }
+
+  data->offset = offset;
+  data->size = size;
+
+  return total_len;
+
+vpd_create_type241_fail:
+  return -1;
+}
+
+/**
+ * vpd_type241_size - return the size of type 241 structure table.
+ *
+ * Type 241 structure contains 3 variant length of string at end of table.
+ * It is non-trival to get the length by sizeof(vpd_table_binary_blob_pointer).
+ * This function can help by adding 3 strlen(NULL-terminated string).
+ *
+ * @header:  pointer to the start address of this structure table.
+ *
+ * returns total size of this structure table.
+ * returns <0 to indicate failure
+ */
+int vpd_type241_size(struct vpd_header *header) {
+  uint8_t *ptr = (uint8_t*)header;
+  char *str = (char*)ptr + header->length;
+  int length = sizeof(struct vpd_header) +
+               sizeof(struct vpd_table_binary_blob_pointer);
+  int i;
+
+  /* Sanity check */
+  if (header->type != VPD_TYPE_BINARY_BLOB_POINTER) return -1;
+
+  /* Three variant-length strings are following. */
+  for (i = 0; i < 3; ++i) {
+    int len = strlen(str) + 1;
+    length += len;
+    str += len;
+  }
+
+  /* Additional null(0) to indicate end of set, so called structure terminator.
+   * Refer to SMBIOS spec 3.1.3 Text Strings.
+   * However, in VPD 1.x, the mosys would generate a buggy type 241 structure,
+   * which missed the terminator. See mosys between r169 and r211.
+   * The workaround is simple, when counting VPD 241 length, look at the byte
+   * byte after variant string. If it is 0x00, means it's the structure
+   * terminator. If non-zero, then it must be the next table type.
+   */
+  if (ptr[length] == 0) length++;  /* increase only if this is terminator. */
+
+  return length;
+}
+
+void vpd_free_table(void *data)
+{
+  uint8_t *foo = data;
+
+  /* clean-up is trivially simple, for now... */
+  free(foo);
+}
diff --git a/util/vpd/lib/lib_vpd_test.c b/util/vpd/lib/lib_vpd_test.c
new file mode 100644
index 0000000..3ad8679
--- /dev/null
+++ b/util/vpd/lib/lib_vpd_test.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lib/lib_vpd.h"
+
+enum {
+  TEST_OK = 0,
+  TEST_FAIL = 1,
+};
+
+
+#define CU8 (const uint8_t *)  /* for compiler warning on sign bit */
+
+
+int testEncodeLen() {
+  unsigned char output[10];
+  int generated;
+
+  /* fail cases */
+  assert(VPD_ERR_INVALID == encodeLen(-1, output, 0, &generated));
+  assert(VPD_ERR_OVERFLOW == encodeLen(0x7f, output, 0, &generated));
+
+  /* success case - 1 byte output, all zeros */
+  assert(VPD_OK == encodeLen(0x00, output, 1, &generated));
+  assert(1 == generated);
+  assert(0x00 == output[0]);
+
+  /* success case - 1 byte output */
+  assert(VPD_OK == encodeLen(0x7f, output, 1, &generated));
+  assert(1 == generated);
+  assert(0x7f == output[0]);
+
+  /* 2 bytes of output */
+  assert(VPD_ERR_OVERFLOW == encodeLen(0x80, output, 1, &generated));
+  /* success */
+  assert(VPD_OK == encodeLen(0x80, output, 2, &generated));
+  assert(2 == generated);
+  assert(0x81 == output[0]);
+  assert(0x00 == output[1]);
+
+  /* 3 bytes of output */
+  assert(VPD_ERR_OVERFLOW == encodeLen(0x100040, output, 0, &generated));
+  assert(VPD_ERR_OVERFLOW == encodeLen(0x100040, output, 1, &generated));
+  assert(VPD_ERR_OVERFLOW == encodeLen(0x100040, output, 2, &generated));
+  /* success */
+  assert(VPD_OK == encodeLen(0x100040, output, 3, &generated));
+  assert(3 == generated);
+  assert(0xc0 == output[0]);
+  assert(0x80 == output[1]);
+  assert(0x40 == output[2]);
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testDecodeLen() {
+  int32_t length;
+  int32_t consumed;
+
+  { /* max_len is 0. No more char in string. */
+    uint8_t encoded[] = { 0x00 };
+    assert(VPD_ERR_OVERFLOW == decodeLen(0, encoded, &length, &consumed));
+  }
+  { /* just decode one byte */
+    uint8_t encoded[] = { 0x00 };
+    assert(VPD_OK == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+    assert(consumed == sizeof(encoded));
+    assert(length == 0);
+  }
+  { /* just decode one byte */
+    uint8_t encoded[] = { 0x7F };
+    assert(VPD_OK == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+    assert(consumed == sizeof(encoded));
+    assert(length == 0x7F);
+  }
+  { /* more bit is set, but reachs end of string. */
+    uint8_t encoded[] = { 0x80 };
+    assert(VPD_ERR_OVERFLOW == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+  }
+  { /* decode 2 bytes, but reachs end of string. */
+    uint8_t encoded[] = { 0x81, 0x02 };
+    assert(VPD_ERR_OVERFLOW == decodeLen(1, encoded, &length, &consumed));
+  }
+  { /* more bit is set, but reachs end of string. */
+    uint8_t encoded[] = { 0x81, 0x82 };
+    assert(VPD_ERR_OVERFLOW == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+  }
+  { /* decode 2 bytes, normal case */
+    uint8_t encoded[] = { 0x81, 0x02 };
+    assert(VPD_OK == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+    assert(consumed == sizeof(encoded));
+    assert(length == 0x82);
+  }
+  { /* decode 2 bytes, normal case (bot reach end of string). */
+    uint8_t encoded[] = { 0xFF, 0x7F, 0xFF };
+    assert(VPD_OK == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+    assert(consumed == 2);
+    assert(length == 0x3FFF);
+  }
+  { /* weird case, but still valid. */
+    uint8_t encoded[] = { 0x80, 0x00 };
+    assert(VPD_OK == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+    assert(consumed == sizeof(encoded));
+    assert(length == 0);
+  }
+  { /* test max length */
+    uint8_t encoded[] = { 0x87, 0xFF, 0xFF, 0xFF, 0x7F };
+    assert(VPD_OK == decodeLen(sizeof(encoded), encoded, &length, &consumed));
+    assert(consumed == sizeof(encoded));
+    assert(length == 0x7FFFFFFF);
+  }
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testEncodeVpdString() {
+  unsigned char expected[] = {
+    VPD_TYPE_STRING,
+    0x03, 'K', 'E', 'Y',
+    0x05, 'V', 'A', 'L', 'U', 'E',
+  };
+  unsigned char buf[256];
+  int generated = 0;
+
+  assert(VPD_OK ==
+         encodeVpdString(CU8"KEY", CU8"VALUE",
+                         VPD_AS_LONG_AS, sizeof(buf), buf, &generated));
+  assert(sizeof(expected) == generated);
+  assert(!memcmp(expected, buf, generated));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testEncodeVpdStringPadding() {
+  unsigned char expected[] = {
+    VPD_TYPE_STRING,
+    0x03, 'K', 'E', 'Y',
+    0x08, 'V', 'A', 'L', 'U', 'E', '\0', '\0', '\0',
+  };
+  unsigned char buf[256];
+  int generated = 0;
+
+  assert(VPD_OK == encodeVpdString(CU8"KEY", CU8"VALUE",
+                                   8, sizeof(buf), buf, &generated));
+  assert(sizeof(expected) == generated);
+  assert(!memcmp(expected, buf, generated));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testEncodeMultiStrings() {
+  unsigned char expected[] = {
+    VPD_TYPE_STRING,
+    0x03, 'M', 'A', 'C',
+    0x08, '0', '1', '2', '3', '4', '5', '6', '7',
+    VPD_TYPE_STRING,
+    0x07, 'P', 'r', 'o', 'd', '/', 'I', 'd',
+    0x0c, 'M', 'a', 'r', 'i', 'o', '0', '9', '2', '8', '4', '\0', '\0',
+  };
+  unsigned char buf[256];
+  int generated = 0;
+
+  assert(VPD_OK == encodeVpdString(CU8"MAC", CU8"01234567",
+                                   0x08, sizeof(buf), buf, &generated));
+  assert(VPD_OK == encodeVpdString(CU8"Prod/Id", CU8"Mario09284",
+                                   0x0c, sizeof(buf), buf, &generated));
+  assert(sizeof(expected) == generated);
+  assert(!memcmp(expected, buf, generated));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testContainer() {
+  unsigned char expected[] = {
+    VPD_TYPE_STRING,
+    0x03, 'K', 'E', 'Y',
+    0x08, 'V', 'A', 'L', 'U', 'E', '\0', '\0', '\0',
+  };
+  unsigned char buf[256];
+  int generated = 0;
+  struct PairContainer container;
+
+  initContainer(&container);
+  setString(&container, CU8"KEY", CU8"VALUE", 8);
+  encodeContainer(&container, sizeof(buf), buf, &generated);
+
+  assert(sizeof(expected) == generated);
+  assert(!memcmp(expected, buf, generated));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+/* Based on previous test cases:
+ *
+ * KEY=VALUE --> encode --> decode --> expected KEY=VALUE
+ */
+int testDecodeVpdString() {
+  unsigned char expected[] = {
+    VPD_TYPE_STRING,
+    0x03, 'K', 'E', 'Y',
+    0x08, 'V', 'A', 'L', 'U', 'E', '\0', '\0', '\0',
+  };
+  unsigned char buf[256];
+  int consumed = 0;
+  struct PairContainer container;
+
+  initContainer(&container);
+  assert(VPD_OK == decodeToContainer(&container, sizeof(expected), expected,
+                                     &consumed));
+  assert(sizeof(expected) == consumed);
+
+  consumed = 0;
+  encodeContainer(&container, sizeof(buf), buf, &consumed);
+  assert(sizeof(expected) == consumed);
+  assert(!memcmp(expected, buf, consumed));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testDeleteEmptyContainer() {
+  struct PairContainer container;
+
+  initContainer(&container);
+  assert(VPD_FAIL == deleteKey(&container, CU8"NON_EXISTED_KEY"));
+
+  /* still good for add */
+  setString(&container, CU8"FIRST", CU8"1", 8);
+  assert(NULL != findString(&container, CU8"FIRST", NULL));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testDeleteFirstOfOne() {
+  struct PairContainer container;
+
+  initContainer(&container);
+
+  /* test the case that only one string in container. */
+  setString(&container, CU8"FIRST", CU8"1", 8);
+  assert(VPD_FAIL == deleteKey(&container, CU8"NON_EXISTED_KEY"));
+  assert(VPD_OK == deleteKey(&container, CU8"FIRST"));
+  assert(NULL == findString(&container, CU8"FIRST", NULL));
+
+  /* still good for add */
+  setString(&container, CU8"SECOND", CU8"2", 12);
+  assert(NULL != findString(&container, CU8"SECOND", NULL));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testDeleteFirstOfTwo() {
+  struct PairContainer container;
+
+  initContainer(&container);
+
+  /* add 2 and remove the first one */
+  setString(&container, CU8"FIRST", CU8"1", 8);
+  setString(&container, CU8"SECOND", CU8"2", 8);
+  assert(VPD_FAIL == deleteKey(&container, CU8"NON_EXISTED_KEY"));
+  assert(VPD_OK == deleteKey(&container, CU8"FIRST"));
+  assert(NULL == findString(&container, CU8"FIRST", NULL));
+  assert(NULL != findString(&container, CU8"SECOND", NULL));
+
+  /* still good for add */
+  setString(&container, CU8"FIRST", CU8"1", 9);
+  assert(NULL != findString(&container, CU8"FIRST", NULL));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testDeleteSecondOfTwo() {
+  struct PairContainer container;
+
+  initContainer(&container);
+
+  /* add 2 and remove the last one */
+  setString(&container, CU8"FIRST", CU8"1", 8);
+  setString(&container, CU8"SECOND", CU8"2", 8);
+  assert(VPD_FAIL == deleteKey(&container, CU8"NON_EXISTED_KEY"));
+  assert(VPD_OK == deleteKey(&container, CU8"SECOND"));
+  assert(NULL != findString(&container, CU8"FIRST", NULL));
+  assert(NULL == findString(&container, CU8"SECOND", NULL));
+
+  /* still good for add */
+  setString(&container, CU8"SECOND", CU8"2", 5);
+  assert(NULL != findString(&container, CU8"SECOND", NULL));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+int testDeleteSecondOfThree() {
+  unsigned char expected[] = {
+    VPD_TYPE_STRING,
+    0x05, 'F', 'I', 'R', 'S', 'T',
+    0x03, '1', '\0', '\0',
+    VPD_TYPE_STRING,
+    0x05, 'T', 'H', 'I', 'R', 'D',
+    0x04, '3', '\0', '\0', '\0',
+  };
+  unsigned char buf[256];
+  int generated = 0;
+  struct PairContainer container;
+
+  initContainer(&container);
+
+  /* add 3 and remove the middle one */
+  setString(&container, CU8"FIRST", CU8"1", 3);
+  setString(&container, CU8"SECOND", CU8"2", 2);
+  setString(&container, CU8"THIRD", CU8"3", 4);
+  assert(VPD_FAIL == deleteKey(&container, CU8"NON_EXISTED_KEY"));
+  assert(VPD_OK == deleteKey(&container, CU8"SECOND"));
+  assert(NULL != findString(&container, CU8"FIRST", NULL));
+  assert(NULL == findString(&container, CU8"SECOND", NULL));
+  assert(NULL != findString(&container, CU8"THIRD", NULL));
+
+  /* expect the middle one is removed. */
+  encodeContainer(&container, sizeof(buf), buf, &generated);
+  assert(sizeof(expected) == generated);
+  assert(!memcmp(expected, buf, generated));
+
+  /* still good if we delete all */
+  assert(VPD_OK == deleteKey(&container, CU8"THIRD"));
+  assert(VPD_OK == deleteKey(&container, CU8"FIRST"));
+
+  /* still good for add */
+  setString(&container, CU8"FORTH", CU8"4", 4);
+  assert(NULL != findString(&container, CU8"FORTH", NULL));
+  setString(&container, CU8"FIFTH", CU8"5", 5);
+  assert(NULL != findString(&container, CU8"FIFTH", NULL));
+
+  printf("[PASS] %s()\n", __FUNCTION__);
+  return TEST_OK;
+}
+
+
+
+int main() {
+  assert(TEST_OK == testEncodeLen());
+  assert(TEST_OK == testDecodeLen());
+  assert(TEST_OK == testEncodeVpdString());
+  assert(TEST_OK == testEncodeVpdStringPadding());
+  assert(TEST_OK == testEncodeMultiStrings());
+  assert(TEST_OK == testContainer());
+  assert(TEST_OK == testDecodeVpdString());
+  assert(TEST_OK == testDeleteEmptyContainer());
+  assert(TEST_OK == testDeleteFirstOfOne());
+  assert(TEST_OK == testDeleteFirstOfTwo());
+  assert(TEST_OK == testDeleteSecondOfTwo());
+  assert(TEST_OK == testDeleteSecondOfThree());
+
+  printf("SUCCESS!\n");
+  return 0;
+}
diff --git a/util/vpd/lib/math.c b/util/vpd/lib/math.c
new file mode 100644
index 0000000..c5b8063
--- /dev/null
+++ b/util/vpd/lib/math.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * math.c: implementations of some numerical utilities
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "lib/math.h"
+
+/*
+ * ctz - count trailing zeros
+ *
+ * @u:  Bit vector to count trailing zeros in.
+ *
+ * Counts bit positions of lower significance than that of the least significant
+ * bit set. Based off of an algorithm from:
+ * http://graphics.stanford.edu/~seander/bithacks.html
+ *
+ * returns count of trailing zeros
+ */
+int ctz(unsigned long long int u)
+{
+  int num_zeros;
+  union {
+    float f;
+    uint32_t i;
+  } alias;
+
+  if (u == 0)   /* The algorithm will return -127 on this condition */
+    return 0;
+
+  alias.f = (float)(u & (~u + 1));
+  num_zeros = (alias.i >> 23) - 0x7f;
+
+  return num_zeros;
+}
+
+/*
+ * logbase2 - Return log base 2 of the absolute value of n (2^r = abs(n)) of an
+ * integer by using a cast to float method (Requires IEEE-754).
+ *
+ * Note: We could just use log2() but that would require messing with our
+ * compilation and linking options and hacking around the n = 0 case in other
+ * areas of the code.
+ *
+ * @n:  The number to find the log base 2 of
+ *
+ * returns log2(n) if successful
+ */
+int logbase2(int n)
+{
+  float f;
+  int r;
+
+  /* This algorithm fails (Returns negative infinity) if n = 0. We'll be
+   * using it mostly in the context of CPU numbers, so we'll take the
+   * liberty of returning 0 instead of aborting */
+  if (n == 0)
+    return 0;
+
+  f = (float)n;
+  memcpy(&r, &f, sizeof(n));
+
+  /* Isolate exponent and un-bias the exponent (Subtract +128) */
+  r = ((r & 0x7F800000) >> 23) - 0x80;
+
+  return r + 1;
+}
+
+/*
+ * rolling8_csum  -  Bytewise rolling summation "checksum" of a buffer
+ *
+ * @buf:  buffer to sum
+ * @len:  length of buffer
+ */
+uint8_t rolling8_csum(uint8_t *buf, size_t len)
+{
+  size_t i;
+  uint8_t sum = 0;
+
+  for (i = 0; i < len; ++i)
+    sum += buf[i];
+  return sum;
+}
+
+/*
+  * zero8_csum - Calculates 8-bit zero-sum checksum
+  *
+  * @buf:  input buffer
+  * @len:  length of buffer
+  *
+  * The summation of the bytes in the array and the csum will equal zero
+  * for 8-bit data size.
+  *
+  * returns checksum to indicate success
+  */
+uint8_t zero8_csum(uint8_t *buf, size_t len)
+{
+  uint8_t *u = buf;
+  uint8_t csum = 0;
+
+  while (u < buf + len) {
+    csum += *u;
+    u++;
+  }
+
+  return (0x100 - csum);
+}
diff --git a/util/vpd/lib/vpd_container.c b/util/vpd/lib/vpd_container.c
new file mode 100644
index 0000000..78f8e15
--- /dev/null
+++ b/util/vpd/lib/vpd_container.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lib/lib_vpd.h"
+
+
+#ifndef MIN
+#define MIN(a, b) ((a < b) ? a : b)
+#endif
+
+
+/***********************************************************************
+ * Container helpers
+ ***********************************************************************/
+void initContainer(struct PairContainer *container) {
+  container->first = NULL;
+}
+
+
+/*
+ * Returns the pointer to the 'key' entry.
+ * Returns NULL if the key is not found in 'container'.
+ *
+ * If 'prev_next' is not NULL, findString() stores the address of the "next"
+ * member of the StringPair prior to the returned StringPair (the special case
+ * is &container->first if first StringPair matches 'key'). This is for linked
+ * list manipulation. If 'prev_next' is NULL, findString() would just ignore
+ * it.
+ */
+struct StringPair *findString(struct PairContainer *container,
+                              const uint8_t *key,
+                              struct StringPair ***prev_next) {
+  struct StringPair *current;
+
+  if (prev_next) {
+    *prev_next = &container->first;
+  }
+
+  for (current = container->first; current; current = current->next) {
+    if (!strcmp((char*)key, (char*)current->key)) {
+      return current;
+    }
+    if (prev_next) {
+      *prev_next = &current->next;
+    }
+  }
+  return NULL;
+}
+
+/* Just a helper function for setString() */
+static void fillStringPair(struct StringPair *pair,
+                           const uint8_t *key,
+                           const uint8_t *value,
+                           const int pad_len) {
+  pair->key = malloc(strlen((char*)key) + 1);
+  assert(pair->key);
+  strcpy((char*)pair->key, (char*)key);
+  pair->value = malloc(strlen((char*)value) + 1);
+  strcpy((char*)pair->value, (char*)value);
+  pair->pad_len = pad_len;
+}
+
+/* If key is already existed in container, its value will be replaced.
+ * If not existed, creates new entry in container.
+ */
+void setString(struct PairContainer *container,
+               const uint8_t *key,
+               const uint8_t *value,
+               const int pad_len) {
+  struct StringPair *found;
+
+  found = findString(container, key, NULL);
+  if (found) {
+    free(found->key);
+    free(found->value);
+    fillStringPair(found, key, value, pad_len);
+  } else {
+    struct StringPair *new_pair = malloc(sizeof(struct StringPair));
+    assert(new_pair);
+    memset(new_pair, 0, sizeof(struct StringPair));
+
+    fillStringPair(new_pair, key, value, pad_len);
+
+    /* append this pair to the end of list. to keep the order */
+    if ((found = container->first)) {
+      while (found->next) found = found->next;
+      found->next = new_pair;
+    } else {
+      container->first = new_pair;
+    }
+    new_pair->next = NULL;
+  }
+}
+
+
+/*
+ * Remove a key.
+ * Returns VPD_OK if deleted successfully. Otherwise, VPD_FAIL.
+ */
+vpd_err_t deleteKey(struct PairContainer *container,
+                    const uint8_t *key) {
+  struct StringPair *found, **prev_next;
+
+  found = findString(container, key, &prev_next);
+  if (found) {
+    free(found->key);
+    free(found->value);
+
+    /* remove the 'found' from the linked list. */
+    assert(prev_next);
+    *prev_next = found->next;
+    free(found);
+
+    return VPD_OK;
+  } else {
+    return VPD_FAIL;
+  }
+}
+
+
+/*
+ * Returns number of pairs in container.
+ */
+int lenOfContainer(const struct PairContainer *container) {
+  int count;
+  struct StringPair *current;
+
+  for (count = 0, current = container->first;
+       current;
+       count++, current = current->next);
+
+  return count;
+}
+
+
+/* Iterate src container and setString() in dst.
+ * so that if key is duplicate, the one in dst is overwritten.
+ */
+void mergeContainer(struct PairContainer *dst,
+                    const struct PairContainer *src) {
+  struct StringPair *current;
+
+  for (current = src->first; current; current = current->next) {
+    setString(dst, current->key, current->value, current->pad_len);
+  }
+}
+
+
+int subtractContainer(struct PairContainer *dst,
+                       const struct PairContainer *src) {
+  struct StringPair *current;
+  int count = 0;
+
+  for (current = src->first; current; current = current->next) {
+    if (VPD_OK == deleteKey(dst, current->key))
+      count++;
+  }
+
+  return count;
+}
+
+
+vpd_err_t encodeContainer(const struct PairContainer *container,
+                          const int max_buf_len,
+                          uint8_t *buf,
+                          int *generated) {
+  struct StringPair *current;
+
+  for (current = container->first; current; current = current->next) {
+    if (VPD_OK != encodeVpdString(current->key,
+                                  current->value,
+                                  current->pad_len,
+                                  max_buf_len,
+                                  buf,
+                                  generated)) {
+      return VPD_FAIL;
+    }
+  }
+  return VPD_OK;
+}
+
+static vpd_err_t callbackDecodeToContainer(const uint8_t *key,
+                                           int32_t key_len,
+                                           const uint8_t *value,
+                                           int32_t value_len,
+                                           void *arg) {
+  struct PairContainer *container = (struct PairContainer*)arg;
+  uint8_t *key_string = (uint8_t*)malloc(key_len + 1),
+          *value_string = (uint8_t*)malloc(value_len + 1);
+  assert(key_string && value_string);
+  memcpy(key_string, key, key_len);
+  memcpy(value_string, value, value_len);
+  key_string[key_len] = '\0';
+  value_string[value_len] = '\0';
+  setString(container, key_string, value_string, value_len);
+  return VPD_OK;
+}
+
+vpd_err_t decodeToContainer(struct PairContainer *container,
+                            const int32_t max_len,
+                            const uint8_t *input_buf,
+                            int32_t *consumed) {
+  return decodeVpdString(max_len, input_buf, consumed,
+                         callbackDecodeToContainer, (void*)container);
+}
+
+vpd_err_t setContainerFilter(struct PairContainer *container,
+                             const uint8_t *filter) {
+  struct StringPair *str;
+
+  for (str = container->first; str; str = str->next) {
+    if (filter) {
+      /*
+       * TODO(yjlou):
+       * Now, we treat the inputing filter string as plain string.
+       * Will support regular expression syntax in future if needed.
+       */
+      if (strcmp((char*)str->key, (char*)filter)) {
+        str->filter_out = 1;
+      }
+    } else {
+      str->filter_out = 0;
+    }
+  }
+  return VPD_OK;
+}
+
+
+/*
+ * A helper function to append a sequence of bytes to the given buffer.  If
+ * the buffer size is not enough, this function will return VPD_ERR_OVERFLOW;
+ * otherwise it will return VPD_OK.
+ */
+static vpd_err_t _appendToBuf(const void *buf_to_append,
+                              int len,
+                              const int max_buf_len,
+                              uint8_t *buf,
+                              int *generated) {
+  if (*generated + len > max_buf_len) return VPD_ERR_OVERFLOW;
+  memcpy(&buf[*generated], buf_to_append, len);
+  *generated += len;
+  return VPD_OK;
+}
+
+
+/*
+ * A helper function to resolve the number of bytes to be exported for the
+ * value field of an instance of StringPair.
+ */
+static int _getStringPairValueLen(const struct StringPair *str) {
+  int len = strlen((const char*)(str->value));
+  return VPD_AS_LONG_AS == str->pad_len ? len : MIN(str->pad_len, len);
+}
+
+
+/* A helper function to export an instance of StringPair to the given buffer. */
+static vpd_err_t _exportStringPairKeyValue(const struct StringPair *str,
+                                           const int max_buf_len,
+                                           uint8_t *buf,
+                                           int *generated) {
+  const void *strs[5] = {"\"", str->key, "\"=\"", str->value, "\"\n"};
+  const int lens[5] = {
+      1, strlen((const char*)str->key), 3, _getStringPairValueLen(str), 2};
+
+  int retval;
+  int i;
+
+  for (i = 0; i < sizeof(lens) / sizeof(int); ++i) {
+    retval = _appendToBuf(strs[i], lens[i], max_buf_len, buf, generated);
+    if (VPD_OK != retval) {
+      break;
+    }
+  }
+
+  return retval;
+}
+
+
+/*
+ * A helper function to escape the special character in a string and then append
+ * the result into the buffer.
+ */
+static vpd_err_t _appendToBufWithShellEscape(const char *str_to_export,
+                                             const int max_buf_len,
+                                             uint8_t *buf,
+                                             int *generated) {
+  int len = strlen(str_to_export);
+  int i;
+  int retval;
+  for (i = 0; i < len; ++i) {
+    if ('\'' == str_to_export[i]) {
+      retval = _appendToBuf("'\"'\"'", 5, max_buf_len, buf, generated);
+    } else {
+      retval = _appendToBuf(str_to_export + i, 1,
+                            max_buf_len, buf, generated);
+    }
+    if (VPD_OK != retval) return retval;
+  }
+  return VPD_OK;
+}
+
+
+/*
+ * A helper function to export an instance of StringPair to the given buffer
+ * as the arguments for the vpd commandline tool.
+ */
+static vpd_err_t _exportStringPairAsParameter(const struct StringPair *str,
+                                              const int max_buf_len,
+                                              uint8_t *buf,
+                                              int *generated) {
+  int retval;
+
+  {
+    char extra_params[32];
+    snprintf(extra_params, sizeof(extra_params), "    -p %d -s ", str->pad_len);
+    retval = _appendToBuf(extra_params, strlen(extra_params),
+                          max_buf_len, buf, generated);
+    if (VPD_OK != retval) return retval;
+  }
+
+  if (*generated + 1 > max_buf_len) return VPD_ERR_OVERFLOW;
+  buf[(*generated)++] = '\'';
+
+  retval = _appendToBufWithShellEscape(
+      (const char*)str->key, max_buf_len, buf, generated);
+  if (VPD_OK != retval) return retval;
+
+  if (*generated + 1 > max_buf_len) return VPD_ERR_OVERFLOW;
+  buf[(*generated)++] = '=';
+
+  retval = _appendToBufWithShellEscape(
+      (const char*)str->value, max_buf_len, buf, generated);
+  if (VPD_OK != retval) return retval;
+
+  retval = _appendToBuf("' \\\n", 4, max_buf_len, buf, generated);
+
+  return retval;
+}
+
+
+/*
+ * A helper function to export an instance of StringPair to the given buffer
+ * in a null terminate format, i.e. "<key>=<value>\0"
+ */
+static vpd_err_t _exportStringPairNullTerminate(const struct StringPair *str,
+                                                const int max_buf_len,
+                                                uint8_t *buf,
+                                                int *generated) {
+  int retval;
+
+  retval = _appendToBuf(str->key, strlen((const char*)str->key),
+                        max_buf_len, buf, generated);
+  if (VPD_OK != retval) return retval;
+
+  if (*generated + 1 > max_buf_len) return VPD_ERR_OVERFLOW;
+  buf[(*generated)++] = '=';
+
+  retval = _appendToBuf(str->value, _getStringPairValueLen(str),
+                        max_buf_len, buf, generated);
+  if (VPD_OK != retval) return retval;
+
+  if (*generated + 1 > max_buf_len) return VPD_ERR_OVERFLOW;
+  buf[(*generated)++] = '\0';
+
+  return VPD_OK;
+}
+
+
+/* Export the value field of the instance of StringPair. */
+vpd_err_t exportStringValue(const struct StringPair *str,
+                            const int max_buf_len,
+                            uint8_t *buf,
+                            int *generated) {
+  assert(generated);
+
+  return _appendToBuf(str->value, _getStringPairValueLen(str),
+                      max_buf_len, buf, generated);
+}
+
+
+/* Export the container content with human-readable text. */
+vpd_err_t exportContainer(const int export_type,
+                          const struct PairContainer *container,
+                          const int max_buf_len,
+                          uint8_t *buf,
+                          int *generated) {
+  struct StringPair *str;
+  int index;
+  int retval;
+
+  assert(generated);
+  index = *generated;
+
+  for (str = container->first; str; str = str->next) {
+    if (str->filter_out)
+      continue;
+
+    if (VPD_EXPORT_KEY_VALUE == export_type) {
+      retval = _exportStringPairKeyValue(str, max_buf_len, buf, &index);
+    } else if (VPD_EXPORT_AS_PARAMETER == export_type) {
+      retval = _exportStringPairAsParameter(str, max_buf_len, buf, &index);
+    } else if (VPD_EXPORT_NULL_TERMINATE == export_type) {
+      retval = _exportStringPairNullTerminate(str, max_buf_len, buf, &index);
+    } else {
+      /* this block shouldn't be reached */
+      assert(0);
+    }
+    if (VPD_OK != retval) return retval;
+  }
+
+  *generated = index;
+
+  return VPD_OK;
+}
+
+void destroyContainer(struct PairContainer *container) {
+  struct StringPair *current;
+
+  for (current = container->first; current;) {
+    struct StringPair *next;
+
+    if (current->key) free(current->key);
+    if (current->value) free(current->value);
+    next = current->next;
+    free(current);
+    current = next;
+  }
+}
diff --git a/util/vpd/lib/vpd_decode.c b/util/vpd/lib/vpd_decode.c
new file mode 100644
index 0000000..7f266fb
--- /dev/null
+++ b/util/vpd/lib/vpd_decode.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+#include <assert.h>
+#include "lib_vpd.h"
+
+vpd_err_t decodeLen(
+    const int32_t max_len,
+    const uint8_t *in,
+    int32_t *length,
+    int32_t *decoded_len) {
+  uint8_t more;
+  int i = 0;
+
+  assert(length);
+  assert(decoded_len);
+
+  *length = 0;
+  do {
+    if (i >= max_len) return VPD_ERR_OVERFLOW;
+    more = in[i] & 0x80;
+    *length <<= 7;
+    *length |= in[i] & 0x7f;
+    ++i;
+  } while (more);
+
+  *decoded_len = i;
+
+  return VPD_OK;
+}
+
+/* Sequentially decodes type, key, and value.
+ */
+vpd_err_t decodeVpdString(
+    const int32_t max_len,
+    const uint8_t *input_buf,
+    int32_t *consumed,
+    VpdDecodeCallback callback,
+    void *callback_arg) {
+  int type;
+  int32_t key_len, value_len;
+  int32_t decoded_len;
+  const uint8_t *key, *value;
+  vpd_err_t retval;
+
+  /* type */
+  if (*consumed >= max_len)
+    return VPD_ERR_OVERFLOW;
+
+  type = input_buf[*consumed];
+  switch (type) {
+    case VPD_TYPE_INFO:
+    case VPD_TYPE_STRING:
+      (*consumed)++;
+
+      /* key */
+      retval = decodeLen(max_len - *consumed, &input_buf[*consumed],
+                         &key_len, &decoded_len);
+      if (VPD_OK != retval)
+        return retval;
+
+      if (*consumed + decoded_len >= max_len)
+        return VPD_ERR_OVERFLOW;
+
+      *consumed += decoded_len;
+      key = &input_buf[*consumed];
+      *consumed += key_len;
+
+      /* value */
+      retval = decodeLen(max_len - *consumed, &input_buf[*consumed],
+                         &value_len, &decoded_len);
+      if (VPD_OK != retval)
+        return retval;
+
+      if (*consumed + decoded_len > max_len)
+        return VPD_ERR_OVERFLOW;
+
+      *consumed += decoded_len;
+      value = &input_buf[*consumed];
+      *consumed += value_len;
+
+      if (type == VPD_TYPE_STRING)
+        return callback(key, key_len, value, value_len, callback_arg);
+
+      return VPD_OK;
+
+    default:
+      return VPD_ERR_INVALID;
+      break;
+  }
+  return VPD_OK;
+}
diff --git a/util/vpd/lib/vpd_encode.c b/util/vpd/lib/vpd_encode.c
new file mode 100644
index 0000000..b8a35d1
--- /dev/null
+++ b/util/vpd/lib/vpd_encode.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lib_vpd.h"
+
+
+/* Encodes the len into multiple bytes with the following format.
+ *
+ *    7   6 ............ 0
+ *  +----+------------------+
+ *  |More|      Length      |  ...
+ *  +----+------------------+
+ *
+ * Returns fail if the buffer is not long enough.
+ */
+vpd_err_t encodeLen(
+    const int32_t len,
+    uint8_t *encode_buf,
+    const int32_t max_len,
+    int32_t *encoded_len) {
+  unsigned int shifting;
+  unsigned int reversed_7bits = 0;
+  int out_index = 0;
+
+  assert(encoded_len);
+
+  if (len < 0) return VPD_ERR_INVALID;
+  shifting = len;
+  /* reverse the len for every 7-bit. The little endian. */
+  for (*encoded_len = 0; shifting; (*encoded_len)++) {
+    reversed_7bits = (reversed_7bits << 7) | (shifting & 0x7f);
+    shifting >>= 7;
+  }
+  if (*encoded_len > max_len) return VPD_ERR_OVERFLOW;
+  if (!*encoded_len) *encoded_len = 1;  /* output at least 1 byte */
+
+  /* Output in reverse order, now big endian. */
+  while (out_index < *encoded_len) {
+    /* always set MORE flag */
+    encode_buf[out_index++] = 0x80 | (reversed_7bits & 0x7f);
+    reversed_7bits >>= 7;
+  }
+  encode_buf[out_index - 1] &= 0x7f;  /* clear the MORE flag in last byte */
+
+  return VPD_OK;
+}
+
+/*  Encodes the terminator.
+ */
+vpd_err_t encodeVpdTerminator(
+    const int max_buffer_len,
+    uint8_t *output_buf,
+    int *generated_len) {
+  assert(generated_len);
+
+  if (*generated_len >= max_buffer_len) return VPD_FAIL;
+
+  output_buf += *generated_len;  /* move cursor to end of string */
+  *(output_buf++) = VPD_TYPE_TERMINATOR;
+  (*generated_len)++;
+
+  return VPD_OK;
+}
+
+/* Encodes a string with padding support. */
+vpd_err_t encodeVpdString(
+    const uint8_t *key,
+    const uint8_t *value,
+    const int pad_value_len,
+    const int max_buffer_len,
+    uint8_t *output_buf,
+    int *generated_len) {
+  int key_len, value_len;
+  int ret_len;
+  int pad_len = 0;
+  vpd_err_t retval;
+
+  assert(generated_len);
+
+  key_len = strlen((char*)key);
+  value_len = strlen((char*)value);
+  output_buf += *generated_len;  /* move cursor to end of string */
+
+  /* encode type */
+  if (*generated_len >= max_buffer_len) return VPD_ERR_OVERFLOW;
+  *(output_buf++) = VPD_TYPE_STRING;
+  (*generated_len)++;
+
+  /* encode key len */
+  if (VPD_OK != encodeLen(key_len, output_buf,
+                          max_buffer_len - *generated_len, &ret_len))
+    return VPD_FAIL;
+  output_buf += ret_len;
+  *generated_len += ret_len;
+  /* encode key string */
+  if (*generated_len + key_len > max_buffer_len) return VPD_ERR_OVERFLOW;
+  memcpy(output_buf, key, key_len);
+  output_buf += key_len;
+  *generated_len += key_len;
+
+  /* count padding length */
+  if (pad_value_len != VPD_AS_LONG_AS) {
+    if (value_len < pad_value_len) {
+      pad_len = pad_value_len - value_len;
+    } else {
+      value_len = pad_value_len;
+    }
+  }
+
+  /* encode value len */
+  retval = encodeLen(value_len + pad_len, output_buf,
+                     max_buffer_len - *generated_len, &ret_len);
+  if (VPD_OK != retval)
+    return retval;
+  output_buf += ret_len;
+  *generated_len += ret_len;
+  /* encode value string */
+  if (*generated_len + value_len > max_buffer_len) return VPD_ERR_OVERFLOW;
+  memcpy(output_buf, value, value_len);
+  output_buf += value_len;
+  *generated_len += value_len;
+  /* encode padding (if applicable) */
+  if (*generated_len + pad_len > max_buffer_len) return VPD_ERR_OVERFLOW;
+  memset(output_buf, 0, pad_len);
+  output_buf += pad_len;
+  *generated_len += pad_len;
+
+  return VPD_OK;
+}
diff --git a/util/vpd/vpd.c b/util/vpd/vpd.c
new file mode 100644
index 0000000..bdf948e
--- /dev/null
+++ b/util/vpd/vpd.c
@@ -0,0 +1,1202 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+#include "lib/flashrom.h"
+#include "lib/fmap.h"
+#include "lib/lib_vpd.h"
+#include "lib/lib_smbios.h"
+#include "lib/vpd.h"
+#include "lib/vpd_tables.h"
+
+/* The buffer length. Right now the VPD partition size on flash is 128KB. */
+#define BUF_LEN (128 * 1024)
+
+/* The comment shown in the begin of --sh output */
+#define SH_COMMENT                                                      \
+  "#\n"                                                                 \
+  "# Prepend 'vpd -O' before this text to always reset VPD content.\n"  \
+  "# Append more -s at end to set additional key/values.\n"             \
+  "# Or an empty line followed by other commands.\n"                    \
+  "#\n"
+
+/* Forward reference(s) */
+static uint8_t *readFileContent(const char* filename, uint32_t *filesize);
+
+/* Linked list to track temporary files
+ */
+struct TempfileNode {
+  char *filename;
+  struct TempfileNode *next;
+} *tempfile_list = NULL;
+
+
+enum FileFlag {
+  HAS_SPD = (1 << 0),
+  HAS_VPD_2_0 = (1 << 1),
+  HAS_VPD_1_2 = (1 << 2),
+  /* TODO(yjlou): refine those variables in main() to here:
+   *              write_back_to_flash, overwrite_it, modified. */
+} file_flag = 0;
+
+
+/* 2 containers:
+ *   file:      stores decoded pairs from file.
+ *   argument:  stores parsed pairs from command arguments.
+ */
+struct PairContainer file;
+struct PairContainer set_argument;
+struct PairContainer del_argument;
+
+int export_type = VPD_EXPORT_KEY_VALUE;
+
+/* The current padding length value.
+ * Default: VPD_AS_LONG_AS
+ */
+int pad_value_len = VPD_AS_LONG_AS;
+
+/* The output buffer */
+uint8_t buf[BUF_LEN];
+int buf_len = 0;
+int max_buf_len = sizeof(buf);
+
+/* The EPS base address used to fill the EPS table entry.
+ * If the VPD partition can be found in fmap, this points to the starting
+ * offset of VPD partition. If not found, this is used to be the base address
+ * to increase SPD and VPD 2.0 offset fields.
+ *
+ * User can overwrite this by -E argument.
+ */
+#define UNKNOWN_EPS_BASE ((uint32_t)-1)
+uint32_t eps_base = UNKNOWN_EPS_BASE;
+int eps_base_force_specified = 0;  /* a bool value to indicate if -E argument
+                                    * is given. */
+
+/* the fmap name of VPD. */
+char fmap_vpd_area_name[FMAP_STRLEN] = "RO_VPD";
+
+/* If found_vpd, replace the VPD partition when saveFile().
+ * If not found, always create new file when saveFlie(). */
+int found_vpd = 0;
+
+/* The VPD partition offset and size in buf[]. The whole partition includes:
+ *
+ *   SMBIOS EPS
+ *   SMBIOS tables[]
+ *   SPD
+ *   VPD 2.0 data
+ *
+ */
+uint32_t vpd_offset = 0, vpd_size;  /* The whole partition */
+/* Below offset are related to vpd_offset and assume positive.
+ * Those are used in saveFile() to write back data. */
+uint32_t eps_offset = 0;  /* EPS's starting address. Tables[] is following. */
+uint32_t spd_offset = GOOGLE_SPD_OFFSET;  /* SPD address .*/
+off_t vpd_2_0_offset = GOOGLE_VPD_2_0_OFFSET;  /* VPD 2.0 data address. */
+
+/* This points to the SPD data if it is availiable when loadFile().
+ * The memory is allocated in loadFile(), will be used in saveFile(),
+ * and freed at end of main(). */
+uint8_t *spd_data = NULL;
+int32_t spd_len = 256;  /* max value for DDR3 */
+
+/* Creates a temporary file and return the filename, or NULL for any failure.
+ */
+const char *myMkTemp() {
+  char tmp_file[] = "/tmp/vpd.flashrom.XXXXXX";
+  struct TempfileNode *node;
+  int fd;
+
+  fd = mkstemp(tmp_file);
+  if (fd < 0) {
+    fprintf(stderr, "mkstemp(%s) failed\n", tmp_file);
+    return NULL;
+  }
+
+  close(fd);
+  node = (struct TempfileNode*)malloc(sizeof(struct TempfileNode));
+  assert(node);
+  node->next = tempfile_list;
+  node->filename = strdup(tmp_file);
+  assert(node->filename);
+  tempfile_list = node;
+
+  return node->filename;
+}
+
+
+/*  Erases all files created by myMkTemp
+ */
+void cleanTempFiles() {
+  struct TempfileNode *node;
+
+  while (tempfile_list) {
+    node = tempfile_list;
+    tempfile_list = node->next;
+    if (unlink(node->filename) < 0) {
+      fprintf(stderr, "warning: failed removing temporary file: %s\n",
+              node->filename);
+    }
+    free(node->filename);
+    free(node);
+  }
+}
+
+
+/*  Given the offset of blob block (related to the first byte of EPS) and
+ *  the size of blob, the is function generates an SMBIOS ESP.
+ */
+vpd_err_t buildEpsAndTables(
+    const int size_blob,
+    const int max_buf_len,
+    unsigned char *buf,
+    int *generated) {
+  struct vpd_entry *eps;
+  unsigned char *table = NULL;          /* the structure table */
+  int table_len = 0;
+  int num_structures = 0;
+  vpd_err_t retval = VPD_OK;
+
+  assert(buf);
+  assert(generated);
+  assert(eps_base != UNKNOWN_EPS_BASE);
+
+  buf += *generated;
+
+  /* Generate type 241 - SPD data */
+  table_len = vpd_append_type241(0, &table, table_len,
+                                 GOOGLE_SPD_UUID,
+                                 eps_base + GOOGLE_SPD_OFFSET,
+                                 spd_len,  /* Max length for DDR3 */
+                                 GOOGLE_SPD_VENDOR,
+                                 GOOGLE_SPD_DESCRIPTION,
+                                 GOOGLE_SPD_VARIANT);
+  if (table_len < 0) {
+    retval = VPD_FAIL;
+    goto error_1;
+  }
+  num_structures++;
+
+  /*
+   * TODO(hungte) Once most systems have been updated to support VPD_INFO
+   * record, we can remove the +sizeof(google_vpd_info) hack.
+   */
+
+  /* Generate type 241 - VPD 2.0 */
+  table_len = vpd_append_type241(1, &table, table_len,
+                                 GOOGLE_VPD_2_0_UUID,
+                                 (eps_base + GOOGLE_VPD_2_0_OFFSET +
+                                  sizeof(struct google_vpd_info)),
+                                 size_blob,
+                                 GOOGLE_VPD_2_0_VENDOR,
+                                 GOOGLE_VPD_2_0_DESCRIPTION,
+                                 GOOGLE_VPD_2_0_VARIANT);
+  if (table_len < 0) {
+    retval = VPD_FAIL;
+    goto error_1;
+  }
+  num_structures++;
+
+  /* Generate type 127 */
+  table_len = vpd_append_type127(2, &table, table_len);
+  if (table_len < 0) {
+    retval = VPD_FAIL;
+    goto error_1;
+  }
+  num_structures++;
+
+  /* Generate EPS */
+  eps = vpd_create_eps(table_len, num_structures, eps_base);
+  if ((*generated + eps->entry_length) > max_buf_len) {
+    retval = VPD_FAIL;
+    goto error_2;
+  }
+
+  /* Copy EPS back to buf */
+  memcpy(buf, eps, eps->entry_length);
+  buf += eps->entry_length;
+  *generated += eps->entry_length;
+
+  /* Copy tables back to buf */
+  if ((*generated + table_len) > max_buf_len) {
+    retval = VPD_FAIL;
+    goto error_2;
+  }
+  memcpy(buf, table, table_len);
+  buf += table_len;
+  *generated += table_len;
+
+error_2:
+  free(eps);
+error_1:
+  free(table);
+
+  return retval;
+}
+
+static int isbase64(uint8_t c)
+{
+  return isalnum(c) || (c == '+') || (c == '/') || (c == '=');
+}
+
+static uint8_t *read_string_from_file(const char *file_name)
+{
+
+  uint32_t i, j, file_size;
+  uint8_t *file_buffer;
+
+  file_buffer = readFileContent(file_name, &file_size);
+
+  if (!file_buffer)
+    return NULL; /* The error has been reported already. */
+
+  /*
+   * Is the contents a proper base64 blob? Verify it and drop EOL characters
+   * along the way, this will help when displaying the contents.
+   */
+  for (i = 0, j = 0; i < file_size; i++) {
+    uint8_t c = file_buffer[i];
+
+    if ((c == 0xa) || (c == 0xd))
+      continue;  /* Skip EOL characters */
+
+    if (!isbase64(c)) {
+      fprintf(stderr, "[ERROR] file %s is not in base64 format (%c at %d)\n",
+              file_name, c, i);
+      free(file_buffer);
+      return NULL;
+    }
+    file_buffer[j++] = c;
+  }
+  file_buffer[j] = '\0';
+
+  return (uint8_t *)file_buffer;
+}
+
+
+/*
+ * Check if given key name is compliant to recommended format.
+ */
+static vpd_err_t checkKeyName(const uint8_t *name) {
+  unsigned char c;
+  while ((c = *name++)) {
+    if (!(isalnum(c) || c == '_' || c == '.')) {
+      fprintf(stderr, "[ERROR] VPD key name does not allow char [%c].\n", c);
+      return VPD_ERR_PARAM;
+    }
+  }
+  return VPD_OK;
+}
+
+
+/*
+ * Given a key=value string, this function parses it and adds to arugument
+ * pair container. The 'value' can be stored in a base64 format file, in this
+ * case the value field is the file name.
+ */
+vpd_err_t parseString(const uint8_t *string, int read_from_file) {
+  uint8_t *key;
+  uint8_t *value;
+  uint8_t *file_contents = NULL;
+  vpd_err_t retval = VPD_OK;
+
+  key = (uint8_t*)strdup((char*)string);
+  if (!key || key[0] == '\0' || key[0] == '=') {
+    if (key) free(key);
+    return VPD_ERR_SYNTAX;
+  }
+
+  /*
+   * Goes through the key string, and stops at the first '='.
+   * If '=' is not found, the whole string is the key and
+   * the value points to the end of string ('\0').
+   */
+  for (value = key; *value && *value != '='; value++);
+  if (*value == '=') {
+    *(value++) = '\0';
+
+    if (read_from_file) {
+      /* 'value' in fact is a file name */
+      file_contents = read_string_from_file((const char *)value);
+      if (!file_contents) {
+        free(key);
+        return VPD_ERR_SYNTAX;
+      }
+      value = file_contents;
+    }
+  }
+
+  retval = checkKeyName(key);
+  if (retval == VPD_OK)
+    setString(&set_argument, key, value, pad_value_len);
+
+  free(key);
+  if (file_contents)
+    free(file_contents);
+
+  return retval;
+}
+
+
+/* Given an address, compare if it is SMBIOS signature ("_SM_"). */
+int isEps(const void* ptr) {
+  return !memcmp(VPD_ENTRY_MAGIC, ptr, sizeof(VPD_ENTRY_MAGIC) - 1);
+  /* TODO(yjlou): need more EPS sanity checks here. */
+}
+
+
+/* There are two possible file content appearng here:
+ *   1. a full and complete BIOS file
+ *   2. a full but only VPD partition area is valid. (no fmap)
+ *   3. a full BIOS, but VPD partition is blank.
+ *
+ * The first case is easy. Just lookup the fmap and find out the VPD partition.
+ * The second is harder. We try to search the SMBIOS signature (since others
+ * are blank). For the third, we just return and leave caller to read full
+ * content, including fmap info.
+ *
+ * If found, vpd_offset and vpd_size are updated.
+ */
+vpd_err_t findVpdPartition(const uint8_t* read_buf, const uint32_t file_size,
+                     uint32_t* vpd_offset, uint32_t* vpd_size) {
+  off_t sig_offset;
+  struct fmap *fmap;
+  int i;
+
+  assert(read_buf);
+  assert(vpd_offset);
+  assert(vpd_size);
+
+  /* scan the file and find out the VPD partition. */
+  sig_offset = fmapFind(read_buf, file_size);
+  if (-1 != sig_offset) {
+    /* FMAP signature is found, try to search the partition name in table. */
+    fmap = (struct fmap *)&read_buf[sig_offset];
+    for(i = 0; i < fmap->nareas; i++) {
+      fmapNormalizeAreaName(fmap->areas[i].name);
+    }
+
+    if (FMAP_OK == fmapGetArea(fmap_vpd_area_name, fmap,
+                               vpd_offset, vpd_size)) {
+      found_vpd = 1;  /* Mark found here then saveFile() knows where to
+                       * write back (vpd_offset, vpd_size). */
+      return VPD_OK;
+    } else {
+      fprintf(stderr, "[ERROR] The VPD partition [%s] is not found.\n",
+              fmap_vpd_area_name);
+      return VPD_ERR_NOT_FOUND;
+    }
+  }
+
+  /* The signature must be aligned to 16-byte. */
+  for (i = 0; i < file_size; i += 16) {
+    if (isEps(&read_buf[i])) {
+      found_vpd = 1;  /* Mark found here then saveFile() knows where to
+                       * write back (vpd_offset, vpd_size). */
+      *vpd_offset = i;
+      /* FIXME: We don't know the VPD partition size in this case.
+       *        However, return 4K should be safe enough now.
+       *        In the long term, this code block will be obscured. */
+      *vpd_size = 4096;
+      return VPD_OK;
+    }
+  }
+  return VPD_ERR_NOT_FOUND;
+}
+
+
+/* Load file content into memory.
+ * Returns: NULL if file opens error or read error.
+ *          Others, pointer to the memory. The filesize is also returned.
+ *
+ * Note: it's caller's responsbility to free the memory.
+ */
+static uint8_t *readFileContent(const char* filename, uint32_t *filesize) {
+  FILE *fp;
+  uint8_t *read_buf;
+
+  assert(filename);
+  assert(filesize);
+
+  if (!(fp = fopen(filename, "r"))) {
+    return NULL;
+  }
+  /* get file size */
+  fseek(fp, 0, SEEK_END);
+  *filesize = ftell(fp);
+
+  /* read file content */
+  fseek(fp, 0, SEEK_SET);
+  read_buf = malloc(*filesize + 1); /* Might need room for a \0. */
+  assert(read_buf);
+  if (*filesize != fread(read_buf, 1, *filesize, fp)) {
+    fprintf(stderr, "[ERROR] Reading file [%s] failed.\n", filename);
+    return NULL;
+  }
+  fclose(fp);
+
+  return read_buf;
+}
+
+
+vpd_err_t getVpdPartitionFromFullBios(uint32_t* offset, uint32_t* size) {
+  const char *filename;
+  uint8_t *buf;
+  uint32_t buf_size;
+  vpd_err_t retval;
+
+  filename = myMkTemp();
+  if (!filename) {
+    return VPD_ERR_SYSTEM;
+  }
+
+  if (FLASHROM_OK != flashromFullRead(filename)) {
+    fprintf(stderr, "[WARN] Cannot read full BIOS.\n");
+    return VPD_ERR_ROM_READ;
+  }
+  assert((buf = readFileContent(filename, &buf_size)));
+  if (findVpdPartition(buf, buf_size, offset, size)) {
+    fprintf(stderr, "[WARN] Cannot get eps_base from full BIOS.\n");
+    retval = VPD_ERR_INVALID;
+  } else {
+    retval = VPD_OK;
+  }
+  free(buf);
+  return retval;
+}
+
+
+/* Below 2 functions are the helper functions for extract data from VPD 1.x
+ * binary-encoded structure.
+ * Note that the returning pointer is a static buffer. Thus the later call will
+ * destroy the former call's result.
+ */
+static uint8_t* extractString(const uint8_t* value, const int max_len) {
+  static uint8_t buf[128];
+  int copy_len;
+
+  /* not longer than the buffer size */
+  copy_len = (max_len > sizeof(buf) - 1) ? sizeof(buf) - 1 : max_len;
+  memcpy(buf, value, copy_len);
+  buf[copy_len] = '\0';
+
+  return buf;
+}
+
+static uint8_t* extractHex(const uint8_t* value, const int len) {
+  char tmp[4];  /* for a hex string */
+  static uint8_t buf[128];
+  int in, out;  /* in points to value[], while out points to buf[]. */
+
+  for (in = 0, out = 0;; ++in) {
+    if (out + 3 > sizeof(buf) - 1) {
+      goto end_of_func;  /* no more buffer */
+    }
+    if (in >= len) {  /* no more input */
+      if (out) --out;  /* remove the tailing colon */
+      goto end_of_func;
+    }
+    sprintf(tmp, "%02x:", value[in]);
+    memcpy(&buf[out], tmp, strlen(tmp));
+    out += strlen(tmp);
+  }
+
+end_of_func:
+  buf[out] = '\0';
+
+  return buf;
+}
+
+
+vpd_err_t loadFile(const char *filename, struct PairContainer *container,
+             int overwrite_it) {
+  uint32_t file_size;
+  uint8_t *read_buf;
+  uint8_t *vpd_buf;
+  struct vpd_entry *eps;
+  uint32_t related_eps_base;
+  struct vpd_header *header;
+  struct vpd_table_binary_blob_pointer *data;
+  uint8_t spd_uuid[16], vpd_2_0_uuid[16], vpd_1_2_uuid[16];
+  int expected_handle;
+  int table_len;
+  int index;
+  vpd_err_t retval = VPD_OK;
+
+  if (!(read_buf = readFileContent(filename, &file_size))) {
+    fprintf(stderr, "[WARN] Cannot LoadFile('%s'), that's fine.\n", filename);
+    return VPD_OK;
+  }
+
+  if (0 == findVpdPartition(read_buf, file_size, &vpd_offset, &vpd_size)) {
+    if (!eps_base_force_specified) {
+      eps_base = vpd_offset;
+    }
+  } else {
+    /* We cannot parse out the VPD partition address from given file.
+     * Then, try to read the whole BIOS chip. */
+    uint32_t offset, size;
+    if (!eps_base_force_specified) {
+      retval = getVpdPartitionFromFullBios(&offset, &size);
+      if (VPD_OK == retval) {
+        eps_base = offset;
+        vpd_size = size;
+      } else {
+        if (overwrite_it) {
+          retval = VPD_OK;
+        } else {
+          fprintf(stderr, "[ERROR] getVpdPartitionFromFullBios() failed.");
+        }
+        goto teardown;
+      }
+    }
+  }
+
+  /* Update the following variables:
+   *   eps_base: integer, the VPD EPS address in ROM.
+   *   vpd_offset: integer, the VPD partition offset in file (read_buf[]).
+   *   vpd_buf: uint8_t*, points to the VPD partition.
+   *   eps: vpd_entry*, points to the EPS structure.
+   *   eps_offset: integer, the offset of EPS related to vpd_buf[].
+   */
+  vpd_buf = &read_buf[vpd_offset];
+  /* eps and eps_offset will be set slightly later. */
+
+  if (eps_base == UNKNOWN_EPS_BASE) {
+    fprintf(stderr, "[ERROR] Cannot determine eps_base. Cannot go on.\n"
+                    "        You may use -E to specify the value.\n");
+    retval = VPD_ERR_INVALID;
+    goto teardown;
+  }
+
+  /* In overwrite mode, we don't care the content inside. Stop parsing. */
+  if (overwrite_it) {
+    retval = VPD_OK;
+    goto teardown;
+  }
+
+  if (vpd_size < sizeof(struct vpd_entry)) {
+    fprintf(stderr, "[ERROR] vpd_size:%d is too small to be compared.\n",
+            vpd_size);
+    retval = VPD_ERR_INVALID;
+    goto teardown;
+  }
+  /* try to search the EPS if it is not aligned to the begin of partition. */
+  for (index = 0; index < vpd_size; index += 16) {
+    if (isEps(&vpd_buf[index])) {
+      eps = (struct vpd_entry *)&vpd_buf[index];
+      eps_offset = index;
+      break;
+    }
+  }
+  /* jump if the VPD partition is not recognized. */
+  if (index >= vpd_size) {
+    /* But OKAY if the VPD partition starts with FF, which might be un-used. */
+    if (!memcmp("\xff\xff\xff\xff", vpd_buf, sizeof(VPD_ENTRY_MAGIC) - 1)) {
+      fprintf(stderr, "[WARN] VPD partition not formatted. It's fine.\n");
+      retval = VPD_OK;
+      goto teardown;
+    } else {
+      fprintf(stderr, "SMBIOS signature is not matched.\n");
+      fprintf(stderr, "You may use -O to overwrite the data.\n");
+      retval = VPD_ERR_INVALID;
+      goto teardown;
+    }
+  }
+
+  /* adjust the eps_base for data->offset field below. */
+  if (!eps_base_force_specified) {
+    related_eps_base = eps->table_address - sizeof(*eps);
+  } else {
+    related_eps_base = eps_base;
+  }
+
+  /* EPS is done above. Parse structure tables below. */
+  /* Get the first type 241 blob, at the tail of EPS. */
+  header = (struct vpd_header*)(((uint8_t*)eps) + eps->entry_length);
+  data = (struct vpd_table_binary_blob_pointer *)
+         ((uint8_t *)header + sizeof(*header));
+
+
+  /* prepare data structure to compare */
+  uuid_parse(GOOGLE_SPD_UUID, spd_uuid);
+  uuid_parse(GOOGLE_VPD_2_0_UUID, vpd_2_0_uuid);
+  uuid_parse(GOOGLE_VPD_1_2_UUID, vpd_1_2_uuid);
+
+  /* Iterate all tables */
+  for (expected_handle = 0;
+       header->type != VPD_TYPE_END;
+       ++expected_handle) {
+    /* make sure we haven't have too much handle already. */
+    if (expected_handle > 65535) {
+      fprintf(stderr, "[ERROR] too many handles. Terminate parsing.\n");
+      retval = VPD_ERR_INVALID;
+      goto teardown;
+    }
+
+    /* check type */
+    if (header->type != VPD_TYPE_BINARY_BLOB_POINTER) {
+      fprintf(stderr, "[ERROR] We now only support Binary Blob Pointer (241). "
+                      "But the %dth handle is type %d. Terminate parsing.\n",
+                      header->handle, header->type);
+      retval = VPD_ERR_INVALID;
+      goto teardown;
+    }
+
+    /* make sure handle is increasing as expected */
+    if (header->handle != expected_handle) {
+      fprintf(stderr, "[ERROR] The handle value must be %d, but is %d.\n"
+                      "        Use -O option to re-format.\n",
+                      expected_handle, header->handle);
+      retval = VPD_ERR_INVALID;
+      goto teardown;
+    }
+
+    /* point to the table 241 data part */
+    index = data->offset - related_eps_base;
+    if (index >= file_size) {
+      fprintf(stderr, "[ERROR] the table offset looks suspicious. "
+                      "index=0x%x, data->offset=0x%x, related_eps_base=0x%x\n",
+                      index, data->offset, related_eps_base);
+      retval = VPD_ERR_INVALID;
+      goto teardown;
+    }
+
+    /*
+     * The main switch case
+     */
+    if (!memcmp(data->uuid, spd_uuid, sizeof(data->uuid))) {
+      /* SPD */
+      spd_offset = index;
+      spd_len = data->size;
+      if (vpd_offset + spd_offset + spd_len >= file_size) {
+        fprintf(stderr, "[ERROR] SPD offset in BBP is not correct.\n"
+                        "        vpd=0x%x spd=0x%x len=0x%x file_size=0x%x\n"
+                        "        If this file is VPD partition only, try to\n"
+                        "        use -E to adjust offset values.\n",
+                        (uint32_t)vpd_offset, (uint32_t)spd_offset,
+                        spd_len, file_size);
+        retval = VPD_ERR_INVALID;
+        goto teardown;
+      }
+
+      if (!(spd_data = malloc(spd_len))) {
+        fprintf(stderr, "spd_data: malloc(%d bytes) failed.\n", spd_len);
+        retval = VPD_ERR_SYSTEM;
+        goto teardown;
+      }
+      memcpy(spd_data, &read_buf[vpd_offset + spd_offset], spd_len);
+      file_flag |= HAS_SPD;
+
+    } else if (!memcmp(data->uuid, vpd_2_0_uuid, sizeof(data->uuid))) {
+      /* VPD 2.0 */
+      /* iterate all pairs */
+      for (;
+           vpd_buf[index] != VPD_TYPE_TERMINATOR &&
+           vpd_buf[index] != VPD_TYPE_IMPLICIT_TERMINATOR;) {
+        retval = decodeToContainer(container, file_size, vpd_buf, &index);
+        if (VPD_OK != retval)
+        {
+          fprintf(stderr, "decodeToContainer() error.\n");
+          goto teardown;
+        }
+      }
+      file_flag |= HAS_VPD_2_0;
+
+    } else if (!memcmp(data->uuid, vpd_1_2_uuid, sizeof(data->uuid))) {
+      /* VPD 1_2: please refer to "Google VPD Type 241 Format v1.2" */
+      struct V12 {
+        uint8_t prod_sn[0x20];
+        uint8_t sku[0x10];
+        uint8_t uuid[0x10];
+        uint8_t mb_sn[0x10];
+        uint8_t imei[0x10];
+        uint8_t ssd_sn[0x10];
+        uint8_t mem_sn[0x10];
+        uint8_t wlan_mac[0x06];
+      } *v12 = (void*)&vpd_buf[index];
+      setString(container, (uint8_t*)"Product_SN",
+                extractString(v12->prod_sn, sizeof(v12->prod_sn)),
+                VPD_AS_LONG_AS);
+      setString(container, (uint8_t*)"SKU",
+                extractString(v12->sku, sizeof(v12->sku)), VPD_AS_LONG_AS);
+      setString(container, (uint8_t*)"UUID",
+                extractHex(v12->uuid, sizeof(v12->uuid)),
+                VPD_AS_LONG_AS);
+      setString(container, (uint8_t*)"MotherBoard_SN",
+                extractString(v12->mb_sn, sizeof(v12->mb_sn)),
+                VPD_AS_LONG_AS);
+      setString(container, (uint8_t*)"IMEI",
+                extractString(v12->imei, sizeof(v12->imei)),
+                VPD_AS_LONG_AS);
+      setString(container, (uint8_t*)"SSD_SN",
+                extractString(v12->ssd_sn, sizeof(v12->ssd_sn)),
+                VPD_AS_LONG_AS);
+      setString(container, (uint8_t*)"Memory_SN",
+                extractString(v12->mem_sn, sizeof(v12->mem_sn)),
+                VPD_AS_LONG_AS);
+      setString(container, (uint8_t*)"WLAN_MAC",
+                extractHex(v12->wlan_mac, sizeof(v12->wlan_mac)),
+                VPD_AS_LONG_AS);
+      file_flag |= HAS_VPD_1_2;
+
+    } else {
+      /* un-supported UUID */
+      char outstr[37];  /* 36-char + 1 null terminator */
+
+      uuid_unparse(data->uuid, outstr);
+      fprintf(stderr, "[ERROR] un-supported UUID: %s\n", outstr);
+      retval = VPD_ERR_INVALID;
+      goto teardown;
+    }
+
+    /* move to next table */
+    if ((table_len = vpd_type241_size(header)) < 0) {
+      fprintf(stderr, "[ERROR] Cannot get type 241 structure table length.\n");
+      retval = VPD_ERR_INVALID;
+      goto teardown;
+    }
+
+    header = (struct vpd_header*)((uint8_t*)header + table_len);
+    data = (struct vpd_table_binary_blob_pointer *)
+           ((uint8_t *)header + sizeof(*header));
+  }
+
+teardown:
+  free(read_buf);
+
+  return retval;
+}
+
+
+vpd_err_t saveFile(const struct PairContainer *container, const char *filename,
+             int write_back_to_flash) {
+  FILE *fp;
+  unsigned char eps[1024];
+  int eps_len = 0;
+  vpd_err_t retval = VPD_OK;
+  uint32_t file_seek;
+  struct google_vpd_info *info = (struct google_vpd_info *)buf;
+
+  memset(eps, 0xff, sizeof(eps));
+  buf_len = sizeof(*info);
+
+  /* prepare info */
+  memset(info, 0, sizeof(*info));
+  memcpy(info->header.magic, VPD_INFO_MAGIC, sizeof(info->header.magic));
+
+  /* encode into buffer */
+  retval = encodeContainer(&file, max_buf_len, buf, &buf_len);
+  if (VPD_OK != retval) {
+    fprintf(stderr, "encodeContainer() error.\n");
+    goto teardown;
+  }
+  retval = encodeVpdTerminator(max_buf_len, buf, &buf_len);
+  if (VPD_OK != retval) {
+    fprintf(stderr, "Out of space for terminator.\n");
+    goto teardown;
+  }
+  info->size = buf_len - sizeof(*info);
+
+  retval = buildEpsAndTables(buf_len, sizeof(eps), eps, &eps_len);
+  if (VPD_OK != retval) {
+    fprintf(stderr, "Cannot build EPS.\n");
+    goto teardown;
+  }
+  assert(eps_len <= GOOGLE_SPD_OFFSET);
+
+  /* Write data in the following order:
+   *   1. EPS
+   *   2. SPD
+   *   3. VPD 2.0
+  */
+  if (found_vpd) {
+    /* We found VPD partition in -f file, which means file is existed.
+     * Instead of truncating the whole file, open to write partial. */
+    if (!(fp = fopen(filename, "r+"))) {
+      fprintf(stderr, "File [%s] cannot be opened for write.\n", filename);
+      retval = VPD_ERR_SYSTEM;
+      goto teardown;
+    }
+  } else {
+    /* VPD is not found, which means the file is pure VPD data.
+     * Always creates the new file and overwrites the original content. */
+    if (!(fp = fopen(filename, "w+"))) {
+      fprintf(stderr, "File [%s] cannot be opened for write.\n", filename);
+      retval = VPD_ERR_SYSTEM;
+      goto teardown;
+    }
+  }
+
+  if (write_back_to_flash) {
+    file_seek = 0;
+  } else {
+    file_seek = vpd_offset;
+  }
+
+  /* write EPS */
+  fseek(fp, file_seek + eps_offset, SEEK_SET);
+  if (fwrite(eps, eps_len, 1, fp) != 1) {
+    fprintf(stderr, "fwrite(EPS) error (%s)\n", strerror(errno));
+    retval = VPD_ERR_SYSTEM;
+    goto teardown;
+  }
+
+  /* write SPD */
+  if (spd_data) {
+    fseek(fp, file_seek + spd_offset, SEEK_SET);
+    if (fwrite(spd_data, spd_len, 1, fp) != 1) {
+      fprintf(stderr, "fwrite(SPD) error (%s)\n", strerror(errno));
+      retval = VPD_ERR_SYSTEM;
+      goto teardown;
+    }
+  }
+
+  /* write VPD 2.0 */
+  fseek(fp, file_seek + vpd_2_0_offset, SEEK_SET);
+  if (fwrite(buf, buf_len, 1, fp) != 1) {
+    fprintf(stderr, "fwrite(VPD 2.0) error (%s)\n", strerror(errno));
+    retval = VPD_ERR_SYSTEM;
+    goto teardown;
+  }
+  fclose(fp);
+
+teardown:
+  return retval;
+}
+
+static void usage(const char *progname) {
+  printf("Chrome OS VPD 2.0 utility --\n");
+#ifdef VPD_VERSION
+  printf("%s\n", VPD_VERSION);
+#endif
+  printf("\n");
+  printf("Usage: %s [OPTION] ...\n", progname);
+  printf("   OPTIONs include:\n");
+  printf("      -h               This help page and version.\n");
+  printf("      -f <filename>    The output file name.\n");
+  printf("      -E <address>     EPS base address (default:0x240000).\n");
+  printf("      -S <key=file>    To add/change a string value, reading its\n");
+  printf("                       base64 contents from a file.\n");
+  printf("      -s <key=value>   To add/change a string value.\n");
+  printf("      -p <pad length>  Pad if length is shorter.\n");
+  printf("      -i <partition>   Specify VPD partition name in fmap.\n");
+  printf("      -l               List content in the file.\n");
+  printf("      --sh             Dump content for shell script.\n");
+  printf("      -0/--null-terminated\n");
+  printf("                       Dump content in null terminate format.\n");
+  printf("      -O               Overwrite and re-format VPD partition.\n");
+  printf("      -g <key>         Print value string only.\n");
+  printf("      -d <key>         Delete a key.\n");
+  printf("\n");
+  printf("   Notes:\n");
+  printf("      You can specify multiple -s and -d. However, vpd always\n");
+  printf("         applies -s first, then -d.\n");
+  printf("      -g and -l must be mutually exclusive.\n");
+  printf("\n");
+}
+
+int main(int argc, char *argv[]) {
+  int opt;
+  int option_index = 0;
+  vpd_err_t retval = VPD_OK;
+  const char *optstring = "hf:E:s:S:p:i:lOg:d:0";
+  static struct option long_options[] = {
+    {"help", 0, 0, 'h'},
+    {"file", 0, 0, 'f'},
+    {"epsbase", 0, 0, 'E'},
+    {"string", 0, 0, 's'},
+    {"base64file", 0, 0, 'S'},
+    {"pad", required_argument, 0, 'p'},
+    {"partition", 0, 0, 'i'},
+    {"list", 0, 0, 'l'},
+    {"overwrite", 0, 0, 'O'},
+    {"filter", 0, 0, 'g'},
+    {"sh", 0, &export_type, VPD_EXPORT_AS_PARAMETER},
+    {"null-terminated", 0, 0, '0'},
+    {"delete", 0, 0, 'd'},
+    {0, 0, 0, 0}
+  };
+  char *filename = NULL;
+  const char *load_file = NULL;
+  const char *save_file = NULL;
+  const char *tmp_part_file = NULL;
+  const char *tmp_full_file = NULL;
+  uint8_t *key_to_export = NULL;
+  int write_back_to_flash = 0;
+  int list_it = 0;
+  int overwrite_it = 0;
+  int modified = 0;
+  int num_to_delete;
+  int read_from_file = 0;
+
+  initContainer(&file);
+  initContainer(&set_argument);
+  initContainer(&del_argument);
+
+  while ((opt = getopt_long(argc, argv, optstring,
+                            long_options, &option_index)) != EOF) {
+    switch (opt) {
+      case 'h':
+        usage(argv[0]);
+        goto teardown;
+        break;
+
+      case 'f':
+        filename = strdup(optarg);
+        break;
+
+      case 'E':
+        errno = 0;
+        eps_base = strtoul(optarg, (char **) NULL, 0);
+        eps_base_force_specified = 1;
+
+        /* FIXME: this is not a stable way to detect error because
+         *        implementation may (or may not) assign errno. */
+        if (!eps_base && errno == EINVAL) {
+          fprintf(stderr, "Not a number for EPS base address: %s\n", optarg);
+          retval = VPD_ERR_SYNTAX;
+          goto teardown;
+        }
+        break;
+
+      case 'S':
+        read_from_file = 1;
+        /* Fall through into the next case */
+      case 's':
+        retval = parseString((uint8_t*)optarg, read_from_file);
+        if (VPD_OK != retval) {
+          fprintf(stderr, "The string [%s] cannot be parsed.\n\n", optarg);
+          goto teardown;
+        }
+        read_from_file = 0;
+        break;
+
+      case 'p':
+        errno = 0;
+        pad_value_len = strtol(optarg, (char **) NULL, 0);
+
+        /* FIXME: this is not a stable way to detect error because
+         *        implementation may (or may not) assign errno. */
+        if (!pad_value_len && errno == EINVAL) {
+          fprintf(stderr, "Not a number for pad length: %s\n", optarg);
+          retval = VPD_ERR_SYNTAX;
+          goto teardown;
+        }
+        break;
+
+      case 'i':
+        snprintf(fmap_vpd_area_name, sizeof(fmap_vpd_area_name), "%s", optarg);
+        break;
+
+      case 'l':
+        list_it = 1;
+        break;
+
+      case 'O':
+        overwrite_it = 1;
+        modified = 1;  /* This option forces to write empty data back even
+                        * no new pair is given. */
+        break;
+
+      case 'g':
+        key_to_export = (uint8_t*)strdup(optarg);
+        break;
+
+      case 'd':
+        /* Add key into container for delete. Since value is non-sense,
+         * keep it empty. */
+        setString(&del_argument, (const uint8_t *)optarg,
+                                 (const uint8_t *)"", 0);
+        break;
+
+      case '0':
+        export_type = VPD_EXPORT_NULL_TERMINATE;
+        break;
+
+      case 0:
+        break;
+
+      default:
+        fprintf(stderr, "Invalid option (%s), use --help for usage.\n", optarg);
+        retval = VPD_ERR_SYNTAX;
+        goto teardown;
+        break;
+    }
+  }
+
+  if (optind < argc) {
+    fprintf(stderr, "[ERROR] unexpected argument: %s\n\n", argv[optind]);
+    usage(argv[0]);
+    retval = VPD_ERR_SYNTAX;
+    goto teardown;
+  }
+
+  tmp_part_file = myMkTemp();
+  tmp_full_file = myMkTemp();
+  if (!tmp_part_file || !tmp_full_file) {
+    fprintf(stderr, "[ERROR] Failed creating temporary files.\n");
+    retval = VPD_ERR_SYSTEM;
+    goto teardown;
+  }
+
+  if (list_it && key_to_export) {
+    fprintf(stderr, "[ERROR] -l and -g must be mutually exclusive.\n");
+    retval = VPD_ERR_SYNTAX;
+    goto teardown;
+  }
+
+  if (VPD_EXPORT_KEY_VALUE != export_type && !list_it) {
+    fprintf(stderr,
+            "[ERROR] --sh/--null-terminated can be set only if -l is set.\n");
+    retval = VPD_ERR_SYNTAX;
+    goto teardown;
+  }
+
+  /* to avoid malicious attack, we replace suspicious chars. */
+  fmapNormalizeAreaName(fmap_vpd_area_name);
+
+  /* if no filename is specified, call flashrom to read from flash. */
+  if (!filename) {
+    if (FLASHROM_OK != flashromPartialRead(tmp_part_file, tmp_full_file,
+                                           fmap_vpd_area_name)) {
+      fprintf(stderr, "[WARN] flashromPartialRead() failed, try full read.\n");
+      /* Try to read whole file */
+      if (FLASHROM_OK != flashromFullRead(tmp_full_file)) {
+        fprintf(stderr, "[ERROR] flashromFullRead() error!\n");
+        retval = VPD_ERR_ROM_READ;
+        goto teardown;
+      } else {
+        /* Success! Then the fmap_vpd_area_name is changed for later use. */
+      }
+    }
+
+    write_back_to_flash = 1;
+    load_file = tmp_full_file;
+    save_file = tmp_part_file;
+  } else {
+    load_file = filename;
+    save_file = filename;
+  }
+
+  retval = loadFile(load_file, &file, overwrite_it);
+  if (VPD_OK != retval) {
+    fprintf(stderr, "loadFile('%s') error.\n", load_file);
+    goto teardown;
+  }
+
+  /* Do -s */
+  if (lenOfContainer(&set_argument) > 0) {
+    mergeContainer(&file, &set_argument);
+    modified++;
+  }
+
+  /* Do -d */
+  num_to_delete = lenOfContainer(&del_argument);
+  if (subtractContainer(&file, &del_argument) !=
+      num_to_delete) {
+    fprintf(stderr, "[ERROR] At least one of the keys to delete"
+        " does not exist. Command ignored.\n");
+    retval = VPD_ERR_PARAM;
+    goto teardown;
+  } else if (num_to_delete > 0) {
+    modified++;
+  }
+
+  /* Do -g */
+  if (key_to_export) {
+    struct StringPair* foundString = findString(&file, key_to_export, NULL);
+    if (NULL == foundString) {
+        fprintf(stderr, "findString(): Vpd data '%s' was not found.\n",
+                key_to_export);
+      retval = VPD_FAIL;
+      goto teardown;
+    } else {
+      uint8_t dump_buf[BUF_LEN * 2];
+      int dump_len = 0;
+
+      retval = exportStringValue(foundString,
+                                 sizeof(dump_buf), dump_buf, &dump_len);
+      if (VPD_OK != retval) {
+        fprintf(stderr, "exportStringValue(): Cannot export the value.\n");
+        goto teardown;
+      }
+
+      fwrite(dump_buf, dump_len, 1, stdout);
+    }
+  }
+
+  /* Do -l */
+  if (list_it) {
+    /* Reserve larger size because the exporting generates longer string than
+     * the encoded data. */
+    uint8_t list_buf[BUF_LEN * 5 + 64];
+    int list_len = 0;
+
+    retval = exportContainer(export_type, &file, sizeof(list_buf),
+                             list_buf, &list_len);
+    if (VPD_OK != retval) {
+      fprintf(stderr, "exportContainer(): Cannot generate string.\n");
+      goto teardown;
+    }
+
+    /* Export necessary program parameters */
+    if (VPD_EXPORT_AS_PARAMETER == export_type) {
+      printf("%s%s -i %s \\\n",
+             SH_COMMENT, argv[0], fmap_vpd_area_name);
+
+      if (filename)
+        printf("    -f %s \\\n", filename);
+    }
+
+    fwrite(list_buf, list_len, 1, stdout);
+  }
+
+  if (modified) {
+    if (file_flag & HAS_VPD_1_2) {
+      fprintf(stderr, "[ERROR] Writing VPD 1.2 not supported yet.\n");
+      retval = VPD_FAIL;
+      goto teardown;
+    }
+
+    retval = saveFile(&file, save_file, write_back_to_flash);
+    if (VPD_OK != retval) {
+      fprintf(stderr, "saveFile('%s') error: %d\n", filename, retval);
+      goto teardown;
+    }
+
+    if (write_back_to_flash) {
+      if (FLASHROM_OK != flashromPartialWrite(save_file, tmp_full_file,
+                                              fmap_vpd_area_name)) {
+        fprintf(stderr, "flashromPartialWrite() error.\n");
+        retval = VPD_ERR_ROM_WRITE;
+        goto teardown;
+      }
+    }
+  }
+
+teardown:
+  if (spd_data) free(spd_data);
+  if (filename) free(filename);
+  if (key_to_export) free(key_to_export);
+  destroyContainer(&file);
+  destroyContainer(&set_argument);
+  destroyContainer(&del_argument);
+  cleanTempFiles();
+
+  return retval;
+}

-- 
To view, visit https://review.coreboot.org/25615
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib3b8de7d27236f3f0e216d5517da0c1f00682f52
Gerrit-Change-Number: 25615
Gerrit-PatchSet: 1
Gerrit-Owner: Marcello Sylvester Bauer
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20180411/ed0336f1/attachment-0001.html>


More information about the coreboot-gerrit mailing list