From ea3571f48953df2cf77a9c4200a7363236736673 Mon Sep 17 00:00:00 2001 From: Alon Giladi Date: Tue, 6 Jun 2023 10:43:05 +0300 Subject: wifi: iwlwifi: Use iwl_pnvm_image in reduce power tables flow Generalize the parsing, loading, and setting of the power-reduce tables, in order to support allocation of several DRAM payloads in the future. Signed-off-by: Alon Giladi Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230606103519.564f1eead99b.Iaba653b21dc09aafc72b9bbb3928abddce0db50a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 90 ++++++++++++---------------- 1 file changed, 37 insertions(+), 53 deletions(-) (limited to 'drivers/net/wireless/intel/iwlwifi/fw/uefi.c') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 01afea33c38c..64b45a5b767e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -55,14 +55,14 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) return data; } -static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans, - const u8 *data, size_t len) +static int iwl_uefi_reduce_power_section(struct iwl_trans *trans, + const u8 *data, size_t len, + struct iwl_pnvm_image *pnvm_data) { const struct iwl_ucode_tlv *tlv; - u8 *reduce_power_data = NULL, *tmp; - u32 size = 0; IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n"); + memset(pnvm_data, 0, sizeof(*pnvm_data)); while (len >= sizeof(*tlv)) { u32 tlv_len, tlv_type; @@ -76,9 +76,7 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans, if (len < tlv_len) { IWL_ERR(trans, "invalid TLV len: %zd/%u\n", len, tlv_len); - kfree(reduce_power_data); - reduce_power_data = ERR_PTR(-EINVAL); - goto out; + return -EINVAL; } data += sizeof(*tlv); @@ -89,23 +87,17 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans, "Got IWL_UCODE_TLV_MEM_DESC len %d\n", tlv_len); - IWL_DEBUG_FW(trans, "Adding data (size %d)\n", tlv_len); - - tmp = krealloc(reduce_power_data, size + tlv_len, GFP_KERNEL); - if (!tmp) { + if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) { IWL_DEBUG_FW(trans, - "Couldn't allocate (more) reduce_power_data\n"); - - kfree(reduce_power_data); - reduce_power_data = ERR_PTR(-ENOMEM); - goto out; + "too many payloads to allocate in DRAM.\n"); + return -EINVAL; } - reduce_power_data = tmp; - - memcpy(reduce_power_data + size, data, tlv_len); + IWL_DEBUG_FW(trans, "Adding data (size %d)\n", tlv_len); - size += tlv_len; + pnvm_data->chunks[pnvm_data->n_chunks].data = data; + pnvm_data->chunks[pnvm_data->n_chunks].len = tlv_len; + pnvm_data->n_chunks++; break; } @@ -124,27 +116,18 @@ static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans, } done: - if (!size) { + if (!pnvm_data->n_chunks) { IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n"); - /* Better safe than sorry, but 'reduce_power_data' should - * always be NULL if !size. - */ - kfree(reduce_power_data); - reduce_power_data = ERR_PTR(-ENOENT); - goto out; + return -ENOENT; } - - IWL_INFO(trans, "loaded REDUCE_POWER\n"); - -out: - return reduce_power_data; + return 0; } -static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans, - const u8 *data, size_t len) +static int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, + const u8 *data, size_t len, + struct iwl_pnvm_image *pnvm_data) { const struct iwl_ucode_tlv *tlv; - void *sec_data; IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n"); @@ -160,7 +143,7 @@ static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans, if (len < tlv_len) { IWL_ERR(trans, "invalid TLV len: %zd/%u\n", len, tlv_len); - return ERR_PTR(-EINVAL); + return -EINVAL; } if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) { @@ -181,11 +164,11 @@ static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans, if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) && trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) && trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) { - sec_data = iwl_uefi_reduce_power_section(trans, - data, - len); - if (!IS_ERR(sec_data)) - return sec_data; + int ret = iwl_uefi_reduce_power_section(trans, + data, len, + pnvm_data); + if (!ret) + return 0; } else { IWL_DEBUG_FW(trans, "SKU ID didn't match!\n"); } @@ -195,20 +178,20 @@ static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans, } } - return ERR_PTR(-ENOENT); + return -ENOENT; } -void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) +int iwl_uefi_get_reduced_power(struct iwl_trans *trans, + struct iwl_pnvm_image *pnvm_data) { struct pnvm_sku_package *package; - void *data = NULL; unsigned long package_size; efi_status_t status; - - *len = 0; + int ret; + size_t len = 0; if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) - return ERR_PTR(-ENODEV); + return -ENODEV; /* * TODO: we hardcode a maximum length here, because reading @@ -219,7 +202,7 @@ void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) package = kmalloc(package_size, GFP_KERNEL); if (!package) - return ERR_PTR(-ENOMEM); + return -ENOMEM; status = efi.get_variable(IWL_UEFI_REDUCED_POWER_NAME, &IWL_EFI_VAR_GUID, NULL, &package_size, package); @@ -228,22 +211,23 @@ void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) "Reduced Power UEFI variable not found 0x%lx (len %lu)\n", status, package_size); kfree(package); - return ERR_PTR(-ENOENT); + return -ENOENT; } IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n", package_size); - *len = package_size; + len = package_size; IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n", package->rev, package->total_size, package->n_skus); - data = iwl_uefi_reduce_power_parse(trans, package->data, - *len - sizeof(*package)); + ret = iwl_uefi_reduce_power_parse(trans, package->data, + len - sizeof(*package), + pnvm_data); kfree(package); - return data; + return ret; } static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data, -- cgit v1.2.3 From 380bf72d1b1dc3cb2572acd9b61153e79413b036 Mon Sep 17 00:00:00 2001 From: Alon Giladi Date: Tue, 6 Jun 2023 10:43:07 +0300 Subject: wifi: iwlwifi: Separate reading and parsing of reduce power table It enables to better handle error cases. Also save the image till the end of the loading and only then free it. Signed-off-by: Alon Giladi Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230606103519.71e3b3e0e794.Ifbe69ad99a7e805eb70e09280365821eb146b1c9@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 40 ++++++++++++++++---------- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 31 +++++++++----------- drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 27 ++++++++++------- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 ++ 4 files changed, 58 insertions(+), 42 deletions(-) (limited to 'drivers/net/wireless/intel/iwlwifi/fw/uefi.c') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index b556abece896..3b5a3c89fedf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -319,29 +319,39 @@ int iwl_pnvm_load(struct iwl_trans *trans, reduce_tables: /* now try to get the reduce power table, if not loaded yet */ + if (trans->failed_to_load_reduce_power_image) + goto notification; + if (!trans->reduce_power_loaded) { memset(&pnvm_data, 0, sizeof(pnvm_data)); - ret = iwl_uefi_get_reduced_power(trans, &pnvm_data); + data = iwl_uefi_get_reduced_power(trans, &length); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + trans->failed_to_load_reduce_power_image = true; + goto notification; + } + + ret = iwl_uefi_reduce_power_parse(trans, data, length, + &pnvm_data); if (ret) { - /* - * Pretend we've loaded it - at least we've tried and - * couldn't load it at all, so there's no point in - * trying again over and over. - */ - trans->reduce_power_loaded = true; - } else { - ret = iwl_trans_load_reduce_power(trans, &pnvm_data, capa); - if (ret) { - IWL_DEBUG_FW(trans, - "Failed to load reduce power table %d\n", - ret); - trans->reduce_power_loaded = true; - } + trans->failed_to_load_reduce_power_image = true; kfree(data); + goto notification; + } + + ret = iwl_trans_load_reduce_power(trans, &pnvm_data, capa); + kfree(data); + if (ret) { + IWL_DEBUG_FW(trans, + "Failed to load reduce power table %d\n", + ret); + trans->failed_to_load_reduce_power_image = true; + goto notification; } } iwl_trans_set_reduce_power(trans, capa); +notification: iwl_init_notification_wait(notif_wait, &pnvm_wait, ntf_cmds, ARRAY_SIZE(ntf_cmds), iwl_pnvm_complete_fn, trans); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 64b45a5b767e..1666ef3a482e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2021-2022 Intel Corporation + * Copyright(c) 2021-2023 Intel Corporation */ #include "iwl-drv.h" @@ -123,9 +123,9 @@ done: return 0; } -static int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, - const u8 *data, size_t len, - struct iwl_pnvm_image *pnvm_data) +int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, + const u8 *data, size_t len, + struct iwl_pnvm_image *pnvm_data) { const struct iwl_ucode_tlv *tlv; @@ -181,17 +181,15 @@ static int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, return -ENOENT; } -int iwl_uefi_get_reduced_power(struct iwl_trans *trans, - struct iwl_pnvm_image *pnvm_data) +u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) { struct pnvm_sku_package *package; unsigned long package_size; efi_status_t status; - int ret; - size_t len = 0; + u8 *data; if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) - return -ENODEV; + return ERR_PTR(-ENODEV); /* * TODO: we hardcode a maximum length here, because reading @@ -202,7 +200,7 @@ int iwl_uefi_get_reduced_power(struct iwl_trans *trans, package = kmalloc(package_size, GFP_KERNEL); if (!package) - return -ENOMEM; + return ERR_PTR(-ENOMEM); status = efi.get_variable(IWL_UEFI_REDUCED_POWER_NAME, &IWL_EFI_VAR_GUID, NULL, &package_size, package); @@ -211,23 +209,22 @@ int iwl_uefi_get_reduced_power(struct iwl_trans *trans, "Reduced Power UEFI variable not found 0x%lx (len %lu)\n", status, package_size); kfree(package); - return -ENOENT; + return ERR_PTR(-ENOENT); } IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n", package_size); - len = package_size; IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n", package->rev, package->total_size, package->n_skus); - ret = iwl_uefi_reduce_power_parse(trans, package->data, - len - sizeof(*package), - pnvm_data); - + *len = package_size - sizeof(*package); + data = kmemdup(package->data, *len, GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); kfree(package); - return ret; + return data; } static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 03176f73151a..10bed372e67c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright(c) 2021-2022 Intel Corporation + * Copyright(c) 2021-2023 Intel Corporation */ #ifndef __iwl_fw_uefi__ #define __iwl_fw_uefi__ @@ -50,25 +50,32 @@ struct uefi_cnv_common_step_data { */ #ifdef CONFIG_EFI void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len); -int iwl_uefi_get_reduced_power(struct iwl_trans *trans, - struct iwl_pnvm_image *pnvm_data); +u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len); +int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, + const u8 *data, size_t len, + struct iwl_pnvm_image *pnvm_data); void iwl_uefi_get_step_table(struct iwl_trans *trans); #else /* CONFIG_EFI */ -static inline -void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) +static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { return ERR_PTR(-EOPNOTSUPP); } -static inline -int iwl_uefi_get_reduced_power(struct iwl_trans *trans, - struct iwl_pnvm_image *pnvm_data) +static inline int +iwl_uefi_reduce_power_parse(struct iwl_trans *trans, + const u8 *data, size_t len, + struct iwl_pnvm_image *pnvm_data) { return -EOPNOTSUPP; } -static inline -void iwl_uefi_get_step_table(struct iwl_trans *trans) +static inline u8 * +iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void iwl_uefi_get_step_table(struct iwl_trans *trans) { } #endif /* CONFIG_EFI */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index eb32683c36fb..d9e465d0f4af 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1047,6 +1047,7 @@ struct iwl_trans_txqs { * @pm_support: set to true in start_hw if link pm is supported * @ltr_enabled: set to true if the LTR is enabled * @fail_to_parse_pnvm_image: set to true if pnvm parsing failed + * @failed_to_load_reduce_power_image: set to true if pnvm loading failed * @wide_cmd_header: true when ucode supports wide command header format * @wait_command_queue: wait queue for sync commands * @num_rx_queues: number of RX queues allocated by the transport; @@ -1096,6 +1097,7 @@ struct iwl_trans { u8 pnvm_loaded:1; u8 fail_to_parse_pnvm_image:1; u8 reduce_power_loaded:1; + u8 failed_to_load_reduce_power_image:1; const struct iwl_hcmd_arr *command_groups; int command_groups_size; -- cgit v1.2.3 From 8ae3e23195188a925b9a3e05f34f114441d97e14 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Tue, 6 Jun 2023 10:43:09 +0300 Subject: wifi: iwlwifi: fw: don't use constant size with efi.get_variable Use efi.get_variable() with NULL pointer for data in order to obtain entry size and then call it again with the correct size to get the entry itself. Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230606103519.ef95a8055a50.Iae5389baaf0a9a3c89469f7502275ee119d378b6@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 143 ++++++++++++++------------- drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 13 +-- 2 files changed, 74 insertions(+), 82 deletions(-) (limited to 'drivers/net/wireless/intel/iwlwifi/fw/uefi.c') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 1666ef3a482e..488b9fb79743 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -17,38 +17,53 @@ 0xb2, 0xec, 0xf5, 0xa3, \ 0x59, 0x4f, 0x4a, 0xea) -void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) +static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid, + unsigned long *data_size) { - void *data; - unsigned long package_size; efi_status_t status; + void *data; - *len = 0; + if (!data_size) + return ERR_PTR(-EINVAL); if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) return ERR_PTR(-ENODEV); - /* - * TODO: we hardcode a maximum length here, because reading - * from the UEFI is not working. To implement this properly, - * we have to call efivar_entry_size(). - */ - package_size = IWL_HARDCODED_PNVM_SIZE; + /* first call with NULL data to get the exact entry size */ + *data_size = 0; + status = efi.get_variable(name, guid, NULL, data_size, NULL); + if (status != EFI_BUFFER_TOO_SMALL || !*data_size) + return ERR_PTR(-EIO); - data = kmalloc(package_size, GFP_KERNEL); + data = kmalloc(*data_size, GFP_KERNEL); if (!data) return ERR_PTR(-ENOMEM); - status = efi.get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID, - NULL, &package_size, data); + status = efi.get_variable(name, guid, NULL, data_size, data); if (status != EFI_SUCCESS) { - IWL_DEBUG_FW(trans, - "PNVM UEFI variable not found 0x%lx (len %lu)\n", - status, package_size); kfree(data); return ERR_PTR(-ENOENT); } + return data; +} + +void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) +{ + unsigned long package_size; + void *data; + + *len = 0; + + data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID, + &package_size); + if (IS_ERR(data)) { + IWL_DEBUG_FW(trans, + "PNVM UEFI variable not found 0x%lx (len %lu)\n", + PTR_ERR(data), package_size); + return data; + } + IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size); *len = package_size; @@ -185,31 +200,24 @@ u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) { struct pnvm_sku_package *package; unsigned long package_size; - efi_status_t status; u8 *data; - if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) - return ERR_PTR(-ENODEV); - - /* - * TODO: we hardcode a maximum length here, because reading - * from the UEFI is not working. To implement this properly, - * we have to call efivar_entry_size(). - */ - package_size = IWL_HARDCODED_REDUCE_POWER_SIZE; + package = iwl_uefi_get_variable(IWL_UEFI_REDUCED_POWER_NAME, + &IWL_EFI_VAR_GUID, &package_size); - package = kmalloc(package_size, GFP_KERNEL); - if (!package) - return ERR_PTR(-ENOMEM); - - status = efi.get_variable(IWL_UEFI_REDUCED_POWER_NAME, &IWL_EFI_VAR_GUID, - NULL, &package_size, package); - if (status != EFI_SUCCESS) { + if (IS_ERR(package)) { IWL_DEBUG_FW(trans, "Reduced Power UEFI variable not found 0x%lx (len %lu)\n", - status, package_size); + PTR_ERR(package), package_size); + return ERR_CAST(package); + } + + if (package_size < sizeof(*package)) { + IWL_DEBUG_FW(trans, + "Invalid Reduced Power UEFI variable len (%lu)\n", + package_size); kfree(package); - return ERR_PTR(-ENOENT); + return ERR_PTR(-EINVAL); } IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n", @@ -220,8 +228,11 @@ u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) *len = package_size - sizeof(*package); data = kmemdup(package->data, *len, GFP_KERNEL); - if (!data) + if (!data) { + kfree(package); return ERR_PTR(-ENOMEM); + } + kfree(package); return data; @@ -245,31 +256,27 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans) { struct uefi_cnv_common_step_data *data; unsigned long package_size; - efi_status_t status; int ret; if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; - if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) - return; + data = iwl_uefi_get_variable(IWL_UEFI_STEP_NAME, &IWL_EFI_VAR_GUID, + &package_size); - /* TODO: we hardcode a maximum length here, because reading - * from the UEFI is not working. To implement this properly, - * we have to call efivar_entry_size(). - */ - package_size = IWL_HARDCODED_STEP_SIZE; - - data = kmalloc(package_size, GFP_KERNEL); - if (!data) + if (IS_ERR(data)) { + IWL_DEBUG_FW(trans, + "STEP UEFI variable not found 0x%lx\n", + PTR_ERR(data)); return; + } - status = efi.get_variable(IWL_UEFI_STEP_NAME, &IWL_EFI_VAR_GUID, - NULL, &package_size, data); - if (status != EFI_SUCCESS) { + if (package_size < sizeof(*data)) { IWL_DEBUG_FW(trans, - "STEP UEFI variable not found 0x%lx\n", status); - goto out_free; + "Invalid STEP table UEFI variable len (%lu)\n", + package_size); + kfree(data); + return; } IWL_DEBUG_FW(trans, "Read STEP from UEFI with size %lu\n", @@ -279,7 +286,6 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans) if (ret < 0) IWL_DEBUG_FW(trans, "Cannot read STEP tables. rev is invalid\n"); -out_free: kfree(data); } IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table); @@ -322,29 +328,26 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, { struct uefi_cnv_wlan_sgom_data *data; unsigned long package_size; - efi_status_t status; int ret; - if (!fwrt->geo_enabled || - !efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) + if (!fwrt->geo_enabled) return; - /* TODO: we hardcode a maximum length here, because reading - * from the UEFI is not working. To implement this properly, - * we have to call efivar_entry_size(). - */ - package_size = IWL_HARDCODED_SGOM_SIZE; - - data = kmalloc(package_size, GFP_KERNEL); - if (!data) + data = iwl_uefi_get_variable(IWL_UEFI_SGOM_NAME, &IWL_EFI_VAR_GUID, + &package_size); + if (IS_ERR(data)) { + IWL_DEBUG_FW(trans, + "SGOM UEFI variable not found 0x%lx\n", + PTR_ERR(data)); return; + } - status = efi.get_variable(IWL_UEFI_SGOM_NAME, &IWL_EFI_VAR_GUID, - NULL, &package_size, data); - if (status != EFI_SUCCESS) { + if (package_size < sizeof(*data)) { IWL_DEBUG_FW(trans, - "SGOM UEFI variable not found 0x%lx\n", status); - goto out_free; + "Invalid SGOM table UEFI variable len (%lu)\n", + package_size); + kfree(data); + return; } IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n", @@ -354,9 +357,7 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, if (ret < 0) IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n"); -out_free: kfree(data); - } IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table); #endif /* CONFIG_ACPI */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 10bed372e67c..dc7ccf49d92d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -10,16 +10,7 @@ #define IWL_UEFI_SGOM_NAME L"UefiCnvWlanSarGeoOffsetMapping" #define IWL_UEFI_STEP_NAME L"UefiCnvCommonSTEP" -/* - * TODO: we have these hardcoded values that the caller must pass, - * because reading from the UEFI is not working. To implement this - * properly, we have to change iwl_pnvm_get_from_uefi() to call - * efivar_entry_size() and return the value to the caller instead. - */ -#define IWL_HARDCODED_PNVM_SIZE 4096 -#define IWL_HARDCODED_REDUCE_POWER_SIZE 32768 -#define IWL_HARDCODED_SGOM_SIZE 339 -#define IWL_HARDCODED_STEP_SIZE 6 +#define IWL_SGOM_MAP_SIZE 339 struct pnvm_sku_package { u8 rev; @@ -31,7 +22,7 @@ struct pnvm_sku_package { struct uefi_cnv_wlan_sgom_data { u8 revision; - u8 offset_map[IWL_HARDCODED_SGOM_SIZE - 1]; + u8 offset_map[IWL_SGOM_MAP_SIZE - 1]; } __packed; struct uefi_cnv_common_step_data { -- cgit v1.2.3 From 372a714808c8ec4f4ae4915c734d80d7f504997c Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Tue, 6 Jun 2023 10:43:10 +0300 Subject: wifi: iwlwifi: pnvm: handle memory descriptor tlv When PNVM is obtained from UEFI, there's an additional memory descriptor TLV that has to be handled. It is the same TLV that holds data in the reduced power tables. Also, in this TLV, the actual data is located after address and size, so add the corresponding offset. Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230606103519.8c5f5ee8e30b.Id1893c9dec140b5ba4abe8a121c2e1a1d121d2d7@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 5 +++ drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 62 ++++++++++++++++++++-------- drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 9 ++++ 3 files changed, 59 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/intel/iwlwifi/fw/uefi.c') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 82eb32e67a2c..650e4bde9c17 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -127,6 +127,11 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, break; } + case IWL_UCODE_TLV_MEM_DESC: + if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len, + pnvm_data)) + return -EINVAL; + break; case IWL_UCODE_TLV_PNVM_SKU: IWL_DEBUG_FW(trans, "New PNVM section started, stop parsing.\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 488b9fb79743..9877988db0d2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -17,6 +17,12 @@ 0xb2, 0xec, 0xf5, 0xa3, \ 0x59, 0x4f, 0x4a, 0xea) +struct iwl_uefi_pnvm_mem_desc { + __le32 addr; + __le32 size; + const u8 data[]; +} __packed; + static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid, unsigned long *data_size) { @@ -70,6 +76,42 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) return data; } +int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, + u32 tlv_len, struct iwl_pnvm_image *pnvm_data) +{ + const struct iwl_uefi_pnvm_mem_desc *desc = (const void *)data; + u32 data_len; + + if (tlv_len < sizeof(*desc)) { + IWL_DEBUG_FW(trans, "TLV len (%d) is too small\n", tlv_len); + return -EINVAL; + } + + data_len = tlv_len - sizeof(*desc); + + IWL_DEBUG_FW(trans, + "Handle IWL_UCODE_TLV_MEM_DESC, len %d data_len %d\n", + tlv_len, data_len); + + if (le32_to_cpu(desc->size) != data_len) { + IWL_DEBUG_FW(trans, "invalid mem desc size %d\n", desc->size); + return -EINVAL; + } + + if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) { + IWL_DEBUG_FW(trans, "too many payloads to allocate in DRAM.\n"); + return -EINVAL; + } + + IWL_DEBUG_FW(trans, "Adding data (size %d)\n", data_len); + + pnvm_data->chunks[pnvm_data->n_chunks].data = desc->data; + pnvm_data->chunks[pnvm_data->n_chunks].len = data_len; + pnvm_data->n_chunks++; + + return 0; +} + static int iwl_uefi_reduce_power_section(struct iwl_trans *trans, const u8 *data, size_t len, struct iwl_pnvm_image *pnvm_data) @@ -97,25 +139,11 @@ static int iwl_uefi_reduce_power_section(struct iwl_trans *trans, data += sizeof(*tlv); switch (tlv_type) { - case IWL_UCODE_TLV_MEM_DESC: { - IWL_DEBUG_FW(trans, - "Got IWL_UCODE_TLV_MEM_DESC len %d\n", - tlv_len); - - if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) { - IWL_DEBUG_FW(trans, - "too many payloads to allocate in DRAM.\n"); + case IWL_UCODE_TLV_MEM_DESC: + if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len, + pnvm_data)) return -EINVAL; - } - - IWL_DEBUG_FW(trans, "Adding data (size %d)\n", tlv_len); - - pnvm_data->chunks[pnvm_data->n_chunks].data = data; - pnvm_data->chunks[pnvm_data->n_chunks].len = tlv_len; - pnvm_data->n_chunks++; - break; - } case IWL_UCODE_TLV_PNVM_SKU: IWL_DEBUG_FW(trans, "New REDUCE_POWER section started, stop parsing.\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index dc7ccf49d92d..1369cc4855c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -46,6 +46,8 @@ int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, const u8 *data, size_t len, struct iwl_pnvm_image *pnvm_data); void iwl_uefi_get_step_table(struct iwl_trans *trans); +int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, + u32 tlv_len, struct iwl_pnvm_image *pnvm_data); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -69,6 +71,13 @@ iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) static inline void iwl_uefi_get_step_table(struct iwl_trans *trans) { } + +static inline int +iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, + u32 tlv_len, struct iwl_pnvm_image *pnvm_data) +{ + return 0; +} #endif /* CONFIG_EFI */ #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) -- cgit v1.2.3