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 static 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 /* SAF-14-safe use boolean as an array index */
88 ip = __mon_lengths[__isleap(y)];
89 for ( y = 0; days >= ip[y]; ++y )
90 days -= ip[y];
91 tbuf.tm_mon = y;
92 tbuf.tm_mday = days + 1;
93 tbuf.tm_isdst = -1;
94
95 return tbuf;
96 }
97
update_domain_wallclock_time(struct domain * d)98 void update_domain_wallclock_time(struct domain *d)
99 {
100 uint32_t *wc_version;
101 uint64_t sec;
102
103 spin_lock(&wc_lock);
104
105 wc_version = &shared_info(d, wc_version);
106 *wc_version = version_update_begin(*wc_version);
107 smp_wmb();
108
109 sec = wc_sec + d->time_offset.seconds;
110 shared_info(d, wc_sec) = sec;
111 shared_info(d, wc_nsec) = wc_nsec;
112 #if defined(CONFIG_X86) && defined(CONFIG_COMPAT)
113 if ( likely(!has_32bit_shinfo(d)) )
114 d->shared_info->native.wc_sec_hi = sec >> 32;
115 else
116 d->shared_info->compat.arch.wc_sec_hi = sec >> 32;
117 #else
118 shared_info(d, wc_sec_hi) = sec >> 32;
119 #endif
120
121 smp_wmb();
122 *wc_version = version_update_end(*wc_version);
123
124 spin_unlock(&wc_lock);
125 }
126
127 /* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */
do_settime(u64 secs,unsigned int nsecs,u64 system_time_base)128 void do_settime(u64 secs, unsigned int nsecs, u64 system_time_base)
129 {
130 u64 x;
131 u32 y;
132 struct domain *d;
133
134 x = SECONDS(secs) + nsecs - system_time_base;
135 y = do_div(x, 1000000000);
136
137 spin_lock(&wc_lock);
138 wc_sec = x;
139 wc_nsec = y;
140 spin_unlock(&wc_lock);
141
142 rcu_read_lock(&domlist_read_lock);
143 for_each_domain ( d )
144 update_domain_wallclock_time(d);
145 rcu_read_unlock(&domlist_read_lock);
146 }
147
148 /* Return secs after 00:00:00 localtime, 1 January, 1970. */
get_localtime(struct domain * d)149 unsigned long get_localtime(struct domain *d)
150 {
151 return wc_sec + (wc_nsec + NOW()) / 1000000000ULL
152 + d->time_offset.seconds;
153 }
154
155 /* Return microsecs after 00:00:00 localtime, 1 January, 1970. */
get_localtime_us(struct domain * d)156 uint64_t get_localtime_us(struct domain *d)
157 {
158 return (SECONDS(wc_sec + d->time_offset.seconds) + wc_nsec + NOW())
159 / 1000UL;
160 }
161
get_sec(void)162 unsigned long get_sec(void)
163 {
164 return wc_sec + (wc_nsec + NOW()) / 1000000000ULL;
165 }
166
wallclock_time(uint64_t * ns)167 struct tm wallclock_time(uint64_t *ns)
168 {
169 uint64_t seconds, nsec;
170
171 if ( !wc_sec )
172 return (struct tm) { 0 };
173
174 seconds = NOW() + SECONDS(wc_sec) + wc_nsec;
175 nsec = do_div(seconds, 1000000000);
176
177 if ( ns )
178 *ns = nsec;
179
180 return gmtime(seconds);
181 }
182