diff mbox

[RFC,9/9] KVM: PPC: Book 3S: Facilities to save/restore XICS source controller state

Message ID 20121105032520.GJ22409@drongo
State New, archived
Headers show

Commit Message

Paul Mackerras Nov. 5, 2012, 3:25 a.m. UTC
This adds the ability for userspace to save and restore the state of
the XICS interrupt source controllers (ICS) via two new VM ioctls,
KVM_IRQCHIP_GET_SOURCES and KVM_IRQCHIP_SET_SOURCES.  Both take an
argument struct that gives the starting interrupt source number, the
number of interrupt sources to be processed, and a pointer to an array
with space for 64 bits per source where the state of the interrupt
sources are to be stored or loaded.  The interrupt sources are required
to all be within one ICS.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 Documentation/virtual/kvm/api.txt |   33 ++++++++++++
 arch/powerpc/kvm/book3s_xics.c    |  108 +++++++++++++++++++++++++++++++++++++
 arch/powerpc/kvm/powerpc.c        |    4 +-
 include/uapi/linux/kvm.h          |   17 ++++++
 4 files changed, 161 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index fbe018e..2ba53a1 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2107,6 +2107,39 @@  struct.  When creating an ICS, the argument struct further indicates
 the BUID (Bus Unit ID) and number of interrupt sources for the new
 ICS.
 
+4.80 KVM_IRQCHIP_GET_SOURCES
+
+Capability: KVM_CAP_SPAPR_XICS
+Architectures: ppc
+Type: vm ioctl
+Parameters: struct kvm_irq_sources
+Returns: 0 on success, -1 on error
+
+Copies configuration and status information about a range of interrupt
+sources into a user-supplied buffer.  The argument struct gives the
+starting interrupt source number and the number of interrupt sources.
+All of the interrupt sources must be within the range of a single
+interrupt source controller (ICS).  The user buffer is an array of
+64-bit quantities, one per interrupt source, with (from the least-
+significant bit) 32 bits of interrupt server number, 8 bits of
+priority, and 1 bit each for a level-sensitive indicator, a masked
+indicator, and a pending indicator.
+
+4.81 KVM_IRQCHIP_SET_SOURCES
+
+Capability: KVM_CAP_SPAPR_XICS
+Architectures: ppc
+Type: vm ioctl
+Parameters: struct kvm_irq_sources
+Returns: 0 on success, -1 on error
+
+Sets the configuration and status for a range of interrupt sources
+from information supplied in a user-supplied buffer.  The argument
+struct gives the starting interrupt source number and the number of
+interrupt sources.  All of the interrupt sources must be within the
+range of a single interrupt source controller (ICS).  The user buffer
+is formatted as for KVM_IRQCHIP_GET_SOURCES.
+
 
 5. The kvm_run structure
 ------------------------
diff --git a/arch/powerpc/kvm/book3s_xics.c b/arch/powerpc/kvm/book3s_xics.c
index 8314cd9..8071c7e 100644
--- a/arch/powerpc/kvm/book3s_xics.c
+++ b/arch/powerpc/kvm/book3s_xics.c
@@ -1066,6 +1066,88 @@  int kvmppc_xics_set_icp(struct kvm_vcpu *vcpu, u64 icpval)
 	return 0;
 }
 
