xref: /netbsd/sys/arch/alpha/alpha/prom.c (revision d23d0ab6)
1 /* $NetBSD: prom.c,v 1.58 2020/10/03 17:31:46 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@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.58 2020/10/03 17:31:46 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 *
prom_lev1map(void)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
prom_uses_prom_console(void)89 prom_uses_prom_console(void)
90 {
91 #ifdef _PROM_MAY_USE_PROM_CONSOLE
92 	return (cputype == ST_DEC_21000);
93 #else
94 	return false;
95 #endif
96 }
97 
98 static void
prom_init_cputype(const struct rpb * const rpb)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
prom_check_qemu(const struct rpb * const rpb)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
init_prom_interface(u_long ptb_pfn,struct rpb * rpb)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
init_bootstrap_console(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
prom_qemu_getenv(const char * var,char * buf,size_t buflen)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
prom_enter(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
prom_leave(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
prom_cache_sync(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
promcnputc(dev_t dev,int c)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
promcngetc(dev_t dev)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
promcnlookc(dev_t dev,char * cp)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
prom_getenv(int id,char * buf,int len)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
prom_halt(int halt)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
hwrpb_checksum(void)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
hwrpb_primary_init(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
hwrpb_restart_setup(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
console_restart(struct trapframe * framep)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