浏览代码

[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>
Michael Chan 19 年之前
父节点
当前提交
fba9fe911b
共有 3 个文件被更改,包括 152 次插入12 次删除
  1. 2 0
      drivers/net/Kconfig
  2. 147 12
      drivers/net/bnx2.c
  3. 3 0
      drivers/net/bnx2.h

+ 2 - 0
drivers/net/Kconfig

@@ -2163,6 +2163,8 @@ config TIGON3
 config BNX2
 config BNX2
 	tristate "Broadcom NetXtremeII support"
 	tristate "Broadcom NetXtremeII support"
 	depends on PCI
 	depends on PCI
+	select CRC32
+	select ZLIB_INFLATE
 	help
 	help
 	  This driver supports Broadcom NetXtremeII gigabit Ethernet cards.
 	  This driver supports Broadcom NetXtremeII gigabit Ethernet cards.
 
 

+ 147 - 12
drivers/net/bnx2.c

@@ -49,6 +49,7 @@
 #include <linux/crc32.h>
 #include <linux/crc32.h>
 #include <linux/prefetch.h>
 #include <linux/prefetch.h>
 #include <linux/cache.h>
 #include <linux/cache.h>
+#include <linux/zlib.h>
 
 
 #include "bnx2.h"
 #include "bnx2.h"
 #include "bnx2_fw.h"
 #include "bnx2_fw.h"
@@ -2083,6 +2084,92 @@ bnx2_set_rx_mode(struct net_device *dev)
 	spin_unlock_bh(&bp->phy_lock);
 	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
 static void
 load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len,
 load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len,
 	u32 rv2p_proc)
 	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) {
 	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++;
 		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++;
 		rv2p_code++;
 
 
 		if (rv2p_proc == RV2P_PROC1) {
 		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;
 		int j;
 
 
 		for (j = 0; j < (fw->text_len / 4); j++, offset += 4) {
 		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);
 	REG_WR_IND(bp, cpu_reg->mode, val);
 }
 }
 
 
-static void
+static int
 bnx2_init_cpus(struct bnx2 *bp)
 bnx2_init_cpus(struct bnx2 *bp)
 {
 {
 	struct cpu_reg cpu_reg;
 	struct cpu_reg cpu_reg;
 	struct fw_info fw;
 	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. */
 	/* 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. */
 	/* Initialize the RX Processor. */
 	cpu_reg.mode = BNX2_RXP_CPU_MODE;
 	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_addr = bnx2_RXP_b06FwTextAddr;
 	fw.text_len = bnx2_RXP_b06FwTextLen;
 	fw.text_len = bnx2_RXP_b06FwTextLen;
 	fw.text_index = 0;
 	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_addr = bnx2_RXP_b06FwDataAddr;
 	fw.data_len = bnx2_RXP_b06FwDataLen;
 	fw.data_len = bnx2_RXP_b06FwDataLen;
@@ -2268,7 +2378,13 @@ bnx2_init_cpus(struct bnx2 *bp)
 	fw.text_addr = bnx2_TXP_b06FwTextAddr;
 	fw.text_addr = bnx2_TXP_b06FwTextAddr;
 	fw.text_len = bnx2_TXP_b06FwTextLen;
 	fw.text_len = bnx2_TXP_b06FwTextLen;
 	fw.text_index = 0;
 	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_addr = bnx2_TXP_b06FwDataAddr;
 	fw.data_len = bnx2_TXP_b06FwDataLen;
 	fw.data_len = bnx2_TXP_b06FwDataLen;
@@ -2314,7 +2430,13 @@ bnx2_init_cpus(struct bnx2 *bp)
 	fw.text_addr = bnx2_TPAT_b06FwTextAddr;
 	fw.text_addr = bnx2_TPAT_b06FwTextAddr;
 	fw.text_len = bnx2_TPAT_b06FwTextLen;
 	fw.text_len = bnx2_TPAT_b06FwTextLen;
 	fw.text_index = 0;
 	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_addr = bnx2_TPAT_b06FwDataAddr;
 	fw.data_len = bnx2_TPAT_b06FwDataLen;
 	fw.data_len = bnx2_TPAT_b06FwDataLen;
@@ -2360,7 +2482,13 @@ bnx2_init_cpus(struct bnx2 *bp)
 	fw.text_addr = bnx2_COM_b06FwTextAddr;
 	fw.text_addr = bnx2_COM_b06FwTextAddr;
 	fw.text_len = bnx2_COM_b06FwTextLen;
 	fw.text_len = bnx2_COM_b06FwTextLen;
 	fw.text_index = 0;
 	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_addr = bnx2_COM_b06FwDataAddr;
 	fw.data_len = bnx2_COM_b06FwDataLen;
 	fw.data_len = bnx2_COM_b06FwDataLen;
@@ -2384,6 +2512,9 @@ bnx2_init_cpus(struct bnx2 *bp)
 
 
 	load_cpu_fw(bp, &cpu_reg, &fw);
 	load_cpu_fw(bp, &cpu_reg, &fw);
 
 
+init_cpu_err:
+	bnx2_gunzip_end(bp);
+	return rc;
 }
 }
 
 
 static int
 static int
@@ -3256,7 +3387,9 @@ bnx2_init_chip(struct bnx2 *bp)
 	 * context block must have already been enabled. */
 	 * context block must have already been enabled. */
 	bnx2_init_context(bp);
 	bnx2_init_context(bp);
 
 
-	bnx2_init_cpus(bp);
+	if ((rc = bnx2_init_cpus(bp)) != 0)
+		return rc;
+
 	bnx2_init_nvram(bp);
 	bnx2_init_nvram(bp);
 
 
 	bnx2_set_mac_addr(bp);
 	bnx2_set_mac_addr(bp);
@@ -3556,7 +3689,9 @@ bnx2_reset_nic(struct bnx2 *bp, u32 reset_code)
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
-	bnx2_init_chip(bp);
+	if ((rc = bnx2_init_chip(bp)) != 0)
+		return rc;
+
 	bnx2_init_tx_ring(bp);
 	bnx2_init_tx_ring(bp);
 	bnx2_init_rx_ring(bp);
 	bnx2_init_rx_ring(bp);
 	return 0;
 	return 0;

+ 3 - 0
drivers/net/bnx2.h

@@ -4051,6 +4051,9 @@ struct bnx2 {
 	u32			flash_size;
 	u32			flash_size;
 
 
 	int			status_stats_size;
 	int			status_stats_size;
+
+	struct z_stream_s	*strm;
+	void			*gunzip_buf;
 };
 };
 
 
 static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset);
 static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset);