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