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.


Minor comment changes to documentation in vmm_time.c
[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_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_offset_time( struct guest_info * info, sint64_t offset )
106 {
107     struct vm_time * time_state = &(info->time_state);
108     PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
109     time_state->guest_host_offset += offset;
110     return 0;
111 }
112
113 #ifdef V3_CONFIG_TIME_DILATION
114 static uint64_t compute_target_host_time(struct guest_info * info, uint64_t guest_time)
115 {
116     struct vm_time * time_state = &(info->time_state);
117     uint64_t guest_elapsed, desired_elapsed;
118     
119     guest_elapsed = (guest_time - time_state->initial_time);
120     desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
121     return time_state->initial_time + desired_elapsed;
122 }
123
124 static uint64_t compute_target_guest_time(struct guest_info *info)
125 {
126     struct vm_time * time_state = &(info->time_state);
127     uint64_t host_elapsed, desired_elapsed;
128
129     host_elapsed = v3_get_host_time(time_state) - time_state->initial_time;
130     desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
131
132     return time_state->initial_time + desired_elapsed;
133
134
135
136 /* Yield time in the host to deal with a guest that wants to run slower than 
137  * the native host cycle frequency */
138 static int yield_host_time(struct guest_info * info) {
139     struct vm_time * time_state = &(info->time_state);
140     uint64_t host_time, target_host_time;
141     uint64_t guest_time, old_guest_time;
142
143     /* Now, let the host run while the guest is stopped to make the two
144      * sync up. Note that this doesn't assume that guest time is stopped;
145      * the offsetting in the next step will change add an offset to guest
146      * time to account for the time paused even if the geust isn't 
147      * usually paused in the VMM. */
148     host_time = v3_get_host_time(time_state);
149     old_guest_time = v3_compute_guest_time(time_state, host_time);
150     target_host_time = compute_target_host_time(info, old_guest_time);
151
152     while (target_host_time > host_time) {
153         v3_yield(info);
154         host_time = v3_get_host_time(time_state);
155     }
156
157     guest_time = v3_compute_guest_time(time_state, host_time);
158
159     /* We do *not* assume the guest timer was paused in the VM. If it was
160      * this offseting is 0. If it wasn't, we need this. */
161     v3_offset_time(info, (sint64_t)(old_guest_time - guest_time));
162
163     return 0;
164 }
165
166 static int skew_guest_time(struct guest_info * info) {
167     struct vm_time * time_state = &(info->time_state);
168     uint64_t target_guest_time, guest_time;
169     /* Now the host may have gotten ahead of the guest because
170      * yielding is a coarse grained thing. Figure out what guest time
171      * we want to be at, and use the use the offsetting mechanism in 
172      * the VMM to make the guest run forward. We limit *how* much we skew 
173      * it forward to prevent the guest time making large jumps, 
174      * however. */
175     target_guest_time = compute_target_guest_time(info);
176     guest_time = v3_get_guest_time(time_state);
177
178     if (guest_time < target_guest_time) {
179         sint64_t max_skew, desired_skew, skew;
180
181         if (time_state->enter_time) {
182             /* Limit forward skew to 10% of the amount the guest has
183              * run since we last could skew time */
184             max_skew = (sint64_t)(guest_time - time_state->enter_time) / 10.0;
185         } else {
186             max_skew = 0;
187         }
188
189         desired_skew = (sint64_t)(target_guest_time - guest_time);
190         skew = desired_skew > max_skew ? max_skew : desired_skew;
191         PrintDebug("Guest %lld cycles behind where it should be.\n",
192                    desired_skew);
193         PrintDebug("Limit on forward skew is %lld. Skewing forward %lld.\n",
194                    max_skew, skew); 
195         
196         v3_offset_time(info, skew);
197     }
198
199     return 0;
200 }
201 #endif /* V3_CONFIG_TIME_DILATION */
202
203 // Control guest time in relation to host time so that the two stay 
204 // appropriately synchronized to the extent possible. 
205 int v3_adjust_time(struct guest_info * info) {
206
207 #ifdef V3_CONFIG_TIME_DILATION
208     /* First deal with yielding if we want to slow down the guest */
209     yield_host_time(info);
210
211     /* Now, if the guest is too slow, (either from excess yielding above,
212      * or because the VMM is doing something that takes a long time to emulate)
213      * allow guest time to jump forward a bit */
214     skew_guest_time(info);
215 #endif
216     return 0;
217 }
218
219 /* Called immediately upon entry in the the VMM */
220 int 
221 v3_time_exit_vm( struct guest_info * info ) 
222 {
223     struct vm_time * time_state = &(info->time_state);
224     
225     time_state->exit_time = v3_get_host_time(time_state);
226
227     return 0;
228 }
229
230 /* Called immediately prior to entry to the VM */
231 int 
232 v3_time_enter_vm( struct guest_info * info )
233 {
234     struct vm_time * time_state = &(info->time_state);
235     uint64_t host_time;
236
237     host_time = v3_get_host_time(time_state);
238     time_state->enter_time = host_time;
239 #ifdef V3_CONFIG_TIME_DILATION
240     { 
241         uint64_t guest_time;
242         sint64_t offset;
243         guest_time = v3_compute_guest_time(time_state, host_time);
244         // XXX we probably want to use an inline function to do these
245         // time differences to deal with sign and overflow carefully
246         offset = (sint64_t)guest_time - (sint64_t)host_time;
247         PrintDebug("v3_time_enter_vm: guest time offset %lld from host time.\n", offset);
248         time_state->guest_host_offset = offset;
249     }
250 #else
251     time_state->guest_host_offset = 0;
252 #endif
253
254     return 0;
255 }
256         
257
258            
259 struct v3_timer * v3_add_timer(struct guest_info * info, 
260                                struct v3_timer_ops * ops, 
261                                void * private_data) {
262     struct v3_timer * timer = NULL;
263     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
264     V3_ASSERT(timer != NULL);
265
266     timer->ops = ops;
267     timer->private_data = private_data;
268
269     list_add(&(timer->timer_link), &(info->time_state.timers));
270     info->time_state.num_timers++;
271
272     return timer;
273 }
274
275 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
276     list_del(&(timer->timer_link));
277     info->time_state.num_timers--;
278
279     V3_Free(timer);
280     return 0;
281 }
282
283 void v3_update_timers(struct guest_info * info) {
284     struct vm_time *time_state = &info->time_state;
285     struct v3_timer * tmp_timer;
286     sint64_t cycles;
287     uint64_t old_time = info->time_state.last_update;
288
289     time_state->last_update = v3_get_guest_time(time_state);
290     cycles = (sint64_t)(time_state->last_update - old_time);
291     V3_ASSERT(cycles >= 0);
292
293     //    V3_Print("Updating timers with %lld elapsed cycles.\n", cycles);
294     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
295         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
296     }
297 }
298
299 /* 
300  * Handle full virtualization of the time stamp counter.  As noted
301  * above, we don't store the actual value of the TSC, only the guest's
302  * offset from monotonic guest's time. If the guest writes to the TSC, we
303  * handle this by changing that offset.
304  *
305  * Possible TODO: Proper hooking of TSC read/writes?
306  */ 
307
308 int v3_rdtsc(struct guest_info * info) {
309     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
310
311     info->vm_regs.rdx = tscval >> 32;
312     info->vm_regs.rax = tscval & 0xffffffffLL;
313
314     return 0;
315 }
316
317 int v3_handle_rdtsc(struct guest_info * info) {
318     v3_rdtsc(info);
319     
320     info->vm_regs.rax &= 0x00000000ffffffffLL;
321     info->vm_regs.rdx &= 0x00000000ffffffffLL;
322
323     info->rip += 2;
324     
325     return 0;
326 }
327
328 int v3_rdtscp(struct guest_info * info) {
329     int ret;
330     /* First get the MSR value that we need. It's safe to futz with
331      * ra/c/dx here since they're modified by this instruction anyway. */
332     info->vm_regs.rcx = TSC_AUX_MSR; 
333     ret = v3_handle_msr_read(info);
334
335     if (ret != 0) {
336         return ret;
337     }
338
339     info->vm_regs.rcx = info->vm_regs.rax;
340
341     /* Now do the TSC half of the instruction */
342     ret = v3_rdtsc(info);
343
344     if (ret != 0) {
345         return ret;
346     }
347
348     return 0;
349 }
350
351
352 int v3_handle_rdtscp(struct guest_info * info) {
353   PrintDebug("Handling virtual RDTSCP call.\n");
354
355     v3_rdtscp(info);
356
357     info->vm_regs.rax &= 0x00000000ffffffffLL;
358     info->vm_regs.rcx &= 0x00000000ffffffffLL;
359     info->vm_regs.rdx &= 0x00000000ffffffffLL;
360
361     info->rip += 3;
362     
363     return 0;
364 }
365
366 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
367                                  struct v3_msr *msr_val, void *priv) {
368     struct vm_time * time_state = &(info->time_state);
369
370     V3_ASSERT(msr_num == TSC_AUX_MSR);
371
372     msr_val->lo = time_state->tsc_aux.lo;
373     msr_val->hi = time_state->tsc_aux.hi;
374
375     return 0;
376 }
377
378 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
379                               struct v3_msr msr_val, void *priv) {
380     struct vm_time * time_state = &(info->time_state);
381
382     V3_ASSERT(msr_num == TSC_AUX_MSR);
383
384     time_state->tsc_aux.lo = msr_val.lo;
385     time_state->tsc_aux.hi = msr_val.hi;
386
387     return 0;
388 }
389
390 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
391                              struct v3_msr *msr_val, void *priv) {
392     uint64_t time = v3_get_guest_tsc(&info->time_state);
393
394     V3_ASSERT(msr_num == TSC_MSR);
395
396     msr_val->hi = time >> 32;
397     msr_val->lo = time & 0xffffffffLL;
398     
399     return 0;
400 }
401
402 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
403                              struct v3_msr msr_val, void *priv) {
404     struct vm_time * time_state = &(info->time_state);
405     uint64_t guest_time, new_tsc;
406
407     V3_ASSERT(msr_num == TSC_MSR);
408
409     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
410     guest_time = v3_get_guest_time(time_state);
411     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
412
413     return 0;
414 }
415
416
417 int v3_init_time_vm(struct v3_vm_info * vm) {
418     int ret;
419
420     PrintDebug("Installing TSC MSR hook.\n");
421     ret = v3_hook_msr(vm, TSC_MSR, 
422                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
423
424     if (ret != 0) {
425         return ret;
426     }
427
428     PrintDebug("Installing TSC_AUX MSR hook.\n");
429     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
430                       tsc_aux_msr_write_hook, NULL);
431
432     if (ret != 0) {
433         return ret;
434     }
435
436     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
437     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
438                                 handle_cpufreq_hcall, NULL);
439
440     return ret;
441 }
442
443 void v3_deinit_time_vm(struct v3_vm_info * vm) {
444     v3_unhook_msr(vm, TSC_MSR);
445     v3_unhook_msr(vm, TSC_AUX_MSR);
446
447     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
448 }
449
450 void v3_init_time_core(struct guest_info * info) {
451     struct vm_time * time_state = &(info->time_state);
452     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
453     char * khz = NULL;
454
455     time_state->host_cpu_freq = V3_CPU_KHZ();
456     khz = v3_cfg_val(cfg_tree, "khz");
457
458     if (khz) {
459         time_state->guest_cpu_freq = atoi(khz);
460         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
461                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
462     } 
463     
464     if ( (khz == NULL) || 
465          (time_state->guest_cpu_freq <= 0)  || 
466          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
467
468         time_state->guest_cpu_freq = time_state->host_cpu_freq;
469     }
470
471     PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
472                info->pcpu_id, info->vcpu_id,
473                time_state->guest_cpu_freq, 
474                time_state->host_cpu_freq);
475
476     time_state->initial_time = 0;
477     time_state->last_update = 0;
478     time_state->guest_host_offset = 0;
479     time_state->tsc_guest_offset = 0;
480
481     INIT_LIST_HEAD(&(time_state->timers));
482     time_state->num_timers = 0;
483     
484     time_state->tsc_aux.lo = 0;
485     time_state->tsc_aux.hi = 0;
486 }
487
488
489 void v3_deinit_time_core(struct guest_info * core) {
490     struct vm_time * time_state = &(core->time_state);
491     struct v3_timer * tmr = NULL;
492     struct v3_timer * tmp = NULL;
493
494     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
495         v3_remove_timer(core, tmr);
496     }
497
498 }