-/*
- * create_mul_quant_GHZ
- * create a constant used to multiply the TSC by to convert to nanoseconds.
- * This is a 32 bit number and the TSC *MUST* have a frequency higher than
- * 1000Mhz for this routine to work.
- *
- * The theory here is that we know how many TSCs-per-sec the processor runs at.
- * Normally to convert this to nanoseconds you would multiply the current
- * timestamp by 1000000000 (a billion) then divide by TSCs-per-sec.
- * Unfortunatly the TSC is 64 bits which would leave us with 96 bit intermediate
- * results from the multiply that must be divided by.
- * Usually thats
- * uint96 = tsc * numer
- * nanos = uint96 / denom
- * Instead, we create this quant constant and it becomes the numerator,
- * the denominator can then be 0x100000000 which makes our division as simple as
- * forgetting the lower 32 bits of the result. We can also pass this number to
- * user space as the numer and pass 0xFFFFFFFF (RTC_FAST_DENOM) as the denom to
- * convert raw counts * to nanos. The difference is so small as to be
- * undetectable by anything.
- *
- * Unfortunatly we can not do this for sub GHZ processors. In this case, all
- * we do is pass the CPU speed in raw as the denom and we pass in 1000000000
- * as the numerator. No short cuts allowed
- */
-#define RTC_FAST_DENOM 0xFFFFFFFF
-inline static uint32_t
-create_mul_quant_GHZ(int shift, uint32_t quant)
-{
- return (uint32_t)((((uint64_t)NSEC_PER_SEC/20) << shift) / quant);
-}
-/*
- * This routine takes a value of raw TSC ticks and applies the passed mul_quant
- * generated by create_mul_quant() This is our internal routine for creating
- * nanoseconds.
- * Since we don't really have uint96_t this routine basically does this....
- * uint96_t intermediate = (*value) * scale
- * return (intermediate >> 32)
- */
-inline static uint64_t
-fast_get_nano_from_abs(uint64_t value, int scale)
-{
- asm (" movl %%edx,%%esi \n\t"
- " mull %%ecx \n\t"
- " movl %%edx,%%edi \n\t"
- " movl %%esi,%%eax \n\t"
- " mull %%ecx \n\t"
- " xorl %%ecx,%%ecx \n\t"
- " addl %%edi,%%eax \n\t"
- " adcl %%ecx,%%edx "
- : "+A" (value)
- : "c" (scale)
- : "%esi", "%edi");
- return value;
-}
-
-/*
- * This routine basically does this...
- * ts.tv_sec = nanos / 1000000000; create seconds
- * ts.tv_nsec = nanos % 1000000000; create remainder nanos
- */
-inline static mach_timespec_t
-nanos_to_timespec(uint64_t nanos)
-{
- union {
- mach_timespec_t ts;
- uint64_t u64;
- } ret;
- ret.u64 = nanos;
- asm volatile("divl %1" : "+A" (ret.u64) : "r" (NSEC_PER_SEC));
- return ret.ts;
-}
-
-/*
- * The following two routines perform the 96 bit arithmetic we need to
- * convert generic absolute<->nanoseconds
- * The multiply routine takes a uint64_t and a uint32_t and returns the result
- * in a uint32_t[3] array.
- * The divide routine takes this uint32_t[3] array and divides it by a uint32_t
- * returning a uint64_t
- */
-inline static void
-longmul(uint64_t *abstime, uint32_t multiplicand, uint32_t *result)
-{
- asm volatile(
- " pushl %%ebx \n\t"
- " movl %%eax,%%ebx \n\t"
- " movl (%%eax),%%eax \n\t"
- " mull %%ecx \n\t"
- " xchg %%eax,%%ebx \n\t"
- " pushl %%edx \n\t"
- " movl 4(%%eax),%%eax \n\t"
- " mull %%ecx \n\t"
- " movl %2,%%ecx \n\t"
- " movl %%ebx,(%%ecx) \n\t"
- " popl %%ebx \n\t"
- " addl %%ebx,%%eax \n\t"
- " popl %%ebx \n\t"
- " movl %%eax,4(%%ecx) \n\t"
- " adcl $0,%%edx \n\t"
- " movl %%edx,8(%%ecx) // and save it"
- : : "a"(abstime), "c"(multiplicand), "m"(result));
-
-}
-
-inline static uint64_t
-longdiv(uint32_t *numer, uint32_t denom)
-{
- uint64_t result;
- asm volatile(
- " pushl %%ebx \n\t"
- " movl %%eax,%%ebx \n\t"
- " movl 8(%%eax),%%edx \n\t"
- " movl 4(%%eax),%%eax \n\t"
- " divl %%ecx \n\t"
- " xchg %%ebx,%%eax \n\t"
- " movl (%%eax),%%eax \n\t"
- " divl %%ecx \n\t"
- " xchg %%ebx,%%edx \n\t"
- " popl %%ebx \n\t"
- : "=A"(result) : "a"(numer),"c"(denom));
- return result;
-}
-
-/*
- * Enable or disable timer 2.
- * Port 0x61 controls timer 2:
- * bit 0 gates the clock,
- * bit 1 gates output to speaker.
- */
-inline static void
-enable_PIT2(void)
-{
- asm volatile(
- " inb $0x61,%%al \n\t"
- " and $0xFC,%%al \n\t"
- " or $1,%%al \n\t"
- " outb %%al,$0x61 \n\t"
- : : : "%al" );
-}
-
-inline static void
-disable_PIT2(void)
-{
- asm volatile(
- " inb $0x61,%%al \n\t"
- " and $0xFC,%%al \n\t"
- " outb %%al,$0x61 \n\t"
- : : : "%al" );
-}
-
-inline static void
-set_PIT2(int value)
-{
-/*
- * First, tell the clock we are going to write 16 bits to the counter
- * and enable one-shot mode (command 0xB8 to port 0x43)
- * Then write the two bytes into the PIT2 clock register (port 0x42).
- * Loop until the value is "realized" in the clock,
- * this happens on the next tick.
- */
- asm volatile(
- " movb $0xB8,%%al \n\t"
- " outb %%al,$0x43 \n\t"
- " movb %%dl,%%al \n\t"
- " outb %%al,$0x42 \n\t"
- " movb %%dh,%%al \n\t"
- " outb %%al,$0x42 \n"
-"1: inb $0x42,%%al \n\t"
- " inb $0x42,%%al \n\t"
- " cmp %%al,%%dh \n\t"
- " jne 1b"
- : : "d"(value) : "%al");
-}
-
-inline static uint64_t
-get_PIT2(unsigned int *value)
-{
- register uint64_t result;
-/*
- * This routine first latches the time (command 0x80 to port 0x43),
- * then gets the time stamp so we know how long the read will take later.
- * Read (from port 0x42) and return the current value of the timer.
- */
- asm volatile(
- " xorl %%ecx,%%ecx \n\t"
- " movb $0x80,%%al \n\t"
- " outb %%al,$0x43 \n\t"
- " rdtsc \n\t"
- " pushl %%eax \n\t"
- " inb $0x42,%%al \n\t"
- " movb %%al,%%cl \n\t"
- " inb $0x42,%%al \n\t"
- " movb %%al,%%ch \n\t"
- " popl %%eax "
- : "=A"(result), "=c"(*value));
- return result;
-}
-
-/*
- * timeRDTSC()
- * This routine sets up PIT counter 2 to count down 1/20 of a second.
- * It pauses until the value is latched in the counter
- * and then reads the time stamp counter to return to the caller.
- */
-static uint64_t
-timeRDTSC(void)
-{
- int attempts = 0;
- uint64_t latchTime;
- uint64_t saveTime,intermediate;
- unsigned int timerValue, lastValue;
- boolean_t int_enabled;
- /*
- * Table of correction factors to account for
- * - timer counter quantization errors, and
- * - undercounts 0..5
- */
-#define SAMPLE_CLKS_EXACT (((double) CLKNUM) / 20.0)
-#define SAMPLE_CLKS_INT ((int) CLKNUM / 20)
-#define SAMPLE_NSECS (2000000000LL)
-#define SAMPLE_MULTIPLIER (((double)SAMPLE_NSECS)*SAMPLE_CLKS_EXACT)
-#define ROUND64(x) ((uint64_t)((x) + 0.5))
- uint64_t scale[6] = {
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-0)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-1)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-2)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-3)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-4)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-5))
- };
-
- int_enabled = ml_set_interrupts_enabled(FALSE);
-
-restart:
- if (attempts >= 2)
- panic("timeRDTSC() calibation failed with %d attempts\n", attempts);
- attempts++;
- enable_PIT2(); // turn on PIT2
- set_PIT2(0); // reset timer 2 to be zero
- latchTime = rdtsc64(); // get the time stamp to time
- latchTime = get_PIT2(&timerValue) - latchTime; // time how long this takes
- set_PIT2(SAMPLE_CLKS_INT); // set up the timer for (almost) 1/20th a second
- saveTime = rdtsc64(); // now time how long a 20th a second is...
- get_PIT2(&lastValue);
- get_PIT2(&lastValue); // read twice, first value may be unreliable
- do {
- intermediate = get_PIT2(&timerValue);
- if (timerValue > lastValue) {
- printf("Hey we are going backwards! %u -> %u, restarting timing\n",
- timerValue,lastValue);
- set_PIT2(0);
- disable_PIT2();
- goto restart;
- }
- lastValue = timerValue;
- } while (timerValue > 5);
- kprintf("timerValue %d\n",timerValue);
- kprintf("intermediate 0x%016llx\n",intermediate);
- kprintf("saveTime 0x%016llx\n",saveTime);
-
- intermediate -= saveTime; // raw count for about 1/20 second
- intermediate *= scale[timerValue]; // rescale measured time spent
- intermediate /= SAMPLE_NSECS; // so its exactly 1/20 a second
- intermediate += latchTime; // add on our save fudge
-
- set_PIT2(0); // reset timer 2 to be zero
- disable_PIT2(); // turn off PIT 2
-
- ml_set_interrupts_enabled(int_enabled);
- return intermediate;
-}
-
-static uint64_t
-tsc_to_nanoseconds(uint64_t abstime)
-{
- uint32_t numer;
- uint32_t denom;
- uint32_t intermediate[3];
-
- numer = rtclock.timebase_const.numer;
- denom = rtclock.timebase_const.denom;
- if (denom == RTC_FAST_DENOM) {
- abstime = fast_get_nano_from_abs(abstime, numer);
- } else {
- longmul(&abstime, numer, intermediate);
- abstime = longdiv(intermediate, denom);
- }
- return abstime;
-}
-
-inline static mach_timespec_t
-tsc_to_timespec(void)
-{
- uint64_t currNanos;
- currNanos = rtc_nanotime_read();
- return nanos_to_timespec(currNanos);
-}
-
-#define DECREMENTER_MAX UINT_MAX
-static uint32_t
-deadline_to_decrementer(
- uint64_t deadline,
- uint64_t now)
-{
- uint64_t delta;
-
- if (deadline <= now)
- return rtc_decrementer_min;
- else {
- delta = deadline - now;
- return MIN(MAX(rtc_decrementer_min,delta),DECREMENTER_MAX);
- }
-}
-
-static inline uint64_t
-lapic_time_countdown(uint32_t initial_count)
-{
- boolean_t state;
- uint64_t start_time;
- uint64_t stop_time;
- lapic_timer_count_t count;
-
- state = ml_set_interrupts_enabled(FALSE);
- lapic_set_timer(FALSE, one_shot, divide_by_1, initial_count);
- start_time = rdtsc64();
- do {
- lapic_get_timer(NULL, NULL, NULL, &count);
- } while (count > 0);
- stop_time = rdtsc64();
- ml_set_interrupts_enabled(state);
-
- return tsc_to_nanoseconds(stop_time - start_time);
-}
-
-static void
-rtc_lapic_timer_calibrate(void)