[BACK]Return to machdep.c CVS log [TXT][DIR] Up to [local] / sys / arch / mac68k / mac68k

File: [local] / sys / arch / mac68k / mac68k / machdep.c (download)

Revision 1.1, Tue Mar 4 16:07:14 2008 UTC (16 years, 2 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*	$OpenBSD: machdep.c,v 1.142 2007/07/29 21:24:05 miod Exp $	*/
/*	$NetBSD: machdep.c,v 1.207 1998/07/08 04:39:34 thorpej Exp $	*/

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1982, 1990 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.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
/*-
 * Copyright (C) 1993	Allen K. Briggs, Chris P. Caputo,
 *			Michael L. Finch, Bradley A. Grantham, and
 *			Lawrence A. Kesteloot
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the Alice Group.
 * 4. The names of the Alice Group or any of its members may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/*
 * from: Utah $Hdr: machdep.c 1.63 91/04/24$
 *
 *	@(#)machdep.c	7.16 (Berkeley) 6/3/91
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/exec.h>
#include <sys/core.h>
#include <sys/kcore.h>
#include <sys/vnode.h>
#include <sys/reboot.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/timeout.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/msgbuf.h>
#include <sys/user.h>
#include <sys/mount.h>
#include <sys/extent.h>
#include <sys/syscallargs.h>
#ifdef SYSVMSG
#include <sys/msg.h>
#endif

#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#include <ddb/db_var.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/kcore.h>
#include <machine/bus.h>
#include <machine/pmap.h>

#include <uvm/uvm_extern.h>

#include <sys/sysctl.h>

#include <dev/cons.h>
#include <mac68k/dev/adbvar.h>

#include <machine/iop.h>
#include <machine/psc.h>
#include <machine/viareg.h>

#include "wsdisplay.h"

/* The following is used externally (sysctl_hw) */
char	machine[] = MACHINE;	/* from <machine/param.h> */

struct mac68k_machine_S mac68k_machine;

volatile u_char *Via1Base, *Via2Base, *PSCBase = NULL;
u_long  NuBusBase = NBBASE;
u_long  IOBase;

vaddr_t SCSIBase;

/* These are used to map kernel space: */
extern int numranges;
extern u_long low[8];
extern u_long high[8];

/* These are used to map NuBus space: */
#define	NBMAXRANGES	16
int	nbnumranges;		/* = 0 == don't use the ranges */
u_long	nbphys[NBMAXRANGES];	/* Start physical addr of this range */
u_long	nblog[NBMAXRANGES];	/* Start logical addr of this range */
long	nblen[NBMAXRANGES];	/* Length of this range If the length is */
				/* negative, all phys addrs are the same. */

/* From Booter via locore */
long	videoaddr;		/* Addr used in kernel for video. */
long	videorowbytes;		/* Used in kernel for video. */
long	videobitdepth;		/* Number of bihs per pixel */
u_long	videosize;		/* height = 31:16, width 15:0 */

/*
 * Values for IIvx-like internal video
 * -- should be zero if it is not used (usual case).
 */
u_int32_t mac68k_vidlog;	/* logical addr */
u_int32_t mac68k_vidphys;	/* physical addr */
u_int32_t mac68k_vidlen;	/* mem length */

/* Callback and cookie to run bell */
int	(*mac68k_bell_callback)(void *, int, int, int);
caddr_t	mac68k_bell_cookie;

struct vm_map *exec_map = NULL;  
struct vm_map *phys_map = NULL;

/*
 * Declare these as initialized data so we can patch them.
 */
#ifndef	BUFCACHEPERCENT
#define	BUFCACHEPERCENT	5
#endif

#ifdef	BUFPAGES
int	bufpages = BUFPAGES;
#else
int	bufpages = 0;
#endif
int	bufcachepercent = BUFCACHEPERCENT;

int	physmem;		/* size of physical memory, in pages */

/*
 * safepri is a safe priority for sleep to set for a spin-wait
 * during autoconfiguration or after a panic.
 */
int	safepri = PSL_LOWIPL;

/*
 * Extent maps to manage all memory space, including I/O ranges.  Allocate
 * storage for 8 regions in each, initially.  Later, iomem_malloc_safe
 * will indicate that it's safe to use malloc() to dynamically allocate
 * region descriptors.
 *
 * The extent maps are not static!  Machine-dependent NuBus and on-board
 * I/O routines need access to them for bus address space allocation.
 */
long iomem_ex_storage[EXTENT_FIXED_STORAGE_SIZE(8) / sizeof(long)];
struct	extent *iomem_ex;
int	iomem_malloc_safe;

/* XXX should be in locore.s for consistency */
int	astpending = 0;

void	identifycpu(void);
u_long	get_physical(u_int, u_long *);

caddr_t	allocsys(caddr_t);
void	initcpu(void);
int	cpu_dumpsize(void);
int	cpu_dump(int (*)(dev_t, daddr64_t, caddr_t, size_t), daddr64_t *);
void	cpu_init_kcore_hdr(void);
int	fpu_probe(void);

/* functions called from locore.s */
void	dumpsys(void);
void	mac68k_init(void);
void	straytrap(int, int);
void	nmihand(struct frame);

/*
 * Machine-dependent crash dump header info.
 */
cpu_kcore_hdr_t cpu_kcore_hdr;

 /*
 * Early initialization, before main() is called.
 */
void
mac68k_init()
{
	int i;
	extern vaddr_t avail_start;

	/*
	 * Tell the VM system about available physical memory.
	 * Notice that we don't need to worry about avail_end here
	 * since it's equal to high[numranges-1].
	 */
	for (i = 0; i < numranges; i++) {
		if (low[i] <= avail_start && avail_start < high[i])
			uvm_page_physload(atop(avail_start), atop(high[i]),
			    atop(avail_start), atop(high[i]),
			    VM_FREELIST_DEFAULT);
		else
			uvm_page_physload(atop(low[i]), atop(high[i]),
			    atop(low[i]), atop(high[i]),
			    VM_FREELIST_DEFAULT);
	}

	/*
	 * Initialize the I/O mem extent map.
	 * Note: we don't have to check the return value since
	 * creation of a fixed extent map will never fail (since
	 * descriptor storage has already been allocated).
	 *
	 * N.B. The iomem extent manages _all_ physical addresses
	 * on the machine.  When the amount of RAM is found, all
	 * extents of RAM are allocated from the map.
	 */
	iomem_ex = extent_create("iomem", 0x0, 0xffffffff, M_DEVBUF,
	    (caddr_t)iomem_ex_storage, sizeof(iomem_ex_storage),
	    EX_NOCOALESCE|EX_NOWAIT);

	/* Initialize the interrupt handlers. */
	intr_init();

	/* Initialize the VIAs */
	via_init();

	/* Initialize the PSC (if present) */
	psc_init();
}

/*
 * Console initialization: called early on from main,
 * before vm init or startup.  Do enough configuration
 * to choose and initialize a console.
 */
void
consinit(void)
{
	/*
	 * Generic console: sys/dev/cons.c
	 *	Initializes either ite or ser as console.
	 *	Can be called from locore.s and init_main.c.  (Ugh.)
	 */
	static int init;	/* = 0 */

	if (!init) {
		cninit();
#ifdef DDB
		/*
		 * Initialize kernel debugger, if compiled in.
		 */
		ddb_init();
#endif
		init = 1;
	} else {
#if NWSDISPLAY > 0
		/*
		 * XXX  This is an evil hack on top of an evil hack!
		 *
		 * With the graybar stuff, we've got a catch-22:  we need
		 * to do at least some console setup really early on, even
		 * before we're running with the mappings we need.  On
		 * the other hand, we're not nearly ready to do anything
		 * with wscons or the ADB driver at that point.
		 *
		 * To get around this, wscninit() ignores the first call
		 * it gets (from cninit(), if not on a serial console).
		 * Once we're here, we call wscninit() again, which sets
		 * up the console devices and does the appropriate wscons
		 * initialization.
		 */
		if (mac68k_machine.serial_console == 0) {
			cons_decl(ws);
			wscninit(NULL);
		}
#endif

		mac68k_calibrate_delay();

#if NZSC > 0 && defined(KGDB)
		zs_kgdb_init();
#endif

		if (boothowto & RB_KDB) {
#ifdef KGDB
			/* XXX - Ask on console for kgdb_dev? */
			/* Note: this will just return if kgdb_dev==NODEV */
			kgdb_connect(1);
#else	/* KGDB */
#ifdef DDB
			/* Enter DDB.  We don't have a monitor PROM. */
			Debugger();
#endif /* DDB */
#endif	/* KGDB */
		}
	}
}

#define CURRENTBOOTERVER	111

/*
 * cpu_startup: allocate memory for variable-sized tables,
 * initialize cpu, and do autoconfiguration.
 */
void
cpu_startup(void)
{
	caddr_t v;
	unsigned i;
	int vers;
	vaddr_t minaddr, maxaddr;
	vsize_t size = 0;	/* To avoid compiler warning */
	int delay;

	/*
	 * Initialize the kernel crash dump header.
	 */
	cpu_init_kcore_hdr();

	/*
	 * Initialize error message buffer (at end of core).
	 * high[numranges-1] was decremented in pmap_bootstrap.
	 */
	for (i = 0; i < btoc(MSGBUFSIZE); i++)
		pmap_enter(pmap_kernel(), (vaddr_t)msgbufp + i * NBPG,
		    high[numranges - 1] + i * NBPG,
		    VM_PROT_READ|VM_PROT_WRITE,
		    VM_PROT_READ|VM_PROT_WRITE|PMAP_WIRED);
	pmap_update(pmap_kernel());
	initmsgbuf((caddr_t)msgbufp, round_page(MSGBUFSIZE));

	/*
	 * Good {morning,afternoon,evening,night}.
	 */
	printf(version);
	identifycpu();

	vers = mac68k_machine.booter_version;
	if (vers < CURRENTBOOTERVER) {
		/* fix older booters with indices, not versions */
		if (vers < 100)
			vers += 99;

		printf("\nYou booted with booter version %d.%d.\n",
		    vers / 100, vers % 100);
		printf("Booter version %d.%d is necessary to fully support\n",
		    CURRENTBOOTERVER / 100, CURRENTBOOTERVER % 100);
		printf("this kernel.\n\n");
		for (delay = 0; delay < 1000000; delay++);
	}
	printf("real mem = %u (%uMB)\n", ctob(physmem),
	    ctob(physmem)/1024/1024);

	/*
	 * Find out how much space we need, allocate it,
	 * and then give everything true virtual addressses.
	 */
	size = (vsize_t)allocsys((caddr_t)0);
	if ((v = (caddr_t)uvm_km_zalloc(kernel_map, round_page(size))) == 0)
		panic("startup: no room for tables");
	if ((allocsys(v) - v) != size)
		panic("startup: table size inconsistency");

	/*
	 * Determine how many buffers to allocate.
	 * We allocate bufcachepercent% of memory for buffer space.
	 */
	if (bufpages == 0)
		bufpages = physmem * bufcachepercent / 100;

	/* Restrict to at most 25% filled kvm */
	if (bufpages >
	    (VM_MAX_KERNEL_ADDRESS-VM_MIN_KERNEL_ADDRESS) / PAGE_SIZE / 4) 
		bufpages = (VM_MAX_KERNEL_ADDRESS-VM_MIN_KERNEL_ADDRESS) /
		    PAGE_SIZE / 4;

	/*
	 * Allocate a submap for exec arguments.  This map effectively
	 * limits the number of processes exec'ing at any time.
	 */
	minaddr = vm_map_min(kernel_map);
	exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
	    16 * NCARGS, VM_MAP_PAGEABLE, FALSE, NULL);

	/*
	 * Allocate a submap for physio
	 */
	phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
	    VM_PHYS_SIZE, 0, FALSE, NULL);

	printf("avail mem = %lu (%luMB)\n", ptoa(uvmexp.free),
	    ptoa(uvmexp.free) / 1024 / 1024);

	/*
	 * Set up CPU-specific registers, cache, etc.
	 */
	initcpu();

	/*
	 * Set up buffers, so they can be used to read disk labels.
	 */
	bufinit();

	/*
	 * Configure the system.
	 */
	if (boothowto & RB_CONFIG) {
#ifdef BOOT_CONFIG
		user_config();
#else
		printf("kernel does not support -c; continuing..\n");
#endif
	}

	/* Safe for extent allocation to use malloc now. */
	iomem_malloc_safe = 1;
}

