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