|
@@ -16,7 +16,6 @@
|
|
|
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
*/
|
|
|
|
|
|
-#include <linux/cpu.h>
|
|
|
#include <linux/cpu_pm.h>
|
|
|
#include <linux/errno.h>
|
|
|
#include <linux/err.h>
|
|
@@ -66,6 +65,8 @@ static DEFINE_SPINLOCK(kvm_vmid_lock);
|
|
|
|
|
|
static bool vgic_present;
|
|
|
|
|
|
+static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
|
|
|
+
|
|
|
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
|
|
|
{
|
|
|
BUG_ON(preemptible());
|
|
@@ -90,11 +91,6 @@ struct kvm_vcpu * __percpu *kvm_get_running_vcpus(void)
|
|
|
return &kvm_arm_running_vcpu;
|
|
|
}
|
|
|
|
|
|
-int kvm_arch_hardware_enable(void)
|
|
|
-{
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
|
|
|
{
|
|
|
return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
|
|
@@ -1033,11 +1029,6 @@ 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;
|
|
@@ -1065,43 +1056,87 @@ static void cpu_hyp_reinit(void)
|
|
|
{
|
|
|
if (is_kernel_in_hyp_mode()) {
|
|
|
/*
|
|
|
- * cpu_init_stage2() is safe to call even if the PM
|
|
|
+ * __cpu_init_stage2() is safe to call even if the PM
|
|
|
* event was cancelled before the CPU was reset.
|
|
|
*/
|
|
|
- cpu_init_stage2(NULL);
|
|
|
+ __cpu_init_stage2();
|
|
|
} 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)
|
|
|
+static void cpu_hyp_reset(void)
|
|
|
+{
|
|
|
+ phys_addr_t boot_pgd_ptr;
|
|
|
+ phys_addr_t phys_idmap_start;
|
|
|
+
|
|
|
+ if (!is_kernel_in_hyp_mode()) {
|
|
|
+ boot_pgd_ptr = kvm_mmu_get_boot_httbr();
|
|
|
+ phys_idmap_start = kvm_get_idmap_start();
|
|
|
+
|
|
|
+ __cpu_reset_hyp_mode(boot_pgd_ptr, phys_idmap_start);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void _kvm_arch_hardware_enable(void *discard)
|
|
|
{
|
|
|
- switch (action) {
|
|
|
- case CPU_STARTING:
|
|
|
- case CPU_STARTING_FROZEN:
|
|
|
+ if (!__this_cpu_read(kvm_arm_hardware_enabled)) {
|
|
|
cpu_hyp_reinit();
|
|
|
+ __this_cpu_write(kvm_arm_hardware_enabled, 1);
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+int kvm_arch_hardware_enable(void)
|
|
|
+{
|
|
|
+ _kvm_arch_hardware_enable(NULL);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- return NOTIFY_OK;
|
|
|
+static void _kvm_arch_hardware_disable(void *discard)
|
|
|
+{
|
|
|
+ if (__this_cpu_read(kvm_arm_hardware_enabled)) {
|
|
|
+ cpu_hyp_reset();
|
|
|
+ __this_cpu_write(kvm_arm_hardware_enabled, 0);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-static struct notifier_block hyp_init_cpu_nb = {
|
|
|
- .notifier_call = hyp_init_cpu_notify,
|
|
|
-};
|
|
|
+void kvm_arch_hardware_disable(void)
|
|
|
+{
|
|
|
+ _kvm_arch_hardware_disable(NULL);
|
|
|
+}
|
|
|
|
|
|
#ifdef CONFIG_CPU_PM
|
|
|
static int hyp_init_cpu_pm_notifier(struct notifier_block *self,
|
|
|
unsigned long cmd,
|
|
|
void *v)
|
|
|
{
|
|
|
- if (cmd == CPU_PM_EXIT) {
|
|
|
- cpu_hyp_reinit();
|
|
|
+ /*
|
|
|
+ * kvm_arm_hardware_enabled is left with its old value over
|
|
|
+ * PM_ENTER->PM_EXIT. It is used to indicate PM_EXIT should
|
|
|
+ * re-enable hyp.
|
|
|
+ */
|
|
|
+ switch (cmd) {
|
|
|
+ case CPU_PM_ENTER:
|
|
|
+ if (__this_cpu_read(kvm_arm_hardware_enabled))
|
|
|
+ /*
|
|
|
+ * don't update kvm_arm_hardware_enabled here
|
|
|
+ * so that the hardware will be re-enabled
|
|
|
+ * when we resume. See below.
|
|
|
+ */
|
|
|
+ cpu_hyp_reset();
|
|
|
+
|
|
|
+ return NOTIFY_OK;
|
|
|
+ case CPU_PM_EXIT:
|
|
|
+ if (__this_cpu_read(kvm_arm_hardware_enabled))
|
|
|
+ /* The hardware was enabled before suspend. */
|
|
|
+ cpu_hyp_reinit();
|
|
|
+
|
|
|
return NOTIFY_OK;
|
|
|
- }
|
|
|
|
|
|
- return NOTIFY_DONE;
|
|
|
+ default:
|
|
|
+ return NOTIFY_DONE;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static struct notifier_block hyp_init_cpu_pm_nb = {
|
|
@@ -1136,18 +1171,12 @@ static int init_common_resources(void)
|
|
|
|
|
|
static int init_subsystems(void)
|
|
|
{
|
|
|
- int err;
|
|
|
+ int err = 0;
|
|
|
|
|
|
/*
|
|
|
- * Register CPU Hotplug notifier
|
|
|
+ * Enable hardware so that subsystem initialisation can access EL2.
|
|
|
*/
|
|
|
- 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;
|
|
|
- }
|
|
|
+ on_each_cpu(_kvm_arch_hardware_enable, NULL, 1);
|
|
|
|
|
|
/*
|
|
|
* Register CPU lower-power notifier
|
|
@@ -1165,9 +1194,10 @@ static int init_subsystems(void)
|
|
|
case -ENODEV:
|
|
|
case -ENXIO:
|
|
|
vgic_present = false;
|
|
|
+ err = 0;
|
|
|
break;
|
|
|
default:
|
|
|
- return err;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1175,12 +1205,15 @@ static int init_subsystems(void)
|
|
|
*/
|
|
|
err = kvm_timer_hyp_init();
|
|
|
if (err)
|
|
|
- return err;
|
|
|
+ goto out;
|
|
|
|
|
|
kvm_perf_init();
|
|
|
kvm_coproc_table_init();
|
|
|
|
|
|
- return 0;
|
|
|
+out:
|
|
|
+ on_each_cpu(_kvm_arch_hardware_disable, NULL, 1);
|
|
|
+
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static void teardown_hyp_mode(void)
|
|
@@ -1197,11 +1230,6 @@ static void teardown_hyp_mode(void)
|
|
|
|
|
|
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);
|
|
@@ -1288,11 +1316,6 @@ static int init_hyp_mode(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * Execute the init code on each CPU.
|
|
|
- */
|
|
|
- on_each_cpu(cpu_init_hyp_mode, NULL, 1);
|
|
|
-
|
|
|
#ifndef CONFIG_HOTPLUG_CPU
|
|
|
free_boot_hyp_pgd();
|
|
|
#endif
|