| /* |
| * TOD (Time Of Day) clock |
| * |
| * Copyright 2018 Red Hat, Inc. |
| * Author(s): David Hildenbrand <david@redhat.com> |
| * |
| * 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 "hw/s390x/tod.h" |
| #include "qapi/error.h" |
| #include "qemu/error-report.h" |
| #include "qemu/module.h" |
| #include "sysemu/kvm.h" |
| #include "migration/qemu-file-types.h" |
| #include "migration/register.h" |
| |
| void s390_init_tod(void) |
| { |
| Object *obj; |
| |
| if (kvm_enabled()) { |
| obj = object_new(TYPE_KVM_S390_TOD); |
| } else { |
| obj = object_new(TYPE_QEMU_S390_TOD); |
| } |
| object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj); |
| object_unref(obj); |
| |
| qdev_realize(DEVICE(obj), NULL, &error_fatal); |
| } |
| |
| S390TODState *s390_get_todstate(void) |
| { |
| static S390TODState *ts; |
| |
| if (!ts) { |
| ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); |
| } |
| |
| return ts; |
| } |
| |
| #define S390_TOD_CLOCK_VALUE_MISSING 0x00 |
| #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 |
| |
| static void s390_tod_save(QEMUFile *f, void *opaque) |
| { |
| S390TODState *td = opaque; |
| S390TODClass *tdc = S390_TOD_GET_CLASS(td); |
| Error *err = NULL; |
| S390TOD tod; |
| |
| tdc->get(td, &tod, &err); |
| if (err) { |
| warn_report_err(err); |
| error_printf("Guest clock will not be migrated " |
| "which could cause the guest to hang."); |
| qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); |
| return; |
| } |
| |
| qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); |
| qemu_put_byte(f, tod.high); |
| qemu_put_be64(f, tod.low); |
| } |
| |
| static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) |
| { |
| S390TODState *td = opaque; |
| S390TODClass *tdc = S390_TOD_GET_CLASS(td); |
| Error *err = NULL; |
| S390TOD tod; |
| |
| if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { |
| warn_report("Guest clock was not migrated. This could " |
| "cause the guest to hang."); |
| return 0; |
| } |
| |
| tod.high = qemu_get_byte(f); |
| tod.low = qemu_get_be64(f); |
| |
| tdc->set(td, &tod, &err); |
| if (err) { |
| error_report_err(err); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static SaveVMHandlers savevm_tod = { |
| .save_state = s390_tod_save, |
| .load_state = s390_tod_load, |
| }; |
| |
| static void s390_tod_realize(DeviceState *dev, Error **errp) |
| { |
| S390TODState *td = S390_TOD(dev); |
| |
| /* Legacy migration interface */ |
| register_savevm_live("todclock", 0, 1, &savevm_tod, td); |
| } |
| |
| static void s390_tod_class_init(ObjectClass *oc, void *data) |
| { |
| DeviceClass *dc = DEVICE_CLASS(oc); |
| |
| dc->desc = "TOD (Time Of Day) Clock"; |
| dc->realize = s390_tod_realize; |
| set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
| |
| /* We only have one TOD clock in the system attached to the machine */ |
| dc->user_creatable = false; |
| } |
| |
| static TypeInfo s390_tod_info = { |
| .name = TYPE_S390_TOD, |
| .parent = TYPE_DEVICE, |
| .instance_size = sizeof(S390TODState), |
| .class_init = s390_tod_class_init, |
| .class_size = sizeof(S390TODClass), |
| .abstract = true, |
| }; |
| |
| static void register_types(void) |
| { |
| type_register_static(&s390_tod_info); |
| } |
| type_init(register_types); |