freedreno: slurp in decode tools

cffdump, crashdec, etc

At this point there is some duplication with other files in-tree (ie.
a2xx and a3xx+ disassembly), which will be cleaned up in a later commit.

Signed-off-by: Rob Clark <robdclark@chromium.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6070>
This commit is contained in:
Rob Clark 2020-07-23 17:32:36 -07:00 committed by Marge Bot
parent 7c0bd8429f
commit 1ea4ef0d3b
32 changed files with 13162 additions and 0 deletions

View File

@ -0,0 +1,191 @@
/*
* Copyright (c) 2012 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
/*
* Helper lib to track gpu buffers contents/address, and map between gpu and
* host address while decoding cmdstream/crashdumps
*/
#include <assert.h>
#include <stdlib.h>
#include "buffers.h"
struct buffer {
void *hostptr;
unsigned int len;
uint64_t gpuaddr;
/* for 'once' mode, for buffers containing cmdstream keep track per offset
* into buffer of which modes it has already been dumped;
*/
struct {
unsigned offset;
unsigned dumped_mask;
} offsets[64];
unsigned noffsets;
};
static struct buffer buffers[512];
static int nbuffers;
static int
buffer_contains_gpuaddr(struct buffer *buf, uint64_t gpuaddr, uint32_t len)
{
return (buf->gpuaddr <= gpuaddr) && (gpuaddr < (buf->gpuaddr + buf->len));
}
static int
buffer_contains_hostptr(struct buffer *buf, void *hostptr)
{
return (buf->hostptr <= hostptr) && (hostptr < (buf->hostptr + buf->len));
}
uint64_t
gpuaddr(void *hostptr)
{
int i;
for (i = 0; i < nbuffers; i++)
if (buffer_contains_hostptr(&buffers[i], hostptr))
return buffers[i].gpuaddr + (hostptr - buffers[i].hostptr);
return 0;
}
uint64_t
gpubaseaddr(uint64_t gpuaddr)
{
int i;
if (!gpuaddr)
return 0;
for (i = 0; i < nbuffers; i++)
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0))
return buffers[i].gpuaddr;
return 0;
}
void *
hostptr(uint64_t gpuaddr)
{
int i;
if (!gpuaddr)
return 0;
for (i = 0; i < nbuffers; i++)
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0))
return buffers[i].hostptr + (gpuaddr - buffers[i].gpuaddr);
return 0;
}
unsigned
hostlen(uint64_t gpuaddr)
{
int i;
if (!gpuaddr)
return 0;
for (i = 0; i < nbuffers; i++)
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0))
return buffers[i].len + buffers[i].gpuaddr - gpuaddr;
return 0;
}
bool
has_dumped(uint64_t gpuaddr, unsigned enable_mask)
{
if (!gpuaddr)
return false;
for (int i = 0; i < nbuffers; i++) {
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0)) {
struct buffer *b = &buffers[i];
assert(gpuaddr >= b->gpuaddr);
unsigned offset = gpuaddr - b->gpuaddr;
unsigned n = 0;
while (n < b->noffsets) {
if (offset == b->offsets[n].offset)
break;
n++;
}
/* if needed, allocate a new offset entry: */
if (n == b->noffsets) {
b->noffsets++;
assert(b->noffsets < ARRAY_SIZE(b->offsets));
b->offsets[n].dumped_mask = 0;
b->offsets[n].offset = offset;
}
if ((b->offsets[n].dumped_mask & enable_mask) == enable_mask)
return true;
b->offsets[n].dumped_mask |= enable_mask;
return false;
}
}
return false;
}
void
reset_buffers(void)
{
for (int i = 0; i < nbuffers; i++) {
free(buffers[i].hostptr);
buffers[i].hostptr = NULL;
buffers[i].len = 0;
buffers[i].noffsets = 0;
}
nbuffers = 0;
}
/**
* Record buffer contents, takes ownership of hostptr (freed in
* reset_buffers())
*/
void
add_buffer(uint64_t gpuaddr, unsigned int len, void *hostptr)
{
int i;
for (i = 0; i < nbuffers; i++) {
if (buffers[i].gpuaddr == gpuaddr)
break;
}
if (i == nbuffers) {
/* some traces, like test-perf, with some blob versions,
* seem to generate an unreasonable # of gpu buffers (a
* leak?), so just ignore them.
*/
if (nbuffers >= ARRAY_SIZE(buffers)) {
free(hostptr);
return;
}
nbuffers++;
}
buffers[i].hostptr = hostptr;
buffers[i].len = len;
buffers[i].gpuaddr = gpuaddr;
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2012 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
#ifndef __BUFFERS_H__
#define __BUFFERS_H__
#include <stdint.h>
#include <stdbool.h>
uint64_t gpuaddr(void *hostptr);
uint64_t gpubaseaddr(uint64_t gpuaddr);
void * hostptr(uint64_t gpuaddr);
unsigned hostlen(uint64_t gpuaddr);
bool has_dumped(uint64_t gpuaddr, unsigned enable_mask);
void reset_buffers(void);
void add_buffer(uint64_t gpuaddr, unsigned int len, void *hostptr);
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif
#endif /* __BUFFERS_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2012 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
#ifndef __CFFDEC_H__
#define __CFFDEC_H__
#include <stdbool.h>
enum query_mode {
/* default mode, dump all queried regs on each draw: */
QUERY_ALL = 0,
/* only dump if any of the queried regs were written
* since last draw:
*/
QUERY_WRITTEN,
/* only dump if any of the queried regs changed since
* last draw:
*/
QUERY_DELTA,
};
struct cffdec_options {
unsigned gpu_id;
int draw_filter;
int color;
int dump_shaders;
int summary;
int allregs;
int dump_textures;
int decode_markers;
char *script;
int query_compare; /* binning vs SYSMEM/GMEM compare mode */
int query_mode; /* enum query_mode */
char **querystrs;
int nquery;
/* In "once" mode, only decode a cmdstream buffer once (per draw
* mode, in the case of a6xx+ where a single cmdstream buffer can
* be used for both binning and draw pass), rather than each time
* encountered (ie. once per tile/bin in GMEM draw passes)
*/
int once;
/* for crashdec, where we know CP_IBx_REM_SIZE, we can use this
* to highlight the cmdstream not parsed yet, to make it easier
* to see how far along the CP is.
*/
struct {
uint64_t base;
uint32_t rem;
} ibs[4];
};
void printl(int lvl, const char *fmt, ...);
const char * pktname(unsigned opc);
uint32_t regbase(const char *name);
const char * regname(uint32_t regbase, int color);
bool reg_written(uint32_t regbase);
uint32_t reg_lastval(uint32_t regbase);
uint32_t reg_val(uint32_t regbase);
void reg_set(uint32_t regbase, uint32_t val);
void reset_regs(void);
void cffdec_init(const struct cffdec_options *options);
void dump_register_val(uint32_t regbase, uint32_t dword, int level);
void dump_commands(uint32_t *dwords, uint32_t sizedwords, int level);
/*
* Helpers for packet parsing:
*/
#define CP_TYPE0_PKT 0x00000000
#define CP_TYPE2_PKT 0x80000000
#define CP_TYPE3_PKT 0xc0000000
#define CP_TYPE4_PKT 0x40000000
#define CP_TYPE7_PKT 0x70000000
#define pkt_is_type0(pkt) (((pkt) & 0XC0000000) == CP_TYPE0_PKT)
#define type0_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
#define type0_pkt_offset(pkt) ((pkt) & 0x7FFF)
#define pkt_is_type2(pkt) ((pkt) == CP_TYPE2_PKT)
/*
* Check both for the type3 opcode and make sure that the reserved bits [1:7]
* and 15 are 0
*/
static inline uint pm4_calc_odd_parity_bit(uint val)
{
return (0x9669 >> (0xf & ((val) ^
((val) >> 4) ^ ((val) >> 8) ^ ((val) >> 12) ^
((val) >> 16) ^ ((val) >> 20) ^ ((val) >> 24) ^
((val) >> 28)))) & 1;
}
#define pkt_is_type3(pkt) \
((((pkt) & 0xC0000000) == CP_TYPE3_PKT) && \
(((pkt) & 0x80FE) == 0))
#define cp_type3_opcode(pkt) (((pkt) >> 8) & 0xFF)
#define type3_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
#define pkt_is_type4(pkt) \
((((pkt) & 0xF0000000) == CP_TYPE4_PKT) && \
((((pkt) >> 27) & 0x1) == \
pm4_calc_odd_parity_bit(type4_pkt_offset(pkt))) \
&& ((((pkt) >> 7) & 0x1) == \
pm4_calc_odd_parity_bit(type4_pkt_size(pkt))))
#define type4_pkt_offset(pkt) (((pkt) >> 8) & 0x7FFFF)
#define type4_pkt_size(pkt) ((pkt) & 0x7F)
#define pkt_is_type7(pkt) \
((((pkt) & 0xF0000000) == CP_TYPE7_PKT) && \
(((pkt) & 0x0F000000) == 0) && \
((((pkt) >> 23) & 0x1) == \
pm4_calc_odd_parity_bit(cp_type7_opcode(pkt))) \
&& ((((pkt) >> 15) & 0x1) == \
pm4_calc_odd_parity_bit(type7_pkt_size(pkt))))
#define cp_type7_opcode(pkt) (((pkt) >> 16) & 0x7F)
#define type7_pkt_size(pkt) ((pkt) & 0x3FFF)
#endif /* __CFFDEC_H__ */

View File

@ -0,0 +1,370 @@
/*
* Copyright (c) 2012 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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 <assert.h>
#include <ctype.h>
#include <err.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <errno.h>
#include "redump.h"
#include "disasm.h"
#include "script.h"
#include "io.h"
#include "rnnutil.h"
#include "pager.h"
#include "buffers.h"
#include "cffdec.h"
static struct cffdec_options options = {
.gpu_id = 220,
};
static bool needs_wfi = false;
static bool is_blob = false;
static int show_comp = false;
static int interactive;
static int vertices;
static int handle_file(const char *filename, int start, int end, int draw);
static void print_usage(const char *name)
{
fprintf(stderr, "Usage:\n\n"
"\t%s [OPTSIONS]... FILE...\n\n"
"Options:\n"
"\t-v, --verbose - more verbose disassembly\n"
"\t--dump-shaders - dump each shader to a raw file\n"
"\t--no-color - disable colorized output (default for non-console\n"
"\t output)\n"
"\t--color - enable colorized output (default for tty output)\n"
"\t--no-pager - disable pager (default for non-console output)\n"
"\t--pager - enable pager (default for tty output)\n"
"\t-s, --summary - don't show individual register writes, but just\n"
"\t register values on draws\n"
"\t-a, --allregs - show all registers (including ones not written\n"
"\t since previous draw) on each draw\n"
"\t-S, --start=N - start decoding from frame N\n"
"\t-E, --end=N - stop decoding after frame N\n"
"\t-F, --frame=N - decode only frame N\n"
"\t-D, --draw=N - decode only draw N\n"
"\t--textures - dump texture contents (if possible)\n"
"\t-L, --script=LUA - run specified lua script to analyze state\n"
"\t-q, --query=REG - query mode, dump only specified query registers on\n"
"\t each draw; multiple --query/-q args can be given to\n"
"\t dump multiple registers; register can be specified\n"
"\t either by name or numeric offset\n"
"\t--query-all - in query mode, show all queried regs on each draw\n"
"\t (default query mode)\n"
"\t--query-written - in query mode, show queried regs on draws if any of\n"
"\t them have been written since previous draw\n"
"\t--query-delta - in query mode, show queried regs on draws if any of\n"
"\t them have changed since previous draw\n"
"\t--query-compare - dump registers for BINNING vs GMEM/BYPASS per draw;\n"
"\t only applicable for regs set via SDS group (a6xx+),\n"
"\t implies --once, can be combined with --query-all,\n"
"\t --query-written, or --query-delta\n"
"\t--once - decode cmdstream only once (per draw mode); if same\n"
"\t cmdstream is executed for each tile, this will decode\n"
"\t it only for the first tile and skip the remainder,\n"
"\t which can be useful when looking at state that does\n"
"\t not change per tile\n"
"\t--not-once - decode cmdstream for each IB (default)\n"
"\t-h, --help - show this message\n"
, name);
exit(2);
}
static const struct option opts[] = {
/* Long opts that simply set a flag (no corresponding short alias: */
{ "dump-shaders", no_argument, &options.dump_shaders, 1 },
{ "no-color", no_argument, &options.color, 0 },
{ "color", no_argument, &options.color, 1 },
{ "no-pager", no_argument, &interactive, 0 },
{ "pager", no_argument, &interactive, 1 },
{ "textures", no_argument, &options.dump_textures, 1 },
{ "show-compositor", no_argument, &show_comp, 1 },
{ "query-all", no_argument, &options.query_mode, QUERY_ALL },
{ "query-written", no_argument, &options.query_mode, QUERY_WRITTEN },
{ "query-delta", no_argument, &options.query_mode, QUERY_DELTA },
{ "query-compare", no_argument, &options.query_compare, 1 },
{ "once", no_argument, &options.once, 1 },
{ "not-once", no_argument, &options.once, 0 },
/* Long opts with short alias: */
{ "verbose", no_argument, 0, 'v' },
{ "summary", no_argument, 0, 's' },
{ "allregs", no_argument, 0, 'a' },
{ "start", required_argument, 0, 'S' },
{ "end", required_argument, 0, 'E' },
{ "frame", required_argument, 0, 'F' },
{ "draw", required_argument, 0, 'D' },
{ "script", required_argument, 0, 'L' },
{ "query", required_argument, 0, 'q' },
{ "help", no_argument, 0, 'h' },
};
int main(int argc, char **argv)
{
int ret = -1;
int start = 0, end = 0x7ffffff, draw = -1;
int c;
interactive = isatty(STDOUT_FILENO);
options.color = interactive;
while ((c = getopt_long(argc, argv, "vsaS:E:F:D:L:q:h", opts, NULL)) != -1) {
switch (c) {
case 0:
/* option that set a flag, nothing to do */
break;
case 'v':
disasm_set_debug(PRINT_RAW | EXPAND_REPEAT | PRINT_VERBOSE);
break;
case 's':
options.summary = true;
break;
case 'a':
options.allregs = true;
break;
case 'S':
start = atoi(optarg);
break;
case 'E':
end = atoi(optarg);
break;
case 'F':
start = end = atoi(optarg);
break;
case 'D':
draw = atoi(optarg);
break;
case 'L':
options.script = optarg;
if (script_load(options.script)) {
errx(-1, "error loading %s\n", options.script);
}
break;
case 'q':
options.querystrs = realloc(options.querystrs,
(options.nquery + 1) * sizeof(*options.querystrs));
options.querystrs[options.nquery] = optarg;
options.nquery++;
interactive = 0;
break;
case 'h':
default:
print_usage(argv[0]);
}
}
if (interactive) {
pager_open();
}
while (optind < argc) {
ret = handle_file(argv[optind], start, end, draw);
if (ret) {
fprintf(stderr, "error reading: %s\n", argv[optind]);
fprintf(stderr, "continuing..\n");
}
optind++;
}
if (ret)
print_usage(argv[0]);
if ((options.query_mode || options.query_compare) && !options.nquery) {
fprintf(stderr, "query options only valid in query mode!\n");
print_usage(argv[0]);
}
script_finish();
if (interactive) {
pager_close();
}
return ret;
}
static void parse_addr(uint32_t *buf, int sz, unsigned int *len, uint64_t *gpuaddr)
{
*gpuaddr = buf[0];
*len = buf[1];
if (sz > 8)
*gpuaddr |= ((uint64_t)(buf[2])) << 32;
}
static int handle_file(const char *filename, int start, int end, int draw)
{
enum rd_sect_type type = RD_NONE;
void *buf = NULL;
struct io *io;
int submit = 0, got_gpu_id = 0;
int sz, ret = 0;
bool needs_reset = false;
bool skip = false;
options.draw_filter = draw;
cffdec_init(&options);
printf("Reading %s...\n", filename);
script_start_cmdstream(filename);
if (!strcmp(filename, "-"))
io = io_openfd(0);
else
io = io_open(filename);
if (!io) {
fprintf(stderr, "could not open: %s\n", filename);
return -1;
}
struct {
unsigned int len;
uint64_t gpuaddr;
} gpuaddr = {0};
while (true) {
uint32_t arr[2];
ret = io_readn(io, arr, 8);
if (ret <= 0)
goto end;
while ((arr[0] == 0xffffffff) && (arr[1] == 0xffffffff)) {
ret = io_readn(io, arr, 8);
if (ret <= 0)
goto end;
}
type = arr[0];
sz = arr[1];
if (sz < 0) {
ret = -1;
goto end;
}
free(buf);
needs_wfi = false;
buf = malloc(sz + 1);
((char *)buf)[sz] = '\0';
ret = io_readn(io, buf, sz);
if (ret < 0)
goto end;
switch(type) {
case RD_TEST:
printl(1, "test: %s\n", (char *)buf);
break;
case RD_CMD:
is_blob = true;
printl(2, "cmd: %s\n", (char *)buf);
skip = false;
if (!show_comp) {
skip |= (strstr(buf, "fdperf") == buf);
skip |= (strstr(buf, "chrome") == buf);
skip |= (strstr(buf, "surfaceflinger") == buf);
skip |= ((char *)buf)[0] == 'X';
}
break;
case RD_VERT_SHADER:
printl(2, "vertex shader:\n%s\n", (char *)buf);
break;
case RD_FRAG_SHADER:
printl(2, "fragment shader:\n%s\n", (char *)buf);
break;
case RD_GPUADDR:
if (needs_reset) {
reset_buffers();
needs_reset = false;
}
parse_addr(buf, sz, &gpuaddr.len, &gpuaddr.gpuaddr);
break;
case RD_BUFFER_CONTENTS:
add_buffer(gpuaddr.gpuaddr, gpuaddr.len, buf);
buf = NULL;
break;
case RD_CMDSTREAM_ADDR:
if ((start <= submit) && (submit <= end)) {
unsigned int sizedwords;
uint64_t gpuaddr;
parse_addr(buf, sz, &sizedwords, &gpuaddr);
printl(2, "############################################################\n");
printl(2, "cmdstream: %d dwords\n", sizedwords);
if (!skip) {
script_start_submit();
dump_commands(hostptr(gpuaddr), sizedwords, 0);
script_end_submit();
}
printl(2, "############################################################\n");
printl(2, "vertices: %d\n", vertices);
}
needs_reset = true;
submit++;
break;
case RD_GPU_ID:
if (!got_gpu_id) {
options.gpu_id = *((unsigned int *)buf);
printl(2, "gpu_id: %d\n", options.gpu_id);
cffdec_init(&options);
got_gpu_id = 1;
}
break;
default:
break;
}
}
end:
script_end_cmdstream();
io_close(io);
fflush(stdout);
if (ret < 0) {
printf("corrupt file\n");
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,623 @@
/*
* Copyright (c) 2012 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "disasm.h"
#include "instr-a2xx.h"
#include "rnnutil.h"
static const char *levels[] = {
"",
"\t",
"\t\t",
"\t\t\t",
"\t\t\t\t",
"\t\t\t\t\t",
"\t\t\t\t\t\t",
"\t\t\t\t\t\t\t",
"\t\t\t\t\t\t\t\t",
"\t\t\t\t\t\t\t\t\t",
"x",
"x",
"x",
"x",
"x",
"x",
};
enum debug_t debug;
static struct rnn *rnn;
/*
* ALU instructions:
*/
static const char chan_names[] = {
'x', 'y', 'z', 'w',
/* these only apply to FETCH dst's: */
'0', '1', '?', '_',
};
static void print_srcreg(uint32_t num, uint32_t type,
uint32_t swiz, uint32_t negate, uint32_t abs)
{
if (negate)
printf("-");
if (abs)
printf("|");
printf("%c%u", type ? 'R' : 'C', num);
if (swiz) {
int i;
printf(".");
for (i = 0; i < 4; i++) {
printf("%c", chan_names[(swiz + i) & 0x3]);
swiz >>= 2;
}
}
if (abs)
printf("|");
}
static void print_dstreg(uint32_t num, uint32_t mask, uint32_t dst_exp)
{
printf("%s%u", dst_exp ? "export" : "R", num);
if (mask != 0xf) {
int i;
printf(".");
for (i = 0; i < 4; i++) {
printf("%c", (mask & 0x1) ? chan_names[i] : '_');
mask >>= 1;
}
}
}
static void print_export_comment(uint32_t num, enum shader_t type)
{
const char *name = NULL;
switch (type) {
case SHADER_VERTEX:
switch (num) {
case 62: name = "gl_Position"; break;
case 63: name = "gl_PointSize"; break;
}
break;
case SHADER_FRAGMENT:
switch (num) {
case 0: name = "gl_FragColor"; break;
}
break;
default:
break;
}
/* if we had a symbol table here, we could look
* up the name of the varying..
*/
if (name) {
printf("\t; %s", name);
}
}
struct {
uint32_t num_srcs;
const char *name;
} vector_instructions[0x20] = {
#define INSTR(opc, num_srcs) [opc] = { num_srcs, #opc }
INSTR(ADDv, 2),
INSTR(MULv, 2),
INSTR(MAXv, 2),
INSTR(MINv, 2),
INSTR(SETEv, 2),
INSTR(SETGTv, 2),
INSTR(SETGTEv, 2),
INSTR(SETNEv, 2),
INSTR(FRACv, 1),
INSTR(TRUNCv, 1),
INSTR(FLOORv, 1),
INSTR(MULADDv, 3),
INSTR(CNDEv, 3),
INSTR(CNDGTEv, 3),
INSTR(CNDGTv, 3),
INSTR(DOT4v, 2),
INSTR(DOT3v, 2),
INSTR(DOT2ADDv, 3), // ???
INSTR(CUBEv, 2),
INSTR(MAX4v, 1),
INSTR(PRED_SETE_PUSHv, 2),
INSTR(PRED_SETNE_PUSHv, 2),
INSTR(PRED_SETGT_PUSHv, 2),
INSTR(PRED_SETGTE_PUSHv, 2),
INSTR(KILLEv, 2),
INSTR(KILLGTv, 2),
INSTR(KILLGTEv, 2),
INSTR(KILLNEv, 2),
INSTR(DSTv, 2),
INSTR(MOVAv, 1),
}, scalar_instructions[0x40] = {
INSTR(ADDs, 1),
INSTR(ADD_PREVs, 1),
INSTR(MULs, 1),
INSTR(MUL_PREVs, 1),
INSTR(MUL_PREV2s, 1),
INSTR(MAXs, 1),
INSTR(MINs, 1),
INSTR(SETEs, 1),
INSTR(SETGTs, 1),
INSTR(SETGTEs, 1),
INSTR(SETNEs, 1),
INSTR(FRACs, 1),
INSTR(TRUNCs, 1),
INSTR(FLOORs, 1),
INSTR(EXP_IEEE, 1),
INSTR(LOG_CLAMP, 1),
INSTR(LOG_IEEE, 1),
INSTR(RECIP_CLAMP, 1),
INSTR(RECIP_FF, 1),
INSTR(RECIP_IEEE, 1),
INSTR(RECIPSQ_CLAMP, 1),
INSTR(RECIPSQ_FF, 1),
INSTR(RECIPSQ_IEEE, 1),
INSTR(MOVAs, 1),
INSTR(MOVA_FLOORs, 1),
INSTR(SUBs, 1),
INSTR(SUB_PREVs, 1),
INSTR(PRED_SETEs, 1),
INSTR(PRED_SETNEs, 1),
INSTR(PRED_SETGTs, 1),
INSTR(PRED_SETGTEs, 1),
INSTR(PRED_SET_INVs, 1),
INSTR(PRED_SET_POPs, 1),
INSTR(PRED_SET_CLRs, 1),
INSTR(PRED_SET_RESTOREs, 1),
INSTR(KILLEs, 1),
INSTR(KILLGTs, 1),
INSTR(KILLGTEs, 1),
INSTR(KILLNEs, 1),
INSTR(KILLONEs, 1),
INSTR(SQRT_IEEE, 1),
INSTR(MUL_CONST_0, 1),
INSTR(MUL_CONST_1, 1),
INSTR(ADD_CONST_0, 1),
INSTR(ADD_CONST_1, 1),
INSTR(SUB_CONST_0, 1),
INSTR(SUB_CONST_1, 1),
INSTR(SIN, 1),
INSTR(COS, 1),
INSTR(RETAIN_PREV, 1),
#undef INSTR
};
static int disasm_alu(uint32_t *dwords, uint32_t alu_off,
int level, int sync, enum shader_t type)
{
instr_alu_t *alu = (instr_alu_t *)dwords;
printf("%s", levels[level]);
if (debug & PRINT_RAW) {
printf("%02x: %08x %08x %08x\t", alu_off,
dwords[0], dwords[1], dwords[2]);
}
printf(" %sALU:\t", sync ? "(S)" : " ");
printf("%s", vector_instructions[alu->vector_opc].name);
if (alu->pred_select & 0x2) {
/* seems to work similar to conditional execution in ARM instruction
* set, so let's use a similar syntax for now:
*/
printf((alu->pred_select & 0x1) ? "EQ" : "NE");
}
printf("\t");
print_dstreg(alu->vector_dest, alu->vector_write_mask, alu->export_data);
printf(" = ");
if (vector_instructions[alu->vector_opc].num_srcs == 3) {
print_srcreg(alu->src3_reg, alu->src3_sel, alu->src3_swiz,
alu->src3_reg_negate, alu->src3_reg_abs);
printf(", ");
}
print_srcreg(alu->src1_reg, alu->src1_sel, alu->src1_swiz,
alu->src1_reg_negate, alu->src1_reg_abs);
if (vector_instructions[alu->vector_opc].num_srcs > 1) {
printf(", ");
print_srcreg(alu->src2_reg, alu->src2_sel, alu->src2_swiz,
alu->src2_reg_negate, alu->src2_reg_abs);
}
if (alu->vector_clamp)
printf(" CLAMP");
if (alu->export_data)
print_export_comment(alu->vector_dest, type);
printf("\n");
if (alu->scalar_write_mask || !alu->vector_write_mask) {
/* 2nd optional scalar op: */
printf("%s", levels[level]);
if (debug & PRINT_RAW)
printf(" \t");
if (scalar_instructions[alu->scalar_opc].name) {
printf("\t \t%s\t", scalar_instructions[alu->scalar_opc].name);
} else {
printf("\t \tOP(%u)\t", alu->scalar_opc);
}
print_dstreg(alu->scalar_dest, alu->scalar_write_mask, alu->export_data);
printf(" = ");
print_srcreg(alu->src3_reg, alu->src3_sel, alu->src3_swiz,
alu->src3_reg_negate, alu->src3_reg_abs);
// TODO ADD/MUL must have another src?!?
if (alu->scalar_clamp)
printf(" CLAMP");
if (alu->export_data)
print_export_comment(alu->scalar_dest, type);
printf("\n");
}
return 0;
}
/*
* FETCH instructions:
*/
static void print_fetch_dst(uint32_t dst_reg, uint32_t dst_swiz)
{
int i;
printf("\tR%u.", dst_reg);
for (i = 0; i < 4; i++) {
printf("%c", chan_names[dst_swiz & 0x7]);
dst_swiz >>= 3;
}
}
static void print_fetch_vtx(instr_fetch_t *fetch)
{
instr_fetch_vtx_t *vtx = &fetch->vtx;
if (vtx->pred_select) {
/* seems to work similar to conditional execution in ARM instruction
* set, so let's use a similar syntax for now:
*/
printf(vtx->pred_condition ? "EQ" : "NE");
}
print_fetch_dst(vtx->dst_reg, vtx->dst_swiz);
printf(" = R%u.", vtx->src_reg);
printf("%c", chan_names[vtx->src_swiz & 0x3]);
const char *fmt = rnn_enumname(rnn, "a2xx_sq_surfaceformat", vtx->format);
if (fmt) {
printf(" %s", fmt);
} else {
printf(" TYPE(0x%x)", vtx->format);
}
printf(" %s", vtx->format_comp_all ? "SIGNED" : "UNSIGNED");
if (!vtx->num_format_all)
printf(" NORMALIZED");
printf(" STRIDE(%u)", vtx->stride);
if (vtx->offset)
printf(" OFFSET(%u)", vtx->offset);
printf(" CONST(%u, %u)", vtx->const_index, vtx->const_index_sel);
if (0) {
// XXX
printf(" src_reg_am=%u", vtx->src_reg_am);
printf(" dst_reg_am=%u", vtx->dst_reg_am);
printf(" num_format_all=%u", vtx->num_format_all);
printf(" signed_rf_mode_all=%u", vtx->signed_rf_mode_all);
printf(" exp_adjust_all=%u", vtx->exp_adjust_all);
}
}
static void print_fetch_tex(instr_fetch_t *fetch)
{
static const char *filter[] = {
[TEX_FILTER_POINT] = "POINT",
[TEX_FILTER_LINEAR] = "LINEAR",
[TEX_FILTER_BASEMAP] = "BASEMAP",
};
static const char *aniso_filter[] = {
[ANISO_FILTER_DISABLED] = "DISABLED",
[ANISO_FILTER_MAX_1_1] = "MAX_1_1",
[ANISO_FILTER_MAX_2_1] = "MAX_2_1",
[ANISO_FILTER_MAX_4_1] = "MAX_4_1",
[ANISO_FILTER_MAX_8_1] = "MAX_8_1",
[ANISO_FILTER_MAX_16_1] = "MAX_16_1",
};
static const char *arbitrary_filter[] = {
[ARBITRARY_FILTER_2X4_SYM] = "2x4_SYM",
[ARBITRARY_FILTER_2X4_ASYM] = "2x4_ASYM",
[ARBITRARY_FILTER_4X2_SYM] = "4x2_SYM",
[ARBITRARY_FILTER_4X2_ASYM] = "4x2_ASYM",
[ARBITRARY_FILTER_4X4_SYM] = "4x4_SYM",
[ARBITRARY_FILTER_4X4_ASYM] = "4x4_ASYM",
};
static const char *sample_loc[] = {
[SAMPLE_CENTROID] = "CENTROID",
[SAMPLE_CENTER] = "CENTER",
};
instr_fetch_tex_t *tex = &fetch->tex;
uint32_t src_swiz = tex->src_swiz;
int i;
if (tex->pred_select) {
/* seems to work similar to conditional execution in ARM instruction
* set, so let's use a similar syntax for now:
*/
printf(tex->pred_condition ? "EQ" : "NE");
}
print_fetch_dst(tex->dst_reg, tex->dst_swiz);
printf(" = R%u.", tex->src_reg);
for (i = 0; i < 3; i++) {
printf("%c", chan_names[src_swiz & 0x3]);
src_swiz >>= 2;
}
printf(" CONST(%u)", tex->const_idx);
if (tex->fetch_valid_only)
printf(" VALID_ONLY");
if (tex->tx_coord_denorm)
printf(" DENORM");
if (tex->mag_filter != TEX_FILTER_USE_FETCH_CONST)
printf(" MAG(%s)", filter[tex->mag_filter]);
if (tex->min_filter != TEX_FILTER_USE_FETCH_CONST)
printf(" MIN(%s)", filter[tex->min_filter]);
if (tex->mip_filter != TEX_FILTER_USE_FETCH_CONST)
printf(" MIP(%s)", filter[tex->mip_filter]);
if (tex->aniso_filter != ANISO_FILTER_USE_FETCH_CONST)
printf(" ANISO(%s)", aniso_filter[tex->aniso_filter]);
if (tex->arbitrary_filter != ARBITRARY_FILTER_USE_FETCH_CONST)
printf(" ARBITRARY(%s)", arbitrary_filter[tex->arbitrary_filter]);
if (tex->vol_mag_filter != TEX_FILTER_USE_FETCH_CONST)
printf(" VOL_MAG(%s)", filter[tex->vol_mag_filter]);
if (tex->vol_min_filter != TEX_FILTER_USE_FETCH_CONST)
printf(" VOL_MIN(%s)", filter[tex->vol_min_filter]);
if (!tex->use_comp_lod) {
printf(" LOD(%u)", tex->use_comp_lod);
printf(" LOD_BIAS(%u)", tex->lod_bias);
}
if (tex->use_reg_lod) {
printf(" REG_LOD(%u)", tex->use_reg_lod);
}
if (tex->use_reg_gradients)
printf(" USE_REG_GRADIENTS");
printf(" LOCATION(%s)", sample_loc[tex->sample_location]);
if (tex->offset_x || tex->offset_y || tex->offset_z)
printf(" OFFSET(%u,%u,%u)", tex->offset_x, tex->offset_y, tex->offset_z);
}
struct {
const char *name;
void (*fxn)(instr_fetch_t *cf);
} fetch_instructions[] = {
#define INSTR(opc, name, fxn) [opc] = { name, fxn }
INSTR(VTX_FETCH, "VERTEX", print_fetch_vtx),
INSTR(TEX_FETCH, "SAMPLE", print_fetch_tex),
INSTR(TEX_GET_BORDER_COLOR_FRAC, "?", print_fetch_tex),
INSTR(TEX_GET_COMP_TEX_LOD, "?", print_fetch_tex),
INSTR(TEX_GET_GRADIENTS, "?", print_fetch_tex),
INSTR(TEX_GET_WEIGHTS, "?", print_fetch_tex),
INSTR(TEX_SET_TEX_LOD, "SET_TEX_LOD", print_fetch_tex),
INSTR(TEX_SET_GRADIENTS_H, "?", print_fetch_tex),
INSTR(TEX_SET_GRADIENTS_V, "?", print_fetch_tex),
INSTR(TEX_RESERVED_4, "?", print_fetch_tex),
#undef INSTR
};
static int disasm_fetch(uint32_t *dwords, uint32_t alu_off, int level, int sync)
{
instr_fetch_t *fetch = (instr_fetch_t *)dwords;
printf("%s", levels[level]);
if (debug & PRINT_RAW) {
printf("%02x: %08x %08x %08x\t", alu_off,
dwords[0], dwords[1], dwords[2]);
}
printf(" %sFETCH:\t", sync ? "(S)" : " ");
printf("%s", fetch_instructions[fetch->opc].name);
fetch_instructions[fetch->opc].fxn(fetch);
printf("\n");
return 0;
}
/*
* CF instructions:
*/
static int cf_exec(instr_cf_t *cf)
{
return (cf->opc == EXEC) ||
(cf->opc == EXEC_END) ||
(cf->opc == COND_EXEC) ||
(cf->opc == COND_EXEC_END) ||
(cf->opc == COND_PRED_EXEC) ||
(cf->opc == COND_PRED_EXEC_END) ||
(cf->opc == COND_EXEC_PRED_CLEAN) ||
(cf->opc == COND_EXEC_PRED_CLEAN_END);
}
static int cf_cond_exec(instr_cf_t *cf)
{
return (cf->opc == COND_EXEC) ||
(cf->opc == COND_EXEC_END) ||
(cf->opc == COND_PRED_EXEC) ||
(cf->opc == COND_PRED_EXEC_END) ||
(cf->opc == COND_EXEC_PRED_CLEAN) ||
(cf->opc == COND_EXEC_PRED_CLEAN_END);
}
static void print_cf_nop(instr_cf_t *cf)
{
}
static void print_cf_exec(instr_cf_t *cf)
{
printf(" ADDR(0x%x) CNT(0x%x)", cf->exec.address, cf->exec.count);
if (cf->exec.yeild)
printf(" YIELD");
if (cf->exec.vc)
printf(" VC(0x%x)", cf->exec.vc);
if (cf->exec.bool_addr)
printf(" BOOL_ADDR(0x%x)", cf->exec.bool_addr);
if (cf->exec.address_mode == ABSOLUTE_ADDR)
printf(" ABSOLUTE_ADDR");
if (cf_cond_exec(cf))
printf(" COND(%d)", cf->exec.condition);
}
static void print_cf_loop(instr_cf_t *cf)
{
printf(" ADDR(0x%x) LOOP_ID(%d)", cf->loop.address, cf->loop.loop_id);
if (cf->loop.address_mode == ABSOLUTE_ADDR)
printf(" ABSOLUTE_ADDR");
}
static void print_cf_jmp_call(instr_cf_t *cf)
{
printf(" ADDR(0x%x) DIR(%d)", cf->jmp_call.address, cf->jmp_call.direction);
if (cf->jmp_call.force_call)
printf(" FORCE_CALL");
if (cf->jmp_call.predicated_jmp)
printf(" COND(%d)", cf->jmp_call.condition);
if (cf->jmp_call.bool_addr)
printf(" BOOL_ADDR(0x%x)", cf->jmp_call.bool_addr);
if (cf->jmp_call.address_mode == ABSOLUTE_ADDR)
printf(" ABSOLUTE_ADDR");
}
static void print_cf_alloc(instr_cf_t *cf)
{
static const char *bufname[] = {
[SQ_NO_ALLOC] = "NO ALLOC",
[SQ_POSITION] = "POSITION",
[SQ_PARAMETER_PIXEL] = "PARAM/PIXEL",
[SQ_MEMORY] = "MEMORY",
};
printf(" %s SIZE(0x%x)", bufname[cf->alloc.buffer_select], cf->alloc.size);
if (cf->alloc.no_serial)
printf(" NO_SERIAL");
if (cf->alloc.alloc_mode) // ???
printf(" ALLOC_MODE");
}
struct {
const char *name;
void (*fxn)(instr_cf_t *cf);
} cf_instructions[] = {
#define INSTR(opc, fxn) [opc] = { #opc, fxn }
INSTR(NOP, print_cf_nop),
INSTR(EXEC, print_cf_exec),
INSTR(EXEC_END, print_cf_exec),
INSTR(COND_EXEC, print_cf_exec),
INSTR(COND_EXEC_END, print_cf_exec),
INSTR(COND_PRED_EXEC, print_cf_exec),
INSTR(COND_PRED_EXEC_END, print_cf_exec),
INSTR(LOOP_START, print_cf_loop),
INSTR(LOOP_END, print_cf_loop),
INSTR(COND_CALL, print_cf_jmp_call),
INSTR(RETURN, print_cf_jmp_call),
INSTR(COND_JMP, print_cf_jmp_call),
INSTR(ALLOC, print_cf_alloc),
INSTR(COND_EXEC_PRED_CLEAN, print_cf_exec),
INSTR(COND_EXEC_PRED_CLEAN_END, print_cf_exec),
INSTR(MARK_VS_FETCH_DONE, print_cf_nop), // ??
#undef INSTR
};
static void print_cf(instr_cf_t *cf, int level)
{
printf("%s", levels[level]);
if (debug & PRINT_RAW) {
uint16_t *words = (uint16_t *)cf;
printf(" %04x %04x %04x \t",
words[0], words[1], words[2]);
}
printf("%s", cf_instructions[cf->opc].name);
cf_instructions[cf->opc].fxn(cf);
printf("\n");
}
/*
* The adreno shader microcode consists of two parts:
* 1) A CF (control-flow) program, at the header of the compiled shader,
* which refers to ALU/FETCH instructions that follow it by address.
* 2) ALU and FETCH instructions
*/
int disasm_a2xx(uint32_t *dwords, int sizedwords, int level, enum shader_t type)
{
instr_cf_t *cfs = (instr_cf_t *)dwords;
int idx, max_idx;
if (!rnn) {
rnn = rnn_new(1);
rnn_load(rnn, "a2xx");
}
for (idx = 0; ; idx++) {
instr_cf_t *cf = &cfs[idx];
if (cf_exec(cf)) {
max_idx = 2 * cf->exec.address;
break;
}
}
for (idx = 0; idx < max_idx; idx++) {
instr_cf_t *cf = &cfs[idx];
print_cf(cf, level);
if (cf_exec(cf)) {
uint32_t sequence = cf->exec.serialize;
uint32_t i;
for (i = 0; i < cf->exec.count; i++) {
uint32_t alu_off = (cf->exec.address + i);
if (sequence & 0x1) {
disasm_fetch(dwords + alu_off * 3, alu_off, level, sequence & 0x2);
} else {
disasm_alu(dwords + alu_off * 3, alu_off, level, sequence & 0x2, type);
}
sequence >>= 2;
}
}
}
return 0;
}
void disasm_set_debug(enum debug_t d)
{
debug = d;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
/*
* Copyright © 2012 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
#ifndef DISASM_H_
#define DISASM_H_
#include <stdio.h>
enum shader_t {
SHADER_VERTEX,
SHADER_TCS,
SHADER_TES,
SHADER_GEOM,
SHADER_FRAGMENT,
SHADER_COMPUTE,
};
/* bitmask of debug flags */
enum debug_t {
PRINT_RAW = 0x1, /* dump raw hexdump */
PRINT_VERBOSE = 0x2,
EXPAND_REPEAT = 0x4,
};
struct shader_stats {
/* instructions counts rpnN, and instlen does not */
int instructions, instlen;
int nops;
int ss, sy;
int constlen;
};
int disasm_a2xx(uint32_t *dwords, int sizedwords, int level, enum shader_t type);
int disasm_a3xx(uint32_t *dwords, int sizedwords, int level, FILE *out, unsigned gpu_id);
int disasm_a3xx_stat(uint32_t *dwords, int sizedwords, int level, FILE *out,
unsigned gpu_id, struct shader_stats *stats);
void disasm_set_debug(enum debug_t debug);
#endif /* DISASM_H_ */

View File

@ -0,0 +1,385 @@
/*
* Copyright (c) 2012 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
#ifndef INSTR_A2XX_H_
#define INSTR_A2XX_H_
#define PACKED __attribute__((__packed__))
/*
* ALU instructions:
*/
typedef enum {
ADDs = 0,
ADD_PREVs = 1,
MULs = 2,
MUL_PREVs = 3,
MUL_PREV2s = 4,
MAXs = 5,
MINs = 6,
SETEs = 7,
SETGTs = 8,
SETGTEs = 9,
SETNEs = 10,
FRACs = 11,
TRUNCs = 12,
FLOORs = 13,
EXP_IEEE = 14,
LOG_CLAMP = 15,
LOG_IEEE = 16,
RECIP_CLAMP = 17,
RECIP_FF = 18,
RECIP_IEEE = 19,
RECIPSQ_CLAMP = 20,
RECIPSQ_FF = 21,
RECIPSQ_IEEE = 22,
MOVAs = 23,
MOVA_FLOORs = 24,
SUBs = 25,
SUB_PREVs = 26,
PRED_SETEs = 27,
PRED_SETNEs = 28,
PRED_SETGTs = 29,
PRED_SETGTEs = 30,
PRED_SET_INVs = 31,
PRED_SET_POPs = 32,
PRED_SET_CLRs = 33,
PRED_SET_RESTOREs = 34,
KILLEs = 35,
KILLGTs = 36,
KILLGTEs = 37,
KILLNEs = 38,
KILLONEs = 39,
SQRT_IEEE = 40,
MUL_CONST_0 = 42,
MUL_CONST_1 = 43,
ADD_CONST_0 = 44,
ADD_CONST_1 = 45,
SUB_CONST_0 = 46,
SUB_CONST_1 = 47,
SIN = 48,
COS = 49,
RETAIN_PREV = 50,
} instr_scalar_opc_t;
typedef enum {
ADDv = 0,
MULv = 1,
MAXv = 2,
MINv = 3,
SETEv = 4,
SETGTv = 5,
SETGTEv = 6,
SETNEv = 7,
FRACv = 8,
TRUNCv = 9,
FLOORv = 10,
MULADDv = 11,
CNDEv = 12,
CNDGTEv = 13,
CNDGTv = 14,
DOT4v = 15,
DOT3v = 16,
DOT2ADDv = 17,
CUBEv = 18,
MAX4v = 19,
PRED_SETE_PUSHv = 20,
PRED_SETNE_PUSHv = 21,
PRED_SETGT_PUSHv = 22,
PRED_SETGTE_PUSHv = 23,
KILLEv = 24,
KILLGTv = 25,
KILLGTEv = 26,
KILLNEv = 27,
DSTv = 28,
MOVAv = 29,
} instr_vector_opc_t;
typedef struct PACKED {
/* dword0: */
uint8_t vector_dest : 6;
uint8_t vector_dest_rel : 1;
uint8_t low_precision_16b_fp : 1;
uint8_t scalar_dest : 6;
uint8_t scalar_dest_rel : 1;
uint8_t export_data : 1;
uint8_t vector_write_mask : 4;
uint8_t scalar_write_mask : 4;
uint8_t vector_clamp : 1;
uint8_t scalar_clamp : 1;
instr_scalar_opc_t scalar_opc : 6;
/* dword1: */
uint8_t src3_swiz : 8;
uint8_t src2_swiz : 8;
uint8_t src1_swiz : 8;
uint8_t src3_reg_negate : 1;
uint8_t src2_reg_negate : 1;
uint8_t src1_reg_negate : 1;
uint8_t pred_select : 2;
uint8_t relative_addr : 1;
uint8_t const_1_rel_abs : 1;
uint8_t const_0_rel_abs : 1;
/* dword2: */
uint8_t src3_reg : 6;
uint8_t src3_reg_select : 1;
uint8_t src3_reg_abs : 1;
uint8_t src2_reg : 6;
uint8_t src2_reg_select : 1;
uint8_t src2_reg_abs : 1;
uint8_t src1_reg : 6;
uint8_t src1_reg_select : 1;
uint8_t src1_reg_abs : 1;
instr_vector_opc_t vector_opc : 5;
uint8_t src3_sel : 1;
uint8_t src2_sel : 1;
uint8_t src1_sel : 1;
} instr_alu_t;
/*
* CF instructions:
*/
typedef enum {
NOP = 0,
EXEC = 1,
EXEC_END = 2,
COND_EXEC = 3,
COND_EXEC_END = 4,
COND_PRED_EXEC = 5,
COND_PRED_EXEC_END = 6,
LOOP_START = 7,
LOOP_END = 8,
COND_CALL = 9,
RETURN = 10,
COND_JMP = 11,
ALLOC = 12,
COND_EXEC_PRED_CLEAN = 13,
COND_EXEC_PRED_CLEAN_END = 14,
MARK_VS_FETCH_DONE = 15,
} instr_cf_opc_t;
typedef enum {
RELATIVE_ADDR = 0,
ABSOLUTE_ADDR = 1,
} instr_addr_mode_t;
typedef enum {
SQ_NO_ALLOC = 0,
SQ_POSITION = 1,
SQ_PARAMETER_PIXEL = 2,
SQ_MEMORY = 3,
} instr_alloc_type_t;
typedef struct PACKED {
uint16_t address : 9;
uint8_t reserved0 : 3;
uint8_t count : 3;
uint8_t yeild : 1;
uint16_t serialize : 12;
uint8_t vc : 6; /* vertex cache? */
uint8_t bool_addr : 8;
uint8_t condition : 1;
instr_addr_mode_t address_mode : 1;
instr_cf_opc_t opc : 4;
} instr_cf_exec_t;
typedef struct PACKED {
uint16_t address : 10;
uint8_t reserved0 : 6;
uint8_t loop_id : 5;
uint32_t reserved1 : 22;
instr_addr_mode_t address_mode : 1;
instr_cf_opc_t opc : 4;
} instr_cf_loop_t;
typedef struct PACKED {
uint16_t address : 10;
uint8_t reserved0 : 3;
uint8_t force_call : 1;
uint8_t predicated_jmp : 1;
uint32_t reserved1 : 18;
uint8_t direction : 1;
uint8_t bool_addr : 8;
uint8_t condition : 1;
instr_addr_mode_t address_mode : 1;
instr_cf_opc_t opc : 4;
} instr_cf_jmp_call_t;
typedef struct PACKED {
uint8_t size : 4;
uint64_t reserved0 : 36;
uint8_t no_serial : 1;
instr_alloc_type_t buffer_select : 2;
uint8_t alloc_mode : 1;
instr_cf_opc_t opc : 4;
} instr_cf_alloc_t;
typedef union PACKED {
instr_cf_exec_t exec;
instr_cf_loop_t loop;
instr_cf_jmp_call_t jmp_call;
instr_cf_alloc_t alloc;
struct PACKED {
uint64_t dummy : 44;
instr_cf_opc_t opc : 4;
};
} instr_cf_t;
/*
* FETCH instructions:
*/
typedef enum {
VTX_FETCH = 0,
TEX_FETCH = 1,
TEX_GET_BORDER_COLOR_FRAC = 16,
TEX_GET_COMP_TEX_LOD = 17,
TEX_GET_GRADIENTS = 18,
TEX_GET_WEIGHTS = 19,
TEX_SET_TEX_LOD = 24,
TEX_SET_GRADIENTS_H = 25,
TEX_SET_GRADIENTS_V = 26,
TEX_RESERVED_4 = 27,
} instr_fetch_opc_t;
typedef enum {
TEX_FILTER_POINT = 0,
TEX_FILTER_LINEAR = 1,
TEX_FILTER_BASEMAP = 2, /* only applicable for mip-filter */
TEX_FILTER_USE_FETCH_CONST = 3,
} instr_tex_filter_t;
typedef enum {
ANISO_FILTER_DISABLED = 0,
ANISO_FILTER_MAX_1_1 = 1,
ANISO_FILTER_MAX_2_1 = 2,
ANISO_FILTER_MAX_4_1 = 3,
ANISO_FILTER_MAX_8_1 = 4,
ANISO_FILTER_MAX_16_1 = 5,
ANISO_FILTER_USE_FETCH_CONST = 7,
} instr_aniso_filter_t;
typedef enum {
ARBITRARY_FILTER_2X4_SYM = 0,
ARBITRARY_FILTER_2X4_ASYM = 1,
ARBITRARY_FILTER_4X2_SYM = 2,
ARBITRARY_FILTER_4X2_ASYM = 3,
ARBITRARY_FILTER_4X4_SYM = 4,
ARBITRARY_FILTER_4X4_ASYM = 5,
ARBITRARY_FILTER_USE_FETCH_CONST = 7,
} instr_arbitrary_filter_t;
typedef enum {
SAMPLE_CENTROID = 0,
SAMPLE_CENTER = 1,
} instr_sample_loc_t;
typedef unsigned instr_surf_fmt_t;
typedef struct PACKED {
/* dword0: */
instr_fetch_opc_t opc : 5;
uint8_t src_reg : 6;
uint8_t src_reg_am : 1;
uint8_t dst_reg : 6;
uint8_t dst_reg_am : 1;
uint8_t fetch_valid_only : 1;
uint8_t const_idx : 5;
uint8_t tx_coord_denorm : 1;
uint8_t src_swiz : 6;
/* dword1: */
uint16_t dst_swiz : 12;
instr_tex_filter_t mag_filter : 2;
instr_tex_filter_t min_filter : 2;
instr_tex_filter_t mip_filter : 2;
instr_aniso_filter_t aniso_filter : 3;
instr_arbitrary_filter_t arbitrary_filter : 3;
instr_tex_filter_t vol_mag_filter : 2;
instr_tex_filter_t vol_min_filter : 2;
uint8_t use_comp_lod : 1;
uint8_t use_reg_lod : 2;
uint8_t pred_select : 1;
/* dword2: */
uint8_t use_reg_gradients : 1;
instr_sample_loc_t sample_location : 1;
uint8_t lod_bias : 7;
uint8_t unused : 7;
uint8_t offset_x : 5;
uint8_t offset_y : 5;
uint8_t offset_z : 5;
uint8_t pred_condition : 1;
} instr_fetch_tex_t;
typedef struct PACKED {
/* dword0: */
instr_fetch_opc_t opc : 5;
uint8_t src_reg : 6;
uint8_t src_reg_am : 1;
uint8_t dst_reg : 6;
uint8_t dst_reg_am : 1;
uint8_t must_be_one : 1;
uint8_t const_index : 5;
uint8_t const_index_sel : 2;
uint8_t reserved0 : 3;
uint8_t src_swiz : 2;
/* dword1: */
uint16_t dst_swiz : 12;
uint8_t format_comp_all : 1; /* '1' for signed, '0' for unsigned? */
uint8_t num_format_all : 1; /* '0' for normalized, '1' for unnormalized */
uint8_t signed_rf_mode_all : 1;
uint8_t reserved1 : 1;
instr_surf_fmt_t format : 6;
uint8_t reserved2 : 1;
uint8_t exp_adjust_all : 7;
uint8_t reserved3 : 1;
uint8_t pred_select : 1;
/* dword2: */
uint8_t stride : 8;
/* possibly offset and reserved4 are swapped on a200? */
uint8_t offset : 8;
uint8_t reserved4 : 8;
uint8_t reserved5 : 7;
uint8_t pred_condition : 1;
} instr_fetch_vtx_t;
typedef union PACKED {
instr_fetch_tex_t tex;
instr_fetch_vtx_t vtx;
struct PACKED {
/* dword0: */
instr_fetch_opc_t opc : 5;
uint32_t dummy0 : 27;
/* dword1: */
uint32_t dummy1 : 32;
/* dword2: */
uint32_t dummy2 : 32;
};
} instr_fetch_t;
#endif /* INSTR_H_ */

File diff suppressed because it is too large Load Diff

163
src/freedreno/decode/io.c Normal file
View File

@ -0,0 +1,163 @@
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
/*
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*
* Authors:
* Rob Clark <robclark@freedesktop.org>
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <archive.h>
#include <archive_entry.h>
#include "io.h"
struct io {
struct archive *a;
struct archive_entry *entry;
unsigned offset;
};
static void io_error(struct io *io)
{
fprintf(stderr, "%s\n", archive_error_string(io->a));
io_close(io);
}
static struct io * io_new(void)
{
struct io *io = calloc(1, sizeof(*io));
int ret;
if (!io)
return NULL;
io->a = archive_read_new();
ret = archive_read_support_filter_gzip(io->a);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
ret = archive_read_support_filter_none(io->a);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
ret = archive_read_support_format_all(io->a);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
ret = archive_read_support_format_raw(io->a);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
return io;
}
struct io * io_open(const char *filename)
{
struct io *io = io_new();
int ret;
if (!io)
return NULL;
ret = archive_read_open_filename(io->a, filename, 10240);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
ret = archive_read_next_header(io->a, &io->entry);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
return io;
}
struct io * io_openfd(int fd)
{
struct io *io = io_new();
int ret;
if (!io)
return NULL;
ret = archive_read_open_fd(io->a, fd, 10240);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
ret = archive_read_next_header(io->a, &io->entry);
if (ret != ARCHIVE_OK) {
io_error(io);
return NULL;
}
return io;
}
void io_close(struct io *io)
{
archive_read_free(io->a);
free(io);
}
unsigned io_offset(struct io *io)
{
return io->offset;
}
#include <assert.h>
int io_readn(struct io *io, void *buf, int nbytes)
{
char *ptr = buf;
int ret = 0;
while (nbytes > 0) {
int n = archive_read_data(io->a, ptr, nbytes);
if (n < 0) {
fprintf(stderr, "%s\n", archive_error_string(io->a));
return n;
}
if (n == 0)
break;
ptr += n;
nbytes -= n;
ret += n;
io->offset += n;
}
return ret;
}

51
src/freedreno/decode/io.h Normal file
View File

@ -0,0 +1,51 @@
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
/*
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*
* Authors:
* Rob Clark <robclark@freedesktop.org>
*/
#ifndef IO_H_
#define IO_H_
/* Simple API to abstract reading from file which might be compressed.
* Maybe someday I'll add writing..
*/
struct io;
struct io * io_open(const char *filename);
struct io * io_openfd(int fd);
void io_close(struct io *io);
unsigned io_offset(struct io *io);
int io_readn(struct io *io, void *buf, int nbytes);
static inline int
check_extension(const char *path, const char *ext)
{
return strcmp(path + strlen(path) - strlen(ext), ext) == 0;
}
#endif /* IO_H_ */

View File

@ -0,0 +1,144 @@
# Copyright © 2020 Google, Inc
# 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, sublicense, 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 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 NONINFRINGEMENT. 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.
dep_lua = dependency('lua53', required: false)
if not dep_lua.found()
dep_lua = dependency('lua52', required: false)
endif
if not dep_lua.found()
dep_lua = dependency('lua', required: false)
endif
dep_libarchive = dependency('libarchive', required: false)
# Shared cmdstream decoding:
libfreedreno_cffdec = static_library(
'freedreno_cffdec',
[
'buffers.c',
'buffers.h',
'cffdec.c',
'cffdec.h',
'disasm-a2xx.c',
'disasm-a3xx.c',
'disasm.h',
'instr-a2xx.h',
'instr-a3xx.h',
'pager.c',
'pager.h',
'rnnutil.c',
'rnnutil.h',
'util.h',
],
include_directories: [
inc_freedreno_rnn,
],
c_args : [ no_override_init_args ],
gnu_symbol_visibility: 'hidden',
dependencies: [],
link_with: libfreedreno_rnn,
build_by_default: false,
)
if dep_libarchive.found()
libfreedreno_io = static_library(
'libfreedreno_io',
[
'io.c',
'io.h',
],
include_directories: [],
c_args : [no_override_init_args],
gnu_symbol_visibility: 'hidden',
dependencies: [
dep_libarchive,
],
build_by_default: false,
)
endif
if dep_lua.found() and dep_libarchive.found()
cffdump = executable(
'cffdump',
[
'cffdump.c',
'script.c',
'script.h'
],
include_directories: [
inc_freedreno_rnn,
],
c_args : [no_override_init_args],
gnu_symbol_visibility: 'hidden',
dependencies: [
dep_lua,
],
link_with: [
libfreedreno_cffdec,
libfreedreno_io,
],
build_by_default: with_tools.contains('freedreno'),
install : with_tools.contains('freedreno'),
)
endif
crashdec = executable(
'crashdec',
'crashdec.c',
include_directories: [
inc_freedreno_rnn,
],
gnu_symbol_visibility: 'hidden',
dependencies: [],
link_with: [
libfreedreno_cffdec,
],
build_by_default: with_tools.contains('freedreno'),
install : with_tools.contains('freedreno'),
)
if dep_libarchive.found()
pgmdump = executable(
'pgmdump',
'pgmdump.c',
include_directories: [],
gnu_symbol_visibility: 'hidden',
dependencies: [],
link_with: [
libfreedreno_cffdec,
libfreedreno_io,
],
build_by_default: with_tools.contains('freedreno'),
install: false,
)
pgmdump2 = executable(
'pgmdump2',
'pgmdump2.c',
include_directories: [],
gnu_symbol_visibility: 'hidden',
dependencies: [],
link_with: [
libfreedreno_cffdec,
libfreedreno_io,
],
build_by_default: with_tools.contains('freedreno'),
install: false,
)
endif

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2018 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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 <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "pager.h"
static pid_t pager_pid;
static void
pager_death(int n)
{
exit(0);
}
void
pager_open(void)
{
int fd[2];
if (pipe(fd) < 0) {
fprintf(stderr, "Failed to create pager pipe: %m\n");
exit(-1);
}
pager_pid = fork();
if (pager_pid < 0) {
fprintf(stderr, "Failed to fork pager: %m\n");
exit(-1);
}
if (pager_pid == 0) {
const char* less_opts;
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
less_opts = "FRSMKX";
setenv("LESS", less_opts, 1);
execlp("less", "less", NULL);
} else {
/* we want to kill the parent process when pager exits: */
signal(SIGCHLD, pager_death);
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
}
}
int
pager_close(void)
{
siginfo_t status;
close(STDOUT_FILENO);
while (true) {
memset(&status, 0, sizeof(status));
if (waitid(P_PID, pager_pid, &status, WEXITED) < 0) {
if (errno == EINTR)
continue;
return -errno;
}
return 0;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
#ifndef __PAGER_H__
#define __PAGER_H__
void pager_open(void);
int pager_close(void);
#endif /* __PAGER_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,585 @@
/*
* Copyright (c) 2018 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
/*
* Decoder for "new" GL_OES_get_program_binary format.
*
* Overall structure is:
*
* - header at top, contains, amongst other things, offsets of
* per shader stage sections.
* - per shader stage section (shader_info) starts with a header,
* followed by a variably length list of descriptors. Each
* descriptor has a type/count/size plus offset from the start
* of shader_info section where the data is found
*/
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
#include <fcntl.h>
#include <string.h>
#include "redump.h"
#include "disasm.h"
#include "io.h"
#include "util.h"
const char *infile;
static int dump_full = 0;
static int dump_offsets = 0;
static int gpu_id = 320;
static int shaderdb = 0; /* output shaderdb style traces to stderr */
struct state {
char *buf;
int sz;
int lvl;
/* current shader_info section, some offsets calculated relative to
* this, rather than relative to start of buffer.
*/
void *shader;
/* size of each entry within a shader_descriptor_blk: */
int desc_size;
const char *shader_type;
int full_regs;
int half_regs;
};
#define PACKED __attribute__((__packed__))
#define OFF(field) do { \
if (dump_offsets) \
printf("%08x: ", (uint32_t)((char *)&field - state->buf));\
} while (0)
/* decode field as hex */
#define X(s, field) do { \
OFF(s->field); \
printf("%s%12s:\t0x%x\n", tab(state->lvl), #field, s->field); \
} while (0)
/* decode field as digit */
#define D(s, field) do { \
OFF(s->field); \
printf("%s%12s:\t%u\n", tab(state->lvl), #field, s->field); \
} while (0)
/* decode field as float/hex */
#define F(s, field) do { \
OFF(s->field); \
printf("%s%12s:\t%f (0x%0x)\n", tab(state->lvl), #field, \
d2f(s->field), s->field); \
} while (0)
/* decode field as register: (type is 'r' or 'c') */
#define R(s, field, type) do { \
OFF(s->field); \
printf("%s%12s:\t%c%u.%c\n", tab(state->lvl), #field, type, \
(s->field >> 2), "xyzw"[s->field & 0x3]); \
} while (0)
/* decode inline string (presumably null terminated?) */
#define S(s, field) do { \
OFF(s->field); \
printf("%s%12s:\t%s\n", tab(state->lvl), #field, s->field); \
} while (0)
/* decode string-table string */
#define T(s, field) TODO
/* decode field as unknown */
#define U(s, start, end) \
dump_unknown(state, s->unk_ ## start ## _ ## end, 0x ## start, (4 + 0x ## end - 0x ## start) / 4)
/* decode field as offset to other section */
#define O(s, field, type) do { \
X(s, field); \
assert(s->field < state->sz); \
void *_p = &state->buf[s->field]; \
state->lvl++; \
decode_ ## type (state, _p); \
state->lvl--; \
} while (0)
struct shader_info;
static void decode_shader_info(struct state *state, struct shader_info *info);
static void dump_unknown(struct state *state, void *buf, unsigned start, unsigned n)
{
uint32_t *ptr = buf;
uint8_t *ascii = buf;
for (unsigned i = 0; i < n; i++) {
uint32_t d = ptr[i];
if (dump_offsets)
printf("%08x:", (uint32_t)((char *)&ptr[i] - state->buf));
printf("%s %04x:\t%08x", tab(state->lvl), start + i * 4, d);
printf("\t|");
for (unsigned j = 0; j < 4; j++) {
uint8_t c = *(ascii++);
printf("%c", (isascii(c) && !iscntrl(c)) ? c : '.');
}
printf("|\t%f", d2f(d));
/* TODO maybe scan for first non-null and non-ascii char starting from
* end of shader binary to (roughly) establish the start of the string
* table.. that would be a bit better filter for deciding if something
* might be a pointer into the string table. Also, the previous char
* to what it points to should probably be null.
*/
if ((d < state->sz) &&
isascii(state->buf[d]) &&
(strlen(&state->buf[d]) > 2) &&
isascii(state->buf[d+1]))
printf("\t<== %s", &state->buf[d]);
printf("\n");
}
}
struct PACKED header {
uint32_t version; /* I guess, always b10bcace ? */
uint32_t unk_0004_0014[5];
uint32_t size;
uint32_t size2; /* just to be sure? */
uint32_t unk_0020_0020[1];
uint32_t chksum; /* I guess? Small changes seem to result in big diffs here */
uint32_t unk_0028_0050[11];
uint32_t fs_info; /* offset of FS shader_info section */
uint32_t unk_0058_0090[15];
uint32_t vs_info; /* offset of VS shader_info section */
uint32_t unk_0098_00b0[7];
uint32_t vs_info2; /* offset of VS shader_info section (again?) */
uint32_t unk_00b8_0110[23];
uint32_t bs_info; /* offset of binning shader_info section */
};
static void decode_header(struct state *state, struct header *hdr)
{
X(hdr, version);
U(hdr, 0004, 0014);
X(hdr, size);
X(hdr, size2);
U(hdr, 0020, 0020);
X(hdr, chksum);
U(hdr, 0028, 0050);
state->shader_type = "FRAG";
O(hdr, fs_info, shader_info);
U(hdr, 0058, 0090);
state->shader_type = "VERT";
O(hdr, vs_info, shader_info);
U(hdr, 0098, 00b0);
assert(hdr->vs_info == hdr->vs_info2); /* not sure what this if it is ever different */
X(hdr, vs_info2);
U(hdr, 00b8, 0110);
state->shader_type = "BVERT";
O(hdr, bs_info, shader_info);
/* not sure how much of the rest of contents before start of fs_info
* is the header, vs other things.. just dump it all as unknown for
* now:
*/
dump_unknown(state, (void *)hdr + sizeof(*hdr),
sizeof(*hdr), (hdr->fs_info - sizeof(*hdr)) / 4);
}
struct PACKED shader_entry_point {
/* entry point name, ie. "main" of TBD length, followed by unknown */
char name[8];
};
static void decode_shader_entry_point(struct state *state,
struct shader_entry_point *e)
{
S(e, name);
}
struct PACKED shader_config {
uint32_t unk_0000_0008[3];
uint32_t full_regs;
uint32_t half_regs;
};
static void decode_shader_config(struct state *state, struct shader_config *cfg)
{
U(cfg, 0000, 0008);
D(cfg, full_regs);
D(cfg, half_regs);
state->full_regs = cfg->full_regs;
state->half_regs = cfg->half_regs;
/* dump reset of unknown (size differs btwn versions) */
dump_unknown(state, (void *)cfg + sizeof(*cfg), sizeof(*cfg),
(state->desc_size - sizeof(*cfg))/4);
}
struct PACKED shader_io_block {
/* name of TBD length followed by unknown.. 42 dwords total */
char name[20];
uint32_t unk_0014_00a4[37];
};
static void decode_shader_io_block(struct state *state,
struct shader_io_block *io)
{
S(io, name);
U(io, 0014, 00a4);
}
struct PACKED shader_constant_block {
uint32_t value;
uint32_t unk_0004_000c[3];
uint32_t regid;
uint32_t unk_0014_0024[5];
};
static void decode_shader_constant_block(struct state *state,
struct shader_constant_block *c)
{
F(c, value);
U(c, 0004, 000c);
R(c, regid, 'c');
U(c, 0014, 0024);
}
enum {
ENTRY_POINT = 0, /* shader_entry_point */
SHADER_CONFIG = 1, /* XXX placeholder name */
SHADER_INPUT = 2, /* shader_io_block */
SHADER_OUTPUT = 3, /* shader_io_block */
CONSTANTS = 6, /* shader_constant_block */
INTERNAL = 8, /* internal input, like bary.f coord */
SHADER = 10,
} shader_info_block_type;
/* Refers to location of some type of records, with an offset relative to
* start of shader_info block.
*/
struct PACKED shader_descriptor_block {
uint32_t type; /* block type */
uint32_t offset; /* offset (relative to start of shader_info block) */
uint32_t size; /* size in bytes */
uint32_t count; /* number of records */
uint32_t unk_0010_0010[1];
};
static void decode_shader_descriptor_block(struct state *state,
struct shader_descriptor_block *blk)
{
D(blk, type);
X(blk, offset);
D(blk, size);
D(blk, count);
U(blk, 0010, 0010);
/* offset relative to current shader block: */
void *ptr = state->shader + blk->offset;
if (blk->count == 0) {
assert(blk->size == 0);
} else {
assert((blk->size % blk->count) == 0);
}
state->desc_size = blk->size / blk->count;
state->lvl++;
for (unsigned i = 0; i < blk->count; i++) {
switch (blk->type) {
case ENTRY_POINT:
printf("%sentry point %u:\n", tab(state->lvl-1), i);
decode_shader_entry_point(state, ptr);
break;
case SHADER_CONFIG:
printf("%sconfig %u:\n", tab(state->lvl-1), i);
decode_shader_config(state, ptr);
break;
case SHADER_INPUT:
printf("%sinput %u:\n", tab(state->lvl-1), i);
decode_shader_io_block(state, ptr);
break;
case SHADER_OUTPUT:
printf("%soutput %u:\n", tab(state->lvl-1), i);
decode_shader_io_block(state, ptr);
break;
case INTERNAL:
printf("%sinternal input %u:\n", tab(state->lvl-1), i);
decode_shader_io_block(state, ptr);
break;
case CONSTANTS:
printf("%sconstant %u:\n", tab(state->lvl-1), i);
decode_shader_constant_block(state, ptr);
break;
case SHADER: {
struct shader_stats stats;
printf("%sshader %u:\n", tab(state->lvl-1), i);
disasm_a3xx_stat(ptr, blk->size/4, state->lvl, stdout, gpu_id, &stats);
if (shaderdb) {
unsigned dwords = 2 * stats.instlen;
if (gpu_id >= 400) {
dwords = ALIGN(dwords, 16 * 2);
} else {
dwords = ALIGN(dwords, 4 * 2);
}
unsigned half_regs = state->half_regs;
unsigned full_regs = state->full_regs;
/* On a6xx w/ merged/conflicting half and full regs, the
* full_regs footprint will be max of full_regs and half
* of half_regs.. we only care about which value is higher.
*/
if (gpu_id >= 600) {
/* footprint of half_regs in units of full_regs: */
unsigned half_full = (half_regs + 1) / 2;
if (half_full > full_regs)
full_regs = half_full;
half_regs = 0;
}
fprintf(stderr,
"%s shader: %u inst, %u nops, %u non-nops, %u dwords, "
"%u half, %u full, %u constlen, "
"%u (ss), %u (sy), %d max_sun, %d loops\n",
state->shader_type, stats.instructions,
stats.nops, stats.instructions - stats.nops,
dwords, half_regs, full_regs,
stats.constlen, stats.ss, stats.sy,
0, 0); /* max_sun or loops not possible */
}
/* this is a special case in a way, blk->count is # of
* instructions but disasm_a3xx() decodes all instructions,
* so just bail.
*/
i = blk->count;
break;
}
default:
dump_unknown(state, ptr, 0, state->desc_size/4);
break;
}
ptr += state->desc_size;
}
state->lvl--;
}
/* there looks like one of these per shader, followed by "main" and
* some more info, and then the shader itself.
*/
struct PACKED shader_info {
uint32_t unk_0000_0010[5];
uint32_t desc_off; /* offset to first descriptor block */
uint32_t num_blocks;
};
static void decode_shader_info(struct state *state, struct shader_info *info)
{
assert((info->desc_off % 4) == 0);
U(info, 0000, 0010);
X(info, desc_off);
D(info, num_blocks);
dump_unknown(state, &info[1], 0, (info->desc_off - sizeof(*info))/4);
state->shader = info;
struct shader_descriptor_block *blocks = ((void *)info) + info->desc_off;
for (unsigned i = 0; i < info->num_blocks; i++) {
printf("%sdescriptor %u:\n", tab(state->lvl), i);
state->lvl++;
decode_shader_descriptor_block(state, &blocks[i]);
state->lvl--;
}
}
static void dump_program(struct state *state)
{
struct header *hdr = (void *)state->buf;
if (dump_full)
dump_unknown(state, state->buf, 0, state->sz/4);
decode_header(state, hdr);
}
int main(int argc, char **argv)
{
enum rd_sect_type type = RD_NONE;
enum debug_t debug = 0;
void *buf = NULL;
int sz;
struct io *io;
int raw_program = 0;
/* lame argument parsing: */
while (1) {
if ((argc > 1) && !strcmp(argv[1], "--verbose")) {
debug |= PRINT_RAW | PRINT_VERBOSE;
argv++;
argc--;
continue;
}
if ((argc > 1) && !strcmp(argv[1], "--expand")) {
debug |= EXPAND_REPEAT;
argv++;
argc--;
continue;
}
if ((argc > 1) && !strcmp(argv[1], "--full")) {
/* only short dump, original shader, symbol table, and disassembly */
dump_full = 1;
argv++;
argc--;
continue;
}
if ((argc > 1) && !strcmp(argv[1], "--dump-offsets")) {
dump_offsets = 1;
argv++;
argc--;
continue;
}
if ((argc > 1) && !strcmp(argv[1], "--raw")) {
raw_program = 1;
argv++;
argc--;
continue;
}
if ((argc > 1) && !strcmp(argv[1], "--shaderdb")) {
shaderdb = 1;
argv++;
argc--;
continue;
}
break;
}
if (argc != 2) {
fprintf(stderr, "usage: pgmdump2 [--verbose] [--expand] [--full] [--dump-offsets] [--raw] [--shaderdb] testlog.rd\n");
return -1;
}
disasm_set_debug(debug);
infile = argv[1];
io = io_open(infile);
if (!io) {
fprintf(stderr, "could not open: %s\n", infile);
return -1;
}
if (raw_program)
{
io_readn(io, &sz, 4);
free(buf);
/* note: allow hex dumps to go a bit past the end of the buffer..
* might see some garbage, but better than missing the last few bytes..
*/
buf = calloc(1, sz + 3);
io_readn(io, buf + 4, sz);
(*(int*)buf) = sz;
struct state state = {
.buf = buf,
.sz = sz,
};
printf("############################################################\n");
printf("program:\n");
dump_program(&state);
printf("############################################################\n");
return 0;
}
/* figure out what sort of input we are dealing with: */
if (!(check_extension(infile, ".rd") || check_extension(infile, ".rd.gz"))) {
int ret;
buf = calloc(1, 100 * 1024);
ret = io_readn(io, buf, 100 * 1024);
if (ret < 0) {
fprintf(stderr, "error: %m");
return -1;
}
return disasm_a3xx(buf, ret/4, 0, stdout, gpu_id);
}
while ((io_readn(io, &type, sizeof(type)) > 0) && (io_readn(io, &sz, 4) > 0)) {
free(buf);
/* note: allow hex dumps to go a bit past the end of the buffer..
* might see some garbage, but better than missing the last few bytes..
*/
buf = calloc(1, sz + 3);
io_readn(io, buf, sz);
switch(type) {
case RD_TEST:
if (dump_full)
printf("test: %s\n", (char *)buf);
break;
case RD_VERT_SHADER:
printf("vertex shader:\n%s\n", (char *)buf);
break;
case RD_FRAG_SHADER:
printf("fragment shader:\n%s\n", (char *)buf);
break;
case RD_PROGRAM: {
struct state state = {
.buf = buf,
.sz = sz,
};
printf("############################################################\n");
printf("program:\n");
dump_program(&state);
printf("############################################################\n");
break;
}
case RD_GPU_ID:
gpu_id = *((unsigned int *)buf);
printf("gpu_id: %d\n", gpu_id);
break;
default:
break;
}
}
io_close(io);
return 0;
}

View File

@ -0,0 +1,76 @@
/*
* Copyright © 2012 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
#ifndef REDUMP_H_
#define REDUMP_H_
enum rd_sect_type {
RD_NONE,
RD_TEST, /* ascii text */
RD_CMD, /* ascii text */
RD_GPUADDR, /* u32 gpuaddr, u32 size */
RD_CONTEXT, /* raw dump */
RD_CMDSTREAM, /* raw dump */
RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */
RD_PARAM, /* u32 param_type, u32 param_val, u32 bitlen */
RD_FLUSH, /* empty, clear previous params */
RD_PROGRAM, /* shader program, raw dump */
RD_VERT_SHADER,
RD_FRAG_SHADER,
RD_BUFFER_CONTENTS,
RD_GPU_ID,
};
/* RD_PARAM types: */
enum rd_param_type {
RD_PARAM_SURFACE_WIDTH,
RD_PARAM_SURFACE_HEIGHT,
RD_PARAM_SURFACE_PITCH,
RD_PARAM_COLOR,
RD_PARAM_BLIT_X,
RD_PARAM_BLIT_Y,
RD_PARAM_BLIT_WIDTH,
RD_PARAM_BLIT_HEIGHT,
RD_PARAM_BLIT_X2, /* BLIT_X + BLIT_WIDTH */
RD_PARAM_BLIT_Y2, /* BLIT_Y + BLIT_WIDTH */
};
void rd_start(const char *name, const char *fmt, ...) __attribute__((weak));
void rd_end(void) __attribute__((weak));
void rd_write_section(enum rd_sect_type type, const void *buf, int sz) __attribute__((weak));
/* for code that should run with and without libwrap, use the following
* macros which check if the fxns are present before calling
*/
#define RD_START(n,f,...) do { if (rd_start) rd_start(n,f,##__VA_ARGS__); } while (0)
#define RD_END() do { if (rd_end) rd_end(); } while (0)
#define RD_WRITE_SECTION(t,b,s) do { if (rd_write_section) rd_write_section(t,b,s); } while (0)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#undef ALIGN
#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif /* REDUMP_H_ */

View File

@ -0,0 +1,217 @@
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
/*
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*
* Authors:
* Rob Clark <robclark@freedesktop.org>
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "rnnutil.h"
static struct rnndomain *finddom(struct rnn *rnn, uint32_t regbase)
{
if (rnndec_checkaddr(rnn->vc, rnn->dom[0], regbase, 0))
return rnn->dom[0];
return rnn->dom[1];
}
void _rnn_init(struct rnn *rnn, int nocolor)
{
rnn_init();
rnn->db = rnn_newdb();
rnn->vc_nocolor = rnndec_newcontext(rnn->db);
rnn->vc_nocolor->colors = &envy_null_colors;
if (nocolor) {
rnn->vc = rnn->vc_nocolor;
} else {
rnn->vc = rnndec_newcontext(rnn->db);
rnn->vc->colors = &envy_def_colors;
}
}
struct rnn *rnn_new(int nocolor)
{
struct rnn *rnn = calloc(sizeof(*rnn), 1);
if (!rnn)
return NULL;
_rnn_init(rnn, nocolor);
return rnn;
}
static void init(struct rnn *rnn, char *file, char *domain)
{
/* prepare rnn stuff for lookup */
rnn_parsefile(rnn->db, file);
rnn_prepdb(rnn->db);
rnn->dom[0] = rnn_finddomain(rnn->db, domain);
if ((strcmp(domain, "A2XX") == 0) || (strcmp(domain, "A3XX") == 0)) {
rnn->dom[1] = rnn_finddomain(rnn->db, "AXXX");
} else {
rnn->dom[1] = rnn->dom[0];
}
if (!rnn->dom[0] && rnn->dom[1]) {
fprintf(stderr, "Could not find domain %s in %s\n", domain, file);
}
rnn->variant = domain;
rnndec_varadd(rnn->vc, "chip", domain);
if (rnn->vc != rnn->vc_nocolor)
rnndec_varadd(rnn->vc_nocolor, "chip", domain);
}
void rnn_load_file(struct rnn *rnn, char *file, char *domain)
{
init(rnn, file, domain);
}
void rnn_load(struct rnn *rnn, const char *gpuname)
{
if (strstr(gpuname, "a2")) {
init(rnn, "adreno/a2xx.xml", "A2XX");
} else if (strstr(gpuname, "a3")) {
init(rnn, "adreno/a3xx.xml", "A3XX");
} else if (strstr(gpuname, "a4")) {
init(rnn, "adreno/a4xx.xml", "A4XX");
} else if (strstr(gpuname, "a5")) {
init(rnn, "adreno/a5xx.xml", "A5XX");
} else if (strstr(gpuname, "a6")) {
init(rnn, "adreno/a6xx.xml", "A6XX");
}
}
uint32_t rnn_regbase(struct rnn *rnn, const char *name)
{
uint32_t regbase = rnndec_decodereg(rnn->vc_nocolor, rnn->dom[0], name);
if (!regbase)
regbase = rnndec_decodereg(rnn->vc_nocolor, rnn->dom[1], name);
return regbase;
}
const char *rnn_regname(struct rnn *rnn, uint32_t regbase, int color)
{
static char buf[128];
struct rnndecaddrinfo *info;
info = rnndec_decodeaddr(color ? rnn->vc : rnn->vc_nocolor,
finddom(rnn, regbase), regbase, 0);
if (info) {
strcpy(buf, info->name);
free(info->name);
free(info);
return buf;
}
return NULL;
}
struct rnndecaddrinfo *rnn_reginfo(struct rnn *rnn, uint32_t regbase)
{
return rnndec_decodeaddr(rnn->vc, finddom(rnn, regbase), regbase, 0);
}
const char *rnn_enumname(struct rnn *rnn, const char *name, uint32_t val)
{
struct rnndeccontext *ctx = rnn->vc;
struct rnnenum *en = rnn_findenum(ctx->db, name);
if (en) {
int i;
for (i = 0; i < en->valsnum; i++) {
struct rnnvalue *eval = en->vals[i];
if (eval->valvalid && eval->value == val &&
rnndec_varmatch(ctx, &eval->varinfo)) {
return en->vals[i]->name;
}
}
}
return NULL;
}
static struct rnndelem *regelem(struct rnndomain *domain, const char *name)
{
int i;
for (i = 0; i < domain->subelemsnum; i++) {
struct rnndelem *elem = domain->subelems[i];
if (!strcmp(elem->name, name))
return elem;
}
return NULL;
}
/* Lookup rnndelem by name: */
struct rnndelem *rnn_regelem(struct rnn *rnn, const char *name)
{
struct rnndelem *elem = regelem(rnn->dom[0], name);
if (elem)
return elem;
return regelem(rnn->dom[1], name);
}
static struct rnndelem *regoff(struct rnndomain *domain, uint32_t offset)
{
int i;
for (i = 0; i < domain->subelemsnum; i++) {
struct rnndelem *elem = domain->subelems[i];
if (elem->offset == offset)
return elem;
}
return NULL;
}
/* Lookup rnndelem by offset: */
struct rnndelem *rnn_regoff(struct rnn *rnn, uint32_t offset)
{
struct rnndelem *elem = regoff(rnn->dom[0], offset);
if (elem)
return elem;
return regoff(rnn->dom[1], offset);
}
enum rnnttype rnn_decodelem(struct rnn *rnn, struct rnntypeinfo *info,
uint32_t regval, union rnndecval *val)
{
val->u = regval;
switch (info->type) {
case RNN_TTYPE_INLINE_ENUM:
case RNN_TTYPE_ENUM:
case RNN_TTYPE_HEX:
case RNN_TTYPE_INT:
case RNN_TTYPE_UINT:
case RNN_TTYPE_FLOAT:
case RNN_TTYPE_BOOLEAN:
return info->type;
case RNN_TTYPE_FIXED:
case RNN_TTYPE_UFIXED:
/* TODO */
default:
return RNN_TTYPE_INVALID;
}
}

