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

Commit 8b78645c authored by Paul Mackerras's avatar Paul Mackerras Committed by Alexander Graf
Browse files

KVM: PPC: Book3S: Facilities to save/restore XICS presentation ctrler state



This adds the ability for userspace to save and restore the state
of the XICS interrupt presentation controllers (ICPs) via the
KVM_GET/SET_ONE_REG interface.  Since there is one ICP per vcpu, we
simply define a new 64-bit register in the ONE_REG space for the ICP
state.  The state includes the CPU priority setting, the pending IPI
priority, and the priority and source number of any pending external
interrupt.

Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
parent d19bd862
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1808,6 +1808,7 @@ registers, find a list below:
  PPC   | KVM_REG_PPC_TLB2PS	| 32
  PPC   | KVM_REG_PPC_TLB3PS	| 32
  PPC   | KVM_REG_PPC_EPTCFG	| 32
  PPC   | KVM_REG_PPC_ICP_STATE | 64

ARM registers are mapped using the lower 32 bits.  The upper 16 of that
is the register group type, or coprocessor number:
+2 −0
Original line number Diff line number Diff line
@@ -313,6 +313,8 @@ extern void kvmppc_xics_free_icp(struct kvm_vcpu *vcpu);
extern int kvmppc_xics_create_icp(struct kvm_vcpu *vcpu, unsigned long server);
extern int kvm_vm_ioctl_xics_irq(struct kvm *kvm, struct kvm_irq_level *args);
extern int kvmppc_xics_hcall(struct kvm_vcpu *vcpu, u32 cmd);
extern u64 kvmppc_xics_get_icp(struct kvm_vcpu *vcpu);
extern int kvmppc_xics_set_icp(struct kvm_vcpu *vcpu, u64 icpval);
#else
static inline int kvmppc_xics_enabled(struct kvm_vcpu *vcpu)
	{ return 0; }
+12 −0
Original line number Diff line number Diff line
@@ -390,6 +390,18 @@ struct kvm_get_htab_header {
	__u16	n_invalid;
};

/* Per-vcpu XICS interrupt controller state */
#define KVM_REG_PPC_ICP_STATE	(KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x8c)

#define  KVM_REG_PPC_ICP_CPPR_SHIFT	56	/* current proc priority */
#define  KVM_REG_PPC_ICP_CPPR_MASK	0xff
#define  KVM_REG_PPC_ICP_XISR_SHIFT	32	/* interrupt status field */
#define  KVM_REG_PPC_ICP_XISR_MASK	0xffffff
#define  KVM_REG_PPC_ICP_MFRR_SHIFT	24	/* pending IPI priority */
#define  KVM_REG_PPC_ICP_MFRR_MASK	0xff
#define  KVM_REG_PPC_ICP_PPRI_SHIFT	16	/* pending irq priority */
#define  KVM_REG_PPC_ICP_PPRI_MASK	0xff

/* Device control API: PPC-specific devices */
#define KVM_DEV_MPIC_GRP_MISC		1
#define   KVM_DEV_MPIC_BASE_ADDR	0	/* 64-bit */
+19 −0
Original line number Diff line number Diff line
@@ -535,6 +535,15 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
					 &opcode, sizeof(u32));
			break;
		}
#ifdef CONFIG_KVM_XICS
		case KVM_REG_PPC_ICP_STATE:
			if (!vcpu->arch.icp) {
				r = -ENXIO;
				break;
			}
			val = get_reg_val(reg->id, kvmppc_xics_get_icp(vcpu));
			break;
#endif /* CONFIG_KVM_XICS */
		default:
			r = -EINVAL;
			break;
@@ -597,6 +606,16 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
			vcpu->arch.vscr.u[3] = set_reg_val(reg->id, val);
			break;
#endif /* CONFIG_ALTIVEC */
#ifdef CONFIG_KVM_XICS
		case KVM_REG_PPC_ICP_STATE:
			if (!vcpu->arch.icp) {
				r = -ENXIO;
				break;
			}
			r = kvmppc_xics_set_icp(vcpu,
						set_reg_val(reg->id, val));
			break;
