123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- #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 */
- 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++;
- }
- }
- void
- main(void)
- {
- mach0init();
- options();
- ioinit();
- i8250console();
- quotefmtinstall();
- screeninit();
- print("\nPlan 9\n");
- kbdinit();
- i8253init();
- cpuidentify();
- meminit();
- confinit();
- archinit();
- xinit();
- 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();
- 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);
- cnameclose(up->slash->name);
- up->slash->name = newcname("/");
- 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)
- {
- 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;
- 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
- */
- 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);
- 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);
- }
- 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;
- 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) - base);
- *lsp = 0;
- sp += (USTKTOP - BY2PG) - 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 userpcnt;
- ulong kpages;
- if(p = getconf("*kernelpercent"))
- userpcnt = 100 - strtol(p, 0, 0);
- else
- userpcnt = 0;
- conf.npage = conf.npage0 + conf.npage1;
- 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;
- }
- 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 & 0x30){
- if(status & 0x200)
- msg = "stack overflow";
- else
- msg = "stack underflow";
- }else
- msg = "invalid operation";
- }
- sprint(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 & 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*, 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");
- 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)
- fpoff();
- 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(&up->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);
- active.machs &= ~(1<<m->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(;;)
- halt();
- }
- 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 */
- (*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();
- }
|