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

resettable.c (9678B)


      1/*
      2 * Resettable interface.
      3 *
      4 * Copyright (c) 2019 GreenSocs SAS
      5 *
      6 * Authors:
      7 *   Damien Hedde
      8 *
      9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
     10 * See the COPYING file in the top-level directory.
     11 */
     12
     13#include "qemu/osdep.h"
     14#include "qemu/module.h"
     15#include "hw/resettable.h"
     16#include "trace.h"
     17
     18/**
     19 * resettable_phase_enter/hold/exit:
     20 * Function executing a phase recursively in a resettable object and its
     21 * children.
     22 */
     23static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
     24static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
     25static void resettable_phase_exit(Object *obj, void *opaque, ResetType type);
     26
     27/**
     28 * enter_phase_in_progress:
     29 * True if we are currently in reset enter phase.
     30 *
     31 * exit_phase_in_progress:
     32 * count the number of exit phase we are in.
     33 *
     34 * Note: These flags are only used to guarantee (using asserts) that the reset
     35 * API is used correctly. We can use global variables because we rely on the
     36 * iothread mutex to ensure only one reset operation is in a progress at a
     37 * given time.
     38 */
     39static bool enter_phase_in_progress;
     40static unsigned exit_phase_in_progress;
     41
     42void resettable_reset(Object *obj, ResetType type)
     43{
     44    trace_resettable_reset(obj, type);
     45    resettable_assert_reset(obj, type);
     46    resettable_release_reset(obj, type);
     47}
     48
     49void resettable_assert_reset(Object *obj, ResetType type)
     50{
     51    /* TODO: change this assert when adding support for other reset types */
     52    assert(type == RESET_TYPE_COLD);
     53    trace_resettable_reset_assert_begin(obj, type);
     54    assert(!enter_phase_in_progress);
     55
     56    enter_phase_in_progress = true;
     57    resettable_phase_enter(obj, NULL, type);
     58    enter_phase_in_progress = false;
     59
     60    resettable_phase_hold(obj, NULL, type);
     61
     62    trace_resettable_reset_assert_end(obj);
     63}
     64
     65void resettable_release_reset(Object *obj, ResetType type)
     66{
     67    /* TODO: change this assert when adding support for other reset types */
     68    assert(type == RESET_TYPE_COLD);
     69    trace_resettable_reset_release_begin(obj, type);
     70    assert(!enter_phase_in_progress);
     71
     72    exit_phase_in_progress += 1;
     73    resettable_phase_exit(obj, NULL, type);
     74    exit_phase_in_progress -= 1;
     75
     76    trace_resettable_reset_release_end(obj);
     77}
     78
     79bool resettable_is_in_reset(Object *obj)
     80{
     81    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
     82    ResettableState *s = rc->get_state(obj);
     83
     84    return s->count > 0;
     85}
     86
     87/**
     88 * resettable_child_foreach:
     89 * helper to avoid checking the existence of the method.
     90 */
     91static void resettable_child_foreach(ResettableClass *rc, Object *obj,
     92                                     ResettableChildCallback cb,
     93                                     void *opaque, ResetType type)
     94{
     95    if (rc->child_foreach) {
     96        rc->child_foreach(obj, cb, opaque, type);
     97    }
     98}
     99
    100/**
    101 * resettable_get_tr_func:
    102 * helper to fetch transitional reset callback if any.
    103 */
    104static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc,
    105                                                   Object *obj)
    106{
    107    ResettableTrFunction tr_func = NULL;
    108    if (rc->get_transitional_function) {
    109        tr_func = rc->get_transitional_function(obj);
    110    }
    111    return tr_func;
    112}
    113
    114static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
    115{
    116    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
    117    ResettableState *s = rc->get_state(obj);
    118    const char *obj_typename = object_get_typename(obj);
    119    bool action_needed = false;
    120
    121    /* exit phase has to finish properly before entering back in reset */
    122    assert(!s->exit_phase_in_progress);
    123
    124    trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
    125
    126    /* Only take action if we really enter reset for the 1st time. */
    127    /*
    128     * TODO: if adding more ResetType support, some additional checks
    129     * are probably needed here.
    130     */
    131    if (s->count++ == 0) {
    132        action_needed = true;
    133    }
    134    /*
    135     * We limit the count to an arbitrary "big" value. The value is big
    136     * enough not to be triggered normally.
    137     * The assert will stop an infinite loop if there is a cycle in the
    138     * reset tree. The loop goes through resettable_foreach_child below
    139     * which at some point will call us again.
    140     */
    141    assert(s->count <= 50);
    142
    143    /*
    144     * handle the children even if action_needed is at false so that
    145     * child counts are incremented too
    146     */
    147    resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
    148
    149    /* execute enter phase for the object if needed */
    150    if (action_needed) {
    151        trace_resettable_phase_enter_exec(obj, obj_typename, type,
    152                                          !!rc->phases.enter);
    153        if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) {
    154            rc->phases.enter(obj, type);
    155        }
    156        s->hold_phase_pending = true;
    157    }
    158    trace_resettable_phase_enter_end(obj, obj_typename, s->count);
    159}
    160
    161static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
    162{
    163    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
    164    ResettableState *s = rc->get_state(obj);
    165    const char *obj_typename = object_get_typename(obj);
    166
    167    /* exit phase has to finish properly before entering back in reset */
    168    assert(!s->exit_phase_in_progress);
    169
    170    trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
    171
    172    /* handle children first */
    173    resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
    174
    175    /* exec hold phase */
    176    if (s->hold_phase_pending) {
    177        s->hold_phase_pending = false;
    178        ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj);
    179        trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
    180        if (tr_func) {
    181            trace_resettable_transitional_function(obj, obj_typename);
    182            tr_func(obj);
    183        } else if (rc->phases.hold) {
    184            rc->phases.hold(obj);
    185        }
    186    }
    187    trace_resettable_phase_hold_end(obj, obj_typename, s->count);
    188}
    189
    190static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
    191{
    192    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
    193    ResettableState *s = rc->get_state(obj);
    194    const char *obj_typename = object_get_typename(obj);
    195
    196    assert(!s->exit_phase_in_progress);
    197    trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
    198
    199    /* exit_phase_in_progress ensures this phase is 'atomic' */
    200    s->exit_phase_in_progress = true;
    201    resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
    202
    203    assert(s->count > 0);
    204    if (s->count == 1) {
    205        trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
    206        if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) {
    207            rc->phases.exit(obj);
    208        }
    209        s->count = 0;
    210    }
    211    s->exit_phase_in_progress = false;
    212    trace_resettable_phase_exit_end(obj, obj_typename, s->count);
    213}
    214
    215/*
    216 * resettable_get_count:
    217 * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
    218 */
    219static unsigned resettable_get_count(Object *obj)
    220{
    221    if (obj) {
    222        ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
    223        return rc->get_state(obj)->count;
    224    }
    225    return 0;
    226}
    227
    228void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
    229{
    230    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
    231    ResettableState *s = rc->get_state(obj);
    232    unsigned newp_count = resettable_get_count(newp);
    233    unsigned oldp_count = resettable_get_count(oldp);
    234
    235    /*
    236     * Ensure we do not change parent when in enter or exit phase.
    237     * During these phases, the reset subtree being updated is partly in reset
    238     * and partly not in reset (it depends on the actual position in
    239     * resettable_child_foreach()s). We are not able to tell in which part is a
    240     * leaving or arriving device. Thus we cannot set the reset count of the
    241     * moving device to the proper value.
    242     */
    243    assert(!enter_phase_in_progress && !exit_phase_in_progress);
    244    trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
    245
    246    /*
    247     * At most one of the two 'for' loops will be executed below
    248     * in order to cope with the difference between the two counts.
    249     */
    250    /* if newp is more reset than oldp */
    251    for (unsigned i = oldp_count; i < newp_count; i++) {
    252        resettable_assert_reset(obj, RESET_TYPE_COLD);
    253    }
    254    /*
    255     * if obj is leaving a bus under reset, we need to ensure
    256     * hold phase is not pending.
    257     */
    258    if (oldp_count && s->hold_phase_pending) {
    259        resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
    260    }
    261    /* if oldp is more reset than newp */
    262    for (unsigned i = newp_count; i < oldp_count; i++) {
    263        resettable_release_reset(obj, RESET_TYPE_COLD);
    264    }
    265}
    266
    267void resettable_cold_reset_fn(void *opaque)
    268{
    269    resettable_reset((Object *) opaque, RESET_TYPE_COLD);
    270}
    271
    272void resettable_class_set_parent_phases(ResettableClass *rc,
    273                                        ResettableEnterPhase enter,
    274                                        ResettableHoldPhase hold,
    275                                        ResettableExitPhase exit,
    276                                        ResettablePhases *parent_phases)
    277{
    278    *parent_phases = rc->phases;
    279    if (enter) {
    280        rc->phases.enter = enter;
    281    }
    282    if (hold) {
    283        rc->phases.hold = hold;
    284    }
    285    if (exit) {
    286        rc->phases.exit = exit;
    287    }
    288}
    289
    290static const TypeInfo resettable_interface_info = {
    291    .name       = TYPE_RESETTABLE_INTERFACE,
    292    .parent     = TYPE_INTERFACE,
    293    .class_size = sizeof(ResettableClass),
    294};
    295
    296static void reset_register_types(void)
    297{
    298    type_register_static(&resettable_interface_info);
    299}
    300
    301type_init(reset_register_types)