/************************************************** UZI (Unix Z80 Implementation) Kernel: process.c ***************************************************/ #include "unix.h" #include "extern.h" init2() { register char *j; static char bootchar; static char *arg[2] = { "init", NULL }; inoptr i_open(), n_open(); ptptr ptab_alloc(); bufinit(); /* Create the context for the first process */ newproc(udata.u_ptab = initproc = ptab_alloc()); initproc->p_status = P_RUNNING; /* User's file table */ for (j=udata.u_files; j < (udata.u_files+UFTSIZE); ++j) *j = -1; /* Turn on the clock */ out(02,0xf1); ei(); /* Wait until the clock has interrupted, to set tod while (!tod.t_date) ; /* Loop */ /* /* Open the console tty device */ if (d_open(TTYDEV) != 0) panic("no tty"); kprintf("boot: "); udata.u_base = &bootchar; udata.u_count = 1; cdread(TTYDEV); ROOTDEV = bootchar - '0'; /* Mount the root device */ if (fmount(ROOTDEV,NULLINODE)) panic("no filesys"); ifnot (root = i_open(ROOTDEV,ROOTINODE)) panic("no root"); i_ref(udata.u_cwd = root); rdtime(&udata.u_time); udata.u_argn2 = (int16)("/init"); udata.u_argn1 = (int16)(&arg[0]); udata.u_argn = (int16)(&arg[1]); _execve(); /* _execve("/init",&arg[0],&arg[1]); */ panic("no /init"); } /* psleep() puts a process to sleep on the given event. If another process is runnable, it swaps out the current one and starts the new one. Normally when psleep is called, the interrupts have already been disabled. An event of 0 means a pause(), while an event equal to the process's own ptab address is a wait(). */ psleep(event) char *event; { register dummy; /* Force saving of registers */ di(); if (udata.u_ptab->p_status != P_RUNNING) panic("psleep: voodoo"); if (!event) udata.u_ptab->p_status = P_PAUSE; else if (event == (char *)udata.u_ptab) udata.u_ptab->p_status = P_WAIT; else udata.u_ptab->p_status = P_SLEEP; udata.u_ptab->p_wait = event; ei(); swapout(); /* Swap us out, and start another process */ /* Swapout doesn't return until we have been swapped back in */ } /* wakeup() looks for any process waiting on the event, and make them runnable */ wakeup(event) char *event; { register ptptr p; di(); for(p=ptab;p < ptab+PTABSIZE; ++p) { if (p->p_status > P_RUNNING && p->p_wait == event) { p->p_status = P_READY; p->p_wait = (char *)NULL; } } ei(); } /* Getproc returns the process table pointer of a runnable process. It is actually the scheduler. If there are none, it loops. This is the only time-wasting loop in the system. */ ptptr getproc() { register status; static ptptr pp = ptab; /* Pointer for round-robin scheduling */ for (;;) { if (++pp >= ptab + PTABSIZE) pp = ptab; di(); status = pp->p_status; ei(); if (status == P_RUNNING) panic("getproc: extra running"); if (status == P_READY) return(pp); } } /* Temp storage for swapout() */ char *stkptr; /* Swapout swaps out the current process, finds another that is READY, possibly the same process, and swaps it in. When a process is restarted after calling swapout, it thinks it has just returned from swapout(). */ /* This function can have no arguments or auto variables */ swapout() { static ptptr newp; ptptr getproc(); /* See if any signals are pending */ chksigs(); /* Get a new process */ newp = getproc(); /* If there is nothing else to run, just return */ if (newp == udata.u_ptab) { udata.u_ptab->p_status = P_RUNNING; return (runticks = 0); } ; /* Save the stack pointer and critical registers */ #asm LD HL,01 ;this will return 1 if swapped. PUSH HL ;will be return value PUSH BC PUSH IX LD HL,0 ADD HL,SP ;get sp into hl LD (stkptr?),HL #endasm udata.u_sp = stkptr; swrite(); /* Read the new process in, and return into its context. */ swapin(newp); /* We should never get here. */ panic("swapin failed"); } /* This actually writes out the image */ swrite() { blkno_t blk; blk = udata.u_ptab->p_swap; /* Start by writing out the user data. */ /* The user data is written so that it is packed to the top of one block */ swapwrite(SWAPDEV, blk, 512, ((char *)(&udata+1))-512 ); /* The user address space is written in two i/o operations, one from 0x100 to the break, and then from the stack up. */ /* Notice that this might also include part or all of the user data, but never anything above it. */ swapwrite(SWAPDEV, blk+1, (((char *)(&udata+1))-PROGBASE) & ~511, PROGBASE); } /* No automatics can be used past tempstack(); */ swapin(pp) ptptr pp; { static blkno_t blk; static ptptr newp; di(); newp = pp; blk = newp->p_swap; ei(); tempstack(); swapread(SWAPDEV, blk, 512, ((char *)(&udata+1))-512 ); /* The user address space is read in two i/o operations, one from 0x100 to the break, and then from the stack up. */ /* Notice that this might also include part or all of the user data, but never anything above it. */ swapread(SWAPDEV, blk+1, (((char *)(&udata+1))-PROGBASE) & ~511, PROGBASE); if (newp != udata.u_ptab) panic("mangled swapin"); di(); newp->p_status = P_RUNNING; runticks = 0; ei(); /* Restore the registers */ stkptr = udata.u_sp; #asm LD HL,(stkptr?) LD SP,HL POP IX POP BC POP HL LD A,H OR L RET ;return into the context of the swapped-in process #endasm } /* Temp storage for dofork */ int16 newid; /* dofork implements forking. */ /* This function can have no arguments or auto variables */ dofork() { static ptptr p; ptptr ptab_alloc(); ifnot (p = ptab_alloc()) { udata.u_error = EAGAIN; return(-1); } di(); udata.u_ptab->p_status = P_READY; /* Parent is READY */ newid = p->p_pid; ei(); /* Save the stack pointer and critical registers */ /* When the process is swapped back in, it will be as if it returns with the value of the childs pid. */ #asm LD HL,(newid?) PUSH HL PUSH BC PUSH IX LD HL,0 ADD HL,SP ;get sp into hl LD (stkptr?),HL #endasm udata.u_sp = stkptr; swrite(); #asm POP HL ;repair stack pointer POP HL POP HL #endasm /* Make a new process table entry, etc. */ newproc(p); di(); runticks = 0; p->p_status = P_RUNNING; ei(); return (0); /* Return to child */ } /* Newproc fixes up the tables for the child of a fork */ newproc(p) ptptr p; /* New process table entry */ { register char *j; /* Note that ptab_alloc clears most of the entry */ di(); p->p_swap = (p - ptab) * 65 + 1; /* Allow 65 blocks per process */ p->p_status = P_RUNNING; p->p_pptr = udata.u_ptab; p->p_ignored = udata.u_ptab->p_ignored; p->p_uid = udata.u_ptab->p_uid; udata.u_ptab = p; bzero(&udata.u_utime,4*sizeof(time_t)); /* Clear tick counters */ ei(); rdtime(&udata.u_time); i_ref(udata.u_cwd); udata.u_cursig = udata.u_error = 0; for (j=udata.u_files; j < (udata.u_files+UFTSIZE); ++j) if (*j >= 0) ++of_tab[*j].o_refs; } /* This allocates a new process table slot, and fills in its p_pid field with a unique number. */ ptptr ptab_alloc() { register ptptr p; register ptptr pp; static int nextpid = 0; di(); for(p=ptab;p < ptab+PTABSIZE; ++p) { if (p->p_status == P_EMPTY) goto found; } ei(); return(NULL); found: /* See if next pid number is unique */ nogood: if (nextpid++ > 32000) nextpid = 1; for (pp=ptab; pp < ptab+PTABSIZE; ++pp) { if (pp->p_status != P_EMPTY && pp->p_pid == nextpid) goto nogood; } bzero(p,sizeof(struct p_tab)); p->p_pid = nextpid; p->p_status = P_FORKING; ei(); return (p); } /* This is the clock interrupt routine. Its job is to increment the clock counters, increment the tick count of the running process, and either swap it out if it has been in long enough and is in user space or mark it to be swapped out if in system space. Also it decrements the alarm clock of processes. This must have no automatic or register variables */ clk_int() { static ptptr p; ifnot (in(0xf0)) /* See if clock actually interrupted, and turn it off */ return(0); /* Increment processes and global tick counters */ if (udata.u_ptab->p_status == P_RUNNING) incrtick(udata.u_insys ? &udata.u_stime : &udata.u_utime); incrtick(&ticks); /* Do once-per-second things */ if (++sec == TICKSPERSEC) { /* Update global time counters */ sec = 0; rdtod(); /* Update time-of-day */ /* Update process alarm clocks */ for (p=ptab; p < ptab+PTABSIZE; ++p) { if (p->p_alarm) ifnot(--p->p_alarm) sendsig(p,SIGALRM); } } /* Check run time of current process */ if (++runticks >= MAXTICKS && !udata.u_insys) /* Time to swap out */ { udata.u_insys = 1; inint = 0; udata.u_ptab->p_status = P_READY; swapout(); di(); udata.u_insys = 0; /* We have swapped back in */ } return(1); } extern int (*disp_tab[])(); static int j; /* No auto vars here, so carry flag will be preserved */ unix(argn3, argn2, argn1, argn, uret, callno) int16 argn3, argn2, argn1, argn; char *uret; int callno; { udata.u_argn3 = argn3; udata.u_argn2 = argn2; udata.u_argn1 = argn1; udata.u_argn = argn; udata.u_retloc = uret; udata.u_callno = callno; udata.u_insys = 1; udata.u_error = 0; ei(); #ifdef DEBUG kprintf ("\t\t\t\t\tcall %d (%x, %x, %x)\n",callno,argn2,argn1,argn); #endif /* Branch to correct routine */ udata.u_retval = (*disp_tab[udata.u_callno])(); #ifdef DEBUG kprintf("\t\t\t\t\t\tcall %d ret %x err %d\n", udata.u_callno,udata.u_retval, udata.u_error); #endif chksigs(); di(); if (runticks >= MAXTICKS) { udata.u_ptab->p_status = P_READY; swapout(); } ei(); udata.u_insys = 0; calltrap(); /* Call trap routine if necessary */ /* If an error, return errno with carry set */ if (udata.u_error) { ; #asm LD HL, (udata? + ?OERR) POP BC ;restore frame pointer PUSH BC POP IX SCF RET #endasm ; } return(udata.u_retval); } /* This sees if the current process has any signals set, and deals with them */ chksigs() { register j; di(); ifnot (udata.u_ptab->p_pending) { ei(); return; } for (j=1; j < NSIGS; ++j) { ifnot (sigmask(j) & udata.u_ptab->p_pending) continue; if (udata.u_sigvec[j] == SIG_DFL) { ei(); doexit(0,j); } if (udata.u_sigvec[j] != SIG_IGN) { /* Arrange to call the user routine at return */ udata.u_ptab->p_pending &= !sigmask(j); udata.u_cursig = j; } } ei(); } sendsig(proc,sig) ptptr proc; int16 sig; { register ptptr p; if (proc) ssig(proc,sig); else for (p=ptab; p < ptab+PTABSIZE; ++p) if (p->p_status) ssig(p,sig); } ssig(proc,sig) register ptptr proc; int16 sig; { register stat; di(); ifnot(proc->p_status) goto done; /* Presumably was killed just now */ if (proc->p_ignored & sigmask(sig)) goto done; stat = proc->p_status; if (stat == P_PAUSE || stat == P_WAIT || stat == P_SLEEP) proc->p_status = P_READY; proc->p_wait = (char *)NULL; proc->p_pending |= sigmask(sig); done: ei(); }