diff mbox

[v2] net/r8169: update the new parser for the new firmware

Message ID 20110616225904.GA2064@electric-eye.fr.zoreil.com
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Francois Romieu June 16, 2011, 10:59 p.m. UTC
Hayes Wang <hayeswang@realtek.com> :
> Update the parser for the new firmware which is embedded some information.

I have modified several things :
- s/u32/__le32/ in fw_info
- fix unsigned (size_t) comparisons
- more size checks before dereferencing fw_info

The new firmware format should be the same. The old r8168d-1.fw firmware
proved usable when prefixed with :

0000000: 0000 0000 3031 0000 0000 0000 0000 0000  ....01..........
0000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000020: 0000 0000 3000 0000 7501 0000 a000 0000  ....0...u.......

I realized after testing that netif_err could be abused with non-string
fw_info.version. :o/

Comments ?

--
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

Comments

Hayes Wang June 17, 2011, 3:25 a.m. UTC | #1
> -----Original Message-----
> From: Francois Romieu [mailto:romieu@fr.zoreil.com] 
> Sent: Friday, June 17, 2011 6:59 AM
> To: Hayeswang
> Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v2] net/r8169: update the new parser for 
> the new firmware
> 
> Hayes Wang <hayeswang@realtek.com> :
> > Update the parser for the new firmware which is embedded 
> some information.
> 
> I have modified several things :
> - s/u32/__le32/ in fw_info
> - fix unsigned (size_t) comparisons
> - more size checks before dereferencing fw_info
> 

Thanks. They are fine.

> The new firmware format should be the same. The old 
> r8168d-1.fw firmware proved usable when prefixed with :
> 
> 0000000: 0000 0000 3031 0000 0000 0000 0000 0000  ....01..........
> 0000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000020: 0000 0000 3000 0000 7501 0000 a000 0000  ....0...u.......
> 
> I realized after testing that netif_err could be abused with 
> non-string fw_info.version. :o/
> 

Excuse me. I don't understand what you want to express. Do you mean the
situation of the old paser with the new firmware for checking the firmware? For
the normal situation, the old paser would not use the new firmware. And I put
zero in front of the new firmware to prevent the old paser from running it. That
is all I do.

If you don't mean that, I could promise the new firmware I release would contain
the valid string unless someone modifies it.

> Comments ?
> 
 
Best Regards,
Hayes

--
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
Francois Romieu June 17, 2011, 6:39 a.m. UTC | #2
hayeswang <hayeswang@realtek.com> :
> Francois Romieu :
[...]
> > The new firmware format should be the same. The old 
> > r8168d-1.fw firmware proved usable when prefixed with :
> > 
> > 0000000: 0000 0000 3031 0000 0000 0000 0000 0000  ....01..........
> > 0000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> > 0000020: 0000 0000 3000 0000 7501 0000 a000 0000  ....0...u.......
> > 
> > I realized after testing that netif_err could be abused with 
> > non-string fw_info.version. :o/
> > 
> 
> Excuse me. I don't understand what you want to express. Do you mean the
> situation of the old paser with the new firmware for checking the firmware?

No. The code does a printk with a %s specifier on fw_info.version while there
is no evidence that fw_info.version is 0 terminated.

> For the normal situation, the old paser would not use the new firmware.
> And I put zero in front of the new firmware to prevent the old paser from
> running it. That is all I do.

It is fine.
Ben Hutchings June 17, 2011, 12:22 p.m. UTC | #3
On Fri, 2011-06-17 at 08:39 +0200, Francois Romieu wrote:
> hayeswang <hayeswang@realtek.com> :
> > Francois Romieu :
> [...]
> > > The new firmware format should be the same. The old 
> > > r8168d-1.fw firmware proved usable when prefixed with :
> > > 
> > > 0000000: 0000 0000 3031 0000 0000 0000 0000 0000  ....01..........
> > > 0000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> > > 0000020: 0000 0000 3000 0000 7501 0000 a000 0000  ....0...u.......
> > > 
> > > I realized after testing that netif_err could be abused with 
> > > non-string fw_info.version. :o/
> > > 
> > 
> > Excuse me. I don't understand what you want to express. Do you mean the
> > situation of the old paser with the new firmware for checking the firmware?
> 
> No. The code does a printk with a %s specifier on fw_info.version while there
> is no evidence that fw_info.version is 0 terminated.

