#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.140 2021/04/05 22:36:27 nakayama Exp $");
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/reboot.h>
#include <sys/cpu.h>
#include <sys/sysctl.h>
#include <sys/kmem.h>
#include <uvm/uvm.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 <machine/hypervisor.h>
#include <machine/mdesc.h>
#include <sparc64/sparc64/cache.h>
#define SUN4V_MONDO_QUEUE_SIZE 32
#define SUN4V_QUEUE_ENTRY_SIZE 64
int ecache_min_line_size;
#if defined(MULTIPROCESSOR)
int sparc_ncpus = 0;
#endif
struct cpu_info *cpus = NULL;
volatile sparc64_cpuset_t cpus_active;
struct cpu_bootargs *cpu_args;
struct pool_cache *fpstate_cache;
static struct cpu_info *alloc_cpuinfo(u_int);
static void cpu_idle_sun4v(void);
char machine[] = MACHINE;
char machine_arch[] = MACHINE_ARCH;
int dcache_line_size;
int dcache_size;
int icache_line_size;
int icache_size;
#ifdef MULTIPROCESSOR
static const char *ipi_evcnt_names[IPI_EVCNT_NUM] = IPI_EVCNT_NAMES;
#endif
static void cpu_reset_fpustate(void);
volatile int sync_tick = 0;
void cpu_attach(device_t, device_t, void *);
int cpu_match(device_t, cfdata_t, void *);
CFATTACH_DECL_NEW(cpu, 0, cpu_match, cpu_attach, NULL, NULL);
static int
cpuid_from_node(u_int cpu_node)
{
int id;
id = prom_getpropint(cpu_node, "upa-portid", -1);
if (id == -1)
id = prom_getpropint(cpu_node, "portid", -1);
if (id == -1)
id = prom_getpropint(cpu_node, "cpuid", -1);
if (CPU_ISSUN4V) {
int reg[4];
int* regp=reg;
int len = 4;
int rc = prom_getprop(cpu_node, "reg", sizeof(int),
&len, ®p);
if ( rc != 0)
panic("No reg property found\n");
id = reg[0] & 0x0fffffff;
}
if (id == -1)
panic("failed to determine cpuid");
return id;
}
static int
cpu_cache_info_sun4v(const char *type, int level, const char *prop)
{
int idx = 0;
uint64_t val = 0;
idx = mdesc_find_node_by_idx(idx, "cache");
while (idx != -1 && val == 0) {
const char *name = mdesc_name_by_idx(idx);
if (strcmp("cache", name) == 0) {
const char *p;
size_t len = 0;
p = mdesc_get_prop_data(idx, "type", &len);
if (p == NULL)
panic("No type found\n");
if (len == 0)
panic("Len is zero");
if (type == NULL || strcmp(p, type) == 0) {
uint64_t l;
l = mdesc_get_prop_val(idx, "level");
if (l == level)
val = mdesc_get_prop_val(idx, prop);
}
}
if (val == 0)
idx = mdesc_next_node(idx);
}
return val;
}
static int
cpu_icache_size(int node)
{
if (CPU_ISSUN4V)
return cpu_cache_info_sun4v("instn", 1, "size");
else
return prom_getpropint(node, "icache-size", 0);
}
static int
cpu_icache_line_size(int node)
{
if (CPU_ISSUN4V)
return cpu_cache_info_sun4v("instn", 1, "line-size");
else
return prom_getpropint(node, "icache-line-size", 0);
}
static int
cpu_icache_nlines(int node)
{
if (CPU_ISSUN4V)
return 0;
else
return prom_getpropint(node, "icache-nlines", 64);
}
static int
cpu_icache_associativity(int node)
{
if (CPU_ISSUN4V) {
int val;
val = cpu_cache_info_sun4v("instn", 1, "associativity");
if (val == 0)
val = 1;
return val;
} else
return prom_getpropint(node, "icache-associativity", 1);
}
static int
cpu_dcache_size(int node)
{
if (CPU_ISSUN4V)
return cpu_cache_info_sun4v("data", 1, "size");
else
return prom_getpropint(node, "dcache-size", 0);
}
static int
cpu_dcache_line_size(int node)
{
if (CPU_ISSUN4V)
return cpu_cache_info_sun4v("data", 1, "line-size");
else
return prom_getpropint(node, "dcache-line-size", 0);
}
static int
cpu_dcache_nlines(int node)
{
if (CPU_ISSUN4V)
return 0;
else
return prom_getpropint(node, "dcache-nlines", 128);
}
static int
cpu_dcache_associativity(int node)
{
if (CPU_ISSUN4V) {
int val;
val = cpu_cache_info_sun4v("data", 1, "associativity");
if (val == 0)
val = 1;
return val;
} else
return prom_getpropint(node, "dcache-associativity", 1);
}
int
cpu_ecache_size(int node)
{
if (CPU_ISSUN4V)
return cpu_cache_info_sun4v(NULL, 2, "size");
else
return prom_getpropint(node, "ecache-size", 0);
}
static int
cpu_ecache_line_size(int node)
{
if (CPU_ISSUN4V)
return cpu_cache_info_sun4v(NULL, 2, "line-size");
else
return prom_getpropint(node, "ecache-line-size", 0);
}
static int
cpu_ecache_nlines(int node)
{
if (CPU_ISSUN4V)
return 0;
else
return prom_getpropint(node, "ecache-nlines", 32768);
}
int
cpu_ecache_associativity(int node)
{
if (CPU_ISSUN4V) {
int val;
val = cpu_cache_info_sun4v(NULL, 2, "associativity");
if (val == 0)
val = 1;
return val;
} else
return prom_getpropint(node, "ecache-associativity", 1);
}
struct cpu_info *
alloc_cpuinfo(u_int cpu_node)
{
paddr_t pa0, pa;
vaddr_t va, va0;
vsize_t sz = 8 * PAGE_SIZE;
int cpuid;
struct cpu_info *cpi, *ci;
extern paddr_t cpu0paddr;
cpuid = cpuid_from_node(cpu_node);
for (cpi = cpus; cpi != NULL; cpi = cpi->ci_next)
if (cpi->ci_cpuid == cpuid)
return cpi;
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, 0);
pmap_update(pmap_kernel());
cpi = (struct cpu_info *)(va0 + CPUINFO_VA - INTSTACK);
memset((void *)va0, 0, sz);
cpi->ci_next = NULL;
cpi->ci_curlwp = NULL;
cpi->ci_cpuid = cpuid;
cpi->ci_fplwp = NULL;
cpi->ci_eintstack = NULL;
cpi->ci_spinup = NULL;
cpi->ci_paddr = pa0;
cpi->ci_self = cpi;
if (CPU_ISSUN4V)
cpi->ci_mmufsa = pa0;
cpi->ci_node = cpu_node;
cpi->ci_idepth = -1;
memset(cpi->ci_intrpending, -1, sizeof(cpi->ci_intrpending));
for (ci = cpus; ci->ci_next != NULL; ci = ci->ci_next)
;
#ifdef MULTIPROCESSOR
ci->ci_next = cpi;
#endif
return (cpi);
}
int
cpu_match(device_t parent, cfdata_t cf, void *aux)
{
struct mainbus_attach_args *ma = aux;
if (strcmp(cf->cf_name, ma->ma_name) != 0)
return 0;
if (cpuid_from_node(ma->ma_node) != cpu_myid()) {
#ifdef MULTIPROCESSOR
if (boothowto & RB_MD1)
#endif
return 0;
}
return 1;
}
static void
cpu_reset_fpustate(void)
{
struct fpstate64 *fpstate;
struct fpstate64 fps[2];
fpstate = ALIGNFPSTATE(&fps[1]);
fpstate->fs_fsr = 7 << FSR_VER_SHIFT;
savefpstate(fpstate);
}
static void
cpu_setup_sysctl(struct cpu_info *ci, device_t dev)
{
const struct sysctlnode *cpunode = NULL;
sysctl_createv(NULL, 0, NULL, &cpunode,
CTLFLAG_PERMANENT,
CTLTYPE_NODE, device_xname(dev), NULL,
NULL, 0, NULL, 0,
CTL_HW,
CTL_CREATE, CTL_EOL);
if (cpunode == NULL)
return;
#define SETUPS(name, member) \
sysctl_createv(NULL, 0, &cpunode, NULL, \
CTLFLAG_PERMANENT, \
CTLTYPE_STRING, name, NULL, \
NULL, 0, member, 0, \
CTL_CREATE, CTL_EOL);
SETUPS("name", __UNCONST(ci->ci_name))
#undef SETUPS
#define SETUPI(name, member) \
sysctl_createv(NULL, 0, &cpunode, NULL, \
CTLFLAG_PERMANENT, \
CTLTYPE_INT, name, NULL, \
NULL, 0, member, 0, \
CTL_CREATE, CTL_EOL);
SETUPI("id", &ci->ci_cpuid);
#undef SETUPI
#define SETUPQ(name, member) \
sysctl_createv(NULL, 0, &cpunode, NULL, \
CTLFLAG_PERMANENT, \
CTLTYPE_QUAD, name, NULL, \
NULL, 0, member, 0, \
CTL_CREATE, CTL_EOL);
SETUPQ("clock_frequency", &ci->ci_cpu_clockrate[0])
SETUPQ("ver", &ci->ci_ver)
#undef SETUPI
sysctl_createv(NULL, 0, &cpunode, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRUCT, "cacheinfo", NULL,
NULL, 0, &ci->ci_cacheinfo, sizeof(ci->ci_cacheinfo),
CTL_CREATE, CTL_EOL);
}
void
cpu_attach(device_t parent, device_t dev, void *aux)
{
int node;
uint64_t clk, sclk = 0;
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, dcachesize, icachesize;
node = ma->ma_node;
ci = alloc_cpuinfo((u_int)node);
if (CPU_IS_PRIMARY(ci)) {
fpstate_cache = pool_cache_init(sizeof(struct fpstate64),
SPARC64_BLOCK_SIZE, 0, 0, "fpstate",
NULL, IPL_NONE, NULL, NULL, NULL);
cpu_reset_fpustate();
}
#ifdef MULTIPROCESSOR
else {
mi_cpu_attach(ci);
ci->ci_cpcb = lwp_getpcb(ci->ci_data.cpu_idlelwp);
}
for (i = 0; i < IPI_EVCNT_NUM; ++i)
evcnt_attach_dynamic(&ci->ci_ipi_evcnt[i], EVCNT_TYPE_INTR,
NULL, device_xname(dev), ipi_evcnt_names[i]);
#endif
evcnt_attach_dynamic(&ci->ci_tick_evcnt, EVCNT_TYPE_INTR, NULL,
device_xname(dev), "timer");
mutex_init(&ci->ci_ctx_lock, MUTEX_SPIN, IPL_VM);
clk = prom_getpropuint64(node, "clock-frequency64", 0);
if (clk == 0)
clk = prom_getpropint(node, "clock-frequency", 0);
if (clk == 0) {
clk = prom_getpropint(findroot(), "clock-frequency", 0);
}
if (clk) {
ci->ci_cpu_clockrate[0] = clk;
ci->ci_cpu_clockrate[1] = clk / 1000000;
}
sclk = prom_getpropint(findroot(), "stick-frequency", 0);
ci->ci_system_clockrate[0] = sclk;
ci->ci_system_clockrate[1] = sclk / 1000000;
ci->ci_name = kmem_strdupsize(prom_getpropstring(node, "name"), NULL,
KM_SLEEP);
snprintf(buf, sizeof buf, "%s @ %s MHz", ci->ci_name, clockfreq(clk));
cpu_setmodel("%s (%s)", machine_model, buf);
aprint_normal(": %s, CPU id %d\n", buf, ci->ci_cpuid);
aprint_naive("\n");
if (CPU_ISSUN4U || CPU_ISSUN4US) {
ci->ci_ver = getver();
aprint_normal_dev(dev, "manuf %x, impl %x, mask %x\n",
(u_int)GETVER_CPU_MANUF(),
(u_int)GETVER_CPU_IMPL(),
(u_int)GETVER_CPU_MASK());
}
#ifdef NUMA
if (CPU_IS_USIIIi()) {
uint64_t start = ci->ci_cpuid;
start <<= 36;
ci->ci_numa_id = ci->ci_cpuid;
printf("NUMA bucket %d %016lx\n", ci->ci_cpuid, start);
uvm_page_numa_load(start, 0x1000000000, ci->ci_cpuid);
}
#endif
if (ci->ci_system_clockrate[0] != 0) {
aprint_normal_dev(dev, "system tick frequency %s MHz\n",
clockfreq(ci->ci_system_clockrate[0]));
}
aprint_normal_dev(dev, "");
bigcache = 0;
icachesize = cpu_icache_size(node);
if (icachesize > icache_size)
icache_size = icachesize;
linesize = l = cpu_icache_line_size(node);
if (linesize > icache_line_size)
icache_line_size = linesize;
for (i = 0; (1 << i) < l && l; i++)
;
if ((1 << i) != l && l)
panic("bad icache line size %d", l);
totalsize = icachesize;
if (totalsize == 0)
totalsize = l *
cpu_icache_nlines(node) * cpu_icache_associativity(node);
cachesize = totalsize / cpu_icache_associativity(node);
bigcache = cachesize;
sep = "";
if (totalsize > 0) {
aprint_normal("%s%ldK instruction (%ld b/l)", sep,
(long)totalsize/1024,
(long)linesize);
sep = ", ";
}
ci->ci_cacheinfo.c_itotalsize = totalsize;
ci->ci_cacheinfo.c_ilinesize = linesize;
dcachesize = cpu_dcache_size(node);
if (dcachesize > dcache_size)
dcache_size = dcachesize;
linesize = l = cpu_dcache_line_size(node);
if (linesize > dcache_line_size)
dcache_line_size = linesize;
for (i = 0; (1 << i) < l && l; i++)
;
if ((1 << i) != l && l)
panic("bad dcache line size %d", l);
totalsize = dcachesize;
if (totalsize == 0)
totalsize = l *
cpu_dcache_nlines(node) * cpu_dcache_associativity(node);
cachesize = totalsize / cpu_dcache_associativity(node);
if (cachesize > bigcache)
bigcache = cachesize;
if (totalsize > 0) {
aprint_normal("%s%ldK data (%ld b/l)", sep,
(long)totalsize/1024,
(long)linesize);
sep = ", ";
}
ci->ci_cacheinfo.c_dtotalsize = totalsize;
ci->ci_cacheinfo.c_dlinesize = linesize;
linesize = l = cpu_ecache_line_size(node);
for (i = 0; (1 << i) < l && l; i++)
;
if ((1 << i) != l && l)
panic("bad ecache line size %d", l);
totalsize = cpu_ecache_size(node);
if (totalsize == 0)
totalsize = l *
cpu_ecache_nlines(node) * cpu_ecache_associativity(node);
cachesize = totalsize / cpu_ecache_associativity(node);
if (cachesize > bigcache)
bigcache = cachesize;
if (totalsize > 0) {
aprint_normal("%s%ldK external (%ld b/l)", sep,
(long)totalsize/1024,
(long)linesize);
}
aprint_normal("\n");
ci->ci_cacheinfo.c_etotalsize = totalsize;
ci->ci_cacheinfo.c_elinesize = linesize;
if (ecache_min_line_size == 0 ||
linesize < ecache_min_line_size)
ecache_min_line_size = linesize;
cpu_setup_sysctl(ci, dev);
uvm_page_recolor(atop(bigcache));
if (CPU_ISSUN4V) {
paddr_t pa = ci->ci_paddr;
int err;
pa += CPUINFO_VA - INTSTACK;
pa += PAGE_SIZE;
ci->ci_cpumq = pa;
err = hv_cpu_qconf(CPU_MONDO_QUEUE, ci->ci_cpumq, SUN4V_MONDO_QUEUE_SIZE);
if (err != H_EOK)
panic("Unable to set cpu mondo queue: %d", err);
pa += SUN4V_MONDO_QUEUE_SIZE * SUN4V_QUEUE_ENTRY_SIZE;
ci->ci_devmq = pa;
err = hv_cpu_qconf(DEVICE_MONDO_QUEUE, ci->ci_devmq, SUN4V_MONDO_QUEUE_SIZE);
if (err != H_EOK)
panic("Unable to set device mondo queue: %d", err);
pa += SUN4V_MONDO_QUEUE_SIZE * SUN4V_QUEUE_ENTRY_SIZE;
ci->ci_mondo = pa;
pa += 64;
ci->ci_cpuset = pa;
pa += 64;
}
if (CPU_ISSUN4V) {
ci->ci_idlespin = cpu_idle_sun4v;
}
}
static void
cpu_idle_sun4v(void)
{
hv_cpu_yield();
}
int
cpu_myid(void)
{
char buf[32];
if (CPU_ISSUN4V) {
uint64_t myid;
hv_cpu_myid(&myid);
return myid;
}
if (OF_getprop(findroot(), "name", buf, sizeof(buf)) > 0 &&
strcmp(buf, "SUNW,Ultra-Enterprise-10000") == 0)
return lduwa(0x1fff40000d0UL, ASI_PHYS_NON_CACHED);
switch (GETVER_CPU_IMPL()) {
case IMPL_OLYMPUS_C:
case IMPL_JUPITER:
return CPU_JUPITERID;
case IMPL_CHEETAH:
case IMPL_CHEETAH_PLUS:
case IMPL_JAGUAR:
case IMPL_PANTHER:
return CPU_FIREPLANEID;
default:
return CPU_UPAID;
}
}
#if defined(MULTIPROCESSOR)
vaddr_t cpu_spinup_trampoline;
void
cpu_boot_secondary_processors(void)
{
int i, pstate;
struct cpu_info *ci;
sync_tick = 0;
sparc64_ipi_init();
if (boothowto & RB_MD1) {
cpus[0].ci_next = NULL;
sparc_ncpus = ncpu = ncpuonline = 1;
return;
}
for (ci = cpus; ci != NULL; ci = ci->ci_next) {
if (ci->ci_cpuid == cpu_myid())
continue;
cpu_pmap_prepare(ci, false);
cpu_args->cb_node = ci->ci_node;
cpu_args->cb_cpuinfo = ci->ci_paddr;
cpu_args->cb_cputyp = cputyp;
membar_Sync();
pstate = getpstate();
setpstate(PSTATE_KERN);
int rc = prom_startcpu_by_cpuid(ci->ci_cpuid,
(void *)cpu_spinup_trampoline, 0);
if (rc == -1)
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);
}
delay(1000);
sync_tick = 1;
membar_Sync();
if (CPU_ISSUN4U || CPU_ISSUN4US)
settick(0);
if (ci->ci_system_clockrate[0] != 0)
if (CPU_ISSUN4U || CPU_ISSUN4US)
setstick(0);
setpstate(pstate);
if (!CPUSET_HAS(cpus_active, ci->ci_index))
printf("cpu%d: startup failed\n", ci->ci_cpuid);
}
}
void
cpu_hatch(void)
{
char *v = (char*)CPUINFO_VA;
int i;
for (i = 0; i < 4*PAGE_SIZE; i += sizeof(long))
sparc_flush_icache(v + i);
cpu_pmap_init(curcpu());
CPUSET_ADD(cpus_active, cpu_number());
cpu_reset_fpustate();
curlwp = curcpu()->ci_data.cpu_idlelwp;
membar_Sync();
while (sync_tick == 0) {
}
if (CPU_ISSUN4U || CPU_ISSUN4US)
settick(0);
if (curcpu()->ci_system_clockrate[0] != 0) {
if (CPU_ISSUN4U || CPU_ISSUN4US)
setstick(0);
stickintr_establish(PIL_CLOCK, stickintr);
} else {
tickintr_establish(PIL_CLOCK, tickintr);
}
spl0();
}
#endif