/*
 * 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.
 *
 * We 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)
	caddr_t 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)))

#ifdef SYSVMSG
	valloc(msgpool, char, msginfo.msgmax);
	valloc(msgmaps, struct msgmap, msginfo.msgseg);
	valloc(msghdrs, struct msg, msginfo.msgtql);
	valloc(msqids, struct msqid_ds, msginfo.msgmni);
#endif

	return (v);
}

void
initcpu()
{
#if defined(M68040)
	extern void (*vectab[256])(void);
	void addrerr4060(void);
#endif
#ifdef M68040
	void buserr40(void);
#endif
#ifdef FPSP
	extern u_long fpvect_tab, fpvect_end, fpsp_tab;
#endif

	switch (cputype) {
#ifdef M68040
	case CPU_68040:
		vectab[2] = buserr40;
		vectab[3] = addrerr4060;
#ifdef FPSP
		bcopy(&fpsp_tab, &fpvect_tab,
		    (&fpvect_end - &fpvect_tab) * sizeof (fpvect_tab));
#endif
		break;
#endif
	default:
		break;
	}

	DCIS();
}

void doboot(void)
	__attribute__((__noreturn__));

int	waittime = -1;

void
boot(howto)
	int howto;
{
	extern u_long maxaddr;

	/* take a snap shot before clobbering any registers */
	if (curproc && curproc->p_addr)
		savectx(&curproc->p_addr->u_pcb);

	/* If system is cold, just halt. */
	if (cold) {
		/* (Unless the user explicitly asked for reboot.) */
		if ((howto & RB_USERREQ) == 0)
			howto |= RB_HALT;
		goto haltsys;
	}

	boothowto = howto;
	if ((howto & RB_NOSYNC) == 0 && waittime < 0) {
		waittime = 0;
		vfs_shutdown();

		if (mac68k_machine.aux_interrupts != 0) {
			/*
			 * If we've been adjusting the clock, the todr
			 * will be out of synch; adjust it now unless
			 * the system was sitting in ddb.
			 */
			if ((howto & RB_TIMEBAD) == 0) {
				resettodr();
			} else {
				printf("WARNING: not updating battery clock\n");
			}
		} else {
#ifdef DIAGNOSTIC
			printf("OpenBSD/mac68k does not trust itself to update"
			    " the clock on shutdown on this machine.\n");
#endif
		}
	}

	/* Disable interrupts. */
	splhigh();

	/* If rebooting and a dump is requested, do it. */
	if (howto & RB_DUMP) {
		dumpsys();
	}

haltsys:
	/* Run any shutdown hooks. */
	doshutdownhooks();

	if (howto & RB_HALT) {
		if (howto & RB_POWERDOWN) {
			printf("\nAttempting to power down...\n");
			via_powerdown();
			/*
			 * Shut down machines whose power functions
			 * are accessed via modified ADB calls.
			 */
			adb_poweroff();
		}
		printf("\nThe operating system has halted.\n");
		printf("Please press any key to reboot.\n\n");
		(void)cngetc();
	}

	/* Map the last physical page VA = PA for doboot() */
	pmap_enter(pmap_kernel(), (vaddr_t)maxaddr, (vaddr_t)maxaddr,
	    VM_PROT_ALL, VM_PROT_ALL|PMAP_WIRED);
	pmap_update(pmap_kernel());

	printf("rebooting...\n");
	DELAY(1000000);
	doboot();
	/* NOTREACHED */
}

/*
 * Initialize the kernel crash dump header.
 */
