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