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