[PATCH v4 05/13] arm64: kvm: allows kvm cpu hotplug
AKASHI Takahiro
takahiro.akashi at linaro.org
Mon Feb 1 22:46:13 PST 2016
On 01/28/2016 07:42 PM, James Morse wrote:
> From: AKASHI Takahiro <takahiro.akashi at linaro.org>
>
> 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 at linaro.org>
> [Moved __kvm_hyp_reset() to use kvm_call_hyp(), instead of having its own
> dedicated entry point in el1_sync. Added some comments and a tlbi.]
> Signed-off-by: James Morse <james.morse at arm.com>
> ---
> This patch is from v13 of kexec, see my [changes] above.
>
> arch/arm/include/asm/kvm_host.h | 10 +++-
> arch/arm/include/asm/kvm_mmu.h | 1 +
> arch/arm/kvm/arm.c | 98 ++++++++++++++++++++++++---------------
> arch/arm/kvm/mmu.c | 5 ++
> arch/arm64/include/asm/kvm_host.h | 1 -
> arch/arm64/include/asm/kvm_mmu.h | 19 ++++++++
> arch/arm64/kvm/hyp-init.S | 42 +++++++++++++++++
> 7 files changed, 136 insertions(+), 40 deletions(-)
>
> diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
> index f9f27792d8ed..8af531d64771 100644
> --- a/arch/arm/include/asm/kvm_host.h
> +++ b/arch/arm/include/asm/kvm_host.h
> @@ -220,6 +220,15 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
> kvm_call_hyp((void*)hyp_stack_ptr, vector_ptr, pgd_ptr);
> }
>
> +static inline void __cpu_reset_hyp_mode(phys_addr_t boot_pgd_ptr,
> + phys_addr_t phys_idmap_start)
> +{
> + /*
> + * TODO
> + * kvm_call_reset(boot_pgd_ptr, phys_idmap_start);
> + */
> +}
> +
> static inline int kvm_arch_dev_ioctl_check_extension(long ext)
> {
> return 0;
> @@ -232,7 +241,6 @@ void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);
>
> struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr);
>
> -static inline void kvm_arch_hardware_disable(void) {}
> static inline void kvm_arch_hardware_unsetup(void) {}
> static inline void kvm_arch_sync_events(struct kvm *kvm) {}
> static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
> diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
> index a520b7987a29..4fd9ddb48c0f 100644
> --- a/arch/arm/include/asm/kvm_mmu.h
> +++ b/arch/arm/include/asm/kvm_mmu.h
> @@ -66,6 +66,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
> phys_addr_t kvm_mmu_get_httbr(void);
> phys_addr_t kvm_mmu_get_boot_httbr(void);
> phys_addr_t kvm_get_idmap_vector(void);
> +phys_addr_t kvm_get_idmap_start(void);
> int kvm_mmu_init(void);
> void kvm_clear_hyp_idmap(void);
>
> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
> index dda1959f0dde..f060567e9c0a 100644
> --- a/arch/arm/kvm/arm.c
> +++ b/arch/arm/kvm/arm.c
> @@ -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>
> @@ -65,6 +64,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());
> @@ -89,11 +90,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;
> @@ -585,7 +581,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
> /*
> * Re-check atomic conditions
> */
> - if (signal_pending(current)) {
> + if (unlikely(!__this_cpu_read(kvm_arm_hardware_enabled))) {
> + /* cpu has been torn down */
> + ret = 0;
> + run->exit_reason = KVM_EXIT_FAIL_ENTRY;
> + run->fail_entry.hardware_entry_failure_reason
> + = (u64)-ENOEXEC;
> + } else if (signal_pending(current)) {
> ret = -EINTR;
> run->exit_reason = KVM_EXIT_INTR;
> }
> @@ -967,7 +969,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
> }
> }
>
> -static void cpu_init_hyp_mode(void *dummy)
> +static void cpu_init_hyp_mode(void)
> {
> phys_addr_t boot_pgd_ptr;
> phys_addr_t pgd_ptr;
> @@ -989,36 +991,61 @@ static void cpu_init_hyp_mode(void *dummy)
> kvm_arm_init_debug();
> }
>
> -static int hyp_init_cpu_notify(struct notifier_block *self,
> - unsigned long action, void *cpu)
> +static void cpu_reset_hyp_mode(void)
> {
> - switch (action) {
> - case CPU_STARTING:
> - case CPU_STARTING_FROZEN:
> - if (__hyp_get_vectors() == hyp_default_vectors)
> - cpu_init_hyp_mode(NULL);
> - break;
> + phys_addr_t boot_pgd_ptr;
> + phys_addr_t phys_idmap_start;
> +
> + 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);
> +}
> +
> +int kvm_arch_hardware_enable(void)
> +{
> + if (!__this_cpu_read(kvm_arm_hardware_enabled)) {
> + cpu_init_hyp_mode();
> + __this_cpu_write(kvm_arm_hardware_enabled, 1);
> }
>
> - return NOTIFY_OK;
> + return 0;
> }
>
> -static struct notifier_block hyp_init_cpu_nb = {
> - .notifier_call = hyp_init_cpu_notify,
> -};
> +void kvm_arch_hardware_disable(void)
> +{
> + if (!__this_cpu_read(kvm_arm_hardware_enabled))
> + return;
> +
> + cpu_reset_hyp_mode();
> + __this_cpu_write(kvm_arm_hardware_enabled, 0);
> +}
>
> #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 &&
> - __hyp_get_vectors() == hyp_default_vectors) {
> - cpu_init_hyp_mode(NULL);
> + /*
> + * 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))
> + cpu_reset_hyp_mode();
> +
> + return NOTIFY_OK;
> + case CPU_PM_EXIT:
> + if (__this_cpu_read(kvm_arm_hardware_enabled))
> + cpu_init_hyp_mode();
> +
> return NOTIFY_OK;
> - }
>
> - return NOTIFY_DONE;
> + default:
> + return NOTIFY_DONE;
> + }
> }
>
> static struct notifier_block hyp_init_cpu_pm_nb = {
> @@ -1122,14 +1149,20 @@ static int init_hyp_mode(void)
> }
>
> /*
> - * Execute the init code on each CPU.
> + * Init this CPU temporarily to execute kvm_hyp_call()
> + * during kvm_vgic_hyp_init().
> */
> - on_each_cpu(cpu_init_hyp_mode, NULL, 1);
> + preempt_disable();
> + cpu_init_hyp_mode();
>
> /*
> * Init HYP view of VGIC
> */
> err = kvm_vgic_hyp_init();
> +
> + cpu_reset_hyp_mode();
> + preempt_enable();
> +
> switch (err) {
> case 0:
> vgic_present = true;
> @@ -1213,26 +1246,15 @@ int kvm_arch_init(void *opaque)
> }
> }
>
> - cpu_notifier_register_begin();
> -
> err = init_hyp_mode();
> if (err)
> goto out_err;
>
> - err = __register_cpu_notifier(&hyp_init_cpu_nb);
> - if (err) {
> - kvm_err("Cannot register HYP init CPU notifier (%d)\n", err);
> - goto out_err;
> - }
> -
> - cpu_notifier_register_done();
> -
> hyp_cpu_pm_init();
>
> kvm_coproc_table_init();
> return 0;
> out_err:
> - cpu_notifier_register_done();
> return err;
> }
>
> diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
> index aba61fd3697a..7a3aed62499a 100644
> --- a/arch/arm/kvm/mmu.c
> +++ b/arch/arm/kvm/mmu.c
> @@ -1643,6 +1643,11 @@ phys_addr_t kvm_get_idmap_vector(void)
> return hyp_idmap_vector;
> }
>
> +phys_addr_t kvm_get_idmap_start(void)
> +{
> + return hyp_idmap_start;
> +}
> +
> int kvm_mmu_init(void)
> {
> int err;
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 689d4c95e12f..7d6d75616fb5 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -332,7 +332,6 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
> hyp_stack_ptr, vector_ptr);
> }
>
> -static inline void kvm_arch_hardware_disable(void) {}
> static inline void kvm_arch_hardware_unsetup(void) {}
> static inline void kvm_arch_sync_events(struct kvm *kvm) {}
> static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
> diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
> index 736433912a1e..1d48208a904a 100644
> --- a/arch/arm64/include/asm/kvm_mmu.h
> +++ b/arch/arm64/include/asm/kvm_mmu.h
> @@ -99,6 +99,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
> phys_addr_t kvm_mmu_get_httbr(void);
> phys_addr_t kvm_mmu_get_boot_httbr(void);
> phys_addr_t kvm_get_idmap_vector(void);
> +phys_addr_t kvm_get_idmap_start(void);
> int kvm_mmu_init(void);
> void kvm_clear_hyp_idmap(void);
>
> @@ -310,5 +311,23 @@ static inline unsigned int kvm_get_vmid_bits(void)
> return (cpuid_feature_extract_field(reg, ID_AA64MMFR1_VMIDBITS_SHIFT) == 2) ? 16 : 8;
> }
>
> +void __kvm_hyp_reset(phys_addr_t boot_pgd_ptr, phys_addr_t phys_idmap_start);
> +
> +/*
> + * Call reset code, and switch back to stub hyp vectors. We need to execute
> + * __kvm_hyp_reset() from the trampoline page, we calculate its address here.
> + */
> +static inline void __cpu_reset_hyp_mode(phys_addr_t boot_pgd_ptr,
> + phys_addr_t phys_idmap_start)
> +{
> + unsigned long trampoline_hyp_reset;
> +
> + trampoline_hyp_reset = TRAMPOLINE_VA +
> + ((unsigned long)__kvm_hyp_reset & ~PAGE_MASK);
> +
> + kvm_call_hyp((void *)trampoline_hyp_reset,
> + boot_pgd_ptr, phys_idmap_start);
> +}
> +
I want to place this definition in kvm_host.h as its counterpart, __cpu_init_hyp_mode().
-Takahiro AKASHI
> #endif /* __ASSEMBLY__ */
> #endif /* __ARM64_KVM_MMU_H__ */
> diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
> index dc6335a7353e..d20d86c7f9d8 100644
> --- a/arch/arm64/kvm/hyp-init.S
> +++ b/arch/arm64/kvm/hyp-init.S
> @@ -150,6 +150,48 @@ merged:
> eret
> ENDPROC(__kvm_hyp_init)
>
> + /*
> + * x0: HYP boot pgd
> + * x1: HYP phys_idmap_start
> + */
> +ENTRY(__kvm_hyp_reset)
> + /*
> + * Retrieve lr from the stack (pushed by el1_sync()), so we can eret
> + * from here.
> + */
> + ldp lr, xzr, [sp], #16
> +
> + /* We're in trampoline code in VA, switch back to boot page tables */
> + msr ttbr0_el2, x0
> + isb
> +
> + /* Ensure the PA branch doesn't find a stale tlb entry. */
> + tlbi alle2
> + dsb sy
> +
> + /* Branch into PA space */
> + adr x0, 1f
> + bfi x1, x0, #0, #PAGE_SHIFT
> + br x1
> +
> + /* We're now in idmap, disable MMU */
> +1: mrs x0, sctlr_el2
> + ldr x1, =SCTLR_ELx_FLAGS
> + bic x0, x0, x1 // Clear SCTL_M and etc
> + msr sctlr_el2, x0
> + isb
> +
> + /* Invalidate the old TLBs */
> + tlbi alle2
> + dsb sy
> +
> + /* Install stub vectors */
> + adr_l x0, __hyp_stub_vectors
> + msr vbar_el2, x0
> +
> + eret
> +ENDPROC(__kvm_hyp_reset)
> +
> .ltorg
>
> .popsection
>
More information about the linux-arm-kernel
mailing list