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