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