| plan 9 kernel history: overview | file list | diff list |
2002/0217/pc/main.c (diff list | history)
| 2002/0217/sys/src/9/pc/main.c:1,740 – 2002/0323/sys/src/9/pc/main.c:1,743 (short | long | prev | next) | ||
| 1991/0702 | #include "u.h" | |
| 1992/0321 | #include "../port/lib.h" | |
| 1991/0706 | #include "mem.h" | |
| 1991/0702 | #include "dat.h" #include "fns.h" | |
| 1991/0706 | #include "io.h" | |
| 1991/0716 | #include "ureg.h" #include "init.h" | |
| 1997/1101 | #include "pool.h" | |
| 2002/0109 | #include "reboot.h" | |
| 1991/0629 | ||
| 1997/0327 | Mach *m; | |
| 1992/0904 | ||
| 1997/0327 | /* * 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?). | |
| 2002/0109 | * There are 3584 bytes available at CONFADDR. | |
| 1997/0327 | */ | |
| 1997/0329 | #define BOOTLINE ((char*)CONFADDR) | |
| 1997/0327 | #define BOOTLINELEN 64 #define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) | |
| 2000/0721 | #define BOOTARGSLEN (4096-0x200-BOOTLINELEN) | |
| 2002/0109 | #define MAXCONF 64 | |
| 1993/0330 | ||
| 2001/0527 | char bootdisk[KNAMELEN]; | |
| 2002/0109 | Conf conf; | |
| 1993/0330 | char *confname[MAXCONF]; char *confval[MAXCONF]; int nconf; | |
| 2002/0109 | uchar *sp; /* user stack of init proc */ | |
| 1993/0330 | ||
| 1997/0327 | static void | |
| 1997/0403 | options(void) | |
| 1997/0327 | { | |
| 1997/0403 | long i, n; char *cp, *line[MAXCONF], *p, *q; | |
| 1997/0327 | ||
| 1997/0403 | /* * 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; | |
| 2001/0527 | n = getfields(cp, line, MAXCONF, 1, "\n"); | |
| 1997/0403 | for(i = 0; i < n; i++){ if(*line[i] == '#') continue; cp = strchr(line[i], '='); | |
| 2001/0527 | if(cp == nil) | |
| 1997/0403 | continue; | |
| 2001/0527 | *cp++ = '\0'; | |
| 1997/0403 | confname[nconf] = line[i]; confval[nconf] = cp; nconf++; } | |
| 1997/0327 | } | |
| 1991/0716 | void | |
| 1991/0702 | main(void) | |
| 1991/0625 | { | |
| 2002/0109 | mach0init(); | |
| 1997/0403 | options(); | |
| 2001/0527 | ioinit(); i8250console(); | |
| 2002/0217 | quotefmtinstall(); | |
| 2002/0109 | ||
| 2001/0527 | print("\nPlan 9\n"); | |
| 2002/0109 | ||
| 1997/0327 | cpuidentify(); | |
| 2001/0527 | screeninit(); | |
| 2002/0109 | meminit(); | |
| 1991/0711 | confinit(); | |
| 1997/0329 | archinit(); | |
| 1992/0625 | xinit(); | |
| 1997/0327 | trapinit(); | |
| 1991/0716 | printinit(); | |
| 1997/0522 | cpuidprint(); | |
| 1997/0327 | mmuinit(); if(arch->intrinit) arch->intrinit(); | |
| 1991/0906 | mathinit(); | |
| 1992/0409 | kbdinit(); | |
| 1997/0327 | if(arch->clockenable) arch->clockenable(); | |
| 1991/0716 | procinit0(); initseg(); | |
| 1995/0329 | links(); | |
| 1997/1101 | conf.monitor = 1; | |
| 1991/0716 | chandevreset(); | |
| 1999/0422 | pageinit(); | |
| 1991/0716 | swapinit(); userinit(); schedinit(); | |
| 1991/0712 | } | |
| 1991/0706 | ||
| 1991/0716 | void | |
| 2002/0109 | mach0init(void) { conf.nmach = 1; MACHP(0) = (Mach*)CPU0MACH; m->pdb = (ulong*)CPU0PDB; machinit(); active.machs = 1; active.exiting = 0; } void | |
| 1991/0716 | machinit(void) | |
| 1991/0712 | { | |
| 1997/0327 | int machno; | |
| 1997/1101 | ulong *pdb; | |
| 1991/0712 | ||
| 1997/0327 | machno = m->machno; pdb = m->pdb; | |
| 1991/0716 | memset(m, 0, sizeof(Mach)); | |
| 1997/0327 | m->machno = machno; m->pdb = pdb; | |
| 2001/0527 | /* * 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; | |
| 1991/0625 | } | |
| 1991/0716 | void init0(void) | |
| 1991/0712 | { | |
| 1993/0330 | int i; | |
| 2002/0109 | char buf[2*KNAMELEN]; | |
| 1992/0918 | ||
| 1993/0915 | up->nerrlab = 0; | |
| 1991/0712 | ||
| 1991/0720 | spllo(); | |
| 1991/0716 | /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ | |
| 1993/0915 | up->slash = namec("#/", Atodir, 0, 0); | |
| 1999/0629 | cnameclose(up->slash->name); up->slash->name = newcname("/"); | |
| 2001/0527 | up->dot = cclone(up->slash); | |
| 1991/0712 | ||
| 1991/0716 | chandevinit(); | |
| 1991/0926 | if(!waserror()){ | |
| 2002/0109 | snprint(buf, sizeof(buf), "%s %s", arch->id, conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "386", 0); | |
| 1997/0329 | if(cpuserver) | |
| 2002/0109 | ksetenv("service", "cpu", 0); | |
| 1997/0329 | else | |
| 2002/0109 | ksetenv("service", "terminal", 0); | |
| 1993/0915 |
| |
| 2002/0323 | for(i = 0; i < nconf; i++){ if(confname[i][0] != '*') ksetenv(confname[i], confval[i], 0); | |
| 2002/0109 | ksetenv(confname[i], confval[i], 1); | |
| 2002/0323 | } | |
| 1991/0926 | poperror(); } | |
| 1993/0915 | kproc("alarm", alarmkproc, 0); | |
| 1992/0323 | touser(sp); | |
| 1991/0712 | } | |
| 1991/0629 | void | |
| 1991/0716 | userinit(void) | |
| 1991/0711 | { | |
| 1991/0716 | Proc *p; Segment *s; KMap *k; | |
| 1992/0323 | Page *pg; | |
| 1991/0905 | ||
| 1991/0716 | p = newproc(); p->pgrp = newpgrp(); | |
| 1992/0625 | p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; | |
| 1997/0606 | p->fgrp = dupfgrp(nil); | |
| 1994/0812 | p->rgrp = newrgrp(); | |
| 1991/1112 | p->procmode = 0640; | |
| 1991/0716 | ||
| 2001/0527 | kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); | |
| 1991/0716 | p->fpstate = FPinit; | |
| 1991/0906 | fpoff(); | |
| 1991/0716 | /* * Kernel Stack | |
| 1991/0717 | * | |
| 2000/1012 | * N.B. make sure there's enough space for syscall to check * for valid args and | |
| 1991/0719 | * 4 bytes for gotolabel's return PC | |
| 1991/0716 | */ p->sched.pc = (ulong)init0; | |
| 2000/1012 | p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); | |
| 1991/0716 | /* * User Stack */ | |
| 1993/0915 | s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); | |
| 1991/0716 | p->seg[SSEG] = s; | |
| 1992/0323 | pg = newpage(1, 0, USTKTOP-BY2PG); segpage(s, pg); k = kmap(pg); bootargs(VA(k)); kunmap(k); | |
| 1991/0716 | /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); | |
| 1993/0915 | s->flushme++; | |
| 1991/0716 | p->seg[TSEG] = s; | |
| 1993/0915 | pg = newpage(1, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); | |
| 1991/0716 | k = kmap(s->map[0]->pages[0]); memmove((ulong*)VA(k), initcode, sizeof initcode); kunmap(k); ready(p); | |
| 1992/0323 | } uchar * pusharg(char *p) { int n; n = strlen(p)+1; sp -= n; memmove(sp, p, n); return sp; } void bootargs(ulong base) { int i, ac; uchar *av[32]; uchar **lsp; | |
| 1992/0903 | char *cp = BOOTLINE; char buf[64]; | |
| 1992/0323 | sp = (uchar*)base + BY2PG - MAXSYSARG*BY2WD; ac = 0; | |
| 1994/0219 | av[ac++] = pusharg("/386/9dos"); | |
| 2002/0109 | /* when boot is changed to only use rc, this code can go away */ | |
| 1997/0327 | cp[BOOTLINELEN-1] = 0; | |
| 1993/0915 | buf[0] = 0; | |
| 2000/0518 | if(strncmp(cp, "fd", 2) == 0){ sprint(buf, "local!#f/fd%lddisk", strtol(cp+2, 0, 0)); | |
| 1992/0903 | av[ac++] = pusharg(buf); | |
| 2000/0518 | } else if(strncmp(cp, "sd", 2) == 0){ sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3)); | |
| 1993/0915 | av[ac++] = pusharg(buf); | |
| 2000/0531 | } else if(strncmp(cp, "ether", 5) == 0) | |
| 1994/1111 | av[ac++] = pusharg("-n"); | |
| 1992/0323 | /* 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; sp += (USTKTOP - BY2PG) - base - sizeof(ulong); | |
| 1991/0711 | } | |
| 1993/0915 | char* getconf(char *name) { int i; for(i = 0; i < nconf; i++) | |
| 1997/0403 | if(cistrcmp(confname[i], name) == 0) | |
| 1993/0915 | return confval[i]; return 0; } | |
| 2002/0109 | 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); } | |
| 1994/1210 | void | |
| 1991/0711 | confinit(void) { | |
| 1997/0403 | char *p; | |
| 1999/1109 | int userpcnt; | |
| 2002/0109 | ulong kpages; | |
| 1991/0711 | ||
| 1997/0403 | if(p = getconf("*kernelpercent")) | |
| 1998/1120 | userpcnt = 100 - strtol(p, 0, 0); | |
| 1997/0403 | else | |
| 1998/1120 | userpcnt = 0; | |
| 1994/0814 | ||
| 1991/0711 | conf.npage = conf.npage0 + conf.npage1; | |
| 1998/1124 | 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; | |
| 1997/1101 | if(cpuserver) { | |
| 1998/1120 | if(userpcnt < 10) userpcnt = 70; kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Hack for the big boys. Only good while physmem < 4GB. | |
| 2000/0609 | * Give the kernel fixed max + enough to allocate the | |
| 1998/1120 | * page pool. * This is an overestimate as conf.upages < conf.npages. | |
| 1998/1124 | * The patch of nimage is a band-aid, scanning the whole * page list in imagereclaim just takes too long. | |
| 1998/1120 | */ | |
| 2001/1011 | if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){ kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG; | |
| 1998/1124 | conf.nimage = 2000; | |
| 1999/0709 | kpages += (conf.nproc*KSTACK)/BY2PG; | |
| 1998/1124 | } | |
| 1997/1101 | } else { | |
| 1998/1120 | if(userpcnt < 10) { | |
| 1997/1101 | if(conf.npage*BY2PG < 16*MB) | |
| 1998/1120 | userpcnt = 40; | |
| 1997/1101 | else | |
| 1998/1120 | userpcnt = 60; | |
| 1997/1101 | } | |
| 1998/1120 | kpages = conf.npage - (conf.npage*userpcnt)/100; | |
| 1998/0420 | /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ | |
| 1997/1101 | if(conf.npage*BY2PG < 16*MB) | |
| 1999/0714 | imagmem->minarena = 4*1024*1024; | |
| 1997/1101 | } | |
| 1998/1120 | conf.upages = conf.npage - kpages; | |
| 1998/1124 | conf.ialloc = (kpages/2)*BY2PG; | |
| 1998/1120 | /* * 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); | |
| 1999/0714 | mainmem->maxsize = kpages; | |
| 1998/1120 | if(!cpuserver){ /* * give terminals lots of image memory, too; the dynamic * allocation will balance the load properly, hopefully. * be careful with 32-bit overflow. */ | |
| 1999/0714 | imagmem->maxsize = kpages; | |
| 1998/1120 | } | |
| 1991/0711 | } | |
| 1998/0910 | static char* mathmsg[] = | |
| 1991/0906 | { | |
| 1991/0913 | "invalid", "denormalized", "div-by-zero", "overflow", "underflow", "precision", "stack", "error", }; | |
| 1991/0906 | ||
| 1998/0916 | static void mathnote(void) { int i; ulong status; | |
| 2001/0527 | char *msg, note[ERRMAX]; | |
| 1998/0916 | status = up->fpsave.status; /* * Some attention should probably be paid here to the * exception masks and error summary. */ msg = "unknown"; for(i = 0; i < 8; i++){ if(!((1<<i) & status)) continue; msg = mathmsg[i]; break; } sprint(note, "sys: fp: %s fppc=0x%lux", msg, up->fpsave.pc); postnote(up, 1, note, NDebug); } | |
| 1991/0906 | /* | |
| 1991/0912 | * math coprocessor error */ | |
| 1998/0910 | static void | |
| 1997/0327 | matherror(Ureg *ur, void*) | |
| 1991/0912 | { | |
| 1991/0913 | /* * a write cycle to port 0xF0 clears the interrupt latch attached * to the error# line from the 387 */ | |
| 1998/0916 | if(!(m->cpuiddx & 0x01)) outb(0xF0, 0xFF); | |
| 1991/0913 | ||
| 1992/0806 | /* * save floating point state to check out error */ | |
| 1993/0915 | fpenv(&up->fpsave); | |
| 1998/0916 | mathnote(); | |
| 1992/0806 | if(ur->pc & KZERO) | |
| 2002/0114 | panic("fp: status %ux fppc=0x%lux pc=0x%lux", | |
| 1998/0916 | up->fpsave.status, up->fpsave.pc, ur->pc); | |
| 1991/0912 | } /* | |
| 1991/0906 | * math coprocessor emulation fault */ | |
| 1998/0910 | static void | |
| 1997/0327 | mathemu(Ureg*, void*) | |
| 1991/0906 | { | |
| 1993/0915 | switch(up->fpstate){ | |
| 1991/0906 | case FPinit: fpinit(); | |
| 1993/0915 | up->fpstate = FPactive; | |
| 1991/0906 | break; case FPinactive: | |
| 1998/0916 | /* * 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; } | |
| 1993/0915 | fprestore(&up->fpsave); up->fpstate = FPactive; | |
| 1991/0906 | break; case FPactive: | |
| 2002/0114 | panic("math emu"); | |
| 1991/0906 | break; } } /* * math coprocessor segment overrun */ | |
| 1998/0910 | static void | |
| 1997/0327 | mathover(Ureg*, void*) | |
| 1991/0906 | { | |
| 1992/0805 | pexit("math overrun", 0); | |
| 1991/0906 | } void mathinit(void) { | |
| 1999/0819 | trapenable(VectorCERR, matherror, 0, "matherror"); | |
| 1997/0327 | if(X86FAMILY(m->cpuidax) == 3) | |
| 1999/0819 | intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror"); trapenable(VectorCNA, mathemu, 0, "mathemu"); trapenable(VectorCSO, mathover, 0, "mathover"); | |
| 1991/0906 | } /* | |
| 1991/0716 | * set up floating point for a new process */ | |
| 1991/0703 | void | |
| 1991/0716 | procsetup(Proc *p) | |
| 1991/0703 | { | |
| 1991/0716 | p->fpstate = FPinit; | |
| 1991/0906 | fpoff(); | |
| 1991/0705 | } | |
| 1991/0712 | /* | |
| 1991/0906 | * Save the mach dependent part of the process state. | |
| 1991/0712 | */ | |
| 1991/0705 | void | |
| 1992/0122 | procsave(Proc *p) | |
| 1991/0705 | { | |
| 1992/0805 | if(p->fpstate == FPactive){ if(p->state == Moribund) fpoff(); | |
| 1998/0916 | 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. */ | |
| 1993/0915 | fpsave(&up->fpsave); | |
| 1998/0916 | } | |
| 1992/0805 | p->fpstate = FPinactive; | |
| 1991/0906 | } | |
| 1998/0605 | /* * 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. */ mmuflushtlb(PADDR(m->pdb)); | |
| 1991/0712 | } | |
| 2002/0109 | static void shutdown(int ispanic) | |
| 1991/0712 | { | |
| 1997/0327 | int ms, once; | |
| 1991/0712 | ||
| 1997/0327 | 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); active.machs &= ~(1<<m->machno); active.exiting = 1; unlock(&active); | |
| 1991/0803 | ||
| 1997/0327 | if(once) print("cpu%d: exiting\n", m->machno); | |
| 1996/01171 | spllo(); | |
| 1997/0327 | 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){ | |
| 1993/1113 | if(cpuserver) | |
| 1993/0915 | delay(10000); else for(;;); | |
| 1997/0327 | } else | |
| 1996/01171 | delay(1000); | |
| 2002/0109 | } | |
| 1992/0903 | ||
| 2002/0109 | 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 */ (*f)(PADDR(entry), PADDR(code), size); } void exit(int ispanic) { shutdown(ispanic); | |
| 1997/0327 | arch->reset(); | |
| 1991/0803 | } | |
| 1991/1210 | int | |
| 1993/0915 | isaconfig(char *class, int ctlrno, ISAConf *isa) { | |
| 2001/0527 | char cc[32], *p; | |
| 2002/0109 | int i; | |
| 1993/0915 | ||
| 2001/0527 | snprint(cc, sizeof cc, "%s%d", class, ctlrno); | |
| 2002/0109 | p = getconf(cc); if(p == nil) 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); | |
| 1993/0915 | } | |
| 2002/0109 | return 1; | |
| 1991/0803 | } | |
| 1995/0222 | 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; } | |
| 1997/0327 | 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; } | |
| 1995/0222 | return 0; } | |
| 2001/0123 | /* * 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(); } | |