void
cpu_init_kcore_hdr()
{
	cpu_kcore_hdr_t *h = &cpu_kcore_hdr;
	int i;

	bzero(&cpu_kcore_hdr, sizeof(cpu_kcore_hdr));

	h->mmutype = mmutype;
	h->kernel_pa = low[0];
	h->sysseg_pa = pmap_kernel()->pm_stpa;

	/*
	 * mac68k has multiple RAM segments on some models.
	 */
	for (i = 0; i < numranges; i++) {
		h->ram_segs[i].start = low[i];
		h->ram_segs[i].size  = high[i] - low[i];
	}
}

/*
 * Compute the size of the machine-dependent crash dump header.
 * Returns size in disk blocks.
 */
int
cpu_dumpsize()
{
	int size;

	size = ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t));
	return (btodb(roundup(size, dbtob(1))));
}

/*
 * Called by dumpsys() to dump the machine-dependent header.
 */
int
cpu_dump(dump, blknop)
	int (*dump)(dev_t, daddr64_t, caddr_t, size_t);
	daddr64_t *blknop;
{
	int buf[dbtob(1) / sizeof(int)];
	cpu_kcore_hdr_t *chdr;
	kcore_seg_t *kseg;
	int error;

	kseg = (kcore_seg_t *)buf;
	chdr = (cpu_kcore_hdr_t *)&buf[ALIGN(sizeof(kcore_seg_t)) /
	    sizeof(int)];

	/* Create the segment header. */
	CORE_SETMAGIC(*kseg, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
	kseg->c_size = dbtob(1) - ALIGN(sizeof(kcore_seg_t));

	bcopy(&cpu_kcore_hdr, chdr, sizeof(cpu_kcore_hdr_t));
	error = (*dump)(dumpdev, *blknop, (caddr_t)buf, sizeof(buf));
	*blknop += btodb(sizeof(buf));
	return (error);
}

/*
 * These variables are needed by /sbin/savecore
 */
u_long	dumpmag = 0x8fca0101;	/* magic number */
int	dumpsize = 0;		/* pages */
long	dumplo = 0;		/* blocks */

/*
 * This is called by main to set dumplo and dumpsize.
 * Dumps always skip the first block of disk space in
 * case there might be a disk label stored there.  If there
 * is extra space, put dump at the end to reduce the chance
 * that swapping trashes it.
 */
void
dumpconf(void)
{
	cpu_kcore_hdr_t *h = &cpu_kcore_hdr;
	int nblks;	/* size of dump area */
	int i;

	if (dumpdev == NODEV ||
	    (nblks = (bdevsw[major(dumpdev)].d_psize)(dumpdev)) == 0)
		return;
	if (nblks <= ctod(1))
		return;

	dumpsize = 0;
	for (i = 0; h->ram_segs[i].size && i < NPHYS_RAM_SEGS; i++)
		dumpsize += btoc(h->ram_segs[i].size);
	dumpsize += cpu_dumpsize();

	/* Always skip the first block, in case there is a label there. */
	if (dumplo < ctod(1))
		dumplo = ctod(1);

	/* Put dump at end of partition, and make it fit. */
	if (dumpsize > dtoc(nblks - dumplo))
		dumpsize = dtoc(nblks - dumplo);
	if (dumplo < nblks - ctod(dumpsize))
		dumplo = nblks - ctod(dumpsize);
}

void
dumpsys()
{
	cpu_kcore_hdr_t *h = &cpu_kcore_hdr;
	daddr64_t blkno;	/* current block to write */
				/* dump routine */
	int (*dump)(dev_t, daddr64_t, caddr_t, size_t);
	int pg;			/* page being dumped */
	vaddr_t maddr;	/* PA being dumped */
	int seg;		/* RAM segment being dumped */
	int error;		/* error code from (*dump)() */
	extern int msgbufmapped;

	/* XXX initialized here because of gcc lossage */
	seg = 0;
	maddr = h->ram_segs[seg].start;
	pg = 0;

	/* Don't record dump msgs in msgbuf. */
	msgbufmapped = 0;

	/* Make sure dump device is valid. */
	if (dumpdev == NODEV)
		return;
	if (dumpsize == 0) {
		dumpconf();
		if (dumpsize == 0)
			return;
	}
	if (dumplo <= 0) {
		printf("\ndump to dev %u,%u not possible\n", major(dumpdev),
		    minor(dumpdev));
		return;
	}
	dump = bdevsw[major(dumpdev)].d_dump;
	blkno = dumplo;

	printf("\ndumping to dev %u,%u offset %ld\n", major(dumpdev),
	    minor(dumpdev), dumplo);

	printf("dump ");

	/* Write the dump header. */
	error = cpu_dump(dump, &blkno);
	if (error)
		goto bad;

	for (pg = 0; pg < dumpsize; pg++) {
#define NPGMB	(1024*1024/NBPG)
		/* print out how many MBs we have dumped */
		if (pg && (pg % NPGMB) == 0)
			printf("%d ", pg / NPGMB);
#undef NPGMB
		while (maddr >=
		    (h->ram_segs[seg].start + h->ram_segs[seg].size)) {
			if (++seg >= NPHYS_RAM_SEGS ||
			    h->ram_segs[seg].size == 0) {
				error = EINVAL;		/* XXX ?? */
				goto bad;
			}
			maddr = h->ram_segs[seg].start;
		}
		pmap_enter(pmap_kernel(), (vaddr_t)vmmap, maddr,
		    VM_PROT_READ, VM_PROT_READ|PMAP_WIRED);
		pmap_update(pmap_kernel());

		error = (*dump)(dumpdev, blkno, vmmap, NBPG);
bad:
		switch (error) {
		case 0:
			maddr += NBPG;
			blkno += btodb(NBPG);
			break;

		case ENXIO:
			printf("device bad\n");
			return;

		case EFAULT:
			printf("device not ready\n");
			return;

		case EINVAL:
			printf("area improper\n");
			return;

		case EIO:
			printf("i/o error\n");
			return;

		case EINTR:
			printf("aborted from console\n");
			return;

		default:
			printf("error %d\n", error);
			return;
		}
	}
	printf("succeeded\n");
}

/*
 * Return the best possible estimate of the time in the timeval
 * to which tvp points.  We do this by returning the current time
 * plus the amount of time since the last clock interrupt (clock.c:clkread).
 *
 * Check that this time is no less than any previously-reported time,
 * which could happen around the time of a clock adjustment.  Just for fun,
 * we guarantee that the time will be greater than the value obtained by a
 * previous call.
 */
void
microtime(tvp)
	register struct timeval *tvp;
{
	int s = splhigh();
	static struct timeval lasttime;

	*tvp = time;
	tvp->tv_usec += clkread();
	while (tvp->tv_usec >= 1000000) {
		tvp->tv_sec++;
		tvp->tv_usec -= 1000000;
	}
	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);
}

void
straytrap(pc, evec)
	int pc;
	int evec;
{
	printf("unexpected trap; vector offset 0x%x from 0x%x.\n",
	    (int) (evec & 0xfff), pc);
#ifdef DEBUG
#ifdef DDB
	Debugger();
#endif
#endif
}

int	*nofault;

/*
 * Level 7 interrupts can be caused by the keyboard or parity errors.
 */
void	nmihand(struct frame);

void
nmihand(frame)
	struct frame frame;
{
	static int nmihanddeep = 0;

	if (nmihanddeep)
		return;
	nmihanddeep = 1;

#ifdef DDB
	if (db_console)
		Debugger();
#endif

	nmihanddeep = 0;
}

/*
 * It should be possible to probe for the top of RAM, but Apple has
 * memory structured so that in at least some cases, it's possible
 * for RAM to be aliased across all memory--or for it to appear that
 * there is more RAM than there really is.
 */
int	get_top_of_ram(void);

int
get_top_of_ram()
{
	return ((mac68k_machine.mach_memsize * (1024 * 1024)) - 4096);
}

/*
 * machine dependent system variables.
 */
int
cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
	int *name;
	u_int   namelen;
	void   *oldp;
	size_t *oldlenp;
	void   *newp;
	size_t  newlen;
	struct proc *p;
{
	dev_t   consdev;

	/* all sysctl names at this level are terminal */
	if (namelen != 1)
		return (ENOTDIR);	/* overloaded */

