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