diff --git a/headers/os/kernel/syscalls.h b/headers/os/kernel/syscalls.h index 5a7e7de6db..2af0fb82f3 100755 --- a/headers/os/kernel/syscalls.h +++ b/headers/os/kernel/syscalls.h @@ -60,6 +60,9 @@ status_t sys_get_next_thread_info(team_id team, int32 *cookie, thread_info *info status_t sys_get_team_info(team_id id, team_info *info); status_t sys_get_next_team_info(int32 *cookie, team_info *info); +int sys_kill(pid_t pid, int sig); +int sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact); + region_id sys_vm_create_anonymous_region(const char *name, void **address, int addr_type, addr size, int wiring, int lock); region_id sys_vm_clone_region(const char *name, void **address, int addr_type, diff --git a/headers/os/kernel/thread_types.h b/headers/os/kernel/thread_types.h index 685d3370f9..743efde13c 100644 --- a/headers/os/kernel/thread_types.h +++ b/headers/os/kernel/thread_types.h @@ -21,6 +21,7 @@ extern "C" { #include #include #include +#include #include extern spinlock_t thread_spinlock; @@ -101,7 +102,11 @@ struct thread { int state; int next_state; union cpu_ent *cpu; - int pending_signals; + + sigset_t sig_pending; + sigset_t sig_block_mask; + struct sigaction sig_action[32]; + bool in_kernel; sem_id sem_blocking; int sem_count; diff --git a/headers/os/support/Errors.h b/headers/os/support/Errors.h index ebcb9787ab..2b0d86dacd 100644 --- a/headers/os/support/Errors.h +++ b/headers/os/support/Errors.h @@ -178,6 +178,10 @@ enum { #define EOPNOTSUPP (B_POSIX_ERROR_BASE + 43) #define ENOTSOCK (B_POSIX_ERROR_BASE + 44) +#define ERESTARTSYS (B_POSIX_ERROR_BASE + 45) +#define ERESTARTNOINTR (B_POSIX_ERROR_BASE + 46) +#define ERESTARTNOHAND (B_POSIX_ERROR_BASE + 47) + #define ENOMEM B_NO_MEMORY #define EACCES B_PERMISSION_DENIED #define EINTR B_INTERRUPTED diff --git a/headers/posix/signal.h b/headers/posix/signal.h new file mode 100644 index 0000000000..1477d82f80 --- /dev/null +++ b/headers/posix/signal.h @@ -0,0 +1,166 @@ +#ifndef _SIGNAL_H_ +#define _SIGNAL_H_ + +/* +** Distributed under the terms of the OpenBeOS License. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int sig_atomic_t; + +typedef void (*__signal_func_ptr)(int); + +__signal_func_ptr signal(int signal, __signal_func_ptr signal_func); +int raise(int signal); + +#ifdef __cplusplus +} +#endif + +#define SIG_DFL ((__signal_func_ptr) 0) +#define SIG_IGN ((__signal_func_ptr) 1) +#define SIG_ERR ((__signal_func_ptr)-1) + +/* + The numbering of signals for BeOS attempts to maintain + some consistency with UN*X conventions so that things + like "kill -9" do what you expect. +*/ +#define SIGHUP 1 /* hangup -- tty is gone! */ +#define SIGINT 2 /* interrupt */ +#define SIGQUIT 3 /* `quit' special character typed in tty */ +#define SIGILL 4 /* illegal instruction */ +#define SIGCHLD 5 /* child process exited */ +#define SIGABRT 6 /* abort() called, dont' catch */ +#define SIGPIPE 7 /* write to a pipe w/no readers */ +#define SIGFPE 8 /* floating point exception */ +#define SIGKILL 9 /* kill a team (not catchable) */ +#define SIGSTOP 10 /* suspend a thread (not catchable) */ +#define SIGSEGV 11 /* segmentation violation (read: invalid pointer) */ +#define SIGCONT 12 /* continue execution if suspended */ +#define SIGTSTP 13 /* `stop' special character typed in tty */ +#define SIGALRM 14 /* an alarm has gone off (see alarm()) */ +#define SIGTERM 15 /* termination requested */ +#define SIGTTIN 16 /* read of tty from bg process */ +#define SIGTTOU 17 /* write to tty from bg process */ +#define SIGUSR1 18 /* app defined signal 1 */ +#define SIGUSR2 19 /* app defined signal 2 */ +#define SIGWINCH 20 /* tty window size changed */ +#define SIGKILLTHR 21 /* be specific: kill just the thread, not team */ +#define SIGTRAP 22 + +#define SIGBUS SIGSEGV /* for old style code */ + +/* + Signal numbers 23-32 are currently free but may be used in future + releases. Use them at your own peril (if you do use them, at least + be smart and use them backwards from signal 32). +*/ +#define __signal_max 22 +#define NSIG (__signal_max+1) + + + +typedef long sigset_t; + +/* + The Posix interface for signal handling functions isn't as useful + as it could be. The standard indicates that only a single argument + (the signal number) is passed to the signal handler. It is useful + to have more information and the BeOS provides two extra arguments. + However, to remain compatible with Posix and ANSI C, we declare the + sa_handler field of the sigaction struct as type __signal_func_ptr. + That means you'll need to cast any function you assign to the + sa_handler field. NOTE: C++ member functions can not be signal + handlers (because they expect a "this" pointer as the first + argument). + + The 3 arguments that the BeOS provides to signal handlers are as + follows: + - The first argument is the signal number (as an integer). + - The next argument is whatever value is put in the sa_userdata field + of the sigaction struct. + - The last argument is a pointer to a vregs struct (defined + below). The vregs struct contains the contents of the volatile + registers at the time the signal was delivered to your thread. + You can change the fields of the structure. After your signal + handler completes, the OS uses this struct to reload the + registers for your thread (privileged registers are not loaded + of course). The vregs struct is of course terribly machine + dependent and is guaranteed to change, potentially even between + different models of the PowerPC family. If you use it, you + should expect to have to re-work your code when new processors + come out. Nonetheless the ability to change the registers does + open some interesting programming possibilities. +*/ +struct sigaction { + __signal_func_ptr sa_handler; + sigset_t sa_mask; + int sa_flags; + void *sa_userdata; /* will be passed to the signal handler */ +}; + + +typedef struct stack_t { + void *ss_sp; + size_t ss_size; + int ss_flags; +} stack_t; + + +#define SA_NOCLDSTOP 0x01 /* for sa_flags */ +#define SA_ONESHOT 0x02 +#define SA_NOMASK 0x04 +#define SA_NODEFER SA_NOMASK +#define SA_RESTART 0x08 +#define SA_STACK 0x10 + + +int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); +int sigemptyset(sigset_t *set); +int sigfillset(sigset_t *set); +int sigaddset(sigset_t *set, int signo); +int sigdelset(sigset_t *set, int signo); +int sigismember(const sigset_t *set, int signo); +int sigprocmask(int how, const sigset_t *set, sigset_t *oset); +extern const char * const sys_siglist[NSIG]; +const char *strsignal(int sig); +const void set_signal_stack(void *ptr, size_t size); +int sigaltstack(const stack_t *ss, stack_t *oss); /* XXXdbg */ + +extern inline int +sigismember(const sigset_t *set, int sig) +{ + sigset_t mask = (((sigset_t) 1) << (( sig ) - 1)) ; + return (*set & mask) ? 1 : 0 ; +} +extern inline int +sigaddset(sigset_t *set, int sig) +{ + sigset_t mask = (((sigset_t) 1) << (( sig ) - 1)) ; + return ((*set |= mask), 0) ; +} +extern inline int +sigdelset(sigset_t *set, int sig) +{ + sigset_t mask = (((sigset_t) 1) << (( sig ) - 1)) ; + return ((*set &= ~mask), 0) ; +} + +#define SIG_BLOCK 1 /* defines for the how arg of sigprocmask() */ +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 3 + +int sigpending(sigset_t *set); +int sigsuspend(const sigset_t *mask); + +int kill(pid_t pid, int sig); +int send_signal(pid_t tid, uint sig); + + +#endif /* _SIGNAL_H_ */ diff --git a/headers/private/kernel/arch/thread.h b/headers/private/kernel/arch/thread.h index 4ad3e097d2..d1a27d9de1 100644 --- a/headers/private/kernel/arch/thread.h +++ b/headers/private/kernel/arch/thread.h @@ -18,6 +18,10 @@ void arch_thread_switch_kstack_and_call(struct thread *t, addr new_kstack, void //struct thread *arch_thread_get_current_thread(void); //void arch_thread_set_current_thread(struct thread *t); +void arch_setup_signal_frame(struct thread *t, struct sigaction *sa, int sig, int sig_mask); +int64 arch_restore_signal_frame(void); +void arch_check_syscall_restart(struct thread *t); + // for any inline overrides #include diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index e379a17144..db7847dc72 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -95,6 +95,8 @@ struct iframe { unsigned int edx; unsigned int ecx; unsigned int eax; + unsigned int orig_eax; + unsigned int orig_edx; unsigned int vector; unsigned int error_code; unsigned int eip; @@ -112,6 +114,10 @@ void i386_enter_uspace(addr entry, void *args, addr ustack_top); void i386_set_kstack(addr kstack); void i386_switch_stack_and_call(addr stack, void (*func)(void *), void *arg); void i386_swap_pgdir(addr new_pgdir); +void i386_fsave(void *fpu_state); +void i386_fxsave(void *fpu_state); +void i386_frstor(void *fpu_state); +void i386_fxrstor(void *fpu_state); void i386_fsave_swap(void *old_fpu_state, void *new_fpu_state); void i386_fxsave_swap(void *old_fpu_state, void *new_fpu_state); diff --git a/headers/private/kernel/arch/x86/arch_thread.h b/headers/private/kernel/arch/x86/arch_thread.h index f61167362c..f108b229a3 100644 --- a/headers/private/kernel/arch/x86/arch_thread.h +++ b/headers/private/kernel/arch/x86/arch_thread.h @@ -15,6 +15,9 @@ extern "C" { void i386_push_iframe(struct thread *t, struct iframe *frame); void i386_pop_iframe(struct thread *t); +void i386_return_from_signal(); +void i386_end_return_from_signal(); + static inline struct thread * diff --git a/headers/private/kernel/arch/x86/thread_struct.h b/headers/private/kernel/arch/x86/thread_struct.h index 96f181856a..93973c8518 100755 --- a/headers/private/kernel/arch/x86/thread_struct.h +++ b/headers/private/kernel/arch/x86/thread_struct.h @@ -18,6 +18,7 @@ struct arch_thread { struct farcall interrupt_stack; // used to track interrupts on this thread + struct iframe *current_iframe; struct iframe *iframes[IFRAME_TRACE_DEPTH]; int iframe_ptr; diff --git a/headers/private/kernel/thread.h b/headers/private/kernel/thread.h index 162a676054..e971db279b 100755 --- a/headers/private/kernel/thread.h +++ b/headers/private/kernel/thread.h @@ -11,6 +11,7 @@ extern "C" { #include #include +#include // Uncomment the line below to compile the single-queue scheduler //#define NEW_SCHEDULER @@ -18,6 +19,10 @@ extern "C" { void resched(void); void start_scheduler(void); +#define BLOCKABLE_SIGS (~((1L << (SIGKILL - 1)) | (1L << (SIGSTOP - 1)))) + +void handle_signals(struct thread *t, int state); + void thread_enqueue(struct thread *t, struct thread_queue *q); struct thread *thread_lookat_queue(struct thread_queue *q); struct thread *thread_dequeue(struct thread_queue *q); @@ -45,9 +50,7 @@ int thread_kill_thread_nowait(thread_id id); #define thread_get_current_thread arch_thread_get_current_thread struct thread *thread_get_thread_struct(thread_id id); -#ifdef NEW_SCHEDULER struct thread *thread_get_thread_struct_locked(thread_id id); -#endif /* NEW_SCHEDULER */ static thread_id thread_get_current_thread_id(void); static inline thread_id @@ -98,6 +101,8 @@ int user_setrlimit(int resource, const struct rlimit * rlp); int user_setenv(const char *name, const char *value, int overwrite); int user_getenv(const char *name, char **value); +int user_sigaction(int sig, const struct sigaction *act, struct sigaction *oact); + #if 1 // XXX remove later int thread_test(void); diff --git a/src/kernel/Jamfile b/src/kernel/Jamfile index 32dece9a43..97cae58d52 100644 --- a/src/kernel/Jamfile +++ b/src/kernel/Jamfile @@ -113,6 +113,7 @@ KernelLd kernel <$(SOURCE_GRIST)!core>queue.o <$(SOURCE_GRIST)!core>scheduler.o <$(SOURCE_GRIST)!core>sem.o + <$(SOURCE_GRIST)!core>signal.o <$(SOURCE_GRIST)!core>smp.o <$(SOURCE_GRIST)!core>syscalls.o <$(SOURCE_GRIST)!core>sysctl.o @@ -159,6 +160,7 @@ KernelLd kernel.so <$(SOURCE_GRIST)!core>queue.o <$(SOURCE_GRIST)!core>scheduler.o <$(SOURCE_GRIST)!core>sem.o + <$(SOURCE_GRIST)!core>signal.o <$(SOURCE_GRIST)!core>smp.o <$(SOURCE_GRIST)!core>syscalls.o <$(SOURCE_GRIST)!core>sysctl.o @@ -576,6 +578,18 @@ KernelLd fibo : bin/fibo ; +KernelLd sig_test : + libglue2.o + <$(SOURCE_GRIST)!apps>sig_test.o + libroot.so + : + $(SUBDIR)/ldscripts/$(OBOS_ARCH)/app.ld + : + : + : + bin/sig_test + ; + KernelLd fortune : libglue2.o <$(SOURCE_GRIST)!apps!fortune>main.o diff --git a/src/kernel/apps/Jamfile b/src/kernel/apps/Jamfile index 5eb5cbf907..2aa233a58d 100644 --- a/src/kernel/apps/Jamfile +++ b/src/kernel/apps/Jamfile @@ -1,6 +1,6 @@ SubDir OBOS_TOP src kernel apps ; -KernelObjects false_main.c fibo_main.c init.c true_main.c ; +KernelObjects false_main.c fibo_main.c init.c true_main.c sig_test.c ; SubInclude OBOS_TOP src kernel apps cpuinfo ; SubInclude OBOS_TOP src kernel apps echo ; diff --git a/src/kernel/apps/shell/commands.c b/src/kernel/apps/shell/commands.c index 36c1cf94bf..c33fcb6d17 100644 --- a/src/kernel/apps/shell/commands.c +++ b/src/kernel/apps/shell/commands.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ struct command cmds[] = { {"cat", &cmd_cat}, {"cd", &cmd_cd}, {"pwd", &cmd_pwd}, + {"kill", &cmd_kill}, {"help", &cmd_help}, {NULL, NULL} }; @@ -185,6 +187,20 @@ int cmd_stat(int argc, char *argv[]) return 0; } +int cmd_kill(int argc, char *argv[]) +{ + int rc; + + if (argc < 3) { + printf("not enough arguments to kill\n"); + return 0; + } + rc = sys_kill(atoi(argv[2]), atoi(argv[1])); + if (rc) + printf("kill failed\n"); + return 0; +} + int cmd_help(int argc, char *argv[]) { printf("command list:\n\n"); @@ -198,6 +214,7 @@ int cmd_help(int argc, char *argv[]) printf("cat : dumps the file to stdout\n"); printf("mount : tries to mount at \n"); printf("unmount : tries to unmount at \n"); + printf("kill : sends signal to thread \n"); return 0; } diff --git a/src/kernel/apps/shell/commands.h b/src/kernel/apps/shell/commands.h index 8621241878..ebf9837530 100644 --- a/src/kernel/apps/shell/commands.h +++ b/src/kernel/apps/shell/commands.h @@ -8,6 +8,7 @@ int cmd_mkdir(int argc, char *argv[]); int cmd_cat(int argc, char *argv[]); int cmd_cd(int argc, char *argv[]); int cmd_pwd(int argc, char *argv[]); +int cmd_kill(int argc, char *argv[]); int cmd_help(int argc, char *argv[]); int cmd_create_proc(int argc,char *argv[]); typedef int(cmd_handler_proc)(int argc,char *argv[]); diff --git a/src/kernel/apps/sig_test.c b/src/kernel/apps/sig_test.c new file mode 100644 index 0000000000..d2396be6de --- /dev/null +++ b/src/kernel/apps/sig_test.c @@ -0,0 +1,80 @@ +/* +** Small test for signals handling +*/ + + +#include +#include +#include +#include +#include + + +void sig_handler(int); +void install_handler(int); +int32 thread(void *); + + +void +sig_handler(int sig) +{ + int i; + + printf("sig_test (sig_handler): Received signal #%d...\n", sig); + printf("Counting for fun: "); + for (i=0; i<10; i++) + printf("%d ", i); + printf("\n"); +} + + +void +install_handler(int sig) +{ + struct sigaction newa; + struct sigaction olda; + + memset(&newa, 0, sizeof(newa)); + + newa.sa_handler = sig_handler; + newa.sa_flags = SA_NOMASK | SA_RESTART; + + if (sys_sigaction(sig, &newa, &olda) < 0) { + printf("Failed installing handler for sig #%d!\n", sig); + } +} + + +int32 +thread(void *data) +{ + printf("Started snoozing thread...\n"); + while (1) { + if (snooze(1000000000L) == B_INTERRUPTED) + printf("sig_test (thread): snooze was interrupted!\n"); + } + return 0; +} + + +int +main(int argc, char **argv) +{ + int i; + + printf("Installing signal handlers...\n"); + for (i=1; i<=NSIG; i++) + install_handler(i); + printf("Spawning some threads...\n"); + for (i=0; i<5; i++) + resume_thread(spawn_thread(thread, "sig_test aux thread", B_NORMAL_PRIORITY, NULL)); + printf("Done. Entering sleep mode...\n"); + while (1) { + if (sys_snooze(1000000000L) == B_INTERRUPTED) + // this never gets called as the syscall is always restarted + printf("sig_test (main): snooze was interrupted!\n"); + } + + return 0; +} + diff --git a/src/kernel/core/Jamfile b/src/kernel/core/Jamfile index a91e5c5ca5..6de473162c 100644 --- a/src/kernel/core/Jamfile +++ b/src/kernel/core/Jamfile @@ -22,7 +22,8 @@ KernelObjects <$(SOURCE_GRIST)>port.c <$(SOURCE_GRIST)>queue.c <$(SOURCE_GRIST)>scheduler.c - <$(SOURCE_GRIST)>sem.c + <$(SOURCE_GRIST)>sem.c + <$(SOURCE_GRIST)>signal.c <$(SOURCE_GRIST)>smp.c <$(SOURCE_GRIST)>syscalls.c <$(SOURCE_GRIST)>sysctl.c diff --git a/src/kernel/core/arch/x86/arch_int.c b/src/kernel/core/arch/x86/arch_int.c index 02824876bb..2366dfce26 100755 --- a/src/kernel/core/arch/x86/arch_int.c +++ b/src/kernel/core/arch/x86/arch_int.c @@ -165,8 +165,10 @@ i386_handle_trap(struct iframe frame) int ret = B_HANDLED_INTERRUPT; struct thread *thread = thread_get_current_thread(); - if (thread) + if (thread) { i386_push_iframe(thread, &frame); + thread->arch_info.current_iframe = &frame; + } // if(frame.vector != 0x20) // dprintf("i386_handle_trap: vector 0x%x, ip 0x%x, cpu %d\n", frame.vector, frame.eip, smp_get_current_cpu()); @@ -249,7 +251,7 @@ i386_handle_trap(struct iframe frame) } break; } - + if (ret == B_INVOKE_SCHEDULER) { int state = disable_interrupts(); GRAB_THREAD_LOCK(); @@ -261,6 +263,7 @@ i386_handle_trap(struct iframe frame) if (frame.cs == USER_CODE_SEG || frame.vector == 99) { thread_atkernel_exit(); } + // dprintf("0x%x cpu %d!\n", thread_get_current_thread_id(), smp_get_current_cpu()); if (thread) diff --git a/src/kernel/core/arch/x86/arch_interrupts.S b/src/kernel/core/arch/x86/arch_interrupts.S index 37c268c599..639a3ea4ca 100755 --- a/src/kernel/core/arch/x86/arch_interrupts.S +++ b/src/kernel/core/arch/x86/arch_interrupts.S @@ -15,6 +15,8 @@ .align 8; \ name: \ pushl $vector; \ + pushl $-1; \ + pushl $-1; \ jmp int_bottom #define TRAP(name, vector) \ @@ -23,6 +25,8 @@ name: \ name: \ pushl $0; \ pushl $vector; \ + pushl %edx; \ + pushl %eax; \ jmp int_bottom TRAP(trap0, 0) @@ -92,7 +96,7 @@ int_bottom: pop %es pop %ds popa - addl $8,%esp + addl $16,%esp iret // custom stack -> copy registers to kernel stack and switch there @@ -102,22 +106,22 @@ int_bottom: addl _interrupt_stack_offset,%edx lss (%edx),%esp movl %ebx,%ds - subl $84,%esp + subl $92,%esp movl %esp,%edi - movl $19,%ecx + movl $21,%ecx rep movsl movl %eax,%ds - subl $76,%esi + subl $84,%esi movl %esi,(%edi) // save custom stack address movl %ebx,4(%edi) call i386_handle_trap - lss 76(%esp),%esp // reload custom stack address + lss 84(%esp),%esp // reload custom stack address pop %gs pop %fs pop %es pop %ds popa - addl $8,%esp + addl $16,%esp iret _interrupt_stack_offset: @@ -151,3 +155,13 @@ FUNCTION(i386_stack_switch): pushl %ecx popf jmp *%edx + +FUNCTION(i386_return_from_signal): + popl %eax // flush the only signal handler parameter + movl $103, %eax // This syscall will restore the cpu context to the + movl $0, %ecx // one existing before calling the signal handler + lea 4(%esp), %edx + int $99 + ret +FUNCTION(i386_end_return_from_signal): + diff --git a/src/kernel/core/arch/x86/arch_thread.c b/src/kernel/core/arch/x86/arch_thread.c index ad4fce2373..c10748a91e 100755 --- a/src/kernel/core/arch/x86/arch_thread.c +++ b/src/kernel/core/arch/x86/arch_thread.c @@ -10,8 +10,11 @@ #include #include #include +#include #include #include +#include +#include // from arch_interrupts.S @@ -194,3 +197,115 @@ arch_thread_enter_uspace(addr entry, void *args, addr ustack_top) i386_enter_uspace(entry, args, ustack_top - 4); } + +void +arch_setup_signal_frame(struct thread *t, struct sigaction *sa, int sig, int sig_mask) +{ + struct iframe *frame = t->arch_info.current_iframe; + uint32 *stack = (uint32 *)frame->user_esp; + uint32 *code; + uint32 *fpu_state; + + if (frame->orig_eax >= 0) { + // we're coming from a syscall + switch (frame->eax) { + case ERESTARTNOHAND: + frame->eax = EINTR; + break; + case EINTR: + case ERESTARTSYS: + if (!(sa->sa_flags & SA_RESTART)) { + frame->eax = EINTR; + break; + } + /* fallthrough */ + case ERESTARTNOINTR: + dprintf("### restarting syscall %d after signal %d\n", frame->orig_eax, sig); + frame->eax = frame->orig_eax; + frame->edx = frame->orig_edx; + frame->eip -= 2; + break; + } + } + + stack -= 192; + code = stack + 25; + fpu_state = stack + 64; + + stack[0] = (uint32)code; // return address when sa_handler done + stack[1] = sig; // only argument to sa_handler + stack[2] = frame->gs; + stack[3] = frame->fs; + stack[4] = frame->es; + stack[5] = frame->ds; + stack[6] = frame->edi; + stack[7] = frame->esi; + stack[8] = frame->ebp; + stack[9] = frame->esp; + stack[10] = frame->ebx; + stack[11] = frame->edx; + stack[12] = frame->ecx; + stack[13] = frame->eax; + stack[18] = frame->eip; + stack[19] = frame->cs; + stack[20] = frame->flags; + stack[21] = frame->user_esp; + stack[22] = frame->user_ss; + stack[23] = sig_mask; + stack[24] = (uint32)fpu_state; + + i386_fsave(fpu_state); + + memcpy(code, i386_return_from_signal, (i386_end_return_from_signal - i386_return_from_signal)); + + frame->user_esp = (uint32)stack; + frame->eip = (uint32)sa->sa_handler; +} + + +int64 +arch_restore_signal_frame(void) +{ + struct thread *t = thread_get_current_thread(); + struct iframe *frame; + uint32 fpu_state; + + dprintf("### arch_restore_signal_frame: entry\n"); + + frame = t->arch_info.current_iframe; + t->sig_block_mask = *((sigset_t *)((void *)frame->user_esp + sizeof(struct iframe))) & BLOCKABLE_SIGS; + fpu_state = *((uint32 *)((void *)frame->user_esp + sizeof(struct iframe) + sizeof(uint32))); + + memcpy((void *)frame, (void *)frame->user_esp, sizeof(struct iframe)); + + i386_frstor((void *)fpu_state); + + dprintf("### arch_restore_signal_frame: exit\n"); + + frame->orig_eax = -1; /* disable syscall checks */ + + return (int64)frame->eax | ((int64)frame->edx << 32); +} + + +void +arch_check_syscall_restart(struct thread *t) +{ + struct iframe *frame = t->arch_info.current_iframe; + + dprintf("### arch_check_syscall_restart: entry\n"); + if (frame->orig_eax >= 0) { + dprintf("### arch_check_syscall_restart: coming from a syscall\n"); + if ((frame->eax == ERESTARTNOHAND) || + (frame->eax == EINTR) || + (frame->eax == ERESTARTSYS) || + (frame->eax == ERESTARTNOINTR)) { + dprintf("### arch_check_syscall_restart: syscall restart needed\n"); + frame->eax = frame->orig_eax; + frame->edx = frame->orig_edx; + frame->eip -= 2; + } + } + dprintf("### arch_check_syscall_restart: exit\n"); +} + diff --git a/src/kernel/core/arch/x86/arch_x86.S b/src/kernel/core/arch/x86/arch_x86.S index 4b2dc78eee..b109978292 100755 --- a/src/kernel/core/arch/x86/arch_x86.S +++ b/src/kernel/core/arch/x86/arch_x86.S @@ -101,6 +101,30 @@ FUNCTION(arch_cpu_global_TLB_invalidate): movl %eax,%cr3 ret +/* void i386_fsave(void *fpu_state); */ +FUNCTION(i386_fsave): + movl 4(%esp), %eax + fsave (%eax) + ret + +/* void i386_fxsave(void *fpu_state); */ +FUNCTION(i386_fxsave): + movl 4(%esp), %eax + fxsave (%eax) + ret + +/* void i386_frstor(void *fpu_state); */ +FUNCTION(i386_frstor): + movl 4(%esp), %eax + frstor (%eax) + ret + +/* void i386_fxrstor(void *fpu_state); */ +FUNCTION(i386_fxrstor): + movl 4(%esp), %eax + fxrstor (%eax) + ret + /* void i386_fsave_swap(void *old_fpu_state, void *new_fpu_state); */ FUNCTION(i386_fsave_swap): movl 4(%esp),%eax diff --git a/src/kernel/core/sem.c b/src/kernel/core/sem.c index 6b35aef22d..025d810100 100644 --- a/src/kernel/core/sem.c +++ b/src/kernel/core/sem.c @@ -401,9 +401,9 @@ acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout) TRACE_BLOCK(("acquire_sem_etc(id = %ld): block name = %s, thread = %p, name = %s\n", id, gSems[slot].name, t, t->name)); - // do a quick check to see if the thread has any pending kill signals + // do a quick check to see if the thread has any pending signals // this should catch most of the cases where the thread had a signal - if ((flags & B_CAN_INTERRUPT) && (t->pending_signals & SIG_KILL)) { + if ((flags & B_CAN_INTERRUPT) && t->sig_pending) { gSems[slot].count += count; status = B_INTERRUPTED; goto err; @@ -436,9 +436,9 @@ acquire_sem_etc(sem_id id, int32 count, uint32 flags, bigtime_t timeout) RELEASE_SEM_LOCK(gSems[slot]); GRAB_THREAD_LOCK(); - // check again to see if a kill signal is pending. + // check again to see if a signal is pending. // it may have been delivered while setting up the sem, though it's pretty unlikely - if ((flags & B_CAN_INTERRUPT) && (t->pending_signals & SIG_KILL)) { + if ((flags & B_CAN_INTERRUPT) && t->sig_pending) { struct thread_queue wakeup_queue; // ok, so a tiny race happened where a signal was delivered to this thread while // it was setting up the sem. We can only be sure a signal wasn't delivered @@ -768,6 +768,8 @@ sem_interrupt_thread(struct thread *t) if ((t->sem_flags & B_CAN_INTERRUPT) == 0) return ERR_SEM_NOT_INTERRUPTABLE; + t->next_state = B_THREAD_READY; + slot = t->sem_blocking % MAX_SEMS; GRAB_SEM_LOCK(gSems[slot]); diff --git a/src/kernel/core/signal.c b/src/kernel/core/signal.c new file mode 100644 index 0000000000..1a192db4b4 --- /dev/null +++ b/src/kernel/core/signal.c @@ -0,0 +1,249 @@ +/* POSIX signals handling routines +** +** Copyright 2002, Angelo Mottola, a.mottola@libero.it. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +const char * const sys_siglist[NSIG] = { + "NONE", "HUP", "INT", "QUIT", "ILL", "CHLD", "ABRT", "PIPE", + "FPE", "KILL", "STOP", "SEGV", "CONT", "TSTP", "ALRM", "TERM", + "TTIN", "TTOU", "USR1", "USR2", "WINCH", "KILLTHR", "TRAP" +}; + + +// Expects interrupts off and thread lock held. +void +handle_signals(struct thread *t, int state) +{ + uint32 sig_mask = t->sig_pending & (~t->sig_block_mask); + int i, sig; + struct sigaction *handler; + + if (sig_mask) { + for (i = 0; i < NSIG; i++) { + if (sig_mask & 0x1) { + + sig = i + 1; + handler = &t->sig_action[i]; + sig_mask >>= 1; + t->sig_pending &= ~(1L << i); + + dprintf("Thread 0x%lx received signal %s\n", t->id, sys_siglist[sig]); + + if (handler->sa_handler == SIG_IGN) { + // signal is to be ignored + // XXX apply zombie cleaning on SIGCHLD + continue; + } + if (handler->sa_handler == SIG_DFL) { + // default signal behaviour + switch (sig) { + case SIGCHLD: + case SIGWINCH: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + case SIGCONT: + continue; + + case SIGSTOP: + t->next_state = B_THREAD_SUSPENDED; + continue; + + case SIGQUIT: + case SIGILL: + case SIGTRAP: + case SIGABRT: + case SIGFPE: + case SIGSEGV: + dprintf("Shutting down thread 0x%lx due to signal #%d\n", t->id, sig); + case SIGKILL: + case SIGKILLTHR: + default: + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + thread_exit(sig); + } + } + + // User defined signal handler + dprintf("### Setting up custom signal handler frame...\n"); + arch_setup_signal_frame(t, handler, sig, t->sig_block_mask); + + if (handler->sa_flags & SA_ONESHOT) + handler->sa_handler = SIG_DFL; + if (!(handler->sa_flags & SA_NOMASK)) + t->sig_block_mask |= (handler->sa_mask | (1L << sig)) & BLOCKABLE_SIGS; + + return; + } else + sig_mask >>= 1; + } + arch_check_syscall_restart(t); + } +} + + +int +send_signal_etc(pid_t tid, uint sig, uint32 flags) +{ + int state; + struct thread *t, *main_t; + + if ((sig < 1) || (sig > 32)) + return B_BAD_VALUE; + + state = disable_interrupts(); + GRAB_THREAD_LOCK(); + + t = thread_get_thread_struct_locked(tid); + if (!t) { + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + return B_BAD_THREAD_ID; + } + // XXX check permission + + // Signals to kernel threads will only wake them up + if (t->team == team_get_kernel_team()) { + if (t->state == B_THREAD_SUSPENDED) { + t->state = t->next_state = B_THREAD_READY; + thread_enqueue_run_q(t); + } + } + else { + t->sig_pending |= (1L << (sig - 1)); + + switch (sig) { + case SIGKILL: + // Forward KILLTHR to the main thread of the team + main_t = t->team->main_thread; + main_t->sig_pending |= (1L << (SIGKILLTHR - 1)); + // Wake up main thread + if (main_t->state == B_THREAD_SUSPENDED) { + main_t->state = main_t->next_state = B_THREAD_READY; + thread_enqueue_run_q(main_t); + } else if (main_t->state == B_THREAD_WAITING) + sem_interrupt_thread(main_t); + // Fallthrough + case SIGKILLTHR: + // Wake up suspended threads and interrupt waiting ones + if (t->state == B_THREAD_SUSPENDED) { + t->state = t->next_state = B_THREAD_READY; + thread_enqueue_run_q(t); + } else if (t->state == B_THREAD_WAITING) + sem_interrupt_thread(t); + break; + case SIGCONT: + // Wake up thread if it was suspended + if ((t->state == B_THREAD_READY) || + (t->state == B_THREAD_SUSPENDED)) { + t->state = t->next_state = B_THREAD_READY; + thread_enqueue_run_q(t); + } + break; + default: + if (t->sig_pending & ((~t->sig_block_mask) | (1L << (SIGCHLD - 1)))) { + // Interrupt thread if it was waiting + if (t->state == B_THREAD_WAITING) + sem_interrupt_thread(t); + } + break; + } + } + + if (!(flags & B_DO_NOT_RESCHEDULE)) + resched(); + + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + + return B_OK; +} + + +int +has_signals_pending(struct thread *t) +{ + if (!t) + t = thread_get_current_thread(); + return (t->sig_pending & ~t->sig_block_mask); +} + + +int +sys_kill(pid_t tid, int sig) +{ + return send_signal_etc(tid, sig, 0); +} + + +int +user_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) +{ + struct sigaction kact; + struct sigaction koact; + int rc; + + if ((!act) || (!oact)) + return EINVAL; + + rc = user_memcpy(&kact, act, sizeof(struct sigaction)); + if (rc < 0) + return rc; + rc = user_memcpy(&koact, oact, sizeof(struct sigaction)); + if (rc < 0) + return rc; + rc = sys_sigaction(sig, &kact, &koact); + if (rc < 0) + return rc; + rc = user_memcpy(oact, &koact, sizeof(struct sigaction)); + return rc; +} + + +int +sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) +{ + struct thread *t; + int state; + + if ((sig < 1) || (sig > 32)) + return EINVAL; + if ((sig == SIGKILL) || (sig == SIGKILLTHR) || (sig == SIGSTOP)) + return EINVAL; + + state = disable_interrupts(); + GRAB_THREAD_LOCK(); + + t = thread_get_current_thread(); + + memcpy(oact, &t->sig_action[sig - 1], sizeof(struct sigaction)); + memcpy(&t->sig_action[sig - 1], act, sizeof(struct sigaction)); + + if (act->sa_handler == SIG_IGN) + t->sig_pending &= ~(1L << (sig - 1)); + else if (act->sa_handler == SIG_DFL) { + if ((sig == SIGCONT) || (sig == SIGCHLD) || (sig == SIGWINCH)) + t->sig_pending &= ~(1L << (sig - 1)); + } + else + dprintf("### custom signal handler set\n"); + + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + + return 0; +} + diff --git a/src/kernel/core/syscalls.c b/src/kernel/core/syscalls.c index 23e6a8d2cf..7f774a050f 100644 --- a/src/kernel/core/syscalls.c +++ b/src/kernel/core/syscalls.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -385,6 +386,15 @@ int syscall_dispatcher(unsigned long call_num, void *arg_buffer, uint64 *call_re case SYSCALL_GET_NEXT_TEAM_INFO: *call_ret = user_get_next_team_info((int32 *)arg0, (team_info *)arg1); break; + case SYSCALL_RETURN_FROM_SIGNAL: + *call_ret = arch_restore_signal_frame(); + break; + case SYSCALL_KILL: + *call_ret = sys_kill((pid_t)arg0, (int)arg1); + break; + case SYSCALL_SIGACTION: + *call_ret = user_sigaction((int)arg0, (const struct sigaction *)arg1, (struct sigaction *)arg2); + break; default: *call_ret = -1; } diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c index d4552f05aa..db85a2d130 100644 --- a/src/kernel/core/thread.c +++ b/src/kernel/core/thread.c @@ -1,4 +1,4 @@ -/* Threading and team information */ +/* Threading routines */ /* ** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. @@ -75,11 +75,8 @@ static struct thread_queue run_q[(B_MAX_PRIORITY / 2) + 1] = { { NULL, NULL }, } struct thread_queue dead_q; static void thread_entry(void); -#ifndef NEW_SCHEDULER -static struct thread *thread_get_thread_struct_locked(thread_id id); -#endif /* not NEW_SCHEDULER */ static void thread_kthread_exit(void); -static void deliver_signal(struct thread *t, int signal); +//static void deliver_signal(struct thread *t, int signal); // insert a thread onto the tail of a queue @@ -266,11 +263,13 @@ create_thread_struct(const char *name) t->q_next = NULL; t->priority = -1; t->args = NULL; - t->pending_signals = SIG_NONE; + t->sig_pending = 0; + t->sig_block_mask = 0; + memset(t->sig_action, 0, 32 * sizeof(struct sigaction)); t->in_kernel = true; t->user_time = 0; t->kernel_time = 0; - t->last_time = 0; + t->last_time = 0; sprintf(temp, "thread_0x%lx_retcode_sem", t->id); t->return_code_sem = create_sem(0, temp); @@ -326,7 +325,9 @@ _create_user_thread_kentry(void) t = thread_get_current_thread(); // a signal may have been delivered here - thread_atkernel_exit(); +// thread_atkernel_exit(); + t->last_time = system_time(); + t->in_kernel = false; // jump to the entry point in user space arch_thread_enter_uspace((addr)t->entry, t->args, t->user_stack_base + STACK_SIZE); @@ -343,7 +344,9 @@ _create_kernel_thread_kentry(void) struct thread *t; t = thread_get_current_thread(); - + + t->last_time = system_time(); + // call the entry function with the appropriate args func = (void *)t->entry; @@ -466,70 +469,22 @@ thread_create_kernel_thread_etc(const char *name, int (*func)(void *), void *arg int thread_suspend_thread(thread_id id) { - int state; - struct thread *t; - int retval; - bool global_resched = false; - - state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - t = thread_get_current_thread(); - if (t->id != id) { - t = thread_get_thread_struct_locked(id); - } - - if (t != NULL) { - if (t->team == team_get_kernel_team()) { - // no way - retval = EPERM; - } else if (t->in_kernel == true) { - t->pending_signals |= SIG_SUSPEND; - retval = B_NO_ERROR; - } else { - t->next_state = B_THREAD_SUSPENDED; - global_resched = true; - retval = B_NO_ERROR; - } - } else - retval = ERR_INVALID_HANDLE; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - if (global_resched) + int rv; + + rv = send_signal_etc(id, SIGSTOP, B_DO_NOT_RESCHEDULE); + if (rv == B_OK) smp_send_broadcast_ici(SMP_MSG_RESCHEDULE, 0, 0, 0, NULL, SMP_MSG_FLAG_SYNC); - - return retval; + return rv; } int thread_resume_thread(thread_id id) { - int state; - struct thread *t; - int retval; - - state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - t = thread_get_thread_struct_locked(id); - if (t != NULL && t->state == B_THREAD_SUSPENDED) { - t->state = B_THREAD_READY; - t->next_state = B_THREAD_READY; - - thread_enqueue_run_q(t); - retval = B_NO_ERROR; - } else - retval = ERR_INVALID_HANDLE; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - - return retval; + return send_signal_etc(id, SIGCONT, B_DO_NOT_RESCHEDULE); } + #ifndef NEW_SCHEDULER status_t thread_set_priority(thread_id id, int32 priority) @@ -614,7 +569,7 @@ _dump_thread_info(struct thread *t) dprintf("(%d)\n", t->cpu->info.cpu_num); else dprintf("\n"); - dprintf("pending_signals: 0x%x\n", t->pending_signals); + dprintf("sig_pending: 0x%lx\n", t->sig_pending); dprintf("in_kernel: %d\n", t->in_kernel); dprintf("sem_blocking:0x%lx\n", t->sem_blocking); dprintf("sem_count: 0x%x\n", t->sem_count); @@ -1072,13 +1027,7 @@ thread_exit(int retcode) } RELEASE_TEAM_LOCK(); restore_interrupts(state); -#if 0 - // Now wait for all of the threads to die - // XXX block on a semaphore - while((volatile int)p->num_threads > 0) { - snooze(10000); // 10 ms - } -#endif + // wait until all threads in team are dead. acquire_sem_etc(p->death_sem, p->num_threads, 0, 0); delete_sem(p->death_sem); } @@ -1125,55 +1074,25 @@ thread_exit(int retcode) } -static int -_thread_kill_thread(thread_id id, bool wait_on) -{ - int state; - struct thread *t; - int rc; - -// dprintf("_thread_kill_thread: id %d, wait_on %d\n", id, wait_on); - - state = disable_interrupts(); - GRAB_THREAD_LOCK(); - - t = thread_get_thread_struct_locked(id); - if (t != NULL) { - if (t->team == team_get_kernel_team()) { - // can't touch this - rc = EPERM; - } else { - deliver_signal(t, SIG_KILL); - rc = B_NO_ERROR; - if (t->id == thread_get_current_thread()->id) - wait_on = false; // can't wait on ourself - } - } else - rc = ERR_INVALID_HANDLE; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - if (rc < 0) - return rc; - - if (wait_on) - thread_wait_on_thread(id, NULL); - - return rc; -} - - int thread_kill_thread(thread_id id) { - return _thread_kill_thread(id, true); + int rv; + + rv = send_signal_etc(id, SIGKILLTHR, B_DO_NOT_RESCHEDULE); + if (rv < 0) + return rv; + if (id != thread_get_current_thread()->id) + thread_wait_on_thread(id, NULL); + + return rv; } int thread_kill_thread_nowait(thread_id id) { - return _thread_kill_thread(id, false); + return send_signal_etc(id, SIGKILLTHR, B_DO_NOT_RESCHEDULE); } @@ -1192,6 +1111,10 @@ thread_wait_on_thread(thread_id id, int *retcode) struct thread *t; int rc; + rc = send_signal_etc(id, SIGCONT, 0); + if (rc != B_OK) + return rc; + state = disable_interrupts(); GRAB_THREAD_LOCK(); @@ -1199,7 +1122,7 @@ thread_wait_on_thread(thread_id id, int *retcode) if (t != NULL) { sem = t->return_code_sem; } else { - sem = ERR_INVALID_HANDLE; + sem = B_BAD_THREAD_ID; } RELEASE_THREAD_LOCK(); @@ -1244,11 +1167,9 @@ thread_get_thread_struct(thread_id id) return t; } -#ifndef NEW_SCHEDULER -static struct thread *thread_get_thread_struct_locked(thread_id id) -#else /* NEW_SCHEDULER */ -struct thread *thread_get_thread_struct_locked(thread_id id) -#endif /* NEW_SCHEDULER */ + +struct thread * +thread_get_thread_struct_locked(thread_id id) { struct thread_key key; @@ -1258,61 +1179,6 @@ struct thread *thread_get_thread_struct_locked(thread_id id) } -// sets the pending signal flag on a thread and possibly does some work to wake it up, etc. -// expects the thread lock to be held - -static void -deliver_signal(struct thread *t, int signal) -{ -// dprintf("deliver_signal: thread %p (%d), signal %d\n", t, t->id, signal); - switch (signal) { - case SIG_KILL: - t->pending_signals |= SIG_KILL; - switch (t->state) { - case B_THREAD_SUSPENDED: - t->state = B_THREAD_READY; - t->next_state = B_THREAD_READY; - - thread_enqueue_run_q(t); - break; - case B_THREAD_WAITING: - sem_interrupt_thread(t); - break; - default: - ; - } - break; - default: - t->pending_signals |= signal; - } -} - - -// expects the thread lock to be held - -static void -_check_for_thread_sigs(struct thread *t, int state) -{ - if (t->pending_signals == SIG_NONE) - return; - - if (t->pending_signals & SIG_KILL) { - t->pending_signals &= ~SIG_KILL; - - RELEASE_THREAD_LOCK(); - restore_interrupts(state); - thread_exit(0); - // never gets to here - } - if (t->pending_signals & SIG_SUSPEND) { - t->pending_signals &= ~SIG_SUSPEND; - t->next_state = B_THREAD_SUSPENDED; - // XXX will probably want to delay this - resched(); - } -} - - // called in the int handler code when a thread enters the kernel for any reason void @@ -1333,13 +1199,8 @@ thread_atkernel_entry(void) t->user_time += now - t->last_time; t->last_time = now; - GRAB_THREAD_LOCK(); - t->in_kernel = true; - _check_for_thread_sigs(t, state); - - RELEASE_THREAD_LOCK(); restore_interrupts(state); } @@ -1360,7 +1221,7 @@ thread_atkernel_exit(void) state = disable_interrupts(); GRAB_THREAD_LOCK(); - _check_for_thread_sigs(t, state); + handle_signals(t, state); t->in_kernel = false; diff --git a/src/kernel/libroot/os/syscalls.S b/src/kernel/libroot/os/syscalls.S index 6fa029c76b..89b2b3acdf 100644 --- a/src/kernel/libroot/os/syscalls.S +++ b/src/kernel/libroot/os/syscalls.S @@ -150,3 +150,8 @@ SYSCALL6(sys_sysctl, 71) SYSCALL3(sys_setenv, 79) SYSCALL2(sys_getenv, 80) +/* Signal handling calls */ +SYSCALL0(sys_return_from_signal, 103) +SYSCALL2(sys_kill, 104) +SYSCALL3(sys_sigaction, 105) +