blob: 460d7dca3aaebdfc4da8192c9fc247f66045f86e [file] [log] [blame]
Angel Pons3ef916f2020-04-02 23:49:13 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Vadim Bendebury318708d2014-10-23 16:02:51 -07002
Vadim Bendebury65f08d52015-03-28 22:13:29 -07003#include <b64_decode.h>
Vadim Bendebury318708d2014-10-23 16:02:51 -07004#include <cbmem.h>
5#include <console/console.h>
Kyösti Mälkki91c077f2021-11-03 18:34:14 +02006#include <stdlib.h>
Vadim Bendebury318708d2014-10-23 16:02:51 -07007#include <string.h>
8
9#include <vendorcode/google/chromeos/chromeos.h>
Patrick Rudolph28cee592018-03-08 15:43:12 +010010#include <drivers/vpd/vpd.h>
Vadim Bendebury318708d2014-10-23 16:02:51 -070011
12/*
13 * This file provides functions looking in the VPD for WiFi calibration data,
14 * and if found, copying the calibration blobs into CBMEM.
15 *
16 * Per interface calibration data is stored in the VPD in opaque blobs. The
17 * keys of the blobs follow one of two possible patterns:
18 * "wifi_base64_calibration<N>" or "wifi_calibration<N>", where <N> is the
19 * interface number.
20 *
21 * This function accommodates up to 4 interfaces. All calibration blobs found
22 * in the VPD are packed into a single CBMEM entry as describe by the
23 * structures below:
24 */
25
26/* This structure describes a single calibration data blob */
27struct calibration_blob {
28 uint32_t blob_size; /* Total size. rounded up to fall on a 4 byte
29 boundary. */
30 uint32_t key_size; /* Size of the name of this entry, \0 included. */
31 uint32_t value_size; /* Size of the value of this entry */
32 /* Zero terminated name(key) goes here, immediately followed by value */
33};
34
35/*
36 * This is the structure of the CBMEM entry containing WiFi calibration blobs.
37 * It starts with the total size (header size included) followed by an
38 * arbitrary number of concatenated 4 byte aligned calibration blobs.
39 */
40struct calibration_entry {
41 uint32_t size;
42 struct calibration_blob entries[0]; /* A varialble size container. */
43};
44
45
46#define MAX_WIFI_INTERFACE_COUNT 4
47
48/*
49 * Structure of the cache to keep information about calibration blobs present
50 * in the VPD, one cache entry per VPD blob.
51 *
52 * Maintaing the cache allows to scan the VPD once, determine the CBMEM entry
53 * memory requirements, then allocate as much room as necessary and fill it
54 * up.
55 */
56struct vpd_blob_cache_t {
57 /* The longest name template must fit with an extra character. */
58 char key_name[40];
Vadim Bendebury65f08d52015-03-28 22:13:29 -070059 void *value_pointer;
Vadim Bendebury318708d2014-10-23 16:02:51 -070060 unsigned blob_size;
61 unsigned key_size;
62 unsigned value_size;
63};
64
65static const char * const templates[] = {
66 "wifi_base64_calibrationX",
67 "wifi_calibrationX"
68};
69
70/*
71 * Scan the VPD for WiFi calibration data, checking for all possible key names
72 * and caching discovered blobs.
73 *
74 * Return the sum of sizes of all blobs, as stored in CBMEM.
75 */
76static size_t fill_up_entries_cache(struct vpd_blob_cache_t *cache,
77 size_t max_entries, size_t *filled_entries)
78{
79 int i;
80 int cbmem_entry_size = 0;
81 size_t used_entries = 0;
82
83
84 for (i = 0;
85 (i < ARRAY_SIZE(templates)) && (used_entries < max_entries);
86 i++) {
87 int j;
88 const int index_location = strlen(templates[i]) - 1;
89 const int key_length = index_location + 2;
90
91 if (key_length > sizeof(cache->key_name))
92 continue;
93
94 for (j = 0; j < MAX_WIFI_INTERFACE_COUNT; j++) {
95 const void *payload;
Vadim Bendebury65f08d52015-03-28 22:13:29 -070096 void *decoded_payload;
Vadim Bendebury318708d2014-10-23 16:02:51 -070097 int payload_size;
Vadim Bendebury65f08d52015-03-28 22:13:29 -070098 size_t decoded_size;
Vadim Bendebury318708d2014-10-23 16:02:51 -070099
100 strcpy(cache->key_name, templates[i]);
101 cache->key_name[index_location] = j + '0';
102
Jonathan Zhanga9117ed2020-05-28 11:02:13 -0700103 payload = vpd_find(cache->key_name, &payload_size, VPD_RO_THEN_RW);
Vadim Bendebury318708d2014-10-23 16:02:51 -0700104 if (!payload)
105 continue;
106
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700107 decoded_size = B64_DECODED_SIZE(payload_size);
108 decoded_payload = malloc(decoded_size);
109 if (!decoded_payload) {
110 printk(BIOS_ERR,
111 "%s: failed allocating %zd bytes\n",
112 __func__, decoded_size);
113 continue;
114 }
115
116 decoded_size = b64_decode(payload, payload_size,
117 decoded_payload);
118 if (!decoded_size) {
119 free(decoded_payload);
120 printk(BIOS_ERR, "%s: failed decoding %s\n",
121 __func__, cache->key_name);
122 continue;
123 }
124
125 cache->value_pointer = decoded_payload;
Vadim Bendebury318708d2014-10-23 16:02:51 -0700126 cache->key_size = key_length;
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700127 cache->value_size = decoded_size;
Vadim Bendebury318708d2014-10-23 16:02:51 -0700128 cache->blob_size =
129 ALIGN(sizeof(struct calibration_blob) +
130 cache->key_size +
131 cache->value_size, 4);
132 cbmem_entry_size += cache->blob_size;
133
134 used_entries++;
135 if (used_entries == max_entries)
136 break;
137
138 cache++;
139 }
140 }
141
142 *filled_entries = used_entries;
143 return cbmem_entry_size;
144}
145
146void cbmem_add_vpd_calibration_data(void)
147{
148 size_t cbmem_entry_size, filled_entries;
149 struct calibration_entry *cbmem_entry;
150 struct calibration_blob *cal_blob;
151 int i;
152 /*
153 * Allocate one more cache entry than max required, to make sure that
154 * the last entry can be identified by the key size of zero.
155 */
156 struct vpd_blob_cache_t vpd_blob_cache[ARRAY_SIZE(templates) *
157 MAX_WIFI_INTERFACE_COUNT];
158
159 cbmem_entry_size = fill_up_entries_cache(vpd_blob_cache,
160 ARRAY_SIZE(vpd_blob_cache),
161 &filled_entries);
162
163 if (!cbmem_entry_size)
164 return; /* No calibration data found in the VPD. */
165
166 cbmem_entry_size += sizeof(struct calibration_entry);
167 cbmem_entry = cbmem_add(CBMEM_ID_WIFI_CALIBRATION, cbmem_entry_size);
168 if (!cbmem_entry) {
169 printk(BIOS_ERR, "%s: no room in cbmem to add %zd bytes\n",
170 __func__, cbmem_entry_size);
171 return;
172 }
173
174 cbmem_entry->size = cbmem_entry_size;
175
176 /* Copy cached data into the CBMEM entry. */
177 cal_blob = cbmem_entry->entries;
178
179 for (i = 0; i < filled_entries; i++) {
180 /* Use this as a pointer to the current cache entry. */
181 struct vpd_blob_cache_t *cache = vpd_blob_cache + i;
182 char *pointer;
183
184 cal_blob->blob_size = cache->blob_size;
185 cal_blob->key_size = cache->key_size;
186 cal_blob->value_size = cache->value_size;
187
188 /* copy the key */
189 pointer = (char *)(cal_blob + 1);
190 memcpy(pointer, cache->key_name, cache->key_size);
191
192 /* and the value */
193 pointer += cache->key_size;
194 memcpy(pointer, cache->value_pointer, cache->value_size);
Vadim Bendebury65f08d52015-03-28 22:13:29 -0700195 free(cache->value_pointer);
Vadim Bendebury318708d2014-10-23 16:02:51 -0700196
197 printk(BIOS_INFO, "%s: added %s to CBMEM\n",
198 __func__, cache->key_name);
199
200 cal_blob = (struct calibration_blob *)
201 ((char *)cal_blob + cal_blob->blob_size);
202 }
203}