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.


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