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