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 "sysemu/tcg.h" 18 #include "sysemu/qtest.h" 19 #include "migration/qemu-file-types.h" 20 #include "migration/register.h" 21 22 void s390_init_tod(void) 23 { 24 Object *obj; 25 26 if (kvm_enabled()) { 27 obj = object_new(TYPE_KVM_S390_TOD); 28 } else if (tcg_enabled()) { 29 obj = object_new(TYPE_QEMU_S390_TOD); 30 } else if (qtest_enabled()) { 31 return; 32 } else { 33 error_report("current accelerator not handled in s390_init_tod!"); 34 abort(); 35 } 36 object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj); 37 object_unref(obj); 38 39 qdev_realize(DEVICE(obj), NULL, &error_fatal); 40 } 41 42 S390TODState *s390_get_todstate(void) 43 { 44 static S390TODState *ts; 45 46 if (!ts) { 47 ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); 48 } 49 50 return ts; 51 } 52 53 #define S390_TOD_CLOCK_VALUE_MISSING 0x00 54 #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 55 56 static void s390_tod_save(QEMUFile *f, void *opaque) 57 { 58 S390TODState *td = opaque; 59 S390TODClass *tdc = S390_TOD_GET_CLASS(td); 60 Error *err = NULL; 61 S390TOD tod; 62 63 tdc->get(td, &tod, &err); 64 if (err) { 65 warn_report_err(err); 66 error_printf("Guest clock will not be migrated " 67 "which could cause the guest to hang."); 68 qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); 69 return; 70 } 71 72 qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); 73 qemu_put_byte(f, tod.high); 74 qemu_put_be64(f, tod.low); 75 } 76 77 static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) 78 { 79 S390TODState *td = opaque; 80 S390TODClass *tdc = S390_TOD_GET_CLASS(td); 81 Error *err = NULL; 82 S390TOD tod; 83 84 if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { 85 warn_report("Guest clock was not migrated. This could " 86 "cause the guest to hang."); 87 return 0; 88 } 89 90 tod.high = qemu_get_byte(f); 91 tod.low = qemu_get_be64(f); 92 93 tdc->set(td, &tod, &err); 94 if (err) { 95 error_report_err(err); 96 return -1; 97 } 98 return 0; 99 } 100 101 static SaveVMHandlers savevm_tod = { 102 .save_state = s390_tod_save, 103 .load_state = s390_tod_load, 104 }; 105 106 static void s390_tod_realize(DeviceState *dev, Error **errp) 107 { 108 S390TODState *td = S390_TOD(dev); 109 110 /* Legacy migration interface */ 111 register_savevm_live("todclock", 0, 1, &savevm_tod, td); 112 } 113 114 static void s390_tod_class_init(ObjectClass *oc, void *data) 115 { 116 DeviceClass *dc = DEVICE_CLASS(oc); 117 118 dc->desc = "TOD (Time Of Day) Clock"; 119 dc->realize = s390_tod_realize; 120 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 121 122 /* We only have one TOD clock in the system attached to the machine */ 123 dc->user_creatable = false; 124 } 125 126 static TypeInfo s390_tod_info = { 127 .name = TYPE_S390_TOD, 128 .parent = TYPE_DEVICE, 129 .instance_size = sizeof(S390TODState), 130 .class_init = s390_tod_class_init, 131 .class_size = sizeof(S390TODClass), 132 .abstract = true, 133 }; 134 135 static void register_types(void) 136 { 137 type_register_static(&s390_tod_info); 138 } 139 type_init(register_types); 140