lima: add standalone disassembler with primitive MBS parser
authorVasily Khoruzhick <anarsoul@gmail.com>
Sat, 14 Sep 2019 07:15:28 +0000 (00:15 -0700)
committerVasily Khoruzhick <anarsoul@gmail.com>
Mon, 16 Sep 2019 16:29:55 +0000 (09:29 -0700)
It's useful for analyzing shader binaries produced by ARM mali offline
compiler which outputs files in MBS format. MBS is mali binary shader,
currently parser just extracts shader binary and ignores everything else.

Reviewed-and-tested-by: Connor Abbott<cwabbott0@gmail.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
src/gallium/drivers/lima/meson.build
src/gallium/drivers/lima/standalone/lima_disasm.c [new file with mode: 0644]

index 29c15bc2e1364dda1eeac6ea3163571d49d4890c..bf91c78856effff3c9420ad4b9e751ba78e313bf 100644 (file)
@@ -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 (file)
index 0000000..334d13e
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2019 Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * 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 <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#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;
+}
+