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.


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