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 } else { 252 unreachable(""); 253 } 254 } else { 255 if (ai->tok == T_OP_CWRITE) { 256 opc = OPC_CWRITE5; 257 } else if (ai->tok == T_OP_CREAD) { 258 opc = OPC_CREAD5; 259 } else if (ai->tok == T_OP_STORE || ai->tok == T_OP_LOAD) { 260 fprintf(stderr, "load and store do not exist on a5xx\n"); 261 exit(1); 262 } else { 263 unreachable(""); 264 } 265 } 266 instr.control.src1 = ai->src1; 267 instr.control.src2 = ai->src2; 268 instr.control.flags = ai->bit; 269 instr.control.uimm = ai->immed; 270 break; 271 case T_OP_BRNE: 272 case T_OP_BREQ: 273 if (ai->has_immed) { 274 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEI : OPC_BREQI; 275 instr.br.bit_or_imm = ai->immed; 276 } else { 277 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEB : OPC_BREQB; 278 instr.br.bit_or_imm = ai->bit; 279 } 280 instr.br.src = ai->src1; 281 instr.br.ioff = resolve_label(ai->label) - i; 282 break; 283 case T_OP_RET: 284 opc = OPC_RET; 285 break; 286 case T_OP_IRET: 287 opc = OPC_RET; 288 instr.ret.interrupt = 1; 289 break; 290 case T_OP_CALL: 291 opc = OPC_CALL; 292 instr.call.uoff = resolve_label(ai->label); 293 break; 294 case T_OP_PREEMPTLEAVE: 295 opc = OPC_PREEMPTLEAVE6; 296 instr.call.uoff = resolve_label(ai->label); 297 break; 298 case T_OP_SETSECURE: 299 opc = OPC_SETSECURE; 300 if (resolve_label(ai->label) != i + 3) { 301 fprintf(stderr, "jump label %s is incorrect for setsecure\n", 302 ai->label); 303 exit(1); 304 } 305 if (ai->src1 != 0x2) { 306 fprintf(stderr, "source for setsecure must be $02\n"); 307 exit(1); 308 } 309 break; 310 case T_OP_JUMP: 311 /* encode jump as: brne $00, b0, #label */ 312 opc = OPC_BRNEB; 313 instr.br.bit_or_imm = 0; 314 instr.br.src = 0x00; /* $00 reads-back 0.. compare to 0 */ 315 instr.br.ioff = resolve_label(ai->label) - i; 316 break; 317 case T_OP_WAITIN: 318 opc = OPC_WIN; 319 break; 320 default: 321 unreachable(""); 322 } 323 324 afuc_set_opc(&instr, opc, ai->rep); 325 326 write(outfd, &instr, 4); 327 } 328} 329 330unsigned 331parse_control_reg(const char *name) 332{ 333 /* skip leading "@" */ 334 return afuc_control_reg(name + 1); 335} 336 337static void 338emit_jumptable(int outfd) 339{ 340 uint32_t jmptable[0x80] = {0}; 341 int i; 342 343 for (i = 0; i < num_labels; i++) { 344 struct asm_label *label = &labels[i]; 345 int id = afuc_pm4_id(label->label); 346 347 /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */ 348 if (id < 0) { 349 if (sscanf(label->label, "UNKN%d", &id) != 1) { 350 /* if still not found, must not belong in jump-table: */ 351 continue; 352 } 353 } 354 355 jmptable[id] = label->offset; 356 } 357 358 write(outfd, jmptable, sizeof(jmptable)); 359} 360 361static void 362usage(void) 363{ 364 fprintf(stderr, "Usage:\n" 365 "\tasm [-g GPUVER] filename.asm filename.fw\n" 366 "\t\t-g - specify GPU version (5, etc)\n"); 367 exit(2); 368} 369 370int 371main(int argc, char **argv) 372{ 373 FILE *in; 374 char *file, *outfile; 375 int c, ret, outfd; 376 377 /* Argument parsing: */ 378 while ((c = getopt(argc, argv, "g:")) != -1) { 379 switch (c) { 380 case 'g': 381 gpuver = atoi(optarg); 382 break; 383 default: 384 usage(); 385 } 386 } 387 388 if (optind >= (argc + 1)) { 389 fprintf(stderr, "no file specified!\n"); 390 usage(); 391 } 392 393 file = argv[optind]; 394 outfile = argv[optind + 1]; 395 396 outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 397 if (outfd < 0) { 398 fprintf(stderr, "could not open \"%s\"\n", outfile); 399 usage(); 400 } 401 402 in = fopen(file, "r"); 403 if (!in) { 404 fprintf(stderr, "could not open \"%s\"\n", file); 405 usage(); 406 } 407 408 yyset_in(in); 409 410 /* if gpu version not specified, infer from filename: */ 411 if (!gpuver) { 412 if (strstr(file, "a5")) { 413 gpuver = 5; 414 } else if (strstr(file, "a6")) { 415 gpuver = 6; 416 } 417 } 418 419 ret = afuc_util_init(gpuver, false); 420 if (ret < 0) { 421 usage(); 422 } 423 424 ret = yyparse(); 425 if (ret) { 426 fprintf(stderr, "parse failed: %d\n", ret); 427 return ret; 428 } 429 430 emit_instructions(outfd); 431 emit_jumptable(outfd); 432 433 close(outfd); 434 435 return 0; 436} 437