mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Multithreaded kernel and improvement of signal handling.
Pardon the big ass-commit, this took months to develop and debug and the refactoring got so far that a clean merge became impossible. The good news is that this commit does quite a bit of cleaning up and generally improves the kernel quality. This makes the kernel fully pre-emptive and multithreaded. This was done by rewriting the interrupt code, the scheduler, introducing new threading primitives, and rewriting large parts of the kernel. During the past few commits the kernel has had its device drivers thread secured; this commit thread secures large parts of the core kernel. There still remains some parts of the kernel that is _not_ thread secured, but this is not a problem at this point. Each user-space thread has an associated kernel stack that it uses when it goes into kernel mode. This stack is by default 8 KiB since that value works for me and is also used by Linux. Strange things tends to happen on x86 in case of a stack overflow - there is no ideal way to catch such a situation right now. The system call conventions were changed, too. The %edx register is now used to provide the errno value of the call, instead of the kernel writing it into a registered global variable. The system call code has also been updated to better reflect the native calling conventions: not all registers have to be preserved. This makes system calls faster and simplifies the assembly. In the kernel, there is no longer the event.h header or the hacky method of 'resuming system calls' that closely resembles cooperative multitasking. If a system call wants to block, it should just block. The signal handling was also improved significantly. At this point, signals cannot interrupt kernel threads (but can always interrupt user-space threads if enabled), which introduces some problems with how a SIGINT could interrupt a blocking read, for instance. This commit introduces and uses a number of new primitives such as kthread_lock_mutex_signal() that attempts to get the lock but fails if a signal is pending. In this manner, the kernel is safer as kernel threads cannot be shut down inconveniently, but in return for complexity as blocking operations must check they if they should fail. Process exiting has also been refactored significantly. The _exit(2) system call sets the exit code and sends SIGKILL to all the threads in the process. Once all the threads have cleaned themselves up and exited, a worker thread calls the process's LastPrayer() method that unmaps memory, deletes the address space, notifies the parent, etc. This provides a very robust way to terminate processes as even half-constructed processes (during a failing fork for instance) can be gracefully terminated. I have introduced a number of kernel threads to help avoid threading problems and simplify kernel design. For instance, there is now a functional generic kernel worker thread that any kernel thread can schedule jobs for. Interrupt handlers run with interrupts off (hence they cannot call kthread_ functions as it may deadlock the system if another thread holds the lock) therefore they cannot use the standard kernel worker threads. Instead, they use a special purpose interrupt worker thread that works much like the generic one expect that interrupt handlers can safely queue work with interrupts off. Note that this also means that interrupt handlers cannot allocate memory or print to the kernel log/screen as such mechanisms uses locks. I'll introduce a lock free algorithm for such cases later on. The boot process has also changed. The original kernel init thread in kernel.cpp creates a new bootstrap thread and becomes the system idle thread. Note that pid=0 now means the kernel, as there is no longer a system idle process. The bootstrap thread launches all the kernel worker threads and then creates a new process and loads /bin/init into it and then creates a thread in pid=1, which starts the system. The bootstrap thread then quietly waits for pid=1 to exit after which it shuts down/reboots/panics the system. In general, the introduction of race conditions and dead locks have forced me to revise a lot of the design and make sure it was thread secure. Since early parts of the kernel was quite hacky, I had to refactor such code. So it seems that the risk of dead locks forces me to write better code. Note that a real preemptive multithreaded kernel simplifies the construction of blocking system calls. My hope is that this will trigger a clean up of the filesystem code that current is almost beyond repair. Almost all of the kernel was modified during this refactoring. To the extent possible, these changes have been backported to older non-multithreaded kernel, but many changes were tightly coupled and went into this commit. Of interest is the implementation of the kthread_ api based on the design of pthreads; this library allows easy synchronization mechanisms and includes C++-style scoped locks. This commit also introduces new worker threads and tested mechanisms for interrupt handlers to schedule work in a kernel worker thread. A lot of code have been rewritten from scratch and has become a lot more stable and correct. Share and enjoy!
This commit is contained in:
parent
c518a37bef
commit
51e3de971c
55 changed files with 2615 additions and 2311 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
LibMaxsi is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
signal.h
|
||||
Handles the good old unix signals.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LIBMAXSI_SIGNAL_H
|
||||
#define LIBMAXSI_SIGNAL_H
|
||||
|
||||
#include "signalnum.h"
|
||||
|
||||
namespace Maxsi
|
||||
{
|
||||
namespace Signal
|
||||
{
|
||||
typedef void (*Handler)(int);
|
||||
|
||||
void Init();
|
||||
Handler RegisterHandler(int signum, Handler handler);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
LibMaxsi is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
signalnum.h
|
||||
Declares the signal-related constants.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LIBMAXSI_SIGNALNUM_H
|
||||
#define LIBMAXSI_SIGNALNUM_H
|
||||
|
||||
namespace Maxsi
|
||||
{
|
||||
namespace Signal
|
||||
{
|
||||
const int HUP = 1; /* Hangup */
|
||||
const int INT = 2; /* Interrupt */
|
||||
const int QUIT = 3; /* Quit */
|
||||
const int ILL = 4; /* Illegal Instruction */
|
||||
const int TRAP = 5; /* Trace/Breakpoint Trap */
|
||||
const int ABRT = 6; /* Abort */
|
||||
const int EMT = 7; /* Emulation Trap */
|
||||
const int FPE = 8; /* Arithmetic Exception */
|
||||
const int KILL = 9; /* Killed */
|
||||
const int BUS = 10; /* Bus Error */
|
||||
const int SEGV = 11; /* Segmentation Fault */
|
||||
const int SYS = 12; /* Bad System Call */
|
||||
const int PIPE = 13; /* Broken Pipe */
|
||||
const int ALRM = 14; /* Alarm Clock */
|
||||
const int TERM = 15; /* Terminated */
|
||||
const int USR1 = 16; /* User Signal 1 */
|
||||
const int USR2 = 17; /* User Signal 2 */
|
||||
const int CHLD = 18; /* Child Status */
|
||||
const int PWR = 19; /* Power Fail/Restart */
|
||||
const int WINCH = 20; /* Window Size Change */
|
||||
const int URG = 21; /* Urgent Socket Condition */
|
||||
const int STOP = 23; /* Stopped (signal) */
|
||||
const int TSTP = 24; /* Stopped (user) */
|
||||
const int CONT = 25; /* Continued */
|
||||
const int TTIN = 26; /* Stopped (tty input) */
|
||||
const int TTOU = 27; /* Stopped (tty output) */
|
||||
const int VTALRM = 28; /* Virtual Timer Expired */
|
||||
const int XCPU = 30; /* CPU time limit exceeded */
|
||||
const int XFSZ = 31; /* File size limit exceeded */
|
||||
const int WAITING = 32; /* All LWPs blocked */
|
||||
const int LWP = 33; /* Virtual Interprocessor Interrupt for Threads Library */
|
||||
const int AIO = 34; /* Asynchronous I/O */
|
||||
const int NUMSIGNALS = 35;
|
||||
|
||||
typedef void (*Handler)(int);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
@ -20,7 +20,7 @@
|
|||
syscall.h
|
||||
Assembly stubs for declaring system calls in libmaxsi.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifdef SORTIX_KERNEL
|
||||
#warning ===
|
||||
|
@ -32,6 +32,7 @@
|
|||
#define LIBMAXSI_SYSCALL_H
|
||||
|
||||
#include <sortix/syscallnum.h>
|
||||
#include <errno.h>
|
||||
|
||||
namespace Maxsi
|
||||
{
|
||||
|
@ -48,7 +49,10 @@ namespace Maxsi
|
|||
inline type fn() \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,10 @@ namespace Maxsi
|
|||
inline type fn(P1 p1) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -64,7 +71,10 @@ namespace Maxsi
|
|||
inline type fn(P1 p1, P2 p2) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -72,7 +82,10 @@ namespace Maxsi
|
|||
inline type fn(P1 p1, P2 p2, P3 p3) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -80,15 +93,21 @@ namespace Maxsi
|
|||
inline type fn(P1 p1, P2 p2, P3 p3, P4 p4) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4)); \
|
||||
return a; \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL5(type, fn, num, P1, P2, P3, P4, P5) \
|
||||
inline type fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4), "S" ((size_t)p5)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -96,14 +115,20 @@ namespace Maxsi
|
|||
inline void fn() \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL1_VOID(fn, num, P1) \
|
||||
inline void fn(P1 p1) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL2_VOID(fn, num, P1, P2) \
|
||||
|
@ -111,27 +136,38 @@ namespace Maxsi
|
|||
{ \
|
||||
size_t a; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL3_VOID(fn, num, P1, P2, P3) \
|
||||
inline void fn(P1 p1, P2 p2, P3 p3) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL4_VOID(fn, num, P1, P2, P3, P4) \
|
||||
inline void fn(P1 p1, P2 p2, P3 p3, P4 p4) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL5_VOID(fn, num, P1, P2, P3, P4, P5) \
|
||||
inline void fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((size_t)p1), "c" ((size_t)p2), "d" ((size_t)p3), "D" ((size_t)p4), "S" ((size_t)p5)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -143,7 +179,10 @@ namespace Maxsi
|
|||
type fn() \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -151,7 +190,10 @@ namespace Maxsi
|
|||
type fn(P1) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -159,7 +201,10 @@ namespace Maxsi
|
|||
type fn(P1, P2) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -167,7 +212,10 @@ namespace Maxsi
|
|||
type fn(P1, P2, P3) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -175,7 +223,10 @@ namespace Maxsi
|
|||
type fn(P1, P2, P3, P4) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -183,7 +234,10 @@ namespace Maxsi
|
|||
type fn(P1, P2, P3, P4, P5) \
|
||||
{ \
|
||||
type a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
return a; \
|
||||
}
|
||||
|
||||
|
@ -191,42 +245,60 @@ namespace Maxsi
|
|||
void fn() \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL1_VOID(fn, num, P1) \
|
||||
void fn(P1) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL2_VOID(fn, num, P1, P2) \
|
||||
void fn(P1, P2) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL3_VOID(fn, num, P1, P2, P3) \
|
||||
void fn(P1, P2, P3) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL4_VOID(fn, num, P1, P2, P3, P4) \
|
||||
void fn(P1, P2, P3, P4) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#define DEFN_SYSCALL5_VOID(fn, num, P1, P2, P3, P4, P5) \
|
||||
void fn(P1, P2, P3, P4, P5) \
|
||||
{ \
|
||||
size_t a; \
|
||||
int reterrno; \
|
||||
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
|
||||
asm volatile("movl %%edx, %0" : "=r"(reterrno)); \
|
||||
if ( reterrno ) { errno = reterrno; } \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
@ -20,7 +20,7 @@
|
|||
signal.h
|
||||
Signals.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
/* TODO: This does not fully implement POSIX 2008-1 yet! */
|
||||
|
||||
|
@ -28,42 +28,10 @@
|
|||
#define _SIGNAL_H 1
|
||||
|
||||
#include <features.h>
|
||||
#include <sortix/signal.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define SIGHUP 1 /* Hangup */
|
||||
#define SIGINT 2 /* Interrupt */
|
||||
#define SIGQUIT 3 /* Quit */
|
||||
#define SIGILL 4 /* Illegal Instruction */
|
||||
#define SIGTRAP 5 /* Trace/Breakpoint Trap */
|
||||
#define SIGABRT 6 /* Abort */
|
||||
#define SIGEMT 7 /* Emulation Trap */
|
||||
#define SIGFPE 8 /* Arithmetic Exception */
|
||||
#define SIGKILL 9 /* Killed */
|
||||
#define SIGBUS 10 /* Bus Error */
|
||||
#define SIGSEGV 11 /* Segmentation Fault */
|
||||
#define SIGSYS 12 /* Bad System Call */
|
||||
#define SIGPIPE 13 /* Broken Pipe */
|
||||
#define SIGALRM 14 /* Alarm Clock */
|
||||
#define SIGTERM 15 /* Terminated */
|
||||
#define SIGUSR1 16 /* User Signal 1 */
|
||||
#define SIGUSR2 17 /* User Signal 2 */
|
||||
#define SIGCHLD 18 /* Child Status */
|
||||
#define SIGPWR 19 /* Power Fail/Restart */
|
||||
#define SIGWINCH 20 /* Window Size Change */
|
||||
#define SIGURG 21 /* Urgent Socket Condition */
|
||||
#define SIGSTOP 23 /* Stopped (signal) */
|
||||
#define SIGTSTP 24 /* Stopped (user) */
|
||||
#define SIGCONT 25 /* Continued */
|
||||
#define SIGTTIN 26 /* Stopped (tty input) */
|
||||
#define SIGTTOU 27 /* Stopped (tty output) */
|
||||
#define SIGVTALRM 28 /* Virtual Timer Expired */
|
||||
#define SIGXCPU 30 /* CPU time limit exceeded */
|
||||
#define SIGXFSZ 31 /* File size limit exceeded */
|
||||
#define SIGWAITING 32 /* All LWPs blocked */
|
||||
#define SIGLWP 33 /* Virtual Interprocessor Interrupt for Threads Library */
|
||||
#define SIGAIO 34 /* Asynchronous I/O */
|
||||
|
||||
@include(pid_t.h);
|
||||
|
||||
typedef void (*sighandler_t)(int);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
|
@ -11,20 +11,19 @@
|
|||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
init.cpp
|
||||
Initializes the process by setting up the heap, signal handling,
|
||||
static memory and other useful things.
|
||||
Initializes the process by setting up the heap, signal handling, static
|
||||
memory and other useful things.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <libmaxsi/platform.h>
|
||||
#include <libmaxsi/signal.h>
|
||||
#include <libmaxsi/io.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include <string.h>
|
||||
|
@ -36,6 +35,7 @@ namespace Maxsi
|
|||
|
||||
extern "C" void init_error_functions();
|
||||
extern "C" void init_stdio();
|
||||
extern "C" void init_signal();
|
||||
|
||||
extern "C" void initialize_standard_library(int argc, char* argv[])
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ namespace Maxsi
|
|||
init_error_functions();
|
||||
|
||||
// It's probably best to initialize the Unix signals early on.
|
||||
Signal::Init();
|
||||
init_signal();
|
||||
|
||||
// Initialize the dynamic heap.
|
||||
Memory::Init();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
@ -26,7 +26,6 @@
|
|||
#include <libmaxsi/memory.h>
|
||||
#include <libmaxsi/syscall.h>
|
||||
#include <libmaxsi/process.h>
|
||||
#include <libmaxsi/signal.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
@ -34,6 +33,8 @@ namespace Maxsi
|
|||
{
|
||||
namespace Signal
|
||||
{
|
||||
typedef void (*Handler)(int);
|
||||
|
||||
void Core(int signum)
|
||||
{
|
||||
Process::Exit(128 + signum);
|
||||
|
@ -41,33 +42,33 @@ namespace Maxsi
|
|||
|
||||
extern "C" void SIG_DFL(int signum)
|
||||
{
|
||||
if ( signum == Signal::HUP ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::INT ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::QUIT ) { Core(signum); } else
|
||||
if ( signum == Signal::TRAP ) { Core(signum); } else
|
||||
if ( signum == Signal::ABRT ) { Core(signum); } else
|
||||
if ( signum == Signal::EMT ) { Core(signum); } else
|
||||
if ( signum == Signal::FPE ) { Core(signum); } else
|
||||
if ( signum == Signal::KILL ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::BUS ) { Core(signum); } else
|
||||
if ( signum == Signal::SEGV ) { Core(signum); } else
|
||||
if ( signum == Signal::SYS ) { Core(signum); } else
|
||||
if ( signum == Signal::PIPE ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::ALRM ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::TERM ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::USR1 ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::USR2 ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == Signal::CHLD ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::PWR ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::WINCH ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::URG ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::CONT ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::VTALRM ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::XCPU ) { Core(signum); } else
|
||||
if ( signum == Signal::XFSZ ) { Core(signum); } else
|
||||
if ( signum == Signal::WAITING ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::LWP ) { /* Ignore this signal. */ } else
|
||||
if ( signum == Signal::AIO ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGHUP ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGINT ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGQUIT ) { Core(signum); } else
|
||||
if ( signum == SIGTRAP ) { Core(signum); } else
|
||||
if ( signum == SIGABRT ) { Core(signum); } else
|
||||
if ( signum == SIGEMT ) { Core(signum); } else
|
||||
if ( signum == SIGFPE ) { Core(signum); } else
|
||||
if ( signum == SIGKILL ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGBUS ) { Core(signum); } else
|
||||
if ( signum == SIGSEGV ) { Core(signum); } else
|
||||
if ( signum == SIGSYS ) { Core(signum); } else
|
||||
if ( signum == SIGPIPE ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGALRM ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGTERM ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGUSR1 ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGUSR2 ) { Process::Exit(128 + signum); } else
|
||||
if ( signum == SIGCHLD ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGPWR ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGWINCH ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGURG ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGCONT ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGVTALRM ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGXCPU ) { Core(signum); } else
|
||||
if ( signum == SIGXFSZ ) { Core(signum); } else
|
||||
if ( signum == SIGWAITING ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGLWP ) { /* Ignore this signal. */ } else
|
||||
if ( signum == SIGAIO ) { /* Ignore this signal. */ } else
|
||||
{ /* Ignore this signal. */ }
|
||||
}
|
||||
|
||||
|
@ -94,10 +95,10 @@ namespace Maxsi
|
|||
}
|
||||
|
||||
DEFN_SYSCALL1_VOID(SysRegisterSignalHandler, SYSCALL_REGISTER_SIGNAL_HANDLER, sighandler_t);
|
||||
DEFN_SYSCALL0_VOID(SysSigReturn, SYSCALL_SIGRETURN);
|
||||
DEFN_SYSCALL2(int, SysKill, SYSCALL_KILL, pid_t, int);
|
||||
DEFN_SYSCALL1(int, SysRaise, SYSCALL_RAISE, int);
|
||||
|
||||
void Init()
|
||||
extern "C" void init_signal()
|
||||
{
|
||||
for ( int i = 0; i < MAX_SIGNALS; i++ )
|
||||
{
|
||||
|
@ -126,7 +127,7 @@ namespace Maxsi
|
|||
|
||||
extern "C" int raise(int signum)
|
||||
{
|
||||
kill(getpid(), signum);
|
||||
return SysRaise(signum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
@ -32,7 +32,5 @@ SignalHandlerAssembly:
|
|||
# The kernel put the signal id in edi.
|
||||
call SignalHandler
|
||||
|
||||
# Return control to the kernel, so normal computation can resume normally.
|
||||
movl $30, %eax # SysSigReturn
|
||||
int $0x80
|
||||
|
||||
# Return control to the kernel, so normal execution can continue.
|
||||
int $131
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
|||
|
||||
LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
@ -20,7 +20,7 @@
|
|||
signal.s
|
||||
An assembly stub that for handling unix signals.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
.globl SignalHandlerAssembly
|
||||
|
||||
|
@ -36,8 +36,5 @@ SignalHandlerAssembly:
|
|||
# Restore the stack as it was.
|
||||
addl $4, %esp
|
||||
|
||||
# Now that the stack is intact, return control to the kernel, so normal
|
||||
# computation can resume normally.
|
||||
movl $30, %eax # SysSigReturn
|
||||
int $0x80
|
||||
|
||||
# Return control to the kernel, so normal execution can continue.
|
||||
int $131
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
#include <sortix/kernel/kthread.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include "interrupt.h"
|
||||
#include "event.h"
|
||||
#include "stream.h"
|
||||
#include "syscall.h"
|
||||
#include "thread.h"
|
||||
#include "signal.h"
|
||||
#include "fs/devfs.h"
|
||||
#include "com.h"
|
||||
|
||||
|
|
13
sortix/cpu.h
13
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
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
event.cpp
|
||||
Each thread can wait for an event to happen and be signaled when it does.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include "thread.h"
|
||||
#include "syscall.h"
|
||||
#include "event.h"
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
Event::Event()
|
||||
{
|
||||
waiting = NULL;
|
||||
}
|
||||
|
||||
Event::~Event()
|
||||
{
|
||||
if ( waiting )
|
||||
{
|
||||
Panic("Thread was waiting on event, but it went out of scope");
|
||||
}
|
||||
}
|
||||
|
||||
void Event::Register()
|
||||
{
|
||||
Thread* thread = CurrentThread();
|
||||
if ( thread->event )
|
||||
{
|
||||
Panic("Thread tried to wait on an event, but was already waiting");
|
||||
}
|
||||
thread->event = this;
|
||||
thread->eventnextwaiting = waiting;
|
||||
waiting = thread;
|
||||
}
|
||||
|
||||
void Event::Signal()
|
||||
{
|
||||
while ( waiting )
|
||||
{
|
||||
waiting->event = NULL;
|
||||
Syscall::ScheduleResumption(waiting);
|
||||
waiting = waiting->eventnextwaiting;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Okay, I realize this is O(N), refactor this to a linked list.
|
||||
void Event::Unregister(Thread* thread)
|
||||
{
|
||||
if ( thread->event != this ) { return; }
|
||||
thread->event = NULL;
|
||||
if ( waiting == thread ) { waiting = thread->eventnextwaiting; return; }
|
||||
for ( Thread* tmp = waiting; tmp; tmp = tmp->eventnextwaiting )
|
||||
{
|
||||
if ( tmp->eventnextwaiting == thread )
|
||||
{
|
||||
tmp->eventnextwaiting = thread->eventnextwaiting;
|
||||
break;
|
||||
}
|
||||
}
|
||||
thread->eventnextwaiting = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
event.h
|
||||
Each thread can wait for an event to happen and be signaled when it does.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_EVENT_H
|
||||
#define SORTIX_EVENT_H
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
class Thread;
|
||||
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
Event();
|
||||
~Event();
|
||||
|
||||
public:
|
||||
void Register();
|
||||
void Signal();
|
||||
void Unregister(Thread* thread);
|
||||
|
||||
private:
|
||||
Thread* waiting;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -18,29 +18,39 @@
|
|||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
kthread.h
|
||||
Fake header providing noop threading functions. This is simply forward
|
||||
compatibility with the upcoming kthread branch and to ease merging.
|
||||
Utility and synchronization mechanisms for kernel threads.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_KTHREAD_H
|
||||
#define SORTIX_KTHREAD_H
|
||||
|
||||
#define GOT_FAKE_KTHREAD
|
||||
#warning Using noop kthread functions
|
||||
#include <sortix/signal.h>
|
||||
#include "../../../signal.h"
|
||||
|
||||
#define GOT_ACTUAL_KTHREAD
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
extern "C" {
|
||||
|
||||
inline static void kthread_yield(void) { asm volatile ("int $129"); }
|
||||
void kthread_exit(void* param = NULL) SORTIX_NORETURN;
|
||||
typedef unsigned kthread_mutex_t;
|
||||
const kthread_mutex_t KTHREAD_MUTEX_INITIALIZER = 0;
|
||||
unsigned kthread_mutex_trylock(kthread_mutex_t* mutex);
|
||||
void kthread_mutex_lock(kthread_mutex_t* mutex);
|
||||
unsigned long kthread_mutex_lock_signal(kthread_mutex_t* mutex);
|
||||
void kthread_mutex_unlock(kthread_mutex_t* mutex);
|
||||
typedef unsigned kthread_cond_t;
|
||||
const kthread_cond_t KTHREAD_COND_INITIALIZER = 0;
|
||||
struct kthread_cond_elem;
|
||||
typedef struct kthread_cond_elem kthread_cond_elem_t;
|
||||
struct kthread_cond
|
||||
{
|
||||
kthread_cond_elem_t* first;
|
||||
kthread_cond_elem_t* last;
|
||||
};
|
||||
typedef struct kthread_cond kthread_cond_t;
|
||||
const kthread_cond_t KTHREAD_COND_INITIALIZER = { NULL, NULL };
|
||||
void kthread_cond_wait(kthread_cond_t* cond, kthread_mutex_t* mutex);
|
||||
unsigned long kthread_cond_wait_signal(kthread_cond_t* cond, kthread_mutex_t* mutex);
|
||||
void kthread_cond_signal(kthread_cond_t* cond);
|
||||
|
@ -74,6 +84,7 @@ public:
|
|||
{
|
||||
this->mutex = mutex;
|
||||
this->acquired = kthread_mutex_lock_signal(mutex);
|
||||
ASSERT(acquired || Signal::IsPending());
|
||||
}
|
||||
|
||||
~ScopedLockSignal()
|
||||
|
|
|
@ -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);
|
||||
|
|
79
sortix/include/sortix/signal.h
Normal file
79
sortix/include/sortix/signal.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
signal.h
|
||||
Defines the numeric values for the various supported signals.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_INCLUDE_SIGNAL_H
|
||||
#define SORTIX_INCLUDE_SIGNAL_H
|
||||
|
||||
/* TODO: Yes, signals are implemented in a non-standard manner for now. */
|
||||
|
||||
#include <features.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define SIGHUP 1 /* Hangup */
|
||||
#define SIGINT 2 /* Interrupt */
|
||||
#define SIGQUIT 3 /* Quit */
|
||||
#define SIGILL 4 /* Illegal Instruction */
|
||||
#define SIGTRAP 5 /* Trace/Breakpoint Trap */
|
||||
#define SIGABRT 6 /* Abort */
|
||||
#define SIGEMT 7 /* Emulation Trap */
|
||||
#define SIGFPE 8 /* Arithmetic Exception */
|
||||
#define SIGKILL 9 /* Killed */
|
||||
#define SIGBUS 10 /* Bus Error */
|
||||
#define SIGSEGV 11 /* Segmentation Fault */
|
||||
#define SIGSYS 12 /* Bad System Call */
|
||||
#define SIGPIPE 13 /* Broken Pipe */
|
||||
#define SIGALRM 14 /* Alarm Clock */
|
||||
#define SIGTERM 15 /* Terminated */
|
||||
#define SIGUSR1 16 /* User Signal 1 */
|
||||
#define SIGUSR2 17 /* User Signal 2 */
|
||||
#define SIGCHLD 18 /* Child Status */
|
||||
#define SIGPWR 19 /* Power Fail/Restart */
|
||||
#define SIGWINCH 20 /* Window Size Change */
|
||||
#define SIGURG 21 /* Urgent Socket Condition */
|
||||
#define SIGSTOP 23 /* Stopped (signal) */
|
||||
#define SIGTSTP 24 /* Stopped (user) */
|
||||
#define SIGCONT 25 /* Continued */
|
||||
#define SIGTTIN 26 /* Stopped (tty input) */
|
||||
#define SIGTTOU 27 /* Stopped (tty output) */
|
||||
#define SIGVTALRM 28 /* Virtual Timer Expired */
|
||||
#define SIGXCPU 30 /* CPU time limit exceeded */
|
||||
#define SIGXFSZ 31 /* File size limit exceeded */
|
||||
#define SIGWAITING 32 /* All LWPs blocked */
|
||||
#define SIGLWP 33 /* Virtual Interprocessor Interrupt for Threads Library */
|
||||
#define SIGAIO 34 /* Asynchronous I/O */
|
||||
#define SIG__NUM_DECLARED 35
|
||||
#define SIG_MAX_NUM 128
|
||||
|
||||
#define SIG_PRIO_NORMAL 0
|
||||
#define SIG_PRIO_HIGH 1
|
||||
#define SIG_PRIO_STOP 2
|
||||
#define SIG_PRIO_CORE 3
|
||||
#define SIG_PRIO_KILL 4
|
||||
#define SIG_NUM_LEVELS 5
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -23,14 +23,16 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "x86-family/idt.h"
|
||||
#include "interrupt.h"
|
||||
#include "scheduler.h"
|
||||
#include "syscall.h"
|
||||
|
||||
#include "process.h" // Hack for SIGSEGV
|
||||
#include "sound.h" // Hack for SIGSEGV
|
||||
#include "thread.h" // HACK FOR SIGSEGV
|
||||
#include "syscall.h" // HACK FOR SIGSEGV
|
||||
#include "scheduler.h" // HACK FOR SIGSEGV
|
||||
|
||||
using namespace Maxsi;
|
||||
|
||||
namespace Sortix {
|
||||
void SysExit(int status); // HACK
|
||||
|
@ -53,8 +55,10 @@ const uint8_t PIC_MODE_BUF_SLAVE = 0x08; // Buffered mode/slave
|
|||
const uint8_t PIC_MODE_BUF_MASTER = 0x0C; // Buffered mode/master
|
||||
const uint8_t PIC_MODE_SFNM = 0x10; // Special fully nested (not)
|
||||
|
||||
extern "C" { unsigned long asm_is_cpu_interrupted = 0; }
|
||||
const bool DEBUG_EXCEPTION = false;
|
||||
const bool DEBUG_IRQ = false;
|
||||
const bool DEBUG_ISR = false;
|
||||
bool initialized;
|
||||
|
||||
const size_t NUM_KNOWN_EXCEPTIONS = 20;
|
||||
|
@ -237,12 +241,20 @@ void CrashHandler(CPU::InterruptRegisters* regs)
|
|||
|
||||
Sound::Mute();
|
||||
|
||||
CurrentProcess()->Exit(139);
|
||||
Scheduler::ProcessTerminated(regs);
|
||||
// Exit the process with the right error code.
|
||||
// TODO: Sent a SIGINT, SIGBUS, or whatever instead.
|
||||
SysExit(139);
|
||||
}
|
||||
|
||||
void ISRHandler(Sortix::CPU::InterruptRegisters* regs)
|
||||
{
|
||||
if ( DEBUG_ISR )
|
||||
{
|
||||
Log::PrintF("ISR%u ", regs->int_no);
|
||||
regs->LogRegisters();
|
||||
Log::Print("\n");
|
||||
}
|
||||
|
||||
if ( regs->int_no < 32 )
|
||||
{
|
||||
CrashHandler(regs);
|
||||
|
@ -289,6 +301,109 @@ extern "C" void interrupt_handler(Sortix::CPU::InterruptRegisters* regs)
|
|||
else { ISRHandler(regs); }
|
||||
}
|
||||
|
||||
// TODO: This implementation is a bit hacky and can be optimized.
|
||||
|
||||
uint8_t* queue;
|
||||
uint8_t* storage;
|
||||
volatile size_t queueoffset;
|
||||
volatile size_t queueused;
|
||||
size_t queuesize;
|
||||
|
||||
struct Package
|
||||
{
|
||||
size_t size;
|
||||
size_t payloadoffset;
|
||||
size_t payloadsize;
|
||||
WorkHandler handler; // TODO: May not be correctly aligned on some systems.
|
||||
uint8_t payload[0];
|
||||
};
|
||||
|
||||
void InitWorker()
|
||||
{
|
||||
const size_t QUEUE_SIZE = 4UL*1024UL;
|
||||
STATIC_ASSERT(QUEUE_SIZE % sizeof(Package) == 0);
|
||||
queue = new uint8_t[QUEUE_SIZE];
|
||||
if ( !queue ) { Panic("Can't allocate interrupt worker queue"); }
|
||||
storage = new uint8_t[QUEUE_SIZE];
|
||||
if ( !storage ) { Panic("Can't allocate interrupt worker storage"); }
|
||||
queuesize = QUEUE_SIZE;
|
||||
queueoffset = 0;
|
||||
queueused = 0;
|
||||
}
|
||||
|
||||
static void WriteToQueue(const void* src, size_t size)
|
||||
{
|
||||
const uint8_t* buf = (const uint8_t*) src;
|
||||
size_t writeat = (queueoffset + queueused) % queuesize;
|
||||
size_t available = queuesize - writeat;
|
||||
size_t count = available < size ? available : size;
|
||||
Memory::Copy(queue + writeat, buf, count);
|
||||
queueused += count;
|
||||
if ( count < size ) { WriteToQueue(buf + count, size - count); }
|
||||
}
|
||||
|
||||
static void ReadFromQueue(void* dest, size_t size)
|
||||
{
|
||||
uint8_t* buf = (uint8_t*) dest;
|
||||
size_t available = queuesize - queueoffset;
|
||||
size_t count = available < size ? available : size;
|
||||
Memory::Copy(buf, queue + queueoffset, count);
|
||||
queueused -= count;
|
||||
queueoffset = (queueoffset + count) % queuesize;
|
||||
if ( count < size ) { ReadFromQueue(buf + count, size - count); }
|
||||
}
|
||||
|
||||
static Package* PopPackage(uint8_t** payloadp, Package* /*prev*/)
|
||||
{
|
||||
Package* package = NULL;
|
||||
uint8_t* payload = NULL;
|
||||
Interrupt::Disable();
|
||||
|
||||
if ( !queueused ) { goto out; }
|
||||
|
||||
package = (Package*) storage;
|
||||
ReadFromQueue(package, sizeof(*package));
|
||||
payload = storage + sizeof(*package);
|
||||
ReadFromQueue(payload, package->payloadsize);
|
||||
*payloadp = payload;
|
||||
|
||||
out:
|
||||
Interrupt::Enable();
|
||||
return package;
|
||||
}
|
||||
|
||||
void WorkerThread(void* /*user*/)
|
||||
{
|
||||
ASSERT(Interrupt::IsEnabled());
|
||||
uint8_t* payload = NULL;
|
||||
Package* package = NULL;
|
||||
while ( true )
|
||||
{
|
||||
package = PopPackage(&payload, package);
|
||||
if ( !package ) { Scheduler::Yield(); continue; }
|
||||
size_t payloadsize = package->payloadsize;
|
||||
package->handler(payload, payloadsize);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize)
|
||||
{
|
||||
ASSERT(!Interrupt::IsEnabled());
|
||||
|
||||
Package package;
|
||||
package.size = sizeof(package) + payloadsize;
|
||||
package.payloadoffset = 0; // Currently unused
|
||||
package.payloadsize = payloadsize;
|
||||
package.handler = handler;
|
||||
|
||||
size_t queuefreespace = queuesize - queueused;
|
||||
if ( queuefreespace < package.size ) { return false; }
|
||||
|
||||
WriteToQueue(&package, sizeof(package));
|
||||
WriteToQueue(payload, payloadsize);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Interrupt
|
||||
} // namespace Sortix
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -24,16 +24,19 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/log.h>
|
||||
#include <sortix/kernel/panic.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <sortix/kernel/pci.h>
|
||||
#include <sortix/kernel/worker.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include <libmaxsi/string.h>
|
||||
#include <libmaxsi/format.h>
|
||||
#include <sortix/kernel/log.h>
|
||||
#include <sortix/kernel/panic.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
#include <sortix/mman.h>
|
||||
#include "kernelinfo.h"
|
||||
#include "x86-family/gdt.h"
|
||||
#include "time.h"
|
||||
|
@ -43,6 +46,7 @@
|
|||
#include "thread.h"
|
||||
#include "process.h"
|
||||
#include "scheduler.h"
|
||||
#include "signal.h"
|
||||
#include "syscall.h"
|
||||
#include "ata.h"
|
||||
#include "com.h"
|
||||
|
@ -67,7 +71,8 @@
|
|||
using namespace Maxsi;
|
||||
|
||||
// Keep the stack size aligned with $CPU/base.s
|
||||
extern "C" { size_t stack[64*1024 / sizeof(size_t)] = {0}; }
|
||||
const size_t STACK_SIZE = 64*1024;
|
||||
extern "C" { size_t stack[STACK_SIZE / sizeof(size_t)] = {0}; }
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
|
@ -97,6 +102,11 @@ void DoWelcome()
|
|||
Log::Print(" BOOTING OPERATING SYSTEM... ");
|
||||
}
|
||||
|
||||
// Forward declarations.
|
||||
static void BootThread(void* user);
|
||||
static void InitThread(void* user);
|
||||
static void SystemIdleThread(void* user);
|
||||
|
||||
static size_t PrintToTextTerminal(void* user, const char* str, size_t len)
|
||||
{
|
||||
return ((TextTerminal*) user)->Print(str, len);
|
||||
|
@ -146,7 +156,7 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
addr_t initrd = NULL;
|
||||
size_t initrdsize = 0;
|
||||
|
||||
uint32_t* modules = (uint32_t*) bootinfo->mods_addr;
|
||||
uint32_t* modules = (uint32_t*) (addr_t) bootinfo->mods_addr;
|
||||
for ( uint32_t i = 0; i < bootinfo->mods_count; i++ )
|
||||
{
|
||||
initrdsize = modules[2*i+1] - modules[2*i+0];
|
||||
|
@ -170,6 +180,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
// Initialize the kernel heap.
|
||||
Maxsi::Memory::Init();
|
||||
|
||||
// Initialize the interrupt worker.
|
||||
Interrupt::InitWorker();
|
||||
|
||||
// Initialize the list of kernel devices.
|
||||
DeviceFS::Init();
|
||||
|
||||
|
@ -212,6 +225,12 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
// Initialize the scheduler.
|
||||
Scheduler::Init();
|
||||
|
||||
// Initialize Unix Signals.
|
||||
Signal::Init();
|
||||
|
||||
// Initialize the worker thread data structures.
|
||||
Worker::Init();
|
||||
|
||||
// Initialize the kernel information query syscall.
|
||||
Info::Init();
|
||||
|
||||
|
@ -230,59 +249,139 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
// Initialize the BGA driver.
|
||||
BGA::Init();
|
||||
|
||||
// Alright, now the system's drivers are loaded and initialized. It is
|
||||
// time to load the initial user-space programs and start execution of
|
||||
// the actual operating system.
|
||||
// Now that the base system has been loaded, it's time to go threaded. First
|
||||
// we create an object that represents this thread.
|
||||
Process* system = new Process;
|
||||
if ( !system ) { Panic("Could not allocate the system process"); }
|
||||
addr_t systemaddrspace = Memory::GetAddressSpace();
|
||||
system->addrspace = systemaddrspace;
|
||||
|
||||
uint32_t inode;
|
||||
byte* program;
|
||||
size_t programsize;
|
||||
|
||||
// Create an address space for the idle process.
|
||||
addr_t idleaddrspace = Memory::Fork();
|
||||
if ( !idleaddrspace ) { Panic("could not fork an idle process address space"); }
|
||||
|
||||
// Create an address space for the initial process.
|
||||
addr_t initaddrspace = Memory::Fork();
|
||||
if ( !initaddrspace ) { Panic("could not fork an initial process address space"); }
|
||||
|
||||
// Create the system idle process.
|
||||
Process* idle = new Process;
|
||||
if ( !idle ) { Panic("could not allocate idle process"); }
|
||||
idle->addrspace = idleaddrspace;
|
||||
Memory::SwitchAddressSpace(idleaddrspace);
|
||||
Scheduler::SetDummyThreadOwner(idle);
|
||||
inode = InitRD::Traverse(InitRD::Root(), "idle");
|
||||
if ( inode == NULL ) { PanicF("initrd did not contain 'idle'"); }
|
||||
program = InitRD::Open(inode, &programsize);
|
||||
if ( program == NULL ) { PanicF("initrd did not contain 'idle'"); }
|
||||
addr_t idlestart = ELF::Construct(idle, program, programsize);
|
||||
if ( !idlestart ) { Panic("could not construct ELF image for idle process"); }
|
||||
Thread* idlethread = CreateThread(idlestart);
|
||||
if ( !idlethread ) { Panic("could not create thread for the idle process"); }
|
||||
// We construct this thread manually for bootstrap reasons. We wish to
|
||||
// create a kernel thread that is the current thread and isn't put into the
|
||||
// scheduler's set of runnable threads, but rather run whenever there is
|
||||
// _nothing_ else to run on this CPU.
|
||||
Thread* idlethread = new Thread;
|
||||
idlethread->process = system;
|
||||
idlethread->kernelstackpos = (addr_t) stack;
|
||||
idlethread->kernelstacksize = STACK_SIZE;
|
||||
idlethread->kernelstackmalloced = false;
|
||||
system->firstthread = idlethread;
|
||||
Scheduler::SetIdleThread(idlethread);
|
||||
|
||||
// Create the initial process.
|
||||
Process* init = new Process;
|
||||
if ( !init ) { Panic("could not allocate init process"); }
|
||||
init->addrspace = initaddrspace;
|
||||
Memory::SwitchAddressSpace(initaddrspace);
|
||||
Scheduler::SetDummyThreadOwner(init);
|
||||
inode = InitRD::Traverse(InitRD::Root(), "init");
|
||||
if ( inode == NULL ) { PanicF("initrd did not contain 'init'"); }
|
||||
program = InitRD::Open(inode, &programsize);
|
||||
if ( program == NULL ) { PanicF("initrd did not contain 'init'"); }
|
||||
addr_t initstart = ELF::Construct(init, program, programsize);
|
||||
if ( !initstart ) { Panic("could not construct ELF image for init process"); }
|
||||
Thread* initthread = CreateThread(initstart);
|
||||
if ( !initthread ) { Panic("could not create thread for the init process"); }
|
||||
Scheduler::SetInitProcess(init);
|
||||
// Let's create a regular kernel thread that can decide what happens next.
|
||||
// Note that we don't do the work here: should it block, then there is
|
||||
// nothing to run. Therefore we must become the system idle thread.
|
||||
RunKernelThread(BootThread, NULL);
|
||||
|
||||
// Lastly set up the timer driver and we are ready to run the OS.
|
||||
// The time driver will run the scheduler on the next timer interrupt.
|
||||
Time::Init();
|
||||
|
||||
// Run the OS.
|
||||
Scheduler::MainLoop();
|
||||
// Become the system idle thread.
|
||||
SystemIdleThread(NULL);
|
||||
}
|
||||
|
||||
static void SystemIdleThread(void* /*user*/)
|
||||
{
|
||||
// Alright, we are now the system idle thread. If there is nothing to do,
|
||||
// then we are run. Note that we must never do any real work here.
|
||||
while(true);
|
||||
}
|
||||
|
||||
static void BootThread(void* /*user*/)
|
||||
{
|
||||
// Hello, threaded world! You can now regard the kernel as a multi-threaded
|
||||
// process with super-root access to the system. Before we boot the full
|
||||
// system we need to start some worker threads.
|
||||
|
||||
// Let's create the interrupt worker thread that executes additional work
|
||||
// requested by interrupt handlers, where such work isn't safe.
|
||||
Thread* interruptworker = RunKernelThread(Interrupt::WorkerThread, NULL);
|
||||
if ( !interruptworker )
|
||||
Panic("Could not create interrupt worker");
|
||||
|
||||
// Create a general purpose worker thread.
|
||||
Thread* workerthread = RunKernelThread(Worker::Thread, NULL);
|
||||
if ( !workerthread )
|
||||
Panic("Unable to create general purpose worker thread");
|
||||
|
||||
// Finally, let's transfer control to a new kernel process that will
|
||||
// eventually run user-space code known as the operating system.
|
||||
addr_t initaddrspace = Memory::Fork();
|
||||
if ( !initaddrspace ) { Panic("Could not create init's address space"); }
|
||||
|
||||
Process* init = new Process;
|
||||
if ( !init ) { Panic("Could not allocate init process"); }
|
||||
|
||||
CurrentProcess()->AddChildProcess(init);
|
||||
|
||||
init->addrspace = initaddrspace;
|
||||
Scheduler::SetInitProcess(init);
|
||||
|
||||
Thread* initthread = RunKernelThread(init, InitThread, NULL);
|
||||
if ( !initthread ) { Panic("Coul not create init thread"); }
|
||||
|
||||
// Wait until init init is done and then shut down the computer.
|
||||
int status;
|
||||
pid_t pid = CurrentProcess()->Wait(init->pid, &status, 0);
|
||||
if ( pid != init->pid )
|
||||
PanicF("Waiting for init to exit returned %i (errno=%i)", pid, errno);
|
||||
|
||||
switch ( status )
|
||||
{
|
||||
case 0: CPU::ShutDown();
|
||||
case 1: CPU::Reboot();
|
||||
default:
|
||||
PanicF("Init returned with unexpected return code %i", status);
|
||||
}
|
||||
}
|
||||
|
||||
static void InitThread(void* /*user*/)
|
||||
{
|
||||
// We are the init process's first thread. Let's load the init program from
|
||||
// the init ramdisk and transfer execution to it. We will then become a
|
||||
// regular user-space program with root permissions.
|
||||
|
||||
Thread* thread = CurrentThread();
|
||||
Process* process = CurrentProcess();
|
||||
|
||||
uint32_t inode = InitRD::Traverse(InitRD::Root(), "init");
|
||||
if ( !inode ) { Panic("InitRD did not contain an 'init' program."); }
|
||||
|
||||
size_t programsize;
|
||||
uint8_t* program = InitRD::Open(inode, &programsize);
|
||||
if ( !program ) { Panic("InitRD did not contain an 'init' program."); }
|
||||
|
||||
const size_t DEFAULT_STACK_SIZE = 64UL * 1024UL;
|
||||
|
||||
size_t stacksize = 0;
|
||||
if ( !stacksize ) { stacksize = DEFAULT_STACK_SIZE; }
|
||||
|
||||
addr_t stackpos = process->AllocVirtualAddr(stacksize);
|
||||
if ( !stackpos ) { Panic("Could not allocate init stack space"); }
|
||||
|
||||
int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE;
|
||||
if ( !Memory::MapRange(stackpos, stacksize, prot) )
|
||||
{
|
||||
Panic("Could not allocate init stack memory");
|
||||
}
|
||||
|
||||
thread->stackpos = stackpos;
|
||||
thread->stacksize = stacksize;
|
||||
|
||||
int argc = 1;
|
||||
const char* argv[] = { "init", NULL };
|
||||
int envc = 0;
|
||||
const char* envp[] = { NULL };
|
||||
CPU::InterruptRegisters regs;
|
||||
|
||||
if ( process->Execute("init", program, programsize, argc, argv, envc, envp,
|
||||
®s) )
|
||||
{
|
||||
Panic("Unable to execute init program");
|
||||
}
|
||||
|
||||
// Now become the init process and the operation system shall run.
|
||||
CPU::LoadRegisters(®s);
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
||||
|
|
|
@ -18,52 +18,107 @@
|
|||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
kthread.cpp
|
||||
Fake header providing noop threading functions. This is simply forward
|
||||
compatibility with the upcoming kthread branch and to ease merging.
|
||||
Utility and synchronization mechanisms for kernel threads.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/worker.h>
|
||||
#include <sortix/signal.h>
|
||||
#include "signal.h"
|
||||
#include "thread.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
// This isn't as bad as it looks. Kernel code traditionally runs with interrupts
|
||||
// disabled so there are no race conditions.
|
||||
|
||||
extern "C" unsigned kthread_mutex_trylock(kthread_mutex_t* mutex)
|
||||
// The kernel thread needs another stack to delete its own stack.
|
||||
static void kthread_do_kill_thread(void* user)
|
||||
{
|
||||
Thread* thread = (Thread*) user;
|
||||
while ( thread->state != Thread::State::DEAD )
|
||||
kthread_yield();
|
||||
delete thread;
|
||||
}
|
||||
|
||||
extern "C" void kthread_mutex_lock(kthread_mutex_t* mutex)
|
||||
extern "C" void kthread_exit(void* /*param*/)
|
||||
{
|
||||
Worker::Schedule(kthread_do_kill_thread, CurrentThread());
|
||||
Scheduler::ExitThread();
|
||||
}
|
||||
|
||||
unsigned long kthread_mutex_lock_signal(kthread_mutex_t* mutex)
|
||||
struct kthread_cond_elem
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" void kthread_mutex_unlock(kthread_mutex_t* mutex)
|
||||
{
|
||||
}
|
||||
kthread_cond_elem_t* next;
|
||||
volatile unsigned long woken;
|
||||
};
|
||||
|
||||
extern "C" void kthread_cond_wait(kthread_cond_t* cond, kthread_mutex_t* mutex)
|
||||
{
|
||||
kthread_cond_elem_t elem;
|
||||
elem.next = NULL;
|
||||
elem.woken = 0;
|
||||
if ( cond->last ) { cond->last->next = &elem; }
|
||||
if ( !cond->last ) { cond->last = cond->first = &elem; }
|
||||
while ( !elem.woken )
|
||||
{
|
||||
kthread_mutex_unlock(mutex);
|
||||
Scheduler::Yield();
|
||||
kthread_mutex_lock(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" unsigned long kthread_cond_wait_signal(kthread_cond_t* cond,
|
||||
kthread_mutex_t* mutex)
|
||||
{
|
||||
if ( Signal::IsPending() )
|
||||
return 0;
|
||||
kthread_cond_elem_t elem;
|
||||
elem.next = NULL;
|
||||
elem.woken = 0;
|
||||
if ( cond->last ) { cond->last->next = &elem; }
|
||||
if ( !cond->last ) { cond->last = cond->first = &elem; }
|
||||
while ( !elem.woken )
|
||||
{
|
||||
if ( Signal::IsPending() )
|
||||
{
|
||||
if ( cond->first == &elem )
|
||||
{
|
||||
cond->first = elem.next;
|
||||
if ( cond->last == &elem )
|
||||
cond->last = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
kthread_cond_elem_t* prev = cond->first;
|
||||
while ( prev->next != &elem )
|
||||
prev = prev->next;
|
||||
prev->next = elem.next;
|
||||
if ( cond->last == &elem )
|
||||
cond->last = prev;
|
||||
}
|
||||
// Note that the thread still owns the mutex.
|
||||
return 0;
|
||||
}
|
||||
kthread_mutex_unlock(mutex);
|
||||
Scheduler::Yield();
|
||||
kthread_mutex_lock(mutex);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" void kthread_cond_signal(kthread_cond_t* cond)
|
||||
{
|
||||
kthread_cond_elem_t* elem = cond->first;
|
||||
if ( !elem ) { return; }
|
||||
if ( !(cond->first = elem->next) ) { cond->last = NULL; }
|
||||
elem->next = NULL;
|
||||
elem->woken = 1;
|
||||
}
|
||||
|
||||
extern "C" void kthread_cond_broadcast(kthread_cond_t* cond)
|
||||
{
|
||||
while ( cond->first ) { kthread_cond_signal(cond); }
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
||||
|
|
|
@ -24,10 +24,12 @@
|
|||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/keycodes.h>
|
||||
#include <sortix/signal.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "utf8.h"
|
||||
#include "keyboard.h"
|
||||
#include "process.h"
|
||||
#include "scheduler.h"
|
||||
#include "terminal.h"
|
||||
#include "logterminal.h"
|
||||
|
@ -130,7 +132,10 @@ namespace Sortix
|
|||
if ( kbkey == -KBKEY_LCTRL ) { control = false; }
|
||||
if ( mode & TERMMODE_SIGNAL && control && kbkey == KBKEY_C )
|
||||
{
|
||||
Scheduler::SigIntHack();
|
||||
pid_t pid = Process::HackGetForegroundProcess();
|
||||
Process* process = Process::Get(pid);
|
||||
if ( process )
|
||||
process->DeliverSignal(SIGINT);
|
||||
return;
|
||||
}
|
||||
if ( mode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D )
|
||||
|
@ -299,7 +304,6 @@ namespace Sortix
|
|||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return sofar;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
logterminal.h
|
||||
A simple terminal that writes to the kernel log.
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <sortix/kernel/platform.h>
|
||||
#include <libmaxsi/string.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "interrupt.h"
|
||||
#include <sortix/kernel/log.h>
|
||||
#include "calltrace.h"
|
||||
#include <sortix/kernel/panic.h>
|
||||
|
@ -43,6 +44,7 @@ namespace Sortix
|
|||
|
||||
void PanicInit()
|
||||
{
|
||||
Interrupt::Disable();
|
||||
if ( panicing )
|
||||
{
|
||||
Log::PrintF("Panic while panicing:\n");
|
||||
|
|
|
@ -24,11 +24,13 @@
|
|||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/signal.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#ifdef GOT_FAKE_KTHREAD
|
||||
#include "event.h"
|
||||
#endif
|
||||
#include "signal.h"
|
||||
#include "thread.h"
|
||||
#include "process.h"
|
||||
#include "syscall.h"
|
||||
|
@ -159,8 +161,7 @@ namespace Sortix
|
|||
}
|
||||
if ( !anyreading )
|
||||
{
|
||||
// TODO: Implement better signal support and uncomment.
|
||||
//CurrentThread()->DeliverSignal(SIGPIPE);
|
||||
CurrentThread()->DeliverSignal(SIGPIPE);
|
||||
Error::Set(EPIPE);
|
||||
return -1;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,19 +14,21 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
process.h
|
||||
Describes a process belonging to a subsystem.
|
||||
A named collection of threads.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_PROCESS_H
|
||||
#define SORTIX_PROCESS_H
|
||||
|
||||
#include "descriptors.h"
|
||||
#include "cpu.h"
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/fork.h>
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
|
@ -34,7 +36,6 @@ namespace Sortix
|
|||
class Process;
|
||||
struct ProcessSegment;
|
||||
|
||||
const size_t DEFAULT_STACK_SIZE = 64*1024;
|
||||
const int SEG_NONE = 0;
|
||||
const int SEG_TEXT = 1;
|
||||
const int SEG_DATA = 2;
|
||||
|
@ -61,6 +62,8 @@ namespace Sortix
|
|||
|
||||
class Process
|
||||
{
|
||||
friend void Process__OnLastThreadExit(void*);
|
||||
|
||||
public:
|
||||
Process();
|
||||
~Process();
|
||||
|
@ -73,28 +76,35 @@ namespace Sortix
|
|||
|
||||
public:
|
||||
addr_t addrspace;
|
||||
int exitstatus;
|
||||
char* workingdir;
|
||||
pid_t pid;
|
||||
int* errnop;
|
||||
|
||||
public:
|
||||
private:
|
||||
// A process may only access its parent if parentlock is locked. A process
|
||||
// may only use its list of children if childlock is locked. A process may
|
||||
// not access its sibling processes.
|
||||
Process* parent;
|
||||
Process* prevsibling;
|
||||
Process* nextsibling;
|
||||
Process* firstchild;
|
||||
Process* zombiechild;
|
||||
kthread_mutex_t childlock;
|
||||
kthread_mutex_t parentlock;
|
||||
kthread_cond_t zombiecond;
|
||||
size_t zombiewaiting;
|
||||
bool iszombie;
|
||||
bool nozombify;
|
||||
addr_t mmapfrom;
|
||||
int exitstatus;
|
||||
|
||||
public:
|
||||
Thread* firstthread;
|
||||
kthread_mutex_t threadlock;
|
||||
|
||||
public:
|
||||
DescriptorTable descriptors;
|
||||
ProcessSegment* segments;
|
||||
|
||||
public:
|
||||
bool sigint;
|
||||
|
||||
public:
|
||||
int Execute(const char* programname, const byte* program,
|
||||
size_t programsize, int argc, const char* const* argv,
|
||||
|
@ -102,36 +112,33 @@ namespace Sortix
|
|||
CPU::InterruptRegisters* regs);
|
||||
void ResetAddressSpace();
|
||||
void Exit(int status);
|
||||
|
||||
public:
|
||||
bool IsSane() { return addrspace != 0; }
|
||||
pid_t Wait(pid_t pid, int* status, int options);
|
||||
bool DeliverSignal(int signum);
|
||||
void OnThreadDestruction(Thread* thread);
|
||||
int GetParentProcessId();
|
||||
void AddChildProcess(Process* child);
|
||||
void ScheduleDeath();
|
||||
void AbortConstruction();
|
||||
|
||||
public:
|
||||
Process* Fork();
|
||||
|
||||
private:
|
||||
Thread* ForkThreads(Process* processclone);
|
||||
void ExecuteCPU(int argc, char** argv, int envc, char** envp,
|
||||
addr_t stackpos, addr_t entry,
|
||||
CPU::InterruptRegisters* regs);
|
||||
void OnLastThreadExit();
|
||||
void LastPrayer();
|
||||
void NotifyChildExit(Process* child, bool zombify);
|
||||
void NotifyNewZombies();
|
||||
|
||||
public:
|
||||
void ResetForExecute();
|
||||
|
||||
public:
|
||||
inline size_t DefaultStackSize() { return DEFAULT_STACK_SIZE; }
|
||||
|
||||
private:
|
||||
addr_t mmapfrom;
|
||||
|
||||
public:
|
||||
addr_t AllocVirtualAddr(size_t size);
|
||||
|
||||
public:
|
||||
void OnChildProcessExit(Process* process);
|
||||
|
||||
public:
|
||||
static Process* Get(pid_t pid);
|
||||
static pid_t HackGetForegroundProcess();
|
||||
|
||||
private:
|
||||
static bool Put(Process* process);
|
||||
|
@ -139,6 +146,8 @@ namespace Sortix
|
|||
|
||||
};
|
||||
|
||||
void InitializeThreadRegisters(CPU::InterruptRegisters* regs,
|
||||
const sforkregs_t* requested);
|
||||
Process* CurrentProcess();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,367 +14,280 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
scheduler.cpp
|
||||
Handles context switching between tasks and deciding when to execute what.
|
||||
Decides the order to execute threads in and switching between them.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/mman.h>
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include <sortix/kernel/panic.h>
|
||||
#include "x86-family/gdt.h"
|
||||
#include "syscall.h"
|
||||
#include "interrupt.h"
|
||||
#include "time.h"
|
||||
#include "thread.h"
|
||||
#include "process.h"
|
||||
#include "time.h"
|
||||
#include "signal.h"
|
||||
#include "scheduler.h"
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include "syscall.h"
|
||||
#include "sound.h" // HACK FOR SIGINT
|
||||
#include "x86-family/gdt.h"
|
||||
|
||||
namespace Sortix
|
||||
using namespace Maxsi;
|
||||
|
||||
namespace Sortix {
|
||||
namespace Scheduler {
|
||||
|
||||
static Thread* currentthread;
|
||||
} // namespace Scheduler
|
||||
Thread* CurrentThread() { return Scheduler::currentthread; }
|
||||
Process* CurrentProcess() { return CurrentThread()->process; }
|
||||
namespace Scheduler {
|
||||
|
||||
uint8_t dummythreaddata[sizeof(Thread)] __attribute__ ((aligned (8)));
|
||||
Thread* dummythread;
|
||||
Thread* idlethread;
|
||||
Thread* firstrunnablethread;
|
||||
Thread* firstsleepingthread;
|
||||
Process* initprocess;
|
||||
|
||||
static inline void SetCurrentThread(Thread* newcurrentthread)
|
||||
{
|
||||
void SysExit(int status); // HACK FOR SIGINT
|
||||
currentthread = newcurrentthread;
|
||||
}
|
||||
|
||||
// Internal forward-declarations.
|
||||
namespace Scheduler
|
||||
void LogBeginSwitch(Thread* current, const CPU::InterruptRegisters* regs);
|
||||
void LogSwitch(Thread* current, Thread* next);
|
||||
void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs);
|
||||
|
||||
static Thread* PopNextThread()
|
||||
{
|
||||
if ( !firstrunnablethread ) { return idlethread; }
|
||||
Thread* result = firstrunnablethread;
|
||||
firstrunnablethread = firstrunnablethread->schedulerlistnext;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Thread* ValidatedPopNextThread()
|
||||
{
|
||||
Thread* nextthread = PopNextThread();
|
||||
if ( !nextthread ) { Panic("Had no thread to switch to."); }
|
||||
if ( nextthread->terminated )
|
||||
{
|
||||
Thread* PopNextThread();
|
||||
void WakeSleeping();
|
||||
void LogBeginContextSwitch(Thread* current, const CPU::InterruptRegisters* state);
|
||||
void LogContextSwitch(Thread* current, Thread* next);
|
||||
void LogEndContextSwitch(Thread* current, const CPU::InterruptRegisters* state);
|
||||
void SysSleep(size_t secs);
|
||||
void SysUSleep(size_t usecs);
|
||||
void HandleSigIntHack(CPU::InterruptRegisters* regs);
|
||||
PanicF("Running a terminated thread 0x%p", nextthread);
|
||||
}
|
||||
|
||||
namespace Scheduler
|
||||
addr_t newaddrspace = nextthread->process->addrspace;
|
||||
if ( !Page::IsAligned(newaddrspace) )
|
||||
{
|
||||
byte dummythreaddata[sizeof(Thread)];
|
||||
Thread* dummythread;
|
||||
Thread* currentthread;
|
||||
Thread* idlethread;
|
||||
Thread* firstrunnablethread;
|
||||
Thread* firstsleepingthread;
|
||||
Process* initprocess;
|
||||
bool hacksigintpending = false;
|
||||
|
||||
void Init()
|
||||
{
|
||||
// We use a dummy so that the first context switch won't crash when
|
||||
// currentthread is accessed. This lets us avoid checking whether
|
||||
// currentthread is NULL (which it only will be once) which gives
|
||||
// simpler code.
|
||||
dummythread = (Thread*) &dummythreaddata;
|
||||
Maxsi::Memory::Set(dummythread, 0, sizeof(*dummythread));
|
||||
currentthread = dummythread;
|
||||
firstrunnablethread = NULL;
|
||||
firstsleepingthread = NULL;
|
||||
idlethread = NULL;
|
||||
hacksigintpending = false;
|
||||
|
||||
Syscall::Register(SYSCALL_SLEEP, (void*) SysSleep);
|
||||
Syscall::Register(SYSCALL_USLEEP, (void*) SysUSleep);
|
||||
|
||||
addr_t stackhigher = Memory::GetKernelStack();
|
||||
size_t stacksize = Memory::GetKernelStackSize();
|
||||
addr_t stacklower = stackhigher - stacksize;
|
||||
int prot = PROT_KREAD | PROT_KWRITE;
|
||||
if ( !Memory::MapRange(stacklower, stacksize, prot) )
|
||||
{
|
||||
PanicF("could not create kernel stack (%zx to %zx)",
|
||||
stacklower, stackhigher);
|
||||
}
|
||||
|
||||
GDT::SetKernelStack(stacklower, stacksize, stackhigher);
|
||||
}
|
||||
|
||||
// The no operating thread is a thread stuck in an infinite loop that
|
||||
// executes absolutely nothing, which is only run when the system has
|
||||
// nothing to do.
|
||||
void SetIdleThread(Thread* thread)
|
||||
{
|
||||
ASSERT(idlethread == NULL);
|
||||
idlethread = thread;
|
||||
SetThreadState(thread, Thread::State::NONE);
|
||||
}
|
||||
|
||||
void SetDummyThreadOwner(Process* process)
|
||||
{
|
||||
dummythread->process = process;
|
||||
}
|
||||
|
||||
void SetInitProcess(Process* init)
|
||||
{
|
||||
initprocess = init;
|
||||
}
|
||||
|
||||
Process* GetInitProcess()
|
||||
{
|
||||
return initprocess;
|
||||
}
|
||||
|
||||
void MainLoop()
|
||||
{
|
||||
// Wait for the first hardware interrupt to trigger a context switch
|
||||
// into the first task! Then the init process should gracefully
|
||||
// start executing.
|
||||
while(true);
|
||||
}
|
||||
|
||||
void Switch(CPU::InterruptRegisters* regs)
|
||||
{
|
||||
LogBeginContextSwitch(currentthread, regs);
|
||||
|
||||
if ( hacksigintpending ) { HandleSigIntHack(regs); }
|
||||
|
||||
WakeSleeping();
|
||||
|
||||
Thread* nextthread = PopNextThread();
|
||||
if ( !nextthread ) { Panic("had no thread to switch to"); }
|
||||
if ( nextthread->terminated ) { PanicF("Running a terminated thread 0x%p", nextthread); }
|
||||
|
||||
LogContextSwitch(currentthread, nextthread);
|
||||
if ( nextthread == currentthread ) { return; }
|
||||
|
||||
currentthread->SaveRegisters(regs);
|
||||
nextthread->LoadRegisters(regs);
|
||||
|
||||
addr_t newaddrspace = nextthread->process->addrspace;
|
||||
if ( unlikely(newaddrspace != Page::AlignDown(newaddrspace)) )
|
||||
{
|
||||
PanicF("Thread 0x%p, process %i (0x%p) (backup: %i), had bad "
|
||||
"address space variable: 0x%zx: not page-aligned "
|
||||
"(backup: 0x%zx)\n", nextthread,
|
||||
nextthread->process->pid, nextthread->process,
|
||||
nextthread->pidbackup, newaddrspace,
|
||||
nextthread->addrspacebackup);
|
||||
}
|
||||
Memory::SwitchAddressSpace(newaddrspace);
|
||||
currentthread = nextthread;
|
||||
|
||||
nextthread->HandleSignal(regs);
|
||||
|
||||
LogEndContextSwitch(currentthread, regs);
|
||||
|
||||
if ( currentthread->scfunc ) { Syscall::Resume(regs); }
|
||||
}
|
||||
|
||||
void ProcessTerminated(CPU::InterruptRegisters* regs)
|
||||
{
|
||||
currentthread = dummythread;
|
||||
Switch(regs);
|
||||
}
|
||||
|
||||
const bool DEBUG_BEGINCTXSWITCH = false;
|
||||
const bool DEBUG_CTXSWITCH = false;
|
||||
const bool DEBUG_ENDCTXSWITCH = false;
|
||||
|
||||
void LogBeginContextSwitch(Thread* current, const CPU::InterruptRegisters* state)
|
||||
{
|
||||
if ( DEBUG_BEGINCTXSWITCH && current->process->pid != 0 )
|
||||
{
|
||||
Log::PrintF("Switching from 0x%p", current);
|
||||
state->LogRegisters();
|
||||
Log::Print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void LogContextSwitch(Thread* current, Thread* next)
|
||||
{
|
||||
if ( DEBUG_CTXSWITCH && current != next )
|
||||
{
|
||||
Log::PrintF("switching from %u:%u (0x%p) to %u:%u (0x%p) \n",
|
||||
current->process->pid, 0, current,
|
||||
next->process->pid, 0, next);
|
||||
}
|
||||
}
|
||||
|
||||
void LogEndContextSwitch(Thread* current, const CPU::InterruptRegisters* state)
|
||||
{
|
||||
if ( DEBUG_ENDCTXSWITCH && current->process->pid != 0 )
|
||||
{
|
||||
Log::PrintF("Switched to 0x%p", current);
|
||||
state->LogRegisters();
|
||||
Log::Print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
Thread* PopNextThread()
|
||||
{
|
||||
if ( !firstrunnablethread ) { return idlethread; }
|
||||
Thread* result = firstrunnablethread;
|
||||
firstrunnablethread = firstrunnablethread->schedulerlistnext;
|
||||
return result;
|
||||
}
|
||||
|
||||
void SetThreadState(Thread* thread, Thread::State state)
|
||||
{
|
||||
if ( thread->state == state ) { return; }
|
||||
|
||||
if ( thread->state == Thread::State::RUNNABLE )
|
||||
{
|
||||
if ( thread == firstrunnablethread ) { firstrunnablethread = thread->schedulerlistnext; }
|
||||
if ( thread == firstrunnablethread ) { firstrunnablethread = NULL; }
|
||||
thread->schedulerlistprev->schedulerlistnext = thread->schedulerlistnext;
|
||||
thread->schedulerlistnext->schedulerlistprev = thread->schedulerlistprev;
|
||||
thread->schedulerlistprev = NULL;
|
||||
thread->schedulerlistnext = NULL;
|
||||
}
|
||||
|
||||
// Insert the thread into the scheduler's carousel linked list.
|
||||
if ( state == Thread::State::RUNNABLE )
|
||||
{
|
||||
if ( firstrunnablethread == NULL ) { firstrunnablethread = thread; }
|
||||
thread->schedulerlistprev = firstrunnablethread->schedulerlistprev;
|
||||
thread->schedulerlistnext = firstrunnablethread;
|
||||
firstrunnablethread->schedulerlistprev = thread;
|
||||
thread->schedulerlistprev->schedulerlistnext = thread;
|
||||
}
|
||||
|
||||
thread->state = state;
|
||||
}
|
||||
|
||||
Thread::State GetThreadState(Thread* thread)
|
||||
{
|
||||
return thread->state;
|
||||
}
|
||||
|
||||
void PutThreadToSleep(Thread* thread, uintmax_t usecs)
|
||||
{
|
||||
SetThreadState(thread, Thread::State::BLOCKING);
|
||||
thread->sleepuntil = Time::MicrosecondsSinceBoot() + usecs;
|
||||
|
||||
// We use a simple linked linked list sorted after wake-up time to
|
||||
// keep track of the threads that are sleeping.
|
||||
|
||||
if ( firstsleepingthread == NULL )
|
||||
{
|
||||
thread->nextsleepingthread = NULL;
|
||||
firstsleepingthread = thread;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( thread->sleepuntil < firstsleepingthread->sleepuntil )
|
||||
{
|
||||
thread->nextsleepingthread = firstsleepingthread;
|
||||
firstsleepingthread = thread;
|
||||
return;
|
||||
}
|
||||
|
||||
for ( Thread* tmp = firstsleepingthread; tmp != NULL; tmp = tmp->nextsleepingthread )
|
||||
{
|
||||
if ( tmp->nextsleepingthread == NULL ||
|
||||
thread->sleepuntil < tmp->nextsleepingthread->sleepuntil )
|
||||
{
|
||||
thread->nextsleepingthread = tmp->nextsleepingthread;
|
||||
tmp->nextsleepingthread = thread;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EarlyWakeUp(Thread* thread)
|
||||
{
|
||||
uintmax_t now = Time::MicrosecondsSinceBoot();
|
||||
if ( thread->sleepuntil < now ) { return; }
|
||||
thread->sleepuntil = now;
|
||||
|
||||
SetThreadState(thread, Thread::State::RUNNABLE);
|
||||
|
||||
if ( firstsleepingthread == thread )
|
||||
{
|
||||
firstsleepingthread = thread->nextsleepingthread;
|
||||
thread->nextsleepingthread = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
for ( Thread* tmp = firstsleepingthread; tmp->nextsleepingthread != NULL; tmp = tmp->nextsleepingthread )
|
||||
{
|
||||
if ( tmp->nextsleepingthread == thread )
|
||||
{
|
||||
tmp->nextsleepingthread = thread->nextsleepingthread;
|
||||
thread->nextsleepingthread = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WakeSleeping()
|
||||
{
|
||||
uintmax_t now = Time::MicrosecondsSinceBoot();
|
||||
|
||||
while ( firstsleepingthread && firstsleepingthread->sleepuntil < now )
|
||||
{
|
||||
SetThreadState(firstsleepingthread, Thread::State::RUNNABLE);
|
||||
Thread* next = firstsleepingthread->nextsleepingthread;
|
||||
firstsleepingthread->nextsleepingthread = NULL;
|
||||
firstsleepingthread = next;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleSigIntHack(CPU::InterruptRegisters* regs)
|
||||
{
|
||||
if ( currentthread == idlethread ) { return; }
|
||||
|
||||
hacksigintpending = false;
|
||||
|
||||
// HACK: Don't crash init or sh.
|
||||
Process* process = CurrentProcess();
|
||||
if ( process->pid < 3 ) { return; }
|
||||
|
||||
Sound::Mute();
|
||||
Log::PrintF("^C\n");
|
||||
|
||||
process->Exit(130);
|
||||
currentthread = dummythread;
|
||||
}
|
||||
|
||||
void SigIntHack()
|
||||
{
|
||||
hacksigintpending = true;
|
||||
}
|
||||
|
||||
void SysSleep(size_t secs)
|
||||
{
|
||||
Thread* thread = currentthread;
|
||||
uintmax_t timetosleep = ((uintmax_t) secs) * 1000ULL * 1000ULL;
|
||||
if ( timetosleep == 0 )
|
||||
{
|
||||
Switch(Syscall::InterruptRegs());
|
||||
Syscall::AsIs();
|
||||
return;
|
||||
}
|
||||
PutThreadToSleep(thread, timetosleep);
|
||||
Syscall::Incomplete();
|
||||
}
|
||||
|
||||
void SysUSleep(size_t usecs)
|
||||
{
|
||||
Thread* thread = currentthread;
|
||||
uintmax_t timetosleep = usecs;
|
||||
if ( timetosleep == 0 )
|
||||
{
|
||||
Switch(Syscall::InterruptRegs());
|
||||
Syscall::AsIs();
|
||||
return;
|
||||
}
|
||||
PutThreadToSleep(thread, timetosleep);
|
||||
Syscall::Incomplete();
|
||||
}
|
||||
PanicF("Thread 0x%p, process %i (0x%p) (backup: %i), had bad "
|
||||
"address space variable: 0x%zx: not page-aligned "
|
||||
"(backup: 0x%zx)\n", nextthread,
|
||||
nextthread->process->pid, nextthread->process,
|
||||
-1/*nextthread->pidbackup*/, newaddrspace,
|
||||
(addr_t)-1 /*nextthread->addrspacebackup*/);
|
||||
}
|
||||
return nextthread;
|
||||
}
|
||||
|
||||
Thread* CurrentThread()
|
||||
{
|
||||
return Scheduler::currentthread;
|
||||
}
|
||||
static void DoActualSwitch(CPU::InterruptRegisters* regs)
|
||||
{
|
||||
Thread* current = CurrentThread();
|
||||
LogBeginSwitch(current, regs);
|
||||
|
||||
Process* CurrentProcess()
|
||||
Thread* next = ValidatedPopNextThread();
|
||||
LogSwitch(current, next);
|
||||
|
||||
if ( current == next ) { return; }
|
||||
|
||||
current->SaveRegisters(regs);
|
||||
next->LoadRegisters(regs);
|
||||
|
||||
addr_t newaddrspace = next->addrspace;
|
||||
Memory::SwitchAddressSpace(newaddrspace);
|
||||
SetCurrentThread(next);
|
||||
|
||||
addr_t stacklower = next->kernelstackpos;
|
||||
size_t stacksize = next->kernelstacksize;
|
||||
addr_t stackhigher = stacklower + stacksize;
|
||||
ASSERT(stacklower && stacksize && stackhigher);
|
||||
GDT::SetKernelStack(stacklower, stacksize, stackhigher);
|
||||
|
||||
LogEndSwitch(next, regs);
|
||||
}
|
||||
|
||||
void Switch(CPU::InterruptRegisters* regs)
|
||||
{
|
||||
DoActualSwitch(regs);
|
||||
if ( regs->signal_pending && regs->InUserspace() )
|
||||
Signal::Dispatch(regs);
|
||||
}
|
||||
|
||||
const bool DEBUG_BEGINCTXSWITCH = false;
|
||||
const bool DEBUG_CTXSWITCH = false;
|
||||
const bool DEBUG_ENDCTXSWITCH = false;
|
||||
|
||||
void LogBeginSwitch(Thread* current, const CPU::InterruptRegisters* regs)
|
||||
{
|
||||
bool alwaysdebug = false;
|
||||
bool isidlethread = current == idlethread;
|
||||
bool dodebug = DEBUG_BEGINCTXSWITCH && !isidlethread;
|
||||
if ( alwaysdebug || dodebug )
|
||||
{
|
||||
return Scheduler::currentthread->process;
|
||||
Log::PrintF("Switching from 0x%p", current);
|
||||
regs->LogRegisters();
|
||||
Log::Print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void LogSwitch(Thread* current, Thread* next)
|
||||
{
|
||||
bool alwaysdebug = false;
|
||||
bool different = current == idlethread;
|
||||
bool dodebug = DEBUG_CTXSWITCH && different;
|
||||
if ( alwaysdebug || dodebug )
|
||||
{
|
||||
Log::PrintF("switching from %u:%u (0x%p) to %u:%u (0x%p) \n",
|
||||
current->process->pid, 0, current,
|
||||
next->process->pid, 0, next);
|
||||
}
|
||||
}
|
||||
|
||||
void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs)
|
||||
{
|
||||
bool alwaysdebug = false;
|
||||
bool isidlethread = current == idlethread;
|
||||
bool dodebug = DEBUG_BEGINCTXSWITCH && !isidlethread;
|
||||
if ( alwaysdebug || dodebug )
|
||||
{
|
||||
Log::PrintF("Switched to 0x%p", current);
|
||||
regs->LogRegisters();
|
||||
Log::Print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/)
|
||||
{
|
||||
Switch(regs);
|
||||
}
|
||||
|
||||
static void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/)
|
||||
{
|
||||
SetThreadState(currentthread, Thread::State::DEAD);
|
||||
InterruptYieldCPU(regs, NULL);
|
||||
}
|
||||
|
||||
// The idle thread serves no purpose except being an infinite loop that does
|
||||
// nothing, which is only run when the system has nothing to do.
|
||||
void SetIdleThread(Thread* thread)
|
||||
{
|
||||
ASSERT(!idlethread);
|
||||
idlethread = thread;
|
||||
SetThreadState(thread, Thread::State::NONE);
|
||||
SetCurrentThread(thread);
|
||||
}
|
||||
|
||||
void SetDummyThreadOwner(Process* process)
|
||||
{
|
||||
dummythread->process = process;
|
||||
}
|
||||
|
||||
void SetInitProcess(Process* init)
|
||||
{
|
||||
initprocess = init;
|
||||
}
|
||||
|
||||
Process* GetInitProcess()
|
||||
{
|
||||
return initprocess;
|
||||
}
|
||||
|
||||
void SetThreadState(Thread* thread, Thread::State state)
|
||||
{
|
||||
bool wasenabled = Interrupt::SetEnabled(false);
|
||||
|
||||
// Remove the thread from the list of runnable threads.
|
||||
if ( thread->state == Thread::State::RUNNABLE &&
|
||||
state != Thread::State::RUNNABLE )
|
||||
{
|
||||
if ( thread == firstrunnablethread ) { firstrunnablethread = thread->schedulerlistnext; }
|
||||
if ( thread == firstrunnablethread ) { firstrunnablethread = NULL; }
|
||||
ASSERT(thread->schedulerlistprev);
|
||||
ASSERT(thread->schedulerlistnext);
|
||||
thread->schedulerlistprev->schedulerlistnext = thread->schedulerlistnext;
|
||||
thread->schedulerlistnext->schedulerlistprev = thread->schedulerlistprev;
|
||||
thread->schedulerlistprev = NULL;
|
||||
thread->schedulerlistnext = NULL;
|
||||
}
|
||||
|
||||
// Insert the thread into the scheduler's carousel linked list.
|
||||
if ( thread->state != Thread::State::RUNNABLE &&
|
||||
state == Thread::State::RUNNABLE )
|
||||
{
|
||||
if ( firstrunnablethread == NULL ) { firstrunnablethread = thread; }
|
||||
thread->schedulerlistprev = firstrunnablethread->schedulerlistprev;
|
||||
thread->schedulerlistnext = firstrunnablethread;
|
||||
firstrunnablethread->schedulerlistprev = thread;
|
||||
thread->schedulerlistprev->schedulerlistnext = thread;
|
||||
}
|
||||
|
||||
thread->state = state;
|
||||
|
||||
ASSERT(thread->state != Thread::State::RUNNABLE || thread->schedulerlistprev);
|
||||
ASSERT(thread->state != Thread::State::RUNNABLE || thread->schedulerlistnext);
|
||||
|
||||
Interrupt::SetEnabled(wasenabled);
|
||||
}
|
||||
|
||||
Thread::State GetThreadState(Thread* thread)
|
||||
{
|
||||
return thread->state;
|
||||
}
|
||||
|
||||
void SysSleep(size_t secs)
|
||||
{
|
||||
uintmax_t timetosleep = ((uintmax_t) secs) * 1000ULL * 1000ULL;
|
||||
uint32_t wakeat = Time::MicrosecondsSinceBoot() + timetosleep;
|
||||
do { Yield(); }
|
||||
while ( Time::MicrosecondsSinceBoot() < wakeat );
|
||||
}
|
||||
|
||||
void SysUSleep(size_t usecs)
|
||||
{
|
||||
uintmax_t timetosleep = (uintmax_t) usecs;
|
||||
uint32_t wakeat = Time::MicrosecondsSinceBoot() + timetosleep;
|
||||
do { Yield(); }
|
||||
while ( Time::MicrosecondsSinceBoot() < wakeat );
|
||||
}
|
||||
|
||||
extern "C" void yield_cpu_handler();
|
||||
extern "C" void thread_exit_handler();
|
||||
|
||||
void Init()
|
||||
{
|
||||
// We use a dummy so that the first context switch won't crash when the
|
||||
// current thread is accessed. This lets us avoid checking whether it is
|
||||
// NULL (which it only will be once), which gives simpler code.
|
||||
dummythread = (Thread*) &dummythreaddata;
|
||||
Maxsi::Memory::Set(dummythread, 0, sizeof(*dummythread));
|
||||
dummythread->schedulerlistprev = dummythread;
|
||||
dummythread->schedulerlistnext = dummythread;
|
||||
currentthread = dummythread;
|
||||
firstrunnablethread = NULL;
|
||||
firstsleepingthread = NULL;
|
||||
idlethread = NULL;
|
||||
|
||||
// Register our raw handler with user-space access. It calls our real
|
||||
// handler after common interrupt preparation stuff has occured.
|
||||
Interrupt::RegisterRawHandler(129, yield_cpu_handler, true);
|
||||
Interrupt::RegisterHandler(129, InterruptYieldCPU, NULL);
|
||||
Interrupt::RegisterRawHandler(132, thread_exit_handler, true);
|
||||
Interrupt::RegisterHandler(132, ThreadExitCPU, NULL);
|
||||
|
||||
Syscall::Register(SYSCALL_SLEEP, (void*) SysSleep);
|
||||
Syscall::Register(SYSCALL_USLEEP, (void*) SysUSleep);
|
||||
}
|
||||
|
||||
} // namespace Scheduler
|
||||
} // namespace Sortix
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,40 +14,34 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
scheduler.h
|
||||
Handles context switching between tasks and deciding when to execute what.
|
||||
Decides the order to execute threads in and switching between them.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_SCHEDULER_H
|
||||
#define SORTIX_SCHEDULER_H
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
namespace Scheduler
|
||||
{
|
||||
void Init();
|
||||
void MainLoop() SORTIX_NORETURN;
|
||||
void Switch(CPU::InterruptRegisters* regs);
|
||||
void ProcessTerminated(CPU::InterruptRegisters* regs);
|
||||
void SetIdleThread(Thread* thread);
|
||||
void SetDummyThreadOwner(Process* process);
|
||||
void SetInitProcess(Process* init);
|
||||
Process* GetInitProcess();
|
||||
namespace Sortix {
|
||||
namespace Scheduler {
|
||||
|
||||
void SetThreadState(Thread* thread, Thread::State state);
|
||||
Thread::State GetThreadState(Thread* thread);
|
||||
void PutThreadToSleep(Thread* thread, uintmax_t usecs);
|
||||
void EarlyWakeUp(Thread* thread);
|
||||
void Init();
|
||||
void Switch(CPU::InterruptRegisters* regs);
|
||||
inline static void Yield() { asm volatile ("int $129"); }
|
||||
inline static void ExitThread() { asm volatile ("int $132"); }
|
||||
void SetThreadState(Thread* thread, Thread::State state);
|
||||
Thread::State GetThreadState(Thread* thread);
|
||||
void SetIdleThread(Thread* thread);
|
||||
void SetDummyThreadOwner(Process* process);
|
||||
void SetInitProcess(Process* init);
|
||||
Process* GetInitProcess();
|
||||
|
||||
void SigIntHack();
|
||||
}
|
||||
}
|
||||
} // namespace Scheduler
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
serialterminal.h
|
||||
A terminal on a serial line.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_SERIALTERMINAL_H
|
||||
#define SORTIX_SERIALTERMINAL_H
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -23,222 +23,114 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include <sortix/kernel/panic.h>
|
||||
#include <sortix/signal.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "interrupt.h"
|
||||
#include "thread.h"
|
||||
#include "signal.h"
|
||||
|
||||
using namespace Maxsi;
|
||||
|
||||
namespace Sortix
|
||||
namespace Sortix {
|
||||
|
||||
// A per-cpu value whether a signal is pending in the running task.
|
||||
extern "C" { volatile unsigned long asm_signal_is_pending = 0; }
|
||||
|
||||
namespace Signal {
|
||||
|
||||
const int PRIORITIES[SIG__NUM_DECLARED] =
|
||||
{
|
||||
const int PRIORITY_NORMAL = 0;
|
||||
const int PRIORITY_HIGH = 1;
|
||||
const int PRIORITY_STOP = 2;
|
||||
const int PRIORITY_CORE = 3;
|
||||
const int PRIORITY_KILL = 4;
|
||||
SIG_PRIO_NORMAL, // unused
|
||||
SIG_PRIO_NORMAL, // SIGHUP
|
||||
SIG_PRIO_NORMAL, // SIGINT
|
||||
SIG_PRIO_NORMAL, // SIGQUIT
|
||||
SIG_PRIO_CORE, // SIGILL
|
||||
SIG_PRIO_CORE, // SIGTRAP
|
||||
SIG_PRIO_CORE, // SIGABRT
|
||||
SIG_PRIO_CORE, // SIGEMT
|
||||
SIG_PRIO_CORE, // SIGFPE
|
||||
SIG_PRIO_KILL, // SIGKILL
|
||||
SIG_PRIO_CORE, // SIGBUS
|
||||
SIG_PRIO_CORE, // SIGSEGV
|
||||
SIG_PRIO_CORE, // SIGSYS
|
||||
SIG_PRIO_NORMAL, // SIGPIPE
|
||||
SIG_PRIO_NORMAL, // SIGALRM
|
||||
SIG_PRIO_NORMAL, // SIGTERM
|
||||
SIG_PRIO_NORMAL, // SIGUSR1
|
||||
SIG_PRIO_NORMAL, // SIGUSR2
|
||||
SIG_PRIO_NORMAL, // SIGCHLD
|
||||
SIG_PRIO_HIGH, // SIGPWR
|
||||
SIG_PRIO_NORMAL, // SIGWINCH
|
||||
SIG_PRIO_NORMAL, // SIGURG
|
||||
SIG_PRIO_NORMAL, // obsolete
|
||||
SIG_PRIO_STOP, // SIGSTOP
|
||||
SIG_PRIO_STOP, // SIGTSTP
|
||||
SIG_PRIO_STOP, // SIGCONT
|
||||
SIG_PRIO_STOP, // SIGTTIN
|
||||
SIG_PRIO_STOP, // SIGTTOU
|
||||
SIG_PRIO_NORMAL, // SIGVTALRM
|
||||
SIG_PRIO_NORMAL, // obsolete
|
||||
SIG_PRIO_CORE, // SIGXCPU
|
||||
SIG_PRIO_CORE, // SIGXFSZ
|
||||
SIG_PRIO_NORMAL, // SIGCORE
|
||||
SIG_PRIO_NORMAL, // SIGLWP
|
||||
SIG_PRIO_NORMAL, // SIGAIO
|
||||
};
|
||||
|
||||
const int PRIORITIES[Maxsi::Signal::NUMSIGNALS] =
|
||||
{
|
||||
PRIORITY_NORMAL, // unused
|
||||
PRIORITY_NORMAL, // SIGHUP
|
||||
PRIORITY_NORMAL, // SIGINT
|
||||
PRIORITY_NORMAL, // SIGQUIT
|
||||
PRIORITY_CORE, // SIGILL
|
||||
PRIORITY_CORE, // SIGTRAP
|
||||
PRIORITY_CORE, // SIGABRT
|
||||
PRIORITY_CORE, // SIGEMT
|
||||
PRIORITY_CORE, // SIGFPE
|
||||
PRIORITY_KILL, // SIGKILL
|
||||
PRIORITY_CORE, // SIGBUS
|
||||
PRIORITY_CORE, // SIGSEGV
|
||||
PRIORITY_CORE, // SIGSYS
|
||||
PRIORITY_NORMAL, // SIGPIPE
|
||||
PRIORITY_NORMAL, // SIGALRM
|
||||
PRIORITY_NORMAL, // SIGTERM
|
||||
PRIORITY_NORMAL, // SIGUSR1
|
||||
PRIORITY_NORMAL, // SIGUSR2
|
||||
PRIORITY_NORMAL, // SIGCHLD
|
||||
PRIORITY_HIGH, // SIGPWR
|
||||
PRIORITY_NORMAL, // SIGWINCH
|
||||
PRIORITY_NORMAL, // SIGURG
|
||||
PRIORITY_NORMAL, // obsolete
|
||||
PRIORITY_STOP, // SIGSTOP
|
||||
PRIORITY_STOP, // SIGTSTP
|
||||
PRIORITY_STOP, // SIGCONT
|
||||
PRIORITY_STOP, // SIGTTIN
|
||||
PRIORITY_STOP, // SIGTTOU
|
||||
PRIORITY_NORMAL, // SIGVTALRM
|
||||
PRIORITY_NORMAL, // obsolete
|
||||
PRIORITY_CORE, // SIGXCPU
|
||||
PRIORITY_CORE, // SIGXFSZ
|
||||
PRIORITY_NORMAL, // SIGCORE
|
||||
PRIORITY_NORMAL, // SIGLWP
|
||||
PRIORITY_NORMAL, // SIGAIO
|
||||
};
|
||||
|
||||
// Returns true of the exact ordering of this signal version others aren't
|
||||
// important - if it returns false, then this signal will be put in the
|
||||
// queue according to priority, instead of being merged into another signal
|
||||
// with the same signum.
|
||||
bool Unifiable(int /*signum*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns whether a specific signal is more important to deliver than
|
||||
// another. This is used to schedule signals.
|
||||
int CompareSignalPriority(int siga, int sigb)
|
||||
{
|
||||
int prioa = PRIORITY_NORMAL;
|
||||
int priob = PRIORITY_NORMAL;
|
||||
|
||||
if ( siga < Maxsi::Signal::NUMSIGNALS ) { prioa = PRIORITIES[siga]; }
|
||||
if ( sigb < Maxsi::Signal::NUMSIGNALS ) { priob = PRIORITIES[sigb]; }
|
||||
|
||||
if ( prioa < priob ) { return -1; } else
|
||||
if ( prioa > priob ) { return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
SignalQueue::SignalQueue()
|
||||
{
|
||||
queue = NULL;
|
||||
}
|
||||
|
||||
SignalQueue::~SignalQueue()
|
||||
{
|
||||
while ( queue )
|
||||
{
|
||||
Signal* todelete = queue;
|
||||
queue = queue->nextsignal;
|
||||
delete todelete;
|
||||
}
|
||||
}
|
||||
|
||||
// Queues the signal and schedules it for processing.
|
||||
bool SignalQueue::Push(int signum)
|
||||
{
|
||||
ASSERT(0 <= signum && signum < 128);
|
||||
|
||||
if ( Unifiable(signum) )
|
||||
{
|
||||
for ( Signal* signal = queue; signal != NULL; signal = signal->nextsignal )
|
||||
{
|
||||
if ( signal->signum != signum ) { continue; }
|
||||
|
||||
signal->numpending++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Signal* signal = new Signal;
|
||||
if ( !signal ) { return false; }
|
||||
|
||||
signal->signum = signum;
|
||||
signal->numpending = 1;
|
||||
signal->nextsignal = NULL;
|
||||
signal->returncode = 128 + signum;
|
||||
|
||||
Insert(signal);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Insert the signal in O(N), which is pretty fast for small Ns.
|
||||
void SignalQueue::Insert(Signal* signal)
|
||||
{
|
||||
if ( !queue )
|
||||
{
|
||||
queue = signal;
|
||||
last = signal;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the signal is to be inserted last, then just do it quickly.
|
||||
if ( last != NULL && 0 <= CompareSignalPriority(last->signum, signal->signum) )
|
||||
{
|
||||
last->nextsignal = signal;
|
||||
signal->nextsignal = NULL;
|
||||
last = signal;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the signal should be inserted first.
|
||||
if ( queue != NULL && CompareSignalPriority(queue->signum, signal->signum) < 0 )
|
||||
{
|
||||
signal->nextsignal = queue;
|
||||
queue = signal;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find where the signal should be inserted.
|
||||
for ( Signal* tmp = queue; tmp != NULL; tmp = tmp->nextsignal )
|
||||
{
|
||||
Signal* next = tmp->nextsignal;
|
||||
|
||||
if ( next != NULL && CompareSignalPriority(next->signum, signal->signum) < 0 )
|
||||
{
|
||||
tmp->nextsignal = signal;
|
||||
signal->nextsignal = next;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( next == NULL )
|
||||
{
|
||||
tmp->nextsignal = signal;
|
||||
signal->nextsignal = NULL;
|
||||
last = signal;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Given the stack of currently processing signals, return a new signal if
|
||||
// it is more important to handle at this point.
|
||||
Signal* SignalQueue::Pop(Signal* current)
|
||||
{
|
||||
if ( queue == NULL ) { return NULL; }
|
||||
|
||||
bool returnqueue = false;
|
||||
|
||||
// If we are currently handling no signal, then just return the first.
|
||||
if ( current == NULL ) { returnqueue = true; }
|
||||
|
||||
// If we are handling a signal, only override it with another if it is
|
||||
// more important.
|
||||
else if ( CompareSignalPriority(current->signum, queue->signum) < 0 )
|
||||
{
|
||||
returnqueue = true;
|
||||
}
|
||||
|
||||
if ( returnqueue )
|
||||
{
|
||||
Signal* result = queue;
|
||||
queue = queue->nextsignal;
|
||||
result->nextsignal = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Signal* Signal::Fork()
|
||||
{
|
||||
Signal* clone = new Signal();
|
||||
if ( !clone ) { return NULL; }
|
||||
|
||||
Memory::Copy(clone, this, sizeof(Signal));
|
||||
|
||||
Signal* nextsignalclone = NULL;
|
||||
if ( nextsignal )
|
||||
{
|
||||
nextsignalclone = nextsignal->Fork();
|
||||
if ( !nextsignalclone ) { delete clone; return NULL; }
|
||||
}
|
||||
|
||||
clone->nextsignal = nextsignalclone;
|
||||
|
||||
return clone;
|
||||
}
|
||||
int Priority(int signum)
|
||||
{
|
||||
ASSERT(0 <= signum && signum < SIG_MAX_NUM);
|
||||
if ( !signum )
|
||||
return -1;
|
||||
if ( SIG__NUM_DECLARED <= signum )
|
||||
return SIG_PRIO_NORMAL;
|
||||
return PRIORITIES[signum];
|
||||
}
|
||||
|
||||
Queue::Queue()
|
||||
{
|
||||
for ( int i = 1; i < SIG_MAX_NUM; i++ )
|
||||
pending[i] = false;
|
||||
}
|
||||
|
||||
void Queue::Push(int signum)
|
||||
{
|
||||
ASSERT(0 < signum && signum < SIG_MAX_NUM);
|
||||
pending[signum] = true;
|
||||
}
|
||||
|
||||
int Queue::Pop(int cursig)
|
||||
{
|
||||
int best = 0;
|
||||
int bestprio = Priority(cursig);
|
||||
for ( int i = 1; i < SIG_MAX_NUM; i++ )
|
||||
if ( pending[i] && bestprio < Priority(i) )
|
||||
{
|
||||
best = i;
|
||||
bestprio = Priority(i);
|
||||
}
|
||||
pending[best] = false;
|
||||
return best;
|
||||
}
|
||||
|
||||
void Dispatch(CPU::InterruptRegisters* regs, void* /*user*/)
|
||||
{
|
||||
return CurrentThread()->HandleSignal(regs);
|
||||
}
|
||||
|
||||
void Return(CPU::InterruptRegisters* regs, void* /*user*/)
|
||||
{
|
||||
return CurrentThread()->HandleSigreturn(regs);
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
Interrupt::RegisterRawHandler(130, isr130, true);
|
||||
Interrupt::RegisterHandler(130, Dispatch, NULL);
|
||||
Interrupt::RegisterRawHandler(131, isr131, true);
|
||||
Interrupt::RegisterHandler(131, Return, NULL);
|
||||
}
|
||||
|
||||
} // namespace Signal
|
||||
} // namespace Sortix
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -25,76 +25,39 @@
|
|||
#ifndef SORTIX_SIGNAL_H
|
||||
#define SORTIX_SIGNAL_H
|
||||
|
||||
#include <libmaxsi/signalnum.h>
|
||||
#include "cpu.h"
|
||||
|
||||
namespace Sortix
|
||||
namespace Sortix {
|
||||
|
||||
extern "C" volatile unsigned long asm_signal_is_pending;
|
||||
|
||||
namespace Signal {
|
||||
|
||||
class Queue
|
||||
{
|
||||
#define SIGHUP 1 /* Hangup */
|
||||
#define SIGINT 2 /* Interrupt */
|
||||
#define SIGQUIT 3 /* Quit */
|
||||
#define SIGILL 4 /* Illegal Instruction */
|
||||
#define SIGTRAP 5 /* Trace/Breakpoint Trap */
|
||||
#define SIGABRT 6 /* Abort */
|
||||
#define SIGEMT 7 /* Emulation Trap */
|
||||
#define SIGFPE 8 /* Arithmetic Exception */
|
||||
#define SIGKILL 9 /* Killed */
|
||||
#define SIGBUS 10 /* Bus Error */
|
||||
#define SIGSEGV 11 /* Segmentation Fault */
|
||||
#define SIGSYS 12 /* Bad System Call */
|
||||
#define SIGPIPE 13 /* Broken Pipe */
|
||||
#define SIGALRM 14 /* Alarm Clock */
|
||||
#define SIGTERM 15 /* Terminated */
|
||||
#define SIGUSR1 16 /* User Signal 1 */
|
||||
#define SIGUSR2 17 /* User Signal 2 */
|
||||
#define SIGCHLD 18 /* Child Status */
|
||||
#define SIGPWR 19 /* Power Fail/Restart */
|
||||
#define SIGWINCH 20 /* Window Size Change */
|
||||
#define SIGURG 21 /* Urgent Socket Condition */
|
||||
#define SIGSTOP 23 /* Stopped (signal) */
|
||||
#define SIGTSTP 24 /* Stopped (user) */
|
||||
#define SIGCONT 25 /* Continued */
|
||||
#define SIGTTIN 26 /* Stopped (tty input) */
|
||||
#define SIGTTOU 27 /* Stopped (tty output) */
|
||||
#define SIGVTALRM 28 /* Virtual Timer Expired */
|
||||
#define SIGXCPU 30 /* CPU time limit exceeded */
|
||||
#define SIGXFSZ 31 /* File size limit exceeded */
|
||||
#define SIGWAITING 32 /* All LWPs blocked */
|
||||
#define SIGLWP 33 /* Virtual Interprocessor Interrupt for Threads Library */
|
||||
#define SIGAIO 34 /* Asynchronous I/O */
|
||||
public:
|
||||
Queue();
|
||||
|
||||
struct Signal
|
||||
{
|
||||
int signum;
|
||||
int returncode;
|
||||
unsigned numpending;
|
||||
CPU::InterruptRegisters regs;
|
||||
Signal* nextsignal;
|
||||
// TODO: This is the wrong data structure for the problem!
|
||||
private:
|
||||
bool pending[SIG_MAX_NUM];
|
||||
|
||||
public:
|
||||
Signal* Fork();
|
||||
// TODO: This is not SMP ready:
|
||||
// To avoid race conditions, these should be called with interrupts off.
|
||||
public:
|
||||
void Push(int signum);
|
||||
int Pop(int cursig);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
class SignalQueue
|
||||
{
|
||||
public:
|
||||
SignalQueue();
|
||||
~SignalQueue();
|
||||
void Init();
|
||||
int Priority(int signum);
|
||||
void Dispatch(CPU::InterruptRegisters* regs, void* user = NULL);
|
||||
void Return(CPU::InterruptRegisters* regs, void* user = NULL);
|
||||
inline bool IsPending() { return asm_signal_is_pending != 0; }
|
||||
|
||||
private:
|
||||
Signal* queue;
|
||||
Signal* last;
|
||||
|
||||
public:
|
||||
bool Push(int signum);
|
||||
Signal* Pop(Signal* current);
|
||||
|
||||
private:
|
||||
void Insert(Signal* signal);
|
||||
|
||||
};
|
||||
}
|
||||
} // namespace Signal
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
syscall.h
|
||||
Handles system calls from userspace safely.
|
||||
Handles system calls from userspace.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <libmaxsi/error.h>
|
||||
|
@ -39,15 +39,14 @@ namespace Sortix
|
|||
{
|
||||
extern "C"
|
||||
{
|
||||
CPU::SyscallRegisters* syscall_state_ptr;
|
||||
unsigned system_was_incomplete;
|
||||
size_t SYSCALL_MAX;
|
||||
volatile void* syscall_list[SYSCALL_MAX_NUM];
|
||||
}
|
||||
|
||||
int BadSyscall()
|
||||
{
|
||||
// TODO: Send signal, set errno, or crash/abort process?
|
||||
Log::PrintF("I am the bad system call!\n");
|
||||
// TODO: Send signal, set errnx o, or crash/abort process?
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -70,87 +69,6 @@ namespace Sortix
|
|||
|
||||
syscall_list[index] = funcptr;
|
||||
}
|
||||
|
||||
void Incomplete()
|
||||
{
|
||||
Thread* thread = CurrentThread();
|
||||
|
||||
system_was_incomplete = 1;
|
||||
|
||||
CPU::InterruptRegisters* regs = InterruptRegs();
|
||||
thread->SaveRegisters(regs);
|
||||
Scheduler::SetThreadState(thread, Thread::State::BLOCKING);
|
||||
Scheduler::Switch(regs);
|
||||
}
|
||||
|
||||
void Yield()
|
||||
{
|
||||
Panic("Syscall::Yield() is not implemented because it caused "
|
||||
"instability and other issues.");
|
||||
}
|
||||
|
||||
void AsIs()
|
||||
{
|
||||
system_was_incomplete = 1;
|
||||
}
|
||||
|
||||
void ScheduleResumption(Thread* thread)
|
||||
{
|
||||
Scheduler::SetThreadState(thread, Thread::State::RUNNABLE);
|
||||
}
|
||||
|
||||
extern "C" void update_userspace_errno()
|
||||
{
|
||||
int error = Error::Last();
|
||||
if ( !error ) { return; }
|
||||
Process* process = CurrentProcess();
|
||||
if ( !process->errnop ) { return; }
|
||||
// TODO: Validate that process->errno is in userspace memory!
|
||||
*process->errnop = error;
|
||||
}
|
||||
|
||||
extern "C" size_t resume_syscall(void* scfunc, size_t scsize, size_t* scstate);
|
||||
|
||||
void Resume(CPU::InterruptRegisters* regs)
|
||||
{
|
||||
Thread* thread = CurrentThread();
|
||||
|
||||
syscall_state_ptr = (CPU::SyscallRegisters*) regs;
|
||||
|
||||
ASSERT(thread->scfunc);
|
||||
|
||||
size_t* scstate = thread->scstate;
|
||||
size_t scsize = thread->scsize;
|
||||
void* scfunc = thread->scfunc;
|
||||
|
||||
system_was_incomplete = 0;
|
||||
Error::Set(0);
|
||||
|
||||
size_t result = resume_syscall(scfunc, scsize, scstate);
|
||||
|
||||
bool incomplete = (system_was_incomplete);
|
||||
|
||||
system_was_incomplete = 1;
|
||||
|
||||
if ( !incomplete )
|
||||
{
|
||||
syscall_state_ptr->result = result;
|
||||
update_userspace_errno();
|
||||
return;
|
||||
}
|
||||
|
||||
Incomplete();
|
||||
}
|
||||
|
||||
CPU::InterruptRegisters* InterruptRegs()
|
||||
{
|
||||
return (CPU::InterruptRegisters*) syscall_state_ptr;
|
||||
}
|
||||
|
||||
CPU::SyscallRegisters* SyscallRegs()
|
||||
{
|
||||
return syscall_state_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
syscall.h
|
||||
Handles system calls from userspace safely.
|
||||
Handles system calls from userspace.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_SYSCALL_H
|
||||
#define SORTIX_SYSCALL_H
|
||||
|
@ -36,33 +36,6 @@ namespace Sortix
|
|||
{
|
||||
void Init();
|
||||
void Register(size_t index, void* funcptr);
|
||||
|
||||
// Aborts the current system call such that the current thread is marked
|
||||
// as blocking, and control is transferred to the next runnable thread.
|
||||
// This allows the thread to wait for an condition that makes the thread
|
||||
// runnable again and stores the return value in the proper register.
|
||||
// Once called, you may safely return from the system call in good faith
|
||||
// that its return value shall be discarded.
|
||||
void Incomplete();
|
||||
|
||||
// Call this prior to Incomplete() to signal that the scheduler should
|
||||
// go run something else for a moment. The current thread will not be
|
||||
// marked as blocking.
|
||||
void Yield();
|
||||
|
||||
// For when you want the syscall exit code not to modify registers.
|
||||
void AsIs();
|
||||
|
||||
// Retries a system call by making the thread runnable and then calling
|
||||
// the system call code whenever the thread is scheduled to run.
|
||||
void ScheduleResumption(Thread* thread);
|
||||
|
||||
// Retries a system call based on the Thread::sc* values of the current
|
||||
// thread and if it succeeds, sets the proper registers.
|
||||
void Resume(CPU::InterruptRegisters* regs);
|
||||
|
||||
CPU::InterruptRegisters* InterruptRegs();
|
||||
CPU::SyscallRegisters* SyscallRegs();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#ifndef SORTIX_TEXTTERMINAL_H
|
||||
#define SORTIX_TEXTTERMINAL_H
|
||||
|
||||
#include <sortix/kernel/kthread.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class TextBufferHandle;
|
||||
|
@ -51,6 +53,7 @@ private:
|
|||
|
||||
private:
|
||||
mutable TextBufferHandle* textbufhandle;
|
||||
mutable kthread_mutex_t termlock;
|
||||
uint8_t vgacolor;
|
||||
unsigned column;
|
||||
unsigned line;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,23 +14,25 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
thread.cpp
|
||||
Describes a thread belonging to a process.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include <sortix/mman.h>
|
||||
#include <sortix/signal.h>
|
||||
#include <libmaxsi/error.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "event.h"
|
||||
#include "process.h"
|
||||
#include "thread.h"
|
||||
#include "scheduler.h"
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include "interrupt.h"
|
||||
#include "time.h"
|
||||
#include "syscall.h"
|
||||
|
||||
|
@ -44,135 +46,64 @@ namespace Sortix
|
|||
process = NULL;
|
||||
prevsibling = NULL;
|
||||
nextsibling = NULL;
|
||||
event = NULL;
|
||||
eventnextwaiting = NULL;
|
||||
sleepuntil = 0;
|
||||
nextsleepingthread = NULL;
|
||||
schedulerlistprev = NULL;
|
||||
schedulerlistnext = NULL;
|
||||
state = NONE;
|
||||
Maxsi::Memory::Set(®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);
|
||||
}
|
||||
}
|
||||
|
|
107
sortix/thread.h
107
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 <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
thread.h
|
||||
Describes a thread belonging to a process.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_THREAD_H
|
||||
#define SORTIX_THREAD_H
|
||||
|
||||
#include <sortix/signal.h>
|
||||
#include "signal.h"
|
||||
|
||||
typedef struct multiboot_info multiboot_info_t;
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
class Event;
|
||||
class Process;
|
||||
class Thread;
|
||||
|
||||
// Adds a thread to the current process.
|
||||
Thread* CreateThread(addr_t entry, size_t stacksize = 0);
|
||||
Thread* CurrentThread();
|
||||
extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo);
|
||||
|
||||
typedef void (*ThreadEntry)(void* user);
|
||||
|
||||
// Simply exits the kernel thread.
|
||||
void KernelThreadExit() SORTIX_NORETURN;
|
||||
|
||||
// Internally used as a kernel thread entry point that exits the thread
|
||||
// upon the actual thread entry returning.
|
||||
extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry) SORTIX_NORETURN;
|
||||
|
||||
// These functions create a new kernel process but doesn't start it.
|
||||
Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs);
|
||||
Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user,
|
||||
size_t stacksize = 0);
|
||||
Thread* CreateKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0);
|
||||
|
||||
// This function can be used to start a thread from the above functions.
|
||||
void StartKernelThread(Thread* thread);
|
||||
|
||||
// Alternatively, these functions both create and start the thread.
|
||||
Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs);
|
||||
Thread* RunKernelThread(Process* process, ThreadEntry entry, void* user,
|
||||
size_t stacksize = 0);
|
||||
Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0);
|
||||
|
||||
void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
|
||||
void* user, addr_t stack, size_t stacksize);
|
||||
|
||||
extern "C" void Thread__OnSigKill(Thread* thread);
|
||||
|
||||
typedef void (*sighandler_t)(int);
|
||||
|
||||
class Thread
|
||||
{
|
||||
friend Thread* CreateThread(addr_t entry, size_t stacksize);
|
||||
friend Thread* CreateKernelThread(Process* process,
|
||||
CPU::InterruptRegisters* regs);
|
||||
friend void KernelInit(unsigned long magic, multiboot_info_t* bootinfo);
|
||||
friend void Thread__OnSigKill(Thread* thread);
|
||||
|
||||
public:
|
||||
enum State { NONE, RUNNABLE, BLOCKING };
|
||||
enum State { NONE, RUNNABLE, BLOCKING, DEAD };
|
||||
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
private:
|
||||
Thread();
|
||||
Thread(const Thread* forkfrom);
|
||||
void ResetCallbacks();
|
||||
|
||||
public:
|
||||
~Thread();
|
||||
|
@ -60,64 +90,53 @@ namespace Sortix
|
|||
public:
|
||||
size_t id;
|
||||
Process* process;
|
||||
pid_t pidbackup;
|
||||
addr_t addrspacebackup;
|
||||
bool terminated;
|
||||
Thread* prevsibling;
|
||||
Thread* nextsibling;
|
||||
|
||||
// These are used internally when a thread is waiting for an Event to
|
||||
// happen. Consider them private.
|
||||
public:
|
||||
Event* event;
|
||||
Thread* eventnextwaiting;
|
||||
|
||||
// These are some things used internally by the scheduler and should not be
|
||||
// touched by anything but it. Consider it private.
|
||||
public:
|
||||
Thread* schedulerlistprev;
|
||||
Thread* schedulerlistnext;
|
||||
State state;
|
||||
uintmax_t sleepuntil;
|
||||
Thread* nextsleepingthread;
|
||||
volatile State state;
|
||||
|
||||
public:
|
||||
addr_t addrspace;
|
||||
addr_t stackpos;
|
||||
size_t stacksize;
|
||||
Signal* currentsignal;
|
||||
SignalQueue signalqueue;
|
||||
sighandler_t sighandler;
|
||||
|
||||
// After being created/forked, a thread is not inserted into the scheduler.
|
||||
// Whenever the thread has been safely established within a process, then
|
||||
// call Ready() to finalize the creation and insert it into the scheduler.
|
||||
private:
|
||||
bool ready;
|
||||
|
||||
public:
|
||||
void Ready();
|
||||
addr_t kernelstackpos;
|
||||
size_t kernelstacksize;
|
||||
bool kernelstackmalloced;
|
||||
|
||||
private:
|
||||
CPU::InterruptRegisters registers;
|
||||
Signal::Queue signalqueue;
|
||||
int currentsignal;
|
||||
int siglevel;
|
||||
int signums[SIG_NUM_LEVELS];
|
||||
CPU::InterruptRegisters sigregs[SIG_NUM_LEVELS];
|
||||
|
||||
public:
|
||||
Thread* Fork();
|
||||
void SaveRegisters(const CPU::InterruptRegisters* src);
|
||||
void LoadRegisters(CPU::InterruptRegisters* dest);
|
||||
void HandleSignal(CPU::InterruptRegisters* regs);
|
||||
void HandleSigreturn(CPU::InterruptRegisters* regs);
|
||||
bool DeliverSignal(int signum);
|
||||
addr_t SwitchAddressSpace(addr_t newaddrspace);
|
||||
|
||||
private:
|
||||
void GotoOnSigKill(CPU::InterruptRegisters* regs);
|
||||
void OnSigKill() SORTIX_NORETURN;
|
||||
void LastPrayer();
|
||||
void SetHavePendingSignals();
|
||||
void HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs);
|
||||
void HandleSignalCPU(CPU::InterruptRegisters* regs);
|
||||
|
||||
public:
|
||||
void* scfunc;
|
||||
size_t scsize;
|
||||
size_t scstate[8];
|
||||
|
||||
public:
|
||||
void (*onchildprocessexit)(Thread*, Process*);
|
||||
|
||||
};
|
||||
|
||||
Thread* CurrentThread();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -255,6 +255,20 @@ isr128:
|
|||
pushq $0 # err_code
|
||||
pushq $128 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global isr130
|
||||
.type isr130, @function
|
||||
isr130:
|
||||
cli
|
||||
pushq $0 # err_code
|
||||
pushq $130 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global isr131
|
||||
.type isr131, @function
|
||||
isr131:
|
||||
cli
|
||||
pushq $0 # err_code
|
||||
pushq $131 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global irq0
|
||||
.type irq0, @function
|
||||
irq0:
|
||||
|
@ -367,8 +381,24 @@ irq15:
|
|||
pushq $0 # err_code
|
||||
pushq $47 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global yield_cpu_handler
|
||||
.type yield_cpu_handler, @function
|
||||
yield_cpu_handler:
|
||||
cli
|
||||
pushq $0 # err_code
|
||||
pushq $129 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global thread_exit_handler
|
||||
.type thread_exit_handler, @function
|
||||
thread_exit_handler:
|
||||
cli
|
||||
pushq $0 # err_code
|
||||
pushq $132 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
|
||||
interrupt_handler_prepare:
|
||||
movq $1, asm_is_cpu_interrupted
|
||||
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
|
@ -401,10 +431,27 @@ interrupt_handler_prepare:
|
|||
movq %cr2, %rbp
|
||||
pushq %rbp
|
||||
|
||||
# Push the current kernel errno value.
|
||||
movl global_errno, %ebp
|
||||
pushq %rbp
|
||||
|
||||
# Push whether a signal is pending.
|
||||
movq asm_signal_is_pending, %rbp
|
||||
pushq %rbp
|
||||
|
||||
# Now call the interrupt handler.
|
||||
movq %rsp, %rdi
|
||||
call interrupt_handler
|
||||
|
||||
load_interrupted_registers:
|
||||
# Restore whether signals are pending.
|
||||
popq %rbp
|
||||
movq %rbp, asm_signal_is_pending
|
||||
|
||||
# Restore the previous kernel errno.
|
||||
popq %rbp
|
||||
movl %ebp, global_errno
|
||||
|
||||
# Remove CR2 from the stack.
|
||||
addq $8, %rsp
|
||||
|
||||
|
@ -418,7 +465,7 @@ interrupt_handler_prepare:
|
|||
popq %rdi
|
||||
popq %rsi
|
||||
popq %rbp
|
||||
popq %rsp
|
||||
addq $8, %rsp # Don't pop %rsp, may not be defined.
|
||||
popq %rbx
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
|
@ -435,6 +482,8 @@ interrupt_handler_prepare:
|
|||
# Remove int_no and err_code
|
||||
addq $16, %rsp
|
||||
|
||||
movq $0, asm_is_cpu_interrupted
|
||||
|
||||
# Return to where we came from.
|
||||
iretq
|
||||
|
||||
|
@ -451,3 +500,10 @@ asm_interrupts_are_enabled:
|
|||
andq $0x000200, %rax # FLAGS_INTERRUPT
|
||||
retq
|
||||
|
||||
.global load_registers
|
||||
.type load_registers, @function
|
||||
load_registers:
|
||||
# Let the register struct become our temporary stack
|
||||
movq %rdi, %rsp
|
||||
jmp load_interrupted_registers
|
||||
|
||||
|
|
85
sortix/x64/kthread.s
Normal file
85
sortix/x64/kthread.s
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x64/kthread.s
|
||||
Utilities and synchronization mechanisms for x64 kernel threads.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
.section .text
|
||||
|
||||
.global kthread_mutex_trylock
|
||||
.type kthread_mutex_trylock, @function
|
||||
kthread_mutex_trylock:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
movl $-1, %eax
|
||||
xchgl (%rdi), %eax
|
||||
not %eax
|
||||
leaveq
|
||||
retq
|
||||
|
||||
.global kthread_mutex_lock
|
||||
.type kthread_mutex_lock, @function
|
||||
kthread_mutex_lock:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
kthread_mutex_lock_retry:
|
||||
movl $-1, %eax
|
||||
xchgl (%rdi), %eax
|
||||
testl %eax, %eax
|
||||
jnz kthread_mutex_lock_failed
|
||||
leaveq
|
||||
retq
|
||||
kthread_mutex_lock_failed:
|
||||
int $0x81 # Yield the CPU.
|
||||
jmp kthread_mutex_lock_retry
|
||||
|
||||
.global kthread_mutex_lock_signal
|
||||
.type kthread_mutex_lock_signal, @function
|
||||
kthread_mutex_lock_signal:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
kthread_mutex_lock_signal_retry:
|
||||
movq asm_signal_is_pending, %rax
|
||||
testq %rax, %rax
|
||||
jnz kthread_mutex_lock_signal_pending
|
||||
movl $-1, %eax
|
||||
xchgl (%rdi), %eax
|
||||
testl %eax, %eax
|
||||
jnz kthread_mutex_lock_signal_failed
|
||||
inc %eax
|
||||
kthread_mutex_lock_signal_out:
|
||||
leaveq
|
||||
retq
|
||||
kthread_mutex_lock_signal_failed:
|
||||
int $0x81 # Yield the CPU.
|
||||
jmp kthread_mutex_lock_signal_retry
|
||||
kthread_mutex_lock_signal_pending:
|
||||
xorl %eax, %eax
|
||||
jmp kthread_mutex_lock_signal_out
|
||||
|
||||
.global kthread_mutex_unlock
|
||||
.type kthread_mutex_unlock, @function
|
||||
kthread_mutex_unlock:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
movl $0, (%rdi)
|
||||
leaveq
|
||||
retq
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -23,11 +23,11 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "multiboot.h"
|
||||
#include <sortix/kernel/panic.h>
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include "x86-family/memorymanagement.h"
|
||||
#include "interrupt.h"
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
|
@ -120,17 +120,18 @@ namespace Sortix
|
|||
PML* pml = PMLS[level] + offset;
|
||||
for ( size_t i = 0; i < ENTRIES; i++ )
|
||||
{
|
||||
if ( !(pml->entry[i] & PML_PRESENT) ) { continue; }
|
||||
if ( !(pml->entry[i] & PML_USERSPACE) ) { continue; }
|
||||
if ( !(pml->entry[i] & PML_FORK) ) { continue; }
|
||||
addr_t entry = pml->entry[i];
|
||||
if ( !(entry & PML_PRESENT) ) { continue; }
|
||||
if ( !(entry & PML_USERSPACE) ) { continue; }
|
||||
if ( !(entry & PML_FORK) ) { continue; }
|
||||
if ( level > 1 ) { RecursiveFreeUserspacePages(level-1, offset * ENTRIES + i); }
|
||||
addr_t addr = pml->entry[i] & PML_ADDRESS;
|
||||
pml->entry[i] = 0;
|
||||
Page::Put(addr);
|
||||
// No need to unmap the page, we just need to mark it as unused.
|
||||
Page::PutUnlocked(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyAddressSpace()
|
||||
void DestroyAddressSpace(addr_t fallback, void (*func)(addr_t, void*), void* user)
|
||||
{
|
||||
// Look up the last few entries used for the fractal mapping. These
|
||||
// cannot be unmapped as that would destroy the world. Instead, we
|
||||
|
@ -143,17 +144,31 @@ namespace Sortix
|
|||
addr_t fractal1 = (PMLS[2] + 510UL * 512UL + 510UL)->entry[510];
|
||||
addr_t dir = currentdir;
|
||||
|
||||
// First let's do the safe part. Garbage collect any PML1/0's left
|
||||
// behind by user-space. These are completely safe to delete.
|
||||
// We want to free the pages, but we are still using them ourselves,
|
||||
// so lock the page allocation structure until we are done.
|
||||
Page::Lock();
|
||||
|
||||
// In case any pages wasn't cleaned at this point.
|
||||
#warning Page::Put calls may internally Page::Get and then reusing pages we are not done with just yet
|
||||
RecursiveFreeUserspacePages(TOPPMLLEVEL, 0);
|
||||
|
||||
// Switch to the address space from when the world was originally
|
||||
// created. It should contain the kernel, the whole kernel, and
|
||||
// nothing but the kernel.
|
||||
PML* const BOOTPML4 = (PML* const) 0x21000UL;
|
||||
SwitchAddressSpace((addr_t) BOOTPML4);
|
||||
if ( !fallback )
|
||||
fallback = (addr_t) BOOTPML4;
|
||||
|
||||
// Now safely mark the pages as unused.
|
||||
if ( func )
|
||||
func(fallback, user);
|
||||
else
|
||||
SwitchAddressSpace(fallback);
|
||||
|
||||
// Ok, now we got marked everything left behind as unused, we can
|
||||
// now safely let another thread use the pages.
|
||||
Page::Unlock();
|
||||
|
||||
// These are safe to free since we switched address space.
|
||||
Page::Put(fractal3 & PML_ADDRESS);
|
||||
Page::Put(fractal2 & PML_ADDRESS);
|
||||
Page::Put(fractal1 & PML_ADDRESS);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,15 +14,17 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x64/process.cpp
|
||||
CPU-specific process code.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/fork.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "process.h"
|
||||
|
||||
namespace Sortix
|
||||
|
@ -31,12 +33,47 @@ namespace Sortix
|
|||
addr_t stackpos, addr_t entry,
|
||||
CPU::InterruptRegisters* regs)
|
||||
{
|
||||
const uint64_t CS = 0x18;
|
||||
const uint64_t DS = 0x20;
|
||||
const uint64_t RPL = 0x3;
|
||||
|
||||
regs->rdi = argc;
|
||||
regs->rsi = (size_t) argv;
|
||||
regs->rdx = envc;
|
||||
regs->rcx = (size_t) envp;
|
||||
regs->rip = entry;
|
||||
regs->userrsp = stackpos & ~(15UL);
|
||||
regs->rbp = stackpos;
|
||||
regs->rbp = regs->userrsp;
|
||||
regs->cs = CS | RPL;
|
||||
regs->ds = DS | RPL;
|
||||
regs->ss = DS | RPL;
|
||||
regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
}
|
||||
|
||||
void InitializeThreadRegisters(CPU::InterruptRegisters* regs,
|
||||
const sforkregs_t* requested)
|
||||
{
|
||||
Maxsi::Memory::Set(regs, 0, sizeof(*regs));
|
||||
regs->rip = requested->rip;
|
||||
regs->userrsp = requested->rsp;
|
||||
regs->rax = requested->rax;
|
||||
regs->rbx = requested->rbx;
|
||||
regs->rcx = requested->rcx;
|
||||
regs->rdx = requested->rdx;
|
||||
regs->rdi = requested->rdi;
|
||||
regs->rsi = requested->rsi;
|
||||
regs->rbp = requested->rbp;
|
||||
regs->r8 = requested->r8;
|
||||
regs->r9 = requested->r9;
|
||||
regs->r10 = requested->r10;
|
||||
regs->r11 = requested->r11;
|
||||
regs->r12 = requested->r12;
|
||||
regs->r13 = requested->r13;
|
||||
regs->r14 = requested->r14;
|
||||
regs->r15 = requested->r15;
|
||||
regs->cs = 0x18 | 0x3;
|
||||
regs->ds = 0x20 | 0x3;
|
||||
regs->ss = 0x20 | 0x3;
|
||||
regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,73 +14,41 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
syscall.s
|
||||
x64/syscall.s
|
||||
An assembly stub that acts as glue for system calls.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
.global syscall_handler
|
||||
.global resume_syscall
|
||||
|
||||
.section .text
|
||||
.type syscall_handler, @function
|
||||
syscall_handler:
|
||||
cli
|
||||
# The processor disabled interrupts during the int $0x80 instruction,
|
||||
# however Sortix system calls runs with interrupts enabled such that they
|
||||
# can be pre-empted.
|
||||
sti
|
||||
|
||||
# Compabillity with InterruptRegisters.
|
||||
pushq $0x0
|
||||
pushq $0x80
|
||||
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %r11
|
||||
pushq %r10
|
||||
pushq %r9
|
||||
pushq %r8
|
||||
pushq %rax
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
pushq %rbx
|
||||
pushq %rsp
|
||||
movl $0, global_errno # Reset errno
|
||||
pushq %rbp
|
||||
pushq %rsi
|
||||
pushq %rdi
|
||||
|
||||
# Push the user-space data segment.
|
||||
# Grant ourselves kernel permissions to the data segment.
|
||||
movl %ds, %ebp
|
||||
pushq %rbp
|
||||
|
||||
# Load the kernel data segment.
|
||||
movw $0x10, %bp
|
||||
movl %ebp, %ds
|
||||
movl %ebp, %es
|
||||
movl %ebp, %fs
|
||||
movl %ebp, %gs
|
||||
|
||||
# Compabillity with InterruptRegisters.
|
||||
movq %cr2, %rbp
|
||||
pushq %rbp
|
||||
|
||||
# Store the state structure's pointer so the call can modify it if needed.
|
||||
movq %rsp, syscall_state_ptr
|
||||
|
||||
# By default, assume the system call was complete.
|
||||
movl $0, system_was_incomplete
|
||||
|
||||
# Reset the kernel errno.
|
||||
movl $0, global_errno
|
||||
|
||||
# Make sure the requested system call is valid.
|
||||
# Make sure the requested system call is valid, if not, then fix it.
|
||||
cmp SYSCALL_MAX, %rax
|
||||
jb valid_rax
|
||||
xorq %rax, %rax
|
||||
jae fix_syscall
|
||||
|
||||
valid_rax:
|
||||
valid_syscall:
|
||||
# Read a system call function pointer.
|
||||
xorq %rbp, %rbp
|
||||
movq syscall_list(%rbp,%rax,8), %rax
|
||||
|
@ -88,72 +56,40 @@ valid_rax:
|
|||
# Oh how nice, user-space put the parameters in: rdi, rsi, rdx, rcx, r8, r9
|
||||
|
||||
# Call the system call.
|
||||
callq *%rax
|
||||
callq *%rax
|
||||
|
||||
# Test if the system call was incomplete
|
||||
movl system_was_incomplete, %ebx
|
||||
testl %ebx, %ebx
|
||||
|
||||
# If the system call was incomplete, the value in %eax is meaningless.
|
||||
jg return_to_userspace
|
||||
|
||||
# The system call was completed, so store the return value.
|
||||
movq %rax, 72(%rsp)
|
||||
|
||||
# Don't forget to update userspace's errno value.
|
||||
call update_userspace_errno
|
||||
|
||||
return_to_userspace:
|
||||
# Compabillity with InterruptRegisters.
|
||||
addq $8, %rsp
|
||||
|
||||
# Restore the user-space data segment.
|
||||
# Restore the previous permissions to data segment.
|
||||
popq %rbp
|
||||
movl %ebp, %ds
|
||||
movl %ebp, %es
|
||||
movl %ebp, %fs
|
||||
movl %ebp, %gs
|
||||
|
||||
popq %rdi
|
||||
popq %rsi
|
||||
# Return to user-space, system call result in %rax, errno in %edx.
|
||||
popq %rbp
|
||||
popq %rsp
|
||||
popq %rbx
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
popq %rax
|
||||
popq %r8
|
||||
popq %r9
|
||||
popq %r10
|
||||
popq %r11
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
movl global_errno, %edx
|
||||
|
||||
# Compabillity with InterruptRegisters.
|
||||
addq $16, %rsp
|
||||
# If any signals are pending, fire them now.
|
||||
movq asm_signal_is_pending, %rdi
|
||||
testq %rdi, %rdi
|
||||
jnz call_signal_dispatcher
|
||||
|
||||
# Return to user-space.
|
||||
iretq
|
||||
|
||||
.type resume_syscall, @function
|
||||
resume_syscall:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
|
||||
movq %rdi, %rax
|
||||
movq %rdx, %r11
|
||||
|
||||
movq 0(%r11), %rdi
|
||||
movq 8(%r11), %rsi
|
||||
movq 16(%r11), %rdx
|
||||
movq 24(%r11), %rcx
|
||||
movq 32(%r11), %r8
|
||||
movq 40(%r11), %r9
|
||||
|
||||
callq *%rax
|
||||
|
||||
leaveq
|
||||
retq
|
||||
fix_syscall:
|
||||
# Call the null system call instead.
|
||||
xorq %rax, %rax
|
||||
jmp valid_syscall
|
||||
|
||||
call_signal_dispatcher:
|
||||
# We can't return to this location after the signal, since if any system
|
||||
# call is made this stack will get reused and all our nice temporaries wil
|
||||
# be garbage. We therefore pass the kernel the state to return to and it'll
|
||||
# handle it for us when the signal is over.
|
||||
movq 0(%rsp), %rdi # userspace rip
|
||||
movq 16(%rsp), %rsi # userspace rflags
|
||||
movq 24(%rsp), %rcx # userspace rsp, note %rdx is used for errno
|
||||
int $130 # Deliver pending signals.
|
||||
# If we end up here, it means that the signal didn't override anything and
|
||||
# that we should just go ahead and return to userspace ourselves.
|
||||
iretq
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
thread.cpp
|
||||
x64 specific parts of thread.cpp.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include "thread.h"
|
||||
|
@ -50,6 +50,8 @@ namespace Sortix
|
|||
registers.ds = src->ds;
|
||||
registers.ss = src->ss;
|
||||
registers.rflags = src->rflags;
|
||||
registers.kerrno = src->kerrno;
|
||||
registers.signal_pending = src->signal_pending;
|
||||
}
|
||||
|
||||
void Thread::LoadRegisters(CPU::InterruptRegisters* dest)
|
||||
|
@ -75,65 +77,78 @@ namespace Sortix
|
|||
dest->ds = registers.ds;
|
||||
dest->ss = registers.ss;
|
||||
dest->rflags = registers.rflags;
|
||||
dest->kerrno = registers.kerrno;
|
||||
dest->signal_pending = registers.signal_pending;
|
||||
}
|
||||
|
||||
const size_t FLAGS_CARRY = (1<<0);
|
||||
const size_t FLAGS_RESERVED1 = (1<<1); /* read as one */
|
||||
const size_t FLAGS_PARITY = (1<<2);
|
||||
const size_t FLAGS_RESERVED2 = (1<<3);
|
||||
const size_t FLAGS_AUX = (1<<4);
|
||||
const size_t FLAGS_RESERVED3 = (1<<5);
|
||||
const size_t FLAGS_ZERO = (1<<6);
|
||||
const size_t FLAGS_SIGN = (1<<7);
|
||||
const size_t FLAGS_TRAP = (1<<8);
|
||||
const size_t FLAGS_INTERRUPT = (1<<9);
|
||||
const size_t FLAGS_DIRECTION = (1<<10);
|
||||
const size_t FLAGS_OVERFLOW = (1<<11);
|
||||
const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13);
|
||||
const size_t FLAGS_NESTEDTASK = (1<<14);
|
||||
const size_t FLAGS_RESERVED4 = (1<<15);
|
||||
const size_t FLAGS_RESUME = (1<<16);
|
||||
const size_t FLAGS_VIRTUAL8086 = (1<<17);
|
||||
const size_t FLAGS_ALIGNCHECK = (1<<18);
|
||||
const size_t FLAGS_VIRTINTR = (1<<19);
|
||||
const size_t FLAGS_VIRTINTRPEND = (1<<20);
|
||||
const size_t FLAGS_ID = (1<<21);
|
||||
|
||||
void CreateThreadCPU(Thread* thread, addr_t entry)
|
||||
void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
|
||||
void* user, addr_t stack, size_t stacksize)
|
||||
{
|
||||
const uint64_t CS = 0x18;
|
||||
const uint64_t DS = 0x20;
|
||||
const uint64_t RPL = 0x3;
|
||||
// Instead of directly calling the desired entry point, we call a small
|
||||
// stub that calls it for us and then destroys the kernel thread if
|
||||
// the entry function returns. Note that since we use a register based
|
||||
// calling convention, we call BootstrapKernelThread directly.
|
||||
regs->rip = (addr_t) BootstrapKernelThread;
|
||||
regs->userrsp = stack + stacksize;
|
||||
regs->rax = 0;
|
||||
regs->rbx = 0;
|
||||
regs->rcx = 0;
|
||||
regs->rdx = 0;
|
||||
regs->rdi = (addr_t) user;
|
||||
regs->rsi = (addr_t) entry;
|
||||
regs->rbp = 0;
|
||||
regs->r8 = 0;
|
||||
regs->r9 = 0;
|
||||
regs->r10 = 0;
|
||||
regs->r11 = 0;
|
||||
regs->r12 = 0;
|
||||
regs->r13 = 0;
|
||||
regs->r14 = 0;
|
||||
regs->r15 = 0;
|
||||
regs->cs = KCS | KRPL;
|
||||
regs->ds = KDS | KRPL;
|
||||
regs->ss = KDS | KRPL;
|
||||
regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
regs->kerrno = 0;
|
||||
regs->signal_pending = 0;
|
||||
}
|
||||
|
||||
CPU::InterruptRegisters regs;
|
||||
regs.rip = entry;
|
||||
regs.userrsp = thread->stackpos + thread->stacksize;
|
||||
regs.rax = 0;
|
||||
regs.rbx = 0;
|
||||
regs.rcx = 0;
|
||||
regs.rdx = 0;
|
||||
regs.rdi = 0;
|
||||
regs.rsi = 0;
|
||||
regs.rbp = thread->stackpos + thread->stacksize;
|
||||
regs.r8 = 0;
|
||||
regs.r9 = 0;
|
||||
regs.r10 = 0;
|
||||
regs.r11 = 0;
|
||||
regs.r12 = 0;
|
||||
regs.r13 = 0;
|
||||
regs.r14 = 0;
|
||||
regs.r15 = 0;
|
||||
regs.cs = CS | RPL;
|
||||
regs.ds = DS | RPL;
|
||||
regs.ss = DS | RPL;
|
||||
regs.rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
|
||||
thread->SaveRegisters(®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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x64.h
|
||||
CPU stuff for the x64 platform.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_X64_H
|
||||
#define SORTIX_X64_H
|
||||
|
@ -33,7 +33,7 @@ namespace Sortix
|
|||
{
|
||||
struct InterruptRegisters
|
||||
{
|
||||
uint64_t cr2;
|
||||
uint64_t signal_pending, kerrno, cr2;
|
||||
uint64_t ds; // Data segment selector
|
||||
uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
|
||||
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
|
||||
|
@ -42,18 +42,17 @@ namespace Sortix
|
|||
|
||||
public:
|
||||
void LogRegisters() const;
|
||||
};
|
||||
bool InUserspace() const { return (cs & 0x3) != 0; }
|
||||
|
||||
struct SyscallRegisters
|
||||
{
|
||||
uint64_t cr2; // For compabillity with above, may be removed soon.
|
||||
uint64_t ds;
|
||||
uint64_t di, si, bp, trash, b, d, c; union { uint64_t a; uint64_t result; };
|
||||
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
|
||||
uint64_t int_no, err_code; // Also compabillity.
|
||||
uint64_t ip, cs, flags, sp, ss;
|
||||
};
|
||||
}
|
||||
|
||||
const uint64_t KCS = 0x08;
|
||||
const uint64_t KDS = 0x10;
|
||||
const uint64_t KRPL = 0x0;
|
||||
const uint64_t UCS = 0x18;
|
||||
const uint64_t UDS = 0x20;
|
||||
const uint64_t URPL = 0x3;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x86-family.h
|
||||
CPU stuff for the x86 CPU family.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_X86_FAMILY_H
|
||||
#define SORTIX_X86_FAMILY_H
|
||||
|
@ -38,6 +38,28 @@ namespace Sortix
|
|||
void Reboot();
|
||||
void ShutDown();
|
||||
}
|
||||
|
||||
const size_t FLAGS_CARRY = (1<<0); // 0x000001
|
||||
const size_t FLAGS_RESERVED1 = (1<<1); // 0x000002, read as one
|
||||
const size_t FLAGS_PARITY = (1<<2); // 0x000004
|
||||
const size_t FLAGS_RESERVED2 = (1<<3); // 0x000008
|
||||
const size_t FLAGS_AUX = (1<<4); // 0x000010
|
||||
const size_t FLAGS_RESERVED3 = (1<<5); // 0x000020
|
||||
const size_t FLAGS_ZERO = (1<<6); // 0x000040
|
||||
const size_t FLAGS_SIGN = (1<<7); // 0x000080
|
||||
const size_t FLAGS_TRAP = (1<<8); // 0x000100
|
||||
const size_t FLAGS_INTERRUPT = (1<<9); // 0x000200
|
||||
const size_t FLAGS_DIRECTION = (1<<10); // 0x000400
|
||||
const size_t FLAGS_OVERFLOW = (1<<11); // 0x000800
|
||||
const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13);
|
||||
const size_t FLAGS_NESTEDTASK = (1<<14); // 0x004000
|
||||
const size_t FLAGS_RESERVED4 = (1<<15); // 0x008000
|
||||
const size_t FLAGS_RESUME = (1<<16); // 0x010000
|
||||
const size_t FLAGS_VIRTUAL8086 = (1<<17); // 0x020000
|
||||
const size_t FLAGS_ALIGNCHECK = (1<<18); // 0x040000
|
||||
const size_t FLAGS_VIRTINTR = (1<<19); // 0x080000
|
||||
const size_t FLAGS_VIRTINTRPEND = (1<<20); // 0x100000
|
||||
const size_t FLAGS_ID = (1<<21); // 0x200000
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -255,6 +255,20 @@ isr128:
|
|||
pushl $0 # err_code
|
||||
pushl $128 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global isr130
|
||||
.type isr130, @function
|
||||
isr130:
|
||||
cli
|
||||
pushl $0 # err_code
|
||||
pushl $130 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global isr131
|
||||
.type isr131, @function
|
||||
isr131:
|
||||
cli
|
||||
pushl $0 # err_code
|
||||
pushl $131 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global irq0
|
||||
.type irq0, @function
|
||||
irq0:
|
||||
|
@ -367,8 +381,29 @@ irq15:
|
|||
pushl $0 # err_code
|
||||
pushl $47 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global yield_cpu_handler
|
||||
.type yield_cpu_handler, @function
|
||||
yield_cpu_handler:
|
||||
cli
|
||||
pushl $0 # err_code
|
||||
pushl $129 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
.global thread_exit_handler
|
||||
.type thread_exit_handler, @function
|
||||
thread_exit_handler:
|
||||
cli
|
||||
pushl $0 # err_code
|
||||
pushl $132 # int_no
|
||||
jmp interrupt_handler_prepare
|
||||
|
||||
interrupt_handler_prepare:
|
||||
movl $1, asm_is_cpu_interrupted
|
||||
|
||||
# Check if an interrupt happened while having kernel permissions.
|
||||
testw $0x3, 12(%esp) # cs
|
||||
jz fixup_relocate_stack
|
||||
fixup_relocate_stack_complete:
|
||||
|
||||
pushl %eax
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
|
@ -393,11 +428,28 @@ interrupt_handler_prepare:
|
|||
movl %cr2, %ebp
|
||||
pushl %ebp
|
||||
|
||||
# Push the current kernel errno value.
|
||||
movl global_errno, %ebp
|
||||
pushl %ebp
|
||||
|
||||
# Push whether a signal is pending.
|
||||
movl asm_signal_is_pending, %ebp
|
||||
pushl %ebp
|
||||
|
||||
# Now call the interrupt handler.
|
||||
pushl %esp
|
||||
call interrupt_handler
|
||||
addl $4, %esp
|
||||
|
||||
load_interrupted_registers:
|
||||
# Restore whether signals are pending.
|
||||
popl %ebp
|
||||
movl %ebp, asm_signal_is_pending
|
||||
|
||||
# Restore the previous kernel errno.
|
||||
popl %ebp
|
||||
movl %ebp, global_errno
|
||||
|
||||
# Remove CR2 from the stack.
|
||||
addl $4, %esp
|
||||
|
||||
|
@ -411,7 +463,7 @@ interrupt_handler_prepare:
|
|||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
popl %esp
|
||||
addl $4, %esp # Don't pop %esp, may not be defined.
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %ecx
|
||||
|
@ -420,9 +472,107 @@ interrupt_handler_prepare:
|
|||
# Remove int_no and err_code
|
||||
addl $8, %esp
|
||||
|
||||
movl $0, asm_is_cpu_interrupted
|
||||
|
||||
# If interrupted with kernel permissions we may need to switch stack.
|
||||
testw $0x3, 4(%esp) # int_no and err_code now gone, so cs is at 4(%esp).
|
||||
jz fixup_switch_stack
|
||||
fixup_switch_stack_complete:
|
||||
|
||||
# Return to where we came from.
|
||||
iret
|
||||
|
||||
fixup_relocate_stack:
|
||||
# Ok, so some genius at Intel decided that if the permission level does not
|
||||
# change during an interrupt then the CPU won't push the stack pointer and
|
||||
# it won't reload it during iret. This seriously messes up the scheduler
|
||||
# that wants to preempt kernel threads each with their own stack. The
|
||||
# scheduler will attempt to read (and modify) the stack value which doesn't
|
||||
# exist and worse: the value at that location is likely used by the
|
||||
# interrupted kernel thread. A quick and dirty solution is to simply move
|
||||
# the stack 8 bytes down the stack. Right now there are the 5 elements on
|
||||
# the stack (eflags, cs, eip, err_code, int_no) of 5 bytes each.
|
||||
mov %eax, -4-8(%esp) # Save eax
|
||||
mov 0(%esp), %eax # int_no
|
||||
mov %eax, 0-8(%esp)
|
||||
mov 4(%esp), %eax # err_code
|
||||
mov %eax, 4-8(%esp)
|
||||
mov 8(%esp), %eax # eip
|
||||
mov %eax, 8-8(%esp)
|
||||
mov 12(%esp), %eax # cs
|
||||
mov %eax, 12-8(%esp)
|
||||
mov 16(%esp), %eax # eflags
|
||||
mov %eax, 16-8(%esp)
|
||||
# Next up we have to fake what the CPU should have done: pushed ss and esp.
|
||||
mov %esp, %eax
|
||||
addl $5*4, %eax # Calculate original esp
|
||||
mov %eax, 20-8(%esp)
|
||||
mov %ss, %eax
|
||||
mov %eax, 24-8(%esp)
|
||||
# Now that we moved the stack, it's time to really handle the interrupt.
|
||||
mov -4-8(%esp), %eax
|
||||
subl $8, %esp
|
||||
jmp fixup_relocate_stack_complete
|
||||
|
||||
fixup_switch_stack:
|
||||
# Yup, we also have to do special processing when we return from the
|
||||
# interrupt. The problem is that if the iret instruction won't load a new
|
||||
# stack if interrupted with kernel permissions and that the scheduler may
|
||||
# wish to change the current stack during a context switch. We will then
|
||||
# switch the stack before calling iret; but iret needs the return
|
||||
# information on the stack (and now it isn't), so we'll copy our stack onto
|
||||
# our new stack and then fire the interrupt and everyone is happy.
|
||||
|
||||
# In the following code, %esp will point our fixed iret return parameters
|
||||
# that has stack data. However, the processor does not expect this
|
||||
# information as cs hasn't changed. %ebx will point to the new stack plus
|
||||
# room for three 32-bit values (eip, cs, eflags) that will be given to the
|
||||
# actual iret. We will then load the new stack and copy the eip, cs and
|
||||
# eflags to the new stack. However, we have to be careful in the case that
|
||||
# we are switching to the same stack (in which case stuff on the same
|
||||
# horizontal location in the diagram is actually on the same memory
|
||||
# location). We therefore copy to the new stack and carefully avoid
|
||||
# corrupting the destination if %esp + 8 = %ebx, This diagram show the
|
||||
# structure of the stacks and where temporaries will be stored:
|
||||
# -12 -8 -4 %esp 4 8 12 16 20
|
||||
# old: IECX IEBX IEAX EIP CS EFLAGS ESP SS ...
|
||||
# new: IECX IEBX IEAX - - EIP CS EFLAGS ...
|
||||
# -20 -16 -12 -8 -4 %ebx 4 8 12
|
||||
|
||||
mov %eax, -4(%esp) # IEAX, Clobbered as copying temporary
|
||||
mov %ebx, -8(%esp) # IEBX, Clobbered as pointer to new stack
|
||||
mov %ecx, -12(%esp) # IECX, Clobbered as new stack selector
|
||||
|
||||
mov 12(%esp), %ebx # Pointer to new stack
|
||||
sub $3*4, %ebx # Point to eip on the new stack (see diagram)
|
||||
movw 16(%esp), %cx # New ss
|
||||
|
||||
# The order of these does not matter if we are switching to the same stack,
|
||||
# as the memory would be copied to the same location (see diagram).
|
||||
mov -4(%esp), %eax # interrupted eax value
|
||||
mov %eax, -12(%ebx)
|
||||
mov -8(%esp), %eax # interrupted ebx value
|
||||
mov %eax, -16(%ebx)
|
||||
mov -12(%esp), %eax # interrupted ecx value
|
||||
mov %eax, -20(%ebx)
|
||||
|
||||
# The order of these three copies matter if switching to the same stack.
|
||||
mov 8(%esp), %eax # eflags
|
||||
mov %eax, 8(%ebx)
|
||||
mov 4(%esp), %eax # cs
|
||||
mov %eax, 4(%ebx)
|
||||
mov 0(%esp), %eax # eip
|
||||
mov %eax, 0(%ebx)
|
||||
|
||||
mov %cx, %ss # Load new stack selector
|
||||
mov %ebx, %esp # Load new stack pointer
|
||||
|
||||
mov -12(%esp), %eax # restore interrupted eax value
|
||||
mov -16(%esp), %ebx # restore interrupted ebx value
|
||||
mov -20(%esp), %ecx # restore interrupted ecx value
|
||||
|
||||
jmp fixup_switch_stack_complete
|
||||
|
||||
.global interrupt_handler_null
|
||||
.type interrupt_handler_null, @function
|
||||
interrupt_handler_null:
|
||||
|
@ -436,3 +586,10 @@ asm_interrupts_are_enabled:
|
|||
andl $0x000200, %eax # FLAGS_INTERRUPT
|
||||
retl
|
||||
|
||||
.global load_registers
|
||||
.type load_registers, @function
|
||||
load_registers:
|
||||
# Let the register struct become our temporary stack
|
||||
movl 4(%esp), %esp
|
||||
jmp load_interrupted_registers
|
||||
|
||||
|
|
104
sortix/x86/kthread.s
Normal file
104
sortix/x86/kthread.s
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x64/kthread.s
|
||||
Utilities and synchronization mechanisms for x86 kernel threads.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
.section .text
|
||||
|
||||
.global kthread_mutex_trylock
|
||||
.type kthread_mutex_trylock, @function
|
||||
kthread_mutex_trylock:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
movl 8(%ebp), %edx
|
||||
movl $-1, %eax
|
||||
xchgl (%edx), %eax
|
||||
not %eax
|
||||
leavel
|
||||
retl
|
||||
|
||||
.global kthread_mutex_lock
|
||||
.type kthread_mutex_lock, @function
|
||||
kthread_mutex_lock:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
movl 8(%ebp), %edx
|
||||
kthread_mutex_lock_retry:
|
||||
movl $-1, %eax
|
||||
xchgl (%edx), %eax
|
||||
testl %eax, %eax
|
||||
jnz kthread_mutex_lock_failed
|
||||
leavel
|
||||
retl
|
||||
kthread_mutex_lock_failed:
|
||||
int $0x81 # Yield the CPU.
|
||||
jmp kthread_mutex_lock_retry
|
||||
|
||||
.global kthread_mutex_lock_signal
|
||||
.type kthread_mutex_lock_signal, @function
|
||||
kthread_mutex_lock_signal:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
movl 8(%ebp), %edx
|
||||
kthread_mutex_lock_signal_retry:
|
||||
movl asm_signal_is_pending, %eax
|
||||
testl %eax, %eax
|
||||
jnz kthread_mutex_lock_signal_pending
|
||||
movl $-1, %eax
|
||||
xchgl (%edx), %eax
|
||||
testl %eax, %eax
|
||||
jnz kthread_mutex_lock_signal_failed
|
||||
inc %eax
|
||||
kthread_mutex_lock_signal_out:
|
||||
leavel
|
||||
retl
|
||||
kthread_mutex_lock_signal_failed:
|
||||
int $0x81 # Yield the CPU.
|
||||
jmp kthread_mutex_lock_signal_retry
|
||||
kthread_mutex_lock_signal_pending:
|
||||
xorl %eax, %eax
|
||||
jmp kthread_mutex_lock_signal_out
|
||||
|
||||
.global kthread_mutex_unlock
|
||||
.type kthread_mutex_unlock, @function
|
||||
kthread_mutex_unlock:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
movl 8(%ebp), %edx
|
||||
movl $0, (%edx)
|
||||
leavel
|
||||
retl
|
||||
|
||||
.global asm_call_BootstrapKernelThread
|
||||
.type asm_call_BootstrapKernelThread, @function
|
||||
asm_call_BootstrapKernelThread:
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
call BootstrapKernelThread
|
||||
# BootstrapKernelThread is noreturn, no need for code here.
|
||||
|
||||
.global asm_call_Thread__OnSigKill
|
||||
.type asm_call_Thread__OnSigKill, @function
|
||||
asm_call_Thread__OnSigKill:
|
||||
pushl %edi
|
||||
call Thread__OnSigKill
|
||||
# Thread__OnSigKill is noreturn, no need for code here.
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,20 +14,21 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x86/process.cpp
|
||||
CPU-specific process code.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/fork.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "process.h"
|
||||
|
||||
namespace Sortix
|
||||
{
|
||||
|
||||
void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp,
|
||||
addr_t stackpos, addr_t entry,
|
||||
CPU::InterruptRegisters* regs)
|
||||
|
@ -37,7 +38,30 @@ namespace Sortix
|
|||
regs->edx = envc;
|
||||
regs->ecx = (size_t) envp;
|
||||
regs->eip = entry;
|
||||
regs->useresp = stackpos;
|
||||
regs->ebp = stackpos;
|
||||
regs->useresp = stackpos & ~(15UL);
|
||||
regs->ebp = regs->useresp;
|
||||
regs->cs = UCS | URPL;
|
||||
regs->ds = UDS | URPL;
|
||||
regs->ss = UDS | URPL;
|
||||
regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
}
|
||||
|
||||
void InitializeThreadRegisters(CPU::InterruptRegisters* regs,
|
||||
const sforkregs_t* requested)
|
||||
{
|
||||
Maxsi::Memory::Set(regs, 0, sizeof(*regs));
|
||||
regs->eip = requested->eip;
|
||||
regs->useresp = requested->esp;
|
||||
regs->eax = requested->eax;
|
||||
regs->ebx = requested->ebx;
|
||||
regs->ecx = requested->ecx;
|
||||
regs->edx = requested->edx;
|
||||
regs->edi = requested->edi;
|
||||
regs->esi = requested->esi;
|
||||
regs->ebp = requested->ebp;
|
||||
regs->cs = UCS | URPL;
|
||||
regs->ds = UDS | URPL;
|
||||
regs->ss = UDS | URPL;
|
||||
regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,129 +14,84 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
syscall.s
|
||||
An assembly stub that acts as glue for system calls.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
.global syscall_handler
|
||||
.global resume_syscall
|
||||
|
||||
.section .text
|
||||
.type syscall_handler, @function
|
||||
syscall_handler:
|
||||
cli
|
||||
# The processor disabled interrupts during the int $0x80 instruction,
|
||||
# however Sortix system calls runs with interrupts enabled such that they
|
||||
# can be pre-empted.
|
||||
sti
|
||||
|
||||
# Compabillity with InterruptRegisters.
|
||||
pushl $0x0
|
||||
pushl $0x80
|
||||
|
||||
# Push eax, ecx, edx, ebx, esp, ebp, esi, edi
|
||||
pushal
|
||||
|
||||
# Push the user-space data segment.
|
||||
movl %ds, %ebp
|
||||
movl $0, global_errno # Reset errno
|
||||
pushl %ebp
|
||||
|
||||
# Load the kernel data segment.
|
||||
# Grant ourselves kernel permissions to the data segment.
|
||||
movl %ds, %ebp
|
||||
pushl %ebp
|
||||
movw $0x10, %bp
|
||||
movl %ebp, %ds
|
||||
movl %ebp, %es
|
||||
movl %ebp, %fs
|
||||
movl %ebp, %gs
|
||||
|
||||
# Compabillity with InterruptRegisters.
|
||||
movl %cr2, %ebp
|
||||
pushl %ebp
|
||||
|
||||
# Store the state structure's pointer so the call can modify it if needed.
|
||||
mov %esp, syscall_state_ptr
|
||||
|
||||
# By default, assume the system call was complete.
|
||||
movl $0, system_was_incomplete
|
||||
|
||||
# Reset the kernel errno.
|
||||
movl $0, global_errno
|
||||
|
||||
# Make sure the requested system call is valid.
|
||||
cmp SYSCALL_MAX, %eax
|
||||
jb valid_eax
|
||||
xorl %eax, %eax
|
||||
jae fix_syscall
|
||||
|
||||
valid_eax:
|
||||
valid_syscall:
|
||||
# Read a system call function pointer.
|
||||
xorl %ebp, %ebp
|
||||
movl syscall_list(%ebp,%eax,4), %eax
|
||||
|
||||
# Give the system call function the values given by user-space.
|
||||
# Call the system call.
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %edx
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
|
||||
# Call the system call.
|
||||
calll *%eax
|
||||
|
||||
# Clean up after the call.
|
||||
addl $20, %esp
|
||||
|
||||
# Test if the system call was incomplete
|
||||
movl system_was_incomplete, %ebx
|
||||
testl %ebx, %ebx
|
||||
|
||||
# If the system call was incomplete, the value in %eax is meaningless.
|
||||
jg return_to_userspace
|
||||
|
||||
# The system call was completed, so store the return value.
|
||||
movl %eax, 36(%esp)
|
||||
|
||||
# Don't forget to update userspace's errno value.
|
||||
call update_userspace_errno
|
||||
|
||||
return_to_userspace:
|
||||
# Compabillity with InterruptRegisters.
|
||||
addl $4, %esp
|
||||
|
||||
# Restore the user-space data segment.
|
||||
# Restore the previous permissions to data segment.
|
||||
popl %ebp
|
||||
movl %ebp, %ds
|
||||
movl %ebp, %es
|
||||
movl %ebp, %fs
|
||||
movl %ebp, %gs
|
||||
|
||||
popal
|
||||
# Return to user-space, system call result in %eax, errno in %edx.
|
||||
popl %ebp
|
||||
movl global_errno, %edx
|
||||
|
||||
# Compabillity with InterruptRegisters.
|
||||
addl $8, %esp
|
||||
# If any signals are pending, fire them now.
|
||||
movl asm_signal_is_pending, %ecx
|
||||
testl %ecx, %ecx
|
||||
jnz call_signal_dispatcher
|
||||
|
||||
# Return to user-space.
|
||||
iretl
|
||||
|
||||
.type resume_syscall, @function
|
||||
resume_syscall:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
movl 8(%esp), %eax
|
||||
movl 16(%esp), %ecx
|
||||
|
||||
pushl 28(%ecx)
|
||||
pushl 24(%ecx)
|
||||
pushl 20(%ecx)
|
||||
pushl 16(%ecx)
|
||||
pushl 12(%ecx)
|
||||
pushl 8(%ecx)
|
||||
pushl 4(%ecx)
|
||||
pushl 0(%ecx)
|
||||
|
||||
call *%eax
|
||||
|
||||
addl $32, %esp
|
||||
|
||||
leavel
|
||||
retl
|
||||
fix_syscall:
|
||||
# Call the null system call instead.
|
||||
xorl %eax, %eax
|
||||
jmp valid_syscall
|
||||
|
||||
call_signal_dispatcher:
|
||||
# We can't return to this location after the signal, since if any system
|
||||
# call is made this stack will get reused and all our nice temporaries wil
|
||||
# be garbage. We therefore pass the kernel the state to return to and it'll
|
||||
# handle it for us when the signal is over.
|
||||
movl %esp, %ecx
|
||||
int $130 # Deliver pending signals.
|
||||
# If we end up here, it means that the signal didn't override anything and
|
||||
# that we should just go ahead and return to userspace ourselves.
|
||||
iretl
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
thread.cpp
|
||||
x86 specific parts of thread.cpp.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include "thread.h"
|
||||
|
@ -42,6 +42,8 @@ namespace Sortix
|
|||
registers.ds = src->ds;
|
||||
registers.ss = src->ss;
|
||||
registers.eflags = src->eflags;
|
||||
registers.kerrno = src->kerrno;
|
||||
registers.signal_pending = src->signal_pending;
|
||||
}
|
||||
|
||||
void Thread::LoadRegisters(CPU::InterruptRegisters* dest)
|
||||
|
@ -59,57 +61,80 @@ namespace Sortix
|
|||
dest->ds = registers.ds;
|
||||
dest->ss = registers.ss;
|
||||
dest->eflags = registers.eflags;
|
||||
dest->kerrno = registers.kerrno;
|
||||
dest->signal_pending = registers.signal_pending;
|
||||
}
|
||||
|
||||
const size_t FLAGS_CARRY = (1<<0);
|
||||
const size_t FLAGS_RESERVED1 = (1<<1); /* read as one */
|
||||
const size_t FLAGS_PARITY = (1<<2);
|
||||
const size_t FLAGS_RESERVED2 = (1<<3);
|
||||
const size_t FLAGS_AUX = (1<<4);
|
||||
const size_t FLAGS_RESERVED3 = (1<<5);
|
||||
const size_t FLAGS_ZERO = (1<<6);
|
||||
const size_t FLAGS_SIGN = (1<<7);
|
||||
const size_t FLAGS_TRAP = (1<<8);
|
||||
const size_t FLAGS_INTERRUPT = (1<<9);
|
||||
const size_t FLAGS_DIRECTION = (1<<10);
|
||||
const size_t FLAGS_OVERFLOW = (1<<11);
|
||||
const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13);
|
||||
const size_t FLAGS_NESTEDTASK = (1<<14);
|
||||
const size_t FLAGS_RESERVED4 = (1<<15);
|
||||
const size_t FLAGS_RESUME = (1<<16);
|
||||
const size_t FLAGS_VIRTUAL8086 = (1<<17);
|
||||
const size_t FLAGS_ALIGNCHECK = (1<<18);
|
||||
const size_t FLAGS_VIRTINTR = (1<<19);
|
||||
const size_t FLAGS_VIRTINTRPEND = (1<<20);
|
||||
const size_t FLAGS_ID = (1<<21);
|
||||
extern "C" void asm_call_BootstrapKernelThread(void);
|
||||
|
||||
void CreateThreadCPU(Thread* thread, addr_t entry)
|
||||
void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
|
||||
void* user, addr_t stack, size_t stacksize)
|
||||
{
|
||||
const uint32_t CS = 0x18;
|
||||
const uint32_t DS = 0x20;
|
||||
const uint32_t RPL = 0x3;
|
||||
// Instead of directly calling the desired entry point, we call a small
|
||||
// stub that calls it for us and then destroys the kernel thread if
|
||||
// the entry function returns. Note that since we use a stack based
|
||||
// calling convention, we go through a proxy that uses %edi and %esi
|
||||
// as parameters and pushes them to the stack and then does the call.
|
||||
regs->eip = (addr_t) asm_call_BootstrapKernelThread;
|
||||
regs->useresp = stack + stacksize;
|
||||
regs->eax = 0;
|
||||
regs->ebx = 0;
|
||||
regs->ecx = 0;
|
||||
regs->edx = 0;
|
||||
regs->edi = (addr_t) user;
|
||||
regs->esi = (addr_t) entry;
|
||||
regs->ebp = 0;
|
||||
regs->cs = KCS | KRPL;
|
||||
regs->ds = KDS | KRPL;
|
||||
regs->ss = KDS | KRPL;
|
||||
regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
regs->kerrno = 0;
|
||||
regs->signal_pending = 0;
|
||||
}
|
||||
|
||||
CPU::InterruptRegisters regs;
|
||||
regs.eip = entry;
|
||||
regs.useresp = thread->stackpos + thread->stacksize;
|
||||
regs.eax = 0;
|
||||
regs.ebx = 0;
|
||||
regs.ecx = 0;
|
||||
regs.edx = 0;
|
||||
regs.edi = 0;
|
||||
regs.esi = 0;
|
||||
regs.ebp = thread->stackpos + thread->stacksize;
|
||||
regs.cs = CS | RPL;
|
||||
regs.ds = DS | RPL;
|
||||
regs.ss = DS | RPL;
|
||||
regs.eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
|
||||
thread->SaveRegisters(®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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
/*******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
|
@ -14,13 +14,13 @@
|
|||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
x86.h
|
||||
CPU stuff for the x86 platform.
|
||||
|
||||
******************************************************************************/
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_X86_H
|
||||
#define SORTIX_X86_H
|
||||
|
@ -33,7 +33,7 @@ namespace Sortix
|
|||
{
|
||||
struct InterruptRegisters
|
||||
{
|
||||
uint32_t cr2;
|
||||
uint32_t signal_pending, kerrno, cr2;
|
||||
uint32_t ds; // Data segment selector
|
||||
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.
|
||||
uint32_t int_no, err_code; // Interrupt number and error code (if applicable)
|
||||
|
@ -41,18 +41,17 @@ namespace Sortix
|
|||
|
||||
public:
|
||||
void LogRegisters() const;
|
||||
bool InUserspace() const { return (cs & 0x3) != 0; }
|
||||
|
||||
};
|
||||
|
||||
struct SyscallRegisters
|
||||
{
|
||||
uint32_t cr2; // For compabillity with above, may be removed soon.
|
||||
uint32_t ds;
|
||||
uint32_t di, si, bp, trash, b, d, c; union { uint32_t a; uint32_t result; };
|
||||
uint32_t int_no, err_code; // Also compabillity.
|
||||
uint32_t ip, cs, flags, sp, ss;
|
||||
};
|
||||
}
|
||||
|
||||
const uint64_t KCS = 0x08;
|
||||
const uint64_t KDS = 0x10;
|
||||
const uint64_t KRPL = 0x0;
|
||||
const uint64_t UCS = 0x18;
|
||||
const uint64_t UDS = 0x20;
|
||||
const uint64_t URPL = 0x3;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue