aboutsummaryrefslogtreecommitdiffstats
path: root/old/xenolinux-2.4.16-sparse/arch/xeno/kernel/time.c
blob: eb5757cdc2c8f6405c6a3741c526688f3256dd0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/* -*-  Mode:C; c-basic-offset:4; tab-width:4 -*-
 ****************************************************************************
 * (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
 ****************************************************************************
 *
 *        File: arch.xeno/time.c
 *      Author: Rolf Neugebauer
 *     Changes: 
 *              
 *        Date: Nov 2002
 * 
 * Environment: XenoLinux
 * Description: Interface with Hypervisor to get correct notion of time
 *              Currently supports Systemtime and WallClock time.
 *
 * (This has hardly any resemblence with the Linux code but left the
 *  copyright notice anyway. Ignore the comments in the copyright notice.)
 ****************************************************************************
 * $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
 ****************************************************************************
 */

/*
 *  linux/arch/i386/kernel/time.c
 *
 *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
 *
 * This file contains the PC-specific time handling details:
 * reading the RTC at bootup, etc..
 * 1994-07-02    Alan Modra
 *	fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
 * 1995-03-26    Markus Kuhn
 *      fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
 *      precision CMOS clock update
 * 1996-05-03    Ingo Molnar
 *      fixed time warps in do_[slow|fast]_gettimeoffset()
 * 1997-09-10	Updated NTP code according to technical memorandum Jan '96
 *		"A Kernel Model for Precision Timekeeping" by Dave Mills
 * 1998-09-05    (Various)
 *	More robust do_fast_gettimeoffset() algorithm implemented
 *	(works with APM, Cyrix 6x86MX and Centaur C6),
 *	monotonic gettimeofday() with fast_get_timeoffset(),
 *	drift-proof precision TSC calibration on boot
 *	(C. Scott Ananian <cananian@alumni.princeton.edu>, Andrew D.
 *	Balsa <andrebalsa@altern.org>, Philip Gladstone <philip@raptor.com>;
 *	ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>).
 * 1998-12-16    Andrea Arcangeli
 *	Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy
 *	because was not accounting lost_ticks.
 * 1998-12-24 Copyright (C) 1998  Andrea Arcangeli
 *	Fixed a xtime SMP race (we need the xtime_lock rw spinlock to
 *	serialize accesses to xtime/lost_ticks).
 */

#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/msr.h>
#include <asm/delay.h>
#include <asm/mpspec.h>
#include <asm/uaccess.h>
#include <asm/processor.h>

#include <asm/div64.h>
#include <asm/hypervisor.h>

#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/irq.h>

#undef XENO_TIME_DEBUG	/* adds sanity checks and periodic printouts */

spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
extern rwlock_t xtime_lock;

unsigned long cpu_khz;	/* get this from Xen, used elsewhere */
static spinlock_t hyp_stime_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t hyp_wctime_lock = SPIN_LOCK_UNLOCKED;

static u32 st_scale_f;
static u32 st_scale_i;
static u32 shadow_st_pcc;
static s64 shadow_st;

/*
 * System time.
 * Although the rest of the Linux kernel doesn't know about this, we
 * we use it to extrapolate passage of wallclock time.
 * We need to read the values from the shared info page "atomically" 
 * and use the cycle counter value as the "version" number. Clashes
 * should be very rare.
 */
static inline long long get_s_time(void)
{
	unsigned long flags;
    u32           delta_tsc, low, pcc;
	u64           delta;
	s64           now;

	spin_lock_irqsave(&hyp_stime_lock, flags);

	while ((pcc = HYPERVISOR_shared_info->st_timestamp) != shadow_st_pcc)
	{
		barrier();
		shadow_st_pcc = pcc;
		shadow_st     = HYPERVISOR_shared_info->system_time;
		barrier();
	}

    now = shadow_st;
    /* only use bottom 32bits of TSC. This should be sufficient */
	rdtscl(low);
    delta_tsc = low - pcc;
	delta = ((u64)delta_tsc * st_scale_f);
	delta >>= 32;
	delta += ((u64)delta_tsc * st_scale_i);

	spin_unlock_irqrestore(&hyp_time_lock, flags);

    return now + delta; 

}
#define NOW()				((long long)get_s_time())

