NetBSD-5.0.2/sys/arch/sparc64/sparc64/cpu.c
/* $NetBSD: cpu.c,v 1.78 2008/10/18 04:41:14 nakayama 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.78 2008/10/18 04:41:14 nakayama Exp $");
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <uvm/uvm_extern.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/reg.h>
#include <machine/trap.h>
#include <machine/pmap.h>
#include <machine/sparc64.h>
#include <machine/openfirm.h>
#include <sparc64/sparc64/cache.h>
int ecache_min_line_size;
/* Linked list of all CPUs in system. */
int sparc_ncpus = 0;
struct cpu_info *cpus = NULL;
volatile sparc64_cpuset_t cpus_active;/* set of active cpus */
struct cpu_bootargs *cpu_args; /* allocated very early in pmap_bootstrap. */
struct pool_cache *fpstate_cache;
static struct cpu_info *alloc_cpuinfo(u_int);
/* The following are used externally (sysctl_hw). */
char machine[] = MACHINE; /* from <machine/param.h> */
char machine_arch[] = MACHINE_ARCH; /* from <machine/param.h> */
char cpu_model[100]; /* machine model (primary CPU) */
extern char machine_model[];
#ifdef MULTIPROCESSOR
static const char *ipi_evcnt_names[IPI_EVCNT_NUM] = IPI_EVCNT_NAMES;
#endif
static void cpu_reset_fpustate(void);
/* The CPU configuration driver. */
void cpu_attach(struct device *, struct device *, void *);
int cpu_match(struct device *, struct cfdata *, void *);
CFATTACH_DECL(cpu, sizeof(struct device),
cpu_match, cpu_attach, NULL, NULL);
struct cpu_info *
alloc_cpuinfo(u_int cpu_node)
{
paddr_t pa0, pa;
vaddr_t va, va0;
vsize_t sz = 8 * PAGE_SIZE;
int portid;
struct cpu_info *cpi, *ci;
extern paddr_t cpu0paddr;
/*
* Check for UPAID in the cpus list.
*/
if (OF_getprop(cpu_node, "upa-portid", &portid, sizeof(portid)) <= 0)
panic("alloc_cpuinfo: upa-portid");
for (cpi = cpus; cpi != NULL; cpi = cpi->ci_next)
if (cpi->ci_cpuid == portid)
return cpi;
/* Allocate the aligned VA and determine the size. */
va = uvm_km_alloc(kernel_map, sz, 8 * PAGE_SIZE, UVM_KMF_VAONLY);
if (!va)
panic("alloc_cpuinfo: no virtual space");
va0 = va;
pa0 = cpu0paddr;
cpu0paddr += sz;
for (pa = pa0; pa < cpu0paddr; pa += PAGE_SIZE, va += PAGE_SIZE)
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE);
pmap_update(pmap_kernel());
cpi = (struct cpu_info *)(va0 + CPUINFO_VA - INTSTACK);
memset((void *)va0, 0, sz);
/*
* Initialize cpuinfo structure.
*
* Arrange pcb, idle stack and interrupt stack in the same
* way as is done for the boot CPU in pmap.c.
*/
cpi->ci_next = NULL;
cpi->ci_curlwp = NULL;
cpi->ci_cpuid = portid;
cpi->ci_fplwp = NULL;
cpi->ci_spinup = NULL;
cpi->ci_paddr = pa0;
cpi->ci_self = cpi;
cpi->ci_node = cpu_node;
cpi->ci_idepth = -1;
memset(cpi->ci_intrpending, -1, sizeof(cpi->ci_intrpending));
/*
* Finally, add itself to the list of active cpus.
*/
for (ci = cpus; ci->ci_next != NULL; ci = ci->ci_next)
;
#ifdef MULTIPROCESSOR
ci->ci_next = cpi;
#endif
return (cpi);
}
int
cpu_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_reset_fpustate(void)
{
struct fpstate64 *fpstate;
struct fpstate64 fps[2];
/* This needs to be 64-byte aligned */
fpstate = ALIGNFPSTATE(&fps[1]);
/*
* 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.
*/
fpstate->fs_fsr = 7 << FSR_VER_SHIFT; /* 7 is reserved for "none" */
savefpstate(fpstate);
}
/*
* 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).
*/
void
cpu_attach(struct device *parent, struct device *dev, void *aux)
{
int node;
long clk;
struct mainbus_attach_args *ma = aux;
struct cpu_info *ci;
const char *sep;
register int i, l;
int bigcache, cachesize;
char buf[100];
int totalsize = 0;
int linesize;
static bool passed = false;
/* tell them what we have */
node = ma->ma_node;
/*
* Allocate cpu_info structure if needed.
*/
ci = alloc_cpuinfo((u_int)node);
/*
* Only do this on the boot cpu. Other cpu's call
* cpu_reset_fpustate() from cpu_hatch() before they
* call into the idle loop.
* For other cpus, we need to call mi_cpu_attach()
* and complete setting up cpcb.
*/
if (!passed) {
passed = true;
fpstate_cache = pool_cache_init(sizeof(struct fpstate64),
BLOCK_SIZE, 0, 0, "fpstate", NULL,
IPL_NONE, NULL, NULL, NULL);
cpu_reset_fpustate();
}
#ifdef MULTIPROCESSOR
else {
mi_cpu_attach(ci);
ci->ci_cpcb = (struct pcb *)ci->ci_data.cpu_idlelwp->l_addr;
}
for (i = 0; i < IPI_EVCNT_NUM; ++i)
evcnt_attach_dynamic(&ci->ci_ipi_evcnt[i], EVCNT_TYPE_MISC,
NULL, device_xname(dev), ipi_evcnt_names[i]);
#endif
evcnt_attach_dynamic(&ci->ci_tick_evcnt, EVCNT_TYPE_MISC, NULL,
device_xname(dev), "timer");
clk = prom_getpropint(node, "clock-frequency", 0);
if (clk == 0) {
/*
* Try to find it in the OpenPROM root...
*/
clk = prom_getpropint(findroot(), "clock-frequency", 0);
}
if (clk) {
/* Tell OS what frequency we run on */
ci->ci_cpu_clockrate[0] = clk;
ci->ci_cpu_clockrate[1] = clk / 1000000;
}
snprintf(buf, sizeof buf, "%s @ %s MHz",
prom_getpropstring(node, "name"), clockfreq(clk));
snprintf(cpu_model, sizeof cpu_model, "%s (%s)", machine_model, buf);
printf(": %s, UPA id %d\n", buf, ci->ci_cpuid);
printf("%s:", device_xname(dev));
bigcache = 0;
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);
totalsize =
prom_getpropint(node, "icache-size", 0) *
prom_getpropint(node, "icache-associativity", 1);
if (totalsize == 0)
totalsize = l *
prom_getpropint(node, "icache-nlines", 64) *
prom_getpropint(node, "icache-associativity", 1);
cachesize = totalsize /
prom_getpropint(node, "icache-associativity", 1);
bigcache = cachesize;
sep = " ";
if (totalsize > 0) {
printf("%s%ldK instruction (%ld b/l)", sep,
(long)totalsize/1024,
(long)linesize);
sep = ", ";
}
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);
totalsize =
prom_getpropint(node, "dcache-size", 0) *
prom_getpropint(node, "dcache-associativity", 1);
if (totalsize == 0)
totalsize = l *
prom_getpropint(node, "dcache-nlines", 128) *
prom_getpropint(node, "dcache-associativity", 1);
cachesize = totalsize /
prom_getpropint(node, "dcache-associativity", 1);
if (cachesize > bigcache)
bigcache = cachesize;
if (totalsize > 0) {
printf("%s%ldK data (%ld b/l)", sep,
(long)totalsize/1024,
(long)linesize);
sep = ", ";
}
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);
totalsize =
prom_getpropint(node, "ecache-size", 0) *
prom_getpropint(node, "ecache-associativity", 1);
if (totalsize == 0)
totalsize = l *
prom_getpropint(node, "ecache-nlines", 32768) *
prom_getpropint(node, "ecache-associativity", 1);
cachesize = totalsize /
prom_getpropint(node, "ecache-associativity", 1);
if (cachesize > bigcache)
bigcache = cachesize;
if (totalsize > 0) {
printf("%s%ldK external (%ld b/l)", sep,
(long)totalsize/1024,
(long)linesize);
}
printf("\n");
if (ecache_min_line_size == 0 ||
linesize < ecache_min_line_size)
ecache_min_line_size = linesize;
/*
* Now that we know the size of the largest cache on this CPU,
* re-color our pages.
*/
uvm_page_recolor(atop(bigcache)); /* XXX */
}
#if defined(MULTIPROCESSOR)
vaddr_t cpu_spinup_trampoline;
/*
* Start secondary processors in motion.
*/
void
cpu_boot_secondary_processors()
{
int i, pstate;
struct cpu_info *ci;
sparc64_ipi_init();
for (ci = cpus; ci != NULL; ci = ci->ci_next) {
if (ci->ci_cpuid == CPU_UPAID)
continue;
cpu_pmap_prepare(ci, false);
cpu_args->cb_node = ci->ci_node;
cpu_args->cb_cpuinfo = ci->ci_paddr;
membar_sync();
/* Disable interrupts and start another CPU. */
pstate = getpstate();
setpstate(PSTATE_KERN);
prom_startcpu(ci->ci_node, (void *)cpu_spinup_trampoline, 0);
for (i = 0; i < 2000; i++) {
membar_sync();
if (CPUSET_HAS(cpus_active, ci->ci_index))
break;
delay(10000);
}
setpstate(pstate);
if (!CPUSET_HAS(cpus_active, ci->ci_index))
printf("cpu%d: startup failed\n", ci->ci_cpuid);
}
}
void
cpu_hatch()
{
char *v = (char*)CPUINFO_VA;
int i;
for (i = 0; i < 4*PAGE_SIZE; i += sizeof(long))
flush(v + i);
cpu_pmap_init(curcpu());
CPUSET_ADD(cpus_active, cpu_number());
cpu_reset_fpustate();
curlwp = curcpu()->ci_data.cpu_idlelwp;
membar_sync();
tickintr_establish(PIL_CLOCK, tickintr);
spl0();
}
#endif /* MULTIPROCESSOR */