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.


More minor fixes on time keeping. VMM clock now appears to be essentially accurate...
[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     info->time_state.last_update = t;
90     info->time_state.initial_time = t;
91     info->yield_start_cycle = t;
92     return 0;
93 }
94
95 // If the guest is supposed to run slower than the host, yield out until
96 // the host time is appropriately far along;
97 int v3_adjust_time(struct guest_info * info) {
98     struct vm_time * time_state = &(info->time_state);
99
100     if (time_state->host_cpu_freq == time_state->guest_cpu_freq) {
101         time_state->guest_host_offset = 0;
102     } else {
103         uint64_t guest_time, guest_elapsed, desired_elapsed;
104         uint64_t host_time, target_host_time;
105
106         guest_time = v3_get_guest_time(time_state);
107
108         /* Compute what host time this guest time should correspond to. */
109         guest_elapsed = (guest_time - time_state->initial_time);
110         desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
111         target_host_time = time_state->initial_time + desired_elapsed;
112
113         /* Yield until that host time is reached */
114         host_time = v3_get_host_time(time_state);
115         while (host_time < target_host_time) {
116             v3_yield(info);
117             host_time = v3_get_host_time(time_state);
118         }
119
120         time_state->guest_host_offset = (sint64_t)guest_time - (sint64_t)host_time;
121     }
122     return 0;
123 }
124
125 int v3_add_timer(struct guest_info * info, struct vm_timer_ops * ops, 
126              void * private_data) {
127     struct vm_timer * timer = NULL;
128     timer = (struct vm_timer *)V3_Malloc(sizeof(struct vm_timer));
129     V3_ASSERT(timer != NULL);
130
131     timer->ops = ops;
132     timer->private_data = private_data;
133
134     list_add(&(timer->timer_link), &(info->time_state.timers));
135     info->time_state.num_timers++;
136
137     return 0;
138 }
139
140 int v3_remove_timer(struct guest_info * info, struct vm_timer * timer) {
141     list_del(&(timer->timer_link));
142     info->time_state.num_timers--;
143
144     V3_Free(timer);
145     return 0;
146 }
147
148 void v3_update_timers(struct guest_info * info) {
149     struct vm_timer * tmp_timer;
150     uint64_t old_time = info->time_state.last_update;
151     uint64_t cycles;
152
153     info->time_state.last_update = v3_get_guest_time(&info->time_state);
154     cycles = info->time_state.last_update - old_time;
155
156     list_for_each_entry(tmp_timer, &(info->time_state.timers), timer_link) {
157         tmp_timer->ops->update_timer(info, cycles, info->time_state.guest_cpu_freq, tmp_timer->private_data);
158     }
159 }
160
161 /* 
162  * Handle full virtualization of the time stamp counter.  As noted
163  * above, we don't store the actual value of the TSC, only the guest's
164  * offset from monotonic guest's time. If the guest writes to the TSC, we
165  * handle this by changing that offset.
166  *
167  * Possible TODO: Proper hooking of TSC read/writes?
168  */ 
169
170 int v3_rdtsc(struct guest_info * info) {
171     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
172     info->vm_regs.rdx = tscval >> 32;
173     info->vm_regs.rax = tscval & 0xffffffffLL;
174     return 0;
175 }
176
177 int v3_handle_rdtsc(struct guest_info * info) {
178     v3_rdtsc(info);
179     
180     info->vm_regs.rax &= 0x00000000ffffffffLL;
181     info->vm_regs.rdx &= 0x00000000ffffffffLL;
182
183     info->rip += 2;
184     
185     return 0;
186 }
187
188 int v3_rdtscp(struct guest_info * info) {
189     int ret;
190     /* First get the MSR value that we need. It's safe to futz with
191      * ra/c/dx here since they're modified by this instruction anyway. */
192     info->vm_regs.rcx = TSC_AUX_MSR; 
193     ret = v3_handle_msr_read(info);
194     if (ret) return ret;
195     info->vm_regs.rcx = info->vm_regs.rax;
196
197     /* Now do the TSC half of the instruction */
198     ret = v3_rdtsc(info);
199     if (ret) return ret;
200     
201     return 0;
202 }
203
204
205 int v3_handle_rdtscp(struct guest_info * info) {
206
207     v3_rdtscp(info);
208     
209     info->vm_regs.rax &= 0x00000000ffffffffLL;
210     info->vm_regs.rcx &= 0x00000000ffffffffLL;
211     info->vm_regs.rdx &= 0x00000000ffffffffLL;
212
213     info->rip += 3;
214     
215     return 0;
216 }
217
218 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
219                                  struct v3_msr *msr_val, void *priv) {
220     struct vm_time * time_state = &(info->time_state);
221
222     V3_ASSERT(msr_num == TSC_AUX_MSR);
223     msr_val->lo = time_state->tsc_aux.lo;
224     msr_val->hi = time_state->tsc_aux.hi;
225
226     return 0;
227 }
228
229 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
230                               struct v3_msr msr_val, void *priv) {
231     struct vm_time * time_state = &(info->time_state);
232
233     V3_ASSERT(msr_num == TSC_AUX_MSR);
234     time_state->tsc_aux.lo = msr_val.lo;
235     time_state->tsc_aux.hi = msr_val.hi;
236
237     return 0;
238 }
239
240 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
241                              struct v3_msr *msr_val, void *priv) {
242     uint64_t time = v3_get_guest_tsc(&info->time_state);
243
244     V3_ASSERT(msr_num == TSC_MSR);
245     msr_val->hi = time >> 32;
246     msr_val->lo = time & 0xffffffffLL;
247     
248     return 0;
249 }
250
251 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
252                              struct v3_msr msr_val, void *priv) {
253     struct vm_time * time_state = &(info->time_state);
254     uint64_t guest_time, new_tsc;
255     V3_ASSERT(msr_num == TSC_MSR);
256     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
257     guest_time = v3_get_guest_time(time_state);
258     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
259
260     return 0;
261 }
262
263
264 static int init_vm_time(struct v3_vm_info *vm_info) {
265     int ret;
266
267     PrintDebug("Installing TSC MSR hook.\n");
268     ret = v3_hook_msr(vm_info, TSC_MSR, 
269                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
270
271     PrintDebug("Installing TSC_AUX MSR hook.\n");
272     if (ret) return ret;
273     ret = v3_hook_msr(vm_info, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
274                       tsc_aux_msr_write_hook, NULL);
275     if (ret) return ret;
276
277     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
278     ret = v3_register_hypercall(vm_info, TIME_CPUFREQ_HCALL, 
279                                 handle_cpufreq_hcall, NULL);
280     return ret;
281 }
282
283 void v3_init_time(struct guest_info * info) {
284     struct vm_time * time_state = &(info->time_state);
285     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
286     static int one_time = 0;
287     char *khz;
288
289     time_state->host_cpu_freq = V3_CPU_KHZ();
290     khz = v3_cfg_val(cfg_tree, "khz");
291     if (khz) {
292         time_state->guest_cpu_freq = atoi(khz);
293         PrintDebug("Core %d CPU frequency requested at %d khz.\n", 
294                    info->cpu_id, time_state->guest_cpu_freq);
295     }
296     
297     if (!khz || time_state->guest_cpu_freq > time_state->host_cpu_freq) {
298         time_state->guest_cpu_freq = time_state->host_cpu_freq;
299     }
300     PrintDebug("Core %d CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n", info->cpu_id, time_state->guest_cpu_freq, time_state->host_cpu_freq);
301
302     time_state->initial_time = 0;
303     time_state->last_update = 0;
304     time_state->guest_host_offset = 0;
305     time_state->tsc_guest_offset = 0;
306
307     INIT_LIST_HEAD(&(time_state->timers));
308     time_state->num_timers = 0;
309     
310     time_state->tsc_aux.lo = 0;
311     time_state->tsc_aux.hi = 0;
312
313     if (!one_time) {
314         init_vm_time(info->vm_info);
315         one_time = 1;
316     }
317 }
318
319
320
321
322
323
324
325
326