/* $NetBSD: cpu.c,v 1.211 2008/06/04 12:41:41 ad Exp $ */ /* * Copyright (c) 1996 * The President and Fellows of Harvard College. All rights reserved. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Harvard University. * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * 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 Aaron Brown and * Harvard University. * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * * @(#)cpu.c 8.5 (Berkeley) 11/23/93 * */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.211 2008/06/04 12:41:41 ad Exp $"); #include "opt_multiprocessor.h" #include "opt_lockdebug.h" #include "opt_ddb.h" #include "opt_sparc_arch.h" #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> #include <sys/malloc.h> #include <sys/simplelock.h> #include <sys/kernel.h> #include <uvm/uvm.h> #include <machine/promlib.h> #include <machine/autoconf.h> #include <machine/cpu.h> #include <machine/reg.h> #include <machine/ctlreg.h> #include <machine/trap.h> #include <machine/pcb.h> #include <machine/pmap.h> #if defined(MULTIPROCESSOR) && defined(DDB) #include <machine/db_machdep.h> #endif #include <sparc/sparc/cache.h> #include <sparc/sparc/asm.h> #include <sparc/sparc/cpuvar.h> #include <sparc/sparc/memreg.h> #if defined(SUN4D) #include <sparc/sparc/cpuunitvar.h> #endif struct cpu_softc { struct device sc_dev; /* generic device info */ struct cpu_info *sc_cpuinfo; }; /* The following are used externally (sysctl_hw). */ char machine[] = MACHINE; /* from <machine/param.h> */ char machine_arch[] = MACHINE_ARCH; /* from <machine/param.h> */ int cpu_arch; /* sparc architecture version */ char cpu_model[100]; /* machine model (primary CPU) */ extern char machine_model[]; int sparc_ncpus; /* # of CPUs detected by PROM */ struct cpu_info **cpus; u_int cpu_ready_mask; /* the set of CPUs marked as READY */ static int cpu_instance; /* current # of CPUs wired by us */ /* The CPU configuration driver. */ static void cpu_mainbus_attach(struct device *, struct device *, void *); int cpu_mainbus_match(struct device *, struct cfdata *, void *); CFATTACH_DECL(cpu_mainbus, sizeof(struct cpu_softc), cpu_mainbus_match, cpu_mainbus_attach, NULL, NULL); #if defined(SUN4D) static int cpu_cpuunit_match(struct device *, struct cfdata *, void *); static void cpu_cpuunit_attach(struct device *, struct device *, void *); CFATTACH_DECL(cpu_cpuunit, sizeof(struct cpu_softc), cpu_cpuunit_match, cpu_cpuunit_attach, NULL, NULL); #endif /* SUN4D */ static void cpu_attach(struct cpu_softc *, int, int); static const char *fsrtoname(int, int, int); void cache_print(struct cpu_softc *); void cpu_setup(void); void fpu_init(struct cpu_info *); #define IU_IMPL(psr) ((u_int)(psr) >> 28) #define IU_VERS(psr) (((psr) >> 24) & 0xf) #define SRMMU_IMPL(mmusr) ((u_int)(mmusr) >> 28) #define SRMMU_VERS(mmusr) (((mmusr) >> 24) & 0xf) int bootmid; /* Module ID of boot CPU */ #if defined(MULTIPROCESSOR) void cpu_spinup(struct cpu_info *); struct cpu_info *alloc_cpuinfo_global_va(int, vsize_t *); struct cpu_info *alloc_cpuinfo(void); int go_smp_cpus = 0; /* non-primary CPUs wait for this to go */ /* lock this to send IPI's */ struct simplelock xpmsg_lock = SIMPLELOCK_INITIALIZER; struct cpu_info * alloc_cpuinfo_global_va(int ismaster, vsize_t *sizep) { int align; vaddr_t sva, va; vsize_t sz, esz; /* * Allocate aligned KVA. `cpuinfo' resides at a fixed virtual * address. Since we need to access an other CPU's cpuinfo * structure occasionally, this must be done at a virtual address * that's cache congruent to the fixed address CPUINFO_VA. * * NOTE: we're using the cache properties of the boot CPU to * determine the alignment (XXX). */ align = PAGE_SIZE; if (CACHEINFO.c_totalsize > align) { /* Need a power of two */ while (align <= CACHEINFO.c_totalsize) align <<= 1; align >>= 1; } sz = sizeof(struct cpu_info); if (ismaster == 0) { /* * While we're here, allocate a per-CPU idle PCB and * interrupt stack as well (8KB + 16KB). */ sz += USPACE; /* `idle' u-area for this CPU */ sz += INT_STACK_SIZE; /* interrupt stack for this CPU */ } sz = (sz + PAGE_SIZE - 1) & -PAGE_SIZE; esz = sz + align - PAGE_SIZE; sva = vm_map_min(kernel_map); if (uvm_map(kernel_map, &sva, esz, NULL, UVM_UNKNOWN_OFFSET, 0, UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE, UVM_ADV_RANDOM, UVM_FLAG_NOWAIT))) panic("alloc_cpuinfo_global_va: no virtual space"); va = sva + (((CPUINFO_VA & (align - 1)) + align - sva) & (align - 1)); /* Return excess virtual memory space */ if (va != sva) (void)uvm_unmap(kernel_map, sva, va); if (va + sz != sva + esz) (void)uvm_unmap(kernel_map, va + sz, sva + esz); if (sizep != NULL) *sizep = sz; return ((struct cpu_info *)va); } struct cpu_info * alloc_cpuinfo(void) { vaddr_t va; vsize_t sz; vaddr_t low, high; struct vm_page *m; struct pglist mlist; struct cpu_info *cpi; /* Allocate the aligned VA and determine the size. */ cpi = alloc_cpuinfo_global_va(0, &sz); va = (vaddr_t)cpi; /* Allocate physical pages */ low = vm_first_phys; high = vm_first_phys + vm_num_phys - PAGE_SIZE; if (uvm_pglistalloc(sz, low, high, PAGE_SIZE, 0, &mlist, 1, 0) != 0) panic("alloc_cpuinfo: no pages"); /* Map the pages */ for (m = TAILQ_FIRST(&mlist); m != NULL; m = TAILQ_NEXT(m, pageq.queue)) { paddr_t pa = VM_PAGE_TO_PHYS(m); pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE); va += PAGE_SIZE; } pmap_update(pmap_kernel()); bzero((void *)cpi, sz); /* * Arrange pcb and interrupt stack in the same * way as is done for the boot CPU in locore. */ cpi->eintstack = (void *)((vaddr_t)cpi + sz - USPACE); /* Allocate virtual space for pmap page_copy/page_zero */ va = uvm_km_alloc(kernel_map, 2*PAGE_SIZE, 0, UVM_KMF_VAONLY); if (va == 0) panic("alloc_cpuinfo: no virtual space"); cpi->vpage[0] = (void *)(va + 0); cpi->vpage[1] = (void *)(va + PAGE_SIZE); return (cpi); } #endif /* MULTIPROCESSOR */ #ifdef notdef /* * IU implementations are parceled out to vendors (with some slight * glitches). Printing these is cute but takes too much space. */ static char *iu_vendor[16] = { "Fujitsu", /* and also LSI Logic */ "ROSS", /* ROSS (ex-Cypress) */ "BIT", "LSIL", /* LSI Logic finally got their own */ "TI", /* Texas Instruments */ "Matsushita", "Philips", "Harvest", /* Harvest VLSI Design Center */ "SPEC", /* Systems and Processes Engineering Corporation */ "Weitek", "vendor#10", "vendor#11", "vendor#12", "vendor#13", "vendor#14", "vendor#15" }; #endif /* * 4/110 comment: the 4/110 chops off the top 4 bits of an OBIO address. * this confuses autoconf. for example, if you try and map * 0xfe000000 in obio space on a 4/110 it actually maps 0x0e000000. * this is easy to verify with the PROM. this causes problems * with devices like "esp0 at obio0 addr 0xfa000000" because the * 4/110 treats it as esp0 at obio0 addr 0x0a000000" which is the * address of the 4/110's "sw0" scsi chip. the same thing happens * between zs1 and zs2. since the sun4 line is "closed" and * we know all the "obio" devices that will ever be on it we just * put in some special case "if"'s in the match routines of esp, * dma, and zs. */ int cpu_mainbus_match(struct device *parent, struct cfdata *cf, void *aux) { struct mainbus_attach_args *ma = aux; return (strcmp(cf->cf_name, ma->ma_name) == 0); } static void cpu_mainbus_attach(struct device *parent, struct device *self, void *aux) { struct mainbus_attach_args *ma = aux; struct { uint32_t va; uint32_t size; } *mbprop = NULL; struct openprom_addr *rrp = NULL; struct cpu_info *cpi; int mid, node; int error, n; node = ma->ma_node; mid = (node != 0) ? prom_getpropint(node, "mid", 0) : 0; cpu_attach((struct cpu_softc *)self, node, mid); cpi = ((struct cpu_softc *)self)->sc_cpuinfo; if (cpi == NULL) return; /* * Map CPU mailbox if available */ if (node != 0 && (error = prom_getprop(node, "mailbox-virtual", sizeof(*mbprop), &n, &mbprop)) == 0) { cpi->mailbox = mbprop->va; free(mbprop, M_DEVBUF); } else if (node != 0 && (error = prom_getprop(node, "mailbox", sizeof(struct openprom_addr), &n, &rrp)) == 0) { /* XXX - map cached/uncached? If cached, deal with * cache congruency! */ if (rrp[0].oa_space == 0) printf("%s: mailbox in mem space\n", self->dv_xname); if (bus_space_map(ma->ma_bustag, BUS_ADDR(rrp[0].oa_space, rrp[0].oa_base), rrp[0].oa_size, BUS_SPACE_MAP_LINEAR, &cpi->mailbox) != 0) panic("%s: can't map CPU mailbox", self->dv_xname); free(rrp, M_DEVBUF); } /* * Map Module Control Space if available */ if (cpi->mxcc == 0) /* We only know what it means on MXCCs */ return; rrp = NULL; if (node == 0 || (error = prom_getprop(node, "reg", sizeof(struct openprom_addr), &n, &rrp)) != 0) return; /* register set #0 is the MBus port register */ if (bus_space_map(ma->ma_bustag, BUS_ADDR(rrp[0].oa_space, rrp[0].oa_base), rrp[0].oa_size, BUS_SPACE_MAP_LINEAR, &cpi->ci_mbusport) != 0) { panic("%s: can't map CPU regs", self->dv_xname); } /* register set #1: MCXX control */ if (bus_space_map(ma->ma_bustag, BUS_ADDR(rrp[1].oa_space, rrp[1].oa_base), rrp[1].oa_size, BUS_SPACE_MAP_LINEAR, &cpi->ci_mxccregs) != 0) { panic("%s: can't map CPU regs", self->dv_xname); } /* register sets #3 and #4 are E$ cache data and tags */ free(rrp, M_DEVBUF); } #if defined(SUN4D) static int cpu_cpuunit_match(struct device *parent, struct cfdata *cf, void *aux) { struct cpuunit_attach_args *cpua = aux; return (strcmp(cf->cf_name, cpua->cpua_type) == 0); } static void cpu_cpuunit_attach(struct device *parent, struct device *self, void *aux) { struct cpuunit_attach_args *cpua = aux; cpu_attach((struct cpu_softc *)self, cpua->cpua_node, cpua->cpua_device_id); } #endif /* SUN4D */ /* * Attach the CPU. * Discover interesting goop about the virtual address cache * (slightly funny place to do it, but this is where it is to be found). */ static void cpu_attach(struct cpu_softc *sc, int node, int mid) { struct cpu_info *cpi; /* * The first CPU we're attaching must be the boot CPU. * (see autoconf.c and cpuunit.c) */ if (cpus == NULL) { cpus = malloc(sparc_ncpus * sizeof(cpi), M_DEVBUF, M_NOWAIT); bzero(cpus, sparc_ncpus * sizeof(cpi)); getcpuinfo(&cpuinfo, node); #if defined(MULTIPROCESSOR) /* * Allocate a suitable global VA for the boot CPU's * cpu_info (which is already statically allocated), * and double map it to that global VA. Then fixup * the self-reference to use the globalized address. */ cpi = sc->sc_cpuinfo = alloc_cpuinfo_global_va(1, NULL); pmap_globalize_boot_cpuinfo(cpi); cpuinfo.ci_self = cpi; /* XXX - fixup lwp0 and idlelwp l_cpu */ lwp0.l_cpu = cpi; cpi->ci_data.cpu_idlelwp->l_cpu = cpi; cpi->ci_data.cpu_idlelwp->l_mutex = cpi->ci_schedstate.spc_lwplock; #else /* The `local' VA is global for uniprocessor. */ cpi = sc->sc_cpuinfo = (struct cpu_info *)CPUINFO_VA; #endif cpi->master = 1; cpi->eintstack = eintstack; /* Note: `curpcb' is set to `proc0' in locore */ /* * If we haven't been able to determine the Id of the * boot CPU, set it now. In this case we can only boot * from CPU #0 (see also the CPU attach code in autoconf.c) */ if (bootmid == 0) bootmid = mid; } else { #if defined(MULTIPROCESSOR) int error; /* * Allocate and initiize this cpu's cpu_info. */ cpi = sc->sc_cpuinfo = alloc_cpuinfo(); cpi->ci_self = cpi; /* * Call the MI attach which creates an idle LWP for us. */ error = mi_cpu_attach(cpi); if (error != 0) { aprint_normal("\n"); aprint_error("%s: mi_cpu_attach failed with %d\n", sc->sc_dev.dv_xname, error); return; } /* * Note: `eintstack' is set in alloc_cpuinfo() above. * The %wim register will be initialized in cpu_hatch(). */ cpi->ci_curlwp = cpi->ci_data.cpu_idlelwp; cpi->curpcb = (struct pcb *)cpi->ci_curlwp->l_addr; cpi->curpcb->pcb_wim = 1; getcpuinfo(cpi, node); #else sc->sc_cpuinfo = NULL; printf(": no SMP support in kernel\n"); return; #endif } #ifdef DEBUG cpi->redzone = (void *)((long)cpi->eintstack + REDSIZE); #endif /* * Allocate a slot in the cpus[] array such that the following * invariant holds: cpus[cpi->ci_cpuid] == cpi; */ cpus[cpu_instance] = cpi; cpi->ci_cpuid = cpu_instance++; cpi->mid = mid; cpi->node = node; if (sparc_ncpus > 1) { printf(": mid %d", mid); if (mid == 0 && !CPU_ISSUN4D) printf(" [WARNING: mid should not be 0]"); } if (cpi->master) { char buf[100]; cpu_setup(); snprintf(buf, sizeof buf, "%s @ %s MHz, %s FPU", cpi->cpu_name, clockfreq(cpi->hz), cpi->fpu_name); snprintf(cpu_model, sizeof cpu_model, "%s (%s)", machine_model, buf); printf(": %s\n", buf); cache_print(sc); return; } #if defined(MULTIPROCESSOR) /* for now use the fixed virtual addresses setup in autoconf.c */ cpi->intreg_4m = (struct icr_pi *) (PI_INTR_VA + (_MAXNBPG * CPU_MID2CPUNO(mid))); /* Now start this CPU */ cpu_spinup(cpi); printf(": %s @ %s MHz, %s FPU\n", cpi->cpu_name, clockfreq(cpi->hz), cpi->fpu_name); cache_print(sc); if (sparc_ncpus > 1 && cpu_instance == sparc_ncpus) { int n; /* * Install MP cache flush functions, unless the * single-processor versions are no-ops. */ for (n = 0; n < sparc_ncpus; n++) { cpi = cpus[n]; if (cpi == NULL) continue; #define SET_CACHE_FUNC(x) \ if (cpi->x != __CONCAT(noop_,x)) cpi->x = __CONCAT(smp_,x) SET_CACHE_FUNC(vcache_flush_page); SET_CACHE_FUNC(vcache_flush_segment); SET_CACHE_FUNC(vcache_flush_region); SET_CACHE_FUNC(vcache_flush_context); } } #endif /* MULTIPROCESSOR */ } #if defined(MULTIPROCESSOR) /* * Start secondary processors in motion. */ void cpu_boot_secondary_processors(void) { int n; if (cpu_instance != sparc_ncpus) { printf("NOTICE: only %d out of %d CPUs were configured\n", cpu_instance, sparc_ncpus); return; } printf("cpu0: booting secondary processors:"); for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (cpi == NULL || cpuinfo.mid == cpi->mid || (cpi->flags & CPUFLG_HATCHED) == 0) continue; printf(" cpu%d", cpi->ci_cpuid); cpi->flags |= CPUFLG_READY; cpu_ready_mask |= (1 << n); } /* Mark the boot CPU as ready */ cpuinfo.flags |= CPUFLG_READY; cpu_ready_mask |= (1 << 0); /* Tell the other CPU's to start up. */ go_smp_cpus = 1; printf("\n"); } #endif /* MULTIPROCESSOR */ /* * Finish CPU attach. * Must be run by the CPU which is being attached. */ void cpu_setup(void) { if (cpuinfo.hotfix) (*cpuinfo.hotfix)(&cpuinfo); /* Initialize FPU */ fpu_init(&cpuinfo); /* Enable the cache */ cpuinfo.cache_enable(); cpuinfo.flags |= CPUFLG_HATCHED; } #if defined(MULTIPROCESSOR) extern void cpu_hatch(void); /* in locore.s */ /* * Allocate per-CPU data, then start up this CPU using PROM. */ void cpu_spinup(struct cpu_info *cpi) { struct openprom_addr oa; void *pc = (void *)cpu_hatch; int n; /* Setup CPU-specific MMU tables */ pmap_alloc_cpu(cpi); cpi->flags &= ~CPUFLG_HATCHED; /* * The physical address of the context table is passed to * the PROM in a "physical address descriptor". */ oa.oa_space = 0; oa.oa_base = (uint32_t)cpi->ctx_tbl_pa; oa.oa_size = cpi->mmu_ncontext * sizeof(cpi->ctx_tbl[0]); /*???*/ /* * Flush entire cache here, since the CPU may start with * caches off, hence no cache-coherency may be assumed. */ cpuinfo.cache_flush_all(); prom_cpustart(cpi->node, &oa, 0, pc); /* * Wait for this CPU to spin up. */ for (n = 10000; n != 0; n--) { cache_flush((void *) __UNVOLATILE(&cpi->flags), sizeof(cpi->flags)); if (cpi->flags & CPUFLG_HATCHED) return; delay(100); } printf("CPU did not spin up\n"); } /* * Call a function on some CPUs. `cpuset' can be set to CPUSET_ALL * to call every CPU, or `1 << cpi->ci_cpuid' for each CPU to call. */ void xcall(xcall_func_t func, xcall_trap_t trap, int arg0, int arg1, int arg2, u_int cpuset) { int s, n, i, done, callself, mybit; volatile struct xpmsg_func *p; int fasttrap; /* XXX - note p->retval is probably no longer useful */ mybit = (1 << cpuinfo.ci_cpuid); callself = func && (cpuset & mybit) != 0; cpuset &= ~mybit; /* * If no cpus are configured yet, just call ourselves. */ if (cpus == NULL) { p = &cpuinfo.msg.u.xpmsg_func; if (callself) p->retval = (*func)(arg0, arg1, arg2); return; } /* Mask any CPUs that are not ready */ cpuset &= cpu_ready_mask; /* prevent interrupts that grab the kernel lock */ s = splsched(); #ifdef DEBUG if (!cold) { u_int pc, lvl = ((u_int)s & PSR_PIL) >> 8; if (lvl > IPL_SCHED) { __asm("mov %%i7, %0" : "=r" (pc) : ); printf_nolog("%d: xcall at lvl %u from 0x%x\n", cpu_number(), lvl, pc); } } #endif LOCK_XPMSG(); /* * Firstly, call each CPU. We do this so that they might have * finished by the time we start looking. */ fasttrap = trap != NULL ? 1 : 0; for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (!cpi) continue; /* Note: n == cpi->ci_cpuid */ if ((cpuset & (1 << n)) == 0) continue; cpi->msg.tag = XPMSG_FUNC; cpi->msg.complete = 0; p = &cpi->msg.u.xpmsg_func; p->func = func; p->trap = trap; p->arg0 = arg0; p->arg1 = arg1; p->arg2 = arg2; /* Fast cross calls use interrupt level 14 */ raise_ipi(cpi,13+fasttrap);/*xcall_cookie->pil*/ } /* * Second, call ourselves. */ p = &cpuinfo.msg.u.xpmsg_func; if (callself) p->retval = (*func)(arg0, arg1, arg2); /* * Lastly, start looping, waiting for all CPUs to register that they * have completed (bailing if it takes "too long", being loud about * this in the process). */ done = 0; i = 100000; /* time-out, not too long, but still an _AGE_ */ while (!done) { if (--i < 0) { printf_nolog("xcall(cpu%d,%p): couldn't ping cpus:", cpu_number(), func); } done = 1; for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (!cpi || (cpuset & (1 << n)) == 0) continue; if (cpi->msg.complete == 0) { if (i < 0) { printf_nolog(" cpu%d", cpi->ci_cpuid); } else { done = 0; break; } } } } if (i < 0) printf_nolog("\n"); UNLOCK_XPMSG(); splx(s); } /* * Tell all CPUs other than the current one to enter the PROM idle loop. */ void mp_pause_cpus(void) { int n; if (cpus == NULL) return; for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (cpi == NULL || cpuinfo.mid == cpi->mid) continue; /* * This PROM utility will put the OPENPROM_MBX_ABORT * message (0xfc) in the target CPU's mailbox and then * send it a level 15 soft interrupt. */ if (prom_cpuidle(cpi->node) != 0) printf("cpu%d could not be paused\n", cpi->ci_cpuid); } } /* * Resume all idling CPUs. */ void mp_resume_cpus(void) { int n; if (cpus == NULL) return; for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (cpi == NULL || cpuinfo.mid == cpi->mid) continue; /* * This PROM utility makes the target CPU return * from its prom_cpuidle(0) call (see intr.c:nmi_soft()). */ if (prom_cpuresume(cpi->node) != 0) printf("cpu%d could not be resumed\n", cpi->ci_cpuid); } } /* * Tell all CPUs except the current one to hurry back into the prom */ void mp_halt_cpus(void) { int n; if (cpus == NULL) return; for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; int r; if (cpi == NULL || cpuinfo.mid == cpi->mid) continue; /* * This PROM utility will put the OPENPROM_MBX_STOP * message (0xfb) in the target CPU's mailbox and then * send it a level 15 soft interrupt. */ r = prom_cpustop(cpi->node); printf("cpu%d %shalted\n", cpi->ci_cpuid, r == 0 ? "" : "(boot CPU?) can not be "); } } #if defined(DDB) void mp_pause_cpus_ddb(void) { int n; if (cpus == NULL) return; for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (cpi == NULL || cpi->mid == cpuinfo.mid) continue; cpi->msg_lev15.tag = XPMSG15_PAUSECPU; raise_ipi(cpi,15); /* high priority intr */ } } void mp_resume_cpus_ddb(void) { int n; if (cpus == NULL) return; for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (cpi == NULL || cpuinfo.mid == cpi->mid) continue; /* tell it to continue */ cpi->flags &= ~CPUFLG_PAUSED; } } #endif /* DDB */ #endif /* MULTIPROCESSOR */ /* * fpu_init() must be run on associated CPU. */ void fpu_init(struct cpu_info *sc) { struct fpstate fpstate; int fpuvers; /* * Get the FSR and clear any exceptions. If we do not unload * the queue here and it is left over from a previous crash, we * will panic in the first loadfpstate(), due to a sequence * error, so we need to dump the whole state anyway. * * If there is no FPU, trap.c will advance over all the stores, * so we initialize fs_fsr here. */ /* 7 is reserved for "none" */ fpstate.fs_fsr = 7 << FSR_VER_SHIFT; savefpstate(&fpstate); sc->fpuvers = fpuvers = (fpstate.fs_fsr >> FSR_VER_SHIFT) & (FSR_VER >> FSR_VER_SHIFT); if (fpuvers == 7) { sc->fpu_name = "no"; return; } sc->fpupresent = 1; sc->fpu_name = fsrtoname(sc->cpu_impl, sc->cpu_vers, fpuvers); if (sc->fpu_name == NULL) { sprintf(sc->fpu_namebuf, "version 0x%x", fpuvers); sc->fpu_name = sc->fpu_namebuf; } } void cache_print(struct cpu_softc *sc) { struct cacheinfo *ci = &sc->sc_cpuinfo->cacheinfo; if (sc->sc_cpuinfo->flags & CPUFLG_SUN4CACHEBUG) printf("%s: cache chip bug; trap page uncached\n", sc->sc_dev.dv_xname); printf("%s: ", sc->sc_dev.dv_xname); if (ci->c_totalsize == 0) { printf("no cache\n"); return; } if (ci->c_split) { const char *sep = ""; printf("%s", (ci->c_physical ? "physical " : "")); if (ci->ic_totalsize > 0) { printf("%s%dK instruction (%d b/l)", sep, ci->ic_totalsize/1024, ci->ic_linesize); sep = ", "; } if (ci->dc_totalsize > 0) { printf("%s%dK data (%d b/l)", sep, ci->dc_totalsize/1024, ci->dc_linesize); } } else if (ci->c_physical) { /* combined, physical */ printf("physical %dK combined cache (%d bytes/line)", ci->c_totalsize/1024, ci->c_linesize); } else { /* combined, virtual */ printf("%dK byte write-%s, %d bytes/line, %cw flush", ci->c_totalsize/1024, (ci->c_vactype == VAC_WRITETHROUGH) ? "through" : "back", ci->c_linesize, ci->c_hwflush ? 'h' : 's'); } if (ci->ec_totalsize > 0) { printf(", %dK external (%d b/l)", ci->ec_totalsize/1024, ci->ec_linesize); } printf(": "); if (ci->c_enabled) printf("cache enabled"); printf("\n"); } /*------------*/ void cpumatch_unknown(struct cpu_info *, struct module_info *, int); void cpumatch_sun4(struct cpu_info *, struct module_info *, int); void cpumatch_sun4c(struct cpu_info *, struct module_info *, int); void cpumatch_ms1(struct cpu_info *, struct module_info *, int); void cpumatch_viking(struct cpu_info *, struct module_info *, int); void cpumatch_hypersparc(struct cpu_info *, struct module_info *, int); void cpumatch_turbosparc(struct cpu_info *, struct module_info *, int); void getcacheinfo_sun4(struct cpu_info *, int node); void getcacheinfo_sun4c(struct cpu_info *, int node); void getcacheinfo_obp(struct cpu_info *, int node); void getcacheinfo_sun4d(struct cpu_info *, int node); void sun4_hotfix(struct cpu_info *); void viking_hotfix(struct cpu_info *); void turbosparc_hotfix(struct cpu_info *); void swift_hotfix(struct cpu_info *); void ms1_mmu_enable(void); void viking_mmu_enable(void); void swift_mmu_enable(void); void hypersparc_mmu_enable(void); void srmmu_get_syncflt(void); void ms1_get_syncflt(void); void viking_get_syncflt(void); void swift_get_syncflt(void); void turbosparc_get_syncflt(void); void hypersparc_get_syncflt(void); void cypress_get_syncflt(void); int srmmu_get_asyncflt(u_int *, u_int *); int hypersparc_get_asyncflt(u_int *, u_int *); int cypress_get_asyncflt(u_int *, u_int *); int no_asyncflt_regs(u_int *, u_int *); int hypersparc_getmid(void); /* cypress and hypersparc can share this function, see ctlreg.h */ #define cypress_getmid hypersparc_getmid int viking_getmid(void); int (*moduleerr_handler)(void); int viking_module_error(void); struct module_info module_unknown = { CPUTYP_UNKNOWN, VAC_UNKNOWN, cpumatch_unknown }; void cpumatch_unknown(struct cpu_info *sc, struct module_info *mp, int node) { panic("Unknown CPU type: " "cpu: impl %d, vers %d; mmu: impl %d, vers %d", sc->cpu_impl, sc->cpu_vers, sc->mmu_impl, sc->mmu_vers); } #if defined(SUN4) struct module_info module_sun4 = { CPUTYP_UNKNOWN, VAC_WRITETHROUGH, cpumatch_sun4, getcacheinfo_sun4, sun4_hotfix, 0, sun4_cache_enable, 0, 0, /* ncontext set in `match' function */ 0, /* get_syncflt(); unused in sun4c */ 0, /* get_asyncflt(); unused in sun4c */ sun4_cache_flush, sun4_vcache_flush_page, NULL, sun4_vcache_flush_segment, NULL, sun4_vcache_flush_region, NULL, sun4_vcache_flush_context, NULL, NULL, NULL, noop_pcache_flush_page, noop_pure_vcache_flush, noop_cache_flush_all, 0, pmap_zero_page4_4c, pmap_copy_page4_4c }; void getcacheinfo_sun4(struct cpu_info *sc, int node) { struct cacheinfo *ci = &sc->cacheinfo; switch (sc->cpu_type) { case CPUTYP_4_100: ci->c_vactype = VAC_NONE; ci->c_totalsize = 0; ci->c_hwflush = 0; ci->c_linesize = 0; ci->c_l2linesize = 0; ci->c_split = 0; ci->c_nlines = 0; /* Override cache flush functions */ sc->cache_flush = noop_cache_flush; sc->sp_vcache_flush_page = noop_vcache_flush_page; sc->sp_vcache_flush_segment = noop_vcache_flush_segment; sc->sp_vcache_flush_region = noop_vcache_flush_region; sc->sp_vcache_flush_context = noop_vcache_flush_context; break; case CPUTYP_4_200: ci->c_vactype = VAC_WRITEBACK; ci->c_totalsize = 128*1024; ci->c_hwflush = 0; ci->c_linesize = 16; ci->c_l2linesize = 4; ci->c_split = 0; ci->c_nlines = ci->c_totalsize >> ci->c_l2linesize; break; case CPUTYP_4_300: ci->c_vactype = VAC_WRITEBACK; ci->c_totalsize = 128*1024; ci->c_hwflush = 0; ci->c_linesize = 16; ci->c_l2linesize = 4; ci->c_split = 0; ci->c_nlines = ci->c_totalsize >> ci->c_l2linesize; sc->flags |= CPUFLG_SUN4CACHEBUG; break; case CPUTYP_4_400: ci->c_vactype = VAC_WRITEBACK; ci->c_totalsize = 128 * 1024; ci->c_hwflush = 0; ci->c_linesize = 32; ci->c_l2linesize = 5; ci->c_split = 0; ci->c_nlines = ci->c_totalsize >> ci->c_l2linesize; break; } } void cpumatch_sun4(struct cpu_info *sc, struct module_info *mp, int node) { struct idprom *idp = prom_getidprom(); switch (idp->idp_machtype) { case ID_SUN4_100: sc->cpu_type = CPUTYP_4_100; sc->classlvl = 100; sc->mmu_ncontext = 8; sc->mmu_nsegment = 256; /*XXX*/ sc->hz = 14280000; break; case ID_SUN4_200: sc->cpu_type = CPUTYP_4_200; sc->classlvl = 200; sc->mmu_nsegment = 512; sc->mmu_ncontext = 16; /*XXX*/ sc->hz = 16670000; break; case ID_SUN4_300: sc->cpu_type = CPUTYP_4_300; sc->classlvl = 300; sc->mmu_nsegment = 256; sc->mmu_ncontext = 16; /*XXX*/ sc->hz = 25000000; break; case ID_SUN4_400: sc->cpu_type = CPUTYP_4_400; sc->classlvl = 400; sc->mmu_nsegment = 1024; sc->mmu_ncontext = 64; sc->mmu_nregion = 256; /*XXX*/ sc->hz = 33000000; sc->sun4_mmu3l = 1; break; } } #endif /* SUN4 */ #if defined(SUN4C) struct module_info module_sun4c = { CPUTYP_UNKNOWN, VAC_WRITETHROUGH, cpumatch_sun4c, getcacheinfo_sun4c, sun4_hotfix, 0, sun4_cache_enable, 0, 0, /* ncontext set in `match' function */ 0, /* get_syncflt(); unused in sun4c */ 0, /* get_asyncflt(); unused in sun4c */ sun4_cache_flush, sun4_vcache_flush_page, NULL, sun4_vcache_flush_segment, NULL, sun4_vcache_flush_region, NULL, sun4_vcache_flush_context, NULL, NULL, NULL, noop_pcache_flush_page, noop_pure_vcache_flush, noop_cache_flush_all, 0, pmap_zero_page4_4c, pmap_copy_page4_4c }; void cpumatch_sun4c(struct cpu_info *sc, struct module_info *mp, int node) { int rnode; rnode = findroot(); sc->mmu_npmeg = sc->mmu_nsegment = prom_getpropint(rnode, "mmu-npmg", 128); sc->mmu_ncontext = prom_getpropint(rnode, "mmu-nctx", 8); /* Get clock frequency */ sc->hz = prom_getpropint(rnode, "clock-frequency", 0); } void getcacheinfo_sun4c(struct cpu_info *sc, int node) { struct cacheinfo *ci = &sc->cacheinfo; int i, l; if (node == 0) /* Bootstrapping */ return; /* Sun4c's have only virtually-addressed caches */ ci->c_physical = 0; ci->c_totalsize = prom_getpropint(node, "vac-size", 65536); /* * Note: vac-hwflush is spelled with an underscore * on the 4/75s. */ ci->c_hwflush = prom_getpropint(node, "vac_hwflush", 0) | prom_getpropint(node, "vac-hwflush", 0); ci->c_linesize = l = prom_getpropint(node, "vac-linesize", 16); for (i = 0; (1 << i) < l; i++) /* void */; if ((1 << i) != l) panic("bad cache line size %d", l); ci->c_l2linesize = i; ci->c_associativity = 1; ci->c_nlines = ci->c_totalsize >> i; ci->c_vactype = VAC_WRITETHROUGH; /* * Machines with "buserr-type" 1 have a bug in the cache * chip that affects traps. (I wish I knew more about this * mysterious buserr-type variable....) */ if (prom_getpropint(node, "buserr-type", 0) == 1) sc->flags |= CPUFLG_SUN4CACHEBUG; } #endif /* SUN4C */ void sun4_hotfix(struct cpu_info *sc) { if ((sc->flags & CPUFLG_SUN4CACHEBUG) != 0) kvm_uncache((char *)trapbase, 1); /* Use the hardware-assisted page flush routine, if present */ if (sc->cacheinfo.c_hwflush) sc->vcache_flush_page = sun4_vcache_flush_page_hw; } #if defined(SUN4M) void getcacheinfo_obp(struct cpu_info *sc, int node) { struct cacheinfo *ci = &sc->cacheinfo; int i, l; if (node == 0) /* Bootstrapping */ return; /* * Determine the Sun4m cache organization. */ ci->c_physical = node_has_property(node, "cache-physical?"); if (prom_getpropint(node, "ncaches", 1) == 2) ci->c_split = 1; else ci->c_split = 0; /* hwflush is used only by sun4/4c code */ ci->c_hwflush = 0; if (node_has_property(node, "icache-nlines") && node_has_property(node, "dcache-nlines") && ci->c_split) { /* Harvard architecture: get I and D cache sizes */ ci->ic_nlines = prom_getpropint(node, "icache-nlines", 0); ci->ic_linesize = l = prom_getpropint(node, "icache-line-size", 0); for (i = 0; (1 << i) < l && l; i++) /* void */; if ((1 << i) != l && l) panic("bad icache line size %d", l); ci->ic_l2linesize = i; ci->ic_associativity = prom_getpropint(node, "icache-associativity", 1); ci->ic_totalsize = l * ci->ic_nlines * ci->ic_associativity; ci->dc_nlines = prom_getpropint(node, "dcache-nlines", 0); ci->dc_linesize = l = prom_getpropint(node, "dcache-line-size",0); for (i = 0; (1 << i) < l && l; i++) /* void */; if ((1 << i) != l && l) panic("bad dcache line size %d", l); ci->dc_l2linesize = i; ci->dc_associativity = prom_getpropint(node, "dcache-associativity", 1); ci->dc_totalsize = l * ci->dc_nlines * ci->dc_associativity; ci->c_l2linesize = min(ci->ic_l2linesize, ci->dc_l2linesize); ci->c_linesize = min(ci->ic_linesize, ci->dc_linesize); ci->c_totalsize = max(ci->ic_totalsize, ci->dc_totalsize); ci->c_nlines = ci->c_totalsize >> ci->c_l2linesize; } else { /* unified I/D cache */ ci->c_nlines = prom_getpropint(node, "cache-nlines", 128); ci->c_linesize = l = prom_getpropint(node, "cache-line-size", 0); for (i = 0; (1 << i) < l && l; i++) /* void */; if ((1 << i) != l && l) panic("bad cache line size %d", l); ci->c_l2linesize = i; ci->c_associativity = prom_getpropint(node, "cache-associativity", 1); ci->dc_associativity = ci->ic_associativity = ci->c_associativity; ci->c_totalsize = l * ci->c_nlines * ci->c_associativity; } if (node_has_property(node, "ecache-nlines")) { /* we have a L2 "e"xternal cache */ ci->ec_nlines = prom_getpropint(node, "ecache-nlines", 32768); ci->ec_linesize = l = prom_getpropint(node, "ecache-line-size", 0); for (i = 0; (1 << i) < l && l; i++) /* void */; if ((1 << i) != l && l) panic("bad ecache line size %d", l); ci->ec_l2linesize = i; ci->ec_associativity = prom_getpropint(node, "ecache-associativity", 1); ci->ec_totalsize = l * ci->ec_nlines * ci->ec_associativity; } if (ci->c_totalsize == 0) printf("warning: couldn't identify cache\n"); } /* * We use the max. number of contexts on the micro and * hyper SPARCs. The SuperSPARC would let us use up to 65536 * contexts (by powers of 2), but we keep it at 4096 since * the table must be aligned to #context*4. With 4K contexts, * we waste at most 16K of memory. Note that the context * table is *always* page-aligned, so there can always be * 1024 contexts without sacrificing memory space (given * that the chip supports 1024 contexts). * * Currently known limits: MS1=64, MS2=256, HS=4096, SS=65536 * some old SS's=4096 */ /* TI Microsparc I */ struct module_info module_ms1 = { CPUTYP_MS1, VAC_NONE, cpumatch_ms1, getcacheinfo_obp, 0, ms1_mmu_enable, ms1_cache_enable, 0, 64, ms1_get_syncflt, no_asyncflt_regs, ms1_cache_flush, noop_vcache_flush_page, NULL, noop_vcache_flush_segment, NULL, noop_vcache_flush_region, NULL, noop_vcache_flush_context, NULL, noop_vcache_flush_range, NULL, noop_pcache_flush_page, noop_pure_vcache_flush, ms1_cache_flush_all, memerr4m, pmap_zero_page4m, pmap_copy_page4m }; void cpumatch_ms1(struct cpu_info *sc, struct module_info *mp, int node) { /* * Turn off page zeroing in the idle loop; an unidentified * bug causes (very sporadic) user process corruption. */ vm_page_zero_enable = 0; } void ms1_mmu_enable(void) { } /* TI Microsparc II */ struct module_info module_ms2 = { /* UNTESTED */ CPUTYP_MS2, VAC_WRITETHROUGH, 0, getcacheinfo_obp, 0, 0, swift_cache_enable, 0, 256, srmmu_get_syncflt, srmmu_get_asyncflt, srmmu_cache_flush, srmmu_vcache_flush_page, NULL, srmmu_vcache_flush_segment, NULL, srmmu_vcache_flush_region, NULL, srmmu_vcache_flush_context, NULL, srmmu_vcache_flush_range, NULL, noop_pcache_flush_page, noop_pure_vcache_flush, srmmu_cache_flush_all, memerr4m, pmap_zero_page4m, pmap_copy_page4m }; struct module_info module_swift = { CPUTYP_MS2, VAC_WRITETHROUGH, 0, getcacheinfo_obp, swift_hotfix, 0, swift_cache_enable, 0, 256, swift_get_syncflt, no_asyncflt_regs, srmmu_cache_flush, srmmu_vcache_flush_page, NULL, srmmu_vcache_flush_segment, NULL, srmmu_vcache_flush_region, NULL, srmmu_vcache_flush_context, NULL, srmmu_vcache_flush_range, NULL, noop_pcache_flush_page, noop_pure_vcache_flush, srmmu_cache_flush_all, memerr4m, pmap_zero_page4m, pmap_copy_page4m }; void swift_hotfix(struct cpu_info *sc) { int pcr = lda(SRMMU_PCR, ASI_SRMMU); /* Turn off branch prediction */ pcr &= ~SWIFT_PCR_BF; sta(SRMMU_PCR, ASI_SRMMU, pcr); } void swift_mmu_enable(void) { } /* ROSS Hypersparc */ struct module_info module_hypersparc = { CPUTYP_UNKNOWN, VAC_WRITEBACK, cpumatch_hypersparc, getcacheinfo_obp, 0, hypersparc_mmu_enable, hypersparc_cache_enable, hypersparc_getmid, 4096, hypersparc_get_syncflt, hypersparc_get_asyncflt, srmmu_cache_flush, srmmu_vcache_flush_page, ft_srmmu_vcache_flush_page, srmmu_vcache_flush_segment, ft_srmmu_vcache_flush_segment, srmmu_vcache_flush_region, ft_srmmu_vcache_flush_region, srmmu_vcache_flush_context, ft_srmmu_vcache_flush_context, srmmu_vcache_flush_range, ft_srmmu_vcache_flush_range, noop_pcache_flush_page, hypersparc_pure_vcache_flush, hypersparc_cache_flush_all, hypersparc_memerr, pmap_zero_page4m, pmap_copy_page4m }; void cpumatch_hypersparc(struct cpu_info *sc, struct module_info *mp, int node) { sc->cpu_type = CPUTYP_HS_MBUS;/*XXX*/ if (node == 0) { /* Flush I-cache */ sta(0, ASI_HICACHECLR, 0); /* Disable `unimplemented flush' traps during boot-up */ wrasr(rdasr(HYPERSPARC_ASRNUM_ICCR) | HYPERSPARC_ICCR_FTD, HYPERSPARC_ASRNUM_ICCR); } } void hypersparc_mmu_enable(void) { #if 0 int pcr; pcr = lda(SRMMU_PCR, ASI_SRMMU); pcr |= HYPERSPARC_PCR_C; pcr &= ~HYPERSPARC_PCR_CE; sta(SRMMU_PCR, ASI_SRMMU, pcr); #endif } int hypersparc_getmid(void) { u_int pcr = lda(SRMMU_PCR, ASI_SRMMU); return ((pcr & HYPERSPARC_PCR_MID) >> 15); } /* Cypress 605 */ struct module_info module_cypress = { CPUTYP_CYPRESS, VAC_WRITEBACK, 0, getcacheinfo_obp, 0, 0, cypress_cache_enable, cypress_getmid, 4096, cypress_get_syncflt, cypress_get_asyncflt, srmmu_cache_flush, srmmu_vcache_flush_page, ft_srmmu_vcache_flush_page, srmmu_vcache_flush_segment, ft_srmmu_vcache_flush_segment, srmmu_vcache_flush_region, ft_srmmu_vcache_flush_region, srmmu_vcache_flush_context, ft_srmmu_vcache_flush_context, srmmu_vcache_flush_range, ft_srmmu_vcache_flush_range, noop_pcache_flush_page, noop_pure_vcache_flush, cypress_cache_flush_all, memerr4m, pmap_zero_page4m, pmap_copy_page4m }; /* Fujitsu Turbosparc */ struct module_info module_turbosparc = { CPUTYP_MS2, VAC_WRITEBACK, cpumatch_turbosparc, getcacheinfo_obp, turbosparc_hotfix, 0, turbosparc_cache_enable, 0, 256, turbosparc_get_syncflt, no_asyncflt_regs, srmmu_cache_flush, srmmu_vcache_flush_page, NULL, srmmu_vcache_flush_segment, NULL, srmmu_vcache_flush_region, NULL, srmmu_vcache_flush_context, NULL, srmmu_vcache_flush_range, NULL, noop_pcache_flush_page, noop_pure_vcache_flush, srmmu_cache_flush_all, memerr4m, pmap_zero_page4m, pmap_copy_page4m }; void cpumatch_turbosparc(struct cpu_info *sc, struct module_info *mp, int node) { int i; if (node == 0 || sc->master == 0) return; i = getpsr(); if (sc->cpu_vers == IU_VERS(i)) return; /* * A cloaked Turbosparc: clear any items in cpuinfo that * might have been set to uS2 versions during bootstrap. */ sc->cpu_name = 0; sc->mmu_ncontext = 0; sc->cpu_type = 0; sc->cacheinfo.c_vactype = 0; sc->hotfix = 0; sc->mmu_enable = 0; sc->cache_enable = 0; sc->get_syncflt = 0; sc->cache_flush = 0; sc->sp_vcache_flush_page = 0; sc->sp_vcache_flush_segment = 0; sc->sp_vcache_flush_region = 0; sc->sp_vcache_flush_context = 0; sc->pcache_flush_page = 0; } void turbosparc_hotfix(struct cpu_info *sc) { int pcf; pcf = lda(SRMMU_PCFG, ASI_SRMMU); if (pcf & TURBOSPARC_PCFG_US2) { /* Turn off uS2 emulation bit */ pcf &= ~TURBOSPARC_PCFG_US2; sta(SRMMU_PCFG, ASI_SRMMU, pcf); } } #endif /* SUN4M */ #if defined(SUN4M) struct module_info module_viking = { CPUTYP_UNKNOWN, /* set in cpumatch() */ VAC_NONE, cpumatch_viking, getcacheinfo_obp, viking_hotfix, viking_mmu_enable, viking_cache_enable, viking_getmid, 4096, viking_get_syncflt, no_asyncflt_regs, /* supersparcs use cached DVMA, no need to flush */ noop_cache_flush, noop_vcache_flush_page, NULL, noop_vcache_flush_segment, NULL, noop_vcache_flush_region, NULL, noop_vcache_flush_context, NULL, noop_vcache_flush_range, NULL, viking_pcache_flush_page, noop_pure_vcache_flush, noop_cache_flush_all, viking_memerr, pmap_zero_page4m, pmap_copy_page4m }; #endif /* SUN4M */ #if defined(SUN4M) || defined(SUN4D) void cpumatch_viking(struct cpu_info *sc, struct module_info *mp, int node) { if (node == 0) viking_hotfix(sc); } void viking_hotfix(struct cpu_info *sc) { static int mxcc = -1; int pcr = lda(SRMMU_PCR, ASI_SRMMU); /* Test if we're directly on the MBus */ if ((pcr & VIKING_PCR_MB) == 0) { sc->mxcc = 1; sc->flags |= CPUFLG_CACHE_MANDATORY; sc->zero_page = pmap_zero_page_viking_mxcc; sc->copy_page = pmap_copy_page_viking_mxcc; moduleerr_handler = viking_module_error; /* * Ok to cache PTEs; set the flag here, so we don't * uncache in pmap_bootstrap(). */ if ((pcr & VIKING_PCR_TC) == 0) printf("[viking: PCR_TC is off]"); else sc->flags |= CPUFLG_CACHEPAGETABLES; } else { #ifdef MULTIPROCESSOR if ((sparc_ncpus > 1) && (sc->cacheinfo.ec_totalsize == 0)) sc->cache_flush = srmmu_cache_flush; #endif } /* Check all modules have the same MXCC configuration */ if (mxcc != -1 && sc->mxcc != mxcc) panic("MXCC module mismatch"); mxcc = sc->mxcc; /* XXX! */ if (sc->mxcc) sc->cpu_type = CPUTYP_SS1_MBUS_MXCC; else sc->cpu_type = CPUTYP_SS1_MBUS_NOMXCC; } void viking_mmu_enable(void) { int pcr; pcr = lda(SRMMU_PCR, ASI_SRMMU); if (cpuinfo.mxcc) { if ((pcr & VIKING_PCR_TC) == 0) { printf("[viking: turn on PCR_TC]"); } pcr |= VIKING_PCR_TC; cpuinfo.flags |= CPUFLG_CACHEPAGETABLES; } else pcr &= ~VIKING_PCR_TC; sta(SRMMU_PCR, ASI_SRMMU, pcr); } int viking_getmid(void) { if (cpuinfo.mxcc) { u_int v = ldda(MXCC_MBUSPORT, ASI_CONTROL) & 0xffffffff; return ((v >> 24) & 0xf); } return (0); } int viking_module_error(void) { uint64_t v; int n, fatal = 0; /* Report on MXCC error registers in each module */ for (n = 0; n < sparc_ncpus; n++) { struct cpu_info *cpi = cpus[n]; if (cpi == NULL) continue; if (cpi->ci_mxccregs == 0) { printf("\tMXCC registers not mapped\n"); continue; } printf("module%d:\n", cpi->ci_cpuid); v = *((uint64_t *)(cpi->ci_mxccregs + 0xe00)); printf("\tmxcc error 0x%llx\n", v); v = *((uint64_t *)(cpi->ci_mxccregs + 0xb00)); printf("\tmxcc status 0x%llx\n", v); v = *((uint64_t *)(cpi->ci_mxccregs + 0xc00)); printf("\tmxcc reset 0x%llx", v); if (v & MXCC_MRST_WD) printf(" (WATCHDOG RESET)"), fatal = 1; if (v & MXCC_MRST_SI) printf(" (SOFTWARE RESET)"), fatal = 1; printf("\n"); } return (fatal); } #endif /* SUN4M || SUN4D */ #if defined(SUN4D) void getcacheinfo_sun4d(struct cpu_info *sc, int node) { struct cacheinfo *ci = &sc->cacheinfo; int i, l; if (node == 0) /* Bootstrapping */ return; /* * The Sun4d always has TI TMS390Z55 Viking CPUs; we hard-code * much of the cache information here. */ ci->c_physical = 1; ci->c_split = 1; /* hwflush is used only by sun4/4c code */ ci->c_hwflush = 0; ci->ic_nlines = 0x00000040; ci->ic_linesize = 0x00000040; ci->ic_l2linesize = 6; ci->ic_associativity = 0x00000005; ci->ic_totalsize = ci->ic_linesize * ci->ic_nlines * ci->ic_associativity; ci->dc_nlines = 0x00000080; ci->dc_linesize = 0x00000020; ci->dc_l2linesize = 5; ci->dc_associativity = 0x00000004; ci->dc_totalsize = ci->dc_linesize * ci->dc_nlines * ci->dc_associativity; ci->c_l2linesize = min(ci->ic_l2linesize, ci->dc_l2linesize); ci->c_linesize = min(ci->ic_linesize, ci->dc_linesize); ci->c_totalsize = max(ci->ic_totalsize, ci->dc_totalsize); ci->c_nlines = ci->c_totalsize >> ci->c_l2linesize; if (node_has_property(node, "ecache-nlines")) { /* we have a L2 "e"xternal cache */ ci->ec_nlines = prom_getpropint(node, "ecache-nlines", 32768); ci->ec_linesize = l = prom_getpropint(node, "ecache-line-size", 0); for (i = 0; (1 << i) < l && l; i++) /* void */; if ((1 << i) != l && l) panic("bad ecache line size %d", l); ci->ec_l2linesize = i; ci->ec_associativity = prom_getpropint(node, "ecache-associativity", 1); ci->ec_totalsize = l * ci->ec_nlines * ci->ec_associativity; } } struct module_info module_viking_sun4d = { CPUTYP_UNKNOWN, /* set in cpumatch() */ VAC_NONE, cpumatch_viking, getcacheinfo_sun4d, viking_hotfix, viking_mmu_enable, viking_cache_enable, viking_getmid, 4096, viking_get_syncflt, no_asyncflt_regs, /* supersparcs use cached DVMA, no need to flush */ noop_cache_flush, noop_vcache_flush_page, NULL, noop_vcache_flush_segment, NULL, noop_vcache_flush_region, NULL, noop_vcache_flush_context, NULL, noop_vcache_flush_range, NULL, viking_pcache_flush_page, noop_pure_vcache_flush, noop_cache_flush_all, viking_memerr, pmap_zero_page4m, pmap_copy_page4m }; #endif /* SUN4D */ #define ANY -1 /* match any version */ struct cpu_conf { int arch; int cpu_impl; int cpu_vers; int mmu_impl; int mmu_vers; const char *name; struct module_info *minfo; } cpu_conf[] = { #if defined(SUN4) { CPU_SUN4, 0, 0, ANY, ANY, "MB86900/1A or L64801", &module_sun4 }, { CPU_SUN4, 1, 0, ANY, ANY, "L64811", &module_sun4 }, { CPU_SUN4, 1, 1, ANY, ANY, "CY7C601", &module_sun4 }, #endif #if defined(SUN4C) { CPU_SUN4C, 0, 0, ANY, ANY, "MB86900/1A or L64801", &module_sun4c }, { CPU_SUN4C, 1, 0, ANY, ANY, "L64811", &module_sun4c }, { CPU_SUN4C, 1, 1, ANY, ANY, "CY7C601", &module_sun4c }, { CPU_SUN4C, 9, 0, ANY, ANY, "W8601/8701 or MB86903", &module_sun4c }, #endif #if defined(SUN4M) { CPU_SUN4M, 0, 4, 0, 4, "MB86904", &module_swift }, { CPU_SUN4M, 0, 5, 0, 5, "MB86907", &module_turbosparc }, { CPU_SUN4M, 1, 1, 1, 0, "CY7C601/604", &module_cypress }, { CPU_SUN4M, 1, 1, 1, 0xb, "CY7C601/605 (v.b)", &module_cypress }, { CPU_SUN4M, 1, 1, 1, 0xc, "CY7C601/605 (v.c)", &module_cypress }, { CPU_SUN4M, 1, 1, 1, 0xf, "CY7C601/605 (v.f)", &module_cypress }, { CPU_SUN4M, 1, 3, 1, ANY, "CY7C611", &module_cypress }, { CPU_SUN4M, 1, 0xe, 1, 7, "RT620/625", &module_hypersparc }, { CPU_SUN4M, 1, 0xf, 1, 7, "RT620/625", &module_hypersparc }, { CPU_SUN4M, 4, 0, 0, ANY, "TMS390Z50 v0 or TMS390Z55", &module_viking }, { CPU_SUN4M, 4, 1, 0, ANY, "TMS390Z50 v1", &module_viking }, { CPU_SUN4M, 4, 1, 4, ANY, "TMS390S10", &module_ms1 }, { CPU_SUN4M, 4, 2, 0, ANY, "TI_MS2", &module_ms2 }, { CPU_SUN4M, 4, 3, ANY, ANY, "TI_4_3", &module_viking }, { CPU_SUN4M, 4, 4, ANY, ANY, "TI_4_4", &module_viking }, #endif #if defined(SUN4D) { CPU_SUN4D, 4, 0, 0, ANY, "TMS390Z50 v0 or TMS390Z55", &module_viking_sun4d }, #endif { ANY, ANY, ANY, ANY, ANY, "Unknown", &module_unknown } }; void getcpuinfo(struct cpu_info *sc, int node) { struct cpu_conf *mp; int i; int cpu_impl, cpu_vers; int mmu_impl, mmu_vers; /* * Set up main criteria for selection from the CPU configuration * table: the CPU implementation/version fields from the PSR * register, and -- on sun4m machines -- the MMU * implementation/version from the SCR register. */ if (sc->master) { i = getpsr(); if (node == 0 || (cpu_impl = prom_getpropint(node, "psr-implementation", -1)) == -1) cpu_impl = IU_IMPL(i); if (node == 0 || (cpu_vers = prom_getpropint(node, "psr-version", -1)) == -1) cpu_vers = IU_VERS(i); if (CPU_HAS_SRMMU) { i = lda(SRMMU_PCR, ASI_SRMMU); if (node == 0 || (mmu_impl = prom_getpropint(node, "implementation", -1)) == -1) mmu_impl = SRMMU_IMPL(i); if (node == 0 || (mmu_vers = prom_getpropint(node, "version", -1)) == -1) mmu_vers = SRMMU_VERS(i); } else { mmu_impl = ANY; mmu_vers = ANY; } } else { /* * Get CPU version/implementation from ROM. If not * available, assume same as boot CPU. */ cpu_impl = prom_getpropint(node, "psr-implementation", cpuinfo.cpu_impl); cpu_vers = prom_getpropint(node, "psr-version", cpuinfo.cpu_vers); /* Get MMU version/implementation from ROM always */ mmu_impl = prom_getpropint(node, "implementation", -1); mmu_vers = prom_getpropint(node, "version", -1); } for (mp = cpu_conf; ; mp++) { if (mp->arch != cputyp && mp->arch != ANY) continue; #define MATCH(x) (mp->x == x || mp->x == ANY) if (!MATCH(cpu_impl) || !MATCH(cpu_vers) || !MATCH(mmu_impl) || !MATCH(mmu_vers)) continue; #undef MATCH /* * Got CPU type. */ sc->cpu_impl = cpu_impl; sc->cpu_vers = cpu_vers; sc->mmu_impl = mmu_impl; sc->mmu_vers = mmu_vers; if (mp->minfo->cpu_match) { /* Additional fixups */ mp->minfo->cpu_match(sc, mp->minfo, node); } if (sc->cpu_name == 0) sc->cpu_name = mp->name; if (sc->mmu_ncontext == 0) sc->mmu_ncontext = mp->minfo->ncontext; if (sc->cpu_type == 0) sc->cpu_type = mp->minfo->cpu_type; if (sc->cacheinfo.c_vactype == VAC_UNKNOWN) sc->cacheinfo.c_vactype = mp->minfo->vactype; mp->minfo->getcacheinfo(sc, node); if (node && sc->hz == 0 && !CPU_ISSUN4/*XXX*/) { sc->hz = prom_getpropint(node, "clock-frequency", 0); if (sc->hz == 0) { /* * Try to find it in the OpenPROM root... */ sc->hz = prom_getpropint(findroot(), "clock-frequency", 0); } } if (sc->master && mp->minfo->getmid != NULL) bootmid = mp->minfo->getmid(); /* * Copy CPU/MMU/Cache specific routines into cpu_info. */ #define MPCOPY(x) if (sc->x == 0) sc->x = mp->minfo->x; MPCOPY(hotfix); MPCOPY(mmu_enable); MPCOPY(cache_enable); MPCOPY(get_syncflt); MPCOPY(get_asyncflt); MPCOPY(cache_flush); MPCOPY(sp_vcache_flush_page); MPCOPY(sp_vcache_flush_segment); MPCOPY(sp_vcache_flush_region); MPCOPY(sp_vcache_flush_context); MPCOPY(sp_vcache_flush_range); MPCOPY(ft_vcache_flush_page); MPCOPY(ft_vcache_flush_segment); MPCOPY(ft_vcache_flush_region); MPCOPY(ft_vcache_flush_context); MPCOPY(ft_vcache_flush_range); MPCOPY(pcache_flush_page); MPCOPY(pure_vcache_flush); MPCOPY(cache_flush_all); MPCOPY(memerr); MPCOPY(zero_page); MPCOPY(copy_page); #undef MPCOPY /* * Use the single-processor cache flush functions until * all CPUs are initialized. */ sc->vcache_flush_page = sc->sp_vcache_flush_page; sc->vcache_flush_segment = sc->sp_vcache_flush_segment; sc->vcache_flush_region = sc->sp_vcache_flush_region; sc->vcache_flush_context = sc->sp_vcache_flush_context; return; } panic("Out of CPUs"); } /* * The following tables convert <IU impl, IU version, FPU version> triples * into names for the CPU and FPU chip. In most cases we do not need to * inspect the FPU version to name the IU chip, but there is one exception * (for Tsunami), and this makes the tables the same. * * The table contents (and much of the structure here) are from Guy Harris. * */ struct info { int valid; int iu_impl; int iu_vers; int fpu_vers; const char *name; }; /* XXX trim this table on a per-ARCH basis */ /* NB: table order matters here; specific numbers must appear before ANY. */ static struct info fpu_types[] = { /* * Vendor 0, IU Fujitsu0. */ { 1, 0x0, ANY, 0, "MB86910 or WTL1164/5" }, { 1, 0x0, ANY, 1, "MB86911 or WTL1164/5" }, { 1, 0x0, ANY, 2, "L64802 or ACT8847" }, { 1, 0x0, ANY, 3, "WTL3170/2" }, { 1, 0x0, 4, 4, "on-chip" }, /* Swift */ { 1, 0x0, 5, 5, "on-chip" }, /* TurboSparc */ { 1, 0x0, ANY, 4, "L64804" }, /* * Vendor 1, IU ROSS0/1 or Pinnacle. */ { 1, 0x1, 0xf, 0, "on-chip" }, /* Pinnacle */ { 1, 0x1, 0xe, 0, "on-chip" }, /* Hypersparc RT 625/626 */ { 1, 0x1, ANY, 0, "L64812 or ACT8847" }, { 1, 0x1, ANY, 1, "L64814" }, { 1, 0x1, ANY, 2, "TMS390C602A" }, { 1, 0x1, ANY, 3, "RT602 or WTL3171" }, /* * Vendor 2, IU BIT0. */ { 1, 0x2, ANY, 0, "B5010 or B5110/20 or B5210" }, /* * Vendor 4, Texas Instruments. */ { 1, 0x4, ANY, 0, "on-chip" }, /* Viking */ { 1, 0x4, ANY, 4, "on-chip" }, /* Tsunami */ /* * Vendor 5, IU Matsushita0. */ { 1, 0x5, ANY, 0, "on-chip" }, /* * Vendor 9, Weitek. */ { 1, 0x9, ANY, 3, "on-chip" }, { 0 } }; static const char * fsrtoname(int impl, int vers, int fver) { struct info *p; for (p = fpu_types; p->valid; p++) { if (p->iu_impl == impl && (p->iu_vers == vers || p->iu_vers == ANY) && (p->fpu_vers == fver)) return (p->name); } return (NULL); } #ifdef DDB #include <ddb/db_output.h> #include <machine/db_machdep.h> #include "ioconf.h" void cpu_debug_dump(void); /* * Dump CPU information from ddb. */ void cpu_debug_dump(void) { struct cpu_info *ci; CPU_INFO_ITERATOR cii; db_printf("%-4s %-10s %-8s %-10s %-10s %-10s\n", "CPU#", "CPUINFO", "FLAGS", "CURLWP", "CURPROC", "FPLWP"); for (CPU_INFO_FOREACH(cii, ci)) { db_printf("%-4d %-10p %-8x %-10p %-10p %-10p\n", ci->ci_cpuid, ci, ci->flags, ci->ci_curlwp, ci->ci_curlwp == NULL ? NULL : ci->ci_curlwp->l_proc, ci->fplwp); } } #endif