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.


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