cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

capstone.c (9149B)


      1/*
      2 * Interface to the capstone disassembler.
      3 * SPDX-License-Identifier: GPL-2.0-or-later
      4 */
      5
      6#include "qemu/osdep.h"
      7#include "qemu/bswap.h"
      8#include "disas/dis-asm.h"
      9#include "disas/capstone.h"
     10
     11
     12/*
     13 * Temporary storage for the capstone library.  This will be alloced via
     14 * malloc with a size private to the library; thus there's no reason not
     15 * to share this across calls and across host vs target disassembly.
     16 */
     17static __thread cs_insn *cap_insn;
     18
     19/*
     20 * The capstone library always skips 2 bytes for S390X.
     21 * This is less than ideal, since we can tell from the first two bits
     22 * the size of the insn and thus stay in sync with the insn stream.
     23 */
     24static size_t CAPSTONE_API
     25cap_skipdata_s390x_cb(const uint8_t *code, size_t code_size,
     26                      size_t offset, void *user_data)
     27{
     28    size_t ilen;
     29
     30    /* See get_ilen() in target/s390x/internal.h.  */
     31    switch (code[offset] >> 6) {
     32    case 0:
     33        ilen = 2;
     34        break;
     35    case 1:
     36    case 2:
     37        ilen = 4;
     38        break;
     39    default:
     40        ilen = 6;
     41        break;
     42    }
     43
     44    return ilen;
     45}
     46
     47static const cs_opt_skipdata cap_skipdata_s390x = {
     48    .mnemonic = ".byte",
     49    .callback = cap_skipdata_s390x_cb
     50};
     51
     52/*
     53 * Initialize the Capstone library.
     54 *
     55 * ??? It would be nice to cache this.  We would need one handle for the
     56 * host and one for the target.  For most targets we can reset specific
     57 * parameters via cs_option(CS_OPT_MODE, new_mode), but we cannot change
     58 * CS_ARCH_* in this way.  Thus we would need to be able to close and
     59 * re-open the target handle with a different arch for the target in order
     60 * to handle AArch64 vs AArch32 mode switching.
     61 */
     62static cs_err cap_disas_start(disassemble_info *info, csh *handle)
     63{
     64    cs_mode cap_mode = info->cap_mode;
     65    cs_err err;
     66
     67    cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN
     68                 : CS_MODE_LITTLE_ENDIAN);
     69
     70    err = cs_open(info->cap_arch, cap_mode, handle);
     71    if (err != CS_ERR_OK) {
     72        return err;
     73    }
     74
     75    /* "Disassemble" unknown insns as ".byte W,X,Y,Z".  */
     76    cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON);
     77
     78    switch (info->cap_arch) {
     79    case CS_ARCH_SYSZ:
     80        cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
     81                  (uintptr_t)&cap_skipdata_s390x);
     82        break;
     83
     84    case CS_ARCH_X86:
     85        /*
     86         * We don't care about errors (if for some reason the library
     87         * is compiled without AT&T syntax); the user will just have
     88         * to deal with the Intel syntax.
     89         */
     90        cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
     91        break;
     92    }
     93
     94    /* Allocate temp space for cs_disasm_iter.  */
     95    if (cap_insn == NULL) {
     96        cap_insn = cs_malloc(*handle);
     97        if (cap_insn == NULL) {
     98            cs_close(handle);
     99            return CS_ERR_MEM;
    100        }
    101    }
    102    return CS_ERR_OK;
    103}
    104
    105static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn,
    106                                int i, int n)
    107{
    108    fprintf_function print = info->fprintf_func;
    109    FILE *stream = info->stream;
    110
    111    switch (info->cap_insn_unit) {
    112    case 4:
    113        if (info->endian == BFD_ENDIAN_BIG) {
    114            for (; i < n; i += 4) {
    115                print(stream, " %08x", ldl_be_p(insn->bytes + i));
    116
    117            }
    118        } else {
    119            for (; i < n; i += 4) {
    120                print(stream, " %08x", ldl_le_p(insn->bytes + i));
    121            }
    122        }
    123        break;
    124
    125    case 2:
    126        if (info->endian == BFD_ENDIAN_BIG) {
    127            for (; i < n; i += 2) {
    128                print(stream, " %04x", lduw_be_p(insn->bytes + i));
    129            }
    130        } else {
    131            for (; i < n; i += 2) {
    132                print(stream, " %04x", lduw_le_p(insn->bytes + i));
    133            }
    134        }
    135        break;
    136
    137    default:
    138        for (; i < n; i++) {
    139            print(stream, " %02x", insn->bytes[i]);
    140        }
    141        break;
    142    }
    143}
    144
    145static void cap_dump_insn(disassemble_info *info, cs_insn *insn)
    146{
    147    fprintf_function print = info->fprintf_func;
    148    FILE *stream = info->stream;
    149    int i, n, split;
    150
    151    print(stream, "0x%08" PRIx64 ": ", insn->address);
    152
    153    n = insn->size;
    154    split = info->cap_insn_split;
    155
    156    /* Dump the first SPLIT bytes of the instruction.  */
    157    cap_dump_insn_units(info, insn, 0, MIN(n, split));
    158
    159    /* Add padding up to SPLIT so that mnemonics line up.  */
    160    if (n < split) {
    161        int width = (split - n) / info->cap_insn_unit;
    162        width *= (2 * info->cap_insn_unit + 1);
    163        print(stream, "%*s", width, "");
    164    }
    165
    166    /* Print the actual instruction.  */
    167    print(stream, "  %-8s %s\n", insn->mnemonic, insn->op_str);
    168
    169    /* Dump any remaining part of the insn on subsequent lines.  */
    170    for (i = split; i < n; i += split) {
    171        print(stream, "0x%08" PRIx64 ": ", insn->address + i);
    172        cap_dump_insn_units(info, insn, i, MIN(n, i + split));
    173        print(stream, "\n");
    174    }
    175}
    176
    177/* Disassemble SIZE bytes at PC for the target.  */
    178bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size)
    179{
    180    uint8_t cap_buf[1024];
    181    csh handle;
    182    cs_insn *insn;
    183    size_t csize = 0;
    184
    185    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
    186        return false;
    187    }
    188    insn = cap_insn;
    189
    190    while (1) {
    191        size_t tsize = MIN(sizeof(cap_buf) - csize, size);
    192        const uint8_t *cbuf = cap_buf;
    193
    194        info->read_memory_func(pc + csize, cap_buf + csize, tsize, info);
    195        csize += tsize;
    196        size -= tsize;
    197
    198        while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
    199            cap_dump_insn(info, insn);
    200        }
    201
    202        /* If the target memory is not consumed, go back for more... */
    203        if (size != 0) {
    204            /*
    205             * ... taking care to move any remaining fractional insn
    206             * to the beginning of the buffer.
    207             */
    208            if (csize != 0) {
    209                memmove(cap_buf, cbuf, csize);
    210            }
    211            continue;
    212        }
    213
    214        /*
    215         * Since the target memory is consumed, we should not have
    216         * a remaining fractional insn.
    217         */
    218        if (csize != 0) {
    219            info->fprintf_func(info->stream,
    220                "Disassembler disagrees with translator "
    221                "over instruction decoding\n"
    222                "Please report this to qemu-devel@nongnu.org\n");
    223        }
    224        break;
    225    }
    226
    227    cs_close(&handle);
    228    return true;
    229}
    230
    231/* Disassemble SIZE bytes at CODE for the host.  */
    232bool cap_disas_host(disassemble_info *info, const void *code, size_t size)
    233{
    234    csh handle;
    235    const uint8_t *cbuf;
    236    cs_insn *insn;
    237    uint64_t pc;
    238
    239    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
    240        return false;
    241    }
    242    insn = cap_insn;
    243
    244    cbuf = code;
    245    pc = (uintptr_t)code;
    246
    247    while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) {
    248        cap_dump_insn(info, insn);
    249    }
    250    if (size != 0) {
    251        info->fprintf_func(info->stream,
    252            "Disassembler disagrees with TCG over instruction encoding\n"
    253            "Please report this to qemu-devel@nongnu.org\n");
    254    }
    255
    256    cs_close(&handle);
    257    return true;
    258}
    259
    260/* Disassemble COUNT insns at PC for the target.  */
    261bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count)
    262{
    263    uint8_t cap_buf[32];
    264    csh handle;
    265    cs_insn *insn;
    266    size_t csize = 0;
    267
    268    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
    269        return false;
    270    }
    271    insn = cap_insn;
    272
    273    while (1) {
    274        /*
    275         * We want to read memory for one insn, but generically we do not
    276         * know how much memory that is.  We have a small buffer which is
    277         * known to be sufficient for all supported targets.  Try to not
    278         * read beyond the page, Just In Case.  For even more simplicity,
    279         * ignore the actual target page size and use a 1k boundary.  If
    280         * that turns out to be insufficient, we'll come back around the
    281         * loop and read more.
    282         */
    283        uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024);
    284        size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc);
    285        const uint8_t *cbuf = cap_buf;
    286
    287        /* Make certain that we can make progress.  */
    288        assert(tsize != 0);
    289        info->read_memory_func(pc + csize, cap_buf + csize, tsize, info);
    290        csize += tsize;
    291
    292        if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
    293            cap_dump_insn(info, insn);
    294            if (--count <= 0) {
    295                break;
    296            }
    297        }
    298        memmove(cap_buf, cbuf, csize);
    299    }
    300
    301    cs_close(&handle);
    302    return true;
    303}
    304
    305/* Disassemble a single instruction directly into plugin output */
    306bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size)
    307{
    308    uint8_t cap_buf[32];
    309    const uint8_t *cbuf = cap_buf;
    310    csh handle;
    311
    312    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
    313        return false;
    314    }
    315
    316    assert(size < sizeof(cap_buf));
    317    info->read_memory_func(pc, cap_buf, size, info);
    318
    319    if (cs_disasm_iter(handle, &cbuf, &size, &pc, cap_insn)) {
    320        info->fprintf_func(info->stream, "%s %s",
    321                           cap_insn->mnemonic, cap_insn->op_str);
    322    }
    323
    324    cs_close(&handle);
    325    return true;
    326}