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 fixes
[palacios.git] / palacios / src / extensions / ext_vtime.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 #include <palacios/vmm.h>
21 #include <palacios/vmm_time.h>
22 #include <palacios/vm_guest.h>
23
24
25
26
27 /* Overview 
28  *
29  * Time handling in VMMs is challenging, and Palacios uses the highest 
30  * resolution, lowest overhead timer on modern CPUs that it can - the 
31  * processor timestamp counter (TSC). Note that on somewhat old processors
32  * this can be problematic; in particular, older AMD processors did not 
33  * have a constant rate timestamp counter in the face of power management
34  * events. However, the latest Intel and AMD CPUs all do (should...) have a 
35  * constant rate TSC, and Palacios relies on this fact.
36  * 
37  * Basically, Palacios keeps track of three quantities as it runs to manage
38  * the passage of time:
39  * (1) The host timestamp counter - read directly from HW and never written
40  * (2) A monotonic guest timestamp counter used to measure the progression of
41  *     time in the guest. This is computed using an offsets from (1) above.
42  * (3) The actual guest timestamp counter (which can be written by
43  *     writing to the guest TSC MSR - MSR 0x10) from the monotonic guest TSC.
44  *     This is also computed as an offset from (2) above when the TSC and
45  *     this offset is updated when the TSC MSR is written.
46  *
47  * The value used to offset the guest TSC from the host TSC is the *sum* of all
48  * of these offsets (2 and 3) above
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  * Future additions:
55  * (1) Add support for temporarily skewing guest time off of where it should
56  *     be to support slack simulation of guests. The idea is that simulators
57  *     set this skew to be the difference between how much time passed for a 
58  *     simulated feature and a real implementation of that feature, making 
59  *     pass at a different rate from real time on this core. The VMM will then
60  *     attempt to move this skew back towards 0 subject to resolution/accuracy
61  *     constraints from various system timers.
62  *   
63  *     The main effort in doing this will be to get accuracy/resolution 
64  *     information from each local timer and to use this to bound how much skew
65  *     is removed on each exit.
66  */
67
68
69
70 struct vtime_state {
71     uint32_t guest_cpu_freq;   // can be lower than host CPU freq!
72     uint64_t initial_time;     // Time when VMM started. 
73     sint64_t guest_host_offset;// Offset of monotonic guest time from host time
74 };
75
76
77
78
79 static int offset_time( struct guest_info * info, sint64_t offset )
80 {
81     struct vm_time * time_state = &(info->time_state);
82 //    PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
83     time_state->guest_host_offset += offset;
84     return 0;
85 }
86
87
88 // Control guest time in relation to host time so that the two stay 
89 // appropriately synchronized to the extent possible. 
90 int v3_adjust_time(struct guest_info * info) {
91     struct vm_time * time_state = &(info->time_state);
92     uint64_t host_time, target_host_time;
93     uint64_t guest_time, target_guest_time, old_guest_time;
94     uint64_t guest_elapsed, host_elapsed, desired_elapsed;
95
96     /* Compute the target host time given how much time has *already*
97      * passed in the guest */
98     guest_time = v3_get_guest_time(time_state);
99     guest_elapsed = (guest_time - time_state->initial_time);
100     desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
101     target_host_time = time_state->initial_time + desired_elapsed;
102
103     /* Now, let the host run while the guest is stopped to make the two
104      * sync up. */
105     host_time = v3_get_host_time(time_state);
106     old_guest_time = v3_get_guest_time(time_state);
107
108     while (target_host_time > host_time) {
109         // this will immediately yield if there is noone to yield to.
110         v3_yield(info,-1);
111         host_time = v3_get_host_time(time_state);
112     }
113
114     guest_time = v3_get_guest_time(time_state);
115
116     // We do *not* assume the guest timer was paused in the VM. If it was
117     // this offseting is 0. If it wasn't we need this.
118    offset_time(info, (sint64_t)old_guest_time - (sint64_t)guest_time);
119
120     /* Now the host may have gotten ahead of the guest because
121      * yielding is a coarse grained thing. Figure out what guest time
122      * we want to be at, and use the use the offsetting mechanism in 
123      * the VMM to make the guest run forward. We limit *how* much we skew 
124      * it forward to prevent the guest time making large jumps, 
125      * however. */
126     host_elapsed = host_time - time_state->initial_time;
127     desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
128     target_guest_time = time_state->initial_time + desired_elapsed;
129
130     if (guest_time < target_guest_time) {
131         uint64_t max_skew, desired_skew, skew;
132
133         if (time_state->enter_time) {
134             max_skew = (time_state->exit_time - time_state->enter_time) / 10;
135         } else {
136             max_skew = 0;
137         }
138
139         desired_skew = target_guest_time - guest_time;
140         skew = desired_skew > max_skew ? max_skew : desired_skew;
141 /*      PrintDebug("Guest %llu cycles behind where it should be.\n",
142                    desired_skew);
143         PrintDebug("Limit on forward skew is %llu. Skewing forward %llu.\n",
144                    max_skew, skew); */
145         
146         offset_time(info, skew);
147     }
148     
149     return 0;
150 }
151
152
153 static int init() {
154     khz = v3_cfg_val(cfg_tree, "khz");
155
156     if (khz) {
157         time_state->guest_cpu_freq = atoi(khz);
158         PrintDebug("Core %d CPU frequency requested at %d khz.\n", 
159                    info->pcpu_id, time_state->guest_cpu_freq);
160     } 
161     
162     if ( (khz == NULL) || 
163          (time_state->guest_cpu_freq <= 0)  || 
164          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
165
166         time_state->guest_cpu_freq = time_state->host_cpu_freq;
167     }
168
169
170 }