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 debugging of fixed-timekeeping change
[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 stored as an absolute number of cycles elapsed
45  *     and is updated on guest entry and exit; it can also be updated explicitly
46  *     in the monitor at times
47  * (3) The actual guest timestamp counter (which can be written by
48  *     writing to the guest TSC MSR - MSR 0x10) from the monotonic guest TSC.
49  *     This is also computed as an offset from (2) above when the TSC and
50  *     this offset is updated when the TSC MSR is written.
51  *
52  * Because all other devices are slaved off of the passage of time in the guest,
53  * it is (2) above that drives the firing of other timers in the guest, 
54  * including timer devices such as the Programmable Interrupt Timer (PIT).
55  *
56  * Future additions:
57  * (1) Add support for temporarily skewing guest time off of where it should
58  *     be to support slack simulation of guests. The idea is that simulators
59  *     set this skew to be the difference between how much time passed for a 
60  *     simulated feature and a real implementation of that feature, making time
61  *     pass at a different rate from real time on this core. The VMM will then
62  *     attempt to move this skew back towards 0 subject to resolution/accuracy
63  *     constraints from various system timers.
64  *   
65  *     The main effort in doing this will be to get accuracy/resolution 
66  *     information from each local timer and to use this to bound how much skew
67  *     is removed on each exit.
68  *
69  * (2) Look more into sychronizing the offsets *across* virtual and physical 
70  *     cores so that multicore guests stay mostly in sync.
71  *
72  * (3) Look into using the AMD TSC multiplier feature and adding explicit time
73  *     dilation support to time handling.
74  */
75
76
77 static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
78     struct vm_core_time * time_state = &(info->time_state);
79
80     info->vm_regs.rbx = time_state->guest_cpu_freq;
81
82     PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx);
83     
84     return 0;
85 }
86
87
88
89 int v3_start_time(struct guest_info * info) {
90     /* We start running with guest_time == host_time */
91     uint64_t t = v3_get_host_time(&info->time_state); 
92
93     info->time_state.enter_time = 0;
94     info->time_state.exit_time = t; 
95     info->time_state.initial_time = t;
96     info->yield_start_cycle = t;
97
98     info->time_state.last_update = 0;
99     info->time_state.guest_cycles = 0;
100     PrintDebug("Starting time for core %d at host time %llu/guest time %llu.\n",
101                info->vcpu_id, t, info->time_state.guest_cycles); 
102     v3_yield(info);
103     return 0;
104 }
105
106 int v3_offset_time( struct guest_info * info, sint64_t offset )
107 {
108     struct vm_core_time * time_state = &(info->time_state);
109     PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
110     time_state->guest_cycles += offset;
111     return 0;
112 }
113
114 #ifdef V3_CONFIG_TIME_DILATION
115 static uint64_t compute_target_host_time(struct guest_info * info, uint64_t guest_time)
116 {
117     struct vm_core_time * time_state = &(info->time_state);
118     uint64_t guest_elapsed, desired_elapsed;
119     
120     guest_elapsed = (guest_time - time_state->initial_time);
121     desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
122     return time_state->initial_time + desired_elapsed;
123 }
124
125 static uint64_t compute_target_guest_time(struct guest_info *info)
126 {
127     struct vm_core_time * time_state = &(info->time_state);
128     uint64_t host_elapsed, desired_elapsed;
129
130     host_elapsed = v3_get_host_time(time_state) - time_state->initial_time;
131     desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
132
133     return time_state->initial_time + desired_elapsed;
134
135
136
137 /* Yield time in the host to deal with a guest that wants to run slower than 
138  * the native host cycle frequency */
139 static int yield_host_time(struct guest_info * info) {
140     struct vm_core_time * time_state = &(info->time_state);
141     uint64_t host_time, target_host_time;
142     uint64_t guest_time, old_guest_time;
143
144     /* Now, let the host run while the guest is stopped to make the two
145      * sync up. Note that this doesn't assume that guest time is stopped;
146      * the offsetting in the next step will change add an offset to guest
147      * time to account for the time paused even if the geust isn't 
148      * usually paused in the VMM. */
149     host_time = v3_get_host_time(time_state);
150     old_guest_time = v3_get_guest_time(time_state);
151     target_host_time = compute_target_host_time(info, old_guest_time);
152
153     while (target_host_time > host_time) {
154         v3_yield(info);
155         host_time = v3_get_host_time(time_state);
156     }
157
158     guest_time = v3_get_guest_time(time_state);
159
160     /* We do *not* assume the guest timer was paused in the VM. If it was
161      * this offseting is 0. If it wasn't, we need this. */
162     v3_offset_time(info, (sint64_t)(old_guest_time - guest_time));
163
164     return 0;
165 }
166
167 static int skew_guest_time(struct guest_info * info) {
168     struct vm_core_time * time_state = &(info->time_state);
169     uint64_t target_guest_time, guest_time;
170     /* Now the host may have gotten ahead of the guest because
171      * yielding is a coarse grained thing. Figure out what guest time
172      * we want to be at, and use the use the offsetting mechanism in 
173      * the VMM to make the guest run forward. We limit *how* much we skew 
174      * it forward to prevent the guest time making large jumps, 
175      * however. */
176     target_guest_time = compute_target_guest_time(info);
177     guest_time = v3_get_guest_time(time_state);
178
179     if (guest_time < target_guest_time) {
180         sint64_t max_skew, desired_skew, skew;
181
182         if (time_state->enter_time) {
183             /* Limit forward skew to 10% of the amount the guest has
184              * run since we last could skew time */
185             max_skew = (sint64_t)(guest_time - time_state->enter_time) / 10;
186         } else {
187             max_skew = 0;
188         }
189
190         desired_skew = (sint64_t)(target_guest_time - guest_time);
191         skew = desired_skew > max_skew ? max_skew : desired_skew;
192         PrintDebug("Guest %lld cycles behind where it should be.\n",
193                    desired_skew);
194         PrintDebug("Limit on forward skew is %lld. Skewing forward %lld.\n",
195                    max_skew, skew); 
196         
197         v3_offset_time(info, skew);
198     }
199
200     return 0;
201 }
202 #endif /* V3_CONFIG_TIME_DILATION */
203
204 // Control guest time in relation to host time so that the two stay 
205 // appropriately synchronized to the extent possible. 
206 int v3_adjust_time(struct guest_info * info) {
207
208 #ifdef V3_CONFIG_TIME_DILATION
209     /* First deal with yielding if we want to slow down the guest */
210     yield_host_time(info);
211
212     /* Now, if the guest is too slow, (either from excess yielding above,
213      * or because the VMM is doing something that takes a long time to emulate)
214      * allow guest time to jump forward a bit */
215     skew_guest_time(info);
216 #endif
217     return 0;
218 }
219
220 /* Called immediately upon entry in the the VMM */
221 int 
222 v3_time_exit_vm( struct guest_info * info, uint64_t * guest_cycles ) 
223 {
224     struct vm_core_time * time_state = &(info->time_state);
225     
226     time_state->exit_time = v3_get_host_time(time_state);
227     if (guest_cycles) {
228         time_state->guest_cycles += *guest_cycles;
229     } else {
230         uint64_t cycles_exec;
231         cycles_exec = time_state->exit_time - time_state->enter_time;
232         time_state->guest_cycles += cycles_exec;
233     }
234     return 0;
235 }
236
237 /* Called immediately prior to entry to the VM */
238 int 
239 v3_time_enter_vm( struct guest_info * info )
240 {
241     struct vm_core_time * time_state = &(info->time_state);
242     uint64_t host_time, vmm_cycles;
243
244     host_time = v3_get_host_time(time_state);
245     time_state->enter_time = host_time;
246     vmm_cycles = host_time - time_state->exit_time;
247     /* XXX How do we want to take into account host/guest CPU speed differences
248      * and time dilation here? Probably time just won't advance in the VMM in that
249      * case so its irrelvant XXX */
250     time_state->guest_cycles += vmm_cycles; 
251     return 0;
252 }
253         
254
255            
256 struct v3_timer * v3_add_timer(struct guest_info * info, 
257                                struct v3_timer_ops * ops, 
258                                void * private_data) {
259     struct v3_timer * timer = NULL;
260     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
261     V3_ASSERT(timer != NULL);
262
263     timer->ops = ops;
264     timer->private_data = private_data;
265
266     list_add(&(timer->timer_link), &(info->time_state.timers));
267     info->time_state.num_timers++;
268
269     return timer;
270 }
271
272 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
273     list_del(&(timer->timer_link));
274     info->time_state.num_timers--;
275
276     V3_Free(timer);
277     return 0;
278 }
279
280 void v3_update_timers(struct guest_info * info) {
281     struct vm_core_time *time_state = &info->time_state;
282     struct v3_timer * tmp_timer;
283     sint64_t cycles;
284     uint64_t old_time = info->time_state.last_update;
285
286     time_state->last_update = v3_get_guest_time(time_state);
287     cycles = (sint64_t)(time_state->last_update - old_time);
288     V3_ASSERT(cycles >= 0);
289
290     //    V3_Print("Updating timers with %lld elapsed cycles.\n", cycles);
291     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
292         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
293     }
294 }
295
296
297 /* 
298  * Handle full virtualization of the time stamp counter.  As noted
299  * above, we don't store the actual value of the TSC, only the guest's
300  * offset from monotonic guest's time. If the guest writes to the TSC, we
301  * handle this by changing that offset.
302  *
303  * Possible TODO: Proper hooking of TSC read/writes?
304  */ 
305
306 int v3_rdtsc(struct guest_info * info) {
307     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
308
309     info->vm_regs.rdx = tscval >> 32;
310     info->vm_regs.rax = tscval & 0xffffffffLL;
311
312     return 0;
313 }
314
315 int v3_handle_rdtsc(struct guest_info * info) {
316     v3_rdtsc(info);
317     
318     info->vm_regs.rax &= 0x00000000ffffffffLL;
319     info->vm_regs.rdx &= 0x00000000ffffffffLL;
320
321     info->rip += 2;
322     
323     return 0;
324 }
325
326 int v3_rdtscp(struct guest_info * info) {
327     int ret;
328     /* First get the MSR value that we need. It's safe to futz with
329      * ra/c/dx here since they're modified by this instruction anyway. */
330     info->vm_regs.rcx = TSC_AUX_MSR; 
331     ret = v3_handle_msr_read(info);
332
333     if (ret != 0) {
334         return ret;
335     }
336
337     info->vm_regs.rcx = info->vm_regs.rax;
338
339     /* Now do the TSC half of the instruction */
340     ret = v3_rdtsc(info);
341
342     if (ret != 0) {
343         return ret;
344     }
345
346     return 0;
347 }
348
349
350 int v3_handle_rdtscp(struct guest_info * info) {
351   PrintDebug("Handling virtual RDTSCP call.\n");
352
353     v3_rdtscp(info);
354
355     info->vm_regs.rax &= 0x00000000ffffffffLL;
356     info->vm_regs.rcx &= 0x00000000ffffffffLL;
357     info->vm_regs.rdx &= 0x00000000ffffffffLL;
358
359     info->rip += 3;
360     
361     return 0;
362 }
363
364 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
365                                  struct v3_msr *msr_val, void *priv) {
366     struct vm_core_time * time_state = &(info->time_state);
367
368     V3_ASSERT(msr_num == TSC_AUX_MSR);
369
370     msr_val->lo = time_state->tsc_aux.lo;
371     msr_val->hi = time_state->tsc_aux.hi;
372
373     return 0;
374 }
375
376 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
377                               struct v3_msr msr_val, void *priv) {
378     struct vm_core_time * time_state = &(info->time_state);
379
380     V3_ASSERT(msr_num == TSC_AUX_MSR);
381
382     time_state->tsc_aux.lo = msr_val.lo;
383     time_state->tsc_aux.hi = msr_val.hi;
384
385     return 0;
386 }
387
388 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
389                              struct v3_msr *msr_val, void *priv) {
390     uint64_t time = v3_get_guest_tsc(&info->time_state);
391
392     V3_ASSERT(msr_num == TSC_MSR);
393
394     msr_val->hi = time >> 32;
395     msr_val->lo = time & 0xffffffffLL;
396     
397     return 0;
398 }
399
400 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
401                              struct v3_msr msr_val, void *priv) {
402     struct vm_core_time * time_state = &(info->time_state);
403     uint64_t guest_time, new_tsc;
404
405     V3_ASSERT(msr_num == TSC_MSR);
406
407     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
408     guest_time = v3_get_guest_time(time_state);
409     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
410
411     return 0;
412 }
413
414
415 int v3_init_time_vm(struct v3_vm_info * vm) {
416     int ret;
417
418     PrintDebug("Installing TSC MSR hook.\n");
419     ret = v3_hook_msr(vm, TSC_MSR, 
420                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
421
422     if (ret != 0) {
423         return ret;
424     }
425
426     PrintDebug("Installing TSC_AUX MSR hook.\n");
427     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
428                       tsc_aux_msr_write_hook, NULL);
429
430     if (ret != 0) {
431         return ret;
432     }
433
434     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
435     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
436                                 handle_cpufreq_hcall, NULL);
437
438     vm->time_state.td_mult = 1;
439     PrintDebug("Setting base time dilation factor to %d.\n", vm->time_state.td_mult);
440
441     return ret;
442 }
443
444 void v3_deinit_time_vm(struct v3_vm_info * vm) {
445     v3_unhook_msr(vm, TSC_MSR);
446     v3_unhook_msr(vm, TSC_AUX_MSR);
447
448     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
449 }
450
451 void v3_init_time_core(struct guest_info * info) {
452     struct vm_core_time * time_state = &(info->time_state);
453     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
454     char * khz = NULL;
455
456     time_state->host_cpu_freq = V3_CPU_KHZ();
457     khz = v3_cfg_val(cfg_tree, "khz");
458
459     if (khz) {
460         time_state->guest_cpu_freq = atoi(khz);
461         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
462                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
463     } 
464     
465     if ( (khz == NULL) || 
466          (time_state->guest_cpu_freq <= 0)  || 
467          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
468
469         time_state->guest_cpu_freq = time_state->host_cpu_freq;
470     }
471
472     PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
473                info->pcpu_id, info->vcpu_id,
474                time_state->guest_cpu_freq, 
475                time_state->host_cpu_freq);
476
477     time_state->initial_time = 0;
478     time_state->last_update = 0;
479     time_state->tsc_guest_offset = 0;
480     time_state->enter_time = 0;
481     time_state->exit_time = 0;
482     time_state->guest_cycles = 0;
483
484     INIT_LIST_HEAD(&(time_state->timers));
485     time_state->num_timers = 0;
486     
487     time_state->tsc_aux.lo = 0;
488     time_state->tsc_aux.hi = 0;
489 }
490
491
492 void v3_deinit_time_core(struct guest_info * core) {
493     struct vm_core_time * time_state = &(core->time_state);
494     struct v3_timer * tmr = NULL;
495     struct v3_timer * tmp = NULL;
496
497     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
498         v3_remove_timer(core, tmr);
499     }
500
501 }