/* * Copyright © 2016-2018 Intel Corporation * * 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 #include #include #include #include "common/gen_gem.h" #include "util/macros.h" #include "aub_read.h" #include "gen_context.h" #include "intel_aub.h" #define TYPE(dw) (((dw) >> 29) & 7) #define OPCODE(dw) (((dw) >> 23) & 0x3f) #define SUBOPCODE(dw) (((dw) >> 16) & 0x7f) #define MAKE_HEADER(type, opcode, subopcode) \ (((type) << 29) | ((opcode) << 23) | ((subopcode) << 16)) #define TYPE_AUB 0x7 /* Classic AUB opcodes */ #define OPCODE_AUB 0x01 #define SUBOPCODE_HEADER 0x05 #define SUBOPCODE_BLOCK 0x41 #define SUBOPCODE_BMP 0x1e /* Newer version AUB opcode */ #define OPCODE_NEW_AUB 0x2e #define SUBOPCODE_REG_POLL 0x02 #define SUBOPCODE_REG_WRITE 0x03 #define SUBOPCODE_MEM_POLL 0x05 #define SUBOPCODE_MEM_WRITE 0x06 #define SUBOPCODE_VERSION 0x0e #define MAKE_GEN(major, minor) (((major) << 8) | (minor)) static void parse_error(struct aub_read *read, const uint32_t *p, const char *fmt, ...) { if (!read->error) return; va_list ap; va_start(ap, fmt); char msg[80]; vsnprintf(msg, sizeof(msg), fmt, ap); read->error(read->user_data, p, msg); va_end(ap); } static bool handle_trace_header(struct aub_read *read, const uint32_t *p) { /* The intel_aubdump tool from IGT is kind enough to put a PCI-ID= tag in * the AUB header comment. If the user hasn't specified a hardware * generation, try to use the one from the AUB file. */ const uint32_t *end = p + (p[0] & 0xffff) + 2; int aub_pci_id = 0; if (end > &p[12] && p[12] > 0) { if (sscanf((char *)&p[13], "PCI-ID=%i", &aub_pci_id) > 0) { if (!gen_get_device_info_from_pci_id(aub_pci_id, &read->devinfo)) { parse_error(read, p, "can't find device information: pci_id=0x%x\n", aub_pci_id); return false; } } } char app_name[33]; strncpy(app_name, (const char *)&p[2], 32); app_name[32] = 0; if (read->info) read->info(read->user_data, aub_pci_id, app_name); return true; } static bool handle_memtrace_version(struct aub_read *read, const uint32_t *p) { int header_length = p[0] & 0xffff; char app_name[64]; int app_name_len = MIN2(4 * (header_length + 1 - 5), ARRAY_SIZE(app_name) - 1); int pci_id_len = 0; int aub_pci_id = 0; strncpy(app_name, (const char *)&p[5], app_name_len); app_name[app_name_len] = 0; if (sscanf(app_name, "PCI-ID=%i %n", &aub_pci_id, &pci_id_len) > 0) { if (!gen_get_device_info_from_pci_id(aub_pci_id, &read->devinfo)) { parse_error(read, p, "can't find device information: pci_id=0x%x\n", aub_pci_id); return false; } if (read->info) read->info(read->user_data, aub_pci_id, app_name + pci_id_len); } return true; } static bool handle_trace_block(struct aub_read *read, const uint32_t *p) { int operation = p[1] & AUB_TRACE_OPERATION_MASK; int type = p[1] & AUB_TRACE_TYPE_MASK; int address_space = p[1] & AUB_TRACE_ADDRESS_SPACE_MASK; int header_length = p[0] & 0xffff; enum drm_i915_gem_engine_class engine = I915_ENGINE_CLASS_RENDER; const void *data = p + header_length + 2; uint64_t address = gen_48b_address((read->devinfo.gen >= 8 ? ((uint64_t) p[5] << 32) : 0) | ((uint64_t) p[3])); uint32_t size = p[4]; switch (operation) { case AUB_TRACE_OP_DATA_WRITE: if (address_space == AUB_TRACE_MEMTYPE_GTT) { if (read->local_write) read->local_write(read->user_data, address, data, size); break; case AUB_TRACE_OP_COMMAND_WRITE: switch (type) { case AUB_TRACE_TYPE_RING_PRB0: engine = I915_ENGINE_CLASS_RENDER; break; case AUB_TRACE_TYPE_RING_PRB1: engine = I915_ENGINE_CLASS_VIDEO; break; case AUB_TRACE_TYPE_RING_PRB2: engine = I915_ENGINE_CLASS_COPY; break; default: parse_error(read, p, "command write to unknown ring %d\n", type); return false; } if (read->ring_write) read->ring_write(read->user_data, engine, data, size); break; } } return true; } static void handle_memtrace_reg_write(struct aub_read *read, const uint32_t *p) { uint32_t offset = p[1]; uint32_t value = p[5]; if (read->reg_write) read->reg_write(read->user_data, offset, value); enum drm_i915_gem_engine_class engine; uint64_t context_descriptor; switch (offset) { case EXECLIST_SUBMITPORT_RCSUNIT: /* render elsp */ read->render_elsp[read->render_elsp_index++] = value; if (read->render_elsp_index < 4) return; read->render_elsp_index = 0; engine = I915_ENGINE_CLASS_RENDER; context_descriptor = (uint64_t)read->render_elsp[2] << 32 | read->render_elsp[3]; break; case EXECLIST_SUBMITPORT_VCSUNIT0: /* video elsp */ read->video_elsp[read->video_elsp_index++] = value; if (read->video_elsp_index < 4) return; read->video_elsp_index = 0; engine = I915_ENGINE_CLASS_VIDEO; context_descriptor = (uint64_t)read->video_elsp[2] << 32 | read->video_elsp[3]; break; case EXECLIST_SUBMITPORT_BCSUNIT: /* blitter elsp */ read->blitter_elsp[read->blitter_elsp_index++] = value; if (read->blitter_elsp_index < 4) return; read->blitter_elsp_index = 0; engine = I915_ENGINE_CLASS_COPY; context_descriptor = (uint64_t)read->blitter_elsp[2] << 32 | read->blitter_elsp[3]; break; case EXECLIST_SQ_CONTENTS0_RCSUNIT: /* render elsq0 lo */ read->render_elsp[3] = value; return; case (EXECLIST_SQ_CONTENTS0_RCSUNIT + 4): /* render elsq0 hi */ read->render_elsp[2] = value; return; case EXECLIST_SQ_CONTENTS0_VCSUNIT0: /* video elsq0 lo */ read->video_elsp[3] = value; return; case EXECLIST_SQ_CONTENTS0_VCSUNIT0 + 4: /* video elsq0 hi */ read->video_elsp[2] = value; return; case EXECLIST_SQ_CONTENTS0_BCSUNIT: /* blitter elsq0 lo */ read->blitter_elsp[3] = value; return; case (EXECLIST_SQ_CONTENTS0_BCSUNIT + 4): /* blitter elsq0 hi */ read->blitter_elsp[2] = value; return; case EXECLIST_CONTROL_RCSUNIT: /* render elsc */ engine = I915_ENGINE_CLASS_RENDER; context_descriptor = (uint64_t)read->render_elsp[2] << 32 | read->render_elsp[3]; break; case EXECLIST_CONTROL_VCSUNIT0: /* video_elsc */ engine = I915_ENGINE_CLASS_VIDEO; context_descriptor = (uint64_t)read->video_elsp[2] << 32 | read->video_elsp[3]; break; case EXECLIST_CONTROL_BCSUNIT: /* blitter elsc */ engine = I915_ENGINE_CLASS_COPY; context_descriptor = (uint64_t)read->blitter_elsp[2] << 32 | read->blitter_elsp[3]; break; default: return; } if (read->execlist_write) read->execlist_write(read->user_data, engine, context_descriptor); } static void handle_memtrace_mem_write(struct aub_read *read, const uint32_t *p) { const void *data = p + 5; uint64_t addr = gen_48b_address(*(uint64_t*)&p[1]); uint32_t size = p[4]; uint32_t address_space = p[3] >> 28; switch (address_space) { case 0: /* GGTT */ if (read->ggtt_write) read->ggtt_write(read->user_data, addr, data, size); break; case 1: /* Local */ if (read->local_write) read->local_write(read->user_data, addr, data, size); break; case 2: /* Physical */ if (read->phys_write) read->phys_write(read->user_data, addr, data, size); break; case 4: /* GGTT Entry */ if (read->ggtt_entry_write) read->ggtt_entry_write(read->user_data, addr, data, size); break; } } int aub_read_command(struct aub_read *read, const void *data, uint32_t data_len) { const uint32_t *p = data, *next; ASSERTED const uint32_t *end = data + data_len; uint32_t h, header_length, bias; assert(data_len >= 4); h = *p; header_length = h & 0xffff; switch (OPCODE(h)) { case OPCODE_AUB: bias = 2; break; case OPCODE_NEW_AUB: bias = 1; break; default: parse_error(read, data, "unknown opcode %d\n", OPCODE(h)); return -1; } next = p + header_length + bias; if ((h & 0xffff0000) == MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BLOCK)) { assert(end - p >= 4); next += p[4] / 4; } assert(next <= end); switch (h & 0xffff0000) { case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_HEADER): if (!handle_trace_header(read, p)) return -1; break; case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BLOCK): if (!handle_trace_block(read, p)) return -1; break; case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BMP): break; case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_VERSION): if (!handle_memtrace_version(read, p)) return -1; break; case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_REG_WRITE): handle_memtrace_reg_write(read, p); break; case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_MEM_WRITE): handle_memtrace_mem_write(read, p); break; case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_MEM_POLL): /* fprintf(outfile, "memory poll block (dwords %d):\n", h & 0xffff); */ break; case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_REG_POLL): break; default: parse_error(read, p, "unknown block type=0x%x, opcode=0x%x, subopcode=0x%x (%08x)\n", TYPE(h), OPCODE(h), SUBOPCODE(h), h); return -1; } return (next - p) * sizeof(*p); }