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!
This commit is contained in:
Jonas 'Sortie' Termansen 2012-08-01 17:30:34 +02:00
parent c518a37bef
commit 51e3de971c
55 changed files with 2615 additions and 2311 deletions

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
@ -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 <sortix/syscallnum.h>
#include <errno.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
@ -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 <features.h>
#include <sortix/signal.h>
__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);

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <libmaxsi/platform.h>
#include <libmaxsi/signal.h>
#include <libmaxsi/io.h>
#include <libmaxsi/memory.h>
#include <string.h>
@ -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();

View File

@ -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 <http://www.gnu.org/licenses/>.
@ -26,7 +26,6 @@
#include <libmaxsi/memory.h>
#include <libmaxsi/syscall.h>
#include <libmaxsi/process.h>
#include <libmaxsi/signal.h>
#include <unistd.h>
#include <signal.h>
@ -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);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
@ -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

View File

@ -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 \

View File

@ -26,10 +26,10 @@
#include <sortix/kernel/kthread.h>
#include <libmaxsi/error.h>
#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"

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
event.cpp
Each thread can wait for an event to happen and be signaled when it does.
******************************************************************************/
#include <sortix/kernel/platform.h>
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -18,29 +18,39 @@
Sortix. If not, see <http://www.gnu.org/licenses/>.
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 <sortix/signal.h>
#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()

View File

@ -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);

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <features.h>
__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

View File

@ -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

View File

@ -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 <sortix/kernel/platform.h>
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#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

View File

@ -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();

View File

@ -24,16 +24,19 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/log.h>
#include <sortix/kernel/panic.h>
#include <sortix/kernel/video.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/textbuffer.h>
#include <sortix/kernel/pci.h>
#include <sortix/kernel/worker.h>
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#include <libmaxsi/string.h>
#include <libmaxsi/format.h>
#include <sortix/kernel/log.h>
#include <sortix/kernel/panic.h>
#include <sortix/kernel/video.h>
#include <sortix/mman.h>
#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,
&regs) )
{
Panic("Unable to execute init program");
}
// Now become the init process and the operation system shall run.
CPU::LoadRegisters(&regs);
}
} // namespace Sortix

View File

@ -18,52 +18,107 @@
Sortix. If not, see <http://www.gnu.org/licenses/>.
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 <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/worker.h>
#include <sortix/signal.h>
#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

View File

@ -24,10 +24,12 @@
#include <sortix/kernel/platform.h>
#include <sortix/keycodes.h>
#include <sortix/signal.h>
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#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;
}

View File

@ -17,7 +17,6 @@
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
logterminal.h
A simple terminal that writes to the kernel log.

View File

@ -25,6 +25,7 @@
#include <sortix/kernel/platform.h>
#include <libmaxsi/string.h>
#include <libmaxsi/memory.h>
#include "interrupt.h"
#include <sortix/kernel/log.h>
#include "calltrace.h"
#include <sortix/kernel/panic.h>
@ -43,6 +44,7 @@ namespace Sortix
void PanicInit()
{
Interrupt::Disable();
if ( panicing )
{
Log::PrintF("Panic while panicing:\n");

View File

@ -24,11 +24,13 @@
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/signal.h>
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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 <sortix/kernel/kthread.h>
#include <sortix/fork.h>
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();
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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 <sortix/kernel/platform.h>
#include <sortix/mman.h>
#include <sortix/kernel/memorymanagement.h>
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#include <sortix/kernel/panic.h>
#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 <sortix/kernel/memorymanagement.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
serialterminal.h
A terminal on a serial line.
******************************************************************************/
*******************************************************************************/
#ifndef SORTIX_SERIALTERMINAL_H
#define SORTIX_SERIALTERMINAL_H

View File

@ -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 <sortix/kernel/platform.h>
#include <libmaxsi/memory.h>
#include <sortix/kernel/panic.h>
#include <sortix/signal.h>
#include <libmaxsi/memory.h>
#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

View File

@ -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 <libmaxsi/signalnum.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
syscall.h
Handles system calls from userspace safely.
Handles system calls from userspace.
******************************************************************************/
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <libmaxsi/error.h>
@ -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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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();
}
}

View File

@ -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);

View File

@ -25,6 +25,8 @@
#ifndef SORTIX_TEXTTERMINAL_H
#define SORTIX_TEXTTERMINAL_H
#include <sortix/kernel/kthread.h>
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;

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
thread.cpp
Describes a thread belonging to a process.
******************************************************************************/
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/mman.h>
#include <sortix/signal.h>
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#include "event.h"
#include "process.h"
#include "thread.h"
#include "scheduler.h"
#include <sortix/kernel/memorymanagement.h>
#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(&registers, 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(&registers, &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(&regs, 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, &regs);
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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
thread.h
Describes a thread belonging to a process.
******************************************************************************/
*******************************************************************************/
#ifndef SORTIX_THREAD_H
#define SORTIX_THREAD_H
#include <sortix/signal.h>
#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

View File

@ -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

85
sortix/x64/kthread.s Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <sortix/kernel/platform.h>
#include <sortix/kernel/memorymanagement.h>
#include <libmaxsi/memory.h>
#include "multiboot.h"
#include <sortix/kernel/panic.h>
#include <sortix/kernel/memorymanagement.h>
#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);

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x64/process.cpp
CPU-specific process code.
******************************************************************************/
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/fork.h>
#include <libmaxsi/memory.h>
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
thread.cpp
x64 specific parts of thread.cpp.
******************************************************************************/
*******************************************************************************/
#include <sortix/kernel/platform.h>
#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(&regs);
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -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();
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -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

104
sortix/x86/kthread.s Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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.

View File

@ -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;

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86/process.cpp
CPU-specific process code.
******************************************************************************/
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/fork.h>
#include <libmaxsi/memory.h>
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
thread.cpp
x86 specific parts of thread.cpp.
******************************************************************************/
*******************************************************************************/
#include <sortix/kernel/platform.h>
#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(&regs);
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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