plan9fox/sys/src/9/xen/xentimer.c
cinap_lenrek a05bab362f pc, pc64: add minimal HPET driver to measure LAPIC and TSC frequencies
This adds the new function pointer PCArch.clockinit(),
which is a timer dependent initialization routine.
It also takes over the job of guesscpuhz(). This way, the
architecture ident code can switch between different
timers (i8253, HPET and XEN timer).
2021-01-17 21:21:12 +01:00

137 lines
2.3 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
static vcpu_time_info_t shadow[MAX_VIRT_CPUS]; // XXX should be in Mach
static ulong wallclock;
static ulong wallclocksystime;
/*
* Return a consistent set of time parameters.
*/
static vcpu_time_info_t *
getshadow(void)
{
vcpu_time_info_t *s, *t;
t = &HYPERVISOR_shared_info->vcpu_info[m->machno].time;
s = &shadow[m->machno]; // XXX place in mach struct
while(t->version != s->version) {
if (t->version&1)
continue;
s->version = t->version;
s->tsc_timestamp = t->tsc_timestamp;
s->system_time = t->system_time;
s->tsc_to_system_mul = t->tsc_to_system_mul;
s->tsc_shift = t->tsc_shift;
}
return s;
}
/* just get it from the shared info */
void
xentimerinit(void)
{
vcpu_time_info_t *t;
t = getshadow();
m->cpuhz = (1000000000LL << 32) / t->tsc_to_system_mul;
if(t->tsc_shift < 0)
m->cpuhz <<= -t->tsc_shift;
else
m->cpuhz >>= t->tsc_shift;
m->cpumhz = m->cpuhz / 1000000L;
}
void
xentimerset(uvlong next)
{
uvlong soon;
soon = fastticks(0) + 100000;
if (next < soon)
next = soon;
HYPERVISOR_set_timer_op(next);
}
void
xentimerclock(Ureg* ureg, void*)
{
timerintr(ureg, 0);
}
void
xentimerenable(void)
{
intrenable(VIRQ_TIMER, xentimerclock, nil, 0, "clock");
}
uvlong
xentimerread(uvlong *hz)
{
uvlong x;
uvlong delta, sdelta;
vcpu_time_info_t *t;
t = getshadow();
cycles(&x);
delta = x - t->tsc_timestamp;
if (t->tsc_shift < 0)
delta >>= -t->tsc_shift;
else
delta <<= t->tsc_shift;
mul64fract(&sdelta, delta, t->tsc_to_system_mul);
x = t->system_time + sdelta;
if (HYPERVISOR_shared_info->wc_sec != wallclock) {
wallclock = HYPERVISOR_shared_info->wc_sec;
wallclocksystime = x;
}
if (hz)
*hz = 1000000000;
return x;
}
ulong
xenwallclock()
{
ulong elapsed;
elapsed = (ulong)((xentimerread(0) - wallclocksystime)/1000000000);
return wallclock + elapsed;
}
void
microdelay(int microsecs)
{
uvlong targ, hz;
targ = xentimerread(&hz);
targ += microsecs * hz / 1000000;
while(xentimerread(0) < targ)
continue;
}
void
delay(int millisecs)
{
microdelay(millisecs * 1000);
}
/*
* performance measurement ticks. must be low overhead.
* doesn't have to count over a second.
*/
ulong
perfticks(void)
{
uvlong x;
cycles(&x);
return x;
}