Hi,
Following is a sys__test_and_set() syscall implementation that should
suite well the _test_and_set() library call as defined by the MIPS ABI. I
didn't have enough time to implement a glibc wrapper, yet, but I have
written a test program that is much similar to how the wrapper will look
like.
The syscall uses a slightly modified calling convention which allows it
to avoid errno mangling code and always return consistent results. The
call never returns an error -- there is no reason to. If an illegal
access is detected, a SIGBUS or a SIGSEGV is sent appropriately, depending
on the kind of the violation. This mimics the ll/sc behaviour in these
circumstances and makes the kernel vs library implementation more
consistent.
Here is the test program:
#include <stdio.h>
#include <sys/syscall.h>
#ifndef __NR__test_and_set
#define __NR__test_and_set (__NR_Linux + 221)
#endif
static inline int _test_and_set(int *p, int v)
{
int r;
asm volatile(
"la $4,%1\n\t"
"move $5,%2\n\t"
"li $2,%3\n\t"
"syscall\n\t"
"move %0,$3"
: "=r" (r)
: "m" (*p), "r" (v), "i" (__NR__test_and_set)
: "$2", "$3", "$4", "$5",
"$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24",
"memory");
return r;
}
int main(void)
{
volatile int v = 1;
int v0, v1, r;
v0 = v;
r = _test_and_set((int *)&v, -1);
v1 = v;
printf("v0: %i, v1: %i, r: %i\n", v0, v1, r);
return 0;
}
Following it the patch. It was built and tested using a linux
2.4.0-test12 CVS snapshot from oss. It applies to a current snapshot of
2.4.3, but it wasn't run-time tested in this configuration. Given it's
self-contained, I hope it works for 2.4.3 as well.
Maciej
--
+ Maciej W. Rozycki, Technical University of Gdansk, Poland +
+--------------------------------------------------------------+
+ e-mail: macro@ds2.pg.gda.pl, PGP key available +
patch-mips-2.4.0-test12-20010110-tas-11
diff -up --recursive --new-file
linux-mips-2.4.0-test12-20010110.macro/arch/mips/kernel/syscall.c
linux-mips-2.4.0-test12-20010110/arch/mips/kernel/syscall.c
--- linux-mips-2.4.0-test12-20010110.macro/arch/mips/kernel/syscall.c Sun Oct
29 05:26:54 2000
+++ linux-mips-2.4.0-test12-20010110/arch/mips/kernel/syscall.c Sun May 27
22:56:21 2001
@@ -174,6 +174,82 @@ asmlinkage int sys_olduname(struct oldol
return error;
}
+/* Note: errno is always zero and the result is in v1. */
+asmlinkage int sys__test_and_set(struct pt_regs regs)
+{
+ int *ptr, val, ret, err, tmp;
+
+ ptr = (int *)(regs.regs[4]);
+ val = (int)(regs.regs[5]);
+
+ /* Don't emulate unaligned accesses. */
+ if ((int)ptr & 3)
+ goto fault;
+
+#ifdef CONFIG_CPU_HAS_LLSC
+ __asm__(".set mips2\n\t"
+ "1:\n\t"
+ "ll %0,%5\n\t"
+ "move %3,%4\n"
+ "2:\n\t"
+ "sc %3,%1\n\t"
+ "beqz %3,1b\n\t"
+ "3:\n\t"
+ ".set mips0\n\t"
+ ".section .fixup,\"ax\"\n"
+ "4:\n\t"
+ "li %2,%7\n\t"
+ "j 3b\n\t"
+ ".previous\n\t"
+ ".section __ex_table,\"a\"\n\t"
+ ".word 1b,4b\n\t"
+ ".word 2b,4b\n\t"
+ ".previous"
+ : "=&r" (ret), "=R" (*ptr), "=r" (err), "=&r" (tmp)
+ : "r" (val), "1" (*ptr), "2" (0), "i" (-EFAULT));
+#else
+ /* A zero here saves us three instructions. */
+ err = verify_area(VERIFY_WRITE, ptr, 0);
+
+ save_and_cli(tmp);
+ err |= __get_user(ret, ptr);
+ err |= __put_user(val, ptr); /* No fault unless unwriteable. */
+ restore_flags(tmp);
+#endif
+
+ if (err)
+ goto fault;
+
+ (int)(regs.regs[3]) = ret;
+
+ return 0;
+
+fault:
+ regs.cp0_epc -= 4; /* Go back to SYSCALL. */
+
+ {
+ struct siginfo info;
+
+
+ if ((int)ptr & 3) {
+ info.si_signo = SIGBUS;
+ info.si_code = BUS_ADRALN;
+ } else {
+ info.si_signo = SIGSEGV;
+ if (verify_area(VERIFY_WRITE, ptr, 0))
+ info.si_code = SEGV_ACCERR;
+ else
+ info.si_code = SEGV_MAPERR;
+ }
+ info.si_errno = 0;
+ info.si_addr = (void *)regs.cp0_epc;
+
+ force_sig_info(info.si_signo, &info, current);
+ }
+
+ return 0;
+}
+
/*
* Do the indirect syscall syscall.
* Don't care about kernel locking; the actual syscall will do it.
diff -up --recursive --new-file
linux-mips-2.4.0-test12-20010110.macro/arch/mips/kernel/syscalls.h
linux-mips-2.4.0-test12-20010110/arch/mips/kernel/syscalls.h
--- linux-mips-2.4.0-test12-20010110.macro/arch/mips/kernel/syscalls.h Wed Nov
8 05:26:57 2000
+++ linux-mips-2.4.0-test12-20010110/arch/mips/kernel/syscalls.h Wed May
23 23:59:02 2001
@@ -235,3 +235,4 @@ SYS(sys_mincore, 3)
SYS(sys_madvise, 3)
SYS(sys_getdents64, 3)
SYS(sys_fcntl64, 3) /* 4220 */
+SYS(sys__test_and_set, 0)
diff -up --recursive --new-file
linux-mips-2.4.0-test12-20010110.macro/include/asm-mips/unistd.h
linux-mips-2.4.0-test12-20010110/include/asm-mips/unistd.h
--- linux-mips-2.4.0-test12-20010110.macro/include/asm-mips/unistd.h Thu Oct
26 04:27:09 2000
+++ linux-mips-2.4.0-test12-20010110/include/asm-mips/unistd.h Wed May 23
23:09:00 2001
@@ -233,11 +233,12 @@
#define __NR_madvise (__NR_Linux + 218)
#define __NR_getdents64 (__NR_Linux + 219)
#define __NR_fcntl64 (__NR_Linux + 220)
+#define __NR__test_and_set (__NR_Linux + 221)
/*
* Offset of the last Linux flavoured syscall
*/
-#define __NR_Linux_syscalls 220
+#define __NR_Linux_syscalls 221
#ifndef _LANGUAGE_ASSEMBLY
|