Merge tag 'split-asm_system_h-for-linus-20120328' of git://git.kernel.org/pub/scm...
[linux-2.6.git] / arch / s390 / kvm / kvm-s390.c
index d56de16..217ce44 100644 (file)
@@ -129,6 +129,10 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_S390_PSW:
        case KVM_CAP_S390_GMAP:
        case KVM_CAP_SYNC_MMU:
+#ifdef CONFIG_KVM_S390_UCONTROL
+       case KVM_CAP_S390_UCONTROL:
+#endif
+       case KVM_CAP_SYNC_REGS:
                r = 1;
                break;
        default:
@@ -171,11 +175,22 @@ long kvm_arch_vm_ioctl(struct file *filp,
        return r;
 }
 
-int kvm_arch_init_vm(struct kvm *kvm)
+int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 {
        int rc;
        char debug_name[16];
 
+       rc = -EINVAL;
+#ifdef CONFIG_KVM_S390_UCONTROL
+       if (type & ~KVM_VM_S390_UCONTROL)
+               goto out_err;
+       if ((type & KVM_VM_S390_UCONTROL) && (!capable(CAP_SYS_ADMIN)))
+               goto out_err;
+#else
+       if (type)
+               goto out_err;
+#endif
+
        rc = s390_enable_sie();
        if (rc)
                goto out_err;
@@ -198,10 +213,13 @@ int kvm_arch_init_vm(struct kvm *kvm)
        debug_register_view(kvm->arch.dbf, &debug_sprintf_view);
        VM_EVENT(kvm, 3, "%s", "vm created");
 
-       kvm->arch.gmap = gmap_alloc(current->mm);
-       if (!kvm->arch.gmap)
-               goto out_nogmap;
-
+       if (type & KVM_VM_S390_UCONTROL) {
+               kvm->arch.gmap = NULL;
+       } else {
+               kvm->arch.gmap = gmap_alloc(current->mm);
+               if (!kvm->arch.gmap)
+                       goto out_nogmap;
+       }
        return 0;
 out_nogmap:
        debug_unregister(kvm->arch.dbf);
@@ -214,11 +232,18 @@ out_err:
 void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 {
        VCPU_EVENT(vcpu, 3, "%s", "free cpu");
-       clear_bit(63 - vcpu->vcpu_id, (unsigned long *) &vcpu->kvm->arch.sca->mcn);
-       if (vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sda ==
-               (__u64) vcpu->arch.sie_block)
-               vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sda = 0;
+       if (!kvm_is_ucontrol(vcpu->kvm)) {
+               clear_bit(63 - vcpu->vcpu_id,
+                         (unsigned long *) &vcpu->kvm->arch.sca->mcn);
+               if (vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sda ==
+                   (__u64) vcpu->arch.sie_block)
+                       vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sda = 0;
+       }
        smp_mb();
+
+       if (kvm_is_ucontrol(vcpu->kvm))
+               gmap_free(vcpu->arch.gmap);
+
        free_page((unsigned long)(vcpu->arch.sie_block));
        kvm_vcpu_uninit(vcpu);
        kfree(vcpu);
@@ -249,13 +274,25 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
        kvm_free_vcpus(kvm);
        free_page((unsigned long)(kvm->arch.sca));
        debug_unregister(kvm->arch.dbf);
-       gmap_free(kvm->arch.gmap);
+       if (!kvm_is_ucontrol(kvm))
+               gmap_free(kvm->arch.gmap);
 }
 
 /* Section: vcpu related */
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
+       if (kvm_is_ucontrol(vcpu->kvm)) {
+               vcpu->arch.gmap = gmap_alloc(current->mm);
+               if (!vcpu->arch.gmap)
+                       return -ENOMEM;
+               return 0;
+       }
+
        vcpu->arch.gmap = vcpu->kvm->arch.gmap;
+       vcpu->run->kvm_valid_regs = KVM_SYNC_PREFIX |
+                                   KVM_SYNC_GPRS |
+                                   KVM_SYNC_ACRS |
+                                   KVM_SYNC_CRS;
        return 0;
 }
 
@@ -270,7 +307,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        save_access_regs(vcpu->arch.host_acrs);
        vcpu->arch.guest_fpregs.fpc &= FPC_VALID_MASK;
        restore_fp_regs(&vcpu->arch.guest_fpregs);
-       restore_access_regs(vcpu->arch.guest_acrs);
+       restore_access_regs(vcpu->run->s.regs.acrs);
        gmap_enable(vcpu->arch.gmap);
        atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
 }
