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