diff options
| author | Miri Korenblit <miriam.rachel.korenblit@intel.com> | 2026-09-23 16:04:50 +0300 |
|---|---|---|
| committer | Miri Korenblit <miriam.rachel.korenblit@intel.com> | 2026-09-30 11:18:31 +0300 |
| commit | 87ba1ab0bf4aa7bc6cc30b0c77960cb8032645fc (patch) | |
| tree | f6f9a423ca55b9971bd942b74baf07459f903f76 /drivers/net/wireless/intel | |
| parent | a97c65a64fe7b8979754ddc415566172f298b8e0 (diff) | |
| download | linux-next-87ba1ab0bf4aa7bc6cc30b0c77960cb8032645fc.tar.gz linux-next-87ba1ab0bf4aa7bc6cc30b0c77960cb8032645fc.zip | |
wifi: iwlwifi: support net detect match info version 3
Since API 77, firmware reports scan offload profile matches using
SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_3, which has more channels (16
byte instead of 7).
Somehow we missed this change, and the driver only handles the 5- and
7-byte layouts, so on such firmware it misparsed the match (reading band
as energy) and could not recover matches beyond the first 56 channels.
Add the version 3 match structure and select it based on the
SCAN_OFFLOAD_PROFILES_QUERY_CMD notification version (in MVM, in MLD,
this is the only version supported).
In MVM, while walking the matched-channels bitmap, bound the channel
index with IWL_FW_CHECK so firmware that sets a bit beyond n_nd_channels
can no longer trigger an out-of-bounds read of nd_channels[].
Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Link: https://patch.msgid.link/20260923160318.07738ca44b54.I58162891250af9f46220fd01db667460e6dd621b@changeid
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/fw/api/scan.h | 28 | ||||
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 89 |
2 files changed, 93 insertions, 24 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index cb6217763bd1..8994a7b840ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -1234,7 +1234,8 @@ struct iwl_umac_scan_complete { } __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ #define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V1 5 -#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 7 +#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V2 7 +#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 16 /** * struct iwl_scan_offload_profile_match_v1 - match information @@ -1281,10 +1282,30 @@ struct iwl_scan_offload_profiles_query_v1 { } __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ /** + * struct iwl_scan_offload_profile_match_v2 - match information + * @bssid: matched bssid + * @reserved: reserved + * @channel: channel where the match occurred + * @energy: energy + * @matching_feature: feature matches + * @matching_channels: bitmap of channels that matched, referencing + * the channels passed in the scan offload request. + */ +struct iwl_scan_offload_profile_match_v2 { + u8 bssid[ETH_ALEN]; + __le16 reserved; + u8 channel; + u8 energy; + u8 matching_feature; + u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V2]; +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_2 */ + +/** * struct iwl_scan_offload_profile_match - match information * @bssid: matched bssid * @reserved: reserved * @channel: channel where the match occurred + * @band: band where the match occurred * @energy: energy * @matching_feature: feature matches * @matching_channels: bitmap of channels that matched, referencing @@ -1294,10 +1315,11 @@ struct iwl_scan_offload_profile_match { u8 bssid[ETH_ALEN]; __le16 reserved; u8 channel; + u8 band; u8 energy; u8 matching_feature; u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; -} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_2 */ +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_3 */ /** * struct iwl_scan_offload_match_info - match results information @@ -1322,7 +1344,7 @@ struct iwl_scan_offload_match_info { u8 self_recovery; __le16 reserved; struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES_V2]; -} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_3 and +} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_5 and * SCAN_OFFLOAD_MATCH_INFO_NOTIFICATION_S_VER_1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index bd52f2ebd3ba..2abc4bc3eb34 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2440,6 +2440,12 @@ struct iwl_mvm_nd_results { u8 matches[ND_QUERY_BUF_LEN]; }; +static bool iwl_mvm_nd_match_info_v3(struct iwl_mvm *mvm) +{ + return iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + SCAN_OFFLOAD_PROFILES_QUERY_CMD, 0) >= 4; +} + static int iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, struct iwl_mvm_nd_results *results) @@ -2459,12 +2465,17 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, return ret; } - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + if (iwl_mvm_nd_match_info_v3(mvm)) { matches_len = sizeof(struct iwl_scan_offload_profile_match) * max_profiles; query_len = offsetof(struct iwl_scan_offload_match_info, matches) + matches_len; + } else if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + matches_len = sizeof(struct iwl_scan_offload_profile_match_v2) * + max_profiles; + query_len = offsetof(struct iwl_scan_offload_match_info, + matches) + matches_len; } else { matches_len = sizeof(struct iwl_scan_offload_profile_match_v1) * max_profiles; @@ -2499,13 +2510,19 @@ static int iwl_mvm_query_num_match_chans(struct iwl_mvm *mvm, { int n_chans = 0, i; - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + if (iwl_mvm_nd_match_info_v3(mvm)) { struct iwl_scan_offload_profile_match *matches = (void *)results->matches; for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; i++) n_chans += hweight8(matches[idx].matching_channels[i]); + } else if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + struct iwl_scan_offload_profile_match_v2 *matches = + (void *)results->matches; + + for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V2; i++) + n_chans += hweight8(matches[idx].matching_channels[i]); } else { struct iwl_scan_offload_profile_match_v1 *matches = (void *)results->matches; @@ -2517,34 +2534,58 @@ static int iwl_mvm_query_num_match_chans(struct iwl_mvm *mvm, return n_chans; } +static void iwl_mvm_set_matching_freqs(struct iwl_mvm *mvm, + const u8 *matching_channels, + size_t num_bytes, + struct cfg80211_wowlan_nd_match *match) +{ + int n_channels = 0; + + for (int i = 0; i < num_bytes * 8; i++) { + if (!(matching_channels[i / 8] & BIT(i % 8))) + continue; + if (IWL_FW_CHECK(mvm, i >= mvm->n_nd_channels, + "FW matched channel bit %d beyond n_nd_channels %d\n", + i, mvm->n_nd_channels)) + break; + match->channels[n_channels++] = + mvm->nd_channels[i]->center_freq; + } + /* We may have ended up with fewer channels than we allocated. */ + match->n_channels = n_channels; +} + static void iwl_mvm_query_set_freqs(struct iwl_mvm *mvm, struct iwl_mvm_nd_results *results, struct cfg80211_wowlan_nd_match *match, int idx) { - int i; - int n_channels = 0; - - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + if (iwl_mvm_nd_match_info_v3(mvm)) { struct iwl_scan_offload_profile_match *matches = (void *)results->matches; - for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; i++) - if (matches[idx].matching_channels[i / 8] & (BIT(i % 8))) - match->channels[n_channels++] = - mvm->nd_channels[i]->center_freq; + iwl_mvm_set_matching_freqs(mvm, + matches[idx].matching_channels, + sizeof(matches[idx].matching_channels), + match); + } else if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + struct iwl_scan_offload_profile_match_v2 *matches = + (void *)results->matches; + + iwl_mvm_set_matching_freqs(mvm, + matches[idx].matching_channels, + sizeof(matches[idx].matching_channels), + match); } else { struct iwl_scan_offload_profile_match_v1 *matches = (void *)results->matches; - for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V1 * 8; i++) - if (matches[idx].matching_channels[i / 8] & (BIT(i % 8))) - match->channels[n_channels++] = - mvm->nd_channels[i]->center_freq; + iwl_mvm_set_matching_freqs(mvm, + matches[idx].matching_channels, + sizeof(matches[idx].matching_channels), + match); } - /* We may have ended up with fewer channels than we allocated. */ - match->n_channels = n_channels; } /** @@ -2817,8 +2858,14 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm, struct iwl_wowlan_status_data *status = d3_data->status; struct ieee80211_vif *vif = iwl_mvm_get_bss_vif(mvm); struct iwl_mvm_nd_results *results = d3_data->nd_results; - size_t i, matches_len = sizeof(struct iwl_scan_offload_profile_match) * - iwl_umac_scan_get_max_profiles(mvm->fw); + size_t i, matches_len; + + if (iwl_mvm_nd_match_info_v3(mvm)) + matches_len = sizeof(struct iwl_scan_offload_profile_match) * + iwl_umac_scan_get_max_profiles(mvm->fw); + else + matches_len = sizeof(struct iwl_scan_offload_profile_match_v2) * + iwl_umac_scan_get_max_profiles(mvm->fw); if (IS_ERR_OR_NULL(vif)) return; |
