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