#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: prom.c,v 1.59 2024/03/03 19:56:29 thorpej Exp $");
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/cpu.h>
#include <uvm/uvm_extern.h>
#include <machine/rpb.h>
#include <machine/alpha.h>
#define ENABLEPROM
#include <machine/prom.h>
#include <dev/cons.h>
struct consdev promcons = {
.cn_getc = promcngetc,
.cn_putc = promcnputc,
.cn_pollc = nullcnpollc,
.cn_dev = makedev(23,0),
.cn_pri = 1
};
struct rpb *hwrpb __read_mostly;
int alpha_console;
extern struct prom_vec prom_dispatch_v;
bool prom_interface_initialized;
int prom_mapped = 1;
static kmutex_t prom_lock;
static struct linux_kernel_params qemu_kernel_params;
#ifdef _PROM_MAY_USE_PROM_CONSOLE
pt_entry_t prom_pte, saved_pte[1];
static pt_entry_t *
prom_lev1map(void)
{
struct alpha_pcb *apcb;
apcb = (struct alpha_pcb *)
ALPHA_PHYS_TO_K0SEG((paddr_t)curlwp->l_md.md_pcbpaddr);
return ((pt_entry_t *)ALPHA_PHYS_TO_K0SEG(apcb->apcb_ptbr << PGSHIFT));
}
#endif
bool
prom_uses_prom_console(void)
{
#ifdef _PROM_MAY_USE_PROM_CONSOLE
return (cputype == ST_DEC_7000 || cputype == ST_DEC_21000);
#else
return false;
#endif
}
static void
prom_init_cputype(const struct rpb * const rpb)
{
cputype = rpb->rpb_type;
if (cputype < 0) {
cputype = -cputype;
}
}
static void
prom_check_qemu(const struct rpb * const rpb)
{
if (!alpha_is_qemu) {
if (rpb->rpb_ssn[0] == 'Q' &&
rpb->rpb_ssn[1] == 'E' &&
rpb->rpb_ssn[2] == 'M' &&
rpb->rpb_ssn[3] == 'U') {
alpha_is_qemu = true;
}
}
}
void
init_prom_interface(u_long ptb_pfn, struct rpb *rpb)
{
if (prom_interface_initialized)
return;
struct crb *c;
prom_init_cputype(rpb);
prom_check_qemu(rpb);
c = (struct crb *)((char *)rpb + rpb->rpb_crb_off);
prom_dispatch_v.routine_arg = c->crb_v_dispatch;
prom_dispatch_v.routine = c->crb_v_dispatch->entry_va;
if (alpha_is_qemu) {
extern char kernel_text[];
memcpy(&qemu_kernel_params,
(void *)((vaddr_t)kernel_text - 0x6000),
sizeof(qemu_kernel_params));
qemu_kernel_params.kernel_cmdline[
sizeof(qemu_kernel_params.kernel_cmdline) - 1] = '\0';
}
#ifdef _PROM_MAY_USE_PROM_CONSOLE
if (prom_uses_prom_console()) {
pt_entry_t * const l1pt =
(pt_entry_t *)ALPHA_PHYS_TO_K0SEG(ptb_pfn << PGSHIFT);
prom_pte = l1pt[0] & ~PG_ASM;
}
#endif
mutex_init(&prom_lock, MUTEX_DEFAULT, IPL_HIGH);
prom_interface_initialized = true;
}
void
init_bootstrap_console(void)
{
char buf[4];
if (! prom_interface_initialized) {
prom_halt(1);
}
prom_getenv(PROM_E_TTY_DEV, buf, sizeof(buf));
alpha_console = buf[0] - '0';
cn_tab = &promcons;
}
bool
prom_qemu_getenv(const char *var, char *buf, size_t buflen)
{
const size_t varlen = strlen(var);
const char *sp;
if (!alpha_is_qemu) {
return false;
}
sp = qemu_kernel_params.kernel_cmdline;
for (;;) {
sp = strstr(sp, var);
if (sp == NULL) {
return false;
}
sp += varlen;
if (*sp++ != '=') {
continue;
}
break;
}
while (--buflen) {
if (*sp == ' ' || *sp == '\t' || *sp == '\0') {
break;
}
*buf++ = *sp++;
}
*buf = '\0';
return true;
}
#ifdef _PROM_MAY_USE_PROM_CONSOLE
static void prom_cache_sync(void);
#endif
void
prom_enter(void)
{
mutex_enter(&prom_lock);
#ifdef _PROM_MAY_USE_PROM_CONSOLE
if (! prom_mapped) {
if (!prom_uses_prom_console())
panic("prom_enter");
{
pt_entry_t *lev1map;
lev1map = prom_lev1map();
saved_pte[0] = lev1map[0];
lev1map[0] = prom_pte;
}
prom_cache_sync();
}
#endif
}
void
prom_leave(void)
{
#ifdef _PROM_MAY_USE_PROM_CONSOLE
if (! prom_mapped) {
if (!prom_uses_prom_console())
panic("prom_leave");
{
pt_entry_t *lev1map;
lev1map = prom_lev1map();
lev1map[0] = saved_pte[0];
}
prom_cache_sync();
}
#endif
mutex_exit(&prom_lock);
}
#ifdef _PROM_MAY_USE_PROM_CONSOLE
static void
prom_cache_sync(void)
{
ALPHA_TBIA();
alpha_pal_imb();
}
#endif
void
promcnputc(dev_t dev, int c)
{
prom_return_t ret;
unsigned char *to = (unsigned char *)0x20000000;
if (alpha_is_qemu)
return;
prom_enter();
*to = c;
do {
ret.bits = prom_putstr(alpha_console, to, 1);
} while ((ret.u.retval & 1) == 0);
prom_leave();
}
int
promcngetc(dev_t dev)
{
prom_return_t ret;
if (alpha_is_qemu)
return 0;
for (;;) {
prom_enter();
ret.bits = prom_getc(alpha_console);
prom_leave();
if (ret.u.status == 0 || ret.u.status == 1)
return (ret.u.retval);
}
}
int
promcnlookc(dev_t dev, char *cp)
{
prom_return_t ret;
if (alpha_is_qemu)
return 0;
prom_enter();
ret.bits = prom_getc(alpha_console);
prom_leave();
if (ret.u.status == 0 || ret.u.status == 1) {
*cp = ret.u.retval;
return 1;
} else
return 0;
}
int
prom_getenv(int id, char *buf, int len)
{
unsigned char *to = (unsigned char *)0x20000000;
prom_return_t ret;
if (alpha_is_qemu)
return 0;
prom_enter();
ret.bits = prom_getenv_disp(id, to, len);
if (ret.u.status & 0x4)
ret.u.retval = 0;
len = uimin(len - 1, ret.u.retval);
memcpy(buf, to, len);
buf[len] = '\0';
prom_leave();
return len;
}
void
prom_halt(int halt)
{
struct pcs *p;
(void) splhigh();
p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
p->pcs_flags &= ~(PCS_RC | PCS_HALT_REQ);
if (halt)
p->pcs_flags |= PCS_HALT_STAY_HALTED;
else
p->pcs_flags |= PCS_HALT_WARM_BOOT;
alpha_pal_halt();
}
uint64_t
hwrpb_checksum(void)
{
uint64_t *p, sum;
int i;
for (i = 0, p = (uint64_t *)hwrpb, sum = 0;
i < (offsetof(struct rpb, rpb_checksum) / sizeof (uint64_t));
i++, p++)
sum += *p;
return (sum);
}
void
hwrpb_primary_init(void)
{
struct pcb *pcb;
struct pcs *p;
p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
pcb = lwp_getpcb(&lwp0);
memcpy(p->pcs_hwpcb, &pcb->pcb_hw, sizeof(pcb->pcb_hw));
hwrpb->rpb_vptb = VPTBASE;
hwrpb->rpb_checksum = hwrpb_checksum();
}
void
hwrpb_restart_setup(void)
{
struct pcs *p;
p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
p->pcs_flags &= ~PCS_BIP;
hwrpb->rpb_rest_term = (uint64_t)&XentRestart;
hwrpb->rpb_rest_term_val = 0x1;
hwrpb->rpb_checksum = hwrpb_checksum();
p->pcs_flags |= (PCS_RC | PCS_CV);
}
uint64_t
console_restart(struct trapframe *framep)
{
struct pcs *p;
p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
p->pcs_flags &= ~PCS_RC;
framep->tf_regs[FRAME_PS] = p->pcs_halt_ps;
framep->tf_regs[FRAME_PC] = p->pcs_halt_pc;
framep->tf_regs[FRAME_T11] = p->pcs_halt_r25;
framep->tf_regs[FRAME_RA] = p->pcs_halt_r26;
framep->tf_regs[FRAME_T12] = p->pcs_halt_r27;
panic("user requested console halt");
return (1);
}