1 /* $OpenBSD: vmmci.c,v 1.9 2021/11/05 11:38:29 mpi 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 #include <sys/sensors.h> 29 30 #include <machine/bus.h> 31 32 #include <dev/pv/virtioreg.h> 33 #include <dev/pv/virtiovar.h> 34 #include <dev/pv/pvvar.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 const 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 (1ULL<<0) 76 #define VMMCI_F_ACK (1ULL<<1) 77 #define VMMCI_F_SYNCRTC (1ULL<<2) 78 79 struct cfdriver vmmci_cd = { 80 NULL, "vmmci", DV_DULL 81 }; 82 83 int 84 vmmci_match(struct device *parent, void *match, void *aux) 85 { 86 struct virtio_softc *va = aux; 87 if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_VMMCI) 88 return (1); 89 return (0); 90 } 91 92 void 93 vmmci_attach(struct device *parent, struct device *self, void *aux) 94 { 95 struct vmmci_softc *sc = (struct vmmci_softc *)self; 96 struct virtio_softc *vsc = (struct virtio_softc *)parent; 97 98 if (vsc->sc_child != NULL) 99 panic("already attached to something else"); 100 101 vsc->sc_child = self; 102 vsc->sc_nvqs = 0; 103 vsc->sc_config_change = vmmci_config_change; 104 vsc->sc_ipl = IPL_NET; 105 sc->sc_virtio = vsc; 106 107 vsc->sc_driver_features = VMMCI_F_TIMESYNC | VMMCI_F_ACK | 108 VMMCI_F_SYNCRTC; 109 virtio_negotiate_features(vsc, NULL); 110 111 if (virtio_has_feature(vsc, VMMCI_F_TIMESYNC)) { 112 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 113 sizeof(sc->sc_sensordev.xname)); 114 sc->sc_sensor.type = SENSOR_TIMEDELTA; 115 sc->sc_sensor.status = SENSOR_S_UNKNOWN; 116 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 117 sensordev_install(&sc->sc_sensordev); 118 119 config_mountroot(self, vmmci_tick_hook); 120 } 121 122 printf("\n"); 123 } 124 125 int 126 vmmci_activate(struct device *self, int act) 127 { 128 struct vmmci_softc *sc = (struct vmmci_softc *)self; 129 struct virtio_softc *vsc = sc->sc_virtio; 130 131 if (virtio_has_feature(vsc, VMMCI_F_ACK) == 0) 132 return (0); 133 134 switch (act) { 135 case DVACT_POWERDOWN: 136 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 virtio_write_device_config_4(vsc, VMMCI_CONFIG_COMMAND, 146 VMMCI_SHUTDOWN); 147 break; 148 default: 149 break; 150 } 151 return (0); 152 } 153 154 int 155 vmmci_config_change(struct virtio_softc *vsc) 156 { 157 struct vmmci_softc *sc = (struct vmmci_softc *)vsc->sc_child; 158 uint32_t cmd; 159 160 /* Check for command */ 161 cmd = virtio_read_device_config_4(vsc, VMMCI_CONFIG_COMMAND); 162 if (cmd == sc->sc_cmd) 163 return (0); 164 sc->sc_cmd = cmd; 165 166 switch (cmd) { 167 case VMMCI_NONE: 168 /* no action */ 169 break; 170 case VMMCI_SHUTDOWN: 171 pvbus_shutdown(&sc->sc_dev); 172 break; 173 case VMMCI_REBOOT: 174 pvbus_reboot(&sc->sc_dev); 175 break; 176 case VMMCI_SYNCRTC: 177 inittodr(gettime()); 178 sc->sc_cmd = VMMCI_NONE; 179 break; 180 default: 181 printf("%s: invalid command %d\n", sc->sc_dev.dv_xname, cmd); 182 cmd = VMMCI_NONE; 183 break; 184 } 185 186 if ((cmd != VMMCI_NONE) && virtio_has_feature(vsc, VMMCI_F_ACK)) 187 virtio_write_device_config_4(vsc, VMMCI_CONFIG_COMMAND, cmd); 188 189 return (1); 190 } 191 192 void 193 vmmci_tick(void *arg) 194 { 195 struct vmmci_softc *sc = arg; 196 struct virtio_softc *vsc = sc->sc_virtio; 197 struct timeval *guest = &sc->sc_sensor.tv; 198 struct timeval host, diff; 199 200 microtime(guest); 201 202 /* Update time delta sensor */ 203 host.tv_sec = virtio_read_device_config_8(vsc, VMMCI_CONFIG_TIME_SEC); 204 host.tv_usec = virtio_read_device_config_8(vsc, VMMCI_CONFIG_TIME_USEC); 205 206 if (host.tv_usec > 0) { 207 timersub(guest, &host, &diff); 208 209 sc->sc_sensor.value = (uint64_t)diff.tv_sec * 1000000000LL + 210 (uint64_t)diff.tv_usec * 1000LL; 211 sc->sc_sensor.status = SENSOR_S_OK; 212 } else 213 sc->sc_sensor.status = SENSOR_S_UNKNOWN; 214 215 timeout_add_sec(&sc->sc_tick, 15); 216 } 217 218 void 219 vmmci_tick_hook(struct device *self) 220 { 221 struct vmmci_softc *sc = (struct vmmci_softc *)self; 222 223 timeout_set(&sc->sc_tick, vmmci_tick, sc); 224 vmmci_tick(sc); 225 } 226