qemu/hw/core/resettable.c
<<
>>
Prefs
   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)
 302