Coverage Report

Created: 2023-06-29 07:13

/src/binutils-gdb/opcodes/pru-dis.c
Line
Count
Source (jump to first uncovered line)
1
/* TI PRU disassemble routines
2
   Copyright (C) 2014-2023 Free Software Foundation, Inc.
3
   Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
4
5
   This file is part of the GNU opcodes library.
6
7
   This library is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3, or (at your option)
10
   any later version.
11
12
   It is distributed in the hope that it will be useful, but WITHOUT
13
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15
   License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this file; see the file COPYING.  If not, write to the
19
   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
20
   MA 02110-1301, USA.  */
21
22
#include "sysdep.h"
23
#include "disassemble.h"
24
#include "opcode/pru.h"
25
#include "libiberty.h"
26
#include <string.h>
27
#include <assert.h>
28
29
/* No symbol table is available when this code runs out in an embedded
30
   system as when it is used for disassembler support in a monitor.  */
31
#if !defined (EMBEDDED_ENV)
32
#define SYMTAB_AVAILABLE 1
33
#include "elf-bfd.h"
34
#include "elf/pru.h"
35
#endif
36
37
/* Length of PRU instruction in bytes.  */
38
158k
#define INSNLEN 4
39
40
/* Return a pointer to an pru_opcode struct for a given instruction
41
   opcode, or NULL if there is an error.  */
42
const struct pru_opcode *
43
pru_find_opcode (unsigned long opcode)
44
39.5k
{
45
39.5k
  const struct pru_opcode *p;
46
39.5k
  const struct pru_opcode *op = NULL;
47
39.5k
  const struct pru_opcode *pseudo_op = NULL;
48
49
2.09M
  for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++)
50
2.05M
    {
51
2.05M
      if ((p->mask & opcode) == p->match)
52
31.9k
  {
53
31.9k
    if ((p->pinfo & PRU_INSN_MACRO) == PRU_INSN_MACRO)
54
24
      pseudo_op = p;
55
31.9k
    else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32)
56
186
      /* ignore - should be caught with regular patterns */;
57
31.7k
    else
58
31.7k
      op = p;
59
31.9k
  }
60
2.05M
    }
61
62
39.5k
  return pseudo_op ? pseudo_op : op;
63
39.5k
}
64
65
/* There are 32 regular registers, each with 8 possible subfield selectors.  */
66
#define NUMREGNAMES (32 * 8)
67
68
static void
69
pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
70
      disassemble_info *info)
71
62.4k
{
72
62.4k
  unsigned int i = r * RSEL_NUM_ITEMS + sel;
73
62.4k
  assert (i < (unsigned int)pru_num_regs);
74
62.4k
  assert (i < NUMREGNAMES);
75
62.4k
  (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
76
62.4k
}
77
78
/* The function pru_print_insn_arg uses the character pointed
79
   to by ARGPTR to determine how it print the next token or separator
80
   character in the arguments to an instruction.  */
81
static int
82
pru_print_insn_arg (const char *argptr,
83
          unsigned long opcode, bfd_vma address,
84
          disassemble_info *info)
85
164k
{
86
164k
  long offs = 0;
87
164k
  unsigned long i = 0;
88
164k
  unsigned long io = 0;
89
90
164k
  switch (*argptr)
91
164k
    {
92
66.7k
    case ',':
93
66.7k
      (*info->fprintf_func) (info->stream, "%c ", *argptr);
94
66.7k
      break;
95
11.7k
    case 'd':
96
11.7k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
97
11.7k
            GET_INSN_FIELD (RDSEL, opcode),
98
11.7k
            info);
99
11.7k
      break;
100
9.73k
    case 'D':
101
      /* The first 4 values for RDB and RSEL are the same, so we
102
   can reuse some code.  */
103
9.73k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
104
9.73k
            GET_INSN_FIELD (RDB, opcode),
105
9.73k
            info);
106
9.73k
      break;
107
18.1k
    case 's':
108
18.1k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
109
18.1k
            GET_INSN_FIELD (RS1SEL, opcode),
110
18.1k
            info);
111
18.1k
      break;
112
6.84k
    case 'S':
113
6.84k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
114
6.84k
            RSEL_31_0,
115
6.84k
            info);
116
6.84k
      break;
117
27.5k
    case 'b':
118
27.5k
      io = GET_INSN_FIELD (IO, opcode);
119
120
27.5k
      if (io)
121
12.5k
  {
122
12.5k
    i = GET_INSN_FIELD (IMM8, opcode);
123
12.5k
    (*info->fprintf_func) (info->stream, "%ld", i);
124
12.5k
  }
125
14.9k
      else
126
14.9k
  {
127
14.9k
  pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
128
14.9k
        GET_INSN_FIELD (RS2SEL, opcode),
129
14.9k
        info);
130
14.9k
  }
131
27.5k
      break;
132
1.15k
    case 'B':
133
1.15k
      io = GET_INSN_FIELD (IO, opcode);
134
135
1.15k
      if (io)
136
616
  {
137
616
    i = GET_INSN_FIELD (IMM8, opcode) + 1;
138
616
    (*info->fprintf_func) (info->stream, "%ld", i);
139
616
  }
140
538
      else
141
538
  {
142
538
  pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
143
538
        GET_INSN_FIELD (RS2SEL, opcode),
144
538
        info);
145
538
  }
146
1.15k
      break;
147
693
    case 'j':
148
693
      io = GET_INSN_FIELD (IO, opcode);
149
150
693
      if (io)
