1/* 2 * Copyright (c) 2017 Rob Clark <robdclark@gmail.com> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24#include <assert.h> 25#include <err.h> 26#include <fcntl.h> 27#include <getopt.h> 28#include <stdarg.h> 29#include <stdbool.h> 30#include <stdint.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <unistd.h> 35 36#include "util/macros.h" 37#include "afuc.h" 38#include "asm.h" 39#include "parser.h" 40#include "util.h" 41 42int gpuver; 43 44/* bit lame to hard-code max but fw sizes are small */ 45static struct asm_instruction instructions[0x2000]; 46static unsigned num_instructions; 47 48static struct asm_label labels[0x512]; 49static unsigned num_labels; 50 51struct asm_instruction * 52next_instr(int tok) 53{ 54 struct asm_instruction *ai = &instructions[num_instructions++]; 55 assert(num_instructions < ARRAY_SIZE(instructions)); 56 ai->tok = tok; 57 return ai; 58} 59 60void 61decl_label(const char *str) 62{ 63 struct asm_label *label = &labels[num_labels++]; 64 65 assert(num_labels < ARRAY_SIZE(labels)); 66 67 label->offset = num_instructions; 68 label->label = str; 69} 70 71static int 72resolve_label(const char *str) 73{ 74 int i; 75 76 for (i = 0; i < num_labels; i++) { 77 struct asm_label *label = &labels[i]; 78 79 if (!strcmp(str, label->label)) { 80 return label->offset; 81 } 82 } 83 84 fprintf(stderr, "Undeclared label: %s\n", str); 85 exit(2); 86} 87 88static afuc_opc 89tok2alu(int tok) 90{ 91 switch (tok) { 92 case T_OP_ADD: 93 return OPC_ADD; 94 case T_OP_ADDHI: 95 return OPC_ADDHI; 96 case T_OP_SUB: 97 return OPC_SUB; 98 case T_OP_SUBHI: 99 return OPC_SUBHI; 100 case T_OP_AND: 101 return OPC_AND; 102 case T_OP_OR: 103 return OPC_OR; 104 case T_OP_XOR: 105 return OPC_XOR; 106 case T_OP_NOT: 107 return OPC_NOT; 108 case T_OP_SHL: 109 return OPC_SHL; 110 case T_OP_USHR: 111 return OPC_USHR; 112 case T_OP_ISHR: 113 return OPC_ISHR; 114 case T_OP_ROT: 115 return OPC_ROT; 116 case T_OP_MUL8: 117 return OPC_MUL8; 118 case T_OP_MIN: 119 return OPC_MIN; 120 case T_OP_MAX: 121 return OPC_MAX; 122 case T_OP_CMP: 123 return OPC_CMP; 124 case T_OP_MSB: 125 return OPC_MSB; 126 default: 127 assert(0); 128 return -1; 129 } 130} 131 132static void 133emit_instructions(int outfd) 134{ 135 int i; 136 137 /* there is an extra 0x00000000 which kernel strips off.. we could 138 * perhaps use it for versioning. 139 */ 140 i = 0; 141 write(outfd, &i, 4); 142 143 for (i = 0; i < num_instructions; i++) { 144 struct asm_instruction *ai = &instructions[i]; 145 afuc_instr instr = {0}; 146 afuc_opc opc; 147 148 /* special case, 2nd dword is patched up w/ # of instructions 149 * (ie. offset of jmptbl) 150 */ 151 if (i == 1) { 152 assert(ai->is_literal); 153 ai->literal &= ~0xffff; 154 ai->literal |= num_instructions; 155 } 156 157 if (ai->is_literal) { 158 write(outfd, &ai->literal, 4); 159 continue; 160 } 161 162 switch (ai->tok) { 163 case T_OP_NOP: 164 opc = OPC_NOP; 165 if (gpuver >= 6) 166 instr.pad = 0x1000000; 167 break; 168 case T_OP_ADD: 169 case T_OP_ADDHI: 170 case T_OP_SUB: 171 case T_OP_SUBHI: 172 case T_OP_AND: 173 case T_OP_OR: 174 case T_OP_XOR: 175 case T_OP_NOT: 176 case T_OP_SHL: 177 case T_OP_USHR: 178 case T_OP_ISHR: 179 case T_OP_ROT: 180 case T_OP_MUL8: 181 case T_OP_MIN: 182 case T_OP_MAX: 183 case T_OP_CMP: 184 case T_OP_MSB: 185 if (ai->has_immed) { 186 /* MSB overlaps with STORE */ 187 assert(ai->tok != T_OP_MSB); 188 if (ai->xmov) { 189 fprintf(stderr, 190 "ALU instruction cannot have immediate and xmov\n"); 191 exit(1); 192 } 193 opc = tok2alu(ai->tok); 194 instr.alui.dst = ai->dst; 195 instr.alui.src = ai->src1; 196 instr.alui.uimm = ai->immed; 197 } else { 198 opc = OPC_ALU; 199 instr.alu.dst = ai->dst; 200 instr.alu.src1 = ai->src1; 201 instr.alu.src2 = ai->src2; 202 instr.alu.xmov = ai->xmov; 203 instr.alu.alu = tok2alu(ai->tok); 204 } 205 break; 206 case T_OP_MOV: 207 /* move can either be encoded as movi (ie. move w/ immed) or 208 * an alu instruction 209 */ 210 if ((ai->has_immed || ai->label) && ai->xmov) { 211 fprintf(stderr, "ALU instruction cannot have immediate and xmov\n"); 212 exit(1); 213 } 214 if (ai->has_immed) { 215 opc = OPC_MOVI; 216 instr.movi.dst = ai->dst; 217 instr.movi.uimm = ai->immed; 218 instr.movi.shift = ai->shift; 219 } else if (ai->label) { 220 /* mov w/ a label is just an alias for an immediate, this 221 * is useful to load the address of a constant table into 222 * a register: 223 */ 224 opc = OPC_MOVI; 225 instr.movi.dst = ai->dst; 226 instr.movi.uimm = resolve_label(ai->label); 227 instr.movi.shift = ai->shift; 228 } else { 229 /* encode as: or $dst, $00, $src */ 230 opc = OPC_ALU; 231 instr.alu.dst = ai->dst; 232 instr.alu.src1 = 0x00; /* $00 reads-back 0 */ 233 instr.alu.src2 = ai->src1; 234 instr.alu.xmov = ai->xmov; 235 instr.alu.alu = OPC_OR; 236 } 237 break; 238 case T_OP_CWRITE: 239 case T_OP_CREAD: 240 case T_OP_STORE: 241 case T_OP_LOAD: 242 if (gpuver >= 6) { 243 if (ai->tok == T_OP_CWRITE) { 244 opc = OPC_CWRITE6; 245 } else if (ai->tok == T_OP_CREAD) { 246 opc = OPC_CREAD6; 247 } else if (ai->tok == T_OP_STORE) { 248 opc = OPC_STORE6; 249 } else if (ai->tok == T_OP_LOAD) { 250 opc = OPC_LOAD6; 251 } 252 } else { 253 if (ai->tok == T_OP_CWRITE) { 254 opc = OPC_CWRITE5; 255 } else if (ai->tok == T_OP_CREAD) { 256 opc = OPC_CREAD5; 257 } else if (ai->tok == T_OP_STORE || ai->tok == T_OP_LOAD) { 258 fprintf(stderr, "load and store do not exist on a5xx\n"); 259 exit(1); 260 } 261 } 262 instr.control.src1 = ai->src1; 263 instr.control.src2 = ai->src2; 264 instr.control.flags = ai->bit; 265 instr.control.uimm = ai->immed; 266 break; 267 case T_OP_BRNE: 268 case T_OP_BREQ: 269 if (ai->has_immed) { 270 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEI : OPC_BREQI; 271 instr.br.bit_or_imm = ai->immed; 272 } else { 273 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEB : OPC_BREQB; 274 instr.br.bit_or_imm = ai->bit; 275 } 276 instr.br.src = ai->src1; 277 instr.br.ioff = resolve_label(ai->label) - i; 278 break; 279 case T_OP_RET: 280 opc = OPC_RET; 281 break; 282 case T_OP_IRET: 283 opc = OPC_RET; 284 instr.ret.interrupt = 1; 285 break; 286 case T_OP_CALL: 287 opc = OPC_CALL; 288 instr.call.uoff = resolve_label(ai->label); 289 break; 290 case T_OP_PREEMPTLEAVE: 291 opc = OPC_PREEMPTLEAVE6; 292 instr.call.uoff = resolve_label(ai->label); 293 break; 294 case T_OP_SETSECURE: 295 opc = OPC_SETSECURE; 296 if (resolve_label(ai->label) != i + 3) { 297 fprintf(stderr, "jump label %s is incorrect for setsecure\n", 298 ai->label); 299 exit(1); 300 } 301 if (ai->src1 != 0x2) { 302 fprintf(stderr, "source for setsecure must be $02\n"); 303 exit(1); 304 } 305 break; 306 case T_OP_JUMP: 307 /* encode jump as: brne $00, b0, #label */ 308 opc = OPC_BRNEB; 309 instr.br.bit_or_imm = 0; 310 instr.br.src = 0x00; /* $00 reads-back 0.. compare to 0 */ 311 instr.br.ioff = resolve_label(ai->label) - i; 312 break; 313 case T_OP_WAITIN: 314 opc = OPC_WIN; 315 break; 316 default: 317 unreachable(""); 318 } 319 320 afuc_set_opc(&instr, opc, ai->rep); 321 322 write(outfd, &instr, 4); 323 } 324} 325 326unsigned 327parse_control_reg(const char *name) 328{ 329 /* skip leading "@" */ 330 return afuc_control_reg(name + 1); 331} 332 333static void 334emit_jumptable(int outfd) 335{ 336 uint32_t jmptable[0x80] = {0}; 337 int i; 338 339 for (i = 0; i < num_labels; i++) { 340 struct asm_label *label = &labels[i]; 341 int id = afuc_pm4_id(label->label); 342 343 /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */ 344 if (id < 0) { 345 if (sscanf(label->label, "UNKN%d", &id) != 1) { 346 /* if still not found, must not belong in jump-table: */ 347 continue; 348 } 349 } 350 351 jmptable[id] = label->offset; 352 } 353 354 write(outfd, jmptable, sizeof(jmptable)); 355} 356 357static void 358usage(void) 359{ 360 fprintf(stderr, "Usage:\n" 361 "\tasm [-g GPUVER] filename.asm filename.fw\n" 362 "\t\t-g - specify GPU version (5, etc)\n"); 363 exit(2); 364} 365 366int 367main(int argc, char **argv) 368{ 369 FILE *in; 370 char *file, *outfile; 371 int c, ret, outfd; 372 373 /* Argument parsing: */ 374 while ((c = getopt(argc, argv, "g:")) != -1) { 375 switch (c) { 376 case 'g': 377 gpuver = atoi(optarg); 378 break; 379 default: 380 usage(); 381 } 382 } 383 384 if (optind >= (argc + 1)) { 385 fprintf(stderr, "no file specified!\n"); 386 usage(); 387 } 388 389 file = argv[optind]; 390 outfile = argv[optind + 1]; 391 392 outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 393 if (outfd < 0) { 394 fprintf(stderr, "could not open \"%s\"\n", outfile); 395 usage(); 396 } 397 398 in = fopen(file, "r"); 399 if (!in) { 400 fprintf(stderr, "could not open \"%s\"\n", file); 401 usage(); 402 } 403 404 yyset_in(in); 405 406 /* if gpu version not specified, infer from filename: */ 407 if (!gpuver) { 408 if (strstr(file, "a5")) { 409 gpuver = 5; 410 } else if (strstr(file, "a6")) { 411 gpuver = 6; 412 } 413 } 414 415 ret = afuc_util_init(gpuver, false); 416 if (ret < 0) { 417 usage(); 418 } 419 420 ret = yyparse(); 421 if (ret) { 422 fprintf(stderr, "parse failed: %d\n", ret); 423 return ret; 424 } 425 426 emit_instructions(outfd); 427 emit_jumptable(outfd); 428 429 close(outfd); 430 431 return 0; 432} 433