broadcom/genxml: Introduce a V3D packet/struct decoder.
authorEric Anholt <eric@anholt.net>
Wed, 12 Jul 2017 20:15:34 +0000 (13:15 -0700)
committerEric Anholt <eric@anholt.net>
Tue, 25 Jul 2017 21:44:52 +0000 (14:44 -0700)
This is copied from Intel's XML decoder, modified to handle V3D's
byte-oriented packets.

v2: Squash in robher's fixes for Android

src/broadcom/Android.cle.mk [new file with mode: 0644]
src/broadcom/Android.genxml.mk
src/broadcom/Android.mk
src/broadcom/Makefile.am
src/broadcom/Makefile.cle.am [new file with mode: 0644]
src/broadcom/Makefile.genxml.am
src/broadcom/Makefile.sources
src/broadcom/cle/v3d_decoder.c [new file with mode: 0644]
src/broadcom/cle/v3d_decoder.h [new file with mode: 0644]

diff --git a/src/broadcom/Android.cle.mk b/src/broadcom/Android.cle.mk
new file mode 100644 (file)
index 0000000..9b72842
--- /dev/null
@@ -0,0 +1,35 @@
+# Copyright © 2016 Intel Corporation
+# Copyright © 2016 Mauro Rossi <issor.oruam@gmail.com>
+#
+# 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 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 $(CLEAR_VARS)
+
+LOCAL_MODULE := libmesa_broadcom_cle
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+LOCAL_SRC_FILES := $(BROADCOM_DECODER_FILES)
+
+LOCAL_STATIC_LIBRARIES := libmesa_broadcom_genxml
+
+LOCAL_SHARED_LIBRARIES := libexpat libz
+
+include $(MESA_COMMON_MK)
+include $(BUILD_STATIC_LIBRARY)
index a504326135c52118dd042c9cdcc10ec24c777ecf..d42701dffe3dc1067d48589db353506ba007440b 100644 (file)
@@ -50,8 +50,14 @@ $(intermediates)/broadcom/cle/v3d_packet_v21_pack.h: PRIVATE_XML := $(LOCAL_PATH
 $(intermediates)/broadcom/cle/v3d_packet_v21_pack.h: $(LOCAL_PATH)/cle/v3d_packet_v21.xml $(LOCAL_PATH)/cle/gen_pack_header.py
        $(call header-gen)
 
 $(intermediates)/broadcom/cle/v3d_packet_v21_pack.h: $(LOCAL_PATH)/cle/v3d_packet_v21.xml $(LOCAL_PATH)/cle/gen_pack_header.py
        $(call header-gen)
 
+$(intermediates)/broadcom/cle/v3d_xml.h: $(addprefix $(MESA_TOP)/src/broadcom/,$(BROADCOM_GENXML_XML_FILES)) $(MESA_TOP)/src/intel/genxml/gen_zipped_file.py
+       @mkdir -p $(dir $@)
+       @echo "Gen Header: $(PRIVATE_MODULE) <= $(notdir $(@))"
+       $(hide) $(MESA_PYTHON2) $(MESA_TOP)/src/intel/genxml/gen_zipped_file.py $(addprefix $(MESA_TOP)/src/broadcom/,$(BROADCOM_GENXML_XML_FILES)) > $@ || (rm -f $@; false)
+
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
        $(MESA_TOP)/src/broadcom/cle \
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
        $(MESA_TOP)/src/broadcom/cle \
+       $(intermediates)/broadcom/cle \
        $(intermediates)
 
 include $(MESA_COMMON_MK)
        $(intermediates)
 
 include $(MESA_COMMON_MK)
index d2da907a210e810c90ef2177f879d591912aac2e..b3bf405104100cd816dacf9429fadaccd6568759 100644 (file)
@@ -26,3 +26,4 @@ LOCAL_PATH := $(call my-dir)
 include $(LOCAL_PATH)/Makefile.sources
 
 include $(LOCAL_PATH)/Android.genxml.mk
 include $(LOCAL_PATH)/Makefile.sources
 
 include $(LOCAL_PATH)/Android.genxml.mk
+include $(LOCAL_PATH)/Android.cle.mk
index f4a005b40cdefc2bd3392f49711273282c4dc06e..cbcd970ecbbaa3a9a5d19a01309976f024a02450 100644 (file)
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
 
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
 
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/include \
+       -I$(top_builddir)/src \
+       -I$(top_srcdir)/src \
+       $(VALGRIND_CFLAGS) \
+       $(DEFINES)
+
 include Makefile.sources
 
 lib_LTLIBRARIES =
 include Makefile.sources
 
 lib_LTLIBRARIES =
@@ -38,5 +45,6 @@ MKDIR_GEN = $(AM_V_at)$(MKDIR_P) $(@D)
 PYTHON_GEN = $(AM_V_GEN)$(PYTHON2) $(PYTHON_FLAGS)
 
 include Makefile.genxml.am
 PYTHON_GEN = $(AM_V_GEN)$(PYTHON2) $(PYTHON_FLAGS)
 
 include Makefile.genxml.am
+include Makefile.cle.am
 
 CLEANFILES += $(BUILT_SOURCES)
 
 CLEANFILES += $(BUILT_SOURCES)
diff --git a/src/broadcom/Makefile.cle.am b/src/broadcom/Makefile.cle.am
new file mode 100644 (file)
index 0000000..3688267
--- /dev/null
@@ -0,0 +1,4 @@
+noinst_LTLIBRARIES += cle/libbroadcom_cle.la
+
+cle_libbroadcom_cle_la_CFLAGS = $(AM_CFLAGS)
+cle_libbroadcom_cle_la_SOURCES = $(BROADCOM_DECODER_FILES)
index b5ff528f2091ce2ad98f5eceffd1c91319f2619e..6cfabaebcb87b938cd26369f8605d211667f76ed 100644 (file)
@@ -32,6 +32,11 @@ $(BROADCOM_GENXML_GENERATED_FILES): cle/gen_pack_header.py
        $(MKDIR_GEN)
        $(PYTHON_GEN) $(srcdir)/cle/gen_pack_header.py $< > $@ || ($(RM) $@; false)
 
        $(MKDIR_GEN)
        $(PYTHON_GEN) $(srcdir)/cle/gen_pack_header.py $< > $@ || ($(RM) $@; false)
 
+GEN_ZIPPED = $(srcdir)/../intel/genxml/gen_zipped_file.py
+cle/v3d_xml.h: $(GEN_ZIPPED) $(BROADCOM_GENXML_XML_FILES)
+       $(MKDIR_GEN)
+       $(PYTHON_GEN) $(GEN_ZIPPED) $(BROADCOM_GENXML_XML_FILES:%=$(srcdir)/%) > $@ || ($(RM) $@; false)
+
 EXTRA_DIST += \
        cle/gen_pack_header.py \
        $()
 EXTRA_DIST += \
        cle/gen_pack_header.py \
        $()
index 4dadb68ee553af24ea6d99608f38c5f09c05b1bc..7e9a023636f130700d3288daae84c86735085ce2 100644 (file)
@@ -1,5 +1,6 @@
 BROADCOM_GENXML_GENERATED_FILES = \
        cle/v3d_packet_v21_pack.h \
 BROADCOM_GENXML_GENERATED_FILES = \
        cle/v3d_packet_v21_pack.h \
+       cle/v3d_xml.h \
        $()
 
 BROADCOM_GENXML_XML_FILES = \
        $()
 
 BROADCOM_GENXML_XML_FILES = \
@@ -11,3 +12,8 @@ BROADCOM_FILES = \
        common/v3d_device_info.h \
        $()
 
        common/v3d_device_info.h \
        $()
 
+BROADCOM_DECODER_FILES = \
+       cle/v3d_decoder.c \
+       cle/v3d_decoder.h \
+       $()
+
diff --git a/src/broadcom/cle/v3d_decoder.c b/src/broadcom/cle/v3d_decoder.c
new file mode 100644 (file)
index 0000000..66f74af
--- /dev/null
@@ -0,0 +1,855 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ * Copyright © 2017 Broadcom
+ *
+ * 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 <inttypes.h>
+#include <zlib.h>
+
+#include <util/macros.h>
+#include <util/ralloc.h>
+
+#include "v3d_decoder.h"
+#include "v3d_packet_helpers.h"
+#include "v3d_xml.h"
+
+struct v3d_spec {
+        uint32_t ver;
+
+        int ncommands;
+        struct v3d_group *commands[256];
+        int nstructs;
+        struct v3d_group *structs[256];
+        int nregisters;
+        struct v3d_group *registers[256];
+        int nenums;
+        struct v3d_enum *enums[256];
+};
+
+struct location {
+        const char *filename;
+        int line_number;
+};
+
+struct parser_context {
+        XML_Parser parser;
+        int foo;
+        struct location loc;
+
+        struct v3d_group *group;
+        struct v3d_enum *enoom;
+
+        int nvalues;
+        struct v3d_value *values[256];
+
+        struct v3d_spec *spec;
+};
+
+const char *
+v3d_group_get_name(struct v3d_group *group)
+{
+        return group->name;
+}
+
+uint8_t
+v3d_group_get_opcode(struct v3d_group *group)
+{
+        return group->opcode;
+}
+
+struct v3d_group *
+v3d_spec_find_struct(struct v3d_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;
+}
+
+struct v3d_group *
+v3d_spec_find_register(struct v3d_spec *spec, uint32_t offset)
+{
+        for (int i = 0; i < spec->nregisters; i++)
+                if (spec->registers[i]->register_offset == offset)
+                        return spec->registers[i];
+
+        return NULL;
+}
+
+struct v3d_group *
+v3d_spec_find_register_by_name(struct v3d_spec *spec, const char *name)
+{
+        for (int i = 0; i < spec->nregisters; i++) {
+                if (strcmp(spec->registers[i]->name, name) == 0)
+                        return spec->registers[i];
+        }
+
+        return NULL;
+}
+
+struct v3d_enum *
+v3d_spec_find_enum(struct v3d_spec *spec, const char *name)
+{
+        for (int i = 0; i < spec->nenums; i++)
+                if (strcmp(spec->enums[i]->name, name) == 0)
+                        return spec->enums[i];
+
+        return NULL;
+}
+
+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));
+}
+
+/* We allow fields to have either a bit index, or append "b" for a byte index.
+ */
+static bool
+is_byte_offset(const char *value)
+{
+        return value[strlen(value) - 1] == 'b';
+}
+
+static void
+get_group_offset_count(const char **atts, uint32_t *offset, uint32_t *count,
+                       uint32_t *size, bool *variable)
+{
+        char *p;
+        int i;
+
+        for (i = 0; atts[i]; i += 2) {
+                if (strcmp(atts[i], "count") == 0) {
+                        *count = strtoul(atts[i + 1], &p, 0);
+                        if (*count == 0)
+                                *variable = true;
+                } else if (strcmp(atts[i], "start") == 0) {
+                        *offset = strtoul(atts[i + 1], &p, 0);
+                } else if (strcmp(atts[i], "size") == 0) {
+                        *size = strtoul(atts[i + 1], &p, 0);
+                }
+        }
+        return;
+}
+
+static struct v3d_group *
+create_group(struct parser_context *ctx,
+             const char *name,
+             const char **atts,
+             struct v3d_group *parent)
+{
+        struct v3d_group *group;
+
+        group = xzalloc(sizeof(*group));
+        if (name)
+                group->name = xstrdup(name);
+
+        group->spec = ctx->spec;
+        group->group_offset = 0;
+        group->group_count = 0;
+        group->variable = false;
+
+        if (parent) {
+                group->parent = parent;
+                get_group_offset_count(atts,
+                                       &group->group_offset,
+                                       &group->group_count,
+                                       &group->group_size,
+                                       &group->variable);
+        }
+
+        return group;
+}
+
+static struct v3d_enum *
+create_enum(struct parser_context *ctx, const char *name, const char **atts)
+{
+        struct v3d_enum *e;
+
+        e = xzalloc(sizeof(*e));
+        if (name)
+                e->name = xstrdup(name);
+
+        e->nvalues = 0;
+
+        return e;
+}
+
+static void
+get_register_offset(const char **atts, uint32_t *offset)
+{
+        char *p;
+        int i;
+
+        for (i = 0; atts[i]; i += 2) {
+                if (strcmp(atts[i], "num") == 0)
+                        *offset = strtoul(atts[i + 1], &p, 0);
+        }
+        return;
+}
+
+static void
+get_start_end_pos(int *start, int *end)
+{
+        /* start value has to be mod with 32 as we need the relative
+         * start position in the first DWord. For the end position, add
+         * the length of the field to the start position to get the
+         * relative postion in the 64 bit address.
+         */
+        if (*end - *start > 32) {
+                int len = *end - *start;
+                *start = *start % 32;
+                *end = *start + len;
+        } else {
+                *start = *start % 32;
+                *end = *end % 32;
+        }
+
+        return;
+}
+
+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)
+{
+        get_start_end_pos(&start, &end);
+        return (value & mask(start, end)) >> (start);
+}
+
+static inline uint64_t
+field_address(uint64_t value, int start, int end)
+{
+        /* no need to right shift for address/offset */
+        get_start_end_pos(&start, &end);
+        return (value & mask(start, end));
+}
+
+static struct v3d_type
+string_to_type(struct parser_context *ctx, const char *s)
+{
+        int i, f;
+        struct v3d_group *g;
+        struct v3d_enum *e;
+
+        if (strcmp(s, "int") == 0)
+                return (struct v3d_type) { .kind = V3D_TYPE_INT };
+        else if (strcmp(s, "uint") == 0)
+                return (struct v3d_type) { .kind = V3D_TYPE_UINT };
+        else if (strcmp(s, "bool") == 0)
+                return (struct v3d_type) { .kind = V3D_TYPE_BOOL };
+        else if (strcmp(s, "float") == 0)
+                return (struct v3d_type) { .kind = V3D_TYPE_FLOAT };
+        else if (strcmp(s, "address") == 0)
+                return (struct v3d_type) { .kind = V3D_TYPE_ADDRESS };
+        else if (strcmp(s, "offset") == 0)
+                return (struct v3d_type) { .kind = V3D_TYPE_OFFSET };
+        else if (sscanf(s, "u%d.%d", &i, &f) == 2)
+                return (struct v3d_type) { .kind = V3D_TYPE_UFIXED, .i = i, .f = f };
+        else if (sscanf(s, "s%d.%d", &i, &f) == 2)
+                return (struct v3d_type) { .kind = V3D_TYPE_SFIXED, .i = i, .f = f };
+        else if (g = v3d_spec_find_struct(ctx->spec, s), g != NULL)
+                return (struct v3d_type) { .kind = V3D_TYPE_STRUCT, .v3d_struct = g };
+        else if (e = v3d_spec_find_enum(ctx->spec, s), e != NULL)
+                return (struct v3d_type) { .kind = V3D_TYPE_ENUM, .v3d_enum = e };
+        else if (strcmp(s, "mbo") == 0)
+                return (struct v3d_type) { .kind = V3D_TYPE_MBO };
+        else
+                fail(&ctx->loc, "invalid type: %s", s);
+}
+
+static struct v3d_field *
+create_field(struct parser_context *ctx, const char **atts)
+{
+        struct v3d_field *field;
+        char *p;
+        int i;
+        uint32_t size = 0;
+
+        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);
+                        if (is_byte_offset(atts[i + 1]))
+                                field->start *= 8;
+                } else if (strcmp(atts[i], "end") == 0) {
+                        field->end = strtoul(atts[i + 1], &p, 0) - 1;
+                        if (is_byte_offset(atts[i + 1]))
+                                field->end *= 8;
+                } else if (strcmp(atts[i], "size") == 0) {
+                        size = strtoul(atts[i + 1], &p, 0);
+                        if (is_byte_offset(atts[i + 1]))
+                                size *= 8;
+                } 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);
+                }
+        }
+
+        if (size)
+                field->end = field->start + size - 1;
+
+        return field;
+}
+
+static struct v3d_value *
+create_value(struct parser_context *ctx, const char **atts)
+{
+        struct v3d_value *value = xzalloc(sizeof(*value));
+
+        for (int i = 0; atts[i]; i += 2) {
+                if (strcmp(atts[i], "name") == 0)
+                        value->name = xstrdup(atts[i + 1]);
+                else if (strcmp(atts[i], "value") == 0)
+                        value->value = strtoul(atts[i + 1], NULL, 0);
+        }
+
+        return value;
+}
+
+static void
+create_and_append_field(struct parser_context *ctx,
+                        const char **atts)
+{
+        if (ctx->group->nfields == ctx->group->fields_size) {
+                ctx->group->fields_size = MAX2(ctx->group->fields_size * 2, 2);
+                ctx->group->fields =
+                        (struct v3d_field **) realloc(ctx->group->fields,
+                                                      sizeof(ctx->group->fields[0]) *
+                                                      ctx->group->fields_size);
+        }
+
+        ctx->group->fields[ctx->group->nfields++] = create_field(ctx, atts);
+}
+
+static void
+set_group_opcode(struct v3d_group *group, const char **atts)
+{
+        char *p;
+        int i;
+
+        for (i = 0; atts[i]; i += 2) {
+                if (strcmp(atts[i], "code") == 0)
+                        group->opcode = strtoul(atts[i + 1], &p, 0);
+        }
+        return;
+}
+
+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 *ver = 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)
+                        ver = atts[i + 1];
+        }
+
+        if (strcmp(element_name, "vcxml") == 0) {
+                if (ver == NULL)
+                        fail(&ctx->loc, "no ver given");
+
+                int major, minor;
+                int n = sscanf(ver, "%d.%d", &major, &minor);
+                if (n == 0)
+                        fail(&ctx->loc, "invalid ver given: %s", ver);
+                if (n == 1)
+                        minor = 0;
+
+                ctx->spec->ver = major * 10 + minor;
+        } else if (strcmp(element_name, "packet") == 0 ||
+                   strcmp(element_name, "struct") == 0) {
+                ctx->group = create_group(ctx, name, atts, NULL);
+
+                if (strcmp(element_name, "packet") == 0)
+                        set_group_opcode(ctx->group, atts);
+        } else if (strcmp(element_name, "register") == 0) {
+                ctx->group = create_group(ctx, name, atts, NULL);
+                get_register_offset(atts, &ctx->group->register_offset);
+        } else if (strcmp(element_name, "group") == 0) {
+                struct v3d_group *previous_group = ctx->group;
+                while (previous_group->next)
+                        previous_group = previous_group->next;
+
+                struct v3d_group *group = create_group(ctx, "", atts,
+                                                       ctx->group);
+                previous_group->next = group;
+                ctx->group = group;
+        } else if (strcmp(element_name, "field") == 0) {
+                create_and_append_field(ctx, atts);
+        } else if (strcmp(element_name, "enum") == 0) {
+                ctx->enoom = create_enum(ctx, name, atts);
+        } else if (strcmp(element_name, "value") == 0) {
+                ctx->values[ctx->nvalues++] = create_value(ctx, atts);
+                assert(ctx->nvalues < ARRAY_SIZE(ctx->values));
+        }
+
+}
+
+static void
+end_element(void *data, const char *name)
+{
+        struct parser_context *ctx = data;
+        struct v3d_spec *spec = ctx->spec;
+
+        if (strcmp(name, "packet") == 0 ||
+            strcmp(name, "struct") == 0 ||
+            strcmp(name, "register") == 0) {
+                struct v3d_group *group = ctx->group;
+
+                ctx->group = ctx->group->parent;
+
+                if (strcmp(name, "packet") == 0) {
+                        spec->commands[spec->ncommands++] = group;
+
+                        /* V3D packet XML has the packet contents with offsets
+                         * starting from the first bit after the opcode, to
+                         * match the spec.  Shift the fields up now.
+                         */
+                        for (int i = 0; i < group->nfields; i++) {
+                                group->fields[i]->start += 8;
+                                group->fields[i]->end += 8;
+                        }
+                }
+                else if (strcmp(name, "struct") == 0)
+                        spec->structs[spec->nstructs++] = group;
+                else if (strcmp(name, "register") == 0)
+                        spec->registers[spec->nregisters++] = group;
+
+                assert(spec->ncommands < ARRAY_SIZE(spec->commands));
+                assert(spec->nstructs < ARRAY_SIZE(spec->structs));
+                assert(spec->nregisters < ARRAY_SIZE(spec->registers));
+        } else if (strcmp(name, "group") == 0) {
+                ctx->group = ctx->group->parent;
+        } else if (strcmp(name, "field") == 0) {
+                assert(ctx->group->nfields > 0);
+                struct v3d_field *field = ctx->group->fields[ctx->group->nfields - 1];
+                size_t size = ctx->nvalues * sizeof(ctx->values[0]);
+                field->inline_enum.values = xzalloc(size);
+                field->inline_enum.nvalues = ctx->nvalues;
+                memcpy(field->inline_enum.values, ctx->values, size);
+                ctx->nvalues = 0;
+        } else if (strcmp(name, "enum") == 0) {
+                struct v3d_enum *e = ctx->enoom;
+                size_t size = ctx->nvalues * sizeof(ctx->values[0]);
+                e->values = xzalloc(size);
+                e->nvalues = ctx->nvalues;
+                memcpy(e->values, ctx->values, size);
+                ctx->nvalues = 0;
+                ctx->enoom = NULL;
+                spec->enums[spec->nenums++] = e;
+        }
+}
+
+static void
+character_data(void *data, const XML_Char *s, int len)
+{
+}
+
+static uint32_t zlib_inflate(const void *compressed_data,
+                             uint32_t compressed_len,
+                             void **out_ptr)
+{
+        struct z_stream_s zstream;
+        void *out;
+
+        memset(&zstream, 0, sizeof(zstream));
+
+        zstream.next_in = (unsigned char *)compressed_data;
+        zstream.avail_in = compressed_len;
+
+        if (inflateInit(&zstream) != Z_OK)
+                return 0;
+
+        out = malloc(4096);
+        zstream.next_out = out;
+        zstream.avail_out = 4096;
+
+        do {
+                switch (inflate(&zstream, Z_SYNC_FLUSH)) {
+                case Z_STREAM_END:
+                        goto end;
+                case Z_OK:
+                        break;
+                default:
+                        inflateEnd(&zstream);
+                        return 0;
+                }
+
+                if (zstream.avail_out)
+                        break;
+
+                out = realloc(out, 2*zstream.total_out);
+                if (out == NULL) {
+                        inflateEnd(&zstream);
+                        return 0;
+                }
+
+                zstream.next_out = (unsigned char *)out + zstream.total_out;
+                zstream.avail_out = zstream.total_out;
+        } while (1);
+ end:
+        inflateEnd(&zstream);
+        *out_ptr = out;
+        return zstream.total_out;
+}
+
+struct v3d_spec *
+v3d_spec_load(const struct v3d_device_info *devinfo)
+{
+        struct parser_context ctx;
+        void *buf;
+        uint8_t *text_data = NULL;
+        uint32_t text_offset = 0, text_length = 0, total_length;
+
+        for (int i = 0; i < ARRAY_SIZE(genxml_files_table); i++) {
+                if (genxml_files_table[i].gen_10 == devinfo->ver) {
+                        text_offset = genxml_files_table[i].offset;
+                        text_length = genxml_files_table[i].length;
+                        break;
+                }
+        }
+
+        if (text_length == 0) {
+                fprintf(stderr, "unable to find gen (%u) data\n", devinfo->ver);
+                return NULL;
+        }
+
+        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");
+                return NULL;
+        }
+
+        XML_SetElementHandler(ctx.parser, start_element, end_element);
+        XML_SetCharacterDataHandler(ctx.parser, character_data);
+
+        ctx.spec = xzalloc(sizeof(*ctx.spec));
+
+        total_length = zlib_inflate(compress_genxmls,
+                                    sizeof(compress_genxmls),
+                                    (void **) &text_data);
+        assert(text_offset + text_length <= total_length);
+
+        buf = XML_GetBuffer(ctx.parser, text_length);
+        memcpy(buf, &text_data[text_offset], text_length);
+
+        if (XML_ParseBuffer(ctx.parser, text_length, true) == 0) {
+                fprintf(stderr,
+                        "Error parsing XML at line %ld col %ld byte %ld/%u: %s\n",
+                        XML_GetCurrentLineNumber(ctx.parser),
+                        XML_GetCurrentColumnNumber(ctx.parser),
+                        XML_GetCurrentByteIndex(ctx.parser), text_length,
+                        XML_ErrorString(XML_GetErrorCode(ctx.parser)));
+                XML_ParserFree(ctx.parser);
+                free(text_data);
+                return NULL;
+        }
+
+        XML_ParserFree(ctx.parser);
+        free(text_data);
+
+        return ctx.spec;
+}
+
+struct v3d_group *
+v3d_spec_find_instruction(struct v3d_spec *spec, const uint8_t *p)
+{
+        for (int i = 0; i < spec->ncommands; i++) {
+                uint8_t opcode = *p;
+                if (opcode == spec->commands[i]->opcode)
+                        return spec->commands[i];
+        }
+
+        return NULL;
+}
+
+/** Returns the size of a V3D packet. */
+int
+v3d_group_get_length(struct v3d_group *group)
+{
+        int last_bit = 0;
+        for (int i = 0; i < group->nfields; i++) {
+                struct v3d_field *field = group->fields[i];
+
+                last_bit = MAX2(last_bit, field->end);
+        }
+        return last_bit / 8 + 1;
+}
+
+void
+v3d_field_iterator_init(struct v3d_field_iterator *iter,
+                        struct v3d_group *group,
+                        const uint8_t *p,
+                        bool print_colors)
+{
+        memset(iter, 0, sizeof(*iter));
+
+        iter->group = group;
+        iter->p = p;
+        iter->print_colors = print_colors;
+}
+
+static const char *
+v3d_get_enum_name(struct v3d_enum *e, uint64_t value)
+{
+        for (int i = 0; i < e->nvalues; i++) {
+                if (e->values[i]->value == value) {
+                        return e->values[i]->name;
+                }
+        }
+        return NULL;
+}
+
+static bool
+iter_more_fields(const struct v3d_field_iterator *iter)
+{
+        return iter->field_iter < iter->group->nfields;
+}
+
+static uint32_t
+iter_group_offset_bits(const struct v3d_field_iterator *iter,
+                       uint32_t group_iter)
+{
+        return iter->group->group_offset + (group_iter *
+                                            iter->group->group_size);
+}
+
+static bool
+iter_more_groups(const struct v3d_field_iterator *iter)
+{
+        if (iter->group->variable) {
+                return iter_group_offset_bits(iter, iter->group_iter + 1) <
+                        (v3d_group_get_length(iter->group) * 8);
+        } else {
+                return (iter->group_iter + 1) < iter->group->group_count ||
+                        iter->group->next != NULL;
+        }
+}
+
+static void
+iter_advance_group(struct v3d_field_iterator *iter)
+{
+        if (iter->group->variable)
+                iter->group_iter++;
+        else {
+                if ((iter->group_iter + 1) < iter->group->group_count) {
+                        iter->group_iter++;
+                } else {
+                        iter->group = iter->group->next;
+                        iter->group_iter = 0;
+                }
+        }
+
+        iter->field_iter = 0;
+}
+
+static bool
+iter_advance_field(struct v3d_field_iterator *iter)
+{
+        while (!iter_more_fields(iter)) {
+                if (!iter_more_groups(iter))
+                        return false;
+
+                iter_advance_group(iter);
+        }
+
+        iter->field = iter->group->fields[iter->field_iter++];
+        if (iter->field->name)
+                strncpy(iter->name, iter->field->name, sizeof(iter->name));
+        else
+                memset(iter->name, 0, sizeof(iter->name));
+        iter->offset = iter_group_offset_bits(iter, iter->group_iter) / 8 +
+                iter->field->start / 8;
+        iter->struct_desc = NULL;
+
+        return true;
+}
+
+bool
+v3d_field_iterator_next(struct v3d_field_iterator *iter)
+{
+        if (!iter_advance_field(iter))
+                return false;
+
+        const char *enum_name = NULL;
+
+        int s = iter->field->start;
+        int e = iter->field->end;
+
+        switch (iter->field->type.kind) {
+        case V3D_TYPE_UNKNOWN:
+        case V3D_TYPE_INT: {
+                uint32_t value = __gen_unpack_sint(iter->p, s, e);
+                snprintf(iter->value, sizeof(iter->value), "%d", value);
+                enum_name = v3d_get_enum_name(&iter->field->inline_enum, value);
+                break;
+        }
+        case V3D_TYPE_UINT: {
+                uint32_t value = __gen_unpack_uint(iter->p, s, e);
+                snprintf(iter->value, sizeof(iter->value), "%u", value);
+                enum_name = v3d_get_enum_name(&iter->field->inline_enum, value);
+                break;
+        }
+        case V3D_TYPE_BOOL: {
+                const char *true_string =
+                        iter->print_colors ? "\e[0;35mtrue\e[0m" : "true";
+                snprintf(iter->value, sizeof(iter->value), "%s",
+                         __gen_unpack_uint(iter->p, s, e) ?
+                         true_string : "false");
+                break;
+        }
+        case V3D_TYPE_FLOAT:
+                snprintf(iter->value, sizeof(iter->value), "%f",
+                         __gen_unpack_float(iter->p, s, e));
+                break;
+        case V3D_TYPE_ADDRESS:
+        case V3D_TYPE_OFFSET:
+                snprintf(iter->value, sizeof(iter->value), "0x%08"PRIx64,
+                         __gen_unpack_uint(iter->p, s, e));
+                break;
+        case V3D_TYPE_STRUCT:
+                snprintf(iter->value, sizeof(iter->value), "<struct %s>",
+                         iter->field->type.v3d_struct->name);
+                iter->struct_desc =
+                        v3d_spec_find_struct(iter->group->spec,
+                                             iter->field->type.v3d_struct->name);
+                break;
+        case V3D_TYPE_SFIXED:
+                snprintf(iter->value, sizeof(iter->value), "%f",
+                         __gen_unpack_sfixed(iter->p, s, e,
+                                             iter->field->type.f));
+                break;
+        case V3D_TYPE_UFIXED:
+                snprintf(iter->value, sizeof(iter->value), "%f",
+                         __gen_unpack_ufixed(iter->p, s, e,
+                                             iter->field->type.f));
+                break;
+        case V3D_TYPE_MBO:
+                break;
+        case V3D_TYPE_ENUM: {
+                uint32_t value = __gen_unpack_uint(iter->p, s, e);
+                snprintf(iter->value, sizeof(iter->value), "%d", value);
+                enum_name = v3d_get_enum_name(iter->field->type.v3d_enum, value);
+                break;
+        }
+        }
+
+        if (strlen(iter->group->name) == 0) {
+                int length = strlen(iter->name);
+                snprintf(iter->name + length, sizeof(iter->name) - length,
+                         "[%i]", iter->group_iter);
+        }
+
+        if (enum_name) {
+                int length = strlen(iter->value);
+                snprintf(iter->value + length, sizeof(iter->value) - length,
+                         " (%s)", enum_name);
+        }
+
+        return true;
+}
+
+void
+v3d_print_group(FILE *outfile, struct v3d_group *group,
+                uint64_t offset, const uint8_t *p, bool color)
+{
+        struct v3d_field_iterator iter;
+
+        v3d_field_iterator_init(&iter, group, p, color);
+        while (v3d_field_iterator_next(&iter)) {
+                fprintf(outfile, "    %s: %s\n", iter.name, iter.value);
+                if (iter.struct_desc) {
+                        uint64_t struct_offset = offset + iter.offset;
+                        v3d_print_group(outfile, iter.struct_desc,
+                                        struct_offset,
+                                        &p[iter.offset], color);
+                }
+        }
+}
diff --git a/src/broadcom/cle/v3d_decoder.h b/src/broadcom/cle/v3d_decoder.h
new file mode 100644 (file)
index 0000000..541d877
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ * Copyright © 2017 Broadcom
+ *
+ * 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.
+ */
+
+#ifndef V3D_DECODER_H
+#define V3D_DECODER_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "broadcom/common/v3d_device_info.h"
+
+struct v3d_spec;
+struct v3d_group;
+struct v3d_field;
+
+struct v3d_group *v3d_spec_find_struct(struct v3d_spec *spec, const char *name);
+struct v3d_spec *v3d_spec_load(const struct v3d_device_info *devinfo);
+struct v3d_group *v3d_spec_find_instruction(struct v3d_spec *spec, const uint8_t *p);
+struct v3d_group *v3d_spec_find_register(struct v3d_spec *spec, uint32_t offset);
+struct v3d_group *v3d_spec_find_register_by_name(struct v3d_spec *spec, const char *name);
+int v3d_group_get_length(struct v3d_group *group);
+const char *v3d_group_get_name(struct v3d_group *group);
+uint8_t v3d_group_get_opcode(struct v3d_group *group);
+struct v3d_enum *v3d_spec_find_enum(struct v3d_spec *spec, const char *name);
+
+struct v3d_field_iterator {
+        struct v3d_group *group;
+        char name[128];
+        char value[128];
+        struct v3d_group *struct_desc;
+        const uint8_t *p;
+        int offset; /**< current field starts at &p[offset] */
+
+        int field_iter;
+        int group_iter;
+
+        struct v3d_field *field;
+        bool print_colors;
+};
+
+struct v3d_group {
+        struct v3d_spec *spec;
+        char *name;
+
+        struct v3d_field **fields;
+        uint32_t nfields;
+        uint32_t fields_size;
+
+        uint32_t group_offset, group_count;
+        uint32_t group_size;
+        bool variable;
+
+        struct v3d_group *parent;
+        struct v3d_group *next;
+
+        uint8_t opcode;
+
+        /* Register specific */
+        uint32_t register_offset;
+};
+
+struct v3d_value {
+        char *name;
+        uint64_t value;
+};
+
+struct v3d_enum {
+        char *name;
+        int nvalues;
+        struct v3d_value **values;
+};
+
+struct v3d_type {
+        enum {
+                V3D_TYPE_UNKNOWN,
+                V3D_TYPE_INT,
+                V3D_TYPE_UINT,
+                V3D_TYPE_BOOL,
+                V3D_TYPE_FLOAT,
+                V3D_TYPE_ADDRESS,
+                V3D_TYPE_OFFSET,
+                V3D_TYPE_STRUCT,
+                V3D_TYPE_UFIXED,
+                V3D_TYPE_SFIXED,
+                V3D_TYPE_MBO,
+                V3D_TYPE_ENUM
+        } kind;
+
+        /* Struct definition for V3D_TYPE_STRUCT */
+        union {
+                struct v3d_group *v3d_struct;
+                struct v3d_enum *v3d_enum;
+                struct {
+                        /* Integer and fractional sizes for V3D_TYPE_UFIXED and
+                         * V3D_TYPE_SFIXED
+                         */
+                        int i, f;
+                };
+        };
+};
+
+struct v3d_field {
+        char *name;
+        int start, end;
+        struct v3d_type type;
+        bool has_default;
+        uint32_t default_value;
+
+        struct v3d_enum inline_enum;
+};
+
+void v3d_field_iterator_init(struct v3d_field_iterator *iter,
+                             struct v3d_group *group,
+                             const uint8_t *p,
+                             bool print_colors);
+
+bool v3d_field_iterator_next(struct v3d_field_iterator *iter);
+
+void v3d_print_group(FILE *out,
+                     struct v3d_group *group,
+                     uint64_t offset, const uint8_t *p,
+                     bool color);
+
+#endif /* V3D_DECODER_H */