151
254
  {
152
    /* For the sake of pretty-printing, dump text addresses with
153
       their "virtual" offset that we use for distinguishing
154
       PMEM vs DMEM. This is needed for printing the correct text
155
       labels.  */
156
254
    bfd_vma text_offset = address & ~0x3fffff;
157
254
    i = GET_INSN_FIELD (IMM16, opcode) * 4;
158
254
    (*info->print_address_func) (i + text_offset, info);
159
254
  }
160
439
      else
161
439
  {
162
439
    pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
163
439
        GET_INSN_FIELD (RS2SEL, opcode),
164
439
        info);
165
439
  }
166
693
      break;
167
186
    case 'W':
168
186
      i = GET_INSN_FIELD (IMM16, opcode);
169
186
      (*info->fprintf_func) (info->stream, "%ld", i);
170
186
      break;
171
7.69k
    case 'o':
172
7.69k
      offs = GET_BROFF_SIGNED (opcode) * 4;
173
7.69k
      (*info->print_address_func) (address + offs, info);
174
7.69k
      break;
175
1.15k
    case 'O':
176
1.15k
      offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4;
177
1.15k
      (*info->print_address_func) (address + offs, info);
178
1.15k
      break;
179
9.45k
    case 'l':
180
9.45k
      i = GET_BURSTLEN (opcode);
181
9.45k
      if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
182
7.30k
  (*info->fprintf_func) (info->stream, "%ld", i + 1);
183
2.14k
      else
184
2.14k
  {
185
2.14k
    i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
186
2.14k
    (*info->fprintf_func) (info->stream, "r0.b%ld", i);
187
2.14k
  }
188
9.45k
      break;
189
281
    case 'n':
190
281
      i = GET_INSN_FIELD (XFR_LENGTH, opcode);
191
281
      if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
192
164
  (*info->fprintf_func) (info->stream, "%ld", i + 1);
193
117
      else
194
117
  {
195
117
    i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
196
117
    (*info->fprintf_func) (info->stream, "r0.b%ld", i);
197
117
  }
198
281
      break;
199
2.61k
    case 'c':
200
2.61k
      i = GET_INSN_FIELD (CB, opcode);
201
2.61k
      (*info->fprintf_func) (info->stream, "%ld", i);
202
2.61k
      break;
203
592
    case 'w':
204
592
      i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
205
592
      (*info->fprintf_func) (info->stream, "%ld", i);
206
592
      break;
207
281
    case 'x':
208
281
      i = GET_INSN_FIELD (XFR_WBA, opcode);
209
281
      (*info->fprintf_func) (info->stream, "%ld", i);
210
281
      break;
211
0
    default:
212
0
      (*info->fprintf_func) (info->stream, "unknown");
213
0
      break;
214
164k
    }
215
164k
  return 0;
216
164k
}
217
218
/* pru_disassemble does all the work of disassembling a PRU
219
   instruction opcode.  */
220
static int
221
pru_disassemble (bfd_vma address, unsigned long opcode,
222
       disassemble_info *info)
223
39.5k
{
224
39.5k
  const struct pru_opcode *op;
225
226
39.5k
  info->bytes_per_line = INSNLEN;
227
39.5k
  info->bytes_per_chunk = INSNLEN;
228
39.5k
  info->display_endian = info->endian;
229
39.5k
  info->insn_info_valid = 1;
230
39.5k
  info->branch_delay_insns = 0;
231
39.5k
  info->data_size = 0;
232
39.5k
  info->insn_type = dis_nonbranch;
233
39.5k
  info->target = 0;
234
39.5k
  info->target2 = 0;
235
236
  /* Find the major opcode and use this to disassemble
237
     the instruction and its arguments.  */
238
39.5k
  op = pru_find_opcode (opcode);
239
240
39.5k
  if (op != NULL)
241
31.7k
    {
242
31.7k
      (*info->fprintf_func) (info->stream, "%s", op->name);
243
244
31.7k
      const char *argstr = op->args;
245
31.7k
      if (argstr != NULL && *argstr != '\0')
246
31.4k
  {
247
31.4k
    (*info->fprintf_func) (info->stream, "\t");
248
196k
    while (*argstr != '\0')
249
164k
      {
250
164k
        pru_print_insn_arg (argstr, opcode, address, info);
251
164k
        ++argstr;
252
164k
      }
253
31.4k
  }
254
31.7k
    }
255
7.76k
  else
256
7.76k
    {
257
      /* Handle undefined instructions.  */
258
7.76k
      info->insn_type = dis_noninsn;
259
7.76k
      (*info->fprintf_func) (info->stream, "0x%lx", opcode);
260
7.76k
    }
261
  /* Tell the caller how far to advance the program counter.  */
262
39.5k
  return INSNLEN;
263
39.5k
}
264
265
266
/* print_insn_pru is the main disassemble function for PRU.  */
267
int
268
print_insn_pru (bfd_vma address, disassemble_info *info)
269
39.5k
{
270
39.5k
  bfd_byte buffer[INSNLEN];
271
39.5k
  int status;
272
273
39.5k
  status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
274
39.5k
  if (status == 0)
275
39.5k
    {
276
39.5k
      unsigned long insn;
277
39.5k
      insn = (unsigned long) bfd_getl32 (buffer);
278
39.5k
      status = pru_disassemble (address, insn, info);
279
39.5k
    }
280
62
  else
281
62
    {
282
62
      (*info->memory_error_func) (status, address, info);
283
62
      status = -1;
284
62
    }
285
39.5k
  return status;
286
39.5k
}