struct guest_info;
struct vm_time {
- uint32_t cpu_freq; // in kHZ in terms of guest CPU speed
- // which ideally can be different lower than
- // host CPU speed!
+ uint32_t host_cpu_freq; // in kHZ
+ uint32_t guest_cpu_freq; // can be lower than host CPU freq!
- uint32_t time_mult; // Fields for computing monotonic guest time
- uint32_t time_div; // from host (tsc) time
- sint64_t time_offset;
-
- sint64_t tsc_time_offset; // Offset for computing guest TSC value from
- // monotonic guest time
+ 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 pause_time; // Cache value to help calculate the guest_tsc
+ uint64_t initial_time; // Time when VMM started.
struct v3_msr tsc_aux; // Auxilliary MSR for RDTSCP
void v3_init_time(struct guest_info * info);
int v3_start_time(struct guest_info * info);
-int v3_pause_time(struct guest_info * info);
-int v3_resume_time(struct guest_info * info);
+int v3_adjust_time(struct guest_info * info);
// Returns host time
static inline uint64_t v3_get_host_time(struct vm_time *t) {
// Returns *monotonic* guest time.
static inline uint64_t v3_get_guest_time(struct vm_time *t) {
- if (t->pause_time) return t->pause_time;
- else return v3_get_host_time(t) + t->time_offset;
+ return v3_get_host_time(t) + t->guest_host_offset;
}
// Returns the TSC value seen by the guest
static inline uint64_t v3_get_guest_tsc(struct vm_time *t) {
- return v3_get_guest_time(t) + t->tsc_time_offset;
+ return v3_get_guest_time(t) + t->tsc_guest_offset;
+}
+
+// Returns offset of guest TSC from host TSC
+static inline sint64_t v3_tsc_host_offset(struct vm_time *time_state) {
+ return time_state->guest_host_offset + time_state->tsc_guest_offset;
}
+
#define TSC_MSR 0x10
#define TSC_AUX_MSR 0xC0000103
#include <palacios/vmcb.h>
static int info_hcall(struct guest_info * core, uint_t hcall_id, void * priv_data) {
v3_cpu_arch_t cpu_type = v3_get_cpu_type(v3_get_cpu_id());
-
+ int cpu_valid = 0;
+
v3_print_guest_state(core);
-
// init SVM/VMX
#ifdef CONFIG_SVM
if ((cpu_type == V3_SVM_CPU) || (cpu_type == V3_SVM_REV3_CPU)) {
+ cpu_valid = 1;
PrintDebugVMCB((vmcb_t *)(core->vmm_data));
}
#endif
#ifdef CONFIG_VMX
if ((cpu_type == V3_VMX_CPU) || (cpu_type == V3_VMX_EPT_CPU)) {
+ cpu_valid = 1;
v3_print_vmcs();
}
#endif
- else {
- PrintError("Invalid CPU Type\n");
+ if (!cpu_valid) {
+ PrintError("Invalid CPU Type 0x%x\n", cpu_type);
return -1;
}
int v3_init_vm(struct v3_vm_info * vm) {
v3_cpu_arch_t cpu_type = v3_get_cpu_type(v3_get_cpu_id());
-
+ int cpu_valid = 0;
if (v3_get_foreground_vm() == NULL) {
v3_set_foreground_vm(vm);
if ((cpu_type == V3_SVM_CPU) || (cpu_type == V3_SVM_REV3_CPU)) {
v3_init_svm_io_map(vm);
v3_init_svm_msr_map(vm);
- }
+ cpu_valid = 1;
+ }
#endif
#ifdef CONFIG_VMX
if ((cpu_type == V3_VMX_CPU) || (cpu_type == V3_VMX_EPT_CPU)) {
v3_init_vmx_io_map(vm);
v3_init_vmx_msr_map(vm);
+ cpu_valid = 1;
}
#endif
- else {
- PrintError("Invalid CPU Type\n");
+ if (!cpu_valid) {
+ PrintError("Invalid CPU Type 0x%x\n", cpu_type);
return -1;
}
-
-
v3_register_hypercall(vm, GUEST_INFO_HCALL, info_hcall, NULL);
-
V3_Print("GUEST_INFO_HCALL=%x\n", GUEST_INFO_HCALL);
return 0;
int v3_init_core(struct guest_info * core) {
v3_cpu_arch_t cpu_type = v3_get_cpu_type(v3_get_cpu_id());
+ int cpu_valid = 0;
struct v3_vm_info * vm = core->vm_info;
/*
PrintError("Error in SVM initialization\n");
return -1;
}
+ cpu_valid = 1;
}
#endif
#ifdef CONFIG_VMX
PrintError("Error in VMX initialization\n");
return -1;
}
+ cpu_valid = 1;
}
#endif
- else {
- PrintError("Invalid CPU Type\n");
+ if (!cpu_valid) {
+ PrintError("Invalid CPU Type 0x%x\n", cpu_type);
return -1;
}
static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
struct vm_time * time_state = &(info->time_state);
- info->vm_regs.rbx = time_state->cpu_freq;
+ info->vm_regs.rbx = time_state->guest_cpu_freq;
PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx);
PrintDebug("Starting initial guest time as %llu\n", t);
info->time_state.last_update = t;
- info->time_state.pause_time = t;
+ info->time_state.initial_time = t;
info->yield_start_cycle = t;
return 0;
}
-int v3_pause_time(struct guest_info * info) {
- V3_ASSERT(info->time_state.pause_time == 0);
- info->time_state.pause_time = v3_get_guest_time(&info->time_state);
- PrintDebug("Time paused at guest time as %llu\n",
- info->time_state.pause_time);
- return 0;
-}
-
-int v3_resume_time(struct guest_info * info) {
- uint64_t t = v3_get_host_time(&info->time_state);
- V3_ASSERT(info->time_state.pause_time != 0);
- info->time_state.time_offset =
- (sint64_t)info->time_state.pause_time - (sint64_t)t;
- info->time_state.pause_time = 0;
- PrintDebug("Time resumed paused at guest time as %llu "
- "offset %lld from host time.\n", t, info->time_state.time_offset);
+// If the guest is supposed to run slower than the host, yield out until
+// the host time is appropriately far along;
+int v3_adjust_time(struct guest_info * info)
+{
+ struct vm_time * time_state = &(info->time_state);
+ uint64_t guest_time, host_time, target_host_time;
+ guest_time = v3_get_guest_time(time_state);
+ host_time = v3_get_host_time(time_state);
+ target_host_time = (host_time - time_state->initial_time) *
+ time_state->host_cpu_freq / time_state->guest_cpu_freq;
+ while (host_time < target_host_time) {
+ v3_yield(info);
+ host_time = v3_get_host_time(time_state);
+ }
+ time_state->guest_host_offset = guest_time - host_time;
return 0;
}
cycles = info->time_state.last_update - old_time;
list_for_each_entry(tmp_timer, &(info->time_state.timers), timer_link) {
- tmp_timer->ops->update_timer(info, cycles, info->time_state.cpu_freq, tmp_timer->private_data);
+ tmp_timer->ops->update_timer(info, cycles, info->time_state.guest_cpu_freq, tmp_timer->private_data);
}
}
V3_ASSERT(msr_num == TSC_MSR);
new_tsc = (((uint64_t)msr_val.hi) << 32) | (uint64_t)msr_val.lo;
guest_time = v3_get_guest_time(time_state);
- time_state->tsc_time_offset = (sint64_t)new_tsc - (sint64_t)guest_time;
+ time_state->tsc_guest_offset = (sint64_t)new_tsc - (sint64_t)guest_time;
return 0;
}
+static int init_vm_time(struct v3_vm_info *vm_info) {
+ int ret;
+
+ PrintDebug("Installing TSC MSR hook.\n");
+ ret = v3_hook_msr(vm_info, TSC_MSR,
+ tsc_msr_read_hook, tsc_msr_write_hook, NULL);
+
+ PrintDebug("Installing TSC_AUX MSR hook.\n");
+ if (ret) return ret;
+ ret = v3_hook_msr(vm_info, TSC_AUX_MSR, tsc_aux_msr_read_hook,
+ tsc_aux_msr_write_hook, NULL);
+ if (ret) return ret;
+
+ PrintDebug("Registering TIME_CPUFREQ hypercall.\n");
+ ret = v3_register_hypercall(vm_info, TIME_CPUFREQ_HCALL,
+ handle_cpufreq_hcall, NULL);
+ return ret;
+}
void v3_init_time(struct guest_info * info) {
struct vm_time * time_state = &(info->time_state);
+ static int one_time = 0;
- time_state->cpu_freq = V3_CPU_KHZ();
+ time_state->host_cpu_freq = V3_CPU_KHZ();
+ time_state->guest_cpu_freq = V3_CPU_KHZ();
- time_state->pause_time = 0;
+ time_state->initial_time = 0;
time_state->last_update = 0;
- time_state->time_offset = 0;
- time_state->time_div = 1;
- time_state->time_mult = 1;
- time_state->tsc_time_offset = 0;
+ time_state->guest_host_offset = 0;
+ time_state->tsc_guest_offset = 0;
INIT_LIST_HEAD(&(time_state->timers));
time_state->num_timers = 0;
time_state->tsc_aux.lo = 0;
time_state->tsc_aux.hi = 0;
- /* does init_time get called once, or once *per core*??? */
- v3_hook_msr(info->vm_info, TSC_MSR,
- tsc_msr_read_hook, tsc_msr_write_hook, NULL);
- v3_hook_msr(info->vm_info, TSC_AUX_MSR, tsc_aux_msr_read_hook,
- tsc_aux_msr_write_hook, NULL);
-
- v3_register_hypercall(info->vm_info, TIME_CPUFREQ_HCALL, handle_cpufreq_hcall, NULL);
+ if (!one_time) {
+ init_vm_time(info->vm_info);
+ one_time = 1;
+ }
}
+
+
+
+
+
+
}
v3_update_timers(info);
- v3_resume_time(info);
- {
- sint64_t total_tsc_offset = info->time_state.time_offset + info->time_state.tsc_time_offset;
-
- tsc_offset_high = (uint32_t)((total_tsc_offset >> 32) & 0xffffffff);
- tsc_offset_low = (uint32_t)(total_tsc_offset & 0xffffffff);
- check_vmcs_write(VMCS_TSC_OFFSET_HIGH, tsc_offset_high);
- check_vmcs_write(VMCS_TSC_OFFSET, tsc_offset_low);
- }
+ tsc_offset_high = (uint32_t)((v3_tsc_host_offset(&info->time_state) >> 32) & 0xffffffff);
+ tsc_offset_low = (uint32_t)(v3_tsc_host_offset(&info->time_state) & 0xffffffff);
+ check_vmcs_write(VMCS_TSC_OFFSET_HIGH, tsc_offset_high);
+ check_vmcs_write(VMCS_TSC_OFFSET, tsc_offset_low);
- PrintDebug("Stored 0x %x %x into vmcs TSC offset.\n",
+ PrintDebug("Stored 0x%x_%x into vmcs TSC offset.\n",
tsc_offset_high, tsc_offset_low);
if (info->vm_info->run_state == VM_STOPPED) {
info->vm_info->run_state = VM_RUNNING;
return -1;
}
- v3_pause_time(info);
-#ifdef CONFIG_TIME_MASK_OVERHEAD
- v3_offset_time(info, -VMX_ENTRY_OVERHEAD);
-#endif
+ /* If this guest is frequency-lagged behind host time, wait
+ * for the appropriate host time. */
+ v3_adjust_time(info);
info->num_exits++;