diff --git a/src/gallium/drivers/lima/meson.build b/src/gallium/drivers/lima/meson.build index 29c15bc2e13..bf91c78856e 100644 --- a/src/gallium/drivers/lima/meson.build +++ b/src/gallium/drivers/lima/meson.build @@ -125,3 +125,21 @@ lima_compiler = executable( install : with_tools.contains('lima'), ) +lima_disasm = executable( + 'lima_disasm', + files( + 'standalone/lima_disasm.c', + ), + include_directories : [ + inc_src, inc_include, inc_gallium, inc_gallium_aux, inc_gallium_drivers, inc_mesa, inc_mapi, inc_compiler, + ], + dependencies : [ + idep_mesautil, + ], + link_with : [ + liblima, + libpanfrost_shared, + ], + build_by_default : with_tools.contains('lima'), + install : with_tools.contains('lima'), +) diff --git a/src/gallium/drivers/lima/standalone/lima_disasm.c b/src/gallium/drivers/lima/standalone/lima_disasm.c new file mode 100644 index 00000000000..334d13eb6d9 --- /dev/null +++ b/src/gallium/drivers/lima/standalone/lima_disasm.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2019 Vasily Khoruzhick + * + * 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, sub license, + * 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 NON-INFRINGEMENT. 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 "util/ralloc.h" + +#include +#include +#include +#include + +#include "ir/pp/codegen.h" +#include "ir/gp/codegen.h" + +static void +print_usage(void) +{ + printf("Usage: lima_disasm [OPTIONS]... FILE\n"); + printf(" --help - show this message\n"); +} + +typedef struct __attribute__((__packed__)) { + char name[4]; + uint32_t size; +} mbs_chunk; + +/* Parses an MBS1 file. MBS1 is used for Mali-400 and earlier which only support + * GLES2, as opposed to MBS2 which is used by later Mali gens, and contains + * the entire inferface between the compiler and the (blob) driver. It's + * produced by the offline compiler as well as glGetProgramBinary(). The + * format is documented at + * https://web.archive.org/web/20171026141029/http://limadriver.org/MBS+File+Format/ + * and consists of a bunch of nested "chunks" where each chunk has a + * 4-character tag followed by a 32-bit size, then the contents of the chunk. + * The chunks are nested as follows: + * + * - MBS1 + * - optional CFRA (fragment shader) + * - core version (uint32_t, Mali-200 vs Mali-400) + * - FSTA (Fragment STAck information) + * - FDIS (if Fragment shader contains a DIScard instruction) + * - FBUU (information on color/depth reads/writes) + * - SUNI (uniform symbol table) + * - SVAR (varying symbol table) + * - DBIN (the actual code) + * - optional CVER (vertex shader) + * - core version (uint32_t, GP2 vs Mali-400) + * - FINS (# of instruction and attrib_prefetch) + * - SUNI (uniform table) + * - SATT (attribute table) + * - SVAR (varying table) + * - DBIN (the actual code) + * + * This routine just finds the DBIN chunk and returns the binary assuming + * there's only the fragment or vertex shader. We don't bother to parse the + * other stuff yet. + */ +static uint32_t * +extract_shader_binary(char *filename, uint32_t *size, bool *is_frag) +{ + mbs_chunk chunk; + + if (!filename || !size || !is_frag) + return NULL; + + FILE *in = fopen(filename, "rb"); + if (!in) + return NULL; + + if (!fread(&chunk, sizeof(chunk), 1, in)) { + printf("Failed to read MBS1 segment\n"); + return NULL; + } + + if (strncmp(chunk.name, "MBS1", 4)) { + printf("File is not MBS\n"); + return NULL; + } + + if (!fread(&chunk, sizeof(chunk), 1, in)) { + printf("Failed to read shader segment\n"); + return NULL; + } + + if (!strncmp(chunk.name, "CFRA", 4)) { + *is_frag = true; + } else if (!strncmp(chunk.name, "CVER", 4)) { + *is_frag = false; + } else { + printf("Unsupported shader type\n"); + return NULL; + } + + /* Skip version */ + fseek(in, 4, SEEK_CUR); + + /* Skip the other chunks and find the DBIN chunk. */ + do { + if (!fread(&chunk, sizeof(chunk), 1, in)) { + printf("Failed to read segment\n"); + return NULL; + } + if (!strncmp(chunk.name, "DBIN", 4)) + break; + fseek(in, chunk.size, SEEK_CUR); + } while (!feof(in)); + + if (feof(in)) { + printf("CBIN segment not found!\n"); + return NULL; + } + + *size = chunk.size; + + uint32_t *bin = ralloc_size(NULL, chunk.size); + if (!bin) { + printf("Failed to allocate shader binary\n"); + return NULL; + } + + if (!fread(bin, chunk.size, 1, in)) { + printf("Failed to read shader binary\n"); + ralloc_free(bin); + bin = NULL; + } + + return bin; +} + +int +main(int argc, char **argv) +{ + int n; + bool is_frag = true; + + if (argc < 2) { + print_usage(); + return 1; + } + + for (n = 1; n < argc; n++) { + if (!strcmp(argv[n], "--help")) { + print_usage(); + return 1; + } + + break; + } + + char *filename = NULL; + filename = argv[n]; + + uint32_t size = 0; + uint32_t *prog = extract_shader_binary(filename, &size, &is_frag); + if (!prog) { + printf("Failed to parse mbs!\n"); + return -1; + } + + if (is_frag) { + assert((size & 0x3) == 0); + size >>= 2; + uint32_t *bin = prog; + uint32_t offset = 0; + do { + ppir_codegen_ctrl *ctrl = (ppir_codegen_ctrl *)bin; + printf("@%6d: ", offset); + ppir_disassemble_instr(bin, offset); + bin += ctrl->count; + offset += ctrl->count; + size -= ctrl->count; + } while (size); + } else { + gpir_disassemble_program((gpir_codegen_instr *)prog, size / (sizeof(gpir_codegen_instr))); + } + + ralloc_free(prog); + + return 0; +} +