View File

@ -0,0 +1,66 @@
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
/*
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*
* Authors:
* Rob Clark <robclark@freedesktop.org>
*/
#ifndef RNNUTIL_H_
#define RNNUTIL_H_
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "rnn.h"
#include "rnndec.h"
struct rnn {
struct rnndb *db;
struct rnndeccontext *vc, *vc_nocolor;
struct rnndomain *dom[2];
const char *variant;
};
union rnndecval {
uint32_t u;
int32_t i;
float f;
};
void _rnn_init(struct rnn *rnn, int nocolor);
struct rnn *rnn_new(int nocolor);
void rnn_load_file(struct rnn *rnn, char *file, char *domain);
void rnn_load(struct rnn *rnn, const char *gpuname);
uint32_t rnn_regbase(struct rnn *rnn, const char *name);
const char *rnn_regname(struct rnn *rnn, uint32_t regbase, int color);
struct rnndecaddrinfo *rnn_reginfo(struct rnn *rnn, uint32_t regbase);
const char *rnn_enumname(struct rnn *rnn, const char *name, uint32_t val);
struct rnndelem *rnn_regelem(struct rnn *rnn, const char *name);
struct rnndelem *rnn_regoff(struct rnn *rnn, uint32_t offset);
enum rnnttype rnn_decodelem(struct rnn *rnn, struct rnntypeinfo *info,
uint32_t regval, union rnndecval *val);
#endif /* RNNUTIL_H_ */

