Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dfd4d47e authored by Scott Wood's avatar Scott Wood Committed by Avi Kivity
Browse files

KVM: PPC: booke: Improve timer register emulation



Decrementers are now properly driven by TCR/TSR, and the guest
has full read/write access to these registers.

The decrementer keeps ticking (and setting the TSR bit) regardless of
whether the interrupts are enabled with TCR.

The decrementer stops at zero, rather than going negative.

Decrementers (and FITs, once implemented) are delivered as
level-triggered interrupts -- dequeued when the TSR bit is cleared, not
on delivery.

Signed-off-by: default avatarLiu Yu <yu.liu@freescale.com>
[scottwood@freescale.com: significant changes]
Signed-off-by: default avatarScott Wood <scottwood@freescale.com>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent b5904972
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -330,7 +330,7 @@ struct kvm_vcpu_arch {
	u32 tbl;
	u32 tbl;
	u32 tbu;
	u32 tbu;
	u32 tcr;
	u32 tcr;
	u32 tsr;
	ulong tsr; /* we need to perform set/clr_bits() which requires ulong */
	u32 ivor[64];
	u32 ivor[64];
	ulong ivpr;
	ulong ivpr;
	u32 pvr;
	u32 pvr;
+1 −0
Original line number Original line Diff line number Diff line
@@ -66,6 +66,7 @@ extern int kvmppc_emulate_instruction(struct kvm_run *run,
extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu);
extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu);
extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu);
extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu);
extern u32 kvmppc_get_dec(struct kvm_vcpu *vcpu, u64 tb);
extern u32 kvmppc_get_dec(struct kvm_vcpu *vcpu, u64 tb);
extern void kvmppc_decrementer_func(unsigned long data);
extern int kvmppc_sanity_check(struct kvm_vcpu *vcpu);
extern int kvmppc_sanity_check(struct kvm_vcpu *vcpu);


/* Core-specific hooks */
/* Core-specific hooks */
+8 −0
Original line number Original line Diff line number Diff line
@@ -515,3 +515,11 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
	mutex_unlock(&kvm->slots_lock);
	mutex_unlock(&kvm->slots_lock);
	return r;
	return r;
}
}

void kvmppc_decrementer_func(unsigned long data)
{
	struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data;

	kvmppc_core_queue_dec(vcpu);
	kvm_vcpu_kick(vcpu);
}
+51 −16
Original line number Original line Diff line number Diff line
@@ -252,9 +252,11 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
		allowed = vcpu->arch.shared->msr & MSR_ME;
		allowed = vcpu->arch.shared->msr & MSR_ME;
		msr_mask = 0;
		msr_mask = 0;
		break;
		break;
	case BOOKE_IRQPRIO_EXTERNAL:
	case BOOKE_IRQPRIO_DECREMENTER:
	case BOOKE_IRQPRIO_DECREMENTER:
	case BOOKE_IRQPRIO_FIT:
	case BOOKE_IRQPRIO_FIT:
		keep_irq = true;
		/* fall through */
	case BOOKE_IRQPRIO_EXTERNAL:
		allowed = vcpu->arch.shared->msr & MSR_EE;
		allowed = vcpu->arch.shared->msr & MSR_EE;
		allowed = allowed && !crit;
		allowed = allowed && !crit;
		msr_mask = MSR_CE|MSR_ME|MSR_DE;
		msr_mask = MSR_CE|MSR_ME|MSR_DE;
@@ -282,11 +284,26 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
	return allowed;
	return allowed;
}
}


static void update_timer_ints(struct kvm_vcpu *vcpu)
{
	if ((vcpu->arch.tcr & TCR_DIE) && (vcpu->arch.tsr & TSR_DIS))
		kvmppc_core_queue_dec(vcpu);
	else
		kvmppc_core_dequeue_dec(vcpu);
}

