mirror of
https://github.com/reactos/reactos.git
synced 2026-06-04 10:20:48 +08:00
Fix KiWriteSystemTime and move it to NDK. The previous implementation of KiWriteSystemTime was broken and updated the fields in the wrong order. Before that it was right for SystemTime and wrong for InterruptTime. ExpSetTimeZoneInformation had it wrong for the TimeZoneBias. Add KiReadSystemTime to read KSYSTEM_TIME values correctly, instead of doing it manually (and partly wrongly) all over the place.
742 lines
22 KiB
C
742 lines
22 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Kernel
|
|
* FILE: ntoskrnl/ex/time.c
|
|
* PURPOSE: Time and Timezone Management
|
|
* PROGRAMMERS: Eric Kohl
|
|
* Thomas Weidenmueller
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#define TICKSPERMINUTE 600000000
|
|
|
|
/* GLOBALS ******************************************************************/
|
|
|
|
/* Note: Bias[minutes] = UTC - local time */
|
|
RTL_TIME_ZONE_INFORMATION ExpTimeZoneInfo;
|
|
ULONG ExpLastTimeZoneBias = -1;
|
|
LARGE_INTEGER ExpTimeZoneBias;
|
|
ULONG ExpAltTimeZoneBias;
|
|
ULONG ExpTimeZoneId;
|
|
ULONG ExpTickCountMultiplier;
|
|
ERESOURCE ExpTimeRefreshLock;
|
|
ULONG ExpKernelResolutionCount = 0;
|
|
ULONG ExpTimerResolutionCount = 0;
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
/*++
|
|
* If successful, this function sets the following global variable:
|
|
* ExpTimeZoneInfo
|
|
*--*/
|
|
static
|
|
BOOLEAN
|
|
ExpGetTimeZoneId(
|
|
_In_ PLARGE_INTEGER TimeNow,
|
|
_Out_ PULONG TimeZoneId,
|
|
_Out_ PLARGE_INTEGER NewTimeZoneBias)
|
|
{
|
|
LARGE_INTEGER StandardTime;
|
|
LARGE_INTEGER DaylightTime;
|
|
LARGE_INTEGER LocalTimeNow = *TimeNow;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("ExpGetTimeZoneId\n");
|
|
|
|
/* Read time zone information from the registry */
|
|
Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the default bias */
|
|
NewTimeZoneBias->QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE;
|
|
|
|
if (ExpTimeZoneInfo.StandardDate.Month != 0 &&
|
|
ExpTimeZoneInfo.DaylightDate.Month != 0)
|
|
{
|
|
/* Get this year's standard start time */
|
|
if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.StandardDate,
|
|
&StandardTime,
|
|
&LocalTimeNow,
|
|
TRUE))
|
|
{
|
|
DPRINT1("RtlCutoverTimeToSystemTime for StandardDate failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get this year's daylight start time */
|
|
if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.DaylightDate,
|
|
&DaylightTime,
|
|
&LocalTimeNow,
|
|
TRUE))
|
|
{
|
|
DPRINT1("RtlCutoverTimeToSystemTime for DaylightDate failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Determine the time zone id and update the time zone bias */
|
|
if (DaylightTime.QuadPart < StandardTime.QuadPart)
|
|
{
|
|
if ((LocalTimeNow.QuadPart >= DaylightTime.QuadPart) &&
|
|
(LocalTimeNow.QuadPart < StandardTime.QuadPart))
|
|
{
|
|
DPRINT("Daylight time\n");
|
|
*TimeZoneId = TIME_ZONE_ID_DAYLIGHT;
|
|
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Standard time\n");
|
|
*TimeZoneId = TIME_ZONE_ID_STANDARD;
|
|
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((LocalTimeNow.QuadPart >= StandardTime.QuadPart) &&
|
|
(LocalTimeNow.QuadPart < DaylightTime.QuadPart))
|
|
{
|
|
DPRINT("Standard time\n");
|
|
*TimeZoneId = TIME_ZONE_ID_STANDARD;
|
|
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Daylight time\n");
|
|
*TimeZoneId = TIME_ZONE_ID_DAYLIGHT;
|
|
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*TimeZoneId = TIME_ZONE_ID_UNKNOWN;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
* @name ExAcquireTimeRefreshLock
|
|
*
|
|
* The ExReleaseTimeRefreshLock routine acquires the system-wide lock used
|
|
* to synchronize clock interrupt frequency changes.
|
|
*
|
|
* @param Wait
|
|
* If TRUE, the system will block the caller thread waiting for the lock
|
|
* to become available. If FALSE, the routine will fail if the lock has
|
|
* already been acquired.
|
|
*
|
|
* @return Boolean value indicating success or failure of the lock acquisition.
|
|
*
|
|
* @remarks None.
|
|
*
|
|
*--*/
|
|
BOOLEAN
|
|
NTAPI
|
|
ExAcquireTimeRefreshLock(IN BOOLEAN Wait)
|
|
{
|
|
/* Block APCs */
|
|
KeEnterCriticalRegion();
|
|
|
|
/* Attempt lock acquisition */
|
|
if (!(ExAcquireResourceExclusiveLite(&ExpTimeRefreshLock, Wait)))
|
|
{
|
|
/* Lock was not acquired, enable APCs and fail */
|
|
KeLeaveCriticalRegion();
|
|
return FALSE;
|
|
}
|
|
|
|
/* Lock has been acquired */
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
* @name ExReleaseTimeRefreshLock
|
|
*
|
|
* The ExReleaseTimeRefreshLock routine releases the system-wide lock used
|
|
* to synchronize clock interrupt frequency changes.
|
|
*
|
|
* @param None.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @remarks None.
|
|
*
|
|
*--*/
|
|
VOID
|
|
NTAPI
|
|
ExReleaseTimeRefreshLock(VOID)
|
|
{
|
|
/* Release the lock and re-enable APCs */
|
|
ExReleaseResourceLite(&ExpTimeRefreshLock);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
/*++
|
|
* @name ExSetTimerResolution
|
|
* @exported
|
|
*
|
|
* The KiInsertQueueApc routine modifies the frequency at which the system
|
|
* clock interrupts.
|
|
*
|
|
* @param DesiredTime
|
|
* Specifies the amount of time that should elapse between each timer
|
|
* interrupt, in 100-nanosecond units.
|
|
*
|
|
* This parameter is ignored if SetResolution is FALSE.
|
|
*
|
|
* @param SetResolution
|
|
* If TRUE, the call is a request to set the clock interrupt frequency to
|
|
* the value specified by DesiredTime. If FALSE, the call is a request to
|
|
* restore the clock interrupt frequency to the system's default value.
|
|
*
|
|
* @return New timer resolution, in 100-nanosecond ticks.
|
|
*
|
|
* @remarks (1) The clock frequency is changed only if the DesiredTime value is
|
|
* less than the current setting.
|
|
*
|
|
* (2) The routine just returns the current setting if the DesiredTime
|
|
* value is greater than what is currently set.
|
|
*
|
|
* (3) If the DesiredTime value is less than the system clock can
|
|
* support, the routine uses the smallest resolution the system can
|
|
* support, and returns that value.
|
|
*
|
|
* (4) If multiple drivers have attempted to change the clock interrupt
|
|
* frequency, the system will only restore the default frequency
|
|
* once ALL drivers have called the routine with SetResolution set
|
|
* to FALSE.
|
|
*
|
|
* NB. This routine synchronizes with IRP_MJ_POWER requests through the
|
|
* TimeRefreshLock.
|
|
*
|
|
*--*/
|
|
ULONG
|
|
NTAPI
|
|
ExSetTimerResolution(IN ULONG DesiredTime,
|
|
IN BOOLEAN SetResolution)
|
|
{
|
|
ULONG CurrentIncrement;
|
|
|
|
/* Wait for clock interrupt frequency and power requests to synchronize */
|
|
ExAcquireTimeRefreshLock(TRUE);
|
|
|
|
/* Obey remark 2*/
|
|
CurrentIncrement = KeTimeIncrement;
|
|
|
|
/* Check the type of operation this is */
|
|
if (SetResolution)
|
|
{
|
|
/*
|
|
* If this is the first kernel change, bump the timer resolution change
|
|
* count, then bump the kernel change count as well.
|
|
*
|
|
* These two variables are tracked differently since user-mode processes
|
|
* can also change the timer resolution through the NtSetTimerResolution
|
|
* system call. A per-process flag in the EPROCESS then stores the per-
|
|
* process change state.
|
|
*
|
|
*/
|
|
if (!ExpKernelResolutionCount++) ExpTimerResolutionCount++;
|
|
|
|
/* Obey remark 3 */
|
|
if (DesiredTime < KeMinimumIncrement) DesiredTime = KeMinimumIncrement;
|
|
|
|
/* Obey remark 1 */
|
|
if (DesiredTime < KeTimeIncrement)
|
|
{
|
|
/* Force this thread on CPU zero, since we don't want it to drift */
|
|
KeSetSystemAffinityThread(1);
|
|
|
|
/* Now call the platform driver (HAL) to make the change */
|
|
CurrentIncrement = HalSetTimeIncrement(DesiredTime);
|
|
|
|
/* Put the thread back to its original affinity */
|
|
KeRevertToUserAffinityThread();
|
|
|
|
/* Finally, keep track of the new value in the kernel */
|
|
KeTimeIncrement = CurrentIncrement;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* First, make sure that a driver has actually changed the resolution */
|
|
if (ExpKernelResolutionCount)
|
|
{
|
|
/* Obey remark 4 */
|
|
if (--ExpKernelResolutionCount)
|
|
{
|
|
/*
|
|
* All kernel drivers have requested the original frequency to
|
|
* be restored, but there might still be user processes with an
|
|
* ongoing clock interrupt frequency change, so make sure that
|
|
* this isn't the case.
|
|
*/
|
|
if (--ExpTimerResolutionCount)
|
|
{
|
|
/* Force this thread on one CPU so that it doesn't drift */
|
|
KeSetSystemAffinityThread(1);
|
|
|
|
/* Call the HAL to restore the frequency to its default */
|
|
CurrentIncrement = HalSetTimeIncrement(KeMaximumIncrement);
|
|
|
|
/* Put the thread back to its original affinity */
|
|
KeRevertToUserAffinityThread();
|
|
|
|
/* Finally, keep track of the new value in the kernel */
|
|
KeTimeIncrement = CurrentIncrement;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release the clock interrupt frequency lock since changes are done */
|
|
ExReleaseTimeRefreshLock();
|
|
|
|
/* And return the current value -- which could reflect the new frequency */
|
|
return CurrentIncrement;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ExUpdateSystemTimeFromCmos(IN BOOLEAN UpdateInterruptTime,
|
|
IN ULONG MaxSepInSeconds)
|
|
{
|
|
/* FIXME: TODO */
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime)
|
|
{
|
|
LARGE_INTEGER CurrentTime, NewTimeZoneBias;
|
|
BOOLEAN Success;
|
|
|
|
DPRINT("ExRefreshTimeZoneInformation\n");
|
|
|
|
/* Set the global data for ExpTimeZoneBias and the Time Zone ID */
|
|
Success = ExpGetTimeZoneId(CurrentBootTime, &ExpTimeZoneId, &NewTimeZoneBias);
|
|
if (!Success)
|
|
{
|
|
DPRINT1("ExpGetTimeZoneId failed\n");
|
|
return FALSE;
|
|
}
|
|
DPRINT("ExpTimeZoneId is %lu\n", ExpTimeZoneId);
|
|
|
|
ExpTimeZoneBias = NewTimeZoneBias;
|
|
|
|
/* Change SharedUserData->TimeZoneBias for user-mode applications */
|
|
KiWriteSystemTime(&SharedUserData->TimeZoneBias, ExpTimeZoneBias);
|
|
SharedUserData->TimeZoneId = ExpTimeZoneId;
|
|
|
|
/* Convert boot time from local time to UTC */
|
|
KeBootTime.QuadPart += ExpTimeZoneBias.QuadPart;
|
|
|
|
/* Convert system time from local time to UTC */
|
|
KeQuerySystemTime(&CurrentTime);;
|
|
|
|
/* Change it for user-mode applications */
|
|
CurrentTime.QuadPart += ExpTimeZoneBias.QuadPart;
|
|
KiWriteSystemTime(&SharedUserData->SystemTime, CurrentTime);
|
|
|
|
/* Return success */
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation)
|
|
{
|
|
LARGE_INTEGER LocalTime, SystemTime, OldTime, NewTimeZoneBias;
|
|
TIME_FIELDS TimeFields;
|
|
BOOLEAN Success;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER LocalTimeNow;
|
|
|
|
DPRINT("ExpSetTimeZoneInformation called\n");
|
|
|
|
/* Read time zone information from the registry */
|
|
Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the default bias */
|
|
NewTimeZoneBias.QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE;
|
|
|
|
/* Get the Shared User Data System Time into the local SystemTime */
|
|
KeQuerySystemTime(&SystemTime);
|
|
|
|
LocalTimeNow = SystemTime;
|
|
|
|
/* Adjust LocalTimeNow for Time Zone Bias to use UTC for comparisons */
|
|
LocalTimeNow.QuadPart -= NewTimeZoneBias.QuadPart;
|
|
|
|
/* Set the Global Data for ExpTimeZoneBias and ExpTimeZoneId */
|
|
Success = ExpGetTimeZoneId(&LocalTimeNow, &ExpTimeZoneId, &NewTimeZoneBias);
|
|
if (!Success)
|
|
{
|
|
DPRINT1("ExpGetTimeZoneId failed\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
DPRINT("ExpTimeZoneId is %lu\n", ExpTimeZoneId);
|
|
|
|
ExpTimeZoneBias = NewTimeZoneBias;
|
|
|
|
DPRINT("Old time zone bias: %d minutes\n", ExpTimeZoneInfo.Bias);
|
|
DPRINT("Old time zone standard bias: %d minutes\n",
|
|
ExpTimeZoneInfo.StandardBias);
|
|
DPRINT("New time zone bias: %d minutes\n", TimeZoneInformation->Bias);
|
|
DPRINT("New time zone standard bias: %d minutes\n",
|
|
TimeZoneInformation->StandardBias);
|
|
|
|
/* Get the local time */
|
|
HalQueryRealTimeClock(&TimeFields);
|
|
RtlTimeFieldsToTime(&TimeFields, &LocalTime);
|
|
|
|
/* Calculate the bias */
|
|
ExpTimeZoneBias.QuadPart = ((LONGLONG)(TimeZoneInformation->Bias +
|
|
TimeZoneInformation->StandardBias)) *
|
|
TICKSPERMINUTE;
|
|
|
|
/* If Daylight Savings Time then add the DayLightBias to the Time Zone Bias */
|
|
if (ExpTimeZoneId == TIME_ZONE_ID_DAYLIGHT)
|
|
ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
|
|
|
|
/* Copy the timezone information */
|
|
RtlCopyMemory(&ExpTimeZoneInfo,
|
|
TimeZoneInformation,
|
|
sizeof(RTL_TIME_ZONE_INFORMATION));
|
|
|
|
/* Set the new time zone information */
|
|
KiWriteSystemTime(&SharedUserData->TimeZoneBias, ExpTimeZoneBias);
|
|
SharedUserData->TimeZoneId = ExpTimeZoneId;
|
|
|
|
DPRINT("New time zone bias: %I64d minutes\n",
|
|
ExpTimeZoneBias.QuadPart / TICKSPERMINUTE);
|
|
|
|
/* Calculate the new system time */
|
|
ExLocalTimeToSystemTime(&LocalTime, &SystemTime);
|
|
|
|
/* Set the new system time and notify the system */
|
|
KeSetSystemTime(&SystemTime, &OldTime, FALSE, NULL);
|
|
PoNotifySystemTimeSet();
|
|
|
|
/* Return success */
|
|
DPRINT("ExpSetTimeZoneInformation done\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Sets the system time.
|
|
* PARAMETERS:
|
|
* NewTime - Points to a variable that specified the new time
|
|
* of day in the standard time format.
|
|
* OldTime - Optionally points to a variable that receives the
|
|
* old time of day in the standard time format.
|
|
* RETURNS: Status
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetSystemTime(IN PLARGE_INTEGER SystemTime,
|
|
OUT PLARGE_INTEGER PreviousTime OPTIONAL)
|
|
{
|
|
LARGE_INTEGER OldSystemTime;
|
|
LARGE_INTEGER NewSystemTime;
|
|
LARGE_INTEGER LocalTime;
|
|
TIME_FIELDS TimeFields;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RTL_TIME_ZONE_INFORMATION TimeZoneInformation = { 0 };
|
|
ULONG TimeZoneIdSave;
|
|
|
|
PAGED_CODE();
|
|
|
|
// TODO: Handle the case when SystemTime == NULL, which means:
|
|
// "update system time using the current time-zone information".
|
|
if (!SystemTime)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Check if we were called from user-mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Verify the time pointers */
|
|
NewSystemTime = ProbeForReadLargeInteger(SystemTime);
|
|
if (PreviousTime) ProbeForWriteLargeInteger(PreviousTime);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Reuse the pointer */
|
|
NewSystemTime = *SystemTime;
|
|
}
|
|
|
|
/* Make sure we have permission to change the time */
|
|
if (!SeSinglePrivilegeCheck(SeSystemtimePrivilege, PreviousMode))
|
|
{
|
|
DPRINT1("NtSetSystemTime: Caller requires the "
|
|
"SeSystemtimePrivilege privilege!\n");
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
/* Convert the time and set it in HAL */
|
|
ExSystemTimeToLocalTime(&NewSystemTime, &LocalTime);
|
|
RtlTimeToTimeFields(&LocalTime, &TimeFields);
|
|
HalSetRealTimeClock(&TimeFields);
|
|
|
|
/* Now set the system time and notify the system */
|
|
KeSetSystemTime(&NewSystemTime, &OldSystemTime, FALSE, NULL);
|
|
PoNotifySystemTimeSet();
|
|
|
|
/* Check if caller wanted previous time */
|
|
if (PreviousTime)
|
|
{
|
|
/* Enter SEH Block for return */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the previous time */
|
|
*PreviousTime = OldSystemTime;
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Read time zone information from the registry and set the clock */
|
|
Status = RtlQueryTimeZoneInformation(&TimeZoneInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
|
|
/* Test if we went from Daylight to Standard Time or vice versa */
|
|
TimeZoneIdSave = ExpTimeZoneId;
|
|
ExpSetTimeZoneInformation(&TimeZoneInformation);
|
|
|
|
if (ExpTimeZoneId != TimeZoneIdSave)
|
|
{
|
|
/* Going from DT to ST or vice versa we need to repeat this */
|
|
DPRINT("Daylight Time and Standard Time are switching\n");
|
|
|
|
/* Set the system time and notify the system */
|
|
KeSetSystemTime(&NewSystemTime, &OldSystemTime, FALSE, NULL);
|
|
PoNotifySystemTimeSet();
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Retrieves the system time.
|
|
* PARAMETERS:
|
|
* CurrentTime - Points to a variable that receives the current
|
|
* time of day in the standard time format.
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQuerySystemTime(OUT PLARGE_INTEGER SystemTime)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PAGED_CODE();
|
|
|
|
/* Check if we were called from user-mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Verify the time pointer */
|
|
ProbeForWriteLargeInteger(SystemTime);
|
|
|
|
/*
|
|
* It's safe to pass the pointer directly to KeQuerySystemTime
|
|
* as it's just a basic copy to this pointer. If it raises an
|
|
* exception nothing dangerous can happen!
|
|
*/
|
|
KeQuerySystemTime(SystemTime);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Query the time directly */
|
|
KeQuerySystemTime(SystemTime);
|
|
}
|
|
|
|
/* Return success */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
ExLocalTimeToSystemTime(PLARGE_INTEGER LocalTime,
|
|
PLARGE_INTEGER SystemTime)
|
|
{
|
|
SystemTime->QuadPart = LocalTime->QuadPart + ExpTimeZoneBias.QuadPart;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
ExSystemTimeToLocalTime(PLARGE_INTEGER SystemTime,
|
|
PLARGE_INTEGER LocalTime)
|
|
{
|
|
LocalTime->QuadPart = SystemTime->QuadPart - ExpTimeZoneBias.QuadPart;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryTimerResolution(OUT PULONG MinimumResolution,
|
|
OUT PULONG MaximumResolution,
|
|
OUT PULONG ActualResolution)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
/* Check if the call came from user-mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the parameters */
|
|
ProbeForWriteUlong(MinimumResolution);
|
|
ProbeForWriteUlong(MaximumResolution);
|
|
ProbeForWriteUlong(ActualResolution);
|
|
|
|
/*
|
|
* Set the parameters to the actual values.
|
|
*
|
|
* NOTE:
|
|
* MinimumResolution corresponds to the biggest time increment and
|
|
* MaximumResolution corresponds to the smallest time increment.
|
|
*/
|
|
*MinimumResolution = KeMaximumIncrement;
|
|
*MaximumResolution = KeMinimumIncrement;
|
|
*ActualResolution = KeTimeIncrement;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Set the parameters to the actual values */
|
|
*MinimumResolution = KeMaximumIncrement;
|
|
*MaximumResolution = KeMinimumIncrement;
|
|
*ActualResolution = KeTimeIncrement;
|
|
}
|
|
|
|
/* Return success */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetTimerResolution(IN ULONG DesiredResolution,
|
|
IN BOOLEAN SetResolution,
|
|
OUT PULONG CurrentResolution)
|
|
{
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PEPROCESS Process = PsGetCurrentProcess();
|
|
ULONG NewResolution;
|
|
|
|
/* Check if the call came from user-mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the parameter */
|
|
ProbeForWriteUlong(CurrentResolution);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Set and return the new resolution */
|
|
NewResolution = ExSetTimerResolution(DesiredResolution, SetResolution);
|
|
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
*CurrentResolution = NewResolution;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
*CurrentResolution = NewResolution;
|
|
}
|
|
|
|
if (SetResolution || Process->SetTimerResolution)
|
|
{
|
|
/* The resolution has been changed now or in an earlier call */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* The resolution hasn't been changed */
|
|
Status = STATUS_TIMER_RESOLUTION_NOT_SET;
|
|
}
|
|
|
|
/* Update the flag */
|
|
Process->SetTimerResolution = SetResolution;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|