View File

@ -0,0 +1,775 @@
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
/*
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*
* Authors:
* Rob Clark <robclark@freedesktop.org>
*/
#define _GNU_SOURCE
#define LUA_COMPAT_APIINTCASTS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <assert.h>
#include "script.h"
#include "cffdec.h"
#include "rnnutil.h"
static lua_State *L;
#if 0
#define DBG(fmt, ...) \
do { printf(" ** %s:%d ** "fmt "\n", \
__FUNCTION__, __LINE__, ##__VA_ARGS__); } while (0)
#else
#define DBG(fmt, ...) do {} while (0)
#endif
/* An rnn based decoder, which can either be decoding current register
* values, or domain based decoding of a pm4 packet.
*
*/
struct rnndec {
struct rnn base;
/* for pm4 packet decoding: */
uint32_t sizedwords;
uint32_t *dwords;
};
static inline struct rnndec *to_rnndec(struct rnn *rnn)
{
return (struct rnndec *)rnn;
}
static uint32_t rnn_val(struct rnn *rnn, uint32_t regbase)
{
struct rnndec *rnndec = to_rnndec(rnn);
if (!rnndec->sizedwords) {
return reg_val(regbase);
} else if (regbase < rnndec->sizedwords) {
return rnndec->dwords[regbase];
} else {
// XXX throw an error
return -1;
}
}
/* does not return */
static void error(const char *fmt)
{
fprintf(stderr, fmt, lua_tostring(L, -1));
exit(1);
}
/*
* An enum type that can be used as string or number:
*/
struct rnndenum {
const char *str;
int val;
};
static int l_meta_rnn_enum_tostring(lua_State *L)
{
struct rnndenum *e = lua_touserdata(L, 1);
if (e->str) {
lua_pushstring(L, e->str);
} else {
char buf[32];
sprintf(buf, "%u", e->val);
lua_pushstring(L, buf);
}
return 1;
}
/* so, this doesn't actually seem to be implemented yet, but hopefully
* some day lua comes to it's senses
*/
static int l_meta_rnn_enum_tonumber(lua_State *L)
{
struct rnndenum *e = lua_touserdata(L, 1);
lua_pushinteger(L, e->val);
return 1;
}
static const struct luaL_Reg l_meta_rnn_enum[] = {
{"__tostring", l_meta_rnn_enum_tostring},
{"__tonumber", l_meta_rnn_enum_tonumber},
{NULL, NULL} /* sentinel */
};
static void pushenum(struct lua_State *L, int val, struct rnnenum *info)
{
struct rnndenum *e = lua_newuserdata(L, sizeof(*e));
e->val = val;
e->str = NULL;
for (int i = 0; i < info->valsnum; i++) {
if (info->vals[i]->valvalid && (info->vals[i]->value == val)) {
e->str = info->vals[i]->name;
break;
}
}
luaL_newmetatable(L, "rnnmetaenum");
luaL_setfuncs(L, l_meta_rnn_enum, 0);
lua_pop(L, 1);
luaL_setmetatable(L, "rnnmetaenum");
}
/* Expose rnn decode to script environment as "rnn" library:
*/
struct rnndoff {
struct rnn *rnn;
struct rnndelem *elem;
uint64_t offset;
};
static void push_rnndoff(lua_State *L, struct rnn *rnn,
struct rnndelem *elem, uint64_t offset)
{
struct rnndoff *rnndoff = lua_newuserdata(L, sizeof(*rnndoff));
rnndoff->rnn = rnn;
rnndoff->elem = elem;
rnndoff->offset = offset;
}
static int l_rnn_etype_array(lua_State *L, struct rnn *rnn,
struct rnndelem *elem, uint64_t offset);
static int l_rnn_etype_reg(lua_State *L, struct rnn *rnn,
struct rnndelem *elem, uint64_t offset);
static int pushdecval(struct lua_State *L, struct rnn *rnn,
uint32_t regval, struct rnntypeinfo *info)
{
union rnndecval val;
switch (rnn_decodelem(rnn, info, regval, &val)) {
case RNN_TTYPE_ENUM:
case RNN_TTYPE_INLINE_ENUM:
pushenum(L, val.i, info->eenum);
return 1;
case RNN_TTYPE_INT:
lua_pushinteger(L, val.i);
return 1;
case RNN_TTYPE_UINT:
case RNN_TTYPE_HEX:
lua_pushunsigned(L, val.u);
return 1;
case RNN_TTYPE_FLOAT:
lua_pushnumber(L, val.f);
return 1;
case RNN_TTYPE_BOOLEAN:
lua_pushboolean(L, val.u);
return 1;
case RNN_TTYPE_INVALID:
default:
return 0;
}
}
static int l_rnn_etype(lua_State *L, struct rnn *rnn,
struct rnndelem *elem, uint64_t offset)
{
int ret;
uint32_t regval;
DBG("elem=%p (%d), offset=%lu", elem, elem->type, offset);
switch (elem->type) {
case RNN_ETYPE_REG:
/* if a register has no bitfields, just return
* the raw value:
*/
regval = rnn_val(rnn, offset);
regval <<= elem->typeinfo.shr;
ret = pushdecval(L, rnn, regval, &elem->typeinfo);
if (ret)
return ret;
return l_rnn_etype_reg(L, rnn, elem, offset);
case RNN_ETYPE_ARRAY:
return l_rnn_etype_array(L, rnn, elem, offset);
default:
/* hmm.. */
printf("unhandled type: %d\n", elem->type);
return 0;
}
}
/*
* Struct Object:
* To implement stuff like 'RB_MRT[n].CONTROL' we need a struct-object
* to represent the current array index (ie. 'RB_MRT[n]')
*/
static int l_rnn_struct_meta_index(lua_State *L)
{
struct rnndoff *rnndoff = lua_touserdata(L, 1);
const char *name = lua_tostring(L, 2);
struct rnndelem *elem = rnndoff->elem;
int i;
for (i = 0; i < elem->subelemsnum; i++) {
struct rnndelem *subelem = elem->subelems[i];
if (!strcmp(name, subelem->name)) {
return l_rnn_etype(L, rnndoff->rnn, subelem,
rnndoff->offset + subelem->offset);
}
}
return 0;
}
static const struct luaL_Reg l_meta_rnn_struct[] = {
{"__index", l_rnn_struct_meta_index},
{NULL, NULL} /* sentinel */
};
static int l_rnn_etype_struct(lua_State *L, struct rnn *rnn,
struct rnndelem *elem, uint64_t offset)
{
push_rnndoff(L, rnn, elem, offset);
luaL_newmetatable(L, "rnnmetastruct");
luaL_setfuncs(L, l_meta_rnn_struct, 0);
lua_pop(L, 1);
luaL_setmetatable(L, "rnnmetastruct");
return 1;
}
/*
* Array Object:
*/
static int l_rnn_array_meta_index(lua_State *L)
{
struct rnndoff *rnndoff = lua_touserdata(L, 1);
int idx = lua_tointeger(L, 2);
struct rnndelem *elem = rnndoff->elem;
uint64_t offset = rnndoff->offset + (elem->stride * idx);
DBG("rnndoff=%p, idx=%d, numsubelems=%d",
rnndoff, idx, rnndoff->elem->subelemsnum);
/* if just a single sub-element, it is directly a register,
* otherwise we need to accumulate the array index while
* we wait for the register name within the array..
*/
if (elem->subelemsnum == 1) {
return l_rnn_etype(L, rnndoff->rnn, elem->subelems[0], offset);
} else {
return l_rnn_etype_struct(L, rnndoff->rnn, elem, offset);
}
return 0;
}
static const struct luaL_Reg l_meta_rnn_array[] = {
{"__index", l_rnn_array_meta_index},
{NULL, NULL} /* sentinel */
};
static int l_rnn_etype_array(lua_State *L, struct rnn *rnn,
struct rnndelem *elem, uint64_t offset)
{
push_rnndoff(L, rnn, elem, offset);
luaL_newmetatable(L, "rnnmetaarray");
luaL_setfuncs(L, l_meta_rnn_array, 0);
lua_pop(L, 1);
luaL_setmetatable(L, "rnnmetaarray");
return 1;
}
/*
* Register element:
*/
static int l_rnn_reg_meta_index(lua_State *L)
{
struct rnndoff *rnndoff = lua_touserdata(L, 1);
const char *name = lua_tostring(L, 2);
struct rnndelem *elem = rnndoff->elem;
struct rnntypeinfo *info = &elem->typeinfo;
struct rnnbitfield **bitfields;
int bitfieldsnum;
int i;
switch (info->type) {
case RNN_TTYPE_BITSET:
bitfields = info->ebitset->bitfields;
bitfieldsnum = info->ebitset->bitfieldsnum;
break;
case RNN_TTYPE_INLINE_BITSET:
bitfields = info->bitfields;
bitfieldsnum = info->bitfieldsnum;
break;
default:
printf("invalid register type: %d\n", info->type);
return 0;
}
for (i = 0; i < bitfieldsnum; i++) {
struct rnnbitfield *bf = bitfields[i];
if (!strcmp(name, bf->name)) {
uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
regval &= typeinfo_mask(&bf->typeinfo);
regval >>= bf->typeinfo.low;
regval <<= bf->typeinfo.shr;
DBG("name=%s, info=%p, subelemsnum=%d, type=%d, regval=%x",
name, info, rnndoff->elem->subelemsnum,
bf->typeinfo.type, regval);
return pushdecval(L, rnndoff->rnn, regval, &bf->typeinfo);
}
}
printf("invalid member: %s\n", name);
return 0;
}
static int l_rnn_reg_meta_tostring(lua_State *L)
{
struct rnndoff *rnndoff = lua_touserdata(L, 1);
uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
struct rnndecaddrinfo *info = rnn_reginfo(rnndoff->rnn, rnndoff->offset);
char *decoded;
if (info && info->typeinfo) {
decoded = rnndec_decodeval(rnndoff->rnn->vc,
info->typeinfo, regval);
} else {
asprintf(&decoded, "%08x", regval);
}
lua_pushstring(L, decoded);
free(decoded);
if (info) {
free(info->name);
free(info);
}
return 1;
}
static int l_rnn_reg_meta_tonumber(lua_State *L)
{
struct rnndoff *rnndoff = lua_touserdata(L, 1);
uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
regval <<= rnndoff->elem->typeinfo.shr;
lua_pushnumber(L, regval);
return 1;
}
static const struct luaL_Reg l_meta_rnn_reg[] = {
{"__index", l_rnn_reg_meta_index},
{"__tostring", l_rnn_reg_meta_tostring},
{"__tonumber", l_rnn_reg_meta_tonumber},
{NULL, NULL} /* sentinel */
};
static int l_rnn_etype_reg(lua_State *L, struct rnn *rnn,
struct rnndelem *elem, uint64_t offset)
{
push_rnndoff(L, rnn, elem, offset);
luaL_newmetatable(L, "rnnmetareg");
luaL_setfuncs(L, l_meta_rnn_reg, 0);
lua_pop(L, 1);
luaL_setmetatable(L, "rnnmetareg");
return 1;
}
/*
*
*/
static int l_rnn_meta_index(lua_State *L)
{
struct rnn *rnn = lua_touserdata(L, 1);
const char *name = lua_tostring(L, 2);
struct rnndelem *elem;
elem = rnn_regelem(rnn, name);
if (!elem)
return 0;
return l_rnn_etype(L, rnn, elem, elem->offset);
}
static int l_rnn_meta_gc(lua_State *L)
{
// TODO
//struct rnn *rnn = lua_touserdata(L, 1);
//rnn_deinit(rnn);
return 0;
}
static const struct luaL_Reg l_meta_rnn[] = {
{"__index", l_rnn_meta_index},
{"__gc", l_rnn_meta_gc},
{NULL, NULL} /* sentinel */
};
static int l_rnn_init(lua_State *L)
{
const char *gpuname = lua_tostring(L, 1);
struct rnndec *rnndec = lua_newuserdata(L, sizeof(*rnndec));
_rnn_init(&rnndec->base, 0);
rnn_load(&rnndec->base, gpuname);
rnndec->sizedwords = 0;
luaL_newmetatable(L, "rnnmeta");
luaL_setfuncs(L, l_meta_rnn, 0);
lua_pop(L, 1);
luaL_setmetatable(L, "rnnmeta");
return 1;
}
static int l_rnn_enumname(lua_State *L)
{
struct rnn *rnn = lua_touserdata(L, 1);
const char *name = lua_tostring(L, 2);
uint32_t val = (uint32_t)lua_tonumber(L, 3);
lua_pushstring(L, rnn_enumname(rnn, name, val));
return 1;
}
static int l_rnn_regname(lua_State *L)
{
struct rnn *rnn = lua_touserdata(L, 1);
uint32_t regbase = (uint32_t)lua_tonumber(L, 2);
lua_pushstring(L, rnn_regname(rnn, regbase, 1));
return 1;
}
static int l_rnn_regval(lua_State *L)
{
struct rnn *rnn = lua_touserdata(L, 1);
uint32_t regbase = (uint32_t)lua_tonumber(L, 2);
uint32_t regval = (uint32_t)lua_tonumber(L, 3);
struct rnndecaddrinfo *info = rnn_reginfo(rnn, regbase);
char *decoded;
if (info && info->typeinfo) {
decoded = rnndec_decodeval(rnn->vc, info->typeinfo, regval);
} else {
asprintf(&decoded, "%08x", regval);
}
lua_pushstring(L, decoded);
free(decoded);
if (info) {
free(info->name);
free(info);
}
return 1;
}
static const struct luaL_Reg l_rnn[] = {
{"init", l_rnn_init},
{"enumname", l_rnn_enumname},
{"regname", l_rnn_regname},
{"regval", l_rnn_regval},
{NULL, NULL} /* sentinel */
};
/* Expose the register state to script enviroment as a "regs" library:
*/
static int l_reg_written(lua_State *L)
{
uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
lua_pushnumber(L, reg_written(regbase));
return 1;
}
static int l_reg_lastval(lua_State *L)
{
uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
lua_pushnumber(L, reg_lastval(regbase));
return 1;
}
static int l_reg_val(lua_State *L)
{
uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
lua_pushnumber(L, reg_val(regbase));
return 1;
}
static const struct luaL_Reg l_regs[] = {
{"written", l_reg_written},
{"lastval", l_reg_lastval},
{"val", l_reg_val},
{NULL, NULL} /* sentinel */
};
/* Expose API to lookup snapshot buffers:
*/
uint64_t gpubaseaddr(uint64_t gpuaddr);
unsigned hostlen(uint64_t gpuaddr);
/* given address, return base-address of buffer: */
static int l_bo_base(lua_State *L)
{
uint64_t addr = (uint64_t)lua_tonumber(L, 1);
lua_pushnumber(L, gpubaseaddr(addr));
return 1;
}
/* given address, return the remaining size of the buffer: */
static int l_bo_size(lua_State *L)
{
uint64_t addr = (uint64_t)lua_tonumber(L, 1);
lua_pushnumber(L, hostlen(addr));
return 1;
}
static const struct luaL_Reg l_bos[] = {
{"base", l_bo_base},
{"size", l_bo_size},
{NULL, NULL} /* sentinel */
};
static void openlib(const char *lib, const luaL_Reg *reg)
{
lua_newtable(L);
luaL_setfuncs(L, reg, 0);
lua_setglobal(L, lib);
}
/* called at start to load the script: */
int script_load(const char *file)
{
int ret;
assert(!L);
L = luaL_newstate();
luaL_openlibs(L);
openlib("bos", l_bos);
openlib("regs", l_regs);
openlib("rnn", l_rnn);
ret = luaL_loadfile(L, file);
if (ret)
error("%s\n");
ret = lua_pcall(L, 0, LUA_MULTRET, 0);
if (ret)
error("%s\n");
return 0;
}
/* called at start of each cmdstream file: */
void script_start_cmdstream(const char *name)
{
if (!L)
return;
lua_getglobal(L, "start_cmdstream");
/* if no handler just ignore it: */
if (!lua_isfunction(L, -1)) {
lua_pop(L, 1);
return;
}
lua_pushstring(L, name);
/* do the call (1 arguments, 0 result) */
if (lua_pcall(L, 1, 0, 0) != 0)
error("error running function `f': %s\n");
}
/* called at each DRAW_INDX, calls script drawidx fxn to process
* the current state
*/
void script_draw(const char *primtype, uint32_t nindx)
{
if (!L)
return;
lua_getglobal(L, "draw");
/* if no handler just ignore it: */
if (!lua_isfunction(L, -1)) {
lua_pop(L, 1);
return;
}
lua_pushstring(L, primtype);
lua_pushnumber(L, nindx);
/* do the call (2 arguments, 0 result) */
if (lua_pcall(L, 2, 0, 0) != 0)
error("error running function `f': %s\n");
}
static int l_rnn_meta_dom_index(lua_State *L)
{
struct rnn *rnn = lua_touserdata(L, 1);
uint32_t offset = (uint32_t)lua_tonumber(L, 2);
struct rnndelem *elem;
/* TODO might be nicer if the arg isn't a number, to search the domain
* for matching bitfields.. so that the script could do something like
* 'pkt.WIDTH' insteadl of 'pkt[1].WIDTH', ie. not have to remember the
* offset of the dword containing the bitfield..
*/
elem = rnn_regoff(rnn, offset);
if (!elem)
return 0;
return l_rnn_etype(L, rnn, elem, elem->offset);
}
/*
* A wrapper object for rnndomain based decoding of an array of dwords
* (ie. for pm4 packet decoding). Mostly re-uses the register-value
* decoding for the individual dwords and bitfields.
*/
static int l_rnn_meta_dom_gc(lua_State *L)
{
// TODO
//struct rnn *rnn = lua_touserdata(L, 1);
//rnn_deinit(rnn);
return 0;
}
static const struct luaL_Reg l_meta_rnn_dom[] = {
{"__index", l_rnn_meta_dom_index},
{"__gc", l_rnn_meta_dom_gc},
{NULL, NULL} /* sentinel */
};
/* called to general pm4 packet decoding, such as texture/sampler state
*/
void script_packet(uint32_t *dwords, uint32_t sizedwords,
struct rnn *rnn, struct rnndomain *dom)
{
if (!L)
return;
lua_getglobal(L, dom->name);
/* if no handler for the packet, just ignore it: */
if (!lua_isfunction(L, -1)) {
lua_pop(L, 1);
return;
}
struct rnndec *rnndec = lua_newuserdata(L, sizeof(*rnndec));
rnndec->base = *rnn;
rnndec->base.dom[0] = dom;
rnndec->base.dom[1] = NULL;
rnndec->dwords = dwords;
rnndec->sizedwords = sizedwords;
luaL_newmetatable(L, "rnnmetadom");
luaL_setfuncs(L, l_meta_rnn_dom, 0);
lua_pop(L, 1);
luaL_setmetatable(L, "rnnmetadom");
lua_pushnumber(L, sizedwords);
if (lua_pcall(L, 2, 0, 0) != 0)
error("error running function `f': %s\n");
}
/* helper to call fxn that takes and returns void: */
static void simple_call(const char *name)
{
if (!L)
return;
lua_getglobal(L, name);
/* if no handler just ignore it: */
if (!lua_isfunction(L, -1)) {
lua_pop(L, 1);
return;
}
/* do the call (0 arguments, 0 result) */
if (lua_pcall(L, 0, 0, 0) != 0)
error("error running function `f': %s\n");
}
/* called at end of each cmdstream file: */
void script_end_cmdstream(void)
{
simple_call("end_cmdstream");
}
/* called at start of submit/issueibcmds: */
void script_start_submit(void)
{
simple_call("start_submit");
}
/* called at end of submit/issueibcmds: */
void script_end_submit(void)
{
simple_call("end_submit");
}
/* called after last cmdstream file: */
void script_finish(void)
{
if (!L)
return;
simple_call("finish");
lua_close(L);
L = NULL;
}

View File

@ -0,0 +1,76 @@
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
/*
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
*
* 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, sublicense,
* 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 NONINFRINGEMENT. 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.
*
* Authors:
* Rob Clark <robclark@freedesktop.org>
*/
#ifndef SCRIPT_H_
#define SCRIPT_H_
#include <stdint.h>
// XXX make script support optional
#define ENABLE_SCRIPTING 1
#ifdef ENABLE_SCRIPTING
/* called at start to load the script: */
int script_load(const char *file);
/* called at start of each cmdstream file: */
void script_start_cmdstream(const char *name);
/* called at each DRAW_INDX, calls script drawidx fxn to process
* the current state
*/
__attribute__((weak))
void script_draw(const char *primtype, uint32_t nindx);
struct rnn;
struct rnndomain;
__attribute__((weak))
void script_packet(uint32_t *dwords, uint32_t sizedwords,
struct rnn *rnn, struct rnndomain *dom);
/* maybe at some point it is interesting to add additional script
* hooks for CP_EVENT_WRITE, etc?
*/
/* called at end of each cmdstream file: */
void script_end_cmdstream(void);
void script_start_submit(void);
void script_end_submit(void);
/* called after last cmdstream file: */
void script_finish(void);
#else
// TODO no-op stubs..
#endif
#endif /* SCRIPT_H_ */

View File

@ -0,0 +1,178 @@
-- A script that compares a set of equivalent cmdstream captures from
-- various generations, looking for equivalencies between registers.
--
-- This would be run across a group of similar tests for various
-- generations, for example:
--
-- cffdump --script scripts/analyze.lua a320/quad-flat-*.rd a420/quad-flat-*.rd
--
-- This is done by comparing unique register values. Ie. for each
-- generation, find the set of registers that have different values
-- between equivalent draw calls.
local posix = require "posix"
io.write("Analyzing Data...\n")
-- results - table structure:
-- * [gpuname] - gpu
-- * tests
-- * [testname] - current test
-- * draws
-- * [1..n] - the draws
-- * primtype - the primitive type
-- * regs - table of values for draw
-- * [regbase] - regval
-- * regvals - table of unique values across all draws
-- * [regbase]
-- * [regval] - list of test names
-- * [1..n] - testname "." didx
local results = {}
local test = nil
local gpuname = nil
local testname = nil
-- srsly, no sparse table size() op?
function tblsz(tbl)
local n = 0;
for k,v in pairs(tbl) do
n = n + 1
end
return n
end
function start_cmdstream(name)
testname = posix.basename(name)
gpuname = posix.basename(posix.dirname(name))
--io.write("START: gpuname=" .. gpuname .. ", testname=" .. testname .. "\n");
local gpu = results[gpuname]
if gpu == nil then
gpu = {["tests"] = {}, ["regvals"] = {}}
results[gpuname] = gpu
end
test = {["draws"] = {}}
gpu["tests"][testname] = test
end
function draw(primtype, nindx)
-- RECTLIST is only used internally.. we want to ignore it for
-- now, although it could potentially be interesting to track
-- these separately (separating clear/restore/resolve) just to
-- figure out which registers are used for which..
if primtype == "DI_PT_RECTLIST" then
return
end
local regtbl = {}
local draw = {["primtype"] = primtype, ["regs"] = regtbl}
local didx = tblsz(test["draws"])
test["draws"][didx] = draw
-- populate current regs. For now just consider ones that have
-- been written.. maybe we need to make that configurable in
-- case it filters out too many registers.
for regbase=0,0xffff do
if regs.written(regbase) ~= 0 then
local regval = regs.val(regbase)
-- track reg vals per draw:
regtbl[regbase] = regval
-- also track which reg vals appear in which tests:
local uniq_regvals = results[gpuname]["regvals"][regbase]
if uniq_regvals == nil then
uniq_regvals = {}
results[gpuname]["regvals"][regbase] = uniq_regvals;
end
local drawlist = uniq_regvals[regval]
if drawlist == nil then
drawlist = {}
uniq_regvals[regval] = drawlist
end
table.insert(drawlist, testname .. "." .. didx)
end
end
-- TODO maybe we want to whitelist a few well known regs, for the
-- convenience of the code that runs at the end to analyze the data?
-- TODO also would be useful to somehow capture CP_SET_BIN..
end
function end_cmdstream()
test = nil
gpuname = nil
testname = nil
end
function print_draws(gpuname, gpu)
io.write(" " .. gpuname .. "\n")
for testname,test in pairs(gpu["tests"]) do
io.write(" " .. testname .. ", draws=" .. #test["draws"] .. "\n")
for didx,draw in pairs(test["draws"]) do
io.write(" " .. didx .. ": " .. draw["primtype"] .. "\n")
end
end
end
-- sort and concat a list of draw names to form a key which can be
-- compared to other drawlists to check for equality
-- TODO maybe we instead want a scheme that allows for some fuzzyness
-- in the matching??
function drawlistname(drawlist)
local name = nil
for idx,draw in pairs(drawlist) do
if name == nil then
name = draw
else
name = name .. ":" .. draw
end
end
return name
end
local rnntbl = {}
function dumpmatches(name)
for gpuname,gpu in pairs(results) do
local r = rnntbl[gpuname]
if r == nil then
io.write("loading rnn database: \n" .. gpuname)
r = rnn.init(gpuname)
rnntbl[gpuname] = r
end
for regbase,regvals in pairs(gpu["regvals"]) do
for regval,drawlist in pairs(regvals) do
local name2 = drawlistname(drawlist)
if name == name2 then
io.write(string.format(" %s:%s:\t%08x %s\n",
gpuname, rnn.regname(r, regbase),
regval, rnn.regval(r, regbase, regval)))
end
end
end
end
end
function finish()
-- drawlistnames that we've already dumped:
local dumped = {}
for gpuname,gpu in pairs(results) do
-- print_draws(gpuname, gpu)
for regbase,regvals in pairs(gpu["regvals"]) do
for regval,drawlist in pairs(regvals) do
local name = drawlistname(drawlist)
if dumped[name] == nil then
io.write("\n" .. name .. ":\n")
dumpmatches(name)
dumped[name] = 1
end
end
end
end
end

View File

@ -0,0 +1,413 @@
-- Parse cmdstream dump and analyse blits and batches
--local posix = require "posix"
function printf(fmt, ...)
return io.write(string.format(fmt, ...))
end
function dbg(fmt, ...)
--printf(fmt, ...)
end
printf("Analyzing Data...\n")
local r = rnn.init("a630")
-- Each submit, all draws will target the same N MRTs:
local mrts = {}
local allmrts = {} -- includes historical render targets
function push_mrt(fmt, w, h, samples, base, flag, gmem)
dbg("MRT: %s %ux%u 0x%x\n", fmt, w, h, base)
local mrt = {}
mrt.format = fmt
mrt.w = w
mrt.h = h
mrt.samples = samples
mrt.base = base
mrt.flag = flag
mrt.gmem = gmem
mrts[base] = mrt
allmrts[base] = mrt
end
-- And each each draw will read from M sources/textures:
local sources = {}
function push_source(fmt, w, h, samples, base, flag)
dbg("SRC: %s %ux%u 0x%x\n", fmt, w, h, base)
local source = {}
source.format = fmt
source.w = w
source.h = h
source.samples = samples
source.base = base
source.flag = flag
sources[base] = source
end
local binw
local binh
local nbins
local blits = 0
local draws = 0
local drawmode
local cleared
local restored
local resolved
local nullbatch
local depthtest
local depthwrite
local stenciltest
local stencilwrite
function start_cmdstream(name)
printf("Parsing %s\n", name)
end
function reset()
dbg("reset\n")
mrts = {}
sources = {}
draws = 0
blits = 0
cleared = {}
restored = {}
resolved = {}
depthtest = false
depthwrite = false
stenciltest = false
stencilwrite = false
drawmode = Nil
end
function start_submit()
dbg("start_submit\n")
reset()
nullbatch = true
end
function finish()
dbg("finish\n")
printf("\n")
-- TODO we get false-positives for 'NULL BATCH!' because we don't have
-- a really good way to differentiate between submits and cmds. Ie.
-- with growable cmdstream, and a large # of tiles, IB1 can get split
-- across multiple buffers. Since we ignore GMEM draws for window-
-- offset != 0,0, the later cmds will appear as null batches
if draws == 0 and blits == 0 then
if nullbatch then
printf("NULL BATCH!\n");
end
return
end
if draws > 0 then
printf("Batch:\n")
printf("-------\n")
printf(" # of draws: %u\n", draws)
printf(" mode: %s\n", drawmode)
if drawmode == "RM6_GMEM" then
printf(" bin size: %ux%u (%u bins)\n", binw, binh, nbins)
end
if depthtest or depthwrite then
printf(" ")
if depthtest then
printf("DEPTHTEST ")
end
if depthwrite then
printf("DEPTHWRITE")
end
printf("\n")
end
if stenciltest or stencilwrite then
printf(" ")
if stenciltest then
printf("STENCILTEST ")
end
if stencilwrite then
printf("STENCILWRITE")
end
printf("\n")
end
else
printf("Blit:\n")
printf("-----\n")
end
for base,mrt in pairs(mrts) do
printf(" MRT[0x%x:0x%x]:\t%ux%u\t\t%s (%s)", base, mrt.flag, mrt.w, mrt.h, mrt.format, mrt.samples)
if drawmode == "RM6_GMEM" then
if cleared[mrt.gmem] then
printf("\tCLEARED")
end
if restored[mrt.gmem] then
printf("\tRESTORED")
end
if resolved[mrt.gmem] then
printf("\tRESOLVED")
end
else
if cleared[mrt.base] then
printf("\tCLEARED")
end
end
printf("\n")
end
function print_source(source)
printf(" SRC[0x%x:0x%x]:\t%ux%u\t\t%s (%s)\n", source.base, source.flag, source.w, source.h, source.format, source.samples)
end
for base,source in pairs(sources) do
-- only show sources that have been previously rendered to, other
-- textures are less interesting. Possibly this should be an
-- option somehow
if draws < 10 then
print_source(source)
elseif allmrts[base] or draws == 0 then
print_source(source)
elseif source.flag and allmrts[source.flag] then
print_source(source)
end
end
reset()
end
function end_submit()
dbg("end_submit\n")
finish()
end
-- Track the current mode:
local mode = ""
function CP_SET_MARKER(pkt, size)
mode = pkt[0].MARKER
dbg("mode: %s\n", mode)
end
function CP_EVENT_WRITE(pkt, size)
if tostring(pkt[0].EVENT) ~= "BLIT" then
return
end
nullbatch = false
local m = tostring(mode)
if m == "RM6_GMEM" then
-- either clear or restore:
if r.RB_BLIT_INFO.CLEAR_MASK == 0 then
restored[r.RB_BLIT_BASE_GMEM] = 1
else
cleared[r.RB_BLIT_BASE_GMEM] = 1
end
-- push_mrt() because we could have GMEM
-- passes with only a clear and no draws:
local flag = 0
local sysmem = 0;
-- try to match up the GMEM addr with the MRT/DEPTH state,
-- to avoid relying on RB_BLIT_DST also getting written:
for n = 0,r.RB_FS_OUTPUT_CNTL1.MRT-1 do
if r.RB_MRT[n].BASE_GMEM == r.RB_BLIT_BASE_GMEM then
sysmem = r.RB_MRT[n].BASE_LO | (r.RB_MRT[n].BASE_HI << 32)
flag = r.RB_MRT_FLAG_BUFFER[n].ADDR_LO | (r.RB_MRT_FLAG_BUFFER[n].ADDR_HI << 32)
break
end
end
if sysmem == 0 and r.RB_BLIT_BASE_GMEM == r.RB_DEPTH_BUFFER_BASE_GMEM then
sysmem = r.RB_DEPTH_BUFFER_BASE_LO | (r.RB_DEPTH_BUFFER_BASE_HI << 32)
flag = r.RB_DEPTH_FLAG_BUFFER_BASE_LO | (r.RB_DEPTH_FLAG_BUFFER_BASE_HI << 32)
end
--NOTE this can get confused by previous blits:
--if sysmem == 0 then
-- -- fallback:
-- sysmem = r.RB_BLIT_DST_LO | (r.RB_BLIT_DST_HI << 32)
-- flag = r.RB_BLIT_FLAG_DST_LO | (r.RB_BLIT_FLAG_DST_HI << 32)
--end
if not r.RB_BLIT_DST_INFO.FLAGS then
flag = 0
end
-- TODO maybe just emit RB_BLIT_DST_LO/HI for clears.. otherwise
-- we get confused by stale values in registers.. not sure
-- if this is a problem w/ blob
push_mrt(r.RB_BLIT_DST_INFO.COLOR_FORMAT,
r.RB_BLIT_SCISSOR_BR.X + 1,
r.RB_BLIT_SCISSOR_BR.Y + 1,
r.RB_BLIT_DST_INFO.SAMPLES,
sysmem,
flag,
r.RB_BLIT_BASE_GMEM)
elseif m == "RM6_RESOLVE" then
resolved[r.RB_BLIT_BASE_GMEM] = 1
else
printf("I am confused!!!\n")
end
end
function A6XX_TEX_CONST(pkt, size)
push_source(pkt[0].FMT,
pkt[1].WIDTH, pkt[1].HEIGHT,
pkt[0].SAMPLES,
pkt[4].BASE_LO | (pkt[5].BASE_HI << 32),
pkt[7].FLAG_LO | (pkt[8].FLAG_HI << 32))
end
function handle_blit()
-- blob sometimes uses CP_BLIT for resolves, so filter those out:
-- TODO it would be nice to not hard-code GMEM addr:
-- TODO I guess the src can be an offset from GMEM addr..
if r.SP_PS_2D_SRC_LO == 0x100000 and not r.RB_2D_BLIT_CNTL.SOLID_COLOR then
resolved[0] = 1
return
end
if draws > 0 then
finish()
end
reset()
drawmode = "BLIT"
-- This kinda assumes that we are doing full img blits, which is maybe
-- Not completely legit. We could perhaps instead just track pitch and
-- size/pitch?? Or maybe the size doesn't matter much
push_mrt(r.RB_2D_DST_INFO.COLOR_FORMAT,
r.GRAS_2D_DST_BR.X + 1,
r.GRAS_2D_DST_BR.Y + 1,
"MSAA_ONE",
r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32),
r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32),
-1)
if r.RB_2D_BLIT_CNTL.SOLID_COLOR then
dbg("CLEAR=%x\n", r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32))
cleared[r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)] = 1
else
push_source(r.SP_2D_SRC_FORMAT.COLOR_FORMAT,
r.GRAS_2D_SRC_BR_X.X + 1,
r.GRAS_2D_SRC_BR_Y.Y + 1,
"MSAA_ONE",
r.SP_PS_2D_SRC_LO | (r.SP_PS_2D_SRC_HI << 32),
r.SP_PS_2D_SRC_FLAGS_LO | (r.SP_PS_2D_SRC_FLAGS_HI << 32))
end
blits = blits + 1
finish()
end
function valid_transition(curmode, newmode)
if curmode == "RM6_BINNING" and newmode == "RM6_GMEM" then
return true
end
if curmode == "RM6_GMEM" and newmode == "RM6_RESOLVE" then
return true
end
return false
end
function draw(primtype, nindx)
dbg("draw: %s (%s)\n", primtype, mode)
nullbatch = false
if primtype == "BLIT_OP_SCALE" then
handle_blit()
return
elseif primtype == "EVENT:BLIT" then
return
end
local m = tostring(mode)
-- detect changes in drawmode which indicate a different
-- pass.. BINNING->GMEM means same pass, but other
-- transitions mean different pass:
if drawmode and m ~= drawmode then
dbg("%s -> %s transition\n", drawmode, m)
if not valid_transition(drawmode, m) then
dbg("invalid transition, new render pass!\n")
finish()
reset()
end
end
if m ~= "RM6_GMEM" and m ~= "RM6_BYPASS" then
if m == "RM6_BINNING" then
drawmode = m
return
end
if m == "RM6_RESOLVE" and primtype == "EVENT:BLIT" then
return
end
printf("unknown MODE %s for primtype %s\n", m, primtype)
return
end
-- Only count the first tile for GMEM mode to avoid counting
-- each draw for each tile
if m == "RM6_GMEM" then
if r.RB_WINDOW_OFFSET.X ~= 0 or r.RB_WINDOW_OFFSET.Y ~= 0 then
return
end
end
drawmode = m
local render_components = {}
render_components[0] = r.RB_RENDER_COMPONENTS.RT0;
render_components[1] = r.RB_RENDER_COMPONENTS.RT1;
render_components[2] = r.RB_RENDER_COMPONENTS.RT2;
render_components[3] = r.RB_RENDER_COMPONENTS.RT3;
render_components[4] = r.RB_RENDER_COMPONENTS.RT4;
render_components[5] = r.RB_RENDER_COMPONENTS.RT5;
render_components[6] = r.RB_RENDER_COMPONENTS.RT6;
render_components[7] = r.RB_RENDER_COMPONENTS.RT7;
for n = 0,r.RB_FS_OUTPUT_CNTL1.MRT-1 do
if render_components[n] ~= 0 then
push_mrt(r.RB_MRT[n].BUF_INFO.COLOR_FORMAT,
r.GRAS_SC_SCREEN_SCISSOR[0].BR.X + 1,
r.GRAS_SC_SCREEN_SCISSOR[0].BR.Y + 1,
r.RB_MSAA_CNTL.SAMPLES,
r.RB_MRT[n].BASE_LO | (r.RB_MRT[n].BASE_HI << 32),
r.RB_MRT_FLAG_BUFFER[n].ADDR_LO | (r.RB_MRT_FLAG_BUFFER[n].ADDR_HI << 32),
r.RB_MRT[n].BASE_GMEM)
end
end
local depthbase = r.RB_DEPTH_BUFFER_BASE_LO |
(r.RB_DEPTH_BUFFER_BASE_HI << 32)
if depthbase ~= 0 then
push_mrt(r.RB_DEPTH_BUFFER_INFO.DEPTH_FORMAT,
r.GRAS_SC_SCREEN_SCISSOR[0].BR.X + 1,
r.GRAS_SC_SCREEN_SCISSOR[0].BR.Y + 1,
r.RB_MSAA_CNTL.SAMPLES,
depthbase,
r.RB_DEPTH_FLAG_BUFFER_BASE_LO | (r.RB_DEPTH_FLAG_BUFFER_BASE_HI << 32),
r.RB_DEPTH_BUFFER_BASE_GMEM)
end
if r.RB_DEPTH_CNTL.Z_WRITE_ENABLE then
depthwrite = true
end
if r.RB_DEPTH_CNTL.Z_ENABLE then
depthtest = true
end
-- clearly 0 != false.. :-/
if r.RB_STENCILWRMASK.WRMASK ~= 0 then
stencilwrite = true
end
if r.RB_STENCIL_CONTROL.STENCIL_ENABLE then
stenciltest = true
end
-- TODO should also check for stencil buffer for z32+s8 case
if m == "RM6_GMEM" then
binw = r.VSC_BIN_SIZE.WIDTH
binh = r.VSC_BIN_SIZE.HEIGHT
nbins = r.VSC_BIN_COUNT.NX * r.VSC_BIN_COUNT.NY
end
draws = draws + 1
end