	switch (name[0]) {
	case CPU_CONSDEV:
		if (cn_tab != NULL)
			consdev = cn_tab->cn_dev;
		else
			consdev = NODEV;
		return (sysctl_rdstruct(oldp, oldlenp, newp, &consdev,
			sizeof consdev));
	default:
		return (EOPNOTSUPP);
	}
	/* NOTREACHED */
}

int
cpu_exec_aout_makecmds(p, epp)
	struct proc *p;
	struct exec_package *epp;
{
	int error = ENOEXEC;

#ifdef COMPAT_SUNOS
	{
		extern int sunos_exec_aout_makecmds(struct proc *,
			        struct exec_package *);
		if ((error = sunos_exec_aout_makecmds(p, epp)) == 0)
			return 0;
	}
#endif
	return error;
}

static char *envbuf = NULL;

/*
 * getenvvars: Grab a few useful variables
 */
void	getenvvars(u_long, char *);
long	getenv(char *);

void
getenvvars(flag, buf)
	u_long  flag;
	char   *buf;
{
	extern u_long bootdev;
#if defined(DDB) || NKSYMS > 0
	extern u_long end, esym;
#endif
	extern u_long macos_boottime;
	extern vaddr_t MacOSROMBase;
	extern long macos_gmtbias;
	extern u_short ADBDelay;
	extern u_int32_t HwCfgFlags3;
	int root_scsi_id;
	vaddr_t ROMBase;

	/*
	 * If flag & 0x80000000 == 0, then we're booting with the old booter
	 * and we should freak out.
	 */
	if ((flag & 0x80000000) == 0) {
		/* Freak out; print something if that becomes available */
	} else
		envbuf = buf;

	/* These next two should give us mapped video & serial */
	/* We need these for pre-mapping graybars & echo, but probably */
	/* only on MacII or LC.  --  XXX */
	/* videoaddr = getenv("MACOS_VIDEO"); */

	/*
	 * The following are not in a structure so that they can be
	 * accessed more quickly.
	 */
	videoaddr = getenv("VIDEO_ADDR");
	videorowbytes = getenv("ROW_BYTES");
	videobitdepth = getenv("SCREEN_DEPTH");
	videosize = getenv("DIMENSIONS");

	/*
	 * More misc stuff from booter.
	 */
	mac68k_machine.machineid = getenv("MACHINEID");
	mac68k_machine.mach_memsize = getenv("MEMSIZE");
	mac68k_machine.do_graybars = getenv("GRAYBARS");
	mac68k_machine.serial_boot_echo = getenv("SERIALECHO");
	mac68k_machine.serial_console = getenv("SERIALCONSOLE");

	mac68k_machine.modem_flags = getenv("SERIAL_MODEM_FLAGS");
	mac68k_machine.modem_cts_clk = getenv("SERIAL_MODEM_HSKICLK");
	mac68k_machine.modem_dcd_clk = getenv("SERIAL_MODEM_GPICLK");
        mac68k_machine.modem_flags = getenv("SERIAL_MODEM_DSPEED");
	mac68k_machine.print_flags = getenv("SERIAL_PRINT_FLAGS");
	mac68k_machine.print_cts_clk = getenv("SERIAL_PRINT_HSKICLK");
	mac68k_machine.print_dcd_clk = getenv("SERIAL_PRINT_GPICLK");
	mac68k_machine.print_d_speed = getenv("SERIAL_PRINT_DSPEED");
	mac68k_machine.booter_version = getenv("BOOTERVER");

	/*
	 * For now, we assume that the boot device is off the first controller.
	 * Booter versions 1.11.0 and later set a flag to tell us to construct
	 * bootdev using the SCSI ID passed in via the environment.
	 */
	root_scsi_id = getenv("ROOT_SCSI_ID");
	if (((mac68k_machine.booter_version < CURRENTBOOTERVER) ||
	    (flag & 0x40000)) && bootdev == 0)
		bootdev = MAKEBOOTDEV(4, 0, 0, root_scsi_id, 0);

	/*
	 * Booter 1.11.3 and later pass a BOOTHOWTO variable with the
	 * appropriate bits set.
	 */
	boothowto = getenv("BOOTHOWTO");
	if (boothowto == 0)
		boothowto = getenv("SINGLE_USER");

	/*
	 * Get end of symbols for kernel debugging
	 */
#if defined(DDB) || NKSYMS > 0
	esym = getenv("END_SYM");
	if (esym == 0)
		esym = (long) &end;
#endif

	/* Get MacOS time */
	macos_boottime = getenv("BOOTTIME");

	/* Save GMT BIAS saved in Booter parameters dialog box */
	macos_gmtbias = getenv("GMTBIAS");

	/*
	 * Save globals stolen from MacOS
	 */

	ROMBase = (vaddr_t)getenv("ROMBASE");
	if (ROMBase != 0)
		MacOSROMBase = ROMBase;
	ADBDelay = (u_short) getenv("ADBDELAY");
	HwCfgFlags3 = getenv("HWCFGFLAG3");
}

char	toupper(char);

char
toupper(c)
	char c;
{
	if (c >= 'a' && c <= 'z') {
		return c - 'a' + 'A';
	} else {
		return c;
	}
}

long
getenv(str)
	char *str;
{
	/*
	 * Returns the value of the environment variable "str".
	 *
	 * Format of the buffer is "var=val\0var=val\0...\0var=val\0\0".
	 *
	 * Returns 0 if the variable is not there, and 1 if the variable is
	 * there without an "=val".
	 */

	char *s, *s1;
	int val, base;

	s = envbuf;
	while (1) {
		for (s1 = str; *s1 && *s && *s != '='; s1++, s++) {
			if (toupper(*s1) != toupper(*s)) {
				break;
			}
		}
		if (*s1) {	/* No match */
			while (*s) {
				s++;
			}
			s++;
			if (*s == '\0') {	/* Not found */
				/* Boolean flags are FALSE (0) if not there */
				return 0;
			}
			continue;
		}
		if (*s == '=') {/* Has a value */
			s++;
			val = 0;
			base = 10;
			if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) {
				base = 16;
				s += 2;
			} else
				if (*s == '0') {
					base = 8;
				}
			while (*s) {
				if (toupper(*s) >= 'A' && toupper(*s) <= 'F') {
					val = val * base + toupper(*s) - 'A' + 10;
				} else {
					val = val * base + (*s - '0');
				}
				s++;
			}
			return val;
		} else {	/* TRUE (1) */
			return 1;
		}
	}
}

