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.


Cleanup to start proper time dilation mechanisms
[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 V3_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 time
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  * (2) Look more into sychronizing the offsets *across* virtual and physical 
71  *     cores so that multicore guests stay mostly in sync.
72  *
73  * (3) Look into using the AMD TSC multiplier feature and adding explicit time
74  *     dilation support to time handling.
75  */
76
77
78 static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
79     struct vm_core_time * time_state = &(info->time_state);
80
81     info->vm_regs.rbx = time_state->guest_cpu_freq;
82
83     PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx);
84     
85     return 0;
86 }
87
88
89
90 int v3_start_time(struct guest_info * info) {
91     /* We start running with guest_time == host_time */
92     uint64_t t = v3_get_host_time(&info->time_state); 
93
94     PrintDebug("Starting initial guest time as %llu\n", t);
95
96     info->time_state.enter_time = 0;
97     info->time_state.exit_time = t; 
98     info->time_state.last_update = t;
99     info->time_state.initial_time = t;
100     info->yield_start_cycle = t;
101
102     return 0;
103 }
104
105 int v3_pause_time( struct guest_info * info )
106 {
107     struct vm_core_time * time_state = &(info->time_state);
108     if (time_state->pause_time != 0) {
109         PrintError("Attempted to pause time when time already paused.\n");
110         return -1;
111     }
112     time_state->pause_time = v3_get_host_time( time_state );
113
114     return 0;
115 }
116
117 int v3_resume_time( struct guest_info * info )
118 {
119     struct vm_core_time * time_state = &(info->time_state);
120     uint64_t host_time, guest_time;
121     sint64_t offset;
122
123     if (time_state->pause_time == 0) {
124         PrintError("Attempted to resume time when time not paused.\n");
125         return -1;
126     }
127
128     host_time = v3_get_host_time(time_state);
129     guest_time = v3_compute_guest_time(time_state, host_time);
130     offset = (sint64_t)guest_time - (sint64_t)host_time;
131     time_state->guest_host_offset = offset;
132     time_state->pause_time = 0;
133
134     return 0;
135 }
136
137 int v3_offset_time( struct guest_info * info, sint64_t offset )
138 {
139     struct vm_core_time * time_state = &(info->time_state);
140     PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
141     time_state->guest_host_offset += offset;
142     return 0;
143 }
144
145 #ifdef V3_CONFIG_TIME_DILATION
146 static uint64_t compute_target_host_time(struct guest_info * info, uint64_t guest_time)
147 {
148     struct vm_core_time * time_state = &(info->time_state);
149     uint64_t guest_elapsed, desired_elapsed;
150     
151     guest_elapsed = (guest_time - time_state->initial_time);
152     desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
153     return time_state->initial_time + desired_elapsed;
154 }
155
156 static uint64_t compute_target_guest_time(struct guest_info *info)
157 {
158     struct vm_core_time * time_state = &(info->time_state);
159     uint64_t host_elapsed, desired_elapsed;
160
161     host_elapsed = v3_get_host_time(time_state) - time_state->initial_time;
162     desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
163
164     return time_state->initial_time + desired_elapsed;
165
166
167
168 /* Yield time in the host to deal with a guest that wants to run slower than 
169  * the native host cycle frequency */
170 static int yield_host_time(struct guest_info * info) {
171     struct vm_core_time * time_state = &(info->time_state);
172     uint64_t host_time, target_host_time;
173     uint64_t guest_time, old_guest_time;
174
175     /* Now, let the host run while the guest is stopped to make the two
176      * sync up. Note that this doesn't assume that guest time is stopped;
177      * the offsetting in the next step will change add an offset to guest
178      * time to account for the time paused even if the geust isn't 
179      * usually paused in the VMM. */
180     host_time = v3_get_host_time(time_state);
181     old_guest_time = v3_compute_guest_time(time_state, host_time);
182     target_host_time = compute_target_host_time(info, old_guest_time);
183
184     while (target_host_time > host_time) {
185         v3_yield(info);
186         host_time = v3_get_host_time(time_state);
187     }
188
189     guest_time = v3_compute_guest_time(time_state, host_time);
190
191     /* We do *not* assume the guest timer was paused in the VM. If it was
192      * this offseting is 0. If it wasn't, we need this. */
193     v3_offset_time(info, (sint64_t)(old_guest_time - guest_time));
194
195     return 0;
196 }
197
198 static int skew_guest_time(struct guest_info * info) {
199     struct vm_core_time * time_state = &(info->time_state);
200     uint64_t target_guest_time, guest_time;
201     /* Now the host may have gotten ahead of the guest because
202      * yielding is a coarse grained thing. Figure out what guest time
203      * we want to be at, and use the use the offsetting mechanism in 
204      * the VMM to make the guest run forward. We limit *how* much we skew 
205      * it forward to prevent the guest time making large jumps, 
206      * however. */
207     target_guest_time = compute_target_guest_time(info);
208     guest_time = v3_get_guest_time(time_state);
209
210     if (guest_time < target_guest_time) {
211         sint64_t max_skew, desired_skew, skew;
212
213         if (time_state->enter_time) {
214             /* Limit forward skew to 10% of the amount the guest has
215              * run since we last could skew time */
216             max_skew = (sint64_t)(guest_time - time_state->enter_time) / 10.0;
217         } else {
218             max_skew = 0;
219         }
220
221         desired_skew = (sint64_t)(target_guest_time - guest_time);
222         skew = desired_skew > max_skew ? max_skew : desired_skew;
223         PrintDebug("Guest %lld cycles behind where it should be.\n",
224                    desired_skew);
225         PrintDebug("Limit on forward skew is %lld. Skewing forward %lld.\n",
226                    max_skew, skew); 
227         
228         v3_offset_time(info, skew);
229     }
230
231     return 0;
232 }
233 #endif /* V3_CONFIG_TIME_DILATION */
234
235 // Control guest time in relation to host time so that the two stay 
236 // appropriately synchronized to the extent possible. 
237 int v3_adjust_time(struct guest_info * info) {
238
239 #ifdef V3_CONFIG_TIME_DILATION
240     /* First deal with yielding if we want to slow down the guest */
241     yield_host_time(info);
242
243     /* Now, if the guest is too slow, (either from excess yielding above,
244      * or because the VMM is doing something that takes a long time to emulate)
245      * allow guest time to jump forward a bit */
246     skew_guest_time(info);
247 #endif
248     return 0;
249 }
250
251 /* Called immediately upon entry in the the VMM */
252 int 
253 v3_time_exit_vm( struct guest_info * info ) 
254 {
255     struct vm_core_time * time_state = &(info->time_state);
256     
257     time_state->exit_time = v3_get_host_time(time_state);
258
259 #ifdef V3_CONFIG_TIME_DILATION
260     v3_pause_time( info );
261 #endif
262
263     return 0;
264 }
265
266 /* Called immediately prior to entry to the VM */
267 int 
268 v3_time_enter_vm( struct guest_info * info )
269 {
270     struct vm_core_time * time_state = &(info->time_state);
271     uint64_t host_time;
272
273     host_time = v3_get_host_time(time_state);
274     time_state->enter_time = host_time;
275 #ifdef V3_CONFIG_TIME_DILATION
276     v3_resume_time( info );
277 #else
278     time_state->guest_host_offset = 0;
279 #endif
280
281     return 0;
282 }
283         
284
285            
286 struct v3_timer * v3_add_timer(struct guest_info * info, 
287                                struct v3_timer_ops * ops, 
288                                void * private_data) {
289     struct v3_timer * timer = NULL;
290     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
291     V3_ASSERT(timer != NULL);
292
293     timer->ops = ops;
294     timer->private_data = private_data;
295
296     list_add(&(timer->timer_link), &(info->time_state.timers));
297     info->time_state.num_timers++;
298
299     return timer;
300 }
301
302 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
303     list_del(&(timer->timer_link));
304     info->time_state.num_timers--;
305
306     V3_Free(timer);
307     return 0;
308 }
309
310 void v3_update_timers(struct guest_info * info) {
311     struct vm_core_time *time_state = &info->time_state;
312     struct v3_timer * tmp_timer;
313     sint64_t cycles;
314     uint64_t old_time = info->time_state.last_update;
315
316     time_state->last_update = v3_get_guest_time(time_state);
317     cycles = (sint64_t)(time_state->last_update - old_time);
318     V3_ASSERT(cycles >= 0);
319
320     //    V3_Print("Updating timers with %lld elapsed cycles.\n", cycles);
321     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
322         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
323     }
324 }
325
326 /* Handle TSC timeout hooks */
327 struct v3_timeout_hook * 
328 v3_add_timeout_hook(struct guest_info * info, v3_timeout_callback_t callback, 
329                     void * priv_data) {
330     struct v3_timeout_hook * timeout = NULL;
331     timeout = (struct v3_timeout_hook *)V3_Malloc(sizeof(struct v3_timeout_hook));
332     V3_ASSERT(timeout != NULL);
333
334     timeout->callback = callback;
335     timeout->private_data = priv_data;
336
337     list_add(&(timeout->hook_link), &(info->time_state.timeout_hooks));
338     return timeout;
339 }
340
341 int 
342 v3_remove_timeout_hook(struct guest_info * info, struct v3_timeout_hook * hook) {
343     list_del(&(hook->hook_link));
344     V3_Free(hook);
345     return 0;
346 }
347
348 int v3_schedule_timeout(struct guest_info * info, ullong_t guest_timeout) {
349     struct vm_core_time *time_state = &info->time_state;
350     /* Note that virtualization architectures that support it (like newer
351      * VMX systems) will turn on an active preemption timeout if 
352      * available to get this timeout as closely as possible. Other systems
353      * only catch it in the periodic interrupt and so are less precise */
354     if (guest_timeout < time_state->next_timeout) {
355         time_state->next_timeout = guest_timeout;
356     }
357     return 0;
358 }
359
360 int v3_check_timeout( struct guest_info * info ) {
361     struct vm_core_time *time_state = &info->time_state;
362     if (time_state->next_timeout <= v3_get_guest_time(time_state)) {
363         struct v3_timeout_hook * tmp_timeout;
364         time_state->next_timeout = (ullong_t)-1;
365         list_for_each_entry(tmp_timeout, &(time_state->timeout_hooks), hook_link) {
366                 tmp_timeout->callback(info, tmp_timeout->private_data);
367         }
368     }
369     return 0;
370 }
371
372 /* 
373  * Handle full virtualization of the time stamp counter.  As noted
374  * above, we don't store the actual value of the TSC, only the guest's
375  * offset from monotonic guest's time. If the guest writes to the TSC, we
376  * handle this by changing that offset.
377  *
378  * Possible TODO: Proper hooking of TSC read/writes?
379  */ 
380
381 int v3_rdtsc(struct guest_info * info) {
382     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
383
384     info->vm_regs.rdx = tscval >> 32;
385     info->vm_regs.rax = tscval & 0xffffffffLL;
386
387     return 0;
388 }
389
390 int v3_handle_rdtsc(struct guest_info * info) {
391     v3_rdtsc(info);
392     
393     info->vm_regs.rax &= 0x00000000ffffffffLL;
394     info->vm_regs.rdx &= 0x00000000ffffffffLL;
395
396     info->rip += 2;
397     
398     return 0;
399 }
400
401 int v3_rdtscp(struct guest_info * info) {
402     int ret;
403     /* First get the MSR value that we need. It's safe to futz with
404      * ra/c/dx here since they're modified by this instruction anyway. */
405     info->vm_regs.rcx = TSC_AUX_MSR; 
406     ret = v3_handle_msr_read(info);
407
408     if (ret != 0) {
409         return ret;
410     }
411
412     info->vm_regs.rcx = info->vm_regs.rax;
413
414     /* Now do the TSC half of the instruction */
415     ret = v3_rdtsc(info);
416
417     if (ret != 0) {
418         return ret;
419     }
420
421     return 0;
422 }
423
424
425 int v3_handle_rdtscp(struct guest_info * info) {
426   PrintDebug("Handling virtual RDTSCP call.\n");
427
428     v3_rdtscp(info);
429
430     info->vm_regs.rax &= 0x00000000ffffffffLL;
431     info->vm_regs.rcx &= 0x00000000ffffffffLL;
432     info->vm_regs.rdx &= 0x00000000ffffffffLL;
433
434     info->rip += 3;
435     
436     return 0;
437 }
438
439 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
440                                  struct v3_msr *msr_val, void *priv) {
441     struct vm_core_time * time_state = &(info->time_state);
442
443     V3_ASSERT(msr_num == TSC_AUX_MSR);
444
445     msr_val->lo = time_state->tsc_aux.lo;
446     msr_val->hi = time_state->tsc_aux.hi;
447
448     return 0;
449 }
450
451 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
452                               struct v3_msr msr_val, void *priv) {
453     struct vm_core_time * time_state = &(info->time_state);
454
455     V3_ASSERT(msr_num == TSC_AUX_MSR);
456
457     time_state->tsc_aux.lo = msr_val.lo;
458     time_state->tsc_aux.hi = msr_val.hi;
459
460     return 0;
461 }
462
463 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
464                              struct v3_msr *msr_val, void *priv) {
465     uint64_t time = v3_get_guest_tsc(&info->time_state);
466
467     V3_ASSERT(msr_num == TSC_MSR);
468
469     msr_val->hi = time >> 32;
470     msr_val->lo = time & 0xffffffffLL;
471     
472     return 0;
473 }
474
475 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
476                              struct v3_msr msr_val, void *priv) {
477     struct vm_core_time * time_state = &(info->time_state);
478     uint64_t guest_time, new_tsc;
479
480     V3_ASSERT(msr_num == TSC_MSR);
481
482     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
483     guest_time = v3_get_guest_time(time_state);
484     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
485
486     return 0;
487 }
488
489
490 int v3_init_time_vm(struct v3_vm_info * vm) {
491     int ret;
492
493     PrintDebug("Installing TSC MSR hook.\n");
494     ret = v3_hook_msr(vm, TSC_MSR, 
495                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
496
497     if (ret != 0) {
498         return ret;
499     }
500
501     PrintDebug("Installing TSC_AUX MSR hook.\n");
502     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
503                       tsc_aux_msr_write_hook, NULL);
504
505     if (ret != 0) {
506         return ret;
507     }
508
509     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
510     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
511                                 handle_cpufreq_hcall, NULL);
512
513     PrintDebug("Setting base time dilation factor.\n");
514     vm->time_state.td_mult = 1;
515
516     return ret;
517 }
518
519 void v3_deinit_time_vm(struct v3_vm_info * vm) {
520     v3_unhook_msr(vm, TSC_MSR);
521     v3_unhook_msr(vm, TSC_AUX_MSR);
522
523     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
524 }
525
526 void v3_init_time_core(struct guest_info * info) {
527     struct vm_core_time * time_state = &(info->time_state);
528     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
529     char * khz = NULL;
530
531     time_state->host_cpu_freq = V3_CPU_KHZ();
532     khz = v3_cfg_val(cfg_tree, "khz");
533
534     if (khz) {
535         time_state->guest_cpu_freq = atoi(khz);
536         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
537                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
538     } 
539     
540     if ( (khz == NULL) || 
541          (time_state->guest_cpu_freq <= 0)  || 
542          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
543
544         time_state->guest_cpu_freq = time_state->host_cpu_freq;
545     }
546
547     PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
548                info->pcpu_id, info->vcpu_id,
549                time_state->guest_cpu_freq, 
550                time_state->host_cpu_freq);
551
552     time_state->initial_time = 0;
553     time_state->last_update = 0;
554     time_state->guest_host_offset = 0;
555     time_state->tsc_guest_offset = 0;
556     time_state->enter_time = 0;
557     time_state->exit_time = 0;
558     time_state->pause_time = 0;
559
560     INIT_LIST_HEAD(&(time_state->timeout_hooks));
561     time_state->next_timeout = (ullong_t)-1;
562
563     INIT_LIST_HEAD(&(time_state->timers));
564     time_state->num_timers = 0;
565     
566     time_state->tsc_aux.lo = 0;
567     time_state->tsc_aux.hi = 0;
568 }
569
570
571 void v3_deinit_time_core(struct guest_info * core) {
572     struct vm_core_time * time_state = &(core->time_state);
573     struct v3_timer * tmr = NULL;
574     struct v3_timer * tmp = NULL;
575
576     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
577         v3_remove_timer(core, tmr);
578     }
579
580 }