--- a/drivers/net/wireless/ath/ath9k/ath9k.h 2016-05-19 02:08:36.000000000 +0200 +++ b/drivers/net/wireless/ath/ath9k/ath9k.h 2016-11-09 16:58:33.978547942 +0100 @@ -1040,6 +1040,8 @@ struct ath_softc { u32 wow_intr_before_sleep; bool force_wow; #endif + + struct work_struct plumb_work; }; /********/ @@ -1084,6 +1086,8 @@ void ath9k_rfkill_poll_state(struct ieee void ath9k_ps_wakeup(struct ath_softc *sc); void ath9k_ps_restore(struct ath_softc *sc); +void ath9k_plumb_worker(struct work_struct *work); + #ifdef CONFIG_ATH9K_PCI int ath_pci_init(void); void ath_pci_exit(void); --- a/drivers/net/wireless/ath/ath9k/init.c 2016-05-19 02:08:36.000000000 +0200 +++ b/drivers/net/wireless/ath/ath9k/init.c 2016-11-09 16:58:09.609913907 +0100 @@ -625,6 +625,8 @@ static int ath9k_init_softc(u16 devid, s INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); + INIT_WORK(&sc->plumb_work, ath9k_plumb_worker); + ath9k_init_channel_context(sc); /* --- a/drivers/net/wireless/ath/ath9k/main.c 2016-05-19 02:08:36.000000000 +0200 +++ b/drivers/net/wireless/ath/ath9k/main.c 2016-11-09 17:47:38.259832467 +0100 @@ -16,6 +16,8 @@ #include #include +#include +#include #include "ath9k.h" #include "btcoex.h" @@ -194,6 +196,7 @@ void ath_cancel_work(struct ath_softc *s { __ath_cancel_work(sc); cancel_work_sync(&sc->hw_reset_work); + cancel_work_sync(&sc->plumb_work); } void ath_restart_work(struct ath_softc *sc) @@ -1650,6 +1653,40 @@ static int ath9k_conf_tx(struct ieee8021 return ret; } +static void ath9k_plumb_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf, void *data) +{ + struct ath_common *common = (struct ath_common *)data; + int ret; + + /* delete and re-install keys which were programmed into the hardware */ + if (vif && test_bit(keyconf->hw_key_idx, common->keymap)) { + ath_key_delete(common, keyconf); + keyconf->hw_key_idx = 0; + ret = ath_key_config(common, vif, sta, keyconf); + if (ret >= 0) + keyconf->hw_key_idx = ret; + } +} + +void ath9k_plumb_worker(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, plumb_work); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath9k_hw_kill_interrupts(sc->sc_ah); + set_bit(ATH_OP_HW_RESET, &common->op_flags); + ath9k_ps_wakeup(sc); + ath_reset_internal(sc, NULL); + rtnl_lock(); + ieee80211_iter_keys(sc->hw, NULL, ath9k_plumb_key, common); + rtnl_unlock(); + ath9k_ps_restore(sc); +} +EXPORT_SYMBOL(ath9k_plumb_worker); + static int ath9k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, @@ -1732,6 +1790,7 @@ static int ath9k_set_key(struct ieee8021 ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); + ieee80211_queue_work(hw, &sc->plumb_work); return ret; } --- a/drivers/net/wireless/ath/key.c 2016-05-19 02:08:36.000000000 +0200 +++ b/drivers/net/wireless/ath/key.c 2016-11-09 10:23:27.475611162 +0100 @@ -126,14 +126,9 @@ static bool ath_hw_keysetmac(struct ath_ return true; } -static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry, - const struct ath_keyval *k, - const u8 *mac) +static bool ath_hw_validate_keycache_param(struct ath_common *common, u16 entry, + const struct ath_keyval *k, u32* keyType) { - void *ah = common->ah; - u32 key0, key1, key2, key3, key4; - u32 keyType; - if (entry >= common->keymax) { ath_err(common, "set-entry: keycache entry %u out of range\n", entry); @@ -142,7 +137,7 @@ static bool ath_hw_set_keycache_entry(st switch (k->kv_type) { case ATH_CIPHER_AES_OCB: - keyType = AR_KEYTABLE_TYPE_AES; + *keyType = AR_KEYTABLE_TYPE_AES; break; case ATH_CIPHER_AES_CCM: if (!(common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)) { @@ -150,10 +145,10 @@ static bool ath_hw_set_keycache_entry(st "AES-CCM not supported by this mac rev\n"); return false; } - keyType = AR_KEYTABLE_TYPE_CCM; + *keyType = AR_KEYTABLE_TYPE_CCM; break; case ATH_CIPHER_TKIP: - keyType = AR_KEYTABLE_TYPE_TKIP; + *keyType = AR_KEYTABLE_TYPE_TKIP; if (entry + 64 >= common->keymax) { ath_dbg(common, ANY, "entry %u inappropriate for TKIP\n", entry); @@ -167,25 +162,40 @@ static bool ath_hw_set_keycache_entry(st return false; } if (k->kv_len <= WLAN_KEY_LEN_WEP40) - keyType = AR_KEYTABLE_TYPE_40; + *keyType = AR_KEYTABLE_TYPE_40; else if (k->kv_len <= WLAN_KEY_LEN_WEP104) - keyType = AR_KEYTABLE_TYPE_104; + *keyType = AR_KEYTABLE_TYPE_104; else - keyType = AR_KEYTABLE_TYPE_128; + *keyType = AR_KEYTABLE_TYPE_128; break; case ATH_CIPHER_CLR: - keyType = AR_KEYTABLE_TYPE_CLR; + *keyType = AR_KEYTABLE_TYPE_CLR; break; default: ath_err(common, "cipher %u not supported\n", k->kv_type); return false; } - key0 = get_unaligned_le32(k->kv_val + 0); - key1 = get_unaligned_le16(k->kv_val + 4); - key2 = get_unaligned_le32(k->kv_val + 6); - key3 = get_unaligned_le16(k->kv_val + 10); - key4 = get_unaligned_le32(k->kv_val + 12); + return true; +} + +static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry, + const struct ath_keyval *k, + const u8 *mac, bool xorKey) +{ + void *ah = common->ah; + u32 xor_mask = xorKey ? 0xaaaaaaaa : 0; + u32 key0, key1, key2, key3, key4; + u32 keyType; + + if (!ath_hw_validate_keycache_param(common, entry, k, &keyType)) + return false; + + key0 = get_unaligned_le32(k->kv_val + 0) ^ xor_mask; + key1 = (get_unaligned_le16(k->kv_val + 4) ^ xor_mask) & 0xffff; + key2 = get_unaligned_le32(k->kv_val + 6) ^ xor_mask; + key3 = (get_unaligned_le16(k->kv_val + 10) ^ xor_mask) & 0xffff; + key4 = get_unaligned_le32(k->kv_val + 12) ^ xor_mask; if (k->kv_len <= WLAN_KEY_LEN_WEP104) key4 &= 0xff; @@ -336,6 +346,124 @@ static bool ath_hw_set_keycache_entry(st return true; } +/* + * Check the contents of the specified key cache entry + * and any associated MIC entry. + */ +static bool ath_hw_check_keycache_entry(struct ath_common *common, u16 entry, + const struct ath_keyval *k, + const u8 *mac, bool xorKey) +{ + void *ah = common->ah; + u32 xor_mask = xorKey ? 0xaaaaaaaa : 0; + u32 key0, key1, key2, key3, key4; + u32 keyType; + + if (!ath_hw_validate_keycache_param(common, entry, k, &keyType)) + return false; + + key0 = get_unaligned_le32(k->kv_val + 0) ^ xor_mask; + key1 = (get_unaligned_le16(k->kv_val + 4) ^ xor_mask) & 0xffff; + key2 = get_unaligned_le32(k->kv_val + 6) ^ xor_mask; + key3 = (get_unaligned_le16(k->kv_val + 10) ^ xor_mask) & 0xffff; + key4 = get_unaligned_le32(k->kv_val + 12) ^ xor_mask; + if (k->kv_len <= WLAN_KEY_LEN_WEP104) + key4 &= 0xff; + + /* + * Note: Key cache registers access special memory area that requires + * two 32-bit writes to actually update the values in the internal + * memory. Consequently, the exact order and pairs used here must be + * maintained. + */ + + if (keyType == AR_KEYTABLE_TYPE_TKIP) { + u16 micentry = entry + 64; + /* + * Invalidate the encrypt/decrypt key until the MIC + * key is installed so pending rx frames will fail + * with decrypt errors rather than a MIC error. + */ + if ((REG_READ(ah, AR_KEYTABLE_KEY0(entry)) == key0) && + (REG_READ(ah, AR_KEYTABLE_KEY1(entry)) == key1) && + (REG_READ(ah, AR_KEYTABLE_KEY2(entry)) == key2) && + (REG_READ(ah, AR_KEYTABLE_KEY3(entry)) == key3) && + (REG_READ(ah, AR_KEYTABLE_KEY4(entry)) == key4) && + ((REG_READ(ah, AR_KEYTABLE_TYPE(entry)) & AR_KEY_TYPE) == (keyType & AR_KEY_TYPE))) { + + if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { + /* + * TKIP uses two key cache entries: + * Michael MIC TX/RX keys in the same key cache entry + * (idx = main index + 64): + * key0 [31:0] = RX key [31:0] + * key1 [15:0] = TX key [31:16] + * key1 [31:16] = reserved + * key2 [31:0] = RX key [63:32] + * key3 [15:0] = TX key [15:0] + * key3 [31:16] = reserved + * key4 [31:0] = TX key [63:32] + */ + u32 mic0, mic1, mic2, mic3, mic4; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff; + mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff; + mic4 = get_unaligned_le32(k->kv_txmic + 4); + + if ((REG_READ(ah, AR_KEYTABLE_KEY0(micentry)) == mic0) && + (REG_READ(ah, AR_KEYTABLE_KEY1(micentry)) == mic1) && + (REG_READ(ah, AR_KEYTABLE_KEY2(micentry)) == mic2) && + (REG_READ(ah, AR_KEYTABLE_KEY3(micentry)) == mic3) && + (REG_READ(ah, AR_KEYTABLE_KEY4(micentry)) == mic4) && + ((REG_READ(ah, AR_KEYTABLE_TYPE(micentry)) & AR_KEY_TYPE) == (AR_KEYTABLE_TYPE_CLR & AR_KEY_TYPE))) + return true; + } else + return true; + } + } else { + if ((REG_READ(ah, AR_KEYTABLE_KEY0(entry)) == key0) && + (REG_READ(ah, AR_KEYTABLE_KEY1(entry)) == key1) && + (REG_READ(ah, AR_KEYTABLE_KEY2(entry)) == key2) && + (REG_READ(ah, AR_KEYTABLE_KEY3(entry)) == key3) && + (REG_READ(ah, AR_KEYTABLE_KEY4(entry)) == key4) && + ((REG_READ(ah, AR_KEYTABLE_TYPE(entry)) & AR_KEY_TYPE) == (keyType & AR_KEY_TYPE))) + return true; + } + return false; +} + +static bool ath_hw_plumb_keycache_entry(struct ath_common *common, u16 entry, + const struct ath_keyval *k, + const u8 *mac) +{ + bool ret; + + /* + * Write the inverted key to the cache, then the real key. Some + * ath9k chips seem to have a known (and old) bug which corrupts + * the key cache 'every now and then'. We observed that the + * corruption happens at least after having uploaded a new (GTK) + * key while the chip is processing large amounts of traffic. + */ + while ((ret = ath_hw_set_keycache_entry(common, entry, k, mac, true))) + { + if (ath_hw_check_keycache_entry(common, entry, k, mac, true)) + break; + } + + if (ret) { + while ((ret = ath_hw_set_keycache_entry(common, entry, k, mac, false))) + { + if (ath_hw_check_keycache_entry(common, entry, k, mac, false)) + break; + } + } + + return ret; +} + static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, struct ath_keyval *hk, const u8 *addr, bool authenticator) @@ -359,20 +489,20 @@ static int ath_setkey_tkip(struct ath_co memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); } - return ath_hw_set_keycache_entry(common, keyix, hk, addr); + return ath_hw_plumb_keycache_entry(common, keyix, hk, addr); } if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { /* TX and RX keys share the same key cache entry. */ memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); - return ath_hw_set_keycache_entry(common, keyix, hk, addr); + return ath_hw_plumb_keycache_entry(common, keyix, hk, addr); } /* Separate key cache entries for TX and RX */ /* TX key goes at first index, RX key at +32. */ memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); - if (!ath_hw_set_keycache_entry(common, keyix, hk, NULL)) { + if (!ath_hw_plumb_keycache_entry(common, keyix, hk, NULL)) { /* TX MIC entry failed. No need to proceed further */ ath_err(common, "Setting TX MIC Key Failed\n"); return 0; @@ -380,7 +510,7 @@ static int ath_setkey_tkip(struct ath_co memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); /* XXX delete tx key on failure? */ - return ath_hw_set_keycache_entry(common, keyix + 32, hk, addr); + return ath_hw_plumb_keycache_entry(common, keyix + 32, hk, addr); } static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) @@ -553,7 +680,7 @@ int ath_key_config(struct ath_common *co ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, vif->type == NL80211_IFTYPE_AP); else - ret = ath_hw_set_keycache_entry(common, idx, &hk, mac); + ret = ath_hw_plumb_keycache_entry(common, idx, &hk, mac); if (!ret) return -EIO;