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