1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2007 Jason King.  All rights reserved.
  29  * Use is subject to license terms.
  30  * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org>
  31  */
  32 
  33 
  34 /*
  35  * The sparc disassembler is mostly straightforward, each instruction is
  36  * represented by an inst_t structure.  The inst_t definitions are organized
  37  * into tables.  The tables are correspond to the opcode maps documented in the
  38  * various sparc architecture manuals.  Each table defines the bit range of the
  39  * instruction whose value act as an index into the array of instructions.  A
  40  * table can also refer to another table if needed.  Each table also contains
  41  * a function pointer of type format_fcn that knows how to output the
  42  * instructions in the table, as well as handle any synthetic instructions
  43  *
  44  * Unfortunately, the changes from sparcv8 -> sparcv9 not only include new
  45  * instructions, they sometimes renamed or just reused the same instruction to
  46  * do different operations (i.e. the sparcv8 coprocessor instructions).  To
  47  * accommodate this, each table can define an overlay table.  The overlay table
  48  * is a list of (table index, architecture, new instruction definition) values.
  49  *
  50  *
  51  * Traversal starts with the first table,
  52  *   get index value from the instruction
  53  *   if an relevant overlay entry exists for this index,
  54  *        grab the overlay definition
  55  *   else
  56  *        grab the definition from the array (corresponding to the index value)
  57  *
  58  * If the entry is an instruction,
  59  *     call print function of instruction.
  60  * If the entry is a pointer to another table
  61  *     traverse the table
  62  * If not valid,
  63  *     return an error
  64  *
  65  *
  66  * To keep dis happy, for sparc, instead of actually returning an error, if
  67  * the instruction cannot be disassembled, we instead merely place the value
  68  * of the instruction into the output buffer.
  69  *
  70  * Adding new instructions:
  71  *
  72  * With the above information, it hopefully makes it clear how to add support
  73  * for decoding new instructions.  Presumably, with new instructions will come
  74  * a new dissassembly mode (I.e. DIS_SPARC_V8, DIS_SPARC_V9, etc.).
  75  *
  76  * If the dissassembled format does not correspond to one of the existing
  77  * formats, a new formatter will have to be written.  The 'flags' value of
  78  * inst_t is intended to instruct the corresponding formatter about how to
  79  * output the instruction.
  80  *
  81  * If the corresponding entry in the correct table is currently unoccupied,
  82  * simply replace the INVALID entry with the correct definition.  The INST and
  83  * TABLE macros are suggested to be used for this.  If there is already an
  84  * instruction defined, then the entry must be placed in an overlay table.  If
  85  * no overlay table exists for the instruction table, one will need to be
  86  * created.
  87  */
  88 
  89 #include <libdisasm.h>
  90 #include <stdlib.h>
  91 #include <stdio.h>
  92 #include <sys/types.h>
  93 #include <sys/byteorder.h>
  94 #include <string.h>
  95 
  96 #include "libdisasm_impl.h"
  97 #include "dis_sparc.h"
  98 
  99 static const inst_t *dis_get_overlay(dis_handle_t *, const table_t *,
 100     uint32_t);
 101 static uint32_t dis_get_bits(uint32_t, int, int);
 102 
 103 #if !defined(DIS_STANDALONE)
 104 static void do_binary(uint32_t);
 105 #endif /* DIS_STANDALONE */
 106 
 107 static void
 108 dis_sparc_handle_detach(dis_handle_t *dhp)
 109 {
 110         dis_free(dhp->dh_arch_private, sizeof (dis_handle_sparc_t));
 111         dhp->dh_arch_private = NULL;
 112 }
 113 
 114 static int
 115 dis_sparc_handle_attach(dis_handle_t *dhp)
 116 {
 117         dis_handle_sparc_t *dhx;
 118 
 119 #if !defined(DIS_STANDALONE)
 120         char *opt = NULL;
 121         char *opt2, *save, *end;
 122 #endif
 123 
 124         /* Validate architecture flags */
 125         if ((dhp->dh_flags & (DIS_SPARC_V8|DIS_SPARC_V9|DIS_SPARC_V9_SGI))
 126             == 0) {
 127                 (void) dis_seterrno(E_DIS_INVALFLAG);
 128                 return (-1);
 129         }
 130 
 131         if ((dhx = dis_zalloc(sizeof (dis_handle_sparc_t))) == NULL) {
 132                 (void) dis_seterrno(E_DIS_NOMEM);
 133                 return (NULL);
 134         }
 135         dhx->dhx_debug = DIS_DEBUG_COMPAT;
 136         dhp->dh_arch_private = dhx;
 137 
 138 #if !defined(DIS_STANDALONE)
 139 
 140         opt = getenv("_LIBDISASM_DEBUG");
 141         if (opt == NULL)
 142                 return (0);
 143 
 144         opt2 = strdup(opt);
 145         if (opt2 == NULL) {
 146                 dis_handle_destroy(dhp);
 147                 dis_free(dhx, sizeof (dis_handle_sparc_t));
 148                 (void) dis_seterrno(E_DIS_NOMEM);
 149                 return (-1);
 150         }
 151         save = opt2;
 152 
 153         while (opt2 != NULL) {
 154                 end = strchr(opt2, ',');
 155 
 156                 if (end != 0)
 157                         *end++ = '\0';
 158 
 159                 if (strcasecmp("synth-all", opt2) == 0)
 160                         dhx->dhx_debug |= DIS_DEBUG_SYN_ALL;
 161 
 162                 if (strcasecmp("compat", opt2) == 0)
 163                         dhx->dhx_debug |= DIS_DEBUG_COMPAT;
 164 
 165                 if (strcasecmp("synth-none", opt2) == 0)
 166                         dhx->dhx_debug &= ~(DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT);
 167 
 168                 if (strcasecmp("binary", opt2) == 0)
 169                         dhx->dhx_debug |= DIS_DEBUG_PRTBIN;
 170 
 171                 if (strcasecmp("format", opt2) == 0)
 172                         dhx->dhx_debug |= DIS_DEBUG_PRTFMT;
 173 
 174                 if (strcasecmp("all", opt2) == 0)
 175                         dhx->dhx_debug = DIS_DEBUG_ALL;
 176 
 177                 if (strcasecmp("none", opt2) == 0)
 178                         dhx->dhx_debug = DIS_DEBUG_NONE;
 179 
 180                 opt2 = end;
 181         }
 182         free(save);
 183 #endif /* DIS_STANDALONE */
 184         return (0);
 185 }
 186 
 187 /* ARGSUSED */
 188 static int
 189 dis_sparc_max_instrlen(dis_handle_t *dhp)
 190 {
 191         return (4);
 192 }
 193 
 194 /* ARGSUSED */
 195 static int
 196 dis_sparc_min_instrlen(dis_handle_t *dhp)
 197 {
 198         return (4);
 199 }
 200 
 201 /*
 202  * The dis_i386.c comment for this says it returns the previous instruction,
 203  * however, I'm fairly sure it's actually returning the _address_ of the
 204  * nth previous instruction.
 205  */
 206 /* ARGSUSED */
 207 static uint64_t
 208 dis_sparc_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
 209 {
 210         if (n <= 0)
 211                 return (pc);
 212 
 213         if (pc < n)
 214                 return (pc);
 215 
 216         return (pc - n*4);
 217 }
 218 
 219 static int
 220 dis_sparc_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf,
 221     size_t buflen)
 222 {
 223         dis_handle_sparc_t *dhx = dhp->dh_arch_private;
 224         const table_t *tp = &initial_table;
 225         const inst_t *inp = NULL;
 226 
 227         uint32_t instr;
 228         uint32_t idx = 0;
 229 
 230         if (dhp->dh_read(dhp->dh_data, addr, &instr, sizeof (instr)) !=
 231             sizeof (instr))
 232                 return (-1);
 233 
 234         dhx->dhx_buf    = buf;
 235         dhx->dhx_buflen = buflen;
 236         dhp->dh_addr    = addr;
 237 
 238         buf[0] = '\0';
 239 
 240         /* this allows sparc code to be tested on x86 */
 241 #if !defined(DIS_STANDALONE)
 242         instr = BE_32(instr);
 243 #endif /* DIS_STANDALONE */
 244 
 245 #if !defined(DIS_STANDALONE)
 246         if ((dhx->dhx_debug & DIS_DEBUG_PRTBIN) != 0)
 247                 do_binary(instr);
 248 #endif /* DIS_STANDALONE */
 249 
 250         /* CONSTCOND */
 251         while (1) {
 252                 idx = dis_get_bits(instr, tp->tbl_field, tp->tbl_len);
 253                 inp = &tp->tbl_inp[idx];
 254 
 255                 inp = dis_get_overlay(dhp, tp, idx);
 256 
 257                 if ((inp->in_type == INST_NONE) ||
 258                     ((inp->in_arch & dhp->dh_flags) == 0))
 259                         goto error;
 260 
 261                 if (inp->in_type == INST_TBL) {
 262                         tp = inp->in_data.in_tbl;
 263                         continue;
 264                 }
 265 
 266                 break;
 267         }
 268 
 269         if (tp->tbl_fmt(dhp, instr, inp, idx) == 0)
 270                 return (0);
 271 
 272 error:
 273 
 274         (void) snprintf(buf, buflen,
 275             ((dhp->dh_flags & DIS_OCTAL) != 0) ? "0%011lo" : "0x%08lx",
 276             instr);
 277 
 278         return (0);
 279 }
 280 
 281 static uint32_t
 282 dis_get_bits(uint32_t instr, int offset, int length)
 283 {
 284         uint32_t mask, val;
 285         int i;
 286 
 287         for (i = 0, mask = 0; i < length; ++i)
 288                 mask |= (1UL << i);
 289 
 290         mask = mask << (offset - length + 1);
 291 
 292         val = instr & mask;
 293 
 294         val = val >> (offset - length + 1);
 295 
 296         return (val);
 297 }
 298 
 299 static const inst_t *
 300 dis_get_overlay(dis_handle_t *dhp, const table_t *tp, uint32_t idx)
 301 {
 302         const inst_t *ip = &tp->tbl_inp[idx];
 303         int i;
 304 
 305         if (tp->tbl_ovp == NULL)
 306                 return (ip);
 307 
 308         for (i = 0; tp->tbl_ovp[i].ov_idx != -1; ++i) {
 309                 if (tp->tbl_ovp[i].ov_idx != idx)
 310                         continue;
 311 
 312                 if ((tp->tbl_ovp[i].ov_inst.in_arch & dhp->dh_flags) == 0)
 313                         continue;
 314 
 315                 ip = &tp->tbl_ovp[i].ov_inst;
 316                 break;
 317         }
 318 
 319         return (ip);
 320 }
 321 
 322 #if !defined(DIS_STANDALONE)
 323 static void
 324 do_binary(uint32_t instr)
 325 {
 326         (void) fprintf(stderr, "DISASM: ");
 327         prt_binary(instr, 32);
 328         (void) fprintf(stderr, "\n");
 329 }
 330 #endif /* DIS_STANDALONE */
 331 
 332 static int
 333 dis_sparc_supports_flags(int flags)
 334 {
 335         int archflags = flags & DIS_ARCH_MASK;
 336 
 337         if (archflags == DIS_SPARC_V8 ||
 338             (archflags & (DIS_SPARC_V9 | DIS_SPARC_V8)) == DIS_SPARC_V9)
 339                 return (1);
 340 
 341         return (0);
 342 }
 343 
 344 const dis_arch_t dis_arch_sparc = {
 345         dis_sparc_supports_flags,
 346         dis_sparc_handle_attach,
 347         dis_sparc_handle_detach,
 348         dis_sparc_disassemble,
 349         dis_sparc_previnstr,
 350         dis_sparc_min_instrlen,
 351         dis_sparc_max_instrlen
 352 };