Patchwork bnx2: Use request_firmware()

login
register
mail settings
Submitter Ben Hutchings
Date March 19, 2009, 10:29 p.m.
Message ID <1237501747.4126.10.camel@deadeye.i.decadent.org.uk>
Download mbox | patch
Permalink /patch/24709/
State Awaiting Upstream
Delegated to: David Miller
Headers show

Comments

Ben Hutchings - March 19, 2009, 10:29 p.m.
Based on a patch for Debian by Bastian Blank <waldi@debian.org>.

This patch contains the code changes for review, but should not be
applied.  The complete patch, which is about 1 MB in size and therefore
too large for the mailing list, is at:
<http://womble.decadent.org.uk/tmp/0001-bnx2-Use-request_firmware.patch>.

Ben.

---
Michael Chan - March 19, 2009, 11:04 p.m.
On Thu, 2009-03-19 at 15:29 -0700, Ben Hutchings wrote:
> Based on a patch for Debian by Bastian Blank <waldi@debian.org>.
> 
> This patch contains the code changes for review, but should not be
> applied.  The complete patch, which is about 1 MB in size and therefore
> too large for the mailing list, is at:
> <http://womble.decadent.org.uk/tmp/0001-bnx2-Use-request_firmware.patch>.
> 


Thanks Ben and Bastian.  I will review this and test it out.  I think I
will add some logic to check firmware versions to guarantee some level
of compatibility.



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bastian Blank - March 19, 2009, 11:25 p.m.
On Thu, Mar 19, 2009 at 04:04:44PM -0700, Michael Chan wrote:
> Thanks Ben and Bastian.  I will review this and test it out.  I think I
> will add some logic to check firmware versions to guarantee some level
> of compatibility.

The firmware version is coded into the filenames.

Bastian
David Miller - March 20, 2009, 10:50 p.m.
From: Bastian Blank <waldi@debian.org>
Date: Fri, 20 Mar 2009 00:25:41 +0100

> On Thu, Mar 19, 2009 at 04:04:44PM -0700, Michael Chan wrote:
> > Thanks Ben and Bastian.  I will review this and test it out.  I think I
> > will add some logic to check firmware versions to guarantee some level
> > of compatibility.
> 
> The firmware version is coded into the filenames.

Michael and co. please do not make such a big deal about
this.

This patch is perfectly fine as-is.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael Chan - March 23, 2009, 9:47 p.m.
On Fri, 2009-03-20 at 15:50 -0700, David Miller wrote:
> From: Bastian Blank <waldi@debian.org>
> Date: Fri, 20 Mar 2009 00:25:41 +0100
> 
> > On Thu, Mar 19, 2009 at 04:04:44PM -0700, Michael Chan wrote:
> > > Thanks Ben and Bastian.  I will review this and test it out.  I think I
> > > will add some logic to check firmware versions to guarantee some level
> > > of compatibility.
> > 
> > The firmware version is coded into the filenames.
> 
> Michael and co. please do not make such a big deal about
> this.
> 
> This patch is perfectly fine as-is.
> 

Yes, the patch looks good.  I just noticed one problem while reviewing.
The following logic to let the firmware know about the host CPU's page
size is missing and I'll need to add it back.  Otherwise it will only
work on CPUs with 4K page size.

