Home | History | Annotate | Line # | Download | only in alpha
      1 /* $NetBSD: prom.c,v 1.59 2024/03/03 19:56:29 thorpej Exp $ */
      2 
      3 /*
      4  * Copyright (c) 1992, 1994, 1995, 1996 Carnegie Mellon University
      5  * All Rights Reserved.
      6  *
      7  * Permission to use, copy, modify and distribute this software and its
      8  * documentation is hereby granted, provided that both the copyright
      9  * notice and this permission notice appear in all copies of the
     10  * software, derivative works or modified versions, and any portions
     11  * thereof, and that both notices appear in supporting documentation.
     12  *
     13  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     14  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     15  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     16  *
     17  * Carnegie Mellon requests users of this software to return to
     18  *
     19  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     20  *  School of Computer Science
     21  *  Carnegie Mellon University
     22  *  Pittsburgh PA 15213-3890
     23  *
     24  * any improvements or extensions that they make and grant Carnegie Mellon
     25  * the rights to redistribute these changes.
     26  */
     27 
     28 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
     29 
     30 __KERNEL_RCSID(0, "$NetBSD: prom.c,v 1.59 2024/03/03 19:56:29 thorpej Exp $");
     31 
     32 #include "opt_multiprocessor.h"
     33 
     34 #include <sys/param.h>
     35 #include <sys/systm.h>
     36 #include <sys/proc.h>
     37 #include <sys/cpu.h>
     38 
     39 #include <uvm/uvm_extern.h>
     40 
     41 #include <machine/rpb.h>
     42 #include <machine/alpha.h>
     43 #define	ENABLEPROM
     44 #include <machine/prom.h>
     45 
     46 #include <dev/cons.h>
     47 
     48 /* XXX this is to fake out the console routines, while booting. */
     49 struct consdev promcons = {
     50 	.cn_getc = promcngetc,
     51 	.cn_putc = promcnputc,
     52 	.cn_pollc = nullcnpollc,
     53 	.cn_dev = makedev(23,0),
     54 	.cn_pri = 1
     55 };
     56 
     57 struct rpb	*hwrpb __read_mostly;
     58 int		alpha_console;
     59 
     60 extern struct prom_vec prom_dispatch_v;
     61 
     62 bool		prom_interface_initialized;
     63 int		prom_mapped = 1;	/* Is PROM still mapped? */
     64 
     65 static kmutex_t	prom_lock;
     66 
     67 static struct linux_kernel_params qemu_kernel_params;
     68 
     69 #ifdef _PROM_MAY_USE_PROM_CONSOLE
     70 
     71 pt_entry_t	prom_pte, saved_pte[1];	/* XXX */
     72 
     73 static pt_entry_t *
     74 prom_lev1map(void)
     75 {
     76 	struct alpha_pcb *apcb;
     77 
     78 	/*
     79 	 * Find the level 1 map that we're currently running on.
     80 	 */
     81 	apcb = (struct alpha_pcb *)
     82 	    ALPHA_PHYS_TO_K0SEG((paddr_t)curlwp->l_md.md_pcbpaddr);
     83 
     84 	return ((pt_entry_t *)ALPHA_PHYS_TO_K0SEG(apcb->apcb_ptbr << PGSHIFT));
     85 }
     86 #endif /* _PROM_MAY_USE_PROM_CONSOLE */
     87 
     88 bool
     89 prom_uses_prom_console(void)
     90 {
     91 #ifdef _PROM_MAY_USE_PROM_CONSOLE
     92 	return (cputype == ST_DEC_7000 || cputype == ST_DEC_21000);
     93 #else
     94 	return false;
     95 #endif
     96 }
     97 
     98 static void
     99 prom_init_cputype(const struct rpb * const rpb)
    100 {
    101 	cputype = rpb->rpb_type;
    102 	if (cputype < 0) {
    103 		/*
    104 		 * At least some white-box systems have SRM which
    105 		 * reports a systype that's the negative of their
    106 		 * blue-box counterpart.
    107 		 */
    108 		cputype = -cputype;
    109 	}
    110 }
    111 
    112 static void
    113 prom_check_qemu(const struct rpb * const rpb)
    114 {
    115 	if (!alpha_is_qemu) {
    116 		if (rpb->rpb_ssn[0] == 'Q' &&
    117 		    rpb->rpb_ssn[1] == 'E' &&
    118 		    rpb->rpb_ssn[2] == 'M' &&
    119 		    rpb->rpb_ssn[3] == 'U') {
    120 			alpha_is_qemu = true;
    121 		}
    122 	}
    123 }
    124 
    125 void
    126 init_prom_interface(u_long ptb_pfn, struct rpb *rpb)
    127 {
    128 
    129 	if (prom_interface_initialized)
    130 		return;
    131 
    132 	struct crb *c;
    133 
    134 	prom_init_cputype(rpb);
    135 	prom_check_qemu(rpb);
    136 
    137 	c = (struct crb *)((char *)rpb + rpb->rpb_crb_off);
    138 
    139 	prom_dispatch_v.routine_arg = c->crb_v_dispatch;
    140 	prom_dispatch_v.routine = c->crb_v_dispatch->entry_va;
    141 
    142 	if (alpha_is_qemu) {
    143 		/*
    144 		 * Qemu has placed a Linux kernel parameter block
    145 		 * at kernel_text[] - 0x6000.  We ensure the command
    146 		 * line field is always NUL-terminated to simplify
    147 		 * things later.
    148 		 */
    149 		extern char kernel_text[];
    150 		memcpy(&qemu_kernel_params,
    151 		       (void *)((vaddr_t)kernel_text - 0x6000),
    152 		       sizeof(qemu_kernel_params));
    153 		qemu_kernel_params.kernel_cmdline[
    154 		    sizeof(qemu_kernel_params.kernel_cmdline) - 1] = '\0';
    155 	}
    156 
    157 #ifdef _PROM_MAY_USE_PROM_CONSOLE
    158 	if (prom_uses_prom_console()) {
    159 		/*
    160 		 * XXX Save old PTE so we can remap the PROM, if
    161 		 * XXX necessary.
    162 		 */
    163 		pt_entry_t * const l1pt =
    164 		    (pt_entry_t *)ALPHA_PHYS_TO_K0SEG(ptb_pfn << PGSHIFT);
    165 		prom_pte = l1pt[0] & ~PG_ASM;
    166 	}
    167 #endif /* _PROM_MAY_USE_PROM_CONSOLE */
    168 
    169 	mutex_init(&prom_lock, MUTEX_DEFAULT, IPL_HIGH);
    170 	prom_interface_initialized = true;
    171 }
    172 
    173 void
    174 init_bootstrap_console(void)
    175 {
    176 	char buf[4];
    177 
    178 	/* init_prom_interface() has already been called. */
    179 	if (! prom_interface_initialized) {
    180 		prom_halt(1);
    181 	}
    182 
    183 	prom_getenv(PROM_E_TTY_DEV, buf, sizeof(buf));
    184 	alpha_console = buf[0] - '0';
    185 
    186 	/* XXX fake out the console routines, for now */
    187 	cn_tab = &promcons;
    188 }
    189 
    190 bool
    191 prom_qemu_getenv(const char *var, char *buf, size_t buflen)
    192 {
    193 	const size_t varlen = strlen(var);
    194 	const char *sp;
    195 
    196 	if (!alpha_is_qemu) {
    197 		return false;
    198 	}
    199 
    200 	sp = qemu_kernel_params.kernel_cmdline;
    201 	for (;;) {
    202 		sp = strstr(sp, var);
    203 		if (sp == NULL) {
    204 			return false;
    205 		}
    206 		sp += varlen;
    207 		if (*sp++ != '=') {
    208 			continue;
    209 		}
    210 		/* Found it. */
    211 		break;
    212 	}
    213 
    214 	while (--buflen) {
    215 		if (*sp == ' ' || *sp == '\t' || *sp == '\0') {
    216 			break;
    217 		}
    218 		*buf++ = *sp++;
    219 	}
    220 	*buf = '\0';
    221 
    222 	return true;
    223 }
    224 
    225 #ifdef _PROM_MAY_USE_PROM_CONSOLE
    226 static void prom_cache_sync(void);
    227 #endif
    228 
    229 void
    230 prom_enter(void)
    231 {
    232 
    233 	mutex_enter(&prom_lock);
    234 
    235 #ifdef _PROM_MAY_USE_PROM_CONSOLE
    236 	/*
    237 	 * If we have not yet switched out of the PROM's context
    238 	 * (i.e. the first one after alpha_init()), then the PROM
    239 	 * is still mapped, regardless of the `prom_mapped' setting.
    240 	 */
    241 	if (! prom_mapped) {
    242 		if (!prom_uses_prom_console())
    243 			panic("prom_enter");
    244 		{
    245 			pt_entry_t *lev1map;
    246 
    247 			lev1map = prom_lev1map();	/* XXX */
    248 			saved_pte[0] = lev1map[0];	/* XXX */
    249 			lev1map[0] = prom_pte;		/* XXX */
    250 		}
    251 		prom_cache_sync();			/* XXX */
    252 	}
    253 #endif
    254 }
    255 
    256 void
    257 prom_leave(void)
    258 {
    259 
    260 #ifdef _PROM_MAY_USE_PROM_CONSOLE
    261 	/*
    262 	 * See comment above.
    263 	 */
    264 	if (! prom_mapped) {
    265 		if (!prom_uses_prom_console())
    266 			panic("prom_leave");
    267 		{
    268 			pt_entry_t *lev1map;
    269 
    270 			lev1map = prom_lev1map();	/* XXX */
    271 			lev1map[0] = saved_pte[0];	/* XXX */
    272 		}
    273 		prom_cache_sync();			/* XXX */
    274 	}
    275 #endif
    276 	mutex_exit(&prom_lock);
    277 }
    278 
    279 #ifdef _PROM_MAY_USE_PROM_CONSOLE
    280 static void
    281 prom_cache_sync(void)
    282 {
    283 	ALPHA_TBIA();
    284 	alpha_pal_imb();
    285 }
    286 #endif
    287 
    288 /*
    289  * promcnputc:
    290  *
    291  * Remap char before passing off to prom.
    292  *
    293  * Prom only takes 32 bit addresses. Copy char somewhere prom can
    294  * find it. This routine will stop working after pmap_rid_of_console
    295  * is called in alpha_init. This is due to the hard coded address
    296  * of the console area.
    297  */
    298 void
    299 promcnputc(dev_t dev, int c)
    300 {
    301 	prom_return_t ret;
    302 	unsigned char *to = (unsigned char *)0x20000000;
    303 
    304 	/* XXX */
    305 	if (alpha_is_qemu)
    306 		return;
    307 
    308 	prom_enter();
    309 	*to = c;
    310 
    311 	do {
    312 		ret.bits = prom_putstr(alpha_console, to, 1);
    313 	} while ((ret.u.retval & 1) == 0);
    314 
    315 	prom_leave();
    316 }
    317 
    318 /*
    319  * promcngetc:
    320  *
    321  * Wait for the prom to get a real char and pass it back.
    322  */
    323 int
    324 promcngetc(dev_t dev)
    325 {
    326 	prom_return_t ret;
    327 
    328 	/* XXX */
    329 	if (alpha_is_qemu)
    330 		return 0;
    331 
    332 	for (;;) {
    333 		prom_enter();
    334 	        ret.bits = prom_getc(alpha_console);
    335 		prom_leave();
    336 	        if (ret.u.status == 0 || ret.u.status == 1)
    337 	                return (ret.u.retval);
    338 	}
    339 }
    340 
    341 /*
    342  * promcnlookc:
    343  *
    344  * See if prom has a real char and pass it back.
    345  */
    346 int
    347 promcnlookc(dev_t dev, char *cp)
    348 {
    349 	prom_return_t ret;
    350 
    351 	/* XXX */
    352 	if (alpha_is_qemu)
    353 		return 0;
    354 
    355 	prom_enter();
    356 	ret.bits = prom_getc(alpha_console);
    357 	prom_leave();
    358 	if (ret.u.status == 0 || ret.u.status == 1) {
    359 		*cp = ret.u.retval;
    360 		return 1;
    361 	} else
    362 		return 0;
    363 }
    364 
    365 int
    366 prom_getenv(int id, char *buf, int len)
    367 {
    368 	unsigned char *to = (unsigned char *)0x20000000;
    369 	prom_return_t ret;
    370 
    371 	/* XXX */
    372 	if (alpha_is_qemu)
    373 		return 0;
    374 
    375 	prom_enter();
    376 	ret.bits = prom_getenv_disp(id, to, len);
    377 	if (ret.u.status & 0x4)
    378 		ret.u.retval = 0;
    379 	len = uimin(len - 1, ret.u.retval);
    380 	memcpy(buf, to, len);
    381 	buf[len] = '\0';
    382 	prom_leave();
    383 
    384 	return len;
    385 }
    386 
    387 void
    388 prom_halt(int halt)
    389 {
    390 	struct pcs *p;
    391 
    392 	/*
    393 	 * Turn off interrupts, for sanity.
    394 	 */
    395 	(void) splhigh();
    396 
    397 	/*
    398 	 * Set "boot request" part of the CPU state depending on what
    399 	 * we want to happen when we halt.
    400 	 */
    401 	p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
    402 	p->pcs_flags &= ~(PCS_RC | PCS_HALT_REQ);
    403 	if (halt)
    404 		p->pcs_flags |= PCS_HALT_STAY_HALTED;
    405 	else
    406 		p->pcs_flags |= PCS_HALT_WARM_BOOT;
    407 
    408 	/*
    409 	 * Halt the machine.
    410 	 */
    411 	alpha_pal_halt();
    412 }
    413 
    414 uint64_t
    415 hwrpb_checksum(void)
    416 {
    417 	uint64_t *p, sum;
    418 	int i;
    419 
    420 	for (i = 0, p = (uint64_t *)hwrpb, sum = 0;
    421 	    i < (offsetof(struct rpb, rpb_checksum) / sizeof (uint64_t));
    422 	    i++, p++)
    423 		sum += *p;
    424 
    425 	return (sum);
    426 }
    427 
    428 void
    429 hwrpb_primary_init(void)
    430 {
    431 	struct pcb *pcb;
    432 	struct pcs *p;
    433 
    434 	p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
    435 
    436 	/* Initialize the primary's HWPCB and the Virtual Page Table Base. */
    437 	pcb = lwp_getpcb(&lwp0);
    438 	memcpy(p->pcs_hwpcb, &pcb->pcb_hw, sizeof(pcb->pcb_hw));
    439 	hwrpb->rpb_vptb = VPTBASE;
    440 
    441 	hwrpb->rpb_checksum = hwrpb_checksum();
    442 }
    443 
    444 void
    445 hwrpb_restart_setup(void)
    446 {
    447 	struct pcs *p;
    448 
    449 	/* Clear bootstrap-in-progress flag since we're done bootstrapping */
    450 	p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
    451 	p->pcs_flags &= ~PCS_BIP;
    452 
    453 	/* when 'c'ontinuing from console halt, do a dump */
    454 	hwrpb->rpb_rest_term = (uint64_t)&XentRestart;
    455 	hwrpb->rpb_rest_term_val = 0x1;
    456 
    457 	hwrpb->rpb_checksum = hwrpb_checksum();
    458 
    459 	p->pcs_flags |= (PCS_RC | PCS_CV);
    460 }
    461 
    462 uint64_t
    463 console_restart(struct trapframe *framep)
    464 {
    465 	struct pcs *p;
    466 
    467 	/* Clear restart-capable flag, since we can no longer restart. */
    468 	p = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
    469 	p->pcs_flags &= ~PCS_RC;
    470 
    471 	/* Fill in the missing frame slots */
    472 
    473 	framep->tf_regs[FRAME_PS] = p->pcs_halt_ps;
    474 	framep->tf_regs[FRAME_PC] = p->pcs_halt_pc;
    475 	framep->tf_regs[FRAME_T11] = p->pcs_halt_r25;
    476 	framep->tf_regs[FRAME_RA] = p->pcs_halt_r26;
    477 	framep->tf_regs[FRAME_T12] = p->pcs_halt_r27;
    478 
    479 	panic("user requested console halt");
    480 
    481 	return (1);
    482 }
    483