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