blob: fea819732af5fc2df15426793bb00942a20c12c5 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 - os kernal
* Author: fire3 <fire3@example.com> yangzh <yangzh@gmail.com>
*/
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <asm/kvm_timer.h>
/*
* The Guest Clock.
*
* There are two sources of virtual interrupts. We saw one in lguest_user.c:
* the Launcher sending interrupts for virtual devices. The other is the Guest
* timer interrupt.
*
* The Guest uses the LHCALL_SET_CLOCKEVENT hypercall to tell us how long to
* the next timer interrupt (in nanoseconds). We use the high-resolution timer
* infrastructure to set a callback at that time.
*
* 0 means "turn off the clock".
*/
void set_timer(struct kvm_vcpu *vcpu, unsigned long delta)
{
ktime_t expires;
if (unlikely(delta == 0)) {
/* Clock event device is shutting down. */
hrtimer_cancel(&vcpu->arch.hrt);
return;
}
/*
* We use wallclock time here, so the Guest might not be running for
* all the time between now and the timer interrupt it asked for. This
* is almost always the right thing to do.
*/
expires = ktime_add_ns(ktime_get_real(), delta);
vcpu->arch.timer_next_event = expires;
hrtimer_start(&vcpu->arch.hrt, expires, HRTIMER_MODE_ABS);
}
/* And this is the routine when we want to set an interrupt for the Guest. */
void set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
{
/*
* Next time the Guest runs, the core code will see if it can deliver
* this interrupt.
*/
set_bit(irq, (vcpu->arch.irqs_pending));
/*
* Make sure it sees it; it might be asleep (eg. halted), or running
* the Guest right now, in which case kick_process() will knock it out.
*/
kvm_vcpu_kick(vcpu);
}
enum hrtimer_restart clockdev_fn(struct hrtimer *timer)
{
struct kvm_vcpu *vcpu;
ktime_t now, delta;
vcpu = container_of(timer, struct kvm_vcpu, arch.hrt);
now = ktime_get_real();
if (now < vcpu->arch.timer_next_event) {
delta = vcpu->arch.timer_next_event - now;
hrtimer_forward_now(timer, delta);
return HRTIMER_RESTART;
}
set_interrupt(vcpu, SW64_KVM_IRQ_TIMER);
return HRTIMER_NORESTART;
}