FROMLIST: [PATCH v5 11/12] lib: vdso: Add support for CLOCK_BOOTTIME
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / arch / arm64 / kernel / vdso.c
index 3b8acfae7797bf54877805cde809d7a061564562..a1e023d3818ceaefb87609a6beb5590c0c78483a 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * VDSO implementation for AArch64 and vector page setup for AArch32.
+ * Additional userspace pages setup for AArch64 and AArch32.
+ *  - AArch64: vDSO pages setup, vDSO data page update.
+ *  - AArch32: sigreturn and kuser helpers pages setup.
  *
  * Copyright (C) 2012 ARM Limited
  *
@@ -53,32 +55,59 @@ struct vdso_data *vdso_data = &vdso_data_store.data;
 /*
  * Create and map the vectors page for AArch32 tasks.
  */
-static struct page *vectors_page[1];
+static struct page *vectors_page[] __ro_after_init;
+static const struct vm_special_mapping compat_vdso_spec[] = {
+       {
+               /* Must be named [sigpage] for compatibility with arm. */
+               .name   = "[sigpage]",
+               .pages  = &vectors_page[0],
+       },
+#ifdef CONFIG_KUSER_HELPERS
+       {
+               .name   = "[kuserhelpers]",
+               .pages  = &vectors_page[1],
+       },
+#endif
+};
+static struct page *vectors_page[ARRAY_SIZE(compat_vdso_spec)] __ro_after_init;
 
 static int __init alloc_vectors_page(void)
 {
+#ifdef CONFIG_KUSER_HELPERS
        extern char __kuser_helper_start[], __kuser_helper_end[];
-       extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];
-
-       int kuser_sz = __kuser_helper_end - __kuser_helper_start;
-       int sigret_sz = __aarch32_sigret_code_end - __aarch32_sigret_code_start;
-       unsigned long vpage;
+       size_t kuser_sz = __kuser_helper_end - __kuser_helper_start;
+       unsigned long kuser_vpage;
+#endif
 
-       vpage = get_zeroed_page(GFP_ATOMIC);
+       extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];
+       size_t sigret_sz =
+               __aarch32_sigret_code_end - __aarch32_sigret_code_start;
+       unsigned long sigret_vpage;
 
-       if (!vpage)
+       sigret_vpage = get_zeroed_page(GFP_ATOMIC);
+       if (!sigret_vpage)
                return -ENOMEM;
 
-       /* kuser helpers */
-       memcpy((void *)vpage + 0x1000 - kuser_sz, __kuser_helper_start,
-               kuser_sz);
+#ifdef CONFIG_KUSER_HELPERS
+       kuser_vpage = get_zeroed_page(GFP_ATOMIC);
+       if (!kuser_vpage) {
+               free_page(sigret_vpage);
+               return -ENOMEM;
+       }
+#endif
 
        /* sigreturn code */
-       memcpy((void *)vpage + AARCH32_KERN_SIGRET_CODE_OFFSET,
-               __aarch32_sigret_code_start, sigret_sz);
+       memcpy((void *)sigret_vpage, __aarch32_sigret_code_start, sigret_sz);
+       flush_icache_range(sigret_vpage, sigret_vpage + PAGE_SIZE);
+       vectors_page[0] = virt_to_page(sigret_vpage);
 
-       flush_icache_range(vpage, vpage + PAGE_SIZE);
-       vectors_page[0] = virt_to_page(vpage);
+#ifdef CONFIG_KUSER_HELPERS
+       /* kuser helpers */
+       memcpy((void *)kuser_vpage + 0x1000 - kuser_sz, __kuser_helper_start,
+               kuser_sz);
+       flush_icache_range(kuser_vpage, kuser_vpage + PAGE_SIZE);
+       vectors_page[1] = virt_to_page(kuser_vpage);
+#endif
 
        return 0;
 }
