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.


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