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.


Attempts to support virutally slower processors - broken!
[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 as a multipler/offset 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  * Because all other devices are slaved off of the passage of time in the guest,
51  * it is (2) above that drives the firing of other timers in the guest, 
52  * including timer devices such as the Programmable Interrupt Timer (PIT).
53  *
54  *  
55  *
56  */
57
58
59 static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
60     struct vm_time * time_state = &(info->time_state);
61
62     info->vm_regs.rbx = time_state->guest_cpu_freq;
63
64     PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx);
65     
66     return 0;
67 }
68
69
70
71 int v3_start_time(struct guest_info * info) {
72     /* We start running with guest_time == host_time */
73     uint64_t t = v3_get_host_time(&info->time_state); 
74
75     PrintDebug("Starting initial guest time as %llu\n", t);
76     info->time_state.last_update = t;
77     info->time_state.initial_time = t;
78     info->yield_start_cycle = t;
79     return 0;
80 }
81
82 // If the guest is supposed to run slower than the host, yield out until
83 // the host time is appropriately far along;
84 int v3_adjust_time(struct guest_info * info) {
85     struct vm_time * time_state = &(info->time_state);
86
87     if (time_state->host_cpu_freq == time_state->guest_cpu_freq) {
88         time_state->guest_host_offset = 0;
89     } else {
90         uint64_t guest_time, guest_elapsed, desired_elapsed;
91         uint64_t host_time, target_host_time;
92         guest_time = v3_get_guest_time(time_state);
93         guest_elapsed = (guest_time - time_state->initial_time);
94         desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
95
96         target_host_time = time_state->initial_time + desired_elapsed;
97         host_time = v3_get_host_time(time_state);
98         PrintDebug("Core %d: Yielding %Lu cycles for guest frequency mismatch "
99                    "(%Lu cycles elapsed in guest, %Lu in host).\n", 
100                    info->cpu_id, target_host_time - host_time,
101                    guest_elapsed, host_time - time_state->initial_time);
102
103         host_time = v3_get_host_time(time_state);
104         while (host_time < target_host_time) {
105             v3_yield(info);
106             host_time = v3_get_host_time(time_state);
107         }
108
109         PrintDebug("Core %d: done adjusting time at host time %Lu.\n", 
110                    info->cpu_id, host_time);
111         time_state->guest_host_offset = guest_time - host_time;
112
113     }
114     return 0;
115 }
116
117 int v3_add_timer(struct guest_info * info, struct vm_timer_ops * ops, 
118              void * private_data) {
119     struct vm_timer * timer = NULL;
120     timer = (struct vm_timer *)V3_Malloc(sizeof(struct vm_timer));
121     V3_ASSERT(timer != NULL);
122
123     timer->ops = ops;
124     timer->private_data = private_data;
125
126     list_add(&(timer->timer_link), &(info->time_state.timers));
127     info->time_state.num_timers++;
128
129     return 0;
130 }
131
132 int v3_remove_timer(struct guest_info * info, struct vm_timer * timer) {
133     list_del(&(timer->timer_link));
134     info->time_state.num_timers--;
135
136     V3_Free(timer);
137     return 0;
138 }
139
140 void v3_update_timers(struct guest_info * info) {
141     struct vm_timer * tmp_timer;
142     uint64_t old_time = info->time_state.last_update;
143     uint64_t cycles;
144
145     info->time_state.last_update = v3_get_guest_time(&info->time_state);
146     cycles = info->time_state.last_update - old_time;
147
148     list_for_each_entry(tmp_timer, &(info->time_state.timers), timer_link) {
149         tmp_timer->ops->update_timer(info, cycles, info->time_state.guest_cpu_freq, tmp_timer->private_data);
150     }
151 }
152
153 /* 
154  * Handle full virtualization of the time stamp counter.  As noted
155  * above, we don't store the actual value of the TSC, only the guest's
156  * offset from the host TSC. If the guest write's the to TSC, we handle
157  * this by changing that offset.
158  */ 
159
160 int v3_rdtsc(struct guest_info * info) {
161     uint64_t tscval = v3_get_guest_tsc(&info->time_state);
162     info->vm_regs.rdx = tscval >> 32;
163     info->vm_regs.rax = tscval & 0xffffffffLL;
164     return 0;
165 }
166
167 int v3_handle_rdtsc(struct guest_info * info) {
168     v3_rdtsc(info);
169     
170     info->vm_regs.rax &= 0x00000000ffffffffLL;
171     info->vm_regs.rdx &= 0x00000000ffffffffLL;
172
173     info->rip += 2;
174     
175     return 0;
176 }
177
178 int v3_rdtscp(struct guest_info * info) {
179     int ret;
180     /* First get the MSR value that we need. It's safe to futz with
181      * ra/c/dx here since they're modified by this instruction anyway. */
182     info->vm_regs.rcx = TSC_AUX_MSR; 
183     ret = v3_handle_msr_read(info);
184     if (ret) return ret;
185     info->vm_regs.rcx = info->vm_regs.rax;
186
187     /* Now do the TSC half of the instruction, which may hit the normal 
188      * TSC hook if it exists */
189     ret = v3_rdtsc(info);
190     if (ret) return ret;
191     
192     return 0;
193 }
194
195
196 int v3_handle_rdtscp(struct guest_info * info) {
197
198     v3_rdtscp(info);
199     
200     info->vm_regs.rax &= 0x00000000ffffffffLL;
201     info->vm_regs.rcx &= 0x00000000ffffffffLL;
202     info->vm_regs.rdx &= 0x00000000ffffffffLL;
203
204     info->rip += 3;
205     
206     return 0;
207 }
208
209 #if 0
210 static int tsc_aux_msr_read_hook(struct guest_info *info, uint_t msr_num, 
211                                  struct v3_msr *msr_val, void *priv) {
212     struct vm_time * time_state = &(info->time_state);
213
214     V3_ASSERT(msr_num == TSC_AUX_MSR);
215     msr_val->lo = time_state->tsc_aux.lo;
216     msr_val->hi = time_state->tsc_aux.hi;
217
218     return 0;
219 }
220
221 static int tsc_aux_msr_write_hook(struct guest_info *info, uint_t msr_num, 
222                               struct v3_msr msr_val, void *priv) {
223     struct vm_time * time_state = &(info->time_state);
224
225     V3_ASSERT(msr_num == TSC_AUX_MSR);
226     time_state->tsc_aux.lo = msr_val.lo;
227     time_state->tsc_aux.hi = msr_val.hi;
228
229     return 0;
230 }
231
232 static int tsc_msr_read_hook(struct guest_info *info, uint_t msr_num,
233                              struct v3_msr *msr_val, void *priv) {
234     uint64_t time = v3_get_guest_tsc(&info->time_state);
235
236     V3_ASSERT(msr_num == TSC_MSR);
237     msr_val->hi = time >> 32;
238     msr_val->lo = time & 0xffffffffLL;
239     
240     return 0;
241 }
242
243 static int tsc_msr_write_hook(struct guest_info *info, uint_t msr_num,
244                              struct v3_msr msr_val, void *priv) {
245     struct vm_time * time_state = &(info->time_state);
246     uint64_t guest_time, new_tsc;
247     V3_ASSERT(msr_num == TSC_MSR);
248     new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
249     guest_time = v3_get_guest_time(time_state);
250     time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time; 
251
252     return 0;
253 }
254
255 #endif
256
257 static int init_vm_time(struct v3_vm_info *vm_info) {
258     int ret;
259
260 #if 0
261     PrintDebug("Installing TSC MSR hook.\n");
262     ret = v3_hook_msr(vm_info, TSC_MSR, 
263                       tsc_msr_read_hook, tsc_msr_write_hook, NULL);
264
265     PrintDebug("Installing TSC_AUX MSR hook.\n");
266     if (ret) return ret;
267     ret = v3_hook_msr(vm_info, TSC_AUX_MSR, tsc_aux_msr_read_hook, 
268                       tsc_aux_msr_write_hook, NULL);
269     if (ret) return ret;
270 #endif
271
272     PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
273     ret = v3_register_hypercall(vm_info, TIME_CPUFREQ_HCALL, 
274                                 handle_cpufreq_hcall, NULL);
275     return ret;
276 }
277
278 void v3_init_time(struct guest_info * info) {
279     struct vm_time * time_state = &(info->time_state);
280     v3_cfg_tree_t * cfg_tree = info->core_cfg_data;
281     static int one_time = 0;
282     char *khz;
283
284     time_state->host_cpu_freq = V3_CPU_KHZ();
285     khz = v3_cfg_val(cfg_tree, "khz");
286     if (khz) {
287         time_state->guest_cpu_freq = atoi(khz);
288         PrintDebug("Core %d CPU frequency requested at %d khz.\n", 
289                    info->cpu_id, time_state->guest_cpu_freq);
290     }
291     
292     if (!khz || time_state->guest_cpu_freq > time_state->host_cpu_freq) {
293         time_state->guest_cpu_freq = time_state->host_cpu_freq;
294     }
295     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);
296
297     time_state->initial_time = 0;
298     time_state->last_update = 0;
299     time_state->guest_host_offset = 0;
300     time_state->tsc_guest_offset = 0;
301
302     INIT_LIST_HEAD(&(time_state->timers));
303     time_state->num_timers = 0;
304     
305     time_state->tsc_aux.lo = 0;
306     time_state->tsc_aux.hi = 0;
307
308     if (!one_time) {
309         init_vm_time(info->vm_info);
310         one_time = 1;
311     }
312 }
313
314
315
316
317
318