minix/kernel/system.c
Jorrit Herder 198c976f7e System processes can be signaled; signals are transformed in SYS_EVENT message
that passes signal map along. This mechanisms is also used for nonuser signals
like SIGKMESS, SIGKSTOP, SIGKSIG.

Revised comments of many system call handlers. Renamed setpriority to nice.
2005-07-19 12:21:36 +00:00

570 lines
22 KiB
C
Executable file

/* This task handles the interface between the kernel and user-level servers.
* System services can be accessed by doing a system call. System calls are
* transformed into request messages, which are handled by this task. By
* convention, a sys_call() is transformed in a SYS_CALL request message that
* is handled in a function named do_call().
*
* A private call vector is used to map all system calls to the functions that
* handle them. The actual handler functions are contained in separate files
* to keep this file clean. The call vector is used in the system task's main
* loop to handle all incoming requests.
*
* In addition to the main sys_task() entry point, which starts the main loop,
* there are several other minor entry points:
* send_sig: send signal directly to a system process
* cause_sig: take action to cause a signal to occur via PM
* clear_proc: clean up a process in the process table, e.g. on exit
* umap_local: map virtual address in LOCAL_SEG to physical
* umap_remote: map virtual address in REMOTE_SEG to physical
* umap_bios: map virtual address in BIOS_SEG to physical
* numap_local: umap_local D segment from proc nr instead of pointer
* virtual_copy: copy bytes from one virtual address to another
* get_randomness: accumulate randomness in a buffer
* generic_handler: interrupt handler for user-level device drivers
*
* Changes:
* Apr 25, 2005 new init_proc() function (Jorrit N. Herder)
* Apr 25, 2005 made mapping of call vector explicit (Jorrit N. Herder)
* Oct 29, 2004 new clear_proc() function (Jorrit N. Herder)
* Oct 17, 2004 generic handler and IRQ policies (Jorrit N. Herder)
* Oct 10, 2004 dispatch system calls from call vector (Jorrit N. Herder)
* Sep 30, 2004 source code documentation updated (Jorrit N. Herder)
* Sep 10, 2004 system call functions in library (Jorrit N. Herder)
* 2004 to 2005 various new syscalls (see syslib.h) (Jorrit N. Herder)
*/
#include "kernel.h"
#include "system.h"
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/sigcontext.h>
#include <sys/svrctl.h>
#if (CHIP == INTEL)
#include <ibm/memory.h>
#include "protect.h"
#endif
/* Declaration of the call vector that defines the mapping of system calls
* to handler functions. The vector is initialized in sys_init() with map(),
* which makes sure the system call numbers are ok. No space is allocated,
* because the dummy is declared extern. If an illegal call is given, the
* array size will be negative and this won't compile.
*/
PUBLIC int (*call_vec[NR_SYS_CALLS])(message *m_ptr);
#define map(call_nr, handler) \
{extern int dummy[NR_SYS_CALLS > (unsigned) (call_nr) ? 1 : -1];} \
call_vec[(call_nr)] = (handler)
FORWARD _PROTOTYPE( void initialize, (void));
/*===========================================================================*
* sys_task *
*===========================================================================*/
PUBLIC void sys_task()
{
/* Main entry point of sys_task. Get the message and dispatch on type. */
static message m;
register int result;
/* Initialize the system task. */
initialize();
while (TRUE) {
/* Get work. */
receive(ANY, &m);
/* Handle the request. */
if ((unsigned) m.m_type < NR_SYS_CALLS) {
result = (*call_vec[m.m_type])(&m); /* handle the kernel call */
} else {
kprintf("Warning, illegal SYSTASK request from %d.\n", m.m_source);
result = EBADREQUEST; /* illegal message type */
}
/* Send a reply, unless inhibited by a handler function. Use the kernel
* function lock_send() to prevent a system call trap. The destination
* is known to be blocked waiting for a message.
*/
if (result != EDONTREPLY) {
m.m_type = result; /* report status of call */
if (OK != lock_send(m.m_source, &m)) {
kprintf("Warning, SYSTASK couldn't reply to request from %d\n",
m.m_source);
}
}
}
}
/*===========================================================================*
* initialize *
*===========================================================================*/
PRIVATE void initialize(void)
{
register struct priv *sp;
int i;
/* Initialize IRQ handler hooks. Mark all hooks available. */
for (i=0; i<NR_IRQ_HOOKS; i++) {
irq_hooks[i].proc_nr = NONE;
}
/* Initialize all alarm timers for all processes. */
for (sp=BEG_PRIV_ADDR; sp < END_PRIV_ADDR; sp++) {
tmr_inittimer(&(sp->s_alarm_timer));
}
/* Initialize the call vector to a safe default handler. Some system calls
* may be disabled or nonexistant. Then explicitely map known calls to their
* handler functions. This is done with a macro that gives a compile error
* if an illegal call number is used. The ordering is not important here.
*/
for (i=0; i<NR_SYS_CALLS; i++) {
call_vec[i] = do_unused;
}
/* Process management. */
map(SYS_FORK, do_fork); /* a process forked a new process */
map(SYS_NEWMAP, do_newmap); /* set up a process memory map */
map(SYS_EXEC, do_exec); /* update process after execute */
map(SYS_EXIT, do_exit); /* clean up after process exit */
map(SYS_NICE, do_nice); /* set scheduling priority */
map(SYS_TRACE, do_trace); /* request a trace operation */
/* Signal handling. */
map(SYS_KILL, do_kill); /* cause a process to be signaled */
map(SYS_GETKSIG, do_getksig); /* PM checks for pending signals */
map(SYS_ENDKSIG, do_endksig); /* PM finished processing signal */
map(SYS_SIGSEND, do_sigsend); /* start POSIX-style signal */
map(SYS_SIGRETURN, do_sigreturn); /* return from POSIX-style signal */
/* Clock functionality. */
map(SYS_TIMES, do_times); /* get uptime and process times */
map(SYS_SETALARM, do_setalarm); /* schedule a synchronous alarm */
/* Device I/O. */
map(SYS_IRQCTL, do_irqctl); /* interrupt control operations */
map(SYS_DEVIO, do_devio); /* inb, inw, inl, outb, outw, outl */
map(SYS_SDEVIO, do_sdevio); /* phys_insb, _insw, _outsb, _outsw */
map(SYS_VDEVIO, do_vdevio); /* vector with devio requests */
/* System control. */
map(SYS_ABORT, do_abort); /* abort MINIX */
map(SYS_GETINFO, do_getinfo); /* request system information */
map(SYS_SEGCTL, do_segctl); /* add segment and get selector */
map(SYS_SVRCTL, do_svrctl); /* kernel control functions */
/* Copying. */
map(SYS_UMAP, do_umap); /* map virtual to physical address */
map(SYS_VIRCOPY, do_vircopy); /* use pure virtual addressing */
map(SYS_PHYSCOPY, do_physcopy); /* use physical addressing */
map(SYS_VIRVCOPY, do_virvcopy); /* vector with copy requests */
map(SYS_PHYSVCOPY, do_physvcopy); /* vector with copy requests */
map(SYS_MEMSET, do_memset); /* write char to memory area */
}
/*===========================================================================*
* init_proc *
*===========================================================================*/
PUBLIC int init_proc(proc_nr, proto_nr)
int proc_nr; /* slot of process to initialize */
int proto_nr; /* prototype process to copy from */
{
register struct proc *rc, *rp;
register struct priv *sp;
int i;
/* Get a pointer to the process to initialize. */
rc = proc_addr(proc_nr);
/* If there is a prototype process to initialize from, use it. Otherwise,
* assume the caller will take care of initialization, but make sure that
* the new process gets a pointer to a system properties structure.
*/
if (isokprocn(proto_nr)) {
kprintf("INIT proc from prototype %d\n", proto_nr);
} else {
for (sp = BEG_PRIV_ADDR, i = 0; sp < END_PRIV_ADDR; ++sp, ++i) {
if (sp->s_proc_nr == NONE) { /* found free slot */
sp->s_proc_nr = proc_nr; /* set association */
rc->p_priv = sp; /* assign to process */
return(OK);
}
}
kprintf("No free PRIV structure!\n", NO_NUM);
return(ENOSPC); /* out of resources */
}
}
/*===========================================================================*
* clear_proc *
*===========================================================================*/
PUBLIC void clear_proc(proc_nr)
int proc_nr; /* slot of process to clean up */
{
register struct proc *rp, *rc;
register struct proc **xpp; /* iterate over caller queue */
int i;
/* Get a pointer to the process that exited. */
rc = proc_addr(proc_nr);
/* Turn off any alarm timers at the clock. */
reset_timer(&rc->p_priv->s_alarm_timer);
/* Make sure the exiting process is no longer scheduled. */
if (rc->p_rts_flags == 0) lock_unready(rc);
/* If the process being terminated happens to be queued trying to send a
* message (e.g., the process was killed by a signal, rather than it doing
* an exit or it is forcibly shutdown in the stop sequence), then it must
* be removed from the message queues.
*/
if (rc->p_rts_flags & SENDING) {
/* Check all proc slots to see if the exiting process is queued. */
for (rp = BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++) {
if (rp->p_caller_q == NIL_PROC) continue;
/* Make sure that the exiting process is not on the queue. */
xpp = &rp->p_caller_q;
while (*xpp != NIL_PROC) { /* check entire queue */
if (*xpp == rc) { /* process is on the queue */
*xpp = (*xpp)->p_q_link; /* replace by next process */
break;
}
xpp = &(*xpp)->p_q_link; /* proceed to next queued */
}
}
}
/* Check the table with IRQ hooks to see if hooks should be released. */
for (i=0; i < NR_IRQ_HOOKS; i++) {
if (irq_hooks[i].proc_nr == proc_nr)
irq_hooks[i].proc_nr = NONE;
}
/* Check if there are pending notifications. Release the buffers. */
while (rc->p_ntf_q != NULL) {
i = (int) (rc->p_ntf_q - &notify_buffer[0]);
free_bit(i, notify_bitmap, NR_NOTIFY_BUFS);
#if TEMP_CODE
rc->p_ntf_q = rc->p_ntf_q->n_next;
}
#endif
/* Now it is safe to release the process table slot. If this is a system
* process, also release its privilege structure. Further cleanup is not
* needed at this point. All important fields are reinitialized when the
* slots are assigned to another, new process.
*/
rc->p_rts_flags = SLOT_FREE;
if (priv(rp)->s_flags & SYS_PROC) priv(rp)->s_proc_nr = NONE;
}
/*===========================================================================*
* get_randomness *
*===========================================================================*/
PUBLIC void get_randomness(source)
int source;
{
/* Gather random information with help of the CPU's cycle counter. Only use
* the lowest bytes because the highest bytes won't differ that much.
*/
int r_next;
unsigned long tsc_high;
/* On machines with the RDTSC (cycle counter read instruction - pentium
* and up), use that for high-resolution raw entropy gathering. Otherwise,
* use the realtime clock (tick resolution).
*
* Unfortunately this test is run-time - we don't want to bother with
* compiling different kernels for different machines..
*
* On machines without RDTSC, we use read_clock().
*/
source %= RANDOM_SOURCES;
r_next= krandom.bin[source].r_next;
if(machine.processor > 486 && 0)
read_tsc(&tsc_high, &krandom.bin[source].r_buf[r_next]);
else
krandom.bin[source].r_buf[r_next] = read_clock();
if (krandom.bin[source].r_size < RANDOM_ELEMENTS)
krandom.bin[source].r_size ++;
krandom.bin[source].r_next = (r_next + 1 ) % RANDOM_ELEMENTS;
}
/*===========================================================================*
* generic_handler *
*===========================================================================*/
PUBLIC int generic_handler(hook)
irq_hook_t *hook;
{
/* This function handles hardware interrupt in a simple and generic way. All
* interrupts are transformed into messages to a driver. The IRQ line will be
* reenabled if the policy says so.
*/
/* As a side-effect, the interrupt handler gathers random information by
* timestamping the interrupt events. This is used for /dev/random.
*/
get_randomness(hook->irq);
/* Add a bit for this interrupt to the process' pending interrupts. When
* sending the notification message, this bit map will be magically set
* as an argument.
*/
priv(proc_addr(hook->proc_nr))->s_int_pending |= (1 << hook->irq);
/* Build notification message and return. */
lock_alert(HARDWARE, hook->proc_nr);
return(hook->policy & IRQ_REENABLE);
}
/*===========================================================================*
* send_sig *
*===========================================================================*/
PUBLIC void send_sig(proc_nr, sig_nr)
int proc_nr; /* system process to be signalled */
int sig_nr; /* signal to be sent, 1 to _NSIG */
{
/* Notify a system process about a signal. This is straightforward. Simply
* set the signal that is to be delivered in the pending signals map and
* send a notification with source SYSTEM.
*/
register struct proc *rp;
rp = proc_addr(proc_nr);
sigaddset(&priv(rp)->s_sig_pending, sig_nr);
lock_alert(SYSTEM, proc_nr);
}
/*===========================================================================*
* cause_sig *
*===========================================================================*/
PUBLIC void cause_sig(proc_nr, sig_nr)
int proc_nr; /* process to be signalled */
int sig_nr; /* signal to be sent, 1 to _NSIG */
{
/* A system process wants to send a signal to a process. Examples are:
* - HARDWARE wanting to cause a SIGSEGV after a CPU exception
* - TTY wanting to cause SIGINT upon getting a DEL
* - FS wanting to cause SIGPIPE for a broken pipe
* Signals are handled by sending a message to PM. This function handles the
* signals and makes sure the PM gets them by sending a notification. The
* process being signaled is blocked while PM has not finished all signals
* for it.
* Race conditions between calls to this function and the system calls that
* process pending kernel signals cannot exist. Signal related functions are
* only called when a user process causes a CPU exception and from the kernel
* process level, which runs to completion.
*/
register struct proc *rp;
/* Check if the signal is already pending. Process it otherwise. */
rp = proc_addr(proc_nr);
if (! sigismember(&rp->p_pending, sig_nr)) {
sigaddset(&rp->p_pending, sig_nr);
if (! (rp->p_rts_flags & SIGNALED)) { /* other pending */
if (rp->p_rts_flags == 0) lock_unready(rp); /* make not ready */
rp->p_rts_flags |= SIGNALED | SIG_PENDING; /* update flags */
send_sig(PM_PROC_NR, SIGKSIG);
}
}
}
/*===========================================================================*
* umap_bios *
*===========================================================================*/
PUBLIC phys_bytes umap_bios(rp, vir_addr, bytes)
register struct proc *rp; /* pointer to proc table entry for process */
vir_bytes vir_addr; /* virtual address in BIOS segment */
vir_bytes bytes; /* # of bytes to be copied */
{
/* Calculate the physical memory address at the BIOS. Note: currently, BIOS
* address zero (the first BIOS interrupt vector) is not considered, as an
* error here, but since the physical address will be zero as well, the
* calling function will think an error occurred. This is not a problem,
* since no one uses the first BIOS interrupt vector.
*/
/* Check all acceptable ranges. */
#if DEAD_CODE /* to be replaced by proper ranges, e.g. 640 - 1 KB */
if (vir_addr >= BIOS_MEM_BEGIN && vir_addr + bytes <= BIOS_MEM_END)
return (phys_bytes) vir_addr;
else if (vir_addr >= UPPER_MEM_BEGIN && vir_addr + bytes <= UPPER_MEM_END)
return (phys_bytes) vir_addr;
#else
if (vir_addr >= BIOS_MEM_BEGIN && vir_addr + bytes <= UPPER_MEM_END)
return (phys_bytes) vir_addr;
#endif
kprintf("Warning, error in umap_bios, virtual address 0x%x\n", vir_addr);
return 0;
}
/*===========================================================================*
* umap_local *
*===========================================================================*/
PUBLIC phys_bytes umap_local(rp, seg, vir_addr, bytes)
register struct proc *rp; /* pointer to proc table entry for process */
int seg; /* T, D, or S segment */
vir_bytes vir_addr; /* virtual address in bytes within the seg */
vir_bytes bytes; /* # of bytes to be copied */
{
/* Calculate the physical memory address for a given virtual address. */
vir_clicks vc; /* the virtual address in clicks */
phys_bytes pa; /* intermediate variables as phys_bytes */
#if (CHIP == INTEL)
phys_bytes seg_base;
#endif
/* If 'seg' is D it could really be S and vice versa. T really means T.
* If the virtual address falls in the gap, it causes a problem. On the
* 8088 it is probably a legal stack reference, since "stackfaults" are
* not detected by the hardware. On 8088s, the gap is called S and
* accepted, but on other machines it is called D and rejected.
* The Atari ST behaves like the 8088 in this respect.
*/
if (bytes <= 0) return( (phys_bytes) 0);
if (vir_addr + bytes <= vir_addr) return 0; /* overflow */
vc = (vir_addr + bytes - 1) >> CLICK_SHIFT; /* last click of data */
#if (CHIP == INTEL) || (CHIP == M68000)
if (seg != T)
seg = (vc < rp->p_memmap[D].mem_vir + rp->p_memmap[D].mem_len ? D : S);
#else
if (seg != T)
seg = (vc < rp->p_memmap[S].mem_vir ? D : S);
#endif
if((vir_addr>>CLICK_SHIFT) >= rp->p_memmap[seg].mem_vir +
rp->p_memmap[seg].mem_len) return( (phys_bytes) 0 );
if(vc >= rp->p_memmap[seg].mem_vir +
rp->p_memmap[seg].mem_len) return( (phys_bytes) 0 );
#if (CHIP == INTEL)
seg_base = (phys_bytes) rp->p_memmap[seg].mem_phys;
seg_base = seg_base << CLICK_SHIFT; /* segment origin in bytes */
#endif
pa = (phys_bytes) vir_addr;
#if (CHIP != M68000)
pa -= rp->p_memmap[seg].mem_vir << CLICK_SHIFT;
return(seg_base + pa);
#endif
#if (CHIP == M68000)
pa -= (phys_bytes)rp->p_memmap[seg].mem_vir << CLICK_SHIFT;
pa += (phys_bytes)rp->p_memmap[seg].mem_phys << CLICK_SHIFT;
return(pa);
#endif
}
/*==========================================================================*
* numap_local *
*==========================================================================*/
PUBLIC phys_bytes numap_local(proc_nr, vir_addr, bytes)
int proc_nr; /* process number to be mapped */
vir_bytes vir_addr; /* virtual address in bytes within D seg */
vir_bytes bytes; /* # of bytes required in segment */
{
/* Do umap_local() starting from a process number instead of a pointer.
* This function is used by device drivers, so they need not know about the
* process table. To save time, there is no 'seg' parameter. The segment
* is always D.
*/
return(umap_local(proc_addr(proc_nr), D, vir_addr, bytes));
}
/*===========================================================================*
* umap_remote *
*===========================================================================*/
PUBLIC phys_bytes umap_remote(rp, seg, vir_addr, bytes)
register struct proc *rp; /* pointer to proc table entry for process */
int seg; /* index of remote segment */
vir_bytes vir_addr; /* virtual address in bytes within the seg */
vir_bytes bytes; /* # of bytes to be copied */
{
/* Calculate the physical memory address for a given virtual address. */
struct far_mem *fm;
if (bytes <= 0) return( (phys_bytes) 0);
if (seg < 0 || seg >= NR_REMOTE_SEGS) return( (phys_bytes) 0);
fm = &rp->p_priv->s_farmem[seg];
if (! fm->in_use) return( (phys_bytes) 0);
if (vir_addr + bytes > fm->mem_len) return( (phys_bytes) 0);
return(fm->mem_phys + (phys_bytes) vir_addr);
}
/*==========================================================================*
* virtual_copy *
*==========================================================================*/
PUBLIC int virtual_copy(src_addr, dst_addr, bytes)
struct vir_addr *src_addr; /* source virtual address */
struct vir_addr *dst_addr; /* destination virtual address */
vir_bytes bytes; /* # of bytes to copy */
{
/* Copy bytes from virtual address src_addr to virtual address dst_addr.
* Virtual addresses can be in ABS, LOCAL_SEG, REMOTE_SEG, or BIOS_SEG.
*/
struct vir_addr *vir_addr[2]; /* virtual source and destination address */
phys_bytes phys_addr[2]; /* absolute source and destination */
int seg_index;
int i;
/* Check copy count. */
if (bytes <= 0) return(EDOM);
/* Do some more checks and map virtual addresses to physical addresses. */
vir_addr[_SRC_] = src_addr;
vir_addr[_DST_] = dst_addr;
for (i=_SRC_; i<=_DST_; i++) {
/* Get physical address. */
switch((vir_addr[i]->segment & SEGMENT_TYPE)) {
case LOCAL_SEG:
seg_index = vir_addr[i]->segment & SEGMENT_INDEX;
phys_addr[i] = umap_local( proc_addr(vir_addr[i]->proc_nr),
seg_index, vir_addr[i]->offset, bytes );
break;
case REMOTE_SEG:
seg_index = vir_addr[i]->segment & SEGMENT_INDEX;
phys_addr[i] = umap_remote( proc_addr(vir_addr[i]->proc_nr),
seg_index, vir_addr[i]->offset, bytes );
break;
case BIOS_SEG:
phys_addr[i] = umap_bios( proc_addr(vir_addr[i]->proc_nr),
vir_addr[i]->offset, bytes );
break;
case PHYS_SEG:
phys_addr[i] = vir_addr[i]->offset;
break;
default:
return(EINVAL);
}
/* Check if mapping succeeded. */
if (phys_addr[i] <= 0 && vir_addr[i]->segment != PHYS_SEG)
return(EFAULT);
}
/* Now copy bytes between physical addresseses. */
phys_copy(phys_addr[_SRC_], phys_addr[_DST_], (phys_bytes) bytes);
return(OK);
}