Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[cascardo/linux.git] / arch / arm / kvm / arm.c
index dda1959..b538431 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/sched.h>
 #include <linux/kvm.h>
 #include <trace/events/kvm.h>
+#include <kvm/arm_pmu.h>
 
 #define CREATE_TRACE_POINTS
 #include "trace.h"
@@ -265,6 +266,7 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
        kvm_mmu_free_memory_caches(vcpu);
        kvm_timer_vcpu_terminate(vcpu);
        kvm_vgic_vcpu_destroy(vcpu);
+       kvm_pmu_vcpu_destroy(vcpu);
        kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
@@ -320,6 +322,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
        vcpu->cpu = -1;
 
        kvm_arm_set_running_vcpu(NULL);
+       kvm_timer_vcpu_put(vcpu);
 }
 
 int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
@@ -370,7 +373,9 @@ static void exit_vm_noop(void *info)
 
 void force_vm_exit(const cpumask_t *mask)
 {
+       preempt_disable();
        smp_call_function_many(mask, exit_vm_noop, NULL, true);
+       preempt_enable();
 }
 
 /**
@@ -506,18 +511,18 @@ static void kvm_arm_resume_guest(struct kvm *kvm)
        struct kvm_vcpu *vcpu;
 
        kvm_for_each_vcpu(i, vcpu, kvm) {
-               wait_queue_head_t *wq = kvm_arch_vcpu_wq(vcpu);
+               struct swait_queue_head *wq = kvm_arch_vcpu_wq(vcpu);
 
                vcpu->arch.pause = false;
-               wake_up_interruptible(wq);
+               swake_up(wq);
        }
 }
 
 static void vcpu_sleep(struct kvm_vcpu *vcpu)
 {
-       wait_queue_head_t *wq = kvm_arch_vcpu_wq(vcpu);
+       struct swait_queue_head *wq = kvm_arch_vcpu_wq(vcpu);
 
-       wait_event_interruptible(*wq, ((!vcpu->arch.power_off) &&
+       swait_event_interruptible(*wq, ((!vcpu->arch.power_off) &&
                                       (!vcpu->arch.pause)));
 }
 
@@ -577,6 +582,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
                 * non-preemptible context.
                 */
                preempt_disable();
+               kvm_pmu_flush_hwstate(vcpu);
                kvm_timer_flush_hwstate(vcpu);
                kvm_vgic_flush_hwstate(vcpu);
 
@@ -593,6 +599,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
                if (ret <= 0 || need_new_vmid_gen(vcpu->kvm) ||
                        vcpu->arch.power_off || vcpu->arch.pause) {
                        local_irq_enable();
+                       kvm_pmu_sync_hwstate(vcpu);
                        kvm_timer_sync_hwstate(vcpu);
                        kvm_vgic_sync_hwstate(vcpu);
                        preempt_enable();
@@ -642,10 +649,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
                trace_kvm_exit(ret, kvm_vcpu_trap_get_class(vcpu), *vcpu_pc(vcpu));
 
                /*
-                * We must sync the timer state before the vgic state so that
-                * the vgic can properly sample the updated state of the
+                * We must sync the PMU and timer state before the vgic state so
+                * that the vgic can properly sample the updated state of the
                 * interrupt line.
                 */
+               kvm_pmu_sync_hwstate(vcpu);
                kvm_timer_sync_hwstate(vcpu);
 
                kvm_vgic_sync_hwstate(vcpu);
@@ -823,11 +831,54 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
        return 0;
 }
 
+static int kvm_arm_vcpu_set_attr(struct kvm_vcpu *vcpu,
+                                struct kvm_device_attr *attr)
+{
+       int ret = -ENXIO;
+
+       switch (attr->group) {
+       default:
+               ret = kvm_arm_vcpu_arch_set_attr(vcpu, attr);
+               break;
+       }
+
+       return ret;
+}
+
+static int kvm_arm_vcpu_get_attr(struct kvm_vcpu *vcpu,
+                                struct kvm_device_attr *attr)
+{
+       int ret = -ENXIO;
+
+       switch (attr->group) {
+       default:
+               ret = kvm_arm_vcpu_arch_get_attr(vcpu, attr);
+               break;
+       }
+
+       return ret;
+}
+
+static int kvm_arm_vcpu_has_attr(struct kvm_vcpu *vcpu,
+                                struct kvm_device_attr *attr)
+{
+       int ret = -ENXIO;
+
+       switch (attr->group) {
+       default:
+               ret = kvm_arm_vcpu_arch_has_attr(vcpu, attr);
+               break;
+       }
+
+       return ret;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
                         unsigned int ioctl, unsigned long arg)
 {
        struct kvm_vcpu *vcpu = filp->private_data;
        void __user *argp = (void __user *)arg;
+       struct kvm_device_attr attr;
 
        switch (ioctl) {
        case KVM_ARM_VCPU_INIT: {
@@ -870,6 +921,21 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                        return -E2BIG;
                return kvm_arm_copy_reg_indices(vcpu, user_list->reg);
        }
+       case KVM_SET_DEVICE_ATTR: {
+               if (copy_from_user(&attr, argp, sizeof(attr)))
+                       return -EFAULT;
+               return kvm_arm_vcpu_set_attr(vcpu, &attr);
+       }
+       case KVM_GET_DEVICE_ATTR: {
+               if (copy_from_user(&attr, argp, sizeof(attr)))
+                       return -EFAULT;
+               return kvm_arm_vcpu_get_attr(vcpu, &attr);
+       }
+       case KVM_HAS_DEVICE_ATTR: {
+               if (copy_from_user(&attr, argp, sizeof(attr)))
+                       return -EFAULT;
+               return kvm_arm_vcpu_has_attr(vcpu, &attr);
+       }
        default:
                return -EINVAL;
        }
@@ -967,6 +1033,11 @@ long kvm_arch_vm_ioctl(struct file *filp,
        }
 }
 
