aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/typhoon.c
diff options
context:
space:
mode:
authorDavid Dillow <dave@thedillows.org>2009-03-03 01:15:09 -0500
committerDavid S. Miller <davem@davemloft.net>2009-03-03 01:15:09 -0500
commita8c9a53c22441efcd57ad7955231b2804696b133 (patch)
tree7c6090475a02814b4a5e1a500d6066a811691bb7 /drivers/net/typhoon.c
parentd20b606c99c7fd9f1b6c6ec43c877eaae827d169 (diff)
typhoon: repair firmware loading
The conversion to avoid using pci_alloc_consistent() broke the firmware load process, as well as added an order-4 kmalloc and doubled the memory usage of the firmware image. Go back to loading a page at a time. Also, since the user can now give us utter garbage for firmware, do a cursory validation so we don't try to load just anything. Signed-off-by: David Dillow <dave@thedillows.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/typhoon.c')
-rw-r--r--drivers/net/typhoon.c137
1 files changed, 83 insertions, 54 deletions
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index 9bba7871636..9dd4f76a2ff 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -100,10 +100,11 @@ static const int multicast_filter_limit = 32;
100#define PKT_BUF_SZ 1536 100#define PKT_BUF_SZ 1536
101 101
102#define DRV_MODULE_NAME "typhoon" 102#define DRV_MODULE_NAME "typhoon"
103#define DRV_MODULE_VERSION "1.5.8" 103#define DRV_MODULE_VERSION "1.5.9"
104#define DRV_MODULE_RELDATE "06/11/09" 104#define DRV_MODULE_RELDATE "Mar 2, 2009"
105#define PFX DRV_MODULE_NAME ": " 105#define PFX DRV_MODULE_NAME ": "
106#define ERR_PFX KERN_ERR PFX 106#define ERR_PFX KERN_ERR PFX
107#define FIRMWARE_NAME "3com/typhoon.bin"
107 108
108#include <linux/module.h> 109#include <linux/module.h>
109#include <linux/kernel.h> 110#include <linux/kernel.h>
@@ -136,7 +137,6 @@ static const int multicast_filter_limit = 32;
136static char version[] __devinitdata = 137static char version[] __devinitdata =
137 "typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 138 "typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
138 139
139#define FIRMWARE_NAME "3com/typhoon.bin"
140MODULE_AUTHOR("David Dillow <dave@thedillows.org>"); 140MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
141MODULE_VERSION(DRV_MODULE_VERSION); 141MODULE_VERSION(DRV_MODULE_VERSION);
142MODULE_LICENSE("GPL"); 142MODULE_LICENSE("GPL");
@@ -1347,11 +1347,16 @@ typhoon_init_rings(struct typhoon *tp)
1347} 1347}
1348 1348
1349static const struct firmware *typhoon_fw; 1349static const struct firmware *typhoon_fw;
1350static u8 *typhoon_fw_image;
1351 1350
1352static int 1351static int
1353typhoon_request_firmware(struct typhoon *tp) 1352typhoon_request_firmware(struct typhoon *tp)
1354{ 1353{
1354 const struct typhoon_file_header *fHdr;
1355 const struct typhoon_section_header *sHdr;
1356 const u8 *image_data;
1357 u32 numSections;
1358 u32 section_len;
1359 u32 remaining;
1355 int err; 1360 int err;
1356 1361
1357 if (typhoon_fw) 1362 if (typhoon_fw)
@@ -1360,31 +1365,45 @@ typhoon_request_firmware(struct typhoon *tp)
1360 err = request_firmware(&typhoon_fw, FIRMWARE_NAME, &tp->pdev->dev); 1365 err = request_firmware(&typhoon_fw, FIRMWARE_NAME, &tp->pdev->dev);
1361 if (err) { 1366 if (err) {
1362 printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n", 1367 printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n",
1363 tp->name, FIRMWARE_NAME); 1368 tp->name, FIRMWARE_NAME);
1364 return err; 1369 return err;
1365 } 1370 }
1366 1371
1367 if (typhoon_fw->size < sizeof(struct typhoon_file_header) || 1372 image_data = (u8 *) typhoon_fw->data;
1368 memcmp(typhoon_fw->data, "TYPHOON", 8)) { 1373 remaining = typhoon_fw->size;
1369 printk(KERN_ERR "%s: Invalid firmware image\n", 1374 if (remaining < sizeof(struct typhoon_file_header))
1370 tp->name); 1375 goto invalid_fw;
1371 err = -EINVAL;
1372 goto out_err;
1373 }
1374 1376
1375 typhoon_fw_image = kmalloc(typhoon_fw->size, GFP_KERNEL); 1377 fHdr = (struct typhoon_file_header *) image_data;
1376 if (!typhoon_fw_image) { 1378 if (memcmp(fHdr->tag, "TYPHOON", 8))
1377 err = -ENOMEM; 1379 goto invalid_fw;
1378 goto out_err; 1380
1381 numSections = le32_to_cpu(fHdr->numSections);
1382 image_data += sizeof(struct typhoon_file_header);
1383 remaining -= sizeof(struct typhoon_file_header);
1384
1385 while (numSections--) {
1386 if (remaining < sizeof(struct typhoon_section_header))
1387 goto invalid_fw;
1388
1389 sHdr = (struct typhoon_section_header *) image_data;
1390 image_data += sizeof(struct typhoon_section_header);
1391 section_len = le32_to_cpu(sHdr->len);
1392
1393 if (remaining < section_len)
1394 goto invalid_fw;
1395
1396 image_data += section_len;
1397 remaining -= section_len;
1379 } 1398 }
1380 memcpy(typhoon_fw_image, typhoon_fw->data, typhoon_fw->size);
1381 1399
1382 return 0; 1400 return 0;
1383 1401
1384out_err: 1402invalid_fw:
1403 printk(KERN_ERR "%s: Invalid firmware image\n", tp->name);
1385 release_firmware(typhoon_fw); 1404 release_firmware(typhoon_fw);
1386 typhoon_fw = NULL; 1405 typhoon_fw = NULL;
1387 return err; 1406 return -EINVAL;
1388} 1407}
1389 1408
1390static int 1409static int
@@ -1395,24 +1414,29 @@ typhoon_download_firmware(struct typhoon *tp)
1395 const struct typhoon_file_header *fHdr; 1414 const struct typhoon_file_header *fHdr;
1396 const struct typhoon_section_header *sHdr; 1415 const struct typhoon_section_header *sHdr;
1397 const u8 *image_data; 1416 const u8 *image_data;
1398 dma_addr_t image_dma; 1417 void *dpage;
1418 dma_addr_t dpage_dma;
1399 __sum16 csum; 1419 __sum16 csum;
1400 u32 irqEnabled; 1420 u32 irqEnabled;
1401 u32 irqMasked; 1421 u32 irqMasked;
1402 u32 numSections; 1422 u32 numSections;
1403 u32 section_len; 1423 u32 section_len;
1424 u32 len;
1404 u32 load_addr; 1425 u32 load_addr;
1405 u32 hmac; 1426 u32 hmac;
1406 int i; 1427 int i;
1407 int err; 1428 int err;
1408 1429
1409 image_data = typhoon_fw_image; 1430 image_data = (u8 *) typhoon_fw->data;
1410 fHdr = (struct typhoon_file_header *) image_data; 1431 fHdr = (struct typhoon_file_header *) image_data;
1411 1432
1433 /* Cannot just map the firmware image using pci_map_single() as
1434 * the firmware is vmalloc()'d and may not be physically contiguous,
1435 * so we allocate some consistent memory to copy the sections into.
1436 */
1412 err = -ENOMEM; 1437 err = -ENOMEM;
1413 image_dma = pci_map_single(pdev, (u8 *) image_data, 1438 dpage = pci_alloc_consistent(pdev, PAGE_SIZE, &dpage_dma);
1414 typhoon_fw->size, PCI_DMA_TODEVICE); 1439 if(!dpage) {
1415 if (pci_dma_mapping_error(pdev, image_dma)) {
1416 printk(KERN_ERR "%s: no DMA mem for firmware\n", tp->name); 1440 printk(KERN_ERR "%s: no DMA mem for firmware\n", tp->name);
1417 goto err_out; 1441 goto err_out;
1418 } 1442 }
@@ -1460,34 +1484,41 @@ typhoon_download_firmware(struct typhoon *tp)
1460 load_addr = le32_to_cpu(sHdr->startAddr); 1484 load_addr = le32_to_cpu(sHdr->startAddr);
1461 section_len = le32_to_cpu(sHdr->len); 1485 section_len = le32_to_cpu(sHdr->len);
1462 1486
1463 if (typhoon_wait_interrupt(ioaddr) < 0 || 1487 while(section_len) {
1464 ioread32(ioaddr + TYPHOON_REG_STATUS) != 1488 len = min_t(u32, section_len, PAGE_SIZE);
1465 TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
1466 printk(KERN_ERR "%s: segment ready timeout\n",
1467 tp->name);
1468 goto err_out_irq;
1469 }
1470 1489
1471 /* Do an pseudo IPv4 checksum on the data -- first 1490 if(typhoon_wait_interrupt(ioaddr) < 0 ||
1472 * need to convert each u16 to cpu order before 1491 ioread32(ioaddr + TYPHOON_REG_STATUS) !=
1473 * summing. Fortunately, due to the properties of 1492 TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
1474 * the checksum, we can do this once, at the end. 1493 printk(KERN_ERR "%s: segment ready timeout\n",
1475 */ 1494 tp->name);
1476 csum = csum_fold(csum_partial(image_data, section_len, 0)); 1495 goto err_out_irq;
1477 1496 }
1478 iowrite32(section_len, ioaddr + TYPHOON_REG_BOOT_LENGTH);
1479 iowrite32(le16_to_cpu((__force __le16)csum),
1480 ioaddr + TYPHOON_REG_BOOT_CHECKSUM);
1481 iowrite32(load_addr,
1482 ioaddr + TYPHOON_REG_BOOT_DEST_ADDR);
1483 iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI);
1484 iowrite32(image_dma + (image_data - typhoon_fw_image),
1485 ioaddr + TYPHOON_REG_BOOT_DATA_LO);
1486 typhoon_post_pci_writes(ioaddr);
1487 iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE,
1488 ioaddr + TYPHOON_REG_COMMAND);
1489 1497
1490 image_data += section_len; 1498 /* Do an pseudo IPv4 checksum on the data -- first
1499 * need to convert each u16 to cpu order before
1500 * summing. Fortunately, due to the properties of
1501 * the checksum, we can do this once, at the end.
1502 */
1503 csum = csum_fold(csum_partial_copy_nocheck(image_data,
1504 dpage, len,
1505 0));
1506
1507 iowrite32(len, ioaddr + TYPHOON_REG_BOOT_LENGTH);
1508 iowrite32(le16_to_cpu((__force __le16)csum),
1509 ioaddr + TYPHOON_REG_BOOT_CHECKSUM);
1510 iowrite32(load_addr,
1511 ioaddr + TYPHOON_REG_BOOT_DEST_ADDR);
1512 iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI);
1513 iowrite32(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO);
1514 typhoon_post_pci_writes(ioaddr);
1515 iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE,
1516 ioaddr + TYPHOON_REG_COMMAND);
1517
1518 image_data += len;
1519 load_addr += len;
1520 section_len -= len;
1521 }
1491 } 1522 }
1492 1523
1493 if(typhoon_wait_interrupt(ioaddr) < 0 || 1524 if(typhoon_wait_interrupt(ioaddr) < 0 ||
@@ -1511,7 +1542,7 @@ err_out_irq:
1511 iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK); 1542 iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK);
1512 iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE); 1543 iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE);
1513 1544
1514 pci_unmap_single(pdev, image_dma, typhoon_fw->size, PCI_DMA_TODEVICE); 1545 pci_free_consistent(pdev, PAGE_SIZE, dpage, dpage_dma);
1515 1546
1516err_out: 1547err_out:
1517 return err; 1548 return err;
@@ -2651,10 +2682,8 @@ typhoon_init(void)
2651static void __exit 2682static void __exit
2652typhoon_cleanup(void) 2683typhoon_cleanup(void)
2653{ 2684{
2654 if (typhoon_fw) { 2685 if (typhoon_fw)
2655 kfree(typhoon_fw_image);
2656 release_firmware(typhoon_fw); 2686 release_firmware(typhoon_fw);
2657 }
2658 pci_unregister_driver(&typhoon_driver); 2687 pci_unregister_driver(&typhoon_driver);
2659} 2688}
2660 2689