drivers/vpd: Add VPD support

VPD reference: https://chromium.googlesource.com/chromiumos/platform/vpd/+/master/README.md

Copy ChromeOS VPD driver to add support for VPD without CROMEOS.
Possible use case:
* Storing calibration data
* Storing MAC address
* Storing serial
* Storing boot options

+ Now it's possible to define the VPD space by choosing
  one of the following enums: VPD_ANY, VPD_RW, VPD_RO.
+ CHROMEOS selects now VPD as part of it.
+ VPD is implemented as driver.

Change-Id: Id9263bd39bf25d024e93daa57053fefcb1adc53a
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/25046
Reviewed-by: David Hendricks <david.hendricks@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Patrick Rudolph 2018-03-08 15:43:12 +01:00 committed by Philipp Deppenwiese
parent c4b0fd0a86
commit 28cee59ca2
16 changed files with 206 additions and 140 deletions

28
src/drivers/vpd/Kconfig Normal file
View File

@ -0,0 +1,28 @@
##
## This file is part of the coreboot project.
##
## Copyright (C) 2012 The Chromium OS Authors. 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; version 2 of the License.
##
## 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.
##
config VPD
bool "Support for Vital Product Data tables"
default n
help
Enable support for flash based vital product data.
if VPD
config VPD_DEBUG
bool "Enable VPD debug output"
default n
endif

View File

@ -0,0 +1,2 @@
romstage-$(CONFIG_VPD) += lib_vpd.c
ramstage-$(CONFIG_VPD) += vpd.c lib_vpd.c

113
src/drivers/vpd/lib_vpd.c Normal file
View File

