1/* $NetBSD: promlib.c,v 1.20 2024/01/13 18:51:38 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Glass, Gordon W. Ross, and Matthew Fredette. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: promlib.c,v 1.20 2024/01/13 18:51:38 thorpej Exp $"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/reboot.h> 38#include <sys/boot_flag.h> 39 40#include <uvm/uvm_extern.h> 41 42#define _SUN2_PROMLIB_PRIVATE 43#include <machine/promlib.h> 44#include <machine/vectors.h> 45 46#include <sun2/sun2/machdep.h> 47#include <sun2/sun2/control.h> 48#include <machine/pte.h> 49 50/* 51 * The state we save when we get ready to disappear into the PROM. 52 */ 53struct kernel_state { 54 int saved_spl; 55 int saved_ctx; 56 u_int saved_ptes[4]; 57}; 58 59static void **sunmon_vbr; 60static struct kernel_state sunmon_kernel_state; 61static struct bootparam sunmon_bootparam; 62static u_int sunmon_ptes[4]; 63 64static void tracedump(int); 65 66/* 67 * The PROM keeps its data is in the first four physical pages, and 68 * assumes that they're mapped to the first four virtual pages. 69 * Normally we keep the first four virtual pages unmapped, so before 70 * we can dereference pointers in romVectorPtr or call the PROM, we 71 * have to restore its mappings. 72 */ 73 74/* 75 * This swaps out one set of PTEs for the first 76 * four virtual pages, and swaps another set in. 77 */ 78static inline void _prom_swap_ptes(u_int *, u_int *); 79static inline void 80_prom_swap_ptes(u_int *swapout, u_int *swapin) 81{ 82 int pte_number; 83 vaddr_t va; 84 85 for (pte_number = 0, va = 0; pte_number < 4; 86 pte_number++, va += PAGE_SIZE) { 87 swapout[pte_number] = get_pte(va); 88 set_pte(va, swapin[pte_number]); 89 } 90} 91 92/* 93 * Prepare for running the PROM monitor. 94 */ 95static inline void _mode_monitor(struct kernel_state *, int); 96static inline void 97_mode_monitor(struct kernel_state *state, int full) 98{ 99 /* 100 * Save the current context, and the PTEs for pages 101 * zero through three, and reset them to what the PROM 102 * expects. 103 */ 104 state->saved_ctx = get_context(); 105 set_context(0); 106 _prom_swap_ptes(state->saved_ptes, sunmon_ptes); 107 108 /* 109 * If we're going to enter the PROM fully, raise the interrupt 110 * level, disable our level 5 clock, restore the PROM vector 111 * table, and enable the PROM NMI clock. 112 */ 113 if (full) { 114 state->saved_spl = splhigh(); 115 set_clk_mode(0, 0); 116 setvbr(sunmon_vbr); 117 set_clk_mode(1, 1); 118 } 119} 120 121/* 122 * Prepare for running the kernel. 123 */ 124static inline void _mode_kernel(struct kernel_state *, int); 125static inline void 126_mode_kernel(struct kernel_state *state, int full) 127{ 128 /* 129 * If we were in the PROM fully, disable the PROM NMI clock, 130 * restore our own vector table, and enable our level 5 clock. 131 */ 132 if (full) { 133 set_clk_mode(1, 0); 134 setvbr(vectab); 135 set_clk_mode(0, 1); 136 splx(state->saved_spl); 137 } 138 139 /* 140 * Restore our PTEs for pages zero through three, 141 * and restore the current context. 142 */ 143 _prom_swap_ptes(sunmon_ptes, state->saved_ptes); 144 set_context(state->saved_ctx); 145} 146 147/* We define many prom_ functions using this macro. */ 148#define PROMLIB_FUNC(type, new, proto, old, args, ret) \ 149type new proto \ 150{ \ 151 struct kernel_state state; \ 152 int rc; \ 153 _mode_monitor(&state, 0); \ 154 rc = (*(romVectorPtr->old)) args; \ 155 __USE(rc); \ 156 _mode_kernel(&state, 0); \ 157 ret ; \ 158} 159PROMLIB_FUNC(int, prom_memsize, (void), memorySize, + 0, return(rc)) 160PROMLIB_FUNC(int, prom_stdin, (void), inSource, + 0, return(rc)) 161PROMLIB_FUNC(int, prom_stdout, (void), outSink, + 0, return(rc)) 162PROMLIB_FUNC(int, prom_kbdid, (void), keyBid, + 0, return(rc)) 163PROMLIB_FUNC(int, prom_getchar, (void), getChar, (), return(rc)) 164PROMLIB_FUNC(int, prom_peekchar, (void), mayGet, (), return(rc)) 165PROMLIB_FUNC(void, prom_putchar, (int c), putChar, (c), return) 166 167void 168prom_putstr(char *buf, int len) 169{ 170 struct kernel_state state; 171 _mode_monitor(&state, 0); 172 for(; len > 0; buf++, len--) { 173 (*(romVectorPtr->putChar))((int) (*buf)); 174 } 175 _mode_kernel(&state, 0); 176} 177 178/* 179 * printf is difficult, because it's a varargs function. 180 * This is very ugly. Please fix me! 181 */ 182void 183prom_printf(const char *fmt, ...) 184{ 185 struct kernel_state state; 186 int rc; 187 va_list ap; 188 const char *p1; 189 char c1; 190 struct printf_args { 191 int arg[15]; 192 } varargs; 193 int i; 194 195 /* 196 * Since the PROM obviously doesn't take a va_list, we conjure 197 * up a structure of ints to hold the arguments, and pass it 198 * the structure (*not* a pointer to the structure!) to get 199 * the same effect. This means there is a limit on the number 200 * of arguments you can use with prom_printf. Ugly indeed. 201 */ 202 va_start(ap, fmt); 203 i = 0; 204 for(p1 = fmt; (c1 = *(p1++)) != '\0'; ) { 205 if (c1 == '%') { 206 if (i == (sizeof(varargs.arg) / 207 sizeof(varargs.arg[0]))) { 208 prom_printf("too many args to prom_printf, " 209 "format %s", fmt); 210 prom_abort(); 211 } 212 varargs.arg[i++] = va_arg(ap, int); 213 } 214 } 215 va_end(ap); 216 217 /* now call the monitor's printf: */ 218 _mode_monitor(&state, 0); 219 rc = (* 220 /* the ghastly type we cast the PROM printf vector to: */ 221 ( (int (*)(const char *, struct printf_args)) 222 /* the PROM printf vector: */ 223 (romVectorPtr->printf)) 224 )(fmt, varargs); 225 __USE(rc); 226 _mode_kernel(&state, 0); 227} 228 229/* Return the boot path. */ 230char * 231prom_getbootpath(void) 232{ 233 /* 234 * The first bootparam argument is the device string. 235 */ 236 return (sunmon_bootparam.argPtr[0]); 237} 238 239/* Return the boot args. */ 240char * 241prom_getbootargs(void) 242{ 243 /* 244 * The second bootparam argument is any options. 245 */ 246 return (sunmon_bootparam.argPtr[1]); 247} 248 249/* Return the boot file. */ 250char * 251prom_getbootfile(void) 252{ 253 return (sunmon_bootparam.fileName); 254} 255 256/* This maps a PROM `sd' unit number into a SCSI target. */ 257int 258prom_sd_target(int unit) 259{ 260 switch(unit) { 261 case 2: return (4); 262 } 263 return (unit); 264} 265 266/* 267 * This aborts to the PROM, but should allow the user 268 * to "c" continue back into the kernel. 269 */ 270void 271prom_abort(void) 272{ 273 uint16_t old_g0_g4_vectors[4], *vec, *store; 274 275 _mode_monitor(&sunmon_kernel_state, 1); 276 277 /* 278 * Set up our g0 and g4 handlers, by writing into 279 * the PROM's vector table directly. Note that 280 * the braw instruction displacement is PC-relative. 281 */ 282#define BRAW 0x6000 283 vec = (uint16_t *) sunmon_vbr; 284 store = old_g0_g4_vectors; 285 *(store++) = *vec; 286 *(vec++) = BRAW; 287 *(store++) = *vec; 288 *vec = ((u_long) g0_entry) - ((u_long) vec); 289 vec++; 290 *(store++) = *vec; 291 *(vec++) = BRAW; 292 *(store++) = *vec; 293 *vec = ((u_long) g4_entry) - ((u_long) vec); 294 vec++; 295#undef BRAW 296 297 delay(100000); 298 299 /* 300 * Drop into the PROM in a way that allows a continue. 301 * Already setup "trap #14" in prom_init(). 302 */ 303 304 __asm(" trap #14 ; _sunmon_continued: nop"); 305 306 /* We have continued from a PROM abort! */ 307 308 /* Put back the old g0 and g4 handlers. */ 309 vec = (uint16_t *) sunmon_vbr; 310 store = old_g0_g4_vectors; 311 *(vec++) = *(store++); 312 *(vec++) = *(store++); 313 *(vec++) = *(store++); 314 *(vec++) = *(store++); 315 316 _mode_kernel(&sunmon_kernel_state, 1); 317} 318 319void 320prom_halt(void) 321{ 322 _mode_monitor(&sunmon_kernel_state, 1); 323 (*romVectorPtr->exitToMon)(); 324 for(;;); 325 /*NOTREACHED*/ 326} 327 328/* 329 * Caller must pass a string that is in our data segment. 330 */ 331void 332prom_boot(const char *bs) 333{ 334 _mode_monitor(&sunmon_kernel_state, 1); 335 (*romVectorPtr->reBoot)(bs); 336 (*romVectorPtr->exitToMon)(); 337 for(;;); 338 /*NOTREACHED*/ 339} 340 341 342/* 343 * Print out a traceback for the caller - can be called anywhere 344 * within the kernel or from the monitor by typing "g4". 345 */ 346struct funcall_frame { 347 struct funcall_frame *fr_savfp; 348 int fr_savpc; 349 int fr_arg[1]; 350}; 351/*VARARGS0*/ 352static void __noinline 353tracedump(int x1) 354{ 355 struct funcall_frame *fp = (struct funcall_frame *)(&x1 - 2); 356 u_int stackpage = ((u_int)fp) & ~PGOFSET; 357 358 prom_printf("Begin traceback...fp = 0x%x\n", fp); 359 do { 360 if (fp == fp->fr_savfp) { 361 prom_printf("FP loop at 0x%x", fp); 362 break; 363 } 364 prom_printf("Called from 0x%x, fp=0x%x, args=0x%x 0x%x 0x%x 0x%x\n", 365 fp->fr_savpc, fp->fr_savfp, 366 fp->fr_arg[0], fp->fr_arg[1], fp->fr_arg[2], fp->fr_arg[3]); 367 fp = fp->fr_savfp; 368 } while ( (((u_int)fp) & ~PGOFSET) == stackpage); 369 prom_printf("End traceback...\n"); 370} 371 372/* Handlers for the old-school "g0" and "g4" */ 373void g0_handler(void); 374void 375g0_handler(void) 376{ 377 _mode_kernel(&sunmon_kernel_state, 1); 378 panic("zero"); 379} 380void g4_handler(int); 381void 382g4_handler(int addr) 383{ 384 _mode_kernel(&sunmon_kernel_state, 1); 385 tracedump(addr); 386} 387 388/* 389 * Set the PROM vector handler (for g0, g4, etc.) 390 * and set boothowto from the PROM arg strings. 391 * 392 * Note, args are always: 393 * argv[0] = boot_device (i.e. "sd(0,0,0)") 394 * argv[1] = options (i.e. "-ds" or NULL) 395 * argv[2] = NULL 396 */ 397void 398prom_init(void) 399{ 400 struct bootparam *old_bp; 401 struct bootparam *new_bp; 402 int bp_shift; 403 int i; 404 char *p; 405 int fl; 406 407 /* 408 * Any second the pointers in the PROM vector are going to 409 * break (since they point into pages zero through three, 410 * which we like to keep unmapped), so we grab a complete 411 * copy of the bootparams, taking care to adjust the pointers 412 * in the copy to also point to the copy. 413 */ 414 old_bp = *romVectorPtr->bootParam; 415 new_bp = &sunmon_bootparam; 416 *new_bp = *old_bp; 417 bp_shift = ((char *) new_bp) - ((char *) old_bp); 418 for(i = 0; i < 8 && new_bp->argPtr[i] != NULL; i++) { 419 new_bp->argPtr[i] += bp_shift; 420 } 421 new_bp->fileName += bp_shift; 422 423 /* Save the PROM's mappings for pages zero through three. */ 424 _prom_swap_ptes(sunmon_ptes, sunmon_ptes); 425 426 /* Save the PROM monitor Vector Base Register (VBR). */ 427 sunmon_vbr = getvbr(); 428 429 /* Arrange for "trap #14" to cause a PROM abort. */ 430 sunmon_vbr[32+14] = romVectorPtr->abortEntry; 431 432 /* Try to find some options. */ 433 p = prom_getbootargs(); 434 if (p != NULL) { 435 436 /* Skip any whitespace */ 437 for(; *p != '-'; ) 438 if (*(p++) == '\0') { 439 p = NULL; 440 break; 441 } 442 } 443 444 /* If we have options. */ 445 if (p != NULL) { 446#ifdef DEBUG 447 prom_printf("boot options: %s\n", p); 448#endif 449 for(; *(++p); ) { 450 fl = 0; 451 BOOT_FLAG(*p, fl); 452 if (fl) 453 boothowto |= fl; 454 else 455 prom_printf("unknown option `%c'\n", *p); 456 } 457 } 458} 459