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.


Cleanup and sanity-checking of before/after null-check and copy+paste errors (Coverit...
[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 (!source || (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=0;
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
413     if (ret!=0) { 
414         return ret;
415     }
416
417     PrintDebug(vm, VCORE_NONE, "Registering TIME_RDHTSC hypercall.\n");
418     ret = v3_register_hypercall(vm, TIME_RDHTSC_HCALL, 
419                                 handle_rdhtsc_hcall, NULL);
420
421     if (ret!=0) { 
422         return ret;
423     }
424
425     handle_time_configuration(vm, v3_cfg_subtree(cfg_tree, "time"));
426
427     return ret;
428 }
429
430 void v3_deinit_time_vm(struct v3_vm_info * vm) {
431     v3_unhook_msr(vm, TSC_MSR);
432     v3_unhook_msr(vm, TSC_AUX_MSR);
433
434     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
435 }
436
437 static uint32_t
438 gcd ( uint32_t a, uint32_t b )
439 {
440     uint32_t c;
441     while ( a != 0 ) {
442         c = a; a = b%a;  b = c;
443     }
444     return b;
445 }
446
447 static int compute_core_ratios(struct guest_info * info, 
448                                uint32_t hostKhz, uint32_t guestKhz)
449 {
450     struct vm_core_time * time_state = &(info->time_state);
451     uint32_t khzGCD;
452
453     /* Compute these using the GCD() of the guest and host CPU freq.
454      * If the GCD is too small, make it "big enough" */
455     khzGCD = gcd(hostKhz, guestKhz);
456     if (khzGCD < 1024)
457         khzGCD = 1024;
458
459     time_state->clock_ratio_num = guestKhz / khzGCD;
460     time_state->clock_ratio_denom = hostKhz / khzGCD;
461
462     time_state->ipc_ratio_num = 1;
463     time_state->ipc_ratio_denom = 1;
464
465     return 0;
466 }
467
468 void v3_init_time_core(struct guest_info * info) {
469     struct vm_core_time * time_state = &(info->time_state);
470     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
471     char * khz = NULL;
472
473     time_state->host_cpu_freq = V3_CPU_KHZ();
474     khz = v3_cfg_val(cfg_tree, "khz");
475
476     if (khz) {
477         time_state->guest_cpu_freq = atoi(khz);
478         PrintDebug(info->vm_info, info, "Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
479                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
480     } 
481     
482     if ( (khz == NULL) || 
483          (time_state->guest_cpu_freq <= 0)) {
484 /*  || (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) { */
485         time_state->guest_cpu_freq = time_state->host_cpu_freq;
486     }
487     compute_core_ratios(info, time_state->host_cpu_freq, 
488                         time_state->guest_cpu_freq);
489     
490     time_state->flags = 0;
491     if (info->vm_info->time_state.flags & V3_TIME_SLAVE_HOST) {
492         time_state->flags |= VM_TIME_SLAVE_HOST;
493     }
494     if (info->vm_info->time_state.flags & V3_TIME_TSC_PASSTHROUGH) {
495         time_state->flags |= VM_TIME_TSC_PASSTHROUGH;
496     }
497
498     if ((time_state->clock_ratio_denom != 1) ||
499         (time_state->clock_ratio_num != 1) ||
500         (info->vm_info->time_state.td_num != 1) || 
501         (info->vm_info->time_state.td_denom != 1)) { 
502         if (time_state->flags | VM_TIME_TSC_PASSTHROUGH) {
503             PrintError(info->vm_info, info, "WARNING: Cannot use reqested passthrough TSC with clock or time modification also requested.\n");
504             time_state->flags &= ~VM_TIME_TSC_PASSTHROUGH;
505         }
506         time_state->flags |= VM_TIME_TRAP_RDTSC;
507     }
508
509     PrintDebug(info->vm_info, info, "Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
510                info->pcpu_id, info->vcpu_id,
511                time_state->guest_cpu_freq, 
512                time_state->host_cpu_freq);
513     PrintDebug(info->vm_info, info, "    td_mult = %d/%d, cl_mult = %u/%u, ipc_mult = %u/%u.\n",
514                info->vm_info->time_state.td_num, 
515                info->vm_info->time_state.td_denom, 
516                time_state->clock_ratio_num, time_state->clock_ratio_denom,
517                time_state->ipc_ratio_num, time_state->ipc_ratio_denom);
518     PrintDebug(info->vm_info, info, "    time source = %s, tsc handling =  %s\n", 
519                (time_state->flags & VM_TIME_SLAVE_HOST) ? "host" : "none",
520                (time_state->flags & VM_TIME_TSC_PASSTHROUGH) ? "passthrough" 
521                    : (time_state->flags & VM_TIME_TRAP_RDTSC) ? "trapping" 
522                        : "offsettting");
523
524     time_state->guest_cycles = 0;
525     time_state->tsc_guest_offset = 0;
526     time_state->last_update = 0;
527     time_state->initial_host_time = 0;
528
529     INIT_LIST_HEAD(&(time_state->timers));
530     time_state->num_timers = 0;
531             
532     time_state->tsc_aux.lo = 0;
533     time_state->tsc_aux.hi = 0;
534 }
535
536
537 void v3_deinit_time_core(struct guest_info * core) {
538     struct vm_core_time * time_state = &(core->time_state);
539     struct v3_timer * tmr = NULL;
540     struct v3_timer * tmp = NULL;
541
542     if (*(void**)&time_state->timers) {
543         list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
544         v3_remove_timer(core, tmr);
545         }
546     }
547 }