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@marcellobauer.com --- A util/vpd/LICENSE A util/vpd/Makefile A util/vpd/README.md A util/vpd/include/lib/flashrom.h A util/vpd/include/lib/fmap.h A util/vpd/include/lib/lib_smbios.h A util/vpd/include/lib/lib_vpd.h A util/vpd/include/lib/math.h A util/vpd/include/lib/vpd.h A util/vpd/include/lib/vpd_tables.h A util/vpd/lib/flashrom.c A util/vpd/lib/fmap.c A util/vpd/lib/lib_smbios.c A util/vpd/lib/lib_vpd_test.c A util/vpd/lib/math.c A util/vpd/lib/vpd_container.c A util/vpd/lib/vpd_decode.c A util/vpd/lib/vpd_encode.c A util/vpd/vpd.c 19 files changed, 3,886 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/15/25615/1
diff --git a/util/vpd/LICENSE b/util/vpd/LICENSE new file mode 100644 index 0000000..d251496 --- /dev/null +++ b/util/vpd/LICENSE @@ -0,0 +1,27 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/util/vpd/Makefile b/util/vpd/Makefile new file mode 100644 index 0000000..0a42399 --- /dev/null +++ b/util/vpd/Makefile @@ -0,0 +1,65 @@ +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Alternatively, this software may be distributed under the terms of the +# GNU General Public License ("GPL") version 2 as published by the Free +# Software Foundation. + +CC?=cc +VERSION := $(shell ./util/getversion.sh) +CFLAGS+=-g -Iinclude/ -Iinclude/lib -Werror -Wall -D'VPD_VERSION="$(VERSION)"' +LDFLAGS+=-luuid +BINARY=vpd +STATIC_BINARY=vpd_s +BINS=$(BINARY) $(STATIC_BINARY) + +LIB_OBJS= \ + lib/flashrom.o \ + lib/fmap.o \ + lib/lib_smbios.o \ + lib/vpd_container.o \ + lib/vpd_decode.o \ + lib/vpd_encode.o \ + lib/math.o + +OBJS = $(LIB_OBJS) vpd.o + +all: $(BINS) + +$(BINARY): $(OBJS) + $(CC) -o $@ $? $(LDFLAGS) + +$(STATIC_BINARY): $(OBJS) + $(CC) -static -o $@ $? $(LDFLAGS) + +clean: + rm -f $(OBJS) $(BINS) + +.PHONY: clean test all diff --git a/util/vpd/README.md b/util/vpd/README.md new file mode 100644 index 0000000..b105016 --- /dev/null +++ b/util/vpd/README.md @@ -0,0 +1,213 @@ +# ChromeOS Vital Product Data (VPD) Binary Blob 2.0 + +[TOC] + +## Overview + +The [Chrome OS Platform VPD Reporting Specification](https://docs.google.com/a/google.com/document/d/1d2l5obmBYxgaJQvltEJD-oiLTb0...) +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; +}