View File

@ -0,0 +1,76 @@
-- Parse cmdstream dump and check for common errors
-- 1) Check for overflowing HLSQ_xS_CNTL.CONSTLEN
-- 2) Check for constant uploades that overwrite each other. The
-- range checking is reset on each draw, since it is a valid
-- use-case to do partial constant upload. But if we see two
-- CP_LOAD_STATE* that overwrite the same range of constants
-- within the same draw, that is almost certainly unintentional.
--
-- TODO add more checks
-- TODO maybe some parts could be shared across
-- different generations
--local posix = require "posix"
function printf(fmt, ...)
return io.write(string.format(fmt, ...))
end
function dbg(fmt, ...)
--printf(fmt, ...)
end
stages = {
"SB6_VS_SHADER",
"SB6_HS_SHADER",
"SB6_DS_SHADER",
"SB6_GS_SHADER",
"SB6_FS_SHADER",
"SB6_CS_SHADER",
}
-- maps shader stage to HLSQ_xS_CNTL register name:
cntl_regs = {
["SB6_VS_SHADER"] = "HLSQ_VS_CNTL",
["SB6_HS_SHADER"] = "HLSQ_HS_CNTL",
["SB6_DS_SHADER"] = "HLSQ_DS_CNTL",
["SB6_GS_SHADER"] = "HLSQ_GS_CNTL",
["SB6_FS_SHADER"] = "HLSQ_FS_CNTL",
["SB6_CS_SHADER"] = "HLSQ_CS_CNTL",
}
-- initialize constant updated ranges:
-- constranges[stagename] -> table of offsets that have been uploaded
constranges = {}
function reset_constranges()
for i,stage in ipairs(stages) do
constranges[stage] = {}
end
end
reset_constranges()
printf("Checking cmdstream...\n")
local r = rnn.init("a630")
function draw(primtype, nindx)
printf("draw!\n")
-- reset ranges of uploaded consts on each draw:
reset_constranges()
end
function CP_LOAD_STATE6(pkt, size)
if tostring(pkt[0].STATE_TYPE) ~= "ST6_CONSTANTS" then
return
end
dbg("got CP_LOAD_STATE6\n")
stage = tostring(pkt[0].STATE_BLOCK)
max = pkt[0].DST_OFF + pkt[0].NUM_UNIT
cntl_reg = cntl_regs[stage]
dbg("looking for %s.. max=%d vs %d\n", cntl_reg, max, r[cntl_reg].CONSTLEN)
if max > r[cntl_reg].CONSTLEN then
printf("ERROR: invalid max constant offset for stage %s: %d vs %d\n", stage, max, r[cntl_reg].CONSTLEN)
end
end

