diff --git a/plat/linux/libsys/sigprocmask.c b/plat/linux/libsys/sigprocmask.c new file mode 100644 index 000000000..ad1b339c2 --- /dev/null +++ b/plat/linux/libsys/sigprocmask.c @@ -0,0 +1,7 @@ +#include +#include "libsys.h" + +int sigprocmask(int flags, const sigset_t *new, sigset_t *old) +{ + return _syscall(__NR_sigprocmask, flags, (quad) new, (quad) old); +} diff --git a/plat/linux386/include/unistd.h b/plat/linux386/include/unistd.h index 5c6f31ef4..8c8637c09 100644 --- a/plat/linux386/include/unistd.h +++ b/plat/linux386/include/unistd.h @@ -56,7 +56,6 @@ extern int write(int fd, void* buffer, size_t count); extern off_t lseek(int fildes, off_t offset, int whence); extern int fcntl(int fd, int op, ...); extern int unlink(const char* path); -extern int remove(const char* path); /* Special variables */ @@ -117,8 +116,16 @@ typedef int sig_atomic_t; #define _NSIG 32 /* Biggest signal number + 1 (not including real-time signals). */ + +/* sigprocmask */ +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 +typedef unsigned long sigset_t; + typedef void (*sighandler_t)(int); extern sighandler_t signal(int signum, sighandler_t handler); +extern int sigprocmask(int, const sigset_t *, sigset_t *); extern int raise(int signum); diff --git a/plat/linux68k/include/unistd.h b/plat/linux68k/include/unistd.h index 307192f77..927a20459 100644 --- a/plat/linux68k/include/unistd.h +++ b/plat/linux68k/include/unistd.h @@ -55,6 +55,7 @@ extern int read(int fd, void* buffer, size_t count); extern int write(int fd, void* buffer, size_t count); extern off_t lseek(int fildes, off_t offset, int whence); extern int fcntl(int fd, int op, ...); +extern int unlink(const char* path); /* Special variables */ @@ -67,6 +68,7 @@ extern pid_t getpid(void); extern int brk(void* ptr); extern void* sbrk(int increment); extern int isatty(int d); +extern int execve(const char *path, char *const argv[], char *const envp[]); /* Signal handling */ @@ -114,8 +116,16 @@ typedef int sig_atomic_t; #define _NSIG 32 /* Biggest signal number + 1 (not including real-time signals). */ + +/* sigprocmask */ +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 +typedef unsigned long sigset_t; + typedef void (*sighandler_t)(int); extern sighandler_t signal(int signum, sighandler_t handler); +extern int sigprocmask(int, const sigset_t *, sigset_t *); extern int raise(int signum); diff --git a/plat/linuxppc/include/unistd.h b/plat/linuxppc/include/unistd.h index a31bd9f0d..f57705365 100644 --- a/plat/linuxppc/include/unistd.h +++ b/plat/linuxppc/include/unistd.h @@ -55,6 +55,7 @@ extern int read(int fd, void* buffer, size_t count); extern int write(int fd, void* buffer, size_t count); extern off_t lseek(int fildes, off_t offset, int whence); extern int fcntl(int fd, int op, ...); +extern int unlink(const char* path); /* Special variables */ @@ -115,8 +116,34 @@ typedef int sig_atomic_t; #define _NSIG 32 /* Biggest signal number + 1 (not including real-time signals). */ + +/* sigprocmask */ +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 +typedef unsigned long sigset_t; + +/* sa_flags */ +#define SA_NODEFER 0x40000000UL +#define SA_RESETHAND 0x80000000UL + +struct __siginfo; +struct sigaction { + union { + void (*__sa_handler)(int); + void (*__sa_sigaction)(int, struct __siginfo *, void *); + } __sigaction_u; + sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; +#define sa_handler __sigaction_u.__sa_handler +#define sa_sigaction __sigaction_u.__sa_sigaction + typedef void (*sighandler_t)(int); +extern int sigaction(int, const struct sigaction *, struct sigaction *); extern sighandler_t signal(int signum, sighandler_t handler); +extern int sigprocmask(int, const sigset_t *, sigset_t *); extern int raise(int signum); diff --git a/plat/linuxppc/libsys/build.lua b/plat/linuxppc/libsys/build.lua index e74f3f416..f7b16b378 100644 --- a/plat/linuxppc/libsys/build.lua +++ b/plat/linuxppc/libsys/build.lua @@ -1,16 +1,36 @@ acklibrary { - name = "lib", - srcs = { - "./*.s", - "plat/linux/libsys/*.c", - "plat/linux/libsys/*.s", - }, + name = "lib", + srcs = { + "./_syscall.s", + "./sigaction.s", + "./signal.c", + "./trap.s", + "plat/linux/libsys/_exit.c", + "plat/linux/libsys/_hol0.s", + "plat/linux/libsys/close.c", + "plat/linux/libsys/creat.c", + "plat/linux/libsys/errno.s", + "plat/linux/libsys/execve.c", + "plat/linux/libsys/getpid.c", + "plat/linux/libsys/gettimeofday.c", + "plat/linux/libsys/ioctl.c", + "plat/linux/libsys/isatty.c", + "plat/linux/libsys/kill.c", + "plat/linux/libsys/lseek.c", + "plat/linux/libsys/open.c", + "plat/linux/libsys/read.c", + "plat/linux/libsys/sbrk.c", + -- omit signal.c + "plat/linux/libsys/sigprocmask.c", + "plat/linux/libsys/unlink.c", + "plat/linux/libsys/write.c", + }, deps = { "lang/cem/libcc.ansi/headers+headers", "plat/linuxppc/include+headers", }, - vars = { - plat = "linuxppc" - } + vars = { + plat = "linuxppc" + } } diff --git a/plat/linuxppc/libsys/sigaction.s b/plat/linuxppc/libsys/sigaction.s new file mode 100644 index 000000000..0509c8e72 --- /dev/null +++ b/plat/linuxppc/libsys/sigaction.s @@ -0,0 +1,156 @@ +#define __NR_sigaction 67 +#define SIG_BLOCK 0 +#define SIG_SETMASK 2 +#define MAXSIG 32 + +/* offsets into our stack frame */ +#define mynew 16 /* new sigaction */ +#define mynset 32 /* new signal set */ +#define myoset 36 /* old signal set */ +#define mysave 40 +#define mysize 56 + +.sect .text; .sect .rodata; .sect .data; .sect .bss + +/* + * Linux calls signal handlers with arguments in registers, but the + * ACK expects arguments on the stack. This sigaction() uses a + * "bridge" to move the arguments. + */ +.sect .text +.define _sigaction +_sigaction: + mflr r0 + subi r1, r1, mysize + stw r31, mysave+8(r1) + stw r30, mysave+4(r1) + stw r29, mysave(r1) + stw r0, mysave+12(r1) + li r3, 0 + stw r3, mynset(r1) ! mynset = 0 + lwz r29, mysize(r1) ! r29 = signal number + lwz r30, mysize+4(r1) ! r30 = new action + lwz r31, mysize+8(r1) ! r31 = old action + /* + * If the new action is non-NULL, the signal number is in + * range 1 to MAXSIG, and the new handler is not SIG_DFL 0 + * or SIG_IGN 1, then we interpose our bridge. + */ + cmpwi cr0, r30, 0 + subi r7, r29, 1 ! r7 = index in handlers + cmplwi cr7, r7, MAXSIG ! unsigned comparison + beq cr0, kernel + bge cr7, kernel + lwz r3, 0(r30) ! r3 = new handler + clrrwi. r3, r3, 1 + beq cr0, kernel + /* + * Block the signal while we build the bridge. Prevents a + * race if a signal arrives after we change the bridge but + * before we change the action in the kernel. + */ + li r4, 1 + slw r4, r4, r7 + stw r4, mynset(r1) ! mynmask = 1 << (signal - 1) + li r3, SIG_BLOCK + la r4, mynset(r1) + la r5, myoset(r1) + stw r3, 0(r1) + stw r4, 4(r1) + stw r5, 8(r1) + bl _sigprocmask + /* + * Point our bridge to the new signal handler. Then copy the + * new sigaction but point it to our bridge. + */ + lis r6, hi16[handlers] + ori r6, r6, lo16[handlers] + subi r7, r29, 1 + slwi r7, r7, 2 + lwz r3, 0(r30) ! r3 = new handler + stwx r3, r6, r7 ! put it in array of handlers + lis r3, hi16[bridge] + ori r3, r3, lo16[bridge] + lwz r4, 4(r30) + lwz r5, 8(r30) + lwz r6, 12(r30) + stw r3, mynew(r1) ! sa_handler or sa_sigaction + stw r4, mynew+4(r1) ! sa_mask + stw r5, mynew+8(r1) ! sa_flags + stw r6, mynew+12(r1) ! sa_restorer + la r30, mynew(r1) +kernel: + li r3, __NR_sigaction + stw r3, 0(r1) + stw r29, 4(r1) + stw r30, 8(r1) + stw r31, 12(r1) + bl __syscall + /* + * If we blocked the signal, then restore the old signal mask. + */ + lwz r3, mynset(r1) + cmpwi cr0, r3, 0 + beq cr0, fixold + li r3, SIG_SETMASK + la r4, myoset(r1) + li r5, 0 + stw r3, 0(r1) + stw r4, 4(r1) + stw r5, 8(r1) + bl _sigprocmask + /* + * If the old sigaction is non-NULL and points to our bridge, + * then point it to the signal handler. + */ +fixold: + cmpwi cr0, r31, 0 + beq cr0, leave + lis r3, hi16[bridge] + ori r3, r3, lo16[bridge] + lwz r4, 0(r31) + cmpw cr0, r3, r4 + bne cr0, leave + lis r6, hi16[handlers] + ori r6, r6, lo16[handlers] + subi r7, r29, 1 + slwi r7, r7, 2 + lwzx r3, r6, r7 ! get it from array of handlers + stw r3, 0(r31) ! put it in old sigaction +leave: + lwz r0, mysave+12(r1) + lwz r29, mysave(r1) + lwz r30, mysave+4(r1) + lwz r31, mysave+8(r1) + addi r1, r1, mysize + mtlr r0 + blr ! return from sigaction + +/* + * Linux calls bridge(signum) or bridge(signum, info, context) with + * arguments in registers r3, r4, r5. + */ +bridge: + mflr r0 + subi r1, r1, 16 + stw r0, 12(r1) + stw r3, 0(r1) ! signal number + stw r4, 4(r1) ! info + stw r5, 8(r1) ! context + + lis r6, hi16[handlers] + ori r6, r6, lo16[handlers] + subi r7, r3, 1 + slwi r7, r7, 2 + lwzx r6, r6, r7 + mtctr r6 + bctrl ! call our signal handler + + lwz r0, 12(r1) + addi r1, r1, 16 + mtlr r0 + blr ! return from bridge + +.sect .bss +handlers: + .space 4 * MAXSIG ! array of signal handlers diff --git a/plat/linuxppc/libsys/signal.c b/plat/linuxppc/libsys/signal.c new file mode 100644 index 000000000..0ed1918e1 --- /dev/null +++ b/plat/linuxppc/libsys/signal.c @@ -0,0 +1,19 @@ +#include + +/* + * Uses our bridge in sigaction.s when calling the signal handler. + * Mimics Linux __NR_signal by using SA_NODEFER | SA_RESETHAND. + */ +sighandler_t signal(int signum, sighandler_t handler) { + struct sigaction new, old; + int i; + + new.sa_handler = handler; + new.sa_mask = 0; /* empty set */ + new.sa_flags = SA_NODEFER | SA_RESETHAND; + + i = sigaction(signum, &new, &old); + if (i < 0) + return SIG_ERR; + return old.sa_handler; +}