diff options
Diffstat (limited to 'arch/powerpc/kvm/book3s_xics.h')
| -rw-r--r-- | arch/powerpc/kvm/book3s_xics.h | 130 | 
1 files changed, 130 insertions, 0 deletions
diff --git a/arch/powerpc/kvm/book3s_xics.h b/arch/powerpc/kvm/book3s_xics.h new file mode 100644 index 00000000000..dd9326c5c19 --- /dev/null +++ b/arch/powerpc/kvm/book3s_xics.h @@ -0,0 +1,130 @@ +/* + * Copyright 2012 Michael Ellerman, IBM Corporation. + * Copyright 2012 Benjamin Herrenschmidt, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + */ + +#ifndef _KVM_PPC_BOOK3S_XICS_H +#define _KVM_PPC_BOOK3S_XICS_H + +/* + * We use a two-level tree to store interrupt source information. + * There are up to 1024 ICS nodes, each of which can represent + * 1024 sources. + */ +#define KVMPPC_XICS_MAX_ICS_ID	1023 +#define KVMPPC_XICS_ICS_SHIFT	10 +#define KVMPPC_XICS_IRQ_PER_ICS	(1 << KVMPPC_XICS_ICS_SHIFT) +#define KVMPPC_XICS_SRC_MASK	(KVMPPC_XICS_IRQ_PER_ICS - 1) + +/* + * Interrupt source numbers below this are reserved, for example + * 0 is "no interrupt", and 2 is used for IPIs. + */ +#define KVMPPC_XICS_FIRST_IRQ	16 +#define KVMPPC_XICS_NR_IRQS	((KVMPPC_XICS_MAX_ICS_ID + 1) * \ +				 KVMPPC_XICS_IRQ_PER_ICS) + +/* Priority value to use for disabling an interrupt */ +#define MASKED	0xff + +/* State for one irq source */ +struct ics_irq_state { +	u32 number; +	u32 server; +	u8  priority; +	u8  saved_priority; +	u8  resend; +	u8  masked_pending; +	u8  asserted; /* Only for LSI */ +	u8  exists; +}; + +/* Atomic ICP state, updated with a single compare & swap */ +union kvmppc_icp_state { +	unsigned long raw; +	struct { +		u8 out_ee:1; +		u8 need_resend:1; +		u8 cppr; +		u8 mfrr; +		u8 pending_pri; +		u32 xisr; +	}; +}; + +/* One bit per ICS */ +#define ICP_RESEND_MAP_SIZE	(KVMPPC_XICS_MAX_ICS_ID / BITS_PER_LONG + 1) + +struct kvmppc_icp { +	struct kvm_vcpu *vcpu; +	unsigned long server_num; +	union kvmppc_icp_state state; +	unsigned long resend_map[ICP_RESEND_MAP_SIZE]; + +	/* Real mode might find something too hard, here's the action +	 * it might request from virtual mode +	 */ +#define XICS_RM_KICK_VCPU	0x1 +#define XICS_RM_CHECK_RESEND	0x2 +#define XICS_RM_REJECT		0x4 +	u32 rm_action; +	struct kvm_vcpu *rm_kick_target; +	u32  rm_reject; + +	/* Debug stuff for real mode */ +	union kvmppc_icp_state rm_dbgstate; +	struct kvm_vcpu *rm_dbgtgt; +}; + +struct kvmppc_ics { +	struct mutex lock; +	u16 icsid; +	struct ics_irq_state irq_state[KVMPPC_XICS_IRQ_PER_ICS]; +}; + +struct kvmppc_xics { +	struct kvm *kvm; +	struct kvm_device *dev; +	struct dentry *dentry; +	u32 max_icsid; +	bool real_mode; +	bool real_mode_dbg; +	struct kvmppc_ics *ics[KVMPPC_XICS_MAX_ICS_ID + 1]; +}; + +static inline struct kvmppc_icp *kvmppc_xics_find_server(struct kvm *kvm, +							 u32 nr) +{ +	struct kvm_vcpu *vcpu = NULL; +	int i; + +	kvm_for_each_vcpu(i, vcpu, kvm) { +		if (vcpu->arch.icp && nr == vcpu->arch.icp->server_num) +			return vcpu->arch.icp; +	} +	return NULL; +} + +static inline struct kvmppc_ics *kvmppc_xics_find_ics(struct kvmppc_xics *xics, +						      u32 irq, u16 *source) +{ +	u32 icsid = irq >> KVMPPC_XICS_ICS_SHIFT; +	u16 src = irq & KVMPPC_XICS_SRC_MASK; +	struct kvmppc_ics *ics; + +	if (source) +		*source = src; +	if (icsid > KVMPPC_XICS_MAX_ICS_ID) +		return NULL; +	ics = xics->ics[icsid]; +	if (!ics) +		return NULL; +	return ics; +} + + +#endif /* _KVM_PPC_BOOK3S_XICS_H */  | 
