/* * Copyright (c) 1988 University of Utah. * Copyright (c) 1992 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department, The Mach Operating System project at * Carnegie-Mellon University, Ralph Campbell, Sony Corp. and Kazumasa * Utashiro of Software Research Associates, Inc. * * %sccs.include.redist.c% * * @(#)machdep.c 7.11 (Berkeley) 05/13/93 */ /* from: Utah $Hdr: machdep.c 1.63 91/04/24$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SYSVSHM #include #endif #include #include #include #include #include #include vm_map_t buffer_map; /* the following is used externally (sysctl_hw) */ char machine[] = "SONY"; /* cpu "architecture" */ char cpu_model[30]; /* * Declare these as initialized data so we can patch them. */ int nswbuf = 0; #ifdef NBUF int nbuf = NBUF; #else int nbuf = 0; #endif #ifdef BUFPAGES int bufpages = BUFPAGES; #else int bufpages = 0; #endif int msgbufmapped = 0; /* set when safe to use msgbuf */ int maxmem; /* max memory per process */ int physmem; /* max supported memory, changes to actual */ /* * safepri is a safe priority for sleep to set for a spin-wait * during autoconfiguration or after a panic. */ int safepri = PSL_LOWIPL; struct user *proc0paddr; struct proc nullproc; /* for use by swtch_exit() */ /* * Do all the stuff that locore normally does before calling main(). * Process arguments passed to us by the prom monitor. * Return the first page address following the system. */ mach_init(x_boothowto, x_unkown, x_bootdev, x_maxmem) int x_boothowto; int x_unkown; int x_bootdev; int x_maxmem; { register char *cp; register int i; register unsigned firstaddr; register caddr_t v; caddr_t start; extern u_long bootdev; extern char edata[], end[]; extern char MachUTLBMiss[], MachUTLBMissEnd[]; extern char MachException[], MachExceptionEnd[]; /* * Save parameters into kernel work area. */ *(int *)(MACH_CACHED_TO_UNCACHED(MACH_MAXMEMSIZE_ADDR)) = x_maxmem; *(int *)(MACH_CACHED_TO_UNCACHED(MACH_BOOTDEV_ADDR)) = x_bootdev; *(int *)(MACH_CACHED_TO_UNCACHED(MACH_BOOTSW_ADDR)) = x_boothowto; /* clear the BSS segment */ v = (caddr_t)pmax_round_page(end); bzero(edata, v - edata); boothowto = x_boothowto; bootdev = x_bootdev; maxmem = physmem = pmax_btop(x_maxmem); /* * Look at arguments passed to us and compute boothowto. */ #ifdef GENERIC boothowto |= RB_SINGLE | RB_ASKNAME; #endif #ifdef KADB boothowto |= RB_KDB; #endif #ifdef MFS /* * Check to see if a mini-root was loaded into memory. It resides * at the start of the next page just after the end of BSS. */ if (boothowto & RB_MINIROOT) { boothowto |= RB_DFLTROOT; v += mfs_initminiroot(v); } #endif /* * Init mapping for u page(s) for proc[0], pm_tlbpid 1. */ start = v; curproc->p_addr = proc0paddr = (struct user *)v; curproc->p_md.md_regs = proc0paddr->u_pcb.pcb_regs; firstaddr = MACH_CACHED_TO_PHYS(v); for (i = 0; i < UPAGES; i++) { MachTLBWriteIndexed(i, (UADDR + (i << PGSHIFT)) | (1 << VMMACH_TLB_PID_SHIFT), curproc->p_md.md_upte[i] = firstaddr | PG_V | PG_M); firstaddr += NBPG; } v += UPAGES * NBPG; MachSetPID(1); /* * init nullproc for swtch_exit(). * init mapping for u page(s), pm_tlbpid 0 * This could be used for an idle process. */ nullproc.p_addr = (struct user *)v; nullproc.p_md.md_regs = nullproc.p_addr->u_pcb.pcb_regs; bcopy("nullproc", nullproc.p_comm, sizeof("nullproc")); for (i = 0; i < UPAGES; i++) { nullproc.p_md.md_upte[i] = firstaddr | PG_V | PG_M; firstaddr += NBPG; } v += UPAGES * NBPG; /* clear pages for u areas */ bzero(start, v - start); /* * Copy down exception vector code. */ if (MachUTLBMissEnd - MachUTLBMiss > 0x80) panic("startup: UTLB code too large"); bcopy(MachUTLBMiss, (char *)MACH_UTLB_MISS_EXC_VEC, MachUTLBMissEnd - MachUTLBMiss); bcopy(MachException, (char *)MACH_GEN_EXC_VEC, MachExceptionEnd - MachException); /* * Clear out the I and D caches. */ MachConfigCache(); MachFlushCache(); /* * Initialize error message buffer (at end of core). */ maxmem -= btoc(sizeof (struct msgbuf)); msgbufp = (struct msgbuf *)(MACH_PHYS_TO_CACHED(maxmem << PGSHIFT)); msgbufmapped = 1; /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * * These data structures are allocated here instead of cpu_startup() * because physical memory is directly addressable. We don't have * to map these into virtual address space. */ start = v; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(cfree, struct cblock, nclist); valloc(callout, struct callout, ncallout); valloc(swapmap, struct map, nswapmap = maxproc * 2); #ifdef SYSVSHM valloc(shmsegs, struct shmid_ds, shminfo.shmmni); #endif /* * Determine how many buffers to allocate. * We allocate more buffer space than the BSD standard of * using 10% of memory for the first 2 Meg, 5% of remaining. * We just allocate a flat 10%. Insure a minimum of 16 buffers. * We allocate 1/2 as many swap buffer headers as file i/o buffers. */ if (bufpages == 0) bufpages = physmem / 10 / CLSIZE; if (nbuf == 0) { nbuf = bufpages; if (nbuf < 16) nbuf = 16; } if (nswbuf == 0) { nswbuf = (nbuf / 2) &~ 1; /* force even */ if (nswbuf > 256) nswbuf = 256; /* sanity */ } valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); /* * Clear allocated memory. */ bzero(start, v - start); /* * Initialize the virtual memory system. */ pmap_bootstrap((vm_offset_t)v); } /* * Console initialization: called early on from main, * before vm init or startup. Do enough configuration * to choose and initialize a console. * XXX need something better here. */ #define SCC_CONSOLE 0 #define SW_CONSOLE 0x07 #define SW_NWB512 0x04 #define SW_NWB225 0x01 #define SW_FBPOP 0x02 #define SW_FBPOP1 0x06 #define SW_FBPOP2 0x03 #define SW_AUTOSEL 0x07 consinit() { extern dev_t consdev; extern struct tty *constty, *cn_tty, *rs_tty; int dipsw = (int)*(volatile u_char *)DIP_SWITCH; #include "bm.h" #if NBM > 0 #if defined(news3200) || defined(news3400) /* KU:XXX */ fbbm_probe(dipsw|2); #else fbbm_probe(dipsw); #endif vt100_open(); setup_fnt(); setup_fnt24(); #else dipsw &= SW_CONSOLE; #endif switch (dipsw & SW_CONSOLE) { case 0: scc_open(SCC_CONSOLE); consdev = makedev(1, 0); constty = rs_tty; break; default: #if NBM > 0 consdev = makedev(22, 0); constty = cn_tty; #endif break; } return(0); } /* * cpu_startup: allocate memory for variable-sized tables, * initialize cpu, and do autoconfiguration. */ cpu_startup() { register unsigned i; register caddr_t v; int base, residual; vm_offset_t minaddr, maxaddr; vm_size_t size; #ifdef DEBUG extern int pmapdebug; int opmapdebug = pmapdebug; pmapdebug = 0; #endif /* * Good {morning,afternoon,evening,night}. */ printf(version); printf("real mem = %d\n", ctob(physmem)); /* * Allocate virtual address space for file I/O buffers. * Note they are different than the array of headers, 'buf', * and usually occupy more virtual memory than physical. */ size = MAXBSIZE * nbuf; buffer_map = kmem_suballoc(kernel_map, (vm_offset_t *)&buffers, &maxaddr, size, FALSE); minaddr = (vm_offset_t)buffers; if (vm_map_find(buffer_map, vm_object_allocate(size), (vm_offset_t)0, &minaddr, size, FALSE) != KERN_SUCCESS) panic("startup: cannot allocate buffers"); base = bufpages / nbuf; residual = bufpages % nbuf; for (i = 0; i < nbuf; i++) { vm_size_t curbufsize; vm_offset_t curbuf; /* * First buffers get (base+1) physical pages * allocated for them. The rest get (base) physical pages. * * The rest of each buffer occupies virtual space, * but has no physical memory allocated for it. */ curbuf = (vm_offset_t)buffers + i * MAXBSIZE; curbufsize = CLBYTES * (i < residual ? base+1 : base); vm_map_pageable(buffer_map, curbuf, curbuf+curbufsize, FALSE); vm_map_simplify(buffer_map, curbuf); } /* * Allocate a submap for exec arguments. This map effectively * limits the number of processes exec'ing at any time. */ exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, 16 * NCARGS, TRUE); /* * Allocate a submap for physio */ phys_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, VM_PHYS_SIZE, TRUE); /* * Finally, allocate mbuf pool. Since mclrefcnt is an off-size * we use the more space efficient malloc in place of kmem_alloc. */ mclrefcnt = (char *)malloc(NMBCLUSTERS+CLBYTES/MCLBYTES, M_MBUF, M_NOWAIT); bzero(mclrefcnt, NMBCLUSTERS+CLBYTES/MCLBYTES); mb_map = kmem_suballoc(kernel_map, (vm_offset_t *)&mbutl, &maxaddr, VM_MBUF_SIZE, FALSE); /* * Initialize callouts */ callfree = callout; for (i = 1; i < ncallout; i++) callout[i-1].c_next = &callout[i]; callout[i-1].c_next = NULL; #ifdef DEBUG pmapdebug = opmapdebug; #endif printf("avail mem = %d\n", ptoa(cnt.v_free_count)); printf("using %d buffers containing %d bytes of memory\n", nbuf, bufpages * CLBYTES); /* * Set up CPU-specific registers, cache, etc. */ initcpu(); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); /* * Configure the system. */ configure(); } /* * Set registers on exec. * Clear all registers except sp, pc. */ setregs(p, entry, retval) register struct proc *p; u_long entry; int retval[2]; { int sp = p->p_md.md_regs[SP]; extern struct proc *machFPCurProcPtr; bzero((caddr_t)p->p_md.md_regs, (FSR + 1) * sizeof(int)); p->p_md.md_regs[SP] = sp; p->p_md.md_regs[PC] = entry & ~3; p->p_md.md_regs[PS] = PSL_USERSET; p->p_md.md_flags & ~MDP_FPUSED; if (machFPCurProcPtr == p) machFPCurProcPtr = (struct proc *)0; } /* * WARNING: code in locore.s assumes the layout shown for sf_signum * thru sf_handler so... don't screw with them! */ struct sigframe { int sf_signum; /* signo for handler */ int sf_code; /* additional info for handler */ struct sigcontext *sf_scp; /* context ptr for handler */ sig_t sf_handler; /* handler addr for u_sigc */ struct sigcontext sf_sc; /* actual context */ }; #ifdef DEBUG int sigdebug = 0; int sigpid = 0; #define SDB_FOLLOW 0x01 #define SDB_KSTACK 0x02 #define SDB_FPSTATE 0x04 #endif /* * Send an interrupt to process. */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig, mask; unsigned code; { register struct proc *p = curproc; register struct sigframe *fp; register int *regs; register struct sigacts *psp = p->p_sigacts; int oonstack, fsize; struct sigcontext ksc; extern char sigcode[], esigcode[]; regs = p->p_md.md_regs; oonstack = psp->ps_sigstk.ss_flags & SA_ONSTACK; /* * Allocate and validate space for the signal handler * context. Note that if the stack is in data space, the * call to grow() is a nop, and the copyout() * will fail if the process has not already allocated * the space with a `brk'. */ fsize = sizeof(struct sigframe); if ((psp->ps_flags & SAS_ALTSTACK) && (psp->ps_sigstk.ss_flags & SA_ONSTACK) == 0 && (psp->ps_sigonstack & sigmask(sig))) { fp = (struct sigframe *)(psp->ps_sigstk.ss_base + psp->ps_sigstk.ss_size - fsize); psp->ps_sigstk.ss_flags |= SA_ONSTACK; } else fp = (struct sigframe *)(regs[SP] - fsize); if ((unsigned)fp <= USRSTACK - ctob(p->p_vmspace->vm_ssize)) (void)grow(p, (unsigned)fp); #ifdef DEBUG if ((sigdebug & SDB_FOLLOW) || (sigdebug & SDB_KSTACK) && p->p_pid == sigpid) printf("sendsig(%d): sig %d ssp %x usp %x scp %x\n", p->p_pid, sig, &oonstack, fp, &fp->sf_sc); #endif /* * Build the signal context to be used by sigreturn. */ ksc.sc_onstack = oonstack; ksc.sc_mask = mask; ksc.sc_pc = regs[PC]; ksc.sc_regs[ZERO] = 0xACEDBADE; /* magic number */ bcopy((caddr_t)®s[1], (caddr_t)&ksc.sc_regs[1], sizeof(ksc.sc_regs) - sizeof(int)); ksc.sc_fpused = p->p_md.md_flags & MDP_FPUSED; if (ksc.sc_fpused) { extern struct proc *machFPCurProcPtr; /* if FPU has current state, save it first */ if (p == machFPCurProcPtr) MachSaveCurFPState(p); bcopy((caddr_t)&p->p_md.md_regs[F0], (caddr_t)ksc.sc_fpregs, sizeof(ksc.sc_fpregs)); } if (copyout((caddr_t)&ksc, (caddr_t)&fp->sf_sc, sizeof(ksc))) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ SIGACTION(p, SIGILL) = SIG_DFL; sig = sigmask(SIGILL); p->p_sigignore &= ~sig; p->p_sigcatch &= ~sig; p->p_sigmask &= ~sig; psignal(p, SIGILL); return; } /* * Build the argument list for the signal handler. */ regs[A0] = sig; regs[A1] = code; regs[A2] = (int)&fp->sf_sc; regs[A3] = (int)catcher; regs[PC] = (int)catcher; regs[SP] = (int)fp; /* * Signal trampoline code is at base of user stack. */ regs[RA] = (int)PS_STRINGS - (esigcode - sigcode); #ifdef DEBUG if ((sigdebug & SDB_FOLLOW) || (sigdebug & SDB_KSTACK) && p->p_pid == sigpid) printf("sendsig(%d): sig %d returns\n", p->p_pid, sig); #endif } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * psl to gain improper priviledges or to cause * a machine fault. */ struct sigreturn_args { struct sigcontext *sigcntxp; }; /* ARGSUSED */ sigreturn(p, uap, retval) struct proc *p; struct sigreturn_args *uap; int *retval; { register struct sigcontext *scp; register int *regs; struct sigcontext ksc; int error; scp = uap->sigcntxp; #ifdef DEBUG if (sigdebug & SDB_FOLLOW) printf("sigreturn: pid %d, scp %x\n", p->p_pid, scp); #endif regs = p->p_md.md_regs; /* * Test and fetch the context structure. * We grab it all at once for speed. */ error = copyin((caddr_t)scp, (caddr_t)&ksc, sizeof(ksc)); if (error || ksc.sc_regs[ZERO] != 0xACEDBADE) { #ifdef DEBUG if (!(sigdebug & SDB_FOLLOW)) printf("sigreturn: pid %d, scp %x\n", p->p_pid, scp); printf(" old sp %x ra %x pc %x\n", regs[SP], regs[RA], regs[PC]); printf(" new sp %x ra %x pc %x err %d z %x\n", ksc.sc_regs[SP], ksc.sc_regs[RA], ksc.sc_regs[PC], error, ksc.sc_regs[ZERO]); #endif return (EINVAL); } scp = &ksc; /* * Restore the user supplied information */ if (scp->sc_onstack & 01) p->p_sigacts->ps_sigstk.ss_flags |= SA_ONSTACK; else p->p_sigacts->ps_sigstk.ss_flags &= ~SA_ONSTACK; p->p_sigmask = scp->sc_mask &~ sigcantmask; regs[PC] = scp->sc_pc; bcopy((caddr_t)&scp->sc_regs[1], (caddr_t)®s[1], sizeof(scp->sc_regs) - sizeof(int)); if (scp->sc_fpused) bcopy((caddr_t)scp->sc_fpregs, (caddr_t)&p->p_md.md_regs[F0], sizeof(scp->sc_fpregs)); return (EJUSTRETURN); } int waittime = -1; boot(howto) register int howto; { /* take a snap shot before clobbering any registers */ if (curproc) savectx(curproc->p_addr, 0); #ifdef DEBUG if (panicstr) traceback(); #endif boothowto = howto; if ((howto & RB_NOSYNC) == 0 && waittime < 0) { register struct buf *bp; int iter, nbusy; waittime = 0; (void) spl0(); printf("syncing disks... "); /* * Release vnodes held by texts before sync. */ if (panicstr == 0) vnode_pager_umount(NULL); #ifdef notyet #include "fd.h" #if NFD > 0 fdshutdown(); #endif #endif sync(&proc0, (void *)NULL, (int *)NULL); for (iter = 0; iter < 20; iter++) { nbusy = 0; for (bp = &buf[nbuf]; --bp >= buf; ) if ((bp->b_flags & (B_BUSY|B_INVAL)) == B_BUSY) nbusy++; if (nbusy == 0) break; printf("%d ", nbusy); DELAY(40000 * iter); } if (nbusy) printf("giving up\n"); else printf("done\n"); /* * If we've been adjusting the clock, the todr * will be out of synch; adjust it now. */ resettodr(); } (void) splhigh(); /* extreme priority */ if (howto & RB_HALT) { halt(howto); /*NOTREACHED*/ } else { if (howto & RB_DUMP) dumpsys(); halt(howto); /*NOTREACHED*/ } /*NOTREACHED*/ } halt(howto) int howto; { if (*(volatile u_char *)DIP_SWITCH & 0x20) howto |= RB_HALT; to_monitor(howto); /*NOTREACHED*/ } int dumpmag = 0x8fca0101; /* magic number for savecore */ int dumpsize = 0; /* also for savecore */ long dumplo = 0; dumpconf() { int nblks; dumpsize = physmem; if (dumpdev != NODEV && bdevsw[major(dumpdev)].d_psize) { nblks = (*bdevsw[major(dumpdev)].d_psize)(dumpdev); if (dumpsize > btoc(dbtob(nblks - dumplo))) dumpsize = btoc(dbtob(nblks - dumplo)); else if (dumplo == 0) dumplo = nblks - btodb(ctob(physmem)); } /* * Don't dump on the first CLBYTES (why CLBYTES?) * in case the dump device includes a disk label. */ if (dumplo < btodb(CLBYTES)) dumplo = btodb(CLBYTES); } /* * Doadump comes here after turning off memory management and * getting on the dump stack, either when called above, or by * the auto-restart code. */ dumpsys() { int error; msgbufmapped = 0; if (dumpdev == NODEV) return; /* * For dumps during autoconfiguration, * if dump device has already configured... */ if (dumpsize == 0) dumpconf(); if (dumplo < 0) return; printf("\ndumping to dev %x, offset %d\n", dumpdev, dumplo); printf("dump "); switch (error = (*bdevsw[major(dumpdev)].d_dump)(dumpdev)) { case ENXIO: printf("device bad\n"); break; case EFAULT: printf("device not ready\n"); break; case EINVAL: printf("area improper\n"); break; case EIO: printf("i/o error\n"); break; default: printf("error %d\n", error); break; case 0: printf("succeeded\n"); } } /* * Return the best possible estimate of the time in the timeval * to which tvp points. Unfortunately, we can't read the hardware registers. * We guarantee that the time will be greater than the value obtained by a * previous call. */ microtime(tvp) register struct timeval *tvp; { int s = splclock(); static struct timeval lasttime; *tvp = time; #ifdef notdef tvp->tv_usec += clkread(); while (tvp->tv_usec > 1000000) { tvp->tv_sec++; tvp->tv_usec -= 1000000; } #endif if (tvp->tv_sec == lasttime.tv_sec && tvp->tv_usec <= lasttime.tv_usec && (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) { tvp->tv_sec++; tvp->tv_usec -= 1000000; } lasttime = *tvp; splx(s); } initcpu() { /* * clear LEDs */ *(char*)DEBUG_PORT = (char)DP_WRITE|DP_LED0|DP_LED1|DP_LED2|DP_LED3; /* * clear all interrupts */ *(char*)INTCLR0 = 0; *(char*)INTCLR1 = 0; /* * It's not a time to enable timer yet. * * INTEN0: PERR ABORT BERR TIMER KBD MS CFLT CBSY * o o o x o o x x * INTEN1: BEEP SCC LANCE DMA SLOT1 SLOT3 EXT1 EXT3 * x o o o o o x x */ *(char*)INTEN0 = (char) INTEN0_PERR|INTEN0_ABORT|INTEN0_BERR| INTEN0_KBDINT|INTEN0_MSINT; *(char*)INTEN1 = (char) INTEN1_SCC|INTEN1_LANCE|INTEN1_DMA| INTEN1_SLOT1|INTEN1_SLOT3; spl0(); /* safe to turn interrupts on now */ } /* * Convert an ASCII string into an integer. */ int atoi(s) char *s; { int c; unsigned base = 10, d; int neg = 0, val = 0; if (s == 0 || (c = *s++) == 0) goto out; /* skip spaces if any */ while (c == ' ' || c == '\t') c = *s++; /* parse sign, allow more than one (compat) */ while (c == '-') { neg = !neg; c = *s++; } /* parse base specification, if any */ if (c == '0') { c = *s++; switch (c) { case 'X': case 'x': base = 16; break; case 'B': case 'b': base = 2; break; default: base = 8; break; } } /* parse number proper */ for (;;) { if (c >= '0' && c <= '9') d = c - '0'; else if (c >= 'a' && c <= 'z') d = c - 'a' + 10; else if (c >= 'A' && c <= 'Z') d = c - 'A' + 10; else break; val *= base; val += d; c = *s++; } if (neg) val = -val; out: return val; } #ifdef CPU_SINGLE /* * small ring buffers for keyboard/mouse */ struct ring_buf { u_char head; u_char tail; u_char count; u_char buf[13]; } ring_buf[2]; xputc(c, chan) u_char c; int chan; { register struct ring_buf *p = &ring_buf[chan]; int s = splhigh(); if (p->count >= sizeof (p->buf)) { (void) splx(s); return (-1); } p->buf[p->head] = c; if (++p->head >= sizeof (p->buf)) p->head = 0; p->count++; (void) splx(s); return (c); } xgetc(chan) int chan; { register struct ring_buf *p = &ring_buf[chan]; int c; int s = splhigh(); if (p->count == 0) { (void) splx(s); return (-1); } c = p->buf[p->tail]; if (++p->tail >= sizeof (p->buf)) p->tail = 0; p->count--; (void) splx(s); return (c); } #endif /* CPU_SINGLE */