/* * Copyright (c) 2009 Atheros Communications Inc. * Copyright (c) 2010 Bruno Randolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "ath.h" #include "reg.h" #include "debug.h" #define REG_READ (common->ops->read) #define REG_WRITE(_ah, _reg, _val) (common->ops->write)(_ah, _val, _reg) #define IEEE80211_WEP_NKID 4 /* number of key ids */ /************************/ /* Key Cache Management */ /************************/ bool ath_hw_keyreset(struct ath_common *common, u16 entry) { u32 keyType; void *ah = common->ah; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry)); REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); if (keyType == AR_KEYTABLE_TYPE_TKIP) { u16 micentry = entry + 64; REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); } return true; } EXPORT_SYMBOL(ath_hw_keyreset); static bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac) { u32 macHi, macLo; u32 unicast_flag = AR_KEYTABLE_VALID; void *ah = common->ah; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } if (mac != NULL) { /* * AR_KEYTABLE_VALID indicates that the address is a unicast * address, which must match the transmitter address for * decrypting frames. * Not setting this bit allows the hardware to use the key * for multicast frame decryption. */ if (mac[0] & 0x01) unicast_flag = 0; macHi = (mac[5] << 8) | mac[4]; macLo = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]; macLo >>= 1; macLo |= (macHi & 1) << 31; macHi >>= 1; } else { macLo = macHi = 0; } REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo); REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag); return true; } static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry, const struct ath_keyval *k, const u8 *mac) { void *ah = common->ah; u32 key0, key1, key2, key3, key4; u32 keyType; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } switch (k->kv_type) { case ATH_CIPHER_AES_OCB: keyType = AR_KEYTABLE_TYPE_AES; break; case ATH_CIPHER_AES_CCM: if (!(common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)) { ath_print(common, ATH_DBG_ANY, "AES-CCM not supported by this mac rev\n"); return false; } keyType = AR_KEYTABLE_TYPE_CCM; break; case ATH_CIPHER_TKIP: keyType = AR_KEYTABLE_TYPE_TKIP; if (entry + 64 >= common->keymax) { ath_print(common, ATH_DBG_ANY, "entry %u inappropriate for TKIP\n", entry); return false; } break; case ATH_CIPHER_WEP: if (k->kv_len < WLAN_KEY_LEN_WEP40) { ath_print(common, ATH_DBG_ANY, "WEP key length %u too small\n", k->kv_len); return false; } if (k->kv_len <= WLAN_KEY_LEN_WEP40) keyType = AR_KEYTABLE_TYPE_40; else if (k->kv_len <= WLAN_KEY_LEN_WEP104) keyType = AR_KEYTABLE_TYPE_104; else keyType = AR_KEYTABLE_TYPE_128; break; case ATH_CIPHER_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); 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; /* * Write inverted key[47:0] first to avoid Michael MIC errors * on frames that could be sent or received at the same time. * The correct key will be written in the end once everything * else is ready. */ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); /* Write key[95:48] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); /* Write key[127:96] and key type */ REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); /* Write MAC address for the entry */ (void) ath_hw_keysetmac(common, entry, mac); 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); /* Write RX[31:0] and TX[31:16] */ REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); /* Write RX[63:32] and TX[15:0] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); /* Write TX[63:32] and keyType(reserved) */ REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR); } else { /* * TKIP uses four key cache entries (two for group * keys): * Michael MIC TX/RX keys are in different key cache * entries (idx = main index + 64 for TX and * main index + 32 + 96 for RX): * key0 [31:0] = TX/RX MIC key [31:0] * key1 [31:0] = reserved * key2 [31:0] = TX/RX MIC key [63:32] * key3 [31:0] = reserved * key4 [31:0] = reserved * * Upper layer code will call this function separately * for TX and RX keys when these registers offsets are * used. */ u32 mic0, mic2; mic0 = get_unaligned_le32(k->kv_mic + 0); mic2 = get_unaligned_le32(k->kv_mic + 4); /* Write MIC key[31:0] */ REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); /* Write MIC key[63:32] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); /* Write TX[63:32] and keyType(reserved) */ REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR); } /* MAC address registers are reserved for the MIC entry */ REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); /* * Write the correct (un-inverted) key[47:0] last to enable * TKIP now that all other registers are set with correct * values. */ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); } else { /* Write key[47:0] */ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); /* Write key[95:48] */ REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); /* Write key[127:96] and key type */ REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); /* Write MAC address for the entry */ (void) ath_hw_keysetmac(common, entry, mac); } return true; } static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, struct ath_keyval *hk, const u8 *addr, bool authenticator) { const u8 *key_rxmic; const u8 *key_txmic; key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; if (addr == NULL) { /* * Group key installation - only two key cache entries are used * regardless of splitmic capability since group key is only * used either for TX or RX. */ if (authenticator) { memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); } else { 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); } 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); } /* 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)) { /* TX MIC entry failed. No need to proceed further */ ath_err(common, "Setting TX MIC Key Failed\n"); return 0; } 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); } static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) { int i; for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { if (test_bit(i, common->keymap) || test_bit(i + 64, common->keymap)) continue; /* At least one part of TKIP key allocated */ if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) && (test_bit(i + 32, common->keymap) || test_bit(i + 64 + 32, common->keymap))) continue; /* At least one part of TKIP key allocated */ /* Found a free slot for a TKIP key */ return i; } return -1; } static int ath_reserve_key_cache_slot(struct ath_common *common, u32 cipher) { int i; if (cipher == WLAN_CIPHER_SUITE_TKIP) return ath_reserve_key_cache_slot_tkip(common); /* First, try to find slots that would not be available for TKIP. */ if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { if (!test_bit(i, common->keymap) && (test_bit(i + 32, common->keymap) || test_bit(i + 64, common->keymap) || test_bit(i + 64 + 32, common->keymap))) return i; if (!test_bit(i + 32, common->keymap) && (test_bit(i, common->keymap) || test_bit(i + 64, common->keymap) || test_bit(i + 64 + 32, common->keymap))) return i + 32; if (!test_bit(i + 64, common->keymap) && (test_bit(i , common->keymap) || test_bit(i + 32, common->keymap) || test_bit(i + 64 + 32, common->keymap))) return i + 64; if (!test_bit(i + 64 + 32, common->keymap) && (test_bit(i, common->keymap) || test_bit(i + 32, common->keymap) || test_bit(i + 64, common->keymap))) return i + 64 + 32; } } else { for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { if (!test_bit(i, common->keymap) && test_bit(i + 64, common->keymap)) return i; if (test_bit(i, common->keymap) && !test_bit(i + 64, common->keymap)) return i + 64; } } /* No partially used TKIP slots, pick any available slot */ for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) { /* Do not allow slots that could be needed for TKIP group keys * to be used. This limitation could be removed if we know that * TKIP will not be used. */ if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) continue; if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) continue; if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) continue; } if (!test_bit(i, common->keymap)) return i; /* Found a free slot for a key */ } /* No free slot found */ return -1; } /* * Configure encryption in the HW. */ int ath_key_config(struct ath_common *common, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath_keyval hk; const u8 *mac = NULL; u8 gmac[ETH_ALEN]; int ret = 0; int idx; memset(&hk, 0, sizeof(hk)); switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: hk.kv_type = ATH_CIPHER_WEP; break; case WLAN_CIPHER_SUITE_TKIP: hk.kv_type = ATH_CIPHER_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: hk.kv_type = ATH_CIPHER_AES_CCM; break; default: return -EOPNOTSUPP; } hk.kv_len = key->keylen; memcpy(hk.kv_val, key->key, key->keylen); if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { switch (vif->type) { case NL80211_IFTYPE_AP: memcpy(gmac, vif->addr, ETH_ALEN); gmac[0] |= 0x01; mac = gmac; idx = ath_reserve_key_cache_slot(common, key->cipher); break; case NL80211_IFTYPE_ADHOC: if (!sta) { idx = key->keyidx; break; } memcpy(gmac, sta->addr, ETH_ALEN); gmac[0] |= 0x01; mac = gmac; idx = ath_reserve_key_cache_slot(common, key->cipher); break; default: idx = key->keyidx; break; } } else if (key->keyidx) { if (WARN_ON(!sta)) return -EOPNOTSUPP; mac = sta->addr; if (vif->type != NL80211_IFTYPE_AP) { /* Only keyidx 0 should be used with unicast key, but * allow this for client mode for now. */ idx = key->keyidx; } else return -EIO; } else { if (WARN_ON(!sta)) return -EOPNOTSUPP; mac = sta->addr; idx = ath_reserve_key_cache_slot(common, key->cipher); } if (idx < 0) return -ENOSPC; /* no free key cache entries */ if (key->cipher == WLAN_CIPHER_SUITE_TKIP) 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); if (!ret) return -EIO; set_bit(idx, common->keymap); if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { set_bit(idx + 64, common->keymap); set_bit(idx, common->tkip_keymap); set_bit(idx + 64, common->tkip_keymap); if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { set_bit(idx + 32, common->keymap); set_bit(idx + 64 + 32, common->keymap); set_bit(idx + 32, common->tkip_keymap); set_bit(idx + 64 + 32, common->tkip_keymap); } } return idx; } EXPORT_SYMBOL(ath_key_config); /* * Delete Key. */ void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key) { ath_hw_keyreset(common, key->hw_key_idx); if (key->hw_key_idx < IEEE80211_WEP_NKID) return; clear_bit(key->hw_key_idx, common->keymap); if (key->cipher != WLAN_CIPHER_SUITE_TKIP) return; clear_bit(key->hw_key_idx + 64, common->keymap); clear_bit(key->hw_key_idx, common->tkip_keymap); clear_bit(key->hw_key_idx + 64, common->tkip_keymap); if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) { ath_hw_keyreset(common, key->hw_key_idx + 32); clear_bit(key->hw_key_idx + 32, common->keymap); clear_bit(key->hw_key_idx + 64 + 32, common->keymap); clear_bit(key->hw_key_idx + 32, common->tkip_keymap); clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap); } } EXPORT_SYMBOL(ath_key_delete);