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
print_usage(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 *
extract_shader_binary(char * filename,uint32_t * size,bool * is_frag)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
main(int argc,char ** argv)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 
168    char *filename = NULL;
169    filename = argv[argc - 1];
170 
171    uint32_t size = 0;
172    uint32_t *prog = extract_shader_binary(filename, &size, &is_frag);
173    if (!prog) {
174       printf("Failed to parse mbs!\n");
175       return -1;
176    }
177 
178    if (is_frag) {
179       assert((size & 0x3) == 0);
180       size >>= 2;
181       uint32_t *bin = prog;
182       uint32_t offset = 0;
183       do {
184          ppir_codegen_ctrl *ctrl = (ppir_codegen_ctrl *)bin;
185          printf("@%6d: ", offset);
186          ppir_disassemble_instr(bin, offset, stdout);
187          bin += ctrl->count;
188          offset += ctrl->count;
189          size -= ctrl->count;
190       } while (size);
191    } else {
192       gpir_disassemble_program((gpir_codegen_instr *)prog, size / (sizeof(gpir_codegen_instr)), stdout);
193    }
194 
195    ralloc_free(prog);
196 
197    return 0;
198 }
199 
200