From 6eaaafa2c32029bd9dc5fb4196fe2b22cb5b4d9b Mon Sep 17 00:00:00 2001 From: Hung-Te Lin Date: Fri, 21 Feb 2014 16:21:00 +0800 Subject: vendorcode: Add ChromeOS VPD parser. Copied (and unmodified) the minimal bits from ChromeOS libVPD: https://chromium.googlesource.com/chromiumos/platform/vpd Old-Change-Id: Id75d1bfd16263ac1b94c22979f9892cf7908d5e6 Signed-off-by: Hung-Te Lin Reviewed-on: https://chromium-review.googlesource.com/187411 Reviewed-by: Yung-chieh Lo (cherry picked from commit a10ca23686299f3fd5b639631242cadaa2ca9e8a) vendorcode: Update ChromeOS VPD Parser. Merge recent changes in ChromeOS VPD that allows non-memory-mapped firmware to load VPD easier and faster (ref: https://chromium-review.googlesource.com/188134 ). Old-Change-Id: I3ee0b89c703f476f3d77cdde52cc7588724f7686 Reviewed-on: https://chromium-review.googlesource.com/188743 Tested-by: Hung-Te Lin Reviewed-by: Yung-chieh Lo Reviewed-by: David Hendricks Commit-Queue: Hung-Te Lin (cherry picked from commit 03f4d521a7fa711b963b0e1822e92eac16a691b1) vendorcode: Access to ChromeOS VPD on default CBFS media. The new function "cros_vpd_gets(key, buf, size)" provides an easy and quick way to retrieve values in ChromeOS VPD section. Old-Change-Id: I38e50615e515707ffaecdc4c4fae65043541b687 Signed-off-by: Hung-Te Lin Reviewed-on: https://chromium-review.googlesource.com/187430 Reviewed-by: Yung-chieh Lo (cherry picked from commit bcd3832c06e8ed357c50f19396da21a218dc4b39) Squashed 3 related commits for a ChromeOS VPD parser. Change-Id: I4ba8fce16ea123c78d7b543c8353ab9bc1e2aa9f Signed-off-by: Isaac Christensen Reviewed-on: http://review.coreboot.org/6959 Reviewed-by: Ronald G. Minnich Tested-by: build bot (Jenkins) --- src/vendorcode/google/chromeos/Makefile.inc | 2 + src/vendorcode/google/chromeos/cros_vpd.c | 140 +++++++++++++++++ src/vendorcode/google/chromeos/cros_vpd.h | 21 +++ src/vendorcode/google/chromeos/lib_vpd.h | 226 ++++++++++++++++++++++++++++ src/vendorcode/google/chromeos/vpd_decode.c | 89 +++++++++++ src/vendorcode/google/chromeos/vpd_tables.h | 114 ++++++++++++++ 6 files changed, 592 insertions(+) create mode 100644 src/vendorcode/google/chromeos/cros_vpd.c create mode 100644 src/vendorcode/google/chromeos/cros_vpd.h create mode 100644 src/vendorcode/google/chromeos/lib_vpd.h create mode 100644 src/vendorcode/google/chromeos/vpd_decode.c create mode 100644 src/vendorcode/google/chromeos/vpd_tables.h (limited to 'src') diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc index b9e42da92c..d02c09ff5e 100644 --- a/src/vendorcode/google/chromeos/Makefile.inc +++ b/src/vendorcode/google/chromeos/Makefile.inc @@ -28,6 +28,8 @@ romstage-y += fmap.c ramstage-y += fmap.c ramstage-$(CONFIG_CHROMEOS_RAMOOPS) += ramoops.c smm-y += fmap.c +romstage-y += vpd_decode.c cros_vpd.c +ramstage-y += vpd_decode.c cros_vpd.c ifeq ($(MOCK_TPM),1) CFLAGS_common += -DMOCK_TPM=1 diff --git a/src/vendorcode/google/chromeos/cros_vpd.c b/src/vendorcode/google/chromeos/cros_vpd.c new file mode 100644 index 0000000000..26b01ee7a3 --- /dev/null +++ b/src/vendorcode/google/chromeos/cros_vpd.c @@ -0,0 +1,140 @@ +/* + * 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 + +#include +#include +#include + +#include "cros_vpd.h" +#include "fmap.h" +#include "lib_vpd.h" +#include "vpd_tables.h" + +/* + * Static variables are available in ramstage (all platforms), and romstage for + * some platforms (ex, ARM, which uses SRAM). + */ +#if defined(__PRE_RAM__) && CONFIG_ARCH_X86 +#define STATIC_VAR +#else +#define STATIC_VAR static +#endif + +/* Currently we only support Google VPD 2.0, which has a fixed offset. */ +enum { + GOOGLE_VPD_2_0_OFFSET = 0x600, +}; + +struct vpd_gets_arg { + const uint8_t *key; + const uint8_t *value; + int32_t key_len, value_len; + int matched; +}; + +static int cros_vpd_load(uint8_t **vpd_address, int32_t *vpd_size) +{ + STATIC_VAR int cached = 0; + STATIC_VAR uint8_t *cached_address = NULL; + STATIC_VAR int32_t cached_size = 0; + STATIC_VAR int result = -1; + struct google_vpd_info info; + int32_t base; + + const struct fmap_area *area; + struct cbfs_media media; + + if (cached) { + *vpd_address = cached_address; + *vpd_size = cached_size; + return result; + } + + cached = 1; + area = find_fmap_area(fmap_find(), "RO_VPD"); + if (!area) { + printk(BIOS_ERR, "%s: No RO_VPD FMAP section.\n", __func__); + return result; + } + if (area->size <= GOOGLE_VPD_2_0_OFFSET + sizeof(info)) { + printk(BIOS_ERR, "%s: Too small (%d) for Google VPD 2.0.\n", + __func__, area->size); + return result; + } + + base = area->offset + GOOGLE_VPD_2_0_OFFSET; + cached_size = area->size - GOOGLE_VPD_2_0_OFFSET; + init_default_cbfs_media(&media); + media.open(&media); + + /* Try if we can find a google_vpd_info, otherwise read whole VPD. */ + if (media.read(&media, &info, base, sizeof(info)) == sizeof(info) && + memcmp(info.header.magic, VPD_INFO_MAGIC, sizeof(info.header.magic)) + == 0 && cached_size >= info.size + sizeof(info)) { + base += sizeof(info); + cached_size = info.size; + } + + cached_address = media.map(&media, base, cached_size); + media.close(&media); + if (cached_address) { + *vpd_address = cached_address; + *vpd_size = cached_size; + printk(BIOS_DEBUG, "%s: Got VPD: %#x+%#x\n", __func__, base, + cached_size); + result = 0; + } + return result; +} + +static int vpd_gets_callback(const uint8_t *key, int32_t key_len, + const uint8_t *value, int32_t value_len, + void *arg) +{ + struct vpd_gets_arg *result = (struct vpd_gets_arg *)arg; + if (key_len != result->key_len || + memcmp(key, result->key, key_len) != 0) + /* Returns VPD_OK to continue parsing. */ + return VPD_OK; + + result->matched = 1; + result->value = value; + result->value_len = value_len; + /* Returns VPD_FAIL to stop parsing. */ + return VPD_FAIL; +} + +char *cros_vpd_gets(const char *key, char *buffer, int size) +{ + uint8_t *vpd_address = NULL; + int32_t vpd_size = 0; + struct vpd_gets_arg arg = {0}; + int consumed = 0; + + if (cros_vpd_load(&vpd_address, &vpd_size) != 0) { + return NULL; + } + + arg.key = (const uint8_t *)key; + arg.key_len = strlen(key); + + while (VPD_OK == decodeVpdString(vpd_size, vpd_address, &consumed, + vpd_gets_callback, &arg)) { + /* Iterate until found or no more entries. */ + } + + if (!arg.matched) + return NULL; + + if (size < arg.value_len + 1) + size = arg.value_len + 1; + memcpy(buffer, arg.value, size - 1); + buffer[size - 1] = '\0'; + return buffer; +} + diff --git a/src/vendorcode/google/chromeos/cros_vpd.h b/src/vendorcode/google/chromeos/cros_vpd.h new file mode 100644 index 0000000000..674dbf65d9 --- /dev/null +++ b/src/vendorcode/google/chromeos/cros_vpd.h @@ -0,0 +1,21 @@ +/* + * 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. + */ + +#ifndef __CROS_VPD_H__ +#define __CROS_VPD_H__ + +/* + * Reads VPD string value by key. + * + * Reads in at most one less than size characters from VPD and stores them + * into buffer. A terminating null byte ('\0') is stored after the last + * character in the buffer. + * + * Returns NULL if key is not found, otherwise buffer. + */ +char *cros_vpd_gets(const char *key, char *buffer, int size); + +#endif /* __CROS_VPD_H__ */ diff --git a/src/vendorcode/google/chromeos/lib_vpd.h b/src/vendorcode/google/chromeos/lib_vpd.h new file mode 100644 index 0000000000..e08cb3a654 --- /dev/null +++ b/src/vendorcode/google/chromeos/lib_vpd.h @@ -0,0 +1,226 @@ +/* + * 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. + * + */ + +#ifndef __LIB_VPD__ +#define __LIB_VPD__ + +#include + +enum { + VPD_OK = 0, + VPD_FAIL, +}; + +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, +}; + +/* Callback for decodeVpdString to invoke. */ +typedef int 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. + */ +int 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. + */ +int 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. + */ +int 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(). + */ +int 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. + */ +int 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. + */ +int 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. + */ +int 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. + */ +int setContainerFilter(struct PairContainer *container, + const uint8_t *filter); + +/* + * Remove a key. + * Returns VPD_OK if deleted successfully. Otherwise, VPD_FAIL. + */ +int deleteKey(struct PairContainer *container, + const uint8_t *key); + +/* + * Returns number of pairs in container. + */ +int lenOfContainer(const struct PairContainer *container); + +/* + * 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. + */ +int 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/src/vendorcode/google/chromeos/vpd_decode.c b/src/vendorcode/google/chromeos/vpd_decode.c new file mode 100644 index 0000000000..545fd8106f --- /dev/null +++ b/src/vendorcode/google/chromeos/vpd_decode.c @@ -0,0 +1,89 @@ +/* + * 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 +#include "lib_vpd.h" + +int 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_FAIL; + 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. + */ +int 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; + + /* type */ + if (*consumed >= max_len) + return VPD_FAIL; + + type = input_buf[*consumed]; + switch (type) { + case VPD_TYPE_INFO: + case VPD_TYPE_STRING: + (*consumed)++; + + /* key */ + if (VPD_OK != decodeLen(max_len - *consumed, &input_buf[*consumed], + &key_len, &decoded_len) || + *consumed + decoded_len >= max_len) { + return VPD_FAIL; + } + + *consumed += decoded_len; + key = &input_buf[*consumed]; + *consumed += key_len; + + /* value */ + if (VPD_OK != decodeLen(max_len - *consumed, &input_buf[*consumed], + &value_len, &decoded_len) || + *consumed + decoded_len > max_len) { + return VPD_FAIL; + } + *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_FAIL; + break; + } + return VPD_OK; +} diff --git a/src/vendorcode/google/chromeos/vpd_tables.h b/src/vendorcode/google/chromeos/vpd_tables.h new file mode 100644 index 0000000000..d58b5e846d --- /dev/null +++ b/src/vendorcode/google/chromeos/vpd_tables.h @@ -0,0 +1,114 @@ +/* + * 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. + * + * Ported from mosys project (http://code.google.com/p/mosys/). + */ + +#ifndef __LIB_VPD_TABLES_H__ +#define __LIB_VPD_TABLES_H__ + +#include + +#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__ */ -- cgit v1.2.3