fd27593538
The "wall clock" in the current code is actually the hypervisor start time. The time of day is the "start time" plus the hypervisor "uptime". Large enough bumps in the dom0 clock lead to a hypervisor "bump" which is implemented as a bump in the start time, not the uptime. The clock.c routines were reading in the hypervisor start time and then using this as the TOD. This meant that any hypervisor time bump would cause the FreeBSD DomU to set its TOD to the hypervisor start time, rather than the actual TOD. This fix is a bit hacky and some reshuffling should be done later on to clarify what is going on. I've left the wall clock code alone. (The code which updates shadow_tv and shadow_tv_version.) A new routine adds the uptime to the shadow_tv, which is then used to update the TOD. I've included some debugging so it is obvious when the clock is nudged. PR: 135008
907 lines
21 KiB
C
907 lines
21 KiB
C
/*-
|
|
* Copyright (c) 1990 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* William Jolitz and Don Ahn.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/* #define DELAYDEBUG */
|
|
/*
|
|
* Routines to handle clock hardware.
|
|
*/
|
|
|
|
#include "opt_ddb.h"
|
|
#include "opt_clock.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/clock.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/time.h>
|
|
#include <sys/timetc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/limits.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/cons.h>
|
|
#include <sys/power.h>
|
|
|
|
#include <machine/clock.h>
|
|
#include <machine/cputypes.h>
|
|
#include <machine/frame.h>
|
|
#include <machine/intr_machdep.h>
|
|
#include <machine/md_var.h>
|
|
#include <machine/psl.h>
|
|
#if defined(SMP)
|
|
#include <machine/smp.h>
|
|
#endif
|
|
#include <machine/specialreg.h>
|
|
#include <machine/timerreg.h>
|
|
|
|
#include <i386/isa/icu.h>
|
|
#include <i386/isa/isa.h>
|
|
#include <isa/rtc.h>
|
|
|
|
#include <xen/xen_intr.h>
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <machine/pmap.h>
|
|
#include <xen/hypervisor.h>
|
|
#include <machine/xen/xen-os.h>
|
|
#include <machine/xen/xenfunc.h>
|
|
#include <xen/interface/vcpu.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/xen/xen_clock_util.h>
|
|
|
|
/*
|
|
* 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
|
|
* can use a simple formula for leap years.
|
|
*/
|
|
#define LEAPYEAR(y) (!((y) % 4))
|
|
#define DAYSPERYEAR (28+30*4+31*7)
|
|
|
|
#ifndef TIMER_FREQ
|
|
#define TIMER_FREQ 1193182
|
|
#endif
|
|
|
|
#ifdef CYC2NS_SCALE_FACTOR
|
|
#undef CYC2NS_SCALE_FACTOR
|
|
#endif
|
|
#define CYC2NS_SCALE_FACTOR 10
|
|
|
|
/* Values for timerX_state: */
|
|
#define RELEASED 0
|
|
#define RELEASE_PENDING 1
|
|
#define ACQUIRED 2
|
|
#define ACQUIRE_PENDING 3
|
|
|
|
struct mtx clock_lock;
|
|
#define RTC_LOCK_INIT \
|
|
mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE)
|
|
#define RTC_LOCK mtx_lock_spin(&clock_lock)
|
|
#define RTC_UNLOCK mtx_unlock_spin(&clock_lock)
|
|
|
|
int adjkerntz; /* local offset from GMT in seconds */
|
|
int clkintr_pending;
|
|
int pscnt = 1;
|
|
int psdiv = 1;
|
|
int wall_cmos_clock;
|
|
u_int timer_freq = TIMER_FREQ;
|
|
static int independent_wallclock;
|
|
static int xen_disable_rtc_set;
|
|
static u_long cyc2ns_scale;
|
|
static struct timespec shadow_tv;
|
|
static uint32_t shadow_tv_version; /* XXX: lazy locking */
|
|
static uint64_t processed_system_time; /* stime (ns) at last processing. */
|
|
|
|
static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
|
|
|
|
SYSCTL_INT(_machdep, OID_AUTO, independent_wallclock,
|
|
CTLFLAG_RW, &independent_wallclock, 0, "");
|
|
SYSCTL_INT(_machdep, OID_AUTO, xen_disable_rtc_set,
|
|
CTLFLAG_RW, &xen_disable_rtc_set, 1, "");
|
|
|
|
|
|
#define do_div(n,base) ({ \
|
|
unsigned long __upper, __low, __high, __mod, __base; \
|
|
__base = (base); \
|
|
__asm("":"=a" (__low), "=d" (__high):"A" (n)); \
|
|
__upper = __high; \
|
|
if (__high) { \
|
|
__upper = __high % (__base); \
|
|
__high = __high / (__base); \
|
|
} \
|
|
__asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \
|
|
__asm("":"=A" (n):"a" (__low),"d" (__high)); \
|
|
__mod; \
|
|
})
|
|
|
|
|
|
#define NS_PER_TICK (1000000000ULL/hz)
|
|
|
|
#define rdtscll(val) \
|
|
__asm__ __volatile__("rdtsc" : "=A" (val))
|
|
|
|
|
|
/* convert from cycles(64bits) => nanoseconds (64bits)
|
|
* basic equation:
|
|
* ns = cycles / (freq / ns_per_sec)
|
|
* ns = cycles * (ns_per_sec / freq)
|
|
* ns = cycles * (10^9 / (cpu_mhz * 10^6))
|
|
* ns = cycles * (10^3 / cpu_mhz)
|
|
*
|
|
* Then we use scaling math (suggested by george@mvista.com) to get:
|
|
* ns = cycles * (10^3 * SC / cpu_mhz) / SC
|
|
* ns = cycles * cyc2ns_scale / SC
|
|
*
|
|
* And since SC is a constant power of two, we can convert the div
|
|
* into a shift.
|
|
* -johnstul@us.ibm.com "math is hard, lets go shopping!"
|
|
*/
|
|
static inline void set_cyc2ns_scale(unsigned long cpu_mhz)
|
|
{
|
|
cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz;
|
|
}
|
|
|
|
static inline unsigned long long cycles_2_ns(unsigned long long cyc)
|
|
{
|
|
return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
uint32_t tmp1, tmp2;
|
|
|
|
if ( shift < 0 )
|
|
delta >>= -shift;
|
|
else
|
|
delta <<= shift;
|
|
|
|
__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) );
|
|
|
|
return product;
|
|
}
|
|
|
|
static uint64_t
|
|
get_nsec_offset(struct shadow_time_info *shadow)
|
|
{
|
|
uint64_t now, delta;
|
|
rdtscll(now);
|
|
delta = now - shadow->tsc_timestamp;
|
|
return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift);
|
|
}
|
|
|
|
static void update_wallclock(void)
|
|
{
|
|
shared_info_t *s = HYPERVISOR_shared_info;
|
|
|
|
do {
|
|
shadow_tv_version = s->wc_version;
|
|
rmb();
|
|
shadow_tv.tv_sec = s->wc_sec;
|
|
shadow_tv.tv_nsec = s->wc_nsec;
|
|
rmb();
|
|
}
|
|
while ((s->wc_version & 1) | (shadow_tv_version ^ s->wc_version));
|
|
|
|
}
|
|
|
|
static void
|
|
add_uptime_to_wallclock(void)
|
|
{
|
|
struct timespec ut;
|
|
|
|
xen_fetch_uptime(&ut);
|
|
timespecadd(&shadow_tv, &ut);
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
shared_info_t *s = HYPERVISOR_shared_info;
|
|
struct vcpu_time_info *src;
|
|
struct shadow_time_info *dst;
|
|
uint32_t pre_version, post_version;
|
|
|
|
src = &s->vcpu_info[smp_processor_id()].time;
|
|
dst = &per_cpu(shadow_time, smp_processor_id());
|
|
|
|
spinlock_enter();
|
|
do {
|
|
pre_version = dst->version = src->version;
|
|
rmb();
|
|
dst->tsc_timestamp = src->tsc_timestamp;
|
|
dst->system_timestamp = src->system_time;
|
|
dst->tsc_to_nsec_mul = src->tsc_to_system_mul;
|
|
dst->tsc_shift = src->tsc_shift;
|
|
rmb();
|
|
post_version = src->version;
|
|
}
|
|
while ((pre_version & 1) | (pre_version ^ post_version));
|
|
|
|
dst->tsc_to_usec_mul = dst->tsc_to_nsec_mul / 1000;
|
|
spinlock_exit();
|
|
}
|
|
|
|
|
|
static inline int time_values_up_to_date(int cpu)
|
|
{
|
|
struct vcpu_time_info *src;
|
|
struct shadow_time_info *dst;
|
|
|
|
src = &HYPERVISOR_shared_info->vcpu_info[cpu].time;
|
|
dst = &per_cpu(shadow_time, cpu);
|
|
|
|
rmb();
|
|
return (dst->version == src->version);
|
|
}
|
|
|
|
static unsigned xen_get_timecount(struct timecounter *tc);
|
|
|
|
static struct timecounter xen_timecounter = {
|
|
xen_get_timecount, /* get_timecount */
|
|
0, /* no poll_pps */
|
|
~0u, /* counter_mask */
|
|
0, /* frequency */
|
|
"ixen", /* name */
|
|
0 /* quality */
|
|
};
|
|
|
|
static int
|
|
clkintr(void *arg)
|
|
{
|
|
int64_t delta_cpu, delta;
|
|
struct trapframe *frame = (struct trapframe *)arg;
|
|
int cpu = smp_processor_id();
|
|
struct shadow_time_info *shadow = &per_cpu(shadow_time, cpu);
|
|
|
|
do {
|
|
__get_time_values_from_xen();
|
|
|
|
delta = delta_cpu =
|
|
shadow->system_timestamp + get_nsec_offset(shadow);
|
|
delta -= processed_system_time;
|
|
delta_cpu -= per_cpu(processed_system_time, cpu);
|
|
|
|
} while (!time_values_up_to_date(cpu));
|
|
|
|
if (unlikely(delta < (int64_t)0) || unlikely(delta_cpu < (int64_t)0)) {
|
|
printf("Timer ISR: Time went backwards: %lld\n", delta);
|
|
return (FILTER_HANDLED);
|
|
}
|
|
|
|
/* Process elapsed ticks since last call. */
|
|
while (delta >= NS_PER_TICK) {
|
|
delta -= NS_PER_TICK;
|
|
processed_system_time += NS_PER_TICK;
|
|
per_cpu(processed_system_time, cpu) += NS_PER_TICK;
|
|
if (PCPU_GET(cpuid) == 0)
|
|
hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
|
|
else
|
|
hardclock_cpu(TRAPF_USERMODE(frame));
|
|
}
|
|
/*
|
|
* Take synchronised time from Xen once a minute if we're not
|
|
* synchronised ourselves, and we haven't chosen to keep an independent
|
|
* time base.
|
|
*/
|
|
|
|
if (shadow_tv_version != HYPERVISOR_shared_info->wc_version) {
|
|
printf("[XEN] hypervisor wallclock nudged; nudging TOD.\n");
|
|
update_wallclock();
|
|
add_uptime_to_wallclock();
|
|
tc_setclock(&shadow_tv);
|
|
}
|
|
|
|
/* XXX TODO */
|
|
return (FILTER_HANDLED);
|
|
}
|
|
static uint32_t
|
|
getit(void)
|
|
{
|
|
struct shadow_time_info *shadow;
|
|
uint64_t time;
|
|
uint32_t local_time_version;
|
|
|
|
shadow = &per_cpu(shadow_time, smp_processor_id());
|
|
|
|
do {
|
|
local_time_version = shadow->version;
|
|
barrier();
|
|
time = shadow->system_timestamp + get_nsec_offset(shadow);
|
|
if (!time_values_up_to_date(smp_processor_id()))
|
|
__get_time_values_from_xen(/*cpu */);
|
|
barrier();
|
|
} while (local_time_version != shadow->version);
|
|
|
|
return (time);
|
|
}
|
|
|
|
|
|
/*
|
|
* XXX: timer needs more SMP work.
|
|
*/
|
|
void
|
|
i8254_init(void)
|
|
{
|
|
|
|
RTC_LOCK_INIT;
|
|
}
|
|
|
|
/*
|
|
* Wait "n" microseconds.
|
|
* Relies on timer 1 counting down from (timer_freq / hz)
|
|
* Note: timer had better have been programmed before this is first used!
|
|
*/
|
|
void
|
|
DELAY(int n)
|
|
{
|
|
int delta, ticks_left;
|
|
uint32_t tick, prev_tick;
|
|
#ifdef DELAYDEBUG
|
|
int getit_calls = 1;
|
|
int n1;
|
|
static int state = 0;
|
|
|
|
if (state == 0) {
|
|
state = 1;
|
|
for (n1 = 1; n1 <= 10000000; n1 *= 10)
|
|
DELAY(n1);
|
|
state = 2;
|
|
}
|
|
if (state == 1)
|
|
printf("DELAY(%d)...", n);
|
|
#endif
|
|
/*
|
|
* Read the counter first, so that the rest of the setup overhead is
|
|
* counted. Guess the initial overhead is 20 usec (on most systems it
|
|
* takes about 1.5 usec for each of the i/o's in getit(). The loop
|
|
* takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
|
|
* multiplications and divisions to scale the count take a while).
|
|
*
|
|
* However, if ddb is active then use a fake counter since reading
|
|
* the i8254 counter involves acquiring a lock. ddb must not go
|
|
* locking for many reasons, but it calls here for at least atkbd
|
|
* input.
|
|
*/
|
|
prev_tick = getit();
|
|
|
|
n -= 0; /* XXX actually guess no initial overhead */
|
|
/*
|
|
* Calculate (n * (timer_freq / 1e6)) without using floating point
|
|
* and without any avoidable overflows.
|
|
*/
|
|
if (n <= 0)
|
|
ticks_left = 0;
|
|
else if (n < 256)
|
|
/*
|
|
* Use fixed point to avoid a slow division by 1000000.
|
|
* 39099 = 1193182 * 2^15 / 10^6 rounded to nearest.
|
|
* 2^15 is the first power of 2 that gives exact results
|
|
* for n between 0 and 256.
|
|
*/
|
|
ticks_left = ((u_int)n * 39099 + (1 << 15) - 1) >> 15;
|
|
else
|
|
/*
|
|
* Don't bother using fixed point, although gcc-2.7.2
|
|
* generates particularly poor code for the long long
|
|
* division, since even the slow way will complete long
|
|
* before the delay is up (unless we're interrupted).
|
|
*/
|
|
ticks_left = ((u_int)n * (long long)timer_freq + 999999)
|
|
/ 1000000;
|
|
|
|
while (ticks_left > 0) {
|
|
tick = getit();
|
|
#ifdef DELAYDEBUG
|
|
++getit_calls;
|
|
#endif
|
|
delta = tick - prev_tick;
|
|
prev_tick = tick;
|
|
if (delta < 0) {
|
|
/*
|
|
* Guard against timer0_max_count being wrong.
|
|
* This shouldn't happen in normal operation,
|
|
* but it may happen if set_timer_freq() is
|
|
* traced.
|
|
*/
|
|
/* delta += timer0_max_count; ??? */
|
|
if (delta < 0)
|
|
delta = 0;
|
|
}
|
|
ticks_left -= delta;
|
|
}
|
|
#ifdef DELAYDEBUG
|
|
if (state == 1)
|
|
printf(" %d calls to getit() at %d usec each\n",
|
|
getit_calls, (n + 5) / getit_calls);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Restore all the timers non-atomically (XXX: should be atomically).
|
|
*
|
|
* This function is called from pmtimer_resume() to restore all the timers.
|
|
* This should not be necessary, but there are broken laptops that do not
|
|
* restore all the timers on resume.
|
|
*/
|
|
void
|
|
timer_restore(void)
|
|
{
|
|
/* Get timebases for new environment. */
|
|
__get_time_values_from_xen();
|
|
|
|
/* Reset our own concept of passage of system time. */
|
|
processed_system_time = per_cpu(shadow_time, 0).system_timestamp;
|
|
per_cpu(processed_system_time, 0) = processed_system_time;
|
|
}
|
|
|
|
void
|
|
startrtclock()
|
|
{
|
|
unsigned long long alarm;
|
|
uint64_t __cpu_khz;
|
|
uint32_t cpu_khz;
|
|
struct vcpu_time_info *info;
|
|
|
|
/* initialize xen values */
|
|
__get_time_values_from_xen();
|
|
processed_system_time = per_cpu(shadow_time, 0).system_timestamp;
|
|
per_cpu(processed_system_time, 0) = processed_system_time;
|
|
|
|
__cpu_khz = 1000000ULL << 32;
|
|
info = &HYPERVISOR_shared_info->vcpu_info[0].time;
|
|
|
|
do_div(__cpu_khz, info->tsc_to_system_mul);
|
|
if ( info->tsc_shift < 0 )
|
|
cpu_khz = __cpu_khz << -info->tsc_shift;
|
|
else
|
|
cpu_khz = __cpu_khz >> info->tsc_shift;
|
|
|
|
printf("Xen reported: %u.%03u MHz processor.\n",
|
|
cpu_khz / 1000, cpu_khz % 1000);
|
|
|
|
/* (10^6 * 2^32) / cpu_hz = (10^3 * 2^32) / cpu_khz =
|
|
(2^32 * 1 / (clocks/us)) */
|
|
|
|
set_cyc2ns_scale(cpu_khz/1000);
|
|
tsc_freq = cpu_khz * 1000;
|
|
|
|
timer_freq = xen_timecounter.tc_frequency = 1000000000LL;
|
|
tc_init(&xen_timecounter);
|
|
|
|
rdtscll(alarm);
|
|
}
|
|
|
|
/*
|
|
* RTC support routines
|
|
*/
|
|
|
|
|
|
static __inline int
|
|
readrtc(int port)
|
|
{
|
|
return(bcd2bin(rtcin(port)));
|
|
}
|
|
|
|
|
|
#ifdef XEN_PRIVILEGED_GUEST
|
|
|
|
/*
|
|
* Initialize the time of day register, based on the time base which is, e.g.
|
|
* from a filesystem.
|
|
*/
|
|
static void
|
|
domu_inittodr(time_t base)
|
|
{
|
|
unsigned long sec;
|
|
int s, y;
|
|
struct timespec ts;
|
|
|
|
update_wallclock();
|
|
add_uptime_to_wallclock();
|
|
|
|
RTC_LOCK;
|
|
|
|
if (base) {
|
|
ts.tv_sec = base;
|
|
ts.tv_nsec = 0;
|
|
tc_setclock(&ts);
|
|
}
|
|
|
|
sec += tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
|
|
|
|
y = time_second - shadow_tv.tv_sec;
|
|
if (y <= -2 || y >= 2) {
|
|
/* badly off, adjust it */
|
|
tc_setclock(&shadow_tv);
|
|
}
|
|
RTC_UNLOCK;
|
|
}
|
|
|
|
/*
|
|
* Write system time back to RTC.
|
|
*/
|
|
static void
|
|
domu_resettodr(void)
|
|
{
|
|
unsigned long tm;
|
|
int s;
|
|
dom0_op_t op;
|
|
struct shadow_time_info *shadow;
|
|
|
|
shadow = &per_cpu(shadow_time, smp_processor_id());
|
|
if (xen_disable_rtc_set)
|
|
return;
|
|
|
|
s = splclock();
|
|
tm = time_second;
|
|
splx(s);
|
|
|
|
tm -= tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
|
|
|
|
if ((xen_start_info->flags & SIF_INITDOMAIN) &&
|
|
!independent_wallclock)
|
|
{
|
|
op.cmd = DOM0_SETTIME;
|
|
op.u.settime.secs = tm;
|
|
op.u.settime.nsecs = 0;
|
|
op.u.settime.system_time = shadow->system_timestamp;
|
|
HYPERVISOR_dom0_op(&op);
|
|
update_wallclock();
|
|
add_uptime_to_wallclock();
|
|
} else if (independent_wallclock) {
|
|
/* notyet */
|
|
;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the time of day register, based on the time base which is, e.g.
|
|
* from a filesystem.
|
|
*/
|
|
void
|
|
inittodr(time_t base)
|
|
{
|
|
unsigned long sec, days;
|
|
int year, month;
|
|
int y, m, s;
|
|
struct timespec ts;
|
|
|
|
if (!(xen_start_info->flags & SIF_INITDOMAIN)) {
|
|
domu_inittodr(base);
|
|
return;
|
|
}
|
|
|
|
if (base) {
|
|
s = splclock();
|
|
ts.tv_sec = base;
|
|
ts.tv_nsec = 0;
|
|
tc_setclock(&ts);
|
|
splx(s);
|
|
}
|
|
|
|
/* Look if we have a RTC present and the time is valid */
|
|
if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
|
|
goto wrong_time;
|
|
|
|
/* wait for time update to complete */
|
|
/* If RTCSA_TUP is zero, we have at least 244us before next update */
|
|
s = splhigh();
|
|
while (rtcin(RTC_STATUSA) & RTCSA_TUP) {
|
|
splx(s);
|
|
s = splhigh();
|
|
}
|
|
|
|
days = 0;
|
|
#ifdef USE_RTC_CENTURY
|
|
year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100;
|
|
#else
|
|
year = readrtc(RTC_YEAR) + 1900;
|
|
if (year < 1970)
|
|
year += 100;
|
|
#endif
|
|
if (year < 1970) {
|
|
splx(s);
|
|
goto wrong_time;
|
|
}
|
|
month = readrtc(RTC_MONTH);
|
|
for (m = 1; m < month; m++)
|
|
days += daysinmonth[m-1];
|
|
if ((month > 2) && LEAPYEAR(year))
|
|
days ++;
|
|
days += readrtc(RTC_DAY) - 1;
|
|
for (y = 1970; y < year; y++)
|
|
days += DAYSPERYEAR + LEAPYEAR(y);
|
|
sec = ((( days * 24 +
|
|
readrtc(RTC_HRS)) * 60 +
|
|
readrtc(RTC_MIN)) * 60 +
|
|
readrtc(RTC_SEC));
|
|
/* sec now contains the number of seconds, since Jan 1 1970,
|
|
in the local time zone */
|
|
|
|
sec += tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
|
|
|
|
y = time_second - sec;
|
|
if (y <= -2 || y >= 2) {
|
|
/* badly off, adjust it */
|
|
ts.tv_sec = sec;
|
|
ts.tv_nsec = 0;
|
|
tc_setclock(&ts);
|
|
}
|
|
splx(s);
|
|
return;
|
|
|
|
wrong_time:
|
|
printf("Invalid time in real time clock.\n");
|
|
printf("Check and reset the date immediately!\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Write system time back to RTC
|
|
*/
|
|
void
|
|
resettodr()
|
|
{
|
|
unsigned long tm;
|
|
int y, m, s;
|
|
|
|
if (!(xen_start_info->flags & SIF_INITDOMAIN)) {
|
|
domu_resettodr();
|
|
return;
|
|
}
|
|
|
|
if (xen_disable_rtc_set)
|
|
return;
|
|
|
|
s = splclock();
|
|
tm = time_second;
|
|
splx(s);
|
|
|
|
/* Disable RTC updates and interrupts. */
|
|
writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
|
|
|
|
/* Calculate local time to put in RTC */
|
|
|
|
tm -= tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
|
|
|
|
writertc(RTC_SEC, bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */
|
|
writertc(RTC_MIN, bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */
|
|
writertc(RTC_HRS, bin2bcd(tm%24)); tm /= 24; /* Write back Hours */
|
|
|
|
/* We have now the days since 01-01-1970 in tm */
|
|
writertc(RTC_WDAY, (tm + 4) % 7 + 1); /* Write back Weekday */
|
|
for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y);
|
|
tm >= m;
|
|
y++, m = DAYSPERYEAR + LEAPYEAR(y))
|
|
tm -= m;
|
|
|
|
/* Now we have the years in y and the day-of-the-year in tm */
|
|
writertc(RTC_YEAR, bin2bcd(y%100)); /* Write back Year */
|
|
#ifdef USE_RTC_CENTURY
|
|
writertc(RTC_CENTURY, bin2bcd(y/100)); /* ... and Century */
|
|
#endif
|
|
for (m = 0; ; m++) {
|
|
int ml;
|
|
|
|
ml = daysinmonth[m];
|
|
if (m == 1 && LEAPYEAR(y))
|
|
ml++;
|
|
if (tm < ml)
|
|
break;
|
|
tm -= ml;
|
|
}
|
|
|
|
writertc(RTC_MONTH, bin2bcd(m + 1)); /* Write back Month */
|
|
writertc(RTC_DAY, bin2bcd(tm + 1)); /* Write back Month Day */
|
|
|
|
/* Reenable RTC updates and interrupts. */
|
|
writertc(RTC_STATUSB, RTCSB_24HR);
|
|
rtcin(RTC_INTR);
|
|
}
|
|
#endif
|
|
|
|
static struct vcpu_set_periodic_timer xen_set_periodic_tick;
|
|
|
|
/*
|
|
* Start clocks running.
|
|
*/
|
|
void
|
|
cpu_initclocks(void)
|
|
{
|
|
unsigned int time_irq;
|
|
int error;
|
|
|
|
xen_set_periodic_tick.period_ns = NS_PER_TICK;
|
|
|
|
HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, 0,
|
|
&xen_set_periodic_tick);
|
|
|
|
error = bind_virq_to_irqhandler(VIRQ_TIMER, 0, "clk",
|
|
clkintr, NULL, NULL,
|
|
INTR_TYPE_CLK | INTR_FAST, &time_irq);
|
|
if (error)
|
|
panic("failed to register clock interrupt\n");
|
|
/* should fast clock be enabled ? */
|
|
|
|
}
|
|
|
|
int
|
|
ap_cpu_initclocks(int cpu)
|
|
{
|
|
unsigned int time_irq;
|
|
int error;
|
|
|
|
xen_set_periodic_tick.period_ns = NS_PER_TICK;
|
|
|
|
HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, cpu,
|
|
&xen_set_periodic_tick);
|
|
error = bind_virq_to_irqhandler(VIRQ_TIMER, 0, "clk",
|
|
clkintr, NULL, NULL,
|
|
INTR_TYPE_CLK | INTR_FAST, &time_irq);
|
|
if (error)
|
|
panic("failed to register clock interrupt\n");
|
|
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
void
|
|
cpu_startprofclock(void)
|
|
{
|
|
|
|
printf("cpu_startprofclock: profiling clock is not supported\n");
|
|
}
|
|
|
|
void
|
|
cpu_stopprofclock(void)
|
|
{
|
|
|
|
printf("cpu_stopprofclock: profiling clock is not supported\n");
|
|
}
|
|
#define NSEC_PER_USEC 1000
|
|
|
|
static uint32_t
|
|
xen_get_timecount(struct timecounter *tc)
|
|
{
|
|
uint64_t clk;
|
|
struct shadow_time_info *shadow;
|
|
shadow = &per_cpu(shadow_time, smp_processor_id());
|
|
|
|
__get_time_values_from_xen();
|
|
|
|
clk = shadow->system_timestamp + get_nsec_offset(shadow);
|
|
|
|
return (uint32_t)((clk / NS_PER_TICK) * NS_PER_TICK);
|
|
|
|
}
|
|
|
|
/* Return system time offset by ticks */
|
|
uint64_t
|
|
get_system_time(int ticks)
|
|
{
|
|
return processed_system_time + (ticks * NS_PER_TICK);
|
|
}
|
|
|
|
/*
|
|
* Track behavior of cur_timer->get_offset() functionality in timer_tsc.c
|
|
*/
|
|
|
|
|
|
/* Convert jiffies to system time. */
|
|
static uint64_t
|
|
ticks_to_system_time(int newticks)
|
|
{
|
|
int delta;
|
|
uint64_t st;
|
|
|
|
delta = newticks - ticks;
|
|
if (delta < 1) {
|
|
/* Triggers in some wrap-around cases,
|
|
* but that's okay:
|
|
* we just end up with a shorter timeout. */
|
|
st = processed_system_time + NS_PER_TICK;
|
|
} else if (((unsigned int)delta >> (BITS_PER_LONG-3)) != 0) {
|
|
/* Very long timeout means there is no pending timer.
|
|
* We indicate this to Xen by passing zero timeout. */
|
|
st = 0;
|
|
} else {
|
|
st = processed_system_time + delta * (uint64_t)NS_PER_TICK;
|
|
}
|
|
|
|
return (st);
|
|
}
|
|
|
|
void
|
|
idle_block(void)
|
|
{
|
|
uint64_t timeout;
|
|
|
|
timeout = ticks_to_system_time(ticks + 1) + NS_PER_TICK/2;
|
|
|
|
__get_time_values_from_xen();
|
|
PANIC_IF(HYPERVISOR_set_timer_op(timeout) != 0);
|
|
HYPERVISOR_sched_op(SCHEDOP_block, 0);
|
|
}
|
|
|
|
int
|
|
timer_spkr_acquire(void)
|
|
{
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
timer_spkr_release(void)
|
|
{
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
timer_spkr_setfreq(int freq)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|