blob: 697ff953dd3fdbf6c6a27656299f7ff09b199927 [file] [log] [blame]
Vadim Bendebury318708d2014-10-23 16:02:51 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Vadim Bendebury318708d2014-10-23 16:02:51 -070014 */
15
Vadim Bendebury65f08d52015-03-28 22:13:29 -070016#include <b64_decode.h>
Vadim Bendebury318708d2014-10-23 16:02:51 -070017#include <cbmem.h>
18#include <console/console.h>
19#include <string.h>
20
21#include <vendorcode/google/chromeos/chromeos.h>
22#include <vendorcode/google/chromeos/cros_vpd.h>
23
24/*
25 * This file provides functions looking in the VPD for WiFi calibration data,
26 * and if found, copying the calibration blobs into CBMEM.
27 *
28 * Per interface calibration data is stored in the VPD in opaque blobs. The
29 * keys of the blobs follow one of two possible patterns:
30 * "wifi_base64_calibration<N>" or "wifi_calibration<N>", where <N> is the
31 * interface number.
32 *
33 * This function accommodates up to 4 interfaces. All calibration blobs found
34 * in the VPD are packed into a single CBMEM entry as describe by the
35 * structures below:
36 */
37
38/* This structure describes a single calibration data blob */
39struct calibration_blob {
40 uint32_t blob_size; /* Total size. rounded up to fall on a 4 byte
41 boundary. */
42 uint32_t key_size; /* Size of the name of this entry, \0 included. */
43 uint32_t value_size; /* Size of the value of this entry */
44 /* Zero terminated name(key) goes here, immediately followed by value */
45};
46
47/*
48 * This is the structure of the CBMEM entry containing WiFi calibration blobs.
49 * It starts with the total size (header size included) followed by an
50 * arbitrary number of concatenated 4 byte aligned calibration blobs.
51 */
52struct calibration_entry {
53 uint32_t size;
54 struct calibration_blob entries[0]; /* A varialble size container. */
55};
56
57
58#define MAX_WIFI_INTERFACE_COUNT 4
59
60/*
61 * Structure of the cache to keep information about calibration blobs present
62 * in the VPD, one cache entry per VPD blob.
63 *
64 * Maintaing the cache allows to scan the VPD once, determine the CBMEM entry
65 * memory requirements, then allocate as much room as necessary and fill it
66 * up.
67 */
68struct vpd_blob_cache_t {
69 /* The longest name template must fit with an extra character. */
70 char key_name[40];
Vadim Bendebury65f08d52015-03-28 22:13:29 -070071 void *value_pointer;
Vadim Bendebury318708d2014-10-23 16:02:51 -070072 unsigned blob_size;
73 unsigned key_size;
74 unsigned value_size;
75};
76
77static const char * const templates[] = {
78 "wifi_base64_calibrationX",
79 "wifi_calibrationX"
80};
81
82/*
83 * Scan the VPD for WiFi calibration data, checking for all possible key names
84 * and caching discovered blobs.
85 *
86 * Return the sum of sizes of all blobs, as stored in CBMEM.
87 */
88static size_t fill_up_entries_cache(struct vpd_blob_cache_t *cache,
89 size_t max_entries, size_t *filled_entries)
90{
91 int i;
92 int cbmem_entry_size = 0;
93 size_t used_entries = 0;
94
95
96 for (i = 0;
97 (i < ARRAY_SIZE(templates)) && (used_entries < max_entries);
98 i++) {
99 int j;
100 const int index_location = strlen(templates[i]) - 1;
101 const int key_length = index_location + 2;
102
103 if (key_length > sizeof(cache->key_name))
104 continue;
105
106 for (j = 0; j < MAX_WIFI_INTERFACE_COUNT; j++) {
107 const void *payload;
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700108 void *decoded_payload;
Vadim Bendebury318708d2014-10-23 16:02:51 -0700109 int payload_size;
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700110 size_t decoded_size;
Vadim Bendebury318708d2014-10-23 16:02:51 -0700111
112 strcpy(cache->key_name, templates[i]);
113 cache->key_name[index_location] = j + '0';
114
115 payload = cros_vpd_find(cache->key_name, &payload_size);
116 if (!payload)
117 continue;
118
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700119 decoded_size = B64_DECODED_SIZE(payload_size);
120 decoded_payload = malloc(decoded_size);
121 if (!decoded_payload) {
122 printk(BIOS_ERR,
123 "%s: failed allocating %zd bytes\n",
124 __func__, decoded_size);
125 continue;
126 }
127
128 decoded_size = b64_decode(payload, payload_size,
129 decoded_payload);
130 if (!decoded_size) {
131 free(decoded_payload);
132 printk(BIOS_ERR, "%s: failed decoding %s\n",
133 __func__, cache->key_name);
134 continue;
135 }
136
137 cache->value_pointer = decoded_payload;
Vadim Bendebury318708d2014-10-23 16:02:51 -0700138 cache->key_size = key_length;
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700139 cache->value_size = decoded_size;
Vadim Bendebury318708d2014-10-23 16:02:51 -0700140 cache->blob_size =
141 ALIGN(sizeof(struct calibration_blob) +
142 cache->key_size +
143 cache->value_size, 4);
144 cbmem_entry_size += cache->blob_size;
145
146 used_entries++;
147 if (used_entries == max_entries)
148 break;
149
150 cache++;
151 }
152 }
153
154 *filled_entries = used_entries;
155 return cbmem_entry_size;
156}
157
158void cbmem_add_vpd_calibration_data(void)
159{
160 size_t cbmem_entry_size, filled_entries;
161 struct calibration_entry *cbmem_entry;
162 struct calibration_blob *cal_blob;
163 int i;
164 /*
165 * Allocate one more cache entry than max required, to make sure that
166 * the last entry can be identified by the key size of zero.
167 */
168 struct vpd_blob_cache_t vpd_blob_cache[ARRAY_SIZE(templates) *
169 MAX_WIFI_INTERFACE_COUNT];
170
171 cbmem_entry_size = fill_up_entries_cache(vpd_blob_cache,
172 ARRAY_SIZE(vpd_blob_cache),
173 &filled_entries);
174
175 if (!cbmem_entry_size)
176 return; /* No calibration data found in the VPD. */
177
178 cbmem_entry_size += sizeof(struct calibration_entry);
179 cbmem_entry = cbmem_add(CBMEM_ID_WIFI_CALIBRATION, cbmem_entry_size);
180 if (!cbmem_entry) {
181 printk(BIOS_ERR, "%s: no room in cbmem to add %zd bytes\n",
182 __func__, cbmem_entry_size);
183 return;
184 }
185
186 cbmem_entry->size = cbmem_entry_size;
187
188 /* Copy cached data into the CBMEM entry. */
189 cal_blob = cbmem_entry->entries;
190
191 for (i = 0; i < filled_entries; i++) {
192 /* Use this as a pointer to the current cache entry. */
193 struct vpd_blob_cache_t *cache = vpd_blob_cache + i;
194 char *pointer;
195
196 cal_blob->blob_size = cache->blob_size;
197 cal_blob->key_size = cache->key_size;
198 cal_blob->value_size = cache->value_size;
199
200 /* copy the key */
201 pointer = (char *)(cal_blob + 1);
202 memcpy(pointer, cache->key_name, cache->key_size);
203
204 /* and the value */
205 pointer += cache->key_size;
206 memcpy(pointer, cache->value_pointer, cache->value_size);
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700207 free(cache->value_pointer);
Vadim Bendebury318708d2014-10-23 16:02:51 -0700208
209 printk(BIOS_INFO, "%s: added %s to CBMEM\n",
210 __func__, cache->key_name);
211
212 cal_blob = (struct calibration_blob *)
213 ((char *)cal_blob + cal_blob->blob_size);
214 }
215}