From f7a7b15344e2bccdd1c73d42685edfe7d43ec5b7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sat, 10 Nov 2007 20:07:57 +0900 Subject: sh: Move in the SH-5 signal trampoline impl. Signed-off-by: Paul Mundt --- arch/sh/kernel/signal_32.c | 629 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100644 arch/sh/kernel/signal_32.c (limited to 'arch/sh/kernel/signal_32.c') diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c new file mode 100644 index 000000000000..ca754fd42437 --- /dev/null +++ b/arch/sh/kernel/signal_32.c @@ -0,0 +1,629 @@ +/* + * linux/arch/sh/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * + * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int +sys_sigsuspend(old_sigset_t mask, + unsigned long r5, unsigned long r6, unsigned long r7, + struct pt_regs __regs) +{ + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + current->saved_sigmask = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} + +asmlinkage int +sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, + unsigned long r6, unsigned long r7, + struct pt_regs __regs) +{ + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + + return do_sigaltstack(uss, uoss, regs->regs[15]); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */ +#if defined(CONFIG_CPU_SH2) +#define TRAP_NOARG 0xc320 /* Syscall w/no args (NR in R3) */ +#else +#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) */ +#endif +#define OR_R0_R0 0x200b /* or r0,r0 (insert to avoid hardware bug) */ + +struct sigframe +{ + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS-1]; + u16 retcode[8]; +}; + +struct rt_sigframe +{ + struct siginfo info; + struct ucontext uc; + u16 retcode[8]; +}; + +#ifdef CONFIG_SH_FPU +static inline int restore_sigcontext_fpu(struct sigcontext __user *sc) +{ + struct task_struct *tsk = current; + + if (!(current_cpu_data.flags & CPU_HAS_FPU)) + return 0; + + set_used_math(); + return __copy_from_user(&tsk->thread.fpu.hard, &sc->sc_fpregs[0], + sizeof(long)*(16*2+2)); +} + +static inline int save_sigcontext_fpu(struct sigcontext __user *sc, + struct pt_regs *regs) +{ + struct task_struct *tsk = current; + + if (!(current_cpu_data.flags & CPU_HAS_FPU)) + return 0; + + if (!used_math()) { + __put_user(0, &sc->sc_ownedfp); + return 0; + } + + __put_user(1, &sc->sc_ownedfp); + + /* This will cause a "finit" to be triggered by the next + attempted FPU operation by the 'current' process. + */ + clear_used_math(); + + unlazy_fpu(tsk, regs); + return __copy_to_user(&sc->sc_fpregs[0], &tsk->thread.fpu.hard, + sizeof(long)*(16*2+2)); +} +#endif /* CONFIG_SH_FPU */ + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *r0_p) +{ + unsigned int err = 0; + +#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) + COPY(regs[1]); + COPY(regs[2]); COPY(regs[3]); + COPY(regs[4]); COPY(regs[5]); + COPY(regs[6]); COPY(regs[7]); + COPY(regs[8]); COPY(regs[9]); + COPY(regs[10]); COPY(regs[11]); + COPY(regs[12]); COPY(regs[13]); + COPY(regs[14]); COPY(regs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + +#ifdef CONFIG_SH_FPU + if (current_cpu_data.flags & CPU_HAS_FPU) { + int owned_fp; + struct task_struct *tsk = current; + + regs->sr |= SR_FD; /* Release FPU */ + clear_fpu(tsk, regs); + clear_used_math(); + __get_user (owned_fp, &sc->sc_ownedfp); + if (owned_fp) + err |= restore_sigcontext_fpu(sc); + } +#endif + + regs->tra = -1; /* disable syscall checks */ + err |= __get_user(*r0_p, &sc->sc_regs[0]); + return err; +} + +asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs) +{ + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15]; + sigset_t set; + int r0; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_NSIG_WORDS > 1 + && __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->sc, &r0)) + goto badframe; + return r0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs) +{ + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15]; + sigset_t set; + stack_t st; + int r0; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack((const stack_t __user *)&st, NULL, (unsigned long)frame); + + return r0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, + unsigned long mask) +{ + int err = 0; + +#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) + COPY(regs[0]); COPY(regs[1]); + COPY(regs[2]); COPY(regs[3]); + COPY(regs[4]); COPY(regs[5]); + COPY(regs[6]); COPY(regs[7]); + COPY(regs[8]); COPY(regs[9]); + COPY(regs[10]); COPY(regs[11]); + COPY(regs[12]); COPY(regs[13]); + COPY(regs[14]); COPY(regs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + +#ifdef CONFIG_SH_FPU + err |= save_sigcontext_fpu(sc, regs); +#endif + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void __user * +get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) +{ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (sas_ss_flags(sp) == 0) + sp = current->sas_ss_sp + current->sas_ss_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) +{ + struct sigframe __user *frame; + int err = 0; + int signal; + + frame = get_sigframe(ka, regs->regs[15], sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + + 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. */ + 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]); + err |= __put_user(TRAP_NOARG, &frame->retcode[1]); + err |= __put_user(OR_R0_R0, &frame->retcode[2]); + err |= __put_user(OR_R0_R0, &frame->retcode[3]); + err |= __put_user(OR_R0_R0, &frame->retcode[4]); + err |= __put_user(OR_R0_R0, &frame->retcode[5]); + err |= __put_user(OR_R0_R0, &frame->retcode[6]); + err |= __put_user((__NR_sigreturn), &frame->retcode[7]); + regs->pr = (unsigned long) frame->retcode; + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->regs[15] = (unsigned long) frame; + regs->regs[4] = signal; /* Arg for signal handler */ + regs->regs[5] = 0; + regs->regs[6] = (unsigned long) &frame->sc; + regs->pc = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + + pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", + current->comm, task_pid_nr(current), 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 0; + +give_sigsegv: + force_sigsegv(sig, current); + return -EFAULT; +} + +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; + int err = 0; + int signal; + + frame = get_sigframe(ka, regs->regs[15], sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= copy_siginfo_to_user(&frame->info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user((void *)current->sas_ss_sp, + &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->regs[15]), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.uc_mcontext, + regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + /* Set up to return from userspace. If provided, use a stub + 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]); + err |= __put_user(TRAP_NOARG, &frame->retcode[1]); + err |= __put_user(OR_R0_R0, &frame->retcode[2]); + err |= __put_user(OR_R0_R0, &frame->retcode[3]); + err |= __put_user(OR_R0_R0, &frame->retcode[4]); + err |= __put_user(OR_R0_R0, &frame->retcode[5]); + err |= __put_user(OR_R0_R0, &frame->retcode[6]); + err |= __put_user((__NR_rt_sigreturn), &frame->retcode[7]); + regs->pr = (unsigned long) frame->retcode; + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->regs[15] = (unsigned long) frame; + regs->regs[4] = signal; /* Arg for signal handler */ + regs->regs[5] = (unsigned long) &frame->info; + regs->regs[6] = (unsigned long) &frame->uc; + regs->pc = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + + pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", + current->comm, task_pid_nr(current), 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 0; + +give_sigsegv: + force_sigsegv(sig, current); + return -EFAULT; +} + +/* + * OK, we're invoking a handler + */ + +static int +handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *oldset, struct pt_regs *regs, unsigned int save_r0) +{ + int ret; + + /* Are we from a system call? */ + if (regs->tra >= 0) { + /* If so, check system call restarting.. */ + switch (regs->regs[0]) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + regs->regs[0] = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->regs[0] = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->regs[0] = save_r0; + regs->pc -= instruction_size( + ctrl_inw(regs->pc - 4)); + break; + } +#ifdef CONFIG_GUSA + } else { + /* gUSA handling */ + preempt_disable(); + + if (regs->regs[15] >= 0xc0000000) { + int offset = (int)regs->regs[15]; + + /* Reset stack pointer: clear critical region mark */ + regs->regs[15] = regs->regs[1]; + if (regs->pc < regs->regs[0]) + /* Go to rewind point #1 */ + regs->pc = regs->regs[0] + offset - + instruction_size(ctrl_inw(regs->pc-4)); + } + + preempt_enable_no_resched(); +#endif + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(sig, ka, info, oldset, regs); + else + ret = setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + 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; +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +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 + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return; + + if (try_to_freeze()) + goto no_signal; + + 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. */ + if (handle_signal(signr, &ka, &info, oldset, + regs, save_r0) == 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); + } + + return; + } + + no_signal: + /* Did we come from a system call? */ + if (regs->tra >= 0) { + /* Restart the system call - no handlers present */ + if (regs->regs[0] == -ERESTARTNOHAND || + regs->regs[0] == -ERESTARTSYS || + regs->regs[0] == -ERESTARTNOINTR) { + regs->regs[0] = save_r0; + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); + } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); + regs->regs[3] = __NR_restart_syscall; + } + } + + /* 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); +} -- cgit v1.2.3-59-g8ed1b From 1efe4ce3ca126da77e450d5a83f7201949d76f62 Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Fri, 30 Nov 2007 16:12:36 +0900 Subject: sh: GUSA atomic rollback support. This implements kernel-level atomic rollback built on top of gUSA, as an alternative non-IRQ based atomicity method. This is generally a faster method for platforms that are lacking the LL/SC pairs that SH-4A and later use, and is only supportable on legacy cores. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 12 ++- arch/sh/kernel/cpu/sh3/entry.S | 24 +++++- arch/sh/kernel/entry-common.S | 19 ----- arch/sh/kernel/process_32.c | 19 ----- arch/sh/kernel/signal_32.c | 18 ----- include/asm-sh/atomic-grb.h | 169 +++++++++++++++++++++++++++++++++++++++++ include/asm-sh/atomic.h | 10 ++- include/asm-sh/bitops-grb.h | 169 +++++++++++++++++++++++++++++++++++++++++ include/asm-sh/bitops-irq.h | 91 ++++++++++++++++++++++ include/asm-sh/bitops.h | 91 ++-------------------- include/asm-sh/cmpxchg-grb.h | 70 +++++++++++++++++ include/asm-sh/cmpxchg-irq.h | 40 ++++++++++ include/asm-sh/system.h | 40 ++-------- 13 files changed, 591 insertions(+), 181 deletions(-) create mode 100644 include/asm-sh/atomic-grb.h create mode 100644 include/asm-sh/bitops-grb.h create mode 100644 include/asm-sh/bitops-irq.h create mode 100644 include/asm-sh/cmpxchg-grb.h create mode 100644 include/asm-sh/cmpxchg-irq.h (limited to 'arch/sh/kernel/signal_32.c') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 2dc3b177193c..f645f8416f1c 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -692,7 +692,7 @@ source "kernel/Kconfig.preempt" config GUSA def_bool y - depends on !SMP + depends on !SMP && SUPERH32 help This enables support for gUSA (general UserSpace Atomicity). This is the default implementation for both UP and non-ll/sc @@ -704,6 +704,16 @@ config GUSA This should only be disabled for special cases where alternate atomicity implementations exist. +config GUSA_RB + bool "Implement atomic operations by roll-back (gRB) (EXPERIMENTAL)" + depends on GUSA && CPU_SH3 || (CPU_SH4 && !CPU_SH4A) + help + Enabling this option will allow the kernel to implement some + atomic operations using a software implemention of load-locked/ + store-conditional (LLSC). On machines which do not have hardware + LLSC, this should be more efficient than the other alternative of + disabling insterrupts around the atomic sequence. + endmenu menu "Boot options" diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index 0d12a124055c..4004073f98cd 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S @@ -13,8 +13,9 @@ #include #include #include -#include #include +#include +#include ! NOTE: ! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address @@ -409,6 +410,27 @@ ENTRY(handle_exception) ! Using k0, k1 for scratch registers (r0_bank1, r1_bank), ! save all registers onto stack. ! + +#ifdef CONFIG_GUSA + ! Check for roll back gRB (User and Kernel) + mov r15, k0 + shll k0 + bf/s 1f + shll k0 + bf/s 1f + stc spc, k1 + stc r0_bank, k0 + cmp/hs k0, k1 ! test k1 (saved PC) >= k0 (saved r0) + bt/s 2f + stc r1_bank, k1 + + add #-2, k0 + add r15, k0 + ldc k0, spc ! PC = saved r0 + r15 - 2 +2: mov k1, r15 ! SP = r1 +1: +#endif + stc ssr, k0 ! Is it from kernel space? shll k0 ! Check MD bit (bit30) by shifting it into... shll k0 ! ...the T bit diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S index 397ac71d97f1..926b2e7b11c1 100644 --- a/arch/sh/kernel/entry-common.S +++ b/arch/sh/kernel/entry-common.S @@ -176,25 +176,6 @@ work_notifysig: jmp @r1 lds r0, pr work_resched: -#if defined(CONFIG_GUSA) && !defined(CONFIG_PREEMPT) - ! gUSA handling - mov.l @(OFF_SP,r15), r0 ! get user space stack pointer - mov r0, r1 - shll r0 - bf/s 1f - shll r0 - bf/s 1f - mov #OFF_PC, r0 - ! SP >= 0xc0000000 : gUSA mark - mov.l @(r0,r15), r2 ! get user space PC (program counter) - mov.l @(OFF_R0,r15), r3 ! end point - cmp/hs r3, r2 ! r2 >= r3? - bt 1f - add r3, r1 ! rewind point #2 - mov.l r1, @(r0,r15) ! reset PC to rewind point #2 - ! -1: -#endif mov.l 1f, r1 jsr @r1 ! schedule nop diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index b48324867eee..9ab1926b9d10 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -322,25 +322,6 @@ struct task_struct *__switch_to(struct task_struct *prev, unlazy_fpu(prev, task_pt_regs(prev)); #endif -#if defined(CONFIG_GUSA) && defined(CONFIG_PREEMPT) - { - struct pt_regs *regs; - - preempt_disable(); - regs = task_pt_regs(prev); - if (user_mode(regs) && regs->regs[15] >= 0xc0000000) { - int offset = (int)regs->regs[15]; - - /* Reset stack pointer: clear critical region mark */ - regs->regs[15] = regs->regs[1]; - if (regs->pc < regs->regs[0]) - /* Go to rewind point */ - regs->pc = regs->regs[0] + offset; - } - preempt_enable_no_resched(); - } -#endif - #ifdef CONFIG_MMU /* * Restore the kernel mode register diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c index ca754fd42437..f6b5fbfe75c4 100644 --- a/arch/sh/kernel/signal_32.c +++ b/arch/sh/kernel/signal_32.c @@ -507,24 +507,6 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, ctrl_inw(regs->pc - 4)); break; } -#ifdef CONFIG_GUSA - } else { - /* gUSA handling */ - preempt_disable(); - - if (regs->regs[15] >= 0xc0000000) { - int offset = (int)regs->regs[15]; - - /* Reset stack pointer: clear critical region mark */ - regs->regs[15] = regs->regs[1]; - if (regs->pc < regs->regs[0]) - /* Go to rewind point #1 */ - regs->pc = regs->regs[0] + offset - - instruction_size(ctrl_inw(regs->pc-4)); - } - - preempt_enable_no_resched(); -#endif } /* Set up the stack frame */ diff --git a/include/asm-sh/atomic-grb.h b/include/asm-sh/atomic-grb.h new file mode 100644 index 000000000000..4c5b7dbfcedb --- /dev/null +++ b/include/asm-sh/atomic-grb.h @@ -0,0 +1,169 @@ +#ifndef __ASM_SH_ATOMIC_GRB_H +#define __ASM_SH_ATOMIC_GRB_H + +static inline void atomic_add(int i, atomic_t *v) +{ + int tmp; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " add %2, %0 \n\t" /* add */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (v) + : "r" (i) + : "memory" , "r0", "r1"); +} + +static inline void atomic_sub(int i, atomic_t *v) +{ + int tmp; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " sub %2, %0 \n\t" /* sub */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (v) + : "r" (i) + : "memory" , "r0", "r1"); +} + +static inline int atomic_add_return(int i, atomic_t *v) +{ + int tmp; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " add %2, %0 \n\t" /* add */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (v) + : "r" (i) + : "memory" , "r0", "r1"); + + return tmp; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + int tmp; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " sub %2, %0 \n\t" /* sub */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (v) + : "r" (i) + : "memory", "r0", "r1"); + + return tmp; +} + +static inline void atomic_clear_mask(unsigned int mask, atomic_t *v) +{ + int tmp; + unsigned int _mask = ~mask; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " and %2, %0 \n\t" /* add */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (v) + : "r" (_mask) + : "memory" , "r0", "r1"); +} + +static inline void atomic_set_mask(unsigned int mask, atomic_t *v) +{ + int tmp; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " or %2, %0 \n\t" /* or */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (v) + : "r" (mask) + : "memory" , "r0", "r1"); +} + +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" + " nop \n\t" + " mov r15, r1 \n\t" + " mov #-8, r15 \n\t" + " mov.l @%1, %0 \n\t" + " cmp/eq %2, %0 \n\t" + " bf 1f \n\t" + " mov.l %3, @%1 \n\t" + "1: mov r1, r15 \n\t" + : "=&r" (ret) + : "r" (v), "r" (old), "r" (new) + : "memory" , "r0", "r1" , "t"); + + return ret; +} + +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long tmp; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" + " nop \n\t" + " mov r15, r1 \n\t" + " mov #-12, r15 \n\t" + " mov.l @%2, %1 \n\t" + " mov %1, %0 \n\t" + " cmp/eq %4, %0 \n\t" + " bt/s 1f \n\t" + " add %3, %1 \n\t" + " mov.l %1, @%2 \n\t" + "1: mov r1, r15 \n\t" + : "=&r" (ret), "=&r" (tmp) + : "r" (v), "r" (a), "r" (u) + : "memory" , "r0", "r1" , "t"); + + return ret != u; +} +#endif /* __ASM_SH_ATOMIC_GRB_H */ diff --git a/include/asm-sh/atomic.h b/include/asm-sh/atomic.h index e12570b9339d..c043ef003028 100644 --- a/include/asm-sh/atomic.h +++ b/include/asm-sh/atomic.h @@ -17,7 +17,9 @@ typedef struct { volatile int counter; } atomic_t; #include #include -#ifdef CONFIG_CPU_SH4A +#if defined(CONFIG_GUSA_RB) +#include +#elif defined(CONFIG_CPU_SH4A) #include #else #include @@ -44,6 +46,7 @@ typedef struct { volatile int counter; } atomic_t; #define atomic_inc(v) atomic_add(1,(v)) #define atomic_dec(v) atomic_sub(1,(v)) +#ifndef CONFIG_GUSA_RB static inline int atomic_cmpxchg(atomic_t *v, int old, int new) { int ret; @@ -58,8 +61,6 @@ static inline int atomic_cmpxchg(atomic_t *v, int old, int new) return ret; } -#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) - static inline int atomic_add_unless(atomic_t *v, int a, int u) { int ret; @@ -73,6 +74,9 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u) return ret != u; } +#endif + +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) /* Atomic operations are already serializing on SH */ diff --git a/include/asm-sh/bitops-grb.h b/include/asm-sh/bitops-grb.h new file mode 100644 index 000000000000..a5907b94395b --- /dev/null +++ b/include/asm-sh/bitops-grb.h @@ -0,0 +1,169 @@ +#ifndef __ASM_SH_BITOPS_GRB_H +#define __ASM_SH_BITOPS_GRB_H + +static inline void set_bit(int nr, volatile void * addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " or %2, %0 \n\t" /* or */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (a) + : "r" (mask) + : "memory" , "r0", "r1"); +} + +static inline void clear_bit(int nr, volatile void * addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = ~(1 << (nr & 0x1f)); + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " and %2, %0 \n\t" /* and */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (a) + : "r" (mask) + : "memory" , "r0", "r1"); +} + +static inline void change_bit(int nr, volatile void * addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%1, %0 \n\t" /* load old value */ + " xor %2, %0 \n\t" /* xor */ + " mov.l %0, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "+r" (a) + : "r" (mask) + : "memory" , "r0", "r1"); +} + +static inline int test_and_set_bit(int nr, volatile void * addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-14, r15 \n\t" /* LOGIN: r15 = size */ + " mov.l @%2, %0 \n\t" /* load old value */ + " mov %0, %1 \n\t" + " tst %1, %3 \n\t" /* T = ((*a & mask) == 0) */ + " mov #-1, %1 \n\t" /* retvat = -1 */ + " negc %1, %1 \n\t" /* retval = (mask & *a) != 0 */ + " or %3, %0 \n\t" + " mov.l %0, @%2 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "=&r" (retval), + "+r" (a) + : "r" (mask) + : "memory" , "r0", "r1" ,"t"); + + return retval; +} + +static inline int test_and_clear_bit(int nr, volatile void * addr) +{ + int mask, retval,not_mask; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + not_mask = ~mask; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-14, r15 \n\t" /* LOGIN */ + " mov.l @%2, %0 \n\t" /* load old value */ + " mov %0, %1 \n\t" /* %1 = *a */ + " tst %1, %3 \n\t" /* T = ((*a & mask) == 0) */ + " mov #-1, %1 \n\t" /* retvat = -1 */ + " negc %1, %1 \n\t" /* retval = (mask & *a) != 0 */ + " and %4, %0 \n\t" + " mov.l %0, @%2 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "=&r" (retval), + "+r" (a) + : "r" (mask), + "r" (not_mask) + : "memory" , "r0", "r1", "t"); + + return retval; +} + +static inline int test_and_change_bit(int nr, volatile void * addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-14, r15 \n\t" /* LOGIN */ + " mov.l @%2, %0 \n\t" /* load old value */ + " mov %0, %1 \n\t" /* %1 = *a */ + " tst %1, %3 \n\t" /* T = ((*a & mask) == 0) */ + " mov #-1, %1 \n\t" /* retvat = -1 */ + " negc %1, %1 \n\t" /* retval = (mask & *a) != 0 */ + " xor %3, %0 \n\t" + " mov.l %0, @%2 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (tmp), + "=&r" (retval), + "+r" (a) + : "r" (mask) + : "memory" , "r0", "r1", "t"); + + return retval; +} +#endif /* __ASM_SH_BITOPS_GRB_H */ diff --git a/include/asm-sh/bitops-irq.h b/include/asm-sh/bitops-irq.h new file mode 100644 index 000000000000..653a12750584 --- /dev/null +++ b/include/asm-sh/bitops-irq.h @@ -0,0 +1,91 @@ +#ifndef __ASM_SH_BITOPS_IRQ_H +#define __ASM_SH_BITOPS_IRQ_H + +static inline void set_bit(int nr, volatile void *addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + local_irq_save(flags); + *a |= mask; + local_irq_restore(flags); +} + +static inline void clear_bit(int nr, volatile void *addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + local_irq_save(flags); + *a &= ~mask; + local_irq_restore(flags); +} + +static inline void change_bit(int nr, volatile void *addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + local_irq_save(flags); + *a ^= mask; + local_irq_restore(flags); +} + +static inline int test_and_set_bit(int nr, volatile void *addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + local_irq_save(flags); + retval = (mask & *a) != 0; + *a |= mask; + local_irq_restore(flags); + + return retval; +} + +static inline int test_and_clear_bit(int nr, volatile void *addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + local_irq_save(flags); + retval = (mask & *a) != 0; + *a &= ~mask; + local_irq_restore(flags); + + return retval; +} + +static inline int test_and_change_bit(int nr, volatile void *addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + local_irq_save(flags); + retval = (mask & *a) != 0; + *a ^= mask; + local_irq_restore(flags); + + return retval; +} + +#endif /* __ASM_SH_BITOPS_IRQ_H */ diff --git a/include/asm-sh/bitops.h b/include/asm-sh/bitops.h index a7bd81a7f064..b6ba5a60dec2 100644 --- a/include/asm-sh/bitops.h +++ b/include/asm-sh/bitops.h @@ -11,97 +11,18 @@ /* For __swab32 */ #include -static inline void set_bit(int nr, volatile void * addr) -{ - int mask; - volatile unsigned int *a = addr; - unsigned long flags; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - local_irq_save(flags); - *a |= mask; - local_irq_restore(flags); -} +#ifdef CONFIG_GUSA_RB +#include +#else +#include +#endif + /* * clear_bit() doesn't provide any barrier for the compiler. */ #define smp_mb__before_clear_bit() barrier() #define smp_mb__after_clear_bit() barrier() -static inline void clear_bit(int nr, volatile void * addr) -{ - int mask; - volatile unsigned int *a = addr; - unsigned long flags; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - local_irq_save(flags); - *a &= ~mask; - local_irq_restore(flags); -} - -static inline void change_bit(int nr, volatile void * addr) -{ - int mask; - volatile unsigned int *a = addr; - unsigned long flags; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - local_irq_save(flags); - *a ^= mask; - local_irq_restore(flags); -} - -static inline int test_and_set_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - unsigned long flags; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - local_irq_save(flags); - retval = (mask & *a) != 0; - *a |= mask; - local_irq_restore(flags); - - return retval; -} - -static inline int test_and_clear_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - unsigned long flags; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - local_irq_save(flags); - retval = (mask & *a) != 0; - *a &= ~mask; - local_irq_restore(flags); - - return retval; -} - -static inline int test_and_change_bit(int nr, volatile void * addr) -{ - int mask, retval; - volatile unsigned int *a = addr; - unsigned long flags; - - a += nr >> 5; - mask = 1 << (nr & 0x1f); - local_irq_save(flags); - retval = (mask & *a) != 0; - *a ^= mask; - local_irq_restore(flags); - - return retval; -} #include diff --git a/include/asm-sh/cmpxchg-grb.h b/include/asm-sh/cmpxchg-grb.h new file mode 100644 index 000000000000..e2681abe764f --- /dev/null +++ b/include/asm-sh/cmpxchg-grb.h @@ -0,0 +1,70 @@ +#ifndef __ASM_SH_CMPXCHG_GRB_H +#define __ASM_SH_CMPXCHG_GRB_H + +static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val) +{ + unsigned long retval; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " nop \n\t" + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-4, r15 \n\t" /* LOGIN */ + " mov.l @%1, %0 \n\t" /* load old value */ + " mov.l %2, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (retval), + "+r" (m) + : "r" (val) + : "memory", "r0", "r1"); + + return retval; +} + +static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val) +{ + unsigned long retval; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-6, r15 \n\t" /* LOGIN */ + " mov.b @%1, %0 \n\t" /* load old value */ + " extu.b %0, %0 \n\t" /* extend as unsigned */ + " mov.b %2, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (retval), + "+r" (m) + : "r" (val) + : "memory" , "r0", "r1"); + + return retval; +} + +static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old, + unsigned long new) +{ + unsigned long retval; + + __asm__ __volatile__ ( + " .align 2 \n\t" + " mova 1f, r0 \n\t" /* r0 = end point */ + " nop \n\t" + " mov r15, r1 \n\t" /* r1 = saved sp */ + " mov #-8, r15 \n\t" /* LOGIN */ + " mov.l @%1, %0 \n\t" /* load old value */ + " cmp/eq %0, %2 \n\t" + " bf 1f \n\t" /* if not equal */ + " mov.l %2, @%1 \n\t" /* store new value */ + "1: mov r1, r15 \n\t" /* LOGOUT */ + : "=&r" (retval), + "+r" (m) + : "r" (new) + : "memory" , "r0", "r1", "t"); + + return retval; +} + +#endif /* __ASM_SH_CMPXCHG_GRB_H */ diff --git a/include/asm-sh/cmpxchg-irq.h b/include/asm-sh/cmpxchg-irq.h new file mode 100644 index 000000000000..43049ec0554b --- /dev/null +++ b/include/asm-sh/cmpxchg-irq.h @@ -0,0 +1,40 @@ +#ifndef __ASM_SH_CMPXCHG_IRQ_H +#define __ASM_SH_CMPXCHG_IRQ_H + +static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val) +{ + unsigned long flags, retval; + + local_irq_save(flags); + retval = *m; + *m = val; + local_irq_restore(flags); + return retval; +} + +static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val) +{ + unsigned long flags, retval; + + local_irq_save(flags); + retval = *m; + *m = val & 0xff; + local_irq_restore(flags); + return retval; +} + +static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old, + unsigned long new) +{ + __u32 retval; + unsigned long flags; + + local_irq_save(flags); + retval = *m; + if (retval == old) + *m = new; + local_irq_restore(flags); /* implies memory barrier */ + return retval; +} + +#endif /* __ASM_SH_CMPXCHG_IRQ_H */ diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h index ad3d2a636130..969f3d4afe2a 100644 --- a/include/asm-sh/system.h +++ b/include/asm-sh/system.h @@ -68,27 +68,11 @@ #define set_mb(var, value) do { (void)xchg(&var, value); } while (0) -static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val) -{ - unsigned long flags, retval; - - local_irq_save(flags); - retval = *m; - *m = val; - local_irq_restore(flags); - return retval; -} - -static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val) -{ - unsigned long flags, retval; - - local_irq_save(flags); - retval = *m; - *m = val & 0xff; - local_irq_restore(flags); - return retval; -} +#ifdef CONFIG_GUSA_RB +#include +#else +#include +#endif extern void __xchg_called_with_bad_pointer(void); @@ -115,20 +99,6 @@ extern void __xchg_called_with_bad_pointer(void); #define xchg(ptr,x) \ ((__typeof__(*(ptr)))__xchg((ptr),(unsigned long)(x), sizeof(*(ptr)))) -static inline unsigned long __cmpxchg_u32(volatile int * m, unsigned long old, - unsigned long new) -{ - __u32 retval; - unsigned long flags; - - local_irq_save(flags); - retval = *m; - if (retval == old) - *m = new; - local_irq_restore(flags); /* implies memory barrier */ - return retval; -} - /* This function doesn't exist, so you'll get a linker error * if something tries to do an invalid cmpxchg(). */ extern void __cmpxchg_called_with_bad_pointer(void); -- cgit v1.2.3-59-g8ed1b