#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "xen.h" #define LOG(a) extern volatile shared_info_t *HYPERVISOR_shared_info; Lock xtime_lock; unsigned long cpu_hz; /* get this from Xen, used elsewhere */ static unsigned int rdtsc_bitshift; static u32 st_scale_f; /* convert ticks -> usecs */ static u32 st_scale_i; /* convert ticks -> usecs */ /* These are peridically updated in shared_info, and then copied here. */ static u32 shadow_tsc_stamp; static u64 shadow_system_time; static u32 shadow_time_version; static u64 shadow_time_nsec; static u32 shadow_tv_sec; static u32 shadow_tv_usec; /* * We use this to ensure that gettimeofday() is monotonically increasing. We * only break this guarantee if the wall clock jumps backwards "a long way". */ static u64 last_seen_nsec = 0; /* Periodically take synchronised time base from Xen, if we need it. */ static long last_update_from_xen; /* UTC seconds when last read Xen clock. */ /* Keep track of last time we did processing/updating of jiffies and xtime. */ static u64 processed_system_time; /* System time (ns) at last processing. */ #define NS_PER_TICK (1000000000ULL/HZ) /* Dynamically-mapped IRQs. */ static int time_irq, debug_irq; enum { Freq= 1193182, /* Real clock frequency */ Tickshift=8, /* extra accuracy */ MaxPeriod=Freq/HZ, MinPeriod=Freq/(100*HZ), }; /* * Reads a consistent set of time-base values from Xen, into a shadow data * area. Must be called with the xtime_lock held for writing. */ static void __get_time_values_from_xen(void) { do { shadow_time_version = HYPERVISOR_shared_info->time_version2; shadow_tv_sec = HYPERVISOR_shared_info->wc_sec; shadow_tv_usec = HYPERVISOR_shared_info->wc_usec; shadow_tsc_stamp = HYPERVISOR_shared_info->tsc_timestamp.tsc_bits; shadow_system_time = HYPERVISOR_shared_info->system_time; } while ( shadow_time_version != HYPERVISOR_shared_info->time_version1 ); } /* set next timeout for 1 second ... */ void xentimerinit(void) { long HYPERVISOR_set_timer_op(uvlong timeout); uvlong nexttimeout = 1000000000; /* no lock here we're not multiprocessing */ __get_time_values_from_xen(); HYPERVISOR_set_timer_op(nexttimeout); } /* just get it from the shared info */ void guesscpuhz() { cpu_hz = HYPERVISOR_shared_info->cpu_freq; m->cpuhz = cpu_hz; m->cpumhz = cpu_hz / 1000000L; } void xentimerset(uvlong next) { long HYPERVISOR_set_timer_op(uvlong timeout); LOG(dp(" settin timer to 0x%llx\n", next<<10)); HYPERVISOR_set_timer_op(next<<10); } void xentimerclock(Ureg* ureg, void*) { LOG(dp("xentimerclock\n")); timerintr(ureg, 0); } void xentimerenable(void) { intrenable(VIRQ_TIMER, xentimerclock, nil, 0, "Xen Timer"); LOG(dp("xentimer enabled\n")); } void xentimerlink(void) { LOG(dp("XENTIMER LINK\n")); xentimerenable(); } /* * Read the epoch time in microseconds. The returned hz will be 1000000 */ uvlong xentimerread(uvlong *hz) { uvlong retval; __get_time_values_from_xen(); // dp("xentimerread: hz %p cpu_hz %d shadow_system_time %llx\n", // hz, cpu_hz, shadow_system_time); if (hz) *hz = 1000000; // dp("xtr: sec 0x%ulx usec 0x%ulx\n", shadow_tv_sec, shadow_tv_usec); retval = shadow_tv_sec; retval *= 1000000; retval += shadow_tv_usec; // retval = shadow_tv_sec<<32 + shadow_tv_usec; // return shadow_system_time>>10; // dp("xentimerread: return 0x%llx\n", retval); return retval; } void delay(int millisecs) { millisecs *= m->loopconst; if(millisecs <= 0) millisecs = 1; aamloop(millisecs); } void microdelay(int microsecs) { microsecs *= m->loopconst; microsecs /= 1000; if(microsecs <= 0) microsecs = 1; aamloop(microsecs); } /* * performance measurement ticks. must be low overhead. * doesn't have to count over a second. */ ulong perfticks(void) { uvlong x; LOG(dp("perfticks\n")); if(m->havetsc) cycles(&x); else { __get_time_values_from_xen(); x = shadow_tsc_stamp; } LOG(dp("perfticks returns 0x%ulx\n", x)); return x; }