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.


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