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