Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/x86/kvm_clock.c
- This file was added.
/*- | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/param.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/limits.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/pcpu.h> | |||||
#include <sys/smp.h> | |||||
#include <sys/timetc.h> | |||||
#include <vm/vm.h> | |||||
#include <vm/pmap.h> | |||||
#include <machine/pvclock.h> | |||||
#include <x86/kvm.h> | |||||
#include <x86/x86_var.h> | |||||
static u_int kvm_clock_get_timecounter(struct timecounter *); | |||||
static void kvm_clock_pcpu_system_time(void *); | |||||
DPCPU_DEFINE(struct pvclock_vcpu_time_info, kvm_clock_vcpu_time_info); | |||||
static struct timecounter kvm_clock_timecounter = { | |||||
.tc_get_timecount = kvm_clock_get_timecounter, | |||||
.tc_poll_pps = NULL, | |||||
.tc_counter_mask = ~0u, | |||||
.tc_frequency = 1000000000ULL, | |||||
.tc_name = "KVMCLOCK", | |||||
.tc_quality = 2000, | |||||
}; | |||||
markj: It would be nice to use designated initializers here, they are used now for other timecounter… | |||||
static uint32_t kvm_clock_wall_clock_msr; | |||||
static uint32_t kvm_clock_system_time_msr; | |||||
static inline int | |||||
kvm_paravirt_supported(void) | |||||
{ | |||||
return (hv_base > 0); | |||||
} | |||||
static u_int | |||||
kvm_get_features(void) | |||||
{ | |||||
u_int regs[4]; | |||||
if (kvm_paravirt_supported()) { | |||||
do_cpuid(hv_base | KVM_CPUID_FEATURES_LEAF, regs); | |||||
return (regs[0]); | |||||
} | |||||
return (0); | |||||
} | |||||
static u_int | |||||
kvm_clock_get_timecounter(struct timecounter *tc) | |||||
{ | |||||
struct pvclock_vcpu_time_info *ti; | |||||
uint64_t time; | |||||
critical_enter(); | |||||
ti = DPCPU_PTR(kvm_clock_vcpu_time_info); | |||||
time = pvclock_get_timecount(ti); | |||||
critical_exit(); | |||||
return (time & UINT_MAX); | |||||
} | |||||
static void | |||||
kvm_clock_pcpu_system_time(void *arg) | |||||
{ | |||||
uint64_t data; | |||||
/* | |||||
* The magical 1 is the enable bit to tell kvm to turn on this feature. | |||||
* It is internally cleared by kvm after being checked to get the proper | |||||
* address. | |||||
*/ | |||||
data = vtophys(DPCPU_PTR(kvm_clock_vcpu_time_info)) | 1; | |||||
Done Inline ActionsThis is too magic, at least a comment explaining it should be written. kib: This is too magic, at least a comment explaining it should be written. | |||||
wrmsr(kvm_clock_system_time_msr, data); | |||||
} | |||||
static void | |||||
kvm_clock_init(void) | |||||
{ | |||||
u_int features; | |||||
if (vm_guest != VM_GUEST_KVM || !kvm_paravirt_supported()) | |||||
return; | |||||
features = kvm_get_features(); | |||||
if (features & KVM_FEATURE_CLOCKSOURCE2) { | |||||
kvm_clock_wall_clock_msr = KVM_MSR_WALL_CLOCK_NEW; | |||||
kvm_clock_system_time_msr = KVM_MSR_SYSTEM_TIME_NEW; | |||||
} else if (hv_high & KVM_FEATURE_CLOCKSOURCE) { | |||||
kvm_clock_wall_clock_msr = KVM_MSR_WALL_CLOCK; | |||||
kvm_clock_system_time_msr = KVM_MSR_SYSTEM_TIME; | |||||
} else | |||||
return; | |||||
smp_rendezvous(smp_no_rendezvous_barrier, kvm_clock_pcpu_system_time, | |||||
smp_no_rendezvous_barrier, NULL); | |||||
tc_init(&kvm_clock_timecounter); | |||||
Done Inline ActionsWhat is the role of/needs for this variable? kib: What is the role of/needs for this variable? | |||||
Done Inline ActionsIt was useless indeed, just pass NULL to smp_rendezvous instead. me_freebsd_mathieu.digital: It was useless indeed, just pass NULL to smp_rendezvous instead. | |||||
} | |||||
Done Inline ActionsI believe the indent is wrong. kib: I believe the indent is wrong. | |||||
SYSINIT(kvm_clock, SI_SUB_SMP, SI_ORDER_ANY, kvm_clock_init, NULL); |
It would be nice to use designated initializers here, they are used now for other timecounter definitions.