/* $OpenBSD: machdep.c,v 1.142 2007/07/29 21:24:05 miod Exp $ */ /* $NetBSD: machdep.c,v 1.207 1998/07/08 04:39:34 thorpej Exp $ */ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1982, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, * Michael L. Finch, Bradley A. Grantham, and * Lawrence A. Kesteloot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Alice Group. * 4. The names of the Alice Group or any of its members may not be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * from: Utah $Hdr: machdep.c 1.63 91/04/24$ * * @(#)machdep.c 7.16 (Berkeley) 6/3/91 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SYSVMSG #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wsdisplay.h" /* The following is used externally (sysctl_hw) */ char machine[] = MACHINE; /* from */ struct mac68k_machine_S mac68k_machine; volatile u_char *Via1Base, *Via2Base, *PSCBase = NULL; u_long NuBusBase = NBBASE; u_long IOBase; vaddr_t SCSIBase; /* These are used to map kernel space: */ extern int numranges; extern u_long low[8]; extern u_long high[8]; /* These are used to map NuBus space: */ #define NBMAXRANGES 16 int nbnumranges; /* = 0 == don't use the ranges */ u_long nbphys[NBMAXRANGES]; /* Start physical addr of this range */ u_long nblog[NBMAXRANGES]; /* Start logical addr of this range */ long nblen[NBMAXRANGES]; /* Length of this range If the length is */ /* negative, all phys addrs are the same. */ /* From Booter via locore */ long videoaddr; /* Addr used in kernel for video. */ long videorowbytes; /* Used in kernel for video. */ long videobitdepth; /* Number of bihs per pixel */ u_long videosize; /* height = 31:16, width 15:0 */ /* * Values for IIvx-like internal video * -- should be zero if it is not used (usual case). */ u_int32_t mac68k_vidlog; /* logical addr */ u_int32_t mac68k_vidphys; /* physical addr */ u_int32_t mac68k_vidlen; /* mem length */ /* Callback and cookie to run bell */ int (*mac68k_bell_callback)(void *, int, int, int); caddr_t mac68k_bell_cookie; struct vm_map *exec_map = NULL; struct vm_map *phys_map = NULL; /* * Declare these as initialized data so we can patch them. */ #ifndef BUFCACHEPERCENT #define BUFCACHEPERCENT 5 #endif #ifdef BUFPAGES int bufpages = BUFPAGES; #else int bufpages = 0; #endif int bufcachepercent = BUFCACHEPERCENT; int physmem; /* size of physical memory, in pages */ /* * safepri is a safe priority for sleep to set for a spin-wait * during autoconfiguration or after a panic. */ int safepri = PSL_LOWIPL; /* * Extent maps to manage all memory space, including I/O ranges. Allocate * storage for 8 regions in each, initially. Later, iomem_malloc_safe * will indicate that it's safe to use malloc() to dynamically allocate * region descriptors. * * The extent maps are not static! Machine-dependent NuBus and on-board * I/O routines need access to them for bus address space allocation. */ long iomem_ex_storage[EXTENT_FIXED_STORAGE_SIZE(8) / sizeof(long)]; struct extent *iomem_ex; int iomem_malloc_safe; /* XXX should be in locore.s for consistency */ int astpending = 0; void identifycpu(void); u_long get_physical(u_int, u_long *); caddr_t allocsys(caddr_t); void initcpu(void); int cpu_dumpsize(void); int cpu_dump(int (*)(dev_t, daddr64_t, caddr_t, size_t), daddr64_t *); void cpu_init_kcore_hdr(void); int fpu_probe(void); /* functions called from locore.s */ void dumpsys(void); void mac68k_init(void); void straytrap(int, int); void nmihand(struct frame); /* * Machine-dependent crash dump header info. */ cpu_kcore_hdr_t cpu_kcore_hdr; /* * Early initialization, before main() is called. */ void mac68k_init() { int i; extern vaddr_t avail_start; /* * Tell the VM system about available physical memory. * Notice that we don't need to worry about avail_end here * since it's equal to high[numranges-1]. */ for (i = 0; i < numranges; i++) { if (low[i] <= avail_start && avail_start < high[i]) uvm_page_physload(atop(avail_start), atop(high[i]), atop(avail_start), atop(high[i]), VM_FREELIST_DEFAULT); else uvm_page_physload(atop(low[i]), atop(high[i]), atop(low[i]), atop(high[i]), VM_FREELIST_DEFAULT); } /* * Initialize the I/O mem extent map. * Note: we don't have to check the return value since * creation of a fixed extent map will never fail (since * descriptor storage has already been allocated). * * N.B. The iomem extent manages _all_ physical addresses * on the machine. When the amount of RAM is found, all * extents of RAM are allocated from the map. */ iomem_ex = extent_create("iomem", 0x0, 0xffffffff, M_DEVBUF, (caddr_t)iomem_ex_storage, sizeof(iomem_ex_storage), EX_NOCOALESCE|EX_NOWAIT); /* Initialize the interrupt handlers. */ intr_init(); /* Initialize the VIAs */ via_init(); /* Initialize the PSC (if present) */ psc_init(); } /* * Console initialization: called early on from main, * before vm init or startup. Do enough configuration * to choose and initialize a console. */ void consinit(void) { /* * Generic console: sys/dev/cons.c * Initializes either ite or ser as console. * Can be called from locore.s and init_main.c. (Ugh.) */ static int init; /* = 0 */ if (!init) { cninit(); #ifdef DDB /* * Initialize kernel debugger, if compiled in. */ ddb_init(); #endif init = 1; } else { #if NWSDISPLAY > 0 /* * XXX This is an evil hack on top of an evil hack! * * With the graybar stuff, we've got a catch-22: we need * to do at least some console setup really early on, even * before we're running with the mappings we need. On * the other hand, we're not nearly ready to do anything * with wscons or the ADB driver at that point. * * To get around this, wscninit() ignores the first call * it gets (from cninit(), if not on a serial console). * Once we're here, we call wscninit() again, which sets * up the console devices and does the appropriate wscons * initialization. */ if (mac68k_machine.serial_console == 0) { cons_decl(ws); wscninit(NULL); } #endif mac68k_calibrate_delay(); #if NZSC > 0 && defined(KGDB) zs_kgdb_init(); #endif if (boothowto & RB_KDB) { #ifdef KGDB /* XXX - Ask on console for kgdb_dev? */ /* Note: this will just return if kgdb_dev==NODEV */ kgdb_connect(1); #else /* KGDB */ #ifdef DDB /* Enter DDB. We don't have a monitor PROM. */ Debugger(); #endif /* DDB */ #endif /* KGDB */ } } } #define CURRENTBOOTERVER 111 /* * cpu_startup: allocate memory for variable-sized tables, * initialize cpu, and do autoconfiguration. */ void cpu_startup(void) { caddr_t v; unsigned i; int vers; vaddr_t minaddr, maxaddr; vsize_t size = 0; /* To avoid compiler warning */ int delay; /* * Initialize the kernel crash dump header. */ cpu_init_kcore_hdr(); /* * Initialize error message buffer (at end of core). * high[numranges-1] was decremented in pmap_bootstrap. */ for (i = 0; i < btoc(MSGBUFSIZE); i++) pmap_enter(pmap_kernel(), (vaddr_t)msgbufp + i * NBPG, high[numranges - 1] + i * NBPG, VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE|PMAP_WIRED); pmap_update(pmap_kernel()); initmsgbuf((caddr_t)msgbufp, round_page(MSGBUFSIZE)); /* * Good {morning,afternoon,evening,night}. */ printf(version); identifycpu(); vers = mac68k_machine.booter_version; if (vers < CURRENTBOOTERVER) { /* fix older booters with indices, not versions */ if (vers < 100) vers += 99; printf("\nYou booted with booter version %d.%d.\n", vers / 100, vers % 100); printf("Booter version %d.%d is necessary to fully support\n", CURRENTBOOTERVER / 100, CURRENTBOOTERVER % 100); printf("this kernel.\n\n"); for (delay = 0; delay < 1000000; delay++); } printf("real mem = %u (%uMB)\n", ctob(physmem), ctob(physmem)/1024/1024); /* * Find out how much space we need, allocate it, * and then give everything true virtual addressses. */ size = (vsize_t)allocsys((caddr_t)0); if ((v = (caddr_t)uvm_km_zalloc(kernel_map, round_page(size))) == 0) panic("startup: no room for tables"); if ((allocsys(v) - v) != size) panic("startup: table size inconsistency"); /* * Determine how many buffers to allocate. * We allocate bufcachepercent% of memory for buffer space. */ if (bufpages == 0) bufpages = physmem * bufcachepercent / 100; /* Restrict to at most 25% filled kvm */ if (bufpages > (VM_MAX_KERNEL_ADDRESS-VM_MIN_KERNEL_ADDRESS) / PAGE_SIZE / 4) bufpages = (VM_MAX_KERNEL_ADDRESS-VM_MIN_KERNEL_ADDRESS) / PAGE_SIZE / 4; /* * Allocate a submap for exec arguments. This map effectively * limits the number of processes exec'ing at any time. */ minaddr = vm_map_min(kernel_map); exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, 16 * NCARGS, VM_MAP_PAGEABLE, FALSE, NULL); /* * Allocate a submap for physio */ phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, VM_PHYS_SIZE, 0, FALSE, NULL); printf("avail mem = %lu (%luMB)\n", ptoa(uvmexp.free), ptoa(uvmexp.free) / 1024 / 1024); /* * Set up CPU-specific registers, cache, etc. */ initcpu(); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); /* * Configure the system. */ if (boothowto & RB_CONFIG) { #ifdef BOOT_CONFIG user_config(); #else printf("kernel does not support -c; continuing..\n"); #endif } /* Safe for extent allocation to use malloc now. */ iomem_malloc_safe = 1; } /* * Allocate space for system data structures. We are given * a starting virtual address and we return a final virtual * address; along the way we set each data structure pointer. * * We call allocsys() with 0 to find out how much space we want, * allocate that much and fill it with zeroes, and then call * allocsys() again with the correct base virtual address. */ caddr_t allocsys(v) caddr_t v; { #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) #ifdef SYSVMSG valloc(msgpool, char, msginfo.msgmax); valloc(msgmaps, struct msgmap, msginfo.msgseg); valloc(msghdrs, struct msg, msginfo.msgtql); valloc(msqids, struct msqid_ds, msginfo.msgmni); #endif return (v); } void initcpu() { #if defined(M68040) extern void (*vectab[256])(void); void addrerr4060(void); #endif #ifdef M68040 void buserr40(void); #endif #ifdef FPSP extern u_long fpvect_tab, fpvect_end, fpsp_tab; #endif switch (cputype) { #ifdef M68040 case CPU_68040: vectab[2] = buserr40; vectab[3] = addrerr4060; #ifdef FPSP bcopy(&fpsp_tab, &fpvect_tab, (&fpvect_end - &fpvect_tab) * sizeof (fpvect_tab)); #endif break; #endif default: break; } DCIS(); } void doboot(void) __attribute__((__noreturn__)); int waittime = -1; void boot(howto) int howto; { extern u_long maxaddr; /* take a snap shot before clobbering any registers */ if (curproc && curproc->p_addr) savectx(&curproc->p_addr->u_pcb); /* If system is cold, just halt. */ if (cold) { /* (Unless the user explicitly asked for reboot.) */ if ((howto & RB_USERREQ) == 0) howto |= RB_HALT; goto haltsys; } boothowto = howto; if ((howto & RB_NOSYNC) == 0 && waittime < 0) { waittime = 0; vfs_shutdown(); if (mac68k_machine.aux_interrupts != 0) { /* * If we've been adjusting the clock, the todr * will be out of synch; adjust it now unless * the system was sitting in ddb. */ if ((howto & RB_TIMEBAD) == 0) { resettodr(); } else { printf("WARNING: not updating battery clock\n"); } } else { #ifdef DIAGNOSTIC printf("OpenBSD/mac68k does not trust itself to update" " the clock on shutdown on this machine.\n"); #endif } } /* Disable interrupts. */ splhigh(); /* If rebooting and a dump is requested, do it. */ if (howto & RB_DUMP) { dumpsys(); } haltsys: /* Run any shutdown hooks. */ doshutdownhooks(); if (howto & RB_HALT) { if (howto & RB_POWERDOWN) { printf("\nAttempting to power down...\n"); via_powerdown(); /* * Shut down machines whose power functions * are accessed via modified ADB calls. */ adb_poweroff(); } printf("\nThe operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); (void)cngetc(); } /* Map the last physical page VA = PA for doboot() */ pmap_enter(pmap_kernel(), (vaddr_t)maxaddr, (vaddr_t)maxaddr, VM_PROT_ALL, VM_PROT_ALL|PMAP_WIRED); pmap_update(pmap_kernel()); printf("rebooting...\n"); DELAY(1000000); doboot(); /* NOTREACHED */ } /* * Initialize the kernel crash dump header. */ void cpu_init_kcore_hdr() { cpu_kcore_hdr_t *h = &cpu_kcore_hdr; int i; bzero(&cpu_kcore_hdr, sizeof(cpu_kcore_hdr)); h->mmutype = mmutype; h->kernel_pa = low[0]; h->sysseg_pa = pmap_kernel()->pm_stpa; /* * mac68k has multiple RAM segments on some models. */ for (i = 0; i < numranges; i++) { h->ram_segs[i].start = low[i]; h->ram_segs[i].size = high[i] - low[i]; } } /* * Compute the size of the machine-dependent crash dump header. * Returns size in disk blocks. */ int cpu_dumpsize() { int size; size = ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t)); return (btodb(roundup(size, dbtob(1)))); } /* * Called by dumpsys() to dump the machine-dependent header. */ int cpu_dump(dump, blknop) int (*dump)(dev_t, daddr64_t, caddr_t, size_t); daddr64_t *blknop; { int buf[dbtob(1) / sizeof(int)]; cpu_kcore_hdr_t *chdr; kcore_seg_t *kseg; int error; kseg = (kcore_seg_t *)buf; chdr = (cpu_kcore_hdr_t *)&buf[ALIGN(sizeof(kcore_seg_t)) / sizeof(int)]; /* Create the segment header. */ CORE_SETMAGIC(*kseg, KCORE_MAGIC, MID_MACHINE, CORE_CPU); kseg->c_size = dbtob(1) - ALIGN(sizeof(kcore_seg_t)); bcopy(&cpu_kcore_hdr, chdr, sizeof(cpu_kcore_hdr_t)); error = (*dump)(dumpdev, *blknop, (caddr_t)buf, sizeof(buf)); *blknop += btodb(sizeof(buf)); return (error); } /* * These variables are needed by /sbin/savecore */ u_long dumpmag = 0x8fca0101; /* magic number */ int dumpsize = 0; /* pages */ long dumplo = 0; /* blocks */ /* * This is called by main to set dumplo and dumpsize. * Dumps always skip the first block of disk space in * case there might be a disk label stored there. If there * is extra space, put dump at the end to reduce the chance * that swapping trashes it. */ void dumpconf(void) { cpu_kcore_hdr_t *h = &cpu_kcore_hdr; int nblks; /* size of dump area */ int i; if (dumpdev == NODEV || (nblks = (bdevsw[major(dumpdev)].d_psize)(dumpdev)) == 0) return; if (nblks <= ctod(1)) return; dumpsize = 0; for (i = 0; h->ram_segs[i].size && i < NPHYS_RAM_SEGS; i++) dumpsize += btoc(h->ram_segs[i].size); dumpsize += cpu_dumpsize(); /* Always skip the first block, in case there is a label there. */ if (dumplo < ctod(1)) dumplo = ctod(1); /* Put dump at end of partition, and make it fit. */ if (dumpsize > dtoc(nblks - dumplo)) dumpsize = dtoc(nblks - dumplo); if (dumplo < nblks - ctod(dumpsize)) dumplo = nblks - ctod(dumpsize); } void dumpsys() { cpu_kcore_hdr_t *h = &cpu_kcore_hdr; daddr64_t blkno; /* current block to write */ /* dump routine */ int (*dump)(dev_t, daddr64_t, caddr_t, size_t); int pg; /* page being dumped */ vaddr_t maddr; /* PA being dumped */ int seg; /* RAM segment being dumped */ int error; /* error code from (*dump)() */ extern int msgbufmapped; /* XXX initialized here because of gcc lossage */ seg = 0; maddr = h->ram_segs[seg].start; pg = 0; /* Don't record dump msgs in msgbuf. */ msgbufmapped = 0; /* Make sure dump device is valid. */ if (dumpdev == NODEV) return; if (dumpsize == 0) { dumpconf(); if (dumpsize == 0) return; } if (dumplo <= 0) { printf("\ndump to dev %u,%u not possible\n", major(dumpdev), minor(dumpdev)); return; } dump = bdevsw[major(dumpdev)].d_dump; blkno = dumplo; printf("\ndumping to dev %u,%u offset %ld\n", major(dumpdev), minor(dumpdev), dumplo); printf("dump "); /* Write the dump header. */ error = cpu_dump(dump, &blkno); if (error) goto bad; for (pg = 0; pg < dumpsize; pg++) { #define NPGMB (1024*1024/NBPG) /* print out how many MBs we have dumped */ if (pg && (pg % NPGMB) == 0) printf("%d ", pg / NPGMB); #undef NPGMB while (maddr >= (h->ram_segs[seg].start + h->ram_segs[seg].size)) { if (++seg >= NPHYS_RAM_SEGS || h->ram_segs[seg].size == 0) { error = EINVAL; /* XXX ?? */ goto bad; } maddr = h->ram_segs[seg].start; } pmap_enter(pmap_kernel(), (vaddr_t)vmmap, maddr, VM_PROT_READ, VM_PROT_READ|PMAP_WIRED); pmap_update(pmap_kernel()); error = (*dump)(dumpdev, blkno, vmmap, NBPG); bad: switch (error) { case 0: maddr += NBPG; blkno += btodb(NBPG); break; case ENXIO: printf("device bad\n"); return; case EFAULT: printf("device not ready\n"); return; case EINVAL: printf("area improper\n"); return; case EIO: printf("i/o error\n"); return; case EINTR: printf("aborted from console\n"); return; default: printf("error %d\n", error); return; } } printf("succeeded\n"); } /* * Return the best possible estimate of the time in the timeval * to which tvp points. We do this by returning the current time * plus the amount of time since the last clock interrupt (clock.c:clkread). * * Check that this time is no less than any previously-reported time, * which could happen around the time of a clock adjustment. Just for fun, * we guarantee that the time will be greater than the value obtained by a * previous call. */ void microtime(tvp) register struct timeval *tvp; { int s = splhigh(); static struct timeval lasttime; *tvp = time; tvp->tv_usec += clkread(); while (tvp->tv_usec >= 1000000) { tvp->tv_sec++; tvp->tv_usec -= 1000000; } if (tvp->tv_sec == lasttime.tv_sec && tvp->tv_usec <= lasttime.tv_usec && (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000) { tvp->tv_sec++; tvp->tv_usec -= 1000000; } lasttime = *tvp; splx(s); } void straytrap(pc, evec) int pc; int evec; { printf("unexpected trap; vector offset 0x%x from 0x%x.\n", (int) (evec & 0xfff), pc); #ifdef DEBUG #ifdef DDB Debugger(); #endif #endif } int *nofault; /* * Level 7 interrupts can be caused by the keyboard or parity errors. */ void nmihand(struct frame); void nmihand(frame) struct frame frame; { static int nmihanddeep = 0; if (nmihanddeep) return; nmihanddeep = 1; #ifdef DDB if (db_console) Debugger(); #endif nmihanddeep = 0; } /* * It should be possible to probe for the top of RAM, but Apple has * memory structured so that in at least some cases, it's possible * for RAM to be aliased across all memory--or for it to appear that * there is more RAM than there really is. */ int get_top_of_ram(void); int get_top_of_ram() { return ((mac68k_machine.mach_memsize * (1024 * 1024)) - 4096); } /* * machine dependent system variables. */ int cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) int *name; u_int namelen; void *oldp; size_t *oldlenp; void *newp; size_t newlen; struct proc *p; { dev_t consdev; /* all sysctl names at this level are terminal */ if (namelen != 1) return (ENOTDIR); /* overloaded */ switch (name[0]) { case CPU_CONSDEV: if (cn_tab != NULL) consdev = cn_tab->cn_dev; else consdev = NODEV; return (sysctl_rdstruct(oldp, oldlenp, newp, &consdev, sizeof consdev)); default: return (EOPNOTSUPP); } /* NOTREACHED */ } int cpu_exec_aout_makecmds(p, epp) struct proc *p; struct exec_package *epp; { int error = ENOEXEC; #ifdef COMPAT_SUNOS { extern int sunos_exec_aout_makecmds(struct proc *, struct exec_package *); if ((error = sunos_exec_aout_makecmds(p, epp)) == 0) return 0; } #endif return error; } static char *envbuf = NULL; /* * getenvvars: Grab a few useful variables */ void getenvvars(u_long, char *); long getenv(char *); void getenvvars(flag, buf) u_long flag; char *buf; { extern u_long bootdev; #if defined(DDB) || NKSYMS > 0 extern u_long end, esym; #endif extern u_long macos_boottime; extern vaddr_t MacOSROMBase; extern long macos_gmtbias; extern u_short ADBDelay; extern u_int32_t HwCfgFlags3; int root_scsi_id; vaddr_t ROMBase; /* * If flag & 0x80000000 == 0, then we're booting with the old booter * and we should freak out. */ if ((flag & 0x80000000) == 0) { /* Freak out; print something if that becomes available */ } else envbuf = buf; /* These next two should give us mapped video & serial */ /* We need these for pre-mapping graybars & echo, but probably */ /* only on MacII or LC. -- XXX */ /* videoaddr = getenv("MACOS_VIDEO"); */ /* * The following are not in a structure so that they can be * accessed more quickly. */ videoaddr = getenv("VIDEO_ADDR"); videorowbytes = getenv("ROW_BYTES"); videobitdepth = getenv("SCREEN_DEPTH"); videosize = getenv("DIMENSIONS"); /* * More misc stuff from booter. */ mac68k_machine.machineid = getenv("MACHINEID"); mac68k_machine.mach_memsize = getenv("MEMSIZE"); mac68k_machine.do_graybars = getenv("GRAYBARS"); mac68k_machine.serial_boot_echo = getenv("SERIALECHO"); mac68k_machine.serial_console = getenv("SERIALCONSOLE"); mac68k_machine.modem_flags = getenv("SERIAL_MODEM_FLAGS"); mac68k_machine.modem_cts_clk = getenv("SERIAL_MODEM_HSKICLK"); mac68k_machine.modem_dcd_clk = getenv("SERIAL_MODEM_GPICLK"); mac68k_machine.modem_flags = getenv("SERIAL_MODEM_DSPEED"); mac68k_machine.print_flags = getenv("SERIAL_PRINT_FLAGS"); mac68k_machine.print_cts_clk = getenv("SERIAL_PRINT_HSKICLK"); mac68k_machine.print_dcd_clk = getenv("SERIAL_PRINT_GPICLK"); mac68k_machine.print_d_speed = getenv("SERIAL_PRINT_DSPEED"); mac68k_machine.booter_version = getenv("BOOTERVER"); /* * For now, we assume that the boot device is off the first controller. * Booter versions 1.11.0 and later set a flag to tell us to construct * bootdev using the SCSI ID passed in via the environment. */ root_scsi_id = getenv("ROOT_SCSI_ID"); if (((mac68k_machine.booter_version < CURRENTBOOTERVER) || (flag & 0x40000)) && bootdev == 0) bootdev = MAKEBOOTDEV(4, 0, 0, root_scsi_id, 0); /* * Booter 1.11.3 and later pass a BOOTHOWTO variable with the * appropriate bits set. */ boothowto = getenv("BOOTHOWTO"); if (boothowto == 0) boothowto = getenv("SINGLE_USER"); /* * Get end of symbols for kernel debugging */ #if defined(DDB) || NKSYMS > 0 esym = getenv("END_SYM"); if (esym == 0) esym = (long) &end; #endif /* Get MacOS time */ macos_boottime = getenv("BOOTTIME"); /* Save GMT BIAS saved in Booter parameters dialog box */ macos_gmtbias = getenv("GMTBIAS"); /* * Save globals stolen from MacOS */ ROMBase = (vaddr_t)getenv("ROMBASE"); if (ROMBase != 0) MacOSROMBase = ROMBase; ADBDelay = (u_short) getenv("ADBDELAY"); HwCfgFlags3 = getenv("HWCFGFLAG3"); } char toupper(char); char toupper(c) char c; { if (c >= 'a' && c <= 'z') { return c - 'a' + 'A'; } else { return c; } } long getenv(str) char *str; { /* * Returns the value of the environment variable "str". * * Format of the buffer is "var=val\0var=val\0...\0var=val\0\0". * * Returns 0 if the variable is not there, and 1 if the variable is * there without an "=val". */ char *s, *s1; int val, base; s = envbuf; while (1) { for (s1 = str; *s1 && *s && *s != '='; s1++, s++) { if (toupper(*s1) != toupper(*s)) { break; } } if (*s1) { /* No match */ while (*s) { s++; } s++; if (*s == '\0') { /* Not found */ /* Boolean flags are FALSE (0) if not there */ return 0; } continue; } if (*s == '=') {/* Has a value */ s++; val = 0; base = 10; if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) { base = 16; s += 2; } else if (*s == '0') { base = 8; } while (*s) { if (toupper(*s) >= 'A' && toupper(*s) <= 'F') { val = val * base + toupper(*s) - 'A' + 10; } else { val = val * base + (*s - '0'); } s++; } return val; } else { /* TRUE (1) */ return 1; } } } struct cpu_model_info cpu_models[] = { /* The first four. */ { MACH_MACII, "II", MACH_CLASSII }, { MACH_MACIIX, "IIx", MACH_CLASSII }, { MACH_MACIICX, "IIcx", MACH_CLASSII }, { MACH_MACSE30, "SE/30", MACH_CLASSII }, /* The rest of the II series... */ { MACH_MACIICI, "IIci", MACH_CLASSIIci }, { MACH_MACIISI, "IIsi", MACH_CLASSIIsi }, { MACH_MACIIVI, "IIvi", MACH_CLASSIIvx }, { MACH_MACIIVX, "IIvx", MACH_CLASSIIvx }, { MACH_MACIIFX, "IIfx", MACH_CLASSIIfx }, /* The Centris/Quadra series. */ { MACH_MACQ700, "Quadra 700", MACH_CLASSQ }, { MACH_MACQ900, "Quadra 900", MACH_CLASSQ }, { MACH_MACQ950, "Quadra 950", MACH_CLASSQ }, { MACH_MACQ800, "Quadra 800", MACH_CLASSQ }, { MACH_MACQ650, "Quadra 650", MACH_CLASSQ }, { MACH_MACC650, "Centris 650", MACH_CLASSQ }, { MACH_MACQ605, "Quadra 605", MACH_CLASSQ }, { MACH_MACQ605_33, "Quadra 605/33", MACH_CLASSQ }, { MACH_MACC610, "Centris 610", MACH_CLASSQ }, { MACH_MACQ610, "Quadra 610", MACH_CLASSQ }, { MACH_MACQ630, "Quadra 630", MACH_CLASSQ2 }, { MACH_MACC660AV, "Centris 660AV", MACH_CLASSAV }, { MACH_MACQ840AV, "Quadra 840AV", MACH_CLASSAV }, /* The Powerbooks/Duos... */ { MACH_MACPB100, "PowerBook 100", MACH_CLASSPB }, /* PB 100 has no MMU! */ { MACH_MACPB140, "PowerBook 140", MACH_CLASSPB }, { MACH_MACPB145, "PowerBook 145", MACH_CLASSPB }, { MACH_MACPB150, "PowerBook 150", MACH_CLASSDUO }, { MACH_MACPB160, "PowerBook 160", MACH_CLASSPB }, { MACH_MACPB165, "PowerBook 165", MACH_CLASSPB }, { MACH_MACPB165C, "PowerBook 165c", MACH_CLASSPB }, { MACH_MACPB170, "PowerBook 170", MACH_CLASSPB }, { MACH_MACPB180, "PowerBook 180", MACH_CLASSPB }, { MACH_MACPB180C, "PowerBook 180c", MACH_CLASSPB }, { MACH_MACPB190, "PowerBook 190", MACH_CLASSPB }, { MACH_MACPB190CS, "PowerBook 190cs", MACH_CLASSPB }, { MACH_MACPB500, "PowerBook 500", MACH_CLASSPB }, /* The Duos */ { MACH_MACPB210, "PowerBook Duo 210", MACH_CLASSDUO }, { MACH_MACPB230, "PowerBook Duo 230", MACH_CLASSDUO }, { MACH_MACPB250, "PowerBook Duo 250", MACH_CLASSDUO }, { MACH_MACPB270, "PowerBook Duo 270C", MACH_CLASSDUO }, { MACH_MACPB280, "PowerBook Duo 280", MACH_CLASSDUO }, { MACH_MACPB280C, "PowerBook Duo 280C", MACH_CLASSDUO }, /* The Performas... */ { MACH_MACP600, "Performa 600", MACH_CLASSIIvx }, { MACH_MACP460, "Performa 460", MACH_CLASSLC }, { MACH_MACP550, "Performa 550", MACH_CLASSLC }, { MACH_MACP580, "Performa 580", MACH_CLASSQ2 }, { MACH_MACTV, "TV", MACH_CLASSLC }, /* The LCs... */ { MACH_MACLCII, "LC II", MACH_CLASSLC }, { MACH_MACLCIII, "LC III", MACH_CLASSLC }, { MACH_MACLC475, "LC 475", MACH_CLASSQ }, { MACH_MACLC475_33, "LC 475/33", MACH_CLASSQ }, { MACH_MACLC520, "LC 520", MACH_CLASSLC }, { MACH_MACLC575, "LC 575", MACH_CLASSQ2 }, { MACH_MACCCLASSIC, "Color Classic", MACH_CLASSLC }, { MACH_MACCCLASSICII, "Color ClassicII", MACH_CLASSLC }, /* Does this belong here? */ { MACH_MACCLASSICII, "Classic II", MACH_CLASSLC }, /* The unknown one and the end... */ { 0, "Unknown", MACH_CLASSII } }; /* End of cpu_models[] initialization. */ struct intvid_info_t { int machineid; u_long fbbase; u_long fbmask; u_long fblen; } intvid_info[] = { { MACH_MACCLASSICII, 0x009f9a80, 0x0, 21888 }, { MACH_MACPB140, 0xfee08000, 0x0, 32 * 1024 }, { MACH_MACPB145, 0xfee08000, 0x0, 32 * 1024 }, { MACH_MACPB170, 0xfee08000, 0x0, 32 * 1024 }, { MACH_MACPB150, 0x60000000, 0x0, 128 * 1024 }, { MACH_MACPB160, 0x60000000, 0x0ffe0000, 128 * 1024 }, { MACH_MACPB165, 0x60000000, 0x0ffe0000, 128 * 1024 }, { MACH_MACPB180, 0x60000000, 0x0ffe0000, 128 * 1024 }, { MACH_MACPB210, 0x60000000, 0x0, 128 * 1024 }, { MACH_MACPB230, 0x60000000, 0x0, 128 * 1024 }, { MACH_MACPB250, 0x60000000, 0x0, 128 * 1024 }, { MACH_MACPB270, 0x60000000, 0x0, 128 * 1024 }, { MACH_MACPB280, 0x60000000, 0x0, 128 * 1024 }, { MACH_MACPB280C, 0x60000000, 0x0, 128 * 1024 }, { MACH_MACIICI, 0x0, 0x0, 320 * 1024 }, { MACH_MACIISI, 0x0, 0x0, 320 * 1024 }, { MACH_MACCCLASSIC, 0x50f40000, 0x0, 512 * 1024 }, /*??*/ { MACH_MACLCII, 0x50f40000, 0x0, 512 * 1024 }, { MACH_MACPB165C, 0xfc040000, 0x0, 512 * 1024 }, { MACH_MACPB180C, 0xfc040000, 0x0, 512 * 1024 }, { MACH_MACPB190, 0x60000000, 0x0, 512 * 1024 }, { MACH_MACPB190CS, 0x60000000, 0x0, 512 * 1024 }, { MACH_MACPB500, 0x60000000, 0x0, 512 * 1024 }, { MACH_MACLCIII, 0x60b00000, 0x0, 768 * 1024 }, { MACH_MACLC520, 0x60000000, 0x0, 1024 * 1024 }, { MACH_MACP550, 0x60000000, 0x0, 1024 * 1024 }, { MACH_MACTV, 0x60000000, 0x0, 1024 * 1024 }, { MACH_MACLC475, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACLC475_33, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACLC575, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACC610, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACC650, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACP580, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ605, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ605_33, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ610, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ630, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ650, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACC660AV, 0x50100000, 0x0, 1024 * 1024 }, { MACH_MACQ700, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ800, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ900, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ950, 0xf9000000, 0x0, 1024 * 1024 }, { MACH_MACQ840AV, 0x50100000, 0x0, 2048 * 1024 }, { 0, 0x0, 0x0, 0 }, }; /* End of intvid_info[] initialization. */ /* * Missing Mac Models: * PowerMac 6100 * PowerMac 7100 * PowerMac 8100 * PowerBook 540 * PowerBook 520 * PowerBook 150 * Duo 280 * Performa 6000s * ...? */ char cpu_model[120]; /* for sysctl() */ int fpu_probe() { /* * A 68881 idle frame is 28 bytes and a 68882's is 60 bytes. * We, of course, need to have enough room for either. */ int fpframe[60 / sizeof(int)]; label_t faultbuf; u_char b; nofault = (int *) &faultbuf; if (setjmp(&faultbuf)) { nofault = (int *) 0; return (FPU_NONE); } /* * Synchronize FPU or cause a fault. * This should leave the 881/882 in the IDLE state, * state, so we can determine which we have by * examining the size of the FP state frame */ asm("fnop"); nofault = (int *) 0; /* * Presumably, if we're an 040 and did not take exception * above, we have an FPU. Don't bother probing. */ if (mmutype == MMU_68040) return (FPU_68040); /* * Presumably, this will not cause a fault--the fnop should * have if this will. We save the state in order to get the * size of the frame. */ asm("movl %0, a0; fsave a0@" : : "a" (fpframe) : "a0" ); b = *((u_char *) fpframe + 1); /* * Now, restore a NULL state to reset the FPU. */ fpframe[0] = fpframe[1] = 0; m68881_restore((struct fpframe *) fpframe); /* * The size of a 68881 IDLE frame is 0x18 * and a 68882 frame is 0x38 */ if (b == 0x18) return (FPU_68881); if (b == 0x38) return (FPU_68882); /* * If it's not one of the above, we have no clue what it is. */ return (FPU_UNKNOWN); } void identifycpu() { #ifdef DEBUG extern u_int delay_factor; #endif /* * Print the machine type... */ snprintf(cpu_model, sizeof cpu_model, "Apple Macintosh %s", cpu_models[mac68k_machine.cpu_model_index].model); /* * ... and the CPU type... */ switch (cputype) { case CPU_68040: strlcat(cpu_model, ", 68040 CPU", sizeof cpu_model); break; case CPU_68030: strlcat(cpu_model, ", 68030 CPU", sizeof cpu_model); break; case CPU_68020: strlcat(cpu_model, ", 68020 CPU", sizeof cpu_model); break; default: strlcat(cpu_model, ", unknown CPU", sizeof cpu_model); break; } /* * ... and the MMU type... */ switch (mmutype) { case MMU_68040: case MMU_68030: strlcat(cpu_model, "+MMU", sizeof cpu_model); break; case MMU_68851: strlcat(cpu_model, ", MC68851 MMU", sizeof cpu_model); break; default: printf("%s\n", cpu_model); panic("unknown MMU type %d", mmutype); /* NOTREACHED */ } /* * ... and the FPU type... */ fputype = fpu_probe(); /* should eventually move to locore */ switch (fputype) { case FPU_68040: strlcat(cpu_model, "+FPU", sizeof cpu_model); break; case FPU_68882: strlcat(cpu_model, ", MC6882 FPU", sizeof cpu_model); break; case FPU_68881: strlcat(cpu_model, ", MC6881 FPU", sizeof cpu_model); break; case FPU_UNKNOWN: strlcat(cpu_model, ", unknown FPU", sizeof cpu_model); break; default: /*strlcat(cpu_model, ", no FPU", sizeof cpu_model);*/ break; } /* * ... and finally, the cache type. */ if (cputype == CPU_68040) strlcat(cpu_model, ", 4k on-chip physical I/D caches", sizeof cpu_model); printf("%s\n", cpu_model); #ifdef DEBUG printf("cpu: delay factor %d\n", delay_factor); #endif } void get_machine_info(void); void get_machine_info() { int i; for (i = 0; cpu_models[i].machineid != 0; i++) if (mac68k_machine.machineid == cpu_models[i].machineid) break; mac68k_machine.cpu_model_index = i; } const struct cpu_model_info *current_mac_model; /* * Sets a bunch of machine-specific variables */ void setmachdep(void); void setmachdep() { struct cpu_model_info *cpui; /* * First, set things that need to be set on the first pass only * Ideally, we'd only call this once, but for some reason, the * VIAs need interrupts turned off twice !? */ get_machine_info(); load_addr = 0; cpui = &(cpu_models[mac68k_machine.cpu_model_index]); current_mac_model = cpui; mac68k_machine.via1_ipl = 1; mac68k_machine.aux_interrupts = 0; /* * Set up any machine specific stuff that we have to before * ANYTHING else happens */ switch (cpui->class) { /* Base this on class of machine... */ case MACH_CLASSII: VIA2 = VIA2OFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ via_reg(VIA2, vIER) = 0x7f; /* disable VIA2 int */ break; case MACH_CLASSPB: VIA2 = VIA2OFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; /* Disable everything but PM; we need it. */ via_reg(VIA1, vIER) = 0x6f; /* disable VIA1 int */ /* Are we disabling something important? */ via_reg(VIA2, vIER) = 0x7f; /* disable VIA2 int */ if (cputype == CPU_68040) mac68k_machine.sonic = 1; break; case MACH_CLASSDUO: /* * The Duo definitely does not use a VIA2, but it looks * like the VIA2 functions might be on the MSC at the RBV * locations. The rest is copied from the Powerbooks. */ VIA2 = RBVOFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; /* Disable everything but PM; we need it. */ via_reg(VIA1, vIER) = 0x6f; /* disable VIA1 int */ /* Are we disabling something important? */ via_reg(VIA2, rIER) = 0x7f; /* disable VIA2 int */ break; case MACH_CLASSQ: case MACH_CLASSQ2: VIA2 = VIA2OFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.sonic = 1; mac68k_machine.scsi96 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ via_reg(VIA2, vIER) = 0x7f; /* disable VIA2 int */ /* Enable A/UX interrupt scheme */ mac68k_machine.aux_interrupts = 1; via_reg(VIA1, vBufB) &= (0xff ^ DB1O_AuxIntEnb); via_reg(VIA1, vDirB) |= DB1O_AuxIntEnb; mac68k_machine.via1_ipl = 6; break; case MACH_CLASSAV: VIA2 = VIA2OFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi96 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ via_reg(VIA2, vIER) = 0x7f; /* disable VIA2 int */ break; case MACH_CLASSIIci: VIA2 = RBVOFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ via_reg(VIA2, rIER) = 0x7f; /* disable RBV int */ break; case MACH_CLASSIIsi: VIA2 = RBVOFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ via_reg(VIA2, rIER) = 0x7f; /* disable RBV int */ break; case MACH_CLASSIIvx: VIA2 = RBVOFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ via_reg(VIA2, rIER) = 0x7f; /* disable RBV int */ break; case MACH_CLASSLC: VIA2 = RBVOFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ via_reg(VIA2, rIER) = 0x7f; /* disable RBV int */ break; case MACH_CLASSIIfx: VIA2 = OSSOFF; IOBase = 0x50f00000; Via1Base = (volatile u_char *)IOBase; mac68k_machine.scsi80 = 1; via_reg(VIA1, vIER) = 0x7f; /* disable VIA1 int */ break; default: case MACH_CLASSH: break; } } /* * Set IO offsets. */ void mac68k_set_io_offsets(base) vaddr_t base; { extern volatile u_char *sccA; switch (current_mac_model->class) { case MACH_CLASSQ: Via1Base = (volatile u_char *)base; /* The following two may be overriden. */ sccA = (volatile u_char *)base + 0xc000; SCSIBase = base + 0xf000; switch (current_mac_model->machineid) { case MACH_MACQ900: case MACH_MACQ950: sccA = (volatile u_char *)base + 0xc020; iop_serial_compatible(); mac68k_machine.scsi96_2 = 1; break; case MACH_MACQ700: break; default: SCSIBase = base + 0x10000; break; } break; case MACH_CLASSQ2: /* * Note the different offset for sccA for this class of * machines. This seems to be common on many of the * Quadra-type machines. */ Via1Base = (volatile u_char *)base; sccA = (volatile u_char *)base + 0xc020; SCSIBase = base + 0x10000; break; case MACH_CLASSAV: Via1Base = (volatile u_char *)base; sccA = (volatile u_char *)base + 0x4000; SCSIBase = base + 0x18000; PSCBase = (volatile u_char *)base + 0x31000; break; case MACH_CLASSII: case MACH_CLASSPB: case MACH_CLASSDUO: case MACH_CLASSIIci: case MACH_CLASSIIsi: case MACH_CLASSIIvx: case MACH_CLASSLC: Via1Base = (volatile u_char *)base; sccA = (volatile u_char *)base + 0x4000; SCSIBase = base; break; case MACH_CLASSIIfx: Via1Base = (volatile u_char *)base; sccA = (volatile u_char *)base + 0x4020; iop_serial_compatible(); SCSIBase = base; break; default: case MACH_CLASSH: panic("Unknown/unsupported machine class (%d).", current_mac_model->class); break; } Via2Base = Via1Base + 0x2000 * VIA2; } #if GRAYBARS u_long gray_nextaddr = 0; void gray_bar() { static int i = 0; static int flag = 0; /* MF basic premise as I see it: 1) Save the scratch regs as they are not saved by the compilier. 2) Check to see if we want gray bars, if so, display some lines of gray, a couple of lines of white(about 8), and loop to slow this down. 3) restore regs */ __asm __volatile (" movl a0,sp@-; movl a1,sp@-; movl d0,sp@-; movl d1,sp@-"); /* check to see if gray bars are turned off */ if (mac68k_machine.do_graybars) { /* MF the 10*rowbytes/4 is done lots, but we want this to be * slow */ for (i = 0; i < 10 * videorowbytes / 4; i++) ((u_long *)videoaddr)[gray_nextaddr++] = 0xaaaaaaaa; for (i = 0; i < 2 * videorowbytes / 4; i++) ((u_long *)videoaddr)[gray_nextaddr++] = 0x00000000; } __asm __volatile (" movl sp@+,d1; movl sp@+,d0; movl sp@+,a1; movl sp@+,a0"); } #endif /* in locore */ extern u_long ptest040(caddr_t addr, u_int fc); extern int get_pte(u_int addr, u_long pte[2], u_short * psr); /* * LAK (7/24/94): given a logical address, puts the physical address * in *phys and return 1, or returns 0 on failure. This is intended * to look through MacOS page tables. */ u_long get_physical(u_int addr, u_long * phys) { extern u_int macos_tc; u_long pte[2], ph, mask; u_short psr; int i, numbits; if (mmutype == MMU_68040) { ph = ptest040((caddr_t)addr, FC_SUPERD); if ((ph & MMU40_RES) == 0) { ph = ptest040((caddr_t)addr, FC_USERD); if ((ph & MMU40_RES) == 0) return 0; } if ((ph & MMU40_TTR) != 0) ph = addr; mask = (macos_tc & 0x4000) ? 0x00001fff : 0x00000fff; ph &= (~mask); } else { i = get_pte(addr, pte, &psr); switch (i) { case -1: return 0; case 0: ph = pte[0] & 0xFFFFFF00; break; case 1: ph = pte[1] & 0xFFFFFF00; break; default: panic("get_physical(): bad get_pte()"); } /* * We must now figure out how many levels down we went and * mask the bits appropriately -- the returned value may only * be the upper n bits, and we have to take the rest from addr. */ numbits = 0; psr &= 0x0007; /* Number of levels we went */ for (i = 0; i < psr; i++) numbits += (macos_tc >> (12 - i * 4)) & 0x0f; /* * We have to take the most significant "numbits" from * the returned value "ph", and the rest from our addr. * Assume that numbits != 0. */ mask = (1 << (32 - numbits)) - 1; } *phys = ph + (addr & mask); return 1; } void check_video(char *, u_long, u_long); void check_video(id, limit, maxm) char *id; u_long limit, maxm; { u_long addr, phys; if (!get_physical(videoaddr, &phys)) { if (mac68k_machine.do_graybars) printf("get_mapping(): %s. False start.\n", id); } else { mac68k_vidlog = videoaddr; mac68k_vidphys = phys; mac68k_vidlen = 32768; addr = videoaddr + 32768; while (get_physical(addr, &phys)) { if ((phys - mac68k_vidphys) != mac68k_vidlen) break; if (mac68k_vidlen + 32768 > limit) { if (mac68k_machine.do_graybars) { printf("mapping: %s. Does it never end?\n", id); printf(" Forcing VRAM size "); printf("to a conservative %ldK.\n", maxm/1024); } mac68k_vidlen = maxm; break; } mac68k_vidlen += 32768; addr += 32768; } if (mac68k_machine.do_graybars) { printf(" %s internal video at paddr 0x%x, len 0x%x.\n", id, mac68k_vidphys, mac68k_vidlen); } } } /* * Find out how MacOS has mapped itself so we can do the same thing. * Returns the address of logical 0 so that locore can map the kernel * properly. */ u_int get_mapping(void) { struct intvid_info_t *iip; u_long addr, lastpage, phys, len, limit; int i, last, same; numranges = 0; for (i = 0; i < 8; i++) { low[i] = 0; high[i] = 0; } lastpage = get_top_of_ram(); get_physical(0, &load_addr); last = 0; for (addr = 0; addr <= lastpage && get_physical(addr, &phys); addr += PAGE_SIZE) { if (numranges > 0 && phys != high[last]) { /* * Attempt to find if this page is already * accounted for in an existing physical segment. */ for (i = 0; i < numranges; i++) { if (low[i] <= phys && phys <= high[i]) { last = i; break; } } if (i >= numranges) last = numranges - 1; if (low[last] <= phys && phys < high[last]) continue; /* Skip pages we've seen. */ } if (numranges > 0 && phys == high[last]) { /* Common case: extend existing segment on high end */ high[last] += PAGE_SIZE; } else { /* This is a new physical segment. */ for (last = 0; last < numranges; last++) if (phys < low[last]) break; /* Create space for segment, if necessary */ if (last < numranges && phys < low[last]) { for (i = numranges; i > last; i--) { low[i] = low[i - 1]; high[i] = high[i - 1]; } } numranges++; low[last] = phys; high[last] = phys + PAGE_SIZE; } /* Coalesce adjoining segments as appropriate */ if (last < (numranges - 1) && high[last] == low[last + 1] && low[last + 1] != load_addr) { high[last] = high[last + 1]; for (i = last + 1; i < numranges; i++) { low[i] = low[i + 1]; high[i] = high[i + 1]; } --numranges; } } if (mac68k_machine.do_graybars) { printf("System RAM: %ld bytes in %ld pages.\n", addr, addr / PAGE_SIZE); for (i = 0; i < numranges; i++) { printf(" Low = 0x%lx, high = 0x%lx\n", low[i], high[i]); } } /* * If we can't figure out the PA of the frame buffer by groveling * the page tables, assume that we already have the correct * address. This is the case on several of the PowerBook 1xx * series, in particular. */ if (!get_physical(videoaddr, &phys)) phys = videoaddr; /* * Find on-board video, if we have an idea of where to look * on this system. */ for (iip = intvid_info; iip->machineid != 0; iip++) if (mac68k_machine.machineid == iip->machineid) break; if (mac68k_machine.machineid == iip->machineid && (phys & ~iip->fbmask) >= iip->fbbase && (phys & ~iip->fbmask) < (iip->fbbase + iip->fblen)) { mac68k_vidphys = phys & ~iip->fbmask; mac68k_vidlen = 32768 - (phys & 0x7fff); limit = iip->fbbase + iip->fblen - mac68k_vidphys; if (mac68k_vidlen > limit) { mac68k_vidlen = limit; } else { addr = videoaddr + mac68k_vidlen; while (get_physical(addr, &phys)) { if ((phys - mac68k_vidphys) != mac68k_vidlen) break; if (mac68k_vidlen + 32768 > limit) { mac68k_vidlen = limit; break; } mac68k_vidlen += 32768; addr += 32768; } } } if (mac68k_vidlen > 0) { /* * We've already figured out where internal video is. * Tell the user what we know. */ if (mac68k_machine.do_graybars) printf("On-board video at addr 0x%lx (phys 0x%x), len 0x%x.\n", videoaddr, mac68k_vidphys, mac68k_vidlen); } else { /* * We should now look through all of NuBus space to find where * the internal video is being mapped. Just to be sure we * handle all the cases, we simply map our NuBus space exactly * how MacOS did it. As above, we find a bunch of ranges that * are contiguously mapped. Since there are a lot of pages * that are all mapped to 0, we handle that as a special case * where the length is negative. We search in increments of * 32768 because that's the page size that MacOS uses. */ nbnumranges = 0; for (i = 0; i < NBMAXRANGES; i++) { nbphys[i] = 0; nblog[i] = 0; nblen[i] = 0; } same = 0; for (addr = NBBASE; addr < NBTOP; addr += 32768) { if (!get_physical(addr, &phys)) { continue; } len = nbnumranges == 0 ? 0 : nblen[nbnumranges - 1]; #if DEBUG if (mac68k_machine.do_graybars) printf ("0x%lx --> 0x%lx\n", addr, phys); #endif if (nbnumranges > 0 && addr == nblog[nbnumranges - 1] + len && phys == nbphys[nbnumranges - 1]) { /* Same as last one */ nblen[nbnumranges - 1] += 32768; same = 1; } else { if ((nbnumranges > 0) && !same && (addr == nblog[nbnumranges - 1] + len) && (phys == nbphys[nbnumranges - 1] + len)) nblen[nbnumranges - 1] += 32768; else { if (same) { nblen[nbnumranges - 1] = -len; same = 0; } if (nbnumranges == NBMAXRANGES) { if (mac68k_machine.do_graybars) printf("get_mapping(): " "Too many NuBus ranges.\n"); break; } nbnumranges++; nblog[nbnumranges - 1] = addr; nbphys[nbnumranges - 1] = phys; nblen[nbnumranges - 1] = 32768; } } } if (same) { nblen[nbnumranges - 1] = -nblen[nbnumranges - 1]; same = 0; } if (mac68k_machine.do_graybars) { printf("Non-system RAM (nubus, etc.):\n"); for (i = 0; i < nbnumranges; i++) { printf(" Log = 0x%lx, Phys = 0x%lx, Len = 0x%lx (%lu)\n", nblog[i], nbphys[i], nblen[i], nblen[i]); } } /* * We must now find the logical address of internal video in the * ranges we made above. Internal video is at physical 0, but * a lot of pages map there. Instead, we look for the logical * page that maps to 32768 and go back one page. */ for (i = 0; i < nbnumranges; i++) { if (nblen[i] > 0 && nbphys[i] <= 32768 && 32768 <= nbphys[i] + nblen[i]) { mac68k_vidlog = nblog[i] - nbphys[i]; mac68k_vidlen = nblen[i] + nbphys[i]; mac68k_vidphys = 0; break; } } if (i == nbnumranges) { if (0x60000000 <= videoaddr && videoaddr < 0x70000000) { if (mac68k_machine.do_graybars) printf("Checking for Internal Video "); /* * Kludge for IIvx internal video (60b0 0000). * PB 520 (6000 0000) */ check_video("PB/IIvx (0x60?00000)", 1 * 1024 * 1024, 1 * 1024 * 1024); } else if (0x50F40000 <= videoaddr && videoaddr < 0x50FBFFFF) { /* * Kludge for LC internal video */ check_video("LC video (0x50f40000)", 512 * 1024, 512 * 1024); } else if (0x50100100 <= videoaddr && videoaddr < 0x50400000) { /* * Kludge for AV internal video */ check_video("AV video (0x50100100)", 1 * 1024 * 1024, 1 * 1024 * 1024); } else { if (mac68k_machine.do_graybars) printf( " no internal video at address 0 -- " "videoaddr is 0x%lx.\n", videoaddr); } } else { if (mac68k_machine.do_graybars) { printf(" Video address = 0x%lx\n", videoaddr); printf(" Int video starts at 0x%x\n", mac68k_vidlog); printf(" Length = 0x%x (%d) bytes\n", mac68k_vidlen, mac68k_vidlen); } } } return load_addr; /* Return physical address of logical 0 */ } #ifdef DEBUG /* * Debugging code for locore page-traversal routine. */ void printstar(void); void printstar(void) { /* * Be careful as we assume that no registers are clobbered * when we call this from assembly. */ __asm __volatile (" movl a0,sp@-; movl a1,sp@-; movl d0,sp@-; movl d1,sp@-"); /* printf("*"); */ __asm __volatile (" movl sp@+,d1; movl sp@+,d0; movl sp@+,a1; movl sp@+,a0"); } #endif /* * Console bell callback; modularizes the console terminal emulator * and the audio system, so neither requires direct knowledge of the * other. */ void mac68k_set_bell_callback(callback, cookie) int (*callback)(void *, int, int, int); void *cookie; { mac68k_bell_callback = callback; mac68k_bell_cookie = (caddr_t)cookie; } int mac68k_ring_bell(freq, length, volume) int freq, length, volume; { if (mac68k_bell_callback) return ((*mac68k_bell_callback)(mac68k_bell_cookie, freq, length, volume)); else return (ENXIO); }