]>
Commit | Line | Data |
---|---|---|
8046f374 DH |
1 | /* |
2 | * TOD (Time Of Day) clock - KVM implementation | |
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 "qapi/error.h" | |
9bc9d3d1 | 13 | #include "sysemu/sysemu.h" |
8046f374 DH |
14 | #include "hw/s390x/tod.h" |
15 | #include "kvm_s390x.h" | |
16 | ||
9bc9d3d1 | 17 | static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp) |
8046f374 DH |
18 | { |
19 | int r; | |
20 | ||
21 | r = kvm_s390_get_clock_ext(&tod->high, &tod->low); | |
22 | if (r == -ENXIO) { | |
23 | r = kvm_s390_get_clock(&tod->high, &tod->low); | |
24 | } | |
25 | if (r) { | |
26 | error_setg(errp, "Unable to get KVM guest TOD clock: %s", | |
27 | strerror(-r)); | |
28 | } | |
29 | } | |
30 | ||
9bc9d3d1 DH |
31 | static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) |
32 | { | |
33 | if (td->stopped) { | |
34 | *tod = td->base; | |
35 | return; | |
36 | } | |
37 | ||
38 | kvm_s390_get_tod_raw(tod, errp); | |
39 | } | |
40 | ||
41 | static void kvm_s390_set_tod_raw(const S390TOD *tod, Error **errp) | |
8046f374 DH |
42 | { |
43 | int r; | |
44 | ||
45 | r = kvm_s390_set_clock_ext(tod->high, tod->low); | |
46 | if (r == -ENXIO) { | |
47 | r = kvm_s390_set_clock(tod->high, tod->low); | |
48 | } | |
49 | if (r) { | |
50 | error_setg(errp, "Unable to set KVM guest TOD clock: %s", | |
51 | strerror(-r)); | |
52 | } | |
53 | } | |
54 | ||
9bc9d3d1 DH |
55 | static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) |
56 | { | |
57 | Error *local_err = NULL; | |
58 | ||
59 | /* | |
60 | * Somebody (e.g. migration) set the TOD. We'll store it into KVM to | |
61 | * properly detect errors now but take a look at the runstate to decide | |
62 | * whether really to keep the tod running. E.g. during migration, this | |
63 | * is the point where we want to stop the initially running TOD to fire | |
64 | * it back up when actually starting the migrated guest. | |
65 | */ | |
66 | kvm_s390_set_tod_raw(tod, &local_err); | |
67 | if (local_err) { | |
68 | error_propagate(errp, local_err); | |
69 | return; | |
70 | } | |
71 | ||
72 | if (runstate_is_running()) { | |
73 | td->stopped = false; | |
74 | } else { | |
75 | td->stopped = true; | |
76 | td->base = *tod; | |
77 | } | |
78 | } | |
79 | ||
80 | static void kvm_s390_tod_vm_state_change(void *opaque, int running, | |
81 | RunState state) | |
82 | { | |
83 | S390TODState *td = opaque; | |
84 | Error *local_err = NULL; | |
85 | ||
86 | if (running && td->stopped) { | |
87 | /* Set the old TOD when running the VM - start the TOD clock. */ | |
88 | kvm_s390_set_tod_raw(&td->base, &local_err); | |
89 | if (local_err) { | |
90 | warn_report_err(local_err); | |
91 | } | |
92 | /* Treat errors like the TOD was running all the time. */ | |
93 | td->stopped = false; | |
94 | } else if (!running && !td->stopped) { | |
95 | /* Store the TOD when stopping the VM - stop the TOD clock. */ | |
96 | kvm_s390_get_tod_raw(&td->base, &local_err); | |
97 | if (local_err) { | |
98 | /* Keep the TOD running in case we could not back it up. */ | |
99 | warn_report_err(local_err); | |
100 | } else { | |
101 | td->stopped = true; | |
102 | } | |
103 | } | |
104 | } | |
105 | ||
106 | static void kvm_s390_tod_realize(DeviceState *dev, Error **errp) | |
107 | { | |
108 | S390TODState *td = S390_TOD(dev); | |
109 | S390TODClass *tdc = S390_TOD_GET_CLASS(td); | |
110 | Error *local_err = NULL; | |
111 | ||
112 | tdc->parent_realize(dev, &local_err); | |
113 | if (local_err) { | |
114 | error_propagate(errp, local_err); | |
115 | return; | |
116 | } | |
117 | ||
118 | /* | |
119 | * We need to know when the VM gets started/stopped to start/stop the TOD. | |
120 | * As we can never have more than one TOD instance (and that will never be | |
121 | * removed), registering here and never unregistering is good enough. | |
122 | */ | |
123 | qemu_add_vm_change_state_handler(kvm_s390_tod_vm_state_change, td); | |
124 | } | |
125 | ||
8046f374 DH |
126 | static void kvm_s390_tod_class_init(ObjectClass *oc, void *data) |
127 | { | |
128 | S390TODClass *tdc = S390_TOD_CLASS(oc); | |
129 | ||
9bc9d3d1 DH |
130 | device_class_set_parent_realize(DEVICE_CLASS(oc), kvm_s390_tod_realize, |
131 | &tdc->parent_realize); | |
8046f374 DH |
132 | tdc->get = kvm_s390_tod_get; |
133 | tdc->set = kvm_s390_tod_set; | |
134 | } | |
135 | ||
9bc9d3d1 DH |
136 | static void kvm_s390_tod_init(Object *obj) |
137 | { | |
138 | S390TODState *td = S390_TOD(obj); | |
139 | ||
140 | /* | |
141 | * The TOD is initially running (value stored in KVM). Avoid needless | |
142 | * loading/storing of the TOD when starting a simple VM, so let it | |
143 | * run although the (never started) VM is stopped. For migration, we | |
144 | * will properly set the TOD later. | |
145 | */ | |
146 | td->stopped = false; | |
147 | } | |
148 | ||
8046f374 DH |
149 | static TypeInfo kvm_s390_tod_info = { |
150 | .name = TYPE_KVM_S390_TOD, | |
151 | .parent = TYPE_S390_TOD, | |
152 | .instance_size = sizeof(S390TODState), | |
9bc9d3d1 | 153 | .instance_init = kvm_s390_tod_init, |
8046f374 DH |
154 | .class_init = kvm_s390_tod_class_init, |
155 | .class_size = sizeof(S390TODClass), | |
156 | }; | |
157 | ||
158 | static void register_types(void) | |
159 | { | |
160 | type_register_static(&kvm_s390_tod_info); | |
161 | } | |
162 | type_init(register_types); |