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