/*
 * Wallclock time.
 * Based on what the hypervisor tells us, extrapolated using system time.
 * Again need to read a number of values from the shared page "atomically".
 * this time using a version number.
 */
static u32        shadow_wc_version=0;
static long       shadow_tv_sec;
static long       shadow_tv_usec;
static long long  shadow_wc_timestamp;
void do_gettimeofday(struct timeval *tv)
{
	unsigned long flags;
    long          usec, sec;
	u32	          version;
	u64           now;

	spin_lock_irqsave(&hyp_wctime_lock, flags);

	while ((version = HYPERVISOR_shared_info->wc_version)!= shadow_wc_version)
	{
		barrier();
		shadow_wc_version   = version;
		shadow_tv_sec       = HYPERVISOR_shared_info->tv_sec;
		shadow_tv_usec      = HYPERVISOR_shared_info->tv_usec;
		shadow_wc_timestamp = HYPERVISOR_shared_info->wc_timestamp;
		barrier();
	}

	now   = NOW();
	usec  = ((unsigned long)(now-shadow_wc_timestamp))/1000;
	sec   = shadow_tv_sec;
	usec += shadow_tv_usec;

    while ( usec >= 1000000 ) 
    {
        usec -= 1000000;
        sec++;
    }

    tv->tv_sec = sec;
    tv->tv_usec = usec;

	spin_unlock_irqrestore(&hyp_time_lock, flags);

#ifdef XENO_TIME_DEBUG
	{
		static long long old_now=0;
		static long long wct=0, old_wct=0;

		/* This debug code checks if time increase over two subsequent calls */
		wct=(((long long)sec) * 1000000) + usec;
		/* wall clock time going backwards */
		if ((wct < old_wct) ) {	
			printk("Urgh1: wc diff=%6ld, usec = %ld (0x%lX)\n",
				   (long)(wct-old_wct), usec, usec);		
			printk("       st diff=%lld cur st=0x%016llX old st=0x%016llX\n",
				   now-old_now, now, old_now);
		}

		/* system time going backwards */
		if (now<=old_now) {
			printk("Urgh2: st diff=%lld cur st=0x%016llX old st=0x%016llX\n",
				   now-old_now, now, old_now);
		}
		old_wct  = wct;
		old_now  = now;
	}
#endif

}

void do_settimeofday(struct timeval *tv)
{
/* XXX RN: should do something special here for dom0 */
#if 0
    write_lock_irq(&xtime_lock);
    /*
     * This is revolting. We need to set "xtime" correctly. However, the
     * value in this location is the value at the most recent update of
     * wall time.  Discover what correction gettimeofday() would have
     * made, and then undo it!
     */
    tv->tv_usec -= do_gettimeoffset();
    tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ);

    while ( tv->tv_usec < 0 )
    {
        tv->tv_usec += 1000000;
        tv->tv_sec--;
    }

    xtime = *tv;
    time_adjust = 0;		/* stop active adjtime() */
    time_status |= STA_UNSYNC;
    time_maxerror = NTP_PHASE_LIMIT;
    time_esterror = NTP_PHASE_LIMIT;
    write_unlock_irq(&xtime_lock);
#endif
}


/*
 * Timer ISR. 
 * Unlike normal Linux these don't come in at a fixed rate of HZ. 
 * In here we wrok out how often it should have been called and then call
 * the architecture independent part (do_timer()) the appropriate number of
 * times. A bit of a nasty hack, to keep the "other" notion of wallclock time
 * happy.
 */
