1/*
2 * Copyright © 2016 Intel Corporation
3 * Copyright © 2017 Broadcom
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25#include "v3d_decoder.h"
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdbool.h>
30#include <stdint.h>
31#include <stdarg.h>
32#include <string.h>
33#ifdef WITH_LIBEXPAT
34#include <expat.h>
35#endif
36#include <inttypes.h>
37#include <zlib.h>
38
39#include <util/macros.h>
40#include <util/ralloc.h>
41#include <util/u_debug.h>
42
43#include "v3d_packet_helpers.h"
44#include "v3d_xml.h"
45#include "broadcom/clif/clif_private.h"
46
47struct v3d_spec {
48        uint32_t ver;
49
50        int ncommands;
51        struct v3d_group *commands[256];
52        int nstructs;
53        struct v3d_group *structs[256];
54        int nregisters;
55        struct v3d_group *registers[256];
56        int nenums;
57        struct v3d_enum *enums[256];
58};
59
60#ifdef WITH_LIBEXPAT
61
62struct location {
63        const char *filename;
64        int line_number;
65};
66
67struct parser_context {
68        XML_Parser parser;
69        const struct v3d_device_info *devinfo;
70        int foo;
71        struct location loc;
72
73        struct v3d_group *group;
74        struct v3d_enum *enoom;
75
76        int nvalues;
77        struct v3d_value *values[256];
78
79        struct v3d_spec *spec;
80
81        int parse_depth;
82        int parse_skip_depth;
83};
84
85#endif /* WITH_LIBEXPAT */
86
87const char *
88v3d_group_get_name(struct v3d_group *group)
89{
90        return group->name;
91}
92
93uint8_t
94v3d_group_get_opcode(struct v3d_group *group)
95{
96        return group->opcode;
97}
98
99struct v3d_group *
100v3d_spec_find_struct(struct v3d_spec *spec, const char *name)
101{
102        for (int i = 0; i < spec->nstructs; i++)
103                if (strcmp(spec->structs[i]->name, name) == 0)
104                        return spec->structs[i];
105
106        return NULL;
107}
108
109struct v3d_group *
110v3d_spec_find_register(struct v3d_spec *spec, uint32_t offset)
111{
112        for (int i = 0; i < spec->nregisters; i++)
113                if (spec->registers[i]->register_offset == offset)
114                        return spec->registers[i];
115
116        return NULL;
117}
118
119struct v3d_group *
120v3d_spec_find_register_by_name(struct v3d_spec *spec, const char *name)
121{
122        for (int i = 0; i < spec->nregisters; i++) {
123                if (strcmp(spec->registers[i]->name, name) == 0)
124                        return spec->registers[i];
125        }
126
127        return NULL;
128}
129
130struct v3d_enum *
131v3d_spec_find_enum(struct v3d_spec *spec, const char *name)
132{
133        for (int i = 0; i < spec->nenums; i++)
134                if (strcmp(spec->enums[i]->name, name) == 0)
135                        return spec->enums[i];
136
137        return NULL;
138}
139
140#ifdef WITH_LIBEXPAT
141
142static void __attribute__((noreturn))
143fail(struct location *loc, const char *msg, ...)
144{
145        va_list ap;
146
147        va_start(ap, msg);
148        fprintf(stderr, "%s:%d: error: ",
149                loc->filename, loc->line_number);
150        vfprintf(stderr, msg, ap);
151        fprintf(stderr, "\n");
152        va_end(ap);
153        exit(EXIT_FAILURE);
154}
155
156static void *
157fail_on_null(void *p)
158{
159        if (p == NULL) {
160                fprintf(stderr, "aubinator: out of memory\n");
161                exit(EXIT_FAILURE);
162        }
163
164        return p;
165}
166
167static char *
168xstrdup(const char *s)
169{
170        return fail_on_null(strdup(s));
171}
172
173static void *
174zalloc(size_t s)
175{
176        return calloc(s, 1);
177}
178
179static void *
180xzalloc(size_t s)
181{
182        return fail_on_null(zalloc(s));
183}
184
185/* We allow fields to have either a bit index, or append "b" for a byte index.
186 */
187static bool
188is_byte_offset(const char *value)
189{
190        return value[strlen(value) - 1] == 'b';
191}
192
193static void
194get_group_offset_count(const char **atts, uint32_t *offset, uint32_t *count,
195                       uint32_t *size, bool *variable)
196{
197        char *p;
198        int i;
199
200        for (i = 0; atts[i]; i += 2) {
201                if (strcmp(atts[i], "count") == 0) {
202                        *count = strtoul(atts[i + 1], &p, 0);
203                        if (*count == 0)
204                                *variable = true;
205                } else if (strcmp(atts[i], "start") == 0) {
206                        *offset = strtoul(atts[i + 1], &p, 0);
207                } else if (strcmp(atts[i], "size") == 0) {
208                        *size = strtoul(atts[i + 1], &p, 0);
209                }
210        }
211        return;
212}
213
214static struct v3d_group *
215create_group(struct parser_context *ctx,
216             const char *name,
217             const char **atts,
218             struct v3d_group *parent)
219{
220        struct v3d_group *group;
221
222        group = xzalloc(sizeof(*group));
223        if (name)
224                group->name = xstrdup(name);
225
226        group->spec = ctx->spec;
227        group->group_offset = 0;
228        group->group_count = 0;
229        group->variable = false;
230
231        if (parent) {
232                group->parent = parent;
233                get_group_offset_count(atts,
234                                       &group->group_offset,
235                                       &group->group_count,
236                                       &group->group_size,
237                                       &group->variable);
238        }
239
240        return group;
241}
242
243static struct v3d_enum *
244create_enum(struct parser_context *ctx, const char *name, const char **atts)
245{
246        struct v3d_enum *e;
247
248        e = xzalloc(sizeof(*e));
249        if (name)
250                e->name = xstrdup(name);
251
252        e->nvalues = 0;
253
254        return e;
255}
256
257static void
258get_register_offset(const char **atts, uint32_t *offset)
259{
260        char *p;
261        int i;
262
263        for (i = 0; atts[i]; i += 2) {
264                if (strcmp(atts[i], "num") == 0)
265                        *offset = strtoul(atts[i + 1], &p, 0);
266        }
267        return;
268}
269
270static void
271get_start_end_pos(int *start, int *end)
272{
273        /* start value has to be mod with 32 as we need the relative
274         * start position in the first DWord. For the end position, add
275         * the length of the field to the start position to get the
276         * relative postion in the 64 bit address.
277         */
278        if (*end - *start > 32) {
279                int len = *end - *start;
280                *start = *start % 32;
281                *end = *start + len;
282        } else {
283                *start = *start % 32;
284                *end = *end % 32;
285        }
286
287        return;
288}
289
290static inline uint64_t
291mask(int start, int end)
292{
293        uint64_t v;
294
295        v = ~0ULL >> (63 - end + start);
296
297        return v << start;
298}
299
300static inline uint64_t
301field(uint64_t value, int start, int end)
302{
303        get_start_end_pos(&start, &end);
304        return (value & mask(start, end)) >> (start);
305}
306
307static inline uint64_t
308field_address(uint64_t value, int start, int end)
309{
310        /* no need to right shift for address/offset */
311        get_start_end_pos(&start, &end);
312        return (value & mask(start, end));
313}
314
315static struct v3d_type
316string_to_type(struct parser_context *ctx, const char *s)
317{
318        int i, f;
319        struct v3d_group *g;
320        struct v3d_enum *e;
321
322        if (strcmp(s, "int") == 0)
323                return (struct v3d_type) { .kind = V3D_TYPE_INT };
324        else if (strcmp(s, "uint") == 0)
325                return (struct v3d_type) { .kind = V3D_TYPE_UINT };
326        else if (strcmp(s, "bool") == 0)
327                return (struct v3d_type) { .kind = V3D_TYPE_BOOL };
328        else if (strcmp(s, "float") == 0)
329                return (struct v3d_type) { .kind = V3D_TYPE_FLOAT };
330        else if (strcmp(s, "f187") == 0)
331                return (struct v3d_type) { .kind = V3D_TYPE_F187 };
332        else if (strcmp(s, "address") == 0)
333                return (struct v3d_type) { .kind = V3D_TYPE_ADDRESS };
334        else if (strcmp(s, "offset") == 0)
335                return (struct v3d_type) { .kind = V3D_TYPE_OFFSET };
336        else if (sscanf(s, "u%d.%d", &i, &f) == 2)
337                return (struct v3d_type) { .kind = V3D_TYPE_UFIXED, .i = i, .f = f };
338        else if (sscanf(s, "s%d.%d", &i, &f) == 2)
339                return (struct v3d_type) { .kind = V3D_TYPE_SFIXED, .i = i, .f = f };
340        else if (g = v3d_spec_find_struct(ctx->spec, s), g != NULL)
341                return (struct v3d_type) { .kind = V3D_TYPE_STRUCT, .v3d_struct = g };
342        else if (e = v3d_spec_find_enum(ctx->spec, s), e != NULL)
343                return (struct v3d_type) { .kind = V3D_TYPE_ENUM, .v3d_enum = e };
344        else if (strcmp(s, "mbo") == 0)
345                return (struct v3d_type) { .kind = V3D_TYPE_MBO };
346        else
347                fail(&ctx->loc, "invalid type: %s", s);
348}
349
350static struct v3d_field *
351create_field(struct parser_context *ctx, const char **atts)
352{
353        struct v3d_field *field;
354        char *p;
355        int i;
356        uint32_t size = 0;
357
358        field = xzalloc(sizeof(*field));
359
360        for (i = 0; atts[i]; i += 2) {
361                if (strcmp(atts[i], "name") == 0)
362                        field->name = xstrdup(atts[i + 1]);
363                else if (strcmp(atts[i], "start") == 0) {
364                        field->start = strtoul(atts[i + 1], &p, 0);
365                        if (is_byte_offset(atts[i + 1]))
366                                field->start *= 8;
367                } else if (strcmp(atts[i], "end") == 0) {
368                        field->end = strtoul(atts[i + 1], &p, 0) - 1;
369                        if (is_byte_offset(atts[i + 1]))
370                                field->end *= 8;
371                } else if (strcmp(atts[i], "size") == 0) {
372                        size = strtoul(atts[i + 1], &p, 0);
373                        if (is_byte_offset(atts[i + 1]))
374                                size *= 8;
375                } else if (strcmp(atts[i], "type") == 0)
376                        field->type = string_to_type(ctx, atts[i + 1]);
377                else if (strcmp(atts[i], "default") == 0) {
378                        field->has_default = true;
379                        field->default_value = strtoul(atts[i + 1], &p, 0);
380                } else if (strcmp(atts[i], "minus_one") == 0) {
381                        assert(strcmp(atts[i + 1], "true") == 0);
382                        field->minus_one = true;
383                }
384        }
385
386        if (size)
387                field->end = field->start + size - 1;
388
389        return field;
390}
391
392static struct v3d_value *
393create_value(struct parser_context *ctx, const char **atts)
394{
395        struct v3d_value *value = xzalloc(sizeof(*value));
396
397        for (int i = 0; atts[i]; i += 2) {
398                if (strcmp(atts[i], "name") == 0)
399                        value->name = xstrdup(atts[i + 1]);
400                else if (strcmp(atts[i], "value") == 0)
401                        value->value = strtoul(atts[i + 1], NULL, 0);
402        }
403
404        return value;
405}
406
407static void
408create_and_append_field(struct parser_context *ctx,
409                        const char **atts)
410{
411        if (ctx->group->nfields == ctx->group->fields_size) {
412                ctx->group->fields_size = MAX2(ctx->group->fields_size * 2, 2);
413                ctx->group->fields =
414                        (struct v3d_field **) realloc(ctx->group->fields,
415                                                      sizeof(ctx->group->fields[0]) *
416                                                      ctx->group->fields_size);
417        }
418
419        ctx->group->fields[ctx->group->nfields++] = create_field(ctx, atts);
420}
421
422static void
423set_group_opcode(struct v3d_group *group, const char **atts)
424{
425        char *p;
426        int i;
427
428        for (i = 0; atts[i]; i += 2) {
429                if (strcmp(atts[i], "code") == 0)
430                        group->opcode = strtoul(atts[i + 1], &p, 0);
431        }
432        return;
433}
434
435static bool
436ver_in_range(int ver, int min_ver, int max_ver)
437{
438        return ((min_ver == 0 || ver >= min_ver) &&
439                (max_ver == 0 || ver <= max_ver));
440}
441
442static bool
443skip_if_ver_mismatch(struct parser_context *ctx, int min_ver, int max_ver)
444{
445        if (!ctx->parse_skip_depth && !ver_in_range(ctx->devinfo->ver,
446                                                    min_ver, max_ver)) {
447                assert(ctx->parse_depth != 0);
448                ctx->parse_skip_depth = ctx->parse_depth;
449        }
450
451        return ctx->parse_skip_depth;
452}
453
454static void
455start_element(void *data, const char *element_name, const char **atts)
456{
457        struct parser_context *ctx = data;
458        int i;
459        const char *name = NULL;
460        const char *ver = NULL;
461        int min_ver = 0;
462        int max_ver = 0;
463
464        ctx->loc.line_number = XML_GetCurrentLineNumber(ctx->parser);
465
466        for (i = 0; atts[i]; i += 2) {
467                if (strcmp(atts[i], "shortname") == 0)
468                        name = atts[i + 1];
469                else if (strcmp(atts[i], "name") == 0 && !name)
470                        name = atts[i + 1];
471                else if (strcmp(atts[i], "gen") == 0)
472                        ver = atts[i + 1];
473                else if (strcmp(atts[i], "min_ver") == 0)
474                        min_ver = strtoul(atts[i + 1], NULL, 0);
475                else if (strcmp(atts[i], "max_ver") == 0)
476                        max_ver = strtoul(atts[i + 1], NULL, 0);
477        }
478
479        if (skip_if_ver_mismatch(ctx, min_ver, max_ver))
480                goto skip;
481
482        if (strcmp(element_name, "vcxml") == 0) {
483                if (ver == NULL)
484                        fail(&ctx->loc, "no ver given");
485
486                /* Make sure that we picked an XML that matched our version.
487                 */
488                assert(ver_in_range(ctx->devinfo->ver, min_ver, max_ver));
489
490                int major, minor;
491                int n = sscanf(ver, "%d.%d", &major, &minor);
492                if (n == 0)
493                        fail(&ctx->loc, "invalid ver given: %s", ver);
494                if (n == 1)
495                        minor = 0;
496
497                ctx->spec->ver = major * 10 + minor;
498        } else if (strcmp(element_name, "packet") == 0 ||
499                   strcmp(element_name, "struct") == 0) {
500                ctx->group = create_group(ctx, name, atts, NULL);
501
502                if (strcmp(element_name, "packet") == 0)
503                        set_group_opcode(ctx->group, atts);
504        } else if (strcmp(element_name, "register") == 0) {
505                ctx->group = create_group(ctx, name, atts, NULL);
506                get_register_offset(atts, &ctx->group->register_offset);
507        } else if (strcmp(element_name, "group") == 0) {
508                struct v3d_group *previous_group = ctx->group;
509                while (previous_group->next)
510                        previous_group = previous_group->next;
511
512                struct v3d_group *group = create_group(ctx, "", atts,
513                                                       ctx->group);
514                previous_group->next = group;
515                ctx->group = group;
516        } else if (strcmp(element_name, "field") == 0) {
517                create_and_append_field(ctx, atts);
518        } else if (strcmp(element_name, "enum") == 0) {
519                ctx->enoom = create_enum(ctx, name, atts);
520        } else if (strcmp(element_name, "value") == 0) {
521                ctx->values[ctx->nvalues++] = create_value(ctx, atts);
522                assert(ctx->nvalues < ARRAY_SIZE(ctx->values));
523        }
524
525skip:
526        ctx->parse_depth++;
527}
528
529static int
530field_offset_compare(const void *a, const void *b)
531{
532        return ((*(const struct v3d_field **)a)->start -
533                (*(const struct v3d_field **)b)->start);
534}
535
536static void
537end_element(void *data, const char *name)
538{
539        struct parser_context *ctx = data;
540        struct v3d_spec *spec = ctx->spec;
541
542        ctx->parse_depth--;
543
544        if (ctx->parse_skip_depth) {
545                if (ctx->parse_skip_depth == ctx->parse_depth)
546                        ctx->parse_skip_depth = 0;
547                return;
548        }
549
550        if (strcmp(name, "packet") == 0 ||
551            strcmp(name, "struct") == 0 ||
552            strcmp(name, "register") == 0) {
553                struct v3d_group *group = ctx->group;
554
555                ctx->group = ctx->group->parent;
556
557                if (strcmp(name, "packet") == 0) {
558                        spec->commands[spec->ncommands++] = group;
559
560                        /* V3D packet XML has the packet contents with offsets
561                         * starting from the first bit after the opcode, to
562                         * match the spec.  Shift the fields up now.
563                         */
564                        for (int i = 0; i < group->nfields; i++) {
565                                group->fields[i]->start += 8;
566                                group->fields[i]->end += 8;
567                        }
568                }
569                else if (strcmp(name, "struct") == 0)
570                        spec->structs[spec->nstructs++] = group;
571                else if (strcmp(name, "register") == 0)
572                        spec->registers[spec->nregisters++] = group;
573
574                /* Sort the fields in increasing offset order.  The XML might
575                 * be specified in any order, but we'll want to iterate from
576                 * the bottom.
577                 */
578                qsort(group->fields, group->nfields, sizeof(*group->fields),
579                      field_offset_compare);
580
581                assert(spec->ncommands < ARRAY_SIZE(spec->commands));
582                assert(spec->nstructs < ARRAY_SIZE(spec->structs));
583                assert(spec->nregisters < ARRAY_SIZE(spec->registers));
584        } else if (strcmp(name, "group") == 0) {
585                ctx->group = ctx->group->parent;
586        } else if (strcmp(name, "field") == 0) {
587                assert(ctx->group->nfields > 0);
588                struct v3d_field *field = ctx->group->fields[ctx->group->nfields - 1];
589                size_t size = ctx->nvalues * sizeof(ctx->values[0]);
590                field->inline_enum.values = xzalloc(size);
591                field->inline_enum.nvalues = ctx->nvalues;
592                memcpy(field->inline_enum.values, ctx->values, size);
593                ctx->nvalues = 0;
594        } else if (strcmp(name, "enum") == 0) {
595                struct v3d_enum *e = ctx->enoom;
596                size_t size = ctx->nvalues * sizeof(ctx->values[0]);
597                e->values = xzalloc(size);
598                e->nvalues = ctx->nvalues;
599                memcpy(e->values, ctx->values, size);
600                ctx->nvalues = 0;
601                ctx->enoom = NULL;
602                spec->enums[spec->nenums++] = e;
603        }
604}
605
606static void
607character_data(void *data, const XML_Char *s, int len)
608{
609}
610
611static uint32_t zlib_inflate(const void *compressed_data,
612                             uint32_t compressed_len,
613                             void **out_ptr)
614{
615        struct z_stream_s zstream;
616        void *out;
617
618        memset(&zstream, 0, sizeof(zstream));
619
620        zstream.next_in = (unsigned char *)compressed_data;
621        zstream.avail_in = compressed_len;
622
623        if (inflateInit(&zstream) != Z_OK)
624                return 0;
625
626        out = malloc(4096);
627        zstream.next_out = out;
628        zstream.avail_out = 4096;
629
630        do {
631                switch (inflate(&zstream, Z_SYNC_FLUSH)) {
632                case Z_STREAM_END:
633                        goto end;
634                case Z_OK:
635                        break;
636                default:
637                        inflateEnd(&zstream);
638                        return 0;
639                }
640
641                if (zstream.avail_out)
642                        break;
643
644                out = realloc(out, 2*zstream.total_out);
645                if (out == NULL) {
646                        inflateEnd(&zstream);
647                        return 0;
648                }
649
650                zstream.next_out = (unsigned char *)out + zstream.total_out;
651                zstream.avail_out = zstream.total_out;
652        } while (1);
653 end:
654        inflateEnd(&zstream);
655        *out_ptr = out;
656        return zstream.total_out;
657}
658
659#endif /* WITH_LIBEXPAT */
660
661struct v3d_spec *
662v3d_spec_load(const struct v3d_device_info *devinfo)
663{
664        struct v3d_spec *spec = calloc(1, sizeof(struct v3d_spec));
665        if (!spec)
666                return NULL;
667
668#ifdef WITH_LIBEXPAT
669        struct parser_context ctx;
670        void *buf;
671        uint8_t *text_data = NULL;
672        uint32_t text_offset = 0, text_length = 0;
673        ASSERTED uint32_t total_length;
674
675        for (int i = 0; i < ARRAY_SIZE(genxml_files_table); i++) {
676                if (i != 0) {
677                        assert(genxml_files_table[i - 1].ver_10 <
678                               genxml_files_table[i].ver_10);
679                }
680
681                if (genxml_files_table[i].ver_10 <= devinfo->ver) {
682                        text_offset = genxml_files_table[i].offset;
683                        text_length = genxml_files_table[i].length;
684                }
685        }
686
687        if (text_length == 0) {
688                fprintf(stderr, "unable to find gen (%u) data\n", devinfo->ver);
689                free(spec);
690                return NULL;
691        }
692
693        memset(&ctx, 0, sizeof ctx);
694        ctx.parser = XML_ParserCreate(NULL);
695        ctx.devinfo = devinfo;
696        XML_SetUserData(ctx.parser, &ctx);
697        if (ctx.parser == NULL) {
698                fprintf(stderr, "failed to create parser\n");
699                free(spec);
700                return NULL;
701        }
702
703        XML_SetElementHandler(ctx.parser, start_element, end_element);
704        XML_SetCharacterDataHandler(ctx.parser, character_data);
705
706        ctx.spec = spec;
707
708        total_length = zlib_inflate(compress_genxmls,
709                                    sizeof(compress_genxmls),
710                                    (void **) &text_data);
711        assert(text_offset + text_length <= total_length);
712
713        buf = XML_GetBuffer(ctx.parser, text_length);
714        memcpy(buf, &text_data[text_offset], text_length);
715
716        if (XML_ParseBuffer(ctx.parser, text_length, true) == 0) {
717                fprintf(stderr,
718                        "Error parsing XML at line %ld col %ld byte %ld/%u: %s\n",
719                        XML_GetCurrentLineNumber(ctx.parser),
720                        XML_GetCurrentColumnNumber(ctx.parser),
721                        XML_GetCurrentByteIndex(ctx.parser), text_length,
722                        XML_ErrorString(XML_GetErrorCode(ctx.parser)));
723                XML_ParserFree(ctx.parser);
724                free(text_data);
725                free(spec);
726                return NULL;
727        }
728
729        XML_ParserFree(ctx.parser);
730        free(text_data);
731
732        return ctx.spec;
733#else /* !WITH_LIBEXPAT */
734        debug_warn_once("CLIF dumping not supported due to missing libexpat");
735        return spec;
736#endif /* !WITH_LIBEXPAT */
737}
738
739struct v3d_group *
740v3d_spec_find_instruction(struct v3d_spec *spec, const uint8_t *p)
741{
742        uint8_t opcode = *p;
743
744        for (int i = 0; i < spec->ncommands; i++) {
745                struct v3d_group *group = spec->commands[i];
746
747                if (opcode != group->opcode)
748                        continue;
749
750                /* If there's a "sub-id" field, make sure that it matches the
751                 * instruction being decoded.
752                 */
753                struct v3d_field *subid = NULL;
754                for (int j = 0; j < group->nfields; j++) {
755                        struct v3d_field *field = group->fields[j];
756                        if (strcmp(field->name, "sub-id") == 0) {
757                                subid = field;
758                                break;
759                        }
760                }
761
762                if (subid && (__gen_unpack_uint(p, subid->start, subid->end) !=
763                              subid->default_value)) {
764                        continue;
765                }
766
767                return group;
768        }
769
770        return NULL;
771}
772
773/** Returns the size of a V3D packet. */
774int
775v3d_group_get_length(struct v3d_group *group)
776{
777        int last_bit = 0;
778        for (int i = 0; i < group->nfields; i++) {
779                struct v3d_field *field = group->fields[i];
780
781                last_bit = MAX2(last_bit, field->end);
782        }
783        return last_bit / 8 + 1;
784}
785
786void
787v3d_field_iterator_init(struct v3d_field_iterator *iter,
788                        struct v3d_group *group,
789                        const uint8_t *p)
790{
791        memset(iter, 0, sizeof(*iter));
792
793        iter->group = group;
794        iter->p = p;
795}
796
797static const char *
798v3d_get_enum_name(struct v3d_enum *e, uint64_t value)
799{
800        for (int i = 0; i < e->nvalues; i++) {
801                if (e->values[i]->value == value) {
802                        return e->values[i]->name;
803                }
804        }
805        return NULL;
806}
807
808static bool
809iter_more_fields(const struct v3d_field_iterator *iter)
810{
811        return iter->field_iter < iter->group->nfields;
812}
813
814static uint32_t
815iter_group_offset_bits(const struct v3d_field_iterator *iter,
816                       uint32_t group_iter)
817{
818        return iter->group->group_offset + (group_iter *
819                                            iter->group->group_size);
820}
821
822static bool
823iter_more_groups(const struct v3d_field_iterator *iter)
824{
825        if (iter->group->variable) {
826                return iter_group_offset_bits(iter, iter->group_iter + 1) <
827                        (v3d_group_get_length(iter->group) * 8);
828        } else {
829                return (iter->group_iter + 1) < iter->group->group_count ||
830                        iter->group->next != NULL;
831        }
832}
833
834static void
835iter_advance_group(struct v3d_field_iterator *iter)
836{
837        if (iter->group->variable)
838                iter->group_iter++;
839        else {
840                if ((iter->group_iter + 1) < iter->group->group_count) {
841                        iter->group_iter++;
842                } else {
843                        iter->group = iter->group->next;
844                        iter->group_iter = 0;
845                }
846        }
847
848        iter->field_iter = 0;
849}
850
851static bool
852iter_advance_field(struct v3d_field_iterator *iter)
853{
854        while (!iter_more_fields(iter)) {
855                if (!iter_more_groups(iter))
856                        return false;
857
858                iter_advance_group(iter);
859        }
860
861        iter->field = iter->group->fields[iter->field_iter++];
862        if (iter->field->name)
863                snprintf(iter->name, sizeof(iter->name), "%s", iter->field->name);
864        else
865                memset(iter->name, 0, sizeof(iter->name));
866        iter->offset = iter_group_offset_bits(iter, iter->group_iter) / 8 +
867                iter->field->start / 8;
868        iter->struct_desc = NULL;
869
870        return true;
871}
872
873bool
874v3d_field_iterator_next(struct clif_dump *clif, struct v3d_field_iterator *iter)
875{
876        if (!iter_advance_field(iter))
877                return false;
878
879        const char *enum_name = NULL;
880
881        int group_member_offset =
882                iter_group_offset_bits(iter, iter->group_iter);
883        int s = group_member_offset + iter->field->start;
884        int e = group_member_offset + iter->field->end;
885
886        assert(!iter->field->minus_one ||
887               iter->field->type.kind == V3D_TYPE_INT ||
888               iter->field->type.kind == V3D_TYPE_UINT);
889
890        switch (iter->field->type.kind) {
891        case V3D_TYPE_UNKNOWN:
892        case V3D_TYPE_INT: {
893                uint32_t value = __gen_unpack_sint(iter->p, s, e);
894                if (iter->field->minus_one)
895                        value++;
896                snprintf(iter->value, sizeof(iter->value), "%d", value);
897                enum_name = v3d_get_enum_name(&iter->field->inline_enum, value);
898                break;
899        }
900        case V3D_TYPE_UINT: {
901                uint32_t value = __gen_unpack_uint(iter->p, s, e);
902                if (iter->field->minus_one)
903                        value++;
904                if (strcmp(iter->field->name, "Vec size") == 0 && value == 0)
905                        value = 1 << (e - s + 1);
906                snprintf(iter->value, sizeof(iter->value), "%u", value);
907                enum_name = v3d_get_enum_name(&iter->field->inline_enum, value);
908                break;
909        }
910        case V3D_TYPE_BOOL:
911                snprintf(iter->value, sizeof(iter->value), "%s",
912                         __gen_unpack_uint(iter->p, s, e) ?
913                         "1 /* true */" : "0 /* false */");
914                break;
915        case V3D_TYPE_FLOAT:
916                snprintf(iter->value, sizeof(iter->value), "%f",
917                         __gen_unpack_float(iter->p, s, e));
918                break;
919
920        case V3D_TYPE_F187:
921                snprintf(iter->value, sizeof(iter->value), "%f",
922                         __gen_unpack_f187(iter->p, s, e));
923                break;
924
925        case V3D_TYPE_ADDRESS: {
926                uint32_t addr =
927                        __gen_unpack_uint(iter->p, s, e) << (31 - (e - s));
928                struct clif_bo *bo = clif_lookup_bo(clif, addr);
929                if (bo) {
930                        snprintf(iter->value, sizeof(iter->value),
931                                 "[%s+0x%08x] /* 0x%08x */",
932                                 bo->name, addr - bo->offset, addr);
933                } else if (addr) {
934                        snprintf(iter->value, sizeof(iter->value),
935                                 "/* XXX: BO unknown */ 0x%08x", addr);
936                } else {
937                        snprintf(iter->value, sizeof(iter->value),
938                                 "[null]");
939                }
940
941                break;
942        }
943
944        case V3D_TYPE_OFFSET:
945                snprintf(iter->value, sizeof(iter->value), "0x%08"PRIx64,
946                         __gen_unpack_uint(iter->p, s, e) << (31 - (e - s)));
947                break;
948        case V3D_TYPE_STRUCT:
949                snprintf(iter->value, sizeof(iter->value), "<struct %s>",
950                         iter->field->type.v3d_struct->name);
951                iter->struct_desc =
952                        v3d_spec_find_struct(iter->group->spec,
953                                             iter->field->type.v3d_struct->name);
954                break;
955        case V3D_TYPE_SFIXED:
956                if (clif->pretty) {
957                        snprintf(iter->value, sizeof(iter->value), "%f",
958                                 __gen_unpack_sfixed(iter->p, s, e,
959                                                     iter->field->type.f));
960                } else {
961                        snprintf(iter->value, sizeof(iter->value), "%u",
962                                 (unsigned)__gen_unpack_uint(iter->p, s, e));
963                }
964                break;
965        case V3D_TYPE_UFIXED:
966                if (clif->pretty) {
967                        snprintf(iter->value, sizeof(iter->value), "%f",
968                                 __gen_unpack_ufixed(iter->p, s, e,
969                                                     iter->field->type.f));
970                } else {
971                        snprintf(iter->value, sizeof(iter->value), "%u",
972                                 (unsigned)__gen_unpack_uint(iter->p, s, e));
973                }
974                break;
975        case V3D_TYPE_MBO:
976                break;
977        case V3D_TYPE_ENUM: {
978                uint32_t value = __gen_unpack_uint(iter->p, s, e);
979                snprintf(iter->value, sizeof(iter->value), "%d", value);
980                enum_name = v3d_get_enum_name(iter->field->type.v3d_enum, value);
981                break;
982        }
983        }
984
985        if (strlen(iter->group->name) == 0) {
986                int length = strlen(iter->name);
987                snprintf(iter->name + length, sizeof(iter->name) - length,
988                         "[%i]", iter->group_iter);
989        }
990
991        if (enum_name) {
992                int length = strlen(iter->value);
993                snprintf(iter->value + length, sizeof(iter->value) - length,
994                         " /* %s */", enum_name);
995        }
996
997        return true;
998}
999
1000void
1001v3d_print_group(struct clif_dump *clif, struct v3d_group *group,
1002                uint64_t offset, const uint8_t *p)
1003{
1004        struct v3d_field_iterator iter;
1005
1006        v3d_field_iterator_init(&iter, group, p);
1007        while (v3d_field_iterator_next(clif, &iter)) {
1008                /* Clif parsing uses the packet name, and expects no
1009                 * sub-id.
1010                 */
1011                if (strcmp(iter.field->name, "sub-id") == 0 ||
1012                    strcmp(iter.field->name, "unused") == 0 ||
1013                    strcmp(iter.field->name, "Pad") == 0)
1014                        continue;
1015
1016                if (clif->pretty) {
1017                        fprintf(clif->out, "    %s: %s\n",
1018                                iter.name, iter.value);
1019                } else {
1020                        fprintf(clif->out, "  /* %30s: */ %s\n",
1021                                iter.name, iter.value);
1022                }
1023                if (iter.struct_desc) {
1024                        uint64_t struct_offset = offset + iter.offset;
1025                        v3d_print_group(clif, iter.struct_desc,
1026                                        struct_offset,
1027                                        &p[iter.offset]);
1028                }
1029        }
1030}
1031