aboutsummaryrefslogtreecommitdiffstats
path: root/mini-os/time.c
blob: ff23b2ee630d4f19dab72a4b8bf9b18a38a8e821 (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
/* -*-  Mode:C; c-basic-offset:4; tab-width:4 -*-
 ****************************************************************************
 * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
 ****************************************************************************
 *
 *        File: time.c
 *      Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
 *     Changes: 
 *              
 *        Date: Jul 2003
 * 
 * Environment: Xen Minimal OS
 * Description: Simple time and timer functions
 *
 ****************************************************************************
 * $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
 ****************************************************************************
 */


#include <os.h>
#include <types.h>
#include <hypervisor.h>
#include <events.h>
#include <time.h>
#include <lib.h>

/************************************************************************
 * Time functions
 *************************************************************************/

static unsigned int rdtsc_bitshift;
static u32      st_scale_f;
static u32      st_scale_i;
static u32      shadow_st_pcc;
static s_time_t shadow_st;
static u32      shadow_wc_version=0;
static long     shadow_tv_sec;
static long     shadow_tv_usec;
static s_time_t shadow_wc_timestamp;

/*
 * System 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.
 */
inline s_time_t get_s_time(void)
{
    s32 delta_tsc;
    u32 low;
    u64 delta, tsc;
    u32	version;
    u64 cpu_freq, scale;

    /* check if our values are still up-to-date */
    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;
        shadow_st_pcc       = HYPERVISOR_shared_info->st_timestamp;
        shadow_st           = HYPERVISOR_shared_info->system_time;

        rdtsc_bitshift      = HYPERVISOR_shared_info->rdtsc_bitshift;
        cpu_freq            = HYPERVISOR_shared_info->cpu_freq;

        /* XXX cpu_freq as u32 limits it to 4.29 GHz. Get a better do_div! */
        scale = 1000000000LL << (32 + rdtsc_bitshift);
        scale /= cpu_freq;
        st_scale_f = scale & 0xffffffff;
        st_scale_i = scale >> 32;

        barrier();
	}

    rdtscll(tsc);
    low = (u32)(tsc >> rdtsc_bitshift);
    delta_tsc = (s32)(low - shadow_st_pcc);
    if ( unlikely(delta_tsc < 0) ) delta_tsc = 0;
    delta = ((u64)delta_tsc * st_scale_f);
    delta >>= 32;
    delta += ((u64)delta_tsc * st_scale_i);

    return shadow_st + delta;
}


/*
 * 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.
 */
void gettimeofday(struct timeval *tv)
{
    long          usec, sec;
    u64           now;

    now   = get_s_time();
    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;
}


static void timer_handler(int ev, struct pt_regs *regs)
{
    static int i;
    s_time_t now;

    i++;
    if (i >= 1000) {
        now = get_s_time();
        printf("T(%lld)\n", now);
        i = 0;
    }
}


void init_time(void)
{
    u64         __cpu_khz;
    unsigned long cpu_khz;

    __cpu_khz = HYPERVISOR_shared_info->cpu_freq;
    cpu_khz = (u32) (__cpu_khz/1000);

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

    add_ev_action(EV_TIMER, &timer_handler);
    enable_ev_action(EV_TIMER);
    enable_hypervisor_event(EV_TIMER);

}