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.


Fixes to time code to allow virtualization to hide costs of running in the
[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_time.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vm_guest.h>
24
25 #ifndef 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 computed using an offsets from (1) above.
45  * (3) The actual guest timestamp counter (which can be written by
46  *     writing to the guest TSC MSR - MSR 0x10) from the monotonic guest TSC.
47  *     This is also computed as an offset from (2) above when the TSC and
48  *     this offset is updated when the TSC MSR is written.
49  *
50  * The value used to offset the guest TSC from the host TSC is the *sum* of all
51  * of these offsets (2 and 3) above
52  * 
53  * Because all other devices are slaved off of the passage of time in the guest,
54  * it is (2) above that drives the firing of other timers in the guest, 
55  * including timer devices such as the Programmable Interrupt Timer (PIT).
56  *
57  * Future additions:
58  * (1) Add support for temporarily skewing guest time off of where it should
59  *     be to support slack simulation of guests. The idea is that simulators
60  *     set this skew to be the difference between how much time passed for a 
61  *     simulated feature and a real implementation of that feature, making 
62  *     pass at a different rate from real time on this core. The VMM will then
63  *     attempt to move this skew back towards 0 subject to resolution/accuracy
64  *     constraints from various system timers.
65  *   
66  *     The main effort in doing this will be to get accuracy/resolution 
67  *     information from each local timer and to use this to bound how much skew
68  *     is removed on each exit.
69  */
70
71
72 static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
73     struct vm_time * time_state = &(info->time_state);
74
75     info->vm_regs.rbx = time_state->guest_cpu_freq;
76
77     PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx);
78     
79     return 0;
80 }
81
82
83
84 int v3_start_time(struct guest_info * info) {
85     /* We start running with guest_time == host_time */
86     uint64_t t = v3_get_host_time(&info->time_state); 
87
88     PrintDebug("Starting initial guest time as %llu\n", t);
89 #ifdef CONFIG_TIME_HIDE_VM_COST
90     info->time_state.pause_time = t; 
91 #else
92     info->time_state.pause_time = 0; 
93 #endif
94     info->time_state.last_update = t;
95     info->time_state.initial_time = t;
96     info->yield_start_cycle = t;
97     return 0;
98 }
99
100 // If the guest is supposed to run slower than the host, yield out until
101 // the host time is appropriately far along;
102 int v3_adjust_time(struct guest_info * info) {
103     struct vm_time * time_state = &(info->time_state);
104
105     if (time_state->host_cpu_freq != time_state->guest_cpu_freq) {
106         uint64_t guest_time, guest_elapsed, desired_elapsed;
107         uint64_t host_time, target_host_time;
108
109         guest_time = v3_get_guest_time(time_state);
110
111         /* Compute what host time this guest time should correspond to. */
112         guest_elapsed = (guest_time - time_state->initial_time);
113         desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
114         target_host_time = time_state->initial_time + desired_elapsed;
115
116         /* Yield until that host time is reached */
117         host_time = v3_get_host_time(time_state);
118
119         while (host_time < target_host_time) {
120             v3_yield(info);
121             host_time = v3_get_host_time(time_state);
122         }
123
124         // This overrides any pause/unpause times because the difference 
125         // is going to be too big for any pause/unpause the notice.
126         time_state->guest_host_offset = (sint64_t)guest_time - (sint64_t)host_time;
127     }
128
129     return 0;
130 }
131
132 int 
133 v3_pause_time( struct guest_info * info ) 
134 {
135     struct vm_time * time_state = &(info->time_state);
136     if (time_state->pause_time == 0) {
137         time_state->pause_time = v3_get_host_time(time_state);
138 //      PrintDebug("Pausing at host time %llu.\n", time_state->pause_time);
139     } else {
140         PrintError("Palacios timekeeping paused when already paused.\n");
141     }
142     return 0;
143 }
144
145 int 
146 v3_restart_time( struct guest_info * info )
147 {
148     struct vm_time * time_state = &(info->time_state);
149
150     if (time_state->pause_time) {
151         sint64_t pause_diff = (v3_get_host_time(time_state) - time_state->pause_time);
152         time_state->guest_host_offset -= pause_diff;
153         time_state->pause_time = 0;
154 //      PrintDebug("Resuming time after %lld cycles with offset %lld.\n", pause_diff, time_state->guest_host_offset);
155     } else {
156         PrintError( "Palacios time keeping restarted when not paused.");
157     }
158
159     return 0;
160 }
161         
162 int v3_offset_time( struct guest_info * info, sint64_t offset )
163 {
164     struct vm_time * time_state = &(info->time_state);
165 //    PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
166     time_state->guest_host_offset += offset;
167     return 0;
168 }
169            
170 struct v3_timer * v3_add_timer(struct guest_info * info, 
171                                struct v3_timer_ops * ops, 
172                                void * private_data) {
173     struct v3_timer * timer = NULL;
174     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
175     V3_ASSERT(timer != NULL);
176
177     timer->ops = ops;
178     timer->private_data = private_data;
179
180     list_add(&(timer->timer_link), &(info->time_state.timers));
181     info->time_state.num_timers++;
182
183     return timer;
184 }
185
186 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
187     list_del(&(timer->timer_link));
188     info->time_state.num_timers--;
189
190     V3_Free(timer);
191     return 0;
192 }
193
194 void v3_update_timers(struct guest_info * info) {
195     struct vm_time *time_state = &info->time_state;
196     struct v3_timer * tmp_timer;
197     uint64_t old_time = info->time_state.last_update;
198     sint64_t cycles;
199
200     time_state->last_update = v3_get_guest_time(time_state);
201     cycles = time_state->last_update - old_time;
202
203     //    PrintDebug("Updating timer for %lld elapsed cycles (pt=%llu, offset=%lld).\n", 
204     //         cycles, time_state->pause_time, time_state->guest_host_offset);
205
206     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
207         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
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     PrintDebug("Returning %llu as TSC.\n", tscval);
223     info->vm_regs.rdx = tscval >> 32;
224     info->vm_regs.rax = tscval & 0xffffffffLL;
225     return 0;
226 }
227
228 int v3_handle_rdtsc(struct guest_info * info) {
229   PrintDebug("Handling virtual RDTSC call.\n");
230     v3_rdtsc(info);
231     
232     info->vm_regs.rax &= 0x00000000ffffffffLL;
233     info->vm_regs.rdx &= 0x00000000ffffffffLL;
234
235     info->rip += 2;
236     
237     return 0;
238 }
239
240 int v3_rdtscp(struct guest_info * info) {
241     int ret;
242     /* First get the MSR value that we need. It's safe to futz with
243      * ra/c/dx here since they're modified by this instruction anyway. */
244     info->vm_regs.rcx = TSC_AUX_MSR; 
245     ret = v3_handle_msr_read(info);
246
247     if (ret != 0) {
248         return ret;
249     }
250
251     info->vm_regs.rcx = info->vm_regs.rax;
252
253     /* Now do the TSC half of the instruction */
254     ret = v3_rdtsc(info);
255
256     if (ret != 0) {
257         return ret;
258     }
259
260     return 0;
261 }
262
263
264 int v3_handle_rdtscp(struct guest_info * info) {
265   PrintDebug("Handling virtual RDTSCP call.\n");
266
267     v3_rdtscp(info);
268
269     info->vm_regs.rax &= 0x00000000ffffffffLL;
270     info->vm_regs.rcx &= 0x00000000ffffffffLL;
271     info->vm_regs.rdx &= 0x00000000ffffffffLL;
272
273     info->rip += 3;
274     
275     return 0;
276 }
277
278 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
279                                  struct v3_msr *msr_val, void *priv) {
280     struct vm_time * time_state = &(info->time_state);
281
282     V3_ASSERT(msr_num == TSC_AUX_MSR);
283
284     msr_val->lo = time_state->tsc_aux.lo;
285     msr_val->hi = time_state->tsc_aux.hi;
286
287     return 0;
288 }
289
290 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
291                               struct v3_msr msr_val, void *priv) {
292     struct vm_time * time_state = &(info->time_state);
293
294     V3_ASSERT(msr_num == TSC_AUX_MSR);
295
296     time_state->tsc_aux.lo = msr_val.lo;
297     time_state->tsc_aux.hi = msr_val.hi;
298
299     return 0;
300 }
301
302 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
303                              struct v3_msr *msr_val, void *priv) {
304     uint64_t time = v3_get_guest_tsc(&info->time_state);
305
306     V3_ASSERT(msr_num == TSC_MSR);
307
308     msr_val->hi = time >> 32;
309     msr_val->lo = time & 0xffffffffLL;
310     
311     return 0;
312 }
313
314 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
315                              struct v3_msr msr_val, void *priv) {
316     struct vm_time * time_state = &(info->time_state);
317     uint64_t guest_time, new_tsc;
318
319     V3_ASSERT(msr_num == TSC_MSR);
320
321     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
322     guest_time = v3_get_guest_time(time_state);
323     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
324
325     return 0;
326 }
327
328
329 int v3_init_time_vm(struct v3_vm_info * vm) {
330     int ret;
331
332     PrintDebug("Installing TSC MSR hook.\n");
333     ret = v3_hook_msr(vm, TSC_MSR, 
334                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
335
336     if (ret != 0) {
337         return ret;
338     }
339
340     PrintDebug("Installing TSC_AUX MSR hook.\n");
341     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
342                       tsc_aux_msr_write_hook, NULL);
343
344     if (ret != 0) {
345         return ret;
346     }
347
348     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
349     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
350                                 handle_cpufreq_hcall, NULL);
351
352     return ret;
353 }
354
355 void v3_deinit_time_vm(struct v3_vm_info * vm) {
356     v3_unhook_msr(vm, TSC_MSR);
357     v3_unhook_msr(vm, TSC_AUX_MSR);
358
359     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
360 }
361
362 void v3_init_time_core(struct guest_info * info) {
363     struct vm_time * time_state = &(info->time_state);
364     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
365     char * khz = NULL;
366
367     time_state->host_cpu_freq = V3_CPU_KHZ();
368     khz = v3_cfg_val(cfg_tree, "khz");
369
370     if (khz) {
371         time_state->guest_cpu_freq = atoi(khz);
372         PrintDebug("Core %d CPU frequency requested at %d khz.\n", 
373                    info->cpu_id, time_state->guest_cpu_freq);
374     }
375     
376     if ((khz == NULL) || (time_state->guest_cpu_freq > time_state->host_cpu_freq)) {
377         time_state->guest_cpu_freq = time_state->host_cpu_freq;
378     }
379
380     PrintDebug("Core %d CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
381                info->cpu_id, 
382                time_state->guest_cpu_freq, 
383                time_state->host_cpu_freq);
384
385     time_state->initial_time = 0;
386     time_state->last_update = 0;
387     time_state->guest_host_offset = 0;
388     time_state->tsc_guest_offset = 0;
389
390     INIT_LIST_HEAD(&(time_state->timers));
391     time_state->num_timers = 0;
392     
393     time_state->tsc_aux.lo = 0;
394     time_state->tsc_aux.hi = 0;
395
396
397 }
398
399
400 void v3_deinit_time_core(struct guest_info * core) {
401     struct vm_time * time_state = &(core->time_state);
402     struct v3_timer * tmr = NULL;
403     struct v3_timer * tmp = NULL;
404
405     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
406         v3_remove_timer(core, tmr);
407     }
408
409 }
410
411
412
413
414
415