1*5c072c8eSSepherosa Ziehau /*- 2*5c072c8eSSepherosa Ziehau * Copyright (c) 2014,2016 Microsoft Corp. 3*5c072c8eSSepherosa Ziehau * All rights reserved. 4*5c072c8eSSepherosa Ziehau * 5*5c072c8eSSepherosa Ziehau * Redistribution and use in source and binary forms, with or without 6*5c072c8eSSepherosa Ziehau * modification, are permitted provided that the following conditions 7*5c072c8eSSepherosa Ziehau * are met: 8*5c072c8eSSepherosa Ziehau * 1. Redistributions of source code must retain the above copyright 9*5c072c8eSSepherosa Ziehau * notice unmodified, this list of conditions, and the following 10*5c072c8eSSepherosa Ziehau * disclaimer. 11*5c072c8eSSepherosa Ziehau * 2. Redistributions in binary form must reproduce the above copyright 12*5c072c8eSSepherosa Ziehau * notice, this list of conditions and the following disclaimer in the 13*5c072c8eSSepherosa Ziehau * documentation and/or other materials provided with the distribution. 14*5c072c8eSSepherosa Ziehau * 15*5c072c8eSSepherosa Ziehau * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16*5c072c8eSSepherosa Ziehau * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17*5c072c8eSSepherosa Ziehau * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18*5c072c8eSSepherosa Ziehau * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19*5c072c8eSSepherosa Ziehau * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20*5c072c8eSSepherosa Ziehau * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21*5c072c8eSSepherosa Ziehau * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22*5c072c8eSSepherosa Ziehau * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23*5c072c8eSSepherosa Ziehau * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24*5c072c8eSSepherosa Ziehau * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25*5c072c8eSSepherosa Ziehau */ 26*5c072c8eSSepherosa Ziehau 27*5c072c8eSSepherosa Ziehau #include <sys/cdefs.h> 28*5c072c8eSSepherosa Ziehau __FBSDID("$FreeBSD$"); 29*5c072c8eSSepherosa Ziehau 30*5c072c8eSSepherosa Ziehau #include <sys/param.h> 31*5c072c8eSSepherosa Ziehau #include <sys/bus.h> 32*5c072c8eSSepherosa Ziehau #include <sys/kernel.h> 33*5c072c8eSSepherosa Ziehau #include <sys/module.h> 34*5c072c8eSSepherosa Ziehau #include <sys/syscallsubr.h> 35*5c072c8eSSepherosa Ziehau #include <sys/sysctl.h> 36*5c072c8eSSepherosa Ziehau #include <sys/systm.h> 37*5c072c8eSSepherosa Ziehau 38*5c072c8eSSepherosa Ziehau #include <dev/hyperv/include/hyperv.h> 39*5c072c8eSSepherosa Ziehau #include <dev/hyperv/include/vmbus.h> 40*5c072c8eSSepherosa Ziehau #include <dev/hyperv/utilities/vmbus_icreg.h> 41*5c072c8eSSepherosa Ziehau #include <dev/hyperv/utilities/vmbus_icvar.h> 42*5c072c8eSSepherosa Ziehau 43*5c072c8eSSepherosa Ziehau #define VMBUS_TIMESYNC_FWVER_MAJOR 3 44*5c072c8eSSepherosa Ziehau #define VMBUS_TIMESYNC_FWVER \ 45*5c072c8eSSepherosa Ziehau VMBUS_IC_VERSION(VMBUS_TIMESYNC_FWVER_MAJOR, 0) 46*5c072c8eSSepherosa Ziehau 47*5c072c8eSSepherosa Ziehau #define VMBUS_TIMESYNC_MSGVER_MAJOR 4 48*5c072c8eSSepherosa Ziehau #define VMBUS_TIMESYNC_MSGVER \ 49*5c072c8eSSepherosa Ziehau VMBUS_IC_VERSION(VMBUS_TIMESYNC_MSGVER_MAJOR, 0) 50*5c072c8eSSepherosa Ziehau 51*5c072c8eSSepherosa Ziehau #define VMBUS_TIMESYNC_DORTT(sc) \ 52*5c072c8eSSepherosa Ziehau ((sc)->ic_msgver >= VMBUS_IC_VERSION(4, 0) && \ 53*5c072c8eSSepherosa Ziehau (hyperv_features & CPUID_HV_MSR_TIME_REFCNT)) 54*5c072c8eSSepherosa Ziehau 55*5c072c8eSSepherosa Ziehau static int vmbus_timesync_probe(device_t); 56*5c072c8eSSepherosa Ziehau static int vmbus_timesync_attach(device_t); 57*5c072c8eSSepherosa Ziehau 58*5c072c8eSSepherosa Ziehau static const struct vmbus_ic_desc vmbus_timesync_descs[] = { 59*5c072c8eSSepherosa Ziehau { 60*5c072c8eSSepherosa Ziehau .ic_guid = { .hv_guid = { 61*5c072c8eSSepherosa Ziehau 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, 62*5c072c8eSSepherosa Ziehau 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf } }, 63*5c072c8eSSepherosa Ziehau .ic_desc = "Hyper-V Timesync" 64*5c072c8eSSepherosa Ziehau }, 65*5c072c8eSSepherosa Ziehau VMBUS_IC_DESC_END 66*5c072c8eSSepherosa Ziehau }; 67*5c072c8eSSepherosa Ziehau 68*5c072c8eSSepherosa Ziehau static device_method_t vmbus_timesync_methods[] = { 69*5c072c8eSSepherosa Ziehau /* Device interface */ 70*5c072c8eSSepherosa Ziehau DEVMETHOD(device_probe, vmbus_timesync_probe), 71*5c072c8eSSepherosa Ziehau DEVMETHOD(device_attach, vmbus_timesync_attach), 72*5c072c8eSSepherosa Ziehau DEVMETHOD(device_detach, vmbus_ic_detach), 73*5c072c8eSSepherosa Ziehau DEVMETHOD_END 74*5c072c8eSSepherosa Ziehau }; 75*5c072c8eSSepherosa Ziehau 76*5c072c8eSSepherosa Ziehau static driver_t vmbus_timesync_driver = { 77*5c072c8eSSepherosa Ziehau "hvtimesync", 78*5c072c8eSSepherosa Ziehau vmbus_timesync_methods, 79*5c072c8eSSepherosa Ziehau sizeof(struct vmbus_ic_softc) 80*5c072c8eSSepherosa Ziehau }; 81*5c072c8eSSepherosa Ziehau 82*5c072c8eSSepherosa Ziehau static devclass_t vmbus_timesync_devclass; 83*5c072c8eSSepherosa Ziehau 84*5c072c8eSSepherosa Ziehau DRIVER_MODULE(hv_timesync, vmbus, vmbus_timesync_driver, 85*5c072c8eSSepherosa Ziehau vmbus_timesync_devclass, NULL, NULL); 86*5c072c8eSSepherosa Ziehau MODULE_VERSION(hv_timesync, 1); 87*5c072c8eSSepherosa Ziehau MODULE_DEPEND(hv_timesync, vmbus, 1, 1, 1); 88*5c072c8eSSepherosa Ziehau 89*5c072c8eSSepherosa Ziehau SYSCTL_NODE(_hw, OID_AUTO, hvtimesync, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 90*5c072c8eSSepherosa Ziehau "Hyper-V timesync interface"); 91*5c072c8eSSepherosa Ziehau 92*5c072c8eSSepherosa Ziehau static int vmbus_ts_ignore_sync = 0; 93*5c072c8eSSepherosa Ziehau SYSCTL_INT(_hw_hvtimesync, OID_AUTO, ignore_sync, CTLFLAG_RWTUN, 94*5c072c8eSSepherosa Ziehau &vmbus_ts_ignore_sync, 0, "Ignore the sync request."); 95*5c072c8eSSepherosa Ziehau 96*5c072c8eSSepherosa Ziehau /* 97*5c072c8eSSepherosa Ziehau * Trigger sample sync when drift exceeds threshold (ms). 98*5c072c8eSSepherosa Ziehau * Ignore the sample request when set to 0. 99*5c072c8eSSepherosa Ziehau */ 100*5c072c8eSSepherosa Ziehau static int vmbus_ts_sample_thresh = 100; 101*5c072c8eSSepherosa Ziehau SYSCTL_INT(_hw_hvtimesync, OID_AUTO, sample_thresh, CTLFLAG_RWTUN, 102*5c072c8eSSepherosa Ziehau &vmbus_ts_sample_thresh, 0, 103*5c072c8eSSepherosa Ziehau "Threshold that makes sample request trigger the sync (unit: ms)."); 104*5c072c8eSSepherosa Ziehau 105*5c072c8eSSepherosa Ziehau static int vmbus_ts_sample_verbose = 0; 106*5c072c8eSSepherosa Ziehau SYSCTL_INT(_hw_hvtimesync, OID_AUTO, sample_verbose, CTLFLAG_RWTUN, 107*5c072c8eSSepherosa Ziehau &vmbus_ts_sample_verbose, 0, "Increase sample request verbosity."); 108*5c072c8eSSepherosa Ziehau 109*5c072c8eSSepherosa Ziehau static void 110*5c072c8eSSepherosa Ziehau vmbus_timesync(struct vmbus_ic_softc *sc, uint64_t hvtime, uint64_t sent_tc, 111*5c072c8eSSepherosa Ziehau uint8_t tsflags) 112*5c072c8eSSepherosa Ziehau { 113*5c072c8eSSepherosa Ziehau struct timespec vm_ts; 114*5c072c8eSSepherosa Ziehau uint64_t hv_ns, vm_ns, rtt = 0; 115*5c072c8eSSepherosa Ziehau 116*5c072c8eSSepherosa Ziehau if (VMBUS_TIMESYNC_DORTT(sc)) 117*5c072c8eSSepherosa Ziehau rtt = rdmsr(MSR_HV_TIME_REF_COUNT) - sent_tc; 118*5c072c8eSSepherosa Ziehau 119*5c072c8eSSepherosa Ziehau hv_ns = (hvtime - VMBUS_ICMSG_TS_BASE + rtt) * HYPERV_TIMER_NS_FACTOR; 120*5c072c8eSSepherosa Ziehau nanotime(&vm_ts); 121*5c072c8eSSepherosa Ziehau vm_ns = (vm_ts.tv_sec * NANOSEC) + vm_ts.tv_nsec; 122*5c072c8eSSepherosa Ziehau 123*5c072c8eSSepherosa Ziehau if ((tsflags & VMBUS_ICMSG_TS_FLAG_SYNC) && !vmbus_ts_ignore_sync) { 124*5c072c8eSSepherosa Ziehau struct timespec hv_ts; 125*5c072c8eSSepherosa Ziehau 126*5c072c8eSSepherosa Ziehau if (bootverbose) { 127*5c072c8eSSepherosa Ziehau device_printf(sc->ic_dev, "apply sync request, " 128*5c072c8eSSepherosa Ziehau "hv: %ju, vm: %ju\n", 129*5c072c8eSSepherosa Ziehau (uintmax_t)hv_ns, (uintmax_t)vm_ns); 130*5c072c8eSSepherosa Ziehau } 131*5c072c8eSSepherosa Ziehau hv_ts.tv_sec = hv_ns / NANOSEC; 132*5c072c8eSSepherosa Ziehau hv_ts.tv_nsec = hv_ns % NANOSEC; 133*5c072c8eSSepherosa Ziehau kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts); 134*5c072c8eSSepherosa Ziehau /* Done! */ 135*5c072c8eSSepherosa Ziehau return; 136*5c072c8eSSepherosa Ziehau } 137*5c072c8eSSepherosa Ziehau 138*5c072c8eSSepherosa Ziehau if ((tsflags & VMBUS_ICMSG_TS_FLAG_SAMPLE) && 139*5c072c8eSSepherosa Ziehau vmbus_ts_sample_thresh > 0) { 140*5c072c8eSSepherosa Ziehau int64_t diff; 141*5c072c8eSSepherosa Ziehau 142*5c072c8eSSepherosa Ziehau if (vmbus_ts_sample_verbose) { 143*5c072c8eSSepherosa Ziehau device_printf(sc->ic_dev, "sample request, " 144*5c072c8eSSepherosa Ziehau "hv: %ju, vm: %ju\n", 145*5c072c8eSSepherosa Ziehau (uintmax_t)hv_ns, (uintmax_t)vm_ns); 146*5c072c8eSSepherosa Ziehau } 147*5c072c8eSSepherosa Ziehau 148*5c072c8eSSepherosa Ziehau if (hv_ns > vm_ns) 149*5c072c8eSSepherosa Ziehau diff = hv_ns - vm_ns; 150*5c072c8eSSepherosa Ziehau else 151*5c072c8eSSepherosa Ziehau diff = vm_ns - hv_ns; 152*5c072c8eSSepherosa Ziehau /* nanosec -> millisec */ 153*5c072c8eSSepherosa Ziehau diff /= 1000000; 154*5c072c8eSSepherosa Ziehau 155*5c072c8eSSepherosa Ziehau if (diff > vmbus_ts_sample_thresh) { 156*5c072c8eSSepherosa Ziehau struct timespec hv_ts; 157*5c072c8eSSepherosa Ziehau 158*5c072c8eSSepherosa Ziehau if (bootverbose) { 159*5c072c8eSSepherosa Ziehau device_printf(sc->ic_dev, 160*5c072c8eSSepherosa Ziehau "apply sample request, hv: %ju, vm: %ju\n", 161*5c072c8eSSepherosa Ziehau (uintmax_t)hv_ns, (uintmax_t)vm_ns); 162*5c072c8eSSepherosa Ziehau } 163*5c072c8eSSepherosa Ziehau hv_ts.tv_sec = hv_ns / NANOSEC; 164*5c072c8eSSepherosa Ziehau hv_ts.tv_nsec = hv_ns % NANOSEC; 165*5c072c8eSSepherosa Ziehau kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts); 166*5c072c8eSSepherosa Ziehau } 167*5c072c8eSSepherosa Ziehau /* Done */ 168*5c072c8eSSepherosa Ziehau return; 169*5c072c8eSSepherosa Ziehau } 170*5c072c8eSSepherosa Ziehau } 171*5c072c8eSSepherosa Ziehau 172*5c072c8eSSepherosa Ziehau static void 173*5c072c8eSSepherosa Ziehau vmbus_timesync_cb(struct vmbus_channel *chan, void *xsc) 174*5c072c8eSSepherosa Ziehau { 175*5c072c8eSSepherosa Ziehau struct vmbus_ic_softc *sc = xsc; 176*5c072c8eSSepherosa Ziehau struct vmbus_icmsg_hdr *hdr; 177*5c072c8eSSepherosa Ziehau const struct vmbus_icmsg_timesync *msg; 178*5c072c8eSSepherosa Ziehau int dlen, error; 179*5c072c8eSSepherosa Ziehau uint64_t xactid; 180*5c072c8eSSepherosa Ziehau void *data; 181*5c072c8eSSepherosa Ziehau 182*5c072c8eSSepherosa Ziehau /* 183*5c072c8eSSepherosa Ziehau * Receive request. 184*5c072c8eSSepherosa Ziehau */ 185*5c072c8eSSepherosa Ziehau data = sc->ic_buf; 186*5c072c8eSSepherosa Ziehau dlen = sc->ic_buflen; 187*5c072c8eSSepherosa Ziehau error = vmbus_chan_recv(chan, data, &dlen, &xactid); 188*5c072c8eSSepherosa Ziehau KASSERT(error != ENOBUFS, ("icbuf is not large enough")); 189*5c072c8eSSepherosa Ziehau if (error) 190*5c072c8eSSepherosa Ziehau return; 191*5c072c8eSSepherosa Ziehau 192*5c072c8eSSepherosa Ziehau if (dlen < sizeof(*hdr)) { 193*5c072c8eSSepherosa Ziehau device_printf(sc->ic_dev, "invalid data len %d\n", dlen); 194*5c072c8eSSepherosa Ziehau return; 195*5c072c8eSSepherosa Ziehau } 196*5c072c8eSSepherosa Ziehau hdr = data; 197*5c072c8eSSepherosa Ziehau 198*5c072c8eSSepherosa Ziehau /* 199*5c072c8eSSepherosa Ziehau * Update request, which will be echoed back as response. 200*5c072c8eSSepherosa Ziehau */ 201*5c072c8eSSepherosa Ziehau switch (hdr->ic_type) { 202*5c072c8eSSepherosa Ziehau case VMBUS_ICMSG_TYPE_NEGOTIATE: 203*5c072c8eSSepherosa Ziehau error = vmbus_ic_negomsg(sc, data, &dlen, 204*5c072c8eSSepherosa Ziehau VMBUS_TIMESYNC_FWVER, VMBUS_TIMESYNC_MSGVER); 205*5c072c8eSSepherosa Ziehau if (error) 206*5c072c8eSSepherosa Ziehau return; 207*5c072c8eSSepherosa Ziehau if (VMBUS_TIMESYNC_DORTT(sc)) 208*5c072c8eSSepherosa Ziehau device_printf(sc->ic_dev, "RTT\n"); 209*5c072c8eSSepherosa Ziehau break; 210*5c072c8eSSepherosa Ziehau 211*5c072c8eSSepherosa Ziehau case VMBUS_ICMSG_TYPE_TIMESYNC: 212*5c072c8eSSepherosa Ziehau if (dlen < sizeof(*msg)) { 213*5c072c8eSSepherosa Ziehau device_printf(sc->ic_dev, "invalid timesync len %d\n", 214*5c072c8eSSepherosa Ziehau dlen); 215*5c072c8eSSepherosa Ziehau return; 216*5c072c8eSSepherosa Ziehau } 217*5c072c8eSSepherosa Ziehau msg = data; 218*5c072c8eSSepherosa Ziehau vmbus_timesync(sc, msg->ic_hvtime, msg->ic_sent_tc, 219*5c072c8eSSepherosa Ziehau msg->ic_tsflags); 220*5c072c8eSSepherosa Ziehau break; 221*5c072c8eSSepherosa Ziehau 222*5c072c8eSSepherosa Ziehau default: 223*5c072c8eSSepherosa Ziehau device_printf(sc->ic_dev, "got 0x%08x icmsg\n", hdr->ic_type); 224*5c072c8eSSepherosa Ziehau break; 225*5c072c8eSSepherosa Ziehau } 226*5c072c8eSSepherosa Ziehau 227*5c072c8eSSepherosa Ziehau /* 228*5c072c8eSSepherosa Ziehau * Send response by echoing the request back. 229*5c072c8eSSepherosa Ziehau */ 230*5c072c8eSSepherosa Ziehau vmbus_ic_sendresp(sc, chan, data, dlen, xactid); 231*5c072c8eSSepherosa Ziehau } 232*5c072c8eSSepherosa Ziehau 233*5c072c8eSSepherosa Ziehau static int 234*5c072c8eSSepherosa Ziehau vmbus_timesync_probe(device_t dev) 235*5c072c8eSSepherosa Ziehau { 236*5c072c8eSSepherosa Ziehau 237*5c072c8eSSepherosa Ziehau return (vmbus_ic_probe(dev, vmbus_timesync_descs)); 238*5c072c8eSSepherosa Ziehau } 239*5c072c8eSSepherosa Ziehau 240*5c072c8eSSepherosa Ziehau static int 241*5c072c8eSSepherosa Ziehau vmbus_timesync_attach(device_t dev) 242*5c072c8eSSepherosa Ziehau { 243*5c072c8eSSepherosa Ziehau 244*5c072c8eSSepherosa Ziehau return (vmbus_ic_attach(dev, vmbus_timesync_cb)); 245*5c072c8eSSepherosa Ziehau } 246