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.


Fix for option processing
[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         v3_yield(info);
110         host_time = v3_get_host_time(time_state);
111     }
112
113     guest_time = v3_get_guest_time(time_state);
114
115     // We do *not* assume the guest timer was paused in the VM. If it was
116     // this offseting is 0. If it wasn't we need this.
117    offset_time(info, (sint64_t)old_guest_time - (sint64_t)guest_time);
118
119     /* Now the host may have gotten ahead of the guest because
120      * yielding is a coarse grained thing. Figure out what guest time
121      * we want to be at, and use the use the offsetting mechanism in 
122      * the VMM to make the guest run forward. We limit *how* much we skew 
123      * it forward to prevent the guest time making large jumps, 
124      * however. */
125     host_elapsed = host_time - time_state->initial_time;
126     desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
127     target_guest_time = time_state->initial_time + desired_elapsed;
128
129     if (guest_time < target_guest_time) {
130         uint64_t max_skew, desired_skew, skew;
131
132         if (time_state->enter_time) {
133             max_skew = (time_state->exit_time - time_state->enter_time) / 10;
134         } else {
135             max_skew = 0;
136         }
137
138         desired_skew = target_guest_time - guest_time;
139         skew = desired_skew > max_skew ? max_skew : desired_skew;
140 /*      PrintDebug("Guest %llu cycles behind where it should be.\n",
141                    desired_skew);
142         PrintDebug("Limit on forward skew is %llu. Skewing forward %llu.\n",
143                    max_skew, skew); */
144         
145         offset_time(info, skew);
146     }
147     
148     return 0;
149 }
150
151
152 static int init() {
153     khz = v3_cfg_val(cfg_tree, "khz");
154
155     if (khz) {
156         time_state->guest_cpu_freq = atoi(khz);
157         PrintDebug("Core %d CPU frequency requested at %d khz.\n", 
158                    info->pcpu_id, time_state->guest_cpu_freq);
159     } 
160     
161     if ( (khz == NULL) || 
162          (time_state->guest_cpu_freq <= 0)  || 
163          (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
164
165         time_state->guest_cpu_freq = time_state->host_cpu_freq;
166     }
167
168
169 }