1/*	$NetBSD: boot.c,v 1.35 2017/09/15 13:25:34 martin Exp $	*/
2
3/*
4 * Copyright (c) 1997, 1999 Eduardo E. Horvath.  All rights reserved.
5 * Copyright (c) 1997 Jason R. Thorpe.  All rights reserved.
6 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
7 * Copyright (C) 1995, 1996 TooLs GmbH.
8 * All rights reserved.
9 *
10 * ELF support derived from NetBSD/alpha's boot loader, written
11 * by Christopher G. Demetriou.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 *    must display the following acknowledgement:
23 *	This product includes software developed by TooLs GmbH.
24 * 4. The name of TooLs GmbH may not be used to endorse or promote products
25 *    derived from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * First try for the boot code
41 *
42 * Input syntax is:
43 *	[promdev[{:|,}partition]]/[filename] [flags]
44 */
45
46#include <lib/libsa/stand.h>
47#include <lib/libsa/bootcfg.h>
48#include <lib/libsa/loadfile.h>
49#include <lib/libkern/libkern.h>
50
51#include <sys/param.h>
52#include <sys/reboot.h>
53#include <sys/disklabel.h>
54#include <sys/boot_flag.h>
55
56#include <machine/cpu.h>
57#include <machine/promlib.h>
58#include <machine/bootinfo.h>
59#include <sparc/stand/common/isfloppy.h>
60
61#include "boot.h"
62#include "ofdev.h"
63#include "openfirm.h"
64
65
66#define COMPAT_BOOT(marks)	(marks[MARK_START] == marks[MARK_ENTRY])
67
68
69typedef void (*entry_t)(long o0, long bootargs, long bootsize, long o3,
70                        long ofw);
71
72/*
73 * Boot device is derived from ROM provided information, or if there is none,
74 * this list is used in sequence, to find a kernel.
75 */
76const char *kernelnames[] = {
77	"netbsd",
78	"netbsd.gz",
79	"netbsd.old",
80	"netbsd.old.gz",
81	"onetbsd",
82	"onetbsd.gz",
83	"vmunix ",
84#ifdef notyet
85	"netbsd.pl ",
86	"netbsd.pl.gz ",
87	"netbsd.el ",
88	"netbsd.el.gz ",
89#endif
90	NULL
91};
92
93char bootdev[PROM_MAX_PATH];
94bool root_fs_quickseekable = true;	/* unset for tftp boots */
95static bool bootinfo_pass_bootdev = false;
96
97int debug  = 0;
98int compatmode = 0;
99extern char twiddle_toggle;
100
101#if 0
102static void
103prom2boot(char *dev)
104{
105	char *cp, *lp = 0;
106	int handle;
107	char devtype[16];
108
109	for (cp = dev; *cp; cp++)
110		if (*cp == ':')
111			lp = cp;
112	if (!lp)
113		lp = cp;
114	*lp = 0;
115}
116#endif
117
118static int
119bootoptions(const char *ap, char *loaddev, char *kernel, char *options)
120{
121	int v = 0;
122	const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL;
123	const char *path;
124	char partition, *pp;
125
126	*kernel  = '\0';
127	*options = '\0';
128
129	if (ap == NULL) {
130		return (0);
131	}
132
133	while (*ap == ' ') {
134		ap++;
135	}
136
137	if (*ap != '-') {
138		start1 = ap;
139		while (*ap != '\0' && *ap != ' ') {
140			ap++;
141		}
142		end1 = ap;
143
144		while (*ap == ' ') {
145			ap++;
146		}
147
148		if (*ap != '-') {
149			start2 = ap;
150			while (*ap != '\0' && *ap != ' ') {
151				ap++;
152			}
153			end2 = ap;
154			while (*ap != '\0' && *ap == ' ') {
155				ap++;
156			}
157		}
158	}
159	if (end2 == start2) {
160		start2 = end2 = NULL;
161	}
162	if (end1 == start1) {
163		start1 = end1 = NULL;
164	}
165
166	if (start1 == NULL) {
167		/* only options */
168	} else if (start2 == NULL) {
169		memcpy(kernel, start1, (end1 - start1));
170		kernel[end1 - start1] = '\0';
171		path = filename(kernel, &partition);
172		if (path == NULL) {
173			strcpy(loaddev, kernel);
174			kernel[0] = '\0';
175		} else if (path != kernel) {
176			/* copy device part */
177			memcpy(loaddev, kernel, path-kernel);
178			loaddev[path-kernel] = '\0';
179			if (partition) {
180				pp = loaddev + strlen(loaddev);
181				pp[0] = ':';
182				pp[1] = partition;
183				pp[2] = '\0';
184			}
185			/* and kernel path */
186			strcpy(kernel, path);
187		}
188	} else {
189		memcpy(loaddev, start1, (end1-start1));
190		loaddev[end1-start1] = '\0';
191		memcpy(kernel, start2, (end2 - start2));
192		kernel[end2 - start2] = '\0';
193	}
194
195	twiddle_toggle = 1;
196	strcpy(options, ap);
197	while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') {
198		BOOT_FLAG(*ap, v);
199		switch(*ap++) {
200		case 'D':
201			debug = 2;
202			break;
203		case 'C':
204			compatmode = 1;
205			break;
206		case 'T':
207			twiddle_toggle = 1 - twiddle_toggle;
208			break;
209		default:
210			break;
211		}
212	}
213
214	if (((v & RB_KDB) != 0) && (debug == 0)) {
215		debug = 1;
216	}
217
218	DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n",
219	    loaddev, kernel, options));
220	return (v);
221}
222
223/*
224 * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle
225 * ksyms information unless it resides in a dedicated memory allocated from
226 * PROM and aligned on NBPG boundary. This is because the kernels calculate
227 * their ends on their own, they use address of 'end[]' reference which follows
228 * text segment. Ok, allocate some memory from PROM and copy symbol information
229 * over there.
230 */
231static void
232ksyms_copyout(void **ssym, void **esym)
233{
234	uint8_t *addr;
235	int kssize = (int)(long)((char *)*esym - (char *)*ssym + 1);
236
237	DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n",
238				*ssym, *esym, kssize));
239
240	if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) {
241		panic("ksyms_copyout(): no space for symbol table");
242	}
243
244	memcpy(addr, *ssym, kssize);
245	*ssym = addr;
246	*esym = addr + kssize - 1;
247
248	DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym));
249}
250
251/*
252 * Prepare boot information and jump directly to the kernel.
253 */
254static void
255jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw,
256	int boothowto)
257{
258	int l, machine_tag;
259	long newargs[4];
260	void *ssym, *esym;
261	vaddr_t bootinfo;
262	struct btinfo_symtab bi_sym;
263	struct btinfo_kernend bi_kend;
264	struct btinfo_boothowto bi_howto;
265	char *cp;
266	char bootline[PROM_MAX_PATH * 2];
267
268	/* Compose kernel boot line. */
269	strncpy(bootline, kernel, sizeof(bootline));
270	cp = bootline + strlen(bootline);
271	if (*args) {
272		*cp++ = ' ';
273		strncpy(bootline, args, sizeof(bootline) - (cp - bootline));
274	}
275	*cp = 0; args = bootline;
276
277	/* Record symbol information in the bootinfo. */
278	bootinfo = bi_init(marks[MARK_END]);
279	bi_sym.nsym = marks[MARK_NSYM];
280	bi_sym.ssym = marks[MARK_SYM];
281	bi_sym.esym = marks[MARK_END];
282	bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym));
283	bi_kend.addr= bootinfo + BOOTINFO_SIZE;
284	bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend));
285	bi_howto.boothowto = boothowto;
286	bi_add(&bi_howto, BTINFO_BOOTHOWTO, sizeof(bi_howto));
287	if (bootinfo_pass_bootunit)
288		bi_add(&bi_unit, BTINFO_BOOTDEV_UNIT,
289		    sizeof(bi_unit));
290	if (bootinfo_pass_bootdev) {
291		struct {
292			struct btinfo_common common;
293			char name[256];
294		} info;
295
296		strcpy(info.name, bootdev);
297		bi_add(&info, BTINFO_BOOTDEV, strlen(bootdev)
298			+sizeof(struct btinfo_bootdev));
299	}
300
301	sparc64_finalize_tlb(marks[MARK_DATA]);
302	sparc64_bi_add();
303
304	ssym  = (void*)(long)marks[MARK_SYM];
305	esym  = (void*)(long)marks[MARK_END];
306
307	DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym));
308
309	/* Adjust ksyms pointers, if needed. */
310	if (COMPAT_BOOT(marks) || compatmode) {
311		ksyms_copyout(&ssym, &esym);
312	}
313
314	freeall();
315	/*
316	 * When we come in args consists of a pointer to the boot
317	 * string.  We need to fix it so it takes into account
318	 * other params such as romp.
319	 */
320
321	/*
322	 * Stash pointer to end of symbol table after the argument
323	 * strings.
324	 */
325	l = strlen(args) + 1;
326	memcpy(args + l, &esym, sizeof(esym));
327	l += sizeof(esym);
328
329	/*
330	 * Tell the kernel we're an OpenFirmware system.
331	 */
332	machine_tag = SPARC_MACHINE_OPENFIRMWARE;
333	memcpy(args + l, &machine_tag, sizeof(machine_tag));
334	l += sizeof(machine_tag);
335
336	/*
337	 * Since we don't need the boot string (we can get it from /chosen)
338	 * we won't pass it in.  Just pass in esym and magic #
339	 */
340	newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
341	newargs[1] = (long)esym;
342	newargs[2] = (long)ssym;
343	newargs[3] = (long)(void*)bootinfo;
344	args = (char *)newargs;
345	l = sizeof(newargs);
346
347	/* if -D is set then pause in the PROM. */
348	if (debug > 1) callrom();
349
350	/*
351	 * Jump directly to the kernel. Solaris kernel and Sun PROM
352	 * flash updates expect ROMP vector in %o0, so we do. Format
353	 * of other parameters and their order reflect OF_chain()
354	 * symantics since this is what older NetBSD kernels rely on.
355	 * (see sparc64/include/bootinfo.h for specification).
356	 */
357	DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw,
358				(long)args, (long)l, (long)ofw, (long)ofw,
359				(void*)marks[MARK_ENTRY]));
360	(*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw,
361				      (long)ofw);
362	printf("Returned from kernel entry point!\n");
363}
364
365static void
366start_kernel(char *kernel, char *bootline, void *ofw, int isfloppy,
367	int boothowto)
368{
369	int fd;
370	u_long marks[MARK_MAX] = {0};
371	int flags = LOAD_ALL;
372
373	if (isfloppy)
374		flags &= ~LOAD_BACKWARDS;
375
376	/*
377	 * First, load headers using default allocator and check whether kernel
378	 * entry address matches kernel text load address. If yes, this is the
379	 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
380	 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
381	 */
382	loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
383	if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
384		if (COMPAT_BOOT(marks) || compatmode) {
385			(void)printf("[c] ");
386			loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
387		} else {
388			loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
389		}
390		(void)printf("Loading %s: ", kernel);
391
392		if (fdloadfile(fd, marks, flags) != -1) {
393			close(fd);
394			jump_to_kernel(marks, kernel, bootline, ofw, boothowto);
395		}
396	}
397	(void)printf("Failed to load '%s'.\n", kernel);
398}
399
400static void
401help(void)
402{
403	printf( "enter a special command\n"
404		"  halt\n"
405		"  exit\n"
406		"    to return to OpenFirmware\n"
407		"  ?\n"
408		"  help\n"
409		"    to display this message\n"
410		"or a boot specification:\n"
411		"  [device] [kernel] [options]\n"
412		"\n"
413		"for example:\n"
414		"  disk:a netbsd -s\n");
415}
416
417static void
418do_config_command(const char *cmd, char *arg)
419{
420	DPRINTF(("do_config_command: %s\n", cmd));
421	if (strcmp(cmd, "bootpartition") == 0) {
422		char *c;
423
424		DPRINTF(("switching boot partition to %s from %s\n",
425		    arg, bootdev));
426		c = strrchr(bootdev, ':');
427		if (!c) return;
428		if (c[1] == 0) return;
429		if (strlen(arg) > strlen(c)) return;
430		strcpy(c, arg);
431		DPRINTF(("new boot device: %s\n", bootdev));
432		bootinfo_pass_bootdev = true;
433	}
434}
435
436static void
437check_boot_config(void)
438{
439	if (!root_fs_quickseekable)
440		return;
441
442	perform_bootcfg(BOOTCFG_FILENAME, &do_config_command, 32768);
443}
444
445void
446main(void *ofw)
447{
448	int boothowto, i = 0, isfloppy, kboothowto;
449
450	char kernel[PROM_MAX_PATH];
451	char bootline[PROM_MAX_PATH];
452
453	/* Initialize OpenFirmware */
454	romp = ofw;
455	prom_init();
456
457	printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
458
459	/* Figure boot arguments */
460	strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
461	kboothowto = boothowto =
462	    bootoptions(prom_getbootargs(), bootdev, kernel, bootline);
463	isfloppy = bootdev_isfloppy(bootdev);
464
465	for (;; *kernel = '\0') {
466		if (boothowto & RB_ASKNAME) {
467			char cmdline[PROM_MAX_PATH];
468
469			printf("Boot: ");
470			kgets(cmdline, sizeof(cmdline));
471
472			if (!strcmp(cmdline,"exit") ||
473			    !strcmp(cmdline,"halt")) {
474				prom_halt();
475			} else if (!strcmp(cmdline, "?") ||
476				   !strcmp(cmdline, "help")) {
477				help();
478				continue;
479			}
480
481			boothowto  = bootoptions(cmdline, bootdev, kernel,
482			    bootline);
483			boothowto |= RB_ASKNAME;
484			i = 0;
485		}
486
487		if (*kernel == '\0') {
488			if (kernelnames[i] == NULL) {
489				boothowto |= RB_ASKNAME;
490				continue;
491			}
492			strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
493		} else if (i == 0) {
494			/*
495			 * Kernel name was passed via command line -- ask user
496			 * again if requested image fails to boot.
497			 */
498			boothowto |= RB_ASKNAME;
499		}
500
501		check_boot_config();
502		start_kernel(kernel, bootline, ofw, isfloppy, kboothowto);
503
504		/*
505		 * Try next name from kernel name list if not in askname mode,
506		 * enter askname on reaching list's end.
507		 */
508		if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
509			printf(": trying %s...\n", kernelnames[i]);
510		} else {
511			printf("\n");
512			boothowto |= RB_ASKNAME;
513		}
514	}
515
516	(void)printf("Boot failed! Exiting to the Firmware.\n");
517	prom_halt();
518}
519