Bladeren bron

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
Govind Singh 7 jaren geleden
bovenliggende
commit
bc679dc919
9 gewijzigde bestanden met toevoegingen van 103 en 14 verwijderingen
  1. 1 0
      hif/inc/hif.h
  2. 18 8
      hif/src/ce/ce_main.c
  3. 22 6
      hif/src/ce/ce_service.c
  4. 4 0
      hif/src/ce/ce_tasklet.c
  5. 2 0
      hif/src/hif_io32.h
  6. 17 0
      hif/src/hif_main.c
  7. 10 0
      hif/src/hif_main.h
  8. 13 0
      hif/src/snoc/hif_io32_snoc.h
  9. 16 0
      hif/src/snoc/if_snoc.c

+ 1 - 0
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 */

+ 18 - 8
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);
+	}
 }
 
 /**

+ 22 - 6
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);

+ 4 - 0
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);
 

+ 2 - 0
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

+ 17 - 0
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

+ 10 - 0
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__ */

+ 13 - 0
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

+ 16 - 0
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;
+}