From 318708ddce2f3322e451bd066c88f5ca04ab179a Mon Sep 17 00:00:00 2001 From: Vadim Bendebury Date: Thu, 23 Oct 2014 16:02:51 -0700 Subject: chromeos: Add a function to copy VPD WiFi calibration data to CBMEM This patch adds functions looking in the VPD for WiFi calibration data, and if found, copying the calibration blobs into CBMEM. Two possible key names templates are used: wifi_base64_calibrationX and wifi_calibrationX, where X is replaced by the WiFi interface number. Up to four interfaces can be provisioned. The calibration data will be retrieved from CBMEM by the bootloader and placed into the device tree before starting the kernel. The structure of the WiFi calibration data CBMEM entry is defined locally: it is a concatenation of the blob names and their contents. Each blob is padded as necessary to make sure that the size divisible by four. To make sure that the exactly required amount of memory is allocated for the CBMEM entry, the function first scans the VPD, caching the information about the available blobs and calculating their combined size. Then the required size CBMEM entry is allocates and the blobs are copied into it. BRANCH=storm BUG=chrome-os-partner:32611 TEST=when this function is called, and the VPD includes calibration data blobs, the WIFI entry shows up in the list of CBMEM entries reported by coreboot. Original-Change-Id: Ibe02dc36ff6254e3b9ad0a5bd2696ca29e1b2be3 Original-Signed-off-by: Vadim Bendebury Original-Reviewed-on: https://chromium-review.googlesource.com/225271 Original-Reviewed-by: Aaron Durbin (cherry picked from commit 9fe185ae5fdc1a896bf892b498bff27a3462caeb) Signed-off-by: Aaron Durbin Change-Id: Ia60f0c5c84decf9854426c4f0cb88f8ccee69046 Reviewed-on: http://review.coreboot.org/9435 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi --- src/vendorcode/google/chromeos/Makefile.inc | 2 +- src/vendorcode/google/chromeos/chromeos.h | 2 + src/vendorcode/google/chromeos/vpd_calibration.c | 197 +++++++++++++++++++++++ 3 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 src/vendorcode/google/chromeos/vpd_calibration.c 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); + } +} -- cgit v1.2.3