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.


Updates to some debugging of time virtualization. Adding infrasructer to support...
[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.vm_enter_host_time = 0;
94     info->time_state.vm_pause_host_time = t; 
95     info->time_state.initial_host_time = t;
96     info->yield_start_cycle = t;
97
98     info->time_state.last_update = 0;
99     info->time_state.guest_cycles = 0;
100     PrintDebug("Starting time for core %d at host time %llu/guest time %llu.\n",
101                info->vcpu_id, t, info->time_state.guest_cycles); 
102     v3_yield(info);
103     return 0;
104 }
105
106 int v3_offset_time( struct guest_info * info, sint64_t offset )
107 {
108     struct vm_core_time * time_state = &(info->time_state);
109     if (info->vm_info->time_state.follow_host_time) {
110         PrintError("Cannot offset guest time passage while slaved to host clock.\n");
111         return 1;
112     } else {
113         time_state->guest_cycles += offset;
114     }
115     return 0;
116 }
117
118 int v3_skip_time(struct guest_info * info) {
119     if (info->vm_info->time_state.follow_host_time) {
120         PrintError("Cannot skip guest time passage while slaved to host clock.\n");
121         return 1;
122     } else {
123         info->time_state.vm_pause_host_time = v3_get_host_time(&info->time_state);
124     }
125     return 0;
126 }
127
128 static sint64_t host_to_guest_cycles(struct guest_info * info, sint64_t host_cycles) {
129     return (host_cycles * info->time_state.clock_ratio_num) / info->time_state.clock_ratio_denom;
130 }
131
132 int v3_time_advance_cycles(struct guest_info * info, uint64_t *host_cycles)
133 {
134     uint64_t t = v3_get_host_time(&info->time_state);
135
136     info->time_state.vm_pause_host_time = t;
137
138     if (info->vm_info->time_state.follow_host_time) {
139         /* How many guest cycles should have elapsed? */
140         sint64_t host_elapsed = t - info->time_state.initial_host_time;
141         sint64_t guest_elapsed = host_to_guest_cycles(info, host_elapsed);
142
143         info->time_state.guest_cycles = guest_elapsed;
144     } else {
145         uint64_t guest_cycles;
146         if (*host_cycles) {
147             guest_cycles = host_to_guest_cycles(info, *host_cycles);
148         } else {
149             guest_cycles = host_to_guest_cycles(info, (sint64_t)(t - info->time_state.vm_pause_host_time));
150         }
151         info->time_state.guest_cycles += guest_cycles;
152     } 
153
154     return 0;
155 }
156
157 int v3_advance_time(struct guest_info * info) {
158     return v3_time_advance_cycles(info, NULL);
159 }
160
161 /* Called immediately upon entry in the the VMM */
162 int 
163 v3_time_exit_vm( struct guest_info * info, uint64_t * host_cycles ) 
164 {
165     return v3_time_advance_cycles(info, host_cycles);
166 }
167
168 /* Called immediately prior to entry to the VM */
169 int 
170 v3_time_enter_vm( struct guest_info * info )
171 {
172     struct vm_core_time * time_state = &(info->time_state);
173     uint64_t host_time = v3_get_host_time(&info->time_state);
174
175     time_state->vm_enter_host_time = host_time;
176     return 0;
177 }
178         
179
180            
181 struct v3_timer * v3_add_timer(struct guest_info * info, 
182                                struct v3_timer_ops * ops, 
183                                void * private_data) {
184     struct v3_timer * timer = NULL;
185     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
186     V3_ASSERT(timer != NULL);
187
188     timer->ops = ops;
189     timer->private_data = private_data;
190
191     list_add(&(timer->timer_link), &(info->time_state.timers));
192     info->time_state.num_timers++;
193
194     return timer;
195 }
196
197 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
198     list_del(&(timer->timer_link));
199     info->time_state.num_timers--;
200
201     V3_Free(timer);
202     return 0;
203 }
204
205 void v3_update_timers(struct guest_info * info) {
206     struct vm_core_time *time_state = &info->time_state;
207     struct v3_timer * tmp_timer;
208     sint64_t cycles;
209     uint64_t old_time = time_state->last_update;
210
211     time_state->last_update = v3_get_guest_time(time_state);
212     cycles = (sint64_t)(time_state->last_update - old_time);
213     if (cycles < 0) {
214         PrintError("Cycles appears to have rolled over - old time %lld, current time %lld.\n",
215                    old_time, time_state->last_update);
216         return;
217     }
218
219     PrintDebug("Updating timers with %lld elapsed cycles.\n", cycles);
220     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
221         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
222     }
223 }
224
225
226 /* 
227  * Handle full virtualization of the time stamp counter.  As noted
228  * above, we don't store the actual value of the TSC, only the guest's
229  * offset from monotonic guest's time. If the guest writes to the TSC, we
230  * handle this by changing that offset.
231  *
232  * Possible TODO: Proper hooking of TSC read/writes?
233  */ 
234
235 int v3_rdtsc(struct guest_info * info) {
236     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
237
238     info->vm_regs.rdx = tscval >> 32;
239     info->vm_regs.rax = tscval & 0xffffffffLL;
240
241     return 0;
242 }
243
244 int v3_handle_rdtsc(struct guest_info * info) {
245     v3_rdtsc(info);
246     
247     info->vm_regs.rax &= 0x00000000ffffffffLL;
248     info->vm_regs.rdx &= 0x00000000ffffffffLL;
249
250     info->rip += 2;
251     
252     return 0;
253 }
254
255 int v3_rdtscp(struct guest_info * info) {
256     int ret;
257     /* First get the MSR value that we need. It's safe to futz with
258      * ra/c/dx here since they're modified by this instruction anyway. */
259     info->vm_regs.rcx = TSC_AUX_MSR; 
260     ret = v3_handle_msr_read(info);
261
262     if (ret != 0) {
263         return ret;
264     }
265
266     info->vm_regs.rcx = info->vm_regs.rax;
267
268     /* Now do the TSC half of the instruction */
269     ret = v3_rdtsc(info);
270
271     if (ret != 0) {
272         return ret;
273     }
274
275     return 0;
276 }
277
278
279 int v3_handle_rdtscp(struct guest_info * info) {
280   PrintDebug("Handling virtual RDTSCP call.\n");
281
282     v3_rdtscp(info);
283
284     info->vm_regs.rax &= 0x00000000ffffffffLL;
285     info->vm_regs.rcx &= 0x00000000ffffffffLL;
286     info->vm_regs.rdx &= 0x00000000ffffffffLL;
287
288     info->rip += 3;
289     
290     return 0;
291 }
292
293 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
294                                  struct v3_msr *msr_val, void *priv) {
295     struct vm_core_time * time_state = &(info->time_state);
296
297     V3_ASSERT(msr_num == TSC_AUX_MSR);
298
299     msr_val->lo = time_state->tsc_aux.lo;
300     msr_val->hi = time_state->tsc_aux.hi;
301
302     return 0;
303 }
304
305 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
306                               struct v3_msr msr_val, void *priv) {
307     struct vm_core_time * time_state = &(info->time_state);
308
309     V3_ASSERT(msr_num == TSC_AUX_MSR);
310
311     time_state->tsc_aux.lo = msr_val.lo;
312     time_state->tsc_aux.hi = msr_val.hi;
313
314     return 0;
315 }
316
317 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
318                              struct v3_msr *msr_val, void *priv) {
319     uint64_t time = v3_get_guest_tsc(&info->time_state);
320
321     V3_ASSERT(msr_num == TSC_MSR);
322
323     msr_val->hi = time >> 32;
324     msr_val->lo = time & 0xffffffffLL;
325     
326     return 0;
327 }
328
329 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
330                              struct v3_msr msr_val, void *priv) {
331     struct vm_core_time * time_state = &(info->time_state);
332     uint64_t guest_time, new_tsc;
333
334     V3_ASSERT(msr_num == TSC_MSR);
335
336     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
337     guest_time = v3_get_guest_time(time_state);
338     time_state->tsc_guest_offset = (sint64_t)(new_tsc - guest_time); 
339
340     return 0;
341 }
342
343
344 int v3_init_time_vm(struct v3_vm_info * vm) {
345     int ret;
346
347     PrintDebug("Installing TSC MSR hook.\n");
348     ret = v3_hook_msr(vm, TSC_MSR, 
349                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
350
351     if (ret != 0) {
352         return ret;
353     }
354
355     PrintDebug("Installing TSC_AUX MSR hook.\n");
356     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
357                       tsc_aux_msr_write_hook, NULL);
358
359     if (ret != 0) {
360         return ret;
361     }
362
363     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
364     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
365                                 handle_cpufreq_hcall, NULL);
366
367     vm->time_state.td_num = 1;
368     vm->time_state.td_denom = 1;
369     PrintDebug("Setting base time dilation factor to %d/%d.\n", 
370                vm->time_state.td_num, vm->time_state.td_denom);
371
372     vm->time_state.follow_host_time = 1;
373     PrintDebug("Locking guest time to host time.\n");
374     return ret;
375 }
376
377 void v3_deinit_time_vm(struct v3_vm_info * vm) {
378     v3_unhook_msr(vm, TSC_MSR);
379     v3_unhook_msr(vm, TSC_AUX_MSR);
380
381     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
382 }
383
384 void v3_init_time_core(struct guest_info * info) {
385     struct vm_core_time * time_state = &(info->time_state);
386     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
387     char * khz = NULL;
388
389     time_state->host_cpu_freq = V3_CPU_KHZ();
390     khz = v3_cfg_val(cfg_tree, "khz");
391
392     if (khz) {
393         time_state->guest_cpu_freq = atoi(khz);
394         PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n", 
395                    info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
396     } 
397     
398     if ( (khz == NULL) || 
399          (time_state->guest_cpu_freq <= 0)  || 
400          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
401
402         time_state->guest_cpu_freq = time_state->host_cpu_freq;
403     }
404
405     /* Compute these using the GCD() of the guest and host CPU freq.
406      * If the GCD is too small, make it "big enough" */
407     time_state->clock_ratio_num = 1;
408     time_state->clock_ratio_denom = 1;
409
410     PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
411                info->pcpu_id, info->vcpu_id,
412                time_state->guest_cpu_freq, 
413                time_state->host_cpu_freq);
414
415     time_state->guest_cycles = 0;
416     time_state->tsc_guest_offset = 0;
417     time_state->last_update = 0;
418
419     time_state->initial_host_time = 0;
420     time_state->vm_enter_host_time = 0;
421     time_state->vm_pause_host_time = 0;
422
423     INIT_LIST_HEAD(&(time_state->timers));
424     time_state->num_timers = 0;
425     
426     time_state->tsc_aux.lo = 0;
427     time_state->tsc_aux.hi = 0;
428 }
429
430
431 void v3_deinit_time_core(struct guest_info * core) {
432     struct vm_core_time * time_state = &(core->time_state);
433     struct v3_timer * tmr = NULL;
434     struct v3_timer * tmp = NULL;
435
436     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
437         v3_remove_timer(core, tmr);
438     }
439
440 }