main.c 15 KB

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