Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Still working on timer updates, heading towards being able to have a CPU
[palacios.git] / palacios / src / palacios / vmm_time.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *         Patrick G. Bridges <bridges@cs.unm.edu>
16  *
17  * This is free software.  You are permitted to use,
18  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
19  */
20
21 #include <palacios/vmm_time.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vm_guest.h>
24
25 #ifndef CONFIG_DEBUG_TIME
26 #undef PrintDebug
27 #define PrintDebug(fmt, args...)
28 #endif
29
30 /* Overview 
31  *
32  * Time handling in VMMs is challenging, and Palacios uses the highest 
33  * resolution, lowest overhead timer on modern CPUs that it can - the 
34  * processor timestamp counter (TSC). Note that on somewhat old processors
35  * this can be problematic; in particular, older AMD processors did not 
36  * have a constant rate timestamp counter in the face of power management
37  * events. However, the latest Intel and AMD CPUs all do (should...) have a 
38  * constant rate TSC, and Palacios relies on this fact.
39  * 
40  * Basically, Palacios keeps track of three quantities as it runs to manage
41  * the passage of time:
42  * (1) The host timestamp counter - read directly from HW and never written
43  * (2) A monotonic guest timestamp counter used to measure the progression of
44  *     time in the guest. This is computed as a multipler/offset from (1) above
45  * (3) The actual guest timestamp counter (which can be written by
46  *     writing to the guest TSC MSR - MSR 0x10) from the monotonic guest TSC.
47  *     This is also computed as an offset from (2) above when the TSC and
48  *     this offset is updated when the TSC MSR is written.
49  *
50  * Because all other devices are slaved off of the passage of time in the guest,
51  * it is (2) above that drives the firing of other timers in the guest, 
52  * including timer devices such as the Programmable Interrupt Timer (PIT).
53  *
54  *  
55  *
56  */
57
58
59 static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
60     struct vm_time * time_state = &(info->time_state);
61
62     info->vm_regs.rbx = time_state->cpu_freq;
63
64     PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx);
65     
66     return 0;
67 }
68
69
70
71 int v3_start_time(struct guest_info * info) {
72     /* We start running with guest_time == host_time */
73     uint64_t t = v3_get_host_time(&info->time_state); 
74
75     PrintDebug("Starting initial guest time as %llu\n", t);
76     info->time_state.last_update = t;
77     info->time_state.pause_time = t;
78     info->yield_start_cycle = t;
79     return 0;
80 }
81
82 int v3_pause_time(struct guest_info * info) {
83     V3_ASSERT(info->time_state.pause_time == 0);
84     info->time_state.pause_time = v3_get_guest_time(&info->time_state);
85     PrintDebug("Time paused at guest time as %llu\n", 
86                info->time_state.pause_time);
87     return 0;
88 }
89
90 int v3_resume_time(struct guest_info * info) {
91     uint64_t t = v3_get_host_time(&info->time_state);
92     V3_ASSERT(info->time_state.pause_time != 0);
93     info->time_state.time_offset = 
94         (sint64_t)info->time_state.pause_time - (sint64_t)t;
95     info->time_state.pause_time = 0;
96     PrintDebug("Time resumed paused at guest time as %llu "
97                "offset %lld from host time.\n", t, info->time_state.time_offset);
98
99     return 0;
100 }
101
102 int v3_add_timer(struct guest_info * info, struct vm_timer_ops * ops, 
103              void * private_data) {
104     struct vm_timer * timer = NULL;
105     timer = (struct vm_timer *)V3_Malloc(sizeof(struct vm_timer));
106     V3_ASSERT(timer != NULL);
107
108     timer->ops = ops;
109     timer->private_data = private_data;
110
111     list_add(&(timer->timer_link), &(info->time_state.timers));
112     info->time_state.num_timers++;
113
114     return 0;
115 }
116
117
118 int v3_remove_timer(struct guest_info * info, struct vm_timer * timer) {
119     list_del(&(timer->timer_link));
120     info->time_state.num_timers--;
121
122     V3_Free(timer);
123     return 0;
124 }
125
126 void v3_update_timers(struct guest_info * info) {
127     struct vm_timer * tmp_timer;
128     uint64_t old_time = info->time_state.last_update;
129     uint64_t cycles;
130
131     info->time_state.last_update = v3_get_guest_time(&info->time_state);
132     cycles = info->time_state.last_update - old_time;
133
134     list_for_each_entry(tmp_timer, &(info->time_state.timers), timer_link) {
135         tmp_timer->ops->update_timer(info, cycles, info->time_state.cpu_freq, tmp_timer->private_data);
136     }
137 }
138
139
140 /* 
141  * Handle full virtualization of the time stamp counter.  As noted
142  * above, we don't store the actual value of the TSC, only the guest's
143  * offset from the host TSC. If the guest write's the to TSC, we handle
144  * this by changing that offset.
145  */ 
146
147 int v3_rdtsc(struct guest_info * info) {
148     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
149     info->vm_regs.rdx = tscval >> 32;
150     info->vm_regs.rax = tscval & 0xffffffffLL;
151     return 0;
152 }
153
154 int v3_handle_rdtsc(struct guest_info * info) {
155     v3_rdtsc(info);
156     
157     info->vm_regs.rax &= 0x00000000ffffffffLL;
158     info->vm_regs.rdx &= 0x00000000ffffffffLL;
159
160     info->rip += 2;
161     
162     return 0;
163 }
164
165 int v3_rdtscp(struct guest_info * info) {
166     int ret;
167     /* First get the MSR value that we need. It's safe to futz with
168      * ra/c/dx here since they're modified by this instruction anyway. */
169     info->vm_regs.rcx = TSC_AUX_MSR; 
170     ret = v3_handle_msr_read(info);
171     if (ret) return ret;
172     info->vm_regs.rcx = info->vm_regs.rax;
173
174     /* Now do the TSC half of the instruction, which may hit the normal 
175      * TSC hook if it exists */
176     ret = v3_rdtsc(info);
177     if (ret) return ret;
178     
179     return 0;
180 }
181
182
183 int v3_handle_rdtscp(struct guest_info * info) {
184
185     v3_rdtscp(info);
186     
187     info->vm_regs.rax &= 0x00000000ffffffffLL;
188     info->vm_regs.rcx &= 0x00000000ffffffffLL;
189     info->vm_regs.rdx &= 0x00000000ffffffffLL;
190
191     info->rip += 3;
192     
193     return 0;
194 }
195
196 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
197                                  struct v3_msr *msr_val, void *priv) {
198     struct vm_time * time_state = &(info->time_state);
199
200     V3_ASSERT(msr_num == TSC_AUX_MSR);
201     msr_val->lo = time_state->tsc_aux.lo;
202     msr_val->hi = time_state->tsc_aux.hi;
203
204     return 0;
205 }
206
207 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
208                               struct v3_msr msr_val, void *priv) {
209     struct vm_time * time_state = &(info->time_state);
210
211     V3_ASSERT(msr_num == TSC_AUX_MSR);
212     time_state->tsc_aux.lo = msr_val.lo;
213     time_state->tsc_aux.hi = msr_val.hi;
214
215     return 0;
216 }
217
218 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
219                              struct v3_msr *msr_val, void *priv) {
220     uint64_t time = v3_get_guest_tsc(&info->time_state);
221
222     V3_ASSERT(msr_num == TSC_MSR);
223     msr_val->hi = time >> 32;
224     msr_val->lo = time & 0xffffffffLL;
225     
226     return 0;
227 }
228
229 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
230                              struct v3_msr msr_val, void *priv) {
231     struct vm_time * time_state = &(info->time_state);
232     uint64_t guest_time, new_tsc;
233     V3_ASSERT(msr_num == TSC_MSR);
234     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
235     guest_time = v3_get_guest_time(time_state);
236     time_state->tsc_time_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
237
238     return 0;
239 }
240
241
242 void v3_init_time(struct guest_info * info) {
243     struct vm_time * time_state = &(info->time_state);
244
245     time_state->cpu_freq = V3_CPU_KHZ();
246  
247     time_state->pause_time = 0;
248     time_state->last_update = 0;
249     time_state->time_offset = 0;
250     time_state->time_div = 1;
251     time_state->time_mult = 1;
252     time_state->tsc_time_offset = 0;
253
254     INIT_LIST_HEAD(&(time_state->timers));
255     time_state->num_timers = 0;
256     
257     time_state->tsc_aux.lo = 0;
258     time_state->tsc_aux.hi = 0;
259
260     /* does init_time get called once, or once *per core*??? */
261     v3_hook_msr(info->vm_info, TSC_MSR, 
262                 tsc_msr_read_hook, tsc_msr_write_hook, NULL);
263     v3_hook_msr(info->vm_info, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
264                 tsc_aux_msr_write_hook, NULL);
265
266     v3_register_hypercall(info->vm_info, TIME_CPUFREQ_HCALL, handle_cpufreq_hcall, NULL);
267 }