+static int
+load_rv2p_fw(struct bnx2 *bp, u32 rv2p_proc,
+            const struct bnx2_fw_file_section *fw_section)
+{
+       u32 rv2p_code_len, file_offset;
+       __be32 *rv2p_code;
        int i;
        u32 val;
 
-       if (rv2p_proc == RV2P_PROC2 && CHIP_NUM(bp) == CHIP_NUM_5709) {
-               val = le32_to_cpu(rv2p_code[XI_RV2P_PROC2_MAX_BD_PAGE_LOC]);
-               val &= ~XI_RV2P_PROC2_BD_PAGE_SIZE_MSK;
-               val |= XI_RV2P_PROC2_BD_PAGE_SIZE;
-               rv2p_code[XI_RV2P_PROC2_MAX_BD_PAGE_LOC] = cpu_to_le32(val);
-       }
+       rv2p_code_len = be32_to_cpu(fw_section->len);
+       file_offset = be32_to_cpu(fw_section->offset);
+
+       rv2p_code = (__be32 *)(bp->firmware->data + file_offset);


I'd also like to add the version information to the bnx2_fw_file_entry
struct instead of embedding it in the file name.  It is more flexible
this way as each section can have different versions and can be updated
separately.  So it will look something like:

struct bnx2_fw_file_entry {
	__be32 start_addr;
	__be32 version;
	struct bnx2_fw_file_section text;
	struct bnx2_fw_file_section data;
	struct bnx2_fw_file_section sbss;
	struct bnx2_fw_file_section bss;
	struct bnx2_fw_file_section rodata;
};


I'll generate an incremental patch on top of this one for review.
Thanks.

 


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller - March 23, 2009, 10:02 p.m.
From: "Michael Chan" <mchan@broadcom.com>
Date: Mon, 23 Mar 2009 14:47:59 -0700

> Yes, the patch looks good.  I just noticed one problem while reviewing.
> The following logic to let the firmware know about the host CPU's page
> size is missing and I'll need to add it back.  Otherwise it will only
> work on CPUs with 4K page size.

Ok.

> I'd also like to add the version information to the bnx2_fw_file_entry
> struct instead of embedding it in the file name.  It is more flexible
> this way as each section can have different versions and can be updated
> separately.  So it will look something like:

Fair enough.

> I'll generate an incremental patch on top of this one for review.

Thanks.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bastian Blank - March 23, 2009, 10:29 p.m.
On Mon, Mar 23, 2009 at 02:47:59PM -0700, Michael Chan wrote:
> I'd also like to add the version information to the bnx2_fw_file_entry
> struct instead of embedding it in the file name.  It is more flexible
> this way as each section can have different versions and can be updated
> separately.  So it will look something like:
> 
> struct bnx2_fw_file_entry {
> 	__be32 start_addr;
> 	__be32 version;
> 	struct bnx2_fw_file_section text;
> 	struct bnx2_fw_file_section data;
> 	struct bnx2_fw_file_section sbss;
> 	struct bnx2_fw_file_section bss;
> 	struct bnx2_fw_file_section rodata;
> };

And how do you want to handle the compatibility? One firmware file have
to work with many kernels, which was always described as impossible.

Bastian
Bastian Blank - March 23, 2009, 10:32 p.m.
On Mon, Mar 23, 2009 at 02:47:59PM -0700, Michael Chan wrote:
> I'd also like to add the version information to the bnx2_fw_file_entry
> struct instead of embedding it in the file name.  It is more flexible
> this way as each section can have different versions and can be updated
> separately.

Only if the firmwares are compatible enough. Can I pull the firmwares from a
2.6.21 kernel and ask a .29 to use them? One argument was always that
the firmware is coupled with the code.

Bastian
Michael Chan - March 23, 2009, 11:24 p.m.
On Mon, 2009-03-23 at 15:29 -0700, Bastian Blank wrote:
> On Mon, Mar 23, 2009 at 02:47:59PM -0700, Michael Chan wrote:
> > I'd also like to add the version information to the bnx2_fw_file_entry
> > struct instead of embedding it in the file name.  It is more flexible
> > this way as each section can have different versions and can be updated
> > separately.  So it will look something like:
> > 
> > struct bnx2_fw_file_entry {
> > 	__be32 start_addr;
> > 	__be32 version;
> > 	struct bnx2_fw_file_section text;
> > 	struct bnx2_fw_file_section data;
> > 	struct bnx2_fw_file_section sbss;
> > 	struct bnx2_fw_file_section bss;
> > 	struct bnx2_fw_file_section rodata;
> > };
> 
> And how do you want to handle the compatibility? One firmware file have
> to work with many kernels, which was always described as impossible.
> 

I see your point.  May be I'll break up each firmware section into a
different file and the file name will be updated with each version.
This will allow different sections to be updated separately and older
kernels will still have access to the older firmware.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael Chan - March 23, 2009, 11:28 p.m.
On Mon, 2009-03-23 at 15:32 -0700, Bastian Blank wrote:
> On Mon, Mar 23, 2009 at 02:47:59PM -0700, Michael Chan wrote:
> > I'd also like to add the version information to the
> bnx2_fw_file_entry
> > struct instead of embedding it in the file name.  It is more
> flexible
> > this way as each section can have different versions and can be
> updated
> > separately.
> 
> Only if the firmwares are compatible enough. Can I pull the firmwares
> from a
> 2.6.21 kernel and ask a .29 to use them? One argument was always that
> the firmware is coupled with the code.

No, this won't work.  Please see new proposal to breakup firmware into
multiple files.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rick Jones - March 24, 2009, 12:14 a.m.
Michael Chan wrote:
> May be I'll break up each firmware section into a
> different file and the file name will be updated with each version.
> This will allow different sections to be updated separately and older
> kernels will still have access to the older firmware.

Is that really necessary?  It is enough "fun" finding just the one firmware file 
as it is.

rick jones
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael Chan - March 24, 2009, 1:11 a.m.
On Mon, 2009-03-23 at 17:14 -0700, Rick Jones wrote:
> Michael Chan wrote:
> > May be I'll break up each firmware section into a
> > different file and the file name will be updated with each version.
> > This will allow different sections to be updated separately and older
> > kernels will still have access to the older firmware.
> 
> Is that really necessary?  It is enough "fun" finding just the one firmware file 
> as it is.

If all the firmware sections are in the same file, much of the firmware
file will be duplicated in a new file when we update just one section.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rick Jones - March 24, 2009, 1:26 a.m.
Michael Chan wrote:
> On Mon, 2009-03-23 at 17:14 -0700, Rick Jones wrote:
> 
>>Michael Chan wrote:
>>
>>>May be I'll break up each firmware section into a
>>>different file and the file name will be updated with each version.
>>>This will allow different sections to be updated separately and older
>>>kernels will still have access to the older firmware.
>>
>>Is that really necessary?  It is enough "fun" finding just the one firmware file 
>>as it is.
> 
> 
> If all the firmware sections are in the same file, much of the firmware
> file will be duplicated in a new file when we update just one section.

So?  Perhaps I'm just experiencing distro pain which may not continue to exist or 
which may not matter to netdev, but when one is installing to a system that uses 
a core NIC which has firmware cast-out into "non-free" siberia, life is "fun" 
enough making sure one has the one firmware file let alone N of them.  If I now 
have to make sure I have all N firmware files, and they are to be updated 
separately, either that means I have to find N packages, or the distros are going 
to package them into one "uber" package that might as well be a single firmware 
file anyway.

rick jones

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael Chan - March 24, 2009, 1:44 a.m.
On Mon, 2009-03-23 at 18:26 -0700, Rick Jones wrote:
> Michael Chan wrote:
> > On Mon, 2009-03-23 at 17:14 -0700, Rick Jones wrote:
> > 
> >>Michael Chan wrote:
> >>
> >>>May be I'll break up each firmware section into a
> >>>different file and the file name will be updated with each version.
> >>>This will allow different sections to be updated separately and older
> >>>kernels will still have access to the older firmware.
> >>
> >>Is that really necessary?  It is enough "fun" finding just the one firmware file 
> >>as it is.
> > 
> > 
> > If all the firmware sections are in the same file, much of the firmware
> > file will be duplicated in a new file when we update just one section.
> 
> So?  Perhaps I'm just experiencing distro pain which may not continue to exist or 
> which may not matter to netdev, but when one is installing to a system that uses 
> a core NIC which has firmware cast-out into "non-free" siberia, life is "fun" 
> enough making sure one has the one firmware file let alone N of them.  If I now 
> have to make sure I have all N firmware files, and they are to be updated 
> separately, either that means I have to find N packages, or the distros are going 
> to package them into one "uber" package that might as well be a single firmware 
> file anyway.
> 

I think we can assume that distros will package firmware files (make
firmware_install) properly and not require users to install these
firmware files on their own.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings - March 24, 2009, 4:27 a.m.
On Mon, 2009-03-23 at 18:26 -0700, Rick Jones wrote:
> Michael Chan wrote:
> > On Mon, 2009-03-23 at 17:14 -0700, Rick Jones wrote:
> > 
> >>Michael Chan wrote:
> >>
> >>>May be I'll break up each firmware section into a
> >>>different file and the file name will be updated with each version.
> >>>This will allow different sections to be updated separately and older
> >>>kernels will still have access to the older firmware.
> >>
> >>Is that really necessary?  It is enough "fun" finding just the one firmware file 
> >>as it is.
> > 
> > 
> > If all the firmware sections are in the same file, much of the firmware
> > file will be duplicated in a new file when we update just one section.
> 
> So?  Perhaps I'm just experiencing distro pain which may not continue to exist or 
> which may not matter to netdev, but when one is installing to a system that uses 
> a core NIC which has firmware cast-out into "non-free" siberia, life is "fun" 
> enough making sure one has the one firmware file let alone N of them.
[...]

If you're referring to Debian, that is exactly what Bastian and I are
working on.  Sourceless firmware is considered non-free, but it can
still be packaged, as the bnx2 firmware already has been.  We've also
had some discussions with David Woodhouse about semi-automatically
packaging all of the firmware that's now in the firmware subdirectory
and is clearly licensed.

Ben.
Bastian Blank - March 24, 2009, 7:42 a.m.
On Mon, Mar 23, 2009 at 06:11:25PM -0700, Michael Chan wrote:
> On Mon, 2009-03-23 at 17:14 -0700, Rick Jones wrote:
> > Michael Chan wrote:
> > > May be I'll break up each firmware section into a
> > > different file and the file name will be updated with each version.
> > > This will allow different sections to be updated separately and older
> > > kernels will still have access to the older firmware.
> > Is that really necessary?  It is enough "fun" finding just the one firmware file 
> > as it is.
> If all the firmware sections are in the same file, much of the firmware
> file will be duplicated in a new file when we update just one section.

What is the advantage of this? I always saw updates to more than one
firmware at once in the past. The question is not: is it possible, but:
it is necessary.

Bastian
Michael Chan - March 24, 2009, 3:27 p.m.
Bastian Blank wrote:

> What is the advantage of this? I always saw updates to more than one
> firmware at once in the past. The question is not: is it
> possible, but:
> it is necessary.
>

Looking at the git history, about 3 out of 7 times, bnx2_fw.h was
partially updated.  About 2 out of 8 times bnx2_fw2.h was partially
updated.  The next planned update for net-next is a partial update.

Either way will work.  Using separate files will reduce the amount
of patch churn during partial updates.  That's the advantage.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 45403e6..0769f09 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2206,6 +2206,7 @@  config BNX2
 	tristate "Broadcom NetXtremeII support"
 	depends on PCI
 	select CRC32
+	select FW_LOADER
 	select ZLIB_INFLATE
 	help
 	  This driver supports Broadcom NetXtremeII gigabit Ethernet cards.
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 8466d35..54e9c06 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -46,12 +46,11 @@ 
 #include <linux/crc32.h>
 #include <linux/prefetch.h>
 #include <linux/cache.h>
-#include <linux/zlib.h>
+#include <linux/firmware.h>
 #include <linux/log2.h>
 
 #include "bnx2.h"
 #include "bnx2_fw.h"
-#include "bnx2_fw2.h"
 
 #define FW_BUF_SIZE		0x10000
 
@@ -59,6 +58,8 @@ 
 #define PFX DRV_MODULE_NAME	": "
 #define DRV_MODULE_VERSION	"1.9.2"
 #define DRV_MODULE_RELDATE	"Feb 11, 2009"
+#define FW_FILE_06		"bnx2-06-4.6.16.fw"
+#define FW_FILE_09		"bnx2-09-4.6.15.fw"
 
 #define RUN_AT(x) (jiffies + (x))
 
@@ -72,6 +73,8 @@  MODULE_AUTHOR("Michael Chan <mchan@broadcom.com>");
 MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/5716 Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_MODULE_VERSION);
