#include <sys/param.h>
#include <sys/clockintr.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <uvm/uvm_extern.h>
#include <machine/atomic.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/rpb.h>
#include <machine/prom.h>
struct cpu_info cpu_info_primary = { .ci_flags = CPUF_PRIMARY };
struct cpu_info *cpu_info_list = &cpu_info_primary;
#if defined(MULTIPROCESSOR)
#include <sys/malloc.h>
struct cpu_info *cpu_info[ALPHA_MAXPROCS];
volatile u_long cpus_booted;
volatile u_long cpus_running;
volatile u_long cpus_paused;
void cpu_boot_secondary(struct cpu_info *);
#endif
u_long cpu_implver, cpu_amask;
int cpumatch(struct device *, void *, void *);
void cpuattach(struct device *, struct device *, void *);
const struct cfattach cpu_ca = {
sizeof(struct device), cpumatch, cpuattach
};
struct cfdriver cpu_cd = {
NULL, "cpu", DV_DULL
};
void cpu_announce_extensions(struct cpu_info *);
static const char *ev4minor[] = {
"pass 2 or 2.1", "pass 3", 0
}, *lcaminor[] = {
"",
"21066 pass 1 or 1.1", "21066 pass 2",
"21068 pass 1 or 1.1", "21068 pass 2",
"21066A pass 1", "21068A pass 1", 0
}, *ev5minor[] = {
"", "pass 2, rev BA or 2.2, rev CA", "pass 2.3, rev DA or EA",
"pass 3", "pass 3.2", "pass 4", 0
}, *ev45minor[] = {
"", "pass 1", "pass 1.1", "pass 2", 0
}, *ev56minor[] = {
"", "pass 1", "pass 2", 0
}, *ev6minor[] = {
"", "pass 1", "pass 2 or 2.1", "pass 2.2", "pass 2.3", "pass 3",
"pass 2.4", "pass 2.5", 0
}, *pca56minor[] = {
"", "pass 1", 0
}, *pca57minor[] = {
"", "pass 1", 0
}, *ev67minor[] = {
"", "pass 1", "pass 2.1", "pass 2.2", "pass 2.1.1",
"pass 2.2.1", "pass 2.3 or 2.4", "pass 2.1.2", "pass 2.2.2",
"pass 2.2.3 or 2.2.5", "pass 2.2.4", "pass 2.5", "pass 2.4.1",
"pass 2.5.1", "pass 2.6", 0
}, *ev68cbminor[] = {
"", "", "", "", "",
"pass 2.4", "pass 4.0", 0
};
struct cputable_struct {
int cpu_major_code;
const char *cpu_major_name;
const char **cpu_minor_names;
} cpunametable[] = {
{ PCS_PROC_EV3, "EV3", 0 },
{ PCS_PROC_EV4, "21064", ev4minor },
{ PCS_PROC_SIMULATION, "Sim", 0 },
{ PCS_PROC_LCA4, "LCA", lcaminor },
{ PCS_PROC_EV5, "21164", ev5minor },
{ PCS_PROC_EV45, "21064A", ev45minor },
{ PCS_PROC_EV56, "21164A", ev56minor },
{ PCS_PROC_EV6, "21264", ev6minor },
{ PCS_PROC_PCA56, "PCA56", pca56minor },
{ PCS_PROC_PCA57, "PCA57", pca57minor },
{ PCS_PROC_EV67, "21264A", ev67minor },
{ PCS_PROC_EV68CB, "21264C", ev68cbminor },
{ PCS_PROC_EV68AL, "21264B", NULL },
{ PCS_PROC_EV68CX, "21264D", NULL },
};
int
cpumatch(struct device *parent, void *cfdata, void *aux)
{
struct mainbus_attach_args *ma = aux;
if (strcmp(ma->ma_name, cpu_cd.cd_name) != 0)
return (0);
#ifndef MULTIPROCESSOR
if (ma->ma_slot != hwrpb->rpb_primary_cpu_id)
return (0);
#endif
return (1);
}
void
cpuattach(struct device *parent, struct device *dev, void *aux)
{
struct mainbus_attach_args *ma = aux;
struct cpu_info *ci;
int i;
const char **s;
struct pcs *p;
#ifdef DEBUG
int needcomma;
#endif
u_int32_t major, minor;
#if defined(MULTIPROCESSOR)
vaddr_t pcbva;
struct pcb *pcb;
const struct kmem_pa_mode kp_contig = {
.kp_constraint = &no_constraint,
.kp_maxseg = 1,
.kp_zero = 1
};
#endif
p = LOCATE_PCS(hwrpb, ma->ma_slot);
major = PCS_CPU_MAJORTYPE(p);
minor = PCS_CPU_MINORTYPE(p);
printf(": ID %d%s, ", ma->ma_slot,
ma->ma_slot == hwrpb->rpb_primary_cpu_id ? " (primary)" : "");
for (i = 0; i < sizeof cpunametable / sizeof cpunametable[0]; ++i) {
if (cpunametable[i].cpu_major_code == major) {
printf("%s-%d", cpunametable[i].cpu_major_name, minor);
s = cpunametable[i].cpu_minor_names;
for (i = 0; s && s[i]; ++i) {
if (i == minor && strlen(s[i]) != 0) {
printf(" (%s)", s[i]);
goto recognized;
}
}
printf(" (unknown minor type %d)", minor);
goto recognized;
}
}
printf("UNKNOWN CPU TYPE (%d:%d)", major, minor);
recognized:
printf("\n");
#ifdef DEBUG
if (p->pcs_proc_var != 0) {
printf("%s: ", dev->dv_xname);
needcomma = 0;
if (p->pcs_proc_var & PCS_VAR_VAXFP) {
printf("VAX FP support");
needcomma = 1;
}
if (p->pcs_proc_var & PCS_VAR_IEEEFP) {
printf("%sIEEE FP support", needcomma ? ", " : "");
needcomma = 1;
}
if (p->pcs_proc_var & PCS_VAR_PE) {
printf("%sPrimary Eligible", needcomma ? ", " : "");
needcomma = 1;
}
if (p->pcs_proc_var & PCS_VAR_RESERVED)
printf("%sreserved bits: 0x%lx", needcomma ? ", " : "",
p->pcs_proc_var & PCS_VAR_RESERVED);
printf("\n");
}
#endif
#if defined(MULTIPROCESSOR)
if (ma->ma_slot > ALPHA_WHAMI_MAXID) {
printf("%s: processor ID too large, ignoring\n", dev->dv_xname);
return;
}
#endif
#if defined(MULTIPROCESSOR)
if (ma->ma_slot == hwrpb->rpb_primary_cpu_id)
ci = &cpu_info_primary;
else
ci = malloc(sizeof(*ci), M_DEVBUF, M_WAITOK | M_ZERO);
cpu_info[ma->ma_slot] = ci;
#else
ci = &cpu_info_primary;
#endif
ci->ci_cpuid = ma->ma_slot;
ci->ci_dev = dev;
#if defined(MULTIPROCESSOR)
if ((p->pcs_flags & PCS_PA) == 0) {
if (ma->ma_slot == hwrpb->rpb_primary_cpu_id)
panic("cpu_attach: primary not available?!");
printf("%s: processor not available for use\n", dev->dv_xname);
return;
}
if ((p->pcs_flags & PCS_PV) == 0) {
if (ma->ma_slot == hwrpb->rpb_primary_cpu_id)
panic("cpu_attach: primary has invalid PALcode?!");
printf("%s: PALcode not valid\n", ci->ci_dev->dv_xname);
return;
}
pcbva = (vaddr_t)km_alloc(USPACE, &kv_any, &kp_contig, &kd_waitok);
if (pcbva == 0) {
if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) {
panic("cpu_attach: unable to allocate idle stack for"
" primary");
}
printf("%s: unable to allocate idle stack\n", dev->dv_xname);
return;
}
ci->ci_idle_pcb_paddr = ALPHA_K0SEG_TO_PHYS(pcbva);
pcb = ci->ci_idle_pcb = (struct pcb *)pcbva;
pcb->pcb_hw.apcb_ksp =
(u_int64_t)pcb + USPACE - sizeof(struct trapframe);
pcb->pcb_hw.apcb_asn = proc0.p_addr->u_pcb.pcb_hw.apcb_asn;
pcb->pcb_hw.apcb_ptbr = proc0.p_addr->u_pcb.pcb_hw.apcb_ptbr;
#if 0
printf("%s: hwpcb ksp = 0x%lx\n", dev->dv_xname,
pcb->pcb_hw.apcb_ksp);
printf("%s: hwpcb ptbr = 0x%lx\n", dev->dv_xname,
pcb->pcb_hw.apcb_ptbr);
#endif
#endif
if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) {
cpu_announce_extensions(ci);
#if defined(MULTIPROCESSOR)
ci->ci_flags |= CPUF_PRIMARY | CPUF_RUNNING;
atomic_setbits_ulong(&cpus_booted, (1UL << ma->ma_slot));
atomic_setbits_ulong(&cpus_running, (1UL << ma->ma_slot));
#endif
} else {
#if defined(MULTIPROCESSOR)
cpu_boot_secondary(ci);
#endif
}
}
void
cpu_announce_extensions(struct cpu_info *ci)
{
u_long implver, amask = 0;
implver = alpha_implver();
if (implver >= ALPHA_IMPLVER_EV5)
amask = (~alpha_amask(ALPHA_AMASK_ALL)) & ALPHA_AMASK_ALL;
if (ci->ci_cpuid == hwrpb->rpb_primary_cpu_id) {
cpu_implver = implver;
cpu_amask = amask;
} else {
if (implver < cpu_implver)
printf("%s: WARNING: IMPLVER %lu < %lu\n",
ci->ci_dev->dv_xname, implver, cpu_implver);
cpu_amask &= amask;
}
if (amask) {
printf("%s: architecture extensions: %lb\n",
ci->ci_dev->dv_xname, amask, ALPHA_AMASK_BITS);
}
}
#if defined(MULTIPROCESSOR)
void
cpu_boot_secondary_processors(void)
{
struct cpu_info *ci, *ci_last;
u_long i;
for (i = 0; i < ALPHA_MAXPROCS; i++) {
ci = cpu_info[i];
if (ci == NULL || ci->ci_idle_pcb == NULL)
continue;
if (CPU_IS_PRIMARY(ci))
continue;
if ((cpus_booted & (1UL << i)) == 0)
continue;
ci_last = cpu_info_list;
while (ci_last->ci_next != NULL)
ci_last = ci_last->ci_next;
ci_last->ci_next = ci;
atomic_setbits_ulong(&ci->ci_flags, CPUF_RUNNING);
atomic_setbits_ulong(&cpus_running, (1UL << i));
ncpus++;
}
hwrpb->rpb_restart = (u_int64_t) cpu_halt;
hwrpb->rpb_checksum = hwrpb_checksum();
}
void
cpu_boot_secondary(struct cpu_info *ci)
{
long timeout;
struct pcs *pcsp, *primary_pcsp;
struct pcb *pcb;
u_long cpumask;
pcb = ci->ci_idle_pcb;
primary_pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
pcsp = LOCATE_PCS(hwrpb, ci->ci_cpuid);
cpumask = (1UL << ci->ci_cpuid);
memcpy(pcsp->pcs_hwpcb, &pcb->pcb_hw, sizeof(pcb->pcb_hw));
hwrpb->rpb_restart = (u_int64_t) cpu_spinup_trampoline;
hwrpb->rpb_restart_val = (u_int64_t) ci;
hwrpb->rpb_checksum = hwrpb_checksum();
memcpy(&pcsp->pcs_pal_rev, &primary_pcsp->pcs_pal_rev,
sizeof(pcsp->pcs_pal_rev));
pcsp->pcs_flags |= (PCS_CV|PCS_RC);
pcsp->pcs_flags &= ~PCS_BIP;
alpha_mb();
if (cpu_iccb_send(ci->ci_cpuid, "START\r\n")) {
printf("%s: unable to issue `START' command\n",
ci->ci_dev->dv_xname);
return;
}
for (timeout = 10000; timeout != 0; timeout--) {
alpha_mb();
if (pcsp->pcs_flags & PCS_BIP)
break;
delay(1000);
}
if (timeout == 0)
printf("%s: processor failed to boot\n", ci->ci_dev->dv_xname);
for (timeout = 10000; timeout != 0; timeout--) {
alpha_mb();
if (cpus_booted & cpumask)
break;
delay(1000);
}
if (timeout == 0)
printf("%s: processor failed to hatch\n", ci->ci_dev->dv_xname);
}
void
cpu_pause_resume(u_long cpu_id, int pause)
{
u_long cpu_mask = (1UL << cpu_id);
if (pause) {
atomic_setbits_ulong(&cpus_paused, cpu_mask);
alpha_send_ipi(cpu_id, ALPHA_IPI_PAUSE);
} else
atomic_clearbits_ulong(&cpus_paused, cpu_mask);
}
void
cpu_pause_resume_all(int pause)
{
struct cpu_info *ci, *self = curcpu();
CPU_INFO_ITERATOR cii;
CPU_INFO_FOREACH(cii, ci) {
if (ci == self)
continue;
cpu_pause_resume(ci->ci_cpuid, pause);
}
}
void
cpu_halt(void)
{
struct cpu_info *ci = curcpu();
u_long cpu_id = cpu_number();
struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id);
printf("%s: shutting down...\n", ci->ci_dev->dv_xname);
pcsp->pcs_flags &= ~(PCS_RC | PCS_HALT_REQ);
pcsp->pcs_flags |= PCS_HALT_STAY_HALTED;
atomic_clearbits_ulong(&cpus_running, (1UL << cpu_id));
atomic_clearbits_ulong(&cpus_booted, (1U << cpu_id));
alpha_pal_halt();
}
void
cpu_hatch(struct cpu_info *ci)
{
u_long cpu_id = cpu_number();
u_long cpumask = (1UL << cpu_id);
atomic_setbits_ulong(&pmap_kernel()->pm_cpus, cpumask);
trap_init();
cpu_announce_extensions(ci);
atomic_setbits_ulong(&cpus_booted, cpumask);
while ((cpus_running & cpumask) == 0)
;
ALPHA_TBIA();
alpha_pal_imb();
clockqueue_init(&ci->ci_queue);
KERNEL_LOCK();
sched_init_cpu(ci);
ci->ci_curproc = ci->ci_fpcurproc = NULL;
ci->ci_randseed = (arc4random() & 0x7fffffff) + 1;
KERNEL_UNLOCK();
clockintr_cpu_init(NULL);
(void) alpha_pal_swpipl(ALPHA_PSL_IPL_0);
sched_toidle();
}
int
cpu_iccb_send(cpuid_t cpu_id, const char *msg)
{
struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id);
int timeout;
u_long cpumask = (1UL << cpu_id);
for (timeout = 10000; timeout != 0; timeout--) {
alpha_mb();
if ((hwrpb->rpb_rxrdy & cpumask) == 0)
break;
delay(1000);
}
if (timeout == 0)
return (EIO);
strlcpy(pcsp->pcs_iccb.iccb_rxbuf, msg,
sizeof pcsp->pcs_iccb.iccb_rxbuf);
pcsp->pcs_iccb.iccb_rxlen = strlen(msg);
atomic_setbits_ulong((volatile u_long *)&hwrpb->rpb_rxrdy, cpumask);
alpha_mb();
for (timeout = 10000; timeout != 0; timeout--) {
alpha_mb();
if ((hwrpb->rpb_rxrdy & cpumask) == 0)
break;
delay(1000);
}
if (timeout == 0)
return (EIO);
return (0);
}
void
cpu_iccb_receive(void)
{
#if 0
u_int64_t txrdy;
char *cp1, *cp2, buf[80];
struct pcs *pcsp;
u_int cnt;
cpuid_t cpu_id;
txrdy = hwrpb->rpb_txrdy;
for (cpu_id = 0; cpu_id < hwrpb->rpb_pcs_cnt; cpu_id++) {
if (txrdy & (1UL << cpu_id)) {
pcsp = LOCATE_PCS(hwrpb, cpu_id);
printf("Inter-console message from CPU %lu "
"HALT REASON = 0x%lx, FLAGS = 0x%lx\n",
cpu_id, pcsp->pcs_halt_reason, pcsp->pcs_flags);
cnt = pcsp->pcs_iccb.iccb_txlen;
if (cnt >= 80) {
printf("Malformed inter-console message\n");
continue;
}
cp1 = pcsp->pcs_iccb.iccb_txbuf;
cp2 = buf;
while (cnt--) {
if (*cp1 != '\r' && *cp1 != '\n')
*cp2++ = *cp1;
cp1++;
}
*cp2 = '\0';
printf("Message from CPU %lu: %s\n", cpu_id, buf);
}
}
#endif
hwrpb->rpb_txrdy = 0;
alpha_mb();
}
void
cpu_unidle(struct cpu_info *ci)
{
if (ci != curcpu())
alpha_send_ipi(ci->ci_cpuid, ALPHA_IPI_AST);
}
#endif