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