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