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.


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