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.


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