struct cpu_model_info cpu_models[] = {

/* The first four. */
	{ MACH_MACII,		"II",			MACH_CLASSII },
	{ MACH_MACIIX,		"IIx",			MACH_CLASSII },
	{ MACH_MACIICX,		"IIcx",			MACH_CLASSII },
	{ MACH_MACSE30,		"SE/30",		MACH_CLASSII },

/* The rest of the II series... */
	{ MACH_MACIICI,		"IIci",			MACH_CLASSIIci },
	{ MACH_MACIISI,		"IIsi",			MACH_CLASSIIsi },
	{ MACH_MACIIVI,		"IIvi",			MACH_CLASSIIvx },
	{ MACH_MACIIVX,		"IIvx",			MACH_CLASSIIvx },
	{ MACH_MACIIFX,		"IIfx",			MACH_CLASSIIfx },

/* The Centris/Quadra series. */
	{ MACH_MACQ700,		"Quadra 700",		MACH_CLASSQ },
	{ MACH_MACQ900,		"Quadra 900",		MACH_CLASSQ },
	{ MACH_MACQ950,		"Quadra 950",		MACH_CLASSQ },
	{ MACH_MACQ800,		"Quadra 800",		MACH_CLASSQ },
	{ MACH_MACQ650,		"Quadra 650",		MACH_CLASSQ },
	{ MACH_MACC650,		"Centris 650",		MACH_CLASSQ },
	{ MACH_MACQ605,		"Quadra 605",		MACH_CLASSQ },
	{ MACH_MACQ605_33,	"Quadra 605/33",	MACH_CLASSQ },
	{ MACH_MACC610,		"Centris 610",		MACH_CLASSQ },
	{ MACH_MACQ610,		"Quadra 610",		MACH_CLASSQ },
	{ MACH_MACQ630,		"Quadra 630",		MACH_CLASSQ2 },
	{ MACH_MACC660AV,	"Centris 660AV",	MACH_CLASSAV },
	{ MACH_MACQ840AV,	"Quadra 840AV",		MACH_CLASSAV },

/* The Powerbooks/Duos... */
	{ MACH_MACPB100,	"PowerBook 100",	MACH_CLASSPB },
	/* PB 100 has no MMU! */
	{ MACH_MACPB140,	"PowerBook 140",	MACH_CLASSPB },
	{ MACH_MACPB145,	"PowerBook 145",	MACH_CLASSPB },
	{ MACH_MACPB150,	"PowerBook 150",	MACH_CLASSDUO },
	{ MACH_MACPB160,	"PowerBook 160",	MACH_CLASSPB },
	{ MACH_MACPB165,	"PowerBook 165",	MACH_CLASSPB },
	{ MACH_MACPB165C,	"PowerBook 165c",	MACH_CLASSPB },
	{ MACH_MACPB170,	"PowerBook 170",	MACH_CLASSPB },
	{ MACH_MACPB180,	"PowerBook 180",	MACH_CLASSPB },
	{ MACH_MACPB180C,	"PowerBook 180c",	MACH_CLASSPB },
	{ MACH_MACPB190,	"PowerBook 190",	MACH_CLASSPB },
	{ MACH_MACPB190CS,	"PowerBook 190cs",	MACH_CLASSPB },
	{ MACH_MACPB500,	"PowerBook 500",	MACH_CLASSPB },

/* The Duos */
	{ MACH_MACPB210,	"PowerBook Duo 210",	MACH_CLASSDUO },
	{ MACH_MACPB230,	"PowerBook Duo 230",	MACH_CLASSDUO },
	{ MACH_MACPB250,	"PowerBook Duo 250",	MACH_CLASSDUO },
	{ MACH_MACPB270,	"PowerBook Duo 270C",	MACH_CLASSDUO },
	{ MACH_MACPB280,	"PowerBook Duo 280",	MACH_CLASSDUO },
	{ MACH_MACPB280C,	"PowerBook Duo 280C",	MACH_CLASSDUO },

/* The Performas... */
	{ MACH_MACP600,		"Performa 600",		MACH_CLASSIIvx },
	{ MACH_MACP460,		"Performa 460",		MACH_CLASSLC },
	{ MACH_MACP550,		"Performa 550",		MACH_CLASSLC },
	{ MACH_MACP580,		"Performa 580",		MACH_CLASSQ2 },
	{ MACH_MACTV,		"TV",			MACH_CLASSLC },

/* The LCs... */
	{ MACH_MACLCII,		"LC II",		MACH_CLASSLC },
	{ MACH_MACLCIII,	"LC III",		MACH_CLASSLC },
	{ MACH_MACLC475,	"LC 475",		MACH_CLASSQ },
	{ MACH_MACLC475_33,	"LC 475/33",		MACH_CLASSQ },
	{ MACH_MACLC520,	"LC 520",		MACH_CLASSLC },
	{ MACH_MACLC575,	"LC 575",		MACH_CLASSQ2 },
	{ MACH_MACCCLASSIC,	"Color Classic",	MACH_CLASSLC },
	{ MACH_MACCCLASSICII,	"Color ClassicII",	MACH_CLASSLC },
/* Does this belong here? */
	{ MACH_MACCLASSICII,	"Classic II",		MACH_CLASSLC },

/* The unknown one and the end... */
	{ 0,			"Unknown",		MACH_CLASSII }
};				/* End of cpu_models[] initialization. */

struct intvid_info_t {
	int	machineid;
	u_long	fbbase;
	u_long	fbmask;
	u_long	fblen;
} intvid_info[] = {
	{ MACH_MACCLASSICII,	0x009f9a80,	0x0,		21888 },
	{ MACH_MACPB140,	0xfee08000,	0x0,		32 * 1024 },
	{ MACH_MACPB145,	0xfee08000,	0x0,		32 * 1024 },
	{ MACH_MACPB170,	0xfee08000,	0x0,		32 * 1024 },
	{ MACH_MACPB150,	0x60000000,	0x0,		128 * 1024 },
	{ MACH_MACPB160,	0x60000000,	0x0ffe0000,	128 * 1024 },
	{ MACH_MACPB165,	0x60000000,	0x0ffe0000,	128 * 1024 },
	{ MACH_MACPB180,	0x60000000,	0x0ffe0000,	128 * 1024 },
	{ MACH_MACPB210,	0x60000000,	0x0,		128 * 1024 },
	{ MACH_MACPB230,	0x60000000,	0x0,		128 * 1024 },
	{ MACH_MACPB250,	0x60000000,	0x0,		128 * 1024 },
	{ MACH_MACPB270,	0x60000000,	0x0,		128 * 1024 },
	{ MACH_MACPB280,	0x60000000,	0x0,		128 * 1024 },
	{ MACH_MACPB280C,	0x60000000,	0x0,		128 * 1024 },
	{ MACH_MACIICI,		0x0,		0x0,		320 * 1024 },
	{ MACH_MACIISI,		0x0,		0x0,		320 * 1024 },
	{ MACH_MACCCLASSIC,	0x50f40000,	0x0,		512 * 1024 },
/*??*/	{ MACH_MACLCII,		0x50f40000,	0x0,		512 * 1024 },
	{ MACH_MACPB165C,	0xfc040000,	0x0,		512 * 1024 },
	{ MACH_MACPB180C,	0xfc040000,	0x0,		512 * 1024 },
	{ MACH_MACPB190,	0x60000000,	0x0,		512 * 1024 },
	{ MACH_MACPB190CS,	0x60000000,	0x0,		512 * 1024 },
	{ MACH_MACPB500,	0x60000000,	0x0,		512 * 1024 },
	{ MACH_MACLCIII,	0x60b00000,	0x0,		768 * 1024 },
	{ MACH_MACLC520,	0x60000000,	0x0,		1024 * 1024 },
	{ MACH_MACP550,		0x60000000,	0x0,		1024 * 1024 },
	{ MACH_MACTV,		0x60000000,	0x0,		1024 * 1024 },
	{ MACH_MACLC475,	0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACLC475_33,	0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACLC575,	0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACC610,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACC650,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACP580,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ605,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ605_33,	0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ610,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ630,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ650,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACC660AV,	0x50100000,	0x0,		1024 * 1024 },
	{ MACH_MACQ700,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ800,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ900,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ950,		0xf9000000,	0x0,		1024 * 1024 },
	{ MACH_MACQ840AV,	0x50100000,	0x0,		2048 * 1024 },
	{ 0,			0x0,		0x0,		0 },
};				/* End of intvid_info[] initialization. */

/*
 * Missing Mac Models:
 *	PowerMac 6100
 *	PowerMac 7100
 *	PowerMac 8100
 *	PowerBook 540
 *	PowerBook 520
 *	PowerBook 150
 *	Duo 280
 *	Performa 6000s
 * 	...?
 */

char	cpu_model[120];		/* for sysctl() */

int
fpu_probe()
{
	/*
	 * A 68881 idle frame is 28 bytes and a 68882's is 60 bytes.
	 * We, of course, need to have enough room for either.
	 */
	int	fpframe[60 / sizeof(int)];
	label_t	faultbuf;
	u_char	b;

	nofault = (int *) &faultbuf;
	if (setjmp(&faultbuf)) {
		nofault = (int *) 0;
		return (FPU_NONE);
	}

	/*
	 * Synchronize FPU or cause a fault.
	 * This should leave the 881/882 in the IDLE state,
	 * state, so we can determine which we have by
	 * examining the size of the FP state frame
	 */
	asm("fnop");

	nofault = (int *) 0;

	/*
	 * Presumably, if we're an 040 and did not take exception
	 * above, we have an FPU.  Don't bother probing.
	 */
	if (mmutype == MMU_68040)
		return (FPU_68040);

	/*
	 * Presumably, this will not cause a fault--the fnop should
	 * have if this will.  We save the state in order to get the
	 * size of the frame.
	 */
	asm("movl %0, a0; fsave a0@" : : "a" (fpframe) : "a0" );

	b = *((u_char *) fpframe + 1);

	/*
	 * Now, restore a NULL state to reset the FPU.
	 */
	fpframe[0] = fpframe[1] = 0;
	m68881_restore((struct fpframe *) fpframe);

	/*
	 * The size of a 68881 IDLE frame is 0x18
	 *         and a 68882 frame is 0x38
	 */
	if (b == 0x18)
		return (FPU_68881);
	if (b == 0x38)
		return (FPU_68882);

	/*
	 * If it's not one of the above, we have no clue what it is.
	 */
	return (FPU_UNKNOWN);
}

