/* Copyright (C) 2015-2018 Giacomo Tesio * See /doc/license/gpl-2.0.txt for details about the licensing. */ /* Copyright (C) Charles Forsyth * See /doc/license/NOTICE.Plan9-9k.txt for details about the licensing. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "ureg.h" #include "pool.h" #include "envvars.h" #include "io.h" #include "apic.h" static uintptr_t sp; /* XXX - must go - user stack of init proc */ Sys system; Sys* sys; usize sizeofSys = sizeof(Sys); /* * Option arguments from the command line. * oargv[0] is the boot file. * Optionsinit() is called from multiboot() to * set it all up. */ static int64_t oargc; static char* oargv[64]; static char oargb[1024]; static int oargblen; static int maxcores = 1024; /* max # of cores given as an argument */ static int numtcs = 32; /* initial # of TCs */ IOConf ioconf; int procmax; char dbgflg[256]; static int vflag = 1; void optionsinit(char* s) { oargblen = jehanne_strecpy(oargb, oargb+sizeof(oargb), s) - oargb; oargc = jehanne_tokenize(oargb, oargv, nelem(oargv)-1); oargv[oargc] = nil; } char* getconf(char *name) { int i; char *a; for(i = 0; i < oargc; i++){ a = strstr(oargv[i], name); if(a == oargv[i]){ a += strlen(name); if(a[0] == '=') return ++a; } } return nil; } int isaconfig(char *class, int ctlrno, ISAConf *isa) { return 0; } static void options(int argc, char* argv[]) { char *p; char *env[2]; int n, o; char envcopy[256]; /* * Process flags. * Flags [A-Za-z] may be optionally followed by * an integer level between 1 and 127 inclusive * (no space between flag and level). * '--' ends flag processing. */ while(--argc > 0){ char* next = *++argv; if(next[0] =='-' && next[1] != '-'){ while(o = *++argv[0]){ if(!(o >= 'A' && o <= 'Z') && !(o >= 'a' && o <= 'z')) continue; n = jehanne_strtol(argv[0]+1, &p, 0); if(p == argv[0]+1 || n < 1 || n > 127) n = 1; argv[0] = p-1; dbgflg[o] = n; } }else if(jehanne_strcmp(next, "waitgdb") == 0){ waitdebugger(); }else if(jehanne_strcmp(next, "idlespin") == 0){ onIdleSpin(); }else{ jehanne_strncpy(envcopy, next, sizeof(envcopy)-1); jehanne_gettokens(envcopy, env, 2, "="); if(jehanne_strcmp(env[0], "maxcores") == 0){ maxcores = jehanne_strtol(env[1], 0, 0); } if(jehanne_strcmp(env[0], "numtcs") == 0){ numtcs = jehanne_strtol(env[1], 0, 0); } } } vflag = dbgflg['v']; } void loadenv(int argc, char* argv[]) { char *env[2]; /* * Process command line env options */ while(--argc > 0){ char* next = *++argv; if(next[0] !='-'){ if (jehanne_gettokens(next, env, 2, "=") == 2){; ksetenv(env[0], env[1], 1); }else{ jehanne_print("Ignoring parameter with no value: %s\n", env[0]); } } } } void initialize_processor(void) { int machno; Segdesc *gdt; uintptr_t *pml4; machno = m->machno; pml4 = m->pml4; gdt = m->gdt; memset(m, 0, sizeof(Mach)); m->machno = machno; m->pml4 = pml4; 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; } static void initialize_boot_processor(void) { system.nmach = 1; system.machptr[0] = m; /* m was set by entry.S */ m->machno = 0; m->pml4 = (uint64_t*)CPU0PML4; m->gdt = (Segdesc*)CPU0GDT; initialize_processor(); active.machs[0] = 1; m->online = 1; active.exiting = 0; } static void intialize_system(void) { extern Ureg _boot_registers; uintptr_t p; p = (uintptr_t)&_boot_registers; p += KZERO; sys = &system; sys->boot_regs = (void*)p; sys->architecture = "amd64"; } static void configure_kernel(void) { char *p; int i, userpcnt; unsigned int kpages; if(p = getconf(ENV_SERVICE)){ if(strcmp(p, "cpu") == 0) cpuserver = 1; else if(strcmp(p,"terminal") == 0) cpuserver = 0; } /* memory */ if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; sys->npages = 0; for(i=0; imem); i++) sys->npages += sys->mem[i].npage; /* processes */ p = getconf("*procmax"); if(p != nil) sys->nproc = jehanne_strtoull(p, nil, 0); if(sys->nproc == 0){ sys->nproc = 100 + ((sys->npages*PGSZ)/MiB)*5; if(cpuserver) sys->nproc *= 3; } if(sys->nproc > 2046){ /* devproc supports at most (2^11)-2 processes */ sys->nproc = 2046; } sys->nimage = 256; if(cpuserver) { if(userpcnt < 10) userpcnt = 70; kpages = sys->npages - (sys->npages*userpcnt)/100; sys->nimage = sys->nproc; } else { if(userpcnt < 10) { if(sys->npages*PGSZ < 16*MiB) userpcnt = 50; else userpcnt = 60; } kpages = sys->npages - (sys->npages*userpcnt)/100; /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ if(sys->npages*PGSZ < 16*MiB) imagmem->minarena = 4*MiB; } /* * can't go past the end of virtual memory. */ if(kpages > ((uintptr_t)-KZERO)/PGSZ) kpages = ((uintptr_t)-KZERO)/PGSZ; sys->upages = sys->npages - kpages; sys->ialloc = (kpages/2)*PGSZ; /* * Guess how much is taken by the large permanent * datastructures. Mntcache and Mntrpc are not accounted for. */ kpages *= PGSZ; kpages -= sys->nproc*sizeof(Proc); mainmem->maxsize = kpages; /* * the dynamic allocation will balance the load properly, * hopefully. be careful with 32-bit overflow. */ imagmem->maxsize = kpages - (kpages/10); if(p = getconf("*imagemaxmb")){ imagmem->maxsize = strtol(p, nil, 0)*MiB; if(imagmem->maxsize > mainmem->maxsize) imagmem->maxsize = mainmem->maxsize; } } void main(void) { intialize_system(); initialize_boot_processor(); multiboot(0); options(oargc, oargv); ioinit(); i8250console(); fmtinit(); screen_init(); jehanne_print("\nJehanne Operating System\n"); trapinit0(); // i8259init(); i8253init(); cpuidentify(); meminit(); configure_kernel(); xinit(); archinit(); // bootscreeninit(); // if(i8237alloc != nil) // i8237alloc(); trapinit(); printinit(); cpuidprint(); mmuinit(); if(arch->intrinit) arch->intrinit(); timersinit(); // keybinit(); // keybenable(); // mouseenable(); mathinit(); if(arch->clockenable) arch->clockenable(); psinit(sys->nproc); links(); devtabreset(); umem_init(); userinit(); schedinit(); } void init0(void) { 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); devtabinit(); if(!waserror()){ jehanne_snprint(buf, sizeof(buf), "%s %s", "AMD64", conffile); loadenv(oargc, oargv); ksetenv("terminal", buf, 0); ksetenv(ENV_CPUTYPE, "amd64", 0); if(cpuserver) ksetenv(ENV_SERVICE, "cpu", 0); else ksetenv(ENV_SERVICE, "terminal", 0); poperror(); } kproc("alarm", alarmkproc, 0); kproc("atimer", awake_timer, 0); kproc("aringer", awake_ringer, 0); touser(sp); } void bootargs(uintptr_t base) { int i; uint32_t ssize; char **av, *p; /* * Push the boot args onto the stack. * Make sure the validaddr check in syscall won't fail * because there are fewer than the maximum number of * args by subtracting sizeof(up->arg). */ i = oargblen+1; p = UINT2PTR(STACKALIGN(base + PGSZ - sizeof(up->arg) - i)); jehanne_memmove(p, oargb, i); /* * Now push argc and the argv pointers. * This isn't strictly correct as the code jumped to by * touser in init9.[cs] calls startboot (port/initcode.c) which * expects arguments * startboot(char* argv0, char* argv[]) * not the usual (int argc, char* argv[]), but argv0 is * unused so it doesn't matter (at the moment...). */ av = (char**)(p - (oargc+2)*sizeof(char*)); ssize = base + PGSZ - PTR2UINT(av); *av++ = (char*)oargc; for(i = 0; i < oargc; i++) *av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - PGSZ); *av = nil; sp = USTKTOP - ssize; } void userinit(void) { Proc *p; ProcSegment *s; PagePointer page; char *k; uintptr_t va, mmuphys; /* NOTE: we use the global pointer up so that the kaddr() * (called by segment_fault) can find it */ up = newproc(); up->pgrp = newpgrp(); up->egrp = smalloc(sizeof(Egrp)); up->egrp->r.ref = 1; up->fgrp = dupfgrp(nil); up->rgrp = newrgrp(); up->procmode = 0640; kstrdup(&eve, ""); kstrdup(&up->text, "*init*"); kstrdup(&up->user, eve); sysprocsetup(up); /* * Kernel Stack * * N.B. make sure there's enough space for syscall to check * for valid args and * space for gotolabel's return PC * AMD64 stack must be quad-aligned. */ up->sched.pc = PTR2UINT(init0); up->sched.sp = PTR2UINT(up->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr_t)); up->sched.sp = STACKALIGN(up->sched.sp); /* * User Stack */ s = nil; if(!segment_virtual(&s, SgStack, SgRead|SgWrite, 0, USTKTOP-USTKSIZE, USTKTOP)) panic("userinit: cannot create stack segment"); up->seg[SSEG] = s; va = USTKTOP-USTKSIZE; mmuphys = 0; if(!segment_fault(&mmuphys, &va, s, FaultWrite)) panic("userinit: cannot allocate first stack page"); page = segment_page(s, va); if(page == 0) panic("userinit: cannot find first stack page (previously faulted)"); k = page_kmap(page); bootargs(PTR2UINT(k)); page_kunmap(page, &k); /* * Text */ s = nil; if(!segment_userinit(&s, 0)) panic("userinit: cannot create text segment"); up->seg[TSEG] = s; /* * Data */ s = nil; if(!segment_userinit(&s, 1)) panic("userinit: cannot create data segment"); up->seg[DSEG] = s; /* reset global pointer up */ p = up; up = nil; ready(p); } static void fullstop(void) { splhi(); // lapicpri(0xff); /* i8259 was initialised as disabled */ for(;;) _halt(); } static void shutdown(int ispanic) { int ms; if(!m->online) fullstop(); active.ispanic = ispanic; m->online = 0; active.exiting = 1; adec((int*)&sys->nmach); iprint("cpu%d: exiting\n", m->machno); /* wait for any other processors to shutdown */ //spllo(); prflush(); for(ms = 10*1000; ms > 0; ms -= 2){ delay(2); if(sys->nmach == 0 && consactive() == 0) break; } if(active.ispanic){ if(!cpuserver || getconf("*debug") || 1) fullstop(); delay(10000); } else delay(1000); } void reboot(void* _1, void* _2, long _3) { panic("reboot\n"); } void exit(int ispanic) { shutdown(ispanic); archreset(); }