1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Kernel 4 * FILE: ntoskrnl/ex/time.c 5 * PURPOSE: Time and Timezone Management 6 * PROGRAMMERS: Eric Kohl 7 * Thomas Weidenmueller 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 #define TICKSPERMINUTE 600000000 17 18 /* GLOBALS ******************************************************************/ 19 20 /* Note: Bias[minutes] = UTC - local time */ 21 RTL_TIME_ZONE_INFORMATION ExpTimeZoneInfo; 22 ULONG ExpLastTimeZoneBias = -1; 23 LARGE_INTEGER ExpTimeZoneBias; 24 ULONG ExpAltTimeZoneBias; 25 ULONG ExpTimeZoneId; 26 ULONG ExpTickCountMultiplier; 27 ERESOURCE ExpTimeRefreshLock; 28 ULONG ExpKernelResolutionCount = 0; 29 ULONG ExpTimerResolutionCount = 0; 30 31 /* FUNCTIONS ****************************************************************/ 32 33 /*++ 34 * @name ExAcquireTimeRefreshLock 35 * 36 * The ExReleaseTimeRefreshLock routine acquires the system-wide lock used 37 * to synchronize clock interrupt frequency changes. 38 * 39 * @param Wait 40 * If TRUE, the system will block the caller thread waiting for the lock 41 * to become available. If FALSE, the routine will fail if the lock has 42 * already been acquired. 43 * 44 * @return Boolean value indicating success or failure of the lock acquisition. 45 * 46 * @remarks None. 47 * 48 *--*/ 49 BOOLEAN 50 NTAPI 51 ExAcquireTimeRefreshLock(IN BOOLEAN Wait) 52 { 53 /* Block APCs */ 54 KeEnterCriticalRegion(); 55 56 /* Attempt lock acquisition */ 57 if (!(ExAcquireResourceExclusiveLite(&ExpTimeRefreshLock, Wait))) 58 { 59 /* Lock was not acquired, enable APCs and fail */ 60 KeLeaveCriticalRegion(); 61 return FALSE; 62 } 63 64 /* Lock has been acquired */ 65 return TRUE; 66 } 67 68 /*++ 69 * @name ExReleaseTimeRefreshLock 70 * 71 * The ExReleaseTimeRefreshLock routine releases the system-wide lock used 72 * to synchronize clock interrupt frequency changes. 73 * 74 * @param None. 75 * 76 * @return None. 77 * 78 * @remarks None. 79 * 80 *--*/ 81 VOID 82 NTAPI 83 ExReleaseTimeRefreshLock(VOID) 84 { 85 /* Release the lock and re-enable APCs */ 86 ExReleaseResourceLite(&ExpTimeRefreshLock); 87 KeLeaveCriticalRegion(); 88 } 89 90 /*++ 91 * @name ExSetTimerResolution 92 * @exported 93 * 94 * The KiInsertQueueApc routine modifies the frequency at which the system 95 * clock interrupts. 96 * 97 * @param DesiredTime 98 * Specifies the amount of time that should elapse between each timer 99 * interrupt, in 100-nanosecond units. 100 * 101 * This parameter is ignored if SetResolution is FALSE. 102 * 103 * @param SetResolution 104 * If TRUE, the call is a request to set the clock interrupt frequency to 105 * the value specified by DesiredTime. If FALSE, the call is a request to 106 * restore the clock interrupt frequency to the system's default value. 107 * 108 * @return New timer resolution, in 100-nanosecond ticks. 109 * 110 * @remarks (1) The clock frequency is changed only if the DesiredTime value is 111 * less than the current setting. 112 * 113 * (2) The routine just returns the current setting if the DesiredTime 114 * value is greater than what is currently set. 115 * 116 * (3) If the DesiredTime value is less than the system clock can 117 * support, the routine uses the smallest resolution the system can 118 * support, and returns that value. 119 * 120 * (4) If multiple drivers have attempted to change the clock interrupt 121 * frequency, the system will only restore the default frequency 122 * once ALL drivers have called the routine with SetResolution set 123 * to FALSE. 124 * 125 * NB. This routine synchronizes with IRP_MJ_POWER requests through the 126 * TimeRefreshLock. 127 * 128 *--*/ 129 ULONG 130 NTAPI 131 ExSetTimerResolution(IN ULONG DesiredTime, 132 IN BOOLEAN SetResolution) 133 { 134 ULONG CurrentIncrement; 135 136 /* Wait for clock interrupt frequency and power requests to synchronize */ 137 ExAcquireTimeRefreshLock(TRUE); 138 139 /* Obey remark 2*/ 140 CurrentIncrement = KeTimeIncrement; 141 142 /* Check the type of operation this is */ 143 if (SetResolution) 144 { 145 /* 146 * If this is the first kernel change, bump the timer resolution change 147 * count, then bump the kernel change count as well. 148 * 149 * These two variables are tracked differently since user-mode processes 150 * can also change the timer resolution through the NtSetTimerResolution 151 * system call. A per-process flag in the EPROCESS then stores the per- 152 * process change state. 153 * 154 */ 155 if (!ExpKernelResolutionCount++) ExpTimerResolutionCount++; 156 157 /* Obey remark 3 */ 158 if (DesiredTime < KeMinimumIncrement) DesiredTime = KeMinimumIncrement; 159 160 /* Obey remark 1 */ 161 if (DesiredTime < KeTimeIncrement) 162 { 163 /* Force this thread on CPU zero, since we don't want it to drift */ 164 KeSetSystemAffinityThread(1); 165 166 /* Now call the platform driver (HAL) to make the change */ 167 CurrentIncrement = HalSetTimeIncrement(DesiredTime); 168 169 /* Put the thread back to its original affinity */ 170 KeRevertToUserAffinityThread(); 171 172 /* Finally, keep track of the new value in the kernel */ 173 KeTimeIncrement = CurrentIncrement; 174 } 175 } 176 else 177 { 178 /* First, make sure that a driver has actually changed the resolution */ 179 if (ExpKernelResolutionCount) 180 { 181 /* Obey remark 4 */ 182 if (--ExpKernelResolutionCount) 183 { 184 /* 185 * All kernel drivers have requested the original frequency to 186 * be restored, but there might still be user processes with an 187 * ongoing clock interrupt frequency change, so make sure that 188 * this isn't the case. 189 */ 190 if (--ExpTimerResolutionCount) 191 { 192 /* Force this thread on one CPU so that it doesn't drift */ 193 KeSetSystemAffinityThread(1); 194 195 /* Call the HAL to restore the frequency to its default */ 196 CurrentIncrement = HalSetTimeIncrement(KeMaximumIncrement); 197 198 /* Put the thread back to its original affinity */ 199 KeRevertToUserAffinityThread(); 200 201 /* Finally, keep track of the new value in the kernel */ 202 KeTimeIncrement = CurrentIncrement; 203 } 204 } 205 } 206 } 207 208 /* Release the clock interrupt frequency lock since changes are done */ 209 ExReleaseTimeRefreshLock(); 210 211 /* And return the current value -- which could reflect the new frequency */ 212 return CurrentIncrement; 213 } 214 215 VOID 216 NTAPI 217 ExUpdateSystemTimeFromCmos(IN BOOLEAN UpdateInterruptTime, 218 IN ULONG MaxSepInSeconds) 219 { 220 /* FIXME: TODO */ 221 return; 222 } 223 224 BOOLEAN 225 NTAPI 226 ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime) 227 { 228 LARGE_INTEGER StandardTime; 229 LARGE_INTEGER DaylightTime; 230 LARGE_INTEGER CurrentTime; 231 NTSTATUS Status; 232 233 /* Read time zone information from the registry */ 234 Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo); 235 if (!NT_SUCCESS(Status)) 236 { 237 DPRINT1("RtlQueryTimeZoneInformation() failed (Status 0x%08lx)\n", Status); 238 return FALSE; 239 } 240 241 /* Get the default bias */ 242 ExpTimeZoneBias.QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE; 243 244 if (ExpTimeZoneInfo.StandardDate.Month != 0 && 245 ExpTimeZoneInfo.DaylightDate.Month != 0) 246 { 247 /* Get this years standard start time */ 248 if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.StandardDate, 249 &StandardTime, 250 CurrentBootTime, 251 TRUE)) 252 { 253 DPRINT1("RtlCutoverTimeToSystemTime() for StandardDate failed!\n"); 254 return FALSE; 255 } 256 257 /* Get this years daylight start time */ 258 if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.DaylightDate, 259 &DaylightTime, 260 CurrentBootTime, 261 TRUE)) 262 { 263 DPRINT1("RtlCutoverTimeToSystemTime() for DaylightDate failed!\n"); 264 return FALSE; 265 } 266 267 /* Determine the time zone id and update the time zone bias */ 268 if (DaylightTime.QuadPart < StandardTime.QuadPart) 269 { 270 if ((CurrentBootTime->QuadPart >= DaylightTime.QuadPart) && 271 (CurrentBootTime->QuadPart < StandardTime.QuadPart)) 272 { 273 DPRINT("Daylight time!\n"); 274 ExpTimeZoneId = TIME_ZONE_ID_DAYLIGHT; 275 ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE; 276 } 277 else 278 { 279 DPRINT("Standard time!\n"); 280 ExpTimeZoneId = TIME_ZONE_ID_STANDARD; 281 ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE; 282 } 283 } 284 else 285 { 286 if ((CurrentBootTime->QuadPart >= StandardTime.QuadPart) && 287 (CurrentBootTime->QuadPart < DaylightTime.QuadPart)) 288 { 289 DPRINT("Standard time!\n"); 290 ExpTimeZoneId = TIME_ZONE_ID_STANDARD; 291 ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE; 292 } 293 else 294 { 295 DPRINT("Daylight time!\n"); 296 ExpTimeZoneId = TIME_ZONE_ID_DAYLIGHT; 297 ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE; 298 } 299 } 300 } 301 else 302 { 303 ExpTimeZoneId = TIME_ZONE_ID_UNKNOWN; 304 } 305 306 /* Change it for user-mode applications */ 307 SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart; 308 SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.u.HighPart; 309 SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.u.LowPart; 310 SharedUserData->TimeZoneId = ExpTimeZoneId; 311 312 /* Convert boot time from local time to UTC */ 313 KeBootTime.QuadPart += ExpTimeZoneBias.QuadPart; 314 315 /* Convert system time from local time to UTC */ 316 do 317 { 318 CurrentTime.u.HighPart = SharedUserData->SystemTime.High1Time; 319 CurrentTime.u.LowPart = SharedUserData->SystemTime.LowPart; 320 } while (CurrentTime.u.HighPart != SharedUserData->SystemTime.High2Time); 321 322 /* Change it for user-mode applications */ 323 CurrentTime.QuadPart += ExpTimeZoneBias.QuadPart; 324 SharedUserData->SystemTime.LowPart = CurrentTime.u.LowPart; 325 SharedUserData->SystemTime.High1Time = CurrentTime.u.HighPart; 326 SharedUserData->SystemTime.High2Time = CurrentTime.u.HighPart; 327 328 /* Return success */ 329 return TRUE; 330 } 331 332 NTSTATUS 333 ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation) 334 { 335 LARGE_INTEGER LocalTime, SystemTime, OldTime; 336 TIME_FIELDS TimeFields; 337 DPRINT("ExpSetTimeZoneInformation() called\n"); 338 339 DPRINT("Old time zone bias: %d minutes\n", ExpTimeZoneInfo.Bias); 340 DPRINT("Old time zone standard bias: %d minutes\n", 341 ExpTimeZoneInfo.StandardBias); 342 DPRINT("New time zone bias: %d minutes\n", TimeZoneInformation->Bias); 343 DPRINT("New time zone standard bias: %d minutes\n", 344 TimeZoneInformation->StandardBias); 345 346 /* Get the local time */ 347 HalQueryRealTimeClock(&TimeFields); 348 RtlTimeFieldsToTime(&TimeFields, &LocalTime); 349 350 /* FIXME: Calculate transition dates */ 351 352 /* Calculate the bias and set the ID */ 353 ExpTimeZoneBias.QuadPart = ((LONGLONG)(TimeZoneInformation->Bias + 354 TimeZoneInformation->StandardBias)) * 355 TICKSPERMINUTE; 356 ExpTimeZoneId = TIME_ZONE_ID_STANDARD; 357 358 /* Copy the timezone information */ 359 RtlCopyMemory(&ExpTimeZoneInfo, 360 TimeZoneInformation, 361 sizeof(RTL_TIME_ZONE_INFORMATION)); 362 363 /* Set the new time zone information */ 364 SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart; 365 SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.u.HighPart; 366 SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.u.LowPart; 367 SharedUserData->TimeZoneId = ExpTimeZoneId; 368 369 DPRINT("New time zone bias: %I64d minutes\n", 370 ExpTimeZoneBias.QuadPart / TICKSPERMINUTE); 371 372 /* Calculate the new system time */ 373 ExLocalTimeToSystemTime(&LocalTime, &SystemTime); 374 375 /* Set the new system time */ 376 KeSetSystemTime(&SystemTime, &OldTime, FALSE, NULL); 377 378 /* Return success */ 379 DPRINT("ExpSetTimeZoneInformation() done\n"); 380 return STATUS_SUCCESS; 381 } 382 383 /* 384 * FUNCTION: Sets the system time. 385 * PARAMETERS: 386 * NewTime - Points to a variable that specified the new time 387 * of day in the standard time format. 388 * OldTime - Optionally points to a variable that receives the 389 * old time of day in the standard time format. 390 * RETURNS: Status 391 */ 392 NTSTATUS 393 NTAPI 394 NtSetSystemTime(IN PLARGE_INTEGER SystemTime, 395 OUT PLARGE_INTEGER PreviousTime OPTIONAL) 396 { 397 LARGE_INTEGER OldSystemTime; 398 LARGE_INTEGER NewSystemTime; 399 LARGE_INTEGER LocalTime; 400 TIME_FIELDS TimeFields; 401 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 402 NTSTATUS Status = STATUS_SUCCESS; 403 PAGED_CODE(); 404 405 /* Check if we were called from user-mode */ 406 if (PreviousMode != KernelMode) 407 { 408 _SEH2_TRY 409 { 410 /* Verify the time pointers */ 411 NewSystemTime = ProbeForReadLargeInteger(SystemTime); 412 if(PreviousTime) ProbeForWriteLargeInteger(PreviousTime); 413 } 414 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 415 { 416 /* Return the exception code */ 417 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 418 } 419 _SEH2_END; 420 } 421 else 422 { 423 /* Reuse the pointer */ 424 NewSystemTime = *SystemTime; 425 } 426 427 /* Make sure we have permission to change the time */ 428 if (!SeSinglePrivilegeCheck(SeSystemtimePrivilege, PreviousMode)) 429 { 430 DPRINT1("NtSetSystemTime: Caller requires the " 431 "SeSystemtimePrivilege privilege!\n"); 432 return STATUS_PRIVILEGE_NOT_HELD; 433 } 434 435 /* Convert the time and set it in HAL */ 436 ExSystemTimeToLocalTime(&NewSystemTime, &LocalTime); 437 RtlTimeToTimeFields(&LocalTime, &TimeFields); 438 HalSetRealTimeClock(&TimeFields); 439 440 /* Now set system time */ 441 KeSetSystemTime(&NewSystemTime, &OldSystemTime, FALSE, NULL); 442 443 /* Check if caller wanted previous time */ 444 if (PreviousTime) 445 { 446 /* Enter SEH Block for return */ 447 _SEH2_TRY 448 { 449 /* Return the previous time */ 450 *PreviousTime = OldSystemTime; 451 } 452 _SEH2_EXCEPT(ExSystemExceptionFilter()) 453 { 454 /* Get the exception code */ 455 Status = _SEH2_GetExceptionCode(); 456 } 457 _SEH2_END; 458 } 459 460 /* Return status */ 461 return Status; 462 } 463 464 /* 465 * FUNCTION: Retrieves the system time. 466 * PARAMETERS: 467 * CurrentTime - Points to a variable that receives the current 468 * time of day in the standard time format. 469 */ 470 NTSTATUS 471 NTAPI 472 NtQuerySystemTime(OUT PLARGE_INTEGER SystemTime) 473 { 474 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 475 PAGED_CODE(); 476 477 /* Check if we were called from user-mode */ 478 if (PreviousMode != KernelMode) 479 { 480 _SEH2_TRY 481 { 482 /* Verify the time pointer */ 483 ProbeForWriteLargeInteger(SystemTime); 484 485 /* 486 * It's safe to pass the pointer directly to KeQuerySystemTime 487 * as it's just a basic copy to this pointer. If it raises an 488 * exception nothing dangerous can happen! 489 */ 490 KeQuerySystemTime(SystemTime); 491 } 492 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 493 { 494 /* Return the exception code */ 495 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 496 } 497 _SEH2_END; 498 } 499 else 500 { 501 /* Query the time directly */ 502 KeQuerySystemTime(SystemTime); 503 } 504 505 /* Return success */ 506 return STATUS_SUCCESS; 507 } 508 509 /* 510 * @implemented 511 */ 512 VOID 513 NTAPI 514 ExLocalTimeToSystemTime(PLARGE_INTEGER LocalTime, 515 PLARGE_INTEGER SystemTime) 516 { 517 SystemTime->QuadPart = LocalTime->QuadPart + ExpTimeZoneBias.QuadPart; 518 } 519 520 /* 521 * @implemented 522 */ 523 VOID 524 NTAPI 525 ExSystemTimeToLocalTime(PLARGE_INTEGER SystemTime, 526 PLARGE_INTEGER LocalTime) 527 { 528 LocalTime->QuadPart = SystemTime->QuadPart - ExpTimeZoneBias.QuadPart; 529 } 530 531 /* 532 * @implemented 533 */ 534 NTSTATUS 535 NTAPI 536 NtQueryTimerResolution(OUT PULONG MinimumResolution, 537 OUT PULONG MaximumResolution, 538 OUT PULONG ActualResolution) 539 { 540 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 541 542 /* Check if the call came from user-mode */ 543 if (PreviousMode != KernelMode) 544 { 545 _SEH2_TRY 546 { 547 /* Probe the parameters */ 548 ProbeForWriteUlong(MinimumResolution); 549 ProbeForWriteUlong(MaximumResolution); 550 ProbeForWriteUlong(ActualResolution); 551 552 /* 553 * Set the parameters to the actual values. 554 * 555 * NOTE: 556 * MinimumResolution corresponds to the biggest time increment and 557 * MaximumResolution corresponds to the smallest time increment. 558 */ 559 *MinimumResolution = KeMaximumIncrement; 560 *MaximumResolution = KeMinimumIncrement; 561 *ActualResolution = KeTimeIncrement; 562 } 563 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 564 { 565 /* Return the exception code */ 566 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 567 } 568 _SEH2_END; 569 } 570 else 571 { 572 /* Set the parameters to the actual values */ 573 *MinimumResolution = KeMaximumIncrement; 574 *MaximumResolution = KeMinimumIncrement; 575 *ActualResolution = KeTimeIncrement; 576 } 577 578 /* Return success */ 579 return STATUS_SUCCESS; 580 } 581 582 /* 583 * @implemented 584 */ 585 NTSTATUS 586 NTAPI 587 NtSetTimerResolution(IN ULONG DesiredResolution, 588 IN BOOLEAN SetResolution, 589 OUT PULONG CurrentResolution) 590 { 591 NTSTATUS Status; 592 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 593 PEPROCESS Process = PsGetCurrentProcess(); 594 ULONG NewResolution; 595 596 /* Check if the call came from user-mode */ 597 if (PreviousMode != KernelMode) 598 { 599 _SEH2_TRY 600 { 601 /* Probe the parameter */ 602 ProbeForWriteUlong(CurrentResolution); 603 } 604 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 605 { 606 /* Return the exception code */ 607 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 608 } 609 _SEH2_END; 610 } 611 612 /* Set and return the new resolution */ 613 NewResolution = ExSetTimerResolution(DesiredResolution, SetResolution); 614 615 if (PreviousMode != KernelMode) 616 { 617 _SEH2_TRY 618 { 619 *CurrentResolution = NewResolution; 620 } 621 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 622 { 623 /* Return the exception code */ 624 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 625 } 626 _SEH2_END; 627 } 628 else 629 { 630 *CurrentResolution = NewResolution; 631 } 632 633 if (SetResolution || Process->SetTimerResolution) 634 { 635 /* The resolution has been changed now or in an earlier call */ 636 Status = STATUS_SUCCESS; 637 } 638 else 639 { 640 /* The resolution hasn't been changed */ 641 Status = STATUS_TIMER_RESOLUTION_NOT_SET; 642 } 643 644 /* Update the flag */ 645 Process->SetTimerResolution = SetResolution; 646 647 return Status; 648 } 649 650 /* EOF */ 651