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