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