diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/pc/main.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/pc/main.c')
-rwxr-xr-x | sys/src/9/pc/main.c | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/sys/src/9/pc/main.c b/sys/src/9/pc/main.c new file mode 100755 index 000000000..f1e1fa20d --- /dev/null +++ b/sys/src/9/pc/main.c @@ -0,0 +1,842 @@ +#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<nelem(conf.mem); i++) + conf.npage += conf.mem[i].npage; + + 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; + + 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; + 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; + } +} + +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<<i) & status)) + continue; + msg = mathmsg[i]; + break; + } + if(status & 0x01){ + if(status & 0x40){ + if(status & 0x200) + msg = "stack overflow"; + else + msg = "stack underflow"; + }else + msg = "invalid operation"; + } + snprint(note, sizeof note, "sys: fp: %s fppc=0x%lux status=0x%lux", + msg, up->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"); +} + +/* + * set up floating point for a new process + */ +void +procsetup(Proc*p) +{ + p->fpstate = FPinit; + fpoff(); +} + +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)); +} + +static void +shutdown(int ispanic) +{ + int ms, once; + + lock(&active); + if(ispanic) + active.ispanic = ispanic; + else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0) + active.ispanic = 0; + once = active.machs & (1<<m->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<<m->machno); + active.exiting = 1; + unlock(&active); + + 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)); + if(active.machs == 0 && consactive() == 0) + break; + } + + if(active.ispanic){ + if(!cpuserver) + for(;;) + halt(); + if(getconf("*debug")) + delay(5*60*1000); + else + delay(10000); + }else + delay(1000); +} + +void +reboot(void *entry, void *code, ulong size) +{ + void (*f)(ulong, ulong, ulong); + ulong *pdb; + + 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); + + splhi(); + + /* turn off buffered serial console */ + serialoq = nil; + + /* shutdown devices */ + chandevshutdown(); + arch->introff(); + + /* + * 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(); +} |