Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
/*	$NetBSD: promlib.c,v 1.19 2020/09/10 02:03:44 rin Exp $	*/

/*-
 * Copyright (c) 1996 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Adam Glass, Gordon W. Ross, and Matthew Fredette.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: promlib.c,v 1.19 2020/09/10 02:03:44 rin Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/reboot.h>
#include <sys/boot_flag.h>

#include <uvm/uvm_extern.h>

#define _SUN2_PROMLIB_PRIVATE
#include <machine/promlib.h>

#include <sun2/sun2/machdep.h>
#include <sun2/sun2/control.h>
#include <sun68k/sun68k/vector.h>
#include <machine/pte.h>

/*
 * The state we save when we get ready to disappear into the PROM.
 */
struct kernel_state {
	int saved_spl;
	int saved_ctx;
	u_int saved_ptes[4];
};

static void **sunmon_vbr;
static struct kernel_state sunmon_kernel_state;
static struct bootparam sunmon_bootparam;
static u_int sunmon_ptes[4];

static void tracedump(int);

/*
 * The PROM keeps its data is in the first four physical pages, and
 * assumes that they're mapped to the first four virtual pages.
 * Normally we keep the first four virtual pages unmapped, so before
 * we can dereference pointers in romVectorPtr or call the PROM, we 
 * have to restore its mappings.  
 */

/*
 * This swaps out one set of PTEs for the first 
 * four virtual pages, and swaps another set in.
 */
static inline void _prom_swap_ptes(u_int *, u_int *);
static inline void 
_prom_swap_ptes(u_int *swapout, u_int *swapin)
{
	int pte_number;
	vaddr_t va;

	for (pte_number = 0, va = 0; pte_number < 4;
	     pte_number++, va += PAGE_SIZE) {
		swapout[pte_number] = get_pte(va);
		set_pte(va, swapin[pte_number]);
	}
}

/*
 * Prepare for running the PROM monitor.
 */
static inline void _mode_monitor(struct kernel_state *, int);
static inline void 
_mode_monitor(struct kernel_state *state, int full)
{
	/*
	 * Save the current context, and the PTEs for pages
	 * zero through three, and reset them to what the PROM 
	 * expects.
	 */
	state->saved_ctx = get_context();
	set_context(0);
	_prom_swap_ptes(state->saved_ptes, sunmon_ptes);

	/*
	 * If we're going to enter the PROM fully, raise the interrupt
	 * level, disable our level 5 clock, restore the PROM vector
	 * table, and enable the PROM NMI clock.
	 */
	if (full) {
		state->saved_spl = splhigh();
		set_clk_mode(0, 0);
		setvbr(sunmon_vbr);
		set_clk_mode(1, 1);
	}
}

/*
 * Prepare for running the kernel.
 */
static inline void _mode_kernel(struct kernel_state *, int);
static inline void 
_mode_kernel(struct kernel_state *state, int full)
{
	/*
	 * If we were in the PROM fully, disable the PROM NMI clock,
	 * restore our own vector table, and enable our level 5 clock.
	 */
	if (full) {
		set_clk_mode(1, 0);
		setvbr(vector_table);
		set_clk_mode(0, 1);
		splx(state->saved_spl);
	}

	/*
	 * Restore our PTEs for pages zero through three, 
	 * and restore the current context.
	 */
	_prom_swap_ptes(sunmon_ptes, state->saved_ptes);
	set_context(state->saved_ctx);
}

/* We define many prom_ functions using this macro. */
#define PROMLIB_FUNC(type, new, proto, old, args, ret)			\
type new proto								\
{									\
	struct kernel_state state;					\
	int rc;								\
	_mode_monitor(&state, 0);					\
	rc = (*(romVectorPtr->old)) args;				\
	__USE(rc);							\
	_mode_kernel(&state, 0);					\
	ret ;								\
}
PROMLIB_FUNC(int, prom_memsize, (void), memorySize, + 0, return(rc))
PROMLIB_FUNC(int, prom_stdin, (void), inSource, + 0, return(rc))
PROMLIB_FUNC(int, prom_stdout, (void), outSink, + 0, return(rc))
PROMLIB_FUNC(int, prom_kbdid, (void), keyBid, + 0, return(rc))
PROMLIB_FUNC(int, prom_getchar, (void), getChar, (), return(rc))
PROMLIB_FUNC(int, prom_peekchar, (void), mayGet, (), return(rc))
PROMLIB_FUNC(void, prom_putchar, (int c), putChar, (c), return)

