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(a)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/1d2l5obmBYxg…
+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 = ¤t->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
Werner Zeh has posted comments on this change. ( https://review.coreboot.org/25586 )
Change subject: siemens/mc_apl1: Make DRAM configuration more flexible
......................................................................
Patch Set 1: Code-Review+2
--
To view, visit https://review.coreboot.org/25586
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: comment
Gerrit-Change-Id: I58c1df0954a436710ecb59487ece07a0832b0de6
Gerrit-Change-Number: 25586
Gerrit-PatchSet: 1
Gerrit-Owner: Mario Scheithauer <mario.scheithauer(a)siemens.com>
Gerrit-Reviewer: Werner Zeh <werner.zeh(a)siemens.com>
Gerrit-Reviewer: build bot (Jenkins) <no-reply(a)coreboot.org>
Gerrit-Comment-Date: Wed, 11 Apr 2018 06:07:32 +0000
Gerrit-HasComments: No
Gerrit-HasLabels: Yes