summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/init.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/runtime.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.c198
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);