]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/main.c
1ea96d6c7a4063640939dac9482ce236eef91239
[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 = 50;
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*MB;
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
444         /*
445          * the dynamic allocation will balance the load properly,
446          * hopefully. be careful with 32-bit overflow.
447          */
448         imagmem->maxsize = mainmem->maxsize;
449         if(p = getconf("*imagemaxmb")){
450                 imagmem->maxsize = strtol(p, nil, 0)*MB;
451                 if(imagmem->maxsize > mainmem->maxsize)
452                         imagmem->maxsize = mainmem->maxsize;
453         }
454 }
455
456 static char* mathmsg[] =
457 {
458         nil,    /* handled below */
459         "denormalized operand",
460         "division by zero",
461         "numeric overflow",
462         "numeric underflow",
463         "precision loss",
464 };
465
466 static void
467 mathnote(void)
468 {
469         int i;
470         ulong status;
471         char *msg, note[ERRMAX];
472
473         status = up->fpsave.status;
474
475         /*
476          * Some attention should probably be paid here to the
477          * exception masks and error summary.
478          */
479         msg = "unknown exception";
480         for(i = 1; i <= 5; i++){
481                 if(!((1<<i) & status))
482                         continue;
483                 msg = mathmsg[i];
484                 break;
485         }
486         if(status & 0x01){
487                 if(status & 0x40){
488                         if(status & 0x200)
489                                 msg = "stack overflow";
490                         else
491                                 msg = "stack underflow";
492                 }else
493                         msg = "invalid operation";
494         }
495         snprint(note, sizeof note, "sys: fp: %s fppc=0x%lux status=0x%lux",
496                 msg, up->fpsave.pc, status);
497         postnote(up, 1, note, NDebug);
498 }
499
500 /*
501  *  math coprocessor error
502  */
503 static void
504 matherror(Ureg *ur, void*)
505 {
506         /*
507          *  a write cycle to port 0xF0 clears the interrupt latch attached
508          *  to the error# line from the 387
509          */
510         if(!(m->cpuiddx & 0x01))
511                 outb(0xF0, 0xFF);
512
513         /*
514          *  save floating point state to check out error
515          */
516         fpenv(&up->fpsave);
517         mathnote();
518
519         if((ur->pc & 0xf0000000) == KZERO)
520                 panic("fp: status %ux fppc=0x%lux pc=0x%lux",
521                         up->fpsave.status, up->fpsave.pc, ur->pc);
522 }
523
524 /*
525  *  math coprocessor emulation fault
526  */
527 static void
528 mathemu(Ureg *ureg, void*)
529 {
530         if(up->fpstate & FPillegal){
531                 /* someone did floating point in a note handler */
532                 postnote(up, 1, "sys: floating point in note handler", NDebug);
533                 return;
534         }
535         switch(up->fpstate){
536         case FPinit:
537                 fpinit();
538                 up->fpstate = FPactive;
539                 break;
540         case FPinactive:
541                 /*
542                  * Before restoring the state, check for any pending
543                  * exceptions, there's no way to restore the state without
544                  * generating an unmasked exception.
545                  * More attention should probably be paid here to the
546                  * exception masks and error summary.
547                  */
548                 if((up->fpsave.status & ~up->fpsave.control) & 0x07F){
549                         mathnote();
550                         break;
551                 }
552                 fprestore(&up->fpsave);
553                 up->fpstate = FPactive;
554                 break;
555         case FPactive:
556                 panic("math emu pid %ld %s pc 0x%lux", 
557                         up->pid, up->text, ureg->pc);
558                 break;
559         }
560 }
561
562 /*
563  *  math coprocessor segment overrun
564  */
565 static void
566 mathover(Ureg*, void*)
567 {
568         pexit("math overrun", 0);
569 }
570
571 void
572 mathinit(void)
573 {
574         trapenable(VectorCERR, matherror, 0, "matherror");
575         if(X86FAMILY(m->cpuidax) == 3)
576                 intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
577         trapenable(VectorCNA, mathemu, 0, "mathemu");
578         trapenable(VectorCSO, mathover, 0, "mathover");
579 }
580
581 /*
582  *  set up floating point for a new process
583  */
584 void
585 procsetup(Proc *p)
586 {
587         p->fpstate = FPinit;
588         fpoff();
589
590         cycles(&p->kentry);
591         p->pcycles = -p->kentry;
592
593         memset(p->gdt, 0, sizeof(p->gdt));
594         p->ldt = nil;
595         p->nldt = 0;
596 }
597
598 void
599 procfork(Proc *p)
600 {
601         p->kentry = up->kentry;
602         p->pcycles = -p->kentry;
603
604         /* inherit user descriptors */
605         memmove(p->gdt, up->gdt, sizeof(p->gdt));
606
607         /* copy local descriptor table */
608         if(up->ldt != nil && up->nldt > 0){
609                 p->ldt = smalloc(sizeof(Segdesc) * up->nldt);
610                 memmove(p->ldt, up->ldt, sizeof(Segdesc) * up->nldt);
611                 p->nldt = up->nldt;
612         }
613 }
614
615 void
616 procrestore(Proc *p)
617 {
618         uvlong t;
619
620         if(p->kp)
621                 return;
622
623         cycles(&t);
624         p->kentry += t;
625         p->pcycles -= t;
626 }
627
628 /*
629  *  Save the mach dependent part of the process state.
630  */
631 void
632 procsave(Proc *p)
633 {
634         uvlong t;
635
636         cycles(&t);
637         p->kentry -= t;
638         p->pcycles += t;
639
640         if(p->fpstate == FPactive){
641                 if(p->state == Moribund)
642                         fpclear();
643                 else{
644                         /*
645                          * Fpsave() stores without handling pending
646                          * unmasked exeptions. Postnote() can't be called
647                          * here as sleep() already has up->rlock, so
648                          * the handling of pending exceptions is delayed
649                          * until the process runs again and generates an
650                          * emulation fault to activate the FPU.
651                          */
652                         fpsave(&p->fpsave);
653                 }
654                 p->fpstate = FPinactive;
655         }
656
657         /*
658          * While this processor is in the scheduler, the process could run
659          * on another processor and exit, returning the page tables to
660          * the free list where they could be reallocated and overwritten.
661          * When this processor eventually has to get an entry from the
662          * trashed page tables it will crash.
663          *
664          * If there's only one processor, this can't happen.
665          * You might think it would be a win not to do this in that case,
666          * especially on VMware, but it turns out not to matter.
667          */
668         mmuflushtlb(PADDR(m->pdb));
669 }
670
671 static void
672 shutdown(int ispanic)
673 {
674         int ms, once;
675
676         lock(&active);
677         if(ispanic)
678                 active.ispanic = ispanic;
679         else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
680                 active.ispanic = 0;
681         once = active.machs & (1<<m->machno);
682         /*
683          * setting exiting will make hzclock() on each processor call exit(0),
684          * which calls shutdown(0) and arch->reset(), which on mp systems is
685          * mpshutdown, from which there is no return: the processor is idled
686          * or initiates a reboot.  clearing our bit in machs avoids calling
687          * exit(0) from hzclock() on this processor.
688          */
689         active.machs &= ~(1<<m->machno);
690         active.exiting = 1;
691         unlock(&active);
692
693         if(once)
694                 iprint("cpu%d: exiting\n", m->machno);
695
696         /* wait for any other processors to shutdown */
697         spllo();
698         for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
699                 delay(TK2MS(2));
700                 if(active.machs == 0 && consactive() == 0)
701                         break;
702         }
703
704         if(active.ispanic){
705                 if(!cpuserver)
706                         for(;;)
707                                 halt();
708                 if(getconf("*debug"))
709                         delay(5*60*1000);
710                 else
711                         delay(10000);
712         }else
713                 delay(1000);
714 }
715
716 void
717 reboot(void *entry, void *code, ulong size)
718 {
719         void (*f)(ulong, ulong, ulong);
720         ulong *pdb;
721
722         writeconf();
723
724         /*
725          * the boot processor is cpu0.  execute this function on it
726          * so that the new kernel has the same cpu0.  this only matters
727          * because the hardware has a notion of which processor was the
728          * boot processor and we look at it at start up.
729          */
730         if (m->machno != 0) {
731                 procwired(up, 0);
732                 sched();
733         }
734
735         shutdown(0);
736
737         /*
738          * should be the only processor running now
739          */
740         if (m->machno != 0)
741                 print("on cpu%d (not 0)!\n", m->machno);
742         if (active.machs)
743                 print("still have active ap processors!\n");
744
745         print("shutting down...\n");
746         delay(200);
747
748         splhi();
749
750         /* turn off buffered serial console */
751         serialoq = nil;
752
753         /* shutdown devices */
754         chandevshutdown();
755         arch->introff();
756
757         /*
758          * Modify the machine page table to directly map the low 4MB of memory
759          * This allows the reboot code to turn off the page mapping
760          */
761         pdb = m->pdb;
762         pdb[PDX(0)] = pdb[PDX(KZERO)];
763         mmuflushtlb(PADDR(pdb));
764
765         /* setup reboot trampoline function */
766         f = (void*)REBOOTADDR;
767         memmove(f, rebootcode, sizeof(rebootcode));
768
769         print("rebooting...\n");
770
771         /* off we go - never to return */
772         coherence();
773         (*f)(PADDR(entry), PADDR(code), size);
774 }
775
776
777 void
778 exit(int ispanic)
779 {
780         shutdown(ispanic);
781         arch->reset();
782 }
783
784 int
785 isaconfig(char *class, int ctlrno, ISAConf *isa)
786 {
787         char cc[32], *p;
788         int i;
789
790         snprint(cc, sizeof cc, "%s%d", class, ctlrno);
791         p = getconf(cc);
792         if(p == nil)
793                 return 0;
794
795         isa->type = "";
796         isa->nopt = tokenize(p, isa->opt, NISAOPT);
797         for(i = 0; i < isa->nopt; i++){
798                 p = isa->opt[i];
799                 if(cistrncmp(p, "type=", 5) == 0)
800                         isa->type = p + 5;
801                 else if(cistrncmp(p, "port=", 5) == 0)
802                         isa->port = strtoul(p+5, &p, 0);
803                 else if(cistrncmp(p, "irq=", 4) == 0)
804                         isa->irq = strtoul(p+4, &p, 0);
805                 else if(cistrncmp(p, "dma=", 4) == 0)
806                         isa->dma = strtoul(p+4, &p, 0);
807                 else if(cistrncmp(p, "mem=", 4) == 0)
808                         isa->mem = strtoul(p+4, &p, 0);
809                 else if(cistrncmp(p, "size=", 5) == 0)
810                         isa->size = strtoul(p+5, &p, 0);
811                 else if(cistrncmp(p, "freq=", 5) == 0)
812                         isa->freq = strtoul(p+5, &p, 0);
813         }
814         return 1;
815 }
816
817 int
818 cistrcmp(char *a, char *b)
819 {
820         int ac, bc;
821
822         for(;;){
823                 ac = *a++;
824                 bc = *b++;
825         
826                 if(ac >= 'A' && ac <= 'Z')
827                         ac = 'a' + (ac - 'A');
828                 if(bc >= 'A' && bc <= 'Z')
829                         bc = 'a' + (bc - 'A');
830                 ac -= bc;
831                 if(ac)
832                         return ac;
833                 if(bc == 0)
834                         break;
835         }
836         return 0;
837 }
838
839 int
840 cistrncmp(char *a, char *b, int n)
841 {
842         unsigned ac, bc;
843
844         while(n > 0){
845                 ac = *a++;
846                 bc = *b++;
847                 n--;
848
849                 if(ac >= 'A' && ac <= 'Z')
850                         ac = 'a' + (ac - 'A');
851                 if(bc >= 'A' && bc <= 'Z')
852                         bc = 'a' + (bc - 'A');
853
854                 ac -= bc;
855                 if(ac)
856                         return ac;
857                 if(bc == 0)
858                         break;
859         }
860
861         return 0;
862 }
863
864 /*
865  *  put the processor in the halt state if we've no processes to run.
866  *  an interrupt will get us going again.
867  */
868 void
869 idlehands(void)
870 {
871         extern int nrdy;
872
873         if(conf.nmach == 1)
874                 halt();
875         else if(m->cpuidcx & Monitor)
876                 mwait(&nrdy);
877 }