Minix2.0/src/kernel/system.c
/* This task handles the interface between file system and kernel as well as
* between memory manager and kernel. System services are obtained by sending
* sys_task() a message specifying what is needed. To make life easier for
* MM and FS, a library is provided with routines whose names are of the
* form sys_xxx, e.g. sys_xit sends the SYS_XIT message to sys_task. The
* message types and parameters are:
*
* SYS_FORK informs kernel that a process has forked
* SYS_NEWMAP allows MM to set up a process memory map
* SYS_GETMAP allows MM to get a process' memory map
* SYS_EXEC sets program counter and stack pointer after EXEC
* SYS_XIT informs kernel that a process has exited
* SYS_GETSP caller wants to read out some process' stack pointer
* SYS_TIMES caller wants to get accounting times for a process
* SYS_ABORT MM or FS cannot go on; abort MINIX
* SYS_FRESH start with a fresh process image during EXEC (68000 only)
* SYS_SENDSIG send a signal to a process (POSIX style)
* SYS_SIGRETURN complete POSIX-style signalling
* SYS_KILL cause a signal to be sent via MM
* SYS_ENDSIG finish up after SYS_KILL-type signal
* SYS_COPY request a block of data to be copied between processes
* SYS_VCOPY request a series of data blocks to be copied between procs
* SYS_GBOOT copies the boot parameters to a process
* SYS_MEM returns the next free chunk of physical memory
* SYS_UMAP compute the physical address for a given virtual address
* SYS_TRACE request a trace operation
*
* Message types and parameters:
*
* m_type PROC1 PROC2 PID MEM_PTR
* ------------------------------------------------------
* | SYS_FORK | parent | child | pid | |
* |------------+---------+---------+---------+---------|
* | SYS_NEWMAP | proc nr | | | map ptr |
* |------------+---------+---------+---------+---------|
* | SYS_EXEC | proc nr | traced | new sp | |
* |------------+---------+---------+---------+---------|
* | SYS_XIT | parent | exitee | | |
* |------------+---------+---------+---------+---------|
* | SYS_GETSP | proc nr | | | |
* |------------+---------+---------+---------+---------|
* | SYS_TIMES | proc nr | | buf ptr | |
* |------------+---------+---------+---------+---------|
* | SYS_ABORT | | | | |
* |------------+---------+---------+---------+---------|
* | SYS_FRESH | proc nr | data_cl | | |
* |------------+---------+---------+---------+---------|
* | SYS_GBOOT | proc nr | | | bootptr |
* |------------+---------+---------+---------+---------|
* | SYS_GETMAP | proc nr | | | map ptr |
* ------------------------------------------------------
*
* m_type m1_i1 m1_i2 m1_i3 m1_p1
* ----------------+---------+---------+---------+--------------
* | SYS_VCOPY | src p | dst p | vec siz | vc addr |
* |---------------+---------+---------+---------+-------------|
* | SYS_SENDSIG | proc nr | | | smp |
* |---------------+---------+---------+---------+-------------|
* | SYS_SIGRETURN | proc nr | | | scp |
* |---------------+---------+---------+---------+-------------|
* | SYS_ENDSIG | proc nr | | | |
* -------------------------------------------------------------
*
* m_type m2_i1 m2_i2 m2_l1 m2_l2
* ------------------------------------------------------
* | SYS_TRACE | proc_nr | request | addr | data |
* ------------------------------------------------------
*
*
* m_type m6_i1 m6_i2 m6_i3 m6_f1
* ------------------------------------------------------
* | SYS_KILL | proc_nr | sig | | |
* ------------------------------------------------------
*
*
* m_type m5_c1 m5_i1 m5_l1 m5_c2 m5_i2 m5_l2 m5_l3
* --------------------------------------------------------------------------
* | SYS_COPY |src seg|src proc|src vir|dst seg|dst proc|dst vir| byte ct |
* --------------------------------------------------------------------------
* | SYS_UMAP | seg |proc nr |vir adr| | | | byte ct |
* --------------------------------------------------------------------------
*
*
* m_type m1_i1 m1_i2 m1_i3
* |------------+----------+----------+----------
* | SYS_MEM | mem base | mem size | tot mem |
* ----------------------------------------------
*
* In addition to the main sys_task() entry point, there are 5 other minor
* entry points:
* cause_sig: take action to cause a signal to occur, sooner or later
* inform: tell MM about pending signals
* numap: umap D segment starting from process number instead of pointer
* umap: compute the physical address for a given virtual address
* alloc_segments: allocate segments for 8088 or higher processor
*/
#include "kernel.h"
#include <signal.h>
#include <unistd.h>
#include <sys/sigcontext.h>
#include <sys/ptrace.h>
#include <minix/boot.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include "proc.h"
#if (CHIP == INTEL)
#include "protect.h"
#endif
/* PSW masks. */
#define IF_MASK 0x00000200
#define IOPL_MASK 0x003000
PRIVATE message m;
FORWARD _PROTOTYPE( int do_abort, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_copy, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_exec, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_fork, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_gboot, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_getsp, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_kill, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_mem, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_newmap, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_sendsig, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_sigreturn, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_endsig, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_times, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_trace, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_umap, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_xit, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_vcopy, (message *m_ptr) );
FORWARD _PROTOTYPE( int do_getmap, (message *m_ptr) );
#if (SHADOWING == 1)
FORWARD _PROTOTYPE( int do_fresh, (message *m_ptr) );
#endif
/*===========================================================================*
* sys_task *
*===========================================================================*/
PUBLIC void sys_task()
{
/* Main entry point of sys_task. Get the message and dispatch on type. */
register int r;
while (TRUE) {
receive(ANY, &m);
switch (m.m_type) { /* which system call */
case SYS_FORK: r = do_fork(&m); break;
case SYS_NEWMAP: r = do_newmap(&m); break;
case SYS_GETMAP: r = do_getmap(&m); break;
case SYS_EXEC: r = do_exec(&m); break;
case SYS_XIT: r = do_xit(&m); break;
case SYS_GETSP: r = do_getsp(&m); break;
case SYS_TIMES: r = do_times(&m); break;
case SYS_ABORT: r = do_abort(&m); break;
#if (SHADOWING == 1)
case SYS_FRESH: r = do_fresh(&m); break;
#endif
case SYS_SENDSIG: r = do_sendsig(&m); break;
case SYS_SIGRETURN: r = do_sigreturn(&m); break;
case SYS_KILL: r = do_kill(&m); break;
case SYS_ENDSIG: r = do_endsig(&m); break;
case SYS_COPY: r = do_copy(&m); break;
case SYS_VCOPY: r = do_vcopy(&m); break;
case SYS_GBOOT: r = do_gboot(&m); break;
case SYS_MEM: r = do_mem(&m); break;
case SYS_UMAP: r = do_umap(&m); break;
case SYS_TRACE: r = do_trace(&m); break;
default: r = E_BAD_FCN;
}
m.m_type = r; /* 'r' reports status of call */
send(m.m_source, &m); /* send reply to caller */
}
}
/*===========================================================================*
* do_fork *
*===========================================================================*/
PRIVATE int do_fork(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_fork(). m_ptr->PROC1 has forked. The child is m_ptr->PROC2. */
#if (CHIP == INTEL)
reg_t old_ldt_sel;
#endif
register struct proc *rpc;
struct proc *rpp;
if (!isoksusern(m_ptr->PROC1) || !isoksusern(m_ptr->PROC2))
return(E_BAD_PROC);
rpp = proc_addr(m_ptr->PROC1);
rpc = proc_addr(m_ptr->PROC2);
/* Copy parent 'proc' struct to child. */
#if (CHIP == INTEL)
old_ldt_sel = rpc->p_ldt_sel; /* stop this being obliterated by copy */
#endif
*rpc = *rpp; /* copy 'proc' struct */
#if (CHIP == INTEL)
rpc->p_ldt_sel = old_ldt_sel;
#endif
rpc->p_nr = m_ptr->PROC2; /* this was obliterated by copy */
#if (SHADOWING == 0)
rpc->p_flags |= NO_MAP; /* inhibit the process from running */
#endif
rpc->p_flags &= ~(PENDING | SIG_PENDING | P_STOP);
/* Only 1 in group should have PENDING, child does not inherit trace status*/
sigemptyset(&rpc->p_pending);
rpc->p_pendcount = 0;
rpc->p_pid = m_ptr->PID; /* install child's pid */
rpc->p_reg.retreg = 0; /* child sees pid = 0 to know it is child */
rpc->user_time = 0; /* set all the accounting times to 0 */
rpc->sys_time = 0;
rpc->child_utime = 0;
rpc->child_stime = 0;
#if (SHADOWING == 1)
rpc->p_nflips = 0;
mkshadow(rpp, (phys_clicks)m_ptr->m1_p1); /* run child first */
#endif
return(OK);
}
/*===========================================================================*
* do_newmap *
*===========================================================================*/
PRIVATE int do_newmap(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Handle sys_newmap(). Fetch the memory map from MM. */
register struct proc *rp;
phys_bytes src_phys;
int caller; /* whose space has the new map (usually MM) */
int k; /* process whose map is to be loaded */
int old_flags; /* value of flags before modification */
struct mem_map *map_ptr; /* virtual address of map inside caller (MM) */
/* Extract message parameters and copy new memory map from MM. */
caller = m_ptr->m_source;
k = m_ptr->PROC1;
map_ptr = (struct mem_map *) m_ptr->MEM_PTR;
if (!isokprocn(k)) return(E_BAD_PROC);
rp = proc_addr(k); /* ptr to entry of user getting new map */
/* Copy the map from MM. */
src_phys = umap(proc_addr(caller), D, (vir_bytes) map_ptr, sizeof(rp->p_map));
if (src_phys == 0) panic("bad call to sys_newmap", NO_NUM);
phys_copy(src_phys, vir2phys(rp->p_map), (phys_bytes) sizeof(rp->p_map));
#if (SHADOWING == 0)
#if (CHIP != M68000)
alloc_segments(rp);
#else
pmmu_init_proc(rp);
#endif
old_flags = rp->p_flags; /* save the previous value of the flags */
rp->p_flags &= ~NO_MAP;
if (old_flags != 0 && rp->p_flags == 0) lock_ready(rp);
#endif
return(OK);
}
/*===========================================================================*
* do_getmap *
*===========================================================================*/
PRIVATE int do_getmap(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Handle sys_getmap(). Report the memory map to MM. */
register struct proc *rp;
phys_bytes dst_phys;
int caller; /* where the map has to be stored */
int k; /* process whose map is to be loaded */
struct mem_map *map_ptr; /* virtual address of map inside caller (MM) */
/* Extract message parameters and copy new memory map to MM. */
caller = m_ptr->m_source;
k = m_ptr->PROC1;
map_ptr = (struct mem_map *) m_ptr->MEM_PTR;
if (!isokprocn(k))
panic("do_getmap got bad proc: ", m_ptr->PROC1);
rp = proc_addr(k); /* ptr to entry of the map */
/* Copy the map to MM. */
dst_phys = umap(proc_addr(caller), D, (vir_bytes) map_ptr, sizeof(rp->p_map));
if (dst_phys == 0) panic("bad call to sys_getmap", NO_NUM);
phys_copy(vir2phys(rp->p_map), dst_phys, sizeof(rp->p_map));
return(OK);
}
/*===========================================================================*
* do_exec *
*===========================================================================*/
PRIVATE int do_exec(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_exec(). A process has done a successful EXEC. Patch it up. */
register struct proc *rp;
reg_t sp; /* new sp */
phys_bytes phys_name;
char *np;
#define NLEN (sizeof(rp->p_name)-1)
if (!isoksusern(m_ptr->PROC1)) return E_BAD_PROC;
/* PROC2 field is used as flag to indicate process is being traced */
if (m_ptr->PROC2) cause_sig(m_ptr->PROC1, SIGTRAP);
sp = (reg_t) m_ptr->STACK_PTR;
rp = proc_addr(m_ptr->PROC1);
rp->p_reg.sp = sp; /* set the stack pointer */
#if (CHIP == M68000)
rp->p_splow = sp; /* set the stack pointer low water */
#ifdef FPP
/* Initialize fpp for this process */
fpp_new_state(rp);
#endif
#endif
rp->p_reg.pc = (reg_t) m_ptr->IP_PTR; /* set pc */
rp->p_alarm = 0; /* reset alarm timer */
rp->p_flags &= ~RECEIVING; /* MM does not reply to EXEC call */
if (rp->p_flags == 0) lock_ready(rp);
/* Save command name for debugging, ps(1) output, etc. */
phys_name = numap(m_ptr->m_source, (vir_bytes) m_ptr->NAME_PTR,
(vir_bytes) NLEN);
if (phys_name != 0) {
phys_copy(phys_name, vir2phys(rp->p_name), (phys_bytes) NLEN);
for (np = rp->p_name; (*np & BYTE) >= ' '; np++) {}
*np = 0;
}
return(OK);
}
/*===========================================================================*
* do_xit *
*===========================================================================*/
PRIVATE int do_xit(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Handle sys_xit(). A process has exited. */
register struct proc *rp, *rc;
struct proc *np, *xp;
int parent; /* number of exiting proc's parent */
int proc_nr; /* number of process doing the exit */
phys_clicks base, size;
parent = m_ptr->PROC1; /* slot number of parent process */
proc_nr = m_ptr->PROC2; /* slot number of exiting process */
if (!isoksusern(parent) || !isoksusern(proc_nr)) return(E_BAD_PROC);
rp = proc_addr(parent);
rc = proc_addr(proc_nr);
lock();
rp->child_utime += rc->user_time + rc->child_utime; /* accum child times */
rp->child_stime += rc->sys_time + rc->child_stime;
unlock();
rc->p_alarm = 0; /* turn off alarm timer */
if (rc->p_flags == 0) lock_unready(rc);
#if (SHADOWING == 1)
rmshadow(rc, &base, &size);
m_ptr->m1_i1 = (int)base;
m_ptr->m1_i2 = (int)size;
#endif
strcpy(rc->p_name, "<noname>"); /* process no longer has a name */
/* If the process being terminated happens to be queued trying to send a
* message (i.e., the process was killed by a signal, rather than it doing an
* EXIT), then it must be removed from the message queues.
*/
if (rc->p_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_callerq == NIL_PROC) continue;
if (rp->p_callerq == rc) {
/* Exiting process is on front of this queue. */
rp->p_callerq = rc->p_sendlink;
break;
} else {
/* See if exiting process is in middle of queue. */
np = rp->p_callerq;
while ( ( xp = np->p_sendlink) != NIL_PROC)
if (xp == rc) {
np->p_sendlink = xp->p_sendlink;
break;
} else {
np = xp;
}
}
}
}
#if (CHIP == M68000) && (SHADOWING == 0)
pmmu_delete(rc); /* we're done remove tables */
#endif
if (rc->p_flags & PENDING) --sig_procs;
sigemptyset(&rc->p_pending);
rc->p_pendcount = 0;
rc->p_flags = P_SLOT_FREE;
return(OK);
}
/*===========================================================================*
* do_getsp *
*===========================================================================*/
PRIVATE int do_getsp(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_getsp(). MM wants to know what sp is. */
register struct proc *rp;
if (!isoksusern(m_ptr->PROC1)) return(E_BAD_PROC);
rp = proc_addr(m_ptr->PROC1);
m_ptr->STACK_PTR = (char *) rp->p_reg.sp; /* return sp here (bad type) */
return(OK);
}
/*===========================================================================*
* do_times *
*===========================================================================*/
PRIVATE int do_times(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_times(). Retrieve the accounting information. */
register struct proc *rp;
if (!isoksusern(m_ptr->PROC1)) return E_BAD_PROC;
rp = proc_addr(m_ptr->PROC1);
/* Insert the times needed by the TIMES system call in the message. */
lock(); /* halt the volatile time counters in rp */
m_ptr->USER_TIME = rp->user_time;
m_ptr->SYSTEM_TIME = rp->sys_time;
unlock();
m_ptr->CHILD_UTIME = rp->child_utime;
m_ptr->CHILD_STIME = rp->child_stime;
m_ptr->BOOT_TICKS = get_uptime();
return(OK);
}
/*===========================================================================*
* do_abort *
*===========================================================================*/
PRIVATE int do_abort(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Handle sys_abort. MINIX is unable to continue. Terminate operation. */
char monitor_code[64];
phys_bytes src_phys;
if (m_ptr->m1_i1 == RBT_MONITOR) {
/* The monitor is to run user specified instructions. */
src_phys = numap(m_ptr->m_source, (vir_bytes) m_ptr->m1_p1,
(vir_bytes) sizeof(monitor_code));
if (src_phys == 0) panic("bad monitor code from", m_ptr->m_source);
phys_copy(src_phys, vir2phys(monitor_code),
(phys_bytes) sizeof(monitor_code));
reboot_code = vir2phys(monitor_code);
}
wreboot(m_ptr->m1_i1);
return(OK); /* pro-forma (really EDISASTER) */
}
#if (SHADOWING == 1)
/*===========================================================================*
* do_fresh *
*===========================================================================*/
PRIVATE int do_fresh(m_ptr) /* for 68000 only */
message *m_ptr; /* pointer to request message */
{
/* Handle sys_fresh. Start with fresh process image during EXEC. */
register struct proc *p;
int proc_nr; /* number of process doing the exec */
phys_clicks base, size;
phys_clicks c1, nc;
proc_nr = m_ptr->PROC1; /* slot number of exec-ing process */
if (!isokprocn(proc_nr)) return(E_BAD_PROC);
p = proc_addr(proc_nr);
rmshadow(p, &base, &size);
do_newmap(m_ptr);
c1 = p->p_map[D].mem_phys;
nc = p->p_map[S].mem_phys - p->p_map[D].mem_phys + p->p_map[S].mem_len;
c1 += m_ptr->m1_i2;
nc -= m_ptr->m1_i2;
zeroclicks(c1, nc);
m_ptr->m1_i1 = (int)base;
m_ptr->m1_i2 = (int)size;
return(OK);
}
#endif /* (SHADOWING == 1) */
/*===========================================================================*
* do_sendsig *
*===========================================================================*/
PRIVATE int do_sendsig(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Handle sys_sendsig, POSIX-style signal */
struct sigmsg smsg;
register struct proc *rp;
phys_bytes src_phys, dst_phys;
struct sigcontext sc, *scp;
struct sigframe fr, *frp;
if (!isokusern(m_ptr->PROC1)) return(E_BAD_PROC);
rp = proc_addr(m_ptr->PROC1);
/* Get the sigmsg structure into our address space. */
src_phys = umap(proc_addr(MM_PROC_NR), D, (vir_bytes) m_ptr->SIG_CTXT_PTR,
(vir_bytes) sizeof(struct sigmsg));
if (src_phys == 0)
panic("do_sendsig can't signal: bad sigmsg address from MM", NO_NUM);
phys_copy(src_phys, vir2phys(&smsg), (phys_bytes) sizeof(struct sigmsg));
/* Compute the usr stack pointer value where sigcontext will be stored. */
scp = (struct sigcontext *) smsg.sm_stkptr - 1;
/* Copy the registers to the sigcontext structure. */
memcpy(&sc.sc_regs, &rp->p_reg, sizeof(struct sigregs));
/* Finish the sigcontext initialization. */
sc.sc_flags = SC_SIGCONTEXT;
sc.sc_mask = smsg.sm_mask;
/* Copy the sigcontext structure to the user's stack. */
dst_phys = umap(rp, D, (vir_bytes) scp,
(vir_bytes) sizeof(struct sigcontext));
if (dst_phys == 0) return(EFAULT);
phys_copy(vir2phys(&sc), dst_phys, (phys_bytes) sizeof(struct sigcontext));
/* Initialize the sigframe structure. */
frp = (struct sigframe *) scp - 1;
fr.sf_scpcopy = scp;
fr.sf_retadr2= (void (*)()) rp->p_reg.pc;
fr.sf_fp = rp->p_reg.fp;
rp->p_reg.fp = (reg_t) &frp->sf_fp;
fr.sf_scp = scp;
fr.sf_code = 0; /* XXX - should be used for type of FP exception */
fr.sf_signo = smsg.sm_signo;
fr.sf_retadr = (void (*)()) smsg.sm_sigreturn;
/* Copy the sigframe structure to the user's stack. */
dst_phys = umap(rp, D, (vir_bytes) frp, (vir_bytes) sizeof(struct sigframe));
if (dst_phys == 0) return(EFAULT);
phys_copy(vir2phys(&fr), dst_phys, (phys_bytes) sizeof(struct sigframe));
/* Reset user registers to execute the signal handler. */
rp->p_reg.sp = (reg_t) frp;
rp->p_reg.pc = (reg_t) smsg.sm_sighandler;
return(OK);
}
/*===========================================================================*
* do_sigreturn *
*===========================================================================*/
PRIVATE int do_sigreturn(m_ptr)
register message *m_ptr;
{
/* POSIX style signals require sys_sigreturn to put things in order before the
* signalled process can resume execution
*/
struct sigcontext sc;
register struct proc *rp;
phys_bytes src_phys;
if (!isokusern(m_ptr->PROC1)) return(E_BAD_PROC);
rp = proc_addr(m_ptr->PROC1);
/* Copy in the sigcontext structure. */
src_phys = umap(rp, D, (vir_bytes) m_ptr->SIG_CTXT_PTR,
(vir_bytes) sizeof(struct sigcontext));
if (src_phys == 0) return(EFAULT);
phys_copy(src_phys, vir2phys(&sc), (phys_bytes) sizeof(struct sigcontext));
/* Make sure that this is not just a jmp_buf. */
if ((sc.sc_flags & SC_SIGCONTEXT) == 0) return(EINVAL);
/* Fix up only certain key registers if the compiler doesn't use
* register variables within functions containing setjmp.
*/
if (sc.sc_flags & SC_NOREGLOCALS) {
rp->p_reg.retreg = sc.sc_retreg;
rp->p_reg.fp = sc.sc_fp;
rp->p_reg.pc = sc.sc_pc;
rp->p_reg.sp = sc.sc_sp;
return (OK);
}
sc.sc_psw = rp->p_reg.psw;
#if (CHIP == INTEL)
/* Don't panic kernel if user gave bad selectors. */
sc.sc_cs = rp->p_reg.cs;
sc.sc_ds = rp->p_reg.ds;
sc.sc_es = rp->p_reg.es;
#if _WORD_SIZE == 4
sc.sc_fs = rp->p_reg.fs;
sc.sc_gs = rp->p_reg.gs;
#endif
#endif
/* Restore the registers. */
memcpy(&rp->p_reg, (char *)&sc.sc_regs, sizeof(struct sigregs));
return(OK);
}
/*===========================================================================*
* do_kill *
*===========================================================================*/
PRIVATE int do_kill(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_kill(). Cause a signal to be sent to a process via MM.
* Note that this has nothing to do with the kill (2) system call, this
* is how the FS (and possibly other servers) get access to cause_sig to
* send a KSIG message to MM
*/
if (!isokusern(m_ptr->PR)) return(E_BAD_PROC);
cause_sig(m_ptr->PR, m_ptr->SIGNUM);
return(OK);
}
/*===========================================================================*
* do_endsig *
*===========================================================================*/
PRIVATE int do_endsig(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Finish up after a KSIG-type signal, caused by a SYS_KILL message or a call
* to cause_sig by a task
*/
register struct proc *rp;
if (!isokusern(m_ptr->PROC1)) return(E_BAD_PROC);
rp = proc_addr(m_ptr->PROC1);
/* MM has finished one KSIG. */
if (rp->p_pendcount != 0 && --rp->p_pendcount == 0
&& (rp->p_flags &= ~SIG_PENDING) == 0)
lock_ready(rp);
return(OK);
}
/*===========================================================================*
* do_copy *
*===========================================================================*/
PRIVATE int do_copy(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_copy(). Copy data for MM or FS. */
int src_proc, dst_proc, src_space, dst_space;
vir_bytes src_vir, dst_vir;
phys_bytes src_phys, dst_phys, bytes;
/* Dismember the command message. */
src_proc = m_ptr->SRC_PROC_NR;
dst_proc = m_ptr->DST_PROC_NR;
src_space = m_ptr->SRC_SPACE;
dst_space = m_ptr->DST_SPACE;
src_vir = (vir_bytes) m_ptr->SRC_BUFFER;
dst_vir = (vir_bytes) m_ptr->DST_BUFFER;
bytes = (phys_bytes) m_ptr->COPY_BYTES;
/* Compute the source and destination addresses and do the copy. */
#if (SHADOWING == 0)
if (src_proc == ABS)
src_phys = (phys_bytes) m_ptr->SRC_BUFFER;
else {
if (bytes != (vir_bytes) bytes)
/* This would happen for 64K segments and 16-bit vir_bytes.
* It would happen a lot for do_fork except MM uses ABS
* copies for that case.
*/
panic("overflow in count in do_copy", NO_NUM);
#endif
src_phys = umap(proc_addr(src_proc), src_space, src_vir,
(vir_bytes) bytes);
#if (SHADOWING == 0)
}
#endif
#if (SHADOWING == 0)
if (dst_proc == ABS)
dst_phys = (phys_bytes) m_ptr->DST_BUFFER;
else
#endif
dst_phys = umap(proc_addr(dst_proc), dst_space, dst_vir,
(vir_bytes) bytes);
if (src_phys == 0 || dst_phys == 0) return(EFAULT);
phys_copy(src_phys, dst_phys, bytes);
return(OK);
}
/*===========================================================================*
* do_vcopy *
*===========================================================================*/
PRIVATE int do_vcopy(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Handle sys_vcopy(). Copy multiple blocks of memory */
int src_proc, dst_proc, vect_s, i;
vir_bytes src_vir, dst_vir, vect_addr;
phys_bytes src_phys, dst_phys, bytes;
cpvec_t cpvec_table[CPVEC_NR];
/* Dismember the command message. */
src_proc = m_ptr->m1_i1;
dst_proc = m_ptr->m1_i2;
vect_s = m_ptr->m1_i3;
vect_addr = (vir_bytes)m_ptr->m1_p1;
if (vect_s > CPVEC_NR) return EDOM;
src_phys= numap (m_ptr->m_source, vect_addr, vect_s * sizeof(cpvec_t));
if (!src_phys) return EFAULT;
phys_copy(src_phys, vir2phys(cpvec_table),
(phys_bytes) (vect_s * sizeof(cpvec_t)));
for (i = 0; i < vect_s; i++) {
src_vir= cpvec_table[i].cpv_src;
dst_vir= cpvec_table[i].cpv_dst;
bytes= cpvec_table[i].cpv_size;
src_phys = numap(src_proc,src_vir,(vir_bytes)bytes);
dst_phys = numap(dst_proc,dst_vir,(vir_bytes)bytes);
if (src_phys == 0 || dst_phys == 0) return(EFAULT);
phys_copy(src_phys, dst_phys, bytes);
}
return(OK);
}
/*==========================================================================*
* do_gboot *
*==========================================================================*/
PUBLIC struct bparam_s boot_parameters;
PRIVATE int do_gboot(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Copy the boot parameters. Normally only called during fs init. */
phys_bytes dst_phys;
dst_phys = umap(proc_addr(m_ptr->PROC1), D, (vir_bytes) m_ptr->MEM_PTR,
(vir_bytes) sizeof(boot_parameters));
if (dst_phys == 0) panic("bad call to SYS_GBOOT", NO_NUM);
phys_copy(vir2phys(&boot_parameters), dst_phys,
(phys_bytes) sizeof(boot_parameters));
return(OK);
}
/*===========================================================================*
* do_mem *
*===========================================================================*/
PRIVATE int do_mem(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Return the base and size of the next chunk of memory. */
struct memory *memp;
for (memp = mem; memp < &mem[NR_MEMS]; ++memp) {
m_ptr->m1_i1 = memp->base;
m_ptr->m1_i2 = memp->size;
m_ptr->m1_i3 = tot_mem_size;
memp->size = 0;
if (m_ptr->m1_i2 != 0) break; /* found a chunk */
}
return(OK);
}
/*==========================================================================*
* do_umap *
*==========================================================================*/
PRIVATE int do_umap(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* Same as umap(), for non-kernel processes. */
m_ptr->SRC_BUFFER = umap(proc_addr((int) m_ptr->SRC_PROC_NR),
(int) m_ptr->SRC_SPACE,
(vir_bytes) m_ptr->SRC_BUFFER,
(vir_bytes) m_ptr->COPY_BYTES);
return(OK);
}
/*==========================================================================*
* do_trace *
*==========================================================================*/
#define TR_PROCNR (m_ptr->m2_i1)
#define TR_REQUEST (m_ptr->m2_i2)
#define TR_ADDR ((vir_bytes) m_ptr->m2_l1)
#define TR_DATA (m_ptr->m2_l2)
#define TR_VLSIZE ((vir_bytes) sizeof(long))
PRIVATE int do_trace(m_ptr)
register message *m_ptr;
{
/* Handle the debugging commands supported by the ptrace system call
* The commands are:
* T_STOP stop the process
* T_OK enable tracing by parent for this process
* T_GETINS return value from instruction space
* T_GETDATA return value from data space
* T_GETUSER return value from user process table
* T_SETINS set value from instruction space
* T_SETDATA set value from data space
* T_SETUSER set value in user process table
* T_RESUME resume execution
* T_EXIT exit
* T_STEP set trace bit
*
* The T_OK and T_EXIT commands are handled completely by the memory manager,
* all others come here.
*/
register struct proc *rp;
phys_bytes src, dst;
int i;
rp = proc_addr(TR_PROCNR);
if (rp->p_flags & P_SLOT_FREE) return(EIO);
switch (TR_REQUEST) {
case T_STOP: /* stop process */
if (rp->p_flags == 0) lock_unready(rp);
rp->p_flags |= P_STOP;
rp->p_reg.psw &= ~TRACEBIT; /* clear trace bit */
return(OK);
case T_GETINS: /* return value from instruction space */
if (rp->p_map[T].mem_len != 0) {
if ((src = umap(rp, T, TR_ADDR, TR_VLSIZE)) == 0) return(EIO);
phys_copy(src, vir2phys(&TR_DATA), (phys_bytes) sizeof(long));
break;
}
/* Text space is actually data space - fall through. */
case T_GETDATA: /* return value from data space */
if ((src = umap(rp, D, TR_ADDR, TR_VLSIZE)) == 0) return(EIO);
phys_copy(src, vir2phys(&TR_DATA), (phys_bytes) sizeof(long));
break;
case T_GETUSER: /* return value from process table */
if ((TR_ADDR & (sizeof(long) - 1)) != 0 ||
TR_ADDR > sizeof(struct proc) - sizeof(long))
return(EIO);
TR_DATA = *(long *) ((char *) rp + (int) TR_ADDR);
break;
case T_SETINS: /* set value in instruction space */
if (rp->p_map[T].mem_len != 0) {
if ((dst = umap(rp, T, TR_ADDR, TR_VLSIZE)) == 0) return(EIO);
phys_copy(vir2phys(&TR_DATA), dst, (phys_bytes) sizeof(long));
TR_DATA = 0;
break;
}
/* Text space is actually data space - fall through. */
case T_SETDATA: /* set value in data space */
if ((dst = umap(rp, D, TR_ADDR, TR_VLSIZE)) == 0) return(EIO);
phys_copy(vir2phys(&TR_DATA), dst, (phys_bytes) sizeof(long));
TR_DATA = 0;
break;
case T_SETUSER: /* set value in process table */
if ((TR_ADDR & (sizeof(reg_t) - 1)) != 0 ||
TR_ADDR > sizeof(struct stackframe_s) - sizeof(reg_t))
return(EIO);
i = (int) TR_ADDR;
#if (CHIP == INTEL)
/* Altering segment registers might crash the kernel when it
* tries to load them prior to restarting a process, so do
* not allow it.
*/
if (i == (int) &((struct proc *) 0)->p_reg.cs ||
i == (int) &((struct proc *) 0)->p_reg.ds ||
i == (int) &((struct proc *) 0)->p_reg.es ||
#if _WORD_SIZE == 4
i == (int) &((struct proc *) 0)->p_reg.gs ||
i == (int) &((struct proc *) 0)->p_reg.fs ||
#endif
i == (int) &((struct proc *) 0)->p_reg.ss)
return(EIO);
#endif
if (i == (int) &((struct proc *) 0)->p_reg.psw)
/* only selected bits are changeable */
SETPSW(rp, TR_DATA);
else
*(reg_t *) ((char *) &rp->p_reg + i) = (reg_t) TR_DATA;
TR_DATA = 0;
break;
case T_RESUME: /* resume execution */
rp->p_flags &= ~P_STOP;
if (rp->p_flags == 0) lock_ready(rp);
TR_DATA = 0;
break;
case T_STEP: /* set trace bit */
rp->p_reg.psw |= TRACEBIT;
rp->p_flags &= ~P_STOP;
if (rp->p_flags == 0) lock_ready(rp);
TR_DATA = 0;
break;
default:
return(EIO);
}
return(OK);
}
/*===========================================================================*
* 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 task wants to send a signal to a process. Examples of such tasks are:
* TTY wanting to cause SIGINT upon getting a DEL
* CLOCK wanting to cause SIGALRM when timer expires
* FS also uses this to send a signal, via the SYS_KILL message.
* Signals are handled by sending a message to MM. The tasks don't dare do
* that directly, for fear of what would happen if MM were busy. Instead they
* call cause_sig, which sets bits in p_pending, and then carefully checks to
* see if MM is free. If so, a message is sent to it. If not, when it becomes
* free, a message is sent. The process being signaled is blocked while MM
* has not seen or finished with all signals for it. These signals are
* counted in p_pendcount, and the SIG_PENDING flag is kept nonzero while
* there are some. It is not sufficient to ready the process when MM is
* informed, because MM can block waiting for FS to do a core dump.
*/
register struct proc *rp, *mmp;
rp = proc_addr(proc_nr);
if (sigismember(&rp->p_pending, sig_nr))
return; /* this signal already pending */
sigaddset(&rp->p_pending, sig_nr);
++rp->p_pendcount; /* count new signal pending */
if (rp->p_flags & PENDING)
return; /* another signal already pending */
if (rp->p_flags == 0) lock_unready(rp);
rp->p_flags |= PENDING | SIG_PENDING;
++sig_procs; /* count new process pending */
mmp = proc_addr(MM_PROC_NR);
if ( ((mmp->p_flags & RECEIVING) == 0) || mmp->p_getfrom != ANY) return;
inform();
}
/*===========================================================================*
* inform *
*===========================================================================*/
PUBLIC void inform()
{
/* When a signal is detected by the kernel (e.g., DEL), or generated by a task
* (e.g. clock task for SIGALRM), cause_sig() is called to set a bit in the
* p_pending field of the process to signal. Then inform() is called to see
* if MM is idle and can be told about it. Whenever MM blocks, a check is
* made to see if 'sig_procs' is nonzero; if so, inform() is called.
*/
register struct proc *rp;
/* MM is waiting for new input. Find a process with pending signals. */
for (rp = BEG_SERV_ADDR; rp < END_PROC_ADDR; rp++)
if (rp->p_flags & PENDING) {
m.m_type = KSIG;
m.SIG_PROC = proc_number(rp);
m.SIG_MAP = rp->p_pending;
sig_procs--;
if (lock_mini_send(proc_addr(HARDWARE), MM_PROC_NR, &m) != OK)
panic("can't inform MM", NO_NUM);
sigemptyset(&rp->p_pending); /* the ball is now in MM's court */
rp->p_flags &= ~PENDING;/* remains inhibited by SIG_PENDING */
lock_pick_proc(); /* avoid delay in scheduling MM */
return;
}
}
/*===========================================================================*
* umap *
*===========================================================================*/
PUBLIC phys_bytes umap(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);
vc = (vir_addr + bytes - 1) >> CLICK_SHIFT; /* last click of data */
#if (CHIP == INTEL) || (CHIP == M68000)
if (seg != T)
seg = (vc < rp->p_map[D].mem_vir + rp->p_map[D].mem_len ? D : S);
#else
if (seg != T)
seg = (vc < rp->p_map[S].mem_vir ? D : S);
#endif
if((vir_addr>>CLICK_SHIFT) >= rp->p_map[seg].mem_vir+ rp->p_map[seg].mem_len)
return( (phys_bytes) 0 );
#if (CHIP == INTEL)
seg_base = (phys_bytes) rp->p_map[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_map[seg].mem_vir << CLICK_SHIFT;
return(seg_base + pa);
#endif
#if (CHIP == M68000)
#if (SHADOWING == 0)
pa -= (phys_bytes)rp->p_map[seg].mem_vir << CLICK_SHIFT;
pa += (phys_bytes)rp->p_map[seg].mem_phys << CLICK_SHIFT;
#else
if (rp->p_shadow && seg != T) {
pa -= (phys_bytes)rp->p_map[D].mem_phys << CLICK_SHIFT;
pa += (phys_bytes)rp->p_shadow << CLICK_SHIFT;
}
#endif
return(pa);
#endif
}
/*==========================================================================*
* numap *
*==========================================================================*/
PUBLIC phys_bytes numap(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() 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(proc_addr(proc_nr), D, vir_addr, bytes));
}
#if (CHIP == INTEL)
/*==========================================================================*
* alloc_segments *
*==========================================================================*/
PUBLIC void alloc_segments(rp)
register struct proc *rp;
{
/* This is called only by do_newmap, but is broken out as a separate function
* because so much is hardware-dependent.
*/
phys_bytes code_bytes;
phys_bytes data_bytes;
int privilege;
if (protected_mode) {
data_bytes = (phys_bytes) (rp->p_map[S].mem_vir + rp->p_map[S].mem_len)
<< CLICK_SHIFT;
if (rp->p_map[T].mem_len == 0)
code_bytes = data_bytes; /* common I&D, poor protect */
else
code_bytes = (phys_bytes) rp->p_map[T].mem_len << CLICK_SHIFT;
privilege = istaskp(rp) ? TASK_PRIVILEGE : USER_PRIVILEGE;
init_codeseg(&rp->p_ldt[CS_LDT_INDEX],
(phys_bytes) rp->p_map[T].mem_phys << CLICK_SHIFT,
code_bytes, privilege);
init_dataseg(&rp->p_ldt[DS_LDT_INDEX],
(phys_bytes) rp->p_map[D].mem_phys << CLICK_SHIFT,
data_bytes, privilege);
rp->p_reg.cs = (CS_LDT_INDEX * DESC_SIZE) | TI | privilege;
#if _WORD_SIZE == 4
rp->p_reg.gs =
rp->p_reg.fs =
#endif
rp->p_reg.ss =
rp->p_reg.es =
rp->p_reg.ds = (DS_LDT_INDEX*DESC_SIZE) | TI | privilege;
} else {
rp->p_reg.cs = click_to_hclick(rp->p_map[T].mem_phys);
rp->p_reg.ss =
rp->p_reg.es =
rp->p_reg.ds = click_to_hclick(rp->p_map[D].mem_phys);
}
}
#endif /* (CHIP == INTEL) */