xref: /linux/drivers/net/wireless/intel/iwlwifi/fw/uefi.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright(c) 2021-2026 Intel Corporation
4  */
5 
6 #include "iwl-drv.h"
7 #include "pnvm.h"
8 #include "iwl-prph.h"
9 #include "iwl-io.h"
10 
11 #include "fw/uefi.h"
12 #include "fw/api/alive.h"
13 #include <linux/efi.h>
14 #include "fw/runtime.h"
15 
16 #define IWL_EFI_WIFI_GUID	EFI_GUID(0x92daaf2f, 0xc02b, 0x455b,	\
17 					 0xb2, 0xec, 0xf5, 0xa3,	\
18 					 0x59, 0x4f, 0x4a, 0xea)
19 #define IWL_EFI_WIFI_BT_GUID	EFI_GUID(0xe65d8884, 0xd4af, 0x4b20,	\
20 					 0x8d, 0x03, 0x77, 0x2e,	\
21 					 0xcc, 0x3d, 0xa5, 0x31)
22 
23 struct iwl_uefi_pnvm_mem_desc {
24 	__le32 addr;
25 	__le32 size;
26 	const u8 data[];
27 } __packed;
28 
iwl_uefi_get_variable(efi_char16_t * name,efi_guid_t * guid,unsigned long * data_size)29 static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid,
30 				   unsigned long *data_size)
31 {
32 	efi_status_t status;
33 	void *data;
34 
35 	if (!data_size)
36 		return ERR_PTR(-EINVAL);
37 
38 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
39 		return ERR_PTR(-ENODEV);
40 
41 	/* first call with NULL data to get the exact entry size */
42 	*data_size = 0;
43 	status = efi.get_variable(name, guid, NULL, data_size, NULL);
44 	if (status != EFI_BUFFER_TOO_SMALL || !*data_size)
45 		return ERR_PTR(-EIO);
46 
47 	data = kmalloc(*data_size, GFP_KERNEL);
48 	if (!data)
49 		return ERR_PTR(-ENOMEM);
50 
51 	status = efi.get_variable(name, guid, NULL, data_size, data);
52 	if (status != EFI_SUCCESS) {
53 		kfree(data);
54 		return ERR_PTR(-ENOENT);
55 	}
56 
57 	return data;
58 }
59 
iwl_uefi_get_pnvm(struct iwl_trans * trans,size_t * len)60 void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
61 {
62 	unsigned long package_size;
63 	void *data;
64 
65 	*len = 0;
66 
67 	data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_WIFI_GUID,
68 				     &package_size);
69 	if (IS_ERR(data)) {
70 		IWL_DEBUG_FW(trans,
71 			     "PNVM UEFI variable not found 0x%lx (len %lu)\n",
72 			     PTR_ERR(data), package_size);
73 		return data;
74 	}
75 
76 	IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);
77 	*len = package_size;
78 
79 	return data;
80 }
81 
82 static void *
iwl_uefi_get_verified_variable_guid(struct iwl_trans * trans,efi_guid_t * guid,efi_char16_t * uefi_var_name,char * var_name,unsigned int expected_size,unsigned long * size)83 iwl_uefi_get_verified_variable_guid(struct iwl_trans *trans,
84 				    efi_guid_t *guid,
85 				    efi_char16_t *uefi_var_name,
86 				    char *var_name,
87 				    unsigned int expected_size,
88 				    unsigned long *size)
89 {
90 	void *var;
91 	unsigned long var_size;
92 
93 	var = iwl_uefi_get_variable(uefi_var_name, guid, &var_size);
94 
95 	if (IS_ERR(var)) {
96 		IWL_DEBUG_RADIO(trans,
97 				"%s UEFI variable not found 0x%lx\n", var_name,
98 				PTR_ERR(var));
99 		return var;
100 	}
101 
102 	if (var_size < expected_size) {
103 		IWL_DEBUG_RADIO(trans,
104 				"Invalid %s UEFI variable len (%lu)\n",
105 				var_name, var_size);
106 		kfree(var);
107 		return ERR_PTR(-EINVAL);
108 	}
109 
110 	IWL_DEBUG_RADIO(trans, "%s from UEFI with size %lu\n", var_name,
111 			var_size);
112 
113 	if (size)
114 		*size = var_size;
115 	return var;
116 }
117 
118 static void *
iwl_uefi_get_verified_variable(struct iwl_trans * trans,efi_char16_t * uefi_var_name,char * var_name,unsigned int expected_size,unsigned long * size)119 iwl_uefi_get_verified_variable(struct iwl_trans *trans,
120 			       efi_char16_t *uefi_var_name,
121 			       char *var_name,
122 			       unsigned int expected_size,
123 			       unsigned long *size)
124 {
125 	return iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_GUID,
126 						   uefi_var_name, var_name,
127 						   expected_size, size);
128 }
129 
iwl_uefi_handle_tlv_mem_desc(struct iwl_trans * trans,const u8 * data,u32 tlv_len,struct iwl_pnvm_image * pnvm_data)130 int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,
131 				 u32 tlv_len, struct iwl_pnvm_image *pnvm_data)
132 {
133 	const struct iwl_uefi_pnvm_mem_desc *desc = (const void *)data;
134 	u32 data_len;
135 
136 	if (tlv_len < sizeof(*desc)) {
137 		IWL_DEBUG_FW(trans, "TLV len (%d) is too small\n", tlv_len);
138 		return -EINVAL;
139 	}
140 
141 	data_len = tlv_len - sizeof(*desc);
142 
143 	IWL_DEBUG_FW(trans,
144 		     "Handle IWL_UCODE_TLV_MEM_DESC, len %d data_len %d\n",
145 		     tlv_len, data_len);
146 
147 	if (le32_to_cpu(desc->size) != data_len) {
148 		IWL_DEBUG_FW(trans, "invalid mem desc size %d\n", desc->size);
149 		return -EINVAL;
150 	}
151 
152 	if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) {
153 		IWL_DEBUG_FW(trans, "too many payloads to allocate in DRAM.\n");
154 		return -EINVAL;
155 	}
156 
157 	IWL_DEBUG_FW(trans, "Adding data (size %d)\n", data_len);
158 
159 	pnvm_data->chunks[pnvm_data->n_chunks].data = desc->data;
160 	pnvm_data->chunks[pnvm_data->n_chunks].len = data_len;
161 	pnvm_data->n_chunks++;
162 
163 	return 0;
164 }
165 
iwl_uefi_reduce_power_section(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data)166 static int iwl_uefi_reduce_power_section(struct iwl_trans *trans,
167 					 const u8 *data, size_t len,
168 					 struct iwl_pnvm_image *pnvm_data)
169 {
170 	const struct iwl_ucode_tlv *tlv;
171 
172 	IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");
173 	memset(pnvm_data, 0, sizeof(*pnvm_data));
174 
175 	while (len >= sizeof(*tlv)) {
176 		u32 tlv_len, tlv_type;
177 
178 		len -= sizeof(*tlv);
179 		tlv = (const void *)data;
180 
181 		tlv_len = le32_to_cpu(tlv->length);
182 		tlv_type = le32_to_cpu(tlv->type);
183 
184 		if (len < tlv_len) {
185 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
186 				len, tlv_len);
187 			return -EINVAL;
188 		}
189 
190 		data += sizeof(*tlv);
191 
192 		switch (tlv_type) {
193 		case IWL_UCODE_TLV_MEM_DESC:
194 			if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len,
195 							 pnvm_data))
196 				return -EINVAL;
197 			break;
198 		case IWL_UCODE_TLV_PNVM_SKU:
199 			IWL_DEBUG_FW(trans,
200 				     "New REDUCE_POWER section started, stop parsing.\n");
201 			goto done;
202 		default:
203 			IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
204 				     tlv_type, tlv_len);
205 			break;
206 		}
207 
208 		len -= ALIGN(tlv_len, 4);
209 		data += ALIGN(tlv_len, 4);
210 	}
211 
212 done:
213 	if (!pnvm_data->n_chunks) {
214 		IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
215 		return -ENOENT;
216 	}
217 	return 0;
218 }
219 
iwl_uefi_reduce_power_parse(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data,__le32 sku_id[3])220 int iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
221 				const u8 *data, size_t len,
222 				struct iwl_pnvm_image *pnvm_data,
223 				__le32 sku_id[3])
224 {
225 	const struct iwl_ucode_tlv *tlv;
226 
227 	IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");
228 
229 	while (len >= sizeof(*tlv)) {
230 		u32 tlv_len, tlv_type;
231 
232 		len -= sizeof(*tlv);
233 		tlv = (const void *)data;
234 
235 		tlv_len = le32_to_cpu(tlv->length);
236 		tlv_type = le32_to_cpu(tlv->type);
237 
238 		if (len < tlv_len) {
239 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
240 				len, tlv_len);
241 			return -EINVAL;
242 		}
243 
244 		if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
245 			const struct iwl_sku_id *tlv_sku_id =
246 				(const void *)(data + sizeof(*tlv));
247 
248 			IWL_DEBUG_FW(trans,
249 				     "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
250 				     tlv_len);
251 			if (tlv_len < sizeof(*tlv_sku_id)) {
252 				IWL_ERR(trans, "invalid PNVM SKU TLV len: %u\n",
253 					tlv_len);
254 				return -EINVAL;
255 			}
256 
257 			IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
258 				     le32_to_cpu(tlv_sku_id->data[0]),
259 				     le32_to_cpu(tlv_sku_id->data[1]),
260 				     le32_to_cpu(tlv_sku_id->data[2]));
261 
262 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
263 			len -= ALIGN(tlv_len, 4);
264 
265 			if (sku_id[0] == tlv_sku_id->data[0] &&
266 			    sku_id[1] == tlv_sku_id->data[1] &&
267 			    sku_id[2] == tlv_sku_id->data[2]) {
268 				int ret = iwl_uefi_reduce_power_section(trans,
269 								    data, len,
270 								    pnvm_data);
271 				if (!ret)
272 					return 0;
273 			} else {
274 				IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
275 			}
276 		} else {
277 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
278 			len -= ALIGN(tlv_len, 4);
279 		}
280 	}
281 
282 	return -ENOENT;
283 }
284 
iwl_uefi_get_reduced_power(struct iwl_trans * trans,size_t * len)285 u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
286 {
287 	struct pnvm_sku_package *package;
288 	unsigned long package_size;
289 	u8 *data;
290 
291 	package = iwl_uefi_get_verified_variable(trans,
292 						 IWL_UEFI_REDUCED_POWER_NAME,
293 						 "Reduced Power",
294 						 sizeof(*package),
295 						 &package_size);
296 	if (IS_ERR(package))
297 		return ERR_CAST(package);
298 
299 	IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
300 		     package->rev, package->total_size, package->n_skus);
301 
302 	*len = package_size - sizeof(*package);
303 	data = kmemdup(package->data, *len, GFP_KERNEL);
304 	if (!data) {
305 		kfree(package);
306 		return ERR_PTR(-ENOMEM);
307 	}
308 
309 	kfree(package);
310 
311 	return data;
312 }
313 
iwl_uefi_step_parse(struct uefi_cnv_common_step_data * common_step_data,struct iwl_trans * trans)314 static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data,
315 			       struct iwl_trans *trans)
316 {
317 	if (common_step_data->revision != 1)
318 		return -EINVAL;
319 
320 	trans->conf.mbx_addr_0_step =
321 		(u32)common_step_data->revision |
322 		(u32)common_step_data->cnvi_eq_channel << 8 |
323 		(u32)common_step_data->cnvr_eq_channel << 16 |
324 		(u32)common_step_data->radio1 << 24;
325 	trans->conf.mbx_addr_1_step = (u32)common_step_data->radio2;
326 	return 0;
327 }
328 
iwl_uefi_get_step_table(struct iwl_trans * trans)329 void iwl_uefi_get_step_table(struct iwl_trans *trans)
330 {
331 	struct uefi_cnv_common_step_data *data;
332 	int ret;
333 
334 	if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
335 		return;
336 
337 	data = iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_BT_GUID,
338 						   IWL_UEFI_STEP_NAME,
339 						   "STEP", sizeof(*data), NULL);
340 	if (IS_ERR(data))
341 		return;
342 
343 	ret = iwl_uefi_step_parse(data, trans);
344 	if (ret < 0)
345 		IWL_DEBUG_FW(trans, "Cannot read STEP tables. rev is invalid\n");
346 
347 	kfree(data);
348 }
349 IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table);
350 
iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data * sgom_data,struct iwl_fw_runtime * fwrt)351 static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
352 			       struct iwl_fw_runtime *fwrt)
353 {
354 	int i, j;
355 
356 	if (sgom_data->revision != 1)
357 		return -EINVAL;
358 
359 	memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
360 	       sizeof(fwrt->sgom_table.offset_map));
361 
362 	for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
363 		for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
364 			/* since each byte is composed of to values, */
365 			/* one for each letter, */
366 			/* extract and check each of them separately */
367 			u8 value = fwrt->sgom_table.offset_map[i][j];
368 			u8 low = value & 0xF;
369 			u8 high = (value & 0xF0) >> 4;
370 
371 			if (high > fwrt->geo_num_profiles)
372 				high = 0;
373 			if (low > fwrt->geo_num_profiles)
374 				low = 0;
375 			fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
376 		}
377 	}
378 
379 	fwrt->sgom_enabled = true;
380 	return 0;
381 }
382 
iwl_uefi_get_sgom_table(struct iwl_trans * trans,struct iwl_fw_runtime * fwrt)383 void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
384 			     struct iwl_fw_runtime *fwrt)
385 {
386 	struct uefi_cnv_wlan_sgom_data *data;
387 	int ret;
388 
389 	if (!fwrt->geo_enabled)
390 		return;
391 
392 	data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME,
393 					      "SGOM", sizeof(*data), NULL);
394 	if (IS_ERR(data))
395 		return;
396 
397 	ret = iwl_uefi_sgom_parse(data, fwrt);
398 	if (ret < 0)
399 		IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");
400 
401 	kfree(data);
402 }
403 IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
404 
iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data * uats_data,struct iwl_fw_runtime * fwrt)405 static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,
406 			       struct iwl_fw_runtime *fwrt)
407 {
408 	if (uats_data->revision != 1)
409 		return -EINVAL;
410 
411 	memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_map,
412 	       uats_data->mcc_to_ap_type_map,
413 	       sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_map));
414 
415 	fwrt->ap_type_cmd_valid = true;
416 
417 	return 0;
418 }
419 
iwl_uefi_get_uats_table(struct iwl_trans * trans,struct iwl_fw_runtime * fwrt)420 void iwl_uefi_get_uats_table(struct iwl_trans *trans,
421 			     struct iwl_fw_runtime *fwrt)
422 {
423 	struct uefi_cnv_wlan_uats_data *data;
424 	int ret;
425 
426 	data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME,
427 					      "UATS", sizeof(*data), NULL);
428 	if (IS_ERR(data))
429 		return;
430 
431 	ret = iwl_uefi_uats_parse(data, fwrt);
432 	if (ret < 0)
433 		IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n");
434 	kfree(data);
435 }
436 IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);
437 
iwl_uefi_get_uneb_table(struct iwl_trans * trans,struct iwl_fw_runtime * fwrt)438 void iwl_uefi_get_uneb_table(struct iwl_trans *trans,
439 			     struct iwl_fw_runtime *fwrt)
440 {
441 	struct uefi_cnv_wlan_uneb_data *data;
442 
443 	data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UNEB_NAME,
444 					      "UNEB", sizeof(*data), NULL);
445 	if (IS_ERR(data))
446 		return;
447 
448 	if (data->revision != 1) {
449 		IWL_DEBUG_RADIO(fwrt,
450 				"Cannot read UNEB table. rev is invalid\n");
451 		goto out;
452 	}
453 
454 	BUILD_BUG_ON(sizeof(data->mcc_to_ap_type_map) !=
455 		     sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map));
456 
457 	memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map,
458 	       data->mcc_to_ap_type_map,
459 	       sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map));
460 
461 	fwrt->ap_type_cmd_valid = true;
462 
463 out:
464 	kfree(data);
465 }
466 IWL_EXPORT_SYMBOL(iwl_uefi_get_uneb_table);
467 
iwl_uefi_set_sar_profile(struct iwl_fw_runtime * fwrt,const u8 * vals,u8 prof_index,u8 num_subbands,bool enabled)468 static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
469 				     const u8 *vals, u8 prof_index,
470 				     u8 num_subbands, bool enabled)
471 {
472 	struct iwl_sar_profile *sar_prof = &fwrt->sar_profiles[prof_index];
473 
474 	/*
475 	 * Make sure fwrt has enough room to hold the data
476 	 * coming from the UEFI table
477 	 */
478 	if (WARN_ON(ARRAY_SIZE(sar_prof->chains) *
479 		    ARRAY_SIZE(sar_prof->chains[0].subbands)  <
480 		    UEFI_SAR_MAX_CHAINS_PER_PROFILE * num_subbands))
481 		return;
482 
483 	BUILD_BUG_ON(ARRAY_SIZE(sar_prof->chains) !=
484 		     UEFI_SAR_MAX_CHAINS_PER_PROFILE);
485 
486 	for (int chain = 0;
487 	     chain < UEFI_SAR_MAX_CHAINS_PER_PROFILE;
488 	     chain++) {
489 		for (int subband = 0; subband < num_subbands; subband++)
490 			sar_prof->chains[chain].subbands[subband] =
491 				vals[chain * num_subbands + subband];
492 	}
493 
494 	fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK;
495 }
496 
iwl_uefi_get_wrds_table(struct iwl_fw_runtime * fwrt)497 int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)
498 {
499 	struct uefi_cnv_var_wrds *data;
500 	unsigned long size;
501 	unsigned long expected_size;
502 	int num_subbands;
503 	int ret = 0;
504 
505 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME,
506 					      "WRDS",
507 					      UEFI_SAR_WRDS_TABLE_SIZE_REV2,
508 					      &size);
509 
510 	if (IS_ERR(data))
511 		return -EINVAL;
512 
513 	switch (data->revision) {
514 	case 2:
515 		expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV2;
516 		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
517 		break;
518 	case 3:
519 		expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV3;
520 		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
521 		break;
522 	default:
523 		IWL_DEBUG_RADIO(fwrt,
524 				"Unsupported UEFI WRDS revision:%d\n",
525 				data->revision);
526 		ret = -EINVAL;
527 		goto out;
528 	}
529 
530 	if (size != expected_size) {
531 		ret = -EINVAL;
532 		goto out;
533 	}
534 
535 	/* The profile from WRDS is officially profile 1, but goes
536 	 * into sar_profiles[0] (because we don't have a profile 0).
537 	 */
538 	iwl_uefi_set_sar_profile(fwrt, data->vals, 0,
539 				 num_subbands, data->mode);
540 out:
541 	kfree(data);
542 	return ret;
543 }
544 
iwl_uefi_get_ewrd_table(struct iwl_fw_runtime * fwrt)545 int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
546 {
547 	struct uefi_cnv_var_ewrd *data;
548 	unsigned long expected_size;
549 	int i, ret = 0;
550 	unsigned long size;
551 	int num_subbands;
552 	int profile_size;
553 
554 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME,
555 					      "EWRD",
556 					      UEFI_SAR_EWRD_TABLE_SIZE_REV2,
557 					      &size);
558 	if (IS_ERR(data))
559 		return -EINVAL;
560 
561 	switch (data->revision) {
562 	case 2:
563 		expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV2;
564 		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
565 		profile_size = UEFI_SAR_PROFILE_SIZE_REV2;
566 		break;
567 	case 3:
568 		expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV3;
569 		num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
570 		profile_size = UEFI_SAR_PROFILE_SIZE_REV3;
571 		break;
572 	default:
573 		IWL_DEBUG_RADIO(fwrt,
574 				"Unsupported UEFI EWRD revision:%d\n",
575 				data->revision);
576 		ret = -EINVAL;
577 		goto out;
578 	}
579 
580 	if (size != expected_size ||
581 	    data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
582 		ret = -EINVAL;
583 		goto out;
584 	}
585 
586 	for (i = 0; i < data->num_profiles; i++)
587 		/* The EWRD profiles officially go from 2 to 4, but we
588 		 * save them in sar_profiles[1-3] (because we don't
589 		 * have profile 0).  So in the array we start from 1.
590 		 */
591 		iwl_uefi_set_sar_profile(fwrt, &data->vals[i * profile_size],
592 					 i + 1, num_subbands, data->mode);
593 
594 out:
595 	kfree(data);
596 	return ret;
597 }
598 
iwl_uefi_get_wgds_table(struct iwl_fw_runtime * fwrt)599 int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
600 {
601 	struct uefi_cnv_var_wgds *data;
602 	unsigned long expected_size;
603 	unsigned long size;
604 	int profile_size;
605 	int n_subbands;
606 	int ret = 0;
607 
608 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME,
609 					      "WGDS", UEFI_WGDS_TABLE_SIZE_REV3,
610 					      &size);
611 	if (IS_ERR(data))
612 		return -EINVAL;
613 
614 	switch (data->revision) {
615 	case 3:
616 		expected_size = UEFI_WGDS_TABLE_SIZE_REV3;
617 		n_subbands = UEFI_GEO_NUM_BANDS_REV3;
618 		break;
619 	case 4:
620 		expected_size = UEFI_WGDS_TABLE_SIZE_REV4;
621 		n_subbands = UEFI_GEO_NUM_BANDS_REV4;
622 		break;
623 	default:
624 		ret = -EINVAL;
625 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n",
626 				data->revision);
627 		goto out;
628 	}
629 
630 	if (size != expected_size) {
631 		ret = -EINVAL;
632 		goto out;
633 	}
634 
635 	if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM ||
636 	    data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) {
637 		ret = -EINVAL;
638 		IWL_DEBUG_RADIO(fwrt, "Invalid number of profiles in WGDS: %d\n",
639 				data->num_profiles);
640 		goto out;
641 	}
642 
643 	if (WARN_ON(BIOS_GEO_MAX_PROFILE_NUM >
644 		    ARRAY_SIZE(fwrt->geo_profiles) ||
645 		    n_subbands > ARRAY_SIZE(fwrt->geo_profiles[0].bands) ||
646 		    BIOS_GEO_NUM_CHAINS >
647 		    ARRAY_SIZE(fwrt->geo_profiles[0].bands[0].chains))) {
648 		ret = -EINVAL;
649 		goto out;
650 	}
651 
652 	fwrt->geo_rev = data->revision;
653 	fwrt->geo_bios_source = BIOS_SOURCE_UEFI;
654 	profile_size = 3 * n_subbands;
655 	for (int prof = 0; prof < data->num_profiles; prof++) {
656 		const u8 *val = &data->vals[profile_size * prof];
657 		struct iwl_geo_profile *geo_prof = &fwrt->geo_profiles[prof];
658 
659 		for (int subband = 0; subband < n_subbands; subband++) {
660 			geo_prof->bands[subband].max = *val++;
661 
662 			for (int chain = 0;
663 			     chain < BIOS_GEO_NUM_CHAINS;
664 			     chain++)
665 				geo_prof->bands[subband].chains[chain] = *val++;
666 		}
667 	}
668 
669 	fwrt->geo_num_profiles = data->num_profiles;
670 	fwrt->geo_enabled = true;
671 out:
672 	kfree(data);
673 	return ret;
674 }
675 
iwl_uefi_get_ppag_table(struct iwl_fw_runtime * fwrt)676 int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
677 {
678 	struct uefi_cnv_var_ppag *data;
679 	int n_subbands;
680 	u32 valid_rev;
681 	int ret = 0;
682 
683 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME,
684 					      "PPAG", UEFI_PPAG_DATA_SIZE_V5,
685 					      NULL);
686 	if (!IS_ERR(data)) {
687 		n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV5;
688 		valid_rev = BIT(5);
689 
690 		goto parse_table;
691 	}
692 
693 	data = iwl_uefi_get_verified_variable(fwrt->trans,
694 					      IWL_UEFI_PPAG_NAME,
695 					      "PPAG",
696 					      UEFI_PPAG_DATA_SIZE_V4,
697 					      NULL);
698 	if (!IS_ERR(data)) {
699 		n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV4;
700 		/* revisions 1-4 have all the same size */
701 		valid_rev = BIT(1) | BIT(2) | BIT(3) | BIT(4);
702 
703 		goto parse_table;
704 	}
705 
706 	return -EINVAL;
707 
708 parse_table:
709 	if (data->revision >= 32 || !(BIT(data->revision) & valid_rev)) {
710 		ret = -EINVAL;
711 		IWL_DEBUG_RADIO(fwrt,
712 				"Unsupported UEFI PPAG revision:%d\n",
713 				data->revision);
714 		goto out;
715 	}
716 
717 	/*
718 	 * Make sure fwrt has enough room to hold
719 	 * data coming from the UEFI table
720 	 */
721 	if (WARN_ON(ARRAY_SIZE(fwrt->ppag_chains) *
722 		    ARRAY_SIZE(fwrt->ppag_chains[0].subbands)  <
723 		    UEFI_PPAG_NUM_CHAINS * n_subbands)) {
724 		ret = -EINVAL;
725 		goto out;
726 	}
727 
728 	fwrt->ppag_bios_rev = data->revision;
729 	fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes,
730 						   fwrt->ppag_bios_rev);
731 
732 	for (int chain = 0; chain < UEFI_PPAG_NUM_CHAINS; chain++) {
733 		for (int subband = 0; subband < n_subbands; subband++)
734 			fwrt->ppag_chains[chain].subbands[subband] =
735 				data->vals[chain * n_subbands + subband];
736 	}
737 
738 	iwl_bios_print_ppag(fwrt, n_subbands);
739 	fwrt->ppag_bios_source = BIOS_SOURCE_UEFI;
740 out:
741 	kfree(data);
742 	return ret;
743 }
744 
iwl_uefi_get_tas_table(struct iwl_fw_runtime * fwrt,struct iwl_tas_data * tas_data)745 int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt,
746 			   struct iwl_tas_data *tas_data)
747 {
748 	struct uefi_cnv_var_wtas *uefi_tas;
749 	int ret, enabled;
750 
751 	uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME,
752 						  "WTAS", sizeof(*uefi_tas), NULL);
753 	if (IS_ERR(uefi_tas))
754 		return -EINVAL;
755 
756 	if (uefi_tas->revision < IWL_UEFI_MIN_WTAS_REVISION ||
757 	    uefi_tas->revision > IWL_UEFI_MAX_WTAS_REVISION) {
758 		ret = -EINVAL;
759 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n",
760 				uefi_tas->revision);
761 		goto out;
762 	}
763 
764 	IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n",
765 			uefi_tas->tas_selection);
766 
767 	enabled = uefi_tas->tas_selection & IWL_WTAS_ENABLED_MSK;
768 	tas_data->table_source = BIOS_SOURCE_UEFI;
769 	tas_data->table_revision = uefi_tas->revision;
770 	tas_data->tas_selection = uefi_tas->tas_selection;
771 
772 	IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n",
773 			enabled ? "is" : "not");
774 
775 	IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n",
776 			uefi_tas->revision);
777 	if (uefi_tas->black_list_size > IWL_WTAS_BLACK_LIST_MAX) {
778 		IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %d\n",
779 				uefi_tas->black_list_size);
780 		ret = -EINVAL;
781 		goto out;
782 	}
783 
784 	tas_data->block_list_size = uefi_tas->black_list_size;
785 	IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size);
786 
787 	for (u8 i = 0; i < uefi_tas->black_list_size; i++) {
788 		tas_data->block_list_array[i] = uefi_tas->black_list[i];
789 		IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n",
790 				uefi_tas->black_list[i]);
791 	}
792 	ret = enabled;
793 out:
794 	kfree(uefi_tas);
795 	return ret;
796 }
797 
iwl_uefi_get_pwr_limit(struct iwl_fw_runtime * fwrt,u64 * dflt_pwr_limit)798 int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt,
799 			   u64 *dflt_pwr_limit)
800 {
801 	struct uefi_cnv_var_splc *data;
802 	int ret = 0;
803 
804 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME,
805 					      "SPLC", sizeof(*data), NULL);
806 	if (IS_ERR(data))
807 		return -EINVAL;
808 
809 	if (data->revision != IWL_UEFI_SPLC_REVISION) {
810 		ret = -EINVAL;
811 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI SPLC revision:%d\n",
812 				data->revision);
813 		goto out;
814 	}
815 	*dflt_pwr_limit = data->default_pwr_limit;
816 out:
817 	kfree(data);
818 	return ret;
819 }
820 
iwl_uefi_get_mcc(struct iwl_fw_runtime * fwrt,char * mcc)821 int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)
822 {
823 	struct uefi_cnv_var_wrdd *data;
824 	int ret = 0;
825 
826 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME,
827 					      "WRDD", sizeof(*data), NULL);
828 	if (IS_ERR(data))
829 		return -EINVAL;
830 
831 	if (data->revision != IWL_UEFI_WRDD_REVISION) {
832 		ret = -EINVAL;
833 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n",
834 				data->revision);
835 		goto out;
836 	}
837 
838 	if (data->mcc != BIOS_MCC_CHINA) {
839 		ret = -EINVAL;
840 		IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n");
841 		goto out;
842 	}
843 
844 	mcc[0] = (data->mcc >> 8) & 0xff;
845 	mcc[1] = data->mcc & 0xff;
846 	mcc[2] = '\0';
847 out:
848 	kfree(data);
849 	return ret;
850 }
851 
iwl_uefi_get_eckv(struct iwl_fw_runtime * fwrt,u32 * extl_clk)852 int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)
853 {
854 	struct uefi_cnv_var_eckv *data;
855 	int ret = 0;
856 
857 	data = iwl_uefi_get_verified_variable_guid(fwrt->trans,
858 						   &IWL_EFI_WIFI_BT_GUID,
859 						   IWL_UEFI_ECKV_NAME,
860 						   "ECKV", sizeof(*data), NULL);
861 	if (IS_ERR(data))
862 		return -EINVAL;
863 
864 	if (data->revision != IWL_UEFI_ECKV_REVISION) {
865 		ret = -EINVAL;
866 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI ECKV revision:%d\n",
867 				data->revision);
868 		goto out;
869 	}
870 	*extl_clk = data->ext_clock_valid;
871 out:
872 	kfree(data);
873 	return ret;
874 }
875 
iwl_uefi_get_wbem(struct iwl_fw_runtime * fwrt,u32 * value)876 int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)
877 {
878 	struct uefi_cnv_wlan_wbem_data *data;
879 	int ret = 0;
880 
881 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WBEM_NAME,
882 					      "WBEM", sizeof(*data), NULL);
883 	if (IS_ERR(data))
884 		return -EINVAL;
885 
886 	if (data->revision != IWL_UEFI_WBEM_REVISION) {
887 		ret = -EINVAL;
888 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WBEM revision:%d\n",
889 				data->revision);
890 		goto out;
891 	}
892 	*value = data->wbem_320mhz_per_mcc & IWL_UEFI_WBEM_REV0_MASK;
893 	fwrt->wbem_source = BIOS_SOURCE_UEFI;
894 	fwrt->wbem_revision = data->revision;
895 	IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from UEFI\n");
896 out:
897 	kfree(data);
898 	return ret;
899 }
900 
iwl_uefi_load_dsm_values(struct iwl_fw_runtime * fwrt)901 static int iwl_uefi_load_dsm_values(struct iwl_fw_runtime *fwrt)
902 {
903 	struct uefi_cnv_var_general_cfg *data;
904 	int ret = -EINVAL;
905 
906 	BUILD_BUG_ON(ARRAY_SIZE(data->functions) < ARRAY_SIZE(fwrt->dsm_values));
907 
908 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME,
909 					      "DSM", sizeof(*data), NULL);
910 	if (IS_ERR(data))
911 		return -EINVAL;
912 
913 	if (data->revision != IWL_UEFI_DSM_REVISION) {
914 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSM revision:%d\n",
915 				data->revision);
916 		goto out;
917 	}
918 	fwrt->dsm_revision = data->revision;
919 	fwrt->dsm_source = BIOS_SOURCE_UEFI;
920 
921 	fwrt->dsm_funcs_valid = data->functions[DSM_FUNC_QUERY];
922 
923 	/*
924 	 * Make sure we don't load the DSM values twice. Set this only after we
925 	 * validated the DSM table so that if the table in UEFI is not valid,
926 	 * we will fallback to ACPI.
927 	 */
928 	fwrt->dsm_funcs_valid |= BIT(DSM_FUNC_QUERY);
929 
930 	for (int func = 0; func < ARRAY_SIZE(fwrt->dsm_values); func++) {
931 		if (!(fwrt->dsm_funcs_valid & BIT(func))) {
932 			IWL_DEBUG_RADIO(fwrt, "DSM func %d not in 0x%x\n",
933 					func, fwrt->dsm_funcs_valid);
934 			continue;
935 		}
936 
937 		fwrt->dsm_values[func] = data->functions[func];
938 
939 		IWL_DEBUG_RADIO(fwrt,
940 				"UEFI: DSM func=%d: value=%d\n", func,
941 				fwrt->dsm_values[func]);
942 	}
943 	ret = 0;
944 
945 out:
946 	kfree(data);
947 	return ret;
948 }
949 
iwl_uefi_get_dsm(struct iwl_fw_runtime * fwrt,enum iwl_dsm_funcs func,u32 * value)950 int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
951 		     u32 *value)
952 {
953 	/* Not supported function index */
954 	if (func >= DSM_FUNC_NUM_FUNCS || func == 5)
955 		return -EOPNOTSUPP;
956 
957 	if (!fwrt->dsm_funcs_valid) {
958 		int ret = iwl_uefi_load_dsm_values(fwrt);
959 
960 		if (ret)
961 			return ret;
962 	}
963 
964 	if (!(fwrt->dsm_funcs_valid & BIT(func))) {
965 		IWL_DEBUG_RADIO(fwrt, "DSM func %d not in 0x%x\n",
966 				func, fwrt->dsm_funcs_valid);
967 		return -EINVAL;
968 	}
969 
970 	*value = fwrt->dsm_values[func];
971 
972 	IWL_DEBUG_RADIO(fwrt,
973 			"UEFI: DSM func=%d: value=%d\n", func, *value);
974 
975 	return 0;
976 }
977 
iwl_uefi_get_puncturing(struct iwl_fw_runtime * fwrt)978 int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt)
979 {
980 	struct uefi_cnv_var_puncturing_data *data;
981 	int ret = 0;
982 
983 	data = iwl_uefi_get_verified_variable(fwrt->trans,
984 					      IWL_UEFI_PUNCTURING_NAME,
985 					      "UefiCnvWlanPuncturing",
986 					      sizeof(*data), NULL);
987 	if (IS_ERR(data))
988 		return -EINVAL;
989 
990 	if (data->revision != IWL_UEFI_PUNCTURING_REVISION) {
991 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PUNCTURING rev:%d\n",
992 				data->revision);
993 		ret = -EINVAL;
994 	} else {
995 		fwrt->puncturing_source = BIOS_SOURCE_UEFI;
996 		fwrt->puncturing_revision = data->revision;
997 		fwrt->bios_puncturing = data->puncturing;
998 		IWL_DEBUG_RADIO(fwrt, "Loaded puncturing bits from UEFI: %d\n",
999 				fwrt->bios_puncturing);
1000 	}
1001 
1002 	kfree(data);
1003 	return ret;
1004 }
1005 IWL_EXPORT_SYMBOL(iwl_uefi_get_puncturing);
1006 
iwl_uefi_get_dsbr(struct iwl_fw_runtime * fwrt,u32 * value)1007 int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value)
1008 {
1009 	struct uefi_cnv_wlan_dsbr_data *data;
1010 	int ret = 0;
1011 
1012 	data = iwl_uefi_get_verified_variable_guid(fwrt->trans,
1013 						   &IWL_EFI_WIFI_BT_GUID,
1014 						   IWL_UEFI_DSBR_NAME, "DSBR",
1015 						   sizeof(*data), NULL);
1016 	if (IS_ERR(data))
1017 		return -EINVAL;
1018 
1019 	if (data->revision != IWL_UEFI_DSBR_REVISION) {
1020 		ret = -EINVAL;
1021 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSBR revision:%d\n",
1022 				data->revision);
1023 		goto out;
1024 	}
1025 	*value = data->config;
1026 	IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from UEFI value: 0x%x\n",
1027 			*value);
1028 out:
1029 	kfree(data);
1030 	return ret;
1031 }
1032 
iwl_uefi_get_phy_filters(struct iwl_fw_runtime * fwrt)1033 int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt)
1034 {
1035 	struct uefi_cnv_wpfc_data *data __free(kfree);
1036 	struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters;
1037 
1038 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WPFC_NAME,
1039 					      "WPFC", sizeof(*data), NULL);
1040 	if (IS_ERR(data))
1041 		return -EINVAL;
1042 
1043 	if (data->revision != 0) {
1044 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WPFC revision:%d\n",
1045 			data->revision);
1046 		return -EINVAL;
1047 	}
1048 
1049 	BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
1050 		     ARRAY_SIZE(data->chains));
1051 
1052 	for (int i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
1053 		filters->filter_cfg_chains[i] = cpu_to_le32(data->chains[i]);
1054 		IWL_DEBUG_RADIO(fwrt, "WPFC: chain %d: %u\n", i, data->chains[i]);
1055 	}
1056 
1057 	IWL_DEBUG_RADIO(fwrt, "Loaded WPFC config from UEFI\n");
1058 	return 0;
1059 }
1060