Skip to content

Commit f9efc79

Browse files
Suzuki K Poulosemathieupoirier
authored andcommitted
coresight: trbe: Work around write to out of range
TRBE implementations affected by Arm erratum (2253138 or 2224489), could write to the next address after the TRBLIMITR.LIMIT, instead of wrapping to the TRBBASER. This implies that the TRBE could potentially corrupt : - A page used by the rest of the kernel/user (if the LIMIT = end of perf ring buffer) - A page within the ring buffer, but outside the driver's range. [head, head + size]. This may contain some trace data, may be consumed by the userspace. We workaround this erratum by : - Making sure that there is at least an extra PAGE space left in the TRBE's range than we normally assign. This will be additional to other restrictions (e.g, the TRBE alignment for working around TRBE_WORKAROUND_OVERWRITE_IN_FILL_MODE, where there is a minimum of PAGE_SIZE. Thus we would have 2 * PAGE_SIZE) - Adjust the LIMIT to leave the last PAGE_SIZE out of the TRBE's allowed range (i.e, TRBEBASER...TRBLIMITR.LIMIT), by : TRBLIMITR.LIMIT -= PAGE_SIZE Cc: Anshuman Khandual <[email protected]> Cc: Mathieu Poirier <[email protected]> Cc: Mike Leach <[email protected]> Cc: Leo Yan <[email protected]> Reviewed-by: Anshuman Khandual <[email protected]> Signed-off-by: Suzuki K Poulose <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mathieu Poirier <[email protected]>
1 parent adf35d0 commit f9efc79

File tree

1 file changed

+62
-1
lines changed

1 file changed

+62
-1
lines changed

drivers/hwtracing/coresight/coresight-trbe.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ struct trbe_buf {
9090
* - Streamlined detection of erratum across the system
9191
*/
9292
#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0
93+
#define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE 1
9394

9495
static int trbe_errata_cpucaps[] = {
9596
[TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE,
97+
[TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE,
9698
-1, /* Sentinel, must be the last entry */
9799
};
98100

@@ -160,6 +162,11 @@ static inline bool trbe_may_overwrite_in_fill_mode(struct trbe_cpudata *cpudata)
160162
return trbe_has_erratum(cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE);
161163
}
162164

165+
static inline bool trbe_may_write_out_of_range(struct trbe_cpudata *cpudata)
166+
{
167+
return trbe_has_erratum(cpudata, TRBE_WORKAROUND_WRITE_OUT_OF_RANGE);
168+
}
169+
163170
static int trbe_alloc_node(struct perf_event *event)
164171
{
165172
if (event->cpu == -1)
@@ -305,7 +312,21 @@ static unsigned long trbe_snapshot_offset(struct perf_output_handle *handle)
305312

306313
static u64 trbe_min_trace_buf_size(struct perf_output_handle *handle)
307314
{
308-
return TRBE_TRACE_MIN_BUF_SIZE;
315+
u64 size = TRBE_TRACE_MIN_BUF_SIZE;
316+
struct trbe_buf *buf = etm_perf_sink_config(handle);
317+
struct trbe_cpudata *cpudata = buf->cpudata;
318+
319+
/*
320+
* When the TRBE is affected by an erratum that could make it
321+
* write to the next "virtually addressed" page beyond the LIMIT.
322+
* We need to make sure there is always a PAGE after the LIMIT,
323+
* within the buffer. Thus we ensure there is at least an extra
324+
* page than normal. With this we could then adjust the LIMIT
325+
* pointer down by a PAGE later.
326+
*/
327+
if (trbe_may_write_out_of_range(cpudata))
328+
size += PAGE_SIZE;
329+
return size;
309330
}
310331

311332
/*
@@ -611,6 +632,17 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle,
611632
/*
612633
* If the TRBE has wrapped around the write pointer has
613634
* wrapped and should be treated as limit.
635+
*
636+
* When the TRBE is affected by TRBE_WORKAROUND_WRITE_OUT_OF_RANGE,
637+
* it may write upto 64bytes beyond the "LIMIT". The driver already
638+
* keeps a valid page next to the LIMIT and we could potentially
639+
* consume the trace data that may have been collected there. But we
640+
* cannot be really sure it is available, and the TRBPTR may not
641+
* indicate the same. Also, affected cores are also affected by another
642+
* erratum which forces the PAGE_SIZE alignment on the TRBPTR, and thus
643+
* could potentially pad an entire PAGE_SIZE - 64bytes, to get those
644+
* 64bytes. Thus we ignore the potential triggering of the erratum
645+
* on WRAP and limit the data to LIMIT.
614646
*/
615647
if (wrap)
616648
write = get_trbe_limit_pointer();
@@ -864,6 +896,35 @@ static int trbe_apply_work_around_before_enable(struct trbe_buf *buf)
864896
buf->trbe_write += TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES;
865897
}
866898

899+
/*
900+
* TRBE_WORKAROUND_WRITE_OUT_OF_RANGE could cause the TRBE to write to
901+
* the next page after the TRBLIMITR.LIMIT. For perf, the "next page"
902+
* may be:
903+
* - The page beyond the ring buffer. This could mean, TRBE could
904+
* corrupt another entity (kernel / user)
905+
* - A portion of the "ring buffer" consumed by the userspace.
906+
* i.e, a page outisde [head, head + size].
907+
*
908+
* We work around this by:
909+
* - Making sure that we have at least an extra space of PAGE left
910+
* in the ring buffer [head, head + size], than we normally do
911+
* without the erratum. See trbe_min_trace_buf_size().
912+
*
913+
* - Adjust the TRBLIMITR.LIMIT to leave the extra PAGE outside
914+
* the TRBE's range (i.e [TRBBASER, TRBLIMITR.LIMI] ).
915+
*/
916+
if (trbe_has_erratum(buf->cpudata, TRBE_WORKAROUND_WRITE_OUT_OF_RANGE)) {
917+
s64 space = buf->trbe_limit - buf->trbe_write;
918+
/*
919+
* We must have more than a PAGE_SIZE worth space in the proposed
920+
* range for the TRBE.
921+
*/
922+
if (WARN_ON(space <= PAGE_SIZE ||
923+
!IS_ALIGNED(buf->trbe_limit, PAGE_SIZE)))
924+
return -EINVAL;
925+
buf->trbe_limit -= PAGE_SIZE;
926+
}
927+
867928
return 0;
868929
}
869930

0 commit comments

Comments
 (0)