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 <vbendeb@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/225271
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>

(cherry picked from commit 9fe185ae5fdc1a896bf892b498bff27a3462caeb)
Signed-off-by: Aaron Durbin <adurbin@chromium.org>

Change-Id: Ia60f0c5c84decf9854426c4f0cb88f8ccee69046
Reviewed-on: http://review.coreboot.org/9435
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
This commit is contained in:
Vadim Bendebury 2014-10-23 16:02:51 -07:00 committed by Aaron Durbin
parent 742fc8d768
commit 318708ddce
3 changed files with 200 additions and 1 deletions

View File

@ -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

View File

@ -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__ */

View File

@ -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 <cbmem.h>
#include <console/console.h>
#include <string.h>
#include <vendorcode/google/chromeos/chromeos.h>
#include <vendorcode/google/chromeos/cros_vpd.h>
/*
* 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<N>" or "wifi_calibration<N>", where <N> 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);
}
}