The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=557866b899b632ca4160953e5430cd9ccba8b570
commit 557866b899b632ca4160953e5430cd9ccba8b570 Author: Nick Price <[email protected]> AuthorDate: 2026-07-19 16:41:49 +0000 Commit: Adrian Chadd <[email protected]> CommitDate: 2026-07-19 16:41:49 +0000 aq(4): harden the attach, detach, and reset error paths Correct several attach/detach/reset paths that either swallowed failures or acted on undefined state. MSI-X attach-failure double-free: aq_if_msix_intr_assign() freed the per-RX-ring interrupts in its failure path and then returned an error, so iflib's IFDI_DETACH freed the same irq structures again -- bus_teardown_intr() on a dangling tag and bus_release_resource() on an already-released IRQ, panicking a box that should have simply failed to attach. Let iflib own the teardown; drop the failure-path loop and the now-dead index bookkeeping. Detach loop bound: aq_if_detach() freed the per-ring interrupts looping to isc_nrxqsets while indexing rx_rings[], which is sized by rx_rings_count; index by rx_rings_count to match every other RX-ring loop. AQ_HW_WAIT_FOR final poll: the macro derived its result from the loop counter rather than the condition, so a condition that became true on the last iteration reported ETIMEDOUT. Worst for the acquire-on-read firmware RAM semaphore, which was acquired in hardware but reported as a timeout. Return based on the last evaluation of the condition. RBL MAC reset SPI cleanup: mac_soft_reset_rbl() fired the global reset without first tearing down the SPI/flash interface, so a flash burst in flight left the SPI bus wedged, the RBL could not re-read flash, and the reset returned EBUSY -- fatal at attach ("MAC reset failed: 16"). Set bit 4 of the SPI control register (0x53c) before the global reset, as the sibling FLB path and the Linux driver do. Reset failure propagation: aq_hw_reset() discarded fw_ops->reset()'s return, so a failed attach-time fw2x capability read left fw_caps == 0 permanently and stats silently froze. Propagate the error so the reset fails and is retried. aq_hw_init failure propagation: aq_hw_init() discarded aq_hw_init_tx_path()/aq_hw_init_rx_path() returns and reported success, bringing the interface up half-initialized; capture both and goto err_exit (mainly the Atlantic 2 RX action-resolver path, which returns EBUSY on ART semaphore timeout). Link-state outputs: aq_hw_get_link_state() left *link_speed and *fc_neg unwritten on early-return paths, and the caller acts on them uninitialized, so a transient firmware get_mode() failure could fabricate a phantom link-up at a garbage speed and program a garbage RX-pause bit. Initialize both to safe link-down values before calling get_mode(). Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58138 --- sys/dev/aq/aq_common.h | 5 +++-- sys/dev/aq/aq_fw.c | 4 ++++ sys/dev/aq/aq_hw.c | 17 +++++++++++++---- sys/dev/aq/aq_main.c | 17 ++++++++++------- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/sys/dev/aq/aq_common.h b/sys/dev/aq/aq_common.h index 5c497d071802..7fa245caeebb 100644 --- a/sys/dev/aq/aq_common.h +++ b/sys/dev/aq/aq_common.h @@ -42,9 +42,10 @@ #define AQ_HW_WAIT_FOR(_B_, _US_, _N_) ({ \ unsigned int _i; \ - for (_i = (_N_); !(_B_) && _i; --_i) \ + int _b; \ + for (_i = (_N_); !(_b = (_B_)) && _i; --_i) \ DELAY(_US_); \ - (_i == 0) ? ETIMEDOUT : 0; \ + _b ? 0 : ETIMEDOUT; \ }) diff --git a/sys/dev/aq/aq_fw.c b/sys/dev/aq/aq_fw.c index 72751c81b773..f2f75dd7d3de 100644 --- a/sys/dev/aq/aq_fw.c +++ b/sys/dev/aq/aq_fw.c @@ -306,6 +306,10 @@ mac_soft_reset_rbl(struct aq_hw* hw, enum aq_fw_bootloader_mode* mode) reg_glb_cpu_no_reset_scratchpad_set(hw, 0xDEAD, NO_RESET_SCRATCHPAD_RBL_STATUS); + /* Clear a stale flash burst so the global reset frees the SPI bus. */ + uint32_t spi = AQ_READ_REG(hw, 0x53c); + AQ_WRITE_REG(hw, 0x53c, spi | 0x10); + // Global software reset rx_rx_reg_res_dis_set(hw, 0); tx_tx_reg_res_dis_set(hw, 0); diff --git a/sys/dev/aq/aq_hw.c b/sys/dev/aq/aq_hw.c index e610f8b27ba7..7fc122482125 100644 --- a/sys/dev/aq/aq_hw.c +++ b/sys/dev/aq/aq_hw.c @@ -253,6 +253,10 @@ aq_hw_get_link_state(struct aq_hw *hw, uint32_t *link_speed, struct aq_hw_fc_inf enum aq_fw_link_speed speed = aq_fw_none; enum aq_fw_link_fc fc; + *link_speed = 0; + fc_neg->fc_rx = false; + fc_neg->fc_tx = false; + err = hw->fw_ops->get_mode(hw, &mode, &speed, &fc); if (err != 0) { @@ -260,7 +264,6 @@ aq_hw_get_link_state(struct aq_hw *hw, uint32_t *link_speed, struct aq_hw_fc_inf AQ_DBG_EXIT(err); return (err); } - *link_speed = 0; if (mode != MPI_INIT) return (0); @@ -376,7 +379,9 @@ aq_hw_reset(struct aq_hw *hw) goto err_exit; } - hw->fw_ops->reset(hw); + err = hw->fw_ops->reset(hw); + if (err != 0) + goto err_exit; err = aq_hw_err_from_flags(hw); @@ -644,8 +649,12 @@ aq_hw_init(struct aq_hw *hw, uint8_t *mac_addr, uint8_t adm_irq, bool msix) */ AQ_WRITE_REG(hw, AQ_HW_TX_DMA_TOTAL_REQ_LIMIT_ADR, 24); - aq_hw_init_tx_path(hw); - aq_hw_init_rx_path(hw); + err = aq_hw_init_tx_path(hw); + if (err != 0) + goto err_exit; + err = aq_hw_init_rx_path(hw); + if (err != 0) + goto err_exit; aq_hw_mac_addr_set(hw, mac_addr, AQ_HW_MAC); diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c index 4c06447b55d3..c77575273e92 100644 --- a/sys/dev/aq/aq_main.c +++ b/sys/dev/aq/aq_main.c @@ -360,9 +360,14 @@ aq_if_attach_pre(if_ctx_t ctx) } if (hw->fast_start_enabled) - hw->fw_ops->reset(hw); + rc = hw->fw_ops->reset(hw); else - aq_hw_reset(&softc->hw); + rc = aq_hw_reset(&softc->hw); + if (rc != 0) { + device_printf(softc->dev, "%s: reset failed, err=%d\n", + __func__, rc); + goto fail; + } aq_hw_capabilities(softc); rc = aq_hw_get_mac_permanent(hw, hw->mac_addr); @@ -480,7 +485,7 @@ aq_if_detach(if_ctx_t ctx) aq_hw_deinit(&softc->hw); - for (i = 0; i < softc->scctx->isc_nrxqsets; i++) + for (i = 0; i < softc->rx_rings_count; i++) iflib_irq_free(ctx, &softc->rx_rings[i]->irq); iflib_irq_free(ctx, &softc->irq); @@ -707,6 +712,8 @@ aq_if_init(if_ctx_t ctx) softc->scctx->isc_intr == IFLIB_INTR_MSIX); if (err != 0) { device_printf(softc->dev, "atlantic: aq_hw_init: %d", err); + AQ_DBG_EXIT(err); + return; } aq_if_media_status(ctx, &ifmr); @@ -1029,7 +1036,6 @@ aq_if_msix_intr_assign(if_ctx_t ctx, int msix) if (rc) { device_printf(softc->dev, "failed to set up RX handler\n"); - i--; goto fail; } @@ -1057,15 +1063,12 @@ aq_if_msix_intr_assign(if_ctx_t ctx, int msix) if (rc) { device_printf(iflib_get_dev(ctx), "Failed to register admin handler"); - i = softc->rx_rings_count - 1; goto fail; } AQ_DBG_EXIT(0); return (0); fail: - for (; i >= 0; i--) - iflib_irq_free(ctx, &softc->rx_rings[i]->irq); AQ_DBG_EXIT(rc); return (rc); }
