2025-04-30 16:24:29 +08:00

475 lines
14 KiB
C

/*
* Copyright (c) 2001 - 2005 NetGroup, Politecnico di Torino (Italy)
* Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
* nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _time_calls
#define _time_calls
#ifdef WIN_NT_DRIVER
#include "debug.h"
#include "ndis.h"
#define DEFAULT_TIMESTAMPMODE 0
#define TIMESTAMPMODE_SINGLE_SYNCHRONIZATION 0
#define TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_WITH_FIXUP 1
#define TIMESTAMPMODE_QUERYSYSTEMTIME 2
#define TIMESTAMPMODE_RDTSC 3
#define TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_NO_FIXUP 99
#define TIMESTAMPMODE_REGKEY L"TimestampMode"
extern ULONG TimestampMode;
/*!
\brief A microsecond precise timestamp.
included in the sf_pkthdr or the bpf_hdr that NPF associates with every packet.
*/
struct timeval {
long tv_sec; ///< seconds
long tv_usec; ///< microseconds
};
#endif /*WIN_NT_DRIVER*/
struct time_conv
{
ULONGLONG reference;
struct timeval start[32];
};
#ifdef WIN_NT_DRIVER
__inline void TIME_DESYNCHRONIZE( struct time_conv * data )
{
data->reference = 0;
// data->start.tv_sec = 0;
// data->start.tv_usec = 0;
}
__inline void ReadTimeStampModeFromRegistry( PUNICODE_STRING RegistryPath )
{
ULONG NewLength;
PWSTR NullTerminatedString;
RTL_QUERY_REGISTRY_TABLE Queries[2];
ULONG DefaultTimestampMode = DEFAULT_TIMESTAMPMODE;
NewLength = RegistryPath->Length / 2;
NullTerminatedString = ExAllocatePoolWithTag( PagedPool, ( NewLength + 1 ) * sizeof( WCHAR ), '2TWA' );
if ( NullTerminatedString != NULL )
{
RtlCopyMemory( NullTerminatedString, RegistryPath->Buffer, RegistryPath->Length );
NullTerminatedString[NewLength] = 0;
RtlZeroMemory( Queries, sizeof( Queries ));
Queries[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
Queries[0].Name = TIMESTAMPMODE_REGKEY;
Queries[0].EntryContext = & TimestampMode;
Queries[0].DefaultType = REG_DWORD;
Queries[0].DefaultData = & DefaultTimestampMode;
Queries[0].DefaultLength = sizeof( ULONG );
if( RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE, NullTerminatedString, Queries, NULL, NULL ) != STATUS_SUCCESS )
{
TimestampMode = DEFAULT_TIMESTAMPMODE;
}
RtlWriteRegistryValue( RTL_REGISTRY_ABSOLUTE, NullTerminatedString, TIMESTAMPMODE_REGKEY, REG_DWORD, & TimestampMode, sizeof( ULONG ));
ExFreePool( NullTerminatedString );
}
else
TimestampMode = DEFAULT_TIMESTAMPMODE;
}
#pragma optimize ("g",off) //Due to some weird behaviour of the optimizer of DDK build 2600
/* KeQueryPerformanceCounter TimeStamps */
__inline void SynchronizeOnCpu( struct timeval * start )
{
// struct timeval *start = (struct timeval*)Data;
struct timeval tmp;
LARGE_INTEGER SystemTime;
LARGE_INTEGER i;
ULONG tmp2;
LARGE_INTEGER TimeFreq, PTime;
// get the absolute value of the system boot time.
PTime = KeQueryPerformanceCounter(& TimeFreq );
KeQuerySystemTime(& SystemTime );
start->tv_sec = ( LONG )( SystemTime.QuadPart / 10000000 - 11644473600 );
start->tv_usec = ( LONG )(( SystemTime.QuadPart % 10000000 )/ 10 );
start->tv_sec -= ( ULONG )( PTime.QuadPart / TimeFreq.QuadPart );
start->tv_usec -= ( LONG )(( PTime.QuadPart % TimeFreq.QuadPart )* 1000000 / TimeFreq.QuadPart );
if ( start->tv_usec < 0 )
{
start->tv_sec --;
start->tv_usec += 1000000;
}
}
//
// inline assembler is not supported with the current AMD64 compilers
// At the moment we simply disable this timestamping mode on AMD64.
// A solution would be to allocate a small memory from the non-paged
// pool, dump the instructions on that buffer, and then execute them.
// The non paged pool is needed since it's the only area of kernel
// data memory that is not subject to the NX protection.
// Or use some lower level trick, like using an assembler to assemble
// a small function for this.
//
#ifdef _X86_
/*RDTSC timestamps */
/* callers must be at IRQL=PASSIVE_LEVEL*/
__inline VOID TimeSynchronizeRDTSC( struct time_conv * data )
{
struct timeval tmp;
LARGE_INTEGER system_time;
ULONGLONG curr_ticks;
KIRQL old;
LARGE_INTEGER start_kqpc, stop_kqpc, start_freq, stop_freq;
ULONGLONG start_ticks, stop_ticks;
ULONGLONG delta, delta2;
KEVENT event;
LARGE_INTEGER i;
ULONGLONG reference;
if ( data->reference != 0 )
return;
KeInitializeEvent(& event, NotificationEvent, FALSE );
i.QuadPart = - 3500000;
KeRaiseIrql( HIGH_LEVEL,& old );
start_kqpc = KeQueryPerformanceCounter(& start_freq );
__asm
{
push eax
push edx
push ecx
rdtsc
lea ecx, start_ticks
mov [ecx + 4], edx
mov [ecx], eax
pop ecx
pop edx
pop eax
}
KeLowerIrql( old );
KeWaitForSingleObject(& event, UserRequest, KernelMode, TRUE ,& i );
KeRaiseIrql( HIGH_LEVEL,& old );
stop_kqpc = KeQueryPerformanceCounter(& stop_freq );
__asm
{
push eax
push edx
push ecx
rdtsc
lea ecx, stop_ticks
mov [ecx + 4], edx
mov [ecx], eax
pop ecx
pop edx
pop eax
}
KeLowerIrql( old );
delta = stop_ticks - start_ticks;
delta2 = stop_kqpc.QuadPart - start_kqpc.QuadPart;
if ( delta > 10000000000 )
{
delta /= 16;
delta2 /= 16;
}
reference = delta *( start_freq.QuadPart )/ delta2;
data->reference = reference / 1000;
if ( reference % 1000 > 500 )
data->reference ++;
data->reference *= 1000;
reference = data->reference;
KeQuerySystemTime(& system_time );
__asm
{
push eax
push edx
push ecx
rdtsc
lea ecx, curr_ticks
mov [ecx + 4], edx
mov [ecx], eax
pop ecx
pop edx
pop eax
}
tmp.tv_sec = -( LONG )( curr_ticks / reference );
tmp.tv_usec = -( LONG )(( curr_ticks % reference )* 1000000 / reference );
system_time.QuadPart -= 116444736000000000;
tmp.tv_sec += ( LONG )( system_time.QuadPart / 10000000 );
tmp.tv_usec += ( LONG )(( system_time.QuadPart % 10000000 )/ 10 );
if ( tmp.tv_usec < 0 )
{
tmp.tv_sec --;
tmp.tv_usec += 1000000;
}
data->start[0] = tmp;
IF_LOUD( DbgPrint( "Frequency %I64u MHz\n", data->reference );)
}
#endif //_X86_
#pragma optimize ("g",on) //Due to some weird behaviour of the optimizer of DDK build 2600
__inline VOID TIME_SYNCHRONIZE( struct time_conv * data )
{
ULONG NumberOfCpus, i;
KAFFINITY AffinityMask;
if ( data->reference != 0 )
return;
NumberOfCpus = NdisSystemProcessorCount();
if ( TimestampMode == TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_WITH_FIXUP || TimestampMode == TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_NO_FIXUP )
{
for ( i = 0 ; i < NumberOfCpus ; i ++ )
{
AffinityMask = ( 1 << i );
ZwSetInformationThread( NtCurrentThread(), ThreadAffinityMask, & AffinityMask, sizeof( KAFFINITY ));
SynchronizeOnCpu(&( data->start[i] ));
}
AffinityMask = 0xFFFFFFFF;
ZwSetInformationThread( NtCurrentThread(), ThreadAffinityMask, & AffinityMask, sizeof( KAFFINITY ));
data->reference = 1;
}
else
if ( TimestampMode == TIMESTAMPMODE_QUERYSYSTEMTIME )
{
//do nothing
data->reference = 1;
}
else
//
// This timestamp mode is supported on x86 (32 bit) only
//
#ifdef _X86_
if ( TimestampMode == TIMESTAMPMODE_RDTSC )
{
TimeSynchronizeRDTSC( data );
}
else
#endif // _X86_
{ //it should be only the normal case i.e. TIMESTAMPMODE_SINGLESYNCHRONIZATION
SynchronizeOnCpu( data->start );
data->reference = 1;
}
return;
}
#pragma optimize ("g",off) //Due to some weird behaviour of the optimizer of DDK build 2600
__inline void GetTimeKQPC( struct timeval * dst, struct time_conv * data )
{
LARGE_INTEGER PTime, TimeFreq;
LONG tmp;
ULONG CurrentCpu;
static struct timeval old_ts = {0, 0};
PTime = KeQueryPerformanceCounter(& TimeFreq );
tmp = ( LONG )( PTime.QuadPart / TimeFreq.QuadPart );
if ( TimestampMode == TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_WITH_FIXUP || TimestampMode == TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_NO_FIXUP )
{
//actually this code is ok only if we are guaranteed that no thread scheduling will take place.
CurrentCpu = KeGetCurrentProcessorNumber();
dst->tv_sec = data->start[CurrentCpu].tv_sec + tmp;
dst->tv_usec = data->start[CurrentCpu].tv_usec + ( LONG )(( PTime.QuadPart % TimeFreq.QuadPart )* 1000000 / TimeFreq.QuadPart );
if ( dst->tv_usec >= 1000000 )
{
dst->tv_sec ++;
dst->tv_usec -= 1000000;
}
if ( TimestampMode == TIMESTAMPMODE_SYNCHRONIZATION_ON_CPU_WITH_FIXUP )
{
if ( old_ts.tv_sec > dst->tv_sec || ( old_ts.tv_sec == dst->tv_sec && old_ts.tv_usec > dst->tv_usec ) )
* dst = old_ts;
else
old_ts = * dst;
}
}
else
{ //it should be only the normal case i.e. TIMESTAMPMODE_SINGLESYNCHRONIZATION
dst->tv_sec = data->start[0].tv_sec + tmp;
dst->tv_usec = data->start[0].tv_usec + ( LONG )(( PTime.QuadPart % TimeFreq.QuadPart )* 1000000 / TimeFreq.QuadPart );
if ( dst->tv_usec >= 1000000 )
{
dst->tv_sec ++;
dst->tv_usec -= 1000000;
}
}
}
//
// inline assembler is not supported with the current AMD64 compilers
// At the moment we simply disable this timestamping mode on AMD64.
// A solution would be to allocate a small memory from the non-paged
// pool, dump the instructions on that buffer, and then execute them.
// The non paged pool is needed since it's the only area of kernel
// data memory that is not subject to the NX protection.
// Or use some lower level trick, like using an assembler to assemble
// a small function for this.
//
#ifdef _X86_
__inline void GetTimeRDTSC( struct timeval * dst, struct time_conv * data )
{
ULONGLONG tmp = 0;
__asm
{
push eax
push edx
push ecx
rdtsc
lea ecx, tmp
mov [ecx + 4], edx
mov [ecx], eax
pop ecx
pop edx
pop eax
}
if ( data->reference == 0 )
{
return;
}
dst->tv_sec = ( LONG )( tmp / data->reference );
dst->tv_usec = ( LONG )(( tmp - dst->tv_sec * data->reference )* 1000000 / data->reference );
dst->tv_sec += data->start[0].tv_sec;
dst->tv_usec += data->start[0].tv_usec;
if ( dst->tv_usec >= 1000000 )
{
dst->tv_sec ++;
dst->tv_usec -= 1000000;
}
}
#endif //_X86_
__inline void GetTimeQST( struct timeval * dst, struct time_conv * data )
{
LARGE_INTEGER SystemTime;
KeQuerySystemTime(& SystemTime );
dst->tv_sec = ( LONG )( SystemTime.QuadPart / 10000000 - 11644473600 );
dst->tv_usec = ( LONG )(( SystemTime.QuadPart % 10000000 )/ 10 );
}
#pragma optimize ("g",on) //Due to some weird behaviour of the optimizer of DDK build 2600
__inline void GET_TIME( struct timeval * dst, struct time_conv * data )
{
//
// This timestamp mode is supported on x86 (32 bit) only
//
#ifdef _X86_
if ( TimestampMode == TIMESTAMPMODE_RDTSC )
{
GetTimeRDTSC( dst, data );
}
else
#endif // _X86_
if ( TimestampMode == TIMESTAMPMODE_QUERYSYSTEMTIME )
{
GetTimeQST( dst, data );
}
else
{
GetTimeKQPC( dst, data );
}
}
#else /*WIN_NT_DRIVER*/
__inline void FORCE_TIME( struct timeval * src, struct time_conv * dest )
{
dest->start[0] = * src;
}
__inline void GET_TIME( struct timeval * dst, struct time_conv * data )
{
* dst = data->start[0];
}
#endif /*WIN_NT_DRIVER*/
#endif /*_time_calls*/