diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc index fbd12fbad2..36997ed44d 100644 --- a/src/vendorcode/google/chromeos/Makefile.inc +++ b/src/vendorcode/google/chromeos/Makefile.inc @@ -29,7 +29,7 @@ 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 vpd_mac.c +ramstage-y += vpd_decode.c cros_vpd.c vpd_mac.c vpd_calibration.c ifeq ($(MOCK_TPM),1) CFLAGS_common += -DMOCK_TPM=1 diff --git a/src/vendorcode/google/chromeos/chromeos.h b/src/vendorcode/google/chromeos/chromeos.h index 5ba54571a5..13a4fe3503 100644 --- a/src/vendorcode/google/chromeos/chromeos.h +++ b/src/vendorcode/google/chromeos/chromeos.h @@ -83,4 +83,6 @@ static inline void chromeos_ram_oops_init(chromeos_acpi_t *chromeos) {} static inline void chromeos_reserve_ram_oops(struct device *dev, int idx) {} #endif /* CONFIG_CHROMEOS_RAMOOPS */ +void cbmem_add_vpd_calibration_data(void); + #endif /* __CHROMEOS_H__ */ diff --git a/src/vendorcode/google/chromeos/vpd_calibration.c b/src/vendorcode/google/chromeos/vpd_calibration.c new file mode 100644 index 0000000000..3bfd843db0 --- /dev/null +++ b/src/vendorcode/google/chromeos/vpd_calibration.c @@ -0,0 +1,197 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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; 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include +#include + +/* + * This file provides functions looking in the VPD for WiFi calibration data, + * and if found, copying the calibration blobs into CBMEM. + * + * Per interface calibration data is stored in the VPD in opaque blobs. The + * keys of the blobs follow one of two possible patterns: + * "wifi_base64_calibration" or "wifi_calibration", where is the + * interface number. + * + * This function accommodates up to 4 interfaces. All calibration blobs found + * in the VPD are packed into a single CBMEM entry as describe by the + * structures below: + */ + +/* This structure describes a single calibration data blob */ +struct calibration_blob { + uint32_t blob_size; /* Total size. rounded up to fall on a 4 byte + boundary. */ + uint32_t key_size; /* Size of the name of this entry, \0 included. */ + uint32_t value_size; /* Size of the value of this entry */ + /* Zero terminated name(key) goes here, immediately followed by value */ +}; + +/* + * This is the structure of the CBMEM entry containing WiFi calibration blobs. + * It starts with the total size (header size included) followed by an + * arbitrary number of concatenated 4 byte aligned calibration blobs. + */ +struct calibration_entry { + uint32_t size; + struct calibration_blob entries[0]; /* A varialble size container. */ +}; + + +#define MAX_WIFI_INTERFACE_COUNT 4 + +/* + * Structure of the cache to keep information about calibration blobs present + * in the VPD, one cache entry per VPD blob. + * + * Maintaing the cache allows to scan the VPD once, determine the CBMEM entry + * memory requirements, then allocate as much room as necessary and fill it + * up. + */ +struct vpd_blob_cache_t { + /* The longest name template must fit with an extra character. */ + char key_name[40]; + const void *value_pointer; + unsigned blob_size; + unsigned key_size; + unsigned value_size; +}; + +static const char * const templates[] = { + "wifi_base64_calibrationX", + "wifi_calibrationX" +}; + +/* + * Scan the VPD for WiFi calibration data, checking for all possible key names + * and caching discovered blobs. + * + * Return the sum of sizes of all blobs, as stored in CBMEM. + */ +static size_t fill_up_entries_cache(struct vpd_blob_cache_t *cache, + size_t max_entries, size_t *filled_entries) +{ + int i; + int cbmem_entry_size = 0; + size_t used_entries = 0; + + + for (i = 0; + (i < ARRAY_SIZE(templates)) && (used_entries < max_entries); + i++) { + int j; + const int index_location = strlen(templates[i]) - 1; + const int key_length = index_location + 2; + + if (key_length > sizeof(cache->key_name)) + continue; + + for (j = 0; j < MAX_WIFI_INTERFACE_COUNT; j++) { + const void *payload; + int payload_size; + + strcpy(cache->key_name, templates[i]); + cache->key_name[index_location] = j + '0'; + + payload = cros_vpd_find(cache->key_name, &payload_size); + if (!payload) + continue; + + cache->value_pointer = payload; + cache->key_size = key_length; + cache->value_size = payload_size; + cache->blob_size = + ALIGN(sizeof(struct calibration_blob) + + cache->key_size + + cache->value_size, 4); + cbmem_entry_size += cache->blob_size; + + used_entries++; + if (used_entries == max_entries) + break; + + cache++; + } + } + + *filled_entries = used_entries; + return cbmem_entry_size; +} + +void cbmem_add_vpd_calibration_data(void) +{ + size_t cbmem_entry_size, filled_entries; + struct calibration_entry *cbmem_entry; + struct calibration_blob *cal_blob; + int i; + /* + * Allocate one more cache entry than max required, to make sure that + * the last entry can be identified by the key size of zero. + */ + struct vpd_blob_cache_t vpd_blob_cache[ARRAY_SIZE(templates) * + MAX_WIFI_INTERFACE_COUNT]; + + cbmem_entry_size = fill_up_entries_cache(vpd_blob_cache, + ARRAY_SIZE(vpd_blob_cache), + &filled_entries); + + if (!cbmem_entry_size) + return; /* No calibration data found in the VPD. */ + + cbmem_entry_size += sizeof(struct calibration_entry); + cbmem_entry = cbmem_add(CBMEM_ID_WIFI_CALIBRATION, cbmem_entry_size); + if (!cbmem_entry) { + printk(BIOS_ERR, "%s: no room in cbmem to add %zd bytes\n", + __func__, cbmem_entry_size); + return; + } + + cbmem_entry->size = cbmem_entry_size; + + /* Copy cached data into the CBMEM entry. */ + cal_blob = cbmem_entry->entries; + + for (i = 0; i < filled_entries; i++) { + /* Use this as a pointer to the current cache entry. */ + struct vpd_blob_cache_t *cache = vpd_blob_cache + i; + char *pointer; + + cal_blob->blob_size = cache->blob_size; + cal_blob->key_size = cache->key_size; + cal_blob->value_size = cache->value_size; + + /* copy the key */ + pointer = (char *)(cal_blob + 1); + memcpy(pointer, cache->key_name, cache->key_size); + + /* and the value */ + pointer += cache->key_size; + memcpy(pointer, cache->value_pointer, cache->value_size); + + printk(BIOS_INFO, "%s: added %s to CBMEM\n", + __func__, cache->key_name); + + cal_blob = (struct calibration_blob *) + ((char *)cal_blob + cal_blob->blob_size); + } +}