1 /*
2  * Copyright (c) 2006-2022 Ali Mashtizadeh
3  * All rights reserved.
4  */
5 
6 #include <stdbool.h>
7 #include <stdint.h>
8 
9 #include <sys/kassert.h>
10 #include <sys/kdebug.h>
11 #include <sys/ktime.h>
12 #include <sys/spinlock.h>
13 
14 static Spinlock ktimeLock;
15 static uint64_t ktimeLastEpoch;
16 static uint64_t ktimeLastTSC;
17 uint64_t ticksPerSecond;
18 
19 static const char *dayOfWeek[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
20 static const char *months[12] = {
21     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
22     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
23 };
24 
25 void
KTime_Init()26 KTime_Init()
27 {
28     Spinlock_Init(&ktimeLock, "KTime Lock", SPINLOCK_TYPE_NORMAL);
29     ktimeLastEpoch = 0;
30     ktimeLastTSC = 0;
31     ticksPerSecond = 0;
32 }
33 
34 static bool
KTimeIsLeapYear(uint64_t year)35 KTimeIsLeapYear(uint64_t year)
36 {
37     if ((year % 4) != 0)
38 	return false;
39     if ((year % 100) != 0)
40 	return true;
41     if ((year % 400) != 0)
42 	return false;
43     return true;
44 }
45 
46 static int
KTimeDaysInMonth(uint64_t year,uint64_t month)47 KTimeDaysInMonth(uint64_t year, uint64_t month)
48 {
49     static const uint64_t days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
50 
51     if ((month == 2) && KTimeIsLeapYear(year))
52 	return 29;
53     else
54 	return days[month];
55 }
56 
57 /*
58  * This function recomputes yday, mday or wday given other fields
59  */
60 void
KTime_Fixup(KTime * tm)61 KTime_Fixup(KTime *tm)
62 {
63     uint64_t m;
64 
65     if (tm->yday == -1) {
66 	uint64_t yday = 0;
67 	for (m = 0; m < tm->month; m++) {
68 	    yday += KTimeDaysInMonth(tm->year, m);
69 	}
70 	yday += tm->mday;
71 	tm->yday = yday;
72     }
73 }
74 
75 UnixEpoch
KTime_ToEpoch(const KTime * tm)76 KTime_ToEpoch(const KTime *tm)
77 {
78     uint64_t days = 0;
79     uint64_t secs = 0;
80     uint64_t y, m;
81 
82     // Convert to UNIX epoch
83     for (y = 1970; y < tm->year; y++) {
84 	if (KTimeIsLeapYear(y))
85 	    days += 366;
86 	else
87 	    days += 365;
88     }
89 
90     if (tm->yday == -1) {
91 	uint64_t yday = 0;
92 	for (m = 0; m < tm->month; m++) {
93 	    yday += KTimeDaysInMonth(tm->year, m);
94 	}
95 	yday += tm->mday;
96 	days += yday;
97     } else {
98 	days += tm->yday;
99     }
100 
101     secs = 24 * days + tm->hour;
102     secs = secs * 60 + tm->min;
103     secs = secs * 60 + tm->sec;
104 
105     return secs;
106 }
107 
108 void
KTime_FromEpoch(UnixEpoch epoch,KTime * tm)109 KTime_FromEpoch(UnixEpoch epoch, KTime *tm)
110 {
111     uint64_t secs, mins, hours;
112     uint64_t days;
113     uint64_t y, m;
114 
115     // Compute seconds
116     secs = epoch % (60 * 60 * 24);
117     days = epoch / (60 * 60 * 24);
118     mins = secs / 60;
119     secs = secs % 60;
120 
121     // Compute minutes
122     hours = mins / 60;
123     mins = mins % 60;
124 
125     // Compute hours
126     hours = hours % 24;
127 
128     tm->sec = secs;
129     tm->min = mins;
130     tm->hour = hours;
131 
132     tm->wday = (days + 3) % 7;
133 
134     for (y = 1970; ; y++) {
135 	uint64_t daysOfYear;
136 	if (KTimeIsLeapYear(y)) {
137 	    daysOfYear = 366;
138 	} else {
139 	    daysOfYear = 365;
140 	}
141 
142 	if (days < daysOfYear) {
143 	    tm->yday = days;
144 	    tm->year = y;
145 	    break;
146 	}
147 	days -= daysOfYear;
148     }
149 
150     for (m = 0; ; m++) {
151 	uint64_t daysOfMonth = KTimeDaysInMonth(tm->year, m);
152 
153 	if (days < daysOfMonth) {
154 	    tm->mday = days;
155 	    tm->month = m;
156 	    break;
157 	}
158 	days -= daysOfMonth;
159     }
160 }
161 
162 void
KTime_SetTime(UnixEpoch epoch,uint64_t tsc,uint64_t tps)163 KTime_SetTime(UnixEpoch epoch, uint64_t tsc, uint64_t tps)
164 {
165     Spinlock_Lock(&ktimeLock);
166     ktimeLastEpoch = epoch;
167     ktimeLastTSC = tsc;
168     ticksPerSecond = tps;
169     Spinlock_Unlock(&ktimeLock);
170 
171 }
172 
173 void
KTime_GetTime(KTime * tm)174 KTime_GetTime(KTime *tm)
175 {
176     KTime_FromEpoch(KTime_GetEpoch(), tm);
177 }
178 
179 UnixEpoch
KTime_GetEpoch()180 KTime_GetEpoch()
181 {
182     uint64_t tscDiff;
183     uint64_t epoch;
184 
185     Spinlock_Lock(&ktimeLock);
186     tscDiff = Time_GetTSC() - ktimeLastTSC;
187     epoch = ktimeLastEpoch + tscDiff / ticksPerSecond;
188     Spinlock_Unlock(&ktimeLock);
189 
190     return epoch;
191 }
192 
193 UnixEpochNS
KTime_GetEpochNS()194 KTime_GetEpochNS()
195 {
196     uint64_t tscDiff;
197     uint64_t epoch;
198 
199     Spinlock_Lock(&ktimeLock);
200     tscDiff = Time_GetTSC() - ktimeLastTSC;
201     /*
202      * This is ugly but it avoids overflowing tscDiff to time computation.
203      * Note that the bottom bits of ticksPerSecond are not significant so it is
204      * okay to discard them.
205      */
206     epoch = (ktimeLastEpoch * 1000000000ULL) + (tscDiff * 1000000ULL / ticksPerSecond * 1000ULL);
207     Spinlock_Unlock(&ktimeLock);
208 
209     return epoch;
210 }
211 
212 static void
Debug_Date()213 Debug_Date()
214 {
215     UnixEpoch epoch = KTime_GetEpoch();
216     KTime tm;
217 
218     KTime_FromEpoch(epoch, &tm);
219 
220     kprintf("%s %s %d %02d:%02d:%02d %04d\n",
221 	    dayOfWeek[tm.wday], months[tm.month],
222 	    tm.mday, tm.hour, tm.min, tm.sec, tm.year);
223 
224     kprintf("Epoch: %lu\n", epoch);
225 }
226 
227 REGISTER_DBGCMD(date, "Print date", Debug_Date);
228 
229 static void
Debug_Ticks()230 Debug_Ticks()
231 {
232     kprintf("Ticks Per Second: %lu\n", ticksPerSecond);
233 }
234 
235 REGISTER_DBGCMD(ticks, "Print ticks per second", Debug_Ticks);
236 
237