oskit/oskit-20020317/doc/sproc.tex

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}