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