VM and attempt to hide hte costs of entering the VM.
menu "Time Management"
+config TIME_HIDE_VM_COST
+ bool "Hide VMM Run Cost"
+ default n
+ help
+ Offset guest time from host time sufficiently to hide the cost of
+ running in the virtual machine. This can aid the consistency of
+ time between multiple timers, but can cause the guest to run
+ slightly slower than the host.
+
+config TIME_HIDE_EXIT_COST
+ bool "Hide VMM Exit Cost"
+ default n
+ depends on TIME_HIDE_VM_COST
+ help
+ Add additional TSC offset to hide VMM exit costs from the guest.
+
+config TIME_EXIT_COST_ADJUST
+ int "Exit cost adjustment for hiding VM exits"
+ default 2000
+ depends on TIME_HIDE_EXIT_COST
+ help
+ Amount to adjust the time to hide the exit cost of fully virtualizing
+ the timestamp counter. Used to hide the cost of fully virtualizing
+ the TSC for OSes that assume that reading the TSC is inexpensive
+ (e.g. Linux TSC-based timer calibration).
+
config TIME_VIRTUALIZE_TSC
- bool "Virtualize guest TSC"
+ bool "Fully virtualize guest TSC"
default n
help
Virtualize the processor time stamp counter in the guest,
generally increasing consistency between various time sources
but also potentially making guest time run slower than real time.
-
+
endmenu
menu "Symbiotic Functions"
* and the University of New Mexico. You can find out more at
* http://www.v3vee.org
*
+ * Copyright (c) 2010, Patrick Bridges <bridges@cs.unm.edu>
* Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
* Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
* All rights reserved.
*
* Author: Jack Lange <jarusl@cs.northwestern.edu>
+ * Patrick Bridges <bridges@cs.unm.edu>
*
* This is free software. You are permitted to use,
* redistribute, and modify it as specified in the file "V3VEE_LICENSE".
struct vm_time {
uint32_t host_cpu_freq; // in kHZ
uint32_t guest_cpu_freq; // can be lower than host CPU freq!
-
+
sint64_t guest_host_offset;// Offset of monotonic guest time from host time
sint64_t tsc_guest_offset; // Offset of guest TSC from monotonic guest time
-
+
uint64_t last_update; // Last time (in monotonic guest time) the
// timers were updated
uint64_t initial_time; // Time when VMM started.
-
+ uint64_t pause_time; // Host time when VM entered and paused.
struct v3_msr tsc_aux; // Auxilliary MSR for RDTSCP
// Installed Timers slaved off of the guest monotonic TSC
void v3_deinit_time_core(struct guest_info * core);
void v3_deinit_time_vm(struct v3_vm_info * vm);
-
int v3_start_time(struct guest_info * core);
+
int v3_adjust_time(struct guest_info * core);
+int v3_pause_time(struct guest_info * core);
+int v3_offset_time(struct guest_info * core, sint64_t offset);
+int v3_restart_time(struct guest_info * core);
// Basic functions for attaching timers to the passage of time
struct v3_timer * v3_add_timer(struct guest_info * info, struct v3_timer_ops * ops, void * private_data);
// Returns *monotonic* guest time.
static inline uint64_t v3_get_guest_time(struct vm_time *t) {
- return v3_get_host_time(t) + t->guest_host_offset;
+ if (t->pause_time) {
+ return t->pause_time + t->guest_host_offset;
+ } else {
+ return v3_get_host_time(t) + t->guest_host_offset;
+ }
}
// Returns the TSC value seen by the guest
// update counter with remainder (mod reload)
state->pit_counter = state->pit_reload - cpu_cycles;
- //PrintDebug("8254 PIT: Handling %d crystal tics\n", oscillations);
+ if (oscillations) {
+ PrintDebug("8254 PIT: Handling %d crystal tics\n", oscillations);
+ }
+
if (handle_crystal_tics(state, &(state->ch_0), oscillations) == 1) {
// raise interrupt
- PrintDebug("8254 PIT: Injecting Timer interrupt to guest\n");
+ // PrintDebug("8254 PIT: Injecting Timer interrupt to guest\n");
v3_raise_irq(info->vm_info, 0);
}
config APIC
bool "APIC"
default y
- depends on EXPERIMENTAL
help
Includes the Virtual APIC device
config IO_APIC
bool "IOAPIC"
- depends on EXPERIMENTAL
default y
help
Includes the Virtual IO APIC
vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data));
addr_t exit_code = 0, exit_info1 = 0, exit_info2 = 0;
- v3_adjust_time(info);
-
// Conditionally yield the CPU if the timeslice has expired
v3_yield_cond(info);
+ v3_adjust_time(info);
+
// disable global interrupts for vm state transition
v3_clgi();
#endif
v3_update_timers(info);
+#ifdef CONFIG_TIME_HIDE_VM_COST
+ v3_restart_time(info);
+#endif
guest_ctrl->TSC_OFFSET = v3_tsc_host_offset(&info->time_state);
//V3_Print("Calling v3_svm_launch\n");
v3_last_exit = (uint32_t)(guest_ctrl->exit_code);
+#ifdef CONFIG_TIME_HIDE_VM_COST
+ v3_pause_time(info);
+#ifdef CONFIG_TIME_HIDE_EXIT_COST
+ v3_offset_time(info, -CONFIG_TIME_EXIT_COST_ADJUST);
+#endif
+#endif
+
//PrintDebug("SVM Returned\n");
info->num_exits++;
uint64_t t = v3_get_host_time(&info->time_state);
PrintDebug("Starting initial guest time as %llu\n", t);
+#ifdef CONFIG_TIME_HIDE_VM_COST
+ info->time_state.pause_time = t;
+#else
+ info->time_state.pause_time = 0;
+#endif
info->time_state.last_update = t;
info->time_state.initial_time = t;
info->yield_start_cycle = t;
int v3_adjust_time(struct guest_info * info) {
struct vm_time * time_state = &(info->time_state);
- if (time_state->host_cpu_freq == time_state->guest_cpu_freq) {
- time_state->guest_host_offset = 0;
- } else {
+ if (time_state->host_cpu_freq != time_state->guest_cpu_freq) {
uint64_t guest_time, guest_elapsed, desired_elapsed;
uint64_t host_time, target_host_time;
host_time = v3_get_host_time(time_state);
}
+ // This overrides any pause/unpause times because the difference
+ // is going to be too big for any pause/unpause the notice.
time_state->guest_host_offset = (sint64_t)guest_time - (sint64_t)host_time;
}
return 0;
}
+int
+v3_pause_time( struct guest_info * info )
+{
+ struct vm_time * time_state = &(info->time_state);
+ if (time_state->pause_time == 0) {
+ time_state->pause_time = v3_get_host_time(time_state);
+// PrintDebug("Pausing at host time %llu.\n", time_state->pause_time);
+ } else {
+ PrintError("Palacios timekeeping paused when already paused.\n");
+ }
+ return 0;
+}
+
+int
+v3_restart_time( struct guest_info * info )
+{
+ struct vm_time * time_state = &(info->time_state);
+
+ if (time_state->pause_time) {
+ sint64_t pause_diff = (v3_get_host_time(time_state) - time_state->pause_time);
+ time_state->guest_host_offset -= pause_diff;
+ time_state->pause_time = 0;
+// PrintDebug("Resuming time after %lld cycles with offset %lld.\n", pause_diff, time_state->guest_host_offset);
+ } else {
+ PrintError( "Palacios time keeping restarted when not paused.");
+ }
+
+ return 0;
+}
+
+int v3_offset_time( struct guest_info * info, sint64_t offset )
+{
+ struct vm_time * time_state = &(info->time_state);
+// PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
+ time_state->guest_host_offset += offset;
+ return 0;
+}
+
struct v3_timer * v3_add_timer(struct guest_info * info,
struct v3_timer_ops * ops,
void * private_data) {
}
void v3_update_timers(struct guest_info * info) {
+ struct vm_time *time_state = &info->time_state;
struct v3_timer * tmp_timer;
uint64_t old_time = info->time_state.last_update;
- uint64_t cycles;
+ sint64_t cycles;
+
+ time_state->last_update = v3_get_guest_time(time_state);
+ cycles = time_state->last_update - old_time;
- info->time_state.last_update = v3_get_guest_time(&info->time_state);
- cycles = info->time_state.last_update - old_time;
+ // PrintDebug("Updating timer for %lld elapsed cycles (pt=%llu, offset=%lld).\n",
+ // cycles, time_state->pause_time, time_state->guest_host_offset);
- list_for_each_entry(tmp_timer, &(info->time_state.timers), timer_link) {
- tmp_timer->ops->update_timer(info, cycles, info->time_state.guest_cpu_freq, tmp_timer->private_data);
+ list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
+ tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
}
}
int v3_rdtsc(struct guest_info * info) {
uint64_t tscval = v3_get_guest_tsc(&info->time_state);
+ PrintDebug("Returning %llu as TSC.\n", tscval);
info->vm_regs.rdx = tscval >> 32;
info->vm_regs.rax = tscval & 0xffffffffLL;
return 0;
}
int v3_handle_rdtsc(struct guest_info * info) {
+ PrintDebug("Handling virtual RDTSC call.\n");
v3_rdtsc(info);
info->vm_regs.rax &= 0x00000000ffffffffLL;
int v3_handle_rdtscp(struct guest_info * info) {
+ PrintDebug("Handling virtual RDTSCP call.\n");
v3_rdtscp(info);
-
+
info->vm_regs.rax &= 0x00000000ffffffffLL;
info->vm_regs.rcx &= 0x00000000ffffffffLL;
info->vm_regs.rdx &= 0x00000000ffffffffLL;