xref: /illumos-gate/usr/src/uts/i86xpv/io/xpvtod.c (revision 843e1988)
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
todxen_get(tod_ops_t * top)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
todxen_set(tod_ops_t * top,timestruc_t ts)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
_init(void)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
_fini(void)182 _fini(void)
183 {
184 	return (EBUSY);
185 }
186 
187 int
_info(struct modinfo * modinfo)188 _info(struct modinfo *modinfo)
189 {
190 	return (mod_info(&modl, modinfo));
191 }
192