Skip to content

Commit

Permalink
KVM: PPC: Book3S: Facilities to save/restore XICS presentation ctrler…
Browse files Browse the repository at this point in the history
… 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>
  • Loading branch information
Paul Mackerras authored and Alexander Graf committed Apr 26, 2013
1 parent d19bd86 commit 8b78645
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions Documentation/virtual/kvm/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions arch/powerpc/include/asm/kvm_ppc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
12 changes: 12 additions & 0 deletions arch/powerpc/include/uapi/asm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
19 changes: 19 additions & 0 deletions arch/powerpc/kvm/book3s.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
90 changes: 90 additions & 0 deletions arch/powerpc/kvm/book3s_xics.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 8b78645

Please sign in to comment.