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.


Added hypercall to read host TSC
[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 static int handle_rdhtsc_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
88     struct vm_core_time * time_state = &(info->time_state);
89
90     info->vm_regs.rbx = v3_get_host_time(time_state);
91
92     // PrintDebug("Guest request host TSC: return %ld\n", (long)info->vm_regs.rbx);
93     
94     return 0;
95 }
96
97
98
99 int v3_start_time(struct guest_info * info) {
100     /* We start running with guest_time == host_time */
101     uint64_t t = v3_get_host_time(&info->time_state); 
102
103     info->time_state.initial_host_time = t;
104     info->yield_start_cycle = t;
105
106     info->time_state.last_update = 0;
107     info->time_state.guest_cycles = 0;
108     PrintDebug("Starting time for core %d at host time %llu/guest time %llu.\n",
109                info->vcpu_id, t, info->time_state.guest_cycles); 
110     v3_yield(info);
111     return 0;
112 }
113
114 static sint64_t 
115 host_to_guest_cycles(struct guest_info * info, sint64_t host_cycles) {
116     struct vm_core_time * core_time_state = &(info->time_state);
117     uint32_t cl_num, cl_denom;
118
119     cl_num = core_time_state->clock_ratio_num;
120     cl_denom = core_time_state->clock_ratio_denom;
121
122     return (host_cycles * cl_num) / cl_denom;
123 }
124
125 /*
126 static sint64_t 
127 guest_to_host_cycles(struct guest_info * info, sint64_t guest_cycles) {
128     struct vm_core_time * core_time_state = &(info->time_state);
129     uint32_t cl_num, cl_denom;
130
131     cl_num = core_time_state->clock_ratio_num;
132     cl_denom = core_time_state->clock_ratio_denom;
133
134     return (guest_cycles * cl_denom) / cl_num;
135 }
136 */
137
138 int v3_advance_time(struct guest_info * info, uint64_t *host_cycles)
139 {
140     uint64_t guest_cycles;
141
142     if (info->time_state.flags & VM_TIME_SLAVE_HOST) {
143         struct v3_time *vm_ts = &(info->vm_info->time_state);
144         uint64_t ht = v3_get_host_time(&info->time_state);
145         uint64_t host_elapsed = ht - info->time_state.initial_host_time;
146         uint64_t dilated_elapsed = (host_elapsed * vm_ts->td_num) / vm_ts->td_denom;
147         uint64_t guest_elapsed = host_to_guest_cycles(info, dilated_elapsed);
148         guest_cycles = guest_elapsed - v3_get_guest_time(&info->time_state);
149     } else if (host_cycles) {
150         guest_cycles = host_to_guest_cycles(info, *host_cycles);
151     } else {
152         guest_cycles = 0;
153     }
154     
155     info->time_state.guest_cycles += guest_cycles;
156
157     return 0;
158
159
160 struct v3_timer * v3_add_timer(struct guest_info * info, 
161                                struct v3_timer_ops * ops, 
162                                void * private_data) {
163     struct v3_timer * timer = NULL;
164     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
165     V3_ASSERT(timer != NULL);
166
167     timer->ops = ops;
168     timer->private_data = private_data;
169
170     list_add(&(timer->timer_link), &(info->time_state.timers));
171     info->time_state.num_timers++;
172
173     return timer;
174 }
175
176 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
177     list_del(&(timer->timer_link));
178     info->time_state.num_timers--;
179
180     V3_Free(timer);
181     return 0;
182 }
183
184 void v3_update_timers(struct guest_info * info) {
185     struct vm_core_time *time_state = &info->time_state;
186     struct v3_timer * tmp_timer;
187     sint64_t cycles;
188     uint64_t old_time = time_state->last_update;
189
190     time_state->last_update = v3_get_guest_time(time_state);
191     cycles = (sint64_t)(time_state->last_update - old_time);
192     if (cycles < 0) {
193         PrintError("Cycles appears to have rolled over - old time %lld, current time %lld.\n",
194                    old_time, time_state->last_update);
195         return;
196     }
197
198     //PrintDebug("Updating timers with %lld elapsed cycles.\n", cycles);
199     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
200         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
201     }
202 }
203
204
205 /* 
206  * Handle full virtualization of the time stamp counter.  As noted
207  * above, we don't store the actual value of the TSC, only the guest's
208  * offset from monotonic guest's time. If the guest writes to the TSC, we
209  * handle this by changing that offset.
210  *
211  * Possible TODO: Proper hooking of TSC read/writes?
212  */ 
213
214 int v3_rdtsc(struct guest_info * info) {
215     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
216
217     info->vm_regs.rdx = tscval >> 32;
218     info->vm_regs.rax = tscval & 0xffffffffLL;
219
220     return 0;
221 }
222
223 int v3_handle_rdtsc(struct guest_info * info) {
224     PrintDebug("Handling virtual RDTSC call.\n");
225     v3_rdtsc(info);
226     
227     info->vm_regs.rax &= 0x00000000ffffffffLL;
228     info->vm_regs.rdx &= 0x00000000ffffffffLL;
229
230     info->rip += 2;
231     
232     return 0;
233 }
234
235 int v3_rdtscp(struct guest_info * info) {
236     int ret;
237     /* First get the MSR value that we need. It's safe to futz with
238      * ra/c/dx here since they're modified by this instruction anyway. */
239     info->vm_regs.rcx = TSC_AUX_MSR; 
240     ret = v3_handle_msr_read(info);
241
242     if (ret != 0) {
243         return ret;
244     }
245
246     info->vm_regs.rcx = info->vm_regs.rax;
247
248     /* Now do the TSC half of the instruction */
249     ret = v3_rdtsc(info);
250
251     if (ret != 0) {
252         return ret;
253     }
254
255     return 0;
256 }
257
258
259 int v3_handle_rdtscp(struct guest_info * info) {
260     PrintDebug("Handling virtual RDTSCP call.\n");
261
262     v3_rdtscp(info);
263
264     info->vm_regs.rax &= 0x00000000ffffffffLL;
265     info->vm_regs.rcx &= 0x00000000ffffffffLL;
266     info->vm_regs.rdx &= 0x00000000ffffffffLL;
267
268     info->rip += 3;
269     
270     return 0;
271 }
272
273 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
274                                  struct v3_msr *msr_val, void *priv) {
275     struct vm_core_time * time_state = &(info->time_state);
276
277     V3_ASSERT(msr_num == TSC_AUX_MSR);
278
279     msr_val->lo = time_state->tsc_aux.lo;
280     msr_val->hi = time_state->tsc_aux.hi;
281
282     return 0;
283 }
284
285 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
286                               struct v3_msr msr_val, void *priv) {
287     struct vm_core_time * time_state = &(info->time_state);
288
289     V3_ASSERT(msr_num == TSC_AUX_MSR);
290
291     time_state->tsc_aux.lo = msr_val.lo;
292     time_state->tsc_aux.hi = msr_val.hi;
293
294     return 0;
295 }
296
297 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
298                              struct v3_msr *msr_val, void *priv) {
299     uint64_t time = v3_get_guest_tsc(&info->time_state);
300
301     PrintDebug("Handling virtual TSC MSR read call.\n");
302     V3_ASSERT(msr_num == TSC_MSR);
303
304     msr_val->hi = time >> 32;
305     msr_val->lo = time & 0xffffffffLL;
306     
307     return 0;
308 }
309
310 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
311                              struct v3_msr msr_val, void *priv) {
312     struct vm_core_time * time_state = &(info->time_state);
313     uint64_t guest_time, new_tsc;
314
315     PrintDebug("Handling virtual TSC MSR write call.\n");
316     V3_ASSERT(msr_num == TSC_MSR);
317
318     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
319     guest_time = v3_get_guest_time(time_state);
320     time_state->tsc_guest_offset = (sint64_t)(new_tsc - guest_time); 
321
322     return 0;
323 }
324
325 static int
326 handle_time_configuration(struct v3_vm_info * vm, v3_cfg_tree_t *cfg) {
327     char *source, *dilation, *tsc;
328
329     vm->time_state.flags = V3_TIME_SLAVE_HOST;
330     vm->time_state.td_num = vm->time_state.td_denom = 1;
331
332     if (!cfg) return 0;
333
334     source = v3_cfg_val(cfg, "source");
335     if (source) {
336         if (strcasecmp(source, "none") == 0) {
337             vm->time_state.flags &= ~V3_TIME_SLAVE_HOST;
338         } else if (strcasecmp(source, "host") != 0) {
339             PrintError("Unknown time source for VM core time management.\n");
340         } else {
341             PrintDebug("VM time slaved to host TSC.\n");
342         }
343     }
344
345     // Should we make a separate TSC device that handles this sort of thing?
346     tsc = v3_cfg_val(cfg, "tsc");
347     if (tsc) {
348         if (strcasecmp(tsc, "host") == 0) {
349             if (!(vm->time_state.flags & V3_TIME_SLAVE_HOST)) {
350                 PrintError("WARNING: Guest TSC set to passthrough host TSC, but guest time not slaved to host time.");
351             }
352             vm->time_state.flags |= V3_TIME_TSC_PASSTHROUGH;
353         } else if (strcasecmp(source, "guest") != 0) {
354             PrintError("ERROR: Unknown TSC configuration in time configuration.\n");
355         }
356     }
357
358     dilation = v3_cfg_val(cfg, "dilation");
359     if (dilation) {
360         if (!(vm->time_state.flags & VM_TIME_SLAVE_HOST)) {
361             PrintError("Time dilation only valid when slaved to host time.\n");
362         } else {
363             uint32_t num = 1, denom = 1;
364             denom = atoi(dilation);
365             if ((num > 0) && (denom > 0)) {
366                 vm->time_state.td_num = num;
367                 vm->time_state.td_denom = denom;
368             }
369         }
370         if ((vm->time_state.td_num != 1) 
371             || (vm->time_state.td_denom != 1)) {
372             V3_Print("Time dilated from host time by a factor of %d/%d"
373                      " in guest.\n", vm->time_state.td_denom, 
374                      vm->time_state.td_num);
375         } else {
376             PrintError("Time dilation specifier in configuration did not"
377                        " result in actual time dilation in VM.\n");
378         }
379     }
380     return 0;
381 }
382
383 int v3_init_time_vm(struct v3_vm_info * vm) {
384     v3_cfg_tree_t * cfg_tree = vm->cfg_data->cfg;
385     int ret;
386     
387     PrintDebug("Installing TSC MSR hook.\n");
388     ret = v3_hook_msr(vm, TSC_MSR, 
389                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
390
391     if (ret != 0) {
392         return ret;
393     }
394
395     PrintDebug("Installing TSC_AUX MSR hook.\n");
396     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
397                       tsc_aux_msr_write_hook, NULL);
398
399     if (ret != 0) {
400         return ret;
401     }
402
403     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
404     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
405                                 handle_cpufreq_hcall, NULL);
406     PrintDebug("Registering TIME_RDHTSC hypercall.\n");
407     ret = v3_register_hypercall(vm, TIME_RDHTSC_HCALL, 
408                                 handle_rdhtsc_hcall, NULL);
409
410     handle_time_configuration(vm, v3_cfg_subtree(cfg_tree, "time"));
411
412     return ret;
413 }
414
415 void v3_deinit_time_vm(struct v3_vm_info * vm) {
416     v3_unhook_msr(vm, TSC_MSR);
417     v3_unhook_msr(vm, TSC_AUX_MSR);
418
419     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
420 }
421
422 static uint32_t
423 gcd ( uint32_t a, uint32_t b )
424 {
425     uint32_t c;
426     while ( a != 0 ) {
427         c = a; a = b%a;  b = c;
428     }
429     return b;
430 }
431
432 static int compute_core_ratios(struct guest_info * info, 
433                                uint32_t hostKhz, uint32_t guestKhz)
434 {
435     struct vm_core_time * time_state = &(info->time_state);
436     uint32_t khzGCD;
437
438     /* Compute these using the GCD() of the guest and host CPU freq.
439      * If the GCD is too small, make it "big enough" */
440     khzGCD = gcd(hostKhz, guestKhz);
441     if (khzGCD < 1024)
442         khzGCD = 1024;
443
444     time_state->clock_ratio_num = guestKhz / khzGCD;
445     time_state->clock_ratio_denom = hostKhz / khzGCD;
446
447     time_state->ipc_ratio_num = 1;
448     time_state->ipc_ratio_denom = 1;
449
450     return 0;
451 }
452
453 void v3_init_time_core(struct guest_info * info) {
454     struct vm_core_time * time_state = &(info->time_state);
455     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
456     char * khz = NULL;
457
458     time_state->host_cpu_freq = V3_CPU_KHZ();
459     khz = v3_cfg_val(cfg_tree, "khz");
460
461     if (khz) {
462         time_state->guest_cpu_freq = atoi(khz);
463         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
464                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
465     } 
466     
467     if ( (khz == NULL) || 
468          (time_state->guest_cpu_freq <= 0)) {
469 /*  || (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) { */
470         time_state->guest_cpu_freq = time_state->host_cpu_freq;
471     }
472     compute_core_ratios(info, time_state->host_cpu_freq, 
473                         time_state->guest_cpu_freq);
474     
475     time_state->flags = 0;
476     if (info->vm_info->time_state.flags & V3_TIME_SLAVE_HOST) {
477         time_state->flags |= VM_TIME_SLAVE_HOST;
478     }
479     if (info->vm_info->time_state.flags & V3_TIME_TSC_PASSTHROUGH) {
480         time_state->flags |= VM_TIME_TSC_PASSTHROUGH;
481     }
482
483     if ((time_state->clock_ratio_denom != 1) ||
484         (time_state->clock_ratio_num != 1) ||
485         (info->vm_info->time_state.td_num != 1) || 
486         (info->vm_info->time_state.td_denom != 1)) { 
487         if (time_state->flags | VM_TIME_TSC_PASSTHROUGH) {
488             PrintError("WARNING: Cannot use reqested passthrough TSC with clock or time modification also requested.\n");
489             time_state->flags &= ~VM_TIME_TSC_PASSTHROUGH;
490         }
491         time_state->flags |= VM_TIME_TRAP_RDTSC;
492     }
493
494     PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
495                info->pcpu_id, info->vcpu_id,
496                time_state->guest_cpu_freq, 
497                time_state->host_cpu_freq);
498     PrintDebug("    td_mult = %d/%d, cl_mult = %u/%u, ipc_mult = %u/%u.\n",
499                info->vm_info->time_state.td_num, 
500                info->vm_info->time_state.td_denom, 
501                time_state->clock_ratio_num, time_state->clock_ratio_denom,
502                time_state->ipc_ratio_num, time_state->ipc_ratio_denom);
503     PrintDebug("    time source = %s, tsc handling =  %s\n", 
504                (time_state->flags & VM_TIME_SLAVE_HOST) ? "host" : "none",
505                (time_state->flags & VM_TIME_TSC_PASSTHROUGH) ? "passthrough" 
506                    : (time_state->flags & VM_TIME_TRAP_RDTSC) ? "trapping" 
507                        : "offsettting");
508
509     time_state->guest_cycles = 0;
510     time_state->tsc_guest_offset = 0;
511     time_state->last_update = 0;
512     time_state->initial_host_time = 0;
513
514     INIT_LIST_HEAD(&(time_state->timers));
515     time_state->num_timers = 0;
516             
517     time_state->tsc_aux.lo = 0;
518     time_state->tsc_aux.hi = 0;
519 }
520
521
522 void v3_deinit_time_core(struct guest_info * core) {
523     struct vm_core_time * time_state = &(core->time_state);
524     struct v3_timer * tmr = NULL;
525     struct v3_timer * tmp = NULL;
526
527     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
528         v3_remove_timer(core, tmr);
529     }
530 }