1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <b64_decode.h>
5 #include <console/console.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
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
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
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
{
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
56 struct vpd_blob_cache_t
{
57 /* The longest name template must fit with an extra character. */
65 static const char * const templates
[] = {
66 "wifi_base64_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
)
80 int cbmem_entry_size
= 0;
81 size_t used_entries
= 0;
85 (i
< ARRAY_SIZE(templates
)) && (used_entries
< max_entries
);
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
))
94 for (j
= 0; j
< MAX_WIFI_INTERFACE_COUNT
; j
++) {
96 void *decoded_payload
;
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
);
107 decoded_size
= B64_DECODED_SIZE(payload_size
);
108 decoded_payload
= malloc(decoded_size
);
109 if (!decoded_payload
) {
111 "%s: failed allocating %zd bytes\n",
112 __func__
, decoded_size
);
116 decoded_size
= b64_decode(payload
, payload_size
,
119 free(decoded_payload
);
120 printk(BIOS_ERR
, "%s: failed decoding %s\n",
121 __func__
, cache
->key_name
);
125 cache
->value_pointer
= decoded_payload
;
126 cache
->key_size
= key_length
;
127 cache
->value_size
= decoded_size
;
129 ALIGN_UP(sizeof(struct calibration_blob
) +
131 cache
->value_size
, 4);
132 cbmem_entry_size
+= cache
->blob_size
;
135 if (used_entries
== max_entries
)
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
;
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
),
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
);
169 printk(BIOS_ERR
, "%s: no room in cbmem to add %zd bytes\n",
170 __func__
, cbmem_entry_size
);
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
;
184 cal_blob
->blob_size
= cache
->blob_size
;
185 cal_blob
->key_size
= cache
->key_size
;
186 cal_blob
->value_size
= cache
->value_size
;
189 pointer
= (char *)(cal_blob
+ 1);
190 memcpy(pointer
, cache
->key_name
, cache
->key_size
);
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
);