View File

@ -0,0 +1,31 @@
io.write("HELLO WORLD\n")
r = rnn.init("a630")
function start_cmdstream(name)
io.write("START: " .. name .. "\n")
end
function draw(primtype, nindx)
io.write("DRAW: " .. primtype .. ", " .. nindx .. "\n")
-- io.write("GRAS_CL_VPORT_XOFFSET: " .. r.GRAS_CL_VPORT_XOFFSET .. "\n")
io.write("RB_MRT[0].CONTROL.ROP_CODE: " .. r.RB_MRT[0].CONTROL.ROP_CODE .. "\n")
io.write("SP_VS_OUT[0].A_COMPMASK: " .. r.SP_VS_OUT[0].A_COMPMASK .. "\n")
--io.write("RB_DEPTH_CONTROL.Z_ENABLE: " .. tostring(r.RB_DEPTH_CONTROL.Z_ENABLE) .. "\n")
io.write("0x2280: written=" .. regs.written(0x2280) .. ", lastval=" .. regs.lastval(0x2280) .. ", val=" .. regs.val(0x2280) .. "\n")
end
function A6XX_TEX_CONST(pkt, size)
io.write("\n-------- " .. size .. "\n")
io.write("-------- w=" .. pkt[1].WIDTH .. ", h=" .. pkt[1].HEIGHT .. "\n")
io.write("\n");
end
function end_cmdstream()
io.write("END\n")
end
function finish()
io.write("FINISH\n")
end

