1/*	$NetBSD: boot.c,v 1.32 2024/01/07 07:58:33 isaki Exp $	*/
2
3/*
4 * Copyright (c) 2001 Minoura Makoto
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <machine/bootinfo.h>
31
32#include <lib/libsa/stand.h>
33#include <lib/libsa/loadfile.h>
34#include <lib/libsa/ufs.h>
35#include <lib/libsa/dev_net.h>
36#include <lib/libkern/libkern.h>
37
38#include "libx68k.h"
39#include "iocs.h"
40#include "switch.h"
41
42#include "exec_image.h"
43
44
45#define HEAP_START	((void*) 0x00080000)
46#define HEAP_END	((void*) 0x000fffff)
47#define EXSCSI_BDID	((void*) 0x00ea0001)
48#define SRAM_MEMSIZE	(*((long*) 0x00ed0008))
49
50char default_kernel[20] =
51#if defined(NETBOOT)
52    "nfs:netbsd";
53#else
54    "sd0a:netbsd";
55#endif
56int mpu;
57int hostadaptor;
58int console_device = -1;
59
60#ifdef DEBUG
61int debug = 1;
62#endif
63
64static void help(void);
65static int get_scsi_host_adapter(void);
66static void doboot(const char *, int);
67static void boot(char *);
68static void cmd_ls(char *);
69int bootmenu(void);
70void bootmain(int);
71extern int detectmpu(void);
72extern int badbaddr(void *);
73
74extern struct fs_ops file_system_ustarfs[];
75extern struct fs_ops file_system_nfs[];
76
77/* from boot_ufs/bootmain.c */
78static int
79get_scsi_host_adapter(void)
80{
81	uint32_t bootinf;
82	char *bootrom;
83	int ha;
84
85	bootinf = IOCS_BOOTINF();
86	if (bootinf < 0xa0) {
87		/* boot from FD */
88		return 0;
89	}
90
91	/* Or, bootinf indicates the boot address */
92	bootrom = (char *)(bootinf & 0x00ffffe0);
93	/*
94	 * bootrom+0x24	"SCSIIN" ... Internal SCSI (spc0@)
95	 *		"SCSIEX" ... External SCSI (spc1@ or mha0@)
96	 */
97	if (*(u_short *)(bootrom + 0x24 + 4) == 0x494e) {	/* "IN" */
98		ha = (X68K_BOOT_SCSIIF_SPC << 4) | 0;
99	} else if (badbaddr(EXSCSI_BDID)) {
100		ha = (X68K_BOOT_SCSIIF_MHA << 4) | 0;
101	} else {
102		ha = (X68K_BOOT_SCSIIF_SPC << 4) | 1;
103	}
104
105	return ha;
106}
107
108static void
109help(void)
110{
111	printf("Usage:\n");
112	printf("boot [ha@][dev:][file] -[flags]\n");
113	printf(" ha:    spc0, spc1, mha0\n");
114	printf(" dev:   sd<ID><PART>, ID=0-7, PART=a-p\n");
115	printf("        cd<ID>a, ID=0-7\n");
116	printf("        fd<UNIT>a, UNIT=0-3, format is detected.\n");
117	printf("        nfs, first probed NE2000 is used.\n");
118	printf(" file:  netbsd, netbsd.gz, etc.\n");
119	printf(" flags: abdqsv\n");
120	printf("ls [dev:][directory]\n");
121	printf("switch [show | key=val]\n");
122	printf("halt\nreboot\n");
123}
124
125static void
126doboot(const char *file, int flags)
127{
128	u_long		marks[MARK_MAX];
129	int fd;
130	int ha;		/* host adaptor */
131	int dev;	/* device number in devspec[] */
132	int unit;
133	int part;
134	int bootdev;
135	int maj;
136	char *name;
137	short *p;
138	int loadflag;
139	struct fs_ops *fs;
140
141	printf("Starting %s, flags 0x%x\n", file, flags);
142
143	if (devparse(file, &ha, &dev, &unit, &part, &name) != 0) {
144		printf("XXX: unknown corruption in /boot.\n");
145	}
146
147#ifdef DEBUG
148	if (file[0] == 'n') {
149		printf("dev = %x, unit = %d, name = %s\n",
150		       dev, unit, name);
151	} else {
152		printf("ha = 0x%x, dev = %x, unit = %d, part = %c, name = %s\n",
153		       ha, dev, unit, part + 'a', name);
154	}
155#endif
156
157	if (dev == 3) {		/* netboot */
158		bootdev = X68K_MAKEBOOTDEV(X68K_MAJOR_NE, unit, 0);
159	} else if (dev == 2) {		/* FD */
160		bootdev = X68K_MAKEBOOTDEV(X68K_MAJOR_FD, unit & 3, 0);
161	} else {		/* SCSI */
162		if (ha != 0) {
163			hostadaptor = ha;
164		}
165		if (hostadaptor == 0) {
166			printf("host adaptor must be specified.\n");
167			return;
168		}
169
170		maj = (dev == 0) ? X68K_MAJOR_SD : X68K_MAJOR_CD;
171		bootdev = X68K_MAKESCSIBOOTDEV(maj,
172		    hostadaptor >> 4,
173		    hostadaptor & 15,
174		    unit & 7, 0, 0);
175	}
176#ifdef DEBUG
177	printf("boot device = %x\n", bootdev);
178	if (file[0] == 'n') {
179		printf("type = %x, if = %d, unit = %d\n",
180		       B_TYPE(bootdev),
181		       B_X68K_SCSI_IF(bootdev),
182		       B_X68K_SCSI_IF_UN(bootdev));
183	} else {
184		printf("type = %x, if = %d, unit = %d, id = %d, lun = %d, part = %c\n",
185		       B_TYPE(bootdev),
186		       B_X68K_SCSI_IF(bootdev),
187		       B_X68K_SCSI_IF_UN(bootdev),
188		       B_X68K_SCSI_ID(bootdev),
189		       B_X68K_SCSI_LUN(bootdev),
190		       B_X68K_SCSI_PART(bootdev) + 'a');
191	}
192#endif
193
194	/*
195	 * Choose the last entry of file_system[] at runtime.
196	 *
197	 * file_system[] is checked in turn from the beginning at all cases.
198	 * Trying FS_OPS(ustarfs) for non-ustarfs displays "@" (as the
199	 * mark which read a cylinder?).  OTOH, trying FS_OPS(nfs) for
200	 * non-nfs displays "must mount first" error message.
201	 * It is better that neither is displayed and in other words you
202	 * should not put these two into file_system[] at the same time.
203	 * Therefore I choose one of these here.
204	 */
205	if (file[0] == 'n') {
206		fs = &file_system_nfs[0];
207	} else {
208		fs = &file_system_ustarfs[0];
209	}
210	memcpy(&file_system[nfsys - 1], fs, sizeof(*fs));
211
212	loadflag = LOAD_KERNEL;
213	if (file[0] == 'f')
214		loadflag &= ~LOAD_BACKWARDS;
215
216	marks[MARK_START] = 0x100000;
217	if ((fd = loadfile(file, marks, loadflag)) == -1) {
218		printf("loadfile failed\n");
219		return;
220	}
221	close(fd);
222
223	p = ((short*) marks[MARK_ENTRY]) - 1;
224#ifdef DEBUG
225	printf("Kernel Version: 0x%x\n", *p);
226#endif
227	if (*p != 0x4e73 && *p != 0) {
228		/*
229		 * XXX temporary solution; compatibility loader
230		 * must be written.
231		 */
232		printf("This kernel is too new to be loaded by "
233		       "this version of /boot.\n");
234		return;
235	}
236
237	exec_image(marks[MARK_START], 0, marks[MARK_ENTRY]-marks[MARK_START],
238		   marks[MARK_END]-marks[MARK_START], bootdev, flags);
239
240	return;
241}
242
243static void
244boot(char *arg)
245{
246	char filename[80];
247	char *p;
248	int flags = 0;
249
250	if (*arg == 0 || *arg == '-') {
251		strcpy(filename, default_kernel);
252		if (*arg == '-')
253			if (parseopts(arg, &flags) == 0) {
254				help();
255				return;
256			}
257		doboot(filename, flags);
258		return;
259	} else {
260		p = gettrailer(arg);
261		if (strchr(arg, ':')) {
262			strcpy(filename, arg);
263			if (arg[strlen(arg) - 1] == ':')
264				strcat(filename, "netbsd");
265		} else {
266			strcpy(filename, default_kernel);
267			strcpy(strchr(filename, ':') + 1, arg);
268		}
269		if (*p == '-') {
270			if (parseopts(p, &flags) == 0)
271				return;
272		} else if (*p != 0) {
273			help();
274			return;
275		}
276
277		doboot(filename, flags);
278		return;
279	}
280}
281
282static void
283cmd_ls(char *arg)
284{
285	char filename[80];
286
287	devopen_open_dir = 1;
288	if (*arg == 0) {
289		strcpy(filename, default_kernel);
290		strcpy(strchr(filename, ':')+1, "/");
291	} else if (strchr(arg, ':') == 0) {
292		strcpy(filename, default_kernel);
293		strcpy(strchr(filename, ':')+1, arg);
294	} else {
295		strcpy(filename, arg);
296		if (*(strchr(arg, ':')+1) == 0)
297			strcat(filename, "/");
298	}
299	ls(filename);
300	devopen_open_dir = 0;
301}
302
303int
304bootmenu(void)
305{
306	char input[80];
307	int n = 5, c;
308
309	printf("Press return to boot now, any other key for boot menu\n");
310	printf("booting %s - starting in %d seconds. ",
311		default_kernel, n);
312	while (n-- > 0 && (c = awaitkey_1sec()) == 0) {
313		printf("\r");
314		printf("booting %s - starting in %d seconds. ",
315		       default_kernel, n);
316	}
317	printf("\r");
318	printf("booting %s - starting in %d seconds. ", default_kernel, 0);
319	printf("\n");
320
321	if (c == 0 || c == '\r') {
322		doboot(default_kernel, 0);
323		printf("Could not start %s; ", default_kernel);
324		strcat(default_kernel, ".gz");
325		printf("trying %s.\n", default_kernel);
326		doboot(default_kernel, 0);
327		printf("Could not start %s; ", default_kernel);
328	}
329
330	printf("Please use the absolute unit# (e.g. SCSI ID)"
331	       " instead of the NetBSD logical #.\n");
332	for (;;) {
333		char *p, *options;
334
335		printf("> ");
336		kgets(input, sizeof(input));
337
338		for (p = &input[0]; p - &input[0] < 80 && *p == ' '; p++)
339			;
340		options = gettrailer(p);
341		if (strcmp("boot", p) == 0)
342			boot(options);
343		else if (strcmp("help", p) == 0 ||
344			 strcmp("?", p) == 0)
345			help();
346		else if (strcmp("halt", p) == 0 ||
347			 strcmp("reboot", p) == 0)
348			exit(0);
349		else if (strcmp("switch", p) == 0)
350			cmd_switch(options);
351		else if (strcmp("ls", p) == 0)
352			cmd_ls(options);
353		else
354			printf("Unknown command %s\n", p);
355	}
356}
357
358static u_int
359checkmemsize(void)
360{
361	u_int m;
362
363#define MIN_MB 4
364#define MAX_MB 12
365
366	for (m = MIN_MB; m <= MAX_MB; m++) {
367		if (badbaddr((void *)(m * 1024 * 1024 - 1))) {
368			/* no memory */
369			break;
370		}
371	}
372
373	return (m - 1) * 1024 * 1024;
374}
375
376extern const char bootprog_rev[];
377extern const char bootprog_name[];
378
379/*
380 * Arguments from the boot block:
381 *   bootdev - specifies the device from which /boot was read, in
382 *		bootdev format.
383 */
384void
385bootmain(int bootdev)
386{
387	u_int sram_memsize;
388	u_int probed_memsize;
389
390	console_device = consio_init(console_device);
391
392	hostadaptor = get_scsi_host_adapter();
393	rtc_offset = RTC_OFFSET;
394	try_bootp = 1;
395	mpu = detectmpu();
396
397	if (mpu < 3) {		/* not tested on 68020 */
398		printf("This MPU cannot run NetBSD.\n");
399		exit(1);
400	}
401	sram_memsize = SRAM_MEMSIZE;
402	if (sram_memsize < 4*1024*1024) {
403		printf("Main memory too small.\n");
404		exit(1);
405	}
406
407	setheap(HEAP_START, HEAP_END);
408
409#if !defined(NETBOOT)
410	switch (B_TYPE(bootdev)) {
411	case X68K_MAJOR_FD:
412		default_kernel[0] = 'f';
413		default_kernel[2] = '0' + B_UNIT(bootdev);
414		default_kernel[3] = 'a';
415		break;
416	case X68K_MAJOR_SD:
417		default_kernel[2] = '0' + B_X68K_SCSI_ID(bootdev);
418		default_kernel[3] =
419			'a' + sd_getbsdpartition(B_X68K_SCSI_ID(bootdev),
420						 B_X68K_SCSI_PART(bootdev));
421		break;
422	case X68K_MAJOR_CD:
423		default_kernel[0] = 'c';
424		default_kernel[2] = '0' + B_X68K_SCSI_ID(bootdev);
425		default_kernel[3] = 'a';
426		break;
427	default:
428		printf("Warning: unknown boot device: %x\n", bootdev);
429	}
430#endif
431	print_title("%s, Revision %s\n", bootprog_name, bootprog_rev);
432
433	/* check actual memory size for machines with a dead SRAM battery */
434	probed_memsize = checkmemsize();
435	if (sram_memsize != probed_memsize) {
436		printf("\x1b[1mWarning: SRAM Memory Size (%d MB) "
437		    "is different from probed Memory Size (%d MB)\n"
438		    "         Check and reset SRAM values.\x1b[m\n\n",
439		    sram_memsize / (1024 * 1024),
440		    probed_memsize / (1024 * 1024));
441	}
442
443	bootmenu();
444}
445