1502 lines
38 KiB
C
1502 lines
38 KiB
C
/* Disassembler for RISC-V.
|
|
Copyright (C) 2019 Red Hat, Inc.
|
|
This file is part of elfutils.
|
|
Written by Ulrich Drepper <drepper@redhat.com>, 2019.
|
|
|
|
This file is free software; you can redistribute it and/or modify
|
|
it under the terms of either
|
|
|
|
* the GNU Lesser General Public License as published by the Free
|
|
Software Foundation; either version 3 of the License, or (at
|
|
your option) any later version
|
|
|
|
or
|
|
|
|
* the GNU General Public License as published by the Free
|
|
Software Foundation; either version 2 of the License, or (at
|
|
your option) any later version
|
|
|
|
or both in parallel, as here.
|
|
|
|
elfutils is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received copies of the GNU General Public License and
|
|
the GNU Lesser General Public License along with this program. If
|
|
not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../libebl/libeblP.h"
|
|
|
|
#define MACHINE_ENCODING __LITTLE_ENDIAN
|
|
#include "memory-access.h"
|
|
|
|
|
|
#define ADD_CHAR(ch) \
|
|
do { \
|
|
if (unlikely (bufcnt == bufsize)) \
|
|
goto enomem; \
|
|
buf[bufcnt++] = (ch); \
|
|
} while (0)
|
|
|
|
#define ADD_STRING(str) \
|
|
do { \
|
|
const char *_str0 = (str); \
|
|
size_t _len0 = strlen (_str0); \
|
|
ADD_NSTRING (_str0, _len0); \
|
|
} while (0)
|
|
|
|
#define ADD_NSTRING(str, len) \
|
|
do { \
|
|
const char *_str = (str); \
|
|
size_t _len = (len); \
|
|
if (unlikely (bufcnt + _len > bufsize)) \
|
|
goto enomem; \
|
|
memcpy (buf + bufcnt, _str, _len); \
|
|
bufcnt += _len; \
|
|
} while (0)
|
|
|
|
|
|
static const char *regnames[32] =
|
|
{
|
|
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
|
|
"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
|
|
"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
|
|
"s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
|
|
};
|
|
#define REG(nr) ((char *) regnames[nr])
|
|
#define REGP(nr) REG (8 + (nr))
|
|
|
|
|
|
static const char *fregnames[32] =
|
|
{
|
|
"ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7",
|
|
"fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5",
|
|
"fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
|
|
"fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"
|
|
};
|
|
#define FREG(nr) ((char *) fregnames[nr])
|
|
#define FREGP(nr) FREG (8 + (nr))
|
|
|
|
|
|
struct known_csrs
|
|
{
|
|
uint16_t nr;
|
|
const char *name;
|
|
};
|
|
|
|
static int compare_csr (const void *a, const void *b)
|
|
{
|
|
const struct known_csrs *ka = (const struct known_csrs *) a;
|
|
const struct known_csrs *kb = (const struct known_csrs *) b;
|
|
if (ka->nr < kb->nr)
|
|
return -1;
|
|
return ka->nr == kb->nr ? 0 : 1;
|
|
}
|
|
|
|
|
|
int
|
|
riscv_disasm (Ebl *ebl,
|
|
const uint8_t **startp, const uint8_t *end, GElf_Addr addr,
|
|
const char *fmt, DisasmOutputCB_t outcb,
|
|
DisasmGetSymCB_t symcb __attribute__((unused)),
|
|
void *outcbarg, void *symcbarg __attribute__((unused)))
|
|
{
|
|
const char *const save_fmt = fmt;
|
|
|
|
#define BUFSIZE 512
|
|
char initbuf[BUFSIZE];
|
|
size_t bufcnt;
|
|
size_t bufsize = BUFSIZE;
|
|
char *buf = initbuf;
|
|
|
|
int retval = 0;
|
|
while (1)
|
|
{
|
|
const uint8_t *data = *startp;
|
|
assert (data <= end);
|
|
if (data + 2 > end)
|
|
{
|
|
if (data != end)
|
|
retval = -1;
|
|
break;
|
|
}
|
|
uint16_t first = read_2ubyte_unaligned (data);
|
|
|
|
// Determine length.
|
|
size_t length;
|
|
if ((first & 0x3) != 0x3)
|
|
length = 2;
|
|
else if ((first & 0x1f) != 0x1f)
|
|
length = 4;
|
|
else if ((first & 0x3f) != 0x3f)
|
|
length = 6;
|
|
else if ((first & 0x7f) != 0x7f)
|
|
length = 8;
|
|
else
|
|
{
|
|
uint16_t nnn = (first >> 12) & 0x7;
|
|
if (nnn != 0x7)
|
|
length = 10 + 2 * nnn;
|
|
else
|
|
// This is invalid as of the RISC-V spec on 2019-06-21.
|
|
// The instruction is at least 192 bits in size so use
|
|
// this minimum size.
|
|
length = 24;
|
|
}
|
|
if (data + length > end)
|
|
{
|
|
retval = -1;
|
|
break;
|
|
}
|
|
|
|
char *mne = NULL;
|
|
char mnebuf[32];
|
|
char *op[5] = { NULL, NULL, NULL, NULL, NULL };
|
|
char immbuf[32];
|
|
size_t len;
|
|
char *strp = NULL;
|
|
char addrbuf[32];
|
|
bufcnt = 0;
|
|
int64_t opaddr;
|
|
if (length == 2)
|
|
{
|
|
size_t idx = (first >> 13) * 3 + (first & 0x3);
|
|
switch (idx)
|
|
{
|
|
uint16_t rd;
|
|
uint16_t rs1;
|
|
uint16_t rs2;
|
|
|
|
case 0:
|
|
if ((first & 0x1fe0) != 0)
|
|
{
|
|
mne = "addi";
|
|
op[0] = REGP ((first & 0x1c) >> 2);
|
|
op[1] = REG (2);
|
|
opaddr = (((first >> 1) & 0x3c0)
|
|
| ((first >> 7) & 0x30)
|
|
| ((first >> 2) & 0x8)
|
|
| ((first >> 4) & 0x4));
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64, opaddr);
|
|
op[2] = addrbuf;
|
|
}
|
|
else if (first == 0)
|
|
mne = "unimp";
|
|
break;
|
|
case 1:
|
|
rs1 = (first >> 7) & 0x1f;
|
|
int16_t nzimm = ((0 - ((first >> 7) & 0x20))
|
|
| ((first >> 2) & 0x1f));
|
|
if (rs1 == 0)
|
|
mne = nzimm == 0 ? "nop" : "c.nop";
|
|
else
|
|
{
|
|
mne = nzimm == 0 ? "c.addi" : "addi";
|
|
op[0] = op[1] = REG (rs1);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, nzimm);
|
|
op[2] = addrbuf;
|
|
}
|
|
break;
|
|
case 2:
|
|
rs1 = (first >> 7) & 0x1f;
|
|
op[0] = op[1] = REG (rs1);
|
|
opaddr = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f);
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
|
|
op[2] = addrbuf;
|
|
mne = rs1 == 0 ? "c.slli" : "slli";
|
|
break;
|
|
case 3:
|
|
op[0] = FREGP ((first >> 2) & 0x7);
|
|
opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)",
|
|
opaddr, REGP ((first >> 7) & 0x7));
|
|
op[1] = addrbuf;
|
|
mne = "fld";
|
|
break;
|
|
case 4:
|
|
if (ebl->class == ELFCLASS32)
|
|
{
|
|
mne = "jal";
|
|
opaddr = (((first << 3) & 0x20) | ((first >> 2) & 0xe)
|
|
| ((first << 1) & 0x80) | ((first >> 1) | 0x40)
|
|
| ((first << 2) & 0x400) | (first & 0xb00)
|
|
| ((first >> 6) & 0x10));
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
|
|
op[0] = addrbuf;
|
|
}
|
|
else
|
|
{
|
|
int32_t imm = (((UINT32_C (0) - ((first >> 12) & 0x1)) << 5)
|
|
| ((first >> 2) & 0x1f));
|
|
uint16_t reg = (first >> 7) & 0x1f;
|
|
if (reg == 0)
|
|
{
|
|
// Reserved
|
|
len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first);
|
|
strp = addrbuf;
|
|
}
|
|
else
|
|
{
|
|
if (imm == 0)
|
|
mne = "sext.w";
|
|
else
|
|
{
|
|
mne = "addiw";
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId32, imm);
|
|
op[2] = addrbuf;
|
|
}
|
|
op[0] = op[1] = REG (reg);
|
|
}
|
|
}
|
|
break;
|
|
case 5:
|
|
op[0] = FREG ((first >> 7) & 0x1f);
|
|
opaddr = ((first << 4) & 0x1c0) | ((first >> 7) & 0x20) | ((first >> 2) & 0x18);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2));
|
|
op[1] = addrbuf;
|
|
mne = "fld";
|
|
break;
|
|
case 6:
|
|
case 18:
|
|
mne = idx == 6 ? "lw" : "sw";
|
|
op[0] = REGP ((first >> 2) & 0x7);
|
|
opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40)
|
|
| ((first >> 4) & 0x4));
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
|
|
opaddr, REGP ((first >> 7) & 0x7));
|
|
op[1] = addrbuf;
|
|
break;
|
|
case 7:
|
|
mne = (first & 0xf80) == 0 ? "c.li" : "li";
|
|
op[0] = REG((first >> 7) & 0x1f);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId16,
|
|
(UINT16_C (0) - ((first >> 7) & 0x20)) | ((first >> 2) & 0x1f));
|
|
op[1] = addrbuf;
|
|
break;
|
|
case 8:
|
|
rd = ((first >> 7) & 0x1f);
|
|
if (rd == 0)
|
|
{
|
|
len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first);
|
|
strp = addrbuf;
|
|
}
|
|
else
|
|
{
|
|
uint16_t uimm = (((first << 4) & 0xc0)
|
|
| ((first >> 7) & 0x20)
|
|
| ((first >> 2) & 0x1c));
|
|
mne = "lw";
|
|
op[0] = REG (rd);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRIu16 "(%s)", uimm, REG (2));
|
|
op[1] = addrbuf;
|
|
}
|
|
break;
|
|
case 9:
|
|
if (ebl->class == ELFCLASS32)
|
|
{
|
|
mne = "flw";
|
|
op[0] = FREGP ((first >> 2) & 0x7);
|
|
opaddr = (((first << 1) & 0x40)
|
|
| ((first >> 7) & 0x38)
|
|
| ((first >> 4) & 0x4));
|
|
}
|
|
else
|
|
{
|
|
mne = "ld";
|
|
op[0] = REGP ((first >> 2) & 0x7);
|
|
opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0);
|
|
}
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
|
|
opaddr, REGP ((first >> 7) & 0x7));
|
|
op[1] = addrbuf;
|
|
break;
|
|
case 10:
|
|
if ((first & 0xf80) == (2 << 7))
|
|
{
|
|
mne = "addi";
|
|
op[0] = op[1] = REG (2);
|
|
opaddr = (((first >> 2) & 0x10) | ((first << 3) & 0x20)
|
|
| ((first << 1) & 0x40) | ((first << 4) & 0x180)
|
|
| ((UINT64_C (0) - ((first >> 12) & 0x1)) << 9));
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
|
|
op[2] = addrbuf;
|
|
}
|
|
else
|
|
{
|
|
mne = "lui";
|
|
op[0] = REG((first & 0xf80) >> 7);
|
|
opaddr = (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0x1f)
|
|
| ((first >> 2) & 0x1f));
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & 0xfffff);
|
|
op[1] = addrbuf;
|
|
}
|
|
break;
|
|
case 11:
|
|
if (ebl->class == ELFCLASS32)
|
|
{
|
|
mne = "flw";
|
|
op[0] = FREG ((first >> 7) & 0x1f);
|
|
opaddr = (((first << 4) & 0xc0)
|
|
| ((first >> 7) & 0x20)
|
|
| ((first >> 2) & 0x1c));
|
|
}
|
|
else
|
|
{
|
|
mne = "ld";
|
|
op[0] = REG ((first >> 7) & 0x1f);
|
|
opaddr = (((first << 4) & 0x1c0)
|
|
| ((first >> 7) & 0x20)
|
|
| ((first >> 2) & 0x18));
|
|
}
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
|
|
op[1] = addrbuf;
|
|
break;
|
|
case 13:
|
|
if ((first & 0xc00) != 0xc00)
|
|
{
|
|
int16_t imm = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f);
|
|
if ((first & 0xc00) == 0x800)
|
|
{
|
|
imm |= 0 - (imm & 0x20);
|
|
mne = "andi";
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, imm);
|
|
}
|
|
else
|
|
{
|
|
if (ebl->class != ELFCLASS32 || imm < 32)
|
|
{
|
|
mne = (first & 0x400) ? "srai" : "srli";
|
|
if (imm == 0)
|
|
{
|
|
strcpy (stpcpy (mnebuf, "c."), mne);
|
|
mne = mnebuf;
|
|
}
|
|
}
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, imm);
|
|
}
|
|
op[2] = addrbuf;
|
|
}
|
|
else
|
|
{
|
|
op[2] = REGP ((first >> 2) & 0x7);
|
|
static const char *const arithmne[8] =
|
|
{
|
|
"sub", "xor", "or", "and", "subw", "addw", NULL, NULL
|
|
};
|
|
mne = (char *) arithmne[((first >> 10) & 0x4) | ((first >> 5) & 0x3)];
|
|
}
|
|
op[0] = op[1] = REGP ((first >> 7) & 0x7);
|
|
break;
|
|
case 14:
|
|
rs1 = (first >> 7) & 0x1f;
|
|
rs2 = (first >> 2) & 0x1f;
|
|
op[0] = REG (rs1);
|
|
if ((first & 0x1000) == 0)
|
|
{
|
|
if (rs2 == 0)
|
|
{
|
|
op[1] = NULL;
|
|
if (rs1 == 1)
|
|
{
|
|
mne = "ret";
|
|
op[0] = NULL;
|
|
}
|
|
else
|
|
mne = "jr";
|
|
}
|
|
else
|
|
{
|
|
mne = rs1 != 0 ? "mv" : "c.mv";
|
|
op[1] = REG (rs2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rs2 == 0)
|
|
{
|
|
if (rs1 == 0)
|
|
{
|
|
mne = "ebreak";
|
|
op[0] = op[1] = NULL;
|
|
}
|
|
else
|
|
mne = "jalr";
|
|
}
|
|
else
|
|
{
|
|
mne = rs1 != 0 ? "add" : "c.add";
|
|
op[2] = REG (rs2);
|
|
op[1] = op[0];
|
|
}
|
|
}
|
|
break;
|
|
case 15:
|
|
op[0] = FREGP ((first >> 2) & 0x7);
|
|
opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)",
|
|
opaddr, REGP ((first >> 7) & 0x7));
|
|
op[1] = addrbuf;
|
|
mne = "fsd";
|
|
break;
|
|
case 16:
|
|
opaddr = (((UINT64_C (0) - ((first >> 12) & 0x1)) << 11)
|
|
| ((first << 2) & 0x400)
|
|
| ((first >> 1) & 0x300)
|
|
| ((first << 1) & 0x80)
|
|
| ((first >> 1) & 0x40)
|
|
| ((first << 3) & 0x20)
|
|
| ((first >> 7) & 0x10)
|
|
| ((first >> 2) & 0xe));
|
|
mne = "j";
|
|
// TODO translate address
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, addr + opaddr);
|
|
op[0] = addrbuf;
|
|
break;
|
|
case 17:
|
|
op[0] = FREG ((first >> 2) & 0x1f);
|
|
opaddr = ((first >> 1) & 0x1c0) | ((first >> 7) & 0x38);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2));
|
|
op[1] = addrbuf;
|
|
mne = "fsd";
|
|
break;
|
|
case 19:
|
|
case 22:
|
|
mne = idx == 19 ? "beqz" : "bnez";
|
|
op[0] = REG (8 + ((first >> 7) & 0x7));
|
|
opaddr = addr + (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0xff)
|
|
| ((first << 1) & 0xc0) | ((first << 3) & 0x20)
|
|
| ((first >> 7) & 0x18) | ((first >> 2) & 0x6));
|
|
// TODO translate address
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
|
|
op[1] = addrbuf;
|
|
break;
|
|
case 20:
|
|
op[0] = REG ((first >> 2) & 0x1f);
|
|
opaddr = ((first >> 1) & 0xc0) | ((first >> 7) & 0x3c);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
|
|
op[1] = addrbuf;
|
|
mne = "sw";
|
|
break;
|
|
case 21:
|
|
if (idx == 18 || ebl->class == ELFCLASS32)
|
|
{
|
|
mne = "fsw";
|
|
op[0] = FREGP ((first >> 2) & 0x7);
|
|
opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40)
|
|
| ((first >> 4) & 0x4));
|
|
}
|
|
else
|
|
{
|
|
mne = "sd";
|
|
op[0] = REGP ((first >> 2) & 0x7);
|
|
opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0);
|
|
}
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
|
|
opaddr, REGP ((first >> 7) & 0x7));
|
|
op[1] = addrbuf;
|
|
break;
|
|
case 23:
|
|
if (idx == 18 || ebl->class == ELFCLASS32)
|
|
{
|
|
mne = "fsw";
|
|
op[0] = FREG ((first & 0x7c) >> 2);
|
|
opaddr = ((first & 0x1e00) >> 7) | ((first & 0x180) >> 1);
|
|
}
|
|
else
|
|
{
|
|
mne = "sd";
|
|
op[0] = REG ((first & 0x7c) >> 2);
|
|
opaddr = ((first & 0x1c00) >> 7) | ((first & 0x380) >> 1);
|
|
}
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
|
|
op[1] = addrbuf;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (strp == NULL && mne == NULL)
|
|
{
|
|
len = snprintf (immbuf, sizeof (immbuf), "0x%04" PRIx16, first);
|
|
strp = immbuf;
|
|
}
|
|
}
|
|
else if (length == 4)
|
|
{
|
|
uint32_t word = read_4ubyte_unaligned (data);
|
|
size_t idx = (word >> 2) & 0x1f;
|
|
|
|
switch (idx)
|
|
{
|
|
static const char widthchar[4] = { 's', 'd', '\0', 'q' };
|
|
static const char intwidthchar[4] = { 'w', 'd', '\0', 'q' };
|
|
static const char *const rndmode[8] = { "rne", "rtz", "rdn", "rup", "rmm", "???", "???", "dyn" };
|
|
uint32_t rd;
|
|
uint32_t rs1;
|
|
uint32_t rs2;
|
|
uint32_t rs3;
|
|
uint32_t func;
|
|
|
|
case 0x00:
|
|
case 0x01:
|
|
// LOAD and LOAD-FP
|
|
rd = (word >> 7) & 0x1f;
|
|
op[0] = idx == 0x00 ? REG (rd) : FREG (rd);
|
|
opaddr = ((int32_t) word) >> 20;
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
|
|
opaddr, REG ((word >> 15) & 0x1f));
|
|
op[1] = addrbuf;
|
|
func = (word >> 12) & 0x7;
|
|
static const char *const loadmne[8] =
|
|
{
|
|
"lb", "lh", "lw", "ld", "lbu", "lhu", "lwu", NULL
|
|
};
|
|
static const char *const floadmne[8] =
|
|
{
|
|
NULL, NULL, "flw", "fld", "flq", NULL, NULL, NULL
|
|
};
|
|
mne = (char *) (idx == 0x00 ? loadmne[func] : floadmne[func]);
|
|
break;
|
|
case 0x03:
|
|
// MISC-MEM
|
|
rd = (word >> 7) & 0x1f;
|
|
rs1 = (word >> 15) & 0x1f;
|
|
func = (word >> 12) & 0x7;
|
|
|
|
if (word == 0x8330000f)
|
|
mne = "fence.tso";
|
|
else if (word == 0x0000100f)
|
|
mne = "fence.i";
|
|
else if (func == 0 && rd == 0 && rs1 == 0 && (word & 0xf0000000) == 0)
|
|
{
|
|
static const char *const order[16] =
|
|
{
|
|
"unknown", "w", "r", "rw", "o", "ow", "or", "orw",
|
|
"i", "iw", "ir", "irw", "io", "iow", "ior", "iorw"
|
|
};
|
|
uint32_t pred = (word >> 20) & 0xf;
|
|
uint32_t succ = (word >> 24) & 0xf;
|
|
if (pred != 0xf || succ != 0xf)
|
|
{
|
|
op[0] = (char *) order[succ];
|
|
op[1] = (char *) order[pred];
|
|
}
|
|
mne = "fence";
|
|
}
|
|
break;
|
|
case 0x04:
|
|
case 0x06:
|
|
// OP-IMM and OP-IMM32
|
|
rd = (word >> 7) & 0x1f;
|
|
op[0] = REG (rd);
|
|
rs1 = (word >> 15) & 0x1f;
|
|
op[1] = REG (rs1);
|
|
opaddr = ((int32_t) word) >> 20;
|
|
static const char *const opimmmne[8] =
|
|
{
|
|
"addi", NULL, "slti", "sltiu", "xori", NULL, "ori", "andi"
|
|
};
|
|
func = (word >> 12) & 0x7;
|
|
mne = (char *) opimmmne[func];
|
|
if (mne == NULL)
|
|
{
|
|
const uint64_t shiftmask = ebl->class == ELFCLASS32 ? 0x1f : 0x3f;
|
|
if (func == 0x1 && (opaddr & ~shiftmask) == 0)
|
|
mne = "slli";
|
|
else if (func == 0x5 && (opaddr & ~shiftmask) == 0)
|
|
mne = "srli";
|
|
else if (func == 0x5 && (opaddr & ~shiftmask) == 0x400)
|
|
mne = "srai";
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & shiftmask);
|
|
op[2] = addrbuf;
|
|
}
|
|
else if (func == 0x0 && (rd != 0 || idx == 0x06) && rs1 == 0 && rd != 0)
|
|
{
|
|
mne = "li";
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
|
|
op[1] = addrbuf;
|
|
}
|
|
else if (func == 0x00 && opaddr == 0)
|
|
{
|
|
if (idx == 0x06)
|
|
mne ="sext.";
|
|
else if (rd == 0)
|
|
{
|
|
mne = "nop";
|
|
op[0] = op[1] = NULL;
|
|
}
|
|
else
|
|
mne = "mv";
|
|
}
|
|
else if (func == 0x3 && opaddr == 1)
|
|
mne = "seqz";
|
|
else if (func == 0x4 && opaddr == -1)
|
|
{
|
|
mne = "not";
|
|
op[2] = NULL;
|
|
}
|
|
else
|
|
{
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
|
|
op[2] = addrbuf;
|
|
|
|
if (func == 0x0 && rs1 == 0 && rd != 0)
|
|
{
|
|
op[1] = op[2];
|
|
op[2] = NULL;
|
|
mne = "li";
|
|
}
|
|
}
|
|
if (mne != NULL && idx == 0x06)
|
|
{
|
|
mne = strcpy (mnebuf, mne);
|
|
strcat (mnebuf, "w");
|
|
}
|
|
break;
|
|
case 0x05:
|
|
case 0x0d:
|
|
// LUI and AUIPC
|
|
mne = idx == 0x05 ? "auipc" : "lui";
|
|
op[0] = REG ((word >> 7) & 0x1f);
|
|
opaddr = word >> 12;
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
|
|
op[1] = addrbuf;
|
|
break;
|
|
case 0x08:
|
|
case 0x09:
|
|
// STORE and STORE-FP
|
|
rs2 = (word >> 20) & 0x1f;
|
|
op[0] = idx == 0x08 ? REG (rs2) : FREG (rs2);
|
|
opaddr = ((((int64_t) ((int32_t) word) >> 20)) & ~0x1f) | ((word >> 7) & 0x1f);
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
|
|
opaddr, REG ((word >> 15) & 0x1f));
|
|
op[1] = addrbuf;
|
|
func = (word >> 12) & 0x7;
|
|
static const char *const storemne[8] =
|
|
{
|
|
"sb", "sh", "sw", "sd", NULL, NULL, NULL, NULL
|
|
};
|
|
static const char *const fstoremne[8] =
|
|
{
|
|
NULL, NULL, "fsw", "fsd", "fsq", NULL, NULL, NULL
|
|
};
|
|
mne = (char *) (idx == 0x08 ? storemne[func] : fstoremne[func]);
|
|
break;
|
|
case 0x0b:
|
|
// AMO
|
|
op[0] = REG ((word >> 7) & 0x1f);
|
|
rs1 = (word >> 15) & 0x1f;
|
|
rs2 = (word >> 20) & 0x1f;
|
|
snprintf (addrbuf, sizeof (addrbuf), "(%s)", REG (rs1));
|
|
op[2] = addrbuf;
|
|
size_t width = (word >> 12) & 0x7;
|
|
func = word >> 27;
|
|
static const char *const amomne[32] =
|
|
{
|
|
"amoadd", "amoswap", "lr", "sc", "amoxor", NULL, NULL, NULL,
|
|
"amoor", NULL, NULL, NULL, "amoand", NULL, NULL, NULL,
|
|
"amomin", NULL, NULL, NULL, "amomax", NULL, NULL, NULL,
|
|
"amominu", NULL, NULL, NULL, "amomaxu", NULL, NULL, NULL
|
|
};
|
|
if (amomne[func] != NULL && width >= 2 && width <= 3
|
|
&& (func != 0x02 || rs2 == 0))
|
|
{
|
|
if (func == 0x02)
|
|
{
|
|
op[1] = op[2];
|
|
op[2] = NULL;
|
|
}
|
|
else
|
|
op[1] = REG (rs2);
|
|
|
|
char *cp = stpcpy (mnebuf, amomne[func]);
|
|
*cp++ = '.';
|
|
*cp++ = " wd "[width];
|
|
assert (cp[-1] != ' ');
|
|
static const char *const aqrlstr[4] =
|
|
{
|
|
"", ".rl", ".aq", ".aqrl"
|
|
};
|
|
strcpy (cp, aqrlstr[(word >> 25) & 0x3]);
|
|
mne = mnebuf;
|
|
}
|
|
break;
|
|
case 0x0c:
|
|
case 0x0e:
|
|
// OP and OP-32
|
|
if ((word & 0xbc000000) == 0)
|
|
{
|
|
rs1 = (word >> 15) & 0x1f;
|
|
rs2 = (word >> 20) & 0x1f;
|
|
op[0] = REG ((word >> 7) & 0x1f);
|
|
func = ((word >> 21) & 0x10) | ((word >> 27) & 0x8) | ((word >> 12) & 0x7);
|
|
static const char *const arithmne2[32] =
|
|
{
|
|
"add", "sll", "slt", "sltu", "xor", "srl", "or", "and",
|
|
"sub", NULL, NULL, NULL, NULL, "sra", NULL, NULL,
|
|
"mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu",
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
static const char *const arithmne3[32] =
|
|
{
|
|
"addw", "sllw", NULL, NULL, NULL, "srlw", NULL, NULL,
|
|
"subw", NULL, NULL, NULL, NULL, "sraw", NULL, NULL,
|
|
"mulw", NULL, NULL, NULL, "divw", "divuw", "remw", "remuw",
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
if (func == 8 && rs1 == 0)
|
|
{
|
|
mne = idx == 0x0c ? "neg" : "negw";
|
|
op[1] = REG (rs2);
|
|
}
|
|
else if (idx == 0x0c && rs2 == 0 && func == 2)
|
|
{
|
|
op[1] = REG (rs1);
|
|
mne = "sltz";
|
|
}
|
|
else if (idx == 0x0c && rs1 == 0 && (func == 2 || func == 3))
|
|
{
|
|
op[1] = REG (rs2);
|
|
mne = func == 2 ? "sgtz" : "snez";
|
|
}
|
|
else
|
|
{
|
|
mne = (char *) (idx == 0x0c ? arithmne2[func] : arithmne3[func]);
|
|
op[1] = REG (rs1);
|
|
op[2] = REG (rs2);
|
|
}
|
|
}
|
|
break;
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
// MADD, MSUB, NMSUB, NMADD
|
|
if ((word & 0x06000000) != 0x04000000)
|
|
{
|
|
rd = (word >> 7) & 0x1f;
|
|
rs1 = (word >> 15) & 0x1f;
|
|
rs2 = (word >> 20) & 0x1f;
|
|
rs3 = (word >> 27) & 0x1f;
|
|
uint32_t rm = (word >> 12) & 0x7;
|
|
width = (word >> 25) & 0x3;
|
|
|
|
static const char *const fmamne[4] =
|
|
{
|
|
"fmadd.", "fmsub.", "fnmsub.", "fnmadd."
|
|
};
|
|
char *cp = stpcpy (mnebuf, fmamne[idx & 0x3]);
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
op[0] = FREG (rd);
|
|
op[1] = FREG (rs1);
|
|
op[2] = FREG (rs2);
|
|
op[3] = FREG (rs3);
|
|
if (rm != 0x7)
|
|
op[4] = (char *) rndmode[rm];
|
|
}
|
|
break;
|
|
case 0x14:
|
|
// OP-FP
|
|
if ((word & 0x06000000) != 0x04000000)
|
|
{
|
|
width = (word >> 25) & 0x3;
|
|
rd = (word >> 7) & 0x1f;
|
|
rs1 = (word >> 15) & 0x1f;
|
|
rs2 = (word >> 20) & 0x1f;
|
|
func = word >> 27;
|
|
uint32_t rm = (word >> 12) & 0x7;
|
|
if (func < 4)
|
|
{
|
|
static const char *const fpop[4] =
|
|
{
|
|
"fadd", "fsub", "fmul", "fdiv"
|
|
};
|
|
char *cp = stpcpy (mnebuf, fpop[func]);
|
|
*cp++ = '.';
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
op[0] = FREG (rd);
|
|
op[1] = FREG (rs1);
|
|
op[2] = FREG (rs2);
|
|
if (rm != 0x7)
|
|
op[3] = (char *) rndmode[rm];
|
|
}
|
|
else if (func == 0x1c && width != 2 && rs2 == 0 && rm <= 1)
|
|
{
|
|
char *cp;
|
|
if (rm == 0)
|
|
{
|
|
cp = stpcpy (mnebuf, "fmv.x.");
|
|
*cp++ = intwidthchar[width];
|
|
}
|
|
else
|
|
{
|
|
cp = stpcpy (mnebuf, "fclass.");
|
|
*cp++ = widthchar[width];
|
|
}
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
op[0] = REG (rd);
|
|
op[1] = FREG (rs1);
|
|
}
|
|
else if (func == 0x1e && width != 2 && rs2 == 0 && rm == 0)
|
|
{
|
|
char *cp = stpcpy (mnebuf, "fmv.");
|
|
*cp++ = intwidthchar[width];
|
|
strcpy (cp, ".x");
|
|
mne = mnebuf;
|
|
op[0] = FREG (rd);
|
|
op[1] = REG (rs1);
|
|
}
|
|
else if (func == 0x14)
|
|
{
|
|
uint32_t cmpop = (word >> 12) & 0x7;
|
|
if (cmpop < 3)
|
|
{
|
|
static const char *const mnefpcmp[3] =
|
|
{
|
|
"fle", "flt", "feq"
|
|
};
|
|
char *cp = stpcpy (mnebuf, mnefpcmp[cmpop]);
|
|
*cp++ = '.';
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
op[0] = REG (rd);
|
|
op[1] = FREG (rs1);
|
|
op[2] = FREG (rs2);
|
|
}
|
|
}
|
|
else if (func == 0x04)
|
|
{
|
|
uint32_t cmpop = (word >> 12) & 0x7;
|
|
if (cmpop < 3)
|
|
{
|
|
op[0] = FREG (rd);
|
|
op[1] = FREG (rs1);
|
|
|
|
static const char *const mnefpcmp[3] =
|
|
{
|
|
"fsgnj.", "fsgnjn.", "fsgnjx."
|
|
};
|
|
static const char *const altsignmne[3] =
|
|
{
|
|
"fmv.", "fneg.", "fabs."
|
|
};
|
|
char *cp = stpcpy (mnebuf, rs1 == rs2 ? altsignmne[cmpop] : mnefpcmp[cmpop]);
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
|
|
if (rs1 != rs2)
|
|
op[2] = FREG (rs2);
|
|
}
|
|
}
|
|
else if (func == 0x08 && width != 2 && rs2 <= 3 && rs2 != 2 && rs2 != width)
|
|
{
|
|
op[0] = FREG (rd);
|
|
op[1] = FREG (rs1);
|
|
char *cp = stpcpy (mnebuf, "fcvt.");
|
|
*cp++ = widthchar[width];
|
|
*cp++ = '.';
|
|
*cp++ = widthchar[rs2];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
}
|
|
else if ((func & 0x1d) == 0x18 && width != 2 && rs2 < 4)
|
|
{
|
|
char *cp = stpcpy (mnebuf, "fcvt.");
|
|
if (func == 0x18)
|
|
{
|
|
*cp++ = rs2 >= 2 ? 'l' : 'w';
|
|
if ((rs2 & 1) == 1)
|
|
*cp++ = 'u';
|
|
*cp++ = '.';
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
op[0] = REG (rd);
|
|
op[1] = FREG (rs1);
|
|
}
|
|
else
|
|
{
|
|
*cp++ = widthchar[width];
|
|
*cp++ = '.';
|
|
*cp++ = rs2 >= 2 ? 'l' : 'w';
|
|
if ((rs2 & 1) == 1)
|
|
*cp++ = 'u';
|
|
*cp = '\0';
|
|
op[0] = FREG (rd);
|
|
op[1] = REG (rs1);
|
|
}
|
|
mne = mnebuf;
|
|
if (rm != 0x7 && (func == 0x18 || width == 0 || rs2 >= 2))
|
|
op[2] = (char *) rndmode[rm];
|
|
}
|
|
else if (func == 0x0b && rs2 == 0)
|
|
{
|
|
op[0] = FREG (rd);
|
|
op[1] = FREG (rs1);
|
|
char *cp = stpcpy (mnebuf, "fsqrt.");
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
if (rm != 0x7)
|
|
op[2] = (char *) rndmode[rm];
|
|
}
|
|
else if (func == 0x05 && rm < 2)
|
|
{
|
|
op[0] = FREG (rd);
|
|
op[1] = FREG (rs1);
|
|
op[2] = FREG (rs2);
|
|
char *cp = stpcpy (mnebuf, rm == 0 ? "fmin." : "fmax.");
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
}
|
|
else if (func == 0x14 && rm <= 0x2)
|
|
{
|
|
op[0] = REG (rd);
|
|
op[1] = FREG (rs1);
|
|
op[2] = FREG (rs2);
|
|
static const char *const fltcmpmne[3] =
|
|
{
|
|
"fle.", "flt.", "feq."
|
|
};
|
|
char *cp = stpcpy (mnebuf, fltcmpmne[rm]);
|
|
*cp++ = widthchar[width];
|
|
*cp = '\0';
|
|
mne = mnebuf;
|
|
}
|
|
}
|
|
break;
|
|
case 0x18:
|
|
// BRANCH
|
|
rs1 = (word >> 15) & 0x1f;
|
|
op[0] = REG (rs1);
|
|
rs2 = (word >> 20) & 0x1f;
|
|
op[1] = REG (rs2);
|
|
opaddr = addr + (((UINT64_C (0) - (word >> 31)) << 12)
|
|
+ ((word << 4) & 0x800)
|
|
+ ((word >> 20) & 0x7e0)
|
|
+ ((word >> 7) & 0x1e));
|
|
// TODO translate address
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
|
|
op[2] = addrbuf;
|
|
static const char *const branchmne[8] =
|
|
{
|
|
"beq", "bne", NULL, NULL, "blt", "bge", "bltu", "bgeu"
|
|
};
|
|
func = (word >> 12) & 0x7;
|
|
mne = (char *) branchmne[func];
|
|
if (rs1 == 0 && func == 5)
|
|
{
|
|
op[0] = op[1];
|
|
op[1] = op[2];
|
|
op[2] = NULL;
|
|
mne = "blez";
|
|
}
|
|
else if (rs1 == 0 && func == 4)
|
|
{
|
|
op[0] = op[1];
|
|
op[1] = op[2];
|
|
op[2] = NULL;
|
|
mne = "bgtz";
|
|
}
|
|
else if (rs2 == 0)
|
|
{
|
|
if (func == 0 || func == 1 || func == 4 || func == 5)
|
|
{
|
|
op[1] = op[2];
|
|
op[2] = NULL;
|
|
strcpy (stpcpy (mnebuf, mne), "z");
|
|
mne = mnebuf;
|
|
}
|
|
}
|
|
else if (func == 5 || func == 7)
|
|
{
|
|
// binutils use these opcodes and the reverse parameter order
|
|
char *tmp = op[0];
|
|
op[0] = op[1];
|
|
op[1] = tmp;
|
|
mne = func == 5 ? "ble" : "bleu";
|
|
}
|
|
break;
|
|
case 0x19:
|
|
// JALR
|
|
if ((word & 0x7000) == 0)
|
|
{
|
|
rd = (word >> 7) & 0x1f;
|
|
rs1 = (word >> 15) & 0x1f;
|
|
opaddr = (int32_t) word >> 20;
|
|
size_t next = 0;
|
|
if (rd > 1)
|
|
op[next++] = REG (rd);
|
|
if (opaddr == 0)
|
|
{
|
|
if (rs1 != 0 || next == 0)
|
|
op[next] = REG (rs1);
|
|
}
|
|
else
|
|
{
|
|
snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (rs1));
|
|
op[next] = addrbuf;
|
|
}
|
|
mne = rd == 0 ? "jr" : "jalr";
|
|
}
|
|
break;
|
|
case 0x1b:
|
|
// JAL
|
|
rd = (word >> 7) & 0x1f;
|
|
if (rd != 0)
|
|
op[0] = REG (rd);
|
|
opaddr = addr + ((UINT64_C (0) - ((word >> 11) & 0x100000))
|
|
| (word & 0xff000)
|
|
| ((word >> 9) & 0x800)
|
|
| ((word >> 20) & 0x7fe));
|
|
// TODO translate address
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
|
|
op[rd != 0] = addrbuf;
|
|
mne = rd == 0 ? "j" : "jal";
|
|
break;
|
|
case 0x1c:
|
|
// SYSTEM
|
|
rd = (word >> 7) & 0x1f;
|
|
rs1 = (word >> 15) & 0x1f;
|
|
if (word == 0x00000073)
|
|
mne = "ecall";
|
|
else if (word == 0x00100073)
|
|
mne = "ebreak";
|
|
else if (word == 0x00200073)
|
|
mne = "uret";
|
|
else if (word == 0x10200073)
|
|
mne = "sret";
|
|
else if (word == 0x30200073)
|
|
mne = "mret";
|
|
else if (word == 0x10500073)
|
|
mne = "wfi";
|
|
else if ((word & 0x3000) == 0x2000 && rs1 == 0)
|
|
{
|
|
uint32_t csr = word >> 20;
|
|
if (/* csr >= 0x000 && */ csr <= 0x007)
|
|
{
|
|
static const char *const unprivrw[4] =
|
|
{
|
|
NULL, "frflags", "frrm", "frsr",
|
|
};
|
|
mne = (char *) unprivrw[csr - 0x000];
|
|
}
|
|
else if (csr >= 0xc00 && csr <= 0xc03)
|
|
{
|
|
static const char *const unprivrolow[3] =
|
|
{
|
|
"rdcycle", "rdtime", "rdinstret"
|
|
};
|
|
mne = (char *) unprivrolow[csr - 0xc00];
|
|
}
|
|
op[0] = REG ((word >> 7) & 0x1f);
|
|
}
|
|
else if ((word & 0x3000) == 0x1000 && rd == 0)
|
|
{
|
|
uint32_t csr = word >> 20;
|
|
if (/* csr >= 0x000 && */ csr <= 0x003)
|
|
{
|
|
static const char *const unprivrs[4] =
|
|
{
|
|
NULL, "fsflags", "fsrm", "fssr",
|
|
};
|
|
static const char *const unprivrsi[4] =
|
|
{
|
|
NULL, "fsflagsi", "fsrmi", NULL
|
|
};
|
|
mne = (char *) ((word & 0x4000) == 0 ? unprivrs : unprivrsi)[csr - 0x000];
|
|
|
|
if ((word & 0x4000) == 0)
|
|
op[0] = REG ((word >> 15) & 0x1f);
|
|
else
|
|
{
|
|
snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & 0x1f);
|
|
op[0] = immbuf;
|
|
}
|
|
}
|
|
}
|
|
if (mne == NULL && (word & 0x3000) != 0)
|
|
{
|
|
static const char *const mnecsr[8] =
|
|
{
|
|
NULL, "csrrw", "csrrs", "csrrc",
|
|
NULL, "csrrwi", "csrrsi", "csrrci"
|
|
};
|
|
static const struct known_csrs known[] =
|
|
{
|
|
// This list must remain sorted by NR.
|
|
{ 0x000, "ustatus" },
|
|
{ 0x001, "fflags" },
|
|
{ 0x002, "fram" },
|
|
{ 0x003, "fcsr" },
|
|
{ 0x004, "uie" },
|
|
{ 0x005, "utvec" },
|
|
{ 0x040, "uscratch" },
|
|
{ 0x041, "uepc" },
|
|
{ 0x042, "ucause" },
|
|
{ 0x043, "utval" },
|
|
{ 0x044, "uip" },
|
|
{ 0x100, "sstatus" },
|
|
{ 0x102, "sedeleg" },
|
|
{ 0x103, "sideleg" },
|
|
{ 0x104, "sie" },
|
|
{ 0x105, "stvec" },
|
|
{ 0x106, "scounteren" },
|
|
{ 0x140, "sscratch" },
|
|
{ 0x141, "sepc" },
|
|
{ 0x142, "scause" },
|
|
{ 0x143, "stval" },
|
|
{ 0x144, "sip" },
|
|
{ 0x180, "satp" },
|
|
{ 0x200, "vsstatus" },
|
|
{ 0x204, "vsie" },
|
|
{ 0x205, "vstvec" },
|
|
{ 0x240, "vsscratch" },
|
|
{ 0x241, "vsepc" },
|
|
{ 0x242, "vscause" },
|
|
{ 0x243, "vstval" },
|
|
{ 0x244, "vsip" },
|
|
{ 0x280, "vsatp" },
|
|
{ 0x600, "hstatus" },
|
|
{ 0x602, "hedeleg" },
|
|
{ 0x603, "hideleg" },
|
|
{ 0x605, "htimedelta" },
|
|
{ 0x606, "hcounteren" },
|
|
{ 0x615, "htimedeltah" },
|
|
{ 0x680, "hgatp" },
|
|
{ 0xc00, "cycle" },
|
|
{ 0xc01, "time" },
|
|
{ 0xc02, "instret" },
|
|
{ 0xc03, "hpmcounter3" },
|
|
{ 0xc04, "hpmcounter4" },
|
|
{ 0xc05, "hpmcounter5" },
|
|
{ 0xc06, "hpmcounter6" },
|
|
{ 0xc07, "hpmcounter7" },
|
|
{ 0xc08, "hpmcounter8" },
|
|
{ 0xc09, "hpmcounter9" },
|
|
{ 0xc0a, "hpmcounter10" },
|
|
{ 0xc0b, "hpmcounter11" },
|
|
{ 0xc0c, "hpmcounter12" },
|
|
{ 0xc0d, "hpmcounter13" },
|
|
{ 0xc0e, "hpmcounter14" },
|
|
{ 0xc0f, "hpmcounter15" },
|
|
{ 0xc10, "hpmcounter16" },
|
|
{ 0xc11, "hpmcounter17" },
|
|
{ 0xc12, "hpmcounter18" },
|
|
{ 0xc13, "hpmcounter19" },
|
|
{ 0xc14, "hpmcounter20" },
|
|
{ 0xc15, "hpmcounter21" },
|
|
{ 0xc16, "hpmcounter22" },
|
|
{ 0xc17, "hpmcounter23" },
|
|
{ 0xc18, "hpmcounter24" },
|
|
{ 0xc19, "hpmcounter25" },
|
|
{ 0xc1a, "hpmcounter26" },
|
|
{ 0xc1b, "hpmcounter27" },
|
|
{ 0xc1c, "hpmcounter28" },
|
|
{ 0xc1d, "hpmcounter29" },
|
|
{ 0xc1e, "hpmcounter30" },
|
|
{ 0xc1f, "hpmcounter31" },
|
|
{ 0xc80, "cycleh" },
|
|
{ 0xc81, "timeh" },
|
|
{ 0xc82, "instreth" },
|
|
{ 0xc83, "hpmcounter3h" },
|
|
{ 0xc84, "hpmcounter4h" },
|
|
{ 0xc85, "hpmcounter5h" },
|
|
{ 0xc86, "hpmcounter6h" },
|
|
{ 0xc87, "hpmcounter7h" },
|
|
{ 0xc88, "hpmcounter8h" },
|
|
{ 0xc89, "hpmcounter9h" },
|
|
{ 0xc8a, "hpmcounter10h" },
|
|
{ 0xc8b, "hpmcounter11h" },
|
|
{ 0xc8c, "hpmcounter12h" },
|
|
{ 0xc8d, "hpmcounter13h" },
|
|
{ 0xc8e, "hpmcounter14h" },
|
|
{ 0xc8f, "hpmcounter15h" },
|
|
{ 0xc90, "hpmcounter16h" },
|
|
{ 0xc91, "hpmcounter17h" },
|
|
{ 0xc92, "hpmcounter18h" },
|
|
{ 0xc93, "hpmcounter19h" },
|
|
{ 0xc94, "hpmcounter20h" },
|
|
{ 0xc95, "hpmcounter21h" },
|
|
{ 0xc96, "hpmcounter22h" },
|
|
{ 0xc97, "hpmcounter23h" },
|
|
{ 0xc98, "hpmcounter24h" },
|
|
{ 0xc99, "hpmcounter25h" },
|
|
{ 0xc9a, "hpmcounter26h" },
|
|
{ 0xc9b, "hpmcounter27h" },
|
|
{ 0xc9c, "hpmcounter28h" },
|
|
{ 0xc9d, "hpmcounter29h" },
|
|
{ 0xc9e, "hpmcounter30h" },
|
|
{ 0xc9f, "hpmcounter31h" },
|
|
};
|
|
uint32_t csr = word >> 20;
|
|
uint32_t instr = (word >> 12) & 0x7;
|
|
size_t last = 0;
|
|
if (rd != 0)
|
|
op[last++] = REG (rd);
|
|
struct known_csrs key = { csr, NULL };
|
|
struct known_csrs *found = bsearch (&key, known,
|
|
sizeof (known) / sizeof (known[0]),
|
|
sizeof (known[0]),
|
|
compare_csr);
|
|
if (found)
|
|
op[last] = (char *) found->name;
|
|
else
|
|
{
|
|
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx32, csr);
|
|
op[last] = addrbuf;
|
|
}
|
|
++last;
|
|
if ((word & 0x4000) == 0)
|
|
op[last] = REG ((word >> 15) & 0x1f);
|
|
else
|
|
{
|
|
snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & UINT32_C(0x1f));
|
|
op[last] = immbuf;
|
|
}
|
|
if (instr == 1 && rd == 0)
|
|
mne = "csrw";
|
|
else if (instr == 2 && rd == 0)
|
|
mne = "csrs";
|
|
else if (instr == 6 && rd == 0)
|
|
mne = "csrsi";
|
|
else if (instr == 2 && rs1 == 0)
|
|
mne = "csrr";
|
|
else if (instr == 3 && rd == 0)
|
|
mne = "csrc";
|
|
else
|
|
mne = (char *) mnecsr[instr];
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (strp == NULL && mne == NULL)
|
|
{
|
|
len = snprintf (addrbuf, sizeof (addrbuf), "0x%08" PRIx32, word);
|
|
strp = addrbuf;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No instruction encodings defined for these sizes yet.
|
|
char *cp = stpcpy (mnebuf, "0x");
|
|
assert (length % 2 == 0);
|
|
for (size_t i = 0; i < length; i += 2)
|
|
cp += snprintf (cp, mnebuf + sizeof (mnebuf) - cp, "%04" PRIx16,
|
|
read_2ubyte_unaligned (data + i));
|
|
strp = mnebuf;
|
|
len = cp - mnebuf;
|
|
}
|
|
|
|
if (strp == NULL)
|
|
{
|
|
|
|
if (0)
|
|
{
|
|
/* Resize the buffer. */
|
|
char *oldbuf;
|
|
enomem:
|
|
oldbuf = buf;
|
|
if (buf == initbuf)
|
|
buf = malloc (2 * bufsize);
|
|
else
|
|
buf = realloc (buf, 2 * bufsize);
|
|
if (buf == NULL)
|
|
{
|
|
buf = oldbuf;
|
|
retval = ENOMEM;
|
|
goto do_ret;
|
|
}
|
|
bufsize *= 2;
|
|
|
|
bufcnt = 0;
|
|
}
|
|
|
|
unsigned long string_end_idx = 0;
|
|
fmt = save_fmt;
|
|
const char *deferred_start = NULL;
|
|
size_t deferred_len = 0;
|
|
// XXX Can we get this from color.c?
|
|
static const char color_off[] = "\e[0m";
|
|
while (*fmt != '\0')
|
|
{
|
|
if (*fmt != '%')
|
|
{
|
|
char ch = *fmt++;
|
|
if (ch == '\\')
|
|
{
|
|
switch ((ch = *fmt++))
|
|
{
|
|
case '0' ... '7':
|
|
{
|
|
int val = ch - '0';
|
|
ch = *fmt;
|
|
if (ch >= '0' && ch <= '7')
|
|
{
|
|
val *= 8;
|
|
val += ch - '0';
|
|
ch = *++fmt;
|
|
if (ch >= '0' && ch <= '7' && val < 32)
|
|
{
|
|
val *= 8;
|
|
val += ch - '0';
|
|
++fmt;
|
|
}
|
|
}
|
|
ch = val;
|
|
}
|
|
break;
|
|
|
|
case 'n':
|
|
ch = '\n';
|
|
break;
|
|
|
|
case 't':
|
|
ch = '\t';
|
|
break;
|
|
|
|
default:
|
|
retval = EINVAL;
|
|
goto do_ret;
|
|
}
|
|
}
|
|
else if (ch == '\e' && *fmt == '[')
|
|
{
|
|
deferred_start = fmt - 1;
|
|
do
|
|
++fmt;
|
|
while (*fmt != 'm' && *fmt != '\0');
|
|
|
|
if (*fmt == 'm')
|
|
{
|
|
deferred_len = ++fmt - deferred_start;
|
|
continue;
|
|
}
|
|
|
|
fmt = deferred_start + 1;
|
|
deferred_start = NULL;
|
|
}
|
|
ADD_CHAR (ch);
|
|
continue;
|
|
}
|
|
++fmt;
|
|
|
|
int width = 0;
|
|
while (isdigit (*fmt))
|
|
width = width * 10 + (*fmt++ - '0');
|
|
|
|
int prec = 0;
|
|
if (*fmt == '.')
|
|
while (isdigit (*++fmt))
|
|
prec = prec * 10 + (*fmt - '0');
|
|
|
|
size_t start_idx = bufcnt;
|
|
size_t non_printing = 0;
|
|
switch (*fmt++)
|
|
{
|
|
case 'm':
|
|
if (deferred_start != NULL)
|
|
{
|
|
ADD_NSTRING (deferred_start, deferred_len);
|
|
non_printing += deferred_len;
|
|
}
|
|
|
|
ADD_STRING (mne);
|
|
|
|
if (deferred_start != NULL)
|
|
{
|
|
ADD_STRING (color_off);
|
|
non_printing += strlen (color_off);
|
|
}
|
|
|
|
string_end_idx = bufcnt;
|
|
break;
|
|
|
|
case 'o':
|
|
if (op[prec - 1] != NULL)
|
|
{
|
|
if (deferred_start != NULL)
|
|
{
|
|
ADD_NSTRING (deferred_start, deferred_len);
|
|
non_printing += deferred_len;
|
|
}
|
|
|
|
ADD_STRING (op[prec - 1]);
|
|
|
|
if (deferred_start != NULL)
|
|
{
|
|
ADD_STRING (color_off);
|
|
non_printing += strlen (color_off);
|
|
}
|
|
|
|
string_end_idx = bufcnt;
|
|
}
|
|
else
|
|
bufcnt = string_end_idx;
|
|
break;
|
|
|
|
case 'e':
|
|
string_end_idx = bufcnt;
|
|
break;
|
|
|
|
case 'a':
|
|
/* Pad to requested column. */
|
|
while (bufcnt - non_printing < (size_t) width)
|
|
ADD_CHAR (' ');
|
|
width = 0;
|
|
break;
|
|
|
|
case 'l':
|
|
// TODO
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* Pad according to the specified width. */
|
|
while (bufcnt - non_printing < start_idx + width)
|
|
ADD_CHAR (' ');
|
|
}
|
|
|
|
strp = buf;
|
|
len = bufcnt;
|
|
}
|
|
|
|
addr += length;
|
|
*startp = data + length;
|
|
retval = outcb (strp, len, outcbarg);
|
|
if (retval != 0)
|
|
break;
|
|
}
|
|
|
|
do_ret:
|
|
if (buf != initbuf)
|
|
free (buf);
|
|
|
|
return retval;
|
|
}
|