/* * Reset handlers. * * Copyright (c) 2003-2008 Fabrice Bellard * Copyright (c) 2016 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "sysemu/reset.h" #include "hw/resettable.h" #include "hw/core/resetcontainer.h" /* * Return a pointer to the singleton container that holds all the Resettable * items that will be reset when qemu_devices_reset() is called. */ static ResettableContainer *get_root_reset_container(void) { static ResettableContainer *root_reset_container; if (!root_reset_container) { root_reset_container = RESETTABLE_CONTAINER(object_new(TYPE_RESETTABLE_CONTAINER)); } return root_reset_container; } /* * Reason why the currently in-progress qemu_devices_reset() was called. * If we made at least SHUTDOWN_CAUSE_SNAPSHOT_LOAD have a corresponding * ResetType we could perhaps avoid the need for this global. */ static ShutdownCause device_reset_reason; /* * This is an Object which implements Resettable simply to call the * callback function in the hold phase. */ #define TYPE_LEGACY_RESET "legacy-reset" OBJECT_DECLARE_SIMPLE_TYPE(LegacyReset, LEGACY_RESET) struct LegacyReset { Object parent; ResettableState reset_state; QEMUResetHandler *func; void *opaque; bool skip_on_snapshot_load; }; OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(LegacyReset, legacy_reset, LEGACY_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { }) static ResettableState *legacy_reset_get_state(Object *obj) { LegacyReset *lr = LEGACY_RESET(obj); return &lr->reset_state; } static void legacy_reset_hold(Object *obj) { LegacyReset *lr = LEGACY_RESET(obj); if (device_reset_reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD && lr->skip_on_snapshot_load) { return; } lr->func(lr->opaque); } static void legacy_reset_init(Object *obj) { } static void legacy_reset_finalize(Object *obj) { } static void legacy_reset_class_init(ObjectClass *klass, void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); rc->get_state = legacy_reset_get_state; rc->phases.hold = legacy_reset_hold; } void qemu_register_reset(QEMUResetHandler *func, void *opaque) { Object *obj = object_new(TYPE_LEGACY_RESET); LegacyReset *lr = LEGACY_RESET(obj); lr->func = func; lr->opaque = opaque; qemu_register_resettable(obj); } void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque) { Object *obj = object_new(TYPE_LEGACY_RESET); LegacyReset *lr = LEGACY_RESET(obj); lr->func = func; lr->opaque = opaque; lr->skip_on_snapshot_load = true; qemu_register_resettable(obj); } typedef struct FindLegacyInfo { QEMUResetHandler *func; void *opaque; LegacyReset *lr; } FindLegacyInfo; static void find_legacy_reset_cb(Object *obj, void *opaque, ResetType type) { LegacyReset *lr; FindLegacyInfo *fli = opaque; /* Not everything in the ResettableContainer will be a LegacyReset */ lr = LEGACY_RESET(object_dynamic_cast(obj, TYPE_LEGACY_RESET)); if (lr && lr->func == fli->func && lr->opaque == fli->opaque) { fli->lr = lr; } } static LegacyReset *find_legacy_reset(QEMUResetHandler *func, void *opaque) { /* * Find the LegacyReset with the specified func and opaque, * by getting the ResettableContainer to call our callback for * every item in it. */ ResettableContainer *rootcon = get_root_reset_container(); ResettableClass *rc = RESETTABLE_GET_CLASS(rootcon); FindLegacyInfo fli; fli.func = func; fli.opaque = opaque; fli.lr = NULL; rc->child_foreach(OBJECT(rootcon), find_legacy_reset_cb, &fli, RESET_TYPE_COLD); return fli.lr; } void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) { Object *obj = OBJECT(find_legacy_reset(func, opaque)); if (obj) { qemu_unregister_resettable(obj); object_unref(obj); } } void qemu_register_resettable(Object *obj) { resettable_container_add(get_root_reset_container(), obj); } void qemu_unregister_resettable(Object *obj) { resettable_container_remove(get_root_reset_container(), obj); } void qemu_devices_reset(ShutdownCause reason) { device_reset_reason = reason; /* Reset the simulation */ resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD); }