aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/bnx2.c
diff options
context:
space:
mode:
authorMichael Chan <mchan@broadcom.com>2006-06-12 22:21:25 -0700
committerDavid S. Miller <davem@sunset.davemloft.net>2006-06-17 21:30:44 -0700
commitfba9fe911bb4213c3de1d142fe0ee127cd361a78 (patch)
tree2fc7a68e612644cc8ce3ff8e44f210e20f6964b4 /drivers/net/bnx2.c
parent160882722cb21cbe5cead55cf38a5e70fc3af63e (diff)
[BNX2]: Add firmware decompression
Add functions to decompress firmware before loading to the internal CPUs. Compressing the firmware reduces the driver size significantly. Added file name length sanity check in the gzip header to prevent going past the end of buffer [suggested by DaveM]. Signed-off-by: Michael Chan <mchan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/bnx2.c')
-rw-r--r--drivers/net/bnx2.c159
1 files changed, 147 insertions, 12 deletions
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 031455a8d1d..c6510e17551 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -49,6 +49,7 @@
#include <linux/crc32.h>
#include <linux/prefetch.h>
#include <linux/cache.h>
+#include <linux/zlib.h>
#include "bnx2.h"
#include "bnx2_fw.h"
@@ -2083,6 +2084,92 @@ bnx2_set_rx_mode(struct net_device *dev)
spin_unlock_bh(&bp->phy_lock);
}
+#define FW_BUF_SIZE 0x8000
+
+static int
+bnx2_gunzip_init(struct bnx2 *bp)
+{
+ if ((bp->gunzip_buf = vmalloc(FW_BUF_SIZE)) == NULL)
+ goto gunzip_nomem1;
+
+ if ((bp->strm = kmalloc(sizeof(*bp->strm), GFP_KERNEL)) == NULL)
+ goto gunzip_nomem2;
+
+ bp->strm->workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
+ if (bp->strm->workspace == NULL)
+ goto gunzip_nomem3;
+
+ return 0;
+
+gunzip_nomem3:
+ kfree(bp->strm);
+ bp->strm = NULL;
+
+gunzip_nomem2:
+ vfree(bp->gunzip_buf);
+ bp->gunzip_buf = NULL;
+
+gunzip_nomem1:
+ printk(KERN_ERR PFX "%s: Cannot allocate firmware buffer for "
+ "uncompression.\n", bp->dev->name);
+ return -ENOMEM;
+}
+
+static void
+bnx2_gunzip_end(struct bnx2 *bp)
+{
+ kfree(bp->strm->workspace);
+
+ kfree(bp->strm);
+ bp->strm = NULL;
+
+ if (bp->gunzip_buf) {
+ vfree(bp->gunzip_buf);
+ bp->gunzip_buf = NULL;
+ }
+}
+
+static int
+bnx2_gunzip(struct bnx2 *bp, u8 *zbuf, int len, void **outbuf, int *outlen)
+{
+ int n, rc;
+
+ /* check gzip header */
+ if ((zbuf[0] != 0x1f) || (zbuf[1] != 0x8b) || (zbuf[2] != Z_DEFLATED))
+ return -EINVAL;
+
+ n = 10;
+
+#define FNAME 0x8
+ if (zbuf[3] & FNAME)
+ while ((zbuf[n++] != 0) && (n < len));
+
+ bp->strm->next_in = zbuf + n;
+ bp->strm->avail_in = len - n;
+ bp->strm->next_out = bp->gunzip_buf;
+ bp->strm->avail_out = FW_BUF_SIZE;
+
+ rc = zlib_inflateInit2(bp->strm, -MAX_WBITS);
+ if (rc != Z_OK)
+ return rc;
+
+ rc = zlib_inflate(bp->strm, Z_FINISH);
+
+ *outlen = FW_BUF_SIZE - bp->strm->avail_out;
+ *outbuf = bp->gunzip_buf;
+
+ if ((rc != Z_OK) && (rc != Z_STREAM_END))
+ printk(KERN_ERR PFX "%s: Firmware decompression error: %s\n",
+ bp->dev->name, bp->strm->msg);
+
+ zlib_inflateEnd(bp->strm);
+
+ if (rc == Z_STREAM_END)
+ return 0;
+
+ return rc;
+}
+
static void
load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len,
u32 rv2p_proc)
@@ -2092,9 +2179,9 @@ load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len,
for (i = 0; i < rv2p_code_len; i += 8) {
- REG_WR(bp, BNX2_RV2P_INSTR_HIGH, *rv2p_code);
+ REG_WR(bp, BNX2_RV2P_INSTR_HIGH, cpu_to_le32(*rv2p_code));
rv2p_code++;
- REG_WR(bp, BNX2_RV2P_INSTR_LOW, *rv2p_code);
+ REG_WR(bp, BNX2_RV2P_INSTR_LOW, cpu_to_le32(*rv2p_code));
rv2p_code++;
if (rv2p_proc == RV2P_PROC1) {
@@ -2134,7 +2221,7 @@ load_cpu_fw(struct bnx2 *bp, struct cpu_reg *cpu_reg, struct fw_info *fw)
int j;
for (j = 0; j < (fw->text_len / 4); j++, offset += 4) {
- REG_WR_IND(bp, offset, fw->text[j]);
+ REG_WR_IND(bp, offset, cpu_to_le32(fw->text[j]));
}
}
@@ -2190,15 +2277,32 @@ load_cpu_fw(struct bnx2 *bp, struct cpu_reg *cpu_reg, struct fw_info *fw)
REG_WR_IND(bp, cpu_reg->mode, val);
}
-static void
+static int
bnx2_init_cpus(struct bnx2 *bp)
{
struct cpu_reg cpu_reg;
struct fw_info fw;
+ int rc = 0;
+ void *text;
+ u32 text_len;
+
+ if ((rc = bnx2_gunzip_init(bp)) != 0)
+ return rc;
/* Initialize the RV2P processor. */
- load_rv2p_fw(bp, bnx2_rv2p_proc1, sizeof(bnx2_rv2p_proc1), RV2P_PROC1);
- load_rv2p_fw(bp, bnx2_rv2p_proc2, sizeof(bnx2_rv2p_proc2), RV2P_PROC2);
+ rc = bnx2_gunzip(bp, bnx2_rv2p_proc1, sizeof(bnx2_rv2p_proc1), &text,
+ &text_len);
+ if (rc)
+ goto init_cpu_err;
+
+ load_rv2p_fw(bp, text, text_len, RV2P_PROC1);
+
+ rc = bnx2_gunzip(bp, bnx2_rv2p_proc2, sizeof(bnx2_rv2p_proc2), &text,
+ &text_len);
+ if (rc)
+ goto init_cpu_err;
+
+ load_rv2p_fw(bp, text, text_len, RV2P_PROC2);
/* Initialize the RX Processor. */
cpu_reg.mode = BNX2_RXP_CPU_MODE;
@@ -2222,7 +2326,13 @@ bnx2_init_cpus(struct bnx2 *bp)
fw.text_addr = bnx2_RXP_b06FwTextAddr;
fw.text_len = bnx2_RXP_b06FwTextLen;
fw.text_index = 0;
- fw.text = bnx2_RXP_b06FwText;
+
+ rc = bnx2_gunzip(bp, bnx2_RXP_b06FwText, sizeof(bnx2_RXP_b06FwText),
+ &text, &text_len);
+ if (rc)
+ goto init_cpu_err;
+
+ fw.text = text;
fw.data_addr = bnx2_RXP_b06FwDataAddr;
fw.data_len = bnx2_RXP_b06FwDataLen;
@@ -2268,7 +2378,13 @@ bnx2_init_cpus(struct bnx2 *bp)
fw.text_addr = bnx2_TXP_b06FwTextAddr;
fw.text_len = bnx2_TXP_b06FwTextLen;
fw.text_index = 0;
- fw.text = bnx2_TXP_b06FwText;
+
+ rc = bnx2_gunzip(bp, bnx2_TXP_b06FwText, sizeof(bnx2_TXP_b06FwText),
+ &text, &text_len);
+ if (rc)
+ goto init_cpu_err;
+
+ fw.text = text;
fw.data_addr = bnx2_TXP_b06FwDataAddr;
fw.data_len = bnx2_TXP_b06FwDataLen;
@@ -2314,7 +2430,13 @@ bnx2_init_cpus(struct bnx2 *bp)
fw.text_addr = bnx2_TPAT_b06FwTextAddr;
fw.text_len = bnx2_TPAT_b06FwTextLen;
fw.text_index = 0;
- fw.text = bnx2_TPAT_b06FwText;
+
+ rc = bnx2_gunzip(bp, bnx2_TPAT_b06FwText, sizeof(bnx2_TPAT_b06FwText),
+ &text, &text_len);
+ if (rc)
+ goto init_cpu_err;
+
+ fw.text = text;
fw.data_addr = bnx2_TPAT_b06FwDataAddr;
fw.data_len = bnx2_TPAT_b06FwDataLen;
@@ -2360,7 +2482,13 @@ bnx2_init_cpus(struct bnx2 *bp)
fw.text_addr = bnx2_COM_b06FwTextAddr;
fw.text_len = bnx2_COM_b06FwTextLen;
fw.text_index = 0;
- fw.text = bnx2_COM_b06FwText;
+
+ rc = bnx2_gunzip(bp, bnx2_COM_b06FwText, sizeof(bnx2_COM_b06FwText),
+ &text, &text_len);
+ if (rc)
+ goto init_cpu_err;
+
+ fw.text = text;
fw.data_addr = bnx2_COM_b06FwDataAddr;
fw.data_len = bnx2_COM_b06FwDataLen;
@@ -2384,6 +2512,9 @@ bnx2_init_cpus(struct bnx2 *bp)
load_cpu_fw(bp, &cpu_reg, &fw);
+init_cpu_err:
+ bnx2_gunzip_end(bp);
+ return rc;
}
static int
@@ -3256,7 +3387,9 @@ bnx2_init_chip(struct bnx2 *bp)
* context block must have already been enabled. */
bnx2_init_context(bp);
- bnx2_init_cpus(bp);
+ if ((rc = bnx2_init_cpus(bp)) != 0)
+ return rc;
+
bnx2_init_nvram(bp);
bnx2_set_mac_addr(bp);
@@ -3556,7 +3689,9 @@ bnx2_reset_nic(struct bnx2 *bp, u32 reset_code)
if (rc)
return rc;
- bnx2_init_chip(bp);
+ if ((rc = bnx2_init_chip(bp)) != 0)
+ return rc;
+
bnx2_init_tx_ring(bp);
bnx2_init_rx_ring(bp);
return 0;