freedreno/afuc: Improve jump table handling
Some a7xx firmwares have junk after the LPAC jump table, and the old method of hardcoding the location of the jumptable and the jumptable offset when assembling and disassembling also doesn't work well on a7xx where the location of the jumptable offset changes. Make jump tables explicit in the disassembly with a ".jumptbl" directive which expands to contain the contents of the jump table, make disassembly find the jump table and emit the directive there. Then add the ability for a literal to reference a label, which will be used for the jump table offset at the beginning of the firmware. The disassembler guesses when a word is actually the jump table offset and replaces it with a relocation. This restores the ability to disassemble a630_sqe.fw and a650_sqe.fw and reassemble to an identical binary without modifying the disassembly to remove the jump table. Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26771>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
; Version: 01000001
|
||||
|
||||
[01000001]
|
||||
[01000081]
|
||||
[#jumptbl]
|
||||
mov $01, 0x830 ; CP_SQE_INSTR_BASE
|
||||
mov $02, 0x2
|
||||
cwrite $01, [$00 + @REG_READ_ADDR]
|
||||
@@ -279,131 +279,5 @@ UNKN96:
|
||||
UNKN97:
|
||||
waitin
|
||||
mov $01, $data
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[00000074]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[00000048]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[00000033]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[00000025]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[00000030]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[00000022]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000002c]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[00000039]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000006b]
|
||||
[0000007f]
|
||||
[0000005e]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
[0000007f]
|
||||
jumptbl:
|
||||
.jumptbl
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
; this to avoid having to host the actual firmware, especially the disassembled
|
||||
; version, in Mesa.
|
||||
[01000001]
|
||||
[01000000]
|
||||
[#jumptbl]
|
||||
loc02:
|
||||
; packet table loading:
|
||||
mov $01, 0x0830 ; CP_SQE_INSTR_BASE
|
||||
@@ -334,3 +334,6 @@ UNKN126:
|
||||
UNKN127:
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
jumptbl:
|
||||
.jumptbl
|
||||
|
||||
@@ -126,6 +126,7 @@ typedef enum {
|
||||
OPC_BRNE,
|
||||
OPC_JUMP,
|
||||
OPC_RAW_LITERAL,
|
||||
OPC_JUMPTBL,
|
||||
} afuc_opc;
|
||||
|
||||
/**
|
||||
@@ -181,6 +182,16 @@ struct afuc_instr {
|
||||
bool peek : 1;
|
||||
};
|
||||
|
||||
/* Literal offsets are sometimes encoded as NOP instructions, which on a6xx+
|
||||
* must have a high 8 bits of 0x01.
|
||||
*/
|
||||
static inline uint32_t
|
||||
afuc_nop_literal(uint32_t x, unsigned gpuver)
|
||||
{
|
||||
assert((x >> 24) == 0);
|
||||
return gpuver < 6 ? x : x | (1 << 24);
|
||||
}
|
||||
|
||||
void print_control_reg(uint32_t id);
|
||||
void print_sqe_reg(uint32_t id);
|
||||
void print_pipe_reg(uint32_t id);
|
||||
|
||||
+43
-33
@@ -88,6 +88,8 @@ int gpuver;
|
||||
static struct afuc_instr instructions[0x4000];
|
||||
static unsigned num_instructions;
|
||||
|
||||
static unsigned instr_offset;
|
||||
|
||||
static struct asm_label labels[0x512];
|
||||
static unsigned num_labels;
|
||||
|
||||
@@ -96,6 +98,7 @@ next_instr(afuc_opc opc)
|
||||
{
|
||||
struct afuc_instr *ai = &instructions[num_instructions++];
|
||||
assert(num_instructions < ARRAY_SIZE(instructions));
|
||||
instr_offset++;
|
||||
ai->opc = opc;
|
||||
return ai;
|
||||
}
|
||||
@@ -107,10 +110,19 @@ decl_label(const char *str)
|
||||
|
||||
assert(num_labels < ARRAY_SIZE(labels));
|
||||
|
||||
label->offset = num_instructions;
|
||||
label->offset = instr_offset;
|
||||
label->label = str;
|
||||
}
|
||||
|
||||
void
|
||||
decl_jumptbl(void)
|
||||
{
|
||||
struct afuc_instr *ai = &instructions[num_instructions++];
|
||||
assert(num_instructions < ARRAY_SIZE(instructions));
|
||||
ai->opc = OPC_JUMPTBL;
|
||||
instr_offset += 0x80;
|
||||
}
|
||||
|
||||
static int
|
||||
resolve_label(const char *str)
|
||||
{
|
||||
@@ -128,6 +140,30 @@ resolve_label(const char *str)
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_jumptable(int outfd)
|
||||
{
|
||||
uint32_t jmptable[0x80] = {0};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_labels; i++) {
|
||||
struct asm_label *label = &labels[i];
|
||||
int id = afuc_pm4_id(label->label);
|
||||
|
||||
/* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
|
||||
if (id < 0) {
|
||||
if (sscanf(label->label, "UNKN%d", &id) != 1) {
|
||||
/* if still not found, must not belong in jump-table: */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
jmptable[id] = label->offset;
|
||||
}
|
||||
|
||||
write(outfd, jmptable, sizeof(jmptable));
|
||||
}
|
||||
|
||||
static void
|
||||
emit_instructions(int outfd)
|
||||
{
|
||||
@@ -185,16 +221,15 @@ emit_instructions(int outfd)
|
||||
break;
|
||||
}
|
||||
|
||||
/* special case, 2nd dword is patched up w/ # of instructions
|
||||
* (ie. offset of jmptbl)
|
||||
*/
|
||||
if (i == 1) {
|
||||
assert(ai->opc == OPC_RAW_LITERAL);
|
||||
ai->literal &= ~0xffff;
|
||||
ai->literal |= num_instructions;
|
||||
if (ai->opc == OPC_JUMPTBL) {
|
||||
emit_jumptable(outfd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ai->opc == OPC_RAW_LITERAL) {
|
||||
if (ai->label) {
|
||||
ai->literal = afuc_nop_literal(resolve_label(ai->label), gpuver);
|
||||
}
|
||||
write(outfd, &ai->literal, 4);
|
||||
continue;
|
||||
}
|
||||
@@ -218,30 +253,6 @@ parse_sqe_reg(const char *name)
|
||||
return afuc_sqe_reg(name + 1);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_jumptable(int outfd)
|
||||
{
|
||||
uint32_t jmptable[0x80] = {0};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_labels; i++) {
|
||||
struct asm_label *label = &labels[i];
|
||||
int id = afuc_pm4_id(label->label);
|
||||
|
||||
/* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
|
||||
if (id < 0) {
|
||||
if (sscanf(label->label, "UNKN%d", &id) != 1) {
|
||||
/* if still not found, must not belong in jump-table: */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
jmptable[id] = label->offset;
|
||||
}
|
||||
|
||||
write(outfd, jmptable, sizeof(jmptable));
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
@@ -314,7 +325,6 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
emit_instructions(outfd);
|
||||
emit_jumptable(outfd);
|
||||
|
||||
close(outfd);
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ struct asm_label {
|
||||
|
||||
struct afuc_instr *next_instr(afuc_opc opc);
|
||||
void decl_label(const char *str);
|
||||
void decl_jumptbl(void);
|
||||
|
||||
static inline uint32_t
|
||||
parse_reg(const char *str)
|
||||
|
||||
@@ -191,13 +191,19 @@ post_instr_cb(void *data, unsigned n, void *instr)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t jumptbl_offset = ~0;
|
||||
|
||||
/* Assume that instructions that don't match are raw data */
|
||||
static void
|
||||
no_match(FILE *out, const BITSET_WORD *bitset, size_t size)
|
||||
{
|
||||
fprintf(out, "[%08x]", bitset[0]);
|
||||
print_gpu_reg(out, bitset[0]);
|
||||
fprintf(out, "\n");
|
||||
if (jumptbl_offset != ~0 && bitset[0] == afuc_nop_literal(jumptbl_offset, gpuver)) {
|
||||
fprintf(out, "[#jumptbl]\n");
|
||||
} else {
|
||||
fprintf(out, "[%08x]", bitset[0]);
|
||||
print_gpu_reg(out, bitset[0]);
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -241,6 +247,25 @@ setup_packet_table(struct isa_decode_options *options,
|
||||
options->entrypoint_count = sizedwords;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
find_jump_table(uint32_t *instrs, uint32_t sizedwords,
|
||||
uint32_t *jmptbl, uint32_t jmptbl_size)
|
||||
{
|
||||
for (unsigned i = 0; i <= sizedwords - jmptbl_size; i++) {
|
||||
bool found = true;
|
||||
for (unsigned j = 0; j < jmptbl_size; j++) {
|
||||
if (instrs[i + j] != jmptbl[j]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
return i;
|
||||
}
|
||||
|
||||
return ~0;
|
||||
}
|
||||
|
||||
static void
|
||||
disasm(struct emu *emu)
|
||||
{
|
||||
@@ -289,6 +314,9 @@ disasm(struct emu *emu)
|
||||
|
||||
setup_packet_table(&options, emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
|
||||
|
||||
jumptbl_offset = find_jump_table(emu->instrs, sizedwords, emu->jmptbl,
|
||||
ARRAY_SIZE(emu->jmptbl));
|
||||
|
||||
/* TODO add option to emulate LPAC SQE instead: */
|
||||
if (emulator) {
|
||||
/* Start from clean slate: */
|
||||
@@ -302,7 +330,18 @@ disasm(struct emu *emu)
|
||||
}
|
||||
|
||||
/* print instructions: */
|
||||
isa_disasm(emu->instrs, sizedwords * 4, stdout, &options);
|
||||
isa_disasm(emu->instrs, MIN2(sizedwords, jumptbl_offset) * 4, stdout, &options);
|
||||
|
||||
/* print jump table */
|
||||
if (jumptbl_offset != ~0) {
|
||||
printf("jumptbl:\n");
|
||||
printf(".jumptbl\n");
|
||||
|
||||
if (jumptbl_offset + ARRAY_SIZE(emu->jmptbl) != sizedwords) {
|
||||
for (unsigned i = jumptbl_offset + ARRAY_SIZE(emu->jmptbl); i < sizedwords; i++)
|
||||
printf("[%08x]\n", emu->instrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (bv_offset) {
|
||||
printf(";\n");
|
||||
@@ -322,7 +361,19 @@ disasm(struct emu *emu)
|
||||
|
||||
uint32_t sizedwords = lpac_offset - bv_offset;
|
||||
|
||||
isa_disasm(emu->instrs, sizedwords * 4, stdout, &options);
|
||||
jumptbl_offset = find_jump_table(emu->instrs, sizedwords, emu->jmptbl,
|
||||
ARRAY_SIZE(emu->jmptbl));
|
||||
|
||||
isa_disasm(emu->instrs, MIN2(sizedwords, jumptbl_offset) * 4, stdout, &options);
|
||||
|
||||
if (jumptbl_offset != ~0) {
|
||||
printf("jumptbl:\n");
|
||||
printf(".jumptbl\n");
|
||||
if (jumptbl_offset + ARRAY_SIZE(emu->jmptbl) != sizedwords) {
|
||||
for (unsigned i = jumptbl_offset + ARRAY_SIZE(emu->jmptbl); i < sizedwords; i++)
|
||||
printf("[%08x]\n", emu->instrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
emu->instrs -= bv_offset;
|
||||
emu->sizedwords += bv_offset;
|
||||
@@ -344,7 +395,19 @@ disasm(struct emu *emu)
|
||||
|
||||
setup_packet_table(&options, emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
|
||||
|
||||
isa_disasm(emu->instrs, emu->sizedwords * 4, stdout, &options);
|
||||
jumptbl_offset = find_jump_table(emu->instrs, emu->sizedwords, emu->jmptbl,
|
||||
ARRAY_SIZE(emu->jmptbl));
|
||||
|
||||
isa_disasm(emu->instrs, MIN2(emu->sizedwords, jumptbl_offset) * 4, stdout, &options);
|
||||
|
||||
if (jumptbl_offset != ~0) {
|
||||
printf("jumptbl:\n");
|
||||
printf(".jumptbl\n");
|
||||
if (jumptbl_offset + ARRAY_SIZE(emu->jmptbl) != emu->sizedwords) {
|
||||
for (unsigned i = jumptbl_offset + ARRAY_SIZE(emu->jmptbl); i < emu->sizedwords; i++)
|
||||
printf("[%08x]\n", emu->instrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
emu->instrs -= lpac_offset;
|
||||
emu->sizedwords += lpac_offset;
|
||||
|
||||
@@ -98,6 +98,8 @@ extern YYSTYPE yylval;
|
||||
"(sds"[1-3]")" yylval.num = yytext[4] - '0'; return T_SDS;
|
||||
"(peek)" return TOKEN(T_PEEK);
|
||||
|
||||
".jumptbl" return TOKEN(T_JUMPTBL);
|
||||
|
||||
"," return ',';
|
||||
"[" return '[';
|
||||
"]" return ']';
|
||||
|
||||
@@ -173,6 +173,8 @@ label(const char *str)
|
||||
%token <num> T_XMOV
|
||||
%token <num> T_SDS
|
||||
|
||||
%token <tok> T_JUMPTBL
|
||||
|
||||
%type <num> reg
|
||||
%type <num> immediate
|
||||
%type <num> xmov
|
||||
@@ -188,10 +190,11 @@ instrs: instrs instr_or_label
|
||||
| instr_or_label
|
||||
|
||||
instr_or_label: instr_r
|
||||
| T_REP instr_r { instr->rep = true; }
|
||||
| T_REP instr_r { instr->rep = true; }
|
||||
| branch_instr
|
||||
| other_instr
|
||||
| T_LABEL_DECL { decl_label($1); }
|
||||
| T_LABEL_DECL { decl_label($1); }
|
||||
| T_JUMPTBL { decl_jumptbl(); }
|
||||
|
||||
xmov: T_XMOV { $$ = $1; }
|
||||
| { $$ = 0; }
|
||||
@@ -295,6 +298,7 @@ other_instr: T_OP_CALL T_LABEL_REF { new_instr(OPC_CALL); label($2); }
|
||||
| T_OP_WAITIN { new_instr(OPC_WAITIN); }
|
||||
| T_OP_NOP { new_instr(OPC_NOP); }
|
||||
| T_LITERAL { new_instr(OPC_RAW_LITERAL); literal($1); }
|
||||
| '[' T_LABEL_REF ']' { new_instr(OPC_RAW_LITERAL); label($2); }
|
||||
|
||||
reg: T_REGISTER
|
||||
|
||||
|
||||
Reference in New Issue
Block a user