Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/xen/timer/timer.c
Context not available. | |||||
#include <machine/clock.h> | #include <machine/clock.h> | ||||
#include <machine/_inttypes.h> | #include <machine/_inttypes.h> | ||||
#include <machine/smp.h> | #include <machine/smp.h> | ||||
#include <machine/pvclock.h> | |||||
#include <dev/xen/timer/timer.h> | #include <dev/xen/timer/timer.h> | ||||
Context not available. | |||||
struct eventtimer et; | struct eventtimer et; | ||||
}; | }; | ||||
/* Last time; this guarantees a monotonically increasing clock. */ | |||||
volatile uint64_t xen_timer_last_time = 0; | |||||
static void | static void | ||||
xentimer_identify(driver_t *driver, device_t parent) | xentimer_identify(driver_t *driver, device_t parent) | ||||
{ | { | ||||
Context not available. | |||||
return (BUS_PROBE_NOWILDCARD); | return (BUS_PROBE_NOWILDCARD); | ||||
} | } | ||||
/* | |||||
* Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, | |||||
* yielding a 64-bit result. | |||||
*/ | |||||
static inline uint64_t | |||||
scale_delta(uint64_t delta, uint32_t mul_frac, int shift) | |||||
{ | |||||
uint64_t product; | |||||
if (shift < 0) | |||||
delta >>= -shift; | |||||
else | |||||
delta <<= shift; | |||||
#if defined(__i386__) | |||||
{ | |||||
uint32_t tmp1, tmp2; | |||||
/** | |||||
* For i386, the formula looks like: | |||||
* | |||||
* lower = (mul_frac * (delta & UINT_MAX)) >> 32 | |||||
* upper = mul_frac * (delta >> 32) | |||||
* product = lower + upper | |||||
*/ | |||||
__asm__ ( | |||||
"mul %5 ; " | |||||
"mov %4,%%eax ; " | |||||
"mov %%edx,%4 ; " | |||||
"mul %5 ; " | |||||
"xor %5,%5 ; " | |||||
"add %4,%%eax ; " | |||||
"adc %5,%%edx ; " | |||||
: "=A" (product), "=r" (tmp1), "=r" (tmp2) | |||||
: "a" ((uint32_t)delta), "1" ((uint32_t)(delta >> 32)), | |||||
"2" (mul_frac) ); | |||||
} | |||||
#elif defined(__amd64__) | |||||
{ | |||||
unsigned long tmp; | |||||
__asm__ ( | |||||
"mulq %[mul_frac] ; shrd $32, %[hi], %[lo]" | |||||
: [lo]"=a" (product), [hi]"=d" (tmp) | |||||
: "0" (delta), [mul_frac]"rm"((uint64_t)mul_frac)); | |||||
} | |||||
#else | |||||
#error "xentimer: unsupported architecture" | |||||
#endif | |||||
return (product); | |||||
} | |||||
static uint64_t | |||||
get_nsec_offset(struct vcpu_time_info *tinfo) | |||||
{ | |||||
return (scale_delta(rdtsc() - tinfo->tsc_timestamp, | |||||
tinfo->tsc_to_system_mul, tinfo->tsc_shift)); | |||||
} | |||||
/* | |||||
* Read the current hypervisor system uptime value from Xen. | |||||
* See <xen/interface/xen.h> for a description of how this works. | |||||
*/ | |||||
static uint32_t | |||||
xen_fetch_vcpu_tinfo(struct vcpu_time_info *dst, struct vcpu_time_info *src) | |||||
{ | |||||
do { | |||||
dst->version = src->version; | |||||
rmb(); | |||||
dst->tsc_timestamp = src->tsc_timestamp; | |||||
dst->system_time = src->system_time; | |||||
dst->tsc_to_system_mul = src->tsc_to_system_mul; | |||||
dst->tsc_shift = src->tsc_shift; | |||||
rmb(); | |||||
} while ((src->version & 1) | (dst->version ^ src->version)); | |||||
return (dst->version); | |||||
} | |||||
/** | /** | ||||
* \brief Get the current time, in nanoseconds, since the hypervisor booted. | * \brief Get the current time, in nanoseconds, since the hypervisor booted. | ||||
* | * | ||||
* \param vcpu vcpu_info structure to fetch the time from. | * \param vcpu vcpu_info structure to fetch the time from. | ||||
* | * | ||||
* \note This function returns the current CPU's idea of this value, unless | |||||
* it happens to be less than another CPU's previously determined value. | |||||
*/ | */ | ||||
static uint64_t | static uint64_t | ||||
xen_fetch_vcpu_time(struct vcpu_info *vcpu) | xen_fetch_vcpu_time(struct vcpu_info *vcpu) | ||||
{ | { | ||||
struct vcpu_time_info dst; | struct pvclock_vcpu_time_info *time; | ||||
struct vcpu_time_info *src; | |||||
uint32_t pre_version; | |||||
uint64_t now; | |||||
volatile uint64_t last; | |||||
src = &vcpu->time; | |||||
do { | |||||
pre_version = xen_fetch_vcpu_tinfo(&dst, src); | |||||
barrier(); | |||||
now = dst.system_time + get_nsec_offset(&dst); | |||||
barrier(); | |||||
} while (pre_version != src->version); | |||||
/* | time = (struct pvclock_vcpu_time_info *) &vcpu->time; | ||||
* Enforce a monotonically increasing clock time across all | |||||
* VCPUs. If our time is too old, use the last time and return. | |||||
* Otherwise, try to update the last time. | |||||
*/ | |||||
do { | |||||
last = xen_timer_last_time; | |||||
if (last > now) { | |||||
now = last; | |||||
break; | |||||
} | |||||
} while (!atomic_cmpset_64(&xen_timer_last_time, last, now)); | |||||
return (now); | return (pvclock_get_timecount(time)); | ||||
} | } | ||||
static uint32_t | static uint32_t | ||||
Context not available. | |||||
xen_fetch_wallclock(struct timespec *ts) | xen_fetch_wallclock(struct timespec *ts) | ||||
{ | { | ||||
shared_info_t *src = HYPERVISOR_shared_info; | shared_info_t *src = HYPERVISOR_shared_info; | ||||
uint32_t version = 0; | struct pvclock_wall_clock *wc; | ||||
do { | wc = (struct pvclock_wall_clock *) &src->wc_version; | ||||
version = src->wc_version; | |||||
rmb(); | pvclock_get_wallclock(wc, ts); | ||||
ts->tv_sec = src->wc_sec; | |||||
ts->tv_nsec = src->wc_nsec; | |||||
rmb(); | |||||
} while ((src->wc_version & 1) | (version ^ src->wc_version)); | |||||
} | } | ||||
static void | static void | ||||
Context not available. | |||||
} | } | ||||
/* Reset the last uptime value */ | /* Reset the last uptime value */ | ||||
xen_timer_last_time = 0; | pvclock_resume(); | ||||
/* Reset the RTC clock */ | /* Reset the RTC clock */ | ||||
inittodr(time_second); | inittodr(time_second); | ||||
Context not available. |