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 */ 53 struct kernel_state { 54 int saved_spl; 55 int saved_ctx; 56 u_int saved_ptes[4]; 57 }; 58 59 static void **sunmon_vbr; 60 static struct kernel_state sunmon_kernel_state; 61 static struct bootparam sunmon_bootparam; 62 static u_int sunmon_ptes[4]; 63 64 static 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 */ 78 static inline void _prom_swap_ptes(u_int *, u_int *); 79 static 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 */ 95 static inline void _mode_monitor(struct kernel_state *, int); 96 static 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 */ 124 static inline void _mode_kernel(struct kernel_state *, int); 125 static 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) \ 149 type 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 } 159 PROMLIB_FUNC(int, prom_memsize, (void), memorySize, + 0, return(rc)) 160 PROMLIB_FUNC(int, prom_stdin, (void), inSource, + 0, return(rc)) 161 PROMLIB_FUNC(int, prom_stdout, (void), outSink, + 0, return(rc)) 162 PROMLIB_FUNC(int, prom_kbdid, (void), keyBid, + 0, return(rc)) 163 PROMLIB_FUNC(int, prom_getchar, (void), getChar, (), return(rc)) 164 PROMLIB_FUNC(int, prom_peekchar, (void), mayGet, (), return(rc)) 165 PROMLIB_FUNC(void, prom_putchar, (int c), putChar, (c), return) 166 167 void 168 prom_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 */ 182 void 183 prom_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. */ 230 char * 231 prom_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. */ 240 char * 241 prom_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. */ 250 char * 251 prom_getbootfile(void) 252 { 253 return (sunmon_bootparam.fileName); 254 } 255 256 /* This maps a PROM `sd' unit number into a SCSI target. */ 257 int 258 prom_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 */ 270 void 271 prom_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 319 void 320 prom_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 */ 331 void 332 prom_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 */ 346 struct funcall_frame { 347 struct funcall_frame *fr_savfp; 348 int fr_savpc; 349 int fr_arg[1]; 350 }; 351 /*VARARGS0*/ 352 static void __noinline 353 tracedump(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" */ 373 void g0_handler(void); 374 void 375 g0_handler(void) 376 { 377 _mode_kernel(&sunmon_kernel_state, 1); 378 panic("zero"); 379 } 380 void g4_handler(int); 381 void 382 g4_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 */ 397 void 398 prom_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