]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/main.c
kernel: pass bootargs also in multiboot command line, retire the bootline mechanism...
[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 Conf conf;
15
16 int delaylink;
17 int idle_spin;
18
19 extern void (*i8237alloc)(void);
20 extern void bootscreeninit(void);
21 extern void multibootdebug(void);
22
23 void
24 main(void)
25 {
26         mach0init();
27         bootargsinit();
28         ioinit();
29         i8250console();
30         quotefmtinstall();
31         screeninit();
32
33         print("\nPlan 9\n");
34         
35         trapinit0();
36         i8253init();
37         cpuidentify();
38         meminit();
39         confinit();
40         xinit();
41         archinit();
42         bootscreeninit();
43         if(i8237alloc != nil)
44                 i8237alloc();
45         trapinit();
46         printinit();
47         cpuidprint();
48         mmuinit();
49         if(arch->intrinit)      /* launches other processors on an mp */
50                 arch->intrinit();
51         timersinit();
52         mathinit();
53         if(arch->clockenable)
54                 arch->clockenable();
55         procinit0();
56         initseg();
57         if(delaylink){
58                 bootlinks();
59                 pcimatch(0, 0, 0);
60         }else
61                 links();
62         chandevreset();
63         netconsole();
64         pageinit();
65         swapinit();
66         userinit();
67         schedinit();
68 }
69
70 void
71 mach0init(void)
72 {
73         conf.nmach = 1;
74         MACHP(0) = (Mach*)CPU0MACH;
75         m->pdb = (ulong*)CPU0PDB;
76         m->gdt = (Segdesc*)CPU0GDT;
77
78         machinit();
79
80         active.machs[0] = 1;
81         active.exiting = 0;
82 }
83
84 void
85 machinit(void)
86 {
87         int machno;
88         ulong *pdb;
89         Segdesc *gdt;
90
91         machno = m->machno;
92         pdb = m->pdb;
93         gdt = m->gdt;
94         memset(m, 0, sizeof(Mach));
95         m->machno = machno;
96         m->pdb = pdb;
97         m->gdt = gdt;
98         m->perf.period = 1;
99
100         /*
101          * For polled uart output at boot, need
102          * a default delay constant. 100000 should
103          * be enough for a while. Cpuidentify will
104          * calculate the real value later.
105          */
106         m->loopconst = 100000;
107 }
108
109 void
110 init0(void)
111 {
112         char buf[2*KNAMELEN], **sp;
113
114         up->nerrlab = 0;
115
116         spllo();
117
118         /*
119          * These are o.k. because rootinit is null.
120          * Then early kproc's will have a root and dot.
121          */
122         up->slash = namec("#/", Atodir, 0, 0);
123         pathclose(up->slash->path);
124         up->slash->path = newpath("/");
125         up->dot = cclone(up->slash);
126
127         chandevinit();
128
129         if(!waserror()){
130                 snprint(buf, sizeof(buf), "%s %s", arch->id, conffile);
131                 ksetenv("terminal", buf, 0);
132                 ksetenv("cputype", "386", 0);
133                 if(cpuserver)
134                         ksetenv("service", "cpu", 0);
135                 else
136                         ksetenv("service", "terminal", 0);
137                 setconfenv();
138                 poperror();
139         }
140         kproc("alarm", alarmkproc, 0);
141
142         sp = (char**)(USTKTOP - sizeof(Tos) - 8 - sizeof(sp[0])*4);
143         sp[3] = sp[2] = nil;
144         strcpy(sp[1] = (char*)&sp[4], "boot");
145         sp[0] = nil;
146         touser(sp);
147 }
148
149 void
150 userinit(void)
151 {
152         void *v;
153         Proc *p;
154         Segment *s;
155         Page *pg;
156
157         p = newproc();
158         p->pgrp = newpgrp();
159         p->egrp = smalloc(sizeof(Egrp));
160         p->egrp->ref = 1;
161         p->fgrp = dupfgrp(nil);
162         p->rgrp = newrgrp();
163         p->procmode = 0640;
164
165         kstrdup(&eve, "");
166         kstrdup(&p->text, "*init*");
167         kstrdup(&p->user, eve);
168
169         procsetup(p);
170
171         /*
172          * Kernel Stack
173          *
174          * N.B. make sure there's enough space for syscall to check
175          *      for valid args and 
176          *      4 bytes for gotolabel's return PC
177          */
178         p->sched.pc = (ulong)init0;
179         p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD);
180
181         /*
182          * User Stack
183          *
184          * N.B. cannot call newpage() with clear=1, because pc kmap
185          * requires up != nil.  use tmpmap instead.
186          */
187         s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
188         p->seg[SSEG] = s;
189         pg = newpage(0, 0, USTKTOP-BY2PG);
190         segpage(s, pg);
191         v = tmpmap(pg);
192         memset(v, 0, BY2PG);
193         tmpunmap(v);
194
195         /*
196          * Text
197          */
198         s = newseg(SG_TEXT, UTZERO, 1);
199         s->flushme++;
200         p->seg[TSEG] = s;
201         pg = newpage(0, 0, UTZERO);
202         pg->txtflush = ~0;
203         segpage(s, pg);
204         v = tmpmap(pg);
205         memset(v, 0, BY2PG);
206         memmove(v, initcode, sizeof initcode);
207         tmpunmap(v);
208
209         ready(p);
210 }
211
212 void
213 confinit(void)
214 {
215         char *p;
216         int i, userpcnt;
217         ulong kpages;
218
219         if(p = getconf("service")){
220                 if(strcmp(p, "cpu") == 0)
221                         cpuserver = 1;
222                 else if(strcmp(p,"terminal") == 0)
223                         cpuserver = 0;
224         }
225
226         if(p = getconf("*kernelpercent"))
227                 userpcnt = 100 - strtol(p, 0, 0);
228         else
229                 userpcnt = 0;
230
231         conf.npage = 0;
232         for(i=0; i<nelem(conf.mem); i++)
233                 conf.npage += conf.mem[i].npage;
234
235         conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
236         if(cpuserver)
237                 conf.nproc *= 3;
238         if(conf.nproc > 2000)
239                 conf.nproc = 2000;
240         conf.nimage = 200;
241         conf.nswap = conf.nproc*80;
242         conf.nswppo = 4096;
243
244         if(cpuserver) {
245                 if(userpcnt < 10)
246                         userpcnt = 70;
247                 kpages = conf.npage - (conf.npage*userpcnt)/100;
248                 conf.nimage = conf.nproc;
249
250                 /*
251                  * Hack for the big boys. Only good while physmem < 4GB.
252                  * Give the kernel fixed max + enough to allocate the
253                  * page pool.
254                  * This is an overestimate as conf.upages < conf.npages.
255                  * The patch of nimage is a band-aid, scanning the whole
256                  * page list in imagereclaim just takes too long.
257                  */
258                 if(getconf("*imagemaxmb") == 0)
259                 if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){
260                         kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG;
261                         kpages += (conf.nproc*KSTACK)/BY2PG;
262                 }
263         } else {
264                 if(userpcnt < 10) {
265                         if(conf.npage*BY2PG < 16*MB)
266                                 userpcnt = 50;
267                         else
268                                 userpcnt = 60;
269                 }
270                 kpages = conf.npage - (conf.npage*userpcnt)/100;
271
272                 /*
273                  * Make sure terminals with low memory get at least
274                  * 4MB on the first Image chunk allocation.
275                  */
276                 if(conf.npage*BY2PG < 16*MB)
277                         imagmem->minarena = 4*MB;
278         }
279
280         /*
281          * can't go past the end of virtual memory
282          * (ulong)-KZERO is 2^32 - KZERO
283          */
284         if(kpages > ((ulong)-KZERO)/BY2PG)
285                 kpages = ((ulong)-KZERO)/BY2PG;
286
287         conf.upages = conf.npage - kpages;
288         conf.ialloc = (kpages/2)*BY2PG;
289
290         /*
291          * Guess how much is taken by the large permanent
292          * datastructures. Mntcache and Mntrpc are not accounted for.
293          */
294         kpages *= BY2PG;
295         kpages -= conf.upages*sizeof(Page)
296                 + conf.nproc*sizeof(Proc)
297                 + conf.nimage*sizeof(Image)
298                 + conf.nswap
299                 + conf.nswppo*sizeof(Page*);
300         mainmem->maxsize = kpages;
301
302         /*
303          * the dynamic allocation will balance the load properly,
304          * hopefully. be careful with 32-bit overflow.
305          */
306         imagmem->maxsize = kpages - (kpages/10);
307         if(p = getconf("*imagemaxmb")){
308                 imagmem->maxsize = strtol(p, nil, 0)*MB;
309                 if(imagmem->maxsize > mainmem->maxsize)
310                         imagmem->maxsize = mainmem->maxsize;
311         }
312 }
313
314 /*
315  * we keep FPsave structure in sse format emulating FXSAVE / FXRSTOR
316  * instructions for legacy x87 fpu.
317  *
318  * Note that fpx87restore() and fpxsserestore() do modify the FPsave
319  * data structure for conversion / realignment shuffeling. this means
320  * that p->fpsave is only valid when p->fpstate == FPinactive.
321  */
322 void
323 fpx87save(FPsave *fps)
324 {
325         ushort tag;
326
327         fpx87save0(fps);
328
329         /*
330          * convert x87 tag word to fxsave tag byte:
331          * 00, 01, 10 -> 1, 11 -> 0
332          */
333         tag = ~fps->tag;
334         tag = (tag | (tag >> 1)) & 0x5555;
335         tag = (tag | (tag >> 1)) & 0x3333;
336         tag = (tag | (tag >> 2)) & 0x0F0F;
337         tag = (tag | (tag >> 4)) & 0x00FF;
338
339         /* NOP fps->fcw = fps->control; */
340         fps->fsw = fps->status;
341         fps->ftw = tag;
342         fps->fop = fps->opcode;
343         fps->fpuip = fps->pc;
344         fps->cs = fps->selector;
345         fps->fpudp = fps->operand;
346         fps->ds = fps->oselector;
347
348 #define MOVA(d,s) \
349         *((ushort*)(d+8)) = *((ushort*)(s+8)), \
350         *((ulong*)(d+4)) = *((ulong*)(s+4)), \
351         *((ulong*)(d)) = *((ulong*)(s))
352
353         MOVA(fps->xregs+0x70, fps->regs+70);
354         MOVA(fps->xregs+0x60, fps->regs+60);
355         MOVA(fps->xregs+0x50, fps->regs+50);
356         MOVA(fps->xregs+0x40, fps->regs+40);
357         MOVA(fps->xregs+0x30, fps->regs+30);
358         MOVA(fps->xregs+0x20, fps->regs+20);
359         MOVA(fps->xregs+0x10, fps->regs+10);
360         MOVA(fps->xregs+0x00, fps->regs+00);
361
362 #undef MOVA
363
364 #define CLR6(d) \
365         *((ulong*)(d)) = 0, \
366         *((ushort*)(d+4)) = 0
367
368         CLR6(fps->xregs+0x70+10);
369         CLR6(fps->xregs+0x60+10);
370         CLR6(fps->xregs+0x50+10);
371         CLR6(fps->xregs+0x40+10);
372         CLR6(fps->xregs+0x30+10);
373         CLR6(fps->xregs+0x20+10);
374         CLR6(fps->xregs+0x10+10);
375         CLR6(fps->xregs+0x00+10);
376
377 #undef CLR6
378
379         fps->rsrvd1 = fps->rsrvd2 = fps->mxcsr = fps->mxcsr_mask = 0;
380 }
381
382 void
383 fpx87restore(FPsave *fps)
384 {
385         ushort msk, tos, tag, *reg;
386
387         /* convert fxsave tag byte to x87 tag word */
388         tag = 0;
389         tos = 7 - ((fps->fsw >> 11) & 7);
390         for(msk = 0x80; msk != 0; tos--, msk >>= 1){
391                 tag <<= 2;
392                 if((fps->ftw & msk) != 0){
393                         reg = (ushort*)&fps->xregs[(tos & 7) << 4];
394                         switch(reg[4] & 0x7fff){
395                         case 0x0000:
396                                 if((reg[0] | reg[1] | reg[2] | reg[3]) == 0){
397                                         tag |= 1;       /* 01 zero */
398                                         break;
399                                 }
400                                 /* no break */
401                         case 0x7fff:
402                                 tag |= 2;               /* 10 special */
403                                 break;
404                         default:
405                                 if((reg[3] & 0x8000) == 0)
406                                         break;          /* 00 valid */
407                                 tag |= 2;               /* 10 special */
408                                 break;
409                         }
410                 }else{
411                         tag |= 3;                       /* 11 empty */
412                 }
413         }
414
415 #define MOVA(d,s) \
416         *((ulong*)(d)) = *((ulong*)(s)), \
417         *((ulong*)(d+4)) = *((ulong*)(s+4)), \
418         *((ushort*)(d+8)) = *((ushort*)(s+8))
419
420         MOVA(fps->regs+00, fps->xregs+0x00);
421         MOVA(fps->regs+10, fps->xregs+0x10);
422         MOVA(fps->regs+20, fps->xregs+0x20);
423         MOVA(fps->regs+30, fps->xregs+0x30);
424         MOVA(fps->regs+40, fps->xregs+0x40);
425         MOVA(fps->regs+50, fps->xregs+0x50);
426         MOVA(fps->regs+60, fps->xregs+0x60);
427         MOVA(fps->regs+70, fps->xregs+0x70);
428
429 #undef MOVA
430
431         fps->oselector = fps->ds;
432         fps->operand = fps->fpudp;
433         fps->opcode = fps->fop & 0x7ff;
434         fps->selector = fps->cs;
435         fps->pc = fps->fpuip;
436         fps->tag = tag;
437         fps->status = fps->fsw;
438         /* NOP fps->control = fps->fcw;  */
439
440         fps->r1 = fps->r2 = fps->r3 = fps->r4 = 0;
441
442         fpx87restore0(fps);
443 }
444
445 /*
446  * sse fp save and restore buffers have to be 16-byte (FPalign) aligned,
447  * so we shuffle the data up and down as needed or make copies.
448  */
449 void
450 fpssesave(FPsave *fps)
451 {
452         FPsave *afps;
453
454         afps = (FPsave *)ROUND(((uintptr)fps), FPalign);
455         fpssesave0(afps);
456         if(fps != afps)  /* not aligned? shuffle down from aligned buffer */
457                 memmove(fps, afps, sizeof(FPssestate) - FPalign);
458 }
459
460 void
461 fpsserestore(FPsave *fps)
462 {
463         FPsave *afps;
464
465         afps = (FPsave *)ROUND(((uintptr)fps), FPalign);
466         if(fps != afps)  /* shuffle up to make aligned */
467                 memmove(afps, fps, sizeof(FPssestate) - FPalign);
468         fpsserestore0(afps);
469 }
470
471 static char* mathmsg[] =
472 {
473         nil,    /* handled below */
474         "denormalized operand",
475         "division by zero",
476         "numeric overflow",
477         "numeric underflow",
478         "precision loss",
479 };
480
481 static void
482 mathnote(ulong status, ulong pc)
483 {
484         char *msg, note[ERRMAX];
485         int i;
486
487         /*
488          * Some attention should probably be paid here to the
489          * exception masks and error summary.
490          */
491         msg = "unknown exception";
492         for(i = 1; i <= 5; i++){
493                 if(!((1<<i) & status))
494                         continue;
495                 msg = mathmsg[i];
496                 break;
497         }
498         if(status & 0x01){
499                 if(status & 0x40){
500                         if(status & 0x200)
501                                 msg = "stack overflow";
502                         else
503                                 msg = "stack underflow";
504                 }else
505                         msg = "invalid operation";
506         }
507         snprint(note, sizeof note, "sys: fp: %s fppc=0x%lux status=0x%lux",
508                 msg, pc, status);
509         postnote(up, 1, note, NDebug);
510 }
511
512 /*
513  *  math coprocessor error
514  */
515 static void
516 matherror(Ureg*, void*)
517 {
518         /*
519          *  a write cycle to port 0xF0 clears the interrupt latch attached
520          *  to the error# line from the 387
521          */
522         if(!(m->cpuiddx & Fpuonchip))
523                 outb(0xF0, 0xFF);
524
525         /*
526          *  get floating point state to check out error
527          */
528         fpsave(&up->fpsave);
529         up->fpstate = FPinactive;
530         mathnote(up->fpsave.fsw, up->fpsave.fpuip);
531 }
532
533 /*
534  *  SIMD error
535  */
536 static void
537 simderror(Ureg *ureg, void*)
538 {
539         fpsave(&up->fpsave);
540         up->fpstate = FPinactive;
541         mathnote(up->fpsave.mxcsr & 0x3f, ureg->pc);
542 }
543
544 /*
545  *  math coprocessor emulation fault
546  */
547 static void
548 mathemu(Ureg *ureg, void*)
549 {
550         ulong status, control;
551
552         if(up->fpstate & FPillegal){
553                 /* someone did floating point in a note handler */
554                 postnote(up, 1, "sys: floating point in note handler", NDebug);
555                 return;
556         }
557         switch(up->fpstate){
558         case FPinit:
559                 fpinit();
560                 if(fpsave == fpssesave)
561                         ldmxcsr(0);     /* no simd exceptions on 386 */
562                 up->fpstate = FPactive;
563                 break;
564         case FPinactive:
565                 /*
566                  * Before restoring the state, check for any pending
567                  * exceptions, there's no way to restore the state without
568                  * generating an unmasked exception.
569                  * More attention should probably be paid here to the
570                  * exception masks and error summary.
571                  */
572                 status = up->fpsave.fsw;
573                 control = up->fpsave.fcw;
574                 if((status & ~control) & 0x07F){
575                         mathnote(status, up->fpsave.fpuip);
576                         break;
577                 }
578                 fprestore(&up->fpsave);
579                 up->fpstate = FPactive;
580                 break;
581         case FPactive:
582                 panic("math emu pid %ld %s pc 0x%lux", 
583                         up->pid, up->text, ureg->pc);
584                 break;
585         }
586 }
587
588 /*
589  *  math coprocessor segment overrun
590  */
591 static void
592 mathover(Ureg*, void*)
593 {
594         pexit("math overrun", 0);
595 }
596
597 void
598 mathinit(void)
599 {
600         trapenable(VectorCERR, matherror, 0, "matherror");
601         if(X86FAMILY(m->cpuidax) == 3)
602                 intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
603         trapenable(VectorCNA, mathemu, 0, "mathemu");
604         trapenable(VectorCSO, mathover, 0, "mathover");
605         trapenable(VectorSIMD, simderror, 0, "simderror");
606 }
607
608 /*
609  *  set up floating point for a new process
610  */
611 void
612 procsetup(Proc *p)
613 {
614         p->fpstate = FPinit;
615         fpoff();
616
617         cycles(&p->kentry);
618         p->pcycles = -p->kentry;
619
620         memset(p->gdt, 0, sizeof(p->gdt));
621         p->ldt = nil;
622         p->nldt = 0;
623         
624         memset(p->dr, 0, sizeof(p->dr));
625 }
626
627 void
628 procfork(Proc *p)
629 {
630         int s;
631
632         p->kentry = up->kentry;
633         p->pcycles = -p->kentry;
634
635         /* inherit user descriptors */
636         memmove(p->gdt, up->gdt, sizeof(p->gdt));
637
638         /* copy local descriptor table */
639         if(up->ldt != nil && up->nldt > 0){
640                 p->ldt = smalloc(sizeof(Segdesc) * up->nldt);
641                 memmove(p->ldt, up->ldt, sizeof(Segdesc) * up->nldt);
642                 p->nldt = up->nldt;
643         }
644
645         /* save floating point state */
646         s = splhi();
647         switch(up->fpstate & ~FPillegal){
648         case FPactive:
649                 fpsave(&up->fpsave);
650                 up->fpstate = FPinactive;
651         case FPinactive:
652                 p->fpsave = up->fpsave;
653                 p->fpstate = FPinactive;
654         }
655         
656         /* clear debug registers */
657         memset(p->dr, 0, sizeof(p->dr));
658         splx(s);
659 }
660
661 void
662 procrestore(Proc *p)
663 {
664         uvlong t;
665         
666         if(p->dr[7] != 0){
667                 m->dr7 = p->dr[7];
668                 putdr(p->dr);
669         }
670
671         if(p->kp)
672                 return;
673
674         cycles(&t);
675         p->kentry += t;
676         p->pcycles -= t;
677 }
678
679 /*
680  *  Save the mach dependent part of the process state.
681  */
682 void
683 procsave(Proc *p)
684 {
685         uvlong t;
686         
687         /* we could just always putdr7(0) but accessing DR7 might be slow in a VM */
688         if(m->dr7 != 0){
689                 m->dr7 = 0;
690                 putdr7(0);
691         }
692
693         cycles(&t);
694         p->kentry -= t;
695         p->pcycles += t;
696
697         if(p->fpstate == FPactive){
698                 if(p->state == Moribund)
699                         fpclear();
700                 else{
701                         /*
702                          * Fpsave() stores without handling pending
703                          * unmasked exeptions. Postnote() can't be called
704                          * here as sleep() already has up->rlock, so
705                          * the handling of pending exceptions is delayed
706                          * until the process runs again and generates an
707                          * emulation fault to activate the FPU.
708                          */
709                         fpsave(&p->fpsave);
710                 }
711                 p->fpstate = FPinactive;
712         }
713
714         /*
715          * While this processor is in the scheduler, the process could run
716          * on another processor and exit, returning the page tables to
717          * the free list where they could be reallocated and overwritten.
718          * When this processor eventually has to get an entry from the
719          * trashed page tables it will crash.
720          *
721          * If there's only one processor, this can't happen.
722          * You might think it would be a win not to do this in that case,
723          * especially on VMware, but it turns out not to matter.
724          */
725         mmuflushtlb(PADDR(m->pdb));
726 }
727
728 void
729 reboot(void *entry, void *code, ulong size)
730 {
731         void (*f)(ulong, ulong, ulong);
732         ulong *pdb;
733
734         writeconf();
735
736         /*
737          * the boot processor is cpu0.  execute this function on it
738          * so that the new kernel has the same cpu0.  this only matters
739          * because the hardware has a notion of which processor was the
740          * boot processor and we look at it at start up.
741          */
742         if (m->machno != 0) {
743                 procwired(up, 0);
744                 sched();
745         }
746         cpushutdown();
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         /* off we go - never to return */
770         coherence();
771         (*f)((ulong)entry & ~0xF0000000UL, PADDR(code), size);
772 }
773
774
775 void
776 exit(int)
777 {
778         cpushutdown();
779         arch->reset();
780 }