1 /* 2 * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Copyright 2003, Jeff Ward, jeff@r2d2.stcloudstate.edu. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include <KernelExport.h> 10 11 #include <arch/real_time_clock.h> 12 #include <commpage.h> 13 #include <real_time_clock.h> 14 #include <real_time_data.h> 15 #include <syscalls.h> 16 #include <thread.h> 17 18 #include <stdlib.h> 19 20 //#define TRACE_TIME 21 #ifdef TRACE_TIME 22 # define TRACE(x) dprintf x 23 #else 24 # define TRACE(x) 25 #endif 26 27 28 #define RTC_SECONDS_DAY 86400 29 #define RTC_EPOCH_JULIAN_DAY 2440588 30 // January 1st, 1970 31 32 static struct real_time_data *sRealTimeData; 33 static bool sIsGMT = false; 34 static char sTimezoneFilename[B_PATH_NAME_LENGTH] = ""; 35 static bigtime_t sTimezoneOffset = 0; 36 static bool sDaylightSavingTime = false; 37 38 39 /*! Write the system time to CMOS. */ 40 static void 41 rtc_system_to_hw(void) 42 { 43 uint32 seconds; 44 45 seconds = (arch_rtc_get_system_time_offset(sRealTimeData) + system_time() 46 + (sIsGMT ? 0 : sTimezoneOffset)) / 1000000; 47 48 arch_rtc_set_hw_time(seconds); 49 } 50 51 52 /*! Read the CMOS clock and update the system time accordingly. */ 53 static void 54 rtc_hw_to_system(void) 55 { 56 uint32 current_time; 57 58 current_time = arch_rtc_get_hw_time(); 59 set_real_time_clock(current_time + (sIsGMT ? 0 : sTimezoneOffset)); 60 } 61 62 63 bigtime_t 64 rtc_boot_time(void) 65 { 66 return arch_rtc_get_system_time_offset(sRealTimeData); 67 } 68 69 70 static int 71 rtc_debug(int argc, char **argv) 72 { 73 if (argc < 2) { 74 // If no arguments were given, output all useful data. 75 uint32 currentTime; 76 bigtime_t systemTimeOffset 77 = arch_rtc_get_system_time_offset(sRealTimeData); 78 79 currentTime = (systemTimeOffset + system_time()) / 1000000; 80 dprintf("system_time: %Ld\n", system_time()); 81 dprintf("system_time_offset: %Ld\n", systemTimeOffset); 82 dprintf("current_time: %lu\n", currentTime); 83 } else { 84 // If there was an argument, reset the system and hw time. 85 set_real_time_clock(strtoul(argv[1], NULL, 10)); 86 } 87 88 return 0; 89 } 90 91 92 status_t 93 rtc_init(kernel_args *args) 94 { 95 sRealTimeData = (struct real_time_data*)allocate_commpage_entry( 96 COMMPAGE_ENTRY_REAL_TIME_DATA, sizeof(struct real_time_data)); 97 98 arch_rtc_init(args, sRealTimeData); 99 rtc_hw_to_system(); 100 101 add_debugger_command("rtc", &rtc_debug, "Set and test the real-time clock"); 102 return B_OK; 103 } 104 105 106 // #pragma mark - public kernel API 107 108 109 void 110 set_real_time_clock(uint32 currentTime) 111 { 112 arch_rtc_set_system_time_offset(sRealTimeData, 113 currentTime * 1000000LL - system_time()); 114 rtc_system_to_hw(); 115 } 116 117 118 uint32 119 real_time_clock(void) 120 { 121 return (arch_rtc_get_system_time_offset(sRealTimeData) + system_time()) 122 / 1000000; 123 } 124 125 126 bigtime_t 127 real_time_clock_usecs(void) 128 { 129 return arch_rtc_get_system_time_offset(sRealTimeData) + system_time(); 130 } 131 132 133 status_t 134 get_rtc_info(rtc_info *info) 135 { 136 if (info == NULL) 137 return B_BAD_VALUE; 138 139 info->time = real_time_clock(); 140 info->is_gmt = sIsGMT; 141 info->tz_minuteswest = sTimezoneOffset / 1000000LL; 142 info->tz_dsttime = sDaylightSavingTime; 143 144 return B_OK; 145 } 146 147 148 // #pragma mark - 149 150 151 /*! Converts the \a tm data to seconds. Note that the base year is not 152 1900 as in POSIX, but 1970. 153 */ 154 uint32 155 rtc_tm_to_secs(const struct tm *tm) 156 { 157 uint32 days; 158 int year, month; 159 160 month = tm->tm_mon + 1; 161 year = tm->tm_year + RTC_EPOCH_BASE_YEAR; 162 163 // Reference: Fliegel, H. F. and van Flandern, T. C. (1968). 164 // Communications of the ACM, Vol. 11, No. 10 (October, 1968). 165 days = tm->tm_mday - 32075 - RTC_EPOCH_JULIAN_DAY 166 + 1461 * (year + 4800 + (month - 14) / 12) / 4 167 + 367 * (month - 2 - 12 * ((month - 14) / 12)) / 12 168 - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4; 169 170 return days * RTC_SECONDS_DAY + tm->tm_hour * 3600 + tm->tm_min * 60 171 + tm->tm_sec; 172 } 173 174 175 void 176 rtc_secs_to_tm(uint32 seconds, struct tm *t) 177 { 178 uint32 year, month, day, l, n; 179 180 // Reference: Fliegel, H. F. and van Flandern, T. C. (1968). 181 // Communications of the ACM, Vol. 11, No. 10 (October, 1968). 182 l = seconds / 86400 + 68569 + RTC_EPOCH_JULIAN_DAY; 183 n = 4 * l / 146097; 184 l = l - (146097 * n + 3) / 4; 185 year = 4000 * (l + 1) / 1461001; 186 l = l - 1461 * year / 4 + 31; 187 month = 80 * l / 2447; 188 day = l - 2447 * month / 80; 189 l = month / 11; 190 month = month + 2 - 12 * l; 191 year = 100 * (n - 49) + year + l; 192 193 t->tm_mday = day; 194 t->tm_mon = month - 1; 195 t->tm_year = year - RTC_EPOCH_BASE_YEAR; 196 197 seconds = seconds % RTC_SECONDS_DAY; 198 t->tm_hour = seconds / 3600; 199 200 seconds = seconds % 3600; 201 t->tm_min = seconds / 60; 202 t->tm_sec = seconds % 60; 203 } 204 205 206 // #pragma mark - 207 208 209 /*! This is called from the gettimeofday() implementation that's part of the 210 kernel. 211 */ 212 status_t 213 _kern_get_timezone(time_t *_timezoneOffset, bool *_daylightSavingTime) 214 { 215 *_timezoneOffset = (time_t)(sTimezoneOffset / 1000000LL); 216 *_daylightSavingTime = sDaylightSavingTime; 217 218 return B_OK; 219 } 220 221 222 // #pragma mark - syscalls 223 224 225 bigtime_t 226 _user_system_time(void) 227 { 228 syscall_64_bit_return_value(); 229 230 return system_time(); 231 } 232 233 234 status_t 235 _user_set_real_time_clock(uint32 time) 236 { 237 if (geteuid() != 0) 238 return B_NOT_ALLOWED; 239 240 set_real_time_clock(time); 241 return B_OK; 242 } 243 244 245 status_t 246 _user_set_timezone(time_t timezoneOffset, bool daylightSavingTime) 247 { 248 bigtime_t offset = (bigtime_t)timezoneOffset * 1000000LL; 249 250 if (geteuid() != 0) 251 return B_NOT_ALLOWED; 252 253 TRACE(("old system_time_offset %Ld old %Ld new %Ld gmt %d\n", 254 arch_rtc_get_system_time_offset(sRealTimeData), sTimezoneOffset, 255 offset, sIsGMT)); 256 257 // We only need to update our time offset if the hardware clock 258 // does not run in the local timezone. 259 // Since this is shared data, we need to update it atomically. 260 if (!sIsGMT) { 261 arch_rtc_set_system_time_offset(sRealTimeData, 262 arch_rtc_get_system_time_offset(sRealTimeData) + sTimezoneOffset 263 - offset); 264 } 265 266 sTimezoneOffset = offset; 267 sDaylightSavingTime = daylightSavingTime; 268 269 TRACE(("new system_time_offset %Ld\n", 270 arch_rtc_get_system_time_offset(sRealTimeData))); 271 272 return B_OK; 273 } 274 275 276 status_t 277 _user_get_timezone(time_t *_timezoneOffset, bool *_daylightSavingTime) 278 { 279 time_t offset = (time_t)(sTimezoneOffset / 1000000LL); 280 281 if (!IS_USER_ADDRESS(_timezoneOffset) 282 || !IS_USER_ADDRESS(_daylightSavingTime) 283 || user_memcpy(_timezoneOffset, &offset, sizeof(time_t)) < B_OK 284 || user_memcpy(_daylightSavingTime, &sDaylightSavingTime, 285 sizeof(bool)) < B_OK) 286 return B_BAD_ADDRESS; 287 288 return B_OK; 289 } 290 291 292 status_t 293 _user_set_tzfilename(const char *filename, size_t length, bool isGMT) 294 { 295 // store previous value 296 bool wasGMT = sIsGMT; 297 if (geteuid() != 0) 298 return B_NOT_ALLOWED; 299 300 if (filename != NULL && length > 0) { 301 if (!IS_USER_ADDRESS(filename) 302 || user_strlcpy(sTimezoneFilename, filename, B_PATH_NAME_LENGTH) < 0) 303 return B_BAD_ADDRESS; 304 } 305 306 sIsGMT = isGMT; 307 308 if (wasGMT != sIsGMT) { 309 arch_rtc_set_system_time_offset(sRealTimeData, 310 arch_rtc_get_system_time_offset(sRealTimeData) 311 + (sIsGMT ? 1 : -1) * sTimezoneOffset); 312 } 313 314 return B_OK; 315 } 316 317 318 status_t 319 _user_get_tzfilename(char *userFilename, size_t length, bool *_userIsGMT) 320 { 321 if ((userFilename == NULL || length == 0) && _userIsGMT == NULL) 322 return B_BAD_VALUE; 323 324 if (userFilename != NULL 325 && (!IS_USER_ADDRESS(userFilename) 326 || user_strlcpy(userFilename, sTimezoneFilename, length) < 0)) 327 return B_BAD_ADDRESS; 328 329 if (_userIsGMT != NULL 330 && (!IS_USER_ADDRESS(_userIsGMT) 331 || user_memcpy(_userIsGMT, &sIsGMT, sizeof(bool)) != B_OK)) 332 return B_BAD_ADDRESS; 333 334 return B_OK; 335 } 336 337