View File

@ -0,0 +1,137 @@
-- Parse logs from test-quad-textured-3d.c to exctract layer/level
-- offsets
--
-- We figure out the offsets from blits, but there may be some
-- unrelated blits. So just save all of them until we find the
-- texture state for the 3d texture. This gives us the base
-- address, and the miplevel #0 width/height/depth. Then work
-- backwards from there finding the blits to the same dst buffer
-- and deducing the miplevel from the minified dimensions
local posix = require "posix"
io.write("Analyzing Data...\n")
local allblits = {}
local nallblits = 0
local r = rnn.init("a630")
function minify(val, lvls)
val = val >> lvls
if val < 1 then
return 1
end
return val
end
function printf(fmt, ...)
return io.write(string.format(fmt, ...))
end
function start_cmdstream(name)
io.write("Parsing " .. name .. "\n")
allblits = {}
nallblits = 0
end
function draw(primtype, nindx)
if primtype ~= "BLIT_OP_SCALE" then
return
end
-- Just in case, filter out anything that isn't starting
-- at 0,0
if r.GRAS_2D_DST_TL.X ~= 0 or r.GRAS_2D_DST_TL.Y ~= 0 then
return
end
local blit = {}
blit.width = r.GRAS_2D_DST_BR.X + 1
blit.height = r.GRAS_2D_DST_BR.Y + 1
blit.pitch = r.RB_2D_DST_SIZE.PITCH
blit.addr = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
blit.base = bos.base(blit.addr)
blit.endaddr = 0 -- filled in later
--printf("Found blit: 0x%x (0x%x)\n", blit.addr, blit.base)
allblits[nallblits] = blit
nallblits = nallblits + 1
end
function A6XX_TEX_CONST(pkt, size)
-- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
-- are looking for
if pkt[5].DEPTH <= 1 then
return
end
local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
local width0 = pkt[1].WIDTH
local height0 = pkt[1].HEIGHT
local depth0 = pkt[5].DEPTH
printf("Found texture state: %ux%ux%u (MIN_LAYERSZ=0x%x)\n",
width0, height0, depth0, pkt[3].MIN_LAYERSZ)
-- Note that in some case the texture has some extra page or so
-- at the beginning:
local basebase = bos.base(base)
printf("base: 0x%x (0x%x)\n", base, basebase)
-- see if we can find the associated blits.. The blob always seems to
-- start from the lower (larger) mipmap levels and layers, so we don't
-- need to sort by dst address. Also, while we are at it, fill in the
-- end-addr (at least for everything but the last blit)
local blits = {}
local nblits = 0
local lastblit = nil
for n = 0,nallblits-1 do
local blit = allblits[n]
--printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
if blit.base == basebase and blit.addr >= base then
blits[nblits] = blit
nblits = nblits + 1
if lastblit then
lastblit.endaddr = blit.addr
end
lastblit = blit
end
end
-- now go thru the relevant blits and print out interesting details
local level = 0
local layer = 0
local w = width0 -- track current width/height to detect changing
local h = height0 -- mipmap level
for n = 0,nblits-1 do
local blit = blits[n]
--printf("%u: %ux%u, addr=%x\n", n, blit.width, blit.height, blit.addr)
if w ~= blit.width or h ~= blit.height then
level = level + 1
layer = 0
if blit.width ~= minify(w, 1) or blit.height ~= minify(h, 1) then
printf("I am confused! %ux%u vs %ux%u\n", blit.width, blit.height, minify(w, 1), minify(h, 1))
printf("addr=%x\n", blit.addr)
--return
end
w = blit.width
h = blit.height
end
printf("level=%u, layer=%u, sz=%ux%u, pitch=%u, offset=0x%x, addr=%x",
level, layer, w, h, blit.pitch, blit.addr - base, blit.addr)
if blit.endaddr ~= 0 then
local layersz = blit.endaddr - blit.addr
local alignedheight = layersz / blit.pitch
printf(", layersz=0x%x, alignedheight=%f", layersz, alignedheight)
end
printf("\n")
layer = layer + 1
end
printf("\n\n")
end

