diff options
Diffstat (limited to 'arch/powerpc/kvm/booke.c')
| -rw-r--r-- | arch/powerpc/kvm/booke.c | 154 | 
1 files changed, 152 insertions, 2 deletions
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index ef76acb455c..8462b3a1c1c 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -569,6 +569,7 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)  	kvmppc_set_msr(vcpu, regs->msr);  	vcpu->arch.shared->srr0 = regs->srr0;  	vcpu->arch.shared->srr1 = regs->srr1; +	kvmppc_set_pid(vcpu, regs->pid);  	vcpu->arch.shared->sprg0 = regs->sprg0;  	vcpu->arch.shared->sprg1 = regs->sprg1;  	vcpu->arch.shared->sprg2 = regs->sprg2; @@ -584,16 +585,165 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)  	return 0;  } +static void get_sregs_base(struct kvm_vcpu *vcpu, +                           struct kvm_sregs *sregs) +{ +	u64 tb = get_tb(); + +	sregs->u.e.features |= KVM_SREGS_E_BASE; + +	sregs->u.e.csrr0 = vcpu->arch.csrr0; +	sregs->u.e.csrr1 = vcpu->arch.csrr1; +	sregs->u.e.mcsr = vcpu->arch.mcsr; +	sregs->u.e.esr = vcpu->arch.esr; +	sregs->u.e.dear = vcpu->arch.shared->dar; +	sregs->u.e.tsr = vcpu->arch.tsr; +	sregs->u.e.tcr = vcpu->arch.tcr; +	sregs->u.e.dec = kvmppc_get_dec(vcpu, tb); +	sregs->u.e.tb = tb; +	sregs->u.e.vrsave = vcpu->arch.vrsave; +} + +static int set_sregs_base(struct kvm_vcpu *vcpu, +                          struct kvm_sregs *sregs) +{ +	if (!(sregs->u.e.features & KVM_SREGS_E_BASE)) +		return 0; + +	vcpu->arch.csrr0 = sregs->u.e.csrr0; +	vcpu->arch.csrr1 = sregs->u.e.csrr1; +	vcpu->arch.mcsr = sregs->u.e.mcsr; +	vcpu->arch.esr = sregs->u.e.esr; +	vcpu->arch.shared->dar = sregs->u.e.dear; +	vcpu->arch.vrsave = sregs->u.e.vrsave; +	vcpu->arch.tcr = sregs->u.e.tcr; + +	if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_DEC) +		vcpu->arch.dec = sregs->u.e.dec; + +	kvmppc_emulate_dec(vcpu); + +	if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_TSR) { +		/* +		 * FIXME: existing KVM timer handling is incomplete. +		 * 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; +} + +static void get_sregs_arch206(struct kvm_vcpu *vcpu, +                              struct kvm_sregs *sregs) +{ +	sregs->u.e.features |= KVM_SREGS_E_ARCH206; + +	sregs->u.e.pir = 0; +	sregs->u.e.mcsrr0 = vcpu->arch.mcsrr0; +	sregs->u.e.mcsrr1 = vcpu->arch.mcsrr1; +	sregs->u.e.decar = vcpu->arch.decar; +	sregs->u.e.ivpr = vcpu->arch.ivpr; +} + +static int set_sregs_arch206(struct kvm_vcpu *vcpu, +                             struct kvm_sregs *sregs) +{ +	if (!(sregs->u.e.features & KVM_SREGS_E_ARCH206)) +		return 0; + +	if (sregs->u.e.pir != 0) +		return -EINVAL; + +	vcpu->arch.mcsrr0 = sregs->u.e.mcsrr0; +	vcpu->arch.mcsrr1 = sregs->u.e.mcsrr1; +	vcpu->arch.decar = sregs->u.e.decar; +	vcpu->arch.ivpr = sregs->u.e.ivpr; + +	return 0; +} + +void kvmppc_get_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ +	sregs->u.e.features |= KVM_SREGS_E_IVOR; + +	sregs->u.e.ivor_low[0] = vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL]; +	sregs->u.e.ivor_low[1] = vcpu->arch.ivor[BOOKE_IRQPRIO_MACHINE_CHECK]; +	sregs->u.e.ivor_low[2] = vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE]; +	sregs->u.e.ivor_low[3] = vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE]; +	sregs->u.e.ivor_low[4] = vcpu->arch.ivor[BOOKE_IRQPRIO_EXTERNAL]; +	sregs->u.e.ivor_low[5] = vcpu->arch.ivor[BOOKE_IRQPRIO_ALIGNMENT]; +	sregs->u.e.ivor_low[6] = vcpu->arch.ivor[BOOKE_IRQPRIO_PROGRAM]; +	sregs->u.e.ivor_low[7] = vcpu->arch.ivor[BOOKE_IRQPRIO_FP_UNAVAIL]; +	sregs->u.e.ivor_low[8] = vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL]; +	sregs->u.e.ivor_low[9] = vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL]; +	sregs->u.e.ivor_low[10] = vcpu->arch.ivor[BOOKE_IRQPRIO_DECREMENTER]; +	sregs->u.e.ivor_low[11] = vcpu->arch.ivor[BOOKE_IRQPRIO_FIT]; +	sregs->u.e.ivor_low[12] = vcpu->arch.ivor[BOOKE_IRQPRIO_WATCHDOG]; +	sregs->u.e.ivor_low[13] = vcpu->arch.ivor[BOOKE_IRQPRIO_DTLB_MISS]; +	sregs->u.e.ivor_low[14] = vcpu->arch.ivor[BOOKE_IRQPRIO_ITLB_MISS]; +	sregs->u.e.ivor_low[15] = vcpu->arch.ivor[BOOKE_IRQPRIO_DEBUG]; +} + +int kvmppc_set_sregs_ivor(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ +	if (!(sregs->u.e.features & KVM_SREGS_E_IVOR)) +		return 0; + +	vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL] = sregs->u.e.ivor_low[0]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_MACHINE_CHECK] = sregs->u.e.ivor_low[1]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE] = sregs->u.e.ivor_low[2]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE] = sregs->u.e.ivor_low[3]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_EXTERNAL] = sregs->u.e.ivor_low[4]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_ALIGNMENT] = sregs->u.e.ivor_low[5]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_PROGRAM] = sregs->u.e.ivor_low[6]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_FP_UNAVAIL] = sregs->u.e.ivor_low[7]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL] = sregs->u.e.ivor_low[8]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL] = sregs->u.e.ivor_low[9]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_DECREMENTER] = sregs->u.e.ivor_low[10]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_FIT] = sregs->u.e.ivor_low[11]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_WATCHDOG] = sregs->u.e.ivor_low[12]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_DTLB_MISS] = sregs->u.e.ivor_low[13]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_ITLB_MISS] = sregs->u.e.ivor_low[14]; +	vcpu->arch.ivor[BOOKE_IRQPRIO_DEBUG] = sregs->u.e.ivor_low[15]; + +	return 0; +} +  int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,                                    struct kvm_sregs *sregs)  { -	return -ENOTSUPP; +	sregs->pvr = vcpu->arch.pvr; + +	get_sregs_base(vcpu, sregs); +	get_sregs_arch206(vcpu, sregs); +	kvmppc_core_get_sregs(vcpu, sregs); +	return 0;  }  int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,                                    struct kvm_sregs *sregs)  { -	return -ENOTSUPP; +	int ret; + +	if (vcpu->arch.pvr != sregs->pvr) +		return -EINVAL; + +	ret = set_sregs_base(vcpu, sregs); +	if (ret < 0) +		return ret; + +	ret = set_sregs_arch206(vcpu, sregs); +	if (ret < 0) +		return ret; + +	return kvmppc_core_set_sregs(vcpu, sregs);  }  int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)  |