@@ -1762,6 +1762,7 @@ registers, find a list below:
PPC | KVM_REG_PPC_VPA_DTL | 128
PPC | KVM_REG_PPC_EPCR | 32
PPC | KVM_REG_PPC_EPR | 32
+ PPC | KVM_REG_PPC_ICP_STATE | 64
4.69 KVM_GET_ONE_REG
@@ -236,6 +236,8 @@ struct kvm_irq_ctrler {
void (*free_ctrler)(struct kvm *kvm);
int (*hcall)(struct kvm_vcpu *vcpu, unsigned long req);
int (*ioctl)(struct kvm *kvm, unsigned int ioctl, unsigned long arg);
+ u64 (*get_one_reg)(struct kvm_vcpu *vcpu, u64 reg);
+ int (*set_one_reg)(struct kvm_vcpu *vcpu, u64 reg, u64 val);
};
struct kvm_arch {
@@ -423,4 +423,17 @@ struct kvm_get_htab_header {
#define KVM_REG_PPC_EPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x85)
#define KVM_REG_PPC_EPR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x86)
+/* Per-vcpu interrupt controller state */
+#define KVM_REG_PPC_ICP_STATE (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x87)
+
+/* Layout of above for XICS */
+#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
+
#endif /* __LINUX_KVM_POWERPC_H */
@@ -529,6 +529,15 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
val = get_reg_val(reg->id, vcpu->arch.vscr.u[3]);
break;
#endif /* CONFIG_ALTIVEC */
+ case KVM_REG_PPC_ICP_STATE: {
+ struct kvm_irq_ctrler *ic = vcpu->kvm->arch.irq_ctrler;
+ if (!ic) {
+ r = -ENXIO;
+ break;
+ }
+ val = get_reg_val(reg->id, ic->get_one_reg(vcpu, reg->id));
+ break;
+ }
default:
r = -EINVAL;
break;
@@ -591,6 +600,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 */
+ case KVM_REG_PPC_ICP_STATE: {
+ struct kvm_irq_ctrler *ic = vcpu->kvm->arch.irq_ctrler;
+
+ if (!ic) {
+ r = -ENXIO;
+ break;
+ }
+ r = ic->set_one_reg(vcpu, reg->id, set_reg_val(reg->id, val));
+ break;
+ }
default:
r = -EINVAL;
break;
@@ -888,6 +888,96 @@ static void kvmppc_xics_free(struct kvm *kvm)
kfree(xics);
}
+static u64 kvmppc_xics_get_icp(struct kvm_vcpu *vcpu, u64 reg_id)
+{
+ 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);
+}
+
+static int kvmppc_xics_set_icp(struct kvm_vcpu *vcpu, u64 reg_id, u64 icpval)
+{
+ struct kvmppc_icp *icp = vcpu->arch.icp;
+ struct kvmppc_xics *xics = vcpu->kvm->arch.irq_ctrler_private;
+ 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;
+}
+
static int kvm_xics_get_sources(struct kvm *kvm, struct kvm_irq_sources *srcs)
{
int ret = 0;
@@ -1080,6 +1170,8 @@ static struct kvm_irq_ctrler xics_ctrler = {
.free_ctrler = kvmppc_xics_free,
.hcall = kvmppc_xics_hcall,
.ioctl = kvmppc_xics_ioctl,
+ .get_one_reg = kvmppc_xics_get_icp,
+ .set_one_reg = kvmppc_xics_set_icp,
};
int kvmppc_xics_create(struct kvm *kvm, struct kvm_irqchip_args *args)
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> --- Documentation/virtual/kvm/api.txt | 1 + arch/powerpc/include/asm/kvm_host.h | 2 + arch/powerpc/include/uapi/asm/kvm.h | 13 +++++ arch/powerpc/kvm/book3s.c | 19 ++++++++ arch/powerpc/kvm/book3s_xics.c | 92 +++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+)