1 /* 2 * TOD (Time Of Day) clock 3 * 4 * Copyright 2018 Red Hat, Inc. 5 * Author(s): David Hildenbrand <david@redhat.com> 6 * 7 * This work is licensed under the terms of the GNU GPL, version 2 or later. 8 * See the COPYING file in the top-level directory. 9 */ 10 11 #include "qemu/osdep.h" 12 #include "hw/s390x/tod.h" 13 #include "qapi/error.h" 14 #include "qemu/error-report.h" 15 #include "qemu/module.h" 16 #include "sysemu/kvm.h" 17 #include "migration/qemu-file-types.h" 18 #include "migration/register.h" 19 20 void s390_init_tod(void) 21 { 22 Object *obj; 23 24 if (kvm_enabled()) { 25 obj = object_new(TYPE_KVM_S390_TOD); 26 } else { 27 obj = object_new(TYPE_QEMU_S390_TOD); 28 } 29 object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj); 30 object_unref(obj); 31 32 qdev_realize(DEVICE(obj), NULL, &error_fatal); 33 } 34 35 S390TODState *s390_get_todstate(void) 36 { 37 static S390TODState *ts; 38 39 if (!ts) { 40 ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); 41 } 42 43 return ts; 44 } 45 46 #define S390_TOD_CLOCK_VALUE_MISSING 0x00 47 #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 48 49 static void s390_tod_save(QEMUFile *f, void *opaque) 50 { 51 S390TODState *td = opaque; 52 S390TODClass *tdc = S390_TOD_GET_CLASS(td); 53 Error *err = NULL; 54 S390TOD tod; 55 56 tdc->get(td, &tod, &err); 57 if (err) { 58 warn_report_err(err); 59 error_printf("Guest clock will not be migrated " 60 "which could cause the guest to hang."); 61 qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); 62 return; 63 } 64 65 qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); 66 qemu_put_byte(f, tod.high); 67 qemu_put_be64(f, tod.low); 68 } 69 70 static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) 71 { 72 S390TODState *td = opaque; 73 S390TODClass *tdc = S390_TOD_GET_CLASS(td); 74 Error *err = NULL; 75 S390TOD tod; 76 77 if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { 78 warn_report("Guest clock was not migrated. This could " 79 "cause the guest to hang."); 80 return 0; 81 } 82 83 tod.high = qemu_get_byte(f); 84 tod.low = qemu_get_be64(f); 85 86 tdc->set(td, &tod, &err); 87 if (err) { 88 error_report_err(err); 89 return -1; 90 } 91 return 0; 92 } 93 94 static SaveVMHandlers savevm_tod = { 95 .save_state = s390_tod_save, 96 .load_state = s390_tod_load, 97 }; 98 99 static void s390_tod_realize(DeviceState *dev, Error **errp) 100 { 101 S390TODState *td = S390_TOD(dev); 102 103 /* Legacy migration interface */ 104 register_savevm_live("todclock", 0, 1, &savevm_tod, td); 105 } 106 107 static void s390_tod_class_init(ObjectClass *oc, void *data) 108 { 109 DeviceClass *dc = DEVICE_CLASS(oc); 110 111 dc->desc = "TOD (Time Of Day) Clock"; 112 dc->realize = s390_tod_realize; 113 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 114 115 /* We only have one TOD clock in the system attached to the machine */ 116 dc->user_creatable = false; 117 } 118 119 static TypeInfo s390_tod_info = { 120 .name = TYPE_S390_TOD, 121 .parent = TYPE_DEVICE, 122 .instance_size = sizeof(S390TODState), 123 .class_init = s390_tod_class_init, 124 .class_size = sizeof(S390TODClass), 125 .abstract = true, 126 }; 127 128 static void register_types(void) 129 { 130 type_register_static(&s390_tod_info); 131 } 132 type_init(register_types); 133