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.


initial simulation functionality
[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;
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, uint64_t * guest_cycles ) 
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
327 /* 
328  * Handle full virtualization of the time stamp counter.  As noted
329  * above, we don't store the actual value of the TSC, only the guest's
330  * offset from monotonic guest's time. If the guest writes to the TSC, we
331  * handle this by changing that offset.
332  *
333  * Possible TODO: Proper hooking of TSC read/writes?
334  */ 
335
336 int v3_rdtsc(struct guest_info * info) {
337     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
338
339     info->vm_regs.rdx = tscval >> 32;
340     info->vm_regs.rax = tscval & 0xffffffffLL;
341
342     return 0;
343 }
344
345 int v3_handle_rdtsc(struct guest_info * info) {
346     v3_rdtsc(info);
347     
348     info->vm_regs.rax &= 0x00000000ffffffffLL;
349     info->vm_regs.rdx &= 0x00000000ffffffffLL;
350
351     info->rip += 2;
352     
353     return 0;
354 }
355
356 int v3_rdtscp(struct guest_info * info) {
357     int ret;
358     /* First get the MSR value that we need. It's safe to futz with
359      * ra/c/dx here since they're modified by this instruction anyway. */
360     info->vm_regs.rcx = TSC_AUX_MSR; 
361     ret = v3_handle_msr_read(info);
362
363     if (ret != 0) {
364         return ret;
365     }
366
367     info->vm_regs.rcx = info->vm_regs.rax;
368
369     /* Now do the TSC half of the instruction */
370     ret = v3_rdtsc(info);
371
372     if (ret != 0) {
373         return ret;
374     }
375
376     return 0;
377 }
378
379
380 int v3_handle_rdtscp(struct guest_info * info) {
381   PrintDebug("Handling virtual RDTSCP call.\n");
382
383     v3_rdtscp(info);
384
385     info->vm_regs.rax &= 0x00000000ffffffffLL;
386     info->vm_regs.rcx &= 0x00000000ffffffffLL;
387     info->vm_regs.rdx &= 0x00000000ffffffffLL;
388
389     info->rip += 3;
390     
391     return 0;
392 }
393
394 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
395                                  struct v3_msr *msr_val, void *priv) {
396     struct vm_core_time * time_state = &(info->time_state);
397
398     V3_ASSERT(msr_num == TSC_AUX_MSR);
399
400     msr_val->lo = time_state->tsc_aux.lo;
401     msr_val->hi = time_state->tsc_aux.hi;
402
403     return 0;
404 }
405
406 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
407                               struct v3_msr msr_val, void *priv) {
408     struct vm_core_time * time_state = &(info->time_state);
409
410     V3_ASSERT(msr_num == TSC_AUX_MSR);
411
412     time_state->tsc_aux.lo = msr_val.lo;
413     time_state->tsc_aux.hi = msr_val.hi;
414
415     return 0;
416 }
417
418 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
419                              struct v3_msr *msr_val, void *priv) {
420     uint64_t time = v3_get_guest_tsc(&info->time_state);
421
422     V3_ASSERT(msr_num == TSC_MSR);
423
424     msr_val->hi = time >> 32;
425     msr_val->lo = time & 0xffffffffLL;
426     
427     return 0;
428 }
429
430 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
431                              struct v3_msr msr_val, void *priv) {
432     struct vm_core_time * time_state = &(info->time_state);
433     uint64_t guest_time, new_tsc;
434
435     V3_ASSERT(msr_num == TSC_MSR);
436
437     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
438     guest_time = v3_get_guest_time(time_state);
439     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
440
441     return 0;
442 }
443
444
445 int v3_init_time_vm(struct v3_vm_info * vm) {
446     int ret;
447
448     PrintDebug("Installing TSC MSR hook.\n");
449     ret = v3_hook_msr(vm, TSC_MSR, 
450                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
451
452     if (ret != 0) {
453         return ret;
454     }
455
456     PrintDebug("Installing TSC_AUX MSR hook.\n");
457     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
458                       tsc_aux_msr_write_hook, NULL);
459
460     if (ret != 0) {
461         return ret;
462     }
463
464     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
465     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
466                                 handle_cpufreq_hcall, NULL);
467
468     PrintDebug("Setting base time dilation factor.\n");
469     vm->time_state.td_mult = 1;
470
471     return ret;
472 }
473
474 void v3_deinit_time_vm(struct v3_vm_info * vm) {
475     v3_unhook_msr(vm, TSC_MSR);
476     v3_unhook_msr(vm, TSC_AUX_MSR);
477
478     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
479 }
480
481 void v3_init_time_core(struct guest_info * info) {
482     struct vm_core_time * time_state = &(info->time_state);
483     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
484     char * khz = NULL;
485
486     time_state->host_cpu_freq = V3_CPU_KHZ();
487     khz = v3_cfg_val(cfg_tree, "khz");
488
489     if (khz) {
490         time_state->guest_cpu_freq = atoi(khz);
491         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
492                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
493     } 
494     
495     if ( (khz == NULL) || 
496          (time_state->guest_cpu_freq <= 0)  || 
497          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
498
499         time_state->guest_cpu_freq = time_state->host_cpu_freq;
500     }
501
502     PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
503                info->pcpu_id, info->vcpu_id,
504                time_state->guest_cpu_freq, 
505                time_state->host_cpu_freq);
506
507     time_state->initial_time = 0;
508     time_state->last_update = 0;
509     time_state->guest_host_offset = 0;
510     time_state->tsc_guest_offset = 0;
511     time_state->enter_time = 0;
512     time_state->exit_time = 0;
513     time_state->pause_time = 0;
514
515
516     INIT_LIST_HEAD(&(time_state->timers));
517     time_state->num_timers = 0;
518     
519     time_state->tsc_aux.lo = 0;
520     time_state->tsc_aux.hi = 0;
521 }
522
523
524 void v3_deinit_time_core(struct guest_info * core) {
525     struct vm_core_time * time_state = &(core->time_state);
526     struct v3_timer * tmr = NULL;
527     struct v3_timer * tmp = NULL;
528
529     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
530         v3_remove_timer(core, tmr);
531     }
532
533 }