Chapter 30
(X86)Simple Process: liboskit_sproc.a

30.1 Introduction

The Simple Process Library (“sproc”) provides a way to implement simple 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 28 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.

30.2 Example

A sample kernel in the 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.

30.3 Requirements

In order to use the sproc library, the kernel implementor must do the following:

30.4 System Calls

The library uses the int 0x60 instruction for implementing system calls on the x86 architecture. 0x60 is defined as SYSCALL_INT in <oskit/machine/sproc.h>.

30.4.1 System call implementation

System call implementing functions (Syscall function for short) have a type oskit_sproc_syscall_func defined as follows in <oskit/sproc.h> .

typedef int (*oskit_sproc_syscall_func)(struct oskit_sproc_thread *sth, void *arg, int rval[2]);

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 30.4.2 for how system call errors are handled.

The passed sth parameter conveys the context information of the thread that issued the system call. The 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 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.

30.4.2 System call 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.
  1. Sets %EAX to the system call number.
  2. Does int 0x60
  3. 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.

A sample syscall stub can be seen in examples/x86/sproc/user_syscall.S.

30.5 API reference

30.5.1 oskit_sproc_init: Initialize the Simple Process Library

SYNOPSIS

#include <oskit/sproc.h>

void oskit_sproc_init(void);

DESCRIPTION

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

30.5.2 oskit_sproc_create: Create a process

SYNOPSIS

#include <oskit/sproc.h>

oskit_error_t oskit_sproc_create( const struct oskit_sproc_desc *desc, oskit_size_t size, [out] struct oskit_sproc *outproc);

DESCRIPTION

Create a process. This API creates a virtual address space and binds it to the specified process description.

PARAMETERS
desc:
The process description structure.
size:
Process size in bytes. The created process resides at the virtual address [0x40000000..0x40000000 + size - 1].
outproc:
The created process structure. Note that oskit_sproc_create does not allocate memory for this structure. The memory for this structure should be allocated by caller in advance.
RETURNS

Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.

RELATED INFORMATION

oskit_sproc_destroy

30.5.3 oskit_sproc_destroy: Destroy a process

SYNOPSIS

#include <oskit/sproc.h>

oskit_error_t oskit_sproc_destroy(struct oskit_sproc *proc);

DESCRIPTION

Destroy a process. The virtual address space bound to the process is discarded.

PARAMETERS
proc:
The process to destroy.
RETURNS

Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.

30.5.4 oskit_sproc_stack_alloc: Allocate a redzone protected stack

SYNOPSIS

#include <oskit/sproc.h>

oskit_error_t oskit_sproc_stack_alloc( struct oskit_sproc *sproc, [in/out] oskit_addr_t *base, oskit_size_t size, oskit_size_t redzonesize, [out] struct oskit_sproc_stack *out_stack);

DESCRIPTION

Allocate a stack within a process’s user address space, that can be used for oskit_sproc_switch later. Information about the created stack is stored in the oskit_sproc_stack structure.

PARAMETERS
sproc:
The process in which the stack is allocated.
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.
size:
Stack size. Must be a multiple of the page size.
redzonesize:
Redzone size. Must be a multiple of the page size (or zero).
out_stack:
The created stack information. Note that the memory for this structure must be allocated by the caller in advance.
RETURNS

Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.

RELATED INFORMATION

oskit_sproc_stack_push

30.5.5 oskit_sproc_stack_push: push arguments onto a stack

SYNOPSIS

#include <oskit/sproc.h>

oskit_error_t oskit_sproc_stack_push(struct oskit_sproc_stack *stack, void *arg, oskit_size_t argsize);

DESCRIPTION

Push parameters onto a stack allocated by 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.

PARAMETERS
stack:
The stack structure obtained by oskit_sproc_stack_alloc.
arg:
A pointer to the parameter.
argsize:
The size of the parameter in bytes.
RETURNS

Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.

RELATED INFORMATION

oskit_sproc_stack_alloc

30.5.6 oskit_sproc_switch: Switch to user mode

SYNOPSIS

#include <oskit/sproc.h>

void oskit_sproc_switch(struct oskit_sproc *proc, oskit_addr_t entry, struct oskit_sproc_stack *stack);

DESCRIPTION

Switch the calling thread to user mode and let it execute from the specified address 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 exitlike system call. Refer to the description of 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.

PARAMETERS
proc:
The process to be called.
entry:
The entry address of the user’s program.
stack:
The stack that will be used for the thread.
RELATED INFORMATION

oskit_sproc_stack_alloc, oskit_sproc_stack_push OSKIT_SPROC_RETURN

30.5.7 OSKIT_SPROC_RETURN: Return to the kernel mode

SYNOPSIS

#include <oskit/sproc.h>

         OSKIT_SPROC_RETURN(struct oskit_sproc_thread *sth, int code)
         
DESCRIPTION

This is a macro to be used in an exit-like syscall function to terminate the calling thread run in the user program. This macro does not return. This macro returns control to oskit_sproc_switch.

PARAMETERS
sth:
The context information passed as a parameter from the system call trap handler.
code:
This parameter is currently not used.

30.5.8 oskit_sproc_load_elf: Map an ELF executable file

SYNOPSIS

#include <oskit/sproc.h>

int oskit_sproc_load_elf(struct oskit_sproc *proc, const char *file, [out] exec_info_t *info_out);

DESCRIPTION

Map an ELF executable file onto a user’s address space. This function uses the exec_load function internally (See Section 35). 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.

PARAMETERS
proc:
The process to be mapped.
file:
The filename of the executable.
info_out:
The information returned from exec_load.
RETURNS

Returns the value returned from exec_load.