From d96966a8aa10028a97ff9db6aa078eec8dce5da0 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Wed, 23 Sep 2026 16:04:54 +0300 Subject: wifi: iwlwifi: uefi: add a way to probe connectivity UEFI variables The connectivity UEFI variables are only trusted when the platform attests they are locked. When the lock status is unknown, the trust of each variable has to be discovered on its own by checking whether it is write protected. Probing depends on trying to rewrite UEFI variables and it is only attempted on non-x86 platforms. If the write is rejected the variable is considered locked and therefore trusted, otherwise it is treated as not trusted. Separate reading such a variable into two steps: iwl_uefi_get_variable_guid() only fetches and validates the variable, while iwl_uefi_get_verified_variable_guid() layers the trust decision on top of it. The latter now takes an iwl_fw_runtime so it can consult the lock status. Rename its WiFi-GUID wrapper from iwl_uefi_get_verified_variable() to iwl_uefi_get_verified_wifi_var(). With the trust check now performed by every iwl_uefi_get_*() reached through the GET_BIOS_TABLE() macro, the per-table lock-status gate in that macro is redundant, so drop it here. This move, and the new unknown-status handling, affect only the tables reached through GET_BIOS_TABLE(), i.e. those that have both a UEFI and an ACPI source. Assisted-by: GitHub-Copilot:claude-opus-4-8 Signed-off-by: Shahar Tzarfati Link: https://patch.msgid.link/20260923160318.6caa9ca17ffe.I877c0d6ab5126edb3e4f1f187ca1e20e563b2ec4@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/init.c | 4 +- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 4 +- drivers/net/wireless/intel/iwlwifi/fw/regulatory.h | 3 + drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 2 +- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 198 +++++++++++++++------ 5 files changed, 148 insertions(+), 63 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c index d1d8058ad29f..d69757b0149d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/init.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2019-2021, 2024-2025 Intel Corporation + * Copyright (C) 2019-2021, 2024-2026 Intel Corporation */ #include "iwl-drv.h" #include "runtime.h" @@ -31,6 +31,8 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, fwrt->sanitize_ops = sanitize_ops; fwrt->sanitize_ctx = sanitize_ctx; fwrt->ops_ctx = ops_ctx; + /* default until the connectivity GUID lock status is resolved */ + fwrt->uefi_tables_lock_status = UEFI_CNV_GUID_UNKNOWN; for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) { fwrt->dump.wks[i].idx = i; INIT_DELAYED_WORK(&fwrt->dump.wks[i].wk, iwl_fw_error_dump_wk); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 8dff97b8e0f1..c62f7f53323a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -11,9 +11,7 @@ #define GET_BIOS_TABLE(__name, ...) \ do { \ - int ret = -ENOENT; \ - if (fwrt->uefi_tables_lock_status > UEFI_CNV_GUID_UNLOCKED) \ - ret = iwl_uefi_get_ ## __name(__VA_ARGS__); \ + int ret = iwl_uefi_get_ ## __name(__VA_ARGS__); \ if (ret < 0) \ ret = iwl_acpi_get_ ## __name(__VA_ARGS__); \ return ret; \ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 08fd11adf905..cd6218e3a740 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -53,11 +53,14 @@ * @UEFI_CNV_GUID_UNLOCKED: the tables are unlocked * @UEFI_CNV_GUID_LOCKED: the tables are locked * @UEFI_CNV_GUID_TEST_MODE: the tables are locked in test mode + * @UEFI_CNV_GUID_UNKNOWN: the lock status could not be determined. + * This value is a driver internal, not a possible BIOS reported one */ enum iwl_uefi_cnv_guid_status { UEFI_CNV_GUID_UNLOCKED = 0, UEFI_CNV_GUID_LOCKED = 1, UEFI_CNV_GUID_TEST_MODE = 2, + UEFI_CNV_GUID_UNKNOWN = 3, }; struct iwl_tas_selection_data { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 12a1cf6b16f1..d0709ab2f593 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -110,7 +110,7 @@ struct iwl_txf_iter_data { * @ap_type_cmd_valid: if &ap_type_cmd is valid * @uefi_tables_lock_status: the status of the WIFI GUID UEFI variables lock, * see &enum iwl_uefi_cnv_guid_status. - * Only read the UEFI variables if locked. + * Trust the UEFI variables if locked or in test mode. * @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables * @geo_profiles: geographic profiles as read from WGDS BIOS table * @geo_bios_source: see &enum bios_source diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 7df0e5ea4a1d..871d8f5db9c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -31,9 +31,10 @@ struct iwl_uefi_pnvm_mem_desc { } __packed; static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid, - unsigned long *data_size) + u32 *attributes, unsigned long *data_size) { efi_status_t status; + u32 attr = 0; void *data; if (!data_size) @@ -52,15 +53,28 @@ static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid, if (!data) return ERR_PTR(-ENOMEM); - status = efi.get_variable(name, guid, NULL, data_size, data); + status = efi.get_variable(name, guid, &attr, data_size, data); if (status != EFI_SUCCESS) { kfree(data); return ERR_PTR(-ENOENT); } + if (attributes) + *attributes = attr; + return data; } +static efi_status_t iwl_uefi_set_variable(efi_char16_t *name, efi_guid_t *guid, + u32 attributes, void *data, + unsigned long data_size) +{ + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) + return EFI_UNSUPPORTED; + + return efi.set_variable(name, guid, attributes, data_size, data); +} + void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { unsigned long package_size; @@ -69,7 +83,7 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) *len = 0; data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_WIFI_GUID, - &package_size); + NULL, &package_size); if (IS_ERR(data)) { IWL_DEBUG_FW(trans, "PNVM UEFI variable not found 0x%lx (len %lu)\n", @@ -84,17 +98,18 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) } 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) +iwl_uefi_get_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, + u32 *attributes) { void *var; unsigned long var_size; - var = iwl_uefi_get_variable(uefi_var_name, guid, &var_size); + var = iwl_uefi_get_variable(uefi_var_name, guid, attributes, &var_size); if (IS_ERR(var)) { IWL_DEBUG_RADIO(trans, @@ -120,13 +135,72 @@ iwl_uefi_get_verified_variable_guid(struct iwl_trans *trans, } static void * -iwl_uefi_get_verified_variable(struct iwl_trans *trans, +iwl_uefi_get_verified_variable_guid(struct iwl_fw_runtime *fwrt, + efi_guid_t *guid, + efi_char16_t *uefi_var_name, + char *var_name, + unsigned int expected_size, + unsigned long *size) +{ + unsigned long var_size; + u32 attributes = 0; + void *var; + + if (fwrt->uefi_tables_lock_status == UEFI_CNV_GUID_UNLOCKED) + return ERR_PTR(-EINVAL); + + /* In x86 either GLUI or GLAI should exist */ + if ((IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_X86_64) || + IS_ENABLED(CONFIG_X86_32)) && + fwrt->uefi_tables_lock_status == UEFI_CNV_GUID_UNKNOWN) + return ERR_PTR(-EINVAL); + + var = iwl_uefi_get_variable_guid(fwrt->trans, + guid, + uefi_var_name, var_name, + expected_size, &var_size, + &attributes); + if (IS_ERR(var)) + return var; + + if (fwrt->uefi_tables_lock_status != UEFI_CNV_GUID_UNKNOWN) + goto out; + + /* + * The lock status is unknown, so probe it: rewrite the variable + * with the data just read and trust it only if the firmware + * refuses the write. Treat a zero attributes value as untrusted, + * since a SetVariable() with no attributes would delete it. + */ + if (attributes) { + efi_status_t status; + + status = iwl_uefi_set_variable(uefi_var_name, guid, attributes, + var, var_size); + if (status == EFI_WRITE_PROTECTED || + status == EFI_SECURITY_VIOLATION || + status == EFI_ACCESS_DENIED) + goto out; + } + + /* unprobeable (e.g. EFI_UNSUPPORTED) or writable: not trusted */ + kfree(var); + return ERR_PTR(-EINVAL); + +out: + if (size) + *size = var_size; + return var; +} + +static void * +iwl_uefi_get_verified_wifi_var(struct iwl_fw_runtime *fwrt, efi_char16_t *uefi_var_name, char *var_name, unsigned int expected_size, unsigned long *size) { - return iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_GUID, + return iwl_uefi_get_verified_variable_guid(fwrt, &IWL_EFI_WIFI_GUID, uefi_var_name, var_name, expected_size, size); } @@ -292,11 +366,12 @@ u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) unsigned long package_size; u8 *data; - package = iwl_uefi_get_verified_variable(trans, - IWL_UEFI_REDUCED_POWER_NAME, - "Reduced Power", - sizeof(*package), - &package_size); + package = + iwl_uefi_get_variable_guid(trans, &IWL_EFI_WIFI_GUID, + IWL_UEFI_REDUCED_POWER_NAME, + "Reduced Power", + sizeof(*package), + &package_size, NULL); if (IS_ERR(package)) return ERR_CAST(package); @@ -338,9 +413,9 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans) if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; - data = iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_BT_GUID, - IWL_UEFI_STEP_NAME, - "STEP", sizeof(*data), NULL); + data = iwl_uefi_get_variable_guid(trans, &IWL_EFI_WIFI_BT_GUID, + IWL_UEFI_STEP_NAME, "STEP", + sizeof(*data), NULL, NULL); if (IS_ERR(data)) return; @@ -393,8 +468,9 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, if (!fwrt->geo_enabled) return; - data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME, - "SGOM", sizeof(*data), NULL); + data = iwl_uefi_get_variable_guid(trans, &IWL_EFI_WIFI_GUID, + IWL_UEFI_SGOM_NAME, "SGOM", + sizeof(*data), NULL, NULL); if (IS_ERR(data)) return; @@ -427,8 +503,9 @@ void iwl_uefi_get_uats_table(struct iwl_trans *trans, struct uefi_cnv_wlan_uats_data *data; int ret; - data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME, - "UATS", sizeof(*data), NULL); + data = iwl_uefi_get_variable_guid(trans, &IWL_EFI_WIFI_GUID, + IWL_UEFI_UATS_NAME, "UATS", + sizeof(*data), NULL, NULL); if (IS_ERR(data)) return; @@ -444,8 +521,9 @@ void iwl_uefi_get_uneb_table(struct iwl_trans *trans, { struct uefi_cnv_wlan_uneb_data *data; - data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UNEB_NAME, - "UNEB", sizeof(*data), NULL); + data = iwl_uefi_get_variable_guid(trans, &IWL_EFI_WIFI_GUID, + IWL_UEFI_UNEB_NAME, "UNEB", + sizeof(*data), NULL, NULL); if (IS_ERR(data)) return; @@ -506,8 +584,8 @@ int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) int num_subbands; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME, - "WRDS", + data = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_WRDS_NAME, "WRDS", UEFI_SAR_WRDS_TABLE_SIZE_REV2, &size); @@ -555,8 +633,8 @@ int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) int num_subbands; int profile_size; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME, - "EWRD", + data = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_EWRD_NAME, "EWRD", UEFI_SAR_EWRD_TABLE_SIZE_REV2, &size); if (IS_ERR(data)) @@ -609,9 +687,9 @@ int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) int n_subbands; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME, - "WGDS", UEFI_WGDS_TABLE_SIZE_REV3, - &size); + data = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_WGDS_NAME, "WGDS", + UEFI_WGDS_TABLE_SIZE_REV3, &size); if (IS_ERR(data)) return -EINVAL; @@ -684,9 +762,9 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) u32 valid_rev; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME, - "PPAG", UEFI_PPAG_DATA_SIZE_V5, - NULL); + data = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_PPAG_NAME, "PPAG", + UEFI_PPAG_DATA_SIZE_V5, NULL); if (!IS_ERR(data)) { n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV5; valid_rev = BIT(5); @@ -694,7 +772,7 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto parse_table; } - data = iwl_uefi_get_verified_variable(fwrt->trans, + data = iwl_uefi_get_verified_wifi_var(fwrt, IWL_UEFI_PPAG_NAME, "PPAG", UEFI_PPAG_DATA_SIZE_V4, @@ -752,8 +830,9 @@ int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, struct uefi_cnv_var_wtas *uefi_tas; int ret, enabled; - uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME, - "WTAS", sizeof(*uefi_tas), NULL); + uefi_tas = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_WTAS_NAME, "WTAS", + sizeof(*uefi_tas), NULL); if (IS_ERR(uefi_tas)) return -EINVAL; @@ -805,7 +884,7 @@ int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, struct uefi_cnv_var_splc *data; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME, + data = iwl_uefi_get_verified_wifi_var(fwrt, IWL_UEFI_SPLC_NAME, "SPLC", sizeof(*data), NULL); if (IS_ERR(data)) return -EINVAL; @@ -827,8 +906,9 @@ int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) struct uefi_cnv_var_wrdd *data; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME, - "WRDD", sizeof(*data), NULL); + data = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_WRDD_NAME, "WRDD", + sizeof(*data), NULL); if (IS_ERR(data)) return -EINVAL; @@ -858,7 +938,7 @@ int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) struct uefi_cnv_var_eckv *data; int ret = 0; - data = iwl_uefi_get_verified_variable_guid(fwrt->trans, + data = iwl_uefi_get_verified_variable_guid(fwrt, &IWL_EFI_WIFI_BT_GUID, IWL_UEFI_ECKV_NAME, "ECKV", sizeof(*data), NULL); @@ -882,8 +962,9 @@ int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value) struct uefi_cnv_wlan_wbem_data *data; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WBEM_NAME, - "WBEM", sizeof(*data), NULL); + data = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_WBEM_NAME, "WBEM", + sizeof(*data), NULL); if (IS_ERR(data)) return -EINVAL; @@ -909,7 +990,7 @@ static int iwl_uefi_load_dsm_values(struct iwl_fw_runtime *fwrt) BUILD_BUG_ON(ARRAY_SIZE(data->functions) < ARRAY_SIZE(fwrt->dsm_values)); - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME, + data = iwl_uefi_get_verified_wifi_var(fwrt, IWL_UEFI_DSM_NAME, "DSM", sizeof(*data), NULL); if (IS_ERR(data)) return -EINVAL; @@ -990,10 +1071,10 @@ int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt) struct uefi_cnv_var_puncturing_data *data; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, - IWL_UEFI_PUNCTURING_NAME, - "UefiCnvWlanPuncturing", - sizeof(*data), NULL); + data = iwl_uefi_get_variable_guid(fwrt->trans, &IWL_EFI_WIFI_GUID, + IWL_UEFI_PUNCTURING_NAME, + "UefiCnvWlanPuncturing", + sizeof(*data), NULL, NULL); if (IS_ERR(data)) return -EINVAL; @@ -1019,7 +1100,7 @@ int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value) struct uefi_cnv_wlan_dsbr_data *data; int ret = 0; - data = iwl_uefi_get_verified_variable_guid(fwrt->trans, + data = iwl_uefi_get_verified_variable_guid(fwrt, &IWL_EFI_WIFI_BT_GUID, IWL_UEFI_DSBR_NAME, "DSBR", sizeof(*data), NULL); @@ -1045,8 +1126,9 @@ int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt) struct uefi_cnv_wpfc_data *data __free(kfree); struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WPFC_NAME, - "WPFC", sizeof(*data), NULL); + data = iwl_uefi_get_verified_wifi_var(fwrt, + IWL_UEFI_WPFC_NAME, "WPFC", + sizeof(*data), NULL); if (IS_ERR(data)) return -EINVAL; @@ -1071,10 +1153,10 @@ int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt) void iwl_uefi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) { struct uefi_cnv_var_glui *data __free(kfree) = - iwl_uefi_get_verified_variable_guid(fwrt->trans, - &IWL_EFI_GLUI_GUID, - IWL_UEFI_GLUI_NAME, "GLUI", - sizeof(*data), NULL); + iwl_uefi_get_variable_guid(fwrt->trans, + &IWL_EFI_GLUI_GUID, + IWL_UEFI_GLUI_NAME, "GLUI", + sizeof(*data), NULL, NULL); if (IS_ERR(data)) return; @@ -1085,7 +1167,7 @@ void iwl_uefi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) return; } - /* UEFI_CNV_GUID_TEST_MODE is the max value */ + /* UEFI_CNV_GUID_TEST_MODE is the max value the BIOS can report */ if (data->guid_lock_status > UEFI_CNV_GUID_TEST_MODE) { IWL_DEBUG_RADIO(fwrt, "Invalid UEFI GUID lock status:%d\n", data->guid_lock_status); -- cgit v1.2.3