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