18046f374SDavid Hildenbrand /* 28046f374SDavid Hildenbrand * TOD (Time Of Day) clock 38046f374SDavid Hildenbrand * 48046f374SDavid Hildenbrand * Copyright 2018 Red Hat, Inc. 58046f374SDavid Hildenbrand * Author(s): David Hildenbrand <david@redhat.com> 68046f374SDavid Hildenbrand * 78046f374SDavid Hildenbrand * This work is licensed under the terms of the GNU GPL, version 2 or later. 88046f374SDavid Hildenbrand * See the COPYING file in the top-level directory. 98046f374SDavid Hildenbrand */ 108046f374SDavid Hildenbrand 118046f374SDavid Hildenbrand #include "qemu/osdep.h" 128046f374SDavid Hildenbrand #include "hw/s390x/tod.h" 138046f374SDavid Hildenbrand #include "qapi/error.h" 148046f374SDavid Hildenbrand #include "qemu/error-report.h" 150b8fa32fSMarkus Armbruster #include "qemu/module.h" 168046f374SDavid Hildenbrand #include "sysemu/kvm.h" 17ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h" 188046f374SDavid Hildenbrand #include "migration/register.h" 198046f374SDavid Hildenbrand 208046f374SDavid Hildenbrand void s390_init_tod(void) 218046f374SDavid Hildenbrand { 228046f374SDavid Hildenbrand Object *obj; 238046f374SDavid Hildenbrand 248046f374SDavid Hildenbrand if (kvm_enabled()) { 258046f374SDavid Hildenbrand obj = object_new(TYPE_KVM_S390_TOD); 268046f374SDavid Hildenbrand } else { 278046f374SDavid Hildenbrand obj = object_new(TYPE_QEMU_S390_TOD); 288046f374SDavid Hildenbrand } 298046f374SDavid Hildenbrand object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj, NULL); 308046f374SDavid Hildenbrand object_unref(obj); 318046f374SDavid Hildenbrand 328046f374SDavid Hildenbrand qdev_init_nofail(DEVICE(obj)); 338046f374SDavid Hildenbrand } 348046f374SDavid Hildenbrand 357de3b1cdSDavid Hildenbrand S390TODState *s390_get_todstate(void) 367de3b1cdSDavid Hildenbrand { 377de3b1cdSDavid Hildenbrand static S390TODState *ts; 387de3b1cdSDavid Hildenbrand 397de3b1cdSDavid Hildenbrand if (!ts) { 407de3b1cdSDavid Hildenbrand ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); 417de3b1cdSDavid Hildenbrand } 427de3b1cdSDavid Hildenbrand 437de3b1cdSDavid Hildenbrand return ts; 447de3b1cdSDavid Hildenbrand } 457de3b1cdSDavid Hildenbrand 468046f374SDavid Hildenbrand #define S390_TOD_CLOCK_VALUE_MISSING 0x00 478046f374SDavid Hildenbrand #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 488046f374SDavid Hildenbrand 498046f374SDavid Hildenbrand static void s390_tod_save(QEMUFile *f, void *opaque) 508046f374SDavid Hildenbrand { 518046f374SDavid Hildenbrand S390TODState *td = opaque; 528046f374SDavid Hildenbrand S390TODClass *tdc = S390_TOD_GET_CLASS(td); 538046f374SDavid Hildenbrand Error *err = NULL; 548046f374SDavid Hildenbrand S390TOD tod; 558046f374SDavid Hildenbrand 568046f374SDavid Hildenbrand tdc->get(td, &tod, &err); 578046f374SDavid Hildenbrand if (err) { 588046f374SDavid Hildenbrand warn_report_err(err); 598046f374SDavid Hildenbrand error_printf("Guest clock will not be migrated " 608046f374SDavid Hildenbrand "which could cause the guest to hang."); 618046f374SDavid Hildenbrand qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); 628046f374SDavid Hildenbrand return; 638046f374SDavid Hildenbrand } 648046f374SDavid Hildenbrand 658046f374SDavid Hildenbrand qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); 668046f374SDavid Hildenbrand qemu_put_byte(f, tod.high); 678046f374SDavid Hildenbrand qemu_put_be64(f, tod.low); 688046f374SDavid Hildenbrand } 698046f374SDavid Hildenbrand 708046f374SDavid Hildenbrand static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) 718046f374SDavid Hildenbrand { 728046f374SDavid Hildenbrand S390TODState *td = opaque; 738046f374SDavid Hildenbrand S390TODClass *tdc = S390_TOD_GET_CLASS(td); 748046f374SDavid Hildenbrand Error *err = NULL; 758046f374SDavid Hildenbrand S390TOD tod; 768046f374SDavid Hildenbrand 778046f374SDavid Hildenbrand if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { 788046f374SDavid Hildenbrand warn_report("Guest clock was not migrated. This could " 798046f374SDavid Hildenbrand "cause the guest to hang."); 808046f374SDavid Hildenbrand return 0; 818046f374SDavid Hildenbrand } 828046f374SDavid Hildenbrand 838046f374SDavid Hildenbrand tod.high = qemu_get_byte(f); 848046f374SDavid Hildenbrand tod.low = qemu_get_be64(f); 858046f374SDavid Hildenbrand 868046f374SDavid Hildenbrand tdc->set(td, &tod, &err); 878046f374SDavid Hildenbrand if (err) { 888046f374SDavid Hildenbrand error_report_err(err); 898046f374SDavid Hildenbrand return -1; 908046f374SDavid Hildenbrand } 918046f374SDavid Hildenbrand return 0; 928046f374SDavid Hildenbrand } 938046f374SDavid Hildenbrand 948046f374SDavid Hildenbrand static SaveVMHandlers savevm_tod = { 958046f374SDavid Hildenbrand .save_state = s390_tod_save, 968046f374SDavid Hildenbrand .load_state = s390_tod_load, 978046f374SDavid Hildenbrand }; 988046f374SDavid Hildenbrand 998046f374SDavid Hildenbrand static void s390_tod_realize(DeviceState *dev, Error **errp) 1008046f374SDavid Hildenbrand { 1018046f374SDavid Hildenbrand S390TODState *td = S390_TOD(dev); 1028046f374SDavid Hildenbrand 1038046f374SDavid Hildenbrand /* Legacy migration interface */ 104*ce62df53SDr. David Alan Gilbert register_savevm_live("todclock", 0, 1, &savevm_tod, td); 1058046f374SDavid Hildenbrand } 1068046f374SDavid Hildenbrand 1078046f374SDavid Hildenbrand static void s390_tod_class_init(ObjectClass *oc, void *data) 1088046f374SDavid Hildenbrand { 1098046f374SDavid Hildenbrand DeviceClass *dc = DEVICE_CLASS(oc); 1108046f374SDavid Hildenbrand 1118046f374SDavid Hildenbrand dc->desc = "TOD (Time Of Day) Clock"; 1128046f374SDavid Hildenbrand dc->realize = s390_tod_realize; 1138046f374SDavid Hildenbrand set_bit(DEVICE_CATEGORY_MISC, dc->categories); 1148046f374SDavid Hildenbrand 1158046f374SDavid Hildenbrand /* We only have one TOD clock in the system attached to the machine */ 1168046f374SDavid Hildenbrand dc->user_creatable = false; 1178046f374SDavid Hildenbrand } 1188046f374SDavid Hildenbrand 1198046f374SDavid Hildenbrand static TypeInfo s390_tod_info = { 1208046f374SDavid Hildenbrand .name = TYPE_S390_TOD, 1218046f374SDavid Hildenbrand .parent = TYPE_DEVICE, 1228046f374SDavid Hildenbrand .instance_size = sizeof(S390TODState), 1238046f374SDavid Hildenbrand .class_init = s390_tod_class_init, 1248046f374SDavid Hildenbrand .class_size = sizeof(S390TODClass), 1258046f374SDavid Hildenbrand .abstract = true, 1268046f374SDavid Hildenbrand }; 1278046f374SDavid Hildenbrand 1288046f374SDavid Hildenbrand static void register_types(void) 1298046f374SDavid Hildenbrand { 1308046f374SDavid Hildenbrand type_register_static(&s390_tod_info); 1318046f374SDavid Hildenbrand } 1328046f374SDavid Hildenbrand type_init(register_types); 133