From 0b8929354cdeddb17e81bfda903812c9adfd0b67 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 27 Sep 2006 17:22:49 +0900 Subject: sh: __NR_restart_syscall support. This implements support for __NR_restart_syscall. Signed-off-by: Paul Mundt --- arch/sh/kernel/signal.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'arch/sh/kernel/signal.c') diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index b475c4d2405f..eb6a3b97e46c 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -33,7 +33,8 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); +asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset, + unsigned int save_r0); /* * Atomically swap in the new signal mask, and wait for a signal. @@ -56,7 +57,7 @@ sys_sigsuspend(old_sigset_t mask, while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(®s, &saveset)) + if (do_signal(®s, &saveset, regs.regs[0])) return -EINTR; } } @@ -85,7 +86,7 @@ sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(®s, &saveset)) + if (do_signal(®s, &saveset, regs.regs[0])) return -EINTR; } } @@ -563,7 +564,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -int do_signal(struct pt_regs *regs, sigset_t *oldset) +int do_signal(struct pt_regs *regs, sigset_t *oldset, unsigned int save_r0) { siginfo_t info; int signr; @@ -597,9 +598,12 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) /* Restart the system call - no handlers present */ if (regs->regs[0] == -ERESTARTNOHAND || regs->regs[0] == -ERESTARTSYS || - regs->regs[0] == -ERESTARTNOINTR || - regs->regs[0] == -ERESTART_RESTARTBLOCK) { + regs->regs[0] == -ERESTARTNOINTR) { + regs->regs[0] = save_r0; regs->pc -= 2; + } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { + regs->pc -= 2; + regs->regs[3] = __NR_restart_syscall; } } return 0; -- cgit From 9f23e7e94f7083d9705b595cbd6b30972be6fbbb Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 27 Sep 2006 17:27:00 +0900 Subject: sh: pselect6 and ppoll, along with signal trampoline rework. This implements support for ppoll() and pselect6().. Signed-off-by: Paul Mundt --- arch/sh/kernel/entry.S | 8 +-- arch/sh/kernel/signal.c | 137 ++++++++++++++++++++----------------------- arch/sh/kernel/syscalls.S | 31 +++++----- include/asm-sh/thread_info.h | 2 + include/asm-sh/unistd.h | 66 +++++++++++---------- 5 files changed, 121 insertions(+), 123 deletions(-) (limited to 'arch/sh/kernel/signal.c') diff --git a/arch/sh/kernel/entry.S b/arch/sh/kernel/entry.S index fcbf50d4ee10..fd5fe2349f20 100644 --- a/arch/sh/kernel/entry.S +++ b/arch/sh/kernel/entry.S @@ -371,12 +371,12 @@ work_pending: ! r8: current_thread_info ! t: result of "tst #_TIF_NEED_RESCHED, r0" bf/s work_resched - tst #_TIF_SIGPENDING, r0 + tst #(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r0 work_notifysig: bt/s restore_all mov r15, r4 - mov #0, r5 - mov r12, r6 ! set arg2(save_r0) + mov r12, r5 ! set arg1(save_r0) + mov r0, r6 mov.l 2f, r1 mova restore_all, r0 jmp @r1 @@ -414,7 +414,7 @@ work_resched: .align 2 1: .long schedule -2: .long do_signal +2: .long do_notify_resume .align 2 syscall_exit_work: diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index eb6a3b97e46c..2f1c9545b49f 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -29,13 +29,10 @@ #include #include -#define DEBUG_SIG 0 +#undef DEBUG #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset, - unsigned int save_r0); - /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -44,51 +41,17 @@ sys_sigsuspend(old_sigset_t mask, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs regs) { - sigset_t saveset; - mask &= _BLOCKABLE; spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; + current->saved_sigmask = current->blocked; siginitset(¤t->blocked, mask); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - regs.regs[0] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(®s, &saveset, regs.regs[0])) - return -EINTR; - } -} - -asmlinkage int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, - unsigned long r6, unsigned long r7, - struct pt_regs regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs.regs[0] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(®s, &saveset, regs.regs[0])) - return -EINTR; - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } asmlinkage int @@ -349,7 +312,7 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) return (void __user *)((sp - frame_size) & -8ul); } -static void setup_frame(int sig, struct k_sigaction *ka, +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs) { struct sigframe __user *frame; @@ -369,10 +332,9 @@ static void setup_frame(int sig, struct k_sigaction *ka, err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); - if (_NSIG_WORDS > 1) { + if (_NSIG_WORDS > 1) err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); - } /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -403,21 +365,22 @@ static void setup_frame(int sig, struct k_sigaction *ka, set_fs(USER_DS); -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", - current->comm, current->pid, frame, regs->pc, regs->pr); -#endif + pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", + current->comm, current->pid, frame, regs->pc, regs->pr); flush_cache_sigtramp(regs->pr); + if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); - return; + + return 0; give_sigsegv: force_sigsegv(sig, current); + return -EFAULT; } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; @@ -478,28 +441,31 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, set_fs(USER_DS); -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", - current->comm, current->pid, frame, regs->pc, regs->pr); -#endif + pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", + current->comm, current->pid, frame, regs->pc, regs->pr); flush_cache_sigtramp(regs->pr); + if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); - return; + + return 0; give_sigsegv: force_sigsegv(sig, current); + return -EFAULT; } /* * OK, we're invoking a handler */ -static void +static int handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) { + int ret; + /* Are we from a system call? */ if (regs->tra >= 0) { /* If so, check system call restarting.. */ @@ -540,19 +506,23 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; } /* @@ -564,11 +534,12 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -int do_signal(struct pt_regs *regs, sigset_t *oldset, unsigned int save_r0) +static void do_signal(struct pt_regs *regs, unsigned int save_r0) { siginfo_t info; int signr; struct k_sigaction ka; + sigset_t *oldset; /* * We want the common case to go fast, which @@ -577,19 +548,27 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset, unsigned int save_r0) * if so. */ if (!user_mode(regs)) - return 1; + return; if (try_to_freeze()) goto no_signal; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { /* Whee! Actually deliver the signal. */ - handle_signal(signr, &ka, &info, oldset, regs); - return 1; + if (handle_signal(signr, &ka, &info, oldset, regs) == 0) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } } no_signal: @@ -606,5 +585,19 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset, unsigned int save_r0) regs->regs[3] = __NR_restart_syscall; } } - return 0; + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } +} + +asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0, + __u32 thread_info_flags) +{ + /* deal with pending signal delivery */ + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) + do_signal(regs, save_r0); } diff --git a/arch/sh/kernel/syscalls.S b/arch/sh/kernel/syscalls.S index ea23b213c77f..768334e95075 100644 --- a/arch/sh/kernel/syscalls.S +++ b/arch/sh/kernel/syscalls.S @@ -319,34 +319,35 @@ ENTRY(sys_call_table) .long sys_mq_getsetattr .long sys_kexec_load .long sys_waitid - .long sys_add_key /* 285 */ + .long sys_ni_syscall /* 285 */ + .long sys_add_key .long sys_request_key .long sys_keyctl .long sys_ioprio_set - .long sys_ioprio_get - .long sys_inotify_init /* 290 */ + .long sys_ioprio_get /* 290 */ + .long sys_inotify_init .long sys_inotify_add_watch .long sys_inotify_rm_watch .long sys_migrate_pages - .long sys_openat - .long sys_mkdirat /* 295 */ + .long sys_openat /* 295 */ + .long sys_mkdirat .long sys_mknodat .long sys_fchownat .long sys_futimesat - .long sys_fstatat64 - .long sys_unlinkat /* 300 */ + .long sys_fstatat64 /* 300 */ + .long sys_unlinkat .long sys_renameat .long sys_linkat .long sys_symlinkat - .long sys_readlinkat - .long sys_fchmodat /* 305 */ + .long sys_readlinkat /* 305 */ + .long sys_fchmodat .long sys_faccessat - .long sys_ni_syscall /* Reserved for pselect6 */ - .long sys_ni_syscall /* Reserved for ppoll */ - .long sys_unshare - .long sys_set_robust_list /* 310 */ + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare /* 310 */ + .long sys_set_robust_list .long sys_get_robust_list .long sys_splice .long sys_sync_file_range - .long sys_tee - .long sys_vmsplice /* 315 */ + .long sys_tee /* 315 */ + .long sys_vmsplice diff --git a/include/asm-sh/thread_info.h b/include/asm-sh/thread_info.h index b986a1914472..5eb874b065a6 100644 --- a/include/asm-sh/thread_info.h +++ b/include/asm-sh/thread_info.h @@ -94,6 +94,7 @@ static inline struct thread_info *current_thread_info(void) #define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ #define TIF_SIGPENDING 2 /* signal pending */ #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_RESTORE_SIGMASK 4 /* restore signal mask in do_signal() */ #define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ #define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ #define TIF_MEMDIE 18 @@ -102,6 +103,7 @@ static inline struct thread_info *current_thread_info(void) #define _TIF_NOTIFY_RESUME (1< Date: Wed, 27 Sep 2006 18:33:49 +0900 Subject: sh: Initial vsyscall page support. This implements initial support for the vsyscall page on SH. At the moment we leave it configurable due to having nommu to support from the same code base. We hook it up for the signal trampoline return at present, with more to be added later, once uClibc catches up. Signed-off-by: Paul Mundt --- arch/sh/kernel/Makefile | 1 + arch/sh/kernel/process.c | 2 +- arch/sh/kernel/signal.c | 17 ++- arch/sh/kernel/vsyscall/Makefile | 36 +++++++ arch/sh/kernel/vsyscall/vsyscall-note.S | 25 +++++ arch/sh/kernel/vsyscall/vsyscall-sigreturn.S | 39 +++++++ arch/sh/kernel/vsyscall/vsyscall-syscall.S | 10 ++ arch/sh/kernel/vsyscall/vsyscall-trapa.S | 42 ++++++++ arch/sh/kernel/vsyscall/vsyscall.c | 150 +++++++++++++++++++++++++++ arch/sh/kernel/vsyscall/vsyscall.lds.S | 74 +++++++++++++ arch/sh/mm/Kconfig | 13 +++ arch/sh/mm/init.c | 3 + arch/sh/mm/tlb-flush.c | 18 ++-- include/asm-sh/auxvec.h | 14 +++ include/asm-sh/elf.h | 20 ++++ include/asm-sh/mmu.h | 7 +- include/asm-sh/mmu_context.h | 8 +- include/asm-sh/page.h | 5 + include/asm-sh/processor.h | 6 ++ 19 files changed, 473 insertions(+), 17 deletions(-) create mode 100644 arch/sh/kernel/vsyscall/Makefile create mode 100644 arch/sh/kernel/vsyscall/vsyscall-note.S create mode 100644 arch/sh/kernel/vsyscall/vsyscall-sigreturn.S create mode 100644 arch/sh/kernel/vsyscall/vsyscall-syscall.S create mode 100644 arch/sh/kernel/vsyscall/vsyscall-trapa.S create mode 100644 arch/sh/kernel/vsyscall/vsyscall.c create mode 100644 arch/sh/kernel/vsyscall/vsyscall.lds.S (limited to 'arch/sh/kernel/signal.c') diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index 0e2148f63e7e..5da88a43d350 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile @@ -9,6 +9,7 @@ obj-y := process.o signal.o entry.o traps.o irq.o \ io.o io_generic.o sh_ksyms.o syscalls.o obj-y += cpu/ timers/ +obj-$(CONFIG_VSYSCALL) += vsyscall/ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_CF_ENABLER) += cf-enabler.o diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 630ec1af2483..0b1d5dd7a93b 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -355,7 +355,7 @@ struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *ne else if (next->thread.ubc_pc && next->mm) { int asid = 0; #ifdef CONFIG_MMU - asid |= next->mm->context & MMU_CONTEXT_ASID_MASK; + asid |= next->mm->context.id & MMU_CONTEXT_ASID_MASK; #endif ubc_set_tracing(asid, next->thread.ubc_pc); } else { diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index 2f1c9545b49f..5213f5bc6ce0 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -8,7 +8,6 @@ * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima * */ - #include #include #include @@ -21,6 +20,7 @@ #include #include #include +#include #include #include @@ -29,8 +29,6 @@ #include #include -#undef DEBUG - #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) /* @@ -312,6 +310,11 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) return (void __user *)((sp - frame_size) & -8ul); } +/* These symbols are defined with the addresses in the vsyscall page. + See vsyscall-trapa.S. */ +extern void __user __kernel_sigreturn; +extern void __user __kernel_rt_sigreturn; + static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs) { @@ -340,6 +343,10 @@ static int setup_frame(int sig, struct k_sigaction *ka, already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { regs->pr = (unsigned long) ka->sa.sa_restorer; +#ifdef CONFIG_VSYSCALL + } else if (likely(current->mm->context.vdso)) { + regs->pr = VDSO_SYM(&__kernel_sigreturn); +#endif } else { /* Generate return code (system call to sigreturn) */ err |= __put_user(MOVW(7), &frame->retcode[0]); @@ -416,6 +423,10 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { regs->pr = (unsigned long) ka->sa.sa_restorer; +#ifdef CONFIG_VSYSCALL + } else if (likely(current->mm->context.vdso)) { + regs->pr = VDSO_SYM(&__kernel_rt_sigreturn); +#endif } else { /* Generate return code (system call to rt_sigreturn) */ err |= __put_user(MOVW(7), &frame->retcode[0]); diff --git a/arch/sh/kernel/vsyscall/Makefile b/arch/sh/kernel/vsyscall/Makefile new file mode 100644 index 000000000000..4bbce1cfa359 --- /dev/null +++ b/arch/sh/kernel/vsyscall/Makefile @@ -0,0 +1,36 @@ +obj-y += vsyscall.o vsyscall-syscall.o + +$(obj)/vsyscall-syscall.o: \ + $(foreach F,trapa,$(obj)/vsyscall-$F.so) + +# Teach kbuild about targets +targets += $(foreach F,trapa,vsyscall-$F.o vsyscall-$F.so) +targets += vsyscall-note.o vsyscall.lds + +# The DSO images are built using a special linker script +quiet_cmd_syscall = SYSCALL $@ + cmd_syscall = $(CC) -nostdlib $(SYSCFLAGS_$(@F)) \ + -Wl,-T,$(filter-out FORCE,$^) -o $@ + +export CPPFLAGS_vsyscall.lds += -P -C -Ush + +vsyscall-flags = -shared -s -Wl,-soname=linux-gate.so.1 \ + $(call ld-option, -Wl$(comma)--hash-style=sysv) + +SYSCFLAGS_vsyscall-trapa.so = $(vsyscall-flags) + +$(obj)/vsyscall-trapa.so: \ +$(obj)/vsyscall-%.so: $(src)/vsyscall.lds $(obj)/vsyscall-%.o FORCE + $(call if_changed,syscall) + +# We also create a special relocatable object that should mirror the symbol +# table and layout of the linked DSO. With ld -R we can then refer to +# these symbols in the kernel code rather than hand-coded addresses. +extra-y += vsyscall-syms.o +$(obj)/built-in.o: $(obj)/vsyscall-syms.o +$(obj)/built-in.o: ld_flags += -R $(obj)/vsyscall-syms.o + +SYSCFLAGS_vsyscall-syms.o = -r +$(obj)/vsyscall-syms.o: $(src)/vsyscall.lds \ + $(obj)/vsyscall-trapa.o $(obj)/vsyscall-note.o FORCE + $(call if_changed,syscall) diff --git a/arch/sh/kernel/vsyscall/vsyscall-note.S b/arch/sh/kernel/vsyscall/vsyscall-note.S new file mode 100644 index 000000000000..d4b5be4f3d5f --- /dev/null +++ b/arch/sh/kernel/vsyscall/vsyscall-note.S @@ -0,0 +1,25 @@ +/* + * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text. + * Here we can supply some information useful to userland. + */ + +#include +#include + +#define ASM_ELF_NOTE_BEGIN(name, flags, vendor, type) \ + .section name, flags; \ + .balign 4; \ + .long 1f - 0f; /* name length */ \ + .long 3f - 2f; /* data length */ \ + .long type; /* note type */ \ +0: .asciz vendor; /* vendor name */ \ +1: .balign 4; \ +2: + +#define ASM_ELF_NOTE_END \ +3: .balign 4; /* pad out section */ \ + .previous + + ASM_ELF_NOTE_BEGIN(".note.kernel-version", "a", UTS_SYSNAME, 0) + .long LINUX_VERSION_CODE + ASM_ELF_NOTE_END diff --git a/arch/sh/kernel/vsyscall/vsyscall-sigreturn.S b/arch/sh/kernel/vsyscall/vsyscall-sigreturn.S new file mode 100644 index 000000000000..555a64f124ca --- /dev/null +++ b/arch/sh/kernel/vsyscall/vsyscall-sigreturn.S @@ -0,0 +1,39 @@ +#include + + .text + .balign 32 + .globl __kernel_sigreturn + .type __kernel_sigreturn,@function +__kernel_sigreturn: +.LSTART_sigreturn: + mov.w 1f, r3 + trapa #0x10 + or r0, r0 + or r0, r0 + or r0, r0 + or r0, r0 + or r0, r0 + +1: .short __NR_sigreturn +.LEND_sigreturn: + .size __kernel_sigreturn,.-.LSTART_sigreturn + + .balign 32 + .globl __kernel_rt_sigreturn + .type __kernel_rt_sigreturn,@function +__kernel_rt_sigreturn: +.LSTART_rt_sigreturn: + mov.w 1f, r3 + trapa #0x10 + or r0, r0 + or r0, r0 + or r0, r0 + or r0, r0 + or r0, r0 + +1: .short __NR_rt_sigreturn +.LEND_rt_sigreturn: + .size __kernel_rt_sigreturn,.-.LSTART_rt_sigreturn + + .section .eh_frame,"a",@progbits + .previous diff --git a/arch/sh/kernel/vsyscall/vsyscall-syscall.S b/arch/sh/kernel/vsyscall/vsyscall-syscall.S new file mode 100644 index 000000000000..c2ac7f0282b3 --- /dev/null +++ b/arch/sh/kernel/vsyscall/vsyscall-syscall.S @@ -0,0 +1,10 @@ +#include + +__INITDATA + + .globl vsyscall_trapa_start, vsyscall_trapa_end +vsyscall_trapa_start: + .incbin "arch/sh/kernel/vsyscall/vsyscall-trapa.so" +vsyscall_trapa_end: + +__FINIT diff --git a/arch/sh/kernel/vsyscall/vsyscall-trapa.S b/arch/sh/kernel/vsyscall/vsyscall-trapa.S new file mode 100644 index 000000000000..3b6eb34c43fa --- /dev/null +++ b/arch/sh/kernel/vsyscall/vsyscall-trapa.S @@ -0,0 +1,42 @@ + .text + .globl __kernel_vsyscall + .type __kernel_vsyscall,@function +__kernel_vsyscall: +.LSTART_vsyscall: + /* XXX: We'll have to do something here once we opt to use the vDSO + * page for something other than the signal trampoline.. as well as + * fill out .eh_frame -- PFM. */ +.LEND_vsyscall: + .size __kernel_vsyscall,.-.LSTART_vsyscall + .previous + + .section .eh_frame,"a",@progbits +.LCIE: + .ualong .LCIE_end - .LCIE_start +.LCIE_start: + .ualong 0 /* CIE ID */ + .byte 0x1 /* Version number */ + .string "zRS" /* NUL-terminated augmentation string */ + .uleb128 0x1 /* Code alignment factor */ + .sleb128 -4 /* Data alignment factor */ + .byte 0x11 /* Return address register column */ + /* Augmentation length and data (none) */ + .byte 0xc /* DW_CFA_def_cfa */ + .uleb128 0xf /* r15 */ + .uleb128 0x0 /* offset 0 */ + + .align 2 +.LCIE_end: + + .ualong .LFDE_end-.LFDE_start /* Length FDE */ +.LFDE_start: + .ualong .LCIE /* CIE pointer */ + .ualong .LSTART_vsyscall-. /* start address */ + .ualong .LEND_vsyscall-.LSTART_vsyscall + .uleb128 0 + .align 2 +.LFDE_end: + .previous + +/* Get the common code for the sigreturn entry points */ +#include "vsyscall-sigreturn.S" diff --git a/arch/sh/kernel/vsyscall/vsyscall.c b/arch/sh/kernel/vsyscall/vsyscall.c new file mode 100644 index 000000000000..075d6cc1a2d7 --- /dev/null +++ b/arch/sh/kernel/vsyscall/vsyscall.c @@ -0,0 +1,150 @@ +/* + * arch/sh/kernel/vsyscall.c + * + * Copyright (C) 2006 Paul Mundt + * + * vDSO randomization + * Copyright(C) 2005-2006, Red Hat, Inc., Ingo Molnar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * Should the kernel map a VDSO page into processes and pass its + * address down to glibc upon exec()? + */ +unsigned int __read_mostly vdso_enabled = 1; +EXPORT_SYMBOL_GPL(vdso_enabled); + +static int __init vdso_setup(char *s) +{ + vdso_enabled = simple_strtoul(s, NULL, 0); + return 1; +} +__setup("vdso=", vdso_setup); + +/* + * These symbols are defined by vsyscall.o to mark the bounds + * of the ELF DSO images included therein. + */ +extern const char vsyscall_trapa_start, vsyscall_trapa_end; +static void *syscall_page; + +int __init vsyscall_init(void) +{ + syscall_page = (void *)get_zeroed_page(GFP_ATOMIC); + + /* + * XXX: Map this page to a fixmap entry if we get around + * to adding the page to ELF core dumps + */ + + memcpy(syscall_page, + &vsyscall_trapa_start, + &vsyscall_trapa_end - &vsyscall_trapa_start); + + return 0; +} + +static struct page *syscall_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + unsigned long offset = address - vma->vm_start; + struct page *page; + + if (address < vma->vm_start || address > vma->vm_end) + return NOPAGE_SIGBUS; + + page = virt_to_page(syscall_page + offset); + + get_page(page); + + return page; +} + +/* Prevent VMA merging */ +static void syscall_vma_close(struct vm_area_struct *vma) +{ +} + +static struct vm_operations_struct syscall_vm_ops = { + .nopage = syscall_vma_nopage, + .close = syscall_vma_close, +}; + +/* Setup a VMA at program startup for the vsyscall page */ +int arch_setup_additional_pages(struct linux_binprm *bprm, + int executable_stack) +{ + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + unsigned long addr; + int ret; + + down_write(&mm->mmap_sem); + addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0); + if (IS_ERR_VALUE(addr)) { + ret = addr; + goto up_fail; + } + + vma = kmem_cache_zalloc(vm_area_cachep, SLAB_KERNEL); + if (!vma) { + ret = -ENOMEM; + goto up_fail; + } + + vma->vm_start = addr; + vma->vm_end = addr + PAGE_SIZE; + /* MAYWRITE to allow gdb to COW and set breakpoints */ + vma->vm_flags = VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC|VM_MAYWRITE; + vma->vm_flags |= mm->def_flags; + vma->vm_page_prot = protection_map[vma->vm_flags & 7]; + vma->vm_ops = &syscall_vm_ops; + vma->vm_mm = mm; + + ret = insert_vm_struct(mm, vma); + if (unlikely(ret)) { + kmem_cache_free(vm_area_cachep, vma); + goto up_fail; + } + + current->mm->context.vdso = (void *)addr; + + mm->total_vm++; +up_fail: + up_write(&mm->mmap_sem); + return ret; +} + +const char *arch_vma_name(struct vm_area_struct *vma) +{ + if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso) + return "[vdso]"; + + return NULL; +} + +struct vm_area_struct *get_gate_vma(struct task_struct *task) +{ + return NULL; +} + +int in_gate_area(struct task_struct *task, unsigned long address) +{ + return 0; +} + +int in_gate_area_no_task(unsigned long address) +{ + return 0; +} diff --git a/arch/sh/kernel/vsyscall/vsyscall.lds.S b/arch/sh/kernel/vsyscall/vsyscall.lds.S new file mode 100644 index 000000000000..b13c3d439fee --- /dev/null +++ b/arch/sh/kernel/vsyscall/vsyscall.lds.S @@ -0,0 +1,74 @@ +/* + * Linker script for vsyscall DSO. The vsyscall page is an ELF shared + * object prelinked to its virtual address, and with only one read-only + * segment (that fits in one page). This script controls its layout. + */ +#include + +#ifdef CONFIG_CPU_LITTLE_ENDIAN +OUTPUT_FORMAT("elf32-sh-linux", "elf32-sh-linux", "elf32-sh-linux") +#else +OUTPUT_FORMAT("elf32-shbig-linux", "elf32-shbig-linux", "elf32-shbig-linux") +#endif +OUTPUT_ARCH(sh) + +/* The ELF entry point can be used to set the AT_SYSINFO value. */ +ENTRY(__kernel_vsyscall); + +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) } + + /* This linker script is used both with -r and with -shared. + For the layouts to match, we need to skip more than enough + space for the dynamic symbol table et al. If this amount + is insufficient, ld -shared will barf. Just increase it here. */ + . = 0x400; + + .text : { *(.text) } :text =0x90909090 + .note : { *(.note.*) } :text :note + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + .dynamic : { *(.dynamic) } :text :dynamic + .useless : { + *(.got.plt) *(.got) + *(.data .data.* .gnu.linkonce.d.*) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + } :text +} + +/* + * 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 FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + note PT_NOTE FLAGS(4); /* PF_R */ + eh_frame_hdr 0x6474e550; /* PT_GNU_EH_FRAME, but ld doesn't match the name */ +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_2.6 { + global: + __kernel_vsyscall; + __kernel_sigreturn; + __kernel_rt_sigreturn; + + local: *; + }; +} diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index b445d02075e8..9dd606464d23 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig @@ -223,6 +223,19 @@ config 32BIT 32-bits through the SH-4A PMB. If this is not set, legacy 29-bit physical addressing will be used. +config VSYSCALL + bool "Support vsyscall page" + depends on MMU + default y + help + This will enable support for the kernel mapping a vDSO page + in process space, and subsequently handing down the entry point + to the libc through the ELF auxiliary vector. + + From the kernel side this is used for the signal trampoline. + For systems with an MMU that can afford to give up a page, + (the default value) say Y. + choice prompt "HugeTLB page size" depends on HUGETLB_PAGE && CPU_SH4 && MMU diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index ad182b31d846..7154d1ce9785 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -286,6 +286,9 @@ void __init mem_init(void) initsize >> 10); p3_cache_init(); + + /* Initialize the vDSO */ + vsyscall_init(); } void free_initmem(void) diff --git a/arch/sh/mm/tlb-flush.c b/arch/sh/mm/tlb-flush.c index fd7e42bcaa40..73ec7f6084fa 100644 --- a/arch/sh/mm/tlb-flush.c +++ b/arch/sh/mm/tlb-flush.c @@ -14,12 +14,12 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { - if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) { + if (vma->vm_mm && vma->vm_mm->context.id != NO_CONTEXT) { unsigned long flags; unsigned long asid; unsigned long saved_asid = MMU_NO_ASID; - asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK; + asid = vma->vm_mm->context.id & MMU_CONTEXT_ASID_MASK; page &= PAGE_MASK; local_irq_save(flags); @@ -39,20 +39,21 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, { struct mm_struct *mm = vma->vm_mm; - if (mm->context != NO_CONTEXT) { + if (mm->context.id != NO_CONTEXT) { unsigned long flags; int size; local_irq_save(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ - mm->context = NO_CONTEXT; + mm->context.id = NO_CONTEXT; if (mm == current->mm) activate_context(mm); } else { - unsigned long asid = mm->context&MMU_CONTEXT_ASID_MASK; + unsigned long asid; unsigned long saved_asid = MMU_NO_ASID; + asid = mm->context.id & MMU_CONTEXT_ASID_MASK; start &= PAGE_MASK; end += (PAGE_SIZE - 1); end &= PAGE_MASK; @@ -81,9 +82,10 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ flush_tlb_all(); } else { - unsigned long asid = init_mm.context&MMU_CONTEXT_ASID_MASK; + unsigned long asid; unsigned long saved_asid = get_asid(); + asid = init_mm.context.id & MMU_CONTEXT_ASID_MASK; start &= PAGE_MASK; end += (PAGE_SIZE - 1); end &= PAGE_MASK; @@ -101,11 +103,11 @@ void flush_tlb_mm(struct mm_struct *mm) { /* Invalidate all TLB of this process. */ /* Instead of invalidating each TLB, we get new MMU context. */ - if (mm->context != NO_CONTEXT) { + if (mm->context.id != NO_CONTEXT) { unsigned long flags; local_irq_save(flags); - mm->context = NO_CONTEXT; + mm->context.id = NO_CONTEXT; if (mm == current->mm) activate_context(mm); local_irq_restore(flags); diff --git a/include/asm-sh/auxvec.h b/include/asm-sh/auxvec.h index fc21e4db5881..1b6916e63e90 100644 --- a/include/asm-sh/auxvec.h +++ b/include/asm-sh/auxvec.h @@ -1,4 +1,18 @@ #ifndef __ASM_SH_AUXVEC_H #define __ASM_SH_AUXVEC_H +/* + * Architecture-neutral AT_ values in 0-17, leave some room + * for more of them. + */ + +#ifdef CONFIG_VSYSCALL +/* + * Only define this in the vsyscall case, the entry point to + * the vsyscall page gets placed here. The kernel will attempt + * to build a gate VMA we don't care about otherwise.. + */ +#define AT_SYSINFO_EHDR 33 +#endif + #endif /* __ASM_SH_AUXVEC_H */ diff --git a/include/asm-sh/elf.h b/include/asm-sh/elf.h index cc8e5e767345..3a07ab40ac4d 100644 --- a/include/asm-sh/elf.h +++ b/include/asm-sh/elf.h @@ -121,4 +121,24 @@ extern int dump_task_fpu (struct task_struct *, elf_fpregset_t *); #define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs) #endif +#ifdef CONFIG_VSYSCALL +/* vDSO has arch_setup_additional_pages */ +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES +struct linux_binprm; +extern int arch_setup_additional_pages(struct linux_binprm *bprm, + int executable_stack); + +extern unsigned int vdso_enabled; +extern void __kernel_vsyscall; + +#define VDSO_BASE ((unsigned long)current->mm->context.vdso) +#define VDSO_SYM(x) (VDSO_BASE + (unsigned long)(x)) + +#define ARCH_DLINFO \ +do { \ + if (vdso_enabled) \ + NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_BASE); \ +} while (0) +#endif /* CONFIG_VSYSCALL */ + #endif /* __ASM_SH_ELF_H */ diff --git a/include/asm-sh/mmu.h b/include/asm-sh/mmu.h index 6383dc84501e..cf47df79bb94 100644 --- a/include/asm-sh/mmu.h +++ b/include/asm-sh/mmu.h @@ -11,7 +11,12 @@ typedef struct { #else /* Default "unsigned long" context */ -typedef unsigned long mm_context_t; +typedef unsigned long mm_context_id_t; + +typedef struct { + mm_context_id_t id; + void *vdso; +} mm_context_t; #endif /* CONFIG_MMU */ diff --git a/include/asm-sh/mmu_context.h b/include/asm-sh/mmu_context.h index 87678ba8d6b6..c7088efe579a 100644 --- a/include/asm-sh/mmu_context.h +++ b/include/asm-sh/mmu_context.h @@ -49,7 +49,7 @@ get_mmu_context(struct mm_struct *mm) unsigned long mc = mmu_context_cache; /* Check if we have old version of context. */ - if (((mm->context ^ mc) & MMU_CONTEXT_VERSION_MASK) == 0) + if (((mm->context.id ^ mc) & MMU_CONTEXT_VERSION_MASK) == 0) /* It's up to date, do nothing */ return; @@ -68,7 +68,7 @@ get_mmu_context(struct mm_struct *mm) if (!mc) mmu_context_cache = mc = MMU_CONTEXT_FIRST_VERSION; } - mm->context = mc; + mm->context.id = mc; } /* @@ -78,7 +78,7 @@ get_mmu_context(struct mm_struct *mm) static __inline__ int init_new_context(struct task_struct *tsk, struct mm_struct *mm) { - mm->context = NO_CONTEXT; + mm->context.id = NO_CONTEXT; return 0; } @@ -123,7 +123,7 @@ static __inline__ unsigned long get_asid(void) static __inline__ void activate_context(struct mm_struct *mm) { get_mmu_context(mm); - set_asid(mm->context & MMU_CONTEXT_ASID_MASK); + set_asid(mm->context.id & MMU_CONTEXT_ASID_MASK); } /* MMU_TTB can be used for optimizing the fault handling. diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index acf6977b4042..3d8dae31a6f6 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h @@ -117,5 +117,10 @@ typedef struct { unsigned long pgprot; } pgprot_t; #include #include +/* vDSO support */ +#ifdef CONFIG_VSYSCALL +#define __HAVE_ARCH_GATE_AREA +#endif + #endif /* __KERNEL__ */ #endif /* __ASM_SH_PAGE_H */ diff --git a/include/asm-sh/processor.h b/include/asm-sh/processor.h index b7cba4e91a72..474773853cd1 100644 --- a/include/asm-sh/processor.h +++ b/include/asm-sh/processor.h @@ -276,5 +276,11 @@ static inline void prefetch(void *x) #define prefetchw(x) prefetch(x) #endif +#ifdef CONFIG_VSYSCALL +extern int vsyscall_init(void); +#else +#define vsyscall_init() do { } while (0) +#endif + #endif /* __KERNEL__ */ #endif /* __ASM_SH_PROCESSOR_H */ -- cgit