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