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