MIPS: Rewrite sysmips(MIPS_ATOMIC_SET, ...) in C with inline assembler
This way it doesn't have to use CONFIG_CPU_HAS_LLSC anymore. Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
f4c6b6bc5a
commit
f1e39a4a61
|
@ -32,6 +32,9 @@ extern asmlinkage void *resume(void *last, void *next, void *next_ti);
|
||||||
|
|
||||||
struct task_struct;
|
struct task_struct;
|
||||||
|
|
||||||
|
extern unsigned int ll_bit;
|
||||||
|
extern struct task_struct *ll_task;
|
||||||
|
|
||||||
#ifdef CONFIG_MIPS_MT_FPAFF
|
#ifdef CONFIG_MIPS_MT_FPAFF
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -187,78 +187,6 @@ illegal_syscall:
|
||||||
j o32_syscall_exit
|
j o32_syscall_exit
|
||||||
END(handle_sys)
|
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)
|
LEAF(sys_syscall)
|
||||||
subu t0, a0, __NR_O32_Linux # check syscall number
|
subu t0, a0, __NR_O32_Linux # check syscall number
|
||||||
sltiu v0, t0, __NR_O32_Linux_syscalls + 1
|
sltiu v0, t0, __NR_O32_Linux_syscalls + 1
|
||||||
|
|
|
@ -124,78 +124,6 @@ illegal_syscall:
|
||||||
j n64_syscall_exit
|
j n64_syscall_exit
|
||||||
END(handle_sys64)
|
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
|
.align 3
|
||||||
sys_call_table:
|
sys_call_table:
|
||||||
PTR sys_read /* 5000 */
|
PTR sys_read /* 5000 */
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/ipc.h>
|
#include <linux/ipc.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
|
#include <asm/asm.h>
|
||||||
#include <asm/branch.h>
|
#include <asm/branch.h>
|
||||||
#include <asm/cachectl.h>
|
#include <asm/cachectl.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
|
@ -290,12 +292,116 @@ SYSCALL_DEFINE1(set_thread_area, unsigned long, addr)
|
||||||
return 0;
|
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) {
|
switch (cmd) {
|
||||||
case MIPS_ATOMIC_SET:
|
case MIPS_ATOMIC_SET:
|
||||||
printk(KERN_CRIT "How did I get here?\n");
|
return mips_atomic_set(®s, arg1, arg2);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
case MIPS_FIXADE:
|
case MIPS_FIXADE:
|
||||||
if (arg1 & ~3)
|
if (arg1 & ~3)
|
||||||
|
|
|
@ -466,9 +466,8 @@ asmlinkage void do_be(struct pt_regs *regs)
|
||||||
* The ll_bit is cleared by r*_switch.S
|
* The ll_bit is cleared by r*_switch.S
|
||||||
*/
|
*/
|
||||||
|
|
||||||
unsigned long ll_bit;
|
unsigned int ll_bit;
|
||||||
|
struct task_struct *ll_task;
|
||||||
static struct task_struct *ll_task = NULL;
|
|
||||||
|
|
||||||
static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode)
|
static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue