From bc679dc919b0723eb60c7fcd5f9dd3325dda40fd Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Thu, 8 Jun 2017 12:33:59 +0530 Subject: [PATCH] qcacmn: Do not allow CE register access when recovery is in progress Currently, Shadow registers is not implemented for all registers. This can lead to unclocked access and followed by NOC errors. In Rx path Interrupt Status and src/dst read index are directly accessed without shadow block. Target may execute reset sequence due to PDR/SSR while rx path is active. Avoid direct access to below registers if target is crashed due to PDR/SSR. HOST_IE_ADDRESS HOST_IS_ADDRESS CURRENT_DRRI_ADDRESS CURRENT_SRRI_ADDRESS Return from ISR without scheduling the bottom half if target is crashed due to PDR/SSR. Change-Id: Ifa993e978579b4d061d21281338494292e19700a CRs-Fixed: 2123967 --- hif/inc/hif.h | 1 + hif/src/ce/ce_main.c | 26 ++++++++++++++++++-------- hif/src/ce/ce_service.c | 28 ++++++++++++++++++++++------ hif/src/ce/ce_tasklet.c | 4 ++++ hif/src/hif_io32.h | 2 ++ hif/src/hif_main.c | 17 +++++++++++++++++ hif/src/hif_main.h | 10 ++++++++++ hif/src/snoc/hif_io32_snoc.h | 13 +++++++++++++ hif/src/snoc/if_snoc.c | 16 ++++++++++++++++ 9 files changed, 103 insertions(+), 14 deletions(-) diff --git a/hif/inc/hif.h b/hif/inc/hif.h index 39368e673b..7cf7f963be 100644 --- a/hif/inc/hif.h +++ b/hif/inc/hif.h @@ -463,6 +463,7 @@ struct hif_driver_state_callbacks { bool (*is_recovery_in_progress)(void *context); bool (*is_load_unload_in_progress)(void *context); bool (*is_driver_unloading)(void *context); + bool (*is_target_ready)(void *context); }; /* This API detaches the HTC layer from the HIF device */ diff --git a/hif/src/ce/ce_main.c b/hif/src/ce/ce_main.c index f0e27fa62a..2e663bd54c 100644 --- a/hif/src/ce/ce_main.c +++ b/hif/src/ce/ce_main.c @@ -2897,11 +2897,16 @@ inline unsigned int hif_get_src_ring_read_index(struct hif_softc *scn, struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); attr = hif_state->host_ce_config[COPY_ENGINE_ID(CE_ctrl_addr)]; - if (attr.flags & CE_ATTR_DISABLE_INTR) + if (attr.flags & CE_ATTR_DISABLE_INTR) { return CE_SRC_RING_READ_IDX_GET_FROM_DDR(scn, CE_ctrl_addr); - else - return A_TARGET_READ(scn, - (CE_ctrl_addr) + CURRENT_SRRI_ADDRESS); + } else { + if (TARGET_REGISTER_ACCESS_ALLOWED(scn)) + return A_TARGET_READ(scn, + (CE_ctrl_addr) + CURRENT_SRRI_ADDRESS); + else + return CE_SRC_RING_READ_IDX_GET_FROM_DDR(scn, + CE_ctrl_addr); + } } /** @@ -2923,11 +2928,16 @@ inline unsigned int hif_get_dst_ring_read_index(struct hif_softc *scn, attr = hif_state->host_ce_config[COPY_ENGINE_ID(CE_ctrl_addr)]; - if (attr.flags & CE_ATTR_DISABLE_INTR) + if (attr.flags & CE_ATTR_DISABLE_INTR) { return CE_DEST_RING_READ_IDX_GET_FROM_DDR(scn, CE_ctrl_addr); - else - return A_TARGET_READ(scn, - (CE_ctrl_addr) + CURRENT_DRRI_ADDRESS); + } else { + if (TARGET_REGISTER_ACCESS_ALLOWED(scn)) + return A_TARGET_READ(scn, + (CE_ctrl_addr) + CURRENT_DRRI_ADDRESS); + else + return CE_DEST_RING_READ_IDX_GET_FROM_DDR(scn, + CE_ctrl_addr); + } } /** diff --git a/hif/src/ce/ce_service.c b/hif/src/ce/ce_service.c index 86001dfaf3..3e844cc033 100644 --- a/hif/src/ce/ce_service.c +++ b/hif/src/ce/ce_service.c @@ -1894,8 +1894,13 @@ more_data: goto more_data; } qdf_atomic_set(&ce_state->rx_pending, 0); - CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr, - HOST_IS_COPY_COMPLETE_MASK); + if (TARGET_REGISTER_ACCESS_ALLOWED(scn)) { + CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr, + HOST_IS_COPY_COMPLETE_MASK); + } else { + HIF_ERROR("%s: target access is not allowed", __func__); + return; + } if (ce_recv_entries_done_nolock_legacy(scn, ce_state)) { if (more_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) { @@ -2091,10 +2096,16 @@ more_watermarks: * more copy completions happened while the misc interrupts were being * handled. */ - if (!ce_srng_based(scn)) - CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr, - CE_WATERMARK_MASK | - HOST_IS_COPY_COMPLETE_MASK); + if (!ce_srng_based(scn)) { + if (TARGET_REGISTER_ACCESS_ALLOWED(scn)) { + CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr, + CE_WATERMARK_MASK | + HOST_IS_COPY_COMPLETE_MASK); + } else { + HIF_ERROR("%s: target access is not allowed", __func__); + return CE_state->receive_count; + } + } /* * Now that per-engine interrupts are cleared, verify that @@ -2217,6 +2228,11 @@ ce_per_engine_handler_adjust_legacy(struct CE_state *CE_state, if (Q_TARGET_ACCESS_BEGIN(scn) < 0) return; + if (!TARGET_REGISTER_ACCESS_ALLOWED(scn)) { + HIF_ERROR("%s: target access is not allowed", __func__); + return; + } + if ((!disable_copy_compl_intr) && (CE_state->send_cb || CE_state->recv_cb)) CE_COPY_COMPLETE_INTR_ENABLE(scn, ctrl_addr); diff --git a/hif/src/ce/ce_tasklet.c b/hif/src/ce/ce_tasklet.c index 038fdadcee..5db6af87c2 100644 --- a/hif/src/ce/ce_tasklet.c +++ b/hif/src/ce/ce_tasklet.c @@ -400,6 +400,10 @@ irqreturn_t ce_dispatch_interrupt(int ce_id, } hif_irq_disable(scn, ce_id); + + if (!TARGET_REGISTER_ACCESS_ALLOWED(scn)) + return IRQ_HANDLED; + hif_record_ce_desc_event(scn, ce_id, HIF_IRQ_EVENT, NULL, NULL, 0); hif_ce_increment_interrupt_count(hif_ce_state, ce_id); diff --git a/hif/src/hif_io32.h b/hif/src/hif_io32.h index 113b94a04b..6d1d1cfff0 100644 --- a/hif/src/hif_io32.h +++ b/hif/src/hif_io32.h @@ -40,6 +40,8 @@ hif_target_sleep_state_adjust(scn, false, true) #define Q_TARGET_ACCESS_END(scn) \ hif_target_sleep_state_adjust(scn, true, false) +#define TARGET_REGISTER_ACCESS_ALLOWED(scn)\ + hif_is_target_register_access_allowed(scn) /* * A_TARGET_ACCESS_LIKELY will not wait for the target to wake up before diff --git a/hif/src/hif_main.c b/hif/src/hif_main.c index afecf3b7de..9a217ba2d3 100644 --- a/hif/src/hif_main.c +++ b/hif/src/hif_main.c @@ -1067,6 +1067,23 @@ bool hif_is_recovery_in_progress(struct hif_softc *scn) return false; } + +/** + * hif_is_target_ready() - API to query if target is in ready state + * progress + * @scn: HIF Context + * + * Return: True/False + */ +bool hif_is_target_ready(struct hif_softc *scn) +{ + struct hif_driver_state_callbacks *cbk = hif_get_callbacks_handle(scn); + + if (cbk && cbk->is_target_ready) + return cbk->is_target_ready(cbk->context); + + return false; +} #if defined(HIF_PCI) || defined(SNOC) || defined(HIF_AHB) /** * hif_batch_send() - API to access hif specific function diff --git a/hif/src/hif_main.h b/hif/src/hif_main.h index 7f33f6b83f..54d5dd7526 100644 --- a/hif/src/hif_main.h +++ b/hif/src/hif_main.h @@ -236,6 +236,7 @@ struct hif_driver_state_callbacks *hif_get_callbacks_handle( bool hif_is_driver_unloading(struct hif_softc *scn); bool hif_is_load_or_unload_in_progress(struct hif_softc *scn); bool hif_is_recovery_in_progress(struct hif_softc *scn); +bool hif_is_target_ready(struct hif_softc *scn); void hif_wlan_disable(struct hif_softc *scn); int hif_target_sleep_state_adjust(struct hif_softc *scn, bool sleep_ok, @@ -259,4 +260,13 @@ static inline void hif_ramdump_handler(struct hif_opaque_softc *scn) {} */ irqreturn_t hif_wake_interrupt_handler(int irq, void *context); +#ifdef HIF_SNOC +bool hif_is_target_register_access_allowed(struct hif_softc *hif_sc); +#else +static inline +bool hif_is_target_register_access_allowed(struct hif_softc *hif_sc) +{ + return true; +} +#endif #endif /* __HIF_MAIN_H__ */ diff --git a/hif/src/snoc/hif_io32_snoc.h b/hif/src/snoc/hif_io32_snoc.h index f2a181698c..f22ac6b2d8 100644 --- a/hif/src/snoc/hif_io32_snoc.h +++ b/hif/src/snoc/hif_io32_snoc.h @@ -47,6 +47,10 @@ static inline void ce_enable_irq_in_individual_register(struct hif_softc *scn, uint32_t offset; offset = HOST_IE_ADDRESS + CE_BASE_ADDRESS(ce_id); + if (!TARGET_REGISTER_ACCESS_ALLOWED(scn)) { + HIF_ERROR("%s: target access is not allowed", __func__); + return; + } hif_write32_mb(scn->mem + offset, 1); } @@ -56,7 +60,16 @@ static inline void ce_disable_irq_in_individual_register(struct hif_softc *scn, uint32_t offset; offset = HOST_IE_ADDRESS + CE_BASE_ADDRESS(ce_id); + if (!TARGET_REGISTER_ACCESS_ALLOWED(scn)) { + HIF_ERROR("%s: target access is not allowed", __func__); + return; + } hif_write32_mb(scn->mem + offset, 0); + + if (!TARGET_REGISTER_ACCESS_ALLOWED(scn)) { + HIF_ERROR("%s: target access is not allowed", __func__); + return; + } hif_read32_mb(scn->mem + offset); } #endif diff --git a/hif/src/snoc/if_snoc.c b/hif/src/snoc/if_snoc.c index 1a76ef82f9..0502f8aa49 100644 --- a/hif/src/snoc/if_snoc.c +++ b/hif/src/snoc/if_snoc.c @@ -464,3 +464,19 @@ int hif_snoc_map_ce_to_irq(struct hif_softc *scn, int ce_id) { return icnss_get_irq(ce_id); } + +/** + * hif_is_target_register_access_allowed(): Check target register access allow + * @scn: HIF Context + * + * This function help to check whether target register access is allowed or not + * + * Return: true if target access is allowed else false + */ +bool hif_is_target_register_access_allowed(struct hif_softc *scn) +{ + if (hif_is_recovery_in_progress(scn)) + return hif_is_target_ready(scn); + else + return true; +}