diff options
| author | Marc Zyngier <marc.zyngier@arm.com> | 2013-04-12 14:12:07 -0400 |
|---|---|---|
| committer | Christoffer Dall <cdall@cs.columbia.edu> | 2013-04-29 01:23:11 -0400 |
| commit | d157f4a5155f4fbd0d1da66b3d2f504c13bd194d (patch) | |
| tree | dd0a619e12202f2ce972a0412401b2b4d2329992 | |
| parent | 5a677ce044f18a341ab942e23516e52ad89f7687 (diff) | |
ARM: KVM: perform HYP initilization for hotplugged CPUs
Now that we have the necessary infrastructure to boot a hotplugged CPU
at any point in time, wire a CPU notifier that will perform the HYP
init for the incoming CPU.
Note that this depends on the platform code and/or firmware to boot the
incoming CPU with HYP mode enabled and return to the kernel by following
the normal boot path (HYP stub installed).
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <cdall@cs.columbia.edu>
| -rw-r--r-- | arch/arm/include/asm/kvm_mmu.h | 1 | ||||
| -rw-r--r-- | arch/arm/kvm/arm.c | 49 | ||||
| -rw-r--r-- | arch/arm/kvm/mmu.c | 35 |
3 files changed, 63 insertions, 22 deletions
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h index 24b767a8cdb9..472ac7091003 100644 --- a/arch/arm/include/asm/kvm_mmu.h +++ b/arch/arm/include/asm/kvm_mmu.h | |||
| @@ -44,6 +44,7 @@ | |||
| 44 | 44 | ||
| 45 | int create_hyp_mappings(void *from, void *to); | 45 | int create_hyp_mappings(void *from, void *to); |
| 46 | int create_hyp_io_mappings(void *from, void *to, phys_addr_t); | 46 | int create_hyp_io_mappings(void *from, void *to, phys_addr_t); |
| 47 | void free_boot_hyp_pgd(void); | ||
| 47 | void free_hyp_pgds(void); | 48 | void free_hyp_pgds(void); |
| 48 | 49 | ||
| 49 | int kvm_alloc_stage2_pgd(struct kvm *kvm); | 50 | int kvm_alloc_stage2_pgd(struct kvm *kvm); |
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index fc47bd721ab0..6ea2aed0d29f 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c | |||
| @@ -16,6 +16,7 @@ | |||
| 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> | ||
| 19 | #include <linux/errno.h> | 20 | #include <linux/errno.h> |
| 20 | #include <linux/err.h> | 21 | #include <linux/err.h> |
| 21 | #include <linux/kvm_host.h> | 22 | #include <linux/kvm_host.h> |
| @@ -785,7 +786,7 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
| 785 | } | 786 | } |
| 786 | } | 787 | } |
| 787 | 788 | ||
| 788 | static void cpu_init_hyp_mode(void *vector) | 789 | static void cpu_init_hyp_mode(void *dummy) |
| 789 | { | 790 | { |
| 790 | unsigned long long boot_pgd_ptr; | 791 | unsigned long long boot_pgd_ptr; |
| 791 | unsigned long long pgd_ptr; | 792 | unsigned long long pgd_ptr; |
| @@ -805,12 +806,28 @@ static void cpu_init_hyp_mode(void *vector) | |||
| 805 | __cpu_init_hyp_mode(boot_pgd_ptr, pgd_ptr, hyp_stack_ptr, vector_ptr); | 806 | __cpu_init_hyp_mode(boot_pgd_ptr, pgd_ptr, hyp_stack_ptr, vector_ptr); |
| 806 | } | 807 | } |
| 807 | 808 | ||
| 809 | static int hyp_init_cpu_notify(struct notifier_block *self, | ||
| 810 | unsigned long action, void *cpu) | ||
| 811 | { | ||
| 812 | switch (action) { | ||
| 813 | case CPU_STARTING: | ||
| 814 | case CPU_STARTING_FROZEN: | ||
| 815 | cpu_init_hyp_mode(NULL); | ||
| 816 | break; | ||
| 817 | } | ||
| 818 | |||
| 819 | return NOTIFY_OK; | ||
| 820 | } | ||
| 821 | |||
| 822 | static struct notifier_block hyp_init_cpu_nb = { | ||
| 823 | .notifier_call = hyp_init_cpu_notify, | ||
| 824 | }; | ||
| 825 | |||
| 808 | /** | 826 | /** |
| 809 | * Inits Hyp-mode on all online CPUs | 827 | * Inits Hyp-mode on all online CPUs |
| 810 | */ | 828 | */ |
| 811 | static int init_hyp_mode(void) | 829 | static int init_hyp_mode(void) |
| 812 | { | 830 | { |
| 813 | phys_addr_t init_phys_addr; | ||
| 814 | int cpu; | 831 | int cpu; |
| 815 | int err = 0; | 832 | int err = 0; |
| 816 | 833 | ||
| @@ -843,19 +860,6 @@ static int init_hyp_mode(void) | |||
| 843 | } | 860 | } |
| 844 | 861 | ||
| 845 | /* | 862 | /* |
| 846 | * Execute the init code on each CPU. | ||
| 847 | * | ||
| 848 | * Note: The stack is not mapped yet, so don't do anything else than | ||
| 849 | * initializing the hypervisor mode on each CPU using a local stack | ||
| 850 | * space for temporary storage. | ||
| 851 | */ | ||
| 852 | init_phys_addr = virt_to_phys(__kvm_hyp_init); | ||
| 853 | for_each_online_cpu(cpu) { | ||
| 854 | smp_call_function_single(cpu, cpu_init_hyp_mode, | ||
| 855 | (void *)(long)init_phys_addr, 1); | ||
| 856 | } | ||
| 857 | |||
| 858 | /* | ||
| 859 | * Map the Hyp-code called directly from the host | 863 | * Map the Hyp-code called directly from the host |
| 860 | */ | 864 | */ |
| 861 | err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end); | 865 | err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end); |
| @@ -900,6 +904,11 @@ static int init_hyp_mode(void) | |||
| 900 | } | 904 | } |
| 901 | 905 | ||
| 902 | /* | 906 | /* |
| 907 | * Execute the init code on each CPU. | ||
| 908 | */ | ||
| 909 | on_each_cpu(cpu_init_hyp_mode, NULL, 1); | ||
| 910 | |||
| 911 | /* | ||
| 903 | * Init HYP view of VGIC | 912 | * Init HYP view of VGIC |
| 904 | */ | 913 | */ |
| 905 | err = kvm_vgic_hyp_init(); | 914 | err = kvm_vgic_hyp_init(); |
| @@ -917,6 +926,10 @@ static int init_hyp_mode(void) | |||
| 917 | if (err) | 926 | if (err) |
| 918 | goto out_free_mappings; | 927 | goto out_free_mappings; |
| 919 | 928 | ||
| 929 | #ifndef CONFIG_HOTPLUG_CPU | ||
| 930 | free_boot_hyp_pgd(); | ||
| 931 | #endif | ||
| 932 | |||
| 920 | kvm_perf_init(); | 933 | kvm_perf_init(); |
| 921 | 934 | ||
| 922 | kvm_info("Hyp mode initialized successfully\n"); | 935 | kvm_info("Hyp mode initialized successfully\n"); |
| @@ -955,6 +968,12 @@ int kvm_arch_init(void *opaque) | |||
| 955 | if (err) | 968 | if (err) |
| 956 | goto out_err; | 969 | goto out_err; |
| 957 | 970 | ||
| 971 | err = register_cpu_notifier(&hyp_init_cpu_nb); | ||
| 972 | if (err) { | ||
| 973 | kvm_err("Cannot register HYP init CPU notifier (%d)\n", err); | ||
| 974 | goto out_err; | ||
| 975 | } | ||
| 976 | |||
| 958 | kvm_coproc_table_init(); | 977 | kvm_coproc_table_init(); |
| 959 | return 0; | 978 | return 0; |
| 960 | out_err: | 979 | out_err: |
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 4646c17f571c..965706578f13 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
| @@ -156,6 +156,31 @@ static void unmap_range(pgd_t *pgdp, unsigned long long start, u64 size) | |||
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | /** | 158 | /** |
| 159 | * free_boot_hyp_pgd - free HYP boot page tables | ||
| 160 | * | ||
| 161 | * Free the HYP boot page tables. The bounce page is also freed. | ||
| 162 | */ | ||
| 163 | void free_boot_hyp_pgd(void) | ||
| 164 | { | ||
| 165 | mutex_lock(&kvm_hyp_pgd_mutex); | ||
| 166 | |||
| 167 | if (boot_hyp_pgd) { | ||
| 168 | unmap_range(boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE); | ||
| 169 | unmap_range(boot_hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE); | ||
| 170 | kfree(boot_hyp_pgd); | ||
| 171 | boot_hyp_pgd = NULL; | ||
| 172 | } | ||
| 173 | |||
| 174 | if (hyp_pgd) | ||
| 175 | unmap_range(hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE); | ||
| 176 | |||
| 177 | kfree(init_bounce_page); | ||
| 178 | init_bounce_page = NULL; | ||
| 179 | |||
| 180 | mutex_unlock(&kvm_hyp_pgd_mutex); | ||
| 181 | } | ||
| 182 | |||
| 183 | /** | ||
| 159 | * free_hyp_pgds - free Hyp-mode page tables | 184 | * free_hyp_pgds - free Hyp-mode page tables |
| 160 | * | 185 | * |
| 161 | * Assumes hyp_pgd is a page table used strictly in Hyp-mode and | 186 | * Assumes hyp_pgd is a page table used strictly in Hyp-mode and |
| @@ -169,13 +194,9 @@ void free_hyp_pgds(void) | |||
| 169 | { | 194 | { |
| 170 | unsigned long addr; | 195 | unsigned long addr; |
| 171 | 196 | ||
| 172 | mutex_lock(&kvm_hyp_pgd_mutex); | 197 | free_boot_hyp_pgd(); |
| 173 | 198 | ||
| 174 | if (boot_hyp_pgd) { | 199 | mutex_lock(&kvm_hyp_pgd_mutex); |
| 175 | unmap_range(boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE); | ||
| 176 | unmap_range(boot_hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE); | ||
| 177 | kfree(boot_hyp_pgd); | ||
| 178 | } | ||
| 179 | 200 | ||
| 180 | if (hyp_pgd) { | 201 | if (hyp_pgd) { |
| 181 | for (addr = PAGE_OFFSET; virt_addr_valid(addr); addr += PGDIR_SIZE) | 202 | for (addr = PAGE_OFFSET; virt_addr_valid(addr); addr += PGDIR_SIZE) |
| @@ -183,9 +204,9 @@ void free_hyp_pgds(void) | |||
| 183 | for (addr = VMALLOC_START; is_vmalloc_addr((void*)addr); addr += PGDIR_SIZE) | 204 | for (addr = VMALLOC_START; is_vmalloc_addr((void*)addr); addr += PGDIR_SIZE) |
| 184 | unmap_range(hyp_pgd, KERN_TO_HYP(addr), PGDIR_SIZE); | 205 | unmap_range(hyp_pgd, KERN_TO_HYP(addr), PGDIR_SIZE); |
| 185 | kfree(hyp_pgd); | 206 | kfree(hyp_pgd); |
| 207 | hyp_pgd = NULL; | ||
| 186 | } | 208 | } |
| 187 | 209 | ||
| 188 | kfree(init_bounce_page); | ||
| 189 | mutex_unlock(&kvm_hyp_pgd_mutex); | 210 | mutex_unlock(&kvm_hyp_pgd_mutex); |