static long long us_per_tick=1000000/HZ;
static long long last_irq;
static inline void do_timer_interrupt(int irq, void *dev_id,
									  struct pt_regs *regs)
{
	struct timeval tv;
	long long time, delta;
	
#ifdef XENO_TIME_DEBUG
	static u32 foo_count = 0;
	foo_count++;		
	if (foo_count>= 1000) {
		s64 n = NOW();
		struct timeval tv;
		do_gettimeofday(&tv);
		printk("0x%08X%08X %ld:%ld\n",
			   (u32)(n>>32), (u32)n, tv.tv_sec, tv.tv_usec);
		foo_count = 0;
	}
#endif
    /*
     * The next bit really sucks:
     * Linux not only uses do_gettimeofday() to keep a notion of
     * wallclock time, but also maintains the xtime struct and jiffies.
     * (Even worse some userland code accesses this via the sys_time()
     * system call)
     * Unfortunately, xtime is maintain in the architecture independent
     * part of the timer ISR (./kernel/timer.c sic!). So, although we have
     * perfectly valid notion of wallclock time from the hypervisor we here
     * fake missed timer interrupts so that the arch independent part of
     * the Timer ISR updates jiffies for us *and* once the bh gets run
     * updates xtime accordingly. Yuck!
     */

	/* work out the number of jiffies past and update them */
	do_gettimeofday(&tv);
	time = (((long long)tv.tv_sec) * 1000000) + tv.tv_usec;
	delta = time - last_irq;
	if (delta <= 0) {
		printk ("Timer ISR: Time went backwards: %lld\n", delta);
		return;
	}
	while (delta >= us_per_tick) {
		do_timer(regs);
		delta    -= us_per_tick;
		last_irq += us_per_tick;
	}

#if 0
    if (!user_mode(regs))
        x86_do_profile(regs->eip);
#endif
}

static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    write_lock(&xtime_lock);
    do_timer_interrupt(irq, NULL, regs);
    write_unlock(&xtime_lock);
}

static struct irqaction irq_timer = {
    timer_interrupt, 
    SA_INTERRUPT, 
    0, 
    "timer", 
    NULL, 
    NULL
};

void __init time_init(void)
{
    unsigned long long alarm;
	u64	cpu_freq = HYPERVISOR_shared_info->cpu_freq;
	u64 scale;

	do_get_fast_time = do_gettimeofday;

	cpu_khz = (u32)cpu_freq/1000;
	printk("Xen reported: %lu.%03lu MHz processor.\n", 
		   cpu_khz / 1000, cpu_khz % 1000);

	/*
     * calculate systemtime scaling factor
	 * XXX RN: have to cast cpu_freq to u32 limits it to 4.29 GHz. 
	 *     Get a better do_div!
	 */
	scale = 1000000000LL << 32;
	do_div(scale,(u32)cpu_freq);
	st_scale_f = scale & 0xffffffff;
	st_scale_i = scale >> 32;
	printk("System Time scale: %X %X\n",st_scale_i, st_scale_f);

	do_gettimeofday(&xtime);
	last_irq = (((long long)xtime.tv_sec) * 1000000) + xtime.tv_usec;

    setup_irq(TIMER_IRQ, &irq_timer);

    /*
     * Start ticker. Note that timing runs of wall clock, not virtual
     * 'domain' time. This means that clock sshould run at the correct
     * rate. For things like scheduling, it's not clear whether it
     * matters which sort of time we use.
	 * XXX RN: unimplemented.
     */

    rdtscll(alarm);
#if 0
    alarm += (1000/HZ)*HYPERVISOR_shared_info->ticks_per_ms;
    HYPERVISOR_shared_info->wall_timeout   = alarm;
    HYPERVISOR_shared_info->domain_timeout = ~0ULL;
#endif
    clear_bit(_EVENT_TIMER, &HYPERVISOR_shared_info->events);
}