From f33d5256dd15dbbe9ebea9c2efe554f2e595de21 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 22 Nov 2021 15:21:08 -0800 Subject: [PATCH] freedreno/crashdec: HFI queue decoding Signed-off-by: Rob Clark Part-of: --- src/freedreno/decode/crashdec-hfi.c | 523 ++++++++++++++++++++++++++++ src/freedreno/decode/crashdec.c | 47 +++ src/freedreno/decode/crashdec.h | 21 ++ src/freedreno/decode/meson.build | 1 + 4 files changed, 592 insertions(+) create mode 100644 src/freedreno/decode/crashdec-hfi.c diff --git a/src/freedreno/decode/crashdec-hfi.c b/src/freedreno/decode/crashdec-hfi.c new file mode 100644 index 00000000000..f777ece3d25 --- /dev/null +++ b/src/freedreno/decode/crashdec-hfi.c @@ -0,0 +1,523 @@ +/* + * Copyright © 2021 Google, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "util/macros.h" +#include "crashdec.h" + +static const char *hfi_msg_name(unsigned msgid); + +/* + * Decode HFI queues + */ + +/* HFI message types */ + +#define HFI_MSG_CMD 0 +#define HFI_MSG_ACK 1 +#define HFI_MSG_ACK_V1 2 + +#define HFI_HEADER_ID(msg) ((msg) & 0xff) +/* Note that header size includes the header itself: */ +#define HFI_HEADER_SIZE(msg) (((msg) >> 8) & 0xff) +#define HFI_HEADER_TYPE(msg) (((msg) >> 16) & 0xf) +#define HFI_HEADER_SEQNUM(msg) (((msg) >> 20) & 0xfff) + +struct a6xx_hfi_queue_header { + uint32_t status; + uint32_t iova; + uint32_t type; + uint32_t size; + uint32_t msg_size; + uint32_t dropped; + uint32_t rx_watermark; + uint32_t tx_watermark; + uint32_t rx_request; + uint32_t tx_request; + uint32_t read_index; + uint32_t write_index; +}; + +struct a6xx_hfi_queue_table_header { + uint32_t version; + uint32_t size; /* Size of the queue table in dwords */ + uint32_t qhdr0_offset; /* Offset of the first queue header */ + uint32_t qhdr_size; /* Size of the queue headers */ + uint32_t num_queues; /* Number of total queues */ + uint32_t active_queues; /* Number of active queues */ + struct a6xx_hfi_queue_header queue[]; +}; + +/* + * HFI message definitions: + */ + +#define HFI_F2H_MSG_ACK 126 + +struct a6xx_hfi_msg_response { + uint32_t header; + uint32_t ret_header; + uint32_t error; + uint32_t payload[16]; +}; + +static void +decode_F2H_MSG_ACK(struct a6xx_hfi_msg_response *msg) +{ + unsigned msgid = HFI_HEADER_ID(msg->ret_header); + + printf("\t\t\t\tret_header: %s (id=%u, size=%u, type=%u, seqnum=%u)\n", + hfi_msg_name(msgid), msgid, HFI_HEADER_SIZE(msg->ret_header), + HFI_HEADER_TYPE(msg->ret_header), HFI_HEADER_SEQNUM(msg->ret_header)); + printf("\t\t\t\terror: %u\n", msg->error); +} + +#define HFI_F2H_MSG_ERROR 100 + +struct a6xx_hfi_msg_error { + uint32_t header; + uint32_t code; + uint32_t payload[2]; +}; + +static void +decode_F2H_MSG_ERROR(struct a6xx_hfi_msg_error *msg) +{ + printf("\t\t\t\tcode: %u\n", msg->code); +} + +#define HFI_H2F_MSG_INIT 0 + +struct a6xx_hfi_msg_gmu_init_cmd { + uint32_t header; + uint32_t seg_id; + uint32_t dbg_buffer_addr; + uint32_t dbg_buffer_size; + uint32_t boot_state; +}; + +static void +decode_H2F_MSG_INIT(struct a6xx_hfi_msg_gmu_init_cmd *msg) +{ + printf("\t\t\t\tseg_id: %u\n", msg->seg_id); + printf("\t\t\t\tdbg_buffer_addr: 0x%08x\n", msg->dbg_buffer_addr); + printf("\t\t\t\tdbg_buffer_size: %u\n", msg->dbg_buffer_size); + printf("\t\t\t\tboot_state: %u\n", msg->boot_state); +} + +#define HFI_H2F_MSG_FW_VERSION 1 + +struct a6xx_hfi_msg_fw_version { + uint32_t header; + uint32_t supported_version; +}; + +static void +decode_H2F_MSG_FW_VERSION(struct a6xx_hfi_msg_fw_version *msg) +{ + printf("\t\t\t\tsupported_version: 0x%x\n", msg->supported_version); +} + +#define HFI_H2F_MSG_PERF_TABLE 4 + +struct perf_level { + uint32_t vote; + uint32_t freq; +}; + +struct perf_gx_level { + uint32_t vote; + uint32_t acd; + uint32_t freq; +}; + +struct a6xx_hfi_msg_perf_table_v1 { + uint32_t header; + uint32_t num_gpu_levels; + uint32_t num_gmu_levels; + + struct perf_level gx_votes[16]; + struct perf_level cx_votes[4]; +}; + +struct a6xx_hfi_msg_perf_table { + uint32_t header; + uint32_t num_gpu_levels; + uint32_t num_gmu_levels; + + struct perf_gx_level gx_votes[16]; + struct perf_level cx_votes[4]; +}; + +static void +decode_H2F_MSG_PERF_TABLE(void *_msg) +{ + if (is_gmu_legacy()) { + struct a6xx_hfi_msg_perf_table_v1 *msg = _msg; + unsigned i; + + printf("\t\t\t\tnum_gpu_levels: %u\n", msg->num_gpu_levels); + printf("\t\t\t\tnum_gmu_levels: %u\n", msg->num_gmu_levels); + + assert(msg->num_gpu_levels <= ARRAY_SIZE(msg->gx_votes)); + for (i = 0; i < msg->num_gpu_levels; i++) { + printf("\t\t\t\tgx_vote[%u]: vote=%u, freq=%u\n", i, + msg->gx_votes[i].vote, msg->gx_votes[i].freq); + } + + for (; i < ARRAY_SIZE(msg->gx_votes); i++) { + assert(!msg->gx_votes[i].vote); + assert(!msg->gx_votes[i].freq); + } + + assert(msg->num_gmu_levels <= ARRAY_SIZE(msg->cx_votes)); + for (i = 0; i < msg->num_gmu_levels; i++) { + printf("\t\t\t\tcx_vote[%u]: vote=%u, freq=%u\n", i, + msg->cx_votes[i].vote, msg->cx_votes[i].freq); + } + + for (; i < ARRAY_SIZE(msg->cx_votes); i++) { + assert(!msg->cx_votes[i].vote); + assert(!msg->cx_votes[i].freq); + } + } else { + struct a6xx_hfi_msg_perf_table *msg = _msg; + unsigned i; + + printf("\t\t\t\tnum_gpu_levels: %u\n", msg->num_gpu_levels); + printf("\t\t\t\tnum_gmu_levels: %u\n", msg->num_gmu_levels); + + assert(msg->num_gpu_levels <= ARRAY_SIZE(msg->gx_votes)); + for (i = 0; i < msg->num_gpu_levels; i++) { + printf("\t\t\t\tgx_vote[%u]: vote=%u, acd=%u, freq=%u\n", i, + msg->gx_votes[i].vote, msg->gx_votes[i].acd, + msg->gx_votes[i].freq); + } + + for (; i < ARRAY_SIZE(msg->gx_votes); i++) { + assert(!msg->gx_votes[i].vote); + assert(!msg->gx_votes[i].acd); + assert(!msg->gx_votes[i].freq); + } + + assert(msg->num_gmu_levels <= ARRAY_SIZE(msg->cx_votes)); + for (i = 0; i < msg->num_gmu_levels; i++) { + printf("\t\t\t\tcx_vote[%u]: vote=%u, freq=%u\n", i, + msg->cx_votes[i].vote, msg->cx_votes[i].freq); + } + + for (; i < ARRAY_SIZE(msg->cx_votes); i++) { + assert(!msg->cx_votes[i].vote); + assert(!msg->cx_votes[i].freq); + } + } +} + +#define HFI_H2F_MSG_BW_TABLE 3 + +struct a6xx_hfi_msg_bw_table { + uint32_t header; + uint32_t bw_level_num; + uint32_t cnoc_cmds_num; + uint32_t ddr_cmds_num; + uint32_t cnoc_wait_bitmask; + uint32_t ddr_wait_bitmask; + uint32_t cnoc_cmds_addrs[6]; + uint32_t cnoc_cmds_data[2][6]; + uint32_t ddr_cmds_addrs[8]; + uint32_t ddr_cmds_data[16][8]; +}; + +static void +decode_H2F_MSG_BW_TABLE(struct a6xx_hfi_msg_bw_table *msg) +{ + printf("\t\t\t\tbw_level_num: %u\n", msg->bw_level_num); + printf("\t\t\t\tcnoc_cmds_num: %u\n", msg->cnoc_cmds_num); + printf("\t\t\t\tddr_cmds_num: %u\n", msg->ddr_cmds_num); + printf("\t\t\t\tcnoc_wait_bitmask: 0x%x\n", msg->cnoc_wait_bitmask); + printf("\t\t\t\tddr_wait_bitmask: 0x%x\n", msg->ddr_wait_bitmask); + printf("\t\t\t\tcnoc_cmds_addrs: %08x %08x %08x %08x %08x %08x\n", + msg->cnoc_cmds_addrs[0], msg->cnoc_cmds_addrs[1], msg->cnoc_cmds_addrs[2], + msg->cnoc_cmds_addrs[3], msg->cnoc_cmds_addrs[4], msg->cnoc_cmds_addrs[5]); + for (unsigned i = 0; i < ARRAY_SIZE(msg->cnoc_cmds_data); i++) { + printf("\t\t\t\tcnoc_cmds_data[%u]: %08x %08x %08x %08x %08x %08x\n", i, + msg->cnoc_cmds_data[i][0], msg->cnoc_cmds_data[i][1], msg->cnoc_cmds_data[i][2], + msg->cnoc_cmds_data[i][3], msg->cnoc_cmds_data[i][4], msg->cnoc_cmds_data[i][5]); + } + printf("\t\t\t\tddr_cmds_addrs: %08x %08x %08x %08x %08x %08x %08x %08x\n", + msg->ddr_cmds_addrs[0], msg->ddr_cmds_addrs[1], msg->ddr_cmds_addrs[2], + msg->ddr_cmds_addrs[3], msg->ddr_cmds_addrs[4], msg->ddr_cmds_addrs[5], + msg->ddr_cmds_addrs[6], msg->ddr_cmds_addrs[7]); + for (unsigned i = 0; i < ARRAY_SIZE(msg->ddr_cmds_data); i++) { + printf("\t\t\t\tddr_cmds_data[%u]: %08x %08x %08x %08x %08x %08x %08x %08x\n", i, + msg->ddr_cmds_data[i][0], msg->ddr_cmds_data[i][1], msg->ddr_cmds_data[i][2], + msg->ddr_cmds_data[i][3], msg->ddr_cmds_data[i][4], msg->ddr_cmds_data[i][5], + msg->ddr_cmds_data[i][6], msg->ddr_cmds_data[i][7]); + } +} + +#define HFI_H2F_MSG_TEST 5 + +struct a6xx_hfi_msg_test { + uint32_t header; +}; + +static void +decode_H2F_MSG_TEST(struct a6xx_hfi_msg_test *msg) +{ +} + +#define HFI_H2F_MSG_START 10 + +struct a6xx_hfi_msg_start { + uint32_t header; +}; + +static void +decode_H2F_MSG_START(struct a6xx_hfi_msg_start *msg) +{ +} + +#define HFI_H2F_MSG_CORE_FW_START 14 + +struct a6xx_hfi_msg_core_fw_start { + uint32_t header; + uint32_t handle; +}; + +static void +decode_H2F_MSG_CORE_FW_START(struct a6xx_hfi_msg_core_fw_start *msg) +{ + printf("\t\t\t\thandle: %u\n", msg->handle); +} + +#define HFI_H2F_MSG_GX_BW_PERF_VOTE 30 + +struct a6xx_hfi_gx_bw_perf_vote_cmd { + uint32_t header; + uint32_t ack_type; + uint32_t freq; + uint32_t bw; +}; + +static void +decode_H2F_MSG_GX_BW_PERF_VOTE(struct a6xx_hfi_gx_bw_perf_vote_cmd *msg) +{ + printf("\t\t\t\tack_type: %u\n", msg->ack_type); + printf("\t\t\t\tfreq: %u\n", msg->freq); + printf("\t\t\t\tbw: %u\n", msg->bw); +} + +#define HFI_H2F_MSG_PREPARE_SLUMBER 33 + +struct a6xx_hfi_prep_slumber_cmd { + uint32_t header; + uint32_t bw; + uint32_t freq; +}; + +static void +decode_H2F_MSG_PREPARE_SLUMBER(struct a6xx_hfi_prep_slumber_cmd *msg) +{ + printf("\t\t\t\tbw: %u\n", msg->bw); + printf("\t\t\t\tfreq: %u\n", msg->freq); +} + +static struct { + const char *name; + void (*decode)(void *); +} hfi_msgs[] = { +#define HFI_MSG(name) [HFI_ ## name] = { #name, (void (*)(void *))decode_ ## name } + HFI_MSG(F2H_MSG_ACK), + HFI_MSG(F2H_MSG_ERROR), + HFI_MSG(H2F_MSG_INIT), + HFI_MSG(H2F_MSG_FW_VERSION), + HFI_MSG(H2F_MSG_PERF_TABLE), + HFI_MSG(H2F_MSG_BW_TABLE), + HFI_MSG(H2F_MSG_TEST), + HFI_MSG(H2F_MSG_START), + HFI_MSG(H2F_MSG_CORE_FW_START), + HFI_MSG(H2F_MSG_GX_BW_PERF_VOTE), + HFI_MSG(H2F_MSG_PREPARE_SLUMBER), +}; + +static bool +is_valid_msg_type(unsigned type) +{ + switch (type) { + case HFI_MSG_CMD: + case HFI_MSG_ACK: + case HFI_MSG_ACK_V1: + return true; + default: + return false; + } +} + +static const char * +hfi_msg_name(unsigned msgid) +{ + if (msgid < ARRAY_SIZE(hfi_msgs)) + return hfi_msgs[msgid].name; + return NULL; +} + +static bool +is_valid_decode_start(struct a6xx_hfi_state *hfi, unsigned qidx, int32_t read_index) +{ + struct a6xx_hfi_queue_table_header *table = hfi->buf; + struct a6xx_hfi_queue_header *queue = &table->queue[qidx]; + uint32_t offset = queue->iova - hfi->iova; + uint32_t *dw = (uint32_t *)(((uint8_t *)hfi->buf) + offset); + int last_seqno = -1; + + if (read_index < 0) + return false; + + while (read_index != queue->write_index) { + uint32_t hdr = dw[read_index]; + + if (!is_valid_msg_type(HFI_HEADER_TYPE(hdr))) + return false; + + if (!hfi_msg_name(HFI_HEADER_ID(hdr))) + return false; + + /* Header size should be at least 1, and not extend past the write_index: */ + unsigned sz = HFI_HEADER_SIZE(hdr); + if (!is_gmu_legacy()) + sz = ALIGN_POT(sz, 4); + int remaining = ((read_index + sz) + (queue->size - 1) - + queue->write_index) % queue->size; + if ((sz == 0) || (remaining < 0)) + return false; + + /* Seqno should be one more than previous seqno: */ + unsigned seqno = HFI_HEADER_SEQNUM(hdr); + if ((last_seqno != -1) && (((last_seqno + 1) & 0xfff) != seqno)) + return false; + + last_seqno = seqno; + + read_index = (read_index + sz) % queue->size; + } + + return true; +} + +static void +decode_hfi(struct a6xx_hfi_state *hfi, unsigned qidx, int32_t read_index) +{ + struct a6xx_hfi_queue_table_header *table = hfi->buf; + struct a6xx_hfi_queue_header *queue = &table->queue[qidx]; + uint32_t offset = queue->iova - hfi->iova; + uint32_t *dw = (uint32_t *)(((uint8_t *)hfi->buf) + offset); + + while (read_index != queue->write_index) { + uint32_t hdr = dw[read_index]; + unsigned msgid = HFI_HEADER_ID(hdr); + unsigned sz = HFI_HEADER_SIZE(hdr); + unsigned type = HFI_HEADER_TYPE(hdr); + unsigned seqno = HFI_HEADER_SEQNUM(hdr); + + assert(is_valid_msg_type(type)); + assert(hfi_msg_name(msgid)); + + printf("\t\t\t------ %s (id=%u, size=%u, type=%u, seqnum=%u)\n", + hfi_msg_name(msgid), msgid, sz, type, seqno); + + if (!is_gmu_legacy()) + sz = ALIGN_POT(sz, 4); + + uint32_t buf[sz]; + for (unsigned i = 0; i < sz; i++) { + buf[i] = dw[(read_index + i) % queue->size]; + } + + if (type == HFI_MSG_CMD) + hfi_msgs[msgid].decode(buf); + + dump_hex_ascii(buf, sz*4, 4); + + read_index = (read_index + sz) % queue->size; + } +} + +/* Search backwards from the most recent (last) history entry to try to + * find start of the oldest HFI message which has not been overwritten + * due to ringbuffer wraparound. + */ +static int32_t +find_decode_start(struct a6xx_hfi_state *hfi, unsigned qidx) +{ + int i; + + for (i = ARRAY_SIZE(hfi->history[qidx]) - 1; i >= 0; i--) { + if (!is_valid_decode_start(hfi, qidx, hfi->history[qidx][i])) + break; + } + + /* Last entry was invalid, or we decremented below zero, so advance + * the index by one: + */ + i++; + + if (i >= ARRAY_SIZE(hfi->history[qidx])) + return -1; + + return hfi->history[qidx][i]; +} + +void +dump_gmu_hfi(struct a6xx_hfi_state *hfi) +{ + struct a6xx_hfi_queue_table_header *table = hfi->buf; + + printf("\tversion: %u\n", table->version); + printf("\tsize: %u\n", table->size); + printf("\tqhdr0_offset: %u\n", table->qhdr0_offset); + printf("\tqhdr_size: %u\n", table->qhdr_size); + printf("\tnum_queues: %u\n", table->num_queues); + printf("\tactive_queues: %u\n", table->active_queues); + + for (unsigned i = 0; i < table->num_queues; i++) { + struct a6xx_hfi_queue_header *queue = &table->queue[i]; + + printf("\tqueue[%u]:\n", i); + printf("\t\tstatus: 0x%x\n", queue->status); + printf("\t\tiova: 0x%x\n", queue->iova); + printf("\t\ttype: 0x%x\n", queue->type); + printf("\t\tsize: %u\n", queue->size); + printf("\t\tmsg_size: %u\n", queue->msg_size); + printf("\t\tdropped: %u\n", queue->dropped); + printf("\t\trx_watermark: 0x%x\n", queue->rx_watermark); + printf("\t\ttx_watermark: 0x%x\n", queue->tx_watermark); + printf("\t\trx_request: 0x%x\n", queue->rx_request); + printf("\t\ttx_request: 0x%x\n", queue->tx_request); + printf("\t\tread_index: %u\n", queue->read_index); + printf("\t\twrite_index: %u\n", queue->write_index); + + int32_t read_index = find_decode_start(hfi, i); + if (read_index >= 0) + decode_hfi(hfi, i, read_index); + } +} diff --git a/src/freedreno/decode/crashdec.c b/src/freedreno/decode/crashdec.c index 0fb8874f332..19d110b2b48 100644 --- a/src/freedreno/decode/crashdec.c +++ b/src/freedreno/decode/crashdec.c @@ -248,6 +248,51 @@ decode_ringbuffer(void) } } +/* + * Decode HFI queues + */ + +static void +decode_gmu_hfi(void) +{ + struct a6xx_hfi_state hfi = {}; + + /* Initialize the history buffers with invalid entries (-1): */ + memset(&hfi.history, 0xff, sizeof(hfi.history)); + + foreach_line_in_section (line) { + if (startswith(line, " iova:")) { + parseline(line, " iova: %" PRIx64, &hfi.iova); + } else if (startswith(line, " size:")) { + parseline(line, " size: %u", &hfi.size); + } else if (startswith(line, " queue-history")) { + unsigned qidx, dummy; + + parseline(line, " queue-history[%u]:", &qidx); + assert(qidx < ARRAY_SIZE(hfi.history)); + + parseline(line, " queue-history[%u]: %d %d %d %d %d %d %d %d", &dummy, + &hfi.history[qidx][0], &hfi.history[qidx][1], + &hfi.history[qidx][2], &hfi.history[qidx][3], + &hfi.history[qidx][4], &hfi.history[qidx][5], + &hfi.history[qidx][6], &hfi.history[qidx][7]); + } else if (startswith(line, " data: !!ascii85 |")) { + hfi.buf = popline_ascii85(hfi.size / 4); + + if (verbose) + dump_hex_ascii(hfi.buf, hfi.size, 1); + + dump_gmu_hfi(&hfi); + + free(hfi.buf); + + continue; + } + + printf("%s", line); + } +} + static bool valid_header(uint32_t pkt) { @@ -693,6 +738,8 @@ decode(void) decode_bos(); } else if (startswith(line, "ringbuffer:")) { decode_ringbuffer(); + } else if (startswith(line, "gmu-hfi:")) { + decode_gmu_hfi(); } else if (startswith(line, "registers:")) { decode_registers(); diff --git a/src/freedreno/decode/crashdec.h b/src/freedreno/decode/crashdec.h index cb767e67637..c92afacce12 100644 --- a/src/freedreno/decode/crashdec.h +++ b/src/freedreno/decode/crashdec.h @@ -71,7 +71,28 @@ is_64b(void) return options.gpu_id >= 500; } +static inline bool +is_gmu_legacy(void) +{ + switch (options.gpu_id) { + case 615: + case 618: + case 630: + return true; + default: + return false; + } +} + void dump_register(struct rnn *rnn, uint32_t offset, uint32_t value); void dump_cp_mem_pool(uint32_t *mempool); +struct a6xx_hfi_state { + uint64_t iova; + void *buf; + uint32_t size; + int32_t history[2][8]; +}; +void dump_gmu_hfi(struct a6xx_hfi_state *hfi); + #endif /* __CRASHDEC_H__ */ diff --git a/src/freedreno/decode/meson.build b/src/freedreno/decode/meson.build index 58810d07438..1d820048525 100644 --- a/src/freedreno/decode/meson.build +++ b/src/freedreno/decode/meson.build @@ -135,6 +135,7 @@ crashdec = executable( [ 'crashdec.c', 'crashdec.h', + 'crashdec-hfi.c', 'crashdec-mempool.c', ], include_directories: [