#include <sys/types.h>
#include <sys/cpr.h>
#include <sys/promimpl.h>
#include <sys/privregs.h>
#include <sys/stack.h>
#include <sys/bitmap.h>
#include "cprboot.h"
#define TIMEOUT_MSECS 1000
int cpr_test_mode;
csu_md_t mdinfo;
caddr_t tmp_stack;
uint_t cb_mid;
uint_t cb_clock_freq;
uint_t cpu_delay;
typedef void (*tlb_func_t)(int, caddr_t, tte_t *);
static uint_t mdlen;
static ulong_t slave_set[BT_BITOUL(NCPU)];
static int has_scbc;
int
cb_check_machdep(void)
{
uint16_t wst32, wst64;
char *fmt, *str;
cmd_t cmach;
str = "cb_check_machdep";
CB_VPRINTF((ent_fmt, str, entry));
SF_DCOPY(cmach);
if (cmach.md_magic != CPR_MACHDEP_MAGIC) {
prom_printf("%s: bad machdep magic 0x%x, expect 0x%x\n",
str, cmach.md_magic, CPR_MACHDEP_MAGIC);
return (ERR);
}
mdlen = cmach.md_size - sizeof (csu_md_t);
SF_DCOPY(mdinfo);
fmt = "found bad statefile data: %s (0x%x), expect 0x%x or 0x%x\n";
if (mdinfo.ksb != 0x0 && mdinfo.ksb != V9BIAS64) {
prom_printf(fmt, "stack bias", mdinfo.ksb, 0, V9BIAS64);
return (ERR);
}
wst32 = WSTATE(WSTATE_U32, WSTATE_K32);
wst64 = WSTATE(WSTATE_U32, WSTATE_K64);
if (mdinfo.kwstate != wst32 && mdinfo.kwstate != wst64) {
prom_printf(fmt, "wstate", mdinfo.kwstate, wst32, wst64);
return (ERR);
}
return (0);
}
int
cb_interpret(void)
{
int bytes, wlen, s;
char minibuf[60];
char *words;
CB_VENTRY(cb_interpret);
words = SF_DATA();
bytes = mdlen;
while (bytes) {
wlen = prom_strlen(words) + 1;
if (verbose) {
s = sizeof (minibuf) - 4;
(void) prom_strncpy(minibuf, words, s);
if (wlen > s)
(void) prom_strcpy(&minibuf[s], "...");
prom_printf(" interpret \"%s\"\n", minibuf);
}
prom_interpret(words, 0, 0, 0, 0, 0);
words += wlen;
bytes -= wlen;
}
SF_ADV(mdlen);
return (0);
}
static void
restore_tlb(struct sun4u_tlb *utp, int cpu_id)
{
struct sun4u_tlb *tail;
tlb_func_t tfunc;
caddr_t virt;
char tname;
if (utp == mdinfo.dtte) {
tfunc = set_dtlb_entry;
tname = 'd';
} else if (utp == mdinfo.itte) {
tfunc = set_itlb_entry;
tname = 'i';
}
for (tail = utp + CPR_MAX_TLB; utp < tail; utp++) {
if (utp->va_tag == 0)
continue;
virt = (caddr_t)utp->va_tag;
(*tfunc)(utp->index, virt, &utp->tte);
if (verbose || CPR_DBG(4)) {
prom_printf(" cpu_id %d: write %ctlb "
"(index %x, virt 0x%lx, size 0x%x)\n",
cpu_id, tname, utp->index, utp->va_tag,
TTEBYTES(utp->tte.tte_size));
}
}
}
int
cb_ksetup(void)
{
CB_VENTRY(cb_ksetup);
restore_tlb(mdinfo.dtte, cb_mid);
restore_tlb(mdinfo.itte, cb_mid);
tmp_stack = (caddr_t)(mdinfo.tmp_stack + mdinfo.tmp_stacksize);
return (0);
}
static void
cb_park_err(int cpu_id)
{
prom_printf("\ncpu_id %d did not stop!...\n", cpu_id);
cb_exit_to_mon();
}
static int
cb_prom_stop_self(void)
{
cell_t ci[3];
ci[0] = p1275_ptr2cell("SUNW,stop-self");
ci[1] = (cell_t)0;
ci[2] = (cell_t)0;
(void) p1275_cif_handler(&ci);
return (0);
}
void
slave_init(int cpu_id)
{
restore_tlb(mdinfo.dtte, cpu_id);
restore_tlb(mdinfo.itte, cpu_id);
BT_SET(slave_set, cpu_id);
membar_stld();
if (has_scbc) {
while (1);
} else {
(void) cb_prom_stop_self();
cb_park_err(cpu_id);
}
}
int
cb_mpsetup(void)
{
struct sun4u_cpu_info *scip, *tail;
int timeout, ncpu;
char *str, *intf;
intf = "SUNW,stop-cpu-by-cpuid";
has_scbc = (prom_test(intf) == 0);
CB_VPRINTF(("\n\"%s\" test %d\n", intf, has_scbc));
str = "cb_mp_setup";
CB_VPRINTF((ent_fmt, str, entry));
ncpu = 0;
bzero(slave_set, sizeof (slave_set));
for (scip = mdinfo.sci, tail = scip + NCPU; scip < tail; scip++) {
if (scip->node == 0 || scip->cpu_id == cb_mid)
continue;
(void) prom_startcpu(scip->node,
(caddr_t)cpu_launch, scip->cpu_id);
for (timeout = TIMEOUT_MSECS; timeout; timeout--) {
if (BT_TEST(slave_set, scip->cpu_id))
break;
cb_usec_wait(MILLISEC);
}
if (timeout == 0) {
prom_printf("\n%s: cpu did not start, "
"cpu_id %d, node 0x%x\n",
prog, scip->cpu_id, scip->node);
return (ERR);
}
if (has_scbc && prom_stopcpu_bycpuid(scip->cpu_id))
cb_park_err(scip->cpu_id);
ncpu++;
}
if (verbose && ncpu)
prom_printf("\n%s: slave cpu count: %d\n", str, ncpu);
return (0);
}