18046f374SDavid Hildenbrand /*
28046f374SDavid Hildenbrand * TOD (Time Of Day) clock - KVM implementation
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 "qapi/error.h"
130b8fa32fSMarkus Armbruster #include "qemu/module.h"
1454d31236SMarkus Armbruster #include "sysemu/runstate.h"
158046f374SDavid Hildenbrand #include "hw/s390x/tod.h"
16*f5f9c6eaSPhilippe Mathieu-Daudé #include "target/s390x/kvm/pv.h"
1767043607SCho, Yu-Chen #include "kvm/kvm_s390x.h"
188046f374SDavid Hildenbrand
kvm_s390_get_tod_raw(S390TOD * tod,Error ** errp)199bc9d3d1SDavid Hildenbrand static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp)
208046f374SDavid Hildenbrand {
218046f374SDavid Hildenbrand int r;
228046f374SDavid Hildenbrand
238046f374SDavid Hildenbrand r = kvm_s390_get_clock_ext(&tod->high, &tod->low);
248046f374SDavid Hildenbrand if (r == -ENXIO) {
258046f374SDavid Hildenbrand r = kvm_s390_get_clock(&tod->high, &tod->low);
268046f374SDavid Hildenbrand }
278046f374SDavid Hildenbrand if (r) {
288046f374SDavid Hildenbrand error_setg(errp, "Unable to get KVM guest TOD clock: %s",
298046f374SDavid Hildenbrand strerror(-r));
308046f374SDavid Hildenbrand }
318046f374SDavid Hildenbrand }
328046f374SDavid Hildenbrand
kvm_s390_tod_get(const S390TODState * td,S390TOD * tod,Error ** errp)339bc9d3d1SDavid Hildenbrand static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp)
349bc9d3d1SDavid Hildenbrand {
359bc9d3d1SDavid Hildenbrand if (td->stopped) {
369bc9d3d1SDavid Hildenbrand *tod = td->base;
379bc9d3d1SDavid Hildenbrand return;
389bc9d3d1SDavid Hildenbrand }
399bc9d3d1SDavid Hildenbrand
409bc9d3d1SDavid Hildenbrand kvm_s390_get_tod_raw(tod, errp);
419bc9d3d1SDavid Hildenbrand }
429bc9d3d1SDavid Hildenbrand
kvm_s390_set_tod_raw(const S390TOD * tod,Error ** errp)439bc9d3d1SDavid Hildenbrand static void kvm_s390_set_tod_raw(const S390TOD *tod, Error **errp)
448046f374SDavid Hildenbrand {
458046f374SDavid Hildenbrand int r;
468046f374SDavid Hildenbrand
478046f374SDavid Hildenbrand r = kvm_s390_set_clock_ext(tod->high, tod->low);
488046f374SDavid Hildenbrand if (r == -ENXIO) {
498046f374SDavid Hildenbrand r = kvm_s390_set_clock(tod->high, tod->low);
508046f374SDavid Hildenbrand }
518046f374SDavid Hildenbrand if (r) {
528046f374SDavid Hildenbrand error_setg(errp, "Unable to set KVM guest TOD clock: %s",
538046f374SDavid Hildenbrand strerror(-r));
548046f374SDavid Hildenbrand }
558046f374SDavid Hildenbrand }
568046f374SDavid Hildenbrand
kvm_s390_tod_set(S390TODState * td,const S390TOD * tod,Error ** errp)579bc9d3d1SDavid Hildenbrand static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp)
589bc9d3d1SDavid Hildenbrand {
599bc9d3d1SDavid Hildenbrand Error *local_err = NULL;
609bc9d3d1SDavid Hildenbrand
619bc9d3d1SDavid Hildenbrand /*
629bc9d3d1SDavid Hildenbrand * Somebody (e.g. migration) set the TOD. We'll store it into KVM to
639bc9d3d1SDavid Hildenbrand * properly detect errors now but take a look at the runstate to decide
649bc9d3d1SDavid Hildenbrand * whether really to keep the tod running. E.g. during migration, this
659bc9d3d1SDavid Hildenbrand * is the point where we want to stop the initially running TOD to fire
669bc9d3d1SDavid Hildenbrand * it back up when actually starting the migrated guest.
679bc9d3d1SDavid Hildenbrand */
689bc9d3d1SDavid Hildenbrand kvm_s390_set_tod_raw(tod, &local_err);
699bc9d3d1SDavid Hildenbrand if (local_err) {
709bc9d3d1SDavid Hildenbrand error_propagate(errp, local_err);
719bc9d3d1SDavid Hildenbrand return;
729bc9d3d1SDavid Hildenbrand }
739bc9d3d1SDavid Hildenbrand
749bc9d3d1SDavid Hildenbrand if (runstate_is_running()) {
759bc9d3d1SDavid Hildenbrand td->stopped = false;
769bc9d3d1SDavid Hildenbrand } else {
779bc9d3d1SDavid Hildenbrand td->stopped = true;
789bc9d3d1SDavid Hildenbrand td->base = *tod;
799bc9d3d1SDavid Hildenbrand }
809bc9d3d1SDavid Hildenbrand }
819bc9d3d1SDavid Hildenbrand
kvm_s390_tod_vm_state_change(void * opaque,bool running,RunState state)82538f0497SPhilippe Mathieu-Daudé static void kvm_s390_tod_vm_state_change(void *opaque, bool running,
839bc9d3d1SDavid Hildenbrand RunState state)
849bc9d3d1SDavid Hildenbrand {
859bc9d3d1SDavid Hildenbrand S390TODState *td = opaque;
869bc9d3d1SDavid Hildenbrand Error *local_err = NULL;
879bc9d3d1SDavid Hildenbrand
8838621181SNico Boehr /*
8938621181SNico Boehr * Under PV, the clock is under ultravisor control, hence we cannot restore
9038621181SNico Boehr * it on resume.
9138621181SNico Boehr */
9238621181SNico Boehr if (s390_is_pv()) {
9338621181SNico Boehr return;
9438621181SNico Boehr }
9538621181SNico Boehr
969bc9d3d1SDavid Hildenbrand if (running && td->stopped) {
979bc9d3d1SDavid Hildenbrand /* Set the old TOD when running the VM - start the TOD clock. */
989bc9d3d1SDavid Hildenbrand kvm_s390_set_tod_raw(&td->base, &local_err);
999bc9d3d1SDavid Hildenbrand if (local_err) {
1009bc9d3d1SDavid Hildenbrand warn_report_err(local_err);
1019bc9d3d1SDavid Hildenbrand }
1029bc9d3d1SDavid Hildenbrand /* Treat errors like the TOD was running all the time. */
1039bc9d3d1SDavid Hildenbrand td->stopped = false;
1049bc9d3d1SDavid Hildenbrand } else if (!running && !td->stopped) {
1059bc9d3d1SDavid Hildenbrand /* Store the TOD when stopping the VM - stop the TOD clock. */
1069bc9d3d1SDavid Hildenbrand kvm_s390_get_tod_raw(&td->base, &local_err);
1079bc9d3d1SDavid Hildenbrand if (local_err) {
1089bc9d3d1SDavid Hildenbrand /* Keep the TOD running in case we could not back it up. */
1099bc9d3d1SDavid Hildenbrand warn_report_err(local_err);
1109bc9d3d1SDavid Hildenbrand } else {
1119bc9d3d1SDavid Hildenbrand td->stopped = true;
1129bc9d3d1SDavid Hildenbrand }
1139bc9d3d1SDavid Hildenbrand }
1149bc9d3d1SDavid Hildenbrand }
1159bc9d3d1SDavid Hildenbrand
kvm_s390_tod_realize(DeviceState * dev,Error ** errp)1169bc9d3d1SDavid Hildenbrand static void kvm_s390_tod_realize(DeviceState *dev, Error **errp)
1179bc9d3d1SDavid Hildenbrand {
1189bc9d3d1SDavid Hildenbrand S390TODState *td = S390_TOD(dev);
1199bc9d3d1SDavid Hildenbrand S390TODClass *tdc = S390_TOD_GET_CLASS(td);
1209bc9d3d1SDavid Hildenbrand Error *local_err = NULL;
1219bc9d3d1SDavid Hildenbrand
1229bc9d3d1SDavid Hildenbrand tdc->parent_realize(dev, &local_err);
1239bc9d3d1SDavid Hildenbrand if (local_err) {
1249bc9d3d1SDavid Hildenbrand error_propagate(errp, local_err);
1259bc9d3d1SDavid Hildenbrand return;
1269bc9d3d1SDavid Hildenbrand }
1279bc9d3d1SDavid Hildenbrand
1289bc9d3d1SDavid Hildenbrand /*
1299bc9d3d1SDavid Hildenbrand * We need to know when the VM gets started/stopped to start/stop the TOD.
1309bc9d3d1SDavid Hildenbrand * As we can never have more than one TOD instance (and that will never be
1319bc9d3d1SDavid Hildenbrand * removed), registering here and never unregistering is good enough.
1329bc9d3d1SDavid Hildenbrand */
1339bc9d3d1SDavid Hildenbrand qemu_add_vm_change_state_handler(kvm_s390_tod_vm_state_change, td);
1349bc9d3d1SDavid Hildenbrand }
1359bc9d3d1SDavid Hildenbrand
kvm_s390_tod_class_init(ObjectClass * oc,void * data)1368046f374SDavid Hildenbrand static void kvm_s390_tod_class_init(ObjectClass *oc, void *data)
1378046f374SDavid Hildenbrand {
1388046f374SDavid Hildenbrand S390TODClass *tdc = S390_TOD_CLASS(oc);
1398046f374SDavid Hildenbrand
1409bc9d3d1SDavid Hildenbrand device_class_set_parent_realize(DEVICE_CLASS(oc), kvm_s390_tod_realize,
1419bc9d3d1SDavid Hildenbrand &tdc->parent_realize);
1428046f374SDavid Hildenbrand tdc->get = kvm_s390_tod_get;
1438046f374SDavid Hildenbrand tdc->set = kvm_s390_tod_set;
1448046f374SDavid Hildenbrand }
1458046f374SDavid Hildenbrand
kvm_s390_tod_init(Object * obj)1469bc9d3d1SDavid Hildenbrand static void kvm_s390_tod_init(Object *obj)
1479bc9d3d1SDavid Hildenbrand {
1489bc9d3d1SDavid Hildenbrand S390TODState *td = S390_TOD(obj);
1499bc9d3d1SDavid Hildenbrand
1509bc9d3d1SDavid Hildenbrand /*
1519bc9d3d1SDavid Hildenbrand * The TOD is initially running (value stored in KVM). Avoid needless
1529bc9d3d1SDavid Hildenbrand * loading/storing of the TOD when starting a simple VM, so let it
1539bc9d3d1SDavid Hildenbrand * run although the (never started) VM is stopped. For migration, we
1549bc9d3d1SDavid Hildenbrand * will properly set the TOD later.
1559bc9d3d1SDavid Hildenbrand */
1569bc9d3d1SDavid Hildenbrand td->stopped = false;
1579bc9d3d1SDavid Hildenbrand }
1589bc9d3d1SDavid Hildenbrand
1595e78c98bSBernhard Beschow static const TypeInfo kvm_s390_tod_info = {
1608046f374SDavid Hildenbrand .name = TYPE_KVM_S390_TOD,
1618046f374SDavid Hildenbrand .parent = TYPE_S390_TOD,
1628046f374SDavid Hildenbrand .instance_size = sizeof(S390TODState),
1639bc9d3d1SDavid Hildenbrand .instance_init = kvm_s390_tod_init,
1648046f374SDavid Hildenbrand .class_init = kvm_s390_tod_class_init,
1658046f374SDavid Hildenbrand .class_size = sizeof(S390TODClass),
1668046f374SDavid Hildenbrand };
1678046f374SDavid Hildenbrand
register_types(void)1688046f374SDavid Hildenbrand static void register_types(void)
1698046f374SDavid Hildenbrand {
1708046f374SDavid Hildenbrand type_register_static(&kvm_s390_tod_info);
1718046f374SDavid Hildenbrand }
1728046f374SDavid Hildenbrand type_init(register_types);
173