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: ...@@ -1808,6 +1808,7 @@ registers, find a list below:
PPC | KVM_REG_PPC_TLB2PS | 32 PPC | KVM_REG_PPC_TLB2PS | 32
PPC | KVM_REG_PPC_TLB3PS | 32 PPC | KVM_REG_PPC_TLB3PS | 32
PPC | KVM_REG_PPC_EPTCFG | 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 ARM registers are mapped using the lower 32 bits. The upper 16 of that
is the register group type, or coprocessor number: is the register group type, or coprocessor number:
......
...@@ -313,6 +313,8 @@ extern void kvmppc_xics_free_icp(struct kvm_vcpu *vcpu); ...@@ -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 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 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 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 #else
static inline int kvmppc_xics_enabled(struct kvm_vcpu *vcpu) static inline int kvmppc_xics_enabled(struct kvm_vcpu *vcpu)
{ return 0; } { return 0; }
......
...@@ -390,6 +390,18 @@ struct kvm_get_htab_header { ...@@ -390,6 +390,18 @@ struct kvm_get_htab_header {
__u16 n_invalid; __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 */ /* Device control API: PPC-specific devices */
#define KVM_DEV_MPIC_GRP_MISC 1 #define KVM_DEV_MPIC_GRP_MISC 1
#define KVM_DEV_MPIC_BASE_ADDR 0 /* 64-bit */ #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) ...@@ -535,6 +535,15 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
&opcode, sizeof(u32)); &opcode, sizeof(u32));
break; 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: default:
r = -EINVAL; r = -EINVAL;
break; break;
...@@ -597,6 +606,16 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) ...@@ -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); vcpu->arch.vscr.u[3] = set_reg_val(reg->id, val);
break; break;
#endif /* CONFIG_ALTIVEC */ #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: default:
r = -EINVAL; r = -EINVAL;
break; break;
......
...@@ -954,6 +954,96 @@ int kvmppc_xics_create_icp(struct kvm_vcpu *vcpu, unsigned long server_num) ...@@ -954,6 +954,96 @@ int kvmppc_xics_create_icp(struct kvm_vcpu *vcpu, unsigned long server_num)
return 0; 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 -- */ /* -- ioctls -- */
int kvm_vm_ioctl_xics_irq(struct kvm *kvm, struct kvm_irq_level *args) 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