libpayload: configs: Add new config.featuretest to broaden CI
[coreboot2.git] / src / vendorcode / google / chromeos / vpd_calibration.c
blobf66811fe5881557c538ba712a3e1df946046388f
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <b64_decode.h>
4 #include <cbmem.h>
5 #include <console/console.h>
6 #include <stdlib.h>
7 #include <string.h>
9 #include <vendorcode/google/chromeos/chromeos.h>
10 #include <drivers/vpd/vpd.h>
13 * This file provides functions looking in the VPD for WiFi calibration data,
14 * and if found, copying the calibration blobs into CBMEM.
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.
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:
26 /* This structure describes a single calibration data blob */
27 struct 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 */
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.
40 struct calibration_entry {
41 uint32_t size;
42 struct calibration_blob entries[]; /* A varialble size container. */
46 #define MAX_WIFI_INTERFACE_COUNT 4
49 * Structure of the cache to keep information about calibration blobs present
50 * in the VPD, one cache entry per VPD blob.
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.
56 struct vpd_blob_cache_t {
57 /* The longest name template must fit with an extra character. */
58 char key_name[40];
59 void *value_pointer;
60 unsigned blob_size;
61 unsigned key_size;
62 unsigned value_size;
65 static const char * const templates[] = {
66 "wifi_base64_calibrationX",
67 "wifi_calibrationX"
71 * Scan the VPD for WiFi calibration data, checking for all possible key names
72 * and caching discovered blobs.
74 * Return the sum of sizes of all blobs, as stored in CBMEM.
76 static size_t fill_up_entries_cache(struct vpd_blob_cache_t *cache,
77 size_t max_entries, size_t *filled_entries)
79 int i;
80 int cbmem_entry_size = 0;
81 size_t used_entries = 0;
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;
91 if (key_length > sizeof(cache->key_name))
92 continue;
94 for (j = 0; j < MAX_WIFI_INTERFACE_COUNT; j++) {
95 const void *payload;
96 void *decoded_payload;
97 int payload_size;
98 size_t decoded_size;
100 strcpy(cache->key_name, templates[i]);
101 cache->key_name[index_location] = j + '0';
103 payload = vpd_find(cache->key_name, &payload_size, VPD_RO_THEN_RW);
104 if (!payload)
105 continue;
107 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;
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;
125 cache->value_pointer = decoded_payload;
126 cache->key_size = key_length;
127 cache->value_size = decoded_size;
128 cache->blob_size =
129 ALIGN_UP(sizeof(struct calibration_blob) +
130 cache->key_size +
131 cache->value_size, 4);
132 cbmem_entry_size += cache->blob_size;
134 used_entries++;
135 if (used_entries == max_entries)
136 break;
138 cache++;
142 *filled_entries = used_entries;
143 return cbmem_entry_size;
146 void cbmem_add_vpd_calibration_data(void)
148 size_t cbmem_entry_size, filled_entries;
149 struct calibration_entry *cbmem_entry;
150 struct calibration_blob *cal_blob;
151 int i;
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.
156 struct vpd_blob_cache_t vpd_blob_cache[ARRAY_SIZE(templates) *
157 MAX_WIFI_INTERFACE_COUNT];
159 cbmem_entry_size = fill_up_entries_cache(vpd_blob_cache,
160 ARRAY_SIZE(vpd_blob_cache),
161 &filled_entries);
163 if (!cbmem_entry_size)
164 return; /* No calibration data found in the VPD. */
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;
174 cbmem_entry->size = cbmem_entry_size;
176 /* Copy cached data into the CBMEM entry. */
177 cal_blob = cbmem_entry->entries;
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;
184 cal_blob->blob_size = cache->blob_size;
185 cal_blob->key_size = cache->key_size;
186 cal_blob->value_size = cache->value_size;
188 /* copy the key */
189 pointer = (char *)(cal_blob + 1);
190 memcpy(pointer, cache->key_name, cache->key_size);
192 /* and the value */
193 pointer += cache->key_size;
194 memcpy(pointer, cache->value_pointer, cache->value_size);
195 free(cache->value_pointer);
197 printk(BIOS_INFO, "%s: added %s to CBMEM\n",
198 __func__, cache->key_name);
200 cal_blob = (struct calibration_blob *)
201 ((char *)cal_blob + cal_blob->blob_size);