void
identifycpu()
{
#ifdef DEBUG
	extern u_int delay_factor;
#endif

	/*
	 * Print the machine type...
	 */
	snprintf(cpu_model, sizeof cpu_model, "Apple Macintosh %s",
	    cpu_models[mac68k_machine.cpu_model_index].model);

	/*
	 * ... and the CPU type...
	 */
	switch (cputype) {
	case CPU_68040:
		strlcat(cpu_model, ", 68040 CPU", sizeof cpu_model);
		break;
	case CPU_68030:
		strlcat(cpu_model, ", 68030 CPU", sizeof cpu_model);
		break;
	case CPU_68020:
		strlcat(cpu_model, ", 68020 CPU", sizeof cpu_model);
		break;
	default:
		strlcat(cpu_model, ", unknown CPU", sizeof cpu_model);
		break;
	}

	/*
	 * ... and the MMU type...
	 */
	switch (mmutype) {
	case MMU_68040:
	case MMU_68030:
		strlcat(cpu_model, "+MMU", sizeof cpu_model);
		break;
	case MMU_68851:
		strlcat(cpu_model, ", MC68851 MMU", sizeof cpu_model);
		break;
	default:
		printf("%s\n", cpu_model);
		panic("unknown MMU type %d", mmutype);
		/* NOTREACHED */
	}

	/*
	 * ... and the FPU type...
	 */
	fputype = fpu_probe();	/* should eventually move to locore */

	switch (fputype) {
	case FPU_68040:
		strlcat(cpu_model, "+FPU", sizeof cpu_model);
		break;
	case FPU_68882:
		strlcat(cpu_model, ", MC6882 FPU", sizeof cpu_model);
		break;
	case FPU_68881:
		strlcat(cpu_model, ", MC6881 FPU", sizeof cpu_model);
		break;
	case FPU_UNKNOWN:
		strlcat(cpu_model, ", unknown FPU", sizeof cpu_model);
		break;
	default:
		/*strlcat(cpu_model, ", no FPU", sizeof cpu_model);*/
		break;
	}

	/*
	 * ... and finally, the cache type.
	 */
	if (cputype == CPU_68040)
		strlcat(cpu_model, ", 4k on-chip physical I/D caches", sizeof cpu_model);

	printf("%s\n", cpu_model);
#ifdef DEBUG
	printf("cpu: delay factor %d\n", delay_factor);
#endif
}

void	get_machine_info(void);

void
get_machine_info()
{
	int i;

	for (i = 0; cpu_models[i].machineid != 0; i++)
		if (mac68k_machine.machineid == cpu_models[i].machineid)
			break;

	mac68k_machine.cpu_model_index = i;
}

const struct cpu_model_info *current_mac_model;

/*
 * Sets a bunch of machine-specific variables
 */
void	setmachdep(void);

void
setmachdep()
{
	struct cpu_model_info *cpui;

	/*
	 * First, set things that need to be set on the first pass only
	 * Ideally, we'd only call this once, but for some reason, the
	 * VIAs need interrupts turned off twice !?
	 */
	get_machine_info();

	load_addr = 0;
	cpui = &(cpu_models[mac68k_machine.cpu_model_index]);
	current_mac_model = cpui;

	mac68k_machine.via1_ipl = 1;
	mac68k_machine.aux_interrupts = 0;

	/*
	 * Set up any machine specific stuff that we have to before
	 * ANYTHING else happens
	 */
	switch (cpui->class) {	/* Base this on class of machine... */
	case MACH_CLASSII:
		VIA2 = VIA2OFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		via_reg(VIA1, vIER) = 0x7f;	/* disable VIA1 int */
		via_reg(VIA2, vIER) = 0x7f;	/* disable VIA2 int */
		break;
	case MACH_CLASSPB:
		VIA2 = VIA2OFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		/* Disable everything but PM; we need it. */
		via_reg(VIA1, vIER) = 0x6f;	/* disable VIA1 int */
		/* Are we disabling something important? */
		via_reg(VIA2, vIER) = 0x7f;	/* disable VIA2 int */
		if (cputype == CPU_68040)
			mac68k_machine.sonic = 1;
		break;
	case MACH_CLASSDUO:
		/*
		 * The Duo definitely does not use a VIA2, but it looks
		 * like the VIA2 functions might be on the MSC at the RBV
		 * locations.  The rest is copied from the Powerbooks.
		 */
		VIA2 = RBVOFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		/* Disable everything but PM; we need it. */
		via_reg(VIA1, vIER) = 0x6f;	/* disable VIA1 int */
		/* Are we disabling something important? */
		via_reg(VIA2, rIER) = 0x7f;	/* disable VIA2 int */
		break;
	case MACH_CLASSQ:
	case MACH_CLASSQ2:
		VIA2 = VIA2OFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.sonic = 1;
		mac68k_machine.scsi96 = 1;
		via_reg(VIA1, vIER) = 0x7f;	/* disable VIA1 int */
		via_reg(VIA2, vIER) = 0x7f;	/* disable VIA2 int */

		/* Enable A/UX interrupt scheme */
		mac68k_machine.aux_interrupts = 1;
		via_reg(VIA1, vBufB) &= (0xff ^ DB1O_AuxIntEnb);
		via_reg(VIA1, vDirB) |= DB1O_AuxIntEnb;
		mac68k_machine.via1_ipl = 6;

		break;
	case MACH_CLASSAV:
		VIA2 = VIA2OFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi96 = 1;
		via_reg(VIA1, vIER) = 0x7f;	/* disable VIA1 int */
		via_reg(VIA2, vIER) = 0x7f;	/* disable VIA2 int */
		break;
	case MACH_CLASSIIci:
		VIA2 = RBVOFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		via_reg(VIA1, vIER) = 0x7f;	/* disable VIA1 int */
		via_reg(VIA2, rIER) = 0x7f;	/* disable RBV int */
		break;
	case MACH_CLASSIIsi:
		VIA2 = RBVOFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		via_reg(VIA1, vIER) = 0x7f;	/* disable VIA1 int */
		via_reg(VIA2, rIER) = 0x7f;	/* disable RBV int */
		break;
	case MACH_CLASSIIvx:
		VIA2 = RBVOFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		via_reg(VIA1, vIER) = 0x7f;	/* disable VIA1 int */
		via_reg(VIA2, rIER) = 0x7f;	/* disable RBV int */
		break;
	case MACH_CLASSLC:
		VIA2 = RBVOFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		via_reg(VIA1, vIER) = 0x7f;	/* disable VIA1 int */
		via_reg(VIA2, rIER) = 0x7f;	/* disable RBV int */
		break;
	case MACH_CLASSIIfx:
		VIA2 = OSSOFF;
		IOBase = 0x50f00000;
		Via1Base = (volatile u_char *)IOBase;
		mac68k_machine.scsi80 = 1;
		via_reg(VIA1, vIER) = 0x7f;  /* disable VIA1 int */
		break;
	default:
	case MACH_CLASSH:
		break;
	}
}

/*
 * Set IO offsets.
 */
