1 /* $NetBSD: disassem.c,v 1.42 2020/12/01 02:48:28 rin Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Mark Brinicombe. 5 * Copyright (c) 1996 Brini. 6 * 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Brini. 20 * 4. The name of the company nor the name of the author may be used to 21 * endorse or promote products derived from this software without specific 22 * prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * RiscBSD kernel project 37 * 38 * db_disasm.c 39 * 40 * Kernel disassembler 41 * 42 * Created : 10/02/96 43 * 44 * Structured after the sparc/sparc/db_disasm.c by David S. Miller & 45 * Paul Kranenburg 46 * 47 * This code is not complete. Not all instructions are disassembled. 48 */ 49 50 #include <sys/param.h> 51 52 __KERNEL_RCSID(0, "$NetBSD: disassem.c,v 1.42 2020/12/01 02:48:28 rin Exp $"); 53 54 #include <sys/systm.h> 55 56 #include <arch/arm/arm/disassem.h> 57 #include <arm/armreg.h> 58 59 #ifndef _KERNEL 60 #include <stdio.h> 61 #endif 62 63 /* 64 * General instruction format 65 * 66 * insn[cc][mod] [operands] 67 * 68 * Those fields with an uppercase format code indicate that the field 69 * follows directly after the instruction before the separator i.e. 70 * they modify the instruction rather than just being an operand to 71 * the instruction. The only exception is the writeback flag which 72 * follows a operand. 73 * 74 * !c - cps flags and mode 75 * !d - debug option (bit 0-3) 76 * !l - dmb/dsb limitation 77 * !m - mode 78 * 2 - print Operand 2 of a data processing instruction 79 * a - address operand of ldr/str instruction 80 * b - branch address 81 * c - comment field bits(0-23) 82 * d - destination register (bits 12-15) 83 * e - address operand of ldrx/strx instruction 84 * f - 1st fp operand (register) (bits 12-14) 85 * g - 2nd fp operand (register) (bits 16-18) 86 * h - 3rd fp operand (register/immediate) (bits 0-4) 87 * i - lsb operand (bits 7-11) 88 * j - msb operand (bits 6,7,12-14) 89 * k - breakpoint comment (bits 0-3, 8-19) 90 * l - register list for ldm/stm instruction 91 * m - m register (bits 0-3) 92 * n - n register (bits 16-19) 93 * o - indirect register rn (bits 16-19) (used by swap) 94 * p - saved or current status register 95 * q - neon N register (7, 19-16) 96 * r - width minus 1 (bits 16-20) 97 * s - s register (bits 8-11) 98 * t - thumb branch address (bits 24, 0-23) 99 * u - neon M register (5, 3-0) 100 * v - co-processor data transfer registers + addressing mode 101 * w - neon D register (22, 15-12) 102 * x - instruction in hex 103 * y - co-processor data processing registers 104 * z - co-processor register transfer registers 105 * C - cps effect 106 * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN 107 * F - PSR transfer fields 108 * I - NEON operand size 109 * L - co-processor transfer size 110 * N - quad neon operand 111 * P - fp precision 112 * Q - fp precision (for ldf/stf) 113 * R - fp rounding 114 * S - set status flag 115 * U - neon unsigned. 116 * W - writeback flag 117 * X - block transfer type 118 * Y - block transfer type (r13 base) 119 * Z - 16-bit value (movw,movt) 120 * # - co-processor number 121 */ 122 123 struct arm32_insn { 124 u_int mask; 125 u_int pattern; 126 const char* name; 127 const char* format; 128 }; 129 130 static const struct arm32_insn arm32_i[] = { 131 /* A5.7 Unconditional instructions */ 132 /* 133 * A5.7.1 Memory hints, Advanced SIMD instructions, and 134 * miscellaneous instructions 135 */ 136 { 0xfff10020, 0xf1000000, "cps", "C!c" }, 137 { 0xfff102f0, 0xf1010000, "setend\tle", "" }, 138 { 0xfff102f0, 0xf1010200, "setend\tbe", "" }, 139 /* pli */ 140 /* pld */ 141 { 0xffffffff, 0xf57ff01f, "clrex", "" }, 142 { 0xfffffff0, 0xf57ff040, "dsb", "!l" }, 143 { 0xfffffff0, 0xf57ff050, "dmb", "!l" }, 144 { 0xfffffff0, 0xf57ff060, "isb", "" }, 145 /* pli */ 146 /* pld */ 147 148 //{ 0x0e100000, 0x08000000, "stm", "XnWl" }, 149 { 0xfe5fffe0, 0xf84d0500, "srs", "XnW!m" }, 150 { 0xfe50ffff, 0xf8100a00, "rfe", "XnW" }, 151 { 0xfe000000, 0xfa000000, "blx", "t" }, /* Before b and bl */ 152 { 0x0ff00000, 0x0c400000, "mcrr", "#&" }, 153 { 0x0ff00000, 0x0c500000, "mrrc", "#&" }, 154 { 0xfe100090, 0xfc000000, "stc2", "L#v" }, 155 { 0x0e100090, 0x0c000000, "stc", "L#v" }, 156 { 0xfe100090, 0xfc100000, "ldc2", "L#v" }, 157 { 0x0e100090, 0x0c100000, "ldc", "L#v" }, 158 { 0xff000010, 0xfe000000, "cdp2", "#y" }, 159 { 0x0f000010, 0x0e000000, "cdp", "#y" }, 160 { 0xff100010, 0xfe000010, "mcr2", "#z" }, 161 { 0x0f100010, 0x0e000010, "mcr", "#z" }, 162 { 0xff100010, 0xfe100010, "mrc2", "#z" }, 163 { 0x0f100010, 0x0e100010, "mrc", "#z" }, 164 165 /* A5.4 Media instructions */ 166 { 0x0fe00070, 0x07c00050, "sbfx", "dmir" }, 167 { 0x0fe0007f, 0x07c0001f, "bfc", "dij" }, 168 { 0x0fe00070, 0x07c00010, "bfi", "dmij" }, 169 { 0x0fe00070, 0x07e00050, "ubfx", "dmir" }, 170 { 0xfff000f0, 0xe70000f0, "und", "x" }, /* Special immediate? */ 171 172 { 0x0e000010, 0x06000010, "und", "x" }, /* Remove when done with media */ 173 174 { 0x0d700000, 0x04200000, "strt", "daW" }, 175 { 0x0d700000, 0x04300000, "ldrt", "daW" }, 176 { 0x0d700000, 0x04600000, "strbt", "daW" }, 177 { 0x0d700000, 0x04700000, "ldrbt", "daW" }, 178 179 { 0x0c500000, 0x04000000, "str", "daW" }, 180 { 0x0c500000, 0x04100000, "ldr", "daW" }, 181 { 0x0c500000, 0x04400000, "strb", "daW" }, 182 { 0x0c500000, 0x04500000, "ldrb", "daW" }, 183 184 185 /* A5.5 Branch, branch with link, and block data transfer */ 186 { 0x0fff0000, 0x092d0000, "push", "l" }, /* separate out r13 base */ 187 { 0x0fff0000, 0x08bd0000, "pop", "l" }, /* separate out r13 base */ 188 { 0x0e1f0000, 0x080d0000, "stm", "YnWl" },/* separate out r13 base */ 189 { 0x0e1f0000, 0x081d0000, "ldm", "YnWl" },/* separate out r13 base */ 190 { 0x0e100000, 0x08000000, "stm", "XnWl" }, 191 { 0x0e100000, 0x08100000, "ldm", "XnWl" }, 192 { 0x0f000000, 0x0a000000, "b", "b" }, 193 { 0x0f000000, 0x0b000000, "bl", "b" }, 194 195 { 0x0fffffff, 0x0ff00000, "imb", "c" }, /* Before swi */ 196 { 0x0fffffff, 0x0ff00001, "imbrange", "c" }, /* Before swi */ 197 { 0x0f000000, 0x0f000000, "swi", "c" }, 198 199 /* 200 * A5.2 Data-process and miscellaneous instructions 201 */ 202 203 /* A5.2 exceptions */ 204 205 /* A5.2.7 Halfword multiply and multiply accumulate */ 206 207 /* A5.2.9 Extra load/store instructions, unprivileged */ 208 209 { 0x0f3000f0, 0x002000b0, "strht", "de" }, 210 { 0x0f3000f0, 0x003000b0, "ldrht", "de" }, 211 { 0x0f3000f0, 0x003000d0, "ldrsbt", "de" }, 212 { 0x0f3000f0, 0x003000f0, "ldrsht", "de" }, 213 214 /* A5.2.8 Extra load/store instructions */ 215 216 { 0x0e1000f0, 0x000000b0, "strh", "de" }, 217 { 0x0e1000f0, 0x001000b0, "ldrh", "de" }, 218 219 { 0x0e1000f0, 0x000000d0, "ldrd", "de" }, 220 { 0x0e1000f0, 0x001000d0, "ldrsb", "de" }, 221 222 { 0x0e1000f0, 0x000000f0, "strd", "de" }, 223 { 0x0e1000f0, 0x001000f0, "ldrsh", "de" }, 224 225 /* A5.2.11 MSR (immediate), and hints */ 226 { 0x0fffffff, 0x0320f000, "nop", "" }, 227 { 0x0fffffff, 0x0320f001, "yield", "" }, 228 { 0x0fffffff, 0x0320f002, "wfe", "" }, 229 { 0x0fffffff, 0x0320f003, "wfi", "" }, 230 { 0x0fffffff, 0x0320f004, "sev", "" }, 231 { 0x0ffffff0, 0x0320f0f0, "dbg", "!d" }, 232 233 /* A5.2.12 Miscellaneous instructions - before data processing */ 234 235 { 0x0fbf0fff, 0x010f0000, "mrs", "dp" }, /* A8.8.109, B9.3.8 */ 236 { 0x0fb00eff, 0x01000200, "mrs", "c" }, /* XXXNH: B9.3.9 */ 237 { 0x0fb0fff0, 0x0120f000, "msr", "pFm" }, 238 { 0x0fe0f000, 0x0320f000, "msr", "pF2" }, 239 240 { 0x0ffffff0, 0x012fff10, "bx", "m" }, 241 { 0x0fff0ff0, 0x016f0f10, "clz", "dm" }, 242 /* bxj */ 243 { 0x0ffffff0, 0x012fff30, "blx", "m" }, 244 /* saturating */ 245 /* eret */ 246 { 0xfff000f0, 0xe1200070, "bkpt", "k" }, 247 /* hvc */ 248 /* smc */ 249 250 { 0x0ff00000, 0x03000000, "movw", "dZ" }, 251 { 0x0ff00000, 0x03400000, "movt", "dZ" }, 252 253 /* A5.2.10 Synchronisation primitives */ 254 { 0x0ff00ff0, 0x01000090, "swp", "dmo" }, 255 { 0x0ff00ff0, 0x01400090, "swpb", "dmo" }, 256 { 0x0ff00fff, 0x01900f9f, "ldrex", "da" }, 257 { 0x0ff00fff, 0x01b00f9f, "ldrexd", "da" }, 258 { 0x0ff00fff, 0x01d00f9f, "ldrexb", "da" }, 259 { 0x0ff00fff, 0x01f00f9f, "ldrexh", "da" }, 260 { 0x0ff00ff0, 0x01800f90, "strex", "dma" }, 261 { 0x0ff00ff0, 0x01a00f90, "strexd", "dma" }, 262 { 0x0ff00ff0, 0x01c00f90, "strexb", "dma" }, 263 { 0x0ff00ff0, 0x01e00f90, "strexh", "dma" }, 264 265 /* A5.2 non-exceptions */ 266 267 /* A5.2.1, A5.2.2, and A5.2.3 Data-processing */ 268 { 0x0de00000, 0x00000000, "and", "Sdn2" }, 269 { 0x0de00000, 0x00200000, "eor", "Sdn2" }, 270 { 0x0de00000, 0x00400000, "sub", "Sdn2" }, 271 { 0x0de00000, 0x00600000, "rsb", "Sdn2" }, 272 { 0x0de00000, 0x00800000, "add", "Sdn2" }, 273 { 0x0de00000, 0x00a00000, "adc", "Sdn2" }, 274 { 0x0de00000, 0x00c00000, "sbc", "Sdn2" }, 275 { 0x0de00000, 0x00e00000, "rsc", "Sdn2" }, 276 { 0x0df00000, 0x01100000, "tst", "Dn2" }, 277 { 0x0df00000, 0x01300000, "teq", "Dn2" }, 278 { 0x0df00000, 0x01500000, "cmp", "Dn2" }, 279 { 0x0df00000, 0x01700000, "cmn", "Dn2" }, 280 { 0x0de00000, 0x01800000, "orr", "Sdn2" }, 281 { 0x0de00000, 0x01a00000, "mov", "Sd2" }, 282 { 0x0de00000, 0x01c00000, "bic", "Sdn2" }, 283 { 0x0de00000, 0x01e00000, "mvn", "Sd2" }, 284 285 /* A5.2.5 Multiply and multiply accumulate */ 286 { 0x0fe000f0, 0x00000090, "mul", "Snms" }, 287 { 0x0fe000f0, 0x00200090, "mla", "Snmsd" }, 288 { 0x0fe000f0, 0x00800090, "umull", "Sdnms" }, 289 { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" }, 290 { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" }, 291 { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" }, 292 293 /* */ 294 { 0x0ff08f10, 0x0e000100, "adf", "PRfgh" }, 295 { 0x0ff08f10, 0x0e100100, "muf", "PRfgh" }, 296 { 0x0ff08f10, 0x0e200100, "suf", "PRfgh" }, 297 { 0x0ff08f10, 0x0e300100, "rsf", "PRfgh" }, 298 { 0x0ff08f10, 0x0e400100, "dvf", "PRfgh" }, 299 { 0x0ff08f10, 0x0e500100, "rdf", "PRfgh" }, 300 { 0x0ff08f10, 0x0e600100, "pow", "PRfgh" }, 301 { 0x0ff08f10, 0x0e700100, "rpw", "PRfgh" }, 302 { 0x0ff08f10, 0x0e800100, "rmf", "PRfgh" }, 303 { 0x0ff08f10, 0x0e900100, "fml", "PRfgh" }, 304 { 0x0ff08f10, 0x0ea00100, "fdv", "PRfgh" }, 305 { 0x0ff08f10, 0x0eb00100, "frd", "PRfgh" }, 306 { 0x0ff08f10, 0x0ec00100, "pol", "PRfgh" }, 307 { 0x0f008f10, 0x0e000100, "fpbop", "PRfgh" }, 308 { 0x0ff08f10, 0x0e008100, "mvf", "PRfh" }, 309 { 0x0ff08f10, 0x0e108100, "mnf", "PRfh" }, 310 { 0x0ff08f10, 0x0e208100, "abs", "PRfh" }, 311 { 0x0ff08f10, 0x0e308100, "rnd", "PRfh" }, 312 { 0x0ff08f10, 0x0e408100, "sqt", "PRfh" }, 313 { 0x0ff08f10, 0x0e508100, "log", "PRfh" }, 314 { 0x0ff08f10, 0x0e608100, "lgn", "PRfh" }, 315 { 0x0ff08f10, 0x0e708100, "exp", "PRfh" }, 316 { 0x0ff08f10, 0x0e808100, "sin", "PRfh" }, 317 { 0x0ff08f10, 0x0e908100, "cos", "PRfh" }, 318 { 0x0ff08f10, 0x0ea08100, "tan", "PRfh" }, 319 { 0x0ff08f10, 0x0eb08100, "asn", "PRfh" }, 320 { 0x0ff08f10, 0x0ec08100, "acs", "PRfh" }, 321 { 0x0ff08f10, 0x0ed08100, "atn", "PRfh" }, 322 { 0x0f008f10, 0x0e008100, "fpuop", "PRfh" }, 323 { 0x0e100f00, 0x0c000100, "stf", "QLv" }, 324 { 0x0e100f00, 0x0c100100, "ldf", "QLv" }, 325 { 0x0ff00f10, 0x0e000110, "flt", "PRgd" }, 326 { 0x0ff00f10, 0x0e100110, "fix", "PRdh" }, 327 { 0x0ff00f10, 0x0e200110, "wfs", "d" }, 328 { 0x0ff00f10, 0x0e300110, "rfs", "d" }, 329 { 0x0ff00f10, 0x0e400110, "wfc", "d" }, 330 { 0x0ff00f10, 0x0e500110, "rfc", "d" }, 331 { 0x0ff0ff10, 0x0e90f110, "cmf", "PRgh" }, 332 { 0x0ff0ff10, 0x0eb0f110, "cnf", "PRgh" }, 333 { 0x0ff0ff10, 0x0ed0f110, "cmfe", "PRgh" }, 334 { 0x0ff0ff10, 0x0ef0f110, "cnfe", "PRgh" }, 335 336 { 0xffb00f10, 0xf2000110, "vand", "Nuqw" }, 337 { 0xffb00f10, 0xf2100110, "vbic", "Nuqw" }, 338 { 0xffb00f10, 0xf2200110, "vorr", "Nuqw" }, 339 { 0xffb00f10, 0xf2300110, "vorn", "Nuqw" }, 340 { 0xffb00f10, 0xf3000110, "veor", "Nuqw" }, 341 { 0xffb00f10, 0xf3100110, "vbsl", "Nuqw" }, 342 { 0xffb00f10, 0xf3200110, "vbit", "Nuqw" }, 343 { 0xffb00f10, 0xf3300110, "vbif", "Nuqw" }, 344 { 0xfe800f10, 0xf3000400, "vshl", "SINuqw" }, 345 { 0xfe800f10, 0xf3000410, "vqshl", "SINuqw" }, 346 { 0xfe800f10, 0xf3000500, "vrshl", "SINuqw" }, 347 { 0xfe800f10, 0xf3000510, "vqrshl", "SINuqw" }, 348 { 0xffb00f10, 0xf2000800, "vadd", "INuqw" }, 349 { 0xffb00f10, 0xf2000810, "vtst", "INuqw" }, 350 { 0xffb00f10, 0xf3000800, "vsub", "INuqw" }, 351 { 0x00000000, 0x00000000, NULL, NULL } 352 }; 353 354 static char const arm32_insn_conditions[][4] = { 355 "eq", "ne", "cs", "cc", 356 "mi", "pl", "vs", "vc", 357 "hi", "ls", "ge", "lt", 358 "gt", "le", "", "nv" 359 }; 360 361 static char const insn_block_transfers[][4] = { 362 "da", "ia", "db", "ib" 363 }; 364 365 static char const insn_stack_block_transfers[][4] = { 366 "ed", "ea", "fd", "fa", /* stm */ 367 "fa", "fd", "ea", "ed", /* ldm */ 368 }; 369 370 static char const op_shifts[][4] = { 371 "lsl", "lsr", "asr", "ror" 372 }; 373 374 static char const *insn_barrier_limiation[] = { 375 "", 376 "", 377 "oshst", /* 0b0010 */ 378 "osh", /* 0b0011 */ 379 "", 380 "", 381 "nshst", /* 0b0110 */ 382 "nsh", /* 0b0111 */ 383 "", 384 "", 385 "ishst", /* 0b1010 */ 386 "ish", /* 0b1011 */ 387 "", 388 "", 389 "st", /* 0b1110 */ 390 "sy", /* 0b1111 */ 391 }; 392 393 static char const insn_fpa_rounding[][2] = { 394 "", "p", "m", "z" 395 }; 396 397 static char const insn_fpa_precision[][2] = { 398 "s", "d", "e", "p" 399 }; 400 401 static char const insn_fpaconstants[][8] = { 402 "0.0", "1.0", "2.0", "3.0", 403 "4.0", "5.0", "0.5", "10.0" 404 }; 405 406 #define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f] 407 #define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3] 408 #define insn_stkblktrans(x) insn_stack_block_transfers[((x >> (20 - 2)) & 4)|((x >> 23) & 3)] 409 #define insn_limitation(x) insn_barrier_limiation[x & 0xf] 410 #define op2_shift(x) op_shifts[(x >> 5) & 3] 411 #define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03] 412 #define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1] 413 #define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1] 414 #define insn_fpaimm(x) insn_fpaconstants[x & 0x07] 415 416 /* Local prototypes */ 417 static void disasm_cps(const disasm_interface_t *, u_int); 418 static void disasm_register_print(const disasm_interface_t *,u_int); 419 static void disasm_register_shift(const disasm_interface_t *, u_int); 420 static void disasm_print_reglist(const disasm_interface_t *, u_int); 421 static void disasm_insn_ldrstr(const disasm_interface_t *, u_int, 422 u_int); 423 static void disasm_insn_ldrxstrx(const disasm_interface_t *, u_int, 424 u_int); 425 static void disasm_insn_ldcstc(const disasm_interface_t *, u_int, 426 u_int); 427 static u_int disassemble_readword(u_int); 428 static void disassemble_printaddr(u_int); 429 430 vaddr_t 431 disasm(const disasm_interface_t *di, vaddr_t loc, int altfmt) 432 { 433 const struct arm32_insn *i_ptr = (const struct arm32_insn *)&arm32_i; 434 435 u_int insn; 436 int matchp; 437 int branch; 438 const char* f_ptr; 439 int fmt; 440 441 if (loc & 3) { 442 /* Don't crash for now. */ 443 di->di_printf("thumb insn\n"); 444 return (loc + THUMB_INSN_SIZE); 445 } 446 447 fmt = 0; 448 matchp = 0; 449 insn = di->di_readword(loc); 450 #ifdef _ARM_ARCH_BE8 451 insn = bswap32(insn); 452 #endif 453 char neonfmt = 'd'; 454 char neonsign = 'u'; 455 456 /* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/ 457 458 while (i_ptr->name) { 459 if ((insn & i_ptr->mask) == i_ptr->pattern) { 460 matchp = 1; 461 break; 462 } 463 i_ptr++; 464 } 465 466 if (!matchp) { 467 di->di_printf("und%s\t%08x\n", insn_condition(insn), insn); 468 return(loc + INSN_SIZE); 469 } 470 471 /* If instruction forces condition code, don't print it. */ 472 if ((i_ptr->mask & 0xf0000000) == 0xf0000000) 473 di->di_printf("%s", i_ptr->name); 474 else 475 di->di_printf("%s%s", i_ptr->name, insn_condition(insn)); 476 477 f_ptr = i_ptr->format; 478 479 /* Insert tab if there are no instruction modifiers */ 480 481 if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') { 482 ++fmt; 483 di->di_printf("\t"); 484 } 485 486 while (*f_ptr) { 487 switch (*f_ptr) { 488 case '!': 489 f_ptr++; 490 switch (*f_ptr) { 491 /* !c - cps flags and mode */ 492 case 'c': 493 disasm_cps(di, insn); 494 break; 495 /* !d - debug option */ 496 case 'd': 497 di->di_printf("#%d", insn & 0xf); 498 break; 499 /* !l - barrier dmb/dsb limitation */ 500 case 'l': 501 di->di_printf("%s", insn_limitation(insn)); 502 break; 503 /* !m - mode */ 504 case 'm': 505 di->di_printf(", #%d", insn & 0x1f); 506 break; 507 } 508 break; 509 /* 2 - print Operand 2 of a data processing instruction */ 510 case '2': 511 if (insn & 0x02000000) { 512 int rotate = ((insn >> 7) & 0x1e); 513 int imm = (insn & 0xff) << (32 - rotate) | 514 (insn & 0xff) >> rotate; 515 di->di_printf("#%d ; #0x%x", 516 imm, imm); 517 } else { 518 disasm_register_shift(di, insn); 519 } 520 break; 521 /* d - destination register (bits 12-15) */ 522 case 'd': 523 disasm_register_print(di, (insn >> 12) & 0x0f); 524 break; 525 /* u - neon destination register (bits 22, 12-15) */ 526 case 'u': 527 di->di_printf("%c%d", neonfmt, 528 ((insn >> 18) & 0x10)|((insn >> 12) & 0x0f)); 529 break; 530 /* D - insert 'p' if Rd is R15 */ 531 case 'D': 532 if (((insn >> 12) & 0x0f) == 15) 533 di->di_printf("p"); 534 break; 535 /* n - n register (bits 16-19) */ 536 case 'n': 537 disasm_register_print(di, (insn >> 16) & 0x0f); 538 break; 539 /* q - neon n register (bits 7, 16-19) */ 540 case 'q': 541 di->di_printf("%c%d", neonfmt, 542 ((insn >> 3) & 0x10)|((insn >> 16) & 0x0f)); 543 break; 544 /* s - s register (bits 8-11) */ 545 case 's': 546 di->di_printf("r%d", ((insn >> 8) & 0x0f)); 547 break; 548 /* o - indirect register rn (bits 16-19) (used by swap) */ 549 case 'o': 550 di->di_printf("[r%d]", ((insn >> 16) & 0x0f)); 551 break; 552 /* m - m register (bits 0-3) */ 553 case 'm': 554 di->di_printf("r%d", ((insn >> 0) & 0x0f)); 555 break; 556 /* w - neon m register (bits 5, 0-3) */ 557 case 'w': 558 di->di_printf("%c%d", neonfmt, 559 ((insn >> 1) & 0x10)|(insn & 0x0f)); 560 break; 561 /* a - address operand of ldr/str instruction */ 562 case 'a': 563 disasm_insn_ldrstr(di, insn, loc); 564 break; 565 /* e - address operand of ldrx/strx instruction */ 566 case 'e': 567 disasm_insn_ldrxstrx(di, insn, loc); 568 break; 569 /* l - register list for ldm/stm instruction */ 570 case 'l': 571 disasm_print_reglist(di, insn); 572 break; 573 /* f - 1st fp operand (register) (bits 12-14) */ 574 case 'f': 575 di->di_printf("f%d", (insn >> 12) & 7); 576 break; 577 /* g - 2nd fp operand (register) (bits 16-18) */ 578 case 'g': 579 di->di_printf("f%d", (insn >> 16) & 7); 580 break; 581 /* h - 3rd fp operand (register/immediate) (bits 0-4) */ 582 case 'h': 583 if (insn & (1 << 3)) 584 di->di_printf("#%s", insn_fpaimm(insn)); 585 else 586 di->di_printf("f%d", insn & 7); 587 break; 588 /* i - lsb operand (bits 7-11) */ 589 case 'i': 590 di->di_printf("#%d", (insn >> 7) & 0x1f); 591 break; 592 /* j - msb operand (bits 16-20) as width */ 593 case 'j': 594 di->di_printf("#%d", 595 ((insn >> 16) & 0x1f) - ((insn >> 7) & 0x1f) + 1); 596 break; 597 /* r - width minus 1 (bits 16-20) */ 598 case 'r': 599 di->di_printf("#%d", ((insn >> 16) & 0x1f) + 1); 600 break; 601 /* b - branch address */ 602 case 'b': 603 branch = ((insn << 2) & 0x03ffffff); 604 if (branch & 0x02000000) 605 branch |= 0xfc000000; 606 di->di_printaddr(loc + 8 + branch); 607 break; 608 /* t - blx address */ 609 case 't': 610 branch = ((insn << 2) & 0x03ffffff) | 611 (insn >> 23 & 0x00000002); 612 if (branch & 0x02000000) 613 branch |= 0xfc000000; 614 di->di_printaddr(loc + 8 + branch); 615 break; 616 case 'N': 617 if (insn & 0x40) 618 neonfmt = 'q'; 619 break; 620 case 'U': 621 if (insn & (1 << 24)) 622 neonsign = 's'; 623 break; 624 case 'I': 625 di->di_printf(".%c%d", neonsign, 626 8 << ((insn >> 20) & 3)); 627 break; 628 /* X - block transfer type */ 629 case 'X': 630 di->di_printf("%s", insn_blktrans(insn)); 631 break; 632 /* Y - block transfer type (r13 base) */ 633 case 'Y': 634 di->di_printf("%s", insn_stkblktrans(insn)); 635 break; 636 /* Z - print movw/movt argument */ 637 case 'Z': 638 di->di_printf(", #0x%04x", 639 ((insn & 0xf0000) >> 4) | (insn & 0xfff)); 640 break; 641 /* c - comment field bits(0-23) */ 642 case 'c': 643 di->di_printf("0x%08x", (insn & 0x00ffffff)); 644 break; 645 /* k - breakpoint comment (bits 0-3, 8-19) */ 646 case 'k': 647 di->di_printf("0x%04x", 648 (insn & 0x000fff00) >> 4 | (insn & 0x0000000f)); 649 break; 650 /* p - saved or current status register */ 651 case 'p': 652 if (insn & 0x00400000) 653 di->di_printf("spsr"); 654 else 655 di->di_printf("cpsr"); 656 break; 657 /* C - cps effect */ 658 case 'C': 659 if ((insn & 0x000c0000) == 0x000c0000) 660 di->di_printf("id"); 661 else if ((insn & 0x000c0000) == 0x00080000) 662 di->di_printf("ie"); 663 break; 664 /* F - PSR transfer fields */ 665 case 'F': 666 di->di_printf("_"); 667 if (insn & (1 << 16)) 668 di->di_printf("c"); 669 if (insn & (1 << 17)) 670 di->di_printf("x"); 671 if (insn & (1 << 18)) 672 di->di_printf("s"); 673 if (insn & (1 << 19)) 674 di->di_printf("f"); 675 break; 676 /* B - byte transfer flag */ 677 case 'B': 678 if (insn & 0x00400000) 679 di->di_printf("b"); 680 break; 681 /* L - co-processor transfer size */ 682 case 'L': 683 if (insn & (1 << 22)) 684 di->di_printf("l"); 685 break; 686 /* S - set status flag */ 687 case 'S': 688 if (insn & 0x00100000) 689 di->di_printf("s"); 690 break; 691 /* P - fp precision */ 692 case 'P': 693 di->di_printf("%s", insn_fpaprec(insn)); 694 break; 695 /* Q - fp precision (for ldf/stf) */ 696 case 'Q': 697 break; 698 /* R - fp rounding */ 699 case 'R': 700 di->di_printf("%s", insn_fparnd(insn)); 701 break; 702 /* W - writeback flag */ 703 case 'W': 704 if (insn & (1 << 21)) 705 di->di_printf("!"); 706 break; 707 /* # - co-processor number */ 708 case '#': 709 di->di_printf("p%d", (insn >> 8) & 0x0f); 710 break; 711 /* v - co-processor data transfer registers+addressing mode */ 712 case 'v': 713 disasm_insn_ldcstc(di, insn, loc); 714 break; 715 /* x - instruction in hex */ 716 case 'x': 717 di->di_printf("0x%08x", insn); 718 break; 719 /* y - co-processor data processing registers */ 720 case 'y': 721 di->di_printf("%d, ", (insn >> 20) & 0x0f); 722 723 di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f, 724 (insn >> 16) & 0x0f, insn & 0x0f); 725 726 di->di_printf(", %d", (insn >> 5) & 0x07); 727 break; 728 /* z - co-processor register transfer registers */ 729 case 'z': 730 di->di_printf("%d, ", (insn >> 21) & 0x07); 731 di->di_printf("r%d, c%d, c%d, %d", 732 (insn >> 12) & 0x0f, (insn >> 16) & 0x0f, 733 insn & 0x0f, (insn >> 5) & 0x07); 734 735 /* if (((insn >> 5) & 0x07) != 0) 736 di->di_printf(", %d", (insn >> 5) & 0x07);*/ 737 break; 738 /* & - co-processor register range transfer registers */ 739 case '&': 740 di->di_printf("%d, r%d, r%d, c%d", 741 (insn >> 4) & 0x0f, (insn >> 12) & 0x0f, 742 (insn >> 16) & 0x0f, insn & 0x0f); 743 break; 744 default: 745 di->di_printf("[%c - unknown]", *f_ptr); 746 break; 747 } 748 if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z') 749 ++f_ptr; 750 else if (*(++f_ptr)) { 751 ++fmt; 752 if (fmt == 1) 753 di->di_printf("\t"); 754 else 755 di->di_printf(", "); 756 } 757 }; 758 759 di->di_printf("\n"); 760 761 return(loc + INSN_SIZE); 762 } 763 764 static void 765 disasm_register_print(const disasm_interface_t *di, u_int r) 766 { 767 switch (r) { 768 case 13: 769 di->di_printf("sp"); 770 break; 771 case 14: 772 di->di_printf("lr"); 773 break; 774 case 15: 775 di->di_printf("pc"); 776 break; 777 default: 778 di->di_printf("r%d", r); 779 break; 780 } 781 } 782 783 static void 784 disasm_cps(const disasm_interface_t *di, u_int insn) 785 { 786 if ((insn & 0x000c0000) == 0x000c0000 || 787 (insn & 0x000c0000) == 0x00080000) { 788 if (insn & (1 << 8)) 789 di->di_printf("a"); 790 if (insn & (1 << 7)) 791 di->di_printf("i"); 792 if (insn & (1 << 6)) 793 di->di_printf("f"); 794 if ((insn & (1 << 17)) && ((insn & 0x1f) != 0)) 795 di->di_printf(", "); 796 } 797 if ((insn & (1 << 17)) && ((insn & 0x1f) != 0)) 798 di->di_printf("#%d", insn & 0x1f); 799 } 800 801 static void 802 disasm_register_shift(const disasm_interface_t *di, u_int insn) 803 { 804 di->di_printf("r%d", (insn & 0x0f)); 805 if ((insn & 0x00000ff0) == 0) 806 ; 807 else if ((insn & 0x00000ff0) == 0x00000060) 808 di->di_printf(", rrx"); 809 else { 810 if (insn & 0x10) 811 di->di_printf(", %s r%d", op2_shift(insn), 812 (insn >> 8) & 0x0f); 813 else 814 di->di_printf(", %s #%d", op2_shift(insn), 815 (insn >> 7) & 0x1f); 816 } 817 } 818 819 820 static void 821 disasm_print_reglist(const disasm_interface_t *di, u_int insn) 822 { 823 int loop; 824 int start; 825 int comma; 826 827 di->di_printf("{"); 828 start = -1; 829 comma = 0; 830 831 for (loop = 0; loop < 17; ++loop) { 832 if (start != -1) { 833 if (loop == 16 || !(insn & (1 << loop))) { 834 if (comma) 835 di->di_printf(", "); 836 else 837 comma = 1; 838 if (start == loop - 1) 839 di->di_printf("r%d", start); 840 else 841 di->di_printf("r%d-r%d", start, loop - 1); 842 start = -1; 843 } 844 } else { 845 if (insn & (1 << loop)) 846 start = loop; 847 } 848 } 849 di->di_printf("}"); 850 851 if (insn & (1 << 22)) 852 di->di_printf("^"); 853 } 854 855 static void 856 disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc) 857 { 858 int offset; 859 860 offset = insn & 0xfff; 861 if ((insn & 0x032f0000) == 0x010f0000) { 862 /* rA = pc, immediate index */ 863 if (insn & 0x00800000) 864 loc += offset; 865 else 866 loc -= offset; 867 di->di_printaddr(loc + 8); 868 } else { 869 di->di_printf("[r%d", (insn >> 16) & 0x0f); 870 if ((insn & 0x03000fff) != 0x01000000 871 && (insn & 0x0f800ff0) != 0x01800f90) { 872 di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); 873 if (!(insn & 0x00800000)) 874 di->di_printf("-"); 875 if (insn & (1 << 25)) 876 disasm_register_shift(di, insn); 877 else 878 di->di_printf("#0x%03x", offset); 879 } 880 if (insn & (1 << 24)) 881 di->di_printf("]"); 882 } 883 } 884 885 static void 886 disasm_insn_ldrxstrx(const disasm_interface_t *di, u_int insn, u_int loc) 887 { 888 int offset; 889 890 offset = ((insn & 0xf00) >> 4) | (insn & 0xf); 891 if ((insn & 0x004f0000) == 0x004f0000) { 892 /* rA = pc, immediate index */ 893 if (insn & 0x00800000) 894 loc += offset; 895 else 896 loc -= offset; 897 di->di_printaddr(loc + 8); 898 } else { 899 di->di_printf("[r%d", (insn >> 16) & 0x0f); 900 if ((insn & 0x01400f0f) != 0x01400000) { 901 di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); 902 char const *sign = (insn & 0x00800000) ? "" : "-"; 903 if (insn & (1 << 22)) 904 di->di_printf("#%s0x%02x", sign, offset); 905 else 906 di->di_printf("%sr%d", sign, (insn & 0x0f)); 907 } 908 if (insn & (1 << 24)) { 909 di->di_printf("]"); 910 if (__SHIFTOUT(insn, __BIT(21))) 911 di->di_printf("!"); 912 } 913 } 914 } 915 916 static void 917 disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc) 918 { 919 if (((insn >> 8) & 0xf) == 1) 920 di->di_printf("f%d, ", (insn >> 12) & 0x07); 921 else 922 di->di_printf("c%d, ", (insn >> 12) & 0x0f); 923 924 di->di_printf("[r%d", (insn >> 16) & 0x0f); 925 926 di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); 927 928 if (!(insn & (1 << 23))) 929 di->di_printf("-"); 930 931 di->di_printf("#0x%03x", (insn & 0xff) << 2); 932 933 if (insn & (1 << 24)) 934 di->di_printf("]"); 935 936 if (insn & (1 << 21)) 937 di->di_printf("!"); 938 } 939 940 static u_int 941 disassemble_readword(u_int address) 942 { 943 return(*((u_int *)address)); 944 } 945 946 static void 947 disassemble_printaddr(u_int address) 948 { 949 printf("0x%08x", address); 950 } 951 952 static const disasm_interface_t disassemble_di = { 953 disassemble_readword, disassemble_printaddr, 954 __FPTRCAST(void (*)(const char *, ...), printf) 955 }; 956 957 void 958 disassemble(u_int address) 959 { 960 961 (void)disasm(&disassemble_di, address, 0); 962 } 963 964 /* End of disassem.c */ 965