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

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
......@@ -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:
......
......@@ -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; }
......
......@@ -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 */
......
......@@ -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;
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment