mirror of https://github.com/dzavalishin/oskit/
425 lines
15 KiB
TeX
Executable File
425 lines
15 KiB
TeX
Executable File
%
|
|
% Copyright (c) 2001, 2002 University of Utah and the Flux Group.
|
|
% All rights reserved.
|
|
%
|
|
% The University of Utah grants you the right to copy and reproduce this
|
|
% document or portions thereof for academic, research, evaluation, and
|
|
% personal use only, provided that (1) the title page appears prominently,
|
|
% and (2) these copyright and permission notices are retained in all copies.
|
|
% To arrange for alternate terms, contact the University of Utah at
|
|
% csl-dist@cs.utah.edu or +1-801-585-3271.
|
|
%
|
|
\label{sproc}
|
|
|
|
\section{Introduction}
|
|
|
|
The Simple Process Library (``sproc'') provides a way to implement simple
|
|
\emph{processes} that run in user mode and which have separate address spaces.
|
|
Multiple threads can run in a single process simultaneously.
|
|
The library also provides a mechanism for defining system calls allowing
|
|
process threads to request privileged services.
|
|
The sproc library uses the NetBSD UVM library (See Section~\ref{netbsd-uvm}
|
|
to implement address spaces.
|
|
Currently the sproc library is only implemented on the x86.
|
|
|
|
Note that this is only a first attempt at a process library.
|
|
Hopefully, this library will be improved in the future.
|
|
|
|
\section{Example}
|
|
|
|
A sample kernel in the \texttt{examples/x86/sproc} directory demonstrates
|
|
the use of the Simple Process Library. That directory contains a sample
|
|
kernel and sample user programs that run on the kernel.
|
|
|
|
\section{Requirements}
|
|
|
|
In order to use the sproc library, the kernel implementor must do the
|
|
following:
|
|
|
|
\begin{itemize}
|
|
\item Write system call implementation functions. See
|
|
Section~\ref{sproc-syscall} for details about system calls.
|
|
|
|
\item Make an array of \texttt{oskit_sproc_sysent} structures.
|
|
Each element of the array contains information about a single
|
|
system call. The index of the array will be the system call
|
|
number. This array is used as the system call dispatch table.
|
|
|
|
\cstruct{oskit_sproc_sysent}{
|
|
oskit_sproc_syscall_func entry; /* pointer to a system call function */
|
|
int nargs; /* \# of args in sizeof(int) */
|
|
};
|
|
|
|
\item Create an \texttt{oskit_sproc_desc} process description
|
|
structure which is defined as:
|
|
|
|
\begin{verbatim}
|
|
/* process description */
|
|
struct oskit_sproc_desc {
|
|
/* maximum number of system call number */
|
|
int sd_nsyscall;
|
|
|
|
/* system call table */
|
|
struct oskit_sproc_sysent *sd_syscalltab;
|
|
|
|
/* trap handler that captures all traps while executing user mode code */
|
|
int (*sd_handler)(struct oskit_sproc_thread *sth,
|
|
int signo, int code,
|
|
struct trap_state *frame);
|
|
};
|
|
\end{verbatim}
|
|
|
|
This structure is bound to a \emph{process} to deal with all traps
|
|
such as system calls, protection or page faults. This
|
|
structure is passed to \texttt{oskit_sproc_create} when
|
|
creating a process.
|
|
|
|
\texttt{sd_syscalltab} is a pointer to the system
|
|
call dispatch table described above. The size of the table
|
|
should be stored in \texttt{sd_nsyscall}. \texttt{sd_handler}
|
|
is a pointer to a handler function that is called when a trap
|
|
is received while executing user mode code. The parameters
|
|
passed to the handler are:
|
|
\begin{description}
|
|
\item[sth] the context information of the thread that caused
|
|
the trap. The \texttt{oskit_sproc_thread} structure is defined
|
|
as follows:
|
|
|
|
\begin{verbatim}
|
|
/* kernel thread definition */
|
|
struct oskit_sproc_thread {
|
|
queue_chain_t st_thread_chain;
|
|
struct oskit_sproc *st_process;
|
|
oskit_addr_t st_entry;
|
|
jmp_buf st_context;
|
|
|
|
struct oskit_sproc_stack *st_stack;
|
|
/* machine dependent part */
|
|
struct oskit_sproc_machdep st_machdep;
|
|
};
|
|
\end{verbatim}
|
|
|
|
\item[signo] The pseudo signal number such as SIGSEGV. This is
|
|
a hint only.
|
|
|
|
\item[code] The architecture dependent trap code. On the x86
|
|
architecture, this number is a hardware trap number.
|
|
|
|
\item[frame] The architecture specific trap frame.
|
|
See Section~\ref{trap-state}.
|
|
|
|
\end{description}
|
|
|
|
\end{itemize}
|
|
|
|
\section{System Calls}
|
|
The library uses the \texttt{int 0x60} instruction for implementing
|
|
system calls on the x86 architecture. 0x60 is defined as
|
|
\texttt{SYSCALL_INT} in $<$oskit/machine/sproc.h$>$.
|
|
|
|
\subsection{System call implementation}
|
|
\label{sproc-syscall}
|
|
|
|
System call implementing functions (\emph{Syscall function} for short)
|
|
have a type \texttt{oskit_sproc_syscall_func} defined as follows in
|
|
$<$oskit/sproc.h$>$ .
|
|
|
|
\begin{quote}
|
|
{\tt typedef int (*\csymbol{oskit_sproc_syscall_func})(struct
|
|
oskit_sproc_thread *sth, void *arg, int rval[2]);}
|
|
\end{quote}
|
|
|
|
Syscall functions are called from the system call trap handler
|
|
provided by the library. A system call can either succeed or fail.
|
|
On success, the syscall function must return 0. On an error, it should
|
|
return a non-zero error number. Please refer to Section~\ref{sproc-stub} for
|
|
how system call errors are handled.
|
|
|
|
The passed \texttt{sth} parameter conveys the context information of the thread
|
|
that issued the system call. The {\tt arg} parameter is a pointer to the first
|
|
of the system call arguments, which were already copied to the kernel memory
|
|
space by the system call trap handler.
|
|
|
|
The return value from the system call can be either 32 or 64 bits
|
|
wide and is returned using {\tt rval}. The return value is meaningful only
|
|
when the system call succeeded. On the x86 architecture, the default
|
|
implementation use \%EAX for rval[0] (and \%EDX for rval[1] if 64bit)
|
|
to return the value. System call stubs will get the value from these
|
|
registers.
|
|
|
|
\subsection{System call stub}
|
|
\label{sproc-stub}
|
|
A system call stub is a small function placed in the user program to invoke
|
|
a system call. Below is what a typical stub function does.
|
|
|
|
\begin{enumerate}
|
|
\item Sets \%EAX to the system call number.
|
|
|
|
\item Does \texttt{int 0x60}
|
|
|
|
\item Checks the carry bit. The carry bit indicates error status.
|
|
If the carry bit is
|
|
cleared, the call succeeded.
|
|
In this case, the return value is stored in the \%EAX
|
|
register (and \%EDX if the return value is 64 bits wide). If the carry
|
|
bit is set, the call failed. The error number is stored in the \%EAX
|
|
register.
|
|
\end{enumerate}
|
|
|
|
A sample syscall stub
|
|
can be seen in \texttt{examples/x86/sproc/user_syscall.S}.
|
|
|
|
\section{API reference}
|
|
|
|
\api{oskit_sproc_init}{Initialize the Simple Process Library}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\funcproto void oskit_sproc_init(void);
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
Initialize the Simple Process Library. Must be called after
|
|
the UVM library is initialized.
|
|
|
|
On the x86 architecture, this function initializes the system call
|
|
trap vector by calling \texttt{gate_init}, setting up two GDTs:
|
|
USER_CS for a user process's code segment and USER_DS for its data
|
|
segment. Also this function installs two hook functions: the UVM
|
|
library's context switch hook to keep track of the currently
|
|
executing thread and a signal hook to catch signals generated
|
|
by the processor.
|
|
\end{apidesc}
|
|
|
|
\api{oskit_sproc_create}{Create a process}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\funcproto oskit_error_t oskit_sproc_create(
|
|
const~struct~oskit_sproc_desc *desc,
|
|
oskit_size_t size,
|
|
\outparam struct~oskit_sproc *outproc);
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
Create a process.
|
|
This API creates a virtual address space and binds it to the specified
|
|
process description.
|
|
\end{apidesc}
|
|
\begin{apiparm}
|
|
\item[desc]
|
|
The process description structure.
|
|
|
|
\item[size]
|
|
Process size in bytes. The created process resides at
|
|
the virtual address [$0x40000000 .. 0x40000000+size-1$].
|
|
|
|
\item[outproc]
|
|
The created process structure. Note that
|
|
\texttt{oskit_sproc_create} does not allocate memory
|
|
for this structure. The memory for this structure
|
|
should be allocated by caller in advance.
|
|
|
|
\end{apiparm}
|
|
\begin{apiret}
|
|
Returns 0 on success, or an error code specified in
|
|
\cinclude{oskit/errno.h}, on error.
|
|
\end{apiret}
|
|
\begin{apirel}
|
|
{\tt oskit_sproc_destroy}
|
|
\end{apirel}
|
|
|
|
\api{oskit_sproc_destroy}{Destroy a process}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\funcproto oskit_error_t oskit_sproc_destroy(struct~oskit_sproc *proc);
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
Destroy a process. The virtual address space bound to the process
|
|
is discarded.
|
|
\end{apidesc}
|
|
\begin{apiparm}
|
|
\item[proc]
|
|
The process to destroy.
|
|
\end{apiparm}
|
|
\begin{apiret}
|
|
Returns 0 on success, or an error code specified in
|
|
\cinclude{oskit/errno.h}, on error.
|
|
\end{apiret}
|
|
|
|
\api{oskit_sproc_stack_alloc}{Allocate a redzone protected stack}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\funcproto oskit_error_t oskit_sproc_stack_alloc(
|
|
struct~oskit_sproc *sproc,
|
|
\inoutparam oskit_addr_t *base,
|
|
oskit_size_t size,
|
|
oskit_size_t redzonesize,
|
|
\outparam struct~oskit_sproc_stack *out_stack);
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
Allocate a stack within a process's user address space,
|
|
that can be used for \texttt{oskit_sproc_switch} later.
|
|
Information about the created stack is stored in the
|
|
\texttt{oskit_sproc_stack} structure.
|
|
|
|
\end{apidesc}
|
|
\begin{apiparm}
|
|
\item[sproc]
|
|
The process in which the stack is allocated.
|
|
|
|
\item[base]
|
|
On entry, the value pointed to by this parameter
|
|
is the preferred virtual address for the
|
|
stack start address. Must be a multiple of the
|
|
page size. This is a hint only and can be specified
|
|
as zero. On return, it contains the start address of the
|
|
stack.
|
|
|
|
\item[size]
|
|
Stack size. Must be a multiple of the page size.
|
|
|
|
\item[redzonesize]
|
|
Redzone size. Must be a multiple of the page size (or zero).
|
|
|
|
\item[out_stack]
|
|
The created stack information. Note that the memory for
|
|
this structure must be allocated by the caller in advance.
|
|
|
|
\end{apiparm}
|
|
\begin{apiret}
|
|
Returns 0 on success, or an error code specified in
|
|
\cinclude{oskit/errno.h}, on error.
|
|
\end{apiret}
|
|
\begin{apirel}
|
|
\texttt{oskit_sproc_stack_push}
|
|
\end{apirel}
|
|
|
|
\api{oskit_sproc_stack_push}{push arguments onto a stack}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\funcproto oskit_error_t oskit_sproc_stack_push(struct~oskit_sproc_stack *stack,
|
|
void *arg, oskit_size_t argsize);
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
Push parameters onto a stack allocated by
|
|
\texttt{oskit_sproc_stack_alloc}. This function
|
|
can be called multiple times to stack more parameters.
|
|
The stacked parameters will be passed to a user program.
|
|
\end{apidesc}
|
|
\begin{apiparm}
|
|
\item[stack]
|
|
The stack structure obtained by \texttt{oskit_sproc_stack_alloc}.
|
|
|
|
\item[arg]
|
|
A pointer to the parameter.
|
|
|
|
\item[argsize]
|
|
The size of the parameter in bytes.
|
|
|
|
\end{apiparm}
|
|
\begin{apiret}
|
|
Returns 0 on success, or an error code specified in
|
|
\cinclude{oskit/errno.h}, on error.
|
|
\end{apiret}
|
|
\begin{apirel}
|
|
{\tt oskit_sproc_stack_alloc}
|
|
\end{apirel}
|
|
|
|
\api{oskit_sproc_switch}{Switch to user mode}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\funcproto void oskit_sproc_switch(struct~oskit_sproc *proc, oskit_addr_t entry,
|
|
struct~oskit_sproc_stack *stack);
|
|
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
Switch the calling thread to user mode and let it
|
|
execute from the specified address \texttt{entry}. The stack pointer
|
|
is changed to the specified stack. Multiple threads can be
|
|
executed within a single process. This function does not
|
|
return until the user mode code executes an \texttt{exit}\-like
|
|
system call. Refer to the description of
|
|
\texttt{OSKIT_SPROC_RETURN} for more information.
|
|
|
|
When the thread starts execution in user mode, \%CS is set to
|
|
USER_CS and \%DS, \%ES are set to USER_DS. \%FS and \%GS are
|
|
set to zero. \%ESP points the first parameter on the stack.
|
|
The frame pointer (\%EBP) is set to zero.
|
|
|
|
|
|
\end{apidesc}
|
|
\begin{apiparm}
|
|
\item[proc]
|
|
The process to be called.
|
|
|
|
\item[entry]
|
|
The entry address of the user's program.
|
|
|
|
\item[stack]
|
|
The stack that will be used for the thread.
|
|
|
|
\end{apiparm}
|
|
\begin{apirel}
|
|
{\tt oskit_sproc_stack_alloc}, {\tt oskit_sproc_stack_push}
|
|
{\tt OSKIT_SPROC_RETURN}
|
|
\end{apirel}
|
|
|
|
\api{OSKIT_SPROC_RETURN}{Return to the kernel mode}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\begin{verbatim}
|
|
OSKIT_SPROC_RETURN(struct oskit_sproc_thread *sth, int code)
|
|
\end{verbatim}
|
|
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
|
|
This is a macro to be used in an \texttt{exit}-like syscall
|
|
function to terminate the calling thread run in the user
|
|
program. This macro does not return. This macro returns
|
|
control to \texttt{oskit_sproc_switch}.
|
|
|
|
\end{apidesc}
|
|
\begin{apiparm}
|
|
\item[sth]
|
|
The context information passed as a parameter from
|
|
the system call trap handler.
|
|
\item[code]
|
|
This parameter is currently not used.
|
|
\end{apiparm}
|
|
|
|
|
|
\api{oskit_sproc_load_elf}{Map an ELF executable file}
|
|
\begin{apisyn}
|
|
\cinclude{oskit/sproc.h}
|
|
|
|
\funcproto int oskit_sproc_load_elf(struct~oskit_sproc *proc, const char *file,
|
|
\outparam exec_info_t *info_out);
|
|
|
|
\end{apisyn}
|
|
\begin{apidesc}
|
|
|
|
Map an ELF executable file onto a user's address space. This
|
|
function uses the \texttt{exec_load} function internally (See
|
|
Section~\ref{exec}). The ELF file's header must indicate an
|
|
address range for the file within the user space address
|
|
range. The file is loaded on demand.
|
|
|
|
\end{apidesc}
|
|
\begin{apiparm}
|
|
\item[proc]
|
|
The process to be mapped.
|
|
\item[file]
|
|
The filename of the executable.
|
|
\item[info_out]
|
|
The information returned from \texttt{exec_load}.
|
|
\end{apiparm}
|
|
\begin{apiret}
|
|
Returns the value returned from \texttt{exec_load}.
|
|
\end{apiret}
|
|
|