]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
kvm: arm64: Set up hyp percpu data for nVHE
authorDavid Brazdil <dbrazdil@google.com>
Tue, 22 Sep 2020 20:49:09 +0000 (21:49 +0100)
committerMarc Zyngier <maz@kernel.org>
Wed, 30 Sep 2020 07:37:14 +0000 (08:37 +0100)
Add hyp percpu section to linker script and rename the corresponding ELF
sections of hyp/nvhe object files. This moves all nVHE-specific percpu
variables to the new hyp percpu section.

Allocate sufficient amount of memory for all percpu hyp regions at global KVM
init time and create corresponding hyp mappings.

The base addresses of hyp percpu regions are kept in a dynamically allocated
array in the kernel.

Add NULL checks in PMU event-reset code as it may run before KVM memory is
initialized.

Signed-off-by: David Brazdil <dbrazdil@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Acked-by: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20200922204910.7265-10-dbrazdil@google.com
arch/arm64/include/asm/kvm_asm.h
arch/arm64/kernel/vmlinux.lds.S
arch/arm64/kvm/arm.c
arch/arm64/kvm/hyp/nvhe/hyp.lds.S
arch/arm64/kvm/pmu.c

index 911d91787fa0f5dff8f5cdf0d91574e0808b2d5a..863f669d4dc8694e28b6c60194806add4d7bb721 100644 (file)
 #define CHOOSE_VHE_SYM(sym)    sym
 #define CHOOSE_NVHE_SYM(sym)   kvm_nvhe_sym(sym)
 
-#define this_cpu_ptr_nvhe_sym(sym)     this_cpu_ptr(&kvm_nvhe_sym(sym))
-#define per_cpu_ptr_nvhe_sym(sym, cpu) per_cpu_ptr(&kvm_nvhe_sym(sym), cpu)
+/*
+ * Compute pointer to a symbol defined in nVHE percpu region.
+ * Returns NULL if percpu memory has not been allocated yet.
+ */
+#define this_cpu_ptr_nvhe_sym(sym)     per_cpu_ptr_nvhe_sym(sym, smp_processor_id())
+#define per_cpu_ptr_nvhe_sym(sym, cpu)                                         \
+       ({                                                                      \
+               unsigned long base, off;                                        \
+               base = kvm_arm_hyp_percpu_base[cpu];                            \
+               off = (unsigned long)&CHOOSE_NVHE_SYM(sym) -                    \
+                     (unsigned long)&CHOOSE_NVHE_SYM(__per_cpu_start);         \
+               base ? (typeof(CHOOSE_NVHE_SYM(sym))*)(base + off) : NULL;      \
+       })
 
 #ifndef __KVM_NVHE_HYPERVISOR__
 /*
@@ -117,6 +128,10 @@ DECLARE_KVM_HYP_SYM(__kvm_hyp_vector);
 #define __kvm_hyp_init         CHOOSE_NVHE_SYM(__kvm_hyp_init)
 #define __kvm_hyp_vector       CHOOSE_HYP_SYM(__kvm_hyp_vector)
 
+extern unsigned long kvm_arm_hyp_percpu_base[NR_CPUS];
+DECLARE_KVM_NVHE_SYM(__per_cpu_start);
+DECLARE_KVM_NVHE_SYM(__per_cpu_end);
+
 #ifdef CONFIG_KVM_INDIRECT_VECTORS
 extern atomic_t arm64_el2_vector_last_slot;
 DECLARE_KVM_HYP_SYM(__bp_harden_hyp_vecs);
index d14166012e5143d27736e452481b8d2bbaef876d..d52e6b5dbfd3e95aa4d76addb44f0944898b0fb5 100644 (file)
@@ -28,8 +28,15 @@ jiffies = jiffies_64;
        __start___kvm_ex_table = .;                             \
        *(__kvm_ex_table)                                       \
        __stop___kvm_ex_table = .;
+
+#define HYPERVISOR_PERCPU_SECTION                              \
+       . = ALIGN(PAGE_SIZE);                                   \
+       HYP_SECTION_NAME(.data..percpu) : {                     \
+               *(HYP_SECTION_NAME(.data..percpu))              \
+       }
 #else /* CONFIG_KVM */
 #define HYPERVISOR_EXTABLE
+#define HYPERVISOR_PERCPU_SECTION
 #endif
 
 #define HYPERVISOR_TEXT                                        \
@@ -195,6 +202,7 @@ SECTIONS
        }
 
        PERCPU_SECTION(L1_CACHE_BYTES)
+       HYPERVISOR_PERCPU_SECTION
 
        .rela.dyn : ALIGN(8) {
                *(.rela .rela*)
index a9d6f095031dd21ff07a4aa4a5869d6043365257..35bed6b6d0a6c82a9253d4763b5985b650ddb37e 100644 (file)
@@ -47,6 +47,7 @@ __asm__(".arch_extension      virt");
 #endif
 
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
+unsigned long kvm_arm_hyp_percpu_base[NR_CPUS];
 
 /* The VMID used in the VTTBR */
 static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
@@ -1255,6 +1256,19 @@ long kvm_arch_vm_ioctl(struct file *filp,
        }
 }
 
+static unsigned long nvhe_percpu_size(void)
+{
+       return (unsigned long)CHOOSE_NVHE_SYM(__per_cpu_end) -
+               (unsigned long)CHOOSE_NVHE_SYM(__per_cpu_start);
+}
+
+static unsigned long nvhe_percpu_order(void)
+{
+       unsigned long size = nvhe_percpu_size();
+
+       return size ? get_order(size) : 0;
+}
+
 static void cpu_init_hyp_mode(void)
 {
        phys_addr_t pgd_ptr;
@@ -1270,8 +1284,8 @@ static void cpu_init_hyp_mode(void)
         * kernel's mapping to the linear mapping, and store it in tpidr_el2
         * so that we can use adr_l to access per-cpu variables in EL2.
         */
-       tpidr_el2 = ((unsigned long)this_cpu_ptr(&kvm_host_data) -
-                    (unsigned long)kvm_ksym_ref(&kvm_host_data));
+       tpidr_el2 = (unsigned long)this_cpu_ptr_nvhe_sym(__per_cpu_start) -
+                   (unsigned long)kvm_ksym_ref(CHOOSE_NVHE_SYM(__per_cpu_start));
 
        pgd_ptr = kvm_mmu_get_httbr();
        hyp_stack_ptr = __this_cpu_read(kvm_arm_hyp_stack_page) + PAGE_SIZE;
@@ -1461,8 +1475,10 @@ static void teardown_hyp_mode(void)
        int cpu;
 
        free_hyp_pgds();
-       for_each_possible_cpu(cpu)
+       for_each_possible_cpu(cpu) {
                free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));
+               free_pages(kvm_arm_hyp_percpu_base[cpu], nvhe_percpu_order());
+       }
 }
 
 /**
@@ -1495,6 +1511,24 @@ static int init_hyp_mode(void)
                per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
        }
 
+       /*
+        * Allocate and initialize pages for Hypervisor-mode percpu regions.
+        */
+       for_each_possible_cpu(cpu) {
+               struct page *page;
+               void *page_addr;
+
+               page = alloc_pages(GFP_KERNEL, nvhe_percpu_order());
+               if (!page) {
+                       err = -ENOMEM;
+                       goto out_err;
+               }
+
+               page_addr = page_address(page);
+               memcpy(page_addr, CHOOSE_NVHE_SYM(__per_cpu_start), nvhe_percpu_size());
+               kvm_arm_hyp_percpu_base[cpu] = (unsigned long)page_addr;
+       }
+
        /*
         * Map the Hyp-code called directly from the host
         */
@@ -1539,6 +1573,21 @@ static int init_hyp_mode(void)
                }
        }
 
+       /*
+        * Map Hyp percpu pages
+        */
+       for_each_possible_cpu(cpu) {
+               char *percpu_begin = (char *)kvm_arm_hyp_percpu_base[cpu];
+               char *percpu_end = percpu_begin + nvhe_percpu_size();
+
+               err = create_hyp_mappings(percpu_begin, percpu_end, PAGE_HYP);
+
+               if (err) {
+                       kvm_err("Cannot map hyp percpu region\n");
+                       goto out_err;
+               }
+       }
+
        for_each_possible_cpu(cpu) {
                kvm_host_data_t *cpu_data;
 
index 3b13d1c7cd1a7e9aa053f474c23b6abc49c003dd..bb2d986ff6962bbcb0b0bc31e0194487cb82d9e1 100644 (file)
@@ -7,7 +7,13 @@
  */
 
 #include <asm/hyp_image.h>
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/cache.h>
+#include <asm/memory.h>
 
 SECTIONS {
        HYP_SECTION(.text)
+       HYP_SECTION_NAME(.data..percpu) : {
+               PERCPU_INPUT(L1_CACHE_BYTES)
+       }
 }
index c869c851d2dde02d453ddf64c7193dd563228d7a..faf32a44ba04a0ac237f42eaaeab199185ab4c16 100644 (file)
@@ -33,7 +33,7 @@ void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr)
 {
        struct kvm_host_data *ctx = this_cpu_ptr_hyp_sym(kvm_host_data);
 
-       if (!kvm_pmu_switch_needed(attr))
+       if (!ctx || !kvm_pmu_switch_needed(attr))
                return;
 
        if (!attr->exclude_host)
@@ -49,6 +49,9 @@ void kvm_clr_pmu_events(u32 clr)
 {
        struct kvm_host_data *ctx = this_cpu_ptr_hyp_sym(kvm_host_data);
 
+       if (!ctx)
+               return;
+
        ctx->pmu_events.events_host &= ~clr;
        ctx->pmu_events.events_guest &= ~clr;
 }