@ -0,0 +1,113 @@
/*
* 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"
/* 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)
{
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;
}
/* 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)
{
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;
}

View File

@ -11,25 +11,25 @@
#include <inttypes.h> #include <inttypes.h>
enum { enum {
VPD_OK = 0, VPD_OK = 0,
VPD_FAIL, VPD_FAIL,
}; };
enum { enum {
VPD_TYPE_TERMINATOR = 0, VPD_TYPE_TERMINATOR = 0,
VPD_TYPE_STRING, VPD_TYPE_STRING,
VPD_TYPE_INFO = 0xfe, VPD_TYPE_INFO = 0xfe,
VPD_TYPE_IMPLICIT_TERMINATOR = 0xff, VPD_TYPE_IMPLICIT_TERMINATOR = 0xff,
}; };
enum { enum {
VPD_AS_LONG_AS = -1, VPD_AS_LONG_AS = -1,
}; };
enum { /* export_type */ enum { /* export_type */
VPD_EXPORT_KEY_VALUE = 1, VPD_EXPORT_KEY_VALUE = 1,
VPD_EXPORT_VALUE, VPD_EXPORT_VALUE,
VPD_EXPORT_AS_PARAMETER, VPD_EXPORT_AS_PARAMETER,
}; };
/* Callback for decodeVpdString to invoke. */ /* Callback for decodeVpdString to invoke. */
@ -39,15 +39,15 @@ typedef int VpdDecodeCallback(const uint8_t *key, int32_t key_len,
/* Container data types */ /* Container data types */
struct StringPair { struct StringPair {
uint8_t *key; uint8_t *key;
uint8_t *value; uint8_t *value;
int pad_len; int pad_len;
int filter_out; /* TRUE means not exported. */ int filter_out; /* TRUE means not exported. */
struct StringPair *next; struct StringPair *next;
}; };
struct PairContainer { struct PairContainer {
struct StringPair *first; struct StringPair *first;
}; };

View File

@ -5,14 +5,13 @@
*/ */
#include <console/console.h> #include <console/console.h>
#include <cbmem.h> #include <cbmem.h>
#include <fmap.h> #include <fmap.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <timestamp.h> #include <timestamp.h>
#include "cros_vpd.h" #include "vpd.h"
#include "lib_vpd.h" #include "lib_vpd.h"
#include "vpd_tables.h" #include "vpd_tables.h"
@ -178,7 +177,7 @@ static int vpd_gets_callback(const uint8_t *key, int32_t key_len,
return VPD_FAIL; return VPD_FAIL;
} }
const void *cros_vpd_find(const char *key, int *size) const void *vpd_find(const char *key, int *size, enum vpd_region region)
{ {
struct vpd_gets_arg arg = {0}; struct vpd_gets_arg arg = {0};
int consumed = 0; int consumed = 0;
@ -191,10 +190,18 @@ const void *cros_vpd_find(const char *key, int *size)
arg.key = (const uint8_t *)key; arg.key = (const uint8_t *)key;
arg.key_len = strlen(key); arg.key_len = strlen(key);
while (VPD_OK == decodeVpdString(vpd->ro_size, vpd->blob, &consumed, if (region == VPD_ANY || region == VPD_RO)
vpd_gets_callback, &arg)) { while (VPD_OK == decodeVpdString(vpd->ro_size, vpd->blob,
&consumed, vpd_gets_callback, &arg)) {
/* Iterate until found or no more entries. */ /* Iterate until found or no more entries. */
} }
if (!arg.matched && region != VPD_RO)
while (VPD_OK == decodeVpdString(vpd->rw_size,
vpd->blob + vpd->ro_size, &consumed,
vpd_gets_callback, &arg)) {
/* Iterate until found or no more entries. */
}
if (!arg.matched) if (!arg.matched)
return NULL; return NULL;
@ -203,12 +210,12 @@ const void *cros_vpd_find(const char *key, int *size)
return arg.value; return arg.value;
} }
char *cros_vpd_gets(const char *key, char *buffer, int size) char *vpd_gets(const char *key, char *buffer, int size, enum vpd_region region)
{ {
const void *string_address; const void *string_address;
int string_size; int string_size;
string_address = cros_vpd_find(key, &string_size); string_address = vpd_find(key, &string_size, region);
if (!string_address) if (!string_address)
return NULL; return NULL;

View File

@ -4,12 +4,14 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#ifndef __CROS_VPD_H__ #ifndef __VPD_H__
#define __CROS_VPD_H__ #define __VPD_H__
#define CROS_VPD_REGION_NAME "region"
#define CROS_VPD_WIFI_SAR_NAME "wifi_sar"
enum vpd_region {
VPD_ANY = 0,
VPD_RO = 1,
VPD_RW = 2
};
/* /*
* Reads VPD string value by key. * Reads VPD string value by key.
* *
@ -19,7 +21,7 @@
* *
* Returns NULL if key is not found, otherwise buffer. * Returns NULL if key is not found, otherwise buffer.
*/ */
char *cros_vpd_gets(const char *key, char *buffer, int size); char *vpd_gets(const char *key, char *buffer, int size, enum vpd_region region);
/* /*
* Find VPD value by key. * Find VPD value by key.
@ -35,6 +37,6 @@ char *cros_vpd_gets(const char *key, char *buffer, int size);
* Returns NULL if key is not found. * Returns NULL if key is not found.
*/ */
const void *cros_vpd_find(const char *key, int *size); const void *vpd_find(const char *key, int *size, enum vpd_region region);
#endif /* __CROS_VPD_H__ */ #endif /* __VPD_H__ */

View File

@ -16,7 +16,7 @@
#include <string.h> #include <string.h>
#include <baseboard/variants.h> #include <baseboard/variants.h>
#include <ec/google/chromeec/ec.h> #include <ec/google/chromeec/ec.h>
#include <vendorcode/google/chromeos/cros_vpd.h> #include <drivers/vpd/vpd.h>
#include <soc/cpu.h> #include <soc/cpu.h>
#include <soc/intel/apollolake/chip.h> #include <soc/intel/apollolake/chip.h>
@ -59,7 +59,7 @@ uint8_t variant_board_sku(void)
if (!IS_ENABLED(CONFIG_CHROMEOS)) if (!IS_ENABLED(CONFIG_CHROMEOS))
return board_sku_num; return board_sku_num;
if (!cros_vpd_gets(vpd_skuid, vpd_buffer, ARRAY_SIZE(vpd_buffer))) if (!vpd_gets(vpd_skuid, vpd_buffer, ARRAY_SIZE(vpd_buffer), VPD_ANY))
return board_sku_num; return board_sku_num;
vpd_len = strlen(vpd_buffer); vpd_len = strlen(vpd_buffer);

View File

@ -24,6 +24,7 @@ config CHROMEOS
select ELOG if BOOT_DEVICE_SUPPORTS_WRITES select ELOG if BOOT_DEVICE_SUPPORTS_WRITES
select COLLECT_TIMESTAMPS select COLLECT_TIMESTAMPS
select VBOOT select VBOOT
select VPD
help help
Enable ChromeOS specific features like the GPIO sub table in Enable ChromeOS specific features like the GPIO sub table in
the coreboot table. NOTE: Enabling this option on an unsupported the coreboot table. NOTE: Enabling this option on an unsupported

View File

@ -17,8 +17,7 @@ ramstage-$(CONFIG_ELOG) += elog.c
ramstage-$(CONFIG_HAVE_ACPI_TABLES) += gnvs.c ramstage-$(CONFIG_HAVE_ACPI_TABLES) += gnvs.c
ramstage-$(CONFIG_HAVE_ACPI_TABLES) += acpi.c ramstage-$(CONFIG_HAVE_ACPI_TABLES) += acpi.c
ramstage-$(CONFIG_CHROMEOS_RAMOOPS) += ramoops.c ramstage-$(CONFIG_CHROMEOS_RAMOOPS) += ramoops.c
romstage-y += vpd_decode.c ramstage-y += vpd_mac.c vpd_serialno.c vpd_calibration.c
ramstage-y += vpd_decode.c cros_vpd.c vpd_mac.c vpd_serialno.c vpd_calibration.c
ramstage-$(CONFIG_CHROMEOS_DISABLE_PLATFORM_HIERARCHY_ON_RESUME) += tpm2.c ramstage-$(CONFIG_CHROMEOS_DISABLE_PLATFORM_HIERARCHY_ON_RESUME) += tpm2.c
ramstage-$(CONFIG_HAVE_REGULATORY_DOMAIN) += wrdd.c ramstage-$(CONFIG_HAVE_REGULATORY_DOMAIN) += wrdd.c
ramstage-$(CONFIG_USE_SAR) += sar.c ramstage-$(CONFIG_USE_SAR) += sar.c

View File

@ -19,9 +19,10 @@
#include <types.h> #include <types.h>
#include <string.h> #include <string.h>
#include <sar.h> #include <sar.h>
#include "cros_vpd.h" #include <drivers/vpd/vpd.h>
#define WIFI_SAR_CBFS_FILENAME "wifi_sar_defaults.hex" #define WIFI_SAR_CBFS_FILENAME "wifi_sar_defaults.hex"
#define CROS_VPD_WIFI_SAR_NAME "wifi_sar"
static int load_sar_file_from_cbfs(void *buf, size_t buffer_size) static int load_sar_file_from_cbfs(void *buf, size_t buffer_size)
{ {
@ -55,7 +56,7 @@ For [WGDS_VERSION] 0x00,
int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits) int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits)
{ {
const char *wifi_sar_limit_key = CROS_VPD_WIFI_SAR_NAME; const char *wifi_sar_limit_key = CROS_VPD_WIFI_SAR_NAME;
/* cros_vpd_gets() reads in one less than size characters from the VPD /* vpd_gets() reads in one less than size characters from the VPD
* with a terminating null byte ('\0') stored as the last character into * with a terminating null byte ('\0') stored as the last character into
* the buffer, thus the increasing by 1 for buffer_size. */ * the buffer, thus the increasing by 1 for buffer_size. */
const size_t buffer_size = (sizeof(struct wifi_sar_limits) / const size_t buffer_size = (sizeof(struct wifi_sar_limits) /
@ -79,8 +80,8 @@ int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits)
} }
/* Try to read the SAR limit entry from VPD */ /* Try to read the SAR limit entry from VPD */
if (!cros_vpd_gets(wifi_sar_limit_key, wifi_sar_limit_str, if (!vpd_gets(wifi_sar_limit_key, wifi_sar_limit_str,
buffer_size)) { buffer_size, VPD_ANY)) {
printk(BIOS_ERR, "Error: Could not locate '%s' in VPD.\n", printk(BIOS_ERR, "Error: Could not locate '%s' in VPD.\n",
wifi_sar_limit_key); wifi_sar_limit_key);

View File

@ -19,7 +19,7 @@
#include <string.h> #include <string.h>
#include <vendorcode/google/chromeos/chromeos.h> #include <vendorcode/google/chromeos/chromeos.h>
#include <vendorcode/google/chromeos/cros_vpd.h> #include <drivers/vpd/vpd.h>
/* /*
* This file provides functions looking in the VPD for WiFi calibration data, * This file provides functions looking in the VPD for WiFi calibration data,
@ -112,7 +112,7 @@ static size_t fill_up_entries_cache(struct vpd_blob_cache_t *cache,
strcpy(cache->key_name, templates[i]); strcpy(cache->key_name, templates[i]);
cache->key_name[index_location] = j + '0'; cache->key_name[index_location] = j + '0';
payload = cros_vpd_find(cache->key_name, &payload_size); payload = vpd_find(cache->key_name, &payload_size, VPD_ANY);
if (!payload) if (!payload)
continue; continue;

View File

@ -1,89 +0,0 @@
/*
* 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"
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;
}

View File

@ -17,7 +17,7 @@
#include <console/console.h> #include <console/console.h>
#include <string.h> #include <string.h>
#include <vendorcode/google/chromeos/cros_vpd.h> #include <drivers/vpd/vpd.h>
/* /*
* Decode string representation of the MAC address (a string of 12 hex * Decode string representation of the MAC address (a string of 12 hex
@ -84,8 +84,8 @@ void lb_table_add_macs_from_vpd(struct lb_header *header)
* If there are no more MAC addresses of this template * If there are no more MAC addresses of this template
* in the VPD - move on. * in the VPD - move on.
*/ */
if (!cros_vpd_gets(mac_addr_key, mac_addr_str, if (!vpd_gets(mac_addr_key, mac_addr_str,
sizeof(mac_addr_str))) sizeof(mac_addr_str), VPD_ANY))
break; break;
if (!macs) { if (!macs) {

View File

@ -18,7 +18,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <vendorcode/google/chromeos/cros_vpd.h> #include <drivers/vpd/vpd.h>
void lb_table_add_serialno_from_vpd(struct lb_header *header) void lb_table_add_serialno_from_vpd(struct lb_header *header)
{ {
@ -27,8 +27,8 @@ void lb_table_add_serialno_from_vpd(struct lb_header *header)
char serialno[32]; char serialno[32];
size_t len; size_t len;
if (!cros_vpd_gets(serialno_key, serialno, if (!vpd_gets(serialno_key, serialno,
sizeof(serialno))) { sizeof(serialno), VPD_ANY)) {
printk(BIOS_ERR, "no serial number in vpd\n"); printk(BIOS_ERR, "no serial number in vpd\n");
return; return;
} }

View File

@ -18,7 +18,9 @@
#include <types.h> #include <types.h>
#include <string.h> #include <string.h>
#include <wrdd.h> #include <wrdd.h>
#include "cros_vpd.h" #include <drivers/vpd/vpd.h>
#define CROS_VPD_REGION_NAME "region"
/* /*
* wrdd_domain_value is ISO 3166-2 * wrdd_domain_value is ISO 3166-2
@ -59,8 +61,8 @@ uint16_t wifi_regulatory_domain(void)
char *separator; char *separator;
/* If not found for any reason fall backto the default value */ /* If not found for any reason fall backto the default value */
if (!cros_vpd_gets(wrdd_domain_key, wrdd_domain_code, if (!vpd_gets(wrdd_domain_key, wrdd_domain_code,
ARRAY_SIZE(wrdd_domain_code))) { ARRAY_SIZE(wrdd_domain_code), VPD_ANY)) {
printk(BIOS_DEBUG, printk(BIOS_DEBUG,
"Error: Could not locate '%s' in VPD\n", wrdd_domain_key); "Error: Could not locate '%s' in VPD\n", wrdd_domain_key);
return WRDD_DEFAULT_REGULATORY_DOMAIN; return WRDD_DEFAULT_REGULATORY_DOMAIN;