Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci Patch from Sverre Froyen to avoid panic when an ...



details:   https://anonhg.NetBSD.org/src/rev/731200e7daef
branches:  trunk
changeset: 755754:731200e7daef
user:      christos <christos%NetBSD.org@localhost>
date:      Fri Jun 18 21:10:23 2010 +0000

description:
Patch from Sverre Froyen to avoid panic when an ioctl happens while the
driver is initializing.

diffstat:

 sys/dev/pci/if_iwn.c    |  295 ++++++++++++++++++++++++++++++++++++-----------
 sys/dev/pci/if_iwnreg.h |   34 ++++-
 sys/dev/pci/if_iwnvar.h |    7 +-
 3 files changed, 256 insertions(+), 80 deletions(-)

diffs (truncated from 490 to 300 lines):

diff -r 7b978a9e4266 -r 731200e7daef sys/dev/pci/if_iwn.c
--- a/sys/dev/pci/if_iwn.c      Fri Jun 18 19:20:04 2010 +0000
+++ b/sys/dev/pci/if_iwn.c      Fri Jun 18 21:10:23 2010 +0000
@@ -1,5 +1,5 @@
-/*     $NetBSD: if_iwn.c,v 1.45 2010/05/12 12:26:16 christos Exp $     */
-/*     $OpenBSD: if_iwn.c,v 1.88 2010/04/10 08:37:36 damien Exp $      */
+/*     $NetBSD: if_iwn.c,v 1.46 2010/06/18 21:10:23 christos Exp $     */
+/*     $OpenBSD: if_iwn.c,v 1.96 2010/05/13 09:25:03 damien Exp $      */
 
 /*-
  * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini%free.fr@localhost>
@@ -22,7 +22,7 @@
  * adapters.
  */
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1.45 2010/05/12 12:26:16 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_iwn.c,v 1.46 2010/06/18 21:10:23 christos Exp $");
 
 #define IWN_USE_RBUF   /* Use local storage for RX */
 #undef IWN_HWCRYPTO    /* XXX does not even compile yet */
@@ -32,7 +32,7 @@
 
 #include <sys/param.h>
 #include <sys/sockio.h>
-#include <sys/sysctl.h>
+#include <sys/proc.h>
 #include <sys/mbuf.h>
 #include <sys/kernel.h>
 #include <sys/socket.h>
@@ -96,6 +96,19 @@
        PCI_PRODUCT_INTEL_WIFI_LINK_6050_2X2_2,
        PCI_PRODUCT_INTEL_WIFI_LINK_6005_2X2_1,
        PCI_PRODUCT_INTEL_WIFI_LINK_6005_2X2_2,
+#ifdef notyet
+       /*
+        * XXX NetBSD: the 6005A replaces the two 6005, above
+        * (see OpenBSD rev 1.96).
+        */
+       PCI_PRODUCT_INTEL_WIFI_LINK_6005A_2X2_1,
+       PCI_PRODUCT_INTEL_WIFI_LINK_6005A_2X2_2,
+       PCI_PRODUCT_INTEL_WIFI_LINK_6005B_1X1_1,
+       PCI_PRODUCT_INTEL_WIFI_LINK_6005B_1X1_2,
+       PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_1,
+       PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_2,
+       PCI_PRODUCT_INTEL_WIFI_LINK_6005B_2X2_3
+#endif
 };
 
 /*
@@ -263,6 +276,10 @@
 static int     iwn4965_load_firmware(struct iwn_softc *);
 static int     iwn5000_load_firmware_section(struct iwn_softc *, uint32_t,
                    const uint8_t *, int);
+static int     iwn_read_firmware_leg(struct iwn_softc *,
+                   struct iwn_fw_info *);
+static int     iwn_read_firmware_tlv(struct iwn_softc *,
+                   struct iwn_fw_info *, uint16_t);
 static int     iwn5000_load_firmware(struct iwn_softc *);
 static int     iwn_read_firmware(struct iwn_softc *);
 static int     iwn_clock_wait(struct iwn_softc *);
@@ -3144,6 +3161,22 @@
        int s, error = 0;
 
        s = splnet();
+       /*
+        * Prevent processes from entering this function while another
+        * process is tsleep'ing in it.
+        */
+       if (sc->sc_flags & IWN_FLAG_BUSY) {
+               switch (cmd) {
+               case SIOCSIFADDR:
+                       /* FALLTHROUGH */
+               case SIOCSIFFLAGS:
+                       splx(s);
+                       aprint_normal_dev(sc->sc_dev,
+                           "ioctl while busy cmd = 0x%lx\n", cmd);
+                       return EBUSY;
+               }
+       }
+       sc->sc_flags |= IWN_FLAG_BUSY;
 
        switch (cmd) {
        case SIOCSIFADDR:
@@ -3190,6 +3223,8 @@
                        error = iwn_init(ifp);
                }
        }
