--- /dev/null
+/*
+ * Copyright © 2016 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <error.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+
+#include "decoder.h"
+#include "intel_aub.h"
+#include "gen_disasm.h"
+
+/* Below is the only command missing from intel_aub.h in libdrm
+ * So, reuse intel_aub.h from libdrm and #define the
+ * AUB_MI_BATCH_BUFFER_END as below
+ */
+#define AUB_MI_BATCH_BUFFER_END (0x0500 << 16)
+
+#define CSI "\e["
+#define HEADER CSI "37;44m"
+#define NORMAL CSI "0m"
+#define CLEAR_TO_EOL CSI "0K"
+
+/* options */
+
+static bool option_full_decode = true;
+static bool option_print_offsets = true;
+static enum { COLOR_AUTO, COLOR_ALWAYS, COLOR_NEVER } option_color;
+
+/* state */
+
+struct gen_disasm *disasm;
+
+uint64_t gtt_size, gtt_end;
+void *gtt;
+uint64_t general_state_base;
+uint64_t surface_state_base;
+uint64_t dynamic_state_base;
+uint64_t instruction_base;
+uint64_t instruction_bound;
+
+static inline uint32_t
+field(uint32_t value, int start, int end)
+{
+ uint32_t mask;
+
+ mask = ~0U >> (31 - end + start);
+
+ return (value >> start) & mask;
+}
+
+struct brw_instruction;
+
+static inline int
+valid_offset(uint32_t offset)
+{
+ return offset < gtt_end;
+}
+
+static void
+decode_structure(struct gen_spec *spec, struct gen_group *strct, const uint32_t *p)
+{
+ struct gen_field_iterator iter;
+
+ gen_field_iterator_init(&iter, strct, p);
+ while (gen_field_iterator_next(&iter)) {
+ printf(" %s: %s\n", iter.name, iter.value);
+ }
+}
+
+static void
+dump_binding_table(struct gen_spec *spec, uint32_t offset)
+{
+ uint32_t *pointers, i;
+ uint64_t start;
+ struct gen_group *surface_state;
+
+ surface_state = gen_spec_find_struct(spec, "RENDER_SURFACE_STATE");
+ if (surface_state == NULL) {
+ printf("did not find RENDER_SURFACE_STATE info\n");
+ return;
+ }
+
+ start = surface_state_base + offset;
+ pointers = gtt + start;
+ for (i = 0; i < 16; i++) {
+ if (pointers[i] == 0)
+ continue;
+ start = pointers[i] + surface_state_base;
+ if (!valid_offset(start)) {
+ printf("pointer %u: %08x <not valid>\n",
+ i, pointers[i]);
+ continue;
+ } else {
+ printf("pointer %u: %08x\n", i, pointers[i]);
+ }
+
+ decode_structure(spec, surface_state, gtt + start);
+ }
+}
+
+static void
+handle_3dstate_index_buffer(struct gen_spec *spec, uint32_t *p)
+{
+ void *start;
+ uint32_t length, i, type, size;
+
+ start = gtt + p[2];
+ type = (p[1] >> 8) & 3;
+ size = 1 << type;
+ length = p[4] / size;
+ if (length > 10)
+ length = 10;
+
+ printf("\t");
+
+ for (i = 0; i < length; i++) {
+ switch (type) {
+ case 0:
+ printf("%3d ", ((uint8_t *)start)[i]);
+ break;
+ case 1:
+ printf("%3d ", ((uint16_t *)start)[i]);
+ break;
+ case 2:
+ printf("%3d ", ((uint32_t *)start)[i]);
+ break;
+ }
+ }
+ if (length < p[4] / size)
+ printf("...\n");
+ else
+ printf("\n");
+}
+
+static inline uint64_t
+get_qword(uint32_t *p)
+{
+ return ((uint64_t) p[1] << 32) | p[0];
+}
+
+static void
+handle_state_base_address(struct gen_spec *spec, uint32_t *p)
+{
+ uint64_t mask = ~((1 << 12) - 1);
+
+ if (gen_spec_get_gen(spec) >= gen_make_gen(8,0)) {
+ if (p[1] & 1)
+ general_state_base = get_qword(&p[1]) & mask;
+ if (p[4] & 1)
+ surface_state_base = get_qword(&p[4]) & mask;
+ if (p[6] & 1)
+ dynamic_state_base = get_qword(&p[6]) & mask;
+ if (p[10] & 1)
+ instruction_base = get_qword(&p[10]) & mask;
+ if (p[15] & 1)
+ instruction_bound = p[15] & mask;
+ } else {
+ if (p[2] & 1)
+ surface_state_base = p[2] & mask;
+ if (p[3] & 1)
+ dynamic_state_base = p[3] & mask;
+ if (p[5] & 1)
+ instruction_base = p[5] & mask;
+ if (p[9] & 1)
+ instruction_bound = p[9] & mask;
+ }
+}
+
+static void
+dump_samplers(struct gen_spec *spec, uint32_t offset)
+{
+ uint32_t i;
+ uint64_t start;
+ struct gen_group *sampler_state;
+
+ sampler_state = gen_spec_find_struct(spec, "SAMPLER_STATE");
+
+ start = dynamic_state_base + offset;
+ for (i = 0; i < 4; i++) {
+ printf("sampler state %d\n", i);
+ decode_structure(spec, sampler_state, gtt + start + i * 16);
+ }
+}
+
+static void
+handle_media_interface_descriptor_load(struct gen_spec *spec, uint32_t *p)
+{
+ int i, length = p[2] / 32;
+ struct gen_group *descriptor_structure;
+ uint32_t *descriptors;
+ uint64_t start;
+ struct brw_instruction *insns;
+
+ descriptor_structure =
+ gen_spec_find_struct(spec, "INTERFACE_DESCRIPTOR_DATA");
+ if (descriptor_structure == NULL) {
+ printf("did not find INTERFACE_DESCRIPTOR_DATA info\n");
+ return;
+ }
+
+ start = dynamic_state_base + p[3];
+ descriptors = gtt + start;
+ for (i = 0; i < length; i++, descriptors += 8) {
+ printf("descriptor %u: %08x\n", i, *descriptors);
+ decode_structure(spec, descriptor_structure, descriptors);
+
+ start = instruction_base + descriptors[0];
+ if (!valid_offset(start)) {
+ printf("kernel: %08lx <not valid>\n", start);
+ continue;
+ } else {
+ printf("kernel: %08lx\n", start);
+ }
+
+ insns = (struct brw_instruction *) (gtt + start);
+ gen_disasm_disassemble(disasm, insns, 0, 8192, stdout);
+
+ dump_samplers(spec, descriptors[3] & ~0x1f);
+ dump_binding_table(spec, descriptors[4] & ~0x1f);
+ }
+}
+
+/* Heuristic to determine whether a uint32_t is probably actually a float
+ * (http://stackoverflow.com/a/2953466)
+ */
+
+static bool
+probably_float(uint32_t bits)
+{
+ int exp = ((bits & 0x7f800000U) >> 23) - 127;
+ uint32_t mant = bits & 0x007fffff;
+
+ /* +- 0.0 */
+ if (exp == -127 && mant == 0)
+ return true;
+
+ /* +- 1 billionth to 1 billion */
+ if (-30 <= exp && exp <= 30)
+ return true;
+
+ /* some value with only a few binary digits */
+ if ((mant & 0x0000ffff) == 0)
+ return true;
+
+ return false;
+}
+
+static void
+handle_3dstate_vertex_buffers(struct gen_spec *spec, uint32_t *p)
+{
+ uint32_t *end, *s, *dw, *dwend;
+ uint64_t offset;
+ int n, i, count, stride;
+
+ end = (p[0] & 0xff) + p + 2;
+ for (s = &p[1], n = 0; s < end; s += 4, n++) {
+ if (gen_spec_get_gen(spec) >= gen_make_gen(8, 0)) {
+ offset = *(uint64_t *) &s[1];
+ dwend = gtt + offset + s[3];
+ } else {
+ offset = s[1];
+ dwend = gtt + s[2] + 1;
+ }
+
+ stride = field(s[0], 0, 11);
+ count = 0;
+ printf("vertex buffer %d, size %d\n", n, s[3]);
+ for (dw = gtt + offset, i = 0; dw < dwend && i < 256; dw++) {
+ if (count == 0 && count % (8 * 4) == 0)
+ printf(" ");
+
+ if (probably_float(*dw))
+ printf(" %8.2f", *(float *) dw);
+ else
+ printf(" 0x%08x", *dw);
+
+ i++;
+ count += 4;
+
+ if (count == stride) {
+ printf("\n");
+ count = 0;
+ } else if (count % (8 * 4) == 0) {
+ printf("\n");
+ } else {
+ printf(" ");
+ }
+ }
+ if (count > 0 && count % (8 * 4) != 0)
+ printf("\n");
+ }
+}
+
+static void
+handle_3dstate_vs(struct gen_spec *spec, uint32_t *p)
+{
+ uint64_t start;
+ struct brw_instruction *insns;
+ int vs_enable;
+
+ if (gen_spec_get_gen(spec) >= gen_make_gen(8, 0)) {
+ start = get_qword(&p[1]);
+ vs_enable = p[7] & 1;
+ } else {
+ start = p[1];
+ vs_enable = p[5] & 1;
+ }
+
+ if (vs_enable) {
+ printf("instruction_base %08lx, start %08lx\n",
+ instruction_base, start);
+
+ insns = (struct brw_instruction *) (gtt + instruction_base + start);
+ gen_disasm_disassemble(disasm, insns, 0, 8192, stdout);
+ }
+}
+
+static void
+handle_3dstate_constant(struct gen_spec *spec, uint32_t *p)
+{
+ int i, j, length;
+ uint32_t *dw;
+ float *f;
+
+ for (i = 0; i < 4; i++) {
+ length = (p[1 + i / 2] >> (i & 1) * 16) & 0xffff;
+ f = (float *) (gtt + p[3 + i * 2] + dynamic_state_base);
+ dw = (uint32_t *) f;
+ for (j = 0; j < length * 8; j++) {
+ if (probably_float(dw[j]))
+ printf(" %04.3f", f[j]);
+ else
+ printf(" 0x%08x", dw[j]);
+
+ if ((j & 7) == 7)
+ printf("\n");
+ }
+ }
+}
+
+static void
+handle_3dstate_ps(struct gen_spec *spec, uint32_t *p)
+{
+ uint32_t mask = ~((1 << 6) - 1);
+ uint64_t start;
+ struct brw_instruction *insns;
+ static const char unused[] = "unused";
+ static const char *pixel_type[3] = {"8 pixel", "16 pixel", "32 pixel"};
+ const char *k0, *k1, *k2;
+ uint32_t k_mask, k1_offset, k2_offset;
+
+ if (gen_spec_get_gen(spec) >= gen_make_gen(8, 0)) {
+ k_mask = p[6] & 7;
+ k1_offset = 8;
+ k2_offset = 10;
+ } else {
+ k_mask = p[4] & 7;
+ k1_offset = 6;
+ k2_offset = 7;
+ }
+
+#define DISPATCH_8 1
+#define DISPATCH_16 2
+#define DISPATCH_32 4
+
+ switch (k_mask) {
+ case DISPATCH_8:
+ k0 = pixel_type[0];
+ k1 = unused;
+ k2 = unused;
+ break;
+ case DISPATCH_16:
+ k0 = pixel_type[1];
+ k1 = unused;
+ k2 = unused;
+ break;
+ case DISPATCH_8 | DISPATCH_16:
+ k0 = pixel_type[0];
+ k1 = unused;
+ k2 = pixel_type[1];
+ break;
+ case DISPATCH_32:
+ k0 = pixel_type[2];
+ k1 = unused;
+ k2 = unused;
+ break;
+ case DISPATCH_16 | DISPATCH_32:
+ k0 = unused;
+ k1 = pixel_type[2];
+ k2 = pixel_type[1];
+ break;
+ case DISPATCH_8 | DISPATCH_16 | DISPATCH_32:
+ k0 = pixel_type[0];
+ k1 = pixel_type[2];
+ k2 = pixel_type[1];
+ break;
+ default:
+ k0 = unused;
+ k1 = unused;
+ k2 = unused;
+ break;
+ }
+
+ start = instruction_base + (p[1] & mask);
+ printf(" Kernel[0] %s\n", k0);
+ if (k0 != unused) {
+ insns = (struct brw_instruction *) (gtt + start);
+ gen_disasm_disassemble(disasm, insns, 0, 8192, stdout);
+ }
+
+ start = instruction_base + (p[k1_offset] & mask);
+ printf(" Kernel[1] %s\n", k1);
+ if (k1 != unused) {
+ insns = (struct brw_instruction *) (gtt + start);
+ gen_disasm_disassemble(disasm, insns, 0, 8192, stdout);
+ }
+
+ start = instruction_base + (p[k2_offset] & mask);
+ printf(" Kernel[2] %s\n", k2);
+ if (k2 != unused) {
+ insns = (struct brw_instruction *) (gtt + start);
+ gen_disasm_disassemble(disasm, insns, 0, 8192, stdout);
+ }
+}
+
+static void
+handle_3dstate_binding_table_pointers(struct gen_spec *spec, uint32_t *p)
+{
+ dump_binding_table(spec, p[1]);
+}
+
+static void
+handle_3dstate_sampler_state_pointers(struct gen_spec *spec, uint32_t *p)
+{
+ dump_samplers(spec, p[1]);
+}
+
+static void
+handle_3dstate_viewport_state_pointers_cc(struct gen_spec *spec, uint32_t *p)
+{
+ uint64_t start;
+ struct gen_group *cc_viewport;
+
+ cc_viewport = gen_spec_find_struct(spec, "CC_VIEWPORT");
+
+ start = dynamic_state_base + (p[1] & ~0x1fu);
+ for (uint32_t i = 0; i < 4; i++) {
+ printf("viewport %d\n", i);
+ decode_structure(spec, cc_viewport, gtt + start + i * 8);
+ }
+}
+
+static void
+handle_3dstate_viewport_state_pointers_sf_clip(struct gen_spec *spec, uint32_t *p)
+{
+ uint64_t start;
+ struct gen_group *sf_clip_viewport;
+
+ sf_clip_viewport = gen_spec_find_struct(spec, "SF_CLIP_VIEWPORT");
+
+ start = dynamic_state_base + (p[1] & ~0x3fu);
+ for (uint32_t i = 0; i < 4; i++) {
+ printf("viewport %d\n", i);
+ decode_structure(spec, sf_clip_viewport, gtt + start + i * 64);
+ }
+}
+
+static void
+handle_3dstate_blend_state_pointers(struct gen_spec *spec, uint32_t *p)
+{
+ uint64_t start;
+ struct gen_group *blend_state;
+
+ blend_state = gen_spec_find_struct(spec, "BLEND_STATE");
+
+ start = dynamic_state_base + (p[1] & ~0x3fu);
+ decode_structure(spec, blend_state, gtt + start);
+}
+
+static void
+handle_3dstate_cc_state_pointers(struct gen_spec *spec, uint32_t *p)
+{
+ uint64_t start;
+ struct gen_group *cc_state;
+
+ cc_state = gen_spec_find_struct(spec, "COLOR_CALC_STATE");
+
+ start = dynamic_state_base + (p[1] & ~0x3fu);
+ decode_structure(spec, cc_state, gtt + start);
+}
+
+static void
+handle_3dstate_scissor_state_pointers(struct gen_spec *spec, uint32_t *p)
+{
+ uint64_t start;
+ struct gen_group *scissor_rect;
+
+ scissor_rect = gen_spec_find_struct(spec, "SCISSOR_RECT");
+
+ start = dynamic_state_base + (p[1] & ~0x1fu);
+ decode_structure(spec, scissor_rect, gtt + start);
+}
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+#define STATE_BASE_ADDRESS 0x61010000
+
+#define MEDIA_INTERFACE_DESCRIPTOR_LOAD 0x70020000
+
+#define _3DSTATE_INDEX_BUFFER 0x780a0000
+#define _3DSTATE_VERTEX_BUFFERS 0x78080000
+
+#define _3DSTATE_VS 0x78100000
+#define _3DSTATE_GS 0x78110000
+
+#define _3DSTATE_CONSTANT_VS 0x78150000
+#define _3DSTATE_CONSTANT_GS 0x78160000
+#define _3DSTATE_CONSTANT_PS 0x78170000
+#define _3DSTATE_CONSTANT_HS 0x78190000
+#define _3DSTATE_CONSTANT_DS 0x781A0000
+
+#define _3DSTATE_PS 0x78200000
+
+#define _3DSTATE_BINDING_TABLE_POINTERS_VS 0x78260000
+#define _3DSTATE_BINDING_TABLE_POINTERS_HS 0x78270000
+#define _3DSTATE_BINDING_TABLE_POINTERS_DS 0x78280000
+#define _3DSTATE_BINDING_TABLE_POINTERS_GS 0x78290000
+#define _3DSTATE_BINDING_TABLE_POINTERS_PS 0x782a0000
+
+#define _3DSTATE_SAMPLER_STATE_POINTERS_VS 0x782b0000
+#define _3DSTATE_SAMPLER_STATE_POINTERS_GS 0x782e0000
+#define _3DSTATE_SAMPLER_STATE_POINTERS_PS 0x782f0000
+
+#define _3DSTATE_VIEWPORT_STATE_POINTERS_CC 0x78230000
+#define _3DSTATE_VIEWPORT_STATE_POINTERS_SF_CLIP 0x78210000
+#define _3DSTATE_BLEND_STATE_POINTERS 0x78240000
+#define _3DSTATE_CC_STATE_POINTERS 0x780e0000
+#define _3DSTATE_SCISSOR_STATE_POINTERS 0x780f0000
+
+struct custom_handler {
+ uint32_t opcode;
+ void (*handle)(struct gen_spec *spec, uint32_t *p);
+} custom_handlers[] = {
+ { STATE_BASE_ADDRESS, handle_state_base_address },
+ { MEDIA_INTERFACE_DESCRIPTOR_LOAD, handle_media_interface_descriptor_load },
+ { _3DSTATE_VERTEX_BUFFERS, handle_3dstate_vertex_buffers },
+ { _3DSTATE_INDEX_BUFFER, handle_3dstate_index_buffer },
+ { _3DSTATE_VS, handle_3dstate_vs },
+ { _3DSTATE_GS, handle_3dstate_vs },
+ /* FIXME: Handle disassmbing for 3DSTATE_HS and 3DSTATE_DS. */
+ { _3DSTATE_CONSTANT_VS, handle_3dstate_constant },
+ { _3DSTATE_CONSTANT_GS, handle_3dstate_constant },
+ { _3DSTATE_CONSTANT_PS, handle_3dstate_constant },
+ { _3DSTATE_CONSTANT_HS, handle_3dstate_constant },
+ { _3DSTATE_CONSTANT_DS, handle_3dstate_constant },
+ { _3DSTATE_PS, handle_3dstate_ps },
+
+ { _3DSTATE_BINDING_TABLE_POINTERS_VS, handle_3dstate_binding_table_pointers },
+ { _3DSTATE_BINDING_TABLE_POINTERS_HS, handle_3dstate_binding_table_pointers },
+ { _3DSTATE_BINDING_TABLE_POINTERS_DS, handle_3dstate_binding_table_pointers },
+ { _3DSTATE_BINDING_TABLE_POINTERS_GS, handle_3dstate_binding_table_pointers },
+ { _3DSTATE_BINDING_TABLE_POINTERS_PS, handle_3dstate_binding_table_pointers },
+
+ { _3DSTATE_SAMPLER_STATE_POINTERS_VS, handle_3dstate_sampler_state_pointers },
+ { _3DSTATE_SAMPLER_STATE_POINTERS_GS, handle_3dstate_sampler_state_pointers },
+ { _3DSTATE_SAMPLER_STATE_POINTERS_PS, handle_3dstate_sampler_state_pointers },
+
+ { _3DSTATE_VIEWPORT_STATE_POINTERS_CC, handle_3dstate_viewport_state_pointers_cc },
+ { _3DSTATE_VIEWPORT_STATE_POINTERS_SF_CLIP, handle_3dstate_viewport_state_pointers_sf_clip },
+ { _3DSTATE_BLEND_STATE_POINTERS, handle_3dstate_blend_state_pointers },
+ { _3DSTATE_CC_STATE_POINTERS, handle_3dstate_cc_state_pointers },
+ { _3DSTATE_SCISSOR_STATE_POINTERS, handle_3dstate_scissor_state_pointers }
+};
+
+static void
+parse_commands(struct gen_spec *spec, uint32_t *cmds, int size, int engine)
+{
+ uint32_t *p, *end = cmds + size / 4;
+ unsigned int length, i;
+ struct gen_group *inst;
+
+ for (p = cmds; p < end; p += length) {
+ inst = gen_spec_find_instruction(spec, p);
+ if (inst == NULL) {
+ printf("unknown instruction %08x\n", p[0]);
+ length = (p[0] & 0xff) + 2;
+ continue;
+ }
+ length = gen_group_get_length(inst, p);
+
+ const char *color, *reset_color = CLEAR_TO_EOL NORMAL;
+ uint64_t offset;
+
+ if (option_full_decode)
+ color = HEADER;
+ else
+ color = NORMAL;
+
+ if (option_color == COLOR_NEVER) {
+ color = "";
+ reset_color = "";
+ }
+
+ if (option_print_offsets)
+ offset = (void *) p - gtt;
+ else
+ offset = 0;
+
+ printf("%s0x%08lx: 0x%08x: %s%s\n",
+ color, offset, p[0],
+ gen_group_get_name(inst), reset_color);
+
+ if (option_full_decode) {
+ struct gen_field_iterator iter;
+ gen_field_iterator_init(&iter, inst, p);
+ while (gen_field_iterator_next(&iter)) {
+ printf(" %s: %s\n", iter.name, iter.value);
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(custom_handlers); i++) {
+ if (gen_group_get_opcode(inst) ==
+ custom_handlers[i].opcode)
+ custom_handlers[i].handle(spec, p);
+ }
+ }
+
+ if ((p[0] & 0xffff0000) == AUB_MI_BATCH_BUFFER_START) {
+ uint64_t start;
+ if (gen_spec_get_gen(spec) >= gen_make_gen(8,0))
+ start = get_qword(&p[1]);
+ else
+ start = p[1];
+
+ parse_commands(spec, gtt + start, 1 << 20, engine);
+ } else if ((p[0] & 0xffff0000) == AUB_MI_BATCH_BUFFER_END) {
+ break;
+ }
+ }
+}
+
+#define GEN_ENGINE_RENDER 1
+#define GEN_ENGINE_BLITTER 2
+
+static void
+handle_trace_block(struct gen_spec *spec, 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;
+ uint64_t offset = p[3];
+ uint32_t size = p[4];
+ int header_length = p[0] & 0xffff;
+ uint32_t *data = p + header_length + 2;
+ int engine = GEN_ENGINE_RENDER;
+
+ if (gen_spec_get_gen(spec) >= gen_make_gen(8,0))
+ offset += (uint64_t) p[5] << 32;
+
+ switch (operation) {
+ case AUB_TRACE_OP_DATA_WRITE:
+ if (address_space != AUB_TRACE_MEMTYPE_GTT)
+ break;
+ if (gtt_size < offset + size)
+ error(EXIT_FAILURE, errno, "overflow gtt space");
+ memcpy((char *) gtt + offset, data, size);
+ if (gtt_end < offset + size)
+ gtt_end = offset + size;
+ break;
+ case AUB_TRACE_OP_COMMAND_WRITE:
+ switch (type) {
+ case AUB_TRACE_TYPE_RING_PRB0:
+ engine = GEN_ENGINE_RENDER;
+ break;
+ case AUB_TRACE_TYPE_RING_PRB2:
+ engine = GEN_ENGINE_BLITTER;
+ break;
+ default:
+ printf("command write to unknown ring %d\n", type);
+ break;
+ }
+
+ parse_commands(spec, data, size, engine);
+ gtt_end = 0;
+ break;
+ }
+}
+
+struct aub_file {
+ char *filename;
+ int fd;
+ struct stat sb;
+ uint32_t *map, *end, *cursor;
+};
+
+static struct aub_file *
+aub_file_open(const char *filename)
+{
+ struct aub_file *file;
+
+ file = malloc(sizeof *file);
+ file->filename = strdup(filename);
+ file->fd = open(file->filename, O_RDONLY);
+ if (file->fd == -1)
+ error(EXIT_FAILURE, errno, "open %s failed", file->filename);
+
+ if (fstat(file->fd, &file->sb) == -1)
+ error(EXIT_FAILURE, errno, "stat failed");
+
+ file->map = mmap(NULL, file->sb.st_size,
+ PROT_READ, MAP_SHARED, file->fd, 0);
+ if (file->map == MAP_FAILED)
+ error(EXIT_FAILURE, errno, "mmap failed");
+
+ file->cursor = file->map;
+ file->end = file->map + file->sb.st_size / 4;
+
+ /* mmap a terabyte for our gtt space. */
+ gtt_size = 1ul << 40;
+ gtt = mmap(NULL, gtt_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
+ if (gtt == MAP_FAILED)
+ error(EXIT_FAILURE, errno, "failed to alloc gtt space");
+
+ return file;
+}
+
+#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_VERSION 0x00
+#define SUBOPCODE_REG_WRITE 0x03
+#define SUBOPCODE_MEM_POLL 0x05
+#define SUBOPCODE_MEM_WRITE 0x06
+
+#define MAKE_GEN(major, minor) ( ((major) << 8) | (minor) )
+
+struct {
+ const char *name;
+ uint32_t gen;
+} device_map[] = {
+ { "bwr", MAKE_GEN(4, 0) },
+ { "cln", MAKE_GEN(4, 0) },
+ { "blc", MAKE_GEN(4, 0) },
+ { "ctg", MAKE_GEN(4, 0) },
+ { "el", MAKE_GEN(4, 0) },
+ { "il", MAKE_GEN(4, 0) },
+ { "sbr", MAKE_GEN(6, 0) },
+ { "ivb", MAKE_GEN(7, 0) },
+ { "lrb2", MAKE_GEN(0, 0) },
+ { "hsw", MAKE_GEN(7, 5) },
+ { "vlv", MAKE_GEN(7, 0) },
+ { "bdw", MAKE_GEN(8, 0) },
+ { "skl", MAKE_GEN(9, 0) },
+ { "chv", MAKE_GEN(8, 0) },
+ { "bxt", MAKE_GEN(9, 0) }
+};
+
+static void
+aub_file_decode_batch(struct aub_file *file, struct gen_spec *spec)
+{
+ uint32_t *p, h, device, data_type;
+ int header_length, payload_size, bias;
+
+ p = file->cursor;
+ h = *p;
+ header_length = h & 0xffff;
+
+ switch (OPCODE(h)) {
+ case OPCODE_AUB:
+ bias = 2;
+ break;
+ case OPCODE_NEW_AUB:
+ bias = 1;
+ break;
+ default:
+ printf("unknown opcode %d at %ld/%ld\n",
+ OPCODE(h), file->cursor - file->map,
+ file->end - file->map);
+ file->cursor = file->end;
+ return;
+ }
+
+ payload_size = 0;
+ switch (h & 0xffff0000) {
+ case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_HEADER):
+ payload_size = p[12];
+ break;
+ case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BLOCK):
+ payload_size = p[4];
+ handle_trace_block(spec, p);
+ break;
+ case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BMP):
+ break;
+
+ case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_VERSION):
+ printf("version block: dw1 %08x\n", p[1]);
+ device = (p[1] >> 8) & 0xff;
+ printf(" device %s\n", device_map[device].name);
+ break;
+ case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_REG_WRITE):
+ printf("register write block: (dwords %d)\n", h & 0xffff);
+ printf(" reg 0x%x, data 0x%x\n", p[1], p[5]);
+ break;
+ case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_MEM_WRITE):
+ printf("memory write block (dwords %d):\n", h & 0xffff);
+ printf(" address 0x%lx\n", *(uint64_t *) &p[1]);
+ data_type = (p[3] >> 20) & 0xff;
+ if (data_type != 0)
+ printf(" data type 0x%x\n", data_type);
+ printf(" address space 0x%x\n", (p[3] >> 28) & 0xf);
+ break;
+ case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_MEM_POLL):
+ printf("memory poll block (dwords %d):\n", h & 0xffff);
+ break;
+ default:
+ printf("unknown block type=0x%x, opcode=0x%x, "
+ "subopcode=0x%x (%08x)\n", TYPE(h), OPCODE(h), SUBOPCODE(h), h);
+ break;
+ }
+ file->cursor = p + header_length + bias + payload_size / 4;
+}
+
+static int
+aub_file_more_stuff(struct aub_file *file)
+{
+ return file->cursor < file->end;
+}
+
+static void
+setup_pager(void)
+{
+ int fds[2];
+ pid_t pid;
+
+ if (!isatty(1))
+ return;
+
+ if (pipe(fds) == -1)
+ return;
+
+ pid = fork();
+ if (pid == -1)
+ return;
+
+ if (pid == 0) {
+ close(fds[1]);
+ dup2(fds[0], 0);
+ execlp("less", "less", "-rFi", NULL);
+ }
+
+ close(fds[0]);
+ dup2(fds[1], 1);
+ close(fds[1]);
+}
+
+static void
+print_help(FILE *file)
+{
+ fprintf(file,
+ "Usage: %s [OPTION]... FILE\n"
+ "Decode aub file contents.\n\n"
+ "A valid --gen option must be provided.\n\n"
+ " --help display this help and exit\n"
+ " --gen=platform decode for given platform (ivb, byt, hsw, bdw, chv, skl, kbl or bxt)\n"
+ " --headers decode only command headers\n"
+ " --color[=WHEN] colorize the output; WHEN can be 'auto' (default\n"
+ " if omitted), 'always', or 'never'\n"
+ " --no-pager don't launch pager\n"
+ " --no-offsets don't print instruction offsets\n",
+ basename(program_invocation_name));
+}
+
+static bool
+is_prefix(const char *arg, const char *prefix, const char **value)
+{
+ int l = strlen(prefix);
+
+ if (strncmp(arg, prefix, l) == 0 && (arg[l] == '\0' || arg[l] == '=')) {
+ if (arg[l] == '=')
+ *value = arg + l + 1;
+ else
+ *value = NULL;
+
+ return true;
+ }
+
+ return false;
+}
+
+int main(int argc, char *argv[])
+{
+ struct gen_spec *spec;
+ struct aub_file *file;
+ int i, pci_id = 0;
+ bool found_arg_gen = false, pager = true;
+ int gen_major, gen_minor;
+ const char *value;
+ char gen_file[256], gen_val[24];
+
+ if (argc == 1) {
+ print_help(stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--no-pager") == 0) {
+ pager = false;
+ } else if (strcmp(argv[i], "--no-offsets") == 0) {
+ option_print_offsets = false;
+ } else if (is_prefix(argv[i], "--gen", &value)) {
+ if (value == NULL)
+ error(EXIT_FAILURE, 0, "option '--gen' requires an argument\n");
+ found_arg_gen = true;
+ gen_major = 0;
+ gen_minor = 0;
+ snprintf(gen_val, sizeof(gen_val), "%s", value);
+ } else if (strcmp(argv[i], "--headers") == 0) {
+ option_full_decode = false;
+ } else if (is_prefix(argv[i], "--color", &value)) {
+ if (value == NULL || strcmp(value, "always") == 0)
+ option_color = COLOR_ALWAYS;
+ else if (strcmp(value, "never") == 0)
+ option_color = COLOR_NEVER;
+ else if (strcmp(value, "auto") == 0)
+ option_color = COLOR_AUTO;
+ else
+ error(EXIT_FAILURE, 0, "invalid value for --color: %s", value);
+ } else if (strcmp(argv[i], "--help") == 0) {
+ print_help(stdout);
+ exit(EXIT_SUCCESS);
+ } else {
+ if (argv[i][0] == '-') {
+ fprintf(stderr, "unknown option %s\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ }
+ }
+
+ if (!found_arg_gen) {
+ fprintf(stderr, "argument --gen is required\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (strstr(gen_val,"ivb") != NULL) {
+ /* Intel(R) Ivybridge Mobile GT2 */
+ pci_id = 0x0166;
+ gen_major = 7;
+ gen_minor = 0;
+ } else if (strstr(gen_val,"hsw") != NULL) {
+ /* Intel(R) Haswell Mobile GT2 */
+ pci_id = 0x0416;
+ gen_major = 7;
+ gen_minor = 5;
+ } else if (strstr(gen_val,"byt") != NULL) {
+ /* Intel(R) Bay Trail */
+ pci_id = 0x0155;
+ gen_major = 7;
+ gen_minor = 5;
+ } else if (strstr(gen_val,"bdw") != NULL) {
+ /* Intel(R) HD Graphics 5500 (Broadwell GT2) */
+ pci_id = 0x1616;
+ gen_major = 8;
+ gen_minor = 0;
+ } else if (strstr(gen_val,"chv") != NULL) {
+ /* Intel(R) HD Graphics (Cherryview) */
+ pci_id = 0x22B3;
+ gen_major = 8;
+ gen_minor = 0;
+ } else if (strstr(gen_val,"skl") != NULL) {
+ /* Intel(R) HD Graphics 530 (Skylake GT2) */
+ pci_id = 0x1912;
+ gen_major = 9;
+ gen_minor = 0;
+ } else if (strstr(gen_val,"kbl") != NULL) {
+ /* Intel(R) Kabylake GT2 */
+ pci_id = 0x591D;
+ gen_major = 9;
+ gen_minor = 0;
+ } else if (strstr(gen_val,"bxt") != NULL) {
+ /* Intel(R) HD Graphics (Broxton) */
+ pci_id = 0x0A84;
+ gen_major = 9;
+ gen_minor = 0;
+ } else {
+ error(EXIT_FAILURE, 0, "can't parse gen: %s, expected ivb, byt, hsw, bdw, chv, skl, kbl or bxt\n", gen_val);
+ }
+
+ /* Do this before we redirect stdout to pager. */
+ if (option_color == COLOR_AUTO)
+ option_color = isatty(1) ? COLOR_ALWAYS : COLOR_NEVER;
+
+ if (isatty(1) && pager)
+ setup_pager();
+
+ if (gen_minor > 0)
+ snprintf(gen_file, sizeof(gen_file), "../genxml/gen%d%d.xml", gen_major, gen_minor);
+ else
+ snprintf(gen_file, sizeof(gen_file), "../genxml/gen%d.xml", gen_major);
+
+ spec = gen_spec_load(gen_file);
+ disasm = gen_disasm_create(pci_id);
+
+ if (argv[i] == NULL) {
+ print_help(stderr);
+ exit(EXIT_FAILURE);
+ } else {
+ file = aub_file_open(argv[i]);
+ }
+
+ while (aub_file_more_stuff(file))
+ aub_file_decode_batch(file, spec);
+
+ fflush(stdout);
+ /* close the stdout which is opened to write the output */
+ close(1);
+
+ wait(NULL);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * Copyright © 2016 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 <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <expat.h>
+
+#include <util/macros.h>
+
+#include "decoder.h"
+
+#define XML_BUFFER_SIZE 4096
+
+#define MAKE_GEN(major, minor) ( ((major) << 8) | (minor) )
+
+struct gen_spec {
+ uint32_t gen;
+
+ int ncommands;
+ struct gen_group *commands[256];
+ int nstructs;
+ struct gen_group *structs[256];
+ int nregisters;
+ struct gen_group *registers[256];
+};
+
+struct gen_group {
+ char *name;
+ int nfields;
+ struct gen_field **fields;
+
+ uint32_t opcode_mask;
+ uint32_t opcode;
+};
+
+struct gen_type {
+ enum {
+ GEN_TYPE_UNKNOWN,
+ GEN_TYPE_INT,
+ GEN_TYPE_UINT,
+ GEN_TYPE_BOOL,
+ GEN_TYPE_FLOAT,
+ GEN_TYPE_ADDRESS,
+ GEN_TYPE_OFFSET,
+ GEN_TYPE_STRUCT,
+ GEN_TYPE_UFIXED,
+ GEN_TYPE_SFIXED,
+ GEN_TYPE_MBO
+ } kind;
+
+ /* Struct definition for GEN_TYPE_STRUCT */
+ struct gen_group *gen_struct;
+
+ /* Integer and fractional sizes for GEN_TYPE_UFIXED and GEN_TYPE_SFIXED */
+ int i, f;
+};
+
+struct gen_field {
+ char *name;
+ int start, end;
+ struct gen_type type;
+ bool has_default;
+ uint32_t default_value;
+};
+
+struct location {
+ const char *filename;
+ int line_number;
+};
+
+struct parser_context {
+ XML_Parser parser;
+ int foo;
+ struct location loc;
+ const char *platform;
+
+ struct gen_group *group;
+
+ int nfields;
+ struct gen_field *fields[128];
+
+ struct gen_spec *spec;
+};
+
+const char *
+gen_group_get_name(struct gen_group *group)
+{
+ return group->name;
+}
+
+uint32_t
+gen_group_get_opcode(struct gen_group *group)
+{
+ return group->opcode;
+}
+
+struct gen_group *
+gen_spec_find_struct(struct gen_spec *spec, const char *name)
+{
+ for (int i = 0; i < spec->nstructs; i++)
+ if (strcmp(spec->structs[i]->name, name) == 0)
+ return spec->structs[i];
+
+ return NULL;
+}
+
+uint32_t
+gen_spec_get_gen(struct gen_spec *spec)
+{
+ return spec->gen;
+}
+
+static void __attribute__((noreturn))
+fail(struct location *loc, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ fprintf(stderr, "%s:%d: error: ",
+ loc->filename, loc->line_number);
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+static void *
+fail_on_null(void *p)
+{
+ if (p == NULL) {
+ fprintf(stderr, "aubinator: out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return p;
+}
+
+static char *
+xstrdup(const char *s)
+{
+ return fail_on_null(strdup(s));
+}
+
+static void *
+zalloc(size_t s)
+{
+ return calloc(s, 1);
+}
+
+static void *
+xzalloc(size_t s)
+{
+ return fail_on_null(zalloc(s));
+}
+
+static struct gen_group *
+create_group(struct parser_context *ctx, const char *name, const char **atts)
+{
+ struct gen_group *group;
+
+ group = xzalloc(sizeof(*group));
+ if (name)
+ group->name = xstrdup(name);
+
+ return group;
+}
+
+static inline uint64_t
+mask(int start, int end)
+{
+ uint64_t v;
+
+ v = ~0ULL >> (63 - end + start);
+
+ return v << start;
+}
+
+static inline uint64_t
+field(uint64_t value, int start, int end)
+{
+ return (value & mask(start, end)) >> start;
+}
+
+static struct gen_type
+string_to_type(struct parser_context *ctx, const char *s)
+{
+ int i, f;
+ struct gen_group *g;
+
+ if (strcmp(s, "int") == 0)
+ return (struct gen_type) { .kind = GEN_TYPE_INT };
+ else if (strcmp(s, "uint") == 0)
+ return (struct gen_type) { .kind = GEN_TYPE_UINT };
+ else if (strcmp(s, "bool") == 0)
+ return (struct gen_type) { .kind = GEN_TYPE_BOOL };
+ else if (strcmp(s, "float") == 0)
+ return (struct gen_type) { .kind = GEN_TYPE_FLOAT };
+ else if (strcmp(s, "address") == 0)
+ return (struct gen_type) { .kind = GEN_TYPE_ADDRESS };
+ else if (strcmp(s, "offset") == 0)
+ return (struct gen_type) { .kind = GEN_TYPE_OFFSET };
+ else if (sscanf(s, "u%d.%d", &i, &f) == 2)
+ return (struct gen_type) { .kind = GEN_TYPE_UFIXED, .i = i, .f = f };
+ else if (sscanf(s, "s%d.%d", &i, &f) == 2)
+ return (struct gen_type) { .kind = GEN_TYPE_SFIXED, .i = i, .f = f };
+ else if (g = gen_spec_find_struct(ctx->spec, s), g != NULL)
+ return (struct gen_type) { .kind = GEN_TYPE_STRUCT, .gen_struct = g };
+ else if (strcmp(s, "mbo") == 0)
+ return (struct gen_type) { .kind = GEN_TYPE_MBO };
+ else
+ fail(&ctx->loc, "invalid type: %s", s);
+}
+
+static struct gen_field *
+create_field(struct parser_context *ctx, const char **atts)
+{
+ struct gen_field *field;
+ char *p;
+ int i;
+
+ field = xzalloc(sizeof(*field));
+
+ for (i = 0; atts[i]; i += 2) {
+ if (strcmp(atts[i], "name") == 0)
+ field->name = xstrdup(atts[i + 1]);
+ else if (strcmp(atts[i], "start") == 0)
+ field->start = strtoul(atts[i + 1], &p, 0);
+ else if (strcmp(atts[i], "end") == 0)
+ field->end = strtoul(atts[i + 1], &p, 0);
+ else if (strcmp(atts[i], "type") == 0)
+ field->type = string_to_type(ctx, atts[i + 1]);
+ else if (strcmp(atts[i], "default") == 0 &&
+ field->start >= 16 && field->end <= 31) {
+ field->has_default = true;
+ field->default_value = strtoul(atts[i + 1], &p, 0);
+ }
+ }
+
+ return field;
+}
+
+static void
+start_element(void *data, const char *element_name, const char **atts)
+{
+ struct parser_context *ctx = data;
+ int i;
+ const char *name = NULL;
+ const char *gen = NULL;
+
+ ctx->loc.line_number = XML_GetCurrentLineNumber(ctx->parser);
+
+ for (i = 0; atts[i]; i += 2) {
+ if (strcmp(atts[i], "name") == 0)
+ name = atts[i + 1];
+ else if (strcmp(atts[i], "gen") == 0)
+ gen = atts[i + 1];
+ }
+
+ if (strcmp(element_name, "genxml") == 0) {
+ if (name == NULL)
+ fail(&ctx->loc, "no platform name given");
+ if (gen == NULL)
+ fail(&ctx->loc, "no gen given");
+
+ ctx->platform = xstrdup(name);
+ int major, minor;
+ int n = sscanf(gen, "%d.%d", &major, &minor);
+ if (n == 0)
+ fail(&ctx->loc, "invalid gen given: %s", gen);
+ if (n == 1)
+ minor = 0;
+
+ ctx->spec->gen = MAKE_GEN(major, minor);
+ } else if (strcmp(element_name, "instruction") == 0 ||
+ strcmp(element_name, "struct") == 0 ||
+ strcmp(element_name, "register") == 0) {
+ ctx->group = create_group(ctx, name, atts);
+ } else if (strcmp(element_name, "group") == 0) {
+ } else if (strcmp(element_name, "field") == 0) {
+ ctx->fields[ctx->nfields++] = create_field(ctx, atts);
+ } else if (strcmp(element_name, "enum") == 0) {
+ } else if (strcmp(element_name, "value") == 0) {
+ }
+}
+
+static void
+end_element(void *data, const char *name)
+{
+ struct parser_context *ctx = data;
+
+ if (strcmp(name, "instruction") == 0 ||
+ strcmp(name, "struct") == 0 ||
+ strcmp(name, "register") == 0) {
+ size_t size = ctx->nfields * sizeof(ctx->fields[0]);
+ struct gen_group *group = ctx->group;
+
+ group->fields = xzalloc(size);
+ group->nfields = ctx->nfields;
+ memcpy(group->fields, ctx->fields, size);
+ ctx->nfields = 0;
+ ctx->group = NULL;
+
+ for (int i = 0; i < group->nfields; i++) {
+ if (group->fields[i]->start >= 16 &&
+ group->fields[i]->end <= 31 &&
+ group->fields[i]->has_default) {
+ group->opcode_mask |=
+ mask(group->fields[i]->start, group->fields[i]->end);
+ group->opcode |=
+ group->fields[i]->default_value << group->fields[i]->start;
+ }
+ }
+
+ struct gen_spec *spec = ctx->spec;
+ if (strcmp(name, "instruction") == 0)
+ spec->commands[spec->ncommands++] = group;
+ else if (strcmp(name, "struct") == 0)
+ spec->structs[spec->nstructs++] = group;
+ else if (strcmp(name, "register") == 0)
+ spec->registers[spec->nregisters++] = group;
+ }
+}
+
+static void
+character_data(void *data, const XML_Char *s, int len)
+{
+}
+
+struct gen_spec *
+gen_spec_load(const char *filename)
+{
+ struct parser_context ctx;
+ void *buf;
+ int len;
+ FILE *input;
+
+ input = fopen(filename, "r");
+ printf("xml filename = %s\n", filename);
+ if (input == NULL) {
+ fprintf(stderr, "failed to open xml description\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&ctx, 0, sizeof ctx);
+ ctx.parser = XML_ParserCreate(NULL);
+ XML_SetUserData(ctx.parser, &ctx);
+ if (ctx.parser == NULL) {
+ fprintf(stderr, "failed to create parser\n");
+ fclose(input);
+ return NULL;
+ }
+
+ XML_SetElementHandler(ctx.parser, start_element, end_element);
+ XML_SetCharacterDataHandler(ctx.parser, character_data);
+ ctx.loc.filename = filename;
+
+ ctx.spec = xzalloc(sizeof(*ctx.spec));
+
+ do {
+ buf = XML_GetBuffer(ctx.parser, XML_BUFFER_SIZE);
+ len = fread(buf, 1, XML_BUFFER_SIZE, input);
+ if (len < 0) {
+ fprintf(stderr, "fread: %m\n");
+ fclose(input);
+ return NULL;
+ }
+ if (XML_ParseBuffer(ctx.parser, len, len == 0) == 0) {
+ fprintf(stderr,
+ "Error parsing XML at line %ld col %ld: %s\n",
+ XML_GetCurrentLineNumber(ctx.parser),
+ XML_GetCurrentColumnNumber(ctx.parser),
+ XML_ErrorString(XML_GetErrorCode(ctx.parser)));
+ fclose(input);
+ return NULL;
+ }
+ } while (len > 0);
+
+ XML_ParserFree(ctx.parser);
+ fclose(input);
+
+ return ctx.spec;
+}
+
+struct gen_group *
+gen_spec_find_instruction(struct gen_spec *spec, const uint32_t *p)
+{
+ /* FIXME: Make sure the opcodes put out are correct */
+ for (int i = 0; i < spec->ncommands; i++) {
+ uint32_t opcode = *p & spec->commands[i]->opcode_mask;
+ if (opcode == spec->commands[i]->opcode)
+ return spec->commands[i];
+ }
+
+ return NULL;
+}
+
+int
+gen_group_get_length(struct gen_group *group, const uint32_t *p)
+{
+ uint32_t h = p[0];
+ uint32_t type = field(h, 29, 31);
+
+ switch (type) {
+ case 0: /* MI */ {
+ uint32_t opcode = field(h, 23, 28);
+ if (opcode < 16)
+ return 1;
+ else
+ return field(h, 0, 7) + 2;
+ break;
+ }
+
+ case 3: /* Render */ {
+ uint32_t subtype = field(h, 27, 28);
+ switch (subtype) {
+ case 0:
+ return field(h, 0, 7) + 2;
+ case 1:
+ return 1;
+ case 2:
+ return 2;
+ case 3:
+ return field(h, 0, 7) + 2;
+ }
+ }
+ }
+
+ unreachable("bad opcode");
+}
+
+void
+gen_field_iterator_init(struct gen_field_iterator *iter,
+ struct gen_group *group, const uint32_t *p)
+{
+ iter->group = group;
+ iter->p = p;
+ iter->i = 0;
+}
+
+bool
+gen_field_iterator_next(struct gen_field_iterator *iter)
+{
+ struct gen_field *f;
+ union {
+ uint32_t dw;
+ float f;
+ } v;
+
+ if (iter->i == iter->group->nfields)
+ return false;
+
+ f = iter->group->fields[iter->i++];
+ iter->name = f->name;
+ v.dw = iter->p[f->start / 32];
+ switch (f->type.kind) {
+ case GEN_TYPE_UNKNOWN:
+ case GEN_TYPE_INT:
+ snprintf(iter->value, sizeof(iter->value),
+ "%ld", field(v.dw, f->start, f->end));
+ break;
+ case GEN_TYPE_UINT:
+ snprintf(iter->value, sizeof(iter->value),
+ "%lu", field(v.dw, f->start, f->end));
+ break;
+ case GEN_TYPE_BOOL:
+ snprintf(iter->value, sizeof(iter->value),
+ "%s", field(v.dw, f->start, f->end) ? "true" : "false");
+ break;
+ case GEN_TYPE_FLOAT:
+ snprintf(iter->value, sizeof(iter->value), "%f", v.f);
+ break;
+ case GEN_TYPE_ADDRESS:
+ case GEN_TYPE_OFFSET:
+ snprintf(iter->value, sizeof(iter->value),
+ "0x%08lx", field(v.dw, f->start, f->end));
+ break;
+ case GEN_TYPE_STRUCT:
+ /* FIXME: Make iterator decode the struct recursively */
+ snprintf(iter->value, sizeof(iter->value),
+ "<struct %s>", f->type.gen_struct->name);
+ break;
+ case GEN_TYPE_UFIXED:
+ snprintf(iter->value, sizeof(iter->value),
+ "%f", (float) field(v.dw, f->start, f->end) / (1 << f->type.f));
+ break;
+ case GEN_TYPE_SFIXED:
+ /* FIXME: Sign extend extracted field. */
+ snprintf(iter->value, sizeof(iter->value), "%s", "foo");
+ break;
+ case GEN_TYPE_MBO:
+ break;
+ }
+
+ return true;
+}