/* * 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" /* 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; }