@@ -87,22 +116,33 @@ arch_initcall(alloc_vectors_page);
 int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp)
 {
        struct mm_struct *mm = current->mm;
-       unsigned long addr = AARCH32_VECTORS_BASE;
-       static const struct vm_special_mapping spec = {
-               .name   = "[vectors]",
-               .pages  = vectors_page,
-
-       };
+       unsigned long addr;
        void *ret;
 
        down_write(&mm->mmap_sem);
-       current->mm->context.vdso = (void *)addr;
+       addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
+       if (IS_ERR_VALUE(addr)) {
+               ret = ERR_PTR(addr);
+               goto out;
+       }
 
-       /* Map vectors page at the high address. */
        ret = _install_special_mapping(mm, addr, PAGE_SIZE,
-                                      VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC,
-                                      &spec);
+                                      VM_READ|VM_EXEC|
+                                      VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
+                                      &compat_vdso_spec[0]);
+       if (IS_ERR(ret))
+               goto out;
 
+       current->mm->context.vdso = (void *)addr;
+
+#ifdef CONFIG_KUSER_HELPERS
+       /* Map the kuser helpers at the ABI-defined high address. */
+       ret = _install_special_mapping(mm, AARCH32_KUSER_HELPERS_BASE,
+                                      PAGE_SIZE,
+                                      VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC,
+                                      &compat_vdso_spec[1]);
+#endif
+out:
        up_write(&mm->mmap_sem);
 
        return PTR_ERR_OR_ZERO(ret);
@@ -114,6 +154,7 @@ static struct vm_special_mapping vdso_spec[2];
 static int __init vdso_init(void)
 {
        int i;
+       unsigned long pfn;
 
        if (memcmp(&vdso_start, "\177ELF", 4)) {
                pr_err("vDSO is not a valid ELF object!\n");
@@ -131,11 +172,14 @@ static int __init vdso_init(void)
                return -ENOMEM;
 
        /* Grab the vDSO data page. */
-       vdso_pagelist[0] = virt_to_page(vdso_data);
+       vdso_pagelist[0] = phys_to_page(__pa_symbol(vdso_data));
+
 
        /* Grab the vDSO code pages. */
+       pfn = sym_to_pfn(&vdso_start);
+
        for (i = 0; i < vdso_pages; i++)
-               vdso_pagelist[i + 1] = virt_to_page(&vdso_start + i * PAGE_SIZE);
+               vdso_pagelist[i + 1] = pfn_to_page(pfn + i);
 
        /* Populate the special mapping structures */
        vdso_spec[0] = (struct vm_special_mapping) {
@@ -214,15 +258,16 @@ void update_vsyscall(struct timekeeper *tk)
        if (!use_syscall) {
                /* tkr_mono.cycle_last == tkr_raw.cycle_last */
                vdso_data->cs_cycle_last        = tk->tkr_mono.cycle_last;
-               vdso_data->raw_time_sec         = tk->raw_time.tv_sec;
-               vdso_data->raw_time_nsec        = tk->raw_time.tv_nsec;
+               vdso_data->raw_time_sec         = tk->raw_sec;
+               vdso_data->raw_time_nsec        = tk->tkr_raw.xtime_nsec;
                vdso_data->xtime_clock_sec      = tk->xtime_sec;
-               vdso_data->xtime_clock_nsec     = tk->tkr_mono.xtime_nsec;
+               vdso_data->xtime_clock_snsec    = tk->tkr_mono.xtime_nsec;
                /* tkr_raw.xtime_nsec == 0 */
                vdso_data->cs_mono_mult         = tk->tkr_mono.mult;
                vdso_data->cs_raw_mult          = tk->tkr_raw.mult;
                /* tkr_mono.shift == tkr_raw.shift */
                vdso_data->cs_shift             = tk->tkr_mono.shift;
+               vdso_data->btm_nsec             = ktime_to_ns(tk->offs_boot);
        }
 
        smp_wmb();