From 70804c83f2914acaca74c1789a6b869bd5d1ea67 Mon Sep 17 00:00:00 2001 From: Damien Hedde Date: Thu, 30 Jan 2020 16:02:03 +0000 Subject: hw/core/qdev: add trace events to help with resettable transition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds trace events to reset procedure and when updating the parent bus of a device. Signed-off-by: Damien Hedde Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cornelia Huck Tested-by: Philippe Mathieu-Daudé Message-id: 20200123132823.1117486-3-damien.hedde@greensocs.com Signed-off-by: Peter Maydell --- hw/core/trace-events | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'hw/core/trace-events') diff --git a/hw/core/trace-events b/hw/core/trace-events index fe47a9c8cb..a375aa88a4 100644 --- a/hw/core/trace-events +++ b/hw/core/trace-events @@ -1,2 +1,11 @@ # loader.c loader_write_rom(const char *name, uint64_t gpa, uint64_t size, bool isrom) "%s: @0x%"PRIx64" size=0x%"PRIx64" ROM=%d" + +# qdev.c +qdev_reset(void *obj, const char *objtype) "obj=%p(%s)" +qdev_reset_all(void *obj, const char *objtype) "obj=%p(%s)" +qdev_reset_tree(void *obj, const char *objtype) "obj=%p(%s)" +qbus_reset(void *obj, const char *objtype) "obj=%p(%s)" +qbus_reset_all(void *obj, const char *objtype) "obj=%p(%s)" +qbus_reset_tree(void *obj, const char *objtype) "obj=%p(%s)" +qdev_update_parent_bus(void *obj, const char *objtype, void *oldp, const char *oldptype, void *newp, const char *newptype) "obj=%p(%s) old_parent=%p(%s) new_parent=%p(%s)" -- cgit 1.4.1 From bc5a39bf2688130bae86351a6c6b005cf9566a3c Mon Sep 17 00:00:00 2001 From: Damien Hedde Date: Thu, 30 Jan 2020 16:02:03 +0000 Subject: hw/core: create Resettable QOM interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit defines an interface allowing multi-phase reset. This aims to solve a problem of the actual single-phase reset (built in DeviceClass and BusClass): reset behavior is dependent on the order in which reset handlers are called. In particular doing external side-effect (like setting an qemu_irq) is problematic because receiving object may not be reset yet. The Resettable interface divides the reset in 3 well defined phases. To reset an object tree, all 1st phases are executed then all 2nd then all 3rd. See the comments in include/hw/resettable.h for a more complete description. The interface defines 3 phases to let the future possibility of holding an object into reset for some time. The qdev/qbus reset in DeviceClass and BusClass will be modified in following commits to use this interface. A mechanism is provided to allow executing a transitional reset handler in place of the 2nd phase which is executed in children-then-parent order inside a tree. This will allow to transition devices and buses smoothly while keeping the exact current qdev/qbus reset behavior for now. Documentation will be added in a following commit. Signed-off-by: Damien Hedde Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20200123132823.1117486-4-damien.hedde@greensocs.com Signed-off-by: Peter Maydell --- hw/core/Makefile.objs | 1 + hw/core/resettable.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/core/trace-events | 17 ++++ include/hw/resettable.h | 211 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 467 insertions(+) create mode 100644 hw/core/resettable.c create mode 100644 include/hw/resettable.h (limited to 'hw/core/trace-events') diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index a522b7297d..9e41ec9a15 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -2,6 +2,7 @@ common-obj-y += qdev.o qdev-properties.o common-obj-y += bus.o common-obj-y += cpu.o +common-obj-y += resettable.o common-obj-y += hotplug.o common-obj-y += vmstate-if.o # irq.o needed for qdev GPIO handling: diff --git a/hw/core/resettable.c b/hw/core/resettable.c new file mode 100644 index 0000000000..9133208487 --- /dev/null +++ b/hw/core/resettable.c @@ -0,0 +1,238 @@ +/* + * Resettable interface. + * + * Copyright (c) 2019 GreenSocs SAS + * + * Authors: + * Damien Hedde + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "hw/resettable.h" +#include "trace.h" + +/** + * resettable_phase_enter/hold/exit: + * Function executing a phase recursively in a resettable object and its + * children. + */ +static void resettable_phase_enter(Object *obj, void *opaque, ResetType type); +static void resettable_phase_hold(Object *obj, void *opaque, ResetType type); +static void resettable_phase_exit(Object *obj, void *opaque, ResetType type); + +/** + * enter_phase_in_progress: + * True if we are currently in reset enter phase. + * + * Note: This flag is only used to guarantee (using asserts) that the reset + * API is used correctly. We can use a global variable because we rely on the + * iothread mutex to ensure only one reset operation is in a progress at a + * given time. + */ +static bool enter_phase_in_progress; + +void resettable_reset(Object *obj, ResetType type) +{ + trace_resettable_reset(obj, type); + resettable_assert_reset(obj, type); + resettable_release_reset(obj, type); +} + +void resettable_assert_reset(Object *obj, ResetType type) +{ + /* TODO: change this assert when adding support for other reset types */ + assert(type == RESET_TYPE_COLD); + trace_resettable_reset_assert_begin(obj, type); + assert(!enter_phase_in_progress); + + enter_phase_in_progress = true; + resettable_phase_enter(obj, NULL, type); + enter_phase_in_progress = false; + + resettable_phase_hold(obj, NULL, type); + + trace_resettable_reset_assert_end(obj); +} + +void resettable_release_reset(Object *obj, ResetType type) +{ + /* TODO: change this assert when adding support for other reset types */ + assert(type == RESET_TYPE_COLD); + trace_resettable_reset_release_begin(obj, type); + assert(!enter_phase_in_progress); + + resettable_phase_exit(obj, NULL, type); + + trace_resettable_reset_release_end(obj); +} + +bool resettable_is_in_reset(Object *obj) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + + return s->count > 0; +} + +/** + * resettable_child_foreach: + * helper to avoid checking the existence of the method. + */ +static void resettable_child_foreach(ResettableClass *rc, Object *obj, + ResettableChildCallback cb, + void *opaque, ResetType type) +{ + if (rc->child_foreach) { + rc->child_foreach(obj, cb, opaque, type); + } +} + +/** + * resettable_get_tr_func: + * helper to fetch transitional reset callback if any. + */ +static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc, + Object *obj) +{ + ResettableTrFunction tr_func = NULL; + if (rc->get_transitional_function) { + tr_func = rc->get_transitional_function(obj); + } + return tr_func; +} + +static void resettable_phase_enter(Object *obj, void *opaque, ResetType type) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + const char *obj_typename = object_get_typename(obj); + bool action_needed = false; + + /* exit phase has to finish properly before entering back in reset */ + assert(!s->exit_phase_in_progress); + + trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type); + + /* Only take action if we really enter reset for the 1st time. */ + /* + * TODO: if adding more ResetType support, some additional checks + * are probably needed here. + */ + if (s->count++ == 0) { + action_needed = true; + } + /* + * We limit the count to an arbitrary "big" value. The value is big + * enough not to be triggered normally. + * The assert will stop an infinite loop if there is a cycle in the + * reset tree. The loop goes through resettable_foreach_child below + * which at some point will call us again. + */ + assert(s->count <= 50); + + /* + * handle the children even if action_needed is at false so that + * child counts are incremented too + */ + resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type); + + /* execute enter phase for the object if needed */ + if (action_needed) { + trace_resettable_phase_enter_exec(obj, obj_typename, type, + !!rc->phases.enter); + if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) { + rc->phases.enter(obj, type); + } + s->hold_phase_pending = true; + } + trace_resettable_phase_enter_end(obj, obj_typename, s->count); +} + +static void resettable_phase_hold(Object *obj, void *opaque, ResetType type) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + const char *obj_typename = object_get_typename(obj); + + /* exit phase has to finish properly before entering back in reset */ + assert(!s->exit_phase_in_progress); + + trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type); + + /* handle children first */ + resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type); + + /* exec hold phase */ + if (s->hold_phase_pending) { + s->hold_phase_pending = false; + ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj); + trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold); + if (tr_func) { + trace_resettable_transitional_function(obj, obj_typename); + tr_func(obj); + } else if (rc->phases.hold) { + rc->phases.hold(obj); + } + } + trace_resettable_phase_hold_end(obj, obj_typename, s->count); +} + +static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + const char *obj_typename = object_get_typename(obj); + + assert(!s->exit_phase_in_progress); + trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type); + + /* exit_phase_in_progress ensures this phase is 'atomic' */ + s->exit_phase_in_progress = true; + resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type); + + assert(s->count > 0); + if (s->count == 1) { + trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit); + if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) { + rc->phases.exit(obj); + } + s->count = 0; + } + s->exit_phase_in_progress = false; + trace_resettable_phase_exit_end(obj, obj_typename, s->count); +} + +void resettable_class_set_parent_phases(ResettableClass *rc, + ResettableEnterPhase enter, + ResettableHoldPhase hold, + ResettableExitPhase exit, + ResettablePhases *parent_phases) +{ + *parent_phases = rc->phases; + if (enter) { + rc->phases.enter = enter; + } + if (hold) { + rc->phases.hold = hold; + } + if (exit) { + rc->phases.exit = exit; + } +} + +static const TypeInfo resettable_interface_info = { + .name = TYPE_RESETTABLE_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(ResettableClass), +}; + +static void reset_register_types(void) +{ + type_register_static(&resettable_interface_info); +} + +type_init(reset_register_types) diff --git a/hw/core/trace-events b/hw/core/trace-events index a375aa88a4..77d61cb66e 100644 --- a/hw/core/trace-events +++ b/hw/core/trace-events @@ -9,3 +9,20 @@ qbus_reset(void *obj, const char *objtype) "obj=%p(%s)" qbus_reset_all(void *obj, const char *objtype) "obj=%p(%s)" qbus_reset_tree(void *obj, const char *objtype) "obj=%p(%s)" qdev_update_parent_bus(void *obj, const char *objtype, void *oldp, const char *oldptype, void *newp, const char *newptype) "obj=%p(%s) old_parent=%p(%s) new_parent=%p(%s)" + +# resettable.c +resettable_reset(void *obj, int cold) "obj=%p cold=%d" +resettable_reset_assert_begin(void *obj, int cold) "obj=%p cold=%d" +resettable_reset_assert_end(void *obj) "obj=%p" +resettable_reset_release_begin(void *obj, int cold) "obj=%p cold=%d" +resettable_reset_release_end(void *obj) "obj=%p" +resettable_phase_enter_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d" +resettable_phase_enter_exec(void *obj, const char *objtype, int type, int has_method) "obj=%p(%s) type=%d method=%d" +resettable_phase_enter_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d" +resettable_phase_hold_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d" +resettable_phase_hold_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d" +resettable_phase_hold_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d" +resettable_phase_exit_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d" +resettable_phase_exit_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d" +resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d" +resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)" diff --git a/include/hw/resettable.h b/include/hw/resettable.h new file mode 100644 index 0000000000..c0b9fc6ad6 --- /dev/null +++ b/include/hw/resettable.h @@ -0,0 +1,211 @@ +/* + * Resettable interface header. + * + * Copyright (c) 2019 GreenSocs SAS + * + * Authors: + * Damien Hedde + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_RESETTABLE_H +#define HW_RESETTABLE_H + +#include "qom/object.h" + +#define TYPE_RESETTABLE_INTERFACE "resettable" + +#define RESETTABLE_CLASS(class) \ + OBJECT_CLASS_CHECK(ResettableClass, (class), TYPE_RESETTABLE_INTERFACE) + +#define RESETTABLE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ResettableClass, (obj), TYPE_RESETTABLE_INTERFACE) + +typedef struct ResettableState ResettableState; + +/** + * ResetType: + * Types of reset. + * + * + Cold: reset resulting from a power cycle of the object. + * + * TODO: Support has to be added to handle more types. In particular, + * ResettableState structure needs to be expanded. + */ +typedef enum ResetType { + RESET_TYPE_COLD, +} ResetType; + +/* + * ResettableClass: + * Interface for resettable objects. + * + * See docs/devel/reset.rst for more detailed information about how QEMU models + * reset. This whole API must only be used when holding the iothread mutex. + * + * All objects which can be reset must implement this interface; + * it is usually provided by a base class such as DeviceClass or BusClass. + * Every Resettable object must maintain some state tracking the + * progress of a reset operation by providing a ResettableState structure. + * The functions defined in this module take care of updating the + * state of the reset. + * The base class implementation of the interface provides this + * state and implements the associated method: get_state. + * + * Concrete object implementations (typically specific devices + * such as a UART model) should provide the functions + * for the phases.enter, phases.hold and phases.exit methods, which + * they can set in their class init function, either directly or + * by calling resettable_class_set_parent_phases(). + * The phase methods are guaranteed to only only ever be called once + * for any reset event, in the order 'enter', 'hold', 'exit'. + * An object will always move quickly from 'enter' to 'hold' + * but might remain in 'hold' for an arbitrary period of time + * before eventually reset is deasserted and the 'exit' phase is called. + * Object implementations should be prepared for functions handling + * inbound connections from other devices (such as qemu_irq handler + * functions) to be called at any point during reset after their + * 'enter' method has been called. + * + * Users of a resettable object should not call these methods + * directly, but instead use the function resettable_reset(). + * + * @phases.enter: This phase is called when the object enters reset. It + * should reset local state of the object, but it must not do anything that + * has a side-effect on other objects, such as raising or lowering a qemu_irq + * line or reading or writing guest memory. It takes the reset's type as + * argument. + * + * @phases.hold: This phase is called for entry into reset, once every object + * in the system which is being reset has had its @phases.enter method called. + * At this point devices can do actions that affect other objects. + * + * @phases.exit: This phase is called when the object leaves the reset state. + * Actions affecting other objects are permitted. + * + * @get_state: Mandatory method which must return a pointer to a + * ResettableState. + * + * @get_transitional_function: transitional method to handle Resettable objects + * not yet fully moved to this interface. It will be removed as soon as it is + * not needed anymore. This method is optional and may return a pointer to a + * function to be used instead of the phases. If the method exists and returns + * a non-NULL function pointer then that function is executed as a replacement + * of the 'hold' phase method taking the object as argument. The two other phase + * methods are not executed. + * + * @child_foreach: Executes a given callback on every Resettable child. Child + * in this context means a child in the qbus tree, so the children of a qbus + * are the devices on it, and the children of a device are all the buses it + * owns. This is not the same as the QOM object hierarchy. The function takes + * additional opaque and ResetType arguments which must be passed unmodified to + * the callback. + */ +typedef void (*ResettableEnterPhase)(Object *obj, ResetType type); +typedef void (*ResettableHoldPhase)(Object *obj); +typedef void (*ResettableExitPhase)(Object *obj); +typedef ResettableState * (*ResettableGetState)(Object *obj); +typedef void (*ResettableTrFunction)(Object *obj); +typedef ResettableTrFunction (*ResettableGetTrFunction)(Object *obj); +typedef void (*ResettableChildCallback)(Object *, void *opaque, + ResetType type); +typedef void (*ResettableChildForeach)(Object *obj, + ResettableChildCallback cb, + void *opaque, ResetType type); +typedef struct ResettablePhases { + ResettableEnterPhase enter; + ResettableHoldPhase hold; + ResettableExitPhase exit; +} ResettablePhases; +typedef struct ResettableClass { + InterfaceClass parent_class; + + /* Phase methods */ + ResettablePhases phases; + + /* State access method */ + ResettableGetState get_state; + + /* Transitional method for legacy reset compatibility */ + ResettableGetTrFunction get_transitional_function; + + /* Hierarchy handling method */ + ResettableChildForeach child_foreach; +} ResettableClass; + +/** + * ResettableState: + * Structure holding reset related state. The fields should not be accessed + * directly; the definition is here to allow further inclusion into other + * objects. + * + * @count: Number of reset level the object is into. It is incremented when + * the reset operation starts and decremented when it finishes. + * @hold_phase_pending: flag which indicates that we need to invoke the 'hold' + * phase handler for this object. + * @exit_phase_in_progress: true if we are currently in the exit phase + */ +struct ResettableState { + unsigned count; + bool hold_phase_pending; + bool exit_phase_in_progress; +}; + +/** + * resettable_reset: + * Trigger a reset on an object @obj of type @type. @obj must implement + * Resettable interface. + * + * Calling this function is equivalent to calling @resettable_assert_reset() + * then @resettable_release_reset(). + */ +void resettable_reset(Object *obj, ResetType type); + +/** + * resettable_assert_reset: + * Put an object @obj into reset. @obj must implement Resettable interface. + * + * @resettable_release_reset() must eventually be called after this call. + * There must be one call to @resettable_release_reset() per call of + * @resettable_assert_reset(), with the same type argument. + * + * NOTE: Until support for migration is added, the @resettable_release_reset() + * must not be delayed. It must occur just after @resettable_assert_reset() so + * that migration cannot be triggered in between. Prefer using + * @resettable_reset() for now. + */ +void resettable_assert_reset(Object *obj, ResetType type); + +/** + * resettable_release_reset: + * Release the object @obj from reset. @obj must implement Resettable interface. + * + * See @resettable_assert_reset() description for details. + */ +void resettable_release_reset(Object *obj, ResetType type); + +/** + * resettable_is_in_reset: + * Return true if @obj is under reset. + * + * @obj must implement Resettable interface. + */ +bool resettable_is_in_reset(Object *obj); + +/** + * resettable_class_set_parent_phases: + * + * Save @rc current reset phases into @parent_phases and override @rc phases + * by the given new methods (@enter, @hold and @exit). + * Each phase is overridden only if the new one is not NULL allowing to + * override a subset of phases. + */ +void resettable_class_set_parent_phases(ResettableClass *rc, + ResettableEnterPhase enter, + ResettableHoldPhase hold, + ResettableExitPhase exit, + ResettablePhases *parent_phases); + +#endif -- cgit 1.4.1 From 614f731adb9cc86bd45ce749f597e68dbde253b1 Mon Sep 17 00:00:00 2001 From: Damien Hedde Date: Thu, 30 Jan 2020 16:02:04 +0000 Subject: hw/core/resettable: add support for changing parent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function resettable_change_parent() to do the required plumbing when changing the parent a of Resettable object. We need to make sure that the reset state of the object remains coherent with the reset state of the new parent. We make the 2 following hypothesis: + when an object is put in a parent under reset, the object goes in reset. + when an object is removed from a parent under reset, the object leaves reset. The added function avoids any glitch if both old and new parent are already in reset. Signed-off-by: Damien Hedde Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20200123132823.1117486-6-damien.hedde@greensocs.com Signed-off-by: Peter Maydell --- hw/core/resettable.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- hw/core/trace-events | 1 + include/hw/resettable.h | 16 +++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) (limited to 'hw/core/trace-events') diff --git a/hw/core/resettable.c b/hw/core/resettable.c index 9133208487..6e0b0f492f 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -28,12 +28,16 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type); * enter_phase_in_progress: * True if we are currently in reset enter phase. * - * Note: This flag is only used to guarantee (using asserts) that the reset - * API is used correctly. We can use a global variable because we rely on the + * exit_phase_in_progress: + * count the number of exit phase we are in. + * + * Note: These flags are only used to guarantee (using asserts) that the reset + * API is used correctly. We can use global variables because we rely on the * iothread mutex to ensure only one reset operation is in a progress at a * given time. */ static bool enter_phase_in_progress; +static unsigned exit_phase_in_progress; void resettable_reset(Object *obj, ResetType type) { @@ -65,7 +69,9 @@ void resettable_release_reset(Object *obj, ResetType type) trace_resettable_reset_release_begin(obj, type); assert(!enter_phase_in_progress); + exit_phase_in_progress += 1; resettable_phase_exit(obj, NULL, type); + exit_phase_in_progress -= 1; trace_resettable_reset_release_end(obj); } @@ -206,6 +212,58 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) trace_resettable_phase_exit_end(obj, obj_typename, s->count); } +/* + * resettable_get_count: + * Get the count of the Resettable object @obj. Return 0 if @obj is NULL. + */ +static unsigned resettable_get_count(Object *obj) +{ + if (obj) { + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + return rc->get_state(obj)->count; + } + return 0; +} + +void resettable_change_parent(Object *obj, Object *newp, Object *oldp) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + unsigned newp_count = resettable_get_count(newp); + unsigned oldp_count = resettable_get_count(oldp); + + /* + * Ensure we do not change parent when in enter or exit phase. + * During these phases, the reset subtree being updated is partly in reset + * and partly not in reset (it depends on the actual position in + * resettable_child_foreach()s). We are not able to tell in which part is a + * leaving or arriving device. Thus we cannot set the reset count of the + * moving device to the proper value. + */ + assert(!enter_phase_in_progress && !exit_phase_in_progress); + trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count); + + /* + * At most one of the two 'for' loops will be executed below + * in order to cope with the difference between the two counts. + */ + /* if newp is more reset than oldp */ + for (unsigned i = oldp_count; i < newp_count; i++) { + resettable_assert_reset(obj, RESET_TYPE_COLD); + } + /* + * if obj is leaving a bus under reset, we need to ensure + * hold phase is not pending. + */ + if (oldp_count && s->hold_phase_pending) { + resettable_phase_hold(obj, NULL, RESET_TYPE_COLD); + } + /* if oldp is more reset than newp */ + for (unsigned i = newp_count; i < oldp_count; i++) { + resettable_release_reset(obj, RESET_TYPE_COLD); + } +} + void resettable_class_set_parent_phases(ResettableClass *rc, ResettableEnterPhase enter, ResettableHoldPhase hold, diff --git a/hw/core/trace-events b/hw/core/trace-events index 77d61cb66e..aecd8e160e 100644 --- a/hw/core/trace-events +++ b/hw/core/trace-events @@ -16,6 +16,7 @@ resettable_reset_assert_begin(void *obj, int cold) "obj=%p cold=%d" resettable_reset_assert_end(void *obj) "obj=%p" resettable_reset_release_begin(void *obj, int cold) "obj=%p cold=%d" resettable_reset_release_end(void *obj) "obj=%p" +resettable_change_parent(void *obj, void *o, unsigned oc, void *n, unsigned nc) "obj=%p from=%p(%d) to=%p(%d)" resettable_phase_enter_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d" resettable_phase_enter_exec(void *obj, const char *objtype, int type, int has_method) "obj=%p(%s) type=%d method=%d" resettable_phase_enter_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d" diff --git a/include/hw/resettable.h b/include/hw/resettable.h index c0b9fc6ad6..96073354fd 100644 --- a/include/hw/resettable.h +++ b/include/hw/resettable.h @@ -194,6 +194,22 @@ void resettable_release_reset(Object *obj, ResetType type); */ bool resettable_is_in_reset(Object *obj); +/** + * resettable_change_parent: + * Indicate that the parent of Ressettable @obj is changing from @oldp to @newp. + * All 3 objects must implement resettable interface. @oldp or @newp may be + * NULL. + * + * This function will adapt the reset state of @obj so that it is coherent + * with the reset state of @newp. It may trigger @resettable_assert_reset() + * or @resettable_release_reset(). It will do such things only if the reset + * state of @newp and @oldp are different. + * + * When using this function during reset, it must only be called during + * a hold phase method. Calling this during enter or exit phase is an error. + */ +void resettable_change_parent(Object *obj, Object *newp, Object *oldp); + /** * resettable_class_set_parent_phases: * -- cgit 1.4.1