#endif /* CONFIG_KVM_XICS */
		default:
			r = -EINVAL;
			break;
+90 −0
Original line number Diff line number Diff line
@@ -954,6 +954,96 @@ int kvmppc_xics_create_icp(struct kvm_vcpu *vcpu, unsigned long server_num)
	return 0;
}

u64 kvmppc_xics_get_icp(struct kvm_vcpu *vcpu)
{
	struct kvmppc_icp *icp = vcpu->arch.icp;
	union kvmppc_icp_state state;

	if (!icp)
		return 0;
	state = icp->state;
	return ((u64)state.cppr << KVM_REG_PPC_ICP_CPPR_SHIFT) |
		((u64)state.xisr << KVM_REG_PPC_ICP_XISR_SHIFT) |
		((u64)state.mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT) |
		((u64)state.pending_pri << KVM_REG_PPC_ICP_PPRI_SHIFT);
}

int kvmppc_xics_set_icp(struct kvm_vcpu *vcpu, u64 icpval)
{
	struct kvmppc_icp *icp = vcpu->arch.icp;
	struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
	union kvmppc_icp_state old_state, new_state;
	struct kvmppc_ics *ics;
	u8 cppr, mfrr, pending_pri;
	u32 xisr;
	u16 src;
	bool resend;

	if (!icp || !xics)
		return -ENOENT;

	cppr = icpval >> KVM_REG_PPC_ICP_CPPR_SHIFT;
	xisr = (icpval >> KVM_REG_PPC_ICP_XISR_SHIFT) &
		KVM_REG_PPC_ICP_XISR_MASK;
	mfrr = icpval >> KVM_REG_PPC_ICP_MFRR_SHIFT;
	pending_pri = icpval >> KVM_REG_PPC_ICP_PPRI_SHIFT;

	/* Require the new state to be internally consistent */
	if (xisr == 0) {
		if (pending_pri != 0xff)
			return -EINVAL;
	} else if (xisr == XICS_IPI) {
		if (pending_pri != mfrr || pending_pri >= cppr)
			return -EINVAL;
	} else {
		if (pending_pri >= mfrr || pending_pri >= cppr)
			return -EINVAL;
		ics = kvmppc_xics_find_ics(xics, xisr, &src);
		if (!ics)
			return -EINVAL;
	}

	new_state.raw = 0;
	new_state.cppr = cppr;
	new_state.xisr = xisr;
	new_state.mfrr = mfrr;
	new_state.pending_pri = pending_pri;

	/*
	 * Deassert the CPU interrupt request.
	 * icp_try_update will reassert it if necessary.
	 */
	kvmppc_book3s_dequeue_irqprio(icp->vcpu,
				      BOOK3S_INTERRUPT_EXTERNAL_LEVEL);

	/*
	 * Note that if we displace an interrupt from old_state.xisr,
	 * we don't mark it as rejected.  We expect userspace to set
	 * the state of the interrupt sources to be consistent with
	 * the ICP states (either before or afterwards, which doesn't
	 * matter).  We do handle resends due to CPPR becoming less
	 * favoured because that is necessary to end up with a
	 * consistent state in the situation where userspace restores
	 * the ICS states before the ICP states.
	 */
	do {
		old_state = ACCESS_ONCE(icp->state);

		if (new_state.mfrr <= old_state.mfrr) {
			resend = false;
			new_state.need_resend = old_state.need_resend;
		} else {
			resend = old_state.need_resend;
			new_state.need_resend = 0;
		}
	} while (!icp_try_update(icp, old_state, new_state, false));

	if (resend)
		icp_check_resend(xics, icp);

	return 0;
}

/* -- ioctls -- */

int kvm_vm_ioctl_xics_irq(struct kvm *kvm, struct kvm_irq_level *args)