xref: /aosp_15_r20/external/coreboot/src/vendorcode/google/chromeos/vpd_calibration.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <b64_decode.h>
4 #include <cbmem.h>
5 #include <console/console.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include <vendorcode/google/chromeos/chromeos.h>
10 #include <drivers/vpd/vpd.h>
11 
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 */
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 */
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  */
40 struct calibration_entry {
41 	uint32_t size;
42 	struct calibration_blob entries[];  /* 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  */
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;
63 };
64 
65 static 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  */
fill_up_entries_cache(struct vpd_blob_cache_t * cache,size_t max_entries,size_t * filled_entries)76 static 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;
96 			void *decoded_payload;
97 			int payload_size;
98 			size_t decoded_size;
99 
100 			strcpy(cache->key_name, templates[i]);
101 			cache->key_name[index_location] = j + '0';
102 
103 			payload = vpd_find(cache->key_name, &payload_size, VPD_RO_THEN_RW);
104 			if (!payload)
105 				continue;
106 
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;
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;
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;
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 
cbmem_add_vpd_calibration_data(void)146 void 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);
195 		free(cache->value_pointer);
196 
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 }
204