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.


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