+static void cpu_init_stage2(void *dummy)
+{
+       __cpu_init_stage2();
+}
+
 static void cpu_init_hyp_mode(void *dummy)
 {
        phys_addr_t boot_pgd_ptr;
@@ -982,22 +1053,35 @@ static void cpu_init_hyp_mode(void *dummy)
        pgd_ptr = kvm_mmu_get_httbr();
        stack_page = __this_cpu_read(kvm_arm_hyp_stack_page);
        hyp_stack_ptr = stack_page + PAGE_SIZE;
-       vector_ptr = (unsigned long)__kvm_hyp_vector;
+       vector_ptr = (unsigned long)kvm_ksym_ref(__kvm_hyp_vector);
 
        __cpu_init_hyp_mode(boot_pgd_ptr, pgd_ptr, hyp_stack_ptr, vector_ptr);
+       __cpu_init_stage2();
 
        kvm_arm_init_debug();
 }
 
+static void cpu_hyp_reinit(void)
+{
+       if (is_kernel_in_hyp_mode()) {
+               /*
+                * cpu_init_stage2() is safe to call even if the PM
+                * event was cancelled before the CPU was reset.
+                */
+               cpu_init_stage2(NULL);
+       } else {
+               if (__hyp_get_vectors() == hyp_default_vectors)
+                       cpu_init_hyp_mode(NULL);
+       }
+}
+
 static int hyp_init_cpu_notify(struct notifier_block *self,
                               unsigned long action, void *cpu)
 {
        switch (action) {
        case CPU_STARTING:
        case CPU_STARTING_FROZEN:
-               if (__hyp_get_vectors() == hyp_default_vectors)
-                       cpu_init_hyp_mode(NULL);
-               break;
+               cpu_hyp_reinit();
        }
 
        return NOTIFY_OK;
@@ -1012,9 +1096,8 @@ static int hyp_init_cpu_pm_notifier(struct notifier_block *self,
                                    unsigned long cmd,
                                    void *v)
 {
-       if (cmd == CPU_PM_EXIT &&
-           __hyp_get_vectors() == hyp_default_vectors) {
-               cpu_init_hyp_mode(NULL);
+       if (cmd == CPU_PM_EXIT) {
+               cpu_hyp_reinit();
                return NOTIFY_OK;
        }
 
@@ -1035,6 +1118,98 @@ static inline void hyp_cpu_pm_init(void)
 }
 #endif
 
+static void teardown_common_resources(void)
+{
+       free_percpu(kvm_host_cpu_state);
+}
+
+static int init_common_resources(void)
+{
+       kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
+       if (!kvm_host_cpu_state) {
+               kvm_err("Cannot allocate host CPU state\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int init_subsystems(void)
+{
+       int err;
+
+       /*
+        * Register CPU Hotplug notifier
+        */
+       cpu_notifier_register_begin();
+       err = __register_cpu_notifier(&hyp_init_cpu_nb);
+       cpu_notifier_register_done();
+       if (err) {
+               kvm_err("Cannot register KVM init CPU notifier (%d)\n", err);
+               return err;
+       }
+
+       /*
+        * Register CPU lower-power notifier
+        */
+       hyp_cpu_pm_init();
+
+       /*
+        * Init HYP view of VGIC
+        */
+       err = kvm_vgic_hyp_init();
+       switch (err) {
+       case 0:
+               vgic_present = true;
+               break;
+       case -ENODEV:
+       case -ENXIO:
+               vgic_present = false;
+               break;
+       default:
+               return err;
+       }
+
+       /*
+        * Init HYP architected timer support
+        */
+       err = kvm_timer_hyp_init();
+       if (err)
+               return err;
+
+       kvm_perf_init();
+       kvm_coproc_table_init();
+
+       return 0;
+}
+
+static void teardown_hyp_mode(void)
+{
+       int cpu;
+
+       if (is_kernel_in_hyp_mode())
+               return;
+
+       free_hyp_pgds();
+       for_each_possible_cpu(cpu)
+               free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));
+}
+
+static int init_vhe_mode(void)
+{
+       /*
+        * Execute the init code on each CPU.
+        */
+       on_each_cpu(cpu_init_stage2, NULL, 1);
+
+       /* set size of VMID supported by CPU */
+       kvm_vmid_bits = kvm_get_vmid_bits();
+       kvm_info("%d-bit VMID\n", kvm_vmid_bits);
+
+       kvm_info("VHE mode initialized successfully\n");
+       return 0;
+}
+
 /**
  * Inits Hyp-mode on all online CPUs
  */
@@ -1065,7 +1240,7 @@ static int init_hyp_mode(void)
                stack_page = __get_free_page(GFP_KERNEL);
                if (!stack_page) {
                        err = -ENOMEM;
-                       goto out_free_stack_pages;
+                       goto out_err;
                }
 
                per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
@@ -1074,16 +1249,18 @@ static int init_hyp_mode(void)
        /*
         * Map the Hyp-code called directly from the host
         */
-       err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end);
+       err = create_hyp_mappings(kvm_ksym_ref(__hyp_text_start),
+                                 kvm_ksym_ref(__hyp_text_end));
        if (err) {
                kvm_err("Cannot map world-switch code\n");
-               goto out_free_mappings;
+               goto out_err;
        }
 
-       err = create_hyp_mappings(__start_rodata, __end_rodata);
+       err = create_hyp_mappings(kvm_ksym_ref(__start_rodata),
+                                 kvm_ksym_ref(__end_rodata));
        if (err) {
                kvm_err("Cannot map rodata section\n");
-               goto out_free_mappings;
+               goto out_err;
        }
 
        /*
@@ -1095,20 +1272,10 @@ static int init_hyp_mode(void)
 
                if (err) {
                        kvm_err("Cannot map hyp stack\n");
-                       goto out_free_mappings;
+                       goto out_err;
                }
        }
 
-       /*
-        * Map the host CPU structures
-        */
-       kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
-       if (!kvm_host_cpu_state) {
-               err = -ENOMEM;
-               kvm_err("Cannot allocate host CPU state\n");
-               goto out_free_mappings;
-       }
-
        for_each_possible_cpu(cpu) {
                kvm_cpu_context_t *cpu_ctxt;
 
@@ -1117,7 +1284,7 @@ static int init_hyp_mode(void)
 
                if (err) {
                        kvm_err("Cannot map host CPU state: %d\n", err);
-                       goto out_free_context;
+                       goto out_err;
                }
        }
 
@@ -1126,35 +1293,10 @@ static int init_hyp_mode(void)
         */
        on_each_cpu(cpu_init_hyp_mode, NULL, 1);
 
-       /*
-        * Init HYP view of VGIC
-        */
-       err = kvm_vgic_hyp_init();
-       switch (err) {
-       case 0:
-               vgic_present = true;
-               break;
-       case -ENODEV:
-       case -ENXIO:
-               vgic_present = false;
-               break;
-       default:
-               goto out_free_context;
-       }
-
-       /*
-        * Init HYP architected timer support
-        */
-       err = kvm_timer_hyp_init();
-       if (err)
-               goto out_free_context;
-
 #ifndef CONFIG_HOTPLUG_CPU
        free_boot_hyp_pgd();
 #endif
 
-       kvm_perf_init();
-
        /* set size of VMID supported by CPU */
        kvm_vmid_bits = kvm_get_vmid_bits();
        kvm_info("%d-bit VMID\n", kvm_vmid_bits);
@@ -1162,14 +1304,9 @@ static int init_hyp_mode(void)
        kvm_info("Hyp mode initialized successfully\n");
 
        return 0;
-out_free_context:
-       free_percpu(kvm_host_cpu_state);
-out_free_mappings:
-       free_hyp_pgds();
-out_free_stack_pages:
-       for_each_possible_cpu(cpu)
-               free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));
+
 out_err:
+       teardown_hyp_mode();
        kvm_err("error initializing Hyp mode: %d\n", err);
        return err;
 }
@@ -1213,26 +1350,27 @@ int kvm_arch_init(void *opaque)
                }
        }
 
-       cpu_notifier_register_begin();
-
-       err = init_hyp_mode();
+       err = init_common_resources();
        if (err)
-               goto out_err;
+               return err;
 
-       err = __register_cpu_notifier(&hyp_init_cpu_nb);
-       if (err) {
-               kvm_err("Cannot register HYP init CPU notifier (%d)\n", err);
+       if (is_kernel_in_hyp_mode())
+               err = init_vhe_mode();
+       else
+               err = init_hyp_mode();
+       if (err)
                goto out_err;
-       }
-
-       cpu_notifier_register_done();
 
-       hyp_cpu_pm_init();
+       err = init_subsystems();
+       if (err)
+               goto out_hyp;
 
-       kvm_coproc_table_init();
        return 0;
+
+out_hyp:
+       teardown_hyp_mode();
 out_err:
-       cpu_notifier_register_done();
+       teardown_common_resources();
        return err;
 }