Skip to content

Commit

Permalink
MIPS: Rewrite sysmips(MIPS_ATOMIC_SET, ...) in C with inline assembler
Browse files Browse the repository at this point in the history
This way it doesn't have to use CONFIG_CPU_HAS_LLSC anymore.

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
  • Loading branch information
Ralf Baechle committed Sep 17, 2009
1 parent f4c6b6b commit f1e39a4
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 150 deletions.
3 changes: 3 additions & 0 deletions arch/mips/include/asm/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ extern asmlinkage void *resume(void *last, void *next, void *next_ti);

struct task_struct;

extern unsigned int ll_bit;
extern struct task_struct *ll_task;

#ifdef CONFIG_MIPS_MT_FPAFF

/*
Expand Down
72 changes: 0 additions & 72 deletions arch/mips/kernel/scall32-o32.S
Original file line number Diff line number Diff line change
Expand Up @@ -187,78 +187,6 @@ illegal_syscall:
j o32_syscall_exit
END(handle_sys)

LEAF(mips_atomic_set)
andi v0, a1, 3 # must be word aligned
bnez v0, bad_alignment

lw v1, TI_ADDR_LIMIT($28) # in legal address range?
addiu a0, a1, 4
or a0, a0, a1
and a0, a0, v1
bltz a0, bad_address

#ifdef CONFIG_CPU_HAS_LLSC
/* Ok, this is the ll/sc case. World is sane :-) */
1: ll v0, (a1)
move a0, a2
2: sc a0, (a1)
#if R10000_LLSC_WAR
beqzl a0, 1b
#else
beqz a0, 1b
#endif

.section __ex_table,"a"
PTR 1b, bad_stack
PTR 2b, bad_stack
.previous
#else
sw a1, 16(sp)
sw a2, 20(sp)

move a0, sp
move a2, a1
li a1, 1
jal do_page_fault

lw a1, 16(sp)
lw a2, 20(sp)

/*
* At this point the page should be readable and writable unless
* there was no more memory available.
*/
1: lw v0, (a1)
2: sw a2, (a1)

.section __ex_table,"a"
PTR 1b, no_mem
PTR 2b, no_mem
.previous
#endif

sw zero, PT_R7(sp) # success
sw v0, PT_R2(sp) # result

j o32_syscall_exit # continue like a normal syscall

no_mem: li v0, -ENOMEM
jr ra

bad_address:
li v0, -EFAULT
jr ra

bad_alignment:
li v0, -EINVAL
jr ra
END(mips_atomic_set)

LEAF(sys_sysmips)
beq a0, MIPS_ATOMIC_SET, mips_atomic_set
j _sys_sysmips
END(sys_sysmips)

LEAF(sys_syscall)
subu t0, a0, __NR_O32_Linux # check syscall number
sltiu v0, t0, __NR_O32_Linux_syscalls + 1
Expand Down
72 changes: 0 additions & 72 deletions arch/mips/kernel/scall64-64.S
Original file line number Diff line number Diff line change
Expand Up @@ -124,78 +124,6 @@ illegal_syscall:
j n64_syscall_exit
END(handle_sys64)

LEAF(mips_atomic_set)
andi v0, a1, 3 # must be word aligned
bnez v0, bad_alignment

LONG_L v1, TI_ADDR_LIMIT($28) # in legal address range?
LONG_ADDIU a0, a1, 4
or a0, a0, a1
and a0, a0, v1
bltz a0, bad_address

#ifdef CONFIG_CPU_HAS_LLSC
/* Ok, this is the ll/sc case. World is sane :-) */
1: ll v0, (a1)
move a0, a2
2: sc a0, (a1)
#if R10000_LLSC_WAR
beqzl a0, 1b
#else
beqz a0, 1b
#endif

.section __ex_table,"a"
PTR 1b, bad_stack
PTR 2b, bad_stack
.previous
#else
sw a1, 16(sp)
sw a2, 20(sp)

move a0, sp
move a2, a1
li a1, 1
jal do_page_fault

lw a1, 16(sp)
lw a2, 20(sp)

/*
* At this point the page should be readable and writable unless
* there was no more memory available.
*/
1: lw v0, (a1)
2: sw a2, (a1)

.section __ex_table,"a"
PTR 1b, no_mem
PTR 2b, no_mem
.previous
#endif

sd zero, PT_R7(sp) # success
sd v0, PT_R2(sp) # result

j n64_syscall_exit # continue like a normal syscall

no_mem: li v0, -ENOMEM
jr ra

bad_address:
li v0, -EFAULT
jr ra

