From: Dave Voutila Subject: Re: qwx(4) crypto offloading To: Stefan Sperling Cc: tech@openbsd.org Date: Thu, 23 May 2024 11:29:12 -0400 Stefan Sperling writes: > With the patch below, qwx(4) offloads CCMP and TKIP to hardware. > The performance benefit is small with 11a/b/g modes but becomes > significant once 11n/11ac speeds will be enabled eventually. > > Also, there is a firmware bug which prevents reception of broadcast > and multicast frames when crypto is done in software. This is why > ARP and IPv6 are kind of broken on qwx right now, and this diff should > fix these issues on WPA networks at least. > > So far I have only tested CCMP-only (WPA2/AES). In ifconfig such > networks show up as: wpaciphers cmmp wpagroupcipher ccmp That's all I have available, but it's working well so far on my x13s. My network mentions: wpakey wpaprotos wpa2 wpaakms psk wpaciphers ccmp wpagroupcipher ccmp qwx0 at pci1 dev 0 function 0 "Qualcomm QCNFA765" rev 0x01: msi qwx0: wcn6855 hw2.1 fw 0x1106196e address 00:03:7f:12:de:ec smbios0: vendor LENOVO version "N3HET87W (1.59 )" date 12/05/2023 smbios0: LENOVO 21BX0007US > > There are several other cases which still need to be tested: > 1) wpaciphers cmmp wpagroupcipher tkip > 2) wpaprotos wpa1 wpaciphers tkip wpagroupcipher tkip > 3) WEP (not sure if anyone ever tested qwx with WEP before?) > > Due to time constraints I would appreciate help with testing the above. > If you have an OpenBSD hostap then setting up all of these combinations > can be done with ifconfig. For APs from other vendors the non-CCMP modes > will usually be called something like "WPA1" or something that is not "AES". > In any case, ifconfig qwx0 will display the config provided by the AP. > > diff refs/heads/master refs/heads/qwx-crypto > commit - 30ca340796a98c20c16842cbd6e4c25de4709bdb > commit + 7cb71619920688a04ffa06cd1aa374fc5f590796 > blob - 7d95691d20a50b10e671a3fd44fc7d6246a19d4d > blob + c03a0d614c20a3e2f3a0f662d27b545232e19048 > --- sys/dev/ic/qwx.c > +++ sys/dev/ic/qwx.c > @@ -152,6 +152,11 @@ int qwx_dp_tx_send_reo_cmd(struct qwx_softc *, struct > void (*func)(struct qwx_dp *, void *, enum hal_reo_cmd_status)); > void qwx_dp_rx_deliver_msdu(struct qwx_softc *, struct qwx_rx_msdu *); > void qwx_dp_service_mon_ring(void *); > +void qwx_peer_frags_flush(struct qwx_softc *, struct ath11k_peer *); > +int qwx_wmi_vdev_install_key(struct qwx_softc *, > + struct wmi_vdev_install_key_arg *, uint8_t); > +int qwx_dp_peer_rx_pn_replay_config(struct qwx_softc *, struct qwx_vif *, > + struct ieee80211_node *, struct ieee80211_key *, int); > > int qwx_scan(struct qwx_softc *); > void qwx_scan_abort(struct qwx_softc *); > @@ -178,7 +183,7 @@ qwx_init(struct ifnet *ifp) > struct ieee80211com *ic = &sc->sc_ic; > > sc->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL; > - sc->crypto_mode = ATH11K_CRYPT_MODE_SW; > + sc->crypto_mode = ATH11K_CRYPT_MODE_HW; > sc->frame_mode = ATH11K_HW_TXRX_NATIVE_WIFI; > ic->ic_state = IEEE80211_S_INIT; > sc->ns_nstate = IEEE80211_S_INIT; > @@ -283,6 +288,7 @@ qwx_stop(struct ifnet *ifp) > /* Cancel scheduled tasks and let any stale tasks finish up. */ > task_del(systq, &sc->init_task); > qwx_del_task(sc, sc->sc_nswq, &sc->newstate_task); > + qwx_del_task(sc, systq, &sc->setkey_task); > refcnt_finalize(&sc->task_refs, "qwxstop"); > > clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags); > @@ -496,6 +502,262 @@ qwx_media_change(struct ifnet *ifp) > } > > int > +qwx_queue_setkey_cmd(struct ieee80211com *ic, struct ieee80211_node *ni, > + struct ieee80211_key *k, int cmd) > +{ > + struct qwx_softc *sc = ic->ic_softc; > + struct qwx_setkey_task_arg *a; > + > + if (sc->setkey_nkeys >= nitems(sc->setkey_arg) || > + k->k_id > WMI_MAX_KEY_INDEX) > + return ENOSPC; > + > + a = &sc->setkey_arg[sc->setkey_cur]; > + a->ni = ieee80211_ref_node(ni); > + a->k = k; > + a->cmd = cmd; > + sc->setkey_cur = (sc->setkey_cur + 1) % nitems(sc->setkey_arg); > + sc->setkey_nkeys++; > + qwx_add_task(sc, systq, &sc->setkey_task); > + return EBUSY; > +} > + > +int > +qwx_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, > + struct ieee80211_key *k) > +{ > + struct qwx_softc *sc = ic->ic_softc; > + > + if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags) || > + (k->k_cipher != IEEE80211_CIPHER_CCMP && > + k->k_cipher != IEEE80211_CIPHER_TKIP)) > + return ieee80211_set_key(ic, ni, k); > + > + return qwx_queue_setkey_cmd(ic, ni, k, QWX_ADD_KEY); > +} > + > +void > +qwx_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, > + struct ieee80211_key *k) > +{ > + struct qwx_softc *sc = ic->ic_softc; > + > + if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags) || > + (k->k_cipher != IEEE80211_CIPHER_CCMP && > + k->k_cipher != IEEE80211_CIPHER_TKIP)) { > + ieee80211_delete_key(ic, ni, k); > + return; > + } > + > + if (ic->ic_state != IEEE80211_S_RUN) { > + /* Keys removed implicitly when firmware station is removed. */ > + return; > + } > + > + /* > + * net80211 calls us with a NULL node when deleting group keys, > + * but firmware expects a MAC address in the command. > + */ > + if (ni == NULL) > + ni = ic->ic_bss; > + > + qwx_queue_setkey_cmd(ic, ni, k, QWX_DEL_KEY); > +} > + > +int > +qwx_wmi_install_key_cmd(struct qwx_softc *sc, struct qwx_vif *arvif, > + uint8_t *macaddr, struct ieee80211_key *k, uint32_t flags, > + int delete_key) > +{ > + int ret; > + struct wmi_vdev_install_key_arg arg = { > + .vdev_id = arvif->vdev_id, > + .key_idx = k->k_id, > + .key_len = k->k_len, > + .key_data = k->k_key, > + .key_flags = flags, > + .macaddr = macaddr, > + }; > + uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */ > +#ifdef notyet > + lockdep_assert_held(&arvif->ar->conf_mutex); > + > + reinit_completion(&ar->install_key_done); > +#endif > + if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags)) > + return 0; > + > + if (delete_key) { > + arg.key_cipher = WMI_CIPHER_NONE; > + arg.key_data = NULL; > + } else { > + switch (k->k_cipher) { > + case IEEE80211_CIPHER_CCMP: > + arg.key_cipher = WMI_CIPHER_AES_CCM; > +#if 0 > + /* TODO: Re-check if flag is valid */ > + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; > +#endif > + break; > + case IEEE80211_CIPHER_TKIP: > + arg.key_cipher = WMI_CIPHER_TKIP; > + arg.key_txmic_len = 8; > + arg.key_rxmic_len = 8; > + break; > +#if 0 > + case WLAN_CIPHER_SUITE_CCMP_256: > + arg.key_cipher = WMI_CIPHER_AES_CCM; > + break; > + case WLAN_CIPHER_SUITE_GCMP: > + case WLAN_CIPHER_SUITE_GCMP_256: > + arg.key_cipher = WMI_CIPHER_AES_GCM; > + break; > +#endif > + default: > + printf("%s: cipher %u is not supported\n", > + sc->sc_dev.dv_xname, k->k_cipher); > + return EOPNOTSUPP; > + } > +#if 0 > + if (test_bit(ATH11K_FLAG_RAW_MODE, &ar->ab->dev_flags)) > + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV | > + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; > +#endif > + } > + > + sc->install_key_done = 0; > + ret = qwx_wmi_vdev_install_key(sc, &arg, pdev_id); > + if (ret) > + return ret; > + > + while (!sc->install_key_done) { > + ret = tsleep_nsec(&sc->install_key_done, 0, "qwxinstkey", > + SEC_TO_NSEC(1)); > + if (ret) { > + printf("%s: install key timeout\n", > + sc->sc_dev.dv_xname); > + return -1; > + } > + } > + > + return sc->install_key_status; > +} > + > +int > +qwx_add_sta_key(struct qwx_softc *sc, struct ieee80211_node *ni, > + struct ieee80211_key *k) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct qwx_node *nq = (struct qwx_node *)ni; > + struct ath11k_peer *peer = &nq->peer; > + struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ > + int ret = 0; > + uint32_t flags = 0; > + const int want_keymask = (QWX_NODE_FLAG_HAVE_PAIRWISE_KEY | > + QWX_NODE_FLAG_HAVE_GROUP_KEY); > + > + /* > + * Flush the fragments cache during key (re)install to > + * ensure all frags in the new frag list belong to the same key. > + */ > + qwx_peer_frags_flush(sc, peer); > + > + if (k->k_flags & IEEE80211_KEY_GROUP) > + flags |= WMI_KEY_GROUP; > + else > + flags |= WMI_KEY_PAIRWISE; > + > + ret = qwx_wmi_install_key_cmd(sc, arvif, ni->ni_macaddr, k, flags, 0); > + if (ret) { > + printf("%s: installing crypto key failed (%d)\n", > + sc->sc_dev.dv_xname, ret); > + return ret; > + } > + > + ret = qwx_dp_peer_rx_pn_replay_config(sc, arvif, ni, k, 0); > + if (ret) { > + printf("%s: failed to offload PN replay detection %d\n", > + sc->sc_dev.dv_xname, ret); > + return ret; > + } > + > + if (k->k_flags & IEEE80211_KEY_GROUP) > + nq->flags |= QWX_NODE_FLAG_HAVE_GROUP_KEY; > + else > + nq->flags |= QWX_NODE_FLAG_HAVE_PAIRWISE_KEY; > + > + if ((nq->flags & want_keymask) == want_keymask) { > + DPRINTF("marking port %s valid\n", > + ether_sprintf(ni->ni_macaddr)); > + ni->ni_port_valid = 1; > + ieee80211_set_link_state(ic, LINK_STATE_UP); > + } > + > + return 0; > +} > + > +int > +qwx_del_sta_key(struct qwx_softc *sc, struct ieee80211_node *ni, > + struct ieee80211_key *k) > +{ > + struct qwx_node *nq = (struct qwx_node *)ni; > + struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ > + int ret = 0; > + > + ret = qwx_wmi_install_key_cmd(sc, arvif, ni->ni_macaddr, k, 0, 1); > + if (ret) { > + printf("%s: deleting crypto key failed (%d)\n", > + sc->sc_dev.dv_xname, ret); > + return ret; > + } > + > + ret = qwx_dp_peer_rx_pn_replay_config(sc, arvif, ni, k, 1); > + if (ret) { > + printf("%s: failed to disable PN replay detection %d\n", > + sc->sc_dev.dv_xname, ret); > + return ret; > + } > + > + if (k->k_flags & IEEE80211_KEY_GROUP) > + nq->flags &= ~QWX_NODE_FLAG_HAVE_GROUP_KEY; > + else > + nq->flags &= ~QWX_NODE_FLAG_HAVE_PAIRWISE_KEY; > + > + return 0; > +} > + > +void > +qwx_setkey_task(void *arg) > +{ > + struct qwx_softc *sc = arg; > + struct ieee80211com *ic = &sc->sc_ic; > + struct qwx_setkey_task_arg *a; > + int err = 0, s = splnet(); > + > + while (sc->setkey_nkeys > 0) { > + if (err || test_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags)) > + break; > + a = &sc->setkey_arg[sc->setkey_tail]; > + KASSERT(a->cmd == QWX_ADD_KEY || a->cmd == QWX_DEL_KEY); > + if (ic->ic_state == IEEE80211_S_RUN) { > + if (a->cmd == QWX_ADD_KEY) > + err = qwx_add_sta_key(sc, a->ni, a->k); > + else > + err = qwx_del_sta_key(sc, a->ni, a->k); > + } > + ieee80211_release_node(ic, a->ni); > + a->ni = NULL; > + a->k = NULL; > + sc->setkey_tail = (sc->setkey_tail + 1) % > + nitems(sc->setkey_arg); > + sc->setkey_nkeys--; > + } > + > + refcnt_rele_wake(&sc->task_refs); > + splx(s); > +} > + > +int > qwx_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) > { > struct ifnet *ifp = &ic->ic_if; > @@ -510,15 +772,27 @@ qwx_newstate(struct ieee80211com *ic, enum ieee80211_s > if (sc->ns_nstate == nstate && nstate != IEEE80211_S_SCAN && > nstate != IEEE80211_S_AUTH) > return 0; > -#if 0 > if (ic->ic_state == IEEE80211_S_RUN) { > + struct qwx_setkey_task_arg *a; > +#if 0 > qwx_del_task(sc, systq, &sc->ba_task); > +#endif > qwx_del_task(sc, systq, &sc->setkey_task); > + while (sc->setkey_nkeys > 0) { > + a = &sc->setkey_arg[sc->setkey_tail]; > + ieee80211_release_node(ic, a->ni); > + a->ni = NULL; > + sc->setkey_tail = (sc->setkey_tail + 1) % > + nitems(sc->setkey_arg); > + sc->setkey_nkeys--; > + } > memset(sc->setkey_arg, 0, sizeof(sc->setkey_arg)); > sc->setkey_cur = sc->setkey_tail = sc->setkey_nkeys = 0; > +#if 0 > qwx_del_task(sc, systq, &sc->bgscan_done_task); > - } > #endif > + } > + > sc->ns_nstate = nstate; > sc->ns_arg = arg; > > @@ -893,6 +1167,13 @@ qwx_hw_mac_id_to_srng_id_qca6390(struct ath11k_hw_para > return mac_id; > } > > +int > +qwx_hw_ipq8074_rx_desc_get_first_msdu(struct hal_rx_desc *desc) > +{ > + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU, > + le32toh(desc->u.ipq8074.msdu_end.info2)); > +} > + > uint8_t > qwx_hw_ipq8074_rx_desc_get_l3_pad_bytes(struct hal_rx_desc *desc) > { > @@ -1060,6 +1341,12 @@ qwx_hw_ipq8074_rx_desc_set_msdu_len(struct hal_rx_desc > } > > int > +qwx_dp_rx_h_msdu_end_first_msdu(struct qwx_softc *sc, struct hal_rx_desc *desc) > +{ > + return sc->hw_params.hw_ops->rx_desc_get_first_msdu(desc); > +} > + > +int > qwx_hw_ipq8074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) > { > return le32toh(desc->u.ipq8074.mpdu_start.info1) & > @@ -1524,7 +1811,9 @@ const struct ath11k_hw_ops ipq8074_ops = { > .mac_id_to_srng_id = qwx_hw_mac_id_to_srng_id_ipq8074, > #if notyet > .tx_mesh_enable = ath11k_hw_ipq8074_tx_mesh_enable, > - .rx_desc_get_first_msdu = ath11k_hw_ipq8074_rx_desc_get_first_msdu, > +#endif > + .rx_desc_get_first_msdu = qwx_hw_ipq8074_rx_desc_get_first_msdu, > +#if notyet > .rx_desc_get_last_msdu = ath11k_hw_ipq8074_rx_desc_get_last_msdu, > #endif > .rx_desc_get_l3_pad_bytes = qwx_hw_ipq8074_rx_desc_get_l3_pad_bytes, > @@ -1576,7 +1865,9 @@ const struct ath11k_hw_ops ipq6018_ops = { > .mac_id_to_srng_id = qwx_hw_mac_id_to_srng_id_ipq8074, > #if notyet > .tx_mesh_enable = ath11k_hw_ipq8074_tx_mesh_enable, > - .rx_desc_get_first_msdu = ath11k_hw_ipq8074_rx_desc_get_first_msdu, > +#endif > + .rx_desc_get_first_msdu = qwx_hw_ipq8074_rx_desc_get_first_msdu, > +#if notyet > .rx_desc_get_last_msdu = ath11k_hw_ipq8074_rx_desc_get_last_msdu, > #endif > .rx_desc_get_l3_pad_bytes = qwx_hw_ipq8074_rx_desc_get_l3_pad_bytes, > @@ -1628,7 +1919,9 @@ const struct ath11k_hw_ops qca6390_ops = { > .mac_id_to_srng_id = qwx_hw_mac_id_to_srng_id_qca6390, > #if notyet > .tx_mesh_enable = ath11k_hw_ipq8074_tx_mesh_enable, > - .rx_desc_get_first_msdu = ath11k_hw_ipq8074_rx_desc_get_first_msdu, > +#endif > + .rx_desc_get_first_msdu = qwx_hw_ipq8074_rx_desc_get_first_msdu, > +#if notyet > .rx_desc_get_last_msdu = ath11k_hw_ipq8074_rx_desc_get_last_msdu, > #endif > .rx_desc_get_l3_pad_bytes = qwx_hw_ipq8074_rx_desc_get_l3_pad_bytes, > @@ -1680,7 +1973,9 @@ const struct ath11k_hw_ops qcn9074_ops = { > .mac_id_to_srng_id = qwx_hw_mac_id_to_srng_id_ipq8074, > #if notyet > .tx_mesh_enable = ath11k_hw_qcn9074_tx_mesh_enable, > - .rx_desc_get_first_msdu = ath11k_hw_qcn9074_rx_desc_get_first_msdu, > +#endif > + .rx_desc_get_first_msdu = qwx_hw_qcn9074_rx_desc_get_first_msdu, > +#if notyet > .rx_desc_get_last_msdu = ath11k_hw_qcn9074_rx_desc_get_last_msdu, > #endif > .rx_desc_get_l3_pad_bytes = qwx_hw_qcn9074_rx_desc_get_l3_pad_bytes, > @@ -1732,7 +2027,9 @@ const struct ath11k_hw_ops wcn6855_ops = { > .mac_id_to_srng_id = qwx_hw_mac_id_to_srng_id_qca6390, > #if notyet > .tx_mesh_enable = ath11k_hw_wcn6855_tx_mesh_enable, > - .rx_desc_get_first_msdu = ath11k_hw_wcn6855_rx_desc_get_first_msdu, > +#endif > + .rx_desc_get_first_msdu = qwx_hw_wcn6855_rx_desc_get_first_msdu, > +#if notyet > .rx_desc_get_last_msdu = ath11k_hw_wcn6855_rx_desc_get_last_msdu, > #endif > .rx_desc_get_l3_pad_bytes = qwx_hw_wcn6855_rx_desc_get_l3_pad_bytes, > @@ -1784,7 +2081,9 @@ const struct ath11k_hw_ops wcn6750_ops = { > .mac_id_to_srng_id = qwx_hw_mac_id_to_srng_id_qca6390, > #if notyet > .tx_mesh_enable = ath11k_hw_qcn9074_tx_mesh_enable, > - .rx_desc_get_first_msdu = ath11k_hw_qcn9074_rx_desc_get_first_msdu, > +#endif > + .rx_desc_get_first_msdu = qwx_hw_qcn9074_rx_desc_get_first_msdu, > +#if notyet > .rx_desc_get_last_msdu = ath11k_hw_qcn9074_rx_desc_get_last_msdu, > #endif > .rx_desc_get_l3_pad_bytes = qwx_hw_qcn9074_rx_desc_get_l3_pad_bytes, > @@ -12990,7 +13289,85 @@ qwx_roam_event(struct qwx_softc *sc, struct mbuf *m) > } > } > > +int > +qwx_pull_vdev_install_key_compl_ev(struct qwx_softc *sc, struct mbuf *m, > + struct wmi_vdev_install_key_complete_arg *arg) > +{ > + const void **tb; > + const struct wmi_vdev_install_key_compl_event *ev; > + int ret; > + > + tb = qwx_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); > + if (tb == NULL) { > + ret = ENOMEM; > + printf("%s: failed to parse tlv: %d\n", > + sc->sc_dev.dv_xname, ret); > + return ret; > + } > + > + ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT]; > + if (!ev) { > + printf("%s: failed to fetch vdev install key compl ev\n", > + sc->sc_dev.dv_xname); > + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); > + return EPROTO; > + } > + > + arg->vdev_id = ev->vdev_id; > + arg->macaddr = ev->peer_macaddr.addr; > + arg->key_idx = ev->key_idx; > + arg->key_flags = ev->key_flags; > + arg->status = ev->status; > + > + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); > + return 0; > +} > + > void > +qwx_vdev_install_key_compl_event(struct qwx_softc *sc, struct mbuf *m) > +{ > + struct wmi_vdev_install_key_complete_arg install_key_compl = { 0 }; > + struct qwx_vif *arvif; > + > + if (qwx_pull_vdev_install_key_compl_ev(sc, m, > + &install_key_compl) != 0) { > + printf("%s: failed to extract install key compl event\n", > + sc->sc_dev.dv_xname); > + return; > + } > + > + DNPRINTF(QWX_D_WMI, "%s: event vdev install key ev idx %d flags %08x " > + "macaddr %s status %d\n", __func__, install_key_compl.key_idx, > + install_key_compl.key_flags, > + ether_sprintf((u_char *)install_key_compl.macaddr), > + install_key_compl.status); > + > + TAILQ_FOREACH(arvif, &sc->vif_list, entry) { > + if (arvif->vdev_id == install_key_compl.vdev_id) > + break; > + } > + if (!arvif) { > + printf("%s: invalid vdev id in install key compl ev %d\n", > + sc->sc_dev.dv_xname, install_key_compl.vdev_id); > + return; > + } > + > + sc->install_key_status = 0; > + > + if (install_key_compl.status != > + WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS) { > + printf("%s: install key failed for %s status %d\n", > + sc->sc_dev.dv_xname, > + ether_sprintf((u_char *)install_key_compl.macaddr), > + install_key_compl.status); > + sc->install_key_status = install_key_compl.status; > + } > + > + sc->install_key_done = 1; > + wakeup(&sc->install_key_done); > +} > + > +void > qwx_wmi_tlv_op_rx(struct qwx_softc *sc, struct mbuf *m) > { > struct wmi_cmd_hdr *cmd_hdr; > @@ -13060,10 +13437,10 @@ qwx_wmi_tlv_op_rx(struct qwx_softc *sc, struct mbuf *m > case WMI_PDEV_BSS_CHAN_INFO_EVENTID: > ath11k_pdev_bss_chan_info_event(ab, skb); > break; > +#endif > case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID: > - ath11k_vdev_install_key_compl_event(ab, skb); > + qwx_vdev_install_key_compl_event(sc, m); > break; > -#endif > case WMI_SERVICE_AVAILABLE_EVENTID: > qwx_service_available_event(sc, m); > break; > @@ -15813,6 +16190,16 @@ qwx_dp_rx_get_attention(struct qwx_softc *sc, struct h > return sc->hw_params.hw_ops->rx_desc_get_attention(desc); > } > > +int > +qwx_dp_rx_h_attn_is_mcbc(struct qwx_softc *sc, struct hal_rx_desc *desc) > +{ > + struct rx_attention *attn = qwx_dp_rx_get_attention(sc, desc); > + > + return qwx_dp_rx_h_msdu_end_first_msdu(sc, desc) && > + (!!FIELD_GET(RX_ATTENTION_INFO1_MCAST_BCAST, > + le32toh(attn->info1))); > +} > + > static inline uint8_t > qwx_dp_rx_h_msdu_end_l3pad(struct qwx_softc *sc, struct hal_rx_desc *desc) > { > @@ -15874,6 +16261,13 @@ qwx_dp_rx_h_attn_msdu_len_err(struct qwx_softc *sc, st > } > > int > +qwx_dp_rx_h_attn_is_decrypted(struct rx_attention *attn) > +{ > + return (FIELD_GET(RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE, > + le32toh(attn->info2)) == RX_DESC_DECRYPT_STATUS_CODE_OK); > +} > + > +int > qwx_dp_rx_msdu_coalesce(struct qwx_softc *sc, struct qwx_rx_msdu_list *msdu_list, > struct qwx_rx_msdu *first, struct qwx_rx_msdu *last, uint8_t l3pad_bytes, > int msdu_len) > @@ -15908,7 +16302,13 @@ void > qwx_dp_rx_h_undecap_nwifi(struct qwx_softc *sc, struct qwx_rx_msdu *msdu, > uint8_t *first_hdr, enum hal_encrypt_type enctype) > { > - printf("%s: not implemented\n", __func__); > + /* > + * This function will need to do some work once we are receiving > + * aggregated frames. For now, it needs to do nothing. > + */ > + > + if (!msdu->is_first_msdu) > + printf("%s: not implemented\n", __func__); > } > > void > @@ -16034,28 +16434,28 @@ qwx_dp_rx_h_undecap(struct qwx_softc *sc, struct qwx_r > } > } > > - > -void > +int > qwx_dp_rx_h_mpdu(struct qwx_softc *sc, struct qwx_rx_msdu *msdu, > struct hal_rx_desc *rx_desc) > { > -#if 0 > - bool fill_crypto_hdr; > -#endif > + struct ieee80211com *ic = &sc->sc_ic; > + int fill_crypto_hdr = 0; > enum hal_encrypt_type enctype; > int is_decrypted = 0; > #if 0 > struct ath11k_skb_rxcb *rxcb; > - struct ieee80211_hdr *hdr; > +#endif > + struct ieee80211_frame *wh; > +#if 0 > struct ath11k_peer *peer; > +#endif > struct rx_attention *rx_attention; > - u32 err_bitmap; > + uint32_t err_bitmap; > > - /* PN for multicast packets will be checked in mac80211 */ > - rxcb = ATH11K_SKB_RXCB(msdu); > - fill_crypto_hdr = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc); > - rxcb->is_mcbc = fill_crypto_hdr; > - > + /* PN for multicast packets will be checked in net80211 */ > + fill_crypto_hdr = qwx_dp_rx_h_attn_is_mcbc(sc, rx_desc); > + msdu->is_mcbc = fill_crypto_hdr; > +#if 0 > if (rxcb->is_mcbc) { > rxcb->peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ar->ab, rx_desc); > rxcb->seq_no = ath11k_dp_rx_h_mpdu_start_seq_no(ar->ab, rx_desc); > @@ -16074,12 +16474,12 @@ qwx_dp_rx_h_mpdu(struct qwx_softc *sc, struct qwx_rx_m > #if 0 > } > spin_unlock_bh(&ar->ab->base_lock); > - > - rx_attention = ath11k_dp_rx_get_attention(ar->ab, rx_desc); > - err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_attention); > +#endif > + rx_attention = qwx_dp_rx_get_attention(sc, rx_desc); > + err_bitmap = qwx_dp_rx_h_attn_mpdu_err(rx_attention); > if (enctype != HAL_ENCRYPT_TYPE_OPEN && !err_bitmap) > - is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_attention); > - > + is_decrypted = qwx_dp_rx_h_attn_is_decrypted(rx_attention); > +#if 0 > /* Clear per-MPDU flags while leaving per-PPDU flags intact */ > rx_status->flag &= ~(RX_FLAG_FAILED_FCS_CRC | > RX_FLAG_MMIC_ERROR | > @@ -16087,12 +16487,23 @@ qwx_dp_rx_h_mpdu(struct qwx_softc *sc, struct qwx_rx_m > RX_FLAG_IV_STRIPPED | > RX_FLAG_MMIC_STRIPPED); > > - if (err_bitmap & DP_RX_MPDU_ERR_FCS) > - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; > +#endif > + if (err_bitmap & DP_RX_MPDU_ERR_FCS) { > + if (ic->ic_flags & IEEE80211_F_RSNON) > + ic->ic_stats.is_rx_decryptcrc++; > + else > + ic->ic_stats.is_rx_decap++; > + } > + > + /* XXX Trusting firmware to handle Michael MIC counter-measures... */ > if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) > - rx_status->flag |= RX_FLAG_MMIC_ERROR; > + ic->ic_stats.is_rx_locmicfail++; > > + if (err_bitmap & DP_RX_MPDU_ERR_DECRYPT) > + ic->ic_stats.is_rx_wepfail++; > + > if (is_decrypted) { > +#if 0 > rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; > > if (fill_crypto_hdr) > @@ -16101,21 +16512,23 @@ qwx_dp_rx_h_mpdu(struct qwx_softc *sc, struct qwx_rx_m > else > rx_status->flag |= RX_FLAG_IV_STRIPPED | > RX_FLAG_PN_VALIDATED; > +#endif > + msdu->rxi.rxi_flags |= IEEE80211_RXI_HWDEC; > } > - > +#if 0 > ath11k_dp_rx_h_csum_offload(ar, msdu); > #endif > qwx_dp_rx_h_undecap(sc, msdu, rx_desc, enctype, is_decrypted); > -#if 0 > - if (!is_decrypted || fill_crypto_hdr) > - return; > > - if (ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rx_desc) != > + if (is_decrypted && !fill_crypto_hdr && > + qwx_dp_rx_h_msdu_start_decap_type(sc, rx_desc) != > DP_RX_DECAP_TYPE_ETHERNET2_DIX) { > - hdr = (void *)msdu->data; > - hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); > + /* Hardware has stripped the IV. */ > + wh = mtod(msdu->m, struct ieee80211_frame *); > + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; > } > -#endif > + > + return err_bitmap ? EIO : 0; > } > > int > @@ -16189,9 +16602,8 @@ qwx_dp_rx_process_msdu(struct qwx_softc *sc, struct qw > > memset(&msdu->rxi, 0, sizeof(msdu->rxi)); > qwx_dp_rx_h_ppdu(sc, rx_desc, &msdu->rxi); > - qwx_dp_rx_h_mpdu(sc, msdu, rx_desc); > > - return 0; > + return qwx_dp_rx_h_mpdu(sc, msdu, rx_desc); > } > > void > @@ -18003,6 +18415,65 @@ qwx_wmi_send_peer_delete_cmd(struct qwx_softc *sc, con > return 0; > } > > +int > +qwx_wmi_vdev_install_key(struct qwx_softc *sc, > + struct wmi_vdev_install_key_arg *arg, uint8_t pdev_id) > +{ > + struct qwx_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; > + struct wmi_vdev_install_key_cmd *cmd; > + struct wmi_tlv *tlv; > + struct mbuf *m; > + int ret, len; > + int key_len_aligned = roundup(arg->key_len, sizeof(uint32_t)); > + > + len = sizeof(*cmd) + TLV_HDR_SIZE + key_len_aligned; > + > + m = qwx_wmi_alloc_mbuf(len); > + if (m == NULL) > + return -ENOMEM; > + > + cmd = (struct wmi_vdev_install_key_cmd *)(mtod(m, uint8_t *) + > + sizeof(struct ath11k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); > + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, > + WMI_TAG_VDEV_INSTALL_KEY_CMD) | > + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); > + cmd->vdev_id = arg->vdev_id; > + IEEE80211_ADDR_COPY(cmd->peer_macaddr.addr, arg->macaddr); > + cmd->key_idx = arg->key_idx; > + cmd->key_flags = arg->key_flags; > + cmd->key_cipher = arg->key_cipher; > + cmd->key_len = arg->key_len; > + cmd->key_txmic_len = arg->key_txmic_len; > + cmd->key_rxmic_len = arg->key_rxmic_len; > + > + if (arg->key_rsc_counter) > + memcpy(&cmd->key_rsc_counter, &arg->key_rsc_counter, > + sizeof(struct wmi_key_seq_counter)); > + > + tlv = (struct wmi_tlv *)(mtod(m, uint8_t *) + > + sizeof(struct ath11k_htc_hdr) + sizeof(struct wmi_cmd_hdr) + > + sizeof(*cmd)); > + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | > + FIELD_PREP(WMI_TLV_LEN, key_len_aligned); > + if (arg->key_data) > + memcpy(tlv->value, (uint8_t *)arg->key_data, > + key_len_aligned); > + > + ret = qwx_wmi_cmd_send(wmi, m, WMI_VDEV_INSTALL_KEY_CMDID); > + if (ret) { > + printf("%s: failed to send WMI_VDEV_INSTALL_KEY cmd\n", > + sc->sc_dev.dv_xname); > + m_freem(m); > + return ret; > + } > + > + DNPRINTF(QWX_D_WMI, > + "%s: cmd vdev install key idx %d cipher %d len %d\n", > + __func__, arg->key_idx, arg->key_cipher, arg->key_len); > + > + return ret; > +} > + > void > qwx_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd, > struct peer_assoc_params *param, int hw_crypto_disabled) > @@ -23303,6 +23774,26 @@ qwx_dp_rx_frags_cleanup(struct qwx_softc *sc, struct d > } > > void > +qwx_peer_frags_flush(struct qwx_softc *sc, struct ath11k_peer *peer) > +{ > + struct dp_rx_tid *rx_tid; > + int i; > +#ifdef notyet > + lockdep_assert_held(&ar->ab->base_lock); > +#endif > + for (i = 0; i < IEEE80211_NUM_TID; i++) { > + rx_tid = &peer->rx_tid[i]; > + > + qwx_dp_rx_frags_cleanup(sc, rx_tid, 1); > +#if 0 > + spin_unlock_bh(&ar->ab->base_lock); > + del_timer_sync(&rx_tid->frag_timer); > + spin_lock_bh(&ar->ab->base_lock); > +#endif > + } > +} > + > +void > qwx_peer_rx_tid_cleanup(struct qwx_softc *sc, struct ath11k_peer *peer) > { > struct dp_rx_tid *rx_tid; > @@ -23556,6 +24047,70 @@ peer_clean: > return ret; > } > > +int > +qwx_dp_peer_rx_pn_replay_config(struct qwx_softc *sc, struct qwx_vif *arvif, > + struct ieee80211_node *ni, struct ieee80211_key *k, int delete_key) > +{ > + struct ath11k_hal_reo_cmd cmd = {0}; > + struct qwx_node *nq = (struct qwx_node *)ni; > + struct ath11k_peer *peer = &nq->peer; > + struct dp_rx_tid *rx_tid; > + uint8_t tid; > + int ret = 0; > + > + /* > + * NOTE: Enable PN/TSC replay check offload only for unicast frames. > + * We use net80211 PN/TSC replay check functionality for bcast/mcast > + * for now. > + */ > + if (k->k_flags & IEEE80211_KEY_GROUP) > + return 0; > + > + cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS; > + cmd.upd0 |= HAL_REO_CMD_UPD0_PN | > + HAL_REO_CMD_UPD0_PN_SIZE | > + HAL_REO_CMD_UPD0_PN_VALID | > + HAL_REO_CMD_UPD0_PN_CHECK | > + HAL_REO_CMD_UPD0_SVLD; > + > + switch (k->k_cipher) { > + case IEEE80211_CIPHER_TKIP: > + case IEEE80211_CIPHER_CCMP: > +#if 0 > + case WLAN_CIPHER_SUITE_CCMP_256: > + case WLAN_CIPHER_SUITE_GCMP: > + case WLAN_CIPHER_SUITE_GCMP_256: > +#endif > + if (!delete_key) { > + cmd.upd1 |= HAL_REO_CMD_UPD1_PN_CHECK; > + cmd.pn_size = 48; > + } > + break; > + default: > + printf("%s: cipher %u is not supported\n", > + sc->sc_dev.dv_xname, k->k_cipher); > + return EOPNOTSUPP; > + } > + > + for (tid = 0; tid < IEEE80211_NUM_TID; tid++) { > + rx_tid = &peer->rx_tid[tid]; > + if (!rx_tid->active) > + continue; > + cmd.addr_lo = rx_tid->paddr & 0xffffffff; > + cmd.addr_hi = (rx_tid->paddr >> 32); > + ret = qwx_dp_tx_send_reo_cmd(sc, rx_tid, > + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, NULL); > + if (ret) { > + printf("%s: failed to configure rx tid %d queue " > + "for pn replay detection %d\n", > + sc->sc_dev.dv_xname, tid, ret); > + break; > + } > + } > + > + return ret; > +} > + > enum hal_tcl_encap_type > qwx_dp_tx_get_encap_type(struct qwx_softc *sc) > { > @@ -23676,20 +24231,25 @@ qwx_dp_tx(struct qwx_softc *sc, struct qwx_vif *arvif, > > ti.meta_data_flags = arvif->tcl_metadata; > > - if (ti.encap_type == HAL_TCL_ENCAP_TYPE_RAW) { > -#if 0 > - if (skb_cb->flags & ATH11K_SKB_CIPHER_SET) { > - ti.encrypt_type = > - ath11k_dp_tx_get_encrypt_type(skb_cb->cipher); > - > - if (ieee80211_has_protected(hdr->frame_control)) > - skb_put(skb, IEEE80211_CCMP_MIC_LEN); > - } else > -#endif > + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && > + ti.encap_type == HAL_TCL_ENCAP_TYPE_RAW) { > + k = ieee80211_get_txkey(ic, wh, ni); > + switch (k->k_cipher) { > + case IEEE80211_CIPHER_CCMP: > + ti.encrypt_type = HAL_ENCRYPT_TYPE_CCMP_128; > + m->m_pkthdr.len += IEEE80211_CCMP_MICLEN; > + break; > + case IEEE80211_CIPHER_TKIP: > + ti.encrypt_type = HAL_ENCRYPT_TYPE_TKIP_MIC; > + m->m_pkthdr.len += IEEE80211_TKIP_MICLEN; > + break; > + default: > + /* Fallback to software crypto for other ciphers. */ > ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; > + break; > + } > > - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { > - k = ieee80211_get_txkey(ic, wh, ni); > + if (ti.encrypt_type == HAL_ENCRYPT_TYPE_OPEN) { > if ((m = ieee80211_encrypt(ic, m, k)) == NULL) > return ENOBUFS; > /* 802.11 header may have moved. */ > @@ -24582,6 +25142,25 @@ qwx_peer_assoc_h_basic(struct qwx_softc *sc, struct qw > arg->peer_caps = ni->ni_capinfo; > } > > +void > +qwx_peer_assoc_h_crypto(struct qwx_softc *sc, struct qwx_vif *arvif, > + struct ieee80211_node *ni, struct peer_assoc_params *arg) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + > + if (ic->ic_flags & IEEE80211_F_RSNON) { > + arg->need_ptk_4_way = 1; > + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) > + arg->need_gtk_2_way = 1; > + } > +#if 0 > + if (sta->mfp) { > + /* TODO: Need to check if FW supports PMF? */ > + arg->is_pmf_enabled = true; > + } > +#endif > +} > + > int > qwx_mac_rate_is_cck(uint8_t rate) > { > @@ -24641,9 +25220,7 @@ qwx_peer_assoc_prepare(struct qwx_softc *sc, struct qw > > arg->peer_new_assoc = !reassoc; > qwx_peer_assoc_h_basic(sc, arvif, ni, arg); > -#if 0 > qwx_peer_assoc_h_crypto(sc, arvif, ni, arg); > -#endif > qwx_peer_assoc_h_rates(ni, arg); > qwx_peer_assoc_h_phymode(sc, ni, arg); > #if 0 > @@ -24757,12 +25334,15 @@ qwx_run_stop(struct qwx_softc *sc) > struct ieee80211com *ic = &sc->sc_ic; > struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ > uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */ > + struct qwx_node *nq = (void *)ic->ic_bss; > int ret; > > sc->ops.irq_disable(sc); > > - if (ic->ic_opmode == IEEE80211_M_STA) > + if (ic->ic_opmode == IEEE80211_M_STA) { > ic->ic_bss->ni_txrate = 0; > + nq->flags = 0; > + } > > ret = qwx_wmi_vdev_down(sc, arvif->vdev_id, pdev_id); > if (ret) > @@ -24801,6 +25381,7 @@ qwx_attach(struct qwx_softc *sc) > > task_set(&sc->init_task, qwx_init_task, sc); > task_set(&sc->newstate_task, qwx_newstate_task, sc); > + task_set(&sc->setkey_task, qwx_setkey_task, sc); > timeout_set_proc(&sc->scan.timeout, qwx_scan_timeout, sc); > #if NBPFILTER > 0 > qwx_radiotap_attach(sc); > blob - 58c0bf6460d6cace7da8c13b8f66b90f5a022617 > blob + 9bafe25e29d455cf89c5508053d3daa116b84cb6 > --- sys/dev/ic/qwxvar.h > +++ sys/dev/ic/qwxvar.h > @@ -264,7 +264,9 @@ struct ath11k_hw_ops { > #if notyet > void (*tx_mesh_enable)(struct ath11k_base *ab, > struct hal_tcl_data_cmd *tcl_cmd); > - bool (*rx_desc_get_first_msdu)(struct hal_rx_desc *desc); > +#endif > + int (*rx_desc_get_first_msdu)(struct hal_rx_desc *desc); > +#if notyet > bool (*rx_desc_get_last_msdu)(struct hal_rx_desc *desc); > #endif > uint8_t (*rx_desc_get_l3_pad_bytes)(struct hal_rx_desc *desc); > @@ -1745,6 +1747,14 @@ struct qwx_tx_radiotap_header { > > #define IWX_TX_RADIOTAP_PRESENT 0 /* TODO add more information */ > > +struct qwx_setkey_task_arg { > + struct ieee80211_node *ni; > + struct ieee80211_key *k; > + int cmd; > +#define QWX_ADD_KEY 1 > +#define QWX_DEL_KEY 2 > +}; > + > struct qwx_softc { > struct device sc_dev; > struct ieee80211com sc_ic; > @@ -1762,6 +1772,23 @@ struct qwx_softc { > enum ieee80211_state ns_nstate; > int ns_arg; > > + /* Task for setting encryption keys and its arguments. */ > + struct task setkey_task; > + /* > + * At present we need to process at most two keys at once: > + * Our pairwise key and a group key. > + * When hostap mode is implemented this array needs to grow or > + * it might become a bottleneck for associations that occur at > + * roughly the same time. > + */ > + struct qwx_setkey_task_arg setkey_arg[2]; > + int setkey_cur; > + int setkey_tail; > + int setkey_nkeys; > + > + int install_key_done; > + int install_key_status; > + > enum ath11k_11d_state state_11d; > int completed_11d_scan; > uint32_t vdev_id_11d_scan; > @@ -1962,9 +1989,16 @@ struct ath11k_peer { > struct qwx_node { > struct ieee80211_node ni; > struct ath11k_peer peer; > + unsigned int flags; > +#define QWX_NODE_FLAG_HAVE_PAIRWISE_KEY 0x01 > +#define QWX_NODE_FLAG_HAVE_GROUP_KEY 0x02 > }; > > struct ieee80211_node *qwx_node_alloc(struct ieee80211com *); > +int qwx_set_key(struct ieee80211com *, struct ieee80211_node *, > + struct ieee80211_key *); > +void qwx_delete_key(struct ieee80211com *, struct ieee80211_node *, > + struct ieee80211_key *); > > void qwx_qrtr_recv_msg(struct qwx_softc *, struct mbuf *); > > blob - 6e27e346cb3b08950b568e86ffcc6a35a4fc2970 > blob + b6b3d73b5ef65491fda360aebad5a260a9ebf3a1 > --- sys/dev/pci/if_qwx_pci.c > +++ sys/dev/pci/if_qwx_pci.c > @@ -1089,6 +1089,8 @@ unsupported_wcn6855_soc: > /* Override 802.11 state transition machine. */ > sc->sc_newstate = ic->ic_newstate; > ic->ic_newstate = qwx_newstate; > + ic->ic_set_key = qwx_set_key; > + ic->ic_delete_key = qwx_delete_key; > #if 0 > ic->ic_updatechan = qwx_updatechan; > ic->ic_updateprot = qwx_updateprot;