xref: /aosp_15_r20/external/coreboot/src/vendorcode/google/chromeos/sar.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <cbfs.h>
4 #include <console/console.h>
5 #include <drivers/vpd/vpd.h>
6 #include <fw_config.h>
7 #include <lib.h>
8 #include <sar.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <types.h>
13 
14 #define LEGACY_BYTES_PER_GEO_OFFSET	6
15 #define LEGACY_BYTES_PER_SAR_LIMIT	10
16 #define LEGACY_NUM_SAR_LIMITS		4
17 #define LEGACY_SAR_BIN_SIZE		81
18 #define LEGACY_SAR_WGDS_BIN_SIZE	119
19 #define LEGACY_SAR_NUM_WGDS_GROUPS	3
20 
wifi_hextostr(const char * sar_str,size_t str_len,size_t * sar_bin_len,bool legacy_hex_format)21 static uint8_t *wifi_hextostr(const char *sar_str, size_t str_len, size_t *sar_bin_len,
22 			      bool legacy_hex_format)
23 {
24 	uint8_t *sar_bin = NULL;
25 	size_t bin_len;
26 
27 	if (!legacy_hex_format) {
28 		sar_bin = malloc(str_len);
29 		if (!sar_bin) {
30 			printk(BIOS_ERR, "Failed to allocate space for SAR binary!\n");
31 			return NULL;
32 		}
33 
34 		memcpy(sar_bin, sar_str, str_len);
35 		*sar_bin_len = str_len;
36 	} else {
37 		bin_len = ((str_len - 1) / 2);
38 		sar_bin = malloc(bin_len);
39 		if (!sar_bin) {
40 			printk(BIOS_ERR, "Failed to allocate space for SAR binary!\n");
41 			return NULL;
42 		}
43 
44 		if (hexstrtobin(sar_str, (uint8_t *)sar_bin, bin_len) != bin_len) {
45 			printk(BIOS_ERR, "sar_limits contains non-hex value!\n");
46 			free(sar_bin);
47 			return NULL;
48 		}
49 
50 		*sar_bin_len = bin_len;
51 	}
52 
53 	return sar_bin;
54 }
55 
sar_table_size(const struct sar_profile * sar)56 static int sar_table_size(const struct sar_profile *sar)
57 {
58 	if (sar == NULL)
59 		return 0;
60 
61 	return (sizeof(struct sar_profile) + ((1 + sar->dsar_set_count) * sar->chains_count *
62 					      sar->subbands_count));
63 }
64 
wgds_table_size(const struct geo_profile * geo)65 static int wgds_table_size(const struct geo_profile *geo)
66 {
67 	if (geo == NULL)
68 		return 0;
69 
70 	return sizeof(struct geo_profile) + (geo->chains_count * geo->bands_count);
71 }
72 
gain_table_size(const struct gain_profile * gain)73 static int gain_table_size(const struct gain_profile *gain)
74 {
75 	if (gain == NULL)
76 		return 0;
77 
78 	return sizeof(struct gain_profile) + (gain->chains_count * gain->bands_count);
79 }
80 
sar_avg_table_size(const struct avg_profile * sar_avg)81 static int sar_avg_table_size(const struct avg_profile *sar_avg)
82 {
83 	if (sar_avg == NULL)
84 		return 0;
85 
86 	return sizeof(struct avg_profile);
87 }
88 
dsm_table_size(const struct dsm_profile * dsm)89 static int dsm_table_size(const struct dsm_profile *dsm)
90 {
91 	if (dsm == NULL)
92 		return 0;
93 
94 	return sizeof(struct dsm_profile);
95 }
96 
bsar_table_size(const struct bsar_profile * bsar)97 static int bsar_table_size(const struct bsar_profile *bsar)
98 {
99 	if (bsar == NULL)
100 		return 0;
101 
102 	return sizeof(struct bsar_profile);
103 }
104 
wbem_table_size(const struct wbem_profile * wbem)105 static int wbem_table_size(const struct wbem_profile *wbem)
106 {
107 	if (wbem == NULL)
108 		return 0;
109 
110 	return sizeof(struct wbem_profile);
111 }
112 
valid_legacy_length(size_t bin_len)113 static bool valid_legacy_length(size_t bin_len)
114 {
115 	if (bin_len == LEGACY_SAR_WGDS_BIN_SIZE)
116 		return true;
117 
118 	if (bin_len == LEGACY_SAR_BIN_SIZE && !CONFIG(GEO_SAR_ENABLE))
119 		return true;
120 
121 	return false;
122 }
123 
sar_header_size(void)124 static int sar_header_size(void)
125 {
126 	return (MAX_PROFILE_COUNT * sizeof(uint16_t)) + sizeof(struct sar_header);
127 }
128 
fill_wifi_sar_limits(union wifi_sar_limits * sar_limits,const uint8_t * sar_bin,size_t sar_bin_size)129 static int fill_wifi_sar_limits(union wifi_sar_limits *sar_limits, const uint8_t *sar_bin,
130 				size_t sar_bin_size)
131 {
132 	struct sar_header *header;
133 	size_t i = 0, expected_sar_bin_size;
134 	size_t header_size = sar_header_size();
135 
136 	if (sar_bin_size < header_size) {
137 		printk(BIOS_ERR, "Invalid SAR format!\n");
138 		return -1;
139 	}
140 
141 	header = (struct sar_header *)sar_bin;
142 
143 	if (header->version != SAR_FILE_REVISION) {
144 		printk(BIOS_ERR, "Invalid SAR file version: %d!\n", header->version);
145 		return -1;
146 	}
147 
148 	for (i = 0; i < MAX_PROFILE_COUNT; i++) {
149 		if (header->offsets[i] > sar_bin_size) {
150 			printk(BIOS_ERR, "Offset is outside the file size!\n");
151 			return -1;
152 		}
153 
154 		if (header->offsets[i])
155 			sar_limits->profile[i] = (void *) (sar_bin + header->offsets[i]);
156 	}
157 
158 	expected_sar_bin_size = header_size;
159 	expected_sar_bin_size += sar_table_size(sar_limits->sar);
160 	expected_sar_bin_size += wgds_table_size(sar_limits->wgds);
161 	expected_sar_bin_size += gain_table_size(sar_limits->ppag);
162 	expected_sar_bin_size += sar_avg_table_size(sar_limits->wtas);
163 	expected_sar_bin_size += dsm_table_size(sar_limits->dsm);
164 	expected_sar_bin_size += bsar_table_size(sar_limits->bsar);
165 	expected_sar_bin_size += wbem_table_size(sar_limits->wbem);
166 
167 	if (sar_bin_size != expected_sar_bin_size) {
168 		printk(BIOS_ERR, "Invalid SAR size, expected: %zu, obtained: %zu\n",
169 		       expected_sar_bin_size, sar_bin_size);
170 		return -1;
171 	}
172 
173 	return 0;
174 }
175 
fill_wifi_sar_limits_legacy(union wifi_sar_limits * sar_limits,const uint8_t * sar_bin,size_t sar_bin_size)176 static int fill_wifi_sar_limits_legacy(union wifi_sar_limits *sar_limits,
177 				       const uint8_t *sar_bin, size_t sar_bin_size)
178 {
179 	uint8_t *new_sar_bin;
180 	size_t size = sar_bin_size + sizeof(struct sar_profile);
181 
182 	if (CONFIG(GEO_SAR_ENABLE))
183 		size += sizeof(struct geo_profile);
184 
185 	new_sar_bin = malloc(size);
186 	if (!new_sar_bin) {
187 		printk(BIOS_ERR, "Failed to allocate space for SAR binary!\n");
188 		return -1;
189 	}
190 
191 	sar_limits->sar = (struct sar_profile *) new_sar_bin;
192 	sar_limits->sar->revision = 0;
193 	sar_limits->sar->dsar_set_count = CONFIG_DSAR_SET_NUM;
194 	sar_limits->sar->chains_count = SAR_REV0_CHAINS_COUNT;
195 	sar_limits->sar->subbands_count = SAR_REV0_SUBBANDS_COUNT;
196 	memcpy(&sar_limits->sar->sar_table, sar_bin,
197 	       LEGACY_BYTES_PER_SAR_LIMIT * LEGACY_NUM_SAR_LIMITS);
198 
199 	if (!CONFIG(GEO_SAR_ENABLE))
200 		return 0;
201 
202 	sar_limits->wgds = (struct geo_profile *)(new_sar_bin +
203 						  sar_table_size(sar_limits->sar));
204 	sar_limits->wgds->revision = 0;
205 	sar_limits->wgds->chains_count = LEGACY_SAR_NUM_WGDS_GROUPS;
206 	sar_limits->wgds->bands_count = LEGACY_BYTES_PER_GEO_OFFSET;
207 	memcpy(&sar_limits->wgds->wgds_table,
208 	       sar_bin + LEGACY_BYTES_PER_SAR_LIMIT * LEGACY_NUM_SAR_LIMITS + REVISION_SIZE,
209 	       LEGACY_BYTES_PER_GEO_OFFSET * LEGACY_SAR_NUM_WGDS_GROUPS);
210 
211 	return 0;
212 }
213 
214 /*
215  * Retrieve WiFi SAR limits data from CBFS and decode it
216  * Legacy WiFi SAR data is expected in the format: [<WRDD><EWRD>][WGDS]
217  *
218  * [<WRDD><EWRD>] = NUM_SAR_LIMITS * BYTES_PER_SAR_LIMIT bytes.
219  * [WGDS]=[WGDS_REVISION][WGDS_DATA]
220  *
221  * Current SAR configuration data is expected in the format:
222  * "$SAR" Marker
223  * Version
224  * Offset count
225  * Offsets
226  * [SAR_REVISION,DSAR_SET_COUNT,CHAINS_COUNT,SUBBANDS_COUNT <WRDD>[EWRD]]
227  * [WGDS_REVISION,CHAINS_COUNT,SUBBANDS_COUNT<WGDS_DATA>]
228  * [PPAG_REVISION,MODE,CHAINS_COUNT,SUBBANDS_COUNT<PPAG_DATA>]
229  * [WTAS_REVISION, WTAS_DATA]
230  * [DSM_RETURN_VALUES]
231  * [BSAR_REVISION,IPML,LB,BR,EDR2,EDR3,LE,LE2,LE_LR]
232  * [WBEM_REVISION, WBEM_DATA]
233  *
234  * The configuration data will always have the revision added in the file for each of the
235  * block, based on the revision number and validity, size of the specific block will be
236  * calculated.
237  *
238  * [WGDS_DATA] = [GROUP#0][GROUP#1][GROUP#2]
239  *
240  * [GROUP#<i>] =
241  *	Supported by Revision 0, 1 and 2
242  *              [2.4Ghz - Max Allowed][2.4Ghz - Chain A Offset][2.4Ghz - Chain B Offset]
243  *              [5Ghz - Max Allowed][5Ghz - Chain A Offset][5Ghz - Chain B Offset]
244  *	Supported by Revision 1 and 2
245  *              [6Ghz - Max Allowed][6Ghz - Chain A Offset][6Ghz - Chain B Offset]
246  *
247  * [GROUP#0] is for FCC
248  * [GROUP#1] is for Europe/Japan
249  * [GROUP#2] is for ROW
250  *
251  * [PPAG_DATA] = [ANT_gain Table Chain A] [ANT_gain Table Chain A]
252  *
253  * [ANT_gain Table] =
254  *	Supported by Revision 0, 1 and 2
255  *              [Antenna gain used for 2400MHz frequency]
256  *              [Antenna gain used for 5150-5350MHz frequency]
257  *              [Antenna gain used for 5350-5470MHz frequency]
258  *              [Antenna gain used for 5470-5725MHz frequency]
259  *              [Antenna gain used for 5725-5945MHz frequency]
260  *	Supported by Revision 1 and 2
261  *              [Antenna gain used for 5945-6165MHz frequency]
262  *              [Antenna gain used for 6165-6405MHz frequency]
263  *              [Antenna gain used for 6405-6525MHz frequency]
264  *              [Antenna gain used for 6525-6705MHz frequency]
265  *              [Antenna gain used for 6705-6865MHz frequency]
266  *              [Antenna gain used for 6865-7105MHz frequency]
267  *
268  * [WTAS_DATA] =
269  *      [Enable/disable the TAS feature]
270  *      [Number of blocked countries that are not approved by the OEM to support this feature]
271  *      [deny_list_entry_<1-16>: ISO country code to block]
272  * [WBEM_DATA] =
273  *      [Enable or disable 320MHZ Bandwidth for Japan, SouthKorea]
274  */
get_wifi_sar_limits(union wifi_sar_limits * sar_limits)275 int get_wifi_sar_limits(union wifi_sar_limits *sar_limits)
276 {
277 	const char *filename;
278 	size_t sar_bin_len, sar_str_len;
279 	uint8_t *sar_bin;
280 	char *sar_str;
281 	int ret = -1;
282 	bool legacy_hex_format = false;
283 
284 	filename = get_wifi_sar_cbfs_filename();
285 	if (filename == NULL) {
286 		printk(BIOS_ERR, "Filename missing for CBFS SAR file!\n");
287 		return ret;
288 	}
289 
290 	sar_str = cbfs_map(filename, &sar_str_len);
291 	if (!sar_str) {
292 		printk(BIOS_ERR, "Failed to get the %s file size!\n", filename);
293 		return ret;
294 	}
295 
296 	if (strncmp(sar_str, SAR_STR_PREFIX, SAR_STR_PREFIX_SIZE) == 0) {
297 		legacy_hex_format = false;
298 	} else if (valid_legacy_length(sar_str_len)) {
299 		legacy_hex_format = true;
300 	} else {
301 		printk(BIOS_ERR, "Invalid SAR format!\n");
302 		goto error;
303 	}
304 
305 	sar_bin = wifi_hextostr(sar_str, sar_str_len, &sar_bin_len, legacy_hex_format);
306 	if (sar_bin == NULL) {
307 		printk(BIOS_ERR, "Failed to parse SAR file %s\n", filename);
308 		goto error;
309 	}
310 
311 	memset(sar_limits, 0, sizeof(*sar_limits));
312 	if (legacy_hex_format) {
313 		ret = fill_wifi_sar_limits_legacy(sar_limits, sar_bin, sar_bin_len);
314 		free(sar_bin);
315 	} else {
316 		ret = fill_wifi_sar_limits(sar_limits, sar_bin, sar_bin_len);
317 		if (ret < 0)
318 			free(sar_bin);
319 	}
320 
321 error:
322 	cbfs_unmap(sar_str);
323 	return ret;
324 }
325 
326 __weak
get_wifi_sar_cbfs_filename(void)327 const char *get_wifi_sar_cbfs_filename(void)
328 {
329 	return WIFI_SAR_CBFS_DEFAULT_FILENAME;
330 }
331 
get_wifi_sar_fw_config_filename(const struct fw_config_field * field)332 char *get_wifi_sar_fw_config_filename(const struct fw_config_field *field)
333 {
334 	uint64_t sar_id = fw_config_get_field(field);
335 	if (sar_id == UNDEFINED_FW_CONFIG) {
336 		printk(BIOS_WARNING, "fw_config unprovisioned, set sar filename to NULL\n");
337 		return NULL;
338 	}
339 	static char filename[20];
340 	printk(BIOS_INFO, "Use wifi_sar_%lld.hex.\n", sar_id);
341 	if (snprintf(filename, sizeof(filename), "wifi_sar_%lld.hex", sar_id) < 0) {
342 		printk(BIOS_ERR, "Error occurred with snprintf, set sar filename to NULL\n");
343 		return NULL;
344 	}
345 	return filename;
346 }
347