Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

AKASHI Takahiro and committed by
Will Deacon
67f69197 c94b0cf2

+155 -53
+9 -1
arch/arm/include/asm/kvm_host.h
··· 265 265 kvm_call_hyp(__init_stage2_translation); 266 266 } 267 267 268 + static inline void __cpu_reset_hyp_mode(phys_addr_t boot_pgd_ptr, 269 + phys_addr_t phys_idmap_start) 270 + { 271 + /* 272 + * TODO 273 + * kvm_call_reset(boot_pgd_ptr, phys_idmap_start); 274 + */ 275 + } 276 + 268 277 static inline int kvm_arch_dev_ioctl_check_extension(long ext) 269 278 { 270 279 return 0; ··· 286 277 287 278 struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr); 288 279 289 - static inline void kvm_arch_hardware_disable(void) {} 290 280 static inline void kvm_arch_hardware_unsetup(void) {} 291 281 static inline void kvm_arch_sync_events(struct kvm *kvm) {} 292 282 static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
+1
arch/arm/include/asm/kvm_mmu.h
··· 66 66 phys_addr_t kvm_mmu_get_httbr(void); 67 67 phys_addr_t kvm_mmu_get_boot_httbr(void); 68 68 phys_addr_t kvm_get_idmap_vector(void); 69 + phys_addr_t kvm_get_idmap_start(void); 69 70 int kvm_mmu_init(void); 70 71 void kvm_clear_hyp_idmap(void); 71 72
+74 -51
arch/arm/kvm/arm.c
··· 16 16 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 17 */ 18 18 19 - #include <linux/cpu.h> 20 19 #include <linux/cpu_pm.h> 21 20 #include <linux/errno.h> 22 21 #include <linux/err.h> ··· 65 66 66 67 static bool vgic_present; 67 68 69 + static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled); 70 + 68 71 static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu) 69 72 { 70 73 BUG_ON(preemptible()); ··· 89 88 struct kvm_vcpu * __percpu *kvm_get_running_vcpus(void) 90 89 { 91 90 return &kvm_arm_running_vcpu; 92 - } 93 - 94 - int kvm_arch_hardware_enable(void) 95 - { 96 - return 0; 97 91 } 98 92 99 93 int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) ··· 1029 1033 } 1030 1034 } 1031 1035 1032 - static void cpu_init_stage2(void *dummy) 1033 - { 1034 - __cpu_init_stage2(); 1035 - } 1036 - 1037 1036 static void cpu_init_hyp_mode(void *dummy) 1038 1037 { 1039 1038 phys_addr_t boot_pgd_ptr; ··· 1056 1065 { 1057 1066 if (is_kernel_in_hyp_mode()) { 1058 1067 /* 1059 - * cpu_init_stage2() is safe to call even if the PM 1068 + * __cpu_init_stage2() is safe to call even if the PM 1060 1069 * event was cancelled before the CPU was reset. 1061 1070 */ 1062 - cpu_init_stage2(NULL); 1071 + __cpu_init_stage2(); 1063 1072 } else { 1064 1073 if (__hyp_get_vectors() == hyp_default_vectors) 1065 1074 cpu_init_hyp_mode(NULL); 1066 1075 } 1067 1076 } 1068 1077 1069 - static int hyp_init_cpu_notify(struct notifier_block *self, 1070 - unsigned long action, void *cpu) 1078 + static void cpu_hyp_reset(void) 1071 1079 { 1072 - switch (action) { 1073 - case CPU_STARTING: 1074 - case CPU_STARTING_FROZEN: 1075 - cpu_hyp_reinit(); 1076 - } 1080 + phys_addr_t boot_pgd_ptr; 1081 + phys_addr_t phys_idmap_start; 1077 1082 1078 - return NOTIFY_OK; 1083 + if (!is_kernel_in_hyp_mode()) { 1084 + boot_pgd_ptr = kvm_mmu_get_boot_httbr(); 1085 + phys_idmap_start = kvm_get_idmap_start(); 1086 + 1087 + __cpu_reset_hyp_mode(boot_pgd_ptr, phys_idmap_start); 1088 + } 1079 1089 } 1080 1090 1081 - static struct notifier_block hyp_init_cpu_nb = { 1082 - .notifier_call = hyp_init_cpu_notify, 1083 - }; 1091 + static void _kvm_arch_hardware_enable(void *discard) 1092 + { 1093 + if (!__this_cpu_read(kvm_arm_hardware_enabled)) { 1094 + cpu_hyp_reinit(); 1095 + __this_cpu_write(kvm_arm_hardware_enabled, 1); 1096 + } 1097 + } 1098 + 1099 + int kvm_arch_hardware_enable(void) 1100 + { 1101 + _kvm_arch_hardware_enable(NULL); 1102 + return 0; 1103 + } 1104 + 1105 + static void _kvm_arch_hardware_disable(void *discard) 1106 + { 1107 + if (__this_cpu_read(kvm_arm_hardware_enabled)) { 1108 + cpu_hyp_reset(); 1109 + __this_cpu_write(kvm_arm_hardware_enabled, 0); 1110 + } 1111 + } 1112 + 1113 + void kvm_arch_hardware_disable(void) 1114 + { 1115 + _kvm_arch_hardware_disable(NULL); 1116 + } 1084 1117 1085 1118 #ifdef CONFIG_CPU_PM 1086 1119 static int hyp_init_cpu_pm_notifier(struct notifier_block *self, 1087 1120 unsigned long cmd, 1088 1121 void *v) 1089 1122 { 1090 - if (cmd == CPU_PM_EXIT) { 1091 - cpu_hyp_reinit(); 1092 - return NOTIFY_OK; 1093 - } 1123 + /* 1124 + * kvm_arm_hardware_enabled is left with its old value over 1125 + * PM_ENTER->PM_EXIT. It is used to indicate PM_EXIT should 1126 + * re-enable hyp. 1127 + */ 1128 + switch (cmd) { 1129 + case CPU_PM_ENTER: 1130 + if (__this_cpu_read(kvm_arm_hardware_enabled)) 1131 + /* 1132 + * don't update kvm_arm_hardware_enabled here 1133 + * so that the hardware will be re-enabled 1134 + * when we resume. See below. 1135 + */ 1136 + cpu_hyp_reset(); 1094 1137 1095 - return NOTIFY_DONE; 1138 + return NOTIFY_OK; 1139 + case CPU_PM_EXIT: 1140 + if (__this_cpu_read(kvm_arm_hardware_enabled)) 1141 + /* The hardware was enabled before suspend. */ 1142 + cpu_hyp_reinit(); 1143 + 1144 + return NOTIFY_OK; 1145 + 1146 + default: 1147 + return NOTIFY_DONE; 1148 + } 1096 1149 } 1097 1150 1098 1151 static struct notifier_block hyp_init_cpu_pm_nb = { ··· 1171 1136 1172 1137 static int init_subsystems(void) 1173 1138 { 1174 - int err; 1139 + int err = 0; 1175 1140 1176 1141 /* 1177 - * Register CPU Hotplug notifier 1142 + * Enable hardware so that subsystem initialisation can access EL2. 1178 1143 */ 1179 - cpu_notifier_register_begin(); 1180 - err = __register_cpu_notifier(&hyp_init_cpu_nb); 1181 - cpu_notifier_register_done(); 1182 - if (err) { 1183 - kvm_err("Cannot register KVM init CPU notifier (%d)\n", err); 1184 - return err; 1185 - } 1144 + on_each_cpu(_kvm_arch_hardware_enable, NULL, 1); 1186 1145 1187 1146 /* 1188 1147 * Register CPU lower-power notifier ··· 1194 1165 case -ENODEV: 1195 1166 case -ENXIO: 1196 1167 vgic_present = false; 1168 + err = 0; 1197 1169 break; 1198 1170 default: 1199 - return err; 1171 + goto out; 1200 1172 } 1201 1173 1202 1174 /* ··· 1205 1175 */ 1206 1176 err = kvm_timer_hyp_init(); 1207 1177 if (err) 1208 - return err; 1178 + goto out; 1209 1179 1210 1180 kvm_perf_init(); 1211 1181 kvm_coproc_table_init(); 1212 1182 1213 - return 0; 1183 + out: 1184 + on_each_cpu(_kvm_arch_hardware_disable, NULL, 1); 1185 + 1186 + return err; 1214 1187 } 1215 1188 1216 1189 static void teardown_hyp_mode(void) ··· 1230 1197 1231 1198 static int init_vhe_mode(void) 1232 1199 { 1233 - /* 1234 - * Execute the init code on each CPU. 1235 - */ 1236 - on_each_cpu(cpu_init_stage2, NULL, 1); 1237 - 1238 1200 /* set size of VMID supported by CPU */ 1239 1201 kvm_vmid_bits = kvm_get_vmid_bits(); 1240 1202 kvm_info("%d-bit VMID\n", kvm_vmid_bits); ··· 1315 1287 goto out_err; 1316 1288 } 1317 1289 } 1318 - 1319 - /* 1320 - * Execute the init code on each CPU. 1321 - */ 1322 - on_each_cpu(cpu_init_hyp_mode, NULL, 1); 1323 1290 1324 1291 #ifndef CONFIG_HOTPLUG_CPU 1325 1292 free_boot_hyp_pgd();
+5
arch/arm/kvm/mmu.c
··· 1666 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 1674 int kvm_mmu_init(void) 1670 1675 { 1671 1676 int err;
+1
arch/arm64/include/asm/kvm_asm.h
··· 42 42 43 43 extern char __kvm_hyp_init[]; 44 44 extern char __kvm_hyp_init_end[]; 45 + extern char __kvm_hyp_reset[]; 45 46 46 47 extern char __kvm_hyp_vector[]; 47 48
+12 -1
arch/arm64/include/asm/kvm_host.h
··· 46 46 int __attribute_const__ kvm_target_cpu(void); 47 47 int kvm_reset_vcpu(struct kvm_vcpu *vcpu); 48 48 int kvm_arch_dev_ioctl_check_extension(long ext); 49 + phys_addr_t kvm_hyp_reset_entry(void); 49 50 50 51 struct kvm_arch { 51 52 /* The VMID generation used for the virt. memory system */ ··· 353 352 hyp_stack_ptr, vector_ptr); 354 353 } 355 354 356 - static inline void kvm_arch_hardware_disable(void) {} 355 + static inline void __cpu_reset_hyp_mode(phys_addr_t boot_pgd_ptr, 356 + phys_addr_t phys_idmap_start) 357 + { 358 + /* 359 + * Call reset code, and switch back to stub hyp vectors. 360 + * Uses __kvm_call_hyp() to avoid kaslr's kvm_ksym_ref() translation. 361 + */ 362 + __kvm_call_hyp((void *)kvm_hyp_reset_entry(), 363 + boot_pgd_ptr, phys_idmap_start); 364 + } 365 + 357 366 static inline void kvm_arch_hardware_unsetup(void) {} 358 367 static inline void kvm_arch_sync_events(struct kvm *kvm) {} 359 368 static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
+1
arch/arm64/include/asm/kvm_mmu.h
··· 109 109 phys_addr_t kvm_mmu_get_httbr(void); 110 110 phys_addr_t kvm_mmu_get_boot_httbr(void); 111 111 phys_addr_t kvm_get_idmap_vector(void); 112 + phys_addr_t kvm_get_idmap_start(void); 112 113 int kvm_mmu_init(void); 113 114 void kvm_clear_hyp_idmap(void); 114 115
+38
arch/arm64/kvm/hyp-init.S
··· 139 139 eret 140 140 ENDPROC(__kvm_hyp_init) 141 141 142 + /* 143 + * x0: HYP boot pgd 144 + * x1: HYP phys_idmap_start 145 + */ 146 + ENTRY(__kvm_hyp_reset) 147 + /* We're in trampoline code in VA, switch back to boot page tables */ 148 + msr ttbr0_el2, x0 149 + isb 150 + 151 + /* Ensure the PA branch doesn't find a stale tlb entry or stale code. */ 152 + ic iallu 153 + tlbi alle2 154 + dsb sy 155 + isb 156 + 157 + /* Branch into PA space */ 158 + adr x0, 1f 159 + bfi x1, x0, #0, #PAGE_SHIFT 160 + br x1 161 + 162 + /* We're now in idmap, disable MMU */ 163 + 1: mrs x0, sctlr_el2 164 + ldr x1, =SCTLR_ELx_FLAGS 165 + bic x0, x0, x1 // Clear SCTL_M and etc 166 + msr sctlr_el2, x0 167 + isb 168 + 169 + /* Invalidate the old TLBs */ 170 + tlbi alle2 171 + dsb sy 172 + 173 + /* Install stub vectors */ 174 + adr_l x0, __hyp_stub_vectors 175 + msr vbar_el2, x0 176 + 177 + eret 178 + ENDPROC(__kvm_hyp_reset) 179 + 142 180 .ltorg 143 181 144 182 .popsection
+14
arch/arm64/kvm/reset.c
··· 29 29 #include <asm/cputype.h> 30 30 #include <asm/ptrace.h> 31 31 #include <asm/kvm_arm.h> 32 + #include <asm/kvm_asm.h> 32 33 #include <asm/kvm_coproc.h> 34 + #include <asm/kvm_mmu.h> 33 35 34 36 /* 35 37 * ARMv8 Reset Values ··· 131 129 132 130 /* Reset timer */ 133 131 return kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq); 132 + } 133 + 134 + extern char __hyp_idmap_text_start[]; 135 + 136 + phys_addr_t kvm_hyp_reset_entry(void) 137 + { 138 + unsigned long offset; 139 + 140 + offset = (unsigned long)__kvm_hyp_reset 141 + - ((unsigned long)__hyp_idmap_text_start & PAGE_MASK); 142 + 143 + return TRAMPOLINE_VA + offset; 134 144 }