blob: df2b5bf667d5e19a5fc3731227a05e061f9669b3 [file] [log] [blame]
/*
* 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 <console/console.h>
#include <cbfs.h>
#include <stdlib.h>
#include <string.h>
#include "cros_vpd.h"
#include "fmap.h"
#include "lib_vpd.h"
#include "vpd_tables.h"
/* 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)
{
MAYBE_STATIC int cached = 0;
MAYBE_STATIC uint8_t *cached_address = NULL;
MAYBE_STATIC int32_t cached_size = 0;
MAYBE_STATIC 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;
}