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