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
todxen_get(tod_ops_t * top)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
todxen_set(tod_ops_t * top,timestruc_t ts)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
_init(void)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
_fini(void)180 _fini(void)
181 {
182 return (EBUSY);
183 }
184
185 int
_info(struct modinfo * modinfo)186 _info(struct modinfo *modinfo)
187 {
188 return (mod_info(&modl, modinfo));
189 }
190