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 } 192