From 47dc608a12063f0375f80c19aa6b85f48094b8ac Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 28 Oct 2010 23:23:48 -0700 Subject: [PATCH] nata - properly teardown interrupts on ENXIO * When a ata controller cannot be attached (e.g. due to AHCI having already taken its resources) be sure to teardown and release any resources that had been obtained. * This fixes a panic on certain motherboards (e.g. gigabyte / phenomX6) where the ata controller ENXIO'd but left interrupt handlers setup. An interrupt would then occur, access stale memory, and blow the machine up. --- sys/dev/disk/nata/ata-chipset.c | 43 ++++++++++++++++++++++++++++----- sys/dev/disk/nata/ata-pci.c | 9 +++++-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/sys/dev/disk/nata/ata-chipset.c b/sys/dev/disk/nata/ata-chipset.c index 6607d6f9ea..91d44445d1 100644 --- a/sys/dev/disk/nata/ata-chipset.c +++ b/sys/dev/disk/nata/ata-chipset.c @@ -187,6 +187,7 @@ static void ata_via_family_setmode(device_t dev, int mode); static struct ata_chip_id *ata_match_chip(device_t dev, struct ata_chip_id *index); static struct ata_chip_id *ata_find_chip(device_t dev, struct ata_chip_id *index, int slot); static int ata_setup_interrupt(device_t dev); +static void ata_teardown_interrupt(device_t dev); static int ata_serialize(device_t dev, int flags); static void ata_print_cable(device_t dev, u_int8_t *who); static int ata_atapi(device_t dev); @@ -1876,8 +1877,10 @@ ata_intel_chipinit(device_t dev) ctlr->r_rid2 = PCIR_BAR(0); if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, &ctlr->r_rid2, - RF_ACTIVE))) + RF_ACTIVE))) { + ata_teardown_interrupt(dev); return ENXIO; + } ctlr->channels = 4; ctlr->allocate = ata_intel_31244_allocate; ctlr->reset = ata_intel_31244_reset; @@ -2517,8 +2520,10 @@ ata_jmicron_chipinit(device_t dev) pci_write_config(dev, 0x40, 0x80c0a131, 4); pci_write_config(dev, 0x80, 0x01200000, 4); - if ((error = ata_ahci_chipinit(dev))) + if ((error = ata_ahci_chipinit(dev))) { + ata_teardown_interrupt(dev); return error; + } ctlr->allocate = ata_jmicron_allocate; ctlr->reset = ata_jmicron_reset; @@ -2706,8 +2711,10 @@ ata_marvell_edma_chipinit(device_t dev) ctlr->r_type1 = SYS_RES_MEMORY; ctlr->r_rid1 = PCIR_BAR(0); if (!(ctlr->r_res1 = bus_alloc_resource_any(dev, ctlr->r_type1, - &ctlr->r_rid1, RF_ACTIVE))) + &ctlr->r_rid1, RF_ACTIVE))) { + ata_teardown_interrupt(dev); return ENXIO; + } /* mask all host controller interrupts */ ATA_OUTL(ctlr->r_res1, 0x01d64, 0x00000000); @@ -4370,8 +4377,10 @@ ata_serverworks_chipinit(device_t dev) ctlr->r_type2 = SYS_RES_MEMORY; ctlr->r_rid2 = PCIR_BAR(5); if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, - &ctlr->r_rid2, RF_ACTIVE))) + &ctlr->r_rid2, RF_ACTIVE))){ + ata_teardown_interrupt(dev); return ENXIO; + } ctlr->channels = ctlr->chip->cfg2; ctlr->allocate = ata_serverworks_allocate; @@ -4559,14 +4568,17 @@ ata_sii_chipinit(device_t dev) ctlr->r_type1 = SYS_RES_MEMORY; ctlr->r_rid1 = PCIR_BAR(0); if (!(ctlr->r_res1 = bus_alloc_resource_any(dev, ctlr->r_type1, - &ctlr->r_rid1, RF_ACTIVE))) + &ctlr->r_rid1, RF_ACTIVE))){ + ata_teardown_interrupt(dev); return ENXIO; + } ctlr->r_rid2 = PCIR_BAR(2); ctlr->r_type2 = SYS_RES_MEMORY; if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, &ctlr->r_rid2, RF_ACTIVE))){ bus_release_resource(dev, ctlr->r_type1, ctlr->r_rid1,ctlr->r_res1); + ata_teardown_interrupt(dev); return ENXIO; } ctlr->allocate = ata_siiprb_allocate; @@ -4589,8 +4601,10 @@ ata_sii_chipinit(device_t dev) ctlr->r_type2 = SYS_RES_MEMORY; ctlr->r_rid2 = PCIR_BAR(5); if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, - &ctlr->r_rid2, RF_ACTIVE))) + &ctlr->r_rid2, RF_ACTIVE))){ + ata_teardown_interrupt(dev); return ENXIO; + } if (ctlr->chip->cfg2 & SIISETCLK) { if ((pci_read_config(dev, 0x8a, 1) & 0x30) != 0x10) @@ -5293,6 +5307,7 @@ ata_sis_chipinit(device_t dev) ctlr->setmode = ata_sata_setmode; return 0; default: + ata_teardown_interrupt(dev); return ENXIO; } ctlr->setmode = ata_sis_setmode; @@ -5758,12 +5773,28 @@ ata_setup_interrupt(device_t dev) if ((bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS, ata_generic_intr, ctlr, &ctlr->handle, NULL))) { device_printf(dev, "unable to setup interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, rid, ctlr->r_irq); + ctlr->r_irq = 0; return ENXIO; } } return 0; } +static void +ata_teardown_interrupt(device_t dev) +{ + struct ata_pci_controller *ctlr = device_get_softc(dev); + + if (!ctlr->legacy) { + if (ctlr->r_irq) { + bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle); + bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ctlr->r_irq); + ctlr->r_irq = 0; + } + } +} + struct ata_serialize { struct spinlock locked_mtx; int locked_ch; diff --git a/sys/dev/disk/nata/ata-pci.c b/sys/dev/disk/nata/ata-pci.c index b0d8d43772..205b7721ae 100644 --- a/sys/dev/disk/nata/ata-pci.c +++ b/sys/dev/disk/nata/ata-pci.c @@ -276,11 +276,16 @@ ata_pci_detach(device_t dev) if (ctlr->r_irq) { bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ctlr->r_irq); + ctlr->r_irq = 0; } - if (ctlr->r_res2) + if (ctlr->r_res2) { bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2); - if (ctlr->r_res1) + ctlr->r_res2 = 0; + } + if (ctlr->r_res1) { bus_release_resource(dev, ctlr->r_type1, ctlr->r_rid1, ctlr->r_res1); + ctlr->r_res1 = 0; + } return 0; } -- 2.41.0