| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * HiSilicon PCIe Trace and Tuning (PTT) support |
| * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <endian.h> |
| #include <byteswap.h> |
| #include <linux/bitops.h> |
| #include <linux/kernel.h> |
| #include <linux/unaligned.h> |
| #include <stdarg.h> |
| |
| #include "../color.h" |
| #include "hisi-ptt-pkt-decoder.h" |
| |
| /* |
| * For 8DW format, the bit[31:11] of DW0 is always 0x1fffff, which can be |
| * used to distinguish the data format. |
| * 8DW format is like: |
| * bits [ 31:11 ][ 10:0 ] |
| * |---------------------------------------|-------------------| |
| * DW0 [ 0x1fffff ][ Reserved (0x7ff) ] |
| * DW1 [ Prefix ] |
| * DW2 [ Header DW0 ] |
| * DW3 [ Header DW1 ] |
| * DW4 [ Header DW2 ] |
| * DW5 [ Header DW3 ] |
| * DW6 [ Reserved (0x0) ] |
| * DW7 [ Time ] |
| * |
| * 4DW format is like: |
| * bits [31:30] [ 29:25 ][24][23][22][21][ 20:11 ][ 10:0 ] |
| * |-----|---------|---|---|---|---|-------------|-------------| |
| * DW0 [ Fmt ][ Type ][T9][T8][TH][SO][ Length ][ Time ] |
| * DW1 [ Header DW1 ] |
| * DW2 [ Header DW2 ] |
| * DW3 [ Header DW3 ] |
| */ |
| |
| enum hisi_ptt_8dw_pkt_field_type { |
| HISI_PTT_8DW_CHK_AND_RSV0, |
| HISI_PTT_8DW_PREFIX, |
| HISI_PTT_8DW_HEAD0, |
| HISI_PTT_8DW_HEAD1, |
| HISI_PTT_8DW_HEAD2, |
| HISI_PTT_8DW_HEAD3, |
| HISI_PTT_8DW_RSV1, |
| HISI_PTT_8DW_TIME, |
| HISI_PTT_8DW_TYPE_MAX |
| }; |
| |
| enum hisi_ptt_4dw_pkt_field_type { |
| HISI_PTT_4DW_HEAD1, |
| HISI_PTT_4DW_HEAD2, |
| HISI_PTT_4DW_HEAD3, |
| HISI_PTT_4DW_TYPE_MAX |
| }; |
| |
| static const char * const hisi_ptt_8dw_pkt_field_name[] = { |
| [HISI_PTT_8DW_PREFIX] = "Prefix", |
| [HISI_PTT_8DW_HEAD0] = "Header DW0", |
| [HISI_PTT_8DW_HEAD1] = "Header DW1", |
| [HISI_PTT_8DW_HEAD2] = "Header DW2", |
| [HISI_PTT_8DW_HEAD3] = "Header DW3", |
| [HISI_PTT_8DW_TIME] = "Time" |
| }; |
| |
| static const char * const hisi_ptt_4dw_pkt_field_name[] = { |
| [HISI_PTT_4DW_HEAD1] = "Header DW1", |
| [HISI_PTT_4DW_HEAD2] = "Header DW2", |
| [HISI_PTT_4DW_HEAD3] = "Header DW3", |
| }; |
| |
| static void hisi_ptt_print_pkt(const unsigned char *buf, int pos, const char *desc) |
| { |
| const char *color = PERF_COLOR_BLUE; |
| uint8_t byte; |
| uint32_t dw; |
| int i; |
| |
| dw = get_unaligned_le32(buf + pos); |
| printf("."); |
| color_fprintf(stdout, color, " %08x: ", pos); |
| for (i = 0; i < HISI_PTT_FIELD_LENGTH; i++) { |
| byte = (dw >> (24 - i * 8)) & 0xFF; |
| color_fprintf(stdout, color, "%02x ", byte); |
| } |
| for (i = 0; i < HISI_PTT_MAX_SPACE_LEN; i++) |
| color_fprintf(stdout, color, " "); |
| color_fprintf(stdout, color, " %s\n", desc); |
| } |
| |
| static int hisi_ptt_8dw_pkt_desc(const unsigned char *buf, int pos) |
| { |
| int i; |
| |
| for (i = 0; i < HISI_PTT_8DW_TYPE_MAX; i++) { |
| /* Do not show 8DW check field and reserved fields */ |
| if (i == HISI_PTT_8DW_CHK_AND_RSV0 || i == HISI_PTT_8DW_RSV1) { |
| pos += HISI_PTT_FIELD_LENGTH; |
| continue; |
| } |
| |
| hisi_ptt_print_pkt(buf, pos, hisi_ptt_8dw_pkt_field_name[i]); |
| pos += HISI_PTT_FIELD_LENGTH; |
| } |
| |
| return hisi_ptt_pkt_size[HISI_PTT_8DW_PKT]; |
| } |
| |
| static void hisi_ptt_4dw_print_dw0(const unsigned char *buf, int pos) |
| { |
| const char *color = PERF_COLOR_BLUE; |
| uint8_t byte; |
| uint32_t dw; |
| int i; |
| |
| dw = get_unaligned_le32(buf + pos); |
| printf("."); |
| color_fprintf(stdout, color, " %08x: ", pos); |
| for (i = 0; i < HISI_PTT_FIELD_LENGTH; i++) { |
| byte = (dw >> (24 - i * 8)) & 0xFF; |
| color_fprintf(stdout, color, "%02x ", byte); |
| } |
| for (i = 0; i < HISI_PTT_MAX_SPACE_LEN; i++) |
| color_fprintf(stdout, color, " "); |
| |
| color_fprintf(stdout, color, |
| " %s %x %s %x %s %x %s %x %s %x %s %x %s %x %s %x\n", |
| "Format", FIELD_GET(HISI_PTT_HEAD0_4DW_FORMAT, dw), |
| "Type", FIELD_GET(HISI_PTT_HEAD0_4DW_TYPE, dw), |
| "T9", FIELD_GET(HISI_PTT_HEAD0_4DW_T9, dw), |
| "T8", FIELD_GET(HISI_PTT_HEAD0_4DW_T8, dw), |
| "TH", FIELD_GET(HISI_PTT_HEAD0_4DW_TH, dw), |
| "SO", FIELD_GET(HISI_PTT_HEAD0_4DW_SO, dw), |
| "Length", FIELD_GET(HISI_PTT_HEAD0_4DW_LEN, dw), |
| "Time", FIELD_GET(HISI_PTT_HEAD0_4DW_TIME, dw)); |
| } |
| |
| static int hisi_ptt_4dw_pkt_desc(const unsigned char *buf, int pos) |
| { |
| int i; |
| |
| hisi_ptt_4dw_print_dw0(buf, pos); |
| pos += HISI_PTT_FIELD_LENGTH; |
| |
| for (i = 0; i < HISI_PTT_4DW_TYPE_MAX; i++) { |
| hisi_ptt_print_pkt(buf, pos, hisi_ptt_4dw_pkt_field_name[i]); |
| pos += HISI_PTT_FIELD_LENGTH; |
| } |
| |
| return hisi_ptt_pkt_size[HISI_PTT_4DW_PKT]; |
| } |
| |
| int hisi_ptt_pkt_desc(const unsigned char *buf, int pos, enum hisi_ptt_pkt_type type) |
| { |
| if (type == HISI_PTT_8DW_PKT) |
| return hisi_ptt_8dw_pkt_desc(buf, pos); |
| |
| return hisi_ptt_4dw_pkt_desc(buf, pos); |
| } |