From 51e3de971c23e3469e5714708970a950baabeba1 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Wed, 1 Aug 2012 17:30:34 +0200 Subject: [PATCH] Multithreaded kernel and improvement of signal handling. Pardon the big ass-commit, this took months to develop and debug and the refactoring got so far that a clean merge became impossible. The good news is that this commit does quite a bit of cleaning up and generally improves the kernel quality. This makes the kernel fully pre-emptive and multithreaded. This was done by rewriting the interrupt code, the scheduler, introducing new threading primitives, and rewriting large parts of the kernel. During the past few commits the kernel has had its device drivers thread secured; this commit thread secures large parts of the core kernel. There still remains some parts of the kernel that is _not_ thread secured, but this is not a problem at this point. Each user-space thread has an associated kernel stack that it uses when it goes into kernel mode. This stack is by default 8 KiB since that value works for me and is also used by Linux. Strange things tends to happen on x86 in case of a stack overflow - there is no ideal way to catch such a situation right now. The system call conventions were changed, too. The %edx register is now used to provide the errno value of the call, instead of the kernel writing it into a registered global variable. The system call code has also been updated to better reflect the native calling conventions: not all registers have to be preserved. This makes system calls faster and simplifies the assembly. In the kernel, there is no longer the event.h header or the hacky method of 'resuming system calls' that closely resembles cooperative multitasking. If a system call wants to block, it should just block. The signal handling was also improved significantly. At this point, signals cannot interrupt kernel threads (but can always interrupt user-space threads if enabled), which introduces some problems with how a SIGINT could interrupt a blocking read, for instance. This commit introduces and uses a number of new primitives such as kthread_lock_mutex_signal() that attempts to get the lock but fails if a signal is pending. In this manner, the kernel is safer as kernel threads cannot be shut down inconveniently, but in return for complexity as blocking operations must check they if they should fail. Process exiting has also been refactored significantly. The _exit(2) system call sets the exit code and sends SIGKILL to all the threads in the process. Once all the threads have cleaned themselves up and exited, a worker thread calls the process's LastPrayer() method that unmaps memory, deletes the address space, notifies the parent, etc. This provides a very robust way to terminate processes as even half-constructed processes (during a failing fork for instance) can be gracefully terminated. I have introduced a number of kernel threads to help avoid threading problems and simplify kernel design. For instance, there is now a functional generic kernel worker thread that any kernel thread can schedule jobs for. Interrupt handlers run with interrupts off (hence they cannot call kthread_ functions as it may deadlock the system if another thread holds the lock) therefore they cannot use the standard kernel worker threads. Instead, they use a special purpose interrupt worker thread that works much like the generic one expect that interrupt handlers can safely queue work with interrupts off. Note that this also means that interrupt handlers cannot allocate memory or print to the kernel log/screen as such mechanisms uses locks. I'll introduce a lock free algorithm for such cases later on. The boot process has also changed. The original kernel init thread in kernel.cpp creates a new bootstrap thread and becomes the system idle thread. Note that pid=0 now means the kernel, as there is no longer a system idle process. The bootstrap thread launches all the kernel worker threads and then creates a new process and loads /bin/init into it and then creates a thread in pid=1, which starts the system. The bootstrap thread then quietly waits for pid=1 to exit after which it shuts down/reboots/panics the system. In general, the introduction of race conditions and dead locks have forced me to revise a lot of the design and make sure it was thread secure. Since early parts of the kernel was quite hacky, I had to refactor such code. So it seems that the risk of dead locks forces me to write better code. Note that a real preemptive multithreaded kernel simplifies the construction of blocking system calls. My hope is that this will trigger a clean up of the filesystem code that current is almost beyond repair. Almost all of the kernel was modified during this refactoring. To the extent possible, these changes have been backported to older non-multithreaded kernel, but many changes were tightly coupled and went into this commit. Of interest is the implementation of the kthread_ api based on the design of pthreads; this library allows easy synchronization mechanisms and includes C++-style scoped locks. This commit also introduces new worker threads and tested mechanisms for interrupt handlers to schedule work in a kernel worker thread. A lot of code have been rewritten from scratch and has become a lot more stable and correct. Share and enjoy! --- libmaxsi/error.cpp | 5 +- libmaxsi/include/libmaxsi/signal.h | 42 - libmaxsi/include/libmaxsi/signalnum.h | 71 -- libmaxsi/include/libmaxsi/syscall.h | 84 +- libmaxsi/include/signal.h | 44 +- libmaxsi/init.cpp | 18 +- libmaxsi/signal.cpp | 71 +- libmaxsi/x64/signal.s | 14 +- libmaxsi/x86/signal.s | 17 +- sortix/Makefile | 2 +- sortix/com.cpp | 2 +- sortix/cpu.h | 13 + sortix/event.cpp | 84 -- sortix/event.h | 50 - sortix/include/sortix/kernel/kthread.h | 23 +- .../include/sortix/kernel/memorymanagement.h | 4 +- sortix/include/sortix/signal.h | 79 ++ sortix/include/sortix/syscallnum.h | 4 +- sortix/interrupt.cpp | 129 ++- sortix/interrupt.h | 11 +- sortix/kernel.cpp | 203 +++- sortix/kthread.cpp | 83 +- sortix/logterminal.cpp | 8 +- sortix/logterminal.h | 1 - sortix/panic.cpp | 2 + sortix/pipe.cpp | 5 +- sortix/process.cpp | 926 +++++++++--------- sortix/process.h | 65 +- sortix/scheduler.cpp | 603 +++++------- sortix/scheduler.h | 46 +- sortix/serialterminal.h | 10 +- sortix/signal.cpp | 314 ++---- sortix/signal.h | 89 +- sortix/syscall.cpp | 98 +- sortix/syscall.h | 39 +- sortix/textterminal.cpp | 4 + sortix/textterminal.h | 3 + sortix/thread.cpp | 352 +++---- sortix/thread.h | 107 +- sortix/x64/interrupt.s | 60 +- sortix/x64/kthread.s | 85 ++ sortix/x64/memorymanagement.cpp | 41 +- sortix/x64/process.cpp | 49 +- sortix/x64/syscall.s | 142 +-- sortix/x64/thread.cpp | 127 +-- sortix/x64/x64.h | 29 +- sortix/x86-family/memorymanagement.cpp | 2 + sortix/x86-family/x86-family.h | 32 +- sortix/x86/interrupt.s | 161 ++- sortix/x86/kthread.s | 104 ++ sortix/x86/memorymanagement.cpp | 60 +- sortix/x86/process.cpp | 40 +- sortix/x86/syscall.s | 121 +-- sortix/x86/thread.cpp | 119 ++- sortix/x86/x86.h | 29 +- 55 files changed, 2615 insertions(+), 2311 deletions(-) delete mode 100644 libmaxsi/include/libmaxsi/signal.h delete mode 100644 libmaxsi/include/libmaxsi/signalnum.h delete mode 100644 sortix/event.cpp delete mode 100644 sortix/event.h create mode 100644 sortix/include/sortix/signal.h create mode 100644 sortix/x64/kthread.s create mode 100644 sortix/x86/kthread.s diff --git a/libmaxsi/error.cpp b/libmaxsi/error.cpp index 9b6fbc21..307ff24e 100644 --- a/libmaxsi/error.cpp +++ b/libmaxsi/error.cpp @@ -1,6 +1,6 @@ /****************************************************************************** - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of LibMaxsi. @@ -37,12 +37,9 @@ extern "C" { int global_errno = 0; } extern "C" { errno_location_func_t errno_location_func = NULL; } #ifndef SORTIX_KERNEL -DEFN_SYSCALL1(int, SysRegisterErrno, SYSCALL_REGISTER_ERRNO, int*); - extern "C" void init_error_functions() { global_errno = 0; - SysRegisterErrno(&global_errno); } #endif diff --git a/libmaxsi/include/libmaxsi/signal.h b/libmaxsi/include/libmaxsi/signal.h deleted file mode 100644 index e0b8e760..00000000 --- a/libmaxsi/include/libmaxsi/signal.h +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** - - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. - - This file is part of LibMaxsi. - - LibMaxsi is free software: you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. - - LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. - - You should have received a copy of the GNU Lesser General Public License - along with LibMaxsi. If not, see . - - signal.h - Handles the good old unix signals. - -******************************************************************************/ - -#ifndef LIBMAXSI_SIGNAL_H -#define LIBMAXSI_SIGNAL_H - -#include "signalnum.h" - -namespace Maxsi -{ - namespace Signal - { - typedef void (*Handler)(int); - - void Init(); - Handler RegisterHandler(int signum, Handler handler); - } -} - -#endif - diff --git a/libmaxsi/include/libmaxsi/signalnum.h b/libmaxsi/include/libmaxsi/signalnum.h deleted file mode 100644 index 5dc0085f..00000000 --- a/libmaxsi/include/libmaxsi/signalnum.h +++ /dev/null @@ -1,71 +0,0 @@ -/****************************************************************************** - - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. - - This file is part of LibMaxsi. - - LibMaxsi is free software: you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. - - LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. - - You should have received a copy of the GNU Lesser General Public License - along with LibMaxsi. If not, see . - - signalnum.h - Declares the signal-related constants. - -******************************************************************************/ - -#ifndef LIBMAXSI_SIGNALNUM_H -#define LIBMAXSI_SIGNALNUM_H - -namespace Maxsi -{ - namespace Signal - { - const int HUP = 1; /* Hangup */ - const int INT = 2; /* Interrupt */ - const int QUIT = 3; /* Quit */ - const int ILL = 4; /* Illegal Instruction */ - const int TRAP = 5; /* Trace/Breakpoint Trap */ - const int ABRT = 6; /* Abort */ - const int EMT = 7; /* Emulation Trap */ - const int FPE = 8; /* Arithmetic Exception */ - const int KILL = 9; /* Killed */ - const int BUS = 10; /* Bus Error */ - const int SEGV = 11; /* Segmentation Fault */ - const int SYS = 12; /* Bad System Call */ - const int PIPE = 13; /* Broken Pipe */ - const int ALRM = 14; /* Alarm Clock */ - const int TERM = 15; /* Terminated */ - const int USR1 = 16; /* User Signal 1 */ - const int USR2 = 17; /* User Signal 2 */ - const int CHLD = 18; /* Child Status */ - const int PWR = 19; /* Power Fail/Restart */ - const int WINCH = 20; /* Window Size Change */ - const int URG = 21; /* Urgent Socket Condition */ - const int STOP = 23; /* Stopped (signal) */ - const int TSTP = 24; /* Stopped (user) */ - const int CONT = 25; /* Continued */ - const int TTIN = 26; /* Stopped (tty input) */ - const int TTOU = 27; /* Stopped (tty output) */ - const int VTALRM = 28; /* Virtual Timer Expired */ - const int XCPU = 30; /* CPU time limit exceeded */ - const int XFSZ = 31; /* File size limit exceeded */ - const int WAITING = 32; /* All LWPs blocked */ - const int LWP = 33; /* Virtual Interprocessor Interrupt for Threads Library */ - const int AIO = 34; /* Asynchronous I/O */ - const int NUMSIGNALS = 35; - - typedef void (*Handler)(int); - } -} - -#endif - diff --git a/libmaxsi/include/libmaxsi/syscall.h b/libmaxsi/include/libmaxsi/syscall.h index adca9c63..71c4eeed 100644 --- a/libmaxsi/include/libmaxsi/syscall.h +++ b/libmaxsi/include/libmaxsi/syscall.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of LibMaxsi. @@ -11,8 +11,8 @@ LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . @@ -20,7 +20,7 @@ syscall.h Assembly stubs for declaring system calls in libmaxsi. -******************************************************************************/ +*******************************************************************************/ #ifdef SORTIX_KERNEL #warning === @@ -32,6 +32,7 @@ #define LIBMAXSI_SYSCALL_H #include +#include namespace Maxsi { @@ -48,7 +49,10 @@ namespace Maxsi inline type fn() \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -56,7 +60,10 @@ namespace Maxsi inline type fn(P1 p1) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -64,7 +71,10 @@ namespace Maxsi inline type fn(P1 p1, P2 p2) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -72,7 +82,10 @@ namespace Maxsi inline type fn(P1 p1, P2 p2, P3 p3) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -80,15 +93,21 @@ namespace Maxsi inline type fn(P1 p1, P2 p2, P3 p3, P4 p4) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4)); \ - return a; \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ + return a; \ } #define DEFN_SYSCALL5(type, fn, num, P1, P2, P3, P4, P5) \ inline type fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4), "S" ((size_t)p5)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -96,14 +115,20 @@ namespace Maxsi inline void fn() \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL1_VOID(fn, num, P1) \ inline void fn(P1 p1) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL2_VOID(fn, num, P1, P2) \ @@ -111,27 +136,38 @@ namespace Maxsi { \ size_t a; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL3_VOID(fn, num, P1, P2, P3) \ inline void fn(P1 p1, P2 p2, P3 p3) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL4_VOID(fn, num, P1, P2, P3, P4) \ inline void fn(P1 p1, P2 p2, P3 p3, P4 p4) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL5_VOID(fn, num, P1, P2, P3, P4, P5) \ inline void fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4), "S" ((size_t)p5)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #else @@ -143,7 +179,10 @@ namespace Maxsi type fn() \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -151,7 +190,10 @@ namespace Maxsi type fn(P1) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -159,7 +201,10 @@ namespace Maxsi type fn(P1, P2) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -167,7 +212,10 @@ namespace Maxsi type fn(P1, P2, P3) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -175,7 +223,10 @@ namespace Maxsi type fn(P1, P2, P3, P4) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -183,7 +234,10 @@ namespace Maxsi type fn(P1, P2, P3, P4, P5) \ { \ type a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ return a; \ } @@ -191,42 +245,60 @@ namespace Maxsi void fn() \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL1_VOID(fn, num, P1) \ void fn(P1) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL2_VOID(fn, num, P1, P2) \ void fn(P1, P2) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL3_VOID(fn, num, P1, P2, P3) \ void fn(P1, P2, P3) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL4_VOID(fn, num, P1, P2, P3, P4) \ void fn(P1, P2, P3, P4) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #define DEFN_SYSCALL5_VOID(fn, num, P1, P2, P3, P4, P5) \ void fn(P1, P2, P3, P4, P5) \ { \ size_t a; \ + int reterrno; \ asm volatile("int $0x80" : "=a" (a) : "0" (num)); \ + asm volatile("movl %%edx, %0" : "=r"(reterrno)); \ + if ( reterrno ) { errno = reterrno; } \ } #endif diff --git a/libmaxsi/include/signal.h b/libmaxsi/include/signal.h index 80b76f2d..af317428 100644 --- a/libmaxsi/include/signal.h +++ b/libmaxsi/include/signal.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of LibMaxsi. @@ -11,8 +11,8 @@ LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . @@ -20,7 +20,7 @@ signal.h Signals. -******************************************************************************/ +*******************************************************************************/ /* TODO: This does not fully implement POSIX 2008-1 yet! */ @@ -28,42 +28,10 @@ #define _SIGNAL_H 1 #include +#include __BEGIN_DECLS -#define SIGHUP 1 /* Hangup */ -#define SIGINT 2 /* Interrupt */ -#define SIGQUIT 3 /* Quit */ -#define SIGILL 4 /* Illegal Instruction */ -#define SIGTRAP 5 /* Trace/Breakpoint Trap */ -#define SIGABRT 6 /* Abort */ -#define SIGEMT 7 /* Emulation Trap */ -#define SIGFPE 8 /* Arithmetic Exception */ -#define SIGKILL 9 /* Killed */ -#define SIGBUS 10 /* Bus Error */ -#define SIGSEGV 11 /* Segmentation Fault */ -#define SIGSYS 12 /* Bad System Call */ -#define SIGPIPE 13 /* Broken Pipe */ -#define SIGALRM 14 /* Alarm Clock */ -#define SIGTERM 15 /* Terminated */ -#define SIGUSR1 16 /* User Signal 1 */ -#define SIGUSR2 17 /* User Signal 2 */ -#define SIGCHLD 18 /* Child Status */ -#define SIGPWR 19 /* Power Fail/Restart */ -#define SIGWINCH 20 /* Window Size Change */ -#define SIGURG 21 /* Urgent Socket Condition */ -#define SIGSTOP 23 /* Stopped (signal) */ -#define SIGTSTP 24 /* Stopped (user) */ -#define SIGCONT 25 /* Continued */ -#define SIGTTIN 26 /* Stopped (tty input) */ -#define SIGTTOU 27 /* Stopped (tty output) */ -#define SIGVTALRM 28 /* Virtual Timer Expired */ -#define SIGXCPU 30 /* CPU time limit exceeded */ -#define SIGXFSZ 31 /* File size limit exceeded */ -#define SIGWAITING 32 /* All LWPs blocked */ -#define SIGLWP 33 /* Virtual Interprocessor Interrupt for Threads Library */ -#define SIGAIO 34 /* Asynchronous I/O */ - @include(pid_t.h); typedef void (*sighandler_t)(int); diff --git a/libmaxsi/init.cpp b/libmaxsi/init.cpp index dd742aed..d8b96920 100644 --- a/libmaxsi/init.cpp +++ b/libmaxsi/init.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of LibMaxsi. @@ -11,20 +11,19 @@ LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . init.cpp - Initializes the process by setting up the heap, signal handling, - static memory and other useful things. + Initializes the process by setting up the heap, signal handling, static + memory and other useful things. -******************************************************************************/ +*******************************************************************************/ #include -#include #include #include #include @@ -36,6 +35,7 @@ namespace Maxsi extern "C" void init_error_functions(); extern "C" void init_stdio(); + extern "C" void init_signal(); extern "C" void initialize_standard_library(int argc, char* argv[]) { @@ -46,7 +46,7 @@ namespace Maxsi init_error_functions(); // It's probably best to initialize the Unix signals early on. - Signal::Init(); + init_signal(); // Initialize the dynamic heap. Memory::Init(); diff --git a/libmaxsi/signal.cpp b/libmaxsi/signal.cpp index 5feb3841..19bf3ad3 100644 --- a/libmaxsi/signal.cpp +++ b/libmaxsi/signal.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of LibMaxsi. @@ -11,8 +11,8 @@ LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -34,6 +33,8 @@ namespace Maxsi { namespace Signal { + typedef void (*Handler)(int); + void Core(int signum) { Process::Exit(128 + signum); @@ -41,33 +42,33 @@ namespace Maxsi extern "C" void SIG_DFL(int signum) { - if ( signum == Signal::HUP ) { Process::Exit(128 + signum); } else - if ( signum == Signal::INT ) { Process::Exit(128 + signum); } else - if ( signum == Signal::QUIT ) { Core(signum); } else - if ( signum == Signal::TRAP ) { Core(signum); } else - if ( signum == Signal::ABRT ) { Core(signum); } else - if ( signum == Signal::EMT ) { Core(signum); } else - if ( signum == Signal::FPE ) { Core(signum); } else - if ( signum == Signal::KILL ) { Process::Exit(128 + signum); } else - if ( signum == Signal::BUS ) { Core(signum); } else - if ( signum == Signal::SEGV ) { Core(signum); } else - if ( signum == Signal::SYS ) { Core(signum); } else - if ( signum == Signal::PIPE ) { Process::Exit(128 + signum); } else - if ( signum == Signal::ALRM ) { Process::Exit(128 + signum); } else - if ( signum == Signal::TERM ) { Process::Exit(128 + signum); } else - if ( signum == Signal::USR1 ) { Process::Exit(128 + signum); } else - if ( signum == Signal::USR2 ) { Process::Exit(128 + signum); } else - if ( signum == Signal::CHLD ) { /* Ignore this signal. */ } else - if ( signum == Signal::PWR ) { /* Ignore this signal. */ } else - if ( signum == Signal::WINCH ) { /* Ignore this signal. */ } else - if ( signum == Signal::URG ) { /* Ignore this signal. */ } else - if ( signum == Signal::CONT ) { /* Ignore this signal. */ } else - if ( signum == Signal::VTALRM ) { /* Ignore this signal. */ } else - if ( signum == Signal::XCPU ) { Core(signum); } else - if ( signum == Signal::XFSZ ) { Core(signum); } else - if ( signum == Signal::WAITING ) { /* Ignore this signal. */ } else - if ( signum == Signal::LWP ) { /* Ignore this signal. */ } else - if ( signum == Signal::AIO ) { /* Ignore this signal. */ } else + if ( signum == SIGHUP ) { Process::Exit(128 + signum); } else + if ( signum == SIGINT ) { Process::Exit(128 + signum); } else + if ( signum == SIGQUIT ) { Core(signum); } else + if ( signum == SIGTRAP ) { Core(signum); } else + if ( signum == SIGABRT ) { Core(signum); } else + if ( signum == SIGEMT ) { Core(signum); } else + if ( signum == SIGFPE ) { Core(signum); } else + if ( signum == SIGKILL ) { Process::Exit(128 + signum); } else + if ( signum == SIGBUS ) { Core(signum); } else + if ( signum == SIGSEGV ) { Core(signum); } else + if ( signum == SIGSYS ) { Core(signum); } else + if ( signum == SIGPIPE ) { Process::Exit(128 + signum); } else + if ( signum == SIGALRM ) { Process::Exit(128 + signum); } else + if ( signum == SIGTERM ) { Process::Exit(128 + signum); } else + if ( signum == SIGUSR1 ) { Process::Exit(128 + signum); } else + if ( signum == SIGUSR2 ) { Process::Exit(128 + signum); } else + if ( signum == SIGCHLD ) { /* Ignore this signal. */ } else + if ( signum == SIGPWR ) { /* Ignore this signal. */ } else + if ( signum == SIGWINCH ) { /* Ignore this signal. */ } else + if ( signum == SIGURG ) { /* Ignore this signal. */ } else + if ( signum == SIGCONT ) { /* Ignore this signal. */ } else + if ( signum == SIGVTALRM ) { /* Ignore this signal. */ } else + if ( signum == SIGXCPU ) { Core(signum); } else + if ( signum == SIGXFSZ ) { Core(signum); } else + if ( signum == SIGWAITING ) { /* Ignore this signal. */ } else + if ( signum == SIGLWP ) { /* Ignore this signal. */ } else + if ( signum == SIGAIO ) { /* Ignore this signal. */ } else { /* Ignore this signal. */ } } @@ -94,10 +95,10 @@ namespace Maxsi } DEFN_SYSCALL1_VOID(SysRegisterSignalHandler, SYSCALL_REGISTER_SIGNAL_HANDLER, sighandler_t); - DEFN_SYSCALL0_VOID(SysSigReturn, SYSCALL_SIGRETURN); DEFN_SYSCALL2(int, SysKill, SYSCALL_KILL, pid_t, int); + DEFN_SYSCALL1(int, SysRaise, SYSCALL_RAISE, int); - void Init() + extern "C" void init_signal() { for ( int i = 0; i < MAX_SIGNALS; i++ ) { @@ -126,7 +127,7 @@ namespace Maxsi extern "C" int raise(int signum) { - kill(getpid(), signum); + return SysRaise(signum); } } } diff --git a/libmaxsi/x64/signal.s b/libmaxsi/x64/signal.s index 5d4bd395..6c15df82 100644 --- a/libmaxsi/x64/signal.s +++ b/libmaxsi/x64/signal.s @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of LibMaxsi. @@ -11,8 +11,8 @@ LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . @@ -32,7 +32,5 @@ SignalHandlerAssembly: # The kernel put the signal id in edi. call SignalHandler - # Return control to the kernel, so normal computation can resume normally. - movl $30, %eax # SysSigReturn - int $0x80 - + # Return control to the kernel, so normal execution can continue. + int $131 diff --git a/libmaxsi/x86/signal.s b/libmaxsi/x86/signal.s index aa311db4..acc1ad95 100644 --- a/libmaxsi/x86/signal.s +++ b/libmaxsi/x86/signal.s @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of LibMaxsi. @@ -11,8 +11,8 @@ LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . @@ -20,7 +20,7 @@ signal.s An assembly stub that for handling unix signals. -******************************************************************************/ +*******************************************************************************/ .globl SignalHandlerAssembly @@ -36,8 +36,5 @@ SignalHandlerAssembly: # Restore the stack as it was. addl $4, %esp - # Now that the stack is intact, return control to the kernel, so normal - # computation can resume normally. - movl $30, %eax # SysSigReturn - int $0x80 - + # Return control to the kernel, so normal execution can continue. + int $131 diff --git a/sortix/Makefile b/sortix/Makefile index eb99894f..c22e2ab9 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -101,6 +101,7 @@ memorymanagement.o \ calltrace.o \ $(CPU)/calltrace.o \ kthread.o \ +$(CPU)/kthread.o \ interlock.o \ $(CPU)/interlock.o \ panic.o \ @@ -132,7 +133,6 @@ elf.o \ process.o \ initrd.o \ thread.o \ -event.o \ io.o \ pipe.o \ filesystem.o \ diff --git a/sortix/com.cpp b/sortix/com.cpp index 7523f200..6ecaee84 100644 --- a/sortix/com.cpp +++ b/sortix/com.cpp @@ -26,10 +26,10 @@ #include #include #include "interrupt.h" -#include "event.h" #include "stream.h" #include "syscall.h" #include "thread.h" +#include "signal.h" #include "fs/devfs.h" #include "com.h" diff --git a/sortix/cpu.h b/sortix/cpu.h index 1aa6cd5d..7c134f09 100644 --- a/sortix/cpu.h +++ b/sortix/cpu.h @@ -35,4 +35,17 @@ #include "x64/x64.h" #endif +namespace Sortix { +namespace CPU { + +extern "C" void load_registers(InterruptRegisters* regs, size_t size) SORTIX_NORETURN; +inline void LoadRegisters(InterruptRegisters* regs) SORTIX_NORETURN; +inline void LoadRegisters(InterruptRegisters* regs) +{ + load_registers(regs, sizeof(*regs)); +} + +} // namespace CPU +} // namespace CPU + #endif diff --git a/sortix/event.cpp b/sortix/event.cpp deleted file mode 100644 index 41f12c6f..00000000 --- a/sortix/event.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/****************************************************************************** - - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . - - event.cpp - Each thread can wait for an event to happen and be signaled when it does. - -******************************************************************************/ - -#include -#include "thread.h" -#include "syscall.h" -#include "event.h" - -namespace Sortix -{ - Event::Event() - { - waiting = NULL; - } - - Event::~Event() - { - if ( waiting ) - { - Panic("Thread was waiting on event, but it went out of scope"); - } - } - - void Event::Register() - { - Thread* thread = CurrentThread(); - if ( thread->event ) - { - Panic("Thread tried to wait on an event, but was already waiting"); - } - thread->event = this; - thread->eventnextwaiting = waiting; - waiting = thread; - } - - void Event::Signal() - { - while ( waiting ) - { - waiting->event = NULL; - Syscall::ScheduleResumption(waiting); - waiting = waiting->eventnextwaiting; - } - } - - // TODO: Okay, I realize this is O(N), refactor this to a linked list. - void Event::Unregister(Thread* thread) - { - if ( thread->event != this ) { return; } - thread->event = NULL; - if ( waiting == thread ) { waiting = thread->eventnextwaiting; return; } - for ( Thread* tmp = waiting; tmp; tmp = tmp->eventnextwaiting ) - { - if ( tmp->eventnextwaiting == thread ) - { - tmp->eventnextwaiting = thread->eventnextwaiting; - break; - } - } - thread->eventnextwaiting = NULL; - } -} - diff --git a/sortix/event.h b/sortix/event.h deleted file mode 100644 index 6e7a8481..00000000 --- a/sortix/event.h +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** - - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . - - event.h - Each thread can wait for an event to happen and be signaled when it does. - -******************************************************************************/ - -#ifndef SORTIX_EVENT_H -#define SORTIX_EVENT_H - -namespace Sortix -{ - class Thread; - - class Event - { - public: - Event(); - ~Event(); - - public: - void Register(); - void Signal(); - void Unregister(Thread* thread); - - private: - Thread* waiting; - - }; -} - -#endif - diff --git a/sortix/include/sortix/kernel/kthread.h b/sortix/include/sortix/kernel/kthread.h index 20c992f5..a808ae2d 100644 --- a/sortix/include/sortix/kernel/kthread.h +++ b/sortix/include/sortix/kernel/kthread.h @@ -18,29 +18,39 @@ Sortix. If not, see . kthread.h - Fake header providing noop threading functions. This is simply forward - compatibility with the upcoming kthread branch and to ease merging. + Utility and synchronization mechanisms for kernel threads. *******************************************************************************/ #ifndef SORTIX_KTHREAD_H #define SORTIX_KTHREAD_H -#define GOT_FAKE_KTHREAD -#warning Using noop kthread functions +#include +#include "../../../signal.h" + +#define GOT_ACTUAL_KTHREAD namespace Sortix { extern "C" { +inline static void kthread_yield(void) { asm volatile ("int $129"); } +void kthread_exit(void* param = NULL) SORTIX_NORETURN; typedef unsigned kthread_mutex_t; const kthread_mutex_t KTHREAD_MUTEX_INITIALIZER = 0; unsigned kthread_mutex_trylock(kthread_mutex_t* mutex); void kthread_mutex_lock(kthread_mutex_t* mutex); unsigned long kthread_mutex_lock_signal(kthread_mutex_t* mutex); void kthread_mutex_unlock(kthread_mutex_t* mutex); -typedef unsigned kthread_cond_t; -const kthread_cond_t KTHREAD_COND_INITIALIZER = 0; +struct kthread_cond_elem; +typedef struct kthread_cond_elem kthread_cond_elem_t; +struct kthread_cond +{ + kthread_cond_elem_t* first; + kthread_cond_elem_t* last; +}; +typedef struct kthread_cond kthread_cond_t; +const kthread_cond_t KTHREAD_COND_INITIALIZER = { NULL, NULL }; void kthread_cond_wait(kthread_cond_t* cond, kthread_mutex_t* mutex); unsigned long kthread_cond_wait_signal(kthread_cond_t* cond, kthread_mutex_t* mutex); void kthread_cond_signal(kthread_cond_t* cond); @@ -74,6 +84,7 @@ public: { this->mutex = mutex; this->acquired = kthread_mutex_lock_signal(mutex); + ASSERT(acquired || Signal::IsPending()); } ~ScopedLockSignal() diff --git a/sortix/include/sortix/kernel/memorymanagement.h b/sortix/include/sortix/kernel/memorymanagement.h index 218a70a6..d02f5e12 100644 --- a/sortix/include/sortix/kernel/memorymanagement.h +++ b/sortix/include/sortix/kernel/memorymanagement.h @@ -65,7 +65,9 @@ namespace Sortix addr_t Fork(); addr_t GetAddressSpace(); addr_t SwitchAddressSpace(addr_t addrspace); - void DestroyAddressSpace(); + void DestroyAddressSpace(addr_t fallback = 0, + void (*func)(addr_t, void*) = NULL, + void* user = NULL); bool Map(addr_t physical, addr_t mapto, int prot); addr_t Unmap(addr_t mapto); addr_t Physical(addr_t mapto); diff --git a/sortix/include/sortix/signal.h b/sortix/include/sortix/signal.h new file mode 100644 index 00000000..07a449c6 --- /dev/null +++ b/sortix/include/sortix/signal.h @@ -0,0 +1,79 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + signal.h + Defines the numeric values for the various supported signals. + +*******************************************************************************/ + +#ifndef SORTIX_INCLUDE_SIGNAL_H +#define SORTIX_INCLUDE_SIGNAL_H + +/* TODO: Yes, signals are implemented in a non-standard manner for now. */ + +#include + +__BEGIN_DECLS + +#define SIGHUP 1 /* Hangup */ +#define SIGINT 2 /* Interrupt */ +#define SIGQUIT 3 /* Quit */ +#define SIGILL 4 /* Illegal Instruction */ +#define SIGTRAP 5 /* Trace/Breakpoint Trap */ +#define SIGABRT 6 /* Abort */ +#define SIGEMT 7 /* Emulation Trap */ +#define SIGFPE 8 /* Arithmetic Exception */ +#define SIGKILL 9 /* Killed */ +#define SIGBUS 10 /* Bus Error */ +#define SIGSEGV 11 /* Segmentation Fault */ +#define SIGSYS 12 /* Bad System Call */ +#define SIGPIPE 13 /* Broken Pipe */ +#define SIGALRM 14 /* Alarm Clock */ +#define SIGTERM 15 /* Terminated */ +#define SIGUSR1 16 /* User Signal 1 */ +#define SIGUSR2 17 /* User Signal 2 */ +#define SIGCHLD 18 /* Child Status */ +#define SIGPWR 19 /* Power Fail/Restart */ +#define SIGWINCH 20 /* Window Size Change */ +#define SIGURG 21 /* Urgent Socket Condition */ +#define SIGSTOP 23 /* Stopped (signal) */ +#define SIGTSTP 24 /* Stopped (user) */ +#define SIGCONT 25 /* Continued */ +#define SIGTTIN 26 /* Stopped (tty input) */ +#define SIGTTOU 27 /* Stopped (tty output) */ +#define SIGVTALRM 28 /* Virtual Timer Expired */ +#define SIGXCPU 30 /* CPU time limit exceeded */ +#define SIGXFSZ 31 /* File size limit exceeded */ +#define SIGWAITING 32 /* All LWPs blocked */ +#define SIGLWP 33 /* Virtual Interprocessor Interrupt for Threads Library */ +#define SIGAIO 34 /* Asynchronous I/O */ +#define SIG__NUM_DECLARED 35 +#define SIG_MAX_NUM 128 + +#define SIG_PRIO_NORMAL 0 +#define SIG_PRIO_HIGH 1 +#define SIG_PRIO_STOP 2 +#define SIG_PRIO_CORE 3 +#define SIG_PRIO_KILL 4 +#define SIG_NUM_LEVELS 5 + +__END_DECLS + +#endif + diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index ab219adc..9ae864de 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -52,7 +52,6 @@ #define SYSCALL_UNLINK 27 #define SYSCALL_REGISTER_ERRNO 28 #define SYSCALL_REGISTER_SIGNAL_HANDLER 29 -#define SYSCALL_SIGRETURN 30 #define SYSCALL_KILL 31 #define SYSCALL_MEMSTAT 32 #define SYSCALL_ISATTY 33 @@ -75,7 +74,8 @@ #define SYSCALL_PWRITE 50 #define SYSCALL_SFORKR 51 #define SYSCALL_TCGETWINSIZE 52 -#define SYSCALL_MAX_NUM 53 /* index of highest constant + 1 */ +#define SYSCALL_RAISE 53 +#define SYSCALL_MAX_NUM 54 /* index of highest constant + 1 */ #endif diff --git a/sortix/interrupt.cpp b/sortix/interrupt.cpp index 3bb01d81..ecd51cc7 100644 --- a/sortix/interrupt.cpp +++ b/sortix/interrupt.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -23,14 +23,16 @@ *******************************************************************************/ #include +#include +#include #include "x86-family/idt.h" #include "interrupt.h" +#include "scheduler.h" +#include "syscall.h" -#include "process.h" // Hack for SIGSEGV #include "sound.h" // Hack for SIGSEGV -#include "thread.h" // HACK FOR SIGSEGV -#include "syscall.h" // HACK FOR SIGSEGV -#include "scheduler.h" // HACK FOR SIGSEGV + +using namespace Maxsi; namespace Sortix { void SysExit(int status); // HACK @@ -53,8 +55,10 @@ const uint8_t PIC_MODE_BUF_SLAVE = 0x08; // Buffered mode/slave const uint8_t PIC_MODE_BUF_MASTER = 0x0C; // Buffered mode/master const uint8_t PIC_MODE_SFNM = 0x10; // Special fully nested (not) +extern "C" { unsigned long asm_is_cpu_interrupted = 0; } const bool DEBUG_EXCEPTION = false; const bool DEBUG_IRQ = false; +const bool DEBUG_ISR = false; bool initialized; const size_t NUM_KNOWN_EXCEPTIONS = 20; @@ -237,12 +241,20 @@ void CrashHandler(CPU::InterruptRegisters* regs) Sound::Mute(); - CurrentProcess()->Exit(139); - Scheduler::ProcessTerminated(regs); + // Exit the process with the right error code. + // TODO: Sent a SIGINT, SIGBUS, or whatever instead. + SysExit(139); } void ISRHandler(Sortix::CPU::InterruptRegisters* regs) { + if ( DEBUG_ISR ) + { + Log::PrintF("ISR%u ", regs->int_no); + regs->LogRegisters(); + Log::Print("\n"); + } + if ( regs->int_no < 32 ) { CrashHandler(regs); @@ -289,6 +301,109 @@ extern "C" void interrupt_handler(Sortix::CPU::InterruptRegisters* regs) else { ISRHandler(regs); } } +// TODO: This implementation is a bit hacky and can be optimized. + +uint8_t* queue; +uint8_t* storage; +volatile size_t queueoffset; +volatile size_t queueused; +size_t queuesize; + +struct Package +{ + size_t size; + size_t payloadoffset; + size_t payloadsize; + WorkHandler handler; // TODO: May not be correctly aligned on some systems. + uint8_t payload[0]; +}; + +void InitWorker() +{ + const size_t QUEUE_SIZE = 4UL*1024UL; + STATIC_ASSERT(QUEUE_SIZE % sizeof(Package) == 0); + queue = new uint8_t[QUEUE_SIZE]; + if ( !queue ) { Panic("Can't allocate interrupt worker queue"); } + storage = new uint8_t[QUEUE_SIZE]; + if ( !storage ) { Panic("Can't allocate interrupt worker storage"); } + queuesize = QUEUE_SIZE; + queueoffset = 0; + queueused = 0; +} + +static void WriteToQueue(const void* src, size_t size) +{ + const uint8_t* buf = (const uint8_t*) src; + size_t writeat = (queueoffset + queueused) % queuesize; + size_t available = queuesize - writeat; + size_t count = available < size ? available : size; + Memory::Copy(queue + writeat, buf, count); + queueused += count; + if ( count < size ) { WriteToQueue(buf + count, size - count); } +} + +static void ReadFromQueue(void* dest, size_t size) +{ + uint8_t* buf = (uint8_t*) dest; + size_t available = queuesize - queueoffset; + size_t count = available < size ? available : size; + Memory::Copy(buf, queue + queueoffset, count); + queueused -= count; + queueoffset = (queueoffset + count) % queuesize; + if ( count < size ) { ReadFromQueue(buf + count, size - count); } +} + +static Package* PopPackage(uint8_t** payloadp, Package* /*prev*/) +{ + Package* package = NULL; + uint8_t* payload = NULL; + Interrupt::Disable(); + + if ( !queueused ) { goto out; } + + package = (Package*) storage; + ReadFromQueue(package, sizeof(*package)); + payload = storage + sizeof(*package); + ReadFromQueue(payload, package->payloadsize); + *payloadp = payload; + +out: + Interrupt::Enable(); + return package; +} + +void WorkerThread(void* /*user*/) +{ + ASSERT(Interrupt::IsEnabled()); + uint8_t* payload = NULL; + Package* package = NULL; + while ( true ) + { + package = PopPackage(&payload, package); + if ( !package ) { Scheduler::Yield(); continue; } + size_t payloadsize = package->payloadsize; + package->handler(payload, payloadsize); + } +} + +bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize) +{ + ASSERT(!Interrupt::IsEnabled()); + + Package package; + package.size = sizeof(package) + payloadsize; + package.payloadoffset = 0; // Currently unused + package.payloadsize = payloadsize; + package.handler = handler; + + size_t queuefreespace = queuesize - queueused; + if ( queuefreespace < package.size ) { return false; } + + WriteToQueue(&package, sizeof(package)); + WriteToQueue(payload, payloadsize); + return true; +} + } // namespace Interrupt } // namespace Sortix diff --git a/sortix/interrupt.h b/sortix/interrupt.h index 64769973..8f93d77f 100644 --- a/sortix/interrupt.h +++ b/sortix/interrupt.h @@ -1,6 +1,6 @@ /******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -48,10 +48,12 @@ const unsigned IRQ14 = 46; const unsigned IRQ15 = 47; extern "C" unsigned long asm_interrupts_are_enabled(); +extern "C" unsigned long asm_is_cpu_interrupted; inline bool IsEnabled() { return asm_interrupts_are_enabled(); } inline void Enable() { asm volatile("sti"); } inline void Disable() { asm volatile("cli"); } +inline bool IsCPUInterrupted() { return asm_is_cpu_interrupted != 0; } inline bool SetEnabled(bool isenabled) { bool wasenabled = IsEnabled(); @@ -66,6 +68,11 @@ typedef void (*RawHandler)(void); void RegisterRawHandler(unsigned index, RawHandler handler, bool userspace); void Init(); +void InitWorker(); +void WorkerThread(void* user); + +typedef void(*WorkHandler)(void* payload, size_t payloadsize); +bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize); } // namespace Interrupt } // namespace Sortix @@ -103,6 +110,8 @@ extern "C" void isr29(); extern "C" void isr30(); extern "C" void isr31(); extern "C" void isr128(); +extern "C" void isr130(); +extern "C" void isr131(); extern "C" void irq0(); extern "C" void irq1(); extern "C" void irq2(); diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 41fac5e1..81752840 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -24,16 +24,19 @@ *******************************************************************************/ #include +#include +#include +#include #include #include #include #include +#include +#include #include #include #include -#include -#include -#include +#include #include "kernelinfo.h" #include "x86-family/gdt.h" #include "time.h" @@ -43,6 +46,7 @@ #include "thread.h" #include "process.h" #include "scheduler.h" +#include "signal.h" #include "syscall.h" #include "ata.h" #include "com.h" @@ -67,7 +71,8 @@ using namespace Maxsi; // Keep the stack size aligned with $CPU/base.s -extern "C" { size_t stack[64*1024 / sizeof(size_t)] = {0}; } +const size_t STACK_SIZE = 64*1024; +extern "C" { size_t stack[STACK_SIZE / sizeof(size_t)] = {0}; } namespace Sortix { @@ -97,6 +102,11 @@ void DoWelcome() Log::Print(" BOOTING OPERATING SYSTEM... "); } +// Forward declarations. +static void BootThread(void* user); +static void InitThread(void* user); +static void SystemIdleThread(void* user); + static size_t PrintToTextTerminal(void* user, const char* str, size_t len) { return ((TextTerminal*) user)->Print(str, len); @@ -146,7 +156,7 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) addr_t initrd = NULL; size_t initrdsize = 0; - uint32_t* modules = (uint32_t*) bootinfo->mods_addr; + uint32_t* modules = (uint32_t*) (addr_t) bootinfo->mods_addr; for ( uint32_t i = 0; i < bootinfo->mods_count; i++ ) { initrdsize = modules[2*i+1] - modules[2*i+0]; @@ -170,6 +180,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // Initialize the kernel heap. Maxsi::Memory::Init(); + // Initialize the interrupt worker. + Interrupt::InitWorker(); + // Initialize the list of kernel devices. DeviceFS::Init(); @@ -212,6 +225,12 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // Initialize the scheduler. Scheduler::Init(); + // Initialize Unix Signals. + Signal::Init(); + + // Initialize the worker thread data structures. + Worker::Init(); + // Initialize the kernel information query syscall. Info::Init(); @@ -230,59 +249,139 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // Initialize the BGA driver. BGA::Init(); - // Alright, now the system's drivers are loaded and initialized. It is - // time to load the initial user-space programs and start execution of - // the actual operating system. + // Now that the base system has been loaded, it's time to go threaded. First + // we create an object that represents this thread. + Process* system = new Process; + if ( !system ) { Panic("Could not allocate the system process"); } + addr_t systemaddrspace = Memory::GetAddressSpace(); + system->addrspace = systemaddrspace; - uint32_t inode; - byte* program; - size_t programsize; - - // Create an address space for the idle process. - addr_t idleaddrspace = Memory::Fork(); - if ( !idleaddrspace ) { Panic("could not fork an idle process address space"); } - - // Create an address space for the initial process. - addr_t initaddrspace = Memory::Fork(); - if ( !initaddrspace ) { Panic("could not fork an initial process address space"); } - - // Create the system idle process. - Process* idle = new Process; - if ( !idle ) { Panic("could not allocate idle process"); } - idle->addrspace = idleaddrspace; - Memory::SwitchAddressSpace(idleaddrspace); - Scheduler::SetDummyThreadOwner(idle); - inode = InitRD::Traverse(InitRD::Root(), "idle"); - if ( inode == NULL ) { PanicF("initrd did not contain 'idle'"); } - program = InitRD::Open(inode, &programsize); - if ( program == NULL ) { PanicF("initrd did not contain 'idle'"); } - addr_t idlestart = ELF::Construct(idle, program, programsize); - if ( !idlestart ) { Panic("could not construct ELF image for idle process"); } - Thread* idlethread = CreateThread(idlestart); - if ( !idlethread ) { Panic("could not create thread for the idle process"); } + // We construct this thread manually for bootstrap reasons. We wish to + // create a kernel thread that is the current thread and isn't put into the + // scheduler's set of runnable threads, but rather run whenever there is + // _nothing_ else to run on this CPU. + Thread* idlethread = new Thread; + idlethread->process = system; + idlethread->kernelstackpos = (addr_t) stack; + idlethread->kernelstacksize = STACK_SIZE; + idlethread->kernelstackmalloced = false; + system->firstthread = idlethread; Scheduler::SetIdleThread(idlethread); - // Create the initial process. - Process* init = new Process; - if ( !init ) { Panic("could not allocate init process"); } - init->addrspace = initaddrspace; - Memory::SwitchAddressSpace(initaddrspace); - Scheduler::SetDummyThreadOwner(init); - inode = InitRD::Traverse(InitRD::Root(), "init"); - if ( inode == NULL ) { PanicF("initrd did not contain 'init'"); } - program = InitRD::Open(inode, &programsize); - if ( program == NULL ) { PanicF("initrd did not contain 'init'"); } - addr_t initstart = ELF::Construct(init, program, programsize); - if ( !initstart ) { Panic("could not construct ELF image for init process"); } - Thread* initthread = CreateThread(initstart); - if ( !initthread ) { Panic("could not create thread for the init process"); } - Scheduler::SetInitProcess(init); + // Let's create a regular kernel thread that can decide what happens next. + // Note that we don't do the work here: should it block, then there is + // nothing to run. Therefore we must become the system idle thread. + RunKernelThread(BootThread, NULL); - // Lastly set up the timer driver and we are ready to run the OS. + // The time driver will run the scheduler on the next timer interrupt. Time::Init(); - // Run the OS. - Scheduler::MainLoop(); + // Become the system idle thread. + SystemIdleThread(NULL); +} + +static void SystemIdleThread(void* /*user*/) +{ + // Alright, we are now the system idle thread. If there is nothing to do, + // then we are run. Note that we must never do any real work here. + while(true); +} + +static void BootThread(void* /*user*/) +{ + // Hello, threaded world! You can now regard the kernel as a multi-threaded + // process with super-root access to the system. Before we boot the full + // system we need to start some worker threads. + + // Let's create the interrupt worker thread that executes additional work + // requested by interrupt handlers, where such work isn't safe. + Thread* interruptworker = RunKernelThread(Interrupt::WorkerThread, NULL); + if ( !interruptworker ) + Panic("Could not create interrupt worker"); + + // Create a general purpose worker thread. + Thread* workerthread = RunKernelThread(Worker::Thread, NULL); + if ( !workerthread ) + Panic("Unable to create general purpose worker thread"); + + // Finally, let's transfer control to a new kernel process that will + // eventually run user-space code known as the operating system. + addr_t initaddrspace = Memory::Fork(); + if ( !initaddrspace ) { Panic("Could not create init's address space"); } + + Process* init = new Process; + if ( !init ) { Panic("Could not allocate init process"); } + + CurrentProcess()->AddChildProcess(init); + + init->addrspace = initaddrspace; + Scheduler::SetInitProcess(init); + + Thread* initthread = RunKernelThread(init, InitThread, NULL); + if ( !initthread ) { Panic("Coul not create init thread"); } + + // Wait until init init is done and then shut down the computer. + int status; + pid_t pid = CurrentProcess()->Wait(init->pid, &status, 0); + if ( pid != init->pid ) + PanicF("Waiting for init to exit returned %i (errno=%i)", pid, errno); + + switch ( status ) + { + case 0: CPU::ShutDown(); + case 1: CPU::Reboot(); + default: + PanicF("Init returned with unexpected return code %i", status); + } +} + +static void InitThread(void* /*user*/) +{ + // We are the init process's first thread. Let's load the init program from + // the init ramdisk and transfer execution to it. We will then become a + // regular user-space program with root permissions. + + Thread* thread = CurrentThread(); + Process* process = CurrentProcess(); + + uint32_t inode = InitRD::Traverse(InitRD::Root(), "init"); + if ( !inode ) { Panic("InitRD did not contain an 'init' program."); } + + size_t programsize; + uint8_t* program = InitRD::Open(inode, &programsize); + if ( !program ) { Panic("InitRD did not contain an 'init' program."); } + + const size_t DEFAULT_STACK_SIZE = 64UL * 1024UL; + + size_t stacksize = 0; + if ( !stacksize ) { stacksize = DEFAULT_STACK_SIZE; } + + addr_t stackpos = process->AllocVirtualAddr(stacksize); + if ( !stackpos ) { Panic("Could not allocate init stack space"); } + + int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE; + if ( !Memory::MapRange(stackpos, stacksize, prot) ) + { + Panic("Could not allocate init stack memory"); + } + + thread->stackpos = stackpos; + thread->stacksize = stacksize; + + int argc = 1; + const char* argv[] = { "init", NULL }; + int envc = 0; + const char* envp[] = { NULL }; + CPU::InterruptRegisters regs; + + if ( process->Execute("init", program, programsize, argc, argv, envc, envp, + ®s) ) + { + Panic("Unable to execute init program"); + } + + // Now become the init process and the operation system shall run. + CPU::LoadRegisters(®s); } } // namespace Sortix diff --git a/sortix/kthread.cpp b/sortix/kthread.cpp index a0998acb..39a9d0e3 100644 --- a/sortix/kthread.cpp +++ b/sortix/kthread.cpp @@ -18,52 +18,107 @@ Sortix. If not, see . kthread.cpp - Fake header providing noop threading functions. This is simply forward - compatibility with the upcoming kthread branch and to ease merging. + Utility and synchronization mechanisms for kernel threads. *******************************************************************************/ #include #include +#include +#include +#include "signal.h" +#include "thread.h" +#include "scheduler.h" namespace Sortix { -// This isn't as bad as it looks. Kernel code traditionally runs with interrupts -// disabled so there are no race conditions. - -extern "C" unsigned kthread_mutex_trylock(kthread_mutex_t* mutex) +// The kernel thread needs another stack to delete its own stack. +static void kthread_do_kill_thread(void* user) { + Thread* thread = (Thread*) user; + while ( thread->state != Thread::State::DEAD ) + kthread_yield(); + delete thread; } -extern "C" void kthread_mutex_lock(kthread_mutex_t* mutex) +extern "C" void kthread_exit(void* /*param*/) { + Worker::Schedule(kthread_do_kill_thread, CurrentThread()); + Scheduler::ExitThread(); } -unsigned long kthread_mutex_lock_signal(kthread_mutex_t* mutex) +struct kthread_cond_elem { - return 1; -} - -extern "C" void kthread_mutex_unlock(kthread_mutex_t* mutex) -{ -} + kthread_cond_elem_t* next; + volatile unsigned long woken; +}; extern "C" void kthread_cond_wait(kthread_cond_t* cond, kthread_mutex_t* mutex) { + kthread_cond_elem_t elem; + elem.next = NULL; + elem.woken = 0; + if ( cond->last ) { cond->last->next = &elem; } + if ( !cond->last ) { cond->last = cond->first = &elem; } + while ( !elem.woken ) + { + kthread_mutex_unlock(mutex); + Scheduler::Yield(); + kthread_mutex_lock(mutex); + } } extern "C" unsigned long kthread_cond_wait_signal(kthread_cond_t* cond, kthread_mutex_t* mutex) { + if ( Signal::IsPending() ) + return 0; + kthread_cond_elem_t elem; + elem.next = NULL; + elem.woken = 0; + if ( cond->last ) { cond->last->next = &elem; } + if ( !cond->last ) { cond->last = cond->first = &elem; } + while ( !elem.woken ) + { + if ( Signal::IsPending() ) + { + if ( cond->first == &elem ) + { + cond->first = elem.next; + if ( cond->last == &elem ) + cond->last = NULL; + } + else + { + kthread_cond_elem_t* prev = cond->first; + while ( prev->next != &elem ) + prev = prev->next; + prev->next = elem.next; + if ( cond->last == &elem ) + cond->last = prev; + } + // Note that the thread still owns the mutex. + return 0; + } + kthread_mutex_unlock(mutex); + Scheduler::Yield(); + kthread_mutex_lock(mutex); + } return 1; } extern "C" void kthread_cond_signal(kthread_cond_t* cond) { + kthread_cond_elem_t* elem = cond->first; + if ( !elem ) { return; } + if ( !(cond->first = elem->next) ) { cond->last = NULL; } + elem->next = NULL; + elem->woken = 1; } extern "C" void kthread_cond_broadcast(kthread_cond_t* cond) { + while ( cond->first ) { kthread_cond_signal(cond); } } } // namespace Sortix diff --git a/sortix/logterminal.cpp b/sortix/logterminal.cpp index 7a61dae8..96588301 100644 --- a/sortix/logterminal.cpp +++ b/sortix/logterminal.cpp @@ -24,10 +24,12 @@ #include #include +#include #include #include #include "utf8.h" #include "keyboard.h" +#include "process.h" #include "scheduler.h" #include "terminal.h" #include "logterminal.h" @@ -130,7 +132,10 @@ namespace Sortix if ( kbkey == -KBKEY_LCTRL ) { control = false; } if ( mode & TERMMODE_SIGNAL && control && kbkey == KBKEY_C ) { - Scheduler::SigIntHack(); + pid_t pid = Process::HackGetForegroundProcess(); + Process* process = Process::Get(pid); + if ( process ) + process->DeliverSignal(SIGINT); return; } if ( mode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D ) @@ -299,7 +304,6 @@ namespace Sortix return -1; } #endif - return sofar; } diff --git a/sortix/logterminal.h b/sortix/logterminal.h index 74147dfe..fc4339e2 100644 --- a/sortix/logterminal.h +++ b/sortix/logterminal.h @@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - logterminal.h A simple terminal that writes to the kernel log. diff --git a/sortix/panic.cpp b/sortix/panic.cpp index 6b1e76fe..ed5898ee 100644 --- a/sortix/panic.cpp +++ b/sortix/panic.cpp @@ -25,6 +25,7 @@ #include #include #include +#include "interrupt.h" #include #include "calltrace.h" #include @@ -43,6 +44,7 @@ namespace Sortix void PanicInit() { + Interrupt::Disable(); if ( panicing ) { Log::PrintF("Panic while panicing:\n"); diff --git a/sortix/pipe.cpp b/sortix/pipe.cpp index 4073211e..f345fdcb 100644 --- a/sortix/pipe.cpp +++ b/sortix/pipe.cpp @@ -24,11 +24,13 @@ #include #include +#include #include #include #ifdef GOT_FAKE_KTHREAD #include "event.h" #endif +#include "signal.h" #include "thread.h" #include "process.h" #include "syscall.h" @@ -159,8 +161,7 @@ namespace Sortix } if ( !anyreading ) { - // TODO: Implement better signal support and uncomment. - //CurrentThread()->DeliverSignal(SIGPIPE); + CurrentThread()->DeliverSignal(SIGPIPE); Error::Set(EPIPE); return -1; } diff --git a/sortix/process.cpp b/sortix/process.cpp index d9fbf752..c00905a7 100644 --- a/sortix/process.cpp +++ b/sortix/process.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,15 +14,19 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . process.cpp - Describes a process belonging to a subsystem. + A named collection of threads. -******************************************************************************/ +*******************************************************************************/ #include +#include +#include +#include +#include #include #include #include @@ -37,7 +41,6 @@ #include "filesystem.h" #include "directory.h" #include "scheduler.h" -#include #include "initrd.h" #include "elf.h" #include "syscall.h" @@ -84,7 +87,8 @@ namespace Sortix return NULL; } - next->prev = nextclone; + if ( nextclone ) + nextclone->prev = clone; clone->next = nextclone; clone->position = position; clone->size = size; @@ -96,15 +100,20 @@ namespace Sortix { addrspace = 0; segments = NULL; - sigint = false; parent = NULL; prevsibling = NULL; nextsibling = NULL; firstchild = NULL; zombiechild = NULL; + parentlock = KTHREAD_MUTEX_INITIALIZER; + childlock = KTHREAD_MUTEX_INITIALIZER; + zombiecond = KTHREAD_COND_INITIALIZER; + zombiewaiting = 0; + iszombie = false; + nozombify = false; firstthread = NULL; + threadlock = KTHREAD_MUTEX_INITIALIZER; workingdir = NULL; - errno = NULL; mmapfrom = 0x80000000UL; exitstatus = -1; pid = AllocatePID(); @@ -113,20 +122,156 @@ namespace Sortix Process::~Process() { + ASSERT(!zombiechild); + ASSERT(!firstchild); + ASSERT(!addrspace); + ASSERT(!segments); + Remove(this); + delete[] workingdir; + } + + void Process__OnLastThreadExit(void* user); + + void Process::OnThreadDestruction(Thread* thread) + { + ASSERT(thread->process == this); + kthread_mutex_lock(&threadlock); + if ( thread->prevsibling ) + thread->prevsibling->nextsibling = thread->nextsibling; + if ( thread->nextsibling ) + thread->nextsibling->prevsibling = thread->prevsibling; + if ( thread == firstthread ) + firstthread = thread->nextsibling; + if ( firstthread ) + firstthread->prevsibling = NULL; + thread->prevsibling = thread->nextsibling = NULL; + bool threadsleft = firstthread; + kthread_mutex_unlock(&threadlock); + + // We are called from the threads destructor, let it finish before we + // we handle the situation by killing ourselves. + if ( !threadsleft ) + ScheduleDeath(); + } + + void Process::ScheduleDeath() + { + // All our threads must have exited at this point. + ASSERT(!firstthread); + Worker::Schedule(Process__OnLastThreadExit, this); + } + + // Useful for killing a partially constructed process without waiting for + // it to die and garbage collect its zombie. It is not safe to access this + // process after this call as another thread may garbage collect it. + void Process::AbortConstruction() + { + nozombify = true; + ScheduleDeath(); + } + + void Process__OnLastThreadExit(void* user) + { + return ((Process*) user)->OnLastThreadExit(); + } + + void Process::OnLastThreadExit() + { + LastPrayer(); + } + + static void SwitchCurrentAddrspace(addr_t addrspace, void* user) + { + ((Thread*) user)->SwitchAddressSpace(addrspace); + } + + void Process::LastPrayer() + { + ASSERT(this); + // This must never be called twice. + ASSERT(!iszombie); + + // This must be called from a thread using another address space as the + // address space of this process is about to be destroyed. + Thread* curthread = CurrentThread(); + ASSERT(curthread->process != this); + + // This can't be called if the process is still alive. + ASSERT(!firstthread); + + // We need to temporarily reload the correct addrese space of the dying + // process such that we can unmap and free its memory. + addr_t prevaddrspace = curthread->SwitchAddressSpace(addrspace); ResetAddressSpace(); + descriptors.Reset(); - // Avoid memory leaks. - ASSERT(segments == NULL); + // Destroy the address space and safely switch to the replacement + // address space before things get dangerous. + Memory::DestroyAddressSpace(prevaddrspace, + SwitchCurrentAddrspace, + curthread); + addrspace = 0; - delete[] workingdir; + // Init is nice and will gladly raise our orphaned children and zombies. + Process* init = Scheduler::GetInitProcess(); + ASSERT(init); + kthread_mutex_lock(&childlock); + while ( firstchild ) + { + ScopedLock firstchildlock(&firstchild->parentlock); + ScopedLock initlock(&init->childlock); + Process* process = firstchild; + firstchild = process->nextsibling; + process->parent = init; + process->prevsibling = NULL; + process->nextsibling = init->firstchild; + if ( init->firstchild ) + init->firstchild->prevsibling = process; + init->firstchild = process; + } + // Since we have no more children (they are with init now), we don't + // have to worry about new zombie processes showing up, so just collect + // those that are left. Then we satisfiy the invariant !zombiechild that + // applies on process termination. + bool hadzombies = zombiechild; + while ( zombiechild ) + { + ScopedLock zombiechildlock(&zombiechild->parentlock); + ScopedLock initlock(&init->childlock); + Process* zombie = zombiechild; + zombiechild = zombie->nextsibling; + zombie->prevsibling = NULL; + zombie->nextsibling = init->zombiechild; + if ( init->zombiechild ) + init->zombiechild->prevsibling = zombie; + init->zombiechild = zombie; + } + kthread_mutex_unlock(&childlock); - // TODO: Delete address space! + if ( hadzombies ) + init->NotifyNewZombies(); + + iszombie = true; + + bool zombify = !nozombify; + + // This class instance will be destroyed by our parent process when it + // has received and acknowledged our death. + kthread_mutex_lock(&parentlock); + if ( parent ) + parent->NotifyChildExit(this, zombify); + kthread_mutex_unlock(&parentlock); + + // If nobody is waiting for us, then simply commit suicide. + if ( !zombify ) + delete this; } void Process::ResetAddressSpace() { + ASSERT(Memory::GetAddressSpace() == addrspace); ProcessSegment* tmp = segments; while ( tmp != NULL ) { @@ -137,7 +282,154 @@ namespace Sortix } segments = NULL; - errno = NULL; + } + + void Process::NotifyChildExit(Process* child, bool zombify) + { + kthread_mutex_lock(&childlock); + + if ( child->prevsibling ) + child->prevsibling->nextsibling = child->nextsibling; + if ( child->nextsibling ) + child->nextsibling->prevsibling = child->prevsibling; + if ( firstchild == child ) + firstchild = child->nextsibling; + if ( firstchild ) + firstchild->prevsibling = NULL; + + if ( zombify ) + { + if ( zombiechild ) + zombiechild->prevsibling = child; + child->prevsibling = NULL; + child->nextsibling = zombiechild; + zombiechild = child; + } + + kthread_mutex_unlock(&childlock); + + if ( zombify ) + NotifyNewZombies(); + } + + void Process::NotifyNewZombies() + { + ScopedLock lock(&childlock); + // TODO: Send SIGCHLD here? + if ( zombiewaiting ) + kthread_cond_broadcast(&zombiecond); + } + + pid_t Process::Wait(pid_t thepid, int* status, int options) + { + // TODO: Process groups are not supported yet. + if ( thepid < -1 || thepid == 0 ) { Error::Set(ENOSYS); return -1; } + + ScopedLock lock(&childlock); + + // A process can only wait if it has children. + if ( !firstchild && !zombiechild ) { Error::Set(ECHILD); return -1; } + + // Processes can only wait for their own children to exit. + if ( 0 < thepid ) + { + // TODO: This is a slow but multithread safe way to verify that the + // target process has the correct parent. + bool found = false; + for ( Process* p = firstchild; !found && p; p = p->nextsibling ) + if ( p->pid == thepid ) + found = true; + for ( Process* p = zombiechild; !found && p; p = p->nextsibling ) + if ( p->pid == thepid ) + found = true; + if ( !found ) { Error::Set(ECHILD); return -1; } + } + + Process* zombie = NULL; + while ( !zombie ) + { + for ( zombie = zombiechild; zombie; zombie = zombie->nextsibling ) + if ( thepid == -1 || thepid == zombie->pid ) + break; + if ( zombie ) + break; + zombiewaiting++; + kthread_cond_wait(&zombiecond, &childlock); + zombiewaiting--; + } + + if ( zombie->prevsibling ) + zombie->prevsibling->nextsibling = zombie->nextsibling; + if ( zombie->nextsibling ) + zombie->nextsibling->prevsibling = zombie->prevsibling; + if ( zombiechild == zombie ) + zombiechild = zombie->nextsibling; + if ( zombiechild ) + zombiechild->prevsibling = NULL; + + thepid = zombie->pid; + + int exitstatus = zombie->exitstatus; + if ( exitstatus < 0 ) + exitstatus = 0; + + // TODO: Validate that status is a valid user-space int! + if ( status ) + *status = exitstatus; + + // And so, the process was fully deleted. + delete zombie; + + return thepid; + } + + pid_t SysWait(pid_t pid, int* status, int options) + { + return CurrentProcess()->Wait(pid, status, options); + } + + void Process::Exit(int status) + { + ScopedLock lock(&threadlock); + // Status codes can only contain 8 bits according to ISO C and POSIX. + if ( exitstatus == -1 ) + exitstatus = status % 256; + + // Broadcast SIGKILL to all our threads which will begin our long path + // of process termination. We simply can't stop the threads as they may + // be running in kernel mode doing dangerous stuff. This thread will be + // destroyed by SIGKILL once the system call returns. + for ( Thread* t = firstthread; t; t = t->nextsibling ) + t->DeliverSignal(SIGKILL); + } + + void SysExit(int status) + { + CurrentProcess()->Exit(status); + } + + bool Process::DeliverSignal(int signum) + { + // TODO: How to handle signals that kill the process? + if ( firstthread ) + return firstthread->DeliverSignal(signum); + Error::Set(EINIT); + return false; + } + + void Process::AddChildProcess(Process* child) + { + ScopedLock mylock(&childlock); + ScopedLock itslock(&child->parentlock); + ASSERT(!child->parent); + ASSERT(!child->nextsibling); + ASSERT(!child->prevsibling); + child->parent = this; + child->nextsibling = firstchild; + child->prevsibling = NULL; + if ( firstchild ) + firstchild->prevsibling = child; + firstchild = child; } Process* Process::Fork() @@ -172,88 +464,36 @@ namespace Sortix delete clone; return NULL; } - // Now it's too late to clean up here, if anything goes wrong, the - // cloned process should be queued for destruction. + // Now it's too late to clean up here, if anything goes wrong, we simply + // ask the process to commit suicide before it goes live. clone->segments = clonesegments; // Remember the relation to the child process. - clone->parent = this; - if ( firstchild ) - { - firstchild->prevsibling = clone; - clone->nextsibling = firstchild; - firstchild = clone; - } - else - { - firstchild = clone; - } + AddChildProcess(clone); + + bool failure = false; - // Fork the file descriptors. if ( !descriptors.Fork(&clone->descriptors) ) - { - Panic("No error handling when forking FDs fails!"); - } + failure = true; - Thread* clonethreads = ForkThreads(clone); - if ( !clonethreads ) - { - Panic("No error handling when forking threads fails!"); - } - - clone->firstthread = clonethreads; - - // Copy variables. clone->mmapfrom = mmapfrom; - clone->errnop = errnop; - if ( workingdir ) { clone->workingdir = String::Clone(workingdir); } - else { clone->workingdir = NULL; } - // Now that the cloned process is fully created, we need to signal to - // its threads that they should insert themselves into the scheduler. - for ( Thread* tmp = clonethreads; tmp != NULL; tmp = tmp->nextsibling ) + clone->workingdir = NULL; + if ( workingdir && !(clone->workingdir = String::Clone(workingdir)) ) + failure = true; + + // If the proces creation failed, ask the process to commit suicide and + // not become a zombie, as we don't wait for it to exit. It will clean + // up all the above resources and delete itself. + if ( failure ) { - tmp->Ready(); + clone->AbortConstruction(); + return NULL; } return clone; } - Thread* Process::ForkThreads(Process* processclone) - { - Thread* result = NULL; - Thread* tmpclone = NULL; - - for ( Thread* tmp = firstthread; tmp != NULL; tmp = tmp->nextsibling ) - { - Thread* clonethread = tmp->Fork(); - if ( clonethread == NULL ) - { - while ( tmpclone != NULL ) - { - Thread* todelete = tmpclone; - tmpclone = tmpclone->prevsibling; - delete todelete; - } - - return NULL; - } - - clonethread->process = processclone; - - if ( result == NULL ) { result = clonethread; } - if ( tmpclone != NULL ) - { - tmpclone->nextsibling = clonethread; - clonethread->prevsibling = tmpclone; - } - - tmpclone = clonethread; - } - - return result; - } - void Process::ResetForExecute() { // TODO: Delete all threads and their stacks. @@ -315,104 +555,14 @@ namespace Sortix stackpos = envppos - envpsize; - ExecuteCPU(argc, stackargv, envc, stackenvp, stackpos, entry, regs); - descriptors.OnExecute(); + ExecuteCPU(argc, stackargv, envc, stackenvp, stackpos, entry, regs); + return 0; } - class SysExecVEState - { - public: - char* filename; - DevBuffer* dev; - byte* buffer; - size_t count; - size_t sofar; - int argc; - int envc; - char** argv; - char** envp; - - public: - SysExecVEState() - { - filename = NULL; - dev = NULL; - buffer = NULL; - count = 0; - sofar = 0; - argc = 0; - argv = NULL; - envc = 0; - envp = NULL; - } - - ~SysExecVEState() - { - delete[] filename; - if ( dev ) { dev->Unref(); } - delete[] buffer; - for ( int i = 0; i < argc; i++ ) { delete[] argv[i]; } - delete[] argv; - for ( int i = 0; i < envc; i++ ) { delete[] envp[i]; } - delete[] envp; - } - - }; - - int SysExevVEStage2(SysExecVEState* state) - { - if ( !state->dev->IsReadable() ) { Error::Set(EBADF); delete state; return -1; } - - byte* dest = state->buffer + state->sofar; - size_t amount = state->count - state->sofar; - ssize_t bytesread = state->dev->Read(dest, amount); - - // Check for premature end-of-file. - if ( bytesread == 0 && amount != 0 ) - { - Error::Set(EIO); delete state; return -1; - } - - // We actually managed to read some data. - if ( 0 <= bytesread ) - { - state->sofar += bytesread; - if ( state->sofar <= state->count ) - { - CPU::InterruptRegisters* regs = Syscall::InterruptRegs(); - Process* process = CurrentProcess(); - int result = process->Execute(state->filename, state->buffer, - state->count, state->argc, - state->argv, state->envc, - state->envp, regs); - if ( result == 0 ) { Syscall::AsIs(); } - delete state; - return result; - } - - return SysExevVEStage2(state); - } - - if ( Error::Last() != EBLOCKING ) { delete state; return -1; } - - // The stream will resume our system call once progress has been - // made. Our request is certainly not forgotten. - - // Resume the system call with these parameters. - Thread* thread = CurrentThread(); - thread->scfunc = (void*) SysExevVEStage2; - thread->scstate[0] = (size_t) state; - thread->scsize = sizeof(state); - - // Now go do something else. - Syscall::Incomplete(); - return 0; - } - - DevBuffer* OpenProgramImage(const char* progname) + DevBuffer* OpenProgramImage(const char* progname, const char* wd, const char* path) { char* abs = Directory::MakeAbsolute("/", progname); if ( !abs ) { Error::Set(ENOMEM); return NULL; } @@ -426,112 +576,126 @@ namespace Sortix return (DevBuffer*) dev; } - int SysExecVE(const char* filename, char* const argv[], char* const envp[]) + int SysExecVE(const char* _filename, char* const _argv[], char* const _envp[]) { - // TODO: Validate that all the pointer-y parameters are SAFE! - - // Use a container class to store everything and handle cleaning up. - SysExecVEState* state = new SysExecVEState; - if ( !state ) { return -1; } - - // Make a copy of argv and filename as they are going to be destroyed - // when the address space is reset. - state->filename = String::Clone(filename); - if ( !state->filename ) { delete state; return -1; } - - int argc; for ( argc = 0; argv && argv[argc]; argc++ ); - int envc; for ( envc = 0; envp && envp[envc]; envc++ ); - - state->argv = new char*[argc+1]; - if ( !state->argv ) { delete state; return -1; } - state->argc = argc; - Maxsi::Memory::Set(state->argv, 0, sizeof(char*) * (state->argc+1)); - - for ( int i = 0; i < state->argc; i++ ) - { - state->argv[i] = String::Clone(argv[i]); - if ( !state->argv[i] ) { delete state; return -1; } - } - - state->envp = new char*[envc+1]; - if ( !state->envp ) { delete state; return -1; } - state->envc = envc; - Maxsi::Memory::Set(state->envp, 0, sizeof(char*) * (state->envc+1)); - - for ( int i = 0; i < state->envc; i++ ) - { - state->envp[i] = String::Clone(envp[i]); - if ( !state->envp[i] ) { delete state; return -1; } - } - + char* filename; + int argc; + int envc; + char** argv; + char** envp; + DevBuffer* dev; + uintmax_t needed; + size_t sofar; + size_t count; + uint8_t* buffer; + int result = -1; Process* process = CurrentProcess(); - state->dev = OpenProgramImage(state->filename); - if ( !state->dev ) { delete state; return -1; } + CPU::InterruptRegisters regs; + Maxsi::Memory::Set(®s, 0, sizeof(regs)); - state->dev->Refer(); // TODO: Rules of GC may change soon. - uintmax_t needed = state->dev->Size(); - if ( SIZE_MAX < needed ) { Error::Set(ENOMEM); delete state; return -1; } + filename = String::Clone(_filename); + if ( !filename ) { goto cleanup_done; } - state->count = needed; - state->buffer = new byte[state->count]; - if ( !state->buffer ) { delete state; return -1; } + for ( argc = 0; _argv && _argv[argc]; argc++ ); + for ( envc = 0; _envp && _envp[envc]; envc++ ); - return SysExevVEStage2(state); + argv = new char*[argc+1]; + if ( !argv ) { goto cleanup_filename; } + Maxsi::Memory::Set(argv, 0, sizeof(char*) * (argc+1)); + + for ( int i = 0; i < argc; i++ ) + { + argv[i] = String::Clone(_argv[i]); + if ( !argv[i] ) { goto cleanup_argv; } + } + + envp = new char*[envc+1]; + if ( !envp ) { goto cleanup_argv; } + envc = envc; + Maxsi::Memory::Set(envp, 0, sizeof(char*) * (envc+1)); + + for ( int i = 0; i < envc; i++ ) + { + envp[i] = String::Clone(_envp[i]); + if ( !envp[i] ) { goto cleanup_envp; } + } + + dev = OpenProgramImage(filename, process->workingdir, "/bin"); + if ( !dev ) { goto cleanup_envp; } + + dev->Refer(); // TODO: Rules of GC may change soon. + needed = dev->Size(); + if ( SIZE_MAX < needed ) { Error::Set(ENOMEM); goto cleanup_dev; } + + if ( !dev->IsReadable() ) { Error::Set(EBADF); goto cleanup_dev; } + + count = needed; + buffer = new byte[count]; + if ( !buffer ) { goto cleanup_dev; } + sofar = 0; + while ( sofar < count ) + { + ssize_t bytesread = dev->Read(buffer + sofar, count - sofar); + if ( bytesread < 0 ) { goto cleanup_buffer; } + if ( bytesread == 0 ) { Error::Set(EEOF); return -1; } + sofar += bytesread; + } + + result = process->Execute(filename, buffer, count, argc, argv, envc, + envp, ®s); + + cleanup_buffer: + delete[] buffer; + cleanup_dev: + dev->Unref(); + cleanup_envp: + for ( int i = 0; i < envc; i++) { delete[] envp[i]; } + delete[] envp; + cleanup_argv: + for ( int i = 0; i < argc; i++) { delete[] argv[i]; } + delete[] argv; + cleanup_filename: + delete[] filename; + cleanup_done: + if ( !result ) { CPU::LoadRegisters(®s); } + return result; } pid_t SysSForkR(int flags, sforkregs_t* regs) { + if ( Signal::IsPending() ) { Error::Set(EINTR); return -1; } + // TODO: Properly support sforkr(2). if ( flags != SFFORK ) { Error::Set(ENOSYS); return -1; } CPU::InterruptRegisters cpuregs; - Maxsi::Memory::Set(&cpuregs, 0, sizeof(cpuregs)); -#if defined(PLATFORM_X64) - cpuregs.rip = regs->rip; - cpuregs.userrsp = regs->rsp; - cpuregs.rax = regs->rax; - cpuregs.rbx = regs->rbx; - cpuregs.rcx = regs->rcx; - cpuregs.rdx = regs->rdx; - cpuregs.rdi = regs->rdi; - cpuregs.rsi = regs->rsi; - cpuregs.rbp = regs->rbp; - cpuregs.r8 = regs->r8; - cpuregs.r9 = regs->r9; - cpuregs.r10 = regs->r10; - cpuregs.r11 = regs->r11; - cpuregs.r12 = regs->r12; - cpuregs.r13 = regs->r13; - cpuregs.r14 = regs->r14; - cpuregs.r15 = regs->r15; - cpuregs.cs = 0x18 | 0x3; - cpuregs.ds = 0x20 | 0x3; - cpuregs.ss = 0x20 | 0x3; - //cpuregs.rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - cpuregs.rflags = (1<<1) | (1<<9) | (1<<21); -#elif defined(PLATFORM_X86) - cpuregs.eip = regs->eip; - cpuregs.useresp = regs->esp; - cpuregs.eax = regs->eax; - cpuregs.ebx = regs->ebx; - cpuregs.ecx = regs->ecx; - cpuregs.edx = regs->edx; - cpuregs.edi = regs->edi; - cpuregs.esi = regs->esi; - cpuregs.ebp = regs->ebp; - cpuregs.cs = 0x18 | 0x3; - cpuregs.ds = 0x20 | 0x3; - cpuregs.ss = 0x20 | 0x3; - //cpuregs.eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - cpuregs.eflags = (1<<1) | (1<<9) | (1<<21); -#else - #error SysSForkR needs to know about your platform -#endif + InitializeThreadRegisters(&cpuregs, regs); - CurrentThread()->SaveRegisters(&cpuregs); + // TODO: Is it a hack to create a new kernel stack here? + Thread* curthread = CurrentThread(); + uint8_t* newkernelstack = new uint8_t[curthread->kernelstacksize]; + if ( !newkernelstack ) { return -1; } Process* clone = CurrentProcess()->Fork(); - if ( !clone ) { return -1; } + if ( !clone ) { delete[] newkernelstack; return -1; } + + // If the thread could not be created, make the process commit suicide + // in a manner such that we don't wait for its zombie. + Thread* thread = CreateKernelThread(clone, &cpuregs); + if ( !thread ) + { + clone->AbortConstruction(); + return -1; + } + + thread->kernelstackpos = (addr_t) newkernelstack; + thread->kernelstacksize = curthread->kernelstacksize; + thread->kernelstackmalloced = true; + thread->stackpos = curthread->stackpos; + thread->stacksize = curthread->stacksize; + thread->sighandler = curthread->sighandler; + + StartKernelThread(thread); return clone->pid; } @@ -541,21 +705,43 @@ namespace Sortix return CurrentProcess()->pid; } - pid_t SysGetParentPID() + pid_t Process::GetParentProcessId() { - Process* parent = CurrentProcess()->parent; - if ( !parent ) { return -1; } - + ScopedLock lock(&parentlock); + if( !parent ) + return 0; return parent->pid; } + pid_t SysGetParentPID() + { + return CurrentProcess()->GetParentProcessId(); + } + pid_t nextpidtoallocate; + kthread_mutex_t pidalloclock; pid_t Process::AllocatePID() { + ScopedLock lock(&pidalloclock); return nextpidtoallocate++; } + // TODO: This is not thread safe. + pid_t Process::HackGetForegroundProcess() + { + for ( pid_t i = nextpidtoallocate; 1 <= i; i-- ) + { + Process* process = Get(i); + if ( !process ) + continue; + if ( process->pid <= 1 ) + continue; + return i; + } + return 0; + } + int ProcessCompare(Process* a, Process* b) { if ( a->pid < b->pid ) { return -1; } @@ -574,6 +760,7 @@ namespace Sortix Process* Process::Get(pid_t pid) { + ScopedLock lock(&pidalloclock); size_t index = pidlist->Search(ProcessPIDCompare, pid); if ( index == SIZE_MAX ) { return NULL; } @@ -582,212 +769,19 @@ namespace Sortix bool Process::Put(Process* process) { + ScopedLock lock(&pidalloclock); return pidlist->Add(process); } void Process::Remove(Process* process) { + ScopedLock lock(&pidalloclock); size_t index = pidlist->Search(process); ASSERT(index != SIZE_MAX); pidlist->Remove(index); } - void Process::OnChildProcessExit(Process* process) - { - ASSERT(process->parent == this); - - for ( Thread* thread = firstthread; thread; thread = thread->nextsibling ) - { - if ( thread->onchildprocessexit ) - { - thread->onchildprocessexit(thread, process); - } - } - } - - void Process::Exit(int status) - { - // Status codes can only contain 8 bits according to ISO C and POSIX. - status %= 256; - - ASSERT(this == CurrentProcess()); - - Process* init = Scheduler::GetInitProcess(); - - if ( pid == 0 ) { Panic("System idle process exited"); } - - // If the init process terminated successfully, time to halt. - if ( this == init ) - { - switch ( status ) - { - case 0: CPU::ShutDown(); - case 1: CPU::Reboot(); - default: PanicF("The init process exited abnormally with status code %u\n", status); - } - } - - // Take care of the orphans, so give them to init. - while ( firstchild ) - { - Process* orphan = firstchild; - firstchild = orphan->nextsibling; - if ( firstchild ) { firstchild->prevsibling = NULL; } - orphan->parent = init; - orphan->prevsibling = NULL; - orphan->nextsibling = init->firstchild; - if ( orphan->nextsibling ) { orphan->nextsibling->prevsibling = orphan; } - init->firstchild = orphan; - } - - // Remove the current process from the family tree. - if ( !prevsibling ) - { - parent->firstchild = nextsibling; - } - else - { - prevsibling->nextsibling = nextsibling; - } - - if ( nextsibling ) - { - nextsibling->prevsibling = prevsibling; - } - - // Close all the file descriptors. - descriptors.Reset(); - - // Make all threads belonging to process unrunnable. - for ( Thread* t = firstthread; t; t = t->nextsibling ) - { - Scheduler::EarlyWakeUp(t); - Scheduler::SetThreadState(t, Thread::State::NONE); - } - - // Delete the threads. - while ( firstthread ) - { - Thread* todelete = firstthread; - firstthread = firstthread->nextsibling; - delete todelete; - } - - // Now clean up the address space. - ResetAddressSpace(); - - // TODO: Actually delete the address space. This is a small memory leak - // of a couple pages. - - exitstatus = status; - nextsibling = parent->zombiechild; - if ( parent->zombiechild ) { parent->zombiechild->prevsibling = this; } - parent->zombiechild = this; - - // Notify the parent process that the child has become a zombie. - parent->OnChildProcessExit(this); - - // Now, as a final operation, get rid of the address space. This should - // return us to the original kernel address space containing nothing - // but the kernel. - Memory::DestroyAddressSpace(); - } - - void SysExit(int status) - { - CurrentProcess()->Exit(status); - - // And so, the process had vanished from existence. But as fate would - // have it, soon a replacement took its place. - Scheduler::ProcessTerminated(Syscall::InterruptRegs()); - Syscall::AsIs(); - } - - struct SysWait_t - { - union { size_t align1; pid_t pid; }; - union { size_t align2; int* status; }; - union { size_t align3; int options; }; - }; - - STATIC_ASSERT(sizeof(SysWait_t) <= sizeof(Thread::scstate)); - - void SysWaitCallback(Thread* thread, Process* exitee) - { - // See if this process matches what we are looking for. - SysWait_t* state = (SysWait_t*) thread->scstate; - if ( state->pid != -1 && state->pid != exitee->pid ) { return; } - - thread->onchildprocessexit = NULL; - - Syscall::ScheduleResumption(thread); - } - - pid_t SysWait(pid_t pid, int* status, int options) - { - Thread* thread = CurrentThread(); - Process* process = thread->process; - - if ( pid != -1 ) - { - Process* waitingfor = Process::Get(pid); - if ( !waitingfor ) { Error::Set(ECHILD); return -1; } - if ( waitingfor->parent != process ) { Error::Set(ECHILD); return -1; } - } - - // Find any zombie children matching the search description. - for ( Process* zombie = process->zombiechild; zombie; zombie = zombie->nextsibling ) - { - if ( pid != -1 && pid != zombie->pid ) { continue; } - - pid = zombie->pid; - // TODO: Validate that status is a valid user-space int! - if ( status ) { *status = zombie->exitstatus; } - - if ( zombie == process->zombiechild ) - { - process->zombiechild = zombie->nextsibling; - if ( zombie->nextsibling ) { zombie->nextsibling->prevsibling = NULL; } - } - else - { - zombie->prevsibling->nextsibling = zombie->nextsibling; - if ( zombie->nextsibling ) { zombie->nextsibling->prevsibling = zombie->prevsibling; } - } - - // And so, the process was fully deleted. - delete zombie; - - return pid; - } - - // The process needs to have children, otherwise we are waiting for - // nothing to happen. - if ( !process->firstchild ) { Error::Set(ECHILD); return -1; } - - // Resumes this system call when the wait condition has been met. - thread->onchildprocessexit = SysWaitCallback; - - // Resume the system call with these parameters. - thread->scfunc = (void*) SysWait; - SysWait_t* state = (SysWait_t*) thread->scstate; - state->pid = pid; - state->status = status; - state->options = options; - thread->scsize = sizeof(SysWait_t); - - // Now go do something else. - Syscall::Incomplete(); - return 0; - } - - int SysRegisterErrno(int* errnop) - { - CurrentProcess()->errnop = errnop; - return 0; - } - void* SysSbrk(intptr_t increment) { Process* process = CurrentProcess(); @@ -843,10 +837,10 @@ namespace Sortix Syscall::Register(SYSCALL_GETPPID, (void*) SysGetParentPID); Syscall::Register(SYSCALL_EXIT, (void*) SysExit); Syscall::Register(SYSCALL_WAIT, (void*) SysWait); - Syscall::Register(SYSCALL_REGISTER_ERRNO, (void*) SysRegisterErrno); Syscall::Register(SYSCALL_SBRK, (void*) SysSbrk); Syscall::Register(SYSCALL_GET_PAGE_SIZE, (void*) SysGetPageSize); + pidalloclock = KTHREAD_MUTEX_INITIALIZER; nextpidtoallocate = 0; pidlist = new SortedList(ProcessCompare); diff --git a/sortix/process.h b/sortix/process.h index f5518706..5e059d13 100644 --- a/sortix/process.h +++ b/sortix/process.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,19 +14,21 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . process.h - Describes a process belonging to a subsystem. + A named collection of threads. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_PROCESS_H #define SORTIX_PROCESS_H #include "descriptors.h" #include "cpu.h" +#include +#include namespace Sortix { @@ -34,7 +36,6 @@ namespace Sortix class Process; struct ProcessSegment; - const size_t DEFAULT_STACK_SIZE = 64*1024; const int SEG_NONE = 0; const int SEG_TEXT = 1; const int SEG_DATA = 2; @@ -61,6 +62,8 @@ namespace Sortix class Process { + friend void Process__OnLastThreadExit(void*); + public: Process(); ~Process(); @@ -73,28 +76,35 @@ namespace Sortix public: addr_t addrspace; - int exitstatus; char* workingdir; pid_t pid; - int* errnop; - public: + private: + // A process may only access its parent if parentlock is locked. A process + // may only use its list of children if childlock is locked. A process may + // not access its sibling processes. Process* parent; Process* prevsibling; Process* nextsibling; Process* firstchild; Process* zombiechild; + kthread_mutex_t childlock; + kthread_mutex_t parentlock; + kthread_cond_t zombiecond; + size_t zombiewaiting; + bool iszombie; + bool nozombify; + addr_t mmapfrom; + int exitstatus; public: Thread* firstthread; + kthread_mutex_t threadlock; public: DescriptorTable descriptors; ProcessSegment* segments; - public: - bool sigint; - public: int Execute(const char* programname, const byte* program, size_t programsize, int argc, const char* const* argv, @@ -102,36 +112,33 @@ namespace Sortix CPU::InterruptRegisters* regs); void ResetAddressSpace(); void Exit(int status); - - public: - bool IsSane() { return addrspace != 0; } + pid_t Wait(pid_t pid, int* status, int options); + bool DeliverSignal(int signum); + void OnThreadDestruction(Thread* thread); + int GetParentProcessId(); + void AddChildProcess(Process* child); + void ScheduleDeath(); + void AbortConstruction(); public: Process* Fork(); private: - Thread* ForkThreads(Process* processclone); void ExecuteCPU(int argc, char** argv, int envc, char** envp, addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs); + void OnLastThreadExit(); + void LastPrayer(); + void NotifyChildExit(Process* child, bool zombify); + void NotifyNewZombies(); public: void ResetForExecute(); - - public: - inline size_t DefaultStackSize() { return DEFAULT_STACK_SIZE; } - - private: - addr_t mmapfrom; - - public: addr_t AllocVirtualAddr(size_t size); - public: - void OnChildProcessExit(Process* process); - public: static Process* Get(pid_t pid); + static pid_t HackGetForegroundProcess(); private: static bool Put(Process* process); @@ -139,6 +146,8 @@ namespace Sortix }; + void InitializeThreadRegisters(CPU::InterruptRegisters* regs, + const sforkregs_t* requested); Process* CurrentProcess(); } diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp index 280c5271..1074db12 100644 --- a/sortix/scheduler.cpp +++ b/sortix/scheduler.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,367 +14,280 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . scheduler.cpp - Handles context switching between tasks and deciding when to execute what. + Decides the order to execute threads in and switching between them. -******************************************************************************/ +*******************************************************************************/ #include -#include +#include +#include #include -#include +#include "x86-family/gdt.h" +#include "syscall.h" +#include "interrupt.h" +#include "time.h" #include "thread.h" #include "process.h" -#include "time.h" +#include "signal.h" #include "scheduler.h" -#include -#include "syscall.h" -#include "sound.h" // HACK FOR SIGINT -#include "x86-family/gdt.h" -namespace Sortix +using namespace Maxsi; + +namespace Sortix { +namespace Scheduler { + +static Thread* currentthread; +} // namespace Scheduler +Thread* CurrentThread() { return Scheduler::currentthread; } +Process* CurrentProcess() { return CurrentThread()->process; } +namespace Scheduler { + +uint8_t dummythreaddata[sizeof(Thread)] __attribute__ ((aligned (8))); +Thread* dummythread; +Thread* idlethread; +Thread* firstrunnablethread; +Thread* firstsleepingthread; +Process* initprocess; + +static inline void SetCurrentThread(Thread* newcurrentthread) { - void SysExit(int status); // HACK FOR SIGINT + currentthread = newcurrentthread; +} - // Internal forward-declarations. - namespace Scheduler +void LogBeginSwitch(Thread* current, const CPU::InterruptRegisters* regs); +void LogSwitch(Thread* current, Thread* next); +void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs); + +static Thread* PopNextThread() +{ + if ( !firstrunnablethread ) { return idlethread; } + Thread* result = firstrunnablethread; + firstrunnablethread = firstrunnablethread->schedulerlistnext; + return result; +} + +static Thread* ValidatedPopNextThread() +{ + Thread* nextthread = PopNextThread(); + if ( !nextthread ) { Panic("Had no thread to switch to."); } + if ( nextthread->terminated ) { - Thread* PopNextThread(); - void WakeSleeping(); - void LogBeginContextSwitch(Thread* current, const CPU::InterruptRegisters* state); - void LogContextSwitch(Thread* current, Thread* next); - void LogEndContextSwitch(Thread* current, const CPU::InterruptRegisters* state); - void SysSleep(size_t secs); - void SysUSleep(size_t usecs); - void HandleSigIntHack(CPU::InterruptRegisters* regs); + PanicF("Running a terminated thread 0x%p", nextthread); } - - namespace Scheduler + addr_t newaddrspace = nextthread->process->addrspace; + if ( !Page::IsAligned(newaddrspace) ) { - byte dummythreaddata[sizeof(Thread)]; - Thread* dummythread; - Thread* currentthread; - Thread* idlethread; - Thread* firstrunnablethread; - Thread* firstsleepingthread; - Process* initprocess; - bool hacksigintpending = false; - - void Init() - { - // We use a dummy so that the first context switch won't crash when - // currentthread is accessed. This lets us avoid checking whether - // currentthread is NULL (which it only will be once) which gives - // simpler code. - dummythread = (Thread*) &dummythreaddata; - Maxsi::Memory::Set(dummythread, 0, sizeof(*dummythread)); - currentthread = dummythread; - firstrunnablethread = NULL; - firstsleepingthread = NULL; - idlethread = NULL; - hacksigintpending = false; - - Syscall::Register(SYSCALL_SLEEP, (void*) SysSleep); - Syscall::Register(SYSCALL_USLEEP, (void*) SysUSleep); - - addr_t stackhigher = Memory::GetKernelStack(); - size_t stacksize = Memory::GetKernelStackSize(); - addr_t stacklower = stackhigher - stacksize; - int prot = PROT_KREAD | PROT_KWRITE; - if ( !Memory::MapRange(stacklower, stacksize, prot) ) - { - PanicF("could not create kernel stack (%zx to %zx)", - stacklower, stackhigher); - } - - GDT::SetKernelStack(stacklower, stacksize, stackhigher); - } - - // The no operating thread is a thread stuck in an infinite loop that - // executes absolutely nothing, which is only run when the system has - // nothing to do. - void SetIdleThread(Thread* thread) - { - ASSERT(idlethread == NULL); - idlethread = thread; - SetThreadState(thread, Thread::State::NONE); - } - - void SetDummyThreadOwner(Process* process) - { - dummythread->process = process; - } - - void SetInitProcess(Process* init) - { - initprocess = init; - } - - Process* GetInitProcess() - { - return initprocess; - } - - void MainLoop() - { - // Wait for the first hardware interrupt to trigger a context switch - // into the first task! Then the init process should gracefully - // start executing. - while(true); - } - - void Switch(CPU::InterruptRegisters* regs) - { - LogBeginContextSwitch(currentthread, regs); - - if ( hacksigintpending ) { HandleSigIntHack(regs); } - - WakeSleeping(); - - Thread* nextthread = PopNextThread(); - if ( !nextthread ) { Panic("had no thread to switch to"); } - if ( nextthread->terminated ) { PanicF("Running a terminated thread 0x%p", nextthread); } - - LogContextSwitch(currentthread, nextthread); - if ( nextthread == currentthread ) { return; } - - currentthread->SaveRegisters(regs); - nextthread->LoadRegisters(regs); - - addr_t newaddrspace = nextthread->process->addrspace; - if ( unlikely(newaddrspace != Page::AlignDown(newaddrspace)) ) - { - PanicF("Thread 0x%p, process %i (0x%p) (backup: %i), had bad " - "address space variable: 0x%zx: not page-aligned " - "(backup: 0x%zx)\n", nextthread, - nextthread->process->pid, nextthread->process, - nextthread->pidbackup, newaddrspace, - nextthread->addrspacebackup); - } - Memory::SwitchAddressSpace(newaddrspace); - currentthread = nextthread; - - nextthread->HandleSignal(regs); - - LogEndContextSwitch(currentthread, regs); - - if ( currentthread->scfunc ) { Syscall::Resume(regs); } - } - - void ProcessTerminated(CPU::InterruptRegisters* regs) - { - currentthread = dummythread; - Switch(regs); - } - - const bool DEBUG_BEGINCTXSWITCH = false; - const bool DEBUG_CTXSWITCH = false; - const bool DEBUG_ENDCTXSWITCH = false; - - void LogBeginContextSwitch(Thread* current, const CPU::InterruptRegisters* state) - { - if ( DEBUG_BEGINCTXSWITCH && current->process->pid != 0 ) - { - Log::PrintF("Switching from 0x%p", current); - state->LogRegisters(); - Log::Print("\n"); - } - } - - void LogContextSwitch(Thread* current, Thread* next) - { - if ( DEBUG_CTXSWITCH && current != next ) - { - Log::PrintF("switching from %u:%u (0x%p) to %u:%u (0x%p) \n", - current->process->pid, 0, current, - next->process->pid, 0, next); - } - } - - void LogEndContextSwitch(Thread* current, const CPU::InterruptRegisters* state) - { - if ( DEBUG_ENDCTXSWITCH && current->process->pid != 0 ) - { - Log::PrintF("Switched to 0x%p", current); - state->LogRegisters(); - Log::Print("\n"); - } - } - - Thread* PopNextThread() - { - if ( !firstrunnablethread ) { return idlethread; } - Thread* result = firstrunnablethread; - firstrunnablethread = firstrunnablethread->schedulerlistnext; - return result; - } - - void SetThreadState(Thread* thread, Thread::State state) - { - if ( thread->state == state ) { return; } - - if ( thread->state == Thread::State::RUNNABLE ) - { - if ( thread == firstrunnablethread ) { firstrunnablethread = thread->schedulerlistnext; } - if ( thread == firstrunnablethread ) { firstrunnablethread = NULL; } - thread->schedulerlistprev->schedulerlistnext = thread->schedulerlistnext; - thread->schedulerlistnext->schedulerlistprev = thread->schedulerlistprev; - thread->schedulerlistprev = NULL; - thread->schedulerlistnext = NULL; - } - - // Insert the thread into the scheduler's carousel linked list. - if ( state == Thread::State::RUNNABLE ) - { - if ( firstrunnablethread == NULL ) { firstrunnablethread = thread; } - thread->schedulerlistprev = firstrunnablethread->schedulerlistprev; - thread->schedulerlistnext = firstrunnablethread; - firstrunnablethread->schedulerlistprev = thread; - thread->schedulerlistprev->schedulerlistnext = thread; - } - - thread->state = state; - } - - Thread::State GetThreadState(Thread* thread) - { - return thread->state; - } - - void PutThreadToSleep(Thread* thread, uintmax_t usecs) - { - SetThreadState(thread, Thread::State::BLOCKING); - thread->sleepuntil = Time::MicrosecondsSinceBoot() + usecs; - - // We use a simple linked linked list sorted after wake-up time to - // keep track of the threads that are sleeping. - - if ( firstsleepingthread == NULL ) - { - thread->nextsleepingthread = NULL; - firstsleepingthread = thread; - return; - } - - if ( thread->sleepuntil < firstsleepingthread->sleepuntil ) - { - thread->nextsleepingthread = firstsleepingthread; - firstsleepingthread = thread; - return; - } - - for ( Thread* tmp = firstsleepingthread; tmp != NULL; tmp = tmp->nextsleepingthread ) - { - if ( tmp->nextsleepingthread == NULL || - thread->sleepuntil < tmp->nextsleepingthread->sleepuntil ) - { - thread->nextsleepingthread = tmp->nextsleepingthread; - tmp->nextsleepingthread = thread; - return; - } - } - } - - void EarlyWakeUp(Thread* thread) - { - uintmax_t now = Time::MicrosecondsSinceBoot(); - if ( thread->sleepuntil < now ) { return; } - thread->sleepuntil = now; - - SetThreadState(thread, Thread::State::RUNNABLE); - - if ( firstsleepingthread == thread ) - { - firstsleepingthread = thread->nextsleepingthread; - thread->nextsleepingthread = NULL; - return; - } - - for ( Thread* tmp = firstsleepingthread; tmp->nextsleepingthread != NULL; tmp = tmp->nextsleepingthread ) - { - if ( tmp->nextsleepingthread == thread ) - { - tmp->nextsleepingthread = thread->nextsleepingthread; - thread->nextsleepingthread = NULL; - return; - } - } - } - - void WakeSleeping() - { - uintmax_t now = Time::MicrosecondsSinceBoot(); - - while ( firstsleepingthread && firstsleepingthread->sleepuntil < now ) - { - SetThreadState(firstsleepingthread, Thread::State::RUNNABLE); - Thread* next = firstsleepingthread->nextsleepingthread; - firstsleepingthread->nextsleepingthread = NULL; - firstsleepingthread = next; - } - } - - void HandleSigIntHack(CPU::InterruptRegisters* regs) - { - if ( currentthread == idlethread ) { return; } - - hacksigintpending = false; - - // HACK: Don't crash init or sh. - Process* process = CurrentProcess(); - if ( process->pid < 3 ) { return; } - - Sound::Mute(); - Log::PrintF("^C\n"); - - process->Exit(130); - currentthread = dummythread; - } - - void SigIntHack() - { - hacksigintpending = true; - } - - void SysSleep(size_t secs) - { - Thread* thread = currentthread; - uintmax_t timetosleep = ((uintmax_t) secs) * 1000ULL * 1000ULL; - if ( timetosleep == 0 ) - { - Switch(Syscall::InterruptRegs()); - Syscall::AsIs(); - return; - } - PutThreadToSleep(thread, timetosleep); - Syscall::Incomplete(); - } - - void SysUSleep(size_t usecs) - { - Thread* thread = currentthread; - uintmax_t timetosleep = usecs; - if ( timetosleep == 0 ) - { - Switch(Syscall::InterruptRegs()); - Syscall::AsIs(); - return; - } - PutThreadToSleep(thread, timetosleep); - Syscall::Incomplete(); - } + PanicF("Thread 0x%p, process %i (0x%p) (backup: %i), had bad " + "address space variable: 0x%zx: not page-aligned " + "(backup: 0x%zx)\n", nextthread, + nextthread->process->pid, nextthread->process, + -1/*nextthread->pidbackup*/, newaddrspace, + (addr_t)-1 /*nextthread->addrspacebackup*/); } + return nextthread; +} - Thread* CurrentThread() - { - return Scheduler::currentthread; - } +static void DoActualSwitch(CPU::InterruptRegisters* regs) +{ + Thread* current = CurrentThread(); + LogBeginSwitch(current, regs); - Process* CurrentProcess() + Thread* next = ValidatedPopNextThread(); + LogSwitch(current, next); + + if ( current == next ) { return; } + + current->SaveRegisters(regs); + next->LoadRegisters(regs); + + addr_t newaddrspace = next->addrspace; + Memory::SwitchAddressSpace(newaddrspace); + SetCurrentThread(next); + + addr_t stacklower = next->kernelstackpos; + size_t stacksize = next->kernelstacksize; + addr_t stackhigher = stacklower + stacksize; + ASSERT(stacklower && stacksize && stackhigher); + GDT::SetKernelStack(stacklower, stacksize, stackhigher); + + LogEndSwitch(next, regs); +} + +void Switch(CPU::InterruptRegisters* regs) +{ + DoActualSwitch(regs); + if ( regs->signal_pending && regs->InUserspace() ) + Signal::Dispatch(regs); +} + +const bool DEBUG_BEGINCTXSWITCH = false; +const bool DEBUG_CTXSWITCH = false; +const bool DEBUG_ENDCTXSWITCH = false; + +void LogBeginSwitch(Thread* current, const CPU::InterruptRegisters* regs) +{ + bool alwaysdebug = false; + bool isidlethread = current == idlethread; + bool dodebug = DEBUG_BEGINCTXSWITCH && !isidlethread; + if ( alwaysdebug || dodebug ) { - return Scheduler::currentthread->process; + Log::PrintF("Switching from 0x%p", current); + regs->LogRegisters(); + Log::Print("\n"); } } + +void LogSwitch(Thread* current, Thread* next) +{ + bool alwaysdebug = false; + bool different = current == idlethread; + bool dodebug = DEBUG_CTXSWITCH && different; + if ( alwaysdebug || dodebug ) + { + Log::PrintF("switching from %u:%u (0x%p) to %u:%u (0x%p) \n", + current->process->pid, 0, current, + next->process->pid, 0, next); + } +} + +void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs) +{ + bool alwaysdebug = false; + bool isidlethread = current == idlethread; + bool dodebug = DEBUG_BEGINCTXSWITCH && !isidlethread; + if ( alwaysdebug || dodebug ) + { + Log::PrintF("Switched to 0x%p", current); + regs->LogRegisters(); + Log::Print("\n"); + } +} + +static void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/) +{ + Switch(regs); +} + +static void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/) +{ + SetThreadState(currentthread, Thread::State::DEAD); + InterruptYieldCPU(regs, NULL); +} + +// The idle thread serves no purpose except being an infinite loop that does +// nothing, which is only run when the system has nothing to do. +void SetIdleThread(Thread* thread) +{ + ASSERT(!idlethread); + idlethread = thread; + SetThreadState(thread, Thread::State::NONE); + SetCurrentThread(thread); +} + +void SetDummyThreadOwner(Process* process) +{ + dummythread->process = process; +} + +void SetInitProcess(Process* init) +{ + initprocess = init; +} + +Process* GetInitProcess() +{ + return initprocess; +} + +void SetThreadState(Thread* thread, Thread::State state) +{ + bool wasenabled = Interrupt::SetEnabled(false); + + // Remove the thread from the list of runnable threads. + if ( thread->state == Thread::State::RUNNABLE && + state != Thread::State::RUNNABLE ) + { + if ( thread == firstrunnablethread ) { firstrunnablethread = thread->schedulerlistnext; } + if ( thread == firstrunnablethread ) { firstrunnablethread = NULL; } + ASSERT(thread->schedulerlistprev); + ASSERT(thread->schedulerlistnext); + thread->schedulerlistprev->schedulerlistnext = thread->schedulerlistnext; + thread->schedulerlistnext->schedulerlistprev = thread->schedulerlistprev; + thread->schedulerlistprev = NULL; + thread->schedulerlistnext = NULL; + } + + // Insert the thread into the scheduler's carousel linked list. + if ( thread->state != Thread::State::RUNNABLE && + state == Thread::State::RUNNABLE ) + { + if ( firstrunnablethread == NULL ) { firstrunnablethread = thread; } + thread->schedulerlistprev = firstrunnablethread->schedulerlistprev; + thread->schedulerlistnext = firstrunnablethread; + firstrunnablethread->schedulerlistprev = thread; + thread->schedulerlistprev->schedulerlistnext = thread; + } + + thread->state = state; + + ASSERT(thread->state != Thread::State::RUNNABLE || thread->schedulerlistprev); + ASSERT(thread->state != Thread::State::RUNNABLE || thread->schedulerlistnext); + + Interrupt::SetEnabled(wasenabled); +} + +Thread::State GetThreadState(Thread* thread) +{ + return thread->state; +} + +void SysSleep(size_t secs) +{ + uintmax_t timetosleep = ((uintmax_t) secs) * 1000ULL * 1000ULL; + uint32_t wakeat = Time::MicrosecondsSinceBoot() + timetosleep; + do { Yield(); } + while ( Time::MicrosecondsSinceBoot() < wakeat ); +} + +void SysUSleep(size_t usecs) +{ + uintmax_t timetosleep = (uintmax_t) usecs; + uint32_t wakeat = Time::MicrosecondsSinceBoot() + timetosleep; + do { Yield(); } + while ( Time::MicrosecondsSinceBoot() < wakeat ); +} + +extern "C" void yield_cpu_handler(); +extern "C" void thread_exit_handler(); + +void Init() +{ + // We use a dummy so that the first context switch won't crash when the + // current thread is accessed. This lets us avoid checking whether it is + // NULL (which it only will be once), which gives simpler code. + dummythread = (Thread*) &dummythreaddata; + Maxsi::Memory::Set(dummythread, 0, sizeof(*dummythread)); + dummythread->schedulerlistprev = dummythread; + dummythread->schedulerlistnext = dummythread; + currentthread = dummythread; + firstrunnablethread = NULL; + firstsleepingthread = NULL; + idlethread = NULL; + + // Register our raw handler with user-space access. It calls our real + // handler after common interrupt preparation stuff has occured. + Interrupt::RegisterRawHandler(129, yield_cpu_handler, true); + Interrupt::RegisterHandler(129, InterruptYieldCPU, NULL); + Interrupt::RegisterRawHandler(132, thread_exit_handler, true); + Interrupt::RegisterHandler(132, ThreadExitCPU, NULL); + + Syscall::Register(SYSCALL_SLEEP, (void*) SysSleep); + Syscall::Register(SYSCALL_USLEEP, (void*) SysUSleep); +} + +} // namespace Scheduler +} // namespace Sortix diff --git a/sortix/scheduler.h b/sortix/scheduler.h index 1cb5c22d..c8630e32 100644 --- a/sortix/scheduler.h +++ b/sortix/scheduler.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,40 +14,34 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . scheduler.h - Handles context switching between tasks and deciding when to execute what. + Decides the order to execute threads in and switching between them. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_SCHEDULER_H #define SORTIX_SCHEDULER_H #include "thread.h" -namespace Sortix -{ - namespace Scheduler - { - void Init(); - void MainLoop() SORTIX_NORETURN; - void Switch(CPU::InterruptRegisters* regs); - void ProcessTerminated(CPU::InterruptRegisters* regs); - void SetIdleThread(Thread* thread); - void SetDummyThreadOwner(Process* process); - void SetInitProcess(Process* init); - Process* GetInitProcess(); +namespace Sortix { +namespace Scheduler { - void SetThreadState(Thread* thread, Thread::State state); - Thread::State GetThreadState(Thread* thread); - void PutThreadToSleep(Thread* thread, uintmax_t usecs); - void EarlyWakeUp(Thread* thread); +void Init(); +void Switch(CPU::InterruptRegisters* regs); +inline static void Yield() { asm volatile ("int $129"); } +inline static void ExitThread() { asm volatile ("int $132"); } +void SetThreadState(Thread* thread, Thread::State state); +Thread::State GetThreadState(Thread* thread); +void SetIdleThread(Thread* thread); +void SetDummyThreadOwner(Process* process); +void SetInitProcess(Process* init); +Process* GetInitProcess(); - void SigIntHack(); - } -} +} // namespace Scheduler +} // namespace Sortix #endif - diff --git a/sortix/serialterminal.h b/sortix/serialterminal.h index 7e47968c..49bb94e0 100644 --- a/sortix/serialterminal.h +++ b/sortix/serialterminal.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . serialterminal.h A terminal on a serial line. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_SERIALTERMINAL_H #define SORTIX_SERIALTERMINAL_H diff --git a/sortix/signal.cpp b/sortix/signal.cpp index 94d869dd..665b37c0 100644 --- a/sortix/signal.cpp +++ b/sortix/signal.cpp @@ -1,6 +1,6 @@ /****************************************************************************** - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -23,222 +23,114 @@ ******************************************************************************/ #include -#include #include +#include +#include +#include "interrupt.h" +#include "thread.h" #include "signal.h" using namespace Maxsi; -namespace Sortix +namespace Sortix { + +// A per-cpu value whether a signal is pending in the running task. +extern "C" { volatile unsigned long asm_signal_is_pending = 0; } + +namespace Signal { + +const int PRIORITIES[SIG__NUM_DECLARED] = { - const int PRIORITY_NORMAL = 0; - const int PRIORITY_HIGH = 1; - const int PRIORITY_STOP = 2; - const int PRIORITY_CORE = 3; - const int PRIORITY_KILL = 4; + SIG_PRIO_NORMAL, // unused + SIG_PRIO_NORMAL, // SIGHUP + SIG_PRIO_NORMAL, // SIGINT + SIG_PRIO_NORMAL, // SIGQUIT + SIG_PRIO_CORE, // SIGILL + SIG_PRIO_CORE, // SIGTRAP + SIG_PRIO_CORE, // SIGABRT + SIG_PRIO_CORE, // SIGEMT + SIG_PRIO_CORE, // SIGFPE + SIG_PRIO_KILL, // SIGKILL + SIG_PRIO_CORE, // SIGBUS + SIG_PRIO_CORE, // SIGSEGV + SIG_PRIO_CORE, // SIGSYS + SIG_PRIO_NORMAL, // SIGPIPE + SIG_PRIO_NORMAL, // SIGALRM + SIG_PRIO_NORMAL, // SIGTERM + SIG_PRIO_NORMAL, // SIGUSR1 + SIG_PRIO_NORMAL, // SIGUSR2 + SIG_PRIO_NORMAL, // SIGCHLD + SIG_PRIO_HIGH, // SIGPWR + SIG_PRIO_NORMAL, // SIGWINCH + SIG_PRIO_NORMAL, // SIGURG + SIG_PRIO_NORMAL, // obsolete + SIG_PRIO_STOP, // SIGSTOP + SIG_PRIO_STOP, // SIGTSTP + SIG_PRIO_STOP, // SIGCONT + SIG_PRIO_STOP, // SIGTTIN + SIG_PRIO_STOP, // SIGTTOU + SIG_PRIO_NORMAL, // SIGVTALRM + SIG_PRIO_NORMAL, // obsolete + SIG_PRIO_CORE, // SIGXCPU + SIG_PRIO_CORE, // SIGXFSZ + SIG_PRIO_NORMAL, // SIGCORE + SIG_PRIO_NORMAL, // SIGLWP + SIG_PRIO_NORMAL, // SIGAIO +}; - const int PRIORITIES[Maxsi::Signal::NUMSIGNALS] = - { - PRIORITY_NORMAL, // unused - PRIORITY_NORMAL, // SIGHUP - PRIORITY_NORMAL, // SIGINT - PRIORITY_NORMAL, // SIGQUIT - PRIORITY_CORE, // SIGILL - PRIORITY_CORE, // SIGTRAP - PRIORITY_CORE, // SIGABRT - PRIORITY_CORE, // SIGEMT - PRIORITY_CORE, // SIGFPE - PRIORITY_KILL, // SIGKILL - PRIORITY_CORE, // SIGBUS - PRIORITY_CORE, // SIGSEGV - PRIORITY_CORE, // SIGSYS - PRIORITY_NORMAL, // SIGPIPE - PRIORITY_NORMAL, // SIGALRM - PRIORITY_NORMAL, // SIGTERM - PRIORITY_NORMAL, // SIGUSR1 - PRIORITY_NORMAL, // SIGUSR2 - PRIORITY_NORMAL, // SIGCHLD - PRIORITY_HIGH, // SIGPWR - PRIORITY_NORMAL, // SIGWINCH - PRIORITY_NORMAL, // SIGURG - PRIORITY_NORMAL, // obsolete - PRIORITY_STOP, // SIGSTOP - PRIORITY_STOP, // SIGTSTP - PRIORITY_STOP, // SIGCONT - PRIORITY_STOP, // SIGTTIN - PRIORITY_STOP, // SIGTTOU - PRIORITY_NORMAL, // SIGVTALRM - PRIORITY_NORMAL, // obsolete - PRIORITY_CORE, // SIGXCPU - PRIORITY_CORE, // SIGXFSZ - PRIORITY_NORMAL, // SIGCORE - PRIORITY_NORMAL, // SIGLWP - PRIORITY_NORMAL, // SIGAIO - }; - - // Returns true of the exact ordering of this signal version others aren't - // important - if it returns false, then this signal will be put in the - // queue according to priority, instead of being merged into another signal - // with the same signum. - bool Unifiable(int /*signum*/) - { - return true; - } - - // Returns whether a specific signal is more important to deliver than - // another. This is used to schedule signals. - int CompareSignalPriority(int siga, int sigb) - { - int prioa = PRIORITY_NORMAL; - int priob = PRIORITY_NORMAL; - - if ( siga < Maxsi::Signal::NUMSIGNALS ) { prioa = PRIORITIES[siga]; } - if ( sigb < Maxsi::Signal::NUMSIGNALS ) { priob = PRIORITIES[sigb]; } - - if ( prioa < priob ) { return -1; } else - if ( prioa > priob ) { return 1; } - return 0; - } - - SignalQueue::SignalQueue() - { - queue = NULL; - } - - SignalQueue::~SignalQueue() - { - while ( queue ) - { - Signal* todelete = queue; - queue = queue->nextsignal; - delete todelete; - } - } - - // Queues the signal and schedules it for processing. - bool SignalQueue::Push(int signum) - { - ASSERT(0 <= signum && signum < 128); - - if ( Unifiable(signum) ) - { - for ( Signal* signal = queue; signal != NULL; signal = signal->nextsignal ) - { - if ( signal->signum != signum ) { continue; } - - signal->numpending++; - return true; - } - } - - Signal* signal = new Signal; - if ( !signal ) { return false; } - - signal->signum = signum; - signal->numpending = 1; - signal->nextsignal = NULL; - signal->returncode = 128 + signum; - - Insert(signal); - - return true; - } - - // Insert the signal in O(N), which is pretty fast for small Ns. - void SignalQueue::Insert(Signal* signal) - { - if ( !queue ) - { - queue = signal; - last = signal; - return; - } - - // If the signal is to be inserted last, then just do it quickly. - if ( last != NULL && 0 <= CompareSignalPriority(last->signum, signal->signum) ) - { - last->nextsignal = signal; - signal->nextsignal = NULL; - last = signal; - return; - } - - // Check if the signal should be inserted first. - if ( queue != NULL && CompareSignalPriority(queue->signum, signal->signum) < 0 ) - { - signal->nextsignal = queue; - queue = signal; - return; - } - - // Find where the signal should be inserted. - for ( Signal* tmp = queue; tmp != NULL; tmp = tmp->nextsignal ) - { - Signal* next = tmp->nextsignal; - - if ( next != NULL && CompareSignalPriority(next->signum, signal->signum) < 0 ) - { - tmp->nextsignal = signal; - signal->nextsignal = next; - return; - } - - if ( next == NULL ) - { - tmp->nextsignal = signal; - signal->nextsignal = NULL; - last = signal; - return; - } - } - } - - // Given the stack of currently processing signals, return a new signal if - // it is more important to handle at this point. - Signal* SignalQueue::Pop(Signal* current) - { - if ( queue == NULL ) { return NULL; } - - bool returnqueue = false; - - // If we are currently handling no signal, then just return the first. - if ( current == NULL ) { returnqueue = true; } - - // If we are handling a signal, only override it with another if it is - // more important. - else if ( CompareSignalPriority(current->signum, queue->signum) < 0 ) - { - returnqueue = true; - } - - if ( returnqueue ) - { - Signal* result = queue; - queue = queue->nextsignal; - result->nextsignal = NULL; - return result; - } - - return NULL; - } - - Signal* Signal::Fork() - { - Signal* clone = new Signal(); - if ( !clone ) { return NULL; } - - Memory::Copy(clone, this, sizeof(Signal)); - - Signal* nextsignalclone = NULL; - if ( nextsignal ) - { - nextsignalclone = nextsignal->Fork(); - if ( !nextsignalclone ) { delete clone; return NULL; } - } - - clone->nextsignal = nextsignalclone; - - return clone; - } +int Priority(int signum) +{ + ASSERT(0 <= signum && signum < SIG_MAX_NUM); + if ( !signum ) + return -1; + if ( SIG__NUM_DECLARED <= signum ) + return SIG_PRIO_NORMAL; + return PRIORITIES[signum]; } +Queue::Queue() +{ + for ( int i = 1; i < SIG_MAX_NUM; i++ ) + pending[i] = false; +} + +void Queue::Push(int signum) +{ + ASSERT(0 < signum && signum < SIG_MAX_NUM); + pending[signum] = true; +} + +int Queue::Pop(int cursig) +{ + int best = 0; + int bestprio = Priority(cursig); + for ( int i = 1; i < SIG_MAX_NUM; i++ ) + if ( pending[i] && bestprio < Priority(i) ) + { + best = i; + bestprio = Priority(i); + } + pending[best] = false; + return best; +} + +void Dispatch(CPU::InterruptRegisters* regs, void* /*user*/) +{ + return CurrentThread()->HandleSignal(regs); +} + +void Return(CPU::InterruptRegisters* regs, void* /*user*/) +{ + return CurrentThread()->HandleSigreturn(regs); +} + +void Init() +{ + Interrupt::RegisterRawHandler(130, isr130, true); + Interrupt::RegisterHandler(130, Dispatch, NULL); + Interrupt::RegisterRawHandler(131, isr131, true); + Interrupt::RegisterHandler(131, Return, NULL); +} + +} // namespace Signal +} // namespace Sortix diff --git a/sortix/signal.h b/sortix/signal.h index c8683957..e3f6d2eb 100644 --- a/sortix/signal.h +++ b/sortix/signal.h @@ -1,6 +1,6 @@ /****************************************************************************** - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -25,76 +25,39 @@ #ifndef SORTIX_SIGNAL_H #define SORTIX_SIGNAL_H -#include #include "cpu.h" -namespace Sortix +namespace Sortix { + +extern "C" volatile unsigned long asm_signal_is_pending; + +namespace Signal { + +class Queue { - #define SIGHUP 1 /* Hangup */ - #define SIGINT 2 /* Interrupt */ - #define SIGQUIT 3 /* Quit */ - #define SIGILL 4 /* Illegal Instruction */ - #define SIGTRAP 5 /* Trace/Breakpoint Trap */ - #define SIGABRT 6 /* Abort */ - #define SIGEMT 7 /* Emulation Trap */ - #define SIGFPE 8 /* Arithmetic Exception */ - #define SIGKILL 9 /* Killed */ - #define SIGBUS 10 /* Bus Error */ - #define SIGSEGV 11 /* Segmentation Fault */ - #define SIGSYS 12 /* Bad System Call */ - #define SIGPIPE 13 /* Broken Pipe */ - #define SIGALRM 14 /* Alarm Clock */ - #define SIGTERM 15 /* Terminated */ - #define SIGUSR1 16 /* User Signal 1 */ - #define SIGUSR2 17 /* User Signal 2 */ - #define SIGCHLD 18 /* Child Status */ - #define SIGPWR 19 /* Power Fail/Restart */ - #define SIGWINCH 20 /* Window Size Change */ - #define SIGURG 21 /* Urgent Socket Condition */ - #define SIGSTOP 23 /* Stopped (signal) */ - #define SIGTSTP 24 /* Stopped (user) */ - #define SIGCONT 25 /* Continued */ - #define SIGTTIN 26 /* Stopped (tty input) */ - #define SIGTTOU 27 /* Stopped (tty output) */ - #define SIGVTALRM 28 /* Virtual Timer Expired */ - #define SIGXCPU 30 /* CPU time limit exceeded */ - #define SIGXFSZ 31 /* File size limit exceeded */ - #define SIGWAITING 32 /* All LWPs blocked */ - #define SIGLWP 33 /* Virtual Interprocessor Interrupt for Threads Library */ - #define SIGAIO 34 /* Asynchronous I/O */ +public: + Queue(); - struct Signal - { - int signum; - int returncode; - unsigned numpending; - CPU::InterruptRegisters regs; - Signal* nextsignal; +// TODO: This is the wrong data structure for the problem! +private: + bool pending[SIG_MAX_NUM]; - public: - Signal* Fork(); +// TODO: This is not SMP ready: +// To avoid race conditions, these should be called with interrupts off. +public: + void Push(int signum); + int Pop(int cursig); - }; +}; - class SignalQueue - { - public: - SignalQueue(); - ~SignalQueue(); +void Init(); +int Priority(int signum); +void Dispatch(CPU::InterruptRegisters* regs, void* user = NULL); +void Return(CPU::InterruptRegisters* regs, void* user = NULL); +inline bool IsPending() { return asm_signal_is_pending != 0; } - private: - Signal* queue; - Signal* last; - - public: - bool Push(int signum); - Signal* Pop(Signal* current); - - private: - void Insert(Signal* signal); - - }; -} +} // namespace Signal +} // namespace Sortix #endif diff --git a/sortix/syscall.cpp b/sortix/syscall.cpp index a98f6564..bd865ab0 100644 --- a/sortix/syscall.cpp +++ b/sortix/syscall.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . syscall.h - Handles system calls from userspace safely. + Handles system calls from userspace. -******************************************************************************/ +*******************************************************************************/ #include #include @@ -39,15 +39,14 @@ namespace Sortix { extern "C" { - CPU::SyscallRegisters* syscall_state_ptr; - unsigned system_was_incomplete; size_t SYSCALL_MAX; volatile void* syscall_list[SYSCALL_MAX_NUM]; } int BadSyscall() { - // TODO: Send signal, set errno, or crash/abort process? + Log::PrintF("I am the bad system call!\n"); + // TODO: Send signal, set errnx o, or crash/abort process? return -1; } @@ -70,87 +69,6 @@ namespace Sortix syscall_list[index] = funcptr; } - - void Incomplete() - { - Thread* thread = CurrentThread(); - - system_was_incomplete = 1; - - CPU::InterruptRegisters* regs = InterruptRegs(); - thread->SaveRegisters(regs); - Scheduler::SetThreadState(thread, Thread::State::BLOCKING); - Scheduler::Switch(regs); - } - - void Yield() - { - Panic("Syscall::Yield() is not implemented because it caused " - "instability and other issues."); - } - - void AsIs() - { - system_was_incomplete = 1; - } - - void ScheduleResumption(Thread* thread) - { - Scheduler::SetThreadState(thread, Thread::State::RUNNABLE); - } - - extern "C" void update_userspace_errno() - { - int error = Error::Last(); - if ( !error ) { return; } - Process* process = CurrentProcess(); - if ( !process->errnop ) { return; } - // TODO: Validate that process->errno is in userspace memory! - *process->errnop = error; - } - - extern "C" size_t resume_syscall(void* scfunc, size_t scsize, size_t* scstate); - - void Resume(CPU::InterruptRegisters* regs) - { - Thread* thread = CurrentThread(); - - syscall_state_ptr = (CPU::SyscallRegisters*) regs; - - ASSERT(thread->scfunc); - - size_t* scstate = thread->scstate; - size_t scsize = thread->scsize; - void* scfunc = thread->scfunc; - - system_was_incomplete = 0; - Error::Set(0); - - size_t result = resume_syscall(scfunc, scsize, scstate); - - bool incomplete = (system_was_incomplete); - - system_was_incomplete = 1; - - if ( !incomplete ) - { - syscall_state_ptr->result = result; - update_userspace_errno(); - return; - } - - Incomplete(); - } - - CPU::InterruptRegisters* InterruptRegs() - { - return (CPU::InterruptRegisters*) syscall_state_ptr; - } - - CPU::SyscallRegisters* SyscallRegs() - { - return syscall_state_ptr; - } } } diff --git a/sortix/syscall.h b/sortix/syscall.h index c0dffef8..a2d20424 100644 --- a/sortix/syscall.h +++ b/sortix/syscall.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . syscall.h - Handles system calls from userspace safely. + Handles system calls from userspace. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_SYSCALL_H #define SORTIX_SYSCALL_H @@ -36,33 +36,6 @@ namespace Sortix { void Init(); void Register(size_t index, void* funcptr); - - // Aborts the current system call such that the current thread is marked - // as blocking, and control is transferred to the next runnable thread. - // This allows the thread to wait for an condition that makes the thread - // runnable again and stores the return value in the proper register. - // Once called, you may safely return from the system call in good faith - // that its return value shall be discarded. - void Incomplete(); - - // Call this prior to Incomplete() to signal that the scheduler should - // go run something else for a moment. The current thread will not be - // marked as blocking. - void Yield(); - - // For when you want the syscall exit code not to modify registers. - void AsIs(); - - // Retries a system call by making the thread runnable and then calling - // the system call code whenever the thread is scheduled to run. - void ScheduleResumption(Thread* thread); - - // Retries a system call based on the Thread::sc* values of the current - // thread and if it succeeds, sets the proper registers. - void Resume(CPU::InterruptRegisters* regs); - - CPU::InterruptRegisters* InterruptRegs(); - CPU::SyscallRegisters* SyscallRegs(); } } diff --git a/sortix/textterminal.cpp b/sortix/textterminal.cpp index 87aeaaaa..8a5c3bf3 100644 --- a/sortix/textterminal.cpp +++ b/sortix/textterminal.cpp @@ -36,6 +36,7 @@ const uint16_t ATTR_CHAR = 1U << 0U; TextTerminal::TextTerminal(TextBufferHandle* textbufhandle) { this->textbufhandle = textbufhandle; textbufhandle->Refer(); + this->termlock = KTHREAD_MUTEX_INITIALIZER; Reset(); } @@ -57,6 +58,7 @@ void TextTerminal::Reset() size_t TextTerminal::Print(const char* string, size_t stringlen) { + ScopedLock lock(&termlock); TextBuffer* textbuf = textbufhandle->Acquire(); for ( size_t i = 0; i < stringlen; i++ ) PutChar(textbuf, string[i]); @@ -67,6 +69,7 @@ size_t TextTerminal::Print(const char* string, size_t stringlen) size_t TextTerminal::Width() const { + ScopedLock lock(&termlock); TextBuffer* textbuf = textbufhandle->Acquire(); size_t width = textbuf->Width(); textbufhandle->Release(textbuf); @@ -75,6 +78,7 @@ size_t TextTerminal::Width() const size_t TextTerminal::Height() const { + ScopedLock lock(&termlock); TextBuffer* textbuf = textbufhandle->Acquire(); size_t height = textbuf->Height(); textbufhandle->Release(textbuf); diff --git a/sortix/textterminal.h b/sortix/textterminal.h index 90738bcd..7c42ada5 100644 --- a/sortix/textterminal.h +++ b/sortix/textterminal.h @@ -25,6 +25,8 @@ #ifndef SORTIX_TEXTTERMINAL_H #define SORTIX_TEXTTERMINAL_H +#include + namespace Sortix { class TextBufferHandle; @@ -51,6 +53,7 @@ private: private: mutable TextBufferHandle* textbufhandle; + mutable kthread_mutex_t termlock; uint8_t vgacolor; unsigned column; unsigned line; diff --git a/sortix/thread.cpp b/sortix/thread.cpp index 83ef1901..a1733d4b 100644 --- a/sortix/thread.cpp +++ b/sortix/thread.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,23 +14,25 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . thread.cpp Describes a thread belonging to a process. -******************************************************************************/ +*******************************************************************************/ #include +#include +#include #include +#include #include #include -#include "event.h" #include "process.h" #include "thread.h" #include "scheduler.h" -#include +#include "interrupt.h" #include "time.h" #include "syscall.h" @@ -44,135 +46,64 @@ namespace Sortix process = NULL; prevsibling = NULL; nextsibling = NULL; - event = NULL; - eventnextwaiting = NULL; - sleepuntil = 0; - nextsleepingthread = NULL; schedulerlistprev = NULL; schedulerlistnext = NULL; state = NONE; Maxsi::Memory::Set(®isters, 0, sizeof(registers)); - ready = false; - scfunc = NULL; - currentsignal = NULL; + stackpos = 0; + stacksize = 0; + kernelstackpos = 0; + kernelstacksize = 0; + kernelstackmalloced = false; + currentsignal = 0; + siglevel = 0; sighandler = NULL; - pidbackup = -1; - addrspacebackup = 0UL; terminated = false; - ResetCallbacks(); - } - - Thread::Thread(const Thread* forkfrom) - { - id = forkfrom->id; - process = NULL; - prevsibling = NULL; - nextsibling = NULL; - state = forkfrom->state; - event = NULL; - eventnextwaiting = NULL; - sleepuntil = forkfrom->sleepuntil; - Maxsi::Memory::Copy(®isters, &forkfrom->registers, sizeof(registers)); - ready = false; - stackpos = forkfrom->stackpos; - stacksize = forkfrom->stacksize; - nextsleepingthread = NULL; - schedulerlistprev = NULL; - schedulerlistnext = NULL; - scfunc = NULL; - sighandler = forkfrom->sighandler; - pidbackup = -1; - addrspacebackup = 0UL; - terminated = false; - ResetCallbacks(); - } - - void Thread::ResetCallbacks() - { - onchildprocessexit = NULL; } Thread::~Thread() { - ASSERT(CurrentProcess() == process); - ASSERT(nextsleepingthread == NULL); - - if ( event ) { event->Unregister(this); } - - // Delete information about signals being processed. - while ( currentsignal ) - { - Signal* todelete = currentsignal; - currentsignal = currentsignal->nextsignal; - delete todelete; - } - - Memory::UnmapRange(stackpos, stacksize); - + if ( process ) + process->OnThreadDestruction(this); + ASSERT(CurrentThread() != this); + if ( kernelstackmalloced ) + delete[] (uint8_t*) kernelstackpos; terminated = true; } - Thread* Thread::Fork() + addr_t Thread::SwitchAddressSpace(addr_t newaddrspace) { - ASSERT(ready); - - Signal* clonesignal = NULL; - if ( currentsignal ) - { - clonesignal = currentsignal->Fork(); - if ( !clonesignal ) { return NULL; } - } - - Thread* clone = new Thread(this); - if ( !clone ) - { - while ( clonesignal ) - { - Signal* todelete = clonesignal; - clonesignal = clonesignal->nextsignal; - delete todelete; - } - return NULL; - } - - clone->currentsignal = clonesignal; - - return clone; + bool wasenabled = Interrupt::SetEnabled(false); + addr_t result = addrspace; + addrspace = newaddrspace; + Memory::SwitchAddressSpace(newaddrspace); + Interrupt::SetEnabled(wasenabled); + return result; } - void CreateThreadCPU(Thread* thread, addr_t entry); - - Thread* CreateThread(addr_t entry, size_t stacksize) + // Last chance to clean up user-space things before this thread dies. + void Thread::LastPrayer() { - Process* process = CurrentProcess(); + Memory::UnmapRange(stackpos, stacksize); + Memory::Flush(); + } - if ( stacksize == 0 ) { stacksize = process->DefaultStackSize(); } + extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry) + { + entry(user); + kthread_exit(); + } - // TODO: Find some unused virtual address space of the needed size - // somewhere in the current process. - addr_t stackpos = process->AllocVirtualAddr(stacksize); - if ( !stackpos ) { return NULL; } + Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs) + { + ASSERT(process && regs && process->addrspace); + Thread* thread = new Thread; + if ( !thread ) { return NULL; } - int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE; - if ( !Memory::MapRange(stackpos, stacksize, prot) ) - { - // TODO: Free the reserved virtual memory area. - return NULL; - } + thread->addrspace = process->addrspace; + thread->SaveRegisters(regs); - Thread* thread = new Thread(); - if ( !thread ) - { - Memory::UnmapRange(stackpos, stacksize); - // TODO: Free the reserved virtual memory area. - return NULL; - } - - thread->stackpos = stackpos; - thread->stacksize = stacksize; - - // Set up the thread state registers. - CreateThreadCPU(thread, entry); + kthread_mutex_lock(&process->threadlock); // Create the family tree. thread->process = process; @@ -181,63 +112,128 @@ namespace Sortix thread->nextsibling = firsty; process->firstthread = thread; - thread->Ready(); - - Scheduler::SetThreadState(thread, Thread::State::RUNNABLE); + kthread_mutex_unlock(&process->threadlock); return thread; } - void Thread::Ready() + Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user, + size_t stacksize) { - if ( ready ) { return; } - ready = true; + const size_t DEFAULT_KERNEL_STACK_SIZE = 64*8192UL; + if ( !stacksize ) { stacksize = DEFAULT_KERNEL_STACK_SIZE; } + uint8_t* stack = new uint8_t[stacksize]; + if ( !stack ) { return NULL; } - this->pidbackup = process->pid; - this->addrspacebackup = process->addrspace; + CPU::InterruptRegisters regs; + SetupKernelThreadRegs(®s, entry, user, (addr_t) stack, stacksize); - if ( Time::MicrosecondsSinceBoot() < sleepuntil ) - { - uintmax_t howlong = sleepuntil - Time::MicrosecondsSinceBoot(); - Scheduler::PutThreadToSleep(this, howlong); - } - else if ( state == State::RUNNABLE ) - { - state = State::NONE; // Since we are in no linked list. - Scheduler::SetThreadState(this, State::RUNNABLE); - } + Thread* thread = CreateKernelThread(process, ®s); + if ( !thread ) { delete[] stack; } + + thread->kernelstackpos = (addr_t) stack; + thread->kernelstacksize = stacksize; + thread->kernelstackmalloced = true; + + return thread; + } + + Thread* CreateKernelThread(ThreadEntry entry, void* user, size_t stacksize) + { + return CreateKernelThread(CurrentProcess(), entry, user, stacksize); + } + + void StartKernelThread(Thread* thread) + { + Scheduler::SetThreadState(thread, Thread::State::RUNNABLE); + } + + Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs) + { + Thread* thread = CreateKernelThread(process, regs); + if ( !thread ) { return NULL; } + StartKernelThread(thread); + return thread; + } + + Thread* RunKernelThread(Process* process, ThreadEntry entry, void* user, + size_t stacksize) + { + Thread* thread = CreateKernelThread(process, entry, user, stacksize); + if ( !thread ) { return NULL; } + StartKernelThread(thread); + return thread; + } + + Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize) + { + Thread* thread = CreateKernelThread(entry, user, stacksize); + if ( !thread ) { return NULL; } + StartKernelThread(thread); + return thread; } void Thread::HandleSignal(CPU::InterruptRegisters* regs) { - Signal* override = signalqueue.Pop(currentsignal); - if ( !override ) { return; } - if ( !sighandler ) { delete override; return; } + int signum = signalqueue.Pop(currentsignal); + regs->signal_pending = 0; - if ( override->signum == SIGKILL ) + if ( !signum ) + return; + + if ( !sighandler ) + return; + + if ( SIG_NUM_LEVELS <= siglevel ) + return; + + // Signals can't return to kernel mode because the kernel stack may have + // been overwritten by a system call during the signal handler. Correct + // the return state so it returns to userspace and not the kernel. + if ( !regs->InUserspace() ) + HandleSignalFixupRegsCPU(regs); + + if ( signum == SIGKILL ) { - + // We need to run the OnSigKill method here with interrupts enabled + // and on our own stack. But this method this may have been called + // from the scheduler on any stack, so we need to do a little + // bootstrap and switch to our own stack. + GotoOnSigKill(regs); + return; } - - override->nextsignal = currentsignal; - Maxsi::Memory::Copy(&override->regs, regs, sizeof(override->regs)); - currentsignal = override; + int level = siglevel++; + signums[level] = currentsignal = signum; + Maxsi::Memory::Copy(sigregs + level, regs, sizeof(*regs)); HandleSignalCPU(regs); } - void SysSigReturn() + void Thread::HandleSigreturn(CPU::InterruptRegisters* regs) { - Thread* thread = CurrentThread(); - if ( !thread->currentsignal ) { return; } + if ( !siglevel ) + return; - CPU::InterruptRegisters* dest = Syscall::InterruptRegs(); - CPU::InterruptRegisters* src = &thread->currentsignal->regs; + siglevel--; - Maxsi::Memory::Copy(dest, src, sizeof(CPU::InterruptRegisters)); - thread->currentsignal = thread->currentsignal->nextsignal; - Syscall::AsIs(); + currentsignal = siglevel ? signums[siglevel-1] : 0; + Maxsi::Memory::Copy(regs, sigregs + siglevel, sizeof(*regs)); + regs->signal_pending = 0; + + // Check if a more important signal is pending. + HandleSignal(regs); + } + + extern "C" void Thread__OnSigKill(Thread* thread) + { + thread->OnSigKill(); + } + + void Thread::OnSigKill() + { + LastPrayer(); + kthread_exit(); } void SysRegisterSignalHandler(sighandler_t sighandler) @@ -245,13 +241,38 @@ namespace Sortix CurrentThread()->sighandler = sighandler; } + void Thread::SetHavePendingSignals() + { + // TODO: This doesn't really work if Interrupt::IsCPUInterrupted()! + if ( CurrentThread() == this ) + asm_signal_is_pending = 1; + else + registers.signal_pending = 1; + } + + bool Thread::DeliverSignal(int signum) + { + if ( signum <= 0 || 128 <= signum ) { Error::Set(EINVAL); return false; } + + bool wasenabled = Interrupt::SetEnabled(false); + signalqueue.Push(signum); + SetHavePendingSignals(); + Interrupt::SetEnabled(wasenabled); + + return true; + } + int SysKill(pid_t pid, int signum) { - if ( signum < 0 || 128 <= signum ) { Error::Set(EINVAL); return -1; } - // Protect the system idle process. - if ( pid == 0 ) { Error::Set(EPERM); return -1; } + if ( !pid ) { Error::Set(EPERM); return -1; } + // If we kill our own process, deliver the signal to this thread. + Thread* currentthread = CurrentThread(); + if ( currentthread->process->pid == pid ) + return currentthread->DeliverSignal(signum) ? 0 : -1; + + // TODO: Race condition: The process could be deleted while we use it. Process* process = Process::Get(pid); if ( !process ) { Error::Set(ESRCH); return -1; } @@ -259,31 +280,18 @@ namespace Sortix // TODO: Check for permission. // TODO: Check for zombies. - Thread* currentthread = CurrentThread(); - Thread* thread = NULL; - if ( currentthread->process->pid == pid ) { thread = currentthread; } - if ( !thread ) { thread = process->firstthread; } - if ( !thread ) { Error::Set(ESRCH); return -1; /* TODO: Zombie? */ } + return process->DeliverSignal(signum) ? 0 : -1; + } - // TODO: If thread is not runnable, wake it and runs its handler? - if ( !thread->signalqueue.Push(signum) ) - { - // TODO: Possibly kill the process? - } - - if ( thread == currentthread ) - { - Syscall::SyscallRegs()->result = 0; - thread->HandleSignal(Syscall::InterruptRegs()); - } - - return 0; + int SysRaise(int signum) + { + return CurrentThread()->DeliverSignal(signum) ? 0 : -1; } void Thread::Init() { Syscall::Register(SYSCALL_KILL, (void*) SysKill); + Syscall::Register(SYSCALL_RAISE, (void*) SysRaise); Syscall::Register(SYSCALL_REGISTER_SIGNAL_HANDLER, (void*) SysRegisterSignalHandler); - Syscall::Register(SYSCALL_SIGRETURN, (void*) SysSigReturn); } } diff --git a/sortix/thread.h b/sortix/thread.h index 14f0b25d..ebe3327a 100644 --- a/sortix/thread.h +++ b/sortix/thread.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,45 +14,75 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . thread.h Describes a thread belonging to a process. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_THREAD_H #define SORTIX_THREAD_H +#include #include "signal.h" +typedef struct multiboot_info multiboot_info_t; + namespace Sortix { - class Event; class Process; class Thread; - // Adds a thread to the current process. - Thread* CreateThread(addr_t entry, size_t stacksize = 0); - Thread* CurrentThread(); + extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo); + + typedef void (*ThreadEntry)(void* user); + + // Simply exits the kernel thread. + void KernelThreadExit() SORTIX_NORETURN; + + // Internally used as a kernel thread entry point that exits the thread + // upon the actual thread entry returning. + extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry) SORTIX_NORETURN; + + // These functions create a new kernel process but doesn't start it. + Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs); + Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user, + size_t stacksize = 0); + Thread* CreateKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0); + + // This function can be used to start a thread from the above functions. + void StartKernelThread(Thread* thread); + + // Alternatively, these functions both create and start the thread. + Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs); + Thread* RunKernelThread(Process* process, ThreadEntry entry, void* user, + size_t stacksize = 0); + Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0); + + void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry, + void* user, addr_t stack, size_t stacksize); + + extern "C" void Thread__OnSigKill(Thread* thread); typedef void (*sighandler_t)(int); class Thread { - friend Thread* CreateThread(addr_t entry, size_t stacksize); + friend Thread* CreateKernelThread(Process* process, + CPU::InterruptRegisters* regs); + friend void KernelInit(unsigned long magic, multiboot_info_t* bootinfo); + friend void Thread__OnSigKill(Thread* thread); public: - enum State { NONE, RUNNABLE, BLOCKING }; + enum State { NONE, RUNNABLE, BLOCKING, DEAD }; public: static void Init(); private: Thread(); - Thread(const Thread* forkfrom); - void ResetCallbacks(); public: ~Thread(); @@ -60,64 +90,53 @@ namespace Sortix public: size_t id; Process* process; - pid_t pidbackup; - addr_t addrspacebackup; bool terminated; Thread* prevsibling; Thread* nextsibling; - // These are used internally when a thread is waiting for an Event to - // happen. Consider them private. - public: - Event* event; - Thread* eventnextwaiting; - // These are some things used internally by the scheduler and should not be // touched by anything but it. Consider it private. public: Thread* schedulerlistprev; Thread* schedulerlistnext; - State state; - uintmax_t sleepuntil; - Thread* nextsleepingthread; + volatile State state; public: + addr_t addrspace; addr_t stackpos; size_t stacksize; - Signal* currentsignal; - SignalQueue signalqueue; sighandler_t sighandler; - - // After being created/forked, a thread is not inserted into the scheduler. - // Whenever the thread has been safely established within a process, then - // call Ready() to finalize the creation and insert it into the scheduler. - private: - bool ready; - - public: - void Ready(); + addr_t kernelstackpos; + size_t kernelstacksize; + bool kernelstackmalloced; private: CPU::InterruptRegisters registers; + Signal::Queue signalqueue; + int currentsignal; + int siglevel; + int signums[SIG_NUM_LEVELS]; + CPU::InterruptRegisters sigregs[SIG_NUM_LEVELS]; public: - Thread* Fork(); void SaveRegisters(const CPU::InterruptRegisters* src); void LoadRegisters(CPU::InterruptRegisters* dest); void HandleSignal(CPU::InterruptRegisters* regs); + void HandleSigreturn(CPU::InterruptRegisters* regs); + bool DeliverSignal(int signum); + addr_t SwitchAddressSpace(addr_t newaddrspace); private: + void GotoOnSigKill(CPU::InterruptRegisters* regs); + void OnSigKill() SORTIX_NORETURN; + void LastPrayer(); + void SetHavePendingSignals(); + void HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs); void HandleSignalCPU(CPU::InterruptRegisters* regs); - public: - void* scfunc; - size_t scsize; - size_t scstate[8]; - - public: - void (*onchildprocessexit)(Thread*, Process*); - }; + + Thread* CurrentThread(); } #endif diff --git a/sortix/x64/interrupt.s b/sortix/x64/interrupt.s index 8832d641..7f3af21d 100644 --- a/sortix/x64/interrupt.s +++ b/sortix/x64/interrupt.s @@ -1,6 +1,6 @@ /******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -255,6 +255,20 @@ isr128: pushq $0 # err_code pushq $128 # int_no jmp interrupt_handler_prepare +.global isr130 +.type isr130, @function +isr130: + cli + pushq $0 # err_code + pushq $130 # int_no + jmp interrupt_handler_prepare +.global isr131 +.type isr131, @function +isr131: + cli + pushq $0 # err_code + pushq $131 # int_no + jmp interrupt_handler_prepare .global irq0 .type irq0, @function irq0: @@ -367,8 +381,24 @@ irq15: pushq $0 # err_code pushq $47 # int_no jmp interrupt_handler_prepare +.global yield_cpu_handler +.type yield_cpu_handler, @function +yield_cpu_handler: + cli + pushq $0 # err_code + pushq $129 # int_no + jmp interrupt_handler_prepare +.global thread_exit_handler +.type thread_exit_handler, @function +thread_exit_handler: + cli + pushq $0 # err_code + pushq $132 # int_no + jmp interrupt_handler_prepare interrupt_handler_prepare: + movq $1, asm_is_cpu_interrupted + pushq %r15 pushq %r14 pushq %r13 @@ -401,10 +431,27 @@ interrupt_handler_prepare: movq %cr2, %rbp pushq %rbp + # Push the current kernel errno value. + movl global_errno, %ebp + pushq %rbp + + # Push whether a signal is pending. + movq asm_signal_is_pending, %rbp + pushq %rbp + # Now call the interrupt handler. movq %rsp, %rdi call interrupt_handler +load_interrupted_registers: + # Restore whether signals are pending. + popq %rbp + movq %rbp, asm_signal_is_pending + + # Restore the previous kernel errno. + popq %rbp + movl %ebp, global_errno + # Remove CR2 from the stack. addq $8, %rsp @@ -418,7 +465,7 @@ interrupt_handler_prepare: popq %rdi popq %rsi popq %rbp - popq %rsp + addq $8, %rsp # Don't pop %rsp, may not be defined. popq %rbx popq %rdx popq %rcx @@ -435,6 +482,8 @@ interrupt_handler_prepare: # Remove int_no and err_code addq $16, %rsp + movq $0, asm_is_cpu_interrupted + # Return to where we came from. iretq @@ -451,3 +500,10 @@ asm_interrupts_are_enabled: andq $0x000200, %rax # FLAGS_INTERRUPT retq +.global load_registers +.type load_registers, @function +load_registers: + # Let the register struct become our temporary stack + movq %rdi, %rsp + jmp load_interrupted_registers + diff --git a/sortix/x64/kthread.s b/sortix/x64/kthread.s new file mode 100644 index 00000000..3921e9bd --- /dev/null +++ b/sortix/x64/kthread.s @@ -0,0 +1,85 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + x64/kthread.s + Utilities and synchronization mechanisms for x64 kernel threads. + +*******************************************************************************/ + +.section .text + +.global kthread_mutex_trylock +.type kthread_mutex_trylock, @function +kthread_mutex_trylock: + pushq %rbp + movq %rsp, %rbp + movl $-1, %eax + xchgl (%rdi), %eax + not %eax + leaveq + retq + +.global kthread_mutex_lock +.type kthread_mutex_lock, @function +kthread_mutex_lock: + pushq %rbp + movq %rsp, %rbp +kthread_mutex_lock_retry: + movl $-1, %eax + xchgl (%rdi), %eax + testl %eax, %eax + jnz kthread_mutex_lock_failed + leaveq + retq +kthread_mutex_lock_failed: + int $0x81 # Yield the CPU. + jmp kthread_mutex_lock_retry + +.global kthread_mutex_lock_signal +.type kthread_mutex_lock_signal, @function +kthread_mutex_lock_signal: + pushq %rbp + movq %rsp, %rbp +kthread_mutex_lock_signal_retry: + movq asm_signal_is_pending, %rax + testq %rax, %rax + jnz kthread_mutex_lock_signal_pending + movl $-1, %eax + xchgl (%rdi), %eax + testl %eax, %eax + jnz kthread_mutex_lock_signal_failed + inc %eax +kthread_mutex_lock_signal_out: + leaveq + retq +kthread_mutex_lock_signal_failed: + int $0x81 # Yield the CPU. + jmp kthread_mutex_lock_signal_retry +kthread_mutex_lock_signal_pending: + xorl %eax, %eax + jmp kthread_mutex_lock_signal_out + +.global kthread_mutex_unlock +.type kthread_mutex_unlock, @function +kthread_mutex_unlock: + pushq %rbp + movq %rsp, %rbp + movl $0, (%rdi) + leaveq + retq diff --git a/sortix/x64/memorymanagement.cpp b/sortix/x64/memorymanagement.cpp index 0473adc0..5a073c5c 100644 --- a/sortix/x64/memorymanagement.cpp +++ b/sortix/x64/memorymanagement.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -23,11 +23,11 @@ *******************************************************************************/ #include +#include #include #include "multiboot.h" -#include -#include #include "x86-family/memorymanagement.h" +#include "interrupt.h" namespace Sortix { @@ -120,17 +120,18 @@ namespace Sortix PML* pml = PMLS[level] + offset; for ( size_t i = 0; i < ENTRIES; i++ ) { - if ( !(pml->entry[i] & PML_PRESENT) ) { continue; } - if ( !(pml->entry[i] & PML_USERSPACE) ) { continue; } - if ( !(pml->entry[i] & PML_FORK) ) { continue; } + addr_t entry = pml->entry[i]; + if ( !(entry & PML_PRESENT) ) { continue; } + if ( !(entry & PML_USERSPACE) ) { continue; } + if ( !(entry & PML_FORK) ) { continue; } if ( level > 1 ) { RecursiveFreeUserspacePages(level-1, offset * ENTRIES + i); } addr_t addr = pml->entry[i] & PML_ADDRESS; - pml->entry[i] = 0; - Page::Put(addr); + // No need to unmap the page, we just need to mark it as unused. + Page::PutUnlocked(addr); } } - void DestroyAddressSpace() + void DestroyAddressSpace(addr_t fallback, void (*func)(addr_t, void*), void* user) { // Look up the last few entries used for the fractal mapping. These // cannot be unmapped as that would destroy the world. Instead, we @@ -143,17 +144,31 @@ namespace Sortix addr_t fractal1 = (PMLS[2] + 510UL * 512UL + 510UL)->entry[510]; addr_t dir = currentdir; - // First let's do the safe part. Garbage collect any PML1/0's left - // behind by user-space. These are completely safe to delete. + // We want to free the pages, but we are still using them ourselves, + // so lock the page allocation structure until we are done. + Page::Lock(); + + // In case any pages wasn't cleaned at this point. +#warning Page::Put calls may internally Page::Get and then reusing pages we are not done with just yet RecursiveFreeUserspacePages(TOPPMLLEVEL, 0); // Switch to the address space from when the world was originally // created. It should contain the kernel, the whole kernel, and // nothing but the kernel. PML* const BOOTPML4 = (PML* const) 0x21000UL; - SwitchAddressSpace((addr_t) BOOTPML4); + if ( !fallback ) + fallback = (addr_t) BOOTPML4; - // Now safely mark the pages as unused. + if ( func ) + func(fallback, user); + else + SwitchAddressSpace(fallback); + + // Ok, now we got marked everything left behind as unused, we can + // now safely let another thread use the pages. + Page::Unlock(); + + // These are safe to free since we switched address space. Page::Put(fractal3 & PML_ADDRESS); Page::Put(fractal2 & PML_ADDRESS); Page::Put(fractal1 & PML_ADDRESS); diff --git a/sortix/x64/process.cpp b/sortix/x64/process.cpp index 9de05cf9..dd74a3cb 100644 --- a/sortix/x64/process.cpp +++ b/sortix/x64/process.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,15 +14,17 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . x64/process.cpp CPU-specific process code. -******************************************************************************/ +*******************************************************************************/ #include +#include +#include #include "process.h" namespace Sortix @@ -31,12 +33,47 @@ namespace Sortix addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs) { + const uint64_t CS = 0x18; + const uint64_t DS = 0x20; + const uint64_t RPL = 0x3; + regs->rdi = argc; regs->rsi = (size_t) argv; regs->rdx = envc; regs->rcx = (size_t) envp; regs->rip = entry; regs->userrsp = stackpos & ~(15UL); - regs->rbp = stackpos; + regs->rbp = regs->userrsp; + regs->cs = CS | RPL; + regs->ds = DS | RPL; + regs->ss = DS | RPL; + regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + } + + void InitializeThreadRegisters(CPU::InterruptRegisters* regs, + const sforkregs_t* requested) + { + Maxsi::Memory::Set(regs, 0, sizeof(*regs)); + regs->rip = requested->rip; + regs->userrsp = requested->rsp; + regs->rax = requested->rax; + regs->rbx = requested->rbx; + regs->rcx = requested->rcx; + regs->rdx = requested->rdx; + regs->rdi = requested->rdi; + regs->rsi = requested->rsi; + regs->rbp = requested->rbp; + regs->r8 = requested->r8; + regs->r9 = requested->r9; + regs->r10 = requested->r10; + regs->r11 = requested->r11; + regs->r12 = requested->r12; + regs->r13 = requested->r13; + regs->r14 = requested->r14; + regs->r15 = requested->r15; + regs->cs = 0x18 | 0x3; + regs->ds = 0x20 | 0x3; + regs->ss = 0x20 | 0x3; + regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; } } diff --git a/sortix/x64/syscall.s b/sortix/x64/syscall.s index 2fe79704..4460c666 100644 --- a/sortix/x64/syscall.s +++ b/sortix/x64/syscall.s @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,73 +14,41 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . - syscall.s + x64/syscall.s An assembly stub that acts as glue for system calls. -******************************************************************************/ +*******************************************************************************/ .global syscall_handler -.global resume_syscall .section .text .type syscall_handler, @function syscall_handler: - cli + # The processor disabled interrupts during the int $0x80 instruction, + # however Sortix system calls runs with interrupts enabled such that they + # can be pre-empted. + sti - # Compabillity with InterruptRegisters. - pushq $0x0 - pushq $0x80 - - pushq %r15 - pushq %r14 - pushq %r13 - pushq %r12 - pushq %r11 - pushq %r10 - pushq %r9 - pushq %r8 - pushq %rax - pushq %rcx - pushq %rdx - pushq %rbx - pushq %rsp + movl $0, global_errno # Reset errno pushq %rbp - pushq %rsi - pushq %rdi - # Push the user-space data segment. + # Grant ourselves kernel permissions to the data segment. movl %ds, %ebp pushq %rbp - - # Load the kernel data segment. movw $0x10, %bp movl %ebp, %ds movl %ebp, %es movl %ebp, %fs movl %ebp, %gs - # Compabillity with InterruptRegisters. - movq %cr2, %rbp - pushq %rbp - - # Store the state structure's pointer so the call can modify it if needed. - movq %rsp, syscall_state_ptr - - # By default, assume the system call was complete. - movl $0, system_was_incomplete - - # Reset the kernel errno. - movl $0, global_errno - - # Make sure the requested system call is valid. + # Make sure the requested system call is valid, if not, then fix it. cmp SYSCALL_MAX, %rax - jb valid_rax - xorq %rax, %rax + jae fix_syscall -valid_rax: +valid_syscall: # Read a system call function pointer. xorq %rbp, %rbp movq syscall_list(%rbp,%rax,8), %rax @@ -88,72 +56,40 @@ valid_rax: # Oh how nice, user-space put the parameters in: rdi, rsi, rdx, rcx, r8, r9 # Call the system call. - callq *%rax + callq *%rax - # Test if the system call was incomplete - movl system_was_incomplete, %ebx - testl %ebx, %ebx - - # If the system call was incomplete, the value in %eax is meaningless. - jg return_to_userspace - - # The system call was completed, so store the return value. - movq %rax, 72(%rsp) - - # Don't forget to update userspace's errno value. - call update_userspace_errno - -return_to_userspace: - # Compabillity with InterruptRegisters. - addq $8, %rsp - - # Restore the user-space data segment. + # Restore the previous permissions to data segment. popq %rbp movl %ebp, %ds movl %ebp, %es movl %ebp, %fs movl %ebp, %gs - popq %rdi - popq %rsi + # Return to user-space, system call result in %rax, errno in %edx. popq %rbp - popq %rsp - popq %rbx - popq %rdx - popq %rcx - popq %rax - popq %r8 - popq %r9 - popq %r10 - popq %r11 - popq %r12 - popq %r13 - popq %r14 - popq %r15 + movl global_errno, %edx - # Compabillity with InterruptRegisters. - addq $16, %rsp + # If any signals are pending, fire them now. + movq asm_signal_is_pending, %rdi + testq %rdi, %rdi + jnz call_signal_dispatcher - # Return to user-space. iretq -.type resume_syscall, @function -resume_syscall: - pushq %rbp - movq %rsp, %rbp - - movq %rdi, %rax - movq %rdx, %r11 - - movq 0(%r11), %rdi - movq 8(%r11), %rsi - movq 16(%r11), %rdx - movq 24(%r11), %rcx - movq 32(%r11), %r8 - movq 40(%r11), %r9 - - callq *%rax - - leaveq - retq +fix_syscall: + # Call the null system call instead. + xorq %rax, %rax + jmp valid_syscall +call_signal_dispatcher: + # We can't return to this location after the signal, since if any system + # call is made this stack will get reused and all our nice temporaries wil + # be garbage. We therefore pass the kernel the state to return to and it'll + # handle it for us when the signal is over. + movq 0(%rsp), %rdi # userspace rip + movq 16(%rsp), %rsi # userspace rflags + movq 24(%rsp), %rcx # userspace rsp, note %rdx is used for errno + int $130 # Deliver pending signals. + # If we end up here, it means that the signal didn't override anything and + # that we should just go ahead and return to userspace ourselves. + iretq diff --git a/sortix/x64/thread.cpp b/sortix/x64/thread.cpp index c973e444..4f092d00 100644 --- a/sortix/x64/thread.cpp +++ b/sortix/x64/thread.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . thread.cpp x64 specific parts of thread.cpp. -******************************************************************************/ +*******************************************************************************/ #include #include "thread.h" @@ -50,6 +50,8 @@ namespace Sortix registers.ds = src->ds; registers.ss = src->ss; registers.rflags = src->rflags; + registers.kerrno = src->kerrno; + registers.signal_pending = src->signal_pending; } void Thread::LoadRegisters(CPU::InterruptRegisters* dest) @@ -75,65 +77,78 @@ namespace Sortix dest->ds = registers.ds; dest->ss = registers.ss; dest->rflags = registers.rflags; + dest->kerrno = registers.kerrno; + dest->signal_pending = registers.signal_pending; } - const size_t FLAGS_CARRY = (1<<0); - const size_t FLAGS_RESERVED1 = (1<<1); /* read as one */ - const size_t FLAGS_PARITY = (1<<2); - const size_t FLAGS_RESERVED2 = (1<<3); - const size_t FLAGS_AUX = (1<<4); - const size_t FLAGS_RESERVED3 = (1<<5); - const size_t FLAGS_ZERO = (1<<6); - const size_t FLAGS_SIGN = (1<<7); - const size_t FLAGS_TRAP = (1<<8); - const size_t FLAGS_INTERRUPT = (1<<9); - const size_t FLAGS_DIRECTION = (1<<10); - const size_t FLAGS_OVERFLOW = (1<<11); - const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13); - const size_t FLAGS_NESTEDTASK = (1<<14); - const size_t FLAGS_RESERVED4 = (1<<15); - const size_t FLAGS_RESUME = (1<<16); - const size_t FLAGS_VIRTUAL8086 = (1<<17); - const size_t FLAGS_ALIGNCHECK = (1<<18); - const size_t FLAGS_VIRTINTR = (1<<19); - const size_t FLAGS_VIRTINTRPEND = (1<<20); - const size_t FLAGS_ID = (1<<21); - - void CreateThreadCPU(Thread* thread, addr_t entry) + void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry, + void* user, addr_t stack, size_t stacksize) { - const uint64_t CS = 0x18; - const uint64_t DS = 0x20; - const uint64_t RPL = 0x3; + // Instead of directly calling the desired entry point, we call a small + // stub that calls it for us and then destroys the kernel thread if + // the entry function returns. Note that since we use a register based + // calling convention, we call BootstrapKernelThread directly. + regs->rip = (addr_t) BootstrapKernelThread; + regs->userrsp = stack + stacksize; + regs->rax = 0; + regs->rbx = 0; + regs->rcx = 0; + regs->rdx = 0; + regs->rdi = (addr_t) user; + regs->rsi = (addr_t) entry; + regs->rbp = 0; + regs->r8 = 0; + regs->r9 = 0; + regs->r10 = 0; + regs->r11 = 0; + regs->r12 = 0; + regs->r13 = 0; + regs->r14 = 0; + regs->r15 = 0; + regs->cs = KCS | KRPL; + regs->ds = KDS | KRPL; + regs->ss = KDS | KRPL; + regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->kerrno = 0; + regs->signal_pending = 0; + } - CPU::InterruptRegisters regs; - regs.rip = entry; - regs.userrsp = thread->stackpos + thread->stacksize; - regs.rax = 0; - regs.rbx = 0; - regs.rcx = 0; - regs.rdx = 0; - regs.rdi = 0; - regs.rsi = 0; - regs.rbp = thread->stackpos + thread->stacksize; - regs.r8 = 0; - regs.r9 = 0; - regs.r10 = 0; - regs.r11 = 0; - regs.r12 = 0; - regs.r13 = 0; - regs.r14 = 0; - regs.r15 = 0; - regs.cs = CS | RPL; - regs.ds = DS | RPL; - regs.ss = DS | RPL; - regs.rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - - thread->SaveRegisters(®s); + void Thread::HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs) + { + if ( regs->InUserspace() ) + return; + regs->rip = regs->rdi; + regs->rflags = regs->rsi; + regs->userrsp = regs->rcx; + regs->cs = UCS | URPL; + regs->ds = UDS | URPL; + regs->ss = UDS | URPL; } void Thread::HandleSignalCPU(CPU::InterruptRegisters* regs) { - regs->rdi = currentsignal->signum; + const size_t STACK_ALIGNMENT = 16UL; + const size_t RED_ZONE_SIZE = 128UL; + regs->userrsp -= RED_ZONE_SIZE; + regs->userrsp &= ~(STACK_ALIGNMENT-1UL); + regs->rbp = regs->userrsp; + regs->rdi = currentsignal; regs->rip = (size_t) sighandler; + regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->kerrno = 0; + regs->signal_pending = 0; + } + + void Thread::GotoOnSigKill(CPU::InterruptRegisters* regs) + { + regs->rip = (unsigned long) Thread__OnSigKill; + regs->rdi = (unsigned long) this; + regs->userrsp = regs->rbp = kernelstackpos + kernelstacksize; + regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->cs = KCS | KRPL; + regs->ds = KDS | KRPL; + regs->ss = KDS | KRPL; + regs->kerrno = 0; + regs->signal_pending = 0; } } diff --git a/sortix/x64/x64.h b/sortix/x64/x64.h index 49a37827..56ef2f14 100644 --- a/sortix/x64/x64.h +++ b/sortix/x64/x64.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . x64.h CPU stuff for the x64 platform. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_X64_H #define SORTIX_X64_H @@ -33,7 +33,7 @@ namespace Sortix { struct InterruptRegisters { - uint64_t cr2; + uint64_t signal_pending, kerrno, cr2; uint64_t ds; // Data segment selector uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; uint64_t r8, r9, r10, r11, r12, r13, r14, r15; @@ -42,18 +42,17 @@ namespace Sortix public: void LogRegisters() const; - }; + bool InUserspace() const { return (cs & 0x3) != 0; } - struct SyscallRegisters - { - uint64_t cr2; // For compabillity with above, may be removed soon. - uint64_t ds; - uint64_t di, si, bp, trash, b, d, c; union { uint64_t a; uint64_t result; }; - uint64_t r8, r9, r10, r11, r12, r13, r14, r15; - uint64_t int_no, err_code; // Also compabillity. - uint64_t ip, cs, flags, sp, ss; }; } + + const uint64_t KCS = 0x08; + const uint64_t KDS = 0x10; + const uint64_t KRPL = 0x0; + const uint64_t UCS = 0x18; + const uint64_t UDS = 0x20; + const uint64_t URPL = 0x3; } #endif diff --git a/sortix/x86-family/memorymanagement.cpp b/sortix/x86-family/memorymanagement.cpp index 27e02d9d..60d3f3f1 100644 --- a/sortix/x86-family/memorymanagement.cpp +++ b/sortix/x86-family/memorymanagement.cpp @@ -192,6 +192,8 @@ namespace Sortix "restrictions.\n", Page::pagesnotonstack * 0x1000UL); } + Memory::Unmap(0x0); // Remove NULL. + // Finish allocating the top level PMLs for the kernels use. AllocateKernelPMLs(); } diff --git a/sortix/x86-family/x86-family.h b/sortix/x86-family/x86-family.h index 11b079dd..e2e0ba2a 100644 --- a/sortix/x86-family/x86-family.h +++ b/sortix/x86-family/x86-family.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . x86-family.h CPU stuff for the x86 CPU family. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_X86_FAMILY_H #define SORTIX_X86_FAMILY_H @@ -38,6 +38,28 @@ namespace Sortix void Reboot(); void ShutDown(); } + + const size_t FLAGS_CARRY = (1<<0); // 0x000001 + const size_t FLAGS_RESERVED1 = (1<<1); // 0x000002, read as one + const size_t FLAGS_PARITY = (1<<2); // 0x000004 + const size_t FLAGS_RESERVED2 = (1<<3); // 0x000008 + const size_t FLAGS_AUX = (1<<4); // 0x000010 + const size_t FLAGS_RESERVED3 = (1<<5); // 0x000020 + const size_t FLAGS_ZERO = (1<<6); // 0x000040 + const size_t FLAGS_SIGN = (1<<7); // 0x000080 + const size_t FLAGS_TRAP = (1<<8); // 0x000100 + const size_t FLAGS_INTERRUPT = (1<<9); // 0x000200 + const size_t FLAGS_DIRECTION = (1<<10); // 0x000400 + const size_t FLAGS_OVERFLOW = (1<<11); // 0x000800 + const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13); + const size_t FLAGS_NESTEDTASK = (1<<14); // 0x004000 + const size_t FLAGS_RESERVED4 = (1<<15); // 0x008000 + const size_t FLAGS_RESUME = (1<<16); // 0x010000 + const size_t FLAGS_VIRTUAL8086 = (1<<17); // 0x020000 + const size_t FLAGS_ALIGNCHECK = (1<<18); // 0x040000 + const size_t FLAGS_VIRTINTR = (1<<19); // 0x080000 + const size_t FLAGS_VIRTINTRPEND = (1<<20); // 0x100000 + const size_t FLAGS_ID = (1<<21); // 0x200000 } #endif diff --git a/sortix/x86/interrupt.s b/sortix/x86/interrupt.s index fb09f746..bcc1ae1f 100644 --- a/sortix/x86/interrupt.s +++ b/sortix/x86/interrupt.s @@ -1,6 +1,6 @@ /******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -255,6 +255,20 @@ isr128: pushl $0 # err_code pushl $128 # int_no jmp interrupt_handler_prepare +.global isr130 +.type isr130, @function +isr130: + cli + pushl $0 # err_code + pushl $130 # int_no + jmp interrupt_handler_prepare +.global isr131 +.type isr131, @function +isr131: + cli + pushl $0 # err_code + pushl $131 # int_no + jmp interrupt_handler_prepare .global irq0 .type irq0, @function irq0: @@ -367,8 +381,29 @@ irq15: pushl $0 # err_code pushl $47 # int_no jmp interrupt_handler_prepare +.global yield_cpu_handler +.type yield_cpu_handler, @function +yield_cpu_handler: + cli + pushl $0 # err_code + pushl $129 # int_no + jmp interrupt_handler_prepare +.global thread_exit_handler +.type thread_exit_handler, @function +thread_exit_handler: + cli + pushl $0 # err_code + pushl $132 # int_no + jmp interrupt_handler_prepare interrupt_handler_prepare: + movl $1, asm_is_cpu_interrupted + + # Check if an interrupt happened while having kernel permissions. + testw $0x3, 12(%esp) # cs + jz fixup_relocate_stack +fixup_relocate_stack_complete: + pushl %eax pushl %ecx pushl %edx @@ -393,11 +428,28 @@ interrupt_handler_prepare: movl %cr2, %ebp pushl %ebp + # Push the current kernel errno value. + movl global_errno, %ebp + pushl %ebp + + # Push whether a signal is pending. + movl asm_signal_is_pending, %ebp + pushl %ebp + # Now call the interrupt handler. pushl %esp call interrupt_handler addl $4, %esp +load_interrupted_registers: + # Restore whether signals are pending. + popl %ebp + movl %ebp, asm_signal_is_pending + + # Restore the previous kernel errno. + popl %ebp + movl %ebp, global_errno + # Remove CR2 from the stack. addl $4, %esp @@ -411,7 +463,7 @@ interrupt_handler_prepare: popl %edi popl %esi popl %ebp - popl %esp + addl $4, %esp # Don't pop %esp, may not be defined. popl %ebx popl %edx popl %ecx @@ -420,9 +472,107 @@ interrupt_handler_prepare: # Remove int_no and err_code addl $8, %esp + movl $0, asm_is_cpu_interrupted + + # If interrupted with kernel permissions we may need to switch stack. + testw $0x3, 4(%esp) # int_no and err_code now gone, so cs is at 4(%esp). + jz fixup_switch_stack +fixup_switch_stack_complete: + # Return to where we came from. iret +fixup_relocate_stack: + # Ok, so some genius at Intel decided that if the permission level does not + # change during an interrupt then the CPU won't push the stack pointer and + # it won't reload it during iret. This seriously messes up the scheduler + # that wants to preempt kernel threads each with their own stack. The + # scheduler will attempt to read (and modify) the stack value which doesn't + # exist and worse: the value at that location is likely used by the + # interrupted kernel thread. A quick and dirty solution is to simply move + # the stack 8 bytes down the stack. Right now there are the 5 elements on + # the stack (eflags, cs, eip, err_code, int_no) of 5 bytes each. + mov %eax, -4-8(%esp) # Save eax + mov 0(%esp), %eax # int_no + mov %eax, 0-8(%esp) + mov 4(%esp), %eax # err_code + mov %eax, 4-8(%esp) + mov 8(%esp), %eax # eip + mov %eax, 8-8(%esp) + mov 12(%esp), %eax # cs + mov %eax, 12-8(%esp) + mov 16(%esp), %eax # eflags + mov %eax, 16-8(%esp) + # Next up we have to fake what the CPU should have done: pushed ss and esp. + mov %esp, %eax + addl $5*4, %eax # Calculate original esp + mov %eax, 20-8(%esp) + mov %ss, %eax + mov %eax, 24-8(%esp) + # Now that we moved the stack, it's time to really handle the interrupt. + mov -4-8(%esp), %eax + subl $8, %esp + jmp fixup_relocate_stack_complete + +fixup_switch_stack: + # Yup, we also have to do special processing when we return from the + # interrupt. The problem is that if the iret instruction won't load a new + # stack if interrupted with kernel permissions and that the scheduler may + # wish to change the current stack during a context switch. We will then + # switch the stack before calling iret; but iret needs the return + # information on the stack (and now it isn't), so we'll copy our stack onto + # our new stack and then fire the interrupt and everyone is happy. + + # In the following code, %esp will point our fixed iret return parameters + # that has stack data. However, the processor does not expect this + # information as cs hasn't changed. %ebx will point to the new stack plus + # room for three 32-bit values (eip, cs, eflags) that will be given to the + # actual iret. We will then load the new stack and copy the eip, cs and + # eflags to the new stack. However, we have to be careful in the case that + # we are switching to the same stack (in which case stuff on the same + # horizontal location in the diagram is actually on the same memory + # location). We therefore copy to the new stack and carefully avoid + # corrupting the destination if %esp + 8 = %ebx, This diagram show the + # structure of the stacks and where temporaries will be stored: + # -12 -8 -4 %esp 4 8 12 16 20 + # old: IECX IEBX IEAX EIP CS EFLAGS ESP SS ... + # new: IECX IEBX IEAX - - EIP CS EFLAGS ... + # -20 -16 -12 -8 -4 %ebx 4 8 12 + + mov %eax, -4(%esp) # IEAX, Clobbered as copying temporary + mov %ebx, -8(%esp) # IEBX, Clobbered as pointer to new stack + mov %ecx, -12(%esp) # IECX, Clobbered as new stack selector + + mov 12(%esp), %ebx # Pointer to new stack + sub $3*4, %ebx # Point to eip on the new stack (see diagram) + movw 16(%esp), %cx # New ss + + # The order of these does not matter if we are switching to the same stack, + # as the memory would be copied to the same location (see diagram). + mov -4(%esp), %eax # interrupted eax value + mov %eax, -12(%ebx) + mov -8(%esp), %eax # interrupted ebx value + mov %eax, -16(%ebx) + mov -12(%esp), %eax # interrupted ecx value + mov %eax, -20(%ebx) + + # The order of these three copies matter if switching to the same stack. + mov 8(%esp), %eax # eflags + mov %eax, 8(%ebx) + mov 4(%esp), %eax # cs + mov %eax, 4(%ebx) + mov 0(%esp), %eax # eip + mov %eax, 0(%ebx) + + mov %cx, %ss # Load new stack selector + mov %ebx, %esp # Load new stack pointer + + mov -12(%esp), %eax # restore interrupted eax value + mov -16(%esp), %ebx # restore interrupted ebx value + mov -20(%esp), %ecx # restore interrupted ecx value + + jmp fixup_switch_stack_complete + .global interrupt_handler_null .type interrupt_handler_null, @function interrupt_handler_null: @@ -436,3 +586,10 @@ asm_interrupts_are_enabled: andl $0x000200, %eax # FLAGS_INTERRUPT retl +.global load_registers +.type load_registers, @function +load_registers: + # Let the register struct become our temporary stack + movl 4(%esp), %esp + jmp load_interrupted_registers + diff --git a/sortix/x86/kthread.s b/sortix/x86/kthread.s new file mode 100644 index 00000000..9d311ecb --- /dev/null +++ b/sortix/x86/kthread.s @@ -0,0 +1,104 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + x64/kthread.s + Utilities and synchronization mechanisms for x86 kernel threads. + +*******************************************************************************/ + +.section .text + +.global kthread_mutex_trylock +.type kthread_mutex_trylock, @function +kthread_mutex_trylock: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %edx + movl $-1, %eax + xchgl (%edx), %eax + not %eax + leavel + retl + +.global kthread_mutex_lock +.type kthread_mutex_lock, @function +kthread_mutex_lock: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %edx +kthread_mutex_lock_retry: + movl $-1, %eax + xchgl (%edx), %eax + testl %eax, %eax + jnz kthread_mutex_lock_failed + leavel + retl +kthread_mutex_lock_failed: + int $0x81 # Yield the CPU. + jmp kthread_mutex_lock_retry + +.global kthread_mutex_lock_signal +.type kthread_mutex_lock_signal, @function +kthread_mutex_lock_signal: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %edx +kthread_mutex_lock_signal_retry: + movl asm_signal_is_pending, %eax + testl %eax, %eax + jnz kthread_mutex_lock_signal_pending + movl $-1, %eax + xchgl (%edx), %eax + testl %eax, %eax + jnz kthread_mutex_lock_signal_failed + inc %eax +kthread_mutex_lock_signal_out: + leavel + retl +kthread_mutex_lock_signal_failed: + int $0x81 # Yield the CPU. + jmp kthread_mutex_lock_signal_retry +kthread_mutex_lock_signal_pending: + xorl %eax, %eax + jmp kthread_mutex_lock_signal_out + +.global kthread_mutex_unlock +.type kthread_mutex_unlock, @function +kthread_mutex_unlock: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %edx + movl $0, (%edx) + leavel + retl + +.global asm_call_BootstrapKernelThread +.type asm_call_BootstrapKernelThread, @function +asm_call_BootstrapKernelThread: + pushl %esi + pushl %edi + call BootstrapKernelThread + # BootstrapKernelThread is noreturn, no need for code here. + +.global asm_call_Thread__OnSigKill +.type asm_call_Thread__OnSigKill, @function +asm_call_Thread__OnSigKill: + pushl %edi + call Thread__OnSigKill + # Thread__OnSigKill is noreturn, no need for code here. diff --git a/sortix/x86/memorymanagement.cpp b/sortix/x86/memorymanagement.cpp index 57f28ee6..024f0d39 100644 --- a/sortix/x86/memorymanagement.cpp +++ b/sortix/x86/memorymanagement.cpp @@ -114,45 +114,53 @@ namespace Sortix PML* pml = PMLS[level] + offset; for ( size_t i = 0; i < ENTRIES; i++ ) { - if ( !(pml->entry[i] & PML_PRESENT) ) { continue; } - if ( !(pml->entry[i] & PML_USERSPACE) ) { continue; } - if ( !(pml->entry[i] & PML_FORK) ) { continue; } + addr_t entry = pml->entry[i]; + if ( !(entry & PML_PRESENT) ) { continue; } + if ( !(entry & PML_USERSPACE) ) { continue; } + if ( !(entry & PML_FORK) ) { continue; } if ( level > 1 ) { RecursiveFreeUserspacePages(level-1, offset * ENTRIES + i); } addr_t addr = pml->entry[i] & PML_ADDRESS; - pml->entry[i] = 0; - Page::Put(addr); + // No need to unmap the page, we just need to mark it as unused. + Page::PutUnlocked(addr); } } - void DestroyAddressSpace() + void DestroyAddressSpace(addr_t fallback, void (*func)(addr_t, void*), void* user) { - // First let's do the safe part. Garbage collect any PML1/0's left - // behind by user-space. These are completely safe to delete. - RecursiveFreeUserspacePages(TOPPMLLEVEL, 0); - - // Let's destroy the current address space! Oh wait. If we do that, - // hell will break loose half-way when we start unmapping this piece - // of code. - // Instead, let's just mark the relevant pages as unused and switch - // to another address space as fast as humanely possible. Any memory - // allocation could potentially modify the current paging structures - // and overwrite their contents causing a tripple-fault! - - // Make sure Page::Put does NOT cause any Page::Get's internally! - const size_t NUM_PAGES = 2; - size_t pagestackfree = Page::stacklength - Page::stackused; - if ( pagestackfree < NUM_PAGES ) { Page::ExtendStack(); } - + // Look up the last few entries used for the fractal mapping. These + // cannot be unmapped as that would destroy the world. Instead, we + // will remember them, switch to another adress space, and safely + // mark them as unused. Also handling the forking related pages. addr_t fractal1 = PMLS[2]->entry[1022]; + addr_t dir = currentdir; - Page::Put(fractal1 & PML_ADDRESS); - Page::Put(currentdir & PML_ADDRESS); + // We want to free the pages, but we are still using them ourselves, + // so lock the page allocation structure until we are done. + Page::Lock(); + + // In case any pages wasn't cleaned at this point. +#warning Page::Put calls may internally Page::Get and then reusing pages we are not done with just yet + RecursiveFreeUserspacePages(TOPPMLLEVEL, 0); // Switch to the address space from when the world was originally // created. It should contain the kernel, the whole kernel, and // nothing but the kernel. PML* const BOOTPML2 = (PML* const) 0x11000UL; - SwitchAddressSpace((addr_t) BOOTPML2); + if ( !fallback ) + fallback = (addr_t) BOOTPML2; + + if ( func ) + func(fallback, user); + else + SwitchAddressSpace(fallback); + + // Ok, now we got marked everything left behind as unused, we can + // now safely let another thread use the pages. + Page::Unlock(); + + // These are safe to free since we switched address space. + Page::Put(fractal1 & PML_ADDRESS); + Page::Put(dir & PML_ADDRESS); } const size_t KERNEL_STACK_SIZE = 256UL * 1024UL; diff --git a/sortix/x86/process.cpp b/sortix/x86/process.cpp index e0424fec..a9e39d0f 100644 --- a/sortix/x86/process.cpp +++ b/sortix/x86/process.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,20 +14,21 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . x86/process.cpp CPU-specific process code. -******************************************************************************/ +*******************************************************************************/ #include +#include +#include #include "process.h" namespace Sortix { - void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp, addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs) @@ -37,7 +38,30 @@ namespace Sortix regs->edx = envc; regs->ecx = (size_t) envp; regs->eip = entry; - regs->useresp = stackpos; - regs->ebp = stackpos; + regs->useresp = stackpos & ~(15UL); + regs->ebp = regs->useresp; + regs->cs = UCS | URPL; + regs->ds = UDS | URPL; + regs->ss = UDS | URPL; + regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + } + + void InitializeThreadRegisters(CPU::InterruptRegisters* regs, + const sforkregs_t* requested) + { + Maxsi::Memory::Set(regs, 0, sizeof(*regs)); + regs->eip = requested->eip; + regs->useresp = requested->esp; + regs->eax = requested->eax; + regs->ebx = requested->ebx; + regs->ecx = requested->ecx; + regs->edx = requested->edx; + regs->edi = requested->edi; + regs->esi = requested->esi; + regs->ebp = requested->ebp; + regs->cs = UCS | URPL; + regs->ds = UDS | URPL; + regs->ss = UDS | URPL; + regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; } } diff --git a/sortix/x86/syscall.s b/sortix/x86/syscall.s index 439ffe9e..418bf92c 100644 --- a/sortix/x86/syscall.s +++ b/sortix/x86/syscall.s @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,129 +14,84 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . syscall.s An assembly stub that acts as glue for system calls. -******************************************************************************/ +*******************************************************************************/ .global syscall_handler -.global resume_syscall .section .text .type syscall_handler, @function syscall_handler: - cli + # The processor disabled interrupts during the int $0x80 instruction, + # however Sortix system calls runs with interrupts enabled such that they + # can be pre-empted. + sti - # Compabillity with InterruptRegisters. - pushl $0x0 - pushl $0x80 - - # Push eax, ecx, edx, ebx, esp, ebp, esi, edi - pushal - - # Push the user-space data segment. - movl %ds, %ebp + movl $0, global_errno # Reset errno pushl %ebp - # Load the kernel data segment. + # Grant ourselves kernel permissions to the data segment. + movl %ds, %ebp + pushl %ebp movw $0x10, %bp movl %ebp, %ds movl %ebp, %es movl %ebp, %fs movl %ebp, %gs - # Compabillity with InterruptRegisters. - movl %cr2, %ebp - pushl %ebp - - # Store the state structure's pointer so the call can modify it if needed. - mov %esp, syscall_state_ptr - - # By default, assume the system call was complete. - movl $0, system_was_incomplete - - # Reset the kernel errno. - movl $0, global_errno - # Make sure the requested system call is valid. cmp SYSCALL_MAX, %eax - jb valid_eax - xorl %eax, %eax + jae fix_syscall -valid_eax: +valid_syscall: # Read a system call function pointer. xorl %ebp, %ebp movl syscall_list(%ebp,%eax,4), %eax - # Give the system call function the values given by user-space. + # Call the system call. pushl %esi pushl %edi pushl %edx pushl %ecx pushl %ebx - - # Call the system call. calll *%eax - - # Clean up after the call. addl $20, %esp - # Test if the system call was incomplete - movl system_was_incomplete, %ebx - testl %ebx, %ebx - - # If the system call was incomplete, the value in %eax is meaningless. - jg return_to_userspace - - # The system call was completed, so store the return value. - movl %eax, 36(%esp) - - # Don't forget to update userspace's errno value. - call update_userspace_errno - -return_to_userspace: - # Compabillity with InterruptRegisters. - addl $4, %esp - - # Restore the user-space data segment. + # Restore the previous permissions to data segment. popl %ebp movl %ebp, %ds movl %ebp, %es movl %ebp, %fs movl %ebp, %gs - popal + # Return to user-space, system call result in %eax, errno in %edx. + popl %ebp + movl global_errno, %edx - # Compabillity with InterruptRegisters. - addl $8, %esp + # If any signals are pending, fire them now. + movl asm_signal_is_pending, %ecx + testl %ecx, %ecx + jnz call_signal_dispatcher - # Return to user-space. iretl -.type resume_syscall, @function -resume_syscall: - pushl %ebp - movl %esp, %ebp - - movl 8(%esp), %eax - movl 16(%esp), %ecx - - pushl 28(%ecx) - pushl 24(%ecx) - pushl 20(%ecx) - pushl 16(%ecx) - pushl 12(%ecx) - pushl 8(%ecx) - pushl 4(%ecx) - pushl 0(%ecx) - - call *%eax - - addl $32, %esp - - leavel - retl +fix_syscall: + # Call the null system call instead. + xorl %eax, %eax + jmp valid_syscall +call_signal_dispatcher: + # We can't return to this location after the signal, since if any system + # call is made this stack will get reused and all our nice temporaries wil + # be garbage. We therefore pass the kernel the state to return to and it'll + # handle it for us when the signal is over. + movl %esp, %ecx + int $130 # Deliver pending signals. + # If we end up here, it means that the signal didn't override anything and + # that we should just go ahead and return to userspace ourselves. + iretl diff --git a/sortix/x86/thread.cpp b/sortix/x86/thread.cpp index 657d25be..fc0680e4 100644 --- a/sortix/x86/thread.cpp +++ b/sortix/x86/thread.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . thread.cpp x86 specific parts of thread.cpp. -******************************************************************************/ +*******************************************************************************/ #include #include "thread.h" @@ -42,6 +42,8 @@ namespace Sortix registers.ds = src->ds; registers.ss = src->ss; registers.eflags = src->eflags; + registers.kerrno = src->kerrno; + registers.signal_pending = src->signal_pending; } void Thread::LoadRegisters(CPU::InterruptRegisters* dest) @@ -59,57 +61,80 @@ namespace Sortix dest->ds = registers.ds; dest->ss = registers.ss; dest->eflags = registers.eflags; + dest->kerrno = registers.kerrno; + dest->signal_pending = registers.signal_pending; } - const size_t FLAGS_CARRY = (1<<0); - const size_t FLAGS_RESERVED1 = (1<<1); /* read as one */ - const size_t FLAGS_PARITY = (1<<2); - const size_t FLAGS_RESERVED2 = (1<<3); - const size_t FLAGS_AUX = (1<<4); - const size_t FLAGS_RESERVED3 = (1<<5); - const size_t FLAGS_ZERO = (1<<6); - const size_t FLAGS_SIGN = (1<<7); - const size_t FLAGS_TRAP = (1<<8); - const size_t FLAGS_INTERRUPT = (1<<9); - const size_t FLAGS_DIRECTION = (1<<10); - const size_t FLAGS_OVERFLOW = (1<<11); - const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13); - const size_t FLAGS_NESTEDTASK = (1<<14); - const size_t FLAGS_RESERVED4 = (1<<15); - const size_t FLAGS_RESUME = (1<<16); - const size_t FLAGS_VIRTUAL8086 = (1<<17); - const size_t FLAGS_ALIGNCHECK = (1<<18); - const size_t FLAGS_VIRTINTR = (1<<19); - const size_t FLAGS_VIRTINTRPEND = (1<<20); - const size_t FLAGS_ID = (1<<21); + extern "C" void asm_call_BootstrapKernelThread(void); - void CreateThreadCPU(Thread* thread, addr_t entry) + void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry, + void* user, addr_t stack, size_t stacksize) { - const uint32_t CS = 0x18; - const uint32_t DS = 0x20; - const uint32_t RPL = 0x3; + // Instead of directly calling the desired entry point, we call a small + // stub that calls it for us and then destroys the kernel thread if + // the entry function returns. Note that since we use a stack based + // calling convention, we go through a proxy that uses %edi and %esi + // as parameters and pushes them to the stack and then does the call. + regs->eip = (addr_t) asm_call_BootstrapKernelThread; + regs->useresp = stack + stacksize; + regs->eax = 0; + regs->ebx = 0; + regs->ecx = 0; + regs->edx = 0; + regs->edi = (addr_t) user; + regs->esi = (addr_t) entry; + regs->ebp = 0; + regs->cs = KCS | KRPL; + regs->ds = KDS | KRPL; + regs->ss = KDS | KRPL; + regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->kerrno = 0; + regs->signal_pending = 0; + } - CPU::InterruptRegisters regs; - regs.eip = entry; - regs.useresp = thread->stackpos + thread->stacksize; - regs.eax = 0; - regs.ebx = 0; - regs.ecx = 0; - regs.edx = 0; - regs.edi = 0; - regs.esi = 0; - regs.ebp = thread->stackpos + thread->stacksize; - regs.cs = CS | RPL; - regs.ds = DS | RPL; - regs.ss = DS | RPL; - regs.eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - - thread->SaveRegisters(®s); + void Thread::HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs) + { + if ( regs->InUserspace() ) + return; + uint32_t* params = (uint32_t*) regs->ecx; + regs->eip = params[0]; + regs->eflags = params[2]; + regs->useresp = params[3]; + regs->cs = UCS | URPL; + regs->ds = UDS | URPL; + regs->ss = UDS | URPL; } void Thread::HandleSignalCPU(CPU::InterruptRegisters* regs) { - regs->edi = currentsignal->signum; + const size_t STACK_ALIGNMENT = 16UL; + const size_t RED_ZONE_SIZE = 128UL; + regs->useresp -= RED_ZONE_SIZE; + regs->useresp &= ~(STACK_ALIGNMENT-1UL); + regs->ebp = regs->useresp; + regs->edi = currentsignal; regs->eip = (size_t) sighandler; + regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->kerrno = 0; + regs->signal_pending = 0; + } + + extern "C" void asm_call_Thread__OnSigKill(void); + + void Thread::GotoOnSigKill(CPU::InterruptRegisters* regs) + { + regs->eip = (unsigned long) asm_call_Thread__OnSigKill; + regs->edi = (unsigned long) this; + // TODO: HACK: The -256 is because if we are jumping to the safe stack + // we currently are on, this may not be fully supported by interrupt.s + // that is quite aware of this (but isn't perfect). If our destination + // is further down the stack, then we are probably safe. + regs->useresp = regs->ebp = kernelstackpos + kernelstacksize - 256; + regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->cs = KCS | KRPL; + regs->ds = KDS | KRPL; + regs->ss = KDS | KRPL; + regs->kerrno = 0; + regs->signal_pending = 0; } } diff --git a/sortix/x86/x86.h b/sortix/x86/x86.h index 27056614..0990d434 100644 --- a/sortix/x86/x86.h +++ b/sortix/x86/x86.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . x86.h CPU stuff for the x86 platform. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_X86_H #define SORTIX_X86_H @@ -33,7 +33,7 @@ namespace Sortix { struct InterruptRegisters { - uint32_t cr2; + uint32_t signal_pending, kerrno, cr2; uint32_t ds; // Data segment selector uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha. uint32_t int_no, err_code; // Interrupt number and error code (if applicable) @@ -41,18 +41,17 @@ namespace Sortix public: void LogRegisters() const; + bool InUserspace() const { return (cs & 0x3) != 0; } }; - - struct SyscallRegisters - { - uint32_t cr2; // For compabillity with above, may be removed soon. - uint32_t ds; - uint32_t di, si, bp, trash, b, d, c; union { uint32_t a; uint32_t result; }; - uint32_t int_no, err_code; // Also compabillity. - uint32_t ip, cs, flags, sp, ss; - }; } + + const uint64_t KCS = 0x08; + const uint64_t KDS = 0x10; + const uint64_t KRPL = 0x0; + const uint64_t UCS = 0x18; + const uint64_t UDS = 0x20; + const uint64_t URPL = 0x3; } #endif