+MODULE_FIRMWARE(FW_FILE_06);
+MODULE_FIRMWARE(FW_FILE_09);
 
 static int disable_msi = 0;
 
@@ -3391,24 +3394,91 @@  bnx2_set_rx_mode(struct net_device *dev)
 	spin_unlock_bh(&bp->phy_lock);
 }
 
-static void
-load_rv2p_fw(struct bnx2 *bp, __le32 *rv2p_code, u32 rv2p_code_len,
-	u32 rv2p_proc)
+static int
+check_fw_section(const struct firmware *fw,
+		 const struct bnx2_fw_file_section *section,
+		 u32 alignment, bool non_empty)
+{
+	u32 offset = be32_to_cpu(section->offset);
+	u32 len = be32_to_cpu(section->len);
+
+	if ((offset == 0 && len != 0) || offset >= fw->size || offset & 3)
+		return -EINVAL;
+	if ((non_empty && len == 0) || len > fw->size - offset ||
+	    len & (alignment - 1))
+		return -EINVAL;
+	return 0;
+}
+
+static int
+check_fw_entry(const struct firmware *fw,
+	       const struct bnx2_fw_file_entry *entry)
+{
+	if (check_fw_section(fw, &entry->text, 4, false) ||
+	    check_fw_section(fw, &entry->data, 4, false) ||
+	    entry->bss.len & 3 ||
+	    entry->sbss.len & 3 ||
+	    check_fw_section(fw, &entry->rodata, 4, false))
+		return -EINVAL;
+	return 0;
+}
+
+static int
+bnx2_request_firmware(struct bnx2 *bp)
 {
+	const struct bnx2_fw_file *fw;
+	const char *fw_file;
+	int rc;
+
+	if (CHIP_NUM(bp) == CHIP_NUM_5709)
+		fw_file = FW_FILE_09;
+	else
+		fw_file = FW_FILE_06;
+
+	rc = request_firmware(&bp->firmware, fw_file, &bp->pdev->dev);
+	if (rc) {
+		printk(KERN_ERR PFX "Can't load firmware file \"%s\"\n",
+		       fw_file);
+		return rc;
+	}
+
+	fw = (const struct bnx2_fw_file *)bp->firmware->data;
+	if (bp->firmware->size < sizeof(*fw) ||
+	    check_fw_entry(bp->firmware, &fw->com) ||
+	    check_fw_entry(bp->firmware, &fw->cp) ||
+	    check_fw_entry(bp->firmware, &fw->rxp) ||
+	    check_fw_entry(bp->firmware, &fw->tpat) ||
+	    check_fw_entry(bp->firmware, &fw->txp) ||
+	    check_fw_section(bp->firmware, &fw->rv2p_proc1, 8, true) ||
+	    check_fw_section(bp->firmware, &fw->rv2p_proc2, 8, true)) {
+		printk(KERN_ERR PFX "Firmware file \"%s\" is invalid\n",
+		       fw_file);
+		release_firmware(bp->firmware);
+		bp->firmware = NULL;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+load_rv2p_fw(struct bnx2 *bp, u32 rv2p_proc,
+	     const struct bnx2_fw_file_section *fw_section)
+{
+	u32 rv2p_code_len, file_offset;
+	__be32 *rv2p_code;
 	int i;
 	u32 val;
 
-	if (rv2p_proc == RV2P_PROC2 && CHIP_NUM(bp) == CHIP_NUM_5709) {
-		val = le32_to_cpu(rv2p_code[XI_RV2P_PROC2_MAX_BD_PAGE_LOC]);
-		val &= ~XI_RV2P_PROC2_BD_PAGE_SIZE_MSK;
-		val |= XI_RV2P_PROC2_BD_PAGE_SIZE;
-		rv2p_code[XI_RV2P_PROC2_MAX_BD_PAGE_LOC] = cpu_to_le32(val);
-	}
+	rv2p_code_len = be32_to_cpu(fw_section->len);
+	file_offset = be32_to_cpu(fw_section->offset);
+
+	rv2p_code = (__be32 *)(bp->firmware->data + file_offset);
 
 	for (i = 0; i < rv2p_code_len; i += 8) {
-		REG_WR(bp, BNX2_RV2P_INSTR_HIGH, le32_to_cpu(*rv2p_code));
+		REG_WR(bp, BNX2_RV2P_INSTR_HIGH, be32_to_cpu(*rv2p_code));
 		rv2p_code++;
-		REG_WR(bp, BNX2_RV2P_INSTR_LOW, le32_to_cpu(*rv2p_code));
+		REG_WR(bp, BNX2_RV2P_INSTR_LOW, be32_to_cpu(*rv2p_code));
 		rv2p_code++;
 
 		if (rv2p_proc == RV2P_PROC1) {
@@ -3428,14 +3498,18 @@  load_rv2p_fw(struct bnx2 *bp, __le32 *rv2p_code, u32 rv2p_code_len,
 	else {
 		REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC2_RESET);
 	}
+
+	return 0;
 }
 
 static int
-load_cpu_fw(struct bnx2 *bp, const struct cpu_reg *cpu_reg, struct fw_info *fw)
+load_cpu_fw(struct bnx2 *bp, const struct cpu_reg *cpu_reg,
+	    const struct bnx2_fw_file_entry *fw_entry)
 {
+	u32 addr, len, file_offset;
+	__be32 *data;
 	u32 offset;
 	u32 val;
-	int rc;
 
 	/* Halt the CPU. */
 	val = bnx2_reg_rd_ind(bp, cpu_reg->mode);
@@ -3444,64 +3518,81 @@  load_cpu_fw(struct bnx2 *bp, const struct cpu_reg *cpu_reg, struct fw_info *fw)
 	bnx2_reg_wr_ind(bp, cpu_reg->state, cpu_reg->state_value_clear);
 
 	/* Load the Text area. */
-	offset = cpu_reg->spad_base + (fw->text_addr - cpu_reg->mips_view_base);
-	if (fw->gz_text) {
-		int j;
+	addr = be32_to_cpu(fw_entry->text.addr);
+	len = be32_to_cpu(fw_entry->text.len);
+	file_offset = be32_to_cpu(fw_entry->text.offset);
+	data = (__be32 *)(bp->firmware->data + file_offset);
 
-		rc = zlib_inflate_blob(fw->text, FW_BUF_SIZE, fw->gz_text,
-				       fw->gz_text_len);
-		if (rc < 0)
-			return rc;
+	offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base);
+	if (len) {
+		int j;
 
-		for (j = 0; j < (fw->text_len / 4); j++, offset += 4) {
-			bnx2_reg_wr_ind(bp, offset, le32_to_cpu(fw->text[j]));
+		for (j = 0; j < (len / 4); j++, offset += 4) {
+			bnx2_reg_wr_ind(bp, offset, be32_to_cpu(data[j]));
 	        }
 	}
 
 	/* Load the Data area. */
-	offset = cpu_reg->spad_base + (fw->data_addr - cpu_reg->mips_view_base);
-	if (fw->data) {
+	addr = be32_to_cpu(fw_entry->data.addr);
+	len = be32_to_cpu(fw_entry->data.len);
+	file_offset = be32_to_cpu(fw_entry->data.offset);
+	data = (__be32 *)(bp->firmware->data + file_offset);
+
+	offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base);
+	if (len) {
 		int j;
 
-		for (j = 0; j < (fw->data_len / 4); j++, offset += 4) {
-			bnx2_reg_wr_ind(bp, offset, fw->data[j]);
+		for (j = 0; j < (len / 4); j++, offset += 4) {
+			bnx2_reg_wr_ind(bp, offset, be32_to_cpu(data[j]));
 		}
 	}
 
 	/* Load the SBSS area. */
-	offset = cpu_reg->spad_base + (fw->sbss_addr - cpu_reg->mips_view_base);
-	if (fw->sbss_len) {
+	addr = be32_to_cpu(fw_entry->sbss.addr);
+	len = be32_to_cpu(fw_entry->sbss.len);
+
+	offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base);
+	if (len) {
 		int j;
 
-		for (j = 0; j < (fw->sbss_len / 4); j++, offset += 4) {
+		for (j = 0; j < (len / 4); j++, offset += 4) {
 			bnx2_reg_wr_ind(bp, offset, 0);
 		}
 	}
 
 	/* Load the BSS area. */
-	offset = cpu_reg->spad_base + (fw->bss_addr - cpu_reg->mips_view_base);
-	if (fw->bss_len) {
+	addr = be32_to_cpu(fw_entry->bss.addr);
+	len = be32_to_cpu(fw_entry->bss.len);
+
+	offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base);
+	if (len) {
 		int j;
 
-		for (j = 0; j < (fw->bss_len/4); j++, offset += 4) {
+		for (j = 0; j < (len / 4); j++, offset += 4) {
 			bnx2_reg_wr_ind(bp, offset, 0);
 		}
 	}
 
 	/* Load the Read-Only area. */
-	offset = cpu_reg->spad_base +
-		(fw->rodata_addr - cpu_reg->mips_view_base);
-	if (fw->rodata) {
+	addr = be32_to_cpu(fw_entry->rodata.addr);
+	len = be32_to_cpu(fw_entry->rodata.len);
+	file_offset = be32_to_cpu(fw_entry->rodata.offset);
+	data = (__be32 *)(bp->firmware->data + file_offset);
+
+	offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base);
+	if (len) {
 		int j;
 
-		for (j = 0; j < (fw->rodata_len / 4); j++, offset += 4) {
-			bnx2_reg_wr_ind(bp, offset, fw->rodata[j]);
+		for (j = 0; j < (len / 4); j++, offset += 4) {
+			bnx2_reg_wr_ind(bp, offset, be32_to_cpu(data[j]));
 		}
 	}
 
 	/* Clear the pre-fetch instruction. */
 	bnx2_reg_wr_ind(bp, cpu_reg->inst, 0);
-	bnx2_reg_wr_ind(bp, cpu_reg->pc, fw->start_addr);
+
+	val = be32_to_cpu(fw_entry->start_addr);
+	bnx2_reg_wr_ind(bp, cpu_reg->pc, val);
 
 	/* Start the CPU. */
 	val = bnx2_reg_rd_ind(bp, cpu_reg->mode);
@@ -3515,95 +3606,38 @@  load_cpu_fw(struct bnx2 *bp, const struct cpu_reg *cpu_reg, struct fw_info *fw)
 static int
 bnx2_init_cpus(struct bnx2 *bp)
 {
-	struct fw_info *fw;
-	int rc, rv2p_len;
-	void *text, *rv2p;
+	const struct bnx2_fw_file *fw =
+		(const struct bnx2_fw_file *)bp->firmware->data;
+	int rc;
 
 	/* Initialize the RV2P processor. */
-	text = vmalloc(FW_BUF_SIZE);
-	if (!text)
-		return -ENOMEM;
-	if (CHIP_NUM(bp) == CHIP_NUM_5709) {
-		rv2p = bnx2_xi_rv2p_proc1;
-		rv2p_len = sizeof(bnx2_xi_rv2p_proc1);
-	} else {
-		rv2p = bnx2_rv2p_proc1;
-		rv2p_len = sizeof(bnx2_rv2p_proc1);
-	}
-	rc = zlib_inflate_blob(text, FW_BUF_SIZE, rv2p, rv2p_len);
-	if (rc < 0)
-		goto init_cpu_err;
-
-	load_rv2p_fw(bp, text, rc /* == len */, RV2P_PROC1);
-
-	if (CHIP_NUM(bp) == CHIP_NUM_5709) {
-		rv2p = bnx2_xi_rv2p_proc2;
-		rv2p_len = sizeof(bnx2_xi_rv2p_proc2);
-	} else {
-		rv2p = bnx2_rv2p_proc2;
-		rv2p_len = sizeof(bnx2_rv2p_proc2);
-	}
-	rc = zlib_inflate_blob(text, FW_BUF_SIZE, rv2p, rv2p_len);
-	if (rc < 0)
-		goto init_cpu_err;
-
-	load_rv2p_fw(bp, text, rc /* == len */, RV2P_PROC2);
+	load_rv2p_fw(bp, RV2P_PROC1, &fw->rv2p_proc1);
+	load_rv2p_fw(bp, RV2P_PROC2, &fw->rv2p_proc2);
 
 	/* Initialize the RX Processor. */
-	if (CHIP_NUM(bp) == CHIP_NUM_5709)
-		fw = &bnx2_rxp_fw_09;
-	else
-		fw = &bnx2_rxp_fw_06;
-
-	fw->text = text;
-	rc = load_cpu_fw(bp, &cpu_reg_rxp, fw);
+	rc = load_cpu_fw(bp, &cpu_reg_rxp, &fw->rxp);
 	if (rc)
 		goto init_cpu_err;
 
 	/* Initialize the TX Processor. */
-	if (CHIP_NUM(bp) == CHIP_NUM_5709)
-		fw = &bnx2_txp_fw_09;
-	else
-		fw = &bnx2_txp_fw_06;
-
-	fw->text = text;
-	rc = load_cpu_fw(bp, &cpu_reg_txp, fw);
+	rc = load_cpu_fw(bp, &cpu_reg_txp, &fw->txp);
 	if (rc)
 		goto init_cpu_err;
 
 	/* Initialize the TX Patch-up Processor. */
-	if (CHIP_NUM(bp) == CHIP_NUM_5709)
-		fw = &bnx2_tpat_fw_09;
-	else
-		fw = &bnx2_tpat_fw_06;
-
-	fw->text = text;
-	rc = load_cpu_fw(bp, &cpu_reg_tpat, fw);
+	rc = load_cpu_fw(bp, &cpu_reg_tpat, &fw->tpat);
 	if (rc)
 		goto init_cpu_err;
 
 	/* Initialize the Completion Processor. */
-	if (CHIP_NUM(bp) == CHIP_NUM_5709)
-		fw = &bnx2_com_fw_09;
-	else
-		fw = &bnx2_com_fw_06;
-
-	fw->text = text;
-	rc = load_cpu_fw(bp, &cpu_reg_com, fw);
+	rc = load_cpu_fw(bp, &cpu_reg_com, &fw->com);
 	if (rc)
 		goto init_cpu_err;
 
 	/* Initialize the Command Processor. */
-	if (CHIP_NUM(bp) == CHIP_NUM_5709)
-		fw = &bnx2_cp_fw_09;
-	else
-		fw = &bnx2_cp_fw_06;
-
-	fw->text = text;
-	rc = load_cpu_fw(bp, &cpu_reg_cp, fw);
+	rc = load_cpu_fw(bp, &cpu_reg_cp, &fw->cp);
 
 init_cpu_err:
-	vfree(text);
 	return rc;
 }
 
@@ -7807,6 +7841,10 @@  bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	pci_set_drvdata(pdev, dev);
 
+	rc = bnx2_request_firmware(bp);
+	if (rc)
+		goto error;
+
 	memcpy(dev->dev_addr, bp->mac_addr, 6);
 	memcpy(dev->perm_addr, bp->mac_addr, 6);
 
@@ -7823,13 +7861,7 @@  bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	if ((rc = register_netdev(dev))) {
 		dev_err(&pdev->dev, "Cannot register net device\n");
-		if (bp->regview)
-			iounmap(bp->regview);
-		pci_release_regions(pdev);
-		pci_disable_device(pdev);
-		pci_set_drvdata(pdev, NULL);
-		free_netdev(dev);
-		return rc;
+		goto error;
 	}
 
 	printk(KERN_INFO "%s: %s (%c%d) %s found at mem %lx, "
@@ -7843,6 +7875,15 @@  bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 		bp->pdev->irq, dev->dev_addr);
 
 	return 0;
+
+error:
+	if (bp->regview)
+		iounmap(bp->regview);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	free_netdev(dev);
+	return rc;
 }
 
 static void __devexit
@@ -7855,6 +7896,8 @@  bnx2_remove_one(struct pci_dev *pdev)
 
 	unregister_netdev(dev);
 
+	release_firmware(bp->firmware);
+
 	if (bp->regview)
 		iounmap(bp->regview);
 
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index 704cbbc..e630ef3 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -6885,6 +6885,7 @@  struct bnx2 {
 
 	u32			idle_chk_status_idx;
 
+	const struct firmware	*firmware;
 };
 
 #define REG_RD(bp, offset)					\
@@ -6915,42 +6916,29 @@  struct cpu_reg {
 	u32 mips_view_base;
 };
 
-struct fw_info {
-	const u32 ver_major;
-	const u32 ver_minor;
-	const u32 ver_fix;
-
-	const u32 start_addr;
-
-	/* Text section. */
-	const u32 text_addr;
-	const u32 text_len;
-	const u32 text_index;
-	__le32 *text;
-	u8 *gz_text;
-	const u32 gz_text_len;
-
-	/* Data section. */
-	const u32 data_addr;
-	const u32 data_len;
-	const u32 data_index;
-	const u32 *data;
-
-	/* SBSS section. */
-	const u32 sbss_addr;
-	const u32 sbss_len;
-	const u32 sbss_index;
-
-	/* BSS section. */
-	const u32 bss_addr;
-	const u32 bss_len;
-	const u32 bss_index;
-
-	/* Read-only section. */
-	const u32 rodata_addr;
-	const u32 rodata_len;
-	const u32 rodata_index;
-	const u32 *rodata;
+struct bnx2_fw_file_section {
+	__be32 addr;
+	__be32 len;
+	__be32 offset;
+};
+
+struct bnx2_fw_file_entry {
+	__be32 start_addr;
+	struct bnx2_fw_file_section text;
+	struct bnx2_fw_file_section data;
+	struct bnx2_fw_file_section sbss;
+	struct bnx2_fw_file_section bss;
+	struct bnx2_fw_file_section rodata;
+};
+
+struct bnx2_fw_file {
+	struct bnx2_fw_file_entry com;
+	struct bnx2_fw_file_entry cp;
+	struct bnx2_fw_file_entry rxp;
+	struct bnx2_fw_file_entry tpat;
+	struct bnx2_fw_file_entry txp;
+	struct bnx2_fw_file_section rv2p_proc1;
+	struct bnx2_fw_file_section rv2p_proc2;
 };
 
 #define RV2P_PROC1                              0
diff --git a/firmware/Makefile b/firmware/Makefile
index aa2e02d..3235d60 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -31,6 +31,7 @@  fw-shipped-$(CONFIG_ADAPTEC_STARFIRE) += adaptec/starfire_rx.bin \
 					 adaptec/starfire_tx.bin
 fw-shipped-$(CONFIG_ATARI_DSP56K) += dsp56k/bootstrap.bin
 fw-shipped-$(CONFIG_ATM_AMBASSADOR) += atmsar11.fw
+fw-shipped-$(CONFIG_BNX2) += bnx2-06-4.6.16.fw bnx2-09-4.6.15.fw
 fw-shipped-$(CONFIG_CASSINI) += sun/cassini.bin
 fw-shipped-$(CONFIG_COMPUTONE) += intelliport2.bin
 fw-shipped-$(CONFIG_CHELSIO_T3) += cxgb3/t3b_psram-1.1.0.bin \
diff --git a/firmware/WHENCE b/firmware/WHENCE
index ea4fc2e..f42e6ad 100644
--- a/firmware/WHENCE
+++ b/firmware/WHENCE
@@ -493,3 +493,21 @@  Licence:
 Found in hex form in kernel source.
 
 --------------------------------------------------------------------------
+
+Driver: BNX2 - Broadcom NetXtremeII
+
+File: bnx2-06-4.6.16.fw
+File: bnx2-09-4.6.15.fw
+
+Licence:
+
+ This file contains firmware data derived from proprietary unpublished
+ source code, Copyright (c) 2004, 2005, 2006, 2007 Broadcom Corporation.
+
+ Permission is hereby granted for the distribution of this firmware data
+ in hexadecimal or equivalent format, provided this copyright notice is
+ accompanying it.
+
+Found in hex form in kernel source.
+
+--------------------------------------------------------------------------