root/sys/arch/mac68k/mac68k/machdep.c
/*      $NetBSD: machdep.c,v 1.389 2026/06/13 15:28:09 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.389 2026/06/13 15:28:09 thorpej Exp $");

#include "opt_adb.h"
#include "opt_copy_symtab.h"
#include "opt_ddb.h"
#include "opt_ddbparam.h"
#include "opt_kgdb.h"
#include "opt_mac68k.h"
#include "opt_modular.h"

#include "akbd.h"
#include "audio.h"
#include "genfb.h"
#include "macfb.h"
#include "zsc.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/core.h>
#include <sys/exec.h>
#include <sys/extent.h>
#include <sys/file.h>
#include <sys/kcore.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/msgbuf.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/reboot.h>
#include <sys/signalvar.h>
#include <sys/syscallargs.h>
#include <sys/vnode.h>
#include <sys/ksyms.h>
#include <sys/module.h>
#ifdef  KGDB
#include <sys/kgdb.h>
#endif
#include <sys/exec_elf.h>
#include <sys/device.h>
#include <sys/cpu.h>

#include <m68k/cacheops.h>
#include <m68k/mmu_40.h>

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

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/reg.h>
#include <machine/pcb.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/kcore.h>      /* XXX should be pulled in by sys/kcore.h */
#include <machine/video.h>

#define MAXMEM  64*1024 /* XXX - from cmap.h */
#include <uvm/uvm_extern.h>

#include <sys/sysctl.h>

#include <dev/cons.h>
#include <dev/mm.h>

#include <machine/iopreg.h>
#include <machine/psc.h>
#include <machine/viareg.h>
#include <mac68k/mac68k/macrom.h>
#include <mac68k/dev/adbvar.h>
#if NAKBD > 0
#include <mac68k/dev/akbdvar.h>
#endif
#if NMACFB > 0
#include <mac68k/dev/macfbvar.h>
#endif
#include <mac68k/dev/pm_direct.h>
#include <mac68k/dev/zs_cons.h>

#include "ksyms.h"

int symsize, end, *ssym, *esym;

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: */
int numranges;
u_long low[8];
u_long high[8];
u_long last_page;       /* PA of last physical page */
vaddr_t newvideoaddr;
int vidlen;

extern paddr_t avail_start, avail_end;

extern int machineid;

/* 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. */

/* Definitions for the variables defined in machine/video.h */
struct mac68k_video mac68k_video;

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

int     maxmem;                 /* max memory per process */

/*
 * 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.
 */
static long iomem_ex_storage[EXTENT_FIXED_STORAGE_SIZE(8) / sizeof(long)];
struct extent *iomem_ex;
int iomem_malloc_safe;

static u_long   get_physical(u_int, u_long *);

void    initcpu(void);
int     cpu_dumpsize(void);
int     cpu_dump(int (*)(dev_t, daddr_t, void *, size_t), daddr_t *);
void    cpu_init_kcore_hdr(void);

void            getenvvars(u_long, char *);
static long     getenv(const char *);

/* functions called from locore.s */
void    dumpsys(void);
void    machine_init(paddr_t);
void    nmihand(struct frame);

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

/*
 * XXX: For zs serial driver. We always initialize the base address
 * to avoid a bunch of #ifdefs.
 */
volatile unsigned char *sccA = 0;

/*
 * Early initialization, before main() is called.
 */
