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

replay.c (10565B)


      1/*
      2 * replay.c
      3 *
      4 * Copyright (c) 2010-2015 Institute for System Programming
      5 *                         of the Russian Academy of Sciences.
      6 *
      7 * This work is licensed under the terms of the GNU GPL, version 2 or later.
      8 * See the COPYING file in the top-level directory.
      9 *
     10 */
     11
     12#include "qemu/osdep.h"
     13#include "qapi/error.h"
     14#include "sysemu/cpu-timers.h"
     15#include "sysemu/replay.h"
     16#include "sysemu/runstate.h"
     17#include "replay-internal.h"
     18#include "qemu/main-loop.h"
     19#include "qemu/option.h"
     20#include "sysemu/cpus.h"
     21#include "qemu/error-report.h"
     22
     23/* Current version of the replay mechanism.
     24   Increase it when file format changes. */
     25#define REPLAY_VERSION              0xe0200a
     26/* Size of replay log header */
     27#define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
     28
     29ReplayMode replay_mode = REPLAY_MODE_NONE;
     30char *replay_snapshot;
     31
     32/* Name of replay file  */
     33static char *replay_filename;
     34ReplayState replay_state;
     35static GSList *replay_blockers;
     36
     37/* Replay breakpoints */
     38uint64_t replay_break_icount = -1ULL;
     39QEMUTimer *replay_break_timer;
     40
     41bool replay_next_event_is(int event)
     42{
     43    bool res = false;
     44
     45    /* nothing to skip - not all instructions used */
     46    if (replay_state.instruction_count != 0) {
     47        assert(replay_state.data_kind == EVENT_INSTRUCTION);
     48        return event == EVENT_INSTRUCTION;
     49    }
     50
     51    while (true) {
     52        unsigned int data_kind = replay_state.data_kind;
     53        if (event == data_kind) {
     54            res = true;
     55        }
     56        switch (data_kind) {
     57        case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
     58            replay_finish_event();
     59            qemu_system_shutdown_request(data_kind - EVENT_SHUTDOWN);
     60            break;
     61        default:
     62            /* clock, time_t, checkpoint and other events */
     63            return res;
     64        }
     65    }
     66    return res;
     67}
     68
     69uint64_t replay_get_current_icount(void)
     70{
     71    return icount_get_raw();
     72}
     73
     74int replay_get_instructions(void)
     75{
     76    int res = 0;
     77    replay_mutex_lock();
     78    if (replay_next_event_is(EVENT_INSTRUCTION)) {
     79        res = replay_state.instruction_count;
     80        if (replay_break_icount != -1LL) {
     81            uint64_t current = replay_get_current_icount();
     82            assert(replay_break_icount >= current);
     83            if (current + res > replay_break_icount) {
     84                res = replay_break_icount - current;
     85            }
     86        }
     87    }
     88    replay_mutex_unlock();
     89    return res;
     90}
     91
     92void replay_account_executed_instructions(void)
     93{
     94    if (replay_mode == REPLAY_MODE_PLAY) {
     95        g_assert(replay_mutex_locked());
     96        if (replay_state.instruction_count > 0) {
     97            replay_advance_current_icount(replay_get_current_icount());
     98        }
     99    }
    100}
    101
    102bool replay_exception(void)
    103{
    104
    105    if (replay_mode == REPLAY_MODE_RECORD) {
    106        g_assert(replay_mutex_locked());
    107        replay_save_instructions();
    108        replay_put_event(EVENT_EXCEPTION);
    109        return true;
    110    } else if (replay_mode == REPLAY_MODE_PLAY) {
    111        g_assert(replay_mutex_locked());
    112        bool res = replay_has_exception();
    113        if (res) {
    114            replay_finish_event();
    115        }
    116        return res;
    117    }
    118
    119    return true;
    120}
    121
    122bool replay_has_exception(void)
    123{
    124    bool res = false;
    125    if (replay_mode == REPLAY_MODE_PLAY) {
    126        g_assert(replay_mutex_locked());
    127        replay_account_executed_instructions();
    128        res = replay_next_event_is(EVENT_EXCEPTION);
    129    }
    130
    131    return res;
    132}
    133
    134bool replay_interrupt(void)
    135{
    136    if (replay_mode == REPLAY_MODE_RECORD) {
    137        g_assert(replay_mutex_locked());
    138        replay_save_instructions();
    139        replay_put_event(EVENT_INTERRUPT);
    140        return true;
    141    } else if (replay_mode == REPLAY_MODE_PLAY) {
    142        g_assert(replay_mutex_locked());
    143        bool res = replay_has_interrupt();
    144        if (res) {
    145            replay_finish_event();
    146        }
    147        return res;
    148    }
    149
    150    return true;
    151}
    152
    153bool replay_has_interrupt(void)
    154{
    155    bool res = false;
    156    if (replay_mode == REPLAY_MODE_PLAY) {
    157        g_assert(replay_mutex_locked());
    158        replay_account_executed_instructions();
    159        res = replay_next_event_is(EVENT_INTERRUPT);
    160    }
    161    return res;
    162}
    163
    164void replay_shutdown_request(ShutdownCause cause)
    165{
    166    if (replay_mode == REPLAY_MODE_RECORD) {
    167        g_assert(replay_mutex_locked());
    168        replay_put_event(EVENT_SHUTDOWN + cause);
    169    }
    170}
    171
    172bool replay_checkpoint(ReplayCheckpoint checkpoint)
    173{
    174    bool res = false;
    175    static bool in_checkpoint;
    176    assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
    177
    178    if (!replay_file) {
    179        return true;
    180    }
    181
    182    if (in_checkpoint) {
    183        /*
    184           Recursion occurs when HW event modifies timers.
    185           Prevent performing icount warp in this case and
    186           wait for another invocation of the checkpoint.
    187        */
    188        g_assert(replay_mode == REPLAY_MODE_PLAY);
    189        return false;
    190    }
    191    in_checkpoint = true;
    192
    193    replay_save_instructions();
    194
    195    if (replay_mode == REPLAY_MODE_PLAY) {
    196        g_assert(replay_mutex_locked());
    197        if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
    198            replay_finish_event();
    199        } else if (replay_state.data_kind != EVENT_ASYNC) {
    200            res = false;
    201            goto out;
    202        }
    203        replay_read_events(checkpoint);
    204        /* replay_read_events may leave some unread events.
    205           Return false if not all of the events associated with
    206           checkpoint were processed */
    207        res = replay_state.data_kind != EVENT_ASYNC;
    208    } else if (replay_mode == REPLAY_MODE_RECORD) {
    209        g_assert(replay_mutex_locked());
    210        replay_put_event(EVENT_CHECKPOINT + checkpoint);
    211        /* This checkpoint belongs to several threads.
    212           Processing events from different threads is
    213           non-deterministic */
    214        if (checkpoint != CHECKPOINT_CLOCK_WARP_START
    215            /* FIXME: this is temporary fix, other checkpoints
    216                      may also be invoked from the different threads someday.
    217                      Asynchronous event processing should be refactored
    218                      to create additional replay event kind which is
    219                      nailed to the one of the threads and which processes
    220                      the event queue. */
    221            && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
    222            replay_save_events(checkpoint);
    223        }
    224        res = true;
    225    }
    226out:
    227    in_checkpoint = false;
    228    return res;
    229}
    230
    231bool replay_has_checkpoint(void)
    232{
    233    bool res = false;
    234    if (replay_mode == REPLAY_MODE_PLAY) {
    235        g_assert(replay_mutex_locked());
    236        replay_account_executed_instructions();
    237        res = EVENT_CHECKPOINT <= replay_state.data_kind
    238              && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
    239    }
    240    return res;
    241}
    242
    243static void replay_enable(const char *fname, int mode)
    244{
    245    const char *fmode = NULL;
    246    assert(!replay_file);
    247
    248    switch (mode) {
    249    case REPLAY_MODE_RECORD:
    250        fmode = "wb";
    251        break;
    252    case REPLAY_MODE_PLAY:
    253        fmode = "rb";
    254        break;
    255    default:
    256        fprintf(stderr, "Replay: internal error: invalid replay mode\n");
    257        exit(1);
    258    }
    259
    260    atexit(replay_finish);
    261
    262    replay_file = fopen(fname, fmode);
    263    if (replay_file == NULL) {
    264        fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
    265        exit(1);
    266    }
    267
    268    replay_filename = g_strdup(fname);
    269    replay_mode = mode;
    270    replay_mutex_init();
    271
    272    replay_state.data_kind = -1;
    273    replay_state.instruction_count = 0;
    274    replay_state.current_icount = 0;
    275    replay_state.has_unread_data = 0;
    276
    277    /* skip file header for RECORD and check it for PLAY */
    278    if (replay_mode == REPLAY_MODE_RECORD) {
    279        fseek(replay_file, HEADER_SIZE, SEEK_SET);
    280    } else if (replay_mode == REPLAY_MODE_PLAY) {
    281        unsigned int version = replay_get_dword();
    282        if (version != REPLAY_VERSION) {
    283            fprintf(stderr, "Replay: invalid input log file version\n");
    284            exit(1);
    285        }
    286        /* go to the beginning */
    287        fseek(replay_file, HEADER_SIZE, SEEK_SET);
    288        replay_fetch_data_kind();
    289    }
    290
    291    replay_init_events();
    292}
    293
    294void replay_configure(QemuOpts *opts)
    295{
    296    const char *fname;
    297    const char *rr;
    298    ReplayMode mode = REPLAY_MODE_NONE;
    299    Location loc;
    300
    301    if (!opts) {
    302        return;
    303    }
    304
    305    loc_push_none(&loc);
    306    qemu_opts_loc_restore(opts);
    307
    308    rr = qemu_opt_get(opts, "rr");
    309    if (!rr) {
    310        /* Just enabling icount */
    311        goto out;
    312    } else if (!strcmp(rr, "record")) {
    313        mode = REPLAY_MODE_RECORD;
    314    } else if (!strcmp(rr, "replay")) {
    315        mode = REPLAY_MODE_PLAY;
    316    } else {
    317        error_report("Invalid icount rr option: %s", rr);
    318        exit(1);
    319    }
    320
    321    fname = qemu_opt_get(opts, "rrfile");
    322    if (!fname) {
    323        error_report("File name not specified for replay");
    324        exit(1);
    325    }
    326
    327    replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
    328    replay_vmstate_register();
    329    replay_enable(fname, mode);
    330
    331out:
    332    loc_pop(&loc);
    333}
    334
    335void replay_start(void)
    336{
    337    if (replay_mode == REPLAY_MODE_NONE) {
    338        return;
    339    }
    340
    341    if (replay_blockers) {
    342        error_reportf_err(replay_blockers->data, "Record/replay: ");
    343        exit(1);
    344    }
    345    if (!icount_enabled()) {
    346        error_report("Please enable icount to use record/replay");
    347        exit(1);
    348    }
    349
    350    /* Timer for snapshotting will be set up here. */
    351
    352    replay_enable_events();
    353}
    354
    355void replay_finish(void)
    356{
    357    if (replay_mode == REPLAY_MODE_NONE) {
    358        return;
    359    }
    360
    361    replay_save_instructions();
    362
    363    /* finalize the file */
    364    if (replay_file) {
    365        if (replay_mode == REPLAY_MODE_RECORD) {
    366            /*
    367             * Can't do it in the signal handler, therefore
    368             * add shutdown event here for the case of Ctrl-C.
    369             */
    370            replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
    371            /* write end event */
    372            replay_put_event(EVENT_END);
    373
    374            /* write header */
    375            fseek(replay_file, 0, SEEK_SET);
    376            replay_put_dword(REPLAY_VERSION);
    377        }
    378
    379        fclose(replay_file);
    380        replay_file = NULL;
    381    }
    382    if (replay_filename) {
    383        g_free(replay_filename);
    384        replay_filename = NULL;
    385    }
    386
    387    g_free(replay_snapshot);
    388    replay_snapshot = NULL;
    389
    390    replay_mode = REPLAY_MODE_NONE;
    391
    392    replay_finish_events();
    393}
    394
    395void replay_add_blocker(Error *reason)
    396{
    397    replay_blockers = g_slist_prepend(replay_blockers, reason);
    398}
    399
    400const char *replay_get_filename(void)
    401{
    402    return replay_filename;
    403}