qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

resettable.c (9658B)


      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  */
     23 static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
     24 static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
     25 static 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  */
     39 static bool enter_phase_in_progress;
     40 static unsigned exit_phase_in_progress;
     41 
     42 void 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 
     49 void 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 
     65 void 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 
     79 bool 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  */
     91 static 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  */
    104 static 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 
    114 static 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 
    161 static 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 
    190 static 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 == 0) {
    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     }
    210     s->exit_phase_in_progress = false;
    211     trace_resettable_phase_exit_end(obj, obj_typename, s->count);
    212 }
    213 
    214 /*
    215  * resettable_get_count:
    216  * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
    217  */
    218 static unsigned resettable_get_count(Object *obj)
    219 {
    220     if (obj) {
    221         ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
    222         return rc->get_state(obj)->count;
    223     }
    224     return 0;
    225 }
    226 
    227 void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
    228 {
    229     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
    230     ResettableState *s = rc->get_state(obj);
    231     unsigned newp_count = resettable_get_count(newp);
    232     unsigned oldp_count = resettable_get_count(oldp);
    233 
    234     /*
    235      * Ensure we do not change parent when in enter or exit phase.
    236      * During these phases, the reset subtree being updated is partly in reset
    237      * and partly not in reset (it depends on the actual position in
    238      * resettable_child_foreach()s). We are not able to tell in which part is a
    239      * leaving or arriving device. Thus we cannot set the reset count of the
    240      * moving device to the proper value.
    241      */
    242     assert(!enter_phase_in_progress && !exit_phase_in_progress);
    243     trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
    244 
    245     /*
    246      * At most one of the two 'for' loops will be executed below
    247      * in order to cope with the difference between the two counts.
    248      */
    249     /* if newp is more reset than oldp */
    250     for (unsigned i = oldp_count; i < newp_count; i++) {
    251         resettable_assert_reset(obj, RESET_TYPE_COLD);
    252     }
    253     /*
    254      * if obj is leaving a bus under reset, we need to ensure
    255      * hold phase is not pending.
    256      */
    257     if (oldp_count && s->hold_phase_pending) {
    258         resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
    259     }
    260     /* if oldp is more reset than newp */
    261     for (unsigned i = newp_count; i < oldp_count; i++) {
    262         resettable_release_reset(obj, RESET_TYPE_COLD);
    263     }
    264 }
    265 
    266 void resettable_cold_reset_fn(void *opaque)
    267 {
    268     resettable_reset((Object *) opaque, RESET_TYPE_COLD);
    269 }
    270 
    271 void resettable_class_set_parent_phases(ResettableClass *rc,
    272                                         ResettableEnterPhase enter,
    273                                         ResettableHoldPhase hold,
    274                                         ResettableExitPhase exit,
    275                                         ResettablePhases *parent_phases)
    276 {
    277     *parent_phases = rc->phases;
    278     if (enter) {
    279         rc->phases.enter = enter;
    280     }
    281     if (hold) {
    282         rc->phases.hold = hold;
    283     }
    284     if (exit) {
    285         rc->phases.exit = exit;
    286     }
    287 }
    288 
    289 static const TypeInfo resettable_interface_info = {
    290     .name       = TYPE_RESETTABLE_INTERFACE,
    291     .parent     = TYPE_INTERFACE,
    292     .class_size = sizeof(ResettableClass),
    293 };
    294 
    295 static void reset_register_types(void)
    296 {
    297     type_register_static(&resettable_interface_info);
    298 }
    299 
    300 type_init(reset_register_types)