void
mac68k_set_io_offsets(base)
	vaddr_t base;
{
	extern volatile u_char *sccA;

	switch (current_mac_model->class) {
	case MACH_CLASSQ:
		Via1Base = (volatile u_char *)base;

		/* The following two may be overriden. */
		sccA = (volatile u_char *)base + 0xc000;
		SCSIBase = base + 0xf000;

		switch (current_mac_model->machineid) {
		case MACH_MACQ900:
		case MACH_MACQ950:
			sccA = (volatile u_char *)base + 0xc020;
			iop_serial_compatible();
			mac68k_machine.scsi96_2 = 1;
			break;
		case MACH_MACQ700:
			break;
		default:
			SCSIBase = base + 0x10000;
			break;
		}
		break;
	case MACH_CLASSQ2:
		/*
		 * Note the different offset for sccA for this class of
		 * machines.  This seems to be common on many of the
		 * Quadra-type machines.
		 */
		Via1Base = (volatile u_char *)base;
		sccA = (volatile u_char *)base + 0xc020;
		SCSIBase = base + 0x10000;
		break;
	case MACH_CLASSAV:
		Via1Base = (volatile u_char *)base;
		sccA = (volatile u_char *)base + 0x4000;
		SCSIBase = base + 0x18000;
		PSCBase = (volatile u_char *)base + 0x31000;
		break;
	case MACH_CLASSII:
	case MACH_CLASSPB:
	case MACH_CLASSDUO:
	case MACH_CLASSIIci:
	case MACH_CLASSIIsi:
	case MACH_CLASSIIvx:
	case MACH_CLASSLC:
		Via1Base = (volatile u_char *)base;
		sccA = (volatile u_char *)base + 0x4000;
		SCSIBase = base;
		break;
	case MACH_CLASSIIfx:
		Via1Base = (volatile u_char *)base;
		sccA = (volatile u_char *)base + 0x4020;
		iop_serial_compatible();
		SCSIBase = base;
		break;
	default:
	case MACH_CLASSH:
		panic("Unknown/unsupported machine class (%d).",
		    current_mac_model->class);
		break;
	}

	Via2Base = Via1Base + 0x2000 * VIA2;
}

#if GRAYBARS
u_long gray_nextaddr = 0;

