segdescpatch/ 775 0 0 0 11342263221 146255ustar00cinap_lenrekcinap_lenreksegdescpatch/kernel.diff 664 0 0 52677 11343343551 17006ustar00cinap_lenrekcinap_lenrek/n/sources/plan9/sys/src/9/pc/fns.h:83,88 - pc/fns.h:83,89 void kbdinit(void); #define kmapinval() void lgdt(ushort[3]); + void lldt(ulong); void lidt(ushort[3]); void links(void); void ltr(ulong); /n/sources/plan9/sys/src/9/pc/fns.h:142,147 - pc/fns.h:143,149 void procrestore(Proc*); void procsave(Proc*); void procsetup(Proc*); + void procfork(Proc*); void putcr0(ulong); void putcr3(ulong); void putcr4(ulong); /n/sources/plan9/sys/src/9/pc/fns.h:166,172 - pc/fns.h:168,174 ulong upaalloc(int, int); void upafree(ulong, int); void upareserve(ulong, int); - #define userureg(ur) (((ur)->cs & 0xFFFF) == UESEL) + #define userureg(ur) (((ur)->cs & 3) == 3) void vectortable(void); void* vmap(ulong, int); int vmapsync(ulong); /n/sources/plan9/sys/src/9/pc/dat.h:108,113 - pc/dat.h:108,119 int nuart; /* number of uart devices */ }; + struct Segdesc + { + ulong d0; + ulong d1; + }; + /* * MMU stuff in proc */ /n/sources/plan9/sys/src/9/pc/dat.h:120,125 - pc/dat.h:126,136 Page* kmaptable; /* page table used by kmap */ uint lastkmap; /* last entry used by kmap */ int nkmap; /* number of current kmaps */ + + Segdesc *ldt; /* local descriptor table */ + int nldt; /* number of ldt descriptors allocated */ + + Segdesc gdt[NPROCSEG]; /* per process descriptors */ }; /* /n/sources/plan9/sys/src/9/pc/dat.h:162,173 - pc/dat.h:173,178 ulong ldt; /* selector for task's LDT */ ulong iomap; /* I/O map base address + T-bit */ } Tss; - - struct Segdesc - { - ulong d0; - ulong d1; - }; struct Mach { /n/sources/plan9/sys/src/9/pc/mem.h:84,91 - pc/mem.h:84,93 #define APMCSEG16 7 /* APM 16-bit code segment */ #define APMDSEG 8 /* APM data segment */ #define KESEG16 9 /* kernel executable 16-bit */ - #define NGDT 10 /* number of GDT entries required */ - /* #define APM40SEG 8 /* APM segment 0x40 */ + #define LDTSEG 10 /* local descriptor table */ + #define PROCSEG0 11 /* per process descriptor0 */ + #define NPROCSEG 3 /* number of per process descriptors */ + #define NGDT 14 /* number of GDT entries required */ #define SELGDT (0<<2) /* selector is in gdt */ #define SELLDT (1<<2) /* selector is in ldt */ /n/sources/plan9/sys/src/9/pc/mem.h:102,107 - pc/mem.h:104,110 #define APMCSEL16 SELECTOR(APMCSEG16, SELGDT, 0) #define APMDSEL SELECTOR(APMDSEG, SELGDT, 0) /* #define APM40SEL SELECTOR(APM40SEG, SELGDT, 0) */ + #define LDTSEL SELECTOR(LDTSEG, SELGDT, 0) /* * fields in segment descriptors /n/sources/plan9/sys/src/9/pc/mem.h:108,117 - pc/mem.h:111,121 */ #define SEGDATA (0x10<<8) /* data/stack segment */ #define SEGEXEC (0x18<<8) /* executable segment */ - #define SEGTSS (0x9<<8) /* TSS segment */ + #define SEGTSS (0x09<<8) /* TSS segment */ #define SEGCG (0x0C<<8) /* call gate */ #define SEGIG (0x0E<<8) /* interrupt gate */ #define SEGTG (0x0F<<8) /* trap gate */ + #define SEGLDT (0x02<<8) /* local descriptor table */ #define SEGTYPE (0x1F<<8) #define SEGP (1<<15) /* segment present */ /n/sources/plan9/sys/src/9/pc/io.h:10,15 - pc/io.h:10,17 VectorCNA = 7, /* coprocessor not available */ Vector2F = 8, /* double fault */ VectorCSO = 9, /* coprocessor segment overrun */ + VectorSNP = 11, /* segment not present */ + VectorGPF = 13, /* general protection fault */ VectorPF = 14, /* page fault */ Vector15 = 15, /* reserved */ VectorCERR = 16, /* coprocessor error */ /n/sources/plan9/sys/src/9/pc/trap.c:326,332 - pc/trap.c:326,332 } m->perf.intrts = perfticks(); - user = (ureg->cs & 0xFFFF) == UESEL; + user = userureg(ureg); if(user){ up->dbgreg = ureg; cycles(&up->kentry); /n/sources/plan9/sys/src/9/pc/trap.c:335,340 - pc/trap.c:335,341 clockintr = 0; vno = ureg->trap; + if(ctl = vctl[vno]){ if(ctl->isintr){ m->intr++; /n/sources/plan9/sys/src/9/pc/trap.c:424,429 - pc/trap.c:425,480 while(m->machno != 0) ; } + + /* + * general protection fault in kernel mode + * can be caused by loading invalid segments + * when returning to userspace. as the user + * can change the descriptors now we take + * it here to prevent the damage to the + * kernel. -cinap + */ + if(vno == VectorGPF || vno == VectorSNP){ + ulong *sp; + uchar *pc; + + /* l.s */ + extern void load_fs(ulong); + extern void load_gs(ulong); + + /* + * CS, SS, DS and ES are initialized by strayintr + * in l.s. initialize the others too so we dont trap + * again when restoring the old context. + */ + load_fs(NULLSEL); + load_gs(NULLSEL); + + pc = (uchar*)ureg->pc; + sp = (ulong*)&ureg->sp; + + /* + * we test for the instructions used by forkret() + * to load the segments. this needs to be changed + * if forkret changes! + */ + + /* POP */ + if((pc[0] == 0x0f && (pc[1] == 0xa9 /*GS*/ || + pc[1] == 0xa1 /*FS*/)) || (pc[0] == 0x07) /*ES*/ || + (pc[0] == 0x1f) /*DS*/){ + sp[0] = NULLSEL; + return; + } + + /* IRET */ + if(pc[0] == 0xcf){ + sp[1] = UESEL; /*CS*/ + sp[4] = UDSEL; /*SS*/ + return; + } + } + dumpregs(ureg); if(!user){ ureg->sp = (ulong)&ureg->sp; /n/sources/plan9/sys/src/9/pc/trap.c:461,467 - pc/trap.c:512,522 iprint("cpu%d: registers for kernel\n", m->machno); iprint("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX", ureg->flags, ureg->trap, ureg->ecode, ureg->pc); - iprint(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp); + if(userureg(ureg)){ + iprint(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp); + } else { + iprint(" SP=%luX\n", (ulong)&ureg->sp); + } iprint(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n", ureg->ax, ureg->bx, ureg->cx, ureg->dx); iprint(" SI %8.8luX DI %8.8luX BP %8.8luX\n", /n/sources/plan9/sys/src/9/pc/trap.c:496,501 - pc/trap.c:551,557 } } iprint("\n ur %#p up %#p\n", ureg, up); + iprint("\n"); } /n/sources/plan9/sys/src/9/pc/trap.c:619,625 - pc/trap.c:675,681 addr = getcr2(); read = !(ureg->ecode & 2); - user = (ureg->cs & 0xFFFF) == UESEL; + user = userureg(ureg); if(!user){ if(vmapsync(addr)) return; /n/sources/plan9/sys/src/9/pc/trap.c:665,671 - pc/trap.c:721,727 int i, s; ulong scallnr; - if((ureg->cs & 0xFFFF) != UESEL) + if(!userureg(ureg)) panic("syscall: cs 0x%4.4luX", ureg->cs); cycles(&up->kentry); /n/sources/plan9/sys/src/9/pc/trap.c:828,833 - pc/trap.c:884,893 *(ulong*)(sp+0*BY2WD) = 0; /* arg 0 is pc */ ureg->usp = sp; ureg->pc = (ulong)up->notify; + + ureg->cs = UESEL; + ureg->ss = ureg->ds = ureg->es = UDSEL; + up->notified = 1; up->nnote--; memmove(&up->lastnote, &up->note[0], sizeof(Note)); /n/sources/plan9/sys/src/9/pc/trap.c:867,889 - pc/trap.c:927,936 pexit("Suicide", 0); } - /* - * Check the segment selectors are all valid, otherwise - * a fault will be taken on attempting to return to the - * user process. - * Take care with the comparisons as different processor - * generations push segment descriptors in different ways. - */ - if((nureg->cs & 0xFFFF) != UESEL || (nureg->ss & 0xFFFF) != UDSEL - || (nureg->ds & 0xFFFF) != UDSEL || (nureg->es & 0xFFFF) != UDSEL - || (nureg->fs & 0xFFFF) != UDSEL || (nureg->gs & 0xFFFF) != UDSEL){ - pprint("bad segment selector in noted\n"); - qunlock(&up->debug); - pexit("Suicide", 0); - } - /* don't let user change system flags */ nureg->flags = (ureg->flags & ~0xCD5) | (nureg->flags & 0xCD5); + nureg->cs |= 3; + nureg->ss |= 3; memmove(ureg, nureg, sizeof(Ureg)); /n/sources/plan9/sys/src/9/pc/trap.c:946,951 - pc/trap.c:993,1003 ureg = up->dbgreg; ureg->usp = (ulong)sp; ureg->pc = entry; + + ureg->cs = UESEL; + ureg->ss = ureg->ds = ureg->es = UDSEL; + ureg->fs = ureg->gs = NULLSEL; + return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ } /n/sources/plan9/sys/src/9/pc/trap.c:967,989 - pc/trap.c:1019,1034 void setregisters(Ureg* ureg, char* pureg, char* uva, int n) { - ulong cs, ds, es, flags, fs, gs, ss; + ulong flags; - ss = ureg->ss; flags = ureg->flags; - cs = ureg->cs; - ds = ureg->ds; - es = ureg->es; - fs = ureg->fs; - gs = ureg->gs; + memmove(pureg, uva, n); - ureg->gs = gs; - ureg->fs = fs; - ureg->es = es; - ureg->ds = ds; - ureg->cs = cs; - ureg->flags = (ureg->flags & 0x00FF) | (flags & 0xFF00); - ureg->ss = ss; + + /* don't allow chaning system flags */ + ureg->flags = (ureg->flags & 0xCD5) | (flags & ~0xCD5); + ureg->cs |= 3; + ureg->ss |= 3; } static void /n/sources/plan9/sys/src/9/pc/trap.c:1024,1029 - pc/trap.c:1069,1075 cureg = (Ureg*)(p->sched.sp+2*BY2WD); memmove(cureg, ureg, sizeof(Ureg)); + /* return value of syscall in child */ cureg->ax = 0; /n/sources/plan9/sys/src/9/pc/mmu.c:294,299 - pc/mmu.c:294,301 mmuswitch(Proc* proc) { ulong *pdb; + ulong x; + int n; if(proc->newtlb){ mmuptefree(proc); /n/sources/plan9/sys/src/9/pc/mmu.c:307,312 - pc/mmu.c:309,323 taskswitch(proc->mmupdb->pa, (ulong)(proc->kstack+KSTACK)); }else taskswitch(PADDR(m->pdb), (ulong)(proc->kstack+KSTACK)); + + memmove(&m->gdt[PROCSEG0], proc->gdt, sizeof(proc->gdt)); + if((x = (ulong)proc->ldt) && (n = proc->nldt) > 0){ + m->gdt[LDTSEG].d0 = (x<<16)|((n * sizeof(Segdesc)) - 1); + m->gdt[LDTSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGLDT|SEGPL(0)|SEGP; + lldt(LDTSEL); + } else { + lldt(NULLSEL); + } } /* /n/sources/plan9/sys/src/9/pc/mmu.c:362,367 - pc/mmu.c:373,384 if(--page->ref) panic("mmurelease: page->ref %d", page->ref); pagechainhead(page); + } + if(proc->ldt){ + lldt(NULLSEL); + free(proc->ldt); + proc->ldt = nil; + proc->nldt = 0; } if(proc->mmufree && palloc.r.p) wakeup(&palloc.r); /n/sources/plan9/sys/src/9/pc/l.s:601,606 - pc/l.s:601,611 MOVL (AX), GDTR RET + TEXT lldt(SB), $0 /* LDTR - local descriptor table */ + MOVL sel+0(FP), AX + BYTE $0x0F; BYTE $0x00; BYTE $0xD0 /* LLDT AX */ + RET + TEXT lidt(SB), $0 /* IDTR - interrupt descriptor table */ MOVL idtptr+0(FP), AX MOVL (AX), IDTR /n/sources/plan9/sys/src/9/pc/l.s:989,994 - pc/l.s:994,1009 _nothingready: STI HLT + RET + + TEXT load_fs(SB), $0 + MOVW fs+0(FP), AX + MOVW AX, FS + RET + + TEXT load_gs(SB), $0 + MOVW gs+0(FP), AX + MOVW AX, GS RET /* /tmp/diff100000066833:0 - pc/segdesc.c:1,272 + #include "u.h" + #include "../port/lib.h" + #include "mem.h" + #include "dat.h" + #include "fns.h" + #include "../port/error.h" + + /* + * flags: + * P = present + * A = accessed (for code/data) + * E = expand down (for data) + * W = writable (for data) + * R = readable (for code) + * C = conforming (for code) + * G = limit granularity in pages (for code/data) + * D = 32 bit operand size (for code) + * B = 32 bit stack pointer (for data) + * Y = busy (for tss and tss16) + * U = available for use by system software + */ + + static struct { + char *name; + char *flags; + } descrtypes[] = { + "data", "--------AWE01--P----U.BG--------", + "code", "--------ARC11--P----U.DG--------", + "tss16", "--------1Y000--P----U..G--------", + "ldt", "--------01000--P----U..G--------", + "callg16", "--------00100--P----U..G--------", + "taskg", "--------10100--P----U..G--------", + "intrg16", "--------01100--P----U..G--------", + "trapg16", "--------11100--P----U..G--------", + "tss", "--------1Y010--P----U..G--------", + "callg", "--------00110--P----U..G--------", + "intrg", "--------01110--P----U..G--------", + "trapg", "--------11110--P----U..G--------", + }; + + /* + * format: + * idx[4] type[8] flags[8] dpl[1] base[8] limit[5]\n + */ + + enum + { + RECLEN = 4+1 + 8+1 + 8+1 + 1+1 + 8+1 + 5+1, + }; + + static long + descwrite(Proc *proc, int local, void *v, long n, vlong) + { + int i, j, t; + char buf[RECLEN+1]; + char c, *p, *s, *e, *f[6]; + Segdesc d; + + int dpl; + ulong base; + ulong limit; + + s = (char*)v; + e = s + n; + + if(waserror()){ + if(proc == up) + flushmmu(); + nexterror(); + } + + while(s < e){ + for(p = s; p < e && *p != '\n'; p++); + ; + if((p - s) > RECLEN) + error(Ebadarg); + memmove(buf, s, p - s); + buf[p-s] = 0; + s = p+1; + + if(getfields(buf, f, nelem(f), 1, " ") != nelem(f)) + error(Ebadarg); + + i = strtoul(f[0], nil, 16); + + for(t=0; t>16); + + for(j=0; c = descrtypes[t].flags[j]; j++){ + switch(c){ + default: + if(strchr(f[2], c) == nil){ + case '0': + case '.': + d.d1 &= ~(1<= 8192) + error(Ebadarg); + if(i >= (c = ((old = proc->ldt) ? proc->nldt : 0))){ + if((new = malloc(sizeof(Segdesc) * (i+1))) == nil) + error(Enomem); + if(c > 0) + memmove(new, old, sizeof(Segdesc) * c); + memset(new + c, 0, sizeof(Segdesc) * ((i+1) - c)); + proc->ldt = new; + proc->nldt = i+1; + free(old); + } + proc->ldt[i] = d; + } else { + if(i < PROCSEG0 || i >= PROCSEG0 + NPROCSEG) + error(Ebadarg); + proc->gdt[i - PROCSEG0] = d; + } + } + poperror(); + + if(proc == up) + flushmmu(); + + return n; + } + + static long + descread(Proc *proc, int local, void *v, long n, vlong o) + { + int i, j, k, t; + char *s; + + int dpl; + ulong base; + ulong limit; + + s = v; + for(i = 0;;i++){ + Segdesc d; + + if(local){ + if(proc->ldt == nil || i >= proc->nldt) + break; + d = proc->ldt[i]; + } else { + if(i < PROCSEG0) + i = PROCSEG0; + if(i >= PROCSEG0 + NPROCSEG) + break; + d = proc->gdt[i - PROCSEG0]; + } + + if(o >= RECLEN){ + o -= RECLEN; + continue; + } + + if(s + RECLEN+1 >= (char*)v + n) + break; + + for(t=0; t>13; + base = ((d.d0 & 0xFFFF0000)>>16) | ((d.d1 & 0xFF)<<16) | (d.d1 & 0xFF000000); + limit = (d.d1 & 0xF0000) | (d.d0 & 0xFFFF); + + s += sprint(s, "%.1d ", dpl); + s += sprint(s, "%.8lux ", base); + s += sprint(s, "%.5lux\n", limit); + } + + return s-(char*)v; + } + + static long + gdtread(Chan*, void *v, long n, vlong o) + { + return descread(up, 0, v, n, o); + } + + static long + gdtwrite(Chan*, void *v, long n, vlong o) + { + return descwrite(up, 0, v, n, o); + } + + static long + ldtread(Chan*, void *v, long n, vlong o) + { + return descread(up, 1, v, n, o); + } + + static long + ldtwrite(Chan*, void *v, long n, vlong o) + { + return descwrite(up, 1, v, n, o); + } + + /* + * devproc hook + * extern long (*psegdescread)(Proc*, int, void*, long, vlong); + * extern long (*psegdescwrite)(Proc*, int, void*, long, vlong); + */ + + void + segdesclink(void) + { + /* + * devproc hook + * psegdescread = descread; + * psegdescwrite = descwrite; + */ + addarchfile("gdt", 0666, gdtread, gdtwrite); + addarchfile("ldt", 0666, ldtread, ldtwrite); + } /n/sources/plan9/sys/src/9/pc/main.c:440,453 - pc/main.c:440,446 + conf.nswap + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; - if(!cpuserver){ - /* - * give terminals lots of image memory, too; the dynamic - * allocation will balance the load properly, hopefully. - * be careful with 32-bit overflow. - */ - imagmem->maxsize = kpages; - } + imagmem->maxsize = kpages; } static char* mathmsg[] = /n/sources/plan9/sys/src/9/pc/main.c:575,588 - pc/main.c:568,582 trapenable(VectorCSO, mathover, 0, "mathover"); } - /* - * set up floating point for a new process - */ void procsetup(Proc*p) { p->fpstate = FPinit; fpoff(); + + memset(p->gdt, 0, sizeof(p->gdt)); + p->ldt = nil; + p->nldt = 0; } void /n/sources/plan9/sys/src/9/pc/main.c:592,597 - pc/main.c:586,592 if(p->kp) return; + cycles(&t); p->pcycles -= t; } /n/sources/plan9/sys/src/9/pc/main.c:637,642 - pc/main.c:632,651 mmuflushtlb(PADDR(m->pdb)); } + void + procfork(Proc *p) + { + /* inherit user descriptors */ + memmove(p->gdt, up->gdt, sizeof(p->gdt)); + + /* copy local descriptor table */ + if(up->ldt != nil && up->nldt > 0){ + p->ldt = malloc(sizeof(Segdesc) * up->nldt); + memmove(p->ldt, up->ldt, sizeof(Segdesc) * up->nldt); + p->nldt = up->nldt; + } + } + static void shutdown(int ispanic) { /n/sources/plan9/sys/src/9/pc/main.c:648,660 - pc/main.c:657,662 else if(m->machno == 0 && (active.machs & (1<machno)) == 0) active.ispanic = 0; once = active.machs & (1<machno); - /* - * setting exiting will make hzclock() on each processor call exit(0), - * which calls shutdown(0) and arch->reset(), which on mp systems is - * mpshutdown, from which there is no return: the processor is idled - * or initiates a reboot. clearing our bit in machs avoids calling - * exit(0) from hzclock() on this processor. - */ active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); /n/sources/plan9/sys/src/9/pc/main.c:661,668 - pc/main.c:663,668 if(once) iprint("cpu%d: exiting\n", m->machno); - - /* wait for any other processors to shutdown */ spllo(); for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); /n/sources/plan9/sys/src/9/pc/main.c:670,683 - pc/main.c:670,683 break; } + if(getconf("*debug")) + delay(5*60*1000); + if(active.ispanic){ if(!cpuserver) for(;;) halt(); - if(getconf("*debug")) - delay(5*60*1000); - else - delay(10000); + delay(10000); }else delay(1000); } /n/sources/plan9/sys/src/9/pc/main.c:690,715 - pc/main.c:690,700 writeconf(); - /* - * the boot processor is cpu0. execute this function on it - * so that the new kernel has the same cpu0. this only matters - * because the hardware has a notion of which processor was the - * boot processor and we look at it at start up. - */ - if (m->machno != 0) { - procwired(up, 0); - sched(); - } - shutdown(0); /* * should be the only processor running now */ - if (m->machno != 0) - print("on cpu%d (not 0)!\n", m->machno); - if (active.machs) - print("still have active ap processors!\n"); print("shutting down...\n"); delay(200); /n/sources/plan9/sys/src/9/pc/main.c:721,727 - pc/main.c:706,711 /* shutdown devices */ chandevshutdown(); - arch->introff(); /* * Modify the machine page table to directly map the low 4MB of memory /n/sources/plan9/sys/src/9/ppc/main.c:297,302 - ppc/main.c:297,308 p->pcycles -= t; } + void + procfork(Proc *) + { + } + + /* * Save the mach dependent part of the process state. */ /n/sources/plan9/sys/src/9/ppc/fns.h:77,82 - ppc/fns.h:77,83 void pcicfgw8(Pcidev*, int, int); void procsave(Proc*); void procsetup(Proc*); + void procfork(Proc*); void putdcmp(ulong); void putdec(ulong); void puthash1(ulong); /n/sources/plan9/sys/src/9/alphapc/main.c:295,300 - alphapc/main.c:295,306 } void + procfork(Proc *) + { + } + + + void setupboot(int halt) { int n = 0; // cpu id of primary cpu, not just m->machno /n/sources/plan9/sys/src/9/alphapc/fns.h:88,93 - alphapc/fns.h:88,94 #define procrestore(p) void procsave(Proc*); void procsetup(Proc*); + void procfork(Proc*); void restfpregs(FPsave*); uvlong rpcc(uvlong*); void screeninit(void); /n/sources/plan9/sys/src/9/bitsy/main.c:252,257 - bitsy/main.c:252,263 USED(p); } + void + procfork(Proc *) + { + } + + /* place holder */ /* * dummy since rdb is not included /n/sources/plan9/sys/src/9/bitsy/fns.h:85,90 - bitsy/fns.h:85,91 #define procrestore(p) void procsave(Proc*); void procsetup(Proc*); + void procfork(Proc*); void putdac(ulong); void putttb(ulong); void putpid(ulong); /n/sources/plan9/sys/src/9/mtx/main.c:274,279 - mtx/main.c:274,285 } void + procfork(Proc *) + { + } + + + void confinit(void) { char *p; /n/sources/plan9/sys/src/9/mtx/fns.h:86,91 - mtx/fns.h:86,92 #define procrestore(p) void procsave(Proc*); void procsetup(Proc*); + void procfork(Proc*); void putdec(ulong); void puthid0(ulong); void puthid1(ulong); /n/sources/plan9/sys/src/9/port/sysproc.c:187,192 - port/sysproc.c:187,195 kstrdup(&p->text, up->text); kstrdup(&p->user, up->user); + + procfork(p); + /* * since the bss/data segments are now shareable, * any mmu info about this process is now stale + "trapg", "--------11110--P----U..G--------", + }; + + /* + segdescpatch/README 664 0 0 4253 11342263221 15511ustar00cinap_lenrekcinap_lenrekthese are the changes to the plan 9 kernel to add per process segment descriptors. the 9/ directory contains all the changed files from /sys/src/9. kernel.diff contains the changes. the changes in detail are: pc/fns.h 1) prototype for lldt() added (for l.s) 2) prototype for procfork() added 3) changed userureg macro to test for the rpl bits but not for the specific UDSEL selector. this is because with the patch, a process is allowed to change its data segment. so we test only for the "requested priviledge level" bits. pc/dat.h 1) moved the Segdesc struct declaration. 2) added ldt and gdt to PMMU struct pc/mem.h 1) added segment definitions for LDT and PROCSEGs pc/io.h 1) added vector constants for general protection fault and seg not present. pc/trap.c 1) replaced the user test with userureg() macro 2) added GPF handler to trap() that catches the fault when we return to userspace, but the segment registers contain invalid selectors. previously this was not needed as the segment registers for user space where constant and checked. 3) in the case we have a trap in kernel mode, the processor doesnt push the SS and SP registers. fixed up dumpregs() to print the right values. 4) set default segments in notify() 5) removed the segment check in notify as we handle it with the GPF handler in trap(). only make sure the user doesnt change the code and stack segments to kernel ones. 6) set default segments in execregs() 7) changing of segments from devproc is ok now as long as code and stack don't point to kernel segments. changed in setregisters() pc/mmu.c 1) added switching of procsegs and ldt in mmuswitch() 2) free the ldt in mmurelease() pc/l.s 1) added lldt() function as it is requred for switching the ldt. 2) added load_fs() and load_gs() as it is needed in the GPF handler. pc/segdesc.c 1) this file contains the code to change the per process descriptors. it adds the ldt and gdt files to devarch. pc/main.c 1) setup gdt and ldt in procsetup() 2) added procfork() to copy ldt and gdt when forking */main.c 1) added empty procfork() as well */fns.h 1) added procfork() prototype port/sysproc.c 1) added call to forkfork() from sysfork() to the ldt and gdt get copied continue; + } + + if(s + RECLEN+1 >= (char*)v + n) + break; + + for(t=0; tcs & 3) == 3) void vectortable(void); void* vmap(ulong, int); int vmapsync(ulong); void vunmap(void*, int); void wbinvd(void); void wrmsr(int, vlong); int xchgw(ushort*, int); #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) #define KADDR(a) kaddr(a) #define PADDR(a) paddr((void*)(a)) #define dcflush(a, b) d putpid(ulong); /n/sources/plan9/sys/src/9/mtx/main.c:274,279 - mtx/main.c:274,285 } void + procfork(Proc *) + { + } + + + void confinit(void) { char *p; /n/sources/plan9/sys/src/9/mtx/fns.h:86,91 - mtx/fns.h:8segdescpatch/9/pc/dat.h 664 0 0 17027 11342263221 16347ustar00cinap_lenrekcinap_lenrektypedef struct BIOS32si BIOS32si; typedef struct BIOS32ci BIOS32ci; typedef struct Conf Conf; typedef struct Confmem Confmem; typedef struct FPsave FPsave; typedef struct ISAConf ISAConf; typedef struct Label Label; typedef struct Lock Lock; typedef struct MMU MMU; typedef struct Mach Mach; typedef struct Notsave Notsave; typedef struct PCArch PCArch; typedef struct Pcidev Pcidev; typedef struct PCMmap PCMmap; typedef struct PCMslot PCMslot; typedef struct Page Page; typedef struct PMMU PMMU; typedef struct Proc Proc; typedef struct Segdesc Segdesc; typedef vlong Tval; typedef struct Ureg Ureg; typedef struct Vctl Vctl; #pragma incomplete BIOS32si #pragma incomplete Pcidev #pragma incomplete Ureg #define MAXSYSARG 5 /* for mount(fd, afd, mpt, flag, arg) */ /* * parameters for sysproc.c */ #define AOUT_MAGIC (I_MAGIC) struct Lock { ulong key; ulong sr; ulong pc; Proc *p; Mach *m; ushort isilock; long lockcycles; }; struct Label { ulong sp; ulong pc; }; /* * FPsave.status */ enum { /* this is a state */ FPinit= 0, FPactive= 1, FPinactive= 2, /* the following is a bit that can be or'd into the state */ FPillegal= 0x100, }; struct FPsave { ushort control; ushort r1; ushort status; ushort r2; ushort tag; ushort r3; ulong pc; ushort selector; ushort r4; ulong operand; ushort oselector; ushort r5; uchar regs[80]; /* floating point registers */ }; struct Confmem { ulong base; ulong npage; ulong kbase; ulong klimit; }; struct Conf { ulong nmach; /* processors */ ulong nproc; /* processes */ ulong monitor; /* has monitor? */ Confmem mem[4]; /* physical memory */ ulong npage; /* total physical pages of memory */ ulong upages; /* user page pool */ ulong nimage; /* number of page cache image headers */ ulong nswap; /* number of swap pages */ int nswppo; /* max # of pageouts per segment pass */ ulong base0; /* base of bank 0 */ ulong base1; /* base of bank 1 */ ulong copymode; /* 0 is copy on write, 1 is copy on reference */ ulong ialloc; /* max interrupt time allocation in bytes */ ulong pipeqsize; /* size in bytes of pipe queues */ int nuart; /* number of uart devices */ }; struct Segdesc { ulong d0; ulong d1; }; /* * MMU stuff in proc */ #define NCOLOR 1 struct PMMU { Page* mmupdb; /* page directory base */ Page* mmufree; /* unused page table pages */ Page* mmuused; /* used page table pages */ Page* kmaptable; /* page table used by kmap */ uint lastkmap; /* last entry used by kmap */ int nkmap; /* number of current kmaps */ Segdesc *ldt; /* local descriptor table */ int nldt; /* number of ldt descriptors allocated */ Segdesc gdt[NPROCSEG]; /* per process descriptors */ }; /* * things saved in the Proc structure during a notify */ struct Notsave { ulong svflags; ulong svcs; ulong svss; }; #include "../port/portdat.h" typedef struct { ulong link; /* link (old TSS selector) */ ulong esp0; /* privilege level 0 stack pointer */ ulong ss0; /* privilege level 0 stack selector */ ulong esp1; /* privilege level 1 stack pointer */ ulong ss1; /* privilege level 1 stack selector */ ulong esp2; /* privilege level 2 stack pointer */ ulong ss2; /* privilege level 2 stack selector */ ulong xcr3; /* page directory base register - not used because we don't use trap gates */ ulong eip; /* instruction pointer */ ulong eflags; /* flags register */ ulong eax; /* general registers */ ulong ecx; ulong edx; ulong ebx; ulong esp; ulong ebp; ulong esi; ulong edi; ulong es; /* segment selectors */ ulong cs; ulong ss; ulong ds; ulong fs; ulong gs; ulong ldt; /* selector for task's LDT */ ulong iomap; /* I/O map base address + T-bit */ } Tss; struct Mach { int machno; /* physical id of processor (KNOWN TO ASSEMBLY) */ ulong splpc; /* pc of last caller to splhi */ ulong* pdb; /* page directory base for this processor (va) */ Tss* tss; /* tss for this processor */ Segdesc *gdt; /* gdt for this processor */ Proc* proc; /* current process on this processor */ Proc* externup; /* extern register Proc *up */ Page* pdbpool; int pdbcnt; ulong ticks; /* of the clock since boot time */ Label sched; /* scheduler wakeup */ Lock alarmlock; /* access to alarm list */ void* alarm; /* alarms bound to this clock */ int inclockintr; Proc* readied; /* for runproc */ ulong schedticks; /* next forced context switch */ int tlbfault; int tlbpurge; int pfault; int cs; int syscall; int load; int intr; int flushmmu; /* make current proc flush it's mmu state */ int ilockdepth; Perf perf; /* performance counters */ ulong spuriousintr; int lastintr; int loopconst; Lock apictimerlock; int cpumhz; uvlong cyclefreq; /* Frequency of user readable cycle counter */ uvlong cpuhz; int cpuidax; int cpuiddx; char cpuidid[16]; char* cpuidtype; int havetsc; int havepge; uvlong tscticks; int pdballoc; int pdbfree; vlong mtrrcap; vlong mtrrdef; vlong mtrrfix[11]; vlong mtrrvar[32]; /* 256 max. */ int stack[1]; }; /* * KMap the structure doesn't exist, but the functions do. */ typedef struct KMap KMap; #define VA(k) ((void*)(k)) KMap* kmap(Page*); void kunmap(KMap*); struct { Lock; int machs; /* bitmap of active CPUs */ int exiting; /* shutdown */ int ispanic; /* shutdown in response to a panic */ int thunderbirdsarego; /* lets the added processors continue to schedinit */ }active; /* * routines for things outside the PC model, like power management */ struct PCArch { char* id; int (*ident)(void); /* this should be in the model */ void (*reset)(void); /* this should be in the model */ int (*serialpower)(int); /* 1 == on, 0 == off */ int (*modempower)(int); /* 1 == on, 0 == off */ void (*intrinit)(void); int (*intrenable)(Vctl*); int (*intrvecno)(int); int (*intrdisable)(int); void (*introff)(void); void (*intron)(void); void (*clockenable)(void); uvlong (*fastclock)(uvlong*); void (*timerset)(uvlong); }; /* cpuid instruction result register bits */ enum { /* dx */ Fpuonchip = 1<<0, // Pse = 1<<3, /* page size extensions */ Tsc = 1<<4, /* time-stamp counter */ Cpumsr = 1<<5, /* model-specific registers, rdmsr/wrmsr */ Pae = 1<<6, /* physical-addr extensions */ Mce = 1<<7, /* machine-check exception */ Cmpxchg8b = 1<<8, Cpuapic = 1<<9, Mtrr = 1<<12, /* memory-type range regs. */ Pge = 1<<13, /* page global extension */ // Pse2 = 1<<17, /* more page size extensions */ Clflush = 1<<19, Mmx = 1<<23, Sse = 1<<25, /* thus sfence instr. */ Sse2 = 1<<26, /* thus mfence & lfence instr.s */ }; /* * a parsed plan9.ini line */ #define NISAOPT 8 struct ISAConf { char *type; ulong port; int irq; ulong dma; ulong mem; ulong size; ulong freq; int nopt; char *opt[NISAOPT]; }; extern PCArch *arch; /* PC architecture */ /* * Each processor sees its own Mach structure at address MACHADDR. * However, the Mach structures must also be available via the per-processor * MMU information array machp, mainly for disambiguation and access to * the clock which is only maintained by the bootstrap processor (0). */ Mach* machp[MAXMACH]; #define MACHP(n) (machp[n]) extern Mach *m; #define up (((Mach*)MACHADDR)->externup) /* * hardware info about a device */ typedef struct { ulong port; int size; } Devport; struct DevConf { ulong intnum; /* interrupt number */ char *type; /* card type, malloced */ int nports; /* Number of ports */ Devport *ports; /* The ports themselves */ }; typedef struct BIOS32ci { /* BIOS32 Calling Interface */ u32int eax; u32int ebx; u32int ecx; u32int edx; u32int esi; u32int edi; } BIOS32ci; segdescpatch/9/pc/mem.h 664 0 0 12671 11342263221 16355ustar00cinap_lenrekcinap_lenrek/* * Memory and machine-specific definitions. Used in C and assembler. */ /* * Sizes */ #define BI2BY 8 /* bits per byte */ #define BI2WD 32 /* bits per word */ #define BY2WD 4 /* bytes per word */ #define BY2V 8 /* bytes per double word */ #define BY2PG 4096 /* bytes per page */ #define WD2PG (BY2PG/BY2WD) /* words per page */ #define BY2XPG (4096*1024) /* bytes per big page */ #define PGSHIFT 12 /* log(BY2PG) */ #define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) #define PGROUND(s) ROUND(s, BY2PG) #define BLOCKALIGN 8 #define MAXMACH 8 /* max # cpus system can run */ #define KSTACK 4096 /* Size of kernel stack */ /* * Time */ #define HZ (100) /* clock frequency */ #define MS2HZ (1000/HZ) /* millisec per clock tick */ #define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ /* * Address spaces */ #define KZERO 0xF0000000 /* base of kernel address space */ #define KTZERO (KZERO+0x100000) /* first address in kernel text - 9load sits below */ #define VPT (KZERO-VPTSIZE) #define VPTSIZE BY2XPG #define NVPT (VPTSIZE/BY2WD) #define KMAP (VPT-KMAPSIZE) #define KMAPSIZE BY2XPG #define VMAP (KMAP-VMAPSIZE) #define VMAPSIZE (0x10000000-VPTSIZE-KMAPSIZE) #define UZERO 0 /* base of user address space */ #define UTZERO (UZERO+BY2PG) /* first address in user text */ #define USTKTOP (VMAP-BY2PG) /* byte just beyond user stack */ #define USTKSIZE (16*1024*1024) /* size of user stack */ #define TSTKTOP (USTKTOP-USTKSIZE) /* end of new stack in sysexec */ #define TSTKSIZ 100 /* pages in new stack; limits exec args */ /* * Fundamental addresses - bottom 64kB saved for return to real mode */ #define CONFADDR (KZERO+0x1200) /* info passed from boot loader */ #define TMPADDR (KZERO+0x2000) /* used for temporary mappings */ #define APBOOTSTRAP (KZERO+0x3000) /* AP bootstrap code */ #define RMUADDR (KZERO+0x7C00) /* real mode Ureg */ #define RMCODE (KZERO+0x8000) /* copy of first page of KTEXT */ #define RMBUF (KZERO+0x9000) /* buffer for user space - known to vga */ #define IDTADDR (KZERO+0x10800) /* idt */ #define REBOOTADDR (0x11000) /* reboot code - physical address */ #define CPU0PDB (KZERO+0x12000) /* bootstrap processor PDB */ #define CPU0PTE (KZERO+0x13000) /* bootstrap processor PTE's for 0-4MB */ #define CPU0GDT (KZERO+0x14000) /* bootstrap processor GDT */ #define MACHADDR (KZERO+0x15000) /* as seen by current processor */ #define CPU0MACH (KZERO+0x16000) /* Mach for bootstrap processor */ #define MACHSIZE BY2PG #define CPU0PTE1 (KZERO+0x17000) /* bootstrap processor PTE's for 4MB-8MB */ #define CPU0END (CPU0PTE1+BY2PG) /* * N.B. ramscan knows that CPU0END is the end of reserved data * N.B. _startPADDR knows that CPU0PDB is the first reserved page * and that there are 6 of them. */ /* * known x86 segments (in GDT) and their selectors */ #define NULLSEG 0 /* null segment */ #define KDSEG 1 /* kernel data/stack */ #define KESEG 2 /* kernel executable */ #define UDSEG 3 /* user data/stack */ #define UESEG 4 /* user executable */ #define TSSSEG 5 /* task segment */ #define APMCSEG 6 /* APM code segment */ #define APMCSEG16 7 /* APM 16-bit code segment */ #define APMDSEG 8 /* APM data segment */ #define KESEG16 9 /* kernel executable 16-bit */ #define LDTSEG 10 /* local descriptor table */ #define PROCSEG0 11 /* per process descriptor0 */ #define NPROCSEG 3 /* number of per process descriptors */ #define NGDT 14 /* number of GDT entries required */ #define SELGDT (0<<2) /* selector is in gdt */ #define SELLDT (1<<2) /* selector is in ldt */ #define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) #define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) #define KDSEL SELECTOR(KDSEG, SELGDT, 0) #define KESEL SELECTOR(KESEG, SELGDT, 0) #define UESEL SELECTOR(UESEG, SELGDT, 3) #define UDSEL SELECTOR(UDSEG, SELGDT, 3) #define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) #define APMCSEL SELECTOR(APMCSEG, SELGDT, 0) #define APMCSEL16 SELECTOR(APMCSEG16, SELGDT, 0) #define APMDSEL SELECTOR(APMDSEG, SELGDT, 0) /* #define APM40SEL SELECTOR(APM40SEG, SELGDT, 0) */ #define LDTSEL SELECTOR(LDTSEG, SELGDT, 0) /* * fields in segment descriptors */ #define SEGDATA (0x10<<8) /* data/stack segment */ #define SEGEXEC (0x18<<8) /* executable segment */ #define SEGTSS (0x09<<8) /* TSS segment */ #define SEGCG (0x0C<<8) /* call gate */ #define SEGIG (0x0E<<8) /* interrupt gate */ #define SEGTG (0x0F<<8) /* trap gate */ #define SEGLDT (0x02<<8) /* local descriptor table */ #define SEGTYPE (0x1F<<8) #define SEGP (1<<15) /* segment present */ #define SEGPL(x) ((x)<<13) /* priority level */ #define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ #define SEGG (1<<23) /* granularity 1==4k (for other) */ #define SEGE (1<<10) /* expand down */ #define SEGW (1<<9) /* writable (for data/stack) */ #define SEGR (1<<9) /* readable (for code) */ #define SEGD (1<<22) /* default 1==32bit (for code) */ /* * virtual MMU */ #define PTEMAPMEM (1024*1024) #define PTEPERTAB (PTEMAPMEM/BY2PG) #define SEGMAPSIZE 1984 #define SSEGMAPSIZE 16 #define PPN(x) ((x)&~(BY2PG-1)) /* * physical MMU */ #define PTEVALID (1<<0) #define PTEWT (1<<3) #define PTEUNCACHED (1<<4) #define PTEWRITE (1<<1) #define PTERONLY (0<<1) #define PTEKERNEL (0<<2) #define PTEUSER (1<<2) #define PTESIZE (1<<7) #define PTEGLOBAL (1<<8) /* * Macros for calculating offsets within the page directory base * and page tables. */ #define PDX(va) ((((ulong)(va))>>22) & 0x03FF) #define PTX(va) ((((ulong)(va))>>12) & 0x03FF) #define getpgcolor(a) 0 c* proc; /* current process on this processor */ Proc* externup; /*segdescpatch/9/pc/trap.c 664 0 0 54161 11343343610 16542ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "tos.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" #include static int trapinited; void noted(Ureg*, ulong); static void debugbpt(Ureg*, void*); static void fault386(Ureg*, void*); static void doublefault(Ureg*, void*); static void unexpected(Ureg*, void*); static void _dumpstack(Ureg*); static Lock vctllock; static Vctl *vctl[256]; enum { Ntimevec = 20 /* number of time buckets for each intr */ }; ulong intrtimes[256][Ntimevec]; void intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) { int vno; Vctl *v; if(f == nil){ print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n", irq, tbdf, name); return; } v = xalloc(sizeof(Vctl)); v->isintr = 1; v->irq = irq; v->tbdf = tbdf; v->f = f; v->a = a; strncpy(v->name, name, KNAMELEN-1); v->name[KNAMELEN-1] = 0; ilock(&vctllock); vno = arch->intrenable(v); if(vno == -1){ iunlock(&vctllock); print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n", irq, tbdf, v->name); xfree(v); return; } if(vctl[vno]){ if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi) panic("intrenable: handler: %s %s %#p %#p %#p %#p", vctl[vno]->name, v->name, vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi); v->next = vctl[vno]; } vctl[vno] = v; iunlock(&vctllock); } int intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name) { Vctl **pv, *v; int vno; /* * For now, none of this will work with the APIC code, * there is no mapping between irq and vector as the IRQ * is pretty meaningless. */ if(arch->intrvecno == nil) return -1; vno = arch->intrvecno(irq); ilock(&vctllock); pv = &vctl[vno]; while (*pv && ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a || strcmp((*pv)->name, name))) pv = &((*pv)->next); assert(*pv); v = *pv; *pv = (*pv)->next; /* Link out the entry */ if(vctl[vno] == nil && arch->intrdisable != nil) arch->intrdisable(irq); iunlock(&vctllock); xfree(v); return 0; } static long irqallocread(Chan*, void *vbuf, long n, vlong offset) { char *buf, *p, str[2*(11+1)+KNAMELEN+1+1]; int m, vno; long oldn; Vctl *v; if(n < 0 || offset < 0) error(Ebadarg); oldn = n; buf = vbuf; for(vno=0; vnonext){ m = snprint(str, sizeof str, "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name); if(m <= offset) /* if do not want this, skip entry */ offset -= m; else{ /* skip offset bytes */ m -= offset; p = str+offset; offset = 0; /* write at most max(n,m) bytes */ if(m > n) m = n; memmove(buf, p, m); n -= m; buf += m; if(n == 0) return oldn; } } } return oldn - n; } void trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name) { Vctl *v; if(vno < 0 || vno >= VectorPIC) panic("trapenable: vno %d", vno); v = xalloc(sizeof(Vctl)); v->tbdf = BUSUNKNOWN; v->f = f; v->a = a; strncpy(v->name, name, KNAMELEN); v->name[KNAMELEN-1] = 0; ilock(&vctllock); if(vctl[vno]) v->next = vctl[vno]->next; vctl[vno] = v; iunlock(&vctllock); } static void nmienable(void) { int x; /* * Hack: should be locked with NVRAM access. */ outb(0x70, 0x80); /* NMI latch clear */ outb(0x70, 0); x = inb(0x61) & 0x07; /* Enable NMI */ outb(0x61, 0x08|x); outb(0x61, x); } /* * Minimal trap setup. Just enough so that we can panic * on traps (bugs) during kernel initialization. * Called very early - malloc is not yet available. */ void trapinit0(void) { int d1, v; ulong vaddr; Segdesc *idt; idt = (Segdesc*)IDTADDR; vaddr = (ulong)vectortable; for(v = 0; v < 256; v++){ d1 = (vaddr & 0xFFFF0000)|SEGP; switch(v){ case VectorBPT: d1 |= SEGPL(3)|SEGIG; break; case VectorSYSCALL: d1 |= SEGPL(3)|SEGIG; break; default: d1 |= SEGPL(0)|SEGIG; break; } idt[v].d0 = (vaddr & 0xFFFF)|(KESEL<<16); idt[v].d1 = d1; vaddr += 6; } } void trapinit(void) { /* * Special traps. * Syscall() is called directly without going through trap(). */ trapenable(VectorBPT, debugbpt, 0, "debugpt"); trapenable(VectorPF, fault386, 0, "fault386"); trapenable(Vector2F, doublefault, 0, "doublefault"); trapenable(Vector15, unexpected, 0, "unexpected"); nmienable(); addarchfile("irqalloc", 0444, irqallocread, nil); trapinited = 1; } static char* excname[32] = { "divide error", "debug exception", "nonmaskable interrupt", "breakpoint", "overflow", "bounds check", "invalid opcode", "coprocessor not available", "double fault", "coprocessor segment overrun", "invalid TSS", "segment not present", "stack exception", "general protection violation", "page fault", "15 (reserved)", "coprocessor error", "alignment check", "machine check", "19 (reserved)", "20 (reserved)", "21 (reserved)", "22 (reserved)", "23 (reserved)", "24 (reserved)", "25 (reserved)", "26 (reserved)", "27 (reserved)", "28 (reserved)", "29 (reserved)", "30 (reserved)", "31 (reserved)", }; /* * keep histogram of interrupt service times */ void intrtime(Mach*, int vno) { ulong diff; ulong x; x = perfticks(); diff = x - m->perf.intrts; m->perf.intrts = x; m->perf.inintr += diff; if(up == nil && m->perf.inidle > diff) m->perf.inidle -= diff; diff /= m->cpumhz*100; /* quantum = 100µsec */ if(diff >= Ntimevec) diff = Ntimevec-1; intrtimes[vno][diff]++; } /* go to user space */ void kexit(Ureg*) { uvlong t; Tos *tos; /* precise time accounting, kernel exit */ tos = (Tos*)(USTKTOP-sizeof(Tos)); cycles(&t); tos->kcycles += t - up->kentry; tos->pcycles = up->pcycles; tos->pid = up->pid; } /* * All traps come here. It is slower to have all traps call trap() * rather than directly vectoring the handler. However, this avoids a * lot of code duplication and possible bugs. The only exception is * VectorSYSCALL. * Trap is called with interrupts disabled via interrupt-gates. */ void trap(Ureg* ureg) { int clockintr, i, vno, user; char buf[ERRMAX]; Vctl *ctl, *v; Mach *mach; if(!trapinited){ /* fault386 can give a better error message */ if(ureg->trap == VectorPF) fault386(ureg, nil); panic("trap %lud: not ready", ureg->trap); } m->perf.intrts = perfticks(); user = userureg(ureg); if(user){ up->dbgreg = ureg; cycles(&up->kentry); } clockintr = 0; vno = ureg->trap; if(ctl = vctl[vno]){ if(ctl->isintr){ m->intr++; if(vno >= VectorPIC && vno != VectorSYSCALL) m->lastintr = ctl->irq; } if(ctl->isr) ctl->isr(vno); for(v = ctl; v != nil; v = v->next){ if(v->f) v->f(ureg, v->a); } if(ctl->eoi) ctl->eoi(vno); if(ctl->isintr){ intrtime(m, vno); if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER) clockintr = 1; if(up && !clockintr) preempted(); } } else if(vno < nelem(excname) && user){ spllo(); sprint(buf, "sys: trap: %s", excname[vno]); postnote(up, 1, buf, NDebug); } else if(vno >= VectorPIC && vno != VectorSYSCALL){ /* * An unknown interrupt. * Check for a default IRQ7. This can happen when * the IRQ input goes away before the acknowledge. * In this case, a 'default IRQ7' is generated, but * the corresponding bit in the ISR isn't set. * In fact, just ignore all such interrupts. */ /* call all interrupt routines, just in case */ for(i = VectorPIC; i <= MaxIrqLAPIC; i++){ ctl = vctl[i]; if(ctl == nil) continue; if(!ctl->isintr) continue; for(v = ctl; v != nil; v = v->next){ if(v->f) v->f(ureg, v->a); } /* should we do this? */ if(ctl->eoi) ctl->eoi(i); } /* clear the interrupt */ i8259isr(vno); if(0)print("cpu%d: spurious interrupt %d, last %d\n", m->machno, vno, m->lastintr); if(0)if(conf.nmach > 1){ for(i = 0; i < 32; i++){ if(!(active.machs & (1<machno == mach->machno) continue; print(" cpu%d: last %d", mach->machno, mach->lastintr); } print("\n"); } m->spuriousintr++; if(user) kexit(ureg); return; } else{ if(vno == VectorNMI){ /* * Don't re-enable, it confuses the crash dumps. nmienable(); */ iprint("cpu%d: PC %#8.8lux\n", m->machno, ureg->pc); while(m->machno != 0) ; } /* * general protection fault in kernel mode * can be caused by loading invalid segments * when returning to userspace. as the user * can change the descriptors now we take * it here to prevent the damage to the * kernel. -cinap */ if(vno == VectorGPF || vno == VectorSNP){ ulong *sp; uchar *pc; /* l.s */ extern void load_fs(ulong); extern void load_gs(ulong); /* * CS, SS, DS and ES are initialized by strayintr * in l.s. initialize the others too so we dont trap * again when restoring the old context. */ load_fs(NULLSEL); load_gs(NULLSEL); pc = (uchar*)ureg->pc; sp = (ulong*)&ureg->sp; /* * we test for the instructions used by forkret() * to load the segments. this needs to be changed * if forkret changes! */ /* POP */ if((pc[0] == 0x0f && (pc[1] == 0xa9 /*GS*/ || pc[1] == 0xa1 /*FS*/)) || (pc[0] == 0x07) /*ES*/ || (pc[0] == 0x1f) /*DS*/){ sp[0] = NULLSEL; return; } /* IRET */ if(pc[0] == 0xcf){ sp[1] = UESEL; /*CS*/ sp[4] = UDSEL; /*SS*/ return; } } dumpregs(ureg); if(!user){ ureg->sp = (ulong)&ureg->sp; _dumpstack(ureg); } if(vno < nelem(excname)) panic("%s", excname[vno]); panic("unknown trap/intr: %d", vno); } splhi(); /* delaysched set because we held a lock or because our quantum ended */ if(up && up->delaysched && clockintr){ sched(); splhi(); } if(user){ if(up->procctl || up->nnote) notify(ureg); kexit(ureg); } } /* * dump registers */ void dumpregs2(Ureg* ureg) { if(up) iprint("cpu%d: registers for %s %lud\n", m->machno, up->text, up->pid); else iprint("cpu%d: registers for kernel\n", m->machno); iprint("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX", ureg->flags, ureg->trap, ureg->ecode, ureg->pc); if(userureg(ureg)){ iprint(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp); } else { iprint(" SP=%luX\n", (ulong)&ureg->sp); } iprint(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n", ureg->ax, ureg->bx, ureg->cx, ureg->dx); iprint(" SI %8.8luX DI %8.8luX BP %8.8luX\n", ureg->si, ureg->di, ureg->bp); iprint(" CS %4.4luX DS %4.4luX ES %4.4luX FS %4.4luX GS %4.4luX\n", ureg->cs & 0xFFFF, ureg->ds & 0xFFFF, ureg->es & 0xFFFF, ureg->fs & 0xFFFF, ureg->gs & 0xFFFF); } void dumpregs(Ureg* ureg) { vlong mca, mct; dumpregs2(ureg); /* * Processor control registers. * If machine check exception, time stamp counter, page size extensions * or enhanced virtual 8086 mode extensions are supported, there is a * CR4. If there is a CR4 and machine check extensions, read the machine * check address and machine check type registers if RDMSR supported. */ iprint(" CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux", getcr0(), getcr2(), getcr3()); if(m->cpuiddx & 0x9A){ iprint(" CR4 %8.8lux", getcr4()); if((m->cpuiddx & 0xA0) == 0xA0){ rdmsr(0x00, &mca); rdmsr(0x01, &mct); iprint("\n MCA %8.8llux MCT %8.8llux", mca, mct); } } iprint("\n ur %#p up %#p\n", ureg, up); iprint("\n"); } /* * Fill in enough of Ureg to get a stack trace, and call a function. * Used by debugging interface rdb. */ void callwithureg(void (*fn)(Ureg*)) { Ureg ureg; ureg.pc = getcallerpc(&fn); ureg.sp = (ulong)&fn; fn(&ureg); } static void _dumpstack(Ureg *ureg) { uintptr l, v, i, estack; extern ulong etext; int x; char *s; if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){ iprint("dumpstack disabled\n"); return; } iprint("dumpstack\n"); x = 0; x += iprint("ktrace /kernel/path %.8lux %.8lux <pc, ureg->sp); i = 0; if(up && (uintptr)&l >= (uintptr)up->kstack && (uintptr)&l <= (uintptr)up->kstack+KSTACK) estack = (uintptr)up->kstack+KSTACK; else if((uintptr)&l >= (uintptr)m->stack && (uintptr)&l <= (uintptr)m+MACHSIZE) estack = (uintptr)m+MACHSIZE; else return; x += iprint("estackx %p\n", estack); for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){ v = *(uintptr*)l; if((KTZERO < v && v < (uintptr)&etext) || estack-l < 32){ /* * Could Pick off general CALL (((uchar*)v)[-5] == 0xE8) * and CALL indirect through AX * (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0), * but this is too clever and misses faulting address. */ x += iprint("%.8p=%.8p ", l, v); i++; } if(i == 4){ i = 0; x += iprint("\n"); } } if(i) iprint("\n"); iprint("EOF\n"); if(ureg->trap != VectorNMI) return; i = 0; for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){ iprint("%.8p ", *(uintptr*)l); if(++i == 8){ i = 0; iprint("\n"); } } if(i) iprint("\n"); } void dumpstack(void) { callwithureg(_dumpstack); } static void debugbpt(Ureg* ureg, void*) { char buf[ERRMAX]; if(up == 0) panic("kernel bpt"); /* restore pc to instruction that caused the trap */ ureg->pc--; sprint(buf, "sys: breakpoint"); postnote(up, 1, buf, NDebug); } static void doublefault(Ureg*, void*) { panic("double fault"); } static void unexpected(Ureg* ureg, void*) { print("unexpected trap %lud; ignoring\n", ureg->trap); } extern void checkpages(void); extern void checkfault(ulong, ulong); static void fault386(Ureg* ureg, void*) { ulong addr; int read, user, n, insyscall; char buf[ERRMAX]; addr = getcr2(); read = !(ureg->ecode & 2); user = userureg(ureg); if(!user){ if(vmapsync(addr)) return; if(addr >= USTKTOP) panic("kernel fault: bad address pc=0x%.8lux addr=0x%.8lux", ureg->pc, addr); if(up == nil) panic("kernel fault: no user process pc=0x%.8lux addr=0x%.8lux", ureg->pc, addr); } if(up == nil) panic("user fault: up=0 pc=0x%.8lux addr=0x%.8lux", ureg->pc, addr); insyscall = up->insyscall; up->insyscall = 1; n = fault(addr, read); if(n < 0){ if(!user){ dumpregs(ureg); panic("fault: 0x%lux", addr); } checkpages(); checkfault(addr, ureg->pc); sprint(buf, "sys: trap: fault %s addr=0x%lux", read ? "read" : "write", addr); postnote(up, 1, buf, NDebug); } up->insyscall = insyscall; } /* * system calls */ #include "../port/systab.h" /* * Syscall is called directly from assembler without going through trap(). */ void syscall(Ureg* ureg) { char *e; ulong sp; long ret; int i, s; ulong scallnr; if(!userureg(ureg)) panic("syscall: cs 0x%4.4luX", ureg->cs); cycles(&up->kentry); m->syscall++; up->insyscall = 1; up->pc = ureg->pc; up->dbgreg = ureg; if(up->procctl == Proc_tracesyscall){ up->procctl = Proc_stopme; procctl(up); } scallnr = ureg->ax; up->scallnr = scallnr; if(scallnr == RFORK && up->fpstate == FPactive){ fpsave(&up->fpsave); up->fpstate = FPinactive; } spllo(); sp = ureg->usp; up->nerrlab = 0; ret = -1; if(!waserror()){ if(scallnr >= nsyscall || systab[scallnr] == 0){ pprint("bad sys call number %lud pc %lux\n", scallnr, ureg->pc); postnote(up, 1, "sys: bad sys call", NDebug); error(Ebadarg); } if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD)) validaddr(sp, sizeof(Sargs)+BY2WD, 0); up->s = *((Sargs*)(sp+BY2WD)); up->psstate = sysctab[scallnr]; ret = systab[scallnr](up->s.args); poperror(); }else{ /* failure: save the error buffer for errstr */ e = up->syserrstr; up->syserrstr = up->errstr; up->errstr = e; if(0 && up->pid == 1) print("syscall %lud error %s\n", scallnr, up->syserrstr); } if(up->nerrlab){ print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab); for(i = 0; i < NERR; i++) print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc); panic("error stack"); } /* * Put return value in frame. On the x86 the syscall is * just another trap and the return value from syscall is * ignored. On other machines the return value is put into * the results register by caller of syscall. */ ureg->ax = ret; if(up->procctl == Proc_tracesyscall){ up->procctl = Proc_stopme; s = splhi(); procctl(up); splx(s); } up->insyscall = 0; up->psstate = 0; if(scallnr == NOTED) noted(ureg, *(ulong*)(sp+BY2WD)); if(scallnr!=RFORK && (up->procctl || up->nnote)){ splhi(); notify(ureg); } /* if we delayed sched because we held a lock, sched now */ if(up->delaysched) sched(); kexit(ureg); } /* * Call user, if necessary, with note. * Pass user the Ureg struct and the note on his stack. */ int notify(Ureg* ureg) { int l; ulong s, sp; Note *n; if(up->procctl) procctl(up); if(up->nnote == 0) return 0; if(up->fpstate == FPactive){ fpsave(&up->fpsave); up->fpstate = FPinactive; } up->fpstate |= FPillegal; s = spllo(); qlock(&up->debug); up->notepending = 0; n = &up->note[0]; if(strncmp(n->msg, "sys:", 4) == 0){ l = strlen(n->msg); if(l > ERRMAX-15) /* " pc=0x12345678\0" */ l = ERRMAX-15; sprint(n->msg+l, " pc=0x%.8lux", ureg->pc); } if(n->flag!=NUser && (up->notified || up->notify==0)){ if(n->flag == NDebug) pprint("suicide: %s\n", n->msg); qunlock(&up->debug); pexit(n->msg, n->flag!=NDebug); } if(up->notified){ qunlock(&up->debug); splhi(); return 0; } if(!up->notify){ qunlock(&up->debug); pexit(n->msg, n->flag!=NDebug); } sp = ureg->usp; sp -= 256; /* debugging: preserve context causing problem */ sp -= sizeof(Ureg); if(0) print("%s %lud: notify %.8lux %.8lux %.8lux %s\n", up->text, up->pid, ureg->pc, ureg->usp, sp, n->msg); if(!okaddr((ulong)up->notify, 1, 0) || !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)){ pprint("suicide: bad address in notify\n"); qunlock(&up->debug); pexit("Suicide", 0); } memmove((Ureg*)sp, ureg, sizeof(Ureg)); *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ up->ureg = (void*)sp; sp -= BY2WD+ERRMAX; memmove((char*)sp, up->note[0].msg, ERRMAX); sp -= 3*BY2WD; *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ *(ulong*)(sp+1*BY2WD) = (ulong)up->ureg; /* arg 1 is ureg* */ *(ulong*)(sp+0*BY2WD) = 0; /* arg 0 is pc */ ureg->usp = sp; ureg->pc = (ulong)up->notify; ureg->cs = UESEL; ureg->ss = ureg->ds = ureg->es = UDSEL; up->notified = 1; up->nnote--; memmove(&up->lastnote, &up->note[0], sizeof(Note)); memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); qunlock(&up->debug); splx(s); return 1; } /* * Return user to state before notify() */ void noted(Ureg* ureg, ulong arg0) { Ureg *nureg; ulong oureg, sp; qlock(&up->debug); if(arg0!=NRSTR && !up->notified) { qunlock(&up->debug); pprint("call to noted() when not notified\n"); pexit("Suicide", 0); } up->notified = 0; nureg = up->ureg; /* pointer to user returned Ureg struct */ up->fpstate &= ~FPillegal; /* sanity clause */ oureg = (ulong)nureg; if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ pprint("bad ureg in noted or call to noted when not notified\n"); qunlock(&up->debug); pexit("Suicide", 0); } /* don't let user change system flags */ nureg->flags = (ureg->flags & ~0xCD5) | (nureg->flags & 0xCD5); nureg->cs |= 3; nureg->ss |= 3; memmove(ureg, nureg, sizeof(Ureg)); switch(arg0){ case NCONT: case NRSTR: if(0) print("%s %lud: noted %.8lux %.8lux\n", up->text, up->pid, nureg->pc, nureg->usp); if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){ qunlock(&up->debug); pprint("suicide: trap in noted\n"); pexit("Suicide", 0); } up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD)); qunlock(&up->debug); break; case NSAVE: if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->usp, BY2WD, 0)){ qunlock(&up->debug); pprint("suicide: trap in noted\n"); pexit("Suicide", 0); } qunlock(&up->debug); sp = oureg-4*BY2WD-ERRMAX; splhi(); ureg->sp = sp; ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ ((ulong*)sp)[0] = 0; /* arg 0 is pc */ break; default: pprint("unknown noted arg 0x%lux\n", arg0); up->lastnote.flag = NDebug; /* fall through */ case NDFLT: if(up->lastnote.flag == NDebug){ qunlock(&up->debug); pprint("suicide: %s\n", up->lastnote.msg); } else qunlock(&up->debug); pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); } } long execregs(ulong entry, ulong ssize, ulong nargs) { ulong *sp; Ureg *ureg; up->fpstate = FPinit; fpoff(); sp = (ulong*)(USTKTOP - ssize); *--sp = nargs; ureg = up->dbgreg; ureg->usp = (ulong)sp; ureg->pc = entry; ureg->cs = UESEL; ureg->ss = ureg->ds = ureg->es = UDSEL; ureg->fs = ureg->gs = NULLSEL; return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ } /* * return the userpc the last exception happened at */ ulong userpc(void) { Ureg *ureg; ureg = (Ureg*)up->dbgreg; return ureg->pc; } /* This routine must save the values of registers the user is not permitted * to write from devproc and then restore the saved values before returning. */ void setregisters(Ureg* ureg, char* pureg, char* uva, int n) { ulong flags; flags = ureg->flags; memmove(pureg, uva, n); /* don't allow chaning system flags */ ureg->flags = (ureg->flags & 0xCD5) | (flags & ~0xCD5); ureg->cs |= 3; ureg->ss |= 3; } static void linkproc(void) { spllo(); up->kpfun(up->kparg); pexit("kproc dying", 0); } void kprocchild(Proc* p, void (*func)(void*), void* arg) { /* * gotolabel() needs a word on the stack in * which to place the return PC used to jump * to linkproc(). */ p->sched.pc = (ulong)linkproc; p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD; p->kpfun = func; p->kparg = arg; } void forkchild(Proc *p, Ureg *ureg) { Ureg *cureg; /* * Add 2*BY2WD to the stack to account for * - the return PC * - trap's argument (ur) */ p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Ureg)+2*BY2WD); p->sched.pc = (ulong)forkret; cureg = (Ureg*)(p->sched.sp+2*BY2WD); memmove(cureg, ureg, sizeof(Ureg)); /* return value of syscall in child */ cureg->ax = 0; /* Things from bottom of syscall which were never executed */ p->psstate = 0; p->insyscall = 0; } /* Give enough context in the ureg to produce a kernel stack for * a sleeping process */ void setkernur(Ureg* ureg, Proc* p) { ureg->pc = p->sched.pc; ureg->sp = p->sched.sp+4; } ulong dbgpc(Proc *p) { Ureg *ureg; ureg = p->dbgreg; if(ureg == 0) return 0; return ureg->pc; } l += sizeof(uintptr)){ v = *(uintptr*)l; if((KTZERO < v && v < (uintptr)&etext) || estack-l < 32){ /* * Could Pick off general CALL (((uchar*)v)[-5] == 0xE8) * and CALL indirect through AX * (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0), * but this is too clever and misses faulting address. */ x += iprint("%.8p=%.8p ", l, v); i++; } if(i == 4){ i = segdescpatch/9/pc/mmu.c 664 0 0 60554 11342263221 16373ustar00cinap_lenrekcinap_lenrek/* * Memory mappings. Life was easier when 2G of memory was enough. * * The kernel memory starts at KZERO, with the text loaded at KZERO+1M * (9load sits under 1M during the load). The memory from KZERO to the * top of memory is mapped 1-1 with physical memory, starting at physical * address 0. All kernel memory and data structures (i.e., the entries stored * into conf.mem) must sit in this physical range: if KZERO is at 0xF0000000, * then the kernel can only have 256MB of memory for itself. * * The 256M below KZERO comprises three parts. The lowest 4M is the * virtual page table, a virtual address representation of the current * page table tree. The second 4M is used for temporary per-process * mappings managed by kmap and kunmap. The remaining 248M is used * for global (shared by all procs and all processors) device memory * mappings and managed by vmap and vunmap. The total amount (256M) * could probably be reduced somewhat if desired. The largest device * mapping is that of the video card, and even though modern video cards * have embarrassing amounts of memory, the video drivers only use one * frame buffer worth (at most 16M). Each is described in more detail below. * * The VPT is a 4M frame constructed by inserting the pdb into itself. * This short-circuits one level of the page tables, with the result that * the contents of second-level page tables can be accessed at VPT. * We use the VPT to edit the page tables (see mmu) after inserting them * into the page directory. It is a convenient mechanism for mapping what * might be otherwise-inaccessible pages. The idea was borrowed from * the Exokernel. * * The VPT doesn't solve all our problems, because we still need to * prepare page directories before we can install them. For that, we * use tmpmap/tmpunmap, which map a single page at TMPADDR. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" /* * Simple segment descriptors with no translation. */ #define DATASEGM(p) { 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW } #define EXECSEGM(p) { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } #define EXEC16SEGM(p) { 0xFFFF, SEGG|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } #define TSSSEGM(b,p) { ((b)<<16)|sizeof(Tss),\ ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP } Segdesc gdt[NGDT] = { [NULLSEG] { 0, 0}, /* null descriptor */ [KDSEG] DATASEGM(0), /* kernel data/stack */ [KESEG] EXECSEGM(0), /* kernel code */ [UDSEG] DATASEGM(3), /* user data/stack */ [UESEG] EXECSEGM(3), /* user code */ [TSSSEG] TSSSEGM(0,0), /* tss segment */ [KESEG16] EXEC16SEGM(0), /* kernel code 16-bit */ }; static int didmmuinit; static void taskswitch(ulong, ulong); static void memglobal(void); #define vpt ((ulong*)VPT) #define VPTX(va) (((ulong)(va))>>12) #define vpd (vpt+VPTX(VPT)) void mmuinit0(void) { memmove(m->gdt, gdt, sizeof gdt); } void mmuinit(void) { ulong x, *p; ushort ptr[3]; didmmuinit = 1; if(0) print("vpt=%#.8ux vpd=%#p kmap=%#.8ux\n", VPT, vpd, KMAP); memglobal(); m->pdb[PDX(VPT)] = PADDR(m->pdb)|PTEWRITE|PTEVALID; m->tss = malloc(sizeof(Tss)); memset(m->tss, 0, sizeof(Tss)); m->tss->iomap = 0xDFFF<<16; /* * We used to keep the GDT in the Mach structure, but it * turns out that that slows down access to the rest of the * page. Since the Mach structure is accessed quite often, * it pays off anywhere from a factor of 1.25 to 2 on real * hardware to separate them (the AMDs are more sensitive * than Intels in this regard). Under VMware it pays off * a factor of about 10 to 100. */ memmove(m->gdt, gdt, sizeof gdt); x = (ulong)m->tss; m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss); m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP; ptr[0] = sizeof(gdt)-1; x = (ulong)m->gdt; ptr[1] = x & 0xFFFF; ptr[2] = (x>>16) & 0xFFFF; lgdt(ptr); ptr[0] = sizeof(Segdesc)*256-1; x = IDTADDR; ptr[1] = x & 0xFFFF; ptr[2] = (x>>16) & 0xFFFF; lidt(ptr); /* make kernel text unwritable */ for(x = KTZERO; x < (ulong)etext; x += BY2PG){ p = mmuwalk(m->pdb, x, 2, 0); if(p == nil) panic("mmuinit"); *p &= ~PTEWRITE; } taskswitch(PADDR(m->pdb), (ulong)m + BY2PG); ltr(TSSSEL); } /* * On processors that support it, we set the PTEGLOBAL bit in * page table and page directory entries that map kernel memory. * Doing this tells the processor not to bother flushing them * from the TLB when doing the TLB flush associated with a * context switch (write to CR3). Since kernel memory mappings * are never removed, this is safe. (If we ever remove kernel memory * mappings, we can do a full flush by turning off the PGE bit in CR4, * writing to CR3, and then turning the PGE bit back on.) * * See also mmukmap below. * * Processor support for the PTEGLOBAL bit is enabled in devarch.c. */ static void memglobal(void) { int i, j; ulong *pde, *pte; /* only need to do this once, on bootstrap processor */ if(m->machno != 0) return; if(!m->havepge) return; pde = m->pdb; for(i=PDX(KZERO); i<1024; i++){ if(pde[i] & PTEVALID){ pde[i] |= PTEGLOBAL; if(!(pde[i] & PTESIZE)){ pte = KADDR(pde[i]&~(BY2PG-1)); for(j=0; j<1024; j++) if(pte[j] & PTEVALID) pte[j] |= PTEGLOBAL; } } } } /* * Flush all the user-space and device-mapping mmu info * for this process, because something has been deleted. * It will be paged back in on demand. */ void flushmmu(void) { int s; s = splhi(); up->newtlb = 1; mmuswitch(up); splx(s); } /* * Flush a single page mapping from the tlb. */ void flushpg(ulong va) { if(X86FAMILY(m->cpuidax) >= 4) invlpg(va); else putcr3(getcr3()); } /* * Allocate a new page for a page directory. * We keep a small cache of pre-initialized * page directories in each mach. */ static Page* mmupdballoc(void) { int s; Page *page; ulong *pdb; s = splhi(); m->pdballoc++; if(m->pdbpool == 0){ spllo(); page = newpage(0, 0, 0); page->va = (ulong)vpd; splhi(); pdb = tmpmap(page); memmove(pdb, m->pdb, BY2PG); pdb[PDX(VPT)] = page->pa|PTEWRITE|PTEVALID; /* set up VPT */ tmpunmap(pdb); }else{ page = m->pdbpool; m->pdbpool = page->next; m->pdbcnt--; } splx(s); return page; } static void mmupdbfree(Proc *proc, Page *p) { if(islo()) panic("mmupdbfree: islo"); m->pdbfree++; if(m->pdbcnt >= 10){ p->next = proc->mmufree; proc->mmufree = p; }else{ p->next = m->pdbpool; m->pdbpool = p; m->pdbcnt++; } } /* * A user-space memory segment has been deleted, or the * process is exiting. Clear all the pde entries for user-space * memory mappings and device mappings. Any entries that * are needed will be paged back in as necessary. */ static void mmuptefree(Proc* proc) { int s; ulong *pdb; Page **last, *page; if(proc->mmupdb == nil || proc->mmuused == nil) return; s = splhi(); pdb = tmpmap(proc->mmupdb); last = &proc->mmuused; for(page = *last; page; page = page->next){ pdb[page->daddr] = 0; last = &page->next; } tmpunmap(pdb); splx(s); *last = proc->mmufree; proc->mmufree = proc->mmuused; proc->mmuused = 0; } static void taskswitch(ulong pdb, ulong stack) { Tss *tss; tss = m->tss; tss->ss0 = KDSEL; tss->esp0 = stack; tss->ss1 = KDSEL; tss->esp1 = stack; tss->ss2 = KDSEL; tss->esp2 = stack; putcr3(pdb); } void mmuswitch(Proc* proc) { ulong *pdb; ulong x; int n; if(proc->newtlb){ mmuptefree(proc); proc->newtlb = 0; } if(proc->mmupdb){ pdb = tmpmap(proc->mmupdb); pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)]; tmpunmap(pdb); taskswitch(proc->mmupdb->pa, (ulong)(proc->kstack+KSTACK)); }else taskswitch(PADDR(m->pdb), (ulong)(proc->kstack+KSTACK)); memmove(&m->gdt[PROCSEG0], proc->gdt, sizeof(proc->gdt)); if((x = (ulong)proc->ldt) && (n = proc->nldt) > 0){ m->gdt[LDTSEG].d0 = (x<<16)|((n * sizeof(Segdesc)) - 1); m->gdt[LDTSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGLDT|SEGPL(0)|SEGP; lldt(LDTSEL); } else { lldt(NULLSEL); } } /* * Release any pages allocated for a page directory base or page-tables * for this process: * switch to the prototype pdb for this processor (m->pdb); * call mmuptefree() to place all pages used for page-tables (proc->mmuused) * onto the process' free list (proc->mmufree). This has the side-effect of * cleaning any user entries in the pdb (proc->mmupdb); * if there's a pdb put it in the cache of pre-initialised pdb's * for this processor (m->pdbpool) or on the process' free list; * finally, place any pages freed back into the free pool (palloc). * This routine is only called from schedinit() with palloc locked. */ void mmurelease(Proc* proc) { Page *page, *next; ulong *pdb; if(islo()) panic("mmurelease: islo"); taskswitch(PADDR(m->pdb), (ulong)m + BY2PG); if(proc->kmaptable){ if(proc->mmupdb == nil) panic("mmurelease: no mmupdb"); if(--proc->kmaptable->ref) panic("mmurelease: kmap ref %d", proc->kmaptable->ref); if(proc->nkmap) panic("mmurelease: nkmap %d", proc->nkmap); /* * remove kmaptable from pdb before putting pdb up for reuse. */ pdb = tmpmap(proc->mmupdb); if(PPN(pdb[PDX(KMAP)]) != proc->kmaptable->pa) panic("mmurelease: bad kmap pde %#.8lux kmap %#.8lux", pdb[PDX(KMAP)], proc->kmaptable->pa); pdb[PDX(KMAP)] = 0; tmpunmap(pdb); /* * move kmaptable to free list. */ pagechainhead(proc->kmaptable); proc->kmaptable = 0; } if(proc->mmupdb){ mmuptefree(proc); mmupdbfree(proc, proc->mmupdb); proc->mmupdb = 0; } for(page = proc->mmufree; page; page = next){ next = page->next; if(--page->ref) panic("mmurelease: page->ref %d", page->ref); pagechainhead(page); } if(proc->ldt){ lldt(NULLSEL); free(proc->ldt); proc->ldt = nil; proc->nldt = 0; } if(proc->mmufree && palloc.r.p) wakeup(&palloc.r); proc->mmufree = 0; } /* * Allocate and install pdb for the current process. */ static void upallocpdb(void) { int s; ulong *pdb; Page *page; if(up->mmupdb != nil) return; page = mmupdballoc(); s = splhi(); if(up->mmupdb != nil){ /* * Perhaps we got an interrupt while * mmupdballoc was sleeping and that * interrupt allocated an mmupdb? * Seems unlikely. */ mmupdbfree(up, page); splx(s); return; } pdb = tmpmap(page); pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)]; tmpunmap(pdb); up->mmupdb = page; putcr3(up->mmupdb->pa); splx(s); } /* * Update the mmu in response to a user fault. pa may have PTEWRITE set. */ void putmmu(ulong va, ulong pa, Page*) { int old, s; Page *page; if(up->mmupdb == nil) upallocpdb(); /* * We should be able to get through this with interrupts * turned on (if we get interrupted we'll just pick up * where we left off) but we get many faults accessing * vpt[] near the end of this function, and they always happen * after the process has been switched out and then * switched back, usually many times in a row (perhaps * it cannot switch back successfully for some reason). * * In any event, I'm tired of searching for this bug. * Turn off interrupts during putmmu even though * we shouldn't need to. - rsc */ s = splhi(); if(!(vpd[PDX(va)]&PTEVALID)){ if(up->mmufree == 0){ spllo(); page = newpage(0, 0, 0); splhi(); } else{ page = up->mmufree; up->mmufree = page->next; } vpd[PDX(va)] = PPN(page->pa)|PTEUSER|PTEWRITE|PTEVALID; /* page is now mapped into the VPT - clear it */ memset((void*)(VPT+PDX(va)*BY2PG), 0, BY2PG); page->daddr = PDX(va); page->next = up->mmuused; up->mmuused = page; } old = vpt[VPTX(va)]; vpt[VPTX(va)] = pa|PTEUSER|PTEVALID; if(old&PTEVALID) flushpg(va); if(getcr3() != up->mmupdb->pa) print("bad cr3 %#.8lux %#.8lux\n", getcr3(), up->mmupdb->pa); splx(s); } /* * Double-check the user MMU. * Error checking only. */ void checkmmu(ulong va, ulong pa) { if(up->mmupdb == 0) return; if(!(vpd[PDX(va)]&PTEVALID) || !(vpt[VPTX(va)]&PTEVALID)) return; if(PPN(vpt[VPTX(va)]) != pa) print("%ld %s: va=%#08lux pa=%#08lux pte=%#08lux\n", up->pid, up->text, va, pa, vpt[VPTX(va)]); } /* * Walk the page-table pointed to by pdb and return a pointer * to the entry for virtual address va at the requested level. * If the entry is invalid and create isn't requested then bail * out early. Otherwise, for the 2nd level walk, allocate a new * page-table page and register it in the 1st level. This is used * only to edit kernel mappings, which use pages from kernel memory, * so it's okay to use KADDR to look at the tables. */ ulong* mmuwalk(ulong* pdb, ulong va, int level, int create) { ulong *table; void *map; table = &pdb[PDX(va)]; if(!(*table & PTEVALID) && create == 0) return 0; switch(level){ default: return 0; case 1: return table; case 2: if(*table & PTESIZE) panic("mmuwalk2: va %luX entry %luX", va, *table); if(!(*table & PTEVALID)){ /* * Have to call low-level allocator from * memory.c if we haven't set up the xalloc * tables yet. */ if(didmmuinit) map = xspanalloc(BY2PG, BY2PG, 0); else map = rampage(); if(map == nil) panic("mmuwalk xspanalloc failed"); *table = PADDR(map)|PTEWRITE|PTEVALID; } table = KADDR(PPN(*table)); return &table[PTX(va)]; } } /* * Device mappings are shared by all procs and processors and * live in the virtual range VMAP to VMAP+VMAPSIZE. The master * copy of the mappings is stored in mach0->pdb, and they are * paged in from there as necessary by vmapsync during faults. */ static Lock vmaplock; static int findhole(ulong *a, int n, int count); static ulong vmapalloc(ulong size); static void pdbunmap(ulong*, ulong, int); /* * Add a device mapping to the vmap range. */ void* vmap(ulong pa, int size) { int osize; ulong o, va; /* * might be asking for less than a page. */ osize = size; o = pa & (BY2PG-1); pa -= o; size += o; size = ROUND(size, BY2PG); if(pa == 0){ print("vmap pa=0 pc=%#p\n", getcallerpc(&pa)); return nil; } ilock(&vmaplock); if((va = vmapalloc(size)) == 0 || pdbmap(MACHP(0)->pdb, pa|PTEUNCACHED|PTEWRITE, va, size) < 0){ iunlock(&vmaplock); return 0; } iunlock(&vmaplock); /* avoid trap on local processor for(i=0; i %#.8lux\n", pa+o, osize, va+o); return (void*)(va + o); } static int findhole(ulong *a, int n, int count) { int have, i; have = 0; for(i=0; i= count) return i+1 - have; } return -1; } /* * Look for free space in the vmap. */ static ulong vmapalloc(ulong size) { int i, n, o; ulong *vpdb; int vpdbsize; vpdb = &MACHP(0)->pdb[PDX(VMAP)]; vpdbsize = VMAPSIZE/(4*MB); if(size >= 4*MB){ n = (size+4*MB-1) / (4*MB); if((o = findhole(vpdb, vpdbsize, n)) != -1) return VMAP + o*4*MB; return 0; } n = (size+BY2PG-1) / BY2PG; for(i=0; i VMAP+VMAPSIZE) panic("vunmap va=%#.8lux size=%#x pc=%#.8lux", va, size, getcallerpc(&va)); pdbunmap(MACHP(0)->pdb, va, size); /* * Flush mapping from all the tlbs and copied pdbs. * This can be (and is) slow, since it is called only rarely. * It is possible for vunmap to be called with up == nil, * e.g. from the reset/init driver routines during system * boot. In that case it suffices to flush the MACH(0) TLB * and return. */ if(!active.thunderbirdsarego){ putcr3(PADDR(MACHP(0)->pdb)); return; } for(i=0; istate == Dead) continue; if(p != up) p->newtlb = 1; } for(i=0; iflushmmu = 1; } flushmmu(); for(i=0; imachno)) && nm->flushmmu) ; } } /* * Add kernel mappings for pa -> va for a section of size bytes. */ int pdbmap(ulong *pdb, ulong pa, ulong va, int size) { int pse; ulong pgsz, *pte, *table; ulong flag, off; flag = pa&0xFFF; pa &= ~0xFFF; if((MACHP(0)->cpuiddx & 0x08) && (getcr4() & 0x10)) pse = 1; else pse = 0; for(off=0; off= 4MB and processor can do it. */ if(pse && (pa+off)%(4*MB) == 0 && (va+off)%(4*MB) == 0 && (size-off) >= 4*MB){ *table = (pa+off)|flag|PTESIZE|PTEVALID; pgsz = 4*MB; }else{ pte = mmuwalk(pdb, va+off, 2, 1); if(*pte&PTEVALID) panic("vmap: va=%#.8lux pa=%#.8lux pte=%#.8lux", va+off, pa+off, *pte); *pte = (pa+off)|flag|PTEVALID; pgsz = BY2PG; } } return 0; } /* * Remove mappings. Must already exist, for sanity. * Only used for kernel mappings, so okay to use KADDR. */ static void pdbunmap(ulong *pdb, ulong va, int size) { ulong vae; ulong *table; vae = va+size; while(va < vae){ table = &pdb[PDX(va)]; if(!(*table & PTEVALID)){ panic("vunmap: not mapped"); /* va = (va+4*MB-1) & ~(4*MB-1); continue; */ } if(*table & PTESIZE){ *table = 0; va = (va+4*MB-1) & ~(4*MB-1); continue; } table = KADDR(PPN(*table)); if(!(table[PTX(va)] & PTEVALID)) panic("vunmap: not mapped"); table[PTX(va)] = 0; va += BY2PG; } } /* * Handle a fault by bringing vmap up to date. * Only copy pdb entries and they never go away, * so no locking needed. */ int vmapsync(ulong va) { ulong entry, *table; if(va < VMAP || va >= VMAP+VMAPSIZE) return 0; entry = MACHP(0)->pdb[PDX(va)]; if(!(entry&PTEVALID)) return 0; if(!(entry&PTESIZE)){ /* make sure entry will help the fault */ table = KADDR(PPN(entry)); if(!(table[PTX(va)]&PTEVALID)) return 0; } vpd[PDX(va)] = entry; /* * TLB doesn't cache negative results, so no flush needed. */ return 1; } /* * KMap is used to map individual pages into virtual memory. * It is rare to have more than a few KMaps at a time (in the * absence of interrupts, only two at a time are ever used, * but interrupts can stack). The mappings are local to a process, * so we can use the same range of virtual address space for * all processes without any coordination. */ #define kpt (vpt+VPTX(KMAP)) #define NKPT (KMAPSIZE/BY2PG) KMap* kmap(Page *page) { int i, o, s; if(up == nil) panic("kmap: up=0 pc=%#.8lux", getcallerpc(&page)); if(up->mmupdb == nil) upallocpdb(); if(up->nkmap < 0) panic("kmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap); /* * Splhi shouldn't be necessary here, but paranoia reigns. * See comment in putmmu above. */ s = splhi(); up->nkmap++; if(!(vpd[PDX(KMAP)]&PTEVALID)){ /* allocate page directory */ if(KMAPSIZE > BY2XPG) panic("bad kmapsize"); if(up->kmaptable != nil) panic("kmaptable"); spllo(); up->kmaptable = newpage(0, 0, 0); splhi(); vpd[PDX(KMAP)] = up->kmaptable->pa|PTEWRITE|PTEVALID; flushpg((ulong)kpt); memset(kpt, 0, BY2PG); kpt[0] = page->pa|PTEWRITE|PTEVALID; up->lastkmap = 0; splx(s); return (KMap*)KMAP; } if(up->kmaptable == nil) panic("no kmaptable"); o = up->lastkmap+1; for(i=0; ipa|PTEWRITE|PTEVALID; up->lastkmap = o; splx(s); return (KMap*)(KMAP+o*BY2PG); } } panic("out of kmap"); return nil; } void kunmap(KMap *k) { ulong va; va = (ulong)k; if(up->mmupdb == nil || !(vpd[PDX(KMAP)]&PTEVALID)) panic("kunmap: no kmaps"); if(va < KMAP || va >= KMAP+KMAPSIZE) panic("kunmap: bad address %#.8lux pc=%#p", va, getcallerpc(&k)); if(!(vpt[VPTX(va)]&PTEVALID)) panic("kunmap: not mapped %#.8lux pc=%#p", va, getcallerpc(&k)); up->nkmap--; if(up->nkmap < 0) panic("kunmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap); vpt[VPTX(va)] = 0; flushpg(va); } /* * Temporary one-page mapping used to edit page directories. * * The fasttmp #define controls whether the code optimizes * the case where the page is already mapped in the physical * memory window. */ #define fasttmp 1 void* tmpmap(Page *p) { ulong i; ulong *entry; if(islo()) panic("tmpaddr: islo"); if(fasttmp && p->pa < -KZERO) return KADDR(p->pa); /* * PDX(TMPADDR) == PDX(MACHADDR), so this * entry is private to the processor and shared * between up->mmupdb (if any) and m->pdb. */ entry = &vpt[VPTX(TMPADDR)]; if(!(*entry&PTEVALID)){ for(i=KZERO; i<=CPU0MACH; i+=BY2PG) print("%#p: *%#p=%#p (vpt=%#p index=%#p)\n", i, &vpt[VPTX(i)], vpt[VPTX(i)], vpt, VPTX(i)); panic("tmpmap: no entry"); } if(PPN(*entry) != PPN(TMPADDR-KZERO)) panic("tmpmap: already mapped entry=%#.8lux", *entry); *entry = p->pa|PTEWRITE|PTEVALID; flushpg(TMPADDR); return (void*)TMPADDR; } void tmpunmap(void *v) { ulong *entry; if(islo()) panic("tmpaddr: islo"); if(fasttmp && (ulong)v >= KZERO && v != (void*)TMPADDR) return; if(v != (void*)TMPADDR) panic("tmpunmap: bad address"); entry = &vpt[VPTX(TMPADDR)]; if(!(*entry&PTEVALID) || PPN(*entry) == PPN(PADDR(TMPADDR))) panic("tmpmap: not mapped entry=%#.8lux", *entry); *entry = PPN(TMPADDR-KZERO)|PTEWRITE|PTEVALID; flushpg(TMPADDR); } /* * These could go back to being macros once the kernel is debugged, * but the extra checking is nice to have. */ void* kaddr(ulong pa) { if(pa > (ulong)-KZERO) panic("kaddr: pa=%#.8lux", pa); return (void*)(pa+KZERO); } ulong paddr(void *v) { ulong va; va = (ulong)v; if(va < KZERO) panic("paddr: va=%#.8lux pc=%#p", va, getcallerpc(&v)); return va-KZERO; } /* * More debugging. */ void countpagerefs(ulong *ref, int print) { int i, n; Mach *mm; Page *pg; Proc *p; n = 0; for(i=0; immupdb){ if(print){ if(ref[pagenumber(p->mmupdb)]) iprint("page %#.8lux is proc %d (pid %lud) pdb\n", p->mmupdb->pa, i, p->pid); continue; } if(ref[pagenumber(p->mmupdb)]++ == 0) n++; else iprint("page %#.8lux is proc %d (pid %lud) pdb but has other refs!\n", p->mmupdb->pa, i, p->pid); } if(p->kmaptable){ if(print){ if(ref[pagenumber(p->kmaptable)]) iprint("page %#.8lux is proc %d (pid %lud) kmaptable\n", p->kmaptable->pa, i, p->pid); continue; } if(ref[pagenumber(p->kmaptable)]++ == 0) n++; else iprint("page %#.8lux is proc %d (pid %lud) kmaptable but has other refs!\n", p->kmaptable->pa, i, p->pid); } for(pg=p->mmuused; pg; pg=pg->next){ if(print){ if(ref[pagenumber(pg)]) iprint("page %#.8lux is on proc %d (pid %lud) mmuused\n", pg->pa, i, p->pid); continue; } if(ref[pagenumber(pg)]++ == 0) n++; else iprint("page %#.8lux is on proc %d (pid %lud) mmuused but has other refs!\n", pg->pa, i, p->pid); } for(pg=p->mmufree; pg; pg=pg->next){ if(print){ if(ref[pagenumber(pg)]) iprint("page %#.8lux is on proc %d (pid %lud) mmufree\n", pg->pa, i, p->pid); continue; } if(ref[pagenumber(pg)]++ == 0) n++; else iprint("page %#.8lux is on proc %d (pid %lud) mmufree but has other refs!\n", pg->pa, i, p->pid); } } if(!print) iprint("%d pages in proc mmu\n", n); n = 0; for(i=0; ipdbpool; pg; pg=pg->next){ if(print){ if(ref[pagenumber(pg)]) iprint("page %#.8lux is in cpu%d pdbpool\n", pg->pa, i); continue; } if(ref[pagenumber(pg)]++ == 0) n++; else iprint("page %#.8lux is in cpu%d pdbpool but has other refs!\n", pg->pa, i); } } if(!print){ iprint("%d pages in mach pdbpools\n", n); for(i=0; ipdballoc, MACHP(i)->pdbfree); } } void checkfault(ulong, ulong) { } /* * Return the number of bytes that can be accessed via KADDR(pa). * If pa is not a valid argument to KADDR, return 0. */ ulong cankaddr(ulong pa) { if(pa >= -KZERO) return 0; return -KZERO - pa; } e in the vmap. */ static ulong vmapalloc(ulong size) { int i, n, o; ulong *vpdb; int vpdbsize; vpdb = &MACHP(0)->pdb[PDX(VMAP)]; vpdbsize = segdescpatch/9/pc/l.s 664 0 0 72456 11342263221 16054ustar00cinap_lenrekcinap_lenrek#include "mem.h" #include "/sys/src/boot/pc/x16.h" #undef DELAY #define PADDR(a) ((a) & ~KZERO) #define KADDR(a) (KZERO|(a)) /* * Some machine instructions not handled by 8[al]. */ #define OP16 BYTE $0x66 #define DELAY BYTE $0xEB; BYTE $0x00 /* JMP .+2 */ #define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */ #define WRMSR BYTE $0x0F; BYTE $0x30 /* WRMSR, argument in AX/DX (lo/hi) */ #define RDTSC BYTE $0x0F; BYTE $0x31 /* RDTSC, result in AX/DX (lo/hi) */ #define RDMSR BYTE $0x0F; BYTE $0x32 /* RDMSR, result in AX/DX (lo/hi) */ #define HLT BYTE $0xF4 #define INVLPG BYTE $0x0F; BYTE $0x01; BYTE $0x39 /* INVLPG (%ecx) */ #define WBINVD BYTE $0x0F; BYTE $0x09 /* * Macros for calculating offsets within the page directory base * and page tables. Note that these are assembler-specific hence * the '<<2'. */ #define PDO(a) (((((a))>>22) & 0x03FF)<<2) #define PTO(a) (((((a))>>12) & 0x03FF)<<2) /* * For backwards compatiblity with 9load - should go away when 9load is changed * 9load currently sets up the mmu, however the first 16MB of memory is identity * mapped, so behave as if the mmu was not setup */ TEXT _startKADDR(SB), $0 MOVL $_startPADDR(SB), AX ANDL $~KZERO, AX JMP* AX /* * Must be 4-byte aligned. */ TEXT _multibootheader(SB), $0 LONG $0x1BADB002 /* magic */ LONG $0x00010003 /* flags */ LONG $-(0x1BADB002 + 0x00010003) /* checksum */ LONG $_multibootheader-KZERO(SB) /* header_addr */ LONG $_startKADDR-KZERO(SB) /* load_addr */ LONG $edata-KZERO(SB) /* load_end_addr */ LONG $end-KZERO(SB) /* bss_end_addr */ LONG $_startKADDR-KZERO(SB) /* entry_addr */ LONG $0 /* mode_type */ LONG $0 /* width */ LONG $0 /* height */ LONG $0 /* depth */ /* * In protected mode with paging turned off and segment registers setup * to linear map all memory. Entered via a jump to PADDR(entry), * the physical address of the virtual kernel entry point of KADDR(entry). * Make the basic page tables for processor 0. Six pages are needed for * the basic set: * a page directory; * page tables for mapping the first 8MB of physical memory to KZERO; * a page for the GDT; * virtual and physical pages for mapping the Mach structure. * The remaining PTEs will be allocated later when memory is sized. * An identity mmu map is also needed for the switch to virtual mode. * This identity mapping is removed once the MMU is going and the JMP has * been made to virtual memory. */ TEXT _startPADDR(SB), $0 CLI /* make sure interrupts are off */ /* set up the gdt so we have sane plan 9 style gdts. */ MOVL $tgdtptr(SB), AX ANDL $~KZERO, AX MOVL (AX), GDTR MOVW $1, AX MOVW AX, MSW /* clear prefetch queue (weird code to avoid optimizations) */ DELAY /* set segs to something sane (avoid traps later) */ MOVW $(1<<3), AX MOVW AX, DS MOVW AX, SS MOVW AX, ES MOVW AX, FS MOVW AX, GS /* JMP $(2<<3):$mode32bit(SB) /**/ BYTE $0xEA LONG $mode32bit-KZERO(SB) WORD $(2<<3) /* * gdt to get us to 32-bit/segmented/unpaged mode */ TEXT tgdt(SB), $0 /* null descriptor */ LONG $0 LONG $0 /* data segment descriptor for 4 gigabytes (PL 0) */ LONG $(0xFFFF) LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) /* exec segment descriptor for 4 gigabytes (PL 0) */ LONG $(0xFFFF) LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) /* * pointer to initial gdt * Note the -KZERO which puts the physical address in the gdtptr. * that's needed as we start executing in physical addresses. */ TEXT tgdtptr(SB), $0 WORD $(3*8) LONG $tgdt-KZERO(SB) TEXT m0rgdtptr(SB), $0 WORD $(NGDT*8-1) LONG $(CPU0GDT-KZERO) TEXT m0gdtptr(SB), $0 WORD $(NGDT*8-1) LONG $CPU0GDT TEXT m0idtptr(SB), $0 WORD $(256*8-1) LONG $IDTADDR TEXT mode32bit(SB), $0 /* At this point, the GDT setup is done. */ MOVL $PADDR(CPU0PDB), DI /* clear 4 pages for the tables etc. */ XORL AX, AX MOVL $(4*BY2PG), CX SHRL $2, CX CLD REP; STOSL MOVL $PADDR(CPU0PDB), AX ADDL $PDO(KZERO), AX /* page directory offset for KZERO */ MOVL $PADDR(CPU0PTE), (AX) /* PTE's for KZERO */ MOVL $(PTEWRITE|PTEVALID), BX /* page permissions */ ORL BX, (AX) ADDL $4, AX MOVL $PADDR(CPU0PTE1), (AX) /* PTE's for KZERO+4MB */ MOVL $(PTEWRITE|PTEVALID), BX /* page permissions */ ORL BX, (AX) MOVL $PADDR(CPU0PTE), AX /* first page of page table */ MOVL $1024, CX /* 1024 pages in 4MB */ _setpte: MOVL BX, (AX) ADDL $(1<machno */ ADDL $(MACHSIZE-4), SP /* initialise stack */ /* * Need to do one final thing to ensure a clean machine environment, * clear the EFLAGS register, which can only be done once there is a stack. */ MOVL $0, AX PUSHL AX POPFL CALL main(SB) /* * Park a processor. Should never fall through a return from main to here, * should only be called by application processors when shutting down. */ TEXT idle(SB), $0 _idle: STI HLT JMP _idle /* * Save registers. */ TEXT saveregs(SB), $0 /* appease 8l */ SUBL $32, SP POPL AX POPL AX POPL AX POPL AX POPL AX POPL AX POPL AX POPL AX PUSHL AX PUSHL BX PUSHL CX PUSHL DX PUSHL BP PUSHL DI PUSHL SI PUSHFL XCHGL 32(SP), AX /* swap return PC and saved flags */ XCHGL 0(SP), AX XCHGL 32(SP), AX RET TEXT restoreregs(SB), $0 /* appease 8l */ PUSHL AX PUSHL AX PUSHL AX PUSHL AX PUSHL AX PUSHL AX PUSHL AX PUSHL AX ADDL $32, SP XCHGL 32(SP), AX /* swap return PC and saved flags */ XCHGL 0(SP), AX XCHGL 32(SP), AX POPFL POPL SI POPL DI POPL BP POPL DX POPL CX POPL BX POPL AX RET /* * Assumed to be in protected mode at time of call. * Switch to real mode, execute an interrupt, and * then switch back to protected mode. * * Assumes: * * - no device interrupts are going to come in * - 0-16MB is identity mapped in page tables * - realmode() has copied us down from 0x100000 to 0x8000 * - can use code segment 0x0800 in real mode * to get at l.s code * - l.s code is less than 1 page */ #define RELOC (RMCODE-KTZERO) TEXT realmodeidtptr(SB), $0 WORD $(4*256-1) LONG $0 TEXT realmode0(SB), $0 CALL saveregs(SB) /* switch to low code address */ LEAL physcode-KZERO(SB), AX JMP *AX TEXT physcode(SB), $0 /* switch to low stack */ MOVL SP, AX MOVL $0x7C00, SP PUSHL AX /* change gdt to physical pointer */ MOVL m0rgdtptr-KZERO(SB), GDTR /* load IDT with real-mode version*/ MOVL realmodeidtptr-KZERO(SB), IDTR /* edit INT $0x00 instruction below */ MOVL $(RMUADDR-KZERO+48), AX /* &rmu.trap */ MOVL (AX), AX MOVB AX, realmodeintrinst+(-KZERO+1+RELOC)(SB) /* disable paging */ MOVL CR0, AX ANDL $0x7FFFFFFF, AX MOVL AX, CR0 /* JMP .+2 to clear prefetch queue*/ BYTE $0xEB; BYTE $0x00 /* jump to 16-bit code segment */ /* JMPFAR SELECTOR(KESEG16, SELGDT, 0):$again16bit(SB) /**/ BYTE $0xEA LONG $again16bit-KZERO(SB) WORD $SELECTOR(KESEG16, SELGDT, 0) TEXT again16bit(SB), $0 /* * Now in 16-bit compatibility mode. * These are 32-bit instructions being interpreted * as 16-bit instructions. I'm being lazy and * not using the macros because I know when * the 16- and 32-bit instructions look the same * or close enough. */ /* disable protected mode and jump to real mode cs */ OPSIZE; MOVL CR0, AX OPSIZE; XORL BX, BX OPSIZE; INCL BX OPSIZE; XORL BX, AX OPSIZE; MOVL AX, CR0 /* JMPFAR 0x0800:now16real */ BYTE $0xEA WORD $now16real-KZERO(SB) WORD $0x0800 TEXT now16real(SB), $0 /* copy the registers for the bios call */ LWI(0x0000, rAX) MOVW AX,SS LWI(RMUADDR, rBP) /* offsets are in Ureg */ LXW(44, xBP, rAX) MOVW AX, DS LXW(40, xBP, rAX) MOVW AX, ES OPSIZE; LXW(0, xBP, rDI) OPSIZE; LXW(4, xBP, rSI) OPSIZE; LXW(16, xBP, rBX) OPSIZE; LXW(20, xBP, rDX) OPSIZE; LXW(24, xBP, rCX) OPSIZE; LXW(28, xBP, rAX) CLC TEXT realmodeintrinst(SB), $0 INT $0x00 /* save the registers after the call */ LWI(0x7bfc, rSP) OPSIZE; PUSHFL OPSIZE; PUSHL AX LWI(0, rAX) MOVW AX,SS LWI(RMUADDR, rBP) OPSIZE; SXW(rDI, 0, xBP) OPSIZE; SXW(rSI, 4, xBP) OPSIZE; SXW(rBX, 16, xBP) OPSIZE; SXW(rDX, 20, xBP) OPSIZE; SXW(rCX, 24, xBP) OPSIZE; POPL AX OPSIZE; SXW(rAX, 28, xBP) MOVW DS, AX OPSIZE; SXW(rAX, 44, xBP) MOVW ES, AX OPSIZE; SXW(rAX, 40, xBP) OPSIZE; POPL AX OPSIZE; SXW(rAX, 64, xBP) /* flags */ /* re-enter protected mode and jump to 32-bit code */ OPSIZE; MOVL $1, AX OPSIZE; MOVL AX, CR0 /* JMPFAR SELECTOR(KESEG, SELGDT, 0):$again32bit(SB) /**/ OPSIZE BYTE $0xEA LONG $again32bit-KZERO(SB) WORD $SELECTOR(KESEG, SELGDT, 0) TEXT again32bit(SB), $0 MOVW $SELECTOR(KDSEG, SELGDT, 0),AX MOVW AX,DS MOVW AX,SS MOVW AX,ES MOVW AX,FS MOVW AX,GS /* enable paging and jump to kzero-address code */ MOVL CR0, AX ORL $0x80010000, AX /* PG|WP */ MOVL AX, CR0 LEAL again32kzero(SB), AX JMP* AX TEXT again32kzero(SB), $0 /* breathe a sigh of relief - back in 32-bit protected mode */ /* switch to old stack */ PUSHL AX /* match popl below for 8l */ MOVL $0x7BFC, SP POPL SP /* restore idt */ MOVL m0idtptr(SB),IDTR /* restore gdt */ MOVL m0gdtptr(SB), GDTR CALL restoreregs(SB) RET /* * BIOS32. */ TEXT bios32call(SB), $0 MOVL ci+0(FP), BP MOVL 0(BP), AX MOVL 4(BP), BX MOVL 8(BP), CX MOVL 12(BP), DX MOVL 16(BP), SI MOVL 20(BP), DI PUSHL BP MOVL 12(SP), BP /* ptr */ BYTE $0xFF; BYTE $0x5D; BYTE $0x00 /* CALL FAR 0(BP) */ POPL BP MOVL DI, 20(BP) MOVL SI, 16(BP) MOVL DX, 12(BP) MOVL CX, 8(BP) MOVL BX, 4(BP) MOVL AX, 0(BP) XORL AX, AX JCC _bios32xxret INCL AX _bios32xxret: RET /* * Port I/O. * in[bsl] input a byte|short|long * ins[bsl] input a string of bytes|shorts|longs * out[bsl] output a byte|short|long * outs[bsl] output a string of bytes|shorts|longs */ TEXT inb(SB), $0 MOVL port+0(FP), DX XORL AX, AX INB RET TEXT insb(SB), $0 MOVL port+0(FP), DX MOVL address+4(FP), DI MOVL count+8(FP), CX CLD REP; INSB RET TEXT ins(SB), $0 MOVL port+0(FP), DX XORL AX, AX OP16; INL RET TEXT inss(SB), $0 MOVL port+0(FP), DX MOVL address+4(FP), DI MOVL count+8(FP), CX CLD REP; OP16; INSL RET TEXT inl(SB), $0 MOVL port+0(FP), DX INL RET TEXT insl(SB), $0 MOVL port+0(FP), DX MOVL address+4(FP), DI MOVL count+8(FP), CX CLD REP; INSL RET TEXT outb(SB), $0 MOVL port+0(FP), DX MOVL byte+4(FP), AX OUTB RET TEXT outsb(SB), $0 MOVL port+0(FP), DX MOVL address+4(FP), SI MOVL count+8(FP), CX CLD REP; OUTSB RET TEXT outs(SB), $0 MOVL port+0(FP), DX MOVL short+4(FP), AX OP16; OUTL RET TEXT outss(SB), $0 MOVL port+0(FP), DX MOVL address+4(FP), SI MOVL count+8(FP), CX CLD REP; OP16; OUTSL RET TEXT outl(SB), $0 MOVL port+0(FP), DX MOVL long+4(FP), AX OUTL RET TEXT outsl(SB), $0 MOVL port+0(FP), DX MOVL address+4(FP), SI MOVL count+8(FP), CX CLD REP; OUTSL RET /* * Read/write various system registers. * CR4 and the 'model specific registers' should only be read/written * after it has been determined the processor supports them */ TEXT lgdt(SB), $0 /* GDTR - global descriptor table */ MOVL gdtptr+0(FP), AX MOVL (AX), GDTR RET TEXT lldt(SB), $0 /* LDTR - local descriptor table */ MOVL sel+0(FP), AX BYTE $0x0F; BYTE $0x00; BYTE $0xD0 /* LLDT AX */ RET TEXT lidt(SB), $0 /* IDTR - interrupt descriptor table */ MOVL idtptr+0(FP), AX MOVL (AX), IDTR RET TEXT ltr(SB), $0 /* TR - task register */ MOVL tptr+0(FP), AX MOVW AX, TASK RET TEXT getcr0(SB), $0 /* CR0 - processor control */ MOVL CR0, AX RET TEXT getcr2(SB), $0 /* CR2 - page fault linear address */ MOVL CR2, AX RET TEXT getcr3(SB), $0 /* CR3 - page directory base */ MOVL CR3, AX RET TEXT putcr0(SB), $0 MOVL cr0+0(FP), AX MOVL AX, CR0 RET TEXT putcr3(SB), $0 MOVL cr3+0(FP), AX MOVL AX, CR3 RET TEXT getcr4(SB), $0 /* CR4 - extensions */ MOVL CR4, AX RET TEXT putcr4(SB), $0 MOVL cr4+0(FP), AX MOVL AX, CR4 RET TEXT invlpg(SB), $0 /* 486+ only */ MOVL va+0(FP), CX INVLPG RET TEXT wbinvd(SB), $0 WBINVD RET TEXT _cycles(SB), $0 /* time stamp counter */ RDTSC MOVL vlong+0(FP), CX /* &vlong */ MOVL AX, 0(CX) /* lo */ MOVL DX, 4(CX) /* hi */ RET /* * stub for: * time stamp counter; low-order 32 bits of 64-bit cycle counter * Runs at fasthz/4 cycles per second (m->clkin>>3) */ TEXT lcycles(SB),1,$0 RDTSC RET TEXT rdmsr(SB), $0 /* model-specific register */ MOVL index+0(FP), CX RDMSR MOVL vlong+4(FP), CX /* &vlong */ MOVL AX, 0(CX) /* lo */ MOVL DX, 4(CX) /* hi */ RET TEXT wrmsr(SB), $0 MOVL index+0(FP), CX MOVL lo+4(FP), AX MOVL hi+8(FP), DX WRMSR RET /* * Try to determine the CPU type which requires fiddling with EFLAGS. * If the Id bit can be toggled then the CPUID instruction can be used * to determine CPU identity and features. First have to check if it's * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be * toggled then it's an older 486 of some kind. * * cpuid(fun, regs[4]); */ TEXT cpuid(SB), $0 MOVL $0x240000, AX PUSHL AX POPFL /* set Id|Ac */ PUSHFL POPL BX /* retrieve value */ MOVL $0, AX PUSHL AX POPFL /* clear Id|Ac, EFLAGS initialised */ PUSHFL POPL AX /* retrieve value */ XORL BX, AX TESTL $0x040000, AX /* Ac */ JZ _cpu386 /* can't set this bit on 386 */ TESTL $0x200000, AX /* Id */ JZ _cpu486 /* can't toggle this bit on some 486 */ MOVL fn+0(FP), AX CPUID JMP _cpuid _cpu486: MOVL $0x400, AX JMP _maybezapax _cpu386: MOVL $0x300, AX _maybezapax: CMPL fn+0(FP), $1 JE _zaprest XORL AX, AX _zaprest: XORL BX, BX XORL CX, CX XORL DX, DX _cpuid: MOVL regs+4(FP), BP MOVL AX, 0(BP) MOVL BX, 4(BP) MOVL CX, 8(BP) MOVL DX, 12(BP) RET /* * Basic timing loop to determine CPU frequency. */ TEXT aamloop(SB), $0 MOVL count+0(FP), CX _aamloop: AAM LOOP _aamloop RET /* * Floating point. * Note: the encodings for the FCLEX, FINIT, FSAVE, FSTCW, FSENV and FSTSW * instructions do NOT have the WAIT prefix byte (i.e. they act like their * FNxxx variations) so WAIT instructions must be explicitly placed in the * code as necessary. */ #define FPOFF(l) ;\ MOVL CR0, AX ;\ ANDL $0xC, AX /* EM, TS */ ;\ CMPL AX, $0x8 ;\ JEQ l ;\ WAIT ;\ l: ;\ MOVL CR0, AX ;\ ANDL $~0x4, AX /* EM=0 */ ;\ ORL $0x28, AX /* NE=1, TS=1 */ ;\ MOVL AX, CR0 #define FPON ;\ MOVL CR0, AX ;\ ANDL $~0xC, AX /* EM=0, TS=0 */ ;\ MOVL AX, CR0 TEXT fpoff(SB), $0 /* disable */ FPOFF(l1) RET TEXT fpinit(SB), $0 /* enable and init */ FPON FINIT WAIT /* setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL) */ /* note that low 6 bits are masks, not enables, on this chip */ PUSHW $0x0232 FLDCW 0(SP) POPW AX WAIT RET TEXT fpsave(SB), $0 /* save state and disable */ MOVL p+0(FP), AX FSAVE 0(AX) /* no WAIT */ FPOFF(l2) RET TEXT fprestore(SB), $0 /* enable and restore state */ FPON MOVL p+0(FP), AX FRSTOR 0(AX) WAIT RET TEXT fpstatus(SB), $0 /* get floating point status */ FSTSW AX RET TEXT fpenv(SB), $0 /* save state without waiting */ MOVL p+0(FP), AX FSTENV 0(AX) RET TEXT fpclear(SB), $0 /* clear pending exceptions */ FPON FCLEX /* no WAIT */ FPOFF(l3) RET /* */ TEXT splhi(SB), $0 shi: PUSHFL POPL AX TESTL $0x200, AX JZ alreadyhi MOVL $(MACHADDR+0x04), CX /* save PC in m->splpc */ MOVL (SP), BX MOVL BX, (CX) alreadyhi: CLI RET TEXT spllo(SB), $0 slo: PUSHFL POPL AX TESTL $0x200, AX JNZ alreadylo MOVL $(MACHADDR+0x04), CX /* clear m->splpc */ MOVL $0, (CX) alreadylo: STI RET TEXT splx(SB), $0 MOVL s+0(FP), AX TESTL $0x200, AX JNZ slo JMP shi TEXT spldone(SB), $0 RET TEXT islo(SB), $0 PUSHFL POPL AX ANDL $0x200, AX /* interrupt enable flag */ RET /* * Test-And-Set */ TEXT tas(SB), $0 MOVL $0xDEADDEAD, AX MOVL lock+0(FP), BX XCHGL AX, (BX) /* lock->key */ RET TEXT _xinc(SB), $0 /* void _xinc(long*); */ MOVL l+0(FP), AX LOCK; INCL 0(AX) RET TEXT _xdec(SB), $0 /* long _xdec(long*); */ MOVL l+0(FP), BX XORL AX, AX LOCK; DECL 0(BX) JLT _xdeclt JGT _xdecgt RET _xdecgt: INCL AX RET _xdeclt: DECL AX RET TEXT mb386(SB), $0 POPL AX /* return PC */ PUSHFL PUSHL CS PUSHL AX IRETL TEXT mb586(SB), $0 XORL AX, AX CPUID RET TEXT sfence(SB), $0 BYTE $0x0f BYTE $0xae BYTE $0xf8 RET TEXT lfence(SB), $0 BYTE $0x0f BYTE $0xae BYTE $0xe8 RET TEXT mfence(SB), $0 BYTE $0x0f BYTE $0xae BYTE $0xf0 RET TEXT xchgw(SB), $0 MOVL v+4(FP), AX MOVL p+0(FP), BX XCHGW AX, (BX) RET TEXT cmpswap486(SB), $0 MOVL addr+0(FP), BX MOVL old+4(FP), AX MOVL new+8(FP), CX LOCK BYTE $0x0F; BYTE $0xB1; BYTE $0x0B /* CMPXCHGL CX, (BX) */ JNZ didnt MOVL $1, AX RET didnt: XORL AX,AX RET TEXT mul64fract(SB), $0 /* * Multiply two 64-bit number s and keep the middle 64 bits from the 128-bit result * See ../port/tod.c for motivation. */ MOVL r+0(FP), CX XORL BX, BX /* BX = 0 */ MOVL a+8(FP), AX MULL b+16(FP) /* a1*b1 */ MOVL AX, 4(CX) /* r2 = lo(a1*b1) */ MOVL a+8(FP), AX MULL b+12(FP) /* a1*b0 */ MOVL AX, 0(CX) /* r1 = lo(a1*b0) */ ADDL DX, 4(CX) /* r2 += hi(a1*b0) */ MOVL a+4(FP), AX MULL b+16(FP) /* a0*b1 */ ADDL AX, 0(CX) /* r1 += lo(a0*b1) */ ADCL DX, 4(CX) /* r2 += hi(a0*b1) + carry */ MOVL a+4(FP), AX MULL b+12(FP) /* a0*b0 */ ADDL DX, 0(CX) /* r1 += hi(a0*b0) */ ADCL BX, 4(CX) /* r2 += carry */ RET /* * label consists of a stack pointer and a PC */ TEXT gotolabel(SB), $0 MOVL label+0(FP), AX MOVL 0(AX), SP /* restore sp */ MOVL 4(AX), AX /* put return pc on the stack */ MOVL AX, 0(SP) MOVL $1, AX /* return 1 */ RET TEXT setlabel(SB), $0 MOVL label+0(FP), AX MOVL SP, 0(AX) /* store sp */ MOVL 0(SP), BX /* store return pc */ MOVL BX, 4(AX) MOVL $0, AX /* return 0 */ RET /* * Attempt at power saving. -rsc */ TEXT halt(SB), $0 CLI CMPL nrdy(SB), $0 JEQ _nothingready STI RET _nothingready: STI HLT RET TEXT load_fs(SB), $0 MOVW fs+0(FP), AX MOVW AX, FS RET TEXT load_gs(SB), $0 MOVW gs+0(FP), AX MOVW AX, GS RET /* * Interrupt/exception handling. * Each entry in the vector table calls either _strayintr or _strayintrx depending * on whether an error code has been automatically pushed onto the stack * (_strayintrx) or not, in which case a dummy entry must be pushed before retrieving * the trap type from the vector table entry and placing it on the stack as part * of the Ureg structure. * The size of each entry in the vector table (6 bytes) is known in trapinit(). */ TEXT _strayintr(SB), $0 PUSHL AX /* save AX */ MOVL 4(SP), AX /* return PC from vectortable(SB) */ JMP intrcommon TEXT _strayintrx(SB), $0 XCHGL AX, (SP) /* swap AX with vectortable CALL PC */ intrcommon: PUSHL DS /* save DS */ PUSHL $(KDSEL) POPL DS /* fix up DS */ MOVBLZX (AX), AX /* trap type -> AX */ XCHGL AX, 4(SP) /* exchange trap type with saved AX */ PUSHL ES /* save ES */ PUSHL $(KDSEL) POPL ES /* fix up ES */ PUSHL FS /* save the rest of the Ureg struct */ PUSHL GS PUSHAL PUSHL SP /* Ureg* argument to trap */ CALL trap(SB) TEXT forkret(SB), $0 POPL AX POPAL POPL GS POPL FS POPL ES POPL DS ADDL $8, SP /* pop error code and trap type */ IRETL TEXT vectortable(SB), $0 CALL _strayintr(SB); BYTE $0x00 /* divide error */ CALL _strayintr(SB); BYTE $0x01 /* debug exception */ CALL _strayintr(SB); BYTE $0x02 /* NMI interrupt */ CALL _strayintr(SB); BYTE $0x03 /* breakpoint */ CALL _strayintr(SB); BYTE $0x04 /* overflow */ CALL _strayintr(SB); BYTE $0x05 /* bound */ CALL _strayintr(SB); BYTE $0x06 /* invalid opcode */ CALL _strayintr(SB); BYTE $0x07 /* no coprocessor available */ CALL _strayintrx(SB); BYTE $0x08 /* double fault */ CALL _strayintr(SB); BYTE $0x09 /* coprocessor segment overflow */ CALL _strayintrx(SB); BYTE $0x0A /* invalid TSS */ CALL _strayintrx(SB); BYTE $0x0B /* segment not available */ CALL _strayintrx(SB); BYTE $0x0C /* stack exception */ CALL _strayintrx(SB); BYTE $0x0D /* general protection error */ CALL _strayintrx(SB); BYTE $0x0E /* page fault */ CALL _strayintr(SB); BYTE $0x0F /* */ CALL _strayintr(SB); BYTE $0x10 /* coprocessor error */ CALL _strayintrx(SB); BYTE $0x11 /* alignment check */ CALL _strayintr(SB); BYTE $0x12 /* machine check */ CALL _strayintr(SB); BYTE $0x13 CALL _strayintr(SB); BYTE $0x14 CALL _strayintr(SB); BYTE $0x15 CALL _strayintr(SB); BYTE $0x16 CALL _strayintr(SB); BYTE $0x17 CALL _strayintr(SB); BYTE $0x18 CALL _strayintr(SB); BYTE $0x19 CALL _strayintr(SB); BYTE $0x1A CALL _strayintr(SB); BYTE $0x1B CALL _strayintr(SB); BYTE $0x1C CALL _strayintr(SB); BYTE $0x1D CALL _strayintr(SB); BYTE $0x1E CALL _strayintr(SB); BYTE $0x1F CALL _strayintr(SB); BYTE $0x20 /* VectorLAPIC */ CALL _strayintr(SB); BYTE $0x21 CALL _strayintr(SB); BYTE $0x22 CALL _strayintr(SB); BYTE $0x23 CALL _strayintr(SB); BYTE $0x24 CALL _strayintr(SB); BYTE $0x25 CALL _strayintr(SB); BYTE $0x26 CALL _strayintr(SB); BYTE $0x27 CALL _strayintr(SB); BYTE $0x28 CALL _strayintr(SB); BYTE $0x29 CALL _strayintr(SB); BYTE $0x2A CALL _strayintr(SB); BYTE $0x2B CALL _strayintr(SB); BYTE $0x2C CALL _strayintr(SB); BYTE $0x2D CALL _strayintr(SB); BYTE $0x2E CALL _strayintr(SB); BYTE $0x2F CALL _strayintr(SB); BYTE $0x30 CALL _strayintr(SB); BYTE $0x31 CALL _strayintr(SB); BYTE $0x32 CALL _strayintr(SB); BYTE $0x33 CALL _strayintr(SB); BYTE $0x34 CALL _strayintr(SB); BYTE $0x35 CALL _strayintr(SB); BYTE $0x36 CALL _strayintr(SB); BYTE $0x37 CALL _strayintr(SB); BYTE $0x38 CALL _strayintr(SB); BYTE $0x39 CALL _strayintr(SB); BYTE $0x3A CALL _strayintr(SB); BYTE $0x3B CALL _strayintr(SB); BYTE $0x3C CALL _strayintr(SB); BYTE $0x3D CALL _strayintr(SB); BYTE $0x3E CALL _strayintr(SB); BYTE $0x3F CALL _syscallintr(SB); BYTE $0x40 /* VectorSYSCALL */ CALL _strayintr(SB); BYTE $0x41 CALL _strayintr(SB); BYTE $0x42 CALL _strayintr(SB); BYTE $0x43 CALL _strayintr(SB); BYTE $0x44 CALL _strayintr(SB); BYTE $0x45 CALL _strayintr(SB); BYTE $0x46 CALL _strayintr(SB); BYTE $0x47 CALL _strayintr(SB); BYTE $0x48 CALL _strayintr(SB); BYTE $0x49 CALL _strayintr(SB); BYTE $0x4A CALL _strayintr(SB); BYTE $0x4B CALL _strayintr(SB); BYTE $0x4C CALL _strayintr(SB); BYTE $0x4D CALL _strayintr(SB); BYTE $0x4E CALL _strayintr(SB); BYTE $0x4F CALL _strayintr(SB); BYTE $0x50 CALL _strayintr(SB); BYTE $0x51 CALL _strayintr(SB); BYTE $0x52 CALL _strayintr(SB); BYTE $0x53 CALL _strayintr(SB); BYTE $0x54 CALL _strayintr(SB); BYTE $0x55 CALL _strayintr(SB); BYTE $0x56 CALL _strayintr(SB); BYTE $0x57 CALL _strayintr(SB); BYTE $0x58 CALL _strayintr(SB); BYTE $0x59 CALL _strayintr(SB); BYTE $0x5A CALL _strayintr(SB); BYTE $0x5B CALL _strayintr(SB); BYTE $0x5C CALL _strayintr(SB); BYTE $0x5D CALL _strayintr(SB); BYTE $0x5E CALL _strayintr(SB); BYTE $0x5F CALL _strayintr(SB); BYTE $0x60 CALL _strayintr(SB); BYTE $0x61 CALL _strayintr(SB); BYTE $0x62 CALL _strayintr(SB); BYTE $0x63 CALL _strayintr(SB); BYTE $0x64 CALL _strayintr(SB); BYTE $0x65 CALL _strayintr(SB); BYTE $0x66 CALL _strayintr(SB); BYTE $0x67 CALL _strayintr(SB); BYTE $0x68 CALL _strayintr(SB); BYTE $0x69 CALL _strayintr(SB); BYTE $0x6A CALL _strayintr(SB); BYTE $0x6B CALL _strayintr(SB); BYTE $0x6C CALL _strayintr(SB); BYTE $0x6D CALL _strayintr(SB); BYTE $0x6E CALL _strayintr(SB); BYTE $0x6F CALL _strayintr(SB); BYTE $0x70 CALL _strayintr(SB); BYTE $0x71 CALL _strayintr(SB); BYTE $0x72 CALL _strayintr(SB); BYTE $0x73 CALL _strayintr(SB); BYTE $0x74 CALL _strayintr(SB); BYTE $0x75 CALL _strayintr(SB); BYTE $0x76 CALL _strayintr(SB); BYTE $0x77 CALL _strayintr(SB); BYTE $0x78 CALL _strayintr(SB); BYTE $0x79 CALL _strayintr(SB); BYTE $0x7A CALL _strayintr(SB); BYTE $0x7B CALL _strayintr(SB); BYTE $0x7C CALL _strayintr(SB); BYTE $0x7D CALL _strayintr(SB); BYTE $0x7E CALL _strayintr(SB); BYTE $0x7F CALL _strayintr(SB); BYTE $0x80 /* Vector[A]PIC */ CALL _strayintr(SB); BYTE $0x81 CALL _strayintr(SB); BYTE $0x82 CALL _strayintr(SB); BYTE $0x83 CALL _strayintr(SB); BYTE $0x84 CALL _strayintr(SB); BYTE $0x85 CALL _strayintr(SB); BYTE $0x86 CALL _strayintr(SB); BYTE $0x87 CALL _strayintr(SB); BYTE $0x88 CALL _strayintr(SB); BYTE $0x89 CALL _strayintr(SB); BYTE $0x8A CALL _strayintr(SB); BYTE $0x8B CALL _strayintr(SB); BYTE $0x8C CALL _strayintr(SB); BYTE $0x8D CALL _strayintr(SB); BYTE $0x8E CALL _strayintr(SB); BYTE $0x8F CALL _strayintr(SB); BYTE $0x90 CALL _strayintr(SB); BYTE $0x91 CALL _strayintr(SB); BYTE $0x92 CALL _strayintr(SB); BYTE $0x93 CALL _strayintr(SB); BYTE $0x94 CALL _strayintr(SB); BYTE $0x95 CALL _strayintr(SB); BYTE $0x96 CALL _strayintr(SB); BYTE $0x97 CALL _strayintr(SB); BYTE $0x98 CALL _strayintr(SB); BYTE $0x99 CALL _strayintr(SB); BYTE $0x9A CALL _strayintr(SB); BYTE $0x9B CALL _strayintr(SB); BYTE $0x9C CALL _strayintr(SB); BYTE $0x9D CALL _strayintr(SB); BYTE $0x9E CALL _strayintr(SB); BYTE $0x9F CALL _strayintr(SB); BYTE $0xA0 CALL _strayintr(SB); BYTE $0xA1 CALL _strayintr(SB); BYTE $0xA2 CALL _strayintr(SB); BYTE $0xA3 CALL _strayintr(SB); BYTE $0xA4 CALL _strayintr(SB); BYTE $0xA5 CALL _strayintr(SB); BYTE $0xA6 CALL _strayintr(SB); BYTE $0xA7 CALL _strayintr(SB); BYTE $0xA8 CALL _strayintr(SB); BYTE $0xA9 CALL _strayintr(SB); BYTE $0xAA CALL _strayintr(SB); BYTE $0xAB CALL _strayintr(SB); BYTE $0xAC CALL _strayintr(SB); BYTE $0xAD CALL _strayintr(SB); BYTE $0xAE CALL _strayintr(SB); BYTE $0xAF CALL _strayintr(SB); BYTE $0xB0 CALL _strayintr(SB); BYTE $0xB1 CALL _strayintr(SB); BYTE $0xB2 CALL _strayintr(SB); BYTE $0xB3 CALL _strayintr(SB); BYTE $0xB4 CALL _strayintr(SB); BYTE $0xB5 CALL _strayintr(SB); BYTE $0xB6 CALL _strayintr(SB); BYTE $0xB7 CALL _strayintr(SB); BYTE $0xB8 CALL _strayintr(SB); BYTE $0xB9 CALL _strayintr(SB); BYTE $0xBA CALL _strayintr(SB); BYTE $0xBB CALL _strayintr(SB); BYTE $0xBC CALL _strayintr(SB); BYTE $0xBD CALL _strayintr(SB); BYTE $0xBE CALL _strayintr(SB); BYTE $0xBF CALL _strayintr(SB); BYTE $0xC0 CALL _strayintr(SB); BYTE $0xC1 CALL _strayintr(SB); BYTE $0xC2 CALL _strayintr(SB); BYTE $0xC3 CALL _strayintr(SB); BYTE $0xC4 CALL _strayintr(SB); BYTE $0xC5 CALL _strayintr(SB); BYTE $0xC6 CALL _strayintr(SB); BYTE $0xC7 CALL _strayintr(SB); BYTE $0xC8 CALL _strayintr(SB); BYTE $0xC9 CALL _strayintr(SB); BYTE $0xCA CALL _strayintr(SB); BYTE $0xCB CALL _strayintr(SB); BYTE $0xCC CALL _strayintr(SB); BYTE $0xCD CALL _strayintr(SB); BYTE $0xCE CALL _strayintr(SB); BYTE $0xCF CALL _strayintr(SB); BYTE $0xD0 CALL _strayintr(SB); BYTE $0xD1 CALL _strayintr(SB); BYTE $0xD2 CALL _strayintr(SB); BYTE $0xD3 CALL _strayintr(SB); BYTE $0xD4 CALL _strayintr(SB); BYTE $0xD5 CALL _strayintr(SB); BYTE $0xD6 CALL _strayintr(SB); BYTE $0xD7 CALL _strayintr(SB); BYTE $0xD8 CALL _strayintr(SB); BYTE $0xD9 CALL _strayintr(SB); BYTE $0xDA CALL _strayintr(SB); BYTE $0xDB CALL _strayintr(SB); BYTE $0xDC CALL _strayintr(SB); BYTE $0xDD CALL _strayintr(SB); BYTE $0xDE CALL _strayintr(SB); BYTE $0xDF CALL _strayintr(SB); BYTE $0xE0 CALL _strayintr(SB); BYTE $0xE1 CALL _strayintr(SB); BYTE $0xE2 CALL _strayintr(SB); BYTE $0xE3 CALL _strayintr(SB); BYTE $0xE4 CALL _strayintr(SB); BYTE $0xE5 CALL _strayintr(SB); BYTE $0xE6 CALL _strayintr(SB); BYTE $0xE7 CALL _strayintr(SB); BYTE $0xE8 CALL _strayintr(SB); BYTE $0xE9 CALL _strayintr(SB); BYTE $0xEA CALL _strayintr(SB); BYTE $0xEB CALL _strayintr(SB); BYTE $0xEC CALL _strayintr(SB); BYTE $0xED CALL _strayintr(SB); BYTE $0xEE CALL _strayintr(SB); BYTE $0xEF CALL _strayintr(SB); BYTE $0xF0 CALL _strayintr(SB); BYTE $0xF1 CALL _strayintr(SB); BYTE $0xF2 CALL _strayintr(SB); BYTE $0xF3 CALL _strayintr(SB); BYTE $0xF4 CALL _strayintr(SB); BYTE $0xF5 CALL _strayintr(SB); BYTE $0xF6 CALL _strayintr(SB); BYTE $0xF7 CALL _strayintr(SB); BYTE $0xF8 CALL _strayintr(SB); BYTE $0xF9 CALL _strayintr(SB); BYTE $0xFA CALL _strayintr(SB); BYTE $0xFB CALL _strayintr(SB); BYTE $0xFC CALL _strayintr(SB); BYTE $0xFD CALL _strayintr(SB); BYTE $0xFE CALL _strayintr(SB); BYTE $0xFF fs(SB), $0 MOVW fs+0(FP), AX MOVW AX, FS RET TEXT load_gs(SB), $0 MOVW gs+0(FP), AX MOVW AX, GS RET /* * Interrupt/exception handling. * Each entry in the vector table calls either _strayintr or _strasegdescpatch/9/pc/segdesc.c 664 0 0 12360 11342263221 17202ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" /* * flags: * P = present * A = accessed (for code/data) * E = expand down (for data) * W = writable (for data) * R = readable (for code) * C = conforming (for code) * G = limit granularity in pages (for code/data) * D = 32 bit operand size (for code) * B = 32 bit stack pointer (for data) * Y = busy (for tss and tss16) * U = available for use by system software */ static struct { char *name; char *flags; } descrtypes[] = { "data", "--------AWE01--P----U.BG--------", "code", "--------ARC11--P----U.DG--------", "tss16", "--------1Y000--P----U..G--------", "ldt", "--------01000--P----U..G--------", "callg16", "--------00100--P----U..G--------", "taskg", "--------10100--P----U..G--------", "intrg16", "--------01100--P----U..G--------", "trapg16", "--------11100--P----U..G--------", "tss", "--------1Y010--P----U..G--------", "callg", "--------00110--P----U..G--------", "intrg", "--------01110--P----U..G--------", "trapg", "--------11110--P----U..G--------", }; /* * format: * idx[4] type[8] flags[8] dpl[1] base[8] limit[5]\n */ enum { RECLEN = 4+1 + 8+1 + 8+1 + 1+1 + 8+1 + 5+1, }; static long descwrite(Proc *proc, int local, void *v, long n, vlong) { int i, j, t; char buf[RECLEN+1]; char c, *p, *s, *e, *f[6]; Segdesc d; int dpl; ulong base; ulong limit; s = (char*)v; e = s + n; if(waserror()){ if(proc == up) flushmmu(); nexterror(); } while(s < e){ for(p = s; p < e && *p != '\n'; p++); ; if((p - s) > RECLEN) error(Ebadarg); memmove(buf, s, p - s); buf[p-s] = 0; s = p+1; if(getfields(buf, f, nelem(f), 1, " ") != nelem(f)) error(Ebadarg); i = strtoul(f[0], nil, 16); for(t=0; t>16); for(j=0; c = descrtypes[t].flags[j]; j++){ switch(c){ default: if(strchr(f[2], c) == nil){ case '0': case '.': d.d1 &= ~(1<= 8192) error(Ebadarg); if(i >= (c = ((old = proc->ldt) ? proc->nldt : 0))){ if((new = malloc(sizeof(Segdesc) * (i+1))) == nil) error(Enomem); if(c > 0) memmove(new, old, sizeof(Segdesc) * c); memset(new + c, 0, sizeof(Segdesc) * ((i+1) - c)); proc->ldt = new; proc->nldt = i+1; free(old); } proc->ldt[i] = d; } else { if(i < PROCSEG0 || i >= PROCSEG0 + NPROCSEG) error(Ebadarg); proc->gdt[i - PROCSEG0] = d; } } poperror(); if(proc == up) flushmmu(); return n; } static long descread(Proc *proc, int local, void *v, long n, vlong o) { int i, j, k, t; char *s; int dpl; ulong base; ulong limit; s = v; for(i = 0;;i++){ Segdesc d; if(local){ if(proc->ldt == nil || i >= proc->nldt) break; d = proc->ldt[i]; } else { if(i < PROCSEG0) i = PROCSEG0; if(i >= PROCSEG0 + NPROCSEG) break; d = proc->gdt[i - PROCSEG0]; } if(o >= RECLEN){ o -= RECLEN; continue; } if(s + RECLEN+1 >= (char*)v + n) break; for(t=0; t>13; base = ((d.d0 & 0xFFFF0000)>>16) | ((d.d1 & 0xFF)<<16) | (d.d1 & 0xFF000000); limit = (d.d1 & 0xF0000) | (d.d0 & 0xFFFF); s += sprint(s, "%.1d ", dpl); s += sprint(s, "%.8lux ", base); s += sprint(s, "%.5lux\n", limit); } return s-(char*)v; } static long gdtread(Chan*, void *v, long n, vlong o) { return descread(up, 0, v, n, o); } static long gdtwrite(Chan*, void *v, long n, vlong o) { return descwrite(up, 0, v, n, o); } static long ldtread(Chan*, void *v, long n, vlong o) { return descread(up, 1, v, n, o); } static long ldtwrite(Chan*, void *v, long n, vlong o) { return descwrite(up, 1, v, n, o); } /* * devproc hook * extern long (*psegdescread)(Proc*, int, void*, long, vlong); * extern long (*psegdescwrite)(Proc*, int, void*, long, vlong); */ void segdesclink(void) { /* * devproc hook * psegdescread = descread; * psegdescwrite = descwrite; */ addarchfile("gdt", 0666, gdtread, gdtwrite); addarchfile("ldt", 0666, ldtread, ldtwrite); } segdescpatch/9/pc/main.c 664 0 0 36173 11342263221 16521ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "init.h" #include "pool.h" #include "reboot.h" Mach *m; /* * Where configuration info is left for the loaded programme. * This will turn into a structure as more is done by the boot loader * (e.g. why parse the .ini file twice?). * There are 3584 bytes available at CONFADDR. */ #define BOOTLINE ((char*)CONFADDR) #define BOOTLINELEN 64 #define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) #define BOOTARGSLEN (4096-0x200-BOOTLINELEN) #define MAXCONF 64 char bootdisk[KNAMELEN]; Conf conf; char *confname[MAXCONF]; char *confval[MAXCONF]; int nconf; uchar *sp; /* user stack of init proc */ int delaylink; static void options(void) { long i, n; char *cp, *line[MAXCONF], *p, *q; /* * parse configuration args from dos file plan9.ini */ cp = BOOTARGS; /* where b.com leaves its config */ cp[BOOTARGSLEN-1] = 0; /* * Strip out '\r', change '\t' -> ' '. */ p = cp; for(q = cp; *q; q++){ if(*q == '\r') continue; if(*q == '\t') *q = ' '; *p++ = *q; } *p = 0; n = getfields(cp, line, MAXCONF, 1, "\n"); for(i = 0; i < n; i++){ if(*line[i] == '#') continue; cp = strchr(line[i], '='); if(cp == nil) continue; *cp++ = '\0'; confname[nconf] = line[i]; confval[nconf] = cp; nconf++; } } extern void mmuinit0(void); extern void (*i8237alloc)(void); void main(void) { mach0init(); options(); ioinit(); i8250console(); quotefmtinstall(); screeninit(); print("\nPlan 9\n"); trapinit0(); mmuinit0(); kbdinit(); i8253init(); cpuidentify(); meminit(); confinit(); archinit(); xinit(); if(i8237alloc != nil) i8237alloc(); trapinit(); printinit(); cpuidprint(); mmuinit(); if(arch->intrinit) /* launches other processors on an mp */ arch->intrinit(); timersinit(); mathinit(); kbdenable(); if(arch->clockenable) arch->clockenable(); procinit0(); initseg(); if(delaylink){ bootlinks(); pcimatch(0, 0, 0); }else links(); conf.monitor = 1; chandevreset(); pageinit(); i8253link(); swapinit(); userinit(); active.thunderbirdsarego = 1; schedinit(); } void mach0init(void) { conf.nmach = 1; MACHP(0) = (Mach*)CPU0MACH; m->pdb = (ulong*)CPU0PDB; m->gdt = (Segdesc*)CPU0GDT; machinit(); active.machs = 1; active.exiting = 0; } void machinit(void) { int machno; ulong *pdb; Segdesc *gdt; machno = m->machno; pdb = m->pdb; gdt = m->gdt; memset(m, 0, sizeof(Mach)); m->machno = machno; m->pdb = pdb; m->gdt = gdt; m->perf.period = 1; /* * For polled uart output at boot, need * a default delay constant. 100000 should * be enough for a while. Cpuidentify will * calculate the real value later. */ m->loopconst = 100000; } void init0(void) { int i; char buf[2*KNAMELEN]; up->nerrlab = 0; spllo(); /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ up->slash = namec("#/", Atodir, 0, 0); pathclose(up->slash->path); up->slash->path = newpath("/"); up->dot = cclone(up->slash); chandevinit(); if(!waserror()){ snprint(buf, sizeof(buf), "%s %s", arch->id, conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "386", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); for(i = 0; i < nconf; i++){ if(confname[i][0] != '*') ksetenv(confname[i], confval[i], 0); ksetenv(confname[i], confval[i], 1); } poperror(); } kproc("alarm", alarmkproc, 0); touser(sp); } void userinit(void) { void *v; Proc *p; Segment *s; Page *pg; p = newproc(); p->pgrp = newpgrp(); p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; p->fgrp = dupfgrp(nil); p->rgrp = newrgrp(); p->procmode = 0640; kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); p->fpstate = FPinit; fpoff(); /* * Kernel Stack * * N.B. make sure there's enough space for syscall to check * for valid args and * 4 bytes for gotolabel's return PC */ p->sched.pc = (ulong)init0; p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); /* * User Stack * * N.B. cannot call newpage() with clear=1, because pc kmap * requires up != nil. use tmpmap instead. */ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); p->seg[SSEG] = s; pg = newpage(0, 0, USTKTOP-BY2PG); v = tmpmap(pg); memset(v, 0, BY2PG); segpage(s, pg); bootargs(v); tmpunmap(v); /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); s->flushme++; p->seg[TSEG] = s; pg = newpage(0, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); v = tmpmap(pg); memset(v, 0, BY2PG); memmove(v, initcode, sizeof initcode); tmpunmap(v); ready(p); } uchar * pusharg(char *p) { int n; n = strlen(p)+1; sp -= n; memmove(sp, p, n); return sp; } void bootargs(void *base) { int i, ac; uchar *av[32]; uchar **lsp; char *cp = BOOTLINE; char buf[64]; sp = (uchar*)base + BY2PG - MAXSYSARG*BY2WD; ac = 0; av[ac++] = pusharg("/386/9dos"); /* when boot is changed to only use rc, this code can go away */ cp[BOOTLINELEN-1] = 0; buf[0] = 0; if(strncmp(cp, "fd", 2) == 0){ sprint(buf, "local!#f/fd%lddisk", strtol(cp+2, 0, 0)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "sd", 2) == 0){ sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "ether", 5) == 0) av[ac++] = pusharg("-n"); /* 4 byte word align stack */ sp = (uchar*)((ulong)sp & ~3); /* build argc, argv on stack */ sp -= (ac+1)*sizeof(sp); lsp = (uchar**)sp; for(i = 0; i < ac; i++) *lsp++ = av[i] + ((USTKTOP - BY2PG) - (ulong)base); *lsp = 0; sp += (USTKTOP - BY2PG) - (ulong)base - sizeof(ulong); } char* getconf(char *name) { int i; for(i = 0; i < nconf; i++) if(cistrcmp(confname[i], name) == 0) return confval[i]; return 0; } static void writeconf(void) { char *p, *q; int n; p = getconfenv(); if(waserror()) { free(p); nexterror(); } /* convert to name=value\n format */ for(q=p; *q; q++) { q += strlen(q); *q = '='; q += strlen(q); *q = '\n'; } n = q - p + 1; if(n >= BOOTARGSLEN) error("kernel configuration too large"); memset(BOOTLINE, 0, BOOTLINELEN); memmove(BOOTARGS, p, n); poperror(); free(p); } void confinit(void) { char *p; int i, userpcnt; ulong kpages; if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; conf.npage = 0; for(i=0; i 2000) conf.nproc = 2000; conf.nimage = 200; conf.nswap = conf.nproc*80; conf.nswppo = 4096; if(cpuserver) { if(userpcnt < 10) userpcnt = 70; kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Hack for the big boys. Only good while physmem < 4GB. * Give the kernel fixed max + enough to allocate the * page pool. * This is an overestimate as conf.upages < conf.npages. * The patch of nimage is a band-aid, scanning the whole * page list in imagereclaim just takes too long. */ if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){ kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG; conf.nimage = 2000; kpages += (conf.nproc*KSTACK)/BY2PG; } } else { if(userpcnt < 10) { if(conf.npage*BY2PG < 16*MB) userpcnt = 40; else userpcnt = 60; } kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ if(conf.npage*BY2PG < 16*MB) imagmem->minarena = 4*1024*1024; } /* * can't go past the end of virtual memory * (ulong)-KZERO is 2^32 - KZERO */ if(kpages > ((ulong)-KZERO)/BY2PG) kpages = ((ulong)-KZERO)/BY2PG; conf.upages = conf.npage - kpages; conf.ialloc = (kpages/2)*BY2PG; /* * Guess how much is taken by the large permanent * datastructures. Mntcache and Mntrpc are not accounted for * (probably ~300KB). */ kpages *= BY2PG; kpages -= conf.upages*sizeof(Page) + conf.nproc*sizeof(Proc) + conf.nimage*sizeof(Image) + conf.nswap + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; imagmem->maxsize = kpages; } static char* mathmsg[] = { nil, /* handled below */ "denormalized operand", "division by zero", "numeric overflow", "numeric underflow", "precision loss", }; static void mathnote(void) { int i; ulong status; char *msg, note[ERRMAX]; status = up->fpsave.status; /* * Some attention should probably be paid here to the * exception masks and error summary. */ msg = "unknown exception"; for(i = 1; i <= 5; i++){ if(!((1<fpsave.pc, status); postnote(up, 1, note, NDebug); } /* * math coprocessor error */ static void matherror(Ureg *ur, void*) { /* * a write cycle to port 0xF0 clears the interrupt latch attached * to the error# line from the 387 */ if(!(m->cpuiddx & 0x01)) outb(0xF0, 0xFF); /* * save floating point state to check out error */ fpenv(&up->fpsave); mathnote(); if((ur->pc & 0xf0000000) == KZERO) panic("fp: status %ux fppc=0x%lux pc=0x%lux", up->fpsave.status, up->fpsave.pc, ur->pc); } /* * math coprocessor emulation fault */ static void mathemu(Ureg *ureg, void*) { if(up->fpstate & FPillegal){ /* someone did floating point in a note handler */ postnote(up, 1, "sys: floating point in note handler", NDebug); return; } switch(up->fpstate){ case FPinit: fpinit(); up->fpstate = FPactive; break; case FPinactive: /* * Before restoring the state, check for any pending * exceptions, there's no way to restore the state without * generating an unmasked exception. * More attention should probably be paid here to the * exception masks and error summary. */ if((up->fpsave.status & ~up->fpsave.control) & 0x07F){ mathnote(); break; } fprestore(&up->fpsave); up->fpstate = FPactive; break; case FPactive: panic("math emu pid %ld %s pc 0x%lux", up->pid, up->text, ureg->pc); break; } } /* * math coprocessor segment overrun */ static void mathover(Ureg*, void*) { pexit("math overrun", 0); } void mathinit(void) { trapenable(VectorCERR, matherror, 0, "matherror"); if(X86FAMILY(m->cpuidax) == 3) intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror"); trapenable(VectorCNA, mathemu, 0, "mathemu"); trapenable(VectorCSO, mathover, 0, "mathover"); } void procsetup(Proc*p) { p->fpstate = FPinit; fpoff(); memset(p->gdt, 0, sizeof(p->gdt)); p->ldt = nil; p->nldt = 0; } void procrestore(Proc *p) { uvlong t; if(p->kp) return; cycles(&t); p->pcycles -= t; } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { uvlong t; cycles(&t); p->pcycles += t; if(p->fpstate == FPactive){ if(p->state == Moribund) fpclear(); else{ /* * Fpsave() stores without handling pending * unmasked exeptions. Postnote() can't be called * here as sleep() already has up->rlock, so * the handling of pending exceptions is delayed * until the process runs again and generates an * emulation fault to activate the FPU. */ fpsave(&p->fpsave); } p->fpstate = FPinactive; } /* * While this processor is in the scheduler, the process could run * on another processor and exit, returning the page tables to * the free list where they could be reallocated and overwritten. * When this processor eventually has to get an entry from the * trashed page tables it will crash. * * If there's only one processor, this can't happen. * You might think it would be a win not to do this in that case, * especially on VMware, but it turns out not to matter. */ mmuflushtlb(PADDR(m->pdb)); } void procfork(Proc *p) { /* inherit user descriptors */ memmove(p->gdt, up->gdt, sizeof(p->gdt)); /* copy local descriptor table */ if(up->ldt != nil && up->nldt > 0){ p->ldt = malloc(sizeof(Segdesc) * up->nldt); memmove(p->ldt, up->ldt, sizeof(Segdesc) * up->nldt); p->nldt = up->nldt; } } static void shutdown(int ispanic) { int ms, once; lock(&active); if(ispanic) active.ispanic = ispanic; else if(m->machno == 0 && (active.machs & (1<machno)) == 0) active.ispanic = 0; once = active.machs & (1<machno); active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); if(once) iprint("cpu%d: exiting\n", m->machno); spllo(); for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); if(active.machs == 0 && consactive() == 0) break; } if(getconf("*debug")) delay(5*60*1000); if(active.ispanic){ if(!cpuserver) for(;;) halt(); delay(10000); }else delay(1000); } void reboot(void *entry, void *code, ulong size) { void (*f)(ulong, ulong, ulong); ulong *pdb; writeconf(); shutdown(0); /* * should be the only processor running now */ print("shutting down...\n"); delay(200); splhi(); /* turn off buffered serial console */ serialoq = nil; /* shutdown devices */ chandevshutdown(); /* * Modify the machine page table to directly map the low 4MB of memory * This allows the reboot code to turn off the page mapping */ pdb = m->pdb; pdb[PDX(0)] = pdb[PDX(KZERO)]; mmuflushtlb(PADDR(pdb)); /* setup reboot trampoline function */ f = (void*)REBOOTADDR; memmove(f, rebootcode, sizeof(rebootcode)); print("rebooting...\n"); /* off we go - never to return */ coherence(); (*f)(PADDR(entry), PADDR(code), size); } void exit(int ispanic) { shutdown(ispanic); arch->reset(); } int isaconfig(char *class, int ctlrno, ISAConf *isa) { char cc[32], *p; int i; snprint(cc, sizeof cc, "%s%d", class, ctlrno); p = getconf(cc); if(p == nil) return 0; isa->type = ""; isa->nopt = tokenize(p, isa->opt, NISAOPT); for(i = 0; i < isa->nopt; i++){ p = isa->opt[i]; if(cistrncmp(p, "type=", 5) == 0) isa->type = p + 5; else if(cistrncmp(p, "port=", 5) == 0) isa->port = strtoul(p+5, &p, 0); else if(cistrncmp(p, "irq=", 4) == 0) isa->irq = strtoul(p+4, &p, 0); else if(cistrncmp(p, "dma=", 4) == 0) isa->dma = strtoul(p+4, &p, 0); else if(cistrncmp(p, "mem=", 4) == 0) isa->mem = strtoul(p+4, &p, 0); else if(cistrncmp(p, "size=", 5) == 0) isa->size = strtoul(p+5, &p, 0); else if(cistrncmp(p, "freq=", 5) == 0) isa->freq = strtoul(p+5, &p, 0); } return 1; } int cistrcmp(char *a, char *b) { int ac, bc; for(;;){ ac = *a++; bc = *b++; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } int cistrncmp(char *a, char *b, int n) { unsigned ac, bc; while(n > 0){ ac = *a++; bc = *b++; n--; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } /* * put the processor in the halt state if we've no processes to run. * an interrupt will get us going again. */ void idlehands(void) { if(conf.nmach == 1) halt(); } l(cp+2, 0, 0)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "sd", 2) == 0){ sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "ether", 5) == 0) av[ac++] = pusharg("-n"); /* 4 byte word align stack */ sp = (uchar*)((ulong)sp & ~3); /* build argc, argv on stack */ sp -= (ac+1)*sizeof(sp); lsp = (uchar**)sp; for(i = 0; segdescpatch/9/pc/io.h 664 0 0 22631 11343343052 16205ustar00cinap_lenrekcinap_lenrek#define X86STEPPING(x) ((x) & 0x0F) /* incorporate extended-model bits */ #define X86MODEL(x) ((((x)>>4) & 0x0F) | (((x)>>16) & 0x0F)<<4) #define X86FAMILY(x) (((x)>>8) & 0x0F) enum { VectorNMI = 2, /* non-maskable interrupt */ VectorBPT = 3, /* breakpoint */ VectorUD = 6, /* invalid opcode exception */ VectorCNA = 7, /* coprocessor not available */ Vector2F = 8, /* double fault */ VectorCSO = 9, /* coprocessor segment overrun */ VectorSNP = 11, /* segment not present */ VectorGPF = 13, /* general protection fault */ VectorPF = 14, /* page fault */ Vector15 = 15, /* reserved */ VectorCERR = 16, /* coprocessor error */ VectorPIC = 32, /* external i8259 interrupts */ IrqCLOCK = 0, IrqKBD = 1, IrqUART1 = 3, IrqUART0 = 4, IrqPCMCIA = 5, IrqFLOPPY = 6, IrqLPT = 7, IrqIRQ7 = 7, IrqAUX = 12, /* PS/2 port */ IrqIRQ13 = 13, /* coprocessor on 386 */ IrqATA0 = 14, IrqATA1 = 15, MaxIrqPIC = 15, VectorLAPIC = VectorPIC+16, /* local APIC interrupts */ IrqLINT0 = 16, /* LINT[01] must be offsets 0 and 1 */ IrqLINT1 = 17, IrqTIMER = 18, IrqERROR = 19, IrqPCINT = 20, IrqSPURIOUS = 31, /* must have bits [3-0] == 0x0F */ MaxIrqLAPIC = 31, VectorSYSCALL = 64, VectorAPIC = 65, /* external APIC interrupts */ MaxVectorAPIC = 255, }; typedef struct Vctl { Vctl* next; /* handlers on this vector */ char name[KNAMELEN]; /* of driver */ int isintr; /* interrupt or fault/trap */ int irq; int tbdf; int (*isr)(int); /* get isr bit for this irq */ int (*eoi)(int); /* eoi */ void (*f)(Ureg*, void*); /* handler to call */ void* a; /* argument to call it with */ } Vctl; enum { BusCBUS = 0, /* Corollary CBUS */ BusCBUSII, /* Corollary CBUS II */ BusEISA, /* Extended ISA */ BusFUTURE, /* IEEE Futurebus */ BusINTERN, /* Internal bus */ BusISA, /* Industry Standard Architecture */ BusMBI, /* Multibus I */ BusMBII, /* Multibus II */ BusMCA, /* Micro Channel Architecture */ BusMPI, /* MPI */ BusMPSA, /* MPSA */ BusNUBUS, /* Apple Macintosh NuBus */ BusPCI, /* Peripheral Component Interconnect */ BusPCMCIA, /* PC Memory Card International Association */ BusTC, /* DEC TurboChannel */ BusVL, /* VESA Local bus */ BusVME, /* VMEbus */ BusXPRESS, /* Express System Bus */ }; #define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) #define BUSFNO(tbdf) (((tbdf)>>8)&0x07) #define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) #define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) #define BUSTYPE(tbdf) ((tbdf)>>24) #define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) #define BUSUNKNOWN (-1) enum { MaxEISA = 16, CfgEISA = 0xC80, }; /* * PCI support code. */ enum { /* type 0 & type 1 pre-defined header */ PciVID = 0x00, /* vendor ID */ PciDID = 0x02, /* device ID */ PciPCR = 0x04, /* command */ PciPSR = 0x06, /* status */ PciRID = 0x08, /* revision ID */ PciCCRp = 0x09, /* programming interface class code */ PciCCRu = 0x0A, /* sub-class code */ PciCCRb = 0x0B, /* base class code */ PciCLS = 0x0C, /* cache line size */ PciLTR = 0x0D, /* latency timer */ PciHDT = 0x0E, /* header type */ PciBST = 0x0F, /* BIST */ PciBAR0 = 0x10, /* base address */ PciBAR1 = 0x14, PciINTL = 0x3C, /* interrupt line */ PciINTP = 0x3D, /* interrupt pin */ }; /* ccrb (base class code) values; controller types */ enum { Pcibcpci1 = 0, /* pci 1.0; no class codes defined */ Pcibcstore = 1, /* mass storage */ Pcibcnet = 2, /* network */ Pcibcdisp = 3, /* display */ Pcibcmmedia = 4, /* multimedia */ Pcibcmem = 5, /* memory */ Pcibcbridge = 6, /* bridge */ Pcibccomm = 7, /* simple comms (e.g., serial) */ Pcibcbasesys = 8, /* base system */ Pcibcinput = 9, /* input */ Pcibcdock = 0xa, /* docking stations */ Pcibcproc = 0xb, /* processors */ Pcibcserial = 0xc, /* serial bus (e.g., USB) */ Pcibcwireless = 0xd, /* wireless */ Pcibcintell = 0xe, /* intelligent i/o */ Pcibcsatcom = 0xf, /* satellite comms */ Pcibccrypto = 0x10, /* encryption/decryption */ Pcibcdacq = 0x11, /* data acquisition & signal proc. */ }; /* ccru (sub-class code) values; common cases only */ enum { /* mass storage */ Pciscscsi = 0, /* SCSI */ Pciscide = 1, /* IDE (ATA) */ /* network */ Pciscether = 0, /* Ethernet */ /* display */ Pciscvga = 0, /* VGA */ Pciscxga = 1, /* XGA */ Pcisc3d = 2, /* 3D */ /* bridges */ Pcischostpci = 0, /* host/pci */ Pciscpcicpci = 1, /* pci/pci */ /* simple comms */ Pciscserial = 0, /* 16450, etc. */ Pciscmultiser = 1, /* multiport serial */ /* serial bus */ Pciscusb = 3, /* USB */ }; enum { /* type 0 pre-defined header */ PciCIS = 0x28, /* cardbus CIS pointer */ PciSVID = 0x2C, /* subsystem vendor ID */ PciSID = 0x2E, /* cardbus CIS pointer */ PciEBAR0 = 0x30, /* expansion ROM base address */ PciMGNT = 0x3E, /* burst period length */ PciMLT = 0x3F, /* maximum latency between bursts */ }; enum { /* type 1 pre-defined header */ PciPBN = 0x18, /* primary bus number */ PciSBN = 0x19, /* secondary bus number */ PciUBN = 0x1A, /* subordinate bus number */ PciSLTR = 0x1B, /* secondary latency timer */ PciIBR = 0x1C, /* I/O base */ PciILR = 0x1D, /* I/O limit */ PciSPSR = 0x1E, /* secondary status */ PciMBR = 0x20, /* memory base */ PciMLR = 0x22, /* memory limit */ PciPMBR = 0x24, /* prefetchable memory base */ PciPMLR = 0x26, /* prefetchable memory limit */ PciPUBR = 0x28, /* prefetchable base upper 32 bits */ PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ PciIUBR = 0x30, /* I/O base upper 16 bits */ PciIULR = 0x32, /* I/O limit upper 16 bits */ PciEBAR1 = 0x28, /* expansion ROM base address */ PciBCR = 0x3E, /* bridge control register */ }; enum { /* type 2 pre-defined header */ PciCBExCA = 0x10, PciCBSPSR = 0x16, PciCBPBN = 0x18, /* primary bus number */ PciCBSBN = 0x19, /* secondary bus number */ PciCBUBN = 0x1A, /* subordinate bus number */ PciCBSLTR = 0x1B, /* secondary latency timer */ PciCBMBR0 = 0x1C, PciCBMLR0 = 0x20, PciCBMBR1 = 0x24, PciCBMLR1 = 0x28, PciCBIBR0 = 0x2C, /* I/O base */ PciCBILR0 = 0x30, /* I/O limit */ PciCBIBR1 = 0x34, /* I/O base */ PciCBILR1 = 0x38, /* I/O limit */ PciCBSVID = 0x40, /* subsystem vendor ID */ PciCBSID = 0x42, /* subsystem ID */ PciCBLMBAR = 0x44, /* legacy mode base address */ }; typedef struct Pcisiz Pcisiz; struct Pcisiz { Pcidev* dev; int siz; int bar; }; typedef struct Pcidev Pcidev; struct Pcidev { int tbdf; /* type+bus+device+function */ ushort vid; /* vendor ID */ ushort did; /* device ID */ ushort pcr; uchar rid; uchar ccrp; uchar ccru; uchar ccrb; uchar cls; uchar ltr; struct { ulong bar; /* base address */ int size; } mem[6]; struct { ulong bar; int size; } rom; uchar intl; /* interrupt line */ Pcidev* list; Pcidev* link; /* next device on this bno */ Pcidev* bridge; /* down a bus */ struct { ulong bar; int size; } ioa, mema; int pmrb; /* power management register block */ }; #define PCIWINDOW 0 #define PCIWADDR(va) (PADDR(va)+PCIWINDOW) #define ISAWINDOW 0 #define ISAWADDR(va) (PADDR(va)+ISAWINDOW) /* SMBus transactions */ enum { SMBquick, /* sends address only */ /* write */ SMBsend, /* sends address and cmd */ SMBbytewrite, /* sends address and cmd and 1 byte */ SMBwordwrite, /* sends address and cmd and 2 bytes */ /* read */ SMBrecv, /* sends address, recvs 1 byte */ SMBbyteread, /* sends address and cmd, recv's byte */ SMBwordread, /* sends address and cmd, recv's 2 bytes */ }; typedef struct SMBus SMBus; struct SMBus { QLock; /* mutex */ Rendez r; /* rendezvous point for completion interrupts */ void *arg; /* implementation dependent */ ulong base; /* port or memory base of smbus */ int busy; void (*transact)(SMBus*, int, int, int, uchar*); }; /* * PCMCIA support code. */ typedef struct PCMslot PCMslot; typedef struct PCMconftab PCMconftab; /* * Map between ISA memory space and PCMCIA card memory space. */ struct PCMmap { ulong ca; /* card address */ ulong cea; /* card end address */ ulong isa; /* ISA address */ int len; /* length of the ISA area */ int attr; /* attribute memory */ int ref; }; /* configuration table entry */ struct PCMconftab { int index; ushort irqs; /* legal irqs */ uchar irqtype; uchar bit16; /* true for 16 bit access */ struct { ulong start; ulong len; } io[16]; int nio; uchar vpp1; uchar vpp2; uchar memwait; ulong maxwait; ulong readywait; ulong otherwait; }; /* a card slot */ struct PCMslot { Lock; int ref; void *cp; /* controller for this slot */ long memlen; /* memory length */ uchar base; /* index register base */ uchar slotno; /* slot number */ /* status */ uchar special; /* in use for a special device */ uchar already; /* already inited */ uchar occupied; uchar battery; uchar wrprot; uchar powered; uchar configed; uchar enabled; uchar busy; /* cis info */ ulong msec; /* time of last slotinfo call */ char verstr[512]; /* version string */ int ncfg; /* number of configurations */ struct { ushort cpresent; /* config registers present */ ulong caddr; /* relative address of config registers */ } cfg[8]; int nctab; /* number of config table entries */ PCMconftab ctab[8]; PCMconftab *def; /* default conftab */ /* memory maps */ Lock mlock; /* lock down the maps */ int time; PCMmap mmap[4]; /* maps, last is always for the kernel */ }; #pragma varargck type "T" int #pragma varargck type "T" uint )sp & ~3); /* build argc, argv on stack */ sp -= (ac+1)*sizeof(sp); lsp = (uchar**)sp; for(i = 0; segdescpatch/9/ppc/ 775 0 0 0 11342263221 155575ustar00cinap_lenrekcinap_lenreksegdescpatch/9/ppc/main.c 664 0 0 21617 11342263221 16676ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "init.h" #include "pool.h" #include "tos.h" #define MAXCONF 64 typedef struct Plan9ini Plan9ini; struct Plan9ini { char *name; char *val; }; char *plan9inistr; Plan9ini plan9ini[MAXCONF]; int nconf; Conf conf; FPsave initfp; Lock testlock; static void plan9iniinit(void); char * cpuid(void) { char *id; id = "unknown PowerPC"; switch(m->cputype) { case 8: id = "PowerPC 750"; break; case 9: id = "PowerPC 604e"; break; case 0x81: id = "PowerPC 8260"; break; case 0x8081: id = "PowerPC 826xA"; break; default: break; } return id; } void cpuidprint(void) { print("cpu0: %s, rev 0x%lux, cpu hz %lld, bus hz %ld\n", cpuid(), getpvr()&0xffff, m->cpuhz, m->bushz); } void main(void) { memset(edata, 0, (ulong)end-(ulong)edata); conf.nmach = 1; machinit(); confinit(); xinit(); trapinit(); mmuinit(); plan9iniinit(); hwintrinit(); clockinit(); timerinit(); console(); quotefmtinstall(); printinit(); cpuidprint(); print("\nPlan 9 from Bell Labs\n"); procinit0(); initseg(); timersinit(); links(); chandevreset(); pageinit(); swapinit(); sharedseginit(); fpsave(&initfp); initfp.fpscr = 0; userinit(); schedinit(); } char* getconf(char *name) { int i; for(i = 0; i < nconf; i++) if(cistrcmp(name, plan9ini[i].name) == 0) return plan9ini[i].val; return nil; } static void plan9iniinit(void) { long i; int c; char *cp, line[MAXCONF], *p, *q; /* * parse configuration args from dos file plan9.ini */ cp = plan9inistr; for(i = 0; i < MAXCONF; i++){ /* * Strip out '\r', change '\t' -> ' ', test for 0xff which is end of file */ p = line; for(q = cp; c = (uchar)*q; q++){ if(c == '\r') continue; if(c == '\t') c = ' '; if(c == 0xff || c == '\n') break; *p++ = c; } *p = 0; if (*line == 0) break; if(*line != '#' && (cp = strchr(line, '='))){ *cp++ = '\0'; kstrdup(&plan9ini[nconf].name, line); kstrdup(&plan9ini[nconf].val, cp); nconf++; } if (c == 0xff) break; cp = q + 1; } } void init0(void) { // char **p, *q, name[KNAMELEN]; int i; char buf[2*KNAMELEN]; up->nerrlab = 0; spllo(); /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ up->slash = namec("#/", Atodir, 0, 0); pathclose(up->slash->path); up->slash->path = newpath("/"); up->dot = cclone(up->slash); chandevinit(); if(!waserror()){ snprint(buf, sizeof(buf), "power %s mtx", conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "power", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); for(i = 0; i < nconf; i++){ if(plan9ini[i].name[0] != '*') ksetenv(plan9ini[i].name, plan9ini[i].val, 0); ksetenv(plan9ini[i].name, plan9ini[i].val, 1); } poperror(); } kproc("alarm", alarmkproc, 0); kproc("mmusweep", mmusweep, 0); touser((void*)(USTKTOP-sizeof(Tos))); } void userinit(void) { Proc *p; Segment *s; KMap *k; Page *pg; p = newproc(); p->pgrp = newpgrp(); p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; p->fgrp = dupfgrp(nil); p->rgrp = newrgrp(); p->procmode = 0640; kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); p->fpstate = FPinit; /* * Stack * * N.B. The -12 for the stack pointer is important. * 4 bytes for gotolabel's return PC */ p->sched.pc = (ulong)init0; p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); /* * User Stack */ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); p->seg[SSEG] = s; pg = newpage(1, 0, USTKTOP-BY2PG); segpage(s, pg); /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); s->flushme++; p->seg[TSEG] = s; pg = newpage(1, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); k = kmap(s->map[0]->pages[0]); memmove((ulong*)VA(k), initcode, sizeof initcode); kunmap(k); ready(p); } void exit(int ispanic) { int ms, once; lock(&active); if(ispanic) active.ispanic = ispanic; else if(m->machno == 0 && (active.machs & (1<machno)) == 0) active.ispanic = 0; once = active.machs & (1<machno); active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); if(once) print("cpu%d: exiting\n", m->machno); spllo(); for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); if(active.machs == 0 && consactive() == 0) break; } if(active.ispanic && m->machno == 0){ if(cpuserver) delay(10000); else if(conf.monitor) for(;;); } else delay(1000); } /* * set up floating point for a new process */ void procsetup(Proc *p) { p->fpstate = FPinit; } void procrestore(Proc *p) { uvlong t; if(p->kp) return; cycles(&t); p->pcycles -= t; } void procfork(Proc *) { } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { uvlong t; cycles(&t); p->pcycles += t; if(p->fpstate == FPactive){ if(p->state != Moribund) fpsave(&up->fpsave); p->fpstate = FPinactive; } } void confinit(void) { char *p; int userpcnt; ulong pa, kpages; /* passed in from ROM monitor: */ if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; pa = PGROUND(PADDR(end)); /* Blast Board specific */ conf.mem[0].npage = (MEM1SIZE - pa)/BY2PG; conf.mem[0].base = pa; conf.mem[1].npage = MEM2SIZE/BY2PG; conf.mem[1].base = MEM2BASE; conf.npage = conf.mem[0].npage + conf.mem[1].npage; conf.nmach = 1; conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; if(cpuserver) conf.nproc *= 3; if(conf.nproc > 2000) conf.nproc = 2000; conf.nimage = 200; conf.nswap = conf.nproc*80; conf.nswppo = 4096; conf.copymode = 0; /* copy on write */ if(cpuserver) { if(userpcnt < 10) userpcnt = 70; kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Hack for the big boys. Only good while physmem < 4GB. * Give the kernel a max. of 16MB + enough to allocate the * page pool. * This is an overestimate as conf.upages < conf.npages. * The patch of nimage is a band-aid, scanning the whole * page list in imagereclaim just takes too long. */ if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){ kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG; conf.nimage = 2000; kpages += (conf.nproc*KSTACK)/BY2PG; } } else { if(userpcnt < 10) { if(conf.npage*BY2PG < 16*MB) userpcnt = 40; else userpcnt = 60; } kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ if(conf.npage*BY2PG < 16*MB) imagmem->minarena = 4*1024*1024; } conf.upages = conf.npage - kpages; conf.ialloc = (kpages/2)*BY2PG; /* * Guess how much is taken by the large permanent * datastructures. Mntcache and Mntrpc are not accounted for * (probably ~300KB). */ kpages *= BY2PG; kpages -= conf.upages*sizeof(Page) + conf.nproc*sizeof(Proc) + conf.nimage*sizeof(Image) + conf.nswap + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; if(!cpuserver){ /* * give terminals lots of image memory, too; the dynamic * allocation will balance the load properly, hopefully. * be careful with 32-bit overflow. */ imagmem->maxsize = kpages; } // conf.monitor = 1; /* BUG */ } static int getcfields(char* lp, char** fields, int n, char* sep) { int i; for(i = 0; lp && *lp && i < n; i++){ while(*lp && strchr(sep, *lp) != 0) *lp++ = 0; if(*lp == 0) break; fields[i] = lp; while(*lp && strchr(sep, *lp) == 0){ if(*lp == '\\' && *(lp+1) == '\n') *lp++ = ' '; lp++; } } return i; } int isaconfig(char *class, int ctlrno, ISAConf *isa) { int i; char cc[KNAMELEN], *p; sprint(cc, "%s%d", class, ctlrno); p = getconf(cc); if(p == 0) return 0; isa->nopt = tokenize(p, isa->opt, NISAOPT); for(i = 0; i < isa->nopt; i++){ p = isa->opt[i]; if(cistrncmp(p, "type=", 5) == 0) isa->type = p + 5; else if(cistrncmp(p, "port=", 5) == 0) isa->port = strtoul(p+5, &p, 0); else if(cistrncmp(p, "irq=", 4) == 0) isa->irq = strtoul(p+4, &p, 0); else if(cistrncmp(p, "dma=", 4) == 0) isa->dma = strtoul(p+4, &p, 0); else if(cistrncmp(p, "mem=", 4) == 0) isa->mem = strtoul(p+4, &p, 0); else if(cistrncmp(p, "size=", 5) == 0) isa->size = strtoul(p+5, &p, 0); else if(cistrncmp(p, "freq=", 5) == 0) isa->freq = strtoul(p+5, &p, 0); } return 1; } int cistrcmp(char *a, char *b) { int ac, bc; for(;;){ ac = *a++; bc = *b++; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } int cistrncmp(char *a, char *b, int n) { unsigned ac, bc; while(n > 0){ ac = *a++; bc = *b++; n--; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } "T" uint )sp & ~3); /* build argc, argv on stack */ sp -= (ac+1)*sizeof(sp); lsp = (uchar**)sp; for(i = 0; segdescpatch/9/ppc/fns.h 664 0 0 6055 11342263221 16524ustar00cinap_lenrekcinap_lenrek#include "../port/portfns.h" int cistrcmp(char*, char*); int cistrncmp(char*, char*, int); void clockinit(void); void clockintr(Ureg*); void cpuidprint(void); void cycles(uvlong*); void dbgputc(int c); void dcacheenb(void); void dcflush(void*, ulong); void dczap(void*, ulong); void delay(int); void delayloopinit(void); void dmiss(void); void dumpregs(Ureg*); void eieio(void); void evenaddr(ulong); void faultpower(Ureg*, ulong addr, int read); void flashprogpower(int); void fpgareset(void); void fprestore(FPsave*); void fpsave(FPsave*); void fptrap(Ureg*); char* getconf(char*); ulong getdar(void); ulong getdcmp(void); ulong getdec(void); ulong getdmiss(void); ulong getdsisr(void); ulong gethash1(void); ulong gethash2(void); ulong gethid0(void); ulong gethid1(void); ulong geticmp(void); ulong geticmp(void); ulong getimiss(void); ulong getmsr(void); ulong getpvr(void); ulong getrpa(void); ulong getsdr1(void); ulong getsr(int); ulong getsrr1(void); void gotopc(ulong); void hwintrinit(void); void icacheenb(void); void icflush(void*, ulong); void idle(void); #define idlehands() /* nothing to do in the runproc */ void imiss(void); int inb(int); void intr(Ureg*); void intrenable(int, void (*)(Ureg*, void*), void*, char*); void intrdisable(int, void (*)(Ureg*, void*), void*, char*); int ioalloc(int, int, int, char*); void iofree(int); int iprint(char*, ...); int isaconfig(char*, int, ISAConf*); void kfpinit(void); #define kmapinval() void links(void); void vectordisable(Vctl *); int vectorenable(Vctl *); void intack(void); void intend(int); int intvec(void); void l2disable(void); void mmuinit(void); void mmusweep(void*); int newmmupid(void); void outb(int, int); int pcicfgr16(Pcidev*, int); int pcicfgr32(Pcidev*, int); int pcicfgr8(Pcidev*, int); void pcicfgw16(Pcidev*, int, int); void pcicfgw32(Pcidev*, int, int); void pcicfgw8(Pcidev*, int, int); void procsave(Proc*); void procsetup(Proc*); void procfork(Proc*); void putdcmp(ulong); void putdec(ulong); void puthash1(ulong); void puthash2(ulong); void puthid0(ulong); void puthid2(ulong); void puticmp(ulong); void puticmp(ulong); void putmsr(ulong); void putrpa(ulong); void putsdr1(ulong); void putsdr1(ulong); void putsr(int, ulong); void putsrr1(ulong); void sethvec(int, void (*)(void)); void setmvec(int, void (*)(void), void (*)(void)); void sharedseginit(void); void console(void); void sync(void); int tas(void*); void timeradd(Timer *); void timerdel(Timer *); void timerinit(void); void tlbflush(ulong); void tlbflushall(void); void tlbld(ulong); void tlbli(ulong); void tlbvec(void); void touser(void*); void trapinit(void); void trapvec(void); #define userureg(ur) (((ur)->status & MSR_PR) != 0) #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) #define KADDR(a) ((void*)((ulong)(a)|KZERO)) #define PADDR(a) ((((ulong)(a)&0xf0000000)==0xf0000000)?(ulong)(a):((ulong)(a)&~KZERO)) #define coherence() eieio() Pcidev* pcimatch(Pcidev*, int, int); Pcidev* pcimatchtbdf(int); void procrestore(Proc*); #ifdef ucuconf extern ulong getpll(void); extern ulong getl2cr(void); extern void putl2cr(ulong); #endif segdescpatch/9/alphapc/ 775 0 0 0 11342263221 164055ustar00cinap_lenrekcinap_lenreksegdescpatch/9/alphapc/main.c 664 0 0 32722 11342263221 17523ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "init.h" #include "pool.h" #include "/sys/src/boot/alphapc/conf.h" #include "axp.h" char argbuf[128]; /* arguments passed to initcode and /boot */ Hwrpb *hwrpb; Bootconf *bootconf; Conf conf; FPsave initfp; /* setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL) */ uvlong initfpcr = (1LL<62)|(1LL<61)|(1LL<60)|(2LL<<58)|(1LL<48); char bootargs[BOOTARGSLEN]; char *confname[MAXCONF]; char *confval[MAXCONF]; int nconf; static void options(void) { long i, n; char *cp, *line[MAXCONF], *p, *q; cp = bootargs; strncpy(cp, bootconf->bootargs, BOOTARGSLEN); cp[BOOTARGSLEN-1] = 0; /* can't print in this routine, see below in main() */ /* * Strip out '\r', change '\t' -> ' '. */ p = cp; for(q = cp; *q; q++){ if(*q == '\r') continue; if(*q == '\t') *q = ' '; *p++ = *q; } *p = 0; n = getfields(cp, line, MAXCONF, 1, "\n"); for(i = 0; i < n; i++){ if(*line[i] == '#') continue; cp = strchr(line[i], '='); if(cp == nil) continue; *cp++ = '\0'; confname[nconf] = line[i]; confval[nconf] = cp; nconf++; } } /* debugging only */ static void dumpopts(void) { int i; print("dumpopts: found /alpha/conf options at %#p\n", bootconf->bootargs); for(i = 0; i < nconf; i++) print("dumpopts: read %s=%s\n", confname[i], confval[i]); } extern void (*i8237alloc)(void); void main(void) { hwrpb = (Hwrpb*)0x10000000; hwrpb = (Hwrpb*)(KZERO|hwrpb->phys); arginit(); machinit(); options(); ioinit(); clockinit(); confinit(); archinit(); xinit(); memholes(); if(i8237alloc != nil) i8237alloc(); mmuinit(); if(arch->coreinit) arch->coreinit(); trapinit(); screeninit(); printinit(); /* it's now safe to print */ /* dumpopts(); /* DEBUG */ kbdinit(); i8250console(); quotefmtinstall(); print("\nPlan 9\n"); cpuidprint(); if(arch->corehello) arch->corehello(); procinit0(); initseg(); timersinit(); links(); chandevreset(); pageinit(); swapinit(); savefpregs(&initfp); initfp.fpstatus = 0x68028000; userinit(); schedinit(); } /* cpu->state bits */ enum { Cpubootinprog = 1, /* boot in progress */ Cpucanrestart = 2, /* restart possible */ Cpuavail = 4, /* processor available */ Cpuexists = 8, /* processor present */ Cpuuserhalted = 0x10, /* user halted */ Cpuctxtokay = 0x20, /* context valid */ Cpupalokay = 0x40, /* PALcode valid */ Cpupalmemokay = 0x80, /* PALcode memory valid */ Cpupalloaded = 0x100, /* PALcode loaded */ Cpuhaltmask = 0xff0000, /* halt request mask */ Cpuhaltdflt = 0, Cpuhaltsaveexit = 0x10000, Cpuhaltcoldboot = 0x20000, Cpuhaltwarmboot = 0x30000, Cpuhaltstayhalted = 0x40000, Cpumustbezero = 0xffffffffff000000ULL, /* 24:63 -- must be zero */ }; /* * initialize a processor's mach structure. each processor does this * for itself. */ void machinit(void) { int n; Hwcpu *cpu; icflush(); n = m->machno; memset(m, 0, sizeof(Mach)); m->machno = n; active.exiting = 0; active.machs = 1; cpu = (Hwcpu*) ((ulong)hwrpb + hwrpb->cpuoff + n*hwrpb->cpulen); cpu->state &= ~Cpubootinprog; if (0) cpu->state |= Cpuhaltstayhalted; } void init0(void) { int i; char buf[2*KNAMELEN]; up->nerrlab = 0; spllo(); /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ up->slash = namec("#/", Atodir, 0, 0); pathclose(up->slash->path); up->slash->path = newpath("/"); up->dot = cclone(up->slash); chandevinit(); if(!waserror()){ snprint(buf, sizeof(buf), "alpha %s alphapc", conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "alpha", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); for(i = 0; i < nconf; i++) if(confname[i]){ if(confname[i][0] != '*') ksetenv(confname[i], confval[i], 0); ksetenv(confname[i], confval[i], 1); } poperror(); } kproc("alarm", alarmkproc, 0); touser((uchar*)(USTKTOP - sizeof(argbuf))); } void userinit(void) { Proc *p; Segment *s; KMap *k; char **av; Page *pg; p = newproc(); p->pgrp = newpgrp(); p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; p->fgrp = dupfgrp(nil); p->rgrp = newrgrp(); p->procmode = 0640; kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); procsetup(p); /* * Kernel Stack */ p->sched.pc = (ulong)init0; p->sched.sp = (ulong)p->kstack+KSTACK-MAXSYSARG*BY2WD; /* * User Stack, pass input arguments to boot process */ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); p->seg[SSEG] = s; pg = newpage(1, 0, USTKTOP-BY2PG); segpage(s, pg); k = kmap(pg); for(av = (char**)argbuf; *av; av++) *av += (USTKTOP - sizeof(argbuf)) - (ulong)argbuf; memmove((uchar*)VA(k) + BY2PG - sizeof(argbuf), argbuf, sizeof argbuf); kunmap(k); /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); s->flushme++; p->seg[TSEG] = s; pg = newpage(1, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); k = kmap(s->map[0]->pages[0]); memmove((uchar*)VA(k), initcode, sizeof initcode); kunmap(k); ready(p); } void procsetup(Proc *p) { p->fpstate = FPinit; fpenab(0); } void procsave(Proc *p) { if(p->fpstate == FPactive){ if(p->state == Moribund) fpenab(0); else savefpregs(&up->fpsave); p->fpstate = FPinactive; } /* * Switch to the prototype page tables for this processor. * While this processor is in the scheduler, the process could run * on another processor and exit, returning the page tables to * the free list where they could be reallocated and overwritten. * When this processor eventually has to get an entry from the * trashed page tables it will crash. */ mmupark(); } void procfork(Proc *) { } void setupboot(int halt) { int n = 0; // cpu id of primary cpu, not just m->machno Hwcpu *cpu = (Hwcpu*)((ulong)hwrpb + hwrpb->cpuoff + n*hwrpb->cpulen); cpu->state &= ~(Cpucanrestart | Cpuhaltmask); cpu->state |= (halt? Cpuhaltstayhalted: Cpuhaltwarmboot); } /* from ../pc */ static void shutdown(int ispanic) { int ms, once; lock(&active); if(ispanic) active.ispanic = ispanic; else if(m->machno == 0 && (active.machs & (1<machno)) == 0) active.ispanic = 0; once = active.machs & (1<machno); active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); if(once) print("cpu%d: exiting\n", m->machno); spllo(); for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); if(active.machs == 0 && consactive() == 0) break; } if(active.ispanic && m->machno == 0) { if(cpuserver) delay(10000); else for (;;) continue; } else delay(1000); } /* from ../pc: */ void reboot(void *entry, void *code, ulong size) { // writeconf(); // pass kernel environment to next kernel shutdown(0); /* * should be the only processor running now */ print("shutting down...\n"); delay(200); splhi(); /* turn off buffered serial console */ serialoq = nil; /* shutdown devices */ chandevshutdown(); #ifdef FUTURE { ulong *pdb; /* * Modify the machine page table to directly map the low 4MB of memory * This allows the reboot code to turn off the page mapping */ pdb = m->pdb; pdb[PDX(0)] = pdb[PDX(KZERO)]; mmuflushtlb(PADDR(pdb)); } /* setup reboot trampoline function */ { void (*f)(ulong, ulong, ulong) = (void*)REBOOTADDR; memmove(f, rebootcode, sizeof(rebootcode)); #else USED(entry, code, size); #endif print("rebooting...\n"); #ifdef FUTURE /* off we go - never to return */ (*f)(PADDR(entry), PADDR(code), size); } #endif setupboot(0); // reboot, don't halt exit(0); } void exit(int ispanic) { canlock(&active); active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); spllo(); print("cpu %d exiting\n", m->machno); do delay(100); while(consactive()); splhi(); delay(1000); /* give serial fifo time to finish flushing */ if (getconf("*debug") != nil) { USED(ispanic); delay(60*1000); /* give us time to read the screen */ } if(arch->coredetach) arch->coredetach(); setupboot(1); // set up to halt for (; ; ) firmware(); // on PC is just: //if (0) { // shutdown(ispanic); // arch->reset(); //} } void confinit(void) { ulong ktop, kpages; Bank *b, *eb; extern void _main(void); int userpcnt; char *p; if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; /* * The console firmware divides memory into 1 or more banks. * FInd the bank with the kernel in it. */ b = bootconf->bank; eb = b+bootconf->nbank; ktop = PGROUND((ulong)end); ktop = PADDR(ktop); while(b < eb) { if(b->min < ktop && ktop < b->max) break; b++; } if(b == eb) panic("confinit"); /* * Split the bank of memory into 2 banks to fool the allocator into * allocating low memory pages from bank 0 for any peripherals * which only have a 24bit address counter. */ conf.mem[0].npage = (8*1024*1024)/BY2PG; conf.mem[0].base = 0; conf.mem[1].npage = (b->max-8*1024*1024)/BY2PG; conf.mem[1].base = 8*1024*1024; conf.npage = conf.mem[0].npage+conf.mem[1].npage; conf.upages = (conf.npage*70)/100; conf.mem[0].npage -= ktop/BY2PG; conf.mem[0].base += ktop; conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG; /* * Fix up the bank we found to be the remnant, below the kernel. * This, and the other banks, will be passed to xhole() later. * BUG: conf.upages needs to be adjusted, but how? In practice, * we only have 1 bank, and the remnant is small. */ b->max = (uvlong)_main & ~(BY2PG-1); conf.nmach = 1; conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; if(cpuserver) conf.nproc *= 3; if(conf.nproc > 2000) conf.nproc = 2000; conf.nimage = 200; conf.nswap = conf.nproc*80; conf.nswppo = 4096; conf.copymode = 0; /* copy on write */ if(cpuserver) { if(userpcnt < 10) userpcnt = 70; kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Hack for the big boys. Only good while physmem < 4GB. * Give the kernel a max. of 16MB + enough to allocate the * page pool. * This is an overestimate as conf.upages < conf.npages. * The patch of nimage is a band-aid, scanning the whole * page list in imagereclaim just takes too long. */ if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){ kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG; conf.nimage = 2000; kpages += (conf.nproc*KSTACK)/BY2PG; } } else { if(userpcnt < 10) { if(conf.npage*BY2PG < 16*MB) userpcnt = 40; else userpcnt = 60; } kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ if(conf.npage*BY2PG < 16*MB) imagmem->minarena = 4*1024*1024; } conf.upages = conf.npage - kpages; conf.ialloc = (kpages/2)*BY2PG; /* * Guess how much is taken by the large permanent * datastructures. Mntcache and Mntrpc are not accounted for * (probably ~300KB). */ kpages *= BY2PG; kpages -= conf.upages*sizeof(Page) + conf.nproc*sizeof(Proc) + conf.nimage*sizeof(Image) + conf.nswap + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; if(!cpuserver){ /* * give terminals lots of image memory, too; the dynamic * allocation will balance the load properly, hopefully. * be careful with 32-bit overflow. */ imagmem->maxsize = kpages; } // conf.monitor = 1; /* BUG */ } void memholes(void) { Bank *b, *eb; b = bootconf->bank; eb = b+bootconf->nbank; while(b < eb) { if(b->min < (1LL<<32) && b->max < (1LL<<32)) xhole(b->min, b->max-b->min); b++; } } char *sp; char * pusharg(char *p) { int n; n = strlen(p)+1; sp -= n; memmove(sp, p, n); return sp; } void arginit(void) { char **av; av = (char**)argbuf; sp = argbuf + sizeof(argbuf); *av++ = pusharg("boot"); *av = 0; } char * getconf(char *name) { int n; for(n = 0; n < nconf; n++) if(cistrcmp(confname[n], name) == 0) { return confval[n]; } return 0; } int isaconfig(char *class, int ctlrno, ISAConf *isa) { char cc[32], *p; int i, n; snprint(cc, sizeof cc, "%s%d", class, ctlrno); for(n = 0; n < nconf; n++){ if(cistrcmp(confname[n], cc) != 0) continue; isa->nopt = tokenize(confval[n], isa->opt, NISAOPT); for(i = 0; i < isa->nopt; i++){ p = isa->opt[i]; if(cistrncmp(p, "type=", 5) == 0) isa->type = p + 5; else if(cistrncmp(p, "port=", 5) == 0) isa->port = strtoul(p+5, &p, 0); else if(cistrncmp(p, "irq=", 4) == 0) isa->irq = strtoul(p+4, &p, 0); else if(cistrncmp(p, "dma=", 4) == 0) isa->dma = strtoul(p+4, &p, 0); else if(cistrncmp(p, "mem=", 4) == 0) isa->mem = strtoul(p+4, &p, 0); else if(cistrncmp(p, "size=", 5) == 0) isa->size = strtoul(p+5, &p, 0); else if(cistrncmp(p, "freq=", 5) == 0) isa->freq = strtoul(p+5, &p, 0); } return 1; } return 0; } int cistrcmp(char *a, char *b) { int ac, bc; for(;;){ ac = *a++; bc = *b++; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } int cistrncmp(char *a, char *b, int n) { unsigned ac, bc; while(n > 0){ ac = *a++; bc = *b++; n--; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } int getcfields(char* lp, char** fields, int n, char* sep) { int i; for(i = 0; lp && *lp && i < n; i++){ while(*lp && strchr(sep, *lp) != 0) *lp++ = 0; if(*lp == 0) break; fields[i] = lp; while(*lp && strchr(sep, *lp) == 0){ if(*lp == '\\' && *(lp+1) == '\n') *lp++ = ' '; lp++; } } return i; } nprint(buf, sizeof(buf), "alpha %s alphapc", csegdescpatch/9/alphapc/fns.h 664 0 0 7167 11342263221 17357ustar00cinap_lenrekcinap_lenrek#include "../port/portfns.h" Dirtab* addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong)); void archinit(void); void arginit(void); void arith(void); ulong cankaddr(ulong); void clock(Ureg*); void clockinit(void); void clockintrsched(void); #define coherence mb int cistrcmp(char*, char*); int cistrncmp(char*, char*, int); int cmpswap(long*, long, long); void cpuidprint(void); void cserve(ulong, ulong); #define cycles(x) do{}while(0) void timeradd(Timer *); void timerdel(Timer *); int dmacount(int); int dmadone(int); void dmaend(int); int dmainit(int, int); long dmasetup(int, void*, long, int); void _dumpstack(Ureg *); void evenaddr(ulong); void fataltrap(Ureg *, char *); void fault0(void); void faultalpha(Ureg*); ulong fcr31(void); void firmware(void); void fpenab(int); void fptrap(Ureg*); int getcfields(char*, char**, int, char*); char *getconf(char*); int havetimer(void); int i8042auxcmd(int); void i8042auxenable(void (*)(int, int)); void i8042reset(void); void i8250console(void); void i8250mouse(char*, int(*)(Queue*,int), int); void i8250setmouseputc(char*, int (*)(Queue*, int)); void i8259init(void); int i8259enable(int, int, Vctl*); #define idlehands() /* nothing to do in the runproc */ void icflush(void); void illegal0(void); void intr0(void); void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); int intrdisable(int, void (*)(Ureg *, void *), void*, int, char*); int ioalloc(int, int, int, char*); void iofree(int); void ioinit(void); int iounused(int, int); int irqallocread(char*, long, vlong); int isaconfig(char*, int, ISAConf*); void kbdinit(void); #define kexit(a) #define kmapinval() void *kmapv(uvlong, int); int kprint(char*, ...); void links(void); void mb(void); void memholes(void); ulong meminit(void); void mmudump(void); void mmuinit(void); void mmupark(void); ulong pcibarsize(Pcidev*, int); int pcicfgr8(Pcidev*, int); int pcicfgr16(Pcidev*, int); int pcicfgr32(Pcidev*, int); void pcicfgw8(Pcidev*, int, int); void pcicfgw16(Pcidev*, int, int); void pcicfgw32(Pcidev*, int, int); void pciclrbme(Pcidev*); void pcihinv(Pcidev*); Pcidev* pcimatch(Pcidev*, int, int); Pcidev* pcimatchtbdf(int); void pcireset(void); void pcisetbme(Pcidev*); int pcmspecial(char*, ISAConf*); int (*_pcmspecial)(char *, ISAConf *); void pcmspecialclose(int); void (*_pcmspecialclose)(int); void prflush(void); void printinit(void); #define procrestore(p) void procsave(Proc*); void procsetup(Proc*); void procfork(Proc*); void restfpregs(FPsave*); uvlong rpcc(uvlong*); void screeninit(void); void (*screenputs)(char*, int); void setpcb(PCB *); PCB *swpctx(PCB *); void syscall0(void); int tas(ulong*); void tlbflush(int, ulong); void touser(void*); void trapinit(void); void unaligned(void); ulong upaalloc(int, int); void upafree(ulong, int); #define userureg(ur) ((ur)->status & UMODE) void* vmap(ulong, int); void wrent(int, void*); void wrvptptr(uvlong); void vunmap(void*, int); #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) #define KADDR(a) ((void*)((ulong)(a)|KZERO)) #define PADDR(a) ((ulong)(a)&~KZERO) #define inb(p) (arch->_inb)(p) #define ins(p) (arch->_ins)(p) #define inl(p) (arch->_inl)(p) #define outb(p, x) (arch->_outb)((p), (x)) #define outs(p, x) (arch->_outs)((p), (x)) #define outl(p, x) (arch->_outl)((p), (x)) #define insb(p, buf, len) (arch->_insb)((p), (buf), (len)) #define inss(p, buf, len) (arch->_inss)((p), (buf), (len)) #define insl(p, buf, len) (arch->_insl)((p), (buf), (len)) #define outsb(p, buf, len) (arch->_outsb)((p), (buf), (len)) #define outss(p, buf, len) (arch->_outss)((p), (buf), (len)) #define outsl(p, buf, len) (arch->_outsl)((p), (buf), (len)) m->machno); do delay(100); while(consactive()); splhi(); delay(1000); /* give serial fifo time to finish flushing */ if (getconf("*debug") != nil) { USED(ispanic); delay(60*1000); /* give us time to read the screen */ } if(arch->coredetach) arch->coredetach(); setupboot(1); // set up to halt for (; ; ) firmware(); // on PC is just: //if (0) { // shutdown(ispanic)segdescpatch/9/bitsy/ 775 0 0 0 11342263221 161275ustar00cinap_lenrekcinap_lenreksegdescpatch/9/bitsy/main.c 664 0 0 21423 11342263221 17241ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "init.h" #include "pool.h" Mach *m; Proc *up; Conf conf; int noprint; void main(void) { mmuinvalidate(); /* zero out bss */ memset(edata, 0, end-edata); /* point to Mach structure */ m = (Mach*)MACHADDR; memset(m, 0, sizeof(Mach)); m->ticks = 1; active.machs = 1; rs232power(1); quotefmtinstall(); iprint("\nPlan 9 bitsy kernel\n"); confinit(); xinit(); mmuinit(); machinit(); trapinit(); sa1110_uartsetup(1); dmainit(); screeninit(); printinit(); /* from here on, print works, before this we need iprint */ clockinit(); procinit0(); initseg(); links(); chandevreset(); pageinit(); swapinit(); userinit(); powerinit(); schedinit(); } /* need to do better */ void reboot(void*, void*, ulong) { exit(0); } /* * exit kernel either on a panic or user request */ void exit(int ispanic) { void (*f)(void); USED(ispanic); delay(1000); iprint("it's a wonderful day to die\n"); cacheflush(); mmuinvalidate(); mmudisable(); f = nil; (*f)(); } static uchar *sp; /* * starting place for first process */ void init0(void) { up->nerrlab = 0; spllo(); /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ up->slash = namec("#/", Atodir, 0, 0); pathclose(up->slash->path); up->slash->path = newpath("/"); up->dot = cclone(up->slash); chandevinit(); if(!waserror()){ ksetenv("terminal", "bitsy", 0); ksetenv("cputype", "arm", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); poperror(); } kproc("alarm", alarmkproc, 0); kproc("power", powerkproc, 0); touser(sp); } /* * pass boot arguments to /boot */ static uchar * pusharg(char *p) { int n; n = strlen(p)+1; sp -= n; memmove(sp, p, n); return sp; } static void bootargs(ulong base) { int i, ac; uchar *av[32]; uchar *bootpath; uchar **lsp; /* * the sizeof(Sargs) is to make the validaddr check in * trap.c's syscall() work even when we have less than the * max number of args. */ sp = (uchar*)base + BY2PG - sizeof(Sargs); bootpath = pusharg("/boot/boot"); ac = 0; av[ac++] = pusharg("boot"); /* 4 byte word align stack */ sp = (uchar*)((ulong)sp & ~3); /* build argc, argv on stack */ sp -= (ac+1)*sizeof(sp); lsp = (uchar**)sp; for(i = 0; i < ac; i++) *lsp++ = av[i] + ((USTKTOP - BY2PG) - base); *lsp = 0; /* push argv onto stack */ sp -= BY2WD; lsp = (uchar**)sp; *lsp = sp + BY2WD + ((USTKTOP - BY2PG) - base); /* push pointer to "/boot" */ sp -= BY2WD; lsp = (uchar**)sp; *lsp = bootpath + ((USTKTOP - BY2PG) - base); /* leave space for where the initcode's caller's return PC would normally reside */ sp -= BY2WD; /* relocate stack to user's virtual addresses */ sp += (USTKTOP - BY2PG) - base; } /* * create the first process */ void userinit(void) { Proc *p; Segment *s; KMap *k; Page *pg; /* no processes yet */ up = nil; p = newproc(); p->pgrp = newpgrp(); p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; p->fgrp = dupfgrp(nil); p->rgrp = newrgrp(); p->procmode = 0640; kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); /* * Kernel Stack */ p->sched.pc = (ulong)init0; p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); /* * User Stack */ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); p->seg[SSEG] = s; pg = newpage(1, 0, USTKTOP-BY2PG); segpage(s, pg); k = kmap(pg); bootargs(VA(k)); kunmap(k); /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); p->seg[TSEG] = s; pg = newpage(1, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); k = kmap(s->map[0]->pages[0]); memmove((ulong*)VA(k), initcode, sizeof initcode); kunmap(k); ready(p); } /* * set mach dependent process state for a new process */ void procsetup(Proc *p) { p->fpstate = FPinit; } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { USED(p); } void procfork(Proc *) { } /* place holder */ /* * dummy since rdb is not included */ void rdb(void) { } /* * probe the last location in a meg of memory, make sure it's not * reflected into something else we've already found. */ int probemem(ulong addr) { int i; ulong *p; ulong a; addr += OneMeg - sizeof(ulong); p = (ulong*)addr; *p = addr; for(i=0; i= conf.mem[i].base && ktop <= conf.mem[i].limit) conf.mem[i].base = ktop; /* zero memory */ memset((void*)conf.mem[i].base, 0, conf.mem[i].limit - conf.mem[i].base); conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG; conf.npage += conf.mem[i].npage; } if(conf.npage > 16*MB/BY2PG){ conf.upages = (conf.npage*60)/100; imagmem->minarena = 4*1024*1024; }else conf.upages = (conf.npage*40)/100; conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG; /* only one processor */ conf.nmach = 1; /* set up other configuration parameters */ conf.nproc = 100; conf.nswap = conf.npage*3; conf.nswppo = 4096; conf.nimage = 200; conf.monitor = 1; conf.copymode = 0; /* copy on write */ } GPIOregs *gpioregs; ulong *egpioreg = (ulong*)EGPIOREGS; PPCregs *ppcregs; MemConfRegs *memconfregs; PowerRegs *powerregs; ResetRegs *resetregs; /* * configure the machine */ void machinit(void) { /* set direction of SA1110 io pins and select alternate functions for some */ gpioregs = mapspecial(GPIOREGS, sizeof(GPIOregs)); gpioregs->direction = GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o |GPIO_CLK_SET0_o|GPIO_CLK_SET1_o |GPIO_L3_SDA_io|GPIO_L3_MODE_o|GPIO_L3_SCLK_o |GPIO_COM_RTS_o; gpioregs->rising = 0; gpioregs->falling = 0; gpioregs->altfunc |= GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o |GPIO_SSP_CLK_i; /* map in special H3650 io pins */ egpioreg = mapspecial(EGPIOREGS, sizeof(ulong)); /* map in peripheral pin controller (ssp will need it) */ ppcregs = mapspecial(PPCREGS, sizeof(PPCregs)); /* SA1110 power management */ powerregs = mapspecial(POWERREGS, sizeof(PowerRegs)); /* memory configuraton */ memconfregs = mapspecial(MEMCONFREGS, sizeof(MemConfRegs)); /* reset controller */ resetregs = mapspecial(RESETREGS, sizeof(ResetRegs)); } /* * manage egpio bits */ static ulong egpiosticky; void egpiobits(ulong bits, int on) { if(on) egpiosticky |= bits; else egpiosticky &= ~bits; *egpioreg = egpiosticky; } void rs232power(int on) { egpiobits(EGPIO_rs232_power, on); delay(50); } void audioamppower(int on) { egpiobits(EGPIO_audio_power, on); delay(50); } void audioicpower(int on) { egpiobits(EGPIO_audio_ic_power, on); delay(50); } void irpower(int on) { egpiobits(EGPIO_ir_power, on); delay(50); } void lcdpower(int on) { egpiobits(EGPIO_lcd_3v|EGPIO_lcd_ic_power|EGPIO_lcd_5v|EGPIO_lcd_9v, on); delay(50); } void flashprogpower(int on) { egpiobits(EGPIO_prog_flash, on); } /* here on hardware reset */ void resettrap(void) { } /* * for drivers that used to run on x86's */ void outb(ulong a, uchar v) { *(uchar*)a = v; } void outs(ulong a, ushort v) { *(ushort*)a = v; } void outss(ulong a, void *p, int n) { ushort *sp = p; while(n-- > 0) *(ushort*)a = *sp++; } void outl(ulong a, ulong v) { *(ulong*)a = v; } uchar inb(ulong a) { return *(uchar*)a; } ushort ins(ulong a) { return *(ushort*)a; } void inss(ulong a, void *p, int n) { ushort *sp = p; while(n-- > 0) *sp++ = *(ushort*)a; } ulong inl(ulong a) { return *(ulong*)a; } char* getconf(char*) { return nil; } long _xdec(long *p) { int s; long v; s = splhi(); v = --*p; splx(s); return v; } void _xinc(long *p) { int s; s = splhi(); ++*p; splx(s); } int cmpswap(long *addr, long old, long new) { int r, s; s = splhi(); if(r = (*addr==old)) *addr = new; splx(s); return r; } { USED(ispanic); delay(60*1000); /* give us time to read the screen */ } if(arch->coredetach) arch->coredetach(); setupboot(1); // set up to halt for (; ; ) firmware(); // on PC is just: //if (0) { // shutdown(ispanic)segdescpatch/9/bitsy/fns.h 664 0 0 6002 11342263221 17064ustar00cinap_lenrekcinap_lenrek#include "../port/portfns.h" void audiopower(int); void audioamppower(int); void audioicpower(int); void cacheflush(void); void cachewb(void); void cachewbaddr(void*); void cachewbregion(ulong, int); ulong cankaddr(ulong); void dcacheinvalidate(void); int cistrcmp(char*, char*); int cistrncmp(char*, char*, int); void clockinit(void); ulong clockpower(int); int cmpswap(long*, long, long); #define coherence() #define cycles(x) do{}while(0) #define dcflush(a, b) void delay(int); void µcpower(int); void deepsleep(void); void dmainit(void); void doze(void); void egpiobits(ulong, int); void evenaddr(ulong); ulong findva(ulong, ulong, ulong); void flashprogpower(int); void flushmmu(void); int fpiarm(Ureg *ur); char* getconf(char*); ulong getcpuid(void); ulong getfar(void); ulong getfsr(void); ulong getcontrol(void); ulong getdac(void); ulong getttb(void); void* getlink(void); #define getpgcolor(a) 0 ulong getsp(void); void icacheinvalidate(void); void idle(void); void idlehands(void); uchar inb(ulong); ushort ins(ulong); void inss(ulong, void*, int); ulong inl(ulong); void intrdisable(int, int, void (*)(Ureg*, void*), void*, char*); void intrenable(int, int, void (*)(Ureg*, void*), void*, char*); void irpower(int); #define kexit(a) #define kmapinval() void lcdpower(int); void links(void); void* mapmem(ulong, int, int); void mappedIvecEnable(void); void mappedIvecDisable(void); void* mapspecial(ulong, int); void meminit(void); void mmuinit(void); void mmuenable(void); void mmudisable(void); void mmuinvalidate(void); void mmuinvalidateaddr(ulong); void mmurestart(void); ulong mmu_kaddr(ulong); ulong mmu_paddr(ulong); int µcputc(Queue*, int); void noted(Ureg*, ulong); int notify(Ureg*); void outb(ulong, uchar); void outs(ulong, ushort); void outss(ulong, void*, int); void outl(ulong, ulong); void pcmciapower(int); void pcmcisread(PCMslot*); int pcmcistuple(int, int, int, void*, int); PCMmap* pcmmap(int, ulong, int, int); void pcmunmap(int, PCMmap*); void penbutton(int, int); void pentrackxy(int x, int y); void power_down(void); void powerinit(void); void powerkproc(void*); #define procrestore(p) void procsave(Proc*); void procsetup(Proc*); void procfork(Proc*); void putdac(ulong); void putttb(ulong); void putpid(ulong); void resetsuspendtimer(void); void rs232power(int); void rtcalarm(ulong); void sa1110_uartsetup(int); void screeninit(void); void screenpower(int); void serialµcputs(uchar *str, int n); void setr13(int, ulong*); uchar* tarlookup(uchar*, char*, int*); void timersinit(void); void timeradd(Timer*); void timerdel(Timer*); void timerset(Tval); void touser(void*); void trapdump(char *tag); void trapinit(void); void trapresume(void); int tas(void*); void uartpower(int); int uartstageoutput(Uart*); void uartkick(void*); void uartrecv(Uart*, char); #define userureg(ur) (((ur)->psr & PsrMask) == PsrMusr) void vectors(void); void vtable(void); void wbflush(void); #define KADDR(a) (void*)mmu_kaddr((ulong)(a)) #define PADDR(a) mmu_paddr((ulong)(a)) #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) *)sp; *lsp = sp + BY2WD + ((USTKTOP - BY2PG) - base); /* push pointer to "/boot" */ sp -= BY2WD; lsp = (uchar**)sp; *lsp = bootpath + ((USTKTOP - BY2PG) - base); /* leave space for where the initcode's caller's return PC would normally reside */ sp -= BY2WD; /* relocate stack to user's virtual addresses */ sp += (USTKTOP - BY2PG) - base; } /* * create the first process */ void userinit(void) { Proc *p; Segment *s; KMap *k; Page *pg; /* no processes yet */ up = nil; p = newproc(); segdescpatch/9/mtx/ 775 0 0 0 11342263221 156055ustar00cinap_lenrekcinap_lenreksegdescpatch/9/mtx/main.c 664 0 0 20217 11342263221 16717ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "init.h" #include "pool.h" Conf conf; FPsave initfp; void main(void) { memset(edata, 0, (ulong)end-(ulong)edata); conf.nmach = 1; machinit(); ioinit(); i8250console(); quotefmtinstall(); print("\nPlan 9\n"); confinit(); xinit(); raveninit(); trapinit(); printinit(); cpuidprint(); mmuinit(); hwintrinit(); clockinit(); kbdinit(); procinit0(); initseg(); timersinit(); links(); chandevreset(); pageinit(); swapinit(); fpsave(&initfp); initfp.fpscr = 0; userinit(); schedinit(); } void machinit(void) { memset(m, 0, sizeof(Mach)); m->cputype = getpvr()>>16; /* * For polled uart output at boot, need * a default delay constant. 100000 should * be enough for a while. Cpuidentify will * calculate the real value later. */ m->loopconst = 100000; /* turn on caches */ puthid0(gethid0() | BIT(16) | BIT(17)); active.machs = 1; active.exiting = 0; } void cpuidprint(void) { char *id; id = "unknown PowerPC"; switch(m->cputype) { case 9: id = "PowerPC 604e"; break; } print("cpu0: %s\n", id); } static struct { char *name; char *val; } plan9ini[] = { { "console", "0" }, { "ether0", "type=2114x" }, }; char* getconf(char *name) { int i; for(i = 0; i < nelem(plan9ini); i++) if(cistrcmp(name, plan9ini[i].name) == 0) return plan9ini[i].val; return nil; } void init0(void) { // char **p, *q, name[KNAMELEN]; // int n; char buf[2*KNAMELEN]; up->nerrlab = 0; spllo(); /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ up->slash = namec("#/", Atodir, 0, 0); pathclose(up->slash->path); up->slash->path = newpath("/"); up->dot = cclone(up->slash); chandevinit(); if(!waserror()){ snprint(buf, sizeof(buf), "power %s mtx", conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "power", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); /* for(p = confenv; *p; p++) { q = strchr(p[0], '='); if(q == 0) continue; n = q-p[0]; if(n >= KNAMELEN) n = KNAMELEN-1; memmove(name, p[0], n); name[n] = 0; if(name[0] != '*') ksetenv(name, q+1, 0); ksetenv(name, q+1, 1); } */ poperror(); } kproc("alarm", alarmkproc, 0); kproc("mmusweep", mmusweep, 0); touser((void*)(USTKTOP-8)); } void userinit(void) { Proc *p; Segment *s; KMap *k; Page *pg; p = newproc(); p->pgrp = newpgrp(); p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; p->fgrp = dupfgrp(nil); p->rgrp = newrgrp(); p->procmode = 0640; kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); p->fpstate = FPinit; /* * Kernel Stack * * N.B. The -12 for the stack pointer is important. * 4 bytes for gotolabel's return PC */ p->sched.pc = (ulong)init0; p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); /* * User Stack */ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); p->seg[SSEG] = s; pg = newpage(1, 0, USTKTOP-BY2PG); segpage(s, pg); /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); s->flushme++; p->seg[TSEG] = s; pg = newpage(1, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); k = kmap(s->map[0]->pages[0]); memmove((ulong*)VA(k), initcode, sizeof initcode); kunmap(k); ready(p); } /* still to do */ void reboot(void*, void*, ulong) { exit(0); } void exit(int ispanic) { int ms, once; lock(&active); if(ispanic) active.ispanic = ispanic; else if(m->machno == 0 && (active.machs & (1<machno)) == 0) active.ispanic = 0; once = active.machs & (1<machno); active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); if(once) print("cpu%d: exiting\n", m->machno); spllo(); for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); if(active.machs == 0 && consactive() == 0) break; } if(active.ispanic && m->machno == 0){ if(cpuserver) delay(10000); else if(conf.monitor) for(;;); } else delay(1000); watchreset(); } /* * set up floating point for a new process */ void procsetup(Proc *p) { p->fpstate = FPinit; } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { if(p->fpstate == FPactive){ if(p->state != Moribund) fpsave(&up->fpsave); p->fpstate = FPinactive; } } void procfork(Proc *) { } void confinit(void) { char *p; int userpcnt; ulong pa, kpages; extern ulong memsize; /* passed in from ROM monitor */ if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; pa = PGROUND(PADDR(end)); conf.mem[0].npage = memsize/BY2PG; conf.mem[0].base = pa; conf.npage = conf.mem[0].npage; conf.nmach = 1; conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; if(cpuserver) conf.nproc *= 3; if(conf.nproc > 2000) conf.nproc = 2000; conf.nimage = 200; conf.nswap = conf.nproc*80; conf.nswppo = 4096; conf.copymode = 0; /* copy on write */ if(cpuserver) { if(userpcnt < 10) userpcnt = 70; kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Hack for the big boys. Only good while physmem < 4GB. * Give the kernel a max. of 16MB + enough to allocate the * page pool. * This is an overestimate as conf.upages < conf.npages. * The patch of nimage is a band-aid, scanning the whole * page list in imagereclaim just takes too long. */ if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){ kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG; conf.nimage = 2000; kpages += (conf.nproc*KSTACK)/BY2PG; } } else { if(userpcnt < 10) { if(conf.npage*BY2PG < 16*MB) userpcnt = 40; else userpcnt = 60; } kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ if(conf.npage*BY2PG < 16*MB) imagmem->minarena = 4*1024*1024; } conf.upages = conf.npage - kpages; conf.ialloc = (kpages/2)*BY2PG; /* * Guess how much is taken by the large permanent * datastructures. Mntcache and Mntrpc are not accounted for * (probably ~300KB). */ kpages *= BY2PG; kpages -= conf.upages*sizeof(Page) + conf.nproc*sizeof(Proc) + conf.nimage*sizeof(Image) + conf.nswap + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; if(!cpuserver){ /* * give terminals lots of image memory, too; the dynamic * allocation will balance the load properly, hopefully. * be careful with 32-bit overflow. */ imagmem->maxsize = kpages; } // conf.monitor = 1; /* BUG */ } static int getcfields(char* lp, char** fields, int n, char* sep) { int i; for(i = 0; lp && *lp && i < n; i++){ while(*lp && strchr(sep, *lp) != 0) *lp++ = 0; if(*lp == 0) break; fields[i] = lp; while(*lp && strchr(sep, *lp) == 0){ if(*lp == '\\' && *(lp+1) == '\n') *lp++ = ' '; lp++; } } return i; } int isaconfig(char *class, int ctlrno, ISAConf *isa) { int i; char cc[KNAMELEN], *p; sprint(cc, "%s%d", class, ctlrno); p = getconf(cc); if(p == 0) return 0; isa->nopt = tokenize(p, isa->opt, NISAOPT); for(i = 0; i < isa->nopt; i++){ p = isa->opt[i]; if(cistrncmp(p, "type=", 5) == 0) isa->type = p + 5; else if(cistrncmp(p, "port=", 5) == 0) isa->port = strtoul(p+5, &p, 0); else if(cistrncmp(p, "irq=", 4) == 0) isa->irq = strtoul(p+4, &p, 0); else if(cistrncmp(p, "dma=", 4) == 0) isa->dma = strtoul(p+4, &p, 0); else if(cistrncmp(p, "mem=", 4) == 0) isa->mem = strtoul(p+4, &p, 0); else if(cistrncmp(p, "size=", 5) == 0) isa->size = strtoul(p+5, &p, 0); else if(cistrncmp(p, "freq=", 5) == 0) isa->freq = strtoul(p+5, &p, 0); } return 1; } int cistrcmp(char *a, char *b) { int ac, bc; for(;;){ ac = *a++; bc = *b++; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } int cistrncmp(char *a, char *b, int n) { unsigned ac, bc; while(n > 0){ ac = *a++; bc = *b++; n--; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } id uartpower(int); int uartstageoutput(Uart*); void uartkick(void*); void uartrecv(Uart*, char); #define userureg(ur) (((ur)->psr & PsrMask) == PsrMusr) void vectors(void); void vtable(void); void wbflush(void); #define KADDR(a) (void*)mmu_kaddr((ulong)(a)) #define PADDR(a) mmu_paddr((ulong)(a)) #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])segdescpatch/9/mtx/fns.h 664 0 0 5374 11342263221 16555ustar00cinap_lenrekcinap_lenrek#include "../port/portfns.h" ulong cankaddr(ulong); int cistrcmp(char*, char*); int cistrncmp(char*, char*, int); void clockinit(void); void clockintr(Ureg*); void clockintrsched(void); int cmpswap(long*, long, long); #define coherence() eieio() void cpuidprint(void); #define cycles(x) do{}while(0) void dcflush(void*, ulong); void delay(int); void dumpregs(Ureg*); void delayloopinit(void); void eieio(void); void evenaddr(ulong); void faultpower(Ureg*, ulong addr, int read); void fprestore(FPsave*); void fpsave(FPsave*); char* getconf(char*); ulong getdar(void); ulong getdec(void); ulong getdsisr(void); ulong gethid0(void); ulong gethid1(void); ulong getmsr(void); ulong getpvr(void); void gotopc(ulong); int havetimer(void); void hwintrinit(void); void i8250console(void); void i8259init(void); int i8259intack(void); int i8259enable(Vctl*); int i8259vecno(int); int i8259disable(int); void icflush(void*, ulong); #define idlehands() /* nothing to do in the runproc */ int inb(int); void insb(int, void*, int); ushort ins(int); void inss(int, void*, int); ulong inl(int); void insl(int, void*, int); void intr(Ureg*); void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); int ioalloc(int, int, int, char*); void iofree(int); void ioinit(void); int iprint(char*, ...); int isaconfig(char*, int, ISAConf*); void kbdinit(void); #define kexit(a) #define kmapinval() void links(void); void mmuinit(void); void mmusweep(void*); void mpicdisable(int); void mpicenable(int, Vctl*); int mpiceoi(int); int mpicintack(void); int newmmupid(void); void outb(int, int); void outsb(int, void*, int); void outs(int, ushort); void outss(int, void*, int); void outl(int, ulong); void outsl(int, void*, int); int pciscan(int, Pcidev **); ulong pcibarsize(Pcidev *, int); int pcicfgr8(Pcidev*, int); int pcicfgr16(Pcidev*, int); int pcicfgr32(Pcidev*, int); void pcicfgw8(Pcidev*, int, int); void pcicfgw16(Pcidev*, int, int); void pcicfgw32(Pcidev*, int, int); void pciclrbme(Pcidev*); void pcihinv(Pcidev*); uchar pciipin(Pcidev *, uchar); Pcidev* pcimatch(Pcidev*, int, int); Pcidev* pcimatchtbdf(int); void pcireset(void); void pcisetbme(Pcidev*); #define procrestore(p) void procsave(Proc*); void procsetup(Proc*); void procfork(Proc*); void putdec(ulong); void puthid0(ulong); void puthid1(ulong); void putmsr(ulong); void putsdr1(ulong); void putsr(int, ulong); void raveninit(void); void sync(void); int tas(void*); void timeradd(Timer *); void timerdel(Timer *); void touser(void*); void trapinit(void); void trapvec(void); void tlbflush(ulong); void tlbflushall(void); #define userureg(ur) (((ur)->status & MSR_PR) != 0) void watchreset(void); #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) #define KADDR(a) ((void*)((ulong)(a)|KZERO)) #define PADDR(a) ((ulong)(a)&~KZERO) sh); chandevinit(); if(!waserror()){ snprint(buf, sizeof(buf), "power %s mtx", conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "power", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); /*segdescpatch/9/port/ 775 0 0 0 11342263221 157615ustar00cinap_lenrekcinap_lenreksegdescpatch/9/port/sysproc.c 664 0 0 51673 11342263221 17663ustar00cinap_lenrekcinap_lenrek#include "u.h" #include "tos.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "edf.h" #include int shargs(char*, int, char**); extern void checkpages(void); extern void checkpagerefs(void); long sysr1(ulong*) { checkpagerefs(); return 0; } long sysrfork(ulong *arg) { Proc *p; int n, i; Fgrp *ofg; Pgrp *opg; Rgrp *org; Egrp *oeg; ulong pid, flag; Mach *wm; flag = arg[0]; /* Check flags before we commit */ if((flag & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG)) error(Ebadarg); if((flag & (RFNAMEG|RFCNAMEG)) == (RFNAMEG|RFCNAMEG)) error(Ebadarg); if((flag & (RFENVG|RFCENVG)) == (RFENVG|RFCENVG)) error(Ebadarg); if((flag&RFPROC) == 0) { if(flag & (RFMEM|RFNOWAIT)) error(Ebadarg); if(flag & (RFFDG|RFCFDG)) { ofg = up->fgrp; if(flag & RFFDG) up->fgrp = dupfgrp(ofg); else up->fgrp = dupfgrp(nil); closefgrp(ofg); } if(flag & (RFNAMEG|RFCNAMEG)) { opg = up->pgrp; up->pgrp = newpgrp(); if(flag & RFNAMEG) pgrpcpy(up->pgrp, opg); /* inherit noattach */ up->pgrp->noattach = opg->noattach; closepgrp(opg); } if(flag & RFNOMNT) up->pgrp->noattach = 1; if(flag & RFREND) { org = up->rgrp; up->rgrp = newrgrp(); closergrp(org); } if(flag & (RFENVG|RFCENVG)) { oeg = up->egrp; up->egrp = smalloc(sizeof(Egrp)); up->egrp->ref = 1; if(flag & RFENVG) envcpy(up->egrp, oeg); closeegrp(oeg); } if(flag & RFNOTEG) up->noteid = incref(¬eidalloc); return 0; } p = newproc(); p->fpsave = up->fpsave; p->scallnr = up->scallnr; p->s = up->s; p->nerrlab = 0; p->slash = up->slash; p->dot = up->dot; incref(p->dot); memmove(p->note, up->note, sizeof(p->note)); p->privatemem = up->privatemem; p->noswap = up->noswap; p->nnote = up->nnote; p->notified = 0; p->lastnote = up->lastnote; p->notify = up->notify; p->ureg = up->ureg; p->dbgreg = 0; /* Make a new set of memory segments */ n = flag & RFMEM; qlock(&p->seglock); if(waserror()){ qunlock(&p->seglock); nexterror(); } for(i = 0; i < NSEG; i++) if(up->seg[i]) p->seg[i] = dupseg(up->seg, i, n); qunlock(&p->seglock); poperror(); /* File descriptors */ if(flag & (RFFDG|RFCFDG)) { if(flag & RFFDG) p->fgrp = dupfgrp(up->fgrp); else p->fgrp = dupfgrp(nil); } else { p->fgrp = up->fgrp; incref(p->fgrp); } /* Process groups */ if(flag & (RFNAMEG|RFCNAMEG)) { p->pgrp = newpgrp(); if(flag & RFNAMEG) pgrpcpy(p->pgrp, up->pgrp); /* inherit noattach */ p->pgrp->noattach = up->pgrp->noattach; } else { p->pgrp = up->pgrp; incref(p->pgrp); } if(flag & RFNOMNT) up->pgrp->noattach = 1; if(flag & RFREND) p->rgrp = newrgrp(); else { incref(up->rgrp); p->rgrp = up->rgrp; } /* Environment group */ if(flag & (RFENVG|RFCENVG)) { p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; if(flag & RFENVG) envcpy(p->egrp, up->egrp); } else { p->egrp = up->egrp; incref(p->egrp); } p->hang = up->hang; p->procmode = up->procmode; /* Craft a return frame which will cause the child to pop out of * the scheduler in user mode with the return register zero */ forkchild(p, up->dbgreg); p->parent = up; p->parentpid = up->pid; if(flag&RFNOWAIT) p->parentpid = 0; else { lock(&up->exl); up->nchild++; unlock(&up->exl); } if((flag&RFNOTEG) == 0) p->noteid = up->noteid; p->fpstate = up->fpstate; pid = p->pid; memset(p->time, 0, sizeof(p->time)); p->time[TReal] = MACHP(0)->ticks; kstrdup(&p->text, up->text); kstrdup(&p->user, up->user); procfork(p); /* * since the bss/data segments are now shareable, * any mmu info about this process is now stale * (i.e. has bad properties) and has to be discarded. */ flushmmu(); p->basepri = up->basepri; p->priority = up->basepri; p->fixedpri = up->fixedpri; p->mp = up->mp; wm = up->wired; if(wm) procwired(p, wm->machno); ready(p); sched(); return pid; } static ulong l2be(long l) { uchar *cp; cp = (uchar*)&l; return (cp[0]<<24) | (cp[1]<<16) | (cp[2]<<8) | cp[3]; } long sysexec(ulong *arg) { Segment *s, *ts; ulong t, d, b; int i; Chan *tc; char **argv, **argp; char *a, *charp, *args, *file; char *progarg[sizeof(Exec)/2+1], *elem, progelem[64]; ulong ssize, spage, nargs, nbytes, n, bssend; int indir; Exec exec; char line[sizeof(Exec)]; Fgrp *f; Image *img; ulong magic, text, entry, data, bss; Tos *tos; validaddr(arg[0], 1, 0); file = (char*)arg[0]; indir = 0; elem = nil; if(waserror()){ free(elem); nexterror(); } for(;;){ tc = namec(file, Aopen, OEXEC, 0); if(waserror()){ cclose(tc); nexterror(); } if(!indir) kstrdup(&elem, up->genbuf); n = devtab[tc->type]->read(tc, &exec, sizeof(Exec), 0); if(n < 2) error(Ebadexec); magic = l2be(exec.magic); text = l2be(exec.text); entry = l2be(exec.entry); if(n==sizeof(Exec) && (magic == AOUT_MAGIC)){ if(text >= USTKTOP-UTZERO || entry < UTZERO+sizeof(Exec) || entry >= UTZERO+sizeof(Exec)+text) error(Ebadexec); break; /* for binary */ } /* * Process #! /bin/sh args ... */ memmove(line, &exec, sizeof(Exec)); if(indir || line[0]!='#' || line[1]!='!') error(Ebadexec); n = shargs(line, n, progarg); if(n == 0) error(Ebadexec); indir = 1; /* * First arg becomes complete file name */ progarg[n++] = file; progarg[n] = 0; validaddr(arg[1], BY2WD, 1); arg[1] += BY2WD; file = progarg[0]; if(strlen(elem) >= sizeof progelem) error(Ebadexec); strcpy(progelem, elem); progarg[0] = progelem; poperror(); cclose(tc); } data = l2be(exec.data); bss = l2be(exec.bss); t = (UTZERO+sizeof(Exec)+text+(BY2PG-1)) & ~(BY2PG-1); d = (t + data + (BY2PG-1)) & ~(BY2PG-1); bssend = t + data + bss; b = (bssend + (BY2PG-1)) & ~(BY2PG-1); if(t >= KZERO || d >= KZERO || b >= KZERO) error(Ebadexec); /* * Args: pass 1: count */ nbytes = sizeof(Tos); /* hole for profiling clock at top of stack (and more) */ nargs = 0; if(indir){ argp = progarg; while(*argp){ a = *argp++; nbytes += strlen(a) + 1; nargs++; } } evenaddr(arg[1]); argp = (char**)arg[1]; validaddr((ulong)argp, BY2WD, 0); while(*argp){ a = *argp++; if(((ulong)argp&(BY2PG-1)) < BY2WD) validaddr((ulong)argp, BY2WD, 0); validaddr((ulong)a, 1, 0); nbytes += ((char*)vmemchr(a, 0, 0x7FFFFFFF) - a) + 1; nargs++; } ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1)); /* * 8-byte align SP for those (e.g. sparc) that need it. * execregs() will subtract another 4 bytes for argc. */ if((ssize+4) & 7) ssize += 4; spage = (ssize+(BY2PG-1)) >> PGSHIFT; /* * Build the stack segment, putting it in kernel virtual for the moment */ if(spage > TSTKSIZ) error(Enovmem); qlock(&up->seglock); if(waserror()){ qunlock(&up->seglock); nexterror(); } up->seg[ESEG] = newseg(SG_STACK, TSTKTOP-USTKSIZE, USTKSIZE/BY2PG); /* * Args: pass 2: assemble; the pages will be faulted in */ tos = (Tos*)(TSTKTOP - sizeof(Tos)); tos->cyclefreq = m->cyclefreq; cycles((uvlong*)&tos->pcycles); tos->pcycles = -tos->pcycles; tos->kcycles = tos->pcycles; tos->clock = 0; argv = (char**)(TSTKTOP - ssize); charp = (char*)(TSTKTOP - nbytes); args = charp; if(indir) argp = progarg; else argp = (char**)arg[1]; for(i=0; itext); up->text = elem; elem = nil; /* so waserror() won't free elem */ USED(elem); /* copy args; easiest from new process's stack */ n = charp - args; if(n > 128) /* don't waste too much space on huge arg lists */ n = 128; a = up->args; up->args = nil; free(a); up->args = smalloc(n); memmove(up->args, args, n); if(n>0 && up->args[n-1]!='\0'){ /* make sure last arg is NUL-terminated */ /* put NUL at UTF-8 character boundary */ for(i=n-1; i>0; --i) if(fullrune(up->args+i, n-i)) break; up->args[i] = 0; n = i+1; } up->nargs = n; /* * Committed. * Free old memory. * Special segments are maintained across exec */ for(i = SSEG; i <= BSEG; i++) { putseg(up->seg[i]); /* prevent a second free if we have an error */ up->seg[i] = 0; } for(i = BSEG+1; i < NSEG; i++) { s = up->seg[i]; if(s != 0 && (s->type&SG_CEXEC)) { putseg(s); up->seg[i] = 0; } } /* * Close on exec */ f = up->fgrp; for(i=0; i<=f->maxfd; i++) fdclose(i, CCEXEC); /* Text. Shared. Attaches to cache image if possible */ /* attachimage returns a locked cache image */ img = attachimage(SG_TEXT|SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT); ts = img->s; up->seg[TSEG] = ts; ts->flushme = 1; ts->fstart = 0; ts->flen = sizeof(Exec)+text; unlock(img); /* Data. Shared. */ s = newseg(SG_DATA, t, (d-t)>>PGSHIFT); up->seg[DSEG] = s; /* Attached by hand */ incref(img); s->image = img; s->fstart = ts->fstart+ts->flen; s->flen = data; /* BSS. Zero fill on demand */ up->seg[BSEG] = newseg(SG_BSS, d, (b-d)>>PGSHIFT); /* * Move the stack */ s = up->seg[ESEG]; up->seg[ESEG] = 0; up->seg[SSEG] = s; qunlock(&up->seglock); poperror(); /* seglock */ poperror(); /* elem */ s->base = USTKTOP-USTKSIZE; s->top = USTKTOP; relocateseg(s, USTKTOP-TSTKTOP); /* * '/' processes are higher priority (hack to make /ip more responsive). */ if(devtab[tc->type]->dc == L'/') up->basepri = PriRoot; up->priority = up->basepri; poperror(); cclose(tc); /* * At this point, the mmu contains info about the old address * space and needs to be flushed */ flushmmu(); qlock(&up->debug); up->nnote = 0; up->notify = 0; up->notified = 0; up->privatemem = 0; procsetup(up); qunlock(&up->debug); if(up->hang) up->procctl = Proc_stopme; return execregs(entry, ssize, nargs); } int shargs(char *s, int n, char **ap) { int i; s += 2; n -= 2; /* skip #! */ for(i=0; s[i]!='\n'; i++) if(i == n-1) return 0; s[i] = 0; *ap = 0; i = 0; for(;;) { while(*s==' ' || *s=='\t') s++; if(*s == 0) break; i++; *ap++ = s; *ap = 0; while(*s && *s!=' ' && *s!='\t') s++; if(*s == 0) break; else *s++ = 0; } return i; } int return0(void*) { return 0; } long syssleep(ulong *arg) { int n; n = arg[0]; if(n <= 0) { if (up->edf && (up->edf->flags & Admitted)) edfyield(); else yield(); return 0; } if(n < TK2MS(1)) n = TK2MS(1); tsleep(&up->sleep, return0, 0, n); return 0; } long sysalarm(ulong *arg) { return procalarm(arg[0]); } long sysexits(ulong *arg) { char *status; char *inval = "invalid exit string"; char buf[ERRMAX]; status = (char*)arg[0]; if(status){ if(waserror()) status = inval; else{ validaddr((ulong)status, 1, 0); if(vmemchr(status, 0, ERRMAX) == 0){ memmove(buf, status, ERRMAX); buf[ERRMAX-1] = 0; status = buf; } poperror(); } } pexit(status, 1); return 0; /* not reached */ } long sys_wait(ulong *arg) { int pid; Waitmsg w; OWaitmsg *ow; if(arg[0] == 0) return pwait(nil); validaddr(arg[0], sizeof(OWaitmsg), 1); evenaddr(arg[0]); pid = pwait(&w); if(pid >= 0){ ow = (OWaitmsg*)arg[0]; readnum(0, ow->pid, NUMSIZE, w.pid, NUMSIZE); readnum(0, ow->time+TUser*NUMSIZE, NUMSIZE, w.time[TUser], NUMSIZE); readnum(0, ow->time+TSys*NUMSIZE, NUMSIZE, w.time[TSys], NUMSIZE); readnum(0, ow->time+TReal*NUMSIZE, NUMSIZE, w.time[TReal], NUMSIZE); strncpy(ow->msg, w.msg, sizeof(ow->msg)); ow->msg[sizeof(ow->msg)-1] = '\0'; } return pid; } long sysawait(ulong *arg) { int i; int pid; Waitmsg w; ulong n; n = arg[1]; validaddr(arg[0], n, 1); pid = pwait(&w); if(pid < 0) return -1; i = snprint((char*)arg[0], n, "%d %lud %lud %lud %q", w.pid, w.time[TUser], w.time[TSys], w.time[TReal], w.msg); return i; } void werrstr(char *fmt, ...) { va_list va; if(up == nil) return; va_start(va, fmt); vseprint(up->syserrstr, up->syserrstr+ERRMAX, fmt, va); va_end(va); } static long generrstr(char *buf, uint nbuf) { char tmp[ERRMAX]; if(nbuf == 0) error(Ebadarg); validaddr((ulong)buf, nbuf, 1); if(nbuf > sizeof tmp) nbuf = sizeof tmp; memmove(tmp, buf, nbuf); /* make sure it's NUL-terminated */ tmp[nbuf-1] = '\0'; memmove(buf, up->syserrstr, nbuf); buf[nbuf-1] = '\0'; memmove(up->syserrstr, tmp, nbuf); return 0; } long syserrstr(ulong *arg) { return generrstr((char*)arg[0], arg[1]); } /* compatibility for old binaries */ long sys_errstr(ulong *arg) { return generrstr((char*)arg[0], 64); } long sysnotify(ulong *arg) { if(arg[0] != 0) validaddr(arg[0], sizeof(ulong), 0); up->notify = (int(*)(void*, char*))(arg[0]); return 0; } long sysnoted(ulong *arg) { if(arg[0]!=NRSTR && !up->notified) error(Egreg); return 0; } long syssegbrk(ulong *arg) { int i; ulong addr; Segment *s; addr = arg[0]; for(i = 0; i < NSEG; i++) { s = up->seg[i]; if(s == 0 || addr < s->base || addr >= s->top) continue; switch(s->type&SG_TYPE) { case SG_TEXT: case SG_DATA: case SG_STACK: error(Ebadarg); default: return ibrk(arg[1], i); } } error(Ebadarg); return 0; /* not reached */ } long syssegattach(ulong *arg) { return segattach(up, arg[0], (char*)arg[1], arg[2], arg[3]); } long syssegdetach(ulong *arg) { int i; ulong addr; Segment *s; qlock(&up->seglock); if(waserror()){ qunlock(&up->seglock); nexterror(); } s = 0; addr = arg[0]; for(i = 0; i < NSEG; i++) if(s = up->seg[i]) { qlock(&s->lk); if((addr >= s->base && addr < s->top) || (s->top == s->base && addr == s->base)) goto found; qunlock(&s->lk); } error(Ebadarg); found: /* * Check we are not detaching the initial stack segment. */ if(s == up->seg[SSEG]){ qunlock(&s->lk); error(Ebadarg); } up->seg[i] = 0; qunlock(&s->lk); putseg(s); qunlock(&up->seglock); poperror(); /* Ensure we flush any entries from the lost segment */ flushmmu(); return 0; } long syssegfree(ulong *arg) { Segment *s; ulong from, to; from = arg[0]; s = seg(up, from, 1); if(s == nil) error(Ebadarg); to = (from + arg[1]) & ~(BY2PG-1); from = PGROUND(from); if(to > s->top) { qunlock(&s->lk); error(Ebadarg); } mfreeseg(s, from, (to - from) / BY2PG); qunlock(&s->lk); flushmmu(); return 0; } /* For binary compatibility */ long sysbrk_(ulong *arg) { return ibrk(arg[0], BSEG); } long sysrendezvous(ulong *arg) { uintptr tag, val; Proc *p, **l; tag = arg[0]; l = &REND(up->rgrp, tag); up->rendval = ~(uintptr)0; lock(up->rgrp); for(p = *l; p; p = p->rendhash) { if(p->rendtag == tag) { *l = p->rendhash; val = p->rendval; p->rendval = arg[1]; while(p->mach != 0) ; ready(p); unlock(up->rgrp); return val; } l = &p->rendhash; } /* Going to sleep here */ up->rendtag = tag; up->rendval = arg[1]; up->rendhash = *l; *l = up; up->state = Rendezvous; unlock(up->rgrp); sched(); return up->rendval; } /* * The implementation of semaphores is complicated by needing * to avoid rescheduling in syssemrelease, so that it is safe * to call from real-time processes. This means syssemrelease * cannot acquire any qlocks, only spin locks. * * Semacquire and semrelease must both manipulate the semaphore * wait list. Lock-free linked lists only exist in theory, not * in practice, so the wait list is protected by a spin lock. * * The semaphore value *addr is stored in user memory, so it * cannot be read or written while holding spin locks. * * Thus, we can access the list only when holding the lock, and * we can access the semaphore only when not holding the lock. * This makes things interesting. Note that sleep's condition function * is called while holding two locks - r and up->rlock - so it cannot * access the semaphore value either. * * An acquirer announces its intention to try for the semaphore * by putting a Sema structure onto the wait list and then * setting Sema.waiting. After one last check of semaphore, * the acquirer sleeps until Sema.waiting==0. A releaser of n * must wake up n acquirers who have Sema.waiting set. It does * this by clearing Sema.waiting and then calling wakeup. * * There are three interesting races here. * The first is that in this particular sleep/wakeup usage, a single * wakeup can rouse a process from two consecutive sleeps! * The ordering is: * * (a) set Sema.waiting = 1 * (a) call sleep * (b) set Sema.waiting = 0 * (a) check Sema.waiting inside sleep, return w/o sleeping * (a) try for semaphore, fail * (a) set Sema.waiting = 1 * (a) call sleep * (b) call wakeup(a) * (a) wake up again * * This is okay - semacquire will just go around the loop * again. It does mean that at the top of the for(;;) loop in * semacquire, phore.waiting might already be set to 1. * * The second is that a releaser might wake an acquirer who is * interrupted before he can acquire the lock. Since * release(n) issues only n wakeup calls -- only n can be used * anyway -- if the interrupted process is not going to use his * wakeup call he must pass it on to another acquirer. * * The third race is similar to the second but more subtle. An * acquirer sets waiting=1 and then does a final canacquire() * before going to sleep. The opposite order would result in * missing wakeups that happen between canacquire and * waiting=1. (In fact, the whole point of Sema.waiting is to * avoid missing wakeups between canacquire() and sleep().) But * there can be spurious wakeups between a successful * canacquire() and the following semdequeue(). This wakeup is * not useful to the acquirer, since he has already acquired * the semaphore. Like in the previous case, though, the * acquirer must pass the wakeup call along. * * This is all rather subtle. The code below has been verified * with the spin model /sys/src/9/port/semaphore.p. The * original code anticipated the second race but not the first * or third, which were caught only with spin. The first race * is mentioned in /sys/doc/sleep.ps, but I'd forgotten about it. * It was lucky that my abstract model of sleep/wakeup still managed * to preserve that behavior. * * I remain slightly concerned about memory coherence * outside of locks. The spin model does not take * queued processor writes into account so we have to * think hard. The only variables accessed outside locks * are the semaphore value itself and the boolean flag * Sema.waiting. The value is only accessed with cmpswap, * whose job description includes doing the right thing as * far as memory coherence across processors. That leaves * Sema.waiting. To handle it, we call coherence() before each * read and after each write. - rsc */ /* Add semaphore p with addr a to list in seg. */ static void semqueue(Segment *s, long *a, Sema *p) { memset(p, 0, sizeof *p); p->addr = a; lock(&s->sema); /* uses s->sema.Rendez.Lock, but no one else is */ p->next = &s->sema; p->prev = s->sema.prev; p->next->prev = p; p->prev->next = p; unlock(&s->sema); } /* Remove semaphore p from list in seg. */ static void semdequeue(Segment *s, Sema *p) { lock(&s->sema); p->next->prev = p->prev; p->prev->next = p->next; unlock(&s->sema); } /* Wake up n waiters with addr a on list in seg. */ static void semwakeup(Segment *s, long *a, long n) { Sema *p; lock(&s->sema); for(p=s->sema.next; p!=&s->sema && n>0; p=p->next){ if(p->addr == a && p->waiting){ p->waiting = 0; coherence(); wakeup(p); n--; } } unlock(&s->sema); } /* Add delta to semaphore and wake up waiters as appropriate. */ static long semrelease(Segment *s, long *addr, long delta) { long value; do value = *addr; while(!cmpswap(addr, value, value+delta)); semwakeup(s, addr, delta); return value+delta; } /* Try to acquire semaphore using compare-and-swap */ static int canacquire(long *addr) { long value; while((value=*addr) > 0) if(cmpswap(addr, value, value-1)) return 1; return 0; } /* Should we wake up? */ static int semawoke(void *p) { coherence(); return !((Sema*)p)->waiting; } /* Acquire semaphore (subtract 1). */ static int semacquire(Segment *s, long *addr, int block) { int acquired; Sema phore; if(canacquire(addr)) return 1; if(!block) return 0; acquired = 0; semqueue(s, addr, &phore); for(;;){ phore.waiting = 1; coherence(); if(canacquire(addr)){ acquired = 1; break; } if(waserror()) break; sleep(&phore, semawoke, &phore); poperror(); } semdequeue(s, &phore); coherence(); /* not strictly necessary due to lock in semdequeue */ if(!phore.waiting) semwakeup(s, addr, 1); if(!acquired) nexterror(); return 1; } long syssemacquire(ulong *arg) { int block; long *addr; Segment *s; validaddr(arg[0], sizeof(long), 1); evenaddr(arg[0]); addr = (long*)arg[0]; block = arg[1]; if((s = seg(up, (ulong)addr, 0)) == nil) error(Ebadarg); if(*addr < 0) error(Ebadarg); return semacquire(s, addr, block); } long syssemrelease(ulong *arg) { long *addr, delta; Segment *s; validaddr(arg[0], sizeof(long), 1); evenaddr(arg[0]); addr = (long*)arg[0]; delta = arg[1]; if((s = seg(up, (ulong)addr, 0)) == nil) error(Ebadarg); if(delta < 0 || *addr < 0) error(Ebadarg); return semrelease(s, addr, arg[1]); } g), 1); evenaddr(arg[0]); pid = pwait(&w); if(pid >= 0){ ow = (O