diff options
author | AKASHI Takahiro <takahiro.akashi@linaro.org> | 2016-04-27 12:47:05 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2016-04-28 07:05:46 -0400 |
commit | 67f6919766620e7ea7aab11a6a3470dc7b451359 (patch) | |
tree | 603439d01bc9ecb8e8d7f0abda6b5a1bed2394e4 /arch/arm/kvm | |
parent | c94b0cf28281d483c8b43b4874fcb7ab14ade1b1 (diff) |
arm64: kvm: allows kvm cpu hotplug
The current kvm implementation on arm64 does cpu-specific initialization
at system boot, and has no way to gracefully shutdown a core in terms of
kvm. This prevents kexec from rebooting the system at EL2.
This patch adds a cpu tear-down function and also puts an existing cpu-init
code into a separate function, kvm_arch_hardware_disable() and
kvm_arch_hardware_enable() respectively.
We don't need the arm64 specific cpu hotplug hook any more.
Since this patch modifies common code between arm and arm64, one stub
definition, __cpu_reset_hyp_mode(), is added on arm side to avoid
compilation errors.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
[Rebase, added separate VHE init/exit path, changed resets use of
kvm_call_hyp() to the __version, en/disabled hardware in init_subsystems(),
added icache maintenance to __kvm_hyp_reset() and removed lr restore, removed
guest-enter after teardown handling]
Signed-off-by: James Morse <james.morse@arm.com>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm/kvm')
-rw-r--r-- | arch/arm/kvm/arm.c | 119 | ||||
-rw-r--r-- | arch/arm/kvm/mmu.c | 5 |
2 files changed, 76 insertions, 48 deletions
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index b5384311dec4..1687e1450c3a 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c | |||
@@ -16,7 +16,6 @@ | |||
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/cpu.h> | ||
20 | #include <linux/cpu_pm.h> | 19 | #include <linux/cpu_pm.h> |
21 | #include <linux/errno.h> | 20 | #include <linux/errno.h> |
22 | #include <linux/err.h> | 21 | #include <linux/err.h> |
@@ -66,6 +65,8 @@ static DEFINE_SPINLOCK(kvm_vmid_lock); | |||
66 | 65 | ||
67 | static bool vgic_present; | 66 | static bool vgic_present; |
68 | 67 | ||
68 | static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled); | ||
69 | |||
69 | static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu) | 70 | static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu) |
70 | { | 71 | { |
71 | BUG_ON(preemptible()); | 72 | BUG_ON(preemptible()); |
@@ -90,11 +91,6 @@ struct kvm_vcpu * __percpu *kvm_get_running_vcpus(void) | |||
90 | return &kvm_arm_running_vcpu; | 91 | return &kvm_arm_running_vcpu; |
91 | } | 92 | } |
92 | 93 | ||
93 | int kvm_arch_hardware_enable(void) | ||
94 | { | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) | 94 | int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) |
99 | { | 95 | { |
100 | return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; | 96 | return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; |
@@ -1033,11 +1029,6 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
1033 | } | 1029 | } |
1034 | } | 1030 | } |
1035 | 1031 | ||
1036 | static void cpu_init_stage2(void *dummy) | ||
1037 | { | ||
1038 | __cpu_init_stage2(); | ||
1039 | } | ||
1040 | |||
1041 | static void cpu_init_hyp_mode(void *dummy) | 1032 | static void cpu_init_hyp_mode(void *dummy) |
1042 | { | 1033 | { |
1043 | phys_addr_t boot_pgd_ptr; | 1034 | phys_addr_t boot_pgd_ptr; |
@@ -1065,43 +1056,87 @@ static void cpu_hyp_reinit(void) | |||
1065 | { | 1056 | { |
1066 | if (is_kernel_in_hyp_mode()) { | 1057 | if (is_kernel_in_hyp_mode()) { |
1067 | /* | 1058 | /* |
1068 | * cpu_init_stage2() is safe to call even if the PM | 1059 | * __cpu_init_stage2() is safe to call even if the PM |
1069 | * event was cancelled before the CPU was reset. | 1060 | * event was cancelled before the CPU was reset. |
1070 | */ | 1061 | */ |
1071 | cpu_init_stage2(NULL); | 1062 | __cpu_init_stage2(); |
1072 | } else { | 1063 | } else { |
1073 | if (__hyp_get_vectors() == hyp_default_vectors) | 1064 | if (__hyp_get_vectors() == hyp_default_vectors) |
1074 | cpu_init_hyp_mode(NULL); | 1065 | cpu_init_hyp_mode(NULL); |
1075 | } | 1066 | } |
1076 | } | 1067 | } |
1077 | 1068 | ||
1078 | static int hyp_init_cpu_notify(struct notifier_block *self, | 1069 | static void cpu_hyp_reset(void) |
1079 | unsigned long action, void *cpu) | 1070 | { |
1071 | phys_addr_t boot_pgd_ptr; | ||
1072 | phys_addr_t phys_idmap_start; | ||
1073 | |||
1074 | if (!is_kernel_in_hyp_mode()) { | ||
1075 | boot_pgd_ptr = kvm_mmu_get_boot_httbr(); | ||
1076 | phys_idmap_start = kvm_get_idmap_start(); | ||
1077 | |||
1078 | __cpu_reset_hyp_mode(boot_pgd_ptr, phys_idmap_start); | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | static void _kvm_arch_hardware_enable(void *discard) | ||
1080 | { | 1083 | { |
1081 | switch (action) { | 1084 | if (!__this_cpu_read(kvm_arm_hardware_enabled)) { |
1082 | case CPU_STARTING: | ||
1083 | case CPU_STARTING_FROZEN: | ||
1084 | cpu_hyp_reinit(); | 1085 | cpu_hyp_reinit(); |
1086 | __this_cpu_write(kvm_arm_hardware_enabled, 1); | ||
1085 | } | 1087 | } |
1088 | } | ||
1089 | |||
1090 | int kvm_arch_hardware_enable(void) | ||
1091 | { | ||
1092 | _kvm_arch_hardware_enable(NULL); | ||
1093 | return 0; | ||
1094 | } | ||
1086 | 1095 | ||
1087 | return NOTIFY_OK; | 1096 | static void _kvm_arch_hardware_disable(void *discard) |
1097 | { | ||
1098 | if (__this_cpu_read(kvm_arm_hardware_enabled)) { | ||
1099 | cpu_hyp_reset(); | ||
1100 | __this_cpu_write(kvm_arm_hardware_enabled, 0); | ||
1101 | } | ||
1088 | } | 1102 | } |
1089 | 1103 | ||
1090 | static struct notifier_block hyp_init_cpu_nb = { | 1104 | void kvm_arch_hardware_disable(void) |
1091 | .notifier_call = hyp_init_cpu_notify, | 1105 | { |
1092 | }; | 1106 | _kvm_arch_hardware_disable(NULL); |
1107 | } | ||
1093 | 1108 | ||
1094 | #ifdef CONFIG_CPU_PM | 1109 | #ifdef CONFIG_CPU_PM |
1095 | static int hyp_init_cpu_pm_notifier(struct notifier_block *self, | 1110 | static int hyp_init_cpu_pm_notifier(struct notifier_block *self, |
1096 | unsigned long cmd, | 1111 | unsigned long cmd, |
1097 | void *v) | 1112 | void *v) |
1098 | { | 1113 | { |
1099 | if (cmd == CPU_PM_EXIT) { | 1114 | /* |
1100 | cpu_hyp_reinit(); | 1115 | * kvm_arm_hardware_enabled is left with its old value over |
1116 | * PM_ENTER->PM_EXIT. It is used to indicate PM_EXIT should | ||
1117 | * re-enable hyp. | ||
1118 | */ | ||
1119 | switch (cmd) { | ||
1120 | case CPU_PM_ENTER: | ||
1121 | if (__this_cpu_read(kvm_arm_hardware_enabled)) | ||
1122 | /* | ||
1123 | * don't update kvm_arm_hardware_enabled here | ||
1124 | * so that the hardware will be re-enabled | ||
1125 | * when we resume. See below. | ||
1126 | */ | ||
1127 | cpu_hyp_reset(); | ||
1128 | |||
1129 | return NOTIFY_OK; | ||
1130 | case CPU_PM_EXIT: | ||
1131 | if (__this_cpu_read(kvm_arm_hardware_enabled)) | ||
1132 | /* The hardware was enabled before suspend. */ | ||
1133 | cpu_hyp_reinit(); | ||
1134 | |||
1101 | return NOTIFY_OK; | 1135 | return NOTIFY_OK; |
1102 | } | ||
1103 | 1136 | ||
1104 | return NOTIFY_DONE; | 1137 | default: |
1138 | return NOTIFY_DONE; | ||
1139 | } | ||
1105 | } | 1140 | } |
1106 | 1141 | ||
1107 | static struct notifier_block hyp_init_cpu_pm_nb = { | 1142 | static struct notifier_block hyp_init_cpu_pm_nb = { |
@@ -1136,18 +1171,12 @@ static int init_common_resources(void) | |||
1136 | 1171 | ||
1137 | static int init_subsystems(void) | 1172 | static int init_subsystems(void) |
1138 | { | 1173 | { |
1139 | int err; | 1174 | int err = 0; |
1140 | 1175 | ||
1141 | /* | 1176 | /* |
1142 | * Register CPU Hotplug notifier | 1177 | * Enable hardware so that subsystem initialisation can access EL2. |
1143 | */ | 1178 | */ |
1144 | cpu_notifier_register_begin(); | 1179 | on_each_cpu(_kvm_arch_hardware_enable, NULL, 1); |
1145 | err = __register_cpu_notifier(&hyp_init_cpu_nb); | ||
1146 | cpu_notifier_register_done(); | ||
1147 | if (err) { | ||
1148 | kvm_err("Cannot register KVM init CPU notifier (%d)\n", err); | ||
1149 | return err; | ||
1150 | } | ||
1151 | 1180 | ||
1152 | /* | 1181 | /* |
1153 | * Register CPU lower-power notifier | 1182 | * Register CPU lower-power notifier |
@@ -1165,9 +1194,10 @@ static int init_subsystems(void) | |||
1165 | case -ENODEV: | 1194 | case -ENODEV: |
1166 | case -ENXIO: | 1195 | case -ENXIO: |
1167 | vgic_present = false; | 1196 | vgic_present = false; |
1197 | err = 0; | ||
1168 | break; | 1198 | break; |
1169 | default: | 1199 | default: |
1170 | return err; | 1200 | goto out; |
1171 | } | 1201 | } |
1172 | 1202 | ||
1173 | /* | 1203 | /* |
@@ -1175,12 +1205,15 @@ static int init_subsystems(void) | |||
1175 | */ | 1205 | */ |
1176 | err = kvm_timer_hyp_init(); | 1206 | err = kvm_timer_hyp_init(); |
1177 | if (err) | 1207 | if (err) |
1178 | return err; | 1208 | goto out; |
1179 | 1209 | ||
1180 | kvm_perf_init(); | 1210 | kvm_perf_init(); |
1181 | kvm_coproc_table_init(); | 1211 | kvm_coproc_table_init(); |
1182 | 1212 | ||
1183 | return 0; | 1213 | out: |
1214 | on_each_cpu(_kvm_arch_hardware_disable, NULL, 1); | ||
1215 | |||
1216 | return err; | ||
1184 | } | 1217 | } |
1185 | 1218 | ||
1186 | static void teardown_hyp_mode(void) | 1219 | static void teardown_hyp_mode(void) |
@@ -1197,11 +1230,6 @@ static void teardown_hyp_mode(void) | |||
1197 | 1230 | ||
1198 | static int init_vhe_mode(void) | 1231 | static int init_vhe_mode(void) |
1199 | { | 1232 | { |
1200 | /* | ||
1201 | * Execute the init code on each CPU. | ||
1202 | */ | ||
1203 | on_each_cpu(cpu_init_stage2, NULL, 1); | ||
1204 | |||
1205 | /* set size of VMID supported by CPU */ | 1233 | /* set size of VMID supported by CPU */ |
1206 | kvm_vmid_bits = kvm_get_vmid_bits(); | 1234 | kvm_vmid_bits = kvm_get_vmid_bits(); |
1207 | kvm_info("%d-bit VMID\n", kvm_vmid_bits); | 1235 | kvm_info("%d-bit VMID\n", kvm_vmid_bits); |
@@ -1288,11 +1316,6 @@ static int init_hyp_mode(void) | |||
1288 | } | 1316 | } |
1289 | } | 1317 | } |
1290 | 1318 | ||
1291 | /* | ||
1292 | * Execute the init code on each CPU. | ||
1293 | */ | ||
1294 | on_each_cpu(cpu_init_hyp_mode, NULL, 1); | ||
1295 | |||
1296 | #ifndef CONFIG_HOTPLUG_CPU | 1319 | #ifndef CONFIG_HOTPLUG_CPU |
1297 | free_boot_hyp_pgd(); | 1320 | free_boot_hyp_pgd(); |
1298 | #endif | 1321 | #endif |
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 58dbd5c439df..7d86e15b5d85 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
@@ -1666,6 +1666,11 @@ phys_addr_t kvm_get_idmap_vector(void) | |||
1666 | return hyp_idmap_vector; | 1666 | return hyp_idmap_vector; |
1667 | } | 1667 | } |
1668 | 1668 | ||
1669 | phys_addr_t kvm_get_idmap_start(void) | ||
1670 | { | ||
1671 | return hyp_idmap_start; | ||
1672 | } | ||
1673 | |||
1669 | int kvm_mmu_init(void) | 1674 | int kvm_mmu_init(void) |
1670 | { | 1675 | { |
1671 | int err; | 1676 | int err; |