main.c 15 KB

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