xref: /netbsd-src/sys/arch/sun2/sun2/promlib.c (revision 1acb62a3e4408c1dfda13cfda517d781e7c0751e)
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
_prom_swap_ptes(u_int * swapout,u_int * swapin)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
_mode_monitor(struct kernel_state * state,int full)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
_mode_kernel(struct kernel_state * state,int full)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
prom_putstr(char * buf,int len)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
prom_printf(const char * fmt,...)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 *
prom_getbootpath(void)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 *
prom_getbootargs(void)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 *
prom_getbootfile(void)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
prom_sd_target(int unit)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
prom_abort(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
prom_halt(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
prom_boot(const char * bs)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
tracedump(int x1)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
g0_handler(void)375 g0_handler(void)
376 {
377 	_mode_kernel(&sunmon_kernel_state, 1);
378 	panic("zero");
379 }
380 void g4_handler(int);
381 void
g4_handler(int addr)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
prom_init(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