View File

@ -0,0 +1,200 @@
-- Parse logs from https://github.com/freedreno/freedreno/
-- test-texturator.c to generate a src/freedreno/fdl/fd5_layout_test.c
-- block. We figure out the offsets from blits, but there may be some
-- unrelated blits. So just save all of them until we find the
-- texture state. This gives us the base address, and the miplevel #0
-- width/height/depth. Then work backwards from there finding the
-- blits to the same dst buffer and deducing the miplevel from the
-- minified dimensions
local posix = require "posix"
io.write("Analyzing Data...\n")
local r = rnn.init("a530")
local found_tex = 0
local allblits = {}
local nallblits = 0
function get_first_blit(base, width, height)
local first_blit = nil
for n = 0,nallblits-1 do
local blit = allblits[n]
if blit.base == base and blit.width == width and blit.height == height then
if not first_blit or blit.addr < first_blit.addr then
first_blit = blit
end
end
end
return first_blit
end
function minify(val, lvls)
val = val >> lvls
if val < 1 then
return 1
end
return val
end
function printf(fmt, ...)
return io.write(string.format(fmt, ...))
end
function start_cmdstream(name)
io.write("Parsing " .. name .. "\n")
allblits = {}
nallblits = 0
end
-- Record texture upload blits done through CP_EVENT_WRITE
function CP_EVENT_WRITE(pkt, size)
if tostring(pkt[0].EVENT) ~= "BLIT" then
return
end
local blit = {}
blit.width = r.RB_RESOLVE_CNTL_2.X + 1
blit.height = r.RB_RESOLVE_CNTL_2.Y + 1
blit.pitch = r.RB_BLIT_DST_PITCH
blit.addr = r.RB_BLIT_DST_LO | (r.RB_BLIT_DST_HI << 32)
blit.base = bos.base(blit.addr)
blit.ubwc_addr = r.RB_BLIT_FLAG_DST_LO | (r.RB_BLIT_FLAG_DST_HI << 32)
blit.ubwc_base = bos.base(blit.ubwc_addr)
blit.ubwc_pitch = r.RB_BLIT_FLAG_DST_PITCH
blit.endaddr = 0 -- filled in later
printf("Found event blit: 0x%x (0x%x) %dx%d UBWC 0x%x (0x%x) tiled %s\n", blit.addr, blit.base, blit.width, blit.height, blit.ubwc_addr, blit.ubwc_base, r.RB_RESOLVE_CNTL_3.TILED)
allblits[nallblits] = blit
nallblits = nallblits + 1
end
function CP_BLIT(pkt, size)
-- Just in case, filter out anything that isn't starting
-- at 0,0
if pkt[1].SRC_X1 ~= 0 or pkt[1].SRC_Y1 ~= 0 then
return
end
local blit = {}
blit.width = pkt[2].SRC_X2 + 1
blit.height = pkt[2].SRC_Y2 + 1
blit.pitch = r.RB_2D_DST_SIZE.PITCH
blit.addr = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
blit.base = bos.base(blit.addr)
blit.ubwc_addr = r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32)
blit.ubwc_base = bos.base(blit.ubwc_addr)
blit.ubwc_pitch = r.RB_2D_DST_FLAGS_PITCH
blit.endaddr = 0 -- filled in later
printf("Found cp blit: 0x%x (0x%x) %dx%d UBWC 0x%x (0x%x) %s\n", blit.addr, blit.base, blit.width, blit.height, blit.ubwc_addr, blit.ubwc_base, r.RB_2D_DST_INFO.TILE_MODE)
allblits[nallblits] = blit
nallblits = nallblits + 1
end
function A5XX_TEX_CONST(pkt, size)
-- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
-- are looking for
local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
-- UBWC base on a5xx seems to be at the start of each miplevel, followed by pixels
-- somewhere past that.
local ubwc_base = base
local width0 = pkt[1].WIDTH
local height0 = pkt[1].HEIGHT
local depth0 = pkt[5].DEPTH
if (found_tex ~= 0) then
return
end
found_tex = 1
printf("Found texture state:\n %ux%ux%u (%s, %s, UBWC=%s)\n",
width0, height0, depth0, pkt[0].FMT, pkt[0].TILE_MODE, tostring(pkt[3].FLAG))
-- Note that in some case the texture has some extra page or so
-- at the beginning:
local basebase = bos.base(base)
printf("base: 0x%x (0x%x)\n", base, basebase)
printf("ubwcbase: 0x%x (0x%x)\n", ubwc_base, bos.base(ubwc_base))
-- see if we can find the associated blits.. The blob always seems to
-- start from the lower (larger) mipmap levels and layers, so we don't
-- need to sort by dst address. Also, while we are at it, fill in the
-- end-addr (at least for everything but the last blit)
local blits = {}
local nblits = 0
local lastblit = nil
for n = 0,nallblits-1 do
local blit = allblits[n]
--printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
if blit.base == basebase and blit.addr >= base then
blits[nblits] = blit
nblits = nblits + 1
if lastblit then
lastblit.endaddr = blit.addr
end
lastblit = blit
end
end
printf(" {\n")
printf(" .format = %s,\n", pkt[0].FMT)
if (tostring(pkt[2].TYPE) == "A5XX_TEX_3D") then
printf(" .is_3d = true,\n")
end
printf(" .layout = {\n")
printf(" .tile_mode = %s,\n", pkt[0].TILE_MODE)
printf(" .ubwc = %s,\n", tostring(pkt[3].FLAG))
if (tostring(pkt[2].TYPE) == "A5XX_TEX_3D") then
printf(" .width0 = %d, .height0 = %d, .depth0 = %d,\n", width0, height0, depth0)
else
printf(" .width0 = %d, .height0 = %d,\n", width0, height0)
end
printf(" .slices = {\n")
local w = 0
local h = 0
local level = 0
repeat
local w = minify(width0, level)
local h = minify(height0, level)
local blit = get_first_blit(basebase, w, h)
if blit then
printf(" { .offset = %d, .pitch = %u },\n",
blit.addr - base,
blit.pitch);
end
level = level + 1
until w == 1 and h == 1
printf(" },\n")
if pkt[3].FLAG then
printf(" .ubwc_slices = {\n")
level = 0
repeat
local w = minify(width0, level)
local h = minify(height0, level)
local blit = get_first_blit(basebase, w, h)
if blit then
printf(" { .offset = %d, .pitch = %u },\n",
blit.ubwc_addr - ubwc_base,
blit.ubwc_pitch);
end
level = level + 1
until w == 1 and h == 1
printf(" },\n")
end
printf(" },\n")
printf(" },\n")
printf("\n\n")
end

