]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc64/main.c
pc64: print "Plan 9" on boot, cleanup pccpu64 files
[plan9front.git] / sys / src / 9 / pc64 / 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 /*
14  * Where configuration info is left for the loaded programme.
15  * This will turn into a structure as more is done by the boot loader
16  * (e.g. why parse the .ini file twice?).
17  * There are 3584 bytes available at CONFADDR.
18  */
19 #define BOOTLINE        ((char*)CONFADDR)
20 #define BOOTLINELEN     64
21 #define BOOTARGS        ((char*)(CONFADDR+BOOTLINELEN))
22 #define BOOTARGSLEN     (4096-0x200-BOOTLINELEN)
23 #define MAXCONF         64
24
25 Conf conf;
26 char *confname[MAXCONF];
27 char *confval[MAXCONF];
28 int nconf;
29 int delaylink;
30 int idle_spin;
31
32 uchar *sp;      /* user stack of init proc */
33
34 extern void (*i8237alloc)(void);
35
36 static void
37 multibootargs(void)
38 {
39         extern ulong multibootptr;
40         ulong *multiboot;
41         char *cp, *ep;
42         ulong *m, l;
43
44         if(multibootptr == 0)
45                 return;
46
47         multiboot = (ulong*)KADDR(multibootptr);
48         /* command line */
49         if((multiboot[0] & (1<<2)) != 0)
50                 strncpy(BOOTLINE, KADDR(multiboot[4]), BOOTLINELEN-1);
51
52         cp = BOOTARGS;
53         ep = cp + BOOTARGSLEN-1;
54
55         /* memory map */
56         if((multiboot[0] & (1<<6)) != 0 && (l = multiboot[11]) >= 24){
57                 cp = seprint(cp, ep, "*e820=");
58                 m = KADDR(multiboot[12]);
59                 while(m[0] >= 20 && m[0] <= l-4){
60                         uvlong base, size;
61                         m++;
62                         base = ((uvlong)m[0] | (uvlong)m[1]<<32);
63                         size = ((uvlong)m[2] | (uvlong)m[3]<<32);
64                         cp = seprint(cp, ep, "%.1lux %.16llux %.16llux ",
65                                 m[4] & 0xF, base, base+size);
66                         l -= m[-1]+4;
67                         m = (ulong*)((uintptr)m + m[-1]);
68                 }
69                 cp[-1] = '\n';
70         }
71
72         /* plan9.ini passed as the first module */
73         if((multiboot[0] & (1<<3)) != 0 && multiboot[5] > 0){
74                 m = KADDR(multiboot[6]);
75                 l = m[1] - m[0];
76                 m = KADDR(m[0]);
77                 if(cp+l > ep)
78                         l = ep - cp;
79                 memmove(cp, m, l);
80                 cp += l;
81         }
82         *cp = 0;
83 }
84
85 static void
86 options(void)
87 {
88         long i, n;
89         char *cp, *line[MAXCONF], *p, *q;
90
91         multibootargs();
92
93         /*
94          *  parse configuration args from dos file plan9.ini
95          */
96         cp = BOOTARGS;  /* where b.com leaves its config */
97         cp[BOOTARGSLEN-1] = 0;
98
99         /*
100          * Strip out '\r', change '\t' -> ' '.
101          */
102         p = cp;
103         for(q = cp; *q; q++){
104                 if(*q == '\r')
105                         continue;
106                 if(*q == '\t')
107                         *q = ' ';
108                 *p++ = *q;
109         }
110         *p = 0;
111
112         n = getfields(cp, line, MAXCONF, 1, "\n");
113         for(i = 0; i < n; i++){
114                 if(*line[i] == '#')
115                         continue;
116                 cp = strchr(line[i], '=');
117                 if(cp == nil)
118                         continue;
119                 *cp++ = '\0';
120                 confname[nconf] = line[i];
121                 confval[nconf] = cp;
122                 nconf++;
123         }
124 }
125
126 char*
127 getconf(char *name)
128 {
129         int i;
130
131         for(i = 0; i < nconf; i++)
132                 if(cistrcmp(confname[i], name) == 0)
133                         return confval[i];
134         return 0;
135 }
136
137 static void
138 writeconf(void)
139 {
140         char *p, *q;
141         int n;
142
143         p = getconfenv();
144
145         if(waserror()) {
146                 free(p);
147                 nexterror();
148         }
149
150         /* convert to name=value\n format */
151         for(q=p; *q; q++) {
152                 q += strlen(q);
153                 *q = '=';
154                 q += strlen(q);
155                 *q = '\n';
156         }
157         n = q - p + 1;
158         if(n >= BOOTARGSLEN)
159                 error("kernel configuration too large");
160         memset(BOOTLINE, 0, BOOTLINELEN);
161         memmove(BOOTARGS, p, n);
162         poperror();
163         free(p);
164 }
165
166 void
167 confinit(void)
168 {
169         char *p;
170         int i, userpcnt;
171         ulong kpages;
172
173         if(p = getconf("service")){
174                 if(strcmp(p, "cpu") == 0)
175                         cpuserver = 1;
176                 else if(strcmp(p,"terminal") == 0)
177                         cpuserver = 0;
178         }
179
180         if(p = getconf("*kernelpercent"))
181                 userpcnt = 100 - strtol(p, 0, 0);
182         else
183                 userpcnt = 0;
184
185         conf.npage = 0;
186         for(i=0; i<nelem(conf.mem); i++)
187                 conf.npage += conf.mem[i].npage;
188
189         conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
190         if(cpuserver)
191                 conf.nproc *= 3;
192         if(conf.nproc > 2000)
193                 conf.nproc = 2000;
194         conf.nimage = 200;
195         conf.nswap = conf.nproc*80;
196         conf.nswppo = 4096;
197
198         if(cpuserver) {
199                 if(userpcnt < 10)
200                         userpcnt = 70;
201                 kpages = conf.npage - (conf.npage*userpcnt)/100;
202         } else {
203                 if(userpcnt < 10) {
204                         if(conf.npage*BY2PG < 16*MB)
205                                 userpcnt = 50;
206                         else
207                                 userpcnt = 60;
208                 }
209                 kpages = conf.npage - (conf.npage*userpcnt)/100;
210
211                 /*
212                  * Make sure terminals with low memory get at least
213                  * 4MB on the first Image chunk allocation.
214                  */
215                 if(conf.npage*BY2PG < 16*MB)
216                         imagmem->minarena = 4*MB;
217         }
218
219         /*
220          * can't go past the end of virtual memory.
221          */
222         if(kpages > ((uintptr)-KZERO)/BY2PG)
223                 kpages = ((uintptr)-KZERO)/BY2PG;
224
225         conf.upages = conf.npage - kpages;
226         conf.ialloc = (kpages/2)*BY2PG;
227
228         /*
229          * Guess how much is taken by the large permanent
230          * datastructures. Mntcache and Mntrpc are not accounted for
231          * (probably ~300KB).
232          */
233         kpages *= BY2PG;
234         kpages -= conf.nproc*sizeof(Proc)
235                 + conf.nimage*sizeof(Image)
236                 + conf.nswap
237                 + conf.nswppo*sizeof(Page*);
238         mainmem->maxsize = kpages;
239
240         /*
241          * the dynamic allocation will balance the load properly,
242          * hopefully. be careful with 32-bit overflow.
243          */
244         imagmem->maxsize = kpages - (kpages/10);
245         if(p = getconf("*imagemaxmb")){
246                 imagmem->maxsize = strtol(p, nil, 0)*MB;
247                 if(imagmem->maxsize > mainmem->maxsize)
248                         imagmem->maxsize = mainmem->maxsize;
249         }
250 }
251
252 /*
253  * The palloc.pages array can be a large chunk out of the 2GB
254  * window above KZERO, so we allocate the array from
255  * upages and map in the VMAP window before pageinit()
256  */
257 static void
258 preallocpages(void)
259 {
260         Pallocmem *pm;
261         uintptr va, base, top;
262         vlong size;
263         ulong np;
264         int i;
265
266         np = 0;
267         for(i=0; i<nelem(palloc.mem); i++){
268                 pm = &palloc.mem[i];
269                 np += pm->npage;
270         }
271         size = (uvlong)np * BY2PG;
272         size += sizeof(Page) + BY2PG;   /* round up */
273         size = (size / (sizeof(Page) + BY2PG)) * sizeof(Page);
274         size = ROUND(size, PGLSZ(1));
275
276         for(i=0; i<nelem(palloc.mem); i++){
277                 pm = &palloc.mem[i];
278                 base = ROUND(pm->base, PGLSZ(1));
279                 top = pm->base + (uvlong)pm->npage * BY2PG;
280                 if((base + size) <= VMAPSIZE && (vlong)(top - base) >= size){
281                         va = base + VMAP;
282                         pmap(m->pml4, base | PTEGLOBAL|PTEWRITE|PTEVALID, va, size);
283                         palloc.pages = (Page*)va;
284                         pm->base = base + size;
285                         pm->npage = (top - pm->base)/BY2PG;
286                         break;
287                 }
288         }
289 }
290
291 void
292 machinit(void)
293 {
294         int machno;
295         Segdesc *gdt;
296         uintptr *pml4;
297
298         machno = m->machno;
299         pml4 = m->pml4;
300         gdt = m->gdt;
301         memset(m, 0, sizeof(Mach));
302         m->machno = machno;
303         m->pml4 = pml4;
304         m->gdt = gdt;
305         m->perf.period = 1;
306
307         /*
308          * For polled uart output at boot, need
309          * a default delay constant. 100000 should
310          * be enough for a while. Cpuidentify will
311          * calculate the real value later.
312          */
313         m->loopconst = 100000;
314 }
315
316 void
317 mach0init(void)
318 {
319         conf.nmach = 1;
320
321         MACHP(0) = (Mach*)CPU0MACH;
322
323         m->machno = 0;
324         m->pml4 = (u64int*)CPU0PML4;
325         m->gdt = (Segdesc*)CPU0GDT;
326
327         machinit();
328
329         active.machs = 1;
330         active.exiting = 0;
331 }
332
333
334 uchar *
335 pusharg(char *p)
336 {
337         int n;
338
339         n = strlen(p)+1;
340         sp -= n;
341         memmove(sp, p, n);
342         return sp;
343 }
344
345 void
346 bootargs(void *base)
347 {
348         int i, ac;
349         uchar *av[32];
350         uchar **lsp;
351         char *cp = BOOTLINE;
352         char buf[64];
353
354         sp = (uchar*)base + BY2PG - sizeof(Tos);
355
356         ac = 0;
357         av[ac++] = pusharg("boot");
358
359         /* when boot is changed to only use rc, this code can go away */
360         cp[BOOTLINELEN-1] = 0;
361         buf[0] = 0;
362         if(strncmp(cp, "fd", 2) == 0){
363                 sprint(buf, "local!#f/fd%lddisk", strtol(cp+2, 0, 0));
364                 av[ac++] = pusharg(buf);
365         } else if(strncmp(cp, "sd", 2) == 0){
366                 sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3));
367                 av[ac++] = pusharg(buf);
368         } else if(strncmp(cp, "ether", 5) == 0)
369                 av[ac++] = pusharg("-n");
370
371         /* 8 byte word align stack */
372         sp = (uchar*)((uintptr)sp & ~7);
373
374         /* build argc, argv on stack */
375         sp -= (ac+1)*sizeof(sp);
376         lsp = (uchar**)sp;
377         for(i = 0; i < ac; i++)
378                 lsp[i] = av[i] + ((uintptr)(USTKTOP - BY2PG) - (uintptr)base);
379         lsp[i] = 0;
380         sp += (uintptr)(USTKTOP - BY2PG) - (uintptr)base;
381         sp -= BY2WD;
382 }
383
384 void
385 init0(void)
386 {
387         int i;
388         char buf[2*KNAMELEN];
389
390         up->nerrlab = 0;
391
392         spllo();
393
394         /*
395          * These are o.k. because rootinit is null.
396          * Then early kproc's will have a root and dot.
397          */
398         up->slash = namec("#/", Atodir, 0, 0);
399         pathclose(up->slash->path);
400         up->slash->path = newpath("/");
401         up->dot = cclone(up->slash);
402
403         chandevinit();
404
405         if(!waserror()){
406                 snprint(buf, sizeof(buf), "%s %s", arch->id, conffile);
407                 ksetenv("terminal", buf, 0);
408                 ksetenv("cputype", "amd64", 0);
409                 if(cpuserver)
410                         ksetenv("service", "cpu", 0);
411                 else
412                         ksetenv("service", "terminal", 0);
413                 for(i = 0; i < nconf; i++){
414                         if(confname[i][0] != '*')
415                                 ksetenv(confname[i], confval[i], 0);
416                         ksetenv(confname[i], confval[i], 1);
417                 }
418                 poperror();
419         }
420         kproc("alarm", alarmkproc, 0);
421
422         touser(sp);
423 }
424
425 void
426 userinit(void)
427 {
428         void *v;
429         Proc *p;
430         Segment *s;
431         Page *pg;
432
433         p = newproc();
434         p->pgrp = newpgrp();
435         p->egrp = smalloc(sizeof(Egrp));
436         p->egrp->ref = 1;
437         p->fgrp = dupfgrp(nil);
438         p->rgrp = newrgrp();
439         p->procmode = 0640;
440
441         kstrdup(&eve, "");
442         kstrdup(&p->text, "*init*");
443         kstrdup(&p->user, eve);
444
445         procsetup(p);
446
447         /*
448          * Kernel Stack
449          *
450          * N.B. make sure there's enough space for syscall to check
451          *      for valid args and 
452          *      8 bytes for gotolabel's return PC
453          */
454         p->sched.pc = (uintptr)init0;
455         p->sched.sp = (uintptr)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD);
456
457         /* temporarily set up for kmap() */
458         up = p;
459
460         /*
461          * User Stack
462          */
463         s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
464         p->seg[SSEG] = s;
465         pg = newpage(0, 0, USTKTOP-BY2PG);
466         v = kmap(pg);
467         memset(v, 0, BY2PG);
468         segpage(s, pg);
469         bootargs(v);
470         kunmap(v);
471
472         /*
473          * Text
474          */
475         s = newseg(SG_TEXT, UTZERO, 1);
476         s->flushme++;
477         p->seg[TSEG] = s;
478         pg = newpage(0, 0, UTZERO);
479         memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
480         segpage(s, pg);
481         v = kmap(pg);
482         memset(v, 0, BY2PG);
483         memmove(v, initcode, sizeof initcode);
484         kunmap(v);
485
486         /* free kmap */
487         mmurelease(p);
488         up = nil;
489
490         ready(p);
491 }
492
493 void
494 main()
495 {
496         mach0init();
497         options();
498         ioinit();
499         i8250console();
500         quotefmtinstall();
501         screeninit();
502         print("\nPlan 9\n");
503         trapinit0();
504         kbdinit();
505         i8253init();
506         cpuidentify();
507         meminit();
508         confinit();
509         archinit();
510         xinit();
511         if(i8237alloc != nil)
512                 i8237alloc();
513         trapinit();
514         printinit();
515         cpuidprint();
516         mmuinit();
517         if(arch->intrinit)
518                 arch->intrinit();
519         timersinit();
520         mathinit();
521         kbdenable();
522         if(arch->clockenable)
523                 arch->clockenable();
524         procinit0();
525         initseg();
526         if(delaylink){
527                 bootlinks();
528                 pcimatch(0, 0, 0);
529         }else
530                 links();
531         conf.monitor = 1;
532         chandevreset();
533         preallocpages();
534         pageinit();
535         swapinit();
536         userinit();
537         active.thunderbirdsarego = 1;
538         schedinit();
539 }
540
541 static void
542 shutdown(int ispanic)
543 {
544         int ms, once;
545
546         lock(&active);
547         if(ispanic)
548                 active.ispanic = ispanic;
549         else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
550                 active.ispanic = 0;
551         once = active.machs & (1<<m->machno);
552         /*
553          * setting exiting will make hzclock() on each processor call exit(0),
554          * which calls shutdown(0) and arch->reset(), which on mp systems is
555          * mpshutdown, from which there is no return: the processor is idled
556          * or initiates a reboot.  clearing our bit in machs avoids calling
557          * exit(0) from hzclock() on this processor.
558          */
559         active.machs &= ~(1<<m->machno);
560         active.exiting = 1;
561         unlock(&active);
562
563         if(once)
564                 iprint("cpu%d: exiting\n", m->machno);
565
566         /* wait for any other processors to shutdown */
567         spllo();
568         for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
569                 delay(TK2MS(2));
570                 if(active.machs == 0 && consactive() == 0)
571                         break;
572         }
573
574         if(active.ispanic){
575                 if(!cpuserver)
576                         for(;;)
577                                 halt();
578                 if(getconf("*debug"))
579                         delay(5*60*1000);
580                 else
581                         delay(10000);
582         }
583 }
584
585 void
586 exit(int ispanic)
587 {
588         shutdown(ispanic);
589         arch->reset();
590 }
591
592 void
593 reboot(void *entry, void *code, ulong size)
594 {
595         void (*f)(uintptr, uintptr, ulong);
596
597         writeconf();
598
599         /*
600          * the boot processor is cpu0.  execute this function on it
601          * so that the new kernel has the same cpu0.  this only matters
602          * because the hardware has a notion of which processor was the
603          * boot processor and we look at it at start up.
604          */
605         if (m->machno != 0) {
606                 procwired(up, 0);
607                 sched();
608         }
609         shutdown(0);
610
611         iprint("shutting down...\n");
612         delay(200);
613
614         splhi();
615
616         /* turn off buffered serial console */
617         serialoq = nil;
618
619         /* shutdown devices */
620         chandevshutdown();
621         arch->introff();
622
623         /*
624          * This allows the reboot code to turn off the page mapping
625          */
626         *mmuwalk(m->pml4, 0, 3, 0) = *mmuwalk(m->pml4, KZERO, 3, 0);
627         *mmuwalk(m->pml4, 0, 2, 0) = *mmuwalk(m->pml4, KZERO, 2, 0);
628         mmuflushtlb();
629
630         /* setup reboot trampoline function */
631         f = (void*)REBOOTADDR;
632         memmove(f, rebootcode, sizeof(rebootcode));
633
634         /* off we go - never to return */
635         coherence();
636         (*f)((uintptr)entry & ~0xF0000000UL, (uintptr)PADDR(code), size);
637 }
638
639 /*
640  * SIMD Floating Point.
641  * Assembler support to get at the individual instructions
642  * is in l.s.
643  * There are opportunities to be lazier about saving and
644  * restoring the state and allocating the storage needed.
645  */
646 extern void _clts(void);
647 extern void _fldcw(u16int);
648 extern void _fnclex(void);
649 extern void _fninit(void);
650 extern void _fxrstor(Fxsave*);
651 extern void _fxsave(Fxsave*);
652 extern void _fwait(void);
653 extern void _ldmxcsr(u32int);
654 extern void _stts(void);
655
656 /*
657  * not used, AMD64 mandated SSE
658  */
659 void
660 fpx87save(FPsave*)
661 {
662 }
663 void
664 fpx87restore(FPsave*)
665 {
666 }
667
668 void
669 fpssesave(FPsave *fps)
670 {
671         Fxsave *fx = (Fxsave*)ROUND(((uintptr)fps), FPalign);
672
673         _fxsave(fx);
674         _stts();
675         if(fx != (Fxsave*)fps)
676                 memmove((Fxsave*)fps, fx, sizeof(Fxsave));
677 }
678 void
679 fpsserestore(FPsave *fps)
680 {
681         Fxsave *fx = (Fxsave*)ROUND(((uintptr)fps), FPalign);
682
683         if(fx != (Fxsave*)fps)
684                 memmove(fx, (Fxsave*)fps, sizeof(Fxsave));
685         _clts();
686         _fxrstor(fx);
687 }
688
689 static char* mathmsg[] =
690 {
691         nil,    /* handled below */
692         "denormalized operand",
693         "division by zero",
694         "numeric overflow",
695         "numeric underflow",
696         "precision loss",
697 };
698
699 static void
700 mathnote(ulong status, uintptr pc)
701 {
702         char *msg, note[ERRMAX];
703         int i;
704
705         /*
706          * Some attention should probably be paid here to the
707          * exception masks and error summary.
708          */
709         msg = "unknown exception";
710         for(i = 1; i <= 5; i++){
711                 if(!((1<<i) & status))
712                         continue;
713                 msg = mathmsg[i];
714                 break;
715         }
716         if(status & 0x01){
717                 if(status & 0x40){
718                         if(status & 0x200)
719                                 msg = "stack overflow";
720                         else
721                                 msg = "stack underflow";
722                 }else
723                         msg = "invalid operation";
724         }
725         snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=0x%lux",
726                 msg, pc, status);
727         postnote(up, 1, note, NDebug);
728 }
729
730 /*
731  *  math coprocessor error
732  */
733 static void
734 matherror(Ureg*, void*)
735 {
736         /*
737          * Save FPU state to check out the error.
738          */
739         fpsave(&up->fpsave);
740         up->fpstate = FPinactive;
741         mathnote(up->fpsave.fsw, up->fpsave.rip);
742 }
743
744 /*
745  *  SIMD error
746  */
747 static void
748 simderror(Ureg *ureg, void*)
749 {
750         fpsave(&up->fpsave);
751         up->fpstate = FPinactive;
752         mathnote(up->fpsave.mxcsr & 0x3f, ureg->pc);
753 }
754
755 /*
756  *  math coprocessor emulation fault
757  */
758 static void
759 mathemu(Ureg *ureg, void*)
760 {
761         ulong status, control;
762
763         if(up->fpstate & FPillegal){
764                 /* someone did floating point in a note handler */
765                 postnote(up, 1, "sys: floating point in note handler", NDebug);
766                 return;
767         }
768         switch(up->fpstate){
769         case FPinit:
770                 /*
771                  * A process tries to use the FPU for the
772                  * first time and generates a 'device not available'
773                  * exception.
774                  * Turn the FPU on and initialise it for use.
775                  * Set the precision and mask the exceptions
776                  * we don't care about from the generic Mach value.
777                  */
778                 _clts();
779                 _fninit();
780                 _fwait();
781                 _fldcw(0x0232);
782                 _ldmxcsr(0x1900);
783                 up->fpstate = FPactive;
784                 break;
785         case FPinactive:
786                 /*
787                  * Before restoring the state, check for any pending
788                  * exceptions, there's no way to restore the state without
789                  * generating an unmasked exception.
790                  * More attention should probably be paid here to the
791                  * exception masks and error summary.
792                  */
793                 status = up->fpsave.fsw;
794                 control = up->fpsave.fcw;
795                 if((status & ~control) & 0x07F){
796                         mathnote(status, up->fpsave.rip);
797                         break;
798                 }
799                 fprestore(&up->fpsave);
800                 up->fpstate = FPactive;
801                 break;
802         case FPactive:
803                 panic("math emu pid %ld %s pc %#p", 
804                         up->pid, up->text, ureg->pc);
805                 break;
806         }
807 }
808
809 /*
810  *  math coprocessor segment overrun
811  */
812 static void
813 mathover(Ureg*, void*)
814 {
815         pexit("math overrun", 0);
816 }
817
818 void
819 mathinit(void)
820 {
821         trapenable(VectorCERR, matherror, 0, "matherror");
822         if(X86FAMILY(m->cpuidax) == 3)
823                 intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror");
824         trapenable(VectorCNA, mathemu, 0, "mathemu");
825         trapenable(VectorCSO, mathover, 0, "mathover");
826         trapenable(VectorSIMD, simderror, 0, "simderror");
827 }
828
829 void
830 procsetup(Proc *p)
831 {
832         p->fpstate = FPinit;
833         _stts();
834         cycles(&p->kentry);
835         p->pcycles = -p->kentry;
836 }
837
838 void
839 procfork(Proc *p)
840 {
841         int s;
842
843         p->kentry = up->kentry;
844         p->pcycles = -p->kentry;
845
846         /* save floating point state */
847         s = splhi();
848         switch(up->fpstate & ~FPillegal){
849         case FPactive:
850                 fpsave(&up->fpsave);
851                 up->fpstate = FPinactive;
852         case FPinactive:
853                 p->fpsave = up->fpsave;
854                 p->fpstate = FPinactive;
855         }
856         splx(s);
857
858 }
859
860 void
861 procrestore(Proc *p)
862 {
863         uvlong t;
864
865         if(p->kp)
866                 return;
867
868         cycles(&t);
869         p->kentry += t;
870         p->pcycles -= t;
871 }
872
873 void
874 procsave(Proc *p)
875 {
876         uvlong t;
877
878         cycles(&t);
879         p->kentry -= t;
880         p->pcycles += t;
881
882         if(p->fpstate == FPactive){
883                 if(p->state == Moribund){
884                         _clts();
885                         _fnclex();
886                         _stts();
887                 }
888                 else{
889                         /*
890                          * Fpsave() stores without handling pending
891                          * unmasked exeptions. Postnote() can't be called
892                          * here as sleep() already has up->rlock, so
893                          * the handling of pending exceptions is delayed
894                          * until the process runs again and generates an
895                          * emulation fault to activate the FPU.
896                          */
897                         fpsave(&p->fpsave);
898                 }
899                 p->fpstate = FPinactive;
900         }
901
902         /*
903          * While this processor is in the scheduler, the process could run
904          * on another processor and exit, returning the page tables to
905          * the free list where they could be reallocated and overwritten.
906          * When this processor eventually has to get an entry from the
907          * trashed page tables it will crash.
908          *
909          * If there's only one processor, this can't happen.
910          * You might think it would be a win not to do this in that case,
911          * especially on VMware, but it turns out not to matter.
912          */
913         mmuflushtlb();
914 }