static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu)
static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu)
{
{
	unsigned long *pending = &vcpu->arch.pending_exceptions;
	unsigned long *pending = &vcpu->arch.pending_exceptions;
	unsigned int priority;
	unsigned int priority;


	if (vcpu->requests) {
		if (kvm_check_request(KVM_REQ_PENDING_TIMER, vcpu)) {
			smp_mb();
			update_timer_ints(vcpu);
		}
	}

	priority = __ffs(*pending);
	priority = __ffs(*pending);
	while (priority <= BOOKE_IRQPRIO_MAX) {
	while (priority <= BOOKE_IRQPRIO_MAX) {
		if (kvmppc_booke_irqprio_deliver(vcpu, priority))
		if (kvmppc_booke_irqprio_deliver(vcpu, priority))
@@ -749,25 +766,16 @@ static int set_sregs_base(struct kvm_vcpu *vcpu,
	vcpu->arch.shared->esr = sregs->u.e.esr;
	vcpu->arch.shared->esr = sregs->u.e.esr;
	vcpu->arch.shared->dar = sregs->u.e.dear;
	vcpu->arch.shared->dar = sregs->u.e.dear;
	vcpu->arch.vrsave = sregs->u.e.vrsave;
	vcpu->arch.vrsave = sregs->u.e.vrsave;
	vcpu->arch.tcr = sregs->u.e.tcr;
	kvmppc_set_tcr(vcpu, sregs->u.e.tcr);


	if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_DEC)
	if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_DEC) {
		vcpu->arch.dec = sregs->u.e.dec;
		vcpu->arch.dec = sregs->u.e.dec;

		kvmppc_emulate_dec(vcpu);
		kvmppc_emulate_dec(vcpu);
	}


	if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_TSR) {
	if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_TSR) {
		/*
		vcpu->arch.tsr = sregs->u.e.tsr;
		 * FIXME: existing KVM timer handling is incomplete.
		update_timer_ints(vcpu);
		 * TSR cannot be read by the guest, and its value in
		 * vcpu->arch is always zero.  For now, just handle
		 * the case where the caller is trying to inject a
		 * decrementer interrupt.
		 */

		if ((sregs->u.e.tsr & TSR_DIS) &&
		    (vcpu->arch.tcr & TCR_DIE))
			kvmppc_core_queue_dec(vcpu);
	}
	}


	return 0;
	return 0;
@@ -923,6 +931,33 @@ void kvmppc_core_destroy_vm(struct kvm *kvm)
{
{
}
}


void kvmppc_set_tcr(struct kvm_vcpu *vcpu, u32 new_tcr)
{
	vcpu->arch.tcr = new_tcr;
	update_timer_ints(vcpu);
}

void kvmppc_set_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits)
{
	set_bits(tsr_bits, &vcpu->arch.tsr);
	smp_wmb();
	kvm_make_request(KVM_REQ_PENDING_TIMER, vcpu);
	kvm_vcpu_kick(vcpu);
}

void kvmppc_clr_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits)
{
	clear_bits(tsr_bits, &vcpu->arch.tsr);
	update_timer_ints(vcpu);
}

void kvmppc_decrementer_func(unsigned long data)
{
	struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data;

	kvmppc_set_tsr_bits(vcpu, TSR_DIS);
}

int __init kvmppc_booke_init(void)
int __init kvmppc_booke_init(void)
{
{
	unsigned long ivor[16];
	unsigned long ivor[16];
+4 −0
Original line number Original line Diff line number Diff line
@@ -55,6 +55,10 @@ extern unsigned long kvmppc_booke_handlers;
void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr);
void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr);
void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr);
void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr);


void kvmppc_set_tcr(struct kvm_vcpu *vcpu, u32 new_tcr);
void kvmppc_set_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits);
void kvmppc_clr_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits);

int kvmppc_booke_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
int kvmppc_booke_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                            unsigned int inst, int *advance);
                            unsigned int inst, int *advance);
int kvmppc_booke_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt);
int kvmppc_booke_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt);
Loading