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