1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/systm.h>
  29 #include <sys/mutex.h>
  30 #include <sys/time.h>
  31 #include <sys/clock.h>
  32 #include <sys/archsystm.h>
  33 #include <sys/modctl.h>
  34 #include <sys/autoconf.h>
  35 #include <sys/cmn_err.h>
  36 #include <sys/atomic.h>
  37 #include <sys/hypervisor.h>
  38 
  39 /*
  40  * tod driver module for i86xpv
  41  */
  42 
  43 static timestruc_t
  44 todxen_get(tod_ops_t *top)
  45 {
  46         todinfo_t tod;
  47         timestruc_t ts, wcts;
  48         shared_info_t *si = HYPERVISOR_shared_info;
  49         uint32_t xen_wc_version;
  50         hrtime_t now;
  51 
  52         ASSERT(MUTEX_HELD(&tod_lock));
  53 
  54         /*
  55          * Pick up the wallclock base time
  56          */
  57         do {
  58                 xen_wc_version = si->wc_version;
  59 
  60                 membar_consumer();
  61 
  62                 wcts.tv_sec = si->wc_sec;
  63                 wcts.tv_nsec = si->wc_nsec;
  64 
  65                 membar_consumer();
  66 
  67         } while ((si->wc_version & 1) | (xen_wc_version ^ si->wc_version));
  68 
  69         /*
  70          * Compute the TOD as the wallclock (boot) time plus time-since-boot
  71          * (/not/ hrtime!) and normalize.
  72          */
  73         now = xpv_getsystime() +
  74             (hrtime_t)wcts.tv_nsec + (hrtime_t)wcts.tv_sec * NANOSEC;
  75         ts.tv_sec = (time_t)(now / NANOSEC);
  76         ts.tv_nsec = (long)(now % NANOSEC);
  77 
  78         /*
  79          * Apply GMT lag correction from /etc/rtc_config to get UTC time
  80          */
  81         ts.tv_sec += ggmtl();
  82 
  83         /*
  84          * Validate the TOD in case of total insanity
  85          */
  86         tod = utc_to_tod(ts.tv_sec);
  87         if (tod.tod_year < 69) {
  88                 static int range_warn = 1;      /* warn only once */
  89 
  90                 if (range_warn) {
  91                         /*
  92                          * If we're dom0, go invoke the underlying driver; the
  93                          * routine should complain if it discovers something
  94                          * wrong.
  95                          */
  96                         if (DOMAIN_IS_INITDOMAIN(xen_info))
  97                                 (void) TODOP_GET(top->tod_next);
  98 
  99                         /*
 100                          * Check the virtual hardware.
 101                          */
 102                         if (tod.tod_year > 38)
 103                                 cmn_err(CE_WARN,
 104                                     "hypervisor wall clock is out "
 105                                     "of range -- time needs to be reset");
 106                         range_warn = 0;
 107                 }
 108                 tod.tod_year += 100;
 109                 ts.tv_sec = tod_to_utc(tod);
 110         }
 111 
 112         return (ts);
 113 }
 114 
 115 /*
 116  * On dom0, invoke the underlying driver to update the physical RTC,
 117  * and tell the hypervisor to update its idea of global time.
 118  *
 119  * On domU, we don't have permission to update the machine's physical RTC,
 120  * so quietly ignore the attempt.
 121  */
 122 static void
 123 todxen_set(tod_ops_t *top, timestruc_t ts)
 124 {
 125         xen_platform_op_t op;
 126 
 127         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 128                 ASSERT(MUTEX_HELD(&tod_lock));
 129                 TODOP_SET(top->tod_next, ts);
 130 
 131                 op.cmd = XENPF_settime;
 132                 op.interface_version = XENPF_INTERFACE_VERSION;
 133                 op.u.settime.secs = ts.tv_sec - ggmtl();
 134                 op.u.settime.nsecs = ts.tv_nsec;
 135                 op.u.settime.system_time = xpv_getsystime();
 136                 (void) HYPERVISOR_platform_op(&op);
 137         }
 138 }
 139 
 140 static tod_ops_t todxen_ops = {
 141         TOD_OPS_VERSION,
 142         todxen_get,
 143         todxen_set,
 144         NULL
 145 };
 146 
 147 static struct modlmisc modlmisc = {
 148         &mod_miscops,
 149         "TOD module for i86xpv"
 150 };
 151 
 152 static struct modlinkage modl = {
 153         MODREV_1,
 154         { &modlmisc, NULL }
 155 };
 156 
 157 int
 158 _init(void)
 159 {
 160         /*
 161          * In future we might need to do something more sophisticated
 162          * for versioning, i.e. dealing with older hardware TOD modules
 163          * underneath us, but for now, anything else but "same" is a
 164          * fatal error.
 165          */
 166         if (tod_ops->tod_version != todxen_ops.tod_version)
 167                 panic("TOD module version mismatch");
 168 
 169         /*
 170          * Stitch the ops vector linkage together, with this module
 171          * sitting on the "front" of the ops list.
 172          */
 173         todxen_ops.tod_next = tod_ops;
 174         tod_ops = &todxen_ops;
 175 
 176         return (mod_install(&modl));
 177 }
 178 
 179 int
 180 _fini(void)
 181 {
 182         return (EBUSY);
 183 }
 184 
 185 int
 186 _info(struct modinfo *modinfo)
 187 {
 188         return (mod_info(&modl, modinfo));
 189 }