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 RDTSC trapping if we are dilating time.
[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;
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     dilation = v3_cfg_val(cfg, "dilation");
336     if (dilation) {
337         if (!(vm->time_state.flags & VM_TIME_SLAVE_HOST)) {
338             PrintError("Time dilation only valid when slaved to host time.\n");
339         } else {
340             uint32_t num = 1, denom = 1;
341             denom = atoi(dilation);
342             if ((num > 0) && (denom > 0)) {
343                 vm->time_state.td_num = num;
344                 vm->time_state.td_denom = denom;
345             }
346         }
347         if ((vm->time_state.td_num != 1) 
348             || (vm->time_state.td_denom != 1)) {
349             V3_Print("Time dilated from host time by a factor of %d/%d"
350                      " in guest.\n", vm->time_state.td_denom, 
351                      vm->time_state.td_num);
352         } else {
353             PrintError("Time dilation specifier in configuration did not"
354                        " result in actual time dilation in VM.\n");
355         }
356     }
357     return 0;
358 }
359
360 int v3_init_time_vm(struct v3_vm_info * vm) {
361     v3_cfg_tree_t * cfg_tree = vm->cfg_data->cfg;
362     int ret;
363     
364     PrintDebug("Installing TSC MSR hook.\n");
365     ret = v3_hook_msr(vm, TSC_MSR, 
366                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
367
368     if (ret != 0) {
369         return ret;
370     }
371
372     PrintDebug("Installing TSC_AUX MSR hook.\n");
373     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
374                       tsc_aux_msr_write_hook, NULL);
375
376     if (ret != 0) {
377         return ret;
378     }
379
380     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
381     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
382                                 handle_cpufreq_hcall, NULL);
383
384     handle_time_configuration(vm, v3_cfg_subtree(cfg_tree, "time"));
385
386     return ret;
387 }
388
389 void v3_deinit_time_vm(struct v3_vm_info * vm) {
390     v3_unhook_msr(vm, TSC_MSR);
391     v3_unhook_msr(vm, TSC_AUX_MSR);
392
393     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
394 }
395
396 static uint32_t
397 gcd ( uint32_t a, uint32_t b )
398 {
399     uint32_t c;
400     while ( a != 0 ) {
401         c = a; a = b%a;  b = c;
402     }
403     return b;
404 }
405
406 static int compute_core_ratios(struct guest_info * info, 
407                                uint32_t hostKhz, uint32_t guestKhz)
408 {
409     struct vm_core_time * time_state = &(info->time_state);
410     uint32_t khzGCD;
411
412     /* Compute these using the GCD() of the guest and host CPU freq.
413      * If the GCD is too small, make it "big enough" */
414     khzGCD = gcd(hostKhz, guestKhz);
415     if (khzGCD < 1024)
416         khzGCD = 1024;
417
418     time_state->clock_ratio_num = guestKhz / khzGCD;
419     time_state->clock_ratio_denom = hostKhz / khzGCD;
420
421     time_state->ipc_ratio_num = 1;
422     time_state->ipc_ratio_denom = 1;
423
424     return 0;
425 }
426
427 void v3_init_time_core(struct guest_info * info) {
428     struct vm_core_time * time_state = &(info->time_state);
429     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
430     char * khz = NULL;
431
432     time_state->host_cpu_freq = V3_CPU_KHZ();
433     khz = v3_cfg_val(cfg_tree, "khz");
434
435     if (khz) {
436         time_state->guest_cpu_freq = atoi(khz);
437         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
438                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
439     } 
440     
441     if ( (khz == NULL) || 
442          (time_state->guest_cpu_freq <= 0)) {
443 /*  || (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) { */
444         time_state->guest_cpu_freq = time_state->host_cpu_freq;
445     }
446     compute_core_ratios(info, time_state->host_cpu_freq, 
447                         time_state->guest_cpu_freq);
448     
449     time_state->flags = 0;
450     if (info->vm_info->time_state.flags & V3_TIME_SLAVE_HOST) {
451         time_state->flags |= VM_TIME_SLAVE_HOST;
452     }
453     if ((time_state->clock_ratio_denom != 1) ||
454         (time_state->clock_ratio_num != 1) ||
455         (info->vm_info->time_state.td_num != 1) || 
456         (info->vm_info->time_state.td_denom != 1)) { 
457         time_state->flags |= VM_TIME_TRAP_RDTSC;
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     PrintDebug("    td_mult = %d/%d, cl_mult = %u/%u, ipc_mult = %u/%u.\n",
465                info->vm_info->time_state.td_num, 
466                info->vm_info->time_state.td_denom, 
467                time_state->clock_ratio_num, time_state->clock_ratio_denom,
468                time_state->ipc_ratio_num, time_state->ipc_ratio_denom);
469     PrintDebug("    source = %s, rdtsc trapping = %s\n", 
470                (time_state->flags & VM_TIME_SLAVE_HOST) ? "host" : "none",
471                (time_state->flags & VM_TIME_TRAP_RDTSC) ? "true" : "false");
472
473     time_state->guest_cycles = 0;
474     time_state->tsc_guest_offset = 0;
475     time_state->last_update = 0;
476     time_state->initial_host_time = 0;
477
478     INIT_LIST_HEAD(&(time_state->timers));
479     time_state->num_timers = 0;
480             
481     time_state->tsc_aux.lo = 0;
482     time_state->tsc_aux.hi = 0;
483 }
484
485
486 void v3_deinit_time_core(struct guest_info * core) {
487     struct vm_core_time * time_state = &(core->time_state);
488     struct v3_timer * tmr = NULL;
489     struct v3_timer * tmp = NULL;
490
491     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
492         v3_remove_timer(core, tmr);
493     }
494 }