e4651682d6403b470567538b713ceb7886d6622f
[unix-history] / usr / src / sys / sparc / sparc / machdep.c
/*
* Copyright (c) 1992 The Regents of the University of California.
* All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratories.
*
* %sccs.include.redist.c%
*
* @(#)machdep.c 7.2 (Berkeley) %G%
*
* from: $Header: machdep.c,v 1.32 92/07/13 01:41:14 torek Exp $
*/
#include "param.h"
#include "proc.h"
#include "user.h"
#include "map.h"
#include "buf.h"
#include "device.h"
#include "reboot.h"
#include "systm.h"
#include "conf.h"
#include "file.h"
#include "clist.h"
#include "callout.h"
#include "malloc.h"
#include "mbuf.h"
#include "mount.h"
#include "msgbuf.h"
#ifdef SYSVSHM
#include "shm.h"
#endif
#include "exec.h"
#include "machine/autoconf.h"
#include "machine/frame.h"
#include "machine/cpu.h"
#include "vm/vm_kern.h"
#include "vm/vm_page.h"
#include "asm.h"
#include "cache.h"
#include "vaddrs.h"
vm_map_t buffer_map;
extern vm_offset_t avail_end;
/*
* 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 physmem;
extern struct msgbuf msgbuf;
struct msgbuf *msgbufp = &msgbuf;
int msgbufmapped = 1; /* message buffer is always mapped */
/*
* safepri is a safe priority for sleep to set for a spin-wait
* during autoconfiguration or after a panic.
*/
int safepri = 0;
caddr_t allocsys();
/*
* Machine-dependent startup code
*/
cpu_startup()
{
register unsigned i;
register caddr_t v;
register int sz;
int base, residual;
#ifdef DEBUG
extern int pmapdebug;
int opmapdebug = pmapdebug;
#endif
vm_offset_t minaddr, maxaddr;
vm_size_t size;
#ifdef DEBUG
pmapdebug = 0;
#endif
/*
* Good {morning,afternoon,evening,night}.
*/
printf(version);
/*identifycpu();*/
physmem = btoc(avail_end);
printf("real mem = %d\n", avail_end);
/*
* Find out how much space we need, allocate it,
* and then give everything true virtual addresses.
*/
sz = (int)allocsys((caddr_t)0);
if ((v = (caddr_t)kmem_alloc(kernel_map, round_page(sz))) == 0)
panic("startup: no room for tables");
if (allocsys(v) - v != sz)
panic("startup: table size inconsistency");
/*
* Now allocate buffers proper. They are different than the above
* in that they 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 <residual> 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 map for physio. Others use a submap of the kernel
* map, but we want one completely separate, even though it uses
* the same pmap.
*/
phys_map = vm_map_create(kernel_pmap, DVMA_BASE, DVMA_END, 1);
if (phys_map == NULL)
panic("unable to create DVMA map");
/*
* 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 buffers, so they can be used to read disk labels.
*/
bufinit();
/*
* Configure the system.
*/
configure();
/*
* Turn on the cache (do after configuration due to a bug in
* some versions of the SPARC chips -- this info from Gilmore).
*/
cache_enable();
}
/*
* Allocate space for system data structures. We are given
* a starting virtual address and we return a final virtual
* address; along the way we set each data structure pointer.
*
* You call allocsys() with 0 to find out how much space we want,
* allocate that much and fill it with zeroes, and then call
* allocsys() again with the correct base virtual address.
*/
caddr_t
allocsys(v)
register caddr_t v;
{
#define valloc(name, type, num) \
v = (caddr_t)(((name) = (type *)v) + (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 (enough to
* hold 5% of total physical memory, but at least 16).
* Allocate 1/2 as many swap buffer headers as file i/o buffers.
*/
if (bufpages == 0)
bufpages = (physmem / 20) / 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);
return (v);
}
/*
* Set up registers on exec.
*
* XXX this entire mess must be fixed
*/
/* ARGSUSED */
setregs(p, entry, retval)
register struct proc *p;
u_long entry;
int retval[2];
{
register struct trapframe *tf = p->p_md.md_tf;
register struct fpstate *fs;
register int psr, sp;
/*
* The syscall will ``return'' to npc or %g7 or %g2; set them all.
* Set the rest of the registers to 0 except for %o6 (stack pointer,
* built in exec()) and psr (retain CWP and PSR_S bits).
*/
psr = tf->tf_psr & (PSR_S | PSR_CWP);
sp = tf->tf_out[6];
if ((fs = p->p_md.md_fpstate) != NULL) {
/*
* We hold an FPU state. If we own *the* FPU chip state
* we must get rid of it, and the only way to do that is
* to save it. In any case, get rid of our FPU state.
*/
if (p == fpproc) {
savefpstate(fs);
fpproc = NULL;
}
free((void *)fs, M_SUBPROC);
p->p_md.md_fpstate = NULL;
}
bzero((caddr_t)tf, sizeof *tf);
tf->tf_psr = psr;
tf->tf_global[2] = tf->tf_global[7] = tf->tf_npc = entry & ~3;
tf->tf_out[6] = sp;
retval[1] = 0;
}
#ifdef DEBUG
int sigdebug = 0;
int sigpid = 0;
#define SDB_FOLLOW 0x01
#define SDB_KSTACK 0x02
#define SDB_FPSTATE 0x04
#endif
struct sigframe {
int sf_signo; /* signal number */
int sf_code; /* code */
#ifdef COMPAT_SUNOS
struct sigcontext *sf_scp; /* points to user addr of sigcontext */
#else
int sf_xxx; /* placeholder */
#endif
int sf_addr; /* SunOS compat, always 0 for now */
struct sigcontext sf_sc; /* actual sigcontext */
};
/*
* 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 sigacts *psp = p->p_sigacts;
register struct sigframe *fp;
register struct trapframe *tf;
register int addr, oonstack;
struct sigframe sf;
extern char sigcode[], esigcode[];
#define szsigcode (esigcode - sigcode)
tf = p->p_md.md_tf;
oonstack = psp->ps_sigstk.ss_flags & SA_ONSTACK;
/*
* Compute new user stack addresses, subtract off
* one signal frame, and align.
*/
if ((psp->ps_flags & SAS_ALTSTACK) && !oonstack &&
(psp->ps_sigonstack & sigmask(sig))) {
fp = (struct sigframe *)(psp->ps_sigstk.ss_base +
psp->ps_sigstk.ss_size);
psp->ps_sigstk.ss_flags |= SA_ONSTACK;
} else
fp = (struct sigframe *)tf->tf_out[6];
fp = (struct sigframe *)((int)(fp - 1) & ~7);
#ifdef DEBUG
if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid)
printf("sendsig: %s[%d] sig %d newusp %x scp %x\n",
p->p_comm, p->p_pid, sig, fp, &fp->sf_sc);
#endif
/*
* Now set up the signal frame. We build it in kernel space
* and then copy it out. We probably ought to just build it
* directly in user space....
*/
sf.sf_signo = sig;
sf.sf_code = code;
#ifdef COMPAT_SUNOS
sf.sf_scp = &fp->sf_sc;
#endif
sf.sf_addr = 0; /* XXX */
/*
* Build the signal context to be used by sigreturn.
*/
sf.sf_sc.sc_onstack = oonstack;
sf.sf_sc.sc_mask = mask;
sf.sf_sc.sc_sp = tf->tf_out[6];
sf.sf_sc.sc_pc = tf->tf_pc;
sf.sf_sc.sc_npc = tf->tf_npc;
sf.sf_sc.sc_psr = tf->tf_psr;
sf.sf_sc.sc_g1 = tf->tf_global[1];
sf.sf_sc.sc_o0 = tf->tf_out[0];
/*
* Put the stack in a consistent state before we whack away
* at it. Note that write_user_windows may just dump the
* registers into the pcb; we need them in the process's memory.
*/
write_user_windows();
if (rwindow_save(p) || copyout((caddr_t)&sf, (caddr_t)fp, sizeof sf)) {
/*
* Process has trashed its stack; give it an illegal
* instruction to halt it in its tracks.
*/
#ifdef DEBUG
if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid)
printf("sendsig: window save or copyout error\n");
#endif
sigexit(p, SIGILL);
/* NOTREACHED */
}
#ifdef DEBUG
if (sigdebug & SDB_FOLLOW)
printf("sendsig: %s[%d] sig %d scp %x\n",
p->p_comm, p->p_pid, sig, &fp->sf_sc);
#endif
/*
* Arrange to continue execution at the code copied out in exec().
* It needs the function to call in %g1, and a new stack pointer.
*/
#ifdef COMPAT_SUNOS
if (psp->ps_usertramp & sigmask(sig)) {
addr = (int)catcher; /* user does his own trampolining */
} else
#endif
{
addr = USRSTACK - sizeof(struct ps_strings) - szsigcode;
tf->tf_global[1] = (int)catcher;
}
tf->tf_pc = addr;
tf->tf_npc = addr + 4;
tf->tf_out[6] = (int)fp - sizeof(struct rwindow);
#ifdef DEBUG
if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid)
printf("sendsig: about to return to catcher\n");
#endif
}
/*
* System call to cleanup state after a signal
* has been taken. Reset signal mask and
* stack state from context left by sendsig (above),
* and return to the given trap frame (if there is one).
* Check carefully to make sure that the user has not
* modified the state to gain improper privileges or to cause
* a machine fault.
*/
/* ARGSUSED */
struct sigreturn_args {
struct sigcontext *scp;
};
sigreturn(p, uap, retval)
register struct proc *p;
struct sigreturn_args *uap;
int *retval;
{
register struct sigcontext *scp;
register struct trapframe *tf;
/* First ensure consistent stack state (see sendsig). */
write_user_windows();
if (rwindow_save(p))
sigexit(p, SIGILL);
#ifdef DEBUG
if (sigdebug & SDB_FOLLOW)
printf("sigreturn: %s[%d], scp %x\n",
p->p_comm, p->p_pid, uap->scp);
#endif
scp = uap->scp;
if ((int)scp & 3 || useracc((caddr_t)scp, sizeof *scp, B_WRITE) == 0)
return (EINVAL);
tf = p->p_md.md_tf;
/*
* Only the icc bits in the psr are used, so it need not be
* verified. pc and npc must be multiples of 4. This is all
* that is required; if it holds, just do it.
*/
if (((scp->sc_pc | scp->sc_npc) & 3) != 0)
return (EINVAL);
/* take only psr ICC field */
tf->tf_psr = (tf->tf_psr & ~PSR_ICC) | (scp->sc_psr & PSR_ICC);
tf->tf_pc = scp->sc_pc;
tf->tf_npc = scp->sc_npc;
tf->tf_global[1] = scp->sc_g1;
tf->tf_out[0] = scp->sc_o0;
tf->tf_out[6] = scp->sc_sp;
if (scp->sc_onstack & 1)
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;
return (EJUSTRETURN);
}
int waittime = -1;
boot(howto)
register int howto;
{
int i;
static char str[4]; /* room for "-sd\0" */
extern volatile void romhalt(void);
extern volatile void romboot(char *);
fb_unblank();
boothowto = howto;
if ((howto & RB_NOSYNC) == 0 && waittime < 0 && rootfs) {
register struct buf *bp;
int iter, nbusy;
#if 1
extern struct proc proc0;
/* protect against curproc->p_stats.foo refs in sync() XXX */
if (curproc == NULL)
curproc = &proc0;
#endif
waittime = 0;
(void) spl0();
printf("syncing disks... ");
/*
* Release vnodes held by texts before sync.
*/
if (panicstr == 0)
vnode_pager_umount((struct mount *)NULL);
#include "fd.h"
#if NFD > 0
fdshutdown();
#endif
sync((struct proc *)NULL, (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(); /* ??? */
if (howto & RB_HALT) {
printf("halted\n\n");
romhalt();
}
if (howto & RB_DUMP)
dumpsys();
printf("rebooting\n\n");
i = 1;
if (howto & RB_SINGLE)
str[i++] = 's';
if (howto & RB_KDB)
str[i++] = 'd';
if (i > 1) {
str[0] = '-';
str[i] = 0;
} else
str[0] = 0;
romboot(str);
/*NOTREACHED*/
}
int dumpmag = 0x8fca0101; /* magic number for savecore */
int dumpsize = 0; /* also for savecore */
long dumplo = 0;
dumpconf()
{
int nblks;
dumpsize = physmem;
#define DUMPMMU
#ifdef DUMPMMU
#define NPMEG 128
/*
* savecore views the image in units of pages (i.e., dumpsize is in
* pages) so we round the two mmu entities into page-sized chunks.
* The PMEGs (32kB) and the segment table (512 bytes plus padding)
* are appending to the end of the crash dump.
*/
dumpsize += btoc(sizeof(((struct kpmap *)0)->pm_rsegmap)) +
btoc(NPMEG * NPTESG * sizeof(int));
#endif
if (dumpdev != NODEV && bdevsw[major(dumpdev)].d_psize) {
nblks = (*bdevsw[major(dumpdev)].d_psize)(dumpdev);
/*
* 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);
/*
* If dumpsize is too big for the partition, truncate it.
* Otherwise, put the dump at the end of the partition
* by making dumplo as large as possible.
*/
if (dumpsize > btoc(dbtob(nblks - dumplo)))
dumpsize = btoc(dbtob(nblks - dumplo));
else if (dumplo + ctod(dumpsize) > nblks)
dumplo = nblks - ctod(dumpsize);
}
}
#ifdef DUMPMMU
/* XXX */
#include "ctlreg.h"
#define getpte(va) lda(va, ASI_PTE)
#define setsegmap(va, pmeg) stba(va, ASI_SEGMAP, pmeg)
/*
* Write the mmu contents to the dump device.
* This gets appended to the end of a crash dump since
* there is no in-core copy of kernel memory mappings.
*/
int
dumpmmu(blkno)
register daddr_t blkno;
{
register int (*dump)(/*dev_t, daddr_t, caddr_t, int*/);
register int pmeg;
register int addr; /* unused kernel virtual address */
register int i;
register int *pte, *ptend;
register int error;
register struct kpmap *kpmap = &kernel_pmap_store;
int buffer[dbtob(1) / sizeof(int)];
extern int seginval; /* from pmap.c */
dump = bdevsw[major(dumpdev)].d_dump;
/*
* dump page table entries
*
* We dump each pmeg in order (by segment number). Since the MMU
* automatically maps the given virtual segment to a pmeg we must
* iterate over the segments by incrementing an unused segment slot
* in the MMU. This fixed segment number is used in the virtual
* address argument to getpte().
*/
/* First find an unused virtual segment. */
i = NKSEG;
while (kpmap->pm_rsegmap[--i] != seginval)
if (i <= 0)
return (-1);
/*
* Compute the base address corresponding to the unused segment.
* Note that the kernel segments start after all the user segments
* so we must account for this offset.
*/
addr = VSTOVA(i + NUSEG);
/*
* Go through the pmegs and dump each one.
*/
pte = buffer;
ptend = &buffer[sizeof(buffer) / sizeof(buffer[0])];
for (pmeg = 0; pmeg < NPMEG; ++pmeg) {
register int va = addr;
setsegmap(addr, pmeg);
i = NPTESG;
do {
*pte++ = getpte(va);
if (pte >= ptend) {
/*
* Note that we'll dump the last block
* the last time through the loops because
* all the PMEGs occupy 32KB which is
* a multiple of the block size.
*/
error = (*dump)(dumpdev, blkno,
(caddr_t)buffer,
dbtob(1));
if (error != 0)
return (error);
++blkno;
pte = buffer;
}
va += NBPG;
} while (--i > 0);
}
setsegmap(addr, seginval);
/*
* dump (512 byte) segment map
* XXX assume it's a multiple of the block size
*/
error = (*dump)(dumpdev, blkno, (caddr_t)kpmap->pm_rsegmap,
sizeof(kpmap->pm_rsegmap), 0);
return (error);
}
#endif
#define BYTES_PER_DUMP (32 * 1024) /* must be a multiple of pagesize */
static vm_offset_t dumpspace;
caddr_t
reserve_dumppages(p)
caddr_t p;
{
dumpspace = (vm_offset_t)p;
return (p + BYTES_PER_DUMP);
}
/*
* Write a crash dump.
*/
dumpsys()
{
register unsigned bytes, i, n;
register int maddr, psize;
register daddr_t blkno;
register int (*dump)(/*dev_t, daddr_t, caddr_t, int, int*/);
int error = 0;
if (dumpdev == NODEV)
return;
/* copy registers to memory */
snapshot(cpcb);
/*
* 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);
psize = (*bdevsw[major(dumpdev)].d_psize)(dumpdev);
printf("dump ");
if (psize == -1) {
printf("area unavailable\n");
return;
}
bytes = physmem << PGSHIFT;
maddr = 0;
blkno = dumplo;
dump = bdevsw[major(dumpdev)].d_dump;
for (i = 0; i < bytes; i += n) {
n = bytes - i;
if (n > BYTES_PER_DUMP)
n = BYTES_PER_DUMP;
#ifdef DEBUG
/* print out how many MBs we have dumped */
if (i && (i % (1024*1024)) == 0)
printf("%d ", i / (1024*1024));
#endif
(void) pmap_map(dumpspace, maddr, maddr + n, VM_PROT_READ);
error = (*dump)(dumpdev, blkno, (caddr_t)dumpspace, (int)n);
if (error)
break;
maddr += n;
blkno += btodb(n);
}
#ifdef DUMPMMU
if (!error)
error = dumpmmu(blkno);
#endif
switch (error) {
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;
case 0:
printf("succeeded\n");
break;
default:
printf("error %d\n", error);
break;
}
}
/*
* Map an I/O device given physical address and size in bytes, e.g.,
*
* mydev = (struct mydev *)mapdev(myioaddr, 0, sizeof(struct mydev));
*
* See also machine/autoconf.h.
*/
void *
mapdev(phys, virt, size)
register void *phys;
register int virt, size;
{
register vm_offset_t v;
register void *ret;
static vm_offset_t iobase = IODEV_BASE;
size = round_page(size);
if (virt)
v = virt;
else {
v = iobase;
iobase += size;
if (iobase > IODEV_END) /* unlikely */
panic("mapiodev");
}
ret = (void *)v;
do {
pmap_enter(kernel_pmap, v,
(vm_offset_t)phys | PMAP_OBIO | PMAP_NC,
VM_PROT_READ | VM_PROT_WRITE, 1);
v += PAGE_SIZE;
phys += PAGE_SIZE;
} while ((size -= PAGE_SIZE) > 0);
return (ret);
}