diff options
30 files changed, 1271 insertions, 69 deletions
diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 7d980706bfb4..e9d1fd6067c6 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile | |||
| @@ -263,6 +263,7 @@ core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ) | |||
| 263 | core-$(CONFIG_VFP) += arch/arm/vfp/ | 263 | core-$(CONFIG_VFP) += arch/arm/vfp/ |
| 264 | core-$(CONFIG_XEN) += arch/arm/xen/ | 264 | core-$(CONFIG_XEN) += arch/arm/xen/ |
| 265 | core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ | 265 | core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ |
| 266 | core-$(CONFIG_VDSO) += arch/arm/vdso/ | ||
| 266 | 267 | ||
| 267 | # If we have a machine-specific directory, then include it in the build. | 268 | # If we have a machine-specific directory, then include it in the build. |
| 268 | core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ | 269 | core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ |
| @@ -320,6 +321,12 @@ dtbs: prepare scripts | |||
| 320 | dtbs_install: | 321 | dtbs_install: |
| 321 | $(Q)$(MAKE) $(dtbinst)=$(boot)/dts | 322 | $(Q)$(MAKE) $(dtbinst)=$(boot)/dts |
| 322 | 323 | ||
| 324 | PHONY += vdso_install | ||
| 325 | vdso_install: | ||
| 326 | ifeq ($(CONFIG_VDSO),y) | ||
| 327 | $(Q)$(MAKE) $(build)=arch/arm/vdso $@ | ||
| 328 | endif | ||
| 329 | |||
| 323 | # We use MRPROPER_FILES and CLEAN_FILES now | 330 | # We use MRPROPER_FILES and CLEAN_FILES now |
| 324 | archclean: | 331 | archclean: |
| 325 | $(Q)$(MAKE) $(clean)=$(boot) | 332 | $(Q)$(MAKE) $(clean)=$(boot) |
| @@ -344,4 +351,5 @@ define archhelp | |||
| 344 | echo ' Install using (your) ~/bin/$(INSTALLKERNEL) or' | 351 | echo ' Install using (your) ~/bin/$(INSTALLKERNEL) or' |
| 345 | echo ' (distribution) /sbin/$(INSTALLKERNEL) or' | 352 | echo ' (distribution) /sbin/$(INSTALLKERNEL) or' |
| 346 | echo ' install to $$(INSTALL_PATH) and run lilo' | 353 | echo ' install to $$(INSTALL_PATH) and run lilo' |
| 354 | echo ' vdso_install - Install unstripped vdso.so to $$(INSTALL_MOD_PATH)/vdso' | ||
| 347 | endef | 355 | endef |
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild index fe74c0d1e485..eb0f43f3e3f1 100644 --- a/arch/arm/include/asm/Kbuild +++ b/arch/arm/include/asm/Kbuild | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | 1 | ||
| 2 | 2 | ||
| 3 | generic-y += auxvec.h | ||
| 4 | generic-y += bitsperlong.h | 3 | generic-y += bitsperlong.h |
| 5 | generic-y += cputime.h | 4 | generic-y += cputime.h |
| 6 | generic-y += current.h | 5 | generic-y += current.h |
diff --git a/arch/arm/include/asm/auxvec.h b/arch/arm/include/asm/auxvec.h new file mode 100644 index 000000000000..fbd388c46299 --- /dev/null +++ b/arch/arm/include/asm/auxvec.h | |||
| @@ -0,0 +1 @@ | |||
| #include <uapi/asm/auxvec.h> | |||
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index afb9cafd3786..e2f4781630f6 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h | |||
| @@ -1,7 +1,9 @@ | |||
| 1 | #ifndef __ASMARM_ELF_H | 1 | #ifndef __ASMARM_ELF_H |
| 2 | #define __ASMARM_ELF_H | 2 | #define __ASMARM_ELF_H |
| 3 | 3 | ||
| 4 | #include <asm/auxvec.h> | ||
| 4 | #include <asm/hwcap.h> | 5 | #include <asm/hwcap.h> |
| 6 | #include <asm/vdso_datapage.h> | ||
| 5 | 7 | ||
| 6 | /* | 8 | /* |
| 7 | * ELF register definitions.. | 9 | * ELF register definitions.. |
| @@ -115,7 +117,7 @@ int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); | |||
| 115 | the loader. We need to make sure that it is out of the way of the program | 117 | the loader. We need to make sure that it is out of the way of the program |
| 116 | that it will "exec", and that there is sufficient room for the brk. */ | 118 | that it will "exec", and that there is sufficient room for the brk. */ |
| 117 | 119 | ||
| 118 | #define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3) | 120 | #define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) |
| 119 | 121 | ||
| 120 | /* When the program starts, a1 contains a pointer to a function to be | 122 | /* When the program starts, a1 contains a pointer to a function to be |
| 121 | registered with atexit, as per the SVR4 ABI. A value of 0 means we | 123 | registered with atexit, as per the SVR4 ABI. A value of 0 means we |
| @@ -130,6 +132,13 @@ extern unsigned long arch_randomize_brk(struct mm_struct *mm); | |||
| 130 | #define arch_randomize_brk arch_randomize_brk | 132 | #define arch_randomize_brk arch_randomize_brk |
| 131 | 133 | ||
| 132 | #ifdef CONFIG_MMU | 134 | #ifdef CONFIG_MMU |
| 135 | #ifdef CONFIG_VDSO | ||
| 136 | #define ARCH_DLINFO \ | ||
| 137 | do { \ | ||
| 138 | NEW_AUX_ENT(AT_SYSINFO_EHDR, \ | ||
| 139 | (elf_addr_t)current->mm->context.vdso); \ | ||
| 140 | } while (0) | ||
| 141 | #endif | ||
| 133 | #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 | 142 | #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 |
| 134 | struct linux_binprm; | 143 | struct linux_binprm; |
| 135 | int arch_setup_additional_pages(struct linux_binprm *, int); | 144 | int arch_setup_additional_pages(struct linux_binprm *, int); |
diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h index 64fd15159b7d..a5b47421059d 100644 --- a/arch/arm/include/asm/mmu.h +++ b/arch/arm/include/asm/mmu.h | |||
| @@ -11,6 +11,9 @@ typedef struct { | |||
| 11 | #endif | 11 | #endif |
| 12 | unsigned int vmalloc_seq; | 12 | unsigned int vmalloc_seq; |
| 13 | unsigned long sigpage; | 13 | unsigned long sigpage; |
| 14 | #ifdef CONFIG_VDSO | ||
| 15 | unsigned long vdso; | ||
| 16 | #endif | ||
| 14 | } mm_context_t; | 17 | } mm_context_t; |
| 15 | 18 | ||
| 16 | #ifdef CONFIG_CPU_HAS_ASID | 19 | #ifdef CONFIG_CPU_HAS_ASID |
diff --git a/arch/arm/include/asm/vdso.h b/arch/arm/include/asm/vdso.h new file mode 100644 index 000000000000..d0295f1dd1a3 --- /dev/null +++ b/arch/arm/include/asm/vdso.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | #ifndef __ASM_VDSO_H | ||
| 2 | #define __ASM_VDSO_H | ||
| 3 | |||
| 4 | #ifdef __KERNEL__ | ||
| 5 | |||
| 6 | #ifndef __ASSEMBLY__ | ||
| 7 | |||
| 8 | struct mm_struct; | ||
| 9 | |||
| 10 | #ifdef CONFIG_VDSO | ||
| 11 | |||
| 12 | void arm_install_vdso(struct mm_struct *mm, unsigned long addr); | ||
| 13 | |||
| 14 | extern char vdso_start, vdso_end; | ||
| 15 | |||
| 16 | extern unsigned int vdso_total_pages; | ||
| 17 | |||
| 18 | #else /* CONFIG_VDSO */ | ||
| 19 | |||
| 20 | static inline void arm_install_vdso(struct mm_struct *mm, unsigned long addr) | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | #define vdso_total_pages 0 | ||
| 25 | |||
| 26 | #endif /* CONFIG_VDSO */ | ||
| 27 | |||
| 28 | #endif /* __ASSEMBLY__ */ | ||
| 29 | |||
| 30 | #endif /* __KERNEL__ */ | ||
| 31 | |||
| 32 | #endif /* __ASM_VDSO_H */ | ||
diff --git a/arch/arm/include/asm/vdso_datapage.h b/arch/arm/include/asm/vdso_datapage.h new file mode 100644 index 000000000000..9be259442fca --- /dev/null +++ b/arch/arm/include/asm/vdso_datapage.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* | ||
| 2 | * Adapted from arm64 version. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 ARM Limited | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | #ifndef __ASM_VDSO_DATAPAGE_H | ||
| 19 | #define __ASM_VDSO_DATAPAGE_H | ||
| 20 | |||
| 21 | #ifdef __KERNEL__ | ||
| 22 | |||
| 23 | #ifndef __ASSEMBLY__ | ||
| 24 | |||
| 25 | #include <asm/page.h> | ||
| 26 | |||
| 27 | /* Try to be cache-friendly on systems that don't implement the | ||
| 28 | * generic timer: fit the unconditionally updated fields in the first | ||
| 29 | * 32 bytes. | ||
| 30 | */ | ||
| 31 | struct vdso_data { | ||
| 32 | u32 seq_count; /* sequence count - odd during updates */ | ||
| 33 | u16 tk_is_cntvct; /* fall back to syscall if false */ | ||
| 34 | u16 cs_shift; /* clocksource shift */ | ||
| 35 | u32 xtime_coarse_sec; /* coarse time */ | ||
| 36 | u32 xtime_coarse_nsec; | ||
| 37 | |||
| 38 | u32 wtm_clock_sec; /* wall to monotonic offset */ | ||
| 39 | u32 wtm_clock_nsec; | ||
| 40 | u32 xtime_clock_sec; /* CLOCK_REALTIME - seconds */ | ||
| 41 | u32 cs_mult; /* clocksource multiplier */ | ||
| 42 | |||
| 43 | u64 cs_cycle_last; /* last cycle value */ | ||
| 44 | u64 cs_mask; /* clocksource mask */ | ||
| 45 | |||
| 46 | u64 xtime_clock_snsec; /* CLOCK_REALTIME sub-ns base */ | ||
| 47 | u32 tz_minuteswest; /* timezone info for gettimeofday(2) */ | ||
| 48 | u32 tz_dsttime; | ||
| 49 | }; | ||
| 50 | |||
| 51 | union vdso_data_store { | ||
| 52 | struct vdso_data data; | ||
| 53 | u8 page[PAGE_SIZE]; | ||
| 54 | }; | ||
| 55 | |||
| 56 | #endif /* !__ASSEMBLY__ */ | ||
| 57 | |||
| 58 | #endif /* __KERNEL__ */ | ||
| 59 | |||
| 60 | #endif /* __ASM_VDSO_DATAPAGE_H */ | ||
diff --git a/arch/arm/include/uapi/asm/Kbuild b/arch/arm/include/uapi/asm/Kbuild index 70a1c9da30ca..a1c05f93d920 100644 --- a/arch/arm/include/uapi/asm/Kbuild +++ b/arch/arm/include/uapi/asm/Kbuild | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | # UAPI Header export list | 1 | # UAPI Header export list |
| 2 | include include/uapi/asm-generic/Kbuild.asm | 2 | include include/uapi/asm-generic/Kbuild.asm |
| 3 | 3 | ||
| 4 | header-y += auxvec.h | ||
| 4 | header-y += byteorder.h | 5 | header-y += byteorder.h |
| 5 | header-y += fcntl.h | 6 | header-y += fcntl.h |
| 6 | header-y += hwcap.h | 7 | header-y += hwcap.h |
diff --git a/arch/arm/include/uapi/asm/auxvec.h b/arch/arm/include/uapi/asm/auxvec.h new file mode 100644 index 000000000000..cb02a767a500 --- /dev/null +++ b/arch/arm/include/uapi/asm/auxvec.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef __ASM_AUXVEC_H | ||
| 2 | #define __ASM_AUXVEC_H | ||
| 3 | |||
| 4 | /* VDSO location */ | ||
| 5 | #define AT_SYSINFO_EHDR 33 | ||
| 6 | |||
| 7 | #endif | ||
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index b6544abe2f5e..ba5f83226011 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
| @@ -75,6 +75,7 @@ obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o | |||
| 75 | CFLAGS_pj4-cp0.o := -marm | 75 | CFLAGS_pj4-cp0.o := -marm |
| 76 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt | 76 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt |
| 77 | obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o | 77 | obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o |
| 78 | obj-$(CONFIG_VDSO) += vdso.o | ||
| 78 | 79 | ||
| 79 | ifneq ($(CONFIG_ARCH_EBSA110),y) | 80 | ifneq ($(CONFIG_ARCH_EBSA110),y) |
| 80 | obj-y += io.o | 81 | obj-y += io.o |
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 2d2d6087b9b1..9147008f0d51 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <asm/memory.h> | 25 | #include <asm/memory.h> |
| 26 | #include <asm/procinfo.h> | 26 | #include <asm/procinfo.h> |
| 27 | #include <asm/suspend.h> | 27 | #include <asm/suspend.h> |
| 28 | #include <asm/vdso_datapage.h> | ||
| 28 | #include <asm/hardware/cache-l2x0.h> | 29 | #include <asm/hardware/cache-l2x0.h> |
| 29 | #include <linux/kbuild.h> | 30 | #include <linux/kbuild.h> |
| 30 | 31 | ||
| @@ -210,5 +211,9 @@ int main(void) | |||
| 210 | #endif | 211 | #endif |
| 211 | DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr)); | 212 | DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr)); |
| 212 | #endif | 213 | #endif |
| 214 | BLANK(); | ||
| 215 | #ifdef CONFIG_VDSO | ||
| 216 | DEFINE(VDSO_DATA_SIZE, sizeof(union vdso_data_store)); | ||
| 217 | #endif | ||
| 213 | return 0; | 218 | return 0; |
| 214 | } | 219 | } |
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index f810a6cc3790..f192a2a41719 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
| @@ -334,7 +334,7 @@ const char *arch_vma_name(struct vm_area_struct *vma) | |||
| 334 | } | 334 | } |
| 335 | 335 | ||
| 336 | /* If possible, provide a placement hint at a random offset from the | 336 | /* If possible, provide a placement hint at a random offset from the |
| 337 | * stack for the signal page. | 337 | * stack for the sigpage and vdso pages. |
| 338 | */ | 338 | */ |
| 339 | static unsigned long sigpage_addr(const struct mm_struct *mm, | 339 | static unsigned long sigpage_addr(const struct mm_struct *mm, |
| 340 | unsigned int npages) | 340 | unsigned int npages) |
| @@ -378,6 +378,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
| 378 | { | 378 | { |
| 379 | struct mm_struct *mm = current->mm; | 379 | struct mm_struct *mm = current->mm; |
| 380 | struct vm_area_struct *vma; | 380 | struct vm_area_struct *vma; |
| 381 | unsigned long npages; | ||
| 381 | unsigned long addr; | 382 | unsigned long addr; |
| 382 | unsigned long hint; | 383 | unsigned long hint; |
| 383 | int ret = 0; | 384 | int ret = 0; |
| @@ -387,9 +388,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
| 387 | if (!signal_page) | 388 | if (!signal_page) |
| 388 | return -ENOMEM; | 389 | return -ENOMEM; |
| 389 | 390 | ||
| 391 | npages = 1; /* for sigpage */ | ||
| 392 | npages += vdso_total_pages; | ||
| 393 | |||
| 390 | down_write(&mm->mmap_sem); | 394 | down_write(&mm->mmap_sem); |
| 391 | hint = sigpage_addr(mm, 1); | 395 | hint = sigpage_addr(mm, npages); |
| 392 | addr = get_unmapped_area(NULL, hint, PAGE_SIZE, 0, 0); | 396 | addr = get_unmapped_area(NULL, hint, npages << PAGE_SHIFT, 0, 0); |
| 393 | if (IS_ERR_VALUE(addr)) { | 397 | if (IS_ERR_VALUE(addr)) { |
| 394 | ret = addr; | 398 | ret = addr; |
| 395 | goto up_fail; | 399 | goto up_fail; |
| @@ -406,6 +410,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
| 406 | 410 | ||
| 407 | mm->context.sigpage = addr; | 411 | mm->context.sigpage = addr; |
| 408 | 412 | ||
| 413 | /* Unlike the sigpage, failure to install the vdso is unlikely | ||
| 414 | * to be fatal to the process, so no error check needed | ||
| 415 | * here. | ||
| 416 | */ | ||
| 417 | arm_install_vdso(mm, addr + PAGE_SIZE); | ||
| 418 | |||
| 409 | up_fail: | 419 | up_fail: |
| 410 | up_write(&mm->mmap_sem); | 420 | up_write(&mm->mmap_sem); |
| 411 | return ret; | 421 | return ret; |
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 910bb1796946..6c777e908a24 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c | |||
| @@ -246,12 +246,9 @@ static int __get_cpu_architecture(void) | |||
| 246 | if (cpu_arch) | 246 | if (cpu_arch) |
| 247 | cpu_arch += CPU_ARCH_ARMv3; | 247 | cpu_arch += CPU_ARCH_ARMv3; |
| 248 | } else if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) { | 248 | } else if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) { |
| 249 | unsigned int mmfr0; | ||
| 250 | |||
| 251 | /* Revised CPUID format. Read the Memory Model Feature | 249 | /* Revised CPUID format. Read the Memory Model Feature |
| 252 | * Register 0 and check for VMSAv7 or PMSAv7 */ | 250 | * Register 0 and check for VMSAv7 or PMSAv7 */ |
| 253 | asm("mrc p15, 0, %0, c0, c1, 4" | 251 | unsigned int mmfr0 = read_cpuid_ext(CPUID_EXT_MMFR0); |
| 254 | : "=r" (mmfr0)); | ||
| 255 | if ((mmfr0 & 0x0000000f) >= 0x00000003 || | 252 | if ((mmfr0 & 0x0000000f) >= 0x00000003 || |
| 256 | (mmfr0 & 0x000000f0) >= 0x00000030) | 253 | (mmfr0 & 0x000000f0) >= 0x00000030) |
| 257 | cpu_arch = CPU_ARCH_ARMv7; | 254 | cpu_arch = CPU_ARCH_ARMv7; |
diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c new file mode 100644 index 000000000000..0d31d3ccab81 --- /dev/null +++ b/arch/arm/kernel/vdso.c | |||
| @@ -0,0 +1,337 @@ | |||
| 1 | /* | ||
| 2 | * Adapted from arm64 version. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 ARM Limited | ||
| 5 | * Copyright (C) 2015 Mentor Graphics Corporation. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/elf.h> | ||
| 21 | #include <linux/err.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/mm.h> | ||
| 24 | #include <linux/of.h> | ||
| 25 | #include <linux/printk.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/timekeeper_internal.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 29 | #include <asm/arch_timer.h> | ||
| 30 | #include <asm/barrier.h> | ||
| 31 | #include <asm/cacheflush.h> | ||
| 32 | #include <asm/page.h> | ||
| 33 | #include <asm/vdso.h> | ||
| 34 | #include <asm/vdso_datapage.h> | ||
| 35 | #include <clocksource/arm_arch_timer.h> | ||
| 36 | |||
| 37 | #define MAX_SYMNAME 64 | ||
| 38 | |||
| 39 | static struct page **vdso_text_pagelist; | ||
| 40 | |||
| 41 | /* Total number of pages needed for the data and text portions of the VDSO. */ | ||
| 42 | unsigned int vdso_total_pages __read_mostly; | ||
| 43 | |||
| 44 | /* | ||
| 45 | * The VDSO data page. | ||
| 46 | */ | ||
| 47 | static union vdso_data_store vdso_data_store __page_aligned_data; | ||
| 48 | static struct vdso_data *vdso_data = &vdso_data_store.data; | ||
| 49 | |||
| 50 | static struct page *vdso_data_page; | ||
| 51 | static struct vm_special_mapping vdso_data_mapping = { | ||
| 52 | .name = "[vvar]", | ||
| 53 | .pages = &vdso_data_page, | ||
| 54 | }; | ||
| 55 | |||
| 56 | static struct vm_special_mapping vdso_text_mapping = { | ||
| 57 | .name = "[vdso]", | ||
| 58 | }; | ||
| 59 | |||
| 60 | struct elfinfo { | ||
| 61 | Elf32_Ehdr *hdr; /* ptr to ELF */ | ||
| 62 | Elf32_Sym *dynsym; /* ptr to .dynsym section */ | ||
| 63 | unsigned long dynsymsize; /* size of .dynsym section */ | ||
| 64 | char *dynstr; /* ptr to .dynstr section */ | ||
| 65 | }; | ||
| 66 | |||
| 67 | /* Cached result of boot-time check for whether the arch timer exists, | ||
| 68 | * and if so, whether the virtual counter is useable. | ||
| 69 | */ | ||
| 70 | static bool cntvct_ok __read_mostly; | ||
| 71 | |||
| 72 | static bool __init cntvct_functional(void) | ||
| 73 | { | ||
| 74 | struct device_node *np; | ||
| 75 | bool ret = false; | ||
| 76 | |||
| 77 | if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER)) | ||
| 78 | goto out; | ||
| 79 | |||
| 80 | /* The arm_arch_timer core should export | ||
| 81 | * arch_timer_use_virtual or similar so we don't have to do | ||
| 82 | * this. | ||
| 83 | */ | ||
| 84 | np = of_find_compatible_node(NULL, NULL, "arm,armv7-timer"); | ||
| 85 | if (!np) | ||
| 86 | goto out_put; | ||
| 87 | |||
| 88 | if (of_property_read_bool(np, "arm,cpu-registers-not-fw-configured")) | ||
| 89 | goto out_put; | ||
| 90 | |||
| 91 | ret = true; | ||
| 92 | |||
| 93 | out_put: | ||
| 94 | of_node_put(np); | ||
| 95 | out: | ||
| 96 | return ret; | ||
| 97 | } | ||
| 98 | |||
| 99 | static void * __init find_section(Elf32_Ehdr *ehdr, const char *name, | ||
| 100 | unsigned long *size) | ||
| 101 | { | ||
| 102 | Elf32_Shdr *sechdrs; | ||
| 103 | unsigned int i; | ||
| 104 | char *secnames; | ||
| 105 | |||
| 106 | /* Grab section headers and strings so we can tell who is who */ | ||
| 107 | sechdrs = (void *)ehdr + ehdr->e_shoff; | ||
| 108 | secnames = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset; | ||
| 109 | |||
| 110 | /* Find the section they want */ | ||
| 111 | for (i = 1; i < ehdr->e_shnum; i++) { | ||
| 112 | if (strcmp(secnames + sechdrs[i].sh_name, name) == 0) { | ||
| 113 | if (size) | ||
| 114 | *size = sechdrs[i].sh_size; | ||
| 115 | return (void *)ehdr + sechdrs[i].sh_offset; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | if (size) | ||
| 120 | *size = 0; | ||
| 121 | return NULL; | ||
| 122 | } | ||
| 123 | |||
| 124 | static Elf32_Sym * __init find_symbol(struct elfinfo *lib, const char *symname) | ||
| 125 | { | ||
| 126 | unsigned int i; | ||
| 127 | |||
| 128 | for (i = 0; i < (lib->dynsymsize / sizeof(Elf32_Sym)); i++) { | ||
| 129 | char name[MAX_SYMNAME], *c; | ||
| 130 | |||
| 131 | if (lib->dynsym[i].st_name == 0) | ||
| 132 | continue; | ||
| 133 | strlcpy(name, lib->dynstr + lib->dynsym[i].st_name, | ||
| 134 | MAX_SYMNAME); | ||
| 135 | c = strchr(name, '@'); | ||
| 136 | if (c) | ||
| 137 | *c = 0; | ||
| 138 | if (strcmp(symname, name) == 0) | ||
| 139 | return &lib->dynsym[i]; | ||
| 140 | } | ||
| 141 | return NULL; | ||
| 142 | } | ||
| 143 | |||
| 144 | static void __init vdso_nullpatch_one(struct elfinfo *lib, const char *symname) | ||
| 145 | { | ||
| 146 | Elf32_Sym *sym; | ||
| 147 | |||
| 148 | sym = find_symbol(lib, symname); | ||
| 149 | if (!sym) | ||
| 150 | return; | ||
| 151 | |||
| 152 | sym->st_name = 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static void __init patch_vdso(void *ehdr) | ||
| 156 | { | ||
| 157 | struct elfinfo einfo; | ||
| 158 | |||
| 159 | einfo = (struct elfinfo) { | ||
| 160 | .hdr = ehdr, | ||
| 161 | }; | ||
| 162 | |||
| 163 | einfo.dynsym = find_section(einfo.hdr, ".dynsym", &einfo.dynsymsize); | ||
| 164 | einfo.dynstr = find_section(einfo.hdr, ".dynstr", NULL); | ||
| 165 | |||
| 166 | /* If the virtual counter is absent or non-functional we don't | ||
| 167 | * want programs to incur the slight additional overhead of | ||
| 168 | * dispatching through the VDSO only to fall back to syscalls. | ||
| 169 | */ | ||
| 170 | if (!cntvct_ok) { | ||
| 171 | vdso_nullpatch_one(&einfo, "__vdso_gettimeofday"); | ||
| 172 | vdso_nullpatch_one(&einfo, "__vdso_clock_gettime"); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | static int __init vdso_init(void) | ||
| 177 | { | ||
| 178 | unsigned int text_pages; | ||
| 179 | int i; | ||
| 180 | |||
| 181 | if (memcmp(&vdso_start, "\177ELF", 4)) { | ||
| 182 | pr_err("VDSO is not a valid ELF object!\n"); | ||
| 183 | return -ENOEXEC; | ||
| 184 | } | ||
| 185 | |||
| 186 | text_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT; | ||
| 187 | pr_debug("vdso: %i text pages at base %p\n", text_pages, &vdso_start); | ||
| 188 | |||
| 189 | /* Allocate the VDSO text pagelist */ | ||
| 190 | vdso_text_pagelist = kcalloc(text_pages, sizeof(struct page *), | ||
| 191 | GFP_KERNEL); | ||
| 192 | if (vdso_text_pagelist == NULL) | ||
| 193 | return -ENOMEM; | ||
| 194 | |||
| 195 | /* Grab the VDSO data page. */ | ||
| 196 | vdso_data_page = virt_to_page(vdso_data); | ||
| 197 | |||
| 198 | /* Grab the VDSO text pages. */ | ||
| 199 | for (i = 0; i < text_pages; i++) { | ||
| 200 | struct page *page; | ||
| 201 | |||
| 202 | page = virt_to_page(&vdso_start + i * PAGE_SIZE); | ||
| 203 | vdso_text_pagelist[i] = page; | ||
| 204 | } | ||
| 205 | |||
| 206 | vdso_text_mapping.pages = vdso_text_pagelist; | ||
| 207 | |||
| 208 | vdso_total_pages = 1; /* for the data/vvar page */ | ||
| 209 | vdso_total_pages += text_pages; | ||
| 210 | |||
| 211 | cntvct_ok = cntvct_functional(); | ||
| 212 | |||
| 213 | patch_vdso(&vdso_start); | ||
| 214 | |||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | arch_initcall(vdso_init); | ||
| 218 | |||
| 219 | static int install_vvar(struct mm_struct *mm, unsigned long addr) | ||
| 220 | { | ||
| 221 | struct vm_area_struct *vma; | ||
| 222 | |||
| 223 | vma = _install_special_mapping(mm, addr, PAGE_SIZE, | ||
| 224 | VM_READ | VM_MAYREAD, | ||
| 225 | &vdso_data_mapping); | ||
| 226 | |||
| 227 | return IS_ERR(vma) ? PTR_ERR(vma) : 0; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* assumes mmap_sem is write-locked */ | ||
| 231 | void arm_install_vdso(struct mm_struct *mm, unsigned long addr) | ||
| 232 | { | ||
| 233 | struct vm_area_struct *vma; | ||
| 234 | unsigned long len; | ||
| 235 | |||
| 236 | mm->context.vdso = 0; | ||
| 237 | |||
| 238 | if (vdso_text_pagelist == NULL) | ||
| 239 | return; | ||
| 240 | |||
| 241 | if (install_vvar(mm, addr)) | ||
| 242 | return; | ||
| 243 | |||
| 244 | /* Account for vvar page. */ | ||
| 245 | addr += PAGE_SIZE; | ||
| 246 | len = (vdso_total_pages - 1) << PAGE_SHIFT; | ||
| 247 | |||
| 248 | vma = _install_special_mapping(mm, addr, len, | ||
| 249 | VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, | ||
| 250 | &vdso_text_mapping); | ||
| 251 | |||
| 252 | if (!IS_ERR(vma)) | ||
| 253 | mm->context.vdso = addr; | ||
| 254 | } | ||
| 255 | |||
| 256 | static void vdso_write_begin(struct vdso_data *vdata) | ||
| 257 | { | ||
| 258 | ++vdso_data->seq_count; | ||
| 259 | smp_wmb(); /* Pairs with smp_rmb in vdso_read_retry */ | ||
| 260 | } | ||
| 261 | |||
| 262 | static void vdso_write_end(struct vdso_data *vdata) | ||
| 263 | { | ||
| 264 | smp_wmb(); /* Pairs with smp_rmb in vdso_read_begin */ | ||
| 265 | ++vdso_data->seq_count; | ||
| 266 | } | ||
| 267 | |||
| 268 | static bool tk_is_cntvct(const struct timekeeper *tk) | ||
| 269 | { | ||
| 270 | if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER)) | ||
| 271 | return false; | ||
| 272 | |||
| 273 | if (strcmp(tk->tkr.clock->name, "arch_sys_counter") != 0) | ||
| 274 | return false; | ||
| 275 | |||
| 276 | return true; | ||
| 277 | } | ||
| 278 | |||
| 279 | /** | ||
| 280 | * update_vsyscall - update the vdso data page | ||
| 281 | * | ||
| 282 | * Increment the sequence counter, making it odd, indicating to | ||
| 283 | * userspace that an update is in progress. Update the fields used | ||
| 284 | * for coarse clocks and, if the architected system timer is in use, | ||
| 285 | * the fields used for high precision clocks. Increment the sequence | ||
| 286 | * counter again, making it even, indicating to userspace that the | ||
| 287 | * update is finished. | ||
| 288 | * | ||
| 289 | * Userspace is expected to sample seq_count before reading any other | ||
| 290 | * fields from the data page. If seq_count is odd, userspace is | ||
| 291 | * expected to wait until it becomes even. After copying data from | ||
| 292 | * the page, userspace must sample seq_count again; if it has changed | ||
| 293 | * from its previous value, userspace must retry the whole sequence. | ||
| 294 | * | ||
| 295 | * Calls to update_vsyscall are serialized by the timekeeping core. | ||
| 296 | */ | ||
| 297 | void update_vsyscall(struct timekeeper *tk) | ||
| 298 | { | ||
| 299 | struct timespec xtime_coarse; | ||
| 300 | struct timespec64 *wtm = &tk->wall_to_monotonic; | ||
| 301 | |||
| 302 | if (!cntvct_ok) { | ||
| 303 | /* The entry points have been zeroed, so there is no | ||
| 304 | * point in updating the data page. | ||
| 305 | */ | ||
| 306 | return; | ||
| 307 | } | ||
| 308 | |||
| 309 | vdso_write_begin(vdso_data); | ||
| 310 | |||
| 311 | xtime_coarse = __current_kernel_time(); | ||
| 312 | vdso_data->tk_is_cntvct = tk_is_cntvct(tk); | ||
| 313 | vdso_data->xtime_coarse_sec = xtime_coarse.tv_sec; | ||
| 314 | vdso_data->xtime_coarse_nsec = xtime_coarse.tv_nsec; | ||
| 315 | vdso_data->wtm_clock_sec = wtm->tv_sec; | ||
| 316 | vdso_data->wtm_clock_nsec = wtm->tv_nsec; | ||
| 317 | |||
| 318 | if (vdso_data->tk_is_cntvct) { | ||
| 319 | vdso_data->cs_cycle_last = tk->tkr.cycle_last; | ||
| 320 | vdso_data->xtime_clock_sec = tk->xtime_sec; | ||
| 321 | vdso_data->xtime_clock_snsec = tk->tkr.xtime_nsec; | ||
| 322 | vdso_data->cs_mult = tk->tkr.mult; | ||
| 323 | vdso_data->cs_shift = tk->tkr.shift; | ||
| 324 | vdso_data->cs_mask = tk->tkr.mask; | ||
| 325 | } | ||
| 326 | |||
| 327 | vdso_write_end(vdso_data); | ||
| 328 | |||
| 329 | flush_dcache_page(virt_to_page(vdso_data)); | ||
| 330 | } | ||
| 331 | |||
| 332 | void update_vsyscall_tz(void) | ||
| 333 | { | ||
| 334 | vdso_data->tz_minuteswest = sys_tz.tz_minuteswest; | ||
| 335 | vdso_data->tz_dsttime = sys_tz.tz_dsttime; | ||
| 336 | flush_dcache_page(virt_to_page(vdso_data)); | ||
| 337 | } | ||
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 3c2509b4b694..4be537977040 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig | |||
| @@ -42,6 +42,7 @@ if ARCH_VEXPRESS | |||
| 42 | config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA | 42 | config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA |
| 43 | bool "Enable A5 and A9 only errata work-arounds" | 43 | bool "Enable A5 and A9 only errata work-arounds" |
| 44 | default y | 44 | default y |
| 45 | select ARM_ERRATA_643719 if SMP | ||
| 45 | select ARM_ERRATA_720789 | 46 | select ARM_ERRATA_720789 |
| 46 | select PL310_ERRATA_753970 if CACHE_L2X0 | 47 | select PL310_ERRATA_753970 if CACHE_L2X0 |
| 47 | help | 48 | help |
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 9b4f29e595a4..b7644310236b 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig | |||
| @@ -738,7 +738,7 @@ config CPU_ICACHE_DISABLE | |||
| 738 | 738 | ||
| 739 | config CPU_DCACHE_DISABLE | 739 | config CPU_DCACHE_DISABLE |
| 740 | bool "Disable D-Cache (C-bit)" | 740 | bool "Disable D-Cache (C-bit)" |
| 741 | depends on CPU_CP15 | 741 | depends on CPU_CP15 && !SMP |
| 742 | help | 742 | help |
| 743 | Say Y here to disable the processor data cache. Unless | 743 | Say Y here to disable the processor data cache. Unless |
| 744 | you have a reason not to or are unsure, say N. | 744 | you have a reason not to or are unsure, say N. |
| @@ -825,6 +825,20 @@ config KUSER_HELPERS | |||
| 825 | Say N here only if you are absolutely certain that you do not | 825 | Say N here only if you are absolutely certain that you do not |
| 826 | need these helpers; otherwise, the safe option is to say Y. | 826 | need these helpers; otherwise, the safe option is to say Y. |
| 827 | 827 | ||
| 828 | config VDSO | ||
| 829 | bool "Enable VDSO for acceleration of some system calls" | ||
| 830 | depends on AEABI && MMU | ||
| 831 | default y if ARM_ARCH_TIMER | ||
| 832 | select GENERIC_TIME_VSYSCALL | ||
| 833 | help | ||
| 834 | Place in the process address space an ELF shared object | ||
| 835 | providing fast implementations of gettimeofday and | ||
| 836 | clock_gettime. Systems that implement the ARM architected | ||
| 837 | timer will receive maximum benefit. | ||
| 838 | |||
| 839 | You must have glibc 2.22 or later for programs to seamlessly | ||
| 840 | take advantage of this. | ||
| 841 | |||
| 828 | config DMA_CACHE_RWFO | 842 | config DMA_CACHE_RWFO |
| 829 | bool "Enable read/write for ownership DMA cache maintenance" | 843 | bool "Enable read/write for ownership DMA cache maintenance" |
| 830 | depends on CPU_V6K && SMP | 844 | depends on CPU_V6K && SMP |
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 8b933dc43e24..e309c8f35af5 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c | |||
| @@ -1131,23 +1131,22 @@ static void __init l2c310_of_parse(const struct device_node *np, | |||
| 1131 | } | 1131 | } |
| 1132 | 1132 | ||
| 1133 | ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K); | 1133 | ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K); |
| 1134 | if (ret) | 1134 | if (!ret) { |
| 1135 | return; | 1135 | switch (assoc) { |
| 1136 | 1136 | case 16: | |
| 1137 | switch (assoc) { | 1137 | *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK; |
| 1138 | case 16: | 1138 | *aux_val |= L310_AUX_CTRL_ASSOCIATIVITY_16; |
| 1139 | *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK; | 1139 | *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK; |
| 1140 | *aux_val |= L310_AUX_CTRL_ASSOCIATIVITY_16; | 1140 | break; |
| 1141 | *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK; | 1141 | case 8: |
| 1142 | break; | 1142 | *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK; |
| 1143 | case 8: | 1143 | *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK; |
| 1144 | *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK; | 1144 | break; |
| 1145 | *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK; | 1145 | default: |
| 1146 | break; | 1146 | pr_err("L2C-310 OF cache associativity %d invalid, only 8 or 16 permitted\n", |
| 1147 | default: | 1147 | assoc); |
| 1148 | pr_err("L2C-310 OF cache associativity %d invalid, only 8 or 16 permitted\n", | 1148 | break; |
| 1149 | assoc); | 1149 | } |
| 1150 | break; | ||
| 1151 | } | 1150 | } |
| 1152 | 1151 | ||
| 1153 | prefetch = l2x0_saved_regs.prefetch_ctrl; | 1152 | prefetch = l2x0_saved_regs.prefetch_ctrl; |
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index b8fe72080108..3866f81c70bc 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c | |||
| @@ -171,7 +171,7 @@ static int __dma_supported(struct device *dev, u64 mask, bool warn) | |||
| 171 | */ | 171 | */ |
| 172 | if (sizeof(mask) != sizeof(dma_addr_t) && | 172 | if (sizeof(mask) != sizeof(dma_addr_t) && |
| 173 | mask > (dma_addr_t)~0 && | 173 | mask > (dma_addr_t)~0 && |
| 174 | dma_to_pfn(dev, ~0) < max_pfn) { | 174 | dma_to_pfn(dev, ~0) < max_pfn - 1) { |
| 175 | if (warn) { | 175 | if (warn) { |
| 176 | dev_warn(dev, "Coherent DMA mask %#llx is larger than dma_addr_t allows\n", | 176 | dev_warn(dev, "Coherent DMA mask %#llx is larger than dma_addr_t allows\n", |
| 177 | mask); | 177 | mask); |
| @@ -1150,13 +1150,28 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, | |||
| 1150 | gfp |= __GFP_NOWARN | __GFP_HIGHMEM; | 1150 | gfp |= __GFP_NOWARN | __GFP_HIGHMEM; |
| 1151 | 1151 | ||
| 1152 | while (count) { | 1152 | while (count) { |
| 1153 | int j, order = __fls(count); | 1153 | int j, order; |
| 1154 | |||
| 1155 | for (order = __fls(count); order > 0; --order) { | ||
| 1156 | /* | ||
| 1157 | * We do not want OOM killer to be invoked as long | ||
| 1158 | * as we can fall back to single pages, so we force | ||
| 1159 | * __GFP_NORETRY for orders higher than zero. | ||
| 1160 | */ | ||
| 1161 | pages[i] = alloc_pages(gfp | __GFP_NORETRY, order); | ||
| 1162 | if (pages[i]) | ||
| 1163 | break; | ||
| 1164 | } | ||
| 1154 | 1165 | ||
| 1155 | pages[i] = alloc_pages(gfp, order); | 1166 | if (!pages[i]) { |
| 1156 | while (!pages[i] && order) | 1167 | /* |
| 1157 | pages[i] = alloc_pages(gfp, --order); | 1168 | * Fall back to single page allocation. |
| 1158 | if (!pages[i]) | 1169 | * Might invoke OOM killer as last resort. |
| 1159 | goto error; | 1170 | */ |
| 1171 | pages[i] = alloc_pages(gfp, 0); | ||
| 1172 | if (!pages[i]) | ||
| 1173 | goto error; | ||
| 1174 | } | ||
| 1160 | 1175 | ||
| 1161 | if (order) { | 1176 | if (order) { |
| 1162 | split_page(pages[i], order); | 1177 | split_page(pages[i], order); |
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index a982dc3190df..6333d9c17875 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c | |||
| @@ -552,6 +552,7 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
| 552 | 552 | ||
| 553 | pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n", | 553 | pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n", |
| 554 | inf->name, fsr, addr); | 554 | inf->name, fsr, addr); |
| 555 | show_pte(current->mm, addr); | ||
| 555 | 556 | ||
| 556 | info.si_signo = inf->sig; | 557 | info.si_signo = inf->sig; |
| 557 | info.si_errno = 0; | 558 | info.si_errno = 0; |
diff --git a/arch/arm/mm/pageattr.c b/arch/arm/mm/pageattr.c index 004e35cdcfff..cf30daff8932 100644 --- a/arch/arm/mm/pageattr.c +++ b/arch/arm/mm/pageattr.c | |||
| @@ -49,7 +49,10 @@ static int change_memory_common(unsigned long addr, int numpages, | |||
| 49 | WARN_ON_ONCE(1); | 49 | WARN_ON_ONCE(1); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | if (!is_module_address(start) || !is_module_address(end - 1)) | 52 | if (start < MODULES_VADDR || start >= MODULES_END) |
| 53 | return -EINVAL; | ||
| 54 | |||
| 55 | if (end < MODULES_VADDR || start >= MODULES_END) | ||
| 53 | return -EINVAL; | 56 | return -EINVAL; |
| 54 | 57 | ||
| 55 | data.set_mask = set_mask; | 58 | data.set_mask = set_mask; |
diff --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S index 0a0b7a9167b6..ee5b66f847c4 100644 --- a/arch/arm/mm/proc-arm940.S +++ b/arch/arm/mm/proc-arm940.S | |||
| @@ -297,26 +297,16 @@ __arm940_setup: | |||
| 297 | mcr p15, 0, r0, c6, c0, 1 | 297 | mcr p15, 0, r0, c6, c0, 1 |
| 298 | 298 | ||
| 299 | ldr r0, =(CONFIG_DRAM_BASE & 0xFFFFF000) @ base[31:12] of RAM | 299 | ldr r0, =(CONFIG_DRAM_BASE & 0xFFFFF000) @ base[31:12] of RAM |
| 300 | ldr r1, =(CONFIG_DRAM_SIZE >> 12) @ size of RAM (must be >= 4KB) | 300 | ldr r7, =CONFIG_DRAM_SIZE >> 12 @ size of RAM (must be >= 4KB) |
| 301 | mov r2, #10 @ 11 is the minimum (4KB) | 301 | pr_val r3, r0, r7, #1 |
| 302 | 1: add r2, r2, #1 @ area size *= 2 | 302 | mcr p15, 0, r3, c6, c1, 0 @ set area 1, RAM |
| 303 | mov r1, r1, lsr #1 | 303 | mcr p15, 0, r3, c6, c1, 1 |
| 304 | bne 1b @ count not zero r-shift | ||
| 305 | orr r0, r0, r2, lsl #1 @ the area register value | ||
| 306 | orr r0, r0, #1 @ set enable bit | ||
| 307 | mcr p15, 0, r0, c6, c1, 0 @ set area 1, RAM | ||
| 308 | mcr p15, 0, r0, c6, c1, 1 | ||
| 309 | 304 | ||
| 310 | ldr r0, =(CONFIG_FLASH_MEM_BASE & 0xFFFFF000) @ base[31:12] of FLASH | 305 | ldr r0, =(CONFIG_FLASH_MEM_BASE & 0xFFFFF000) @ base[31:12] of FLASH |
| 311 | ldr r1, =(CONFIG_FLASH_SIZE >> 12) @ size of FLASH (must be >= 4KB) | 306 | ldr r7, =CONFIG_FLASH_SIZE @ size of FLASH (must be >= 4KB) |
| 312 | mov r2, #10 @ 11 is the minimum (4KB) | 307 | pr_val r3, r0, r6, #1 |
| 313 | 1: add r2, r2, #1 @ area size *= 2 | 308 | mcr p15, 0, r3, c6, c2, 0 @ set area 2, ROM/FLASH |
| 314 | mov r1, r1, lsr #1 | 309 | mcr p15, 0, r3, c6, c2, 1 |
| 315 | bne 1b @ count not zero r-shift | ||
| 316 | orr r0, r0, r2, lsl #1 @ the area register value | ||
| 317 | orr r0, r0, #1 @ set enable bit | ||
| 318 | mcr p15, 0, r0, c6, c2, 0 @ set area 2, ROM/FLASH | ||
| 319 | mcr p15, 0, r0, c6, c2, 1 | ||
| 320 | 310 | ||
| 321 | mov r0, #0x06 | 311 | mov r0, #0x06 |
| 322 | mcr p15, 0, r0, c2, c0, 0 @ Region 1&2 cacheable | 312 | mcr p15, 0, r0, c2, c0, 0 @ Region 1&2 cacheable |
diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S index c85b40d2117e..7361837edc31 100644 --- a/arch/arm/mm/proc-arm946.S +++ b/arch/arm/mm/proc-arm946.S | |||
| @@ -343,24 +343,14 @@ __arm946_setup: | |||
| 343 | mcr p15, 0, r0, c6, c0, 0 @ set region 0, default | 343 | mcr p15, 0, r0, c6, c0, 0 @ set region 0, default |
| 344 | 344 | ||
| 345 | ldr r0, =(CONFIG_DRAM_BASE & 0xFFFFF000) @ base[31:12] of RAM | 345 | ldr r0, =(CONFIG_DRAM_BASE & 0xFFFFF000) @ base[31:12] of RAM |
| 346 | ldr r1, =(CONFIG_DRAM_SIZE >> 12) @ size of RAM (must be >= 4KB) | 346 | ldr r7, =CONFIG_DRAM_SIZE @ size of RAM (must be >= 4KB) |
| 347 | mov r2, #10 @ 11 is the minimum (4KB) | 347 | pr_val r3, r0, r7, #1 |
| 348 | 1: add r2, r2, #1 @ area size *= 2 | 348 | mcr p15, 0, r3, c6, c1, 0 |
| 349 | mov r1, r1, lsr #1 | ||
| 350 | bne 1b @ count not zero r-shift | ||
| 351 | orr r0, r0, r2, lsl #1 @ the region register value | ||
| 352 | orr r0, r0, #1 @ set enable bit | ||
| 353 | mcr p15, 0, r0, c6, c1, 0 @ set region 1, RAM | ||
| 354 | 349 | ||
| 355 | ldr r0, =(CONFIG_FLASH_MEM_BASE & 0xFFFFF000) @ base[31:12] of FLASH | 350 | ldr r0, =(CONFIG_FLASH_MEM_BASE & 0xFFFFF000) @ base[31:12] of FLASH |
| 356 | ldr r1, =(CONFIG_FLASH_SIZE >> 12) @ size of FLASH (must be >= 4KB) | 351 | ldr r7, =CONFIG_FLASH_SIZE @ size of FLASH (must be >= 4KB) |
| 357 | mov r2, #10 @ 11 is the minimum (4KB) | 352 | pr_val r3, r0, r7, #1 |
| 358 | 1: add r2, r2, #1 @ area size *= 2 | 353 | mcr p15, 0, r3, c6, c2, 0 |
| 359 | mov r1, r1, lsr #1 | ||
| 360 | bne 1b @ count not zero r-shift | ||
| 361 | orr r0, r0, r2, lsl #1 @ the region register value | ||
| 362 | orr r0, r0, #1 @ set enable bit | ||
| 363 | mcr p15, 0, r0, c6, c2, 0 @ set region 2, ROM/FLASH | ||
| 364 | 354 | ||
| 365 | mov r0, #0x06 | 355 | mov r0, #0x06 |
| 366 | mcr p15, 0, r0, c2, c0, 0 @ region 1,2 d-cacheable | 356 | mcr p15, 0, r0, c2, c0, 0 @ region 1,2 d-cacheable |
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S index 0f13b5f9281e..c671f345266a 100644 --- a/arch/arm/mm/proc-macros.S +++ b/arch/arm/mm/proc-macros.S | |||
| @@ -335,3 +335,27 @@ ENTRY(\name\()_tlb_fns) | |||
| 335 | .macro initfn, func, base | 335 | .macro initfn, func, base |
| 336 | .long \func - \base | 336 | .long \func - \base |
| 337 | .endm | 337 | .endm |
| 338 | |||
| 339 | /* | ||
| 340 | * Macro to calculate the log2 size for the protection region | ||
| 341 | * registers. This calculates rd = log2(size) - 1. tmp must | ||
| 342 | * not be the same register as rd. | ||
| 343 | */ | ||
| 344 | .macro pr_sz, rd, size, tmp | ||
| 345 | mov \tmp, \size, lsr #12 | ||
| 346 | mov \rd, #11 | ||
| 347 | 1: movs \tmp, \tmp, lsr #1 | ||
| 348 | addne \rd, \rd, #1 | ||
| 349 | bne 1b | ||
| 350 | .endm | ||
| 351 | |||
| 352 | /* | ||
| 353 | * Macro to generate a protection region register value | ||
| 354 | * given a pre-masked address, size, and enable bit. | ||
| 355 | * Corrupts size. | ||
| 356 | */ | ||
| 357 | .macro pr_val, dest, addr, size, enable | ||
| 358 | pr_sz \dest, \size, \size @ calculate log2(size) - 1 | ||
| 359 | orr \dest, \addr, \dest, lsl #1 @ mask in the region size | ||
| 360 | orr \dest, \dest, \enable | ||
| 361 | .endm | ||
diff --git a/arch/arm/vdso/.gitignore b/arch/arm/vdso/.gitignore new file mode 100644 index 000000000000..f8b69d84238e --- /dev/null +++ b/arch/arm/vdso/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| vdso.lds | |||
diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile new file mode 100644 index 000000000000..bab0a8be7924 --- /dev/null +++ b/arch/arm/vdso/Makefile | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | hostprogs-y := vdsomunge | ||
| 2 | |||
| 3 | obj-vdso := vgettimeofday.o datapage.o | ||
| 4 | |||
| 5 | # Build rules | ||
| 6 | targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.so.raw vdso.lds | ||
| 7 | obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) | ||
| 8 | |||
| 9 | ccflags-y := -shared -fPIC -fno-common -fno-builtin -fno-stack-protector | ||
| 10 | ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 -DDISABLE_BRANCH_PROFILING | ||
| 11 | ccflags-y += -Wl,--no-undefined $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) | ||
| 12 | |||
| 13 | obj-y += vdso.o | ||
| 14 | extra-y += vdso.lds | ||
| 15 | CPPFLAGS_vdso.lds += -P -C -U$(ARCH) | ||
| 16 | |||
| 17 | CFLAGS_REMOVE_vdso.o = -pg | ||
| 18 | |||
| 19 | # Force -O2 to avoid libgcc dependencies | ||
| 20 | CFLAGS_REMOVE_vgettimeofday.o = -pg -Os | ||
| 21 | CFLAGS_vgettimeofday.o = -O2 | ||
| 22 | |||
| 23 | # Disable gcov profiling for VDSO code | ||
| 24 | GCOV_PROFILE := n | ||
| 25 | |||
| 26 | # Force dependency | ||
| 27 | $(obj)/vdso.o : $(obj)/vdso.so | ||
| 28 | |||
| 29 | # Link rule for the .so file | ||
| 30 | $(obj)/vdso.so.raw: $(src)/vdso.lds $(obj-vdso) FORCE | ||
| 31 | $(call if_changed,vdsold) | ||
| 32 | |||
| 33 | $(obj)/vdso.so.dbg: $(obj)/vdso.so.raw $(obj)/vdsomunge FORCE | ||
| 34 | $(call if_changed,vdsomunge) | ||
| 35 | |||
| 36 | # Strip rule for the .so file | ||
| 37 | $(obj)/%.so: OBJCOPYFLAGS := -S | ||
| 38 | $(obj)/%.so: $(obj)/%.so.dbg FORCE | ||
| 39 | $(call if_changed,objcopy) | ||
| 40 | |||
| 41 | # Actual build commands | ||
| 42 | quiet_cmd_vdsold = VDSO $@ | ||
| 43 | cmd_vdsold = $(CC) $(c_flags) -Wl,-T $(filter %.lds,$^) $(filter %.o,$^) \ | ||
| 44 | $(call cc-ldoption, -Wl$(comma)--build-id) \ | ||
| 45 | -Wl,-Bsymbolic -Wl,-z,max-page-size=4096 \ | ||
| 46 | -Wl,-z,common-page-size=4096 -o $@ | ||
| 47 | |||
| 48 | quiet_cmd_vdsomunge = MUNGE $@ | ||
| 49 | cmd_vdsomunge = $(objtree)/$(obj)/vdsomunge $< $@ | ||
| 50 | |||
| 51 | # | ||
| 52 | # Install the unstripped copy of vdso.so.dbg. If our toolchain | ||
| 53 | # supports build-id, install .build-id links as well. | ||
| 54 | # | ||
| 55 | # Cribbed from arch/x86/vdso/Makefile. | ||
| 56 | # | ||
| 57 | quiet_cmd_vdso_install = INSTALL $< | ||
| 58 | define cmd_vdso_install | ||
| 59 | cp $< "$(MODLIB)/vdso/vdso.so"; \ | ||
| 60 | if readelf -n $< | grep -q 'Build ID'; then \ | ||
| 61 | buildid=`readelf -n $< |grep 'Build ID' |sed -e 's/^.*Build ID: \(.*\)$$/\1/'`; \ | ||
| 62 | first=`echo $$buildid | cut -b-2`; \ | ||
| 63 | last=`echo $$buildid | cut -b3-`; \ | ||
| 64 | mkdir -p "$(MODLIB)/vdso/.build-id/$$first"; \ | ||
| 65 | ln -sf "../../vdso.so" "$(MODLIB)/vdso/.build-id/$$first/$$last.debug"; \ | ||
| 66 | fi | ||
| 67 | endef | ||
| 68 | |||
| 69 | $(MODLIB)/vdso: FORCE | ||
| 70 | @mkdir -p $(MODLIB)/vdso | ||
| 71 | |||
| 72 | PHONY += vdso_install | ||
| 73 | vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso FORCE | ||
| 74 | $(call cmd,vdso_install) | ||
diff --git a/arch/arm/vdso/datapage.S b/arch/arm/vdso/datapage.S new file mode 100644 index 000000000000..a2e60367931b --- /dev/null +++ b/arch/arm/vdso/datapage.S | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #include <linux/linkage.h> | ||
| 2 | #include <asm/asm-offsets.h> | ||
| 3 | |||
| 4 | .align 2 | ||
| 5 | .L_vdso_data_ptr: | ||
| 6 | .long _start - . - VDSO_DATA_SIZE | ||
| 7 | |||
| 8 | ENTRY(__get_datapage) | ||
| 9 | .fnstart | ||
| 10 | adr r0, .L_vdso_data_ptr | ||
| 11 | ldr r1, [r0] | ||
| 12 | add r0, r0, r1 | ||
| 13 | bx lr | ||
| 14 | .fnend | ||
| 15 | ENDPROC(__get_datapage) | ||
diff --git a/arch/arm/vdso/vdso.S b/arch/arm/vdso/vdso.S new file mode 100644 index 000000000000..b2b97e3e7bab --- /dev/null +++ b/arch/arm/vdso/vdso.S | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * Adapted from arm64 version. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 ARM Limited | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | * | ||
| 18 | * Author: Will Deacon <will.deacon@arm.com> | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/linkage.h> | ||
| 23 | #include <linux/const.h> | ||
| 24 | #include <asm/page.h> | ||
| 25 | |||
| 26 | __PAGE_ALIGNED_DATA | ||
| 27 | |||
| 28 | .globl vdso_start, vdso_end | ||
| 29 | .balign PAGE_SIZE | ||
| 30 | vdso_start: | ||
| 31 | .incbin "arch/arm/vdso/vdso.so" | ||
| 32 | .balign PAGE_SIZE | ||
| 33 | vdso_end: | ||
| 34 | |||
| 35 | .previous | ||
diff --git a/arch/arm/vdso/vdso.lds.S b/arch/arm/vdso/vdso.lds.S new file mode 100644 index 000000000000..89ca89f12d23 --- /dev/null +++ b/arch/arm/vdso/vdso.lds.S | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | /* | ||
| 2 | * Adapted from arm64 version. | ||
| 3 | * | ||
| 4 | * GNU linker script for the VDSO library. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2012 ARM Limited | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | * | ||
| 20 | * Author: Will Deacon <will.deacon@arm.com> | ||
| 21 | * Heavily based on the vDSO linker scripts for other archs. | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/const.h> | ||
| 25 | #include <asm/page.h> | ||
| 26 | #include <asm/vdso.h> | ||
| 27 | |||
| 28 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") | ||
| 29 | OUTPUT_ARCH(arm) | ||
| 30 | |||
| 31 | SECTIONS | ||
| 32 | { | ||
| 33 | PROVIDE(_start = .); | ||
| 34 | |||
| 35 | . = SIZEOF_HEADERS; | ||
| 36 | |||
| 37 | .hash : { *(.hash) } :text | ||
| 38 | .gnu.hash : { *(.gnu.hash) } | ||
| 39 | .dynsym : { *(.dynsym) } | ||
| 40 | .dynstr : { *(.dynstr) } | ||
| 41 | .gnu.version : { *(.gnu.version) } | ||
| 42 | .gnu.version_d : { *(.gnu.version_d) } | ||
| 43 | .gnu.version_r : { *(.gnu.version_r) } | ||
| 44 | |||
| 45 | .note : { *(.note.*) } :text :note | ||
| 46 | |||
| 47 | |||
| 48 | .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr | ||
| 49 | .eh_frame : { KEEP (*(.eh_frame)) } :text | ||
| 50 | |||
| 51 | .dynamic : { *(.dynamic) } :text :dynamic | ||
| 52 | |||
| 53 | .rodata : { *(.rodata*) } :text | ||
| 54 | |||
| 55 | .text : { *(.text*) } :text =0xe7f001f2 | ||
| 56 | |||
| 57 | .got : { *(.got) } | ||
| 58 | .rel.plt : { *(.rel.plt) } | ||
| 59 | |||
| 60 | /DISCARD/ : { | ||
| 61 | *(.note.GNU-stack) | ||
| 62 | *(.data .data.* .gnu.linkonce.d.* .sdata*) | ||
| 63 | *(.bss .sbss .dynbss .dynsbss) | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | /* | ||
| 68 | * We must supply the ELF program headers explicitly to get just one | ||
| 69 | * PT_LOAD segment, and set the flags explicitly to make segments read-only. | ||
| 70 | */ | ||
| 71 | PHDRS | ||
| 72 | { | ||
| 73 | text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ | ||
| 74 | dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ | ||
| 75 | note PT_NOTE FLAGS(4); /* PF_R */ | ||
| 76 | eh_frame_hdr PT_GNU_EH_FRAME; | ||
| 77 | } | ||
| 78 | |||
| 79 | VERSION | ||
| 80 | { | ||
| 81 | LINUX_2.6 { | ||
| 82 | global: | ||
| 83 | __vdso_clock_gettime; | ||
| 84 | __vdso_gettimeofday; | ||
| 85 | local: *; | ||
| 86 | }; | ||
| 87 | } | ||
diff --git a/arch/arm/vdso/vdsomunge.c b/arch/arm/vdso/vdsomunge.c new file mode 100644 index 000000000000..9005b07296c8 --- /dev/null +++ b/arch/arm/vdso/vdsomunge.c | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2015 Mentor Graphics Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or | ||
| 5 | * modify it under the terms of the GNU General Public License | ||
| 6 | * as published by the Free Software Foundation; version 2 of the | ||
| 7 | * License. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but | ||
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | * General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | * | ||
| 17 | * | ||
| 18 | * vdsomunge - Host program which produces a shared object | ||
| 19 | * architecturally specified to be usable by both soft- and hard-float | ||
| 20 | * programs. | ||
| 21 | * | ||
| 22 | * The Procedure Call Standard for the ARM Architecture (ARM IHI | ||
| 23 | * 0042E) says: | ||
| 24 | * | ||
| 25 | * 6.4.1 VFP and Base Standard Compatibility | ||
| 26 | * | ||
| 27 | * Code compiled for the VFP calling standard is compatible with | ||
| 28 | * the base standard (and vice-versa) if no floating-point or | ||
| 29 | * containerized vector arguments or results are used. | ||
| 30 | * | ||
| 31 | * And ELF for the ARM Architecture (ARM IHI 0044E) (Table 4-2) says: | ||
| 32 | * | ||
| 33 | * If both EF_ARM_ABI_FLOAT_XXXX bits are clear, conformance to the | ||
| 34 | * base procedure-call standard is implied. | ||
| 35 | * | ||
| 36 | * The VDSO is built with -msoft-float, as with the rest of the ARM | ||
| 37 | * kernel, and uses no floating point arguments or results. The build | ||
| 38 | * process will produce a shared object that may or may not have the | ||
| 39 | * EF_ARM_ABI_FLOAT_SOFT flag set (it seems to depend on the binutils | ||
| 40 | * version; binutils starting with 2.24 appears to set it). The | ||
| 41 | * EF_ARM_ABI_FLOAT_HARD flag should definitely not be set, and this | ||
| 42 | * program will error out if it is. | ||
| 43 | * | ||
| 44 | * If the soft-float flag is set, this program clears it. That's all | ||
| 45 | * it does. | ||
| 46 | */ | ||
| 47 | |||
| 48 | #define _GNU_SOURCE | ||
| 49 | |||
| 50 | #include <byteswap.h> | ||
| 51 | #include <elf.h> | ||
| 52 | #include <errno.h> | ||
| 53 | #include <error.h> | ||
| 54 | #include <fcntl.h> | ||
| 55 | #include <stdbool.h> | ||
| 56 | #include <stdio.h> | ||
| 57 | #include <stdlib.h> | ||
| 58 | #include <string.h> | ||
| 59 | #include <sys/mman.h> | ||
| 60 | #include <sys/stat.h> | ||
| 61 | #include <sys/types.h> | ||
| 62 | #include <unistd.h> | ||
| 63 | |||
| 64 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | ||
| 65 | #define HOST_ORDER ELFDATA2LSB | ||
| 66 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | ||
| 67 | #define HOST_ORDER ELFDATA2MSB | ||
| 68 | #endif | ||
| 69 | |||
| 70 | /* Some of the ELF constants we'd like to use were added to <elf.h> | ||
| 71 | * relatively recently. | ||
| 72 | */ | ||
| 73 | #ifndef EF_ARM_EABI_VER5 | ||
| 74 | #define EF_ARM_EABI_VER5 0x05000000 | ||
| 75 | #endif | ||
| 76 | |||
| 77 | #ifndef EF_ARM_ABI_FLOAT_SOFT | ||
| 78 | #define EF_ARM_ABI_FLOAT_SOFT 0x200 | ||
| 79 | #endif | ||
| 80 | |||
| 81 | #ifndef EF_ARM_ABI_FLOAT_HARD | ||
| 82 | #define EF_ARM_ABI_FLOAT_HARD 0x400 | ||
| 83 | #endif | ||
| 84 | |||
| 85 | static const char *outfile; | ||
| 86 | |||
| 87 | static void cleanup(void) | ||
| 88 | { | ||
| 89 | if (error_message_count > 0 && outfile != NULL) | ||
| 90 | unlink(outfile); | ||
| 91 | } | ||
| 92 | |||
| 93 | static Elf32_Word read_elf_word(Elf32_Word word, bool swap) | ||
| 94 | { | ||
| 95 | return swap ? bswap_32(word) : word; | ||
| 96 | } | ||
| 97 | |||
| 98 | static Elf32_Half read_elf_half(Elf32_Half half, bool swap) | ||
| 99 | { | ||
| 100 | return swap ? bswap_16(half) : half; | ||
| 101 | } | ||
| 102 | |||
| 103 | static void write_elf_word(Elf32_Word val, Elf32_Word *dst, bool swap) | ||
| 104 | { | ||
| 105 | *dst = swap ? bswap_32(val) : val; | ||
| 106 | } | ||
| 107 | |||
| 108 | int main(int argc, char **argv) | ||
| 109 | { | ||
| 110 | const Elf32_Ehdr *inhdr; | ||
| 111 | bool clear_soft_float; | ||
| 112 | const char *infile; | ||
| 113 | Elf32_Word e_flags; | ||
| 114 | const void *inbuf; | ||
| 115 | struct stat stat; | ||
| 116 | void *outbuf; | ||
| 117 | bool swap; | ||
| 118 | int outfd; | ||
| 119 | int infd; | ||
| 120 | |||
| 121 | atexit(cleanup); | ||
| 122 | |||
| 123 | if (argc != 3) | ||
| 124 | error(EXIT_FAILURE, 0, "Usage: %s [infile] [outfile]", argv[0]); | ||
| 125 | |||
| 126 | infile = argv[1]; | ||
| 127 | outfile = argv[2]; | ||
| 128 | |||
| 129 | infd = open(infile, O_RDONLY); | ||
| 130 | if (infd < 0) | ||
| 131 | error(EXIT_FAILURE, errno, "Cannot open %s", infile); | ||
| 132 | |||
| 133 | if (fstat(infd, &stat) != 0) | ||
| 134 | error(EXIT_FAILURE, errno, "Failed stat for %s", infile); | ||
| 135 | |||
| 136 | inbuf = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, infd, 0); | ||
| 137 | if (inbuf == MAP_FAILED) | ||
| 138 | error(EXIT_FAILURE, errno, "Failed to map %s", infile); | ||
| 139 | |||
| 140 | close(infd); | ||
| 141 | |||
| 142 | inhdr = inbuf; | ||
| 143 | |||
| 144 | if (memcmp(&inhdr->e_ident, ELFMAG, SELFMAG) != 0) | ||
| 145 | error(EXIT_FAILURE, 0, "Not an ELF file"); | ||
| 146 | |||
| 147 | if (inhdr->e_ident[EI_CLASS] != ELFCLASS32) | ||
| 148 | error(EXIT_FAILURE, 0, "Unsupported ELF class"); | ||
| 149 | |||
| 150 | swap = inhdr->e_ident[EI_DATA] != HOST_ORDER; | ||
| 151 | |||
| 152 | if (read_elf_half(inhdr->e_type, swap) != ET_DYN) | ||
| 153 | error(EXIT_FAILURE, 0, "Not a shared object"); | ||
| 154 | |||
| 155 | if (read_elf_half(inhdr->e_machine, swap) != EM_ARM) { | ||
| 156 | error(EXIT_FAILURE, 0, "Unsupported architecture %#x", | ||
| 157 | inhdr->e_machine); | ||
| 158 | } | ||
| 159 | |||
| 160 | e_flags = read_elf_word(inhdr->e_flags, swap); | ||
| 161 | |||
| 162 | if (EF_ARM_EABI_VERSION(e_flags) != EF_ARM_EABI_VER5) { | ||
| 163 | error(EXIT_FAILURE, 0, "Unsupported EABI version %#x", | ||
| 164 | EF_ARM_EABI_VERSION(e_flags)); | ||
| 165 | } | ||
| 166 | |||
| 167 | if (e_flags & EF_ARM_ABI_FLOAT_HARD) | ||
| 168 | error(EXIT_FAILURE, 0, | ||
| 169 | "Unexpected hard-float flag set in e_flags"); | ||
| 170 | |||
| 171 | clear_soft_float = !!(e_flags & EF_ARM_ABI_FLOAT_SOFT); | ||
| 172 | |||
| 173 | outfd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); | ||
| 174 | if (outfd < 0) | ||
| 175 | error(EXIT_FAILURE, errno, "Cannot open %s", outfile); | ||
| 176 | |||
| 177 | if (ftruncate(outfd, stat.st_size) != 0) | ||
| 178 | error(EXIT_FAILURE, errno, "Cannot truncate %s", outfile); | ||
| 179 | |||
| 180 | outbuf = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, | ||
| 181 | outfd, 0); | ||
| 182 | if (outbuf == MAP_FAILED) | ||
| 183 | error(EXIT_FAILURE, errno, "Failed to map %s", outfile); | ||
| 184 | |||
| 185 | close(outfd); | ||
| 186 | |||
| 187 | memcpy(outbuf, inbuf, stat.st_size); | ||
| 188 | |||
| 189 | if (clear_soft_float) { | ||
| 190 | Elf32_Ehdr *outhdr; | ||
| 191 | |||
| 192 | outhdr = outbuf; | ||
| 193 | e_flags &= ~EF_ARM_ABI_FLOAT_SOFT; | ||
| 194 | write_elf_word(e_flags, &outhdr->e_flags, swap); | ||
| 195 | } | ||
| 196 | |||
| 197 | if (msync(outbuf, stat.st_size, MS_SYNC) != 0) | ||
| 198 | error(EXIT_FAILURE, errno, "Failed to sync %s", outfile); | ||
| 199 | |||
| 200 | return EXIT_SUCCESS; | ||
| 201 | } | ||
diff --git a/arch/arm/vdso/vgettimeofday.c b/arch/arm/vdso/vgettimeofday.c new file mode 100644 index 000000000000..79214d5ff097 --- /dev/null +++ b/arch/arm/vdso/vgettimeofday.c | |||
| @@ -0,0 +1,282 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2015 Mentor Graphics Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or | ||
| 5 | * modify it under the terms of the GNU General Public License | ||
| 6 | * as published by the Free Software Foundation; version 2 of the | ||
| 7 | * License. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but | ||
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | * General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/compiler.h> | ||
| 19 | #include <linux/hrtimer.h> | ||
| 20 | #include <linux/time.h> | ||
| 21 | #include <asm/arch_timer.h> | ||
| 22 | #include <asm/barrier.h> | ||
| 23 | #include <asm/bug.h> | ||
| 24 | #include <asm/page.h> | ||
| 25 | #include <asm/unistd.h> | ||
| 26 | #include <asm/vdso_datapage.h> | ||
| 27 | |||
| 28 | #ifndef CONFIG_AEABI | ||
| 29 | #error This code depends on AEABI system call conventions | ||
| 30 | #endif | ||
| 31 | |||
| 32 | extern struct vdso_data *__get_datapage(void); | ||
| 33 | |||
| 34 | static notrace u32 __vdso_read_begin(const struct vdso_data *vdata) | ||
| 35 | { | ||
| 36 | u32 seq; | ||
| 37 | repeat: | ||
| 38 | seq = ACCESS_ONCE(vdata->seq_count); | ||
| 39 | if (seq & 1) { | ||
| 40 | cpu_relax(); | ||
| 41 | goto repeat; | ||
| 42 | } | ||
| 43 | return seq; | ||
| 44 | } | ||
| 45 | |||
| 46 | static notrace u32 vdso_read_begin(const struct vdso_data *vdata) | ||
| 47 | { | ||
| 48 | u32 seq; | ||
| 49 | |||
| 50 | seq = __vdso_read_begin(vdata); | ||
| 51 | |||
| 52 | smp_rmb(); /* Pairs with smp_wmb in vdso_write_end */ | ||
| 53 | return seq; | ||
| 54 | } | ||
| 55 | |||
| 56 | static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start) | ||
| 57 | { | ||
| 58 | smp_rmb(); /* Pairs with smp_wmb in vdso_write_begin */ | ||
| 59 | return vdata->seq_count != start; | ||
| 60 | } | ||
| 61 | |||
| 62 | static notrace long clock_gettime_fallback(clockid_t _clkid, | ||
| 63 | struct timespec *_ts) | ||
| 64 | { | ||
| 65 | register struct timespec *ts asm("r1") = _ts; | ||
| 66 | register clockid_t clkid asm("r0") = _clkid; | ||
| 67 | register long ret asm ("r0"); | ||
| 68 | register long nr asm("r7") = __NR_clock_gettime; | ||
| 69 | |||
| 70 | asm volatile( | ||
| 71 | " swi #0\n" | ||
| 72 | : "=r" (ret) | ||
| 73 | : "r" (clkid), "r" (ts), "r" (nr) | ||
| 74 | : "memory"); | ||
| 75 | |||
| 76 | return ret; | ||
| 77 | } | ||
| 78 | |||
| 79 | static notrace int do_realtime_coarse(struct timespec *ts, | ||
| 80 | struct vdso_data *vdata) | ||
| 81 | { | ||
| 82 | u32 seq; | ||
| 83 | |||
| 84 | do { | ||
| 85 | seq = vdso_read_begin(vdata); | ||
| 86 | |||
| 87 | ts->tv_sec = vdata->xtime_coarse_sec; | ||
| 88 | ts->tv_nsec = vdata->xtime_coarse_nsec; | ||
| 89 | |||
| 90 | } while (vdso_read_retry(vdata, seq)); | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static notrace int do_monotonic_coarse(struct timespec *ts, | ||
| 96 | struct vdso_data *vdata) | ||
| 97 | { | ||
| 98 | struct timespec tomono; | ||
| 99 | u32 seq; | ||
| 100 | |||
| 101 | do { | ||
| 102 | seq = vdso_read_begin(vdata); | ||
| 103 | |||
| 104 | ts->tv_sec = vdata->xtime_coarse_sec; | ||
| 105 | ts->tv_nsec = vdata->xtime_coarse_nsec; | ||
| 106 | |||
| 107 | tomono.tv_sec = vdata->wtm_clock_sec; | ||
| 108 | tomono.tv_nsec = vdata->wtm_clock_nsec; | ||
| 109 | |||
| 110 | } while (vdso_read_retry(vdata, seq)); | ||
| 111 | |||
| 112 | ts->tv_sec += tomono.tv_sec; | ||
| 113 | timespec_add_ns(ts, tomono.tv_nsec); | ||
| 114 | |||
| 115 | return 0; | ||
| 116 | } | ||
| 117 | |||
| 118 | #ifdef CONFIG_ARM_ARCH_TIMER | ||
| 119 | |||
| 120 | static notrace u64 get_ns(struct vdso_data *vdata) | ||
| 121 | { | ||
| 122 | u64 cycle_delta; | ||
| 123 | u64 cycle_now; | ||
| 124 | u64 nsec; | ||
| 125 | |||
| 126 | cycle_now = arch_counter_get_cntvct(); | ||
| 127 | |||
| 128 | cycle_delta = (cycle_now - vdata->cs_cycle_last) & vdata->cs_mask; | ||
| 129 | |||
| 130 | nsec = (cycle_delta * vdata->cs_mult) + vdata->xtime_clock_snsec; | ||
| 131 | nsec >>= vdata->cs_shift; | ||
| 132 | |||
| 133 | return nsec; | ||
| 134 | } | ||
| 135 | |||
| 136 | static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata) | ||
| 137 | { | ||
| 138 | u64 nsecs; | ||
| 139 | u32 seq; | ||
| 140 | |||
| 141 | do { | ||
| 142 | seq = vdso_read_begin(vdata); | ||
| 143 | |||
| 144 | if (!vdata->tk_is_cntvct) | ||
| 145 | return -1; | ||
| 146 | |||
| 147 | ts->tv_sec = vdata->xtime_clock_sec; | ||
| 148 | nsecs = get_ns(vdata); | ||
| 149 | |||
| 150 | } while (vdso_read_retry(vdata, seq)); | ||
| 151 | |||
| 152 | ts->tv_nsec = 0; | ||
| 153 | timespec_add_ns(ts, nsecs); | ||
| 154 | |||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata) | ||
| 159 | { | ||
| 160 | struct timespec tomono; | ||
| 161 | u64 nsecs; | ||
| 162 | u32 seq; | ||
| 163 | |||
| 164 | do { | ||
| 165 | seq = vdso_read_begin(vdata); | ||
| 166 | |||
| 167 | if (!vdata->tk_is_cntvct) | ||
| 168 | return -1; | ||
| 169 | |||
| 170 | ts->tv_sec = vdata->xtime_clock_sec; | ||
| 171 | nsecs = get_ns(vdata); | ||
| 172 | |||
| 173 | tomono.tv_sec = vdata->wtm_clock_sec; | ||
| 174 | tomono.tv_nsec = vdata->wtm_clock_nsec; | ||
| 175 | |||
| 176 | } while (vdso_read_retry(vdata, seq)); | ||
| 177 | |||
| 178 | ts->tv_sec += tomono.tv_sec; | ||
| 179 | ts->tv_nsec = 0; | ||
| 180 | timespec_add_ns(ts, nsecs + tomono.tv_nsec); | ||
| 181 | |||
| 182 | return 0; | ||
| 183 | } | ||
| 184 | |||
| 185 | #else /* CONFIG_ARM_ARCH_TIMER */ | ||
| 186 | |||
| 187 | static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata) | ||
| 188 | { | ||
| 189 | return -1; | ||
| 190 | } | ||
| 191 | |||
| 192 | static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata) | ||
| 193 | { | ||
| 194 | return -1; | ||
| 195 | } | ||
| 196 | |||
| 197 | #endif /* CONFIG_ARM_ARCH_TIMER */ | ||
| 198 | |||
| 199 | notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts) | ||
| 200 | { | ||
| 201 | struct vdso_data *vdata; | ||
| 202 | int ret = -1; | ||
| 203 | |||
| 204 | vdata = __get_datapage(); | ||
| 205 | |||
| 206 | switch (clkid) { | ||
| 207 | case CLOCK_REALTIME_COARSE: | ||
| 208 | ret = do_realtime_coarse(ts, vdata); | ||
| 209 | break; | ||
| 210 | case CLOCK_MONOTONIC_COARSE: | ||
| 211 | ret = do_monotonic_coarse(ts, vdata); | ||
| 212 | break; | ||
| 213 | case CLOCK_REALTIME: | ||
| 214 | ret = do_realtime(ts, vdata); | ||
| 215 | break; | ||
| 216 | case CLOCK_MONOTONIC: | ||
| 217 | ret = do_monotonic(ts, vdata); | ||
| 218 | break; | ||
| 219 | default: | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | |||
| 223 | if (ret) | ||
| 224 | ret = clock_gettime_fallback(clkid, ts); | ||
| 225 | |||
| 226 | return ret; | ||
| 227 | } | ||
| 228 | |||
| 229 | static notrace long gettimeofday_fallback(struct timeval *_tv, | ||
| 230 | struct timezone *_tz) | ||
| 231 | { | ||
| 232 | register struct timezone *tz asm("r1") = _tz; | ||
| 233 | register struct timeval *tv asm("r0") = _tv; | ||
| 234 | register long ret asm ("r0"); | ||
| 235 | register long nr asm("r7") = __NR_gettimeofday; | ||
| 236 | |||
| 237 | asm volatile( | ||
| 238 | " swi #0\n" | ||
| 239 | : "=r" (ret) | ||
| 240 | : "r" (tv), "r" (tz), "r" (nr) | ||
| 241 | : "memory"); | ||
| 242 | |||
| 243 | return ret; | ||
| 244 | } | ||
| 245 | |||
| 246 | notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) | ||
| 247 | { | ||
| 248 | struct timespec ts; | ||
| 249 | struct vdso_data *vdata; | ||
| 250 | int ret; | ||
| 251 | |||
| 252 | vdata = __get_datapage(); | ||
| 253 | |||
| 254 | ret = do_realtime(&ts, vdata); | ||
| 255 | if (ret) | ||
| 256 | return gettimeofday_fallback(tv, tz); | ||
| 257 | |||
| 258 | if (tv) { | ||
| 259 | tv->tv_sec = ts.tv_sec; | ||
| 260 | tv->tv_usec = ts.tv_nsec / 1000; | ||
| 261 | } | ||
| 262 | if (tz) { | ||
| 263 | tz->tz_minuteswest = vdata->tz_minuteswest; | ||
| 264 | tz->tz_dsttime = vdata->tz_dsttime; | ||
| 265 | } | ||
| 266 | |||
| 267 | return ret; | ||
| 268 | } | ||
| 269 | |||
| 270 | /* Avoid unresolved references emitted by GCC */ | ||
| 271 | |||
| 272 | void __aeabi_unwind_cpp_pr0(void) | ||
| 273 | { | ||
| 274 | } | ||
| 275 | |||
| 276 | void __aeabi_unwind_cpp_pr1(void) | ||
| 277 | { | ||
| 278 | } | ||
| 279 | |||
| 280 | void __aeabi_unwind_cpp_pr2(void) | ||
| 281 | { | ||
| 282 | } | ||
