xref: /aosp_15_r20/external/mesa3d/src/freedreno/afuc/emu.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1*61046927SAndroid Build Coastguard Worker /*
2*61046927SAndroid Build Coastguard Worker  * Copyright © 2021 Google, Inc.
3*61046927SAndroid Build Coastguard Worker  * SPDX-License-Identifier: MIT
4*61046927SAndroid Build Coastguard Worker  */
5*61046927SAndroid Build Coastguard Worker 
6*61046927SAndroid Build Coastguard Worker #include <assert.h>
7*61046927SAndroid Build Coastguard Worker #include <ctype.h>
8*61046927SAndroid Build Coastguard Worker #include <errno.h>
9*61046927SAndroid Build Coastguard Worker #include <stdio.h>
10*61046927SAndroid Build Coastguard Worker #include <stdlib.h>
11*61046927SAndroid Build Coastguard Worker #include <string.h>
12*61046927SAndroid Build Coastguard Worker #include <sys/mman.h>
13*61046927SAndroid Build Coastguard Worker #include <unistd.h>
14*61046927SAndroid Build Coastguard Worker 
15*61046927SAndroid Build Coastguard Worker #include "util/u_math.h"
16*61046927SAndroid Build Coastguard Worker 
17*61046927SAndroid Build Coastguard Worker #include "freedreno_pm4.h"
18*61046927SAndroid Build Coastguard Worker 
19*61046927SAndroid Build Coastguard Worker #include "afuc-isa.h"
20*61046927SAndroid Build Coastguard Worker 
21*61046927SAndroid Build Coastguard Worker #include "emu.h"
22*61046927SAndroid Build Coastguard Worker #include "util.h"
23*61046927SAndroid Build Coastguard Worker 
24*61046927SAndroid Build Coastguard Worker #define rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
25*61046927SAndroid Build Coastguard Worker #define rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r))))
26*61046927SAndroid Build Coastguard Worker 
27*61046927SAndroid Build Coastguard Worker EMU_SQE_REG(SP);
28*61046927SAndroid Build Coastguard Worker EMU_SQE_REG(STACK0);
29*61046927SAndroid Build Coastguard Worker EMU_CONTROL_REG(DRAW_STATE_SET_HDR);
30*61046927SAndroid Build Coastguard Worker 
31*61046927SAndroid Build Coastguard Worker /**
32*61046927SAndroid Build Coastguard Worker  * AFUC emulator.  Currently only supports a6xx
33*61046927SAndroid Build Coastguard Worker  *
34*61046927SAndroid Build Coastguard Worker  * TODO to add a5xx it might be easier to compile this multiple times
35*61046927SAndroid Build Coastguard Worker  * with conditional compile to deal with differences between generations.
36*61046927SAndroid Build Coastguard Worker  */
37*61046927SAndroid Build Coastguard Worker 
38*61046927SAndroid Build Coastguard Worker static uint32_t
emu_alu(struct emu * emu,afuc_opc opc,uint32_t src1,uint32_t src2)39*61046927SAndroid Build Coastguard Worker emu_alu(struct emu *emu, afuc_opc opc, uint32_t src1, uint32_t src2)
40*61046927SAndroid Build Coastguard Worker {
41*61046927SAndroid Build Coastguard Worker    uint64_t tmp;
42*61046927SAndroid Build Coastguard Worker    switch (opc) {
43*61046927SAndroid Build Coastguard Worker    case OPC_ADD:
44*61046927SAndroid Build Coastguard Worker       tmp = (uint64_t)src1 + (uint64_t)src2;
45*61046927SAndroid Build Coastguard Worker       emu->carry = tmp >> 32;
46*61046927SAndroid Build Coastguard Worker       return (uint32_t)tmp;
47*61046927SAndroid Build Coastguard Worker    case OPC_ADDHI:
48*61046927SAndroid Build Coastguard Worker       return src1 + src2 + emu->carry;
49*61046927SAndroid Build Coastguard Worker    case OPC_SUB:
50*61046927SAndroid Build Coastguard Worker       tmp = (uint64_t)src1 - (uint64_t)src2;
51*61046927SAndroid Build Coastguard Worker       emu->carry = tmp >> 32;
52*61046927SAndroid Build Coastguard Worker       return (uint32_t)tmp;
53*61046927SAndroid Build Coastguard Worker    case OPC_SUBHI:
54*61046927SAndroid Build Coastguard Worker       return src1 - src2 + emu->carry;
55*61046927SAndroid Build Coastguard Worker    case OPC_AND:
56*61046927SAndroid Build Coastguard Worker       return src1 & src2;
57*61046927SAndroid Build Coastguard Worker    case OPC_OR:
58*61046927SAndroid Build Coastguard Worker       return src1 | src2;
59*61046927SAndroid Build Coastguard Worker    case OPC_XOR:
60*61046927SAndroid Build Coastguard Worker       return src1 ^ src2;
61*61046927SAndroid Build Coastguard Worker    case OPC_NOT:
62*61046927SAndroid Build Coastguard Worker       return ~src1;
63*61046927SAndroid Build Coastguard Worker    case OPC_SHL:
64*61046927SAndroid Build Coastguard Worker       return src1 << src2;
65*61046927SAndroid Build Coastguard Worker    case OPC_USHR:
66*61046927SAndroid Build Coastguard Worker       return src1 >> src2;
67*61046927SAndroid Build Coastguard Worker    case OPC_ISHR:
68*61046927SAndroid Build Coastguard Worker       return (int32_t)src1 >> src2;
69*61046927SAndroid Build Coastguard Worker    case OPC_ROT:
70*61046927SAndroid Build Coastguard Worker       if (src2 & 0x80000000)
71*61046927SAndroid Build Coastguard Worker          return rotl64(src1, -*(int32_t *)&src2);
72*61046927SAndroid Build Coastguard Worker       else
73*61046927SAndroid Build Coastguard Worker          return rotl32(src1, src2);
74*61046927SAndroid Build Coastguard Worker    case OPC_MUL8:
75*61046927SAndroid Build Coastguard Worker       return (src1 & 0xff) * (src2 & 0xff);
76*61046927SAndroid Build Coastguard Worker    case OPC_MIN:
77*61046927SAndroid Build Coastguard Worker       return MIN2(src1, src2);
78*61046927SAndroid Build Coastguard Worker    case OPC_MAX:
79*61046927SAndroid Build Coastguard Worker       return MAX2(src1, src2);
80*61046927SAndroid Build Coastguard Worker    case OPC_CMP:
81*61046927SAndroid Build Coastguard Worker       if (src1 > src2)
82*61046927SAndroid Build Coastguard Worker          return 0x00;
83*61046927SAndroid Build Coastguard Worker       else if (src1 == src2)
84*61046927SAndroid Build Coastguard Worker          return 0x2b;
85*61046927SAndroid Build Coastguard Worker       return 0x1e;
86*61046927SAndroid Build Coastguard Worker    case OPC_BIC:
87*61046927SAndroid Build Coastguard Worker       return src1 & ~src2;
88*61046927SAndroid Build Coastguard Worker    case OPC_MSB:
89*61046927SAndroid Build Coastguard Worker       if (!src2)
90*61046927SAndroid Build Coastguard Worker          return 0;
91*61046927SAndroid Build Coastguard Worker       return util_last_bit(src2) - 1;
92*61046927SAndroid Build Coastguard Worker    case OPC_SETBIT: {
93*61046927SAndroid Build Coastguard Worker       unsigned bit = src2 >> 1;
94*61046927SAndroid Build Coastguard Worker       unsigned val = src2 & 1;
95*61046927SAndroid Build Coastguard Worker       return (src1 & ~(1u << bit)) | (val << bit);
96*61046927SAndroid Build Coastguard Worker    }
97*61046927SAndroid Build Coastguard Worker    default:
98*61046927SAndroid Build Coastguard Worker       printf("unhandled alu opc: 0x%02x\n", opc);
99*61046927SAndroid Build Coastguard Worker       exit(1);
100*61046927SAndroid Build Coastguard Worker    }
101*61046927SAndroid Build Coastguard Worker }
102*61046927SAndroid Build Coastguard Worker 
103*61046927SAndroid Build Coastguard Worker /**
104*61046927SAndroid Build Coastguard Worker  * Helper to calculate load/store address based on LOAD_STORE_HI
105*61046927SAndroid Build Coastguard Worker  */
106*61046927SAndroid Build Coastguard Worker static uintptr_t
load_store_addr(struct emu * emu,unsigned gpr)107*61046927SAndroid Build Coastguard Worker load_store_addr(struct emu *emu, unsigned gpr)
108*61046927SAndroid Build Coastguard Worker {
109*61046927SAndroid Build Coastguard Worker    EMU_CONTROL_REG(LOAD_STORE_HI);
110*61046927SAndroid Build Coastguard Worker 
111*61046927SAndroid Build Coastguard Worker    uintptr_t addr = emu_get_reg32(emu, &LOAD_STORE_HI);
112*61046927SAndroid Build Coastguard Worker    addr <<= 32;
113*61046927SAndroid Build Coastguard Worker 
114*61046927SAndroid Build Coastguard Worker    return addr + emu_get_gpr_reg(emu, gpr);
115*61046927SAndroid Build Coastguard Worker }
116*61046927SAndroid Build Coastguard Worker 
117*61046927SAndroid Build Coastguard Worker static void
emu_instr(struct emu * emu,struct afuc_instr * instr)118*61046927SAndroid Build Coastguard Worker emu_instr(struct emu *emu, struct afuc_instr *instr)
119*61046927SAndroid Build Coastguard Worker {
120*61046927SAndroid Build Coastguard Worker    uint32_t rem = emu_get_gpr_reg(emu, REG_REM);
121*61046927SAndroid Build Coastguard Worker 
122*61046927SAndroid Build Coastguard Worker    switch (instr->opc) {
123*61046927SAndroid Build Coastguard Worker    case OPC_NOP:
124*61046927SAndroid Build Coastguard Worker       break;
125*61046927SAndroid Build Coastguard Worker    case OPC_MSB:
126*61046927SAndroid Build Coastguard Worker    case OPC_ADD ... OPC_BIC: {
127*61046927SAndroid Build Coastguard Worker       uint32_t val = emu_alu(emu, instr->opc,
128*61046927SAndroid Build Coastguard Worker                              emu_get_gpr_reg(emu, instr->src1),
129*61046927SAndroid Build Coastguard Worker                              instr->has_immed ? instr->immed :
130*61046927SAndroid Build Coastguard Worker                              emu_get_gpr_reg_alu(emu, instr->src2, instr->peek));
131*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst, val);
132*61046927SAndroid Build Coastguard Worker 
133*61046927SAndroid Build Coastguard Worker       if (instr->xmov) {
134*61046927SAndroid Build Coastguard Worker          unsigned m = MIN2(instr->xmov, rem);
135*61046927SAndroid Build Coastguard Worker 
136*61046927SAndroid Build Coastguard Worker          assert(m <= 3);
137*61046927SAndroid Build Coastguard Worker 
138*61046927SAndroid Build Coastguard Worker          if (m == 1) {
139*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_REM, --rem);
140*61046927SAndroid Build Coastguard Worker             emu_dump_state_change(emu);
141*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_DATA,
142*61046927SAndroid Build Coastguard Worker                             emu_get_gpr_reg(emu, instr->src2));
143*61046927SAndroid Build Coastguard Worker          } else if (m == 2) {
144*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_REM, --rem);
145*61046927SAndroid Build Coastguard Worker             emu_dump_state_change(emu);
146*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_DATA,
147*61046927SAndroid Build Coastguard Worker                             emu_get_gpr_reg(emu, instr->src2));
148*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_REM, --rem);
149*61046927SAndroid Build Coastguard Worker             emu_dump_state_change(emu);
150*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_DATA,
151*61046927SAndroid Build Coastguard Worker                             emu_get_gpr_reg(emu, instr->src2));
152*61046927SAndroid Build Coastguard Worker          } else if (m == 3) {
153*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_REM, --rem);
154*61046927SAndroid Build Coastguard Worker             emu_dump_state_change(emu);
155*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_DATA,
156*61046927SAndroid Build Coastguard Worker                             emu_get_gpr_reg(emu, instr->src2));
157*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_REM, --rem);
158*61046927SAndroid Build Coastguard Worker             emu_dump_state_change(emu);
159*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, instr->dst,
160*61046927SAndroid Build Coastguard Worker                             emu_get_gpr_reg(emu, instr->src2));
161*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_REM, --rem);
162*61046927SAndroid Build Coastguard Worker             emu_dump_state_change(emu);
163*61046927SAndroid Build Coastguard Worker             emu_set_gpr_reg(emu, REG_DATA,
164*61046927SAndroid Build Coastguard Worker                             emu_get_gpr_reg(emu, instr->src2));
165*61046927SAndroid Build Coastguard Worker          }
166*61046927SAndroid Build Coastguard Worker       }
167*61046927SAndroid Build Coastguard Worker       break;
168*61046927SAndroid Build Coastguard Worker    }
169*61046927SAndroid Build Coastguard Worker    case OPC_MOVI: {
170*61046927SAndroid Build Coastguard Worker       uint32_t val = instr->immed << instr->shift;
171*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst, val);
172*61046927SAndroid Build Coastguard Worker       break;
173*61046927SAndroid Build Coastguard Worker    }
174*61046927SAndroid Build Coastguard Worker    case OPC_SETBITI: {
175*61046927SAndroid Build Coastguard Worker       uint32_t src = emu_get_gpr_reg(emu, instr->src1);
176*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst, src | (1u << instr->bit));
177*61046927SAndroid Build Coastguard Worker       break;
178*61046927SAndroid Build Coastguard Worker    }
179*61046927SAndroid Build Coastguard Worker    case OPC_CLRBIT: {
180*61046927SAndroid Build Coastguard Worker       uint32_t src = emu_get_gpr_reg(emu, instr->src1);
181*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst, src & ~(1u << instr->bit));
182*61046927SAndroid Build Coastguard Worker       break;
183*61046927SAndroid Build Coastguard Worker    }
184*61046927SAndroid Build Coastguard Worker    case OPC_UBFX: {
185*61046927SAndroid Build Coastguard Worker       uint32_t src = emu_get_gpr_reg(emu, instr->src1);
186*61046927SAndroid Build Coastguard Worker       unsigned lo = instr->bit, hi = instr->immed;
187*61046927SAndroid Build Coastguard Worker       uint32_t dst = (src >> lo) & BITFIELD_MASK(hi - lo + 1);
188*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst, dst);
189*61046927SAndroid Build Coastguard Worker       break;
190*61046927SAndroid Build Coastguard Worker    }
191*61046927SAndroid Build Coastguard Worker    case OPC_BFI: {
192*61046927SAndroid Build Coastguard Worker       uint32_t src = emu_get_gpr_reg(emu, instr->src1);
193*61046927SAndroid Build Coastguard Worker       unsigned lo = instr->bit, hi = instr->immed;
194*61046927SAndroid Build Coastguard Worker       src = (src & BITFIELD_MASK(hi - lo + 1)) << lo;
195*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst, emu_get_gpr_reg(emu, instr->dst) | src);
196*61046927SAndroid Build Coastguard Worker       break;
197*61046927SAndroid Build Coastguard Worker    }
198*61046927SAndroid Build Coastguard Worker    case OPC_CWRITE: {
199*61046927SAndroid Build Coastguard Worker       uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
200*61046927SAndroid Build Coastguard Worker       uint32_t src2 = emu_get_gpr_reg(emu, instr->src2);
201*61046927SAndroid Build Coastguard Worker       uint32_t reg = src2 + instr->immed;
202*61046927SAndroid Build Coastguard Worker 
203*61046927SAndroid Build Coastguard Worker       if (instr->preincrement) {
204*61046927SAndroid Build Coastguard Worker          emu_set_gpr_reg(emu, instr->src2, reg);
205*61046927SAndroid Build Coastguard Worker       }
206*61046927SAndroid Build Coastguard Worker 
207*61046927SAndroid Build Coastguard Worker       emu_set_control_reg(emu, reg, src1);
208*61046927SAndroid Build Coastguard Worker 
209*61046927SAndroid Build Coastguard Worker       for (unsigned i = 0; i < instr->sds; i++) {
210*61046927SAndroid Build Coastguard Worker          uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
211*61046927SAndroid Build Coastguard Worker 
212*61046927SAndroid Build Coastguard Worker          /* TODO: There is likely a DRAW_STATE_SET_BASE register on a6xx, as
213*61046927SAndroid Build Coastguard Worker           * there is on a7xx, and we should be writing that instead of setting
214*61046927SAndroid Build Coastguard Worker           * the base directly.
215*61046927SAndroid Build Coastguard Worker           */
216*61046927SAndroid Build Coastguard Worker          if (reg == emu_reg_offset(&DRAW_STATE_SET_HDR))
217*61046927SAndroid Build Coastguard Worker             emu_set_draw_state_base(emu, i, src1);
218*61046927SAndroid Build Coastguard Worker       }
219*61046927SAndroid Build Coastguard Worker       break;
220*61046927SAndroid Build Coastguard Worker    }
221*61046927SAndroid Build Coastguard Worker    case OPC_CREAD: {
222*61046927SAndroid Build Coastguard Worker       uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
223*61046927SAndroid Build Coastguard Worker 
224*61046927SAndroid Build Coastguard Worker       if (instr->preincrement) {
225*61046927SAndroid Build Coastguard Worker          emu_set_gpr_reg(emu, instr->src1, src1 + instr->immed);
226*61046927SAndroid Build Coastguard Worker       }
227*61046927SAndroid Build Coastguard Worker 
228*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst,
229*61046927SAndroid Build Coastguard Worker                       emu_get_control_reg(emu, src1 + instr->immed));
230*61046927SAndroid Build Coastguard Worker       break;
231*61046927SAndroid Build Coastguard Worker    }
232*61046927SAndroid Build Coastguard Worker    case OPC_SWRITE: {
233*61046927SAndroid Build Coastguard Worker       uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
234*61046927SAndroid Build Coastguard Worker       uint32_t src2 = emu_get_gpr_reg(emu, instr->src2);
235*61046927SAndroid Build Coastguard Worker 
236*61046927SAndroid Build Coastguard Worker       if (instr->preincrement) {
237*61046927SAndroid Build Coastguard Worker          emu_set_gpr_reg(emu, instr->src2, src2 + instr->immed);
238*61046927SAndroid Build Coastguard Worker       }
239*61046927SAndroid Build Coastguard Worker 
240*61046927SAndroid Build Coastguard Worker       emu_set_sqe_reg(emu, src2 + instr->immed, src1);
241*61046927SAndroid Build Coastguard Worker       break;
242*61046927SAndroid Build Coastguard Worker    }
243*61046927SAndroid Build Coastguard Worker    case OPC_SREAD: {
244*61046927SAndroid Build Coastguard Worker       uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
245*61046927SAndroid Build Coastguard Worker 
246*61046927SAndroid Build Coastguard Worker       if (instr->preincrement) {
247*61046927SAndroid Build Coastguard Worker          emu_set_gpr_reg(emu, instr->src1, src1 + instr->immed);
248*61046927SAndroid Build Coastguard Worker       }
249*61046927SAndroid Build Coastguard Worker 
250*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst,
251*61046927SAndroid Build Coastguard Worker                       emu_get_sqe_reg(emu, src1 + instr->immed));
252*61046927SAndroid Build Coastguard Worker       break;
253*61046927SAndroid Build Coastguard Worker    }
254*61046927SAndroid Build Coastguard Worker    case OPC_LOAD: {
255*61046927SAndroid Build Coastguard Worker       uintptr_t addr = load_store_addr(emu, instr->src1) +
256*61046927SAndroid Build Coastguard Worker             instr->immed;
257*61046927SAndroid Build Coastguard Worker 
258*61046927SAndroid Build Coastguard Worker       if (instr->preincrement) {
259*61046927SAndroid Build Coastguard Worker          uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
260*61046927SAndroid Build Coastguard Worker          emu_set_gpr_reg(emu, instr->src1, src1 + instr->immed);
261*61046927SAndroid Build Coastguard Worker       }
262*61046927SAndroid Build Coastguard Worker 
263*61046927SAndroid Build Coastguard Worker       uint32_t val = emu_mem_read_dword(emu, addr);
264*61046927SAndroid Build Coastguard Worker 
265*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, instr->dst, val);
266*61046927SAndroid Build Coastguard Worker 
267*61046927SAndroid Build Coastguard Worker       break;
268*61046927SAndroid Build Coastguard Worker    }
269*61046927SAndroid Build Coastguard Worker    case OPC_STORE: {
270*61046927SAndroid Build Coastguard Worker       uintptr_t addr = load_store_addr(emu, instr->src2) +
271*61046927SAndroid Build Coastguard Worker             instr->immed;
272*61046927SAndroid Build Coastguard Worker 
273*61046927SAndroid Build Coastguard Worker       if (instr->preincrement) {
274*61046927SAndroid Build Coastguard Worker          uint32_t src2 = emu_get_gpr_reg(emu, instr->src2);
275*61046927SAndroid Build Coastguard Worker          emu_set_gpr_reg(emu, instr->src2, src2 + instr->immed);
276*61046927SAndroid Build Coastguard Worker       }
277*61046927SAndroid Build Coastguard Worker 
278*61046927SAndroid Build Coastguard Worker       uint32_t val = emu_get_gpr_reg(emu, instr->src1);
279*61046927SAndroid Build Coastguard Worker 
280*61046927SAndroid Build Coastguard Worker       emu_mem_write_dword(emu, addr, val);
281*61046927SAndroid Build Coastguard Worker 
282*61046927SAndroid Build Coastguard Worker       break;
283*61046927SAndroid Build Coastguard Worker    }
284*61046927SAndroid Build Coastguard Worker    case OPC_BRNEI ... OPC_BREQB: {
285*61046927SAndroid Build Coastguard Worker       uint32_t off = emu->gpr_regs.pc + instr->offset;
286*61046927SAndroid Build Coastguard Worker       uint32_t src = emu_get_gpr_reg(emu, instr->src1);
287*61046927SAndroid Build Coastguard Worker 
288*61046927SAndroid Build Coastguard Worker       if (instr->opc == OPC_BRNEI) {
289*61046927SAndroid Build Coastguard Worker          if (src != instr->immed)
290*61046927SAndroid Build Coastguard Worker             emu->branch_target = off;
291*61046927SAndroid Build Coastguard Worker       } else if (instr->opc == OPC_BREQI) {
292*61046927SAndroid Build Coastguard Worker          if (src == instr->immed)
293*61046927SAndroid Build Coastguard Worker             emu->branch_target = off;
294*61046927SAndroid Build Coastguard Worker       } else if (instr->opc == OPC_BRNEB) {
295*61046927SAndroid Build Coastguard Worker          if (!(src & (1 << instr->bit)))
296*61046927SAndroid Build Coastguard Worker             emu->branch_target = off;
297*61046927SAndroid Build Coastguard Worker       } else if (instr->opc == OPC_BREQB) {
298*61046927SAndroid Build Coastguard Worker          if (src & (1 << instr->bit))
299*61046927SAndroid Build Coastguard Worker             emu->branch_target = off;
300*61046927SAndroid Build Coastguard Worker       } else {
301*61046927SAndroid Build Coastguard Worker          assert(0);
302*61046927SAndroid Build Coastguard Worker       }
303*61046927SAndroid Build Coastguard Worker       break;
304*61046927SAndroid Build Coastguard Worker    }
305*61046927SAndroid Build Coastguard Worker    case OPC_RET: {
306*61046927SAndroid Build Coastguard Worker       unsigned sp = emu_get_reg32(emu, &SP);
307*61046927SAndroid Build Coastguard Worker       assert(sp > 0);
308*61046927SAndroid Build Coastguard Worker 
309*61046927SAndroid Build Coastguard Worker       /* counter-part to 'call' instruction, also has a delay slot: */
310*61046927SAndroid Build Coastguard Worker       emu->branch_target = emu_get_sqe_reg(emu, emu_reg_offset(&STACK0) + sp - 1);
311*61046927SAndroid Build Coastguard Worker       emu_set_reg32(emu, &SP, sp - 1);
312*61046927SAndroid Build Coastguard Worker 
313*61046927SAndroid Build Coastguard Worker       break;
314*61046927SAndroid Build Coastguard Worker    }
315*61046927SAndroid Build Coastguard Worker    case OPC_CALL: {
316*61046927SAndroid Build Coastguard Worker       unsigned sp = emu_get_reg32(emu, &SP);
317*61046927SAndroid Build Coastguard Worker       assert(sp + emu_reg_offset(&STACK0) < ARRAY_SIZE(emu->sqe_regs.val));
318*61046927SAndroid Build Coastguard Worker 
319*61046927SAndroid Build Coastguard Worker       /* call looks to have same delay-slot behavior as branch/etc, so
320*61046927SAndroid Build Coastguard Worker        * presumably the return PC is two instructions later:
321*61046927SAndroid Build Coastguard Worker        */
322*61046927SAndroid Build Coastguard Worker       emu_set_sqe_reg(emu, emu_reg_offset(&STACK0) + sp, emu->gpr_regs.pc + 2);
323*61046927SAndroid Build Coastguard Worker       emu_set_reg32(emu, &SP, sp + 1);
324*61046927SAndroid Build Coastguard Worker       emu->branch_target = instr->literal;
325*61046927SAndroid Build Coastguard Worker 
326*61046927SAndroid Build Coastguard Worker       break;
327*61046927SAndroid Build Coastguard Worker    }
328*61046927SAndroid Build Coastguard Worker    case OPC_WAITIN: {
329*61046927SAndroid Build Coastguard Worker       assert(!emu->branch_target);
330*61046927SAndroid Build Coastguard Worker       emu->run_mode = false;
331*61046927SAndroid Build Coastguard Worker       emu->waitin = true;
332*61046927SAndroid Build Coastguard Worker       break;
333*61046927SAndroid Build Coastguard Worker    }
334*61046927SAndroid Build Coastguard Worker    case OPC_BL: {
335*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, REG_LR, emu->gpr_regs.pc + 2);
336*61046927SAndroid Build Coastguard Worker       emu->branch_target = instr->literal;
337*61046927SAndroid Build Coastguard Worker       break;
338*61046927SAndroid Build Coastguard Worker    }
339*61046927SAndroid Build Coastguard Worker    case OPC_JUMPR: {
340*61046927SAndroid Build Coastguard Worker       emu->branch_target = emu_get_gpr_reg(emu, instr->src1);
341*61046927SAndroid Build Coastguard Worker       break;
342*61046927SAndroid Build Coastguard Worker    }
343*61046927SAndroid Build Coastguard Worker    case OPC_SRET: {
344*61046927SAndroid Build Coastguard Worker       emu->branch_target = emu_get_gpr_reg(emu, REG_LR);
345*61046927SAndroid Build Coastguard Worker       /* TODO: read $sp and check for stack overflow? */
346*61046927SAndroid Build Coastguard Worker       break;
347*61046927SAndroid Build Coastguard Worker    }
348*61046927SAndroid Build Coastguard Worker    case OPC_SETSECURE: {
349*61046927SAndroid Build Coastguard Worker       // TODO this acts like a conditional branch, but in which case
350*61046927SAndroid Build Coastguard Worker       // does it branch?
351*61046927SAndroid Build Coastguard Worker       break;
352*61046927SAndroid Build Coastguard Worker    }
353*61046927SAndroid Build Coastguard Worker    default:
354*61046927SAndroid Build Coastguard Worker       printf("unhandled opc: 0x%02x\n", instr->opc);
355*61046927SAndroid Build Coastguard Worker       exit(1);
356*61046927SAndroid Build Coastguard Worker    }
357*61046927SAndroid Build Coastguard Worker 
358*61046927SAndroid Build Coastguard Worker    if (instr->rep) {
359*61046927SAndroid Build Coastguard Worker       assert(rem > 0);
360*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, REG_REM, --rem);
361*61046927SAndroid Build Coastguard Worker    }
362*61046927SAndroid Build Coastguard Worker }
363*61046927SAndroid Build Coastguard Worker 
364*61046927SAndroid Build Coastguard Worker void
emu_step(struct emu * emu)365*61046927SAndroid Build Coastguard Worker emu_step(struct emu *emu)
366*61046927SAndroid Build Coastguard Worker {
367*61046927SAndroid Build Coastguard Worker    struct afuc_instr *instr;
368*61046927SAndroid Build Coastguard Worker    bool decoded =
369*61046927SAndroid Build Coastguard Worker       afuc_isa_decode((void *)&instr, (void *)&emu->instrs[emu->gpr_regs.pc],
370*61046927SAndroid Build Coastguard Worker                       &(struct isa_decode_options){
371*61046927SAndroid Build Coastguard Worker                          .gpu_id = gpuver,
372*61046927SAndroid Build Coastguard Worker                       });
373*61046927SAndroid Build Coastguard Worker 
374*61046927SAndroid Build Coastguard Worker    if (!decoded) {
375*61046927SAndroid Build Coastguard Worker       uint32_t instr_val = emu->instrs[emu->gpr_regs.pc];
376*61046927SAndroid Build Coastguard Worker       if ((instr_val >> 27) == 0) {
377*61046927SAndroid Build Coastguard Worker          /* This is printed as an undecoded literal to show the immediate
378*61046927SAndroid Build Coastguard Worker           * payload, but when executing it's just a NOP.
379*61046927SAndroid Build Coastguard Worker           */
380*61046927SAndroid Build Coastguard Worker          instr = calloc(1, sizeof(struct afuc_instr));
381*61046927SAndroid Build Coastguard Worker          instr->opc = OPC_NOP;
382*61046927SAndroid Build Coastguard Worker       } else {
383*61046927SAndroid Build Coastguard Worker          printf("unmatched instruction: 0x%08x\n", instr_val);
384*61046927SAndroid Build Coastguard Worker          exit(1);
385*61046927SAndroid Build Coastguard Worker       }
386*61046927SAndroid Build Coastguard Worker    }
387*61046927SAndroid Build Coastguard Worker 
388*61046927SAndroid Build Coastguard Worker    emu_main_prompt(emu);
389*61046927SAndroid Build Coastguard Worker 
390*61046927SAndroid Build Coastguard Worker    uint32_t branch_target = emu->branch_target;
391*61046927SAndroid Build Coastguard Worker    emu->branch_target = 0;
392*61046927SAndroid Build Coastguard Worker 
393*61046927SAndroid Build Coastguard Worker    bool waitin = emu->waitin;
394*61046927SAndroid Build Coastguard Worker    emu->waitin = false;
395*61046927SAndroid Build Coastguard Worker 
396*61046927SAndroid Build Coastguard Worker    if (instr->rep) {
397*61046927SAndroid Build Coastguard Worker       do {
398*61046927SAndroid Build Coastguard Worker          if (!emu_get_gpr_reg(emu, REG_REM))
399*61046927SAndroid Build Coastguard Worker             break;
400*61046927SAndroid Build Coastguard Worker 
401*61046927SAndroid Build Coastguard Worker          emu_clear_state_change(emu);
402*61046927SAndroid Build Coastguard Worker          emu_instr(emu, instr);
403*61046927SAndroid Build Coastguard Worker 
404*61046927SAndroid Build Coastguard Worker          /* defer last state-change dump until after any
405*61046927SAndroid Build Coastguard Worker           * post-delay-slot handling below:
406*61046927SAndroid Build Coastguard Worker           */
407*61046927SAndroid Build Coastguard Worker          if (emu_get_gpr_reg(emu, REG_REM))
408*61046927SAndroid Build Coastguard Worker             emu_dump_state_change(emu);
409*61046927SAndroid Build Coastguard Worker       } while (true);
410*61046927SAndroid Build Coastguard Worker    } else {
411*61046927SAndroid Build Coastguard Worker       emu_clear_state_change(emu);
412*61046927SAndroid Build Coastguard Worker       emu_instr(emu, instr);
413*61046927SAndroid Build Coastguard Worker    }
414*61046927SAndroid Build Coastguard Worker 
415*61046927SAndroid Build Coastguard Worker    emu->gpr_regs.pc++;
416*61046927SAndroid Build Coastguard Worker 
417*61046927SAndroid Build Coastguard Worker    if (branch_target) {
418*61046927SAndroid Build Coastguard Worker       emu->gpr_regs.pc = branch_target;
419*61046927SAndroid Build Coastguard Worker    }
420*61046927SAndroid Build Coastguard Worker 
421*61046927SAndroid Build Coastguard Worker    if (waitin) {
422*61046927SAndroid Build Coastguard Worker       uint32_t hdr = emu_get_gpr_reg(emu, 1);
423*61046927SAndroid Build Coastguard Worker       uint32_t id, count;
424*61046927SAndroid Build Coastguard Worker 
425*61046927SAndroid Build Coastguard Worker       if (pkt_is_type4(hdr)) {
426*61046927SAndroid Build Coastguard Worker          id = afuc_pm4_id("PKT4");
427*61046927SAndroid Build Coastguard Worker          count = type4_pkt_size(hdr);
428*61046927SAndroid Build Coastguard Worker 
429*61046927SAndroid Build Coastguard Worker          /* Possibly a hack, not sure what the hw actually
430*61046927SAndroid Build Coastguard Worker           * does here, but we want to mask out the pkt
431*61046927SAndroid Build Coastguard Worker           * type field from the hdr, so that PKT4 handler
432*61046927SAndroid Build Coastguard Worker           * doesn't see it and interpret it as part as the
433*61046927SAndroid Build Coastguard Worker           * register offset:
434*61046927SAndroid Build Coastguard Worker           */
435*61046927SAndroid Build Coastguard Worker          emu->gpr_regs.val[1] &= 0x0fffffff;
436*61046927SAndroid Build Coastguard Worker       } else if (pkt_is_type7(hdr)) {
437*61046927SAndroid Build Coastguard Worker          id = cp_type7_opcode(hdr);
438*61046927SAndroid Build Coastguard Worker          count = type7_pkt_size(hdr);
439*61046927SAndroid Build Coastguard Worker       } else {
440*61046927SAndroid Build Coastguard Worker          printf("Invalid opcode: 0x%08x\n", hdr);
441*61046927SAndroid Build Coastguard Worker          exit(1);  /* GPU goes *boom* */
442*61046927SAndroid Build Coastguard Worker       }
443*61046927SAndroid Build Coastguard Worker 
444*61046927SAndroid Build Coastguard Worker       assert(id < ARRAY_SIZE(emu->jmptbl));
445*61046927SAndroid Build Coastguard Worker 
446*61046927SAndroid Build Coastguard Worker       emu_set_gpr_reg(emu, REG_REM, count);
447*61046927SAndroid Build Coastguard Worker       emu->gpr_regs.pc = emu->jmptbl[id];
448*61046927SAndroid Build Coastguard Worker    }
449*61046927SAndroid Build Coastguard Worker 
450*61046927SAndroid Build Coastguard Worker    emu_dump_state_change(emu);
451*61046927SAndroid Build Coastguard Worker 
452*61046927SAndroid Build Coastguard Worker    free(instr);
453*61046927SAndroid Build Coastguard Worker }
454*61046927SAndroid Build Coastguard Worker 
455*61046927SAndroid Build Coastguard Worker void
emu_run_bootstrap(struct emu * emu)456*61046927SAndroid Build Coastguard Worker emu_run_bootstrap(struct emu *emu)
457*61046927SAndroid Build Coastguard Worker {
458*61046927SAndroid Build Coastguard Worker    EMU_CONTROL_REG(THREAD_SYNC);
459*61046927SAndroid Build Coastguard Worker 
460*61046927SAndroid Build Coastguard Worker    emu->quiet = true;
461*61046927SAndroid Build Coastguard Worker    emu->run_mode = true;
462*61046927SAndroid Build Coastguard Worker    emu->bootstrap_mode = true;
463*61046927SAndroid Build Coastguard Worker    emu->bootstrap_finished = false;
464*61046927SAndroid Build Coastguard Worker 
465*61046927SAndroid Build Coastguard Worker    if (gpuver == 6 && emu->processor == EMU_PROC_LPAC) {
466*61046927SAndroid Build Coastguard Worker       /* Emulate what the SQE bootstrap routine does after launching LPAC */
467*61046927SAndroid Build Coastguard Worker       emu_set_reg32(emu, &THREAD_SYNC, 1u << 0);
468*61046927SAndroid Build Coastguard Worker    }
469*61046927SAndroid Build Coastguard Worker 
470*61046927SAndroid Build Coastguard Worker    while (!emu->bootstrap_finished && !emu->waitin) {
471*61046927SAndroid Build Coastguard Worker       emu_step(emu);
472*61046927SAndroid Build Coastguard Worker    }
473*61046927SAndroid Build Coastguard Worker 
474*61046927SAndroid Build Coastguard Worker    emu->bootstrap_mode = false;
475*61046927SAndroid Build Coastguard Worker }
476*61046927SAndroid Build Coastguard Worker 
477*61046927SAndroid Build Coastguard Worker 
478*61046927SAndroid Build Coastguard Worker static void
check_access(struct emu * emu,uintptr_t gpuaddr,unsigned sz)479*61046927SAndroid Build Coastguard Worker check_access(struct emu *emu, uintptr_t gpuaddr, unsigned sz)
480*61046927SAndroid Build Coastguard Worker {
481*61046927SAndroid Build Coastguard Worker    if ((gpuaddr % sz) != 0) {
482*61046927SAndroid Build Coastguard Worker       printf("unaligned access fault: %p\n", (void *)gpuaddr);
483*61046927SAndroid Build Coastguard Worker       exit(1);
484*61046927SAndroid Build Coastguard Worker    }
485*61046927SAndroid Build Coastguard Worker 
486*61046927SAndroid Build Coastguard Worker    if ((gpuaddr + sz) >= EMU_MEMORY_SIZE) {
487*61046927SAndroid Build Coastguard Worker       printf("iova fault: %p\n", (void *)gpuaddr);
488*61046927SAndroid Build Coastguard Worker       exit(1);
489*61046927SAndroid Build Coastguard Worker    }
490*61046927SAndroid Build Coastguard Worker }
491*61046927SAndroid Build Coastguard Worker 
492*61046927SAndroid Build Coastguard Worker uint32_t
emu_mem_read_dword(struct emu * emu,uintptr_t gpuaddr)493*61046927SAndroid Build Coastguard Worker emu_mem_read_dword(struct emu *emu, uintptr_t gpuaddr)
494*61046927SAndroid Build Coastguard Worker {
495*61046927SAndroid Build Coastguard Worker    check_access(emu, gpuaddr, 4);
496*61046927SAndroid Build Coastguard Worker    return *(uint32_t *)(emu->gpumem + gpuaddr);
497*61046927SAndroid Build Coastguard Worker }
498*61046927SAndroid Build Coastguard Worker 
499*61046927SAndroid Build Coastguard Worker static void
mem_write_dword(struct emu * emu,uintptr_t gpuaddr,uint32_t val)500*61046927SAndroid Build Coastguard Worker mem_write_dword(struct emu *emu, uintptr_t gpuaddr, uint32_t val)
501*61046927SAndroid Build Coastguard Worker {
502*61046927SAndroid Build Coastguard Worker    check_access(emu, gpuaddr, 4);
503*61046927SAndroid Build Coastguard Worker    *(uint32_t *)(emu->gpumem + gpuaddr) = val;
504*61046927SAndroid Build Coastguard Worker }
505*61046927SAndroid Build Coastguard Worker 
506*61046927SAndroid Build Coastguard Worker void
emu_mem_write_dword(struct emu * emu,uintptr_t gpuaddr,uint32_t val)507*61046927SAndroid Build Coastguard Worker emu_mem_write_dword(struct emu *emu, uintptr_t gpuaddr, uint32_t val)
508*61046927SAndroid Build Coastguard Worker {
509*61046927SAndroid Build Coastguard Worker    mem_write_dword(emu, gpuaddr, val);
510*61046927SAndroid Build Coastguard Worker    assert(emu->gpumem_written == ~0);
511*61046927SAndroid Build Coastguard Worker    emu->gpumem_written = gpuaddr;
512*61046927SAndroid Build Coastguard Worker }
513*61046927SAndroid Build Coastguard Worker 
514*61046927SAndroid Build Coastguard Worker void
emu_init(struct emu * emu)515*61046927SAndroid Build Coastguard Worker emu_init(struct emu *emu)
516*61046927SAndroid Build Coastguard Worker {
517*61046927SAndroid Build Coastguard Worker    emu->gpumem = mmap(NULL, EMU_MEMORY_SIZE,
518*61046927SAndroid Build Coastguard Worker                       PROT_READ | PROT_WRITE,
519*61046927SAndroid Build Coastguard Worker                       MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
520*61046927SAndroid Build Coastguard Worker                       0, 0);
521*61046927SAndroid Build Coastguard Worker    if (emu->gpumem == MAP_FAILED) {
522*61046927SAndroid Build Coastguard Worker       printf("Could not allocate GPU memory: %s\n", strerror(errno));
523*61046927SAndroid Build Coastguard Worker       exit(1);
524*61046927SAndroid Build Coastguard Worker    }
525*61046927SAndroid Build Coastguard Worker 
526*61046927SAndroid Build Coastguard Worker    /* Copy the instructions into GPU memory: */
527*61046927SAndroid Build Coastguard Worker    for (unsigned i = 0; i < emu->sizedwords; i++) {
528*61046927SAndroid Build Coastguard Worker       mem_write_dword(emu, EMU_INSTR_BASE + (4 * i), emu->instrs[i]);
529*61046927SAndroid Build Coastguard Worker    }
530*61046927SAndroid Build Coastguard Worker 
531*61046927SAndroid Build Coastguard Worker    EMU_GPU_REG(CP_SQE_INSTR_BASE);
532*61046927SAndroid Build Coastguard Worker    EMU_GPU_REG(CP_LPAC_SQE_INSTR_BASE);
533*61046927SAndroid Build Coastguard Worker    EMU_CONTROL_REG(BV_INSTR_BASE);
534*61046927SAndroid Build Coastguard Worker    EMU_CONTROL_REG(LPAC_INSTR_BASE);
535*61046927SAndroid Build Coastguard Worker 
536*61046927SAndroid Build Coastguard Worker    /* Setup the address of the SQE fw, just use the normal CPU ptr address: */
537*61046927SAndroid Build Coastguard Worker    switch (emu->processor) {
538*61046927SAndroid Build Coastguard Worker    case EMU_PROC_SQE:
539*61046927SAndroid Build Coastguard Worker       emu_set_reg64(emu, &CP_SQE_INSTR_BASE, EMU_INSTR_BASE);
540*61046927SAndroid Build Coastguard Worker       break;
541*61046927SAndroid Build Coastguard Worker    case EMU_PROC_BV:
542*61046927SAndroid Build Coastguard Worker       emu_set_reg64(emu, &BV_INSTR_BASE, EMU_INSTR_BASE);
543*61046927SAndroid Build Coastguard Worker       break;
544*61046927SAndroid Build Coastguard Worker    case EMU_PROC_LPAC:
545*61046927SAndroid Build Coastguard Worker       if (gpuver >= 7)
546*61046927SAndroid Build Coastguard Worker          emu_set_reg64(emu, &LPAC_INSTR_BASE, EMU_INSTR_BASE);
547*61046927SAndroid Build Coastguard Worker       else
548*61046927SAndroid Build Coastguard Worker          emu_set_reg64(emu, &CP_LPAC_SQE_INSTR_BASE, EMU_INSTR_BASE);
549*61046927SAndroid Build Coastguard Worker       break;
550*61046927SAndroid Build Coastguard Worker    }
551*61046927SAndroid Build Coastguard Worker 
552*61046927SAndroid Build Coastguard Worker    if (emu->fw_id == AFUC_A750) {
553*61046927SAndroid Build Coastguard Worker       emu_set_control_reg(emu, 0, 7 << 28);
554*61046927SAndroid Build Coastguard Worker       emu_set_control_reg(emu, 2, 0x40 << 8);
555*61046927SAndroid Build Coastguard Worker    } else if (emu->fw_id == AFUC_A730 || emu->fw_id == AFUC_A740) {
556*61046927SAndroid Build Coastguard Worker       emu_set_control_reg(emu, 0xef, 1 << 21);
557*61046927SAndroid Build Coastguard Worker       emu_set_control_reg(emu, 0, 7 << 28);
558*61046927SAndroid Build Coastguard Worker    } else if (emu->fw_id == AFUC_A660) {
559*61046927SAndroid Build Coastguard Worker       emu_set_control_reg(emu, 0, 3 << 28);
560*61046927SAndroid Build Coastguard Worker    } else if (emu->fw_id == AFUC_A650) {
561*61046927SAndroid Build Coastguard Worker       emu_set_control_reg(emu, 0, 1 << 28);
562*61046927SAndroid Build Coastguard Worker    }
563*61046927SAndroid Build Coastguard Worker }
564*61046927SAndroid Build Coastguard Worker 
565*61046927SAndroid Build Coastguard Worker void
emu_fini(struct emu * emu)566*61046927SAndroid Build Coastguard Worker emu_fini(struct emu *emu)
567*61046927SAndroid Build Coastguard Worker {
568*61046927SAndroid Build Coastguard Worker    uint32_t *instrs = emu->instrs;
569*61046927SAndroid Build Coastguard Worker    unsigned sizedwords = emu->sizedwords;
570*61046927SAndroid Build Coastguard Worker    unsigned fw_id = emu->fw_id;
571*61046927SAndroid Build Coastguard Worker 
572*61046927SAndroid Build Coastguard Worker    munmap(emu->gpumem, EMU_MEMORY_SIZE);
573*61046927SAndroid Build Coastguard Worker    memset(emu, 0, sizeof(*emu));
574*61046927SAndroid Build Coastguard Worker 
575*61046927SAndroid Build Coastguard Worker    emu->instrs = instrs;
576*61046927SAndroid Build Coastguard Worker    emu->sizedwords = sizedwords;
577*61046927SAndroid Build Coastguard Worker    emu->fw_id = fw_id;
578*61046927SAndroid Build Coastguard Worker }
579