@@ -280,7 +317,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
        atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
        gmap_disable(vcpu->arch.gmap);
        save_fp_regs(&vcpu->arch.guest_fpregs);
-       save_access_regs(vcpu->arch.guest_acrs);
+       save_access_regs(vcpu->run->s.regs.acrs);
        restore_fp_regs(&vcpu->arch.host_fpregs);
        restore_access_regs(vcpu->arch.host_acrs);
 }
@@ -290,8 +327,7 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
        /* this equals initial cpu reset in pop, but we don't switch to ESA */
        vcpu->arch.sie_block->gpsw.mask = 0UL;
        vcpu->arch.sie_block->gpsw.addr = 0UL;
-       vcpu->arch.sie_block->prefix    = 0UL;
-       vcpu->arch.sie_block->ihcpu     = 0xffff;
+       kvm_s390_set_prefix(vcpu, 0);
        vcpu->arch.sie_block->cputm     = 0UL;
        vcpu->arch.sie_block->ckc       = 0UL;
        vcpu->arch.sie_block->todpr     = 0;
@@ -342,12 +378,19 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
                goto out_free_cpu;
 
        vcpu->arch.sie_block->icpua = id;
-       BUG_ON(!kvm->arch.sca);
-       if (!kvm->arch.sca->cpu[id].sda)
-               kvm->arch.sca->cpu[id].sda = (__u64) vcpu->arch.sie_block;
-       vcpu->arch.sie_block->scaoh = (__u32)(((__u64)kvm->arch.sca) >> 32);
-       vcpu->arch.sie_block->scaol = (__u32)(__u64)kvm->arch.sca;
-       set_bit(63 - id, (unsigned long *) &kvm->arch.sca->mcn);
+       if (!kvm_is_ucontrol(kvm)) {
+               if (!kvm->arch.sca) {
+                       WARN_ON_ONCE(1);
+                       goto out_free_cpu;
+               }
+               if (!kvm->arch.sca->cpu[id].sda)
+                       kvm->arch.sca->cpu[id].sda =
+                               (__u64) vcpu->arch.sie_block;
+               vcpu->arch.sie_block->scaoh =
+                       (__u32)(((__u64)kvm->arch.sca) >> 32);
+               vcpu->arch.sie_block->scaol = (__u32)(__u64)kvm->arch.sca;
+               set_bit(63 - id, (unsigned long *) &kvm->arch.sca->mcn);
+       }
 
        spin_lock_init(&vcpu->arch.local_int.lock);
        INIT_LIST_HEAD(&vcpu->arch.local_int.list);
@@ -388,29 +431,29 @@ static int kvm_arch_vcpu_ioctl_initial_reset(struct kvm_vcpu *vcpu)
 
 int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
 {
-       memcpy(&vcpu->arch.guest_gprs, &regs->gprs, sizeof(regs->gprs));
+       memcpy(&vcpu->run->s.regs.gprs, &regs->gprs, sizeof(regs->gprs));
        return 0;
 }
 
 int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
 {
-       memcpy(&regs->gprs, &vcpu->arch.guest_gprs, sizeof(regs->gprs));
+       memcpy(&regs->gprs, &vcpu->run->s.regs.gprs, sizeof(regs->gprs));
        return 0;
 }
 
 int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
                                  struct kvm_sregs *sregs)
 {
-       memcpy(&vcpu->arch.guest_acrs, &sregs->acrs, sizeof(sregs->acrs));
+       memcpy(&vcpu->run->s.regs.acrs, &sregs->acrs, sizeof(sregs->acrs));
        memcpy(&vcpu->arch.sie_block->gcr, &sregs->crs, sizeof(sregs->crs));
-       restore_access_regs(vcpu->arch.guest_acrs);
+       restore_access_regs(vcpu->run->s.regs.acrs);
        return 0;
 }
 
 int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
                                  struct kvm_sregs *sregs)
 {
-       memcpy(&sregs->acrs, &vcpu->arch.guest_acrs, sizeof(sregs->acrs));
+       memcpy(&sregs->acrs, &vcpu->run->s.regs.acrs, sizeof(sregs->acrs));
        memcpy(&sregs->crs, &vcpu->arch.sie_block->gcr, sizeof(sregs->crs));
        return 0;
 }
@@ -418,7 +461,7 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
 int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
        memcpy(&vcpu->arch.guest_fpregs.fprs, &fpu->fprs, sizeof(fpu->fprs));