+
+       sc->sc_flags &= ~IWN_FLAG_BUSY;
        splx(s);
        return error;
 }
@@ -4296,7 +4331,7 @@
        tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
 
        if (flags & IEEE80211_CHAN_5GHZ) {
-               hdr->crc_threshold = htole16(1);
+               hdr->crc_threshold = 0xffff;
                /* Send probe requests at 6Mbps. */
                tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp;
                rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
@@ -4334,6 +4369,10 @@
        frm = (uint8_t *)(wh + 1);
        frm = ieee80211_add_ssid(frm, NULL, 0);
        frm = ieee80211_add_rates(frm, rs);
+#ifndef IEEE80211_NO_HT
+       if (ic->ic_flags & IEEE80211_F_HTON)
+               frm = ieee80211_add_htcaps(frm, ic);
+#endif
        if (rs->rs_nrates > IEEE80211_RATE_SIZE)
                frm = ieee80211_add_xrates(frm, rs);
 
@@ -5242,91 +5281,47 @@
        return 0;
 }
 
+/*
+ * Extract text and data sections from a legacy firmware image.
+ */
 static int
-iwn_read_firmware(struct iwn_softc *sc)
+iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw)
 {
-       const struct iwn_hal *hal = sc->sc_hal;
-       struct iwn_fw_info *fw = &sc->fw;
-       firmware_handle_t fwh;
        const uint32_t *ptr;
+       size_t hdrlen = 24;
        uint32_t rev;
-       size_t size;
-       int error;
-
-       /* Initialize for error returns */
-       fw->data = NULL;
-       fw->datasz = 0;
-
-       /* Open firmware image. */
-       if ((error = firmware_open("if_iwn", sc->fwname, &fwh)) != 0) {
-               aprint_error_dev(sc->sc_dev,
-                   "could not get firmware handle %s\n", sc->fwname);
-               return error;
-       }
-       size = firmware_get_size(fwh);
-       if (size < 28) {
-               aprint_error_dev(sc->sc_dev,
-                   "truncated firmware header: %zu bytes\n", size);
-               firmware_close(fwh);
-               return EINVAL;
-       }
-
-       /* Read the firmware. */
-       fw->data = firmware_malloc(size);
-       if (fw->data == NULL) {
-               aprint_error_dev(sc->sc_dev,
-                   "not enough memory to stock firmware %s\n", sc->fwname);
-               firmware_close(fwh);
-               return ENOMEM;
-       }
-       error = firmware_read(fwh, 0, fw->data, size);
-       firmware_close(fwh);
-       fw->datasz = size;
-       if (error != 0) {
-               aprint_error_dev(sc->sc_dev,
-                   "could not read firmware %s\n", sc->fwname);
-               goto out;
-       }
-
-       /* Process firmware header. */
+
        ptr = (const uint32_t *)fw->data;
        rev = le32toh(*ptr++);
+
        /* Check firmware API version. */
        if (IWN_FW_API(rev) <= 1) {
                aprint_error_dev(sc->sc_dev,
                    "bad firmware, need API version >=2\n");
-               goto out;
+               return EINVAL;
        }
        if (IWN_FW_API(rev) >= 3) {
                /* Skip build number (version 2 header). */
-               size -= 4;
+               hdrlen += 4;
                ptr++;
        }
+       if (fw->size < hdrlen) {
+               aprint_error_dev(sc->sc_dev,
+                   "firmware too short: %zd bytes\n", fw->size);
+               return EINVAL;
+       }
        fw->main.textsz = le32toh(*ptr++);
        fw->main.datasz = le32toh(*ptr++);
        fw->init.textsz = le32toh(*ptr++);
        fw->init.datasz = le32toh(*ptr++);
        fw->boot.textsz = le32toh(*ptr++);
-       size -= 24;
-
-       /* Sanity-check firmware header. */
-       if (fw->main.textsz > hal->fw_text_maxsz ||
-           fw->main.datasz > hal->fw_data_maxsz ||
-           fw->init.textsz > hal->fw_text_maxsz ||
-           fw->init.datasz > hal->fw_data_maxsz ||
-           fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ ||
-           (fw->boot.textsz & 3) != 0) {
-               aprint_error_dev(sc->sc_dev,
-                   "invalid firmware header\n");
-               goto out;
-       }
 
        /* Check that all firmware sections fit. */
-       if (fw->main.textsz + fw->main.datasz + fw->init.textsz +
-           fw->init.datasz + fw->boot.textsz > size) {
+       if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz +
+           fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
                aprint_error_dev(sc->sc_dev,
-                   "firmware file too short: %zu bytes\n", size);
-               goto out;
+                   "firmware too short: %zd bytes\n", fw->size);
+               return EINVAL;
        }
 
        /* Get pointers to firmware sections. */
