diff options
author | Greentime Hu <greentime@andestech.com> | 2017-10-25 11:16:56 +0800 |
---|---|---|
committer | Greentime Hu <greentime@andestech.com> | 2018-02-22 10:44:33 +0800 |
commit | eefacd1dfe38a3334d2e1c44fee0d7b0fdd5d252 (patch) | |
tree | ecc49e54c9a797a7f402c39da9a2d7f25cd1c104 /arch/nds32/kernel/vdso | |
parent | 1932fbe36e02f54223ac4c6779b92269ca8b9b60 (diff) | |
download | linux-eefacd1dfe38a3334d2e1c44fee0d7b0fdd5d252.tar.bz2 |
nds32: VDSO support
This patch adds VDSO support. The VDSO code is currently used for
sys_rt_sigreturn() and optimised gettimeofday() (using the SoC timer counter).
Signed-off-by: Vincent Chen <vincentc@andestech.com>
Signed-off-by: Greentime Hu <greentime@andestech.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/nds32/kernel/vdso')
-rw-r--r-- | arch/nds32/kernel/vdso/Makefile | 82 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/datapage.S | 21 | ||||
-rwxr-xr-x | arch/nds32/kernel/vdso/gen_vdso_offsets.sh | 15 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/gettimeofday.c | 270 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/note.S | 11 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/sigreturn.S | 19 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/vdso.S | 18 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/vdso.lds.S | 76 |
8 files changed, 512 insertions, 0 deletions
diff --git a/arch/nds32/kernel/vdso/Makefile b/arch/nds32/kernel/vdso/Makefile new file mode 100644 index 000000000000..e6c50a701313 --- /dev/null +++ b/arch/nds32/kernel/vdso/Makefile @@ -0,0 +1,82 @@ +# +# Building a vDSO image for AArch64. +# +# Author: Will Deacon <will.deacon@arm.com> +# Heavily based on the vDSO Makefiles for other archs. +# + +obj-vdso := note.o datapage.o sigreturn.o gettimeofday.o + +# Build rules +targets := $(obj-vdso) vdso.so vdso.so.dbg +obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) + +ccflags-y := -shared -fno-common -fno-builtin +ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \ + $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) +ccflags-y += -fPIC -Wl,-shared -g + +# Disable gcov profiling for VDSO code +GCOV_PROFILE := n + + +obj-y += vdso.o +extra-y += vdso.lds +CPPFLAGS_vdso.lds += -P -C -U$(ARCH) + +# Force dependency +$(obj)/vdso.o : $(obj)/vdso.so + +# Link rule for the .so file, .lds has to be first +$(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) + $(call if_changed,vdsold) + + +# Strip rule for the .so file +$(obj)/%.so: OBJCOPYFLAGS := -S +$(obj)/%.so: $(obj)/%.so.dbg FORCE + $(call if_changed,objcopy) + +# Generate VDSO offsets using helper script +gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh +quiet_cmd_vdsosym = VDSOSYM $@ +define cmd_vdsosym + $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ +endef + +include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE + $(call if_changed,vdsosym) + + + +# Assembly rules for the .S files + +sigreturn.o : sigreturn.S + $(call if_changed_dep,vdsoas) + +note.o : note.S + $(call if_changed_dep,vdsoas) + +datapage.o : datapage.S + $(call if_changed_dep,vdsoas) + +gettimeofday.o : gettimeofday.c FORCE + $(call if_changed_dep,vdsocc) + +# Actual build commands +quiet_cmd_vdsold = VDSOL $@ + cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@ +quiet_cmd_vdsoas = VDSOA $@ + cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $< +quiet_cmd_vdsocc = VDSOA $@ + cmd_vdsocc = $(CC) $(c_flags) -c -o $@ $< + +# Install commands for the unstripped file +quiet_cmd_vdso_install = INSTALL $@ + cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ + +vdso.so: $(obj)/vdso.so.dbg + @mkdir -p $(MODLIB)/vdso + $(call cmd,vdso_install) + +vdso_install: vdso.so diff --git a/arch/nds32/kernel/vdso/datapage.S b/arch/nds32/kernel/vdso/datapage.S new file mode 100644 index 000000000000..4a62c3cab1c8 --- /dev/null +++ b/arch/nds32/kernel/vdso/datapage.S @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2005-2017 Andes Technology Corporation + +#include <linux/linkage.h> +#include <asm/page.h> + +ENTRY(__get_timerpage) + sethi $r0, hi20(. + PAGE_SIZE + 8) + ori $r0, $r0, lo12(. + PAGE_SIZE + 4) + mfusr $r1, $pc + sub $r0, $r1, $r0 + ret +ENDPROC(__get_timerpage) + +ENTRY(__get_datapage) + sethi $r0, hi20(. + 2*PAGE_SIZE + 8) + ori $r0, $r0, lo12(. + 2*PAGE_SIZE + 4) + mfusr $r1, $pc + sub $r0, $r1, $r0 + ret +ENDPROC(__get_datapage) diff --git a/arch/nds32/kernel/vdso/gen_vdso_offsets.sh b/arch/nds32/kernel/vdso/gen_vdso_offsets.sh new file mode 100755 index 000000000000..01924ff071ad --- /dev/null +++ b/arch/nds32/kernel/vdso/gen_vdso_offsets.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Match symbols in the DSO that look like VDSO_*; produce a header file +# of constant offsets into the shared object. +# +# Doing this inside the Makefile will break the $(filter-out) function, +# causing Kbuild to rebuild the vdso-offsets header file every time. +# +# Author: Will Deacon <will.deacon@arm.com +# + +LC_ALL=C +sed -n -e 's/^00*/0/' -e \ +'s/^\([0-9a-fA-F]*\) . VDSO_\([a-zA-Z0-9_]*\)$/\#define vdso_offset_\2\t0x\1/p' diff --git a/arch/nds32/kernel/vdso/gettimeofday.c b/arch/nds32/kernel/vdso/gettimeofday.c new file mode 100644 index 000000000000..038721af40e3 --- /dev/null +++ b/arch/nds32/kernel/vdso/gettimeofday.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2005-2017 Andes Technology Corporation + +#include <linux/compiler.h> +#include <linux/hrtimer.h> +#include <linux/time.h> +#include <asm/io.h> +#include <asm/barrier.h> +#include <asm/bug.h> +#include <asm/page.h> +#include <asm/unistd.h> +#include <asm/vdso_datapage.h> +#include <asm/vdso_timer_info.h> +#include <asm/asm-offsets.h> + +#define X(x) #x +#define Y(x) X(x) + +extern struct vdso_data *__get_datapage(void); +extern struct vdso_data *__get_timerpage(void); + +static notrace unsigned int __vdso_read_begin(const struct vdso_data *vdata) +{ + u32 seq; +repeat: + seq = READ_ONCE(vdata->seq_count); + if (seq & 1) { + cpu_relax(); + goto repeat; + } + return seq; +} + +static notrace unsigned int vdso_read_begin(const struct vdso_data *vdata) +{ + unsigned int seq; + + seq = __vdso_read_begin(vdata); + + smp_rmb(); /* Pairs with smp_wmb in vdso_write_end */ + return seq; +} + +static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start) +{ + smp_rmb(); /* Pairs with smp_wmb in vdso_write_begin */ + return vdata->seq_count != start; +} + +static notrace long clock_gettime_fallback(clockid_t _clkid, + struct timespec *_ts) +{ + register struct timespec *ts asm("$r1") = _ts; + register clockid_t clkid asm("$r0") = _clkid; + register long ret asm("$r0"); + + asm volatile ("movi $r15, %3\n" + "syscall 0x0\n" + :"=r" (ret) + :"r"(clkid), "r"(ts), "i"(__NR_clock_gettime) + :"$r15", "memory"); + + return ret; +} + +static notrace int do_realtime_coarse(struct timespec *ts, + struct vdso_data *vdata) +{ + u32 seq; + + do { + seq = vdso_read_begin(vdata); + + ts->tv_sec = vdata->xtime_coarse_sec; + ts->tv_nsec = vdata->xtime_coarse_nsec; + + } while (vdso_read_retry(vdata, seq)); + return 0; +} + +static notrace int do_monotonic_coarse(struct timespec *ts, + struct vdso_data *vdata) +{ + struct timespec tomono; + u32 seq; + + do { + seq = vdso_read_begin(vdata); + + ts->tv_sec = vdata->xtime_coarse_sec; + ts->tv_nsec = vdata->xtime_coarse_nsec; + + tomono.tv_sec = vdata->wtm_clock_sec; + tomono.tv_nsec = vdata->wtm_clock_nsec; + + } while (vdso_read_retry(vdata, seq)); + + ts->tv_sec += tomono.tv_sec; + timespec_add_ns(ts, tomono.tv_nsec); + return 0; +} + +static notrace inline u64 vgetsns(struct vdso_data *vdso) +{ + u32 cycle_now; + u32 cycle_delta; + u32 *timer_cycle_base; + + timer_cycle_base = + (u32 *) ((char *)__get_timerpage() + vdso->cycle_count_offset); + cycle_now = readl_relaxed(timer_cycle_base); + if (true == vdso->cycle_count_down) + cycle_now = ~(*timer_cycle_base); + cycle_delta = cycle_now - (u32) vdso->cs_cycle_last; + return ((u64) cycle_delta & vdso->cs_mask) * vdso->cs_mult; +} + +static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata) +{ + unsigned count; + u64 ns; + do { + count = vdso_read_begin(vdata); + ts->tv_sec = vdata->xtime_clock_sec; + ns = vdata->xtime_clock_nsec; + ns += vgetsns(vdata); + ns >>= vdata->cs_shift; + } while (vdso_read_retry(vdata, count)); + + ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns); + ts->tv_nsec = ns; + + return 0; +} + +static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata) +{ + struct timespec tomono; + u64 nsecs; + u32 seq; + + do { + seq = vdso_read_begin(vdata); + + ts->tv_sec = vdata->xtime_clock_sec; + nsecs = vdata->xtime_clock_nsec; + nsecs += vgetsns(vdata); + nsecs >>= vdata->cs_shift; + + tomono.tv_sec = vdata->wtm_clock_sec; + tomono.tv_nsec = vdata->wtm_clock_nsec; + + } while (vdso_read_retry(vdata, seq)); + + ts->tv_sec += tomono.tv_sec; + ts->tv_nsec = 0; + timespec_add_ns(ts, nsecs + tomono.tv_nsec); + return 0; +} + +notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts) +{ + struct vdso_data *vdata; + int ret = -1; + + vdata = __get_datapage(); + if (vdata->cycle_count_offset == EMPTY_REG_OFFSET) + return clock_gettime_fallback(clkid, ts); + + switch (clkid) { + case CLOCK_REALTIME_COARSE: + ret = do_realtime_coarse(ts, vdata); + break; + case CLOCK_MONOTONIC_COARSE: + ret = do_monotonic_coarse(ts, vdata); + break; + case CLOCK_REALTIME: + ret = do_realtime(ts, vdata); + break; + case CLOCK_MONOTONIC: + ret = do_monotonic(ts, vdata); + break; + default: + break; + } + + if (ret) + ret = clock_gettime_fallback(clkid, ts); + + return ret; +} + +static notrace int clock_getres_fallback(clockid_t _clk_id, + struct timespec *_res) +{ + register clockid_t clk_id asm("$r0") = _clk_id; + register struct timespec *res asm("$r1") = _res; + register int ret asm("$r0"); + + asm volatile ("movi $r15, %3\n" + "syscall 0x0\n" + :"=r" (ret) + :"r"(clk_id), "r"(res), "i"(__NR_clock_getres) + :"$r15", "memory"); + + return ret; +} + +notrace int __vdso_clock_getres(clockid_t clk_id, struct timespec *res) +{ + if (res == NULL) + return 0; + switch (clk_id) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_RAW: + res->tv_sec = 0; + res->tv_nsec = CLOCK_REALTIME_RES; + break; + case CLOCK_REALTIME_COARSE: + case CLOCK_MONOTONIC_COARSE: + res->tv_sec = 0; + res->tv_nsec = CLOCK_COARSE_RES; + break; + default: + return clock_getres_fallback(clk_id, res); + } + return 0; +} + +static notrace inline int gettimeofday_fallback(struct timeval *_tv, + struct timezone *_tz) +{ + register struct timeval *tv asm("$r0") = _tv; + register struct timezone *tz asm("$r1") = _tz; + register int ret asm("$r0"); + + asm volatile ("movi $r15, %3\n" + "syscall 0x0\n" + :"=r" (ret) + :"r"(tv), "r"(tz), "i"(__NR_gettimeofday) + :"$r15", "memory"); + + return ret; +} + +notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + struct timespec ts; + struct vdso_data *vdata; + int ret; + + vdata = __get_datapage(); + + if (vdata->cycle_count_offset == EMPTY_REG_OFFSET) + return gettimeofday_fallback(tv, tz); + + ret = do_realtime(&ts, vdata); + + if (tv) { + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + } + if (tz) { + tz->tz_minuteswest = vdata->tz_minuteswest; + tz->tz_dsttime = vdata->tz_dsttime; + } + + return ret; +} diff --git a/arch/nds32/kernel/vdso/note.S b/arch/nds32/kernel/vdso/note.S new file mode 100644 index 000000000000..0aeaa19b05f0 --- /dev/null +++ b/arch/nds32/kernel/vdso/note.S @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012 ARM Limited +// Copyright (C) 2005-2017 Andes Technology Corporation + +#include <linux/uts.h> +#include <linux/version.h> +#include <linux/elfnote.h> + +ELFNOTE_START(Linux, 0, "a") + .long LINUX_VERSION_CODE +ELFNOTE_END diff --git a/arch/nds32/kernel/vdso/sigreturn.S b/arch/nds32/kernel/vdso/sigreturn.S new file mode 100644 index 000000000000..67e4d1d1612a --- /dev/null +++ b/arch/nds32/kernel/vdso/sigreturn.S @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012 ARM Limited +// Copyright (C) 2005-2017 Andes Technology Corporation + +#include <linux/linkage.h> +#include <asm/unistd.h> + + .text + +ENTRY(__kernel_rt_sigreturn) + .cfi_startproc + movi $r15, __NR_rt_sigreturn + /* + * The SWID of syscall should be __NR_rt_sigreturn to synchronize + * the unwinding scheme in gcc + */ + syscall __NR_rt_sigreturn + .cfi_endproc +ENDPROC(__kernel_rt_sigreturn) diff --git a/arch/nds32/kernel/vdso/vdso.S b/arch/nds32/kernel/vdso/vdso.S new file mode 100644 index 000000000000..16737c11e55b --- /dev/null +++ b/arch/nds32/kernel/vdso/vdso.S @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012 ARM Limited +// Copyright (C) 2005-2017 Andes Technology Corporation + +#include <linux/init.h> +#include <linux/linkage.h> +#include <linux/const.h> +#include <asm/page.h> + + .globl vdso_start, vdso_end + .section .rodata + .balign PAGE_SIZE +vdso_start: + .incbin "arch/nds32/kernel/vdso/vdso.so" + .balign PAGE_SIZE +vdso_end: + + .previous diff --git a/arch/nds32/kernel/vdso/vdso.lds.S b/arch/nds32/kernel/vdso/vdso.lds.S new file mode 100644 index 000000000000..36630352ee02 --- /dev/null +++ b/arch/nds32/kernel/vdso/vdso.lds.S @@ -0,0 +1,76 @@ +/* + * SPDX-License-Identifier: GPL-2.0 + * Copyright (C) 2005-2017 Andes Technology Corporation + */ + + +#include <linux/const.h> +#include <asm/page.h> +#include <asm/vdso.h> + +OUTPUT_FORMAT("elf32-nds32le-linux", "elf32-nds32be-linux", "elf32-nds32le-linux") +OUTPUT_ARCH(nds32) + +SECTIONS +{ + . = SIZEOF_HEADERS; + + .hash : { *(.hash) } :text + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + .note : { *(.note.*) } :text :note + + + .text : { *(.text*) } :text + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + + .dynamic : { *(.dynamic) } :text :dynamic + + .rodata : { *(.rodata*) } :text + + + /DISCARD/ : { + *(.note.GNU-stack) + *(.data .data.* .gnu.linkonce.d.* .sdata*) + *(.bss .sbss .dynbss .dynsbss) + } +} + +/* + * We must supply the ELF program headers explicitly to get just one + * PT_LOAD segment, and set the flags explicitly to make segments read-only. + */ +PHDRS +{ + text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + note PT_NOTE FLAGS(4); /* PF_R */ + eh_frame_hdr PT_GNU_EH_FRAME; +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_4 { + global: + __kernel_rt_sigreturn; + __vdso_gettimeofday; + __vdso_clock_getres; + __vdso_clock_gettime; + local: *; + }; +} + +/* + * Make the rt_sigreturn code visible to the kernel. + */ +VDSO_rt_sigtramp = __kernel_rt_sigreturn; |