1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2021 Google, Inc. 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21bf215546Sopenharmony_ci * SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#include <assert.h> 25bf215546Sopenharmony_ci#include <ctype.h> 26bf215546Sopenharmony_ci#include <errno.h> 27bf215546Sopenharmony_ci#include <stdio.h> 28bf215546Sopenharmony_ci#include <stdlib.h> 29bf215546Sopenharmony_ci#include <string.h> 30bf215546Sopenharmony_ci#include <sys/mman.h> 31bf215546Sopenharmony_ci#include <unistd.h> 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ci#include "util/u_math.h" 34bf215546Sopenharmony_ci 35bf215546Sopenharmony_ci#include "freedreno_pm4.h" 36bf215546Sopenharmony_ci 37bf215546Sopenharmony_ci#include "emu.h" 38bf215546Sopenharmony_ci#include "util.h" 39bf215546Sopenharmony_ci 40bf215546Sopenharmony_ci#define rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) 41bf215546Sopenharmony_ci#define rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) 42bf215546Sopenharmony_ci 43bf215546Sopenharmony_ci/** 44bf215546Sopenharmony_ci * AFUC emulator. Currently only supports a6xx 45bf215546Sopenharmony_ci * 46bf215546Sopenharmony_ci * TODO to add a5xx it might be easier to compile this multiple times 47bf215546Sopenharmony_ci * with conditional compile to deal with differences between generations. 48bf215546Sopenharmony_ci */ 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_cistatic uint32_t 51bf215546Sopenharmony_ciemu_alu(struct emu *emu, afuc_opc opc, uint32_t src1, uint32_t src2) 52bf215546Sopenharmony_ci{ 53bf215546Sopenharmony_ci uint64_t tmp; 54bf215546Sopenharmony_ci switch (opc) { 55bf215546Sopenharmony_ci case OPC_ADD: 56bf215546Sopenharmony_ci tmp = (uint64_t)src1 + (uint64_t)src2; 57bf215546Sopenharmony_ci emu->carry = tmp >> 32; 58bf215546Sopenharmony_ci return (uint32_t)tmp; 59bf215546Sopenharmony_ci case OPC_ADDHI: 60bf215546Sopenharmony_ci return src1 + src2 + emu->carry; 61bf215546Sopenharmony_ci case OPC_SUB: 62bf215546Sopenharmony_ci tmp = (uint64_t)src1 - (uint64_t)src2; 63bf215546Sopenharmony_ci emu->carry = tmp >> 32; 64bf215546Sopenharmony_ci return (uint32_t)tmp; 65bf215546Sopenharmony_ci case OPC_SUBHI: 66bf215546Sopenharmony_ci return src1 - src2 + emu->carry; 67bf215546Sopenharmony_ci case OPC_AND: 68bf215546Sopenharmony_ci return src1 & src2; 69bf215546Sopenharmony_ci case OPC_OR: 70bf215546Sopenharmony_ci return src1 | src2; 71bf215546Sopenharmony_ci case OPC_XOR: 72bf215546Sopenharmony_ci return src1 ^ src2; 73bf215546Sopenharmony_ci case OPC_NOT: 74bf215546Sopenharmony_ci return ~src1; 75bf215546Sopenharmony_ci case OPC_SHL: 76bf215546Sopenharmony_ci return src1 << src2; 77bf215546Sopenharmony_ci case OPC_USHR: 78bf215546Sopenharmony_ci return src1 >> src2; 79bf215546Sopenharmony_ci case OPC_ISHR: 80bf215546Sopenharmony_ci return (int32_t)src1 >> src2; 81bf215546Sopenharmony_ci case OPC_ROT: 82bf215546Sopenharmony_ci if (src2 & 0x80000000) 83bf215546Sopenharmony_ci return rotl64(src1, -*(int32_t *)&src2); 84bf215546Sopenharmony_ci else 85bf215546Sopenharmony_ci return rotl32(src1, src2); 86bf215546Sopenharmony_ci case OPC_MUL8: 87bf215546Sopenharmony_ci return (src1 & 0xff) * (src2 & 0xff); 88bf215546Sopenharmony_ci case OPC_MIN: 89bf215546Sopenharmony_ci return MIN2(src1, src2); 90bf215546Sopenharmony_ci case OPC_MAX: 91bf215546Sopenharmony_ci return MAX2(src1, src2); 92bf215546Sopenharmony_ci case OPC_CMP: 93bf215546Sopenharmony_ci if (src1 > src2) 94bf215546Sopenharmony_ci return 0x00; 95bf215546Sopenharmony_ci else if (src1 == src2) 96bf215546Sopenharmony_ci return 0x2b; 97bf215546Sopenharmony_ci return 0x1e; 98bf215546Sopenharmony_ci case OPC_MSB: 99bf215546Sopenharmony_ci if (!src2) 100bf215546Sopenharmony_ci return 0; 101bf215546Sopenharmony_ci return util_last_bit(src2) - 1; 102bf215546Sopenharmony_ci default: 103bf215546Sopenharmony_ci printf("unhandled alu opc: 0x%02x\n", opc); 104bf215546Sopenharmony_ci exit(1); 105bf215546Sopenharmony_ci } 106bf215546Sopenharmony_ci} 107bf215546Sopenharmony_ci 108bf215546Sopenharmony_ci/** 109bf215546Sopenharmony_ci * Helper to calculate load/store address based on LOAD_STORE_HI 110bf215546Sopenharmony_ci */ 111bf215546Sopenharmony_cistatic uintptr_t 112bf215546Sopenharmony_ciload_store_addr(struct emu *emu, unsigned gpr) 113bf215546Sopenharmony_ci{ 114bf215546Sopenharmony_ci EMU_CONTROL_REG(LOAD_STORE_HI); 115bf215546Sopenharmony_ci 116bf215546Sopenharmony_ci uintptr_t addr = emu_get_reg32(emu, &LOAD_STORE_HI); 117bf215546Sopenharmony_ci addr <<= 32; 118bf215546Sopenharmony_ci 119bf215546Sopenharmony_ci return addr + emu_get_gpr_reg(emu, gpr); 120bf215546Sopenharmony_ci} 121bf215546Sopenharmony_ci 122bf215546Sopenharmony_cistatic void 123bf215546Sopenharmony_ciemu_instr(struct emu *emu, afuc_instr *instr) 124bf215546Sopenharmony_ci{ 125bf215546Sopenharmony_ci uint32_t rem = emu_get_gpr_reg(emu, REG_REM); 126bf215546Sopenharmony_ci afuc_opc opc; 127bf215546Sopenharmony_ci bool rep; 128bf215546Sopenharmony_ci 129bf215546Sopenharmony_ci afuc_get_opc(instr, &opc, &rep); 130bf215546Sopenharmony_ci 131bf215546Sopenharmony_ci switch (opc) { 132bf215546Sopenharmony_ci case OPC_NOP: 133bf215546Sopenharmony_ci break; 134bf215546Sopenharmony_ci case OPC_ADD ... OPC_CMP: { 135bf215546Sopenharmony_ci uint32_t val = emu_alu(emu, opc, 136bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alui.src), 137bf215546Sopenharmony_ci instr->alui.uimm); 138bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->alui.dst, val); 139bf215546Sopenharmony_ci break; 140bf215546Sopenharmony_ci } 141bf215546Sopenharmony_ci case OPC_MOVI: { 142bf215546Sopenharmony_ci uint32_t val = instr->movi.uimm << instr->movi.shift; 143bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->movi.dst, val); 144bf215546Sopenharmony_ci break; 145bf215546Sopenharmony_ci } 146bf215546Sopenharmony_ci case OPC_ALU: { 147bf215546Sopenharmony_ci uint32_t val = emu_alu(emu, instr->alu.alu, 148bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src1), 149bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src2)); 150bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->alu.dst, val); 151bf215546Sopenharmony_ci 152bf215546Sopenharmony_ci if (instr->alu.xmov) { 153bf215546Sopenharmony_ci unsigned m = MIN2(instr->alu.xmov, rem); 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_ci assert(m <= 3); 156bf215546Sopenharmony_ci 157bf215546Sopenharmony_ci if (m == 1) { 158bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, --rem); 159bf215546Sopenharmony_ci emu_dump_state_change(emu); 160bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_DATA, 161bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src2)); 162bf215546Sopenharmony_ci } else if (m == 2) { 163bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, --rem); 164bf215546Sopenharmony_ci emu_dump_state_change(emu); 165bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_DATA, 166bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src2)); 167bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, --rem); 168bf215546Sopenharmony_ci emu_dump_state_change(emu); 169bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_DATA, 170bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src2)); 171bf215546Sopenharmony_ci } else if (m == 3) { 172bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, --rem); 173bf215546Sopenharmony_ci emu_dump_state_change(emu); 174bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_DATA, 175bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src2)); 176bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, --rem); 177bf215546Sopenharmony_ci emu_dump_state_change(emu); 178bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->alu.dst, 179bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src2)); 180bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, --rem); 181bf215546Sopenharmony_ci emu_dump_state_change(emu); 182bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_DATA, 183bf215546Sopenharmony_ci emu_get_gpr_reg(emu, instr->alu.src2)); 184bf215546Sopenharmony_ci } 185bf215546Sopenharmony_ci } 186bf215546Sopenharmony_ci break; 187bf215546Sopenharmony_ci } 188bf215546Sopenharmony_ci case OPC_CWRITE6: { 189bf215546Sopenharmony_ci uint32_t src1 = emu_get_gpr_reg(emu, instr->control.src1); 190bf215546Sopenharmony_ci uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2); 191bf215546Sopenharmony_ci 192bf215546Sopenharmony_ci if (instr->control.flags == 0x4) { 193bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm); 194bf215546Sopenharmony_ci } else if (instr->control.flags && !emu->quiet) { 195bf215546Sopenharmony_ci printf("unhandled flags: %x\n", instr->control.flags); 196bf215546Sopenharmony_ci } 197bf215546Sopenharmony_ci 198bf215546Sopenharmony_ci emu_set_control_reg(emu, src2 + instr->control.uimm, src1); 199bf215546Sopenharmony_ci break; 200bf215546Sopenharmony_ci } 201bf215546Sopenharmony_ci case OPC_CREAD6: { 202bf215546Sopenharmony_ci uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2); 203bf215546Sopenharmony_ci 204bf215546Sopenharmony_ci if (instr->control.flags == 0x4) { 205bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm); 206bf215546Sopenharmony_ci } else if (instr->control.flags && !emu->quiet) { 207bf215546Sopenharmony_ci printf("unhandled flags: %x\n", instr->control.flags); 208bf215546Sopenharmony_ci } 209bf215546Sopenharmony_ci 210bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->control.src1, 211bf215546Sopenharmony_ci emu_get_control_reg(emu, src2 + instr->control.uimm)); 212bf215546Sopenharmony_ci break; 213bf215546Sopenharmony_ci } 214bf215546Sopenharmony_ci case OPC_LOAD6: { 215bf215546Sopenharmony_ci uintptr_t addr = load_store_addr(emu, instr->control.src2) + 216bf215546Sopenharmony_ci instr->control.uimm; 217bf215546Sopenharmony_ci 218bf215546Sopenharmony_ci if (instr->control.flags == 0x4) { 219bf215546Sopenharmony_ci uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2); 220bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm); 221bf215546Sopenharmony_ci } else if (instr->control.flags && !emu->quiet) { 222bf215546Sopenharmony_ci printf("unhandled flags: %x\n", instr->control.flags); 223bf215546Sopenharmony_ci } 224bf215546Sopenharmony_ci 225bf215546Sopenharmony_ci uint32_t val = emu_mem_read_dword(emu, addr); 226bf215546Sopenharmony_ci 227bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->control.src1, val); 228bf215546Sopenharmony_ci 229bf215546Sopenharmony_ci break; 230bf215546Sopenharmony_ci } 231bf215546Sopenharmony_ci case OPC_STORE6: { 232bf215546Sopenharmony_ci uintptr_t addr = load_store_addr(emu, instr->control.src2) + 233bf215546Sopenharmony_ci instr->control.uimm; 234bf215546Sopenharmony_ci 235bf215546Sopenharmony_ci if (instr->control.flags == 0x4) { 236bf215546Sopenharmony_ci uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2); 237bf215546Sopenharmony_ci emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm); 238bf215546Sopenharmony_ci } else if (instr->control.flags && !emu->quiet) { 239bf215546Sopenharmony_ci printf("unhandled flags: %x\n", instr->control.flags); 240bf215546Sopenharmony_ci } 241bf215546Sopenharmony_ci 242bf215546Sopenharmony_ci uint32_t val = emu_get_gpr_reg(emu, instr->control.src1); 243bf215546Sopenharmony_ci 244bf215546Sopenharmony_ci emu_mem_write_dword(emu, addr, val); 245bf215546Sopenharmony_ci 246bf215546Sopenharmony_ci break; 247bf215546Sopenharmony_ci } 248bf215546Sopenharmony_ci case OPC_BRNEI ... OPC_BREQB: { 249bf215546Sopenharmony_ci uint32_t off = emu->gpr_regs.pc + instr->br.ioff; 250bf215546Sopenharmony_ci uint32_t src = emu_get_gpr_reg(emu, instr->br.src); 251bf215546Sopenharmony_ci 252bf215546Sopenharmony_ci if (opc == OPC_BRNEI) { 253bf215546Sopenharmony_ci if (src != instr->br.bit_or_imm) 254bf215546Sopenharmony_ci emu->branch_target = off; 255bf215546Sopenharmony_ci } else if (opc == OPC_BREQI) { 256bf215546Sopenharmony_ci if (src == instr->br.bit_or_imm) 257bf215546Sopenharmony_ci emu->branch_target = off; 258bf215546Sopenharmony_ci } else if (opc == OPC_BRNEB) { 259bf215546Sopenharmony_ci if (!(src & (1 << instr->br.bit_or_imm))) 260bf215546Sopenharmony_ci emu->branch_target = off; 261bf215546Sopenharmony_ci } else if (opc == OPC_BREQB) { 262bf215546Sopenharmony_ci if (src & (1 << instr->br.bit_or_imm)) 263bf215546Sopenharmony_ci emu->branch_target = off; 264bf215546Sopenharmony_ci } else { 265bf215546Sopenharmony_ci assert(0); 266bf215546Sopenharmony_ci } 267bf215546Sopenharmony_ci break; 268bf215546Sopenharmony_ci } 269bf215546Sopenharmony_ci case OPC_RET: { 270bf215546Sopenharmony_ci assert(emu->call_stack_idx > 0); 271bf215546Sopenharmony_ci 272bf215546Sopenharmony_ci /* counter-part to 'call' instruction, also has a delay slot: */ 273bf215546Sopenharmony_ci emu->branch_target = emu->call_stack[--emu->call_stack_idx]; 274bf215546Sopenharmony_ci 275bf215546Sopenharmony_ci break; 276bf215546Sopenharmony_ci } 277bf215546Sopenharmony_ci case OPC_CALL: { 278bf215546Sopenharmony_ci assert(emu->call_stack_idx < ARRAY_SIZE(emu->call_stack)); 279bf215546Sopenharmony_ci 280bf215546Sopenharmony_ci /* call looks to have same delay-slot behavior as branch/etc, so 281bf215546Sopenharmony_ci * presumably the return PC is two instructions later: 282bf215546Sopenharmony_ci */ 283bf215546Sopenharmony_ci emu->call_stack[emu->call_stack_idx++] = emu->gpr_regs.pc + 2; 284bf215546Sopenharmony_ci emu->branch_target = instr->call.uoff; 285bf215546Sopenharmony_ci 286bf215546Sopenharmony_ci break; 287bf215546Sopenharmony_ci } 288bf215546Sopenharmony_ci case OPC_WIN: { 289bf215546Sopenharmony_ci assert(!emu->branch_target); 290bf215546Sopenharmony_ci emu->run_mode = false; 291bf215546Sopenharmony_ci emu->waitin = true; 292bf215546Sopenharmony_ci break; 293bf215546Sopenharmony_ci } 294bf215546Sopenharmony_ci /* OPC_PREEMPTLEAVE6 */ 295bf215546Sopenharmony_ci case OPC_SETSECURE: { 296bf215546Sopenharmony_ci // TODO this acts like a conditional branch, but in which case 297bf215546Sopenharmony_ci // does it branch? 298bf215546Sopenharmony_ci break; 299bf215546Sopenharmony_ci } 300bf215546Sopenharmony_ci default: 301bf215546Sopenharmony_ci printf("unhandled opc: 0x%02x\n", opc); 302bf215546Sopenharmony_ci exit(1); 303bf215546Sopenharmony_ci } 304bf215546Sopenharmony_ci 305bf215546Sopenharmony_ci if (rep) { 306bf215546Sopenharmony_ci assert(rem > 0); 307bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, --rem); 308bf215546Sopenharmony_ci } 309bf215546Sopenharmony_ci} 310bf215546Sopenharmony_ci 311bf215546Sopenharmony_civoid 312bf215546Sopenharmony_ciemu_step(struct emu *emu) 313bf215546Sopenharmony_ci{ 314bf215546Sopenharmony_ci afuc_instr *instr = (void *)&emu->instrs[emu->gpr_regs.pc]; 315bf215546Sopenharmony_ci afuc_opc opc; 316bf215546Sopenharmony_ci bool rep; 317bf215546Sopenharmony_ci 318bf215546Sopenharmony_ci emu_main_prompt(emu); 319bf215546Sopenharmony_ci 320bf215546Sopenharmony_ci uint32_t branch_target = emu->branch_target; 321bf215546Sopenharmony_ci emu->branch_target = 0; 322bf215546Sopenharmony_ci 323bf215546Sopenharmony_ci bool waitin = emu->waitin; 324bf215546Sopenharmony_ci emu->waitin = false; 325bf215546Sopenharmony_ci 326bf215546Sopenharmony_ci afuc_get_opc(instr, &opc, &rep); 327bf215546Sopenharmony_ci 328bf215546Sopenharmony_ci if (rep) { 329bf215546Sopenharmony_ci do { 330bf215546Sopenharmony_ci if (!emu_get_gpr_reg(emu, REG_REM)) 331bf215546Sopenharmony_ci break; 332bf215546Sopenharmony_ci 333bf215546Sopenharmony_ci emu_clear_state_change(emu); 334bf215546Sopenharmony_ci emu_instr(emu, instr); 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci /* defer last state-change dump until after any 337bf215546Sopenharmony_ci * post-delay-slot handling below: 338bf215546Sopenharmony_ci */ 339bf215546Sopenharmony_ci if (emu_get_gpr_reg(emu, REG_REM)) 340bf215546Sopenharmony_ci emu_dump_state_change(emu); 341bf215546Sopenharmony_ci } while (true); 342bf215546Sopenharmony_ci } else { 343bf215546Sopenharmony_ci emu_clear_state_change(emu); 344bf215546Sopenharmony_ci emu_instr(emu, instr); 345bf215546Sopenharmony_ci } 346bf215546Sopenharmony_ci 347bf215546Sopenharmony_ci emu->gpr_regs.pc++; 348bf215546Sopenharmony_ci 349bf215546Sopenharmony_ci if (branch_target) { 350bf215546Sopenharmony_ci emu->gpr_regs.pc = branch_target; 351bf215546Sopenharmony_ci } 352bf215546Sopenharmony_ci 353bf215546Sopenharmony_ci if (waitin) { 354bf215546Sopenharmony_ci uint32_t hdr = emu_get_gpr_reg(emu, 1); 355bf215546Sopenharmony_ci uint32_t id, count; 356bf215546Sopenharmony_ci 357bf215546Sopenharmony_ci if (pkt_is_type4(hdr)) { 358bf215546Sopenharmony_ci id = afuc_pm4_id("PKT4"); 359bf215546Sopenharmony_ci count = type4_pkt_size(hdr); 360bf215546Sopenharmony_ci 361bf215546Sopenharmony_ci /* Possibly a hack, not sure what the hw actually 362bf215546Sopenharmony_ci * does here, but we want to mask out the pkt 363bf215546Sopenharmony_ci * type field from the hdr, so that PKT4 handler 364bf215546Sopenharmony_ci * doesn't see it and interpret it as part as the 365bf215546Sopenharmony_ci * register offset: 366bf215546Sopenharmony_ci */ 367bf215546Sopenharmony_ci emu->gpr_regs.val[1] &= 0x0fffffff; 368bf215546Sopenharmony_ci } else if (pkt_is_type7(hdr)) { 369bf215546Sopenharmony_ci id = cp_type7_opcode(hdr); 370bf215546Sopenharmony_ci count = type7_pkt_size(hdr); 371bf215546Sopenharmony_ci } else { 372bf215546Sopenharmony_ci printf("Invalid opcode: 0x%08x\n", hdr); 373bf215546Sopenharmony_ci exit(1); /* GPU goes *boom* */ 374bf215546Sopenharmony_ci } 375bf215546Sopenharmony_ci 376bf215546Sopenharmony_ci assert(id < ARRAY_SIZE(emu->jmptbl)); 377bf215546Sopenharmony_ci 378bf215546Sopenharmony_ci emu_set_gpr_reg(emu, REG_REM, count); 379bf215546Sopenharmony_ci emu->gpr_regs.pc = emu->jmptbl[id]; 380bf215546Sopenharmony_ci } 381bf215546Sopenharmony_ci 382bf215546Sopenharmony_ci emu_dump_state_change(emu); 383bf215546Sopenharmony_ci} 384bf215546Sopenharmony_ci 385bf215546Sopenharmony_civoid 386bf215546Sopenharmony_ciemu_run_bootstrap(struct emu *emu) 387bf215546Sopenharmony_ci{ 388bf215546Sopenharmony_ci EMU_CONTROL_REG(PACKET_TABLE_WRITE_ADDR); 389bf215546Sopenharmony_ci 390bf215546Sopenharmony_ci emu->quiet = true; 391bf215546Sopenharmony_ci emu->run_mode = true; 392bf215546Sopenharmony_ci 393bf215546Sopenharmony_ci while (emu_get_reg32(emu, &PACKET_TABLE_WRITE_ADDR) < 0x80) { 394bf215546Sopenharmony_ci emu_step(emu); 395bf215546Sopenharmony_ci } 396bf215546Sopenharmony_ci} 397bf215546Sopenharmony_ci 398bf215546Sopenharmony_ci 399bf215546Sopenharmony_cistatic void 400bf215546Sopenharmony_cicheck_access(struct emu *emu, uintptr_t gpuaddr, unsigned sz) 401bf215546Sopenharmony_ci{ 402bf215546Sopenharmony_ci if ((gpuaddr % sz) != 0) { 403bf215546Sopenharmony_ci printf("unaligned access fault: %p\n", (void *)gpuaddr); 404bf215546Sopenharmony_ci exit(1); 405bf215546Sopenharmony_ci } 406bf215546Sopenharmony_ci 407bf215546Sopenharmony_ci if ((gpuaddr + sz) >= EMU_MEMORY_SIZE) { 408bf215546Sopenharmony_ci printf("iova fault: %p\n", (void *)gpuaddr); 409bf215546Sopenharmony_ci exit(1); 410bf215546Sopenharmony_ci } 411bf215546Sopenharmony_ci} 412bf215546Sopenharmony_ci 413bf215546Sopenharmony_ciuint32_t 414bf215546Sopenharmony_ciemu_mem_read_dword(struct emu *emu, uintptr_t gpuaddr) 415bf215546Sopenharmony_ci{ 416bf215546Sopenharmony_ci check_access(emu, gpuaddr, 4); 417bf215546Sopenharmony_ci return *(uint32_t *)(emu->gpumem + gpuaddr); 418bf215546Sopenharmony_ci} 419bf215546Sopenharmony_ci 420bf215546Sopenharmony_cistatic void 421bf215546Sopenharmony_cimem_write_dword(struct emu *emu, uintptr_t gpuaddr, uint32_t val) 422bf215546Sopenharmony_ci{ 423bf215546Sopenharmony_ci check_access(emu, gpuaddr, 4); 424bf215546Sopenharmony_ci *(uint32_t *)(emu->gpumem + gpuaddr) = val; 425bf215546Sopenharmony_ci} 426bf215546Sopenharmony_ci 427bf215546Sopenharmony_civoid 428bf215546Sopenharmony_ciemu_mem_write_dword(struct emu *emu, uintptr_t gpuaddr, uint32_t val) 429bf215546Sopenharmony_ci{ 430bf215546Sopenharmony_ci mem_write_dword(emu, gpuaddr, val); 431bf215546Sopenharmony_ci assert(emu->gpumem_written == ~0); 432bf215546Sopenharmony_ci emu->gpumem_written = gpuaddr; 433bf215546Sopenharmony_ci} 434bf215546Sopenharmony_ci 435bf215546Sopenharmony_civoid 436bf215546Sopenharmony_ciemu_init(struct emu *emu) 437bf215546Sopenharmony_ci{ 438bf215546Sopenharmony_ci emu->gpumem = mmap(NULL, EMU_MEMORY_SIZE, 439bf215546Sopenharmony_ci PROT_READ | PROT_WRITE, 440bf215546Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, 441bf215546Sopenharmony_ci 0, 0); 442bf215546Sopenharmony_ci if (emu->gpumem == MAP_FAILED) { 443bf215546Sopenharmony_ci printf("Could not allocate GPU memory: %s\n", strerror(errno)); 444bf215546Sopenharmony_ci exit(1); 445bf215546Sopenharmony_ci } 446bf215546Sopenharmony_ci 447bf215546Sopenharmony_ci /* Copy the instructions into GPU memory: */ 448bf215546Sopenharmony_ci for (unsigned i = 0; i < emu->sizedwords; i++) { 449bf215546Sopenharmony_ci mem_write_dword(emu, EMU_INSTR_BASE + (4 * i), emu->instrs[i]); 450bf215546Sopenharmony_ci } 451bf215546Sopenharmony_ci 452bf215546Sopenharmony_ci EMU_GPU_REG(CP_SQE_INSTR_BASE); 453bf215546Sopenharmony_ci EMU_GPU_REG(CP_LPAC_SQE_INSTR_BASE); 454bf215546Sopenharmony_ci 455bf215546Sopenharmony_ci /* Setup the address of the SQE fw, just use the normal CPU ptr address: */ 456bf215546Sopenharmony_ci if (emu->lpac) { 457bf215546Sopenharmony_ci emu_set_reg64(emu, &CP_LPAC_SQE_INSTR_BASE, EMU_INSTR_BASE); 458bf215546Sopenharmony_ci } else { 459bf215546Sopenharmony_ci emu_set_reg64(emu, &CP_SQE_INSTR_BASE, EMU_INSTR_BASE); 460bf215546Sopenharmony_ci } 461bf215546Sopenharmony_ci 462bf215546Sopenharmony_ci if (emu->gpu_id == 660) { 463bf215546Sopenharmony_ci emu_set_control_reg(emu, 0, 3 << 28); 464bf215546Sopenharmony_ci } else if (emu->gpu_id == 650) { 465bf215546Sopenharmony_ci emu_set_control_reg(emu, 0, 1 << 28); 466bf215546Sopenharmony_ci } 467bf215546Sopenharmony_ci} 468bf215546Sopenharmony_ci 469bf215546Sopenharmony_civoid 470bf215546Sopenharmony_ciemu_fini(struct emu *emu) 471bf215546Sopenharmony_ci{ 472bf215546Sopenharmony_ci uint32_t *instrs = emu->instrs; 473bf215546Sopenharmony_ci unsigned sizedwords = emu->sizedwords; 474bf215546Sopenharmony_ci unsigned gpu_id = emu->gpu_id; 475bf215546Sopenharmony_ci 476bf215546Sopenharmony_ci munmap(emu->gpumem, EMU_MEMORY_SIZE); 477bf215546Sopenharmony_ci memset(emu, 0, sizeof(*emu)); 478bf215546Sopenharmony_ci 479bf215546Sopenharmony_ci emu->instrs = instrs; 480bf215546Sopenharmony_ci emu->sizedwords = sizedwords; 481bf215546Sopenharmony_ci emu->gpu_id = gpu_id; 482bf215546Sopenharmony_ci} 483