Skip to content

Commit

Permalink
[BNX2]: Add firmware decompression
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Chan authored and David S. Miller committed Jun 18, 2006
1 parent 1608827 commit fba9fe9
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 12 deletions.
2 changes: 2 additions & 0 deletions drivers/net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2163,6 +2163,8 @@ config TIGON3
config BNX2
tristate "Broadcom NetXtremeII support"
depends on PCI
select CRC32
select ZLIB_INFLATE
help
This driver supports Broadcom NetXtremeII gigabit Ethernet cards.

Expand Down
159 changes: 147 additions & 12 deletions drivers/net/bnx2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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]));
}
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/bnx2.h
Original file line number Diff line number Diff line change
Expand Up @@ -4051,6 +4051,9 @@ struct bnx2 {
u32 flash_size;

int status_stats_size;

struct z_stream_s *strm;
void *gunzip_buf;
};

static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset);
Expand Down

0 comments on commit fba9fe9

Please sign in to comment.