void
prom_putstr(char *buf, int len)
{
	struct kernel_state state;
	_mode_monitor(&state, 0);
	for(; len > 0; buf++, len--) {
		(*(romVectorPtr->putChar))((int) (*buf));
	}
	_mode_kernel(&state, 0);
}

/*
 * printf is difficult, because it's a varargs function.
 * This is very ugly.  Please fix me!
 */
void
prom_printf(const char *fmt, ...)
{
	struct kernel_state state;
	int rc;
	va_list ap;
	const char *p1;
	char c1;
	struct printf_args {
		int arg[15];
	} varargs;
	int i;

	/*
	 * Since the PROM obviously doesn't take a va_list, we conjure
	 * up a structure of ints to hold the arguments, and pass it
	 * the structure (*not* a pointer to the structure!) to get
	 * the same effect.  This means there is a limit on the number
	 * of arguments you can use with prom_printf.  Ugly indeed.
	 */
	va_start(ap, fmt);
	i = 0;
	for(p1 = fmt; (c1 = *(p1++)) != '\0'; ) {
		if (c1 == '%') {
			if (i == (sizeof(varargs.arg) /
				  sizeof(varargs.arg[0]))) {
				prom_printf("too many args to prom_printf, "
					    "format %s", fmt);
				prom_abort();
			}
			varargs.arg[i++] = va_arg(ap, int);
		}
	}
	va_end(ap);

	/* now call the monitor's printf: */
	_mode_monitor(&state, 0);
	rc = (*
	    /* the ghastly type we cast the PROM printf vector to: */
	    ( (int (*)(const char *, struct printf_args))
	    /* the PROM printf vector: */
		(romVectorPtr->printf))
		)(fmt, varargs);
	__USE(rc);
	_mode_kernel(&state, 0);
}

/* Return the boot path. */
char *
prom_getbootpath(void)
{
	/*
	 * The first bootparam argument is the device string.
	 */
	return (sunmon_bootparam.argPtr[0]);
}

/* Return the boot args. */
char *
prom_getbootargs(void)
{
	/*
	 * The second bootparam argument is any options.
	 */
	return (sunmon_bootparam.argPtr[1]);
}

/* Return the boot file. */
char *
prom_getbootfile(void)
{
	return (sunmon_bootparam.fileName);
}

/* This maps a PROM `sd' unit number into a SCSI target. */
int 
prom_sd_target(int unit)
{
	switch(unit) {
	case 2:	return (4);
	}
	return (unit);
}

/*
 * This aborts to the PROM, but should allow the user
 * to "c" continue back into the kernel.
 */
void 
prom_abort(void)
{
	uint16_t old_g0_g4_vectors[4], *vec, *store;

	_mode_monitor(&sunmon_kernel_state, 1);

	/*
	 * Set up our g0 and g4 handlers, by writing into
	 * the PROM's vector table directly.  Note that
	 * the braw instruction displacement is PC-relative.
	 */
#define	BRAW	0x6000
	vec = (uint16_t *) sunmon_vbr;
	store = old_g0_g4_vectors;
	*(store++) = *vec;
	*(vec++) = BRAW;
	*(store++) = *vec;
	*vec = ((u_long) g0_entry) - ((u_long) vec);
	vec++;
	*(store++) = *vec;
	*(vec++) = BRAW;
	*(store++) = *vec;
	*vec = ((u_long) g4_entry) - ((u_long) vec);
	vec++;
#undef	BRAW

	delay(100000);

	/*
	 * Drop into the PROM in a way that allows a continue.
	 * Already setup "trap #14" in prom_init().
	 */

	__asm(" trap #14 ; _sunmon_continued: nop");

	/* We have continued from a PROM abort! */

	/* Put back the old g0 and g4 handlers. */
	vec = (uint16_t *) sunmon_vbr;
	store = old_g0_g4_vectors;
	*(vec++) = *(store++);
	*(vec++) = *(store++);
	*(vec++) = *(store++);
	*(vec++) = *(store++);

	_mode_kernel(&sunmon_kernel_state, 1);
}

