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" 17*4f91550aSCho, Yu-Chen #include "sysemu/tcg.h" 18*4f91550aSCho, Yu-Chen #include "sysemu/qtest.h" 19ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h" 208046f374SDavid Hildenbrand #include "migration/register.h" 218046f374SDavid Hildenbrand 228046f374SDavid Hildenbrand void s390_init_tod(void) 238046f374SDavid Hildenbrand { 248046f374SDavid Hildenbrand Object *obj; 258046f374SDavid Hildenbrand 268046f374SDavid Hildenbrand if (kvm_enabled()) { 278046f374SDavid Hildenbrand obj = object_new(TYPE_KVM_S390_TOD); 28*4f91550aSCho, Yu-Chen } else if (tcg_enabled()) { 298046f374SDavid Hildenbrand obj = object_new(TYPE_QEMU_S390_TOD); 30*4f91550aSCho, Yu-Chen } else if (qtest_enabled()) { 31*4f91550aSCho, Yu-Chen return; 32*4f91550aSCho, Yu-Chen } else { 33*4f91550aSCho, Yu-Chen error_report("current accelerator not handled in s390_init_tod!"); 34*4f91550aSCho, Yu-Chen abort(); 358046f374SDavid Hildenbrand } 36d2623129SMarkus Armbruster object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj); 378046f374SDavid Hildenbrand object_unref(obj); 388046f374SDavid Hildenbrand 39ce189ab2SMarkus Armbruster qdev_realize(DEVICE(obj), NULL, &error_fatal); 408046f374SDavid Hildenbrand } 418046f374SDavid Hildenbrand 427de3b1cdSDavid Hildenbrand S390TODState *s390_get_todstate(void) 437de3b1cdSDavid Hildenbrand { 447de3b1cdSDavid Hildenbrand static S390TODState *ts; 457de3b1cdSDavid Hildenbrand 467de3b1cdSDavid Hildenbrand if (!ts) { 477de3b1cdSDavid Hildenbrand ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); 487de3b1cdSDavid Hildenbrand } 497de3b1cdSDavid Hildenbrand 507de3b1cdSDavid Hildenbrand return ts; 517de3b1cdSDavid Hildenbrand } 527de3b1cdSDavid Hildenbrand 538046f374SDavid Hildenbrand #define S390_TOD_CLOCK_VALUE_MISSING 0x00 548046f374SDavid Hildenbrand #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 558046f374SDavid Hildenbrand 568046f374SDavid Hildenbrand static void s390_tod_save(QEMUFile *f, void *opaque) 578046f374SDavid Hildenbrand { 588046f374SDavid Hildenbrand S390TODState *td = opaque; 598046f374SDavid Hildenbrand S390TODClass *tdc = S390_TOD_GET_CLASS(td); 608046f374SDavid Hildenbrand Error *err = NULL; 618046f374SDavid Hildenbrand S390TOD tod; 628046f374SDavid Hildenbrand 638046f374SDavid Hildenbrand tdc->get(td, &tod, &err); 648046f374SDavid Hildenbrand if (err) { 658046f374SDavid Hildenbrand warn_report_err(err); 668046f374SDavid Hildenbrand error_printf("Guest clock will not be migrated " 678046f374SDavid Hildenbrand "which could cause the guest to hang."); 688046f374SDavid Hildenbrand qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); 698046f374SDavid Hildenbrand return; 708046f374SDavid Hildenbrand } 718046f374SDavid Hildenbrand 728046f374SDavid Hildenbrand qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); 738046f374SDavid Hildenbrand qemu_put_byte(f, tod.high); 748046f374SDavid Hildenbrand qemu_put_be64(f, tod.low); 758046f374SDavid Hildenbrand } 768046f374SDavid Hildenbrand 778046f374SDavid Hildenbrand static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) 788046f374SDavid Hildenbrand { 798046f374SDavid Hildenbrand S390TODState *td = opaque; 808046f374SDavid Hildenbrand S390TODClass *tdc = S390_TOD_GET_CLASS(td); 818046f374SDavid Hildenbrand Error *err = NULL; 828046f374SDavid Hildenbrand S390TOD tod; 838046f374SDavid Hildenbrand 848046f374SDavid Hildenbrand if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { 858046f374SDavid Hildenbrand warn_report("Guest clock was not migrated. This could " 868046f374SDavid Hildenbrand "cause the guest to hang."); 878046f374SDavid Hildenbrand return 0; 888046f374SDavid Hildenbrand } 898046f374SDavid Hildenbrand 908046f374SDavid Hildenbrand tod.high = qemu_get_byte(f); 918046f374SDavid Hildenbrand tod.low = qemu_get_be64(f); 928046f374SDavid Hildenbrand 938046f374SDavid Hildenbrand tdc->set(td, &tod, &err); 948046f374SDavid Hildenbrand if (err) { 958046f374SDavid Hildenbrand error_report_err(err); 968046f374SDavid Hildenbrand return -1; 978046f374SDavid Hildenbrand } 988046f374SDavid Hildenbrand return 0; 998046f374SDavid Hildenbrand } 1008046f374SDavid Hildenbrand 1018046f374SDavid Hildenbrand static SaveVMHandlers savevm_tod = { 1028046f374SDavid Hildenbrand .save_state = s390_tod_save, 1038046f374SDavid Hildenbrand .load_state = s390_tod_load, 1048046f374SDavid Hildenbrand }; 1058046f374SDavid Hildenbrand 1068046f374SDavid Hildenbrand static void s390_tod_realize(DeviceState *dev, Error **errp) 1078046f374SDavid Hildenbrand { 1088046f374SDavid Hildenbrand S390TODState *td = S390_TOD(dev); 1098046f374SDavid Hildenbrand 1108046f374SDavid Hildenbrand /* Legacy migration interface */ 111ce62df53SDr. David Alan Gilbert register_savevm_live("todclock", 0, 1, &savevm_tod, td); 1128046f374SDavid Hildenbrand } 1138046f374SDavid Hildenbrand 1148046f374SDavid Hildenbrand static void s390_tod_class_init(ObjectClass *oc, void *data) 1158046f374SDavid Hildenbrand { 1168046f374SDavid Hildenbrand DeviceClass *dc = DEVICE_CLASS(oc); 1178046f374SDavid Hildenbrand 1188046f374SDavid Hildenbrand dc->desc = "TOD (Time Of Day) Clock"; 1198046f374SDavid Hildenbrand dc->realize = s390_tod_realize; 1208046f374SDavid Hildenbrand set_bit(DEVICE_CATEGORY_MISC, dc->categories); 1218046f374SDavid Hildenbrand 1228046f374SDavid Hildenbrand /* We only have one TOD clock in the system attached to the machine */ 1238046f374SDavid Hildenbrand dc->user_creatable = false; 1248046f374SDavid Hildenbrand } 1258046f374SDavid Hildenbrand 1268046f374SDavid Hildenbrand static TypeInfo s390_tod_info = { 1278046f374SDavid Hildenbrand .name = TYPE_S390_TOD, 1288046f374SDavid Hildenbrand .parent = TYPE_DEVICE, 1298046f374SDavid Hildenbrand .instance_size = sizeof(S390TODState), 1308046f374SDavid Hildenbrand .class_init = s390_tod_class_init, 1318046f374SDavid Hildenbrand .class_size = sizeof(S390TODClass), 1328046f374SDavid Hildenbrand .abstract = true, 1338046f374SDavid Hildenbrand }; 1348046f374SDavid Hildenbrand 1358046f374SDavid Hildenbrand static void register_types(void) 1368046f374SDavid Hildenbrand { 1378046f374SDavid Hildenbrand type_register_static(&s390_tod_info); 1388046f374SDavid Hildenbrand } 1398046f374SDavid Hildenbrand type_init(register_types); 140