diff -Naur xnu-1228.0.2.orig/osfmk/i386/misc_protos.h xnu-1228.0.2/osfmk/i386/misc_protos.h --- xnu-1228.0.2.orig/osfmk/i386/misc_protos.h 2008-01-05 13:34:12.000000000 +1100 +++ xnu-1228.0.2/osfmk/i386/misc_protos.h 2008-10-03 11:24:51.000000000 +1000 @@ -126,7 +126,7 @@ extern void rtc_clock_stepped( uint32_t new_frequency, uint32_t old_frequency); -extern void rtc_clock_napped(uint64_t); +extern void rtc_clock_napped(uint64_t, uint64_t); extern void x86_lowmem_free(void); diff -Naur xnu-1228.0.2.orig/osfmk/i386/rtclock.c xnu-1228.0.2/osfmk/i386/rtclock.c --- xnu-1228.0.2.orig/osfmk/i386/rtclock.c 2008-01-05 13:34:12.000000000 +1100 +++ xnu-1228.0.2/osfmk/i386/rtclock.c 2008-10-03 11:25:07.000000000 +1000 @@ -107,6 +107,28 @@ rtc_nanotime_t rtc_nanotime_info = {0,0,0,0,1,0}; +/* + * tsc_to_nanoseconds: + * + * Basic routine to convert a raw 64 bit TSC value to a + * 64 bit nanosecond value. The conversion is implemented + * based on the scale factor and an implicit 32 bit shift. + */ +static inline uint64_t +_tsc_to_nanoseconds(uint64_t value) +{ + asm volatile("movl %%edx,%%esi ;" + "mull %%ecx ;" + "movl %%edx,%%edi ;" + "movl %%esi,%%eax ;" + "mull %%ecx ;" + "addl %%edi,%%eax ;" + "adcl $0,%%edx " + : "+A" (value) : "c" (rtc_nanotime_info.scale) : "esi", "edi"); + + return (value); +} + static uint32_t deadline_to_decrementer( uint64_t deadline, @@ -234,26 +256,31 @@ /* * rtc_clock_napped: * - * Invoked from power manangement when we have awoken from a nap (C3/C4) - * during which the TSC lost counts. The nanotime data is updated according - * to the provided value which indicates the number of nanoseconds that the - * TSC was not counting. - * - * The caller must guarantee non-reentrancy. + * Invoked from power management when we exit from a low C-State (>= C4) + * and the TSC has stopped counting. The nanotime data is updated according + * to the provided value which represents the new value for nanotime. */ void -rtc_clock_napped( - uint64_t delta) +rtc_clock_napped(uint64_t base, uint64_t tsc_base) { rtc_nanotime_t *rntp = &rtc_nanotime_info; - uint32_t generation; + uint64_t oldnsecs; + uint64_t newnsecs; + uint64_t tsc; assert(!ml_get_interrupts_enabled()); - generation = rntp->generation; - rntp->generation = 0; - rntp->ns_base += delta; - rntp->generation = ((generation + 1) != 0) ? (generation + 1) : 1; - rtc_nanotime_set_commpage(rntp); + tsc = rdtsc64(); + oldnsecs = rntp->ns_base + _tsc_to_nanoseconds(tsc - rntp->tsc_base); + newnsecs = base + _tsc_to_nanoseconds(tsc - tsc_base); + + /* + * Only update the base values if time using the new base values + * is later than the time using the old base values. + */ + if (oldnsecs < newnsecs) { + _rtc_nanotime_store(tsc_base, base, rntp->scale, rntp->shift, rntp); + rtc_nanotime_set_commpage(rntp); + } } void diff -Naur xnu-1228.0.2.orig/osfmk/i386/tsc.c xnu-1228.0.2/osfmk/i386/tsc.c --- xnu-1228.0.2.orig/osfmk/i386/tsc.c 2008-01-05 13:34:12.000000000 +1100 +++ xnu-1228.0.2/osfmk/i386/tsc.c 2008-10-03 11:38:13.000000000 +1000 @@ -160,13 +160,16 @@ * Get the TSC increment. The TSC is incremented by this * on every bus tick. Calculate the TSC conversion factors * to and from nano-seconds. + * The tsc granularity is also called the "bus ratio". If the N/2 bit + * is set this indicates the bus ration is 0.5 more than this - i.e. + * that the true bus ratio is (2*tscGranularity + 1)/2. */ if (cpuid_info()->cpuid_family == CPU_FAMILY_PENTIUM_M) { uint64_t prfsts; prfsts = rdmsr64(IA32_PERF_STS); tscGranularity = (uint32_t)bitfield(prfsts, 44, 40); - N_by_2_bus_ratio = prfsts & bit(46); + N_by_2_bus_ratio = (prfsts & bit(46)) != 0; } else { panic("rtclock_init: unknown CPU family: 0x%X\n", @@ -174,20 +177,20 @@ } if (N_by_2_bus_ratio) - tscFCvtt2n = busFCvtt2n * 2 / (uint64_t)tscGranularity; + tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); else - tscFCvtt2n = busFCvtt2n / (uint64_t)tscGranularity; + tscFCvtt2n = busFCvtt2n / tscGranularity; tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; kprintf(" TSC: Frequency = %6d.%04dMHz, " - "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld\n", + "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld%s\n", (uint32_t)(tscFreq / Mega), (uint32_t)(tscFreq % Mega), (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, - tscGranularity); + tscGranularity, N_by_2_bus_ratio ? " (N/2)" : ""); /* * Calculate conversion from BUS to TSC