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