void
gray_bar()
{
	static int i = 0;
	static int flag = 0;

/* MF basic premise as I see it:
	1) Save the scratch regs as they are not saved by the compilier.
   	2) Check to see if we want gray bars, if so,
		display some lines of gray,
		a couple of lines of white(about 8),
		and loop to slow this down.
   	3) restore regs
*/

	__asm __volatile ("	movl a0,sp@-;
				movl a1,sp@-;
				movl d0,sp@-;
				movl d1,sp@-");

/* check to see if gray bars are turned off */
	if (mac68k_machine.do_graybars) {
		/* MF the 10*rowbytes/4 is done lots, but we want this to be
		 * slow */
		for (i = 0; i < 10 * videorowbytes / 4; i++)
			((u_long *)videoaddr)[gray_nextaddr++] = 0xaaaaaaaa;
		for (i = 0; i < 2 * videorowbytes / 4; i++)
			((u_long *)videoaddr)[gray_nextaddr++] = 0x00000000;
	}

	__asm __volatile ("	movl sp@+,d1;
				movl sp@+,d0;
				movl sp@+,a1;
				movl sp@+,a0");
}
#endif

/* in locore */
extern u_long ptest040(caddr_t addr, u_int fc);
extern int get_pte(u_int addr, u_long pte[2], u_short * psr);

/*
 * LAK (7/24/94): given a logical address, puts the physical address
 *  in *phys and return 1, or returns 0 on failure.  This is intended
 *  to look through MacOS page tables.
 */

u_long
get_physical(u_int addr, u_long * phys)
{
	extern u_int macos_tc;
	u_long pte[2], ph, mask;
	u_short psr;
	int i, numbits;

	if (mmutype == MMU_68040) {
		ph = ptest040((caddr_t)addr, FC_SUPERD);
		if ((ph & MMU40_RES) == 0) {
			ph = ptest040((caddr_t)addr, FC_USERD);
			if ((ph & MMU40_RES) == 0)
				return 0;
		}
		if ((ph & MMU40_TTR) != 0)
			ph = addr;

		mask = (macos_tc & 0x4000) ? 0x00001fff : 0x00000fff;
		ph &= (~mask);
	} else {
		i = get_pte(addr, pte, &psr);

		switch (i) {
		case -1:
			return 0;
		case 0:
			ph = pte[0] & 0xFFFFFF00;
			break;
		case 1:
			ph = pte[1] & 0xFFFFFF00;
			break;
		default:
			panic("get_physical(): bad get_pte()");
		}

		/*
		 * We must now figure out how many levels down we went and
		 * mask the bits appropriately -- the returned value may only
		 * be the upper n bits, and we have to take the rest from addr.
		 */
		numbits = 0;
		psr &= 0x0007;		/* Number of levels we went */
		for (i = 0; i < psr; i++)
			numbits += (macos_tc >> (12 - i * 4)) & 0x0f;

		/*
		 * We have to take the most significant "numbits" from
		 * the returned value "ph", and the rest from our addr.
		 * Assume that numbits != 0.
		 */
		mask = (1 << (32 - numbits)) - 1;
	}
	*phys = ph + (addr & mask);

	return 1;
}

void	check_video(char *, u_long, u_long);

void
check_video(id, limit, maxm)
	char *id;
	u_long limit, maxm;
{
	u_long addr, phys;

	if (!get_physical(videoaddr, &phys)) {
		if (mac68k_machine.do_graybars)
			printf("get_mapping(): %s.  False start.\n", id);
	} else {
		mac68k_vidlog = videoaddr;
		mac68k_vidphys = phys;
		mac68k_vidlen = 32768;
		addr = videoaddr + 32768;
		while (get_physical(addr, &phys)) {
			if ((phys - mac68k_vidphys) != mac68k_vidlen)
				break;
			if (mac68k_vidlen + 32768 > limit) {
				if (mac68k_machine.do_graybars) {
					printf("mapping: %s.  Does it never end?\n",
					    id);
					printf("    Forcing VRAM size ");
					printf("to a conservative %ldK.\n",
					    maxm/1024);
				}
				mac68k_vidlen = maxm;
				break;
			}
			mac68k_vidlen += 32768;
			addr += 32768;
		}
		if (mac68k_machine.do_graybars) {
			printf("  %s internal video at paddr 0x%x, len 0x%x.\n",
			    id, mac68k_vidphys, mac68k_vidlen);
		}
	}
}

/*
 * Find out how MacOS has mapped itself so we can do the same thing.
 * Returns the address of logical 0 so that locore can map the kernel
 * properly.
 */
u_int
get_mapping(void)
{
	struct intvid_info_t *iip;
	u_long addr, lastpage, phys, len, limit;
	int i, last, same;

	numranges = 0;
	for (i = 0; i < 8; i++) {
		low[i] = 0;
		high[i] = 0;
	}

	lastpage = get_top_of_ram();

	get_physical(0, &load_addr);

	last = 0;
	for (addr = 0; addr <= lastpage && get_physical(addr, &phys);
	    addr += PAGE_SIZE) {
		if (numranges > 0 && phys != high[last]) {
			/*
			 * Attempt to find if this page is already
			 * accounted for in an existing physical segment.
			 */
			for (i = 0; i < numranges; i++) {
				if (low[i] <= phys && phys <= high[i]) {
					last = i;
					break;
				}
			}
			if (i >= numranges)
				last = numranges - 1;

			if (low[last] <= phys && phys < high[last])
				continue;	/* Skip pages we've seen. */
		}

		if (numranges > 0 && phys == high[last]) {
			/* Common case: extend existing segment on high end */
			high[last] += PAGE_SIZE;
		} else {
			/* This is a new physical segment. */
			for (last = 0; last < numranges; last++)
				if (phys < low[last])
					break;

			/* Create space for segment, if necessary */
			if (last < numranges && phys < low[last]) {
				for (i = numranges; i > last; i--) {
					low[i] = low[i - 1];
					high[i] = high[i - 1];
				}
			}

			numranges++;
			low[last] = phys;
			high[last] = phys + PAGE_SIZE;
		}

		/* Coalesce adjoining segments as appropriate */
		if (last < (numranges - 1) && high[last] == low[last + 1] &&
		    low[last + 1] != load_addr) {
			high[last] = high[last + 1];
			for (i = last + 1; i < numranges; i++) {
				low[i] = low[i + 1];
				high[i] = high[i + 1];
			}
			--numranges;
		}
	}
	if (mac68k_machine.do_graybars) {
		printf("System RAM: %ld bytes in %ld pages.\n",
		    addr, addr / PAGE_SIZE);
		for (i = 0; i < numranges; i++) {
			printf("     Low = 0x%lx, high = 0x%lx\n",
			    low[i], high[i]);
		}
	}

	/*
	 * If we can't figure out the PA of the frame buffer by groveling
	 * the page tables, assume that we already have the correct
	 * address.  This is the case on several of the PowerBook 1xx
	 * series, in particular.
	 */
	if (!get_physical(videoaddr, &phys))
		phys = videoaddr;

	/*
	 * Find on-board video, if we have an idea of where to look
	 * on this system.
	 */
	for (iip = intvid_info; iip->machineid != 0; iip++)
		if (mac68k_machine.machineid == iip->machineid)
			break;

	if (mac68k_machine.machineid == iip->machineid &&
	    (phys & ~iip->fbmask) >= iip->fbbase &&
	    (phys & ~iip->fbmask) < (iip->fbbase + iip->fblen)) {
		mac68k_vidphys = phys & ~iip->fbmask;
		mac68k_vidlen = 32768 - (phys & 0x7fff);

		limit = iip->fbbase + iip->fblen - mac68k_vidphys;
		if (mac68k_vidlen > limit) {
			mac68k_vidlen = limit;
		} else {
			addr = videoaddr + mac68k_vidlen;
			while (get_physical(addr, &phys)) {
				if ((phys - mac68k_vidphys) != mac68k_vidlen)
					break;
				if (mac68k_vidlen + 32768 > limit) {
					mac68k_vidlen = limit;
					break;
				}
				mac68k_vidlen += 32768;
				addr += 32768;
			}
		}
	}

	if (mac68k_vidlen > 0) {
		/*
		 * We've already figured out where internal video is.
		 * Tell the user what we know.
		 */
		if (mac68k_machine.do_graybars)
			printf("On-board video at addr 0x%lx (phys 0x%x), len 0x%x.\n",
			    videoaddr, mac68k_vidphys, mac68k_vidlen);
	} else {
		/*
	 	* We should now look through all of NuBus space to find where
	 	* the internal video is being mapped.  Just to be sure we
	 	* handle all the cases, we simply map our NuBus space exactly
	 	* how MacOS did it.  As above, we find a bunch of ranges that
	 	* are contiguously mapped.  Since there are a lot of pages
	 	* that are all mapped to 0, we handle that as a special case
	 	* where the length is negative.  We search in increments of
	 	* 32768 because that's the page size that MacOS uses.
		*/
		nbnumranges = 0;
		for (i = 0; i < NBMAXRANGES; i++) {
			nbphys[i] = 0;
			nblog[i] = 0;
			nblen[i] = 0;
		}

		same = 0;
		for (addr = NBBASE; addr < NBTOP; addr += 32768) {
			if (!get_physical(addr, &phys)) {
				continue;
			}
			len = nbnumranges == 0 ? 0 : nblen[nbnumranges - 1];

#if DEBUG
			if (mac68k_machine.do_graybars)
				printf ("0x%lx --> 0x%lx\n", addr, phys);
#endif
			if (nbnumranges > 0
			    && addr == nblog[nbnumranges - 1] + len
			    && phys == nbphys[nbnumranges - 1]) {
				/* Same as last one */
				nblen[nbnumranges - 1] += 32768;
				same = 1;
			} else {
				if ((nbnumranges > 0)
				    && !same
				    && (addr == nblog[nbnumranges - 1] + len)
				    && (phys == nbphys[nbnumranges - 1] + len))
					nblen[nbnumranges - 1] += 32768;
				else {
					if (same) {
						nblen[nbnumranges - 1] = -len;
						same = 0;
					}
					if (nbnumranges == NBMAXRANGES) {
						if (mac68k_machine.do_graybars)
							printf("get_mapping(): "
							    "Too many NuBus ranges.\n");
						break;
					}
					nbnumranges++;
					nblog[nbnumranges - 1] = addr;
					nbphys[nbnumranges - 1] = phys;
					nblen[nbnumranges - 1] = 32768;
				}
			}
		}
		if (same) {
			nblen[nbnumranges - 1] = -nblen[nbnumranges - 1];
			same = 0;
		}
		if (mac68k_machine.do_graybars) {
			printf("Non-system RAM (nubus, etc.):\n");
			for (i = 0; i < nbnumranges; i++) {
				printf("     Log = 0x%lx, Phys = 0x%lx, Len = 0x%lx (%lu)\n",
				    nblog[i], nbphys[i], nblen[i], nblen[i]);
			}
		}

		/*
		 * We must now find the logical address of internal video in the
		 * ranges we made above.  Internal video is at physical 0, but
		 * a lot of pages map there.  Instead, we look for the logical
		 * page that maps to 32768 and go back one page.
		 */
		for (i = 0; i < nbnumranges; i++) {
			if (nblen[i] > 0
			    && nbphys[i] <= 32768
			    && 32768 <= nbphys[i] + nblen[i]) {
				mac68k_vidlog = nblog[i] - nbphys[i];
				mac68k_vidlen = nblen[i] + nbphys[i];
				mac68k_vidphys = 0;
				break;
			}
		}
		if (i == nbnumranges) {
			if (0x60000000 <= videoaddr && videoaddr < 0x70000000) {
				if (mac68k_machine.do_graybars)
					printf("Checking for Internal Video ");
				/*
				 * Kludge for IIvx internal video (60b0 0000).
				 * PB 520 (6000 0000)
				 */
				check_video("PB/IIvx (0x60?00000)",
				    1 * 1024 * 1024, 1 * 1024 * 1024);
			} else if (0x50F40000 <= videoaddr
			    && videoaddr < 0x50FBFFFF) {
				/*
				 * Kludge for LC internal video
				 */
				check_video("LC video (0x50f40000)",
				    512 * 1024, 512 * 1024);
			} else if (0x50100100 <= videoaddr
			    && videoaddr < 0x50400000) {
				/*
				 * Kludge for AV internal video
				 */
				check_video("AV video (0x50100100)",
				    1 * 1024 * 1024, 1 * 1024 * 1024);
			} else {
				if (mac68k_machine.do_graybars)
					printf( "  no internal video at address 0 -- "
						"videoaddr is 0x%lx.\n", videoaddr);
			}
		} else {
			if (mac68k_machine.do_graybars) {
				printf("  Video address = 0x%lx\n", videoaddr);
				printf("  Int video starts at 0x%x\n",
				    mac68k_vidlog);
				printf("  Length = 0x%x (%d) bytes\n",
				    mac68k_vidlen, mac68k_vidlen);
			}
		}
	}

	return load_addr;	/* Return physical address of logical 0 */
}

#ifdef DEBUG
/*
 * Debugging code for locore page-traversal routine.
 */
void printstar(void);
void
printstar(void)
{
	/*
	 * Be careful as we assume that no registers are clobbered
	 * when we call this from assembly.
	 */
	__asm __volatile ("	movl a0,sp@-;
				movl a1,sp@-;
				movl d0,sp@-;
				movl d1,sp@-");

	/* printf("*"); */

	__asm __volatile ("	movl sp@+,d1;
				movl sp@+,d0;
				movl sp@+,a1;
				movl sp@+,a0");
}
#endif

/*
 * Console bell callback; modularizes the console terminal emulator
 * and the audio system, so neither requires direct knowledge of the
 * other.
 */

void
mac68k_set_bell_callback(callback, cookie)
	int (*callback)(void *, int, int, int);
	void *cookie;
{
	mac68k_bell_callback = callback;
	mac68k_bell_cookie = (caddr_t)cookie;
}

int
mac68k_ring_bell(freq, length, volume)
	int freq, length, volume;
{
	if (mac68k_bell_callback)
		return ((*mac68k_bell_callback)(mac68k_bell_cookie,
		    freq, length, volume));
	else
		return (ENXIO);
}