bad_alignment:
li v0, -EINVAL
jr ra
END(mips_atomic_set)

LEAF(sys_sysmips)
beq a0, MIPS_ATOMIC_SET, mips_atomic_set
j _sys_sysmips
END(sys_sysmips)

.align 3
sys_call_table:
PTR sys_read /* 5000 */
Expand Down
112 changes: 109 additions & 3 deletions arch/mips/kernel/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/ipc.h>
#include <linux/uaccess.h>

#include <asm/asm.h>
#include <asm/branch.h>
#include <asm/cachectl.h>
#include <asm/cacheflush.h>
Expand Down Expand Up @@ -290,12 +292,116 @@ SYSCALL_DEFINE1(set_thread_area, unsigned long, addr)
return 0;
}

asmlinkage int _sys_sysmips(long cmd, long arg1, long arg2, long arg3)
static inline int mips_atomic_set(struct pt_regs *regs,
unsigned long addr, unsigned long new)
{
unsigned long old, tmp;
unsigned int err;

if (unlikely(addr & 3))
return -EINVAL;

if (unlikely(!access_ok(VERIFY_WRITE, addr, 4)))
return -EINVAL;

if (cpu_has_llsc && R10000_LLSC_WAR) {
__asm__ __volatile__ (
" li %[err], 0 \n"
"1: ll %[old], (%[addr]) \n"
" move %[tmp], %[new] \n"
"2: sc %[tmp], (%[addr]) \n"
" beqzl %[tmp], 1b \n"
"3: \n"
" .section .fixup,\"ax\" \n"
"4: li %[err], %[efault] \n"
" j 3b \n"
" .previous \n"
" .section __ex_table,\"a\" \n"
" "STR(PTR)" 1b, 4b \n"
" "STR(PTR)" 2b, 4b \n"
" .previous \n"
: [old] "=&r" (old),
[err] "=&r" (err),
[tmp] "=&r" (tmp)
: [addr] "r" (addr),
[new] "r" (new),
[efault] "i" (-EFAULT)
: "memory");
} else if (cpu_has_llsc) {
__asm__ __volatile__ (
" li %[err], 0 \n"
"1: ll %[old], (%[addr]) \n"
" move %[tmp], %[new] \n"
"2: sc %[tmp], (%[addr]) \n"
" bnez %[tmp], 4f \n"
"3: \n"
" .subsection 2 \n"
"4: b 1b \n"
" .previous \n"
" \n"
" .section .fixup,\"ax\" \n"
"5: li %[err], %[efault] \n"
" j 3b \n"
" .previous \n"
" .section __ex_table,\"a\" \n"
" "STR(PTR)" 1b, 5b \n"
" "STR(PTR)" 2b, 5b \n"
" .previous \n"
: [old] "=&r" (old),
[err] "=&r" (err),
[tmp] "=&r" (tmp)
: [addr] "r" (addr),
[new] "r" (new),
[efault] "i" (-EFAULT)
: "memory");
} else {
do {
preempt_disable();
ll_bit = 1;
ll_task = current;
preempt_enable();

err = __get_user(old, (unsigned int *) addr);
err |= __put_user(new, (unsigned int *) addr);
if (err)
break;
rmb();
} while (!ll_bit);
}

if (unlikely(err))
return err;

regs->regs[2] = old;
regs->regs[7] = 0; /* No error */

/*
* Don't let your children do this ...
*/
__asm__ __volatile__(
" move $29, %0 \n"
" j syscall_exit \n"
: /* no outputs */
: "r" (regs));

/* unreached. Honestly. */
while (1);
}

save_static_function(sys_sysmips);
static int __used noinline
_sys_sysmips(nabi_no_regargs struct pt_regs regs)
{
long cmd, arg1, arg2, arg3;

cmd = regs.regs[4];
arg1 = regs.regs[5];
arg2 = regs.regs[6];
arg3 = regs.regs[7];

switch (cmd) {
case MIPS_ATOMIC_SET:
printk(KERN_CRIT "How did I get here?\n");
return -EINVAL;
return mips_atomic_set(&regs, arg1, arg2);

case MIPS_FIXADE:
if (arg1 & ~3)
Expand Down
5 changes: 2 additions & 3 deletions arch/mips/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,8 @@ asmlinkage void do_be(struct pt_regs *regs)
* The ll_bit is cleared by r*_switch.S
*/

unsigned long ll_bit;

static struct task_struct *ll_task = NULL;
unsigned int ll_bit;
struct task_struct *ll_task;

static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode)
{
Expand Down

0 comments on commit f1e39a4

Please sign in to comment.