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.


change virtualized time configuration
[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 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
90     info->time_state.enter_time = 0;
91     info->time_state.exit_time = t; 
92     info->time_state.last_update = t;
93     info->time_state.initial_time = t;
94     info->yield_start_cycle = t;
95
96     return 0;
97 }
98
99 // Control guest time in relation to host time so that the two stay 
100 // appropriately synchronized to the extent possible. 
101 int v3_adjust_time(struct guest_info * info) {
102     struct vm_time * time_state = &(info->time_state);
103     uint64_t host_time, target_host_time;
104     uint64_t guest_time, target_guest_time, old_guest_time;
105     uint64_t guest_elapsed, host_elapsed, desired_elapsed;
106
107     /* Compute the target host time given how much time has *already*
108      * passed in the guest */
109     guest_time = v3_get_guest_time(time_state);
110     guest_elapsed = (guest_time - time_state->initial_time);
111     desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
112     target_host_time = time_state->initial_time + desired_elapsed;
113
114     /* Now, let the host run while the guest is stopped to make the two
115      * sync up. */
116     host_time = v3_get_host_time(time_state);
117     old_guest_time = v3_get_guest_time(time_state);
118
119     while (target_host_time > host_time) {
120         v3_yield(info);
121         host_time = v3_get_host_time(time_state);
122     }
123
124     guest_time = v3_get_guest_time(time_state);
125
126     // We do *not* assume the guest timer was paused in the VM. If it was
127     // this offseting is 0. If it wasn't we need this.
128     v3_offset_time(info, (sint64_t)old_guest_time - (sint64_t)guest_time);
129
130     /* Now the host may have gotten ahead of the guest because
131      * yielding is a coarse grained thing. Figure out what guest time
132      * we want to be at, and use the use the offsetting mechanism in 
133      * the VMM to make the guest run forward. We limit *how* much we skew 
134      * it forward to prevent the guest time making large jumps, 
135      * however. */
136     host_elapsed = host_time - time_state->initial_time;
137     desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
138     target_guest_time = time_state->initial_time + desired_elapsed;
139
140     if (guest_time < target_guest_time) {
141         uint64_t max_skew, desired_skew, skew;
142
143         if (time_state->enter_time) {
144             max_skew = (time_state->exit_time - time_state->enter_time) / 10;
145         } else {
146             max_skew = 0;
147         }
148
149         desired_skew = target_guest_time - guest_time;
150         skew = desired_skew > max_skew ? max_skew : desired_skew;
151 /*      PrintDebug("Guest %llu cycles behind where it should be.\n",
152                    desired_skew);
153         PrintDebug("Limit on forward skew is %llu. Skewing forward %llu.\n",
154                    max_skew, skew); */
155         
156         v3_offset_time(info, skew);
157     }
158     
159     return 0;
160 }
161
162 /* Called immediately upon entry in the the VMM */
163 int 
164 v3_time_exit_vm( struct guest_info * info ) 
165 {
166     struct vm_time * time_state = &(info->time_state);
167     
168     time_state->exit_time = v3_get_host_time(time_state);
169
170     return 0;
171 }
172
173 /* Called immediately prior to entry to the VM */
174 int 
175 v3_time_enter_vm( struct guest_info * info )
176 {
177     struct vm_time * time_state = &(info->time_state);
178     uint64_t guest_time, host_time;
179
180     guest_time = v3_get_guest_time(time_state);
181     host_time = v3_get_host_time(time_state);
182     time_state->enter_time = host_time;
183     time_state->guest_host_offset = guest_time - host_time;
184
185     // Because we just modified the offset - shouldn't matter as this should be 
186     // the last time-related call prior to entering the VMM, but worth it 
187     // just in case.
188     time_state->exit_time = host_time; 
189
190     return 0;
191 }
192         
193 int v3_offset_time( struct guest_info * info, sint64_t offset )
194 {
195     struct vm_time * time_state = &(info->time_state);
196 //    PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
197     time_state->guest_host_offset += offset;
198     return 0;
199 }
200            
201 struct v3_timer * v3_add_timer(struct guest_info * info, 
202                                struct v3_timer_ops * ops, 
203                                void * private_data) {
204     struct v3_timer * timer = NULL;
205     timer = (struct v3_timer *)V3_Malloc(sizeof(struct v3_timer));
206     V3_ASSERT(timer != NULL);
207
208     timer->ops = ops;
209     timer->private_data = private_data;
210
211     list_add(&(timer->timer_link), &(info->time_state.timers));
212     info->time_state.num_timers++;
213
214     return timer;
215 }
216
217 int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) {
218     list_del(&(timer->timer_link));
219     info->time_state.num_timers--;
220
221     V3_Free(timer);
222     return 0;
223 }
224
225 void v3_update_timers(struct guest_info * info) {
226     struct vm_time *time_state = &info->time_state;
227     struct v3_timer * tmp_timer;
228     uint64_t old_time = info->time_state.last_update;
229     sint64_t cycles;
230
231     time_state->last_update = v3_get_guest_time(time_state);
232     cycles = time_state->last_update - old_time;
233
234     list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
235         tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
236     }
237 }
238
239 /* 
240  * Handle full virtualization of the time stamp counter.  As noted
241  * above, we don't store the actual value of the TSC, only the guest's
242  * offset from monotonic guest's time. If the guest writes to the TSC, we
243  * handle this by changing that offset.
244  *
245  * Possible TODO: Proper hooking of TSC read/writes?
246  */ 
247
248 int v3_rdtsc(struct guest_info * info) {
249     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
250
251     info->vm_regs.rdx = tscval >> 32;
252     info->vm_regs.rax = tscval & 0xffffffffLL;
253
254     return 0;
255 }
256
257 int v3_handle_rdtsc(struct guest_info * info) {
258     v3_rdtsc(info);
259     
260     info->vm_regs.rax &= 0x00000000ffffffffLL;
261     info->vm_regs.rdx &= 0x00000000ffffffffLL;
262
263     info->rip += 2;
264     
265     return 0;
266 }
267
268 int v3_rdtscp(struct guest_info * info) {
269     int ret;
270     /* First get the MSR value that we need. It's safe to futz with
271      * ra/c/dx here since they're modified by this instruction anyway. */
272     info->vm_regs.rcx = TSC_AUX_MSR; 
273     ret = v3_handle_msr_read(info);
274
275     if (ret != 0) {
276         return ret;
277     }
278
279     info->vm_regs.rcx = info->vm_regs.rax;
280
281     /* Now do the TSC half of the instruction */
282     ret = v3_rdtsc(info);
283
284     if (ret != 0) {
285         return ret;
286     }
287
288     return 0;
289 }
290
291
292 int v3_handle_rdtscp(struct guest_info * info) {
293   PrintDebug("Handling virtual RDTSCP call.\n");
294
295     v3_rdtscp(info);
296
297     info->vm_regs.rax &= 0x00000000ffffffffLL;
298     info->vm_regs.rcx &= 0x00000000ffffffffLL;
299     info->vm_regs.rdx &= 0x00000000ffffffffLL;
300
301     info->rip += 3;
302     
303     return 0;
304 }
305
306 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
307                                  struct v3_msr *msr_val, void *priv) {
308     struct vm_time * time_state = &(info->time_state);
309
310     V3_ASSERT(msr_num == TSC_AUX_MSR);
311
312     msr_val->lo = time_state->tsc_aux.lo;
313     msr_val->hi = time_state->tsc_aux.hi;
314
315     return 0;
316 }
317
318 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
319                               struct v3_msr msr_val, void *priv) {
320     struct vm_time * time_state = &(info->time_state);
321
322     V3_ASSERT(msr_num == TSC_AUX_MSR);
323
324     time_state->tsc_aux.lo = msr_val.lo;
325     time_state->tsc_aux.hi = msr_val.hi;
326
327     return 0;
328 }
329
330 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
331                              struct v3_msr *msr_val, void *priv) {
332     uint64_t time = v3_get_guest_tsc(&info->time_state);
333
334     V3_ASSERT(msr_num == TSC_MSR);
335
336     msr_val->hi = time >> 32;
337     msr_val->lo = time & 0xffffffffLL;
338     
339     return 0;
340 }
341
342 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
343                              struct v3_msr msr_val, void *priv) {
344     struct vm_time * time_state = &(info->time_state);
345     uint64_t guest_time, new_tsc;
346
347     V3_ASSERT(msr_num == TSC_MSR);
348
349     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
350     guest_time = v3_get_guest_time(time_state);
351     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
352
353     return 0;
354 }
355
356
357 int v3_init_time_vm(struct v3_vm_info * vm) {
358     int ret;
359
360     PrintDebug("Installing TSC MSR hook.\n");
361     ret = v3_hook_msr(vm, TSC_MSR, 
362                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
363
364     if (ret != 0) {
365         return ret;
366     }
367
368     PrintDebug("Installing TSC_AUX MSR hook.\n");
369     ret = v3_hook_msr(vm, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
370                       tsc_aux_msr_write_hook, NULL);
371
372     if (ret != 0) {
373         return ret;
374     }
375
376     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
377     ret = v3_register_hypercall(vm, TIME_CPUFREQ_HCALL, 
378                                 handle_cpufreq_hcall, NULL);
379
380     return ret;
381 }
382
383 void v3_deinit_time_vm(struct v3_vm_info * vm) {
384     v3_unhook_msr(vm, TSC_MSR);
385     v3_unhook_msr(vm, TSC_AUX_MSR);
386
387     v3_remove_hypercall(vm, TIME_CPUFREQ_HCALL);
388 }
389
390 void v3_init_time_core(struct guest_info * info) {
391     struct vm_time * time_state = &(info->time_state);
392     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
393     char * khz = NULL;
394
395     time_state->host_cpu_freq = V3_CPU_KHZ();
396     khz = v3_cfg_val(cfg_tree, "khz");
397
398     if (khz) {
399         time_state->guest_cpu_freq = atoi(khz);
400         PrintDebug("Core %d CPU frequency requested at %d khz.\n", 
401                    info->cpu_id, time_state->guest_cpu_freq);
402     } 
403     
404     if ( (khz == NULL) || 
405          (time_state->guest_cpu_freq <= 0)  || 
406          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
407
408         time_state->guest_cpu_freq = time_state->host_cpu_freq;
409     }
410
411     PrintDebug("Core %d CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", 
412                info->cpu_id, 
413                time_state->guest_cpu_freq, 
414                time_state->host_cpu_freq);
415
416     time_state->initial_time = 0;
417     time_state->last_update = 0;
418     time_state->guest_host_offset = 0;
419     time_state->tsc_guest_offset = 0;
420
421     INIT_LIST_HEAD(&(time_state->timers));
422     time_state->num_timers = 0;
423     
424     time_state->tsc_aux.lo = 0;
425     time_state->tsc_aux.hi = 0;
426 }
427
428
429 void v3_deinit_time_core(struct guest_info * core) {
430     struct vm_time * time_state = &(core->time_state);
431     struct v3_timer * tmr = NULL;
432     struct v3_timer * tmp = NULL;
433
434     list_for_each_entry_safe(tmr, tmp, &(time_state->timers), timer_link) {
435         v3_remove_timer(core, tmr);
436     }
437
438 }