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.


Merged updates from devel, added more time debugging information
[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->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     v3_rdtsc(info);
215     
216     info->vm_regs.rax &= 0x00000000ffffffffLL;
217     info->vm_regs.rdx &= 0x00000000ffffffffLL;
218
219     info->rip += 2;
220     
221     return 0;
222 }
223
224 int v3_rdtscp(struct guest_info * info) {
225     int ret;
226     /* First get the MSR value that we need. It's safe to futz with
227      * ra/c/dx here since they're modified by this instruction anyway. */
228     info->vm_regs.rcx = TSC_AUX_MSR; 
229     ret = v3_handle_msr_read(info);
230
231     if (ret != 0) {
232         return ret;
233     }
234
235     info->vm_regs.rcx = info->vm_regs.rax;
236
237     /* Now do the TSC half of the instruction */
238     ret = v3_rdtsc(info);
239
240     if (ret != 0) {
241         return ret;
242     }
243
244     return 0;
245 }
246
247
248 int v3_handle_rdtscp(struct guest_info * info) {
249   PrintDebug("Handling virtual RDTSCP call.\n");
250
251     v3_rdtscp(info);
252
253     info->vm_regs.rax &= 0x00000000ffffffffLL;
254     info->vm_regs.rcx &= 0x00000000ffffffffLL;
255     info->vm_regs.rdx &= 0x00000000ffffffffLL;
256
257     info->rip += 3;
258     
259     return 0;
260 }
261
262 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
263                                  struct v3_msr *msr_val, void *priv) {
264     struct vm_core_time * time_state = &(info->time_state);
265
266     V3_ASSERT(msr_num == TSC_AUX_MSR);
267
268     msr_val->lo = time_state->tsc_aux.lo;
269     msr_val->hi = time_state->tsc_aux.hi;
270
271     return 0;
272 }
273
274 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
275                               struct v3_msr msr_val, void *priv) {
276     struct vm_core_time * time_state = &(info->time_state);
277
278     V3_ASSERT(msr_num == TSC_AUX_MSR);
279
280     time_state->tsc_aux.lo = msr_val.lo;
281     time_state->tsc_aux.hi = msr_val.hi;
282
283     return 0;
284 }
285
286 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
287                              struct v3_msr *msr_val, void *priv) {
288     uint64_t time = v3_get_guest_tsc(&info->time_state);
289
290     V3_ASSERT(msr_num == TSC_MSR);
291
292     msr_val->hi = time >> 32;
293     msr_val->lo = time & 0xffffffffLL;
294     
295     return 0;
296 }
297
298 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
299                              struct v3_msr msr_val, void *priv) {
300     struct vm_core_time * time_state = &(info->time_state);
301     uint64_t guest_time, new_tsc;
302
303             V3_ASSERT(msr_num == TSC_MSR);
304
305             new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
306             guest_time = v3_get_guest_time(time_state);
307             time_state->tsc_guest_offset = (sint64_t)(new_tsc - guest_time); 
308
309             return 0;
310         }
311
312 static int
313 handle_time_configuration(struct v3_vm_info * vm, v3_cfg_tree_t *cfg) {
314     char *source, *dilation;
315
316     vm->time_state.flags = V3_TIME_SLAVE_HOST;
317     vm->time_state.td_num = vm->time_state.td_denom = 1;
318
319     if (!cfg) return 0;
320
321     source = v3_cfg_val(cfg, "source");
322     if (source) {
323         if (strcasecmp(source, "none") == 0) {
324             vm->time_state.flags &= ~V3_TIME_SLAVE_HOST;
325         } else if (strcasecmp(source, "host") != 0) {
326             PrintError("Unknown time source for VM core time management.\n");
327         } else {
328             PrintDebug("VM time slaved to host TSC.\n");
329         }
330     }
331
332     dilation = v3_cfg_val(cfg, "dilation");
333     if (dilation) {
334         if (!(vm->time_state.flags & VM_TIME_SLAVE_HOST)) {
335             PrintError("Time dilation only valid when slaved to host time.\n");
336         } else {
337             uint32_t num = 1, denom = 1;
338             denom = atoi(dilation);
339             if ((num > 0) && (denom > 0)) {
340                 vm->time_state.td_num = num;
341                 vm->time_state.td_denom = denom;
342             }
343         }
344         if ((vm->time_state.td_num != 1) 
345             || (vm->time_state.td_denom != 1)) {
346             V3_Print("Time dilated from host time by a factor of %d/%d"
347                      " in guest.\n", vm->time_state.td_denom, 
348                      vm->time_state.td_num);
349         } else {
350             PrintError("Time dilation specifier in configuration did not"
351                        " result in actual time dilation in VM.\n");
352         }
353     }
354     return 0;
355 }
356
357 int v3_init_time_vm(struct v3_vm_info * vm) {
358     v3_cfg_tree_t * cfg_tree = vm->cfg_data->cfg;
359     int ret;
360     
361     PrintDebug("Installing TSC MSR hook.\n");
362     ret = v3_hook_msr(vm, TSC_MSR, 
363                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
364
365     if (ret != 0) {
366         return ret;
367     }
368
369     PrintDebug("Installing TSC_AUX MSR hook.\n");
370     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
371                       tsc_aux_msr_write_hook, NULL);
372
373     if (ret != 0) {
374         return ret;
375     }
376
377     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
378     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
379                                 handle_cpufreq_hcall, NULL);
380
381     handle_time_configuration(vm, v3_cfg_subtree(cfg_tree, "time"));
382
383     return ret;
384 }
385
386 void v3_deinit_time_vm(struct v3_vm_info * vm) {
387     v3_unhook_msr(vm, TSC_MSR);
388     v3_unhook_msr(vm, TSC_AUX_MSR);
389
390     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
391 }
392
393 static uint32_t
394 gcd ( uint32_t a, uint32_t b )
395 {
396     uint32_t c;
397     while ( a != 0 ) {
398         c = a; a = b%a;  b = c;
399     }
400     return b;
401 }
402
403 static int compute_core_ratios(struct guest_info * info, 
404                                uint32_t hostKhz, uint32_t guestKhz)
405 {
406     struct vm_core_time * time_state = &(info->time_state);
407     uint32_t khzGCD;
408
409     /* Compute these using the GCD() of the guest and host CPU freq.
410      * If the GCD is too small, make it "big enough" */
411     khzGCD = gcd(hostKhz, guestKhz);
412     if (khzGCD < 1024)
413         khzGCD = 1024;
414
415     time_state->clock_ratio_num = guestKhz / khzGCD;
416     time_state->clock_ratio_denom = hostKhz / khzGCD;
417
418     time_state->ipc_ratio_num = 1;
419     time_state->ipc_ratio_denom = 1;
420
421     return 0;
422 }
423
424 void v3_init_time_core(struct guest_info * info) {
425     struct vm_core_time * time_state = &(info->time_state);
426     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
427     char * khz = NULL;
428
429     time_state->host_cpu_freq = V3_CPU_KHZ();
430     khz = v3_cfg_val(cfg_tree, "khz");
431
432     if (khz) {
433         time_state->guest_cpu_freq = atoi(khz);
434         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
435                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
436     } 
437     
438     if ( (khz == NULL) || 
439          (time_state->guest_cpu_freq <= 0)) {
440 /*  || (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) { */
441         time_state->guest_cpu_freq = time_state->host_cpu_freq;
442     }
443     compute_core_ratios(info, time_state->host_cpu_freq, 
444                         time_state->guest_cpu_freq);
445     
446     time_state->flags = 0;
447     if (info->vm_info->time_state.flags & V3_TIME_SLAVE_HOST) {
448         time_state->flags |= VM_TIME_SLAVE_HOST;
449     }
450     if ((time_state->clock_ratio_denom != 1) ||
451         (time_state->clock_ratio_num != 1)) {
452         time_state->flags |= VM_TIME_TRAP_RDTSC;
453     }
454
455     PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
456                info->pcpu_id, info->vcpu_id,
457                time_state->guest_cpu_freq, 
458                time_state->host_cpu_freq);
459     PrintDebug("    td_mult = %d/%d, cl_mult = %u/%u, ipc_mult = %u/%u.\n",
460                info->vm_info->time_state.td_num, 
461                info->vm_info->time_state.td_denom, 
462                time_state->clock_ratio_num, time_state->clock_ratio_denom,
463                time_state->ipc_ratio_num, time_state->ipc_ratio_denom);
464     PrintDebug("    source = %s, rdtsc trapping = %s\n", 
465                (time_state->flags & VM_TIME_SLAVE_HOST) ? "host" : "none",
466                (time_state->flags & VM_TIME_TRAP_RDTSC) ? "true" : "false");
467
468     time_state->guest_cycles = 0;
469     time_state->tsc_guest_offset = 0;
470     time_state->last_update = 0;
471     time_state->initial_host_time = 0;
472
473     INIT_LIST_HEAD(&(time_state->timers));
474     time_state->num_timers = 0;
475             
476     time_state->tsc_aux.lo = 0;
477     time_state->tsc_aux.hi = 0;
478 }
479
480
481 void v3_deinit_time_core(struct guest_info * core) {
482     struct vm_core_time * time_state = &(core->time_state);
483     struct v3_timer * tmr = NULL;
484     struct v3_timer * tmp = NULL;
485
486     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
487         v3_remove_timer(core, tmr);
488     }
489 }