lima: add standalone disassembler with primitive MBS parser
[mesa.git] / src / gallium / drivers / lima / standalone / lima_disasm.c
1 /*
2 * Copyright (c) 2019 Vasily Khoruzhick <anarsoul@gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25 #include "util/ralloc.h"
26
27 #include <err.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdint.h>
31
32 #include "ir/pp/codegen.h"
33 #include "ir/gp/codegen.h"
34
35 static void
36 print_usage(void)
37 {
38 printf("Usage: lima_disasm [OPTIONS]... FILE\n");
39 printf(" --help - show this message\n");
40 }
41
42 typedef struct __attribute__((__packed__)) {
43 char name[4];
44 uint32_t size;
45 } mbs_chunk;
46
47 /* Parses an MBS1 file. MBS1 is used for Mali-400 and earlier which only support
48 * GLES2, as opposed to MBS2 which is used by later Mali gens, and contains
49 * the entire inferface between the compiler and the (blob) driver. It's
50 * produced by the offline compiler as well as glGetProgramBinary(). The
51 * format is documented at
52 * https://web.archive.org/web/20171026141029/http://limadriver.org/MBS+File+Format/
53 * and consists of a bunch of nested "chunks" where each chunk has a
54 * 4-character tag followed by a 32-bit size, then the contents of the chunk.
55 * The chunks are nested as follows:
56 *
57 * - MBS1
58 * - optional CFRA (fragment shader)
59 * - core version (uint32_t, Mali-200 vs Mali-400)
60 * - FSTA (Fragment STAck information)
61 * - FDIS (if Fragment shader contains a DIScard instruction)
62 * - FBUU (information on color/depth reads/writes)
63 * - SUNI (uniform symbol table)
64 * - SVAR (varying symbol table)
65 * - DBIN (the actual code)
66 * - optional CVER (vertex shader)
67 * - core version (uint32_t, GP2 vs Mali-400)
68 * - FINS (# of instruction and attrib_prefetch)
69 * - SUNI (uniform table)
70 * - SATT (attribute table)
71 * - SVAR (varying table)
72 * - DBIN (the actual code)
73 *
74 * This routine just finds the DBIN chunk and returns the binary assuming
75 * there's only the fragment or vertex shader. We don't bother to parse the
76 * other stuff yet.
77 */
78 static uint32_t *
79 extract_shader_binary(char *filename, uint32_t *size, bool *is_frag)
80 {
81 mbs_chunk chunk;
82
83 if (!filename || !size || !is_frag)
84 return NULL;
85
86 FILE *in = fopen(filename, "rb");
87 if (!in)
88 return NULL;
89
90 if (!fread(&chunk, sizeof(chunk), 1, in)) {
91 printf("Failed to read MBS1 segment\n");
92 return NULL;
93 }
94
95 if (strncmp(chunk.name, "MBS1", 4)) {
96 printf("File is not MBS\n");
97 return NULL;
98 }
99
100 if (!fread(&chunk, sizeof(chunk), 1, in)) {
101 printf("Failed to read shader segment\n");
102 return NULL;
103 }
104
105 if (!strncmp(chunk.name, "CFRA", 4)) {
106 *is_frag = true;
107 } else if (!strncmp(chunk.name, "CVER", 4)) {
108 *is_frag = false;
109 } else {
110 printf("Unsupported shader type\n");
111 return NULL;
112 }
113
114 /* Skip version */
115 fseek(in, 4, SEEK_CUR);
116
117 /* Skip the other chunks and find the DBIN chunk. */
118 do {
119 if (!fread(&chunk, sizeof(chunk), 1, in)) {
120 printf("Failed to read segment\n");
121 return NULL;
122 }
123 if (!strncmp(chunk.name, "DBIN", 4))
124 break;
125 fseek(in, chunk.size, SEEK_CUR);
126 } while (!feof(in));
127
128 if (feof(in)) {
129 printf("CBIN segment not found!\n");
130 return NULL;
131 }
132
133 *size = chunk.size;
134
135 uint32_t *bin = ralloc_size(NULL, chunk.size);
136 if (!bin) {
137 printf("Failed to allocate shader binary\n");
138 return NULL;
139 }
140
141 if (!fread(bin, chunk.size, 1, in)) {
142 printf("Failed to read shader binary\n");
143 ralloc_free(bin);
144 bin = NULL;
145 }
146
147 return bin;
148 }
149
150 int
151 main(int argc, char **argv)
152 {
153 int n;
154 bool is_frag = true;
155
156 if (argc < 2) {
157 print_usage();
158 return 1;
159 }
160
161 for (n = 1; n < argc; n++) {
162 if (!strcmp(argv[n], "--help")) {
163 print_usage();
164 return 1;
165 }
166
167 break;
168 }
169
170 char *filename = NULL;
171 filename = argv[n];
172
173 uint32_t size = 0;
174 uint32_t *prog = extract_shader_binary(filename, &size, &is_frag);
175 if (!prog) {
176 printf("Failed to parse mbs!\n");
177 return -1;
178 }
179
180 if (is_frag) {
181 assert((size & 0x3) == 0);
182 size >>= 2;
183 uint32_t *bin = prog;
184 uint32_t offset = 0;
185 do {
186 ppir_codegen_ctrl *ctrl = (ppir_codegen_ctrl *)bin;
187 printf("@%6d: ", offset);
188 ppir_disassemble_instr(bin, offset);
189 bin += ctrl->count;
190 offset += ctrl->count;
191 size -= ctrl->count;
192 } while (size);
193 } else {
194 gpir_disassemble_program((gpir_codegen_instr *)prog, size / (sizeof(gpir_codegen_instr)));
195 }
196
197 ralloc_free(prog);
198
199 return 0;
200 }
201