1 /******************************************************************************
2 * time.c
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <xen/sched.h>
19 #include <xen/shared.h>
20 #include <xen/spinlock.h>
21 #include <xen/time.h>
22 #include <asm/div64.h>
23 #include <asm/domain.h>
24
25 /* Nonzero if YEAR is a leap year (every 4 years,
26 except every 100th isn't, and every 400th is). */
27 #define __isleap(year) \
28 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
29
30 /* How many days are in each month. */
31 const unsigned short int __mon_lengths[2][12] = {
32 /* Normal years. */
33 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
34 /* Leap years. */
35 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
36 };
37
38 #define SECS_PER_HOUR (60 * 60)
39 #define SECS_PER_DAY (SECS_PER_HOUR * 24)
40
41 static uint64_t wc_sec; /* UTC time at last 'time update'. */
42 static unsigned int wc_nsec;
43 static DEFINE_SPINLOCK(wc_lock);
44
gmtime(unsigned long t)45 struct tm gmtime(unsigned long t)
46 {
47 struct tm tbuf;
48 long days, rem;
49 int y;
50 const unsigned short int *ip;
51
52 y = 1970;
53 #if BITS_PER_LONG >= 64
54 /* Allow the concept of time before 1970. 64-bit only; for 32-bit
55 * time after 2038 seems more important than time before 1970. */
56 while ( t & (1UL<<39) )
57 {
58 y -= 400;
59 t += ((unsigned long)(365 * 303 + 366 * 97)) * SECS_PER_DAY;
60 }
61 t &= (1UL << 40) - 1;
62 #endif
63
64 days = t / SECS_PER_DAY;
65 rem = t % SECS_PER_DAY;
66
67 tbuf.tm_hour = rem / SECS_PER_HOUR;
68 rem %= SECS_PER_HOUR;
69 tbuf.tm_min = rem / 60;
70 tbuf.tm_sec = rem % 60;
71 /* January 1, 1970 was a Thursday. */
72 tbuf.tm_wday = (4 + days) % 7;
73 if ( tbuf.tm_wday < 0 )
74 tbuf.tm_wday += 7;
75 while ( days >= (rem = __isleap(y) ? 366 : 365) )
76 {
77 ++y;
78 days -= rem;
79 }
80 while ( days < 0 )
81 {
82 --y;
83 days += __isleap(y) ? 366 : 365;
84 }
85 tbuf.tm_year = y - 1900;
86 tbuf.tm_yday = days;
87 ip = (const unsigned short int *)__mon_lengths[__isleap(y)];
88 for ( y = 0; days >= ip[y]; ++y )
89 days -= ip[y];
90 tbuf.tm_mon = y;
91 tbuf.tm_mday = days + 1;
92 tbuf.tm_isdst = -1;
93
94 return tbuf;
95 }
96
update_domain_wallclock_time(struct domain * d)97 void update_domain_wallclock_time(struct domain *d)
98 {
99 uint32_t *wc_version;
100 uint64_t sec;
101
102 spin_lock(&wc_lock);
103
104 wc_version = &shared_info(d, wc_version);
105 *wc_version = version_update_begin(*wc_version);
106 smp_wmb();
107
108 sec = wc_sec + d->time_offset.seconds;
109 shared_info(d, wc_sec) = sec;
110 shared_info(d, wc_nsec) = wc_nsec;
111 #ifdef CONFIG_X86
112 if ( likely(!has_32bit_shinfo(d)) )
113 d->shared_info->native.wc_sec_hi = sec >> 32;
114 else
115 d->shared_info->compat.arch.wc_sec_hi = sec >> 32;
116 #else
117 shared_info(d, wc_sec_hi) = sec >> 32;
118 #endif
119
120 smp_wmb();
121 *wc_version = version_update_end(*wc_version);
122
123 spin_unlock(&wc_lock);
124 }
125
126 /* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */
do_settime(u64 secs,unsigned int nsecs,u64 system_time_base)127 void do_settime(u64 secs, unsigned int nsecs, u64 system_time_base)
128 {
129 u64 x;
130 u32 y;
131 struct domain *d;
132
133 x = SECONDS(secs) + nsecs - system_time_base;
134 y = do_div(x, 1000000000);
135
136 spin_lock(&wc_lock);
137 wc_sec = x;
138 wc_nsec = y;
139 spin_unlock(&wc_lock);
140
141 rcu_read_lock(&domlist_read_lock);
142 for_each_domain ( d )
143 update_domain_wallclock_time(d);
144 rcu_read_unlock(&domlist_read_lock);
145 }
146
147 /* Return secs after 00:00:00 localtime, 1 January, 1970. */
get_localtime(struct domain * d)148 unsigned long get_localtime(struct domain *d)
149 {
150 return wc_sec + (wc_nsec + NOW()) / 1000000000ULL
151 + d->time_offset.seconds;
152 }
153
154 /* Return microsecs after 00:00:00 localtime, 1 January, 1970. */
get_localtime_us(struct domain * d)155 uint64_t get_localtime_us(struct domain *d)
156 {
157 return (SECONDS(wc_sec + d->time_offset.seconds) + wc_nsec + NOW())
158 / 1000UL;
159 }
160
get_sec(void)161 unsigned long get_sec(void)
162 {
163 return wc_sec + (wc_nsec + NOW()) / 1000000000ULL;
164 }
165
wallclock_time(uint64_t * ns)166 struct tm wallclock_time(uint64_t *ns)
167 {
168 uint64_t seconds, nsec;
169
170 if ( !wc_sec )
171 return (struct tm) { 0 };
172
173 seconds = NOW() + SECONDS(wc_sec) + wc_nsec;
174 nsec = do_div(seconds, 1000000000);
175
176 if ( ns )
177 *ns = nsec;
178
179 return gmtime(seconds);
180 }
181