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