@@ -89,14 +89,22 @@ struct trbe_buf {
8989 * - Not duplicating the detection logic
9090 * - Streamlined detection of erratum across the system
9191 */
92+ #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0
9293
9394static int trbe_errata_cpucaps [] = {
95+ [TRBE_WORKAROUND_OVERWRITE_FILL_MODE ] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE ,
9496 -1 , /* Sentinel, must be the last entry */
9597};
9698
9799/* The total number of listed errata in trbe_errata_cpucaps */
98100#define TRBE_ERRATA_MAX (ARRAY_SIZE(trbe_errata_cpucaps) - 1)
99101
102+ /*
103+ * Safe limit for the number of bytes that may be overwritten
104+ * when ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE is triggered.
105+ */
106+ #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
107+
100108/*
101109 * struct trbe_cpudata: TRBE instance specific data
102110 * @trbe_flag - TRBE dirty/access flag support
@@ -147,6 +155,11 @@ static inline bool trbe_has_erratum(struct trbe_cpudata *cpudata, int i)
147155 return (i < TRBE_ERRATA_MAX ) && test_bit (i , cpudata -> errata );
148156}
149157
158+ static inline bool trbe_may_overwrite_in_fill_mode (struct trbe_cpudata * cpudata )
159+ {
160+ return trbe_has_erratum (cpudata , TRBE_WORKAROUND_OVERWRITE_FILL_MODE );
161+ }
162+
150163static int trbe_alloc_node (struct perf_event * event )
151164{
152165 if (event -> cpu == -1 )
@@ -550,10 +563,13 @@ static void trbe_enable_hw(struct trbe_buf *buf)
550563 set_trbe_limit_pointer_enabled (buf -> trbe_limit );
551564}
552565
553- static enum trbe_fault_action trbe_get_fault_act (u64 trbsr )
566+ static enum trbe_fault_action trbe_get_fault_act (struct perf_output_handle * handle ,
567+ u64 trbsr )
554568{
555569 int ec = get_trbe_ec (trbsr );
556570 int bsc = get_trbe_bsc (trbsr );
571+ struct trbe_buf * buf = etm_perf_sink_config (handle );
572+ struct trbe_cpudata * cpudata = buf -> cpudata ;
557573
558574 WARN_ON (is_trbe_running (trbsr ));
559575 if (is_trbe_trg (trbsr ) || is_trbe_abort (trbsr ))
@@ -562,10 +578,16 @@ static enum trbe_fault_action trbe_get_fault_act(u64 trbsr)
562578 if ((ec == TRBE_EC_STAGE1_ABORT ) || (ec == TRBE_EC_STAGE2_ABORT ))
563579 return TRBE_FAULT_ACT_FATAL ;
564580
565- if (is_trbe_wrap (trbsr ) && (ec == TRBE_EC_OTHERS ) && (bsc == TRBE_BSC_FILLED )) {
566- if (get_trbe_write_pointer () == get_trbe_base_pointer ())
567- return TRBE_FAULT_ACT_WRAP ;
568- }
581+ /*
582+ * If the trbe is affected by TRBE_WORKAROUND_OVERWRITE_FILL_MODE,
583+ * it might write data after a WRAP event in the fill mode.
584+ * Thus the check TRBPTR == TRBBASER will not be honored.
585+ */
586+ if ((is_trbe_wrap (trbsr ) && (ec == TRBE_EC_OTHERS ) && (bsc == TRBE_BSC_FILLED )) &&
587+ (trbe_may_overwrite_in_fill_mode (cpudata ) ||
588+ get_trbe_write_pointer () == get_trbe_base_pointer ()))
589+ return TRBE_FAULT_ACT_WRAP ;
590+
569591 return TRBE_FAULT_ACT_SPURIOUS ;
570592}
571593
@@ -574,6 +596,8 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle,
574596{
575597 u64 write ;
576598 u64 start_off , end_off ;
599+ u64 size ;
600+ u64 overwrite_skip = TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES ;
577601
578602 /*
579603 * If the TRBE has wrapped around the write pointer has
@@ -594,7 +618,18 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle,
594618
595619 if (WARN_ON_ONCE (end_off < start_off ))
596620 return 0 ;
597- return (end_off - start_off );
621+
622+ size = end_off - start_off ;
623+ /*
624+ * If the TRBE is affected by the following erratum, we must fill
625+ * the space we skipped with IGNORE packets. And we are always
626+ * guaranteed to have at least a PAGE_SIZE space in the buffer.
627+ */
628+ if (trbe_has_erratum (buf -> cpudata , TRBE_WORKAROUND_OVERWRITE_FILL_MODE ) &&
629+ !WARN_ON (size < overwrite_skip ))
630+ __trbe_pad_buf (buf , start_off , overwrite_skip );
631+
632+ return size ;
598633}
599634
600635static void * arm_trbe_alloc_buffer (struct coresight_device * csdev ,
@@ -713,7 +748,7 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev,
713748 clr_trbe_irq ();
714749 isb ();
715750
716- act = trbe_get_fault_act (status );
751+ act = trbe_get_fault_act (handle , status );
717752 /*
718753 * If this was not due to a WRAP event, we have some
719754 * errors and as such buffer is empty.
@@ -737,21 +772,117 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev,
737772 return size ;
738773}
739774
775+
776+ static int trbe_apply_work_around_before_enable (struct trbe_buf * buf )
777+ {
778+ /*
779+ * TRBE_WORKAROUND_OVERWRITE_FILL_MODE causes the TRBE to overwrite a few cache
780+ * line size from the "TRBBASER_EL1" in the event of a "FILL".
781+ * Thus, we could loose some amount of the trace at the base.
782+ *
783+ * Before Fix:
784+ *
785+ * normal-BASE head (normal-TRBPTR) tail (normal-LIMIT)
786+ * | \/ /
787+ * -------------------------------------------------------------
788+ * | Pg0 | Pg1 | | | PgN |
789+ * -------------------------------------------------------------
790+ *
791+ * In the normal course of action, we would set the TRBBASER to the
792+ * beginning of the ring-buffer (normal-BASE). But with the erratum,
793+ * the TRBE could overwrite the contents at the "normal-BASE", after
794+ * hitting the "normal-LIMIT", since it doesn't stop as expected. And
795+ * this is wrong. This could result in overwriting trace collected in
796+ * one of the previous runs, being consumed by the user. So we must
797+ * always make sure that the TRBBASER is within the region
798+ * [head, head+size]. Note that TRBBASER must be PAGE aligned,
799+ *
800+ * After moving the BASE:
801+ *
802+ * normal-BASE head (normal-TRBPTR) tail (normal-LIMIT)
803+ * | \/ /
804+ * -------------------------------------------------------------
805+ * | | |xyzdef. |.. tuvw| |
806+ * -------------------------------------------------------------
807+ * /
808+ * New-BASER
809+ *
810+ * Also, we would set the TRBPTR to head (after adjusting for
811+ * alignment) at normal-PTR. This would mean that the last few bytes
812+ * of the trace (say, "xyz") might overwrite the first few bytes of
813+ * trace written ("abc"). More importantly they will appear in what
814+ * userspace sees as the beginning of the trace, which is wrong. We may
815+ * not always have space to move the latest trace "xyz" to the correct
816+ * order as it must appear beyond the LIMIT. (i.e, [head..head+size]).
817+ * Thus it is easier to ignore those bytes than to complicate the
818+ * driver to move it, assuming that the erratum was triggered and
819+ * doing additional checks to see if there is indeed allowed space at
820+ * TRBLIMITR.LIMIT.
821+ *
822+ * Thus the full workaround will move the BASE and the PTR and would
823+ * look like (after padding at the skipped bytes at the end of
824+ * session) :
825+ *
826+ * normal-BASE head (normal-TRBPTR) tail (normal-LIMIT)
827+ * | \/ /
828+ * -------------------------------------------------------------
829+ * | | |///abc.. |.. rst| |
830+ * -------------------------------------------------------------
831+ * / |
832+ * New-BASER New-TRBPTR
833+ *
834+ * To summarize, with the work around:
835+ *
836+ * - We always align the offset for the next session to PAGE_SIZE
837+ * (This is to ensure we can program the TRBBASER to this offset
838+ * within the region [head...head+size]).
839+ *
840+ * - At TRBE enable:
841+ * - Set the TRBBASER to the page aligned offset of the current
842+ * proposed write offset. (which is guaranteed to be aligned
843+ * as above)
844+ * - Move the TRBPTR to skip first 256bytes (that might be
845+ * overwritten with the erratum). This ensures that the trace
846+ * generated in the session is not re-written.
847+ *
848+ * - At trace collection:
849+ * - Pad the 256bytes skipped above again with IGNORE packets.
850+ */
851+ if (trbe_has_erratum (buf -> cpudata , TRBE_WORKAROUND_OVERWRITE_FILL_MODE )) {
852+ if (WARN_ON (!IS_ALIGNED (buf -> trbe_write , PAGE_SIZE )))
853+ return - EINVAL ;
854+ buf -> trbe_hw_base = buf -> trbe_write ;
855+ buf -> trbe_write += TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES ;
856+ }
857+
858+ return 0 ;
859+ }
860+
740861static int __arm_trbe_enable (struct trbe_buf * buf ,
741862 struct perf_output_handle * handle )
742863{
864+ int ret = 0 ;
865+
743866 perf_aux_output_flag (handle , PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW );
744867 buf -> trbe_limit = compute_trbe_buffer_limit (handle );
745868 buf -> trbe_write = buf -> trbe_base + PERF_IDX2OFF (handle -> head , buf );
746869 if (buf -> trbe_limit == buf -> trbe_base ) {
747- trbe_stop_and_truncate_event ( handle ) ;
748- return - ENOSPC ;
870+ ret = - ENOSPC ;
871+ goto err ;
749872 }
750873 /* Set the base of the TRBE to the buffer base */
751874 buf -> trbe_hw_base = buf -> trbe_base ;
875+
876+ ret = trbe_apply_work_around_before_enable (buf );
877+ if (ret )
878+ goto err ;
879+
752880 * this_cpu_ptr (buf -> cpudata -> drvdata -> handle ) = handle ;
753881 trbe_enable_hw (buf );
754882 return 0 ;
883+ err :
884+ trbe_stop_and_truncate_event (handle );
885+ return ret ;
755886}
756887
757888static int arm_trbe_enable (struct coresight_device * csdev , u32 mode , void * data )
@@ -891,7 +1022,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
8911022 if (!is_perf_trbe (handle ))
8921023 return IRQ_NONE ;
8931024
894- act = trbe_get_fault_act (status );
1025+ act = trbe_get_fault_act (handle , status );
8951026 switch (act ) {
8961027 case TRBE_FAULT_ACT_WRAP :
8971028 truncated = !!trbe_handle_overflow (handle );
@@ -1043,7 +1174,22 @@ static void arm_trbe_probe_cpu(void *info)
10431174 */
10441175 trbe_check_errata (cpudata );
10451176
1046- cpudata -> trbe_align = cpudata -> trbe_hw_align ;
1177+ /*
1178+ * If the TRBE is affected by erratum TRBE_WORKAROUND_OVERWRITE_FILL_MODE,
1179+ * we must always program the TBRPTR_EL1, 256bytes from a page
1180+ * boundary, with TRBBASER_EL1 set to the page, to prevent
1181+ * TRBE over-writing 256bytes at TRBBASER_EL1 on FILL event.
1182+ *
1183+ * Thus make sure we always align our write pointer to a PAGE_SIZE,
1184+ * which also guarantees that we have at least a PAGE_SIZE space in
1185+ * the buffer (TRBLIMITR is PAGE aligned) and thus we can skip
1186+ * the required bytes at the base.
1187+ */
1188+ if (trbe_may_overwrite_in_fill_mode (cpudata ))
1189+ cpudata -> trbe_align = PAGE_SIZE ;
1190+ else
1191+ cpudata -> trbe_align = cpudata -> trbe_hw_align ;
1192+
10471193 cpudata -> trbe_flag = get_trbe_flag_update (trbidr );
10481194 cpudata -> cpu = cpu ;
10491195 cpudata -> drvdata = drvdata ;
0 commit comments