diff options
Diffstat (limited to 'arch/frv/kernel')
-rw-r--r-- | arch/frv/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/frv/kernel/entry-table.S | 8 | ||||
-rw-r--r-- | arch/frv/kernel/entry.S | 20 | ||||
-rw-r--r-- | arch/frv/kernel/frv_ksyms.c | 1 | ||||
-rw-r--r-- | arch/frv/kernel/head.inc | 2 | ||||
-rw-r--r-- | arch/frv/kernel/pm.c | 8 | ||||
-rw-r--r-- | arch/frv/kernel/semaphore.c | 155 | ||||
-rw-r--r-- | arch/frv/kernel/switch_to.S | 7 | ||||
-rw-r--r-- | arch/frv/kernel/traps.c | 240 |
9 files changed, 263 insertions, 180 deletions
diff --git a/arch/frv/kernel/Makefile b/arch/frv/kernel/Makefile index e8f73ed28b52..c36f70b6699a 100644 --- a/arch/frv/kernel/Makefile +++ b/arch/frv/kernel/Makefile @@ -9,7 +9,7 @@ extra-y:= head.o init_task.o vmlinux.lds obj-y := $(heads-y) entry.o entry-table.o break.o switch_to.o kernel_thread.o \ kernel_execve.o process.o traps.o ptrace.o signal.o dma.o \ - sys_frv.o time.o semaphore.o setup.o frv_ksyms.o \ + sys_frv.o time.o setup.o frv_ksyms.o \ debug-stub.o irq.o sleep.o uaccess.o obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-io.o diff --git a/arch/frv/kernel/entry-table.S b/arch/frv/kernel/entry-table.S index d3b9253d862a..bf35f33e48c9 100644 --- a/arch/frv/kernel/entry-table.S +++ b/arch/frv/kernel/entry-table.S @@ -316,8 +316,14 @@ __trap_fixup_kernel_data_tlb_miss: .section .trap.vector .org TBR_TT_TRAP0 >> 2 .long system_call - .rept 126 + .rept 119 .long __entry_unsupported_trap .endr + + # userspace atomic op emulation, traps 120-126 + .rept 7 + .long __entry_atomic_op + .endr + .org TBR_TT_BREAK >> 2 .long __entry_debug_exception diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S index f36d7f4a7c25..b8a4b94779b1 100644 --- a/arch/frv/kernel/entry.S +++ b/arch/frv/kernel/entry.S @@ -656,6 +656,26 @@ __entry_debug_exception: ############################################################################### # +# handle atomic operation emulation for userspace +# +############################################################################### + .globl __entry_atomic_op +__entry_atomic_op: + LEDS 0x6012 + sethi.p %hi(atomic_operation),gr5 + setlo %lo(atomic_operation),gr5 + movsg esfr1,gr8 + movsg epcr0,gr9 + movsg esr0,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call atomic_operation(esfr1,epcr0,esr0) + +############################################################################### +# # handle media exception # ############################################################################### diff --git a/arch/frv/kernel/frv_ksyms.c b/arch/frv/kernel/frv_ksyms.c index f772704b3d28..0316b3c50eff 100644 --- a/arch/frv/kernel/frv_ksyms.c +++ b/arch/frv/kernel/frv_ksyms.c @@ -12,7 +12,6 @@ #include <asm/pgalloc.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/semaphore.h> #include <asm/checksum.h> #include <asm/hardirq.h> #include <asm/cacheflush.h> diff --git a/arch/frv/kernel/head.inc b/arch/frv/kernel/head.inc index d424cd2eb213..bff66628b99a 100644 --- a/arch/frv/kernel/head.inc +++ b/arch/frv/kernel/head.inc @@ -46,5 +46,5 @@ #ifdef CONFIG_MMU __sdram_base = 0x00000000 /* base address to which SDRAM relocated */ #else -__sdram_base = 0xc0000000 /* base address to which SDRAM relocated */ +__sdram_base = __page_offset /* base address to which SDRAM relocated */ #endif diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c index c57ce3f1f2e2..73f3aeefd203 100644 --- a/arch/frv/kernel/pm.c +++ b/arch/frv/kernel/pm.c @@ -163,14 +163,11 @@ static int sysctl_pm_do_suspend(ctl_table *ctl, int write, struct file *filp, if ((mode != 1) && (mode != 5)) return -EINVAL; - retval = pm_send_all(PM_SUSPEND, (void *)3); - if (retval == 0) { if (mode == 5) retval = pm_do_bus_sleep(); else retval = pm_do_suspend(); - pm_send_all(PM_RESUME, (void *)0); } return retval; @@ -183,9 +180,6 @@ static int try_set_cmode(int new_cmode) if (!(clock_cmodes_permitted & (1<<new_cmode))) return -EINVAL; - /* tell all the drivers we're suspending */ - pm_send_all(PM_SUSPEND, (void *)3); - /* now change cmode */ local_irq_disable(); frv_dma_pause_all(); @@ -201,8 +195,6 @@ static int try_set_cmode(int new_cmode) frv_dma_resume_all(); local_irq_enable(); - /* tell all the drivers we're resuming */ - pm_send_all(PM_RESUME, (void *)0); return 0; } diff --git a/arch/frv/kernel/semaphore.c b/arch/frv/kernel/semaphore.c deleted file mode 100644 index 7ee3a147b471..000000000000 --- a/arch/frv/kernel/semaphore.c +++ /dev/null @@ -1,155 +0,0 @@ -/* semaphore.c: FR-V semaphores - * - * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - Derived from lib/rwsem-spinlock.c - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <linux/sched.h> -#include <linux/module.h> -#include <asm/semaphore.h> - -struct sem_waiter { - struct list_head list; - struct task_struct *task; -}; - -#ifdef CONFIG_DEBUG_SEMAPHORE -void semtrace(struct semaphore *sem, const char *str) -{ - if (sem->debug) - printk("[%d] %s({%d,%d})\n", - current->pid, - str, - sem->counter, - list_empty(&sem->wait_list) ? 0 : 1); -} -#else -#define semtrace(SEM,STR) do { } while(0) -#endif - -/* - * wait for a token to be granted from a semaphore - * - entered with lock held and interrupts disabled - */ -void __down(struct semaphore *sem, unsigned long flags) -{ - struct task_struct *tsk = current; - struct sem_waiter waiter; - - semtrace(sem, "Entering __down"); - - /* set up my own style of waitqueue */ - waiter.task = tsk; - get_task_struct(tsk); - - list_add_tail(&waiter.list, &sem->wait_list); - - /* we don't need to touch the semaphore struct anymore */ - spin_unlock_irqrestore(&sem->wait_lock, flags); - - /* wait to be given the semaphore */ - set_task_state(tsk, TASK_UNINTERRUPTIBLE); - - for (;;) { - if (list_empty(&waiter.list)) - break; - schedule(); - set_task_state(tsk, TASK_UNINTERRUPTIBLE); - } - - tsk->state = TASK_RUNNING; - semtrace(sem, "Leaving __down"); -} - -EXPORT_SYMBOL(__down); - -/* - * interruptibly wait for a token to be granted from a semaphore - * - entered with lock held and interrupts disabled - */ -int __down_interruptible(struct semaphore *sem, unsigned long flags) -{ - struct task_struct *tsk = current; - struct sem_waiter waiter; - int ret; - - semtrace(sem,"Entering __down_interruptible"); - - /* set up my own style of waitqueue */ - waiter.task = tsk; - get_task_struct(tsk); - - list_add_tail(&waiter.list, &sem->wait_list); - - /* we don't need to touch the semaphore struct anymore */ - set_task_state(tsk, TASK_INTERRUPTIBLE); - - spin_unlock_irqrestore(&sem->wait_lock, flags); - - /* wait to be given the semaphore */ - ret = 0; - for (;;) { - if (list_empty(&waiter.list)) - break; - if (unlikely(signal_pending(current))) - goto interrupted; - schedule(); - set_task_state(tsk, TASK_INTERRUPTIBLE); - } - - out: - tsk->state = TASK_RUNNING; - semtrace(sem, "Leaving __down_interruptible"); - return ret; - - interrupted: - spin_lock_irqsave(&sem->wait_lock, flags); - - if (!list_empty(&waiter.list)) { - list_del(&waiter.list); - ret = -EINTR; - } - - spin_unlock_irqrestore(&sem->wait_lock, flags); - if (ret == -EINTR) - put_task_struct(current); - goto out; -} - -EXPORT_SYMBOL(__down_interruptible); - -/* - * release a single token back to a semaphore - * - entered with lock held and interrupts disabled - */ -void __up(struct semaphore *sem) -{ - struct task_struct *tsk; - struct sem_waiter *waiter; - - semtrace(sem,"Entering __up"); - - /* grant the token to the process at the front of the queue */ - waiter = list_entry(sem->wait_list.next, struct sem_waiter, list); - - /* We must be careful not to touch 'waiter' after we set ->task = NULL. - * It is allocated on the waiter's stack and may become invalid at - * any time after that point (due to a wakeup from another source). - */ - list_del_init(&waiter->list); - tsk = waiter->task; - mb(); - waiter->task = NULL; - wake_up_process(tsk); - put_task_struct(tsk); - - semtrace(sem,"Leaving __up"); -} - -EXPORT_SYMBOL(__up); diff --git a/arch/frv/kernel/switch_to.S b/arch/frv/kernel/switch_to.S index b5275fa9cd0d..b06668670fcc 100644 --- a/arch/frv/kernel/switch_to.S +++ b/arch/frv/kernel/switch_to.S @@ -102,13 +102,6 @@ __switch_to: movgs gr14,lr bar - srli gr15,#28,gr5 - subicc gr5,#0xc,gr0,icc0 - beq icc0,#0,111f - break - nop -111: - # jump to __switch_back or ret_from_fork as appropriate # - move prev to GR8 movgs gr4,psr diff --git a/arch/frv/kernel/traps.c b/arch/frv/kernel/traps.c index 2e6098c85578..a40df80b2ebd 100644 --- a/arch/frv/kernel/traps.c +++ b/arch/frv/kernel/traps.c @@ -49,7 +49,7 @@ asmlinkage void insn_access_error(unsigned long esfr1, unsigned long epcr0, unsi info.si_signo = SIGSEGV; info.si_code = SEGV_ACCERR; info.si_errno = 0; - info.si_addr = (void *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); + info.si_addr = (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); force_sig_info(info.si_signo, &info, current); } /* end insn_access_error() */ @@ -73,7 +73,7 @@ asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, un epcr0, esr0, esfr1); info.si_errno = 0; - info.si_addr = (void *) ((epcr0 & EPCR0_PC) ? (epcr0 & EPCR0_PC) : __frame->pc); + info.si_addr = (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); switch (__frame->tbr & TBR_TT) { case TBR_TT_ILLEGAL_INSTR: @@ -102,6 +102,234 @@ asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, un /*****************************************************************************/ /* + * handle atomic operations with errors + * - arguments in gr8, gr9, gr10 + * - original memory value placed in gr5 + * - replacement memory value placed in gr9 + */ +asmlinkage void atomic_operation(unsigned long esfr1, unsigned long epcr0, + unsigned long esr0) +{ + static DEFINE_SPINLOCK(atomic_op_lock); + unsigned long x, y, z; + unsigned long __user *p; + mm_segment_t oldfs; + siginfo_t info; + int ret; + + y = 0; + z = 0; + + oldfs = get_fs(); + if (!user_mode(__frame)) + set_fs(KERNEL_DS); + + switch (__frame->tbr & TBR_TT) { + /* TIRA gr0,#120 + * u32 __atomic_user_cmpxchg32(u32 *ptr, u32 test, u32 new) + */ + case TBR_TT_ATOMIC_CMPXCHG32: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + y = __frame->gr10; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + if (z != x) + goto done; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + if (z != x) + goto done2; + + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#121 + * u32 __atomic_kernel_xchg32(void *v, u32 new) + */ + case TBR_TT_ATOMIC_XCHG32: + p = (unsigned long __user *) __frame->gr8; + y = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#122 + * ulong __atomic_kernel_XOR_return(ulong i, ulong *v) + */ + case TBR_TT_ATOMIC_XOR: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = x ^ z; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#123 + * ulong __atomic_kernel_OR_return(ulong i, ulong *v) + */ + case TBR_TT_ATOMIC_OR: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = x ^ z; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#124 + * ulong __atomic_kernel_AND_return(ulong i, ulong *v) + */ + case TBR_TT_ATOMIC_AND: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = x & z; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#125 + * int __atomic_user_sub_return(atomic_t *v, int i) + */ + case TBR_TT_ATOMIC_SUB: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = z - x; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#126 + * int __atomic_user_add_return(atomic_t *v, int i) + */ + case TBR_TT_ATOMIC_ADD: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = z + x; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + default: + BUG(); + } + +done2: + spin_unlock_irq(&atomic_op_lock); +done: + if (!user_mode(__frame)) + set_fs(oldfs); + __frame->gr5 = z; + __frame->gr9 = y; + return; + +error2: + spin_unlock_irq(&atomic_op_lock); +error: + if (!user_mode(__frame)) + set_fs(oldfs); + __frame->pc -= 4; + + die_if_kernel("-- Atomic Op Error --\n"); + + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; + info.si_errno = 0; + info.si_addr = (void __user *) __frame->pc; + + force_sig_info(info.si_signo, &info, current); +} + +/*****************************************************************************/ +/* * */ asmlinkage void media_exception(unsigned long msr0, unsigned long msr1) @@ -116,7 +344,7 @@ asmlinkage void media_exception(unsigned long msr0, unsigned long msr1) info.si_signo = SIGFPE; info.si_code = FPE_MDAOVF; info.si_errno = 0; - info.si_addr = (void *) __frame->pc; + info.si_addr = (void __user *) __frame->pc; force_sig_info(info.si_signo, &info, current); } /* end media_exception() */ @@ -156,7 +384,7 @@ asmlinkage void memory_access_exception(unsigned long esr0, info.si_addr = NULL; if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV)) - info.si_addr = (void *) ear0; + info.si_addr = (void __user *) ear0; force_sig_info(info.si_signo, &info, current); @@ -185,7 +413,7 @@ asmlinkage void data_access_error(unsigned long esfr1, unsigned long esr15, unsi info.si_signo = SIGSEGV; info.si_code = SEGV_ACCERR; info.si_errno = 0; - info.si_addr = (void *) + info.si_addr = (void __user *) (((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0); force_sig_info(info.si_signo, &info, current); @@ -219,7 +447,7 @@ asmlinkage void division_exception(unsigned long esfr1, unsigned long esr0, unsi info.si_signo = SIGFPE; info.si_code = FPE_INTDIV; info.si_errno = 0; - info.si_addr = (void *) __frame->pc; + info.si_addr = (void __user *) __frame->pc; force_sig_info(info.si_signo, &info, current); } /* end division_exception() */ |