diff options
author | David Dillow <dave@thedillows.org> | 2009-03-03 01:15:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-03-03 01:15:09 -0500 |
commit | a8c9a53c22441efcd57ad7955231b2804696b133 (patch) | |
tree | 7c6090475a02814b4a5e1a500d6066a811691bb7 /drivers/net/typhoon.c | |
parent | d20b606c99c7fd9f1b6c6ec43c877eaae827d169 (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.c | 137 |
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; | |||
136 | static char version[] __devinitdata = | 137 | static 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" | ||
140 | MODULE_AUTHOR("David Dillow <dave@thedillows.org>"); | 140 | MODULE_AUTHOR("David Dillow <dave@thedillows.org>"); |
141 | MODULE_VERSION(DRV_MODULE_VERSION); | 141 | MODULE_VERSION(DRV_MODULE_VERSION); |
142 | MODULE_LICENSE("GPL"); | 142 | MODULE_LICENSE("GPL"); |
@@ -1347,11 +1347,16 @@ typhoon_init_rings(struct typhoon *tp) | |||
1347 | } | 1347 | } |
1348 | 1348 | ||
1349 | static const struct firmware *typhoon_fw; | 1349 | static const struct firmware *typhoon_fw; |
1350 | static u8 *typhoon_fw_image; | ||
1351 | 1350 | ||
1352 | static int | 1351 | static int |
1353 | typhoon_request_firmware(struct typhoon *tp) | 1352 | typhoon_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 | ||
1384 | out_err: | 1402 | invalid_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 | ||
1390 | static int | 1409 | static 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 | ||
1516 | err_out: | 1547 | err_out: |
1517 | return err; | 1548 | return err; |
@@ -2651,10 +2682,8 @@ typhoon_init(void) | |||
2651 | static void __exit | 2682 | static void __exit |
2652 | typhoon_cleanup(void) | 2683 | typhoon_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 | ||