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