]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/main.c
pc kernel: remove i8253link
[plan9front.git] / sys / src / 9 / pc / main.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "io.h"
7 #include        "tos.h"
8 #include        "ureg.h"
9 #include        "init.h"
10 #include        "pool.h"
11 #include        "reboot.h"
12
13 Mach *m;
14
15 /*
16  * Where configuration info is left for the loaded programme.
17  * This will turn into a structure as more is done by the boot loader
18  * (e.g. why parse the .ini file twice?).
19  * There are 3584 bytes available at CONFADDR.
20  */
21 #define BOOTLINE        ((char*)CONFADDR)
22 #define BOOTLINELEN     64
23 #define BOOTARGS        ((char*)(CONFADDR+BOOTLINELEN))
24 #define BOOTARGSLEN     (4096-0x200-BOOTLINELEN)
25 #define MAXCONF         64
26
27 char bootdisk[KNAMELEN];
28 Conf conf;
29 char *confname[MAXCONF];
30 char *confval[MAXCONF];
31 int nconf;
32 uchar *sp;      /* user stack of init proc */
33 int delaylink;
34
35 static void
36 options(void)
37 {
38         long i, n;
39         char *cp, *line[MAXCONF], *p, *q;
40
41         /*
42          *  parse configuration args from dos file plan9.ini
43          */
44         cp = BOOTARGS;  /* where b.com leaves its config */
45         cp[BOOTARGSLEN-1] = 0;
46
47         /*
48          * Strip out '\r', change '\t' -> ' '.
49          */
50         p = cp;
51         for(q = cp; *q; q++){
52                 if(*q == '\r')
53                         continue;
54                 if(*q == '\t')
55                         *q = ' ';
56                 *p++ = *q;
57         }
58         *p = 0;
59
60         n = getfields(cp, line, MAXCONF, 1, "\n");
61         for(i = 0; i < n; i++){
62                 if(*line[i] == '#')
63                         continue;
64                 cp = strchr(line[i], '=');
65                 if(cp == nil)
66                         continue;
67                 *cp++ = '\0';
68                 confname[nconf] = line[i];
69                 confval[nconf] = cp;
70                 nconf++;
71         }
72 }
73
74 extern void mmuinit0(void);
75 extern void (*i8237alloc)(void);
76
77 void
78 main(void)
79 {
80         mach0init();
81         options();
82         ioinit();
83         i8250console();
84         quotefmtinstall();
85         screeninit();
86
87         print("\nPlan 9\n");
88
89         trapinit0();
90         mmuinit0();
91
92         kbdinit();
93         i8253init();
94         cpuidentify();
95         meminit();
96         confinit();
97         archinit();
98         xinit();
99         if(i8237alloc != nil)
100                 i8237alloc();
101         trapinit();
102         printinit();
103         cpuidprint();
104         mmuinit();
105         if(arch->intrinit)      /* launches other processors on an mp */
106                 arch->intrinit();
107         timersinit();
108         mathinit();
109         kbdenable();
110         if(arch->clockenable)
111                 arch->clockenable();
112         procinit0();
113         initseg();
114         if(delaylink){
115                 bootlinks();
116                 pcimatch(0, 0, 0);
117         }else
118                 links();
119         conf.monitor = 1;
120         chandevreset();
121         pageinit();
122         swapinit();
123         userinit();
124         active.thunderbirdsarego = 1;
125         schedinit();
126 }
127
128 void
129 mach0init(void)
130 {
131         conf.nmach = 1;
132         MACHP(0) = (Mach*)CPU0MACH;
133         m->pdb = (ulong*)CPU0PDB;
134         m->gdt = (Segdesc*)CPU0GDT;
135
136         machinit();
137
138         active.machs = 1;
139         active.exiting = 0;
140 }
141
142 void
143 machinit(void)
144 {
145         int machno;
146         ulong *pdb;
147         Segdesc *gdt;
148
149         machno = m->machno;
150         pdb = m->pdb;
151         gdt = m->gdt;
152         memset(m, 0, sizeof(Mach));
153         m->machno = machno;
154         m->pdb = pdb;
155         m->gdt = gdt;
156         m->perf.period = 1;
157
158         /*
159          * For polled uart output at boot, need
160          * a default delay constant. 100000 should
161          * be enough for a while. Cpuidentify will
162          * calculate the real value later.
163          */
164         m->loopconst = 100000;
165 }
166
167 void
168 init0(void)
169 {
170         int i;
171         char buf[2*KNAMELEN];
172
173         up->nerrlab = 0;
174
175         spllo();
176
177         /*
178          * These are o.k. because rootinit is null.
179          * Then early kproc's will have a root and dot.
180          */
181         up->slash = namec("#/", Atodir, 0, 0);
182         pathclose(up->slash->path);
183         up->slash->path = newpath("/");
184         up->dot = cclone(up->slash);
185
186         chandevinit();
187
188         if(!waserror()){
189                 snprint(buf, sizeof(buf), "%s %s", arch->id, conffile);
190                 ksetenv("terminal", buf, 0);
191                 ksetenv("cputype", "386", 0);
192                 if(cpuserver)
193                         ksetenv("service", "cpu", 0);
194                 else
195                         ksetenv("service", "terminal", 0);
196                 for(i = 0; i < nconf; i++){
197                         if(confname[i][0] != '*')
198                                 ksetenv(confname[i], confval[i], 0);
199                         ksetenv(confname[i], confval[i], 1);
200                 }
201                 poperror();
202         }
203         kproc("alarm", alarmkproc, 0);
204         touser(sp);
205 }
206
207 void
208 userinit(void)
209 {
210         void *v;
211         Proc *p;
212         Segment *s;
213         Page *pg;
214
215         p = newproc();
216         p->pgrp = newpgrp();
217         p->egrp = smalloc(sizeof(Egrp));
218         p->egrp->ref = 1;
219         p->fgrp = dupfgrp(nil);
220         p->rgrp = newrgrp();
221         p->procmode = 0640;
222
223         kstrdup(&eve, "");
224         kstrdup(&p->text, "*init*");
225         kstrdup(&p->user, eve);
226
227         procsetup(p);
228
229         /*
230          * Kernel Stack
231          *
232          * N.B. make sure there's enough space for syscall to check
233          *      for valid args and 
234          *      4 bytes for gotolabel's return PC
235          */
236         p->sched.pc = (ulong)init0;
237         p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD);
238
239         /*
240          * User Stack
241          *
242          * N.B. cannot call newpage() with clear=1, because pc kmap
243          * requires up != nil.  use tmpmap instead.
244          */
245         s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
246         p->seg[SSEG] = s;
247         pg = newpage(0, 0, USTKTOP-BY2PG);
248         v = tmpmap(pg);
249         memset(v, 0, BY2PG);
250         segpage(s, pg);
251         bootargs(v);
252         tmpunmap(v);
253
254         /*
255          * Text
256          */
257         s = newseg(SG_TEXT, UTZERO, 1);
258         s->flushme++;
259         p->seg[TSEG] = s;
260         pg = newpage(0, 0, UTZERO);
261         memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
262         segpage(s, pg);
263         v = tmpmap(pg);
264         memset(v, 0, BY2PG);
265         memmove(v, initcode, sizeof initcode);
266         tmpunmap(v);
267
268         ready(p);
269 }
270
271 uchar *
272 pusharg(char *p)
273 {
274         int n;
275
276         n = strlen(p)+1;
277         sp -= n;
278         memmove(sp, p, n);
279         return sp;
280 }
281
282 void
283 bootargs(void *base)
284 {
285         int i, ac;
286         uchar *av[32];
287         uchar **lsp;
288         char *cp = BOOTLINE;
289         char buf[64];
290
291         sp = (uchar*)base + BY2PG - sizeof(Tos);
292
293         ac = 0;
294         av[ac++] = pusharg("boot");
295
296         /* when boot is changed to only use rc, this code can go away */
297         cp[BOOTLINELEN-1] = 0;
298         buf[0] = 0;
299         if(strncmp(cp, "fd", 2) == 0){
300                 sprint(buf, "local!#f/fd%lddisk", strtol(cp+2, 0, 0));
301                 av[ac++] = pusharg(buf);
302         } else if(strncmp(cp, "sd", 2) == 0){
303                 sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3));
304                 av[ac++] = pusharg(buf);
305         } else if(strncmp(cp, "ether", 5) == 0)
306                 av[ac++] = pusharg("-n");
307
308         /* 4 byte word align stack */
309         sp = (uchar*)((ulong)sp & ~3);
310
311         /* build argc, argv on stack */
312         sp -= (ac+1)*sizeof(sp);
313         lsp = (uchar**)sp;
314         for(i = 0; i < ac; i++)
315                 lsp[i] = av[i] + ((USTKTOP - BY2PG) - (ulong)base);
316         lsp[i] = 0;
317         sp += (USTKTOP - BY2PG) - (ulong)base;
318         sp -= BY2WD;
319 }
320
321 char*
322 getconf(char *name)
323 {
324         int i;
325
326         for(i = 0; i < nconf; i++)
327                 if(cistrcmp(confname[i], name) == 0)
328                         return confval[i];
329         return 0;
330 }
331
332 static void
333 writeconf(void)
334 {
335         char *p, *q;
336         int n;
337
338         p = getconfenv();
339
340         if(waserror()) {
341                 free(p);
342                 nexterror();
343         }
344
345         /* convert to name=value\n format */
346         for(q=p; *q; q++) {
347                 q += strlen(q);
348                 *q = '=';
349                 q += strlen(q);
350                 *q = '\n';
351         }
352         n = q - p + 1;
353         if(n >= BOOTARGSLEN)
354                 error("kernel configuration too large");
355         memset(BOOTLINE, 0, BOOTLINELEN);
356         memmove(BOOTARGS, p, n);
357         poperror();
358         free(p);
359 }
360
361 void
362 confinit(void)
363 {
364         char *p;
365         int i, userpcnt;
366         ulong kpages;
367
368         if(p = getconf("*kernelpercent"))
369                 userpcnt = 100 - strtol(p, 0, 0);
370         else
371                 userpcnt = 0;
372
373         conf.npage = 0;
374         for(i=0; i<nelem(conf.mem); i++)
375                 conf.npage += conf.mem[i].npage;
376
377         conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
378         if(cpuserver)
379                 conf.nproc *= 3;
380         if(conf.nproc > 2000)
381                 conf.nproc = 2000;
382         conf.nimage = 200;
383         conf.nswap = conf.nproc*80;
384         conf.nswppo = 4096;
385
386         if(cpuserver) {
387                 if(userpcnt < 10)
388                         userpcnt = 70;
389                 kpages = conf.npage - (conf.npage*userpcnt)/100;
390
391                 /*
392                  * Hack for the big boys. Only good while physmem < 4GB.
393                  * Give the kernel fixed max + enough to allocate the
394                  * page pool.
395                  * This is an overestimate as conf.upages < conf.npages.
396                  * The patch of nimage is a band-aid, scanning the whole
397                  * page list in imagereclaim just takes too long.
398                  */
399                 if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){
400                         kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG;
401                         conf.nimage = 2000;
402                         kpages += (conf.nproc*KSTACK)/BY2PG;
403                 }
404         } else {
405                 if(userpcnt < 10) {
406                         if(conf.npage*BY2PG < 16*MB)
407                                 userpcnt = 40;
408                         else
409                                 userpcnt = 60;
410                 }
411                 kpages = conf.npage - (conf.npage*userpcnt)/100;
412
413                 /*
414                  * Make sure terminals with low memory get at least
415                  * 4MB on the first Image chunk allocation.
416                  */
417                 if(conf.npage*BY2PG < 16*MB)
418                         imagmem->minarena = 4*1024*1024;
419         }
420
421         /*
422          * can't go past the end of virtual memory
423          * (ulong)-KZERO is 2^32 - KZERO
424          */
425         if(kpages > ((ulong)-KZERO)/BY2PG)
426                 kpages = ((ulong)-KZERO)/BY2PG;
427
428         conf.upages = conf.npage - kpages;
429         conf.ialloc = (kpages/2)*BY2PG;
430
431         /*
432          * Guess how much is taken by the large permanent
433          * datastructures. Mntcache and Mntrpc are not accounted for
434          * (probably ~300KB).
435          */
436         kpages *= BY2PG;
437         kpages -= conf.upages*sizeof(Page)
438                 + conf.nproc*sizeof(Proc)
439                 + conf.nimage*sizeof(Image)
440                 + conf.nswap
441                 + conf.nswppo*sizeof(Page);
442         mainmem->maxsize = kpages;
443         if(!cpuserver){
444                 /*
445                  * give terminals lots of image memory, too; the dynamic
446                  * allocation will balance the load properly, hopefully.
447                  * be careful with 32-bit overflow.
448                  */
449                 imagmem->maxsize = kpages;
450         }
451 }
452
453 static char* mathmsg[] =
454 {
455         nil,    /* handled below */
456         "denormalized operand",
457         "division by zero",
458         "numeric overflow",
459         "numeric underflow",
460         "precision loss",
461 };
462
463 static void
464 mathnote(void)
465 {
466         int i;
467         ulong status;
468         char *msg, note[ERRMAX];
469
470         status = up->fpsave.status;
471
472         /*
473          * Some attention should probably be paid here to the
474          * exception masks and error summary.
475          */
476         msg = "unknown exception";
477         for(i = 1; i <= 5; i++){
478                 if(!((1<<i) & status))
479                         continue;
480                 msg = mathmsg[i];
481                 break;
482         }
483         if(status & 0x01){
484                 if(status & 0x40){
485                         if(status & 0x200)
486                                 msg = "stack overflow";
487                         else
488                                 msg = "stack underflow";
489                 }else
490                         msg = "invalid operation";
491         }
492         snprint(note, sizeof note, "sys: fp: %s fppc=0x%lux status=0x%lux",
493                 msg, up->fpsave.pc, status);
494         postnote(up, 1, note, NDebug);
495 }
496
497 /*
498  *  math coprocessor error
499  */
500 static void
501 matherror(Ureg *ur, void*)
502 {
503         /*
504          *  a write cycle to port 0xF0 clears the interrupt latch attached
505          *  to the error# line from the 387
506          */
507         if(!(m->cpuiddx & 0x01))
508                 outb(0xF0, 0xFF);
509
510         /*
511          *  save floating point state to check out error
512          */
513         fpenv(&up->fpsave);
514         mathnote();
515
516         if((ur->pc & 0xf0000000) == KZERO)
517                 panic("fp: status %ux fppc=0x%lux pc=0x%lux",
518                         up->fpsave.status, up->fpsave.pc, ur->pc);
519 }
520
521 /*
522  *  math coprocessor emulation fault
523  */
524 static void
525 mathemu(Ureg *ureg, void*)
526 {
527         if(up->fpstate & FPillegal){
528                 /* someone did floating point in a note handler */
529                 postnote(up, 1, "sys: floating point in note handler", NDebug);
530                 return;
531         }
532         switch(up->fpstate){
533         case FPinit:
534                 fpinit();
535                 up->fpstate = FPactive;
536                 break;
537         case FPinactive:
538                 /*
539                  * Before restoring the state, check for any pending
540                  * exceptions, there's no way to restore the state without
541                  * generating an unmasked exception.
542                  * More attention should probably be paid here to the
543                  * exception masks and error summary.
544                  */
545                 if((up->fpsave.status & ~up->fpsave.control) & 0x07F){
546                         mathnote();
547                         break;
548                 }
549                 fprestore(&up->fpsave);
550                 up->fpstate = FPactive;
551                 break;
552         case FPactive:
553                 panic("math emu pid %ld %s pc 0x%lux", 
554                         up->pid, up->text, ureg->pc);
555                 break;
556         }
557 }
558
559 /*
560  *  math coprocessor segment overrun
561  */
562 static void
563 mathover(Ureg*, void*)
564 {
565         pexit("math overrun", 0);
566 }
567
568 void
569 mathinit(void)
570 {
571         trapenable(VectorCERR, matherror, 0, "matherror");
572         if(X86FAMILY(m->cpuidax) == 3)
573                 intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
574         trapenable(VectorCNA, mathemu, 0, "mathemu");
575         trapenable(VectorCSO, mathover, 0, "mathover");
576 }
577
578 /*
579  *  set up floating point for a new process
580  */
581 void
582 procsetup(Proc *p)
583 {
584         p->fpstate = FPinit;
585         fpoff();
586
587         cycles(&p->kentry);
588         p->pcycles = -p->kentry;
589
590         memset(p->gdt, 0, sizeof(p->gdt));
591         p->ldt = nil;
592         p->nldt = 0;
593 }
594
595 void
596 procfork(Proc *p)
597 {
598         p->kentry = up->kentry;
599         p->pcycles = -p->kentry;
600
601         /* inherit user descriptors */
602         memmove(p->gdt, up->gdt, sizeof(p->gdt));
603
604         /* copy local descriptor table */
605         if(up->ldt != nil && up->nldt > 0){
606                 p->ldt = malloc(sizeof(Segdesc) * up->nldt);
607                 memmove(p->ldt, up->ldt, sizeof(Segdesc) * up->nldt);
608                 p->nldt = up->nldt;
609         }
610 }
611
612 void
613 procrestore(Proc *p)
614 {
615         uvlong t;
616
617         if(p->kp)
618                 return;
619
620         cycles(&t);
621         p->kentry += t;
622         p->pcycles -= t;
623 }
624
625 /*
626  *  Save the mach dependent part of the process state.
627  */
628 void
629 procsave(Proc *p)
630 {
631         uvlong t;
632
633         cycles(&t);
634         p->kentry -= t;
635         p->pcycles += t;
636
637         if(p->fpstate == FPactive){
638                 if(p->state == Moribund)
639                         fpclear();
640                 else{
641                         /*
642                          * Fpsave() stores without handling pending
643                          * unmasked exeptions. Postnote() can't be called
644                          * here as sleep() already has up->rlock, so
645                          * the handling of pending exceptions is delayed
646                          * until the process runs again and generates an
647                          * emulation fault to activate the FPU.
648                          */
649                         fpsave(&p->fpsave);
650                 }
651                 p->fpstate = FPinactive;
652         }
653
654         /*
655          * While this processor is in the scheduler, the process could run
656          * on another processor and exit, returning the page tables to
657          * the free list where they could be reallocated and overwritten.
658          * When this processor eventually has to get an entry from the
659          * trashed page tables it will crash.
660          *
661          * If there's only one processor, this can't happen.
662          * You might think it would be a win not to do this in that case,
663          * especially on VMware, but it turns out not to matter.
664          */
665         mmuflushtlb(PADDR(m->pdb));
666 }
667
668 static void
669 shutdown(int ispanic)
670 {
671         int ms, once;
672
673         lock(&active);
674         if(ispanic)
675                 active.ispanic = ispanic;
676         else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
677                 active.ispanic = 0;
678         once = active.machs & (1<<m->machno);
679         /*
680          * setting exiting will make hzclock() on each processor call exit(0),
681          * which calls shutdown(0) and arch->reset(), which on mp systems is
682          * mpshutdown, from which there is no return: the processor is idled
683          * or initiates a reboot.  clearing our bit in machs avoids calling
684          * exit(0) from hzclock() on this processor.
685          */
686         active.machs &= ~(1<<m->machno);
687         active.exiting = 1;
688         unlock(&active);
689
690         if(once)
691                 iprint("cpu%d: exiting\n", m->machno);
692
693         /* wait for any other processors to shutdown */
694         spllo();
695         for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
696                 delay(TK2MS(2));
697                 if(active.machs == 0 && consactive() == 0)
698                         break;
699         }
700
701         if(active.ispanic){
702                 if(!cpuserver)
703                         for(;;)
704                                 halt();
705                 if(getconf("*debug"))
706                         delay(5*60*1000);
707                 else
708                         delay(10000);
709         }else
710                 delay(1000);
711 }
712
713 void
714 reboot(void *entry, void *code, ulong size)
715 {
716         void (*f)(ulong, ulong, ulong);
717         ulong *pdb;
718
719         writeconf();
720
721         /*
722          * the boot processor is cpu0.  execute this function on it
723          * so that the new kernel has the same cpu0.  this only matters
724          * because the hardware has a notion of which processor was the
725          * boot processor and we look at it at start up.
726          */
727         if (m->machno != 0) {
728                 procwired(up, 0);
729                 sched();
730         }
731
732         shutdown(0);
733
734         /*
735          * should be the only processor running now
736          */
737         if (m->machno != 0)
738                 print("on cpu%d (not 0)!\n", m->machno);
739         if (active.machs)
740                 print("still have active ap processors!\n");
741
742         print("shutting down...\n");
743         delay(200);
744
745         splhi();
746
747         /* turn off buffered serial console */
748         serialoq = nil;
749
750         /* shutdown devices */
751         chandevshutdown();
752         arch->introff();
753
754         /*
755          * Modify the machine page table to directly map the low 4MB of memory
756          * This allows the reboot code to turn off the page mapping
757          */
758         pdb = m->pdb;
759         pdb[PDX(0)] = pdb[PDX(KZERO)];
760         mmuflushtlb(PADDR(pdb));
761
762         /* setup reboot trampoline function */
763         f = (void*)REBOOTADDR;
764         memmove(f, rebootcode, sizeof(rebootcode));
765
766         print("rebooting...\n");
767
768         /* off we go - never to return */
769         coherence();
770         (*f)(PADDR(entry), PADDR(code), size);
771 }
772
773
774 void
775 exit(int ispanic)
776 {
777         shutdown(ispanic);
778         arch->reset();
779 }
780
781 int
782 isaconfig(char *class, int ctlrno, ISAConf *isa)
783 {
784         char cc[32], *p;
785         int i;
786
787         snprint(cc, sizeof cc, "%s%d", class, ctlrno);
788         p = getconf(cc);
789         if(p == nil)
790                 return 0;
791
792         isa->type = "";
793         isa->nopt = tokenize(p, isa->opt, NISAOPT);
794         for(i = 0; i < isa->nopt; i++){
795                 p = isa->opt[i];
796                 if(cistrncmp(p, "type=", 5) == 0)
797                         isa->type = p + 5;
798                 else if(cistrncmp(p, "port=", 5) == 0)
799                         isa->port = strtoul(p+5, &p, 0);
800                 else if(cistrncmp(p, "irq=", 4) == 0)
801                         isa->irq = strtoul(p+4, &p, 0);
802                 else if(cistrncmp(p, "dma=", 4) == 0)
803                         isa->dma = strtoul(p+4, &p, 0);
804                 else if(cistrncmp(p, "mem=", 4) == 0)
805                         isa->mem = strtoul(p+4, &p, 0);
806                 else if(cistrncmp(p, "size=", 5) == 0)
807                         isa->size = strtoul(p+5, &p, 0);
808                 else if(cistrncmp(p, "freq=", 5) == 0)
809                         isa->freq = strtoul(p+5, &p, 0);
810         }
811         return 1;
812 }
813
814 int
815 cistrcmp(char *a, char *b)
816 {
817         int ac, bc;
818
819         for(;;){
820                 ac = *a++;
821                 bc = *b++;
822         
823                 if(ac >= 'A' && ac <= 'Z')
824                         ac = 'a' + (ac - 'A');
825                 if(bc >= 'A' && bc <= 'Z')
826                         bc = 'a' + (bc - 'A');
827                 ac -= bc;
828                 if(ac)
829                         return ac;
830                 if(bc == 0)
831                         break;
832         }
833         return 0;
834 }
835
836 int
837 cistrncmp(char *a, char *b, int n)
838 {
839         unsigned ac, bc;
840
841         while(n > 0){
842                 ac = *a++;
843                 bc = *b++;
844                 n--;
845
846                 if(ac >= 'A' && ac <= 'Z')
847                         ac = 'a' + (ac - 'A');
848                 if(bc >= 'A' && bc <= 'Z')
849                         bc = 'a' + (bc - 'A');
850
851                 ac -= bc;
852                 if(ac)
853                         return ac;
854                 if(bc == 0)
855                         break;
856         }
857
858         return 0;
859 }
860
861 /*
862  *  put the processor in the halt state if we've no processes to run.
863  *  an interrupt will get us going again.
864  */
865 void
866 idlehands(void)
867 {
868         if(conf.nmach == 1)
869                 halt();
870 }