@@ -5335,12 +5330,166 @@
        fw->init.text = fw->main.data + fw->main.datasz;
        fw->init.data = fw->init.text + fw->init.textsz;
        fw->boot.text = fw->init.data + fw->init.datasz;
-
+       return 0;
+}
+
+/*
+ * Extract text and data sections from a TLV firmware image.
+ */
+static int
+iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw,
+    uint16_t alt)
+{
+       const struct iwn_fw_tlv_hdr *hdr;
+       const struct iwn_fw_tlv *tlv;
+       const uint8_t *ptr, *end;
+       uint64_t altmask;
+       uint32_t len;
+
+       if (fw->size < sizeof (*hdr)) {
+               aprint_error_dev(sc->sc_dev,
+                   "firmware too short: %zd bytes\n", fw->size);
+               return EINVAL;
+       }
+       hdr = (const struct iwn_fw_tlv_hdr *)fw->data;
+       if (hdr->signature != htole32(IWN_FW_SIGNATURE)) {
+               aprint_error_dev(sc->sc_dev,
+                   "bad firmware signature 0x%08x\n", le32toh(hdr->signature));
+               return EINVAL;
+       }
+       DPRINTF(("FW: \"%.64s\", build 0x%x\n", hdr->descr,
+           le32toh(hdr->build)));
+
+       /*
+        * Select the closest supported alternative that is less than
+        * or equal to the specified one.
+        */
+       altmask = le64toh(hdr->altmask);
+       while (alt > 0 && !(altmask & (1ULL << alt)))
+               alt--;  /* Downgrade. */
+       DPRINTF(("using alternative %d\n", alt));
+
+       ptr = (const uint8_t *)(hdr + 1);
+       end = (const uint8_t *)(fw->data + fw->size);
+
+       /* Parse type-length-value fields. */
+       while (ptr + sizeof (*tlv) <= end) {
+               tlv = (const struct iwn_fw_tlv *)ptr;
+               len = le32toh(tlv->len);
+
+               ptr += sizeof (*tlv);
+               if (ptr + len > end) {
+                       aprint_error_dev(sc->sc_dev,
+                           "firmware too short: %zd bytes\n", fw->size);
+                       return EINVAL;
+               }
+               /* Skip other alternatives. */
+               if (tlv->alt != 0 && tlv->alt != htole16(alt))
+                       goto next;
+
+               switch (le16toh(tlv->type)) {
+               case IWN_FW_TLV_MAIN_TEXT:
+                       fw->main.text = ptr;
+                       fw->main.textsz = len;
+                       break;
+               case IWN_FW_TLV_MAIN_DATA:
+                       fw->main.data = ptr;
+                       fw->main.datasz = len;
+                       break;
+               case IWN_FW_TLV_INIT_TEXT:
+                       fw->init.text = ptr;
+                       fw->init.textsz = len;
+                       break;
+               case IWN_FW_TLV_INIT_DATA:
+                       fw->init.data = ptr;
+                       fw->init.datasz = len;



Home | Main Index | Thread Index | Old Index