+static int kvm_xics_get_sources(struct kvm *kvm, struct kvm_irq_sources *srcs)
+{
+	int ret = 0;
+	struct kvmppc_xics *xics = kvm->arch.xics;
+	struct kvmppc_ics *ics;
+	struct ics_irq_state *irqp;
+	u64 __user *ubufp;
+	u16 idx;
+	u64 val;
+	long int i;
+
+	ics = kvmppc_xics_find_ics(xics, srcs->irq, &idx);
+	if (!ics || idx + srcs->nr_irqs > ics->nr_irqs)
+		return -ENOENT;
+
+	irqp = &ics->irq_state[idx];
+	ubufp = srcs->irqbuf;
+	mutex_lock(&ics->lock);
+	for (i = 0; i < srcs->nr_irqs; ++i, ++irqp, ++ubufp) {
+		val = irqp->server;
+		if (irqp->priority == MASKED && irqp->saved_priority != MASKED)
+			val |= KVM_IRQ_MASKED | ((u64)irqp->saved_priority <<
+						 KVM_IRQ_PRIORITY_SHIFT);
+		else
+			val |= ((u64)irqp->priority << KVM_IRQ_PRIORITY_SHIFT);
+		if (irqp->asserted)
+			val |= KVM_IRQ_LEVEL_SENSITIVE | KVM_IRQ_PENDING;
+		else if (irqp->masked_pending || irqp->resend)
+			val |= KVM_IRQ_PENDING;
+		ret = -EFAULT;
+		if (__put_user(val, ubufp))
+			break;
+		ret = 0;
+	}
+	mutex_unlock(&ics->lock);
+	return ret;
+}
+
+static int kvm_xics_set_sources(struct kvm *kvm, struct kvm_irq_sources *srcs)
+{
+	int ret = 0;
+	struct kvmppc_xics *xics = kvm->arch.xics;
+	struct kvmppc_ics *ics;
+	struct ics_irq_state *irqp;
+	u64 __user *ubufp;
+	u16 idx;
+	u64 val;
+	long int i;
+
+	ics = kvmppc_xics_find_ics(xics, srcs->irq, &idx);
+	if (!ics || idx + srcs->nr_irqs > ics->nr_irqs)
+		return -ENOENT;
+
+	irqp = &ics->irq_state[idx];
+	ubufp = srcs->irqbuf;
+	for (i = 0; i < srcs->nr_irqs; ++i, ++irqp, ++ubufp) {
+		ret = -EFAULT;
+		if (__get_user(val, ubufp))
+			break;
+		ret = 0;
+
+		mutex_lock(&ics->lock);
+		irqp->server = val & KVM_IRQ_SERVER_MASK;
+		irqp->saved_priority = val >> KVM_IRQ_PRIORITY_SHIFT;
+		if (val & KVM_IRQ_MASKED)
+			irqp->priority = MASKED;
+		else
+			irqp->priority = irqp->saved_priority;
+		irqp->resend = 0;
+		irqp->masked_pending = 0;
+		irqp->asserted = 0;
+		if ((val & KVM_IRQ_PENDING) && (val & KVM_IRQ_LEVEL_SENSITIVE))
+			irqp->asserted = 1;
+		mutex_unlock(&ics->lock);
+
+		if (val & KVM_IRQ_PENDING)
+			icp_deliver_irq(xics, NULL, irqp->number);
+	}
+
+	return ret;
+}
+
 /* -- ioctls -- */
 
 static int kvm_vm_ioctl_create_icp(struct kvm *kvm,
@@ -1200,6 +1282,32 @@  int kvmppc_xics_ioctl(struct kvm *kvm, unsigned ioctl, unsigned long arg)
 		break;
 	}
 
+	case KVM_IRQCHIP_GET_SOURCES: {
+		struct kvm_irq_sources sources;
+
+		rc = -EFAULT;
+		if (copy_from_user(&sources, argp, sizeof(sources)))
+			break;
+		if (!access_ok(VERIFY_WRITE, sources.irqbuf,
+			       sources.nr_irqs * sizeof(u64)))
+			break;
+		rc = kvm_xics_get_sources(kvm, &sources);
+		break;
+	}
+
+	case KVM_IRQCHIP_SET_SOURCES: {
+		struct kvm_irq_sources sources;
+
+		rc = -EFAULT;
+		if (copy_from_user(&sources, argp, sizeof(sources)))
+			break;
+		if (!access_ok(VERIFY_READ, sources.irqbuf,
+			       sources.nr_irqs * sizeof(u64)))
+			break;
+		rc = kvm_xics_set_sources(kvm, &sources);
+		break;
+	}
+
 	default:
 		rc = -ENOTTY;
 		break;
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 90b5b5c..0d443d4 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -955,7 +955,9 @@  long kvm_arch_vm_ioctl(struct file *filp,
 		r = kvm_vm_ioctl_rtas_define_token(kvm, argp);
 		break;
 	}
-	case KVM_IRQ_LINE: {
+	case KVM_IRQ_LINE:
+	case KVM_IRQCHIP_GET_SOURCES:
+	case KVM_IRQCHIP_SET_SOURCES: {
 		struct kvm *kvm = filp->private_data;
 
 		r = -ENOTTY;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 8674d32..edcf78d 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -779,6 +779,21 @@  struct kvm_msi {
 	__u8  pad[16];
 };
 
+struct kvm_irq_sources {
+	__u32 irq;
+	__u32 nr_irqs;
+	__u64 __user *irqbuf;
+};
+
+/* irqbuf entries are laid out like this: */
+#define KVM_IRQ_SERVER_SHIFT	0
+#define KVM_IRQ_SERVER_MASK	0xffffffffULL
+#define KVM_IRQ_PRIORITY_SHIFT	32
+#define KVM_IRQ_PRIORITY_MASK	0xff
+#define KVM_IRQ_LEVEL_SENSITIVE	(1ULL << 40)
+#define KVM_IRQ_MASKED		(1ULL << 41)
+#define KVM_IRQ_PENDING		(1ULL << 42)
+
 /*
  * ioctls for VM fds
  */
@@ -867,6 +882,8 @@  struct kvm_s390_ucas_mapping {
 #ifdef __KVM_HAVE_IRQCHIP_ARGS
 #define KVM_CREATE_IRQCHIP_ARGS   _IOW(KVMIO,  0xab, struct kvm_irqchip_args)
 #endif
+#define KVM_IRQCHIP_GET_SOURCES	  _IOW(KVMIO,  0xac, struct kvm_irq_sources)
+#define KVM_IRQCHIP_SET_SOURCES	  _IOW(KVMIO,  0xad, struct kvm_irq_sources)
 
 /*
  * ioctls for vcpu fds