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 35static void 36print_usage(void) 37{ 38 printf("Usage: lima_disasm [OPTIONS]... FILE\n"); 39 printf(" --help - show this message\n"); 40} 41 42typedef 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 */ 78static uint32_t * 79extract_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 150int 151main(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