I thought the idea of embedding a version here was to be able to report
in in get_drvinfo, not to print it on load.

Ben.

> > For the normal situation, the old paser would not use the new firmware.
> > And I put zero in front of the new firmware to prevent the old paser from
> > running it. That is all I do.
> 
> It is fine.
>
Francois Romieu June 17, 2011, 12:53 p.m. UTC | #4
Ben Hutchings <bhutchings@solarflare.com> :
[...]
> I thought the idea of embedding a version here was to be able to report
> in in get_drvinfo, not to print it on load.

Reporting the version for the new firmware format through ethtool is
currently hacked on.

Do you want the load time version / format messages removed ?

I think they can be of some use - at least temporarily - but I won't mind
removing them now.
Ben Hutchings June 17, 2011, 2:50 p.m. UTC | #5
On Fri, 2011-06-17 at 14:53 +0200, Francois Romieu wrote:
> Ben Hutchings <bhutchings@solarflare.com> :
> [...]
> > I thought the idea of embedding a version here was to be able to report
> > in in get_drvinfo, not to print it on load.
> 
> Reporting the version for the new firmware format through ethtool is
> currently hacked on.
> 
> Do you want the load time version / format messages removed ?
>
> I think they can be of some use - at least temporarily - but I won't mind
> removing them now.

I don't care one way or the other.  But it's more important to report
the version through get_drvinfo.

Ben.
diff mbox

Patch

diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 7310824..5a0a7c3 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -1741,21 +1741,82 @@  static void rtl_writephy_batch(struct rtl8169_private *tp,
 #define PHY_DELAY_MS		0xe0000000
 #define PHY_WRITE_ERI_WORD	0xf0000000
 
+struct fw_info {
+	u32	magic;
+	char	version[32];
+	__le32	fw_start;
+	__le32	fw_len;
+	u8	chksum;
+} __packed;
+
+struct fw_phy_code {
+	__le32 *code;
+	size_t size;
+};
+
+#define FW_OPCODE_SIZE	sizeof(typeof(*((struct fw_phy_code *)0)->code))
+
+static bool rtl_fw_format_ok(struct rtl8169_private *tp, struct net_device *dev,
+			     const struct firmware *fw, struct fw_phy_code *pc)
+{
+	struct fw_info *fw_info = (struct fw_info *)fw->data;
+	bool rc = false;
+
+	if (fw->size < FW_OPCODE_SIZE)
+		goto out;
+
+	if (fw_info->magic == 0) {
+		size_t i, size, start;
+		u8 checksum = 0;
+
+		if (fw->size < sizeof(*fw_info))
+			goto out;
+
+		for (i = 0; i < fw->size; i++)
+			checksum += fw->data[i];
+		if (checksum != 0)
+			goto out;
+
+		start = le32_to_cpu(fw_info->fw_start);
+		if (start > fw->size)
+			goto out;
+
+		size = le32_to_cpu(fw_info->fw_len);
+		if (size > (fw->size - start) / FW_OPCODE_SIZE)
+			goto out;
+
+		netif_info(tp, probe, dev, "firmware: %s\n", fw_info->version);
+		pc->code = (__le32 *)(fw->data + start);
+		pc->size = size;
+	} else {
+		if (fw->size % FW_OPCODE_SIZE)
+			goto out;
+
+		netif_info(tp, probe, dev, "legacy firmware format detected\n");
+		pc->code = (__le32 *)fw->data;
+		pc->size = fw->size / FW_OPCODE_SIZE;
+	}
+
+	rc = true;
+out:
+	return rc;
+}
+
 static void
 rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
 {
-	__le32 *phytable = (__le32 *)fw->data;
 	struct net_device *dev = tp->dev;
-	size_t index, fw_size = fw->size / sizeof(*phytable);
+	struct fw_phy_code phytable;
 	u32 predata, count;
+	size_t i;
 
-	if (fw->size % sizeof(*phytable)) {
-		netif_err(tp, probe, dev, "odd sized firmware %zd\n", fw->size);
+	if (!rtl_fw_format_ok(tp, dev, fw, &phytable)) {
+		netif_err(tp, probe, dev, "invalid firwmare\n");
 		return;
 	}
 
-	for (index = 0; index < fw_size; index++) {
-		u32 action = le32_to_cpu(phytable[index]);
+	for (i = 0; i < phytable.size; i++) {
+		u32 action = le32_to_cpu(phytable.code[i]);
 		u32 regno = (action & 0x0fff0000) >> 16;
 
 		switch(action & 0xf0000000) {
@@ -1770,14 +1831,14 @@  rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
 			break;
 
 		case PHY_BJMPN:
-			if (regno > index) {
+			if (regno > i) {
 				netif_err(tp, probe, tp->dev,
 					  "Out of range of firmware\n");
 				return;
 			}
 			break;
 		case PHY_READCOUNT_EQ_SKIP:
-			if (index + 2 >= fw_size) {
+			if (i + 2 >= phytable.size) {
 				netif_err(tp, probe, tp->dev,
 					  "Out of range of firmware\n");
 				return;
@@ -1786,7 +1847,7 @@  rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
 		case PHY_COMP_EQ_SKIPN:
 		case PHY_COMP_NEQ_SKIPN:
 		case PHY_SKIPN:
-			if (index + 1 + regno >= fw_size) {
+			if (i + 1 + regno >= phytable.size) {
 				netif_err(tp, probe, tp->dev,
 					  "Out of range of firmware\n");
 				return;
@@ -1806,8 +1867,8 @@  rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
 	predata = 0;
 	count = 0;
 
-	for (index = 0; index < fw_size; ) {
-		u32 action = le32_to_cpu(phytable[index]);
+	for (i = 0; i < phytable.size; ) {
+		u32 action = le32_to_cpu(phytable.code[i]);
 		u32 data = action & 0x0000ffff;
 		u32 regno = (action & 0x0fff0000) >> 16;
 
@@ -1818,54 +1879,54 @@  rtl_phy_write_fw(struct rtl8169_private *tp, const struct firmware *fw)
 		case PHY_READ:
 			predata = rtl_readphy(tp, regno);
 			count++;
-			index++;
+			i++;
 			break;
 		case PHY_DATA_OR:
 			predata |= data;
-			index++;
+			i++;
 			break;
 		case PHY_DATA_AND:
 			predata &= data;
-			index++;
+			i++;
 			break;
 		case PHY_BJMPN:
-			index -= regno;
+			i -= regno;
 			break;
 		case PHY_READ_EFUSE:
 			predata = rtl8168d_efuse_read(tp->mmio_addr, regno);
-			index++;
+			i++;
 			break;
 		case PHY_CLEAR_READCOUNT:
 			count = 0;
-			index++;
+			i++;
 			break;
 		case PHY_WRITE:
 			rtl_writephy(tp, regno, data);
-			index++;
+			i++;
 			break;
 		case PHY_READCOUNT_EQ_SKIP:
-			index += (count == data) ? 2 : 1;
+			i += (count == data) ? 2 : 1;
 			break;
 		case PHY_COMP_EQ_SKIPN:
 			if (predata == data)
-				index += regno;
-			index++;
+				i += regno;
+			i++;
 			break;
 		case PHY_COMP_NEQ_SKIPN:
 			if (predata != data)
-				index += regno;
-			index++;
+				i += regno;
+			i++;
 			break;
 		case PHY_WRITE_PREVIOUS:
 			rtl_writephy(tp, regno, predata);
-			index++;
+			i++;
 			break;
 		case PHY_SKIPN:
-			index += regno + 1;
+			i += regno + 1;
 			break;
 		case PHY_DELAY_MS:
 			mdelay(data);
-			index++;
+			i++;
 			break;
 
 		case PHY_READ_MAC_BYTE: