761 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright(c) 2021-2024 Intel Corporation4 */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_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b, \17 0xb2, 0xec, 0xf5, 0xa3, \18 0x59, 0x4f, 0x4a, 0xea)19 20struct iwl_uefi_pnvm_mem_desc {21 __le32 addr;22 __le32 size;23 const u8 data[];24} __packed;25 26static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid,27 unsigned long *data_size)28{29 efi_status_t status;30 void *data;31 32 if (!data_size)33 return ERR_PTR(-EINVAL);34 35 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))36 return ERR_PTR(-ENODEV);37 38 /* first call with NULL data to get the exact entry size */39 *data_size = 0;40 status = efi.get_variable(name, guid, NULL, data_size, NULL);41 if (status != EFI_BUFFER_TOO_SMALL || !*data_size)42 return ERR_PTR(-EIO);43 44 data = kmalloc(*data_size, GFP_KERNEL);45 if (!data)46 return ERR_PTR(-ENOMEM);47 48 status = efi.get_variable(name, guid, NULL, data_size, data);49 if (status != EFI_SUCCESS) {50 kfree(data);51 return ERR_PTR(-ENOENT);52 }53 54 return data;55}56 57void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)58{59 unsigned long package_size;60 void *data;61 62 *len = 0;63 64 data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID,65 &package_size);66 if (IS_ERR(data)) {67 IWL_DEBUG_FW(trans,68 "PNVM UEFI variable not found 0x%lx (len %lu)\n",69 PTR_ERR(data), package_size);70 return data;71 }72 73 IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);74 *len = package_size;75 76 return data;77}78 79static80void *iwl_uefi_get_verified_variable(struct iwl_trans *trans,81 efi_char16_t *uefi_var_name,82 char *var_name,83 unsigned int expected_size,84 unsigned long *size)85{86 void *var;87 unsigned long var_size;88 89 var = iwl_uefi_get_variable(uefi_var_name, &IWL_EFI_VAR_GUID,90 &var_size);91 92 if (IS_ERR(var)) {93 IWL_DEBUG_RADIO(trans,94 "%s UEFI variable not found 0x%lx\n", var_name,95 PTR_ERR(var));96 return var;97 }98 99 if (var_size < expected_size) {100 IWL_DEBUG_RADIO(trans,101 "Invalid %s UEFI variable len (%lu)\n",102 var_name, var_size);103 kfree(var);104 return ERR_PTR(-EINVAL);105 }106 107 IWL_DEBUG_RADIO(trans, "%s from UEFI with size %lu\n", var_name,108 var_size);109 110 if (size)111 *size = var_size;112 return var;113}114 115int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,116 u32 tlv_len, struct iwl_pnvm_image *pnvm_data)117{118 const struct iwl_uefi_pnvm_mem_desc *desc = (const void *)data;119 u32 data_len;120 121 if (tlv_len < sizeof(*desc)) {122 IWL_DEBUG_FW(trans, "TLV len (%d) is too small\n", tlv_len);123 return -EINVAL;124 }125 126 data_len = tlv_len - sizeof(*desc);127 128 IWL_DEBUG_FW(trans,129 "Handle IWL_UCODE_TLV_MEM_DESC, len %d data_len %d\n",130 tlv_len, data_len);131 132 if (le32_to_cpu(desc->size) != data_len) {133 IWL_DEBUG_FW(trans, "invalid mem desc size %d\n", desc->size);134 return -EINVAL;135 }136 137 if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) {138 IWL_DEBUG_FW(trans, "too many payloads to allocate in DRAM.\n");139 return -EINVAL;140 }141 142 IWL_DEBUG_FW(trans, "Adding data (size %d)\n", data_len);143 144 pnvm_data->chunks[pnvm_data->n_chunks].data = desc->data;145 pnvm_data->chunks[pnvm_data->n_chunks].len = data_len;146 pnvm_data->n_chunks++;147 148 return 0;149}150 151static int iwl_uefi_reduce_power_section(struct iwl_trans *trans,152 const u8 *data, size_t len,153 struct iwl_pnvm_image *pnvm_data)154{155 const struct iwl_ucode_tlv *tlv;156 157 IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");158 memset(pnvm_data, 0, sizeof(*pnvm_data));159 160 while (len >= sizeof(*tlv)) {161 u32 tlv_len, tlv_type;162 163 len -= sizeof(*tlv);164 tlv = (const void *)data;165 166 tlv_len = le32_to_cpu(tlv->length);167 tlv_type = le32_to_cpu(tlv->type);168 169 if (len < tlv_len) {170 IWL_ERR(trans, "invalid TLV len: %zd/%u\n",171 len, tlv_len);172 return -EINVAL;173 }174 175 data += sizeof(*tlv);176 177 switch (tlv_type) {178 case IWL_UCODE_TLV_MEM_DESC:179 if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len,180 pnvm_data))181 return -EINVAL;182 break;183 case IWL_UCODE_TLV_PNVM_SKU:184 IWL_DEBUG_FW(trans,185 "New REDUCE_POWER section started, stop parsing.\n");186 goto done;187 default:188 IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",189 tlv_type, tlv_len);190 break;191 }192 193 len -= ALIGN(tlv_len, 4);194 data += ALIGN(tlv_len, 4);195 }196 197done:198 if (!pnvm_data->n_chunks) {199 IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");200 return -ENOENT;201 }202 return 0;203}204 205int iwl_uefi_reduce_power_parse(struct iwl_trans *trans,206 const u8 *data, size_t len,207 struct iwl_pnvm_image *pnvm_data)208{209 const struct iwl_ucode_tlv *tlv;210 211 IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");212 213 while (len >= sizeof(*tlv)) {214 u32 tlv_len, tlv_type;215 216 len -= sizeof(*tlv);217 tlv = (const void *)data;218 219 tlv_len = le32_to_cpu(tlv->length);220 tlv_type = le32_to_cpu(tlv->type);221 222 if (len < tlv_len) {223 IWL_ERR(trans, "invalid TLV len: %zd/%u\n",224 len, tlv_len);225 return -EINVAL;226 }227 228 if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {229 const struct iwl_sku_id *sku_id =230 (const void *)(data + sizeof(*tlv));231 232 IWL_DEBUG_FW(trans,233 "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",234 tlv_len);235 IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",236 le32_to_cpu(sku_id->data[0]),237 le32_to_cpu(sku_id->data[1]),238 le32_to_cpu(sku_id->data[2]));239 240 data += sizeof(*tlv) + ALIGN(tlv_len, 4);241 len -= ALIGN(tlv_len, 4);242 243 if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&244 trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&245 trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {246 int ret = iwl_uefi_reduce_power_section(trans,247 data, len,248 pnvm_data);249 if (!ret)250 return 0;251 } else {252 IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");253 }254 } else {255 data += sizeof(*tlv) + ALIGN(tlv_len, 4);256 len -= ALIGN(tlv_len, 4);257 }258 }259 260 return -ENOENT;261}262 263u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)264{265 struct pnvm_sku_package *package;266 unsigned long package_size;267 u8 *data;268 269 package = iwl_uefi_get_verified_variable(trans,270 IWL_UEFI_REDUCED_POWER_NAME,271 "Reduced Power",272 sizeof(*package),273 &package_size);274 if (IS_ERR(package))275 return ERR_CAST(package);276 277 IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",278 package->rev, package->total_size, package->n_skus);279 280 *len = package_size - sizeof(*package);281 data = kmemdup(package->data, *len, GFP_KERNEL);282 if (!data) {283 kfree(package);284 return ERR_PTR(-ENOMEM);285 }286 287 kfree(package);288 289 return data;290}291 292static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data,293 struct iwl_trans *trans)294{295 if (common_step_data->revision != 1)296 return -EINVAL;297 298 trans->mbx_addr_0_step = (u32)common_step_data->revision |299 (u32)common_step_data->cnvi_eq_channel << 8 |300 (u32)common_step_data->cnvr_eq_channel << 16 |301 (u32)common_step_data->radio1 << 24;302 trans->mbx_addr_1_step = (u32)common_step_data->radio2;303 return 0;304}305 306void iwl_uefi_get_step_table(struct iwl_trans *trans)307{308 struct uefi_cnv_common_step_data *data;309 int ret;310 311 if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)312 return;313 314 data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_STEP_NAME,315 "STEP", sizeof(*data), NULL);316 if (IS_ERR(data))317 return;318 319 ret = iwl_uefi_step_parse(data, trans);320 if (ret < 0)321 IWL_DEBUG_FW(trans, "Cannot read STEP tables. rev is invalid\n");322 323 kfree(data);324}325IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table);326 327static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,328 struct iwl_fw_runtime *fwrt)329{330 int i, j;331 332 if (sgom_data->revision != 1)333 return -EINVAL;334 335 memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,336 sizeof(fwrt->sgom_table.offset_map));337 338 for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {339 for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {340 /* since each byte is composed of to values, */341 /* one for each letter, */342 /* extract and check each of them separately */343 u8 value = fwrt->sgom_table.offset_map[i][j];344 u8 low = value & 0xF;345 u8 high = (value & 0xF0) >> 4;346 347 if (high > fwrt->geo_num_profiles)348 high = 0;349 if (low > fwrt->geo_num_profiles)350 low = 0;351 fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;352 }353 }354 355 fwrt->sgom_enabled = true;356 return 0;357}358 359void iwl_uefi_get_sgom_table(struct iwl_trans *trans,360 struct iwl_fw_runtime *fwrt)361{362 struct uefi_cnv_wlan_sgom_data *data;363 int ret;364 365 if (!fwrt->geo_enabled)366 return;367 368 data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME,369 "SGOM", sizeof(*data), NULL);370 if (IS_ERR(data))371 return;372 373 ret = iwl_uefi_sgom_parse(data, fwrt);374 if (ret < 0)375 IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");376 377 kfree(data);378}379IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);380 381static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,382 struct iwl_fw_runtime *fwrt)383{384 if (uats_data->revision != 1)385 return -EINVAL;386 387 memcpy(fwrt->uats_table.offset_map, uats_data->offset_map,388 sizeof(fwrt->uats_table.offset_map));389 return 0;390}391 392int iwl_uefi_get_uats_table(struct iwl_trans *trans,393 struct iwl_fw_runtime *fwrt)394{395 struct uefi_cnv_wlan_uats_data *data;396 int ret;397 398 data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME,399 "UATS", sizeof(*data), NULL);400 if (IS_ERR(data))401 return -EINVAL;402 403 ret = iwl_uefi_uats_parse(data, fwrt);404 if (ret < 0) {405 IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n");406 kfree(data);407 return ret;408 }409 410 kfree(data);411 return 0;412}413IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);414 415static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,416 struct uefi_sar_profile *uefi_sar_prof,417 u8 prof_index, bool enabled)418{419 memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof,420 sizeof(struct uefi_sar_profile));421 422 fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK;423}424 425int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)426{427 struct uefi_cnv_var_wrds *data;428 int ret = 0;429 430 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME,431 "WRDS", sizeof(*data), NULL);432 if (IS_ERR(data))433 return -EINVAL;434 435 if (data->revision != IWL_UEFI_WRDS_REVISION) {436 ret = -EINVAL;437 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n",438 data->revision);439 goto out;440 }441 442 /* The profile from WRDS is officially profile 1, but goes443 * into sar_profiles[0] (because we don't have a profile 0).444 */445 iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode);446out:447 kfree(data);448 return ret;449}450 451int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)452{453 struct uefi_cnv_var_ewrd *data;454 int i, ret = 0;455 456 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME,457 "EWRD", sizeof(*data), NULL);458 if (IS_ERR(data))459 return -EINVAL;460 461 if (data->revision != IWL_UEFI_EWRD_REVISION) {462 ret = -EINVAL;463 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n",464 data->revision);465 goto out;466 }467 468 if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {469 ret = -EINVAL;470 goto out;471 }472 473 for (i = 0; i < data->num_profiles; i++)474 /* The EWRD profiles officially go from 2 to 4, but we475 * save them in sar_profiles[1-3] (because we don't476 * have profile 0). So in the array we start from 1.477 */478 iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1,479 data->mode);480 481out:482 kfree(data);483 return ret;484}485 486int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)487{488 struct uefi_cnv_var_wgds *data;489 int i, ret = 0;490 491 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME,492 "WGDS", sizeof(*data), NULL);493 if (IS_ERR(data))494 return -EINVAL;495 496 if (data->revision != IWL_UEFI_WGDS_REVISION) {497 ret = -EINVAL;498 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n",499 data->revision);500 goto out;501 }502 503 if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM ||504 data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) {505 ret = -EINVAL;506 IWL_DEBUG_RADIO(fwrt, "Invalid number of profiles in WGDS: %d\n",507 data->num_profiles);508 goto out;509 }510 511 fwrt->geo_rev = data->revision;512 for (i = 0; i < data->num_profiles; i++)513 memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i],514 sizeof(struct iwl_geo_profile));515 516 fwrt->geo_num_profiles = data->num_profiles;517 fwrt->geo_enabled = true;518out:519 kfree(data);520 return ret;521}522 523int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)524{525 struct uefi_cnv_var_ppag *data;526 int ret = 0;527 528 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME,529 "PPAG", sizeof(*data), NULL);530 if (IS_ERR(data))531 return -EINVAL;532 533 if (data->revision < IWL_UEFI_MIN_PPAG_REV ||534 data->revision > IWL_UEFI_MAX_PPAG_REV) {535 ret = -EINVAL;536 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n",537 data->revision);538 goto out;539 }540 541 fwrt->ppag_ver = data->revision;542 fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes,543 fwrt->ppag_ver);544 545 BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains));546 memcpy(&fwrt->ppag_chains, &data->ppag_chains,547 sizeof(data->ppag_chains));548out:549 kfree(data);550 return ret;551}552 553int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt,554 struct iwl_tas_data *tas_data)555{556 struct uefi_cnv_var_wtas *uefi_tas;557 int ret = 0, enabled, i;558 559 uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME,560 "WTAS", sizeof(*uefi_tas), NULL);561 if (IS_ERR(uefi_tas))562 return -EINVAL;563 564 if (uefi_tas->revision != IWL_UEFI_WTAS_REVISION) {565 ret = -EINVAL;566 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n",567 uefi_tas->revision);568 goto out;569 }570 571 enabled = iwl_parse_tas_selection(fwrt, tas_data,572 uefi_tas->tas_selection);573 if (!enabled) {574 IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");575 ret = 0;576 goto out;577 }578 579 IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n",580 uefi_tas->revision);581 if (uefi_tas->black_list_size > IWL_WTAS_BLACK_LIST_MAX) {582 IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %d\n",583 uefi_tas->black_list_size);584 ret = -EINVAL;585 goto out;586 }587 tas_data->block_list_size = cpu_to_le32(uefi_tas->black_list_size);588 IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size);589 590 for (i = 0; i < uefi_tas->black_list_size; i++) {591 tas_data->block_list_array[i] =592 cpu_to_le32(uefi_tas->black_list[i]);593 IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n",594 uefi_tas->black_list[i]);595 }596out:597 kfree(uefi_tas);598 return ret;599}600 601int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt,602 u64 *dflt_pwr_limit)603{604 struct uefi_cnv_var_splc *data;605 int ret = 0;606 607 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME,608 "SPLC", sizeof(*data), NULL);609 if (IS_ERR(data))610 return -EINVAL;611 612 if (data->revision != IWL_UEFI_SPLC_REVISION) {613 ret = -EINVAL;614 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI SPLC revision:%d\n",615 data->revision);616 goto out;617 }618 *dflt_pwr_limit = data->default_pwr_limit;619out:620 kfree(data);621 return ret;622}623 624int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)625{626 struct uefi_cnv_var_wrdd *data;627 int ret = 0;628 629 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME,630 "WRDD", sizeof(*data), NULL);631 if (IS_ERR(data))632 return -EINVAL;633 634 if (data->revision != IWL_UEFI_WRDD_REVISION) {635 ret = -EINVAL;636 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n",637 data->revision);638 goto out;639 }640 641 if (data->mcc != BIOS_MCC_CHINA) {642 ret = -EINVAL;643 IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n");644 goto out;645 }646 647 mcc[0] = (data->mcc >> 8) & 0xff;648 mcc[1] = data->mcc & 0xff;649 mcc[2] = '\0';650out:651 kfree(data);652 return ret;653}654 655int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)656{657 struct uefi_cnv_var_eckv *data;658 int ret = 0;659 660 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME,661 "ECKV", sizeof(*data), NULL);662 if (IS_ERR(data))663 return -EINVAL;664 665 if (data->revision != IWL_UEFI_ECKV_REVISION) {666 ret = -EINVAL;667 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n",668 data->revision);669 goto out;670 }671 *extl_clk = data->ext_clock_valid;672out:673 kfree(data);674 return ret;675}676 677int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)678{679 struct uefi_cnv_wlan_wbem_data *data;680 int ret = 0;681 682 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WBEM_NAME,683 "WBEM", sizeof(*data), NULL);684 if (IS_ERR(data))685 return -EINVAL;686 687 if (data->revision != IWL_UEFI_WBEM_REVISION) {688 ret = -EINVAL;689 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WBEM revision:%d\n",690 data->revision);691 goto out;692 }693 *value = data->wbem_320mhz_per_mcc & IWL_UEFI_WBEM_REV0_MASK;694 IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from UEFI\n");695out:696 kfree(data);697 return ret;698}699 700int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,701 u32 *value)702{703 struct uefi_cnv_var_general_cfg *data;704 int ret = -EINVAL;705 706 /* Not supported function index */707 if (func >= DSM_FUNC_NUM_FUNCS || func == 5)708 return -EOPNOTSUPP;709 710 data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME,711 "DSM", sizeof(*data), NULL);712 if (IS_ERR(data))713 return -EINVAL;714 715 if (data->revision != IWL_UEFI_DSM_REVISION) {716 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSM revision:%d\n",717 data->revision);718 goto out;719 }720 721 if (ARRAY_SIZE(data->functions) != UEFI_MAX_DSM_FUNCS) {722 IWL_DEBUG_RADIO(fwrt, "Invalid size of DSM functions array\n");723 goto out;724 }725 726 *value = data->functions[func];727 ret = 0;728out:729 kfree(data);730 return ret;731}732 733int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt)734{735 struct uefi_cnv_var_puncturing_data *data;736 /* default value is not enabled if there is any issue in reading737 * uefi variable or revision is not supported738 */739 int puncturing = 0;740 741 data = iwl_uefi_get_verified_variable(fwrt->trans,742 IWL_UEFI_PUNCTURING_NAME,743 "UefiCnvWlanPuncturing",744 sizeof(*data), NULL);745 if (IS_ERR(data))746 return puncturing;747 748 if (data->revision != IWL_UEFI_PUNCTURING_REVISION) {749 IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PUNCTURING rev:%d\n",750 data->revision);751 } else {752 puncturing = data->puncturing & IWL_UEFI_PUNCTURING_REV0_MASK;753 IWL_DEBUG_RADIO(fwrt, "Loaded puncturing bits from UEFI: %d\n",754 puncturing);755 }756 757 kfree(data);758 return puncturing;759}760IWL_EXPORT_SYMBOL(iwl_uefi_get_puncturing);761