Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/fw/uefi.c
48285 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright(c) 2021-2025 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
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
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 *
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 *
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
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
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
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
IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
252
le32_to_cpu(tlv_sku_id->data[0]),
253
le32_to_cpu(tlv_sku_id->data[1]),
254
le32_to_cpu(tlv_sku_id->data[2]));
255
256
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
257
len -= ALIGN(tlv_len, 4);
258
259
if (sku_id[0] == tlv_sku_id->data[0] &&
260
sku_id[1] == tlv_sku_id->data[1] &&
261
sku_id[2] == tlv_sku_id->data[2]) {
262
int ret = iwl_uefi_reduce_power_section(trans,
263
data, len,
264
pnvm_data);
265
if (!ret)
266
return 0;
267
} else {
268
IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
269
}
270
} else {
271
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
272
len -= ALIGN(tlv_len, 4);
273
}
274
}
275
276
return -ENOENT;
277
}
278
279
u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
280
{
281
struct pnvm_sku_package *package;
282
unsigned long package_size;
283
u8 *data;
284
285
package = iwl_uefi_get_verified_variable(trans,
286
IWL_UEFI_REDUCED_POWER_NAME,
287
"Reduced Power",
288
sizeof(*package),
289
&package_size);
290
if (IS_ERR(package))
291
return ERR_CAST(package);
292
293
IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
294
package->rev, package->total_size, package->n_skus);
295
296
*len = package_size - sizeof(*package);
297
data = kmemdup(package->data, *len, GFP_KERNEL);
298
if (!data) {
299
kfree(package);
300
return ERR_PTR(-ENOMEM);
301
}
302
303
kfree(package);
304
305
return data;
306
}
307
308
static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data,
309
struct iwl_trans *trans)
310
{
311
if (common_step_data->revision != 1)
312
return -EINVAL;
313
314
trans->conf.mbx_addr_0_step =
315
(u32)common_step_data->revision |
316
(u32)common_step_data->cnvi_eq_channel << 8 |
317
(u32)common_step_data->cnvr_eq_channel << 16 |
318
(u32)common_step_data->radio1 << 24;
319
trans->conf.mbx_addr_1_step = (u32)common_step_data->radio2;
320
return 0;
321
}
322
323
void iwl_uefi_get_step_table(struct iwl_trans *trans)
324
{
325
struct uefi_cnv_common_step_data *data;
326
int ret;
327
328
if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
329
return;
330
331
data = iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_BT_GUID,
332
IWL_UEFI_STEP_NAME,
333
"STEP", sizeof(*data), NULL);
334
if (IS_ERR(data))
335
return;
336
337
ret = iwl_uefi_step_parse(data, trans);
338
if (ret < 0)
339
IWL_DEBUG_FW(trans, "Cannot read STEP tables. rev is invalid\n");
340
341
kfree(data);
342
}
343
IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table);
344
345
static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
346
struct iwl_fw_runtime *fwrt)
347
{
348
int i, j;
349
350
if (sgom_data->revision != 1)
351
return -EINVAL;
352
353
memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
354
sizeof(fwrt->sgom_table.offset_map));
355
356
for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
357
for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
358
/* since each byte is composed of to values, */
359
/* one for each letter, */
360
/* extract and check each of them separately */
361
u8 value = fwrt->sgom_table.offset_map[i][j];
362
u8 low = value & 0xF;
363
u8 high = (value & 0xF0) >> 4;
364
365
if (high > fwrt->geo_num_profiles)
366
high = 0;
367
if (low > fwrt->geo_num_profiles)
368
low = 0;
369
fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
370
}
371
}
372
373
fwrt->sgom_enabled = true;
374
return 0;
375
}
376
377
void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
378
struct iwl_fw_runtime *fwrt)
379
{
380
struct uefi_cnv_wlan_sgom_data *data;
381
int ret;
382
383
if (!fwrt->geo_enabled)
384
return;
385
386
data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME,
387
"SGOM", sizeof(*data), NULL);
388
if (IS_ERR(data))
389
return;
390
391
ret = iwl_uefi_sgom_parse(data, fwrt);
392
if (ret < 0)
393
IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");
394
395
kfree(data);
396
}
397
IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
398
399
static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,
400
struct iwl_fw_runtime *fwrt)
401
{
402
if (uats_data->revision != 1)
403
return -EINVAL;
404
405
memcpy(fwrt->uats_table.offset_map, uats_data->offset_map,
406
sizeof(fwrt->uats_table.offset_map));
407
408
fwrt->uats_valid = true;
409
410
return 0;
411
}
412
413
void iwl_uefi_get_uats_table(struct iwl_trans *trans,
414
struct iwl_fw_runtime *fwrt)
415
{
416
struct uefi_cnv_wlan_uats_data *data;
417
int ret;
418
419
data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME,
420
"UATS", sizeof(*data), NULL);
421
if (IS_ERR(data))
422
return;
423
424
ret = iwl_uefi_uats_parse(data, fwrt);
425
if (ret < 0)
426
IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n");
427
kfree(data);
428
}
429
IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);
430
431
static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
432
struct uefi_sar_profile *uefi_sar_prof,
433
u8 prof_index, bool enabled)
434
{
435
memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof,
436
sizeof(struct uefi_sar_profile));
437
438
fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK;
439
}
440
441
int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)
442
{
443
struct uefi_cnv_var_wrds *data;
444
int ret = 0;
445
446
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME,
447
"WRDS", sizeof(*data), NULL);
448
if (IS_ERR(data))
449
return -EINVAL;
450
451
if (data->revision != IWL_UEFI_WRDS_REVISION) {
452
ret = -EINVAL;
453
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n",
454
data->revision);
455
goto out;
456
}
457
458
/* The profile from WRDS is officially profile 1, but goes
459
* into sar_profiles[0] (because we don't have a profile 0).
460
*/
461
iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode);
462
out:
463
kfree(data);
464
return ret;
465
}
466
467
int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
468
{
469
struct uefi_cnv_var_ewrd *data;
470
int i, ret = 0;
471
472
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME,
473
"EWRD", sizeof(*data), NULL);
474
if (IS_ERR(data))
475
return -EINVAL;
476
477
if (data->revision != IWL_UEFI_EWRD_REVISION) {
478
ret = -EINVAL;
479
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n",
480
data->revision);
481
goto out;
482
}
483
484
if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
485
ret = -EINVAL;
486
goto out;
487
}
488
489
for (i = 0; i < data->num_profiles; i++)
490
/* The EWRD profiles officially go from 2 to 4, but we
491
* save them in sar_profiles[1-3] (because we don't
492
* have profile 0). So in the array we start from 1.
493
*/
494
iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1,
495
data->mode);
496
497
out:
498
kfree(data);
499
return ret;
500
}
501
502
int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
503
{
504
struct uefi_cnv_var_wgds *data;
505
int i, ret = 0;
506
507
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME,
508
"WGDS", sizeof(*data), NULL);
509
if (IS_ERR(data))
510
return -EINVAL;
511
512
if (data->revision != IWL_UEFI_WGDS_REVISION) {
513
ret = -EINVAL;
514
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n",
515
data->revision);
516
goto out;
517
}
518
519
if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM ||
520
data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) {
521
ret = -EINVAL;
522
IWL_DEBUG_RADIO(fwrt, "Invalid number of profiles in WGDS: %d\n",
523
data->num_profiles);
524
goto out;
525
}
526
527
fwrt->geo_rev = data->revision;
528
for (i = 0; i < data->num_profiles; i++)
529
memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i],
530
sizeof(struct iwl_geo_profile));
531
532
fwrt->geo_num_profiles = data->num_profiles;
533
fwrt->geo_enabled = true;
534
out:
535
kfree(data);
536
return ret;
537
}
538
539
int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
540
{
541
struct uefi_cnv_var_ppag *data;
542
int ret = 0;
543
544
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME,
545
"PPAG", sizeof(*data), NULL);
546
if (IS_ERR(data))
547
return -EINVAL;
548
549
if (data->revision < IWL_UEFI_MIN_PPAG_REV ||
550
data->revision > IWL_UEFI_MAX_PPAG_REV) {
551
ret = -EINVAL;
552
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n",
553
data->revision);
554
goto out;
555
}
556
557
fwrt->ppag_bios_rev = data->revision;
558
fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes,
559
fwrt->ppag_bios_rev);
560
561
BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains));
562
memcpy(&fwrt->ppag_chains, &data->ppag_chains,
563
sizeof(data->ppag_chains));
564
fwrt->ppag_bios_source = BIOS_SOURCE_UEFI;
565
out:
566
kfree(data);
567
return ret;
568
}
569
570
int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt,
571
struct iwl_tas_data *tas_data)
572
{
573
struct uefi_cnv_var_wtas *uefi_tas;
574
int ret, enabled;
575
576
uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME,
577
"WTAS", sizeof(*uefi_tas), NULL);
578
if (IS_ERR(uefi_tas))
579
return -EINVAL;
580
581
if (uefi_tas->revision < IWL_UEFI_MIN_WTAS_REVISION ||
582
uefi_tas->revision > IWL_UEFI_MAX_WTAS_REVISION) {
583
ret = -EINVAL;
584
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n",
585
uefi_tas->revision);
586
goto out;
587
}
588
589
IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n",
590
uefi_tas->tas_selection);
591
592
enabled = uefi_tas->tas_selection & IWL_WTAS_ENABLED_MSK;
593
tas_data->table_source = BIOS_SOURCE_UEFI;
594
tas_data->table_revision = uefi_tas->revision;
595
tas_data->tas_selection = uefi_tas->tas_selection;
596
597
IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n",
598
enabled ? "is" : "not");
599
600
IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n",
601
uefi_tas->revision);
602
if (uefi_tas->black_list_size > IWL_WTAS_BLACK_LIST_MAX) {
603
IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %d\n",
604
uefi_tas->black_list_size);
605
ret = -EINVAL;
606
goto out;
607
}
608
609
tas_data->block_list_size = uefi_tas->black_list_size;
610
IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size);
611
612
for (u8 i = 0; i < uefi_tas->black_list_size; i++) {
613
tas_data->block_list_array[i] = uefi_tas->black_list[i];
614
IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n",
615
uefi_tas->black_list[i]);
616
}
617
ret = enabled;
618
out:
619
kfree(uefi_tas);
620
return ret;
621
}
622
623
int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt,
624
u64 *dflt_pwr_limit)
625
{
626
struct uefi_cnv_var_splc *data;
627
int ret = 0;
628
629
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME,
630
"SPLC", sizeof(*data), NULL);
631
if (IS_ERR(data))
632
return -EINVAL;
633
634
if (data->revision != IWL_UEFI_SPLC_REVISION) {
635
ret = -EINVAL;
636
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI SPLC revision:%d\n",
637
data->revision);
638
goto out;
639
}
640
*dflt_pwr_limit = data->default_pwr_limit;
641
out:
642
kfree(data);
643
return ret;
644
}
645
646
int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)
647
{
648
struct uefi_cnv_var_wrdd *data;
649
int ret = 0;
650
651
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME,
652
"WRDD", sizeof(*data), NULL);
653
if (IS_ERR(data))
654
return -EINVAL;
655
656
if (data->revision != IWL_UEFI_WRDD_REVISION) {
657
ret = -EINVAL;
658
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n",
659
data->revision);
660
goto out;
661
}
662
663
if (data->mcc != BIOS_MCC_CHINA) {
664
ret = -EINVAL;
665
IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n");
666
goto out;
667
}
668
669
mcc[0] = (data->mcc >> 8) & 0xff;
670
mcc[1] = data->mcc & 0xff;
671
mcc[2] = '\0';
672
out:
673
kfree(data);
674
return ret;
675
}
676
677
int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)
678
{
679
struct uefi_cnv_var_eckv *data;
680
int ret = 0;
681
682
data = iwl_uefi_get_verified_variable_guid(fwrt->trans,
683
&IWL_EFI_WIFI_BT_GUID,
684
IWL_UEFI_ECKV_NAME,
685
"ECKV", sizeof(*data), NULL);
686
if (IS_ERR(data))
687
return -EINVAL;
688
689
if (data->revision != IWL_UEFI_ECKV_REVISION) {
690
ret = -EINVAL;
691
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI ECKV revision:%d\n",
692
data->revision);
693
goto out;
694
}
695
*extl_clk = data->ext_clock_valid;
696
out:
697
kfree(data);
698
return ret;
699
}
700
701
int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)
702
{
703
struct uefi_cnv_wlan_wbem_data *data;
704
int ret = 0;
705
706
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WBEM_NAME,
707
"WBEM", sizeof(*data), NULL);
708
if (IS_ERR(data))
709
return -EINVAL;
710
711
if (data->revision != IWL_UEFI_WBEM_REVISION) {
712
ret = -EINVAL;
713
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WBEM revision:%d\n",
714
data->revision);
715
goto out;
716
}
717
*value = data->wbem_320mhz_per_mcc & IWL_UEFI_WBEM_REV0_MASK;
718
IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from UEFI\n");
719
out:
720
kfree(data);
721
return ret;
722
}
723
724
int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
725
u32 *value)
726
{
727
struct uefi_cnv_var_general_cfg *data;
728
int ret = -EINVAL;
729
730
/* Not supported function index */
731
if (func >= DSM_FUNC_NUM_FUNCS || func == 5)
732
return -EOPNOTSUPP;
733
734
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME,
735
"DSM", sizeof(*data), NULL);
736
if (IS_ERR(data))
737
return -EINVAL;
738
739
if (data->revision != IWL_UEFI_DSM_REVISION) {
740
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSM revision:%d\n",
741
data->revision);
742
goto out;
743
}
744
745
if (ARRAY_SIZE(data->functions) != UEFI_MAX_DSM_FUNCS) {
746
IWL_DEBUG_RADIO(fwrt, "Invalid size of DSM functions array\n");
747
goto out;
748
}
749
750
if (!(data->functions[DSM_FUNC_QUERY] & BIT(func))) {
751
IWL_DEBUG_RADIO(fwrt, "DSM func %d not in 0x%x\n",
752
func, data->functions[DSM_FUNC_QUERY]);
753
goto out;
754
}
755
756
*value = data->functions[func];
757
758
IWL_DEBUG_RADIO(fwrt,
759
"UEFI: DSM func=%d: value=%d\n", func, *value);
760
761
ret = 0;
762
out:
763
kfree(data);
764
return ret;
765
}
766
767
int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt)
768
{
769
struct uefi_cnv_var_puncturing_data *data;
770
/* default value is not enabled if there is any issue in reading
771
* uefi variable or revision is not supported
772
*/
773
int puncturing = 0;
774
775
data = iwl_uefi_get_verified_variable(fwrt->trans,
776
IWL_UEFI_PUNCTURING_NAME,
777
"UefiCnvWlanPuncturing",
778
sizeof(*data), NULL);
779
if (IS_ERR(data))
780
return puncturing;
781
782
if (data->revision != IWL_UEFI_PUNCTURING_REVISION) {
783
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PUNCTURING rev:%d\n",
784
data->revision);
785
} else {
786
puncturing = data->puncturing & IWL_UEFI_PUNCTURING_REV0_MASK;
787
IWL_DEBUG_RADIO(fwrt, "Loaded puncturing bits from UEFI: %d\n",
788
puncturing);
789
}
790
791
kfree(data);
792
return puncturing;
793
}
794
IWL_EXPORT_SYMBOL(iwl_uefi_get_puncturing);
795
796
int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value)
797
{
798
struct uefi_cnv_wlan_dsbr_data *data;
799
int ret = 0;
800
801
data = iwl_uefi_get_verified_variable_guid(fwrt->trans,
802
&IWL_EFI_WIFI_BT_GUID,
803
IWL_UEFI_DSBR_NAME, "DSBR",
804
sizeof(*data), NULL);
805
if (IS_ERR(data))
806
return -EINVAL;
807
808
if (data->revision != IWL_UEFI_DSBR_REVISION) {
809
ret = -EINVAL;
810
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSBR revision:%d\n",
811
data->revision);
812
goto out;
813
}
814
*value = data->config;
815
IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from UEFI value: 0x%x\n",
816
*value);
817
out:
818
kfree(data);
819
return ret;
820
}
821
822
int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt)
823
{
824
struct uefi_cnv_wpfc_data *data __free(kfree);
825
struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters;
826
827
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WPFC_NAME,
828
"WPFC", sizeof(*data), NULL);
829
if (IS_ERR(data))
830
return -EINVAL;
831
832
if (data->revision != 0) {
833
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WPFC revision:%d\n",
834
data->revision);
835
return -EINVAL;
836
}
837
838
BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
839
ARRAY_SIZE(data->chains));
840
841
for (int i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
842
filters->filter_cfg_chains[i] = cpu_to_le32(data->chains[i]);
843
IWL_DEBUG_RADIO(fwrt, "WPFC: chain %d: %u\n", i, data->chains[i]);
844
}
845
846
IWL_DEBUG_RADIO(fwrt, "Loaded WPFC config from UEFI\n");
847
return 0;
848
}
849
850