View File

@ -0,0 +1,179 @@
-- Parse logs from https://github.com/freedreno/freedreno/
-- test-texturator.c to generate a src/freedreno/fdl/fd6_layout_test.c
-- block. We figure out the offsets from blits, but there may be some
-- unrelated blits. So just save all of them until we find the
-- texture state. This gives us the base address, and the miplevel #0
-- width/height/depth. Then work backwards from there finding the
-- blits to the same dst buffer and deducing the miplevel from the
-- minified dimensions
local posix = require "posix"
io.write("Analyzing Data...\n")
local r = rnn.init("a630")
local found_tex = 0
local allblits = {}
local nallblits = 0
function get_first_blit(base, width, height)
local first_blit = nil
for n = 0,nallblits-1 do
local blit = allblits[n]
if blit.base == base and blit.width == width and blit.height == height then
if not first_blit or blit.addr < first_blit.addr then
first_blit = blit
end
end
end
return first_blit
end
function minify(val, lvls)
val = val >> lvls
if val < 1 then
return 1
end
return val
end
function printf(fmt, ...)
return io.write(string.format(fmt, ...))
end
function start_cmdstream(name)
io.write("Parsing " .. name .. "\n")
allblits = {}
nallblits = 0
end
function draw(primtype, nindx)
if primtype ~= "BLIT_OP_SCALE" then
return
end
-- Just in case, filter out anything that isn't starting
-- at 0,0
if r.GRAS_2D_DST_TL.X ~= 0 or r.GRAS_2D_DST_TL.Y ~= 0 then
return
end
local blit = {}
blit.width = r.GRAS_2D_DST_BR.X + 1
blit.height = r.GRAS_2D_DST_BR.Y + 1
blit.pitch = r.RB_2D_DST_SIZE.PITCH
blit.addr = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
blit.base = bos.base(blit.addr)
blit.ubwc_addr = r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32)
blit.ubwc_base = bos.base(blit.uwbc_addr)
blit.ubwc_pitch = r.RB_2D_DST_FLAGS_PITCH.PITCH
blit.endaddr = 0 -- filled in later
printf("Found blit: 0x%x (0x%x) %dx%d UBWC 0x%x (0x%x)\n", blit.addr, blit.base, blit.width, blit.height, blit.ubwc_addr, blit.ubwc_base)
allblits[nallblits] = blit
nallblits = nallblits + 1
end
function A6XX_TEX_CONST(pkt, size)
-- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
-- are looking for
local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
local ubwc_base = pkt[7].FLAG_LO | (pkt[8].FLAG_HI << 32)
local width0 = pkt[1].WIDTH
local height0 = pkt[1].HEIGHT
local depth0 = pkt[5].DEPTH
if (found_tex ~= 0) then
return
end
found_tex = 1
printf("Found texture state:\n %ux%ux%u (%s, %s, MIN_LAYERSZ=0x%x, TILE_ALL=%s, UBWC=%s FLAG_LOG2=%ux%u)\n",
width0, height0, depth0, pkt[0].FMT, pkt[0].TILE_MODE, pkt[3].MIN_LAYERSZ, tostring(pkt[3].TILE_ALL), tostring(pkt[3].FLAG), pkt[10].FLAG_BUFFER_LOGW, pkt[10].FLAG_BUFFER_LOGH)
-- Note that in some case the texture has some extra page or so
-- at the beginning:
local basebase = bos.base(base)
printf("base: 0x%x (0x%x)\n", base, basebase)
printf("ubwcbase: 0x%x (0x%x)\n", ubwc_base, bos.base(ubwc_base))
-- see if we can find the associated blits.. The blob always seems to
-- start from the lower (larger) mipmap levels and layers, so we don't
-- need to sort by dst address. Also, while we are at it, fill in the
-- end-addr (at least for everything but the last blit)
local blits = {}
local nblits = 0
local lastblit = nil
for n = 0,nallblits-1 do
local blit = allblits[n]
--printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
if blit.base == basebase and blit.addr >= base then
blits[nblits] = blit
nblits = nblits + 1
if lastblit then
lastblit.endaddr = blit.addr
end
lastblit = blit
end
end
printf(" {\n")
printf(" .format = %s,\n", pkt[0].FMT)
if (tostring(pkt[2].TYPE) == "A6XX_TEX_3D") then
printf(" .is_3d = true,\n")
end
printf(" .layout = {\n")
printf(" .tile_mode = %s,\n", pkt[0].TILE_MODE)
printf(" .ubwc = %s,\n", tostring(pkt[3].FLAG))
if (tostring(pkt[2].TYPE) == "A6XX_TEX_3D") then
printf(" .width0 = %d, .height0 = %d, .depth = %d,\n", width0, height0, depth0)
else
printf(" .width0 = %d, .height0 = %d,\n", width0, height0)
end
printf(" .slices = {\n")
local w = 0
local h = 0
local level = 0
repeat
local w = minify(width0, level)
local h = minify(height0, level)
local blit = get_first_blit(basebase, w, h)
if blit then
printf(" { .offset = %d, .pitch = %u },\n",
blit.addr - base,
blit.pitch);
end
level = level + 1
until w == 1 and h == 1
printf(" },\n")
if pkt[3].FLAG then
printf(" .ubwc_slices = {\n")
level = 0
repeat
local w = minify(width0, level)
local h = minify(height0, level)
local blit = get_first_blit(basebase, w, h)
if blit then
printf(" { .offset = %d, .pitch = %u },\n",
blit.ubwc_addr - ubwc_base,
blit.ubwc_pitch);
end
level = level + 1
until w == 1 and h == 1
printf(" },\n")
end
printf(" },\n")
printf(" },\n")
printf("\n\n")
end

204
src/freedreno/decode/util.h Normal file
View File

@ -0,0 +1,204 @@
/*
* Copyright (c) 2012-2018 Rob Clark <robdclark@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, sublicense,
* 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 NONINFRINGEMENT. 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.
*/
#ifndef __UTIL_H__
#define __UTIL_H__
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
/* old-style program binary XOR'd ascii w/ 0xff */
#ifndef ASCII_XOR
# define ASCII_XOR 0
#endif
static inline const char *tab(int lvl)
{
const char *TAB = "\t\t\t\t\t\t\t\t\0";
return &TAB[strlen(TAB) - lvl];
}
/* convert float to dword */
static inline float d2f(uint32_t d)
{
union {
float f;
uint32_t d;
} u = {
.d = d,
};
return u.f;
}
static inline void dump_hex(const void *buf, int sz)
{
uint8_t *ptr = (uint8_t *)buf;
uint8_t *end = ptr + sz;
int i = 0;
while (ptr < end) {
uint32_t d = 0;
printf((i % 8) ? " " : "\t");
d |= *(ptr++) << 0;
d |= *(ptr++) << 8;
d |= *(ptr++) << 16;
d |= *(ptr++) << 24;
printf("%08x", d);
if ((i % 8) == 7) {
printf("\n");
}
i++;
}
if (i % 8) {
printf("\n");
}
}
static inline void
dump_float(const void *buf, int sz)
{
uint8_t *ptr = (uint8_t *)buf;
uint8_t *end = ptr + sz - 3;
int i = 0;
while (ptr < end) {
uint32_t d = 0;
printf((i % 8) ? " " : "\t");
d |= *(ptr++) << 0;
d |= *(ptr++) << 8;
d |= *(ptr++) << 16;
d |= *(ptr++) << 24;
printf("%8f", d2f(d));
if ((i % 8) == 7) {
printf("\n");
}
i++;
}
if (i % 8) {
printf("\n");
}
}
#define is_ok_ascii(c) \
(isascii(c) && ((c == '\t') || !iscntrl(c)))
static inline void
clean_ascii(char *buf, int sz)
{
uint8_t *ptr = (uint8_t *)buf;
uint8_t *end = ptr + sz;
while (ptr < end) {
*(ptr++) ^= ASCII_XOR;
}
}
static inline void
dump_ascii(const void *buf, int sz)
{
uint8_t *ptr = (uint8_t *)buf;
uint8_t *end = ptr + sz;
printf("\t");
while (ptr < end) {
uint8_t c = *(ptr++) ^ ASCII_XOR;
if (c == '\n') {
printf("\n\t");
} else if (c == '\0') {
printf("\n\t-----------------------------------\n\t");
} else if (is_ok_ascii(c)) {
printf("%c", c);
} else {
printf("?");
}
}
printf("\n");
}
static inline void
dump_hex_ascii(const void *buf, int sz, int level)
{
uint8_t *ptr = (uint8_t *)buf;
uint8_t *end = ptr + sz;
uint8_t *ascii = ptr;
int i = 0;
printf("%s-----------------------------------------------\n", tab(level));
printf("%s%d (0x%x) bytes\n", tab(level), sz, sz);
while (ptr < end) {
uint32_t d = 0;
if (i % 4) {
printf(" ");
} else {
printf("%s%06x: ", tab(level), (uint32_t)(ptr - (uint8_t *)buf));
}
d |= *(ptr++) << 0;
d |= *(ptr++) << 8;
d |= *(ptr++) << 16;
d |= *(ptr++) << 24;
printf("%08x", d);
if ((i % 4) == 3) {
int j;
printf("\t|");
for (j = 0; j < 16; j++) {
uint8_t c = *(ascii++);
c ^= ASCII_XOR;
printf("%c", (isascii(c) && !iscntrl(c)) ? c : '.');
}
printf("|\n");
}
i++;
}
if (i % 4) {
for (int j = 4 - (i % 4); j > 0; j--) {
printf(" ");
}
printf("\t|");
while (ascii < end) {
uint8_t c = *(ascii++);
c ^= ASCII_XOR;
printf("%c", (isascii(c) && !iscntrl(c)) ? c : '.');
}
printf("|\n");
}
}
#endif /* __UTIL_H__ */

View File

@ -19,6 +19,7 @@
# SOFTWARE.
inc_freedreno = include_directories(['.', './registers'])
inc_freedreno_rnn = include_directories('rnn')
subdir('common')
subdir('registers')
@ -33,6 +34,7 @@ dep_libxml2 = dependency('libxml-2.0', required: false)
# Everything that depends on rnn requires (indirectly) libxml2:
if dep_libxml2.found()
subdir('rnn')
subdir('decode')
endif
if with_tools.contains('drm-shim')