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 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <sys/types.h>
  30 #include <sys/systm.h>
  31 #include <sys/mutex.h>
  32 #include <sys/time.h>
  33 #include <sys/clock.h>
  34 #include <sys/archsystm.h>
  35 #include <sys/modctl.h>
  36 #include <sys/autoconf.h>
  37 #include <sys/cmn_err.h>
  38 #include <sys/atomic.h>
  39 #include <sys/hypervisor.h>
  40 
  41 /*
  42  * tod driver module for i86xpv
  43  */
  44 
  45 static timestruc_t
  46 todxen_get(tod_ops_t *top)
  47 {
  48         todinfo_t tod;
  49         timestruc_t ts, wcts;
  50         shared_info_t *si = HYPERVISOR_shared_info;
  51         uint32_t xen_wc_version;
  52         hrtime_t now;
  53 
  54         ASSERT(MUTEX_HELD(&tod_lock));
  55 
  56         /*
  57          * Pick up the wallclock base time
  58          */
  59         do {
  60                 xen_wc_version = si->wc_version;
  61 
  62                 membar_consumer();
  63 
  64                 wcts.tv_sec = si->wc_sec;
  65                 wcts.tv_nsec = si->wc_nsec;
  66 
  67                 membar_consumer();
  68 
  69         } while ((si->wc_version & 1) | (xen_wc_version ^ si->wc_version));
  70 
  71         /*
  72          * Compute the TOD as the wallclock (boot) time plus time-since-boot
  73          * (/not/ hrtime!) and normalize.
  74          */
  75         now = xpv_getsystime() +
  76             (hrtime_t)wcts.tv_nsec + (hrtime_t)wcts.tv_sec * NANOSEC;
  77         ts.tv_sec = (time_t)(now / NANOSEC);
  78         ts.tv_nsec = (long)(now % NANOSEC);
  79 
  80         /*
  81          * Apply GMT lag correction from /etc/rtc_config to get UTC time
  82          */
  83         ts.tv_sec += ggmtl();
  84 
  85         /*
  86          * Validate the TOD in case of total insanity
  87          */
  88         tod = utc_to_tod(ts.tv_sec);
  89         if (tod.tod_year < 69) {
  90                 static int range_warn = 1;      /* warn only once */
  91 
  92                 if (range_warn) {
  93                         /*
  94                          * If we're dom0, go invoke the underlying driver; the
  95                          * routine should complain if it discovers something
  96                          * wrong.
  97                          */
  98                         if (DOMAIN_IS_INITDOMAIN(xen_info))
  99                                 (void) TODOP_GET(top->tod_next);
 100 
 101                         /*
 102                          * Check the virtual hardware.
 103                          */
 104                         if (tod.tod_year > 38)
 105                                 cmn_err(CE_WARN,
 106                                     "hypervisor wall clock is out "
 107                                     "of range -- time needs to be reset");
 108                         range_warn = 0;
 109                 }
 110                 tod.tod_year += 100;
 111                 ts.tv_sec = tod_to_utc(tod);
 112         }
 113 
 114         return (ts);
 115 }
 116 
 117 /*
 118  * On dom0, invoke the underlying driver to update the physical RTC,
 119  * and tell the hypervisor to update its idea of global time.
 120  *
 121  * On domU, we don't have permission to update the machine's physical RTC,
 122  * so quietly ignore the attempt.
 123  */
 124 static void
 125 todxen_set(tod_ops_t *top, timestruc_t ts)
 126 {
 127         xen_platform_op_t op;
 128 
 129         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 130                 ASSERT(MUTEX_HELD(&tod_lock));
 131                 TODOP_SET(top->tod_next, ts);
 132 
 133                 op.cmd = XENPF_settime;
 134                 op.interface_version = XENPF_INTERFACE_VERSION;
 135                 op.u.settime.secs = ts.tv_sec - ggmtl();
 136                 op.u.settime.nsecs = ts.tv_nsec;
 137                 op.u.settime.system_time = xpv_getsystime();
 138                 (void) HYPERVISOR_platform_op(&op);
 139         }
 140 }
 141 
 142 static tod_ops_t todxen_ops = {
 143         TOD_OPS_VERSION,
 144         todxen_get,
 145         todxen_set,
 146         NULL
 147 };
 148 
 149 static struct modlmisc modlmisc = {
 150         &mod_miscops,
 151         "TOD module for i86xpv"
 152 };
 153 
 154 static struct modlinkage modl = {
 155         MODREV_1,
 156         &modlmisc
 157 };
 158 
 159 int
 160 _init(void)
 161 {
 162         /*
 163          * In future we might need to do something more sophisticated
 164          * for versioning, i.e. dealing with older hardware TOD modules
 165          * underneath us, but for now, anything else but "same" is a
 166          * fatal error.
 167          */
 168         if (tod_ops->tod_version != todxen_ops.tod_version)
 169                 panic("TOD module version mismatch");
 170 
 171         /*
 172          * Stitch the ops vector linkage together, with this module
 173          * sitting on the "front" of the ops list.
 174          */
 175         todxen_ops.tod_next = tod_ops;
 176         tod_ops = &todxen_ops;
 177 
 178         return (mod_install(&modl));
 179 }
 180 
 181 int
 182 _fini(void)
 183 {
 184         return (EBUSY);
 185 }
 186 
 187 int
 188 _info(struct modinfo *modinfo)
 189 {
 190         return (mod_info(&modl, modinfo));
 191 }