freedreno/crashdec: HFI queue decoding
Signed-off-by: Rob Clark <robdclark@chromium.org> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/13937>
This commit is contained in:
parent
2133d34b11
commit
f33d5256dd
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
static bool
|
||||||
valid_header(uint32_t pkt)
|
valid_header(uint32_t pkt)
|
||||||
{
|
{
|
||||||
|
@ -693,6 +738,8 @@ decode(void)
|
||||||
decode_bos();
|
decode_bos();
|
||||||
} else if (startswith(line, "ringbuffer:")) {
|
} else if (startswith(line, "ringbuffer:")) {
|
||||||
decode_ringbuffer();
|
decode_ringbuffer();
|
||||||
|
} else if (startswith(line, "gmu-hfi:")) {
|
||||||
|
decode_gmu_hfi();
|
||||||
} else if (startswith(line, "registers:")) {
|
} else if (startswith(line, "registers:")) {
|
||||||
decode_registers();
|
decode_registers();
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,28 @@ is_64b(void)
|
||||||
return options.gpu_id >= 500;
|
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_register(struct rnn *rnn, uint32_t offset, uint32_t value);
|
||||||
void dump_cp_mem_pool(uint32_t *mempool);
|
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__ */
|
#endif /* __CRASHDEC_H__ */
|
||||||
|
|
|
@ -135,6 +135,7 @@ crashdec = executable(
|
||||||
[
|
[
|
||||||
'crashdec.c',
|
'crashdec.c',
|
||||||
'crashdec.h',
|
'crashdec.h',
|
||||||
|
'crashdec-hfi.c',
|
||||||
'crashdec-mempool.c',
|
'crashdec-mempool.c',
|
||||||
],
|
],
|
||||||
include_directories: [
|
include_directories: [
|
||||||
|
|
Loading…
Reference in New Issue