mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
Any IMPDEF events not printed out from a known core's IMPDEF list or for a completely unknown core will still not be shown to the user. Fix this by printing the remaining bits as comma separated raw numbers, e.g. "IMPDEF:1,2,3,4". Suggested-by: Al Grant <al.grant@arm.com> Reviewed-by: Leo Yan <leo.yan@arm.com> Signed-off-by: James Clark <james.clark@linaro.org> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Ian Rogers <irogers@google.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: John Garry <john.g.garry@oracle.com> Cc: Leo Yan <leo.yan@linux.dev> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Mike Leach <mike.leach@linaro.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Will Deacon <will@kernel.org> Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
615 lines
17 KiB
C
615 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Arm Statistical Profiling Extensions (SPE) support
|
|
* Copyright (c) 2017-2018, Arm Ltd.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <endian.h>
|
|
#include <byteswap.h>
|
|
#include <linux/bitmap.h>
|
|
#include <linux/bitops.h>
|
|
#include <stdarg.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/unaligned.h>
|
|
|
|
#include "arm-spe-pkt-decoder.h"
|
|
|
|
#include "../../arm64/include/asm/cputype.h"
|
|
|
|
static const char * const arm_spe_packet_name[] = {
|
|
[ARM_SPE_PAD] = "PAD",
|
|
[ARM_SPE_END] = "END",
|
|
[ARM_SPE_TIMESTAMP] = "TS",
|
|
[ARM_SPE_ADDRESS] = "ADDR",
|
|
[ARM_SPE_COUNTER] = "LAT",
|
|
[ARM_SPE_CONTEXT] = "CONTEXT",
|
|
[ARM_SPE_OP_TYPE] = "OP-TYPE",
|
|
[ARM_SPE_EVENTS] = "EVENTS",
|
|
[ARM_SPE_DATA_SOURCE] = "DATA-SOURCE",
|
|
};
|
|
|
|
const char *arm_spe_pkt_name(enum arm_spe_pkt_type type)
|
|
{
|
|
return arm_spe_packet_name[type];
|
|
}
|
|
|
|
/*
|
|
* Extracts the field "sz" from header bits and converts to bytes:
|
|
* 00 : byte (1)
|
|
* 01 : halfword (2)
|
|
* 10 : word (4)
|
|
* 11 : doubleword (8)
|
|
*/
|
|
static unsigned int arm_spe_payload_len(unsigned char hdr)
|
|
{
|
|
return 1U << ((hdr & GENMASK_ULL(5, 4)) >> 4);
|
|
}
|
|
|
|
static int arm_spe_get_payload(const unsigned char *buf, size_t len,
|
|
unsigned char ext_hdr,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
size_t payload_len = arm_spe_payload_len(buf[ext_hdr]);
|
|
|
|
if (len < 1 + ext_hdr + payload_len)
|
|
return ARM_SPE_NEED_MORE_BYTES;
|
|
|
|
buf += 1 + ext_hdr;
|
|
|
|
switch (payload_len) {
|
|
case 1: packet->payload = *(uint8_t *)buf; break;
|
|
case 2: packet->payload = get_unaligned_le16(buf); break;
|
|
case 4: packet->payload = get_unaligned_le32(buf); break;
|
|
case 8: packet->payload = get_unaligned_le64(buf); break;
|
|
default: return ARM_SPE_BAD_PACKET;
|
|
}
|
|
|
|
return 1 + ext_hdr + payload_len;
|
|
}
|
|
|
|
static int arm_spe_get_pad(struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_PAD;
|
|
return 1;
|
|
}
|
|
|
|
static int arm_spe_get_alignment(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
unsigned int alignment = 1 << ((buf[0] & 0xf) + 1);
|
|
|
|
if (len < alignment)
|
|
return ARM_SPE_NEED_MORE_BYTES;
|
|
|
|
packet->type = ARM_SPE_PAD;
|
|
return alignment - (((uintptr_t)buf) & (alignment - 1));
|
|
}
|
|
|
|
static int arm_spe_get_end(struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_END;
|
|
return 1;
|
|
}
|
|
|
|
static int arm_spe_get_timestamp(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_TIMESTAMP;
|
|
return arm_spe_get_payload(buf, len, 0, packet);
|
|
}
|
|
|
|
static int arm_spe_get_events(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_EVENTS;
|
|
|
|
/* we use index to identify Events with a less number of
|
|
* comparisons in arm_spe_pkt_desc(): E.g., the LLC-ACCESS,
|
|
* LLC-REFILL, and REMOTE-ACCESS events are identified if
|
|
* index > 1.
|
|
*/
|
|
packet->index = arm_spe_payload_len(buf[0]);
|
|
|
|
return arm_spe_get_payload(buf, len, 0, packet);
|
|
}
|
|
|
|
static int arm_spe_get_data_source(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_DATA_SOURCE;
|
|
return arm_spe_get_payload(buf, len, 0, packet);
|
|
}
|
|
|
|
static int arm_spe_get_context(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_CONTEXT;
|
|
packet->index = SPE_CTX_PKT_HDR_INDEX(buf[0]);
|
|
return arm_spe_get_payload(buf, len, 0, packet);
|
|
}
|
|
|
|
static int arm_spe_get_op_type(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_OP_TYPE;
|
|
packet->index = SPE_OP_PKT_HDR_CLASS(buf[0]);
|
|
return arm_spe_get_payload(buf, len, 0, packet);
|
|
}
|
|
|
|
static int arm_spe_get_counter(const unsigned char *buf, size_t len,
|
|
const unsigned char ext_hdr, struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_COUNTER;
|
|
|
|
if (ext_hdr)
|
|
packet->index = SPE_HDR_EXTENDED_INDEX(buf[0], buf[1]);
|
|
else
|
|
packet->index = SPE_HDR_SHORT_INDEX(buf[0]);
|
|
|
|
return arm_spe_get_payload(buf, len, ext_hdr, packet);
|
|
}
|
|
|
|
static int arm_spe_get_addr(const unsigned char *buf, size_t len,
|
|
const unsigned char ext_hdr, struct arm_spe_pkt *packet)
|
|
{
|
|
packet->type = ARM_SPE_ADDRESS;
|
|
|
|
if (ext_hdr)
|
|
packet->index = SPE_HDR_EXTENDED_INDEX(buf[0], buf[1]);
|
|
else
|
|
packet->index = SPE_HDR_SHORT_INDEX(buf[0]);
|
|
|
|
return arm_spe_get_payload(buf, len, ext_hdr, packet);
|
|
}
|
|
|
|
static int arm_spe_do_get_packet(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet)
|
|
{
|
|
unsigned int hdr;
|
|
unsigned char ext_hdr = 0;
|
|
|
|
memset(packet, 0, sizeof(struct arm_spe_pkt));
|
|
|
|
if (!len)
|
|
return ARM_SPE_NEED_MORE_BYTES;
|
|
|
|
hdr = buf[0];
|
|
|
|
if (hdr == SPE_HEADER0_PAD)
|
|
return arm_spe_get_pad(packet);
|
|
|
|
if (hdr == SPE_HEADER0_END) /* no timestamp at end of record */
|
|
return arm_spe_get_end(packet);
|
|
|
|
if (hdr == SPE_HEADER0_TIMESTAMP)
|
|
return arm_spe_get_timestamp(buf, len, packet);
|
|
|
|
if ((hdr & SPE_HEADER0_MASK1) == SPE_HEADER0_EVENTS)
|
|
return arm_spe_get_events(buf, len, packet);
|
|
|
|
if ((hdr & SPE_HEADER0_MASK1) == SPE_HEADER0_SOURCE)
|
|
return arm_spe_get_data_source(buf, len, packet);
|
|
|
|
if ((hdr & SPE_HEADER0_MASK2) == SPE_HEADER0_CONTEXT)
|
|
return arm_spe_get_context(buf, len, packet);
|
|
|
|
if ((hdr & SPE_HEADER0_MASK2) == SPE_HEADER0_OP_TYPE)
|
|
return arm_spe_get_op_type(buf, len, packet);
|
|
|
|
if ((hdr & SPE_HEADER0_MASK2) == SPE_HEADER0_EXTENDED) {
|
|
/* 16-bit extended format header */
|
|
if (len == 1)
|
|
return ARM_SPE_BAD_PACKET;
|
|
|
|
ext_hdr = 1;
|
|
hdr = buf[1];
|
|
if (hdr == SPE_HEADER1_ALIGNMENT)
|
|
return arm_spe_get_alignment(buf, len, packet);
|
|
}
|
|
|
|
/*
|
|
* The short format header's byte 0 or the extended format header's
|
|
* byte 1 has been assigned to 'hdr', which uses the same encoding for
|
|
* address packet and counter packet, so don't need to distinguish if
|
|
* it's short format or extended format and handle in once.
|
|
*/
|
|
if ((hdr & SPE_HEADER0_MASK3) == SPE_HEADER0_ADDRESS)
|
|
return arm_spe_get_addr(buf, len, ext_hdr, packet);
|
|
|
|
if ((hdr & SPE_HEADER0_MASK3) == SPE_HEADER0_COUNTER)
|
|
return arm_spe_get_counter(buf, len, ext_hdr, packet);
|
|
|
|
return ARM_SPE_BAD_PACKET;
|
|
}
|
|
|
|
int arm_spe_get_packet(const unsigned char *buf, size_t len,
|
|
struct arm_spe_pkt *packet, u64 midr)
|
|
{
|
|
int ret;
|
|
|
|
ret = arm_spe_do_get_packet(buf, len, packet);
|
|
packet->midr = midr;
|
|
/* put multiple consecutive PADs on the same line, up to
|
|
* the fixed-width output format of 16 bytes per line.
|
|
*/
|
|
if (ret > 0 && packet->type == ARM_SPE_PAD) {
|
|
while (ret < 16 && len > (size_t)ret && !buf[ret])
|
|
ret += 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int arm_spe_pkt_out_string(int *err, char **buf_p, size_t *blen,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int ret;
|
|
|
|
/* Bail out if any error occurred */
|
|
if (err && *err)
|
|
return *err;
|
|
|
|
va_start(ap, fmt);
|
|
ret = vsnprintf(*buf_p, *blen, fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (ret < 0) {
|
|
if (err && !*err)
|
|
*err = ret;
|
|
|
|
/*
|
|
* A return value of *blen or more means that the output was
|
|
* truncated and the buffer is overrun.
|
|
*/
|
|
} else if ((size_t)ret >= *blen) {
|
|
(*buf_p)[*blen - 1] = '\0';
|
|
|
|
/*
|
|
* Set *err to 'ret' to avoid overflow if tries to
|
|
* fill this buffer sequentially.
|
|
*/
|
|
if (err && !*err)
|
|
*err = ret;
|
|
} else {
|
|
*buf_p += ret;
|
|
*blen -= ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct ev_string {
|
|
u8 event;
|
|
const char *desc;
|
|
};
|
|
|
|
static const struct ev_string common_ev_strings[] = {
|
|
{ .event = EV_EXCEPTION_GEN, .desc = "EXCEPTION-GEN" },
|
|
{ .event = EV_RETIRED, .desc = "RETIRED" },
|
|
{ .event = EV_L1D_ACCESS, .desc = "L1D-ACCESS" },
|
|
{ .event = EV_L1D_REFILL, .desc = "L1D-REFILL" },
|
|
{ .event = EV_TLB_ACCESS, .desc = "TLB-ACCESS" },
|
|
{ .event = EV_TLB_WALK, .desc = "TLB-REFILL" },
|
|
{ .event = EV_NOT_TAKEN, .desc = "NOT-TAKEN" },
|
|
{ .event = EV_MISPRED, .desc = "MISPRED" },
|
|
{ .event = EV_LLC_ACCESS, .desc = "LLC-ACCESS" },
|
|
{ .event = EV_LLC_MISS, .desc = "LLC-REFILL" },
|
|
{ .event = EV_REMOTE_ACCESS, .desc = "REMOTE-ACCESS" },
|
|
{ .event = EV_ALIGNMENT, .desc = "ALIGNMENT" },
|
|
{ .event = EV_TRANSACTIONAL, .desc = "TXN" },
|
|
{ .event = EV_PARTIAL_PREDICATE, .desc = "SVE-PARTIAL-PRED" },
|
|
{ .event = EV_EMPTY_PREDICATE, .desc = "SVE-EMPTY-PRED" },
|
|
{ .event = EV_L2D_ACCESS, .desc = "L2D-ACCESS" },
|
|
{ .event = EV_L2D_MISS, .desc = "L2D-MISS" },
|
|
{ .event = EV_CACHE_DATA_MODIFIED, .desc = "HITM" },
|
|
{ .event = EV_RECENTLY_FETCHED, .desc = "LFB" },
|
|
{ .event = EV_DATA_SNOOPED, .desc = "SNOOPED" },
|
|
{ .event = EV_STREAMING_SVE_MODE, .desc = "STREAMING-SVE" },
|
|
{ .event = EV_SMCU, .desc = "SMCU" },
|
|
{ .event = 0, .desc = NULL },
|
|
};
|
|
|
|
static const struct ev_string n1_event_strings[] = {
|
|
{ .event = 12, .desc = "LATE-PREFETCH" },
|
|
{ .event = 0, .desc = NULL },
|
|
};
|
|
|
|
static u64 print_event_list(int *err, char **buf, size_t *buf_len,
|
|
const struct ev_string *ev_strings, u64 payload)
|
|
{
|
|
for (const struct ev_string *ev = ev_strings; ev->desc != NULL; ev++) {
|
|
if (payload & BIT_ULL(ev->event))
|
|
arm_spe_pkt_out_string(err, buf, buf_len, " %s", ev->desc);
|
|
payload &= ~BIT_ULL(ev->event);
|
|
}
|
|
return payload;
|
|
}
|
|
|
|
struct event_print_handle {
|
|
const struct midr_range *midr_ranges;
|
|
const struct ev_string *ev_strings;
|
|
};
|
|
|
|
#define EV_PRINT(range, strings) \
|
|
{ \
|
|
.midr_ranges = range, \
|
|
.ev_strings = strings, \
|
|
}
|
|
|
|
static const struct midr_range n1_event_encoding_cpus[] = {
|
|
MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1),
|
|
{},
|
|
};
|
|
|
|
static const struct event_print_handle event_print_handles[] = {
|
|
EV_PRINT(n1_event_encoding_cpus, n1_event_strings),
|
|
};
|
|
|
|
static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet,
|
|
char *buf, size_t buf_len)
|
|
{
|
|
u64 payload = packet->payload;
|
|
int err = 0;
|
|
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "EV");
|
|
payload = print_event_list(&err, &buf, &buf_len, common_ev_strings,
|
|
payload);
|
|
|
|
/* Try to decode IMPDEF bits for known CPUs */
|
|
for (unsigned int i = 0; i < ARRAY_SIZE(event_print_handles); i++) {
|
|
if (is_midr_in_range_list(packet->midr,
|
|
event_print_handles[i].midr_ranges))
|
|
payload = print_event_list(&err, &buf, &buf_len,
|
|
event_print_handles[i].ev_strings,
|
|
payload);
|
|
}
|
|
|
|
/*
|
|
* Print remaining IMPDEF bits that weren't printed above as raw
|
|
* "IMPDEF:1,2,3,4" etc.
|
|
*/
|
|
if (payload) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " IMPDEF:");
|
|
for (int i = 0; i < 64; i++) {
|
|
const char *sep = payload & (payload - 1) ? "," : "";
|
|
|
|
if (payload & BIT_ULL(i)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "%d%s", i,
|
|
sep);
|
|
payload &= ~BIT_ULL(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int arm_spe_pkt_desc_op_type(const struct arm_spe_pkt *packet,
|
|
char *buf, size_t buf_len)
|
|
{
|
|
u64 payload = packet->payload;
|
|
int err = 0;
|
|
|
|
switch (packet->index) {
|
|
case SPE_OP_PKT_HDR_CLASS_OTHER:
|
|
if (SPE_OP_PKT_OTHER_SUBCLASS_SVE(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "SVE-OTHER");
|
|
|
|
/* SVE effective vector length */
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " EVLEN %d",
|
|
SPE_OP_PKG_SVE_EVL(payload));
|
|
|
|
if (payload & SPE_OP_PKT_SVE_FP)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " FP");
|
|
if (payload & SPE_OP_PKT_SVE_PRED)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " PRED");
|
|
} else if (SPE_OP_PKT_OTHER_SUBCLASS_SME(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "SME-OTHER");
|
|
|
|
/* SME effective vector length or tile size */
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " ETS %d",
|
|
SPE_OP_PKG_SME_ETS(payload));
|
|
|
|
if (payload & SPE_OP_PKT_OTHER_FP)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " FP");
|
|
} else if (SPE_OP_PKT_OTHER_SUBCLASS_OTHER(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "OTHER");
|
|
if (payload & SPE_OP_PKT_OTHER_ASE)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " ASE");
|
|
if (payload & SPE_OP_PKT_OTHER_FP)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " FP");
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " %s",
|
|
payload & SPE_OP_PKT_COND ?
|
|
"COND-SELECT" : "INSN-OTHER");
|
|
}
|
|
break;
|
|
case SPE_OP_PKT_HDR_CLASS_LD_ST_ATOMIC:
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len,
|
|
payload & 0x1 ? "ST" : "LD");
|
|
|
|
if (SPE_OP_PKT_LDST_SUBCLASS_EXTENDED(payload)) {
|
|
if (payload & SPE_OP_PKT_AT)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " AT");
|
|
if (payload & SPE_OP_PKT_EXCL)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " EXCL");
|
|
if (payload & SPE_OP_PKT_AR)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " AR");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_SIMD_FP(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " SIMD-FP");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_GP_REG(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " GP-REG");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_UNSPEC_REG(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " UNSPEC-REG");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_NV_SYSREG(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " NV-SYSREG");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_MTE_TAG(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " MTE-TAG");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_MEMCPY(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " MEMCPY");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_MEMSET(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " MEMSET");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_SVE_SME_REG(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " SVE-SME-REG");
|
|
|
|
/* SVE effective vector length */
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " EVLEN %d",
|
|
SPE_OP_PKG_SVE_EVL(payload));
|
|
|
|
if (payload & SPE_OP_PKT_SVE_PRED)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " PRED");
|
|
if (payload & SPE_OP_PKT_SVE_SG)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " SG");
|
|
} else if (SPE_OP_PKT_LDST_SUBCLASS_GCS(payload)) {
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " GCS");
|
|
if (payload & SPE_OP_PKT_GCS_COMM)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " COMM");
|
|
}
|
|
break;
|
|
case SPE_OP_PKT_HDR_CLASS_BR_ERET:
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "B");
|
|
|
|
if (payload & SPE_OP_PKT_COND)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " COND");
|
|
if (payload & SPE_OP_PKT_INDIRECT_BRANCH)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " IND");
|
|
if (payload & SPE_OP_PKT_GCS)
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " GCS");
|
|
if (SPE_OP_PKT_CR_BL(payload))
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " CR-BL");
|
|
if (SPE_OP_PKT_CR_RET(payload))
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " CR-RET");
|
|
if (SPE_OP_PKT_CR_NON_BL_RET(payload))
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, " CR-NON-BL-RET");
|
|
break;
|
|
default:
|
|
/* Unknown index */
|
|
err = -1;
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int arm_spe_pkt_desc_addr(const struct arm_spe_pkt *packet,
|
|
char *buf, size_t buf_len)
|
|
{
|
|
int ns, el, idx = packet->index;
|
|
int ch, pat;
|
|
u64 payload = packet->payload;
|
|
int err = 0;
|
|
static const char *idx_name[] = {"PC", "TGT", "VA", "PA", "PBT"};
|
|
|
|
switch (idx) {
|
|
case SPE_ADDR_PKT_HDR_INDEX_INS:
|
|
case SPE_ADDR_PKT_HDR_INDEX_BRANCH:
|
|
case SPE_ADDR_PKT_HDR_INDEX_PREV_BRANCH:
|
|
ns = !!SPE_ADDR_PKT_GET_NS(payload);
|
|
el = SPE_ADDR_PKT_GET_EL(payload);
|
|
payload = SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len,
|
|
"%s 0x%llx el%d ns=%d",
|
|
idx_name[idx], payload, el, ns);
|
|
break;
|
|
case SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT:
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len,
|
|
"VA 0x%llx", payload);
|
|
break;
|
|
case SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS:
|
|
ns = !!SPE_ADDR_PKT_GET_NS(payload);
|
|
ch = !!SPE_ADDR_PKT_GET_CH(payload);
|
|
pat = SPE_ADDR_PKT_GET_PAT(payload);
|
|
payload = SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len,
|
|
"PA 0x%llx ns=%d ch=%d pat=%x",
|
|
payload, ns, ch, pat);
|
|
break;
|
|
default:
|
|
/* Unknown index */
|
|
err = -1;
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int arm_spe_pkt_desc_counter(const struct arm_spe_pkt *packet,
|
|
char *buf, size_t buf_len)
|
|
{
|
|
u64 payload = packet->payload;
|
|
const char *name = arm_spe_pkt_name(packet->type);
|
|
int err = 0;
|
|
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "%s %d ", name,
|
|
(unsigned short)payload);
|
|
|
|
switch (packet->index) {
|
|
case SPE_CNT_PKT_HDR_INDEX_TOTAL_LAT:
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "TOT");
|
|
break;
|
|
case SPE_CNT_PKT_HDR_INDEX_ISSUE_LAT:
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "ISSUE");
|
|
break;
|
|
case SPE_CNT_PKT_HDR_INDEX_TRANS_LAT:
|
|
arm_spe_pkt_out_string(&err, &buf, &buf_len, "XLAT");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int arm_spe_pkt_desc(const struct arm_spe_pkt *packet, char *buf,
|
|
size_t buf_len)
|
|
{
|
|
int idx = packet->index;
|
|
unsigned long long payload = packet->payload;
|
|
const char *name = arm_spe_pkt_name(packet->type);
|
|
char *buf_orig = buf;
|
|
size_t blen = buf_len;
|
|
int err = 0;
|
|
|
|
switch (packet->type) {
|
|
case ARM_SPE_BAD:
|
|
case ARM_SPE_PAD:
|
|
case ARM_SPE_END:
|
|
arm_spe_pkt_out_string(&err, &buf, &blen, "%s", name);
|
|
break;
|
|
case ARM_SPE_EVENTS:
|
|
err = arm_spe_pkt_desc_event(packet, buf, buf_len);
|
|
break;
|
|
case ARM_SPE_OP_TYPE:
|
|
err = arm_spe_pkt_desc_op_type(packet, buf, buf_len);
|
|
break;
|
|
case ARM_SPE_DATA_SOURCE:
|
|
case ARM_SPE_TIMESTAMP:
|
|
arm_spe_pkt_out_string(&err, &buf, &blen, "%s %lld", name, payload);
|
|
break;
|
|
case ARM_SPE_ADDRESS:
|
|
err = arm_spe_pkt_desc_addr(packet, buf, buf_len);
|
|
break;
|
|
case ARM_SPE_CONTEXT:
|
|
arm_spe_pkt_out_string(&err, &buf, &blen, "%s 0x%lx el%d",
|
|
name, (unsigned long)payload, idx + 1);
|
|
break;
|
|
case ARM_SPE_COUNTER:
|
|
err = arm_spe_pkt_desc_counter(packet, buf, buf_len);
|
|
break;
|
|
default:
|
|
/* Unknown packet type */
|
|
err = -1;
|
|
break;
|
|
}
|
|
|
|
/* Output raw data if detect any error */
|
|
if (err) {
|
|
err = 0;
|
|
arm_spe_pkt_out_string(&err, &buf_orig, &buf_len, "%s 0x%llx (%d)",
|
|
name, payload, packet->index);
|
|
}
|
|
|
|
return err;
|
|
}
|