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