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.


More timing cleanup with a fix to 8254.c for running cores slower than
[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.h>
22 #include <palacios/vmm_time.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 using an offsets 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  * The value used to offset the guest TSC from the host TSC is the *sum* of all
51  * of these offsets (2 and 3) above
52  * 
53  * Because all other devices are slaved off of the passage of time in the guest,
54  * it is (2) above that drives the firing of other timers in the guest, 
55  * including timer devices such as the Programmable Interrupt Timer (PIT).
56  *
57  * Future additions:
58  * (1) Add support for temporarily skewing guest time off of where it should
59  *     be to support slack simulation of guests. The idea is that simulators
60  *     set this skew to be the difference between how much time passed for a 
61  *     simulated feature and a real implementation of that feature, making 
62  *     pass at a different rate from real time on this core. The VMM will then
63  *     attempt to move this skew back towards 0 subject to resolution/accuracy
64  *     constraints from various system timers.
65  *   
66  *     The main effort in doing this will be to get accuracy/resolution 
67  *     information from each local timer and to use this to bound how much skew
68  *     is removed on each exit.
69  */
70
71
72 static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
73     struct vm_time * time_state = &(info->time_state);
74
75     info->vm_regs.rbx = time_state->guest_cpu_freq;
76
77     PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx);
78     
79     return 0;
80 }
81
82
83
84 int v3_start_time(struct guest_info * info) {
85     /* We start running with guest_time == host_time */
86     uint64_t t = v3_get_host_time(&info->time_state); 
87
88     PrintDebug("Starting initial guest time as %llu\n", t);
89     info->time_state.enter_time = 0;
90     info->time_state.exit_time = t; 
91     info->time_state.last_update = t;
92     info->time_state.initial_time = t;
93     info->yield_start_cycle = t;
94     return 0;
95 }
96
97 // Control guest time in relation to host time so that the two stay 
98 // appropriately synchronized to the extent possible. 
99 int v3_adjust_time(struct guest_info * info) {
100     struct vm_time * time_state = &(info->time_state);
101     uint64_t host_time, target_host_time;
102     uint64_t guest_time, target_guest_time, old_guest_time;
103     uint64_t guest_elapsed, host_elapsed, desired_elapsed;
104
105     /* Compute the target host time given how much time has *already*
106      * passed in the guest */
107     guest_time = v3_get_guest_time(time_state);
108     guest_elapsed = (guest_time - time_state->initial_time);
109     desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
110     target_host_time = time_state->initial_time + desired_elapsed;
111
112     /* Now, let the host run while the guest is stopped to make the two
113      * sync up. */
114     host_time = v3_get_host_time(time_state);
115     old_guest_time = v3_get_guest_time(time_state);
116     while (target_host_time > host_time) {
117         v3_yield(info);
118         host_time = v3_get_host_time(time_state);
119     }
120     guest_time = v3_get_guest_time(time_state);
121     // We do *not* assume the guest timer was paused in the VM. If it was
122     // this offseting is 0. If it wasn't we need this.
123     v3_offset_time(info, (sint64_t)old_guest_time - (sint64_t)guest_time);
124
125     /* Now the host may have gotten ahead of the guest because
126      * yielding is a coarse grained thing. Figure out what guest time
127      * we want to be at, and use the use the offsetting mechanism in 
128      * the VMM to make the guest run forward. We limit *how* much we skew 
129      * it forward to prevent the guest time making large jumps, 
130      * however. */
131     host_elapsed = host_time - time_state->initial_time;
132     desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
133     target_guest_time = time_state->initial_time + desired_elapsed;
134
135     if (guest_time < target_guest_time) {
136         uint64_t max_skew, desired_skew, skew;
137
138         if (time_state->enter_time) {
139             max_skew = (time_state->exit_time - time_state->enter_time)/10;
140         } else {
141             max_skew = 0;
142         }
143         desired_skew = target_guest_time - guest_time;
144         skew = desired_skew > max_skew ? max_skew : desired_skew;
145 /*      PrintDebug("Guest %llu cycles behind where it should be.\n",
146                    desired_skew);
147         PrintDebug("Limit on forward skew is %llu. Skewing forward %llu.\n",
148                    max_skew, skew); */
149         
150         v3_offset_time(info, skew);
151     }
152     
153     return 0;
154 }
155
156 /* Called immediately upon entry in the the VMM */
157 int 
158 v3_time_exit_vm( struct guest_info * info ) 
159 {
160     struct vm_time * time_state = &(info->time_state);
161     
162     time_state->exit_time = v3_get_host_time(time_state);
163
164     return 0;
165 }
166
167 /* Called immediately prior to entry to the VM */
168 int 
169 v3_time_enter_vm( struct guest_info * info )
170 {
171     struct vm_time * time_state = &(info->time_state);
172     uint64_t guest_time, host_time;
173
174     guest_time = v3_get_guest_time(time_state);
175     host_time = v3_get_host_time(time_state);
176     time_state->enter_time = host_time;
177     time_state->guest_host_offset = guest_time - host_time;
178
179     // Because we just modified the offset - shouldn't matter as this should be 
180     // the last time-related call prior to entering the VMM, but worth it 
181     // just in case.
182     time_state->exit_time = host_time; 
183
184     return 0;
185 }
186         
187 int v3_offset_time( struct guest_info * info, sint64_t offset )
188 {
189     struct vm_time * time_state = &(info->time_state);
190 //    PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
191     time_state->guest_host_offset += offset;
192     return 0;
193 }
194            
195 struct v3_timer * v3_add_timer(struct guest_info * info, 
196                                struct v3_timer_ops * ops, 
197                                void * private_data) {
198     struct v3_timer * timer = NULL;
199     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
200     V3_ASSERT(timer != NULL);
201
202     timer->ops = ops;
203     timer->private_data = private_data;
204
205     list_add(&(timer->timer_link), &(info->time_state.timers));
206     info->time_state.num_timers++;
207
208     return timer;
209 }
210
211 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
212     list_del(&(timer->timer_link));
213     info->time_state.num_timers--;
214
215     V3_Free(timer);
216     return 0;
217 }
218
219 void v3_update_timers(struct guest_info * info) {
220     struct vm_time *time_state = &info->time_state;
221     struct v3_timer * tmp_timer;
222     uint64_t old_time = info->time_state.last_update;
223     sint64_t cycles;
224
225     time_state->last_update = v3_get_guest_time(time_state);
226     cycles = time_state->last_update - old_time;
227
228     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
229         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
230     }
231 }
232
233 /* 
234  * Handle full virtualization of the time stamp counter.  As noted
235  * above, we don't store the actual value of the TSC, only the guest's
236  * offset from monotonic guest's time. If the guest writes to the TSC, we
237  * handle this by changing that offset.
238  *
239  * Possible TODO: Proper hooking of TSC read/writes?
240  */ 
241
242 int v3_rdtsc(struct guest_info * info) {
243     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
244     info->vm_regs.rdx = tscval >> 32;
245     info->vm_regs.rax = tscval & 0xffffffffLL;
246     return 0;
247 }
248
249 int v3_handle_rdtsc(struct guest_info * info) {
250     v3_rdtsc(info);
251     
252     info->vm_regs.rax &= 0x00000000ffffffffLL;
253     info->vm_regs.rdx &= 0x00000000ffffffffLL;
254
255     info->rip += 2;
256     
257     return 0;
258 }
259
260 int v3_rdtscp(struct guest_info * info) {
261     int ret;
262     /* First get the MSR value that we need. It's safe to futz with
263      * ra/c/dx here since they're modified by this instruction anyway. */
264     info->vm_regs.rcx = TSC_AUX_MSR; 
265     ret = v3_handle_msr_read(info);
266
267     if (ret != 0) {
268         return ret;
269     }
270
271     info->vm_regs.rcx = info->vm_regs.rax;
272
273     /* Now do the TSC half of the instruction */
274     ret = v3_rdtsc(info);
275
276     if (ret != 0) {
277         return ret;
278     }
279
280     return 0;
281 }
282
283
284 int v3_handle_rdtscp(struct guest_info * info) {
285   PrintDebug("Handling virtual RDTSCP call.\n");
286
287     v3_rdtscp(info);
288
289     info->vm_regs.rax &= 0x00000000ffffffffLL;
290     info->vm_regs.rcx &= 0x00000000ffffffffLL;
291     info->vm_regs.rdx &= 0x00000000ffffffffLL;
292
293     info->rip += 3;
294     
295     return 0;
296 }
297
298 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
299                                  struct v3_msr *msr_val, void *priv) {
300     struct vm_time * time_state = &(info->time_state);
301
302     V3_ASSERT(msr_num == TSC_AUX_MSR);
303
304     msr_val->lo = time_state->tsc_aux.lo;
305     msr_val->hi = time_state->tsc_aux.hi;
306
307     return 0;
308 }
309
310 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
311                               struct v3_msr msr_val, void *priv) {
312     struct vm_time * time_state = &(info->time_state);
313
314     V3_ASSERT(msr_num == TSC_AUX_MSR);
315
316     time_state->tsc_aux.lo = msr_val.lo;
317     time_state->tsc_aux.hi = msr_val.hi;
318
319     return 0;
320 }
321
322 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
323                              struct v3_msr *msr_val, void *priv) {
324     uint64_t time = v3_get_guest_tsc(&info->time_state);
325
326     V3_ASSERT(msr_num == TSC_MSR);
327
328     msr_val->hi = time >> 32;
329     msr_val->lo = time & 0xffffffffLL;
330     
331     return 0;
332 }
333
334 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
335                              struct v3_msr msr_val, void *priv) {
336     struct vm_time * time_state = &(info->time_state);
337     uint64_t guest_time, new_tsc;
338
339     V3_ASSERT(msr_num == TSC_MSR);
340
341     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
342     guest_time = v3_get_guest_time(time_state);
343     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
344
345     return 0;
346 }
347
348
349 int v3_init_time_vm(struct v3_vm_info * vm) {
350     int ret;
351
352     PrintDebug("Installing TSC MSR hook.\n");
353     ret = v3_hook_msr(vm, TSC_MSR, 
354                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
355
356     if (ret != 0) {
357         return ret;
358     }
359
360     PrintDebug("Installing TSC_AUX MSR hook.\n");
361     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
362                       tsc_aux_msr_write_hook, NULL);
363
364     if (ret != 0) {
365         return ret;
366     }
367
368     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
369     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
370                                 handle_cpufreq_hcall, NULL);
371
372     return ret;
373 }
374
375 void v3_deinit_time_vm(struct v3_vm_info * vm) {
376     v3_unhook_msr(vm, TSC_MSR);
377     v3_unhook_msr(vm, TSC_AUX_MSR);
378
379     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
380 }
381
382 void v3_init_time_core(struct guest_info * info) {
383     struct vm_time * time_state = &(info->time_state);
384     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
385     char * khz = NULL;
386
387     time_state->host_cpu_freq = V3_CPU_KHZ();
388     khz = v3_cfg_val(cfg_tree, "khz");
389
390     if (khz) {
391         time_state->guest_cpu_freq = atoi(khz);
392         PrintDebug("Core %d CPU frequency requested at %d khz.\n", 
393                    info->cpu_id, time_state->guest_cpu_freq);
394     } 
395     
396     if ((khz == NULL) || (time_state->guest_cpu_freq <= 0) 
397         || (time_state->guest_cpu_freq > time_state->host_cpu_freq)) {
398         time_state->guest_cpu_freq = time_state->host_cpu_freq;
399     }
400
401     PrintDebug("Core %d CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
402                info->cpu_id, 
403                time_state->guest_cpu_freq, 
404                time_state->host_cpu_freq);
405
406     time_state->initial_time = 0;
407     time_state->last_update = 0;
408     time_state->guest_host_offset = 0;
409     time_state->tsc_guest_offset = 0;
410
411     INIT_LIST_HEAD(&(time_state->timers));
412     time_state->num_timers = 0;
413     
414     time_state->tsc_aux.lo = 0;
415     time_state->tsc_aux.hi = 0;
416
417
418 }
419
420
421 void v3_deinit_time_core(struct guest_info * core) {
422     struct vm_time * time_state = &(core->time_state);
423     struct v3_timer * tmr = NULL;
424     struct v3_timer * tmp = NULL;
425
426     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
427         v3_remove_timer(core, tmr);
428     }
429
430 }
431
432
433
434
435
436