void
machine_init(paddr_t nextpa)
{
        int i;

        /*
         * 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);
        }

#ifdef __HAVE_NEW_PMAP_68K
        /*
         * We mapped the kernel text read/write in pmap_bootstrap1() to
         * deal with the vectors and Mac ROM variable region.  Go ahead
         * and write-protect &start - &etext here.
         */
        extern char start[], etext[];
        pmap_protect(pmap_kernel(), m68k_round_page((vaddr_t)start),
            m68k_trunc_page((vaddr_t)etext),
            UVM_PROT_READ | UVM_PROT_EXEC);
        pmap_update(pmap_kernel());
#endif /* __HAVE_NEW_PMAP_68K */

        /*
         * 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,
            (void *)iomem_ex_storage, sizeof(iomem_ex_storage),
            EX_NOCOALESCE|EX_NOWAIT);

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

        /* Initialize the IOPs (if present) */
        iop_init(1);

        /*
         * 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)msgbufaddr + i * PAGE_SIZE,
                    high[numranges - 1] + i * PAGE_SIZE,
                    VM_PROT_READ|VM_PROT_WRITE,
                    VM_PROT_READ|VM_PROT_WRITE|PMAP_WIRED);
        initmsgbuf(msgbufaddr, m68k_round_page(MSGBUFSIZE));
        pmap_update(pmap_kernel());
}

/*
 * 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();
                init = 1;
        } else {
#if NAKBD > 0 && (NMACFB + NGENFB) > 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, maccninit() ignores the first call
                 * it gets (from cninit(), if not on a serial console).
                 * Once we're here, we call maccninit() again, which sets
                 * up the console devices and does the appropriate wscons
                 * initialization.
                 */
                if (mac68k_machine.serial_console == 0) {
                        void maccninit(struct consdev *);
                        maccninit(NULL);
                }
#endif

                mac68k_calibrate_delay();

#if NZSC > 0 && defined(KGDB)
                zs_kgdb_init();
#endif
#if NKSYMS || defined(DDB) || defined(MODULAR)
                /*
                 * Initialize kernel debugger, if compiled in.
                 */

                ksyms_addsyms_elf(symsize, ssym, esym);
#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, make
 * (most of) kernel text read-only, and other miscellaneous bits
 */
void
cpu_startup(void)
{
        int vers;
        int xdelay;

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

        cpu_startup_common();

        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 (xdelay = 0; xdelay < 1000000; xdelay++);
        }

        extern u_int delay_factor;
        printf("cpu: delay factor %d\n", delay_factor);

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

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

void
initcpu(void)
{
        /* Invalidate supervisor mode data cache. */
        DCIS();
}

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

struct pcb dumppcb;

void
machine_powerdown(void)
{
        /* First try to power down under VIA control. */
        via_powerdown();

#ifndef MRG_ADB
        /*
         * Shut down machines whose power functions are accessed
         * via modified ADB calls.  adb_poweroff() is available
         * only when the MRG ADB is not being used.
         */
        adb_poweroff();
#endif
        /*
         * Try to shutdown via the power manager (PowerBooks mainly).
         */
        pm_poweroff();
}

void
machine_reboot(int howto, char *bootstr)
{
        extern u_long last_page;

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

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

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

/*
 * Compute the size of the machine-dependent crash dump header.
 * Returns size in disk blocks.
 */

#define CHDRSIZE (ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t)))
#define MDHDRSIZE roundup(CHDRSIZE, dbtob(1))

int
cpu_dumpsize(void)
{

        return btodb(MDHDRSIZE);
}

/*
 * Called by dumpsys() to dump the machine-dependent header.
 */
int
cpu_dump(int (*dump)(dev_t, daddr_t, void *, size_t), daddr_t *blknop)
{
        int buf[MDHDRSIZE / 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 = MDHDRSIZE - ALIGN(sizeof(kcore_seg_t));

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

/*
 * These variables are needed by /sbin/savecore
 */
u_int32_t 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 PAGE_SIZE 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
cpu_dumpconf(void)
{
        cpu_kcore_hdr_t *h = &cpu_kcore_hdr;
        struct m68k_kcore_hdr *m = &h->un._m68k;
        int chdrsize;   /* size of dump header */
        int nblks;      /* size of dump area */
        int i;

        if (dumpdev == NODEV)
                return;

        nblks = bdev_size(dumpdev);
        chdrsize = cpu_dumpsize();

        dumpsize = 0;
        for (i = 0; i < M68K_NPHYS_RAM_SEGS && m->ram_segs[i].size; i++)
                dumpsize += btoc(m->ram_segs[i].size);

        /*
         * Check to see if we will fit.  Note we always skip the
         * first PAGE_SIZE in case there is a disk label there.
         */
        if (nblks < (ctod(dumpsize) + chdrsize + ctod(1))) {
                dumpsize = 0;
                dumplo = -1;
                return;
        }

        /*
         * Put dump at the end of the partition.
         */
        dumplo = (nblks - 1) - ctod(dumpsize) - chdrsize;
}

void
dumpsys(void)
{
        cpu_kcore_hdr_t *h = &cpu_kcore_hdr;
        struct m68k_kcore_hdr *m = &h->un._m68k;
        const struct bdevsw *bdev;
        daddr_t blkno;          /* current block to write */
                                /* dump routine */
        int (*dump)(dev_t, daddr_t, void *, size_t);
        int pg;                 /* page being dumped */
        paddr_t maddr;          /* PA being dumped */
        int seg;                /* RAM segment being dumped */
        int error;              /* error code from (*dump)() */

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

        /* Make sure dump device is valid. */
        if (dumpdev == NODEV)
                return;
        bdev = bdevsw_lookup(dumpdev);
        if (bdev == NULL)
                return;
        if (dumpsize == 0) {
                cpu_dumpconf();
                if (dumpsize == 0)
                        return;
        }
        if (dumplo <= 0) {
                printf("\ndump to dev %u,%u not possible\n",
                    major(dumpdev), minor(dumpdev));
                return;
        }
        dump = bdev->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/PAGE_SIZE)
                /* print out how many MBs we have dumped */
                if (pg && (pg % NPGMB) == 0)
                        printf("%d ", pg / NPGMB);
#undef NPGMB
                while (maddr >=
                    (m->ram_segs[seg].start + m->ram_segs[seg].size)) {
                        if (++seg >= M68K_NPHYS_RAM_SEGS ||
                            m->ram_segs[seg].size == 0) {
                                error = EINVAL;         /* XXX ?? */
                                goto bad;
                        }
                        maddr = m->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, PAGE_SIZE);
 bad:
                switch (error) {
                case 0:
                        maddr += PAGE_SIZE;
                        blkno += btodb(PAGE_SIZE);
                        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");
}

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

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

        if (nmihanddeep++)
                return;
/*      regdump((struct trapframe *)&frame, 128);
        dumptrace(); */
#ifdef DDB
        printf("Panic switch: PC is 0x%x.\n", frame.f_pc);
        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(void)
{
        return ((mac68k_machine.mach_memsize * (1024 * 1024)) - PAGE_SIZE);
}

/*
 * machine dependent system variables.
 */
SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup")
{

        sysctl_createv(clog, 0, NULL, NULL,
                       CTLFLAG_PERMANENT,
                       CTLTYPE_NODE, "machdep", NULL,
                       NULL, 0, NULL, 0,
                       CTL_MACHDEP, CTL_EOL);

        sysctl_createv(clog, 0, NULL, NULL,
                       CTLFLAG_PERMANENT,
                       CTLTYPE_STRUCT, "console_device", NULL,
                       sysctl_consdev, 0, NULL, sizeof(dev_t),
                       CTL_MACHDEP, CPU_CONSDEV, CTL_EOL);
}

static char *envbuf = NULL;

/*
 * getenvvars: Grab a few useful variables
 */

void
getenvvars(u_long flag, char *buf)
{
        extern u_long bootdev;
        extern u_long macos_boottime, MacOSROMBase;
        extern long macos_gmtbias;
        int root_scsi_id;
        u_long root_ata_dev;
        int i;
        Elf_Ehdr *ehdr;
        Elf_Shdr *shp;
        vaddr_t minsym;

        /*
         * 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 */
        /* mac68k_video.mv_kvaddr = getenv("MACOS_VIDEO"); */

        mac68k_video.mv_kvaddr = getenv("VIDEO_ADDR");
        mac68k_video.mv_stride = getenv("ROW_BYTES");
        mac68k_video.mv_depth = getenv("SCREEN_DEPTH");
        mac68k_video.mv_width = getenv("DIMENSIONS") & 0xffff;
        mac68k_video.mv_height = (getenv("DIMENSIONS") >> 16) & 0xffff;

        /*
         * More misc stuff from booter.
         */
        mac68k_machine.machineid = machineid = getenv("MACHINEID");
        mac68k_machine.mach_processor = getenv("PROCESSOR");
#ifndef MAC68K_MEMSIZE
        mac68k_machine.mach_memsize = getenv("MEMSIZE");
#else
        mac68k_machine.mach_memsize = MAC68K_MEMSIZE;
#endif
        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_d_speed = 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");
        root_ata_dev = getenv("ROOT_ATA_DEV");
        if (((mac68k_machine.booter_version < CURRENTBOOTERVER) ||
            (flag & 0x40000)) && bootdev == 0) {
                if (root_ata_dev) {
                        /*
                         * Consider only internal IDE drive.
                         * Buses(=channel) will be always 0.
                         * Because 68k Mac has only single channel.
                         */
                        switch (root_ata_dev) {
                        default: /* fall through */
                        case 0xffffffe0: /* buses,drive = 0,0 */
                        case 0x20: /* buses,drive = 1,0 */
                        case 0x21: /* buses,drive = 1,1 */
                                bootdev = MAKEBOOTDEV(22, 0, 0, 0, 0);
                                break;
                        case 0xffffffe1: /* buses,drive = 0,1 */
                                bootdev = MAKEBOOTDEV(22, 0, 0, 1, 0);
                                break;
                        }
                } else {
                        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
         */
        esym = (int *)getenv("END_SYM");
#ifndef makeoptions_COPY_SYMTAB
        if (esym == (int *)0)
#endif
                esym = (int *)&end;

        /* 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 = (void *)getenv("ROMBASE");
        if (ROMBase == (void *)0) {
                ROMBase = (void *)ROMBASE;
        }
        MacOSROMBase = (unsigned long)ROMBase;
        TimeDBRA = getenv("TIMEDBRA");
        ADBDelay = (u_short)getenv("ADBDELAY");
        HwCfgFlags  = getenv("HWCFGFLAGS");
        HwCfgFlags2 = getenv("HWCFGFLAG2");
        HwCfgFlags3 = getenv("HWCFGFLAG3");
        ADBReInit_JTBL = getenv("ADBREINIT_JTBL");
        mrg_ADBIntrPtr = (void *)getenv("ADBINTERRUPT");

        /*
         * Check the ELF headers.
         */

        ehdr = (void *)getenv("MARK_SYM");
        if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0 ||
            ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
                return;
        }

        /*
         * Find the end of the symbols and strings.
         */

        minsym = ~0;
        shp = (Elf_Shdr *)(end + ehdr->e_shoff);
        for (i = 0; i < ehdr->e_shnum; i++) {
                if (shp[i].sh_type != SHT_SYMTAB &&
                    shp[i].sh_type != SHT_STRTAB) {
                        continue;
                }
                minsym = MIN(minsym, (vaddr_t)end + shp[i].sh_offset);
        }

        symsize = 1;
        ssym = (int *)ehdr;
}

static long
getenv(const 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;
        const char *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;
                }
        }
}

/*
 * ROM Vector information for calling drivers in ROMs
 *
 * According to information published on the Web by Apple, there have
 * been 9 different ROM families used in the Mac since the introduction
 * of the Lisa/XL through the latest PowerMacs (May 96).  Each family
 * has zero or more version variants and in some cases a version variant
 * may exist in one than one length format.  Generally any one specific
 * Mac will use a common set of routines within the ROM and a model-specific
 * set also in the ROM.  Luckily most of the routines used by NetBSD fall
 * into the common set and can therefore be defined in the ROM Family.
 * The offset addresses (address minus the ROM Base) of these common routines
 * is the same for all machines which use that ROM.  The offset addresses of
 * the machine-specific routines is generally different for each machine.
 * The machine-specific routines currently used by NetBSD/mac68k include:
 *       ADB_interrupt, PM_interrupt, ADBBase+130_interrupt,
 *       PMgrOp, jClkNoMem, Egret, InitEgret, and ADBReInit_JTBL
 *
 * It is possible that the routine at "jClkNoMem" is a common routine, but
 * some variation in addresses has been seen.  Also, execept for the very
 * earliest machines which used Egret, the machine-specific value of the
 * Egret routine may be unimportant as the machine-specific InitEgret code
 * seems to always set the OS Trap vector for Egret.
 *
 * Only three of the nine different ROMs are important to NetBSD/mac68k.
 * All other ROMs are used in early model Macs which are unable to run
 * NetBSD due to other hardware limitations such as 68000 CPU, no MMU
 * capability, or used only in PowerMacs.  The three that we are interested
 * in are:
 *
 * ROM Family $0178 - used in the II, IIx, IIcx, and SE/30
 *            All machines which use this ROM are now supported by NetBSD.
 *            There are no machine-dependent routines in these ROMs used by
 *            NetBSD/mac68k.  This ROM is always 256K in length.
 *
 * ROM Family $067c - used in Classic, Color Classic, Color Classic II,
 *                      IIci, IIsi, IIvi, IIvx, IIfx, LC, LC II, LC III,
 *                      LC III+, LC475, LC520, LC550, LC575, LC580, LC630,
 *                      MacTV, P200, P250, P275, P400/405/410/430, P450,
 *                      P460/466/467, P475/476, P520, P550/560, P575/577/578,
 *                      P580/588, P600, P630/631/635/636/637/638/640, Q605,
 *                      Q610, C610, Q630, C650, Q650, Q700, Q800, Q900, Q950,
 *                      PB140, PB145/145B, PB150, PB160, PB165, PB165c, PB170,
 *                      PB180, PB180c, Duo 210, Duo 230, Duo 250, Duo 270c,
 *                      Duo280, Duo 280c, PB 520/520c/540/540c/550
 *             This is the so-called "Universal" ROM used in almost all 68K
 *             machines. There are machine-dependent and machine-independent
 *             routines used by NetBSD/mac68k in this ROM, and except for the
 *             PowerBooks and the Duos, this ROM seems to be fairly well
 *             known by NetBSD/mac68k.  Desktop machines listed here that are
 *             not yet running NetBSD probably only lack the necessary
 *             addresses for the machine-dependent routines, or are waiting
 *             for IDE disk support.  This ROM is generally 1Meg in length,
 *             however when used in the IIci, IIfx, IIsi, LC, Classic II, and
 *             P400/405/410/430 it is 512K in length, and when used in the
 *             PB 520/520c/540/540c/550 it is 2Meg in length.
 *
 * ROM Family - $077d - used in C660AV/Q660AV, Q840AV
 *             The "Universal" ROM used on the PowerMacs and used in the
 *             68K line for the AV Macs only.  When used in the 68K AV
 *             machines the ROM is 2Meg in length; all uses in the PowerMac
 *             use a length of 4Meg.
 *
 *              Bob Nestor - <rnestor@metronet.com>
 */
static romvec_t romvecs[] =
{
        /* Vectors verified for II, IIx, IIcx, SE/30 */
        {                       /* 0 */
                "Mac II class ROMs",
                (void *)0x40807002,     /* where does ADB interrupt */
                (void *)0x0,            /* PM interrupt (?) */
                (void *)0x4080a4d8,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x40807778,     /* CountADBs */
                (void *)0x40807792,     /* GetIndADB */
                (void *)0x408077be,     /* GetADBInfo */
                (void *)0x408077c4,     /* SetADBInfo */
                (void *)0x40807704,     /* ADBReInit */
                (void *)0x408072fa,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080d6d0,     /* WriteParam */
                (void *)0x4080d6fa,     /* SetDateTime */
                (void *)0x4080dbe8,     /* InitUtil */
                (void *)0x4080dd78,     /* ReadXPRam */
                (void *)0x4080dd82,     /* WriteXPRam */
                (void *)0x4080ddd6,     /* jClkNoMem */
                (void *)0x0,            /* ADBAlternateInit */
                (void *)0x0,            /* Egret */
                (void *)0x0,            /* InitEgret */
                (void *)0x0,            /* ADBReInit_JTBL */
                (void *)0x0,            /* ROMResourceMap List Head */
                (void *)0x40814c58,     /* FixDiv */
                (void *)0x40814b64,     /* FixMul */
        },
        /*
         * Vectors verified for PB 140, PB 145, PB 170
         * (PB 100?)
         */
        {                       /* 1 */
                "Powerbook class ROMs",
                (void *)0x4088ae5e,     /* ADB interrupt */
                (void *)0x408885ec,     /* PB ADB interrupt */
                (void *)0x4088ae0e,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x408888ec,     /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x4080b1e4,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x40814800,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x0,            /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for IIsi, IIvx, IIvi
         */
        {                       /* 2 */
                "Mac IIsi class ROMs",
                (void *)0x40814912,     /* ADB interrupt */
                (void *)0x0,            /* PM ADB interrupt */
                (void *)0x408150f0,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x4080b1e4,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x40814800,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x0,            /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for Mac Classic II and LC II
         * (Other LC's?  680x0 Performas?)
         */
        {                       /* 3 */
                "Mac Classic II ROMs",
                (void *)0x40a14912,     /* ADB interrupt */
                (void *)0x0,            /* PM ADB interrupt */
                (void *)0x40a150f0,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x40a0a360,     /* CountADBs */
                (void *)0x40a0a37a,     /* GetIndADB */
                (void *)0x40a0a3a6,     /* GetADBInfo */
                (void *)0x40a0a3ac,     /* SetADBInfo */
                (void *)0x40a0a752,     /* ADBReInit */
                (void *)0x40a0a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x40a0c05c,     /* WriteParam */
                (void *)0x40a0c086,     /* SetDateTime */
                (void *)0x40a0c5cc,     /* InitUtil */
                (void *)0x40a0b186,     /* ReadXPRam */
                (void *)0x40a0b190,     /* WriteXPRam */
                (void *)0x40a0b1e4,     /* jClkNoMem */
                (void *)0x40a0a818,     /* ADBAlternateInit */
                (void *)0x40a14800,     /* Egret */
                (void *)0x40a147c4,     /* InitEgret */
                (void *)0x40a03ba6,     /* ADBReInit_JTBL */
                (void *)0x40a7eb90,     /* ROMResourceMap List Head */
                (void *)0x40a1c406,     /* FixDiv, wild guess */
                (void *)0x40a1c312,     /* FixMul, wild guess */
        },
        /*
         * Vectors verified for IIci, Q700
         */
        {                       /* 4 */
                "Mac IIci/Q700 ROMs",
                (void *)0x4080a700,     /* ADB interrupt */
                (void *)0x0,            /* PM ADB interrupt */
                (void *)0x4080a5aa,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x4080b1e4,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x0,            /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x0,            /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for Duo 230, PB 180, PB 160, PB 165/165C
         * (Duo 210?  Duo 250?  Duo 270?)
         */
        {                       /* 5 */
                "2nd Powerbook class ROMs",
                (void *)0x408b2eec,     /* ADB interrupt */
                (void *)0x408885ec,     /* PB ADB interrupt */
                (void *)0x408b2e76,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x408888ec,     /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b39b2,     /* jClkNoMem */ /* From PB180 */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x40814800,     /* Egret */
                (void *)0x40888400,     /* InitPwrMgr */ /* From PB180 */
                (void *)0x408cce28,     /* ADBReInit_JTBL -- from PB160*/
                (void *)0x4087eb90,     /* ROMRsrcMap List Head -- from PB160*/
                (void *)0x4081c406,     /* FixDiv, wild guess */
                (void *)0x4081c312,     /* FixMul, wild guess */
        },
        /*
         * Vectors verified for the Quadra, Centris 650
         *  (610, Q800?)
         */
        {                       /* 6 */
                "Quadra/Centris ROMs",
                (void *)0x408b2dea,     /* ADB int */
                (void *)0x0,            /* PM intr */
                (void *)0x408b2c72,     /* ADBBase + 130 */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x40809ae6,     /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b39b6,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x40814800,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x408d2b64,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv, wild guess */
                (void *)0x4081c312,     /* FixMul, wild guess */
        },
        /*
         * Vectors verified for the Quadra 660AV
         *  (Quadra 840AV?)
         */
        {                       /* 7 */
                "Quadra AV ROMs",
                (void *)0x4080cac6,     /* ADB int */
                (void *)0x0,            /* PM int */
                (void *)0x40805cd4,     /* ADBBase + 130 */
                (void *)0x40839600,     /* CountADBs */
                (void *)0x4083961a,     /* GetIndADB */
                (void *)0x40839646,     /* GetADBInfo */
                (void *)0x4083964c,     /* SetADBInfo */
                (void *)0x408397b8,     /* ADBReInit */
                (void *)0x4083967c,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4081141c,     /* WriteParam */
                (void *)0x4081144e,     /* SetDateTime */
                (void *)0x40811930,     /* InitUtil */
                (void *)0x4080b624,     /* ReadXPRam */
                (void *)0x4080b62e,     /* WriteXPRam */
                (void *)0x40806884,     /* jClkNoMem */
                (void *)0x408398c2,     /* ADBAlternateInit */
                (void *)0x4080cada,     /* Egret */
                (void *)0x4080de14,     /* InitEgret */
                (void *)0x408143b8,     /* ADBReInit_JTBL */
                (void *)0x409bdb60,     /* ROMResourceMap List Head */
                (void *)0x4083b3d8,     /* FixDiv */
                (void *)0x4083b2e4,     /* FixMul */
        },
        /*
         * PB 540, PB 550
         * (PB 520?  Duo 280?)
         */
        {                       /* 8 */
                "68040 PowerBook ROMs",
                (void *)0x400b2efc,     /* ADB int */
                (void *)0x400d8e66,     /* PM int */
                (void *)0x400b2e86,     /* ADBBase + 130 */
                (void *)0x4000a360,     /* CountADBs */
                (void *)0x4000a37a,     /* GetIndADB */
                (void *)0x4000a3a6,     /* GetADBInfo */
                (void *)0x4000a3ac,     /* SetADBInfo */
                (void *)0x4000a752,     /* ADBReInit */
                (void *)0x4000a3dc,     /* ADBOp */
                (void *)0x400d9302,     /* PmgrOp */
                (void *)0x4000c05c,     /* WriteParam */
                (void *)0x4000c086,     /* SetDateTime */
                (void *)0x4000c5cc,     /* InitUtil */
                (void *)0x4000b186,     /* ReadXPRam */
                (void *)0x4000b190,     /* WriteXPRam */
                (void *)0x400b3c08,     /* jClkNoMem */
                (void *)0x4000a818,     /* ADBAlternateInit */
                (void *)0x40009ae6,     /* Egret */ /* From PB520 */
                (void *)0x400147c4,     /* InitEgret */
                (void *)0x400a7a5c,     /* ADBReInit_JTBL */
                (void *)0x4007eb90,     /* ROMResourceMap List Head */
                (void *)0x4001c406,     /* FixDiv, wild guess */
                (void *)0x4001c312,     /* FixMul, wild guess */
        },
        /*
         * Verified for the Q605
         */
        {                       /* 9 */
                "Quadra/Centris 605 ROMs",
                (void *)0x408a9b56,     /* ADB int */
                (void *)0x0,            /* PM int */
                (void *)0x408b2f94,     /* ADBBase + 130 */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PmgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b3bf8,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x408a99c0,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x408a82c0,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for Duo 270c, PB150
         */
        {                       /* 10 */
                "Duo 270C ROMs",
                (void *)0x408b2efc,     /* ADB interrupt */
                (void *)0x408885ec,     /* PB ADB interrupt */
                (void *)0x408b2e86,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x408888ec,     /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b3bf8,     /* jClkNoMem */ /* from PB 150 */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x40814800,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x0,            /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv, wild guess */
                (void *)0x4081c312,     /* FixMul, wild guess */
        },
        /*
         * Vectors verified for Performa/LC 550
         */
        {                       /* 11 */
                "P/LC 550 ROMs",
                (void *)0x408d16d6,     /* ADB interrupt */
                (void *)0x0,            /* PB ADB interrupt */
                (void *)0x408b2f84,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b3c04,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x408d1450,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x408d24a4,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv for P550 */
                (void *)0x4081c312,     /* FixMul for P550 */
        },
        /*
         * Vectors verified for the MacTV
         */
        {                       /* 12 */
                "MacTV ROMs",
                (void *)0x40acfed6,     /* ADB interrupt */
                (void *)0x0,            /* PB ADB interrupt */
                (void *)0x40ab2f84,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x40a0a360,     /* CountADBs */
                (void *)0x40a0a37a,     /* GetIndADB */
                (void *)0x40a0a3a6,     /* GetADBInfo */
                (void *)0x40a0a3ac,     /* SetADBInfo */
                (void *)0x40a0a752,     /* ADBReInit */
                (void *)0x40a0a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x40a0c05c,     /* WriteParam */
                (void *)0x40a0c086,     /* SetDateTime */
                (void *)0x40a0c5cc,     /* InitUtil */
                (void *)0x40a0b186,     /* ReadXPRam */
                (void *)0x40a0b190,     /* WriteXPRam */
                (void *)0x40ab3bf4,     /* jClkNoMem */
                (void *)0x40a0a818,     /* ADBAlternateInit */
                (void *)0x40acfd40,     /* Egret */
                (void *)0x40a147c4,     /* InitEgret */
                (void *)0x40a038a0,     /* ADBReInit_JTBL */
                (void *)0x40a7eb90,     /* ROMResourceMap List Head */
                (void *)0x40a1c406,     /* FixDiv */
                (void *)0x40a1c312,     /* FixMul */
        },
        /*
         * Vectors verified for the Quadra630
         */
        {                       /* 13 */
                "Quadra630 ROMs",
                (void *)0x408a9bd2,     /* ADB int */
                (void *)0x0,            /* PM intr */
                (void *)0x408b2f94,     /* ADBBase + 130 */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0,              /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* Wild guess at ReadXPRam */
                (void *)0x4080b190,     /* Wild guess at WriteXPRam */
                (void *)0x408b39f4,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x408a99c0,     /* Egret */
                (void *)0x408147c8,     /* InitEgret */
                (void *)0x408a7ef8,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for LC III
         */
        {                       /* 14 */
                "LC III ROMs",
                (void *)0x40814912,     /* ADB interrupt */
                (void *)0x0,            /* PM ADB interrupt */
                (void *)0x408b2f94,     /* ADBBase + 130 interrupt */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b39b6,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x40814800,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x408d2918,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for the LC520
         */
        {                       /* 15 */
                "MacLC520 ROMs",
                (void *)0x408d16d6,     /* ADB interrupt */
                (void *)0x0,            /* PB ADB interrupt */
                (void *)0x408b2f84,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b3c04,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x408d1450,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x408d2460,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv for P520 */
                (void *)0x4081c312,     /* FixMul for P520 */
        },
        /*
         * Vectors verified for the LC 575/577/578
         */
        {                       /* 16 */
                "MacLC575 ROMs",
                (void *)0x408a9b56,     /* ADB interrupt */
                (void *)0x0,            /* PB ADB interrupt */
                (void *)0x408b2f94,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x408b3bf8,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x408a99c0,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x408a81a0,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv for P520 */
                (void *)0x4081c312,     /* FixMul for P520 */
        },
        /*
         * Vectors verified for the Quadra 950
         */
        {                       /* 17 */
                "Quadra950 class ROMs",
                (void *)0x40814912,     /* ADB interrupt */
                (void *)0x0,            /* PM ADB interrupt */
                (void *)0x4080a4d8,     /* ADBBase + 130 interrupt; whatzit? */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x4080b1e4,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x40814800,     /* Egret */
                (void *)0x408147c4,     /* InitEgret */
                (void *)0x408038bc,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for the Mac IIfx
         */
        {                       /* 18 */
                "Mac IIfx ROMs",
                (void *)0x40809f4a,     /* ADB interrupt */
                (void *)0x0,            /* PM ADB interrupt */
                (void *)0x4080a4d8,     /* ADBBase + 130 interrupt */
                (void *)0x4080a360,     /* CountADBs */
                (void *)0x4080a37a,     /* GetIndADB */
                (void *)0x4080a3a6,     /* GetADBInfo */
                (void *)0x4080a3ac,     /* SetADBInfo */
                (void *)0x4080a752,     /* ADBReInit */
                (void *)0x4080a3dc,     /* ADBOp */
                (void *)0x0,            /* PMgrOp */
                (void *)0x4080c05c,     /* WriteParam */
                (void *)0x4080c086,     /* SetDateTime */
                (void *)0x4080c5cc,     /* InitUtil */
                (void *)0x4080b186,     /* ReadXPRam */
                (void *)0x4080b190,     /* WriteXPRam */
                (void *)0x4080b1e4,     /* jClkNoMem */
                (void *)0x4080a818,     /* ADBAlternateInit */
                (void *)0x0,            /* Egret */
                (void *)0x0,            /* InitEgret */
                (void *)0x408037c0,     /* ADBReInit_JTBL */
                (void *)0x4087eb90,     /* ROMResourceMap List Head */
                (void *)0x4081c406,     /* FixDiv */
                (void *)0x4081c312,     /* FixMul */
        },
        /*
         * Vectors verified for the Performa 588 (and 580?)
         */
        {                       /* 19 */
                "Performa 580 ROMs",
                (void *) 0x4089a8be,    /* ADB interrupt */
                (void *) 0x0,           /* PM ADB interrupt */
                (void *) 0x408b2f94,    /* ADBBase + 130 interrupt */
                (void *) 0x4080a360,    /* CountADBs */
                (void *) 0x4080a37a,    /* GetIndADB */
                (void *) 0x4080a3a6,    /* GetADBInfo */
                (void *) 0x4080a3ac,    /* SetADBInfo */
                (void *) 0x4080a752,    /* ADBReInit */
                (void *) 0x4080a3dc,    /* ADBOp */
                (void *) 0x0,           /* PMgrOp */
                (void *) 0x4080c05c,    /* WriteParam */
                (void *) 0x4080c086,    /* SetDateTime */
                (void *) 0x4080c5cc,    /* InitUtil */
                (void *) 0x4080b186,    /* ReadXPRam */
                (void *) 0x4080b190,    /* WriteXPRam */
                (void *) 0x408b3bf4,    /* jClkNoMem */
                (void *) 0x4080a818,    /* ADBAlternateInit */
                (void *) 0x408a99c0,    /* Egret */
                (void *) 0x408147c8,    /* InitEgret */
                (void *) 0x408a7f74,    /* ADBReInit_JTBL */
                (void *) 0x4087eb90,    /* ROMResourceMap List Head */
                (void *) 0x4081c406,    /* FixDiv */
                (void *) 0x4081c312,    /* FixMul */
        },
        /* Please fill these in! -BG */
};


struct cpu_model_info cpu_models[] = {

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

/* The rest of the II series... */
        {MACH_MACIICI, "IIci ", "", MACH_CLASSIIci, &romvecs[4]},
        {MACH_MACIISI, "IIsi ", "", MACH_CLASSIIsi, &romvecs[2]},
        {MACH_MACIIVI, "IIvi ", "", MACH_CLASSIIvx, &romvecs[2]},
        {MACH_MACIIVX, "IIvx ", "", MACH_CLASSIIvx, &romvecs[2]},
        {MACH_MACIIFX, "IIfx ", "", MACH_CLASSIIfx, &romvecs[18]},

/* The Centris/Quadra series. */
        {MACH_MACQ700, "Quadra", " 700 ", MACH_CLASSQ, &romvecs[4]},
        {MACH_MACQ900, "Quadra", " 900 ", MACH_CLASSQ, &romvecs[6]},
        {MACH_MACQ950, "Quadra", " 950 ", MACH_CLASSQ, &romvecs[17]},
        {MACH_MACQ800, "Quadra", " 800 ", MACH_CLASSQ, &romvecs[6]},
        {MACH_MACQ650, "Quadra", " 650 ", MACH_CLASSQ, &romvecs[6]},
        {MACH_MACC650, "Centris", " 650 ", MACH_CLASSQ, &romvecs[6]},
        {MACH_MACQ605, "Quadra", " 605 ", MACH_CLASSQ, &romvecs[9]},
        {MACH_MACQ605_33, "Quadra", " 605/33 ", MACH_CLASSQ, &romvecs[9]},
        {MACH_MACC610, "Centris", " 610 ", MACH_CLASSQ, &romvecs[6]},
        {MACH_MACQ610, "Quadra", " 610 ", MACH_CLASSQ, &romvecs[6]},
        {MACH_MACQ630, "Quadra", " 630 ", MACH_CLASSQ2, &romvecs[13]},
        {MACH_MACC660AV, "Centris", " 660AV ", MACH_CLASSAV, &romvecs[7]},
        {MACH_MACQ840AV, "Quadra", " 840AV ", MACH_CLASSAV, &romvecs[7]},

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

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

/* The Performas... */
        {MACH_MACP600, "Performa", " 600 ", MACH_CLASSIIvx, &romvecs[2]},
        {MACH_MACP460, "Performa", " 460 ", MACH_CLASSLC, &romvecs[14]},
        {MACH_MACP550, "Performa", " 550 ", MACH_CLASSLC, &romvecs[11]},
        {MACH_MACP580, "Performa", " 580 ", MACH_CLASSQ2, &romvecs[19]},
        {MACH_MACTV,   "TV ",      "",      MACH_CLASSLC, &romvecs[12]},

/* The LCs... */
        {MACH_MACLCII,  "LC", " II ",  MACH_CLASSLC, &romvecs[3]},
        {MACH_MACLCIII, "LC", " III ", MACH_CLASSLC, &romvecs[14]},
        {MACH_MACLC475, "LC", " 475 ", MACH_CLASSQ,  &romvecs[9]},
        {MACH_MACLC475_33, "LC", " 475/33 ", MACH_CLASSQ,  &romvecs[9]},
        {MACH_MACLC520, "LC", " 520 ", MACH_CLASSLC, &romvecs[15]},
        {MACH_MACLC575, "LC", " 575 ", MACH_CLASSQ2, &romvecs[16]},
        {MACH_MACCCLASSIC, "Color Classic ", "", MACH_CLASSLC, &romvecs[3]},
        {MACH_MACCCLASSICII, "Color Classic"," II ", MACH_CLASSLC, &romvecs[3]},
/* Does this belong here? */
        {MACH_MACCLASSICII, "Classic", " II ", MACH_CLASSLC, &romvecs[3]},

/* The unknown one and the end... */
        {0, "Unknown", "", MACH_CLASSII, NULL},
        {0, NULL, NULL, 0, NULL},
};                              /* 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
 *      ...?
 */

int     mach_cputype(void);

int
mach_cputype(void)
{
        return (mac68k_machine.mach_processor);
}

void
machine_set_model(void)
{
        cpu_setmodel("Apple Macintosh %s%s",
            cpu_models[mac68k_machine.cpu_model_index].model_major,
            cpu_models[mac68k_machine.cpu_model_index].model_minor);
}

static void     get_machine_info(void);

static void
get_machine_info(void)
{
        int i;

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

        if (cpu_models[i].model_major == NULL)
                i--;

        mac68k_machine.cpu_model_index = i;
}

struct cpu_model_info *current_mac_model;
romvec_t *mrg_MacOSROMVectors = 0;

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

void
setmachdep(void)
{
        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.via2_ipl = 2;
        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;
                mac68k_machine.zs_chip = 0;
                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;
                mac68k_machine.zs_chip = 0;
                /* 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;
                mac68k_machine.zs_chip = 0;
                /* 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;
                mac68k_machine.zs_chip = 0;
                via_reg(VIA1, vIER) = 0x7f;     /* disable VIA1 int */
                via_reg(VIA2, vIER) = 0x7f;     /* disable VIA2 int */

#if 1
                switch (current_mac_model->machineid) {
                default:
        /*      case MACH_MACQ900: These three, at least, support the
                case MACH_MACQ950: A/UX interrupts.  What Quadras don't?
                case MACH_MACQ700: */
                        /* 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;
                        mac68k_machine.via2_ipl = 2;
                        break;
                }
#endif

                break;
        case MACH_CLASSAV:
        case MACH_CLASSP580:
                VIA2 = VIA2OFF;
                IOBase = 0x50f00000;
                Via1Base = (volatile u_char *)IOBase;
                mac68k_machine.scsi96 = 1;
                mac68k_machine.zs_chip = 0;
                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;
                mac68k_machine.zs_chip = 0;
                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;
                mac68k_machine.zs_chip = 0;
                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;
                mac68k_machine.zs_chip = 0;
                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;
                mac68k_machine.zs_chip = 0;
                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;
                mac68k_machine.zs_chip = 0;
                via_reg(VIA1, vIER) = 0x7f;  /* disable VIA1 int */
                break;
        default:
        case MACH_CLASSH:
                break;
        }

        /*
         * Set up current ROM Glue vectors.  Actually now all we do
         * is save the address of the ROM Glue Vector table. This gets
         * used later when we re-map the vectors from MacOS Address
         * Space to NetBSD Address Space.
         */
        mrg_MacOSROMVectors = cpui->rom_vectors;
}

/*
 * Set IO offsets.
 */
void
mac68k_set_io_offsets(vaddr_t base)
{

        Via1Base = (volatile u_char *)base;
        Via2Base = Via1Base + 0x2000 * VIA2;
        switch (current_mac_model->class) {
        case MACH_CLASSQ:
                switch (current_mac_model->machineid) {
                case MACH_MACQ900:
                case MACH_MACQ950:
                        sccA = (volatile u_char *)base + 0xc020;
                        SCSIBase = base + 0xf000;
                        mac68k_machine.scsi96_2 = 1;
                        iop_init(0);    /* For console */
                        break;
                case MACH_MACQ800:
                        /*
                         * The H/W partially decode address for sccA; it is
                         * available at offsets 0xc000, 0xc020, .... Here,
                         * we choose 0xc020, where Mac toolbox ROM uses.
                         */
                        sccA = (volatile u_char *)base + 0xc020;
                        SCSIBase = base + 0x10000;
                        break;
                case MACH_MACQ700:
                        sccA = (volatile u_char *)base + 0xc000;
                        SCSIBase = base + 0xf000;
                        break;
                default:
                        sccA = (volatile u_char *)base + 0xc000;
                        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.
                 */
                sccA = (volatile u_char *)base + 0xc020;
                SCSIBase = base + 0x10000;
                break;
        case MACH_CLASSP580:
                /*
                 * Here's a queer bird... it seems to be a cross between
                 * the two different Quadra classes.
                 */
                sccA = (volatile u_char *)base + 0xc020;
                SCSIBase = base;
                break;
        case MACH_CLASSAV:
                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:
                sccA = (volatile u_char *)base + 0x4000;
                SCSIBase = base;
                break;
        case MACH_CLASSIIfx:
                /*
                 * Note that sccA base address is based on having
                 * the serial port in `compatible' mode (set in
                 * the Serial Switch control panel before booting).
                 */
                sccA = (volatile u_char *)base + 0x4020;
                SCSIBase = base;
                iop_init(0);    /* For console */
                break;
        default:
        case MACH_CLASSH:
                panic("Unknown/unsupported machine class (%d).",
                    current_mac_model->class);
                break;
        }
}

#if GRAYBARS
static u_long gray_nextaddr = 0;

void
gray_bar(void)
{
        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*stride/4 is done lots, but we want this to be
                 * slow */
                for (i = 0; i < 10 * mac68k_video.mv_stride / 4; i++)
                        ((u_long *)mac68k_video.mv_kvaddr)
                            [gray_nextaddr++] = 0xaaaaaaaa;
                for (i = 0; i < 2 * mac68k_video.mv_stride / 4; i++)
                        ((u_long *)mac68k_video.mv_kvaddr)
                            [gray_nextaddr++] = 0x00000000;
        }

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

/* in locore */
extern u_long ptest040(void *, u_int);
extern u_long plpar060(void *, u_int);
extern int get_pte(u_int, u_long *, u_short *);

/*
 * 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.
 */

static 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 (cputype == CPU_68060) {
                ph = plpar060((void *)addr, FC_SUPERD);
                if (ph == (u_long)-1) {
                        ph = plpar060((void *)addr, FC_USERD);
                        if (ph == (u_long)-1)  {
                                return 0;
                        }
                }
                *phys = ph;
                return 1;
        } else if (mmutype == MMU_68040) {
                ph = ptest040((void *)addr, FC_SUPERD);
                if ((ph & MMUSR40_R) == 0) {
                        ph = ptest040((void *)addr, FC_USERD);
                        if ((ph & MMUSR40_R) == 0)
                                return 0;
                }
                if ((ph & MMUSR40_T) != 0)
                        ph = addr;

                mask = (macos_tc & TCR40_P) ? 0x00001fff : 0x00000fff;
                ph &= (~mask);
        } else {
                switch (get_pte(addr, pte, &psr)) {
                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;
}

static void     check_video(const char *, u_long, u_long);

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

        if (!get_physical(mac68k_video.mv_kvaddr, &phys)) {
                if (mac68k_machine.do_graybars)
                        printf("get_mapping(): %s.  False start.\n", id);
        } else {
                mac68k_video.mv_log = mac68k_video.mv_kvaddr;
                mac68k_video.mv_phys = phys;
                mac68k_video.mv_len = 32768;
                addr = mac68k_video.mv_kvaddr + 32768;
                while (get_physical(addr, &phys)) {
                        if ((phys - mac68k_video.mv_phys)
                            != mac68k_video.mv_len)
                                break;
                        if (mac68k_video.mv_len + 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_video.mv_len = maxm;
                                break;
                        }
                        mac68k_video.mv_len += 32768;
                        addr += 32768;
                }
                if (mac68k_machine.do_graybars) {
                        printf("  %s internal video at addr %p (phys %p), ",
                            id, (void *)mac68k_video.mv_log,
                            (void *)mac68k_video.mv_phys);
                        printf("len 0x%x.\n", mac68k_video.mv_len);
                }
        }
}

/*
 * 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);

        if (mac68k_machine.do_graybars)
                printf("Loaded at 0x%0lx\n", 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(mac68k_video.mv_kvaddr, &phys))
                phys = mac68k_video.mv_kvaddr;

        /*
         * Find on-board video, if we have an idea of where to look
         * on this system.
         */
        for (iip = intvid_info; iip->machineid; 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_video.mv_phys = phys & ~iip->fbmask;
                mac68k_video.mv_len = 32768 - (phys & 0x7fff);

                limit = iip->fbbase + iip->fblen - mac68k_video.mv_phys;
                if (mac68k_video.mv_len > limit) {
                        mac68k_video.mv_len = limit;
                } else {
                        addr = mac68k_video.mv_kvaddr + mac68k_video.mv_len;
                        while (get_physical(addr, &phys)) {
                                phys &= ~iip->fbmask;
                                if ((phys - mac68k_video.mv_phys) !=
                                    mac68k_video.mv_len)
                                        break;
                                if ((mac68k_video.mv_phys + 32768) > limit) {
                                        mac68k_video.mv_len = limit;
                                        break;
                                }
                                mac68k_video.mv_len += 32768;
                                addr += 32768;
                        }
                }
        }

        if (mac68k_video.mv_len > 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 %p (phys %p), "
                            "len 0x%x.\n",
                            (void *)mac68k_video.mv_kvaddr,
                            (void *)mac68k_video.mv_phys,
                            mac68k_video.mv_len);
        } 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 = 0xF9000000; addr < 0xFF000000; addr += 32768) {
                        if (!get_physical(addr, &phys)) {
                                continue;
                        }
                        len = nbnumranges == 0 ? 0 : nblen[nbnumranges - 1];

#ifdef __debug_mondo_verbose__
                        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 &&
                                            nbnumranges > 0 /* XXXGCC14 */) {
                                                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_video.mv_log = nblog[i] - nbphys[i];
                                mac68k_video.mv_len = nblen[i] + nbphys[i];
                                mac68k_video.mv_phys = 0;
                                break;
                        }
                }
                if (i == nbnumranges) {
                        if (0x60000000 <= mac68k_video.mv_kvaddr
                            && mac68k_video.mv_kvaddr < 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 <= mac68k_video.mv_kvaddr
                            && mac68k_video.mv_kvaddr < 0x50FBFFFF) {
                                /*
                                 * Kludge for LC internal video
                                 */
                                check_video("LC video (0x50f40000)",
                                    512 * 1024, 512 * 1024);
                        } else if (0x50100100 <= mac68k_video.mv_kvaddr
                            && mac68k_video.mv_kvaddr < 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 -- "
                                            "mac68k_video.mv_kvaddr is "
                                            "0x%lx.\n",
                                            mac68k_video.mv_kvaddr);
                        }
                } else if (mac68k_machine.do_graybars) {
                        printf("  Video address = %p\n",
                            (void *)mac68k_video.mv_kvaddr);
                        printf("  Int video starts at %p\n",
                            (void *)mac68k_video.mv_log);
                        printf("  Length = 0x%x (%d) bytes\n",
                            mac68k_video.mv_len, mac68k_video.mv_len);
                }
        }
        /* mv_len sanity check */
        int reqsize = mac68k_video.mv_height * mac68k_video.mv_stride;
        if (mac68k_video.mv_len < reqsize)
                mac68k_video.mv_len = reqsize;

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

/*
 * 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");
}

/*
 * 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(int (*callback)(void *, int, int, int), void *cookie)
{
        mac68k_bell_callback = callback;
        mac68k_bell_cookie = (void *)cookie;
}

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

int
mm_md_physacc(paddr_t pa, vm_prot_t prot)
{
        extern u_long last_page;

        return (pa < last_page) ? 0 : EFAULT;
}

void __attribute__((no_instrument_function))
pmap_machine_check_bootstrap_allocations(paddr_t nextpa, paddr_t firstpa)
{
        int i;

        for (i = 0; i < numranges; i++) {
                if (low[i] <= firstpa && firstpa < high[i]) {
                        break;
                }
        }
        if (i >= numranges || nextpa > high[i]) {
                if (mac68k_machine.do_graybars) {
                        printf("Failure in NetBSD boot; ");
                        if (i < numranges) {
                                printf("nextpa=0x%lx, high[%d]=0x%lx.\n",
                                    nextpa, i, high[i]);
                        } else {
                                printf("can't find kernel RAM segment.\n");
                        }
                        printf("You're hosed!  Try booting with 32-bit ");
                        printf("addressing enabled in the memory control ");
                        printf("panel.\n");
                        printf("Older machines may need Mode32 to get that ");
                        printf("option.\n");
                }
                panic("Cannot work with the current memory mappings.");
        }
}

#define PMBM_IOBase     0
#define PMBM_ROMBase    1
#define PMBM_VIDBase    2
struct pmap_bootmap machine_bootmap[] = {
        /*
         * Map the I/O region VA==PA; some of the ROM routines
         * have hard-coded addresses for e.g. VIA1.
         */
        { .pmbm_vaddr = 0,              /* initialized below */
          .pmbm_paddr = 0,              /* initialized below */
          .pmbm_size  = m68k_ptob(IIOMAPSIZE),
          .pmbm_flags = PMBM_F_FIXEDVA | PMBM_F_CI },

        { .pmbm_vaddr_ptr = (vaddr_t *)&ROMBase,
          .pmbm_paddr = 0,              /* initialized below */
          .pmbm_size  = m68k_ptob(ROMMAPSIZE),
          .pmbm_flags = PMBM_F_RO },

        { .pmbm_vaddr_ptr = &newvideoaddr,
          .pmbm_paddr = 0,              /* initialized below */
          .pmbm_size  = 0,              /* initialized below */
          .pmbm_flags = PMBM_F_CI },

        { .pmbm_vaddr = -1 },
};

paddr_t bootstrap_mac68k(int);

paddr_t __attribute__((no_instrument_function))
bootstrap_mac68k(int tc)
{
#if NZSC > 0
        extern int zsinited;
        extern void zs_init(void);
#endif
        extern int *esym;
        paddr_t nextpa;
        void *oldROMBase;
        char use_bootmem = 0;
        int i;

#ifdef DJMEMCMAX
        if(mac68k_machine.machineid == MACH_MACC650 ||
            mac68k_machine.machineid == MACH_MACQ650 ||
            mac68k_machine.machineid == MACH_MACQ610 ||
            mac68k_machine.machineid == MACH_MACC610 ||
            mac68k_machine.machineid == MACH_MACQ800) {
                use_bootmem = 1;
        }
#endif

        if (mac68k_machine.do_graybars)
                printf("Bootstrapping NetBSD/mac68k.\n");

        oldROMBase = ROMBase;
        mac68k_video.mv_phys = mac68k_video.mv_kvaddr;

        if ((!use_bootmem) && (((tc & 0x80000000) && (mmutype == MMU_68030)) ||
            ((tc & 0x8000) && (mmutype == MMU_68040)))) {
                if (mac68k_machine.do_graybars)
                        printf("Getting mapping from MMU.\n");
                (void) get_mapping();
                if (mac68k_machine.do_graybars)
                        printf("Done.\n");
        } else {
                /* MMU not enabled.  Fake up ranges. */
                numranges = 1;
                low[0] = 0;
                high[0] = mac68k_machine.mach_memsize * (1024 * 1024);
                if (mac68k_machine.do_graybars)
                        printf("Faked range to byte 0x%lx.\n", high[0]);
        }
        nextpa = load_addr + m68k_round_page(esym);

        if (mac68k_machine.do_graybars)
                printf("Bootstrapping the pmap system.\n");

        vidlen = m68k_round_page(mac68k_video.mv_height *
            mac68k_video.mv_stride + m68k_page_offset(mac68k_video.mv_phys));

        /* Sum up the memory for pmap_bootstrap1(). */
        vsize_t mem_size = 0;
        for (i = 0; i < numranges; i++)
                mem_size += high[i] - low[i];
        physmem = m68k_btop(mem_size);

#ifdef __HAVE_NEW_PMAP_68K
        /* Initialize machine_bootmap[] for pmap_bootstrap1(). */
        machine_bootmap[PMBM_IOBase].pmbm_vaddr = (vaddr_t)IOBase;
        machine_bootmap[PMBM_IOBase].pmbm_paddr = (paddr_t)IOBase;
        machine_bootmap[PMBM_ROMBase].pmbm_paddr = (paddr_t)ROMBase;
        machine_bootmap[PMBM_VIDBase].pmbm_paddr =
            m68k_trunc_page(mac68k_video.mv_phys);
        machine_bootmap[PMBM_VIDBase].pmbm_size = vidlen;
#endif

        nextpa = pmap_bootstrap1(nextpa, load_addr);

#ifdef __HAVE_NEW_PMAP_68K
        /*
         * machine_bootmap[] deals in whole pages; fixup newvideoaddr to
         * include the page offset.
         */
        if (vidlen) {
                newvideoaddr += m68k_page_offset(mac68k_video.mv_phys);
        }
#endif

        /*
         * VM data structures are now initialized, set up data for
         * the pmap module.
         *
         * Note about avail_end: msgbuf is initialized just after
         * avail_end in machdep.c.  Since the last page is used
         * for rebooting the system (code is copied there and
         * execution continues from copied code before the MMU
         * is disabled), the msgbuf will get trounced between
         * reboots if it's placed in the last physical page.
         * To work around this, we move avail_end back one more
         * page so the msgbuf can be preserved.
         */
        avail_start = m68k_round_page(nextpa);
        last_page = high[numranges - 1] - m68k_ptob(1);
#if NAUDIO > 0
        /*
         * Reduce high by an extra 7 pages which are used by the EASC on some
         * machines.  last_page is unchanged as the last page can still be
         * safetly used to reboot the system.
         */
        high[numranges - 1] -= (m68k_round_page(MSGBUFSIZE) + m68k_ptob(8));
#else
        high[numranges - 1] -= (m68k_round_page(MSGBUFSIZE) + m68k_ptob(1));
#endif
        avail_end = high[numranges - 1];

        if (mac68k_machine.do_graybars)
                printf("Pmap bootstrapped.\n");

        if (!vidlen)
                panic("Don't know how to relocate video!");

        if (mac68k_machine.do_graybars)
                printf("Moving ROMBase from %p to %p.\n", oldROMBase, ROMBase);

        mrg_fixupROMBase(oldROMBase, ROMBase);

        if (mac68k_machine.do_graybars)
                printf("Video address %p -> %p.\n",
                    (void *)mac68k_video.mv_kvaddr, (void *)newvideoaddr);

        mac68k_set_io_offsets(IOBase);

        /*
         * If the serial ports are going (for console or 'echo'), then
         * we need to make sure the IO change gets propagated properly.
         * This resets the base addresses for the 8530 (serial) driver.
         *
         * WARNING!!! No printfs() (etc) BETWEEN zs_init() and the end
         * of this function (where we start using the MMU, so the new
         * address is correct.
         */
#if NZSC > 0
        if (zsinited != 0)
                zs_init();
#endif

        mac68k_video.mv_kvaddr = newvideoaddr;

        return nextpa;
}