1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Arm Statistical Profiling Extensions (SPE) support 4 * Copyright (c) 2017-2018, Arm Ltd. 5 */ 6 7#include <stdio.h> 8#include <string.h> 9#include <endian.h> 10#include <byteswap.h> 11 12#include "arm-spe-pkt-decoder.h" 13 14#define BIT(n) (1ULL << (n)) 15 16#define NS_FLAG BIT(63) 17#define EL_FLAG (BIT(62) | BIT(61)) 18 19#define SPE_HEADER0_PAD 0x0 20#define SPE_HEADER0_END 0x1 21#define SPE_HEADER0_ADDRESS 0x30 /* address packet (short) */ 22#define SPE_HEADER0_ADDRESS_MASK 0x38 23#define SPE_HEADER0_COUNTER 0x18 /* counter packet (short) */ 24#define SPE_HEADER0_COUNTER_MASK 0x38 25#define SPE_HEADER0_TIMESTAMP 0x71 26#define SPE_HEADER0_TIMESTAMP 0x71 27#define SPE_HEADER0_EVENTS 0x2 28#define SPE_HEADER0_EVENTS_MASK 0xf 29#define SPE_HEADER0_SOURCE 0x3 30#define SPE_HEADER0_SOURCE_MASK 0xf 31#define SPE_HEADER0_CONTEXT 0x24 32#define SPE_HEADER0_CONTEXT_MASK 0x3c 33#define SPE_HEADER0_OP_TYPE 0x8 34#define SPE_HEADER0_OP_TYPE_MASK 0x3c 35#define SPE_HEADER1_ALIGNMENT 0x0 36#define SPE_HEADER1_ADDRESS 0xb0 /* address packet (extended) */ 37#define SPE_HEADER1_ADDRESS_MASK 0xf8 38#define SPE_HEADER1_COUNTER 0x98 /* counter packet (extended) */ 39#define SPE_HEADER1_COUNTER_MASK 0xf8 40 41#if __BYTE_ORDER == __BIG_ENDIAN 42#define le16_to_cpu bswap_16 43#define le32_to_cpu bswap_32 44#define le64_to_cpu bswap_64 45#define memcpy_le64(d, s, n) do { \ 46 memcpy((d), (s), (n)); \ 47 *(d) = le64_to_cpu(*(d)); \ 48} while (0) 49#else 50#define le16_to_cpu 51#define le32_to_cpu 52#define le64_to_cpu 53#define memcpy_le64 memcpy 54#endif 55 56static const char * const arm_spe_packet_name[] = { 57 [ARM_SPE_PAD] = "PAD", 58 [ARM_SPE_END] = "END", 59 [ARM_SPE_TIMESTAMP] = "TS", 60 [ARM_SPE_ADDRESS] = "ADDR", 61 [ARM_SPE_COUNTER] = "LAT", 62 [ARM_SPE_CONTEXT] = "CONTEXT", 63 [ARM_SPE_OP_TYPE] = "OP-TYPE", 64 [ARM_SPE_EVENTS] = "EVENTS", 65 [ARM_SPE_DATA_SOURCE] = "DATA-SOURCE", 66}; 67 68const char *arm_spe_pkt_name(enum arm_spe_pkt_type type) 69{ 70 return arm_spe_packet_name[type]; 71} 72 73/* return ARM SPE payload size from its encoding, 74 * which is in bits 5:4 of the byte. 75 * 00 : byte 76 * 01 : halfword (2) 77 * 10 : word (4) 78 * 11 : doubleword (8) 79 */ 80static int payloadlen(unsigned char byte) 81{ 82 return 1 << ((byte & 0x30) >> 4); 83} 84 85static int arm_spe_get_payload(const unsigned char *buf, size_t len, 86 struct arm_spe_pkt *packet) 87{ 88 size_t payload_len = payloadlen(buf[0]); 89 90 if (len < 1 + payload_len) 91 return ARM_SPE_NEED_MORE_BYTES; 92 93 buf++; 94 95 switch (payload_len) { 96 case 1: packet->payload = *(uint8_t *)buf; break; 97 case 2: packet->payload = le16_to_cpu(*(uint16_t *)buf); break; 98 case 4: packet->payload = le32_to_cpu(*(uint32_t *)buf); break; 99 case 8: packet->payload = le64_to_cpu(*(uint64_t *)buf); break; 100 default: return ARM_SPE_BAD_PACKET; 101 } 102 103 return 1 + payload_len; 104} 105 106static int arm_spe_get_pad(struct arm_spe_pkt *packet) 107{ 108 packet->type = ARM_SPE_PAD; 109 return 1; 110} 111 112static int arm_spe_get_alignment(const unsigned char *buf, size_t len, 113 struct arm_spe_pkt *packet) 114{ 115 unsigned int alignment = 1 << ((buf[0] & 0xf) + 1); 116 117 if (len < alignment) 118 return ARM_SPE_NEED_MORE_BYTES; 119 120 packet->type = ARM_SPE_PAD; 121 return alignment - (((uintptr_t)buf) & (alignment - 1)); 122} 123 124static int arm_spe_get_end(struct arm_spe_pkt *packet) 125{ 126 packet->type = ARM_SPE_END; 127 return 1; 128} 129 130static int arm_spe_get_timestamp(const unsigned char *buf, size_t len, 131 struct arm_spe_pkt *packet) 132{ 133 packet->type = ARM_SPE_TIMESTAMP; 134 return arm_spe_get_payload(buf, len, packet); 135} 136 137static int arm_spe_get_events(const unsigned char *buf, size_t len, 138 struct arm_spe_pkt *packet) 139{ 140 int ret = arm_spe_get_payload(buf, len, packet); 141 142 packet->type = ARM_SPE_EVENTS; 143 144 /* we use index to identify Events with a less number of 145 * comparisons in arm_spe_pkt_desc(): E.g., the LLC-ACCESS, 146 * LLC-REFILL, and REMOTE-ACCESS events are identified iff 147 * index > 1. 148 */ 149 packet->index = ret - 1; 150 151 return ret; 152} 153 154static int arm_spe_get_data_source(const unsigned char *buf, size_t len, 155 struct arm_spe_pkt *packet) 156{ 157 packet->type = ARM_SPE_DATA_SOURCE; 158 return arm_spe_get_payload(buf, len, packet); 159} 160 161static int arm_spe_get_context(const unsigned char *buf, size_t len, 162 struct arm_spe_pkt *packet) 163{ 164 packet->type = ARM_SPE_CONTEXT; 165 packet->index = buf[0] & 0x3; 166 167 return arm_spe_get_payload(buf, len, packet); 168} 169 170static int arm_spe_get_op_type(const unsigned char *buf, size_t len, 171 struct arm_spe_pkt *packet) 172{ 173 packet->type = ARM_SPE_OP_TYPE; 174 packet->index = buf[0] & 0x3; 175 return arm_spe_get_payload(buf, len, packet); 176} 177 178static int arm_spe_get_counter(const unsigned char *buf, size_t len, 179 const unsigned char ext_hdr, struct arm_spe_pkt *packet) 180{ 181 if (len < 2) 182 return ARM_SPE_NEED_MORE_BYTES; 183 184 packet->type = ARM_SPE_COUNTER; 185 if (ext_hdr) 186 packet->index = ((buf[0] & 0x3) << 3) | (buf[1] & 0x7); 187 else 188 packet->index = buf[0] & 0x7; 189 190 packet->payload = le16_to_cpu(*(uint16_t *)(buf + 1)); 191 192 return 1 + ext_hdr + 2; 193} 194 195static int arm_spe_get_addr(const unsigned char *buf, size_t len, 196 const unsigned char ext_hdr, struct arm_spe_pkt *packet) 197{ 198 if (len < 8) 199 return ARM_SPE_NEED_MORE_BYTES; 200 201 packet->type = ARM_SPE_ADDRESS; 202 if (ext_hdr) 203 packet->index = ((buf[0] & 0x3) << 3) | (buf[1] & 0x7); 204 else 205 packet->index = buf[0] & 0x7; 206 207 memcpy_le64(&packet->payload, buf + 1, 8); 208 209 return 1 + ext_hdr + 8; 210} 211 212static int arm_spe_do_get_packet(const unsigned char *buf, size_t len, 213 struct arm_spe_pkt *packet) 214{ 215 unsigned int byte; 216 217 memset(packet, 0, sizeof(struct arm_spe_pkt)); 218 219 if (!len) 220 return ARM_SPE_NEED_MORE_BYTES; 221 222 byte = buf[0]; 223 if (byte == SPE_HEADER0_PAD) 224 return arm_spe_get_pad(packet); 225 else if (byte == SPE_HEADER0_END) /* no timestamp at end of record */ 226 return arm_spe_get_end(packet); 227 else if (byte & 0xc0 /* 0y11xxxxxx */) { 228 if (byte & 0x80) { 229 if ((byte & SPE_HEADER0_ADDRESS_MASK) == SPE_HEADER0_ADDRESS) 230 return arm_spe_get_addr(buf, len, 0, packet); 231 if ((byte & SPE_HEADER0_COUNTER_MASK) == SPE_HEADER0_COUNTER) 232 return arm_spe_get_counter(buf, len, 0, packet); 233 } else 234 if (byte == SPE_HEADER0_TIMESTAMP) 235 return arm_spe_get_timestamp(buf, len, packet); 236 else if ((byte & SPE_HEADER0_EVENTS_MASK) == SPE_HEADER0_EVENTS) 237 return arm_spe_get_events(buf, len, packet); 238 else if ((byte & SPE_HEADER0_SOURCE_MASK) == SPE_HEADER0_SOURCE) 239 return arm_spe_get_data_source(buf, len, packet); 240 else if ((byte & SPE_HEADER0_CONTEXT_MASK) == SPE_HEADER0_CONTEXT) 241 return arm_spe_get_context(buf, len, packet); 242 else if ((byte & SPE_HEADER0_OP_TYPE_MASK) == SPE_HEADER0_OP_TYPE) 243 return arm_spe_get_op_type(buf, len, packet); 244 } else if ((byte & 0xe0) == 0x20 /* 0y001xxxxx */) { 245 /* 16-bit header */ 246 byte = buf[1]; 247 if (byte == SPE_HEADER1_ALIGNMENT) 248 return arm_spe_get_alignment(buf, len, packet); 249 else if ((byte & SPE_HEADER1_ADDRESS_MASK) == SPE_HEADER1_ADDRESS) 250 return arm_spe_get_addr(buf, len, 1, packet); 251 else if ((byte & SPE_HEADER1_COUNTER_MASK) == SPE_HEADER1_COUNTER) 252 return arm_spe_get_counter(buf, len, 1, packet); 253 } 254 255 return ARM_SPE_BAD_PACKET; 256} 257 258int arm_spe_get_packet(const unsigned char *buf, size_t len, 259 struct arm_spe_pkt *packet) 260{ 261 int ret; 262 263 ret = arm_spe_do_get_packet(buf, len, packet); 264 /* put multiple consecutive PADs on the same line, up to 265 * the fixed-width output format of 16 bytes per line. 266 */ 267 if (ret > 0 && packet->type == ARM_SPE_PAD) { 268 while (ret < 16 && len > (size_t)ret && !buf[ret]) 269 ret += 1; 270 } 271 return ret; 272} 273 274int arm_spe_pkt_desc(const struct arm_spe_pkt *packet, char *buf, 275 size_t buf_len) 276{ 277 int ret, ns, el, idx = packet->index; 278 unsigned long long payload = packet->payload; 279 const char *name = arm_spe_pkt_name(packet->type); 280 281 switch (packet->type) { 282 case ARM_SPE_BAD: 283 case ARM_SPE_PAD: 284 case ARM_SPE_END: 285 return snprintf(buf, buf_len, "%s", name); 286 case ARM_SPE_EVENTS: { 287 size_t blen = buf_len; 288 289 ret = 0; 290 ret = snprintf(buf, buf_len, "EV"); 291 buf += ret; 292 blen -= ret; 293 if (payload & 0x1) { 294 ret = snprintf(buf, buf_len, " EXCEPTION-GEN"); 295 buf += ret; 296 blen -= ret; 297 } 298 if (payload & 0x2) { 299 ret = snprintf(buf, buf_len, " RETIRED"); 300 buf += ret; 301 blen -= ret; 302 } 303 if (payload & 0x4) { 304 ret = snprintf(buf, buf_len, " L1D-ACCESS"); 305 buf += ret; 306 blen -= ret; 307 } 308 if (payload & 0x8) { 309 ret = snprintf(buf, buf_len, " L1D-REFILL"); 310 buf += ret; 311 blen -= ret; 312 } 313 if (payload & 0x10) { 314 ret = snprintf(buf, buf_len, " TLB-ACCESS"); 315 buf += ret; 316 blen -= ret; 317 } 318 if (payload & 0x20) { 319 ret = snprintf(buf, buf_len, " TLB-REFILL"); 320 buf += ret; 321 blen -= ret; 322 } 323 if (payload & 0x40) { 324 ret = snprintf(buf, buf_len, " NOT-TAKEN"); 325 buf += ret; 326 blen -= ret; 327 } 328 if (payload & 0x80) { 329 ret = snprintf(buf, buf_len, " MISPRED"); 330 buf += ret; 331 blen -= ret; 332 } 333 if (idx > 1) { 334 if (payload & 0x100) { 335 ret = snprintf(buf, buf_len, " LLC-ACCESS"); 336 buf += ret; 337 blen -= ret; 338 } 339 if (payload & 0x200) { 340 ret = snprintf(buf, buf_len, " LLC-REFILL"); 341 buf += ret; 342 blen -= ret; 343 } 344 if (payload & 0x400) { 345 ret = snprintf(buf, buf_len, " REMOTE-ACCESS"); 346 buf += ret; 347 blen -= ret; 348 } 349 } 350 if (ret < 0) 351 return ret; 352 blen -= ret; 353 return buf_len - blen; 354 } 355 case ARM_SPE_OP_TYPE: 356 switch (idx) { 357 case 0: return snprintf(buf, buf_len, "%s", payload & 0x1 ? 358 "COND-SELECT" : "INSN-OTHER"); 359 case 1: { 360 size_t blen = buf_len; 361 362 if (payload & 0x1) 363 ret = snprintf(buf, buf_len, "ST"); 364 else 365 ret = snprintf(buf, buf_len, "LD"); 366 buf += ret; 367 blen -= ret; 368 if (payload & 0x2) { 369 if (payload & 0x4) { 370 ret = snprintf(buf, buf_len, " AT"); 371 buf += ret; 372 blen -= ret; 373 } 374 if (payload & 0x8) { 375 ret = snprintf(buf, buf_len, " EXCL"); 376 buf += ret; 377 blen -= ret; 378 } 379 if (payload & 0x10) { 380 ret = snprintf(buf, buf_len, " AR"); 381 buf += ret; 382 blen -= ret; 383 } 384 } else if (payload & 0x4) { 385 ret = snprintf(buf, buf_len, " SIMD-FP"); 386 buf += ret; 387 blen -= ret; 388 } 389 if (ret < 0) 390 return ret; 391 blen -= ret; 392 return buf_len - blen; 393 } 394 case 2: { 395 size_t blen = buf_len; 396 397 ret = snprintf(buf, buf_len, "B"); 398 buf += ret; 399 blen -= ret; 400 if (payload & 0x1) { 401 ret = snprintf(buf, buf_len, " COND"); 402 buf += ret; 403 blen -= ret; 404 } 405 if (payload & 0x2) { 406 ret = snprintf(buf, buf_len, " IND"); 407 buf += ret; 408 blen -= ret; 409 } 410 if (ret < 0) 411 return ret; 412 blen -= ret; 413 return buf_len - blen; 414 } 415 default: return 0; 416 } 417 case ARM_SPE_DATA_SOURCE: 418 case ARM_SPE_TIMESTAMP: 419 return snprintf(buf, buf_len, "%s %lld", name, payload); 420 case ARM_SPE_ADDRESS: 421 switch (idx) { 422 case 0: 423 case 1: ns = !!(packet->payload & NS_FLAG); 424 el = (packet->payload & EL_FLAG) >> 61; 425 payload &= ~(0xffULL << 56); 426 return snprintf(buf, buf_len, "%s 0x%llx el%d ns=%d", 427 (idx == 1) ? "TGT" : "PC", payload, el, ns); 428 case 2: return snprintf(buf, buf_len, "VA 0x%llx", payload); 429 case 3: ns = !!(packet->payload & NS_FLAG); 430 payload &= ~(0xffULL << 56); 431 return snprintf(buf, buf_len, "PA 0x%llx ns=%d", 432 payload, ns); 433 default: return 0; 434 } 435 case ARM_SPE_CONTEXT: 436 return snprintf(buf, buf_len, "%s 0x%lx el%d", name, 437 (unsigned long)payload, idx + 1); 438 case ARM_SPE_COUNTER: { 439 size_t blen = buf_len; 440 441 ret = snprintf(buf, buf_len, "%s %d ", name, 442 (unsigned short)payload); 443 buf += ret; 444 blen -= ret; 445 switch (idx) { 446 case 0: ret = snprintf(buf, buf_len, "TOT"); break; 447 case 1: ret = snprintf(buf, buf_len, "ISSUE"); break; 448 case 2: ret = snprintf(buf, buf_len, "XLAT"); break; 449 default: ret = 0; 450 } 451 if (ret < 0) 452 return ret; 453 blen -= ret; 454 return buf_len - blen; 455 } 456 default: 457 break; 458 } 459 460 return snprintf(buf, buf_len, "%s 0x%llx (%d)", 461 name, payload, packet->index); 462} 463