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@xxxxxxxxx> --- 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 --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 -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html