void 
prom_halt(void)
{
	_mode_monitor(&sunmon_kernel_state, 1);
	(*romVectorPtr->exitToMon)();
	for(;;);
	/*NOTREACHED*/
}

/*
 * Caller must pass a string that is in our data segment.
 */
void 
prom_boot(const char *bs)
{
	_mode_monitor(&sunmon_kernel_state, 1);
	(*romVectorPtr->reBoot)(bs);
	(*romVectorPtr->exitToMon)();
	for(;;);
	/*NOTREACHED*/
}


/*
 * Print out a traceback for the caller - can be called anywhere
 * within the kernel or from the monitor by typing "g4".
 */
struct funcall_frame {
	struct funcall_frame *fr_savfp;
	int fr_savpc;
	int fr_arg[1];
};
/*VARARGS0*/
static void __noinline
tracedump(int x1)
{
	struct funcall_frame *fp = (struct funcall_frame *)(&x1 - 2);
	u_int stackpage = ((u_int)fp) & ~PGOFSET;

	prom_printf("Begin traceback...fp = 0x%x\n", fp);
	do {
		if (fp == fp->fr_savfp) {
			prom_printf("FP loop at 0x%x", fp);
			break;
		}
		prom_printf("Called from 0x%x, fp=0x%x, args=0x%x 0x%x 0x%x 0x%x\n",
				   fp->fr_savpc, fp->fr_savfp,
				   fp->fr_arg[0], fp->fr_arg[1], fp->fr_arg[2], fp->fr_arg[3]);
		fp = fp->fr_savfp;
	} while ( (((u_int)fp) & ~PGOFSET) == stackpage);
	prom_printf("End traceback...\n");
}

/* Handlers for the old-school "g0" and "g4" */
void g0_handler(void);
void 
g0_handler(void)
{
	_mode_kernel(&sunmon_kernel_state, 1);
	panic("zero");
}
void g4_handler(int);
void 
g4_handler(int addr)
{
	_mode_kernel(&sunmon_kernel_state, 1);
	tracedump(addr);
}

/*
 * Set the PROM vector handler (for g0, g4, etc.)
 * and set boothowto from the PROM arg strings.
 *
 * Note, args are always:
 * argv[0] = boot_device	(i.e. "sd(0,0,0)")
 * argv[1] = options	(i.e. "-ds" or NULL)
 * argv[2] = NULL
 */
void 
prom_init(void)
{
	struct bootparam *old_bp;
	struct bootparam *new_bp;
	int bp_shift;
	int i;
	char *p;
	int fl;

	/*
	 * Any second the pointers in the PROM vector are going to
	 * break (since they point into pages zero through three, 
	 * which we like to keep unmapped), so we grab a complete 
	 * copy of the bootparams, taking care to adjust the pointers 
	 * in the copy to also point to the copy.
	 */
	old_bp = *romVectorPtr->bootParam;
	new_bp = &sunmon_bootparam;
	*new_bp = *old_bp;
	bp_shift = ((char *) new_bp) - ((char *) old_bp);
	for(i = 0; i < 8 && new_bp->argPtr[i] != NULL; i++) {
		new_bp->argPtr[i] += bp_shift;
	}
	new_bp->fileName += bp_shift;

	/* Save the PROM's mappings for pages zero through three. */
	_prom_swap_ptes(sunmon_ptes, sunmon_ptes);

	/* Save the PROM monitor Vector Base Register (VBR). */
	sunmon_vbr = getvbr();

	/* Arrange for "trap #14" to cause a PROM abort. */
	sunmon_vbr[32+14] = romVectorPtr->abortEntry;

	/* Try to find some options. */
	p = prom_getbootargs();
	if (p != NULL) {

		/* Skip any whitespace */
		for(; *p != '-'; )
			if (*(p++) == '\0') {
				p = NULL;
				break;
			}
	}

	/* If we have options. */
	if (p != NULL) {
#ifdef	DEBUG
		prom_printf("boot options: %s\n", p);
#endif
		for(; *(++p); ) {
			fl = 0;
			BOOT_FLAG(*p, fl);
			if (fl)
				boothowto |= fl;
			else
				prom_printf("unknown option `%c'\n", *p);
		}
	}
}