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: Paul Mackerras <paulus@samba.org> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
d19bd86204
commit
8b78645c93
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue