linux-stable/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
Wei Li 05e91e7fe2 perf arm-spe: Add support for ARMv8.3-SPE
This patch is to support Armv8.3 extension for SPE, it adds alignment
field in the Events packet and it supports the Scalable Vector Extension
(SVE) for Operation packet and Events packet with two additions:

  - The vector length for SVE operations in the Operation Type packet;
  - The incomplete predicate and empty predicate fields in the Events
    packet.

Signed-off-by: Wei Li <liwei391@huawei.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Reviewed-by: Andre Przywara <andre.przywara@arm.com>
Acked-by: Will Deacon <will@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Al Grant <Al.Grant@arm.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Dave Martin <Dave.Martin@arm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20201119152441.6972-17-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-26 09:31:46 -03:00

533 lines
14 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/bitops.h>
#include <stdarg.h>
#include "arm-spe-pkt-decoder.h"
#if __BYTE_ORDER == __BIG_ENDIAN
#define le16_to_cpu bswap_16
#define le32_to_cpu bswap_32
#define le64_to_cpu bswap_64
#define memcpy_le64(d, s, n) do { \
memcpy((d), (s), (n)); \
*(d) = le64_to_cpu(*(d)); \
} while (0)
#else
#define le16_to_cpu
#define le32_to_cpu
#define le64_to_cpu
#define memcpy_le64 memcpy
#endif
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 = le16_to_cpu(*(uint16_t *)buf); break;
case 4: packet->payload = le32_to_cpu(*(uint32_t *)buf); break;
case 8: packet->payload = le64_to_cpu(*(uint64_t *)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 */
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)
{
int ret;
ret = arm_spe_do_get_packet(buf, len, packet);
/* 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;
}
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");
if (payload & BIT(EV_EXCEPTION_GEN))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " EXCEPTION-GEN");
if (payload & BIT(EV_RETIRED))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " RETIRED");
if (payload & BIT(EV_L1D_ACCESS))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " L1D-ACCESS");
if (payload & BIT(EV_L1D_REFILL))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " L1D-REFILL");
if (payload & BIT(EV_TLB_ACCESS))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " TLB-ACCESS");
if (payload & BIT(EV_TLB_WALK))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " TLB-REFILL");
if (payload & BIT(EV_NOT_TAKEN))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " NOT-TAKEN");
if (payload & BIT(EV_MISPRED))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " MISPRED");
if (payload & BIT(EV_LLC_ACCESS))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " LLC-ACCESS");
if (payload & BIT(EV_LLC_MISS))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " LLC-REFILL");
if (payload & BIT(EV_REMOTE_ACCESS))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " REMOTE-ACCESS");
if (payload & BIT(EV_ALIGNMENT))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " ALIGNMENT");
if (payload & BIT(EV_PARTIAL_PREDICATE))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " SVE-PARTIAL-PRED");
if (payload & BIT(EV_EMPTY_PREDICATE))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " SVE-EMPTY-PRED");
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_IS_OTHER_SVE_OP(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 {
arm_spe_pkt_out_string(&err, &buf, &buf_len, "OTHER");
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_IS_LDST_ATOMIC(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");
}
switch (SPE_OP_PKT_LDST_SUBCLASS_GET(payload)) {
case SPE_OP_PKT_LDST_SUBCLASS_SIMD_FP:
arm_spe_pkt_out_string(&err, &buf, &buf_len, " SIMD-FP");
break;
case SPE_OP_PKT_LDST_SUBCLASS_GP_REG:
arm_spe_pkt_out_string(&err, &buf, &buf_len, " GP-REG");
break;
case SPE_OP_PKT_LDST_SUBCLASS_UNSPEC_REG:
arm_spe_pkt_out_string(&err, &buf, &buf_len, " UNSPEC-REG");
break;
case SPE_OP_PKT_LDST_SUBCLASS_NV_SYSREG:
arm_spe_pkt_out_string(&err, &buf, &buf_len, " NV-SYSREG");
break;
default:
break;
}
if (SPE_OP_PKT_IS_LDST_SVE(payload)) {
/* 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");
}
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 (SPE_OP_PKT_IS_INDIRECT_BRANCH(payload))
arm_spe_pkt_out_string(&err, &buf, &buf_len, " IND");
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;
switch (idx) {
case SPE_ADDR_PKT_HDR_INDEX_INS:
case SPE_ADDR_PKT_HDR_INDEX_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 == 1) ? "TGT" : "PC", 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;
}