Line data Source code
1 : /* $OpenBSD: vmmci.c,v 1.3 2017/05/02 09:50:38 mlarkin Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 : #include <sys/systm.h>
21 : #include <sys/kernel.h>
22 : #include <sys/timeout.h>
23 : #include <sys/signalvar.h>
24 : #include <sys/syslog.h>
25 : #include <sys/device.h>
26 : #include <sys/pool.h>
27 : #include <sys/proc.h>
28 :
29 : #include <machine/bus.h>
30 :
31 : #include <dev/pv/virtioreg.h>
32 : #include <dev/pv/virtiovar.h>
33 : #include <dev/pv/pvvar.h>
34 : #include <dev/rndvar.h>
35 :
36 : enum vmmci_cmd {
37 : VMMCI_NONE = 0,
38 : VMMCI_SHUTDOWN,
39 : VMMCI_REBOOT,
40 : VMMCI_SYNCRTC,
41 : };
42 :
43 : struct vmmci_softc {
44 : struct device sc_dev;
45 : struct virtio_softc *sc_virtio;
46 : enum vmmci_cmd sc_cmd;
47 : unsigned int sc_interval;
48 : struct ksensordev sc_sensordev;
49 : struct ksensor sc_sensor;
50 : struct timeout sc_tick;
51 : };
52 :
53 : int vmmci_match(struct device *, void *, void *);
54 : void vmmci_attach(struct device *, struct device *, void *);
55 : int vmmci_activate(struct device *, int);
56 :
57 : int vmmci_config_change(struct virtio_softc *);
58 : void vmmci_tick(void *);
59 : void vmmci_tick_hook(struct device *);
60 :
61 : struct cfattach vmmci_ca = {
62 : sizeof(struct vmmci_softc),
63 : vmmci_match,
64 : vmmci_attach,
65 : NULL,
66 : vmmci_activate
67 : };
68 :
69 : /* Configuration registers */
70 : #define VMMCI_CONFIG_COMMAND 0
71 : #define VMMCI_CONFIG_TIME_SEC 4
72 : #define VMMCI_CONFIG_TIME_USEC 12
73 :
74 : /* Feature bits */
75 : #define VMMCI_F_TIMESYNC (1<<0)
76 : #define VMMCI_F_ACK (1<<1)
77 : #define VMMCI_F_SYNCRTC (1<<2)
78 :
79 : struct cfdriver vmmci_cd = {
80 : NULL, "vmmci", DV_DULL
81 : };
82 :
83 : int
84 0 : vmmci_match(struct device *parent, void *match, void *aux)
85 : {
86 0 : struct virtio_softc *va = aux;
87 0 : if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_VMMCI)
88 0 : return (1);
89 0 : return (0);
90 0 : }
91 :
92 : void
93 0 : vmmci_attach(struct device *parent, struct device *self, void *aux)
94 : {
95 0 : struct vmmci_softc *sc = (struct vmmci_softc *)self;
96 0 : struct virtio_softc *vsc = (struct virtio_softc *)parent;
97 : uint32_t features;
98 :
99 0 : if (vsc->sc_child != NULL)
100 0 : panic("already attached to something else");
101 :
102 0 : vsc->sc_child = self;
103 0 : vsc->sc_nvqs = 0;
104 0 : vsc->sc_config_change = vmmci_config_change;
105 0 : vsc->sc_ipl = IPL_NET;
106 0 : sc->sc_virtio = vsc;
107 :
108 : features = VMMCI_F_TIMESYNC | VMMCI_F_ACK | VMMCI_F_SYNCRTC;
109 0 : features = virtio_negotiate_features(vsc, features, NULL);
110 :
111 0 : if (features & VMMCI_F_TIMESYNC) {
112 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
113 : sizeof(sc->sc_sensordev.xname));
114 0 : sc->sc_sensor.type = SENSOR_TIMEDELTA;
115 0 : sc->sc_sensor.status = SENSOR_S_UNKNOWN;
116 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
117 0 : sensordev_install(&sc->sc_sensordev);
118 :
119 0 : config_mountroot(self, vmmci_tick_hook);
120 0 : }
121 :
122 0 : printf("\n");
123 0 : }
124 :
125 : int
126 0 : vmmci_activate(struct device *self, int act)
127 : {
128 0 : struct vmmci_softc *sc = (struct vmmci_softc *)self;
129 0 : struct virtio_softc *vsc = sc->sc_virtio;
130 :
131 0 : if ((vsc->sc_features & VMMCI_F_ACK) == 0)
132 0 : return (0);
133 :
134 0 : switch (act) {
135 : case DVACT_POWERDOWN:
136 0 : printf("%s: powerdown\n", sc->sc_dev.dv_xname);
137 :
138 : /*
139 : * Tell the host that we are shutting down. The host will
140 : * start a timer and kill our VM if we didn't reboot before
141 : * expiration. This avoids being stuck in the
142 : * "Please press any key to reboot" handler on RB_HALT;
143 : * without hooking into the MD code directly.
144 : */
145 0 : virtio_write_device_config_4(vsc, VMMCI_CONFIG_COMMAND,
146 : VMMCI_SHUTDOWN);
147 0 : break;
148 : default:
149 : break;
150 : }
151 0 : return (0);
152 0 : }
153 :
154 : int
155 0 : vmmci_config_change(struct virtio_softc *vsc)
156 : {
157 0 : struct vmmci_softc *sc = (struct vmmci_softc *)vsc->sc_child;
158 : uint32_t cmd;
159 :
160 : /* Check for command */
161 0 : cmd = virtio_read_device_config_4(vsc, VMMCI_CONFIG_COMMAND);
162 0 : if (cmd == sc->sc_cmd)
163 0 : return (0);
164 0 : sc->sc_cmd = cmd;
165 :
166 0 : switch (cmd) {
167 : case VMMCI_NONE:
168 : /* no action */
169 : break;
170 : case VMMCI_SHUTDOWN:
171 0 : pvbus_shutdown(&sc->sc_dev);
172 0 : break;
173 : case VMMCI_REBOOT:
174 0 : pvbus_reboot(&sc->sc_dev);
175 0 : break;
176 : case VMMCI_SYNCRTC:
177 0 : inittodr(time_second);
178 0 : sc->sc_cmd = VMMCI_NONE;
179 0 : break;
180 : default:
181 0 : printf("%s: invalid command %d\n", sc->sc_dev.dv_xname, cmd);
182 : cmd = VMMCI_NONE;
183 0 : break;
184 : }
185 :
186 0 : if ((cmd != VMMCI_NONE) &&
187 0 : (vsc->sc_features & VMMCI_F_ACK))
188 0 : virtio_write_device_config_4(vsc, VMMCI_CONFIG_COMMAND, cmd);
189 :
190 0 : return (1);
191 0 : }
192 :
193 : void
194 0 : vmmci_tick(void *arg)
195 : {
196 0 : struct vmmci_softc *sc = arg;
197 0 : struct virtio_softc *vsc = sc->sc_virtio;
198 0 : struct timeval *guest = &sc->sc_sensor.tv;
199 : struct timeval host, diff;
200 :
201 0 : microtime(guest);
202 :
203 : /* Update time delta sensor */
204 0 : host.tv_sec = virtio_read_device_config_8(vsc, VMMCI_CONFIG_TIME_SEC);
205 0 : host.tv_usec = virtio_read_device_config_8(vsc, VMMCI_CONFIG_TIME_USEC);
206 :
207 0 : if (host.tv_usec > 0) {
208 0 : timersub(guest, &host, &diff);
209 :
210 0 : sc->sc_sensor.value = (uint64_t)diff.tv_sec * 1000000000LL +
211 0 : (uint64_t)diff.tv_usec * 1000LL;
212 0 : sc->sc_sensor.status = SENSOR_S_OK;
213 0 : } else
214 0 : sc->sc_sensor.status = SENSOR_S_UNKNOWN;
215 :
216 0 : timeout_add_sec(&sc->sc_tick, 15);
217 0 : }
218 :
219 : void
220 0 : vmmci_tick_hook(struct device *self)
221 : {
222 0 : struct vmmci_softc *sc = (struct vmmci_softc *)self;
223 :
224 0 : timeout_set(&sc->sc_tick, vmmci_tick, sc);
225 0 : vmmci_tick(sc);
226 0 : }
|