diff options
author | Grant Grundler <grundler@parisc-linux.org> | 2008-03-23 23:23:10 -0600 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2008-03-28 21:52:14 -0400 |
commit | 209261c019f56d77f6a0cc38048e9a6f25867589 (patch) | |
tree | 36824a55e62c33e4ae4c24ce824d2948a83924de | |
parent | 3480c63bdf008e9289aab94418f43b9592978fff (diff) |
[netdrvr] tulip_read_eeprom fixes for BUG 4420
If "location" is > "addr_len" bits, the high bits of location would interfere
with the READ_CMD sent to the eeprom controller.
A patch was submitted to bug:
http://bugzilla.kernel.org/show_bug.cgi?id=4420
which simply truncated the "location", read whatever was in "location
modulo addr_len", and returned that value. That avoids confusing the
eeprom but seems like the wrong solution to me.
Correct would be to not read beyond "1 << addr_len" address of the eeprom.
I am submitting two changes to implement this:
1) tulip_read_eeprom will return zero (since we can't return -EINVAL)
if this is attempted (defensive programming).
2) In tulip_core.c, fix the tulip_read_eeprom caller so they don't
iterate past addr_len bits and make sure the entire tp->eeprom[]
array is cleared.
I konw we don't strictly need both. I would prefer both in the tree
since it documents the issue and provides a second "defense" from
the bug from creeping back in.
Signed-off-by: Grant Grundler <grundler@parisc-linux.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/net/tulip/eeprom.c | 6 | ||||
-rw-r--r-- | drivers/net/tulip/tulip_core.c | 7 |
2 files changed, 12 insertions, 1 deletions
diff --git a/drivers/net/tulip/eeprom.c b/drivers/net/tulip/eeprom.c index 206918bad53..da2206f6021 100644 --- a/drivers/net/tulip/eeprom.c +++ b/drivers/net/tulip/eeprom.c @@ -343,6 +343,12 @@ int __devinit tulip_read_eeprom(struct net_device *dev, int location, int addr_l void __iomem *ee_addr = tp->base_addr + CSR9; int read_cmd = location | (EE_READ_CMD << addr_len); + /* If location is past the end of what we can address, don't + * read some other location (ie truncate). Just return zero. + */ + if (location > (1 << addr_len) - 1) + return 0; + iowrite32(EE_ENB & ~EE_CS, ee_addr); iowrite32(EE_ENB, ee_addr); diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index ed600bf56e7..82f404b76d8 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -1437,6 +1437,7 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, EEPROM. */ ee_data = tp->eeprom; + memset(ee_data, 0, sizeof(tp->eeprom)); sum = 0; if (chip_idx == LC82C168) { for (i = 0; i < 3; i++) { @@ -1458,8 +1459,12 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, /* A serial EEPROM interface, we read now and sort it out later. */ int sa_offset = 0; int ee_addr_size = tulip_read_eeprom(dev, 0xff, 8) & 0x40000 ? 8 : 6; + int ee_max_addr = ((1 << ee_addr_size) - 1) * sizeof(u16); - for (i = 0; i < sizeof(tp->eeprom); i+=2) { + if (ee_max_addr > sizeof(tp->eeprom)) + ee_max_addr = sizeof(tp->eeprom); + + for (i = 0; i < ee_max_addr ; i += sizeof(u16)) { u16 data = tulip_read_eeprom(dev, i/2, ee_addr_size); ee_data[i] = data & 0xff; ee_data[i + 1] = data >> 8; |