-       vcpu->arch.guest_fpregs.fpc = fpu->fpc;
+       vcpu->arch.guest_fpregs.fpc = fpu->fpc & FPC_VALID_MASK;
        restore_fp_regs(&vcpu->arch.guest_fpregs);
        return 0;
 }
@@ -467,9 +510,11 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
        return -EINVAL; /* not implemented yet */
 }
 
-static void __vcpu_run(struct kvm_vcpu *vcpu)
+static int __vcpu_run(struct kvm_vcpu *vcpu)
 {
-       memcpy(&vcpu->arch.sie_block->gg14, &vcpu->arch.guest_gprs[14], 16);
+       int rc;
+
+       memcpy(&vcpu->arch.sie_block->gg14, &vcpu->run->s.regs.gprs[14], 16);
 
        if (need_resched())
                schedule();
@@ -477,7 +522,8 @@ static void __vcpu_run(struct kvm_vcpu *vcpu)
        if (test_thread_flag(TIF_MCCK_PENDING))
                s390_handle_mcck();
 
-       kvm_s390_deliver_pending_interrupts(vcpu);
+       if (!kvm_is_ucontrol(vcpu->kvm))
+               kvm_s390_deliver_pending_interrupts(vcpu);
 
        vcpu->arch.sie_block->icptcode = 0;
        local_irq_disable();
@@ -485,9 +531,15 @@ static void __vcpu_run(struct kvm_vcpu *vcpu)
        local_irq_enable();
        VCPU_EVENT(vcpu, 6, "entering sie flags %x",
                   atomic_read(&vcpu->arch.sie_block->cpuflags));
-       if (sie64a(vcpu->arch.sie_block, vcpu->arch.guest_gprs)) {
-               VCPU_EVENT(vcpu, 3, "%s", "fault in sie instruction");
-               kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = sie64a(vcpu->arch.sie_block, vcpu->run->s.regs.gprs);
+       if (rc) {
+               if (kvm_is_ucontrol(vcpu->kvm)) {
+                       rc = SIE_INTERCEPT_UCONTROL;
+               } else {
+                       VCPU_EVENT(vcpu, 3, "%s", "fault in sie instruction");
+                       kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+                       rc = 0;
+               }
        }
        VCPU_EVENT(vcpu, 6, "exit sie icptcode %d",
                   vcpu->arch.sie_block->icptcode);
@@ -495,7 +547,8 @@ static void __vcpu_run(struct kvm_vcpu *vcpu)
        kvm_guest_exit();
        local_irq_enable();
 
-       memcpy(&vcpu->arch.guest_gprs[14], &vcpu->arch.sie_block->gg14, 16);
+       memcpy(&vcpu->run->s.regs.gprs[14], &vcpu->arch.sie_block->gg14, 16);
+       return rc;
 }
 
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
@@ -516,6 +569,7 @@ rerun_vcpu:
        case KVM_EXIT_UNKNOWN:
        case KVM_EXIT_INTR:
        case KVM_EXIT_S390_RESET:
+       case KVM_EXIT_S390_UCONTROL:
                break;
        default:
                BUG();
@@ -523,12 +577,26 @@ rerun_vcpu:
 
        vcpu->arch.sie_block->gpsw.mask = kvm_run->psw_mask;
        vcpu->arch.sie_block->gpsw.addr = kvm_run->psw_addr;
+       if (kvm_run->kvm_dirty_regs & KVM_SYNC_PREFIX) {
+               kvm_run->kvm_dirty_regs &= ~KVM_SYNC_PREFIX;
+               kvm_s390_set_prefix(vcpu, kvm_run->s.regs.prefix);
+       }
+       if (kvm_run->kvm_dirty_regs & KVM_SYNC_CRS) {
+               kvm_run->kvm_dirty_regs &= ~KVM_SYNC_CRS;
+               memcpy(&vcpu->arch.sie_block->gcr, &kvm_run->s.regs.crs, 128);
+               kvm_s390_set_prefix(vcpu, kvm_run->s.regs.prefix);
+       }
 
        might_fault();
 
        do {
-               __vcpu_run(vcpu);
-               rc = kvm_handle_sie_intercept(vcpu);
+               rc = __vcpu_run(vcpu);
+               if (rc)
+                       break;
+               if (kvm_is_ucontrol(vcpu->kvm))
+                       rc = -EOPNOTSUPP;
+               else
+                       rc = kvm_handle_sie_intercept(vcpu);
        } while (!signal_pending(current) && !rc);
 
        if (rc == SIE_INTERCEPT_RERUNVCPU)
@@ -539,6 +607,16 @@ rerun_vcpu:
                rc = -EINTR;
        }
 
+#ifdef CONFIG_KVM_S390_UCONTROL
+       if (rc == SIE_INTERCEPT_UCONTROL) {
+               kvm_run->exit_reason = KVM_EXIT_S390_UCONTROL;
+               kvm_run->s390_ucontrol.trans_exc_code =
+                       current->thread.gmap_addr;
+               kvm_run->s390_ucontrol.pgm_code = 0x10;
+               rc = 0;
+       }
+#endif
+
        if (rc == -EOPNOTSUPP) {
                /* intercept cannot be handled in-kernel, prepare kvm-run */
                kvm_run->exit_reason         = KVM_EXIT_S390_SIEIC;
@@ -556,6 +634,8 @@ rerun_vcpu:
 
        kvm_run->psw_mask     = vcpu->arch.sie_block->gpsw.mask;
        kvm_run->psw_addr     = vcpu->arch.sie_block->gpsw.addr;
+       kvm_run->s.regs.prefix = vcpu->arch.sie_block->prefix;
+       memcpy(&kvm_run->s.regs.crs, &vcpu->arch.sie_block->gcr, 128);
 
        if (vcpu->sigset_active)
                sigprocmask(SIG_SETMASK, &sigsaved, NULL);
@@ -602,7 +682,7 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
                return -EFAULT;
 
        if (__guestcopy(vcpu, addr + offsetof(struct save_area, gp_regs),
-                       vcpu->arch.guest_gprs, 128, prefix))
+                       vcpu->run->s.regs.gprs, 128, prefix))
                return -EFAULT;
 
        if (__guestcopy(vcpu, addr + offsetof(struct save_area, psw),
@@ -631,7 +711,7 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
                return -EFAULT;
 
        if (__guestcopy(vcpu, addr + offsetof(struct save_area, acc_regs),
-                       &vcpu->arch.guest_acrs, 64, prefix))
+                       &vcpu->run->s.regs.acrs, 64, prefix))
                return -EFAULT;
 
        if (__guestcopy(vcpu,
@@ -673,12 +753,77 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
        case KVM_S390_INITIAL_RESET:
                r = kvm_arch_vcpu_ioctl_initial_reset(vcpu);
                break;
+#ifdef CONFIG_KVM_S390_UCONTROL
+       case KVM_S390_UCAS_MAP: {
+               struct kvm_s390_ucas_mapping ucasmap;
+
+               if (copy_from_user(&ucasmap, argp, sizeof(ucasmap))) {
+                       r = -EFAULT;
+                       break;
+               }
+
+               if (!kvm_is_ucontrol(vcpu->kvm)) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               r = gmap_map_segment(vcpu->arch.gmap, ucasmap.user_addr,
+                                    ucasmap.vcpu_addr, ucasmap.length);
+               break;
+       }
+       case KVM_S390_UCAS_UNMAP: {
+               struct kvm_s390_ucas_mapping ucasmap;
+
+               if (copy_from_user(&ucasmap, argp, sizeof(ucasmap))) {
+                       r = -EFAULT;
+                       break;
+               }
+
+               if (!kvm_is_ucontrol(vcpu->kvm)) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               r = gmap_unmap_segment(vcpu->arch.gmap, ucasmap.vcpu_addr,
+                       ucasmap.length);
+               break;
+       }
+#endif
+       case KVM_S390_VCPU_FAULT: {
+               r = gmap_fault(arg, vcpu->arch.gmap);
+               if (!IS_ERR_VALUE(r))
+                       r = 0;
+               break;
+       }
        default:
-               r = -EINVAL;
+               r = -ENOTTY;
        }
        return r;
 }
 
+int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
+{
+#ifdef CONFIG_KVM_S390_UCONTROL
+       if ((vmf->pgoff == KVM_S390_SIE_PAGE_OFFSET)
+                && (kvm_is_ucontrol(vcpu->kvm))) {
+               vmf->page = virt_to_page(vcpu->arch.sie_block);
+               get_page(vmf->page);
+               return 0;
+       }
+#endif
+       return VM_FAULT_SIGBUS;
+}
+
+void kvm_arch_free_memslot(struct kvm_memory_slot *free,
+                          struct kvm_memory_slot *dont)
+{
+}
+
+int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages)
+{
+       return 0;
+}
+
 /* Section: memory related */
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                   struct kvm_memory_slot *memslot,